大体划分
首先基于前端技术的演进,大体上可以将天猫搜索前端的发展历程和未来趋势总结成几个时代:
之所以这么划分,主要是基于天猫搜索的前端技术方向以及天猫乃至淘系前端技术体系发生的较大变化。而在这其中可以再提炼时代的关键词:
下面就来介绍各个时代天猫前端的技术状态和一些思考。
PC 时代可以说是天猫搜索前端的上古时代,那还是手机流量非常贵的 3g/2g 时代。因此大多还是简单的 WAP 页面,大多数人都还是习惯用 PC 进行购物。
▐ 技术方案
模块化
PC 时代的前端技术方案采用的是 KISSY + MUI 3 ,MUI 3 就是那套 KISSY 的模块规范 KMD 。页面上还有一些非常老旧的 YUI 依赖。在那个 jQuery 王霸天下的时代,基本上天猫的 PC 页面都是采用集团内部根据 YUI 自研的大而全的 KISSY 框架。KISSY框架包含了前端所需要的几乎所有基础功能:模块加载器、 DOM 操作、事件处理、异步请求等等。
页面渲染
PC 天猫搜索采用了同步渲染的方式,页面的主要内容通过 VM 模板渲染输出到前端,因此前端需要维护大量的 Velocity 模板代码以保证 HTML 中的内容和自己 JS 代码能够配合得当。需求修改一旦涉及到 HTML ,就需要改造 Velocity 模板从而陷入 aone 发布的同步模板和部署炼狱之中。
模块管理
而 PC 时代的天猫搜索前端页面的模块管理方式十分粗暴,基于业务逻辑划分的大量 KISSY 模块,他们共同操作一个统一的页面 DOM ,交叉修改时有发生。模块间的通信也是直接调用模块实例方法来实现,模块耦合非常严重。
▐ 小结
由此可以看到那个时代最主要的问题有如下几个:
随着智能手机的普及,4g 技术的发展使得流量费用的大幅下降,无线端需求不断增多,流量不断增大,天猫前端也将方向定为 mobile first 。显然 PC 搜索这一套技术方案无法满足 H5 搜索需求,因此 H5 搜索采用了全新的技术方案。
▐ 技术方案
模块化
H5 搜索采用 Zepto + MUI 4 作为模块化方案,MUI 4 相对 MUI 3 主要变化是将 KMD 规范修改成了业内较为通用的 AMD 规范。同时引入了前端模板来实现 DOM 的更新,首先 Zepto 要比大而全的 KISSY 轻量许多,同时前端模板的引入使得模块自我管理了 DOM ,有效的降低了模块之间 DOM 操作交叉引发的混乱。
页面渲染
H5 搜索的页面渲染基于应用控制,但仅仅将框架、筛选等一些少量 DOM 同步渲染,大多数 DOM 如商品列表都是采用异步渲染前端模板的方式实现。因此同步模板中的内容量相对较小,维护成本大幅度降低。同时引入了前后端分离的 wormhole app ,前端不再需要维护不熟悉的 VM 模板,而是维护和异步模板语法相同的 xtpl 模板,进一步降低了模板维护的成本。
模块管理
与 PC 连翻页都是页面跳转的方式不同,如筛选、翻页等行为的渲染需要前端异步处理,模块通信的需求也相对复杂。H5 搜索采用了 MDV 框架——通过将模块封装成一个个模型,再由 MDV 建立的模型之间的订阅机制进行模块的实例管理,以此来实现模块通信。
▐ 小结
H5 搜索采用的技术解决了几个 PC 搜索时代的问题:
但随着业务的发展,依然发现有一些问题:
在 all in 无线的大环境下,由于搜索结构较为固定、无线端开发资源较为充裕等等各种原因,天猫搜索业务在端内完全被 Native 承接,而 H5 更加注重端外和站外。这也导致了 H5 搜索的业务能力和 Native 脱节,仅保留核心筛选功能以及唤端功能。因此 H5 搜索很长一段时间几乎没有前端资源投入,而在 2017 年 8 月左右由于组织架构调整,我手上的天猫商品业务交接后,我开始逐步接手天猫搜索前端业务。而我要做的第一个需求,就是一个包含 H5 搜索 80% 功能的 H5 搜索凑单页。
▐ 技术方案
模块化
我和团队同学采用 Preact + MUI 5 方案来实现,MUI 5 其实就是 MUI 4 的升级,采用 CommonJS 的写法来编写模块,再经过构建工具编译封装生成 AMD 模块。其实当时会场已经大规模采用 Vue Weex 了,那我们为什么要采用 Preact 呢,主要是基于如下几点考量:
因此在这个阶段,我们使用 Preact 做了很多的搜索周边的功能页面,如上面提到的 H5 凑单、还有猫搜的领券弹层、凑单领券页面、搜索分类页面等等。
页面渲染
由于 Preact 的异步渲染特性,页面渲染我们直接抛弃了应用,而是采用斑马源码页面的方式。这样的好处是,前端不再需要和 aone 频繁的打交道,服务端只需要封装一个 mtop 接口给前端,前端在斑马上创建基础 HTML 引入资源并调用接口获取数据渲染即可,前端自己完全掌控了调试、开发节奏。由于对于应用的依赖仅限于一个 mtop 接口,因此前端可以提前定好数据规范,在服务端开发接口的同时基于本地调试工具进行 mock ,大大降低了联调成本。
模块管理
React 生态有非常多的模块管理方案,我们调研过的 Flux、Redux、Reflux 等等都可以满足我们的需求。但经过仔细的思考我们选择直接撸,不使用任何框架来管理模块通信。如此考量的原因主要遵循如下理念:
▐ 小结
引入 MV* 框架后,我们的技术方案又得到了一些提升:
这里还是有一些问题:
借鉴天猫超市极致 H5 经验,搜索在分类页做了非常多性能优化尝试,比如:
1、代码优化
2、DNS优化
3、接口优化
4、埋点优化
5、加载优化
6、体感优化
17 年双十一之后,搜索一成不变的模式也受到挑战,在这个拥有大量流量和强用户心智的场景下,搜索能够玩出更多场景化、行业化、品牌化的玩法。因此产品上需要更多快速试错的机会,也导致对于动态化的诉求则日益增加。而由于一些组织架构调整,天猫搜索的客户端缩减到 2 人—— 1 个 ios 和 1 个安卓。客户端需求开发因为需要发版而迭代缓慢。因此催生了 Native 内嵌 Weex 坑位的技术方案和 Weex 模板下发的 Oreo 平台。
▐ 技术方案
模块化
其实在我进入到搜索之前,Weex 坑位方案和 Oreo 就已经在小范围使用了,有一部分客户端同学写的 Weex 1.0 的 Vue 模块,另外还有一些 Weapp 模块。这些模块由于人员调整和离职已经很难找到仓库源码了。另外这些模块也是仅面向 Weex 的,对于 H5 来说是完全无法使用的。因此在我进入搜索之后,提出了Prax的解决方案,本质上就是使用 Rax 0.x(当时还是0.4) + Preact 通过编译转换供Weex和 Web 同时使用。之所以要用Preact,主要是考虑到我们有端外 H5 场景,但是 Rax 当时 H5 性能是真的堪忧,而且当时的Weex团队方向对H5性能优化投入并不多。另外从上面 MV* 时代可以看出我们在 Preact + MUI5 的技术方案上也有较多的沉淀。最终我们确定了采用编写 Preact 代码,通过自动化工具转换成 Rax 代码的方式来实现一份源码同时复用在 Weex 和 Web 的技术方案。前端的开发流程就变成了如下:
页面渲染
Prax 页面采用斑马来处理页面渲染。由于源码页面都是采用打包的方式,因此脚手架中分别有 Web 和 Rax 两个打包 webpack 配置,构建时候分别运行生成 Weex Bundle 和 Web Bundle 。由于 Web 端本质上就是之前 Preact 的方式,因此渲染沿用了过去 Preact 的页面框架。而Rax则是开发了一个全新的页面框架。斑马渲染 Weex 页面本质上是通过xtpl控制 Weex Bundle 代码生成,所以源码页面编译出来的 Weex bundle 只需要在外通过 xtpl 增加一层 Weex 的头尾就可以了。
对于 Native 内嵌坑位,则是采用模板下发的方式。前端会编译生成一个自身可运行的 Weex Bundle ,包含头尾等完整信息,然后通过发布推送到搜索应用的机器上。当客户端的搜索请求到达服务端,服务端基于业务逻辑确定需要使用哪些模块,将模块相关的信息告诉客户端,这些信息包括模块名称、模块位置、模块数据。客户端渲染 Native 页面时根据模块位置创建对应容器,并根据模板名称请求 Weex 代码,最后将代码渲染到 Weex 容器中,并传入模块数据,实现模块最终渲染。而在 Rax 代码中,我们可以通过 window.weex_data 拿到客户端塞给容器的数据。当然这些适配都可以在构建层面处理,一般情况下开发者只需要关注纯粹的 UI 模块编写即可。
模块管理
Prax 页面的模块管理和 Preact 开发时期基本一样。但是对于 Native 内嵌 Weex 坑位,模块管理方案就完全不同了。看起来 Native 内嵌 Weex 坑位只展示一个Weex模块,但实际上是一个只有单一模块渲染的完整 Weex 页面。因此 Weex 模块之间是无法通信的,它们甚至都不在一个 Weex 容器内。正因为如此 Native 内嵌 Weex 大多做一些纯展示的需求,对于复杂联动需求依然是交给客户端完成。另外客户端提供了一些通用能力,封装在 Weex 模块中,如:
▐ 小结
经过了 Weex 时代,我们的技术又得到了提升:
但这里还是有几个问题没有解决:
至此我们可以发现,现在搜索前端核心解决的问题逐渐变成了共建和规模化了,不再是之前单纯前端技术问题了。既然要解决共建和规模化,我们可以看看规模化成功的一个典型例子:会场。过去会场也是一个个页面前端人肉开发,到后来有了斑马就可以由运营模块化搭建了,前端只需要开发一些基础模块就可以满足所有行业的诉求,多么的美好!那么搜索是不是也能搭建呢?
答案是:“是的”,但毕竟场景不同,我们不能和会场一样楼层 duangduang 地堆砌,为什么呢?
首先在场景的层面有比较大的不同。在用户进入这个会场时,已经决定了当前的场景了,一个会场页面就是一个场景,因此在这个会场页面下,只需要圈出这个场景的模块就可以了。而对于搜索,所有的场景都需要在搜索这一个页面展示,而在搜索如何定义一个场景呢?
这么多维度,一个或多个交织在一起才定义了一个场景,而在这个场景下会展示一个或多个模块。
以 Query 举例:
当然,会场模块里面的货品完全可以多样化,另外现在会场基于袋鼠也有千人千面模块的能力了,这里是拿之前的会场做例子
基于这样的思考,搜索启动了一个长颈鹿项目,以之前搜索域就有的一个产品——品牌 Minisite 作为切入点,为品牌商提供了一个全新的品牌运营阵地
如上图,从左往右分别是三个版本的 minisite 。这里可以看到, Minisite 作为品牌在搜索的一个搭建阵地,一直采用的是提供模块让品牌商填入数据的玩法。品牌商自定义程度很低、维护欲望很低,因此导购效率一直不高。而在长颈鹿项目中,搜索在商品列表层之下又新开了一个层,在这个层里展示一个品牌商家搭建的页面,在这个页面中品牌商家可以充分的自定义。而对于一个跨品类经营的大品牌,我们也开放了品牌词+主营类目词的组合,方便其对每个主营类目去搭建自己的页面。
而在品牌词下取得成功后,我们将能力扩展到了行业侧,在搜索中台——美高上建设了心智造产品。让行业也可以基于同样的方式去经营品类词,搭建行业自己的页面。
心智造和 Minisite 分别是针对品类词和品牌词,相互不会交叉。而对于一些横跨多品类的平台级需求,比如一些频道相关的需求和拉新相关的需求,是非常容易与其他场景交叉的。为了解决这个问题,我们在美高上沉淀了一套模块定投方案——无界,通过模块级别的投放,让场景之间的交叉变成可能,只需要简单定义模块展示的规则,就能让不同场景的模块展示在同一个页面上。
▐ 技术方案
模块化
首先是心智造和 Minisite ,它们的核心技术方案是相似的——都是采用千叶(一个给商家用的页面搭建平台)作为模块搭建平台,并在千叶的搭建能力之上,包装了一层页面投放策略的管理。其实到这里,模块化方案和会场并无二致,只是模块的实现采用了 Prax 来兼容 Weex 和 H5 :
1、编码阶段
2、渲染阶段
而到了无界,这些就完全不 work 了。毕竟无界是走的模块级定投,容器只有一个,因此投放场景很可能和心智造、 Minisite 场景交叉:
而我们采用了如下技术方案将无界的定投模块插入到页面中:
通过这样的方式,最终渲染出当前场景下所有的模块内容。另外在无界后台我们有一套场景干预规则,解决场景交叉时进行一些手动干预。与此同时我们还提供了一套动态数据源机制,方便三方 HSF、TPP 数据能直接绑定到模块上,实现模块和数据解耦。
页面渲染
页面渲染如上述所示,依旧是通过斑马。但与之前不同的是:
由于是模块化搭建,页面通过 PI 统一管控,页面渲染依赖 PI 行为控制
对于长颈鹿容器,同样是 Native 创建 Weex 容器渲染,但与之前渲染一个仅包含一个模块的 Weex 页面显然是不同的,长颈鹿容器内渲染的也是一个真正的模块化搭建的页面
对于无界定投的模块,提供了插入机制,解决了场景交叉时的展示问题
模块管理
对于页面内的通信,由于页面通过 PI 统一管理,也就意味着模块的各种能力都需要依赖 PI 来进行实现。由于这样的楼层搭建一般通信结构都是扁平的,所以基本上通过模块冒泡给 PI , PI 下发给对应模块即可。不过一般不建议模块之间进行通信。
对于页面和容器通信,我们是在 PI 中提供了一些封装的能力,另外也可以自己调用容器提供的方法,与 Native 内嵌坑位的 weex-module 方案别无二致。
▐ 小结
由于长颈鹿内的技术方案已经脱离传统意义上的前端,更多设计客户端和服务端,是整个产品架构的设计,这里很难讲清楚,放一张图可能清晰一点:
这里前端技术又有了提升:
但这里我们还是有几个问题尚未解决:
搜索最擅长的显然就是搜索结果页那种商品瀑布流,将用户最想要、最有可能购买的商品推荐给他们。而这个能力也可以复用在诸如首页、频道、店铺、专辑等等地方。而当我们要在很多不同的地方去搭建这样一个列表页时,在上面所说的楼层搭建中,我们一般会怎么做呢?
如果这么处理,若行业大面积规模化,会出现如下几个问题:
这些显然都是我们不愿意看到的,我们将组件的不同场景完全剥离,可以发现一些规律:
进一步提炼:
所以我们有没有可能在这个理念上更深一步进行搭建呢?
模块化
答案显然是有的,深度搭建的概念其实就是用来解决这种问题。将页面上的模块基于功能进行了重新定义:
通过这样子的划分,容器模块控制了交互、展示模块控制了 UI 、数据模块控制了业务逻辑,比如一个包含筛选、商品流的列表更具体用 React hooks 来说就是:
合理的场景划分至关重要,不同业务场景应该是数据模块+容器模块+展示模块的组合,大多数情况下容器模块和展示模块可以复用,但数据模块都是需要重新开发的。尽可能让数据模块来适配容器模块和展示模块,不改变交互就不改变容器模块,不改变UI就不改变展示模块。
在这里模块的编写切换到了 Rax 1.0,与会场的开发方式一致以实现复用。Rax 1.0 针对 Web 做了很多优化,打出来的 bundle 也更加轻量,性能也好了很多,已成为天猫前端业务开发主要方式,因此不再需要使用 Prax 了。
页面渲染
这里和传统搭建有个很重要的不同,模块之间出现了关联关系。比如如下关联:
那么我们就需要一个全局方式来处理这些关联。首先需要在搭建模块的时候去指明模块之间的关联关系,比如我自己是定义了三个字端:
data 和 container 都是二段结构的,比如 data 的 a:b 代表使用 key 为 a 的数据模块的 b 字端内的数据,而 container 的 a:b 则代表使用key为a的容器模块,并将自己作为其 b 区域的展示模块使用。
这几个字端都会在脚手架里,初始化时生成到 schema.$attr 中。而真正负责识别这些字端并将模块串起来的自然是页面初始化脚本—— PI ,PI 干了如下事情:
模块管理
这里模块的通信有两种,一种是基于公用数据模块的通信,一种是基于 PI 的通信。
一个页面上可以有多个数据模块,而每个数据模块都可以对接到多个容器/展示模块(这也是为什么 data 是二段结构的原因)。因此当多个模块需要通信的时候,可以通过dispatch触发数据模块行为,数据模块刷新数据影响另外一个模块。
基于PI的通信一般都是针对一些特殊场景,比如一个很经典的例子——筛选列表筛选栏变形吸顶:当页面有很多模块,筛选列表并不是全屏大小而是跟着整个页面一起滚动,当筛选栏滚出视野外,它需要变成一个相对简单的结构吸顶。在这种场景下,由于吸顶层本身是一个绝对定位的层,而页面文档流也是一个层,两个层之间不基于 DOM 操作的话,最合理的方式就是基于一个数据模块进行状态共享。如吸顶组件定义了吸顶时的样式和正常的样式,并且暴露一个创建数据模块的方法, PI 在渲染吸顶组件时会单独为其创建一个数据模块来共享筛选栏状态。
▐ 小结
为什么要搞的这么复杂?其实就是希望能将 UI 和交互做的更加纯粹,当 UI 和交互都是针对单一场景、足够简单的时候,我们就能引入一些产品来进行提效,比如:
通过这这样的方式,最终页面上大多数内容都变得可以复用,规模化的成本也就大大降低了。
由于搜索还在深度搭建时代探索,以下是我基于团队方向 YY 的,但我相信这个时代很快就会到来
但上述的各种方式终究还是人肉的,需要人力投入的。既然工具能让我们以配置的方式解决交互和 UI ,那么有没有可能连配置都不需要,机器自动帮我们生成一个数据模块或者交互呢?
这就是我们团队正在做的,目前团队的同学正在尝试基于机器学习训练模型实现智能化UI。只需要给出一些关键信息(比如要展示商品的哪些字端),就会自动化的生成对应的展示模块或交互模块。这些模块使用到深度搭建之中,前端就只需要通过数据模块定义展示的内容来源就能完成整个页面的搭建。实现UI零成本。
而我们进一步扩展去思考,把大象装冰箱分为三步,那么把搜索智能化应该也只需要三步:
具体一点可能是:
因此,我们可能需要一个三层的模型:
1、第一层基于不同的内容,找出最合理的UI界面来进行展示,也就是智能UI模型。
2、第二层则是需要产出这些内容,这里分了三个维度:行业&平台+品牌&店铺+货品。
3、第三层则更为复杂
货品模型:从货品中提炼出货品中的最吸引人的一些特质,比如卖点、好评、销量,以及将货品结构化,方便货品在不同的地方展示;
场景模型:基于用户的实时行为来定义当前用户的购买场景,他可能想买一台电视所以浏览了很多电视,抑或是在一家卖电视的专卖店里面找同型号线上价格,抑或是在过年准备给父母购置一台电视作为年货等等;
用户模型:基于用户的历史行为,将用户划归某个类型的人群,进行人群精细化运营。比如一个用户经常购买年轻人的潮流酷玩,但每到父亲母亲节或者特定时间就会购买一些长辈礼物的品类,说明是个有孝心的boy/girl,那么针对这类孝顺宝宝就可以在不同的节日或其他重要日子推荐一些给长辈送礼的内容
那么我们都有哪些数据呢?
One More Thing
由于我们不是活在未来,因此无法断定前端的未来到底是怎样的,但前端不会停止对于未来的探索。而这个未来不只是技术的未来,也是业务的未来。技术和业务是我们前进的两条腿,业务的诉求推动技术迈向新的高度,而更先进的技术也为业务带来更多的想象空间。
如果你不想再沉溺于过去,不想再埋头做为业务切页面的工具人,那就加入频道与 D2C 智能团队吧。有意向者请发送简历到 :tianxiang.wly@alibaba-inc.com
本文作者:王令宇(天镶)
原文链接
更多技术干货敬请关注云栖社区知乎机构号:阿里云云栖社区 – 知乎
本文为云栖社区原创内容,未经允许不得转载。