接上篇,有同事看了我寫的博客,覺得我這人不靠譜,文筆太白了,不夠嚴肅,所以這次我一定要做一個嚴肅的人,寫博客要有寫博客的態度,第三篇開始我在考慮一個問題,會不會太着急了,要知道Vue的組件化時它的一個買點,現在還沒有完全的過一遍其核心概念我就開始結合后端了,有點操之過急了,沒寫過這類博文,大家見諒。
計划由於上述原因就會有些變動,會在接下來的文章中,先以demo的形式把一些核心概念先過一遍,這樣我們才能寫逼格很高的代碼,要不寫代碼的時候總是捉急,就如以前學數學概念的時候,沒學到的知識不能用,但是當前學的知識去解題又土又笨,學了后面的知識才發現原來解法如此可愛,好了,水話結束,我們以component開始我們的第三篇。
1、什么是組件
現在的前端框架,假如做宣傳,不提組件化就感覺沒有前途,三大主流框架(Vue,React,Ng2+)都是完全支持組件化開發,那什么是組件化呢,這里我不會去拿百度google的結果貼出來,說一下簡單的理解。我理解的組件化就是把內容分塊,把業務分塊,把結構分塊,可能還有其它維度;所謂分塊其實專業一點可以稱為封裝,封裝、繼承、多態是面向對象中的概念,但是也是代碼重用的一種手段,我們對代碼、對系統進行組件化也是代碼重用的一種手段。
舉個簡單的例子,在H5發布之后,新增了幾個復雜標簽,比如video,audio等,這種本質上是什么東西呢,為什么瀏覽器遇到這個標簽都能渲染出一個播放窗口呢,請看下圖:

我在Html中添加了一個video標簽,打開chrome控制台查看元素看到的內容說明了其實video這個標簽是有好多基本標簽組成的,只不過我們看不到而已,里面的shadow,大家可以去查一下shadow dom,不只是這種標簽,甚至一個簡單的輸入框也不是看到的那么簡單:

現在說一下vue中的組件或者前端框架中的組件,我們的目的也是構建類似video這樣的標簽,但是和video不同的是,video瀏覽器能識別,可以直接渲染,而我們定義的標簽比如“my-video”瀏覽器不能識別無法渲染,但是我們借助框架的支持也可以渲染,這樣不就相當於我們通過自定義標簽的方式擴展了瀏覽器標簽了么。
組件一般分為ui組件和業務組件,ui組件由於其業務無關性,重用度比較高。業務組件只是系統內封裝,方便組件消費者使用,組件化的系統一般結構都是從根組件開始下方為二級組件,三級組件,由組件構成樹狀結構。
2、Vue中的組件
Vue中注冊組件有兩種方式:全局注冊和局部注冊;全局注冊通過component方法進行注冊,在Vue實例對象的作用於內都可以直接使用;局部注冊通過給Vue實例中的components對象添加屬性的方式來注冊,這種方式注冊的組件只能在當前實例(可能是一個組件,也可能是根實例)中使用。
1、全局注冊
Vue.component('comp1',options);
2、局部注冊
var comp1={
....
};
new Vue({ el:'#app', components:{ 'comp1':comp1 } })
已經了解了組建的注冊機制,我們需要關注的另外一個點就是,當組件在創建注冊后,怎么進行交互呢,比如父子組件,兄弟組件以什么機制來進行數據傳輸呢?傳值是web開發中很重要的一個環節;在熟悉的angular中,父子controller傳值可以通過$emit 和 $broadcast ,前者向上傳播,后者向下傳播;但是並不建議使用后者,因為后者在傳遞過程中會遍歷所有的同級節點。而向組件或者指令中傳值,angular有自己的綁定策略,那么Vue中是不是也有類似的機制呢。沒錯,在Vue中建議的方式就是“props in ,event out”。
現在就以我們的todolist為例,簡單說一下組件開發步驟:
1、划分設計組件
2、分析並實現組件定義
首先對我們的todolist進行一下組件化划分,按照我們對業務的理解,可以划分為以下幾個組件:

其中包含todo-container容器組件,該組件用來管理其它組件;search-bar組件,todo-form組件、todolist組件、todolist組件的子組件todoitem組件。
下面我們基於上圖,對我們的組件進行簡單的定義:
var TodoItem=(function(){ var id=1; return function(title,desc){ this.title=title; this.desc=desc; } })(); /** * 搜索組件 */ var searchBar={ template:` //TODO: `, data:function(){ return { keyword:'' } }, methods:{ search:function(){ this.$emit('onsearch',this.keyword); } } } /** * 表單組件 */ var todoForm={ template:``, data:function(){ return { id:'', title:'', desc:'' } }, methods:{ ok:function(){ this.$emit('onsave',new TodoItem(this.title,this.desc)); this.title=this.desc=''; } } } /** * 列表項組件 */ var todoItem={ template:` //TODO: `, props:['todo'], methods:{ edit:function(){ this.$emit('onedit',this.todo.id); }, remove:function(){ this.$emit('onremove',this.todo.id); } } } /** * 列表組件 */ var todoList={ template:` ... <todo-item v-for="item in items" :todo="item" @onedit="edit($event)" @onremove="remove($event)"></todo-item> `, props:['items'], components:{ 'todo-item':todoItem }, methods:{ edit:function($e){ this.$emit('onedit',$e); }, remove:function($e){ this.$emit('onremove',$e); } } } /** * 容器組件 * 說明:容器組件包括三個字組件 */ var todoContainer={ template:` <div class="container"> <search-bar @onsearch="search($event)"></search-bar> <todo-form @onsave="save($event)"></todo-form> <todo-list :items="todos" @onremove="remove($event)" @onedit="edit($event)"></todo-list> </div> `, data:function(){ return { items:[] } }, components:{ 'search-bar':searchBar, 'todo-form':todoForm, 'todo-list':todoList }, methods:{ search:function($e){ console.log('TODO search'); }, save:function($e){ console.log('TODO save'); }, remove:function($e){ console.log('TODO remove'); }, edit:function($e){ console.log('TODO edit'); } } } var app=new Vue({ el:'#app', components:{ 'todo-container':todoContainer } }); /** * * * <div id="app"> * <todo-container></todo-container> * </app> */
上面的為代碼(應該算半成品代碼),代表了基本的定義,以及依賴關系,最后的注釋表示最終使用方式,具體可運行的代碼我會放在下一篇細講,並分析代碼這樣寫的原因,大家可以根據組件設計草圖,自己按照文旦去寫一下自己的實現,然后通過對比我的實現比較一下優劣,可以在下方評論中說出你的思路或者好的想法,歡迎腦爆!希望大家先寫出可運行的代碼,周五晚上是大家最向往的時候,have a good weekend!!! 休息一下。
