Vue源码模块分析
2022-04-06 16:44:36 1 举报
AI智能生成
登录查看完整内容
vue源码分析
作者其他创作
大纲/内容
webpack1、browserfiry等老版本打包器(vue.common.xx.js)
cjs
webpack2等新版本打包器(vue.esm.xx.js)
esm
兼容cjs和amd(vue.js、vue.min.js)
umd
仅包含运行时,没有编译器(预编译)
runtime
dist:所有输出的版本
examples:测试代码
flow:类型声明
packages:独立模块
scripts:打包脚本
codegen:代码生成
directives:指令v-bind v-model v-on
parser:ast语法树生成部分
compiler:编译器,包括把模板解析成AST语法树,AST语法树优化,代码生成等功能
components:内置组件 KeepAlive
font color=\"#f68b1f\
instance:render相关 以及vue构造函数定义 生命周期钩子挂载 原型链上实例方法的挂载
observer:响应式的实现
util:工具包 主要是 debug,lang,next-tick,options(合并策略),props(props处理),env运行环境嗅探
vdom:虚拟dom实现
core:核心代码。包括内置组件、全局API封装、Vue实例化、观察者、虚拟DOM、工具函数等
platforms:平台特殊代码
server:服务端渲染的逻辑,这部分代码是跑在服务器的node.js
sfc:单文件解析器,把 .vue 文件内容解析成一个 javascript 对象
shared:公共帮助代码,一些定义的工具方法,这些方法会被浏览器端的Vue.js 和服务端的Vue.js 共同使用
src:核心代码
types:源码使用了Flow做静态类型检查,需要给typescript专门写一套
文件结构
package.json
通过dev脚本,提取出关键信息:scripts/config.js、web-full-dev
scripts/config.js
在 scripts/config.js 中寻找web-full-dev,拿到entry
寻找entry中的resolve函数定义
scripts/alias.js
寻找resolve函数中调用的aliases,然后向上合并,得到entry完整地址
src/platforms/web/entry-runtime-with-compiler.js
成功得到入口文件,代码从这里开始执行
寻找入口文件
import Vue from './runtime/index'
从入口文件开始寻找
import Vue from 'core/index'
src/platforms/web/runtime/index.js
寻找入口文件中Vue的来源
import Vue from './instance/index'
src/core/index.js
寻找第2步中Vue来源
实现了Vue.prototype._init(),初始化的入口,各种初始化工作,包括下方4个Mixin()涉及的数据初始化
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
src/core/instance/index.js
寻找第3步中Vue来源,终于找到Vue构造函数
寻找Vue构造函数
文件入口
初始化$parent、$root、$children、$refs
initLifecycle(vm)
处理父组件传递的监听器
initEvents(vm)
initRender(vm)
显然beforeCreate的钩子函数中无法获取到props、data中定义的值,也不能调用methods中定义的函数
beforeCreate钩子函数
获取注入的数据,resolve injections before data/props
initInjections(vm)
初始化组件中的props,methods,data,computed,watch...
initState(vm)
提供数据,resolve provide after data/props
initProvide(vm)
created钩子函数
挂载组件。选项里有el时,自动执行$mount(所以new Vue({})选项里不需要单独设置$mount)
vm.$mount(vm.$options.el)
src/core/instance/init.js
Vue构造函数内部调用了_init()完成了初始化
Vue 不能挂载在 body、html 这样的根节点上
把 el 或者 template 字符串转换成 render 方法(在 Vue 2.0 版本中,所有 Vue 的组件的渲染最终都需要 render 方法)
若有template,判断template类型(#id、模板字符串、dom元素),转化成html字符串
若没有template,则判断el,用 getOuterHTML(el) 提取挂载DOM元素的HTML用作template
调用编译器,将template字符串转换为render函数(关于编译器,下一分支会详细介绍)
如果render不存在
调用原型上的$mount挂载
在原型的基础上扩展的$mount()
对el做处理,转变成对象
调用mountComponent()
原型上的$mount()
执行beforeMount钩子函数之前检查选项里有无render函数,没有的话建立一个空的vnode
定义函数 updateComponent(),内部会执行 vm._render()和vm._update(),生成虚拟dom并更新dom
实例化一个渲染 watcher 进行数据绑定,在watcher内部把 updateComponent() 赋值给getter
挂载实例,调用mounted钩子(vm._isMounted设置为true表示这个实例已经挂载)
src/core/instance/lifecycle.js
mountComponent()具体实现
src/core/instance/render.js
寻找vm.$createElement的定义,发现内部调用了createElement()
src/core/vdom/create-element.js
寻找createElement(),内部调用_createElement()生成虚拟DOM
目的:把children 变成一个类型为 VNode 的 Array
src/core/vdom/helpers/normalzie-children.js
normalizeChildren()
simpleNormalizeChildren()
children 的规范化
如果是内置节点,则直接创建一个普通 VNode
如果是为已注册的组件名,则通过createComponent创建一个组件类型的VNode
否则创建一个未知的标签的 VNode
如果tag为string 类型
调用 createComponent 创建一个组件类型的 VNode 节点
如果tag为Component 类型
创建VNode
寻找 _createElement()
vm._render()生成虚拟DOM
核心:调用 vm.__patch__ 方法
在服务端渲染中没有真实的浏览器DOM环境,所以不需要把VNode最终转换成DOM,因此是一个空函数noop,而在浏览器端渲染中,它指向了 patch 方法
寻找vm.__patch__方法
nodeOps:封装了一系列DOM操作的方法,更新dom时使用;
modules:定义了一些模块的钩子函数的实现
src/platforms/web/runtime/patch.js
寻找patch()方法
内部定义了一系列的辅助方法,最终返回了一个patch方法,这个方法赋值给了vm._update函数里调用的vm.__patch__
作用:通过虚拟节点创建真实的 DOM 并插入到它的父节点中
尝试创建子组件
如果vnode包含tag,则对tag的合法性在非生产环境下做校验,看是否是一个合法标签
调用平台 DOM 的操作去创建一个占位符元素
src/core/vdom/patch.js
原理:遍历子虚拟节点,深度优先递归调用 createElm
调用 createChildren 方法创建子元素
执行所有的 create 的钩子并把 vnode push 到 insertedVnodeQueue 中
最后调用 insert 方法把 DOM 以先子后父的顺序插入到父节点中
实现
核心:createElm()
整个过程就是递归创建一个完整的 DOM 树并插入到 Body 上
寻找createPatchFunction()方法
vm._update()更新DOM
数据渲染
构造子类构造函数
安装组件钩子函数
实例化 VNode
src/core/vdom/create-component.js
createComponent()
组件化
在使用 vue-cli 脚手架构建项目时,会遇到一个构建选项 Vue build,有两个选项,Runtime + Compiler和Runtime-only,接下来我们分析一下这两者的区别
内部包含编译代码,可以把编译过程放在运行时做
运行时+编译器
Runtime + Compiler
运行的时候是不带编译器的,编译是在离线的时候做的
在构建时通过 webpack 的 vue-loader 工具将 .vue模板预编译成 Javascript,进行了预编译,在这个过程中会将组件中的template模板编译为render函数
运行时版本
Runtime-only
定义
生成的脚手架
runtime-only 比 runtime-compiler 轻 6kb,只包含运行时的Vue.js代码,因此代码体积也会更轻量,runtime-only 运行更快
文件大小
template -> ast -> render -> virtual dom -> 真实dom
render -> virtual dom -> 真实dom
前一种比后一种多了将template转为render的过程,这部分就是靠编译器实现的
运行过程
在原型的基础上扩展的$mount(),可以解析template,编译成render
原型上的$mount(),直接解析render
src/platforms/web/entry-runtime.js
入口文件
区别
前置知识
在《数据渲染》模块的第二步已经分析过在扩展$mount()中是如何获取到template的,这里不做重复分析,直接看template是如何转化成render函数的
调用编译器compileToFunctions()生成动态节点渲染方法render和静态节点渲染方法集合staticRenderFns
compile
compileToFunctions
createCompiler()的返回值赋值给变量
src/platforms/web/compiler/index.js
寻找compileToFunctions(),内部调用createCompiler()
font color=\"#c41230\
传参是个函数,真正的编译过程都在这个 baseCompile 函数里执行
src/compiler/index.js
寻找createCompiler(),内部调用createCompilerCreator()
compile 方法属性,createCompiler()是内部定义的一个函数
compileToFunctions 属性,这个 compileToFunctions 对应的就是 $mount 函数调用的 compileToFunctions 方法,它是调用 createCompileToFunctionFn 方法的返回值
createCompiler()返回一个对象
src/compiler/create-compiler.js
寻找createCompilerCreator(),return一个createCompiler函数
compile 函数在执行 createCompileToFunctionFn 的时候作为参数传入,它是 createCompiler 函数中定义的 compile 函数
核心:const compiled = font color=\"#c41230\
src/compiler/to-function/js
寻找createCompileToFunctionFn(),return一个compileToFunctions函数
寻找compile()
编译入口
循环解析 template ,用正则做各种匹配,对于不同情况分别进行不同的处理,直到整个 template 被解析完毕。
在匹配的过程中会利用 advance 函数不断前进整个模板字符串,直到字符串末尾。
src/compiler/parser/html-parser
内部调用了parseHTML
src/compiler/parse/index.js
parse:生成AST树
深度遍历AST树,先标记静态节点,再标记静态根
过程
目标:通过标记静态节点的方式,优化更新时对静态节点的处理逻辑
目标
src/compiler/optimizer.js
optimize:优化AST树
generate:AST转化成可执行代码
编译过程
编译
编译器
git地址
Vue源码
0 条评论
回复 删除
下一页