Vue倔強青銅-入門和元件化通訊G
作為前端最容易上手的框架,Vue入門其實沒啥說的,我放一段清單的程式碼,大家能看懂就說明能上手了
<template> <div id="app"> <h1>{{title}}</h1> <div> <input type="text" v-model="val"> <button @click="add">新增</button> <button @click="clear">清空</button> </div> <ul> <li v-for="todo in todos" :key="todo.title" :class="{done:todo.done}"> <input type="checkbox" v-model="todo.done"> {{todo.title}} </li> </ul> <p>{{active}} / {{all}}</p> </div> </template> <script> export default { name: "app", data() { return { title: "蝸牛老溼很騷氣", val: "", todos: [] }; }, mounted() { const todos = localStorage.getItem("todos"); if (todos) { this.todos = JSON.parse(todos); } else { this.todos = [ { title: "吃飯", done: true }, { title: "睡覺", done: false }, { title: "寫程式碼", done: false } ]; } }, computed: { active() { return this.todos.filter(v => !v.done).length; }, all() { return this.todos.length; } }, watch: { todos: { deep: true, handler(todos) { localStorage.setItem("todos", JSON.stringify(todos)); } } }, methods: { clear() { this.todos = this.todos.filter(v => !v.done); }, add() { if (this.val) { this.todos.push({ title: this.val, done: false }); this.val = ""; } } } }; </script> <style> li.done { color: red; text-decoration: line-through; } </style> 複製程式碼
大概包含的內容如下,對這個例子熟悉後,才是我們的正文,如果上面程式碼有沒看懂的地方,快去Vuejs官網回顧一下吧
- 變數渲染
- 迴圈渲染
- class渲染
- 計算屬性
- 監聽器
- 繫結事件
- 生命週期
元件化
Vue單檔案元件。Vue的單檔案元件相信大家都體驗過,通過vue-cli初始化的專案自動就支援了,新建Child1.vue
<template> <div>Child1</div> </template> <script> export default { } </script> 複製程式碼
App中使用
<template> <div id="app"> <Child1></Child1> </div> </template> <script> import Child1 from '@/components/Child1' export default { name: "app", components:{Child1} } </script> 複製程式碼
下面就迎來了第一個常見問題, 如果元件多了,他們之間如何通訊嘮嗑呢,不要小看這個問題,騷氣的面試官,比如我,就經常喜歡問,下面我們來演示一下Vue元件之間常用的通訊收件
1. 父傳子元件
父子元件傳值,最簡單的就是通過props傳遞,話不多說看程式碼
// App <template> <div id="app"> <Child1 :title="title1"></Child1> </div> </template> <script> import Child1 from '@/components/Child1' export default { name: "app", data(){ return { title1:'我是你爸爸' } }, components:{Child1} } </script> 複製程式碼
// Child1 <template> <div> <h2>Child2</h2> <div>{{title}}</div> </div> </template> <script> export default { props:['title'] } </script> 複製程式碼
2. 子傳父
Vue更推薦單向資料流,所以子元件像修改傳遞的資料,需要通知父元件來修改,使用$emit觸發父元素傳遞的事件
<template> <div id="app"> <h2>Parent</h2> <h3>{{msg}}</h3> <Child1 :title="title1" @getmsg="getmsg"></Child1> </div> </template> <script> import Child1 from '@/components/Child1' export default { name: "app", data(){ return { msg:'', title1:'我是你爸爸' } }, methods:{ getmsg(msg){ console.log(msg) this.msg = msg } }, components:{Child1} } </script> <style> div{ border:1px red solid; padding:20px; } </style> 複製程式碼
// child1 <template> <div> <h2>Child2</h2> <p>{{title}}</p> <button @click="toParent">傳遞到父元素</button> </div> </template> <script> export default { props:['title'], methods:{ toParent(){ this.$emit('getmsg','爸爸,我知道錯了') } } } </script> 複製程式碼
3. 兄弟元件
兄弟元件不能直接通訊,只需要父元素搭個橋即可,大家自己體驗即可
4. 祖先後代 provide & inject
props一層層傳遞,爺爺給孫子還好,如果嵌套了五六層還這麼寫,感覺自己就是一個沙雕,所以這裡介紹一個 稍微冷門的API,provice/inject,類似React中的上下文,專門用來跨層級提供資料
現在很多開源庫都使用這個api來做跨層級的資料共享,比如element-ui的 tabs 和 select
<script> import Child1 from '@/components/Child1' export default { name: "app", provide:{ woniu:'我是蝸牛' }, components:{Child1} } </script> <style> 複製程式碼
// 子孫元素 <template> <div> <h3>Grandson1</h3> <p> 祖先元素提供的資料 : {{woniu}} </p> </div> </template> <script> export default { inject:['woniu'] } </script> 複製程式碼
但是provider和inject不是響應式的,如果子孫元素想通知祖先,就需要hack一下,Vue1中有dispatch和boardcast兩個方法,但是vue2中被幹掉了,我們自己可以模擬一下
原理就是可以通過this. children來獲取父元件和子元件,我們遞迴一下就可以了
5. dispatch
遞迴獲取$parent即可 比較簡單
<button @click="dispatch('dispatch','哈嘍 我是GrandGrandChild1')">dispatch</button> 複製程式碼
methods: { dispatch(eventName, data) { let parent = this.$parent // 查詢父元素 while (parent ) { if (parent) { // 父元素用$emit觸發 parent.$emit(eventName,data) // 遞迴查詢父元素 parent = parent.$parent }else{ break } } } } 複製程式碼
注意只向上傳遞了,並沒有影響別的元素
6. boardcast
和dispatch類似,遞迴獲取$children 來向所有子元素廣播
<button @click="$boardcast('boardcast','我是Child1')">廣播子元素</button> 複製程式碼
function boardcast(eventName, data){ this.$children.forEach(child => { // 子元素觸發$emit child.$emit(eventName, data) if(child.$children.length){ // 遞迴呼叫,通過call修改this指向 child boardcast.call(child, eventName, data) } }); } { methods: { $boardcast(eventName, data) { boardcast.call(this,eventName,data) } } } 複製程式碼
7. 全域性掛載dispatch和boardcast
想用的時候,需要自己元件內部定理dispatch和boardcast太煩了,我們掛載到Vue的原型鏈上,豈不是很high,找到main.js
Vue.prototype.$dispatch =function(eventName, data) { let parent = this.$parent // 查詢父元素 while (parent ) { if (parent) { // 父元素用$emit觸發 parent.$emit(eventName,data) // 遞迴查詢父元素 parent = parent.$parent }else{ break } } } Vue.prototype.$boardcast = function(eventName, data){ boardcast.call(this,eventName,data) } function boardcast(eventName, data){ this.$children.forEach(child => { // 子元素觸發$emit child.$emit(eventName, data) if(child.$children.length){ // 遞迴呼叫,通過call修改this指向 child boardcast.call(child, eventName, data) } }); } 複製程式碼
這樣元件裡直接就可以用了 無壓力
8. 沒啥關係的元件:event-bus
如果倆元件沒啥關係呢,我們只能使用訂閱釋出模式來做,並且掛載到Vue.protytype之上,我們來試試,我們稱呼這種機制為匯流排機制,也就是喜聞樂見的 event-bus
class Bus{ constructor(){ // { //eventName1:[fn1,fn2], //eventName2:[fn3,fn4], // } this.callbacks = {} } $on(name,fn){ this.callbacks[name] = this.callbacks[name] || [] this.callbacks[name].push(fn) } $emit(name,args){ if(this.callbacks[name]){ // 存在 遍歷所有callback this.callbacks[name].forEach(cb=> cb(args)) } } } Vue.prototype.$bus = new Bus() 複製程式碼
使用
// 使用 eventBus(){ this.$bus.$emit('event-bus','測試eventBus') } // 監聽 this.$bus.$on("event-bus",msg=>{ this.msg = '接收event-bus訊息:'+ msg }) 複製程式碼
9. vuex
總結了那麼多,其實最佳實踐就是vuex,這個後面再專門寫文章學習吧
看完這個文章,Vue元件化通訊應該就難不住你了,也恭喜你度過青銅,正式邁入Vue秩序白銀級別