исходный код цепочки веб-пакетов vue-cli3.0 настроить веб-пакет

Vue.js

Получить исходный код

GitHub.com/neutrino это/…

 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) После их освоения чтение становится легким

Структура каталогов

Цель состоит в том, чтобы понять и использовать методы в 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 ().

ChainedMap.js ChainedSet.jsЭти два упакованы в родную карту JS, установить структуру.

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 
 
 # 生成环境的配置 
 vue inspect --mode production > pro.json
 

Создаст конфигурацию по умолчанию для default.json

Эти конфигурации из этого пакетаnode_modules/@vue/cli-service/lib/config/base.jsСгенерировано

Откройте этот файл, чтобы увидеть использование цепочки webpack.

Файл, который vue вводит в цепочку webpack: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.jsonContrast intry.json и default.json Поле ввода, успешно изменено.

  • Сокращенный метод См. введение Map extend выше.
Изменить простые поля с помощью сокращений
    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()  会把所有别名都删掉
  }

Добавить плагин 7 крупного рогатого скота
    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.

    config.module.rules.clear()

удалить указанное правило Добавлено с использованием в использовании, добавлено с помощью 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)

vue удалить console.log

	configureWebpack: config => {
		if (process.env.NODE_ENV === 'production') {
			config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
		}
	}