├── .env
├── .github
└── workflows
│ └── webpack.yml
├── .gitignore
├── .swcrc
├── LICENSE
├── README.md
├── build
└── index.html
├── eslint.config.mjs
├── package.json
├── postcss.config.js
├── renovate.json
├── src
├── App.less
├── App.tsx
└── index.tsx
├── tsconfig.json
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js
/.env:
--------------------------------------------------------------------------------
1 | TEST=123
--------------------------------------------------------------------------------
/.github/workflows/webpack.yml:
--------------------------------------------------------------------------------
1 | name: NodeJS with Webpack
2 |
3 | on:
4 | push:
5 | branches: [ "main" ]
6 | pull_request:
7 | branches: [ "main" ]
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 |
13 | strategy:
14 | matrix:
15 | node-version: [18.x, 20.x]
16 |
17 | steps:
18 | - uses: actions/checkout@v4
19 |
20 | - name: Use Node.js ${{ matrix.node-version }}
21 | uses: actions/setup-node@v4
22 | with:
23 | node-version: ${{ matrix.node-version }}
24 | - name: Use Pnpm v9.14.1
25 | uses: pnpm/action-setup@v2
26 | with:
27 | version: 9.14.1
28 |
29 | - name: Build
30 | run: |
31 | pnpm install
32 | pnpm build
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .idea
4 |
--------------------------------------------------------------------------------
/.swcrc:
--------------------------------------------------------------------------------
1 | {
2 | "jsc": {
3 | "parser": {
4 | "syntax": "typescript",
5 | "tsx": true,
6 | "dynamicImport": true
7 | },
8 | "transform": {
9 | "react": {
10 | "pragma": "React.createElement",
11 | "pragmaFrag": "React.Fragment"
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Jacob
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 从零开始使用 webpack5 搭建 react 项目
2 |
3 | > 已集成功能
4 | > - [x] cache 缓存提升热更新速度
5 | > - [x] style module
6 | > - [x] HMR
7 | > - [x] lodash-es
8 | > - [x] antd v5
9 | > - [x] dayjs
10 | > - [x] swc
11 | > - [x] lightningcss
12 | >
13 |
14 | > 本文的示例项目源码可以点击 [这里](https://github.com/jacob-lcs/react-webpack5-template) 获取
15 |
16 | ## 一、前言
17 |
18 | webpack5 也已经发布一段时间了,其模块联邦、bundle 缓存等新特性值得在项目中进行使用。经过笔者在公司实际项目中的升级结果来看,其提升效果显著,热更新时间由原来的 8s 减少到了 2s,会极大的提升开发幸福感。除此之外,webpack5 也带来了更好的 tree shaking 算法,项目的打包体积也会进一步减少,提升用户体验。
19 |
20 | 目前来看,create-react-app 脚手架还没有适配 webpack5,如果你想熟悉下如何从零开始配置 webpack5 项目的话,不妨跟着文档操作一下。
21 |
22 | ## 二、项目初始化
23 |
24 | ### 2.1 初始化文件结构
25 |
26 | 首先创建一个文件夹,进行 npm 初始化
27 |
28 | ```bash
29 | mkdir react-webpack5-template
30 | cd react-webpack5-template
31 | # npm 初始化配置
32 | npm init -y
33 | # 创建 webpack 配置文件
34 | touch webpack.common.js
35 | # 创建 babel 配置文件
36 | mkdir src && cd src
37 | # 创建入口文件
38 | touch index.js
39 | cd .. && mkdir build
40 | touch index.html
41 | ```
42 |
43 | 在上述步骤执行完毕之后,你的目录结构应该如下所示:
44 |
45 | ```
46 | ├── src
47 | │ └── index.js
48 | ├── build
49 | │ └── index.html
50 | ├── webpack.common.js
51 | ├── .babelrc
52 | ├── package.json
53 | ```
54 |
55 | 随后安装必要的依赖
56 |
57 | ```bash
58 | npm i webpack webpack-cli webpack-dev-server html-webpack-plugin babel-loader path -D
59 | npm i react react-dom
60 | ```
61 |
62 | ### 2.2 完善配置文件
63 |
64 | 文件结构生成完毕后,我们开始编写代码。首先,在`index.js` 中写入以下代码:
65 |
66 | ```jsx
67 | import React from 'react';
68 | import ReactDOM from 'react-dom';
69 |
70 | ReactDOM.render(
71 |
72 | 你好,React-webpack5-template
73 | ,
74 | document.getElementById('root')
75 | );
76 | ```
77 |
78 | 在` webpack.common.js` 中写入以下内容:
79 |
80 | ```js
81 | const path = require("path");
82 | const HtmlWebpackPlugin = require("html-webpack-plugin");
83 |
84 | module.exports = (env) => {
85 | return {
86 | mode: "development",
87 | entry: {
88 | index: './src/index.js'
89 | },
90 | output: {
91 | // 打包文件根目录
92 | path: path.resolve(__dirname, "dist/"),
93 | },
94 | plugins: [
95 | // 生成 index.html
96 | new HtmlWebpackPlugin({
97 | filename: "index.html",
98 | template: "./build/index.html",
99 | }),
100 | ],
101 | module: {
102 | rules: [
103 | {
104 | test: /\.(jsx|js)?$/,
105 | use: ["babel-loader"],
106 | include: path.resolve(__dirname, 'src'),
107 | },
108 | ]
109 | },
110 | devServer: {
111 | port: 8080,
112 | host: '0.0.0.0',
113 | },
114 | }
115 | }
116 | ```
117 |
118 | 在 `index.html` 中写入以下代码:
119 |
120 | ```html
121 |
122 |
123 |
124 |
125 | Title
126 |
127 |
128 |
129 |
130 |
131 | ```
132 |
133 | 在 `.babalrc` 中写入以下代码:
134 |
135 | ```json
136 | {
137 | "presets": ["@babel/preset-react"]
138 | }
139 | ```
140 |
141 | 然后在 package.json 中添加如下 script:
142 |
143 | ```diff
144 | "scripts": {
145 | "test": "echo \"Error: no test specified\" && exit 1",
146 | + "dev": "webpack serve --config webpack.common.js"
147 | },
148 | ```
149 |
150 | 随后我们运行 `npm run dev` 就可以直接运行了,由于我们上面设置的 devServer 端口号为 8080,所以在浏览器中打开 `localhost:8080` 即可看到如下效果:
151 |
152 | 
153 |
154 | 到这里位置,我们的初步搭建已经完成了,但是我们在现有的项目中看到的 webpack 配置文件不止这些,有 less、css 文件的解析,image 等资源文件的处理,还有一些优化项的配置等,接下来会一一介绍。
155 |
156 | ## 三、功能性配置
157 |
158 | 上面我们已经做到可以将一个简单的 React 项目运行起来了,接下来我们要做的是加一些功能。
159 |
160 | ### 3.1 样式文件解析
161 |
162 | 在前端项目开发过程中,比较经常使用的是 css、less、scss、sass、stylus,下面我们就先仅对 less 进行配置,其余的样式文件可参考 GitHub 源码。首先安装 loader:
163 |
164 | ```bash
165 | npm i style-loader less-loader less css-loader postcss-loader postcss-normalize autoprefixer postcss-preset-env -D
166 | ```
167 |
168 | 首先,在 webpack.common.js 顶部加入以下正则表达式,用来判断样式文件:
169 |
170 | ```js
171 | // less/less module 正则表达式
172 | const lessRegex = /\.less$/;
173 | const lessModuleRegex = /\.module\.less$/;
174 | ```
175 |
176 | 然后在 webpack.common.js 中加入以下配置:
177 |
178 | ```js
179 | module: {
180 | rules: [
181 | {
182 | test: lessRegex,
183 | use: ["style-loader", "css-loader", "postcss-loader", "less-loader"],
184 | sideEffects: true,
185 | },
186 | ]
187 | }
188 | ```
189 |
190 | 新增 postcss.config.js 文件并配置:
191 |
192 | ```js
193 | const postcssNormalize = require('postcss-normalize');
194 |
195 | module.exports = {
196 | plugins: [
197 | [
198 | "postcss-preset-env",
199 | {
200 | autoprefixer: {
201 | flexbox: "no-2009",
202 | },
203 | stage: 3,
204 | }
205 | ],
206 | postcssNormalize(),
207 | require('autoprefixer') ({
208 | overrideBrowserslist: ['last 2 version', '>1%', 'ios 7']
209 | })
210 | ],
211 | };
212 | ```
213 |
214 |
215 |
216 | 然后我们在 src 目录下新建 index.less 文件,测试配置是否成功:
217 |
218 | ```less
219 | // index.less
220 | .title {
221 | text-align: center;
222 | color: coral;
223 | }
224 | ```
225 |
226 | 重新运行项目后发现样式生效,配置成功。
227 |
228 | 
229 |
230 | 但是仅配置 less 是不够的,我们日常在开发过程中经常用到 less module,在这里我们进行如下配置,首先安装 `react-dev-utils`:
231 |
232 | ```bash
233 | npm i react-dev-utils resolve-url-loader -D
234 | ```
235 |
236 | 在 webpack.common.js 中进行如下配置:
237 |
238 | ```diff
239 | const getCSSModuleLocalIdent = require("react-dev-utils/getCSSModuleLocalIdent");
240 |
241 | module: {
242 | rules: [
243 | {
244 | test: lessRegex,
245 | + exclude: lessModuleRegex,
246 | use: ["style-loader", "css-loader", "postcss-loader", "less-loader"],
247 | sideEffects: true,
248 | },
249 | + {
250 | + test: lessModuleRegex,
251 | + use: [
252 | + "style-loader",
253 | + {
254 | + loader: "css-loader",
255 | + options: {
256 | + modules: {
257 | + getLocalIdent: getCSSModuleLocalIdent,
258 | + }
259 | + }
260 | + },
261 | + "postcss-loader",
262 | + "less-loader"
263 | + ],
264 | + }
265 | ]
266 | }
267 | ```
268 |
269 | 接下来我们新建 index.module.less 来进行测试:
270 |
271 | ```less
272 | .font {
273 | color: red;
274 | }
275 | ```
276 |
277 | 重新运行项目后样式生效,并且 className 也发生了相应变化:
278 |
279 | 
280 |
281 | CSS、SCSS 与 SASS 的配置都大同小异,大家可以移步到我的 [GitHub](https://github.com/jacob-lcs/react-webpack5-template)。
282 |
283 | ### 3.2 图片地址解析
284 |
285 | > 资源模块(asset module)是一种模块类型,它允许使用资源文件(字体,图标等)而无需配置额外 loader。
286 | >
287 | > 在 webpack 5 之前,通常使用:
288 | >
289 | > - [`raw-loader`](https://webpack.docschina.org/loaders/raw-loader/) 将文件导入为字符串
290 | > - [`url-loader`](https://webpack.docschina.org/loaders/url-loader/) 将文件作为 data URI 内联到 bundle 中
291 | > - [`file-loader`](https://webpack.docschina.org/loaders/file-loader/) 将文件发送到输出目录
292 | >
293 | > 资源模块类型(asset module type),通过添加 4 种新的模块类型,来替换所有这些 loader:
294 | >
295 | > - `asset/resource` 发送一个单独的文件并导出 URL。之前通过使用 `file-loader` 实现。
296 | > - `asset/inline` 导出一个资源的 data URI。之前通过使用 `url-loader` 实现。
297 | > - `asset/source` 导出资源的源代码。之前通过使用 `raw-loader` 实现。
298 | > - `asset` 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 `url-loader`,并且配置资源体积限制实现。
299 | >
300 | > —— 引自 webpack5 中文文档
301 |
302 | webpack5 内置 assets 类型,我们不需要额外安装插件就可以进行图片等资源文件的解析,配置如下:
303 |
304 | ```javascript
305 | {
306 | test: /\.(jpe?g|png|gif|svg|woff|woff2|eot|ttf|otf)$/i,
307 | type: "asset/resource"
308 | },
309 | ```
310 |
311 | 如此我们便可以处理引入的图片资源文件,可以根据自身需要进行拓展。
312 |
313 | ## 四、性能优化
314 |
315 | ### 4. 1 引入缓存
316 |
317 | 前面提到,webpack5 引入了缓存来提高二次构建速度,我们只需要在 webpack 配置文件中加入如下代码即可开心缓存
318 |
319 | ```js
320 | cache: {
321 | type: 'filesystem',
322 | // 可选配置
323 | buildDependencies: {
324 | config: [__filename], // 当构建依赖的config文件(通过 require 依赖)内容发生变化时,缓存失效
325 | },
326 | name: 'development-cache',
327 | },
328 | ```
329 |
330 | 重新运行项目后会发现 node_modules 目录下会新增一个 .cache 文件夹:
331 |
332 | 
333 |
334 | 笔者在实际项目中测试,热更新时间由原来的 8s 缩短到 2s 可以说是提升巨大。
335 |
336 | #### 4.2 引入多线程
337 | 为了提升构建速度,我们可以引入 `thread-loader` 提升构建速度,首先我们需要安装:
338 | ```bash
339 | npm i thread-loader -D
340 | ```
341 | 然后在 `webpack.common.js` 中进行配置:
342 | ```diff
343 | {
344 | test: /\.(jsx|js)?$/,
345 | - use: ["babel-loader"],
346 | + use: ["thread-loader", "babel-loader"],
347 | include: path.resolve(__dirname, 'src'),
348 | },
349 | ```
350 |
351 | ## 五、总结
352 |
353 | 到目前为止,配置工作算是已经完成了,本篇文章只是指导大家进行一些初始化配置,项目中肯定还有很多可以优化的地方,比如说分别配置 webpack.dev.js 以及 webpack.prod.js 以通过测试环境与正式环境的不同需求,在这里就不细说,环境区分的相关配置我会上传到 GitHub 中,如果你觉得项目对你有点用处的话,还请点个 star。
354 |
--------------------------------------------------------------------------------
/build/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Title
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import globals from "globals";
2 | import pluginJs from "@eslint/js";
3 | import tseslint from "typescript-eslint";
4 | import pluginReact from "eslint-plugin-react";
5 | import reactCompiler from 'eslint-plugin-react-compiler'
6 |
7 | /** @type {import('eslint').Linter.Config[]} */
8 | export default [
9 | { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] },
10 | { languageOptions: { globals: globals.browser } },
11 | pluginJs.configs.recommended,
12 | ...tseslint.configs.recommended,
13 | pluginReact.configs.flat.recommended,
14 | {
15 | plugins: {
16 | 'react-compiler': reactCompiler,
17 | },
18 | rules: {
19 | 'react-compiler/react-compiler': 'error',
20 | },
21 | },
22 | ];
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-webpack5-template",
3 | "version": "1.1.0",
4 | "description": "webpack5 react 模板",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "webpack serve --config webpack.dev.js --hot",
8 | "build": "webpack --config webpack.prod.js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/jacob-lcs/react-webpack5-template.git"
13 | },
14 | "keywords": [],
15 | "author": "",
16 | "license": "ISC",
17 | "bugs": {
18 | "url": "https://github.com/jacob-lcs/react-webpack5-template/issues"
19 | },
20 | "homepage": "https://github.com/jacob-lcs/react-webpack5-template#readme",
21 | "dependencies": {
22 | "antd": "^5.22.1",
23 | "dayjs": "^1.11.13",
24 | "lodash-es": "^4.17.21",
25 | "react": "^19.0.0",
26 | "react-dom": "^19.0.0"
27 | },
28 | "devDependencies": {
29 | "@eslint/js": "^9.16.0",
30 | "@swc/core": "^1.9.2",
31 | "@types/lodash-es": "^4.17.12",
32 | "@types/node": "^22.9.1",
33 | "@types/react": "^19.0.0",
34 | "@types/react-dom": "^19.0.0",
35 | "autoprefixer": "^10.4.20",
36 | "browserslist": "^4.24.2",
37 | "css-loader": "^7.1.2",
38 | "css-minimizer-webpack-plugin": "^7.0.0",
39 | "dotenv-webpack": "^8.1.0",
40 | "eslint": "^9.16.0",
41 | "eslint-plugin-react": "^7.37.2",
42 | "eslint-plugin-react-compiler": "19.0.0-beta-e552027-20250112",
43 | "globals": "^16.0.0",
44 | "html-webpack-plugin": "^5.6.3",
45 | "less": "^4.2.0",
46 | "less-loader": "^12.2.0",
47 | "lightningcss": "^1.28.1",
48 | "mini-css-extract-plugin": "^2.9.2",
49 | "path": "^0.12.7",
50 | "postcss": "^8.4.49",
51 | "postcss-loader": "^8.1.1",
52 | "postcss-normalize": "^13.0.1",
53 | "postcss-preset-env": "^10.1.1",
54 | "react-compiler-webpack": "^0.1.2",
55 | "resolve-url-loader": "^5.0.0",
56 | "sass": "^1.81.0",
57 | "sass-loader": "^16.0.3",
58 | "style-loader": "^4.0.0",
59 | "stylus": "^0.64.0",
60 | "stylus-loader": "^8.1.1",
61 | "swc-loader": "^0.2.6",
62 | "terser-webpack-plugin": "^5.3.10",
63 | "thread-loader": "^4.0.4",
64 | "typescript": "^5.6.3",
65 | "typescript-eslint": "^8.18.0",
66 | "webpack": "^5.96.1",
67 | "webpack-bundle-analyzer": "^4.10.2",
68 | "webpack-cli": "^6.0.0",
69 | "webpack-dev-server": "^5.1.0",
70 | "webpack-manifest-plugin": "^5.0.0",
71 | "webpack-merge": "^6.0.1",
72 | "webpackbar": "^7.0.0"
73 | },
74 | "engines": {
75 | "pnpm": "^9.0.0"
76 | }
77 | }
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | const postcssNormalize = require('postcss-normalize');
2 |
3 | module.exports = {
4 | plugins: [
5 | [
6 | "postcss-preset-env",
7 | {
8 | autoprefixer: {
9 | flexbox: "no-2009",
10 | },
11 | stage: 3,
12 | }
13 | ],
14 | postcssNormalize(),
15 | require('autoprefixer') ({
16 | overrideBrowserslist: ['last 2 version', '>1%', 'ios 7']
17 | })
18 | ],
19 | };
20 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:recommended"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/src/App.less:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 100px;
3 |
4 | .time {
5 | box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
6 | padding: 20px;
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Statistic, Row, Col } from 'antd'
3 | import { set } from 'lodash-es'
4 | import dayjs from 'dayjs'
5 | import './App.less';
6 |
7 | const { Countdown } = Statistic;
8 |
9 | const App = () => {
10 | const deadline = dayjs().unix() * 1000 + 1000 * 60 * 60 * 24 * 2 + 1000 * 30;
11 | const onFinish = () => {
12 | const a = {}
13 | set(a, 'finish', true)
14 | }
15 |
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | )
31 | }
32 |
33 | export default App
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { createRoot } from 'react-dom/client';
3 |
4 | import App from './App'
5 |
6 | // Render your React component instead
7 | const root = createRoot(document.getElementById('root') as Element);
8 | root.render();
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 |
5 | /* Projects */
6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
12 |
13 | /* Language and Environment */
14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
16 | "jsx": "preserve", /* Specify what JSX code is generated. */
17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
26 |
27 | /* Modules */
28 | "module": "commonjs", /* Specify what module code is generated. */
29 | // "rootDir": "./", /* Specify the root folder within your source files. */
30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
42 | // "resolveJsonModule": true, /* Enable importing .json files. */
43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
45 |
46 | /* JavaScript Support */
47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
50 |
51 | /* Emit */
52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
58 | // "outDir": "./", /* Specify an output folder for all emitted files. */
59 | // "removeComments": true, /* Disable emitting comments. */
60 | // "noEmit": true, /* Disable emitting files from a compilation. */
61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
68 | // "newLine": "crlf", /* Set the newline character for emitting files. */
69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
74 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
75 |
76 | /* Interop Constraints */
77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
78 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
83 |
84 | /* Type Checking */
85 | "strict": true, /* Enable all strict type-checking options. */
86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
104 |
105 | /* Completeness */
106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/webpack.common.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const HtmlWebpackPlugin = require("html-webpack-plugin");
3 | const WebpackBar = require("webpackbar");
4 | const Dotenv = require("dotenv-webpack");
5 | const {
6 | defineReactCompilerLoaderOption,
7 | reactCompilerLoader,
8 | } = require("react-compiler-webpack");
9 |
10 | // css/css module 正则表达式
11 | const cssRegex = /\.css$/;
12 | const cssModuleRegex = /\.module\.css$/;
13 | // sass/sass module 正则表达式
14 | const sassRegex = /\.(scss|sass)$/;
15 | const sassModuleRegex = /\.module\.(scss|sass)$/;
16 | // less/less module 正则表达式
17 | const lessRegex = /\.less$/;
18 | const lessModuleRegex = /\.module\.less$/;
19 | // stylus/stylus module 正则表达式
20 | const stylRegex = /\.styl$/;
21 | const stylModuleRegex = /\.module\.styl$/;
22 |
23 | module.exports = () => {
24 | return {
25 | mode: "development",
26 | entry: {
27 | index: "./src/index.tsx",
28 | },
29 | target: "web",
30 | output: {
31 | // 打包文件根目录
32 | path: path.resolve(__dirname, "dist/"),
33 | },
34 | plugins: [
35 | // 生成 index.html
36 | new HtmlWebpackPlugin({
37 | filename: "index.html",
38 | template: "./build/index.html",
39 | }),
40 | new WebpackBar(),
41 | new Dotenv(),
42 | ],
43 | resolve: {
44 | extensions: [".tsx", ".jsx", ".ts", ".js", ".json", ".wasm"],
45 | },
46 | module: {
47 | rules: [
48 | {
49 | test: /\.(jsx|js|ts|tsx)?$/,
50 | use: [
51 | "thread-loader",
52 | "swc-loader",
53 | {
54 | loader: reactCompilerLoader,
55 | options: defineReactCompilerLoaderOption({
56 | // React Compiler options goes here
57 | }),
58 | },
59 | ],
60 | include: path.resolve(__dirname, "src"),
61 | },
62 | {
63 | test: cssRegex,
64 | exclude: cssModuleRegex,
65 | use: ["style-loader", "css-loader", "postcss-loader"],
66 | },
67 | {
68 | test: cssModuleRegex,
69 | use: [
70 | "style-loader",
71 | {
72 | loader: "css-loader",
73 | options: {
74 | modules: true,
75 | },
76 | },
77 | "postcss-loader",
78 | ],
79 | },
80 | {
81 | test: sassRegex,
82 | exclude: sassModuleRegex,
83 | use: ["style-loader", "css-loader", "postcss-loader", "sass-loader"],
84 | },
85 | {
86 | test: sassModuleRegex,
87 | use: [
88 | "style-loader",
89 | {
90 | loader: "css-loader",
91 | options: {
92 | modules: true,
93 | },
94 | },
95 | "postcss-loader",
96 | "sass-loader",
97 | ],
98 | },
99 | {
100 | test: stylRegex,
101 | exclude: stylModuleRegex,
102 | use: [
103 | "style-loader",
104 | "css-loader",
105 | "postcss-loader",
106 | "stylus-loader",
107 | ],
108 | },
109 | {
110 | test: stylModuleRegex,
111 | use: [
112 | "style-loader",
113 | {
114 | loader: "css-loader",
115 | options: {
116 | modules: true,
117 | },
118 | },
119 | "postcss-loader",
120 | "stylus-loader",
121 | ],
122 | },
123 | {
124 | test: lessRegex,
125 | exclude: lessModuleRegex,
126 | use: ["style-loader", "css-loader", "postcss-loader", "less-loader"],
127 | sideEffects: true,
128 | },
129 | {
130 | test: lessModuleRegex,
131 | use: [
132 | "style-loader",
133 | {
134 | loader: "css-loader",
135 | options: {
136 | modules: true,
137 | },
138 | },
139 | "postcss-loader",
140 | "less-loader",
141 | ],
142 | },
143 | {
144 | test: /\.(jpe?g|png|gif|svg|woff|woff2|eot|ttf|otf)$/i,
145 | type: "asset/resource",
146 | },
147 | ],
148 | },
149 | };
150 | };
151 |
--------------------------------------------------------------------------------
/webpack.dev.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const common = require('./webpack.common.js');
3 |
4 | module.exports = env => {
5 | return merge(common(env), {
6 | mode: 'development',
7 | cache: {
8 | type: 'filesystem',
9 | // 可选配置
10 | buildDependencies: {
11 | config: [__filename], // 当构建依赖的config文件(通过 require 依赖)内容发生变化时,缓存失效
12 | },
13 | name: 'development-cache', // 配置以name为隔离,创建不同的缓存文件,如生成PC或mobile不同的配置缓存
14 | },
15 | devServer: {
16 | port: 8081,
17 | host: '0.0.0.0',
18 | allowedHosts: 'all'
19 | },
20 | optimization: {
21 | moduleIds: "deterministic",
22 | },
23 | });
24 | }
25 |
--------------------------------------------------------------------------------
/webpack.prod.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge');
2 | const common = require('./webpack.common.js');
3 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
4 | const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
5 | const MiniCssExtractPlugin = require("mini-css-extract-plugin");
6 | const TerserPlugin = require('terser-webpack-plugin');
7 | const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
8 | const lightningcss = require('lightningcss');
9 | const browserslist = require('browserslist');
10 |
11 | module.exports = env => {
12 | return merge(common(env), {
13 | mode: 'production',
14 | output: {
15 | clean: true,
16 | filename: 'js/[name].[contenthash].js',
17 | chunkFilename: 'js/[name].[contenthash].js',
18 | assetModuleFilename: 'assets/[name].[contenthash][ext]',
19 | },
20 | plugins: [
21 | // 打包分析
22 | // new BundleAnalyzerPlugin(),
23 | // 生成 manifest.json
24 | new WebpackManifestPlugin(),
25 | // 将 css 从 js 中分离
26 | new MiniCssExtractPlugin({
27 | filename: "css/[name].[contenthash].css",
28 | }),
29 | ],
30 | optimization: {
31 | minimize: true,
32 | minimizer: [
33 | new TerserPlugin({
34 | parallel: true,
35 | }),
36 | new CssMinimizerPlugin({
37 | minify: CssMinimizerPlugin.lightningCssMinify,
38 | minimizerOptions: {
39 | targets: lightningcss.browserslistToTargets(browserslist('>= 0.25%'))
40 | },
41 | }),
42 | ],
43 | splitChunks: {
44 | chunks: 'all',
45 | cacheGroups: {
46 | libAntd: {
47 | test: /[\\/]node_modules[\\/]antd[\\/]/, // 匹配 antd 库
48 | name: 'lib-antd', // 输出的文件名
49 | priority: 20, // 优先级,数值越大优先级越高
50 | reuseExistingChunk: true, // 复用已存在的 chunk
51 | },
52 | libLodash: {
53 | test: /[\\/]node_modules[\\/]antd[\\/]/, // 匹配 antd 库
54 | name: 'lib-lodash', // 输出的文件名
55 | priority: 20, // 优先级,数值越大优先级越高
56 | reuseExistingChunk: true, // 复用已存在的 chunk
57 | },
58 | },
59 | },
60 | },
61 | });
62 | }
63 |
--------------------------------------------------------------------------------