Vue源码: 构造函数入口
栏目: JavaScript · 发布时间: 5年前
内容简介:编译器: 把template变异为Render函数。我们如果使用vue-loader, 那么*.vue文件模板会在构建时候预编译成JS, 所以打包完成的文件实际上不需要编译器的, 只需要引入运行时版本(体积小)即可。如果确实需要使用完整版只需要在打包工具中配置一个别名。
├── scripts ------------------------------- 构建相关的脚本/配置文件 │ ├── git-hooks ------------------------- 存放git钩子的目录 │ ├── alias.js -------------------------- 别名配置 │ ├── config.js ------------------------- 生成rollup配置的文件 │ ├── build.js -------------------------- 对 config.js 中所有的rollup配置进行构建 │ ├── ci.sh ----------------------------- 持续集成运行的脚本 │ ├── release.sh ------------------------ 用于自动发布新版本的脚本 ├── dist ---------------------------------- 构建后文件的输出目录 ├── examples ------------------------------ 存放一些使用Vue开发的应用案例 ├── flow ---------------------------------- 类型声明,使用开源项目 [Flow](https://flowtype.org/) ├── packages ------------------------------ 存放独立发布的包的目录 ├── test ---------------------------------- 包含所有测试文件 ├── src ----------------------------------- 源码 │ ├── compiler -------------------------- 编译器代码的存放目录,将 template 编译为 render 函数 │ ├── core ------------------------------ 存放通用的,与平台无关的代码 │ │ ├── observer ---------------------- 响应系统,包含数据观测的核心代码 │ │ ├── vdom -------------------------- 包含虚拟DOM创建(creation)和打补丁(patching)的代码 │ │ ├── instance ---------------------- 包含Vue构造函数设计相关的代码 │ │ ├── global-api -------------------- 包含给Vue构造函数挂载全局方法(静态方法)或属性的代码 │ │ ├── components -------------------- 包含抽象出来的通用组件 │ ├── server ---------------------------- 包含服务端渲染(server-side rendering)的相关代码 │ ├── platforms ------------------------- 包含平台特有的相关代码,不同平台的不同构建的入口文件也在这里 │ │ ├── web --------------------------- web平台 │ │ │ ├── entry-runtime.js ---------- 运行时构建的入口,不包含模板(template)到render函数的编译器,所以不支持 `template` 选项,我们使用vue默认导出的就是这个运行时的版本。大家使用的时候要注意 │ │ │ ├── entry-runtime-with-compiler.js -- 独立构建版本的入口,它在 entry-runtime 的基础上添加了模板(template)到render函数的编译器 │ │ │ ├── entry-compiler.js --------- vue-template-compiler 包的入口文件 │ │ │ ├── entry-server-renderer.js -- vue-server-renderer 包的入口文件 │ │ │ ├── entry-server-basic-renderer.js -- 输出 packages/vue-server-renderer/basic.js 文件 │ │ ├── weex -------------------------- 混合应用 │ ├── sfc ------------------------------- 包含单文件组件(.vue文件)的解析逻辑,用于vue-template-compiler包 │ ├── shared ---------------------------- 包含整个代码库通用的代码 ├── package.json -------------------------- 不解释 ├── yarn.lock ----------------------------- yarn 锁定文件 ├── .editorconfig ------------------------- 针对编辑器的编码风格配置文件 ├── .flowconfig --------------------------- flow 的配置文件 ├── .babelrc ------------------------------ babel 配置文件 ├── .eslintrc ----------------------------- eslint 配置文件 ├── .eslintignore ------------------------- eslint 忽略配置 ├── .gitignore ---------------------------- git 忽略配置 复制代码
Vue.js构建版本
完整版: 构建后文件包括编译器+运行时 编译器: 负责把模板字符串变异为JS的Render函数 运行时: 负责创建Vue.js实例, 渲染视图, 使用虚拟DOM算法重新渲染 UMD: 支持通过script标签在浏览器引入 CJS: 用来支持一些低版本打包工具, 因为它们package.json文件的main字段只包含运行时的CJS版本 ESM: 用来支持现代打包工具, 这些打包工具package.json的module字段只包含运行时候的ESM版本 复制代码
什么时候我们需要使用编译器?
编译器: 把template变异为Render函数。
// 用到了template就需要编译器 new Vue({ template: '<div></div>' }) // 如果本身就是Render函数不需要编译器 new Vue({ render (h) { return h('div', this.hi) } }) 复制代码
我们如果使用vue-loader, 那么*.vue文件模板会在构建时候预编译成JS, 所以打包完成的文件实际上不需要编译器的, 只需要引入运行时版本(体积小)即可。
如果确实需要使用完整版只需要在打包 工具 中配置一个别名。
// webpack resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js', } }, 复制代码
关于开发环境与生产环境
我们知道Vue有很多打包后的版本
它们都依赖于都process.env.NODE_ENV环境变量, 根据其值来决定选择什么模式。 所以我们可以在打包工具中配置这些环境变量。
在webpack中配置环境变量
var webpack = require('webpack'); module.exports = { ..., plugins: [ // 配置全局变量的插件 new webpack.DefinePlugin({ 'NODE_ENV': JSON.stringify('production') }) ] }; 复制代码
构造函数的入口
一步步找到Vue的构造函数入口。
执行npm run dev
通过查看package.json文件下的scripts命令。
"dev": "rollup -w -c scripts/config.js --environment TARGET:web-full-dev" 复制代码
scripts/config.js为打开的对应配置文件, process.env.TARGET为web-full-dev。 在scripts/config.js找到对应的配置对象
const builds = { // Runtime+compiler development build (Browser) '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 }, } 当然主要生成配置对象是这段代码 复制代码
function genConfig (name) { // opts为builds里面对应key的基础配置对象 const opts = builds[name] // config是真正要返回的配置对象 const config = { input: opts.entry, external: opts.external, plugins: [ flow(), alias(Object.assign({}, aliases, opts.alias)) ].concat(opts.plugins || []), output: { file: opts.dest, format: opts.format, banner: opts.banner, name: opts.moduleName || 'Vue' }, onwarn: (msg, warn) => { if (!/Circular/.test(msg)) { warn(msg) } } }
// built-in vars const vars = { WEEX : !!opts.weex, WEEX_VERSION : weexVersion, VERSION : version } // feature flags Object.keys(featureFlags).forEach(key => { vars[ process.env.${key}
] = featureFlags[key] }) // build-specific env // 根据不同的process.env.NODE_ENV加载不同的打包后版本 if (opts.env) { vars['process.env.NODE_ENV'] = JSON.stringify(opts.env) } config.plugins.push(replace(vars))
if (opts.transpile !== false) { config.plugins.push(buble()) }
Object.defineProperty(config, '_name', { enumerable: false, value: name })
return config }
if (process.env.TARGET) { module.exports = genConfig(process.env.TARGET) } else { exports.getBuild = genConfig exports.getAllBuilds = () => Object.keys(builds).map(genConfig) }
### 找到打包入口文件 根据配置对象的entry字段: 复制代码
entry: resolve('web/entry-runtime-with-compiler.js')
以及resolve函数 复制代码
const aliases = require('./alias') const resolve = p => { // web/ weex /server const base = p.split('/')[0] if (aliases[base]) { // 拼接完整的入口文件 return path.resolve(aliases[base], p.slice(base.length + 1)) } else { return path.resolve(__dirname, '../', p) } }
aliases.js文件 复制代码
const path = require('path')
const resolve = p => path.resolve(__dirname, '../', p)
module.exports = { vue: resolve('src/platforms/web/entry-runtime-with-compiler'), compiler: resolve('src/compiler'), core: resolve('src/core'), shared: resolve('src/shared'), web: resolve('src/platforms/web'), weex: resolve('src/platforms/weex'), server: resolve('src/server'), sfc: resolve('src/sfc') }
找到真正的入口文件为: vue-dev/src/platforms/web/entry-runtime-with-compiler.js。 在entry-runtime-with-compiler.js文件中发现 复制代码
import Vue from './runtime/index'
其实这里主要做的是挂载$mount()方法, 可以看我之前写的文章[mount挂载函数](https://juejin.im/post/5c8531995188251bbf2edf82)。 OK回到继续回到我们之前话题, 在vue-dev/src/platforms/web/runtime/index.js下发现这里还不是真正的Vue构造函数 复制代码
import Vue from './instance/index'
不过也马上接近了, 继续查找vue-dev/src/core/instance/index.js, 很明显这里才是真正的构造函数。 复制代码
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'
// Vue构造函数 function Vue (options) { // 提示必须使用new Vue() 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原型上挂载方法 initMixin(Vue) stateMixin(Vue) eventsMixin(Vue) lifecycleMixin(Vue) renderMixin(Vue)
export default Vue
### initMixin() 复制代码
export function initMixin (Vue: Class) { Vue.prototype._init = function (options?: Object) { // 缓存this const vm: Component = this // a uid vm._uid = uid++
// 这里只要是开启config.performance进行性能调试时候一些组件埋点 let startTag, endTag /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { startTag = `vue-perf-start:${vm._uid}` endTag = `vue-perf-end:${vm._uid}` mark(startTag) } // a flag to avoid this being observed // 标识一个对象是 Vue 实例, 避免再次被observed vm._isVue = true // merge options // options是new Vue(options)配置对象 // _isComponent是一个内部属性, 用于创建组件 if (options && options._isComponent) { // optimize internal component instantiation // since dynamic options merging is pretty slow, and none of the // internal component options needs special treatment. initInternalComponent(vm, options) } else { // 定义实例属性$options: 用于当前 Vue 实例的初始化选项 vm.$options = mergeOptions( resolveConstructorOptions(vm.constructor), options || {}, vm ) } /* istanbul ignore else */ if (process.env.NODE_ENV !== 'production') { initProxy(vm) } else { vm._renderProxy = vm } // expose real self // 定义一个内部属性_self vm._self = vm // 执行各种初始化操作 initLifecycle(vm) initEvents(vm) initRender(vm) callHook(vm, 'beforeCreate') initInjections(vm) // resolve injections before data/props initState(vm) initProvide(vm) // resolve provide after data/props callHook(vm, 'created') /* istanbul ignore if */ if (process.env.NODE_ENV !== 'production' && config.performance && mark) { vm._name = formatComponentName(vm, false) mark(endTag) measure(`vue ${vm._name} init`, startTag, endTag) } // 执行挂载操作 if (vm.$options.el) { vm.$mount(vm.$options.el) } 复制代码
} }
## 参考阅读 深入浅出vue.js复制代码
以上所述就是小编给大家介绍的《Vue源码: 构造函数入口》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Java构造函数与普通函数用法详解
- 构造函数、原型、原型链、继承
- Hashmap源码解析-构造函数
- JavaScript 构造函数的继承
- Swift学习之构造函数与析构函数
- Java类 静态代码块、构造代码块、构造函数初始化顺序
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Imperfect C++中文版
威尔逊 / 荣耀、刘未鹏 / 人民邮电出版社 / 2006-1 / 75.0
汇集实用的C++编程解决方案,C++虽然是一门非凡的语言,但并不完美。Matthew Wilson使用C++十年有余,其间发现C++存在一些固有的限制,需要一些颇具技术性的工作进行弥补。本书不仅指出了C++的缺失,更为你编写健壮、灵活、高效、可维护的代码提供了实用的技术和工具。Wilson向你展示了如何克服C++的复杂性,穿越C++庞大的范式阵列。夺回对代码的控制权,从而获得更理想的结果。一起来看看 《Imperfect C++中文版》 这本书的介绍吧!