Skip to content

Latest commit

 

History

History
122 lines (96 loc) · 5.07 KB

从Vue源码打包开始讲起.md

File metadata and controls

122 lines (96 loc) · 5.07 KB

从打包开始讲起

​ 如果我们通过script标签使用Vue时,我们可以使用以下两种方式引入Vue

<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 生产环境版本,优化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>

​ 我们来看下这些代码是怎么生成的,在Vue源码的package.jsonscript我们可以看到各种命令。对于开发环境,诸多的命令都是指向scripts/config.js;对生产环境,命令指向scripts/build.js,但是build.js也会引入config.js中的配置,所以我们来看下这个build.js

scripts/config.js主要作用之一是,根据打包命令不同参数,确定rollup打包入口、出口文件以及其他参数。当我们运行命令npm run dev时,执行的内容实际是rollup命令,通过命令将环境变量的TARGET设置为web-full-dev

"scripts": {
    "dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev",
}

​ 在script/config.js中,有这样一块定义和逻辑

const builds = {
    // ...
    'web-full-dev': {
        entry: resolve('web/entry-runtime-with-compiler.js'),
        dest: resolve('dist/vue.js'),
        format: 'umd',
        env: 'development',
        alias: { he: './entity-decoder' },
        banner
  	},
}
// 根据builds来生成rollup配置
function genConfig (name) {}

// 获取到target,这里是web-full-dev
if (process.env.TARGET) {
  module.exports = genConfig(process.env.TARGET)
}

这里进入web-full-dev的配置,如果是npm run build通过这个配置打包出来的文件也是dist/vue.js,所以我们以该配置继续进行分析,他的入口文件指向web/entry-runtime-with-compiler.js。主要内容如下:

import Vue from './runtime/index'
Vue.prototype.$mount = function () { /** do something */ }
Vue.compile = compileToFunctions
export default Vue

​ 可以看出,这里是引入Vue,并在原型链挂载$mount方法。当然Vue的定义还是来自于./runtime/index

// .runtime/index
import Vue from 'core/index'
Vue.prototype.__patch__ = inBrowser ? patch : noop
// public mount method
Vue.prototype.$mount = function (){ /** do something */ }
export default Vue

​ 这里我们还是看不出来Vue是什么,在这里也是对Vue进行了原型链的增强,挂载了__patch__$mount方法。在上面代码中我们也进行了$mount方法的挂载,覆盖了此处的$mount。从注释可以看到,这里使用的是公共的mount方法,在其它版本的Vue中,例如web/entry-runtime.js就没有进行方法重写,使用默认公共mount方法。继续溯源

// core/index.js
import Vue from './instance/index'
export default Vue	

// 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

​ 从core/index我们直接继续分析到core/instance/index.js中,可以看到Vue是一个函数,准确来说是一个构造函数,只能通过new进行调用。通过下面的Mixin增强构造函数特性,这里面也就涉及到Vue的各种特性实现。有关响应式原理的部分在之前的文章中有介绍,后续将一边复习一边介绍关于组件化相关的知识。

​ 构造函数中只执行了this._init方法,这个方法通过下面的initMixin中定义。先不急着查看定义,我们先看看Vue这个构造函数。现在我们大部分时间通过脚手架的方式进行项目搭建、业务编写。在早些时间,我还在大学使用Jquery写页面时,我们也引入了Vue,那时在单页中进行引用,如下。

<div id="new_app">
</div>
<script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
<script>
var newApp= new Vue({
  el: '#new_app',
  // other options
})
</script>

​ 我们先在DOM中声明一个id = new_app的元素,在以选择器的方式以el字段传入Vue构造函数。在#new_app内写模板语法,构造函数中传入更多的参数,这样就能够像脚手架搭建之后一样进行开发。

​ 现在我们再看下构造函数中的_init方法做了什么。

Vue.prototype._init = function (options?: Object) {
  // important but ignore
  if (vm.$options.el) {
    vm.$mount(vm.$options.el)
  }
}

​ 这里的vm.$options.el是我们传入的el,传入的参数已经通过一些处理挂载到了vm.$options上。这里回到了我们前面多次看到的$mount函数,在$mount函数在做什么中我们继续探索。