干货 | 携程活动搭建平台的前端“开放性”建设探索

2022-07-26 18:13:15 浏览数 (1)

作者简介

Jackie,携程前端开发,关注组件化开发,低代码式建设,致力于通过前端技术解决现实问题。

乐高系统是携程市场研发部开发的活动搭建平台,主要满足运营所需的各种营销、广告、频道、定制等页面的快速灵活搭建。平台在自身发展的过程中不断改进。刚开始着力于满足运营配置需求,满足业务需求,不断扩充和丰富组件库,目前平台已配置了10000 有效页面,同时在线页面达到1000 ,组件类型300 。当体量达到一定程度后,我们又在思考,平台能力的边界在哪里,如何推动平台创造更大的价值?

这个时候,建设平台不再局限于扩展组件等基础建设,会更多地考虑如何将平台建设为一种“开放性”的平台,将平台优秀,成熟,可扩展的“点“开放出去,使平台或者平台相关技术在其他团队或者场景中有更多的应用,产生更大的价值。这种开放性的思路,也积极促进了平台的进一步发展。

这篇文章将总结我们在平台建设中一些相关思考和实现细节。

一、组件开放性建设 - 自定义扩展

在经过了自给自足的初期阶段之后,我们觉得组件化、“低代码式” 的搭建思路是正确的方向,一方面给运营同学提供了极大的方便,另一方面组件的“良性循环、迭代”促使组件功能更丰富,更灵活,更可靠。并且开发人员不需要把时间浪费在重复劳动上,可以设计和开发出更具通用性,更可靠,更“新”的组件模块。

因此,除了在乐高平台上“发挥能力”,我们希望乐高上的“业务组件”,“配置能力”等可以几乎“无限制”的提供给其他开发者在其他需求中使用,因此进一步改造了平台,使乐高平台的组件库更容易扩展,性能更好,使用性更广。

乐高渲染和开发环境都是基于公司成熟的服务端渲染框架NFES,技术栈选择为react nextjs mobx。

如渲染示意图:

乐高整体框架相当于一个容器:

1)负责组织组件关系

例如父子组件关系,嵌套关系,组件依赖关系,数据依赖关系等。

2)解析组件

容器会提供公共模块的注入(如:react,react-dom,公共库等),目的是为了公共依赖的统一,并且还减小包的大小。容器在客户端和服务端分别解析当前组件,以供渲染使用。

由于页面其实是由多个不同组件构成,因此需要支持到组件级别深度的SSR(实现方式在后文会有说明)。

3)递归式渲染组件

组件结构可以看成是轻量的DSL,整体简洁、扁平,不过为了能够灵活的处理组件依赖、控制组件渲染时机(先后)、处理父子组件等问题,容器内部在node中间层“处理完数据结构”之后采用了“递归式”渲染的方式处理组件。例如tab切换类组件,tab关联的可能还是tab切换,如此反复,其实就是个递归的过程。

1.1 构建组件

乐高组件最终形式是一份UMD代码,事实上是一份同构代码,既能在服务端渲染又能在客户端渲染。页面渲染的时候,能够在服务端和客户端“按需”的,“动态”的拉取某个组件的资源包,这些也是为了组件解耦,方便扩展新组件,提升页面渲染性能,同时为“多场景的使用组件”这一目标打下基础。

“多场景的使用组件”,即为:

  • 可以在乐高渲染引擎中动态使用
  • 可以npm单包输出
  • 可以通过sdk(乐高渲染外壳 webapi接口)整体渲染引入到其他三方页面

这些是乐高组件可以充分发挥能力的几个方面。为了达到这些需要在组件构建环节进行处理,如下:

1)构建“动态组件”资源包(js、css),并支持同构渲染

构建工具默认打出的UMD包无法满足乐高特定的渲染需求,需要自定义一些webpack插件干预构建结果,如:如何解决动态组件的公共依赖问题,如何使得渲染引擎能够在客户端和服务端都能够解析到动态组件实例。

首先需要改造组件的最终形式,使其可以接收公共依赖(react,react-dom,公共依赖等),这时可以修改默认打出的UMD自执行函数,使其返回一个普通函数,“依赖”可以通过渲染引擎在解析时通过“形参”变量传入,如:

如图,banner组件依赖React等,因此构建自动改变组件打包结果,使得banner成为这样一个“function”:通过执行传入e.React,e.ReactDom等依赖后,return得到真正的组件包。

实现方法是在自定义plugin中,接管组件的打包过程,替换依赖部分的代码,将真正需要的依赖如react,react-dom等以形式参数的“代码字符串”写入到组件文件里面,最终通过替换字符串代码改写组件构建的结果(组件的文本字符串)。

此外客户端还需要使得打包后的资源能够挂载在某个全局对象下,以方便渲染时候按需获取组件对象,这就需要通过配置构建工具实现。

而webpack的配置项globalObject的作用为:

当输出为 library 时,尤其是当 libraryTarget 为 'umd'时,此选项将决定使用哪个对象来挂载 library。

这个正好可以为我们所用,如下:

客户端通过注入脚本script的方式将组件挂载到前文提到的全局变量window.LEGAO_COMPONENTS上面,渲染引擎将通过组件唯一标志(组件英文名)获取到组件实例。

最终在服务端和客户端通过不同的方式获取到真正的组件:

客户端:

window.LEGAO_COMPONENTS[component.name](dependency).default获取

服务端:

component.realComponnet(dependency).default获取

2)实现按需(服务端和客户端)动态拉取组件

“乐高组件”可以托管到到公司框架部门开发的现成的静态资源管理平台ARES上面,它既能快速将我们的资源(组件的js,css)发布到CDN上面,又能够帮助我们管理资源version,此外还支持自动线上构建(CI/CD)发布单个或多个组件包资源,相关API也很完善,如:

乐高脚手架通过ARES的“commit信息触发CI/CD”机制,将单个或多个组件发布到CDN上面,当页面被访问时,渲染引擎会根据当前所需的“组件类型”按需拉取组件。

渲染引擎通过ARES相应的API(如:在node端预热组件资源,获取单个组件最新有效版本,资源地址等)获取到资源地址,然后在服务端下载组件的javascript资源文件的文本内容,并通过requireFromString将字符串文件转换为内存变量module,即而完成服务端的渲染,而客户端则动态加载这些异步js,完成客户端的渲染。

1.2 乐高脚手架@ctrip/legao-cli

乐高平台目前不仅“服务于”市场部活动组,越来越多团队通过自定义组件的形式接入,例如一些频道页的模块,用了现成的组件,业务逻辑不同的组件则由团队自己定制开发。不仅达到组件“开发一次,到处使用”,还使得相关团队的运营也能充分利用到乐高的便利配置系统。

为了建设“开放性”平台,我们也需要做好开发环境的建设,以方便更多团队开发组件。

@ctrip/legao-cli 能够创建跟线上运行环境高度一致的“开发环境”,能够通过proxy模式,代理测试线上代码,能够构建组件资源等。如下:

开发环境项目作用:

  • 提供组件本地开发环境
  • 可作为组件仓库
  • 可配置本地开发组件配置信息
  • 配置线上打包(CI/CD)配置信息等

项目目录说明:

  • common : 组件公共模块
  • packages : 组件目录
  • legao.config.js : 组件开发配置项
  • postcss.config.js等

其中legao.config.js影响开发环境最终拉取组件形式:

配置说明:

  • env:组件及渲染server的接口请求环境
  • components:当前需要开发调试或者代理的组件名称
  • devMode:为开发提供两种调试模式:

1)dev:本地开发模式

组件默认走本地资源,相当于import xxx的形式,这种方式主要是方便开发环境的调试(更好的热更新),开发环境会做好组件的引用,标记,获取实例等工作。

server端读取组件目录mock/mockData.json的配置数据,作为组件的属性传入并渲染组件。

2)proxy:代理模式

在本地能够代理和调试线上页面内的对应组件,server端会请求线上页面的组件配置数据,匹配替换页面内对应组件的js模块为本地模块。

开启这种模式后,通过一些配置就能完全模拟生产环境。

步骤为:设置生产host→在legao.config.js中配置需要代理的组件名称→用https协议打开生产环境的页面地址→完成。

1.3 开发环境其他配置项

开发环境默认设置了满足当前开发和构建需求的各种配置,如babel配置(plugins, preset等),postcss配置,也支持扩展、修改这些构建配置项。“构建组件”的时候会读取合并之后的配置项。

如postcss配置项,目前默认是采用postcss-px-to-viewport来处理组件的UI适配,可以根据需求修改或者增加其他处理配置项。(这里推荐用vw来做适配,比起rem,组件不依赖外部。像rem需要依赖根字体大小,但不同项目设置的计算比例是不一样的,所以根字体大小无法保证统一,这样不利于组件嵌入到其他项目(如sdk方式,npm包方式))。

1.4 公共库注入:@ctrip/easy

开发一个再简单的页面都需要面临“水面下的冰山”,除了要实现核心逻辑,还需要实现其他必要功能。如“分享功能”,需要兼容app环境,微信h5环境,微信小程序嵌套环境,快应用,甚至其他app环境等等的方法,还有跳转、登陆等,这些往往比我们最终要做的页面的核心业务还要复杂。于是乐高结合现实开发需求,总结归纳了常用的方法,总结了通用库:@ctrip/easy。

这个库提供了封装大部分情况分享的setShare方法,兼容大部分情况的jump方法,还有login,model等等。乐高框架会向组件“注入”这个几乎每个项目开发都需要的“业务方法”库,使得任何乐高组件在实时渲染环境都可以直接使用。有了这个“环境”,开发者能够集中精力快速开发组件中的业务核心代码。

当然它也能单独以npm形式引用。目前在公司内有了很多接入,节省了开发时间,提供了最佳实践的方法。

二、组件开发特殊函数 - 为现实开发需求设计

作为一个开放性的平台,在现实开发组件过程中,有一些比较特殊且必要的情况需要支持。

1)组件不是扁平罗列的结构,而是可以相互依赖和嵌套的(如父子组件,先后渲染依赖等) 2)需要有数据上的通信机制(数据依赖) 3)需要有组件级别的SSR

为了满足这些需求,乐高设计了一些简单有效的机制。会“预处理”一些能够运行在node端(中间层,页面渲染之前)的静态方法,渲染引擎会遍历所有的组件,在拿到组件实例之后,判断哪些组件是否含有这些静态方法,然后通过执行这些静态方法,把影响后续组件渲染的所有数据处理好。

方法如下:

2.1 beforeRender静态方法
作用:组件嵌套、依赖,组织组件关系等
组件使用实例:
如上图,这是一个tab切换类组件,它的作用是切换其他任意的组件,它需要在beforeRender里面声明好页面上的哪些组件是它的子组件。为了处理这种父子依赖关系的组件,渲染引擎会利用这个函数在渲染之前就把所有父组件和子组件分开,放在两个数组中,并把最终有依赖关系的数据结构传给页面渲染的部分,进行后续渲染。
说明:
第一个变量componentData,为该组件的配置数据,渲染引擎根据componentData下配置的关联的“子组件id”等数据知道“当前页面”上具体哪些组件是这个组件的子组件或者父组件,第二个变量为一个对象,对象包含几个框架提供的方法,如:

分别对应下图中的几种情况:

2.2 asyncData静态方法
作用:可以作为组件的服务端,中间层(node环境)
说明:主要用于在服务端请求接口获取数据,需返回一个promise,最终执行结果数据,会在中间层赋值给当前组件的一个属性:__FETCHED_DATA__。在服务端渲染的时候,组件第一时间可以拿到请求之后的数据,所以可以更充足、更细致的“ssr”。组件内部则可以根据“这个变量”来选择是服务端渲染还是客户端重新请求数据去渲染。
使用demo:

这个函数的作用还有以下几个其他的方面:

1)因为这个方法的执行环境是node,所以可以“直连”调用node接口和方法。不过这里的require是作用在node环境的,其实并不需要解析,例如require(“ctriputil”),于是可以通过配置rule,利用string-replace-loader自动将require替换为__non_webpack_require__,来防止webpack处理require的资源,例如:

2)req可以挂载单次请求级别的全局变量,例如一个页面上有多个同一类型组件,每个都需要在node环境获取用户信息,那这个信息就可以挂载在req,防止同种组件重复请求。

3)allComponents参数:可以在此基础上自定义逻辑,如修改其他组件的property控制它的具体渲染。

4)如有seo相关需求,可在asyncData resolve数据中返回自定义seo数据(框架会提前处理好动态meta等seo相关数据)。

2.3 provideData静态变量
作用:状态管理,公共变量,组件通信
说明:组件维度将某个变量注册到全局store,可以提供给其他组件使用(订阅数据)。如下图所示,一个页面上有一个“定位组件”和几个依赖定位组件的组件,这些组件不仅需要在渲染时候能够响应定位组件分发的定位数据,还需要再定位组件“点选城市”之后能够响应数据的变化,从而刷新渲染。
实现是基于mobx,乐高渲染引擎会先在中间层通过组件上的provideData静态变量搜集所有需要注册到store的数据,然后在页面渲染获取组件module的时候,用mobx-react的observer包裹,使这个组件变成“reactive”的,最后将这些数据的宿主对象extraProps挂载在组件上,用来获取数据,和改变全局数据。如props.extraProps.geoInfos等。使用demo,数据提供方:

数据使用方:

也可以放在如useEffect中监听(类组件可通过在componentDidUpdate等生命周期中监听)props.extraProp上的具体某个字段,如定位信息geoInfo。

那么所有监听了extraProps.geoInfo的组件都会在“定位组件”分发了定位信息“ geoInfo ”之后触发自身的渲染更新。

三、“在线依赖组件”探索 - 在线公共组件

很多机制的思考、开发动机都是来自于现实问题,开发研究也是不断提取这些问题的本质,抽象化,并把解决方案具体化,在线公共组件也是在实际开发问题中提炼出来的。

考虑这样一种情形,一个产品组件A,经过不断的迭代扩展,有了十数种样式,代码实现很简单,先抽取一个本地依赖的子组件如SingleProduct,然后通过产品组件A向SingleProduct传入需要渲染的模板类型type字段等,最终在SingleProduct里面进行区分和渲染不同的UI。

然后,需求来了,我们又要新增一些业务逻辑完全不同的产品组件B,C,D等等,并希望能够完全复用组件A的样式和业务逻辑(跳转,埋点等),甚至是为B,C等组件新增的UI,也希望能够在A组件里面能够复用。

这个时候一般做法是直接复制组件A的代码到组件B本地,或者破釜沉舟,将组件A和组件B等通用的样式抽取成为UI组件,后者能解决一部分模板复用问题,但是事实情况是如果需要修改的UI组件的一丁点代码,都需要将所有依赖它的组件A,B,C,D等等分别打包,发布,测试,上线,这无疑增加许多维护成本。而且“代码复制,搬家的方式”从开发角度来看,存在代码同步的问题,维护起来非常困难。另外,组件A,B,C等等每一个组件都打进来了需要复用的UI组件的所有资源。如果都用在同一个页面上,就等于重复代码一大堆,这又肯定增大了总体资源的大小。

再比如:平台已经有视频组件,运营同学可以根据需求,给运营页面增加视频组件,他们可以自行配置视频地址,封面等。这种视频组件经过了长期迭代,已经是一种比较成熟的视频组件了。如果新增的一种产品业务模块,正好需要实现播放视频的效果,那是自己copy代码,重新实现一遍,还是直接复用之前开发的视频组件呢?最方便的做法是希望能够动态复用视频组件,即,在“产品组件”需要视频组件的时候才会拉取视频组件,不需要的时候,代码中是不会有视频组件的资源的。

我们第一时间会想到走npm包的方式import引入,这是一种方式,但是这种要求我们引用的npm包的版本是最新的、没有问题的版本。首先那么多的组件是否有精力都去维护npm包是一个问题(因为主要的使用方式是“乐高拉取CDN组件”,一般不需要打npm包,而且组件普遍功能迭代很快),其次npm引入的资源被直接打在了你的组件包里面。

我们都知道package.json里面的main,module,browser等的作用,它为了使最终包能够根据不同的环境打出相对纯净的包,设计了区分不同环境的标志,并通过构建工具按需打出最终包。

所以,希望能够考虑一种比较好的方案,既方便新增,维护,使用,又能够独立,减轻页面资源大小。

事实上,通过这个案例,可以思考更多的类似使用场景。试想,如果一个部门的所有的公共组件资源能够以一种在线引用的方式维护在CDN上面(云端),以供大家使用,这是不是一种非常方便复用公共组件的方式,同时非常方便维护更新。这是不是能够大大促进“组件化”开发的良性循环呢?

如下图:

乐高组件众多,300 组件,有丰富的组件资源,应该是比较适合这种的一种场景。

接上文产品组件的问题,现在乐高有了一种这种“云”依赖组件的机制,我们可以开发“UI原子组件”,这种组件就是纯静态的UI组件,也是普通的乐高组件,支持跟其他“乐高组件”一样的各种机制,拥有一样的数据结构。

那么现在我们可以在产品组件A,B等里面声明依赖“UI原子组件”,然后传入“原子组件”所需要的所有的业务字段,渲染类型字段等,这样就可以使所有的产品组件(A,B,C…)使用到“UI原子组件”的样式,这个组件能够不断迭代优化下去。而需要修改、维护UI的时候,基本只需要发布“UI原子组件”这一个组件就好了。

乐高组件可以通过fnGetMetaComponent在任意组件里面动态的拉取线上的其他组件并使用。

此方法会挂载在组件props上,“在线引用”其他乐高组件,并覆盖其渲染props(如id,property等)。产品列表中需要新增视频展示,则产品组件可以关联引用“视频组件”,并将列表项的数据(如视频地址)传给视频组件,用作渲染。

真实使用情形有两种:

情况1:通过已保存的组件的唯一id拉取。

这种在乐高平台的情况是:依赖的组件已配置到页面(已保存,并生成了id)的组件。

其他组件可以用fnGetMetaComponent通过id依赖该线上组件,这种的使用场景下,被依赖的组件能够读取两部分配置数据,一部分来自运营通过乐高offline配置,一部分是来自开发赋值,如:

demo:

情况2:通过“组件类型名”拉取,支持服务端请求数据:

这种就相当于import一个组件,但是没有实际打包引入,而是通过在线资源引入。

demo:

此外还可以提供插槽,如fnRenderProps方法:自定义内容插槽,可在父组件中自定义内部渲染,例如抽取AtomSwiper组件,只负责引入swiper和轮播,至于轮播的内容(组件)则可以通过fnRenderProps定义。

实现的大概过程如下:

渲染引擎在node环境(页面渲染之前)识别“需要依赖的”组件,在线拉取和解析,并处理其服务端请求。

在处理这些“原子组件”服务端请求的数据时需要通过renderBy这样的“依赖它的组件id”来区分哪份数据挂载在哪个渲染运行时下,因为原子组件需要考虑一份组件多次复用的情况。

fnGetMetaComponents则负责从所有的“在线组件”中查找,并覆盖其渲染属性,并返回最终需要渲染的组件。

目前乐高已经有较多这种 “在线组件”的使用场景。例如上文说到的产品组件,通过这种方式拆分为几个组件:

1)产品组件(包含业务逻辑)

2)轮播组件、视频组件

3)原子UI组件

如下图:

如此,三个组件都可以单独复用,单独按需拉取,单独维护。

四、@ctrip/legao-nfes-sdk建设:组件移植渲染   

乐高组件建设希望开发的组件,无论业务组件也好,通用组件,在线组件也好,能够不局限于只在乐高平台本身使用。

当然组件已经支持打包npm包了,为什么还需要sdk呢?

还是为了方便维护和使用。通过sdk能够根据运营配置需求动态拉取需要的组件,以及组件资源,并且这种对接方式对于接入方来说简单,易用,更新任何组件都不需要重新发布。能够做到“动态资源,实时拉取,按需加载”,使“乐高组件”能够无缝渲染到其他页面。

如下图:

嵌入案例:

实现方式其实就是把乐高渲染引擎拆分成渲染sdk和webapi服务,服务是基于公司的serverless开发的webapi,扮演乐高渲染引擎的中间层服务的角色。

目前这种对接方式已经大量在各种对接三方页面的场景中使用(如任务组件,选货组件,星球号,商城,会员,售卖等等),事实证明,这种轻量、友好的方式能够推动乐高组件的更多使用,也属于乐高“开放性建设”的一个方面。

五、动态表单能力建设

乐高有大量的组件,每个组件都有不同的属性配置面板,如果都去花费人力开发维护,则无疑是一项巨大的工作,不利于组件的扩展,于是我们基于自身需求,开发了“动态表单”。初期是希望能够解决组件的配置问题,但是后来建设中觉得这种表单其实有更广阔的应用场景,可以走出乐高,走向公司,为更多类似的表单场景提供配置能力,这也是乐高“开放性”建设的思考的一个点。

动态表单孵化于建设平台过程中,是一种可视化在线配置动态表单方案,专注于解决通用常规表单的可视化自由配置,目前能够解决大部分的常规表单的在线配置场景,支持数据联动、复杂数据嵌套、拖拽布局等。

已实现的动态表单具有如下亮点:

  • 可视化:可视化搭建、修改和预览表单。
  • 可拖拽布局:控件可在画布内拖拽至任意坐标,以搭建最佳布局。
  • 可扩展:可二次开发,可扩展控件集。
  • 可联动:某个控件可以控制别的控件的显示和隐藏。
  • 支持复杂数据类型:支持对象结构以及对象数组结构等复杂数据类型(JSON)的配置。

基本架构模块如下:

渲染介绍

系统有表单生成器编辑面板Form Generator,表单渲染入口Form Viewer两个主要模块。这两个模块共用常规的基础组件如输入框,颜色选择等,还有一些基于业务扩展的复杂组件,例如热区选择,视频上传,数据聚合(JSON列表)等。

目前,动态表单已经大量使用在乐高的组件配置界面,如:

当然,乐高开放性建设的最终目标是,期望动态表单能够作为成熟的独立的npm包,为其他表单场景提供公共功能,打造轻量“泛应用”动态表单。

六、其他方面

乐高系统的灵活,以“开放性”建设为目标还催生了一些其他方面的能力。

6.1 静态页面建设能力:DIY

乐高除了有已开发好的组件外,还提供了一种可供运营同学配置静态模块的能力,称作“DIY组件”。这部分可以让运营同学自行拖拽配置自定义的模块,例如配置不同形式的表单表单提交模块,配置静态页面,最终以组件形式由乐高渲染。

乐高开放了这个界面的拖拽配置能力和接口服务能力,比如你有一个需要运营自己拖拽配置界面的需求,但是不需要乐高默认的渲染,那你可以在这个场景下新增场景,并配置自定义字段。然后将上图中的“可选字段组件”跟这些自定义字段进行关联,使用方(也就是开发同学)通过接口获取运营同学配置之后的数据,就可以根据实际需求进行渲染(可不依赖乐高渲染)。

6.2 产品样式扩展:产品画布

乐高基于“DIY组件”扩展了产品画布的功能,之前提到的 “原子产品组件”配合产品画布功能可以扩展更多“自定义的样式”。

运营同学通过乐高的“DIY组件”,可自主拖拽配置产品UI界面,最后只需要把“产品画布”的场景号配置到上述的“原子产品UI组件”的属性中,即可实现自定义渲染界面,如图:

6.3 npm仓库建设

乐高打包机制增加了构建为npm包的配置,并打包到单独的文件夹中(方便发布npm包)。

脚手架在打包“乐高框架”所需要的umd包的同时,也同时打出适用于npm方式引入的React组件包。发布了这些npm包(react组件)后,就能被使用方import引用。

另外乐高有很多一些交互复杂,并且效果不错的组件,如转盘组件,九宫格,红包雨等等,这些组件默认是跟业务逻辑紧密相连的,有使用方想用这些交互,而里面的具体逻辑自己实现,所以开放一些成熟的静态组件是有必要的。

乐高基于目前的组件库在建设一个静态组件库,基于storybook,根据“实际需求”去逐步开放一些纯静态的组件,例如转盘抽奖等。

另外,可以开放一些上面提到的UI原子组件如(产品UI组件,定位组件等),这些组件是相对独立,可复用的组件,可以尝试在乐高之外的其他页面上复用,走乐高sdk复用,或者npm包复用。

6.4 热区能力建设

热区能力最初是由配置banner而来。刚开始为了复杂多变的banner配置,我们开发了banner组件,多banner组件等,但是后来发现,需求要配置比例大小不一的多banner,甚至是图形不规则的复杂banner,这个时候再从配置上增加字段显然是不好的,配置复杂不说,图片被切的支离破碎,从最终的渲染效果上有可能因为图片较大,网络较差,看起来也是支离破碎的。

从运营同学的角度看,这简直是折磨,要切一大堆的图,要配置一堆参数,到后来由于要配置极度不规则的图形(例如地图那种banner)直接没有了办法,这才催生了我们开发热区banner。

乐高支持的热区可以支持任意图形的热区(通过多个定位点连接起来)。

具体实现方案也经过了一系列的改进,最开始的基于html的map和area标签法,如:

后来优化为记录点的坐标以及图片的宽高方法,通过点击事件获取到点击时相对于图片的坐标。最终遍历每个热区的描点坐标信息,判断点击坐标是否在当前热区中(实现思路——射线法,从点发射一条射线,点在多边形内则会产生奇数个交点,点在多边形外则有偶数个交点)。

热区banner一上线就大量被使用,节省了运营同学配置时间,极大的提高了banner配置的效率,也优化了这一块的渲染效果。

既然热区这么好用,应该扩展到别的方面,于是就有了热区规则,热区tab切换等。甚至是可以考虑将具体交互,如点击跳转,点击领券,点击弹出等等封装为事件组件,将UI和交互事件解耦。

从配置面板到热区渲染这一套流程可以开放出来给开发同学使用,如上图的banner,甚至是更复杂的拼接banner,即使是开发来做,都需要耗费大量精力去摆位置,切图等。如果遇到复杂的banner,可以直接使用乐高的热区banner配置。如果需要的只是热区的配置和区域数据,可以利用乐高开发的“热区采集工具“收集运营配置的热区数据,根据实际开发需要处理渲染或逻辑。

最后

上面提到的众多的“开放性”建设,有成熟的,有在实践尝试中的,还有正在规划和思考的,我们的目标是立足当下,维护好成熟的,努力建设还在实践尝试中的,然后不断的思考优秀的、可以更进一步优化的“点”。

开放性建设是双向促进的,既能给开发同学带来方便和最佳实践,同时也在反向推动“乐高”平台的优化,给运营同学带来诸多方便。

乐高的开发是不断的发现和总结来自于现实的问题,提取问题的本质,抽象化问题,具象化解决方案,来逐步优化和扩展平台,这样才能在有限的资源下推动平台创造更大的价值。

【推荐阅读】

  • 携程基于 GraphQL 的前端 BFF 服务开发实践
  • 携程微信小程序如何进行Size治理
  • 从47%到80%,携程酒店APP流畅度提升实践
  • 携程动态表单DynamicForm的设计与实现

 “携程技术”公众号

  分享,交流,成长

0 人点赞