全局事件總線原理圖
通信方式 props
父子之間傳遞
父向子傳遞屬性,通過屬性把數據交給子組件
子向父傳遞數據,子組件通過調用父組件的行為函數,把數據當參數交給父組件
通信方式 全局事件總線(自定義事件是全局事件總線的基礎)
vm對象和組件對象的關系
vm的原型對象 === 組件對象的原型對象的原型對象
本身自定義事件可以完成子父之間的傳遞,因為父組件中可以看到子組件,可以為子組件綁定事件,子組件中可以觸發事件
但是如果不是子向父,那么其余的就沒辦法了,因為兩個組件互相是看不到的,沒辦法再其中一個給另外一個綁定事件
此時我們可以借助中間人,也就是他們都可以同時看到的一個人,就是全局事件總線(所有的組件對象都能看到)
在接收數據的組件中,獲取總線綁定事件
在發送數據的組件中,獲取總線觸發事件
全局事件總線說到底就是個對象,我們通常就是用vm對象作為全局事件總線使用
把vm對象添加到Vue原型對象 就形成全局事件總線(vm)
在main.js中設置全局事件總線
// new Vue()實例化了一個Vue的實例化對象
//因為只有組件對象或者Vue的實例化對象才能調用$on和$emit
//想要成為事件總線的條件:
//1、所有的組件對象必須都能看得到這個總線對象,因此我們把這個對象放在了Vue原型 //2、這個事件總線對象必須能調用$on和$emit方法(總線對象必須是Vue的實例化對象或者是組件對象)
new Vue({
beforeCreate(){
Vue.prototype.$bus = this
},
el:'#root',
render: h => h(App)
})
在父組件app中綁定事件
mounted(){
this.$bus.$on('updateOne',this.updateOne)
//跟新事件
this.$bus.$on("addTodo",this.addTodo)
//刪除一個事件
this.$bus.$on("deleteOne",this.deleteOne)
//刪除全部事件
this.$bus.$on("deleteAll",this.deleteAll)
//全選框,跟新所有的li狀態
this.$bus.$on("updateAll", this.updateAll)
},
在子組件中觸發事件,並且傳遞數據給父組件
addT(){
//回車之后干活
let {content} = this
if(content.trim()){
let id = Date.now()
let isOver = false
let todo = {
id,
content,
isOver
}
this.$bus.$emit('addTodo',todo)
二, 具名插槽,默認插槽, 作用域插槽
通信方式 slot插槽 一個組件會多次使用,但是不同場景下又有少部分結構數據會發生變化,(當然可以用不同的子組件) 那么就得通過父組件告訴子組件變化的內容是什么,此時就要用到這個插槽 子組件當中<slot></sloat>其實就是占位用的,讓父元素給它填充內容,可以帶標簽
作用域插槽
子組件的slot可以通過 屬性傳遞值給父組件,然后父組件可以根據不同需求改變這個slot內部的顯示結構
把子組件的值,傳給父組件固定的區域進行操作
默認不能有其它插槽,如果要有其它插槽,必須設置為具名插槽
App組件
<template> <div> <Child> <template slot="btn"> <button>點我</button> </template> <template slot="aa"> <a href="http://www.atguigu.com">點我去學習</a> </template> <template slot="ss"> <span>嘿嘿</span> </template> </Child> <Child> <template> <h2>我愛你</h2> </template> </Child> <Child2 :todos="todos"> <!-- 決定子組件內部的結構,比如假設isOver為true,那么內容需要包含在span當中並且內容前面帶√ --> <!-- slot-scope會把子組件傳遞過來的數據,放在一個對象當中作為屬性 --> <!-- 什么時候用作用域插槽: 當碰到數據是在子組件當中展示的,而結構是由父組件決定的,此時必然使用作用域插槽 --> <template slot-scope="scopePerps"> <span v-if="scopePerps.todo.isOver"> √ </span> {{scopePerps.todo.content}} </template> </Child2> </div> </template> <script> import Child from '@/components/Child' import Child2 from '@/components/Child2' export default { name: '', components:{ Child, Child2 }, data(){ return { todos:[ {id:1,content:'抽煙',isOver:false}, {id:2,content:'喝酒',isOver:true}, {id:3,content:'燙頭',isOver:false} ] } }, } </script> <style scoped> </style>
Child1 組件
<template> <div> <h1>我愛你</h1> <!-- slot占位置,結構不確定,需要父組件傳遞 --> <slot name="btn"></slot> <slot name="aa"></slot> <slot name="ss"></slot> <!-- 默認插槽,沒有名字的slot --> <slot name="btn"></slot> </div> </template> <script> export default { data() { return { }; }, }; </script> <style scoped > </style>
Childer2組件
<template> <div> <h1>我愛你趙麗穎</h1> <ul> <li v-for="(todo, index) in todos" :key="todo.id"> <slot :todo="todo"> <!-- 這個:todo="todo" 是作用域插槽的一部分,會傳遞給父組件當中固定的某個區域 --> <!-- {{todo.content}} --> </slot> </li> </ul> </div> </template> <script> export default { name: '', props:['todos'] } </script> <style scoped> </style>
二, 利用全局事件總線傳遞數據, 組件內發送請求,獲取數據
main.js中設置全局事件總線
// 引入vue
import Vue from 'vue'
//引入app
import APP from '@/APP'
Vue.config.productionTip=false
new Vue({
beforeCreate() { // 設置事件總線 Vue.prototype.$bus=this },
el:"#root",
render:h=>h(APP)
})
app組件
<template> <div > <Header></Header> <Main></Main> </div> </template> <script> import Header from '@/components/Header' import Main from '@/components/Main' export default { data() { return {}; }, components:{ Header, Main, } }; </script> <style scoped ></style>
header組件
<template> <section class="jumbotron"> <h3 class="jumbotron-heading">Search Github Users</h3> <div> <input type="text" placeholder="enter the name you search" v-model="content" /> <button @click="sendSj">Search</button> </div> </section> </template> <script> export default { //在該組件中,需要將輸入的數據傳遞給main,讓main組件去發送請求,獲取數據 //兄弟關系,需要用事件總線傳遞 name:"Header", data() { return { content:'', }; }, methods:{ sendSj(){ this.$bus.$emit('addO', this.content) } } }; </script> <style scoped></style>
main組件, 安裝axios, 引入axios
<template> <div class="row"> <h2 v-if="isFirst">歡迎光臨,請輸入關鍵字進行搜索</h2> <h2 v-else-if="isLoading">正在搜索中,請稍后</h2> <h2 v-else-if="errMsg">請求出錯:{{ errMsg }}</h2> <div v-else class="card" v-for="(user, index) in users" :key="user.id"> <a :href="user.url" target="_blank"> <img :src="user.imgUrl" style="width: 100px" /> </a> <p class="card-text">{{user.name}}</p> </div> </div> </template> <script> // 引入axios import axios from "axios"; export default { //發送請求前,isFirst為fasle, isLoading為true //發送請求后,isLoading為false, name: "Main", data() { return { users: [], isFirst: true, isLoading: false, errMsg: "", }; }, //頁面加載后獲取header的數據 mounted() { this.$bus.$on("addO", this.addO); // this.addO() console.log(111) }, // #region // methods:{ // addO(q){ // //發送請求前,在搜索中 // this.isFirst= false // this.isLoading= true // axios({ // url:"https://api.github.com/search/users", // method:'get', // params:{ // q // } // }).then((res)=>{ // let newArray= res.data.items.map(item=>({name:item.login, url:item.url, imgUrl: item.avatar_url, id:item.id})) // // console.log(newArray) // this.users = newArray // //發送請求后,更改狀態 // this.isLoading = false // }).catch((error)=>{ // console.log(error.message) // //發送請求有誤,更改狀態 // this.errMsg = error.message // this.isLoading = false // }) // } // } // #endregion methods: { // 接口2: https://api.github.com/search/users?q=aa //收到header組件的數據,並且去發送請求,獲取數據 async addO(content) { //發送請求前,在搜索中 this.isFirst= false this.isLoading= true try { let result = await axios({ url: "https://api.github.com/search/users", method: "get", params: { q: content, }, }); //map計算一個新數組 let newAarry= result.data.items.map(item =>({name:item.login, url:item.url, imgUrl: item.avatar_url, id:item.id})) this.users = newAarry //發送請求后,更改狀態 this.isLoading = false } catch (error) { console.log(error.message) //發送請求有誤,更改狀態 this.errMsg = error.message this.isLoading = false } }, }, }; </script> <style scoped> .card { float: left; width: 33.333%; padding: 0.75rem; margin-bottom: 2rem; border: 1px solid #efefef; text-align: center; } .card > img { margin-bottom: 0.75rem; border-radius: 100px; } .card-text { font-size: 85%; } </style>
注;async, awiat 是es8語法,需要用它babel- polyfill來轉換,安裝;npm install --save @babel/polyfill
webpackde.config.js的 entry配置, entry: ["@babel/polyfill", "./src/main.js"], 在任何組件中引入async和await, 都不會報錯了
報錯情況