git clone https://github.com/neutrinojs/webpack-chain.git
複製代碼
須要先預習 下列方法html
ES6 Set
結構ES6 Map
結構ES6 Class
類繼承includes reduce map forEach
Object.keys Object.assign
...
若是對這些知識生疏,點此學習 (es6.ruanyifeng.com/#docs/class) 把這些掌握後,閱讀很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
# 生成環境的配置
vue inspect --mode production > pro.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
}
}
複製代碼