--- tags: Vue,第五章 --- # v-model原理 ![](https://hackmd.io/_uploads/H1Lczt-eT.png) ```javascript= <template> <div class="app"> <input type="text" v-model="msg1"> <br> <!-- v-model本質是語法糖 --> <input type="text" :value="msg2" @input="hInput"> </div> </template> <script> export default { data() { return { msg1: '', msg2: '', } }, methods: { hInput(e) { // 只要改動內容就會觸發事件對象 console.log(e.target.value); this.msg2 = e.target.value } } } </script> <style></style> ``` ```javascript= <template> <div class="app"> <input type="text" v-model="msg1"> <br> <!-- v-model本質是語法糖 --> <input type="text" :value="msg2" @input="msg2 = $event.target.value"> </div> </template> <script> export default { data() { return { msg1: '', msg2: '', } }, // methods: { // hInput(e) { // // 只要改動內容就會觸發事件對象 // console.log(e.target.value); // this.msg2 = e.target.value // } // } } </script> <style></style> ``` ## 封裝下拉框之父子通信 App.vue ```javascript= <template> <div class="app"> <base-select @changeCity="hChange" :cityId="selectId"></base-select> </div> </template> <script> import BaseSelect from './components/BaseSelect.vue' export default { data() { return { selectId: '102' } }, methods: { hChange(id) { this.selectId = id } }, components: { BaseSelect }, } </script> <style></style> ``` BaseSelect.vue ```javascript= <template> <div> <select @change="hChange" :value="cityId"> <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: { type: String, required: true } }, methods: { hChange(e) { console.log('用戶修改了'); this.$emit('changeCity',e.target.value) } } } </script> <style></style> ``` ## 封裝下拉框之v-model進階 App.vue ```javascript= <template> <div class="app"> <!-- <base-select @changeCity="hChange" :cityId="selectId"></base-select> --> <!-- <base-select @changeCity="selectId = $event" :cityId="selectId"></base-select> --> <!-- <base-select @input="selectId = $event" :value="selectId"></base-select> --> <base-select v-model="selectId"></base-select> </div> </template> <script> import BaseSelect from './components/BaseSelect.vue' export default { data() { return { selectId: '102' } }, // methods: { // hChange(id) { // this.selectId = id // } // }, components: { BaseSelect }, } </script> <style></style> ``` BaseSelect.vue ```javascript= <template> <div> <select @change="hChange" :value="value"> <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: { value: { type: String, required: true } }, methods: { hChange(e) { console.log('用戶修改了'); this.$emit('input',e.target.value) } } } </script> <style></style> ``` # .sync修飾符 App.vue ```javascript= <template> <div class="app"> <button @click="openDialog">退出按鈕</button> <!-- <base-dialog @close="isShow = $event" :visible="isShow"></base-dialog> --> <base-dialog :visible.sync="isShow"></base-dialog> <!-- .sync做了2件事 1.將數據傳給子組件 2.監聽一個事件,事件名為update:visible v-model做了2件事 1.將數據傳給子組件,數據名為value 2.監聽一個事件,事件名為input v-model的侷限性 1.數據名必須叫value 2.事件名必須叫input 3.一個標籤只能用1個v-model --> </div> </template> <script> import BaseDialog from './components/BaseDialog.vue' export default { data() { return { isShow: false } }, methods: { openDialog() { this.isShow = true } }, components: { BaseDialog }, } </script> <style></style> ``` BaseDialog.vue ```javascript= <template> <div v-show="visible"> <div> <div> <h3>溫馨提示</h3> <button @click="hClose">x</button> </div> <div> <p>你確認要退出本系統嗎?</p> </div> <div> <button>確認</button> <button>取消</button> </div> </div> </div> </template> <script> export default { props: { visible: { type: Boolean, required: true } }, methods: { hClose() { this.$emit('update:visible', false) // this.$emit('close', false) } } } </script> <style></style> ``` # ElementUI https://element.eleme.cn/#/zh-CN # ref和$refs ![](https://hackmd.io/_uploads/BJQMDh7gT.png) App.vue ```javascript= <template> <div> <div class="base-chart-box">這是搗亂的盒子</div> <base-chart></base-chart> </div> </template> <script> import BaseChart from './components/BaseChart.vue' export default { components: { BaseChart }, } </script> <style> .base-chart-box { width: 300px; height: 300px; text-align: center; line-height: 300px; } </style> ``` BaseChart.vue ```javascript= <template> <div ref="box" class="base-chart-box">子組件</div> </template> <script> import * as echarts from 'echarts' export default { mounted() { // 基于准备好的dom,初始化echarts实例 // const myChart = echarts.init(document.querySelector('.base-chart-box')); const myChart = echarts.init(this.$refs.box); // 使用刚指定的配置项和数据显示图表。 myChart.setOption({ title: { text: "Referer of a Website", subtext: "Fake Data", left: "center", }, tooltip: { trigger: "item", }, legend: { orient: "vertical", left: "left", }, series: [ { name: "消費帳單", type: "pie", radius: "50%", data: [ { value: 1048, name: "表" }, { value: 735, name: "帽子" }, ], emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: "rgba(0, 0, 0, 0.5)", }, }, }, ], }); }, } </script> <style scoped> .base-chart-box { width: 300px; height: 300px; } </style> ``` # $nextTick ```javascript= <template> <div class="app"> <div v-if="isShowEdit"> <input type="text" v-model="editValue" ref="inp"> <button>確認</button> </div> <div v-else> <span>{{ title }}</span> <button @click="editFn">編輯</button> </div> </div> </template> <script> export default { data() { return { title: '大標題', isShowEdit: false, editValue: '' } }, methods: { editFn() { // 顯示文本框 this.isShowEdit = true // 在vue中數據變化試圖會更新 // 關鍵點:視圖更新是異步任務 // DOM更新是異步的,為了性能優化 // 數據變化->視圖更新->DOM更新 // 讓文本框聚焦(沒用) // this.$refs.inp.focus() // 定時器(有用) // setTimeout(() => { // this.$refs.inp.focus() // }, 0) // 今天學的指令(有用) this.$nextTick(() => { // 這個回調函數在dom更新後執行 this.$refs.inp.focus() } ) } } } </script> <style></style> ``` # 自定義指令 ![](https://hackmd.io/_uploads/HJdS0rnxT.png) main.js ```javascript= // 全局註冊自定義指令 Vue.directive("focus", { // 參數1:被綁定的DOM對象 inserted(el) { el.focus(); }, }); ``` 全局註冊用法 ```javascript= <template> <div> <h1>指令</h1> <!-- 方法1:autofocus,在ios有兼容性問題 --> <!-- <input autofocus ref="inp" type="text"> --> <!-- 方法2:在mounted獲取焦點 --> <!-- <input ref="inp" type="text"> --> <!-- 方法3:使用自定義指令 --> <input v-focus ref="inp" type="text"> </div> </template> <script> export default { // mounted() { // this.$refs.inp.focus() // } } </script> <style></style> ``` 局部註冊用法 ```javascript= <template> <div> <h1>指令</h1> <!-- 方法1:autofocus,在ios有兼容性問題 --> <!-- <input autofocus ref="inp" type="text"> --> <!-- 方法2:在mounted獲取焦點 --> <!-- <input ref="inp" type="text"> --> <!-- 方法3:使用自定義指令 --> <input v-focus ref="inp" type="text"> </div> </template> <script> export default { directives: { focus: { inserted(el) { el.focus() } } } // mounted() { // this.$refs.inp.focus() // } } </script> <style></style> ``` ## v-color ![](https://hackmd.io/_uploads/rkoI7paxa.png) ```javascript= <template> <div> <h1 v-color="color1">指令1</h1> <h1 v-color="color2">指令2</h1> </div> </template> <script> export default { directives: { color: { // inserted元素插入到DOM時執行 // 參數1:被綁定的DOM對象 // 參數2:指令相關的值(對象包多屬性) inserted(el, binding) { el.style.color = binding.value }, // update會在指令更新時執行 update(el, binding) { el.style.color = binding.value } } }, data() { return { color1: 'red', color2: 'orange' } } } </script> <style></style> ``` ## v-loading ```javascript= <template> <div class="main"> <div class="box" v-loading="list.length === 0"> <ul> <!-- <ul v-loading="list.length === 0"> --> <li v-for="item in list" :key="item.id" class="news"> <div class="left"> <div class="title">{{ item.title }}</div> <div class="info"> <span>{{ item.source }}</span> <span>{{ item.time }}</span> </div> </div> <div class="right"> <img :src="item.img" alt=""> </div> </li> </ul> </div> </div> </template> <script> import axios from 'axios' export default { directives: { loading: { inserted(el, binding) { binding.value ? el.classList.add('loading') : el.classList.remove('loading') }, update(el, binding) { binding.value ? el.classList.add('loading') : el.classList.remove('loading') }, } }, data() { return { list: [], isLoading: true } }, async created() { const res = await axios.get('http://hmajax.itheima.net/api/news') setTimeout(() => { this.list = res.data.data }, 2000) } } </script> <style scoped> *{ list-style: none; } .loading:before { content: ''; position: absolute; left: 0; top: 0; width: 100%; height: 100%; border:1px solid black; background: #fff url('./123.jpg') no-repeat center; } .box { width: 1000px; height: 800px; position: relative; } .right{ width: 200px; height: 200px; } .right img{ width: 100%; height: 100%; } </style> ``` ### 注意點 directives 記得要加s inserted 記得要加ed update 記得不要用過去式 url('./123.jpg') 記得要用字符串 # 插槽 ![](https://hackmd.io/_uploads/HkXGD5kba.png) App.vue ```javascript= <template> <div> <my-dialog></my-dialog> <my-dialog>你確認要刪除嗎</my-dialog> <my-dialog>你真的要退出嗎</my-dialog> <my-dialog> 用戶名: <input type="text"> <br> 密碼:<input type="password" > </my-dialog> </div> </template> <script> import MyDialog from './components/MyDialog.vue' export default { data() { return { } }, components: { MyDialog }, } </script> <style></style> ``` 子組件 ```javascript= <template> <div> <div> <h3>友情提示</h3> <span>+</span> </div> <div> <slot>我是默認內容</slot> </div> <div> <button>取消</button> <button>確認</button> </div> </div> </template> <script> export default { } </script> <style></style> ``` ## 具名插槽 App.vue ```javascript= <template> <div> <my-dialog> <template v-slot:header> <h3>友情提示</h3> </template> <!-- v-slot可以簡寫為# --> <template #body> 你確認要刪除嗎 </template> <template #footer> <button>取消</button> <button>確認</button> </template> </my-dialog> </div> </template> <script> import MyDialog from './components/MyDialog.vue' export default { data() { return { } }, components: { MyDialog }, } </script> <style></style> ``` 子組件 ```javascript= <template> <div> <div> <!-- <h3>友情提示</h3> --> <slot name="header"></slot> <span>+</span> </div> <div> <slot name="body">我是默認內容</slot> </div> <div> <slot name="footer"></slot> </div> </div> </template> <script> export default { } </script> <style></style> ``` ## 作用域插槽