大白话Vue源码系列(01):万事开头难
Angular 是 Google 亲儿子,React 是 Facebook 小正太,那咱为啥偏偏选择了 Vue 下手,一句话,Vue 是咱见过的最对脾气的 MVVM 框架。之前也使用过 knockout,angular,react 这些框架,但都没有让咱产生 follow 的冲动。直到见到 Vue,简直是一见钟情啊。
Vue 的官方文档已经对 Vue 如何使用提供了最好的教程,建议 Vue 新手直接去官网学习,而不要在网上找些质量参差不齐的文档去看,以免误人子弟。中文版 和 英文版 文档写的都很地道,毕竟是国产,中文文档真心赞。不夸张地说,中文文档咱已经看了不下 5 遍了,每次都有收获,第一遍看的很慢,边看边做,之后就非常快了,主要都是扫读。英文的咱也不是看不了,只是速度问题,没必要较这个劲。值得一提的是,Vue 每次版本更新官方文档迅速都会跟进到最新,所以说 Vue 的官方文档是学习 Vue 的不二之选。
本系列的目的不是介绍如何使用 Vue,而是希望把 Vue 的源码实现思路简单清晰地描绘出来,从而摸清一个 MVVM 框架是如何工作的,并从中学习封装轮子(库或框架)的各种实用技巧。文章中的不足和欠缺之处,请大家多多指教/抱拳。
Vue 版本:2.5.9
Vue 的源码目录结构
如果直接去看 Vue 生成的代码文件 Vue.js,代码足足有上万行。这样去研究源码肯定是行不通的,也是不明智的。从 Vue 项目的目录结构入手是个不错的选择,这么大的项目,一个好的目录结构对开发和维护的重要性不言而喻。
Vue 源码目录如下:
Vue 源码目录
各目录和文件功能:
- compiler
此目录存放编译模板相关的代码,用于将 html 模板编译成 Vue 的 render 函数。 - runtime
此目录存放 Vue 生命周期内使用的相关代码,负责 Vue 实例的创建,视图渲染和处理虚拟 DOM 等等一切编译 html 模板之外的事情。 - server
这里存放 Vue 服务端渲染(SSR)需要使用的代码。 - util
这里存放一些其他模块共用的工具函数。 - entry-compiler.js
该文件是一个提供编译 html 模板相关接口的模块,通常用于为 Vue 编写的构建插件,比如vue-loader
或vueify
。 - entry-runtime.js
用于构建仅包含运行时的文件,不具备编译 html 模板功能。 - entry-runtime-with-compiler.js
用于构建同时包含编译器和运行时的全功能文件。 - entry-server-basic-renderer.js 和 entry-server-renderer.js
用于构建服务端渲染可用的文件。
可见 Vue 按照功能块对整个项目进行了目录拆分,每个目录负责一块功能,接下来就可以从这些 entry 文件入手按照 模块依赖 进行由浅及深,从整体到局部的深入剖析。
在继续之前咱们可以先看一下由上面这些源代码构建出来的可用文件是怎么样的。
Vue 构建生成的目录如下:
Vue 构建生成的目录
各文件功能:
UMD | CommonJS | ES Module | |
---|---|---|---|
Full | vue.js | vue.common.js | vue.esm.js |
Runtime-only | vue.runtime.js | vue.runtime.common.js | vue.runtime.esm.js |
表格中的术语解释:
- Full:包含编译器和运行时的全部功能。
- Runtime-only:仅包含运行时。
- UMD:可通过
<script>
标签引入直接在浏览器中使用,Vue 会暴露一个全局变量window.Vue
。同时适配require.js
这种 AMD 系统的使用。 - CommonJS:适配
const Vue = require('vue')
这种 node 式的模块系统。 - ES Module:适配
import Vue from 'vue'
这种 es6 提供的模块系统。
这些被构建出来的文件才是咱们在实际项目中可以直接使用的文件。
是用 Full 还是用 Runtime-only ?
这个需要具体情况具体分析。如果你需要使用 Vue 提供的 html 模板功能,那么就使用 Full 版本。否则,最好用 Runtime-only 版本,因为它比 Full 版本的文件体积会小上 30% 左右。值得注意的是,*.vue
单文件组件会被 vue-loader 或 vueify 直接构建成 JavaScript,并没有使用到 Vue 的编译器,因此可使用 Runtime-only 版本。
预备知识
# Flow
Vue 使用了 Facebook 出品的 flow.js 作为静态类型检查工具,所以特意跑到 flow 的官网了解了一下(不然看到一些奇怪的写法会一脸懵逼),发现其实很简单,就是在变量或函数签名的地方加上一些类型注解,而且都是可选项,加不加都行,主要是为了方便开发维护用的。
为什么选择 FLow 而不是 TypeScript ?
Vue 是想找一个静态类型检查工具以便提高项目的开发效率和可维护性。Flow 和 TypeScript 都提供了静态类型检查功能,但 TypeScript 提供了更多的有用功能,静态类型检查只是 TypeScript 提供的诸多强大功能里并不起眼的一个。而 Flow 则不一样,静态类型检查几乎是它的全部,可以说是典型小而美的实现。可能是本着杀鸡焉用牛刀,尽可能降低项目复杂度的想法,Vue 选择了 Flow 而不是 TypeScript。无独有偶,Vue 的构建工具选择了 rollup 而不是 webpack,原因应该也是如出一辙。所以说最强的不一定是最好的,最合适的才是最好的。
# ES6
Vue 的源码完全使用 ES6 编写,使得代码更清晰,更易维护。本系列的所有代码片段亦全部使用 ES6,不熟悉 ES6 的同学是时候加强学习了,这可是 JavaScript 的未来。如果想系统了解一下 ES6 的话推荐阮一峰老师的 ECMAScript 6 入门 教程。
# Rollup
Vue 使用了 Rollup 作为最后的打包工具。并且使用了 Rollup 的 rollup-plugin-alias 插件,该插件可以为目录取一个别名,使得在编写 ES6 代码 import 模块时可以使用更短的路径,而不用每次都小心翼翼地去拼相对路径,非常方便。如果不了解这一点,在看到 import config from 'core/config'
这种语句时可能会很迷惑,同级目录下并没有 core
目录啊,实际上 core
是 Vue 为其他目录配置的别名。这个映射表可以在 build/alias.js
文件中找到,映射表中有一条 core: resolve('src/core')
,resolve('src/core')
会解析出 core
目录的绝对路径,这其实就是告诉 rollup 在解析 import config from 'core/config'
时从这个绝对目录中去加载 config.js
。
先捡软的捏
从源码的目录结构可以看出,Vue 的 runtime 模块负责的事情很多,代码量必然也很大,应该是块难啃的骨头。而 compiler 则只负责将 html 模板转换为 Vue 的 render 函数,这一块应该是水很浅的,因此从这块入手先吃掉它一部分。
本系列将以每周一篇的速度定时更新,喜欢的小伙伴可以点推荐哦。
大白话Vue源码系列(01):万事开头难的更多相关文章
- 大白话Vue源码系列(03):生成AST
阅读目录 AST 节点定义 标签的正则匹配 解析用到的工具方法 解析开始标签 解析结束标签 解析文本 解析整块 HTML 模板 未提及的细节 本篇探讨 Vue 根据 html 模板片段构建出 AST ...
- 大白话Vue源码系列(02):编译器初探
阅读目录 编译器代码藏在哪 Vue.prototype.$mount 构建 AST 的一般过程 Vue 构建的 AST 题接上文,上回书说到,Vue 的编译器模块相对独立且简单,那咱们就从这块入手,先 ...
- 大白话Vue源码系列(03):生成render函数
阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...
- 大白话Vue源码系列(04):生成render函数
阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...
- 大白话Vue源码系列(05):运行时鸟瞰图
阅读目录 Vue 实例的生命周期 实例创建 响应的数据绑定 挂载到 DOM 节点 结论 研究 runtime 一边 Vue 一边源码 初看 Vue 是 Vue 源码是源码 再看 Vue 不是 Vue ...
- 大白话Vue源码系列目录
.first-level{ font-size: 1.2rem; cursor: default; color: #666; } .second-level{ font-size: 1.1rem; p ...
- 手牵手,从零学习Vue源码 系列一(前言-目录篇)
系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 手牵手,从零学习Vue源码 系列三(虚拟DOM篇) 陆续更新中... 预计八月中旬更新 ...
- 手牵手,从零学习Vue源码 系列二(变化侦测篇)
系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 陆续更新中... 预计八月中旬更新完毕. 1 概述 Vue最大的特点之一就是数据驱动视 ...
- VUE 源码学习01 源码入口
VUE[version:2.4.1] Vue项目做了不少,最近在学习设计模式与Vue源码,记录一下自己的脚印!共勉!注:此处源码学习方式为先了解其大模块,从宏观再去到微观学习,以免一开始就研究细节然后 ...
随机推荐
- Python 抽象篇:面向对象之类的方法与属性
概览:类成员之字段:-普通字段,保存在对象中,执行职能通过对象访问-静态字段,保存在类中,执行可以通过对象访问,也可以通过类访问类成员之方法:-普通方法,保存在类中,由对象来调用,self->对 ...
- Xilinx ISE 14.1生成Rom内核并读取Rom中的数据
<一>建立一个项目readDataFromRom 详细过程参照另一篇文章 http://www.cnblogs.com/LCCRNblog/p/3397666.html <二> ...
- A - 棋盘问题 POJ - 1321
在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C. ...
- Holding Bin-Laden Captive!(1.多重背包 2.母函数)
Holding Bin-Laden Captive! Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/ ...
- Even Tree 小议
原题链接:https://www.hackerrank.com/challenges/even-tree/problem 思路:把树还原出来,对每个结点,计算以该结点为根的子树结点数.子树结点数为偶数 ...
- 原生promise
你应该会用事件加回调的办法来处理这类情况: var img1 = document.querySelector('.img-1'); img1.addEventListener('load', f ...
- java设计师初入职场,如何站稳脚跟
本文内容一共由3部分展开 a:新人如何快速融入团队 b:如何在职场中提升自己影响力 c:如何规进行职业规划 a:如何快速融入团队 能在层层选拔下进入公司,说明你工作的能力还是得到公司的认可,不过这 ...
- 将非常规Json字符串转换为常用的json对象
如下所示,这是一个已经转换为Json对象的非常规Json字符串,原来是一个Json类型的字符串,在转换为Json对象时,查询资料发现有两种转换法,.parse()和.eval()方法,但是前辈们都极其 ...
- 【Tesseract】Tesseract API在VS 2013中的配置以及调用
想要在VS中使用Tesseract库,必须使用经过相对应的VS版本编译过的dll以及lib.比如在VS 2013中,就必须使用在VS 2013中编译过的Tesseract库. 这里我给出经过VS 20 ...
- js滚动加载小插件
本文实例讲述了jquery滚动加载数据的方法.分享给大家供大家参考.具体分析如下: 少废话直接上代码!!!粗暴,直接,干脆 0//lk-2017-05-04 1(function($, win) { ...