看 React-Conf Europe 2020 的 recompilation 优化笔记。
开篇,作者讲了其灵感来源。源自项目 re-bulma 的文档,文档中的 React 代码可以在网页内被实时编译执行。
Dev bundlers
作者认为理想的处理方式:
- If you improve the recompilation time, you increase productivity.(如果您提高了重新编译的时间,您就提高了生产率。)
- If you remove the recompilation time, you introduce a new style of development.(如果您去掉重新编译时间,您引入了一种新的开发风格。)
Let's make instant recompilation the norm
让我们把即时重新编译变成规范。
How do bundlers work?
What do current bundlers do?
- Resolve - 查找依赖
import a from './a.js'
console.log(a)
export default 'Hello World'
- Transform - 转换代码
We need to make this code understandable by the browser so that can run it. so the bundle will be converted to commonjs module.
const a = require('./a.js')
console.log(a.default)
exports.default = 'Hello World'
Next step we wrap the code into a function that exposes require module and exports:
function(require, module, exports) {
const a = require('./a.js')
console.log(a.default)
}
function(require, module, exports) {
exports.default = 'Hello World'
}
The last step that we need to do is we need to map every file to a module number because this is more of an optimization but it is really important and useful.
moduleId: {0}
function(require, module, exports) {
const a = require(1) // 1 => './a.js'
console.log(a.default)
}
moduleId: {1}
function(require, module, exports) {
exports.default = 'Hello World'
}
- Concatenate - 拼接/组装代码
Final step is the concatenation process.
const moduleMap = {
0: function(require, module, exports) {
const a = require(1) // 1 => './a.js'
console.log(a.default)
},
1: function(require, module, exports) {
exports.default = 'Hello World'
}
}
const cache = {}
const bundlerRequire = function(requireId) {
if (cached[requireId]) {
return cached[requireId]
}
const module = { exports: {} }
cached[requireId] = module
const moduleFunc = moduleMap[requireId]
moduleFunc(bundlerRequire, module, module.exports)
return module.exports
}
bundlerRequire(0)
- Hot-Reload - development 热更新
- Minify - prod - 压缩大小
- Optimize - prod - 代码优化
Development focused "Bundlers"
- Snowpack
- Vite
- 使用 es6 module 浏览器已原生支持,所以 resolve 过程浏览器会自己去请求 JS 文件。
- 监听对应文件变化,执行更新 change 替换 transformed 之后的文件
- 浏览器根据现有 state 执行 render 重新渲染被影响的对应组件
- Sandpack(sandpack 是 codesandbox 的打包工具)
- Pre-bundled Dependencies
- Server 直接返回预编译好的代码到前端给浏览器执行,所以跳过了编译过程,直接执行。这一步就是前面提到的 Resolve + transform。
- 用户代码如图中的示例就是简单的
eval(codeByUser)
- 如果 UI 更新,那开发的代码中跟UI相关的部分也会被实时 Recompilation instantly
- Distributed Cache
What about Hot Module Reloading?
- React-Fast-Refresh
- Recompilation instantly