最近老是感受對vue的一些用法和語句仍是不理解,因而決定擼一下源碼,用於加深本身對vue的理解,同時vue主要是經過rollup進行打包編譯,由於它相比webpack更加輕量,行了,廢話很少說了,開始了!javascript
如上圖所示,當咱們執行npm runbuild命令的時候,首先package.json會將其解析爲node build/build.js,執行這個目錄,咱們看看這個目錄是什麼!vue
代碼若是理解起來比較吃力,在文件代碼下面會有梳理!java
直接進入到build /build.js貼代碼!node
const fs = require('fs')
const path = require('path')
const zlib = require('zlib')
const rollup = require('rollup')
const uglify = require('uglify-js')
if (!fs.existsSync('dist')) { // 判斷是否存在dist文件夾,若是沒有則建立一個
fs.mkdirSync('dist') // 這也是爲何,當咱們執行完build命令後,會出現一個dist文件夾
}
let builds = require('./config').getAllBuilds() // 引入./config中的文件,而後執行這個文件下的getAllBuilds()方法
//process.argv得到附加的命令行參數 如: node app 127.0.0.1 7001 咱們將會獲得127.0.0.1
if (process.argv[2]) { // 主要針對build:ssr和week形式的
const filters = process.argv[2].split(',') // 經過逗號分隔成數組
builds = builds.filter(b => { // 過濾因此.output.file和._name包含filters內容的
return filters.some(f => b.output.file.indexOf(f) > -1 || b._name.indexOf(f) > -1) // 檢測是否又符合條件的,有則返回true沒有則是false
})
} else { // 說明這塊執行的就是單純的 npm run build
// filter out weex builds by default //但也對weex的進行過濾
builds = builds.filter(b => { // 過濾輸出文件中不包含weex的
return b.output.file.indexOf('weex') === -1
})
} // 綜上所述,主要是對builds中的值進行過濾操做
build(builds)
function build (builds) { // 對拿到的builds進行一個簡單的遍歷
let built = 0
const total = builds.length
const next = () => {
buildEntry(builds[built]).then(() => { // builds數組從0到最後一個元素執行buildEntry方法
built++
if (built < total) {
next()
}
}).catch(logError)
}
next()
}
function buildEntry (config) { // 真正開始經過rollup對其進行編譯
const output = config.output
const { file, banner } = output
const isProd = /min\.js$/.test(file) // 匹配min.js結尾的文件
return rollup.rollup(config)
.then(bundle => bundle.generate(output))
.then(({ code }) => {
if (isProd) {
var minified = (banner ? banner + '\n' : '') + uglify.minify(code, { // 判斷生成的js是否須要壓縮
output: {
ascii_only: true
},
compress: {
pure_funcs: ['makeMap']
}
}).code
return write(file, minified, true)
} else {
return write(file, code)
}
})
}
function write (dest, code, zip) {
return new Promise((resolve, reject) => {
function report (extra) { // 必要的時候,在文件中加入console.log
console.log(blue(path.relative(process.cwd(), dest)) + ' ' + getSize(code) + (extra || ''))
resolve()
}
fs.writeFile(dest, code, err => { // 寫文件操做
if (err) return reject(err)
if (zip) {
zlib.gzip(code, (err, zipped) => {
if (err) return reject(err)
report(' (gzipped: ' + getSize(zipped) + ')')
})
} else {
report()
}
})
})
}
function getSize (code) {
return (code.length / 1024).toFixed(2) + 'kb'
}
function logError (e) {
console.log(e)
}
function blue (str) {
return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m'
}
複製代碼
前兩行主要是引入了一些模塊,以及對dist文件的判斷,相信經過註釋,你們必定能看得懂。webpack
let builds = require('./config').getAllBuilds()web
下面咱們看一下這個builds究竟是什麼,首先咱們先看一下config文件夾下的代碼npm
JavaScript 示例:json
const path = require('path')
const buble = require('rollup-plugin-buble')
const alias = require('rollup-plugin-alias')
const cjs = require('rollup-plugin-commonjs')
const replace = require('rollup-plugin-replace')
const node = require('rollup-plugin-node-resolve')
const flow = require('rollup-plugin-flow-no-whitespace')
const version = process.env.VERSION || require('../package.json').version
const weexVersion = process.env.WEEX_VERSION || require('../packages/weex-vue-framework/package.json').version
// 對版本號的一個註釋
const banner =
'/*!\n' +
' * Vue.js v' + version + '\n' +
' * (c) 2014-' + new Date().getFullYear() + ' Evan You\n' +
' * Released under the MIT License.\n' +
' */'
const weexFactoryPlugin = {
intro () {
return 'module.exports = function weexFactory (exports, document) {'
},
outro () {
return '}'
}
}
const aliases = require('./alias') // alias是對文件真實路徑的一個映射
const resolve = p => {
const base = p.split('/')[0] // 獲取第一個/前的名字
if (aliases[base]) { // 判斷aliases中是否有這個名字,同時獲取它的映射路徑
return path.resolve(aliases[base], p.slice(base.length + 1)) // p.slice(base.length + 1) 爲 ‘/’ 後的名字
} else {
return path.resolve(__dirname, '../', p) // 即dist目錄下
}
}
// entry爲入口,對應rollup的input dest爲出口,對應rollup的output format爲格式 banner上面有過解釋,爲版本信息註釋
const builds = {
// Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify
'web-runtime-cjs': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.common.js'),
format: 'cjs',
banner
},
// Runtime+compiler CommonJS build (CommonJS)
'web-full-cjs': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.common.js'),
format: 'cjs',
alias: { he: './entity-decoder' },
banner
},
// Runtime only (ES Modules). Used by bundlers that support ES Modules,
// e.g. Rollup & Webpack 2
'web-runtime-esm': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.esm.js'),
format: 'es',
banner
},
// Runtime+compiler CommonJS build (ES Modules)
'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-only build (Browser)
'web-runtime-dev': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.js'),
format: 'umd',
env: 'development',
banner
},
// runtime-only production build (Browser)
'web-runtime-prod': {
entry: resolve('web/entry-runtime.js'),
dest: resolve('dist/vue.runtime.min.js'),
format: 'umd',
env: 'production',
banner
},
// 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
},
// Runtime+compiler production build (Browser)
'web-full-prod': {
entry: resolve('web/entry-runtime-with-compiler.js'),
dest: resolve('dist/vue.min.js'),
format: 'umd',
env: 'production',
alias: { he: './entity-decoder' },
banner
},
// Web compiler (CommonJS).
'web-compiler': {
entry: resolve('web/entry-compiler.js'),
dest: resolve('packages/vue-template-compiler/build.js'),
format: 'cjs',
external: Object.keys(require('../packages/vue-template-compiler/package.json').dependencies)
},
// Web compiler (UMD for in-browser use).
'web-compiler-browser': {
entry: resolve('web/entry-compiler.js'),
dest: resolve('packages/vue-template-compiler/browser.js'),
format: 'umd',
env: 'development',
moduleName: 'VueTemplateCompiler',
plugins: [node(), cjs()]
},
// Web server renderer (CommonJS).
'web-server-renderer': {
entry: resolve('web/entry-server-renderer.js'),
dest: resolve('packages/vue-server-renderer/build.js'),
format: 'cjs',
external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
},
'web-server-basic-renderer': {
entry: resolve('web/entry-server-basic-renderer.js'),
dest: resolve('packages/vue-server-renderer/basic.js'),
format: 'umd',
env: 'development',
moduleName: 'renderVueComponentToString',
plugins: [node(), cjs()]
},
'web-server-renderer-webpack-server-plugin': {
entry: resolve('server/webpack-plugin/server.js'),
dest: resolve('packages/vue-server-renderer/server-plugin.js'),
format: 'cjs',
external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
},
'web-server-renderer-webpack-client-plugin': {
entry: resolve('server/webpack-plugin/client.js'),
dest: resolve('packages/vue-server-renderer/client-plugin.js'),
format: 'cjs',
external: Object.keys(require('../packages/vue-server-renderer/package.json').dependencies)
},
// Weex runtime factory
'weex-factory': {
weex: true,
entry: resolve('weex/entry-runtime-factory.js'),
dest: resolve('packages/weex-vue-framework/factory.js'),
format: 'cjs',
plugins: [weexFactoryPlugin]
},
// Weex runtime framework (CommonJS).
'weex-framework': {
weex: true,
entry: resolve('weex/entry-framework.js'),
dest: resolve('packages/weex-vue-framework/index.js'),
format: 'cjs'
},
// Weex compiler (CommonJS). Used by Weex's Webpack loader.
'weex-compiler': {
weex: true,
entry: resolve('weex/entry-compiler.js'),
dest: resolve('packages/weex-template-compiler/build.js'),
format: 'cjs',
external: Object.keys(require('../packages/weex-template-compiler/package.json').dependencies)
}
}
function genConfig (name) { /// 主要是經過咱們上面的builds對應到rollup格式的一個轉換,如把entry轉換爲input
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
}
if (process.env.TARGET) { // 拿到用戶環境信息中的TARGET
module.exports = genConfig(process.env.TARGET)
} else {
exports.getBuild = genConfig
exports.getAllBuilds = () => Object.keys(builds).map(genConfig)
// 取到builds下的全部索引,而後遍歷執行genConfig方法
}
複製代碼
當咱們執行build文件時,在引入模塊以後,會在./config中拿到builds,builds是什麼呢?數組
在config文件下的尾部有這樣一段代碼weex
咱們能夠清晰的看到該在最後一行對builds拿到了因此的keys進行了一個遍歷,執行genConfig方法
經過上圖能夠看出來,builds是含有一個個文件信息的對象,至關因而對rollup參數的一個映射,其中entry爲入口,對應rollup的input,dest爲出口,對應rollup的output ,format爲格式,banner爲版本註釋
這個對象經過key傳給了genConfig方法,genConfig又是什麼?咱們看一下:
不難看出,genConfig方法就是一個將builds對象轉化爲rollup使用的格式的方法,就這樣含有rollup信息的格式,吐給了builds文件中的變量builds,也就是咱們最開始提到的。
接下來經過拿到process.argv[2],進行了一個過濾,過濾掉不須要編譯的文件,註釋說的已經夠詳細了,不想在過多解釋了。
接下來會執行build()方法,能夠看的出來,build方法是一個循環,循環執行buildEntry()方法
buildEntry則是真正的開始執行rollup,對返回的builds進行編譯,而後生成對應的文件。