vue2 框架运行原理剖析系列(二)之 组件挂载$mount神秘之旅!!!

一、vue组件挂载

1.1 上一篇文章中,介绍到组件执行 mountComponent 函数,本文对此展开详细的讲解。 1.2 调用改方法的位置在于entry-runtime-with-compiler.js 的Vue.prototype.$mount,具体代码如下: 其中, (1)el 是 实例化中,传入构造函数的el 选项,且options.render 的值不存在 (2)如果构造函数中,传入了template 选项,则template 的值就是把这个值处理成对应的template。 (3)如果template 不存在,其次会判断el 选项是否存在,如果存在,则根据文档文字,找到指定的标签包裹的HTML,作为template。 (4)以上方法在实例化 vue 中,存在el 选项的情况下执行,如果我们在开发过程中,没有使用这样方式创建,而是创建完之后,再挂载,也是可以的,这样就是用户手动触发该挂载函数,

javascript 复制代码
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  debugger
  el = el && query(el)
  //执行$mount 函数,会判断是否存在render 函数,不存在在则将template编译成渲染函数,把render赋值为改渲染函数
  const options = this.$options
  // resolve template/el and convert to render function
  if (!options.render) {
    let template = options.template
    if (template) {
      if (typeof template === 'string') {
        if (template.charAt(0) === '#') {
          template = idToTemplate(template)
          /* istanbul ignore if */
        }
      } else if (template.nodeType) {
        template = template.innerHTML
      } else {
        return this
      }
    } else if (el) {
      template = getOuterHTML(el)
    }
    if (template) {
      /* istanbul ignore if */
      const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns
    }
  }
  return mount.call(this, el, hydrating)
}

1.3 代码的解释可以对应到vue官网的生命周期示例图 1.4 是否存在el 的选项,实际上是在 init 函数的时候判断的,代码如下:

javascript 复制代码
//文件位置 src/core/instance/init.js
if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }

1.5 挂载vue 的两种方式如下: (1)用户手动触发 挂载函数,就是对于上图的对应的没有el 选项的情况

javascript 复制代码
const app= new Vue();
app.$mount('.todoapp')

(2)实例化过程传入el选项

javascript 复制代码
const app= new Vue({el:".todoapp"});

这两种方式实际上都会调用同一个方法,就是上面说的挂载函数,还会执行同样的判断逻辑。

二、模板 template 编译生成抽象语法树ast与渲染函数render生成

2.1 在我们实际过程中,我们把vue 的模板语法写到HTML 文件中,这样,如果没有经过特殊处理,浏览器都会把他们当做普通的文本处理,不会显示成我们想要的效果,那么,这一个步骤究竟是怎么实现的,下面我就带大家深入vue的模板编译。 2.2 在 一 中,介绍了template如何获取,那么下面就针对其接下来的要做的工作展开剖析,下一步代码如下: 将 获取到的template 传入 compileToFunctions 函数中 ,得到一个 render 和 staticRenderFns , 然后将其赋值给 options,这个options 实际上就是指向 构造函数的参数对象

javascript 复制代码
 const { render, staticRenderFns } = compileToFunctions(template, {
        outputSourceRange: process.env.NODE_ENV !== 'production',
        shouldDecodeNewlines,
        shouldDecodeNewlinesForHref,
        delimiters: options.delimiters,
        comments: options.comments
      }, this)
      options.render = render
      options.staticRenderFns = staticRenderFns

2.3 那么这个compileToFunctions 函数到底做了什么,其代码如下:

javascript 复制代码
export const createCompiler = createCompilerCreator(function baseCompile (
  template: string,
  options: CompilerOptions
): CompiledResult {
  debugger
  const ast = parse(template.trim(), options)
  if (options.optimize !== false) {
    optimize(ast, options)
  }
  const code = generate(ast, options)
  return {
    ast,
    render: code.render,
    staticRenderFns: code.staticRenderFns
  }
})

2.4 其中,parse() 函数,是将template 编译成抽象语法树,其实际上就是一个对象,根据文档的嵌套结构,使用js 对象和其属性,模拟出来的一个表达其文档信息的对象,如下图所示: 2.5 得到抽象语法树之后,调用 generate(ast, options) 方法,把抽象语法树,作为参数,生成一个 code对象 ,code 具体样子如下: 2.6 其中code对象 render 值,就是一个函数,这个函数其实就是生成一个虚拟DOM 的函数,后面会在 _render 函数中调用这个函数

三、虚拟DOM 与 渲染函数与视图更新

3.1 mountComponent 的代码如下: 其中定义一个 updateComponent 方法用于更新视图,里面调用了vm._render(), 这个方法就是调用了二中的 render 函数,生成一个虚拟DOM ,然后执行vm._update 方法渲染真实的dom。 其中updateComponent 方法触发的机制是 数据方式变化是,由Watcher 调用 updateComponent 方法更新视图

javascript 复制代码
xport function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  // debugger
  vm.$el = el
  callHook(vm, 'beforeMount')
  let updateComponent
  /* istanbul ignore if */
   updateComponent = () => {
     vm._update(vm._render(), hydrating)
  }
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted && !vm._isDestroyed) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
  hydrating = false
  // manually mounted instance, call mounted on self
  // mounted is called for render-created child components in its inserted hook
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  return vm

3.2 vm._update 方法代码如下,其位置在src/core/instance/lifecycle 中,代码如下: 该方法 调用了patch 方法,进行新旧虚拟dom 的对比,然后更新视图,实现视图更新

javascript 复制代码
 Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
    const vm: Component = this
    const prevEl = vm.$el
    const prevVnode = vm._vnode
    const restoreActiveInstance = setActiveInstance(vm)
    vm._vnode = vnode
    // Vue.prototype.__patch__ is injected in entry points
    // based on the rendering backend used.
    if (!prevVnode) {
      // initial render
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
    } else {
      // updates
      vm.$el = vm.__patch__(prevVnode, vnode)
    }
    restoreActiveInstance()
    // update __vue__ reference
    if (prevEl) {
      prevEl.__vue__ = null
    }
    if (vm.$el) {
      vm.$el.__vue__ = vm
    }
    // if parent is an HOC, update its $el as well
    if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {
      vm.$parent.$el = vm.$el
    }
    // updated hook is called by the scheduler to ensure that children are
    // updated in a parent's updated hook.
  }
相关推荐
弗锐土豆35 分钟前
一个基于若依(ruoyi-vue3)的小项目部署记录
前端·vue.js·部署·springcloud·ruoyi·若依
1undefined239 分钟前
element中的table改造成虚拟列表(不定高),并封装成hooks
前端·vue.js
wangpq1 小时前
element-ui表单使用validateField校验多层循环中的字段
javascript·vue.js
豆苗学前端1 小时前
从零开始教你如何使用 Vue 3 + TypeScript 实现一个现代化的液态玻璃效果(Glass Morphism)登录卡片
前端·vue.js·面试
我命由我123451 小时前
Vue 开发问题:Missing required prop: “value“
开发语言·前端·javascript·vue.js·前端框架·ecmascript·js
快起来别睡了1 小时前
Vue 中组件的生命周期与 v-if、v-show 的区别详解
前端·vue.js
张旭超2 小时前
vue3 + element-plus el-table表格二次封装,支持复选框,排序,分页。
前端·vue.js
亿坊电商3 小时前
VUE混合开发,选哪个PHP框架最顺手?
前端·vue.js·php
程序猿小D3 小时前
[附源码+数据库+毕业论]基于Spring Boot+mysql+vue结合内容推荐算法的学生咨询系统
数据库·vue.js·spring boot·mysql·毕业设计·推荐算法·学生咨询系统
我爱加班、、3 小时前
element-plus表单校验失败问题
前端·javascript·vue.js·elementui·ecmascript