vue使用过程中可以采用以下两种方式
- 在vue脚手架中直接使用,参考文档:
https://cn.vuejs.org/v2/guide/installation.html
- 或者在html文件的头部通过静态文件的方式引入:
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
那么问题来了,使用的或者引入的到底是什么?
答:引入的是已经打包好的vue.js文件,通过rollup构建打包所得。
一、构建入口
在vue源码的package.json文件中:
"scripts": {
// ...
"build": "node scripts/build.js",
"build:ssr": "npm run build -- web-runtime-cjs,web-server-renderer",
"build:weex": "npm run build -- weex",
// ...
},
通过执行npm run build的时候,会进行scripts/build.js文件的执行,npm run build:ssr和npm run build:weex的时候,将ssr和weex作为参数传入,按照参数构建出不一样的vue.js打包文件。
二、构建函数的执行(核心)
scripts/build.js:
function build (builds) {
let built = 0
const total = builds.length
const next = () => {
buildEntry(builds[built]).then(() => {
built++
if (built < total) {
next()
}
}).catch(logError)
}
next()
}
获取当前total=builds.length,可以使得当前total数量的构建都是一个成功后才进行下一个构建,依次有序的进行。buildEntry(builds[built])是真实的rollup构建方法,只有在.then(成功后)并且当前构建索引built还小于总构建数total才再次调用自己next进行下一次的构建。builds是哪儿来的?
三、构建元素的产生
在scripts/build.js文件中有builds的来源
let builds = require('./config').getAllBuilds()
实际是执行scripts/config.js的getAllBuilds方法,即Object.keys(builds).map(genConfig)
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
Object.keys(builds)获取到builds的key值,并通过genConfig方法的将描述式的构建元素重新构建成rollup真正需要的元素。 builds数组如下:
const builds = {
// ...
// Runtime+compiler ES modules build (for bundlers)
'web-full-esm': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.esm.js'),
format: 'es',
alias: { he: './entity-decoder' },
banner
},
// Runtime+compiler ES modules build (for direct import in browser)
'web-full-esm-browser-dev': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.esm.browser.js'),
format: 'es',
transpile: false,
env: 'development',
alias: { he: './entity-decoder' },
banner
},
// Runtime+compiler ES modules build (for direct import in browser)
'web-full-esm-browser-prod': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.esm.browser.min.js'),
format: 'es',
transpile: false,
env: 'production',
alias: { he: './entity-decoder' },
banner
},
// ...
}
genConfig数组如下:
function genConfig (name) {
const opts = builds[name]
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)
}
}
}
// ...
return config
}
四、构建元素的真实路径
我们看到构建元素中有entry: resolve('web/entry-runtime-with-compiler.js')和dest:resolve('dist/vue.esm.js'),这不是真实路径,真实路径由resolve方法获取:
const aliases = require('./alias')
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)
}
}
其中用到的alias在文件scripts/alias.js中
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')
}
入口web/entry-runtime-with-compiler.js'对应的真实路径就是path.resolve(aliases[base], p.slice(base.length + 1)),即src/platforms/web/entry-runtime-with-compiler。这个是带有compiler的版本,主要目的是可以对带有template参数的进行编译处理。
五、构建数组的过滤
最终的builds还会进行再次的过滤
// filter builds via command line arg
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
})
}
filter builds via command line arg(通过命令行参数来过滤构建数组)。如果,我们通过npm run build:ssr,那么获取到的命令行参数就是ssr,就会只保留输出文件名b.output.file或者当前文件b._name中包含ssr的构建。如果,没有传递参数,就会执行else逻辑,将包含weex的进行过滤。
小结:在构建过程中,将描述式的构建数组转换成rollup可以执行的数组,并通过控制台的参数对当前数组进行过滤,最后通过递归的方式进行构建队列的有序执行。