Vue
(一)Vue簡介
- Vue是一個JavaScript框架,有其獨特的使用規則,按照其規則使用,可以事半功倍
- 它可以大大簡化Dom操作
- 具有回應式資料驅動
- 官方檔案 官方檔案
一.Vue特點
- 采用組件化模式,提高代碼復用率、且讓代碼更好維護(組件.vue==HTML+CSS+JS)
- 宣告式編碼,讓編碼人員無需直接操作DOM,提高開發效率
- 使用虛擬DOM+優秀的Diff演算法,盡量復用DOM結點
(二)Vue核心基礎
一、Vue基礎
1.匯入(安裝)
<!-- 開發環境版本,包含了有幫助的命令列警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- 生產環境版本,優化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
2.el:掛載點
<div id="app">
{{ message }}
</div>
//第一種寫法
var app = new Vue({
el: '#app', //el:掛載點
data: { // data:資料物件
message: 'Hello Vue!'
}
})
//第二種寫法
var app = new Vue({
data: { // data:資料物件
message: 'Hello Vue!'
}
})
app.$mount('#app'); //掛載
掛載點 :選擇HTML元素,Vue會管理該元素及其后代元素(不要掛載和標簽)
注意:掛載只能一一對應,不能一對多,也不能多對一
3.data:資料物件
可以嵌套物件,陣列等復雜資料,語法與JS語法一致
//data物件寫法
data:{
massage:'hello!'
}
//data函式寫法
//注意!組件中必須用這種寫法
data(){
return{
massage:'hello!'
}
}
//注意!凡是由Vue管理的函式只能定義為普通函式,不能是箭頭函式!!!
4.MVVM模型
- M:模型(Model):對應data中的資料
- V:視圖(View):DOM
- VM:視圖模型(ViewModel):Vue實體物件
- data中所有的屬性最終都會出現在VM上(資料代理)
5.Object.defineProperty()回顧
let test={
name:'test'
}
let a=18;
//為物件添加屬性,第三個引數為配置項物件
Object.defineProperty(test,'age',{
value:18,
enumerable:true, //控制屬性是否可以列舉,默認為false
writable:true, //控制屬性是否可以被修改,默認false
configurable:true, //控制屬性是否可以被洗掉,默認false
//當有人讀取test的age屬性時,get函式(getter)會被呼叫,而且回傳值就是age的值
get(){
console.log('有人讀取了age屬性');
return a;
},
//當有人修改test的age屬性時,set函式(setter)會被呼叫,而且會收到修改的具體值
set(value){
console.log('有人修改了age屬性,且值是'+value);
a=value;
}
})
6.資料代理(重點難點)
資料代理:通過一個物件代理另一個物件中屬性的操作(讀/寫)
- 資料代理的好處:更加方便的操作data中的資料
- 基表原理:
- 通過Object.defineProperty()將data物件中的所有屬性添加到VM上
- 為每一個添加到VM上的屬性定義一個getter和setter
- 在getter和setter內部去操作(讀/寫)data中對應屬性
二、Vue指令
1.v-text
v-text : 設定標簽的文本值(textContent)
<div id="app">
<h1 v-text="massage+'!!!!'"></h1>
<h1 v-text="massage+inf"></h1>
<h1> {{massage}}啊!{{inf}}</h1>
</div>
var app=new Vue({
el:'#app',
data:{
massage:'你好',
inf:'世界'
}
})
2.v-html
v-html : 設定標簽的innerHTML
<div id="app">
<h1 v-text="link"></h1>
<h1 v-html="link"></h1>
</div>
var app=new Vue({
el:'#app',
data:{
link:'<a href="https://www.baidu.com">百度</a>'
}
})
注意:v-html存在安全性問題!!!
- 在網站上動態渲染任何HTML是非常危險的,容易導致XSS攻擊
- 一定要在可信的內容上使用v-html,一定不能用在用戶提交的內容上!
3.v-on
3.1 v-on : 為元素系結事件
//語法
<div id="app">
<input type="button" value="點我一下" v-on:事件="事件回應函式名">
<input type="button" value="點我一下" @事件="事件回應函式名"> <!--更簡單的寫法-->
</div>
//實體
<div id="app">
<input type="button" value="點我一下" @click="fun">
</div>
var app=new Vue({
el:'#app',
methods:{ //事件回應函式寫這里
fun:function(){
alert('你好啊!');
}
}
})
3.2 v-on補充:傳遞自定義引數,事件修飾符
<div id="app">
<input type="button" value="點我一下" @click="fun(name)">
<!-- $event占位,用于傳遞事件物件 -->
<input type="text" @keyup.enter="fun($event,'李四')"> <!-- Vue提供了鍵盤按鍵的便捷系結方式 @keyup.按鍵名="事件回應函式" -->
</div>
var app=new Vue({
el:'#app',
data:{
name:'張三'
},
methods:{
fun:function(event,name){
console.log(event);
alert('你好啊!'+name);
}
}
})
注 :更多詳細用法查看檔案!官方檔案
3.3 Vue中的事件修飾符
- prevent:阻止默認事件(常用)
- stop:阻止事件冒泡(常用)
- once:事件只觸發一次(常用)
- capture:使用事件的捕獲模式
- self:只有event.target是當前操作元素時才觸發事件
- passive:事件的默認行為立即執行,無需等待事件回呼執行完畢
.test5,.test2{
width: 500px;
height: 100px;
padding: 20px;
background-color: skyblue;
}
.test41{
width: 500px;
height: 100px;
background-color: springgreen;
padding: 20px;
}
.test42{
width: 400px;
height: 50px;
background-color: skyblue;
}
.test6{
width: 300px;
height: 100px;
overflow: auto;
}
.test61{
width: 200px;
height: 1000px;
background-color: skyblue;
}
<div id="root">
<!-- prevent:阻止默認事件(常用) -->
<a href="https://www.baidu.com" @click.prevent='test1'>百度一下</a>
<!-- stop:阻止事件冒泡(常用) -->
<div class="test2" @click='test2'>
<button @click.stop='test2'>test2</button>
</div>
<!-- once:事件只觸發一次(常用) -->
<button @click.once='test3'>test3</button>
<!-- capture:使用事件的捕獲模式 -->
<div class="test41" @click.capture="test4('test41')">
test41
<div class="test42" @click="test4('test42')">
test42
</div>
</div>
<!-- self:只有event.target是當前操作元素時才觸發事件 -->
<div class="test5" @click.self='test5'>
<button @click='test5'>test5</button>
</div>
<!-- passive:事件的默認行為立即執行,無需等待事件回呼執行完畢 -->
<div class="test6" @wheel.passive="test6">
test6
<div class="test61"></div>
</div>
</div>
var vm=new Vue({
el:'#root',
data:{
},
methods:{
test1(){
alert('就不百度一下!');
},
test2(){
alert('不會冒泡喲!');
},
test3(){
alert('再點一次我就不會彈出來了!');
},
test4(i){
alert(i+'捕獲到了點擊事件!');
},
test5(e){
alert('你點的是'+e.target);
},
test6(){
for(var i=0;i<10000000;i++){
console.log('test6');
}
}
}
})
注:修飾符可以多個連著使用
3.4 鍵盤事件
-
Vue常用事件別名
- enter:回車
- delete:洗掉/退格
- esc:退出
- space:空格
- tab:換行(特殊,必須配合keydown使用,因為tab默認事件是切換焦點)
- up:上
- down:下
- left:左
- right:右
-
Vue未提供別名的按鍵,可以使用按鍵原始的Key值去系結,但是注意如果Key值為兩個以上單詞,轉化為-連接命名(例如:CapsLock:caps-lock)
//獲取鍵值,鍵碼 //為某一元素系結按鍵事件(keydown,keyup都行) console.log(event.key,event.keyCode); -
系統修飾鍵(用法特殊):ctrl,alt,shift,meta(win鍵)
- 配合keyup使用:按下修飾鍵同時,再按下其他鍵,隨后釋放其他鍵,事件才觸發,
- 配合keydown使用:正常觸發事件
-
可以使用kkeyCode去指定具體按鍵,但是不推薦!!!
- 不同鍵盤同一鍵鍵碼可能不同
- 瀏覽器逐漸不支持
-
Vue.config.keyCodes.自定義鍵名=鍵碼,可以自定義鍵名,不推薦!!!
注:鍵指定可以連著寫實作Ctrl+y等事件系結
4.v-show
v-show : 根據運算式真偽,切換元素的顯示和隱藏(操作樣式:display)
<div id="app">
<img src="" v-show="true"> <!-- 直接布林值 -->
<img src="" v-show="isShow"> <!-- 變數 -->
<img src="" v-show="age>=18"> <!-- 運算式 -->
</div>
var app=new Vue({
el:'#app',
data:{
isShow:true,
age:20
}
})
5.v-if/v-else-if/v-else
v-if : 根據運算式真偽,切換元素顯示和隱藏(操作Dom元素,直接將Dom元素移除或添加)
- 用起來和普通編程語言條件分支陳述句一樣
<div id="app">
<img src="" v-if="true"> <!-- 直接布林值 -->
<img src="" v-if="isShow"> <!-- 變數 -->
<img src="" v-if="age>=18"> <!-- 運算式 -->
<div v-if="n===1">a</div>
<div v-else-if="n===2">b</div>
<div v-else-if="n===3">c</div>
<div v-else>d</div>
<!-- 需求:條件相同,顯示不同內容 -->
<div v-if="n===1">a</div>
<div v-if="n===1">b</div>
<div v-if="n===1">c</div>
<!-- 解決方案一 會破環原HTML結構,進而影響樣式等生效,不推薦使用-->
<div v-if="n===1">
<div>a</div>
<div>b</div>
<div>c</div>
</div>
<!-- 解決方案二 -->
<template v-if="n===1">
<div>a</div>
<div>b</div>
<div>c</div>
</template>
<!-- template只能與v-if一起使用,不能和v-show一起使用 -->
</div>
var app=new Vue({
el:'#app',
data:{
isShow:true,
age:20,
n:2
}
})
6.v-bind
v-bind : 設定元素屬性(如:src,title,class等)
<div id="app">
<img v-bind:src="imgsrc"> <!-- v-bind:屬性名="屬性值" -->
<img :alt="ingalt+'!!!'"> <!-- 簡寫為 :屬性名="屬性值" -->
<img v-bind:class="isActive?'active':''">
<img v-bind:class="{active:isActive}">
</div>
var app=new Vue({
el:'#app',
data:{
imgsrc:'./01.jpg',
imgalt:'圖片1',
isActive:false
}
})
6.1 通過class系結樣式
<div id="root">
<!-- 字串寫法,適用于:樣式的類名不確定,需要動態指定(只有一個樣式(類)) -->
<div :class="test1" @click="change1">test1</div>
<!-- 陣列寫法,適用于:樣式的個數不確定,名字不確定 -->
<div class="basic" :class="test2" @click="change2">test2</div>
<!-- 物件寫法,適用于:樣式個數確定,名字確定,但是用不用不確定 -->
<div class="basic" :class="test3" @click="change3">test3</div>
</div>
.class1{
width: 200px;
height: 200px;
background-color: skyblue;
}
.class2{
width: 200px;
height: 400px;
background-color: skyblue;
}
.class3{
width: 400px;
height: 200px;
background-color: skyblue;
}
.class4{
width: 200px;
height: 200px;
background-color: slateblue;
}
.basic{
width: 100px;
height: 100px;
background-color: skyblue;
}
.plus1{
border: black solid 3px;
}
.plus2{
border-radius: 10px;
}
.plus3{
color: blue;
}
var vm=new Vue({
el:'#root',
data:{
test1:'class1',
test2:[],
test3:{
plus1:false,
plus2:false,
plus3:false,
}
},
methods:{
change1(){
var classArr=['class1','class2','class3','class4'];
var index=Math.floor(Math.random()*4);
console.log(index);
this.test1=classArr[index];
},
change2(){
var classArr=['plus1','plus2','plus3'];
if(this.test2.length<3){
this.test2.push(classArr[this.test2.length]);
}else{
for(var i=0;i<3;++i){
this.test2.pop();
}
}
},
change3(){
var a,b,c;
a=Math.floor(Math.random()*2);
b=Math.floor(Math.random()*2);
c=Math.floor(Math.random()*2);
if(a)this.test3.plus1=!this.test3.plus1;
if(b)this.test3.plus2=!this.test3.plus2;
if(c)this.test3.plus3=!this.test3.plus3;
}
}
})
6.2 通過style系結
<div id="root">
<!-- 物件寫法 -->
<div class="basic" :style="styleObj">test1</div>
<!-- 陣列方法 -->
<div class="basic" :style="styleArr">test2</div>
</div>
.basic{
width: 100px;
height: 100px;
background-color: skyblue;
border-radius: ;
}
var vm=new Vue({
el:'#root',
data:{
styleObj:{
fontSize:'30px',
color:'red',
},
styleArr:[
{
fontSize:'30px',
color:'red'
},
{marginTop:'10px'},
{
border:'black solid 2px',
borderRadius:'10%'
}
]
},
})
7.v-for
7.1 v-for : 根據資料生成串列結構
- 可以遍歷:陣列、物件、字串(不常用)、指定次數(不常用)
- 用in或者of都行
<div id="root">
<!-- 遍歷陣列 -->
<h1>學生串列</h1>
<ul>
<li v-for="(item,index) in students" :key="index">
{{item.id}}:{{item.name}}-{{item.age}}
</li>
</ul>
<!-- 遍歷物件 -->
<h1>學生資訊</h1>
<ul>
<li v-for="(value,key) of student" :key="key">
{{key}}:{{value}}
</li>
</ul>
<!-- 遍歷字串 -->
<h1>hello</h1>
<ul>
<li v-for="(char,index) of str" :key="index">
{{index}}:{{char}}
</li>
</ul>
<!-- 遍歷指定次數 -->
<h1>12345</h1>
<ul>
<li v-for="(v,index) in 5" :key="index">
{{index}}:{{v}}
</li>
</ul>
</div>
var vm=new Vue({
el:'#root',
data:{
students:[
{id:0,name:'張三',age:18},
{id:1,name:'李四',age:20},
{id:2,name:'王五',age:18},
{id:3,name:'趙六',age:19},
],
student:{
id:0,
name:'張三',
age:18
},
str:'hello'
},
})
7.2 v-for中的key屬性(重點難點)
面試題:react、vue中key有什么作用?(key的原理是什么?)
要點:就是為了提高效率
- 虛擬DOM中key的作用:key是虛擬DOM物件的標識,當資料發生變化時,Vue會根據新資料生成新的虛擬DOM,隨后Vue進行新的虛擬DOM與舊的虛擬DOM的差異比較(diff演算法)
- 比對規則:
- 舊的虛擬DOM中找到了與新的虛擬DOM相同的key:
- 若虛擬DOM中內容沒變,直接使用之前的真實DOM!(提高效率)
- 如果虛擬DOM中內容變了,則生成新的真實DOM,隨后替換掉頁面中之前的真實DOM,
- 舊虛擬DOM中未找到與新的虛擬DOM相同的key:
- 創建新的真實DOM,隨后渲染到頁面中
- 舊的虛擬DOM中找到了與新的虛擬DOM相同的key:
- v-for用索引值(index)作為key可能會引發的問題(不寫key,Vue就會默認這樣使用):
- 如果對資料進行逆序添加、逆序洗掉等破環順序的操作:
- 會產生沒有必要的真實DOM更新,效率低
- 如果結構中還有輸入類DOM:
- 會產生錯誤DOM更新,效率低,界面還會出錯(亂序)
- 如果對資料進行逆序添加、逆序洗掉等破環順序的操作:
- 開發中如何選擇key
- 最好使用每條資料的唯一標識作為key
- 如果不存在對資料進行逆序添加、逆序洗掉等破環順序的操作,僅僅用于渲染展示,使用索引(index)也是沒有問題的
8.v-model
8.1 v-model : 獲取和設定表單元素的值(雙向資料系結)
<div id="app">
<input type="text" v-model:value="massage"> <!-- 簡寫如下 -->
<input type="text" v-model="massage"> <!-- 文本框內容與massage雙向系結(輸入值改變,massage值會相應改變) -->
<h1>{{massage}}</h1>
</div>
var app=new Vue({
el:'#app',
data:{
massage:'你好!'
}
})
8.2 v-mdel補充
-
如果是文本框,則v-model系結的是value值,用戶輸入的就是value
-
如果是單選框,責v-model系結的是value值,必須要給標簽配置value
-
如果是多選框,則:
- 沒有配置value,那么系結的就是checked(true or false)
- 配置了value
- 如果v-modle的初始值是非陣列,那么系結的就是checked(true or false)
- 如果v-modle的初始值是陣列,那么系結的就是value組成的陣列
-
v-model修飾符
- number:輸入字串轉化為有效數字
- lazy:失去焦點才收集更新資料
- trim:去掉字串首尾的空格
-
實體
<div id="root"> <form @submit.prevent="output"> <!-- v-model.trim 會自動去掉字串前后空格 --> 用戶名: <input type="text" v-model.trim="userinf.username"> <br><br> 密碼: <input type="password" v-model="userinf.password" autocomplete="on"> <br><br> <!-- type="number" 可以限制用戶輸入只能是數字 --> <!-- v-model.number 可以自動將輸入的內容轉化為數值型別 --> 年齡: <input type="number" v-model.number="userinf.age"> <br><br> 性別: 男<input type="radio" name="gender" value="男" v-model="userinf.gender"> 女<input type="radio" name="gender" value="女" v-model="userinf.gender"> <br><br> 班級: <select v-model="userinf.class"> <option value="">請選擇班級</option> <option value="一班">一班</option> <option value="二班">二班</option> <option value="三班">三班</option> </select> <br><br> 愛好: 學習<input type="checkbox" value="學習" v-model="userinf.hobby"> 打游戲<input type="checkbox" value="打游戲" v-model="userinf.hobby"> 開車<input type="checkbox" value="開車" v-model="userinf.hobby"> <br><br> <!-- v-model.lazy 失去焦點才會收集更新資訊 --> 其他資訊:<textarea v-model.lazy="userinf.other"></textarea> <br><br> <input type="checkbox" v-model="userinf.agree">是否同意<a href="#">用戶協議</a> <br><br> <button>提交</button> </form> </div>var vm = new Vue({ el: '#root', data: { userinf:{ username:'', password:'', age:'', class:'', hobby:[], other:'', agree:'' } }, methods: { output(){ console.log(JSON.stringify(this.userinf)); } }, })
9.v-cloak
- 沒有值
- 是一個特殊的屬性,Vue實體創建完畢并接管容器后,會自動洗掉v-colak屬性
- 使用css配合v-cloak可以解決網速慢時頁面展示出未被Vue決議的模板頁面的問題
<!DOCTYPE html>
<html lang="ch">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
[v-clock]{
display: none;
}
</style>
</head>
<body>
<!-- 整個頁面核心結構 -->
<div id="root">
<h1 v-cloak>{{name}}</h1>
</div>
</body>
<!-- 如果網路卡頓,Vue.js無法在打開頁面時立即加載 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
var vm = new Vue({
el: '#root',
data: {
name:'Vue'
},
})
</script>
</html>
10.v-once
- v-once所在結點在初次動態渲染后,就會被視為靜態內容
- 以后資料改變不會引起v-once所在結點的更新,可以優化性能
<div id="root">
<h1 v-once>初始n: {{n}}</h1>
<h1 >n: {{n}}</h1>
<button @click="n++">n+1</button>
</div>
var vm = new Vue({
el: '#root',
data: {
n:1
},
})
11.v-pre
- Vue會跳過其所在結點的決議程序
- 可以利用它跳過:沒有用指令,插值等結點,提高性能
<div id="root">
<h1>歡迎訪問該系統!!!!!</h1>
<h1 >n: {{n}}</h1>
<button @click="n++">n+1</button>
</div>
var vm = new Vue({
el: '#root',
data: {
n:1
},
})
三、計算屬性(重點難點)
計算屬性:根據原有的屬性經過加工處理計算出來的屬性叫做計算屬性(computed)
- 原理:底層借助了Object.defineproperty方法提供的getter和setter
- get什么時候執行
- 初次讀取計算屬性
- 當計算屬性依賴的資料發生改變時會被再次呼叫
- 優勢:與methods實作相比,內部有快取機制(可復用),效率更高
- 注意:
- 計算屬性最侄訓被處理為vm的一個屬性,直接讀取使用即可
- 計算屬性如果需要被修改,則必須使用setter,setter中對應修改計算屬性的依賴資料即可
<div id="root">
性:<input type="text" v-model="firstname"> <br><br>
名:<input type="text" v-model="lastname"> <br><br>
性名: <span>{{name}}</span>
</div>
var vm=new Vue({
el:'#root',
data:{
firstname:'張',
lastname:'三'
},
computed:{
//這里得寫成一個物件,但是Vue會把它決議處理為vm中的屬性
name:{
//getter,setter中的this都已經被Vue預先處理為了vm,方便使用
get(){
return this.firstname+'-'+this.lastname;
},
set(value){
var values=value.split('-');
this.firstname=values[0];
this.lastname=values[1];
}
}
//當只需要讀取,不需要修改時,可以簡寫為
// name(){
// return this.firstname+'-'+this.lastname;
// }
}
})
四、偵聽(監視)屬性
1.偵聽(監視)屬性
- 當偵聽(監視)屬性變化時,handler自動呼叫,進行相關操作
- 偵聽(監視)屬性必須存在,才能被偵聽(監視)
<div id="root">
<h1>你真是個大{{description}}</h1>
<button @click='change'>點我</button>
</div>
var vm=new Vue({
el:'#root',
data:{
isCool:true
},
methods:{
change(){
this.isCool=!this.isCool;
}
},
computed:{
description(){
return this.isCool? '帥哥':'聰明';
}
},
watch:{
isCool:{
immediate:true, //初始化時讓handler呼叫一次
// 當被偵聽(監視)的值變化時,handler被呼叫
handler(newvalue,oldvalue){
console.log('isCool被改變了,從'+oldvalue+'改成了'+newvalue);
}
},
//當偵聽(監視)時配置項只有handler時,可以簡寫為
// isColl(){
// console.log('isCool被改變了,從'+oldvalue+'改成了'+newvalue);
// }
}
})
// 另一種寫法
// vm.$watch('isCool',{
// immediate:true, //初始化時讓handler呼叫一次
// // 當被偵聽(監視)的值變化時,handler被呼叫
// handler(newvalue,oldvalue){
// console.log('isCool被改變了,從'+oldvalue+'改成了'+newvalue);
// }
// })
//當偵聽(監視)時配置項只有handler時,可以簡寫為
// vm.$watch('isCool',function(){
// console.log('isCool被改變了,從'+oldvalue+'改成了'+newvalue);
// })
2.深度偵聽(監視)
- Vue中的watch默認不偵聽(監視)物件內部值的改變(一層)
- 配置項
deep:true可以偵聽(監視)物件內部屬性值的改變(多層) - Vue自身是可以偵聽(監視)物件內部值改變,但是Vue提供的watch默認不可以
- 使用watch時根據實際使用需求,決定是否采用深度偵聽(監視)
3.計算屬性(computed)和偵聽(監視)屬性(watch)的區別
- computed能完成的功能,watch都可以完成,
- watch能完成的功能,computed不一定能成功,例如:watch可以進行異步操作
- 兩個都行的用computed更簡單
- 注意!!
- 所被Vue管理的函式,最好寫成普通函式,這樣this的指向才是vm或組件實體物件
- 所有不被Vue管理的函式(定時器回呼函式,ajax回呼函式,Promise的回呼函式等),最好寫成箭頭函式,這樣this的指向才是vm或組件實體物件
4.偵聽(監視)原理(重點難點)
偵聽(監視):包括Vue對于資料改變,動態更新頁面中用到該資料的DOM,以及watch中偵聽(監視)資料地秤實作(同樣的邏輯)
-
Vue會偵聽(監視)data中所有層級的資料
-
如何偵聽(監視)物件中的資料?
-
通過setter實作偵聽(監視),而且要是在創建Vue實體物件時就傳入的資料才會被Vue偵聽(監視)
-
物件中后追加的屬性,Vue默認不偵聽(監視),進而也不會做出回應式處理
-
如果想讓后追加的屬性也可以被Vue偵聽(監視),并回應式處理,需要使用:
Vue.set() vm.$set()
-
-
如何偵聽(監視)陣列中的資料?
-
通過包裹陣列更新元素方法實作,本質上就是兩個處理
- 通過原生對應的方法對陣列進行更新
- 重新決議模板,渲染頁面
-
在Vue中修改某陣列中某元素一定要用如下幾種方法:
//這些方法用起來和原生JS中Array物件的這些方法一樣,但是實際上底層邏輯不一樣,Vue對這些方法做了如上述的包裹處理 push() pop() shift() unshift() splice() sort() reverse() //Vue的set方法 //注意!! 這兩個方法用起來一樣,但是都有局限, //不能為vm或者vm的根資料物件(即 data等)添加屬性!!! Vue.set() vm.$set()
-
-
實體
<div id="root"> <button @click="student.age++">年齡加一歲</button> <br> <button @click.once="addgender">添加性別屬性,默認男</button> <br> <button @click.once="addaf">在朋友們最前面加一個朋友</button> <br> <button @click.once="updateff">修改第一個朋友的名字</button> <br> <button @click.once="addh">添加一個愛好</button> <br> <button @click.once="updatafh">修改第一個愛好</button> <br> <h1>姓名 {{student.name}}</h1> <h2>綽號 {{student.nickname}}</h2> <h3>年齡 {{student.age}}</h3> <h3 v-if="student.gender">性別 {{student.gender}}</h3> <h3>朋友們</h3> <ul> <li v-for="(f,index) in student.friends" :key="index"> {{f.name}}--{{f.age}} </li> </ul> <h3>愛好</h3> <ul> <li v-for="(h,index) in student.hobby" :key="index"> {{h}} </li> </ul> </div>var vm = new Vue({ el: '#root', data: { student: { name: '張三', nickname: '法外狂徒', age: 20, friends: [ { name: '羅老師', age: 18 }, { name: '李四', age: 22 }, ], hobby: ['抽煙', '賭博', '干壞事'] } }, methods: { addgender() { // 第一種 // Vue.set(this.student,'gender','男'); // 第二種 this.$set(this.student, 'gender', '男'); }, addaf() { this.student.friends.unshift({ name: '王五', age: 40 }); }, updateff() { this.student.friends[0].name = '王大炮' }, addh() { this.student.hobby.push('開車'); }, updatafh() { // 第一種 // this.student.hobby.splice(0,1,'學習'); // 第二種 // Vue.set(this.student.hobby, 0, '學習'); // 第三種 this.$set(this.student.hobby, 0, '學習'); } } })
五、過濾器
過濾器:對要顯示的資料進行特定格式化后再顯示(適用于一些簡單邏輯處理)
- 語法:
- 注冊過濾器:Vue.filter(name,callback)(全域過濾器) ,new Vue({filters:{}})(區域過濾器)
- 使用過濾器:{{xxx | 過濾器名}} 或 v-bind:屬性 = “xxx | 過濾器名”
- 備注:
- 過濾器相對于一個函式,但是使用時不需要呼叫(即使用())默認會接收需要被過濾的資料作為第一個引數,也可以接收額外的引數
- 多個過濾器可以串聯
- 過濾器不會改變原始資料,而是對原始資料進行操作,如何產生新的資料
- 一個中文前端插件庫:BootCDN
- 日期時間處理庫(功能強大):moment.js
- 輕量日期時間處理庫:day.js
六、自定義Vue指令
-
定義語法:
-
區域指令
new Vue({ directives:{ 指令名:配置物件 //配置物件 bind() //指令與元素成功系結的時候呼叫 inserted() //指令所在元素被插入頁面時呼叫 update() //指令所在模板被重新決議時呼叫 } //物件形式 //簡寫 directives:{ 指令名(){} //寫成一個函式 //這個函式相對于定義了上述配置項的bind()和update() } }) -
全域指令
Vue.directive(指令名,配置物件) Vue.directive(指令名,配置函式)
-
-
備注
- 指令定義時不加v-,但是使用的時候需要加v-
- 指令名如果是多個單詞,使用kebab-case命名方式,不用駝峰命名方式,
七、生命周期(重點難點)
- 生命周期,又叫做生命周期回呼函式、生命周期函式、生命周期鉤子
- 生命周期是Vue在關鍵時刻幫我們呼叫了一些特殊名稱的函式
- 生命周期函式名稱不可更改,但是函式邏輯是由程式員根據需求撰寫的
- 生命周期函式中的this指向的是vm或組件實體物件

常用的生命周期鉤子:
- mounted:發送ajax請求、啟動定時器、系結自定義事件、訂閱訊息等(初始化操作)
- beforeDestroy:清除定時器、解除自定義事件系結、取消訂閱訊息等(收尾作業)
銷毀Vue實體:
- 銷毀后借助Vue開發者工具將看不見任何資訊
- 銷毀后自定義事件會失效,但是Vue的作業成果(最后一次更新后的真實DOM)會保留,原生DOM的事件依然有效
- 一般不會在beforeDestroy鉤子中操作Vue實體相關資料,因為即使操了,Vue也不會再進行更新等流程
(三)Vue組件化編程
一、簡介
-
模塊:向外提供特定功能的JS檔案
- JS檔案在實際開發中很多很復雜
- 可以復用JS,簡化JS的撰寫,提高效率
- 模塊化:當應用的JS都以模塊形式來撰寫,那么這個應用就是一個模塊化的應用
-
組件:用來實作區域(特定)功能效果的代碼及依賴資料集合(html/css/js/img/mp3…)
- 實際開發中一個界面的功能很復雜
- 復用編碼,簡化專案編碼,便于理清專案結構,提高運行效率
- 組件化:當應用都是以組件的形式來搭建撰寫的,那么這個應用就是一個組件化的應用
- 非單檔案組件:一個檔案中包含了一個到多個組件
- 單檔案組件:一個檔案中只包含一個檔案
二、基礎
1.非單檔案組件
1.1 基礎使用
-
定義組件
- 使用Vue.extend(options)創建,其中options和創建Vue實體時傳入的配置項基基本一樣,有如下區別
- el配置項不能使用,因為最終組件都會被一個Vue實體管理,由Vue(大哥)決定他及他的組件(小弟)為哪個容器服務
- data必須用函式形式,因為組件為了被復用,用函式才能避免資料存在參考關系
- 注:組件中用template配置組件結構
- 使用Vue.extend(options)創建,其中options和創建Vue實體時傳入的配置項基基本一樣,有如下區別
-
注冊組件
- 區域注冊:創建Vue實體時的components配置項
- 全域注冊:Vue.component(‘組件名’,組件);
- 組件名不要與HTML標簽名沖突
-
使用組件
- 組件標簽(類似于HTML標簽)
-
實體
<div id="root"> <h1>root</h1> <heade></heade> <hr> <bodyer></bodyer> <foote></foote> </div> <div id="root1"> <h1>root1</h1> <foote></foote> </div>// 定義組件header var heade=Vue.extend({ template:` <div> <h1>歡迎訪問{{name}}系統!</h1> </div> `, data(){ return { name:'智慧張三' } } }) // 定義組件bodyer var bodyer=Vue.extend({ template:` <div> <h1>{{name}}串列</h1> <ul> <li v-for="(s,index) in students"> {{s.name}}---{{s.age}} </li> </ul> </div> `, data(){ return { name:'學生', students:[ {name:'張三',age:18}, {name:'羅老師',age:30}, ] } } }) var foote=Vue.extend({ template:` <div> <h2>版本號{{num}}!</h2> </div> `, data(){ return { num:'v1.0.1' } } }) // 注冊組件(全域) Vue.component('foote',foote) var vm = new Vue({ el: '#root', data: { }, // 注冊組件(區域) components:{ heade, bodyer } }) new Vue({ el: '#root1', })
1.2 組件命名等注意事項
-
組件命名
- 一個單詞
- 首字母小寫
- 首字母大寫
- 多個單詞
- kebab-case命名法
- 單詞首字母全大寫(需要Vue腳手架支持)
- 注意:
- 組件命名需要回避HTML標簽
- 可以使用name屬性指定組件在Vue開發者工具中的展現名字(組件定義時)
- 一個單詞
-
組件標簽
- 第一種寫法:雙標簽,如:
<bodyer></bodyer> - 第二種寫法:自閉合標簽,如:
<bodyer/>(需要Vue腳手架支持,否則其后的組件不能被渲染)
- 第一種寫法:雙標簽,如:
-
定義簡寫
var heade=Vue.extend(options) //簡寫 var heade=options;
1.3 關于VueComponent函式
- 組件本質上是一個名為VueComponent的建構式,而且不需要由我們定義,是Vue.extend生成的
- 我們在使用組件時,Vue決議到會幫我們創建對應組件的實體物件,即執行
new VueComponent(options); - 每次呼叫Vue.extend,回傳的都是一個全新的VueComponent!!!
- 關于this指向
- 組件配置中:data函式、methods中的函式、watch中的函式、computed中的函式,它們的this都指向VueComponent實體物件
- new Vue()配置中:data函式、methods中的函式、watch中的函式、computed中的函式,它們的this都指向Vue實體物件
1.4 一個重要的內置關系
VueComponent.prototype.__proto__ === Vue.prototype- 目的:讓組件實體物件也可以訪問到Vue原型上的屬性、方法
2.單檔案組件
(四)腳手架(CLI)的使用
一、初始化腳手架
- 腳手架是Vue官方提供的標準化開發工具(開發平臺)
- 官方檔案
- 安裝步驟
npm install -g @vue/cli全域安裝@vue/cli (只需要安裝一次)- 在你要創建專案的目錄下,使用命令創建專案
vue create xxxx - 啟動專案
npm run serve - 備注:
-
如果出現下載緩慢,請配置npm淘寶鏡像:
npm config set registry https://registry.npm.taobao.org --globalnpm config set disturl https: //npm.taobao.org/dist --global -
Vue腳手架隱藏了所有的webpack相關的配置,如想要查看具體的wekpack配置,請執行:
vue inspect > output.js
-
二、關于不同版本Vue
- vue.js與vue.runtime.xxx.js的區別:
- vue.js是完整版的vue,包含了核心功能+模板決議器
- vue.runtime.xxx.js是運行版的vue,只包含了核心功能,沒有模板決議器,
- 因為vue.runtime.xxx.js沒有模板決議器,所以不能使用template配置項,需要使用render函式將收到的createELement函式去指定具體內容,
三、vue.config.js組態檔
- 使用output.js可以查看到腳手架默認配置,
- 使用vue.config.js可以對腳手架進行個性化定制,詳見:腳手架配置
//單檔案組件示例
<template>
<div>
<School></School>
<Student></Student>
</div>
</template>
<script>
import School from './components/Test1.vue';
import Student from './components/Test2.vue';
export default {
name:'App',
components:{
School,
Student
}
}
</script>
<style>
</style>
四、ref屬性
-
用來給元素或者子組件注冊參考資訊(相當于id)
-
應用在html標簽上獲取真實的DOM元素,應用在組件標簽上是組件的實體物件(vc)
-
使用方式
<template> <div> <h1 ref="h1">你好</h1> <School ref="sch"></School> <button ref="but" @click="console">輸出refs</button> <Student></Student> </div> </template> <script> import School from './components/Test1.vue'; import Student from './components/Test2.vue'; export default { name:'App', components:{ School, Student }, methods: { console(){ console.log(this.$refs); } }, } </script>
五、配置項props
功能:讓組件接收外部傳過來的資料
備注:props是只讀的,Vue底層會監視你對props的修改,如果進行了修改,就會發出警告
export default {
name:'Student',
data(){
return {
mas:'你好,我是:'
}
},
// 第一種方式:(只是簡單接收)
// props:['name','gender','age'],
// 第二種方式:(限制資料型別)
// props:{
// name:String,
// gender:String,
// age:Number
// }
// 第三種方式:(詳細配置)
props:{
name:{
type:String, //型別
required:true //必要性
},
gender:{
type:String,
default:'男' //默認值
},
age:{
type:Number,
required:true
}
}
}
六、混合(混入)
功能:把某些組件共用的配置提取出來,成為一個混合物件
使用方式
//定義并暴露混合
export const mixin={
data(){
return {
message:'你好啊!!'
}
},
methods: {
nihao(){
alert("Hello!");
}
},
mounted(){
console.log("掛載了組件");
},
}
//使用混合
//區域使用
import {mixin} from './mixin';
mixins:[mixin]
//全域使用(在main.js中引入注冊混合)
import {mixin} from './mixin';
Vue.mixin(mixin);
七、插件
功能:增強Vue
本質:包含install方法的一個物件,install的第一個引數是Vue,第二個以后的引數是插件使用所需要的資料
//定義插件
export default {
install(Vue){
Vue.prototype.test=()=>{
alert('你好!');
}
}
}
//引入及使用(在main.js中)
import plugin from './plugins';
Vue.use(plugin);
八、scoped樣式
作用:讓樣式只在區域(當前組件生效)其他地方不生效,這樣來避免組件間樣式沖突
<style scopes></style>
九、web Storage
- 存盤內容大小一般支持5MB左右(不同的瀏覽器可能不一樣)
- 瀏覽端通過Window.sessionStorage和Window.localStorage屬性來實作本地存盤機制,
- 相關API:
setItem('key','value');該方法接收一個鍵和值作為引數,會把鍵值對添加到存盤中,如果鍵名存在,則直接更新對應的值getItem('key');該方法根據鍵獲取存盤中的值,如果沒有,回傳nullremoveItem('key');該方法根據鍵洗掉存盤中的相應的鍵值對clear();清空存盤的資訊
- SessionStorage(會話存盤)存盤的內容會隨著瀏覽器關閉而消失
- LocalStorage(本地存盤)存盤的內容,需要手動清除才會消失
十、組件自定義事件
-
一種組件間通信方式,子組件===>父組件
-
系結與觸發事件
//父組件中 <!-- 為子組件系結自定義事件,實作子給父傳遞資料(v-on/@)--> <School @test="getName"></School> <!-- 普通事件系結的修飾符仍然可用,如once等 --> <!-- 為子組件系結自定義事件,實作子給父傳遞資料(ref 更加靈活) --> <School ref="school"></School>//父組件中 methods: { getName(name){ console.log(name+'拿到了'); } }, mounted(){ setTimeout(()=>{ this.$refs.school.$on('test',this.getName); //使用$once方法,事件只觸發一次 //注意:通過該方法系結事件時,回呼建議配置在methods中或者使用箭頭函式,否則回呼中(this.getName中)的this指向的是觸發事件的子組件 },3000) } //子組件中觸發事件 this.$emit('test',this.name); -
解除事件系結
//在子組件中 this.$off('test'); //解除事件test系結 this.$off(['test1','test2']); //解除事件test1,test2的系結 this.$off(); //解除所有事件系結 -
組件系結原生DOM事件,需要使用native修飾符
十一、全域事件總線(GlobalEventBus)(重點)
-
一種組件間通信的方式,任意組件間通信(基于組件自定義事件)
-
安裝全域事件總線:(實際上就是造一個中轉的組件實體物件,該VC可以被所有其他組件訪問到)
//在main.js的root組件中 beforeCreate(){ Vue.prototype.$bus=this //$bus就是當前應用的vm,也就作為上述要造的vc } -
使用事件總線
-
接收資料的組件:給$bus系結自定義事件,事件的回呼在自身
methods:{ test(data){} //回呼函式 } mounted(){ this.$bus.$on('事件名',this.test); //系結事件 } -
發送資料的組件
this.$bus.$emit('事件名',data); //觸發事件
-
-
最好在beforeDestroy鉤子中,用$off去解綁當前組件所用到的事件,
十二、訊息訂閱與發布(pubsub)
-
一種組件間的通信方式,適用于任意組件間通信,
-
使用
-
安裝第三方庫:(這里用的是pubsub-js,也可以用其他的)
npm i pubsub-js -
引入:
import pubsub from 'pubsub-js' -
接收資料的組件中訂閱訊息,在自身撰寫回呼函式
methods(){ test(data){} } mounted(){ this.pubid=pubsub.subscribe('訊息名',this.test); //訂閱訊息 } -
提供資料的組件發送訊息
pubsub.publish('訊息名',data); -
最好在beforeDestroy鉤子中,用pubsub.unsubscribe(this.pubid)去取消訊息訂閱
-
十三、$nextTick
this.$nextTick(回呼函式)- 作用:在下一次DOM更新結束后執行其回呼函式,
- 什么時候用,當改變資料后,要基于更行后的DOM進行某些操作的時候,要在nextTick所指定的回呼中執行,
十四、Vue封裝的過度與影片
-
在插入、更新或者移除DOM元素時,會在合適的時候給元素添加樣式類名
-
v-enter == v-enter-active ==> v-enter-to v-leave == v-leave-active ==> v-leave-to
-
使用方法
-
準備好樣式:
- v-enter:進入的起點
- v-enter-active:進入的程序中
- v-enter-to:進入的終點
- v-leave:離開的起點
- v-leave-active:離開的程序中
- v-leave-to:離開的終點
-
使用
<transition>包裹要過度的元素,并配置name屬性:<transition name="test"> <!-- 指定了name屬性,需要將類名對應為相應的(例如test-enter) --> <h1 v-show="isShow">你好啊!</h1> </transition> -
備注:如果有多個元素需要影片效果,則需要使用:
transition-group,且每個元素都要指定key值
實體一:
<button @click="change">顯示/隱藏</button> <transition name="test" appear> <div v-show="isShow" class="a"> <h1>{{mes}}</h1> </div> </transition><button @click="change">顯示/隱藏</button> .test-enter-active{ animation: test 1s; } .test-leave-active{ animation: test 1s reverse; } @keyframes test{ from{ transform: translateX(-100%); } to{ transform: translateX(0); } }實體二:
<transition name="test1" appear> <div v-show="isShow" class="a"> <h1>{{mes}}</h1> </div> </transition><button @click="change">顯示/隱藏</button> .test1-enter, .test1-leave-to{ transform: translateX(-100%); } .test1-leave, .test1-enter-to{ transform: translateX(0); } .test1-leave-active, .test1-enter-active{ transition: 1s linear; }實體三:
<button @click="change">顯示/隱藏</button> <transition-group name="test2" appear> <div v-show="isShow" class="a" key="1"> <h1>{{mes}}</h1> </div> <div v-show="!isShow" class="a" key="2"> <h1>{{mes}}</h1> </div> </transition-group>.test2-enter, .test2-leave-to{ transform: translateX(-100%); } .test2-leave, .test2-enter-to{ transform: translateX(0); } .test2-leave-active, .test2-enter-active{ transition: 1s linear; } -
-
影片庫推薦:animate.css
- 安裝
npm i animate.css - 引入
import 'animate.css'; - 官方檔案 animate.css
<button @click="change">顯示/隱藏</button> <transition appear name="animate__animated animate__bounce" enter-active-class="animate__rubberBand" leave-active-class="animate__backOutDown" > <div v-show="isShow" class="a"> <h1>{{mes}}</h1> </div> </transition> - 安裝
```javascript
import 'animate.css';
//記得配置這個methods
change(){
this.isShow=!this.isShow;
}
十五、插槽
-
作用:讓父組件可以向子組件中指定位置插入HTML結構,也是一種組件間通信方式 父組件===>子組件
-
使用
-
默認插槽
//父組件 <template> <List title="汽車"> <img src="" alt=""> </List> <List title="食物"> <ul> <li v-for="(f,i) in foods" :key="i">{{f}}</li> </ul> </List> <List title="游戲"> <!-- <ul> <li v-for="(g,i) in games" :key="i">{{g}}</li> </ul> --> </List> </template> <script> import List from "./components/Test1.vue"; export default { name: "App", components: { List }, data() { return { foods: ["海底撈", "燒烤"], cars: ["勞斯萊斯", "蘭博基尼"], games: ["推箱子", "連連看"], }; }, }; </script> <style> .lists, .a { display: flex; justify-content: space-around; } img { width: 100%; } </style> //子組件 <template> <div class="list"> <h1>{{title}} 串列</h1> <slot>沒有內容了,,,</slot> </div> </template> -
具名插槽
//父組件 <template> <div class="lists"> <List title="汽車"> <a slot="test" href="#">更多</a> </List> <List title="食物"> <div slot="test" class="a"> <a href="#">中餐</a> <a href="#">西餐</a> </div> </List> <List title="游戲"> <!-- 在用template標簽時可以這樣用,也可以slot="test" --> <template v-slot:test> <div class="a"> <a href="#">單機</a> <a href="#">聯機</a> </div> <h2>快來游玩吧!</h2> </template> </List> </div> </template> //子組件 <template> <div class="list"> <h1>{{title}} 串列</h1> <slot name="test">沒有內容了111,,,</slot> </div> </template> -
作用域插槽
- 資料在子組件中,但是根據資料生成什么樣的結構由父組件決定
//父組件 <template> <div class="lists"> <List title="手機"> <template slot="test2" scope="{phones}"> <ul> <li v-for="(p,i) in phones" :key="i">{{p}}</li> </ul> </template> </List> <List title="手機"> <!-- vue2.6后新的寫法 --> <template v-slot:test2="{phones}"> <ol> <li v-for="(p,i) in phones" :key="i">{{p}}</li> </ol> </template> </List> </div> </template> //子組件 <template> <div class="list"> <h1>{{title}} 串列</h1> <slot name="test2" :phones="phones">沒有內容222</slot> </div> </template> <script> export default { name:'List', data(){ return { phones:['小米','華為'] } }, props:['title'] } </script> <style scoped> .list{ background-color: skyblue; width: 200px; height: 300px; } h1{ text-align: center; } li{ background-color: orange; font-size: 18px; margin-bottom: 8px; } </style>
-
(五)Vue使用Ajax
一、axios:功能強大的網路請求庫
安裝 :npm npm上axios地址,網上找都行,到處都是
1.axios基本使用
<div id="app">
<button @click="get">get</button>
<br>
<button @click="post">post</button>
</div>
var app=new Vue({
el:'#app',
methods:{
get:function(){
axios.get("http://127.0.0.1:3000/get?num=4") //自己寫的測驗介面,下同
.then(
function(res){
console.log(res);
},function(err){
console.log(err);
}
)
},
post:function(){
axios.post("http://127.0.0.1:3000/post",{num:5})
.then(
function(res){
console.log(res);
},function(err){
console.log(err);
}
)
}
}
})
二、Vue腳手架配置服務器代理
-
方法一
// 開啟代理服務器(方式一) devServer: { proxy: 'http://localhost:5000' },students(){ axios.get("http://localhost:8081/students").then( (res)=>{ console.log(res.data); }, (err)=>{ console.log(err.massage); } ) }- 優點:配置簡單,請求資源時直接發送給本機服務器(8081)即可,
- 缺點:不能配置多個代理,不能靈活選擇是否讓代理轉發請求
- 作業方式:當請求時,優先訪問本機服務器,如果本機服務器沒有資源,才會轉發請求到對應服務器
-
方法二
// 開啟代理服務器(方式二) devServer: { proxy: { '/s': { //當請求前綴為/s時代理服務器轉發請求到target target: 'http://localhost:5000', pathRewrite:{'^/s':''}, //代理服務器轉發請求時,將前綴/s去掉 ws: true, //用于支持websocket changeOrigin: true //用于控制請求頭中的Host值(true: localhost:5000 false :localhost:8081) }, '/c': { //當請求前綴為/s時代理服務器轉發請求到target target: 'http://localhost:5001', pathRewrite:{'^/c':''}, //代理服務器轉發請求時,將前綴/s去掉 ws: true, //用于支持websocket changeOrigin: true //用于控制請求頭中的Host值(true: localhost:5000 false :localhost:8081) } } }students(){ axios.get("http://localhost:8081/s/students").then( (res)=>{ console.log(res.data); }, (err)=>{ console.log(err.massage); } ) }, cars(){ axios.get("http://localhost:8081/c/cars").then( (res)=>{ console.log(res.data); }, (err)=>{ console.log(err.massage); } ) }- 優點:可以配置多個代理,且靈活控制
- 配置繁瑣,請求時需要加前綴
三、vue-resource插件(了解)
- 安裝
npm i vue-resource - 使用
- 引入
import vueResource from 'vue-resource' - 使用
Vue.use(vueResource); - 使用
this.$http.get(...)
- 引入
(六)Vuex(重點)
一、Vuex簡介
-
概念:專門在Vue中實作集中式狀態(資料)管理的一個Vue插件,對vue應用中多個組件的共享狀態進行集中式的管理(讀寫),也是組件間通信方式之一,適用于任意組件間通信
-
Github地址:vuex
-
原理:

二、Vuex搭建
-
安裝:
npm i vuex -
創建檔案:src/store/index.js
// 引入Vue import Vue from 'vue' // 引入Vuex import Vuex from 'vuex' // 應用Vuex插件 Vue.use(Vuex); // 創建并暴露store export default new Vuex.Store({ actions:{}, mutations:{}, state:{} }) -
在main.js中配置store
// 引入store import store from './store' import App from './App.vue' Vue.config.productionTip = false Vue.use(vuex); new Vue({ render: h => h(App), store }).$mount('#app')
三、基本使用
-
初始化資料state、配置actions,mutations
actions:{ //回應組件中的動作 fun(context,value){ //context:背景關系,相當于一個minstore,在里面有所有這里可能需要用到的資料和方法 //context.dispatch('action中的方法名',value) 可以對資料做多步中間處理 //context.state.test可以直接訪問到state中的資料,這里不要直接去修改資料,可以讀取資料做條件判斷等, //如果直接修改資料,vue開發者工具無法捕捉到 //context.commit('mutations中的方法名',data) 提交操作 } //業務邏輯全部寫在這里 }, mutations:{ //執行操作 //這里的方法名約定全大寫,方便與actions中的方法進行區分 FUN(state,value){ //state 就是state,這里就對資料進行基本操作(不關心業務邏輯,只關心基本增刪改查操作) } }, state:{ //資料 test:0 } -
組件中讀取資料,發起操作
//讀取資料 $store.state.test //發起操作 $store.dispatch('action中的方法名',data) //如果不需要action做中間處理,則直接提交操作 $store.commit('mutations中的方法名',data)
1. getters配置項
-
概念:當state中的資料需要經過加工后再共享使用的時候,可以用getters加工
-
用法:
getters:{ bigTest(state){ return state.test*10; } } //用法形如組件中的computed計算屬性,但是getters可以在組件間復用
2.四個map方法
引入import {mapState,mapGetters,mapActions,mapMutations} from 'vuex'
2.1 mapState
-
用于幫助我們映射state中的資料為computed計算屬性
computed:{ //物件寫法 ...mapState({a:'test1',b:'test2',c:'test3'}) //陣列寫法(不能重新命名,生成的計算屬性名與state物件中的屬性名一致,傳遞的引數也得與state物件中的屬性名一致) ...mapState(['test1','test2','test3']) }
2.2 mapGetters
-
用于幫助我們映射getters中的資料為computed計算屬性
computed:{ //物件寫法 ...mapGetters({a:'test1',b:'test2',c:'test3'}) //陣列寫法(不能重新命名,生成的計算屬性名與Getters物件中的屬性名一致,傳遞的引數也得與Getters物件中的屬性名一致) ...mapGetters(['test1','test2','test3']) }
2.3 mapActions
-
用于幫助我們生成與actions對話的方法
methods:{ //物件寫法 ...mapActions({a:'test1',b:'test2',c:'test3'}) //生成的函式 //a(value){ // $store.dispatch('test1',value); //} //陣列寫法(不能重新命名,生成的函式名與actions物件中的方法名一致,傳遞的引數也得與actions物件中的方法名一致) ...mapActions(['test1','test2','test3']) }
2.4 mapMutations
-
用于幫助我們生成與mutations對話的方法
methods:{ //物件寫法 ...mapMutations({a:'TEST1',b:'TEST2'}) //生成的函式 //a(value){ // $store.commit('TEST1',value); //} //陣列寫法(不能重新命名,生成的函式名與mutations物件中的方法名一致,傳遞的引數也得與mutations物件中的方法名一致) ...mapMutations(['TEST1','TEST2']) }
3.模塊化+命名空間
-
目的:讓代碼更好的維護,讓多種資料分類更加明確
-
使用方法
-
創建并暴露模塊
//在model1.js中 export default model1={ //開啟命名空間 namespaced:true, state:{}, actions:{}, mutations:{}, getters:{} }//在model2.js中 export default model2={ //開啟命名空間 namespaced:true, state:{}, actions:{}, mutations:{}, getters:{} } -
在store模塊中引入并使用其他模塊
//在index.js中 // 引入Vue import Vue from 'vue' // 引入Vuex import Vuex from 'vuex' //引入其他模塊 import model1 from './model1' import model2 from './model2' // 應用Vuex插件 Vue.use(Vuex); // 創建并暴露store export default new Vuex.Store({ modules:{ model1, model2 } }) -
讀取state中資料的方法(在讀取時要以某一種形式指明資料所在的模塊)
//直接讀取 this.$store.state.model1.test1 //借助mapState讀取 ...mapState('model1',['test1','test2']) -
讀取getters中資料的方法
//直接讀取 this.$store.getters['model2/test1'] //借助mapGetters ...mapGetters('model2',['test1','test2']) -
呼叫dispatch與Actions對話的方法
//直接對話 this.$store.dispatch('model1/test1',data) //借助mapActions ...mapActions('model1',['test1','test2']) -
呼叫commit與Mutations對話的方法
//直接對話 this.$store.commit('model2/test1',data) //借助mapMutations ...mapMutations('model2',['TEST1','TEST2'])
-
(七)路由 vue-router(重點)
一、vue-router簡介
vue-router是vue的一個插件庫,專門用來實作SPA(單頁面)應用
1.SPA簡介
- 單頁面Web應用(Single page web application,SPA)
- 整個應用只有一個完整的頁面
- 點擊頁面中的導航鏈接不會重繪頁面,只會做頁面的區域更新
- 資料通過Ajax請求獲取
2.路由
- 什么是路由
- 一個路由就是一組映射關系(key-value)
- key是路徑,value可能是function或component
- 路由分類
- 后端路由
- 理解:value是function,用于處理客戶端提交的請求
- 作業程序:服務器收到一個請求時,根據請求路徑找到匹配的函式來處理請求,回傳回應資料
- 前端路由
- 理解:value是component,用于展示頁面內容
- 作業程序:當瀏覽器的路徑改變的時候,對應的組件就會顯示
- 后端路由
二、基本使用
-
安裝vue-router
npm i vue-router -
引入
import VueRouter from 'vue-router' -
應用插件
Vue.use(VueRouter); -
創建并暴露一個router
//在src/router/index.js下 //引入vue-router import VueRouter from 'vue-router' //引入相關組件 import Component1 from '../component/Component1' import Component2 from '../component/Component2' //創建并暴露router export default router= new VueRouter({ routes:[ { path:'/component1', component:Component1 }, { path:'/component2', component:Component2 } ] }) -
實作切換
<router-link active-class="active" to="/component1">Component1</router-link> <!-- active-class指定被選中時的樣式,to指定跳轉路徑 --> <!-- router-link 最終呈現出來是一個a標簽 --> -
指定顯示位置
<router-view></router-view> -
注意!
- 在開發中,路由組件通常放在src/pages下,一般的組件通常放在src/components下
- 切換路徑進而切換顯示的組件時,默認是在銷毀、掛載組件
- 每個組件都有自己的$route屬性,里面存盤著自己的路由資訊
- 整個應用只有一個router,通過每個路由組件的$router屬性訪問
三、多級路由
-
配置路由規則,在一級路由里使用children進行配置
export default router= new VueRouter({ routes:[ { path:'/component1', component:Component1, //配置子級路由,可以無限套娃 children:[ { path:'child1', //children中的路徑不需要寫/ component:Child1 }, { path:'child2', component:Child2 } ] } ] }) -
跳轉(to中的路徑需要寫完整)
<router-link to="/component1/child1">Child1</router-link>
四、路由傳參
1. query引數
-
傳遞引數
<!-- 字串寫法 --> <router-link :to="`/component1/child1/test1?a=${data.a}&b=${data.b}`">Test1</router-link> <!-- 物件寫法 --> <router-link :to="{ path:'/component1/child1/test1', query:{ a:data.a, b:data.b } }" >Test1</router-link> -
接收引數
this.$route.query.a
2.路由命名(name配置項)
-
使用
export default router= new VueRouter({ routes:[ ... { name:'c1', //配置名字 path:'xxxx', component:xxxxx }, ] }) -
簡化跳轉
<!-- 主要是在子級路由跳轉時,即路徑較長,或者需要傳遞引數,使用物件寫法 --> <router-link :to="{ name:'c1', query:{ ... } }" >Test1</router-link>
3.params引數
-
在配置中宣告params引數
name:'c1' path:'xxxx/:a/:b' //使用占位符宣告params引數 -
傳遞引數
<!-- 字串寫法 --> <router-link :to="`/xxxx/${data.a}/${data.b}`">Test1</router-link> <!-- 物件寫法 --> <router-link :to="{ name:'c1', //這里必須用name,不能用path!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! query:{ a:data.a, b:data.b } }" >Test1</router-link> -
讀取引數
this.$route.params.a
4.路由的props配置
作用,讓路由組件更方便接收到引數
{
...
//第一種寫法:props值為物件,該物件中所有的key-value的組合最侄訓通過props傳給對應組件
//props:{a:100,b:'hello'}
//這種寫法不能動態賦值
//第二種寫法:props值為true,路由收到的所有**params**引數通過props傳給對應組件
//props:true
//這種方法有局限性,只能處理params引數
//第三種寫法:props值為函式,函式回傳一個物件,函式的第一個引數是$route,物件中所有的key-value的組合最侄訓通過props傳給對應組件
props($route){
return {
a:$router.query.xxx
b:$router.params.xxx
}
}
}
五、<router-link>的replace屬性
- 作用:控制路由跳轉時操作瀏覽器歷史記錄的模式
- 瀏覽器的歷史記錄有兩種寫入模式:分別是
push和replace,push是追加記錄(壓堆疊),replace是替換,默認是push - 開啟replace模式:
<router-link replace>.....</router-link>
六、編程式路由導航
-
作用:不需要再借助
<router-link>,路由跳轉更加靈活 -
具體方法
//$router的兩個API this.$router.push({ //push模式跳轉 name:'xxx', query:{ ... } }) //配置與to的物件寫法一樣 this.$router.replace({ //replace模式跳轉 name:'xxx', params:{ ... } }) //歷史記錄操作方法,$router的三個API this.$router.back() //后退一步 this.$router.forward() //前進一步 this.$router.go() //傳遞引數,正數前進,負數后退具體的步數
七、快取路由組件
-
作用:讓不展示的路由組件保持掛載,不被銷毀
-
使用
<keep-alive include="xxx"> <router-view></router-view> </keep-alive> <!-- keep-alive標簽中的內容將會被快取 include是一個配置項,里面值為**組件名**,其中包含的組件才會被快取,如果不配置include,則keep-alive中的組件都會被快取 include中需要包含多個組件時 :include="['xxx1','xxx2']" -->
八、activated與deactivated生命周期鉤子
- 作用:路由組件獨有,用于捕獲路由組件的激活狀態
activated路由組件被激活時觸發deactivated路由組件失活時觸發
九、路由守衛
1.全域守衛
//在暴露router之前,使用router相應API設定
//全域前置守衛:初始化時執行、每次路由切換前執行(控制訪問權限等)
router.beforeEach((to,from,next)=>{
//to是即將跳轉的路由的相關資訊
//from是跳轉前的路由相關資訊
//next是一個函式,呼叫該函式即跳轉路由
})
//全域后置守衛:初始化時執行、每次路由切換后執行(控制頁簽名稱document.title變化等)
router.afterEach((to,from)=>{
//to是即將跳轉的路由的相關資訊
//from是跳轉前的路由相關資訊
})
//補充:路由配置中提供程式員存放其他資訊(標識資訊等)的配置項meta
2.獨享守衛
- 某一個路由特有的
//配置在路由配置中
routes:[
...
{
...
beforeEnter(to,from,next){
...
//這里的三個引數與全域前置守衛一致
}
}
]
3.組件內守衛
-
組件獨享的守衛
//在組件配置中 //進入守衛:通過路由規則,進入該組件時被呼叫 beforeRouteEnter(to,from,next){} //離開守衛:通過路由規則,離開該組件時被呼叫 beforeRouteLeave(to,from,next){} //這里的三個引數與全域前置守衛一致
十、路由器作業模式
- 對與一個路徑來說,hash值就是#及其后面的內容
- hash值不會包含在HTTP請求中,即has值不會帶給服務器
- hash模式
- 優點
- 兼容性較好
- 缺點
- 如果以后將地址通過第三方手機app分享,如果app校驗嚴格,則地址會標記為不合法
- 地址中帶著/#/,不美觀
- 優點
- history模式
- 優點
- 地址干凈美觀
- 缺點
- 兼容性略差
- 應用部署上線時需要后端人員支持,解決重繪頁面服務器404問題(本質上是因為重繪時瀏覽器將路徑全部發送給了服務器,請求服務器導致)
- 優點
- 補充:Nodejs解決重繪頁面服務器404問題方法(借助中間件)
- 比如:
connect-history-api-fallbacknpm上的connect-history-api-fallback- 安裝:
npm i connect-history-api-fallback - 引入:
const history = require('connect-history-api-fallback'); - 使用:
app.use(history());(注意要在配置靜態資源前使用)
- 安裝:
- 比如:
(八)Vue的常用UI組件庫
一、移動端常用UI組件庫
1.Vant
2.Cube UI
3.Mint UI
二、PC端常用UI組件庫
1.Element UI
2.IView UI
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/413463.html
標籤:其他
上一篇:關于 JS 閉包看這一篇就夠了
