Vue的特性精华

以下内容是我在学习和研究Vue时,对Vue的特性、重点和注意事项的提取、精练和总结,可以做为Vue特性的字典;

1. 性能优化:

  • 相对于计算属性computed、方法methods、观察watch,优先考虑使用计算属性实现,因为计算属性会缓存它的值,只有当计算属性的相关依赖发生改变时才会重新求值;

  • 尽可能最大化地让Vue复用可用的元素,即,在用key管理可利用元素的地方看是否有其它更快速的替代方案;例如:对于仅展示数据不一样的列表项,尽量设置相同的 key ,以便其重用;

  • 适当地选择v-if和v-show,当元素被渲染后,需要经常被切换时,使用v-show;否则,使用v-if;

  • 对于不会变化的组件或元素,尽量使用v-once指令以使其路过重新渲染;

  • 对于仅使用JavaScript钩子进行过渡和动画的元素,尽量添加v-bind:css="false",这样Vue就会跳过css的检测;从而提高性能;

  • 对于界面布局相同的路由组件,尽可能用同一个路由组件通过路由参数来实现与渲染多个路由组件的效果,因为这不需要销毁再创建路由组件;

  • 如果一个组件不需要生命周期钩子函数,则优先使用 函数式组件 ;比如下列使用场景:

    • 程序化地在多个组件中选择一个;
    • 在将 children, props, data 传递给子组件之前操作它们;
  • 在使用 DOM 内模板或 JavaScript 内的字符串模板时,模板会在运行时被编译为渲染函数;为了减少运行时开销,可以通过打包工具 webpack 直接把模板编译为渲染函数;
    注意:

    • 当使用单文件组件时,脚手架的相关的构建设置会自动把预编译处理好,所以构建好的代码已经包含了编译出来的渲染函数而不是原始的模板字符串;
    • 当分离 JavaScript 和模板文件时,可以为 webpack 设置 vue-template-loader ,它也可以在构建过程中把模板文件转换成为 JavaScript 渲染函数;
  • 当使用单文件组件时,组件内的 CSS 会以 <style> 标签的方式通过 JavaScript 动态注入,这有一些小小的运行时开销,如果你使用服务端渲染,这也会导致一段“无样式内容闪烁 (fouc)”。将所有组件的 CSS 提取到同一个文件可以避免这个问题,也会让 CSS 更好地进行压缩和缓存;

  • 在使用单文件组件时,考虑到浏览器渲染各种 CSS 选择器的方式,当 p { color: red } 设置了作用域时 (即与特性选择器组合使用时) 会慢很多倍。如果你使用 class 或者 id 取而代之,比如 .example { color: red },性能影响就会消除;

  • 尽量给不含转换、编译的节点加上 v-pre 指令,以告诉编译器不需要编译相应节点及其子节点,从而提交编译速度;

  • 对于频繁创建和销毁的组件尝试用 <keep-alive> 组件包裹;

2. v-if和v-show

v-if支持v-else和v-else-if语法,也支持< template>语法;v-show不支持这些;
v-show是通过简单地切换元素的CSS属性display属性来实现显示隐藏效果;
v-if 是“真正的”条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;
v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块;
相比之下, v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换;
也可以使用key标签属性来实现元素的切换显示;

3. 支持< template>的指令

v-if、v-else、v-else-if、v-for,等结构性指令;

4. 模板语法

  1. 双大括号{{表达式}}语法会将表达式的值作为纯文本,而非 HTML;若要输出真正的 HTML ,需要使用 v-html 指令;
  2. 双大括号{{表达式}}语法不能作用在 标签属性上;若要作用在标签属性上,应该使用 v-bind 指令;
  3. 插值模板表达式只能是单个表达式;
  4. 插值表达式是被放在沙盒中执行的,只能访问全局变量的一个白名单,如 Math 和 Date 。不应该在模板表达式中试图访问用户定义的全局变量;

5. 组件

  1. Vue.component的主要功能是注册组件,不是创建组件;
  2. 所有的 Vue.js 组件其实都是被扩展的 Vue 实例;
  3. 使用Vue.component注册组件时,创建Vue实例的代码必须在注册组件的代码的后面,否则注册的组件不会被显示;
  4. Vue.component(id,[definition])的参数id是字符串类型,它用于定义组件的标签名,所以不能通过传入id选择器、类选择器、属性选择器等等来定义组件;

6. Vue特性

  1. 当 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。

  2. 事件可以以链式的方式添加多个事件修饰符,修饰符会按照修饰的顺序起作用;

  3. 在vue中,双大括号{{}}语法不能用在标签属性中;

  4. 在textarea标签中使用双大括号{{}}语法并不会生效,应用v-model来实事插值;

  5. 在Vue中,对于所有的数据绑定,都支持JavaScripot表达式,这些表达式会在所属 Vue 实例的数据作用域下作为 JavaScript 被解析。有个限制就是,每个绑定都只能包含单个表达式;

  6. 当使用DOM 作为模版时 (例如,将 el 选项挂载到一个已存在的元素上), 会受到 HTML 的一些限制,因为 Vue 只有在浏览器解析和标准化 HTML 后才能获取模版内容,所以,如果DOM模版中包含了一些非法的标签,则将会被浏览器移除掉;

  7. 如果需要在某个组件的根元素上监听一个原生事件,可以使用 .native 修饰 v-on绑定的事件;

  8. 只能在字符串模板中使用自动闭合的组件名标签,因为对浏览器来说,自动闭合的自定义元素是无效的HTML标签;

  9. Vue 不允许在已经创建的实例上动态添加新的根级响应式属性(root-level reactive property);

  10. 当你设置 vm.someData = 'new value' ,该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新;

  11. 如果同时设置了transitionend 和animationend ,则需要使用transition标签的 type 标签属性并设置 animation 或 transition 来明确声明你需要 Vue 监听的类型;

  12. 使用 FLIP 过渡的元素不能设置为 display: inline 。作为替代方案,可以设置为 display: inline-block 或者放置于 flex 中;

  13. 当 v-bind:style 使用需要特定前缀的 CSS 属性时,如 transform,Vue.js 会自动侦测并添加相应的前缀;

7. 当创建组件时,data选项应是是函数类型的原因

选项data可以是Object类型,也可以是Function类型,当data是Function类型时,data函数必须返回一个Object实例作为真正的data选项的值;
但在定义组件时,选项data最好应该是函数类型,因为,如果data是对象类型,则当此组件需要被用来创建多个实例时,则所有的实例将都引用同一个data实例;然而,如果将data定义为函数类型,并在函数里面返回新创建的对象,则每当此组件创建实例时,就会引用 data函数返回的全新的对象,从而不会使此组件的实例共享同一个data对象;

8. 指令

指令(Directives)是带有 v- 前缀的特殊标签属性。指令属性的值预期是单一 JavaScript 表达式(除了 v-for)。指令的职责就是当其表达式的值改变时相应地将某些行为应用到 DOM 上。

  • 参数:一些指令能接受一个“参数”,在指令后以冒号指明;
  • 修饰符:修饰符(Modifiers)是以半角句号.指明的特殊后缀,用于指出一个指令应该以特殊方式绑定;
  • v-bind: 可以简写为 :
  • v-on: 可以简写为 @
  • :@ 对于标签的特性名来说都是合法字符,在所有支持 Vue.js 的浏览器都能被正确地解析。而且,它们不会出现在最终渲染的标记中。缩写语法是完全可选的;

9. 过滤器

Vue允许你自定义过滤器,可被用作一些常见的文本格式化。过滤器可以用在两个地方:{{}} 插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由管道符|指示开始位置;

10. 选项:template

  • 类型:string
  • 说明:
    一个字符串模板作为 Vue 实例的标识使用。模板将会 替换 挂载的元素。挂载元素的内容都将被忽略,除非模板的内容有分发 slot。
    如果值以 # 开始,则它会被当作ID选择器,将使用匹配元素的 innerHTML 作为模板。
    如果选项中不包含template,则Vue实例将使用被挂载的元素的outerHTML作为template;
    如果选项中包含 render 函数,template 选项将被忽略。

11. 选项:data

  • 类型:Object | Function
  • 限制:组件的定义只接受 function。
  • 说明:
    Vue 实例的数据对象。Vue 将会递归将 data 的属性转换为 getter/setter,从而让 data 的属性能够响应数据变化。对象必须是纯粹的对象 (含有零个或多个的 key/value 对):浏览器 API 创建的原生对象,原型上的属性会被忽略。大概来说,data 应该只能是数据 - 不推荐观察拥有状态行为的对象。
    一旦观察过,不需要再次在数据对象上添加响应式属性。因此推荐在创建实例之前,就声明所有的根级响应式属性。
    实例创建之后,可以通过 vm.$data 访问原始数据对象。Vue 实例也代理了 data 对象上所有的属性,因此访问 vm.a 等价于访问 vm.$data.a。
    以 _ 或 $ 开头的属性 不会 被 Vue 实例代理,因为它们可能和 Vue 内置的属性、API 方法冲突。你可以使用例如 vm.$data._property 的方式访问这些属性。

12. 选项:computed

  • 类型:{ [key: string]: Function | { get: Function, set: Function } }
  • 说明:
    计算属性将被混入到 Vue 实例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。
    计算属性的结果会被缓存,除非依赖的响应式属性变化才会重新计算。注意,如果实例范畴之外的依赖 (比如非响应式的 not reactive) 是不会触发计算属性更新的。
    注意: 不应该使用箭头函数来定义计算属性函数 (例如 aDouble: () => this.a * 2)。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined。

13. 选项:watch

  • 类型: { [key: string]: string | Function | Object }
  • 说明:
    一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性。

14. 标签属性key的理解

Vue 为了尽可能高效地渲染元素,通常会复用已有元素而不是重新创建元素;为了不让vue复用已有的元素,可以给元素加个标签属性key,并给该标签属性赋一个字符串类型的值;我认为这个标签属性key可以理解为IOS中的重用标识符;

15. 数组

由于 JavaScript 的限制, Vue 不能检测以下变动的数组:

  1. 当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength

为了解决第一类问题,以下两种方式都可以实现和 vm.items[indexOfItem] = newValue 相同的效果, 同时也将触发状态更新:
方法1:

// Vue.set
Vue.set(example1.items, indexOfItem, newValue)

方法2:

// Array.prototype.splice
example1.items.splice(indexOfItem, 1, newValue)

为了解决第二类问题,可以使用splice

example1.items.splice(newLength)

16. 对象更改检测注意事项

  1. 由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除;
  2. 对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, key, value) 方法向嵌套对象添加响应式属性;

17. 事件

所有的 Vue.js 事件处理方法和表达式都严格绑定在当前视图的ViewModel上;当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除;
可以通过Vue.config.keyCodes对象自定义键值修饰符别名,格式如下:

// 可以使用 v-on:keyup.f1
Vue.config.keyCodes.f1 = 112

或者:

Vue.config.keyCodes = {
  v: 86,
  f1: 112,
  // camelCase 不可用
  mediaPlayPause: 179,
  // 取而代之的是 kebab-case 且用双引号括起来
  "media-play-pause": 179,
  up: [38, 87]
}

18. 双向绑定v-model指令

  1. v-model 会忽略所有表单元素的 value、checked、selected 特性的初始值。因为它会选择 Vue 实例数据来作为具体的值。所以应该通过 JavaScript 在组件的 data 选项中声明初始值。
  2. 当在select元素中使用v-model时,如果 v-model 表达初始的值不匹配任何的选项,select 元素就会以”未选中”的状态渲染。

19. props

  1. 在使用组件时,若需要通过标签属性给子组件传数据,则需要先显式地在 props 选项声明;
  2. prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是不会反过来。
  3. 在JavaScript 中对象是引用类型,如果 prop 是一个对象,在子组件内部改变它的属性值会影响父组件的状态;
  4. props 会在组件实例创建之前进行校验,所以在 default 或 validator 函数里,诸如 data、computed 或 methods 等实例属性还无法使用。

20. Vue中实现双向绑定的指令

Vue中实现双向绑定的所有方式都是一个语法糖,本质都是通过v-bind指令 和 v-on指令实现的;
它们的具体实现方式如下:

  1. 修饰符.sync
    示例:

    < comp v-bind:foo.sync="bar"></comp>
    

    等效语法:

    < comp :foo="bar" @update:foo="val => bar = val"></comp>
    

    当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:

    this.$emit('update:foo', newValue)
    
  2. 指令v-model

    1. v-model用于原生input元素时,有如下等效:
      示例:
    < input v-model="something">
    

    等效语法:

    < input
      v-bind:value="something"
      v-on:input="something = $event.target.value">
    
    1. v-model用于自定义组件时,有如下等效:
      示例:
    < my-component v-model="something">
    

    等效语法:

    < my-component
      v-bind:value="something"
      v-on:input="something = arguments[0]">< /my-component>
    

    所以,如果要在组件上使用v-model,则组件应该具备以下条件:

    1. 有一个名字是value的prop;
    2. 当需要向父组件更新新值时,需要通过触发名字为input的事件传值;
    1. 更改组件中v-model默认绑定的prop和事件
      默认情况下,一个组件的 v-model 会使用 value 属性和 input 事件,不过,可以通过选项model更改v-model默认绑定的prop和事件;
      示例如下:
    Vue.my-component('my-component', {
      props: {
        checked: Boolean
      },
      model: {
        prop: 'checked',
        event: 'change'
      }
    })
    

    然后,可以如下使用v-model:

    < my-component v-model="foo" value="some value">< /my-component>
    

    等效语法:

    < my-component
      :checked="foo"
      @change="val => { foo = val }">< /my-component>
    

21. 内容分发

  1. 如果组件模板中没有包含slot插口,则在使用组件时,组件标签内的内容将被丢弃;
  2. 如果组件模板只有一个没有属性的slot,则在使用组件时,组件标签内的内容都将插入slot所在的DOM位置,并替换掉slot标签本身;
  3. 组件模版中slot标签中的任何内容都被视为备用内容;备用内容在子组件的作用域内编译;并有只有没有要插入的内容时,才会显示备用内容;
  4. 可以通过配置slot的name标签属性来分发内容;仍然可以有一个匿名 slot,它是默认 slot,作为找不到匹配的内容片段的备用插槽。如果没有默认的 slot,这些找不到匹配的内容片段将被抛弃;

22. 作用域插槽

作用域插槽是一种特殊类型的插槽,用作使用一个 (能够传递数据到) 可重用模板替换已渲染元素;
使用方式如下:
在组件模板中给slot标签设置的标签属性,可以在使用组件时,通过该组件标签内部的template元素的scope标签属性访问;
具体示例如下:
子组件的模板:

< div class="child">
  < slot text="hello from child">< /slot>
< /div>

使用子组件:

< div class="parent">
  < child>
      < template scope="props">
          < span>hello from parent< /span>
      < span>{{ props.text }}< /span>
      < /template>
    < /child>
< /div>

标签属性scope是被用来定义临时变量的,被定义的临时变量就是scope的值,也就是说:此例中的props是被定义临时变量;这个临时变量保存的是从子组件中传递出来的props对象;
如果我们渲染以上结果,得到的输出会是:

< div class="parent">
  < div class="child">
      < span>hello from parent< /span>
      < span>hello from child< /span>
  < /div>
< /div>

注意:

  • slot-scope 的值实际上是一个可以出现在函数签名参数位置的合法的 JavaScript 表达式;也就是说,slot-scope 的值在解析后会成为函数的参数,所以,一切合法的 函数参数 表达式 都可作为 slot-scope 的值。这也意味着在受支持的环境 (单文件组件或现代浏览器) 中,您还可以在表达式中使用 ES2015 解构:slot-scope="{ text }"
  • 在 版本2.5.0+的Vue中,slot-scope 能被用在任意元素或组件中而不再局限于 <template>

23. 动态定义组件的模板的方式

  1. 内容分发:用slot标签;
  2. 作用域插槽:用template和slot标签;
  3. 动态组件:用预定义的component标签;
  4. 内联模版:用标签属性inline-template;
  5. 路由;

24. 路由

  1. 同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高;

25. 子组件实例的引用

通过如下方式,可以在JavaScript中获取子组件实例的引用:

  1. 在子组件上设置 ref 标签属性;

    <child-component ref="引用名字" >
    
  2. 通过当前组件实例的 $refs 属性 当前组件实例.$refs.引用名字 获取子组件实例的引用,如:

    this.$refs.引用名字
    

注意:

  • 当 ref 和 v-for 一起使用时,获取到的引用会是一个数组,包含和循环数据源对应的子组件;
  • $refs 只在组件渲染完成后才填充,并且它是非响应式的。它仅仅是一个直接操作子组件的应急方案——应当避免在模板或计算属性中使用 $refs ;

26. 内联模板

如果子组件有 inline-template 特性,组件将把它的内容当作它的模板,而不是把它当作分发内容。这让模板编写起来更灵活。

<my-component inline-template>
  <div>
    <p>这些将作为组件自身的模板。</p>
    <p>而非父组件透传进来的内容。</p>
  </div>
</my-component>

但是 inline-template 让模板的作用域难以理解。使用 template 选项在组件内定义模板或者在 .vue 文件中使用 template 元素才是最佳实践。

27. X-Template

另一种定义模板的方式是在 JavaScript 标签里使用 text/x-template 类型,并且指定一个 id。例如:

<script type="text/x-template" id="hello-world-template">
  <p>Hello hello hello</p>
</script>
Vue.component('hello-world', {
  template: '#hello-world-template'
})

这在有很多大模板的演示应用或者特别小的应用中可能有用,其它场合应该避免使用,因为这将模板和组件的其它定义分离了。

推荐阅读更多精彩内容

  • 这篇笔记主要包含 Vue 2 不同于 Vue 1 或者特有的内容,还有我对于 Vue 1.0 印象不深的内容。关于...
    云之外阅读 2,687评论 0 30
  • 1.安装 可以简单地在页面引入Vue.js作为独立版本,Vue即被注册为全局变量,可以在页面使用了。 如果希望搭建...
    Awey阅读 7,461评论 5 126
  • Vue 实例 属性和方法 每个 Vue 实例都会代理其 data 对象里所有的属性:var data = { a:...
    云之外阅读 582评论 0 6
  • 下载安装搭建环境 可以选npm安装,或者简单下载一个开发版的vue.js文件 浏览器打开加载有vue的文档时,控制...
    冥冥2017阅读 3,431评论 0 43
  • 此文基于官方文档,里面部分例子有改动,加上了一些自己的理解 什么是组件? 组件(Component)是 Vue.j...
    陆志均阅读 1,769评论 5 15