上一篇是mount之前的添加一些方法,包括全局方法gloal-api,XXXMixin,initXXX,然后一切准备就绪,来到了mount阶段,这个阶段主要是

  • 解析template
  • 创建watcher并存入Dep
  • 更新数据时更新视图

Vue源码里有两个mount

  • 第一个
  1. // src/platform/web/runtime/index.js
  2. Vue.prototype.$mount = function (
  3. el?: string | Element,
  4. hydrating?: boolean
  5. ): Component {
  6. el = el && inBrowser ? query(el) : undefined
  7. // 核心方法,往下看
  8. return mountComponent(this, el, hydrating)
  9. }
  • 第二个
  1. // src/platforms/web/entry-runtime-with-compiler.js
  2. // 把上面的第一个取出来,在最后一行执行
  3. const mount = Vue.prototype.$mount
  4. // 把原本的替换掉,这就是第二个mount
  5. Vue.prototype.$mount = function (
  6. el?: string | Element,
  7. hydrating?: boolean
  8. ): Component {
  9. el = el && query(el)
  10. const options = this.$options
  11. if (!options.render) {
  12. let template = getOuterHTML(el)
  13. if (template) {
  14. // compileToFunctions,来自 compiler/index.js
  15. const { render, staticRenderFns } = compileToFunctions(template, {
  16. shouldDecodeNewlines,
  17. shouldDecodeNewlinesForHref,
  18. delimiters: options.delimiters,
  19. comments: options.comments
  20. }, this)
  21. options.render = render
  22. options.staticRenderFns = staticRenderFns
  23. }
  24. // 把第一个mount执行
  25. return mount.call(this, el, hydrating)
  26. }
  1. // compiler/index.js
  2. function compileToFunctions (
  3. template: string,
  4. options?: CompilerOptions,
  5. vm?: Component
  6. ): CompiledFunctionResult {
  7. options = options || {}
  8. // 编译,核心内容
  9. const compiled = compile(template, options)
  10. }

第二个mount才是在init里真正被执行的,也就是在第一个mount之前先被执行,叫做compiler阶段

compiler阶段,整个阶段全程只执行一次,目的就是生成render表达式

  • parse,将templat转成AST模型树
  • optimize,标注静态节点,就是选出没有参数的固定内容的html标签
  • generate,生成render表达式
  1. <div id="el">Hello {{name}}</div>
  1. // compile方法就是把 html 转为 AST,效果如下
  2. {
  3. type: 1,
  4. div: 'div',
  5. attrsList: [{
  6. name: 'id',
  7. value: ''el
  8. }],
  9. attrs: [{
  10. name: 'id',
  11. value: ''el
  12. }],
  13. attrsMap: {
  14. id: 'el'
  15. },
  16. plain: false,
  17. static: false,
  18. staticRoot: false,
  19. children: [
  20. type: 2,
  21. expression: '"hello "+ _s(name)',
  22. text: 'Hello {{name}}',
  23. static: false
  24. ]
  25. }
  1. // 由上面的AST生成 render表达式,
  2. with (this) {
  3. return _c(
  4. "div",
  5. {
  6. attrs: {id: 'el'}
  7. },
  8. [
  9. _v("Hello "+_s(name))
  10. ]
  11. )
  12. }
  1. // render-helpers 下 index.js
  2. export function installRenderHelpers (target) {
  3. target._o = markOnce
  4. target._n = toNumber
  5. target._s = toString
  6. target._l = renderList
  7. target._t = renderSlot
  8. target._q = looseEqual
  9. target._i = looseIndexOf
  10. target._m = renderStatic
  11. target._f = resolveFilter
  12. target._k = checkKeyCodes
  13. target._b = bindObjectProps
  14. target._v = createTextVNode
  15. target._e = createEmptyVNode
  16. target._u = resolveScopedSlots
  17. target._g = bindObjectListeners
  18. }
  19. // 怎么没有_c,_c在上篇笔记的initRender方法里
  20. // _c对应元素节点、_v对应文本节点、_s对应动态文本

到这里执行第一个mount,也就是mountComponent方法

  1. // instance/lifecycle.js
  2. export function mountComponent (
  3. vm: Component,
  4. el: ?Element,
  5. hydrating?: boolean
  6. ): Component {
  7. vm.$el = el
  8. // 挂载前,执行beforMount
  9. callHook(vm, 'beforeMount')
  10. let updateComponent
  11. // 定义updateComponent,vm._render将render表达式转化为vnode,vm._update将vnode渲染成实际的dom节点
  12. updateComponent = () => {
  13. // 核心内容,理解为
  14. // var A = vm._render(),生成vDom
  15. // vm._update(A, hydrating),生成真实dom
  16. vm._update(vm._render(), hydrating)
  17. }
  18. // 首次渲染,并监听数据变化,并实现dom的更新
  19. new Watcher(vm, updateComponent, noop, {
  20. before () {
  21. if (vm._isMounted) {
  22. callHook(vm, 'beforeUpdate')
  23. }
  24. }
  25. }, true /* isRenderWatcher */)
  26. hydrating = false
  27. // 挂载完成,回调mount函数
  28. if (vm.$vnode == null) {
  29. vm._isMounted = true
  30. callHook(vm, 'mounted')
  31. }
  32. return vm
  33. }

看看watch构造函数

  1. export default class Watcher {
  2. constructor (
  3. vm: Component,
  4. expOrFn: string | Function,
  5. cb: Function,
  6. options?: ?Object,
  7. isRenderWatcher?: boolean
  8. ) {
  9. this.vm = vm
  10. // 上面的函数传了true
  11. if (isRenderWatcher) {
  12. vm._watcher = this
  13. }
  14. // 当数据发生改变,_watchers会被循环更新,也就是视图更新
  15. vm._watchers.push(this)
  16. if (typeof expOrFn === 'function') {
  17. this.getter = expOrFn
  18. }
  19. if (this.computed) {
  20. this.value = undefined
  21. this.dep = new Dep()
  22. } else {
  23. // 把expOrFn执行了,启动了初次渲染
  24. this.value = this.get()
  25. }
  26. }
  27. get () {
  28. return this.getter.call(vm, vm)
  29. }
  30. }

最值得研究的patch,这个函数特别的长,下面是超简略版

  1. // src/core/vdom/patch.js
  2. export function createPatchFunction (backend) {
  3. ...
  4. // Vue.prototype.__path__ = createPatchFunction()
  5. return function patch (oldVnode, vnode, hydrating, removeOnly) {
  6. // 对比
  7. console.log(oldVnode)
  8. console.log(vnode)
  9. // 最后返回的就是真实的可以用的dom
  10. return vnode.elm
  11. }
  12. }

不管是初始化渲染还是数据更新,都是把整个页面的render表达式重新渲染生成全部的vdom,进行新旧的对比,这个方法里还有最牛逼的domDiff算法,这里就不研究了,百度很多大佬解析

上面出现的几个重要的方法

  • _render函数主要执行compiler阶段,最后返回vDom
  • patch,在core/vdom/patch.js里,主要功能将对比新旧vDom转换为dom节点,最后返回的就是dom
  • _update主要是当数据改变时调用了patch函数

vue的整个实现流程

  • 给Vue函数添加很多的方法【Global-api,XXXMixin】
  • 对参数进行解析和监听【initXXX】
  • 启动mount阶段
  • _render函数把模版解析成AST,再解析成vnode
  • 初次渲染,执行_update,实际执行的是patch方法,patch将vDom渲染成DOM,初次渲染完成
  • data属性变化,_render通过AST再次生成新的vDom,通过_update里的patch进行对比,渲染到html中

最好的调试方法是下载vue.js文件,不要压缩版的,不用脚手架,然后在js里打断点就行

Vue源码(下篇)的更多相关文章

  1. 大白话Vue源码系列(02):编译器初探

    阅读目录 编译器代码藏在哪 Vue.prototype.$mount 构建 AST 的一般过程 Vue 构建的 AST 题接上文,上回书说到,Vue 的编译器模块相对独立且简单,那咱们就从这块入手,先 ...

  2. 大白话Vue源码系列(03):生成AST

    阅读目录 AST 节点定义 标签的正则匹配 解析用到的工具方法 解析开始标签 解析结束标签 解析文本 解析整块 HTML 模板 未提及的细节 本篇探讨 Vue 根据 html 模板片段构建出 AST ...

  3. 大白话Vue源码系列(03):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

  4. 大白话Vue源码系列(04):生成render函数

    阅读目录 优化 AST 生成 render 函数 小结 本来以为 Vue 的编译器模块比较好欺负,结果发现并没有那么简单.每一种语法指令都要考虑到,处理起来相当复杂.上篇已经生成了 AST,本篇依然对 ...

  5. 入口文件开始,分析Vue源码实现

    Why? 网上现有的Vue源码解析文章一搜一大批,但是为什么我还要去做这样的事情呢?因为觉得纸上得来终觉浅,绝知此事要躬行. 然后平时的项目也主要是Vue,在使用Vue的过程中,也对其一些约定产生了一 ...

  6. 入口开始,解读Vue源码(一)-- 造物创世

    Why? 网上现有的Vue源码解析文章一搜一大批,但是为什么我还要去做这样的事情呢?因为觉得纸上得来终觉浅,绝知此事要躬行. 然后平时的项目也主要是Vue,在使用Vue的过程中,也对其一些约定产生了一 ...

  7. Vue2.x源码学习笔记-Vue源码调试

    如果我们不用单文件组件开发,一般直接<script src="dist/vue.js">引入开发版vue.js这种情况下debug也是很方便的,只不过vue.js文件代 ...

  8. Vue 源码解读(8)—— 编译器 之 解析(上)

    特殊说明 由于文章篇幅限制,所以将 Vue 源码解读(8)-- 编译器 之 解析 拆成了上下两篇,所以在阅读本篇文章时请同时打开 Vue 源码解读(8)-- 编译器 之 解析(下)一起阅读. 前言 V ...

  9. VUE 源码学习01 源码入口

    VUE[version:2.4.1] Vue项目做了不少,最近在学习设计模式与Vue源码,记录一下自己的脚印!共勉!注:此处源码学习方式为先了解其大模块,从宏观再去到微观学习,以免一开始就研究细节然后 ...

随机推荐

  1. [蓝桥杯][基础训练]2n皇后问题

    Description 给定一个n*n的棋盘,棋盘中有一些位置不能放皇后.现在要向棋盘中放入n个黑皇后和n个白皇后,使任意的两个黑皇后都不在同一行.同一列或同一条对角线上,任意的两个白皇后都不在同一行 ...

  2. Django中的path函数

    path( )作用:解析URL地址 path( ) 标准语法: (<>为必须的参数,[]为可选参数) path(<route>, <view>, [name=Non ...

  3. VS2017项目中使用代码连接MySQL数据库,以及进行数据添加

    //头文件 #include "mysql.h" //函数定义 // 执行sql语句, 包括增加.删除.更新数据 bool ExecuteSql(MYSQL m_mysql,con ...

  4. Git基础及进阶-系统总结

    Git基础及进阶-系统总结 by 小强 2019-07-01 考虑到入职后不仅需要熟练掌握git的基本使用,在企业实际操作中还涉及一些进阶指令.作为一个程序员,熟练使用工具是一项基本技能,也是程序员的 ...

  5. 无法打开物理文件 XXX.mdf",操作系统错误 5.5(拒绝访问) 的解决办法

    用T-SQL命令附加数据库时,出现如下异常信息: 无法打开物理文件 XXX.mdf".操作系统错误 5:"5(拒绝访问.)". (Microsoft SQL Server ...

  6. agc026F Lotus Leaves

    题目链接 题目大意 一个n*m的网格上有一些点,一个点可以跳到与其同一行或同一列的点上.给定起点和终点. 求要使起点不能跳到终点,最少撤走几个点. \(n,m\leq 100\) 解题思路 考虑将能够 ...

  7. 用python来更改windows开机密码

    今天教大家用python脚本来控制小伙伴们windows电脑的开机密码.没错就是神不知鬼不觉,用random()随机生成的密码,只有你自己知道哦~ 代码呢分两部分,一部分是client端跟server ...

  8. Spring Boot 整合MaBatis如何在控制台打印执行的SQL语句

    yml文件:logging: level: com.XXX.Mapper: debug (红色部分为Dao层的包名,注意不是XML文件的包名) properties文件: logging.level. ...

  9. Mybatis中mapper.xml的使用

    详解多对多,mybatis多对多查询(xml方式和注解方式) 链接:https://blog.csdn.net/qq_42524262/article/details/98383977 链接:http ...

  10. 吴裕雄 python 神经网络——TensorFlow variables_to_restore函数的使用样例

    import tensorflow as tf v = tf.Variable(0, dtype=tf.float32, name="v") ema = tf.train.Expo ...