指令是Vue.js提供的带有v-前缀的特殊特性,用于辅助开发者渲染页面的基本结构。
指令相关的知识贯穿Vue.js内部各个核心技术点。在模板解析阶段,我们在将指令解析到AST的directives属性中,然后使用AST生成代码字符串的过程中实现某些内置指令的功能,最后在虚拟DOM渲染的过程中触发自定义指令的钩子函数使指令生效。

v-text 几乎不用 <p v-text="age">年龄</p>// 会覆盖元素内部原有的内容
{{}} 插值表达式(Mustache) 实际开发中用的最多 支持JS语句<!-- {{}} 只是内容的占位符,不会覆盖原有的内容 -->
<p>年龄:{{age}}</p>
v-html 可以把包含HTML标签的字符串渲染到页面 <p v-html="info"></p>
info: "个人信息
",
v-bind 给元素属性动态绑定值,简写英文 : 姓名: <input type="text" v-bind:placeholder="tips">
tips: '请输入名称',
补充 :在使用v-bind属性绑定期间,如果绑定内容需要进行动态拼接,则字符串的外面应该包裹单引号,支持JS运算,例如:
<div :title=" 'box' + index ">这是一个div</div>
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;
}
}
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">
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>
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>
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’的值作为第三个参数
{{message | capitalize}}
这个过滤器在模板编译阶段会编译成下面的样子:
_s(_f("capitalize")(message))
这个代码原理简单的来说,其实就是执行了capitalize过滤器函数并把message当作参数传递过去,接着将capitalize过滤器处理后的结果当作参数传递给toString函数。最终toString函数执行后的结果会保存到VNode中的text属性,去渲染视图。
其中_s函数是toString函数的别名,_f函数是resolveFilter的别名,其作用是从 this.$options.filters中找出注册的过滤器并返回。
_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中查找过滤器即可。
parseFilters函数,专门用来解析过滤器,它可以将模板过滤器解析成过滤器函数调用表达式。逻辑就是,解析出过滤器的列表后,循环过滤器列表并拼接一个字符串即可。
key这个特殊属性主要用在Vue.js的虚拟DOM算法中,在更新子节点时,需要从旧虚拟节点列表中查找与新虚拟节点相同的节点进行更新。如果这个查找过程设置了属性key,那么查找速度会快很多。所以无论何时,建议尽可能地在使用v-for时提供key。
如果一组 v-if + v-else 的元素类型相同,最好使用属性key。默认情况下,Vue.js会尽可能高效地更新DOM。这意味着,当它在相同类型的元素之间切换时,会修补已存在的元素,而不是将旧的元素移除,然后再同一位置添加一个新元素。如果本不相同的元素被识别为相同,则会出现意料之外的副作用。如果添加了属性key,那么在对比虚拟DOM时,则会认为它们是两个不同的节点,于是会将旧元素移除并在相同的位置添加一个新元素,从而避免意料之外的副作用。
在使用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就会重新创建这个组件。所以组件内的生命周期会重复触发。
优点:简单粗暴,改动小;
缺点:每次切换路由组件时都会被销毁并且重新创建,非常浪费性能。
通常,在项目开发中,业务组件会使用Vuex维护状态,使用不同组件统一操作Vuex中的状态。这样不论是父子组件间的通信还是兄弟组件间的通信,都很容易。
对于通用组件,我会使用props语句事件进行父子组件间的通信(通用组件不需要兄弟组件间的通信)。这样做是因为通用组件会拿到各个业务组件中使用,它要与业务解耦,所以需要使用props获取状态。
当Vue.js处理指令时,v-for比v-if具有更高的优先级。
Css 的规则都是全局的,任何一个组件的样式规则都对整个页面有效。因此,我们很容易在一个组件中写了某个样式,而不小心影响了另一个组件的样式,或者自己的组件被第三方库的CSS影响了。
解决方法:在Vue.js中,可以通过scoped特性或CSS Modules(一个基于class的类似BEM的策略)来设置组件式作用域。


在组件库,我们应该更倾向于选用基于class的策略而不是scoped特性
在scoped样式中,类选择器比元素选择器更好,因此大量使用元素选择器是很慢的。
我们应该优先通过prop和事件进行父子组件之间的通信,而不是使用this.$parent或改变prop。
单文件组件的文件名应该始终是单词首字母大写,或者始终是横线连接的;
应用特定样式和约定的基础组件(也就是展示类的,无逻辑的或无状态的组件)应该全部以一个特定的前缀开头,比如Base、App或V;


只拥有单个活跃实例的组件以The前缀命名,以示其唯一性;

和父组件紧密耦合的子组件应该以父组件名作为前缀命名;
组件名应该倾向于完整单词而不是缩写;
在单文件组件和字符串模板中的组件名应该总是单词首字母大写,但是在DOM模板中总是横线连接的,JS中,单词首字母大写的是类和构造函数;
多个特性的元素应该分多行撰写,每个特性一行。

《深入浅出Vue.js》已经阅读完成,这本书还是蛮不错的,如果有写的不好的地方,欢迎来讨论
