npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@luichooy/webpack-builder

v0.0.5

Published

common webpack builder

Readme

自动补全 CSS 前缀

利用 postcss 的插件 autoprefixer

install

npm install postcss-loader autoprefixer -D

usage

  1. css-loader后面添加postcss-loader
// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader']
      }
    ]
  }
}
  1. 新建 postcss 配置文件 postcss.config.js, 使用 autoprefixer 插件
// postcss.config.js

module.exports = {
  plugins: [require('autoprefixer')]
}
  1. 新建 .browserslistrc
// .browserslistrc

> 1%
last 2 versions
not ie <= 8

每次构建前清理 构建目录

  • 通过 npm script 清理构建目录
# rm 命令
rm -rf ./dist && webpack

# rimraf 库
rimraf ./dist && webpack
  • 自动清理构建目录 使用 clean-webpack-plugin
const cleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
  plugin: [
    new cleanWebpackPlugin()
  ]
}

代码压缩(html/css/js)

  1. webpack4 内置了 uglifyjs-webpack-plugin,当 mode 为“production”时,webpack 构建是默认已将 js 压缩过了,所以不用再去处理。但是也可以手动安装uglifyjs-webpack-plugin来开启并行压缩。

  2. css 压缩使用 optimize-css-assets-webpack-plugincssnano

// webpack.prod.js

module.exports = {
  plugins: [
    new OptimizeCssAssetsWebpackPlugin({
        assetNameRegExp: /\.css$/,
        cssPorcessor: cssnano
    })
  ]
}
  1. html 压缩通过给html-webpack-plugin插件传入minify参数来配置压缩策略。

html-webpack-plugin

new HtmlWebpackPlugin({
  template: path.resolve(__dirname, `./src/pages/index.html`),
  filename: `index.html`,
  chunks: [index],
  minify: {
    collapseBooleanAttributes: true,
    collapseWhitespace: true,
    minifyJS: true,
    minifyCss: true,
    removeComments: true
  }
})

minify 用法:

  • collapseBooleanAttributes: true // 删除布尔属性的属性值,如:
<input value=“foo” readonly=“readonly”>
===>
<input value=“foo” readonly>

静态资源内联

静态资源内联的意义:

  • 代码层面
    • 页面框架的初始化脚本
    • 上报相关打点
    • css 内联避免页面闪动
  • 请求层面: 减少 HTTP 网络请求数 * 小图片或者字体内联(url-loader)

HTML 和 JS 内联, 使用 raw-loader

<!--html-webpack-plugin 默认的模板引擎是 ejs,所以可以这样插入-->

<!DOCTYPE html>
<html lang="“en”">
  <head>
    ${require(‘raw-loader!../meta.html’)}
    <title>Search</title>
    <script>
      { require('raw-loader!babel-loader!../../node_modules/lib-flexible/flexible.js')}
    </script>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

raw-loader 版本: v0.5.1

CSS 内联

  • 方案一: 借助 style-loader
  • 方案二:html-inline-css-webpack-plugin

代码分割

对于⼤的 Web 应⽤来讲,将所有的代码都放在⼀个⽂件中显然是不够有效的,特别是当你的 某些代码块是在某些特殊的时候才会被使⽤到。webpack 有⼀个功能就是将你的代码库分割成 chunks(语块),当代码运⾏到需要它们的时候再进⾏加载。

适用场景:

  • 抽离相同代码到⼀个共享块
  • 脚本懒加载,使得初始下载的代码更⼩

基础库分离(html-webpack-externals-plugin)

首先,在 webpack.prod.config.js 添加配置

// webpack.prod.config.js

const HtmlWebpackExternalsPlugin = require('html-webpack-externals-plugin')
module.exports = {
  plugins: [
    new HtmlWebpackExternalsPlugin({
      externals: [
        {
          module: 'react',
          entry: 'https://cdn.bootcss.com/react/16.13.1/umd/react.production.min.js',
          global: 'React'
        },
        {
          module: 'react-dom',
          entry: 'https://cdn.bootcss.com/react-dom/16.13.1/umd/react-dom.production.min.js',
          global: 'ReactDOM'
        }
      ]
    })
  ]
}

然后,在 html 模板中引入分离的基础库脚本

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    ${require('raw-loader!../meta.html')}
    <title>Search</title>
    <script>
      ${require('raw-loader!babel-loader!../../../node_modules/lib-flexible/flexible.js')}
    </script>
  </head>
  <body>
    <div id="root"></div>
    <script src="https://cdn.bootcss.com/react/16.13.1/umd/react.development.js" type="text/javascript"></script>
    <script src="https://cdn.bootcss.com/react-dom/16.13.1/umd/react-dom.development.js" type="text/javascript"></script>
  </body>
</html>

利用SplitChunkplugin进行公共脚本分离

webpack4 内置splitChunkPlugin,替代 webpack3 中常用的 CommonsChunkPlugin

// webpack.prod.config.js

// 分离第三方基础库
module.exports = {
  optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          test: /(react|react-dom)/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  }
}

// 分离公共模块
module.exports = {
  optimization: {
    splitChunks: {
      minSize: 0,
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks: 'all',
          minChunks: 2
        }
      }
    }
  }
}

// HtmlWebpackPlugin 插件中配置分离出来的公共库
new HtmlWebpackPlugin({
  chunks: ['commons', 'otherChunk']
})

chunks 参数说明:

  • async 异步引入的库进行分离(默认)
  • initial 同步引入的库进行分离
  • all 所有引入的库进行分离(推荐)

动态代码分割

懒加载 JS 脚本的方式

  • commonJS: require.ensure
  • ES6: 动态 import(目前还没有原生支持,需要 babel 转换)

如何使用动态 import

第一步,安装 babel 插件

npm install @babel/plugin-syntax-dynamic-import -D

第二步,配置 babel 配置文件

// .babelrc
{
  "plugins": ["@babel/plugin-syntax-dynamic-import"]
}

第三步,使用

import(filepath).then(res => {
  var variable = res.default
})

貌似现在 webpack 又默认支持动态 import 了?

tree shaking

webpack4 中将 mode 设置为“production”将默认启用 tree shaking

哪些代码会被标记为不可用?DCE(Elimination)

  • 代码不会执行,不可达到
  • 代码执行的结果不会被用到
  • 代码只会影响死变量(只写不读)

tree shaking 原理

利用 ES6 模块的特点:

  • 只能作为模块顶层的语句出现
  • import 的模块名只能是字符串常亮
  • import binding 是 immutable 的

代码擦除:uglify 阶段删除无用代码

tree shaking 本质是利用编译阶段的静态代码分析,代码是否可用需要在编译阶段就确定下来,所以它只能支持 ES module,而不支持 commonjs,因为 commonjs 模块是动态模块,无法做静态分析。tree shaking 会在编译阶段将无用代码通过添加注释的方式标记出来,然后在 uglify 阶段将这些无用代码删除

scope hoisting

现象: webpack 打包后存在大量的必报代码,一个模块就是一个闭包,

大量函数闭包包裹代码,导致体积增大(模块越多越明显)

代码运行时创建的函数作用域变多,内存开销变大

webpack 模块机制分析

;(function(modules) {
  var installModules = {}

  function __webpack_require__(moduleId){
    if(installModules[moduleId]){
      return installModules[moduleId].exports
    }

    var module = installModules[moduleId] = {
      i: moduleId,
      l: false, // 标记模块是否加载
      exports: {}
    }

    modules[moduleId].call(module.exports, module, module.exports,__webpack_require__)
    module.l = true

    return module.exports
  }

  __webpack_require__(0)
})([
  /* 0 module */
  (function(module, __webpack_exports__, __webpack_require__){
    ...
  }),

  /* 1 module */
  (function(module, __webpack_exports__, __webpack_require__){
    ...
  }),


  /* 2 module */
  (function(module, __webpack_exports__, __webpack_require__){
    ...
  })
])
// 打包后的模块初始化函数

;(function(module, __webpack_exports__, __webpack_require__) {
	'use strict'

	__webpack_require__.r(__webpack_exports__)

  var _x__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1)
  var _y__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2)

  ...
})

分析:

  • 打包出来的是一个 IIFE(匿名闭包)
  • modules 是一个数组,每一项是一个模块初始化函数
  • __webpack_require__用来加载模块,返回 module.exports
  • 通过 WEBPACK_REQUIRE_METHOD(0)启动程序

scope hoisting 原理

将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突

通过 scope hoisting 可以减少函数申明代码和内存开销

使用

webpack3 手动开启

module.exports = {
  plugins: [new webpack.optimize.ModuleConcatenationPlugin()]
}

webpack4 mode 为 “production” 默认开启

必须是 ES6 模块,cjs 不支持

ESLint

如何落地:

  • 和 CI/CD 系统集成
  • 和 webpack 集成

husky + lint-staged

webpack 和 eslint 集成

使用 eslint-loader,构建时检查 JS 规范

优化构建时命令行的显示

webpack 统计信息 stats

| Preset | Alternative | Description | | :---------------- | :---------- | :----------------------------- | | "errors-only" | none | 只在发生错误时显示 | | "minimal" | none | 只有发生错误或有新的编译时输出 | | "none" | false | 没有输出 | | " normal" | true | 标准输出 | | "verbose" | none | 全部输出 | | "detailed" | | | | "errors-warnings" | | |

// webpack.prod.config.js
module.exports = {
  stats: 'errors-only'
}

// webpack.dev.config.js
module.exports = {
  devServer: {
    stats: 'errors-only'
  }
}

friendly-errors-webpack-plugin

// webpack.config.js

const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
module.exports = {
  plugins: [new FriendlyErrorsWebpackPlugin()]
}

FriendlyErrorsWebpackPlugin 需要和 stats配合使用

webpack 错误处理和异常捕获

如何判断构建是否成功

在 CI/CD 的 pipline 或者发布系统需要知道当前的构建状态

每次构建完成后输入 echo $?获取错误码

webpack4 之前的版本构建失败不会抛出错误码(error code)

Node.js 中的 process.exit 规范:

  • 0 表示成功完成,回调函数中,err 为 null
  • 非 0 表示执行失败,回调函数中,err 不为 null,err.code 就是传给 exit 的数字

如何主动捕获并处理构建错误?

webpack 中的 compiler 在每次构建结束后会触发 done 这个 hook, process.exit 主动处理构建报错

// webpack.config.js

module.exports = {
  plugins: [
    function() {
      /*
      * webpack3写法
      * this.plugin(done, stats => {})
      */
      this.hooks.done.tap('done', stats => {
        if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf('--watch') === -1) {
          console.log('build error')
          process.exit(1)
        }
      })
    }
  ]
}

冒烟测试

  • 构建是否成功
  • 每次构建完成 build 目录是否有内容输出
    • 是否有 JS、CSS 等静态资源文件
    • 是否有 HTML 文件

npm发布

// 第一步: 登陆 npm
npm login

// 第二步:发布
npm publish --access=public

如果在包前面要加作用范围,有两种方式:

第一种是直接在 package.json中修改 name 字段

{
  "name": "@luichooy/webpack-build"
}

第二种是初始化项目是使用 npm init 的方式

npm init --scope=usename

发包时的版本管理

npm version patch

npm version minor

npm version major

运行 npm version时会自动执行 git tag命令为本次发布打上标签