new Vue 发生了什么?

110929d53e9e5cbe32e79577606ddec9.png

const app = new Vue({
  template: '<div>hello world</div>',
  el: '#app'
})

Vue实际上是一个类

function Vue (options) {
  if (!(this instanceof Vue)) {
    // 必须通过new来调用
    warn('Vue is a constructor and should be called with the `new` keyword')
  }
  // 注意options参数,是创建实例时候传入的对象
  this._init(options)
}

可以看到 Vue 只能通过 new 关键字初始化,然后会调用 this._init 方法。

Vue.prototype._init = function (options) {
  const vm = this
  vm._self = vm
  // 这些代码之后再讲
  initLifecycle(vm)
  initEvents(vm)
  initRender(vm)
  callHook(vm, 'beforeCreate')
  initInjections(vm) // resolve injections before data/props
  initState(vm)
  initProvide(vm) // resolve provide after data/props
  callHook(vm, 'created')


  // 先看这行代码
  if (vm.$options.el) {
    vm.$mount(vm.$options.el)
  }
}

先看$mount

// 先缓存之前的$mount方法
const mount = Vue.prototype.$mount
// 再重写$mount方法
Vue.prototype.$mount = function (el) {
  const options = this.$options
  if (template) {
    const { render, staticRenderFns } = compileToFunctions(template, {
      shouldDecodeNewlines,
      shouldDecodeNewlinesForHref,
      delimiters: options.delimiters,
      comments: options.comments
    }, this)
    options.render = render // render很重要,在后面会用到
    options.staticRenderFns = staticRenderFns
  }
  return mount.call(this, el, hydrating) // 开始挂载
}

注意compileToFunctions是把template转换成render函数,怎么转的看下下面

const compiler = require('vue-template-compiler')
let str = `<div v-if="msg">999</div>`


console.log(compiler.compile(str))

发现包含ast和render这两个属性。注意这个render我们之后会用到

{
  ast: {
    type: 1,
    tag: 'div',
    attrsList: [],
    attrsMap: { 'v-if': 'msg' },
    rawAttrsMap: {},
    parent: undefined,
    children: [ [Object] ],
    if: 'msg',
    ifConditions: [ [Object] ],
    plain: true,
    static: false,
    staticRoot: false,
    ifProcessed: true
  },
  render: `with(this){return (msg)?_c('div',[_v("999")]):_e()}`,
  staticRenderFns: [],
  errors: [],
  tips: []
}

之前的$mount方法

Vue.prototype.$mount = function (el) {
  el = el && query(el)
  return mountComponent(this, el)
}

终于来到了重点mountComponent方法

export function mountComponent () {
  callHook(vm, 'beforeMount')
  let updateComponent
  updateComponent = () => {
    const name = vm._name
    const id = vm._uid
    const startTag = `vue-perf-start:${id}`
    const endTag = `vue-perf-end:${id}`


    mark(startTag)
    const vnode = vm._render() // 重点
    mark(endTag)
    measure(`vue ${name} render`, startTag, endTag)


    mark(startTag)
    vm._update(vnode, hydrating) // 重点
    mark(endTag)
    measure(`vue ${name} patch`, startTag, endTag)
  }
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
}

mountComponent 核心就是先实例化一个渲染Watcher,在它的回调函数中会调用 updateComponent 方法,在此方法中调用 vm._render 方法先生成虚拟 Node,最终调用 vm._update 更新 DOM。

Watcher 在这里起到两个作用,一个是初始化的时候会执行回调函数,另一个是当 vm 实例中的监测的数据发生变化的时候执行回调函数,这块儿我们会在之后的章节中介绍。

Vue 的 _render 方法,它用来把实例渲染成一个虚拟 Node。

Vue.prototype._render = function () {
  const vm = this
  const { render, _parentVnode } = vm.$options
  let vnode
  try {
    vnode = render.call(vm._renderProxy, vm.$createElement)
  } catch (e) {}
}

这段代码最关键的是 render 方法的调用,我们在平时的开发工作中手写 render 方法的场景比较少,而写的比较多的是 template 模板,在之前的 mounted 方法的实现中,会把 template 编译成 render 方法,但这个编译过程是非常复杂的,我们不打算在这里展开讲,之后会专门花一个章节来分析 Vue 的编译过程。

在 Vue 的官方文档中介绍了 render 函数的第一个参数是 createElement

render: function (createElement) {
  return createElement('div', {
     attrs: {
        id: 'app'
      },
  }, this.message)
}

再回到 _render 函数中的 render 方法的调用:

vnode = render.call(vm._renderProxy, vm.$createElement)

可以看到,render 函数中的 createElement 方法就是 vm.$createElement 方法。实际上,vm.$createElement 方法定义是在执行 initRender 方法的时候。

export function initRender (vm) {
  vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false)
  vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true)
}

结合我们之前的render

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值