# 基本使用场景

# 解析代码、资源

# 解析 CSS、Less、Sass

以 Sass 为例,按顺序使用 sass-loader、css-loader、style-loader 即可。

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [ 'style-loader', 'css-loader', 'sass-loader' ]
      }
    ]
  }
}

# 解析图片、字体等二进制文件

可以使用 file-loader 或 url-loader 加载二进制文件,file-loader 更简单一些,我们以 file-loader 为例。

module.exports = {
  module: {
    rules: [
      {
        test: /\.(png|jpg|jpeg)$/,
        use: 'file-loader'
      }
    ]
  }
}

以 PNG 图片为例,file-loader 会将图片移动到输出目录,并修改文件名为:xxx.png。在业务中,我们可以像下面这样引入图片,代码中 logo 变量的值就是 xxx.png。

import logo from './logo.png'

const img = document.createElement('img')
img.src = logo
document.body.appendChild(img)

# 解析 React JSX

# 解析 Vue

# 解析 TypeScript

# 打包基础库

打包库一般会使用 Rollup,因为更加简单、纯粹,但用 Webpack 也可以。

基本需求为:

  • 支持打包压缩版、非压缩版本
  • 支持 AMD/CMD/ESM 引入
  • 单元测试
  • npm publish 发包,通过钩子配置发包前执行的命令

发包的流程是:

  • 执行 npm publish 命令
  • 自动执行钩子,例如 npm testnpm build 这样的命令
  • 然后就会根据 package.json 中 main 字段指定的入口发包

TODO: 详细代码待补充。

# 兼容性

# 自动添加 CSS 前缀

Autoprefixer (opens new window)

# 移动端 px 自动转 rem

# 工程、效率

# HtmlWebpackPlugin

可以自动生成一个 index.html,很方便,开发必备。最高级的地方在于可以在页面中自动插入一行 <script src="bundle.js">

const HtmlWebpackPlugin = require('html-webpack-plugin')

module.exports = {
  plugins: [
    new HtmlWebpackPlugin()
  ]
}

# 自动清理 dist 目录

使用 clean-webpack-plugin。

# 优化 Webpack 命令行输出

使用 stats 选项可以配置输出哪些信息,隐藏哪些信息。

也可以用 friendly-errors-webpack-plugin (opens new window) 插件。

# 其它

# 打包多页应用

单页应用叫做 SPA,多页应用叫做 MPA,MPA 会有多个入口文件。

传统 MPA 的配置方法
const path = require('path')
const HtmlwebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: {
    home: './src/home/index.js',
    about: './src/about/index.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name]_[hash:8].js'
  },
  plugins: [
    new CleanWebpackPlugin(),
    new HtmlwebpackPlugin({
      template: path.resolve(__dirname, './src/home/index.html'),
      filename: 'home.html',
      chunks: ['home']
    }),
    new HtmlwebpackPlugin({
      template: path.resolve(__dirname, './src/about/index.html'),
      filename: 'about.html',
      chunks: ['about']
    })
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-react']
            }
          }
        ]
      }
    ]
  },
}

上面的配置方法不够灵活,如果增加了新的页面,我们还需要手动修改 Webpack 配置文件。考虑到 Webpack 配置文件本身就是 JavaScript 代码,我们可以借此实现更灵活的打包。

关键思路如下:

  • 团队成员约定多页应用中,代码文件的组织规则
  • 利用 glob (opens new window) 这个包,根据约定的规则动态获取 src 目录下的文件
利用 glob 灵活打包多页应用
const path = require('path')
const HtmlwebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const glob = require('glob')

function generateMPAConfig() {
  const entryFiles = glob.sync('./src/*/index.js')
  // entryFiles === [ './src/about/index.js', './src/home/index.js' ]
  const htmlWebpackPlugins = []
  const entry = {}
  entryFiles.forEach((filePath) => {
    const match = filePath.match(/src\/(.+)\/index\.(js|ts)$/)
    const entryName = match && match[1]
    entry[entryName] = filePath
    htmlWebpackPlugins.push(new HtmlwebpackPlugin({
      template: path.resolve(__dirname, `./src/${entryName}/index.html`),
      filename: `${entryName}.html`,
      chunks: [entryName] 
    }))
  })
  return {
    entry,
    htmlWebpackPlugins
  }
}

const { entry, htmlWebpackPlugins } = generateMPAConfig()

module.exports = {
  mode: 'development',
  entry,
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name]_[hash:8].js'
  },
  plugins: [
    new CleanWebpackPlugin(),
    ...htmlWebpackPlugins
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['@babel/preset-react']
            }
          }
        ]
      }
    ]
  },
}