前文从零搭建了一个基础的react开发环境,但是对webpack的很多知识点没有理解到位

本篇文章以Fishing Funds的v3分支到v4分支的改造过程,进一步学习webpack

Fishing Funds 在v4版本以前均基于electron-react-boilerplate 当时时间节点的最新master分支开发,electron-react-boilerplate 作为一个React + Electron + Typescript的模版仓库,是一个非常优秀的开源项目,但是看了很多issues以及最近的PR,发现官方维护力度不如以前,很多好的issue都没法第一时间解决,Electron目前已经发布了基于Chromium V91的V13正式版 ,而前不久 electron-react-boilerplate 才将基于Electron v12的3.0分支合并到master分支,目前最新的开发进度在next上,也仅仅是还有几个api变更的测试没做完。

最近看到 #2836 中提到建议官方移除babel,作为前端发展时期的重要工具,babel解决了旧浏览器不兼容新ECMA标准的痛点,配合polyfill,几乎可以让前端开发的核心从兼容性转移到业务,节约了开发成本,这样一款好的工具在那个IE作妖的年代确实能大展拳脚,但是随着前端近些年的发展和各大浏览器厂商对新API的积极更进,现代浏览器几乎成为了装机标配,再加上Typescript的崛起和生态发展趋势,babel现在已经不再是从前那么的重要

Electron作为前端跨平台开发的首选,其最大的好处之一就是运行环境新,不用担心学到的新知识无处施展,因为基于最新Chromium内核,如果在这种情况下再使用babel 将高版本语法降级,那不仅增加了打包后的代码量,很多原生的优化也无法享受到。

当然,移除babel的劣势也有,比如各种法力无边的stage0提案没法使用,不过#2836中说的很明确,TS不支持的语法,我们压根就不该使用,老老实实遵循官方规范才是王道。作者也在下面表示了赞同,并且添加到了后续版本的TODO中。

分析package.json

从ERB的script入手

发现build分别执行了main和renderer的打包,熟悉Electron就会知道Electron将主进程和渲染进程分开,各司其职,打包这里用到了webpack,那么就进一步分析webpack的config文件是如何配置的

查看了两个进程的webpack config文件发现都merge于webpack.config.base.js

这里看到在rules中将ts和tsx文件交给了babel-loader去处理

那么就再找到根目录中的babel.config.js,看看是怎么配置的

发现presets中是熟悉的老三样,@babel/preset-env,@babel/preset-typescript,@babel/preset-react,没什么问题,但是看到plugins中添加了草案阶段的各种语法转换插件,这些东西我们是完全用不到的

再回到webpack,分别看看main和renderer的配置文件,无非就是将node环境设置成了production,并且添加了一些loader用来处理scss和sass,将sourceMap设置为false之类的。

但是在webpack.renderer.prod.babel.js的entry发现添加了core-js和regenerator-runtime这两个插件,前者集成了新api实现了完全无污染的API转译,后者是生成器在babel语法转译后的实现,两者一起食用相当于添加了@babel/polyfill(不再维护)

改造webpack config

前面已经分析了ERB的打包流程,可以看出其主要思路就是用babel-loader去处理ts和tsx文件,再配合babel的插件实现各种转译和polyfill支持,现在要做的就是移除babel

首先从源头webpack.config.base.js出发,由于不需要babel来转换语法,所以将babel-loader替换为ts-loader

然后在根目录tsconfig.json中设置

{
    ...
    "jsx": "react-jsx"
}

⚠️ React17以及Typescript4.1以后使用react-jsx,否则为react,具体区别在此

编译后的区别:

可以看到,react的方案会默认编译成硬编码,React.createElement 这个方法,有一些 React.createElement 无法做到的性能优化和简化,关于这点以后可以做详细解读

再看到webpack.main.prod.babel.js 的entry,将preload.ts转换为同名js文件

由于Electron12之后默认废弃了remote模块以及开启contextIsolation,所以在调用node模块时必须通过 contextBridge 模块暴露在window上的方法来实现,所以过去可有可无的preload.js现在是必备

至此,生产环境的打包部分已经改造完成,此时main和renderer都走webpack且使用ts-loader编译代码,不添加任何polyfill,并且在任何构建阶段会对ts文件进行类型检查,能进一步提升代码书写质量

接下来要修改的就是开发环境下的配置,由于开发时主进程是需要启动的,但是原来的main.dev.ts是通过 babel/register 这个模块来启动的

 "start:main": "cross-env NODE_ENV=development electron -r ./.erb/scripts/babel-register ./src/main/main.dev.ts"

这样子主进程还是走了babel,是没有必要的,但是如果不用babel转码,electron是无法直接运行ts文件的,而且nodejs 中 对esModule 的支持还存在问题,代码中大量的import 语句都要替换成require

electron 使用 -r 可以预加载模块,于是这里使用ts-node/register 来直接运行主进程的ts文件

 "start:main": "cross-env NODE_ENV=development && electron -r ts-node/register ./src/main/main.dev.ts"

由于开发环境,主进程是用electron启动,所以正式环境配置中的preload.js无法通过webpack产生,所以还要添加一个预加载脚本来编译preload.ts

 "build:preload": "yarn tsc ./src/main/preload.ts"

这样子,开发环境主进程需要babel的地方也移除掉了,由于渲染进程本身的webpack.config.js文件就是采用的base方案走ts-loader,所以全部运行代码已经彻底抛弃babel全面拥抱Typescript了

后记

当我在删除根目录下的babel.config.js后运行出错了,一脸问号,难道还有地方在依赖babel运行

最终在webpack的文档上看到 https://webpack.js.org/configuration/configuration-languages/#typescript

原来webpack 采用node-interpret,对不同后缀的文件走不通的编译,这个时候就明白 ERB 的webpack配置文件都是以babel.js结尾的,所以在执行renderer的脚本时

  "start:renderer": "cross-env NODE_ENV=development webpack serve --config ./.erb/configs/webpack.config.renderer.dev.babel.js"

在webpack内部走了一下babel 将webpack config文件本身也转换了一下,所以现在只需要将babel的配置文件修改为

module.exports = {
  presets: [[require('@babel/preset-env')]],
};

对此,如果有想彻底把babel移除,那么就两种选择

  • 将webpack config修改为nodejs 的 commonjs规范
  • 将webpack config从js修改为ts,node-interpret 也能处理ts文件

但这一步其实可有可无,对程序本身无论是开发还是生产环境都没有任何影响,所以可略过

至此针对Fishing Funds v4分支的改造已经改造完成,期待ERB官方的下一个release版本发布,想看下官方是如何移除babel改造webpack的。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注