
1.不要在选项 property 或回调上使用箭头函数
比如 created: () => console.log(this.a)
或 vm.$watch('a', newValue => this.myMethod())。
因为箭头函数并没有 this
,this
会作为变量一直向上级词法作用域查找,直至找到为止。
经常导致 Uncaught TypeError: Cannot read property of undefined
或 Uncaught TypeError: this.myMethod is not a function
之类的错误。
2.动态参数
可以在指令参数中使用 JavaScript 表达式,方法是用方括号括起来:
1 | <!-- 注意,参数表达式的写法存在一些约束。--> |
这里的 attributeName
会被作为一个 JavaScript 表达式进行动态求值,求得的值将会作为最终的参数来使用。例如,如果你的组件实例有一个 data property attributeName
,其值为 "href"
,那么这个绑定将等价于 v-bind:href
。
同样地,你可以使用动态参数为一个动态的事件名绑定处理函数:
1 | <a v-on:[eventName]="doSomething"> ... </a> |
在这个示例中,当 eventName
的值为 "focus"
时,v-on:[eventName]
将等价于 v-on:focus。
注意事项
对动态参数值约定
动态参数预期会求出一个字符串,异常情况下值为 null
。这个特殊的 null
值可以被显性地用于移除绑定。任何其它非字符串类型的值都将会触发一个警告。
对动态参数表达式约定
动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute 名里是无效的。例如:
1 | <!-- 这会触发一个编译警告 --> |
变通的办法是使用没有空格或引号的表达式,或用计算属性替代这种复杂表达式。
在 DOM 中使用模板时 (直接在一个 HTML 文件里撰写模板),还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写:
1 | <!-- |
3.修饰符【详情见第9条】
修饰符 (modifier) 是以半角句号.
指明的特殊后缀,用于指出一个指令应该以特殊方式绑定。
例如,.prevent
修饰符告诉 v-on
指令对于触发的事件调用 event.preventDefault():
1 | <form v-on:submit.prevent="onSubmit">...</form> |
4.计算属性缓存 vs 方法
我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。
然而,不同的是计算属性是基于它们的反应依赖关系缓存的。计算属性只在相关==响应式依赖发生改变时==它们才会重新求值。
这就意味着只要相关响应式依赖还没有发生改变,多次访问计算属性会立即返回之前的计算结果,而不必再次执行函数。
这也同样意味着下面的计算属性将不再更新,因为 Date.now () ==不是响应式依赖==:
1 | computed: { |
相比之下,每当触发重新渲染时,调用方法将总会再次执行函数。
我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 list
,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 list
。如果没有缓存,我们将不可避免的多次执行 list
的 getter!如果你不希望有缓存,请用 method
来替代。
5.绑定 HTML Class
对象语法
当有如下模板:
1 | <div |
和如下 data:
1 | // 或者 |
数组语法
1 | <div :class="[isActive ? activeClass : '', errorClass]"></div> |
1 | data() { |
6.绑定 HTML Style
数组语法
:style
的数组语法可以将多个样式对象应用到同一个元素上:
1 | <div :style="[baseStyles, overridingStyles]"></div> |
多重值
可以为 style 绑定中的 property 提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:
1 | <div :style="{ display: ['-webkit-box', '-ms-flexbox', 'flex'] }"></div> |
7.条件渲染
v-show
带有 v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS property display。
注意,v-show
不支持 <template>
元素,也不支持 v-else
。
v-if
VS v-show
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。v-if
也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。v-show
就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
8.列表渲染
v-for
与 v-if
一同使用
永远不要把 v-if
和 v-for
同时用在同一个元素上。
当它们处于同一节点,v-if
的优先级比 v-for
更高,这意味着 v-if
将没有权限访问 v-for
里的变量:
一般我们在两种常见的情况下会倾向于这样做:
- 为了过滤一个列表中的项目 (比如
v-for="user in users" v-if="user.isActive"
)。在这种情形下,请将users
替换为一个计算属性 (比如activeUsers
),让其返回过滤后的列表。 - 为了避免渲染本应该被隐藏的列表 (比如
v-for="user in users" v-if="shouldShowUsers"
)。这种情形下,请将v-if
移动至容器元素上 (比如ul
、ol
、template
)。
vue2:v-if
的优先级比 v-for
更低
vue3:v-if
的优先级比 v-for
更高
9.事件处理
访问原始的 DOM 事件
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event
把它传入方法:
1 | <button @click="warn('Form cannot be submitted yet.', $event)"> |
1 | methods: { |
多事件处理器
事件处理程序中可以有多个方法,这些方法由逗号运算符分隔:
1 | <!-- 这两个 one() 和 two() 将执行按钮点击事件 --> |
1 | methods: { |
事件修饰符
.stop
:停止冒泡;.prevent
:阻止事件默认行为;.capture
:添加事件监听器时使用事件捕获模式;.self
:只当事件是从侦听器绑定的元素本身触发时才触发回调.once
:只触发一次回调;.passive
1 | <!-- 阻止单击事件继续传播 --> |
TIP
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用
v-on:click.prevent.self
会阻止所有的点击,而v-on:click.self.prevent
只会阻止对元素自身的点击。
10.Props
传入一个对象的所有 property
如果你想要将一个对象的所有 property 都作为 prop 传入,你可以使用不带参数的 v-bind
(取代 v-bind
:prop-name
)。例如,对于一个给定的对象 post
1 | post: { |
下面的模板:
1 | <blog-post v-bind="post"></blog-post> |
等价于:
1 | <blog-post v-bind:id="post.id" v-bind:title="post.title"></blog-post> |
单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
另外,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
提示
注意在 JavaScript 中对象和数组是通过引用传入的,所以对于一个数组或对象类型的 prop 来说,在子组件中改变变更这个对象或数组本身将会影响到父组件的状态。
Prop 验证
1 | app.component('my-component', { |
当 prop 验证失败的时候,(开发环境构建版本的) Vue 将会产生一个控制台的警告。
提示
注意那些 prop 会在一个组件实例创建之前进行验证,所以实例的 property (如
data
、computed
等) 在default
或validator
函数中是不可用的。
11.非 Prop 的 Attribute
一个非 prop 的 attribute 是指传向一个组件,但是该组件并没有相应 props 或 emits 定义的 attribute。常见的示例包括 class
、style
和 id
属性。
Attribute 继承
当组件返回单个根节点时,非 prop attribute 将自动添加到根节点的 attribute 中。
class
1 | <!-- 具有非prop attribute的Date-picker组件--> |
自定义属性
1 | <!-- 具有非prop attribute的Date-picker组件--> |
事件监听器
1 | <date-picker @change="submitChange"></date-picker> |
1 | app.component('date-picker', { |
禁用 Attribute 继承
如果你不希望组件的根元素继承 attribute,你可以在组件的选项中设置 inheritAttrs: false
。例如:
禁用 attribute 继承的常见情况是需要将 attribute 应用于根节点之外的其他元素。
通过将 inheritAttrs
选项设置为 false
,你可以访问组件的 $attrs
property,该 property 包括组件 props
和 emits
property 中未包含的所有属性 (例如,class
、style
、v-on
监听器等)。
与单个根节点组件不同,具有多个根节点的组件不具有自动 attribute 回退行为。如果未显式绑定 $attrs
,将发出运行时警告。
1 | <custom-layout id="custom-layout" @click="changeValue"></custom-layout> |
1 | // 这将发出警告 |
组件上使用v-model
v-model
参数
默认情况下,组件上的 v-model
使用 modelValue
作为 prop 和 update:modelValue
作为事件。
1 | <custom-input v-model="searchText"></custom-input> |
1 | app.component('custom-input', { |
我们可以通过向 v-model
传递参数来修改这些名称:
1 | <my-component v-model:foo="bar"></my-component> |
子组件将需要一个 foo
prop 并发出 update:foo
要同步的事件:
1 | const app = Vue.createApp({}) |
多个 v-model
绑定
通过利用以特定 prop 和事件为目标的能力,正如我们之前在 v-model
参数中所学的那样,我们现在可以在单个组件实例上创建多个 v-model 绑定。
每个 v-model 将同步到不同的 prop,而不需要在组件中添加额外的选项:
1 | <user-name |
1 | const app = Vue.createApp({}) |
12.插槽
具名插槽
有时我们需要多个插槽。例如对于一个带有如下模板的 <base-layout>
组件:
1 | <div class="container"> |
一个不带 name
的 <slot>
出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以在一个 <template>
元素上使用 v-slot
指令,并以 v-slot
的参数的形式提供其名称:
1 | <base-layout> |
渲染的 HTML 将会是:
1 | <div class="container"> |
注意,**v-slot
只能添加在 <template>
上** (只有一种例外情况)
作用域插槽
有时让插槽内容能够访问子组件中才有的数据是很有用的。当一个组件被用来渲染一个项目数组时,这是一个常见的情况,我们希望能够自定义每个项目的渲染方式。
1 | app.component('todo-list', { |
绑定在 <slot
> 元素上的 attribute 被称为插槽 prop。现在在父级作用域中,我们可以使用带值的 v-slot
来定义我们提供的插槽 prop 的名字:
1 | <todo-list> |
当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用
1 | <todo-list v-slot="slotProps"> |
注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确。
只要出现多个插槽,请始终为所有的插槽使用完整的基于 <template>
的语法:
1 | <todo-list> |
具名插槽的缩写
跟 v-on
和 v-bind
一样,v-slot
也有缩写,即把参数之前的所有内容 (v-slot:
) 替换为字符 #
。例如 v-slot:header
可以被重写为 #header
:
1 | <base-layout> |
然而,和其它指令一样,该缩写只在其有参数的时候才可用。这意味着以下语法是无效的:
1 | <!-- This will trigger a warning --> |
如果你希望使用缩写的话,你必须始终以明确插槽名取而代之:
1 | <todo-list #default="{ item }"> |
13.提供 / 注入
通常,当我们需要将数据从父组件传递到子组件时,我们使用 props。想象一下这样的结构:你有一些深嵌套的组件,而你只需要来自深嵌套子组件中父组件的某些内容。在这种情况下,你仍然需要将 prop 传递到整个组件链中,这可能会很烦人。
对于这种情况,我们可以使用 provide
和 inject
对。父组件可以作为其所有子组件的依赖项提供程序,而不管组件层次结构有多深。这个特性有两个部分:父组件有一个 provide
选项来提供数据,子组件有一个 inject
选项来开始使用这个数据。
1 | const app = Vue.createApp({}) |
果我们尝试在此处提供一些组件实例 property,则这将不起作用;我们需要将 provide
转换为返回对象的函数
1 | app.component('todo-list', { |
处理响应性
在上面的例子中,如果我们更改了 todos
的列表,这个更改将不会反映在注入的 todoLength
property 中。这是因为默认情况下,provide/inject
绑定不是被动绑定。(vue2.x中也不提供响应式的property)
在vue3中,我们可以通过将 ref
property 或 reactive
对象传递给 provide
来更改此行为。在我们的例子中,如果我们想对祖先组件中的更改做出反应,我们需要为我们提供的 todoLength
分配一个组合式 API computed
property:
1 | app.component('todo-list', { |
$forceUpdate【待完成】
14.混入
基础
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
选项合并
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。
比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
1 | const myMixin = { |
同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。
值为对象的选项,例如 methods
、components
和 directives
,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。
1 | const myMixin = { |
全局混入
混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响每一个之后创建的组件 (例如,每个子组件)。
1 | const app = Vue.createApp({ |
大多数情况下,只应当应用于自定义选项,就像上面示例一样。推荐将其作为插件发布,以避免重复应用混入。
自定义选项合并策略
自定义选项将使用默认策略,即简单地覆盖已有值。如果想让自定义选项以自定义逻辑合并,可以向 app.config.optionMergeStrategies
添加一个函数
合并策略接收在父实例和子实例上定义的该选项的值,分别作为第一个和第二个参数。让我们来检查一下使用 mixin 时,这些参数有哪些:
1 | // 子实例 |
15.Teleport
Vue 鼓励我们通过将 UI 和相关行为封装到组件中来构建 UI。我们可以将它们嵌套在另一个内部,以构建一个组成应用程序 UI 的树。
然而,有时组件模板的一部分逻辑上属于该组件,而从技术角度来看,最好将模板的这一部分移动到 DOM 中 Vue app 之外的其他位置。
一个常见的场景是创建一个包含全屏模式的组件。在大多数情况下,你希望模态的逻辑存在于组件中,但是模态的定位就很难通过 CSS 来解决,或者需要更改组件组合。
例如:有一个嵌套的组件,子组件是一个模态框组件,模态框组件组件会用到脱离文档流的定位,一般我们需要将该组件的DOM元素移动到body元素的最后面,如果是嵌套组件的话,该DOM元素将被嵌套在父组件元素里。
Teleport 提供了一种干净的方法,允许我们控制在 DOM 中哪个父节点下呈现 HTML,而不必求助于全局状态或将其拆分为两个组件。
1 | <body> |
1 | <template> |
因此,一旦我们单击按钮打开模式,Vue 将正确地将模态内容渲染为 body
标签的子级。
与 Vue components 一起使用
如果 <teleport>
包含 Vue 组件,则它仍将是 <teleport>
父组件的逻辑子组件
这也意味着来自父组件的注入按预期工作,并且子组件将嵌套在 Vue Devtools 中的父组件之下,而不是放在实际内容移动到的位置。
在同一目标上使用多个 teleport
一个常见的用例场景是一个可重用的 <Modal>
组件,它可能同时有多个实例处于活动状态。对于这种情况,多个 <teleport>
组件可以将其内容挂载到同一个目标元素。顺序将是一个简单的追加——稍后挂载将位于目标元素中较早的挂载之后。
1 | <teleport to="#modals"> |
16.钩子函数
Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载 Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是 Vue 的生命周期。
生命周期 | 描述 |
---|---|
beforeCreate | 组件实例被创建之初,组件的属性生效之前 |
created | 组件实例已经完全创建,属性也绑定,但真实 dom 还没有生成,**$el** 还不可用 |
beforeMount | 在挂载开始之前被调用:相关的 render 函数首次被调用 |
mounted | el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子 |
beforeUpdate | 组件数据更新之前调用,发生在虚拟 DOM 打补丁之前 |
activited | keep-alive 专属,组件被激活时调用 |
deactivated | keep-alive 专属,组件被销毁时调用 |
beforeDestory | 组件销毁前调用 |
destoryed | 组件销毁后调用 |
生命周期示意图

watch异步
过滤器
过滤器实质不改变原始数据,只是对数据进行加工处理后,返回过滤后的数据再进行调用处理,我们也可以理解成纯函数。
1 | {{ messgae | filterA("arg1", "arg2") | filterB("arg1", "arg2") }} |
1 | vue.filter('filterA', function(value) { |
常见场景:==单位转换、千分符、文本格式化、时间格式化==等操作。 这个写个方法不香么?
Vue3 果断废弃了过滤器……
1 | <p> {{format(number)}}</p> |
1 | const format = () => { |
- Post title: Vue2遗漏知识点
- Create time: 2022-03-17 16:35:00
- Post link: 2022/03/17/vue2遗漏知识点/
- Copyright notice: All articles in this blog are licensed under BY-NC-SA unless stating additionally.