摘要:框架加冕時(shí)代年橫空出世的前端框架的模塊機(jī)制的模塊機(jī)制相比老王老李的解決方案上增強(qiáng)了模塊的約束性,和幫助開(kāi)發(fā)者劃分模塊外,最重要的是解決了模塊的運(yùn)行時(shí)管理問(wèn)題模塊的初始化順序問(wèn)題和依賴的模塊自動(dòng)初始化問(wèn)題。
前端框架工程化之路
人類的發(fā)展動(dòng)力源于一個(gè)“懶”字,就如現(xiàn)在的大前端正是史前那群“懶”而聰明的“切圖仔”進(jìn)了軟件工程的施工現(xiàn)場(chǎng),懷揣著更少代碼、更少溝通、更少錯(cuò)誤、更少維護(hù)的夢(mèng)想奔襲而來(lái)。從框架齊放鬧革命到三大框架三足鼎立,從構(gòu)建工具爭(zhēng)鳴到Webpack一統(tǒng)江湖,從Javascript 遵循ES5長(zhǎng)達(dá)7年統(tǒng)治到向ES6的自我進(jìn)化。前端的發(fā)展與它們的成功都離不開(kāi)一個(gè)“術(shù)”,工程化。
模塊的進(jìn)化 在沒(méi)有框架的史前我們面臨的問(wèn)題:
1.全局變量污染:各個(gè)文件的變量都是掛載到window對(duì)象上,污染全局變量。
2.變量重名:不同文件中的變量如果重名,后面的會(huì)覆蓋前面的,造成程序運(yùn)行錯(cuò)誤。
3.文件依賴順序:多個(gè)文件之間存在依賴關(guān)系,需要保證一定加載順序問(wèn)題嚴(yán)重。
于是老王想出使用自執(zhí)行函數(shù)的方法去解決問(wèn)題
var foo = (function(cNum){ var aStr = "aa"; var aNum = cNum + 1; return { aStr: aStr, aNum: aNum }; })(cNum);
前端最初始的模塊誕生了,這個(gè)模塊有問(wèn)題嗎?有!雖然模塊內(nèi)部的變量對(duì)全局不可見(jiàn)了,但暴露出來(lái)的foo是一個(gè)全局變量,這樣的模塊多了全局變量也會(huì)很多。
老李在老王的辦法基礎(chǔ)上添加命名空間去解決問(wèn)題:
app.util.modA = xxx; app.common.modA = xxx; app.tools.modA.format = xxx;
除了寫(xiě)法丑陋外,這樣的模塊約束力極低,很容易遭到不遵守的開(kāi)發(fā)者破壞,需要開(kāi)發(fā)者有一定的劃分不同模塊的能力,更大的問(wèn)題是需要人為的解決模塊加載、初始化等管理問(wèn)題。
框架加冕時(shí)代2009年橫空出世的前端框架Angularjs的模塊機(jī)制
angular.module("ConfigModule").service("TextConfig", function () { this.headerText = { }; }); angular.module("HeaderModule", ["ConfigModule"]).controller("HeaderCtr", ["$scope", "TextConfig", function ($scope, textConfig) { $scope.headerText = textConfig.headerText; }]);
Angularjs的模塊機(jī)制相比老王、老李的解決方案上增強(qiáng)了模塊的約束性,和幫助開(kāi)發(fā)者劃分模塊外,最重要的是解決了模塊的運(yùn)行時(shí)管理問(wèn)題(模塊的初始化順序問(wèn)題和依賴的模塊自動(dòng)初始化問(wèn)題。再被多個(gè)模塊依賴的情況,模塊僅且只加載一次的問(wèn)題,統(tǒng)一的輸入輸出api問(wèn)題)。看似完美的方案,但仍有問(wèn)題。
構(gòu)建工具輔政Angularjs的模塊機(jī)制只解決了運(yùn)行時(shí)管理問(wèn)題,但沒(méi)有解決模塊加載管理問(wèn)題。這讓使用者不得不去鏈?zhǔn)皆陧?yè)面引用模塊文件。所以在那個(gè)時(shí)候出現(xiàn)了一些構(gòu)建工具與相應(yīng)的插件來(lái)幫助我們 比如Gulp、Grunt、插件 Browserify。
實(shí)際上Angularjs的模塊機(jī)制也只是一定程度的解決了運(yùn)行時(shí)管理問(wèn)題,了解的同學(xué)應(yīng)該知道在Angularjs里做模塊異步懶加載是件非常困難的事情。在Angular 2及以上版本加入了動(dòng)態(tài)加載模塊的支持。其它框架,例如Vue組件(這里暫且把Vue的組件當(dāng)作模塊看待,后面會(huì)進(jìn)行區(qū)分)也加了相應(yīng)的支持, 這得益于框架的組件或模塊的factor機(jī)制的支持和Webpack code splitting功能的支持。
const foo = resolve => { require.ensure(["./Foo.vue"], () => { resolve(require("./Foo.vue")) }) } const router = new VueRouter({ routes: [ { path: "/foo", component: Foo } ] })一直在接近,但從未實(shí)現(xiàn)的組件化
前面談模塊化發(fā)展中談到了Vue的組件,組件是一種模塊,但又超越模塊。模塊是邏輯單元的封裝,讓開(kāi)發(fā)及維護(hù)成本更低。那么組件則是更高一層的抽象,是一個(gè)業(yè)務(wù)單元的封裝,能夠獨(dú)立運(yùn)行的軟件單元。組件需要解決的問(wèn)題:隔離、去污染問(wèn)題(模塊解決的隔離只限于js變量的隔離、而組件還需要解決css的隔離)、狀態(tài)管理問(wèn)題、與其它組件通訊問(wèn)題,生命周期問(wèn)題,一個(gè)好的組件設(shè)計(jì)還需要遵循軟件設(shè)計(jì)的一些原則。
不得不改源碼的Jquery組件我們先來(lái)看看Jquery的年代組件長(zhǎng)什么樣? 以前的代碼一般是用自執(zhí)行函數(shù)作為一個(gè)類,這里為方便理解,我用TS展示一下。
export class component { selector:Element; options:any static defaultOption = { "color": "red", "fontSize": "16px", "textDecoration":"none"} constructor(selector,opt) { this.selector = selector; this.options = $.extend({}, this.defaultOption, opt) } highlight () { return this. selector.css({ "color": this.options.color, "fontSize": this.options.fontSize, "textDecoration": this.options.textDecoration }); } }
組件使用者拿到這個(gè)組件并初始化,根據(jù)組件上層的一些交互,調(diào)用組件方法,改動(dòng)組件內(nèi)部。 我們可以看到組件上層依賴這個(gè)組件,且依賴的是highlight()的具體實(shí)現(xiàn)。根據(jù)OCP原則,對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉。當(dāng)我們需求變化時(shí),比如我們的highlight需要把背景色改一下,只能對(duì)組件內(nèi)部邏輯做修改。很顯然這樣的組件不是一個(gè)好設(shè)計(jì)。
數(shù)據(jù)驅(qū)動(dòng)讓組件高可用性進(jìn)入有前端框架的時(shí)代,Angular使用數(shù)據(jù)驅(qū)動(dòng)改變視圖的狀態(tài),這是很大的一個(gè)進(jìn)步,數(shù)據(jù)驅(qū)動(dòng)解耦了組件外層對(duì)組件的依賴關(guān)系,將真正的依賴拋向外層的傳給組件的數(shù)據(jù)(有點(diǎn)類似依賴倒置的意思),組件內(nèi)部負(fù)責(zé)根據(jù)數(shù)據(jù)的變化改變UI狀態(tài)。(React Virtual DOM 驅(qū)動(dòng)視圖實(shí)際也是一種數(shù)據(jù)驅(qū)動(dòng),只是一個(gè)是找到數(shù)據(jù)最小粒度的變化直接改動(dòng)對(duì)應(yīng)的視圖,一個(gè)是數(shù)據(jù)生成Virtual DOM找到最小粒度的Virtual DOM變化,改動(dòng)對(duì)應(yīng)的視圖。本質(zhì)上都是數(shù)據(jù)驅(qū)動(dòng)視圖)。
數(shù)據(jù)驅(qū)動(dòng)視圖解耦了組件與組件的依賴問(wèn)題。但同時(shí)引入了一個(gè)問(wèn)題,狀態(tài)混亂問(wèn)題。寫(xiě)過(guò)Angularjs的同學(xué)應(yīng)該知道狀態(tài)混亂之痛,當(dāng)我們?cè)贏ngularjs的多個(gè)組件依賴同一份數(shù)據(jù)時(shí),當(dāng)一個(gè)組件樹(shù)中某一個(gè)組件將該數(shù)據(jù)更改時(shí),整顆組件樹(shù)中使用該數(shù)據(jù)的組件都會(huì)跟著共振。但實(shí)際情況是,樹(shù)當(dāng)中有一部分組件不需要跟著某一次的數(shù)據(jù)變化而變化。
React、Angular使用Immutablejs強(qiáng)化單向數(shù)據(jù)流。這的確減輕了復(fù)雜度,但這種方式對(duì)于子組件想通過(guò)狀態(tài)變更驅(qū)動(dòng)父組件、兄弟組件變化的情況,只能通過(guò)注冊(cè)事件通知的形式。首先這種形式會(huì)違背隔離性,有很高的耦合,組件內(nèi)部必須知道外部想要知道我會(huì)有什么變化,預(yù)留訂閱的鉤子。其次對(duì)于一顆組件樹(shù)跨了N層,極端點(diǎn)從葉子節(jié)點(diǎn)到根節(jié)點(diǎn)這樣一個(gè)通知在每層訂閱子組件事件,會(huì)顯得非常不合理。
于是衍生的一些Flux Redux庫(kù)的狀態(tài)集中式托管,讓一個(gè)組件的數(shù)據(jù)驅(qū)動(dòng)視圖的變化,可以來(lái)源于任何一個(gè)組件樹(shù)節(jié)點(diǎn),又不會(huì)讓變化成復(fù)雜的網(wǎng)狀拓?fù)浣Y(jié)構(gòu),而是成星型拓?fù)浣Y(jié)構(gòu)。
這個(gè)發(fā)展過(guò)程實(shí)際也是為了解耦合,讓組件更加獨(dú)立,就好似以前一個(gè)人的每一個(gè)日常活動(dòng)都推送出去讓全人類知道,由于日常太過(guò)苦逼影起社會(huì)負(fù)面情緒暴增,影響太大了(無(wú)單向數(shù)據(jù)流的情況)。于是乎改為在推送前,大家先訂閱建立關(guān)系,建立關(guān)系則推送,大家發(fā)現(xiàn)這套太麻煩了,需要個(gè)體知道別人關(guān)心我哪個(gè)日?;顒?dòng),家人想知道我吃的什么我建立一個(gè)曬圖發(fā)布方式,領(lǐng)導(dǎo)想知道工作情況,我建立一個(gè)寫(xiě)周報(bào)發(fā)布方式,于是乎我不停的改變自己適應(yīng)社會(huì)(這就是一種耦合,單向數(shù)據(jù)流方式)。當(dāng)個(gè)體喪失人性后,終于想到了一個(gè)更好的辦法,做自己該做的事,把這種拔內(nèi)褲的事情交給社會(huì),我在辦公室就給我采集認(rèn)真工作的照片,系統(tǒng)讓領(lǐng)導(dǎo)看還是讓其它同事看作為個(gè)體的我不需關(guān)心。我在吃大餐的時(shí)候你采集照片,系統(tǒng)要給哪些聯(lián)系人,這由我此刻處的環(huán)境下社會(huì)關(guān)系決定。這個(gè)系統(tǒng)就是狀態(tài)管理器,這個(gè)社會(huì)就是組件所處的環(huán)境。
組件進(jìn)化之殤組件的進(jìn)化從未停下腳步,例如css隔離問(wèn)題從依靠項(xiàng)目wiki中制訂css命名規(guī)范到css Modules自動(dòng)化解決css隔離問(wèn)題。從Angularjs的混亂的網(wǎng)狀狀態(tài)管理到React、Angular使用Immutablejs強(qiáng)化單向數(shù)據(jù)流、和衍生的一些Flux Redux庫(kù)的狀態(tài)集中式托管。從Vue的簡(jiǎn)單生命周期到2.0加入keep-alive、activated、deactivated使生命周期的增強(qiáng)。組件的發(fā)展一片欣欣向榮,但為什么我仍認(rèn)為還沒(méi)有實(shí)現(xiàn)組件化呢?
框架的出現(xiàn)讓組件擁有了其應(yīng)該有的特性,讓開(kāi)發(fā)者無(wú)需再重復(fù)造輪子解決這些問(wèn)題。但也引入了新的問(wèn)題,組件的獨(dú)立性。
前面提到組件應(yīng)當(dāng)是一個(gè)獨(dú)立運(yùn)行的軟件單元。而實(shí)際的情況是,組件只是在某框架體系下獨(dú)立運(yùn)行的軟件單元。而工程化也是一個(gè)去底層服務(wù)的趨勢(shì),我們可以看看近年的Docker技術(shù)、云服務(wù)的Serverless概念,都強(qiáng)調(diào)其無(wú)需關(guān)注底層執(zhí)行環(huán)境,想象一下如果某天我們開(kāi)發(fā)一個(gè)頁(yè)面無(wú)論采用任何技術(shù)架構(gòu)與框架,都只需引入一個(gè)個(gè)Custom Element,把DOM Attribute作為API(或者拿著各團(tuán)隊(duì)發(fā)布的在線運(yùn)行的一個(gè)組件地址),去組裝頁(yè)面即可。這就是近幾年前端的一個(gè)研究課題微前端技術(shù)。目前的微前端技術(shù)也有不錯(cuò)的發(fā)展,利用Custom Element的實(shí)現(xiàn)方案,解決了一些基本的問(wèn)題,如前面提到的:隔離性、狀態(tài)管理、通訊問(wèn)題、生命周期問(wèn)題、不依賴前端架構(gòu)體系問(wèn)題。但作為能獨(dú)立服務(wù)端部署提供使用的一個(gè)component還有很多問(wèn)題待解決,但這是一個(gè)組件化發(fā)展的方向。
這是一個(gè)不錯(cuò)的微前端實(shí)現(xiàn)方案 https://micro-frontends.org/
工程化的烏托邦——規(guī)范化規(guī)范是工程的施工藍(lán)圖,保證產(chǎn)出的產(chǎn)品穩(wěn)健和易維護(hù)。如果沒(méi)有規(guī)范我們改一行代碼可能出現(xiàn)這樣的情況。
那是不是有了規(guī)范文檔,就好了?在趕著上線的高壓、高疲勞下?tīng)顟B(tài),可能出現(xiàn)這樣的同事。
我們前面已經(jīng)提到過(guò)一些古老“法典”,如Jquery時(shí)代模塊定義的規(guī)則、css規(guī)范的規(guī)則,還有前面沒(méi)提到的項(xiàng)目結(jié)構(gòu)劃分、代碼書(shū)寫(xiě)規(guī)范。 大家有沒(méi)有發(fā)現(xiàn)它們都在歷史的舞臺(tái)中消失或者說(shuō)們不必在為規(guī)范的實(shí)現(xiàn)耗費(fèi)精力。為什么?當(dāng)一個(gè)“法典”的受管控者和執(zhí)法者都是自身的時(shí)候,那法典也就成了空談。所以我們需要一個(gè)公正的執(zhí)法者——機(jī)器(自動(dòng)化)。
終會(huì)讓項(xiàng)目wiki消失的自動(dòng)化代碼規(guī)范,我們擁有jslint幫我們校驗(yàn),有編輯器插件幫我們根據(jù)規(guī)范自動(dòng)格式化。
項(xiàng)目結(jié)構(gòu),我們有對(duì)應(yīng)的CLI幫我們生成。
模塊的定義,我們有框架幫助劃分解決運(yùn)行時(shí)問(wèn)題,有Webpack幫助解決加載問(wèn)題,等等。
自動(dòng)化并非真正讓規(guī)范消失,而是對(duì)規(guī)范的更加強(qiáng)制化和易實(shí)施化,達(dá)到“無(wú)約”自制的效果。
那些wiki里規(guī)范讓開(kāi)發(fā)者要怎么怎么寫(xiě)代碼的文章我覺(jué)得大可不必,話說(shuō):“能動(dòng)手的不BB,能自動(dòng)化的不文檔”。
而對(duì)于項(xiàng)目wiki里那些讓開(kāi)發(fā)者如何與其它人合作寫(xiě)代碼的文檔我也覺(jué)得大可不必。比如:我們與后端如何對(duì)接,我們可以使用YAPI這類工具,讓前后端對(duì)接口定義及數(shù)據(jù)結(jié)構(gòu)一目了然和保持實(shí)時(shí)穩(wěn)定性。
對(duì)于前端與前端之間如何互相調(diào)用模塊或組件,我們可以利用Typescript,讓模塊組件接口更加清晰和強(qiáng)類型帶來(lái)的穩(wěn)定性。
對(duì)于強(qiáng)類型帶來(lái)的其它好處我舉個(gè)例子:
這個(gè)是我寫(xiě)無(wú)ts的vue項(xiàng)目時(shí)一個(gè)很低級(jí)的bug
export default { props: {}, data() { return { isFullscreen: false; } }, methods: { toggle() { this.isFullScreen = true; } } }
看似是個(gè)大小寫(xiě)問(wèn)題,但完全是可以避免,如果這個(gè)組件是個(gè)強(qiáng)類型,IDE(支持ts的)會(huì)推斷this類型,該字段是否聲明過(guò)給予校驗(yàn)提示。這個(gè)是書(shū)寫(xiě)上的帶來(lái)的好處。強(qiáng)類型還給我們帶來(lái)很多好處與方便,比如可以很快的了解一個(gè)模塊提供的API。可以在多模塊引用同一個(gè)數(shù)據(jù)時(shí),某個(gè)時(shí)期對(duì)該數(shù)據(jù)結(jié)構(gòu)進(jìn)行一定調(diào)整后,能立刻知道那些陳舊的代碼哪些需要隨著這次改動(dòng)一起調(diào)整等等。
除此之外TS還能在自動(dòng)化文檔上起到輔助作用。我們可以看一下Angular的文檔自動(dòng)生成工具有多棒~(yú)
https://compodoc.github.io/co...
demo: https://compodoc.github.io/co...
當(dāng)我們站在巨人的肩膀上時(shí),從未覺(jué)得向前走一步是如此輕松...愿,未來(lái)的前端走得更輕松。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/53550.html
摘要:框架加冕時(shí)代年橫空出世的前端框架的模塊機(jī)制的模塊機(jī)制相比老王老李的解決方案上增強(qiáng)了模塊的約束性,和幫助開(kāi)發(fā)者劃分模塊外,最重要的是解決了模塊的運(yùn)行時(shí)管理問(wèn)題模塊的初始化順序問(wèn)題和依賴的模塊自動(dòng)初始化問(wèn)題。 前端框架工程化之路 人類的發(fā)展動(dòng)力源于一個(gè)懶字,就如現(xiàn)在的大前端正是史前那群懶而聰明的切圖仔進(jìn)了軟件工程的施工現(xiàn)場(chǎng),懷揣著更少代碼、更少溝通、更少錯(cuò)誤、更少維護(hù)的夢(mèng)想奔襲而來(lái)。從框架...
某熊的技術(shù)之路指北 ? 當(dāng)我們站在技術(shù)之路的原點(diǎn),未來(lái)可能充滿了迷茫,也存在著很多不同的可能;我們可能成為 Web/(大)前端/終端工程師、服務(wù)端架構(gòu)工程師、測(cè)試/運(yùn)維/安全工程師等質(zhì)量保障、可用性保障相關(guān)的工程師、大數(shù)據(jù)/云計(jì)算/虛擬化工程師、算法工程師、產(chǎn)品經(jīng)理等等某個(gè)或者某幾個(gè)角色。某熊的技術(shù)之路系列文章/書(shū)籍/視頻/代碼即是筆者蹣跚行進(jìn)于這條路上的點(diǎn)滴印記,包含了筆者作為程序員的技術(shù)視野、...
摘要:一步,兩步,三步四步五步,就這樣到達(dá)了人生的巔峰傳統(tǒng)前端生態(tài)初級(jí)不使用打包中間處理工具,手工處理圖片等資源掌握以下知識(shí)點(diǎn)基礎(chǔ)結(jié)構(gòu),基礎(chǔ)樣式,基礎(chǔ)語(yǔ)法框架,系列插件框架,等基礎(chǔ)插件,等其他移動(dòng)端適配,瀏覽器兼容,瀏覽器調(diào)試等恭喜完成新手村修 一步,兩步,三步四步五步,就這樣到達(dá)了人生的巔峰~ 傳統(tǒng)前端生態(tài)-初級(jí) 不使用打包、中間處理工具,手工處理js、css、圖片等資源 掌握以下知識(shí)點(diǎn):...
閱讀 4318·2021-09-24 09:47
閱讀 1192·2021-09-03 10:33
閱讀 2077·2019-08-30 11:13
閱讀 1038·2019-08-30 10:49
閱讀 1762·2019-08-29 16:13
閱讀 2052·2019-08-29 11:28
閱讀 3101·2019-08-26 13:31
閱讀 3638·2019-08-23 17:14