返回首页

Webpack 配置笔记

ghoti-cli 全静态 React 模板里踩过的几个 webpack 配置坑。

发布 2018年3月28日 标签 #javascript #webpack

~/posts/webpack-config-notes $ cat post.md

/ 语言 EN / 中文
/ 主题 / /

概览

Webpack 的配置到处是坑。本篇借 ghoti-cli 里”全静态 React”模板的配置经验,整理几个值得记的点。

什么是 ghoti-cli

Ghoti-cli 是我自用还算顺手的一个项目管理 CLI,提供十几个快启模板和进度管理功能。其中最容易踩坑的一份配置就是它的”全静态 React”模板。

什么是”全静态 React”

思路是这样的:先按正常 React dev 项目写——支持热更新、router 等标准功能。写完之后用服务端渲染把页面渲染成字符串,缓存在内存里,再用 Express 起一个服务器,请求进来就把缓存返回。因为服务器本身是 webpack 打包的,源文件改动后没法热加载——这没什么关系:服务器实现了多线程监听,需要的时候多实例分别重启就能在不影响访问的情况下生效。

热更新的入口配置

webpack.dev.js 里的入口大概长这样:

entry: [
    "react-hot-loader/patch",
    "webpack-dev-server/client",
    "webpack/hot/only-dev-server",
    APP_DIR + "/index.dev.tsx",
],

第一、二、四个比较好理解。第三个字面意思是”在开发服务器中接收热更新作为入口”,看起来很多余——既然这个配置文件只在 dev 跑,为什么不删掉?

读源码发现这一行其实是真正让热更新生效的关键配置。第一个入口处理的是”非组件级”的整页刷新场景,第三个才是热更新的 runtime 入口。

总之这套热更新入口配置就保持不动。

打 Node 包时的图片处理

打包图片是 webpack 常用功能之一,把 logo 之类的小图打进 bundle,可以省一次 HTTP 请求。比如:

{
    test: /\.(jpg|png|gif|webp)$/,
    loader: "url-loader?limit=8192",
}

这个 rule 让我们能直接 require 图片。limit 的单位是 byte,也就是说 ≤ 8 KB 的图片会被转成 base64 内联进 bundle,大于这个值的图片会复制到目标目录、文件名替换为 hash,require 的目标也自动改成对应 hash 的路径。

之所以值得提一下:大多数情况下我们用这个 loader 而不是直接放一张外链图,是因为要复用这张图、希望减少请求数量。一个典型用例是把图片当成按钮或 logo——所以 limit 的取值要权衡:太低很多图片走外链反而增加请求,太高 bundle 又胖。

CSS 和 JS 分离输出

服务端返回纯 HTML 字符串时,我们尽量不让里面带 JS 代码(ghoti-cli 提供一种外部无依赖的 JS 运行方法,能覆盖 React 里大多数无交互效果)。但 CSS 还是不要嵌进 HTML 的 style 标签更好。

我用这个配置把 CSS 单独抽出来:

{
    test: /\.sass$/,
    use: ExtractTextWebpackPlugin.extract("css-loader?sourceMap!sass-loader?sourceMap"),
}

ExtractTextWebpackPlugin 在 webpack 4+ 已经被 MiniCssExtractPlugin 替代,这里的写法仅适用于 webpack 3 时代的项目。

“魔法字符串”的细节

css-loader?sourceMap!sass-loader?sourceMap 这个字符串看起来很玄。直觉上可能会想写:

"css-loader!post-css!sass-loader";

不行——首先 post-css 没法在这个插件里跑;其次 sourceMap 不能省,因为 ExtractTextWebpackPlugin 内部要靠它的依赖图把 CSS 正确输出。照上面的写法抄就行。

记得把插件实例加到 plugins 列表里:

plugins: [
    new ExtractTextWebpackPlugin("bundle.css"),
    // 或者用 webpack 通配符命名
    new ExtractTextWebpackPlugin("[name].[hash].css"),
];

别用 style-loader

非 Node 静态渲染的项目里,webpack 通常这么配:

{
    test: /\.sass$/,
    use: [
        "style-loader",
        "css-loader",
        "sass-loader",
    ],
},

style-loader 会把 CSS 注入到 React 的 className 里(实现”只有 JS 文件”的效果)。我们用 ExtractTextPlugin 单独抽 CSS,就要去掉 style-loader

返回首页