一个电商网站的微前端落地实践

背景

XX电子商务平台是一个H5版网站,该网站前端主要使用的技术是Vue1.X全家桶。项目开发到现在快两年了,一些问题开始显露出来:

  1. 项目日益庞大,代码结构变得复杂 ,项目中的模块越来越多,很少人能对项目的代码有整体的掌握。对开发而言,总会有“牵一发而动全身”之虞。
  2. 技术选型落后, vue1.x 至今已经有两年未更新,项目中使用的Webpack, Karma等工具也远远低于最新版本。ssr, Flow等新技术也无法用在老项目中。
  3. CI 跑测试和打包速度慢CI 服务器上单次跑测试和打包时间需要15分钟左右。
  4. 测试成本高。前端所有的代码在单一仓库上,理论上每次上线都需要对整站做回归测试。

考虑微前端的引入

后端已经微服务化,前端依旧臃肿

在后端已经引入微服务,拆分出不同模块进行独立开发的时候,前端依旧臃肿,且随着时间增长变得越来越庞大和难以维护。
微前端是将后端微服务的概念引入到前端来,希望对前端代码进行拆分,使得前端各部分能独立维护、独立交付。


将前端也微服务化

微前端带来的好处

  1. 复杂性降低,代码的拆分使得各部分有自己的职责,每部分都足够小。
  2. 在技术选型上更加灵活,可以选择最适合的技术和架构。对于每个小服务而言,可以独立进行技术选型,也能很方便升级某服务的框架版本。
  3. 各服务可以相对独立地开发,部署。每个服务可以扩展自己的功能,也可以交由特定团队开发维护。
  4. 更易测试,修改了某个服务,可以更专注于此服务的测试。

引入微前端本项目需要考虑哪些问题?

  1. CICD。前端由一个项目变成了多个项目,需要同时建立多套CI并调整部署方案,这将带来运维成本上升。
  2. 如何集成?各服务要组成一个完整的站点,用户在各服务之间能无感知地跳转。具体而言,不同服务需要共用domain, 是否登录等状态,另外服务间也要能互相通信。
  3. 组件库共用?前端拆分出多个服务之后,这些服务的技术选型和开发是相对独立的,但对于一个风格统一的大型网站而言,我们又需要一套公共的组件库来保证网站样式和行为的统一,以及降低开发成本,避免同样的组件在各服务间各实现一套。 那么组件库如何能保证在技术选型不一的各服务间共用?
  4. 项目成员的学习成本和开发环境。使用微前端,更改了之前的开发和部署方式,如何降低这一部分的上手难度和学习成本,并顺利推广到组内所有开发?

我们使用的方案

吕靖 在洞见中发表了一篇微服务实战的文章: 「微前端」- 将微服务理念扩展到前端开发(实战篇)。这篇文章列了四种微前端的方案:

  • 使用后端模板引擎插入 HTML
  • 使用 IFrame 隔离运行时
  • 客户端 JavaScript 异步加载
  • Web Components 整合所有功能模块

考虑到XX电商是一个H5网站,每个页面的内容有限,我们不需要将页面中的组件划分到各服务,我们直接以页面为最小粒度进行划分
所以我们直接采用了第四种方案,使用Web Components技术开发公共组件,各服务独立开发部署,然后通过Nginx连接起来。

1. 如何拆分

引入微前端的契机是客户准备开发 * * 模块,该模块相对独立,跟其他模块耦合较低,我们的初衷是先将其拆分出来进行独立开发和维护。于是将 * * 模块整个流程涉及到的页面拆分出来,新起一个服务。

2. 使用Nginx将各个服务连接到一起

拆分出新的服务之后,各服务需要衔接在一起。我们用Nginx将各服务代理起来,访问各服务时,通过特殊路由前缀就能将请求转发到某一服务上,而各服务能共享Nginx Server的状态(cookie, session等)。

使用Nginx连接各服务

3. 实现一个框架无关的组件库

为何强调框架无关? 我们认为,这个共用的组件库最好跟框架无关的,无论服务选择VueAngular还是React,最好都能使用这个组件库。不然如果组件库使用Vue实现,那么必定无法无缝迁移到React和Angular中,这样就限制了前端各服务的技术选型甚至主流框架版本。总之,这种考虑为了提高技术选型的自由。

使用 Custom Element

对于组件化,W3C已经有了Web Components的草案,使用浏览器原生能力实现组件化已经成为现实。Web Components草案其中非常重要的一点是提供了自定义HTML Element的能力。
这里给出使用Custom Element技术定义HTML Element的示例:

class Header extends HTMLElement {
  connectedCallback() {
    this.attachShadow({mode: 'open'}).innerHTML = `

My Header

`; } } customElements.define('my-header', Header); // 注册, 第一个参数是标签名称
image.png

使用Custom Elements定义标签时,能够使用组件绑定和注销等生命周期:

  • connectedCallback // 插入到文档中
  • disconnectedCallback // 从文档中移除
  • adoptedCallback // 移动到新文档中

也能使用AttributeChangeCallback来实现对属性变化的监测。
一个更完整的例子: https://github.com/neuland/micro-frontends/tree/master/2-composition-universal

4. 部署问题

多个服务开发完成之后,打包成静态文件,放到Nginx服务器上即可:

    server {
        listen       8000;
        server_name  localhost;

        location ~ ^/service01/ {
            root /var/www/service01;
        }

        location ~ ^/service02/ {
            root /var/www/service02;
        }

    }

5. 对本地开发环境的升级

使用微服务之后,带来的另一个问题是本地开发环境,最明显的是前端开发要启动的项目变多了,原来打开一个项目并运行起来就能修改的活,可能需要同时打开多个项目。另外我们也需要Nginx或者Http-proxy类似的解决方案做请求的转发。那么怎么能使得开发更方便一些?

使用DockerDocker Compose
使用Docker Compose

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中。

我们将Nginx 配置打包到docker中,这样就在开发者之间同步了路由配置。

Compose 是用于定义和运行复杂Docker 应用的工具。你可以在一个文件中定义一个多容器的应用,然后使用一条命令来启动你的应用,然后所有相关的操作都会被自动完成。

我们也将前端各项目的运行环境塞到docker中,并使用Docker Compose将这些服务管理起来,在compose的配置中写入了安装第三方依赖、运行项目的脚本。

services:
  nginx:
    container_name: nginx
    image: nginx:1-alpine
    volumes:
     - ./nginx.conf:/etc/nginx/conf.d/default.conf
    ports:
     - "80:80"
  service01:
    container_name: service01
    build:
      context: ./
      dockerfile: Dockerfile
    volumes:
     - ../:/code/
    working_dir: /code
    command: ["bash", "-c", "yarn && yarn dev"]

  service02:
    container_name: service02
    build:
      context: ../../service02
      dockerfile: Dockerfile.dev
    volumes:
      - ../../service02/:/code/
    working_dir: /code
    command: ["bash", "-c", "yarn && yarn dev"]

使用DockerDocker Compose之后,一个新人搭建前端开发环境并启动只需要执行下面几步:

  1. 将所有前端项目克隆到同一个文件夹下
  2. 安装DockerDocker Compose
  3. 执行 docker-compose up --build
    然后就可以打开编辑器开始写代码了。

总结

以上就是我们XX电商前端团队的一些微前端实践,主要讨论了落地的一些问题。欢迎大家来拍砖~~

你可能感兴趣的:(一个电商网站的微前端落地实践)