开场白
- 今天主题是《你不知道的组件化开发:组件化的前世今生》(微信群文字直播,了解一下历史吧)
- 今天前端生态里面,React、Angular和Vue三分天下。虽然这三个框架的定位各有不同,但是它们有一个核心的共同点,那就是提供了组件化的能力。W3C也有Web Component的相关草案,也是为了提供组件化能力。今天我们就来聊聊组件化是什么,以及它为什么这么重要。
什么是组件化
- 其实组件化思想是一种前端技术非常自然的延伸,如果你使用过HTML,相信你一定有过“我要是能定义一个标签就好了”这样的想法。HTML虽然提供了一百多个标签,但是它们都只能实现一些非常初级的功能。
- 但是,HTML本身的目标,是标准化的语义,既然是标准化,跟自定义标签名就有一定的冲突。所以从前端最早出现的2005年,到现在2019年,我们一直没有等到自定义标签这个功能,至今仍然是Draft状态。
- 不过有同学会问:自定义标签也不等于组件化本身啊。没错,不过可以进一步思考,其实我们需要的并不一定是自定义标签这样的一个形式,可以从软件工程的通用思想来解释,组件化可以拆解为四个更基本的概念:
- 复用:组件将会作为一种复用单元,被用在多处。
- 解耦:组件本身隔离了变化,组件开发者和业务开发者可以根据组件的约定各自独立开发和测试。
- 封装:组件屏蔽了内部的细节,组件的使用者可以只关心组件的属性、事件和方法。
- 抽象:组件通过属性和事件、方法等基础设施,提供了一种描述UI的统一模式,降低了使用者学习的心智成本
- 我们可以进一步分析,其实组件化并非前端独有的一种需求,任何软件开发过程,或多或少都有那么一些组件化的需求。而你可以思考为什么较好的组件化解决方案(三大框架)会出现在2014年左右这个时间点,这是一个耐人寻味的问题。而我认为这个问题的答案,就藏在前端发展的历史当中。
- 我们可以看下复用、解耦、封装、抽象这四个概念,很显然,它们都是为了大型的、多人协作的软件开发所准备的。所以我认为,2014年左右这个时间点,正是前端发展到了一个需要规模化的时间点。
- 我们纵观前端的发展历程,开始于2005年左右,是特效为王的时代,前端领域追逐的是炫酷的效果;到了2009年左右,jQuery开始统治前端,这时API易用性和浏览器兼容性是最重要的;等到了2012年移动互联网出现之后,提供组件化方案的“三大框架”逐步占据了前端重要的生态地位。
- 接下来我们深入具体的技术细节,看看组件化是如何一步步发展的。
组件化的发展
首先,标准的DOM元素是这样创建和挂载的:这里的element是一个对象,但是其实JavaScript(早期)里面,根本没法创建这样的对象。一方面也是我们没有办法改变createElement这个函数的行为
1
2var element = document.createElement("div")
document.getElementById("container").appendChild(element)既然API风格没法靠拢DOM原生,那么就靠拢JS原生吧,所以一些前端开发同学就萌生了创建一个带容器的对象的想法:
1
2
3
4function MyComponent() {
this.prop1
this.methode1
}不过,要想挂载又成了难题,普通的JS对象没法被用于appendChild,所以前端工程师就有了两种思路,第一种是反过来,设计一个appendTo方法,让组件把自己挂到DOM树上去:
1
2
3
4
5
6function MyComponent() {
this._root = document.createElement("div")
this.appendTo = function(node) {
node.appendChild(this._root)
}
}第二种比较有意思,是让组件直接返回一个DOM元素,把方法和自定义属性挂到这个元素上:
1
2
3
4
5
6
7
8
9function MyComponent() {
var _root = document.createElement("div")
root.prop1 // =...
root.method1 = function() {
// todo sth.
}
return root;
}
document.getElementById("container").appendChild(new MyComponent())虽然从前端全领域来看,组件化到后期(2014年)才有比较普及的应用,但是早年用这样的思路实现组件体系的方案并不少,说明组件化在一些公司和领域始终有需求。虽然当时有一些组件化方案没能够影响行业,但是不可否认它们也还算是不错的解决方案,比如著名的ExtJS(现已更名为Sencha),我们来看看它的组件定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29MainPanel = function() {
this.preview = new Ext.Panel({
id: 'preview',
region: 'south',
// ...
})
//...
MainPanel.superclass.constructor.call(this. {
id: 'main-tabs',
activeTab: 0,
region: 'center',
// ...
})
this.gsm = this.grid.getSelectionModel()
this.gsm.on('rowselect', function(sm, index, record) {
//...
}, this, { buffer: 250 })
this.grid.store.on('beforeload', this.preview.clear, this.preview)
this.grid.store.on('load', this.gsm.selectFirstRow, this.gsm)
this.grid.on('rowblclick', this.openTab, this)
}
Ext.extend(MainPanel, Ext.TabPanel, {
loadFeed: function(feed) {
//...
},
movePreview: function(m, pressed) {
//...
}
})你可以看到,这是一个完全使用JS来实现组件的体系,它定义了严格的继承关系,这样的方案很好地支撑了ExtJS的前端架构。不过,创建和挂载对象的方式可不止DOM API,还有HTML语言,如何让前端组件融合进HTML语言呢?
1
<my-component attr1="xxx"></my-component>
关于这方面,依赖早期标准的前端技术可以说几乎没有办法。但是,历史中总有些遗珠,微软的IE浏览器已经提供了组件化的解决方案,名为HTML Component,我找了一段非常古老的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<HTML xmlns:PUBLIC="urn:HMTLComponent">
<PUBLIC:PROPERTY NAME="child" />
<PUBLIC:ATTACH EVENT="onclick" HANDLER="onclick_handler" />
<PUBLIC:ATTACH EVENT="onmouseover" HANDLER="onmouseover_handler" />
<PUBLIC:ATTACH EVENT="onmouseout" HANDLER="onmouseout_handler" />
<SCRIPT LANGUAGE="JScript">
funtion onmouseover_handler() {
element.style.color = "red"
}
function onmouseout_handler() {
element.style.color = "black"
}
funtion onclick_handler() {
// ...
}
</SCRIPT>这项技术提供了事件绑定和属性、方法定义,以及一些生命周期相关的事件,应该说已经是一个比较完整的组件化方案了。但是我们可以看到后来的结果,它没有能够进入标准,默默地消失了。用我们今天的角度来看,它可以说是生不逢时。
- 到了“三大框架”出现的时代,因为面向的客户群体从少数公司、少数领域变成了广大前端开发群众,也因为一些新技术的出现,让旧时代组件化没法解决的问题有了新的可能性,这些新的组件化方案都保持了HTML甚至CSS的书写习惯。
Vue.js
Vue.js采用了JSON的方法描述一个组件:
1
2
3
4
5Vue.component('todo-item', {
// The todo-item component now accepts a "prop", which is like a custom attribute. This prop is called todo.
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})还提供了SFC(Single File Component,单文件组件)“.vue”文件格式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<template>
<div class="example">{{ msg }}</div>
</template>
<script>
export default {
data() {
return {
msg: 'Hello World!'
}
}
}
</script>
<style>
.example {
color: red;
}
</style>
React.js
React.js发明了JSX,把CSS和HTML都塞进JS文件里:
1 | class ShoppingList extends React.Component { |
Angular.js
选择在原本的HTML上扩展,定义组件的方式也是JS class:
1 | export class HeroListComponent implements OnInit { |
继往开来
- 我们可以看到,现代的组件化方案跟旧时代组件化方案的一个明显区别就是,现在的组件化方案保留了原有的标记语言部分,并且努力保留了样式表部分。
- 虽然技术从旧到新经历了各种变迁,但是组件化的核心并没有变化,我们的目标仍然是在API设计尽可能接近原生的情况下完成复用、解耦、封装、抽象的目标,最终服务于开发,提高效率,降低错误发生比率。
- 如果你的公司和前端团队规模正好面临需要建立组件化体系,希望你能从今天所分享的历史中获得一点灵感。
- 今天的分享内容主要就是这些,接下来有什么问题欢迎大家提问。(真短啊,一个PPT的感觉,难怪只要10分钟)
Q&A环节
实时记录,所以“我”都是指Winter自称
我们要用什么样的力度去做组件
- 其实组件这个东西呢,它的力度是无所谓的,他是一个这样的级联的结构,所以说你可以有粗粒度的组件,也可以有细粒度的组件
- 其实相比说“力度”,更应该关心“体系”。
- 不论是细粒度还是粗粒度的组件,最后的目的都是形成一个“很好用”的组件体系
SEO和组件化的问题
- 现代的SEO已经走到了一条完全不一样的道路上了
- 我们现在有一套方案,比如服务器专门渲染一个页面给搜索引擎看
- 组件化自定义的标签,肯定还是有影响的,毕竟搜索引擎不认识
- 但是本身HTML也有专门写给搜索引擎看得标签,所以也没问题。
过度封装的问题
- 很少会到“过度”的地步
- 需要对OOP的基本原则有一定了解
- 属性、方法聚合封装的前提是他们有局部性
怎么解决组件化的体系设计问题
朋友勾三股四说:如果你做UI组件,可以直接参考W3C的accessblity的标准。他本身不是组件,但是基本总结了人类常用标准,所以在设计的时候是很好的参考(emmmm,这上次Vue杭州的夜会上也听勾3说起)
微服务和组件化
- 微服务就是来自后端的,还没见说前端谁这么说
- 因为前端没有什么服务的,所以这个讲法的维度就过大了
- 其主张是一个服务只做一件事而不是混合业务场景去设计Api
Web3.0 WebComponent TypeScript
未来的事肯定说不好,但是可以积极对待,目前也确实可以解决不少问题
组件怎么迭代
- 淘宝曾经有一个轮播组件被加了30多个参数……
- 设计组件也是遵循面向对象原则,对修改封闭,对扩展开放
- 所以新功能可以通过继承或者包装来做
GraphQL
- Taobao没怎么做,Facebook那边也一点
- 但是目前很少有公司这么落地的
- 淘宝有一些变种
组件间的消息机制
- 不建议postMessage这样的机制
- 听阿里晋升演讲里,很多人都讲自己建立的组件体系和消息机制,消息进去了debug的链路就断了。不好。
- flux、redux都是变形的消息机制。
- 我更推崇Vue和Angular的双向绑定机制,其实内部也是有消息流转的,但是外在语义化更好,不是一个未经定义的消息,而是规定好的数据变化型消息
Vue首次加载页面白屏
- 看看模板是不是没有预编译
- 是不是引了调试版本,导致每次都要编译
- 我用Vue-cli的话,都没有这个问题
页面性能的问题
- 性能和组件化其实没什么关系
- 工程体系都需要一些监管和评估手段,所以我们首先要建立一些标准,然后再考虑怎么优化性能
前端架构师和后端架构的区别
- 前端架构面临的问题不太一样,是零碎的页面,大量的重复性劳动,而不是传统软件里的模块耦合和复杂性问题
- 前端面临和重点关注的主要问题是“复用”——这就是组件化的意义
- 前端架构还是可以用很多工具来解决。比如淘宝非常重视的搭建系统,可以大量产出简单的页面(模板/模块化的方法)
- (emmm,18年到19年参加的前端各种会,都能感受到阿里的研发方向……)
Angular.js脏检查是否影响性能
- 确实影响性能
- 但这是设计者技术不过关的问题,所以Angular从2开始就解决的不错
全栈
- 国内其实市场不是特别看好
- 不建议做Nodejs全栈,因为挺难的,百废待兴
- 你要自己去写一个服务很难,阿里的那些大神是可以搞,但其实也很难。
- 还不如多学一点手艺,学别的语言来搞。
- 职业发展是个人规划问题。
金额计算
- 阿里确实都是在后端计算
- 然后从风险评估说了一下
HTTP协议规范和设计模式
- HTTP状态码都没几个人知道,知道HTTP协议细节我觉得更难了
- 面试的话也不知道会从哪里考,所以不好说
- 设计模式这本书,他是为基于类的OOP语言编写来写的,所以对JS其实就有点尴尬
- 比如工厂模式就是为了解决new的时候,class的东西没法传,但是JS不怕啊,JS构造器可以当作函数参数往里传,所以根本不存在工厂模式这个需求
Flutter要不要学
- Flutter不是最火的,最火的是区块链和AI
- 什么火学什么,要累死。要转也可以,但是他是一个全新的体系,有一定的隔离,目前也算个小众
- 以后会有一席之地吧。
前端的自己的设计模式
前端没有“四人帮”那样的人物,总结的模式都比较弱,难有23模式那种总结
数据结构和算法
- 别想太复杂。其实for循环都是一种简单的算法,数组也是一种数据结构(顺序表,和链表相对)
- 经典数据结构和经典算法,不用硬啃,但是数据结构和算法的水平有比较提升
- 面试考算法题其实有点过了,代码和算法不太容易分开,有逻辑也算算法啊
WebGL
- 个人非常看好图形学
- 但是推荐资深前端,面临瓶颈的时候可以考虑突破
- 竞争很激烈,你看比如很多CSS大神会去考虑转这个方向
- W3C和chrome是有一些非技术的因素的,前者想推WebGPU,所以,学计算机图形学会更保险,而不是押宝式的选择。
自己的App能否在Chrome上模拟注入某段代码判断目前H5运行在什么环境下吗
- 如果WebView是自己的代码,那肯定可以
- 如果要到浏览器里,也可以注册一个协议,拉起自己的App,打开了之后再回来。
- 可以用密码学手段注册一个特殊的字符串进去,比如和时间有关系,然后找个签名,让这个网页的代码去判断签名
- 大部分没有这么高的要求,淘宝判断页面是否在App里其实就是加了一段ua而已!因为不需要安全性的判断,不怕你骗我环境
Webassembly
可以关注
作为前端负责人如何评估前端工作的绩效
- 评估的时候不要参杂一些无关的东西
- 先排行,再评估
- 不确定的时候,互相比一比,看哪个好
结束
之后是广告。