最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue2.x源码解析 - 数据驱动

    正文概述 掘金(沐寒晚枫)   2021-02-13   477

    new Vue 实例

    // 整体流程
    new Vue()
    
    this._init(options)
    
    //调用 initMixin(Vue) 中绑定的 _init 方法
    Vue.prototype._init = function(options) { const vm =  this }
    
    // 一系列初始化操作 initLifecycle, initEvents, initRender ... initState, ...
    
    // 挂载
    vm.$mount(vm.$options.el) // #app
    
    // 关注 initState 方法
    export function initState(vm) {
      // options合并
      vm.$options = mergeOptions( ... )
      
      const opts = vm.$options
      initProps(vm, opts.props)
      initMethods(vm, opts.methods)
      initData(vm) // 关注点
      initComputed(vm, opts.computed)
      initWatch(vm, opts.watch)
    }
    
    // initData 方法
    function initData(vm) {
      const data = vm.$options.data
      // data 是否为 function, 注意 vm._data
      data = vm._data = typeof data === 'function' ? getData(data, vm) : data || {}
      
      // 判断属性是否与 props, methods中定义的属性冲突
      const keys = Object.keys(data)
      // ... 
      
      // 通过 Object.defineProperty 代理 this._data.xxx 至 this.xxx
      // get: return this._data.xxx
      // set: this._data.xxx = val
      proxy(vm, `_data`, key)
      
      // 响应式处理
      observe(data, true /* asRootData */)
    }
    

    Vue 实例挂载

    const mount = Vue.prototype.$mount // 缓存 - runtime only 版本直接使用此 mount 方法
    Vue.prototype.$mount = function() {} // 重新定义
    
    // 分别判断 render 和 template
    // 最终都会转化为 render function
    const { render } = compileToFunctions()
    vm.$options.render = render
    
    // 调用原先原型上(即缓存的)$mount 方法进行挂载
    return mount.call(this, el, hydrating) // hydrating 服务端渲染相关, 默认 false 即可
    
    // 执行 mount 方法中的 mountComponent 方法
    return mountComponent(this, el, hydrating)
    
    // mountComponent 方法中主要做了两点
    
    // 1. 调用 vm._render() 生成虚拟 node
    const vnode = vm._render()
    
    // 2. 实例化 watcher, 并调用 updateComponent 方法 - 初始化 & 数据变化均会执行
    new Watcher(vm, updateComponent, noop, {}, true)
    updateComponent = () => {
      vm._update(vm._render(), hydrating) // 更新 dom
    }
    
    // 流程结束, 改变状态
    vm._isMounted = true
    callhook(vm, 'mounted')
    

    render 函数

    Vue.prototype._render = function() {
      const vm = this
      // 入口部分处理好的 render 函数
      const { render } = vm.$options
      
      // 核心部分 - vm.$createElement
      vnode = render.call(vm._renderProxy, vm.$createElement)
      
      return vnode
    }
    
    // vm.$createElement 方法的定义是在 initRender(vm) 中
    
    // e.g. 在项目中使用 render 函数渲染的示例
    import Vue from 'vue'
    
    new Vue({
      el: '#app',
      // createElement -> vm.$createElement
      render: function(createElement) {
        return createElement('div', {
          attrs: {
            id: 'app'
          }
        }, this.message)
      },
      data() {
       return {
        message: 'hello, vue'
       }
      },
    })
    

    virtual dom

    createElement

    // 对 _createElement 的一层封装
    export function createElement() {
      return _createElement(context, tag, data, children, normalizationType)
    }
    // context - VNode 上下文环境: Component
    // data - VNode 数据: VNodeData
    // children - VNode 子节点: any - 需要被规范为标准的 VNode 数组
    
    // normalizationType - 字节点规范类型 - 【主要】区分函数编译生成 || 用户手写
    
    // src/core/instance/render.js
    export function initRender(vm) {
      vm._c(vm, a, b, c, d, false) // render functions complied from template.
      vm.$createElement(vm, a, b, c, d, true) // user-written render functions.
    }
    
    // normalizationType 类别
    // false - simpleNormalize
    // true - alwaysNormalize
    
    // 总结一下
    // render functions 是编译生成 - false - simple - 调用 simpleNormalizeChildren
    // render functions 是用户手写 - true - always - 调用 normalizeChildren
    
    // src/core/vdom/helpers/nomalize-children.js
    export function simpleNormalizeChildren(children) {
      // 默认 VNode 类型, 但函数式组件返回的是数组而不是根结点
      // 1-level deep
      for(let i = 0; i < children.length; i++) {
        if(Array.isArray(children[i])) {
          return Array.prototype.concat.apply([], children)
        }
      }
      return children
    }
    
    // 两个场景
    // 1. 用户手写, children 只有一个节点, 创建简单的文本节点
    // 2. <template>, <slot>, v-for 产生嵌套数组的情况
    export function normalizeChildren(children) {
      return isPrimitive(children) // 是否为基本类型
      ? [createTextVNode(children)] // 创建简单的文本节点
      : Array.isArray(children)
      ? normalizeArrayChildren(children)
      : undefined  
    }
    export function normalizeArrayChildren(children) {
      // 遍历 children
      // 若子节点 c 为 Array, 则递归调用该方法
      // 若子节点 c 为基础类型, 则调用 createTextVNode()
      
      // opt: 若存在连续的 text 节点, 会把它们合并成一个 text 节点
    }
    
    // 创建 VNode
    if(typeof tag === 'string') {
      // 1. 内置节点
      vnode = new VNode(parsePlatformTagName(tag), data, children, undefined, undefined, context)
      // 2. 已注册组件名
      vnode = createComponent(Ctor, data, context, children, tag)
      // 3. 未知节点
      vnode = new VNode(tag, data, children, undefined, undefined, context)
    } else {
      vnode = createComponent(tag, data, context, children)
    }
    return vnode
    
    // vm._render() 过程至此完毕
    

    update

    Vue.prototype._update = function(vnode, hydrating) {
      // 核心部分
      vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false)
    }
    
    // src/platforms/web/runtime/index.js
    Vue.prototype.__patch__ = inBrowser ? patch : noop // 服务端渲染没有 dom 环境
    // src/platforms/web/runtime/patch.js
    export const patch = createPatchFuntion({nodeOps, modules }) // 真正执行的是 patch
    
    // src/core/vdom/patch.js
    export function createPatchFunction(backend) {
      // 核心部分 - 通过虚拟节点创建真实的 DOM 并插入父节点
      createElm() {
        // 遍历子虚拟节点, 递归调用 - 深度优先
        // 递归调用, 子元素优先 insert, 所以 vnode 树的插入顺序是先父后子
        createChildren()
      }
    }
    
    // 调用原生 DOM API 操作
    function insert(parent, ele, ref) {
      // e.g.
      // insertBefore()
      // appendChild()
    }
    

    Vue2.x源码解析 - 数据驱动


    起源地下载网 » Vue2.x源码解析 - 数据驱动

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元