Vue
一、使用語法
1.插值語法
功能:用於解析標籤體內容。
寫法:{{xxx}},xxx是js表示式,且ß可以直接讀取到data中的所有屬性。
2.指令語法
功能:用於解析標籤(包括:標籤屬性、標籤體內容、繫結事件.....)。
舉例:v-bind:href="xxx" 或 簡寫為 :href="xxx",xxx同樣要寫js表示式,且可以直接讀取到data中的所有屬性。
備註:Vue中有很多的指令,且形式都是:v-????,此處我們只是拿v-bind舉個例子。
二、資料繫結
1.單項繫結(v-bind)
原寫法<input type="text" v-bind:value="name"><br/>
簡寫<input type="text" :value="name"><br/>
2.雙向繫結(v-model)
原寫法<input type="text" v-model:value="name"><br/>
簡寫<input type="text" v-model="name"><br/>
三、資料代理
1.何為資料代理?
透過一個物件代理對另一個物件中屬性的操作(讀/寫)
2.Vue中的資料代理
透過vm物件來代理data物件中屬性的操作(讀/寫)
基本原理:
透過Object.defineProperty()把data物件中所有屬性新增到vm上。 為每一個新增到vm上的屬性,都指定一個getter/setter。 在getter/setter內部去操作(讀/寫)data中對應的屬性。
四、事件
1.事件的基本使用 v-on
使用v-on:xxx 或 @xxx 繫結事件,其中xxx是事件名;
事件的回撥需要配置在methods物件中,最終會在vm上;
methods中配置的函式,不要用箭頭函式!否則this就不是vm了;
methods中配置的函式,都是被Vue所管理的函式,this的指向是vm 或 元件例項物件;
@click="demo" 和 @click="demo($event)" 效果一致,但後者可以傳參;
2.事件的修飾符
prevent:阻止預設事件(常用);
stop:阻止事件冒泡(常用);
once:事件只觸發一次(常用);
capture:使用事件的捕獲模式;
self:只有event.target是當前操作的元素時才觸發事件;
passive:事件的預設行為立即執行,無需等待事件回撥執行完畢;
3.鍵盤事件
1.Vue中常用的按鍵別名:
回車 => enter
刪除 => delete (捕獲“刪除”和“退格”鍵)
退出 => esc
空格 => space
換行 => tab (特殊,必須配合keydown去使用)
上 => up
下 => down
左 => left
右 => right
2.Vue未提供別名的按鍵,可以使用按鍵原始的key值去繫結,但注意要轉為kebab-case(短橫線命名)
3.系統修飾鍵(用法特殊):ctrl、alt、shift、meta
(1).配合keyup使用:按下修飾鍵的同時,再按下其他鍵,隨後釋放其他鍵,事件才被觸發。
(2).配合keydown使用:正常觸發事件。
4.也可以使用keyCode去指定具體的按鍵(不推薦)
5.Vue.config.keyCodes.自定義鍵名 = 鍵碼,可以去定製按鍵別名
五、計算屬性
1.計算屬性的功能實現可以透過methods實現
方法,在方法中進行操作,可以修改屬性值
2.計算屬性computed
定義:要用的屬性不存在,要透過已有屬性計算得來。
原理:底層藉助了Objcet.defineproperty方法提供的getter和setter。
get函式什麼時候執行?
(1).初次讀取時會執行一次。
(2).當依賴的資料發生改變時會被再次呼叫。
優勢:與methods實現相比,內部有快取機制(複用),效率更高,除錯方便。
備註:
1.計算屬性最終會出現在vm上,直接讀取使用即可。是將data中的屬性,計算完,放置在vm上的.
2.如果計算屬性要被修改,那必須寫set函式去響應修改,且set中要引起計算時依賴的資料發生改變。
computed:{
//完整寫法
/* fullName:{
get(){
console.log('get被呼叫了')
return this.firstName + '-' + this.lastName
},
set(value){
console.log('set',value)
const arr = value.split('-')
this.firstName = arr[0]
this.lastName = arr[1]
}
} */
//簡寫
fullName(){
console.log('get被呼叫了')
return this.firstName + '-' + this.lastName
}
}
六、監視屬性
1.watch
當被監視的屬性變化時, 回撥函式自動呼叫, 進行相關操作
監視的屬性必須存在,才能進行監視!!
監視的兩種寫法:
(1)new Vue時傳入watch配置
const vm = new Vue({
el:'#root',
data:{
isHot:true,
},
watch:{
isHot:{
immediate:true, //初始化時讓handler呼叫一下
//handler什麼時候呼叫?當isHot發生改變時。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
}
}
})
(2)透過vm.$watch監視
vm.$watch('isHot',{
immediate:true, //初始化時讓handler呼叫一下
//handler什麼時候呼叫?當isHot發生改變時。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
})
2.深度監視
Vue中的watch預設不監測物件內部值的改變(一層)。
配置deep:true可以監測物件內部值改變(多層)。
const vm = new Vue({
el:'#root',
data:{
isHot:true,
numbers:{
a:1,
b:1,
c:{
d:{
e:100
}
}
}
},
watch:{
isHot:{
// immediate:true, //初始化時讓handler呼叫一下
//handler什麼時候呼叫?當isHot發生改變時。
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
},
//監視多級結構中某個屬性的變化
/* 'numbers.a':{
handler(){
console.log('a被改變了')
}
} */
//監視多級結構中所有屬性的變化
numbers:{
deep:true,
handler(){
console.log('numbers改變了')
}
}
}
})
3.監視屬性簡寫
vm內
//正常寫法
isHot:{
// immediate:true, //初始化時讓handler呼叫一下
// deep:true,//深度監視
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
},
//簡寫
isHot(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue,this)
}
//正常寫法
vm.$watch('isHot',{
immediate:true, //初始化時讓handler呼叫一下
deep:true,//深度監視
handler(newValue,oldValue){
console.log('isHot被修改了',newValue,oldValue)
}
})
//簡寫
vm.$watch('isHot',(newValue,oldValue)=>{
console.log('isHot被修改了',newValue,oldValue,this)
})
4.計算屬性和監視屬性對比
1.computed能完成的功能,watch都可以完成。
2.watch能完成的功能,computed不一定能完成,例如:watch可以進行非同步操作。
重要的小原則:
1.所被Vue管理的函式,最好寫成普通函式,這樣this的指向才是vm 或 元件例項物件.
2.所有不被Vue所管理的函式(定時器的回撥函式、ajax的回撥函式等、Promise的回撥函式),最好寫成箭頭函式,這樣this的指向才是vm 或 元件例項物件。
七、繫結樣式
<!-- 繫結class樣式--字串寫法,適用於:樣式的類名不確定,需要動態指定 -->
<div class="basic" :class="mood" @click="changeMood">{{name}}</div> <br/><br/>
<!-- 繫結class樣式--陣列寫法,適用於:要繫結的樣式個數不確定、名字也不確定 -->
<div class="basic" :class="classArr">{{name}}</div> <br/><br/>
<!-- 繫結class樣式--物件寫法,適用於:要繫結的樣式個數確定、名字也確定,但要動態決定用不用 -->
<div class="basic" :class="classObj">{{name}}</div> <br/><br/>
<!-- 繫結style樣式--物件寫法 -->
<div class="basic" :style="styleObj">{{name}}</div> <br/><br/>
<!-- 繫結style樣式--陣列寫法 -->
<div class="basic" :style="styleArr">{{name}}</div>
data:{
name:'尚矽谷',
mood:'normal',
classArr:['atguigu1','atguigu2','atguigu3'],
classObj:{
atguigu1:false,
atguigu2:false,
},
styleObj:{
fontSize: '40px',
color:'red',
},
styleObj2:{
backgroundColor:'orange'
},
styleArr:[
{
fontSize: '40px',
color:'blue',
},
{
backgroundColor:'gray'
}
]
},
八、條件渲染
1.v-if
寫法:
(1).v-if="表示式"
(2).v-else-if="表示式"
(3).v-else="表示式"
適用於:切換頻率較低的場景。
特點:不展示的DOM元素直接被移除。
注意:v-if可以和:v-else-if、v-else一起使用,但要求結構不能被“打斷”。
2.v-show
寫法:v-show="表示式"
適用於:切換頻率較高的場景。
特點:不展示的DOM元素未被移除,僅僅是使用樣式隱藏掉
3.備註:
使用v-if的時,元素可能無法獲取到,而使用v-show一定可以獲取到。
九、列表渲染
1.v-for
<!-- 遍歷陣列 -->
<h2>人員列表(遍歷陣列)</h2>
<ul>
<li v-for="(p,index) of persons" :key="index">
{{p.name}}-{{p.age}}
</li>
</ul>
<!-- 遍歷物件 -->
<h2>汽車資訊(遍歷物件)</h2>
<ul>
<li v-for="(value,k) of car" :key="k">
{{k}}-{{value}}
</li>
</ul>
<!-- 遍歷字串 -->
<h2>測試遍歷字串(用得少)</h2>
<ul>
<li v-for="(char,index) of str" :key="index">
{{char}}-{{index}}
</li>
</ul>
<!-- 遍歷指定次數 -->
<h2>測試遍歷指定次數(用得少)</h2>
<ul>
<li v-for="(number,index) of 5" :key="index">
{{index}}-{{number}}
</li>
</ul>
data:{
persons:[
{id:'001',name:'張三',age:18},
{id:'002',name:'李四',age:19},
{id:'003',name:'王五',age:20}
],
car:{
name:'奧迪A8',
price:'70萬',
color:'黑色'
},
str:'hello'
}
2.key 的原理
類似於真實DOM中的id,唯一標識,只不過是在vue中使用,在虛擬DOM中使用。
key 使用index使用,或者不適用key(因為預設key用的是index),可能會出現問題:
-
若對資料進行:逆序新增、逆序刪除等破壞順序操作: 會產生沒有必要的真實DOM更新 ==> 介面效果沒問題, 但效率低。
-
如果結構中還包含輸入類的DOM: 會產生錯誤DOM更新 ==> 介面有問題。
原理如下:
![image-20240308220129757](/Users/zhangqi/Library/Application Support/typora-user-images/image-20240308220129757.png)
最後,就是建議:key中使用物件的id
3.列表過濾
兩種實現方式
//用watch實現
//#region --用來合併程式碼的
new Vue({
el:'#root',
data:{
keyWord:'',
persons:[
{id:'001',name:'馬冬梅',age:19,sex:'女'},
{id:'002',name:'周冬雨',age:20,sex:'女'},
{id:'003',name:'周杰倫',age:21,sex:'男'},
{id:'004',name:'溫兆倫',age:22,sex:'男'}
],
filPerons:[]
},
watch:{
keyWord:{
immediate:true,//用來先呼叫一個handler,空字串的index是0.
handler(val){
this.filPerons = this.persons.filter((p)=>{
return p.name.indexOf(val) !== -1
})
}
}
}
})
//#endregion
//用computed實現
new Vue({
el:'#root',
data:{
keyWord:'',
persons:[
{id:'001',name:'馬冬梅',age:19,sex:'女'},
{id:'002',name:'周冬雨',age:20,sex:'女'},
{id:'003',name:'周杰倫',age:21,sex:'男'},
{id:'004',name:'溫兆倫',age:22,sex:'男'}
]
},
computed:{
filPerons(){
return this.persons.filter((p)=>{//filpersons的返回值
return p.name.indexOf(this.keyWord) !== -1//filter的篩選條件
})
}
}
})
4.列表排序
arr.sort((p1,p2)=>{
return this.sortType === 1 ? p2.age-p1.age : p1.age-p2.age
})
5.Vue監測資料改變
透過包裹陣列更新元素的方法實現,本質就是做了兩件事:
(1).呼叫原生對應的方法對陣列進行更新。
(2).重新解析模板,進而更新頁面。
6.Vue檢測陣列改變
只能透過vue管理的7個運算元組的原生方法:
push():最後位置加一個
pop():刪除最後一個
shift():刪除第一個
unshift():增加到第一個
splice():指定位置插入、刪除、替換
sort():排序
reverse():翻轉
若需要過濾陣列,使用filter後,需要替換原來的陣列
十、收集表單資料
v-model的深度應用
1.若:,則v-model收集的是value值,使用者輸入的就是value值。
2.若:,則v-model收集的是value值,且要給標籤配置value值。
3.若:
1.沒有配置input的value屬性,那麼收集的就是checked(勾選 or 未勾選,是布林值)
2.配置input的value屬性:
(1)v-model的初始值是非陣列,那麼收集的就是checked(勾選 or 未勾選,是布林值)
(2)v-model的初始值是陣列,那麼收集的的就是value組成的陣列
備註:v-model的三個修飾符:
lazy:失去焦點再收集資料
number:輸入字串轉為有效的數字
trim:輸入首尾空格過濾,中間空格不過濾
十一、過濾器
//容器
<!--
過濾器:
定義:對要顯示的資料進行特定格式化後再顯示(適用於一些簡單邏輯的處理)。
語法:
1.註冊過濾器:Vue.filter(name,callback) 或 new Vue{filters:{}}
2.使用過濾器:{{ xxx | 過濾器名}} 或 v-bind:屬性 = "xxx | 過濾器名"
備註:
1.過濾器也可以接收額外引數、多個過濾器也可以串聯
2.並沒有改變原本的資料, 是產生新的對應的資料
-->
<!-- 準備好一個容器-->
<div id="root">
<h2>顯示格式化後的時間</h2>
<!-- 計算屬性實現 -->
<h3>現在是:{{fmtTime}}</h3>
<!-- methods實現 -->
<h3>現在是:{{getFmtTime()}}</h3>
<!-- 過濾器實現 -->
<h3>現在是:{{time | timeFormater}}</h3>
<!-- 過濾器實現(傳參) -->
<h3>現在是:{{time | timeFormater('YYYY_MM_DD') | mySlice}}</h3>
<h3 :x="msg | mySlice">尚矽谷</h3>
</div>
<div id="root2">
<h2>{{msg | mySlice}}</h2>
</div>
<script type="text/javascript">
Vue.config.productionTip = false
//全域性過濾器 寫在Vue例項前邊
Vue.filter('mySlice',function(value){
return value.slice(0,4)
})
//區域性過濾器
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')
}
},
//區域性過濾器
filters:{
timeFormater(value,str='YYYY年MM月DD日 HH:mm:ss'){
// console.log('@',value)
return dayjs(value).format(str)
}
}
})
new Vue({
el:'#root2',
data:{
msg:'hello,atguigu!'
}
})
</script>
十二、內建指令
常用的指令作對比
v-bind : 單向繫結解析表示式, 可簡寫為 :xxx
v-model : 雙向資料繫結
v-for : 遍歷陣列/物件/字串
v-on : 繫結事件監聽, 可簡寫為@
v-if : 條件渲染(動態控制節點是否存存在)
v-else 條件渲染(動態控制節點是否存存在)
v-show : 條件渲染 (動態控制節點是否展示)
1.v-text
(1)作用:向器所在的節點中渲染文字內容
(2)與插值與法的區別:會替換節點的內容,{{}}不會
2.v-html
(1)作用:向指定節點中渲染包含html結構的內容。
(2).與插值語法的區別:
(a).v-html會替換掉節點中所有的內容,{{xx}}則不會。
(b).v-html可以識別html結構。
(3).嚴重注意:v-html有安全性問題!!!!
(a).在網站上動態渲染任意HTML是非常危險的,容易導致XSS攻擊。
(b).一定要在可信的內容上使用v-html,永不要用在使用者提交的內容上!
3.v-cloak
需要注意的是,瞭解.vue檔案在網頁上的渲染順序:從上到下
若網速慢,則會等待引入的js檔案載入,在載入body中的內容,然後vue再接管進行渲染,會給使用者帶來不好的體驗。
用於網速慢時,頁面展示出{{xxx}}的問題
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>v-cloak指令</title>
<style>
[v-cloak]{
display:none;
}
</style>
<!-- 引入Vue -->
</head>
<body>
<!--
v-cloak指令(沒有值):
1.本質是一個特殊屬性,Vue例項建立完畢並接管容器後,會刪掉v-cloak屬性。
2.使用css配合v-cloak可以解決網速慢時頁面展示出{{xxx}}的問題。
-->
<!-- 準備好一個容器-->
<div id="root">
<h2 v-cloak>{{name}}</h2>
</div>
<script type="text/javascript" src="http://localhost:8080/resource/5s/vue.js"></script>
</body>
<script type="text/javascript">
console.log(1)
Vue.config.productionTip = false //阻止 vue 在啟動時生成生產提示。
new Vue({
el:'#root',
data:{
name:'尚矽谷'
}
})
</script>
</html>
4.v-once
(1)所在節點在初次動態渲染後,就視為靜態內容了
(2)以後資料的改變,不會引起v-once所在結構的更新,可以用於最佳化效能
5.v-pre
(1)跳過其所在節點的編譯過程
(2)可利用她跳過:沒有使用指令語法、沒有使用插值語法的節點、會加快編譯
十三、自定義指令
在Vue例項中,使用directives:{},進行自定義指令
<!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元素預設獲取焦點。
自定義指令總結:
一、定義語法:
(1).區域性指令:
new Vue({ new Vue({
directives:{指令名:配置物件} 或 directives{指令名:回撥函式}
}) })
(2).全域性指令:
Vue.directive(指令名,配置物件) 或 Vue.directive(指令名,回撥函式)
二、配置物件中常用的3個回撥:
(1).bind:指令與元素成功繫結時呼叫。
(2).inserted:指令所在元素被插入頁面時呼叫。
(3).update:指令所在模板結構被重新解析時呼叫。
三、備註:
1.指令定義時不加v-,但使用時要加v-;
2.指令名如果是多個單詞,要使用kebab-case命名方式,不要用camelCase命名。
-->
<!-- 準備好一個容器-->
<div id="root">
<h2>{{name}}</h2>
<h2>當前的n值是:<span v-text="n"></span> </h2>
<!-- <h2>放大10倍後的n值是:<span v-big-number="n"></span> </h2> -->
<h2>放大10倍後的n值是:<span v-big="n"></span> </h2>
<button @click="n++">點我n+1</button>
<hr/>
<input type="text" v-fbind:value="n">
</div>
</body>
<script type="text/javascript">
Vue.config.productionTip = false
//定義全域性指令
/* Vue.directive('fbind',{
//指令與元素成功繫結時(一上來)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入頁面時
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析時
update(element,binding){
element.value = binding.value
}
}) */
new Vue({
el:'#root',
data:{
name:'尚矽谷',
n:1
},
directives:{
//big函式何時會被呼叫?1.指令與元素成功繫結時(一上來)。2.指令所在的模板被重新解析時。
/* 'big-number'(element,binding){
// console.log('big')
element.innerText = binding.value * 10
}, */
big(element,binding){
console.log('big',this) //注意此處的this是window
// console.log('big')
element.innerText = binding.value * 10
},
fbind:{
//指令與元素成功繫結時(一上來)
bind(element,binding){
element.value = binding.value
},
//指令所在元素被插入頁面時
inserted(element,binding){
element.focus()
},
//指令所在的模板被重新解析時
update(element,binding){
element.value = binding.value
}
}
}
})
</script>
</html>
十四、生命週期
又名生命週期回撥函式、生命週期函式、生命週期鉤子
其各自的名稱不可進行更改,但函式內的具體內容是程式設計師進行定製的
函式內的this指向vm 或 元件例項物件
常用的鉤子
1.mounted:渲染後,傳送ajax請求、啟動定時器、繫結自定義事件、訂閱訊息等【初始化操作】。
2.beforeDestroy:清除定時器、解綁自定義事件、取消訂閱訊息等【收尾工作】。
注意
關於銷燬Vue例項
1.銷燬後藉助Vue開發者工具看不到任何資訊。
2.銷燬後自定義事件會失效,但原生DOM事件依然有效。
3.一般不會在beforeDestroy運算元據,因為即便運算元據,也不會再觸發更新流程了。