vuedemo20230801
Project setup
npm install
Compiles and hot-reloads for developments
npm run serve
Compiles and minifies for production
npm run build
Lints and fixes files
npm run lint
Customize configuration
See Configuration Reference.
Vue 是用來構(gòu)建用戶界面的漸進(jìn)式框架
漸進(jìn)式:如果應(yīng)用很簡單,就引用一個輕量小巧的核心庫
如果需要更多,則可以引用很多復(fù)雜的Vue第三方庫
允許用戶從簡單向復(fù)雜按需引用
Vue 特點(diǎn)
組件化:提高代碼復(fù)用率玛歌,跟容易維護(hù)
申明式編碼:讓編碼人員無需直接操作DOM端幼,提高開發(fā)效率
Vue 文檔
API,相當(dāng)于一個字典飒箭,有問題查詢API
Awesome Vue 萬能的Vue,官方給出的好用的第三方庫
網(wǎng)站:awesomejs.dev/for/vue
引入vue
<script type="text/javascript" src="../js/vue.js"></script>
引入Jquery,代碼里可以通過$或者jQuery調(diào)用封裝方法
引入Vue剑按,代碼里就多了一個Vue 構(gòu)造函數(shù)
開發(fā)建議瀏覽器安裝DEV tools: vue.js.devtools
VSCode 安裝 LiveServer 輔助開發(fā)
Vue3 代碼插件推薦
Vue 3 Snippets
=======================================================
1.想讓Vue工作,就必須創(chuàng)建一個Vue實(shí)例澜术,且要傳入一個配置對象
2.root 容器里的代碼依然符合html規(guī)范艺蝴,只不過混入了一些特殊的vue語法
3.root 容器里的代碼被稱為 Vue模板
4.Vue實(shí)例和容器是一一對應(yīng)的關(guān)系
5.真是開發(fā)中只有一個Vue實(shí)例,并且會配合著組件一起使用
6.{{XXX}} 插值模板中的XXX 是js 表達(dá)式鸟废,XXX 會自動讀取到data中對應(yīng)的屬性
7.一旦data的數(shù)據(jù)發(fā)生變化猜敢,那么模板中用到該數(shù)據(jù)的地方也會自動更新
注意區(qū)分 js 表達(dá)式 和 js 語句
- 表達(dá)式:一個表達(dá)式會產(chǎn)生一個值,可以放在任何一個需要值的地方
(1)變量a
(2)運(yùn)算表達(dá)式盒延,a + b
(3)方法表達(dá)式缩擂,demo(1)
(4)三元表達(dá)式
... - js 代碼(語句)
(1)if() {}
(2)for() {}
code 示例
<div id="root"></div>
new Vue({
el: "#root",
data: {
name: 'tom'
}
})
===============================================================
<a v-bind:href="url">鏈接地址</a>
把Vue對象data里的url值綁定到 a 標(biāo)簽上,v-bind簡寫方式為:
<a :href="url">鏈接地址</a>
<a v-bind:href="url">鏈接地址</a>
插值語法一般用于標(biāo)簽體里面的內(nèi)容的綁定
指令用于解析標(biāo)簽屬性
v-bind 一般用于標(biāo)簽屬性的綁定添寺,簡寫為:
v-model 雙向數(shù)據(jù)綁定胯盯,但是v-model只能應(yīng)用于輸入元素(元素有value標(biāo)簽)
簡寫為v-model
=================================================================
const v = new Vue({
el: "#root",
data: {
name: "tome"
}
cosnole.log(v); 查看v對象
})
v對象原型有個mount('#root')陨闹,此寫法跟new vue 是傳入el: "#root" 效果一樣
但是v.$mount 更靈活,比如設(shè)置定時器薄坏,在頁面加載一秒鐘以后再綁定模板
mount 是掛載的意思
data綁定還可以用函數(shù)時
data: function() {
return{
name: "tom"
}
}
data() {
return{
name: "tom"
}
}
data函數(shù)必須是普通函數(shù)趋厉,不支持箭頭函數(shù),如果使用箭頭函數(shù)胶坠,調(diào)用this不再是Vue實(shí)例君账,而是window
箭頭函數(shù)沒有自己的this,只能往外找沈善,找到window對象
==================================================================================
MVVM模型
M: 對象Vue對象data里的數(shù)據(jù)乡数,
V: 頁面模板
VM: Vue實(shí)例對象
Vue實(shí)例對象通過Dom Listeners 監(jiān)聽頁面數(shù)據(jù)變化綁定到模型
通過Data Bindings 把data 數(shù)據(jù)綁定到頁面模板
data 中所有的屬性最終都會出現(xiàn)在vm身上
vm 身上的所有屬性以及vm原型的所有屬性椭蹄,都可以在模板里直接使用
=================================================================================
Object.defineproperty方法
let person = {
name: "tom"
}
給person 添加age屬性
Object.defineproperty(person, 'age', {
value: 18
})
通過Object.keys(person) 遍歷屬性名只能拿到 name 屬性,通過 Object.defineproperty 添加的屬性age遍歷不到
想要age 可遍歷(或者叫可枚舉)净赴,用下面這種寫法
Object.defineproperty(person, 'age', {
value: 18,
enumerable: true, // 控制屬性是否可枚舉绳矩,默認(rèn)false
writable: true, // 控制屬性是否可以被修改,默認(rèn)false
configurable: true, // 控制屬性是否可以被刪除(通過 delete person.age 來刪除屬性)玖翅,默認(rèn)false
get() { // 當(dāng)有人讀取age 屬性的值的時候翼馆,get 屬性就會被調(diào)用,且返回值就是age 的值
},
set(value) { // 當(dāng)有人修改age 值的時候會調(diào)用set方法金度,且回收厚道修改的value值
}
})
數(shù)據(jù)代理技術(shù)
通過一個對象代理另一個對象的讀寫操作
let obj1 = {x: 100}
let obj2 = {y: 200}
讓obj2 代理obj1
Object.defineproperty(obj2, 'x', {
get() {
return obj1.x;
},
set(value) {
obj1.x = value;
}
})
vm._data == vue options 里傳的data
==============================================================
- Vue中的數(shù)據(jù)代理
通過vm對象來代理data對象中的屬性操作 - Vue中的數(shù)據(jù)代理好處
更加方便的操作data中的數(shù)據(jù) - 基本原理
通過Object.defineproperty() 把data對象中的屬性加到vm上
為每一個添加到vm上的屬性都指定 一個 getter/setter
在getter/setter內(nèi)部讀寫 data對應(yīng)的屬性
============================================================
v-on:click 點(diǎn)擊事件 簡寫 @click
如果方法想要得到event, 方法調(diào)用必須加上$event 占位符
<button v-on:click="showInfo1"></button> // 不傳參
<button v-on:click="showInfo2(66, $event)"></button> // 傳參
showInfo方法 不能用箭頭函數(shù)应媚,否則this對象就會執(zhí)行window
const vm = new Vue({
el: "#root",
data: {
name: "tom"
},
methods: {
showInfo1(event){
// console.log(event.target.innertext);
// console.log(this); // 此處的this是vm
// alert('同學(xué)你好');
},
showInfo2(number, event){
// console.log(event.target.innertext);
// console.log(this); // 此處的this是vm
// alert('同學(xué)你好');
}
}
})
=======================================================
a標(biāo)簽, 接收event對象,用 e.preventDefault() 阻止原有事件
<a @click="showInfo">點(diǎn)擊</a>
showInfo(e) {
e.preventDefault();
alert("Hello");
}
也可以這么寫
<a @click.prevent="showInfo">點(diǎn)擊</a>
showInfo 方法里就不用寫了
還有其他事件修飾符
@Click.prevent 阻止默認(rèn)事件猜极,點(diǎn)擊a標(biāo)簽中姜,鏈接就不會跳走
@Click.stop 阻止事件冒泡,事件不會被父元素捕獲
@Click.once 事件只出發(fā)一次跟伏,點(diǎn)第一次彈窗丢胚,再點(diǎn)無效
@Click.capture 在之間捕獲的時候處理,先捕獲受扳,再冒泡
@Click.self 只有當(dāng)e.event.target 是當(dāng)前元素的時候才觸發(fā)事件嗜桌,也能用于實(shí)現(xiàn)阻止冒泡事件
@Click.passive 事件的默認(rèn)行為立即執(zhí)行,無需等待回調(diào)執(zhí)行
比如@Wheel 出發(fā)回調(diào)辞色,回調(diào)執(zhí)行很復(fù)雜允許骨宠,如果不加paasive,則也買你會出現(xiàn)卡頓(等回調(diào)執(zhí)行完相满,滾動條才會滾動)层亿,加了passive 即使回調(diào)有復(fù)雜運(yùn)算,也會立即滾動滾動條立美,同時復(fù)雜計(jì)算慢慢算去
小技巧:既阻止冒泡匿又,又阻止默認(rèn)事件,可以這么寫
@Click.stop.prevent
滾動事件有兩種寫法
@Scroll
滾動條滾動事件建蹄,當(dāng)滾動條滾到頭就不再出發(fā)事件
@Wheel
即使?jié)L動條滾到頭碌更,只要滾輪繼續(xù)滾動,還能出發(fā)事件
==================================================================
鍵盤事件:
@keydown 按下去立即出發(fā)回調(diào)
@keyup 按下去松手再出發(fā)回調(diào)
<input type="text" @keyup="showInfo">
showInfo(e) {
// 通過e.keyCode 可以獲取鍵盤編碼
if(e.keyCode == "13") {
console.log("回車了");
}
}
上面捕獲回車事件還可以這么些洞慎,不用再回調(diào)里判斷keyCode
<input type="text" @keyup.enter="showInfo">
showInfo(e) {
console.log("回車了");
}
常用按鍵別名有enter delete(退格和刪除) esc space tab up down left ritht
監(jiān)聽tab鍵更適合用keydown去監(jiān)聽痛单,因?yàn)閠ab鍵會切換焦點(diǎn),當(dāng)keyup 的時候焦點(diǎn)已經(jīng)切換到了別的元素
還有幾個系統(tǒng)按鍵也應(yīng)當(dāng)使用keydown劲腿,ctrl旭绒,alt, shift, meta
舉例:如果ctrl 使用keyup,如果按了Contrl C 那么等C鍵抬起來才會觸發(fā)事件
不推薦使用按鍵編號 (如:@keyup.13) 去監(jiān)聽回車事件,因?yàn)橛械逆I盤事件編號不統(tǒng)一挥吵,將來web協(xié)議會廢棄重父,不支持
Vue 支持自己定義按鍵別名,比如定義回車:
Vue.config.keyCodes.huiche = 13;
小技巧:監(jiān)聽Contrl + y 事件可以這么寫 ====> @keydown.ctrl.y
Vue 計(jì)算屬性:用data里的屬性去計(jì)算加工忽匈,產(chǎn)生一個新的屬性叫做計(jì)算屬性
插值語法調(diào)用方法必須要帶小括號房午,表示調(diào)用方法,否則就會把方法本身打印到界面
事件語法里可以省略小括號
data 里的數(shù)據(jù)發(fā)生變化丹允,模板要重新解析
所以某個方法A使用了data里的值郭厌,并且該方法A在插值語法里有調(diào)用,每次data數(shù)據(jù)發(fā)生變化嫌松,方法A必然會被調(diào)用
computed: {
fullName {
// 當(dāng)有人讀取fullname 時沪曙,get屬性就會被調(diào)用奕污,底層實(shí)現(xiàn)用的就是 Object.defineproperty里的getter
// get 方法調(diào)用時機(jī)
// 1. 初次讀取fullname
// 2. 所依賴屬性發(fā)生變化時
get() {
return this.FirstName + '-' + this.LastName; // 此處 this指向vm
},
// 修改fullname時候會被調(diào)用萎羔,如果fullname 確定不會被修改,就不用寫set
set(value) {
...
}
}
// 簡寫形式
fullName() {
return this.FirstName + '-' + this.LastName;
}
}
fullName 也會綁定到vm對象上
假如界面上同時讀取四次 fullName碳默,fullName getter 方法也只會執(zhí)行一次贾陷,fullname 值Vue 會幫我們緩存
計(jì)算屬性比Method實(shí)現(xiàn)的優(yōu)勢
- 計(jì)算屬性多次使用fullName 之會調(diào)用一次get方法,而method 執(zhí)行多少次就調(diào)用多少次
======================================================================================
@XXX綁定事件的時候嘱根,事件后面可以寫簡單語句去執(zhí)行
@XXX="YYY" YYY 可以是簡單語句比如: isHot = !isHot; i ++
監(jiān)視屬性
watch: {
// 計(jì)算屬性也可以被監(jiān)視
isHot: {
// 初始化時rang handler調(diào)用一些
immediate: true,
// 當(dāng)被監(jiān)聽屬性isHot發(fā)生變化時髓废,handler會被調(diào)用
handler(newvalue, oldvalue) {
}
}
}
也可以在Vue 對象外面監(jiān)視屬性
vm.$watch('isHot', {
immediate: true,
handler(newvalue, oldvalue) {
}
});
// 深度監(jiān)測
data: {
numbers: {
a: 1,
b: 2
}
},
// 監(jiān)測多級屬性 numbers 下面的a屬性
watch: {
'numbers.a': {
// 初始化時rang handler調(diào)用一些
immediate: true,
// 當(dāng)被監(jiān)聽屬性isHot發(fā)生變化時,handler會被調(diào)用
handler(newvalue, oldvalue) {
}
}
}
假如想監(jiān)測整個numbers的變化该抒,直接再watch寫numbers: {...}是不起作用的慌洪,
因?yàn)檫@種情況下vue監(jiān)測的是numbers對象的地址,除非numbers 重新被復(fù)制
正確的寫法是開啟deep屬性
watch: {
numbers: {
// 當(dāng)numbers下面任何屬性發(fā)生變化都能監(jiān)測到
deep: true,
// 當(dāng)被監(jiān)聽屬性isHot發(fā)生變化時凑保,handler會被調(diào)用
handler(newvalue, oldvalue) {
}
}
}
如果不考慮deep 和 immediate 配置冈爹,watch 屬性可以簡寫為
watch: {
isHot(newvalue, oldvalue) {
console.log(newvalue, oldvalue);
}
}
vm.watch('isHot', function(newvalue, oldvalue) {
console.log(newvalue, oldvalue);
})
計(jì)算屬性靠返回值完成任務(wù),沒法使用setTimeout欧引,因?yàn)閟etTimeout 是把結(jié)果返回給了異步方法频伤,而不是返回給了計(jì)算屬性
Watch 可以完美的實(shí)現(xiàn)異步任務(wù)
setTimeout(() => {
console.log('123');
}, 1000)
定時器所指定的回調(diào)是由瀏覽器定時器管理模塊控制的,到點(diǎn)了由JS引擎調(diào)用的芝此,不是由Vue管理的
computed能完成的watch 一定能完成憋肖,
watch 能完成的computed不一定能完成,例如:watch 可以進(jìn)行異步操作
所有被Vue管理的函數(shù)最好寫成普通函數(shù)婚苹,這樣的話this的指向才是vm
所有不被Vue管理的函數(shù)(定時器回調(diào)函數(shù)岸更,ajax函數(shù),promise的回調(diào)函數(shù))膊升,最好寫成箭頭函數(shù)
這樣this的指向才是vm坐慰,可以訪問vue屬性
比如定時器是由window 調(diào)用的,如果申明成普通函數(shù),this一定指向window
如果是箭頭函數(shù)结胀,則沒有指定this赞咙,this向外尋找,找到了vm對象
=============================================================
vue style綁定class樣式
適用于樣式的類名不確定糟港,需要動態(tài)綁定
不變的樣式還是跟在html里面一樣
變化的樣式用:class來綁定攀操,vue 會把變化和不變的樣式組合到一起生效
綁定字符串,場景:類名不確定秸抚,需要動態(tài)指定
<div class="basic" :class="mod" @Click="changeMod"></div>
綁定數(shù)組, 多個類組合到一起生效速和,場景:類名不確定,樣式也不確定
<div class="basic" :class="arr" @Click="changeMod"></div>
綁定對象, 應(yīng)用樣式atguigu1剥汤, atguigu3颠放,atguigu2 是false,所以不生效
場景:類名確定吭敢,應(yīng)用哪個需要動態(tài)決定
<div class="basic" :class="classObj" @Click="changeMod"></div>
直接綁定內(nèi)聯(lián)樣式
<div class="basic" :style="styleObj" @Click="changeMod"></div>
數(shù)組形式綁定內(nèi)聯(lián)樣式
<div class="basic" :style="[styleObj, styleObj2]" @Click="changeMod"></div>
new Vue({
data: {
mod: "normal",
arr: ["atguigu1", "atguigu2", "atguigu3"],
classObj: {
atguigu1: true,
atguigu2: false,
atguigu3: true
},
styleObj: {
fontSize: '40px' // 所有有中劃線的css屬性名都要去掉中劃線碰凶,使用駝峰命名
},
styleObj2: {
color: 'red';
}
},
methods: {
changeMod(){
this.mod = "happy"
}
}
})
================================================================
v-show 是通過display:none 來隱藏元素
后面的表達(dá)式不一定是true或false,能夠解析為布爾值的表達(dá)式都可以
<div v-show="true">hello</div>
v-if 值為false直接會刪除對應(yīng)的dom節(jié)點(diǎn)鹿驼,
同時還有v-else-if 和 v-else欲低,跟js 里的if else 邏輯一模一樣
要使用v-else-if 和 v-else,所有dom元素必須是連續(xù)的畜晰,中間不能打斷插入別的元素
<div v-if="true">hello</div>
如果dom切換頻率很高砾莱,則使用v-show,動態(tài)控制隱藏和顯示凄鼻,效率更高
想要同時控制多個元素的隱藏和顯示可以使用 template 元素
template 元素渲染的時候會消失腊瑟,不影響原來dom,但是template只能配合v-if 使用
<template v-if="true">
<h1>Angular</h1>
<h1>React</h1>
<h1>Vue</h1>
</template>
=======================================================
v-for
// 遍歷數(shù)組
<ul>
// key 也可以綁定index块蚌,只要在ul里不重復(fù)就行
<li v-for="(person, index) in persons" :key="person.id">
{{p.name}} - {{p.age}}
</li>
</ul>
// 遍歷對象
<ul>
// v 是屬性值闰非,p是屬性名稱
<li v-for="(v, p) in car" :key="p">
{{v}} - {{p}}
</li>
</ul>
// 遍歷字符串
<ul>
// c是字符,index是下標(biāo)
<li v-for="(c, index) in car" :key="index">
{{c}} - {{index}}
</li>
</ul>
// 遍歷指定次數(shù)
<ul>
// i 是1匈子,2河胎,3,4虎敦,5 index 是下標(biāo)游岳,0,1其徙,2胚迫,3,4
<li v-for="(i, index) of 5" :key="index">
{{i}} - {{index}}
</li>
</ul>
new Vue({
data: {
persons: [
{id: '001', name: '張三', age: 18}
{id: '002', name: '張五', age: 19}
{id: '003', name: '張六', age: 20}
],
car: {
name: "Audi A8",
price: "18 W",
color: "黑色"
},
str: "hello"
},
methods: {
}
})
=======================================================================
v-for key 的原理
// 遍歷數(shù)組
<ul>
// key 也可以綁定index,只要在ul里不重復(fù)就行
<li v-for="(person, index) in persons" :key="person.id">
{{p.name}} - {{p.age}}
</li>
</ul>
new Vue({
data: {
persons: [
{id: '001', name: '張三', age: 18},
{id: '002', name: '張五', age: 19},
{id: '003', name: '張六', age: 20}
]
}
})
react-vue 循環(huán)渲染,key的作用
虛擬dom的作用,key是dom的標(biāo)識基括,當(dāng)數(shù)據(jù)發(fā)生變化時期犬,vue 會根據(jù)新數(shù)據(jù)生成新的虛擬dom
新的虛擬dom和舊的虛擬dom的差異對比河哑,比較規(guī)則如下:
- 如果舊的虛擬dom中找到了與新的虛擬dom相同的key
如果虛擬dom內(nèi)容沒有變,則直接使用之前的真實(shí)dom
如果虛擬dom內(nèi)容變了龟虎,則生成新的真實(shí)dom璃谨,隨后替換掉頁面上之前的真實(shí)dom - 如果舊的dom中沒有找到與新的虛擬dom相同的key
根據(jù)新的虛擬dom直接創(chuàng)建真實(shí)dom - 如果舊的虛擬dom中的key在新的虛擬dom中不存在
刪除舊的虛擬dom對應(yīng)的真實(shí)dom
用index下標(biāo)作為key可能引發(fā)的問題:
- 如果對數(shù)據(jù)進(jìn)行逆序添加,逆序刪除等操作
會產(chǎn)生沒有必要的真實(shí)dom更新鲤妥,界面沒有問題佳吞,效率低下
一旦結(jié)構(gòu)中有輸入類dom,會產(chǎn)生錯誤的dom更新棉安,界面會有問題
如果使用v-for沒有指定key底扳,vue默認(rèn)會把下標(biāo)作為key
開發(fā)中選擇key時,最好使用真實(shí)數(shù)據(jù)的唯一標(biāo)識:比如數(shù)據(jù)主鍵
如果緊緊是對數(shù)據(jù)的渲染贡耽,不會逆序刪除衷模,添加數(shù)據(jù),則使用 index 沒有問題
=====================================================================
列表的過濾:
<ul>
<li v-for="(person, index) in filPersons" :key="person.id">
{{p.name}} - {{p.age}} - {{p.gender}}
</li>
</ul>
new Vue({
data: {
keyWord: ''
persons: [
{id: '001', name: '馬冬梅', age: 18, gender: "女"},
{id: '002', name: '周冬雨', age: 19, gender: "女"},
{id: '003', name: '周杰倫', age: 20, gender: "男"},
{id: '003', name: '溫兆倫', age: 20, gender: "男"}
]
},
computed: {
filPersons() {
return this.persons.filter(p => {
return p.name.indexOf(this.keyword) != -1;
});
}
}
})
=====================================================================
Vue 修改數(shù)據(jù)信息但是不會更新到界面的Demo
<ul>
<li v-for="(person, index) in persons" :key="person.id">
{{p.name}} - {{p.age}} - {{p.gender}}
</li>
</ul>
<button @Click="updateMei">更新第一條數(shù)據(jù)</button>
const vm = new Vue({
data: {
keyWord: ''
persons: [
{id: '001', name: '馬冬梅', age: 18, gender: "女"},
{id: '002', name: '周冬雨', age: 19, gender: "女"},
{id: '003', name: '周杰倫', age: 20, gender: "男"},
{id: '003', name: '溫兆倫', age: 20, gender: "男"}
]
},
methods: {
updateMei() {
// 下面修改方法會更新到頁面菇爪,沒有問題
this.persons[0].Name = "馬老師";
this.persons[0].age = "34";
this.persons[0].gender = "男";
// 下面修改方法不生效算芯,有問題
this.persons[0] = { name = "馬老師", age = 34, gender = "男" }
}
}
})
Vue 監(jiān)測數(shù)據(jù)改變原理分析
- 我們傳入的data 會保存到 vm._data屬性下面柒昏,具體流程如下:
加工傳入的data凳宙,給每個屬性添加 gettter/setter
加工后的data 賦值給vm._data
加工之后有啥用?
比如修改data 的name屬性职祷,就會引起set_name方法的調(diào)用
set_name 調(diào)用就會重新解析模板生成新的虛擬dom氏涩,虛擬dom對比,更新真實(shí)dom
==========================================================
簡單模擬Vue監(jiān)測data對象改變:
let data = {
name: "馬老師"
}
// 創(chuàng)建一個監(jiān)視的實(shí)例對象, 用于監(jiān)視data中屬性的變化
// Vue其實(shí)就是在setter方法里添加了動態(tài)更新頁面的操作
const obs = new Observer(data);
let vm = {}
vm._data = data = obs
function Observer(obj) {
const keys = Object.keys(obj);
keys.foreach((k) => {
Object.defineproperty(this, k, {
get(){
return obj[key]
},
set(val) {
// 此處可以添加動態(tài)更新頁面的操作
console.log(k + "被修改了");
obj[k] = val
}
})
})
}
明白了上面代碼有梆,假如在代碼運(yùn)行中新添加一個屬性是尖,那么這個屬性是不是就沒有被加工,沒有添加getter/setter泥耀?答案是肯定的饺汹,
既然新添加屬性沒有被添加getter/setter,那么新添加屬性改變就不會被更新到Dom上
那如果我就像在代碼運(yùn)行中給data 添加屬性并且動態(tài)綁定到頁面要咋整呢痰催?
別急兜辞,Vue提供了專門的方法來干這事兒
Vue.set(vm._data.student, 'sex', '男') //
還可以寫成vm.student,因?yàn)関m.student 這個方法就是通過數(shù)據(jù)代理綁定了vm._data.Student
這么寫頁面就能監(jiān)測到屬性變化了
還有另一個方法夸溶,vm對象的set(vm._data.student, 'sex', '男')
注意R莩场!缝裁!
在vue里面調(diào)用set 可以直接寫
Vue.set(this.student, 'sex', '男')
或 Vue. $set(this.student, 'sex', '男')
注意2Iㄖ濉!!
Vue 的set方法只能給data下面的某一個對象添加屬性韩脑,不能直接給data添加屬性
==================================================
Vue 的數(shù)組中的元素是沒有添加getter/setter 來監(jiān)視數(shù)組元素變化的
但是Vue 會監(jiān)測以下方法調(diào)用氢妈,當(dāng)以下方法調(diào)用了,vue認(rèn)為你的數(shù)組就變了段多,并更新到頁面
push最后面新增,
pop刪除最后一個元素,
shift刪除第一個,
unshift在前面加一個,
splice指定位置插入/刪除/替換元素,
sort,
reverse反轉(zhuǎn)
Vue是如何監(jiān)測這些方法的呢允懂,很簡單,Vue 重寫了這些方法衩匣,你在vue里面調(diào)用的push等這些方法其實(shí)是vue自己實(shí)現(xiàn)的
既然是自己實(shí)現(xiàn)的蕾总,那還不是想咋寫就咋寫
另一種實(shí)現(xiàn)方式是使用Vue.set, Vue 的set方法也可以操作數(shù)組
Vue.set(this.student.hobby, 1, '搗蛋')
Vue set方法操作了student 的hobby數(shù)組中下標(biāo)為1的元素
直接替換整個數(shù)組也是可以的,因?yàn)閿?shù)組屬性也是普通屬性琅捏,有g(shù)etter/setter
I佟!柄延! 數(shù)據(jù)劫持啥意思蚀浆?
就是修改data中的某個屬性,修改的動作會被setter 劫持到
================================================================
from 表單
假如想讓點(diǎn)擊label的時候搜吧,label后面的輸入組件獲得標(biāo)簽市俊,
那就要通過for屬性把labe和輸入組件做綁定,操作如下
<label for="demo">賬號:</label>
<input type="text" id="demo">
雙向綁定radio 單選按鈕的時候滤奈,radio 元素一定要設(shè)置value值摆昧,不然屬性拿不到值
雙向綁定CheckBox的時候默認(rèn)綁定checked屬性拿到true/false,
如果要綁定checkbox具體的值,必須指定value屬性value
Q殉獭I鹉恪!綁定元素的初始值能夠影響綁定model
假如綁定model一開始是bool值昭躺,后面只能接收check屬性
如果綁定model一開始是數(shù)組忌锯,那么就會把勾選的value值加到數(shù)組里
表單提交綁定事件可以用 @submit來綁定,用prevent阻止默認(rèn)事件
<form @submit.prevent="submit">
...
</form>
雙向綁定接收數(shù)據(jù)默認(rèn)是字符串领炫,想要接收進(jìn)來的數(shù)據(jù)是數(shù)字可以加.number偶垮,這么寫,type=number 和 v-model.number 一般一起使用
年齡:<input type="number" v-model.number="age">
v-model.trim 可以接收輸入時去掉前后空格
v-model.lazy 不是實(shí)時收集輸入帝洪,等輸入控件失去焦點(diǎn)的時候似舵,才會更新綁定model
vue 過濾器
日期處理第三方包:moment.js, day.js
var fromatRes = dayjs(new Date()).format('yyyy-mm-dd');
1690780406197
// 無參過濾器
<h3>現(xiàn)在是:{{time | timeFormater}}</h3>
// 有參過濾器
<h3>現(xiàn)在是:{{time | timeFormater2('YYYY-MM-DD')}}</h3>
// 多個過濾器寫法,先格式化日期碟狞,再截取前四位
<h3>現(xiàn)在是:{{time | timeFormater2('YYYY-MM-DD') | mySlice}}</h3>
// 動態(tài)綁定屬性也可使使用過濾器
<h3 :x="msg | mySlice"></h3>
const vm = new Vue({
data: {
time: 1690780406197,
msg: '大哥你好, 哈哈哈'
},
filters: {
timeFormater(val) {
return dayjs(val).format('YYYY-MM-DD HH:mm:ss')
},
timeFormater2(val, str){
return dayjs(val).format(str)
},
mySlice(val) {
return val.slice(0, 4);
}
}
})
過濾器的功能也可以通過計(jì)算屬性computed和methods來實(shí)現(xiàn)
全局過濾器實(shí)現(xiàn)啄枕,直接把過濾器注冊到Vue全局,所有Vue組件都能調(diào)用
Vue.filter('mySlice', function(value){
return value.slice(0, 4);
})
過濾器只支持插值語法和屬性綁定
v-text指令族沃,向其所在標(biāo)簽插入text文本
v-html指令频祝,支持html解析
<h3 v-text="title"></h3> // 輸入任何值以文本形式展示到節(jié)點(diǎn)
<h3 v-html="htmlStr"></h3> // 會把html 解析到頁面
const vm = new Vue({
data: {
title: '大哥你好, 哈哈哈',
htmlStr: '<h1>哈哈</h1>'
}
})
cookie 加上http only 屬性泌参,那么這個cookie 只能被http協(xié)議拿到,沒法通過document.getCookie() 方法拿到
v-html 在頁面直接渲染html 有可能會導(dǎo)致XSS攻擊
一定在可信的內(nèi)容渲染上使用v-html
用戶輸入的內(nèi)容一定不要直接使用v-html 來渲染
=============================================================
js 阻塞
js 頭文件如果加載延遲常空,那么后面的內(nèi)容渲染沽一,js代碼指向都會被阻塞
直到j(luò)s 頭文件加載完成
如果把引入的js腳本放在body最后面去加載,則不會阻塞
v-cloak 標(biāo)簽用途
當(dāng)網(wǎng)速過慢的時候漓糙,未經(jīng)過解析的Vue模板會被展示到頁面上
比如直接展示:{{name}} 這種插值語法
如果不想讓頁面閃一下铣缠,那么就使用v-clock
<style>
[v-cloak] {
display: none;
}
</style>
<h1 v-cloak>{{name}}</h1>
vue 執(zhí)行之前由于插值元素加了v-cloak,所以會被display:none隱藏掉元素昆禽,
啥也不展示蝗蛙,當(dāng)vue創(chuàng)建實(shí)例接管容器后,Vue會刪除所有v-clock屬性醉鳖,一切有Vue接管捡硅,恢復(fù)正常
=================================================
v-once 指令
v-once所在的節(jié)點(diǎn)在初次動態(tài)渲染之后,就會被視為靜態(tài)內(nèi)容了
以后綁定之變后盗棵,不會再引起v-once所在節(jié)點(diǎn)的更新
<h2 v-once>N的初始值:{{n}}</h2>
<h2 >N的當(dāng)前值:{{n}}</h2>
========================================================
v-pre指令
跳過所在節(jié)點(diǎn)的編譯過程壮韭,
可以利用它跳過沒有指令語法,沒有使用插值語法的節(jié)點(diǎn)纹因,加快編譯
<button v-pre @Click="n++">點(diǎn)我n+1</button>
<h2 v-pre>{{name}}</h2>
=====================================================
Vue自定義屬性
自己寫一個v-big指令喷屋,
會把綁定的數(shù)值放大10 倍
自己寫一個v-fbind,
和v-bind功能類似,到那時可以讓所綁定的輸入控件獲得焦點(diǎn)
<div id="root">
<h2>當(dāng)前值為:<span v-text="n"></span></h2>
<h2>放大十倍的值為:<span v-big="n"></span></h2>
</div>
const vm = new Vue({
el: "#root",
data: {
n: 10
},
directives: {
// big 函數(shù)調(diào)用時機(jī):
// 指令與元素被成功綁定時調(diào)用一次(一上來就調(diào)用);
// 指令所在的模板被重新解析時調(diào)用
// element是真實(shí)dom元素
// binding是綁定對象
big(element, binding) {
console.log(this); // 此處的this是window
element.innerText = binding.value * 10;
},
// fbind 錯誤實(shí)現(xiàn)方式
fbindError(element, binding) {
element.value = binding.value;
element.focus();
// 上面的focus 會在頁面打開時自動把焦點(diǎn)放到綁定元素上面嗎瞭恰?屯曹??
// 答案是不會的寄疏,因?yàn)閑lement是屬于Vue模板是牢,Vue模板經(jīng)過解析后才會被真正渲染到頁面上
// 當(dāng)?shù)谝淮未蜷_頁面僵井,元素成功綁定意味著再內(nèi)存里建立了指令與元素的綁定關(guān)系陕截,整個模板還未被渲染,此時調(diào)用focus當(dāng)然無效
},
// fbind 正確實(shí)現(xiàn)方式批什,不能簡寫农曲,只能采用完整的指令申明方法
fbind:{
// 調(diào)用順序:bind>inserted>update
// bind就是上面簡寫形式的方法
bind(element, binding){
element.value = binding.value;
},
// inserted 所在元素在被插入頁面時調(diào)用
inserted(element, binding){
element.focus();
},
//指令所在模板在被重新解析時調(diào)用
update(element, binding){
}
}
}
})
總結(jié):指令語法就是用于解析標(biāo)簽(包括:標(biāo)簽屬性,標(biāo)簽內(nèi)容驻债,綁定事件...)
簡寫語法相當(dāng)于同時寫了:bind和update乳规,沒有寫inserted
指令名稱的坑,指令名稱多個單詞用中劃線分隔合呐,不能使用駝峰命名
指令定義則么寫
'bind-number': function(element, binding){
}
定義指令的方法里的this都是指向window
定義全局指令:
Vue.directive('fbind', {
// 調(diào)用順序:bind>inserted>update
// bind就是上面簡寫形式的方法
bind(element, binding){
element.value = binding.value;
},
// inserted 所在元素在被插入頁面時調(diào)用
inserted(element, binding){
element.focus();
},
//指令所在模板在被重新解析時調(diào)用
update(element, binding){
}
});
===============================================================
vue 生命周期
- 生命周期回調(diào)函數(shù)是Vue在關(guān)鍵的時刻幫我們調(diào)用的一些特殊名稱的函數(shù)
- 生命周期函數(shù)的名字不可更改暮的,但是函數(shù)內(nèi)容可以根據(jù)具體需求編寫
- 生命周期函數(shù)中的this指向是vm,或 組件實(shí)例對象
--veu對象創(chuàng)建淌实,先會去初始化生命周期冻辩,事件等猖腕,但是數(shù)據(jù)代理還沒開始
===beforeCreate() {} 鉤子函數(shù)
此時還不能訪問methods 和 data,因?yàn)閿?shù)據(jù)代理還沒開始
--此階段進(jìn)行數(shù)據(jù)監(jiān)測恨闪,和數(shù)據(jù)代理
===created() {} 鉤子函數(shù)
此時可以通過vm訪問到methods 和 data倘感,因?yàn)閿?shù)據(jù)代理已經(jīng)完成
--此階段Vue開始解析模板,生成虛擬DOM咙咽,頁面還不能顯示解析好的內(nèi)容
===beforeMount() {}
頁面呈現(xiàn)的是未經(jīng)Vue編譯的DOM結(jié)構(gòu) 所有對DOM的操作都不奏效@下辍!钧敞!(不奏效的原因是因?yàn)橄乱徊讲迦胝鎸?shí)DOM 會覆蓋掉現(xiàn)在的所有操作)
--此階段將內(nèi)存中的虛擬DOM轉(zhuǎn)為真實(shí)DOM插入頁面蜡豹,Vue會把虛擬DOM往Vue對象$el 屬性上存一份
===mounted() {} 鉤子函數(shù)
此時,頁面中呈現(xiàn)的都是經(jīng)過Vue編譯的DOM
對DOM的操作均有效溉苛,直至此初始化過程結(jié)束余素,一般在此時進(jìn)行:開啟定時器,發(fā)送網(wǎng)絡(luò)請求炊昆,訂閱消息桨吊,綁定自定義事件等初始化操作
===beforeUpdate() {} 鉤子函數(shù)
此時,數(shù)據(jù)是新的凤巨,但是頁面是舊的视乐,即:頁面尚未和數(shù)據(jù)保持同步
--此階段會根據(jù)新數(shù)據(jù)生成新的虛擬DOM,并且比較新舊虛擬DOM敢茁,并更新真實(shí)DOM佑淀,完成了Model -> View 的更新
===updated() {} 鉤子函數(shù)
此時數(shù)據(jù)是新的,頁面也是新的彰檬,頁面和數(shù)據(jù)保持同步
===beforeDestory() {} 鉤子函數(shù)
此時vm中的data,methods,指令等等都處于可用狀態(tài)伸刃,即將要執(zhí)行銷毀操作
一般在這時候:關(guān)閉定時器,取消訂閱消息逢倍,解綁自定義事件等等
在beforeDestory 再去操作數(shù)據(jù)捧颅,數(shù)據(jù)不會被更新到界面上,因?yàn)榈搅诉@個階段较雕,不會再走到 beforeUpdate 和 updated 方法里面去了
===destoryed() {} 鉤子函數(shù)
沒啥屌用
=====================================================================
Vue中清理定時器碉哑,定時器的Id不知道往哪兒放?慎玖??放到外面有點(diǎn)不合適趁怔,其實(shí)可以放在vm對象上
比如在mounted上開啟一個定時器:
mounted(){
console.log('mounted');
this.timerId = setInterval(() => {
this.n ++;
}, 16)
}
銷毀的是就可以這么清理
beforeDestory() {
clearInterval(this.timerId);
}
======================================================================
Vue 組件
定義Vue組件不能寫 el 配置項(xiàng)湿硝,因?yàn)樽罱K所有的組件都要被一個vm管理图柏,由VM決定服務(wù)于哪個模板
定義Vue組件時例诀,data一定要寫成函數(shù)式,返回一個data對象
為什么一定要這么寫裁着?
直接定義成data對象繁涂,那么多個地方引用組件時他們就共享同一份data,互相影響
寫成函數(shù)式則不一樣二驰,每次被引用都是返回一個新的data對象扔罪,互相直接沒有關(guān)聯(lián)
使用組件攏共分三步:
- 創(chuàng)建組件
- 注冊組件
- 調(diào)用組件
創(chuàng)建一個非單文件schoo組件
const school = Vue.extend({
template: <div> <h3>學(xué)習(xí)名稱:{{schoolName}}</h3> <h3>地址:{{address}}</h3> </div>
,
data() {
return {
schoolName: "英才",
address: "定西市安定區(qū)"
}
}
})
創(chuàng)建非單文件student組件
const student = Vue.extend({
template: <div> <h3>學(xué)生名稱:{{name}}</h3> <h3>年齡:{{age}}</h3> </div>
,
data() {
return {
name: "張三",
age: 18
}
}
})
<body>
<div id="root">
<h1>{{message}}</h1>
// 使用組件
<xuexiao></xuexiao>
<xuesheng></xuesheng>
</div>
</body>
new Vue({
el: "#root",
data: {
message: "hello"
},
// 注冊組件
components: {
xuexiao: school,
xuesheng: student
}
})
全局注冊組件,所有vm都可以使用
Vue.component("xuexiao", school)
Vue.component("xuesheng", student)
=======================================================
組件名稱命名:多個單詞桶雀,用中劃線'my-school'
不要用html已經(jīng)使用的元素名矿酵,如h2,div
可以在name屬性指定組件在開發(fā)者工具中顯示的名字
const student = Vue.extend({
name: '[name-in-dev-tools]'
})
簡寫 可以省略Vue.extend()
const student = {
name: '[name-in-dev-tools]'
}
Vue 自己會調(diào)用Vue.extend()
====================================================
嵌套組件
// 定義school的子組件student
const student = Vue.extend({
template: <div> <h3>學(xué)生名稱:{{name}}</h3> <h3>年齡:{{age}}</h3> </div>
,
data() {
return {
name: "張三",
age: 18
}
}
})
const school = Vue.extend({
template: <div> <h3>學(xué)習(xí)名稱:{{schoolName}}</h3> <h3>地址:{{address}}</h3> <student></student> </div>
,
data() {
return {
schoolName: "英才",
address: "定西市安定區(qū)"
}
},
components: {
student: student // 把student注冊給school矗积,成為school組件的子組件
}
})
創(chuàng)建非單文件student組件
<body>
<div id="root">
<h1>{{message}}</h1>
// 使用schoo組件全肮,組件內(nèi)部還有個student組件
<xuexiao></xuexiao>
</div>
</body>
new Vue({
el: "#root",
data: {
message: "hello"
},
// 注冊組件
components: {
xuexiao: school
}
})
=========================================================
VueComponent
- school 組件本質(zhì)是一個VueComponent的構(gòu)造函數(shù),不是程序員定義的棘捣,是由Vue.extend 生成的
- 我們只要寫<school></school>辜腺,Vue解析時就會幫我們創(chuàng)建school組件的實(shí)例對象
即Vue幫我們執(zhí)行new VueComponent(options) - 特別注意,每次調(diào)用Vue.extend, 返回的都是一個全新的VueComponent
- 關(guān)于this指向
組件配置中乍恐,data,methods,watch,computed中的函數(shù)评疗,它們的this均指向VueComponent示例對象
new Vue 配置中的data,methods,watch,computed中的函數(shù),它們的this均指向Vue實(shí)例對象 - VueComponent 的實(shí)例對象茵烈,以后簡稱VC
Vue的實(shí)例對象百匆,簡稱cm
vm 可以傳入el,vc不行
function demo(){
this.a = 1
this.b = 2
}
const d = new Demo()
demo身上有個屬性叫 prototype 顯式原型屬性
demo.prototype
d 身上有個屬性叫 proto (隱式原型屬性)
d.proto
demo.prototype 和 d.proto 都指向同一個原型對象
// 程序員通過顯示原型屬性瞧毙,給demo追加一個x屬性
demo.prototype.x = 99
d.proto.x 就能拿到值胧华,這么寫也能拿到d.x
=================================================
一個重要的內(nèi)置關(guān)系,VueComponent.prototype.proto === Vue.prototype
為什么有這個關(guān)系:讓組件實(shí)例對象vc可以訪問Vue原型上的屬性和方法
比如:Vue.prototype.x = 99
Vue下的組件school 也可以訪問到x宙彪,vc.x
===================================================
單文件組件 以.vue結(jié)尾
.vue 文件可以被vue腳手架編譯成js文件
文件結(jié)構(gòu)
定義School組件
<template>
<div>
<h2>學(xué)習(xí)名稱:{{schoolName}}</h2>
</div>
</template>
<script>
// 把school對象暴露出去,讓別的地方可以調(diào)用
export default Vue.extend({
name: "School"
});
// 簡寫形式
export default {
name: "School"
};
</script>
<style>
.demo {
background-color: red;
}
</style>
定義App.Vue 組件領(lǐng)導(dǎo)所有組件
<template>
<div>
<school></school>
</div>
</template>
<script>
import School from './School'
export default {
name: 'App',
components: {
School: School
}
}
</script>
創(chuàng)建main.js
import App from './App.vue'
new Vue({
el: '#root',
template: '<app></app>' // 把root div用APP替換掉
components: {
App: App
}
})
創(chuàng)建index.html
<body>
<div id="root"></div>
<script type="text/javascript" src="./js/vue.js"></script>
<script type="text/javascript" src="./main.js"></script>
</body>
以上代碼放到vue腳手架里就可以運(yùn)行
Vue 腳手架是Vue官方提供的開發(fā)平臺
babel es6 => es5
eslint 語法檢查
npm run serve 運(yùn)行項(xiàng)目
腳手架結(jié)構(gòu):
new Vue({
render: h => h(App)
}).destory銷毀一個組件有巧,再通過$amount 掛載一個新的組件
vue 主要包含兩個部分
vue.js 與vue.runtime.xxx.js 的區(qū)別
vue.js 是完整版vue释漆,包含: 核心功能+模板解析器
vue.runtime.xxx.js 是運(yùn)行版本vue,只包含 核心功能
因?yàn)関ue.runtime.xxx.js 沒有模板解析器篮迎,所以不能使用tempplate配置項(xiàng)
需要使用render函數(shù)接收createElement函數(shù)去指定具體內(nèi)容
=============================================================
vue 腳手架依賴webpack男图,所有配置都在webpack.config.js 中
想要查看配置示姿,運(yùn)行以下命令:
vue inspect > output.js,所有配置都會被生成到output.js 中
入口文件就在entry 下面配置
想要自定義配置逊笆,可以在根目錄添加vue.config.js栈戳,可以調(diào)整腳手架工作模式
此配置最終有webpack接手,采用common.js 的暴露方式难裆,因?yàn)閣ebapck就用的是common.js
可以在vue.config.js 設(shè)置關(guān)閉語法檢查
lintOnSave: false
================================================================
vue 獲取dom元素
ref 用于給元素或者子組件注冊引用信息
應(yīng)用在html標(biāo)簽上獲取的是真實(shí)的DOM元素子檀,應(yīng)用在組件標(biāo)簽上獲取的是組件實(shí)例對象 VC
<template>
<h1 v-text="msg" ref="title"></h1>
<School ref="school"/>
</template>
<script>
export default {
name: "App",
components: {School},
data() {
return {
msg: "hello"
}
},
methods: {
showDom() {
// 獲取h1 dom 元素
console.log(this.refs.school // 獲取組件實(shí)例對象
}
}
}
</script>
============================================================
props 用于向子組件傳參
父組件調(diào)用:
<Student name="馬保國" :age="45" gender="男"></Student>
子組件:
<template>
<div>
<h1>{{name}}</h1>
<h1>{{age}}</h1>
<h1>{{gender}}</h1>
</div>
</template>
<script>
export default({
name: "Student",
data(){
return {
msg: "哈嘍啊,大家好",
// name: "張三",
// age: 18,
// gender: "女"
}
},
// 外部調(diào)用傳遞進(jìn)來
// props: ['name', 'age', 'gender']
// 對接收參數(shù)作類型限制
// props: {
// name: String,
// age: Number,
// gender: String
// }
// 更完善的寫法如下乃戈,顯示類型和是否必填
props: {
name: {
type: String,
required: true
},
age: {
type: Number,
required: false,
default: 20
},
gender: {
type: String,
required: false
}
}
})
</script>
====================================================================
props 是只讀的
如果業(yè)務(wù)一定要改褂痰,先把傳進(jìn)來的屬性賦值給,data屬性症虑,然后隨便改
如下缩歪,myname 隨便改
data(){
return {
msg: "哈嘍啊,大家好",
myName: this.name
}
}
props: {
name: {
type: String,
required: true
}
}
=====================================================================
假如兩個vue組件用了相同的配置 methods谍憔,可以通過以下方法實(shí)現(xiàn)代碼復(fù)用
mixins 屬性匪蝙,就是用來復(fù)用配置的
假如混合傳入一個x屬性,組件本身也有一個x屬性习贫,那么就以組件本身為準(zhǔn)
但是注意F邸!I蛱酢P璺蕖!
混合引入的生命周期鉤子是 【都要@酢N堇濉!】兩個都生效
寫一個mixin.js
export const hunhe = {
methods: {
showName() {
alert(this.name)
}
}
}
使用的時候 引入
import {mixin} from '../mixin'
export default {
name: "school",
data(){
return {
name: "zhangsan"
}
},
// 此處的hunhe是外部引入的月而,下面的節(jié)點(diǎn)可以直接被當(dāng)前組件使用
// 你在引入的hunhe里配置的data,methods,mounted 等任何節(jié)點(diǎn)都可以共用
mixins: [hunhe]
}
===============================================
全局引入混合:
在main.js 引入mixin.js汗洒,并注冊
import main from './main.js'
Vue.mixin(hunhe)
以后項(xiàng)目下的所有子組件都會引入hunhe 里的配置
=================================================
插件就是一個包含instal方法的一個對象,install的第一個參數(shù)是Vue父款,
第二個及以后的參數(shù)是插件使用者傳遞的數(shù)據(jù)
新建一個plugins.js
export default {
install(Vue) {
// 注冊全局過濾器
Vue.filter('mySlice', function(value) {
return value.slice(0, 4);
})
// 注冊全局指令
Vue.directive('fbind': {
bind(element, binding) {
element.value = binding.value
},
inserted(element, binding) {
element.focus();
},
update(element, binding) {
element.value = binding.value
element.focus()
}
});
// 定義全局混入
Vue.mixin({
data() {
return {
x: 99
}
}
})
// 給Vue全局上添加方法
Vue.prototype.hello = () => {
alert("你好啊");
}
}
}
在main.js里引入并注冊
import plugins from './plugins.js'
Vue.use(plugins)
=======================================================
組件樣式類名如果沖突溢谤,后來的就會覆蓋前面的
比如:先引入School, 再引入Student,那么Student的同名樣式會覆蓋School的
想要 樣式不沖突憨攒,給style添加scoped屬性世杀,style 的樣式之在當(dāng)前組件生效
<style scoped>
...
</style>
實(shí)現(xiàn)原理:Vue給樣式加了scoped屬性的組件生成一個隨機(jī)屬性
用的時候會通過類名 + 隨機(jī)屬性來匹配樣式
<div data-v-256434>...</div>
.demo-style[data-v-256434] {
...
}
!!!!!!App組件不應(yīng)該加scoped
App里寫的樣式一般是項(xiàng)目里通用的,加上的話子組件就用不了了
========================================================
// 這么配置寫樣式可以用less, 不屑默認(rèn)是css
less 可以嵌套著寫肝集,css不允許
<style lang="less">
.demo{
background-color: pink;
.qwe {
font-size: 40px;
}
}
</style>
=======================================================
平級組件之間傳遞參數(shù)
最初級方式:子組件 把數(shù)據(jù)傳給父組件瞻坝,父組件再把數(shù)據(jù)傳給 另一個子組件
父組件App把a(bǔ)ddToDo方法傳給子組件header
header 里的輸入事件調(diào)用addToDo方法,往父組件添加數(shù)據(jù)
這里有個原則:數(shù)據(jù)在哪里杏瞻,操作數(shù)據(jù)的方法就在哪里
toDoList 數(shù)據(jù)又傳給+了子組件list
這樣 子組件header 就把數(shù)據(jù)通過父組件header傳給了自己的兄弟組件 list
父組件 App
<template>
<header :addToDo="addToDo"></header>
<list :toDoList=toDoList></list>
</template>
<script>
import header from './header'
import list from './list'
export default ({
data() {
return {
toDoList: []
}
},
methods: {
addToDo(toDoObj){
// 給數(shù)組前面加一個元素
this.toDoList.unshift(toDoObj);
}
}
})
</script>
子組件header
<template>
<input type="text" @keyup.enter="add"/>
</template>
<script>
export default ({
data() {
return {
toDoList: []
}
},
props: [addToDo]
methods: {
add(e) {
const toDoObj = {id: '001', title='e.target.value', done: false };
this.addToDo(toDoObj);
}
}
})
</script>
子組件list
<template>
<ul>
<li v-for="let toDoObj in toDoList" :key="toDoObj.id">{{toDoObj.title}}</li>
</ul>
</template>
<script>
export default ({
data() {
return {
toDoList: []
}
},
props: [toDoList]
methods: {
addToDo(toDoObj) {
// 給數(shù)組前面加一個元素
this.toDoList.unshift(toDoObj);
}
}
})
</script>
使用v-model時候切記所刀,v-model 綁定的值不能是props 傳遞過來的衙荐,因?yàn)閜rops是不可以修改的
如果props 傳過來的是對象屬性,v-model綁定其中的某一個屬性是可以修改的浮创,但是不推薦
=================================================
統(tǒng)計(jì)數(shù)據(jù)函數(shù)忧吟,reduce
pre 初始值為0,對象的done為true斩披,統(tǒng)計(jì)+1
list.reduce((pre, current) => pre + (current.done ? 1 : 0), 0)
計(jì)算屬性允許套娃溜族,也就是說計(jì)算屬性 可以用別的計(jì)算屬性來計(jì)算
瀏覽器本地存儲,能存儲5M左右的字符串
localStorage: 瀏覽器關(guān)了再打開雏掠,存儲的數(shù)據(jù)還在
localStorage.setItem("", "")
localStorage.getItem("")
localStorage.removeItem("")
localStorage.clear()
sessionStorage: 瀏覽器已關(guān)閉斩祭,存儲數(shù)據(jù)就沒了
sessionStorage.setItem("", "")
sessionStorage.getItem("")
sessionStorage.removeItem("")
sessionStorage.clear()
=============================================================
組件的自定義事件:
父組件實(shí)現(xiàn)
父組件使用Student組件,往Student組件身上綁定一個myevent事件乡话,事件觸發(fā)就調(diào)用testMethod方法
<Student v-on:myevent="testMethod"></Student>
// 只觸發(fā)一次
<Student @myevent.once="testMethod"></Student>
methods: {
testMethod(name) {
console.log("收到name:" + name);
}
}
子組件
點(diǎn)擊按鈕調(diào)用emit('myevent', this.name);
}
}
父組件還可以通過ref屬性給子組件綁定事件
這種事件靈活性強(qiáng)摧玫,可以異步實(shí)現(xiàn)
<Student ref="student"></Student>
mounted: {
setTimeout(() => {
this.on('myEvent', this.testMethod)
// 只觸發(fā)一次
// this.once('myEvent', this.testMethod)
}, 3000)
},
methods: {
testMethod(name) {
console.log(name);
}
}
================================================
解綁自定義事件:
Student 組件定義方法
methods: {
unbindEvent(){
this.off(["myEvent", "myEvent2"]); // 解綁多個
this.$off(); // 解綁所有
}
}
this.$destory() 方法調(diào)用后,組件和子組件會被銷毀绑青,Student的自定義事件全都不奏效诬像,原生事件不受影響
=============================================
給屬性綁定原生事件:
Student組件所在的div一點(diǎn)擊就能觸發(fā)click事件
不寫.netive Vue默認(rèn)click是組件自定義事件蔽挠,點(diǎn)點(diǎn)點(diǎn)是沒有作用的
這也是vue模板根元素只能由一個的原因之一芍碧,不然寫了多個根元素,組件的原生click事件不知道綁給誰
<Student ref="student" @click.native="show"></Student>
==============================================
全局事件總線:可以實(shí)現(xiàn)任意組件間的通信
全局事件總線又以下要求
1. 全局訪問
2. 可以調(diào)用 emit,
once方法
所以奠涌,可以使用Vue原型對象來作為載體
實(shí)現(xiàn)方式如下:
- 在Main.js 安裝全局事件總線
new Vue({
el: "#app",
render: h => h(App),
// 在Vue啟動之之前在Vue的原型對象上 添加事件總線
// bus = this
}
})
組件School里面注冊一個事件
mounted: {
this.on('hello', (data) => {
console.log('我是school組件邪乍,收到了數(shù)據(jù)', data);
});
},
// 組件銷毀之前降狠,關(guān)閉hello事件監(jiān)聽
beforeDestory() {
this.off('hello');
}
組件Student里觸發(fā)事件,發(fā)送數(shù)據(jù)
methods: {
sendStudentName() {
this.emit('hello', this.name);
}
}
====================================================
消息訂閱與發(fā)布:
- 訂閱消息:消息名
- 發(fā)布消息:消息內(nèi)容
訂報紙:
- 訂閱報紙:住址
- 郵遞員送報紙:報紙
安裝pubsub.js庫(publish,subscribe)
npm i pubsub-js
在School中訂閱消息
import pubsub from 'pubsub-js'
mounted:{
this.pubId = pubsub.subscribe('hello', function(msgName, data) {
console.log('有人發(fā)布了hello消息', data);
})
}
// 銷毀前取消訂閱
beforeDestory() {
pubsub.unsubscribe(this.pubId);
}
在Student中發(fā)布消息
methods: {
publishStudentName() {
pubsub.publish('hello', 666)
}
}
==============================
判斷tudo對象是否具有isEdit屬性庇楞,用以下方法
todo.hasOwnProperty('isEdit')
有這樣一個功能
點(diǎn)擊編輯按鈕榜配,數(shù)據(jù)顯示到一個輸入框里,并且自動獲取焦點(diǎn)
e.target.focus() 這句話不會生效B郎巍5叭臁!
為什么??? 因?yàn)閟howEdit改為true之后睛驳,界面上不會立即顯示<input>框
要等到handelEdit方法執(zhí)行完采取更新界面烙心,那么e.target.focus() 在input顯示出來之前就去獲取焦點(diǎn),肯定是失敗的
可以使用timeout函數(shù)乏沸,正確的做法是用 nextTick 在下一次DOM更新結(jié)束后執(zhí)行其指定的回調(diào)函數(shù)
什么時候用淫茵?當(dāng)改變數(shù)據(jù)后,要基于更新后的DOM進(jìn)行某些操作時屎蜓,就要在netTick指定的回調(diào)函數(shù)中執(zhí)行
<input v-show="showEdit" type="text" :value="todo.title" ref="inputTitle"/>
handelEdit(e){
this.showEdit = true;
// this.nextTick(function() {
this.$refs.inputTitle.focus();
})
}
======================================
Animate.js // 第三方動畫庫
npm install animate.css
import 'animate.css'
使用:
<transition-group
appear
name="animate__animated animate_bounce"
enter-active-class="animate__swing"
leave-active-class="animate__backOutUp"
>
<h1 v-show-"!isShow" key = "1">你好岸笈驻啤!</h1>
<h1 v-show="isShow" key="2">尚硅谷!</h1>
</transition-group>
Vue 封裝的過渡與動畫
- 作用:在插入荐吵,更新骑冗,或移除DOM元素時,在合適的時候給元素添加各種樣式
- 進(jìn)入:v-enter-active先煎,退出:v-leave-active
- 寫法:
- 準(zhǔn)備好樣式
- v-enter: 進(jìn)入的起點(diǎn)
- v-enter-active: 進(jìn)入的過程
- v-enter-to:進(jìn)入的終點(diǎn)
- 元素離開的樣式
- v-leave:退出的七點(diǎn)
- v-leave-active:離開的過程
- v-leave-to:離開的終點(diǎn)
- 使用
<transition name="hello">
<h1 v-show="isShow">你好啊</h1>
</transition> - 如果又多個元素需要過度贼涩,則需要使用<transition-group>,且每個元素都要指定key
=======================================================================
解決跨域問題薯蝎,配置代理服務(wù)器
- 準(zhǔn)備好樣式
配置方式1:
在vue.config.js 中添加
devServer: {
// 代理服務(wù)器地址
proxy: 'http://localhost:5000'
}
使用的時候調(diào)用請求8080遥倦,代理服務(wù)器會把請求轉(zhuǎn)發(fā)給5000
配置方式2:
請求8080/api/[path]
會轉(zhuǎn)發(fā)給5000/[path]
devServer: {
proxy: {
'/api': {
target: 'http://localhost:5000',
// 轉(zhuǎn)發(fā)的時候把API替換成空
pathRewrite: {'/api':''},
// 支持websocket
ws: true,
// 修改HttpRequest中的Host為被代理服務(wù)器Host
// 為true host是5000,為false占锯,host為8080
changeOrigin: true
},
// 可以配置多個代理
// '/api2': {
// ...
// }
}
}
=======================================================================
引入靜態(tài)資源
在src下創(chuàng)建assets/css文件夾
在assets/css下添加bootstrap.css
在App.vue中引入
// 用import方式引入css文件袒哥,假如css使用了不存在的資源,腳手架會做嚴(yán)格檢查
import './assets/css/bootstrap.css'
// 想要不報錯消略,可以換一種引入方式
在public創(chuàng)建css文件夾堡称,在index.html中引入
<link res="stylesheet" href="<% BASE_URL %>css/bootstrap.css">
================================================================
在組件Category內(nèi)部定義一個插槽,挖個坑艺演,等待使用者填充
默認(rèn)插槽
<div>
<h1>標(biāo)題</h1>
<slot>不給插槽傳遞東西却紧,這句話就會顯示</slot>
</div>
調(diào)用者給插槽傳入一個圖片,圖片就會展示在組件插槽的位置上
<category>
<img src="XXX" alt="">
</category>具名插槽
有多個插槽胎撤,需要指定插槽名稱
<div>
<h1>標(biāo)題</h1>
<slot name="center"></slot>
<slot name="bottom"></slot>
</div>
調(diào)用者需要需要傳插槽名稱
<category>
<img slot="center" src="XXX" alt="">
<a slot="bottom" >我是超鏈接</a>
</category>
調(diào)用插槽還有一種寫法
通過template包裹需要傳入的東西晓殊,給template添加標(biāo)簽 v-slot:bottm
<category>
<img slot="center" src="XXX" alt="">
<template v-slot:bottm>
<div>
<a >我是超鏈接</a>
</div>
<h4>歡迎光臨</h4>
</template>
</category>
- 作用域插槽
在組件定義一個插槽,不一樣的地方在于插槽有個綁定參數(shù)哩照,插槽會把參數(shù)傳給調(diào)用者
<div>
<slot :games="games"></slot>
</div>
調(diào)用者使用scope(名字隨便起)變量接收插槽傳過來的數(shù)據(jù)挺物,然后渲染
<category>
<template scope="scope">
<ul>
<li v-for="(g, index) in scope.games" :key="index">{{g}}</li>
</ul>
</template>
</category>
也可以通過解構(gòu)賦值獲取games,這么寫
<category>
<template scope="{games}">
<ul>
<li v-for="(g, index) in games" :key="index">{{g}}</li>
</ul>
</template>
</category>
F J短佟!4瘟妗3彰痢!9谕酢赶撰!作用域插槽也可以有名字,傳入時指定名字即可
============================================================
vuex 是什么
用來對Vue中多個組件中共享的數(shù)據(jù)進(jìn)行集中式的狀態(tài)管理,也是一種組件間的通信方式
- 什么時候使用vuex豪娜?
- 當(dāng)多個組件依賴同一個數(shù)據(jù)時候
- 來自不同組件的行為需要變更同一數(shù)據(jù)的時候
107 需要回看
安裝vuex
npm -i vuex@3餐胀,Vue2 只能使用版本3 的vuex,Vue3 只能使用4版本的veux
創(chuàng)建在根目錄store/index.js
// 該文件用于創(chuàng)建Vuex中最為核心的store
import Vue from 'vue'
import Vuex from 'vuex'
// 使用vuex插件
Vue.use(Vuex);
// 準(zhǔn)備actions---用于響應(yīng)組件中的動作瘤载,通過this.$store.dispatch()調(diào)用
const actions = {}
// 準(zhǔn)備mutations---用于操作數(shù)據(jù)(state)否灾,通過this.$store.commit()調(diào)用
const mutations = {}
// 準(zhǔn)備state---用于存儲數(shù)據(jù)
const state = {}
// 用于將state中的值加工輸出
const getters = {}
// 創(chuàng)建store
export default new Vuex.Store({
actions,
mutations,
state,
getters
})
在main.js 中引入vuex
import store from './store/index'
new Vue({
el: "#app",
render: h => h(App),
store: store // 安裝store
})
vuex使用流程
- 數(shù)據(jù)保存在state下
const state = {
sum: 0
}
- 在組件中通過this.$store.dispatch('addWait', 2) 調(diào)用action中的方法
actions 中的方法可以寫一些通用的業(yè)務(wù)邏輯,操作state數(shù)據(jù)的操作通過調(diào)用mutations 中的方法來完成
actions 中的方法還可以繼續(xù)通過dispatch來調(diào)用其他的actions方法
const actions = {
addWait(context, value) {
settimeOut(() => {
context.commit('add', value);
}, 500);
}
}
- 在actions中通過context.commit調(diào)用mutations 中的方法
mutations 中的方法用來直接修改state中的數(shù)據(jù)鸣奔,一般不寫業(yè)務(wù)邏輯
Vue開發(fā)者工具直接監(jiān)聽mutations墨技,如果在其他地方修改state中的數(shù)據(jù),開發(fā)者工具檢測不到
const mutations = {
add(state, value) {
state.sum += value;
}
}
- mutations修改完數(shù)據(jù)挎狸,模板會重新渲染
- getters 使用方法
getter 接收一個state參數(shù)扣汪,可以把state中的數(shù)據(jù)做加工,然后輸出
const getters = {
bigSum(state) {
return state.sum * 10;
}
}
組件使用bigSum可以通過this.$store.getters.bigSum獲取
mapState
獲取store下的數(shù)據(jù)必須要寫成 this.store.state...
其中this.$store.state.可以通過mapState簡化
import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
export default{
// ...
computed: {
// mapState把state中的sum返回給計(jì)算屬性he崭别,使用的時候直接取this.he (對象寫法)
//...mapState({he: 'sum', xuexiao: 'school'})
// 如果映射后的計(jì)算屬性名跟state字段名稱一樣,可以用數(shù)組寫法统刮,更簡便
...mapState(['sum', 'school'])
// mapGetters同理紊遵,跟mapState一樣一樣的,自己實(shí)現(xiàn)
},
methods: {
// 通過mapMutations生成mutations方法侥蒙,
// 注意暗膜,加入有參數(shù)要傳遞,調(diào)用生成方法jia的時候可以傳參
...mapMutations({jia: 'sum', jian: 'sub'})
// mapMutations 簡寫形式如mapSate
// mapActions 同理鞭衩,自己實(shí)現(xiàn)
}
}
===========================================================================
vuex模塊化編程
假如很多個業(yè)務(wù)的 actions学搜,state,mutations论衍,getters全部混在一起放在store里瑞佩,代碼將變得難以維護(hù),vuex模塊化編程支持把a(bǔ)ctions等以模塊化分類坯台,放到不同模塊地下炬丸,這樣編碼和調(diào)用都將變得更有條理
- store 里面采用模塊化編程
const personOptions = {
namespaced: true,
actions: {},
mutations: {
add(state, value) {
state.sum += value;
},
sub(state, value) {
state.sum -= value;
}
},
state: {
school: 'YC',
},
getters: {}
}
const countOptions = {
namespaced: true,
actions: {},
mutations: {},
state: {
sum: 0,
},
getters: {
mutiTen(state) {
return state.sum * 10;
}
}
}
export default new Vuex.Store({
modules: {
person: personOptions,
count: countOptions
}
})
- 組件使用時:
<h1>{{count.sum}}</h1>
<h1>{{count.add(2)}}</h1>
computed: {
...mapState(['person', 'count']),
// 想要直接取到count下state里面的sum,這么寫
// 同時要在store 里面countOptions 下面加上namespeaced: true !!!!!!!!!!!!
...mapState('count', ['sum']),
// 用map獲取getters里面的數(shù)據(jù)同state
// 如果不想用map蜒蕾,可以這么寫
sum() {
return this.$store.state.count.sum;
}
// 不用map獲取getters里的數(shù)據(jù)稠炬,注意跟獲取state不一樣,要這么寫
bigSum() {
return this.$store.getters['count/mutiTen']
}
}
// ...
methods: {
...mapActions(['person', 'count'])
// 想要直接取count下mutations里面的add咪啡,sub方法可以這么寫
// 同時要在store 里面countOptions 下面加上namespeaced: true !!!!!!!!!!!!
...mapMutations('count', {jia: 'add', jian: 'sub'})
// 如果獲取mutations里的方法不用重命名首启,直接這么寫
...mapMutations('count', ['add', 'sub'])
// 如果不想用map,可以這么寫
add() {
this.$store.commit('count/add', 2)
}
}
模塊化編程可以把 上面例子中的count和person寫到不同的js文件里面撤摸,在store/index.js 中引入再注冊
=======================================================================================
vue 路由
- 安裝
vue3 對應(yīng) vue-router@4毅桃, vue2對應(yīng) vue-router@3
npm i vue-router@3
- 創(chuàng)建路由
在根目錄新建router/index.js文件
// 該文件用于創(chuàng)建應(yīng)用的路由器
import VueRouter from 'vue-router'
import About from '../components/About'
import Home from '../components/Home'
export default new VueRouter({
routes: [
{
path: '/about',
component: About
},
{
path: '/home',
component: Home
}
]
})
- 引入
在main.js
import router from './router'
Vue.use(VueRouter)
new Vue({
el: '#app',
render: h => h(App),
router: router
})
- 使用
- 在頁面中通過router-link 跳轉(zhuǎn)路由
- 在頁面中通過router-view 指定組件呈現(xiàn)位置
<router-link class="..." active-class="路由激活時候的高亮樣式" to="/about"></router-link>
<router-link class="..." active-class="路由激活時候的高亮樣式" to="/home"></router-link>
<div class="panel-body">
// 指定組件的呈現(xiàn)位置
<router-view></router-view>
</div>
============================================
- 組件一般分為路由組件和普通組件褒纲,路由組件一般放在pages文件夾下,普通組件一般放在components文件夾下
- 通過切換钥飞,隱藏了的路由組件默認(rèn)是被銷毀了莺掠,需要的是再重新掛載
- 每個組件都有自己的$route屬性,里面存儲著自己的路由信息
- 整個應(yīng)用只有一個router代承,可以通過組建的$router屬性獲得
=========================================================
子路由
- 子路由配置在父路由節(jié)點(diǎn)下的children下
- 子路由path前面不要加/
- router-link使用子路由的時候要帶上父路由汁蝶,比如:
<router-link active="active-class" to="/home/news">news</router-link>
export default new VueRouter({
routes: [
{
path: '/about',
component: About
},
{
path: '/home',
component: Home,
children: [
{
path: 'message',
component: Message
},
{
path: 'news',
component: News
}
]
}
]
})
=======================================================
路由傳參
- 在路由組件里過query傳參
// 在router-link里傳參
<router-link to="/home/new/message?id=666&title=ddd">message</router-link>
// 動態(tài)傳參渐扮,to 必須要用bind取綁定论悴,to里面用模板字符串
<router-link :to="`/home/new/message?id=${m.id}&title=${m.title}`">message</router-link>
// 在message組件里接收參數(shù)
this.$route.query.id
this.$toute.query.title
- 對象傳參
<router-link
:to={
path:'/home/message/detail',
query: {
id: m.id,
title: m.title
}
}>
{{m.title}}
</router-link>
==========================================================================
命名路由
- 命名路由的作用就是簡化路由跳轉(zhuǎn)
- 使用
// 給路由命名
{
path: '/demo',
component: Demo,
children: [
{
path: 'test',
component: Test,
children: [
{
name: 'hello',
path: 'welcome',
component: Hello
}
]
}
]
}
// 簡化跳轉(zhuǎn)
完整路徑寫法
<router-link to="/demo/test/welcome">跳轉(zhuǎn)</router-link>
簡化寫法
<router-link :to="{name: 'hello'}">跳轉(zhuǎn)</router-link>
==============================================================
params 傳參
- 在路由里配置參數(shù)
{
path: 'message',
component: Message,
children: [
{
name: 'detail',
path: 'detail/:id/:title',
component: Detail
}
]
}
- 跳轉(zhuǎn)時攜帶參數(shù)
// 方式1
<router-link :to="`/message/detail/${m.id}/${m.title}`">詳情<router-link>
// 方式2
<router-link :to="{
name: 'detail', // 對象寫法此處只能支持name!D孤伞0蚬馈!耻讽!察纯,不支持path
params: {
id: m.id,
title: m.title
}
}">
</router-link>
- 使用時獲取參數(shù)
this.$route.params.id
this.$route.params.title
=================================================================
路由的props
- 第一種寫法,傳固定值(此寫法用處極少针肥,幾乎沒用饼记。。慰枕。)
props 值為對象具则,對象中的key-value都會以props的形式傳遞給Detail組件
// 在route中配置
{
path: 'message',
component: Message,
children: [
{
path: 'detail',
component: Detail,
props: {
a: 1,
b: 'hello'
}
}
]
}
// 在detail中通過props配置項(xiàng)接收參數(shù)
<div>
<h1>a: {{this.a}}</h1>
<h1>b: {{this.b}}</h1>
</div>
name: 'Detail',
computed: {
// ...
},
props: ['a', 'b']
- 第二種寫法,路由配置props為true
props 值為布爾值具帮,若布爾值為真博肋,就會把路由組件收到的所有params參數(shù),以props的形式傳遞給Detail組件
// 在route中配置
{
path: 'message',
component: Message,
children: [
{
path: 'detail/:id/:title',
component: Detail,
props: true
}
]
}
// 在detail中通過props配置項(xiàng)接收參數(shù)
<div>
<h1>a: {{this.a}}</h1>
<h1>b: {{this.b}}</h1>
</div>
name: 'Detail',
computed: {
// ...
},
props: ['a', 'b']
- 第三種寫法蜂厅,路由配置props為一個函數(shù)
函數(shù)里返回值返回的key-value會傳遞給detail組件的props
// 在route中配置
{
path: 'message',
component: Message,
children: [
{
path: 'detail/:id/:title',
component: Detail,
props($route) {
return {id: $route.query.id, title: $route.query.title}
}
// 可以用解構(gòu)賦值簡化為下面這種寫法
props({query: {id, title}}) {
return {id, title}
}
}
]
}
// 在detail中通過props配置項(xiàng)接收參數(shù)
<div>
<h1>a: {{this.a}}</h1>
<h1>b: {{this.b}}</h1>
</div>
name: 'Detail',
computed: {
// ...
},
props: ['a', 'b']
===========================================================
router-link 處理瀏覽器歷史記錄
- 模式1:push模式(默認(rèn))
每次新的瀏覽記錄會被追加到歷史記錄里面
- 模式2:replace模式(需要手動開啟)
每次產(chǎn)生的新的歷史記錄會替換上一條歷史記錄
<router-link replace></router-link>
啥時候用push匪凡,啥時候用replace?掘猿?病游?
比如有這么一個頁面A,A里面有幾個子路由B稠通,C衬衬,D
假如想要A 進(jìn)入時候保留歷史記錄,而B采记,C佣耐,D三個子路由不保留歷史記錄,后退直接到A唧龄,則可以這么設(shè)置
A 為push 模式兼砖,B奸远,C,D為replace 模式
先點(diǎn)A讽挟,然后點(diǎn)B懒叛,C,D耽梅,點(diǎn)一下瀏覽器后退按鈕,頁面直接退回A眼姐,
因?yàn)锽诅迷,C,D為replace模式众旗,雖然點(diǎn)了三下罢杉,但是只保留了一條歷史記錄,一后退直接就回到A了
=====================================================================
編程式路由導(dǎo)航
methods:{
// 用push模式跳轉(zhuǎn)路由
pushShow(m) {
this.$router.push({
path: '/home/message',
query: {
id: m.id,
title: m.title
}
})
},
// 用replace模式跳轉(zhuǎn)路由
replaceShow(m) {
this.$router.replace({
path: '/home/message',
query: {
id: m.id,
title: m.title
}
})
},
// 路由回退
back(){
this.$router.back();
}
// 路由前進(jìn)
forward(){
this.$router.forward();
},
// go test
goTest(){
// 后退兩步
this.$router.go(-2);
// 前進(jìn)三步
this.$router.go(3);
}
}
===============================================================
緩存路由組件
- 路由組件切換默認(rèn)是先銷毀贡歧,再重新掛載新的路由組件
- 想要前一個組件不被銷毀滩租,而是隱藏起來,那就要設(shè)置<router-view>的緩存
// 當(dāng)前router-view切換的所有組件都緩存起來利朵,不銷毀
<keep-alive>
<router-view></router-view>
</keep-alive>
// keep-alive還可以指定特定組件緩存律想,其他銷毀,此處只指定News組件緩存
<keep-alive include="News">
<router-view></router-view>
</keep-alive>
// 指定緩存多個組件
<keep-alive :include="['News', 'Messages']">
<router-view></router-view>
</keep-alive>
======================================================
路由組件獨(dú)有的生命周期鉤子
- activated: 路由組件激活會被調(diào)用
- deactivated: 路由組件失活會被調(diào)用
對于路由組件來說绍弟,如果被指定緩存了(keep-alive)技即,那么組件的mounted 和 beforeDestory兩個鉤子組件就失效不會被調(diào)用了,這時候可以用activated和deactivated兩個鉤子函數(shù)替代
===========================================================
路由守衛(wèi)
- 配置全局前置路由守衛(wèi)
const router = new VueRouter({
routes: [
{
path: '/about',
component: About
},
{
path: '/home',
component: Home,
children: [
{
path: 'message',
component: Message
},
{
path: 'news',
component: News
}
]
}
]
})
// 全局前置路由守衛(wèi)晌柬,初始化被調(diào)用姥份,每次路由切換之前調(diào)用
router.beforeEach((to, from, next) => {
// to: 要去的路由
// from: 來的路由
// next: 執(zhí)行跳轉(zhuǎn),不調(diào)用next則停止跳轉(zhuǎn)
if(to.path === 'home/news' || to.path === 'home/messages'){
if (localStorage.getItem('role') === 'admin'){
next();
} else {
alert('沒有權(quán)限D甑狻3呵浮!');
}
} else {
next();
}
})
export default router
- 配置route的元信息
路由配置里有個meta配置項(xiàng)屿衅,接收一個對象埃难,配置好之后可以在this.$route 中拿到
// 比如配置當(dāng)前路由是否需要授權(quán)
{
path: '/userinfo',
component: UserInfo,
meta: {
isAuth: true
}
}
在路由守衛(wèi)中可以用meta元信息作判斷
router.beforeEach((to, from, next) => {
// 判斷當(dāng)前路由是否需要授權(quán),如果需要授權(quán)涤久,再驗(yàn)證權(quán)限
if(to.meta.isAuth) {
if (localStorage.getItem('role') === 'admin'){
next();
} else {
alert('沒有權(quán)限N谐尽!响迂!');
}
} else {
next();
}
})
- 后置路由守衛(wèi): 初始化時和每次路由切換之后被調(diào)用
router.afterEach((to, from) => {
// to: 要去的路由
// from: 來的路由
// 后置路由守衛(wèi)沒有next考抄,因?yàn)槁酚梢呀?jīng)切換完成,沒有接下來的操作了
// 比如路由切換完成后去修改頁面title信息 (title 配置在路由元信息中)
document.title = to.meta.title;
})
- 獨(dú)享路由守衛(wèi):某一個路由單獨(dú)使用的路由守衛(wèi)
{
path: '/userinfo',
component: UserInfo,
meta: {
isAuth: true
},
beforeEnter(to, from, next){
// 內(nèi)部邏輯只對userinfo這個頁面產(chǎn)生作用
// 獨(dú)享路由守衛(wèi)只有前置蔗彤,沒有后置
}
}
- 組件內(nèi)路由守衛(wèi)
組件內(nèi)部實(shí)現(xiàn)的路由守衛(wèi)川梅,在路由進(jìn)入和離開時會調(diào)用
mounted() {
// ...
},
// 通過路由規(guī)則疯兼,進(jìn)入該組件時被調(diào)用
// 假如當(dāng)前組件作為子組件直接加載到頁面上,不是通過路由規(guī)則進(jìn)入到組件贫途,beforeRouteEnter 不會被調(diào)用
// afterRouteLeave同理吧彪,必須是通過路由規(guī)則進(jìn)入組件,在離開時才會被調(diào)用
beforeRouteEnter(to, from, next) {
// 可以寫鑒權(quán)邏輯
// 此處的to時當(dāng)前組件的route對象
},
// 通過路由規(guī)則丢早,離開該組件時被調(diào)用
afterRouteLeave(to, from, next) {
// 此處的to是離開當(dāng)前頁面要去的路由對象
// from是當(dāng)前組件的路由對象
}
beforeRouteEnter 和 afterRouteLeave 不與前置和后置路由守衛(wèi)對應(yīng)
前置后置路由守衛(wèi)是在頁面切換之前和之后調(diào)用姨裸,一旦頁面切換,必然會先調(diào)用前置路由守衛(wèi)怨酝,再調(diào)用后置路由守衛(wèi)
beforeRouteEnter 是在進(jìn)入頁面時被調(diào)用傀缩,如果停留在頁面上,afterRouteLeave 不會被調(diào)用凫碌,只有在跳走之前才會調(diào)用 afterRouteLeave
===============================================================================
vue 路由的hash工作模式和history工作模式
- hash 哈希模式路勁里帶有#, 對于瀏覽器來說往后端發(fā)送路勁只會發(fā)送#之前的扑毡,比如:
瀏覽器發(fā)送路由:http://www.test.com/userinfo/test1/#/sdfs/sdfsd/sdf
后端收到的只是#前面的部分:http://www.test.com/userinfo/test1/,#后面的部分不會發(fā)送給后端 - history模式
vue路由模式默認(rèn)是hash模式盛险,history模式需要手動開啟
const router = new VueRouter({
mode: history,
routes: [
{
path: '/about',
component: About
},
// ...
]
})
- hash模式和history模式的區(qū)別
- hash模式
- 路由中永遠(yuǎn)帶著#,不美觀
- 若以后將地址通過手機(jī)第三方app分享勋又,若app嚴(yán)格校驗(yàn)苦掘,則地址會被標(biāo)記為不合法
- 兼容性好
- history模式
- 路由中沒有#,顯得美觀
- 兼容性跟hash模式相比略差
- 應(yīng)用部署上線時楔壤,需要后臺服務(wù)器支持鹤啡,比如IIS,nginx另外作配置蹲嚣,否則頁面刷新會出現(xiàn)404問題
- 递瑰??隙畜? 為啥刷新會出現(xiàn)404抖部,因?yàn)関ue是單頁面應(yīng)用,路勁只是用于vue app內(nèi)部作路由跳轉(zhuǎn)
- history模式路由中沒有#议惰,頁面刷新瀏覽器會把整個路由發(fā)送給后臺慎颗,后臺找不到對應(yīng)的路勁,自然會被404
- 后臺服務(wù)器可以作相關(guān)配置言询,把該地址的路由都認(rèn)為是靜態(tài)路由俯萎,不返回404
==========================================================================
vue UI組件庫
- 移動端
- Vant
- Cube UI
- Mint UI
- PC 端
- element UI
- Iview UI
組件庫可以按需引入,也可以完整引入运杭,具體怎么引入夫啊,自己研究官方文檔!A俱尽撇眯!
===============================================================================
Vue3
- Vue3 帶來了什么
- 性能提升
- 打包速度谆趾,內(nèi)存減少,初次渲染速度
- 源碼升級
- 使用Proxy代替defineProperty實(shí)現(xiàn)響應(yīng)式
- 重寫虛擬dom的實(shí)現(xiàn)和Tree-Shanking
- 擁抱TypeScript
- 新特性
- 性能提升
- vue 3 不在通過Vue對象創(chuàng)建APP
// 引入工廠方法叛本,用于創(chuàng)建應(yīng)用實(shí)例對象
import { createApp } from 'vue'
// 根模塊
import App from './App.vue'
// 先創(chuàng)建 app實(shí)例對象沪蓬,然后把對象掛載到index.html的 app容器上
createApp(App).mount('#app')
- vue 3 的模板結(jié)構(gòu)可以沒有根標(biāo)簽
- vue 3 的setup
setup 是vue3 的一個新的配置項(xiàng),數(shù)據(jù)来候,方法跷叉,生命周期鉤子等都要寫在setup里
export default {
name: 'App',
setup() {
let name = 'zhangsan';
let age = 9;
function sayHello() {
alert(`I'm ${name} and ${age} years old`)
}
// 返回一個正常對象,對象里的屬性和方法可以被模板直接使用
return {
name,
age,
sayHello
}
// 2. 返回一個函數(shù)营搅,函數(shù)會渲染一個h1 替換模板的內(nèi)容
return () => h('h1', 'hunter')
}
}
=================================================================
vue 3 ref 函數(shù)
- vue 3 setup中申明的變量默認(rèn)不是響應(yīng)式的云挟,變量變化不會引起頁面的變化,想要讓變量成為響應(yīng)式變量转质,就要加上ref
setup() {
// 加了ref的變量就變成了一個 引用對象(refrence implement)
let name = ref('張三')
let age = ref(18)
let job = ref({
type: '前端工程師',
salary: '18k'
})
// 修改引用變量
function changeInfo(){
name.value = '李四'
age.value = 19
job.value.type = '后端工程師'园欣、
job.value.salary = '20k'
}
// 在模板里面讀取變量不用.value,vue3 解析發(fā)現(xiàn)對象是ref對象的時候會自動加上.value
}
- ref 函數(shù)處理基本數(shù)據(jù)類型休蟹,使用的是Object.defineProperty實(shí)現(xiàn)數(shù)據(jù)劫持沸枯,最后實(shí)現(xiàn)相響應(yīng)式
- ref 函數(shù)處理對象或數(shù)組數(shù)據(jù),使用的是Proxy來實(shí)現(xiàn)響應(yīng)式赂弓,Proxy 操作封裝在了一個名字叫reactive的函數(shù)中绑榴;所以處理對象或者數(shù)組建議直接使用reactive函數(shù)
- 用ref處理基本類型,修改數(shù)據(jù)永遠(yuǎn)都要用.value來訪問盈魁,比較麻煩翔怎,推薦用下面寫法
setup() {
// 推薦寫法!Q畎摇赤套!
let person = reactive({
name: '張三',
age: 18,
job: {
type: '前端工程師',
salary: '18K'
}
})
// 在模板里面讀取變量不用.value,vue3 解析發(fā)現(xiàn)對象是ref對象的時候會自動加上.value
return {
person
}
}
- reactive 總結(jié)
- 作用:定義一個對象類型的響應(yīng)式數(shù)據(jù)(基本類型還是使用ref)
- 語法:const 代理對象 = reactive(源對象)珊膜,接收一個對象容握,返回一個代理對象(Proxy的實(shí)例對象)
- reactive 代理的響應(yīng)式數(shù)據(jù)是深層次的
- 內(nèi)部基于es6的proxy實(shí)現(xiàn),通過代理對象操作源對象內(nèi)部數(shù)據(jù)進(jìn)行操作
===========================================================================
vue 2 響應(yīng)式原理總結(jié)
-
實(shí)現(xiàn)原理
- 對象類型:通過Object.defineProperty()對屬性的讀寫進(jìn)行劫持
- 數(shù)組類型:通過對數(shù)組的一系列方法進(jìn)行攔截(對數(shù)組的操作方法進(jìn)行了包裹)
-
存在的問題
- 新增刪除屬性界面不會更新辅搬,需要用Vue.set方法綁定新屬性唯沮,Vue.delete刪除一個存在的屬性
- 直接通過下標(biāo)修改數(shù)組,界面不會自動更新
=================================================================================
vue 3 的響應(yīng)式
可以直接新增堪遂、刪除對象屬性介蛉,可以直接修改數(shù)組元素,不需要使用vue 的set溶褪,delete 方法
代碼實(shí)現(xiàn)
let person = {
name: '張三',
age: 18
}
// 任何對p的操作都會修改person對象币旧,p是對person的代理
cosnt p = new Proxy(person, {
// 讀取屬性時會被調(diào)用
// target 是person對象
// propName 是讀取的屬性名稱
get(target, propName) {
return target[propName]
},
// 增加或者修改屬性時會調(diào)用
// target 是person對象
// propName 是讀取的屬性名稱
// value 是傳入的修改值
set(target, propName, value) {
target[propName] = value
},
// 刪除屬性的時候會調(diào)用
delete(target, propName) {
return delete target[propName]
},
})
- es6 反射
let obj = {a: 1, b: 2}
// 反射讀取屬性
Reflect.get(obj, 'a')
// 反射修改obj屬性
Reflect.set(obj, 'a', 110)
// 反射刪除obj屬性
Reflect.deleteProperty(obj, a)
// Reflect 也有defineProperty
// res 時布爾值,返回是否成功
const res = Reflect.defineProperty(obj, 'c', {
get(){
return 3
}
})
- vue 3操作Proxy對象其實(shí)用的是Reflect
let person = {
name: '張三',
age: 18
}
// 任何對p的操作都會修改person對象猿妈,p是對person的代理
cosnt p = new Proxy(person, {
// 讀取屬性時會被調(diào)用
// target 是person對象
// propName 是讀取的屬性名稱
get(target, propName) {
// return target[propName]
return Reflect.get(target, propName)
},
// 增加或者修改屬性時會調(diào)用
// target 是person對象
// propName 是讀取的屬性名稱
// value 是傳入的修改值
set(target, propName, value) {
// target[propName] = value
Reflect.set(target, propName, value)
},
// 刪除屬性的時候會調(diào)用
delete(target, propName) {
// return delete target[propName]
return Reflect.deleteProperty(propName)
},
})
對比reactive和ref
- 從數(shù)據(jù)角度對比
- ref 用來定義基本類型數(shù)據(jù)
- reactive 用來定義對象類型數(shù)據(jù)
- 備注:ref也可以用來定義對象類型數(shù)據(jù)吹菱,它內(nèi)部會求助reactive轉(zhuǎn)換為代理對象
- 從原理角度對比
- ref 通過 Object.defineProperty() 的get巍虫、set來實(shí)現(xiàn)響應(yīng)式
- reactive內(nèi)部通過Proxy來實(shí)現(xiàn)響應(yīng)式,通過Reflect來對對象進(jìn)行操作
- 從使用角度對比
- ref 定義的數(shù)據(jù):操作時需要用.value鳍刷,讀取數(shù)據(jù)時候占遥,模板中直接讀取,不需要.value
- reactive 定義的數(shù)據(jù):操作于讀取數(shù)據(jù)都不需要.value
Vue3 setup 注意點(diǎn)
- setup執(zhí)行時機(jī)
- 在beforeCreate之前執(zhí)行一次输瓜,this時undifined
- set的參數(shù)
- props:值為對象瓦胎,包含:組件外部傳遞過來,且組件內(nèi)部申明接收
- context上下文:
- attrs: 值為對象尤揣,包含組件外部傳遞過來搔啊,但是沒有在props中申明接收的屬性,相當(dāng)于this.$attrs
- slots: 收到的插槽內(nèi)容北戏,相當(dāng)于 this.$slots
- emit: 分發(fā)自定義事件的函數(shù)负芋,相當(dāng)于 this.$emit
- setup接收參數(shù)
export default{
name: 'Demo',
props: ['msg', 'school'],
// 申明自定義事件,申明后父組件才可以綁定
emits: ['sayHello']
// props 接收父組件傳遞的參數(shù)嗜愈,
// context 上下文對象旧蛾,包含:attrs, emit, slots
setup(props, context){
}
}
Vue3 計(jì)算屬性
import {reactive, computed} from 'vue'
export default{
name: 'Demo',
// vue2 寫法
//computed: {
// fullName() {
// return `${this.person.firstName} - ${this.person.lastName}`;
// }
//},
setup(props, context){
let person = reactive({
firstName: '張',
lastName: '三'
})
//vue3 計(jì)算屬性
person.fullName = computed(() => {
return `${this.person.firstName} - ${this.person.lastName}`
})
//vue3 計(jì)算屬性完整寫法,考慮讀和取
person.fullName = computed({
get() {
return `${this.person.firstName} - ${this.person.lastName}`
},
set() {
const nameArr = value.split('-')[0]
person.firstName = nameArr[0]
person.lastName = nameArr[1]
}
})
return {
person
}
}
}
vue3 watch屬性
import {ref, watch, reactive} from 'vue'
export default{
name: 'Demo',
// vue2 寫法
//watch: {
// sum: {
// // 立即監(jiān)聽
// immediate: true,
// // 深度監(jiān)視
// deep: true,
// handler(newValue, oldValue) {
// }
// }
//},
setup() {
let sum = ref(0)
let msg = ref(msg)
let person = reactive({
name: '張三',
age: '18',
salary: {
j1: '30K'
}
})
// 1. 監(jiān)視ref定義的響應(yīng)式數(shù)據(jù)
watch(sum, (newValue, oldValue) => {
console.log('sum變了', newValue, oldValue);
})
// 2. 監(jiān)視ref定義的多個響應(yīng)式數(shù)據(jù)
// newValue 和 oldValue都是數(shù)組
watch([sum, msg], (newValue, oldValue) => {
console.log('監(jiān)視數(shù)據(jù)變了', newValue, oldValue)
}, {immediate: true})
// 3. 監(jiān)聽reactive對象定義的響應(yīng)式數(shù)據(jù)芝硬,此處無法正確的獲得newvalue和oldvalue
// 強(qiáng)制開啟了深度監(jiān)視蚜点,deep配置無效
watch(person, (newValue, oldValue) => {
})
// 4. 監(jiān)聽reactive對象的某個屬性
// 必須通過函數(shù)返回一個屬性,直接寫person.age無效
watch(() => person.age, (newValue, oldValue) => {
console.log('age 變了', newValue, oldValue)
})
// 5. 監(jiān)聽reactive對象的多個屬性
// 必須通過函數(shù)返回一個屬性拌阴,直接寫person.age無效
watch([() => person.age, () => person.name], (newValue, oldValue) => {
console.log('屬性 變了', newValue, oldValue)
})
// 特殊情況:監(jiān)聽reactive對象中的某個屬性,這個屬性是一個對象奶镶,這時候deep屬性會生效
watch(() => person.salary, (newValue, oldValue) => {
console.log('屬性 變了', newValue, oldValue)
}, {deep: true})
return {
sum,
msg,
person
}
}
}
vue3 watchEffect
import {ref, watch, reactive, watchEffect} from 'vue'
export default{
name: 'Demo',
setup() {
let sum = ref(0)
let msg = ref(msg)
let person = reactive({
name: '張三',
age: '18',
salary: {
j1: '30K'
}
})
watch(sum, (newValue, oldValue) => {
console.log('sum變了', newValue, oldValue);
})
// watchEffect 不用指明監(jiān)視屬性
// 用誰就監(jiān)視誰迟赃,如果sum或者person.salary.j1的值變了,watchEffect就會被調(diào)用
// 與computed有點(diǎn)像厂镇,computed必須有返回值纤壁,watchEffect更注重邏輯
watchEffect(() => {
const x1 = sum.value;
const x2 = person.salary.j1
})
return {
sum,
msg,
person
}
}
}
vue3 生命周期、
- 有兩個鉤子函數(shù)改名了
- beforeDestory => beforeUnmount
- destoryed => Unmounted
- vue2 中的鉤子函數(shù)在vue3 中還可以以配置的方式使用捺信,也支持在setup中通過組合式函數(shù)使用鉤子函數(shù)
- 組合式鉤子函數(shù)名稱都是在原來函數(shù)名稱前面加個on
- beforeCreate 和 created沒有對應(yīng)的組合式鉤子函數(shù)
- vue3 認(rèn)為setup方法就相當(dāng)于 beforeCreate + created
- 如果同時寫了組合式API的鉤子函數(shù) 和 配置項(xiàng)鉤子函數(shù)酌媒,那么組合式API的先調(diào)用
- 比如:setup > beforeCreate > created > onBeforeMounte > beforeMounte > onMounted > mounted ...
vue3 自定義hook函數(shù)
- 有如下需求:鼠標(biāo)點(diǎn)擊時輸出鼠標(biāo)的坐標(biāo)
import {ref, reactive, onMounted} from 'vue'
export default {
name: 'Demo',
setup() {
let point = reactive({
x: 0,
y: 0
})
function savePoint(event) {
point.x = event.pageX
point.y = event.pageY
}
// 添加window窗體監(jiān)聽事件
onMounted(() => {
window.addEventListener('click', savePoint)
})
// 組件卸載前移除 window窗體監(jiān)聽事件
onBeforeUnmount(() => {
window.removeEventListener('click', savePoint)
})
return {point}
}
}
- 假如掛載函數(shù)、卸載函數(shù)代碼想要提取出來供大家調(diào)用迄靠,可以這么寫
- 在根目錄下創(chuàng)建/hooks/usePoint.js文件
- 把代碼放進(jìn)去
import {reactive, onMounte, onBeforeUnmounted}
export default function() {
let point = reactive({
x: 0,
y: 0
})
function savePoint(event) {
point.x = event.pageX
point.y = event.pageY
}
// 添加window窗體監(jiān)聽事件
onMounted(() => {
window.addEventListener('click', savePoint)
})
// 組件卸載前移除 window窗體監(jiān)聽事件
onBeforeUnmount(() => {
window.removeEventListener('click', savePoint)
})
return point
}
- 其他地方引入上面定義的文件
import usePoint from '../hooks/usePoint'
export default {
name: 'Demo',
setup() {
let point = usePoint()
return {point}
}
}
- 總結(jié):
- 什么是hook:本質(zhì)是一個函數(shù)对省,把setup函數(shù)中的CompositionAPI進(jìn)行了封裝
- 類似于vue2中的mixin
- 自定義hook的優(yōu)勢:復(fù)用代碼藕夫,讓setup可讀性更好
toRef 和 toRefs
- 作用:創(chuàng)建一個ref對象,其value指向另一個對象
- 語法:const name = toRef(person, 'name')
- 應(yīng)用:要將響應(yīng)式中的某個屬性單獨(dú)提供給外部使用
- 擴(kuò)展:toRefs 與 toRef功能一致,但是可以批量創(chuàng)建ref對象嗽冒,語法toRefs(person)
import {reactive, toRef, toRefs} from 'vue'
export default{
name: 'Demo',
setup() {
let person = reactive({
name: '',
age: 18,
job: {
j1: {
salary: 20
}
}
})
// toRef讓name2變量指向 person.name,name2修改會影響 person.name
const name2 = toRef(person, 'name')
const salary = toRef(person.job.j1, 'salary')
// 批量處理person的所有屬性柱宦,功能根ref一樣
const x = toRefs(person)
return {
name2,
...x
}
}
}
其他Composition API
- shallowReactive 淺層次響應(yīng)式
- 何時使用:當(dāng)對象只有最外層的屬性的時候使用
// shallowReactive 只考慮第一層屬性的響應(yīng)式,下面的salary 不支持響應(yīng)式
let person = shallowReactive({
name: '',
age: 18,
job: {
j1: {
salary: 20
}
}
})
- shallowRef 傳入基本類型的話根ref沒有區(qū)別抽米,但是shallowRef 不會處理對象類型的響應(yīng)式
- 何時使用:如果后續(xù)功能不會修改對象的屬性,而是用新生對象來代替糙置,就使用shallowRef
readonly 和 shallowReadonly
- 作用:讓對象只讀
- 語法:person = readonly(person)
- 場景:不希望數(shù)據(jù)被修改時
return default{
name: 'Demo',
setup(){
let person = reactive({
name: '',
age: 18,
job: {
j1: {
salary: 20
}
}
})
// 限制一個對象的所有層次的屬性只讀
person = readonly(person)
// 只對第一層對象限制只讀
person2 = shallowReadonly(person)
}
}
toRaw & markRaw
- toRaw
- 作用:把一個reactive生成的響應(yīng)式對象變成普通對象
- 場景:用于讀取響應(yīng)式對象的普通對象云茸,對這個普通對象的所有操作都不會引起頁面更新
- 語法:
- markRaw
- 作用:用于標(biāo)記一個對象, 使其用于不會再成為響應(yīng)式對象
- 場景:
- 有些值不應(yīng)該被設(shè)置為響應(yīng)式的谤饭,例如復(fù)雜的第三方類庫
- 當(dāng)渲染具有不可變數(shù)據(jù)的大列表時标捺,跳過響應(yīng)式轉(zhuǎn)換,可以提高性能
export default {
name: 'Demo',
setup() {
let person = reactive({
name: '張三'
})
// 把響應(yīng)式對象轉(zhuǎn)換成普通對象
const p = toRaw(person)
function addCar(){
let car = {name: 'benchi', price: 40}
// 如果直接寫 person.car = car网持,那么car就是一個響應(yīng)式對象
// 用markRaw把car變成普通對象
person.car = markRaw(car)
}
}
}
customRef
- 作用: 創(chuàng)建一個自定義Ref, 并對其依賴項(xiàng)和更新觸發(fā)進(jìn)行顯示控制
- 案例:實(shí)現(xiàn)防抖效果
// ref 相當(dāng)于精裝房宜岛,customRef相當(dāng)于毛坯房,需要自己實(shí)現(xiàn)一些東西
import {ref, customRef} from 'vue'
export default {
name: 'App',
setup() {
function myRef(value, delay) {
let timer = null
return customRef((track, trigger) => {
return {
get() {
// 通知vue追蹤value的變化
// 也就是提前和getter商量一下功舀,讓他認(rèn)為這個value是有用的萍倡,當(dāng)值變化時讀取新值
track()
return value
},
set(nweValue) {
clearTimeout(timer)
value = newValue // 修改value
timer = setTimeout(() => {
trigger() // 通知Vue重寫解析模板,模板會調(diào)用get方法讀取新值
}, delay)
}
}
})
}
let keyWord = myRef('hello', 500)
return {keyWord}
}
}
provide 與 inject
- 作用:適用于祖孫組件之間通信
- 套路:父組件有一個provide選項(xiàng)來提供數(shù)據(jù)辟汰,子組件
- 用法:
// 祖先組件
import {provide} from 'vue'
export default {
name: 'app',
setup() {
let car = reactive({name: 'BYD', price: 40})
// 通過provide把數(shù)據(jù)提供給后代組件
provide('car', car)
}
}
// 后代組件
import {inject} from 'vue'
export default {
name: 'Child',
setup() {
// 后代組件通過inject獲取父組件提供的對象
let x = inject('car')
}
}
響應(yīng)式數(shù)據(jù)判斷方法
- isRef:檢查一個值是否為一個ref對象
- isReactive:檢查一個對象是否是由reactive創(chuàng)建的響應(yīng)式對象
- isReadonly:檢查一個對象是否是由readonly創(chuàng)建的只讀對象
- isProxy:檢查一個對象是否是由reactive 或者 readonly方法創(chuàng)建
Vue2 中的API為配置式(Options API)列敲,Vue3中的API為組合式(Composition API)
- Options API:
- 新增或者修改需求,需要分別在data帖汞,methods戴而,computed中修改功能對應(yīng)的代碼
- 同一個功能的代碼時分別放到data,methods翩蘸,computed等模塊中所意,代碼結(jié)構(gòu)不合理
- composition API:
- 同一個功能相關(guān)的data,methods催首,computed方法都寫在一起扶踊,代碼結(jié)構(gòu)更合理
- hook函數(shù)功能強(qiáng)大,提高代碼復(fù)用
Fragment
- Vue2 中必須要有根標(biāo)簽
- Vue3 中可以沒有根標(biāo)簽郎任,內(nèi)部多個標(biāo)簽會包裹在一個Fragment虛擬元素中
- 好處:減少元素層級