3
本文首发于 vivo互联网技术 微信公众号 
链接: https://mp.weixin.qq.com/s/Ka1pjJKuFwuVL8B-t7CwuA
作者:悟空中台研发团队

00.png

vivo大厦(南京)

一、缘起 - 什么是悟空活动中台?

随着 vivo 互联网用户量级不断增加,应用商店、官网商场、 游戏中心和浏览器等 vivo 官方产品相继进入存量用户运营时代。在这种大背景下,营销活动日益增多,传统活动开发模式已经不能满足井喷式且多样化的需求,项目开发和产品运营过程中遇到种种困难:

1. 浏览器团队开发的活动组件, 商城活动项目能否拿来复用?

2. 游戏中心团队开发的 H5 页面,会员活动项目能否拿来复用?

3. H5 项目发布周期较长,修改文案需要重新上线,能否把活动运营效率最大化?

4. 轻 H5 业务能否节省相应配比的服务端和测试人力?

5.各 BU 业务不同,活动后台系统过多,研发效率和运营效率低下,导致数据孤岛现象严重。

6.研发轮子过多,问题场景没有标准的解决方案,营销活动常见问题没有官方团队进行统一收集沉淀。

7.非业务 H5 需求,如招聘、节日、活动报名等,需使用外部的建站产品且数据安全风险高。

8.……

「悟空」,是我们在攻克上述问题场景过程中诞生的 「插拔化、可视化、一站式」的综合活动中台,目前已是 vivo 活动项目首选的在线开发平台、运营平台

取名为 「悟空」,是承载了我们的愿景,在技术领域持续探索,修炼“七十二变”,不断攻克企业活动项目运作过程中的“九九八十一难”,取得“真经”。

截止目前,悟空接入 BU30+ 、上线 H5 页面 7000+ 、营销组件 20+ 、各品类模版 50+ 和营销任务 60+ 。

以下是 vivo 各 BU 互联网团队在悟空上进行开发提交、上线运营的经典活动。

图1 - 经典营销活动截图

二、活动运营为什么选择悟空?

举个栗子:

vivo 商城团队针对国庆节促销活动开发了一款爆款 H5 ,其中【集卡】组件效果出众。vivo 会员团队希望双十一也上线一款类似的 H5 活动页,也期望具备【集卡】组件能力。

传统方案:

会员团队线下 clone 一份商城 H5 活动代码,根据业务进行定制修改后上线。因【集卡】涉及运营策略线上配置,需要重新开发相应的活动配置功能。

痛点:

1. H5 代码耦合业务严重,可维护性、可扩展性下降。

2. 单次开发的后台功能,可能仅服务于本次活动,投入产出比低。

3. 针对活动上线后的效果,相关埋点需要重新设计。

4.若需要风控、数据分析、微信二次分享等活动配套能力,需要重新开发。

如何解决:

而悟空从组件/模版市场、任务中心、实时配置、活动配套,四个维度解决上述的痛点。

目前会员的运营同学只需要在悟空模版市场中挑选自己需要的 H5 活动,进行业务化调整配置,发布上线即可。若有定制需求,悟空提供在线开发能力,帮助研发快速产出业务组件。

下面是各功能维度的详细介绍。

1、组件/模版市场

悟空活动中台提供通用的营销组件(抽奖、表单、答题、投票等),内容组件(文本、图片、楼层、热区、导航、轮播)等。

在此基础上悟空提出组件市场概念:
各 BU 团队在线提交定制的活动组件,悟空活动设计器提供实时拉取并渲染组件能力。同时支持版本切换、环境切换等能力。

此外组件可视化拼接成的 H5 活动页后,可再次转换成活动模版,供下次或其他团队使用。通过悟空提供的市场, H5 模版或组件很方便的在内部团队中流转,从而达到高复用、高保障的目的。

2、任务中心

大多数的 H5 营销组件或活动场景都会配套复杂的营销任务和配置需求,也是众多活动系统诞生的原因。悟空为了解决不同活动的配置后台个性化问题,提出了任务中心的概念。

任务中心:悟空支持各 BU 团队线下提交「管理后台页面」,无需发版即可线上自动拉取,用户提交的页面并渲染至页面。通过该机制,悟空就具备了收容不同活动管理后台的能力。

与传统 iframe 嵌入子系统方案不同,悟空装载的是离线代码,符合悟空标准提交的制品,可“严丝合缝”热加载至平台框架。提供给用户一致的交互体验和使用习惯。

与组件概念类似,悟空提供通用任务中心(奖品配置中心、答题题目配置中心等)和私有任务中心概念。私有任务中心是各 BU 线下提交开发的活动配置页面。

图2 - 任务中心示例图

如上图,具备系统标签的任务,是由悟空平台分配给该 BU 的系统级任务。而非系统标签,如:预约、直播等,则是当前登录的 BU 业务方,自行开发上传至平台使用。(系统具备维护扩展自身的能力)

3、实时配置

营销活动支持配置实时生效,是精准营销的基础。悟空平台提供的可视化活动设计器,机制上会将配置数据(页面配置、组件配置、任务配置)与 H5 页面分离。

用户终端访问 H5 活动时,会从云端自动拉取在平台配置的活动数据,渲染至页面或活动组件中,达到代码与数据动静分离,且实时生效的能力。

图3 - H5代码与数据存取示意图

流程如上图,我们发布活动上线后,活动配置数据存储在云端, H5 代码上线在 CDN ,通过该机制成功避免修改配置,需要再次上线发版的尴尬。

H5 核心设计器功能概览

图4 - 悟空H5设计器功能概览图

4、活动配套

H5 活动可复用性高,定制化门槛低,线上时效性高是悟空活动中台最基本的能力。在复杂的线上营销场景下,作为专业的活动中台,只具备这些能力往往是不够的。悟空针对营销活动前中后时期,提供了常见的活动配套能力。例如:

素材库:对接专业的素材网站,同时悟空提供在线压缩,剪裁等图片处理能力。

自动化埋点:组件加载器会天然包装 H5 组件,页面和组件默认具备点击、曝光、操作时长等埋点能力。

微信分享:无需重复申请微信开放平台,默认具备二次分享能力。

数据线索:悟空支持在线实时数据可视化分析(计算指标、指标分档、多维度、数据透视图表等能力)。

同时悟空中台还集成借助了公司内部其他中台能力(反垃圾、实验、监控、风控等),多维度助力线上活动营销。

悟空中台产品架构图

图5 - 悟空中台产品架构图

三、活动开发为什么选择悟空?

开篇我们提出了研发的痛点

研发轮子过多,问题场景没有标准的解决方案,营销活动常见问题没有官方团队进行统一收集沉淀。

悟空中台如何服务于开发者?

悟空提供了 PaaS 平台从开发环境、开发管理两个维度,提升研发效率、研发体验,帮助研发快速交付,做活动开发者最强大的后盾。

1、开发环境

系统默认提供的组件和任务若不能满足各 BU 的业务需求, BU 就需要根据自身的业务场景去开发相应的套件(组件+任务)。这些套件可能只是业务场景不一样,但是底层能力都是一致的。例如:

  • 工具类库

    H5 投放场景多种多样,相同交互触发的方法各不相同,为了针对对不同客户端环境的适配, 悟空封装了 @vivo/wukong-api 工具类库。其中包含了强业务性的能力封装,如:环境判断、用户信息获取、不同环境的分享或下载能力唤起等等。
    在此基础上,悟空还提供了常用的研发级的能力封装,例如 cookie 操作, fetch 能力, webp ,埋点操作等工具方法。帮助前端开发者精力聚焦在业务开发,提升活动组件开发效率。同时 wukong-api 也是一个内部开源的解决方案,支持按需加载,持续跟进最新最优的解决方案。
    目前已提供 10+ 强业务能力封装。15+ 基础能力封装。

  • 内置组件
    内置组件是悟空提供给开发者快捷场景的能力封装,区别于 @vivo/wukong-api ,它可以提供给开发者使用平台的能力,如媒体选择器、图层选择器、活动选择器、富文本、遮罩弹框等等。
    举个栗子:媒体选择器,用户需要在配置面板直接上传媒体或选择已上传的媒体。该选择器会出现在不同的业务组件场景,而且强依赖平台提供能力。

示例代码如下:

<template>
  <media-picker
    selectType="image"
    :defaultImgs="url"
    :uploadDialogVisible.sync="visible"
    v-on:select-end-call="func">
  </media-picker>
</template>
<script>
  export default {
    data() {
      return {
        visible: false
      }
    },
    methods: {
      func() { }
    }
  }
</script>

目前平台已提供内置组件 8+ ,通过多个维度帮助开发便捷使用平台能力,提升活动配置侧开发效率。

  • vscode 插件
    为统一开发环境,不改变开发习惯,提供高效的开发工具,悟空开发了一款 vscode 插件,该插件集成了初始化制品,代码远程托管、NPM私服存取、活动组件管理、开发素材存储等能力。
    从组件初始化代码生成,本地开发环境调试预览到代码自动托管。一站式开发体验,帮助研发快速交付。

    图6 - vscode插件截图

    同时悟空 vscode 插件集成了本地开发工作台,支持跨设备配置同步,本地存储组件组合关系等功能,开发者所见即所得。

    下图演示了本地开发配置发生变更,多端设备实时同步的效果。

    图7 - 本地开发平台实时预览演示

  • 开发者手册
    文档即服务,上述的所有能力包括插件安装、工具使用方法、案例演示等,悟空为开发者建立了在线开发者手册,帮助开发者快速上手,方便查阅。

图8 - 开发者手册截图

2、开发管理

悟空提供了 PaaS 开发平台,帮助研发管理开发产物,流程闭环,能力沉淀。

  • 开发素材管理(组件、静态资源文件)
  • 权限管理(多人协作,多项目维护)
  • 组件管理(版本上下线,权限集成)
  • 开发社区(问题收集、反馈、沉淀)

功能显而易见,此处就不展开叙述了。

四、揭秘悟空微前端方案

通过运营、研发两个角度我们叙述了悟空中台能力。

那悟空 H5 设计器是如何做到平台无需版本升级, H5 活动制品(组件、任务)线上自动装载并直接使用?

其实这就是悟空研发团队一直在探索的 workless 的工作流中的微前端架构方案的应用。

该方案主要特点如下:

  1. 不同子系统可独立部署,也可集成访问。(可聚可散)
  2. 主系统提供热插拔能力,状态同步通道。(子系统间也可相互协作)

通过上述的特点进行放大和缩小,就是悟空的任务中心、活动设计器 。

workless 是悟空目前致力打造的高效能综合研发体系,目标是追求极致效果,解放生产力。目前 workless 在持续演进中,我们相信这将是 vivo 前端最先进生产力的代表。后续将会有专题文章进行介绍,此处就不展开了。

workless ≈ 终端即开(展现层)+云端一站式开发(研发层)+ 微前端架构(架构层)+ 数据心脏(数据驱动层)

本节将为大家揭秘 vivo 微前端架构中的核心实现原理。

1、设计思路

千里之行,始于足下。回归最原始的开发功能来叙述其原理:即前端组件不与平台系统耦合,平台具备从服务端拉取组件能力,并提供组件热插拔式渲染接口。

从服务端拉取并渲染至页面的组件,我们称之为微组件,即 RSC(Remote Service Component)远程服务组件。

图9 - 个性化组件流转示意图

为了满足线上运营策略可以实时调整,悟空制定了 RSC 活动组件是由( UI + 配置)两个子组件构成的规范。

├── code.vue     # 编辑器中渲染的UI组件
├── prop.vue     # 属性面板中渲染的配置组件
├── setting.json # 存储初始化基础配置和业务配置
└── package.json # 依赖信息

同时设计器将会提供 UI 与配置组件间的数据共享机制,开发同学可以通过 setting.json 提前预设基础设置或业务配置。机制上节省大量运营重复配置工作和学习组件使用的成本。

通过微组件的抽象和设计极大的缩短了活动页的开发周期,运营同学可以从海量的组件和模板中选择活动所需,通过可视化的系统配置,做到所见即所得,一键实时发布活动页触达用户。

同时微组件建立在不改变开发习惯基础上,减少彼此之间的依赖,降低了开发和维护成本。基于 RSC 的理念开发出的微组件具有下列特性:

  1. 高内聚
  2. 弱耦合
  3. 职责单一
  4. 可组合
  5. 可配置

演示场景

图10 - RSC组件机制加载演示图

当用户点击或拖拽编辑器左侧区域的组件到设计区域使用,设计器将根据组件的属性(名称、版本)发起 HTTP 组件请求(代码、预设配置)。之后组件会热加载到设计器中,预设配置也会随之生效。

针对不同的环境系统,不断的迭代升级认知和理念,我们经过实践发现 RSC 机制很好解决了组件和环境系统之间的协同和共享,达成了期待的结果:

运营:无需关心技术细节,可视化组合和配置,一键发布直达用户,线上实时调整运营策略,带来更多商业价值。

产品:更快的上线速度,提升了服务质量和稳定性,承载更多商业价值。

开发:提升了迭代速度,提升了代码质量,对新人友好,减少学习成本。

测试:通过可靠稳定的方案,减少重复测试工作量。

2、实现步骤

下面说明如何实现动态加载远程组件。

步骤一:本地 SFC 组件

示例:

<template>
  <div @click="test">click me | {{item}}</div>
</template>
<script>
export default {
  data () {
    return {
      item: 1
    }
  },
  methods: {
    test () {
      this.item++
    }
  }
}
</script>

注意:该SFC文件仅是组件入口,可正常引入其他第三方组件或子组件。

步骤二:编译 umd.js

vue 文件转换成 js 文件,有以下两种方案可选:

(1)webpack 配置

通过简单的 webpack 导出配置,即导出我们的目标 js 文件。

webpack 配置文件参考如下:

const VueLoaderPlugin = require("vue-loader/lib/plugin");
const path = require("path");
module.exports = {
  entry: "./code.vue", // 需要转换的vue文件路径
  plugins: [new VueLoaderPlugin()],
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: "vue-loader"
      }
    ]
  },
  externals: {
    vue: "vue"
  },
  output: {
    filename: "myLib.umd.min.js",
    path: path.resolve(__dirname, "dist"),
    library: "myLib",
    libraryTarget: "umd",
    libraryExport: "default",
    globalObject: "typeof self !== 'undefined' ? self : this"
  }
};

output 关键字段说明

library  //导出js对象的名称。
libraryTarget //导出对象的规范。
globalObject  //umd对象挂载目标对象。

(2)@vue/cli

通过 @vue/cli 命令,我们可以将 vue 单文件组件导出一个 myLib.umd.min.js(命名自定义) 。

命令如下:​​​​​​​

vue-cli-service build --target lib --name myLib ./myLib.vue --formats umd-min

若有额外的 webpack 配置,可在 vue 文件目录下新建 vue.config.js 进行配置。

扩展

@vue/cli 的内置 build 构建导出命令底层同样是使用了 webpack 的能力。

构建配置在

@vue/cli-service/lib/commands/build/resolveLibConfig.js //114行

截取如下​​​​​​​

rawConfig.output = Object.assign(
  {
    library: libName,
    libraryExport: isVueEntry ? "default" : undefined,
    libraryTarget: format,
    // preserve UDM header from webpack 3 until webpack provides either
    // libraryTarget: 'esm' or target: 'universal'
    // https://github.com/webpack/webpack/issues/6522
    // https://github.com/webpack/webpack/issues/6525
    globalObject: `(typeof self !== 'undefined' ? self : this)`
  },
  rawConfig.output,
  {
    filename: `${entryName}.js`,
    chunkFilename: `${entryName}.[name].js`,
    // use dynamic publicPath so this can be deployed anywhere
    // the actual path will be determined at runtime by checking
    // document.currentScript.src.
    publicPath: ""
  }
);

步骤三:线上渲染

如何将动态组件的 umd.js 中的组件对象导出并在 web 端使用呢?下面将揭开线上渲染的奥秘。

通过上述方法导出文件后,我们将 umd.js 打开查看一下,如下所示:​​​​​​​

(function (e, t) {
  'object' === typeof exports && 'object' === typeof module ? module.exports = t() : 'function' === typeof define && define.amd ? define([], t) : 'object' === typeof exports ? exports['myLib'] = t() : e['myLib'] = t()
})('undefined' !== typeof self ? self : this, function () {
  return function (e) {
    var t = {};
    ...
    ...
  })["default"]
})
// # sourceMappingURL=myLib.umd.min.js.map

找到关键代码

"undefined" !== typeof self ? self : this;

该代码正是我们将 vue 代码编译成 js 文件时的配置 globalObject 变量。

我们利用 new Function 构建一个函数的执行环境,然后把 self 对象进行篡改导出。再通过 vue 的 <component> 组件的 is 属性,将远程 vue 组件渲染至网页。

完整代码如下:​​​​​​​

<template>
  <div>
    <component :is="mode" v-if="mode"></component>
    <div @click="load">远程加载组件</div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      mode: null
    };
  },
  methods: {
    async load() {
      let data = await fetch("http://localhost:8080/myLib.umd.min.js");
      let mode = {};
      //此时内部的self变量,被外部变量mode代替,成功将组件对象导出
      new Function("self", `return ${await data.text()}`)(mode);
      this.mode = mode.myLib;
    }
  }
};
</script>

代码演示效果

图11 - 线上案例演示图

如图11效果,组件代码是通过 fetch 动态加载,点击组件数字可正常自增。

通过 RSC 组件加载机制实现的微前端架构,使前端业务系统与平台分离。为系统可第三方定制的能力,奠定了强大的基础,也是一切可能的开始。

3、关于数据服务

悟空微前端方案中的子系统若需要业务数据服务,此时谁来提供?

悟空默认不提供数据 API 服务,不同业务的数据服务由业务方的服务端提供。对应系统配置允许悟空跨域调用即可。

但是长久协作下来,慢慢暴露了开篇里的问题。

轻 H5 业务能否节省相应配比的服务端和测试人力?

举个栗子:

我们需要开发一个投票组件,只有两个选项 0 、 1 。通过该组件,我们能适配不同的投票场景。该组件只需一个数据接口,例如:

http://api.vivo.com.cn/.../save.do?action=

投票组件示例图

图12 - 投票组件示例图

传统来说,前台的项目或组件,需要相应后台提供数据接口,才能完成功能闭环。

如果只需提供一个接口,便要新建后台工程、申请库表和部署服务等工作,相对来说会造成不小的人力成本和资源浪费。

悟空为了解决这个问题,提出悟空微前端的下一步的演进方向:

支持用户在线可视化创建数据源,动态创建指标、维度等操作。实时动态生成数据源的 CRUD 接口。

该功能再结合悟空的数据平台能力,真实的做到了无服务介入自助生产的场景。为轻 H5 业务开发,解放了服务端、测试同学人力成本。

后续也会有详细的专题文章为大家叙述该能力的探索与实现。

五、写在最后

通过对悟空活动中台的介绍和 RSC 技术方案的讲解实现,大家应该对悟空活动中台为何具备高度定制的能力有了初步的了解。

当我们把 H5 活动制品(组件、任务)和平台框架分离,那么意味着开发者也正式成为我们中台的客户。我们为开发者准备了线下开发环境,线上开发管理。通过研发基建、流程统一、产品化服务整体为活动开发提效,加速公司营销内容生态的成长。

前端在活动中台的探索的征程并未结束,更多的挑战等着我们,微组件的使用和场景还有很多难点,后面我们会围绕 RSC 组件机制带来以下的专题文章,为大家进行详尽解析。

  • 《悟空活动中台|微组件状态管理(上)》
  • 《悟空活动中台|微组件状态管理(下)》
  • 《悟空活动中台|微组件满屏适配方案》
  • 《悟空活动中台|微组件与uni-app的多端探索》
  • ……

此外 vivo悟空中台研发团队也会持续产出一系列围绕活动产生中台解决方案系列专题,敬请期待。

更多内容敬请关注 vivo 互联网技术 微信公众号

注:转载文章请先与微信号:labs2020 联系。​​​​​​​


vivo互联网技术
3.3k 声望10.2k 粉丝