# VUE ## 06-VUE中的數據代理 ![](https://hackmd.io/_uploads/S1iCEwJH3.png) ![](https://hackmd.io/_uploads/rJxeBvkr2.png) * setter * ![](https://hackmd.io/_uploads/BJ8gLD1r3.png) * 一改頁面馬上更改 * data.name有被改到?有vm裡的就是了![](https://hackmd.io/_uploads/HyP98v1S2.png) * ![](https://hackmd.io/_uploads/r1d8Dv1Bn.png),options是配置的意思,f12可以測試vm._data===data,![](https://hackmd.io/_uploads/BklaPPkrn.png) * ![](https://hackmd.io/_uploads/Hkti9DyB3.png) * 1.) script data 放進 vm._data 2.) vm._data數據setter到vm.name... 3.) 綠色部分_data內有屬性也有get set,因為view監聽,所以一發生改變,則view就會立即改變(數據截池) ## 07-事件處理 ### 7-1 事件的基本使用 ![](https://hackmd.io/_uploads/ryZF95yB3.png =33%x)![](https://hackmd.io/_uploads/Sk_cq9yHh.png =33%x)![](https://hackmd.io/_uploads/SkUo591r3.png =33%x) ![](https://hackmd.io/_uploads/H1kZcqyH3.png) ```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 事件修飾符 ![](https://hackmd.io/_uploads/r1wy1jkrh.png) * 冒泡事件說明 : 假設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 鍵盤事件 ![](https://hackmd.io/_uploads/SkC5qAeBh.png) ```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-計算屬性 ![](https://hackmd.io/_uploads/BJMeyg-rh.png) ### 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 姓名案例-計算屬性 ![](https://hackmd.io/_uploads/Byd4kbfrh.png) ```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 監聽屬性 ![](https://hackmd.io/_uploads/Hy1B8rGBn.png) ### 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 ![](https://hackmd.io/_uploads/S1RzX4mB2.png) ```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 天氣案例_深度監視屬性 ![](https://hackmd.io/_uploads/SJeRvNXrn.png) ```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 ![](https://hackmd.io/_uploads/BJMeyg-rh.png) ![](https://hackmd.io/_uploads/Sk8o8BXB3.png) ```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 ![](https://hackmd.io/_uploads/rJoZ4t7rh.png) ![](https://hackmd.io/_uploads/BJdy4KXHh.png) ```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 基本列表 ![](https://hackmd.io/_uploads/Hk4r9FQS3.png) ![](https://hackmd.io/_uploads/SJ7rQ9QS2.png) ```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的原理 ![](https://hackmd.io/_uploads/ryFsA5QSh.png) * 1. index作為key,先比對key,一樣的東西續用,不一樣的東西新生成 ![](https://hackmd.io/_uploads/rJVen9XH3.png) * 如果堅持用index作為key,則新增數據一定要加在後面 ![](https://hackmd.io/_uploads/Sy2s39XH3.png) * 2. p.id作為key,先比對key,一樣的東西續用,不一樣的東西新生成 ![](https://hackmd.io/_uploads/ByB_6cmr2.png) ### 12-3 列表過濾 ![](https://hackmd.io/_uploads/Sy2S_BNHh.png) ```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 列表排序 ![](https://hackmd.io/_uploads/HynhrIEB3.png) ```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. 更新頁面 ![](https://hackmd.io/_uploads/BJVzEFEB2.png) ### 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裡作解決 ![](https://hackmd.io/_uploads/BJZW1iErn.png) ```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> ``` ![](https://hackmd.io/_uploads/r107u34rn.png =20%x) * 可以發現數組並沒有get set ![](https://hackmd.io/_uploads/rk-QuhEH3.png =60%x) * 數組操作 ![](https://hackmd.io/_uploads/H1WvK2VH2.png =10%x) * hobby有get set所以可以對這個數組做改變,vue才監測的到 ![](https://hackmd.io/_uploads/HkkRFhVSn.png =40%x) * 1.)vue的push調用js原生push。 2.)vue再重新解析模板 * 重點vue有重新封裝過數組操作 ![](https://hackmd.io/_uploads/H1NS324rh.png =40%x) * Vue.set(vm._data.student.hobby,0,'學習'),這也能改。 ### 12-10 總結Vue監測數據 ![](https://hackmd.io/_uploads/HJTiJ1rH3.png) ![](https://hackmd.io/_uploads/rJ1UJJrS3.png =20%x) ```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 收集表單數據 ![](https://hackmd.io/_uploads/HkL_csSHh.png) ![](https://hackmd.io/_uploads/H1ksq6HB3.png) ```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 過濾器 ![](https://hackmd.io/_uploads/ryeojTBH3.png) ![](https://hackmd.io/_uploads/SkIhOAHrn.png) ```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 ![](https://hackmd.io/_uploads/rJ6e31UBh.png) ```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實例真的串建完成,該屬性則會自動消失。可看例子。 ![](https://hackmd.io/_uploads/r1EHigLH3.png) ```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 * 目的: 得到初始化數值,不受任何變動,依然可以顯示。 ![](https://hackmd.io/_uploads/BkljNG4USn.png) ```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解析。 ![](https://hackmd.io/_uploads/Hkw444LB3.png) ```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元素默認獲取焦點。 ![](https://hackmd.io/_uploads/H1H1NlDSn.png) ```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 ![](https://hackmd.io/_uploads/Bk8ZKxwH2.png) * 這個標題越來越淡,淡到一定程度恢復全黑,再越來越淡。 ![](https://hackmd.io/_uploads/rksBxZwSh.png) ```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整個為模板 ![](https://hackmd.io/_uploads/H1wWZfPB2.png) ![](https://hackmd.io/_uploads/rysf4uPH3.png) * 關注左邊 ![](https://hackmd.io/_uploads/Bk6u5fPSh.png) * 關注右邊 ![](https://hackmd.io/_uploads/ryFAqGvH3.png) ![](https://hackmd.io/_uploads/Sky6JPwrh.png) ```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 生命週期總結 ![](https://hackmd.io/_uploads/Hkn0GOwHn.png) ![](https://hackmd.io/_uploads/BJ42F_wSn.png) ![](https://hackmd.io/_uploads/BJ21qdvrn.png) ```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> ```