Vue2
0、前言
- 首先說明:要直接上手簡單得很,看官網熟悉大概有哪些東西、怎麼用的,然後簡單練一下就可以做出程式來了,最多兩天,無論Vue2還是Vue3,就都完全可以了,Vue3就是比Vue2多了一些東西而已,所以:要快速上手的,濾過本篇部落格,自行檢視Vue官網即可
1、基礎篇
1.1、初識Vue
- 下載Vue.js,連結:https://cn.vuejs.org/v2/guide/installation.html
- 開發版和生產版就字面意思
- Vue開發工具:vscode(IDEA也可以寫Vue程式,用IDEA寫之前最好安裝一個Vue.js外掛,然後就可以寫Vue程式了),vscode編輯器百度下載
- vscode中的外掛和設定自行百度進行配置( 搜尋vscode初始配置即可,然後後續的需要時百度進行配置即可 ),如:瀏覽器開啟方式、Vue快捷模板.........
- 給瀏覽器安裝vuejs devtool工具,阿里雲盤連結:https://www.aliyundrive.com/s/JtHVq5SX3po
- 把資料夾下載了,拖到擴充程式中即可
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>01-初識vue.html</title>
<!-- 0、匯入vue.js -->
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 1、定義一個容器 -->
<div id="app">
<h3> 第一個vue程式,並使用插值表示式取得值為:{{name}} </h3>
</div>
</body>
<script>
// 去除瀏覽器控制檯中的錯誤資訊
Vue.config.productionTip = false;
// 2、初始化Vue容器
new Vue({
el: "#app",
data: {
name: "紫邪情",
age: "18"
}
})
</script>
</html>
啟動效果如下:
初識Vue小結
- Vue容器中的程式碼一樣符合html規範,只不過是混入了一些Vue的特殊用法而已,這個容器中的程式碼被稱為:Vue模板
- Vue例項( new Vue處 )和 Vue容器是一 一對應的( 即:一個Vue例項只能對應一個Vue容器 )
- 當然:真實開發中只有一個Vue例項,後續會見到
- {{xxx}}中的xxx要寫js表示式( {{xxx}}即為插值表示式,就是取data中的東西而已 ),且xxx可以自動讀取到data中的所有屬性
- js表示式 和 js程式碼的區分
- 1、表示式:一個表示式會產生一個值,可以放在任何一個需要值的地方,如:
- a
- a + b
- test( 2 )
- x === y ? 'a' : 'b'
- 2、js程式碼:
- if(){}
- for(){}
- .........
- 1、表示式:一個表示式會產生一個值,可以放在任何一個需要值的地方,如:
- js表示式 和 js程式碼的區分
- 一旦data中的資料發生改變,那麼頁面中使用該資料的地方一樣發生改變
1.2、認識v-bind
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>02-vue之v-bind</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 常規寫法:v-bind繫結的是屬性 -->
<a v-bind:href="url"></a>
<!-- 多層巢狀取值:插值表示式 取的東西就是下面Vue例項new Vue({})
data中的東西,可以理解為在找尋集合中的元素
-->
<h3>{{person.name}}</h3>
<!-- 簡寫 -->
<h3 :mySex="person.sex">{{person.name}}</h3>
</div>
</body>
<script>
Vue.config.productionTip = false;
new Vue({
el: "#app", // el指定的是為哪一個容器服務 值就是一個css中的選擇器
data: { // data儲存的就是資料,這些資料是供el指定的容器去用的
url: "https://www.cnblogs.com/xiegongzi/",
person: {
name: "紫邪情",
sex: "女"
}
}
})
</script>
</html>
啟動效果
1.3、認識Vue的資料繫結
- 這裡需要了解mvvm模式,要理解,可以參照java中的mvc模式( MVVM本來就是借鑑的MVC模式而整出來的 )
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>03-v-model</title>
<script src="../js/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 單向繫結:綁的是屬性 -->
單向繫結:<input type="text" :value="name"> <br/>
<!-- 雙向繫結:綁的是值 -->
雙向繫結:<input type="text" :myName="name" v-model:value="username"> <br/>
<!-- 雙向繫結的簡寫 因為:model綁的就是值,因此:value可以省掉 -->
雙向繫結:<input type="text" :myName="name" v-model="username">
</div>
</body>
<script>
Vue.config.productionTip = false;
new Vue({
el: "#app",
data: {
name: "紫邪情",
username: "邪公子"
}
})
</script>
</html>
效果如下:
測試單向繫結
測試雙向繫結
1.4、el和data的兩種寫法
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>04-vue的el和data的兩種寫法</title>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app"></div>
<script>
// 建立 vm 例項物件
const vm = new Vue({
//指定控制的區域
// el:'#app', // el 常規寫法
// data:{}, // data 常規寫法 ———— 這種也叫物件式寫法
// data 函式式寫法,這種寫法後續經常見
data () { // 這裡使用data:function(){}也可以,但是:絕對不能寫成data:()=>{}
// 因為這種寫法變質了,成為Window物件了,這樣後續用this指向時,這個this所指代的就不Vue例項了
return {
}
}
});
// el 另外的寫法
vm.this.$mount('#app') // 此種寫法需要記住,後續元件化開發還會見到
</script>
</body>
</html>
小結:el和data的兩種寫法
- 1、el
- (1)、new Vue時配置el選項
- (2)、先建立Vue例項,然後再通過vm.this.$mount( '#app' ) 來指定el的值
- 2、data
- (1)、物件式
- (2)、函式式
- 如何選擇哪種用法:目前都可以,但是後續會玩元件,則:必須用函式式,而且不可以用蘭姆達表示式( 即:上面的data:()=>{},否則:會出錯 )
- 3、一個原則:
- 由Vue管理的函式,一定不要寫箭頭函式,就上面的蘭姆達表示式,否則就會導致this不再是Vue例項了( 這個this有大用處,在"new Vue中"用了this就是指的當前的Vue例項,後續可以通過這個this玩很多東西 )
1.5、理解MVVM模型
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>理解MVVM</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2>姓名:{{name}}</h2> <br>
<h2>性別:{{sex}}</h2>
<hr>
<h2>智商:{{1+1}}</h2>
</div>
<script>
// 建立 vm 例項物件
const vm = new Vue({
//指定控制的區域
el:'#app',
data:{
name: "紫邪情",
sex: "女"
},
});
console.log( vm );
</script>
</body>
</html>
檢視程式碼中的MVVM
小結:MVVM模型
- 1、M:Model( 模型 ) ————> data中的資料
- 2、V:View( 檢視 ) —————> 模板程式碼
- 3、ViewModel:檢視模型( ViewModel ) ————> Vue例項
觀察效果發現
- 1、data中所有的屬性最後都在vm身上( 即:ViewModel )
- 2、vm身上所有的屬性 及 Vue原型上的所有屬性,在Vue模板中都可以直接使用
- 顯示原型屬性就是下圖中的這個( 它和另一個隱式原型屬性_ _ proto _ _指向的物件就是原型 )
- 顯示原型屬性就是下圖中的這個( 它和另一個隱式原型屬性_ _ proto _ _指向的物件就是原型 )
1.6、回顧Object.defineProperty()函式
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>05-瞭解object.defineProperty()</title>
<script src="../js/vue.js"></script>
</head>
<body>
<script>
Vue.config.productionTip=false;
let sex = '女'
let person = {
name: '紫邪情'
}
// 引數說明:person 為要修改的物件 sex為具體修改的物件中的哪個屬性 {} 對修改屬性的配置
Object.defineProperty( person , 'sex' , {
// 以下就是{}中的相關配置
// value: '男',
// enumerable: true, // 這個sex是否可以被遍歷
// writable: true, // 這個sex是否可以被修改
// configurable: true, // 這個sex是否可以被刪除
// 當有人獲取sex這個屬性時會觸發這個get()方法
get(){
console.log('有人讀取sex屬性了');
return sex;
},
// 當有人修改sex屬性的值時會觸發這個set方法
set(value){
console.log('有人修改了sex的值');
return sex = value;
}
});
</script>
</body>
</html>
- 注意:去找temp或者值是呼叫了get()方法進行獲取的,相應的set()方法也知道是怎麼回事了
1.7、簡單理解資料代理原理
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>06-簡單理解資料代理</title>
</head>
<body>
<script>
let obj1 = {x:100};
let obj2 = {y:200};
// 資料代理: 通過一個物件代理 對 另一個物件中屬性的操作( 讀 / 寫 )
Object.defineProperty(obj2 , 'x' , {
get(){
console.log("有人獲取了x的值");
return obj1.x;
},
set(value){
console.log("有人修改了x的值");
obj1.x = value;
}
})
</script>
</body>
</html>
原理圖
- 注意:上面這個是簡單瞭解,深入瞭解在後面玩Vue監視資料原理時會再次見到defineProperty()和這個資料代理
1.8、事件繫結及其修飾符
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>07-事件繫結及其修飾符</title>
<script src="../js/vue.js"></script>
</head>
<!--
常用的事件修飾
prevent 表示:阻止預設事件的觸發
stop 表示:阻止事件冒泡
once 表示:只執行一次事件
-->
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<!-- vue中事件繫結的簡單寫法 -->
<button v-on:click="showInfo()">點我顯示提示資訊</button>
<br/>
<!-- vue中事件繫結的簡寫 還可以傳遞引數 -->
<button @click="showInfo2($event, 66)">點我獲取帶參的事件資訊</button>
</div>
<script>
Vue.config.productionTip=false;
// 建立 vm 例項物件
const vm = new Vue({
//指定控制的區域
el:'#app',
data:{},
methods: {
showInfo(){
alert("這是vue中的事件繫結");
},
showInfo2(event , number){
console.log(event + "=====>" + number);
}
}
});
</script>
</body>
</html>
1.9、計算屬性
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>10-計算屬性conputed</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
姓名: <input type="text" v-model="name"> <br/>
性別: <input type="text" v-model="sex">
<hr>
資訊: <span>{{info}}</span>
</div>
<script>
Vue.config.productionTip=false;
// 建立 vm 例項物件
const vm = new Vue({
//指定控制的區域
el:'#app',
data:{
name:'紫邪情',
sex:'女'
},
// 所謂的計算屬性:就是通過已有屬性( 一般為data中的 )計算得來
computed: {
info:{
get(){
console.log('開始呼叫get()');
return this.name + '-' + this.sex
},
set(value){
console.log('開始呼叫set()' + value);
}
}
}
});
</script>
</body>
</html>
計算屬性小結
- 定義:要用的屬性不存在,要通過已有屬性計算得來
- 原理:藉助了object.defineproperty()的getter和setter
- get()什麼時候執行
- 1、初次讀取時會執行一次
- 2、當依賴的資料發生改變時會再次被呼叫
- 優勢:與methods相比,內部有快取機制( 複用 ),效率更高、除錯方便
- 注意:
- 1、計算屬性最終都會在vm( Vue例項 )上,直接讀取即可
- 2、若計算屬性要被修改,那必須寫set()去響應修改,且set()中要引起計算時依賴的資料發生改變( 例子中沒有做這一步,在控制檯輸出後面再執行一步讓依賴資料發生改變就可以了【 使用value.split('-')進行拆分嘛,然後把得到的資料對應元素賦給name和sex即可 】 )
- 另外:計算屬性是可以簡寫的
- 就是把set()去掉,因為:更多時候是讀取,並不修改,同時修改還要去控制檯( 重新整理就又沒了 ),因此:把set()去掉就是計算屬性的簡寫
- 多提一嘴:react的核心思想就是虛擬DOM,而它的原理我個人認為就是計算屬性
1.9.1、結合computed、methods、v-on做一個小Demo
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>11 - 使用計算屬性做一個Demo</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2>你是:{{result}}</h2>
<button @click="changeResult">切換名字</button>
</div>
<script>
// 去除瀏覽器控制檯中的錯誤資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
//指定控制的區域
el:'#app',
data:{
name: true
},
computed:{
result(){
return this.name?'紫邪情':'紫女'
}
},
methods: {
changeResult(){
return this.name = !this.name
}
},
});
</script>
</body>
</html>
但是:上面的程式碼其實是可以做簡化的
- 我們主要做的是圖中的這一步,因此:簡化 ———— 衍生出v-on的另外一個玩法
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>12 - 使用計算屬性做一個Demo 簡化</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2>你是:{{result}}</h2>
<!-- @xxx = 'yyyy' 如果只是做一件簡單的事情,那麼就可以使用下面的方式
因為:v-on是可以支援js表示式的( 注意:不是js程式碼啊 )
但是:如果這個v-on需要做多件事,那麼最好就別這麼玩
如:切換了名字,還要弄一個彈窗( 切換成功 )這種就別玩
-->
<button @click="name = !name">切換名字</button>
</div>
<script>
// 去除瀏覽器控制檯中的錯誤資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
//指定控制的區域
el:'#app',
data:{
name: true
},
computed:{
result(){
return this.name?'紫邪情':'紫女'
}
},
// methods: {
// changeResult(){
// return this.name = !this.name
// }
// },
});
</script>
</body>
</html>
1.10、監視屬性
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>13 - 監視屬性 watch</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2>你是:{{result}}</h2>
<button @click= "name = !name">切換名字</button>
</div>
<script>
// 去除瀏覽器控制檯中的錯誤資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
//指定控制的區域
el:'#app',
data:{
name: true
},
computed: {
result() {
return this.name?'紫邪情':'紫女'
}
},
// 監視屬性 watch 監視的是屬性,也就是data中和computed中的都可以監視
// 現在這種我是用的簡寫形式 ———— 前提是:只需要使用handler()中的東西時,handler後續會用完整形式
watch: {
name( newValue, oldVaule ){ // 表示的是:監視哪個屬性
console.log('屬性發生變化了' , newValue , oldVaule);
}
},
});
// 當然:監視屬性還有一種寫法,就是利用Vue的內建函式
/* vm.$watch( 'name', { // 這裡的name就是指監聽哪個屬性 而且必須是' '引起來的
handler( newValue, oldVaule ){
console.log('屬性發生變化了' , newValue , oldVaule);
}
}) */
</script>
</body>
</html>
- 效果都一樣
監視屬性 watch小結
- 1、當被監視的屬性變化時,回撥函式自動呼叫,進行相關操作
- 回撥函式:指的是:handler() ,第一種寫法也是可以用handler,只是把watch換成了$watch而已,後面步驟其實是一樣的,只是我把第一種給簡寫了而已
- 2、監視的屬性必須存在,才能進行監視
- 3、監視有兩種寫法
- (1)、new Vue時傳入watch配置
- (2)、通過vm.$watch內建函式進行監視
1.10.1、深度監視 - 需要掌握的一種
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>14 - 深度監視</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2>你是:{{result}}</h2>
<!-- 現在這個name就是person裡面的了,這就是需要監視多層屬性變化 -->
<button @click= "person.name = !person.name">切換名字</button>
</div>
<script>
// 去除瀏覽器控制檯中的錯誤資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
//指定控制的區域
el:'#app',
data:{
person:{
name: true
}
},
computed: {
result(){
return this.person.name?'紫邪情':'紫女'
}
},
// 下面這種事監視屬性的完整寫法
watch: {
person:{
// 監視多層結構中所有屬性的變化
deep: true, // 深度監視 就做了這一步操作而已
/*
Vue中watch預設是不可以監視屬性更裡層的,
如:上面person只可以監視person本身這個屬性,理解的話,
可以採用物件空間值來比對,假如:person空間地址是是01x23,
那麼Vue只會監視這個空間變數有沒有發生改變,而內層就不可以監視,
因為:內層中的屬性改變了,但是person這個物件本身並沒有改變
*/
handler(){
console.log('屬性改變了');
}
}
}
});
</script>
</body>
</html>
深度監視小結
- 1、Vue中的watch預設不監視物件內部值得改變( 監視的是一層 )
- 2、配置deep:true可以監視物件內部值得改變( 監視多層 )
- 注意:
- 1、Vue自身可以監視物件內部值得改變,但Vue提供的watch預設不可以
- 這個可以驗證的,最後是繫結在vm上的,那麼在控制檯使用vm去改變那麼頁面上也可以改變,這裡是為了解釋Vue不是不可以監視多層的改變,只是:watch預設不支援
- 2、使用watch時根據資料的具體結構,決定是否採用深度監視
- 1、Vue自身可以監視物件內部值得改變,但Vue提供的watch預設不可以
1.11、computed和watch的坑
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>15 - computed和watch的坑</title>
<script src="../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
姓: <input type="text" v-model="firstname"> <br/>
名: <input type="text" v-model="lastname">
<hr/>
資訊: {{fullname}}
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
//指定控制的區域
el:'#app',
data:{
firstname: '紫',
lastname: '邪情',
fullname: '紫邪情'
},
watch: {
firstname(val){
this.fullname = val + this.lastname;
},
lastname(val){
this.fullname = this.firstname + val;
}
}
});
</script>
</body>
</html>
用computed實現
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>02 - computed實現</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
姓: <input type="text" v-model = "firstname"> <br/>
名: <input type="text" v-model = "lastname">
<hr/>
資訊: {{fullname}}
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
//指定控制的區域
el:'#app',
data:{
firstname: '紫',
lastname: '邪情'
},
computed: {
fullname(){
return this.firstname + this.lastname;
}
}
});
</script>
</body>
</html>
最後執行的效果都是一樣的,而官網中對這二者有著這樣的例項演示
但是:雖然看起來沒區別,可是computed和watch還是有區別的,假如:現在我需要讓姓改了之後,隔1s之後再顯示到資訊那裡。那麼:computed無法做到,但是watch就可以
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>03 - 用watch實現非同步操作</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
姓: <input type="text" v-model = "firstname"> <br/>
名: <input type="text" v-model = "lastname">
<hr/>
資訊: {{fullname}}
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
firstname: '紫',
lastname: '邪情',
fullname: '紫邪情'
},
watch: {
firstname(val){
// 一定需要注意:這裡必須使用蘭姆達表示式() =>{}
/*
前面說過,Vue所管理的函式,切記別用蘭姆達表示式,智慧用函式式
這是為了能夠通過this拿到vm例項的東西而已
但是注意:這裡這個setTimeout()定時函式是Vue所管理的嗎?
不是,而後面需要的val是哪裡的?是Vue例項中的,修改的值
是在Vue例項身上
所以:想要拿到Vue身上的val怎麼弄?頁面的展示都是js引擎幫忙去進行操作 / 找尋的
因此:利用js引擎做文章,讓它找的時候自動去層級查詢,它執行到裡面的this.fullname = val + this.lastname時
會去找this是誰,()=>{}這裡是用的蘭姆達表示式,這就會指向Window物件,而Window上沒有fullname,所以就會
往外找,找到firstname(val)這是函式式,指向的就是Vue例項,也就找到了fullname、val.....
*/
setTimeout( () => {
this.fullname = val + this.lastname;
}, 1000);
},
lastname(val){
this.fullname = this.firstname + val;
}
}
});
</script>
</body>
</html>
- 而利用computed就不可以做到上面的這種非同步操作
computed和watch的區別
- 1、computed能完成的功能,watch絕對都可以做到
- 2、watch能完成的功能,computed不一定可以做到,如:上面舉的例子進行非同步操作
- 原則:
- 1、凡是被Vue所管理的函式,最好都寫成函式式( 即:普通函式 ),這是為了能夠讓this指向Vue例項 或 元件例項物件( 後續會見到 ),這樣就可以通過this拿到它們對應的東西
- 2、凡是不被Vue所管理的函式( 如:定時器裡面的回撥函式、ajax中的回撥函式.... ),最好都寫成蘭姆達表示式( 即:箭頭函式 ),這樣做也是為了能夠讓this指向Vue例項 或 元件例項物件
1.12、樣式繫結
1.12.1、class樣式繫結
假如有如下的一個頁面
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>01 - class樣式繫結</title>
<script src="../../js/vue.js"></script>
<style>
body {
background-image: url(img/bg3.jpg);
text-align: center;
background-size: 100% 100%;
height: 100%;
overflow: hidden;
background-repeat: no-repeat;
background-position: center;
background-attachment: fixed;
}
.top {
background: #ffffff2e;
width: 100%;
position: absolute;
bottom: 0;
line-height: 60px;
left: 0px;
right: 0px;
color: #fff;
text-align: center;
font-size: 16px;
font-weight: 600;
}
.basicLogin {
position: absolute;
top: 16%;
left: 28.5%;
width: 40%;
padding: 70px 2%;
text-align: center;
}
.title {
font-weight: 600;
font-size: 22px;
color: #0000FF;
margin-bottom: 40px;
}
.line {
border-bottom: 1px solid #ffff;
margin: 22px 1%;
width: 96%;
}
.line input {
border: none;
padding: 0px 1%;
margin: 1%;
background: #ffffff14;
width: 84%;
font-size: 16px;
line-height: 30px;
outline: none;
}
.line .smallImg {
width: 26px;
float: left;
vertical-align: middle;
margin-top: 1px;
}
.logBut {
background: #7bb5ee;
padding: 10px 80px;
border: none;
color: #fff;
margin-top: 40px;
font-size: 16px;
cursor:pointer;
}
</style>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<div class="top">©紫邪情 · 用Java改變未來</div>
<div class="basicLogin">
<div class="title">©紫邪情 · 登入</div>
<div class="line">
<img class="smallImg" src="img/icon-4.png" />
<input placeholder="請輸入賬號" type="text" />
</div>
<div class="line">
<img class="smallImg" src="img/icon-5.png" />
<input placeholder="請輸入密碼" type="password" />
</div>
<button type="button" class="logBut">登 錄</button>
</div>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{},
});
</script>
</body>
</html>
-
效果圖如下
-
現在需求1、中間的登入頁樣式,不要這種,想要換一種:假如是如下這種
點選檢視程式碼
.login1 {
background: #ffffffd6;
border-radius: 2px;
}
- 則:切換之後是如下效果
- 可是,看了效果還是不行,又想要另外一種效果,此時:增加一種樣式
點選檢視程式碼
.login2 {
background: rgba(0,0,0,.8);
box-sizing : border-box;
box-shadow: 0 15px 25px rgba(0,0,0,.5);
border-radius: 10px;
}
- 切換之後效果如下
-
因此:此時這個class的名字不確定到底是哪一個,需要動態去繫結,所以通過Vue來動態繫結一下( 即:把class類名交給Vue託管 )
-
class類名不確定,交由Vue託管很簡單,而且知識點前面已經學過了,就是v-bind
點選檢視程式碼
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<div class="top">©紫邪情 · 用Java改變未來</div>
<div class="basicLogin" :class = "unknown" @click = "changeLogin">
<div class="title">©紫邪情 · 登入</div>
<div class="line">
<img class="smallImg" src="img/icon-4.png" />
<input placeholder="請輸入賬號" type="text" />
</div>
<div class="line">
<img class="smallImg" src="img/icon-5.png" />
<input placeholder="請輸入密碼" type="password" />
</div>
<button type="button" class="logBut">登 錄</button>
</div>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
unknown: 'login1'
},
methods: {
changeLogin(){
if( this.unknown === 'login1'){
this.unknown = 'login2'
}else{
this.unknown = 'login1'
}
}
}
});
</script>
</body>
需求2、現在class類名的個數、名字都不確定
- 那麼就繼續改造
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>2 - 陣列寫法</title>
<script src="../../../js/vue.js"></script>
<style>
body {
background-image: url(../img/bg3.jpg);
text-align: center;
background-size: 100% 100%;
height: 100%;
overflow: hidden;
background-repeat: no-repeat;
background-position: center;
background-attachment: fixed;
}
.top {
background: #ffffff2e;
width: 100%;
position: absolute;
bottom: 0;
line-height: 60px;
left: 0px;
right: 0px;
color: #fff;
text-align: center;
font-size: 16px;
font-weight: 600;
}
.basicLogin {
position: absolute;
top: 16%;
left: 28.5%;
width: 40%;
padding: 70px 2%;
text-align: center;
}
.login1 {
background: #ffffffd6;
border-radius: 2px;
}
.login2 {
background: rgba(0,0,0,.8);
box-sizing : border-box;
box-shadow: 0 15px 25px rgba(0,0,0,.5);
border-radius: 10px;
}
.title {
font-weight: 600;
font-size: 22px;
color: #0000FF;
margin-bottom: 40px;
}
.line {
border-bottom: 1px solid #ffff;
margin: 22px 1%;
width: 96%;
}
.line input {
border: none;
padding: 0px 1%;
margin: 1%;
background: #ffffff14;
width: 84%;
font-size: 16px;
line-height: 30px;
outline: none;
}
.line .smallImg {
width: 26px;
float: left;
vertical-align: middle;
margin-top: 1px;
}
.logBut {
background: #7bb5ee;
padding: 10px 80px;
border: none;
color: #fff;
margin-top: 40px;
font-size: 16px;
cursor:pointer;
}
</style>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<div class="top">©紫邪情 · 用Java改變未來</div>
<div class="basicLogin" :class = "unknown" @click = "changeLogin">
<div class="title">©紫邪情 · 登入</div>
<div class="line">
<img class="smallImg" src="../img/icon-4.png" />
<input placeholder="請輸入賬號" type="text" />
</div>
<div class="line">
<img class="smallImg" src="../img/icon-5.png" />
<input placeholder="請輸入密碼" type="password" />
</div>
<button type="button" class="logBut">登 錄</button>
</div>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
unknown: [ 'login1' , 'login2' ]
},
methods: {
changeLogin(){
const arr = [ 'login1' , 'login2' ];
this.unknown = arr[ Math.floor( Math.random() * 2 ) ]
}
}
});
</script>
</body>
</html>
需求3、要繫結的class名字確定,但是:個數不確定,也需要動態繫結
class樣式繫結小結
- 1、字串寫法,適用於:class名字不確定時使用
- 2、陣列寫法,適用於:class名字、個數都不確定時使用
- 3、物件寫法,適用於:class名字確定、但個數不確定( 如:例子中的login1可能用,可能不用,而login2也是一樣,所以這就是各種組合 )
1.12.2、style行內樣式繫結 - 瞭解即可
class和style樣式繫結小結
- 1、class樣式繫結
- 寫法 :class = "xxxx",其中xxxx可以使字串、陣列、物件
- 字串寫法適用於:類名不確定、要動態獲取( 交由Vue管理,隨時除錯 )
- 陣列寫法:要繫結多個樣式、class名字和個數都不確定
- 物件寫法:要繫結多個樣式、class名字確定而個數不確定( 個數不確定是因為不知道某個樣式用不用 )
- 2、style樣式繫結
- 寫法 :style = “xxx",其中xxx可以是物件、陣列
- 物件寫法是推薦用的一種
- 3、原則
- 在Vue中靜態不變的東西,就放在Vue模板中即可( 即:那個div容器中 ),而動態改變的東西就放在Vue例項身上
1.13、條件渲染
1.13.1、v-if
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>1 - v-if</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<!-- v-if和java中的if一樣,只要結果為true就執行,為false就不執行
所以:這裡面不用true,用 1 === 1 也行,
當然:使用v-bind繫結,寫到data中,只要最後結果為boolean值即可
相應地:有了v-if,那當然也有v-else-if、v-else( 不過這個有點特殊
這是什麼條件都不滿足的時候,最終執行的[ 類似於java的try....catch....finally
中的finally ]
-->
<div v-if = "true">
<img :src="result" alt="不見了影響你開法拉利不?" :style="obj">
</div>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
result: 'img/19.jpg',
obj: {
width: '500px',
hight: '500px'
}
},
});
</script>
</body>
</html>
- 注意:v-if、v-else-if、v-else組合用時,需要留意程式碼是緊挨著的即可,這就好比:java的Mybatis框架的pageHelper分頁外掛,用startPage和pageInfo聯合使用一樣( 資料查詢完了之後放到pageInfo<>()中,這二者中間就不可以夾雜另外的語句,否則報錯 )
1.13.2、v-if配套工具template
- 在弄這個之前,回到前面的v-if程式碼
- 所以:使用v-if和template改造
1.13.3、v-show
-
v-if會了,v-show就會了,用法和單純的v-if一模一樣
-
不過它和v-if的原理不一樣
v-if和v-show小結
- 1、v-if
- 寫法
- (1)、v-if = “表示式”
- (2)、v-else-if = “表示式”
- (3)、v-else = “表示式”
- 適用於:切換頻率較低的場景,因為:v-if是直接把不展示的DOM元素給移除掉
- 特點:不展示的DOM直接移除
- 注意:v-if可以和v-else-if、v-else一起使用,但是:要求結構不可以被“打斷”( 程式碼緊挨著 )
- 寫法
- 2、v-show
- 寫法:v-show = “表示式”
- 適用於:切換頻率較高的場景
- 特點:不展示的DOM元素未被移除,僅僅是使用display:none樣式給隱藏掉了而已
- 3、注意:使用v-if時,元素可能無法獲取到,而使用v-show,則:一定可以獲取到
- 因為:v-show是做了樣式修改,但是DOM節點還在,所以是可以操作這個DOM節點的,但是:v-if是直接把DOM元素給移除掉了( 萬一是誤操作而導致把v-if的值弄為false了,那後續萬一其他地方用到了v-if移除的節點呢?不就裂開了嗎 )
1.14、列表渲染
1.14.1、認識v-for
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>1 - 認識v-for</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<!-- 1、遍歷陣列 -->
<h2>人員資訊</h2>
<!-- 簡寫形式 -->
<ul>
<li v-for = "p in persons" :key="p.id">
{{p.name}} ----- {{p.age}}
</li>
<!--
v-for就和js中的for in差不多
:key="p.id" 此處:是給每一個li節點一個唯一的id( 和身份證號一樣 就相當於是<li id = ‘xxx’> ),此時不寫沒事,但是最好都帶上,後續有用
p 代表的就是:從persons中遍歷出來的每一條資料
-->
</ul>
<!-- 完整寫法 -->
<ul>
<li v-for = "(val,index) in persons" :key="val.id">
{{val.name}} ----- {{val.age}} ------ {{index}}
</li>
</ul>
<!-- 2、遍歷物件 -->
<h2>神奇之地</h2>
<ul>
<li v-for = "(val,index) in like" :key="index">
{{val}} ------ {{index}}
</li>
</ul>
<!-- 其實另外還有:v-for還可以遍歷字串、某個數字( 迴圈這個數字這麼多次 ),但是這兩種基本上都不用,所以不說明了 -->
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
persons: [
// 陣列中又放得有物件
{'id':'10001','name':'紫邪情','age':'18'},
{'id':'10002','name':'紫女','age':'18'},
{'id':'10003','name':'邪公子','age':'19'}
],
like: {
name: '香牌坊',
ko:'范冰冰'
}
},
});
</script>
</body>
</html>
v-for小結
- 語法:v-for = “ ( intem,index ) in xxx ” :key = “yyy”
- 可遍歷:陣列、物件、字串( 用得少 )、指定次數( 用得更少 )
- 可用於:展示列表資料
1.14.2、key值的坑( 作用與原理 )
- 在前面v-for中key值使用index時有一些坑的,正確的做法是:最好使用資料的id作為key值,但不是說index作為key值就是錯的,前面演示過了,是正常效果,可是:在特定的場景下使用 :key = "index"就會出問題
例項
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>2 - key值的坑</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2>人員資訊</h2>
<ul>
<!-- 這裡使用index作為key值 -->
<li v-for = " (p , index) in persons" :key="index">
{{p.name}} ----- {{p.age}} <input type="text">
</li>
</ul>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
persons:[
{'id':'001' , 'name':'張三','age':'18'},
{'id':'002' , 'name':'李四','age':'19'},
{'id':'003' , 'name':'王五','age':'20'}
]
},
});
</script>
</body>
</html>
-
效果如下
-
現在有這麼一個需求:想要在“張三”的前面新增一條“老劉”資料( 這裡使用頁面button新增,不是直接在data中新增)
-
效果如下
- 現在需要在做一件事情
-
為什麼會發生上面的問題,來看看原理圖,瞭解用index做key值的坑
-
瞭解了上面的index作為key值,那麼用資料id作為key值也懂了
-
其中:圖中的對比演算法就是所謂的“diff演算法”
-
而不寫key的話,Vue做了一件預設的操作,就是把遍歷的index作為了key值,也就是和前面使用index作為key值是一樣的效果了
把以上的內容,換成文字小結一波
-
Vue中的key有什麼作用( 原理是什麼? )【 不止Vue啊,React中的key也是同樣的原理,前面說過React核心就是虛擬DOM 】
-
1、虛擬DOM中key的作用
- key是虛擬DOM物件的標識,當狀態中的資料發生改變時,Vue會根據【 新資料 】生成【 新的虛擬DOM 】
- 隨後Vue進行【 新虛擬DOM 】 與【 舊虛擬DOM 】的差異比較( 對比演算法 / diff演算法 )
-
2、對比演算法的規則
- (1)、舊虛擬DOM中找到了與新虛擬DOM相同的key
- 若虛擬DOM中內容沒變,則:直接使用之前的真實DOM
- 若虛擬DOM中內容變了,則:生成新的真實DOM,隨後替換掉頁面中之前的真實DOM
- (2)、舊虛擬DOM中未找到與新虛擬DOM相同的key
- 直接建立新的真實DOM,雖然渲染到頁面
- (1)、舊虛擬DOM中找到了與新虛擬DOM相同的key
-
3、用index作為key值可能會引發的問題
-
(1)、若對資料進行:逆序新增、逆序刪除.....等破壞資料順序的操作
- 則:會產生不必要的真實DOM更新 即:介面顯示沒問題,但:效率低( 效率低是因為:最後生成真實DOM時,文字節點是重新生成的 )
-
所謂的逆序操作就是資料新增在前面( 如:例子中是新增在“張三”前面的,但是:在最後新增( 即:在例子中“王五”後面新增資料,這種在最後面新增,則:在新虛擬DOM中生成真實DOM時,順序是對的,不會出現上述問題的 )
-
(2)、若結構中還包含輸入類的DOM( 如:例子中的input )
- 則:會產生錯誤DOM更新 即:頁面中資料錯位
-
-
4、開發中如何選擇key?
- (1)、最好使用資料自己的唯一標識作為key值,比如:手機號、身份證號.....( 直接利用資料庫中的主鍵id也行 )
- (2)、如果不存在對資料逆序新增、逆序刪除等破壞資料順序的操作,只用來渲染列表頁面,則:使用index作為key是莫問題的
-
1.1.4.3、列表過濾(查詢功能 )
1、使用watch實現
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>3 - 列表過濾( 查詢功能 )</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<H2>人員資訊列表</H2>
搜尋:<input type="text" placeholder="請輸入搜尋詞" v-model="keyWord">
<ul>
<li v-for = " p in filterPersons" :key="p.id">
{{p.name}} -------- {{p.age}}
</li>
</ul>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
// 1、蒐集搜尋框中的內容 搜尋框內容變,這裡的內容也要變,所以:雙向繫結v-model
keyWord: '',
// 2、準備初始資料
persons: [
{'id':'10001','name':'紫邪情','age':'18'},
{'id':'10002','name':'紫邪晴','age':'19'},
{'id':'10003','name':'邪公子','age':'20'},
{'id':'10004','name':'公孫策','age':'21'},
{'id':'10005','name':'孫悟空','age':'22'}
],
// 4、需要準備另一個容器來裝過濾之後的資料
filterPersons: []
},
// 3、使用watch來實現搜尋效果
/*
需要做兩件事
(1)、拿到data的keyWord中的內容
(2)、使用keyWod中的內容 去 persons中進行過濾篩選,從而把結果渲染到頁面
*/
watch: {
keyWord: {
// 這個的作用:頁面初始化時就執行一次handler()
immediate: true,
handler(newVal){
// 1)、過濾資料 把結果放到filterPersons中去
this.filterPersons = this.persons.filter( (p)=>{
// 沒在陣列中indexOf()返回的就是-1
return p.name.indexOf(newVal) !== -1
})
}
}
}
});
</script>
</body>
</html>
效果如下
2、使用computed實現
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>3 - 列表過濾( 查詢功能 )</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<H2>人員資訊列表</H2>
搜尋:<input type="text" placeholder="請輸入搜尋詞" v-model="keyWord">
<ul>
<li v-for = " p in filterPersons" :key="p.id">
{{p.name}} -------- {{p.age}}
</li>
</ul>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
// 1、蒐集搜尋框中的內容 搜尋框內容變,這裡的內容也要變,所以:雙向繫結v-model
keyWord: '',
// 2、準備初始資料
persons: [
{'id':'10001','name':'紫邪情','age':'18'},
{'id':'10002','name':'紫邪晴','age':'19'},
{'id':'10003','name':'邪公子','age':'20'},
{'id':'10004','name':'公孫策','age':'21'},
{'id':'10005','name':'孫悟空','age':'22'}
],
},
// 3、使用computed來實現搜尋效果
/*
需要做兩件事
(1)、拿到data的keyWord中的內容
(2)、使用keyWod中的內容 去 persons中進行過濾篩選,從而把結果渲染到頁面
*/
computed: {
filterPersons() {
return this.persons.filter( (p)=>{
return p.name.indexOf( this.keyWord ) !== -1
})
}
}
});
</script>
</body>
</html>
效果如下
1.14.4、列表排序
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>4 - 列表排序</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2>人員資訊</h2>
搜尋: <input type="text" v-model = "keyWoed" placeholder="請輸入關鍵字">
<button @click = "sortType = 1">升序</button>
<button @click = "sortType = 2">降序</button>
<button @click = "sortType = 0">原順序</button>
<ul>
<li v-for = " p in filterPersons" :key="p.id">
{{p.name}} ----- {{p.age}}
</li>
</ul>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
keyWoed: '',
sortType: 0,
persons: [
{'id':'10001','name':'紫邪情','age':'18'},
{'id':'10002','name':'紫邪晴','age':'14'},
{'id':'10003','name':'邪公子','age':'121'},
{'id':'10004','name':'公孫策','age':'21'},
{'id':'10005','name':'孫悟空','age':'42'}
]
},
computed: {
filterPersons(){
/*
排序:就是將列表過濾之後的資料 根據特定欄位排序之後,再渲染到頁面中即可
因此:return this.persons.filter這裡不能直接用return,不然就直接把過濾的資料渲染到頁面了
所以:再對這一步做一下排序操作
*/
// 先用個陣列裝起來
const arr = this.persons.filter( (p)=>{
return p.name.indexOf( this.keyWoed ) !== -1;
})
// 然後對陣列進行排序操作,但是得先知道使用者是點選的哪個按鈕( sortType )
if (this.sortType) {
arr.sort( (previous,last)=>{
return this.sortType == 1 ? previous.age - last.age : last.age - previous.age;
})
}
return arr;
}
}
});
</script>
</body>
</html>
效果如下
- 這裡有個容易鑽牛角尖的地方
1.15、Vue監視資料的原理
1.15.1、監視資料失效例項
先來看看Vue檢測屬性失效的問題,從而引申處Vue監視資料的原理
先來玩正常版
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>1 - 監視資料失效例項</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2>Vue監視資料失效問題</h2>
<ul>
<li v-for = "p in persons" :key="p.id">
{{p.name}} ---- {{p.age}}
</li>
</ul>
<button @click = "update">修改資料</button>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
persons: [
{'id':'10001','name':'紫邪情','age':'18'}
]
},
methods: {
update(){
this.persons[0].name = '紫女';
}
}
});
</script>
</body>
</html>
- 效果如下
現在變一下,整出bug
- 上面的問題是怎麼回事?要知道這個的原因就需要了解一下Vue監視資料的原理了,可是裝資料的型別有什麼?物件 { } 和 陣列 [ ] ,所以:這裡又得去了解這兩種型別的原理,當然:像什麼data中還可以放字串這就不用說明了,瞭解了物件和陣列,字串也就可以反應過來了
1.15.2、Vue監視物件的原理
-
玩這個就需要又回到前面玩過的“資料代理”了
-
前面就說過,這個資料代理,只是簡單瞭解而已,上面的流程其實不算對,因為少了一個步驟,整個流程應該是:自己寫的data ————> 對data進行加工 —————> 把data中的資料給_data ————> 再把_data的內容給頁面需要的data中( 即:這一步相當於執行了 vm._data = data , 這個data就是頁面上的data嘛,所以就成功地讓頁面中的資料跟著自己的修改而改變了,但是底層不是用 vm._data = data,方法名不一樣,而且還要更復雜一點
-
來回顧一下例子
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>3 - 回顧資料代理</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2> 姓名: {{person.name}} </h2> <br/>
<h2> 性別: {{person.sex}} </h2>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
person: {
name: '紫邪情',
sex: '女'
}
},
});
</script>
</body>
</html>
根據上面的原理,簡單模擬一個vue的資料監視
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>4 - 模擬Vue資料監視</title>
</head>
<body>
<script>
// 準備一個物件
let data = {
name: '紫邪情',
sex: '女'
}
// vue做了精妙之一就是:設計了一個構造方法Observer(傳物件名),這個東西剛剛在_data中見過它,開啟就是用Observer開啟的
function Observer(obj) {
// 拿到data中的所有key值( 即:上面的name、sex ),放到一個陣列裡面去
const keys = Object.keys( obj );
// 遍歷這些key值,得到每一個key
keys.forEach( (key) => {
// 使用defineProperty()做資料代理 這裡的this就是指:傳的物件
Object.defineProperty( this, key , {
get(){
return obj[key];
},
set(val){
console.log( "值被修改了,要開始解析資料、生成虛擬DOM、進行對比演算法、響應資料了");
obj[key] = val;
}
})
});
}
// 呼叫設計的構造方法,從而開始做 data 轉 _data
const objs = new Observer(data);
let vm = {};
vm._data = data = objs
console.log( objs );
</script>
</body>
</html>
- 效果如下
- 當然:上面只是簡單流程,甚至多層物件的data加工( 一個物件中又有另一個物件 / 陣列,這樣遞迴下去,所以Vue做得完善一點就是再對物件進行了遞迴,直到不是物件了為止 ),同時:這個例子也沒有弄“資料修改再次解析模板、生成虛擬DOM、響應資料的事 ,研究得明明白白,全整得出來的話,我還坐起,早飄了!!! )
1.15.3、瞭解Vue.set( target ,key , val )
例項
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>5 - 瞭解Vue.set( target, key, val )</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2> 姓名: {{person.name}} </h2> <br/>
<h2> 性別: {{person.sex}} </h2> <br/>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
person:{
name: '紫邪情',
sex: '女'
}
},
});
</script>
</body>
</html>
-
效果如下
-
現在有個需求:需要新增一個屬性 age: 18,不可以直接新增在data中,這樣就沒意義了
-
當然:還有一種方式也可以實現
-
上面這是正常情況,我說了target這個引數有坑,官網有介紹
- 就來玩一下這個坑吧
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>6 - target引數的坑</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2> 姓名: {{name}}</h2> <br/>
<h2> 性別: {{sex}}</h2> <br/>
<h2 v-if = "age"> 年齡: {{age}}</h2>
<button @click = "addAge">新增年齡屬性</button>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
// 把這裡的屬性換一下位置,不用person{}物件包起來,直接放到data中
name: '紫邪情',
sex: '女'
},
methods: {
addAge(){
this.$set( this.data, 'age', 18);
}
}
});
</script>
</body>
</html>
- 這就是target這個引數的坑:target不可以直接指向vm這個例項物件,更不可以把要操作的物件指向data這個根資料物件上
1.15.4、Vue監視陣列資料的原理
寫個正常例子,去控制檯看陣列的結構
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>1 - 正常例項 - 控制檯檢視結構</title>
<script src="../../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2>愛好</h2>
<ul>
<li v-for = "(h , index) in hobby" :key="index">
<h2> {{h}} </h2>
</li>
</ul>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
hobby: [ '吃', '喝', '嫖', '賭', '要不得']
},
});
</script>
</body>
</html>
-
那麼我們在控制檯把資料改了呢?會不會響應到頁面上?
-
上面這個問題就和前面一開始的問題一樣了:監視資料失效的問題
-
這二者都是使用的"索引"來找的陣列的具體某個值,而這:恰恰就是一個坑,想一下在Vue中我們運算元組是怎麼做的?是通過陣列調了對應的API,如:arr.push()、arr.shift()........,那Vue怎麼知道我調的是哪個API( 換句話說:它怎麼知道這個API是做什麼的 / 這個API是陣列中的 ),要搞這個問題,就要看看在js中是怎麼去調陣列的API的?
-
試驗一下:這7個API是否可以做到監視資料的效果
-
答案肯定不是
-
而上面探索的結果,在Vue官網說的有
所以:Vue中要監視到陣列的變化,呼叫下面的7個API中的一個就可以
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()
一定是上面的7個API中的一個才可以嗎?
- 答案也肯定不是,前面玩了Vue.set( target, key, val )和vm.$set( target, key, val )也可以做到啊,只需要把target轉成對應的物件即可( 只要找他陣列中要監視到的物件就行嘛
1.15.5、Vue監視資料原理總結
Vue監視資料的原理
- 1、vue會監視data中所有層級的資料
- 2、Vue如何監視物件中的資料?
- 通過setter實現監視,且要在new Vue時就傳入要監視的資料
- (1)、物件中後追加的屬性,Vue預設不做響應式處理
- (2)、如果需要對後新增的屬性做響應式,則:通過如下API就可做到
- vue.set( target, key, val ) 說明一下:其中的key可以是屬性名,也可以是陣列的下標值
- vm.$set( target, key, val ) key和上面一樣
- 3、Vue如何監視陣列中的資料?
- 通過封裝 / 包裹陣列更新元素的方法來實現,這個封裝的方法做了兩件事
- (1)、呼叫陣列原生的同名API,對陣列進行更新
- (2)、重新解析資料、生成新的虛擬DOM、進行對比演算法、響應頁面
- 通過封裝 / 包裹陣列更新元素的方法來實現,這個封裝的方法做了兩件事
- 4、在Vue中要修改陣列中的某個元素時,只能使用如下的方法
- (1)、push()、pop()、shift()、unshift()、splice()、sort()、reverse()
- (2)、vue.set( target, key, val ) 或 vm.$set( target, key, val )
- 注意:vue.set( target, key, val ) 和 vm.$set( target, key, val ) 不能給vm( 即:vue例項 ) 或 根資料物件data 進行新增屬性操作
- 5、前面分析物件的原理時說過一個流程:
- 資料發生改變,是通過setter來獲取改變的資料,從而重新解析模板、生成新的虛擬DOM、進行對比演算法、響應頁面。在這裡面有一個重要的流程:就是1、資料改變、setter獲取到改變後的值、2、setter重新解析模板,這裡1 ——> 2這一步有個專業的詞叫做:資料劫持,劫持劫持嘛,半路把東西拿走了唄( 就是setter把資料劫走了,它去做後續的操作 )
1.16、input輸入框中v-model的使用技巧
1、text型別
2、radio單選框型別
- 原因:
- 1、v-model收集的是value值
- 2、而radio是選擇,是使用checked = true / false來做到是否選中的
- 3、因此:給此種radio型別的input新增一個value屬性即可
3、checkBox多選框型別
4、select與option下拉框型別
5、textarea型別
6、checkBox的另一種型別:做協議勾選的
7、做了上面這些就可以把資料收集起來了
8、v-model的修飾符
-
前面說過事件的修飾符,
- prevent 表示:阻止預設事件的觸發
- stop 表示:阻止事件冒泡
- once 表示:只執行一次事件
-
而v-model也有修飾符,就3個而已:number、lazy、trim,看名字大概就知道是幹嘛的
(1)、number修飾符,這個修飾符一般都是和input的number型別一起使用的,如:增加一個年齡
-
去控制檯檢視,會發現一個問題
-
因此:需要把number弄為number型別
(2)、lazy修飾符,這個修飾符就游標離開之後再收集資料,拿textarea來舉例
- 所以這種情況應該用lazy來修飾,讓游標離開當前輸入框之後再收集資料
(3)、trim修飾符,這個在java的String中見過,再熟悉不過了,就是去除前後的空格,不演示了
9、使用v-model收集表單資料小結
- 若: < input type = "text" /> ,則:v-model收集的是value值,而使用者輸入的就是value值
- 若:< input type = "radio" /> ,則:v-model收集的是value值,且要給標籤配置value屬性
- 若:< input type = "checkBox" />
- 1、沒有配置input的value屬性,那麼收集的就是checked( true / false,是否勾選 )
- 2、配置input的value屬性:
- (1)、v-model的初始值是非陣列,那麼收集的就是checked
- (2)、v-model的初始值是陣列,那麼收集的就是value組成的陣列
- v-model的3個修飾符
- number 將輸入的字串轉為 有效的數字
- lazy 失去游標焦點才收集資料
- trim 去掉首尾的空格
1.18、Vue內建指令
回顧一下:已經學了Vue的哪些內建指令
- v-on
- v-bind
- v-model
- v-if 、v-else-if、v-else
- v-show
另外還有哪些內建指令?
- v-text
- v-html
- v-cloak
- v-once
- v-pre
- 以及可以自定義指令
接下來就對上面還未見到的內建指令做一下演示和說明
1.18.1、v-text指令
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>1 - v-text指令</title>
<script src="../../js/vue.js"></script>
<!-- 回顧已經學過的Vue內建指令
v-on
v-bind
v-model
v-if、v-else-if、v-else
v-show
-->
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<!-- 插值表示式 -->
<h2> {{name}}</h2>
<!-- v-text指令
這和java中的springboot推薦的thymeleaf模板中的th:text很像
-->
<h2 v-text="name"></h2>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
name: '紫邪情'
},
});
</script>
</body>
</html>
- 效果如下
- 但是:v-text和插值表示式有區別
-
這就是插值表示式和v-text指令的最大區別,v-text是將v-text的值去找尋之後,把內容回填到對應的標籤裡面,是內容全覆蓋,所以:例子中就算寫了“資訊*兩個字,但是:也會被v-text值的返回內容給全覆蓋了
-
當然:還有一個注意點
- v-text和插值表示式都不可以解析html標籤
v-text內建指令小結
- 作用:向其所在的節點中渲染“文字”內容
- 與插值表示式的區別:v-text會替換掉節點中的所有內容,而{{xxx}}插值表示式則不會
1.18.2、v-html內建指令 - 根據需要慎用
先說用法,其實和v-text差不多,但是內在區別很大
- 用法簡單,但是:謹慎用就體現在可以解析html標籤上,不安全
-
這裡涉及到cookie的機制( 後端理解就對照玩大系統、分散式時的jwt單點登入流程原理 )
-
去訪問一個需要輸入資訊的網站時( 如:要輸入使用者名稱、密碼之類的重要資訊 ),資訊在你要登入的網站的伺服器上核對成功之後,伺服器返回的除了會把使用者名稱、密碼這些後續需要的東西返回回來之外,還會返回一些伺服器響應的特殊json字串,從而憑藉伺服器返回的這些資訊,你才可以進入到相應的頁面,而這些資訊就儲存在瀏覽器的cookie中,如下圖:
-
而伺服器返回的json資訊有大用處,你的重要資訊就儲存在cookie中,比如:成功登入之後,操作其他的功能就不會要求再次輸入使用者名稱、密碼之類的( 個別除外,那是另外設計機制 ),這裡聰明點的就會想到另一個東西:跨瀏覽器不會出現無賬號密碼就可以訪問澀,比如:我在google瀏覽器登入之後,再用Edge瀏覽器開啟,那Edge是不會登入的澀,但是:利用v-html就可以做到竊取你在google瀏覽器中登入之後伺服器給你返回的json資訊,然後把這些json資訊放到Edge瀏覽器中,那麼Edge瀏覽器就不需要做登入操作,一樣可以登入進去
-
而使用v-html能夠獲取到cookie是因為:可以在用v-html獲取的內容中夾雜一些獲取你當前伺服器的cookie,然後拼接到竊取人的伺服器地址的後面( 比如:網頁中的惡意廣告,那廣告詞中的詞條是通過v-html渲染上去的,那麼一點選就會把你電腦上的cookie獲取到然後轉發到他自己的伺服器上去 )
-
當然:現在好一點的瀏覽器都為了防止xss攻擊( 模擬使用者之手 ),所以:對瀏覽器做了一些安全限定,最典型的就是google瀏覽器,預設是不可以攜帶cookie轉發的,但是有些做的不好的瀏覽器( 網站 )就可能沒弄這些,一點然後跳過去就遭殃了
-
v-html小結
- 作用:向指定節點中渲染包含html結構的內容
- 與插值表示式的區別
- 1、v-html會替換掉節點中所有的內容,而插值表示式{{xx}}則不會
- 2、v-html可以識別html結構
- 特別注意:v-html有安全性問題
- 1、在網站上動態渲染任意html是非常危險的,容易導致XSS攻擊
- 2、一定要在可信的內容上使用v-html,此指令永遠不要用在使用者提交的內容上!!!!!!
1.19.3、v-cloak指令
- 這是專門用來處理頁面渲染時因網路或其他原因導致頁面無法及時重新整理出來,把頁面載入過程給顯露出來了( 等到後續懂了Vue的生命週期時,此命令會更容易懂 ,當然:現在也很容易懂)
例項
-
把載入js的程式碼移動一個位置
-
然後把瀏覽器的網速調一下
-
然後重新重新整理頁面,會發現頁面的name會有一個稍微慢一點的頁面載入過程,是從 {{name}} ——————> 紫邪情,這裡不方便演示,就不弄了
-
而為了解決這個問題,可以使用v-cloak指令,本質是操作CSS罷了
v-cloak指令小結
- 本質是一個特殊屬性,Vue例項建立完畢並接管容器後,會刪掉v-cloak屬性
- 使用CSS配合v-cloak指令可以解決網速慢時頁面展示出{{xxx}}插值表示式 / axios互動延遲的問題
- axios就是另類的ajax,甚至和ajax差不多一模一樣,而Vue本身並不支援互動,所以才需要藉助axios外掛來進行互動,這也是玩了Vue之後必須掌握的一個知識點,對於學java的人來說太簡單了,上手的話,看一眼就懂了,那就是一個鏈式呼叫罷了,就和StringBuilder一樣鏈式調,只是需要傳一些東西進去而已,而傳的東西在ajax中都見過,而如果鏈式調起來看著不舒服,那就封裝一下就OK了( 當然:只學了Vue,然後使用ajax一樣可以進行資料傳送,只是axios更輕小而已 )
1.19.4、v-once指令
例項
- 因此:使用v-once實現效果
v-once指令小結
- v-once所在節點在初次動態渲染後,就視為靜態內容了
- 以後資料的改變不會引起v-once所在結構的更新,可以用來做效能優化
- 注意:和事件修飾符中的once區別開
- 事件中的once修飾符是說的事件( 函式 / js表示式)只執行一次
- 而v-once指令是說的對div容器中的模板只動態渲染一次
1.19.5、v-pre指令
例項
v-pre小結
- v-pre是跳過其所在節點的編譯過程,也就是:原本是什麼樣子,那麼Vue就拿到的是什麼樣子,不會再去解析它
- 可利用它跳過:沒有使用指令語法、沒有使用插值表示式的節點,從而加快編譯,優化效能
1.19.1、函式式定義
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>1 - 函式式定義</title>
<script src="../../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2>原來的數值為: {{number}}</h2>
<hr/>
<!-- 使用自定義指令 -->
<h2> 擴大10倍之後的數值: <span v-big = "number"/> </h2>
<hr/>
<button @click = "number ++"> <h4>number+1</h4></button>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
number: 10
},
// 設定自定義指令 - 函式式
// directives裡面是可以寫N多個自定義指令的,加了s嘛
directives: {
/*
element 是自定義指令所在結構的HTML標籤元素( 節點 )
binding 是模板中自定義指令和HTML元素進行的繫結的資訊,在這個繫結中有一些需要的東西
注:次繫結和v-bind中的繫結不是一個概念
v-bind是指的HTML元素內的"屬性"和值的繫結
*/
big(element , binding){
element.innerText = binding.value * 10
}
}
});
</script>
</body>
</html>
注意點:上面自定義的指令什麼時候會被呼叫?
- 1、毫無疑問,HTML元素和自定義指令成功繫結時,自定義指令就會被呼叫,注意:這句話有坑,待會兒下一種自定義方式就會弄到這句話
- 看到的效果就是初始化頁面一上來就呼叫了一次,但是裡面的門道不是這樣的,成功繫結是什麼意思,下一種自定義指令再說明
- 2、自定義指令所在的模板( Div容器 )被重新解析時會被呼叫
- 別混淆了此種說法:自定義指令所依賴的資料發生改變時就會呼叫自定義指令,此種說法是錯的
來個例項
1.19.2、物件式定義
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>2 - 物件式定義</title>
<script src="../../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<!-- 現在做一件事情:模擬lable for標籤效果 就是:游標位置鎖定 別在意設計好不好-->
<h2>計數: {{count}}</h2>
<button @click = "count++">地址: </button>
<input type="text" v-findfocus:value= "address">
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
count: 1,
address: '中國大陸'
},
directives: {
// 先用函式式來玩一下 注意:自定義指令 多個單詞別整駝峰命名啊,識別不了的
findfocus( node, binding){
console.log(node , binding);
node.value = binding.value;
// 獲取焦點
node.focus();
}
}
});
</script>
</body>
</html>
-
現在再加一點要求:就是頁面一初始化時,焦點就在input框中,此時發現貌似做不到( 用js可以做到啊,只是現在是用Vue ),為什麼Vue不行?因為這要考慮到Vue的載入順序問題( 重點咯,開始衍生出Vue的生命週期和鉤子函式咯,也就可以很容器理解前面使用v-cloak解決頁面閃爍的問題了)
-
所以:應該把focus()獲取焦點,放到頁面渲染時再做,不然:無法聚焦
-
相應的,用函式式並不能做到,因為:函式式直接一條龍在記憶體執行完了,所以:需要使用物件式自定義指令來解決這種小細節問題
-
update()中不寫邏輯目前是不報錯的,但是:最好還是寫上
1.19.3、自定義指令的兩大坑和補充
1、命名的坑
-
這個坑在前面玩物件式時已經說過了,多個單詞的名字別採用駝峰命名,但是:只說了這一點,並沒有說正確形式應該怎麼命名
-
現在採用駝峰命名看一下這個坑
- 多個單片語成的指令名正確形式,採用 - 分隔開,指令定義中迴歸js原始配置
2、this指向的坑
-
為什麼自定義指令中的this是window物件?
- 這是因為:自定義指令已經不是Vue自己本身的東西了,而是根據需要自行設計,因此:此時你要操作的是HTML的DOM節點,因此:為了方便,Vue就把this還回來了,重新給了window
-
this變了,那想要拿到Vue中的資料咋辦?
3、補充:自定義全域性指令
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>4 - 全域性指令配置</title>
<script src="../../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2>計數: {{count}}</h2>
<button @click = "count++">地址: </button>
<!-- 正確形式命名 採用 - 分隔開 -->
<input type="text" v-find-focus:value= "address">
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 定義全域性指令 - 物件式
/*
第一個引數 指令名
第二個引數 配置項( 就是把前面的物件配置{}這個花括號整體搬過來了而已 ),函式式也是一樣的
只不過函式式需要使用 function給包裹起來而已
*/
Vue.directive('find-focus',{
// 1、HTML元素和指令進行繫結時執行的函式
bind(node, binding){
node.value = binding.value;
},
// 2、指令所在元素被插入被解析完畢,插入到頁面後執行的函式
inserted(node, binding){
node.focus();
},
// 3、指令所在模板被重新解析時執行的函式
update(node, binding){
node.value = binding.value;
}
})
/* // 函式式全域性指令
Vue.directive('find-focus', function(){
// 1、HTML元素和指令進行繫結時執行的函式
bind(node, binding){
node.value = binding.value;
},
// 2、指令所在元素被插入被解析完畢,插入到頁面後執行的函式
inserted(node, binding){
node.focus();
},
// 3、指令所在模板被重新解析時執行的函式
update(node, binding){
node.value = binding.value;
}
})
*/
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
count: 1,
address: '中國大陸'
},
});
</script>
</body>
</html>
- 建立了全域性指令之後,就可以擺脫只能在一個div容器中使用前面那種區域性指令的缺陷了,也就是:現在可以再開一個div容器,然後在這個新開的div容器中一樣可以使用全域性配置中的東西
- 全域性配置東西基本上都是這麼玩的,後續玩元件會用到
1.19.4、指令總結
-
1、定義語法
-
(1)、區域性指令
-
物件式 new Vue({ directives: { 指令名: { 配置 } } }) 函式式 new Vue({ directives: { 指令名(){ 配置 } } })
-
(2)、全域性配置
-
物件式 Vue.directive( '指令名',{ 配置 }) 函式式 Vue.directive('指令名', function(){ 配置 })
-
-
配置中常用的3個回撥
- (1)、bind( element, binding ) 指令與元素成功繫結時呼叫
- (2)、inserted( element, binding ) 指令所在元素被解析完、插入到頁面時呼叫
- (3)、update( element, binding ) 指令所在模板被重新解析時呼叫
-
注意項:
- (1)、指令定義時不加 v- ,但使用時必須加 v-
- (2)、指令名如果是多個單詞,要使用 - 短橫線分隔開,切忌使用駝峰命名( 包括大駝峰和小駝峰 )
-
-
1.20、Vue生命週期
1.20.1、瞭解Vue生命週期的概念
做這麼一個效果 —— 文字動態透明
- 先用不對的方式來做一下
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>1 - 認識Vue生命週期概念</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2 :style="{opacity: num}">玩動態改變文字透明度</h2>
{{ change() }}
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
num: 1,
},
// 改變透明度值大小
methods: {
change(){
setInterval(() => {
if( this.num <= 0 ) this.num = 1;
this.num -= 0.01;
}, 50);
}
}
});
</script>
</body>
</html>
- 上面這樣做之後,看起來好像成功了,但是執行一會就會發現:電腦的風扇會使勁兒轉,而且要是在change()中輸出一句話的話,會發現:執行了N多次,一直在跑_
- 原因:
改造:使用Vue的鉤子函式
注意:鉤子函式執行的次數問題
- 這就是前面說mounted()為什麼是:Vue解析完了模板之後,準備把“初始的真實DOM”渲染到頁面時會呼叫的一個函式,初始的真實DOM,是初次轉成真實DOM時執行的,後面數值改變那是更新,對於模板來說,它不叫改變,別忘了前面玩diff演算法時說過的流程 ———— 新的虛擬DOM會和初始虛擬DOM進行對比
Vue生命週期總結
- 定義:Vue在關鍵時刻幫我們呼叫的一些特殊名稱的函式
- 別名:生命週期回撥函式 / 生命週期函式 / 生命週期鉤子
- 注意:
- 生命週期函式的名字不可更改,但是:函式中的執行體是可以自己根據需求編寫的
- 生命週期函式中的this指向是Vue例項 或 元件例項物件
生命週期過程
- 首先這個知識點的原理圖在官網中有
- 但是:我對上圖中的內容做了補充( 藍色的東西就是新加的 )
- 上面這個圖是為了先了解大概,接下來會簡單說明一下圖中的內容
1.20.2、Vue的掛載流程
- 所謂的掛載在前面引入生命週期概念時說過了,就是Vue在調mounted()時,換言之:就是前面圖中直到mounted()時的步驟就是掛載流程,只是需要解讀一下
先來看一下這部分
- 說在beforeCreate時還不可以使用vm訪問data中的資料、methods中的方法,在這之前只開始初始化生命週期、事件,那就來測試一下
點選檢視程式碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>2 - Vue的掛載</title>
<script src="../../js/vue.js"></script>
</head>
<body>
<!-- 被 vm 例項所控制的區域 -->
<div id="app">
<h2>{{name}}</h2>
<button @click = "print">切換名字</button>
</div>
<script>
// 去除瀏覽器控制檯中的警告提示資訊
Vue.config.productionTip = false;
// 建立 vm 例項物件
const vm = new Vue({
// 指定控制的區域
el:'#app',
data:{
name: '紫邪情'
},
methods: {
print(){
console.log("正在執行print()函式");
}
},
// 之前只開始初始化生命週期、事件,此時:還不能使用vm訪問data和methods
beforeCreate () {
// 操作data中的資料 methods也是差不多的
console.log( this.name );
// 檢視Vue內容,看看是哪些東西
console.log(this);
// 打斷點 和java中的debug是一回事
debugger;
}
});
</script>
</body>
</html>
- 要出現下面的頁面,記得執行程式之後,先調出控制檯,然後重新整理頁面才會進入debug斷點
接著來看這一部分
- 說的是:已經開始了資料監測、資料代理,可以使用vm訪問data和methods了,那久測試一下
接著看這一部分
- 先看下面部分,說:頁面呈現的是未經編譯的DOM結果且對DOM的操作,“最終”都不奏效,測試一下
- 同時還說:所有對DOM結構的操作都不奏效,那就試一下
- 現在接著回去看那個邏輯判斷裡面的內容
- 外層和內層就是下面這個
- 而所謂的template模板更簡單
- 截圖中有個地方容易產生歧義:template中應該還需要使用一個div給套起來,template中只能有一個根節點,而例子中有h2 和 button兩個節點,會報錯的,所以需要使用div( 或其他標籤 )把要寫的程式碼給套起來,可以對照Vue例項和容器的對應關係( 一 一對應 )
最後來看這一部分
- 但是:這裡有一個點需要注意
- 這裡是將真實DOM使用”vm.$el“把真實DOM中的內容給複製了一份儲存起來了,這一步很關鍵,為的就是後續 新的虛擬DOM 和 初始的虛擬DOM進行對比演算法時,有相同的資料內容,那麼新的虛擬DOM就會直接用初始虛擬DOM的資料( 前面玩v-for中key的原理時,key值為index和資料id的區別 ),而要用初始DOM就需要找到初始DOM唄,它儲存起來之後就方便找到了嘛
以上就是Vue的掛載過程
1.20.3、Vue的更新流程
- 內容就一丟丟,下圖中紅色框起來的內容
來看beforeUpdate()
接著來看Updated
1.20.4、Vue的銷燬流程
- 內容就是圖中的最後一點東西,但是:這裡面坑也是有點多的
- 圖中提到了"vm.$destroy()" 和 銷燬哪些東西,所以先看官網對銷燬的介紹
- 現在就用程式碼去驗證一下圖中的內容,需要改造一下程式碼
- 執行效果如下
- 開始踩另一個坑
- vm確實是銷燬了,但是這個坑是前面說的銷燬事件監聽,它銷燬的是:自定義事件( 後續接觸 ),所以Vue自身的事件還在,也執行了
- 那又有問題:頁面為什麼沒 +1 ?
- 是因為beforeDestroy ()是一個賊尷尬的階段,這裡面確實可以拿到data、methods、指令這些,但是:當一點選銷燬( 呼叫了"vm.$destroy()" ),程式就會執行beforeDestroy (),這裡面的呼叫methods也會去執行,可是:關鍵點就是它不會再去呼叫beforeUpdate ()和updated () ———— 正常流程是資料發生改變就會調這兩個,但是:deforeDestroy()就不會呼叫,所以:才在前面說 此階段一般乾的事情是:關閉定時器、解除自定義事件繫結等操作
以上的這些流程也是React的生命週期流程,當然:有些名字改了一下啊,而Vue就多了一個destroyed(),但是此階段也是個極度忽略的階段,因為沒什麼事做
1.20.5、Vue生命週期總結
-
Vue生命週期共有8個( 4對 ),另外其實還有3個週期( 但是:這三個需要到router路由中玩切換時才能整 )
-
beforeCreate() created() beforeMount() mounted() beforeUpdate() Updated() beforeDestroy() Destroyed()
-
其中:常用的鉤子函式就兩個
- mounted() 用來傳送ajax請求、啟動定時器、繫結自定義事件、訂閱訊息( 後續說明 )等 即:完成的是"初始化操作"
- beforeDestroy() 用來清除定時器、解綁自定義事件、取消訂閱訊息等,即:做的是“收尾操作”
-
關於銷燬vm的問題
- 銷燬後藉助Vue開發者工具是看不到任何資訊的
- 銷燬後自定義事件會失效,但是:Vue原生DOM事件依然有效
- 一般不會在beforeDestroy()中運算元據,因為就算操作了,那也不會觸發beforeUpdate()和updated()
-