├── .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 | ![image-20210228004846012](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/8d2c643391e04c319548827e4adef444~tplv-k3u1fbpfcp-zoom-1.image) 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 | ![image-20210228102928287](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/134d33f3528d497d992855178aebce39~tplv-k3u1fbpfcp-zoom-1.image) 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 | ![image-20210228110624352](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/46a4750428ee451189eba42e090fe1cd~tplv-k3u1fbpfcp-zoom-1.image) 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 | ![image-20210228114440107](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/96572954a479431b8ab2051f8b46d98b~tplv-k3u1fbpfcp-zoom-1.image) 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 | --------------------------------------------------------------------------------