Vue 首次渲染的过程

在这里插入图片描述

一,从入口文件开始
定义在 src/platform/web/entry-runtime-with-compiler.js
二,Vue 初始化过程
1,首先取出 Vue 的 $mount 进行重写,给 $mount 增加新的功能

  // src/platform/web/entry-runtime-with-compiler.js
  // 保留 Vue实例的 $mount 方法
  const mount = Vue.prototype.$mount
  Vue.prototype.$mount = function (
    el?: string | Element,
    // 非ssr情况下的为false, ssr时候为true
    hydrating?: boolean
  ): Component {
    
    
  // 获取el对象
  el = el && query(el)

2,判断是否有 render 选项,则会把模板 template 取出,把模板编译成 render 函数,接着调用 mount 方法,渲染 DOM
3,给 Vue 增加了一个动态的 compile 方法,作用是把 HTML 字符串编译成 render 函数

  if (!options.render) {
    
    
    let template = options.template
      if (template) {
    
    
        ...
      }
  }

  Vue.compile = compileToFunctions
  export default Vue

4,这个文件主要通过 extend 给 Vue 全局注册了指令和组件,组件是 Transition 和 TransitionGroup,指令是 v-model 和 v-show,接着在 Vue 的原型上
注册了 patch 函数,patch 函数作用是将虚拟 DOM 转换为真实 DOM,在给 patch 函数赋值的时候会判断是否是浏览器环境

  // 导入两个组件成员
  // src/platforms/web/runtime/index.js
  extend(Vue.options.directives, platformDirectives)
  extend(Vue.options.components, platformComponents)
  // 判断是否是浏览器环境
  // __patch__ 函数是把虚拟 DOM 转化成为真实的 DOM
  // 判断是否是浏览器环境,如果是返回 patch 否则是 noop
  Vue.prototype.__patch__ = inBrowser ? patch : noop

三,初始化静态成员
5,定义在 src/core/index.js
在这个文件中调用 initGlobalAPI(Vue) 方法,给 Vue 的构造函数增加静态方法
6,initGlobalAPI(Vue) 定义在 src/core/global-api/index.js

初始化 Vue.config 对象
设置 keep-alive 组件
注册 Vue.use() 用来注册插件
注册 Vue.mixin() 实现混入
注册 Vue.extend() 基于传入 options 返回一个数组的构造函数
注册 Vue.directive(),Vue.component(),Vue.filter 
export function initGlobalAPI (Vue: GlobalAPI) {
    
    
      // config
      const configDef = {
    
    }
      configDef.get = () => config
      if (process.env.NODE_ENV !== 'production') {
    
    
        configDef.set = () => {
    
    
          warn(
            'Do not replace the Vue.config object, set individual fields instead.'
          )
        }
      }
      // 初始化 Vue.config 对象
      Object.defineProperty(Vue, 'config', configDef)

      // exposed util methods.
      // NOTE: these are not considered part of the public API - avoid relying on
      // them unless you are aware of the risk.
      // 这些工具方法不视作全局API的一部分,除非你已经意识到某些风险,否则不要去依赖他们
      Vue.util = {
    
    
        warn,
        extend,
        mergeOptions,
        defineReactive
      }
      // 静态方法 set/delete/nextTick
      Vue.set = set
      Vue.delete = del
      Vue.nextTick = nextTick

      // 2.6 explicit observable API
      // 让一个对象可响应
      Vue.observable = <T>(obj: T): T => {
    
    
        observe(obj)
        return obj
      }
      // 初始化 Vue.options 对象,并给其扩展
      // components/directives/filters
      Vue.options = Object.create(null)
      ASSET_TYPES.forEach(type => {
    
    
        Vue.options[type + 's'] = Object.create(null)
      })

      // this is used to identify the "base" constructor to extend all plain-object
      // components with in Weex's multi-instance scenarios.
      Vue.options._base = Vue

      // 设置 keep-alive 组件
      extend(Vue.options.components, builtInComponents)

      // 注册 Vue.use() 用来注册插件
      initUse(Vue)
      // 注册 Vue.mixin() 实现混入
      initMixin(Vue)
      // 注册 Vue.extend() 基于传入的options返回一个组件的构造函数
      initExtend(Vue)
      // 注册 Vue.directive()、 Vue.component()、Vue.filter()
      initAssetRegisters(Vue)
    }

四,初始化实例成员
_init()
定义在 src/core/instance/index.js
定义了构造函数,调用了 this._init(options) 方法
给 Vue 中混入了常用的实例成员

  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')
  }
    // 调用 _init()方法
    this._init(options)
  }
  // 注册vm的_init()方法, 初始化vm
  initMixin(Vue)
  // 注册vm 的$data/$props/$set/$delete/$watch
  stateMixin(Vue)
  // 初始化事件相关方法
  // $on/$once/$off/$emit
  eventsMixin(Vue)
  // 初始化生命周期相关的混入方法
  // _update/$forceUpdate/$destroy
  lifecycleMixin(Vue)
  // 混入 render
  // $nextTick/_render
  renderMixin(Vue)

  export default Vue

五,初始化实例成员 init()
当静态成员和实例成员都初始化完成之后,接着调用 Vue 的构造函数,在构造函数中调用 _init() 方法
_init 是在 initMixin 中初始化的,主要对 Vue 实例初始化

// vm的生命周期相关变量初始化
initLifecycle(vm)
// vm的事件监听初始化,父组件绑定在当前组件上的事件
initEvents(vm)
// vm的编译render初始化
// $slots/$scopedSlots/_c/$createElement/$attrs/$listeners
initRender(vm)
// beforeCreate 生命钩子的回调
callHook(vm, 'beforeCreate')
// 把inject的成员注入到vm上
initInjections(vm) // resolve injections before data/props
// 初始化vm的 _props/methods/_data/computed/watch
initState(vm)
// 初始化provide
initProvide(vm) // resolve provide after data/props
// created 生命钩子回调
callHook(vm, 'created')

六,初始化实例成员 initState()
初始化 vm 的 _props/methods/_data/computed/watch

export function initState (vm: Component) {
    
    
 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 && opts.watch !== nativeWatch) {
    
    
   initWatch(vm, opts.watch)
 }
}
在 instance/stats.js 中,首先获取了 Vue 实例中的 $options, 然后判断 options 中是否有 
props,methods,data 以及 computed 和 watch 这些属性,如果有的话,通过 initProps 进行初始化
initProps(vm, opts.props)接收了两个参数,一个是 Vue 实例,是一个 Props 属性,我们跳转到
initProps 函数中,首先给 Vue 实例定义了一个 _Props 对象,并且把他存储到了常量里面

const props = vm._props = { }

然后开始遍历 PropOption 的所有属性,他其实就是 initProps 方法中的第二个参数,遍历每个属性。
然后通过 defineReactive 注入到 _Props 这个对象上,这个 props 其实就是 vm.props 所有的成员
都会通过 defineReacttive 转化为 get 和 set, 最后在 Props 对象上存储。

注意:
在开发模式中,如果直接给一个属性赋值的话,会发出一个警告
生产环境中,直接通过 defineReactive 把 props 中的属性转化成 get 和 set
最后判断了 props 属性是否在 Vue 实例中存在,不存在通过 Proxy 这个函数把我们的属性注入到 Vue 的实例中
在 Proxy 中,通过调用 Object.defineProperty(target, key, sharePropertyDefinition)

七,总结
initProps 的作用是把我们的 Props 成员转化成响应式数据,并且注入到 Vue 实例里面中

2、请简述 Vue 响应式原理。

请简述 Vue 响应式原理

3、请简述虚拟 DOM 中 Key 的作用和好处。

请简述虚拟 DOM 中 Key 的作用和好处

4、请简述 Vue 中模板编译的过程。

请简述 Vue 中模板编译的过程

Guess you like

Origin blog.csdn.net/weixin_46261261/article/details/121307271