一、Vue 源码解析 - 响应式原理
1.1 准备工作
- 源码目录结构
src
├─compiler 编译相关
├─core Vue 核心库
├─platforms 平台相关代码
├─server SSR,服务端渲染
├─sfc .vue 文件编译为 js 对象
└─shared 公共的代码
-
vue采用flow静态类型检查器
-
调试, vue 采用rollup打包工具,设置 sourcemap,package.json 文件中的 dev 脚本中添加参数 --sourcemap
"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web-
full-dev"
1.2 Vue的不同构建版本
- npm run build 重新打包所有文件
UMD | CommonJS | ES Module | |
---|---|---|---|
Full | vue.js | vue.common.js | vue.esm.js |
Runtim-only | vue.runtime.js | vue.runtime.common.js | vue.runtime.esm.js |
Full(production) | vue.min.js | - | |
Runtime-only(production) | vue.runtime.min.js | - |
术语
-
完整版:同时包含编译器和运行时的版本。
-
编译器:用来将模板字符串编译成为 JavaScript 渲染函数的代码。
-
运行时:用来创建 Vue 实例、渲染并处理虚拟 DOM 等的代码。基本上就是除去编译器的其它一切。
-
UMD:UMD 版本可以通过
<script>
标签直接用在浏览器中。jsDelivr CDN 的 https://cdn.jsdelivr.net/npm/vue 默认文件就是运行时 + 编译器的 UMD 版本 (vue.js
)。 -
CommonJS:CommonJS 版本用来配合老的打包工具比如 Browserify 或 webpack 1。这些打包工具的默认文件 (
pkg.main
) 是只包含运行时的 CommonJS 版本 (vue.runtime.common.js
)。 -
ES Module:从 2.6 开始 Vue 会提供两个 ES Modules (ESM) 构建文件:
寻找入口文件
-
查看 dist/vue.js 的构建过程
-
其实可以执行yarn dev文件 查看打包结果也可以直接查看入口文件
执行构建
npm run dev
# "dev": "rollup -w -c scripts/config.js --sourcemap --environment
TARGET:web-full-dev"
# --environment TARGET:web-full-dev 设置环境变量 TARGET
-
script/config.js 的执行过程
-
作用:生成 rollup 构建的配置文件
-
使用环境变量 TARGET = web-full-dev
-
// 判断环境变量是否有 TARGET
// 如果有的话 使用 genConfig() 生成 rollup 配置文件
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET)
} else {
// 否则获取全部配置
exports.getBuild = genConfig
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}
-
genConfig(name)
-
根据环境变量 TARGET 获取配置信息
-
builds[name] 获取生成配置的信息
-
// Runtime+compiler development build (Browser)
'web-full-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.js'),
format: 'umd',
env: 'development',
alias: { he: './entity-decoder' },
banner
},
-
resolve()
- 获取入口和出口文件的绝对路径
const aliases = require('./alias')
const resolve = p => {
// 根据路径中的前半部分去alias中找别名
const base = p.split('/')[0]
if (aliases[base]) {
return path.resolve(aliases[base], p.slice(base.length + 1))
} else {
return path.resolve(__dirname, '../', p)
}
}
通过调用栈的方式查看$mount在哪里调用
问题:以下代码页面渲染的结果是
new Vue({
el: '#app',
template: '<h1>我是template</h1>',
render (h) {
return h('h1', '我是reder渲染的')
},
})
答案:
分析:可以通过源码中的 if(!options.render)来判断,这句话的作用是如果render不存在,就会将template转换成render函数
Vue阅读源码需要注意的事项
vscode会出现红色波浪线报错 和 源码没有高调的情况
需要做的就是在vue项目的根目录建立.vscode/settings.json
{
// 设置不检查js的语法问题,防止flow报错
"javascript.validate.enable": false
}
同时为vscode安装 Babel javascript 插件可以解决高亮的问题, 有一个缺陷就是不能跳转到源码的位置
1.2 三种类型的Watcher对象
Watcher主要分为三种:computed watcher、用户自定义watcher(侦听器)、渲染watcher
- 创建顺序是 computed watcher -> 用户自定义watcher -> 渲染watcher
1.3 vue中采用key的好处(diff算法)
1.4 Vue模板编译过程
把html模板转换成render函数工具
_c 函数定义路径:src\core\instance\render.js
_m, _v, _s函数的定义路径:src\core\instance\render-helpers\index.js
AST抽象语法树
-
抽象语法树简称AST
-
使用对象的形式描述树形的代码结构
-
此处的抽象语法树是用来描述树形结构的HTML字符串
Vue.js 源码剖析-响应式原理、虚拟 DOM、模板编译和组件化
一、简答题
1、请简述 Vue 首次渲染的过程。
2、请简述 Vue 响应式原理。
3、请简述虚拟 DOM 中 Key 的作用和好处。
首先需要说明diff算法的原理
-
老开始节点和新开始节点对比
-
老结束节点和新结束节点对比
-
老开始节点和新结束节点对比
-
老结束节点和新开始节点对比
-
上述情况都不满足才会老节点找新节点存在的索引
不设置或者设置key为index索引值,当往数组中splice插入一个元素时,会在updateChildren方法中,里面的sameVnode方法会判断为同一个vnode,不断比较老开始节点和新开始节点,索引向后移动,子节点会多次操作dom消耗性能。
function sameVnode (a, b) {
return (
a.key === b.key && (
(
a.tag === b.tag &&
a.isComment === b.isComment &&
isDef(a.data) === isDef(b.data) &&
sameInputType(a, b)
) || (
isTrue(a.isAsyncPlaceholder) &&
a.asyncFactory === b.asyncFactory &&
isUndef(b.asyncFactory.error)
)
)
)
}