Vue 介绍

Vue PS:最近入坑 Vue,发现了个宝藏,要好好总结。

Feature(功能/特性)

Vue 读作/vju:/(与 view 同音),是一个基于 js 的前端渐进式框架。它按照 MVVM 模式设计,专注于 View 层。

界面及数据互动更新

Vue 对象可以与一个 Dom 对象绑定(两者可以是复杂嵌套对象),底层数据的任何变化(经过过滤)都可以表达在界面上,而 input 类型的 Dom 对象的任何输入变化也都可以(经过过滤,将有效值 )自动写入绑定的底层数据对象。这就是 MVVM 模式的基本逻辑。

渐进式

Vue 的渐进式搭建方法可以让你从小到大不同规模下使用 Vue,从而进行平滑过度。

  • 只用于 form 表单 -> 整个页面用 Vue 对象管理 -> 组件 -> 模板(大规模组件复用) -> VueX 复杂状态管理 -> vue-router 控制路由 -> 与服务端通信 axios

语法易用

Vue 语法设计非常人性化,稍做适应就能很容易的实现原本大量代码才能实现的功能,适合极限编程。

社区活跃、组件丰富

由于 Vue 优秀的性能、人性化的语法、完整的文档,吸引了大量开源社区力量开发了丰富的组件。例如知名的 element(饿了么组件)提供丰付的 UI 组件、storybook 响应式 UI 开发及测试组件等。同时文档、教学视频资源丰富,很容易上手,形成了良性社区。

前置技术:

  • HTML+CSS+JavaScript,Vue 是一个 Web 前端框架,所以基础是需要的。
  • MVC(Model View Controller)设计模式(24 种常见设计模式的综合模式),Vue 是 MVVM 设计模式(MVC 设计模式的增强版),所以理解基本设计模式有助于理解 Vue。
  • Ajax(Asynchronous Javascript And XML),Vue 可以看做 Ajax 的延伸,只对局部 HTML 元素用 JavaScrip 进行操作,通过 HTTP 请求与服务端通信。

Structure(关键机制)

观察者模式

Vue 如图,A B C 是最底层被观察的对象,与 D E F G 一起形成一个观察模式网络结构。当 B 发生变化时,会通知到 D E 更新,进而通知到 F(如果 F 绑定了某个页面上的 Dom 元素,这时就会发生用户可见的变化)。

Vue 的基本设计思路就是通过观察者模式对 Vue 对象中的数据等进行监听,然后做出合理的动作,围绕变化的对象进行工作,避免轮训造成性能低下。而这种监听是自动建立的,只要我们不要违反 Vue 的设计框架,就能轻松使用。观察者与被观察者之间可以是多对多的关系,形成双向的树形结构,当被监听的对象发生变化时会通知相关对象,形成通知链。

  • 对于一个 Vue 对象,其中的数据(data、computed 字段)都是被监听的,而这种监听实际上是在上面封装一层对象实现的,所以我们在使用时注意不要对 Vue 对象随意增加属性,要么在创建时就存在、要么通过$set操作增加,否则会导致数据变化不能被监听到。
  • 对于一个 Vue 对象,我们通常通过 this 指针操作它的属性和函数,所以在函数绑定时要注意,箭头函数可能会造成 this 指针问题,除非你知道什么时候该用箭头函数。
  • 在进行监听链设计时,要单向依赖,避免形成环装依赖。

缓存机制

Vue 对象的 computed 属性可以根据 data 数据动态计算结果(比如根据各项属性值综合决定称号),多个 computed 和被使用的数据(data、computed)之间可以形成网络结构。当 computed 被读取时,会形成一系列连锁计算。 为避免每次读取都要重新计算,Vue 会对上一次的运算结果进行缓存,只有在网络末端数据发生变化后才会重新计算,在方便的同时性能没有任何多余损耗。

虚拟 Dom 机制

通畅我们会直接给 Dom 对象的属性赋值,这样来最终改变显示。Vue 给每个 Dom 对象绑定了一个虚拟 Dom 对象,当有外部消息进入后(键盘、通信)、监听的 data 发生变化,会先把数据更新到虚拟 Dom 对象。如果一次消息可能导致同一个 Dom 属性的多次变更,那这些变更都会只作用在虚拟 Dom 对象,这样避免频繁改变 Dom 属性导致性能下降。然后,等这次通信结束、所有变更完成后,Vue 通过异步机制(promise、timeout 0 等方式)统一将变更赋值于 Dom 对象。

组件(对象)间通信

父组件调用子组件(向子对象传递消息)

  • (不推荐)1.可以直接通过this.$refs.子对象.方法()调用,但是这种调用方法没有利用好 Vue 的数据驱动模式,并且耦合性高。
  • (推荐)2.父对象通过 prop 参数在子对象构造时传入并绑定子对象监听函数,当参数对象发生改变时双方都能做出响应。

子组件调用父组件函数

  • (不推荐)1.子组件通过this.$parent.方法()直接调用,同样的耦合姓高。
  • (推荐)2.子组件通过$emit向上抛出某种消息,由父组件决定捕获这种消息后怎样处理。

非子父关系的组件间通信

通过对 VueX 对象的监听、mutation 赋值、getter 查询进行间接通信。

生命周期 Hook

drawing 如图,在Vue对象生命周期的各个阶段的时机点,你都有机会执行特定代码。只要在对象构造时注册各阶段的Hook函数即可。

Key-Words(关键概念)

Directive(指令)

刚开始接触 Vue 看到这个词会一头雾水,其实就是指标签上添加的动态属性。(大概是因为 Vue 开发者是后端程序员,导致的这种命名…)

Vue 的指令名称都是以v-xxx开头的,v 表示 Vue。例如:

1
2
<!-- 当Vue对象的isShow成员变量为true时显示标签 -->
<div v-show='isShow'> 你好 </div>
  • 指令后边可以跟指令参数,如<button @click.stop="doThis">,具体每种指令可以加哪些参数要查看 API 文档。
  • Vue 为最常用的v-bindv-on分别指定了缩略形式:@。(从这点也可以看出 Vue 的人性化设计)
  • 可以添加自定义指令,这样可以把常用处理过程写成通用指令函数

Scope(作用域)

一个 Vue 对象被创建后形成一个作用域,在其中以this指针来调用 Vue 对象的变量(data)和函数(method),一个 Vue 对象通过 id 与 Dom 对象绑定。

1
2
3
4
5
6
7
8
9
10
11
12
<!-- html -->
<div id="app">
  
</div>

// javascript
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})
  • 有些情况使用箭头函数会破坏变量的作用域,所以在 Vue 开发时使用箭头函数要注意。

expression(表达式)

在后端开发中,我们经常会用到 View 模板,其本质就是把页面上一些静态内容用动态代码计算得出结果后填入。Vue 也有相同的功能,而且遍及各处。

在指令(v-bind/v-if 等)后面的字符串中使用表达式

1
<img :src="'/path/to/images/' + fileName">
  • 指令字符串中的表达式有多种用法:普通语句、三目运算符等,需查看 API 文档。

将模板内容替换为表达式

在非指令的 html 文本中通过后端模板常用的{{ }}符号使用表达式

1
2
3

<span>{{ msg }}</span>

数据成员字段:data/props/computed/filter

  • data 字段存放最基本的成员变量,可被动态监听或赋值
  • props 字段是 Vue 对象作为组件时,由调用者(父组件)传入的参数成员变量,同样可以提供动态监听或赋值
  • computed 字段定义的变量可以对内部依赖的 data 进行监控,如果发生变化则改变自己的值,同时提供缓存功能避免重复计算
  • filter 与 computed 类似,不同点是每次被读取都会重新计算一遍,不论它内部依赖的 data 对象是否发生变化。如果计算时使用的所有对象都是可监控的,一般用 computed;反之,如果有无法监控的对象,则使用 filter 来获得最新结果。

函数成员字段:methods/Lifecycle Hooks/watch

  • methods 字段可以定义属于本对象的函数,可以在本作用域的任何地方通过this.xxx()的形式调用
  • Lifecycle Hooks 前面已经说过,定义的函数可以根据生命周期的各个阶段被回调。
  • watch 与 computed 类似,可以监控某成员变量,重点在于这个变量发生变化时执行的函数动作

其它字段:component/mixin/slot

  • component(组件) 用于指定当前 Vue 对象会用到哪些组件。
  • mixin(混合) 可以实现类似面向对象的继承机制
    • 具有相同成员时,以当前组件成员为准
    • 具有相同 hook 时,都会被调用,被 mixin 的会先被调用
    • 可以在全局范围 mixin,将影响所有 Vue 对象,所以要小心
    • 可以设定全局的自定义混合策略
  • slot(插槽) 为定制 component 提供了一种机制,可以在 component 特定位置插入调用 component 时才决定的 html 结构

Convention(惯例约定)

常用语法

数组元素赋值

Vue 数组对象的元素直接通过arr[0] = {...}赋值,会由于没有调用重写的函数而没有通知监听者更新。 需要使用下面函数才能正常更新数组:

数组对象赋值'='(是整体数组,不是数组元素)
push
pop
shift
unshift
splice
sort
reverse

也可以使用 Vue 自带的赋值函数来增加一个元素,同时进行绑定:

// this 是Vue对象
this.$set(this.arr, 0, {...})

// 对于非数组元素可以用
this.$set(this.obj, "newAttribuit", 123)

watch 的两个可选参数

通常的 watch 定义是一个函数:

watch: {
    watchedValue(newValue, oldValue) {
      // xxx
    }
}

也可以增加两个可选参数,形式如下:

  obj: {
    handler(newValue, oldValue) {
      // xxx
    },
    immediate: true,  // 第一次绑定时就调用(通常刚绑定是不调用的)
    deep: true  // 子孙对象属性变化时也监听(通常只监听要监听的对象本身的属性或元素)

性能优化(包括一般 Javascript 优化)

由于 JS 运行于 UI 线程,所以出现性能问题后会直接影响用户体验。

  • 过多的 watch、computed
  • 描述 由于 watch 特别方便,所以在代码中可能会滥用,当处理数据量增长到 500 ~ 1000 以上,性能问题开始显现。
  • 方案
    • 减小 watch 的范围,能对一个元素 watch 就不要对一个数组 watch
    • 减小 watch 处理时的嵌套循环,由于 watch 触发较多,如果其中存在 O(n^2)时间复杂度以上的循环,就很可能造成性能问题
    • computed 与 watch 相比有缓存,可以好很多,但同样会产生类似问题,在性能遇到问题时也要和 watch 同等对待
  • 列表元素频繁析构、重建
  • 描述 由于列表更新机制,每次全部删除列表元素后再重新添加,在量大时,会导致性能较差
  • 方案
    • Vue 列表元素可以绑定到一个数组对象,利用数组自带的 filter、map 操作元素时,性能会较好。
    • 可以固定一些列表元素,绑定数组对象,更新时只通过修改元素属性内容变更,避免析构元素。
  • 大量对象创建、释放
  • 描述 通常在设计时,应该避免客户端承受过多数据量,因为客户端的用户难以消化所有信息,同时也会造成通信过程中有大量的对象创建和释放
  • 方案
    • 与界面元素绑定,超出界面显示数量的数据不处理,减少对象创建和释放
  • 大数据量 json 解析 parse 较慢
  • 描述 大数据量情况下做什么都慢,不论是输入操作还是 json 转对象
  • 方案 将大数据量分割成小数据量进行处理

  • 过多的消息通信
  • 描述 过多的消息通信(无论是发送还是接收,达到几百条)会影响性能
  • 方案
    • JS 无解
    • 通过于服务端协商协议,聚合消息,减少通知次数
  • 一些不必要的重排影响性能
  • 描述 有些 flex 自适应的容器控件,其中的元素少量变化时可能引起大面积的重排。现象是 UI 线程卡顿,通过 Chrome 的 Performence 查看 Rendering 耗时较多。
  • 方案
    • 这种情况往往伴随着 flex 自适应容器,需要设定overflow=hiddenmin-height=xxx等属性,将不应被重排的容器尺寸锁定在一定范围。