资讯专栏INFORMATION COLUMN

Vue源码学习(二)——从宏观看Vue

AndroidTraveler / 1229人阅读

摘要:上一篇文章我们写到从入口文件一步步找到的构造函数,现在我们要去看看实例化经历的过程的构造函数我们知道的构造函数在中不明白的可以去看上一篇文章源码学习笔记一。

上一篇文章我们写到从入口文件一步步找到Vue的构造函数,现在我们要去看看Vue实例化经历的过程
Vue的构造函数

我们知道Vue的构造函数在src/core/instance/index.js中,不明白的可以去看上一篇文章 Vue源码学习笔记一。那我们关注一下Vue的构造函数的内容:

// src/core/instance/index.js

import { initMixin } from "./init"

// Vue的构造函数
function Vue (options) {
  //... 验证环境
  this._init(options)
}

// 在Vue原型上绑定实例方法
initMixin(Vue)  // init
stateMixin(Vue)  // $set $delete $watch
eventsMixin(Vue)  // $on $once $off $emit
lifecycleMixin(Vue)  // _update $forceUpdate $destroy
renderMixin(Vue)  // $nextTick _render
添加Vue属性和方法

这边我们可以看到Vue的构造函数中执行了init方法,从下方得知init是在srccoreinstanceinit.js中导出的initMixin函数中定义的

initMixin

vmthis ,同时为实例添加一个唯一的uid vm._isVue = true 监听对象变化时用于过滤vm,因为Vue的实例是不需要监听变化的。

// src/core/instance/init.js
Vue.prototype._init = function (options?: Object) {
 const vm: Component = this

  // 当前实例添加了一个唯一的uid
  vm._uid = uid++   

  // ...

  // 监听对象变化时用于过滤vm
  vm._isVue = true
  
  //...
  }

参数处理,根据我们的小栗子,我们的options处理直接进入了else,然后对参数进行合并,这里是对vue extend的参数需要进行合并处理,我们这里resolveConstructorOptions 返回的即是constructor.options本身

生命周期相关变量初始化 initLifecycle(vm)

 // srccoreinstancelifecycle.js
 
 // 为组件挂载相应属性,并初始化
  vm.$parent = parent
  vm.$root = parent ? parent.$root : vm

  vm.$children = []
  vm.$refs = {}

  vm._watcher = null
  vm._inactive = null
  vm._directInactive = false
  vm._isMounted = false
  vm._isDestroyed = false
  vm._isBeingDestroyed = false

4.vm 事件监听初始化 initEvents()

// src/core/instance/events.js
export function initEvents (vm: Component) {
  // 创建事件对象,用于存储事件
  vm._events = Object.create(null)
  // 系统事件标识位
  vm._hasHookEvent = false
  
  // init parent attached events npm 
  // 将父组件模板中注册的事件放到当前组件实例的listeners
  const listeners = vm.$options._parentListeners
  if (listeners) {
    updateComponentListeners(vm, listeners)
  }
}
  initEvents(vm)
  initRender(vm)
  callHook(vm, "beforeCreate")
  initInjections(vm)  

  //  vm状态初始化,prop/data/computed/method/watch都在这里初始化完成,vue实例create的关键
  initState(vm)
  initProvide(vm)  
  callHook(vm, "created")
stateMixin
Vue实例方法--数据,该文件对应的是Vue的数据的处理,首先对$data进行挂载,然后设置数据$set、删除数据$delete、观测数据$watch方法挂载
// stateMixin(Vue)    src/core/instance/state.js
export function stateMixin (Vue: Class) {
  // data
  const dataDef = {}
  dataDef.get = function () { return this._data }
  // prop
  const propsDef = {}
  propsDef.get = function () { return this._props }

  // ...

  // 定义$data & prop属性
  Object.defineProperty(Vue.prototype, "$data", dataDef)
  Object.defineProperty(Vue.prototype, "$props", propsDef)

  // 原型链添加函数set 和 delete
  Vue.prototype.$set = set
  Vue.prototype.$delete = del

  // 原型链添加函数$watch
  Vue.prototype.$watch = function (){
    // ...
  }
}
eventsMixin
Vue实例方法--事件,该文件主要挂载Vue实例方法的事件,监听事件on once、移除事件off、触发事件emit的挂载
// eventsMixin(Vue)    src/core/instance/events.js
export function eventsMixin (Vue: Class) {
  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实例方法--生命周期,,该文件主要挂载Vue实例方法中的生命周期方法,重新渲染$forceUpdate()、销毁实例$destroy()
// 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 () {}
renderMixin
文件主要挂载Vue实例方法中的dom更新回调$nextTick及一些其他的render函数,后续我们再的深挖一下
// 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(){}
全局API
上面部分,我们对Vue的构造函数,在src/core/instance/index.js文件中的作用进行了大体的了解,当然这并没有结束,依据我们Vue源码学习笔记一中提到的,我们追溯到上一级src/core/index.js
// src/core/index.js

import Vue from "./instance/index"
import { initGlobalAPI } from "./global-api/index"

import { isServerRendering } from "core/util/env"
import { FunctionalRenderContext } from "core/vdom/create-functional-component"

// 初始化全局变量
initGlobalAPI(Vue)

// 为vue原型定义属性 isServer  判断是否为服务端渲染
Object.defineProperty(Vue.prototype, "$isServer", {
  get: isServerRendering
})

// 为vue原型定义属性 ssrContext
Object.defineProperty(Vue.prototype, "$ssrContext", {
  get () {
    /* istanbul ignore next */
    return this.$vnode && this.$vnode.ssrContext
  }
})

Object.defineProperty(Vue, "FunctionalRenderContext", {
  value: FunctionalRenderContext
})

Vue.version = "__VERSION__"

export default Vue
initGlobalAPI(Vue)
在Vue 构造函数上挂载静态属性和方法即全局API
// src/core/global-api/index.js

export function initGlobalAPI(Vue: GlobalAPI) {
  const configDef = {}
  configDef.get = () => config
  // ...
  Object.defineProperty(Vue, "config", configDef)
  
  Vue.util = { // Vue.util
    warn,
    extend,
    mergeOptions,
    defineReactive
  }

  Vue.set = set
  Vue.delete = del
  Vue.nextTick = nextTick

  Vue.options = Object.create(null)
  ASSET_TYPES.forEach(type => {
    Vue.options[type + "s"] = Object.create(null)
  })

  Vue.options._base = Vue

  extend(Vue.options.components, builtInComponents)
  
  initUse(Vue)   // Vue.use

  initMixin(Vue)  // Vue.mixin

  initExtend(Vue) // Vue.extend
  
  initAssetRegisters(Vue) // Vue.component Vue.directive Vue.filter
}
内置组件&命令
在追溯到上一级,在文件src/platforms/web/runtime/index.js,该文件注册了一些 Vue内置的组件:包裹动态组件KeepAlive、元素过渡效果Transition、多个元素过渡TransitionGroup
// src/platforms/web/runtime/index.js 执行后

// 安装平台特定的utils
Vue.config.isUnknownElement = isUnknownElement
Vue.config.isReservedTag = isReservedTag
Vue.config.getTagNamespace = getTagNamespace
Vue.config.mustUseProp = mustUseProp
// 安装平台特定的 指令 和 组件
Vue.options = {
    components: {
        KeepAlive,
        Transition,
        TransitionGroup
    },
    directives: {
        model,
        show
    },
    filters: {},
    _base: Vue
}
Vue.prototype.__patch__
Vue.prototype.$mount
compiler编译器添加
再上一级为src/platforms/web/entry-runtime-with-compiler.js,该文件对原来的Vue.prototype.$mount进行覆盖定义,并且在Vue上挂载了 compile。给Vue的 $mount 方法添加 compiler 编译器,支持 template。
// src/platforms/web/entry-runtime-with-compiler.js
const mount = Vue.prototype.$mount

// ...
 
Vue.prototype.$mount = function (){

//... 覆盖 Vue.prototype.$mount
}

// ...

//在 Vue 上挂载 compile 
//compileToFunctions 函数的作用,就是将模板 template 编译为render函数。
Vue.compile = compileToFunctions
总结

至此的话我们从宏观上过了一下从我们一层层找到vue到一层层往外看到对Vue的添加属性方法等,我们有了一个整体的概念

src/core/instance/index.js vue的构造函数,添加Vue属性和方法

src/core/index.js 全局API的挂载

src/platforms/web/runtime/index.js 主要是添加web平台特有的配置、组件和指令

web/entry-runtime-with-compiler.js 给Vue的 $mount 方法添加 compiler 编译器,支持 template

scripts/config.js 编译入口文件

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/99898.html

相关文章

  • Vue源码学习(一)——追根究底Vue

    摘要:其中执行命令是我们当前所关注的,对于项目的文件结构,我们需要去找到项目的启动的入口文件,接下来我们去一步一步的找到分析命令是一个模块打包器。 打算开始学习vue的源码开始,我开始 serach 关键词:vue 源码,可是发现很多都不是我想要看到的东西,所以打算记录下来,学习的记录和日后分享。我在想这个文章的名字时,手把手系列?十分钟系列?小白到大佬系列?都不是,只是单纯的学习笔记 文件...

    Harriet666 评论0 收藏0
  • Vue2.0—仿知乎日报总结

    摘要:一个基于全家桶开发的仿知乎日报单页应用项目地址源码地址项目在线地址在线地址模式下推荐使用移动端模式浏览去观看如果觉得做得还不错或者项目源码对您有帮助希望您小抬右手到右上角点一个您的支持是作者长期更新维护的动力项目起源从二月份开始学习学习了 Vue-News 一个基于vue全家桶开发的仿知乎日报单页应用 项目github地址:源码地址 项目在线地址:在线地址 (PC模式下推荐使用chro...

    lentoo 评论0 收藏0
  • vue+vant同一项目实现豆瓣电影加网易云音乐webapp-最佳练手项目

    摘要:很多小伙伴练手都会去仿写豆瓣电影或者网易云音乐,所以,这回干脆把两个集合起来,具体长什么样,请各位看预览或下面图片啦。怎么折腾法呢比如豆瓣部分我使用了,网易云部分则没有,网易的组件则是自己造的轮子,当然在实际开发中肯定不会这么搞啦。。 很多小伙伴练手都会去仿写豆瓣电影或者网易云音乐,所以,这回干脆把两个集合起来,具体长什么样,请各位看预览或下面图片啦。 目的: 做这个项目最主要的目的是...

    suosuopuo 评论0 收藏0
  • 听飞狐聊JavaScript设计模式系列13

    摘要:介一回聊状态模式,官方描述允许一个对象在其内部状态改变时改变它的行为。有限状态机有限状态机是一个非常有用的模型,可以模拟世界上大部分事物。这个是官方说法,简单说,她有三个特征,状态总数是有限的。,任一时刻,只处在一种状态之中。 本回内容介绍 上一回聊了聊组合模式(Composite),用组合模式模拟了个图片库,聊了递归。介一回聊状态模式(State),官方描述允许一个对象在其内部状态改...

    linkin 评论0 收藏0
  • Vue原理】Compile - 源码版 之 Parse 主要流程

    写文章不容易,点个赞呗兄弟 专注 Vue 源码分享,文章分为白话版和 源码版,白话版助于理解工作原理,源码版助于了解内部详情,让我们一起学习吧研究基于 Vue版本 【2.5.17】 如果你觉得排版难看,请点击 下面链接 或者 拉到 下面关注公众号也可以吧 【Vue原理】Compile - 源码版 之 Parse 主要流程 本文难度较繁琐,需要耐心观看,如果你对 compile 源码暂时...

    Forest10 评论0 收藏0

发表评论

0条评论

AndroidTraveler

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<