2019-06-18Winter在微信群的公开课讲组件

开场白

  1. 今天主题是《你不知道的组件化开发:组件化的前世今生》(微信群文字直播,了解一下历史吧)
  2. 今天前端生态里面,React、Angular和Vue三分天下。虽然这三个框架的定位各有不同,但是它们有一个核心的共同点,那就是提供了组件化的能力。W3C也有Web Component的相关草案,也是为了提供组件化能力。今天我们就来聊聊组件化是什么,以及它为什么这么重要。

什么是组件化

  1. 其实组件化思想是一种前端技术非常自然的延伸,如果你使用过HTML,相信你一定有过“我要是能定义一个标签就好了”这样的想法。HTML虽然提供了一百多个标签,但是它们都只能实现一些非常初级的功能。
  2. 但是,HTML本身的目标,是标准化的语义,既然是标准化,跟自定义标签名就有一定的冲突。所以从前端最早出现的2005年,到现在2019年,我们一直没有等到自定义标签这个功能,至今仍然是Draft状态。
  3. 不过有同学会问:自定义标签也不等于组件化本身啊。没错,不过可以进一步思考,其实我们需要的并不一定是自定义标签这样的一个形式,可以从软件工程的通用思想来解释,组件化可以拆解为四个更基本的概念:
    1. 复用:组件将会作为一种复用单元,被用在多处。
    2. 解耦:组件本身隔离了变化,组件开发者和业务开发者可以根据组件的约定各自独立开发和测试。
    3. 封装:组件屏蔽了内部的细节,组件的使用者可以只关心组件的属性、事件和方法。
    4. 抽象:组件通过属性和事件、方法等基础设施,提供了一种描述UI的统一模式,降低了使用者学习的心智成本
  4. 我们可以进一步分析,其实组件化并非前端独有的一种需求,任何软件开发过程,或多或少都有那么一些组件化的需求。而你可以思考为什么较好的组件化解决方案(三大框架)会出现在2014年左右这个时间点,这是一个耐人寻味的问题。而我认为这个问题的答案,就藏在前端发展的历史当中。
  5. 我们可以看下复用、解耦、封装、抽象这四个概念,很显然,它们都是为了大型的、多人协作的软件开发所准备的。所以我认为,2014年左右这个时间点,正是前端发展到了一个需要规模化的时间点。
  6. 我们纵观前端的发展历程,开始于2005年左右,是特效为王的时代,前端领域追逐的是炫酷的效果;到了2009年左右,jQuery开始统治前端,这时API易用性和浏览器兼容性是最重要的;等到了2012年移动互联网出现之后,提供组件化方案的“三大框架”逐步占据了前端重要的生态地位。
  7. 接下来我们深入具体的技术细节,看看组件化是如何一步步发展的。

组件化的发展

  1. 首先,标准的DOM元素是这样创建和挂载的:这里的element是一个对象,但是其实JavaScript(早期)里面,根本没法创建这样的对象。一方面也是我们没有办法改变createElement这个函数的行为

    1
    2
    var element = document.createElement("div")
    document.getElementById("container").appendChild(element)
  2. 既然API风格没法靠拢DOM原生,那么就靠拢JS原生吧,所以一些前端开发同学就萌生了创建一个带容器的对象的想法:

    1
    2
    3
    4
    function MyComponent() {
    this.prop1
    this.methode1
    }
  3. 不过,要想挂载又成了难题,普通的JS对象没法被用于appendChild,所以前端工程师就有了两种思路,第一种是反过来,设计一个appendTo方法,让组件把自己挂到DOM树上去:

    1
    2
    3
    4
    5
    6
    function MyComponent() {
    this._root = document.createElement("div")
    this.appendTo = function(node) {
    node.appendChild(this._root)
    }
    }
  4. 第二种比较有意思,是让组件直接返回一个DOM元素,把方法和自定义属性挂到这个元素上:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function MyComponent() {
    var _root = document.createElement("div")
    root.prop1 // =...
    root.method1 = function() {
    // todo sth.
    }
    return root;
    }
    document.getElementById("container").appendChild(new MyComponent())
  5. 虽然从前端全领域来看,组件化到后期(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
    29
    MainPanel = 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) {
    //...
    }
    })
  6. 你可以看到,这是一个完全使用JS来实现组件的体系,它定义了严格的继承关系,这样的方案很好地支撑了ExtJS的前端架构。不过,创建和挂载对象的方式可不止DOM API,还有HTML语言,如何让前端组件融合进HTML语言呢?

    1
    <my-component attr1="xxx"></my-component>
  7. 关于这方面,依赖早期标准的前端技术可以说几乎没有办法。但是,历史中总有些遗珠,微软的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>
  8. 这项技术提供了事件绑定和属性、方法定义,以及一些生命周期相关的事件,应该说已经是一个比较完整的组件化方案了。但是我们可以看到后来的结果,它没有能够进入标准,默默地消失了。用我们今天的角度来看,它可以说是生不逢时。

  9. 到了“三大框架”出现的时代,因为面向的客户群体从少数公司、少数领域变成了广大前端开发群众,也因为一些新技术的出现,让旧时代组件化没法解决的问题有了新的可能性,这些新的组件化方案都保持了HTML甚至CSS的书写习惯。

Vue.js

  1. Vue.js采用了JSON的方法描述一个组件:

    1
    2
    3
    4
    5
    Vue.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>'
    })
  2. 还提供了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
2
3
4
5
6
7
8
9
10
11
12
13
14
class ShoppingList extends React.Component {
render() {
return (
<div className="shopping-list">
<h1>Shopping List for {this.props.name}</h1>
<ul>
<li>Instagram</li>
<li>WhatsApp</li>
<li>Oculus</li>
</ul>
</div>
)
}
}

Angular.js

选择在原本的HTML上扩展,定义组件的方式也是JS class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
export class HeroListComponent implements OnInit {
heros: Hero[];
selectedHero: Hero;

constructor(private service: HeroService) {
//...
}
ngOnInit() {
this.heroes = this.service.getHeroes()
}
selectHero(hero: Hero) {
this.selectedHero = hero;
}
}

继往开来

  1. 我们可以看到,现代的组件化方案跟旧时代组件化方案的一个明显区别就是,现在的组件化方案保留了原有的标记语言部分,并且努力保留了样式表部分。
  2. 虽然技术从旧到新经历了各种变迁,但是组件化的核心并没有变化,我们的目标仍然是在API设计尽可能接近原生的情况下完成复用、解耦、封装、抽象的目标,最终服务于开发,提高效率,降低错误发生比率。
  3. 如果你的公司和前端团队规模正好面临需要建立组件化体系,希望你能从今天所分享的历史中获得一点灵感。
  4. 今天的分享内容主要就是这些,接下来有什么问题欢迎大家提问。(真短啊,一个PPT的感觉,难怪只要10分钟)

Q&A环节

实时记录,所以“我”都是指Winter自称

我们要用什么样的力度去做组件

  1. 其实组件这个东西呢,它的力度是无所谓的,他是一个这样的级联的结构,所以说你可以有粗粒度的组件,也可以有细粒度的组件
  2. 其实相比说“力度”,更应该关心“体系”。
  3. 不论是细粒度还是粗粒度的组件,最后的目的都是形成一个“很好用”的组件体系

SEO和组件化的问题

  1. 现代的SEO已经走到了一条完全不一样的道路上了
  2. 我们现在有一套方案,比如服务器专门渲染一个页面给搜索引擎看
  3. 组件化自定义的标签,肯定还是有影响的,毕竟搜索引擎不认识
  4. 但是本身HTML也有专门写给搜索引擎看得标签,所以也没问题。

过度封装的问题

  1. 很少会到“过度”的地步
  2. 需要对OOP的基本原则有一定了解
  3. 属性、方法聚合封装的前提是他们有局部性

怎么解决组件化的体系设计问题

朋友勾三股四说:如果你做UI组件,可以直接参考W3C的accessblity的标准。他本身不是组件,但是基本总结了人类常用标准,所以在设计的时候是很好的参考(emmmm,这上次Vue杭州的夜会上也听勾3说起)

微服务和组件化

  1. 微服务就是来自后端的,还没见说前端谁这么说
  2. 因为前端没有什么服务的,所以这个讲法的维度就过大了
  3. 其主张是一个服务只做一件事而不是混合业务场景去设计Api

Web3.0 WebComponent TypeScript

未来的事肯定说不好,但是可以积极对待,目前也确实可以解决不少问题

组件怎么迭代

  1. 淘宝曾经有一个轮播组件被加了30多个参数……
  2. 设计组件也是遵循面向对象原则,对修改封闭,对扩展开放
  3. 所以新功能可以通过继承或者包装来做

GraphQL

  1. Taobao没怎么做,Facebook那边也一点
  2. 但是目前很少有公司这么落地的
  3. 淘宝有一些变种

组件间的消息机制

  1. 不建议postMessage这样的机制
  2. 听阿里晋升演讲里,很多人都讲自己建立的组件体系和消息机制,消息进去了debug的链路就断了。不好。
  3. flux、redux都是变形的消息机制。
  4. 我更推崇Vue和Angular的双向绑定机制,其实内部也是有消息流转的,但是外在语义化更好,不是一个未经定义的消息,而是规定好的数据变化型消息

Vue首次加载页面白屏

  1. 看看模板是不是没有预编译
  2. 是不是引了调试版本,导致每次都要编译
  3. 我用Vue-cli的话,都没有这个问题

页面性能的问题

  1. 性能和组件化其实没什么关系
  2. 工程体系都需要一些监管和评估手段,所以我们首先要建立一些标准,然后再考虑怎么优化性能

前端架构师和后端架构的区别

  1. 前端架构面临的问题不太一样,是零碎的页面,大量的重复性劳动,而不是传统软件里的模块耦合和复杂性问题
  2. 前端面临和重点关注的主要问题是“复用”——这就是组件化的意义
  3. 前端架构还是可以用很多工具来解决。比如淘宝非常重视的搭建系统,可以大量产出简单的页面(模板/模块化的方法)
  4. (emmm,18年到19年参加的前端各种会,都能感受到阿里的研发方向……)

Angular.js脏检查是否影响性能

  1. 确实影响性能
  2. 但这是设计者技术不过关的问题,所以Angular从2开始就解决的不错

全栈

  1. 国内其实市场不是特别看好
  2. 不建议做Nodejs全栈,因为挺难的,百废待兴
  3. 你要自己去写一个服务很难,阿里的那些大神是可以搞,但其实也很难。
  4. 还不如多学一点手艺,学别的语言来搞。
  5. 职业发展是个人规划问题。

金额计算

  1. 阿里确实都是在后端计算
  2. 然后从风险评估说了一下

HTTP协议规范和设计模式

  1. HTTP状态码都没几个人知道,知道HTTP协议细节我觉得更难了
  2. 面试的话也不知道会从哪里考,所以不好说
  3. 设计模式这本书,他是为基于类的OOP语言编写来写的,所以对JS其实就有点尴尬
  4. 比如工厂模式就是为了解决new的时候,class的东西没法传,但是JS不怕啊,JS构造器可以当作函数参数往里传,所以根本不存在工厂模式这个需求

Flutter要不要学

  1. Flutter不是最火的,最火的是区块链和AI
  2. 什么火学什么,要累死。要转也可以,但是他是一个全新的体系,有一定的隔离,目前也算个小众
  3. 以后会有一席之地吧。

前端的自己的设计模式

前端没有“四人帮”那样的人物,总结的模式都比较弱,难有23模式那种总结

数据结构和算法

  1. 别想太复杂。其实for循环都是一种简单的算法,数组也是一种数据结构(顺序表,和链表相对)
  2. 经典数据结构和经典算法,不用硬啃,但是数据结构和算法的水平有比较提升
  3. 面试考算法题其实有点过了,代码和算法不太容易分开,有逻辑也算算法啊

WebGL

  1. 个人非常看好图形学
  2. 但是推荐资深前端,面临瓶颈的时候可以考虑突破
  3. 竞争很激烈,你看比如很多CSS大神会去考虑转这个方向
  4. W3C和chrome是有一些非技术的因素的,前者想推WebGPU,所以,学计算机图形学会更保险,而不是押宝式的选择。

自己的App能否在Chrome上模拟注入某段代码判断目前H5运行在什么环境下吗

  1. 如果WebView是自己的代码,那肯定可以
  2. 如果要到浏览器里,也可以注册一个协议,拉起自己的App,打开了之后再回来。
  3. 可以用密码学手段注册一个特殊的字符串进去,比如和时间有关系,然后找个签名,让这个网页的代码去判断签名
  4. 大部分没有这么高的要求,淘宝判断页面是否在App里其实就是加了一段ua而已!因为不需要安全性的判断,不怕你骗我环境

Webassembly

可以关注

作为前端负责人如何评估前端工作的绩效

  1. 评估的时候不要参杂一些无关的东西
  2. 先排行,再评估
  3. 不确定的时候,互相比一比,看哪个好

结束

之后是广告。