按 Vue 作者的说法,Vue(及其生态)是一个渐进式 MVVM 框架,可以按照实际需要逐步进阶使用更多特性。
vue - 图1
Vue 渐进式示意图

声明式渲染

简单来说,声明式渲染即希望开发者更多地表达 “想要什么”,而不用太关心想要的效果的实现细节。例如,我们希望页面上有一个 2x2 的表格,而命令式的实现方式是使用 Canvas 自己画:

  1. // 获取画面和context
  2. const canvas = document.createElement('canvas');
  3. const ctx = canvas.getContext('2d');
  4. // 定义宽高和行列数
  5. const width = 200, height = 100;
  6. const colCount = 2, rowCount = 2;
  7. // todo:定义画笔颜色
  8. // 画横线
  9. for(let i = 0; i <= rowCount; i++){
  10. ctx.moveTo(0, height / rowCount * i);
  11. ctx.lineTo(width, height / rowCount * i);
  12. }
  13. // 画竖线
  14. for(let i = 0; i <= colCount; i++){
  15. ctx.moveTo(width / colCount * i);
  16. ctx.lineTo(width / colCount * i, height);
  17. }
  18. document.body.appendChild(canvas);

我们需要自己关心实现细节,例如每一条线的坐标、每一条表格线的绘制等等。所幸 HTML 为我们提供了 <table> 元素,可以让我们更简单地实现表格:

  1. <table>
  2. <tr>
  3. <td></td>
  4. <td></td>
  5. </tr>
  6. <tr>
  7. <td></td>
  8. <td></td>
  9. </tr>
  10. </table>

相比 Canvas 的表格,我们只需要说明需要多少行,每一行有多少个单元格即可,不用自己关注绘制细节。这个例子可以让我们对 “声明式渲染” 有一个更直观的认知。
而在 Vue 中,开发者可以直接通过模板指令来表达 “想要什么”,并不用关心它的底层实现。常见的指令如 v-ifv-forv-show 等都是同样的作用。此外 Vue 还可以声明式地表达对数据的渲染逻辑,例如用 {{message}} 来表示 “在此处显示变量 message 的值”,而不用关心 message 的变量是如何被填入 DOM 对象中。

组件系统

每一个程序员都知道 “高内聚 低耦合” 的编程原则,也都知道代码复用的重要性。但在前端代码中如何实现 “高内聚 低耦合”,以及前端代码复用,并不是一件容易的事情。对于 JavaScript,尚可以使用模块化来解决内聚和复用的问题,但一旦涉及到结构(HTML)和样式,事情就不容易了。
Vue 为开发者提供了 “组件” 的概念,一个组件即一组关联的结构和逻辑。组件内部可以方便地使用声明式渲染将逻辑和结构关联起来,实现组件的高内聚。例如 Vue 官方文档的一个组件例子:

  1. Vue.component('button-counter', {
  2. data: function () {
  3. return {
  4. count: 0
  5. }
  6. },
  7. template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
  8. })

这个例子中,template 中定义了组件的结构,并且声明式地将 count 变量显示到 <button> 的内容中。在 <button> 被点击时改变 count 的值。可以看到,组件内部的结构和逻辑都是非常容易互相访问和操作的。而在组件外部,不管是 count 变量还是 <button> 结构,都无法直接进行访问和操作。如果需要外部进行访问和操作,则需要定义相应的接口或者事件。这非常符合 “高内聚 低耦合” 的思想。
组件一旦定义好之后即可被重复使用:

  1. <div id="components-demo">
  2. <button-counter></button-counter>
  3. <button-counter></button-counter>
  4. <button-counter></button-counter>
  5. </div>

从而很好地解决前端代码复用的问题。

客户端路由和状态管理

针对单页面应用,客户端路由的支持是必不可少的一个功能。通过客户端路由,开发者可以根据不同的 URL 加载不同的前端组件(有时候也称为 “页面”),也可以允许用户通过跳转的形式在不同的前端组件之间跳转。
要支持客户端路由,首先需要对当前 URL 进行解析,获取当前访问的页面地址以及参数等信息,然后根据开发者定义的路由与组件的对应关系(路由表)找到页面地址对应的组件,并负责加载和运行组件。
Vue 官方的客户端路由被放到一个独立的库 vue-router 中。开发者也可以根据需要选用。
随着应用复杂度的上升,页面的数据会越来越多,且同一个数据关联多个组件的情况会越来越多。此时数据如何组织、如何定义与组件的关联关系、如何确保变更会同步到每一个关联的组件中就变得非常重要。此时可能就需要使用 “状态管理” 的库来辅助开发者进行页面状态的管理。
状态管理库会提供全局的状态(state)对象,并定义一系列读写状态的方法。组件中只要使用状态管理提供的读写方法读写状态即可。
Vue 官方也提供了状态管理库 vuex,可供复杂应用使用。

编译 / 构建工具

借助构建工具的力量,Vue 还可以提供更好的开发体验。例如基于 webpack 强大的 loader 机制,style-loadercss-loader 可以允许开发者在 JS 文件中直接引入 CSS 文件。
借鉴这一思路,Vue 推出了单文件组件(Single File Component,简称 SFC)格式.vue,即允许开发者将同一组件的结构、样式、逻辑全部写在同一个文件中,然后由 vue-loader 在编译阶段将结构和逻辑都编译成 JS 文件,将 CSS 部分借用上述 style-loadercss-loader 同样的机制挂载到页面中。
单文件组件的推出使得 Vue 的文件组织又上一个台阶,我们可以更明确地在文件层面就定义好组件,同时由于 vue-loader 的支持,组件也可以通过各种机制(例如 npm)在不同项目间得以复用。
除了单文件组件之外,Vue 也推出了 CLI 工具 vue-cli,方便开发者更好地初始化项目、搭建项目开发环境并完成构建工作。