乐趣区

关于vue.js:Vue源码解析入口解析

Vue 入口文件查找

  • 查看 dist/vue.js 的构建过程
  • 当运行 npm run dev 时指定了配置文件,从配置文件 script/config.js 动手
  • web-full-dev(web 蕴含编译器和运行时的开发版)
  • script/config.js 基于 node 的模块

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

配置文件 config.js

...
// 判断是否有环境变量 target
if (process.env.TARGET) {module.exports = genConfig(process.env.TARGET)
} else {
  exports.getBuild = genConfig
  exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
}

genrator config 生成配置文件

function genConfig (name) {const opts = builds[name]
  ...
  return config
}

咱们传入的环境变量指向 build 中的 带编译器的 开发版本

const builds = {
  'web-full-dev': {

    // resolve 把前面的门路转化为绝对路径
    // 理论获取的是 src/platforms/web/entry-runtime-with-compiler.js
    // resolve 办法会找到下面的门路,有别名操作

    entry: resolve('web/entry-runtime-with-compiler.js'), 

    dest: resolve('dist/vue.js'),
    format: 'umd',
    env: 'development',
    alias: {he: './entity-decoder'},

    // 打包出的文件的文件头 banner:banner 函数
    banner 
  }
}

问题:如果同时设置 template 和 render 会执行谁?

const vm = new Vue({
  el:"#app",
  template:"<h3>Hello Template</h3>",
  render(h) {return h('h4',"Hello Render")
  }
})

解析 entry 所对应的入口文件,并答复下面的问题

  • 从 入口文件中,src/platforms/web/entry-runtime-with-compiler.js

    • el 是 dom 元素
    • el 不能是 body 或 html 标签
    • 如果没有 render,把 template 转化成 render 函数
    • 如果有 render,间接调用 mount 挂载 DOM
  • 断点调试 $mount 办法

    • 留神 npm run build 时并未开启 sourcemap,如果想调试源码须要
    • 给 vue.js 增加 //# sourceMappingURL=vue.js.map,可调试 vue.js 源码 或批改配置文件增加 -m

  • 因而 以后入口文件文件的作用就是 重写原型 $mount 办法,对 template 模板进行解决,而后导出 vue 实例(带编译器的版本)
  • 留神 与 entry-runtime.js 不同,没有解决 tempalte 代码,间接返回了 vue 实例(运行时版本)

问题:Vue 的构造函数在哪?Vue 实例的成员 /Vue 的动态成员从哪里来的?

从入口文件反推,找到引入的中央

...
// # src/platforms/web/entry-runtime-with-compiler.js

import Vue from './runtime/index' // Vue 引入的中央
...
export default Vue

解析 src/platforms/web/runtime/index.js

  • 此脚本次要是 跟 web 平台相干的,对 Vue 构造函数进行解决的脚本
  • 给 Vue 注册平台相干指令,给 Vue 原型上挂载了$mount__patch__, 导出了 Vue 实例
  • 然而仍然没有看到 Vue 构造函数!

    // # platforms/web/runtime/index.js
    
    import Vue from 'core/index'
    ...
    export default Vue

解析 src/core/index.js

  • 持续查找下面引入的 Vue
  • core 下的代码是与平台无关的
  • initGlobalAPI(Vue) 给 Vue 构造函数增加静态方法
  • 然而仍然没有看到 Vue 构造函数!
import Vue from './instance/index'
...
initGalobalAPI(Vue)
...

export default vue

解析 src/core/instance/index.js

  • 与平台无关的. 终于有 Vue 了
  • 此处不必 class 而用构造函数,起因是因为不便后续给 Vue 原型上挂成员办法
  • 对 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')
  }
  // 在 initMixin 中有这个办法
  this._init(options)
}
...

总结:

四个导出 Vue 的模块

  • src/platforms/web/entry-runtime-with-compiler.js

    • web 平台相干的入口
    • 重写了平台相干的 $mount()办法
    • 注册了 Vue.compile()办法,传递一个 HTML 字符串返回 render 函数
  • src/platforms/web/runtime/index.js

    • web 平台相干
    • 注册和平台相干的全局指令:v-model、v-show
    • 注册和平台相干的全局组件:v-transition、v-transition-group
    • 全局办法:

      • _patch_: 把虚构 DOM 转换成实在 DOM
      • $mount: 挂载办法
  • src/core/index.js

    • 与平台无关
    • 设置了 Vue 的静态方法,initGlobalAPI(Vue)
  • src/core/instance/index.js

    • 与平台无关
    • 定义了构造函数,调用了 this._init(options)办法
    • 给 Vue 中混入了罕用的实例成员
退出移动版