拿来即用的纯前端实现 A/B test 方案

2,282 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情

上一篇文章有说到过,实现前端资源灰度的几种方案和原理你想知道的前端灰度方案都在这了,这篇文章主要讲解第五种方案的具体实现

流程

先来看看整体的实现流程,灰色框是原有的构建部署流程,蓝色的是修改或者添加的步骤

image.png

  1. 获取上一版本资源
  2. 灰度策略
  3. 灰度版本判断

前端获取线上最新代码

1. 获取线上目前使用的 A 版本

在执行 git commit 的时候,借助 githook 执行下面的代码

async function init() {
  console.log('======== 获取 ===========');
  try {
    const $ = await request({ url: grayUrl });
    console.log('======== 解析 ===========');
    oldResourceUrl = parseNormalPage($);
    writeFile('./gray-config/gray-config.json', JSON.stringify(oldResourceUrl, null, 2));
    console.log('======== 解析资源路径成功 =========== 输出 gray-config.json 存档');
  } catch (e) {
    console.log(e);
    console.log('======== 失败,请重试 ===========');
  }
}

解析出来的 gray-config.json 文件格式大致如下, 是目前线上运行的最新版本的 html 内加载的资源,我们先称之为 featureA

{
  "/vendors.js": "https://baidu.com/vendors.123.async.js",
  "/umi.js": "https://baidu.com/umi.35b717ce.js",
  "/umi.css": "https://baidu.com/umi.9685918c.css"
}

2. 把获取到的资源,提交到代码仓库上做版本备份

  "gitHooks": {
    "pre-commit": "node ./gray-config/get-config.js && git add ./gray-config && lint-staged"
  },

服务器构建资源

正常我们会执行 npm build ,生成一个新的 ./dist/html 即可线上访问,我们先来看看我们目前有什么

  1. 可以从 ./dist/asset-manifest.json 里面拿到 featureA 的资源
  2. 可以从 ./gray-config/gray-config.json 里面拿到 featureB 的资源

其实到这里已经解决了上一篇文章里说到的三大本质里面最难的资源问题了,剩下的灰度策略和逻辑判断就非常简单了

生成新的 html 文件

我们需要在 npm build 之后,再加入一个命令

    "build": "npm build && node gray-config/build-gray-html.js",

这命令做什么,其实就是根据灰度策略和灰度逻辑,再生成一个新的 html 即可,最后生成出来的 html 大概会是这样

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
      const loadJS = (url, callback) => {
      const script = document.createElement('script');
      const fn = callback || (() => {});
      script.type = 'text/javascript';
      script.onload = () => {
        fn();
      };
      script.src = url;
      script.async = false;
      document.getElementsByTagName('body')[0].appendChild(script);
    };
    const loadCSS = (url) => {
      const link = document.createElement('link');
      link.rel = 'stylesheet';
      link.href = url;
      document.getElementsByTagName('body')[0].appendChild(link);
    };
    funcntion loadA(){
        loadJS(vendorA.js)
        loadJS(xxx.A.js)
        loadCSS(xxx.A.css)
    }
    funcntion loadB(){
        loadJS(vendorB.js)
        loadJS(xxx.B.js)
        loadCSS(xxx.B.css)
    }
    // 灰度判断逻辑
    function loadAB() {
      if(灰度策略()){
        loadB()
      } else {
        loadA()
      }
    }
    loadAB()
  </script>
</body>
</html>

这里解释下 loadJS 为什么要用 script.async = false; 如果不加这个的话就会需要串行加载才能保证 js j加载的顺序,导致首屏变慢

    funcntion loadA(){
        loadJS(vendorA.js,function (){
           loadJS(xxx.A.js)
        })
       
        loadCSS(xxx.A.css)
    }