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