Vue.js 要点

1 minute read

关于 Vue

Vue 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。

下面是渐进式(自底向上)的图示:

渐进式框架

看看 Vue 作者的描述:

Vue从设计角度来讲,虽然能够涵盖这张图上所有的东西,但是你并不需要一上手就把所有东西全用上,因为没有必要。无论从学习角度,还是实际情况,这都是可选的。声明式渲染和组建系统是Vue的核心库所包含内容,而客户端路由、状态管理、构建工具都有专门解决方案。这些解决方案相互独立,你可以在核心的基础上任意选用其他的部件,不一定要全部整合在一起。

响应式原理

  1. 在响应式原理中是如何追踪变化的呢?

    原理是把一个对象传给 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。

  2. 每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

    响应式原理的图示:

    响应式原理

生命周期

生命周期是指每个 Vue 实例在被创建时都要经过一系列的初始化过程。这个过程中会运行一些叫做生命周期钩子的函数,给用户在不同阶段添加自己的代码。

下面是生命周期图示,其中红色字就是生命周期钩子:

生命周期

缓存

我们为什么需要缓存?

假设我们有一个性能开销比较大的的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter。如果你不希望有缓存,请用 methods 来替代。

计算属性

  1. 模板内的表达式可以对 data 做额外操作,但是设计它的初衷是用于简单运算的,所以对于任何复杂逻辑,应当使用 computed。

    除了 computed 还可以用一个 methods 实现复杂逻辑,两种方式的最终结果确实是完全相同的。不同的是 computed 是基于它们的依赖进行缓存的。

  2. computed 取值操作时,getter 会被调用,运行赋值语句时,setter 会被调用。

data

当一个组件(.vue)被定义,data 必须声明为返回一个函数,因为组件可能被用来创建多个实例。如果返回一个对象,则所有实例将共享同一个对象。

侦听器

Vue 有个选项叫 watch,它和 computed 看起来很像,其实是有区别的。

当需要在数据变化时执行异步或开销较大的操作时,watch 选项是最有用的。它允许我们执行异步操作,限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性 computed 无法做到的。

条件渲染

看个例子:

<template v-if="loginType === 'username'">
  <label>Username</label>
  <input placeholder="Enter your username" key="username-input">
</template>
<template v-else>
  <label>Email</label>
  <input placeholder="Enter your email address" key="email-input">
</template>

这个例子要注意:

  1. 使用条件渲染多个元素时,可以将 v-if 放在不可见元素(如 template)
  2. v-else 元素必须紧跟在带 v-if 或者 v-else-if 的元素的后面。
  3. 添加一个具有唯一值的 key 属性,使得两个元素是完全独立的。

除了 v-if,有时会看到用 v-show 判断,区别是 v-if 的开销比 v-show 大,所以:

  1. 如果需要非常频繁地切换,使用 v-show 较好。
  2. 如果在运行时条件很少改变,使用 v-if 较好。

关于循环和判断的优先关系,当 v-for 和 v-if 处于同一节点,for 的优先级比 if 高,即循环判断。

列表渲染

  1. 列表渲染时加不加 key 看起来没有差别,不加 key 是高效的,不过只适用于不依赖子组件状态或临时 DOM 状态(?)的列表渲染。
  2. 列表渲染一般是操作数组,关于操作数组时是否会触发视图更新的条件如下:

    能改变原数组的就会触发视图更新:push()pop()shift()unshift()splice()sort()reverse()

    不能改变原数组的不会触发视图更新:filter()concat()slice()

    用索引赋值不会触发视图更新:item[index] = newValue

    用 set 赋值会触发视图更新:vm.$set(item, index, newValue) 或者 Vue.set(item, index, newValue)

  3. 列表渲染时不能检测对象属性的添加或删除:

     let vm = new Vue({
       el: '#list',
       data: {
         obj: {
           a: 1
         }
       }
     });
     vm.obj.b = 4; // 不响应
    
  4. 列表渲染时不能动态添加根级别的响应式属性:

     let vm = new Vue({
       el: '#list',
       data: {
         a: 1
       }
     });
     vm.$set(vm, 'b', 4); // 报错
    

    但是通过 set 可以给嵌套对象添加响应式属性:

     let vm = new Vue({
       el: '#list',
       data: {
         obj: {
           a: 1
         }
       }
     });
     vm.$set(vm.obj, 'b', 4);
    
  5. 在自定义组件使用列表渲染时,我们要用 props 把数据传给组件,而不是注入组件,因为组件有自己的作用域。使用 props 使得组件可以在其它场合利用,用法如下:

     <todo-item v-for="todo in todos" :title="todo.title"></todo-item>
    

    这里的 title 是在父组件中定义的,然后在注册组件时传入:

     Vue.component('todo-item', {
       template: ``,
       props: ['title']
     });
    

事件处理

在实际业务中,常常会使用 event.preventDefault()event.stopPropagation(),但 Vue 建议方法中只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。可以使用事件修饰符来代替它们:.stop.prevent

此外还有其它修饰符 .capture.self.once.passive

表单输入绑定

  1. Vue 用 v-model 来绑定表单输入,v-model 本质上不过是语法糖:

     <input v-model="searchText">
    

    等价于:

     <input v-bind:value="searchText" v-on:input="searchText = $event.target.value">
    
  2. 对于单选按钮,复选框及选择框的选项,v-model 绑定的值通常是静态字符串 (对于复选框也可以是布尔值)。

组件

组件 (Component) 是 Vue.js 最强大的功能之一。

组件是自定义元素,也可以表现为用 is 特性进行了扩展的原生 HTML 元素。

关于组件的父子关系:

  1. 组件设计初衷就是要配合使用的,最常见的就是形成父子组件的关系:组件 A 在它的模板中使用了组件 B。它们之间必然需要相互通信:父组件可能要给子组件下发数据,子组件则可能要将它内部发生的事情告知父组件。然而,通过一个良好定义的接口来尽可能将父子组件解耦也是很重要的。这保证了每个组件的代码可以在相对隔离的环境中书写和理解,从而提高了其可维护性和复用性。
  2. 关于父子组件通信可以参考:vue2.0 子组件和父组件之间的传值
  3. 父组件模板的内容在父组件作用域内编译;子组件模板的内容在子组件作用域内编译。

关于 prop:

  1. 父子组件的关系可以总结为 prop 向下传递,事件向上传递。
  2. prop 是单向绑定的:当父组件的属性变化时,将传导给子组件,但是反过来不会。这是为了防止子组件无意间修改了父组件的状态,来避免应用的数据流变得难以理解。
  3. prop 会在组件实例创建之前进行校验,所以在 default 或 validator 函数里,诸如 data、computed 或 methods 等实例属性还无法使用。
  4. 所谓非 prop 特性,就是指它可以直接传入组件,而不需要定义相应的 prop。
  5. 在 JavaScript 中对象和数组是引用类型,指向同一个内存空间,如果 prop 是一个对象或数组,在子组件内部改变它会影响父组件的状态。

关于事件:

自定义事件可以用来创建自定义的表单输入组件,使用 v-model 来进行数据双向绑定。

异步更新队列

  1. 只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。
  2. Vue 在内部尝试对异步队列使用原生的 Promise.then 和 MessageChannel,如果执行环境不支持,会采用 setTimeout(fn, 0) 代替。

vue-router

Vue Router 是路由管理器。

  1. 导航守卫用来解决 URL 不合规范的问题
  2. Redirect 用来解决默认路由的问题

AJAX

在 Vue 中如何使用 axios 跨域访问数据
axios 请求问题

学习 Vue 的心得

没使用过框架的人读文档可能会感觉吃力,看完文档再看看风格指南会更明白为什么要按某种格式写代码。

相关

Vue 2.0——渐进式前端解决方案
Vue2.0 中,“渐进式框架”和“自底向上增量开发的设计”这两个概念是什么?

Updated:

Leave a comment