新的一天又開始啦,你們也應該看到個人標題了,是滴,Vue基礎基本就到這裏了,我們回頭看看這一路,若是你都看了,而且都會寫了,那麼如今你就能夠本身寫一個Demo了,若是再瞭解一點路由,ajax請求(這裏是axios),那麼你就能夠準備面試前端了,哈哈,固然沒有這麼誇張,日後的路還很長,至少我們基礎都會了。html
這裏我們再溫習下以前講了哪些基礎知識:前端
《十五 ║Vue前篇:瞭解JS面向對象原理 & 學會嵌套字面量等4種函數定義 & this指向》vue
《十六 ║Vue前篇:ES6知識詳細說明 & 如何在JS中進行模塊化編程》ios
《十七 ║Vue基礎:第一次頁面引入Vue.js,瞭解 Vue實例是如何經過數據驅動來操做DOM的》git
《十八 ║Vue基礎: 學習了經常使用的十大指令並一一舉例,而後瞭解到了Vue的計算屬性,偵聽器,固然還有過濾器》github
《十九 ║Vue基礎: 經過樣式的動態綁定,進一步學習Vue是如何操做DOM的,而後深刻了解生命週期的八個階段,以及其中的職能》面試
一共是五篇,基本已經涵蓋了Vue的基礎知識,今天呢,再說完組件之後,明天就正式開始搭建本地腳手架,終於開始安裝軟件了[ 哭笑 ],我這幾天考慮了一下,在以後的講解中,可能須要兩個小項目的講解,第一個就是我如今本身用到的一個,你們其實也能夠看看 http://vue.blog.azlinli.com(買的服務器很差,首次加載慢),也不是啥隱私,這個是我以前練習的時候本身瞎玩的,只有首頁和詳情頁,用的數據就是我們在以前系列裏講到的.net core api,這個可能在本週,或者本週末說到,主要的就是把以前的講解給穿起來,而後再說下如何使用路由 v-router 和 ajax請求——axios,這樣我這個項目就說到這裏,而後第二個也是一個博客系統,用的是一套數據,只不過是用到了 Nuxt 框架了,基本結構又發生了變化,項目總體被封裝了,更趨於工程化,至於爲何要用到這個,就是由於它能夠解決 MVVM 先後端分離的 SEO 的老大難的問題,你們能夠先問問度娘,到時候都會說到滴。好啦,開始今天的基礎篇最後一章節 —— 深刻了解下組件。ajax
上篇文件咱們也說到了,註冊組件的其中一個辦法就是 Vue.component()
方法,先傳入一個自定義組件的名字,而後傳入這個組件的配置。編程
Vue.component('mycomponent',{ template: `<div>個人組件</div>`, data () { return { message: '老張的哲學' } } })
定義好後,咱們就能夠在Vue實例所定義的DOM元素內使用它(就是咱們在new vue的時候的 el 節點),這裏咱們的頁腳組件,全局組件,能夠在其餘地方使用axios
<div id="app"> <mycomponent></mycomponent> <my-component></my-component> </div> <script> //注意要在vue實例以前去定義,否則渲染頁面的時候,會報錯 // 定義一個名爲 footer-vue 的新組件 Vue.component('footer-vue', { template: ` <div id="footer-vue"> <p>2018 <a href="#">LZ's Blog</a> - Hosted by <a href="#" style="font-weight: bold">Coding Pages</a></p> <p> <a href="#">京ICP備00000000號</a> </p> </div> `, data () { return { message: 'hello world' } } }) var app = new Vue({ el: '#app',//沒錯,vue實例所定義的DOM元素就是這個,超過了這個區域,定義的組件會無效 data: { }, }) </script>
上邊,咱們定義的組件是一個全局的組件,也就是說若是咱們定義了多個 vue實例,咱們均可以使用這一個組件,這就是全局的,
固然,既然有全局的,咱們也有局部的(咱們對聯繫方式定義局部組件,表示只有在當前頁面的app元素內使用):
注意:全局的組件是 component,而 局部的是 components
var app = new Vue({ el: '#app', data: { }, components: { 'my-component': {//這個就是咱們局部組件的名字 在頁面內使用 <my-component></my-component> template: ` <ul class ="contact-list non-style-list"> <li> <b class ="twitter">TWITTER</b>: <a href="#">@laozhang</a> </li> <li> <b class ="weibo">微博</b>: <a href="#">@laozhang</a> </li> <li> <b class ="zhihu">知乎</b>: <a href="#" ></a> </li> <li> <b class ="github">GITHUB</b>: <a href="https://github.com/anjoy8">anjoy8</a> </li> <li> <b class ="email">EMAIL</b>: <a href="mailto:laozhang@azlinli.com">randypriv at azlinli</a> </li> </ul> `,
data () {
return {
message: 'hello world two'
}
},
directives:{//自定義局部指令,使用的時候,直接能夠 <my-component v-focus><my-component>
focus;{
inserted(el){
el.focus();
}
}
}
}
}
})
觀察一下上邊兩種寫法與的特色,你們應該也能說出來:
相同點:組件的模板只能有一個根節點,或者說根標籤只能有一個(第一個的根元素是 <div>,第二個根元素是 <ul>),若是定義成這樣就是不容許的,這裏是兩個根元素 <div> 和 <a>:
template: `<div>個人地盤聽個人,哈哈,只能在當前個人Vue實例中使用</div> <a>個人一個標籤</a>
`,
咱們看到在定義組件的時候和平時定義的 data 不同,這裏的定義必定要是一個函數,由於若是像Vue實例那樣,傳入一個對象,因爲JS中對象類型的變量實際上保存的是對象的引用
,因此當存在多個這樣的組件時,會共享數據,致使一個組件中數據的改變會引發其餘組件數據的改變。而使用一個返回對象的函數,每次使用組件都會建立一個新的對象,這樣就不會出現共享數據的問題來了。
Vue.component('footer-vue', { template: ` <div id="footer-vue"> <p> <a href="#">京ICP備00000000號</a> </p> </div> `, data :{//這是錯誤栗子 message: 'hello world'//咱們用普通屬性的方法 } })
若是咱們按照一個屬性的寫法的話,頁面會成功的報了一個這樣的錯誤,並且你們注意,這個錯誤是出如今哪裏的,沒錯就是掛載結束前,也就是說,和實例化數據沒影響,可是在掛載到頁面,頁面渲染的時候,出現了這個錯誤,因此你們在初學的時候,仍是要多瞭解下生命週期的概念。
注意:由於組件是可複用的 Vue 實例,因此它們與
new Vue
接收相同的選項,例如data
、computed
、watch
、methods
以及生命週期鉤子等。僅有的例外是像el
這樣根實例特有的選項。
Vue.extend()建立,而後由component來註冊,兩步
// extend 建立組件 var MyComponent = Vue.extend({ template: '<div>A custom component!</div>' }); // component註冊 組件 Vue.component('my-component', MyComponent);//使用到了 extend 建立的組件 var vm = new Vue({ el: '#example', data: { } })
兩種寫法沒有什麼太多的區別,基原本說
extend 是構造建立一個組件的語法器,你給它參數 他給你建立一個組件, 而後這個組件,你能夠做用到Vue.component 這個全局註冊方法裏, 也能夠在任意vue模板裏使用apple組件
var apple = Vue.extend({
….
})
Vue.component(‘apple’,apple)你能夠做用到vue實例或者某個組件中的components屬性中並在內部使用apple組件
new Vue({
components:{
apple:apple
}
})
可見上邊的定義過程比較繁瑣,也能夠不用每次都調用兩個,能夠直接用 Vue.component 建立 ,也能夠取組件 例以下
var apple = Vue.component(‘apple’)
<template id="temApp">//這裏是定義一個id, <div> <div id="nav"> <router-link to="/">Home</router-link> | <router-link to="/about">Form</router-link> | <router-link to="/Vuex">Vuex</router-link> </div> <router-view/> </div> </template> //在使用的時候,直接引用 #temApp Vue.component('footer-vue', { template:'#temApp', data () { return { message: 'hello world' } } })
你必定在開發中會遇到這樣的需求,就是一個banner的切換:
咱們這時候可使用動態組件,很容易實現這個需求,經過 Vue 的 <component>
元素加一個特殊的 is
特性來實現:
<!-- 組件會在 `currentTabComponent` 改變時改變 --> <component v-bind:is="currentTabComponent"></component>
在上述示例中,currentTabComponent
能夠包括
<div id="dynamic-component-demo" class="demo"> <button v-for="tab in tabs"//for 循環展現 banner v-bind:key="tab" v-bind:class="['tab-button', { active: currentTab === tab }]" //這裏語法錯誤,刪除本行註釋!綁定樣式,當前組件增長 active 樣式 v-on:click="currentTab = tab" >{{ tab }}</button> <!-- 組件的使用 經過currentTabComponent 來動態展現是哪個組件 --> <component v-bind:is="currentTabComponent" //這裏語法錯誤,刪除本行註釋!經過 is 特性,來動態實現組件,核心 class="tab" ></component> </div> //定義三個組件,能夠好比是咱們的三個頁面, Vue.component('tab-home', { template: '<div>Home component</div>' //組件1,也就是頁面1 }) Vue.component('tab-posts', { template: '<div>Posts component</div>' //組件2,頁面2 }) Vue.component('tab-archive', { template: '<div>Archive component</div>' //組件3,頁面3 }) new Vue({ el: '#dynamic-component-demo', data: { currentTab: 'Home',//當前banner名稱 tabs: ['Home', 'Posts', 'Archive']//定義三個banner }, computed: {//計算屬性,實時監控獲取固然banner的值,並返回到頁面 currentTabComponent: function () { return 'tab-' + this.currentTab.toLowerCase()//組件名稱拼串,必定要和上邊的三個組件名對應 } } })
注意:這裏可使用 <keep-alive> 來緩存固然組件的內容
<keep-alive>
<component :is="currentTabComponent"></component>
<keep-alive>
在 Vue 中,父子組件的關係能夠總結爲 prop
向下傳遞,事件
向上傳遞。父組件經過 prop
給子組件下發數據,子組件經過事件
給父組件發送消息,這裏我們先說下向下傳遞,經過屬性Props屬性來完成。
還記得以前我們說的,Vue 其實就是由許許多多個組件拼接而成,高效複用,相互之間能夠傳值,可是又不受影響,最多見的應用就是:組件 A 在它的模板中使用了組件 B。它們之間必然須要相互通訊:父組件可能要給子組件下發數據,子組件則可能要將它內部發生的事情告知父組件。你們第一次使用的時候可能會有點兒不舒服,可是使用熟練之後,就會發現真的駕輕就熟,因此我們就先看看組件是如何通信的。
首先你們還記得我們定義的頁腳組件麼,就是剛剛說到的。我們看到最下邊是備案號,如今想在備案號旁邊加上咱的暱稱」老張的哲學「,想一想很簡單嘛,想一想確定不能直接寫死數據吧,正好看到頁面內定義vue實例的時候,有這個屬性嘛,直接放到我們的頁腳組件裏,嗯就是這樣:
// 定義一個名爲 footer-vue 的新組件 Vue.component('footer-vue', { template: ` <div id="footer-vue"> <p>2018 <a href="#">LZ's Blog</a> - Hosted by <a href="#" style="font-weight: bold">Coding Pages</a></p> <p> <a href="#">京ICP備00000000號{{authorHtml}}</a> </p> </div> `, data () { return { message: 'hello world' } } })
而後滿懷開心的刷新頁面一看,額報錯了:
而後很熟練的必應翻譯了一下(這是一個反面教材,你們要好好學習英文,多看國外教程 [苦笑] ),獲得這樣的:屬性或方法 "authorHtml " 不該該是在實例上定義的, 而是在呈現過程當中引用的。經過初始化屬性, 確保此屬性在數據選項或基於類的組件中是被動的。說人話就是,這裏不能使用實例上定義的值,好吧,查閱資料發現,組件只能被動的接受父組件的傳參數,嗯因而乎咱們這樣寫:
<footer-vue :foo="author"></footer-vue>//在自定義的組件上,新增一個動態屬性,而後屬性的值 author 是父組件的,這個時候父組件就是已經把值給發過去了
這個時候,咱們就須要在子組件裏接收一下
// 定義一個名爲 footer-vue 的新組件 Vue.component('footer-vue', { template: ` <div id="footer-vue"> <p>2018 <a href="#">LZ's Blog</a> - Hosted by <a href="#" style="font-weight: bold">Coding Pages</a></p> <p> <a href="#">京ICP備00000000號{{foo}}</a>//這裏根據自身的props的參數來賦值 </p> </div> `, props: ['foo'],//這裏根據組件的props屬性,來被動接受組件傳遞來的參數 data () { return { message: 'hello world' } } })
刷新頁面,這時候就真正的成功了。
這裏咱們獲得的結果和上邊的是同樣的,直接是一個字符串結果,而不是一個變量屬性。
Vue.component('child', { // 聲明 props props: ['message'], // 就像 data 同樣,prop 也能夠在模板中使用 // 一樣也能夠在 vm 實例中經過 this.message 來使用 template: '<span>{{ message }}</span>' }) <child message="老張的哲學"></child>
上邊我們能夠看到,經過父子傳值,咱們把數據傳遞了過去,可是這個時候你須要思考,咱們只能傳遞數據 data 麼,答案是否認的,咱們還能夠傳遞方法事件 Function !很簡單,直接看代碼:
// 實例化 Vue var V = new Vue({ el: '#app', data: { now:'', lists:[ {id:1,name:'孫悟空'}, {id:2,name:'豬八戒'}, {id:3,name:'沙和尚'}, {id:4,name:'唐僧'}, {id:5,name:'小白龍'}, ], answer: 'Time is:' }, watch: { }, methods: { run(){ console.log(77) } }, mounted() { //this.now=this.dateFtt("yyyy-MM-dd hh:mm:ss",new Date());; } }) // 調用子組件,傳遞數據 lists 和事件 run <child :lists="lists" :run="run"> <template slot-scope="a"> {{a}} </template> </child> // 定義子組件 Vue.component('child',{ props:['lists','run'], methods:{ testRun(){ this.run() } }, template:` <div @click='testRun'> <ul> <li v-for="list in lists"> <slot :bbbbb="list"></slot> </li> </ul> </div> ` });
而後咱們點擊 div,就會觸發父組件事件:
是否是很簡單,父子傳值,方法事件也是特別經常使用的,你們要多學習學習。
注意:HTML 特性是不區分大小寫的。因此,當使用的不是字符串模板時,camelCase (駝峯式命名) 的 prop 須要轉換爲相對應的 kebab-case (短橫線分隔式命名),好比 如何上邊的foo 寫成了 fooPro ,那咱們定義屬性的時候,就應該寫 foo-pro
<footer-vue :foo-pro="author"></footer-vue>
若是咱們寫了 fooPro 這樣的寫法,掛載頁面的時候,就會警告,而且不會成功渲染
若是你使用字符串模板,則沒有這些限制。(字符串模板:指的是在組件選項裏用 template:"" 指定的模板,換句話說,寫在 js 中的 template:"" 中的就是字符串模板。)
這個其實很簡單,使用的也通常,不是不少,須要安裝一個庫 pubsub-js ,而後咱們就能夠在任何組件內來實現訂閱發佈,從而來實現通信了:
父組件 —— 訂閱消息(綁定事件監聽)
子/孫組件 —— 發佈消息(觸發事件)
咱們知道,父組件使用 prop 傳遞數據給子組件。但子組件怎麼跟父組件通訊呢?這個時候 Vue 的自定義事件系統就派得上用場了。
每一個 Vue 實例都實現了事件接口,即:
Vue 的事件系統與瀏覽器的 EventTarget API
有所不一樣。儘管它們的運行起來相似,可是 $on
和 $emit
並非addEventListener
和 dispatchEvent
的別名。
另外,父組件能夠在使用子組件的地方直接用 v-on
來監聽子組件觸發的事件。
//一、子組件內,有一個click,當點擊的時候 觸發 incrementCounter 方法
//二、方法被觸發之後,向父組件 發送一個信號廣播,並傳遞參數 counter,名字就是 increment。
//三、父組件經過監聽,來獲取到這個廣播信號 increment ,而後觸發 incrementTotal 方法
//四、incrementTotal 被觸發,獲取到參數 counter 值,執行相應的操做
<div id="counter-event-example"> <p>{{ total }}</p> <button-counter v-on:increment="incrementTotal"></button-counter>//三、父組件經過監聽,來獲取到這個廣播信號 increment ,而後觸發 incrementTotal 方法 </div> Vue.component('button-counter', { template: '<button v-on:click="incrementCounter">{{ counter }}</button>',//一、子組件內,有一個click,當點擊的時候 觸發 incrementCounter 方法 data: function () { return { counter: 0 } }, methods: { incrementCounter: function () { this.counter += 1 this.$emit('increment',this.counter)//二、方法被觸發之後,向父組件 發送一個信號廣播,並傳遞參數 counter,名字就是 increment。 } },
mounted(){//經常使用,掛載完成後執行
this.incrementCounter();
} }) new Vue({ el: '#counter-event-example', data: { total: 0 }, methods: { incrementTotal(counter) {//四、incrementTotal 被觸發,獲取到參數 counter 值,執行相應的操做 this.total = counter } } })
上邊註釋的已經很清楚,就好像冒泡同樣的,往上走,和 父傳子,正好相反。
若是你這個看懂了,能夠把上一節中的,經過屬性傳遞方法的那種操做給改一下,改爲咱們的自定義事件的方式:
<custom-input @run="run"></custom-input> <br/> {{something}} Vue.component('custom-input', { template: '<input type="text" @input="updateValue($event.target.value)"/>', methods:{ updateValue:function(value){ this.$emit('run', value) } } }) var vm = new Vue({ el: '#app', data: { something:'' }, methods:{ run(value){ this.something=value } } })
自定義事件能夠用來建立自定義的表單輸入組件,使用 v-model 來進行數據雙向綁定。要牢記:
<input v-model="something">
這不過是如下示例的語法糖:
<input v-bind:value="something" v-on:input="something = $event.target.value">
因此在組件中使用時,它至關於下面的簡寫:
<custom-input v-bind:value="something" v-on:input="something = arguments[0]"> </custom-input>
<div id="app"> <custom-input v-model="something"></custom-input> <br/> {{something}} </div> // 註冊 Vue.component('custom-input', { props:['something'], template: '<input type="text" v-bind:value="something" v-on:input="updateValue($event.target.value)"/>', methods:{ updateValue:function(value){ this.$emit('input', value) } } }) var vm = new Vue({ el: '#app', data: { something:'' } })
2019-06-27 更新:
<div id="app"> <child-component></child-component> </div> <script> Vue.component('child-component',{ template:` <div>Hello,World!</div> ` }) let vm = new Vue({ el:'#app', data:{ } }) </script>
Vue.component('child-component',{ template:` <div> <h4>Header.vue</h4> <slot name="girl"></slot> <div style="height:1px;background-color:red;"></div> <slot name="boy"></slot> <div style="height:1px;background-color:red;"></div> <slot></slot> </div> ` })
<child-component>
<template slot="girl">
<p style="color:red;">漂亮、美麗、購物、逛街</p>
</template>
<template slot="boy">
<strong>帥氣、才實</strong>
</template>
<div>
<a href='#' >
我是一個沒有表情的 a 標籤
</a>
</div>
</child-component>
<child-component>
<template slot="boy">
<strong>帥氣、才實</strong>
</template>
<div>
<div >
我這裏須要一個div就行
</div>
</div>
</child-component>
Vue.component('child',{ props:['lists'], template:` <div> <ul> <li v-for="list in lists"> <slot :scope="list"></slot> </li> </ul> </div> ` });
var V = new Vue({ el: '#app', data: { lists:[ {id:1,name:'孫悟空'}, {id:2,name:'豬八戒'}, {id:3,name:'沙和尚'}, {id:4,name:'唐僧'}, {id:5,name:'小白龍'}, ] }, })
<div id="app"> <child :lists="lists"> <template slot-scope="a"> <div v-if='a.scope.id==1'>你好:<span>{{a.scope.name}}</span></div> <div v-else>{{a.scope.name}}</div> </template> </child> </div>
2018-09-14 更新:
若是咱們定義了一個組件 O,須要在 A、B、C三個頁面使用,在O中有一部分是三個子組件相同的,有一部分是各自的不一樣的,這個時候咱們就可使用 slot 分發;
內容分發的做用,就是爲了組件的靈活性,在一個模板中,能夠供調用者自定義部分
//聲明模板 <template id="mysolt"> <div> <h3>個人公共的相同的內容</h3> <slot name="s1"></slot> </div> </template> //定義組件 Vue.component('my-solt', { template:'#mysolt', data () { return { message: 'hello world' } } }) //在a頁面 <body> <div id="app"> <my-solt> <ul slot="s1"> <li>a</li> <li>a</li> <li>a</li> </ul> </my-solt> </div> </body> //在b頁面 <body> <div id="app"> <my-solt> <ul slot="s1"> <p>b</p> <p>b</p> <p>b</p> </ul> </my-solt> </div> </body> //在c頁面 <body> <div id="app"> <my-solt> <ul slot="s1"> <div>ccc</div> </ul> </my-solt> </div> </body>
在使用組件時,咱們經常要像這樣組合它們:
<app> <app-header></app-header> <app-footer></app-footer> </app>
注意兩點:
<app>
組件不知道它會收到什麼內容。這是由使用 <app>
的父組件決定的。<app>
組件極可能有它本身的模板。爲了讓組件能夠組合,咱們須要一種方式來混合父組件的內容與子組件本身的模板。使用特殊的 <slot>
元素做爲原始內容的插槽。
一個常見錯誤是試圖在父組件模板內將一個指令綁定到子組件的屬性/方法:
<!-- 無效 --> <child-component v-show="someChildProperty"></child-component>
正確作法:
Vue.component('child-component', { // 有效,由於是在正確的做用域內 template: '<div v-show="someChildProperty">Child</div>', data: function () { return { someChildProperty: true } } })
假定 my-component 組件有以下模板:
<div>
<h2>我是子組件的標題</h2>
<slot>
只有在沒有要分發的內容時纔會顯示。
</slot>
</div>
父組件模板:
<div> <h1>我是父組件的標題</h1> <my-component> <p>這是一些初始內容</p> <p>這是更多的初始內容</p> </my-component> </div>
渲染結果:
今天簡單的說了下關於組件的一些問題,由於事件的問題,尚未說完,還在進一步整理當中,你們能夠之後進一步的瀏覽本博文,經過組件的學習,你們在Vue開發的道路上又進了一步,好啦,關於Vue的基礎知識就是這麼多了,明天開始進入代碼練習啦~~