likes
comments
collection
share

每天认识一个npm包,开源世界我闯荡!

作者站长头像
站长
· 阅读数 13

webpack常用的Loader以及Plugins

这里主要收集一下平时在项目中或者看开源的时候,遇到的一些npm包,特此记录以下

项目优化相关

Friendly-errors-webpack-plugin

美化控制台输出的,比webpack自带的stats配置更高度定制化

const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin");

module.exports = {
  plugins: [
    new FriendlyErrorsPlugin({
        compilationSuccessInfo: {
          messages: ['You application is running here http://localhost:3000'],
          notes: ['Some additional notes to be displayed upon successful compilation']
        },
        // 是否每次都清除控制台输出
        clearConsole: true,
      })
  ]
}

webpack-bundle-analyzer

分析打包后的资源体积,为优化提供可视化分析数据

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

mini-css-extract-plugin css提取到单独文件

  • 将样式代码提取到单独的文件中, 它还提供了相应的loader,基础配置如下

    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    module.exports = {
      plugins: [new MiniCssExtractPlugin({
        filename: 'css/[name].[contenthash:7].css'
      })],
      module: {
        rules: [
          {
            test: /.(less|css)$/,
            // 注意 该loader和style-loader不兼容,需要注意下,下面是通过isProd判断是否是生产环境
            use: [isProd ? MiniCssExtractPlugin.loader : 'style-loader', "css-loader", "less-loader"],
          },
        ],
      },
    };

文档地址:

optimize-css-assets-webpack-plugincss压缩

  • 压缩css

    const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
    module.exports = {
      optimization: {
        minimizer: [
          new OptimizeCSSAssetsPlugin({})
        ]
      }
    }

uglifyjs-webpack-pluginjs压缩

  • 压缩js代码
  • 也可开启缓存和多进程

    const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
    module.exports = {
      optimization: {
        minimizer: [
          new UglifyJsPlugin({
            cache: true,
            parallel: true,
            sourceMap: false
          })
        ]
      }
    }

babel-loader开启缓存

    {
            test: /.(jsx?|babel|es6)$/,
            include: process.cwd(),
            exclude: "node_modules",
            use: {
              loader: 'babel-loader',
              options: {
                compact: true,
                // 这个配置
                cacheDirectory: true
              }
            }
    },

terser-webpack-plugin

  • 使用多进程并发运行以提高构建速度
  • 删除注释

    const TerserPlugin = require('terser-webpack-plugin');
    module.exports = {
      optimization: {
        minimizer: [
          new TerserPlugin({
            terserOptions: {
              // 这里
             parallel: true,
             // 删除注释
             format: {
                comments: false,
              },
            },
            extractComments: false,
          })
        ]
      }
    }

hard-source-webpack-plugin

可开启缓存, 增加二次打包或者热更新速度

    const HardSourcePlugin = require('hard-source-webpack-plugin');
    module.exports = {
      plugins: [new HardSourcePlugin()]
    }

style-loader的替换方案

  • 在webpack5中,资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。

    // 该例子就是控制资源在一定大小内使用内联的形式
    module: {
      rules: [
        {
            test: /.(png|svg|jpg|jpeg|gif)$/i,
            type: "asset/resource",
            parser: {
              //转base64的条件
              dataUrlCondition: {
                maxSize: 10 * 1024, // 10kb
              },
            },
            generator: {
              filename: "static/[name].[contenthash:7][ext]",
            },
          },
      ]
    }

thread-loader

  • 多进程程处理loader
  • 使用时,需将此 loader 放置在其他 loader 之前。放置在此 loader 之后的 loader 会在一个独立的 worker 池中运行。
  • 请仅在耗时的操作中使用此 loader!

    module.exports = {
      module: {
        rules: [
          {
            test: /.js$/,
            include: path.resolve('src'),
            use: [
              "thread-loader",
              // 耗时的 loader (例如 babel-loader)
            ],
          },
        ],
      },
    };

happypack

  • 启动多进程来优化构建速度,但是作者不维护了,替换方案请使用thread-loader

@babel/polyfill 按需引入

详情见 目录babel相关@babel/polyfill 按需引入

babel相关的

babel-plugin-add-module-exports

如果希望对它的转码符合 commonJS,就需要安装 babel-plugin-add-module-exports插件,并在.babelrc 文件内声明该插件。

在 babel5 时代, export default {} 除了会被转译成 exports.default = {},还会加一句 module.exports = exports.default,这是为了兼顾commonJS规范。但在 babel6 时代,后面一句不再添加,这是为了区分commonJS和ES6的模块定义。

每天认识一个npm包,开源世界我闯荡!k

babel-plugin-module-resolver

是一个Babel模块解析插件, 在.babelrc中可以配置模块的导入搜索路径

简单的来说就跟webpack的resolve.alias配置很相似.举例:

    // .bablerc文件内
    {
      "plugins": [
        ["module-resolver", {
          "root": ["./"],
          "alias": {
            "P":"./app/p"
          }
        }]
      ]
    }

如上配置后,在项目里面如果有如下路径都会被解析成配置项里面的alias对应的路径

    //import Mp from '../../p/MyPropTypes';

    import Mp from 'P/MyPropTypes'

    import MyUtilFn from 'utils/MyUtilFn';

    //import MyUtilFn from '../../../../utils/MyUtilFn';

babel-plugin-syntax-jsx babel-plugin-transform-vue-jsx babel-helper-vue-jsx-merge-props

这几个是配置项目支持jsx语法的,主要是针对vue项目的,react的话需要安装@babel/preset-react,直接安装使用就行,不需要额外配置

@babel/polyfill 按需引入

项目中如果想使用新语法,并使浏览器兼容的话,就需要安装@babel/polyfill, 正常是在入口文件中直接import'@babel/polyfill'引入,但打包你会发现有个core-js体积很大,是因为直接医用,会把@babel/polyfill全量引入并打包了,所以优化点就是要按需引入

    // .babelrc 文件中

    {
      "presets": [
        [
          "@babel/preset-env",
          // 通过babel的预设插件来进行配置,                core对应的版本
          { "modules": false, "useBuiltIns": "usage", "corejs": 3 }
        ]
      ]
    }

配置后无需引入 polyfill,babel 会自动按需加载需要的功能,下图为当前配置对应的相应包的版本号

每天认识一个npm包,开源世界我闯荡!

如果配置后报错core-js/***什么无法解析之类的,就在node_modules文件中找到@babel/polyfill中的packages.json,查看里面的core-js的版本,然后在项目中安装一下,并把.babelrc中的corejs配置改为对应的版本

@babel/plugin-transform-runtime

优化项 能减少babel内公共方法代码的重复的使用带来的问题

    {
      "plugins": [
        [
          "@babel/plugin-transform-runtime",
          {
            "regenerator": true
          }
        ]
      ]
    }

Node 环境中常用的库

dotenv 将 .env 文件 中的环境变量加载到 process.env 中

dotenv 是一个零依赖模块,可将 .env 文件 中的环境变量加载到 process.env 中,或者解析env文件的变量

如何使用:

创建 .env 文件

    S3_BUCKET="YOURS3BUCKET"
    SECRET_KEY="YOURSECRETKEYGOESHERE"

使用

    import * as dotenv from 'dotenv'
    dotenv.config()
    console.log(process.env) 

    // 解析
    const env = dotenv.parse(
            fs.readFileSync(path.resolve(process.cwd(), "env.product"))
          );

npm-run-all 并行 串行 执行多个npm脚本

这个工具是为了解决官方的 npm run 命令无法同时运行多个脚本的问题,它可以把诸如 npm run clean && npm run build:css && npm run build:js && npm run build:html 的一长串的命令通过 glob 语法简化成 npm-run-all clean build:* 这样精致小巧的模样。再者大家也知道 shell 的 & 语法实际上是不支持 cmd 的,为了跨平台也最好使用这样的第三方库来提供支持。preact 中就是用来对数量繁多的子模块进行并行构建和顺序测试。

    --parallel: 并行运行多个命令,例如:npm-run-all --parallel lint build
    --serial: 多个命令按排列顺序执行,例如:npm-run-all --serial clean lint build:**
    --continue-on-error: 是否忽略错误,添加此参数 npm-run-all 会自动退出出错的命令,继续运行正常的
    --race: 添加此参数之后,只要有一个命令运行出错,那么 npm-run-all 就会结束掉全部的命令

    run-s:为 npm-run-all --serial的缩写;

    run-p:为 npm-run-all --parallel的缩写

esno 用esbuild执行ts脚本

esno 是基于 esbuild 的 TS/ESNext node 运行时。该库会针对不同的模块化标准,采用不同的方案:

  • esno - Node in CJS mode - by esbuild-register
  • esmo - Node in ESM mode - by esbuild-node-loader

    esno index.ts
    esmo index.ts

    // 示例
    "script": {
      "test": "node test.js", // 运行js脚本
      "test": "esno test.ts", // 使用esbuild运行ts脚本
      
    }

uppercamelcase 转换成大驼峰命名规范

  • 将短划线/点/下划线/空格分隔的字符串转换为大写字母,也就是转换成大驼峰命名规范

每天认识一个npm包,开源世界我闯荡!

picocolors 在终端修改输出字符样式的 npm 包 作用同chalk

picocolors 是一个可以在终端修改输出字符样式的 npm 包,说直白点就是给字符添加颜色;

    import pc from "picocolors"

    console.log(
      pc.green(`How are ${pc.italic(`you`)} doing?`)
    )

优点

  • 无依赖包;
  • 比 chalk 体积小 14 倍,速度快 2 倍;
  • 支持 CJS 和 ESM 项目

注意

  1. 不过 chalk 为了优化,在最近的最新版本 v5 中已剔除依赖包
  2. 因为 picocolors 包比较小,所以功能边界没有 chalk 的全面

chalk 控制log在控制台输出的颜色

  • 控制log在控制台输出的颜色
  • "chalk": "2.4.2" 新版的是采用的ESM模式,如果在node中使用,就安装开头的这个版本

每天认识一个npm包,开源世界我闯荡!

json-templater 模板语法

使用特定的模板语法, 相当于es6模板语法的高级版,当变量过多的时候使用这个,更方便

    var render = require('json-templater/string');
    render('{{xfoo}} {{say.what}}', { xfoo: 'yep', say: { what: 'yep' } });

update-notifier检查库的更新

    // 引用 update-notifier 库,用于检查更新
    const updateNotifier = require('update-notifier')
    // 引用 chalk 库,用于控制台字符样式
    const chalk = require('chalk')
    // 引入 package.json 文件,用于 update-notifier 库读取相关信息
    const pkg = require('../package.json')

    // updateNotifier 是 update-notifier 的方法,其他方法可到 npmjs 查看
    const notifier = updateNotifier({
      // 从 package.json 获取 name 和 version 进行查询
      pkg,
        // 设定检查更新周期,默认为 1000 * 60 * 60 * 24(1 天)
        // 这里设定为 1000 毫秒(1秒)
      updateCheckInterval: 1000,
    })

    function updateChk() {
      // 当检测到版本时,notifier.update 会返回 Object
        // 此时可以用 notifier.update.latest 获取最新版本号
      if (notifier.update) {
        console.log(`New version available: ${chalk.cyan(notifier.update.latest)}, it's recommended that you update before using.`)
        notifier.notify()
      } else {
        console.log('No new version is available.')
      }
    }

    // 将上面的 updateChk() 方法导出
    module.exports = updateChk

rimraf 删除文件和文件夹的

以包的形式包装rm -rf命令,用来删除文件和文件夹的,不管文件夹是否为空,都可删除.

此处就可以在windows上删除node_modules文件特别有用和高效,只需要配置相关命令即可

    npm install rimraf --save-dev

    const rimraf = require('rimraf');
    rimraf('./test.txt', function (err) { // 删除当前目录下的 test.txt
      console.log(err);
    });


    or
    "scripts": {
        ...
        "build": "rimraf dist"
      },

commander 注册命令,解析命令

命令行工具,有了它我们就可以读取命令行命令,知道用户想要做什么了,总之就是注册命令,解析命令

具体参考github.com/tj/commande…

    const { Command } = require('commander');
    const program = new Command();

    program
      .name('string-util')
      .description('CLI to some JavaScript string utilities')
      .version('0.8.0');

    program.command('split')
      .description('Split a string into substrings and display as an array')
      .argument('<string>', 'string to split')
      .option('--first', 'display just the first substring')
      .option('-s, --separator <char>', 'separator character', ',')
      .action((str, options) => {
        const limit = options.first ? 1 : undefined;
        console.log(str.split(options.separator, limit));
      });

    program.parse();

inquirer交互式命令行工具

交互式命令行工具,给用户提供一个漂亮的界面和提出问题流的方式,例如使用vuecli创建项目时,会有提问和选项与用户进行交互

    #!/usr/bin/env node

    // 交互式命令行
    const inquirer = require("inquirer");

    // 自定义交互式命令行的问题和简单校验

    let question = [
      {
        name: "name",
        type: "input",
        message: "请输入模板名称",
        validate(val) {
          if (val === "") {
            return "请输入模板名称";
          } else if (tplObj[val]) {
            return "模板已存在!";
          } else {
            return true;
          }
        },
      },
      {
        name: "type",
        type: "rawlist",
        message: "请选择模板仓库类型",
        choices: [
          {
            name: "github:",
          },
          {
            name: "gitlab:",
          },
          {
            name: "Bitbucket:",
          },
        ],
      },
      {
        name: "url",
        type: "input",
        message: "请输入模板地址",
        validate(val) {
          if (val === "") return "请输入模板地址";
          return true;
        },
      },
    ];

    inquirer.prompt(question).then((options) => {
      // options是用户输入的参数  是一个对象
      let { name, url, type } = options;
    });

download-git-repo 下载远程仓库

下载远程模板工具,负责下载远程仓库的模板项目

ora 显示加载中的效果

用于显示加载中的效果,类似于前端页面的 loading 效果,像下载模板这种耗时的操作,有了 loading 效果可以提示用户正在进行中,请耐心等待

log-symbols

日志彩色符号,用来显示√ 或 × 等的图标

minimist 命令行参数解析工具,

命令行参数解析工具,可以更直观的让我们使用 命令行参数

    node index.js --beep=boop -t -z 12 -n5 foo bar 

Node.js 程序启动后可以直接从process.argv中读取到参数列表:

    console.log(process.argv);
    // ['/bin/node', '/tmp/index.js', '--beep=boop', '-t', '-z', '12', '-n5', 'foo', 'bar']

从上述代码中可以看到,process.argv 变量是一个数组,数组前两项分别是 node 程序位置和js脚本位置,数组中随后的元素都是我们启动Node.js后的参数,这些参数以空格分隔成数组。

虽然从 process.argv 中可以得到启动参数列表,但是我们仍需要对参数进行进一步解析处理才行

    const argv = require('minimist')(process.argv.slice(2));
    console.dir(argv);
    // { _: [ 'foo', 'bar' ], beep: 'boop', t: true, z: 12, n: 5 }

经过 minimist 解析的process.argv是一个对象,例如,我们可以直接从访问 argv.beep 得到 --beep=boop 参数的值。

globby glob 快速批量导入、读取文件的库

node环境中搜索文件使用的库 globby 是 glob的增强版 后面可以直接使用globby

每天认识一个npm包,开源世界我闯荡!

每天认识一个npm包,开源世界我闯荡!

fast-glob 快速批量导入、读取文件的库

该包提供了遍历文件系统的方法github.com/mrmlnc/fast…

    import glob from "fast-glob";

    const files = await glob("*.ts", {
        cwd: process.cwd(), // 需要匹配的工作目录, 默认是process.cwd()
        absolute: true, // 返回绝对路径
        onlyFiles: true, // 只返回文件
      });
      // 返回值 [ 'D:/zpp/组件库/multiple-technologies/packages/components-v3/index.ts' ]

ts-morph

来完成类型的生成和导出 ts-morph.com/setup/

Rollup中用的包

@rollup/plugin-node-resolve

@rollup/plugin-commonjs

解析commonjs ts vue文件的包

     import { nodeResolve } from "@rollup/plugin-node-resolve";
    import commonjs from "@rollup/plugin-commonjs";
    import vue from "rollup-plugin-vue";
     const config = {
          input,
          plugins: [nodeResolve(), typescript(), vue(), commonjs()],
          external: (id) => /^vue/.test(id) || /^@soul-cli/.test(id), // 排除掉vue和@soul-cli的依赖
        };