vue源码之数据驱动总结速记

vue源码 ---- 数据驱动

数据驱动的理解

指视图由数据驱动生成,也就是说我们修改视图时并不会直接操作DOM,而是借助修改数据来达到目的,这样使代码更利于维护。

// 最终它会在页面上渲染出 Hello Vue。接下来,我们会从源码角度来分析 Vue 是如何实现的
<div id="app">
  {{ message }}
</div>
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

原理总结

数据驱动原理图:
数据驱动原理图

渲染过程:

1. 初始化vue(this._init)【包括合并配置,初始化生命周期,事件中心,data,props等等】

Vue.prototype._init = function (options?: Object) {
  const vm: Component = this
	....
    ....
  // 合并配置
  if (options && options._isComponent) {
    // optimize internal component instantiation
    // since dynamic options merging is pretty slow, and none of the
    // internal component options needs special treatment.
    initInternalComponent(vm, options)
  } else {
    vm.$options = mergeOptions(
      resolveConstructorOptions(vm.constructor),
      options || {},
      vm
    )
  }
  .....
  .....
  // 初始化生命周期,初始化事件中心,初始化渲染,初始化 data、props、computed、watcher 等等
  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')
  .....
 

2. 有el属性后调用 m o u n t ( D O M ) ( mount挂载(渲染模板到DOM)( mount)【经过可能的转换使之可以使用render方法,接着实例化渲染Watcher,这个方法会先调用vm._render 方法先生成虚拟 Node,最终调用 vm._update 更新 DOM】

  • 当有el属性后,调用 vm.$mount 方法挂载 vm,挂载的目标就是把模板渲染成最终的 DOM:
 // 当有el属性后,调用 vm.$mount 方法挂载 vm,挂载的目标就是把模板渲染成最终的 DOM
  if (vm.$options.el) {
    vm.$mount(vm.$options.el)
  }
}
  • 如果没有render方法就把 el 或者 template 字符串转换成 render 方法
// 如果没有render方法就把 el 或者 template 字符串转换成 render 方法
  if (!options.render) {
    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)
    }
    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')
      }
    }
  }
    // 调用原型上的mount方法,Vue.prototype.$mount上主要会return  mountComponent方法
  return mount.call(this, el, hydrating)
}
  • 实例化渲染Watcher
 new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted) {
        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. vm._render 方法(render)通过createElement将实例渲染为Vnode(一个虚拟node)【使用normalizeChildren(children)simpleNormalizeChildren(children)将children这个参数规范化,然后根据 tag创建对应类型的Vnode】

  • VNode 是对真实 DOM 的一种抽象描述。
  • 它的核心定义无非就几个关键属性,标签名、数据、子节点、键值等,其它属性都是都是用来扩展 VNode 的灵活性以及实现一些特殊 feature 的。
  • 由于 VNode 只是用来映射到真实 DOM 的渲染,不需要包含操作 DOM 的方法,因此它是非常轻量和简单的。
  • Virtual DOM 除了它的数据结构的定义,映射到真实的 DOM 实际上要经历 VNode 的 create、diff、patch 等过程。
    VNode 的 create 是通过之前提到的 createElement 方法创建的,该方法是对_createElement的封装,期间重点是对children(Virtual DOM是树状结构,其中每个VNode也有若干给子节点)的规范,目的是将他们都规范成为 VNode 类型。

4. 执行vm._update方法(patch),将创建的Vnode渲染为真实的DOM【分为首次渲染和再次更新,其中首次渲染核心是 不断递归createElm方法,经过判断,创建占位符,传入占位符,先子后父的将DOM插入,最终创建了一个完整的 DOM 树】(DOM)

  • patch 是平台相关的,在 Web 和 Weex 环境,它们把虚拟 DOM 映射到 “平台 DOM” 的方法是不同的,并且对 “DOM” 包括的属性模块创建和更新也不尽相同。因此每个平台都有各自的 nodeOps 和 modules,但是的主要逻辑部分是相同的
    -patch主要调用了createElm,createElm 的作用是通过虚拟节点创建真实的 DOM 并插入到它的父节点中。createComponent 方法目的是尝试创建子组件。
发布了46 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/yuanfangyoushan/article/details/100189276
今日推荐