一、简介
vue几个核心思想:
- 数据驱动
- 组件化
- 虚拟dom、diff局部最优更新
源码目录介绍
Vue.js 的源码在 src 目录下,其目录结构如下。
src
├── compiler # 编译相关
├── core # 核心代码
├── platforms # 不同平台的支持
├── server # 服务端渲染
├── sfc # .vue 文件解析
├── shared # 共享代码
- compiler:编译相关的代码。它包括把模板解析成 ast 语法树,ast 语法树优化,代码生成等功能。
- core:核心代码,包括内置组件、全局 API 封装,Vue 实例化、观察者、虚拟 DOM、工具函数等等。
- platform:不同平台的支持,是 Vue.js 的入口,2 个目录代表 2 个主要入口,分别打包成运行在 web 上和 weex 上的 Vue.js。
- server:服务端渲染,把组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序。
- sfc: .vue 文件内容解析成一个 JavaScript 的对象。
- shared:浏览器端和服务端所共享的工具方法。
源码构建
基于 Rollup 构建,相关配置在 scripts 目录下。
构建时通过不同的命令执行不同的脚本,去读取不同用处的配置,然后生成适合各种场景的Vue源码。
vue2.0有以下几种场景:
- 浏览器端
- 服务端渲染
- 配合weex平台在客户端使用
类型检查
在vue2.x版本中使用 Flow 作为js静态类型检查工具,3.x版本使用typescript实现,自带类型检查。
二、数据驱动
vue核心思想之一就是数据驱动
,指数据
驱动生成视图
,通过修改数据自动实现对视图的修改。这里主要分析模板和数据是如何渲染成最终的DOM的。
1. new Vue的过程
Vue 初始化主要就干了几件事情,
- 合并配置
- 初始化生命周期
- 初始化事件中心
- 初始化渲染
- 初始化 data、props、computed、watcher 等等。
2. 实例挂载
$mount方法
- Vue 不能挂载在 body、html 这样的根节点上;
- 如果没有定义 render 方法,则会把 el 或者 template 字符串转换成 render 方法
- 在 Vue 2.0 版本中所有 Vue 的组件的渲染最终都需要 render 方法,是一个“在线编译”的过程;
挂载组件:
mountComponent
核心就是先实例化一个渲染Watcher
,在它的回调函数中会调用 updateComponent
方法,在此方法中调用 vm._render
方法先生成虚拟 Node
,最终调用 vm._update
更新 DOM。
Watcher在这里起到两个作用:
- 初始化的时候会执行回调函数;
- 当 vm 实例中的监测的数据发生变化的时候执行回调函数
3. render渲染
- 把 template 编译成 render 方法【编译过程后面专门介绍】
- createElement创建dom节点
4. 虚拟dom
- 由于dom操作耗时十分长,且dom对象的体积很大,单个div的dom属性就有
294
个之多; - Virtual DOM 就是用一个原生的 JS 对象去描述一个 DOM 节点,所以它比创建一个 DOM 的代价要小很多。
- VNode 是对真实 DOM 的一种抽象描述,它的核心定义无非就几个关键属性,标签名、数据、子节点、键值等,其它属性都是用来扩展 VNode 的灵活性以及实现一些特殊 feature 的。由于 VNode 只是用来映射到真实 DOM 的渲染,不需要包含操作 DOM 的方法,因此它是非常轻量和简单的。
- Virtual DOM到真实的dom需要经过以下过程:VNode 的 create、diff、patch
5. createElement
判断第一个参数tag的类型,分为普通html标签、组件和其他类型,将子节点规范成 VNode 类型,递归整个树完成虚拟dom树的构建。
此方法是render函数的参数。
const app = new Vue({
el: '#app',
render: createElement => createElement(App)
})
6. update
调用的时机:一个是首次渲染,一个是数据更新的时候;
首次渲染会将虚拟dom树整个渲染为dom节点,数据更新的时候会经过diff
过程,只选取修改的虚拟dom节点进行局部更新。
update 的核心就是调用 vm.__patch__
方法,不同的平台实现不一样,web平台生成dom节点,ssr服务端渲染生成html字符串。
dom树节点的插入
顺序是先子后父
,
- vue初始渲染的工作流程:
new Vue
➜init
➜$mount
➜compile
➜render
➜vnode
➜patch
➜dom
三、组件化
- 组件化,就是把页面拆分成多个组件 (component),每个组件依赖的 CSS、JavaScript、模板、图片等资源放在一起开发和维护。
- 组件是资源独立的,组件在系统内部可复用,组件和组件之间可以嵌套。
1. createComponent
在createElement里面调用,判断tag类型为组件时调用,用来将组件转换成虚拟dom。
核心步骤:
- 构造子类构造函数
- 安装组件钩子函数
- 实例化 vnode
Vue.extend
- 作用就是构造一个 Vue 的子类,这个子类就是组件本身,使用原型继承把纯对象转换一个继承于 Vue 的构造器 Sub 并返回
- Sub 扩展了属性,如扩展 options、添加全局 API 等;并且对配置中的 props 和 computed 做了初始化工作;
- 最后对于这个 Sub 构造函数做了缓存,避免多次执行 Vue.extend 的时候对同一个子组件重复构造。参考 前端vue面试题详细解答
2. patch
patch主要完成组件的渲染
工作。
createComponent过程把组件转换成了VNode,patch过程会调用createElm把 VNode 转换成真正的 DOM 节点。
- createComponent:
递归实现深度遍历
整个VNode树,用先子后父
的方式插入dom树 - 最终根节点VNode转化为dom后挂载到
#app
的节点上,且挂载元素不能是html
或body
- patch整体流程:createComponent ➜ 自组件初始化 ➜ 子组件render ➜ 自组件patch
3. 合并配置
vue自身定义了一些默认配置,同时又可以在初始化阶段传入一些定义配置,然后去 merge 默认配置,来达到定制化不同需求的目的。
vue组件其实是一个js对象,我们写组件其实就是在写各种配置,这个配置在构建组件的时候会调用Vue.extent
方法构建成一个组件类(因此我们组件内部访问到的this
才是Vue的实例),那么在组件类实例化 new Vue()
的过程中,就会做合并配置
这件事。
合并配置分为两种方式:
- 外部初始化调用
new Vue
(例如挂载#app的时候) - 组件场景
主要合并以下几方面的配置:
- mixin的配置
- extends继承的配置
- 编写的对象组件配置
4. 生命周期
生命周期
是vue在运行期间的各个关键节点运行的钩子函数
,以便可以在特定场景做特定的事。
-
生命周期依次有:
beforeCr