Vue3学习笔记(三)——模板语法、Class 与 Style 、ES6新增数组方法、UI库介绍
一、模板语法
Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。
在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作。
如果你对虚拟 DOM 的概念比较熟悉,并且偏好直接使用 JavaScript,你也可以结合可选的 JSX 支持直接手写渲染函数而不采用模板。但请注意,这将不会享受到和模板同等级别的编译时优化。
1.1、文本插值
最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 (即双大括号):
<span>Message: {{ msg }}</span>
双大括号标签会被替换为相应组件实例中 msg
属性的值。同时每次 msg
属性更改时它也会同步更新。
1.2、原始 HTML
双大括号会将数据解释为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html
指令:
<p>Using text interpolation: {{ rawHtml }}</p> <p>Using v-html directive: <span v-html="rawHtml"></span></p>
Using text interpolation: <span style="color: red">This should be red.</span>
Using v-html directive: This should be red.
<template> <h2> {{ msg }} </h2> <h2 v-text="msg"></h2> <h2 v-html="msg"></h2> </template> <script lang="ts"> import { ref, getCurrentInstance } from "vue"; export default { setup() { let msg = "<p style='color:blue'>Hello, world!</p>"; return { msg }; }, }; </script>
这里我们遇到了一个新的概念。这里看到的 v-html
attribute 被称为一个指令。指令由 v-
作为前缀,表明它们是一些由 Vue 提供的特殊 attribute,你可能已经猜到了,它们将为渲染的 DOM 应用特殊的响应式行为。这里我们做的事情简单来说就是:在当前组件实例上,将此元素的 innerHTML 与 rawHtml
属性保持同步。
span
的内容将会被替换为 rawHtml
属性的值,插值为纯 HTML——数据绑定将会被忽略。注意,你不能使用 v-html
来拼接组合模板,因为 Vue 不是一个基于字符串的模板引擎。在使用 Vue 时,应当使用组件作为 UI 重用和组合的基本单元。
安全警告
在网站上动态渲染任意 HTML 是非常危险的,因为这非常容易造成 XSS 漏洞。请仅在内容安全可信时再使用 v-html
,并且永远不要使用用户提供的 HTML 内容。
跨站脚本攻击(也称为XSS)指利用网站漏洞从用户那里恶意盗取信息。
<template> <h2> {{ msg }} </h2> <h2 v-text="msg"></h2> <div v-html="msg"></div> <textarea v-model="msg" style="width: 100%; height: 300px"></textarea> </template> <script lang="ts"> import { ref, getCurrentInstance } from "vue"; export default { setup() { let msg = ref("<p style='color:blue'>Hello, world!</p>"); return { msg }; }, }; </script>
1.3、属性绑定
双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind
指令:
<div v-bind:id="dynamicId"></div>
v-bind
指令指示 Vue 将元素的 id
attribute 与组件的 dynamicId
属性保持一致。如果绑定的值是 null
或者 undefined
,那么该 attribute 将会从渲染的元素上移除。
1.3.1、简写
因为 v-bind
非常常用,我们提供了特定的简写语法:
<div :id="dynamicId"></div>
开头为 :
的 attribute 可能和一般的 HTML attribute 看起来不太一样,但它的确是合法的 attribute 名称字符,并且所有支持 Vue 的浏览器都能正确解析它。此外,他们不会出现在最终渲染的 DOM 中。简写语法是可选的,但相信在你了解了它更多的用处后,你应该会更喜欢它。
接下来的指引中,我们都将在示例中使用简写语法,因为这是在实际开发中更常见的用法。
1.3.2、布尔型属性
布尔型 attribute 依据 true / false 值来决定 attribute 是否应该存在于该元素上。disabled
就是最常见的例子之一。
v-bind
在这种场景下的行为略有不同:
<button :disabled="isButtonDisabled">Button</button>
当 isButtonDisabled
为真值或一个空字符串 (即 <button disabled="">
) 时,元素会包含这个 disabled
attribute。而当其为其他假值时 attribute 将被忽略。
1.3.3、动态绑定多个值
如果你有像这样的一个包含多个 attribute 的 JavaScript 对象:
const objectOfAttrs = { id: 'container', class: 'wrapper' }
通过不带参数的 v-bind
,你可以将它们绑定到单个元素上:
<div v-bind="objectOfAttrs"></div>
<template> <button :title="msg + '!!!'">冒号绑定</button> <button v-bind:title="msg + '!!!'">v-bind绑定</button> <button :disabled="isDisalbed">被禁用的按钮</button> <button v-bind="attrs">登录按钮</button> </template> <script lang="ts"> import { ref, getCurrentInstance } from "vue"; export default { setup() { let msg = "这是一个按钮"; let isDisalbed = ref(true); let attrs = { class: "cls1", style: "color:red", id: "btnLogin", }; return { msg, isDisalbed, attrs }; }, }; </script>
1.4、使用 JavaScript 表达式
至此,我们仅在模板中绑定了一些简单的属性名。但是 Vue 实际上在所有的数据绑定中都支持完整的 JavaScript 表达式:
{{ number + 1 }} {{ ok ? 'YES' : 'NO' }} {{ message.split('').reverse().join('') }} <div :id="`list-${id}`"></div>
这些表达式都会被作为 JavaScript ,以组件为作用域解析执行。
在 Vue 模板内,JavaScript 表达式可以被使用在如下场景上:
- 在文本插值中 (双大括号)
- 在任何 Vue 指令 (以
v-
开头的特殊属性) 属性的值中
1.4.1、仅支持表达式
每个绑定仅支持单一表达式,也就是一段能够被求值的 JavaScript 代码。一个简单的判断方法是是否可以合法地写在 return
后面。
因此,下面的例子都是无效的:
<!-- 这是一个语句,而非表达式 --> {{ var a = 1 }} <!-- 条件控制也不支持,请使用三元表达式 --> {{ if (ok) { return message } }}
1.4.2、调用函数
可以在绑定的表达式中使用一个组件暴露的方法:
<span :title="toTitleDate(date)"> {{ formatDate(date) }} </span>
TIP
绑定在表达式中的方法在组件每次更新时都会被重新调用,因此不应该产生任何副作用,比如改变数据或触发异步操作。
<template> {{ show() }} <hr /> <input v-model="msg" /> </template> <script lang="ts" setup> import { ref } from "vue"; function show() { console.log("show被调用了"); } let msg = ref("Hello"); </script>
<template> <button @click="msg = msg ? 'ok' : 'no'"> {{ (msg + "!!!").split("").reverse().join("") }} </button> <h2> {{ `消息内容是:${msg}` }} </h2> <input v-model="msg" /> <h2> {{ showInfo() }} </h2> <h2> {{ showInfoComputed }} </h2> </template> <script lang="ts"> import { computed } from "@vue/reactivity"; import { ref, getCurrentInstance } from "vue"; export default { setup() { let msg = ref("这是一个按钮"); function showInfo() { console.log("showInfo被调用了"); return "showinfo的返回值"; } let showInfoComputed = computed(() => { console.log("showInfoComputed被调用了"); return "showInfoComputed的返回值"; }); return { msg, showInfo, showInfoComputed }; }, }; </script>
1.4.3、受限的全局访问
模板中的表达式将被沙盒化,仅能够访问到有限的全局对象列表。该列表中会暴露常用的内置全局对象,比如 Math
和 Date
。
没有显式包含在列表中的全局对象将不能在模板内表达式中访问,例如用户附加在 window
上的属性。然而,你也可以自行在 app.config.globalProperties
上显式地添加它们,供所有的
Vue 表达式使用。
1.4.4、vue3.0之全局变量app.config.globalProperties的使用
globalProperties
类型:[key: string]: any
默认:undefined
用法
添加一个可以在应用的任何组件实例中访问的全局 property。组件的 property 在命名冲突具有优先权。
这可以代替 Vue 2.x Vue.prototype 扩展:
// 之前(Vue 2.x)
Vue.prototype.$http = () => {}
Vue3挂载前添加全局对象,main.ts:
import { createApp } from 'vue' import App from './App.vue' import HelloWorld from './components/HelloWorld.vue' import router from './router' let app=createApp(App); app.config.globalProperties.msg="abc"; app.use(router).mount('#app');
取值:
<template> <input v-model="info" /> <button @click="info += '1'">加1</button> </template> <script lang="ts"> import { ref, getCurrentInstance } from "vue"; export default { setup() { let msg = getCurrentInstance()?.appContext.config.globalProperties.msg; let info = ref(msg); return { info }; }, mounted(this: any) { console.log(this.msg); }, }; </script>
注意:vue3推荐使用依赖注入:provide和inject。原因:vue/rfcs.
1.5、指令 Directives
指令是带有 v-
前缀的特殊 attribute。Vue 提供了许多内置指令,包括上面我们所介绍的 v-bind
和 v-html
。
- v-text
- v-html
- v-show
- v-if
- v-else
- v-else-if
- v-for
- v-on
- v-bind
- v-model
- v-slot
- v-pre
- v-once
- v-memo
- v-cloak
指令 attribute 的期望值为一个 JavaScript 表达式
(除了少数几个例外,即之后要讨论到的 v-for
、v-on
和 v-slot
)。一个指令的任务是在其表达式的值变化时响应式地更新
DOM。以 v-if
为例:
<p v-if="seen">Now you see me</p>
这里,v-if
指令会基于表达式 seen
的值的真假来移除/插入该 <p>
元素。
1.5.1、参数 Arguments
某些指令会需要一个“参数”,在指令名后通过一个冒号隔开做标识。例如用 v-bind
指令来响应式地更新一个 HTML attribute:
<a v-bind:href="url"> ... </a> <!-- 简写 --> <a :href="url"> ... </a>
这里 href
就是一个参数,它告诉 v-bind
指令将表达式 url
的值绑定到元素的 href
attribute 上。在简写中,参数前的一切 (例如 v-bind:
) 都会被缩略为一个 :
字符。
1. 动态 v-bind:src
图片资源一般就近放置在组件中 或者 src/assets/img 文件中
// 组件 template 中 <img :src="getAssetsFile(icon)" alt=""> // 组件 js 中 import { getAssetsFile } from '@/utils/index.js' // 公共 util.js 中 const getAssetsFile = (url) => { return new URL(`../assets/images/${url}`, import.meta.url).href }
注意:new URL(url,import.meta.url) 中的参数 url ,不能如下面定义变量拼接
GPT解释:
你提供的是一个Javascript函数,它用来生成指向资产文件(例如图片、样式表或脚本)的URL。这个函数假设这些文件都位于名为"icon"的目录中,该目录与当前的Javascript模块(即调用import.meta.url的模块)同目录。这用于支持 import.meta.url 的模块,如ES6 或更新的版本。
以下是关于该函数工作原理的简单解释:
import.meta.url 给出的是当前模块的URL。在浏览器中,这将是提取模块脚本的URL。在本地,它将是从中导入或执行模块的文件的URL。
new URL(...) 用于创建新的URL对象。传给new URL(...)的第一个参数是相对URL —— 在这种情况下,./icon/${url} 是一个将 url 参数添加到 ./icon/ 后得到的相对URL的模板字符串。
new URL(...)的第二个参数是在第一个参数为相对URL时使用的基础URL — 在这种情况下,作为基础URL的 import.meta.url 被使用。如前所述,import.meta.url解析为当前模块的URL。
.href 是URL对象的一个属性,返回整个URL作为一个字符串。
因此,如果你从具有URL "http://example.com/path/to/module.js" 的模块调用 getAssetsFile("image.jpg"),它将返回 "http://example.com/path/to/icon/image.jpg".
const getAssetsFile = (url) => {
let path = 'src/assets'
return new URL(`${path}${url}`, import.meta.url).href
}
上述开发没问题,但是打包会报错,不支持import.meta.url
2. 静态 src
公共 assets 文件夹中存放的图片
<!-- 绝对路径 --> <img src="/src/assets/img/1.png"> <img src="@/assets/img/1.png" class="link-icon">
组件就近原则目录存放的图片
<!-- 相对路径 --> <img src="./icon/1.png"> css中,背景图片 background: url(@/assets/img/bg.jpg) no-repeat center; 或者 background: url(/src/assets/img/bg.jpg) no-repeat center;
另一个例子是 v-on
指令,它将监听 DOM 事件:
<a v-on:click="doSomething"> ... </a> <!-- 简写 --> <a @click="doSomething"> ... </a>
这里的参数是要监听的事件名称:click
。v-on
有一个相应的缩写,即 @
字符。我们之后也会讨论关于事件处理的更多细节。
1.5.2、动态参数
同样在指令参数上也可以使用一个 JavaScript 表达式,需要包含在一对方括号内:
<!-- 注意,参数表达式有一些约束, 参见下面“动态参数值的限制”与“动态参数语法的限制”章节的解释 --> <a v-bind:[attributeName]="url"> ... </a> <!-- 简写 --> <a :[attributeName]="url"> ... </a>
这里的 attributeName
会作为一个 JavaScript 表达式被动态执行,计算得到的值会被用作最终的参数。举例来说,如果你的组件实例有一个数据属性 attributeName
,其值为 "href"
,那么这个绑定就等价于 v-bind:href
。
相似地,你还可以将一个函数绑定到动态的事件名称上:
<a v-on:[eventName]="doSomething"> ... </a> <!-- 简写 --> <a @[eventName]="doSomething">
在此示例中,当 eventName
的值是 "focus"
时,v-on:[eventName]
就等价于 v-on:focus
。
第一步,vite 配置 vite.config.ts 文件增加 resolve.alias 配置 import { resolve } from 'path' export default defineConfig({ plugins: [vue()], resolve: { alias: [{ find: '@', replacement: resolve(__dirname, 'src') }] } }) 如果“path”有错误提示, 是因为 TS 找不到类型定义,按照提示安装 “@types/node”后即可 运行命令 npm install --dev @types/node 这时候已经可以使用@别名了 但还不能通过鼠标点击跳转到定义位置,鼠标点击跳转到定义位置需要在 tsconfig.json 中增加配置 "baseUrl": ".", "paths": { "@/*": ["src/*"] } 这时候就可以通过鼠标点击跳转到方法或变量定义位
动态参数值的限制
动态参数中表达式的值应当是一个字符串,或者是 null
。特殊值 null
意为显式移除该绑定。其他非字符串的值会触发警告。
动态参数语法的限制
动态参数表达式因为某些字符的缘故有一些语法限制,比如空格和引号,在 HTML attribute 名称中都是不合法的。例如下面的示例:
<!-- 这会触发一个编译器警告 --> <a :['foo' + bar]="value"> ... </a>
如果你需要传入一个复杂的动态参数,我们推荐使用计算属性替换复杂的表达式,也是 Vue 最基础的概念之一,我们很快就会讲到。
当使用 DOM 内嵌模板 (直接写在 HTML 文件里的模板) 时,我们需要避免在名称中使用大写字母,因为浏览器会强制将其转换为小写:
<a :[someAttr]="value"> ... </a>
上面的例子将会在 DOM 内嵌模板中被转换为 :[someattr]
。如果你的组件拥有 “someAttr” 属性而非 “someattr”,这段代码将不会工作。单文件组件内的模板不受此限制。
<template> <button :[attrName]="'button'">这是一个按钮</button> 要绑定的属性名称:<input v-model="attrName" /> <hr /> <button @[eventName]="show">这是一个按钮</button> 要绑定的事件名称:<input v-model="eventName" /> </template> <script lang="ts"> import { ref } from "vue"; export default { setup() { let attrName = ref("title"); let eventName = ref("click"); let show = (e) => { console.log(eventName.value); }; return { attrName, eventName, show }; }, }; </script> <style scoped></style>
1.5.3、修饰符 Modifiers
修饰符是以点开头的特殊后缀,表明指令需要以一些特殊的方式被绑定。例如 .prevent
修饰符会告知 v-on
指令对触发的事件调用 event.preventDefault()
:
<form @submit.prevent="onSubmit">...</form>
之后在讲到 v-on
和 v-model
的功能时,你将会看到其他修饰符的例子。
最后,在这里你可以直观地看到完整的指令语法:
1.5.4、v-text
更新元素的文本内容。
-
期望的绑定值类型:
string
-
详细信息
v-text
通过设置元素的 textContent 属性来工作,因此它将覆盖元素中所有现有的内容。如果你需要更新textContent
的部分,应该使用 mustache interpolations 代替。 -
示例
-
<span v-text="msg"></span> <!-- 等同于 --> <span>{{msg}}</span>
-
参考:模板语法 - 文本插值
1.5.5、v-html
更新元素的 innerHTML。
-
期望的绑定值类型:
string
-
详细信息
v-html
的内容直接作为普通 HTML 插入—— Vue
模板语法是不会被解析的。如果你发现自己正打算用 v-html
来编写模板,不如重新想想怎么使用组件来代替。
安全说明
在你的站点上动态渲染任意的 HTML 是非常危险的,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要将用户提供的内容作为插值
在单文件组件,scoped
样式将不会作用于 v-html
里的内容,因为
HTML 内容不会被 Vue 的模板编译器解析。如果你想让 v-html
的内容也支持 scoped CSS,你可以使用 CSS
modules 或使用一个额外的全局 <style>
元素,手动设置类似 BEM 的作用域策略。
-
示例:
-
<div v-html="html"></div>
<template> <h2 v-html="msg"></h2> <p>这是一个P标签</p> </template> <script lang="ts"> import { computed } from "@vue/reactivity"; import { ref, getCurrentInstance } from "vue"; export default { setup() { let msg = ref("<p>这是一个按钮</p>"); return { msg }; }, }; </script> <style scoped> p { color: red; } </style>
1.5.6、v-show
基于表达式值的真假性,来改变元素的可见性。
-
期望的绑定值类型:
any
-
详细信息
v-show
通过设置内联样式的display
CSS 属性来工作,当元素可见时将使用初始display
值。当条件改变时,也会触发过渡效果。
1.5.7、v-if
基于表达式值的真假性,来条件性地渲染元素或者模板片段。
-
期望的绑定值类型:
any
-
详细信息
当
v-if
元素被触发,元素及其所包含的指令/组件都会销毁和重构。如果初始条件是假,那么其内部的内容根本都不会被渲染。可用于
<template>
表示仅包含文本或多个元素的条件块。当条件改变时会触发过渡效果。
当同时使用时,
v-if
比v-for
优先级更高。我们并不推荐在一元素上同时使用这两个指令 — 查看列表渲染指南详情。 -
参考:条件渲染 - v-if
-
因为
v-if
是一个指令,他必须依附于某个元素。但如果我们想要切换不止一个元素呢?在这种情况下我们可以在一个<template>
元素上使用v-if
,这只是一个不可见的包装器元素,最后渲染的结果并不会包含这个<template>
元素。<template v-if="ok"> <h1>Title</h1> <p>Paragraph 1</p> <p>Paragraph 2</p> </template>
v-else
和v-else-if
也可以在<template>
上使用。 -
<template> <button @click="isShow = !isShow">{{ isShow ? "隐藏" : "显示" }}</button> <div v-if="isShow"> <h2>这段文字的显示与隐藏完全是由isShow控制的,DOM控制的</h2> <textarea>textarea</textarea> <textarea>textarea</textarea> </div> <template v-if="isShow"> <h2>这段文字的显示与隐藏完全是由isShow控制的,DOM控制的</h2> <textarea>textarea</textarea> <textarea>textarea</textarea> </template> </template> <script lang="ts"> import { ref } from "vue"; export default { setup() { let isShow = ref(true); return { isShow }; }, }; </script> <style scoped></style>
1.5.8、v-if
vs v-show
v-if
是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。v-if
也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。- 相比之下,
v-show
简单许多,元素无论初始条件如何,始终会被渲染,只有 CSSdisplay
属性会被切换。 - 总的来说,
v-if
有更高的切换开销,而v-show
有更高的初始渲染开销。因此,如果需要频繁切换,则使用v-show
较好;如果在运行时绑定条件很少改变,则v-if
会更合适。
1.5.9、v-else
表示 v-if
或 v-if
/ v-else-if
链式调用的“else
块”。
-
无需传入表达式
-
详细信息
-
限定:上一个兄弟元素必须有
v-if
或v-else-if
。 -
可用于
<template>
表示仅包含文本或多个元素的条件块。
-
-
示例
-
<div v-if="Math.random() > 0.5"> Now you see me </div> <div v-else> Now you don't </div>
1.5.10、v-else-if
表示 v-if
的“else if 块”。可以进行链式调用。
-
期望的绑定值类型:
any
-
详细信息
-
限定:上一个兄弟元素必须有
v-if
或v-else-if
。 -
可用于
<template>
表示仅包含文本或多个元素的条件块。
-
-
示例
-
<div v-if="type === 'A'"> A </div> <div v-else-if="type === 'B'"> B </div> <div v-else-if="type === 'C'"> C </div> <div v-else> Not A/B/C </div>
示例:
<template> <h2 v-html="msg"></h2> <template v-if="isShow"> <p>这是一个P标签</p> <button>abc</button> </template> <button @click="isShow = !isShow">{{ `isShow=${isShow}` }}</button> <div v-if="Math.random() > 0.5">Math.random()>0.5</div> <div v-else>Math.random()<=0.5</div> <hr /> <input v-model="type" /> <p v-if="type === 'A'">优秀</p> <p v-else-if="type === 'B'">良好</p> <p v-else-if="type === 'C'">达标</p> <p v-else>不达标</p> </template> <script lang="ts"> import { computed } from "@vue/reactivity"; import { ref, getCurrentInstance } from "vue"; export default { setup() { let msg = ref("<p>这是一个按钮</p>"); let isShow = ref(true); let type = ref("A"); return { msg, isShow, type }; }, }; </script> <style scoped> p { color: red; } </style>
结果:
修改:
1.5.11、v-for
基于原始数据多次渲染元素或模板块。
-
期望的绑定值类型:
Array | Object | number | string | Iterable
-
详细信息
指令值必须使用特殊语法
alias in expression
为正在迭代的元素提供一个别名: -
<div v-for="item in items"> {{ item.text }} </div>
-
或者,你也可以为索引指定别名 (如果用在对象,则是键值):
-
<div v-for="(item, index) in items"></div> <div v-for="(value, key) in object"></div> <div v-for="(value, name, index) in object"></div>
-
v-for
的默认方式是尝试就地更新元素而不移动它们。要强制其重新排序元素,你需要用特殊 attributekey
来提供一个排序提示: -
<div v-for="item in items" :key="item.id"> {{ item.text }} </div>
-
v-for
也可以用于 Iterable Protocol 的实现,包括原生Map
和Set
。 -
<template> <ul> <li v-for="(item, index) in users" :key="item.id"> {{ index }} - {{ item.id }} - {{ item.name }} <hr /> <ul> <li v-for="(value, key, index) in item"> {{ index }} - {{ key }}:{{ value }} </li> </ul> </li> </ul> <hr /> <ul> <li v-for="(value, key, index) in user"> {{ index }} - {{ key }}:{{ value }} </li> </ul> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { let users = reactive([ { id: 201, name: "mark" }, { id: 202, name: "rose" }, { id: 203, name: "jack" }, ]); let user = { name: "张三", age: 19 }; return { users, user }; }, }; </script> <style scoped> .div1 { height: 200px; overflow: auto; width: 300px; background-color: lightgoldenrodyellow; } .div1 ul li { height: 300px; line-height: 300px; } </style>
-
参考:
1.5.12、v-on
给元素绑定事件监听器。
-
缩写:
@
-
期望的绑定值类型:
Function | Inline Statement | Object (不带参数)
-
参数:
event
(使用对象语法则为可选项) -
修饰符:
.stop
——调用event.stopPropagation()
。-
<template> <div id="div1" @click="showInfo"> <button @click.stop="showInfo">stop示例</button> </div> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { function showInfo() { console.log("事件被触发了"); } return { showInfo }; }, }; </script> <style scoped> #div1 { height: 100px; line-height: 100px; text-align: center; width: 200px; background: yellow; } </style>
.prevent
——调用event.preventDefault()
。-
<template> <div id="div1" @click="showInfo"> <button @click.stop="showInfo">stop示例</button> <a href="http://best.cnblogs.com" @click.stop.prevent="showInfo">博客2</a> </div> <hr /> <a href="http://best.cnblogs.com" @click.prevent="showInfo">博客1</a> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { function showInfo() { console.log("事件被触发了"); } return { showInfo }; }, }; </script> <style scoped> #div1 { height: 100px; line-height: 100px; text-align: center; width: 200px; background: yellow; } </style>
.capture
——在捕获模式添加事件监听器。-
<template> <div id="div1" @click.capture="showInfo('2、父元素', $event)"> <button @click="showInfo('1、子元素', $event)">capture示例</button> </div> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { function showInfo(msg, event) { console.log(msg, event); } return { showInfo }; }, }; </script> <style scoped> #div1 { height: 100px; line-height: 100px; text-align: center; width: 200px; background: yellow; } </style>
-
<div id="div1" @click="showInfo('2、父元素', $event)"><button @click="showInfo('1、子元素', $event)">确定</button></div><div id="div1" @click.capture="showInfo('2、父元素', $event)"><button @click="showInfo('1、子元素', $event)">确定</button></div>
-
.self
——只有事件从元素本身发出才触发处理函数。-
<template> <div id="div1" @click.self="showInfo('2、父元素', $event)"> <button @click="showInfo('1、子元素', $event)">self示例</button> </div> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { function showInfo(msg, event) { console.log(msg, event.target); } return { showInfo }; }, }; </script> <style scoped> #div1 { height: 100px; line-height: 100px; text-align: center; width: 200px; background: yellow; } </style>
-
stop与self的区别:
self只能限制自己的触发机制,不管会不会进行冒泡或者捕获事件,只要不点击我,我就不触发,(你给谁添加.self只能让限制自己的点击事件,如果这个元素外面还有一个元素,这外面的元素照样能进行冒泡或捕获行为)
stop 就能阻止事件冒泡,不会影响其他的事件
.{keyAlias}
——只在某些按键下触发处理函数。- 常用别名如下:
Vue中常用的按键别名:
-
回车 => enter
删除 => delete (捕获"删除”和“退格”键)
退出 => esc
空格 => space
换行 => tab (不适合用keyup事件 适合用于keydown事件)
上 => up
下 => down
左 => left
右 => right
<template> <input @keyup.enter="keyEvent" /> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { function keyEvent(e) { console.log("key:" + e.key, "code:" + e.code, "keyCode:" + e.keyCode); } return { keyEvent }; }, }; </script> <style scoped> .div1 { height: 200px; overflow: auto; width: 300px; background-color: lightgoldenrodyellow; } .div1 ul li { height: 300px; line-height: 300px; } </style>
- 常用别名如下:
.once
——最多触发一次处理函数。.left
——只在鼠标左键事件触发处理函数。.right
——只在鼠标右键事件触发处理函数。.middle
——只在鼠标中键事件触发处理函数。-
<template> <input @keyup.ctrl.alt.s="keyEvent" /> <button @click.right.prevent="rightClickEvent">只有按右键才触发事件</button> <button @mousedown.middle="rightClickEvent">只有按中键才触发事件</button> <button @mouseup.left="rightClickEvent">只有按左键才触发事件</button> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { function keyEvent(e) { console.log("key:" + e.key, "code:" + e.code, "keyCode:" + e.keyCode); return false; } function rightClickEvent(e) { console.log("您点击了", e); console.log(["左", "中", "右"][e.button] + "键被点击"); } return { keyEvent, rightClickEvent }; }, }; </script> <style scoped></style>
- 模拟同步延时
-
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> </head> <body> <button id="btnLogin">登录</button> <script> //异步的,异步IO // setTimeout(() => { // console.log("A、2000毫秒后你可以看到控制台输出该消息"); // }, 2000); // console.log("B、2000毫秒后你可以看到控制台输出该消息"); function sleep(millSeconds) { let time = new Date().getTime(); while (new Date().getTime() <= time + millSeconds); } for (var i = 0; i < 10; i++) { console.log(i); sleep(2000); } </script> </body> </html>
.passive
——通过{ passive: true }
附加一个 DOM 事件。 positive正面的-
<!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 --> <!-- 以防其中包含 `event.preventDefault()` --> <div @scroll.passive="onScroll">...</div>
-
-
<template> <!--wheel是中间的滚轮--> <!--scroll是滚动事件--> <div id="div1" @wheel.passive="eventMethod"> <ul> <li>rose</li> <li>mark</li> <li>jack</li> <li>lucy</li> <li>lili</li> </ul> </div> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { //延时 let sleep = (millSeconds) => { let time = new Date().getTime(); while (new Date().getTime() <= time + millSeconds); }; let eventMethod = (e) => { sleep(2000); console.log(e); }; return { eventMethod }; }, }; </script> <style scoped> #div1 { width: 300px; height: 100px; background: lightgoldenrodyellow; overflow: auto; } #div1 li { margin: 20px; background-color: aquamarine; height: 100px; width: 200px; } </style>
-
事件的默认行为立即执行,不用等到函数执行完毕
在鼠标滚动事件添加.passive,会先执行滚动事件,再执行该事件后面调用的方法(@scroll.passive=“函数名”)
- 注意:如果上面的wheel事件不加passive则下一次滚动必须等事件执行完成后再滚动,滚动条会卡住,加上后事件的执行会后置。
-
详细信息
事件类型由参数来指定。表达式可以是一个方法名,一个内联声明,如果有修饰符则可省略。
当用于普通元素,只监听原生 DOM 事件。当用于自定义元素组件,则监听子组件触发的自定义事件。
当监听原生 DOM 事件时,方法接收原生事件作为唯一参数。如果使用内联声明,声明可以访问一个特殊的
$event
变量:v-on:click="handle('ok', $event)"
。v-on
还支持绑定不带参数的事件/监听器对的对象。请注意,当使用对象语法时,不支持任何修饰符。 -
示例:
-
<!-- 方法处理函数 --> <button v-on:click="doThis"></button> <!-- 动态事件 --> <button v-on:[event]="doThis"></button> <!-- 内联声明 --> <button v-on:click="doThat('hello', $event)"></button> <!-- 缩写 --> <button @click="doThis"></button> <!-- 使用缩写的动态事件 --> <button @[event]="doThis"></button> <!-- 停止传播 --> <button @click.stop="doThis"></button> <!-- 阻止默认事件 --> <button @click.prevent="doThis"></button> <!-- 不带表达式地阻止默认事件 --> <form @submit.prevent></form> <!-- 链式调用修饰符 --> <button @click.stop.prevent="doThis"></button> <!-- 按键用于 keyAlias 修饰符--> <input @keyup.enter="onEnter" /> <!-- 点击事件将最多触发一次 --> <button v-on:click.once="doThis"></button> <!-- 对象语法 --> <button v-on="{ mousedown: doThis, mouseup: doThat }"></button>
-
监听子组件的自定义事件 (当子组件的“my-event”事件被触发,处理函数将被调用):
-
<MyComponent @my-event="handleThis" /> <!-- 内联声明 --> <MyComponent @my-event="handleThis(123, $event)" />
-
参考:
1.5.13、v-bind
动态的绑定一个或多个 attribute,也可以是组件的 prop。
-
缩写:
:
或者.
(当使用.prop
修饰符) -
期望:
any (带参数) | Object (不带参数)
-
参数:
attrOrProp (可选的)
-
修饰符:
.camel
——将短横线命名的 attribute 转变为驼峰式命名。.prop
——强制绑定为 DOM property。3.2+.attr
——强制绑定为 DOM attribute。3.2+
-
用途:
当用于绑定
class
或style
attribute,v-bind
支持额外的值类型如数组或对象。详见下方的指南链接。在处理绑定时,Vue 默认会利用
in
操作符来检查该元素上是否定义了和绑定的 key 同名的 DOM property。如果存在同名的 property,则 Vue 会把作为 DOM property 赋值,而不是作为 attribute 设置。这个行为在大多数情况都符合期望的绑定值类型,但是你也可以显式用.prop
和.attr
修饰符来强制绑定方式。有时这是必要的,特别是在和自定义元素打交道时。当用于组件 props 绑定时,所绑定的 props 必须在子组件中已被正确声明。
当不带参数使用时,可以用于绑定一个包含了多个 attribute 名称-绑定值对的对象。
-
示例:
-
<!-- 绑定 attribute --> <img v-bind:src="imageSrc" /> <!-- 动态 attribute 名 --> <button v-bind:[key]="value"></button> <!-- 缩写 --> <img :src="imageSrc" /> <!-- 缩写形式的动态 attribute 名 --> <button :[key]="value"></button> <!-- 内联字符串拼接 --> <img :src="'/path/to/images/' + fileName" /> <!-- class 绑定 --> <div :class="{ red: isRed }"></div> <div :class="[classA, classB]"></div> <div :class="[classA, { classB: isB, classC: isC }]"></div> <!-- style 绑定 --> <div :style="{ fontSize: size + 'px' }"></div> <div :style="[styleObjectA, styleObjectB]"></div> <!-- 绑定对象形式的 attribute --> <div v-bind="{ id: someProp, 'other-attr': otherProp }"></div> <!-- prop 绑定。“prop” 必须在子组件中已声明。 --> <MyComponent :prop="someThing" /> <!-- 传递子父组件共有的 prop --> <MyComponent v-bind="$props" /> <!-- XLink --> <svg><a :xlink:special="foo"></a></svg>
-
.prop
修饰符也有专门的缩写,.
: -
<div :someProperty.prop="someObject"></div> <!-- 等同于 --> <div .someProperty="someObject"></div>
-
当在 DOM 内模板使用
.camel
修饰符,可以驼峰化v-bind
attribute 的名称,例如 SVGviewBox
attribute: -
<svg :view-box.camel="viewBox"></svg>
-
如果使用字符串模板或使用构建步骤预编译模板,则不需要
.camel
。 -
参考:
1.5.14、v-model
在表单输入元素或组件上创建双向绑定。
-
期望的绑定值类型:根据表单输入元素或组件输出的值而变化
-
仅限:
<input>
<select>
<textarea>
- components
-
修饰符:
-
<template> <p> <input v-model.lazy="msg" /> </p> <p> <select v-model="msg"> <option value="apple">苹果</option> <option value="orange">橙子</option> </select> </p> <p> <textarea v-model="msg" /> </p> <h2> {{ msg }} </h2> <p> <input v-model.number.trim="cardno" @keyup="show" /> </p> <h2>卡号:{{ cardno }}</h2> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { let msg = ref(""); let cardno = ref(""); function show() { console.log(typeof cardno.value, cardno); } return { msg, cardno, show }; }, }; </script> <style scoped> #div1 { width: 300px; height: 100px; background: lightgoldenrodyellow; overflow: auto; } #div1 li { margin: 20px; background-color: aquamarine; height: 100px; width: 200px; } </style>
-
参考:
1.5.15、v-slot
用于声明具名插槽或是期望接收 props 的作用域插槽。
-
缩写:
#
-
期望的绑定值类型:能够合法在函数参数位置使用的 JavaScript 表达式。支持解构语法。绑定值是可选的——只有在给作用域插槽传递 props 才需要。
-
参数:插槽名 (可选,默认是
default
) -
仅限:
<template>
- components (用于带有 prop 的单个默认插槽)
-
示例:
-
<!-- 具名插槽 --> <BaseLayout> <template v-slot:header> Header content </template> <template v-slot:default> Default slot content </template> <template v-slot:footer> Footer content </template> </BaseLayout> <!-- 接收 prop 的具名插槽 --> <InfiniteScroll> <template v-slot:item="slotProps"> <div class="item"> {{ slotProps.item.text }} </div> </template> </InfiniteScroll> <!-- 接收 prop 的默认插槽,并解构 --> <Mouse v-slot="{ x, y }"> Mouse position: {{ x }}, {{ y }} </Mouse>
-
参考:
1.5.16、v-pre
跳过该元素及其所有子元素的编译。
-
无需传入
-
详细信息
元素内具有
v-pre
,所有 Vue 模板语法都会被保留并按原样渲染。最常见的用例就是显示原始双大括号标签及内容。 -
<template> <div v-pre> <h2> {{ msg }} </h2> </div> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { let msg = ref("apple"); return { msg }; }, }; </script> <style scoped> </style>
-
示例:
-
<span v-pre>{{ this will not be compiled }}</span>
1.5.17、v-once
仅渲染元素和组件一次,并跳过之后的更新。
-
无需传入
-
详细信息
在随后的重新渲染,元素/组件及其所有子项将被当作静态内容并跳过渲染。这可以用来优化更新时的性能。
-
<!-- 单个元素 --> <span v-once>This will never change: {{msg}}</span> <!-- 带有子元素的元素 --> <div v-once> <h1>comment</h1> <p>{{msg}}</p> </div> <!-- 组件 --> <MyComponent v-once :comment="msg" /> <!-- `v-for` 指令 --> <ul> <li v-for="i in list" v-once>{{i}}</li> </ul>
-
从 3.2 起,你也可以搭配
v-memo
的无效条件来缓存部分模板。 -
参考:
1.5.18、v-memo
-
期望的绑定值类型:
any[]
-
详细信息
缓存一个模板的子树。在元素和组件上都可以使用。为了实现缓存,该指令需要传入一个固定长度的依赖值数组进行比较。如果数组里的每个值都与最后一次的渲染相同,那么整个子树的更新将被跳过。举例来说:
-
<div v-memo="[valueA, valueB]"> ... </div>
-
<template> valueA:<input v-model="valueA" /> msg:<input v-model="msg" /> <div v-memo="[valueA]"> <h2>msg:{{ msg }} {{ show() }}</h2> </div> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { let msg = ref("apple"); let valueA = ref("ok"); function show() { console.log("msg被渲染了"); } return { msg, valueA, show }; }, }; </script> <style scoped></style>
当组件重新渲染,如果
valueA
和valueB
都保持不变,这个<div>
及其子项的所有更新都将被跳过。实际上,甚至虚拟 DOM 的 vnode 创建也将被跳过,因为缓存的子树副本可以被重新使用。正确指定缓存数组很重要,否则应该生效的更新可能被跳过。
v-memo
传入空依赖数组 (v-memo="[]"
) 将与v-once
效果相同。 -
<template> <div v-memo="msg"> <h2>{{ msg }} {{ info }}</h2> </div> msg<input v-model="msg" /> <hr /> info<input v-model="info" /> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { let msg = ref("apple"); let info = ref("pear"); return { msg, info }; }, }; </script> <style scoped></style>
上面的示例中只有当msg更新时才重新渲染界面
-
与
v-for
一起使用v-memo
仅用于性能至上场景中的微小优化,应该很少需要。最常见的情况可能是有助于渲染海量v-for
列表 (长度超过 1000 的情况): -
<div v-for="item in list" :key="item.id" v-memo="[item.id === selected]"> <p>ID: {{ item.id }} - selected: {{ item.id === selected }}</p> <p>...more child nodes</p> </div>
-
当组件的
selected
状态改变,默认会重新创建大量的 vnode,尽管绝大部分都跟之前是一模一样的。v-memo
用在这里本质上是在说“只有当该项的被选中状态改变时才需要更新”。这使得每个选中状态没有变的项能完全重用之前的 vnode 并跳过差异比较。注意这里 memo 依赖数组中并不需要包含item.id
,因为 Vue 也会根据 item 的:key
进行判断。警告
当搭配
v-for
使用v-memo
,确保两者都绑定在同一个元素上。v-memo
不能用在v-for
内部。v-memo
也能被用于在一些默认优化失败的边际情况下,手动避免子组件出现不需要的更新。但是一样的,开发者需要负责指定正确的依赖数组以免跳过必要的更新。 -
<template> <ul v-for="(item, index) in users" :key="item.id" v-memo="[selected == item.id]" > <input name="user" type="radio" :value="item.id" v-on:change="selected = item.id" :checked="selected === item.id" /> {{ item.id }} - {{ item.name }} - {{ showMsg(item) }} - selected: {{ item.id === selected }} </ul> 选择编号:{{ selected }} <input v-model="selected" /> <input v-model="key" /> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { let users = reactive([ { id: 201, name: "mark" }, { id: 202, name: "rose" }, { id: 203, name: "jack" }, { id: 204, name: "lucy" }, ]); let selected = ref(0); function showMsg(item) { console.log("渲染了", item); } let key = ref("100"); return { users, selected, showMsg, key }; }, }; </script> <style scoped> .div1 { height: 200px; overflow: auto; width: 300px; background-color: lightgoldenrodyellow; } .div1 ul li { height: 300px; line-height: 300px; } </style>
-
参考:
1.5.19、v-cloak
用于隐藏尚未完成编译的 DOM 模板。
-
无需传入
-
详细信息
该指令只在没有构建步骤的环境下需要使用。
当使用直接在 DOM 中书写的模板时,可能会出现一种叫做“未编译模板闪现”的情况:用户可能先看到的是还没编译完成的双大括号标签,直到挂载的组件将它们替换为实际渲染的内容。
v-cloak
会保留在所绑定的元素上,直到相关组件实例被挂载后才移除。配合像[v-cloak] { display: none }
这样的 CSS 规则,它可以在组件编译完毕前隐藏原始模板。 -
示例:
[v-cloak] { display: none; }
<div v-cloak> {{ message }} </div>
直到编译完成前,
<div>
将不可见。 -
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> /* 使用属性选择器 */ [v-cloak] { display: none; } </style> </head> <body> <!-- 在这个标签中中编写Vue的视图 --> <div id="app"> <!-- 1直接进行使用 --> <p v-cloak>{{str}}</p> <hr /> <!-- 2字符拼接进行输出 --> <p>{{str + '拼接文本'}}</p> <!-- 3进行数学运算 --> <p>{{num +10}}</p> <!-- 4三元表达式 --> <p>{{age > 18 ? '成年人' : '未成年' }}</p> <!-- 5 使用方法,将字符串进行剪切 --> <p>{{str.substr(1,5)}}</p> </div> </body> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script> // !创建Vue实例 new Vue({ // 通过el绑定元素 el: "#app", // 所有的数据都放在data中 data: { // 定义一个数据 str: "Vue里面data里面的数据", num: 10, age: 13, str: "0123456789", }, }); </script> </html>
1.6、重要的v-for指令
1.6.1、v-for
我们可以使用 v-for
指令基于一个数组来渲染一个列表。v-for
指令的值需要使用 item in items
形式的特殊语法,其中 items
是源数据的数组,而 item
是迭代项的别名:
const items = ref([{ message: 'Foo' }, { message: 'Bar' }]) <li v-for="item in items"> {{ item.message }} </li>
在 v-for
块中可以完整地访问父作用域内的属性和变量。v-for
也支持使用可选的第二个参数表示当前项的位置索引。
const parentMessage = ref('Parent') const items = ref([{ message: 'Foo' }, { message: 'Bar' }]) <li v-for="(item, index) in items"> {{ parentMessage }} - {{ index }} - {{ item.message }} </li>
- Parent - 0 - Foo
- Parent - 1 - Bar
v-for
变量的作用域和下面的 JavaScript 代码很类似:
const parentMessage = 'Parent' const items = [ /* ... */ ] items.forEach((item, index) => { // 可以访问外层的 `parentMessage` // 而 `item` 和 `index` 只在这个作用域可用 console.log(parentMessage, item.message, index) })
注意 v-for
是如何对应 forEach
回调的函数签名的。实际上,你也可以在定义 v-for
的变量别名时使用解构,和解构函数参数类似:
<li v-for="{ message } in items"> {{ message }} </li> <!-- 有 index 索引时 --> <li v-for="({ message }, index) in items"> {{ message }} {{ index }} </li>
对于多层嵌套的 v-for
,作用域的工作方式和函数的作用域很类似。每个 v-for
作用域都可以访问到父级作用域:
<li v-for="item in items"> <span v-for="childItem in item.children"> {{ item.message }} {{ childItem }} </span> </li>
你也可以使用 of
作为分隔符来替代 in
,这更接近 JavaScript 的迭代器语法:
<div v-for="item of items"></div>
1.6.2、v-for
与对象
你也可以使用 v-for
来遍历一个对象的所有属性。遍历的顺序会基于对该对象调用 Object.keys()
的返回值来决定。
const myObject = reactive({ title: 'How to do lists in Vue', author: 'Jane Doe', publishedAt: '2016-04-10' }) <ul> <li v-for="value in myObject"> {{ value }} </li> </ul>
可以通过提供第二个参数表示属性名 (例如 key):
<li v-for="(value, key) in myObject"> {{ key }}: {{ value }} </li>
第三个参数表示位置索引:
<li v-for="(value, key, index) in myObject"> {{ index }}. {{ key }}: {{ value }} </li>
1.6.3、在 v-for
里使用范围值
v-for
可以直接接受一个整数值。在这种用例中,会将该模板基于 1...n
的取值范围重复多次。
<span v-for="n in 10">{{ n }}</span>
注意此处 n
的初值是从 1
开始而非 0
。
1.6.4、template 上的v-for
与模板上的 v-if
类似,你也可以在 <template>
标签上使用 v-for
来渲染一个包含多个元素的块。例如:
<ul> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider" role="presentation"></li> </template> </ul>
1.6.3、v-for
与 v-if
注意
同时使用 v-if
和 v-for
是不推荐的,因为这样二者的优先级不明显。请转阅风格指南查看更多细节。
当它们同时存在于一个节点上时,v-if
比 v-for
的优先级更高。这意味着 v-if
的条件将无法访问到 v-for
作用域内定义的变量别名:
<!-- 这会抛出一个错误,因为属性 todo 此时 没有在该实例上定义 --> <li v-for="todo in todos" v-if="!todo.isComplete"> {{ todo.name }} </li>
在外新包装一层 <template>
再在其上使用 v-for
可以解决这个问题 (这也更加明显易读):
<template v-for="todo in todos"> <li v-if="!todo.isComplete"> {{ todo.name }} </li> </template>
1.6.4、通过 key 管理状态
Vue 默认按照“就地更新”的策略来更新通过 v-for
渲染的元素列表。当数据项的顺序改变时,Vue 不会随之移动 DOM
元素的顺序,而是就地更新每个元素,确保它们在原本指定的索引位置上渲染。
默认模式是高效的,但只适用于列表渲染输出的结果不依赖子组件状态或者临时 DOM 状态 (例如表单输入值) 的情况。
为了给 Vue 一个提示,以便它可以跟踪每个节点的标识,从而重用和重新排序现有的元素,你需要为每个元素对应的块提供一个唯一的 key
attribute:
<div v-for="item in items" :key="item.id"> <!-- 内容 --> </div>
当你使用 <template v-for>
时,key
应该被放置在这个 <template>
容器上:
<template v-for="todo in todos" :key="todo.name"> <li>{{ todo.name }}</li> </template>
注意
key
在这里是一个通过 v-bind
绑定的特殊 attribute。请不要和在 v-for
中使用对象里所提到的对象属性名相混淆。
推荐在任何可行的时候为 v-for
提供一个 key
attribute,除非所迭代的 DOM 内容非常简单 (例如:不包含组件或有状态的 DOM 元素),或者你想有意采用默认行为来提高性能。
key
绑定的值期望是一个基础类型的值,例如字符串或 number 类型。不要用对象作为 v-for
的 key。关于 key
attribute 的更多用途细节,请参阅 key
API 文档。
示例:
<template> id:<input type="text" v-model="id" />,name:<input type="text" v-model="name" /><button @click="add1">在末尾添加新人物</button> <button @click="add2">在前部添加新人物</button> <div v-for="item in list"> <input type="checkbox" /> {{ item.id }}.{{ item.name }} </div> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { let list = reactive([ { id: 1, name: "刘备", }, { id: 2, name: "孙权", }, { id: 3, name: "曹操", }, { id: 4, name: "诸葛亮", }, ]); let id = ref(0); let name = ref(""); function add1() { list.push({ id: id.value, name: name.value, }); } function add2() { list.unshift({ id: id.value, name: name.value, }); } return { list, id, name, add1, add2 }; }, }; </script> <style scoped></style>
事先勾选3.曹操,使用push方法王数组中添加对象并没有什么问题
但是,如果将push修改unshift方式添加对象,
v就会发现执行同样的操作时,原先勾选曹操变成了勾选孙权
这是因为勾选是和索引绑定的,当使用unshift方法时,原先的数组对象索引都加1,所以勾选的位置就往回退了一个对象,但是索引仍然不变。因此,就需要将checkbox的勾选和对象本身绑定,而非其索引,这里就需要使用到key。
前面说到需要将checkbox和对象数组中的对象绑定,但是当我们输入如下代码却发现页面报错
<div v-for="item in list" :key="item"> <input type="checkbox"> {{item.id}}.{{item.name}} </div>
所以可以得出v-for循环时,key属性只能使用number或者string,而不是整个对象,修改代码
key的作用主要是为了高效的更新虚拟DOM
1、在组件中使用v-for循环的时候,如果v-for出现了问题,就必须同时指定唯一的字符串/数字类型的:key值。
2、v-for循环时,key属性只能用number或者string类型。
3、key使用时,必须使用bind属性绑定的形式,指定key的值。
6.5、组件上使用 v-for
这一小节假设你已了解组件的相关知识,或者你也可以先跳过这里,之后再回来看。
我们可以直接在组件上使用 v-for
,和在一般的元素上使用没有区别 (别忘记提供一个 key
):
<MyComponent v-for="item in items" :key="item.id" />
但是,这不会自动将任何数据传递给组件,因为组件有自己独立的作用域。为了将迭代后的数据传递到组件中,我们还需要传递 props:
<MyComponent v-for="(item, index) in items" :item="item" :index="index" :key="item.id" />
不自动将 item
注入组件的原因是,这会使组件与 v-for
的工作方式紧密耦合。明确其数据的来源可以使组件在其他情况下重用。
这里是一个简单的 Todo List 的例子,展示了如何通过 v-for
来渲染一个组件列表,并向每个实例中传入不同的数据。
1.6.6、组件 和 v-for
了解组件相关知识,查看 组件 。Feel free to skip it and come back later.
在自定义组件里,你可以像任何普通元素一样用 v-for
。
<my-component v-for="item in items"></my-component>
然而他不能自动传递数据到组件里,因为组件有自己独立的作用域。为了传递迭代数据到组件里,我们要用 props
:
<my-component v-for="(item, index) in items" v-bind:item="item" v-bind:index="index"> </my-component>
不自动注入 item
到组件里的原因是,因为这使得组件会紧密耦合到 v-for
如何运作。在一些情况下,明确数据的来源可以使组件可重用。
下面是一个简单的 todo list 完整的例子:
<div id="todo-list-example"> <input v-model="newTodoText" v-on:keyup.enter="addNewTodo" placeholder="Add a todo" > <ul> <li is="todo-item" v-for="(todo, index) in todos" v-bind:title="todo" v-on:remove="todos.splice(index, 1)" ></li> </ul> </div>
Vue.component('todo-item', { template: ' <li> {{ title }} <button v-on:click="$emit(\'remove\')">X</button> </li>', props: ['title'] }) new Vue({ el: '#todo-list-example', data: { newTodoText: '', todos: [ 'Do the dishes', 'Take out the trash', 'Mow the lawn' ] }, methods: { addNewTodo: function () { this.todos.push(this.newTodoText) this.newTodoText = '' } } })
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>列表渲染</title> </head> <body> <div id="app1"> 任务:<input v-model="newTask" @keyup.enter="addNew" placeholder="请输入您要完成的任务" /> <ul> <li is="todoitem" v-for="(task,index) in tasks" :title="task" @remove="removeItem(index)"></li> </ul> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> Vue.component("todoitem", { template: "<li>{{title}} <button @click='$emit(\"remove\")'>X</button></li>", props: ['title'] }); var app1 = new Vue({ el: "#app1", data: { newTask: '', tasks: ["买一本书", "给爸妈打电话", "整理自己的硬盘"] }, methods: { addNew: function() { this.tasks.unshift(this.newTask); this.newTask = ''; }, removeItem: function(index) { if(confirm('确定要移除吗?')) { this.tasks.splice(index, 1); } } } }); </script> </body> </html>
结果:
1.7、数组变化侦测
1.7.1、变更方法
Vue 能够侦听响应式数组的变更方法,并在它们被调用时触发相关的更新。这些变更方法包括:
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
1. push () 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
2. pop () 方法数组最后一位元素删除并返回数组的最后一个元素。
3. shift () 方法用于把数组的第一个元素从其中删除,并返回第一个元素的值。
4. unshift () 方法可向数组的开头添加一个或更多元素,并返回新的长度。
5.splice (index,howmany,item1, …, itemX) 方法向 / 从数组中添加 / 删除项目,然后返回被删除的项目
第一个参数:表示从哪个索引位置(index)添加 / 删除元素
第二个参数:要删除的项目数量。如果设置为 0,则不会删除项目。
第三个参数:可选。向数组添加的新项目。
6. sort () 方法对原列表进行排序,如果指定参数,则使用比较函数指定的比较函数。
arr.sort (sortby) 可选。规定排序顺序。必须是函数。
7. 例:大小排序
function sortNumber (a, b) {
return a - b
}
let arr = [10,5,40,25,1000,1]
arr.sort(sortNumber)
console.log(arr) // [1, 5, 10, 25, 40, 1000]
8. reverse () 方法颠倒数组中元素的顺序。
9. 替换数组
它们不会变更原始数组,而总是返回一个新数组。当使用非变更方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
10.filter () 方法创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
11.concat () 方法用于连接两个或多个数组。
12.slice () 方法可从已有的数组中返回选定的元素。
1.7.2、替换一个数组
变更方法,顾名思义,就是会对调用它们的原数组进行变更。相对地,也有一些不可变 (immutable) 方法,例如 filter()
,concat()
和 slice()
,这些都不会更改原数组,而总是返回一个新数组。当遇到的是非变更方法时,我们需要将旧的数组替换为新的:
// `items` 是一个数组的 ref
items.value = items.value.filter((item) => item.message.match(/Foo/))
你可能认为这将导致 Vue 丢弃现有的 DOM 并重新渲染整个列表——幸运的是,情况并非如此。Vue 实现了一些巧妙的方法来最大化对 DOM 元素的重用,因此用另一个包含部分重叠对象的数组来做替换,仍会是一种非常高效的操作。
<template> {{ arr1 }} <button @click="changeArray">修改数组</button> </template> <script lang="ts"> import { reactive, ref } from "vue"; export default { setup() { let arr1 = ref([1, 2, 3, 4, 5, 6]); function changeArray() { arr1.value = arr1.value.filter((a) => a % 2 == 0); } return { arr1, changeArray }; }, }; </script> <style scoped></style>
1.7.3、展示过滤或排序后的结果
有时,我们希望显示数组经过过滤或排序后的内容,而不实际变更或重置原始数据。在这种情况下,你可以创建返回已过滤或已排序数组的计算属性。
举例来说:
const numbers = ref([1, 2, 3, 4, 5]) const evenNumbers = computed(() => { return numbers.value.filter((n) => n % 2 === 0) }) <li v-for="n in evenNumbers">{{ n }}</li>
在计算属性不可行的情况下 (例如在多层嵌套的 v-for
循环中),你可以使用以下方法:
const sets = ref([ [1, 2, 3, 4, 5], [6, 7, 8, 9, 10] ]) function even(numbers) { return numbers.filter((number) => number % 2 === 0) } <ul v-for="numbers in sets"> <li v-for="n in even(numbers)">{{ n }}</li> </ul>
在计算属性中使用 reverse()
和 sort()
的时候务必小心!这两个方法将变更原始数组,计算函数中不应该这么做。请在调用这些方法之前创建一个原数组的副本:
- return numbers.reverse()
+ return [...numbers].reverse()
二、Class 与 Style 绑定
数据绑定一个常见需求是操作元素的 class 列表和它的内联样式。因为它们都是属性 ,我们可以用v-bind
处理它们:只需要计算出表达式最终的字符串。不过,字符串拼接麻烦又易错。因此,在 v-bind
用于 class
和 style
时, Vue.js 专门增强了它。表达式的结果类型除了字符串之外,还可以是对象或数组。
2.1、绑定 HTML Class
2.1.1、对象语法
我们可以传给 v-bind:class
一个对象,以动态地切换 class 。
<div v-bind:class="{ active: isActive }"></div>
上面的语法表示 classactive
的更新将取决于数据属性 isActive
是否为 真值 。
<template> <h2 :class="{ active: isActive }">{{ isActive ? "激活" : "未激活" }}</h2> <button @click="isActive = !isActive">isActive:{{ isActive }}</button> </template> <script lang="ts" setup> import { computed } from "@vue/reactivity"; import { reactive, ref, toRefs } from "vue"; let isActive = ref(false); </script> <style scoped> .active { background: #9f9; } </style>
我们也可以在对象中传入更多属性用来动态切换多个 class 。此外, v-bind:class
指令可以与普通的 class 属性共存。如下模板:
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"> </div>
如下 data:
data: { isActive: true, hasError: false }
渲染为:
<div class="static active"></div>
当 isActive
或者 hasError
变化时,class 列表将相应地更新。例如,如果 hasError
的值为 true
, class列表将变为 "static active text-danger"
。
你也可以直接绑定数据里的一个对象:
<div v-bind:class="classObject"></div>
data: { classObject: { active: true, 'text-danger': false } }
渲染的结果和上面一样。我们也可以在这里绑定返回对象的 计算属性。这是一个常用且强大的模式:
<div v-bind:class="classObject"></div>
data: { isActive: true, error: null }, computed: { classObject: function () { return { active: this.isActive && !this.error, 'text-danger': this.error && this.error.type === 'fatal', } } }
示例:
<template> <h2 :class="{ active: isActive, 'text-danger': true }"> {{ isActive ? "激活" : "未激活" }} </h2> <button @click="isActive = !isActive">isActive:{{ isActive }}</button> <p> <label>价格:</label> <input v-model="price" /> <span class="valid" :class="{ requiredPrice }">请输入价格</span> <span class="valid" :class="{ rangePrice }">价格只能是0-10000之间</span> </p> </template> <script lang="ts" setup> import { computed } from "@vue/reactivity"; import { reactive, ref, toRefs } from "vue"; let isActive = ref(false); let price = ref(); let requiredPrice = computed(() => { return price + "" === "" || isNaN(price.value); }); let rangePrice = computed(() => { return price.value < 0 || price.value > 10000; }); </script> <style scoped> .active { background: #9f9; } .text-danger { color: orangered; } .valid { color: orangered; display: none; } .requiredPrice, .rangePrice { display: inline; } </style>
结果:
2.1.2、数组语法
我们可以把一个数组传给 v-bind:class
,以应用一个 class 列表:
<div v-bind:class="[activeClass, errorClass]">
data: { activeClass: 'active', errorClass: 'text-danger' }
渲染为:
<div class="active text-danger"></div>
如果你也想根据条件切换列表中的 class ,可以用三元表达式:
<div v-bind:class="[isActive ? activeClass : '', errorClass]">
此例始终添加 errorClass
,但是只有在 isActive
是 true 时添加 activeClass
。
不过,当有多个条件 class 时这样写有些繁琐。可以在数组语法中使用对象语法:
<div v-bind:class="[{ active: isActive }, errorClass]">
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Class 与 Style 绑定</title> </head> <body> <div id="app1"> <span class="bgGreen" v-bind:class="[isHide,isRight]">span3</span> <span class="bgGreen" v-bind:class="[(isShow?'bg3':''),isRight,bg4]">span4</span> <span class="bgGreen" v-bind:class="[{bg3:isShow},isRight,bg4]">span5</span> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> var app1 = new Vue({ el: "#app1", data: { isShow: true, isHide: 'bg1', isRight: 'bg2', price: 0 } }); </script> </body> </html>
结果:
2.1.3、With Components
This section assumes knowledge of Vue Components. Feel free to skip it and come back later.
When you use the class
attribute on a custom component, those classes will be added to the component’s root element. Existing classes on this element will not be overwritten.
For example, if you declare this component:
Vue.component('my-component', { template: '<p class="foo bar">Hi</p>' })
Then add some classes when using it:
<my-component class="baz boo"></my-component>
The rendered HTML will be:
<p class="foo bar baz boo">Hi</p>
The same is true for class bindings:
<my-component v-bind:class="{ active: isActive }"></my-component>
When isActive
is truthy, the rendered HTML will be:
<div class="foo bar active"></div>
2.2、绑定内联样式
2.2.1、对象语法
v-bind:style
的对象语法十分直观——看着非常像 CSS ,其实它是一个 JavaScript 对象。 CSS 属性名可以用驼峰式(camelCase)或短横分隔命名(kebab-case):
<template> <h2 :style="{ background: background, fontSize: fontSize + 'px' }">样式表</h2> </template> <script lang="ts" setup> import { computed } from "@vue/reactivity"; import { reactive, ref, toRefs } from "vue"; let background = ref("red"); let fontSize = ref(30); </script> <style scoped> </style>
直接绑定到一个样式对象通常更好,让模板更清晰:
<template> <h2 :style="objStyle">样式表</h2> </template> <script lang="ts" setup> let objStyle = { color: "blue", textDecoration: "underline", }; </script> <style scoped></style>
同样的,对象语法常常结合返回对象的计算属性使用。
2.2.2、数组语法
v-bind:style
的数组语法可以将多个样式对象应用到一个元素上:
<div v-bind:style="[baseStyles, overridingStyles]">
<template> <h2 :style="[objStyle, borderStyle]">样式表</h2> </template> <script lang="ts" setup> let borderStyle = { border: "1px solid #666", }; let objStyle = { color: "blue", textDecoration: "underline", }; </script> <style scoped></style>
2.2.3、自动添加前缀
当 v-bind:style
使用需要特定前缀的 CSS 属性时,如 transform
,Vue.js 会自动侦测并添加相应的前缀。
官方帮助: http://vuejs.org/guide/class-and-style.html
2.3、引入外部样式
1.main.ts 引入(推荐)
一般src目录下新建styles文件夹, 该方式能解析less的语法
import "@/styles/index.less";
2.App.vue的style标签内引入
<style>
@import "./styles/index.less"
</style>
3.index.html 引入
<link href="index.css" type="text/css" rel="stylesheet" /> #文件引入
#或者
<style>
.test{
color: red;
}
</style>
三、ES6新增数组方法
ECMAScript2015中新增了9个方法,分别是:
- Array.prototype.indexOf
- Array.prototype.lastIndexOf
- Array.prototype.every
- Array.prototype.some
- Array.prototype.forEach
- Array.prototype.map
- Array.prototype.filter
- Array.prototype.reduce
- Array.prototype.reduceRight
3.1、indexOf()找到的元素位置
indexOf()方法返回在该数组中第一个找到的元素位置,如果它不存在则返回-1。
不使用indexOf时:
var arr = ['apple','orange','pear'], found = false; for(var i= 0, l = arr.length; i< l; i++){ if(arr[i] === 'orange'){ found = true; } } console.log("found:",found);
使用后:
var arr = ['apple','orange','pear']; console.log("found:", arr.indexOf("orange") != -1);
3.2、filter()过滤
该filter()方法创建一个新的匹配过滤条件的数组。
不用 filter() 时
var arr = [ {"name":"apple", "count": 2}, {"name":"orange", "count": 5}, {"name":"pear", "count": 3}, {"name":"orange", "count": 16}, ]; var newArr = []; for(var i= 0, l = arr.length; i< l; i++){ if(arr[i].name === "orange" ){ newArr.push(arr[i]); } } console.log("Filter results:",newArr);
用了 filter():
var arr = [ {"name":"apple", "count": 2}, {"name":"orange", "count": 5}, {"name":"pear", "count": 3}, {"name":"orange", "count": 16}, ]; var newArr = arr.filter(function(item){ return item.name === "orange"; }); console.log("Filter results:",newArr);
3.3、forEach()迭代
forEach为每个元素执行对应的方法
var arr = [1,2,3,4,5,6,7,8]; // Uses the usual "for" loop to iterate for(var i= 0, l = arr.length; i< l; i++){ console.log(arr[i]); } console.log("========================"); //Uses forEach to iterate arr.forEach(function(item,index){ console.log(item); });
forEach是用来替换for循环的
3.4、map()映射
map()对数组的每个元素进行一定操作(映射)后,会返回一个新的数组
不使用map:
var oldArr = [{first_name:"Colin",last_name:"Toh"},{first_name:"Addy",last_name:"Osmani"},{first_name:"Yehuda",last_name:"Katz"}]; function getNewArr(){ var newArr = []; for(var i= 0, l = oldArr.length; i< l; i++){ var item = oldArr[i]; item.full_name = [item.first_name,item.last_name].join(" "); newArr[i] = item; } return newArr; } console.log(getNewArr());
使用map后:
var oldArr = [{first_name:"Colin",last_name:"Toh"},{first_name:"Addy",last_name:"Osmani"},{first_name:"Yehuda",last_name:"Katz"}]; function getNewArr(){ return oldArr.map(function(item,index){ item.full_name = [item.first_name,item.last_name].join(" "); return item; }); } console.log(getNewArr());
map()是处理服务器返回数据时是一个非常实用的函数。
3.5、reduce()累加器
reduce()可以实现一个累加器的功能,将数组的每个值(从左到右)将其降低到一个值。
说实话刚开始理解这句话有点难度,它太抽象了。
场景: 统计一个数组中有多少个不重复的单词
不使用reduce时:
var arr = ["apple","orange","apple","orange","pear","orange"]; function getWordCnt(){ var obj = {}; for(var i= 0, l = arr.length; i< l; i++){ var item = arr[i]; obj[item] = (obj[item] +1 ) || 1; } return obj; } console.log(getWordCnt());
使用reduce()后
var arr = ["apple","orange","apple","orange","pear","orange"]; function getWordCnt(){ return arr.reduce(function(prev,next){ prev[next] = (prev[next] + 1) || 1; return prev; },{}); } console.log(getWordCnt());
让我先解释一下我自己对reduce的理解。reduce(callback, initialValue)会传入两个变量。回调函数(callback)和初始值(initialValue)。假设函数它有个传入参数,prev和next,index和array。prev和next你是必须要了解的。
一般来讲prev是从数组中第一个元素开始的,next是第二个元素。但是当你传入初始值(initialValue)后,第一个prev将是initivalValue,next将是数组中的第一个元素。
比如:
/* * 二者的区别,在console中运行一下即可知晓 */ var arr = ["apple","orange"]; function noPassValue(){ return arr.reduce(function(prev,next){ console.log("prev:",prev); console.log("next:",next); return prev + " " +next; }); } function passValue(){ return arr.reduce(function(prev,next){ console.log("prev:",prev); console.log("next:",next); prev[next] = 1; return prev; },{}); } console.log("No Additional parameter:",noPassValue()); console.log("----------------"); console.log("With {} as an additional parameter:",passValue());
示例:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>列表渲染</title> </head> <body> <div id="app1"> <span v-for="n in items"> {{n}} </span> <button @click="indexOfMethod">indexOf()找到的元素位置</button> <button @click="filterMethod">filter()过滤</button> <button @click="forEachMethod">forEach()迭代</button> <button @click="mapMethod">map()映射</button> <button @click="reduceMethod">reduce()累加器</button> </div> <script src="../js/vue.js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> var app1 = new Vue({ el: "#app1", data: { items: [1, 3, 7, 9, 2, 4, 6, 8, 3], fruits: [{ "name": "apple", "count": 2 }, { "name": "orange", "count": 5 }, { "name": "pear", "count": 3 }, { "name": "orange", "count": 16 } ], words: ["apple", "orange", "apple", "orange", "pear", "orange"] }, methods: { indexOfMethod: function() { console.log("数字3第一次出现的位置是:" + this.items.indexOf(3)); console.log("数字5第一次出现的位置是:" + this.items.indexOf(5)); }, filterMethod: function() { //获得数量不小于5的水果 var arr1 = this.fruits.filter(function(f) { return f.count >= 5; }); console.log(JSON.stringify(arr1)); //获得名称中含有r的水果 var arr2 = this.fruits.filter(function(f) { return f.name.match(/r/igm); }); console.log(JSON.stringify(arr2)); }, forEachMethod: function() { this.fruits.forEach(function(obj, index) { console.log(index + "-" + obj.name + "-" + obj.count); }); }, mapMethod: function() { var arr3 = this.fruits.map(function(obj, index) { obj.showInfo = index + "->水果:" + obj.name + ",数量:" + obj.count; return obj; }); console.log(JSON.stringify(arr3)); }, reduceMethod: function() { var objs = {}; for(var i = 0, l = this.words.length; i < l; i++) { var item = this.words[i]; objs[item] = (objs[item] + 1) || 1; } console.log(JSON.stringify(objs)); var objs2 = this.words.reduce(function(prev, next) { console.log("prev:", JSON.stringify(prev)); console.log("next:", JSON.stringify(next)); prev[next] = (prev[next] + 1) || 1; return prev; }, {}); console.log(JSON.stringify(objs2)); } } }); </script> </body> </html>
结果:
结果
四、VUE UI框架
4.1、PC端UI库
4.1.1、 element-plus(PC端)
饿了么UI库
文档地址:https://element-plus.org/zh-CN/
npm install element-plus --save
4.1.2、完整引入
如果你对打包后的文件大小不是很在乎,那么使用完整导入会更方便。
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')
4.1.3、Volar 支持
如果您使用 Volar,请在 tsconfig.json
中通过 compilerOptions.type
指定全局组件类型。
// tsconfig.json
{
"compilerOptions": {
// ...
"types": ["element-plus/global"]
}
}
4.1.4、按需导入
您需要使用额外的插件来导入要使用的组件。
自动导入推荐
首先你需要安装unplugin-vue-components
和 unplugin-auto-import
这两款插件
npm install -D unplugin-vue-components unplugin-auto-import
然后把下列代码插入到你的 Vite
或 Webpack
的配置文件中
// webpack.config.js
const { defineConfig } = require('@vue/cli-service')
// webpack.config.js
const AutoImport = require('unplugin-auto-import/webpack')
const Components = require('unplugin-vue-components/webpack')
const { ElementPlusResolver } = require('unplugin-vue-components/resolvers')
module.exports = defineConfig({
transpileDependencies: true,
configureWebpack:{
plugins: [
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
}
})
想了解更多打包 (Rollup, Vue CLI) 和配置工具,请参考 unplugin-vue-components 和 unplugin-auto-import。
4.1.5、手动导入
Element Plus 提供了基于 ES Module 开箱即用的 Tree Shaking 功能。
但是你需要安装 unplugin-element-plus 来导入样式。 请参考 文档 了解如何配置它。
App.vue
<template>
<el-button>I am ElButton</el-button>
</template>
<script>
import { ElButton } from 'element-plus'
export default {
components: { ElButton },
}
</script>
// vite.config.ts
import { defineConfig } from 'vite'
import ElementPlus from 'unplugin-element-plus/vite'
export default defineConfig({
// ...
plugins: [ElementPlus()],
})
WARNING
如果您使用 unplugin-element-plus
并且只使用组件 API,您需要手动导入样式。
示例︰
import 'element-plus/es/components/message/style/css'
import { ElMessage } from 'element-plus'
4.1.6、快捷搭建项目模板
我们提供了 Vite 模板。 对于 Laravel 用户,我们也准备了相应的模板,同样可以直接下载使用。
4.1.7、全局配置
在引入 Element Plus 时,可以传入一个包含 size
和 zIndex
属性的全局配置对象。 size
用于设置表单组件的默认尺寸,zIndex
用于设置弹出组件的层级,zIndex
的默认值为 2000。
完整引入:
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus, { size: 'small', zIndex: 3000 })
按需引入:
<template>
<el-config-provider :size="size" :z-index="zIndex">
<app />
</el-config-provider>
</template>
<script>
import { defineComponent } from 'vue'
import { ElConfigProvider } from 'element-plus'
export default defineComponent({
components: {
ElConfigProvider,
},
setup() {
return {
zIndex: 3000,
size: 'small',
}
},
})
</script>
4.1.18、CDN引入在HTML中直接使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <!-- Import style --> <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/element-plus/dist/index.css" /> <!-- Import Vue 3 --> <script src="//cdn.jsdelivr.net/npm/vue@3"></script> <!-- Import component library --> <script src="//cdn.jsdelivr.net/npm/element-plus"></script> </head> <body> <div id="app"> <el-button @click="messageHandle">{{message}}</el-button> </div> <script> const { ElMessage } = ElementPlus; let app = Vue.createApp({ data() { return { message: "Hello Element Plus!", }; }, setup() { function messageHandle() { ElMessage({ message: "操作成功了!", type: "success", "show-close": true, }); } return { messageHandle }; }, }) .use(ElementPlus) .mount("#app"); </script> </body> </html>
2. tdesign-vue-next(PC端)
腾讯优质 UI 组件,配套工具完满,设计工整,文档清晰
文档地址:https://tdesign.tencent.com/vue-next/overview
3. arco-design-vue(PC端)
字节跳动 UI 组件库开源,大厂逻辑,设计文档完美
文档地址:https://arco.design/vue/docs/start
4. ant-design-vue(PC端)
蚂蚁前端 UI 库,面向企业级中后台
文档地址:https://www.antdv.com/components/overview-cn
5. naive-ui(PC端)
宝藏 Vue UI 库,Vue UI 新星,从 Vue 3 起步
文档地址:https://www.naiveui.com/zh-CN/os-theme/docs/introduction
6. Buefy(PC端)
为基于 Bulma 的 Vue.js 提供了轻量级的 UI 组件
文档地址:https://buefy.org/documentation
7. Vue Material(PC端)
简单、轻巧,并且完全按照 Google Material Design 规范构建
文档地址:https://www.creative-tim.com/vuematerial/components/app
8. Vuesax(PC端)
一个用Vuesax创建的UI组件框架,具有独特和愉快的风格
文档地址:https://vuesax.com/docs/guide/
9. Vuestic(PC端)
完全兼容Vue.js 3,包含50多个功能丰富的组件,可用于任何设计解决方案,允许通过配置和CSS变量全局配置组件,完全响应和支持现代浏览器(除IE11),与i18n无缝集成
文档地址:https://vuestic.dev/zh/introduction/overview
10. View UI Plus(PC端)
View UI Plus 是 View Design 设计体系中基于 Vue.js 3 的一套 UI 组件库,主要用于企业级中后台系统。
文档地址:https://www.iviewui.com/view-ui-plus/guide/introduce
https://github.com/iview/iview
https://iviewui.com/
4.2、移动端UI库
4.2.1. vant-ui(移动端)
有赞团队开源移动 UI 组件库,全面支持 Vue 3,针对移动端
文档地址:https://vant-contrib.gitee.io/vant/#/zh-CN
4.2.2、在HTML中直接使用vant,CDN依赖
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <!-- 引入样式文件 --> <link rel="stylesheet" href="https://fastly.jsdelivr.net/npm/vant@3/lib/index.css" /> </head> <body> <div id="app"> <van-button @click="showInfo">按钮</van-button> </div> <!-- 引入 Vue 和 Vant 的 JS 文件 --> <script src="https://fastly.jsdelivr.net/npm/vue@3"></script> <script src="https://fastly.jsdelivr.net/npm/vant@3/lib/vant.min.js"></script> <script> // 在 #app 标签下渲染一个按钮组件 const app = Vue.createApp({ setup() { function showInfo() { vant.Toast("提示"); } return { showInfo }; }, }); app.use(vant); // 通过 CDN 引入时不会自动注册 Lazyload 组件 // 可以通过下面的方式手动注册 app.use(vant.Lazyload); app.mount("#app"); </script> </body> </html>
2. nutui(移动端)
京东出品,移动端友好,面向电商业务场景
文档地址:https://nutui.jd.com/#/guide/intro
3. vuetify(移动端)
老牌 Vue UI ,基于谷歌的 Material Design 样式开发
文档地址:https://vuetifyjs.com/en/introduction/why-vuetify/#feature-guides
4. varlet(移动端)
Varlet 是一个基于 Vue3 开发的 Material 风格移动端组件库,全面拥抱 Vue3 生态,由社区建立起来的组件库团队进行维护。
文档地址:https://varlet.gitee.io/varlet-ui/#/zh-CN/home
5. tdesign-mobile-vue(移动端)
tdesign的移动端版本UI库,面向vue3
文档地址:https://tdesign.tencent.com/mobile-vue/getting-started
4.3、PC端后台管理
4.3.1、打不开github的方法
记住三个网站:
github网址查询:https://ipaddress.com/website/github.com
github域名查询:https://ipaddress.com/website/github.global.ssl.fastly.net
github静态资源ip:https://ipaddress.com/website/assets-cdn.github.com
1、打开hosts文件(C:\Windows\System32\drivers\etc)
2、然末尾放入一下两个 IP 地址:
# GitHub Start
140.82.114.4 github.com
199.232.69.194 github.global.ssl.fastly.net
# GitHub End
保存退出
3、在 CMD 命令行中执行下面语句 来刷新 DNS,重启浏览器之后就能进入Github 网址。
ipconfig/flushdns
然后直接访问
4.3.2、基于Vue3与Element plus的管理系统
https://github.com/search?o=desc&q=Element+Plus+admin&s=stars&type=Repositories
https://search.gitee.com/?q=element+plus+admin++vue3&type=repository
vue-admin-better
vue-pure-admin
Geeker-Admin
五、数制转换
JS中,通过利用js方法,可以很方便的实现2,8,10,16进制之间的相互转换
1、2,8,16进制格式的数据转换到10进制数据
var num=parseInt(arg1,arg2);
第一个参数就是需要转换为10进制的数,arg2就是被转换数据的进制值,可以是2,8,16等。
如:将十六进制的数‘d9’转为十进制数:
var num=parseInt(d9,16);//num=217
2、将10进制格式的数据转为2,8,16进制格式数据
var num=parseInt(“217”);//如果这个数是字符串格式的,需要执行这一步 var oxNum=num.toString(16);//参数可以是2,8,16.设置转换10进制数据到对应进制格式,本例是将num转成16进制数据 oxNum=d9
3、2,8,10,16任意进制之间转化
通过10进制作为媒介,便可以实现任意进制之间相互转化了
六、示例下载
https://gitee.com/zhangguo5/vue3_-chapter1.git
小红书项目要求:
http://www.cnblogs.com/xsblog/p/8144290.html
七、作业
7.1、完成一个简单的南方打游戏,页面随机出现字符(1级出现5个,2级10个,一直增加;打完50个没有错则升级到下一级;第n关打错n个就结束),字符可以下落,可以有动画效果;
7.2、可以滚动的“回顶端”
不允许使用fixed布局,要求使用鼠标滚动事件,要有动画效果,延迟下移
7.3、请完成一个只能输入数字与-的文本框
7.4、完成一个不能输入空格的文本框
7.5、完成上课的每一个示例
7.6、请完成一个商品管理模块,要求如下:
- 使用Element+vue技术实现
- 添加与修改功能使用模式窗口
- 支持全选与反选,隔行换色与光棒效果
- 详细是点击时在弹出层中显示当前商品的所有信息
- 尝试分页(选做)
7.7、增强商品管理
- 数据从后台加载,请注意跨域(axios)
- 实现搜索功能(Lodash)
- 有验证、有错误提示
- 增加删除与添加时的动画效果(选作)
- 了解UIKit(选作)
7.8、请完成一个智能机器人,重点练习使用vue-cli+app界面(任意框架)
接口:http://www.itpk.cn/robot.php、http://www.haoservice.com/docs/119
打包后在手机端根据提问给出答案,app界面
7.9、请随机生成100个字母,放入数组中,统计每个字母出现的次数,使用两种方法使用:a不使用reduce,b使用reduce
八、视频
【Vue3 + Vuex + Pinia + TypeScript + Router】 https://www.bilibili.com/video/BV1at4y1F75D?share_source=copy_web&vd_source=475a31f3c5d6353a782007cd4c638a8a