一.模板语法:允许开发者把DOM绑定在最底层Vue实例上。在底层实现上,Vue会将模板编译成虚拟DOM渲染函数,Vue可以计算出最少需要重新渲染多少组件,并减少DOM操作。
1.插值
#文本
数据绑定最常见的就是Mustache语法(双大括号){{msg}}的文本插值。你也可以用v-once指令来定义一次性插值,数据改变插值处的内容也不会改变,但是请注意,这会影响到该节点的其他插值。
//html //js //浏览器
我在这里给span标签添加了一个点击事件,运用了v-once指令,点击span标签内容仍不会改变。
#v-html
双大括号输出的只是文本,想输出html代码,需要用到v-html指令
//html和js //浏览器
浏览器里,上面输出的就是纯文本,下面就是把aaa里的东西变成了html代码再渲染到了浏览器上,可以看到字体变成了红色。
!注意:你的站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容使用插值。
#特性
Mustache语法不能作用在HTML特性上,需要用v-bind指令来进行操作,
用v-bind把class绑定在div上,shtie是一个变量名,初始值等于one,div初始是one的样式。当点击div时,改变shtie变量的值为two,div就会变为two的样式。
//点击前 //点击后 (样式有点丑,见谅)
v-bind也可以用来绑定布尔值
这两种写法显示是一样的,可以给data中的firstName和lastName重新赋值,但是给reverseMsg重新赋值就会报错,提示没有setter函数,在需要时也可以提供一个setter函数。
这是因为你对reverseMsg赋值时,传入的值保存在value中,然后把value中的值被空格分割成一个数组分别赋给firstName和lastName,当firstName和lastName改变时,就又触发计算属性重新计算,从而改变页面。
#计算属性缓存vs方法
通过在表达式中调用方法也可以达到同样的效果:
虽然两个方式最后的结果是相同的,但是不同的是计算属性是基于它们的响应式依赖缓存的,只有相关响应式依赖改变它们才会重新求值,也就是说只要msg没有发生改变,多次访问reverseMsg计算属性会立即返回之前的计算结果,而不必再执行函数。
相比之下,每次触发重新渲染,调用方法将总会再次执行函数。
为什么要缓存?假设有一个计算属性A,它需要做一些非常复杂且大量的计算,然后有一个依赖于A计算属性,如果没有缓存,我们会不可避免的多次执行A中的getter函数。如果你不希望有缓存,可用方法代替。
#计算属性vs侦听属性
vue提供了一种通用的方式来观察和响应vue实例上的数据变动:侦听属性watch。
//html和js //浏览器和控制台显示
其中newVal是改变之后的值,oldVal是改变之前的值。
当有一些数据需要随着其它数据变动而变动时,很容易滥用 watch,通常更好的做法是使用计算属性而不是命令式的回调watch。
三. class与style绑定
操作元素的 class 列表和内联样式是数据绑定的一个常见需求。因为它们都是属性,所以我们可以用 v-bind处理它们:只需要通过表达式计算出字符串结果即可。不过,字符串拼接麻烦且易错。因此,在将 v-bind用于class和style时,Vue做了专门的增强。表达式结果的类型除了字符串之外,还可以是对象或数组。
1. 绑定HTML Class
#对象语法
我们给v-bind:class绑定一个对象,来动态切换css:
<div v-bind:class="{ active:isActive }">
现在active这个class的存在与否就取决于isActive的真值了。
你可以在对象传递更多属性来动态切换多个class,此外v-bind:class也可以和普通class共存:
//HTML和js //浏览器控制台显示
当 isTwo 和 isError 变化,class列表也相应的更新。例如isError的值变为true,class列表将变为class=“one two button-text”。
绑定的对象不用内联定义在模块里:
//HTML和js //浏览器控制台显示
两种写法渲染是相同的,也可以在这里绑定一个返回对象的计算属性,这是个常用且强大的模式:
#数组语法
我们可以传一个数组给v-bind:class,以应用一个class列表:
//html和JS //浏览器控制台显示
也可以运用三元表达式,<div v-bind : class =" [ isActive ? active : ' ' ,errorClass ] "></div>。
这样写始终添加errorClass,但只有isActive为真值时才添加active。不过当有多个条件class时,这样写太繁琐,所有数组语法也可以使用对象语法:
<div v-bind : class= " [ { active : isActive } ,errorClass ] ">
#用在组件上
当在一个自定义组件上使用 class
属性时,这些类将被添加到该组件的根元素上面。这个元素上已经存在的类不会被覆盖。
//父组件 //子组件 // 浏览器
我这里是将组件拆出去,然后在父组件里引用子组件。以上面为例,bbb相当于一个包在子组件外的div,是子组件的容器,就算没有定义类名,这个div还是会存在。
对于带数据绑定也同样适用:
<myComponent v-bind : class = " { active : isActive } "></myComponent> 当isActive为真值时,active将被渲染。
2. 绑定内联样式
#对象语法
v-bind:style的对象语法很直观,看着非常像css,但其实是一个JavaScript对象,css属性名可以用驼峰式命名(fontSize)或短横线分割(font-size,记得用引号括起来:“font-size”)来命名:
直接绑定到一个样式对象通常更好,这会让模板更清晰:
同样的,对象语法常常结合返回对象的计算属性使用。
#数组语法
v-bind:style可以将多个样式对象应用到一个元素上:
#多重值
从 2.3.0 起你可以为 style
绑定中的属性提供一个包含多个值的数组,常用于提供多个带前缀的值,例如:
<div :style = " { display: [ '-webkit-box','-ms-flexbox','flex' ] }"></div>
这样写只会渲染数组中最后一个被浏览器支持的值。在本例中,如果浏览器支持不带浏览器前缀的 flexbox,那么就只会渲染 display: flex
。
四. 条件渲染
1. v-if
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回真值的时候被渲染。
<div v-if="awesome"> yes </div>
也可以用 v-else
添加一个“else 块”:
<div v-if="awesome"> yes </div>
<div v-else> no </div>
#在template元素上使用v-if条件渲染分组
因为 v-if
是一个指令,所以必须将它添加到一个元素上。但是如果想切换多个元素呢?此时可以把一个 <template>
元素当做不可见的包裹元素,并在上面使用 v-if
。最终的渲染结果将不包含 <template>
元素。
<template v-if=" ok ">
<h1>为真时</h1>
<div>这里</div>
<p>会渲染</p>
</template>
#v-else
你可以使用 v-else
指令来表示 v-if
的“else 块”:
<div v-if=" Math.random() > 0.5 ">满足条件时渲染</div>
<div v-else>否则渲染这里</div>
v-else
元素必须紧跟在带 v-if
或者 v-else-if
的元素的后面,否则它将不会被识别。
#v-else-if
v-else-if
,顾名思义,充当 v-if
的“else-if 块”,可以连续使用:
<div v-if= "index === 1"> A </div>
<div v-else-if= "index === 2"> B </div>
<div v-else-if= "index === 3"> C </div>
<div v-else-if= "index === 4"> D </div>
<div v-else> 不是A/B/C/D </div>
类似于 v-else
,v-else-if
也必须紧跟在带 v-if
或者 v-else-if
的元素之后。
#用key管理可复用的元素
Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。这么做除了使 Vue 变得非常快之外,还有其它一些好处。例如,如果你允许用户在不同的登录方式之间切换:
那么在上面的代码中切换 loginType
将不会清除用户已经输入的内容。因为两个模板使用了相同的元素,<input>
不会被替换掉——仅仅是替换了它的 placeholder
。
这样也不总是符合实际需求,所以 Vue 提供了一种方式来表达“这两个元素是完全独立的,不要复用它们”。只需添加一个具有唯一值的 key
属性即可:
注意,与<input>同级 <div> 元素仍然会被高效地复用,因为它们没有添加 key
属性。
2. v-show
另一个用于根据条件展示元素的选项是 v-show
指令。用法大致一样:
<div v-show=" ok ">为真时渲染</div>
不同的是带有 v-show
的元素始终会被渲染并保留在 DOM 中。v-show
只是简单地切换元素的 CSS 属性 display
。
!注意:v-show不支持<template>元素,也不支持v-else。
3. v-if 与 v-show
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
v-if
也是惰性的,如果在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。
相比之下,v-show
就简单得多,不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
4. v-if 与 v-show
不推荐 v-if 与 v-for 同时使用。 v-for的优先级是高于v-if的,也就是说会先执行v-for,然后在每个for里都会执行一遍if。
这种优先级机制在有些地方是比较实用的,比如需要渲染满足条件的项:
在这个例子中,我们只想渲染为0的项,就可以利用这个机制。
五. 列表渲染
1. 用v-for把一个数组对应为一组元素
我们可以用 v-for
指令基于一个数组来渲染一个列表。v-for
指令需要使用 item in items
形式的特殊语法,其中 items
是源数据数组,而 item
则是被迭代的数组元素的别名。
//渲染结果
在 v-for
块中,我们可以访问所有父作用域的属性。v-for
还支持一个可选的第二个参数,即当前项的索引。
//渲染结果
你也可以用 of
替代 in
作为分隔符,因为它更接近 JavaScript 迭代器的语法:
<div v-for=" item of items ">
2. 在v-for 里使用对象
用 v-for 遍历对象:
//渲染结果
第一个参数为值,第二个的参数为 property 名称 (也就是键名),第三个参数作为索引。
在遍历对象时,会按 Object.keys()
的结果遍历,但是不能保证它的结果在不同的 JavaScript 引擎下都一致。
3. 维护状态
当 Vue 正在更新使用 v-for
渲染的元素列表时,它默认使用“就地更新”的策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染。
为了给 Vue 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key
属性:<div v-for =" item in items " v-bind : key =" item.id ">。
建议尽可能在使用 v-for
时提供 key
attribute,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。因为它是 Vue 识别节点的一个通用机制,key
并不仅与 v-for
特别关联,还有其他的用途。
不要使用对象或数组之类的非基本类型值作为 v-for
的 key
。请用字符串或数值类型的值。
4. 更新检测
#变异方法
Vue 将被侦听的数组的变异方法进行了包裹,所以它们也将会触发视图更新。这些被包裹过的方法包括:
-
push() //可向数组的末尾添加一个或多个元素,并返回新的长度。
pop() //用于删除并返回数组的最后一个元素。
shift() //用于把数组的第一个元素从其中删除,并返回第一个元素的值。
unshift() //可向数组的开头添加一个或更多元素,并返回新的长度。
splice() //向/从数组中添加/删除项目,然后返回被删除的项目。
sort() //用于对数组的元素进行排序。
reverse() //用于颠倒数组中元素的顺序。
#替换数组
变异方法,顾名思义,会改变调用了这些方法的原始数组。相比之下,也有非变异 (non-mutating method) 方法,例如 filter()
、concat()
和 slice()
。它们不会改变原始数组,而总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组:
example1.items = example1.items.filter(function (item) {
return item.message.match(/Foo/)
})
你可能认为这将导致 Vue 丢弃现有 DOM 并重新渲染整个列表。幸运的是,事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用而实现了一些智能的启发式方法,所以用一个含有相同元素的数组去替换原来的数组是非常高效的操作。
#注意事项
由于 JavaScript 的限制,Vue 不能检测以下数组的变动:
- 当你利用索引直接设置一个数组项时,例如:
vm.items[indexOfItem] = newValue
- 当你修改数组的长度时,例如:
vm.items.length = newLength
- 当你利用索引直接设置一个数组项时,例如:
为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue
相同的效果,同时也将在响应式系统内触发状态更新:
// Vue.set // Array.prototype.splice
Vue.set(vm.items, indexOfItem, newValue) vm.items.splice(indexOfItem, 1, newValue)
为了解决第二类问题,你可以使用 splice
: vm.items.splice(newLength)
5.显示过滤/排序后的结果
有时,我们想要显示一个数组经过过滤或排序后的版本,而不实际改变或重置原始数据。在这种情况下,可以创建一个计算属性,来返回过滤或排序后的数组。
//渲染结果
这里改变了原数组,但是因为是计算属性,没有setter函数不能改变它的依赖,所以number不会改变,可以正常遍历。
在计算属性不适用的情况下 (例如,在嵌套 v-for
循环中) 你可以使用一个方法:
//渲染结果
注意,这里使用的方法必须是返回一个新数组的方法,使用改变原数组但不返回新数组的方法,会无限更新循环。
6. 在v-for 里使用值范围
v-for
也可以接受整数。在这种情况下,它会把模板重复对应次数。
<div> <span v-for="n in 10">{{ n }} </span> </div>
7. 在<template> 上使用v-for
//渲染结果
8. 在组件上使用v-for
在自定义组件上,你可以像在任何普通元素上一样使用 v-for
。
<myComponent v-for="item in items" :key="item.id"></myComponent>
2.2.0+ 的版本里,当在组件上使用 v-for
时,key
现在是必须的。
然而,任何数据都不会被自动传递到组件里,因为组件有自己独立的作用域。为了把迭代数据传递到组件里,我们要使用 prop:
//父组件
//子组件
把父组件中items的值传给子组件,需要用到prop,把items绑定在子组件上,在子组件里用props接收数据。
六. 事件处理
1. 监听事件
可以用 v-on
指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
除了直接绑定到一个方法,也可以在内联 JavaScript 语句中调用方法:
有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量 $event
把它传入方法:
preventDefault() 方法阻止元素发生默认的行为(例如,当点击提交按钮时阻止对表单的提交)。
2. 事件修饰符
方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
<!-- 阻止单击事件继续传播 -->
<!-- 阻止冒泡 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重载页面 -->
<!-- 阻止默认事件 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>
<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即元素自身触发的事件先在此处理,然后才交由内部元素进行处理 -->
<!-- 通常我们只监听冒泡过程。在vue中,当我们添加了事件修饰符capture后,才会变成捕获监听器。 -->
<div v-on:click.capture="doThis">...</div>
<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 v-on:click.prevent.self
会阻止所有的点击,而 v-on:click.self.prevent
只会阻止对元素自身的点击。
Vue 还对应 addEventListener
中的 passive
选项提供了 .passive
修饰符。
<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>
不要把 .passive
和 .prevent
一起使用,因为 .prevent
将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive
会告诉浏览器你不想阻止事件的默认行为。
3. 按键修饰符
在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on
在监听键盘事件时添加按键修饰符:
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit">
也可以直接将 KeyboardEvent.key
暴露的任意有效按键名转换为 kebab-case 来作为修饰符。
<input v-on:keyup.page-down="onPageDown">
在上述示例中,处理函数只会在 $event.key
等于 PageDown
时被调用。
七. 表单输入绑定
1. 基础用法
你可以用 v-model
指令在表单 <input>
、<textarea>
及 <select>
元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model
本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。
v-model
会忽略所有表单元素的 value
、checked
、selected
特性的初始值而总是将 Vue 实例的数据作为数据来源。你应该通过 JavaScript 在组件的 data
选项中声明初始值。
#文本
#多行文本
=
在文本区域插值 (<textarea>{{text}}</textarea>
) 并不会生效,应用 v-model
来代替。
#复选框
单个复选框,绑定布尔值
多个复选框绑定同一数组
#单选按钮
#选择框
如果 v-model
表达式的初始值未能匹配任何选项,<select>
元素将被渲染为“未选中”状态。在 iOS 中,这会使用户无法选择第一个选项。因为这样的情况下,iOS 不会触发 change 事件。因此,更推荐像上面这样提供一个值为空的禁用选项。
#多选(绑定到一个数组)
v-for渲染动态选项
2. 值绑定
对于单选按钮,复选框及选择框的选项,v-model
绑定的值通常是静态字符串 (对于复选框也可以是布尔值)。
但是有时我们可能想把值绑定到 Vue 实例的一个动态属性上,这时可以用 v-bind
实现,并且这个属性的值可以不是字符串。
#单选框
#选择框选项
八. 组件基础
1. 基本示例
vue项目目录结构
components下的base里放的是基础组件,business放的是业务组件。可能你的许多组件只是包裹了一个输入框或按钮之类的元素,是相对通用的。我们有时候会把它们称为基础组件,它们会在各个组件中被频繁的用到。
在组件文件夹下创建vue文件,在父组件页面里引用子组件,
因为组件是可复用的 Vue 实例,所以它们与 new Vue
接收相同的选项,例如 data
、computed
、watch
、methods
以及生命周期钩子等。
2. 组件的复用
你可以将组件进行任意次数的复用:
每个组件都会各自独立维护,每用一次组件,就会有一个它的新实例被创建。
一个组件的 data
选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立的拷贝:
data(){
return{
}
}
如果 Vue 没有这条规则,复用组件,操作其中一个,其他相同组件就会受到影响。
3. 组件的组织
通常一个应用会以一棵嵌套的组件树的形式来组织:
例如,你可能会有页头、侧边栏、内容区等组件,每个组件又包含了其它的像导航链接、博文之类的组件。
4. 通过prop向子组件传递数据:这个我在列表渲染里,在组件上使用v-for实际操作过一次。
5. 单个根元素:每个组件里必须在<template>里套一个<div>,然后在这个最外层<div>里写你要实现的内容。
6. 监听子组件事件:
#子组件抛出事件
#父组件里调用事件
// 这里打印val为子组件传过来的值 true。
#组件上使用v-model
自定义事件也可以用于创建支持 v-model
的自定义输入组件。
<input v-model="searchText">
等价于
<input v-bind:value="searchText" v-on:input="searchText = $event.target.value" >
当用在组件上时,v-model
则会这样:
<custom-input v-bind:value="searchText" v-on:input="searchText = $event" ></custom-input>
为了让它正常工作,这个组件内的 <input>
必须:
-
- 将其
value
特性绑定到一个名叫value
的 prop 上 - 在其
input
事件被触发时,将新的值通过自定义的input
事件抛出
- 将其
写成代码后:
现在 v-model
就应该可以在这个组件上完美地工作起来了:
<custom-input v-model="searchText"></custom-input>
7. 通过插槽分发内容
和 HTML 元素一样,我们经常需要向一个组件传递内容,像这样:
<myComponent>123</myComponent>
可能什么也渲染不出来,也可能会渲染出这样的东西:
幸好,Vue 自定义的 <slot>
元素让这变得非常简单:
只需要在子组件里,把<slot>标签写在你想添加的位置。这就是插槽,后面还会介绍更多。
九. 组件注册
1.组件名:单文件组件(.vue),推荐驼峰式命名(myComponent)或跟据公司规范命名。
2.全局注册
在注册之后可以用在任何新创建的 Vue 根实例 (new Vue
) 的模板中。
创建一个vue文件作为组件
在main.js里引入组件路径并创建组件
在父组件里使用组件标签名
3. 局部注册:本文中除了全局注册,剩下的组件注册都是局部注册。
十. Prop
1.Prop大小写
HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,驼峰命名法 的 prop 名需要使用其等价的短横线分隔命名:
props: [ ' postTitle ' ]
<myComponent post-title=" hello "></myComponent>
如果使用字符串模板,这个限制就不存在了。
2.Prop类型
现在我们只看到以字符串数组形式列出的prop:
props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
但是,通常你希望每个 prop 都有指定的值类型。这时,你可以以对象形式列出 prop,这些属性的名称和值分别是 prop 各自的名称和类型:
这不仅为组件提供了文档,还会在它们遇到错误的类型时从浏览器的 JavaScript 控制台提示用户。
3.单向数据流
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。额外的,每次父级组件发生更新时,子组 件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台给出警告。
这里有两种常见的试图改变一个prop的情形:
1.这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用。在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
2.这个 prop 以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
4. prop 验证
我们可以为组件的 prop 指定验证要求,例如你知道的这些类型。如果有一个需求没有被满足,则 Vue 会在浏览器控制台中警告你。这在开发一个会被别人用到的组件时尤其有帮助。
为了定制 prop 的验证方式,你可以为 props
中的值提供一个带有验证需求的对象,而不是一个字符串数组。
当prop验证失败的时候,Vue将会产生一个控制台的警告。
type可以是下面原生构造函数中的一个:
-
String
Number
Boolean
Array
Object
Date
Function
Symbol
5.非prop特性:组件可以接受任意的特性,而这些特性会被添加到这个组件的根元素上
十一. 自定义事件
1.事件名
不同于组件和 prop,事件名不存在任何自动化的大小写转换。而是触发的事件名需要完全匹配监听这个事件所用的名称。举个例子,如果触发一个驼峰式名字的事件:
this.$emit('myEvent')
则监听这个名字的短横线分隔命名版本是不会有任何效果的:
<!-- 没有效果 -->
<myComponent @my-event="doSomething"></myComponent>
2. 将原生事件绑定到组件
Vue 提供了一个 $listeners
属性,它是一个对象,里面包含了作用在这个组件上的所有监听器。
{
focus: function (event) { /* ... */ }
input: function (value) { /* ... */ },
}
有了这个 $listeners
属性,你就可以配合 v-on="$listeners"
将所有的事件监听器指向这个组件的某个特定的子元素。
十二. 插槽:在 2.6.0 中,我们为具名插槽和作用域插槽引入了一个新的统一的语法 (即 v-slot
指令)。它取代了 slot
和 slot-scope
这两个目前已被废弃但未被移除且仍在文档中的特性。
1.插槽内容
上面插槽的例子只是插入了数据,其实还可以插入组件:
如果 <myComponent>
没有包含一个 <slot>
元素,则该组件起始标签和结束标签之间的任何内容都会被抛弃。
2.编译作用域
当你想在一个插槽中使用数据时,例如:
该插槽跟模板的其它地方一样可以访问相同的实例属性 (也就是相同的“作用域”),而不能访问 <myComponent>
的作用域。例如 url
是访问不到的:
父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。
3.后备内容
有时为一个插槽设置具体的后备 (也就是默认的) 内容是很有用的,它只会在没有提供内容的时候被渲染。
我们可能希望这个 <button>
内绝大多数情况下都渲染文本“Submit”。为了将“Submit”作为后备内容,我们可以将它放在 <slot>
标签内,
现在当我在一个父级组件中使用该组件并且不提供任何插槽内容时,后备内容“submit”将会被渲染,
但当我们提供内容,则这个提供的内容将会被渲染从而取代后备内容:
4. 具名插槽
有时我们需要多个插槽。<slot>
元素有一个特殊的特性:name
。这个特性可以用来定义额外的插槽,一个不带 name
的 <slot>
出口会带有隐含的名字“default”。
在向具名插槽提供内容的时候,我们可以向<div>元素上使用slot指令,并以slot参数形式提供名称。
//父组件中 //<myComponent>子组件中
现在父组件 <div>
元素中的所有内容都将会被传入相应的插槽。
任何没有被包裹在带有 slot
的 <div>
中的内容都会被视为默认插槽的内容。
也可以有另一种写法,把slot换成v-slot,但是v-slot只能添加在一个<template>上,只有一种情况例外。
当被提供的内容只有默认插槽时,组件的标签才可以被当作插槽的模板来使用。这样我们就可以把 v-slot
直接用在组件上。
注意默认插槽的缩写语法不能和具名插槽混用,因为它会导致作用域不明确。
只要出现多个插槽,就要用具名插槽。
5作用域插槽
有时让插槽内容能够访问子组件中才有的数据是很有用的。
//父组件中
//子组件中
在添加一个slot-scope属性,给一个scope参数,在子组件里也同样使用。
还可以有其他方法,比如,<current-user>组件里的slot想访问传给父级渲染的数据是不行的,
如下,为了让 user
在父级的插槽内容中可用,我们可以将 user
作为 <slot>
元素的一个特性绑定上去:
绑定在 <slot>
元素上的特性被称为插槽 prop。现在在父级作用域中,我们可以给 v-slot
带一个值来定义我们提供的插槽 prop 的名字:
在这个例子中,我们选择将包含所有插槽 prop 的对象命名为 aaa,但你也可以使用任意你喜欢的名字。
#解构插槽Prop
作用域插槽的内部工作原理是将你的插槽内容包括在一个传入单个参数的函数里:
function (slotProps) { // 插槽内容 }
这意味着 v-slot
的值实际上可以是任何能够作为函数定义中的参数的 JavaScript 表达式。所以在支持的环境下 (单文件组件或现代浏览器),你也可以使用 ES2015 解构来传入具体的插槽 prop,如下:
这样可以使模板更简洁,尤其是在该插槽提供了多个 prop 的时候。它同样开启了 prop 重命名等其它可能,例如将 user
重命名为 person
:
甚至可以定义后备内容,用于插槽 prop 是 undefined 的情形:
注意是插槽prop是undefined,在子组件会给出警告。
#动态插槽
动态指令参数也可以用在 v-slot
上,来定义动态的插槽名:
#具名插槽缩写
v-slot:header 等价于 #header
和其它指令一样,该缩写只在其有参数的时候才可用。
十三. 动态组件&异步组件
1. 动态组件
在不同组件之间动态切换,通过 Vue 的 <component>
元素加一个特殊的 is
特性来实现:
first 、 second 、third 是注册的三个组件,com可以是已注册组件的名字,或一个组件的选项对象。我这里设置的是一个组件选项的数组。
2.在组件上使用keep-alive
当在first内输入内容,然后点击second,再切换会first,first内输入的内容会消失。这是因为你每次切换新标签的时候,Vue 都创建了一个新的 com实例。
重新创建动态组件的行为通常是非常有用的,但是在这个案例中,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 <keep-alive>
元素将其动态组件包裹起来。
注意这个 <keep-alive>
要求被切换到的组件都有自己的名字,不论是通过组件的 name
选项还是局部/全局注册。
十四. 混入
1.基础
混入 (mixin) 提供了一种非常灵活的方式,来分发 Vue 组件中的可复用功能。一个混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被“混合”进入该组件本身的选项。
创建混入文件
注册一个mixin对象,定义需要的方法或者数据
在需要的页面引入并使用,混入
scss文件混入
创建scss文件并写入需要混入的样式
引入需要混入的文件
2.选项合并
当组件和混入对象含有同名选项时,这些选项将以恰当的方式进行“合并”。
比如,数据对象在内部会进行递归合并,并在发生冲突时以组件数据优先。
同名钩子函数将合并为一个数组,因此都将被调用。另外,混入对象的钩子将在组件自身钩子之前调用。也就是说先执行混入对象的钩子内的操作。
值为对象的选项,例如 methods
、components
和 directives
,将被合并为同一个对象。两个对象键名冲突时,取组件对象的键值对。
3.全局混入
混入也可以进行全局注册。使用时格外小心!一旦使用全局混入,它将影响每一个之后创建的 Vue 实例。使用恰当时,这可以用来为自定义选项注入处理逻辑。
在main.js中定义mixin,在需要的使用的页面直接使用
4.自定义合并策略
optionMergeStrategies
主要用于 mixin
对于子组件和父组件如果有相同的属性(option)时的合并策略。
自定义选项将使用默认策略,即简单地覆盖已有值。
#defaultStrat
var defaultStrat = function (parentVal, childVal) {
return childVal === undefined
? parentVal
: childVal
}
源代码很简单,传入两个参数 parentVal
, childVal
分别对应于父组件和子组件的选项,合并的策略就是,子组件的选项不存在,才会使用父组件的选项,如果子组件的选项存在,使用子组件自身的。
#option.hook
如果父组件和子组件都设置了钩子函数选项,那么 它们会合并到一个数组里,而且父组件的钩子函数会先执行,最后返回一个合并后的数组。具体见源码里的注释。
如果想让自定义选项以自定义逻辑合并,可以向 Vue.config.optionMergeStrategies
添加一个函数:
Vue.config.optionMergeStrategies.myOption = function (toVal, fromVal) {
// 返回合并后的值
}
对于多数值为对象的选项,可以使用与 methods
相同的合并策略:
var strategies = Vue.config.optionMergeStrategies
strategies.myOption = strategies.methods
十五. 自定义指令
1. 简介:除了核心功能默认内置的指令 (v-model
和 v-show
),Vue 也允许注册自定义指令。有的情况下,你仍然需要对普通 DOM 元素进行底层操作,这时候就会用到自定义指令。
#全局注册
在main.js中注册,JQuery事件中,当元素获得焦点时,发生 focus 事件。
#局部注册
如果想注册局部指令,组件中也接受一个 directives
的选项:
然后你可以在模板中任何元素上使用新的 v-focus
属性,如下:
2.钩子函数
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
bind:只调用一次,指令第一次绑定到元素时调用,在这里可以进行一次性的初始化设置。
inserted:被绑定元素插入父节点时调用(仅保证父节点存在,但不一定已被插入文档中)。
update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 (详细的钩子函数参数见下)。
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
unbind:只调用一次,指令与元素解绑时调用。
3.钩子函数参数
我只写了一个例子,大致的都相同
这是官网给的例子
#动态指令参数
指令的参数可以是动态的。例如,在 v-mydirective:[argument]="value"
中,argument
参数可以根据组件实例数据进行更新!这使得自定义指令可以在应用中被灵活使用。
例如你想要创建一个自定义指令,用来通过固定布局将元素固定在页面上。我们可以像这样创建一个通过指令值来更新竖直位置像素值的自定义指令:
这会把该元素固定在距离页面顶部 200 像素的位置。但如果场景是我们需要把元素固定在左侧而不是顶部又该怎么办呢?这时使用动态参数就可以非常方便地根据每个组件实例来进行更新。
4.函数简写
如果指向在bind与update的时候执行指令, 那么第二个参数可以直接写函数
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value
})
5.对象字面量
如果指令需要多个值,可以传入一个 JavaScript 对象字面量。记住,指令函数能够接受所有合法的 JavaScript 表达式。
十六. 过滤器
vue允许自定义过滤器,可被用于一些常见的文本格式化。
过滤器可以用在两个地方:双花括号插值和 v-bind
表达式 。
过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:
在{{}}中:{{msg | capitalize}} 在v-bind中:<div :id="rawId | formId" >
在一个组件的选项中定义本地的过滤器:
我这里是让输入的值渲染在页面上的时候前面加上$,
在创建 Vue 实例之前全局定义过滤器:
当全局过滤器和局部过滤器重名时,会采用局部过滤器。
过滤器函数总接收表达式的值 (之前的操作链的结果) 作为第一个参数。在上述例子中,capitalize
过滤器函数将会收到 message
的值作为第一个参数。
过滤器可以串联:
{{ message | filterA | filterB }}
在这个例子中,filterA
被定义为接收单个参数的过滤器函数,表达式 message
的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB
,将 filterA
的结果传递到 filterB
中。
过滤器是 JavaScript 函数,因此可以接收参数:
{{ message | filterA('arg1', arg2) }}
这里,filterA
被定义为接收三个参数的过滤器函数。其中 message
的值作为第一个参数,普通字符串 'arg1'
作为第二个参数,表达式 arg2
的值作为第三个参数。