YUI3在美团的实践

美团网在2010年引爆了团购行业,并在2012年销售额超过55亿,实现了全面盈利。在业务规模不断增长的背后,作为研发队伍中和用户最接近的前端团队承担着非常大的压力,比如用户量急剧上升带来的产品多样化,业务运营系统的界面交互日益复杂,代码膨胀造成维护成本增加等等。面对这些挑战,我们持续改进前端技术架构,在提升用户体验和工作效率的同时,成功支撑了美团业务的快速发展,这一切都得益于构建在YUI3框架之上稳定高效的前端代码。在应用YUI3的过程中,我们团队积累了一些经验,这里总结成篇,分享给大家。

为什么选择YUI3

使用什么前端基础框架是建立前端团队最重要的技术决策之一。美团项目初期因为要加快开发进度,选择了当时团队最熟悉的YUI2(前框架时代杰出的类库),保证美团能够更快更早地上线,抢占市场先机。不久由于前端技术发展很快,YUI2的缺点逐渐凸显,例如开发方式落后、影响工作效率等等,于是我们开始考虑基础库的迁移。

经过一段时间对主流前端库、框架的反复考量,我们认为YUI3是最适合我们团队使用的基础框架。

首先,国内的开源框架及其社区刚开始起步,在代码质量、架构设计和理念创新上还难以跟YUI3比肩,所以基本排除在外。其次,国外像YUI3这样面向用户产品、文档丰富、扩展性良好的成熟框架屈指可数,例如ExtJS和Dojo则更适合业务复杂的传统企业级开发。最后,使用jQuery这种类库构建同YUI3一样强大的框架对创业团队来说并不可取,美团快速发展、竞争多变的业务特点决定了我们必须把主要精力放在更高一层的业务开发上,而不是去重复发明一个蹩脚的YUI。

YUI3成为最终选择有以下几个直接的原因:

  • 非常优秀,是真正的框架,真正的重型武器,具有强劲的持续开发能力,可以应对业务的快速发展。不管是规模不断增长的用户产品,还是交互日趋复杂的业务系统(美团有超过100个业务系统作全电子化的运营支撑),YUI3都游刃有余。
  • 代码整齐规范,容易维护,适合有洁癖的工程师,同时能够显著提高团队协作时的开发效率。因为人手紧缺,后端工程师也需要参与前端开发,一致的代码风格使前后端配合轻松简单。
  • 有出色的架构设计,是很好的框架范本,通过研究学习可以帮助工程师成长,培养良好的工程思维。人是美团最重要的产品。

随着团队成长,我们最后引入了YUI3,在迁移过程中,遇到了很多技术上的和工程上的挑战,但是我们一直在前进,一直在行进中开火。从结果来看,YUI3为我们团队提供了先进生产力,为快速开发、快速部署、快速迭代提供了源源不断的力量。

YUI3的优秀主要表现在模块和组件框架的出色设计,下面我们着重介绍这两方面的一些实践经验。

改变一切的模块

前端开发日益复杂化,代码组织成为一个显著的问题。受到后端代码普遍采用的模块机制启发,很多前端模块机制应运而生。目前比较著名的有CommonJS和AMD。但早在2008年8月13日,YUI3 Preview Release 1中就已经给出了YUI团队的解决方案,并在2009年9月29日YUI3正式版发布时定型。

以下是使用YUI3进行模块化开发的简单例子

// 定义模块
YUI.add('greeting', function (Y) {Y.sayHello = function () {console.log('Hello, world!');};
});// 调用模块
YUI().use('greeting', function (Y) {Y.sayHello(); // output 'Hello, world!'
});

模块的引入,使得更细粒度的按功能进行代码组织成为可能,也为方便的进行扩展和分层提供了基础,自底向上的彻底改变了YUI3。一套完整的模块机制,还包括解决关系依赖、自动加载的Loader和提高加载效率的Combo。

面对如此彻底的改变,我们需要解决很多挑战:

  • 如何将原来的功能划分为模块?
  • 如何管理模块元信息?
  • 如何高效的获取模块?

划分模块

经过两年来不断的实践和总结,我们归纳了如下几条划分模块的原则:

  • 抽象与应用脱离。更通用的功能放在更低的层级,应用层完全面向实际问题,在解决的过程中调用抽象出来的方法。
  • 职责单一。保持每个模块的足够简单和专一,方便维护和可持续开发。
  • 粒度得当。有了Combo,我们可以不必担心粒度太小,文件过多导致的速度问题。但是,从可维护的角度来考虑,粒度应该适当而不宜过小,避免海底捞针的情形出现。
  • 海纳百川。我们的模块体系应该是开放的,不符合YUI规范的第三方模块,可以借鉴整合进来,使我们的基础框架更加完善,更加性感。

美团前端架构

按照模块的层次划分,美团的JS框架可以分为四个层次:

  • 最底层交给强悍的YUI3,为我们提供跨浏览器兼容的API和良好的框架设计。
  • 第二层是我们二次开发的核心方法、组件(Component)和控件(Widget)。现已独立为前端核心库,为美团所有系统提供前端支持。核心库的种子文件中定义了全局变量M,除了对YUI3进行封装的代码以外,还包含了对语言层面的扩展,以及一些基础工具类。核心库有一个非常重要的组成部分,就是我们功能丰富的控件集合,比如常用的自动完成、排序表格、气泡提示、对话框等基础控件。除了这些,核心库还包含了常用的基础组件、插件(Plugin)、扩展(Extension)以及单元测试代码。
  • 第三层包含各个系统的一些通用模块。例如www-base模块包含美团主站(www)的消息系统、用户行为追踪系统等通用功能。这一层更加接近应用。
  • 最上面一层,应用模块。这些模块的方法都是用来解决实际业务问题。例如www-deal用来处理美团主站所有deal相关功能的交互,finance-pay用来处理财务系统中付款相关的交互。一些零碎的应用方法我们放在对应系统的misc模块中,避免模块碎片化。

这套框架仍在不断演变,以便更好的支撑业务需求。其中一个明显的方向是,在第二层和第三层之间,出现一个为了更好整合所有内部业务系统前端通用资源的中间层。

管理模块元信息

模块元信息主要包括模块名称、路径、依赖关系等内容。其中最为重要的是依赖关系,这决定了有哪些模块需要加载。为了实现自动加载,需要将所有模块的元信息提供给YUI的Loader。

最初,为了更快的从YUI2迁移到YUI3,模块元信息放在PHP中进行维护。随着时间的推移,渐渐显示出很多弊端。首先,在定义模块的js文件中已经包含模块名称、依赖关系等信息,和PHP中内容重复。其次,这些元信息最终直接输出到html中,没有有效利用缓存。

随后,我们使用NodeJS开发了一系列脚本,收集所有模块元信息,保存为独立js文件,并实现了自动化。为了防止出错,在Git Hooks和上线脚本中都加入了校验过程。工程师需要做的,只是修改模块定义中的元信息。

最近一段时间,我们的精力主要放在两个方面:

  • 自动生成依赖。随着模块粒度细化和模块数量的增长,依赖关系日益复杂,依靠人工配置经常出现过多依赖或过少依赖等问题。我们准备开发一套自动扫描模块引用API,并确定依赖关系的机制。
  • 自动打包依赖模块。如果在代码发布时,就已根据页面模块调用计算好所有依赖模块,并进行打包,可以避免引用全部模块元信息、Loader计算依赖等过程,提高网站性能。

Combo

Combo可以一次请求多个文件,能够有效解决多个模块加载带来的性能问题。Yahoo提供了Combo服务,但只能提供YUI3模块,而且速度在国内并不理想。为了提供更好的体验,让用户访问速度更快,我们最终考虑搭建自己的Combo服务,并把Combo发布到CDN上。

以下是一个Combo请求的例子:

http://c.meituan.net/combo/?f=mt-yui-core.v3.5.1.js;fecore/mt/js/base.js

为了节约时间,我们最开始采用了开源的minify,经过一些修改和配置,就可以在生产和开发环境提供Combo服务。使用一段时间后,发现minify过于复杂,以至于添加一些定制功能相当困难。我们需要的只是简单的文件合并功能,在明确需求和开发量后,着手开发自己的Combo程序。从最初的仅支持文件合并,后来陆续添加了服务器/浏览器端缓存、文件集别名、调试模式、CSS图片相对路径转URI、错误日志等特性,全部代码仅有300多行。经过两年时间以及每天几千万PV的考验,服务一直非常稳定。

灵活健壮的组件框架

YUI3之所以成为纯粹的框架,真正的原因在于提供了一套灵活、健壮的组件框架。借助这套框架,可以轻松的将业务场景进行解耦、分层,并持续的进行改进。通过不断的实践,我们越发认为这是YUI3的精髓所在。

从YUI3定义的开发范式和源代码中可以看出,YUI团队非常重视AOP(Aspect Oriented Programming)和OOP(Object Oriented Programming),这一点可以在接下来的介绍中有所体会。

EventTarget、Attribute和Base

在介绍组件框架之前,有必要首先了解下EventTarget。YUI3创建了一套类似DOM事件的自定义事件体系,支持冒泡传播、默认行为等功能。EventTarget提供了操作自定义事件的接口,可以让任意一个对象拥有定义、监听、触发、注销自定义事件的功能。YUI组件框架中的所有类,以及在此框架之上开发的所有组件,都继承了EventTarget。

Attribute是组件框架中最底层的类,实现了数据和逻辑的完美解耦。为什么说是完美呢?存储在attribute(Attribute提供的数据存取接口)中的数据发生变化时,会触发相应的事件,为相关的逻辑处理提供了便捷的接口。从下面这个简单的例子可以感受到这一点:

// 在name属性变化时,触发nameChange事件
this.on('nameChange', function (e) {console.log(e.newVal);
});// 修改name属性
this.set('name', 'meituan'); // output 'meituan'

实践中发现,妥善处理属性的分类非常重要。供实例进行操作的属性适合作为attribute,例如表单验证组件FormChecker的fields属性,方便应用层进行表单项的增删改。在类方法内部使用的一些属性可以作为私有属性,例如计时器、监听器句柄。供所有类的实例使用的一些常量适合作为类的静态属性,例如一些模板、样式类。

Base是组件框架的核心类。它模拟了C++、Java等语言的经典继承方式和生命周期管理,借助Attribute来实现数据与逻辑的分离,并提供扩展、插件支持,从而获得了良好的扩展性以及强大的可持续开发能力。YUI团队通过多年来对业务实践的抽象,最终演化而成一种开发范式,这,就是一切组件的基石——Base,实至名归。

依照这种范式,我们开发了一系列组件,例如之前提到的FormChecker,以及延迟加载器LazyLoader、地图的封装Map等。最显著的体会是,开发思路更为清晰,代码结构更有条理,维护变得简单轻松。

// 构造方法
FormChecker.prototype.initializer = function () {var form = this.get('form');this._handle = form.on('submit', function (e) {// check fields});
};
// 析构方法
FormChecker.prototype.destructor = function () {this._handle.detach();
};// 创建实例时,自动执行构造方法
var checker = new FormChecker({ form: Y.one('#buy-form') });
// 销毁实例时,自动执行析构方法
checker.destroy();

Extension和Plugin

Extension(扩展)是为了解决多重继承,以一种类似组合的方式在类上添加功能的模式,它本身不能创建实例。这种设计非常像Ruby等语言中的Mixin。Plugin(插件)的作用是在对象上添加一些功能,这些功能也可以很方便的移除。

它们有什么区别呢?简单来说,Extension是在类上加一些功能,所有类的实例都拥有这些功能。Plugin只是在某些类的实例中添加功能。举两个典型的例子:一些节点需要使用动画效果,这个功能适合作为Plugin。气泡提示控件需要支持多种对齐方式,所有实例都需要此功能,因此使用YUI3的WidgetPositionAlign扩展。

// 传统的函数方式实现动画
Effect.fadeIn(nodeTip);// 插件方式实现动画
nodeTip.plug(NodeEffect);
nodeTip.effect.fadeIn();

Extension和Plugin很好的解决了我们遇到的诸多功能重用问题。我们开发了提供全屏功能的WidgetFullScreen、自动对齐对话框的DialogAutoAlign等扩展,以及进行异步查询的AsyncSearch、提供动画效果的NodeEffect等插件。将这些偏重OOP的编程思想应用在前端开发中,比较深刻的体会是:有更多的概念清晰、定位明确的开发模式可以选择。

Widget体系

Widget(控件)建立在Base之上,主要增加了UI层面的功能,例如renderUIbindUIsyncUI等生命周期方法,HTML_PARSER等渐进增强功能,以及样式类、HTML结构和DOM事件的统一管理。Widget提供了控件开发的通用范式。

由于前端资源相对紧张,我们倾向于大量使用控件,尤其在业务系统这样更注重功能的场景。主要出于两点考虑:

  • 减少不必要的重复劳动,提高产出。通过将交互、业务逻辑合理抽象,一次解决一类问题,One Shot One Kill。
  • 节约前端工程师资源。通过自动加载和初始化控件、封装简单易用的后端方法、制作Demo和使用手册等措施,降低使用门槛,后端工程师只需要知道参数的数据结构就可以轻松调用,提高了开发效率。

以下是一个自动加载控件的例子

// 页面初始化时,会扫描所有带有data-widget属性的节点,自动加载对应控件,并根据data-params数据进行初始化
<a href="…" data-widget="bubbleTip" data-params='{ "tip": "全新改版,支持随时退款" }'>下载手机版</a>

目前,我们已经构建了一个包含近30个控件的Widget体系,为所有系统提供丰富、便捷、集成的解决方案。

行进中开火

在整个YUI3的实践中,我们犯过很多错误,例如全局只有一个YUI实例、Combo的CSS图片依赖等等,但这些并没有成为放弃的理由。从今天回过头来看,YUI3带给我们团队的,不只是更高的开发效率、更好的可持续开发能力,还有它本身的设计思路、源码书写、辅助工具等诸多方面潜移默化的影响。这些回报的价值,比起较高的使用门槛、犯过的一些错误,要贵重百倍。

指导这一切的,是我们始终坚持的 “行进中开火”。在互联网这个高速发展的行业里,对于我们这种小规模的创业团队,一天不前进,就意味落后。做事不应该准备太多,一定要先做起来,然后发现不足并不断改进,宁可十年不将军,不可一日不拱卒。每天都做得更好一点,日积月累,我们才会在激烈的竞争中占据越来越大的优势。

YUI3并非完美,存在着学习成本高、对社区不够开放等问题。我们所做的更远非完美,但经过不断的尝试和经验的积累,已经渐渐摸索出一条明确的路线,并会坚持不懈的继续走下去。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/477784.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文浅尝 - ICLR2022 | OntoProtein:融入基因本体知识的蛋白质预训练

论文题目&#xff1a;OntoProtein: Protein Pretraining With Gene Ontology Embedding本文作者&#xff1a;张宁豫&#xff08;浙江大学&#xff09;、毕祯&#xff08;浙江大学&#xff09;、梁孝转&#xff08;浙江大学&#xff09;、程思源&#xff08;浙江大学&#xff09…

LeetCode 540. 有序数组中的单一元素(位运算二分查找)

1. 题目 给定一个只包含整数的有序数组&#xff0c;每个元素都会出现两次&#xff0c;唯有一个数只会出现一次&#xff0c;找出这个数。 示例 1: 输入: [1,1,2,3,3,4,4,8,8] 输出: 2示例 2: 输入: [3,3,7,7,10,11,11] 输出: 10注意: 您的方案应该在 O(log n) 时间复杂度 和 O…

迁移Prompt–解决Prompt Tuning三大问题!

文 | Harris刘鹏飞博士将近代NLP的研究划归为四种范式 [1] 并把预训练语言模型加持下的Prompt Learning看作是近代自然语言处理技术发展的“第四范式”。当我们使用新范式的方法的时候&#xff0c;能够意识到它带来的优异性可能是以某种“人力”牺牲为代价的。而如何让这种人力…

征文 | 2022年全国知识图谱与语义计算大会(CCKS 2022) 征稿通知

2022年全国知识图谱与语义计算大会征稿通知Call for Papers2022年8月25日-28日&#xff0c;秦皇岛征稿截止: 2022年5月22日第十六届全国知识图谱与语义计算大会&#xff08;CCKS: China Conference on Knowledge Graph and Semantic Computing&#xff09;由中国中文信息学会语…

Spring Cloud 和 Dubbo 哪个会被淘汰?

今天在知乎上看到了这样一个问题&#xff1a;Spring Cloud 和 Dubbo哪个会被淘汰&#xff1f;看了几个回答&#xff0c;都觉得不在点子上&#xff0c;所以要么就干脆写篇小文瞎逼叨一下。 简单说说个人观点 我认为这两个框架大概率会长期都存在。 时至今日&#xff0c;这两个…

DNN与推荐两大门派,一念神魔,功不唐捐

文 | 水哥源 | 知乎Saying1. embeddingDNN范式有两个流派&#xff0c;一个更关注DNN&#xff0c;叫逍遥派&#xff1b;一个更关注embedding&#xff0c;叫少林派2. embeddingDNN这种结构中&#xff0c;embedding一般是模型并行&#xff1b;DNN一般是数据并行3. 逍遥派能够创造奇…

会议交流—PPT下载|DataFunSummit2022:知识图谱在线峰会PPT合集!

点击上方公众号卡片&#xff0c;后台回复『20220312』&#xff0c;即可下载&#xff01;有哪些PPT&#xff1f;下载方式点击下方公众号卡片&#xff0c;后台回复『20220312』&#xff0c;即可下载&#xff01;OpenKGOpenKG&#xff08;中文开放知识图谱&#xff09;旨在推动以中…

Spring Cloud 2020年路线图发布,涵盖Spring Boot 2.3、2.4,Spring Cloud Ilford等重磅内容!

Spring Cloud 开发团队昨日公布了 Spring Cloud 2020 年的路线图&#xff0c;并对 Spring Cloud Greenwich 和 Hoxton 的生命周期进行了一些讲解。 Spring Cloud Ilford 开发团队称 Spring Cloud Ilford 将是下一个主要版本&#xff0c;这也将是自 Spring Cloud Finchley 发布…

LeetCode 398. 随机数索引(概率)

1. 题目 给定一个可能含有重复元素的整数数组&#xff0c;要求随机输出给定的数字的索引。 您可以假设给定的数字一定存在于数组中。 注意&#xff1a; 数组大小可能非常大。 使用太多额外空间的解决方案将不会通过测试。 示例: int[] nums new int[] {1,2,3,3,3}; Solutio…

再论推荐特征与embedding生成

文 | 水哥源 | 知乎Saying1. 工业特征处理和学术特征处理存在巨大差异&#xff0c;这里建议同学们一定认真阅读。这个差异可能引发未来各种方法落地的矛盾。2. full embedding在概念上和one-hot的操作等价&#xff0c;但在操作上省略了这个过程。3. hash是最省事的&#xff0c;…

图谱实战 | 李翔:美团到店综合知识图谱的构建与应用

转载公众号 | DataFunTalk分享嘉宾&#xff1a;李翔 美团 算法专家编辑整理&#xff1a;王惠灵 合肥工业大学出品平台&#xff1a;DataFunTalk导读&#xff1a;美团到店综合业务涵盖了本地生活中的休闲玩乐、丽人、亲子、结婚、宠物等多个行业。为了不断提升到店综合业务场景下…

Spring Cloud Hoxton正式发布,Spring Boot 2.2 不再孤单

距离Spring Boot 2.2.0的发布已经有一个半月左右时间&#xff0c;由于与之匹配的Spring Cloud版本一直没有Release&#xff0c;所以在这期间碰到不少读者咨询的问题都是由于Spring Boot和Spring Cloud版本不匹配导致。 很多时候&#xff0c;我们在学习或重建系统的时候都喜欢直…

加了元学习之后,少样本学习竟然可以变得这么简单!

文 | Rukawa_Y编 | Sheryc_王苏&#xff0c;小轶去年年初 GPT-3 的论文在 arxiv 上出现&#xff0c;论文名为 “Language Models are Few-Shot Learners”&#xff0c;引起一阵轰动。除了前无古人的模型规模外&#xff0c;最抓人眼球的是&#xff0c; GPT-3 能够不需要 fine-tu…

Spring Cloud Alibaba基础教程:与Dubbo的完美融合

很早以前&#xff0c;在刚开始搞Spring Cloud基础教程的时候&#xff0c;写过这样一篇文章&#xff1a;《微服务架构的基础框架选择&#xff1a;Spring Cloud还是Dubbo&#xff1f;》&#xff0c;可能不少读者也都看过。之后也就一直有关于这两个框架怎么选的问题出来&#xff…

DIN+DIEN,机器学习唯一指定涨点技Attention

文 | 水哥源 | 知乎Saying1. 如果你面对一个全新的机器学习任务&#xff0c;让你来涨点。你可能第一个想到的往往是attention&#xff0c;第一个实现的是attention&#xff0c;第一个真的涨点了的技术也是attention&#xff1b;2. DIN的最主要的意义&#xff0c;把attention引入…

RabbitMQ延迟消息的极限是多少?

之前在写Spring Cloud Stream专题内容的时候&#xff0c;特地介绍了一下如何使用RabbitMQ的延迟消息来实现定时任务。最近正好因为开发碰到了使用过程中发现&#xff0c;延迟消息没有效果&#xff0c;消息直接就被消费了的情况。因此就继续深入研究了一下问题原因&#xff0c;在…

推荐中的attention有什么作用?

文 | 水哥源 | 知乎Saying1. attention要解决两个问题&#xff1a;&#xff08;1&#xff09;attention怎么加&#xff0c;在哪个层面上做attention&#xff1b;&#xff08;2&#xff09;attention的系数怎么来&#xff0c;谁来得到attention2. Attention常见的本质原因是求和…

LeetCode 667. 优美的排列 II(找规律)

1. 题目 给定两个整数 n 和 k&#xff0c;你需要实现一个数组&#xff0c;这个数组包含从 1 到 n 的 n 个不同整数&#xff0c;同时满足以下条件&#xff1a; ① 如果这个数组是 [a1, a2, a3, ... , an] &#xff0c;那么数组 [|a1 - a2|, |a2 - a3|, |a3 - a4|, ... , |an-1…

图谱实战 | 医学知识图谱的价值与应用场景

转载公众号 | OMAHA联盟随着技术的进步和市场的逐渐成熟&#xff0c;人工智能在医疗等领域的应用日益广泛和深入。而知识图谱技术作为一种从海量文本和图像中抽取结构化知识的手段&#xff0c;正在成为推动人工智能发展的核心驱动力之一。◆ ◆ ◆知识图谱概述知识图谱是一种…

Spring Cloud Alibaba基础教程:Sentinel Dashboard中修改规则同步到Nacos

上一篇我们介绍了如何通过改造Sentinel Dashboard来实现修改规则之后自动同步到Apollo。下面通过这篇&#xff0c;详细介绍当使用Nacos作为配置中心之后&#xff0c;如何实现Sentinel Dashboard中修改规则同步到Nacos。关于下面改造的原理和分析可以见上一篇《Sentinel Dashboa…