🧑💻 写在开头
上一篇文章手把手带你搭建前端项目:react18、ts5、lint四剑客、webpack、storybook【保姆级教程一】中我们配置了项目初始化文件,代码风格统一、代码质量检测、代码自动修复,ts基本配置等等。本篇文章我们继续完成上一篇文章未完成的工作,配置webpack打包,打包文件根据环境配置,开发服务器配置,css、less、sass、图片、字体、文件的打包。
你能学到什么?
希望在你阅读本篇文章之后,不会觉得浪费了时间。如果你跟着读下来,你将会学到:
- 🧑 配置一下生成更新日志changelog
- 🍺 配置webpack5基本打包流程
- 🍑 webpack5打包样式文件
- 🍒 webpack5打包图片,字体,等文件
- 🍎 使用rollup打包组件库代码
预告
后续我们将配置
- 支持react和tsx,实现基本的react+ts开发环境。
- 实现加入storybook编写组件
- 实现rollup打包组件库,本地测试组件库,发布组件库
🍎 系列文章
- 手把手带你搭建前端项目:react18、ts5、lint四剑客、webpack、storybook【保姆级教程一】
- 手把手带你搭建前端项目:react18、ts5、lint四剑客、webpack、storybook【保姆级教程二】
- 手把手带你搭建前端项目:react18、ts5、lint四剑客、webpack、storybook【保姆级教程三】
配置更新日志changelog
sql
yarn add [email protected] -D
在 package.json 的 scripts 下增加一个命令:
json
{
"scripts": {
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s"
},
}
之后就可以通过 npm run changelog 生成 angular 风格的 changelog ,需要注意的是,上面这条命令产生的 changelog 是基于上次 tag 版本之后的变更(feat、fix 等等)所产生的。
Webpack5 基本配置
这个图可以大致总结webpack的配置分类和逻辑,webpack的配置其实就是这么一个流程,在打包流程的基础上在优化开发体验和产物优化的配置
安装、配置
sql
yarn add [email protected] [email protected] -D
- webpack :这不必多说,其用于编译 JavaScript 模块。
- webpack-cli :此工具用于在命令行中运行 webpack。 紧接着我们在根目录下新建文件夹 scripts ,在之下再建一个文件夹 config ,在 config 中再建一个 .js 文件 webpack.common.js ,此结构如下:
arduino
scripts/
config/
webpack.common.js
后面会用到webpack-merge,提取公共的逻辑,区分开发环境和生产环境的配置。
打包出、入口input、output
这里就是配置webpack打包的输入输出。
在 Webpack 5 中,配置文件(通常是 webpack.config.js)的输入(entry)和输出(output)是最基本的部分,用于定义 Webpack 应当处理的文件以及它们处理完毕应当输出到哪里。
输入(entry)
Webpack 的 Entry 配置项用于定义项目的入口点。Webpack 会从这些入口点开始,解析出项目内的依赖关系。
基础的单个入口简写语法:
ini
module.exports = {
entry: './path/to/my/entry/file.js'
};
对象语法,当你需要更复杂的配置时使用:
css
module.exports = {
entry: {
main: './src/index.js',
app: './src/app.js'
// 可以添加更多入口
}
};
常见的配置选项值:
- 字符串:单一入口点简化写法。
- 数组:当你想对多个文件一起打包但只创建一个 bundle。
- 对象:多个入口点,可以创建多个 bundle。适合多页面应用。
输出(output)
配置输出属性(output)告诉 Webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值是 ./dist。 基础配置:
lua
const path = require('path');
module.exports = {
//...
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
}
};
多个入口起点时配置: 如果配置创建了多个单独的 "chunk"(例如,使用多个入口点或使用像 CommonsChunkPlugin 这样的插件),应该使用占位符(substitutions)来确保每个文件具有唯一的名称。
lua
module.exports = {
//...
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].bundle.js'
}
};
常见的配置选项值:
- path:输出文件所在的目录绝对路径。
- filename:指定输出文件的名称。
- publicPath:指定在浏览器中被引用的时候的公共的URL的路径。
- library:暴露库的名称。作为全局变量,当你的库加载完成后,入口起点的返回值将分配给这个变量。
- libraryTarget:在何种形式下导出库。
- chunkFilename:非入口(non-entry) chunk 文件的名称。
- assetModuleFilename:生成的资产(asset)文件名模版。 示例配置:
lua
module.exports = {
//...
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
publicPath: '/',
library: 'MyLibrary',
libraryTarget: 'umd',
chunkFilename: '[id].[chunkhash].js',
assetModuleFilename: 'images/[hash][ext][query]'
}
};
我们在webpack.common.js文件中配置如下,
lua
const path = require('path')
module.exports = {
entry: {
app: path.resolve(__dirname, '../../src/app.js'),
},
output: {
filename: 'js/[name].[hash:8].js',
path: path.resolve(__dirname, '../../dist'),
},
}
接下来在根目录创建 src 文件夹,新建 app.js 文件,输入以下代码:
ini
const root = document.querySelector('#root')
root.innerHTML = 'hello, webpack!'
现在我们尝试使用刚才的 webpack 配置对其进行打包,那如何操作呢? 打开 package.json ,为其添加一条 npm 命令:
json
{
"scripts": {
"build": "webpack --config ./scripts/config/webpack.common.js",
},
}
测试,在dist下就生成了打包的文件

这里有一个警告,这里是因为我们没有配置打包环境,后面我们配置了之后就不会有了

这里解释下output.filename的配置方式和原因,
在 Webpack 中,output.filename 属性是用来定义输出文件的名称的。你可以使用静态名称的字符串,但更常用的是利用占位符来动态地为每个输出文件指定名称。以下是可用的几种配置方式,和几种不同的哈希值类型:
Filename 配置方式: 静态名称:
vbnet
filename: 'bundle.js'
这将会输出一个名为 bundle.js 的文件。
使用入口点名称:
vbnet
filename: '[name].js'
这里的 [name] 是一个占位符,为每个入口点生成带有其名称的输出文件。
使用内部 chunk id:
vbnet
filename: '[id].js'
id\] 将会被替换为每个 chunk 的内部唯一标识符。 **根据内容生成的哈希值:** ```vbnet filename: '[contenthash].js' ``` \[contenthash\] 是文件内容的哈希值,一般用于缓存逻辑。 聚焦在哈希占位符,Webpack 提供了几种不同类型的哈希值,它们有不同的意图和使用场景: **Hash 值类型:** * `[hash]`:与整个项目的构建相关的哈希值。不论哪个文件有变化,整个项目构建的哈希值都会改变。 * `[chunkhash]`:与 Webpack 打包过程中生成的 chunk 相关的哈希值。同一 chunk 内文件没有变化时,\[chunkhash\] 是不变的。它可以用来优化浏览器缓存,因为不同的文件通常被打包进不同的 chunk。 * `[contenthash]`:与文件内容直接相关的哈希值。只有文件内容改变了,`[contenthash]` 才会改变。这在使用如 css-extract-plugin 分离 CSS 文件时非常有用,因为你可能只希望在 CSS 文件的内容实际发生变化时才改变文件名。 * `[modulehash]`:与单个模块相关的哈希值。 * `[fullhash]`:Webpack 5 引入的哈希值,用来代替旧版本中的 \[hash\]。 **Hash 值的区别:** * `[hash]` 和 `[fullhash]` 考虑的是整个编译过程。任何一个文件的改动都会导致所有输出文件的哈希值变化。这意味着即使只有一个文件被修改,缓存的所有文件也因为文件名改变而需要重新获取。 * `[chunkhash]` 是针对每个 chunk 的。如果不同的入口导致了不同的 chunk 输出,那么只有当特定 chunk 中的模块变化时,该 chunk 的文件名才会改变。这允许客户端缓存未变更的 chunks。 * `[contenthash]` 特别适用于 CSS 或其他静态资源离散文件的缓存策略。文件内容改变时,相关文件哈希值变化,未改变的文件则哈希值保持不变,使缓存更有效。 在使用哈希占位符时,可以指定哈希值的长度,如 `[hash:8]`,这将输出8个字符长度的哈希值,让文件名更为精简。 ## 提取公共变量 在 scripts 下新建一个 constant.js 文件,存放一些用到的常量,项目根目录的绝对路径,开发服务器的端口路径,项目名称等等。 ```arduino scripts/ config/ webpack.common.js + constant.js ``` 在里面定义我们的变量: ```ini const path = require('path') // 拼接绝对路径 const PROJECT_PATH = path.resolve(__dirname, '../') // 项目名 const PROJECT_NAME = path.parse(PROJECT_PATH).name module.exports = { PROJECT_PATH, PROJECT_NAME } ``` 然后在 webpack.common.js 中引入,修改代码: ```js const {resolve} = require('path'); const {PROJECT_PATH} = require('../constants'); module.exports = { entry: { app: resolve(PROJECT_PATH, './src/app.js'), }, output: { filename: 'js/[name].[hash:8].js', path: resolve(PROJECT_PATH, './dist'), }, }; ``` ## 区分开发/生产环境 开发环境不需要压缩代码,不需要提取css为单文件,但是需要开发服务器等优化开发体验的配置,但是生产环境又不需要,有的又是两个环境都需要的,所以我们需要区分开发、生产环境,我们可以使用[webpack-merge](https://link.juejin.cn?target=https%3A%2F%2Fgithub.com%2Fsurvivejs%2Fwebpack-merge "https://github.com/survivejs/webpack-merge")来配置,然后合并。 ```sql yarn add [email protected] -D ``` 在 scripts/config 下新建文件 webpack.dev.js 作为开发环境配置,并输入以下代码: ```js const { merge } = require('webpack-merge') const common = require('./webpack.common.js') module.exports = merge(common, { mode: 'development', }) ``` 同样地,在 scripts/config 下新建文件 webpack.prod.js 作为生产环境配置,并输入以下代码: ```javascript const { merge } = require('webpack-merge') const common = require('./webpack.common.js') module.exports = merge(common, { mode: 'production', }) ``` 对于mode我们可以使用环境变量去区分,就可以提取到common的配置中 ## cross-env可跨平台设置和使用环境变量 cross-env 可跨平台设置和使用环境变量,不同操作系统设置环境变量的方式不一定相同,比如 Mac 电脑上使用 export NODE_ENV=development ,而 Windows 电脑上使用的是 set NODE_ENV=development ,有了这个利器,我们无需在考虑操作系统带来的差异性。 ```sql yarn add [email protected] -D ``` 然后在 package.json 中添加修改以下代码: ```json { "scripts": { "start": "cross-env NODE_ENV=development webpack --config ./scripts/config/webpack.dev.js", "build": "cross-env NODE_ENV=production webpack --config ./scripts/config/webpack.prod.js", }, } ``` 修改 srcipt/constants.js 文件,增加一个公用布尔变量 isDev : ```js const path = require('path'); const PROJECT_PATH = path.resolve(__dirname, '../'); const PROJECT_NAME = path.parse(PROJECT_PATH).name; const isDev = process.env.NODE_ENV !== 'production'; module.exports = { PROJECT_PATH, PROJECT_NAME, isDev, }; ``` hash 值在开发环境中并不需要,于是我们修改 webpack.common.js 文件: ```js const {resolve} = require('path'); const {PROJECT_PATH, isDev} = require('../constants'); module.exports = { mode: isDev ? 'development' : 'production', entry: { app: resolve(PROJECT_PATH, './src/app.js'), }, output: { filename: `js/[name]${isDev ? '' : '.[hash:8]'}.js`, path: resolve(PROJECT_PATH, './dist'), }, }; ``` ## 本地服务实时查看页面 ```sql yarn add [email protected] [email protected] -D ``` 简单介绍一下两个工具的作用: * html-webpack-plugin :每一个页面是一定要有 html 文件的,而这个插件能帮助我们将打包后的 js 文件自动引进 html 文件中,毕竟你不可能每次更改代码后都手动去引入 js 文件。 * webpack-dev-server :可以在本地起一个 http 服务,通过简单的配置还可指定其端口、热更新的开启等。 现在,我们先在项目根目录下新建一个 public 文件夹,里面存放一些公用的静态资源,现在我们先在其中新建一个 index.html ,写入以下内容: ```html