---
tags: Vue,第五章
---
# v-model原理

```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

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>
```
# 自定義指令

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

```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') 記得要用字符串
# 插槽

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>
```
## 作用域插槽