微前端的一个简单应用
做为后端工程师,对微服务肯定耳熟能详了,或者可以说,微服务已经烂大街了。
做为前端工程师,也有些类似的概念,那就是微前端。
很多年前,其实前端就已经开始探索面向组件开发了,当时有过一些 Web Component 的概念,我曾经尝试过,工程上很难用,兼容性又不好,后来似乎不温不火渐渐没有消息了。不过经过多年的发展,前端框架层出不穷,如今面向组件开发已经成为了很自然的事情了。
乾坤是一个优秀的微前端框架,它的官网也说明了为什么会有微前端,为什么 iframe 不好用等等。但是如果不使用任何微前端框架,可以进行微前端的开发吗?答案是可以的,搞清楚本质就行。当然,如果项目比较大比较复杂,还是建议使用框架,毕竟功能更全。不过,在简单的场景里,引入框架就没有必要了。
本文以一个实际的例子,来说明微前端的一个具体应用,可以展示出,这个理念本身非常有用,框架并不重要。
我个人创建了很多网站,比较杂也比较分散。如今尝试把它们渐渐地联结成一个整体,先想到的是让它们复用一个统一的页脚,于是首先将自己的个人主页站点和我学习《计算机程序的构造和解释》时做的习题集站点加上统一的页脚:
https://jeff-tian.jiwai.win
https://sicp.jiwai.win
这里详解一下过程:
页脚组件选型
看过一些,比如 bit.dev、contentful 站点的,后来还是觉得 rc-footer 最美观,同时开发量最小。它是基于 React 的组件。
我想集成进入的前两个站点,个人主页站点是 Gatsby Js 的,前端使用到了 React;而那个 sicp 站点,则是基于 GitBook,是一个早就停止维护的开源框架,前端是基于 jQuery 的。因此想到集成进个人主页是很自然的,集成进入 sicp 站点时,可能会有一些挑战。但是基于之前成功地在微信小程序里动态嵌入了 React 组件,因此还是很有信心能够搞定,毕竟在网页里嵌入代码,比起微信小程序来,要少很多限制。
【视频】个人微信小程序如何动态加载并运行远程代码(热更新)?
页脚开发
使用 rc-footer,基本上不需要什么开发,填入数据就完成了。但是我将它发布在了 bit.dev,因为它提供了方便管理的 Dashboard,也同时提供 npm 仓库源服务。
https://bit.cloud/uniheart/experience/ui/uniheart-rich-footer
不过它还很新,版本号还在零点几。感觉类似 Backstage,但是专注微前端托管,有了它,感觉 Storybook 都可以不用了。
嵌入个人主页
因为是 React 技术栈,所以只需要
yarn install @uniheart/experience.ui.uniheart-rich-footer
要注意的是,以上 npm 包目前没有发布到 npm registry,而是发布在了 bit.dev,所以需要在项目中增加 .npmrc 文件,并且将相关的 scope 写入:
registry=https://registry.yarnpkg.com/
@uniheart:registry=https://node.bit.dev
@teambit:registry=https://node.bit.dev
然后在页面里使用就行了。
import {UniheartRichFooter} from '@uniheart/experience.ui.uniheart-rich-footer'
嵌入 GitBook
这个要克服的问题略微多一些。首先,要知道 GitBook 提供了主题,而加入页脚应该在主题的布局中完成。所以,第一步是需要自己建立一个主题。
建立主题
我从官方的 default 主题源代码仓库 fork 了一个,命名为 jeff-tian 主题:https://github.com/Jeff-Tian/theme-default。
引入 npm 包
这一步和嵌入个人主页一样,并且需要额外安装 react 和 react dom。
增加 footer.js
这个文件就是将页脚渲染出来的 js 代码。由于不想再给已有的 GitBook Theme 工程增加更多依赖,所以没有配置解析 jsx 语法的工具链。这时不像在个人主页中那样可以使用
同时,由于宿主不是 React 技术环境,因此,需要手动自行使用 ReactDOM.render 来将组件渲染在指定的位置。
修改布局文件
就是在合适的地方,增加一个placeholder,让 footer.js 渲染后生效的一块区域,我准备渲染页面,自然是在页面最底部。在 layout.html 文件里,找到 body 标签,在结束前增加一点代码:
解决打包问题
GitBook 主题的打包,需要运行 src/build.sh 文件。我在运行时,碰到了错误,看上去是不知道如何处理 css 文件。原来我在实现页脚组件时,引入了一个 css 文件,而 build.sh 是使用的 browserify 进行 js bundle,然后使用 uglify 进行代码压缩。原来的 js 文件里不会引入 css,所以没有问题。
为了解决 css 文件和 js 文件的混合 bundle,再次安装一个 npm 包:browserify-css,并修改 build.sh:
- node_modules/.bin/browserify src/js/theme/index.js | node_modules/.bin/uglifyjs -mc > _assets/website/theme.js
+ node_modules/.bin/browserify -g browserify-css src/js/theme/index.js -o _assets/website/theme.js
为了简化问题,没有再折腾 uglify 对 css 的支持,代价是目前代码文件会大一丢丢。
在 sicp 站点中引入 theme-jeff-tian
book.json 的 plugins 里增加:
{
"plugins": [
...
"-theme-default",
"theme-jeff-tian@1.2.0",
],
...
}
同时在 package.json 里增加 theme-jeff-tian 包的 dependency 项,不然 book install 后,仍然会报找不到 theme-jeff-tian 的错误。感觉 GitBook 这一块儿的逻辑有点过于复杂了,如果细看,会发现它自己用了一个老版本的 npm 安装 GitBook 插件,并单独放在一个文件夹中。
...
"dependencies": {
"gitbook-plugin-theme-jeff-tian": "1.2.0"
},
...
"scripts": {
"build": "gitbook install && yarn install && book sm && gitbook build",
"serve": "gitbook serve"
},
...
完成
运行 yarn build && yarn serve
,效果完美。
总结
一、微前端的理念非常好,实现的框架相对没那么重要。
二、React 组件并不要求宿主基于 React 技术栈。
三、jsx 只是语法糖,本质上
四、由于 React 组件就是一个闭包函数,所以要渲染它很容易,直接 ReactDOM.render() 就行了。这种隔离渲染真的是很爽,也是高内聚低耦合的典范。