# VUE
## 06-VUE中的數據代理


* setter
* 
* 一改頁面馬上更改
* data.name有被改到?有vm裡的就是了
* ,options是配置的意思,f12可以測試vm._data===data,
* 
* 1.) script data 放進 vm._data 2.) vm._data數據setter到vm.name... 3.) 綠色部分_data內有屬性也有get set,因為view監聽,所以一發生改變,則view就會立即改變(數據截池)
## 07-事件處理
### 7-1 事件的基本使用


```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>事件的基本使用</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>歡迎來到{{name}}學習</h2>
<button v-on:click="showInfo">點我提示信息0</button>
<button @click="showInfo">點我提示信息1(簡寫)(不傳參)</button>
<button @click="showInfo2($event,66)">點我提示信息2(簡寫)(傳參)</button>
</div>
<script>
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷'
},
methods:{
// 第一個參數一定是event,可以(a,b,c,d)=>a=event,bcd=undefind
showInfo(event){
// console.log(event.target.innerText) //按鈕內容
// console.log(this)// vue實例
alert('同學您好');
},
showInfo2(event, number){
console.log(number)
console.log(event.target.innerText) //按鈕內容
// console.log(this)// vue實例
alert('同學您好!!');
}
}
});
</script>
</body>
</html>
```
### 7-2 事件修飾符

* 冒泡事件說明 : 假設div裡面包著按鈕點擊事件,然後div本身也綁著事件,則一旦點擊按鈕事件,會一路執行到最外層事件。
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>事件修飾符</title>
<script type="text/javascript" src="../js/vue.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/js/bootstrap.min.js"></script>
<style>
*{
margin-top: 20px;
}
.demo1{
background-color: red;
}
.box1{
padding: 5px;
background-color: yellow;
}
.box2{
padding: 5px;
background-color: green;
}
.list{
width: 200px;
height: 200px;
background-color: orange;
/* 滾動 */
overflow: auto;
}
li{
height: 100px;
}
</style>
</head>
<body>
<div id="root">
<h2>歡迎來到{{name}}學習</h2>
<a href="https://tw.yahoo.com/" @click="showInfo">點我提示信息0</a>
<!-- prevent: 阻止a標籤跳走預設事件,只執行自己綁定事件 -->
<a href="https://tw.yahoo.com/" @click.prevent="showInfo">點我提示信息1</a>
<!-- stop: 阻止事件冒泡(常用) -->
<div class="demo1" @click="showInfo">
<button @click.stop="showInfo">點我提示信息2</button>
</div>
<!-- once: 事件只能觸發一次(常用) -->
<button @click.once="showInfo">點我提示信息2</button>
<!-- capture: 使用事件的捕獲模式,先捕獲,再冒泡 (不常用) -->
<!-- 捕獲階段不執行事件,冒泡: div2->div1 想要在捕獲階段就執行事件 -->
<div class="box1" @click.capture="showMsg($event,1)">
div1
<div class="box2" @click="showMsg($event,2)">
div2
</div>
</div>
<!-- self: 只有event.target是當前操作的元素才觸發事件 (不常用) -->
<!-- 冒泡上去的target都是button -->
<!-- self會去比對點擊的物件是否跟你本身是同個物件,像div!=button則冒泡上去的事件不會執行 -->
<div class="demo1" @click.self="showInfo">
<button @click.stop="showInfo">點我提示信息3</button>
</div>
<!-- passive: 事件的默認行為立即執行,無需等待事件回調執行完畢,搭配scroll、wheel,如果複雜任務,則滾輪或下拉都可能掛掉不會動 -->
<!-- 不用等事件完全處理完 -->
<ul @wheel="demo" class="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
</div>
<script>
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷'
},
methods:{
showInfo(e){
// e.preventDefault();// 可阻止a標籤跳走預設事件
// 也可用@click.prevent="showInfo"阻止a標籤跳走預設事件
alert('yahoo');
},
showMsg(e,msg){
console.log(msg);
},
demo(){
for (let i = 0; i <1000;i++) {
console.log('#')
}
}
}
});
</script>
</body>
</html>
```
### 7-3 鍵盤事件

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>鍵盤事件</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>歡迎來到{{name}}學習</h2>
<input type="text" placeholder="按下回車提示輸入" @keyup="showInfo($event)">
</div>
<script type="text/javascript">
// 自訂義
// @keyup.13="showInfo" //放在上面的input
// Vue.config.keyCodes.byMySelf = 13;
new Vue({
el:'#root',
data:{
name:'尚硅谷'
},
methods:{
showInfo(e){
// 按enter 13
// if(e.keyCode != 13) return;
console.log(e.target.value);
console.log(e.keyCode);
}
},
});
</script>
</body>
</html>
```
## 08-計算屬性

### 08-1 姓名案例-插值
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>姓名案例-插值語法實現</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
姓: <input type="text" v-model:value="firstName">
<br>
名: <input type="text" v-model:value="lastName">
<br>
姓名: <span>{{firstName.slice(0,3)}}-{{lastName}}</span>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
firstName:'張',
lastName:'三'
}
})
</script>
</body>
</html>
```
### 08-2 姓名案例-methods
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>姓名案例-methods實現</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<!-- 重點 當數據發生改變,模板會自動重新解析(div root模板),所以{{fullName()}}才會自動更新 -->
<body>
<div id="root">
姓: <input type="text" v-model:value="firstName">
<br>
名: <input type="text" v-model:value="lastName">
<br>
姓名: <span>{{fullName()}}</span>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
firstName:'張',
lastName:'三'
},
methods:{
fullName(){
console.log('查看是否調用fullName');
return this.firstName+'-'+this.lastName;
}
}
})
</script>
</body>
</html>
```
### 08-3 姓名案例-計算屬性

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>姓名案例-計算屬性實現</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
姓: <input type="text" v-model:value="firstName">
<br>
名: <input type="text" v-model:value="lastName">
<br>
<!-- 這個永遠不會影響到fullName,所以不會用到fullName的get -->
測試: <input type="text" v-model:value="x">
<br>
姓名: <span>{{fullName}}</span>
<br>
<!-- 可以發現'調用get'只會印出一次,因為有緩存機制 -->
<!-- 這樣會不會都取緩存?下面a1有解答 -->
姓名: <span>{{fullName}}</span>
</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
firstName:'張',
lastName:'三',
x:'您好'
},
computed:{
// 計算後的屬性_data不會出現
fullName:{
// get:當有人讀取fullName,get就會被調用,且返回值就作為fullName的值
// a1: get啥時調用? 1. 初次讀取 2. 所依賴的數據發生改變
get(){
console.log('調用get')
return this.firstName+'-'+this.lastName;
},
// set啥時調用? 當fullName被修改時
// 用f12 直接更改 vm.fullName=張-小名,即可看出輸入框更改
set(value){
const arr = value.split('-');
this.firstName = arr[0];
this.lastName = arr[1];
}
}
}
})
</script>
</body>
</html>
```
### 08-3 姓名案例-計算屬性實現_簡寫
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>姓名案例-計算屬性實現_簡寫</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<!-- 只讀不改,才可以用簡寫(ex:只有get,無set) -->
<body>
<div id="root">
姓: <input type="text" v-model:value="firstName">
<br>
名: <input type="text" v-model:value="lastName">
<br>
姓名: <span>{{fullName}}</span>
</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
firstName:'張',
lastName:'三'
},
computed:{
// 完整寫法
// fullName:{
// get(){
// console.log('調用get')
// return this.firstName+'-'+this.lastName;
// },
// set(value){
// const arr = value.split('-');
// this.firstName = arr[0];
// this.lastName = arr[1];
// }
// }
// 法1
// fullName:function(){
// return this.firstName+'-'+this.lastName;
// }
// 法ˇ
fullName(){
return this.firstName+'-'+this.lastName;
}
}
})
</script>
</body>
</html>
```
## 09 監聽屬性

### 09-1 天氣案例
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>天氣案例</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<!-- 只讀不改,才可以用簡寫(ex:只有get,無set) -->
<body>
<div id="root">
<h2>今天天氣很{{info}}</h2>
<button @click="changeMeather">切換天氣</button>
<!-- 下面如果一句業務邏輯可以用下面的 -->
<!-- <button @click="isHot = !isHot">切換天氣</button> -->
</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
isHot:true
},
computed: {
info(){
return this.isHot ? '炎熱' : '涼爽';
}
},
methods: {
changeMeather(){
this.isHot = !this.isHot;
}
},
})
</script>
</body>
</html>
```
### 09-2 天氣案例_監視屬性_watch

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>天氣案例_監視屬性</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<!-- 只讀不改,才可以用簡寫(ex:只有get,無set) -->
<body>
<div id="root">
<h2>今天天氣很{{info}}</h2>
<button @click="changeMeather">切換天氣</button>
<!-- 下面如果一句業務邏輯可以用下面的 -->
<!-- <button @click="isHot = !isHot">切換天氣</button> -->
</div>
<script type="text/javascript">
// 法1 法2選擇用誰? 如果一開始就知道監視誰,就用法1,如果後續才知道用法2
const vm = new Vue({
el:'#root',
data:{
isHot:true
},
computed: {
info(){
return this.isHot ? '炎熱' : '涼爽';
}
},
methods: {
changeMeather(){
this.isHot = !this.isHot;
}
},
// 法1
// watch: {
// isHot:{
// // 設定handler是否一開始就執行,還是發生改變才執行
// immediate:true,
// // 什麼時候調用呢? 當isHot發生改變時
// // 這樣就可以監聽每個屬性修改要做甚麼動作
// // ex: 新的溫度 和 舊的溫度 取差,差過幾度提醒...應用場景
// handler(newValue, oldValue){
// console.log('isHot發生改變', newValue, oldValue);
// }
// }
// },
})
// 法2
vm.$watch('isHot',{
// 設定handler是否一開始就執行,還是發生改變才執行
immediate:true,
// 什麼時候調用呢? 當isHot發生改變時
// 這樣就可以監聽每個屬性修改要做甚麼動作
// ex: 新的溫度 和 舊的溫度 取差,差過幾度提醒...應用場景
handler(newValue, oldValue){
console.log('isHot發生改變', newValue, oldValue);
}
})
</script>
</body>
</html>
```
### 09-3 天氣案例_深度監視屬性

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>天氣案例_深度監視屬性</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<!-- 只讀不改,才可以用簡寫(ex:只有get,無set) -->
<body>
<div id="root">
<h2>今天天氣很{{info}}</h2>
<button @click="changeMeather">切換天氣</button>
<hr>
<h3>a的值是{{numbers.a}}</h3>
<button @click="numbers.a++">點我a+1</button>
<h3>b的值是{{numbers.b}}</h3>
<button @click="numbers.b++">點我b+1</button>
<!-- 下面如果一句業務邏輯可以用下面的 -->
<!-- <button @click="isHot = !isHot">切換天氣</button> -->
</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
isHot:true,
numbers:{
a:1,
b:2
}
},
computed: {
info(){
return this.isHot ? '炎熱' : '涼爽';
}
},
methods: {
changeMeather(){
this.isHot = !this.isHot;
}
},
// 法1
watch: {
isHot:{
// 設定handler是否一開始就執行,還是發生改變才執行
immediate:true,
// 什麼時候調用呢? 當isHot發生改變時
// 這樣就可以監聽每個屬性修改要做甚麼動作
// ex: 新的溫度 和 舊的溫度 取差,差過幾度提醒...應用場景
handler(newValue, oldValue){
console.log('isHot發生改變', newValue, oldValue);
}
},
// 監測多層屬性的特定屬性寫法
'numbers.a':{
handler(){
console.log('a被改變');
}
},
//如果100個key只有一個改變呢?
numbers:{
deep:true,// 要開啟才會監視內部所有屬性,不然只是監視numbers本身這個物件而已
handler(){
console.log('numbers改變')
}
}
},
})
</script>
</body>
</html>
```
## 09_08_計算屬性vs監視屬性
* computed vs watch


```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>姓名案例-watch實現</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<!-- 只讀不改,才可以用簡寫(ex:只有get,無set) -->
<body>
<div id="root">
姓: <input type="text" v-model:value="firstName">
<br>
名: <input type="text" v-model:value="lastName">
<br>
姓名: <span>{{fullName}}</span>
</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
firstName:'張',
lastName:'三',
fullName:'張-三' //用watch屬性要自己先宣告
},
computed:{
// 完整寫法
// fullName:{
// get(){
// console.log('調用get')
// return this.firstName+'-'+this.lastName;
// },
// set(value){
// const arr = value.split('-');
// this.firstName = arr[0];
// this.lastName = arr[1];
// }
// }
// 法1
// fullName:function(){
// return this.firstName+'-'+this.lastName;
// }
// 法2
// fullName(){
// return this.firstName+'-'+this.lastName;
// }
},
watch:{
firstName:{
handler(newValue, oldVlaue){
// this.fullName = newValue+'-'+this.lastName;
// vue都用function(){},才可調用this(vm)
// 這邊seTimeput,是原生js調用,this會調成window,
// 寫箭頭函式是因為這樣沒有this,就會自動去找上一層的當作this
setTimeout(()=>{
this.fullName = newValue+'-'+this.lastName;
},10000);
}
},
lastName:{
handler(newValue, oldVlaue){
this.fullName = this.firstName+'-'+newValue;
}
},
}
// 提出一個需求,如果fullName要三秒之後才出現呢? 只能用watch
// computed 用的是return立即計算,
// watch可以異步處理業務邏輯(同時間處理所有事物)
})
</script>
</body>
</html>
```
## 10 樣式綁定
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>綁定樣式</title>
<script type="text/javascript" src="../js/vue.js"></script>
<style>
.basic{}
.happy{}
.sad{}
.normal{}
.atguigu1{}
.atguigu2{}
.atguigu3{}
</style>
</head>
<body>
<div id="root">
<!-- class basic normal -->
<!-- 綁定class樣式--字符串寫法,適用於: 樣式的類名不確定,需要動態指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div>
<br>
<br>
<!-- 需求:綁定所有樣式,--數組寫法,操作arr等於操作要綁定的樣式 -->
<div class="basic" :class="classArr">{{name}}</div>
<br>
<br>
<!-- 需求:綁定特定樣式,--對象寫法,操作arr等於操作要綁定的樣式 -->
<div class="basic" :class="classObj">{{name}}</div>
<br>
<br>
<div class="basic" :style="styleObj">{{name}}</div>
</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷a',
mood:'normal',
classArr:['atguigu1','atguigu2','atguigu3'],
classObj:{
atguigu1:false,
atguigu2:true
},
styleObj:{
fontSize: '40px',
color:'red'
}
},
methods: {
changeMood(){
const arr = ['happy','sad','normal'];
const index = Math.floor(Math.random()*3);
this.mood = arr[index];
}
},
})
</script>
</body>
</html>
```
## 11 條件渲染
* 當n===某值,顯示某div


```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>條件渲染</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- style="display: none;" -->
<h2 v-show="false">歡迎來到{{name}}</h2>
<!-- 整個不見 -->
<h2 v-if="false">歡迎來到{{name}}</h2>
<h2>當前的n值是:{{n}}</h2>
<button @click="n++">點我n+1</button>
<!-- 高頻率切換,使用v-show -->
<!-- if elseif else 中間程式碼不能打斷 -->
<div v-if="n === 1">Angular</div>
<div v-else-if="n === 2">React</div>
<div v-else-if="n === 3">Vue</div>
<div v-else>哈哈</div>
<!-- else撿剩的,根本不用條件式 -->
<!-- 需求n是4時,同時顯,但只保留h2,法1為了實現外面多了一層div破壞原始結構 -->
<!-- 法2:只能配合v-if,且不會破壞結構 -->
<!-- <div v-if="n===4">
<h2>你好</h2>
<h2>台灣</h2>
</div> -->
<template v-if="n===4">
<h2>你好</h2>
<h2>台灣</h2>
</template>
</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
name:'尚硅谷',
n:0
}
})
</script>
</body>
</html>
```
## 12 列表渲染
### 12-1 基本列表


```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>基本列表</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人員列表</h2>
<!-- 遍歷數組 -->
<ul>
<!-- 下一小截會說明key,這邊先任知道他是唯一標示 -->
<li v-for="p in persons" :key="p.id">
{{p.id}}-{{p.name}}-{{p.age}}
</li>
<!-- p:{id:'001',name:'張三',age:18}, index:索引 -->
<li v-for="(p,index) in persons" :key="index">
{{p.name}}-{{p.age}}-{{index}}
</li>
</ul>
<!-- 遍歷對象 -->
<h2>汽車信息</h2>
<ul>
<li v-for="(val,key) of car" :key="key">
{{key}}-{{val}}
</li>
</ul>
<!-- 遍歷字符串 -->
<h2>遍歷字符串信息</h2>
<ul>
<li v-for="(val,index) of str" :key="index">
{{index}}-{{val}}
</li>
</ul>
<!-- 遍歷指定次數 -->
<h2>遍歷指定次數</h2>
<ul>
<li v-for="(number,index) of 5" :key="index">
{{index}}-{{number}}
</li>
</ul>
</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'張三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
],
car:{
name:'奧迪',
price:'100萬'
},
str:'hello'
}
})
</script>
</body>
</html>
```
### 12-2 key的原理

* 1. index作為key,先比對key,一樣的東西續用,不一樣的東西新生成

* 如果堅持用index作為key,則新增數據一定要加在後面

* 2. p.id作為key,先比對key,一樣的東西續用,不一樣的東西新生成

### 12-3 列表過濾

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>列表過濾</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人員列表</h2>
<input type="text" placeholder="請輸入名字" v-model="keyWord">
<!-- 遍歷數組 -->
<ul>
<!-- p:{id:'001',name:'張三',age:18}, index:索引 -->
<li v-for="(p,index) in filPersons" :key="index">
{{p.name}}-{{p.age}}-{{index}}
</li>
</ul>
</div>
<script type="text/javascript">
// // 用watch實現
// const vm = new Vue({
// el:'#root',
// data:{
// keyWord:'',
// persons:[
// {id:'001',name:'馬冬梅',age:18, sex:"女"},
// {id:'002',name:'周冬雨',age:20, sex:"女"},
// {id:'003',name:'周杰倫',age:21, sex:"男"},
// {id:'004',name:'溫亮倫',age:22, sex:"男"}
// ],
// filPersons:[]
// },
// watch:{
// keyWord:{
// immediate:true,// 一啟動就先做
// handler(newValue){
// this.filPersons = this.persons.filter((p)=>{
// // p.name.indexOf(newValue)是否包含(0,-1)
// // indexOf比對到空字串(''),全部數據都符合
// return p.name.indexOf(newValue) !== -1
// })
// // 笨蛋寫法
// // if(newValue===''){
// // this.filPersons = this.persons;
// // }
// }
// }
// }
// })
// 用computed實現
const vm = new Vue({
el:'#root',
data:{
keyWord:'',
persons:[
{id:'001',name:'馬冬梅',age:18, sex:"女"},
{id:'002',name:'周冬雨',age:20, sex:"女"},
{id:'003',name:'周杰倫',age:21, sex:"男"},
{id:'004',name:'溫亮倫',age:22, sex:"男"}
]
},
computed:{
filPersons:{
get(){
// get的return
return this.filPersons = this.persons.filter((p)=>{
// p.name.indexOf(newValue)是否包含(0,-1)
// indexOf比對到空字串(''),全部數據都符合
return p.name.indexOf(this.keyWord) !== -1
})
},
set(){}
}
}
})
</script>
</body>
</html>
```
### 12-4 列表排序

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>列表排序</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人員列表</h2>
<input type="text" placeholder="請輸入名字" v-model="keyWord">
<button @click="sortType = 2">年齡升序</button>
<button @click="sortType = 1">年齡降序</button>
<button @click="sortType = 0">原順序</button>
<!-- 遍歷數組 -->
<ul>
<!-- p:{id:'001',name:'張三',age:18}, index:索引 -->
<li v-for="(p,index) in filPersons" :key="p.id">
{{p.name}}-{{p.age}}-{{index}}
</li>
</ul>
</div>
<script type="text/javascript">
// 用computed實現
const vm = new Vue({
el:'#root',
data:{
keyWord:'',
sortType:0,// 0:原順序, 1:年齡升序, 2:年齡降序
persons:[
{id:'001',name:'馬冬梅',age:30, sex:"女"},
{id:'002',name:'周冬雨',age:31, sex:"女"},
{id:'003',name:'周杰倫',age:18, sex:"男"},
{id:'004',name:'溫亮倫',age:19, sex:"男"}
]
},
computed:{
filPersons:{
get(){
const arr = this.persons.filter((p)=>{
// p.name.indexOf(newValue)是否包含(0,-1)
// indexOf比對到空字串(''),全部數據都符合
return p.name.indexOf(this.keyWord) !== -1
})
if(this.sortType){
arr.sort((p1,p2)=>{
return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age;
})
}
return arr;
// sort說明
// let arr = [1,3,2,6,4,5];
// arr.sort((a,b)=>{
// // 升序
// // return a-b;
// // 降序
// return b-a;
// })
},
set(){}
}
}
})
</script>
</body>
</html>
```
### 12-5 觀察監測數據如何更新
* 可以發現下面程式有個方式更改data,但view不改變,因為vue沒監聽到
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>觀察監測數據如何更新</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>人員列表</h2>
<button @click="updateMei">更新馬冬梅的信息</button>
<!-- 遍歷數組 -->
<ul>
<!-- p:{id:'001',name:'張三',age:18}, index:索引 -->
<li v-for="(p,index) in persons" :key="p.id">
{{p.name}}-{{p.age}}-{{p.sex}}-{{index}}
</li>
</ul>
</div>
<script type="text/javascript">
// 用computed實現
const vm = new Vue({
el:'#root',
data:{
persons:[
{id:'001',name:'馬冬梅',age:30, sex:"女"},
{id:'002',name:'周冬雨',age:31, sex:"女"},
{id:'003',name:'周杰倫',age:18, sex:"男"},
{id:'004',name:'溫亮倫',age:19, sex:"男"}
]
},
methods: {
updateMei(){
// this.persons[0].name='馬老師';// 奏效
// this.persons[0].age=50;// 奏效
// this.persons[0].sex='男';// 奏效
// vue的view不奏效,vm確實改了,但vue監測不到
// this.persons[0] = {id:'001',name:'馬老師',age:50, sex:"男"}; //不奏效
// 原因直接對persons數組直接改動,而不是對內容物
this.persons.splice(0,1,{id:'001',name:'馬老師',age:50, sex:"男"}); //奏效
}
},
})
</script>
</body>
</html>
```
### 12-6 vue監測數據原理_對象(重要)
1. data的name改變
2. vm._data改變數據(調用set)
3. 一調用set就重新解析模板
4. 解析模板就生成新的虛擬DOM
5. 新舊DOM對比
6. 更新頁面

### 12-7 模擬一個數據監測(重要,_data如何實現)
* 只示範一層數據
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>模擬一個數據監測</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<script type="text/javascript">
// 1. 創建obs,且傳入data 2. 創建vm,將vm._data及data指向obs物件,且綁定所有屬性的get、set
// 3. 當vm._data及data的屬性改變,則調用set
let data = {
name:'尚硅谷',
address:'北京'
}
// 創建監測data的物件
const obs = new Observer(data);
// 模擬vm
let vm = {}
vm._data = obs;
data = obs;
// 監測物件
function Observer(obj){
// 匯總對象中所有的屬性形成一個數組
const keys = Object.keys(obj)
// 遍歷
keys.forEach((k) => {
Object.defineProperty(this,k,{
get(){
return obj[k];
},
set(val){
console.log(`${k}被改了,我要去解析模板,生成虛擬DOM...`)
obj[k]=val
}
})
});
}
</script>
</body>
</html>
```
### 12-8 Vue.set的使用
* 主要可以實現後添加數據在vm裡也有get set方法影響view改變
* 可以發現sex手動增上去,view沒改變,_data裡沒set方法
* 方法1 : vm.$set(vm.student,'sex','女')
* 方法2 : Vue.set(this.student,'sex','男')
* 這是有限制的,不能將添加數據直接加在vm或者data上,ex:vm.$set(vm._data,'leader','小張')
* 下面就是包在school裡作解決

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue.set的使用</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>學校名稱:{{school.name}}</h2>
<h2>學校地址:{{school.address}}</h2>
<hr>
<h2>學生姓名:{{student.name}}</h2>
<h2>學生年齡: 真實={{student.age.rAge}}, 對外:{{student.age.sAge}}</h2>
<h2>朋友們</h2>
<ul>
<li v-for="(f, index) in student.friends" :key="index">
{{f.name}}---{{f.age}}
</li>
</ul>
<h2 v-if="student.sex">學生性別:{{student.sex}}</h2>
<button @click="addSex">添加一個性別屬性,默認是男</button>
<h2 v-if="school.leader">校長是:{{school.leader}}</h2>
<button @click="addLeader">添加一個校長,默認是小張</button>
</div>
<script type="text/javascript">
// 用computed實現
const vm = new Vue({
el:'#root',
data:{
school:{
name:'尚硅谷',
address:'北京'
},
student:{
name:'tom',
age:{
rAge:40,
sAge:29
},
friends:[
{name:'jerry',age:35},
{name:'tony',age:36}
]
}
},
methods: {
addSex(){
// 重點
// Vue.set(this.student,'sex','男')
this.$set(this.student,'sex','男')
},
addLeader(){
// 重點
// Vue.set(this.student,'sex','男')
this.$set(this.school,'leader','小張')
}
},
})
</script>
</body>
</html>
```
### 12-9 Vue監測數據的原理_數組
* 針對hobby去觀察
```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Vue監測數據的原理_數組</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>愛好</h2>
<ul>
<li v-for="(h, index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
</div>
<script type="text/javascript">
// 用computed實現
const vm = new Vue({
el:'#root',
data:{
school:{
name:'尚硅谷',
address:'北京'
},
student:{
name:'tom',
age:{
rAge:40,
sAge:29
},
hobby:['抽菸','喝酒','燙頭髮'],
friends:[
{name:'jerry',age:35},
{name:'tony',age:36}
]
}
},
})
</script>
</body>
</html>
```

* 可以發現數組並沒有get set

* 數組操作

* hobby有get set所以可以對這個數組做改變,vue才監測的到

* 1.)vue的push調用js原生push。 2.)vue再重新解析模板
* 重點vue有重新封裝過數組操作

* Vue.set(vm._data.student.hobby,0,'學習'),這也能改。
### 12-10 總結Vue監測數據


```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>總結Vue監測數據</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<button @click="addAge">年齡+1歲</button> <br>
<button @click="addSex">添加性別屬性,默認值: 男</button> <br>
<button @click="student.sex = '未知'">修改性別</button> <br> <!-- sex必須存在 -->
<button @click="addFriend">在列表首位添加一個朋友</button> <br>
<button @click="updateFirstFriendName">修改第一個朋友的名字為: 張三</button> <br>
<button @click="addHobby">添加一個愛好</button> <br>
<button @click="updateFirstHobby">修改第一個愛好為: 開車</button> <br>
<button @click="removeSmoke">過濾掉愛好中的抽菸</button> <br>
<h1>學生信息</h1>
<h2>學生姓名:{{student.name}}</h2>
<h2>學生年齡:{{student.age}}</h2>
<h2 v-if="student.sex">學生性別:{{student.sex}}</h2>
<h2>愛好</h2>
<ul>
<li v-for="(h, index) in student.hobby" :key="index">
{{h}}
</li>
</ul>
<h2>朋友們</h2>
<ul>
<li v-for="(f, index) in student.friends" :key="index">
{{f.name}}---{{f.age}}
</li>
</ul>
</div>
<script type="text/javascript">
// 用computed實現
const vm = new Vue({
el:'#root',
data:{
school:{
name:'尚硅谷',
address:'北京'
},
student:{
name:'tom',
age:18,
hobby:['抽菸','喝酒','燙頭髮'],
friends:[
{name:'jerry',age:35},
{name:'tony',age:36}
]
}
},
methods: {
addAge(){
this.student.age++;
},
addSex(){
this.$set(this.student,'sex','男');
},
addFriend(){
this.student.friends.unshift({name:'jack',age:70});
},
updateFirstFriendName(){
this.student.friends[0].name = '張三';// 之前是數組直接附值會掛,因為數組本身沒有get set,這邊name有阿
},
addHobby(){
this.student.hobby.push('學習');
},
updateFirstHobby(){
// this.student.hobby.splice(0,1,'開車');// 第0個位置改成開車,1代表刪原本的第0個位置
this.$set(this.student.hobby,0,'開車');
},
removeSmoke(){
this.student.hobby = this.student.hobby.filter((h)=>{
return h != '抽菸';
})
}
},
})
</script>
</body>
</html>
```
## 13 收集表單數據


```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>收集表單數據</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- form預設會跳轉,prevent阻止默認行為,只執行自己綁定事件 -->
<form>
<label for="test1">帳號: </label>
<input type="text" id="test1" v-model.trim="userInfo.account">
<br>
密碼: <input type="password" v-model="userInfo.password">
<br>
年齡: <input type="number" v-model.number="userInfo.age">
<br>
<!-- name綁定一組 -->
性別:
男<input type="radio" name="sex" v-model="userInfo.sex" value="male">
女<input type="radio" name="sex" v-model="userInfo.sex" value="female">
<br>
<!-- 要用數組去接 -->
愛好:
學習<input type="checkbox" v-model="userInfo.hobby" value="study">
打遊戲<input type="checkbox" v-model="userInfo.hobby" value="game">
吃飯<input type="checkbox" v-model="userInfo.hobby" value="eat">
<br>
所屬校區
<select v-model="userInfo.city">
<option value="">請選擇校區</option>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
<option value="shenzhen">深圳</option>
<option value="wuhan">武漢</option>
</select>
<br>
<!-- lazy: 不會輸入一個字,就直接改vm裡的屬性,而是失去焦點再去更改屬性內容 -->
其他信息
<textarea v-mode.lazy="userInfo.other"></textarea>
<br>
<input type="checkbox" v-model="userInfo.agree"> 閱讀並接受<a href="https://tw.yahoo.com/"><用戶協議></a>
<br>
<button @click.prevent="demo">提交</button>
</form>
</div>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
userInfo:{
account:'',
password:'',
age:'',
sex:'',
hobby:[],
city:'beijing',
other:'',
agree:''
}
},
methods: {
demo(){
console.log(JSON.stringify(this.userInfo));
}
},
})
</script>
</body>
</html>
```
## 14 過濾器


```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>收集表單數據</title>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript" src="../js/dayjs.min.js"></script>
</head>
<body>
<div id="root">
<h2>顯示格式化後的時間</h2>
<!-- 計算屬性實現 -->
<h3>{{fmtTime}}</h3>
<br>
<!-- methods實現 -->
<h3>{{getFmtTime()}}</h3>
<br>
<!-- 過濾器 -->
<h3>{{time | timeFormater}}</h3>
<!-- 連鎖過濾器 -->
<h3>{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3> <!--2021 -->
<!-- 屬性使用過濾器 -->
<h3 :x="msg | mySlice2">尚硅谷</h3>
<!-- 錯的不能用在v-model -->
<!-- <input type="text" v-model="msg | mySlice2"> -->
</div>
<!-- 全局過濾器使用 -->
<div id="root2">
<h2>{{msg | mySlice2}}</h2>
</div>
<script type="text/javascript">
// 全局過濾器
Vue.filter('mySlice2', function(value){
return value.slice(0, 4);
})
const vm = new Vue({
el:'#root',
data:{
time:1621561377603, // 時間戳
msg:'您好,台灣'
},
computed: {
fmtTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
}
},
methods: {
getFmtTime(){
return dayjs(this.time).format('YYYY-MM-DD HH:mm:ss');
}
},
// 局部過濾器: 寫在Vue裡,只有該VM裡可以用,之後組件為一個VM,所以一個VUE實體可能包含多個VM
filters:{
timeFormater(value, var1='YYYY-MM-DD HH:mm:ss'){
console.log(var1)
return dayjs(value).format(var1);
},
mySlice(value){
return value.slice(0, 4);
}
}
})
const vm2 = new Vue({
el:'#root2',
data:{
msg:'hello' // 時間戳
}
})
</script>
</body>
</html>
```
## 15 內置指令
### 15-1 v-text
```htmlembedded=
<div id="root">
<div>{{name}}</div>
<div v-text="name"></div>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
```
### 15-2 v-html

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>收集表單數據</title>
<script type="text/javascript" src="../js/vue.js"></script>
<script type="text/javascript" src="../js/dayjs.min.js"></script>
</head>
<body>
<div id="root">
<div>{{name}}</div>
<div v-html="str"></div>
</div>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'尚硅谷',
str:'<h3>你好阿!</h3>'
}
})
</script>
</body>
</html>
```
### 15-3 v-cloak
* 目的: v-cloak為隱藏屬性,當vue實例真的串建完成,該屬性則會自動消失。可看例子。

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>v-cloak</title>
<style>
[v-cloak]{
display: none;
}
</style>
</head>
<body>
<div id="root">
<div v-cloak>{{name}}</div>
</div>
<!-- 這邊我並沒有這伺服器隔五秒才拿到vue.js -->
<!-- 頁面會先出現{{name}},五秒後拿到js,頁面才會出現尚硅谷 -->
<!-- 解決方法v-cloak,防止直接先出現{{name}} -->
<!-- v-cloak運作方式:過一段時間(vue一介入)該屬性不見 -->
<!-- 所以一開始先display:none,v-cloak消失,則display:none消失,就顯示了 -->
<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
name:'尚硅谷'
}
})
</script>
</html>
```
### 15-4 v-once
* 目的: 得到初始化數值,不受任何變動,依然可以顯示。

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>v-once指令</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 只跑第一次 -->
<h2 v-once>初始化n值是:{{n}}</h2>
<h2>當前的n值是:{{n}}</h2>
<button @click="n++">點我n+1</button>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
</html>
```
### 15-5 v-pre
* 目的: html元素不綁定vue解析。

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>v-pre指令</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 不綁定vue解析 -->
<h2 p-pre>Vue其實很簡單</h2>
<h2>當前的n值是:{{n}}</h2>
<button @click="n++">點我n+1</button>
</div>
</body>
<script type="text/javascript">
new Vue({
el:'#root',
data:{
n:1
}
})
</script>
</html>
```
## 16 自訂義指令
* 需求1:定義一個v-big指令,和v-text功能類似,但會把綁定的數值放大10倍。
* 需求2:定義一個v-fbind指令,和v-bind功能類似,但可以讓其所綁定的input元素默認獲取焦點。

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>自訂義指令</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<!--
需求1:定義一個v-big指令,和v-text功能類似,但會把綁定的數值放大10倍。
需求2:定義一個v-fbind指令,和v-bind功能類似,但可以讓其所綁定的input元素默認獲取焦點。
-->
<div id="root">
<!-- 需求1 -->
<h2>當前的n值是: <span v-text="n"></span></h2>
<h2>放大10倍後的n值是: <span v-big="n"></span></h2>
<h2>放大10倍後的n值是: <span v-big-method2="n"></span></h2>
<button @click="n++">點我n+1</button>
<!-- 需求2 -->
<input type="text" v-fbind:value="n">
</div>
</body>
<script type="text/javascript">
// 全局寫法 v-test
Vue.directive('test', {
bind(element,binding){
element.value = binding.value;
},
inserted(element,binding){
element.focus();
},
update(element,binding){
element.value = binding.value;
}
})
new Vue({
el:'#root',
data:{
n:1
},
directives:{
// element: span, binding裡面放著各種n的訊息之類的,this:window
// big函數何時倍調用?
// 1. 指令與元素成功綁定時
// 2. 當點擊button時,數據改變時(也會造成模板重新解析時),會調用。(因為數據改變=>模板就會重新解析)
// 所以如果改變數據其他key也會調用big
big(element,binding){
// console.log(element, binding, this);
element.innerText = binding.value * 10;
},
'big-method2'(element,binding){
element.innerText = binding.value * 10;
},
// 失敗
// fbind(element,binding){
// element.value = binding.value;
// element.focus(); // 沒用,雖然綁定內容,但是focus只在輸入框生成後執行,可發現點擊一次後可獲得焦點
// // 先討論fbind何時執行?上面有
// }
fbind:{
// 可設定不同時間點,調用不同函數bind(){}、inserted(){}、update(){}
// 如果用big寫法:代表只寫了bind、update都使用同個函式
// 1. 指令與元素成功綁定時
bind(element,binding){
element.value = binding.value;
},
// 2.指令元素被插入頁面時,被調用
inserted(element,binding){
element.focus();
},
// 3.指令所在的模板重新解析頁面時,被調用
update(element,binding){
element.value = binding.value;
}
}
}
})
</script>
</html>
```
## 17 Vue生命週期
### 17-1 引出生命週期 mounted

* 這個標題越來越淡,淡到一定程度恢復全黑,再越來越淡。

```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>引出生命週期</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<!-- 一組一組的要寫對象{opacity: opacity} -->
<h2 :style="{opacity: opacity}">歡迎學習Vue</h2>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
opacity:1
},
methods: {
// 必須綁在按鈕上違反需求
// change(){
// setInterval(()=>{
// this.opacity -= 0.01;
// if(this.opacity <= 0){
// this.opacity = 1;
// };
// },16)
// }
},
// 重點: 在生命週期中mounted是其中一個會調用的函數
// Vue完成模板的解析並把初始的真實的DOM元素放入頁面後(掛載完畢)調用mounted
// 只調用一次
mounted() {
// 寫箭頭函式是因為這樣沒有this,就會自動去找上一層的當作this
setInterval(()=>{
this.opacity -= 0.01;
if(this.opacity <= 0){
this.opacity = 1;
};
},16)
},
})
// 通過外部定時器實現(不推薦)
// setInterval(()=>{
// vm.opacity -= 0.01;
// if(vm.opacity <= 0){
// vm.opacity = 1;
// };
// },16)
</script>
</html>
```
### 17-2 分析生命週期(049)
* 黃色框框:Has 'el' option,outerHTML:是否包含root div整個為模板


* 關注左邊

* 關注右邊


```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>分析生命週期</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2>當前的n值是:{{n}}</h2>
<button @click="add">點我n+1</button>
<button @click="bye">點我銷毀vm</button>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
// template:`
// <div>
// <h2>當前的n值是:{{n}}</h2>
// <button @click="add">點我n+1</button>
// </div>
// `,// 直接當模板,上面可以是空的html,上面div root還是要存在
data:{
n:1
},
methods: {
add(){
console.log('add');
this.n++;
},
bye(){
console.log('bye');
this.$destroy();
}
},
beforeCreate() {
// console.log('beforeCreate');
// console.log(this);// 看看是否有_data?這時候還沒處理到那邊
// // 讓程式跑到這就先停
// debugger;
},
created() {
// console.log(this);// 有_data、methods
// debugger;
},
beforeMount() {
// console.log(this);
// debugger;
},
mounted() {
// console.log(this);
// debugger;
},
beforeUpdate() {
// 數據已更新,淡頁面還未重新解析前
// console.log(this.n);
// debugger;
},
updated() {
console.log('updated');
},
beforeDestroy() {
// 在vm銷毀前一刻執行
// 可以調用方法那些,但是內部更新數據的指令已經不會跑了
},
destroyed() {
},
})
</script>
</html>
```
### 17-3 生命週期總結



```htmlembedded=
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>引出生命週期</title>
<script type="text/javascript" src="../js/vue.js"></script>
</head>
<body>
<div id="root">
<h2 :style="{opacity: opacity}">歡迎學習Vue</h2>
<button @click="opacity = 1">點我透明度為1</button>
<button @click="stop">點我停止變換</button>
</div>
</body>
<script type="text/javascript">
const vm = new Vue({
el:'#root',
data:{
opacity:1
},
methods: {
// 自殺,他殺連這個stop都沒有
stop(){
// clearInterval(this.timer);// vm還存在,所以可以透過f12或者任意方式都還可以被更改
this.$destroy(); // 銷毀時定時器並不會消失阿,定時器是在window上阿,所以上面定時器可以在下面銷毀之前先取消訂時
}
},
// 重點: 在生命週期中mounted是其中一個會調用的函數
// Vue完成模板的解析並把初始的真實的DOM元素放入頁面後(掛載完畢)調用mounted
// 只調用一次
mounted() {
// 寫箭頭函式是因為這樣沒有this,就會自動去找上一層的當作this
// this=>vm
this.timer = setInterval(()=>{
console.log('setInterval');
this.opacity -= 0.01;
if(this.opacity <= 0){
this.opacity = 1;
};
},16)
},
// 善後動作
beforeDestroy() {
clearInterval(this.timer);
},
})
</script>
</html>
```