美文网首页
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