webpack-chain原始碼 vue-cli配置webpack

高大師發表於2019-04-26

獲取原始碼

github.com/neutrinojs/…

 git clone   https://github.com/neutrinojs/webpack-chain.git
複製程式碼

知識準備

需要先預習 下列方法

  • ES6 Set 結構
  • ES6 Map 結構
  • ES6 Class 類繼承
  • 箭頭函式
  • 陣列的一些方法: includes reduce map forEach
  • 物件的一些方法: Object.keys Object.assign
  • 展開運算子 ...

如果對這些知識生疏,點此學習 (es6.ruanyifeng.com/#docs/class) 把這些掌握後,閱讀很easy

目錄結構

目標是把src 裡面的方法都瞭解會用

└── 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來實現繼承

Chainable ChainedMap.js ChainedSet.js

ChainedMap.js ChainedSet.js 都繼承了 Chainable

Chainable 以able 為字尾表示具有什麼能力。這個庫維護了 parent ,用end來實現鏈式。很像jq的end().

ChainedMap.js ChainedSet.js 這兩個裡面分別包裝了 js 原生的 Map, Set 結構。

ChainedMap
// 維護了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);

複製程式碼
理解了ChainedMap,那ChainedSet也容易

實戰配置vue-cli 專案

用vue-cli3生成專案後

 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() }),
          {}
        ),
      })
    );
  }
複製程式碼
修改 entry
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欄位,成功修改。

  • 速記方法 看上文Map extend 介紹
用速記方法修改簡單的欄位
    config.mode('production')
    config.watch(true)

    生成檔案,檢視已變成
     mode: 'production',
     watch: true,

複製程式碼
用速記方法修改proxy
  chainWebpack: config => {
    config.devServer.port(9898)
      .open(true)
      .proxy({'/dev': {
                 target: 'http://123.57.153.106:8080/',
                 changeOrigin: true,
                 pathRewrite: {
                   '^/dev': ''
                 }
               }
           })
  }

複製程式碼
用速記方法修改output
  chainWebpack: config => {
   config
      .when(process.env.NODE_ENV === 'production', config => {
        config.output.publicPath('//busi.cdnimg.com/demo-finance/dist/')
      });
  }

複製程式碼
新增7牛plugin
    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$/
      }
    )
複製程式碼
新增clear plugin

用物件例項的方式


const CleanPlugin = require("clean-webpack-plugin");

const clean  = new CleanPlugin()

    config
      .plugin('clean').use(clean)
複製程式碼

新增 刪除loader

把vue的loader 都刪掉

    config.module
      .rule('vue').uses.clear()

複製程式碼

新增loader 從@vue/cli-service/lib/config/base.js 的一段程式碼來參考

    webpackConfig.module
      .rule('images')
        .test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
        .use('url-loader')
          .loader('url-loader')
          .options(genUrlLoaderOptions('img'))
複製程式碼

單獨除錯

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)
複製程式碼

相關文章