最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue首次渲染过程

    正文概述 掘金(zhangdebao)   2020-12-22   518

    简答题

    一、请简述 Vue 首次渲染的过程?

    Vue首次渲染过程

    一、入口文件

    src/platform/web/entry-runtime-with-compiler.js

    一、Vue初始化过程,寻找Vue构造函数
    • 重写Vue的$mount的方法,增加新功能
    /**src/platforms/web/runtime/index.js
     * 把模版编译成render函数
     * @param {*}
     * @return {*}
     */
    Vue.prototype.$mount = function (
      el?: string | Element,
      // 非ssr情况下为 false,ssr 时候为true
      hydrating?: boolean
    ): Component {
      ....
    // 调用 mount 方法,渲染 DOM
    return mount.call(this, el, hydrating)
    
    
    • 判断是否有render选项, 如果没有render选项,则会把模板template编译成render函数, 然后调用mount方法(/src/platforms/web/runtime/index.js的$mount),渲染DOM
    • Vue增加静态成员compile方法,把html字符串编译成render函数
    if (!options.render) {
      let template = options.template
        if (template) {
          ...
        }
    }
    
    Vue.compile = compileToFunctions
    export default Vue
    
    • src/platforms/web/runtime/index.js 该文件通过extend给vue全局注册指令(v-model和v-show)和组件(Transition和TransitionGroup), 给Vue原型上添加_patch_函数, 作用是将虚拟DOM转换成真实DOM,调用patch函数会判断浏览器环境
    // /src/platforms/web/runtime/index.js
    
    // install platform runtime directives & components
    extend(Vue.options.directives, platformDirectives)
    extend(Vue.options.components, platformComponents)
    
    // install platform patch function
    Vue.prototype.__patch__ = inBrowser ? patch : noop
    
      • 继续寻找Vue构造函数
    一、完整版Vue入口文件(src/platform/web/entry-runtime-with-compiler.js)
    二、Vue的初始化过程
    • 重写Vue的$mount的方法,增加新功能
    /**
     * 把模版编译成render函数
     * @param {*}
     * @return {*}
     */
    Vue.prototype.$mount = function (
      el?: string | Element,
      // 非ssr情况下为 false,ssr 时候为true
      hydrating?: boolean
    ): Component {
      ....
    // 调用 mount 方法,渲染 DOM
    return mount.call(this, el, hydrating)
    
    
    • 判断是否有render选项, 如果没有render选项,则会把模板template编译成render函数, 然后调用mount方法(/src/platforms/web/runtime/index.js的$mount),渲染DOM
    • 调用/src/platforms/web/runtime/index.js下的$mount方法,返回调用mountComponent(this, el, hydrating)结果
    Vue.prototype.$mount = function (
      el?: string | Element,
      hydrating?: boolean
    ): Component {
      el = el && inBrowser ? query(el) : undefined
      return mountComponent(this, el, hydrating)
    }
    
    三、初始化静态成员
    • src/core/index.js
    • 调用initGlobalAPI(Vue)方法, 给Vue的构造函数, 添加静态方法
      • 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) {
      ...
      // 初始化 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
    
      ...
      // 初始化 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中混入了常用的实例成员
    import { initMixin } from './init'
    import { stateMixin } from './state'
    import { renderMixin } from './render'
    import { eventsMixin } from './events'
    import { lifecycleMixin } from './lifecycle'
    import { warn } from '../util/index'
    // 此处不用 class 的原因是因为方便后续给 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实例初始化
    export function initMixin (Vue: Class<Component>) {
      // 给 Vue 实例增加 _init() 方法
      // 合并 options / 初始化操作
      Vue.prototype._init = function (options?: Object) {
        ...
        // vm 的生命周期相关变量初始化
        // $children/$parent/$root/$refs
        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
    // /vue/src/core/instance/state.js
    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)
      }
    }
    

    initProps(vm, opts.props)接收了两个参数,一个是Vue实例,一个是Props属性,我们跳转到initProps函数中,首先给Vue实例定义了一个_Props对象, 并且把它存储到了常量里面

    const props = vm._props = {}
    
    注意

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

    initState
    • 在initMethods(vm, opts.methods)中,也是接收两个参数,Vue实例和选项中的methods,首先获取了选项中的Props,接着遍历methods所有属性,接着判断当前的环境是否是开发或者生产开发环境会判断methods是否是functicon
    • 接着判断methods方法的名称是否在Props对象中存在,存在就会发送一个警告,警告在属性在Props中已经存在,因为Props和methods最终都要注入到Vue实例上,不能出现同名
    • 下面继续判断key是否在Vue中存在,并且调用了isReserved(key),判断我们的key是否以_开头或$开头
    • 最后把methods注入到Vue实例上来,注入的时候会判断是否是function,如果不是返回noop,是的话把函数返回bind(methods[key], vm)
    initData(vm)
    • 当options中有data选项时,会调用initData(vm)
    • 当没有的时候此时会给vm初始化一个_data属性observe(vm._data = {}, true)然后调用observe函数,observe是响应式中的一个函数
    • 在initData中获取了options的data选项,判断了data选项是否是function,如果是调用getData(data,vm)
    • 接着获取data中的所有属性,同时获取了props,methods中所有的属性
    // src/core/instance/state.js
    const keys = Object.keys(data)
    const props = vm.$options.props
    const methods = vm.$options.methods
    

    最后做一个响应式处理

    observe(data, true)
    

    在_init函数的最后,又调用了$mount来挂载整个页面

    // src/core/instance/init.js
    
    if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
    

    总结

    • 在首次渲染之前,首先进行Vue初始化,初始化实例成员和静态成员
    • 当初始化结束之后,要调用Vue的构造函数new Vue(),在构造函数中调用了_init()方法,这个方法相当于我们整个Vue的入口
    • 在_init方法中,最终调用了mount,一共有两个mount,一共有两个mount,一共有两个mount,第一个定义在entry-runtime-with-compiler.js文件中,也就是我们的入口文件mount,这个mount,这个mount,这个mount()的核心作用是帮我们把模板编译成render函数,但它首先会判断一下当前是否传入了render选项,如果没有传入的话,它会去获取我们的template选项,如果template选项也没有的话,他会把el中的内容作为我们的模板,然后把模板编译成render函数,它是通过compileToFunctions()函数,帮我们把模板编译成render函数的,当把render函数编译好之后,它会把render函数存在我们的options.render中。
    • 接着会调用src/platforms/web/runtime/index.js文件中的mount方法,在这个中首先会重新获取el,因为如果是运行时版本的话,是不会走entryruntimewithcompiler.js这个入口中获取el,所以如果是运行时版本的话,我们会在runtime/index.jsmount方法,在这个中首先会重新获取el,因为如果是运行时版本的话,是不会走entry-runtime-with-compiler.js这个入口中获取el,所以如果是运行时版本的话,我们会在runtime/index.js的mount方法,在这个中首先会重新获取el,因为如果是运行时版本的话,是不会走entry−runtime−with−compiler.js这个入口中获取el,所以如果是运行时版本的话,我们会在runtime/index.js的mount()中重新获取el。
    • 接下来调用mountComponent(),这个方法在src/core/instance/lifecycle.js中定义的,在mountComponent()中,首先会判断render选项,如果没有render选项,但是我们传入了模板,并且当前是开发环境的话会发送一个警告,目的是如果我们当前使用运行时版本的Vue,而且我们没有传入render,但是传入了模版,告诉我们运行时版本不支持编译器。接下来会触发beforeMount这个生命周期中的钩子函数,也就是开始挂载之前。
    • 然后定义了updateComponent(),在这个函数中,调用vm._render和vm._update,vm._render的作用是生成虚拟DOM,vm._update的作用是将虚拟DOM转换成真实DOM,并且挂载到页面上
    • 创建Watcher对象,在创建Watcher时,传递了updateComponent这个函数,这个函数最终是在Watcher内部调用的。在Watcher内部会用了get方法,当Watcher创建完成之后,会触发生命周期中的mounted钩子函数,在get方法中,会调用updateComponent()
    • 挂载结束,最终返回Vue实例。

    起源地下载网 » Vue首次渲染过程

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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