# Vue源码学习(一)

  • 调试与打包
  • template和render同时存在
  • 平台无关与平台相关
  • Vue的构造函数
  • 源码报错的两个小问题
  • Vue初始化-静态成员
  • Vue的实例属性和方法

# 调试与打包

  • vue源码中的打包工具 Rollup
    • Vue.js 源码的打包工具使用的是 Rollup,比 Webpack 轻量
    • Webpack 把所有文件当做模块,Rollup 只处理 js 文件更适合在 Vue.js 这样的库中使用
    • Rollup 打包不会生成冗余的代码
  • 安装依赖后设置 sourcemap
"dev": "rollup -w -c scripts/config.js --sourcemap --environment TARGET:web- full-dev"
  • 执行npm run dev打包,发现dist目录下生成许多文件

此处参考:官方文档 (opens new window)

  • 推荐使用运行时版本,因为运行时版本相比完整版体积要小大约 30%

  • 基于 Vue-CLI 创建的项目默认使用的是 vue.runtime.esm.js

  • *.vue 文件中的模板是在构建时预编译的,最终打包后的结果不需要编译器,只需要运行

    时版本即可

# 通过看源码解决问题

const vm = new Vue({ 
    el: '#app', 
    template: '<h3>Hello template</h3>', 
    render (h) { 
        return h('h4', 'Hello render') 
    } 
})
// 同时存在template和render函数时页面输出 Hello render
  • el 不能是 body 或者 html 标签
  • 如果没有 render,把 template 转换成 render 函数
  • 如果有 render 方法,直接调用 mount 挂载 DOM
// 1. el 不能是 body 或者 html 
if (el === document.body || el === document.documentElement) { 
    process.env.NODE_ENV !== 'production' && warn( 
        `Do not mount Vue to <html> or <body> - mount to normal elements 
instead.` 
    )
    return this 
}
const options = this.$options 
if (!options.render) 
{ 
    // 2. 把 template/el 转换成 render 函数 
    …… 
}
// 3. 调用 mount 方法,挂载 DOM 
return mount.call(this, el, hydrating)

# 平台无关与平台相关

源码的src目录下有这样的结构

|--core
|--platforms
	  |--web
	  |--weex

其中core存放的就是平台无关代码,platforms中存放的就是平台相关代码

什么是平台无关性:

  • 平台无关性就是一种语言在计算机上的运行不受平台的约束,一次编译,到处执行 。

平台无关有两种:

  • 源代码级和目标代码级。而C和C++具有一定程度的源代码级平台无关,表明用C或C++写的应用程序不用修改只需重新编译就可以在不同平台上运行。

  • 而Java编译出来的是字节码,去到哪个平台都能用,只要有那个平台的JDK就可以运行,所以,Java程序的最大优势就是平台无关。

# Vue的构造函数

  • src/platform/web中有一些entry-文件,引用了 '/runtime/index'

  • src/platform/web/runtime/index.js 中引用了 'core/index'

  • src/core/index.js

    • 定义了 Vue 的静态方法
    • initGlobalAPI(Vue)
  • src/core/index.js 中引用了 './instance/index'

  • src/core/instance/index.js

    • 定义了 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() 方法,在 initMixin 中定义
  this._init(options)
}

export default Vue

# 源码报错的两个小问题

例如在src/core/global/index.js中会有报错

// 例如此处不支持泛型会报错  
Vue.observable = <T>(obj: T): T => {
    observe(obj)
    return obj
  }
// 而且这段代码之后的部分不会高亮显示

解决:文件 => 首选项 => 设置 => 右上角json格式

// 不检查 js 语法问题
"javascript.validate.enable": false

高亮显示:安装Babel JavaScript插件,但是之后的代码有些功能丢失了,不能跳转定义

# Vue初始化-静态成员

静态方法目录在:src/core/global-api

src/core/global-api/index.js:初始化 Vue 的静态方法

export function initGlobalAPI (Vue: GlobalAPI) {

  // src/core/global-api/index.js 
  // 初始化 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.
  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 对象,并给其扩展
  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)
}

上面所注册的全局API在官方文档中 (opens new window)

关于Vue.directive可参考这篇博客 (opens new window)

# Vue的实例属性和方法

定义实例属性和方法的目录在:src/core/instance

src/core/instance/index.js:定义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() 方法,在 initMixin 中定义
  this._init(options)
}

// 下面这些方法都是在 Vue 的原型上增加属性,即实例上也可以调用

// 注册 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
最后更新时间: 9/3/2021, 7:07:07 PM