Vue學習筆記
一、非父子通信-event bus 事件總線
作用:非父子組件之間,進行簡易消息傳遞。(復雜場景用----Vuex)
使用步驟:
- 創建一個都能訪問的事件總線 (空Vue實例)-----utils/EventBus.js
// 1.創建一個都能訪問你的時間總線(空閑的Vue實例)
import Vue from "vue"const Bus = new Vue()export default Bus
- A組件(接受方),監聽Bus的 $on事件
<script>
import Bus from '../utils/EventBus'
export default {created(){// 2.在A組件(接收方),進行監聽Bus的事件(訂閱消息)Bus.$on('sendMsg',(msg) => {consloe.log(msg)})}
}
</script>
- B組件(發送方),觸發Bus實例的事件
<script>
//導入事件總線
import Bus from '../utils/EventBus'
export default {methods:{clickSend(){// 3.B組件(發送方)觸發事件的方式來傳遞參數(發布消息)Bus.$emit('sendMsg','今日天氣不錯')}}
}
</script>
二、非父子通信(拓展)----provide&indect(跨層級共享數據)
provide&indect作用:跨層級共享數據
語法:
- 父組件 provide提供數據
export default {provide () {return {// 普通類型【非響應式】color: this.color, // 復雜類型【響應式】userInfo: this.userInfo, }}
}
- 子/孫組件 inject獲取數據
export default {inject: ['color','userInfo'],created () {console.log(this.color, this.userInfo)}
}
注意:
- provide提供的簡單類型的數據不是響應式的,復雜類型數據是響應式。(推薦提供復雜類型數據)
- 子/孫組件通過inject獲取的數據,不能在自身組件內修改
三、子組件與父組件之間的雙向綁定
3.1、原理介紹
v-model本質上是一個 語法糖(語法的簡寫)。例如應用在輸入框上,就是value屬性 和 input事件 的合寫(不同的表單元素會有所不同)
作用:提供數據的雙向綁定
- 數據發生改變,頁面就會自動變 :value(v-bind:value=‘實例中的數據’)
- 頁面輸入改變,數據會自動變化 @input
注意:$event 用于在模板中,獲取事件的形參
下面兩種寫法等價:
<template><div class="app">1:<input v-model="msg1" type="text"/><br><!-- 模版中獲取事件的形參 -> $event獲取 -->2:<input :value="msg2" @input=" msg2 = $event.target.value" type="text"/></div>
</template>
不同的表單元素, v-model在底層的處理機制是不一樣的。比如給checkbox使用v-model
底層處理的是 checked屬性和change事件。
3.2、表單類組件封裝&v-model簡化代碼
- 表單類組件封裝—>實現了子組件和父組件數據的雙向綁定
- 父傳子:數據 應該是父組件props傳遞過來的,v-model拆解綁定數據
- 子傳父:監聽輸入,子傳父值給父組件修改
App.vue
<template><div class="app"><!-- $event就可以拿到當前子傳父的形參 --><BaseSelect :cityId="selectId" @changeId=" selectId = $event"></BaseSelect></div>
</template><script>
import BaseSelect from './components/BaseSelect.vue'
export default {data(){return{selectId: '102'}},components:{BaseSelect:BaseSelect}
}
BaseSelect
<template><div><!-- 父傳子 ::value="cityId" --><select :value="cityId" @change="handlerChange" ><option value="101">北京</option><option value="102">上海</option><option value="103">武漢</option><option value="104">廣州</option><option value="105">深圳</option></select></div>
</template><script>
export default {props:{cityId: String },methods:{handlerChange(e){//e.target.value 獲取下拉菜單的值// alert(e.target.value)this.$emit('changeId',e.target.value)}}}
</script><style>
</style>
注意:不是自己的數據不能用v-model實現雙向綁定,只能通過將v-model拆解,利用父子通信的手段進行修改。’
- 父組件v-model簡化代碼,實現子組件和父組件的雙向綁定
相比與上述代碼并沒有大致區別,只是將子組件的一些名字替換為value與input,從而在父組件中利用v-model實現數據綁定
v-model其實就是 :value和@input事件的簡寫
步驟:
- 子組件:props通過value接收數據,事件觸發 input
- 父組件:v-model直接綁定數據
輸入框子組件通信
App.vue
<template><div class="app"><!-- <BaseSelect :value="inputValue" @input="inputValue = $event"></BaseSelect> --><!-- :value="inputValue" @input="inputValue = $event" 等價于v-model="inputValue" --><BaseSelect v-model="inputValue"></BaseSelect></div>
</template><script>
import BaseSelect from './components/BaseSelect.vue'
export default {data(){return{inputValue: 'i love china'}},components:{BaseSelect:BaseSelect}}
</script><style></style>
BaseSelect
<template><div><!-- 父傳子 ::value="cityId" --><input type="text" :value="value" @change="handleChange"></div>
</template><script>
export default {props:{value: String },methods:{handleChange(e){//e.target.value 獲取下拉菜單的值// alert(e.target.value)this.$emit('input',e.target.value)}}}
</script><style>
</style>
四、.sync修飾符(重要)
作用:可以實現子組件與父組件的雙向綁定,簡化代碼
特點:prop屬性名,可以自定義,非固定為value(用v-model)
場景:封裝彈框類的基礎組件,visible屬性 true顯示 false隱藏
本質:就是 :屬性名和@update:屬性名 合寫
子父組件的使用方式
彈出框數據
App.vue
<template><div class="app"><button @click=" isShow = true ">退出按鈕</button><!-- :visible.sync 等價于 :visible 和@update:visible整合 --><!-- <BaseDialog :visible.sync="isShow"></BaseDialog> --><!-- $event用來接收 this.$emit('update:visible',false)的參數 --><BaseDialog :visible="isShow" @update:visible=" isShow = $event"></BaseDialog></div>
</template><script>
import BaseDialog from './components/BaseDialog.vue'
export default {data() {return {isShow: false,}},components: {BaseDialog,}
}
</script><style>
</style>
BaseDialog
<template><div class="base-dialog-wrap" v-show="visible"><div class="base-dialog"><div class="title"><h3>溫馨提示:</h3><button class="close" @click="close">x</button></div><div class="content"><p>你確認要退出本系統么?</p></div><div class="footer"><button @click="close">確認</button><button @click="close">取消</button></div></div></div>
</template><script>
export default {props: {visible: Boolean,},methods:{close(){this.$emit('update:visible',false)}}
}
</script><style scoped>
.base-dialog-wrap {width: 300px;height: 200px;box-shadow: 2px 2px 2px 2px #ccc;position: fixed;left: 50%;top: 50%;transform: translate(-50%, -50%);padding: 0 10px;
}
.base-dialog .title {display: flex;justify-content: space-between;align-items: center;border-bottom: 2px solid #000;
}
.base-dialog .content {margin-top: 38px;
}
.base-dialog .title .close {width: 20px;height: 20px;cursor: pointer;line-height: 10px;
}
.footer {display: flex;justify-content: flex-end;margin-top: 26px;
}
.footer button {width: 80px;height: 40px;
}
.footer button:nth-child(1) {margin-right: 10px;cursor: pointer;
}
</style>
五、ref和$ref
5.1、獲取dom
BaseChart
<template><div class="base-chart-box" ref="myCharts">子組件</div><!-- -->
</template><script>
// yarn add echarts 或者 npm i echarts
import * as echarts from 'echarts'
// import echarts from 'echarts'export default {mounted() {// 基于準備好的dom,初始化echarts實例// this.$refs.myCharts替代document.querySelector('.base-chart-box')查找范圍是當前頁面的盒子var myChart = echarts.init(this.$refs.myCharts)// 繪制圖表myChart.setOption({title: {text: 'ECharts 入門示例',},tooltip: {},xAxis: {data: ['襯衫', '羊毛衫', '雪紡衫', '褲子', '高跟鞋', '襪子'],},yAxis: {},series: [{name: '銷量',type: 'bar',data: [5, 20, 36, 10, 10, 20],},],})},
}
</script><style scoped>
.base-chart-box {width: 400px;height: 300px;border: 3px solid #000;border-radius: 6px;
}
</style>
5.2、獲取組件實例
App.vue
<template><div class="app"><h4>父組件 -- <button @click="getData">獲取組件實例</button></h4><BaseFromVue ref="fromVue"></BaseFromVue><div><button @click="getData">獲取數據</button><button @click="resetData">重置數據</button></div></div>
</template><script>
import BaseFromVue from './components/BaseFrom.vue'export default {components: {BaseFromVue :BaseFromVue},data(){return{user :{username : '',password : ''}}},methods: {getData(){var user =this.$refs.fromVue.getFromValue()// alert(user.username)this.user=user},resetData(){this.$refs.fromVue.resetFrom()}}
}
</script><style>
</style>
BaseFrom.vue
<template><div class="app"><div>賬號: <input v-model="username" type="text"></div><div>密碼: <input v-model="password" type="text"></div></div>
</template><script>
export default {data(){return{//定義數據username : '',password : ''}},methods:{//獲取到表單數據并返回getFromValue(){console.log("用戶名:"+this.username)return{username : this.username,password : this.password}},resetFrom(){this.username=''this.password=''}}
}
</script><style scoped>
.app {border: 2px solid #ccc;padding: 10px;
}
.app div{margin: 10px 0;
}
.app div button{margin-right: 8px;
}
</style>
六、Vue異步更新、$nextTick
需求:點擊編輯按鈕,顯示編輯框,并讓編輯框自動聚焦
this.isShowEdit = true //控制顯示this.$refs.inp.focus() //利用ref得到Dom聚焦
問題:“顯示后”,立即獲取焦點失敗
原因:Vue是異步更新Dom(提升性能)
解決方法:
$nextTick:等Dom更新后,才會觸發方法里的函數體
語法:this. $ nextTick
methods: {editFn() {// 顯示輸入框(異步dom更新)---this.$refs.inp獲取不到Domthis.isShowEdit = true //$nextTick()this.$nextTick(()=>{// 獲取焦點this.$refs.inp.focus() })//setTimeout等待的時間不精準 -------- 推薦使用 $nextTick()// setTimeout(() => {// this.$refs.inp.focus() // }, 100);} }