• Vue源码(一)


    入口文件 src/core/instance/index.js 中可以看到

    function Vue (options) {
      if (process.env.NODE_ENV !== 'production' &&
        !(this instanceof Vue)) {
        warn('Vue is a constructor and should be called with the `new` keyword')
      }
      this._init(options)
    }
    
    initMixin(Vue)
    stateMixin(Vue)
    eventsMixin(Vue)
    lifecycleMixin(Vue)
    renderMixin(Vue)
    
    export default Vue
    

    Vue的构造函数在此, 然后通过mixin的方式将

    • 数据方法等特性处理(state)
    • 事件处理(events)
    • 生命周期(lifecycle)
    • 渲染函数(render)
      的相关的方法挂到Vue的原型上去。

    1.init

    挂上了_init 方法,没错就是 构造函数当中调用的_init方法。

    这里做了一些参数处理,其中作者在参数对象赋值的时候,用一个个赋值代替列举赋值来提升性能。 这样注释的: // doing this because it's faster than dynamic enumeration.

    然后就是依次执行以下方法,包括各个init函数和触发钩子

    
     /* 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)
        initRender(vm)
        callHook(vm, 'beforeCreate')
        initState(vm)
        callHook(vm, 'created')
        if (vm.$options.el) {
          vm.$mount(vm.$options.el)
        }
    

    2.state

    initState一共做了如下操作

     vm._watchers = []
      const opts = vm.$options
      if (opts.props) initProps(vm, opts.props)
      if (opts.methods) initMethods(vm, opts.methods)
      if (opts.data) {
        initData(vm)
      } else {
        observe(vm._data = {}, true /* asRootData */)
      }
      if (opts.computed) initComputed(vm, opts.computed)
      if (opts.watch) initWatch(vm, opts.watch)
    

    initProps和initData的处理类似:

    initProps做了类型检验,做了defineReactive就是响应式处理;

    initData做了函数处理(data是对象或者函数),然后检查key是否与props重复,还检查了key是否$ 或者 _ 开头,这是vue内部使用, 如果你的key以此开头,你就会拿不到你的key了(这个好像没看到说明,感觉可以提示下)

    然后做了个proxy,把用户参数props和data分别代理到 _props 和 _data 这2个内部属性,用户访问其实访问到了_props和_data下。

    initMethods 这个就是把methods内部的函数bind到实例然后挂到实例

    initWatch 通过遍历然后执行$watch方法来处理

    initComputed 遍历为创建一个 Watcher 实例,然后放到_computedWatchers

    (响应式相关后续再展开)

    stateMixin 混入方法挂了$data,$props,$set,$delete等让数据操作指向_data 和 _props,然后挂了$watch 方法

    3.events

    挂载$on,$once,$off,$emit 四个方法

    事件句柄都保存在 _events 这个属性里,在$on方法里做了个处理,判断该实例是否有使用生命周期的hook,如果没有_hasHookEvent这个值是false,然后在lifecycle的callHook方法里将不会$emit生命周期hook,是一个性能优化。

    4.lifecycle

    在原型上挂上 _mount 方法,这个比较重要, $mount 用的就是方法, ($mount在上面的init函数被调用)这个方法运行的时候需要用到render函数里了,没有会报错(关于这点,vue会把template或者el里的html模版最终转成render函数,一般如果我们用vue-loader开发,已经把template里的内容转成了render函数,所以最后打包出来的不会含有转化template的功能代码),render函数返回的是一个vnode,下面贴个_mount部分代码:

     callHook(vm, 'beforeMount')
        vm._watcher = new Watcher(vm, function updateComponent () {
          vm._update(vm._render(), hydrating)
        }, noop)
        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')
        }
    

    这里可以看到2个生命周期了

    _update 方法开始去做dom更新了,看_update方法

    一开始用_isMounted判断是否触发beforeUpdate钩子,因为第一次插入dom也是调用这个方法,但是不应该触发beforeUpdate

    然后就去调用 patch 方法去做diff 然后更新dom了。(vnode以后再看)

    另外,还挂载了$forceUpdate和$destroy 这2个方法

    5.render

    先挂载了 $nextTick 方法.

    然后在renderMixin函数内往原型挂载了_render 方法(在instancei 下有个render-helpers文件夹里面有处理render相关的工具函数供调用)这个方法做了slot相关的操作,然后调用render函数拿到vnode ( render 相关的需要具体展开)

    这里有个_renderProxy ,在非生产环境下做了proxy,看 proxy.js ,就是在访问实例属性的时候,访问不存在的属性的时候给予提示.

  • 相关阅读:
    springboot项目打包方式
    Spring Boot 打包方式的选择
    java 正则匹配输出匹配内容
    Nacos注册中心的部署与用法详细介绍
    分库分表ShardingSphere,一文带你搞透(建议收藏)
    SpringCloud Ribbon负载均衡 + OpenFeign远程HTTP调用用法与原理
    vue和微信小程序的区别
    归并排序.md
    域渗透之银票据学习.md
    1_windows历程.md
  • 原文地址:https://www.cnblogs.com/Juphy/p/7041956.html
Copyright © 2020-2023  润新知