写点什么

智联招聘的 Web 模块扩展落地方案

作者:智联大前端
  • 2021 年 11 月 15 日
  • 本文字数:1934 字

    阅读完需:约 6 分钟

传统模块化在利于开发的同时,存在诸多不便,例如:


  • 当前端多页面工程场景中使用的组件库更新时,需要编译并上线所有使用到的页面,提高了上线成本,存在部分页面漏发或回滚时漏发带来的风险;

  • 当使用 CDN 资源管理组件库时,CDN 资源更新可以实现浏览器端更新,但是服务器端渲染场景 node 端加载资源并执行无法保证与浏览器端渲染中加载的 CDN 资源版本一致,会带来渲染差异导致重新渲染问题;

  • 当采用打包工具提取公共资源时,打包工具的 chunk 提取局限于当前构建批次,若一个工程单次只构建部分页面,无法实现工程内所有的公共资源被全部提取,由于各批次构建的公共资源 hash 值不同,会导致客户端渲染时加载 CDN 资源的效率大打折扣。


致力于解决以上问题,我们开发了 web 模块的概念,从架构的层面解决困扰业务的问题。

愿景

基于已知问题,我们的关注点有:


  • 实现 web 模块单独编译上线,亦可单独回滚;

  • web 模块上线后所有使用模块的工程页面立即生效;

  • 支持服务端渲染的同构工程。


在实际业务场景中,我们希望通过 web 模块扩展解决多页面项目内部 UI 组件库、工具库(lodash,moment)、请求库(axios)等的跨页面资源共享,全量上线一次,后续更新共享资源只需要单独构建发布 web 模块扩展即可,发布后通过我们提供的能力达到多页面实时更新共享资源的效果。


另外我们希望业务方使用时引用 web 模块扩展与引用其它资源语法一致,采用原生的 import 或 require,不引入特殊语法,避免增加业务线的学习成本。

实现思路

此方案基于智联大前端 Ada 架构之上,可以到 《揭秘智联招聘的大前端架构Ada》了解,下文会直接使用 Ada 进行描述。


Ada 从开发到构建使用的是清单服务进行解析渲染的,构建工具打包会生成一份清单文件,此文件包含本次打包的 url 及打包资源等信息。


利用清单的约定,我们实现了微前端落地方案 Widget,本次设计一样是针对清单进行一些约定实现运行时的动态加载。


首先我们约定了一种新的工件类型,叫做“web 模块扩展”,开发时需要写到固定的文件夹内,才会被脚手架识别并编译。


同正常的入口文件一样,写到规定文件夹内的文件会被编译成单独的 bundle 文件,方便引用这里选择只打包成一个 bundle 的方式,即 js 与 css 最多各只有一个文件。


到这里我们就拿到了 web 公共模块,并写入到清单文件当中,拿 axios 举例如图:


接下来就是项目中的 axios 提取,既然是公共的 web 资源,所以需要将 import 稍微修改一下,即:


import axios from 'axios' // 原引用import axios from 'extensions/web-modules/axios' // 新引用
复制代码


此时需要做的就是把 'extensions/web-modules/axios' 这个路径变成一个动态加载的代码,各打包工具都提供了 Externals,利用这一特性可以自定义排除构建资源的加载方式。


拿 webpack 举例,简单实现如下:


{  externals: [    ({ request }, callback) => {    // ...      if (!matched) return callback() // 未match到web模块扩展路径直接返回
const url = path.posix.join(projectKey, WEB_MODULES_SCOPE, matched.name) // 真实URL路径
const name = target === 'node' ? `ada.webModules.require('${url}')` // 由Ada server内部实现此方法 : url
return callback(null, name, scope) } ]}
复制代码


将静态路径引用变成动态获取,浏览器端使用 window 全局变量的方式,node 端使用自实现 require 的方式。



如图,编译后的 web 模块 URL,分别对应清单内的 web 与 node 属性内的资源,下一步只需要将资源挂载到服务当中就实现闭环了。


浏览器端我们实现了 template 的模版占位符,我们将 web 相关的资源挂载到当前请求上下文的 ctx 当中,使用时:


async function GET (ctx) {  const html = `    ...    <script src="${ctx.template.placeholders.webModules.axios.js}"></script>    ...  `
ctx.response.set(html, 200)}
复制代码


服务端有发布模块提供监听器,当监听到 web 模块扩展上线时,会拉取最新的清单并将资源文件更新至本地缓存中,这样下次请求拿到的 ctx.template.placeholders.webModules.axios.js 就是替换后的最新资源。


同理,ada.webModules.require 方法是一个 node 端暴漏到全局的工具方法,里面对服务端的 web 扩展模块进行了资源加载代理,当资源更新的时候会清除 require 缓存,重新 require 最新的 js bundle。


这里不深入介绍发布注册及发现的机制,因为更偏向于服务端同步分发的内容,与本文中心想表达的设计思想无关。


到这里 web 模块扩展机制就完成了,达成了我们设计之初的愿景。

总结

模块化和动态化带来开发便利的同时,也增加了上线带来的影响和风险,毕竟上线会影响所有使用的页面,对开发者的风险把控能力也有一定的门槛。


智联大前端团队发布 web 模块扩展已有一年多的时间,内部反响比较正面,说明相对比保守大家更愿意节约时间成本和尝试新能力。

发布于: 2021 年 11 月 15 日阅读数: 4
用户头像

zhaopin.com 2020.08.04 加入

作为智联招聘的前端架构团队,我们开创了细粒度的前端研发和发布模式,统一了移动端和桌面端的技术栈,搭建了灵活可靠的Serverless运行环境,率先落地了微前端方案,并且还在向FaaS和轻研发等方向不断迈进。

评论

发布
暂无评论
智联招聘的Web模块扩展落地方案