美文网首页原创的~Vue.js
3.最俗学习之-Vue源码学习-引入篇(下)

3.最俗学习之-Vue源码学习-引入篇(下)

作者: 木子tar | 来源:发表于2018-02-05 19:26 被阅读4次

    源码地址

    文件:src/core/instance/init.js

    这个就是Vue引入初始化的最后一个文件了

    这里执行5个方法,参数都是Vue构造函数

    
    initMixin(Vue)
    stateMixin(Vue)
    eventsMixin(Vue)
    lifecycleMixin(Vue)
    renderMixin(Vue)
    
    
    

    这里就直接引用大神的分析的结果了

    引入依赖,定义 Vue 构造函数,然后以Vue构造函数为参数,调用了五个方法,最后导出 Vue。这五个方法分别来自五个文件:init.js state.js render.js events.js 以及 lifecycle.js。
    打开这五个文件,找到相应的方法,你会发现,这些方法的作用,就是在 Vue 的原型 prototype 上挂载方法或属性,经历了这五个方法后的Vue会变成这样:

    
    // initMixin(Vue)    src/core/instance/init.js **************************************************
    Vue.prototype._init = function (options?: Object) {}
    
    // stateMixin(Vue)    src/core/instance/state.js **************************************************
    Vue.prototype.$data
    Vue.prototype.$set = set
    Vue.prototype.$delete = del
    Vue.prototype.$watch = function(){}
    
    // renderMixin(Vue)    src/core/instance/render.js **************************************************
    Vue.prototype.$nextTick = function (fn: Function) {}
    Vue.prototype._render = function (): VNode {}
    Vue.prototype._s = _toString
    Vue.prototype._v = createTextVNode
    Vue.prototype._n = toNumber
    Vue.prototype._e = createEmptyVNode
    Vue.prototype._q = looseEqual
    Vue.prototype._i = looseIndexOf
    Vue.prototype._m = function(){}
    Vue.prototype._o = function(){}
    Vue.prototype._f = function resolveFilter (id) {}
    Vue.prototype._l = function(){}
    Vue.prototype._t = function(){}
    Vue.prototype._b = function(){}
    Vue.prototype._k = function(){}
    
    // eventsMixin(Vue)    src/core/instance/events.js **************************************************
    Vue.prototype.$on = function (event: string, fn: Function): Component {}
    Vue.prototype.$once = function (event: string, fn: Function): Component {}
    Vue.prototype.$off = function (event?: string, fn?: Function): Component {}
    Vue.prototype.$emit = function (event: string): Component {}
    
    // lifecycleMixin(Vue)    src/core/instance/lifecycle.js **************************************************
    Vue.prototype._mount = function(){}
    Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {}
    Vue.prototype._updateFromParent = function(){}
    Vue.prototype.$forceUpdate = function () {}
    Vue.prototype.$destroy = function () {}
    
    

    到了这里,引入Vue这个构造函数的初始化就基本告一段落了,接下来就是我们要创建一个实例了
    这里还是采用大神的例子

    
    let v = new Vue({
        el: '#app',
        data: {
            a: 1,
            b: [1, 2, 3]
        }
    })
    
    // 然后就会走这个方法
    
    Vue.prototype._init = function (options?: Object) {
      const vm: Component = this
      // a uid
      vm._uid = uid++
      // a flag to avoid this being observed
      vm._isVue = true
      // merge options
      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
        )
      }
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production') {
        initProxy(vm)
      } else {
        vm._renderProxy = vm
      }
    
      // expose real self
      vm._self = vm
      initLifecycle(vm)
      initEvents(vm)
      callHook(vm, 'beforeCreate')
      initState(vm)
      callHook(vm, 'created')
      initRender(vm)
    }
    
    // 这里的if (options && options._isComponent)就不说了,看了很多教程都跳过这里,因为这里是
    // 内部使用的,我们主要看这两个方法mergeOptions和resolveConstructorOptions(vm.constructor)
    // 先说这个resolveConstructorOptions,如果按照平常的套路这里就会直接返回我们传入的options参数
    // 这里个人好奇的用了一个简单的例子尝试了一下,看看它的作用是什么
    
    

    <p style="font-weight: bold;margin-bottom: 10px;color: blue">
    resolveConstructorOptions之个人试用
    </p>

    
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>resolveConstructorOptions之个人试用</title>
    </head>
    <body>
      <div id="app">
        <p style="color: blue">{{msg}}</p>
        <p style="color: blue">{{name}}</p>
        <p style="color: blue">{{cpName}}</p>
        <button v-on:click="clickMe">click me</button>
      </div>
    </body>
    <script type="text/javascript" src="vue.js"></script>
    <script>
    
    Vue.super = {
      options: {
        data () {
          return {
            name: 'super defined',
            msg: 'hello world from Vue.super'
          }
        },
        computed: {
          cpName () {
            return this.name + ' from Vue.super'
          }
        },
        methods: {
          clickMe () {
            alert('click form Vue.super')
          }
        }
      }
    }
    
    Vue.extendOptions = {
    
    }
    
    var options = {
      el: '#app',
      data () {
        return {
          msg: 'hello world'
        }
      },
      methods: {
        clickMe () {
          alert('click form new Vue')
        }
      }
    }
    
    
    var myVue = new Vue(options)
    
    console.log(myVue)
    </script>
    </html>
    
    
    

    <span style="color: blue">上面的例子直接复制到浏览器打开即可看到效果,这里我们看到new Vue的数据会把Vue.super的数据覆盖,
    如果没有的话,则会使用Vue.super的数据,感觉有点像是合并参数的样子,但是这个东西肯定不是这样用的,
    这里还是坐等大神解释这个高级的用法,这里就不做深入研究了,因为一般都很少用到这种情况</span>

    
    // 那么这里就直接是返回Vue.options了,也就是之前说过的这个东西
    
    
    Vue.options = {
        components: {
            KeepAlive,
            Transition,
            TransitionGroup
        },
        directives: {
            model,
            show
        },
        filters: {},
        _base: Vue
    }
    
    // 至于options则是我们传入的各种参数,按照上面的例子则是这样的,这里也直接从大神文章里面复制过来了
    
    vm.$options = mergeOptions(
        // Vue.options
      {
          components: {
              KeepAlive,
              Transition,
              TransitionGroup
          },
          directives: {
              model,
              show
          },
          filters: {},
          _base: Vue
      },
      // 调用Vue构造函数时传入的参数选项 options
      {
          el: '#app',
          data: {
              a: 1,
              b: [1, 2, 3]
          }
      },
      // this
      vm
    )
    
    
    

    src/core/util/options.js

    顺着路径我们找到这个文件,在264行里就可以看到这个方法,这个是Vue的合并策略方法,再看源码之前,
    有看过很多的文章,其中这个方法非常<span style="font-weight: bold;color: red;">重要!重要!重要!</span>
    这里打算把这个东西研究的尽量深入一点,以免后面各种懵逼,首先我们从最顶部开始

    
    const strats = config.optionMergeStrategies;
    
    定义一个全局变量,然而optionMergeStrategies: Object.create(null),所以就是
    
    const strats = {}
    
    strats.el = strats.propsData = function (parent, child, vm, key) {
      return defaultStrat(parent, child)
    }
    
    strats.data = function (
      parentVal: any,
      childVal: any,
      vm?: Component
    ) {}
    
    
    // 然后还有这两个东东,找到config.js下面的两个变量,其实就是两个数组
    config._lifecycleHooks.forEach(hook => {
      strats[hook] = mergeHook
    })
    
    config._assetTypes.forEach(function (type) {
      strats[type + 's'] = mergeAssets
    })
    
    
    /**
     * List of asset types that a component can own.
     */
    _assetTypes: [
      'component',
      'directive',
      'filter'
    ],
    
    /**
     * List of lifecycle hooks.
     */
    _lifecycleHooks: [
      'beforeCreate',
      'created',
      'beforeMount',
      'mounted',
      'beforeUpdate',
      'updated',
      'beforeDestroy',
      'destroyed',
      'activated',
      'deactivated'
    ],
    
    // 经过这一段操作后,则strats变成了下面这样
    
    strats = {
      el: function (parent, child, vm, key) {
        return defaultStrat(parent, child)
      },
      propsData: function (parent, child, vm, key) {
        return defaultStrat(parent, child)
      },
      data: function (parentVal: any,childVal: any,vm?: Component) {
        do something...
      },
      beforeCreate: function mergeHook () {
        do something...
      },
      created: function mergeHook () {
        do something...
      },
      beforeMount: function mergeHook () {
        do something...
      },
      mounted: function mergeHook () {
        do something...
      },
      beforeUpdate: function mergeHook () {
        do something...
      },
      updated: function mergeHook () {
        do something...
      },
      beforeDestroy: function mergeHook () {
        do something...
      },
      destroyed: function mergeHook () {
        do something...
      },
      activated: function mergeHook () {
        do something...
      },
      deactivated: function mergeHook () {
        do something...
      },
      components: function mergeAssets () {  // 这里加上个s复数
        do something...
      },
      directives: function mergeAssets () {  // 这里加上个s复数
        do something...
      },
      filters: function mergeAssets () {  // 这里加上个s复数
        do something...
      },
      watch: function (parentVal: ?Object, childVal: ?Object) {
        do something...
      },
      props: function (parentVal: ?Object, childVal: ?Object) {
        do something...
      },
      methods: function (parentVal: ?Object, childVal: ?Object) {
        do something...
      },
      computed: function (parentVal: ?Object, childVal: ?Object) {
        do something...
      }
    }
    
    

    这样子就清晰多了,引用大神的话就是这样的:

    “config 对象引用自 src/core/config.js 文件,最终的结果就是在 strats 下添加了相应的生命周期选项的合并策略函数为 mergeHook,添加指令(directives)、组件(components)、过滤器(filters)等选项的合并策略函数为 mergeAssets。”

    <span style="color: red;font-weight: bold;">回到主题</span>

    
    vm.$options = mergeOptions(
        // Vue.options
      {
          components: {
              KeepAlive,
              Transition,
              TransitionGroup
          },
          directives: {
              model,
              show
          },
          filters: {},
          _base: Vue
      },
      // 调用Vue构造函数时传入的参数选项 options
      {
          el: '#app',
          data: {
              a: 1,
              b: [1, 2, 3]
          }
      },
      // this
      vm
    )
    
    // 方法首先执行checkComponents(child),其中涉及两个方法isBuiltInTag和isReservedTag,后者已经,
    // 说过前面两个方法在methods realizes目录查看,所以这里就是禁止使用html标签svg标签还有slot,component这两个
    // 作为组件名字,当然我们这个例子不会有问题,因为我们没有用到组件components来注册
    // 然后再到了normalizeProps(child)和normalizeDirectives(child)还是在methods realizes目录说明
    // 这里我们使用的例子没有props字段,在方法解释里面我们模拟一个即可,这里面还有个camelize方法,也
    // 一起放在methods realizes目录下,那么这三个方法大概如下
    // camelize  =>  字符串转驼峰
    // normalizeProps  =>  标准化props字段的格式
    // normalizeDirectives  =>  标准化directives字段的格式
    
    // 然后到了下面这一段,这里不明白具体功能,先不管
    
    const extendsFrom = child.extends
    if (extendsFrom) {
      parent = typeof extendsFrom === 'function'
        ? mergeOptions(parent, extendsFrom.options, vm)
        : mergeOptions(parent, extendsFrom, vm)
    }
    
    // 这一段留到下一篇再讲,顺便把之前留的一个问题也解决了
    
    if (child.mixins) {
      for (let i = 0, l = child.mixins.length; i < l; i++) {
        let mixin = child.mixins[i]
        if (mixin.prototype instanceof Vue) {
          mixin = mixin.options
        }
        parent = mergeOptions(parent, mixin, vm)
      }
    }
    
    // 这一段将会举N多个例子来测试,尽量把这个合并策略弄懂,因为这个是Vue的精粹之一
    
    const options = {}
    let key
    for (key in parent) {
      mergeField(key)
    }
    for (key in child) {
      if (!hasOwn(parent, key)) {
        mergeField(key)
      }
    }
    function mergeField (key) {
      const strat = strats[key] || defaultStrat
      options[key] = strat(parent[key], child[key], vm, key)
    }
    return options
    
    
    

    <p style="font-weight: bold;margin-bottom: 10px;color: #FF0000">剩下的几个问题</p>

    
    Vue.set = set                       // 涉及到Vue的数据响应式系统,先保留
    Vue.delete = del                    // 涉及到Vue的数据响应式系统,先保留
    Vue.nextTick = util.nextTick        // 水平有限,看不懂 - -#
    initMixin(Vue)                      // 这个后面再讲
    initExtend(Vue)                     // 水平有限,看不懂 - -#
    
    
    extend(Vue.options.directives, platformDirectives)  // 水平有限,看不懂 - -#
    extend(Vue.options.components, platformComponents)  // 水平有限,看不懂 - -#
    Vue.prototype.__patch__                             // 水平有限,看不懂 - -#
    compileToFunctions                                  // 水平有限,看不懂 - -#
    
    
    const extendsFrom = child.extends                   // 水平有限,看不懂 - -#
    
    

    相关文章

      网友评论

        本文标题:3.最俗学习之-Vue源码学习-引入篇(下)

        本文链接:https://www.haomeiwen.com/subject/mdvizxtx.html