---
# System prepended metadata

title: Vue2第五章
tags: [第五章, Vue]

---

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