Rollup 基礎知識(2)

Design-Build.jpg

rollupjs.jpg
我們來看一看 vue 是使用了 Rollup 進行打包專案的。通過 npm 建立一個專案,然後建立 script.js 或者 index.js 檔案,來作為可執行的腳步檔案,首先我們需要引入所需要庫。
今天我們就 vue 打包的原始碼分析來看一看 rollup 在 vue 中應用。

th (1).jpeg
const fs = require('fs') const path = require('path') const rollup = require('rollup') const terser = require('terser')
fs 和 path 是 nodejs 提供有關檔案操作和路徑解析的包, rollup 和 terser 是用於構建打包專案庫,當然這裡 rollup 使我們今天主角。如果不存在 dist 資料夾我們就建立一個。
if (!fs.existsSync('dist')) { fs.mkdirSync('dist') }
接下來讀取 config 這個配置檔案,
let builds = require('./config').getAllBuilds()
if (process.env.TARGET) { module.exports = genConfig(process.env.TARGET) } else { exports.getBuild = genConfig //通常我們會呼叫這個 exports.getAllBuilds = () => Object.keys(builds).map(genConfig) }
這裡輸出 getAllBuilds 是返回一個方法,方法Object.keys 獲取物件的屬性集合,
var foo = {name:'koo',age:23} undefined Object.keys(foo) (2) ["name", "age"]
Object.keys(foo).map(function(item){ console.log(foo[item])}) VM493:1 koo VM493:1 23
用 genConfig 為每個要打包module輸出配置
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) } } } // 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 // 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 }
這裡主要是返回一個 rollup 在構建專案所用的配置檔案,簡單地解釋一下 config 各個屬性,
-
input 輸入檔案
-
plugins 外掛,vue 開始使用 flow 來作為自己型別系統的,alias 方法
-i, --input 要打包的檔案(必須)
-o, --output.file 輸出的檔案 (如果沒有這個引數,則直接輸出到控制檯)
-f, --output.format [es] 輸出的檔案型別 (amd, cjs, es, iife, umd)
-e, --external 將模組ID的逗號分隔列表排除
-g, --globals 以
module ID:Global
鍵值對的形式,用逗號分隔開任何定義在這裡模組ID定義新增到外部依賴
-n, --name 生成UMD模組的名字
-m, --sourcemap 生成 sourcemap (
-m inline
for inline map)--amd.id AMD模組的ID,預設是個匿名函式
--amd.define 使用Function來代替define
--no-strict 在生成的包中省略"use strict";
--no-conflict 對於UMD模組來說,給全域性變數生成一個無衝突的方法
--intro 在打包好的檔案的塊的內部(wrapper內部)的最頂部插入一段內容
--outro 在打包好的檔案的塊的內部(wrapper內部)的最底部插入一段內容
--banner 在打包好的檔案的塊的外部(wrapper外部)的最頂部插入一段內容
--footer 在打包好的檔案的塊的外部(wrapper外部)的最底部插入一段內容
--interop 包含公共的模組(這個選項是預設新增的
-
用
Object.defineProperty
將 name 作為 _name 屬性寫入到 config 物件中並且為不可遍歷。
根據 name 獲取 opts, 就是 web-runtime-cjs-dev
屬性值,opts 物件提供壓縮 web-runtime-cjs-dev
基本資訊。我們可以將每個屬性和上面函式進行對應。
const builds = { // Runtime only (CommonJS). Used by bundlers e.g. Webpack & Browserify 'web-runtime-cjs-dev': { entry: resolve('web/entry-runtime.js'), dest: resolve('dist/zi.runtime.common.dev.js'), format: 'cjs', env: 'development', banner } }
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 檔案
const path = require('path') const resolve = p => path.resolve(__dirname, '../', p) module.exports = { web: resolve('src/platforms/web'), }
build 函式接受 builds 作為引數通過回撥中再次呼叫自己來完成非同步執行完所有的構建任務。
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 方法,我們這裡呼叫 rollup 成功後構建專案程式碼通過 primise 來獲取,然後就可以對處理好的程式碼進行一些寫入附加資訊等操作。
function buildEntry(config) { const output = config.output const { file, banner } = output const isProd = /(min|prod)\.js$/.test(file) return rollup.rollup(config) .then(bundle => bundle.generate(output)) .then(({ output: [{ code }] }) => { if (isProd) { const minified = (banner ? banner + '\n' : '') + terser.minify(code, { toplevel: true, output: { ascii_only: true }, compress: { pure_funcs: ['makeMap'] } }).code return write(file, minified, true) } else { return write(file, code) } }) }