可能你不知道,从去年(或者前年?)某个时候起,店铺开始着手将 KISSY 版本从 1.3 升级到 1.4, 期间因为业务或团队变动已经换了两波前端,然而还是没有升级彻底,即部分店铺是 1.4, 另一部分还停留在 1.3 上。不彻底的升级意味着根本就没有升级,因为所有的代码要同时兼容两个版本。
我从 15 年 10 月接手店铺业务,前不久学长找我商量店铺升级 KISSY 6.0 的事情,那一刻我的内心是崩溃的、拒绝的、惨不忍睹的……然而痛苦归痛苦,坑终究是要填的,跟各位大大讨论方案后做了一些尝试,最后终于在上周把这个问题解决掉了。本文就来向大家简单介绍下这段历史以及最终的解决方案,最后附带个人对于开放脚本的一些思考。
对于第三方代码,安全问题自不用去提,在此基础上,对于官方代码和三方代码一定要做技术方案的隔离,否则当未来官方代码需要升级技术方案的时候就会步履维艰。
在店铺体系里,装修系统是很重要的一部分,在整个体系建立之初,为了保证卖家可以在装修过程中有更多的选择,店铺开放了其模块体系:第三方开发者可以开发店铺的模块,然后上架到装修市场,然后卖家需要的时候去市场里订购。(第三方开放者开发的模块下文统一称为设计师模块)
开放模块意外着第三方开发者可以写 HTML, 写 CSS, 而且可以写 JavaScript, 在当时(2010?)的技术条件下,主要使用了两个库:
KISSY 1.3 自不必多说,Caja 是什么呢,借用其官方介绍:
Caja is a tool for safely embedding third party HTML, CSS and JavaScript in your website.
值得一提的是,经过这套开发体系的编译,最终我们存储的设计师代码是这样的:
{ ___.loadModule({
'instantiate': function (___, IMPORTS___) {
return ___.prepareModule({
'instantiate': function (___, IMPORTS___) {
var dis___ = IMPORTS___;
var moduleResult___, x0___, x1___, x2___, x3___, x4___, x5___,
x6___, x7___, x8___, x9___, x10___, x11___, x12___, x13___,
x14___, x15___, x16___, x17___, x18___, x19___, x20___, x21___,
x22___, x23___, x24___, x25___, x26___, x27___, x28___, x29___,
x30___, x31___, x32___, x33___, x34___, x35___, x36___, x37___,
x38___, x39___, x40___, x41___, x42___, x43___;
moduleResult___ = ___.NO_RESULT;
try {
{
{
function log(x) {
var x0___;
// ...
另一方面,基于 Caja 我们定制了自己的 shop-caja, 其中在初始化设计师模块时会对 KISSY 做一些改写,负责这部分的代码是 Caja-adapter,因此,我猜测,选择这套方案的同学一定是考虑到未来 KISSY 版本的升级问题,而对应的策略就是在 Caja-adapter 这一层做文章: 无论是 KISSY 哪个版本,在 adapter 里总会让其 API 兼容 KISSY 1.3.
以上只是个人猜测,因为人员的变动这些猜测很难去考证或者考证了也没什么意义。最终的结果就是:前任同学在接手了进行一半的升级工作之后,试图通过这样方式去实现,然而在一年之后还是把这个半成品交接到我这个现任手中。
因为上文说的 KISSY 版本问题,引发了一些不良后果:
大促活动有个超级顶通,各个业务方都要引入,店铺当然也不例外,之前作为日常需求接入,因为顶通开起来就是一个很简单的展示区块,因此和开发在预发上看了几个店铺没有什么问题于是就推到预发,然后这个顶通是凌晨才会推送上线的,因此这时候线上也自然不会有任何感知。
直到凌晨一点左右,之前的缓存基本失效,预发环境超级顶通生效,使用 KISSY 1.3 的店铺脚本无法执行,很多模块初始化失败,排查之后才发现超级顶通里没有兼容 1.3 的语法,其中最为严重的一点是 use
io 模块失效,这里不得不提两个点:
KISSY.use
失败之后会导致页面其他依赖 KISSY 的脚本都无法执行 然后开发同学紧急下掉了超级顶通,我当时惊出了一身冷汗,然而负责超级顶通的然姐倒是很淡定 LOL…
所以出现这个问题可能是因为很多因素: 测试的时候没有回归 1.3 的店铺、超级顶通没有兼容 1.3 语法、 KISSY.use
的机制太过脆弱 ……但是原罪还是店铺里竟然还存在 KISSY 1.3 版本这一事实。
店铺目前的整个开发模式很落后 + 繁琐:KISSY 1.3 的语法,自写的打包脚本 Gruntfile
, 异常落后的模块组织体系……自我然不会将这些都归罪于 KISSY 版本,但是如果不解决 KISSY 版本的问题,我们未来无法升级到 KISSY 6.0, 无法升级到 cake, 无法重新梳理店铺的模块机制,无法切换到新版的发布系统,接下来就是一步一步的老死?NO!
因为两种版本同时存在,导致了线上的情况会变复杂,进而就是时不时的线上问题。是的,每次线上问题花两到三个小时都可以解决,然而这些付出几乎是毫无意义的,每次排查类似的线上问题我都会很头疼:
所以,基于这些原因,必须要扛起担子解决问题,接着我们就来聊聊方案吧。
在这个问题上,大致的解决思路有两个:
这个方案,上文已经简单提过,并且店铺前任前端同学之前也有做一些尝试,但是因为 1.4 跟 1.3 的 anim
模块差别太大,一直没有进展,最终随着人员变动无疾而终。简单总结下优缺点:
这个方案是之前跟@展炎、@释然、@乔福、@林谦一起讨论出来的结果,大体思路:有设计师模块的店铺引入两份 KISSY,一份供官方代码依赖,另一份供 shop-caja 和设计师模块使用。同样优缺点:
对比了两种方案之后,最终选择了方案 2, 原因呢:一方面方案 1 已经有同学尝试未果,另一方面自身没有太多的精力去研究 shop-caja 以及 KISSY 的核心代码。 确定方案之后就是 step by step 的行动啦~
整个方案实施起来并没有花费太多精力,大体的步骤如下:
同时将 shop-caja 里所有用到 KISSY 的地方替换为 KISSY_CAJA, 其中较为重要的是初始化设计师模块的地方:
prepareEnv.run({
// KISSY 就是从这里传进设计师模块脚本里的
KISSY: exposed_kissy,
GS: tameGlobalService,
onerror: onerror
}, function (re, a) {
});
在页面中 shop-caja 之前引入 kissy-caja
线上验证:发布完成后,观察了 jstracker 的异常数据,每 PV 报错率跟之前差别不大,基本稳定在 10% 左右,基本确保问题解决。
比较欣慰的是用了这个方案之后,预发上只出现了一个问题: jsonp 方法 callback 同名导致报错 。KISSY 中 jsonp 的回调名是通过 jsonp + S.guid()
来生成的,而这个 guid 是挂载在 KISSY 下而非全局变量下,因此两个版本的 KISSY 很大几率会生成相同的 guid, 而多个 jsonp 回调名相同之后就会导致后面执行的方法出错,解决方案也很简单,因为 guid 是从 0 开始累加的,所以我直接将 kissy-caja 的 guid 基准值从 0 改为 12306, 别问我为什么是 12306, 任性!
最后,虽然我对于开放第三方代码没有做过研究,但是因为这件事对开放三方代码有一点自己的认识:
比如说,如果当时店铺的设计师模块直接基于 jQuery 某个指定版本去开发,现在也就不会遇到这些问题了,当然这只是个马后炮,希望对类似应用会有帮助吧。