在业务中,我们常常会遇到一个场景:同一套web业务代码要在多平执行其对应的不同职能。这样很容易出现两个问题:代码里“尸横遍野”的环境判断和分支,提高了代码难度;执行环境下载了其他环境的功能代码,造成了资源的浪费。只要我们合理使用Webpack的Tree-shaking功能,就可以很好地解决问题。
出现一套Web代码在多个平执行需要实现不同功能的问题,功能包括但不限于:数据加载、展示样式、用户交互等。
例如,腾讯课堂H5课程详情页需要承载起H5、App、PadApp、小程序等多平台的页面功能,以该页面在H5和App两个环境下的对比为例:
以其中的一个组件为例(如下代码),只要是在移动端需要适配多平台,那类似这样isApp的运行时环境判断代码一定不会少见(无论你是通过App/小程序内嵌H5页面、React-Native-Web三端同构、kbone同构小程序/H5等)。
这样的代码一方面容易在多次迭代中慢慢沦为垃圾代码(当然这个可以通过更合理的目录和代码重构解决);另一方面在不同的平台也加载了多余的代码逻辑,例如App相关的逻辑代码在H5上完全不会执行,但是还是被加载了。
一套web代码想要在多个平台实现不同功能,无论你使用条件分支、还是继承派生等方法,一个页面一份代码打天下的实践已经无法满足我们的需求了。细究这么多种多平台同构的方案,其基本原理都是一份统一API的代码,通过编译打包引用不同的平台底层组件,最后打包成多份可执行程序的过程。
重新回头看上文的isApp判断逻辑,如果我们把运行时环境判断提前到编译时环境判断,根据逻辑判断的结果,通过Tree-shaking优化去除多余的代码,那么就能得到指定运行平台的可执行代码了!
通过webpack.DefinePlugin注入编译时环境变量,后续我们的执行代码里就可以引用这个环境变量进行当前平台的判断了。
以上图的CourseDetail组件为例,当编译时环境变量RUNTIME_ENV_EXPECT注入为APP时,相关条件判断代码将被置为true,借而产生不可到达的分支,而这种条件分支和相关依赖都会被 Tree-shaking 自动去除,也就达到了去除非本环境依赖代码的效果。
通过的三个步骤,我们可以走通指定一个运行平台的代码构建打包过程。接下来要做的事情就是将该过程重复多轮,每一轮注入特定的编译时环境变量用来指定运行平台。
其中buildDistConfigForEnv根据输入的参数生成指定运行平台的构建配置,需要做以下几件事情:
最后打包进直出templates的模板有多个,例如腾讯课堂App内嵌课详页时是使用course.app.html。所以需要一个直出服务的由逻辑,在访问同一个URL时,自动根据请求带的用户环境信息选择对应合适的模板文件(指向不同的静态资源)进行渲染。
每个平台都需要进行静态资源 + 直出资源的打包,总共累计平台数*2的编译过程,这个过程是串行执行的,一旦打包平台增加不免需要等待更长的构建事件。
但是以前无往不利的构建配置似乎出现了异常,最后输出的文件夹只有一个平台的打包代码,这是为什么呢?原因很简单,在构建打包的各个阶段我们使用了不同的插件,其中CleanWebpackPlugin和EndWebpackPlugin造成了性结果,前者会在构建开始前清除构建输出目录,后者会在构建结束阶段允许用户执行脚本。
于是我们的多进程并行打包过程就受影响了,后一个启动的进程把前一个进程的结果给了,最后构建结束阶段做的工作也被重复了多次。
我们可以通过 parallel-webpack 提供的 Node.js API,手动控制打包过程,特别是打包前置操作和打包后置操作,例如:
代码压缩率可以达到 4.1% - 24.7% ,随着支撑平台数增多,跨平台功能逻辑复杂度的上跑男灵异事件升,这里的优化效果会越来越明显;
其中 App 平台的页面逻辑(page.js)上升,公共逻辑(common.js)下降,其主要原因是因为在该平台仅部分页面了多平台打包过程,抽取的公共模块(即大于两个页面共同引用的模块)比较少;
Web 上的基础依赖(vendor.js)没有下降,其主要原因为基础依赖的模块并为标识为 sideEffects = lse 缩小Tree-shaking影响的范围,降低本次重构造成的风险,当然如果把这部分模块也,可以得到更加明显的优化效果;
App 上的基础依赖(vendor.js)下降 21.8%,其主要原因是App中对比H5端少了部分功能组件,而这些功能组件依赖的一些基础模块也被 Tree-shaking 消除了;
这是前文留下的一个疑问,先抛出结论:没有一个简单快捷的方式来确认模块到底会不会被Tree-shaking。
这个也是教程中给的例子,如果这个模块的被标志成unused harmonyexport,就说明该没有外部引用使用到该,那么是可以将其安全去除的。当然这里还有一种情况就是该没有被外部引用,但是被内部调用了,那这种情况也会把export语句和声明语句分离,只将export语句去除。
部分模块是自执行的,即本身自带副作用的模块,而我们通常会使用import来进行模块引用,而不进行显示的调用。
没错,这个第四个分类你没看错,部分被标注为 harmony export 的模块依旧会被消除掉。当然 Tree-shaking 最后是由著名压缩工具UglifyJS做的。如果你真的对这里的细节感兴趣,可以看一下 UglifyJS 跟以下压缩选项配置相关的代码:
我们专注前端领域多年,负责过QQ资料、QQ注册、QQ群等亿级业务。目前聚焦于在线教育领域,精心打磨腾讯课堂、企鹅及ABCMouse三大产品。返回搜狐,查看更多苏东坡纪念馆什么时候开放_苏东坡纪念馆开放时间http://www.uzai.com/gotour/lygl/9811.html。
网友评论 ()条 查看