Vue 源码
你想要的 vue 源码分析 · 02
vuejs 源码学习第二天 这里用到的 vue 版本是 vue 目录 编译相关,把模版解析成抽象语法树 核心代码核心代码:1、内置组件,2、全局 API ,3、vue 实例化 ,4、观察者 5、虚拟 DOM 6、工具函数 不同平台的支持分别打包成运行在 weex 和 web 上的 vue.js 服务端渲染服务端渲染的代码 .vue 文件解析把.vue 文件解析成一个 javascript 对象 共…
vuejs 源码学习第二天 这里用到的 vue 版本是 vue 目录 编译相关,把模版解析成抽象语法树 核心代码核心代码:1、内置组件,2、全局 API ,3、vue 实例化 ,4、观察者 5、虚拟 DOM 6、工具函数 不同平台的支持分别打包成运行在 weex 和 web 上的 vue.js 服务端渲染服务端渲染的代码 .vue 文件解析把.vue 文件解析成一个 javascript 对象 共…
这里用到的 vue 版本是
2.5.17-beta.0
vue/src/compiler编译相关,把模版解析成抽象语法树
vue/src/core核心代码核心代码:1、内置组件,2、全局 API ,3、vue 实例化 ,4、观察者 5、虚拟 DOM 6、工具函数
vue/src/paltforms不同平台的支持分别打包成运行在 weex 和 web 上的 vue.js
vue/src/server服务端渲染服务端渲染的代码
vue/src/sfc.vue 文件解析把.vue 文件解析成一个 javascript 对象
vue/src/shared共享代码定义一些服务端和浏览器端的 vue 的共享代码
mainnpm 包入口 import vue 通过 main 查找入口
module跟 main 类似
scripts定义很多脚步,这些脚本代表不同任务
vuejs 是用 rollup,rollup 与 webpack 的区别是:webpack 会把图片等都打包成 js,而 rollup 只是构建 js
vue/scripts/config.jsvue/scripts/build.js从配置文件读取配置通过命令行参数对配置进行过滤
if (process.env.TARGET) {
module.exports = genConfig(process.env.TARGET);
} else {
exports.getBuild = genConfig;
exports.getAllBuilds = () => Object.keys(builds).map(genConfig);
}
builds 是什么builds 是不同版本 vuejs 的配置 genConfig 拿到 key 建立一个支持 rollup 的打包的配置结构
const builds = {
'web-runtime-cjs': {
entry: resolve('web/entry-runtime.js'), // 建入口 js 地址
dest: resolve('dist/vue.runtime.common.js'), //构建后的 JS 文件地址
format: 'cjs', // format 是构建格式,其中 cjs 是 commonJS、es 是 esmodule,umd 是 UMD 规范
banner
}
...
}
resolve 是什么// resolve 是映射到了不同的构建工具,
// 不同的映射工具可导出不同格式的 js,有 AMD的和CMD等
const resolve = (p) => {
const base = p.split('/')[0];
if (aliases[base]) {
return path.resolve(aliases[base], p.slice(base.length + 1));
} else {
return path.resolve(__dirname, '../', p);
}
};
genConfig是什么// 生成rollup格式支持的配置
// 通过 object.keys(builds)拿到数组,genConfig 会构造出支持 rollup 打包的参数
function genConfig(name) {
const opts = builds[name];
const config = {
input: opts.entry,
external: opts.external,
plugins: [
replace({
__WEEX__: !!opts.weex,
__WEEX_VERSION__: weexVersion,
__VERSION__: version
}),
flow(),
buble(),
alias(Object.assign({}, aliases, opts.alias))
].concat(opts.plugins || []),
output: {
file: opts.dest,
format: opts.format,
banner: opts.banner,
name: opts.moduleName || 'Vue'
}
};
if (opts.env) {
config.plugins.push(
replace({
'process.env.NODE_ENV': JSON.stringify(opts.env)
})
);
}
Object.defineProperty(config, '_name', {
enumerable: false,
value: name
});
return config;
}
config 生成完就该 build 了
判断打包方式# 这个是scripts文件的打包入口
# 会根据参数判断是 web 打包方式还是 weex 打包方式
rollup -w -c scripts/config.js --environment TARGET:web-full-dev
rollup -w -c scripts/config.js --environment TARGET:weex-factory
// 这是判断哪种打包方式
let builds = require('./config').getAllBuilds();
if (process.argv[2]) {
const filters = process.argv[2].split(',');
builds = builds.filter((b) => {
return filters.some((f) => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1);
});
} else {
// filter out weex builds by default
builds = builds.filter((b) => {
return b.output.file.indexOf('weex') === -1;
});
}
进行编译// 每次编译都要计数
build(builds);
function build(builds) {
let built = 0;
const total = builds.length;
const next = () => {
buildEntry(builds[built])
.then(() => {
built++;
if (built < total) {
next();
}
})
.catch(logError);
};
next();
}
buildEntry// 获取 config 作为 rollup 编译的参数
function buildEntry(config) {
const output = config.output;
const { file, banner } = output;
const isProd = /min\.js$/.test(file);
return (
rollup
.rollup(config)
// 产生 output
.then((bundle) => bundle.generate(output))
.then(({ code }) => {
// 如果 isProd 为压缩的,执行 uglify 压缩
if (isProd) {
var minified =
(banner ? banner + '\n' : '') +
uglify.minify(code, {
output: {
ascii_only: true
},
compress: {
pure_funcs: ['makeMap']
}
}).code;
// write 是为了在编译的时候生成日志
return write(file, minified, true);
} else {
// 否则直接 wrtie
// write 是为了在编译的时候生成日志
return write(file, code);
}
})
);
}
是在编译阶段把.vue 文件编译成 js,只运行 vue.js 代码
如果在 vue 对象里面使用了 template,那就要实时编译,只写 render,可不用这种,.vue 的 template 是都通过 vue-loader 转成成 render 函数,所以跟这个没关系。
以 Runtime + Compiler 方式为例
进入src/platforms/web/entry-runtime-with-compiler.js导入import Vue from './runtime/index'再导入import Vue from 'core/index'导入的同时,也执行了 initGlobalAPI(Vue)
export function initGlobalAPI(Vue: GlobalAPI) {
// config
const configDef = {};
configDef.get = () => config;
if (process.env.NODE_ENV !== 'production') {
configDef.set = () => {
warn('Do not replace the Vue.config object, set individual fields instead.');
};
}
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
};
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);
});
// 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;
extend(Vue.options.components, builtInComponents);
// 挂载了很多静态的方法
initUse(Vue);
initMixin(Vue);
initExtend(Vue);
initAssetRegisters(Vue);
}
import Vue from './instance/index'// 最终Vue是 ES5 的 Function 实例
// 为什么不用 ES6 的 class 方法去构造 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');
}
this._init(options);
}
// 定义了很多 mixin 的原型方法对vue 对象进行扩展
initMixin(Vue);
stateMixin(Vue);
eventsMixin(Vue);
lifecycleMixin(Vue);
renderMixin(Vue);
暂无评论
确认操作
请再次确认编辑图片
待上传图片 (剩余 0 张)
项目详情
确认评论邮箱
邀请制评论