很多年以前,我們寫(xiě)網(wǎng)頁(yè)的時(shí)候都是這樣的:根據(jù)設(shè)計(jì)稿寫(xiě)好一個(gè)頁(yè)面的html和css,然后再去寫(xiě)js來(lái)做一些交互纱控。如果遇到同樣功能的代碼牙言,最簡(jiǎn)單粗暴的方式是復(fù)制粘貼酸钦,如果為了更好的復(fù)用性,就封裝個(gè)jquery的插件咱枉,需要用的時(shí)候就引入插件卑硫,調(diào)用初始化的方法,傳入?yún)?shù)蚕断,比如一個(gè)日歷欢伏、一個(gè)輪播圖。在那個(gè)web野蠻生長(zhǎng)的年代亿乳,這樣的插件產(chǎn)生了很多颜懊,那個(gè)時(shí)代的前端工程師必須會(huì)自定義jquery的插件。那時(shí)候也有一些組件庫(kù),比如extjs河爹、bootstrap匠璧、jquery ui等。
但是這種組件的方案或者說(shuō)jquery本身就有很多問(wèn)題:
- 瀏覽器端效率最低的就是dom操作咸这,因?yàn)闀?huì)觸發(fā)reflow夷恍,repaint,jquery是操作dom的一個(gè)庫(kù)媳维,基于jquery封裝的插件當(dāng)然也避免不了頻繁的操作dom酿雪,所以這樣的的插件如果代碼寫(xiě)的時(shí)候不注意,效率很可能會(huì)比較低侄刽。
- jquery只是一個(gè)庫(kù)指黎,而不是決定代碼組織方式的框架,沒(méi)有固定的代碼規(guī)范州丹,每個(gè)人都會(huì)有自己的編碼風(fēng)格醋安,雖然可以規(guī)定一些規(guī)范,但畢竟不是強(qiáng)制的墓毒。如果團(tuán)隊(duì)成員吓揪,項(xiàng)目規(guī)模比較小的時(shí)候還好,隨著項(xiàng)目所计、團(tuán)隊(duì)規(guī)模的擴(kuò)大柠辞,這樣的代碼會(huì)越來(lái)越難以維護(hù)和復(fù)用。
現(xiàn)在的組件化的方案已經(jīng)在那個(gè)時(shí)代的基礎(chǔ)上前進(jìn)了很大一步主胧。
一些常見(jiàn)的邏輯叭首,我們會(huì)把他們封裝成函數(shù)或者類(lèi),比如BaseXxx踪栋、XxxUtils焙格,牽扯到ui的組件復(fù)用的不只是邏輯,還有模板和樣式己英。也就是說(shuō)一個(gè)組件需要封裝的就是關(guān)聯(lián)的html间螟、css吴旋、js损肛。
我們可以先想想如果我們自己去做一個(gè)組件化的框架,我們會(huì)怎么做(主要考慮如何設(shè)計(jì))荣瑟。
如何去設(shè)計(jì)一個(gè)組件化的框架
模板治拿,樣式,交互邏輯
組件最基礎(chǔ)的就是這三部分笆焰。樣式我們可以不做封裝劫谅,通過(guò)全局引入然后加個(gè)命名空間的方式來(lái)區(qū)分組件。模板可以?huà)燧d到dom樹(shù)上通過(guò)選擇器來(lái)取,或者直接傳入一段模板字符串捏检。交互邏輯的部分荞驴,我們會(huì)通過(guò)事件綁定調(diào)用組件上的一些方法。
class Component{
constructor({el,template,onXxx}){
this.el = el;
this.template = template;
this.onXxx = onXxx;
this.render();
this.bindEvents();
}
render(){
var ele = document.querySelector(this.el);
ele.innerHTML = this.template;
}
bindEvents(){
this.el.querySelector('xx').addEventListener('click', this.onXxx)
}
}
現(xiàn)在我們的組件有了最初的模型贯城,模板熊楼,邏輯,事件綁定能犯,可以傳參數(shù)來(lái)進(jìn)行一些定制
鲫骗。
模板引擎
現(xiàn)在我們把需要把數(shù)據(jù)填充到模板需要用拼接字符串的方式,這樣的代碼寫(xiě)起來(lái)很是繁瑣踩晶,針對(duì)這個(gè)問(wèn)題执泰,已經(jīng)有了成熟的解決方案,我們可以選用某一個(gè)模板引擎渡蜻,像ejs术吝,jsmart,jade之類(lèi)的晴楔。但是我們需要的是一個(gè)能和我們的組件結(jié)合緊密的一個(gè)模板引擎顿苇,我們需要自己實(shí)現(xiàn)一個(gè),這樣税弃,我們可以直接直接取組件中的數(shù)據(jù)纪岁,調(diào)用組件的某個(gè)方法,甚至自己擴(kuò)展一些模板的功能则果。
比如幔翰,我們?nèi)绻雽?shí)現(xiàn)這樣一個(gè)模板引擎,
<table>
<my:forEach items="goodsList" var="goods">
<td>${goods.name}</td>
<td>${goods.price}</td>
<td>${goods.amount}</td>
</my:forEach>
</table>
看上去是不是比較像jsp的語(yǔ)法西壮,其實(shí)jsp就是一個(gè)專(zhuān)用的模板引擎遗增,他有page,session,application,request,response等隱式對(duì)象,可以直接取幾個(gè)域中的數(shù)據(jù)款青,而且也可以支持自定義標(biāo)簽和自定義el函數(shù)做修。
想想該怎么實(shí)現(xiàn)。一種思路是通過(guò)xml的解析抡草,xml解析方式有dom和sax兩種饰及,就是分析出有什么標(biāo)簽有什么屬性。然后對(duì)應(yīng)的屬性做什么操作康震。屬性和對(duì)應(yīng)操作我們給封裝起來(lái)燎含,叫做指令。開(kāi)發(fā)者可以自己去注冊(cè)一些自定義的指令腿短。模板在解析的時(shí)候解析出對(duì)應(yīng)的屬性就會(huì)執(zhí)行對(duì)應(yīng)的操作屏箍。
通過(guò)模板解析的方式來(lái)初始化
我們組件用的時(shí)候绘梦,需要new一個(gè)組件的對(duì)象,傳入需要的參數(shù)赴魁。比如:
new Component({
template:"<div><h1>title</h1><p>content</p></div>",
onXxx: function(){}
});
想一下卸奉,我們?nèi)绻氩煌ㄟ^(guò)js來(lái)初始化,想通過(guò)下面這種方式來(lái)初始化該怎么做颖御,
<Component template="xxxx" onXxx=""></Component>
我們之前自己實(shí)現(xiàn)了一個(gè)模板引擎择卦,除了自定義指令的解析,當(dāng)然也會(huì)把自定義組件的解析加進(jìn)去郎嫁。這樣一棵組件樹(shù)秉继,我們只需要調(diào)用一次初始化方法,然后在解析組件樹(shù)模板的過(guò)程中泽铛,把一個(gè)個(gè)組件初始化尚辑,組裝好。這一些都是用戶(hù)感知不到的盔腔,用戶(hù)只需要寫(xiě)模板杠茬。
雙向綁定MVVM
現(xiàn)在我們的組件還是避免不了要大量的操作dom,這必定會(huì)有很多的性能問(wèn)題弛随。能不能把dom操作也給封裝起來(lái)瓢喉,開(kāi)發(fā)者不需要再去操作dom,只需要管理好數(shù)據(jù)就可以了呢舀透。
想一下后端開(kāi)發(fā)栓票,最頻繁的就是增刪改查,這樣的sql語(yǔ)句是經(jīng)常要寫(xiě)的愕够,于是后端有了orm框架走贪,比如hibernate,映射好實(shí)體類(lèi)和數(shù)據(jù)表惑芭,類(lèi)的屬性和字段的關(guān)系之后坠狡,只需要調(diào)用hibernate提供的Session類(lèi)的增刪改查的方法就好了,sql語(yǔ)句會(huì)自動(dòng)生成遂跟,比如mybatis逃沿,映射好方法和寫(xiě)在xml中的sql語(yǔ)句的關(guān)系,之后只要調(diào)用對(duì)應(yīng)的方法就可以了幻锁,不需要自己去寫(xiě)sql語(yǔ)句凯亮。
數(shù)據(jù)庫(kù)中的表和java的實(shí)體類(lèi)建立了映射關(guān)系就能夠做到開(kāi)發(fā)時(shí)不需要寫(xiě)sql語(yǔ)句,那么我們建立好數(shù)據(jù)和dom越败,也就是model和view之間的關(guān)系是不是也就可以不寫(xiě)任何一句dom操作的代碼触幼,只去管理數(shù)據(jù)呢硼瓣,然后view會(huì)自動(dòng)同步呢究飞。
當(dāng)然是可以的置谦,從model到view的綁定,我們可以監(jiān)聽(tīng)model的變化亿傅,變化的時(shí)候就去通知view中的Observer媒峡,然后那個(gè)Observer去操作dom,去更新視圖葵擎。
監(jiān)聽(tīng)model的變化谅阿,很容易想到的是es5中的Object.defineProperty這個(gè)api,他可以定義set方法酬滤,攔截對(duì)對(duì)象屬性的賦值操作签餐。
//觀察者的隊(duì)列
var observers = [];
observers.push(new Observer({...}));
var obj = {};
var value = "";
Object.defineProperty(obj, 'name', {
get: function() {
return value;
},
set: function(val) {
value = val;
//數(shù)據(jù)改變,通知觀察者盯串,去更新view
var target = this;
observers.forEach(function(observer,index){
observer.notify(target);
});
}
});
當(dāng)然es6提供的Proxy這個(gè)更高層次的封裝類(lèi)也可以氯檐。
// 觀察者的隊(duì)列
var observers = [];
observers.push(new Observer({...}));
let obj = {};
let proxy = new Proxy(obj, {
get: function (target, key, receiver) {
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
Reflect.set(target, key, value, receiver);
for(let observer in observers){
observer.notify(this);
}
}
})
至于從view到model的綁定,其實(shí)就是監(jiān)聽(tīng)用戶(hù)輸入的一些操作体捏,監(jiān)聽(tīng)表單的事件冠摄,然后去根據(jù)用戶(hù)輸入的數(shù)據(jù)和映射關(guān)系,去同步model几缭。
生命周期函數(shù)
我們把dom操作給封裝了河泳,也就是把dom元素的增刪改給自動(dòng)化了,組件對(duì)應(yīng)的dom元素的創(chuàng)建和銷(xiāo)毀或者是重新繪制更新dom的時(shí)候年栓,想做一些操作拆挥,就不能做了,所以我們要在這些時(shí)刻暴露一些鉤子某抓,讓開(kāi)發(fā)者可以在這些時(shí)候去做一些操作竿刁。比如組件的dom初次渲染完的時(shí)候要去請(qǐng)求數(shù)據(jù),比如組件銷(xiāo)毀的時(shí)候要做一些資源釋放的工作避免內(nèi)存泄漏等搪缨。主要的生命周期鉤子函數(shù)就這么四類(lèi)食拜,創(chuàng)建前后,掛載到dom前后副编,更新前后,從dom中移除(銷(xiāo)毀)前后负甸。
生命周期的名字可以叫beforeCreate
,created
痹届,beforeMount
呻待,mounted
,beforeUpdate
队腐,updated
蚕捉,beforeDestroy
,destroyed
柴淘,
也可以叫componentWillMount
迫淹,componentDidMount
秘通,componentWillUpdate
,componentDidUpdate
敛熬,componentWillUnmount
肺稀,componentDidUnmount
等。
虛擬dom和diff算法
現(xiàn)在我們的組件渲染是直接渲染到dom元素应民,并且是全局的渲染话原。model改變不大的時(shí)候,也會(huì)全局重新渲染一次诲锹,會(huì)有很多不必要的dom操作繁仁,性能損耗。我們知道归园,計(jì)算機(jī)領(lǐng)域很多問(wèn)題都可以加一個(gè)中間層來(lái)解決改备,這里也一樣,我們可以不直接渲染到真實(shí)dom元素蔓倍,用js對(duì)象來(lái)模擬真實(shí)dom元素悬钳,每次渲染渲染成這樣的一顆虛擬dom元素組成的樹(shù)。
{
name: 'a',
props: {
},
children: [
{
name: 'a-1',
props:{},
children:[]
},
{
name: 'a-2',
props:{},
children:[]
},
{
name: 'a-3',
props:{},
children:[]
}
]
}
這樣可以把上一次的渲染結(jié)果保留偶翅,下次渲染的時(shí)候和上一次的渲染結(jié)果做對(duì)比默勾,比較有沒(méi)有變化,有變化的話(huà)找出變化的部分聚谁,局部增量的渲染改變的部分母剥。這樣能避免不必要的dom操作帶來(lái)的性能開(kāi)銷(xiāo)。比較的過(guò)程我們可以叫他diff算法形导。
引入了虛擬dom這一層环疼,雖然會(huì)增大計(jì)算量和內(nèi)存消耗,但是卻減少了大量的dom操作朵耕。性能會(huì)有明顯的提升炫隶。
Immutable
我們會(huì)在model變化以后去更新view,但是model有沒(méi)有變化需要和之前的model做對(duì)比阎曹,model是一個(gè)對(duì)象伪阶,可能層次比較深,深層的比較是比較慢的处嫌,這里又會(huì)有性能的問(wèn)題栅贴。針對(duì)這一問(wèn)題,我們應(yīng)該怎么去優(yōu)化呢熏迹?
我們都知道字符串是常量檐薯。jvm的內(nèi)存空間分為堆、棧注暗、方法區(qū)坛缕、靜態(tài)域4個(gè)部分墓猎,方法區(qū)中有個(gè)字符串常量池,來(lái)存放字符串祷膳。也就是我們創(chuàng)建一個(gè)字符串,如果常量池中有的話(huà)屡立,他會(huì)直接把引用返回給你直晨,如果沒(méi)有的話(huà)會(huì)創(chuàng)建一個(gè)字符串然后放入常量池中。對(duì)字符串的修改會(huì)創(chuàng)建一個(gè)新的字符串膨俐,而不是直接修改原字符串勇皇。編程語(yǔ)言基本都是這樣處理字符串的,好處也是很明顯的焚刺,設(shè)想一下敛摘,如果有一個(gè)長(zhǎng)度為1000的字符串,要和另一個(gè)字符串做比較乳愉,那么如果字符串不是常量兄淫,那么完成比較就要要遍歷字符串的每一個(gè)字符,復(fù)雜度為o(n)蔓姚。但如果我們把字符串設(shè)計(jì)為常量捕虽,比較時(shí)只需要比較兩個(gè)字符串的內(nèi)存地址,那么復(fù)雜度就降到了o(1)坡脐。這種優(yōu)化的思路是典型的空間換時(shí)間泄私。
組件的model我們也可以實(shí)現(xiàn)為不可變(immutable)的,這樣比較的時(shí)候只需要比較兩個(gè)model的引用就可以了备闲,會(huì)使性能又有一個(gè)大的提高晌端。
fiber
想一想我們的組件化框架還有哪里有問(wèn)題。
我們知道瀏覽器中每個(gè)頁(yè)面是單線(xiàn)程的恬砂,渲染和js計(jì)算共用一個(gè)線(xiàn)程咧纠,會(huì)相互阻塞。
model改變后要生成虛擬dom泻骤,生成虛擬dom惧盹、虛擬dom之間的diff可能會(huì)計(jì)算比較長(zhǎng)的時(shí)間,如果這時(shí)候頁(yè)面上有個(gè)動(dòng)畫(huà)在同時(shí)搶占著主線(xiàn)程瞪讼,那么勢(shì)必會(huì)導(dǎo)致動(dòng)畫(huà)的卡頓钧椰。每個(gè)痛點(diǎn)的解決,都能會(huì)帶來(lái)性能的提升符欠,為了追求極致的性能嫡霞,這個(gè)問(wèn)題我們也要想辦法解決。
虛擬dom是一顆樹(shù)形的結(jié)構(gòu)希柿,生成或比較一般都是遞歸的過(guò)程诊沪。我們知道所有的遞歸都可以改成循環(huán)的方式养筒,只要我們可以一個(gè)隊(duì)列來(lái)保存中間狀態(tài)。把遞歸改成循環(huán)后端姚,就可以異步化分段執(zhí)行了晕粪。先執(zhí)行一段計(jì)算,然后把執(zhí)行狀態(tài)保存渐裸,釋放主線(xiàn)程去做渲染巫湘,渲染完之后再去做之后的計(jì)算。這樣就完美的解決了瀏覽器環(huán)境下計(jì)算和渲染之間相互阻塞的問(wèn)題了昏鹃,性能有了進(jìn)一步的提升尚氛。
這種資源的競(jìng)爭(zhēng)在計(jì)算機(jī)中隨處可見(jiàn),就像cpu的進(jìn)程調(diào)度洞渤,每個(gè)進(jìn)程的計(jì)算都要用到cpu阅嘶,操作系統(tǒng)就需要用一種合理的方式來(lái)分配cpu資源。cpu調(diào)度策略有很多幾種载迄,比如分時(shí)讯柔,按照優(yōu)先級(jí)等等,都是把一個(gè)大的計(jì)算量給分成多次來(lái)執(zhí)行护昧,暫停執(zhí)行的時(shí)候把上下文信息保存下來(lái)磷杏,得到cpu的時(shí)候再恢復(fù)上下文繼續(xù)執(zhí)行。
計(jì)算量分段捏卓,類(lèi)似切菜极祸,我們把這種調(diào)度策略叫fiber,即纖維化怠晴。
沒(méi)有fiber之前的虛擬dom計(jì)算是這樣的
fiber之后是這樣的
完美解決了瀏覽器的單線(xiàn)程下單次計(jì)算量過(guò)大會(huì)阻塞渲染的問(wèn)題遥金。
Component-Native
之前為了減少不必要的渲染,我們加了個(gè)中間層-虛擬dom蒜田,除了可以帶來(lái)性能的提示之外稿械,我們可以有一些別的思考,比如我可不可以不只渲染成dom元素冲粤,渲染成安卓美莫、ios原生的組件?
經(jīng)過(guò)思考梯捕,我們覺(jué)得這是可行的厢呵,邏輯依然用js來(lái)寫(xiě),通過(guò)jscore來(lái)執(zhí)行js傀顾,js需要調(diào)用的原生api由框架封裝襟铭,提供給js。渲染部分,建立原生組件和和模板中組件的映射關(guān)系寒砖,渲染的時(shí)候生成對(duì)應(yīng)的原生組件赐劣。邏輯的部分可以復(fù)用,除了渲染的是原生的組件哩都,別的功能依然都有魁兼。
思路是可行的,但是實(shí)現(xiàn)這些組件漠嵌、提供供js調(diào)用的原生api咐汞,工作量肯定比較大,而且會(huì)有很多坑献雅。
全局狀態(tài)管理
組件之間可以通過(guò)傳遞參數(shù)來(lái)通信碉考。如果只是父子組件通信比較簡(jiǎn)單塌计,但是如果需要通信的兩個(gè)組件之間間隔的層次比較多挺身,或者是兄弟組件,那么之間互相通信就很麻煩了锌仅,需要多層的傳遞或者是通過(guò)父組件做中轉(zhuǎn)章钾。針對(duì)這個(gè)問(wèn)題,有沒(méi)有什么別的思路呢热芹?
其實(shí)可以引入一個(gè)中介者來(lái)解決贱傀,就像婚姻中介,如果男方自己去找女方伊脓,或者女方自己去找男方都不太方便府寒,這時(shí)候可以找一個(gè)中介,男方和女方分別在那里注冊(cè)自己的信息报腔,然后等中介有消息的時(shí)候通知自己株搔。這樣男方和女方就不需要相互聯(lián)系,只要和婚姻中介聯(lián)系就可以了纯蛾。
類(lèi)似的纤房,我們可以創(chuàng)建一個(gè)store來(lái)存儲(chǔ)全局的信息,組件在store那里注冊(cè)翻诉,當(dāng)一個(gè)組件向store發(fā)送消息的時(shí)候炮姨,監(jiān)聽(tīng)store的組件就能收到消息,從store中取出變化后的數(shù)據(jù)碰煌。
其他
關(guān)于組件的想象空間還有很大舒岸。未來(lái)可能會(huì)能夠渲染到所有的端,渲染過(guò)程中的每一個(gè)環(huán)節(jié)芦圾,每一個(gè)痛點(diǎn)都有相應(yīng)的優(yōu)化方案吁津。性能、功能都可以不斷地提升。只要我們不要停止思考碍脏、停止敲代碼的雙手梭依。
現(xiàn)在主流的組件化的框架
我們從jquery插件出發(fā),思考了很多我們想要的組件化框架的樣子典尾,回到現(xiàn)實(shí)役拴,我們看一下現(xiàn)在主流的組件化的框架有哪些,他們各自都有哪些特性钾埂。
react
- react支持jsx的語(yǔ)法河闰,可以html和js混著寫(xiě),而不像模板引擎褥紫,需要去另外學(xué)習(xí)一套模板的語(yǔ)法姜性。
- 有了jsx,可以直接用
ReactDOM.render(
<MyComponent values="xxx"></MyComponent>,
document.getElementById("container")
)
通過(guò)解析jsx來(lái)初始化髓考,而不需要手動(dòng)去new一個(gè)組件對(duì)象部念。
- react提供了從model到view的單向的綁定,state發(fā)生了變化氨菇,就會(huì)去render
- react也提供了完善的生命周期函數(shù)供開(kāi)發(fā)者在組件創(chuàng)建儡炼、更新、銷(xiāo)毀前后進(jìn)擴(kuò)展一些功能查蓉。而且提供了componentWillReceiveProps和shouldComponentUpdate兩個(gè)用于優(yōu)化性能的生命周期函數(shù)乌询。
componentWillReceiveProps是在組件接收到新的props,還沒(méi)有render之前調(diào)用豌研,在這里去調(diào)用setState更新?tīng)顟B(tài)妹田,不會(huì)觸發(fā)額外的render。shouldComponentUpdate是在state或props變化之后調(diào)用的鹃共,根據(jù)返回的結(jié)果決定是不是調(diào)用render鬼佣, 可以和Immutable.js結(jié)合,來(lái)避免state的深層比較帶來(lái)的性能損耗及汉。沮趣。
- react 有虛擬dom這一層,并且會(huì)通過(guò)優(yōu)化到的o(n)的diff算法來(lái)進(jìn)行虛擬dom的對(duì)比坷随。
- react是reconsiler(調(diào)度者)房铭,react-dom是renderer。react 16使用了fiber這個(gè)新的調(diào)度算法温眉。使得大計(jì)算量被拆解缸匪,提高了應(yīng)用的可訪問(wèn)性和性能。
- react-native提供了可以渲染成安卓类溢、ios組件的renderer凌蔬,同時(shí)提供了原生的api供js調(diào)用露懒。
- 可以結(jié)合redux來(lái)做狀態(tài)管理
vue
- vue提供了內(nèi)置的專(zhuān)用的模板引擎,有指令砂心、過(guò)濾器懈词、插值表達(dá)式等功能,有內(nèi)置的指令過(guò)濾器辩诞,也可以注冊(cè)自己擴(kuò)展的指令過(guò)濾器坎弯。而且提供了render函數(shù),可以結(jié)合babel來(lái)實(shí)現(xiàn)jsx的編譯译暂。
- vue提供了雙向綁定MVVM
- vue有完善的生命周期函數(shù)抠忘,包括create前后,mount前后外永,update前后和destory前后
- vue2.x加入了虛擬dom崎脉,可以減少不必要的渲染
- vue社區(qū)有weex這個(gè)做原生渲染的框架
- vue可以結(jié)合vuex來(lái)做全局狀態(tài)管理
angular2
- 支持模板的語(yǔ)法,指令伯顶、過(guò)濾器囚灼、插值表達(dá)式
- decorator的方式來(lái)聲明組件
- 支持IOC
- 支持組件化
- 支持雙向綁定MVVM
- 創(chuàng)建、更新砾淌、銷(xiāo)毀前后的生命周期函數(shù)
- 和typescript結(jié)合緊密
其他組件化的框架
實(shí)現(xiàn)組件化的框架很多啦撮,比如Avalon谭网、Ember汪厨、Konckout等等,都有各自的特點(diǎn)
WebComponents
組件化是一個(gè)趨勢(shì)愉择,現(xiàn)在有很多實(shí)現(xiàn)組件化的框架劫乱,W3C提出了web compoenents的標(biāo)準(zhǔn):。這個(gè)標(biāo)準(zhǔn)主要由4種技術(shù)組成锥涕,html import衷戈、shadow dom、custom elment和html template层坠。新的標(biāo)準(zhǔn)肯定會(huì)有兼容性的問(wèn)題殖妇,goole推出了Polymer
這個(gè)基于web components規(guī)范的組件化框架。
總結(jié)
從最開(kāi)始的jquery插件破花,到現(xiàn)在的各種組件化的框架谦趣、web components標(biāo)準(zhǔn),組件化已經(jīng)是一種必然的趨勢(shì)座每,我們不僅要會(huì)去設(shè)計(jì)前鹅、封裝組件,更要去了解組件的發(fā)展的前世今生峭梳,這樣才不會(huì)在框架的海洋中迷失舰绘。