前端組件化認識

什么是前端組件化和模塊化?
這兩天一直在思考這個問題导匣,以前對這兩個概念的理解很模糊茸时。認為“模塊化”是側重于功能或者數(shù)據的封裝,目的是為了解耦合厕氨;而“組件化”更關注的UI部分命斧,如一個頁面可以分為頭部嘱兼、底部和內容區(qū)域等等。

這樣的理解很明顯是表層的簡單的可能還是不正確的理解汇四,最近反復閱讀了蘇寧前端“代碼民工徐飛”關于組件化話的幾篇文章踢涌,結合Angular1.x和Vue1.x的組件思想對組件化和模塊化有了一些新的認識(原文鏈接在文末)睁壁。

什么是組件化

組件化的概念在后端早已存在多年,只不過近幾年隨著前端的發(fā)展行剂,這個概念在前端開始被頻繁提及钳降,特別是在MV*的框架中遂填。

前端中的“組件化”這個詞,在UI這一層通常指“標簽化”备燃,也就是把大塊的業(yè)務界面凌唬,拆分成若干小塊漏麦,然后進行組裝撕贞。

狹義的組件化一般是指標簽化测垛,也就是以自定義標簽(自定義屬性)為核心的機制食侮。

廣義的組件化包括對數(shù)據邏輯層業(yè)務梳理,形成不同層級的能力封裝链快。

為什么要有組件化眉尸?

不管是前端組件化還是后端的組件化,我認為其目的無非就是為了提高開發(fā)效率和后期維護的效率霉祸。
在說的詳細點丝蹭,就是比如我想實現(xiàn)一個網站的頭部坪蚁,我可以把頭部單獨拿出來進行封裝,根據不同頁面或者說業(yè)務要求,可以靈活定制不同的頭部(結構一致茵典,顏色或展示等不同)宾舅。這樣就可以在不同的頁面進行靈活的復用筹我,后期如果頭部結構有大的變動,可以只修改該頭部組件就好了结澄。是不是這個概念很熟悉,其實你早就在這么干了们妥,比如以前用jsp做靜態(tài)頁面生成的時候勉吻,就可以利用jsp的<jsp:include page="xxx.jsp"/>指令進行引入公共組件齿桃,可能也有人理解那是模板的概念。

MV*框架中的組件

大概了解了一下組件化的概念带污,我們來看看Vue和Angular中是怎樣運用組件的思想的踩娘。

Vue中的組件

組件(Component)是 Vue.js 最強大的功能之一养渴。組件可以擴展 HTML 元素,封裝可重用的代碼翘紊。在較高層面上藐唠,組件是自定義元素宇立, Vue.js 的編譯器為它添加特殊功能。在有些情況下柳琢,組件也可以是原生 HTML 元素的形式柬脸,以 is 特性擴展毙驯。

以上是Vue官網對于Vue中組件的解釋,其中在徐飛的從HTML Components的衰落看Web Components的危機一文中的評論部分垦巴,Vue的作者尤雨溪(github:yyx990803)發(fā)表了對民工叔叔(我只是小菜鳥,叫叔叔沒啥問題蛾号,哈哈)文章的看法鲜结,其中也提到了他對于組件的認識活逆。

Vue中關于組件的介紹是Vue中的重點部分蔗候,從它在Vue官方文檔中的篇幅就可以看出來。我認為組件中最重要的方面纫事,到目前為止我能理解的就兩部分:通訊復用所灸。接下來重點介紹一下Vue對于通訊的實現(xiàn)爬立。

結合尤雨溪在民工叔叔文章中評論那樣,組件之間的通訊可分為從內向外從外向內兩種抡秆。Vue對于這兩種通信時怎樣解決的呢儒士?文檔中說的很詳細了檩坚,events upprops down

從外向內的“props down”

具體來說,就是當父組件向子組件傳遞信息的時候剩檀,采用的是在子組件的構造對象中顯示的設置props屬性進行數(shù)據的傳輸沪猴。

  Vue.component('child', {
    // 聲明 props
    props: ['message'],
    // 就像 data 一樣,prop 可以用在模板內
    // 同樣也可以在 vm 實例中像 “this.message” 這樣使用
    template: '<span>{{ message }}</span>'
})

然后向他傳入一個普通字符串值:

  <child message="hello!"></child>

當然為了實現(xiàn)字面量語法或者動態(tài)語法壶辜,可以使用v-bind在父組件上來綁定數(shù)據砸民,詳細語法請參照Vue文檔奋救,本文不做詳細介紹尝艘。

需要特別注意的是:

  1. Vue默認的是單項數(shù)據流,當父組件的屬性變化時秒际,將傳導給子組件狡汉,但是不會反過來轴猎。這是為了防止子組件無意修改了父組件的狀態(tài)——這會讓應用的數(shù)據流難以理解。
  2. 另外锐峭,每次父組件更新時可婶,子組件的所有 prop 都會更新為最新值矛渴。這意味著你不應該在子組件內部改變 prop 。如果你這么做了蚕涤,Vue 會在控制臺給出警告揖铜。
  3. 注意在 JavaScript 中對象和數(shù)組是引用類型达皿,指向同一個內存空間天吓,如果 prop 是一個對象或數(shù)組贿肩,在子組件內部改變它會影響父組件的狀態(tài)。
    并且當props為應用類型的時候還可以為其添加驗證龄寞。

從內向外的“events up”

Vue中默認的雖然是單項數(shù)據流汰规,但還是可以實現(xiàn)子組件向父組件傳輸數(shù)據的,因為有些場景中物邑,我們不可避免的會使用到溜哮。而Vue中向上傳遞數(shù)據采用的和Angular中一樣的思想,通過自定義事件的方式實現(xiàn)拂封。大體需要以下兩步步:

  1. 使用 $emit(eventName)在子組件上觸發(fā)事件
  2. 使用 $on(eventName) 在父組件上(或者祖先組件)監(jiān)聽事件
  <div id="counter-event-example">
    <p>{{ total }}</p>
    //父組件監(jiān)聽事件
    <button-counter v-on:increment="incrementTotal"></button-counter>
    <button-counter v-on:increment="incrementTotal"></button-counter>
</div>
  Vue.component('button-counter', {
    template: '<button v-on:click="increment">{{ counter }}</button>',
    data: function () {
      return {
        counter: 0
      }
    },
    methods: {
      increment: function () {
        this.counter += 1;
        this.$emit('increment');//子組件觸發(fā)事件
      }
    },
  })

  new Vue({
    el: '#counter-event-example',
    data: {
      total: 0
    },
    methods: {
      incrementTotal: function () {
        this.total += 1;
      }
    }
  })

中央事件總線

有時候非父子關系的組件也需要通信。在簡單的場景下冒签,使用一個空的 Vue 實例作為中央事件總線:

var bus = new Vue();
// 觸發(fā)組件 A 中的事件
bus.$emit('id-selected', 1)
// 在組件 B 創(chuàng)建的鉤子中監(jiān)聽事件
bus.$on('id-selected', function (id) {
// ...
})

在更多復雜的情況下在抛,你應該考慮使用專門的 狀態(tài)管理模式--Vuex

另外Vue還可以使用slot分發(fā)內容,具體實現(xiàn)和更多實現(xiàn)請參考Vue文檔萧恕。

簡單描述了一下Vue的組件思想刚梭,主要目的是從民工叔叔的組件化文章中重新認識到了組件化的思想,結合Vue框架去理解票唆,可能更容易明白民工叔叔的見解朴读。

Angular中的組件

嚴格來說,Angular1.x中并沒有明確提及組件的概念走趋,只是我們可以使用app.directive()來實現(xiàn)自定義指令衅金,我覺著這其實就是組件。

而Angular中實現(xiàn)組件之間進行通信的方式主要有四種方式:

基于$rootScope的全局變量和$scope作用域的繼承性

基于作用域的繼承性來實現(xiàn)組件通信僅限于作用域鏈上的通信簿煌,需要對Angular的controller之間的作用域關系特別熟悉氮唯,詳細可以參考民工叔叔的AngularJS實例教程(二)——作用域與事件

利用事件$on()姨伟、$emit()惩琉、$broastcase()方式

先上一張圖:


$rootScope
$rootScope

上圖中清晰的展示了Angular中利用事件去上傳數(shù)據和廣播事件的關系圖,于是我們就可以利用事件做點事情了夺荒。

從作用域往上發(fā)送事件瞒渠,使用scope.$emit

$scope.$emit("someEvent", {});

從作用域往下發(fā)送事件,使用scope.$broadcast

$scope.$broadcast("someEvent", {});

這兩個方法的第二個參數(shù)是要隨事件帶出的數(shù)據技扼。
注意伍玖,這兩種方式傳播事件,事件的發(fā)送方自己也會收到一份剿吻。

利用服務實現(xiàn)

利用myApp.factory()生成一個需要共享數(shù)據的對象窍箍,然后在controller中注入,就可以是獲取到共享數(shù)據了,并進行修改了仔燕。
直接上代碼:

  var myApp = angular.module("myApp", []);
  myApp.factory('Data', function() {
    return {
      name: "Ting"
    }
  });
  myApp.controller('FirstCtrl', function($scope, Data) {
    $scope.data = Data;
    $scope.setName = function() {
      Data.name = "Jack";
     }
    });
  myApp.controller('SecondCtrl', function($scope, Data) {
    $scope.data = Data;
    $scope.setName = function() {
      Data.name = "Moby";
    }
  });

訂閱發(fā)布模式

民工叔叔在AngularJS實例教程(二)——作用域與事件一文的末尾提出了利用訂閱發(fā)布模式來通信,我覺著太經典了魔招,接收方在這里訂閱消息晰搀,發(fā)布方在這里發(fā)布消息。這個過程可以用這樣的圖形來表示:

訂閱發(fā)布模式通信
訂閱發(fā)布模式通信

代碼寫起來也很簡單办斑,把它做成一個公共模塊外恕,就可以被各種業(yè)務方調用了:

app.factory("EventBus", function() { 
  var eventMap = {}; 
  var EventBus = { 
    on : function(eventType, handler) { 
      //multiple event listener
      if (!eventMap[eventType]) { 
        eventMap[eventType] = [];
      }
      eventMap[eventType].push(handler);
    }, 
    off : function(eventType, handler) { 
      for (var i = 0; i < eventMap[eventType].length; i++) { 
        if (eventMap[eventType][i] === handler) { 
          eventMap[eventType].splice(i, 1); 
          break; 
        } 
      } 
     }, 
    fire : function(event) { 
      var eventType = event.type; 
      if (eventMap && eventMap[eventType]) { 
        for (var i = 0; i < eventMap[eventType].length; i++) { 
          eventMap[eventType][i](event); 
        } 
      } 
    } 
  }; 
  return EventBus;
});

事件訂閱代碼:

EventBus.on("someEvent", function(event) { 
  // 這里處理事件 
  var c = event.data.a + event.data.b;
});

事件發(fā)布代碼:

EventBus.fire({
  type: "someEvent",
  data: { 
    aaa: 1, 
    bbb: 2 
  }
});

注意,如果在復雜的應用中使用事件總線乡翅,需要慎重規(guī)劃事件名鳞疲,推薦使用業(yè)務路徑,比如:"portal.menu.selectedMenuChange"蠕蚜,以避免事件沖突尚洽。

非常經典的Angular組件之間通信的一種方式,我在解釋一下靶累,如果A組件中有值需要傳遞給B組件腺毫。那么在B組件Controller中通過EventBus.on()訂閱事件:

EventBus.on("someEvent", function(event) { 
  // 這里是B組件中對A組件傳過來的值進行處理的回調函數(shù)
  var c = event.data.a + event.data.b;
});

然后,在A組件Controller中將其中的值通過EventBus.fire()發(fā)布一下:

EventBus.fire({
  type: "someEvent",
  data: { 
    aaa: 1, 
    bbb: 2 
  }
});

這樣B組件就可以拿到A組件中的數(shù)據值了挣柬。

組件化再思考

簡單了解了一下Vue和Angular中的組件思想潮酒,我們再來回想一下組件化的概念:

狹義的組件化一般是指標簽化,也就是以自定義標簽(自定義屬性)為核心的機制邪蛔。
廣義的組件化包括對數(shù)據邏輯層業(yè)務梳理急黎,形成不同層級的能力封裝

很明顯侧到,不管是Vue還是Angular勃教,對組件的封裝都是為了對數(shù)據邏輯業(yè)務的梳理,使得不同組件各司其職床牧,當然這其中就包括了對HTML的組件化荣回,CSS的組件化和JS的組件化。

對于HTML的組件化可以理解為利用各種語義化標簽或者自定義標簽(Vue中的組件和Angular中的自定義指令等)對結構進行封裝戈咳。而對于CSS的組件化心软,我們現(xiàn)在可以利用SASS或者LESS來實現(xiàn),根據不同的功能對樣式進行不同的封裝著蛙,我覺著現(xiàn)在前端的一些UI框架就是對組件化的使用删铃,去哪兒網杜瑤的Yo框架就是一個很好的例子。

至于對于JS的組件化運用踏堡,我個人覺著就是模塊化猎唁。不管是CommonJS規(guī)范、AMD規(guī)范顷蟆、CMD規(guī)范還是ES2015的模塊機制目的都是對JS進行模塊化開發(fā)诫隅,使得不同功能的邏輯或者業(yè)務分離開腐魂,每個模塊專注自己的業(yè)務邏輯,這樣不僅是開發(fā)的時候工作目錄一目了然逐纬,后期維護的時候也能快速定位到各個業(yè)務邏輯模塊蛔屹。

小結

簡單的對民工叔叔的幾篇文章做了一下總結,其實還沒有理解到位豁生,每篇文章讀了僅僅3-4遍兔毒,每讀一遍都有不一樣的感受,理解也不一樣甸箱∮可能等過段時間再去讀又會顛覆我現(xiàn)在的認識,不管對于錯芍殖,先記錄一下現(xiàn)在的感受豪嗽,后期有了新的認識再來補充或者修改。
下面推薦民工叔叔的對于組件化和Angular的一些文章豌骏,我做了少許整理昵骤,大家可以直接按照目錄由淺至深的閱讀原文。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末肯适,一起剝皮案震驚了整個濱河市变秦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌框舔,老刑警劉巖蹦玫,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異刘绣,居然都是意外死亡樱溉,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門纬凤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來福贞,“玉大人,你說我怎么就攤上這事停士⊥诹保” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵恋技,是天一觀的道長拇舀。 經常有香客問我,道長蜻底,這世上最難降的妖魔是什么骄崩? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上要拂,老公的妹妹穿的比我還像新娘抠璃。我一直安慰自己,他們只是感情好脱惰,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布鸡典。 她就那樣靜靜地躺著,像睡著了一般枪芒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谁尸,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天舅踪,我揣著相機與錄音,去河邊找鬼良蛮。 笑死抽碌,一個胖子當著我的面吹牛,可吹牛的內容都是我干的决瞳。 我是一名探鬼主播货徙,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼皮胡!你這毒婦竟也來了痴颊?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤屡贺,失蹤者是張志新(化名)和其女友劉穎蠢棱,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體甩栈,經...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡泻仙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了量没。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玉转。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖殴蹄,靈堂內的尸體忽然破棺而出究抓,到底是詐尸還是另有隱情,我是刑警寧澤袭灯,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布漩蟆,位于F島的核電站妓蛮,受9級特大地震影響怠李,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望唐础。 院中可真熱鬧一膨,春花似錦、人聲如沸蝉衣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灯萍。三九已至,卻和暖如春脸候,著一層夾襖步出監(jiān)牢的瞬間配深,已是汗流浹背携添。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凉馆,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓亡资,卻偏偏與公主長得像澜共,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子锥腻,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內容

  • 本文章是我最近在公司的一場內部分享的內容嗦董。我有個習慣就是每次分享都會先將要分享的內容寫成文章。所以這個文集也是用來...
    Awey閱讀 9,452評論 4 67
  • 序 今年大前端的概念一而再再而三的被提及瘦黑,那么大前端時代究竟是什么呢京革?大前端這個詞最早是因為在阿里內部有很多前端開...
    一縷殤流化隱半邊冰霜閱讀 11,235評論 19 92
  • 又到了寫月度總結的時間啦。剛過去的一個月里發(fā)生了很多奇妙的事情幸斥,我觸碰到了很多舊人舊事匹摇,和一些原本以為不會再有交集...
    茜喵閱讀 596評論 10 13
  • 好久了已經 已經快要忘記了 我們最初那些青澀的對白 也許是某些種奇怪有趣的東東吧 我們逐漸由陌生走向熟悉 第一句 ...
    Ohmyeah閱讀 335評論 2 0
  • 地球每天都轉,世界每天都變甲葬,我們也一天天地成長起來廊勃,成長的經歷有苦也有樂,有酸也有甜经窖。人生就像一道美味佳肴坡垫,它包括...
    奎啟發(fā)閱讀 407評論 0 2