项目背景
团队开发维护的tim项目,分为几个平台,分别是 pc端,app端,微信端,以及外网的一些其他页面总共四个项目。一开始是分成四个仓库维护,公用的部分发npm包维护,但是随着项目的复杂度上升,而且每周至少两个迭代的速度,npm包维护公共工具、库的形式非常繁琐。
改一个东西你需要先改公共包,你要先改包文件,然后发包构建,然后用到的地方可能app需要改,wechat需要改,pc不需要改,一天下来这套流程会浪费不少时间…
所以现在更倾向于合在一个大仓库的形式,公用的部分直接在外面写一个shard包,这样可以解决频繁改动的问题,项目件的公用部分抽离会更加方便。
但是目前app项目中的构建体系不支持这种引用外部包的形式,所以需要对app项目的构建进行一次升级,以支持shard目录的模式,后面再统一对tim-web项目的整体构建。
计划
目前的计划是先对 app 构建体系升级,去除gulp部分,然后再对这个老项目的文件目录与图片路径等进行迁移处理,最后统一用一套构建体系。
1 | ├── README.md |
构建系统升级其实就是一个发现问题,解决问题,再发现问题的螺旋上升过程。
目前APP主要的痛点是:
- 开发体验极差,经常要等个5s-10s,而且现在功能一直在迭代,项目的复杂度直线攀升…
- app页面有很多表单,代码每次重新构建都会刷新页面,一刷新表单数据和页面状态都消失了。如果用webpack的话,可以利用
hot reload
来解决这个状态问题,并且加快dev-rebuild的速度,可以大大减少同事等待构建摸鱼的时间。 - 无法引用app之外的包,比如数据处理工具,通用逻辑,i18n多语言,通用的组件以及业务逻辑等等。目前很多数据类型的数据其实和平台无关,如果可以做到很低门槛的公用,会大大减少后续维护难度
- gulp 开发模式一直都是构建出实体文件,对电脑性能消耗大,特别是我这种小破机,每次都不敢开太多服务…
遇到的难点其实总结为一点就是,老旧代码的迁移问题,很多代码之前在这套构建没问题,但是你一升级你得对这些旧代码做兼容处理,而且也不能手动改,因为这种构建升级的项目肯定会伴随着业务开发,你现在手动改了很多文件,后面一合代码又是白搭。所以涉及到的改动一方面要记下来,另一方面要尽可能的用脚本去改动。
app 构建升级第一版
第一版最初的设想是与pc端一致,用webpack3先取代gulp,这里总结一下升级中遇到的一些问题
css与js依赖管理
目前app项目中有许多js和css是直接引入的, 类似在 index.html中直接写了 ‘/public/a/to/b.js’,然后文件也直接放在 ‘public’下面,有点像几年前手动管理依赖的味道,如果你的 jquery 放在需要依赖jq的文件下面,那页面就报错了。
所以做的第一步是把 /vendor 挪到开发目录,然后各自js和css写到一个 vendor.js vendor.css去管理。
webpack3的文档问题
webpack3目前官方没有维护文档,所以你要找对应的文档得去找网上热心网民存的快照。很多的插件也是默认安装时会报不支持 webpack < 4 的版本,而且这些插件的文档也不太好找,一个小技巧就是去npm查看这些插件的版本,然后大概猜一下webpack3需要的版本是多少,一般都是降1-2个大版本。然后再通过npm的版本去看当时这个插件的文档和使用是怎样的,这里再次标明写文档是真的很有用…
gulp.genmockConf 生成文件
这个gulp的任务是根据当前的运行模式,将一些文件的变量替换后,再输出到构建目录,然后index.html直接去引用这些文件,比如 appVersion, apiUrl等。
我们可以选择在index.js引入这些文件,然后通过 webpack.DefinePlugin 来实现,不过这次改造的目标是尽可能只动构建部分,不动代码部分。
所以我选择了用 CopyWebpackPlugin 配合 transform方法来解决这块问题。
css前缀问题
gulp 处理css的时候,老项目会在部分css变量名的前面加一个前缀,比如 .demo
=> .mui-demo
。
也就是说在写css/less的时候,是写 .demo
,然后在用的时候是写 .mui-demo
。而且只有部分文件需要这么处理。
这里我用了一个 postcssSelectorPrefix
的插件来解决,然后将需要加前缀的和不需要加前缀的区分开来,给需要加前缀的单独应用这个 loader。
但是这个插件有个问题,一些ionic自定义的标签转换会有问题,转换后.mui-demo的前缀自定义标签不见了,导致很多样式错乱,比如:
1 | slide-tabs { |
后面看了下这个东西实现不复杂,直接fork一个本地改了之后放到项目里维护了。
css 动画消失了
改完后,发现有一些动画消失了,这些动画很多是ionic改的…极其复杂,一时很难找到在哪里加的动画,加的是什么className。尝试性点即几下,发现className有快速变动,于是想到应该又是那个 className 加前缀出的问题。
首先在 chrome 调试直接给这个dom打个 break on attribute modifications
,结果发现触发了n次,特别难察觉到有哪些切换,而且断点一堆也不好看…
于是换了一种思路, MutationObserver
去监听dom变化,在 class attribute变动的时候输出出来,还是没有看出什么问题。
最后用 chrome Animations面看查看运动的动画,发现果然是前缀的问题,修改了一下添加前缀的规则解决。
html 改动不触发页面reload
html是放在类似 /public/templates
里面,然后通过angularJs框架运行时去请求html模板进行渲染的,所以他确实也没有参加到我们的构建服务中,这就导致一个问题。如果我们改动这些html,是不会触发reload的,需要手动刷新页面。
当下想到两种办法,第一种格式遍历所有的template,然后都写到 webpack.devServer.contentBase
,虽然很hock,但是确实可以解决问题。
第二种是看webpackDevServer.watch是怎么处理的,看如何解决这个问题,发现 watch依赖的是 chokidar@2.1.2
,这个版本已经被废弃了基本无解,所以最后决定用 glob
扫出 public/template
的文件加到contentBase
中,曲线救国…等后面升级webpack版本就好了。
至此,第一版基本完成,和同事讨论之后,同事说这种改造测试起来很废时间,还不如一步到位直接上webpack5好一点…我被说服了…
app 构建升级第二版
第二版直接在第一版的基础上升级webpack的版本,所以总体会感觉轻松一些。但是毕竟升级了两个大版本,问题还是不少。
arguments is not defined
升级完webpack5之后的第一个问题就是,代码报错,报了 arguments is not defined
,报错的地方是一个依赖的包,这个报错的原因是 webpack4之前,包裹模块的函数是函数表达式,而webpack4以后改为了箭头函数,所以其实问题一直都存在,只是以前没有暴露出来。
这个问题也好解决,直接升级该包的版本就可以了。
esm和cjs混写问题
旧项目很多是esm和cjs混着写,因为angularJs的依赖注入需要用 cjs,所以一开始是想着用一个脚本去分析ast,然后都改为cjs。后面想着不应该,于是各种找资料,然后在本地直接用 babel编译cjs和esm混用的代码,发现没有报错。最后通过改了babel的配置解决。
production 模式报错
开发测试成功之后,开始自测构建生产环境的代码,发现如果 mode:development一切正常,但是改成production的时候就会一直报 angularJs 模块注入的错误。
这个比较好解决,既然都知道是mode的问题,直接去webpack官网看这两种模式的配置区别是什么,一个一个排除之后发现是代码压缩导致的,修改下配置,去掉 mangle选项后解决。不过具体的原因目前还没有了解。
1 | minimizer: [ |
总结
期间还遇到其他配置等问题,无脑刷官网就可以解决了。
目前改造后未深度优化的效果:
老项目:
- 首次构建:33940ms
- 开发编译:5004ms
- 生产构建: 49479ms
- 打包后www目录大小: 31M
改造后:
- 首次构建: 11184ms
- 开发编译: 1462ms
- 生产构建: 56634ms (这就有点尴尬了)
- 打包后www目录大小: 11M
我觉得最大的作用还不是构建速度和代码大小的问题,最大的作用是为后面很多新特性和改造奠定了根基。
下一步的工作是:
- 图片、字体等资源纳入构建系统中
- 文件夹目录统一
- tim-web的另外几个项目构建升级