目录
第15章 指令的奥秘
指令是
Vue.js
提供的带有
v-
前缀的特殊特性,用于辅助开发者渲染页面的基本结构。
15.1 指令原理概述
指令相关的知识贯穿
Vue.js
内部各个核心技术点。在模板解析阶段,我们在将指令解析到
AST
的
directives
属性中,然后使用
AST
生成代码字符串的过程中实现某些内置指令的功能,最后在虚拟DOM渲染的过程中触发自定义指令的钩子函数使指令生效。
15.2 指令的分类
15.2.1 内容渲染指令
v-text
几乎不用
<p v-text="age">年龄</p>// 会覆盖元素内部原有的内容
{{}}
插值表达式(Mustache) 实际开发中用的最多 支持JS语句
<!-- {{}} 只是内容的占位符,不会覆盖原有的内容 --><p>年龄:{{age}}</p>
v-html
可以把包含HTML标签的字符串渲染到页面
<p v-html="info"></p>
info: "<h2 color='red'>个人信息</h2>",
15.2.2 属性绑定指令
v-bind
给元素属性动态绑定值,简写英文 :
姓名: <input type="text" v-bind:placeholder="tips">
tips: '请输入名称',
补充 :在使用
v-bind
属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,支持JS运算,例如:
<div :title=" 'box' + index ">这是一个div</div>
15.2.3 事件绑定指令
v-on
事件绑定,简写为@
<button v-on:click="add(2)">+1</button>
methods: {
// add:function(){} 简写如下
add(n){
this.count += n; // this就是vm实例,它里面有个count值
},
reduce(){
this.count -=1;}}
15.2.4 双向绑定指令
v-model
事件修饰符
<--.number 自动将用户的输入的内容进行处理,转换为数字格式
.trim 自动过滤用户输入的首尾空白字符
.lazy 在失去焦点的时候更新--><input type="text" v-model.number="n1">+<input type="text" v-model.number="n2">={{n1+n2}}<hr><input type="text" v-model.trim="username"><button @click="showbtn">点击获取名字</button><hr><input type="text" v-model.lazy="username">
15.2.5 条件渲染指令
v-if / v-show
按需控制DOM的显示与隐藏
<!-- v-if 是动态创建和动态移除来实现的 一般这个用的多--><!-- 如果刚进入页面的时候,某些元素不需要被展示,而且后期这个元素很可能不需要被展示,v-if性能会更好 --><p v-if="flag">这是被v-if所控制的元素</p><!-- v-show 是动态添加或移除style="display:none"样式来实现元素显示与隐藏 --><!-- 如果要频繁的切换元素的显示状态,用v-show性能最好的 --><p v-show="flag">这是被v-show所控制的元素</p>
v-if v-else-if v-else
<div v-if="type=='A'">优秀</div><div v-else-if="type=='B'">良好</div><div v-else-if="type=='C'">一般</div><div v-else="type=='D'">较差</div>
15.2.6 列表渲染指令
v-for
基于一个数组来循环渲染一个列表结构
<tbody>
// index 是索引
// list是data中的列表
// v-for 指令,一定要绑定一个:key属性,尽量把id作为key的值
// key值不能重复,否则会在终端报错
// 官方对key的值类型,是有要求的:字符串或者数字类型
<tr v-for="(item ,index)in list" :key="item.id" :title="item.name"><td>{{index}}</td><td>{{item.id}}</td><td>{{ item.name })</td></tr></tbody>
第16章 过滤器的奥秘
Vue.js
运行我们自定义过滤器来格式化文本。它可以用在两个地方:双花括号插值 和 **
v-bind
表达式**。它应该被添加在JavaScript表达式的尾部,由“管道”符号指示:
// 在双花括号中
{{message | capitalize}}
// 在v-bind中
<div v-bind:id="rawId | formatId"></div>
此外,过滤器可以串联,比如:
{{message | filterA | filterB}}
在这个例子中,fillterA被定义为接收单个参数的过滤器函数,表达式message的值多为参数传入到fillterA过滤器函数中。然后将过滤函数fillterA的执行结果当作参数传递给fillterB函数。
过滤器还可以接收参数,比如:
{{message | filterA('arg1','arg2')}}
在这个例子中,filterA被定义为接收三个参数的过滤器函数。其中message的值作为第一个参数,普通字符串‘arg1’作为第二个参数,表达式’arg2’的值作为第三个参数
16.1 过滤器原理概述
{{message | capitalize}}
这个过滤器在模板编译阶段会编译成下面的样子:
_s(_f("capitalize")(message))
这个代码原理简单的来说,其实就是执行了
capitalize
过滤器函数并把message当作参数传递过去,接着将
capitalize
过滤器处理后的结果当作参数传递给
toString
函数。最终
toString
函数执行后的结果会保存到
VNode
中的text属性,去渲染视图。
其中
_s
函数是
toString
函数的别名,
_f
函数是
resolveFilter
的别名,其作用是从
this.$options.filters
中找出注册的过滤器并返回。
resolveFilter的内部原理
_f
函数是
resolveFilter
函数的别名。
resolveFilter
代码如下:
resolveAsset
查找过滤器代码如下:
这里首先判断参数id 的类型(它是过滤器id ),它必须是字符串类型,如果不是,则使用return语句终止函数继续执行。随后声明变量
assets
并将
options[type]
保存到该变量中。事实上,
resolveAsset
函数除了可以查找过滤器外,还可以查找组件和指令。
本例中变量
assets
中保存的是过滤器集合。
1、通过
hasOwn
函数检查
assets
自身是否存在
id
属性,如果存在,则直接返回结果。
hasOwn
函数基于
0bject.prototype.hasOwnProperty
实现。
2、如果不存在,则使用函数
camelize
将
id驼峰化
之后再检查
assets
身上是否存在将
id驼峰化
之后的属性。
3、如果驼峰化后的属性也不存在,那么使用
capitalize
函数将
id的首字母大写
后再次检查
assets
中是否存在。
4、如果还是找不到,那么按照前面的顺序重新查找―遍属性,不同的是这次将检查
原型链
.'查找原型链很简单:只需要访问属性即可。如果找到,则返回过滤器。如果找不到,那么在非生产环境下在控制台打印警告。最后,无论是否找到,都返回查找结果。
注册过滤器有两种途径:注册全局过滤器 和 在组件的选项中定义本地的过滤器。全局注册的过滤器会保存在
Vue
构造函数中。
而resolveAsse
函数在查找过滤器的过程中并没有去
vue
构造函数中搜索过滤器。这是因为在初始化
Vue.js
实例时,把全局过滤器与组件内注册的过滤器合并到
this.$options.filters
中了,而
this.$options.filters
其实同时保存了全局过滤器和组件内注册的过滤器。
resolveAsset
只需要从
this.$options.filters
中查找过滤器即可。
16.2 解析过滤器
parseFilters
函数,专门用来解析过滤器,它可以将模板过滤器解析成过滤器函数调用表达式。逻辑就是,解析出过滤器的列表后,循环过滤器列表并拼接一个字符串即可。
第17章 最佳实践
17.1 为列表渲染设置属性key
key
这个特殊属性主要用在
Vue.js
的虚拟DOM算法中,在更新子节点时,需要从旧虚拟节点列表中查找与新虚拟节点相同的节点进行更新。如果这个查找过程设置了属性key,那么查找速度会快很多。所以无论何时,建议尽可能地在使用v-for时提供key。
17.2 在 v-if / v-if-else / v-else 中使用key
如果一组
v-if + v-else
的元素类型相同,最好使用属性key。默认情况下,
Vue.js
会尽可能高效地更新DOM。这意味着,当它在相同类型的元素之间切换时,会修补已存在的元素,而不是将旧的元素移除,然后再同一位置添加一个新元素。如果本不相同的元素被识别为相同,则会出现意料之外的副作用。如果添加了属性
key
,那么在对比虚拟DOM时,则会认为它们是两个不同的节点,于是会将旧元素移除并在相同的位置添加一个新元素,从而避免意料之外的副作用。
17.3 路由切换组件不变
在使用
Vue.js
开发项目时,最常遇到的一个典型问题就是,当页面切换到同一个路由但不同参数的地址时,组件的生命周期钩子并不会重新触发。
这是意味
vue-router
会识别出两个路由使用的是同一个组件从而进行复用,并不会重新创建组件,因此组件的生命周期钩子自然也不会被触发。
下面总结3个方法来解决这个问题:
1、路由导航守卫
beforeRouteUpdate
组件的生命周期钩子虽然不会重新触发,但是路由提供的
beforeRouteUpdate
守卫可以被触发。因此,只需要把每次切换路由时需要执行的逻辑放到
beforeRouteUpdate
守卫中即可。这是比较推荐的一种方式。
2、观察
$route
对象的变化
通过
watch
可以监听到路由对象发生的变化,从而对路由变化作出响应。
这个方式也可以解决上述问题。但代价是组件内多了一个
watch
,这会带来依赖追踪的内存开销。如果最终选择使用
watch
解决这个问题,推荐只观察自己需要的
query
,这样有利于减少不必要的请求。
3、为
router-view
组件添加属性
key
这中做法非常取巧,非常“暴力”,但非常有效。它本质上是利用虚拟DOM在渲染时通过
key
来对比两个节点是否相同的原理。通过给
router-view
组件设置
key
,可以使每次切换路由时的
key
都不一样,让虚拟DOM认为
router-view
组件时一个新节点,从而先销毁组件,让后再重新创建新组件。即使是相同的组件,但是如果url变了,key就变了,
Vue.js
就会重新创建这个组件。所以组件内的生命周期会重复触发。
优点:简单粗暴,改动小;
缺点:每次切换路由组件时都会被销毁并且重新创建,非常浪费性能。
17.4 区分Vuex与props的使用边界
通常,在项目开发中,业务组件会使用
Vuex
维护状态,使用不同组件统一操作
Vuex
中的状态。这样不论是父子组件间的通信还是兄弟组件间的通信,都很容易。
对于通用组件,我会使用
props
语句事件进行父子组件间的通信(通用组件不需要兄弟组件间的通信)。这样做是因为通用组件会拿到各个业务组件中使用,它要与业务解耦,所以需要使用
props
获取状态。
17.5 避免 v-if 和 v-for 一起使用
当
Vue.js
处理指令时,
v-for
比
v-if
具有更高的优先级。
17.6 为组件样式设置作用域
Css 的规则都是全局的,任何一个组件的样式规则都对整个页面有效。因此,我们很容易在一个组件中写了某个样式,而不小心影响了另一个组件的样式,或者自己的组件被第三方库的CSS影响了。
解决方法:在Vue.js中,可以通过
scoped
特性或
CSS Modules
(一个基于class的类似BEM的策略)来设置组件式作用域。
在组件库,我们应该更倾向于选用基于class的策略而不是scoped特性
17.7 避免在scoped中使用元素选择器
在
scoped
样式中,类选择器比元素选择器更好,因此大量使用元素选择器是很慢的。
17.8 避免隐性的父子组件通信
我们应该优先通过
prop
和事件进行父子组件之间的通信,而不是使用
this.$parent
或改变
prop
。
17.9 单文件组件的文件名的大小写
单文件组件的文件名应该始终是单词首字母大写,或者始终是横线连接的;
应用特定样式和约定的基础组件(也就是展示类的,无逻辑的或无状态的组件)应该全部以一个特定的前缀开头,比如
Base
、
App
或
V
;
只拥有单个活跃实例的组件以
The
前缀命名,以示其唯一性;
和父组件紧密耦合的子组件应该以父组件名作为前缀命名;
组件名应该倾向于完整单词而不是缩写;
在单文件组件和字符串模板中的组件名应该总是单词首字母大写,但是在DOM模板中总是横线连接的,JS中,单词首字母大写的是类和构造函数;
多个特性的元素应该分多行撰写,每个特性一行。
《深入浅出Vue.js》已经阅读完成,这本书还是蛮不错的,如果有写的不好的地方,欢迎来讨论
版权归原作者 *neverGiveUp* 所有, 如有侵权,请联系我们删除。