Vue 筆記(未整理)

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 語句

  1. 表達(dá)式:一個表達(dá)式會產(chǎn)生一個值,可以放在任何一個需要值的地方
    (1)變量a
    (2)運(yùn)算表達(dá)式盒延,a + b
    (3)方法表達(dá)式缩擂,demo(1)
    (4)三元表達(dá)式
    ...
  2. 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方法,可以綁定vue對象到元素上计露,寫法如下 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

==============================================================

  1. Vue中的數(shù)據(jù)代理
    通過vm對象來代理data對象中的屬性操作
  2. Vue中的數(shù)據(jù)代理好處
    更加方便的操作data中的數(shù)據(jù)
  3. 基本原理
    通過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)勢

  1. 計(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 簡寫 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管理的

  1. computed能完成的watch 一定能完成憋肖,

  2. watch 能完成的computed不一定能完成,例如:watch 可以進(jìn)行異步操作

  3. 所有被Vue管理的函數(shù)最好寫成普通函數(shù)婚苹,這樣的話this的指向才是vm

  4. 所有不被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

  5. 綁定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ī)則如下:

  1. 如果舊的虛擬dom中找到了與新的虛擬dom相同的key
    如果虛擬dom內(nèi)容沒有變,則直接使用之前的真實(shí)dom
    如果虛擬dom內(nèi)容變了龟虎,則生成新的真實(shí)dom璃谨,隨后替換掉頁面上之前的真實(shí)dom
  2. 如果舊的dom中沒有找到與新的虛擬dom相同的key
    根據(jù)新的虛擬dom直接創(chuàng)建真實(shí)dom
  3. 如果舊的虛擬dom中的key在新的虛擬dom中不存在
    刪除舊的虛擬dom對應(yīng)的真實(shí)dom

用index下標(biāo)作為key可能引發(fā)的問題:

  1. 如果對數(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ù)改變原理分析

  1. 我們傳入的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.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 生命周期

  1. 生命周期回調(diào)函數(shù)是Vue在關(guān)鍵的時刻幫我們調(diào)用的一些特殊名稱的函數(shù)
  2. 生命周期函數(shù)的名字不可更改暮的,但是函數(shù)內(nèi)容可以根據(jù)具體需求編寫
  3. 生命周期函數(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)

使用組件攏共分三步:

  1. 創(chuàng)建組件
  2. 注冊組件
  3. 調(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

  1. school 組件本質(zhì)是一個VueComponent的構(gòu)造函數(shù),不是程序員定義的棘捣,是由Vue.extend 生成的
  2. 我們只要寫<school></school>辜腺,Vue解析時就會幫我們創(chuàng)建school組件的實(shí)例對象
    即Vue幫我們執(zhí)行new VueComponent(options)
  3. 特別注意,每次調(diào)用Vue.extend, 返回的都是一個全新的VueComponent
  4. 關(guān)于this指向
    組件配置中乍恐,data,methods,watch,computed中的函數(shù)评疗,它們的this均指向VueComponent示例對象
    new Vue 配置中的data,methods,watch,computed中的函數(shù),它們的this均指向Vue實(shí)例對象
  5. 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)
}).amount('#app'); 另一種寫法 new Vue({ el: '#app' render: h => h(App) }); el切換路由其實(shí)就是調(diào)用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.title); // 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來觸發(fā)myevent事件 <button @click="btnClick">點(diǎn)擊按鈕觸發(fā)myevent事件</button> methods: { btnClick(){ this.emit('myevent', this.name);
}
}


父組件還可以通過ref屬性給子組件綁定事件
這種事件靈活性強(qiáng)摧玫,可以異步實(shí)現(xiàn)
<Student ref="student"></Student>

mounted: {
setTimeout(() => {
this.refs.student.on('myEvent', this.testMethod)
// 只觸發(fā)一次
// this.refs.student.once('myEvent', this.testMethod)
}, 3000)
},
methods: {
testMethod(name) {
console.log(name);
}
}
================================================
解綁自定義事件:
Student 組件定義方法
methods: {
unbindEvent(){
this.off("myEvent"); // 解綁一個 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)用 on,emit, off,once方法
所以奠涌,可以使用Vue原型對象來作為載體
實(shí)現(xiàn)方式如下:

  1. 在Main.js 安裝全局事件總線

new Vue({
el: "#app",
render: h => h(App),
// 在Vue啟動之之前在Vue的原型對象上 添加事件總線
// bus 就是當(dāng)前應(yīng)用的vm beforeCreate() { Vue.prototype.bus = this
}
})

組件School里面注冊一個事件
mounted: {
this.bus.on('hello', (data) => {
console.log('我是school組件邪乍,收到了數(shù)據(jù)', data);
});
},
// 組件銷毀之前降狠,關(guān)閉hello事件監(jiān)聽
beforeDestory() {
this.bus.off('hello');
}

組件Student里觸發(fā)事件,發(fā)送數(shù)據(jù)
methods: {
sendStudentName() {
this.bus.emit('hello', this.name);
}
}

====================================================

消息訂閱與發(fā)布:

  1. 訂閱消息:消息名
  2. 發(fā)布消息:消息內(nèi)容

訂報紙:

  1. 訂閱報紙:住址
  2. 郵遞員送報紙:報紙

安裝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ù)乏沸,正確的做法是用 nextTicknextTick 在下一次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.refs.inputTitle.focus(); // 不生效 // 等下一輪界面渲染完畢痘昌,再去執(zhí)行nextTick里面的回調(diào) // 所以,回調(diào)實(shí)在方法中執(zhí)行完成炬转,去渲染頁面辆苔,頁面顯示了 input之后,就會調(diào)用focus() 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 封裝的過渡與動畫

  1. 作用:在插入荐吵,更新骑冗,或移除DOM元素時,在合適的時候給元素添加各種樣式
  2. 進(jìn)入:v-enter-active先煎,退出:v-leave-active
  3. 寫法:
    • 準(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ù)器

配置方式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)部定義一個插槽,挖個坑艺演,等待使用者填充

  1. 默認(rèn)插槽
    <div>
    <h1>標(biāo)題</h1>
    <slot>不給插槽傳遞東西却紧,這句話就會顯示</slot>
    </div>
    調(diào)用者給插槽傳入一個圖片,圖片就會展示在組件插槽的位置上
    <category>
    <img src="XXX" alt="">
    </category>

  2. 具名插槽
    有多個插槽胎撤,需要指定插槽名稱
    <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>

  1. 作用域插槽
    在組件定義一個插槽,不一樣的地方在于插槽有個綁定參數(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)管理,也是一種組件間的通信方式

  1. 什么時候使用vuex豪娜?
    1. 當(dāng)多個組件依賴同一個數(shù)據(jù)時候
    2. 來自不同組件的行為需要變更同一數(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使用流程

  1. 數(shù)據(jù)保存在state下
const state = {
    sum: 0
}
  1. 在組件中通過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);
    }
}
  1. 在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;
    }
}
  1. mutations修改完數(shù)據(jù)挎狸,模板會重新渲染
  2. 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.sum锨匆,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)用都將變得更有條理

  1. 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
    }
})
  1. 組件使用時:
    <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 路由

  1. 安裝
    vue3 對應(yīng) vue-router@4毅桃, vue2對應(yīng) vue-router@3

npm i vue-router@3

  1. 創(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
        }
    ]
})
  1. 引入
    在main.js
import router from './router'

Vue.use(VueRouter)

new Vue({
    el: '#app',
    render: h => h(App),
    router: router
})
  1. 使用
  • 在頁面中通過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>

============================================

  1. 組件一般分為路由組件和普通組件褒纲,路由組件一般放在pages文件夾下,普通組件一般放在components文件夾下
  2. 通過切換钥飞,隱藏了的路由組件默認(rèn)是被銷毀了莺掠,需要的是再重新掛載
  3. 每個組件都有自己的$route屬性,里面存儲著自己的路由信息
  4. 整個應(yīng)用只有一個router代承,可以通過組建的$router屬性獲得

=========================================================

子路由

  1. 子路由配置在父路由節(jié)點(diǎn)下的children下
  2. 子路由path前面不要加/
  3. 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
                }
            ]
        }
    ]
})

=======================================================

路由傳參

  1. 在路由組件里過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
  1. 對象傳參
<router-link
    :to={
        path:'/home/message/detail',
        query: {
            id: m.id,
            title: m.title
        }
    }>
{{m.title}}
</router-link>

==========================================================================
命名路由

  1. 命名路由的作用就是簡化路由跳轉(zhuǎn)
  2. 使用
// 給路由命名
{
    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 傳參

  1. 在路由里配置參數(shù)
{
    path: 'message',
    component: Message,
    children: [
        {
            name: 'detail',
            path: 'detail/:id/:title',
            component: Detail
        }
    ]
}
  1. 跳轉(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>
  1. 使用時獲取參數(shù)
this.$route.params.id
this.$route.params.title

=================================================================

路由的props

  1. 第一種寫法,傳固定值(此寫法用處極少针肥,幾乎沒用饼记。。慰枕。)

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']
  1. 第二種寫法,路由配置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']
  1. 第三種寫法蜂厅,路由配置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. 模式1:push模式(默認(rèn))

每次新的瀏覽記錄會被追加到歷史記錄里面

  1. 模式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);
    }
}

===============================================================
緩存路由組件

  1. 路由組件切換默認(rèn)是先銷毀贡歧,再重新掛載新的路由組件
  2. 想要前一個組件不被銷毀滩租,而是隱藏起來,那就要設(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ú)有的生命周期鉤子

  1. activated: 路由組件激活會被調(diào)用
  2. deactivated: 路由組件失活會被調(diào)用

對于路由組件來說绍弟,如果被指定緩存了(keep-alive)技即,那么組件的mounted 和 beforeDestory兩個鉤子組件就失效不會被調(diào)用了,這時候可以用activated和deactivated兩個鉤子函數(shù)替代

===========================================================

路由守衛(wèi)

  1. 配置全局前置路由守衛(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
  1. 配置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();
    }
})
  1. 后置路由守衛(wèi): 初始化時和每次路由切換之后被調(diào)用
router.afterEach((to, from) => {
    // to: 要去的路由
    // from: 來的路由
    // 后置路由守衛(wèi)沒有next考抄,因?yàn)槁酚梢呀?jīng)切換完成,沒有接下來的操作了
    // 比如路由切換完成后去修改頁面title信息 (title 配置在路由元信息中)
    document.title = to.meta.title;
})
  1. 獨(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)只有前置蔗彤,沒有后置
    }
}
  1. 組件內(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工作模式

  1. hash 哈希模式路勁里帶有#, 對于瀏覽器來說往后端發(fā)送路勁只會發(fā)送#之前的扑毡,比如:
    瀏覽器發(fā)送路由:http://www.test.com/userinfo/test1/#/sdfs/sdfsd/sdf
    后端收到的只是#前面的部分:http://www.test.com/userinfo/test1/,#后面的部分不會發(fā)送給后端
  2. history模式
    vue路由模式默認(rèn)是hash模式盛险,history模式需要手動開啟
const router = new VueRouter({
    mode: history,
    routes: [
        {
            path: '/about', 
            component: About
        },
        // ...
    ]
})
  1. 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組件庫

  1. 移動端
    • Vant
    • Cube UI
    • Mint UI
  2. PC 端
    • element UI
    • Iview UI
      組件庫可以按需引入,也可以完整引入运杭,具體怎么引入夫啊,自己研究官方文檔!A俱尽撇眯!

===============================================================================

Vue3

  1. Vue3 帶來了什么
    • 性能提升
      • 打包速度谆趾,內(nèi)存減少,初次渲染速度
    • 源碼升級
      • 使用Proxy代替defineProperty實(shí)現(xiàn)響應(yīng)式
      • 重寫虛擬dom的實(shí)現(xiàn)和Tree-Shanking
    • 擁抱TypeScript
    • 新特性
  2. 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')

  1. vue 3 的模板結(jié)構(gòu)可以沒有根標(biāo)簽
  2. 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ù)

  1. 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
}
  1. ref 函數(shù)處理基本數(shù)據(jù)類型休蟹,使用的是Object.defineProperty實(shí)現(xiàn)數(shù)據(jù)劫持沸枯,最后實(shí)現(xiàn)相響應(yīng)式
  2. ref 函數(shù)處理對象或數(shù)組數(shù)據(jù),使用的是Proxy來實(shí)現(xiàn)響應(yīng)式赂弓,Proxy 操作封裝在了一個名字叫reactive的函數(shù)中绑榴;所以處理對象或者數(shù)組建議直接使用reactive函數(shù)
  3. 用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
    }
}
  1. 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é)

  1. 實(shí)現(xiàn)原理

    • 對象類型:通過Object.defineProperty()對屬性的讀寫進(jìn)行劫持
    • 數(shù)組類型:通過對數(shù)組的一系列方法進(jìn)行攔截(對數(shù)組的操作方法進(jìn)行了包裹)
  2. 存在的問題

    • 新增刪除屬性界面不會更新辅搬,需要用Vue.set方法綁定新屬性唯沮,Vue.delete刪除一個存在的屬性
    • 直接通過下標(biāo)修改數(shù)組,界面不會自動更新
      =================================================================================
      vue 3 的響應(yīng)式
  3. 可以直接新增堪遂、刪除對象屬性介蛉,可以直接修改數(shù)組元素,不需要使用vue 的set溶褪,delete 方法

  4. 代碼實(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]
    },
})
  1. 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
    }
})
  1. 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

  1. 從數(shù)據(jù)角度對比
    • ref 用來定義基本類型數(shù)據(jù)
    • reactive 用來定義對象類型數(shù)據(jù)
    • 備注:ref也可以用來定義對象類型數(shù)據(jù)吹菱,它內(nèi)部會求助reactive轉(zhuǎn)換為代理對象
  2. 從原理角度對比
    • ref 通過 Object.defineProperty() 的get巍虫、set來實(shí)現(xiàn)響應(yīng)式
    • reactive內(nèi)部通過Proxy來實(shí)現(xiàn)響應(yīng)式,通過Reflect來對對象進(jìn)行操作
  3. 從使用角度對比
    • ref 定義的數(shù)據(jù):操作時需要用.value鳍刷,讀取數(shù)據(jù)時候占遥,模板中直接讀取,不需要.value
    • reactive 定義的數(shù)據(jù):操作于讀取數(shù)據(jù)都不需要.value

Vue3 setup 注意點(diǎn)

  1. setup執(zhí)行時機(jī)
    • 在beforeCreate之前執(zhí)行一次输瓜,this時undifined
  2. 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
  3. 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 生命周期、

  1. 有兩個鉤子函數(shù)改名了
    • beforeDestory => beforeUnmount
    • destoryed => Unmounted
  2. 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ù)

  1. 有如下需求:鼠標(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}
    }
}
  1. 假如掛載函數(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}
    }
}
  1. 總結(jié):
    • 什么是hook:本質(zhì)是一個函數(shù)对省,把setup函數(shù)中的CompositionAPI進(jìn)行了封裝
    • 類似于vue2中的mixin
    • 自定義hook的優(yōu)勢:復(fù)用代碼藕夫,讓setup可讀性更好

toRef 和 toRefs

  1. 作用:創(chuàng)建一個ref對象,其value指向另一個對象
  2. 語法:const name = toRef(person, 'name')
  3. 應(yīng)用:要將響應(yīng)式中的某個屬性單獨(dú)提供給外部使用
  4. 擴(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

  1. shallowReactive 淺層次響應(yīng)式
    • 何時使用:當(dāng)對象只有最外層的屬性的時候使用
// shallowReactive 只考慮第一層屬性的響應(yīng)式,下面的salary 不支持響應(yīng)式
let person = shallowReactive({
    name: '',
    age: 18,
    job: {
        j1: {
            salary: 20
        }
    }
})
  1. shallowRef 傳入基本類型的話根ref沒有區(qū)別抽米,但是shallowRef 不會處理對象類型的響應(yīng)式
    • 何時使用:如果后續(xù)功能不會修改對象的屬性,而是用新生對象來代替糙置,就使用shallowRef

readonly 和 shallowReadonly

  1. 作用:讓對象只讀
  2. 語法:person = readonly(person)
  3. 場景:不希望數(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

  1. toRaw
    • 作用:把一個reactive生成的響應(yīng)式對象變成普通對象
    • 場景:用于讀取響應(yīng)式對象的普通對象云茸,對這個普通對象的所有操作都不會引起頁面更新
    • 語法:
  2. 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

  1. 作用: 創(chuàng)建一個自定義Ref, 并對其依賴項(xiàng)和更新觸發(fā)進(jìn)行顯示控制
  2. 案例:實(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

  1. 作用:適用于祖孫組件之間通信
  2. 套路:父組件有一個provide選項(xiàng)來提供數(shù)據(jù)辟汰,子組件
  3. 用法:
// 祖先組件
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虛擬元素中
  • 好處:減少元素層級
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末秧耗,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子舶治,更是在濱河造成了極大的恐慌分井,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件霉猛,死亡現(xiàn)場離奇詭異尺锚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)韩脏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門缩麸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事杭朱≡淖校” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵弧械,是天一觀的道長八酒。 經(jīng)常有香客問我,道長刃唐,這世上最難降的妖魔是什么羞迷? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮画饥,結(jié)果婚禮上衔瓮,老公的妹妹穿的比我還像新娘。我一直安慰自己抖甘,他們只是感情好热鞍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著衔彻,像睡著了一般薇宠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上艰额,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天澄港,我揣著相機(jī)與錄音,去河邊找鬼柄沮。 笑死回梧,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的祖搓。 我是一名探鬼主播漂辐,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼棕硫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起袒啼,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤哈扮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蚓再,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滑肉,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年摘仅,在試婚紗的時候發(fā)現(xiàn)自己被綠了靶庙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡娃属,死狀恐怖六荒,靈堂內(nèi)的尸體忽然破棺而出护姆,到底是詐尸還是另有隱情,我是刑警寧澤掏击,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布卵皂,位于F島的核電站,受9級特大地震影響砚亭,放射性物質(zhì)發(fā)生泄漏灯变。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一捅膘、第九天 我趴在偏房一處隱蔽的房頂上張望添祸。 院中可真熱鬧,春花似錦寻仗、人聲如沸刃泌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蔬咬。三九已至,卻和暖如春沐寺,著一層夾襖步出監(jiān)牢的瞬間林艘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工混坞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狐援,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓究孕,卻偏偏與公主長得像啥酱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子厨诸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容