【Vue】詳解Vue組件系統


 

正文

Vue渲染的兩大基礎方式

new 一個Vue的實例

這個我們一般會使用在掛載根節點這一初始化操作上

new Vue({
  el: '#app'
})

 

注冊組件並使用

通過Vue.component()去注冊一個組件,你就可以全局地使用它了,具體體現在每個被new的 Vue

實例/注冊組件, 的template選項屬性或者對應的DOM模板中,去直接使用

 

注冊組件

全局注冊

例如,放在通過new創建的Vue實例當中

復制代碼
Vue.component('my-component', {
  template: '<p>我是被全局注冊的組件</p>'
})
/*
  Vue.component(組件名稱[字符串], 組件對象)
*/

new Vue({
  el: '#app',
  template: '<my-component></my-component>'
})
復制代碼

 

demo:

 

 

又例如,放在另外一個組件中

復制代碼
Vue.component('my-component', {
  template: '<p>我是被全局注冊的組件</p>'
})

Vue.component('other-component', {
  template: '<div>我是另一個全局組件:<my-component></my-component></div>'
})

new Vue({
  el: '#app',
  template: '<other-component></other-component>'
})
復制代碼

 

 

 

局部注冊

復制代碼
const child = {
  template: '<p>我是局部注冊的組件</p>'
}
/*
   通過components選項屬性進行局部注冊:
   components: {
    組件名稱[字符串]: 組件對象
  }
*/
new Vue({
  el: '#app',
template: '<my-component></my-component>', components: { 'my-component': child } })
復制代碼

demo:

 

 

通過組件組合(嵌套),構建大型的應用:

復制代碼
const child = {
  template: '<p>我是child組件</p>'
}

const father = {
  template: '<p>我是father組件,我包含了:<child-component></child-component></p>',
  components: {
    'child-component': child
  }
}

const grandFather = {
  template: '<p>我是grandFather組件,我包含了:<father-component></father-component></p>',
  components: {
    'father-component': father
  }
}
new Vue({ el: '#app', template: '<my-component></my-component>', components: { 'my-component': grandFather } })
復制代碼

 

demo:

 

通過new創建Vue實例,  全局注冊組件,局部注冊組件三者的使用頻率(場景)

1.new  Vue(),  盡管在Vue官方文檔上在相當多的例子中使用到了創建Vue實例這個操作,實際上它的使用可能並沒有你想象的那么平凡,在很多時候,它可能就只在掛載根實例的時候使用到

 

【這段話給寫react框架的人看】

對 new Vue()做個最簡單的描述!:在使用上類似於ReactDOM.render()...對,就是那個一開始你擼文檔的時候覺得好像很重要,但最后發現在整個APP中就只使用了一次的那個頂層API ....

 

2.全局注冊組件的使用也不太頻繁,首先來說,如果大量使用全局注冊的話,當然容易產生組件的命名沖突,這就意味着你在構建大型組件的時候,你不應該選擇用全局注冊構建具體的細顆粒度的組件(實際上即使是小型應用也不推薦啦~~~)

那么全局注冊組件會在哪里使用到呢?

2.1 有許多可全局復用的公共UI組件,你可能希望通過Vue.component({ ...})的方式全局注冊它

2.2 可以很簡單地添加第三方UI框架

【對比】大凡使用過一些UI框架的人,都知道一般情況下,使用這些UI組件的方式就是為元素添加類,像這樣:

 

<div class='UI框架中定義的類名'></div>

而在Vue中,你可以通過直接使用組件名稱去使用,就和react相關的UI框架一樣

 

3.大多數時候我們通過組件組合的方式構建頁面的時候,運用的是局部注冊,就像上文所提及的那樣

 

 

【注意點】

1.注冊組件必須發生在根實例初始化前

2.data是函數!

 

Vue中的props數據流

【寫給react學習者們看的】這跟react中設計非常類似,連名稱都相同,所以學過react的同學看這里應該會很輕松吧~~

 

這里要用到Vue的一個選項屬性——props;

通過在注冊組件中聲明需要使用的props,然后通過props中與模板中傳入的對應的屬性名,去取用這傳入的值

例子:

model部分:

復制代碼
Vue.component('my-component', {
  props: ['name', 'birthTime'],
  template: '<p>我叫:{{name}} 我出生於:{{birthTime}}</p>',
  created: function () {
    console.log('在created鈎子函數中被調用')
    console.log('我叫:', this.name)
    console.log('我出生於:', this.birthTime)
  }
})

new Vue({
  el: '#app'
})
復制代碼

 

HTML部分:

<div id='app'>
   <my-component name="彭湖灣" birth-time="1997 -06 - 06"></my-component>
<div id='app'>

 

demo:

 

 

你在注冊組件的時候通過props選項聲明了要取用的多個prop:

props: ['name', 'birthTime'],

 

然后在模板中通過屬性傳值的方式進行數據的注入:

<my-component name="彭湖灣" birth-time="1997 -06 - 06"></my-component>

 

再然后我們就可以在注冊組件的模板中使用到props選項中聲明的值了:

template: '<p>我叫:{{name}} 我出生於:{{birthTime}}</p>'

 

這里要注意幾個點:

props取值的方式

1.如果是在注冊組件的模板內部,直接通過prop的名稱取就OK了,例如

template: '<p>我叫:{{name}} 我出生於:{{birthTime}}</p>'

 

2.如果在注冊組件的其他地方,用this.prop的方式取用,例如

console.log('我叫:', this.name)

 

props內寫的是駝峰命名法,為什么在HTML(模板)中又用了短橫線命名法?

(camelCased VS kebab-case)

 

首先我們知道,Vue組件的模板可以放在兩個地方:

1. Vue組件的template選項屬性中,作為模板字符串

2.放在index.html中,作為HTML

 

這里的問題在於,HTML特性是不區分大小寫的

所以在Vue注冊組件中通用的駝峰命名法,顯然不適用於HTML中的Vue模板,所以

在HTML中寫入props屬性,必須寫短橫線命名法(就是把原來props屬性中的每個prop大寫換成小寫,並且在前面加個“-”)

 

總結:

1.在template選項屬性中,可以寫駝峰命名法,也可以寫短橫線命名法

2.在HTML(模板)中,只能寫短橫線命名法,不能寫駝峰

 

下面我就來證明以上兩點:

對1

復制代碼
Vue.component('my-component', {
  props: ['name', 'birthTime'],
  template: '<p>我叫:{{name}} 我出生於:{{birthTime}}</p>',
  created: function () {
    console.log('在created鈎子函數中被調用')
    console.log('我叫:', this.name)
    console.log('我出生於:', this.birthTime)
  }
})
new Vue({
  el: '#app',
  template: '<my-component name="彭湖灣" birthTime="1997 -06 - 06"></my-component>'
})
復制代碼

 

demo:

 

 

name和birthTime都正常顯示,這說明在template模板字符串中,是可以寫駝峰的

(請注意到一點:name既符合駝峰寫法也符合短橫線寫法,而birthTime只符合駝峰寫法)

 

JS部分

復制代碼
Vue.component('my-component', {
  props: ['name', 'birthTime'],
  template: '<p>我叫:{{name}} 我出生於:{{birthTime}}</p>',
  created: function () {
    console.log('在created鈎子函數中被調用')
    console.log('我叫:', this.name)
    console.log('我出生於:', this.birthTime)
  }
})

 

new Vue({
  el: '#app'
})
復制代碼

 

HTML(模板)部分

<div id='app'>
       <my-component name="彭湖灣" birthTime="1997 -06 - 06"></my-component>
</div>

  

demo:

 

 

這里有個有趣的現象:name對應的值可以正常地顯示,但!birthTime不能

這是因為上文提到的:

name既符合駝峰寫法也符合短橫線寫法,而birthTime只符合駝峰寫法,不符合HTML要求的短橫線寫法

 

使用v-bind的必要性:props不綁定的前提下,只能被作為字符串解析

復制代碼
Vue.component('my-component', {
  props: ['number'],
  template: '<p>檢測number的類型</p>',
  created: function () {
    console.log(typeof this.number)
  }
})

new Vue({
  el: '#app',
  template: '<my-component number="1"></my-component>'
})
復制代碼

 

demo:

 

 

 

number被檢測為字符串,這表明在不加v-bind綁定的情況下,props接受到的都是字符串,(注:如果被作為javacript,”1“會被解析為Number的1,而” ‘1’ “才會被解析為String的1)

 

沒錯,僅僅這一點就會讓我們非常為難,所以,我們需要使用v-bind:

當使用v-bind的時候,在模板中props將會被作為javascript解析

復制代碼
Vue.component('my-component', {
  props: ['number'],
  template: '<p>檢測number的類型</p>',
  created: function () {
    console.log(typeof this.number)
  }
})

new Vue({
  el: '#app',
  template: '<my-component v-bind:number="1"></my-component>'
})
復制代碼

 

demo:

 

 

這可能拓展我們對v-bind的認知

1.用v-bind一般是為了做數據的動態綁定

2.有時v-bind並不為了實現點1,只是純粹為了讓字符串內的內容被當作JS解析罷了

 

Vue的自定義事件

自定義事件是我非常喜歡的Vue的一大特性!!! 看文檔的第一眼我就對它情有獨鍾(雖然那一天離現在也就幾天而已的時間。。。)

 

先展示代碼和demo:

復制代碼
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-event')
    }
  }
}) 

new Vue({
  el: '#app',
  data: {
    totalCounter: 0
  },

  methods: {
    total_increment: function () {
      this.totalCounter += 1
    }
  }
})
復制代碼

 

模板HTML部分:

復制代碼
<div id='app'>
      <button>{{ totalCounter }}</button>
      </br>
      <button-counter v-on:increment-event='total_increment'></button-counter>
      <button-counter v-on:increment-event='total_increment'></button-counter>
</div>
復制代碼

 

demo:

下面兩個按鈕是兩個相同的子組件,並和上面那個按鈕共同組成了父組件。

當點擊任意一個子組件的按鈕,使其加1,都會使得父組件+1,最終:父組件的數值 = 子組件的數值之和

點擊下方左邊button

 

 點擊下方右邊button

 

 

 

 

自定義事件的原理

通過$emit(event)觸發一個自定義事件

然后通過$on(event,callback) 去執行對應的callback(回調函數)

(兩個event是字符串,且必須名稱相同)

但$on不能在父組件中監聽子組件拋出的事件,所以我們要做到這一點,可以在父組件的模板中使用到子組件的時候,直接用v-on綁定 (和$on作用效果一致) 就像上面那樣:

<button-counter v-on:increment-event='total_increment'></button-counter>

 

這樣一來,自定義事件的雛形就變得和原生事件一樣了 

即使這樣,上面的代碼可能還是有些難理解,我認為比較重要的是這一段:

 

increment: function () {
      this.counter += 1
      this.$emit('increment-event')
  }

 

因為我們對於事件的運用主要是:利用事件和函數綁定,從而在事件觸發的時候能執行相印的函數

所以! 對於自定義事件,我們要解決的問題就是,“這個事件在什么時候被觸發” 在上面的代碼中,觸發事件的時間(執行 this.$emit('increment-event')的時間)

就恰恰是執行this.counter += 1 的時候

 

 

自定義事件的作用

對此,我主要從兩點闡述我的觀點:(非官方文檔內容,自己思考的,覺得不對的可以指出):

自定義事件的作用1 ——“重新定義”了事件監聽機制的范圍

 

MDN是這樣描述DOM事件的:“DOM事件被發送以通知代碼已發生的有趣的事。每個事件都由基於Event接口的一個對象表示”

在我看來:當你使用事件的時候,你可能試圖做這樣一件事情: 在某一個特定的時間節點(或場景)做某個操作,例如調用一個函數。 而定位這個“時間節點”或“場景”的,就是事件。而我們對事件最喜歡做的事情,就是把事件和某個函數給綁定起來

 但我們可能一直都忽略了一個認知:我們認知范圍內的事件,好像只有原生事件呀?例如click(點擊),focus(聚焦),keydown(按鍵)

 

我們認知內的事件,難道只有這些個固定的范圍嗎?點擊是事件,按下鍵盤按鈕是事件。那么,我們能不能人為地定義一個事件呢? 例如上面的,我們通過代碼處理,讓"某個數據增加1"也作為一個事件,從而去觸發一個函數呢?

這,就是自定義事件的目的和魅力

 

自定義事件的作用2 ——使得父子組件權責明確

就讓我們看一下 父組件的這個模板吧,在這里,我們發現:

1.父組件不知道子組件究竟做了什么(increment事件觸發前的處理),同時也無需關心

2.父組件只要完成它的任務:在increment事件觸發的時候執行對應的函數就足夠了

對子組件反是

 

所以,從這個角度上說,自定義事件使得父子組件“權責明確”

 

【注意】官方文檔的示例可能容易制造這樣一種錯覺:自定義事件是以原生事件(如click)為基礎的,但實際上並不是這樣。

雖然自定義事件和原生事件息息相關,但自定義事件並不以原生事件的觸發為基礎的

 

Slot的使用

當你試圖使用slot的時候,你可能試圖做這樣一件事情:

用父組件動態地控制子組件的顯示的內容

復制代碼
Vue.component('son-component', {
  template: '<div><slot></slot></div>'
})

new Vue({
  el: '#app'
})
復制代碼

模板HTML:

復制代碼
<div id='app'>
      <p>這是slot的內容</p>
      <son-component>
        <p>你好,我是slot</p>
      </son-component>
</div>
復制代碼

 

demo:

 

【寫給react的同學看的】你可以把slot看作是個3.0版本的props.children

 

通俗的理解

在父組件模板中使用子組件的時候,如果在子組件里面嵌套了HTML的內容,它會以”props“的方式傳遞給子組件的模板,並被子組件中的slot接受,例如:

 

【一個不太專業的說法】

<son-component>
   <p>你好,我是slot</p>
</son-component>

等同於

<son-component  slot = '<p>你好,我是slot</p>'></son-component>

 

具名slot

為了使增強slot的用法,使父組件能夠更加靈活地控制子組件,Vue引入了具名slot

通過name屬性,可以把在父組件中控制子組件同時渲染不同的slot

復制代碼
Vue.component('son-component', {
  template: '<div><slot name="h1"></slot><slot name="button"></slot><slot name="a"></slot></div>'
})

new Vue({
  el: '#app'
})
復制代碼

 

HTML模板部分:

復制代碼
<div id='app'>

      <son-component>

        <h1 slot='h1' >我是標題</h1>

        <button slot='button'>我是按鈕</button>

        <a href='#' slot='a'>我是鏈接</a>

      </son-component>

 </div>
復制代碼

 demo:

 

【注意事項】

1.實際上,我覺得我這篇文章的語言有些過於羅嗦(其實很為難,因為說多了怕羅嗦——”太長不看“),說少了又怕不能完整地表達自己的意思,這是我權衡后的所做的結果

2.文中很多只為知識點服務,跟實際的項目構建存在很大差異,例如我把很多模板都放在template選項中,而實際上我們會使用Vue單文件組件來實現這些

【完】

 

悄悄問react,redux美不美,redux~美~誒不美~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM