https://github.com/neutrinojs...css
git clone https://github.com/neutrinojs/webpack-chain.git
須要先預習 下列方法html
ES6 Set
結構ES6 Map
結構ES6 Class
類繼承includes reduce map forEach
Object.keys Object.assign
...
若是對這些知識生疏,點此學習 (http://es6.ruanyifeng.com/#do...
把這些掌握後,閱讀很easyvue
目標是把src 裏面的方法都瞭解會用node
└── webpack-chain ├── src │ ├── Chainable.js │ ├── ChainedMap.js │ ├── ChainedSet.js │ ├── Config.js │ ├── DevServer.js │ ├── Module.js │ ├── Optimization.js │ ├── Orderable.js │ ├── Output.js │ ├── Performance.js │ ├── Plugin.js │ ├── Resolve.js │ ├── ResolveLoader.js │ ├── Rule.js │ └── Use.js
文件名全是首字母大寫,從命名看,每一個裏面全是類的定義,用extends
來實現繼承webpack
ChainedMap.js
ChainedSet.js
都繼承了 Chainable
git
Chainable 以able 爲後綴表示具備什麼能力。這個庫維護了 parent ,用end來實現鏈式。很像jq的end().es6
ChainedMap.js
ChainedSet.js
這兩個裏面分別包裝了 js 原生的 Map, Set 結構。github
// 維護了parent 鏈,由上文提到的`Chainable`來作的 // 實例化了一個store (倉庫) 擁有Map結構的方法 constructor(parent) { super(parent); this.store = new Map(); } // 從 Map 移除全部 配置. clear() // 經過鍵值從 Map 移除單個配置. // key: * delete(key) // 獲取 Map 中相應鍵的值 // key: * // returns: value get(key) // 都是map的原生方法封裝 has(key) + set(key, value) // 傳入key 和 函數兩個參數 當擁有key的時候 返回獲取到的值 若是key 沒有定義過,調用函數生成鍵值對 getOrCompute(key, fn) { if (!this.has(key)) { this.set(key, fn()); } return this.get(key); } 把對象進行清洗,傳參數{a:undefined, b: [], c:{}, d:0 } 輸出{ d: 0 },會把undefined,[],{}這些過濾掉。 clean(obj) // when的用法 // when 有斷言做用,第2,3參數是函數。函數參數就是this。 // config // .when(process.env.NODE_ENV === 'production', // config => config.plugin('minify').use(BabiliWebpackPlugin), // config => config.devtool('source-map') // ); when( condition, whenTruthy = Function.prototype, whenFalsy = Function.prototype ) { if (condition) { whenTruthy(this); } else { whenFalsy(this); } return this; } extend 參數是數組,會批量往this上綁定一些方法, 綁定方法是用的set,說明再次調用會覆蓋掉上次。 例如: this.extend(['watch']) 會生成 this.watch = value => this.set(method, value);
用vue-cli3生成項目後web
vue inspect > default.json
會生成默認配置到default.json正則表達式
這些配置是從這個包node_modules/@vue/cli-service/lib/config/base.js
生成的
打開這個文件看一下webpack-chain的用法.
vue引入webpack-chain的文件是node_modules/@vue/cli-service/lib/Service.js
在此文章搜索下面三行
const Config = require('webpack-chain') // 會引入webpack-chain庫"src/Config.js"文件 // 只實例化一次,chainWebpack 的config參數就是這個實例 const chainableConfig = new Config() // 會生成配置 let config = chainableConfig.toConfig()
咱們看一下webpack-chain
源碼 Config.js
文件
toConfig() { // 入口entry const entryPoints = this.entryPoints.entries() || {}; // clean 方法上文Map結構有講 return this.clean( // this.entries() 所有this.store的值。包含this.extend()方法生成的速記方法 Object.assign(this.entries() || {}, { node: this.node.entries(), output: this.output.entries(), resolve: this.resolve.toConfig(), resolveLoader: this.resolveLoader.toConfig(), devServer: this.devServer.toConfig(), module: this.module.toConfig(), optimization: this.optimization.toConfig(), plugins: this.plugins.values().map(plugin => plugin.toConfig()), performance: this.performance.entries(), entry: Object.keys(entryPoints).reduce( (acc, key) => Object.assign(acc, { [key]: entryPoints[key].values() }), {} ), }) ); }
chainWebpack: config => { config.entryPoints.clear() // 會把默認的入口清空 config.entry('main').add('./src/main.js') config.entry('routes').add('./src/app-routes.js') } 鏈式調用:end方法 clear方法會把vue-cli默認的.entry('app')清空。能夠在同一個chunk,add多個模塊。 config.entryPoints.clear().end() .entry('main').add('./src/main.js').end() .entry('routes').add('./src/app-routes.js')
vue inspect > entry.json
對比entry.json和default.json的entry字段,成功修改。
config.mode('production') config.watch(true) 生成文件,查看已變成 mode: 'production', watch: true,
chainWebpack: config => { config.devServer.port(9898) .open(true) .proxy({'/dev': { target: 'http://123.57.153.106:8080/', changeOrigin: true, pathRewrite: { '^/dev': '' } } }) }
resolve.alias
chainWebpack: config => { config.resolve.alias .set('assets', '@/assets') .set('fetch', '@/config/http.config') .delete('fetch') // 刪掉指定的 // .clear() 會把全部別名都刪掉 }
const QiniuPlugin = require('qn-webpack') chainWebpack: config => { config.plugin('7niu') .use(QiniuPlugin,[{ accessKey: '1234567654356', secretKey: '2344344545', bucket: 'busi-cdn', path: 'cdn-finance/dist/', exclude: /index\.html$/ // 排除特定文件,正則表達式 }]); } 經過閱讀Plugin.js源碼toConfig方法,發現還能夠傳字符串類型,還有對象實例 const init = this.get('init'); // 會調用init(plugin, args); 把插件函數和參數傳入 let plugin = this.get('plugin'); // 獲取plugin const args = this.get('args');// 獲取參數 config .plugin('7niu') .use('qn-webpack',[{ accessKey: '1234567654356', secretKey: '2344344545', bucket: 'busi-cdn', path: 'cdn-finance/dist/', exclude: /index\.html$/ // 排除特定文件,正則表達式 }]); 生成的效果 /* config.plugin('7niu') */ new (require('qn-webpack'))( { accessKey: '1234567654356', secretKey: '2344344545', bucket: 'busi-cdn', path: 'cdn-finance/dist/', exclude: /index\.html$/ } )
用對象實例的方式
const CleanPlugin = require("clean-webpack-plugin"); const clean = new CleanPlugin() config .plugin('clean').use(clean)
chainWebpack: config => { config.plugins.delete('prefetch') // 移除 preload 插件 config.plugins.delete('preload'); }
把vue默認添加的loader 都刪掉.
config.module.rules.clear()
刪除指定rule
用use添加的在uses,用oneOf添加的在oneOfs
config.module .rule('vue').uses.clear() config.module .rule('scss').oneOfs.clear() 輸出後效果: /* config.module.rule('scss') */ { test: /\.scss$/ }, /* config.module.rule('vue') */ { test: /\.vue$/ },
從@vue/cli-service/lib/config/base.js
的一段代碼來參考
webpackConfig.module .rule('vue') // 會獲得Rule實例 new Rule() .test(/\.vue$/) // 速記方法調用的set,每次test()會覆蓋掉以前的 .use('cache-loader') // new Use()實例參數是this.name。 loader和options是shorthands 調用set賦值 .loader('cache-loader') .options(vueLoaderCacheConfig) .end() .use('vue-loader') .loader('vue-loader') .options(Object.assign({ compilerOptions: { preserveWhitespace: false } }, vueLoaderCacheConfig))
const Rule = Orderable( class extends ChainedMap { constructor(parent, name) { super(parent); this.name = name; this.names = []; this.uses = new ChainedMap(this); // 全部的use this.include = new ChainedSet(this); // 包含 this.exclude = new ChainedSet(this); // 排除 this.oneOfs = new ChainedMap(this); // 全部的oneOf // 速記方法,使用Map的Set 方法再次調用會覆蓋掉上次 this.extend([ 'enforce', 'parser', 'resource', 'resourceQuery', 'sideEffects', 'test', 'type', ]); } use(name) { // Use 是 ChainedMap,有就get返回,木有就調用會在this.uses註冊一個 return this.uses.getOrCompute(name, () => new Use(this, name)); } oneOf(name) { // Rule 是 ChainedMap,有就get返回,木有就調用會在this.oneOfs註冊一個 return this.oneOfs.getOrCompute(name, () => new Rule(this, name)); }
const Config = require('webpack-chain'); const config = new Config(); const CleanPlugin = require("clean-webpack-plugin"); config .plugin('clean') .use(CleanPlugin); let r = config.toConfig() console.log(r)
configureWebpack: config => { if (process.env.NODE_ENV === 'production') { config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true } }