美文网首页
webpack-chain 速查手册之 ChainedMap

webpack-chain 速查手册之 ChainedMap

作者: AizawaSayo | 来源:发表于2021-04-27 23:58 被阅读0次

webpack-chain GitHub 中文文档
webpack-chain 速查手册之 ChainedSet

Set 和 Map 数据类型

Set 是一组值的集合,值允许任何类型,但不可以重复。会记住元素原始插入顺序所以是可迭代的。如果传入重复元素,会在Set中自动被过滤,可以利用Set来去重。
初始化Set需要传入一个数组,或者直接初始化空Set:new Set()

var s = new Set([1, 2, 3, 2, 1, '哈哈'])
console.log(s) // Set(2) {1, 2, 3, '哈哈'} 自动去除重复项
s.add(4) // 添加新元素 4 
s.delete(3) // 删除元素 3
s.has(3) // 是否存在元素 3:false 
s.clear() // 移除s中所有项

Map以[键,值]的形式存储数据,并且能够记住键的原始插入顺序。键和值允许任何数据类型,键不可重复是惟一的,可以极高效地查找数据。
初始化Map需要传一个二维数组,或者直接初始化一个空Map:new Map()

var m = new Map([['Michael', 95], ['Bob', 75], ['Tracy', 85]])
console.log(m) // Map(3) {"Michael" => 95, "Bob" => 75, "Tracy" => 85}
m.set('Adam', 67); // 添加新的key-value
m.has('Adam'); // 是否存在key 'Adam': true
m.get('Adam'); // 67
m.delete('Adam'); // 删除key 'Adam'
m.clear() // Map {}  

获取它们的元素个数的属性是size,如:

console.log(new Set([1,2,1,2]).size) // 2 

它们都拥有遍历方法:

  • keys():返回所有键名
  • values():返回所有键值
  • entries():返回所有成员
  • forEach(value, key):遍历所有成员
    由于Set中的数据没有键名只有值,或者说键和值是同一个,遍历时返回的 key 和 value 是一样的。
console.log(new Set([1,2,3,4]).values()) // SetIterator {1, 2, 3, 4}
console.log(new Set([1,2,3,4]).entries()) // SetIterator {1 => 1, 2 => 2, 3 => 3, 4 => 4}
console.log(new Map([['name','jackson'],['age',11]]).keys()) // MapIterator {"name", "age"}
new Map([['name','jackson'],['age',11]]).forEach((value, key) => { console.log(key, value)} )  
// name jackson
// age 11

ChainedMap的API和方法:

操作类似于JavaScript Map。除非另有说明,否则这些方法将返回 ChainedMap , 允许再链式调用这些方法。

// 从 Map 移除所有 配置.
clear()

// 通过键值从 Map 移除单个配置.
// key: *
delete(key)

// 获取 Map 中相应键的值
// key: *
// returns: value
get(key)

// 获取 Map 中相应键的值
// 如果键在Map中不存在,则ChainedMap中该键的值会被配置为fn的返回值.
// key: *
// fn: Function () -> value
// returns: value
getOrCompute(key, fn)

// 配置Map中 已存在的键的值
// key: *
// value: *
set(key, value)

// Map中是否存在一个配置值的特定键,返回 真或假
// key: *
// returns: Boolean
has(key)

// 返回 Map中已存储的所有值的数组
// returns: Array
values()

// 返回Map中全部配置的一个对象, 其中 键是这个对象属性,值是相应键的值,
// 如果Map是空,返回 `undefined`
// 使用 `.before() 或 .after()` 的ChainedMap, 则将按照属性名进行排序。
// returns: Object, undefined if empty
entries()

//  提供一个对象,这个对象的属性和值将 映射进 Map。
// 你也可以提供一个数组作为第二个参数以便忽略合并的属性名称。
// obj: Object
// omit: Optional Array
merge(obj, omit)

// 对当前配置上下文执行函数。
// handler: Function -> ChainedMap
  // 一个把ChainedMap实例作为单个参数的函数
batch(handler)

// 条件执行一个函数去继续配置
// condition: Boolean
// whenTruthy: Function -> ChainedMap
  // 当条件为真,调用把ChainedMap实例作为单一参数传入的函数
// whenFalsy: Optional Function -> ChainedMap
  // 当条件为假,调用把ChainedMap实例作为单一参数传入的函数
when(condition, whenTruthy, whenFalsy)

被标记为ChainedMap的属性

const path = require('path')
config.set('context', path.resolve(__dirname, '.')) // 设置 webpack 上下文目录为项目根目录

配置速记方法:可以用键名作为方法名为ChainedMap设置一个值,从而简化ChainedMap.set(键, 值)方法

// 在 ChainedMap 上设置一个值的 速记方法
config.mode('development') // 告诉 webpack 使用开发环境模式优化
// 等效于:
config.set('mode', 'development')

end()方法可以让我们回到上一级API实例
链式调用时我们会移动到API的下一级甚至更深层,这会更改当前的上下文。编写规范是到下一层遍缩进一个tab,同一层级缩进空格相等。如果想回到上一层级继续链式调用上级API的方法,调用** .end()**即可实现。因为全部的API调用都将在当前上下文中返回该API实例,关于webpack配置项层级关系请参阅 webpack文档层级结构

举例:

config.module
  .rule('vue')
    .test(/\.vue$/)
    .use('cache-loader') // use 和 test 同级,都是 rule 下级 API
      .loader(require.resolve('cache-loader'))
      .end() // 调用end() 由回到 rule API 层级
    .use('vue-loader')
      .loader(require.resolve('vue-loader'))
config.
  output
    .path(path.join(__dirname, 'dist')) // 设置打包输出的目录为根目录的 dist 
config.resolve.alias
  .set('@', path.join(__dirname,'src'))
  .set('@utils', path.join(__dirname,'src/utils'))
  .delete('@api')
  .clear()
  • config.resolve.plugin(name) 使用额外的解析插件
    使用方式和下面config.plugin(name)一致,只是前面多了个.resolve而已
  • config.performance 性能
config.performance
  .hints('error')
  .maxEntrypointSize(400000)
  .maxAssetSize(100000)
  .assetFilter(assetFilename => assetFilename.endsWith('.js'))
// Minimizer 使用插件时可以通过它们的路径指定,从而允许在不使用插件或webpack配置的情况下跳过昂贵的 require
config.optimization
  .minimizer('css')
  .use(require.resolve('optimize-css-assets-webpack-plugin'), [{ cssProcessorOptions: { safe: true }

// 修改参数
config.optimization
  .minimizer('css')
  .tap(args => [...args, { cssProcessorOptions: { safe: false } }])

// 修改实例
config.optimization
  .minimizer('css')
  .init((Plugin, args) => new Plugin(...args))

// 移除
config.optimization.minimizers.delete('css')

顺便提下 when(condition, whenTruthy, whenFalsy) 的用法:

// 在生产环境中添加缩小插件,非生产环境devtool到源映射
config
  .when(process.env.NODE_ENV === 'production',
    config => {
      const terserOptions = require('./terserOptions')
      config.optimization
        .minimizer(terser)
          .use(require.resolve('terser-webpack-plugin'), [terserOptions(options)])  
    },
    config => config.devtool('source-map')
  )
// 添加
config
  .plugin('env')
  .use(require.resolve('webpack/lib/EnvironmentPlugin'), [{ 'VAR': false }])

// 修改参数
config
  .plugin('env')
  .tap(args => [...args, 'SECRET_KEY'])

// 修改实例
config
  .plugin('env')
  .init((Plugin, args) => new Plugin(...args))

// 移除
config.plugins.delete('env')

// 指定当前插件上下文应该在另一个指定插件之前执行
config
  .plugin('html-template')
    .use(HtmlWebpackTemplate)
    .end()
  .plugin('script-ext')
    .use(ScriptExtWebpackPlugin)
    .before('html-template')

// 指定当前插件上下文应该在另一个指定插件之后执行
config
  .plugin('html-template')
    .after('script-ext')
    .use(HtmlWebpackTemplate)
    .end()
  .plugin('script-ext')
    .use(ScriptExtWebpackPlugin)
  • config.node 配置node
config.node
  .set('__dirname', 'mock')
  .set('__filename', 'mock')
config.node
  .set('__dirname', 'mock')
  .set('__filename', 'mock')
// 不解析的模块
config.module
  .noParse(/^(vue|vue-router|vuex|vuex-router-sync)$/)
  • config.module.rule(name) 创建模块时匹配请求的规则数组中的一项

    和 webpack 中 module: { rules: [...] }的配置方式不同,webpack-chain 规则是一项一项用.rule 分开写的。rule(name) 中的 name 为自定义规则名称。
  • config.module.rule(name).use(name)
    Rule.use 不同,这个use(name)是每个loader入口的自定义名称,即.rule(name).set('use',name),为了后续针对性的修改,方便识别而自行取名的。一个use(name)对应一个loader(loader)属性,注意和loader入口数组区分(rules: [{ use: ['style-loader','css-loader'] })

.tap()修改这个接口很实用,特别适合当在别处已经有做过某规则(如.rule(name))的配置时,不方便去原文件修改,直接修改loader配置的情况,也可以同时链式加其他loader。比如覆盖框架的默认webpack配置。

// 创建
config.module
  .rule('compile')
    .use('babel')
      .loader('babel-loader')
      .options({ presets: ['@babel/preset-env'] })

// 修改选项
config.module
  .rule('compile')
    .use('babel')
      .tap(options => merge(options, {
        plugins: ['@babel/plugin-proposal-class-properties']
      }))
  • enforce(preOrPost)定义loader执行顺序
    先执行上面的loader(pre) 或下面的loader (post)。
    根据文档的API和实测,这个配置和webpack官方的区别挺大,不要把这里的prepost跟前置loader和后置loader混淆了。
    用 webpack-chain 为一条规则配置多个loader时,也是按从下到上的顺序执行,即默认最后一个是前置loader(先执行),第一个是后置loader。我们可以用enforce来调整这个顺序。
// 速记格式解读
config.module
  .rule(name)
    .test(test)
    .pre() // 指代 .use(loader-name-pre)
    .post() // 指代另一个 .use(loader-name-post)
    .enforce(preOrPost) // 值为'pre' 则表示先执行上面的loader,即loader-name-pre;值为'post' 则先执行下方的loader,loader-name-post
// 本来必须把 image-webpack-loader 配置在后面才能先执行
// 用 enforce('pre') 手动设置先执行上面的 loader
config.module
  .rule('images')
    .use('image-webpack-loader') // 在前面表示 pre
      .loader('image-webpack-loader')
      .options({...})
      .end()  //回到use同层级
    .use('url-loader') // 在后面表示 post
      .loader(require.resolve('url-loader'))
      .options({...})
      .end()
    .enforce('pre') // 先执行上面的 image-webpack-loader
  • config.module.rule(name).oneOf(name) Rule.oneOf
    通过匹配资源请求字符串的查询部分,如foo.css?inlineinline,一旦匹配到oneOf的其中一条的resourceQuery,就跳过其他所有的loader。
    可以通过.before().after()使某条oneOf先于或后于另一条去被尝试匹配。
config.module
  .rule('css')
    .oneOf('inline')
      .resourceQuery(/inline/)
      .use('url')
        .loader('url-loader')
        .end()
      .end()
    .oneOf('external')
    .before('inline')
      .resourceQuery(/external/)
      .use('file')
        .loader('file-loader')

相关文章

网友评论

      本文标题:webpack-chain 速查手册之 ChainedMap

      本文链接:https://www.haomeiwen.com/subject/cdgurltx.html