渲染函数的观察者与进阶的数据响应系统

渲染函数的观察者与进阶的数据响应系统

$mount 挂载函数

在上面讲述中已经为data中每个属性添加了观察者 但是哪的dep dep.depend() dep.notify()收集 触发相应 我们还并不知道其是什么样的,在这之外还有更重要的东西 Wather, 继续从init 函数

Vue.prototype._init = function (options?: Object) {
  // 省略...
  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)
  }
}

最后一句 vm.$mount(vm.$options.el) 是_init初始化最后一行代码,之前都初始化了,我们先来了解一下$mount函数

$mount 的定义出现在两个地方,第一个地方是 platforms/web/runtime/index.js 文件,如下

Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
//接受两个参数 元素或者字符串 第二个先不用了解
  el = el && inBrowser ? query(el) : undefined
  query实在查找对应元素即字符串 
  return mountComponent(this, el, hydrating)
}

第二个在src/platforms/web/entry-runtime-with-compiler.js 文件 即完整版 其实在原功能上添加了编译模板的功能

const mount = Vue.prototype.$mount
Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  // 省略...
  return mount.call(this, el, hydrating)
}

首先用mount缓存了运行版的$mount函数,然后重新定义了 $mount接受参数不变

el = el && query(el)

/* istanbul ignore if */
if (el === document.body || el === document.documentElement) {
  process.env.NODE_ENV !== 'production' && warn(
    `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
  )
  return this
}

同样是先获取dom元素,这个元素为挂载点,之后判断是否为body和html元素 若是会警告,因为挂载点会被整个模板替换掉所以不能选择挂载body和html元素

const options = this.$options
// resolve template/el and convert to render function
if (!options.render) {
  // 省略...
}
return mount.call(this, el, hydrating)

取得options应用 检测是否有render函数 若有就不做 直接运行运行版 其实是通过mountComponent来挂载 而if里的作用就是提供render函数给mountComponent的

let template = options.template
if (template) {
  if (typeof template === 'string') {
    if (template.charAt(0) === '#') {
      template = idToTemplate(template)
      /* istanbul ignore if */
      if (process.env.NODE_ENV !== 'production' && !template) {
        warn(
          `Template element not found or is empty: ${options.template}`,
          this
        )
      }
    }
  } else if (template.nodeType) {
    template = template.innerHTML
  } else {
    if (process.env.NODE_ENV !== 'production') {
      warn('invalid template option:' + template, this)
    }
    return this
  }
} else if (el) {
  template = getOuterHTML(el)
}

. 首先获取template,没有render优先使用template来渲染,并尝试编译为render函数 但是不一定会传template,这时会检测el是否存在,存在就使用el.outerHTML作为temlate值 其实 本质就是在选区合适内容做模板

  1. 如果 template 选项不存在,那么使用 el 元素的 outerHTML 作为模板内容
  2. 如果 template 选项存在:
  3. 且 template 的类型是字符串
  4. 如果第一个字符是 #,那么会把该字符串作为 css 选择符去选中对应的元素,并把该元素的 innerHTML 作为模板
  5. 如果第一个字符不是 #,那么什么都不做,就用 template 自身的字符串值作为模板
  6. 且 template 的类型是元素节点(template.nodeType 存在) 则使用该元素的 innerHTML 作为模板
  7. 若 template 既不是字符串又不是元素节点,那么在非生产环境会提示开发者传递的 template 选项无效

template是有一个模板字符串, 后续就是求值的简化 和边界情况,如svg没有outerHTML 之后就到了关键阶段

if (template) {
  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    mark('compile')
  }

  const { render, staticRenderFns } = compileToFunctions(template, {
    shouldDecodeNewlines,
    shouldDecodeNewlinesForHref,
    delimiters: options.delimiters,
    comments: options.comments
  }, this)
  options.render = render
  options.staticRenderFns = staticRenderFns

  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    mark('compile end')
    measure(`vue ${this._name} compile`, 'compile', 'compile end')
  }
}

使用 compileToFunctions将template编译为渲染函数 并且添加到vm.$options 而上下的 mark是在统计编译器性能的 其实mount函数作用就是将模板编译为渲染函数,然后交给 mountComponent去挂载

渲染函数的观察者

export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  // 省略...
  接受3个参数 实例vm 挂载元素el 以及透传来的hydrating
}

这里到底是挂载在哪呢 vm.$el是id为bar的div引用, 继续向下

if (!vm.$options.render) {
  vm.$options.render = createEmptyVNode
  if (process.env.NODE_ENV !== 'production') {
    /* istanbul ignore if */
    if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||
      vm.$options.el || el) {
      warn(
        'You are using the runtime-only build of Vue where the template ' +
        'compiler is not available. Either pre-compile the templates into ' +
        'render functions, or use the compiler-included build.',
        vm
      )
    } else {
      warn(
        'Failed to mount component: template or render function not defined.',
        vm
      )
    }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值