@modern-js/main-doc 2.40.0 → 2.42.0
Sign up to get free protection for your applications and to get access to all the features.
- package/docs/en/apis/app/commands.mdx +3 -1
- package/docs/en/community/contributing-guide.mdx +10 -0
- package/docs/en/configure/app/output/enable-inline-route-manifests.mdx +14 -4
- package/docs/en/configure/app/tools/bundler-chain.mdx +0 -4
- package/docs/en/guides/advanced-features/build-performance.mdx +156 -0
- package/docs/en/guides/advanced-features/optimize-bundle.mdx +115 -0
- package/docs/en/guides/advanced-features/rspack-start.mdx +1 -1
- package/docs/en/guides/advanced-features/source-build.mdx +161 -0
- package/docs/en/guides/advanced-features/testing.mdx +2 -2
- package/docs/en/guides/advanced-features/using-storybook.mdx +1 -1
- package/docs/en/guides/advanced-features/web-server.mdx +1 -1
- package/docs/en/guides/get-started/introduction.mdx +8 -13
- package/docs/en/guides/get-started/tech-stack.mdx +1 -1
- package/docs/en/guides/troubleshooting/builder.mdx +550 -21
- package/docs/en/guides/troubleshooting/hmr.mdx +148 -0
- package/docs/zh/apis/app/commands.mdx +3 -1
- package/docs/zh/community/contributing-guide.mdx +10 -0
- package/docs/zh/configure/app/output/enable-inline-route-manifests.mdx +6 -6
- package/docs/zh/configure/app/tools/bundler-chain.mdx +0 -4
- package/docs/zh/guides/advanced-features/build-performance.mdx +156 -0
- package/docs/zh/guides/advanced-features/optimize-bundle.mdx +115 -0
- package/docs/zh/guides/advanced-features/rspack-start.mdx +1 -1
- package/docs/zh/guides/advanced-features/source-build.mdx +161 -0
- package/docs/zh/guides/advanced-features/testing.mdx +1 -1
- package/docs/zh/guides/advanced-features/using-storybook.mdx +1 -1
- package/docs/zh/guides/advanced-features/web-server.mdx +1 -1
- package/docs/zh/guides/get-started/introduction.mdx +8 -15
- package/docs/zh/guides/get-started/tech-stack.mdx +1 -1
- package/docs/zh/guides/topic-detail/generator/plugin/structure.md +1 -1
- package/docs/zh/guides/troubleshooting/builder.mdx +553 -20
- package/docs/zh/guides/troubleshooting/hmr.mdx +148 -0
- package/package.json +8 -8
- package/rspress.config.ts +8 -4
- package/src/components/SolutionCards/index.module.scss +4 -0
- package/src/components/SolutionCards/index.tsx +5 -1
- package/src/pages/index.tsx +0 -10
@@ -0,0 +1,148 @@
|
|
1
|
+
---
|
2
|
+
sidebar_position: 4
|
3
|
+
---
|
4
|
+
|
5
|
+
# HMR FAQ
|
6
|
+
|
7
|
+
### How to troubleshooting HMR ineffective issues?
|
8
|
+
|
9
|
+
There are several possible reasons why HMR may not be work. This document will cover most common causes and provide guidance for troubleshooting. Please refer to the following content for troubleshooting.
|
10
|
+
|
11
|
+
Before starting the troubleshooting process, it is helpful to have a basic understanding of how HMR works:
|
12
|
+
|
13
|
+
:::tip HMR Principle
|
14
|
+
|
15
|
+
1. The browser establishes a WebSocket connection with the development server for real-time communication.
|
16
|
+
2. Whenever the development server finishes recompiling, it sends a notification to the browser via the WebSocket. The browser then sends a `hot-update.xxx` request to the development server to load the newly compiled module.
|
17
|
+
3. After receiving the new module, if it is a React project, React Refresh, an official React tool, is used to update React components. Other frameworks have similar tools.
|
18
|
+
|
19
|
+
:::
|
20
|
+
|
21
|
+
After understanding the principle of HMR, you can follow these steps for basic troubleshooting:
|
22
|
+
|
23
|
+
#### 1. Check the WebSocket Connection
|
24
|
+
|
25
|
+
Open the browser console and check for the presence of the `[HMR] connected.` log.
|
26
|
+
|
27
|
+
- If it is present, the WebSocket connection is working correctly. You can continue with the following steps.
|
28
|
+
- If it is not present, open the Network panel in Chrome and check the status of the `ws://[host]:[port]/webpack-hmr` request. If the request is failed, this indicates that the HMR failed because the WebSocket connection was not successfully established.
|
29
|
+
|
30
|
+
There can be various reasons why the WebSocket connection fails to establish, such as using a network proxy that prevents the WebSocket request from reaching the development server. You can check whether the WebSocket request address matches your development server address. If it does not match, you can configure the WebSocket request address using [tools.devServer.client](/configure/app/tools/dev-server.html#client).
|
31
|
+
|
32
|
+
#### 2. Check the hot-update Requests
|
33
|
+
|
34
|
+
When you modify the code of a module and trigger a recompilation, the browser sends several `hot-update.json` and `hot-update.js` requests to the development server to fetch the updated code.
|
35
|
+
|
36
|
+
You can try modifying a module and inspect the content of the `hot-update.xxx` requests. If the content of the request is the latest code, it indicates that the hot update request is working correctly.
|
37
|
+
|
38
|
+
If the content of the request is incorrect, it is likely due to a network proxy. Check whether the address of the `hot-update.xxx` request matches your development server address. If it does not match, you need to adjust the proxy rules to route the `hot-update.xxx` request to the development server address.
|
39
|
+
|
40
|
+
#### 3. Check for Other Causes
|
41
|
+
|
42
|
+
If the above two steps do not reveal any issues, it is possible that other factors are causing the HMR to fail. For example, it could be that the code does not meet React's requirements for HMR. You can refer to the following questions for further troubleshooting.
|
43
|
+
|
44
|
+
---
|
45
|
+
|
46
|
+
### HMR not working when external React?
|
47
|
+
|
48
|
+
To ensure that HMR works properly, we need to use the development builds of React and ReactDOM.
|
49
|
+
|
50
|
+
If you exclude React via `externals` when bundling, the production build of React is usually injected through CDN, and this can cause HMR to fail.
|
51
|
+
|
52
|
+
```js
|
53
|
+
export default {
|
54
|
+
output: {
|
55
|
+
externals: {
|
56
|
+
react: 'React',
|
57
|
+
'react-dom': 'ReactDOM',
|
58
|
+
},
|
59
|
+
},
|
60
|
+
};
|
61
|
+
```
|
62
|
+
|
63
|
+
To solve this problem, you need to reference the development builds of React or not configure `externals` in the development environment.
|
64
|
+
|
65
|
+
If you are unsure about the type of React build you are using, you can refer to the [React documentation - Use the Production Build](https://legacy.reactjs.org/docs/optimizing-performance.html#use-the-production-build).
|
66
|
+
|
67
|
+
---
|
68
|
+
|
69
|
+
### HMR not working when setting filename hash in development mode?
|
70
|
+
|
71
|
+
Usually, we only set the filename hash in the production mode (i.e., when `process.env.NODE_ENV === 'production'`).
|
72
|
+
|
73
|
+
If you set the filename hash in the development mode, it may cause HMR to fail (especially for CSS files). This is because every time the file content changes, the hash value changes, preventing tools like [mini-css-extract-plugin](https://www.npmjs.com/package/mini-css-extract-plugin) from reading the latest file content.
|
74
|
+
|
75
|
+
- Correct usage:
|
76
|
+
|
77
|
+
```js
|
78
|
+
export default {
|
79
|
+
output: {
|
80
|
+
filename: {
|
81
|
+
css:
|
82
|
+
process.env.NODE_ENV === 'production'
|
83
|
+
? '[name].[contenthash:8].css'
|
84
|
+
: '[name].css',
|
85
|
+
},
|
86
|
+
},
|
87
|
+
};
|
88
|
+
```
|
89
|
+
|
90
|
+
- Incorrect usage:
|
91
|
+
|
92
|
+
```js
|
93
|
+
export default {
|
94
|
+
output: {
|
95
|
+
filename: {
|
96
|
+
css: '[name].[contenthash:8].css',
|
97
|
+
},
|
98
|
+
},
|
99
|
+
};
|
100
|
+
```
|
101
|
+
|
102
|
+
---
|
103
|
+
|
104
|
+
### HMR not working when updating React components?
|
105
|
+
|
106
|
+
Modern.js uses React's official [Fast Refresh](https://github.com/pmmmwh/react-refresh-webpack-plugin) capability to perform component hot updates.
|
107
|
+
|
108
|
+
If there is a problem that the hot update of the React component cannot take effect, or the state of the React component is lost after the hot update, it is usually because your React component uses an anonymous function. In the official practice of React Fast Refresh, it is required that the component cannot be an anonymous function, otherwise the state of the React component cannot be preserved after hot update.
|
109
|
+
|
110
|
+
Here are some examples of wrong usage:
|
111
|
+
|
112
|
+
```tsx
|
113
|
+
// bad
|
114
|
+
export default function () {
|
115
|
+
return <div>Hello World</div>;
|
116
|
+
}
|
117
|
+
|
118
|
+
// bad
|
119
|
+
export default () => <div>Hello World</div>;
|
120
|
+
```
|
121
|
+
|
122
|
+
The correct usage is to declare a name for each component function:
|
123
|
+
|
124
|
+
```tsx
|
125
|
+
// good
|
126
|
+
export default function MyComponent() {
|
127
|
+
return <div>Hello World</div>;
|
128
|
+
}
|
129
|
+
|
130
|
+
// good
|
131
|
+
const MyComponent = () => <div>Hello World</div>;
|
132
|
+
|
133
|
+
export default MyComponent;
|
134
|
+
```
|
135
|
+
|
136
|
+
---
|
137
|
+
|
138
|
+
### HMR not working when use https?
|
139
|
+
|
140
|
+
If https is enabled, the HMR connection may fail due to a certificate issue, and if you open the console, you will get an HMR connect failed error.
|
141
|
+
|
142
|
+

|
143
|
+
|
144
|
+
The solution to this problem is to click on "Advanced" -> "Proceed to xxx (unsafe)" on the Chrome problem page.
|
145
|
+
|
146
|
+

|
147
|
+
|
148
|
+
> Tips: When accessing the page through Localhost, the words "Your connection is not private" may not appear and can be handled by visiting the Network domain.
|
@@ -177,6 +177,16 @@ pnpm run lint
|
|
177
177
|
|
178
178
|
---
|
179
179
|
|
180
|
+
## Benchmarking
|
181
|
+
|
182
|
+
你可以在 PR 的评论区中输入 `!bench-framework` 或 `!bench-module` 分别对 `@modern-js/app-tools` 和 `@modern-js/module-tools` 做性能测试(这需要你具有 Collaborator 及以上的权限)。
|
183
|
+
|
184
|
+
你可以根据评论输出的对比表格,重点关注构建时间和产物体积相关的指标,辅助进行相关性能判断与决策。
|
185
|
+
|
186
|
+
依赖安装相关指标由于需要前置的发包流程,因此数据相对有滞后性,仅供参考。
|
187
|
+
|
188
|
+
---
|
189
|
+
|
180
190
|
## 文档
|
181
191
|
|
182
192
|
目前 Modern.js 提供英文和中文文档。如果你熟悉中文,请同时更新中英文文档。否则,只需更新英文文档即可。
|
@@ -1,21 +1,21 @@
|
|
1
1
|
---
|
2
|
-
sidebar_label:
|
2
|
+
sidebar_label: disableInlineRouteManifests
|
3
3
|
---
|
4
4
|
|
5
|
-
# output.
|
5
|
+
# output.disableInlineRouteManifests
|
6
6
|
|
7
7
|
- **类型:** `boolean`
|
8
|
-
- **默认值:** `
|
8
|
+
- **默认值:** `false`
|
9
9
|
|
10
10
|
当使用约定式路由时,框架为了做一些优化,会向客户端中注入路由信息,默认情况下路由信息会注入到 html 中,
|
11
|
-
当该配置为 `
|
11
|
+
当该配置为 `true` 时,路由信息会注入到一个单独的 JS 文件中。
|
12
12
|
|
13
13
|
Example:
|
14
14
|
|
15
15
|
```ts
|
16
16
|
export default {
|
17
17
|
output: {
|
18
|
-
|
18
|
+
disableInlineRouteManifests: true,
|
19
19
|
},
|
20
|
-
}
|
20
|
+
};
|
21
21
|
```
|
@@ -4,10 +4,6 @@ sidebar_label: bundlerChain
|
|
4
4
|
|
5
5
|
# tools.bundlerChain
|
6
6
|
|
7
|
-
:::tip
|
8
|
-
该配置由 Modern.js Builder 提供,更多信息可参考 [tools.bundlerChain](https://modernjs.dev/builder/api/config-tools.html#toolsbundlerchain)。
|
9
|
-
:::
|
10
|
-
|
11
7
|
import Main from '@modern-js/builder-doc/docs/zh/config/tools/bundlerChain.mdx';
|
12
8
|
|
13
9
|
<Main />
|
@@ -0,0 +1,156 @@
|
|
1
|
+
---
|
2
|
+
sidebar_position: 12
|
3
|
+
---
|
4
|
+
|
5
|
+
# 提升构建性能
|
6
|
+
|
7
|
+
Modern.js 默认对构建性能进行了充分优化,但是随着业务场景变复杂、项目代码量变大,你可能会遇到一些构建性能的问题。
|
8
|
+
|
9
|
+
本文档提供了一些可选的提速策略,**开发者可以根据实际场景选取其中的部分策略**,从而进一步提升构建速度。
|
10
|
+
|
11
|
+
:::tip 📢 注意
|
12
|
+
在[优化产物体积](/guides/advanced-features/optimize-bundle.html)一文中介绍的策略也可以用于提升构建性能,这里不再重复介绍。
|
13
|
+
:::
|
14
|
+
|
15
|
+
## 通用优化策略
|
16
|
+
|
17
|
+
以下是一些通用的优化策略,对开发环境和生产环境均有提速效果,其中部分策略对包体积也有优化。
|
18
|
+
|
19
|
+
### 升级 Node.js 版本
|
20
|
+
|
21
|
+
通常来说,将 Node.js 更新到最新的 [LTS 版本](https://github.com/nodejs/release#release-schedule),有助于提升构建性能。
|
22
|
+
|
23
|
+
尤其是对于 Apple M1/M2 芯片的机型,推荐使用 Node 18 进行构建。
|
24
|
+
|
25
|
+
Node >= 16 默认提供了 Apple Silicon binaries,因此在 M1/M2 机型上性能会比 Node 14 有大幅度提升。根据我们的测试,**从 Node 14 切换到 Node >= 16 后,编译速度可以提升 100% 以上**。
|
26
|
+
|
27
|
+
你可以通过以下步骤来切换到 Node 18:
|
28
|
+
|
29
|
+
```bash
|
30
|
+
# 安装 Node v18
|
31
|
+
nvm install 18
|
32
|
+
|
33
|
+
# 切换到 Node 18
|
34
|
+
nvm use 18
|
35
|
+
|
36
|
+
# 将 Node 18 设置为默认版本
|
37
|
+
nvm default 18
|
38
|
+
|
39
|
+
# 查看 Node 版本
|
40
|
+
node -v
|
41
|
+
```
|
42
|
+
|
43
|
+
### 使用 Rspack 构建
|
44
|
+
|
45
|
+
如果你对构建性能有更极致的需求,可以一键切换到 Rspack 构建模式,请参考 [使用 Rspack](/guides/advanced-features/rspack-start.html) 来进行切换。
|
46
|
+
|
47
|
+
### 使用 SWC 或 esbuild 编译
|
48
|
+
|
49
|
+
[SWC](https://swc.rs/) (Speedy Web Compiler) 是基于 `Rust` 语言编写的高性能 JavaScript 和 TypeScript 转译和压缩工具。在 Polyfill 和语法降级方面可以和 Babel 提供一致的能力,并且性能比 Babel 高出一个数量级。
|
50
|
+
|
51
|
+
[esbuild](https://esbuild.github.io/) 是一款基于 Golang 开发的前端构建工具,具有打包、编译和压缩 JavaScript 代码的功能,相比传统的打包编译工具,esbuild 在性能上有显著提升。
|
52
|
+
|
53
|
+
Modern.js 提供了 SWC 插件和 esbuild 插件,让你能使用 SWC 或 esbuild 代替 babel-loader、ts-loader 和 terser 等库进行代码编译和压缩。详见:
|
54
|
+
|
55
|
+
- [SWC 插件文档](/configure/app/tools/swc.html)
|
56
|
+
- [esbuild 插件文档](/configure/app/tools/esbuild.html)
|
57
|
+
|
58
|
+
:::tip SWC vs esbuild
|
59
|
+
SWC 编译产物的兼容性较好,支持注入 core-js 等 Polyfill,并且功能更加完备,因此推荐优先使用 SWC 插件。
|
60
|
+
:::
|
61
|
+
|
62
|
+
### 避免使用 ts-loader
|
63
|
+
|
64
|
+
默认情况下,Modern.js 使用 Babel 编译 TS 文件,开启 [tools.tsLoader](/configure/app/tools/ts-loader.html) 选项后,会使用 `ts-loader` 编译 TS 文件。
|
65
|
+
|
66
|
+
由于 `ts-loader` 需要进行额外的语法解析和类型检查,因此会导致项目构建速度变慢,请避免使用。
|
67
|
+
|
68
|
+
```js
|
69
|
+
export default {
|
70
|
+
tools: {
|
71
|
+
// 移除这项配置
|
72
|
+
tsLoader: {},
|
73
|
+
},
|
74
|
+
};
|
75
|
+
```
|
76
|
+
|
77
|
+
详见 [tools.tsLoader 文档](/configure/app/tools/ts-loader.html)。
|
78
|
+
|
79
|
+
## 开发环境优化策略
|
80
|
+
|
81
|
+
以下是针对开发环境进行提速的策略。
|
82
|
+
|
83
|
+
### 调整 Source Map 格式
|
84
|
+
|
85
|
+
为了提供良好的调试体验,Modern.js 在开发环境下默认使用 `cheap-module-source-map` 格式 Source Map,这是一种高质量的 Source Map 格式,会带来一定的性能开销。
|
86
|
+
|
87
|
+
你可以通过调整开发环境的 Source Map 格式来提升构建速度。
|
88
|
+
|
89
|
+
比如禁用 Source Map:
|
90
|
+
|
91
|
+
```js
|
92
|
+
export default {
|
93
|
+
tools: {
|
94
|
+
bundlerChain(chain, { env }) {
|
95
|
+
if (env === 'development') {
|
96
|
+
chain.devtool(false);
|
97
|
+
}
|
98
|
+
},
|
99
|
+
},
|
100
|
+
};
|
101
|
+
```
|
102
|
+
|
103
|
+
或是把开发环境的 Source Map 格式设置为开销最小的 `eval` 格式:
|
104
|
+
|
105
|
+
```js
|
106
|
+
export default {
|
107
|
+
tools: {
|
108
|
+
bundlerChain(chain, { env }) {
|
109
|
+
if (env === 'development') {
|
110
|
+
chain.devtool('eval');
|
111
|
+
}
|
112
|
+
},
|
113
|
+
},
|
114
|
+
};
|
115
|
+
```
|
116
|
+
|
117
|
+
> 关于不同 Source Map 格式之间的详细差异,请查看 [webpack - devtool](https://webpack.js.org/configuration/devtool/)。
|
118
|
+
|
119
|
+
### 调整 Browserslist 范围
|
120
|
+
|
121
|
+
这项优化的原理与[「提升 Browserslist 范围」](/guides/advanced-features/optimize-bundle.html#adjust-browserslist)类似,区别在于,我们可以为开发环境和生产环境设置不同的 browserslist,从而减少开发环境下的编译开销。
|
122
|
+
|
123
|
+
比如,你可以在 `package.json` 中添加以下配置,表示在开发环境下只兼容最新的浏览器,在生产环境下兼容实际需要的浏览器:
|
124
|
+
|
125
|
+
```json
|
126
|
+
{
|
127
|
+
"browserslist": {
|
128
|
+
"production": [">0.2%", "not dead", "not op_mini all"],
|
129
|
+
"development": [
|
130
|
+
"last 1 chrome version",
|
131
|
+
"last 1 firefox version",
|
132
|
+
"last 1 safari version"
|
133
|
+
]
|
134
|
+
}
|
135
|
+
}
|
136
|
+
```
|
137
|
+
|
138
|
+
注意,这项优化策略会导致开发环境与生产环境的构建产物存在一定差异。
|
139
|
+
|
140
|
+
## 生产环境优化策略
|
141
|
+
|
142
|
+
以下是针对生产环境进行提速的策略。
|
143
|
+
|
144
|
+
### 禁用 Source Map
|
145
|
+
|
146
|
+
如果项目在生产环境下不需要 Source Map,可以通过 `disableSourceMap` 配置项关闭,从而提升 build 构建的速度。
|
147
|
+
|
148
|
+
```js
|
149
|
+
export default {
|
150
|
+
output: {
|
151
|
+
disableSourceMap: true,
|
152
|
+
},
|
153
|
+
};
|
154
|
+
```
|
155
|
+
|
156
|
+
详见 [output.disableSourceMap](/configure/app/output/disable-source-map.html)。
|
@@ -0,0 +1,115 @@
|
|
1
|
+
---
|
2
|
+
sidebar_position: 13
|
3
|
+
---
|
4
|
+
|
5
|
+
# 产物体积优化
|
6
|
+
|
7
|
+
产物体积的优化在生产环境中是非常重要的,因为它直接影响到了线上的用户体验。在这篇文档中,我们将介绍在 Builder 中一些常见的产物体积优化方式。
|
8
|
+
|
9
|
+
## 减少重复依赖
|
10
|
+
|
11
|
+
在业务项目中,会存在某些第三方依赖被安装了多个版本的现象。重复依赖会导致包体积变大、构建速度变慢。
|
12
|
+
|
13
|
+
我们可以通过社区中的一些工具来检测或消除重复依赖。
|
14
|
+
|
15
|
+
- 如果你在使用 `pnpm >= 7.26.0`,可以使用 pnpm 自带的 [pnpm dedupe](https://pnpm.io/cli/dedupe) 命令来升级和消除其中的重复依赖。
|
16
|
+
|
17
|
+
```bash
|
18
|
+
pnpm dedupe
|
19
|
+
```
|
20
|
+
|
21
|
+
- 如果你在使用 `pnpm < 7.26.0` 版本,可以使用 [pnpm-deduplicate](https://github.com/ocavue/pnpm-deduplicate) 来分析出所有的重复依赖,并通过升级依赖或声明 [pnpm overrides](https://pnpm.io/package_json#pnpmoverrides) 进行版本合并。
|
22
|
+
|
23
|
+
```bash
|
24
|
+
npx pnpm-deduplicate --list
|
25
|
+
```
|
26
|
+
|
27
|
+
- 如果你在使用 `yarn`,可以使用 [yarn-deduplicate](https://github.com/scinos/yarn-deduplicate) 来自动合并重复依赖:
|
28
|
+
|
29
|
+
```bash
|
30
|
+
npx yarn-deduplicate && yarn
|
31
|
+
```
|
32
|
+
|
33
|
+
## 使用更轻量的库
|
34
|
+
|
35
|
+
建议将项目中体积较大的三方库替换为更轻量的库,比如将 [moment](https://momentjs.com/) 替换为 [day.js](https://day.js.org/)。
|
36
|
+
|
37
|
+
如果你需要找出项目中体积较大的三方库,可以在执行构建时添加 `BUNDLE_ANALYZE=true` 环境变量:
|
38
|
+
|
39
|
+
```bash
|
40
|
+
BUNDLE_ANALYZE=true pnpm build
|
41
|
+
```
|
42
|
+
|
43
|
+
详见 [performance.bundleAnalyze](/configure/app/performance/bundle-analyze.html) 配置项。
|
44
|
+
|
45
|
+
## 提升 Browserslist 范围
|
46
|
+
|
47
|
+
Builder 会根据项目的 Browserslist 配置范围进行代码编译,并注入相应的 Polyfill。如果项目不需要兼容旧版浏览器,可以根据实际情况来提升 Browserslist 范围,从而减少在语法和 Polyfill 上的编译开销。
|
48
|
+
|
49
|
+
Builder 默认的 Browserslist 配置为:
|
50
|
+
|
51
|
+
```js
|
52
|
+
['> 0.01%', 'not dead', 'not op_mini all'];
|
53
|
+
```
|
54
|
+
|
55
|
+
比如只兼容 Chrome 61 以上的浏览器,可以改成:
|
56
|
+
|
57
|
+
```js
|
58
|
+
['Chrome >= 61'];
|
59
|
+
```
|
60
|
+
|
61
|
+
:::tip
|
62
|
+
请阅读 [设置浏览器范围](https://modernjs.dev/builder/guide/advanced/browserslist.html) 章节来了解更多关于 Browserslist 的用法。
|
63
|
+
:::
|
64
|
+
|
65
|
+
## 按需引入 polyfill
|
66
|
+
|
67
|
+
在明确第三方依赖不需要额外 polyfill 的情况下,你可以将 [output.polyfill](/configure/app/output/polyfill.html) 设置为 `usage`。
|
68
|
+
|
69
|
+
在 `usage` 模式下,Builder 会分析源代码中使用的语法,按需注入所需的 polyfill 代码,从而减少 polyfill 的代码量。
|
70
|
+
|
71
|
+
```js
|
72
|
+
export default {
|
73
|
+
output: {
|
74
|
+
polyfill: 'usage',
|
75
|
+
},
|
76
|
+
};
|
77
|
+
```
|
78
|
+
|
79
|
+
:::tip
|
80
|
+
请阅读 [浏览器兼容性](https://modernjs.dev/builder/guide/advanced/browser-compatibility.html) 章节来了解更多关于 polyfill 的用法。
|
81
|
+
:::
|
82
|
+
|
83
|
+
## 使用图片压缩
|
84
|
+
|
85
|
+
在一般的前端项目中,图片资源的体积往往是项目产物体积的大头,因此如果能尽可能精简图片的体积,那么将会对项目的打包产物体积起到明显的优化效果。你可以在 Builder 中注册插件来启用图片压缩功能:
|
86
|
+
|
87
|
+
```js
|
88
|
+
import { builderPluginImageCompress } from '@modern-js/builder-plugin-image-compress';
|
89
|
+
|
90
|
+
// 往 builder 实例上添加插件
|
91
|
+
builder.addPlugins([builderPluginImageCompress()]);
|
92
|
+
```
|
93
|
+
|
94
|
+
详见 [Image Compress 插件](https://modernjs.dev/builder/plugins/plugin-image-compress.html)。
|
95
|
+
|
96
|
+
## 代码拆包
|
97
|
+
|
98
|
+
良好的拆包策略对于提升应用的加载性能是十分重要的,可以充分利用浏览器的缓存机制,减少请求数量,加快页面加载速度。
|
99
|
+
|
100
|
+
在 Builder 中内置了[多种拆包策略](https://modernjs.dev/builder/guide/optimization/split-chunk),可以满足大部分应用的需求,你也可以根据自己的业务场景,自定义拆包配置。
|
101
|
+
|
102
|
+
比如将 node_modules 下的 `axios` 库拆分到 `axios.js` 中:
|
103
|
+
|
104
|
+
```js
|
105
|
+
export default {
|
106
|
+
performance: {
|
107
|
+
chunkSplit: {
|
108
|
+
strategy: 'split-by-experience',
|
109
|
+
forceSplitting: {
|
110
|
+
axios: /node_modules\/axios/,
|
111
|
+
},
|
112
|
+
},
|
113
|
+
},
|
114
|
+
};
|
115
|
+
```
|
@@ -83,7 +83,7 @@ import RspackPrecautions from '@modern-js/builder-doc/docs/zh/shared/rspackPreca
|
|
83
83
|
"pnpm": {
|
84
84
|
"overrides": {
|
85
85
|
"@rspack/core": "nightly",
|
86
|
-
"@rspack/
|
86
|
+
"@rspack/plugin-react-refresh": "nightly",
|
87
87
|
"@rspack/plugin-html": "nightly"
|
88
88
|
}
|
89
89
|
}
|
@@ -0,0 +1,161 @@
|
|
1
|
+
---
|
2
|
+
sidebar_position: 11
|
3
|
+
---
|
4
|
+
|
5
|
+
# 源码构建模式
|
6
|
+
|
7
|
+
源码构建模式用于 monorepo 开发场景,它允许开发者直接引用 monorepo 中其他子项目的源码进行开发。
|
8
|
+
|
9
|
+
## 使用场景
|
10
|
+
|
11
|
+
**monorepo 中项目互相引用主要有产物引用和源码引用两种方式**。我们以一个最简单的 monorepo 为例子,来介绍源码引用的使用场景。
|
12
|
+
|
13
|
+
比如 monorepo 中包含了一个 app 应用和一个 lib 包:
|
14
|
+
|
15
|
+
```ts
|
16
|
+
monorepo
|
17
|
+
├── app
|
18
|
+
└── lib
|
19
|
+
└── src
|
20
|
+
└── index.ts
|
21
|
+
```
|
22
|
+
|
23
|
+
其中,app 是基于 Modern.js 构建的,app 依赖了 lib 中的一些方法:
|
24
|
+
|
25
|
+
```json
|
26
|
+
{
|
27
|
+
"name": "app",
|
28
|
+
"dependencies": {
|
29
|
+
"lib": "workspace:*"
|
30
|
+
}
|
31
|
+
}
|
32
|
+
```
|
33
|
+
|
34
|
+
### 产物引用
|
35
|
+
|
36
|
+
**产物引用指的是当前项目引用其他子项目构建后的产物。**
|
37
|
+
|
38
|
+
比如上述例子中的 lib 是使用 TypeScript 编写的,通常情况下,我们需要提前构建 lib 的代码,生成 JavaScript 产物,这样 app 才可以正确引用它。当 lib 代码更新时,还需要重新执行一次构建(或者使用 `tsc --watch`),否则 app 无法引用到最新的代码。
|
39
|
+
|
40
|
+
这种方式的优势在于:
|
41
|
+
|
42
|
+
- 各个子项目的构建过程是完全独立的,可以拥有不同的构建配置。
|
43
|
+
- 可以针对子项目进行构建缓存。
|
44
|
+
|
45
|
+
劣势在于:
|
46
|
+
|
47
|
+
- 本地开发时 HMR 的链路变长。
|
48
|
+
- 当一个项目中包含多个 lib 包时,以上过程会显得十分繁琐。
|
49
|
+
|
50
|
+
### 源码引用
|
51
|
+
|
52
|
+
**源码引用指的是当前项目引用其他子项目的源码进行构建。**
|
53
|
+
|
54
|
+
比如上述例子,当你开启了源码构建模式,并在 lib 中添加相关配置后,Modern.js 会自动引用 lib 的 `src/index.ts` 源代码。这意味着,你不需要提前构建 lib 的代码,并且当 lib 的源代码更新时,也可以自动触发 app 的热更新。
|
55
|
+
|
56
|
+
这种方式的优势在于:
|
57
|
+
|
58
|
+
- 子项目不依赖构建工具,也不需要添加构建配置,子项目的代码会被当前项目的构建工具编译。
|
59
|
+
- 不需要提前执行子项目的构建流程。
|
60
|
+
- 本地开发时 HMR 更高效。
|
61
|
+
|
62
|
+
劣势在于:
|
63
|
+
|
64
|
+
- 当前项目需要支持子项目用到的语法特性,并且遵循相同的语法规范,比如使用一致的装饰器语法版本。如果当前项目和子项目需要使用不同的编译配置,则不适合使用源码构建。
|
65
|
+
- 当前项目需要编译更多的代码,因此构建时间可能会变长。
|
66
|
+
|
67
|
+
## 使用源码构建
|
68
|
+
|
69
|
+
### 开启配置
|
70
|
+
|
71
|
+
你可以通过设置 [experiments.sourceBuild](/configure/app/experiments/source-build.html) 为 `true` 来开启源码构建模式。
|
72
|
+
|
73
|
+
```ts
|
74
|
+
export default {
|
75
|
+
experiments: {
|
76
|
+
sourceBuild: true,
|
77
|
+
},
|
78
|
+
};
|
79
|
+
```
|
80
|
+
|
81
|
+
### 配置子项目
|
82
|
+
|
83
|
+
当开启源码构建模式后,Modern.js 在构建过程中,会优先读取子项目的 `source` 字段对应的文件。因此,你需要在子项目的 package.json 中配置 `source` 字段,并且指向源码文件路径。
|
84
|
+
|
85
|
+
比如以下例子,当 lib 包被引用时,会读取 `./src/index.ts` 文件进行构建:
|
86
|
+
|
87
|
+
```json title="package.json"
|
88
|
+
{
|
89
|
+
"name": "lib",
|
90
|
+
"main": "./dist/index.js",
|
91
|
+
"source": "./src/index.ts"
|
92
|
+
}
|
93
|
+
```
|
94
|
+
|
95
|
+
如果子项目使用了 [exports](https://nodejs.org/api/packages.html#package-entry-points) 配置,那么你同样需要在 `exports` 中增加 `source` 字段。
|
96
|
+
|
97
|
+
```json title="package.json"
|
98
|
+
{
|
99
|
+
"name": "lib",
|
100
|
+
"exports": {
|
101
|
+
".": {
|
102
|
+
"source": "./src/index.ts",
|
103
|
+
"default": "./dist/index.js"
|
104
|
+
},
|
105
|
+
"./features": {
|
106
|
+
"source": "./src/features/index.ts",
|
107
|
+
"default": "./dist/features/index.js"
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
111
|
+
```
|
112
|
+
|
113
|
+
## 配置 Project Reference
|
114
|
+
|
115
|
+
在 TypeScript 项目中,你需要使用 TypeScript 提供的 [Project Reference](https://www.typescriptlang.org/docs/handbook/project-references.html) 能力,它可以帮助你更好地使用源码开发。
|
116
|
+
|
117
|
+
### 介绍
|
118
|
+
|
119
|
+
Project reference 提供了以下能力:
|
120
|
+
|
121
|
+
- 使 TypeScript 可以正确识别其他子项目的类型,而无须对子项目进行构建。
|
122
|
+
- 当你在 VS Code 内进行代码跳转时,VS Code 可以自动跳转到对应模块的源代码文件。
|
123
|
+
- Modern.js 会读取 project reference 配置,并自动识别子项目的 `tsconfig.compilerOptions.path` 配置,从而让子项目的别名可以正确生效。
|
124
|
+
|
125
|
+
### 示例
|
126
|
+
|
127
|
+
在上文的例子中,由于 app 引用了 lib 子项目,我们需要在 app 的 `tsconfig.json` 内配置 `composite` 和 `references`,并指向 lib 对应的相对目录:
|
128
|
+
|
129
|
+
```json title="app/tsconfig.json"
|
130
|
+
{
|
131
|
+
"compilerOptions": {
|
132
|
+
"composite": true
|
133
|
+
},
|
134
|
+
"references": [
|
135
|
+
{
|
136
|
+
"path": "../lib"
|
137
|
+
}
|
138
|
+
]
|
139
|
+
}
|
140
|
+
```
|
141
|
+
|
142
|
+
添加以上两个选项后,project reference 就已经配置完成了,你可以重新启动 VS Code 来查看配置以后的效果。
|
143
|
+
|
144
|
+
注意以上只是一个最简单的例子,在实际的 monorepo 项目中,可能会有更复杂的依赖关系,你需要添加完整的 `references` 配置,才能使上述功能正确运作。
|
145
|
+
|
146
|
+
:::tip
|
147
|
+
如果你想了解更多关于 project reference 的内容,请阅读 [TypeScript - Project References](https://www.typescriptlang.org/docs/handbook/project-references.html) 官方文档。
|
148
|
+
:::
|
149
|
+
|
150
|
+
## 注意事项
|
151
|
+
|
152
|
+
在使用源码构建模式的时候,需要注意几点:
|
153
|
+
|
154
|
+
1. 需要保证当前项目可以编译子项目里使用的语法或特性。比如子项目使用了 Stylus 来编写 CSS 样式,那就需要当前 app 支持 Stylus 编译。
|
155
|
+
2. 需要保证当前项目与子项目使用的代码语法特性相同,例如装饰器的语法版本一致。
|
156
|
+
3. 源码构建可能存在一些限制。如果在使用中遇到问题,你可以将子项目 package.json 中的 `source` 字段移除,使用子项目的构建产物进行调试。
|
157
|
+
4. 开启 `composite: true` 后,TypeScript 会生成 `*.tsbuildinfo` 临时文件,你需要将这些临时文件加入 .gitignore 中。
|
158
|
+
|
159
|
+
```text title=".gitignore"
|
160
|
+
*.tsbuildinfo
|
161
|
+
```
|