1. 邂逅Vuejs
1.1 簡單認識一下Vuejs
Vue是一個漸進式的框架,什麼是漸進式呢?
- 漸進式意味著你可以將Vue作為應用的一部分嵌入其中,帶來更豐富的互動體驗
- 或者你希望將更多的業務邏輯使用Vue實現,那麼Vue核心庫以及其生態系統。
- 比如Core+Vue-router-Vuex,也可以滿足你各種各樣的需求。
Vue有很多特點和Web開發中常見的高階功能
- 解耦試圖的資料
- 可複用的元件
- 前端路由技術
- 狀態管理
- 虛擬Dom
這些特點,你不需要一個個去記住,我們在後面的學習和開發中都會慢慢體會到的
學習Vuejs的前提?
- 從零學習Vue,並不需要你具備其他類似與Angular、React,甚至JQuery的經驗
- 但是你需要具備一定的HTML、CSS、JavaScript基礎
1.2 安裝Vuejs
安裝Vue的方式有很多:
-
直接CDN引入
-
CDN
對於製作原型或學習,你可以這樣使用最新版本: <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> 對於生產環境,我們推薦連結到一個明確的版本號和構建檔案,以避免新版本造成的不可預期的破壞: <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12"></script>
-
你可以選擇引入開發環境版還是生成環境版本
<!-- 開發環境版本,包含了有幫助的命令列警告 --> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <!-- 生產環境版本,最佳化了尺寸和速度 --> <script src="https://cdn.jsdelivr.net/npm/vue"></script>
-
NPM安裝
後續透過webpack和CLI的使用,我們使用改方式
-
1.3. Hello Vuejs
我們來做一個Vue程式,體驗一下Vue的響應式
我們來閱讀JavaScript程式碼,會發現建立了一個Vue物件
建立Vue物件的時候,傳入了一些options:{}
- {}中包含了el屬性:該屬性決定了這個Vue物件掛載到了哪一個元素上,很明顯,我們這裡掛載了id為app的元素上
- {}中包含了data屬性:該屬性中通常會儲存一些資料
- 這些資料可以是我們直接定義出來的,比如想上面這樣。
- 也可能來自網路,伺服器載入的
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">{{messages}}</div>
<script src="../js/vue.js"></script>
<script>
<!-- 程式設計方式:宣告式程式設計 -->
let app = new Vue({
el: '#app', // 用於掛載管理的元素
data: {
message: 'Hello Vue js!'
}
});
// 元素js的做法(程式設計方式:指令式程式設計)
// 1.建立div元素,設定id屬性
// 2.定一個變數叫message
// 3.將message變數放在前面的div元素中顯示
</script>
</body>
</html>
瀏覽器顯示
什麼是指令式程式設計和宣告式程式設計:
指令式程式設計:命令“機器”如何去做事情(how),這樣不管你想要的是什麼(what),它都會按照你的命令實現。
宣告式程式設計:告訴“機器”你想要的是什麼(what),讓機器想出如何去做(how)。
詳細:https://www.cnblogs.com/sirkevin/p/8283110.html
1.4. 建立Vue例項傳入的Options
你會發現,我們在建立Vue例項的時候,傳入了一個物件options
詳細請訪問:https://cn.vuejs.org/v2/api/#選項-DOM
目前掌握這些選項:
- el:
- 型別:
string | Element
- 限制:只在用
new
建立例項時生效。 - 作用:決定之後Vue例項會管理哪一個DOM
- 詳細:https://cn.vuejs.org/v2/api/#el
- 型別:
- data:
- 型別:
Object | Function
- 限制:元件的定義只接受
function
。
- 型別:
- methods:
- 型別:
{ [key: string]: Function }
- 作用:定義屬於Vue的一些方法,可以在其他地方呼叫,也可以在指令中使用
- 詳細:https://cn.vuejs.org/v2/api/#methods
- 型別:
1.5. Vue的生命週期
生命週期:事物從誕生到消亡的整個過程
vue每個元件都是獨立的,每個元件都有一個屬於它的生命週期,從一個元件建立、資料初始化、掛載、更新、銷燬,這就是一個元件所謂的生命週期。在元件中具體的方法有:
beforeCreate
created
beforeMount
mounted
(
beforeUpdate
updated
)
beforeDestroy
destroyed
生命週期流程圖:
詳細:https://www.jianshu.com/p/410b6099be69
1.6 設定Vue的template(模板)
-
開啟Webstorm
-
找到Editor-> Live Template - >Vue
-
進行設定 (Abbreviation: 縮寫詞, Description: 說明)
<div id="app">{{message}}</div> <script src="../js/vue.js"></script> <script> let app = new Vue({ el: '#app', data: { message: 'Hello Vue js!' } }); </script>
-
點選Change
-
OK
2. 基本語法
2.1 插值操作
如何將data中的檔案資料,插入到HTML中呢?
- 可以透過Mustache語法(也就是雙大括號)
- Mustache:鬍子/鬍鬚
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<p>{{message}}</p>
<p>{{firstName}} {{lastName}}</p>
<p>{{firstName + lastName}}</p>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!',
firstName: '不能說的秘密',
lastName: '周杰倫'
}
});
</script>
</body>
</html>
2.2 指令
v-once
在某些情況下,我們可能不希望介面隨意的隨意改變
- 這個時候,我們就可以使用Vue的指令
v-once:
- 改指令後面不需要跟如何表示式
- 該指令表達元素和其他元件只渲染一次,不會隨著資料改變而改變
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<p v-once>{{message}}</p>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!',
}
});
</script>
</body>
</html>
v-html
某些情況下,我們從伺服器請求到的資料本身就是一個HTML程式碼
- 如果我們直接透過{{}}來輸出,會將HTML程式碼也一起輸出
- 但是我們可能希望的是按照HTML進行解析,並且顯示對應的資料內容
如果我們希望解析出HTML展示可以使用v-html指令
- 該指令後面往往會將跟上一個string型別
- 會將string的html解析出來並且進行渲染
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<p v-once>{{message}}</p>
<p>{{url}}</p>
// 使用v-html進行渲染
<p v-html="url"></p>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!',
url: '<a href="https://cn.vuejs.org/v2/guide/">官網</a>'
}
});
</script>
</body>
</html>
v-text
v-pre
v-cloak(cloak:斗篷)
3. 繫結屬性
3.1 v-bind
某些屬性我們也希望動態來繫結。
- 比如動態繫結a元素的href屬性
- 比如動態繫結img元素的src屬性
這個時候,我們可以使用v-bind指令:
- 作用:動態繫結屬性
- 縮寫::
- 預期: any fwith argument)I Object(without argument)
- 引數:attrOrProp(optional)
我們如果直接使用musttache語法進行繫結的話,會出現下面的情況
正確的使用方式是透過v-bind,讓標籤進行進行動態繫結達到我們想要的效果
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 錯誤的做法:這裡不能使用mustache語法 -->
<!-- <img src="{{img}}" alt="">-->
<!-- 正確的使用方式 -->
<img v-bind:src="img" alt=""><br>
<a v-bind:href="href">嗶哩嗶哩</a>
<!-- 語法糖使用方式 -->
<a :href="href"></a>
<img :src="img" alt="">
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!',
img: "https://cn.vuejs.org/images/logo.png",
href: "https://www.bilibili.com/"
}
});
</script>
</body>
</html>
動態繫結class
當然我們也可以使用v-bind進行動態繫結class
效果:
透過console來更改Boolean值
上面那種寫法會讓人感覺到不是很友好,我們可以使用函式來封裝
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.active{
color: skyblue;
}
</style>
</head>
<body>
<div id="app">
<!--
{}表示是一個物件
{key1: value1, key2: value2}
{類名1:boolean, 型別2: boolean}
-->
<!-- 兩個標量都為true時,樣式才會生效 -->
<h2 v-bind:class="{active: isActive, line: isLine}">{{message}}</h2>
<!-- 上面那種寫法會讓人感覺到不是很友好,我們可以使用函式來封裝 -->
<h2 v-bind:class="">{{message}}</h2>
<!-- 使用v-on指令進行動態繫結事件,後面我們會學習到 -->
<button v-on:click="btnClick">按鈕</button>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!',
isActive: true,
isLine: true
},
methods: {
btnClick: function(){
this.isActive = !this.isActive
},
getClass: function() {
return {active: this.isActive, line: this.isLine}
}
}
});
</script>
</body>
</html>
動態繫結style
在寫CSS屬性的時候,比如font-size
- 我們可以使用駝峰式
- 或者短橫線分隔font-size(但是記得加單引號括起來)
例項圖:
效果圖:
也可以使用變數名來進行
效果圖:
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- <h2 :style="{key(屬性名): value(屬性值)}"></h2>-->
<!-- 50px必須加單引號, 否則當做一個變數去解析 -->
<h2 :style="{fontSize: '50px'}">{{message}}</h2>
<!-- 使用font-size必須加單引號 -->
<h2 :style="{'fontSize': '50px'}">{{message}}</h2>
<!-- finalSize當成一個變數使用 -->
<h2 :style="{fontSize: finalSize + 'px'}">{{message}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!',
finalSize: 100
}
});
</script>
</body>
</html>
3.2 計算屬性
基本使用:
我們知道,在模板中可以直接透過插值語法顯示一些data中的資料。
但是在某些情況,我們可能需要對資料進行一些轉化後再顯示,或者需要將多個資料結合起來進行顯示
-
比如我們有firstName和lastName兩個變數,我們需要顯示完整的名稱。
-
但是如果多個地方都需要顯示完整的名稱,我們就需要寫多個
基本使用:
效果圖:
複雜操作:
結果:
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
{{totalPrice}}
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
books:[
{id:110, name: 'Java', price: 100},
{id:111, name: 'Python', price: 100},
{id:112, name: 'Vue', price: 100},
{id:113, name: 'C', price: 100},
]
},
// computed: 計算機屬性()
computed:{
totalPrice: function (){
let result = 0
for (let i = 0; i < this.books.length; i++) {
result += this.books[i].price;
}
return resultA
// 這兩種是es6語法當中的
// for (let i in this.books) {
// result += this.books[i].price
// }
// for (let book of this.books) {
// result += book.price
// }
}
}
});
</script>
</body>
</html>
3.3 computed和methods的對比
你可能已經注意到我們可以透過在表示式中呼叫方法來達到同樣的效果:
<p>Reversed message: "{{ reversedMessage() }}"</p>
// 在元件中
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
我們可以將同一函式定義為一個方法而不是一個計算屬性。兩種方式的最終結果確實是完全相同的。然而,不同的是計算屬性是基於它們的響應式依賴進行快取的。只在相關響應式依賴發生改變時它們才會重新求值。這就意味著只要 message
還沒有發生改變,多次訪問 reversedMessage
計算屬性會立即返回之前的計算結果,而不必再次執行函式。
這也同樣意味著下面的計算屬性將不再更新,因為 Date.now()
不是響應式依賴:
computed: {
now: function () {
return Date.now()
}
}
相比之下,每當觸發重新渲染時,呼叫方法將總會再次執行函式。
我們為什麼需要快取?假設我們有一個效能開銷比較大的計算屬性 A,它需要遍歷一個巨大的陣列並做大量的計算。然後我們可能有其他的計算屬性依賴於 A。如果沒有快取,我們將不可避免的多次執行 A 的 getter!如果你不希望有快取,請用方法來替代。
總結:
computed:會進行快取,如果多次使用時,計算屬性只會呼叫一次。
methods: 呼叫一次執行一次
3.4 v-on
概述:
在前端開發中,我們需要經常和用於互動。
- 這個時候,我們就必須監聽使用者發生的時間,比如擊、拖拽、鍵盤事件等等
- 在Vue中如何監聽事件呢?使用v-on指令v-on介紹
v-on介紹:
- 作用:繫結事件監聽器
- 縮寫:@
- 預期:Function |Inline Statement|Object
- 引數:event
基本使用
上面這種方法,適合簡單的事件繫結,如果我們有其他的功能實現這種寫法就不太好,所以我推薦下面這種寫法
當然,也可以用語法糖的方式進行編寫
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h2>{{number}}</h2>
<!-- 簡單的使用 click:滑鼠點選事件 -->
<!-- <button v-on:click="number++">+</button>-->
<!-- <button v-on:click="number--">-</button>-->
<!-- <button v-on:click="increment">+</button>-->
<!-- <button v-on:click="decrement">-</button>-->
<button @:click="increment">+</button>
<button @:click="decrement">-</button>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!',
number: 10 // 定義一個引數
},
methods:{
increment() {
this.number++
},
decrement() {
this.number--
}
}
});
</script>
</body>
</html>
v-on還可以進行傳引數
如果你需要傳遞引數就記得新增(),如果你不需要傳遞引數就不用新增()
如果不知道什麼是MouseEvent物件的話:
https://blog.csdn.net/claroja/article/details/103990202
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 傳遞引數 -->
<button @click="btnClick(123)">按鈕1</button>
<!-- 如果方法定義了一個引數,我們這裡不傳遞引數的話,它會傳遞一個MouseEvent物件 -->
<button @click="btnClick">按鈕2</button>
<!-- 我們這裡加了括號沒有傳遞引數的話,形參就是undefined-->
<button @click="btnClick()">按鈕3</button>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!'
},
methods: {
btnClick(name) {
console.log(name)
}
}
});
</script>
</body>
</html>
v-on的修飾符:
- .stop - 呼叫event.stopPropagation()
- .prevent - 呼叫event.preventDefault()
- .{keyCode | keyAlias} 只當時間是從特定鍵觸發時才觸發回撥
- .native - 監聽元件根元素的原生事件
- .once - 只觸發一次回撥
基本使用:
效果圖:
如果我們想組織這種冒泡事件可以使用.stop
效果圖:
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<div @click="divClick">
aaa
<!-- 當我們點選這個按鈕時,它會出現冒泡事件 -->
<button @click.stop="btnClick">按鈕</button>
</div>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!'
},
methods: {
btnClick() {
console.log("btnClick")
},
divClick() {
console.log("divClick")
}
}
});
</script>
</body>
</html>
4. 條件判斷
4.1 v-if v-else-if v-else
v-if 指令用於條件性地渲染一塊內容。這塊內容只會在指令的表示式返回true 值的時候被渲染。
基本使用:
效果圖:
v-else的使用
效果圖:
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h1 v-if="isShow">{{message}}</h1>
<h1 v-else>isShow為false的時候你才能看到我</h1>
<button @click="isShow_function">按鈕</button>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!',
isShow: true
},
methods: {
isShow_function() {
this.isShow = !this.isShow
console.log(this.isShow)
}
}
});
</script>
</body>
</html>
4.2 v-show
v-show和v-if非常相似,也用於決定一個元素是否渲染
v-if和v-show對比
-
v-if和v-show都可以決定一個元素是否渲染,那麼開發中我們如何選擇?
- v-if當條件為false時,壓根不會有對應的元素在DOM中
- v-show當條件為false時,僅僅是將元素的display屬性設定為none而已
-
開發中如何選擇?
- 當需要在顯示與隱藏之間切片很頻繁時,使用v-show
- 但只有一次切換時,使用v-if
基本使用:
效果圖:
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- v-if當條件為false時,壓根不會有對應的元素在DOM中 -->
<p v-if="isShow">{{message}} v-if</p>
<!-- v-show為false時,僅僅是將元素的display屬性設定為none而已 -->
<p v-show="isShow">{{message}} v-show</p>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!',
isShow: true
}
});
</script>
</body>
</html>
5. 迴圈遍歷
5.1 v-for
當我們有一組資料需要渲染時,我們就可以使用v-for
- v-for的語法類似於JavaScript中的for迴圈
- 格式:item in itmes的形式
如果在遍歷的過程中,我們需要拿到元素在陣列的中的索引值
- 語法格式: v-for = (item, index) in items
- 其中的index就代表了取出的item在原陣列中的索引值
基本使用:
效果圖:
當然也可以遍歷物件
效果圖:
如果想拿到key的話,可以這樣:
效果圖:
注意:遍歷陣列拿下標,item在前,index在後, 遍歷物件拿key的話, values在前, key在後
6.表單繫結
6.1 v-model
Vue中使用v-model指令來實現表單元素和資料的雙向繫結
效果圖:
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="text" v-model="message">
{{message}}
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!'
}
});
</script>
</body>
</html>
v-model其實是一個語法糖,他的背後本質上是包含兩個操作:
- v-bind繫結一個value屬性
- v-on指令給當前元素繫結input事件
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="text" v-bind:value="message" v-on:input="message=$event.target.value">
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!'
}
});
</script>
</body>
</html>
我們可以使用v-model結合radio使用:
效果圖:
也可以配合checkbox:
效果圖:
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="checkbox" value="LXL" v-model="hobbies">LXL
<input type="checkbox" value="LOL" v-model="hobbies">LOL
<input type="checkbox" value="LkL" v-model="hobbies">LKL
<input type="checkbox" value="LPL" v-model="hobbies">LPL
<br>
<h2>你的愛好:{{hobbies}}</h2>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!',
hobbies: []
}
});
</script>
</body>
</html>
6.2 值繫結
值繫結的意思就是給value賦值
- 我們前面的value中的值,可以回頭看一下,都是在定義input的時候直接給定的
- 但是真實開發中,這些input的值可能是從網路獲取或定義data中的
- 所以我們可以透過v-bind:value動態給value繫結值
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<input type="checkbox" value="LXL" v-model="hobbies">LXL
<input type="checkbox" value="LOL" v-model="hobbies">LOL
<input type="checkbox" value="LkL" v-model="hobbies">LKL
<input type="checkbox" value="LPL" v-model="hobbies">LPL
<br>
<h2>你的愛好:{{hobbies}}</h2>
<!-- 我們透過v-for迴圈遍歷my_hobbies並且生成input標籤 在透過v-bind繫結值 -->
<label v-for="item in my_hobbies" :for="item">
<!-- 我們在透過v-bind繫結item所遍歷的值, 在透過v-model進行雙向繫結 -->
<input type="checkbox" :value="item" v-model="hobbies">{{item}}
</label>
</div>
<script src="../js/vue.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!',
hobbies: [],
my_hobbies: ['LXL', 'LOL', 'LKL', 'LPL']
}
});
</script>
</body>
</html>
6.3 修飾符
lazy修飾符:
- lazy修飾符可以讓資料在失去焦點或者回車時才會更新
number修飾符:
- number修飾符可以讓在輸入框中輸入的內容自動轉成數字型別
trim修飾符:
- trim修飾符可以過濾內容左右兩邊的空格
7. 元件化開發
7.1 什麼是元件化?
人面對複雜問題的處理方式:
- 任何一個人處理資訊的邏輯能力都是有限的
- 所以,當面對一個非常複雜的問題時,我們不太可能一次性搞定一大堆的內容。
- 但是,我們人有一種天生的能力,就是將問題進行拆解。
- 如果將一個複雜的問題,拆分成很多個可以處理的小問題,再將其放在整體當中,你會發現大的問題也會迎刃而解。
元件化也是類似的思想:
- 如果我們將一個頁面中所有的處理邏輯全部放在一起,處理起來就會變得非常複雜,而且不利於後續的管理以及擴充套件。
- 但如果,我們講一個頁面拆分成一個個小的功能塊,每個功能塊完成屬於自己這部分獨立的功能,那麼之後整個頁面的管理和維護就變得非常容易了。
7.2 Vue元件化思想
元件化是Vue.js中的重要思想
-
他提供了一中抽象,讓我們可以開發出一個個獨立可複用的小元件來構造我們的應用
-
任何的應用都會被抽象成一顆元件樹
元件化思想的應用:
- 有了元件化的思想,我們在之後的開發中就要充分的利用它
- 儘可能的將頁面拆分成一個個小的、可複用的元件
- 這樣讓我們的程式碼更加方便組織和管理,並且擴充套件性也更強
7.3 元件化的基本步驟
-
建立元件構造器
Vue.extend0:
- 呼叫Vue.extend0建立的是一個元件構造器。
- 通常在建立元件構造器時,傳入template代表我們自定義元件的模板。
- 該模板就是在使用到元件的地方,要顯示的HTML程式碼。
- 事實上,這種寫法在Vue2.x的檔案中幾乎已經看不到了,它會直接使用下面我們會講到的語法糖,但是在很多資料還是會提到這種方式,而且這種方式是學習後面方式的基礎。
-
註冊元件
Vue.component():
-
呼叫Vue.component0是將剛才的元件構造器註冊為一個元件,並且給它起一個元件的標籤名稱。
-
所以需要傳遞兩個引數:1、註冊元件的標籤名 2、元件構造器
-
-
使用元件
- 元件必須掛載在某個Vue例項下,否則它不會生效。
效果圖:
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 使用元件 -->
<my-cpn></my-cpn>
</div>
<script src="../js/vue.js"></script>
<script>
<!-- 1.建立元件構造器物件 -->
const cpnC = Vue.extend({
template: `
<div>
<h2>我是標題</h2>
<p>我是內容1</p>
<p>我是內容2</p>
</div>
`
})
// 2. 註冊元件
Vue.component('my-cpn', cpnC)
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!'
}
});
</script>
</body>
</html>
7.4 全域性元件和區域性元件
我們上面建立的就是全域性元件,全域性元件就是可以在多個Vue例項裡面使用
效果圖:
那麼我們如何建立區域性元件? 其實建立區域性元件也很簡單,把要註冊的元件放到某個Vue例項裡面註冊就好了
效果圖:
我們會發現
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
</div>
<div id="app1">
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
<!-- 1.建立元件構造器物件 -->
const cpnC = Vue.extend({
template: `
<div>
<h2>我是標題</h2>
<p>我是內容1</p>
<p>我是內容2</p>
</div>
`
})
// 註冊元件(意味著可以在多個Vue例項中使用)
// Vue.component("my-cpn", cpnC)
// 如何註冊區域性元件?
// 我們只需要在某個Vue例項裡面註冊即可
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!'
},
// components:元件,component的複數,表示可以使用多次
components: {
// cpn:使用元件的標籤名
cpn: cpnC
}
});
// 建立一個vue例項
let app1 = new Vue({
el: '#app1'
})
</script>
</body>
</html>
7.5 父元件和子元件
什麼是父元件,什麼又是子元件。簡單來說:我們把某段程式碼封裝成了一個元件,而這個段元件裡面又引入另一個元件,我們把引入封裝的元件叫做父元件,把被引入的元件叫做子元件。
效果圖:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
<!-- 註冊子元件 -->
const cpnC2 = Vue.extend({
template: `
<div>
<h2>我是子元件</h2>
<p>我是子元件的內容</p>
</div>
`
})
// 註冊父元件
const cpnC1 = Vue.extend({
template: `
<div>
<h2>我是父元件</h2>
<p>我是父元件的內容</p>
<!-- // 但解析到這一步的時候,它會先去你Vue例項components裡面找你是否註冊了cpn2-->
<!-- // 如果沒有找到他才會在cpnC1components去找-->
<cpn2></cpn2>
</div>
`,
// 把子元件註冊到父元件
components: {
cpn2: cpnC2
},
})
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!'
},
components: {
cpn: cpnC1
}
});
</script>
</body>
</html>
7.6 註冊元件的語法糖方式
我們之前註冊元件的方式,會覺得有些繁瑣
- Vue為了簡化這個過程,提供了註冊的語法糖
- 主要是省去了調傭Vue.extend()的步驟,而是可以直接使用一個物件來替換
效果圖:
程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 使用元件 -->
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// component會跳用Vue.extend來建立元件
// 所以我們以後可以使用這種語法糖模式來建立全域性元件
Vue.component('my-cpn', {
template: `
<div>
<h2>元件</h2>
<p>元件的內容</p>
</div>
`
})
let app = new Vue({
el: '#app',
data: {
message: 'Hello Vue js!'
},
// 區域性元件
components: {
cpn:{
template: `
<div>
<h2>元件</h2>
<p>元件的內容</p>
</div>
`
}
}
});
</script>
</body>
</html>
7.7 元件模板抽離
我們之前雖然簡化了Vue元件的註冊過程,另外還有一個地方的寫法比較麻煩,就是template模板的寫法
如果我們能將其中的html分離出來寫,然後掛載到對應的元件上,必然結構會變得非常清晰
Vue提供了兩種方案來定義HTML模組內容:
- 使用
標籤 - 使用標籤 (推薦使用這種方法)
使用srcipt標籤:
使用template標籤:
程式碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 使用元件 --> <my-cpn></my-cpn> </div> <!--使用template標籤進行模板分離--> <template id="cpn1"> <div> <h2>元件標題</h2> </div> </template> <script src="../js/vue.js"></script> <script> // 2. 註冊元件 Vue.component('my-cpn', { template: "#cpn1" }) let app = new Vue({ el: '#app', data: { message: 'Hello Vue js!' } }); </script> </body> </html>
7.8 Vue元件中的data為什麼必須是一個函式
Vue元件中data為什麼必須是一個函式呢?
從之前的Vue例項裡面我們可以看出來,data是一個物件
<div id="app"> </div> <script src="../js/vue.js"></script> <script> let app = new Vue({ el: '#app', data: { message: 'Hello Vue js!' } }); </script>
當我們使用元件開發的時候,如果data不是個函式,是個物件的話,會出現一種情況,連鎖反應,我們用下面的程式碼來舉個例子
<script> Vue.component('cpn', { template: '#cpn1', data: { number: 0 }, methods: { increment() { this.number++ }, decrement(){ this.number-- } } }) </script>
如果我們直接這樣寫的法,Vue會給我們直接報錯,所以這種方式沒法演示,我將採取另外一種方式,來演示
但我們點選其中某一個按鈕的時候,會發現所以的數量都發生了變化。
為什麼會出現這種情況呢?
因為Object是引用資料型別,如果不用function返回,每個元件的data都是記憶體的同一個地址,一個資料改變了其他也改變了;
JavaScript只有函式構成作用域(注意理解作用域,只有函式{}構成作用域,物件的{}以及if(){}都不構成作用域),data是一個函式時,每個元件例項都有自己的作用域,每個例項相互獨立,不會相互影響。
效果圖:
程式碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> <cpn></cpn> <cpn></cpn> </div> <template id="cpn1"> <div> <h2>當前計數:{{number}}</h2> <button @click="increment">+</button> <button @click="decrement">-</button> </div> </template> <script src="../js/vue.js"></script> <script> Vue.component('cpn', { template: '#cpn1', data() { return { number: 0 } }, methods: { increment() { this.number++ }, decrement(){ this.number-- } } }) let app = new Vue({ el: '#app', data: { message: 'Hello Vue js!' } }); </script> </body> </html>
7.9 父子元件通訊
我們提到了子元件是不能引用父元件或者Vue例項的資料的。
但是,在開發中,往往一些資料確實需要從上層傳遞到下層∶
- 比如在一個頁面中,我們從伺服器請求到了很多的資料。
- 其中一部分資料,並非是我們整個頁面的大元件來展示的,而是需要下面的子元件進行展示.
- 這個時候,並不會讓子元件再次傳送一個網路請求,而是直接讓大元件(父元件)將資料傳遞給小元件(子元件)
如何進行父子元件間的通訊呢?Vue官方提到
- 透過props向子元件傳遞資料
- 透過事件向父元件傳送訊息
7.9.1 props基本用法
在元件中,使用選項props來宣告需要從父級接收到的資料。
props的值有兩種方式:
- 方式一︰字串陣列,陣列中的字串就是傳遞時的名稱。
- 方式二∶物件,物件可以設定傳遞時的型別,也可以設定預設值等。
使用陣列的方式進行傳遞:
效果圖:
程式碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn :cmessage="message" :cmovies="movies"></cpn> </div> <template id="cpn"> <div> <p>{{cmessage}}</p> <ul> <li v-for="item in cmovies">{{item}}</li> </ul> </div> </template> <script src="../js/vue.js"></script> <script> <!-- 父傳子:props --> const cpn = { template: '#cpn', // 可以透過陣列的方式進行傳遞 // 陣列裡面的資料,不是字串,而是一個變數名 props:['cmessage', 'cmovies'] } let app = new Vue({ el: '#app', data: { message: 'Hello Vue js!', movies: ['不能說的秘密', 'LXY', 'YXL'] }, // 註冊元件 components: { cpn // 註冊模板 } }); </script> </body> </html>
透過陣列的傳遞方式,我們會感覺到有點變扭,陣列裡面的資料看起來明明是字串,為什麼會是陣列呢
所以我們一般使用用物件的方式傳遞
程式碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 這裡必須使用v-bind 不然cmessage表示一個變數, 就會把message當初一個字串賦值給cmessage --> <cpn :cmessage="message" :cmovies="movies"></cpn> </div> <template id="cpn"> <div> <p>{{cmessage}}</p> <ul> <li v-for="item in cmovies">{{item}}</li> </ul> </div> </template> <script src="../js/vue.js"></script> <script> <!-- 父傳子:props --> const cpn = { template: '#cpn', // 可以透過陣列的方式進行傳遞 // 陣列裡面的資料,不是字串,而是一個變數名 props: { cmessage: { type: String, // 型別限制 default: 'aaa', // 設定預設值 required: true // required:必須的, 這個引數必須要傳,不傳就會報錯 }, cmovies: { type: Array, default: [] } } } let app = new Vue({ el: '#app', data: { message: 'Hello Vue js!', movies: ['不能說的秘密', 'LXY', 'YXL'] }, // 註冊元件 components: { cpn // 註冊模板 } }); </script> </body> </html>
7.9.2 props駝峰標識
html程式碼有一個缺陷,就是不區別大小寫,所以當我們使用駝峰命名發的時候,會出現這種問題
效果圖:
如果我們要是用駝峰標識的話,需要這樣寫
效果:
程式碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 因為HTML程式碼不區分大小寫,如果我們要使用駝峰標識需要寫成這樣c-message --> <cpn :c-message="message" :c-movies="movies"></cpn> </div> <template id="cpn"> <div> <p>{{cMessage}}</p> <ul> <li v-for="item in cMovies">{{item}}</li> </ul> </div> </template> <script src="../js/vue.js"></script> <script> <!-- 父傳子:props --> const cpn = { template: '#cpn', props: { cMessage: String, cMovies: Array } } let app = new Vue({ el: '#app', data: { message: 'Hello Vue js!', movies: ['不能說的秘密', 'LXY', 'YXL'] }, // 註冊元件 components: { cpn // 註冊模板 } }); </script> </body> </html>
7.10 子傳父通訊(自定義事件)
子元件向父元件傳遞資料時,使用自定義事件實現
自定義事件的流程:
- 在子元件中,透過$emit()來監聽事件
- 在父元件中,透過v-on來監聽子元件事件
程式碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--父元件模板--> <div id="app"> <cpn @item-click="cpnClick"></cpn> </div> <!--子元件模板--> <template id="cpn"> <div> <!-- 這裡迴圈遍歷並且繫結點選事件 --> <button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button> </div> </template> <script src="../js/vue.js"></script> <script> <!-- 子元件 --> const cpn = { template: '#cpn', data(){ return{ categories:[ {id: 111, name: 'LXY', age:18}, {id: 222, name: 'YXL', age:18}, {id: 333, name: 'YF', age:18} ] } }, methods: { btnClick(item) { // emit: 發出,射出; 發出事件: 自定義事件 // 透過點選事件,把item物件傳給父元件 // this.$emit('自定義的事件名稱',傳送引數) this.$emit('item-click', item) } } } // 父元件 let app = new Vue({ el: '#app', methods: { // 父元件設定一個方法,獲取到子元件傳遞過來的物件 cpnClick(item) { console.log(item) } }, // 註冊元件 components: { cpn // 註冊模板 } }); </script> </body> </html>
小結:
props用於父元件向子元件傳遞資料,還有一種比較常見的是子元件傳遞資料或事件到父元件中
我們應該如何處理呢?這個時候,我們需要使用自定義事件來完成。
什麼時候需要自定義事件呢?- 當子元件需要向父元件傳遞資料時,就要用到自定義事件了。
我們之前學習的v-on不僅僅可以用於監聽DOM事件,也可以用於元件間的自定義事件。自定義事件的流程︰
- 在子元件中,透過$emit()來觸發事件。
- 在父元件中,透過v-on來監聽子元件事件。
我們來看一個簡單的例子︰
- 我們之前做過一個兩個按鈕+1和-1,點選後修改couRter。
- 我們整個操作的過程還是在子元件中完成,但是之後的展示交給父元件。
- 這樣,我們就需要將子元件中的counter,傳給父元件的某個屬性,比如total。
7. 11 父子元件的訪問方式
有時候我們需要父元件直接訪問子元件,子元件直接訪問父元件,或者是子元件訪問跟元件。
- 父元件訪問子元件∶使用\(children**或**\)refs reference(引用)
- 子元件訪問父元件:使用$parent
我們先來看下$children的訪問
- this.$children是一個陣列型別,它包含所有子元件物件。
- 我們這裡透過一個遍歷,取出所有子元件的message狀態。
效果圖:
透過上述圖我們發現,$children去子元件返回了一個Vuecomponent物件
裡面有很多不是我們想要的資料,所以我們一般不推薦使用\(children,我們一般推薦使用**\)refs**
效果圖:
程式碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--父元件--> <div id="app"> <cpn ref="aaa"></cpn> <!-- 透過點選事件去template獲取資料 --> <button @click="btnClick">按鈕</button> </div> <!--子元件--> <template id="cpn"> <div> <div>我是子元件</div> </div> </template> <script src="../js/vue.js"></script> <script> let app = new Vue({ el: '#app', data: { message: 'Hello Vue js!' }, methods: { btnClick() { // 1.透過$children去子元件中拿到資料 // console.log(this.$children) // console.log(this.$children[0].name) // 2. $refs console.log(this.$refs.aaa.name) } }, components: { cpn: { template: '#cpn', data() { return { name: "我的名字叫做子元件" } } } } }); </script> </body> </html>
7.12 slot
為什麼要使用slot(插槽)?
- 在生活中很多地方都有插槽,電腦的USB插槽,插板當中的電源插槽
- 插槽的目的是讓我們原來的裝置具備更多的擴充套件性。
- 比如電腦的USB我們可以插入隨身碟、硬碟、手機、音響、鍵盤、滑鼠等等。
元件的插槽∶
- 元件的插槽也是為了讓我們封裝的元件更加具有擴充套件性。
- 讓使用者可以決定元件內部的一些內容到底展示什麼。
例子︰移動網站中的導航欄
- 移動開發中,幾乎每個頁面都有導航欄。
- 導航欄我們必然會封裝成一個外掛,比如nav-bar元件。
- 一旦有了這個元件,我們就可以在多個頁面中複用了。
如何去封裝這類的元件呢?
- 它們也很多區別,但是也有很多共性。
- 如果,我們每一個單獨去封裝一個元件,顯然不合適︰比如每個頁面都返回,這部分內容我們就要重複去封裝。
- 但是,如果我們封裝成一個,好像也不合理∶有些左側是選單,有些是返回,有些中間是搜尋,有些是文字,等等。
如何封裝合適呢?抽取共性,保留不同。
- 最好的封裝方式就是將共性抽取到元件中,將不同暴露為插槽。
- 一旦我們預留了插槽,就可以讓使用者根據自己的需求,決定插槽中插入什麼內容。
- 是搜尋框,還是文字,還是選單。由呼叫者自己來決定。
這就是為什麼我們要學習元件中的插槽slot的原因。
7.12.1 基本使用
效果圖:
插槽也可以設定預設值:
效果圖:
也可以更改插槽的預設值:
效果:
程式碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> <!-- 我們在元件裡面寫其他的東西就可以改變,插槽預設的東西 --> <cpn>132321</cpn> </div> <template id="cpn"> <div> <h2>我是元件</h2> <p>對的,元件</p> <!-- slot基本使用 --> <!-- <slot></slot>--> <!-- 預設值 --> <slot><button>按鈕</button></slot> </div> </template> <script src="../js/vue.js"></script> <script> let app = new Vue({ el: '#app', components: { cpn: { template: '#cpn' } } }); </script> </body> </html>
7.12.2 具名slot
具名插槽,顧名思義,就是有具體名字的插槽,使用時指定替換模板中哪個插槽的內容
就比如說我們用三個按鈕
這個時候我想替換中間的按鈕,如果我們直接這樣去更改按鈕的資料的話,會替換所有插槽的內容
這時我們就要使用到具名插槽,具名插槽的使用方法就是給每一個插槽新增一個name屬性,我們透過name屬性,來更改我們想要更改的插槽
7.12.3 編譯的作用域
什麼是編譯作用域,我們將透過下面一個程式碼來解釋
思考:這裡的模板裡面的內容會顯示嗎?
答案:會顯示
為什麼會顯示呢?
因為這裡面isShow的值來自Vue例項裡面的決定的,而不是元件裡面的
這個元件是資料Vue例項裡面的,所以首要選擇的作用域是vue例項如果不是很明白,那麼就在來一個案例
這裡button會顯示嗎?
答案是不會顯示
這裡的isShow的值是來自元件的,不是Vue例項,因為我們定義這個是在元件裡面,所以首要選擇的作用域當然是元件
程式碼:
<!DOCTYPE html> <html lang = "en"> <head> <meta charset = "UTF-8"> <title>Title</title> </head> <body> <div id="app"> <!-- 這裡面isShow的值來自Vue例項裡面的決定的,而不是元件裡面的 因為這個元件是資料Vue例項裡面的,所以首要選擇的作用域是vue例項 --> <cpn v-show="isShow"></cpn> </div> <template id="cpn"> <h2>我是標題</h2> <p>哈哈哈哈哈哈</p> <!--這裡的isShow的值是來自元件的,不是Vue例項, 因為我們定義這個是在元件裡面,所以首要選擇的作用域當然是元件--> <button v-show="isShow"></button> </template> <script src="../js/vue.js"></script> <script> const app = new Vue({ el: '#app', data: { message: '你好啊!!!', isShow:true }, components:{ cpn:{ template:`#cpn`, data(){ return{ isShow:false } } } } }) </script> </body> </html>
7.12.4 作用域插槽
父元件替換插槽的標籤,但是內容由子元件來提供的。
我們先提一個需求∶
-
子元件中包括一組資料,比如:pLanguages: ['JavaScript', 'Python', 'Swift', 'Go','C++']
-
需要在多個介面進行展示:
- 某些介面是以水平方向——展示的,
- 某些介面是以列表形式展示的,
- 某些介面直接展示一個陣列
-
內容在子元件,希望父元件告訴我們如何展示,怎麼辦呢?
- 利用slot作用域插槽就可以了
程式碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div id="app"> <cpn></cpn> <cpn> <!-- 目的是獲取子元件中的play --> <template v-slot="slot"> <span>{{slot.data.join('-')}}</span> </template> </cpn> </div> <template id="cpn"> <div> <slot :data="play"> <ul> <li v-for="item in play">{{item}}</li> </ul> </slot> </div> </template> <script src="../js/vue.js"></script> <script> let app = new Vue({ el: '#app', data: { message: 'Hello Vue js!' }, components: { cpn: { template: '#cpn', data() { return { play: ['Java', 'Python', 'Vue'] } }, // 定義一個方法 create() { this.play.join('-') } } } }); </script> </body> </html>
7.12.5 v-slot(動態插槽名)
但我們學習完 slot 和 slot-scope這兩個在vue3.x已經不在使用了
v-slot使用方法跟slot一樣,這裡就不在演示
具名插槽在2.6.0 新增新增了一個語法糖的功能,跟
v-on
和v-bind
一樣,v-slot
也有縮寫,即把引數之前的所有內容 (v-slot:
) 替換為字元#
。例如v-slot:header
可以被重寫為#header
:<!-- v-slot只能在template或者components裡使用 --> <template id="cpn" v-slot="head"> <div> <p>我是頭部</p> </div> </template> <template id="footer" v-slot="footer"> <div> <p>我是低部</p> </div> </template>
也可以使用語法糖寫法
<!-- v-slot語法糖寫法 # --> <template id="cpn" #head> <div> <p>我是頭部</p> </div> </template> <template id="footer" #footer> <div> <p>我是低部</p> </div> </template>
Vue CLI
1. 什麼是Vue CLI
Vue CLI 是一個基於 Vue.js 進行快速開發的完整系統,提供:
-
透過
@vue/cli
實現的互動式的專案腳手架。 -
透過
@vue/cli
+@vue/cli-service-global
實現的零配置原型開發。 -
一個執行時依賴 (
@vue/cli-service
),該依賴:
- 可升級;
- 基於 webpack 構建,並帶有合理的預設配置;
- 可以透過專案內的配置檔案進行配置;
- 可以透過外掛進行擴充套件。
-
一個豐富的官方外掛集合,整合了前端生態中最好的工具。
-
一套完全圖形化的建立和管理 Vue.js 專案的使用者介面。
Vue CLI 致力於將 Vue 生態中的工具基礎標準化。它確保了各種構建工具能夠基於智慧的預設配置即可平穩銜接,這樣你可以專注在撰寫應用上,而不必花好幾天去糾結配置的問題。與此同時,它也為每個工具提供了調整配置的靈活性,無需 eject。
2. 安裝腳手架
關於舊版本
Vue CLI 的包名稱由
vue-cli
改成了@vue/cli
。 如果你已經全域性安裝了舊版本的vue-cli
(1.x 或 2.x),你需要先透過npm uninstall vue-cli -g
或yarn global remove vue-cli
解除安裝它。Node 版本要求
Vue CLI 4.x 需要 Node.js v8.9 或更高版本 (推薦 v10 以上)。你可以使用 n,nvm 或 nvm-windows 在同一臺電腦中管理多個 Node 版本。
可以使用下列任一命令安裝這個新的包:
npm install -g @vue/cli # OR yarn global add @vue/cli
安裝之後,你就可以在命令列中訪問
vue
命令。你可以透過簡單執行vue
,看看是否展示出了一份所有可用命令的幫助資訊,來驗證它是否安裝成功。3. 建立腳手架
1. vue create 專案名
執行以下命令來建立一個新專案:
vue create hello-world
- 預設選擇Vue 2.x腳手架
- 預設選擇Vue 3.x腳手架
- 手動選擇要素
我們這裡選擇手動選擇要素,會出現下圖配置選項
2. 選擇配置,看個人專案需求
空格鍵是選中與取消,A鍵是全選, 按i是反選
- Choose Vue version: 選擇Vue版本 - Babel:轉碼器,可以將ES6程式碼轉為ES5程式碼,從而在現有環境執行。 - TypeScript: TypeScript是一個JavaScript(字尾.js)的超集(字尾.ts)包含並擴充套件了 JavaScript 的語法,需要被編譯輸出為 JavaScript在瀏覽器執行 - Progressive Web App (PWA) Support PWA: 漸進式Web應用程式 - Router: 支援 vue-router(vue路由 我們後面會學到) - Vuex: 支援 vuex (vuex的管理模式 我們後面會學到) - CSS Pre-processors:CSS 前處理器(如: less、sass) - Linter / Formatter: 程式碼風格檢查和格式化 (如:ESlint) - Unit Testing: 單元測試(unit tests) - E2E Testing: E2E(end to end) 測試。
3. 選擇一個你想用的Vue.js版本來啟動專案
這裡我選擇的是2.x
4. 選擇是否使用history router
Vue-Router 利用了瀏覽器自身的hash 模式和 history 模式的特性來實現前端路由(透過呼叫瀏覽器提供的介面)。
-
我這裡建議選n。這樣打包出來丟到伺服器上可以直接使用了,後期要用的話,也可以自己再開起來。
-
選yes的話需要伺服器那邊再進行設定。
5. 選擇css 前處理器
我選擇的是Sass/Scss(with dart-sass)
node-sass是自動編譯實時的,dart-sass需要儲存後才會生效。sass 官方目前主力推dart-sass 最新的特性都會在這個上面先實現
6. 選擇Eslint程式碼驗證規則
提供一個外掛化的javascript程式碼檢測工具,ESLint + Prettier (使用較多)
6.1 選擇什麼時候進行程式碼規則檢測
( ) Lint on save // 儲存就檢測
( ) Lint and fix on commit // fix和commit時候檢查
建議選擇儲存就檢測,等到commit的時候,問題可能都已經積累很多了7. 你喜歡把Babel、ESLint等的配置放在哪裡?
- 在專用配置檔案中
- 在package.json中
我這裡選擇package.json
如果是選擇 獨立檔案放置,專案會有單獨如下圖所示的幾件檔案。
8. 是否將此儲存為將來專案的預設?
鍵入N不記錄,如果鍵入Y需要輸入儲存名字,如第2步所看到的我儲存的名字為test。
9. 安裝中
4. Vue CLI 目錄解釋
1. node_modules
所有的第三方依賴,以及安裝包
2. public
公共資源。
public目錄下的資源會被直接複製,不會經過webpack的打包處理。(注:其中空資料夾資源不會被複制,即使使用copy-webpack-plugin也不會)。3. src
所有的專案檔案(原始碼)
3.1 assets
靜態資源
透過相對路徑被引入,這類引用會被webpack處理。
eg:background: url(’./image.png’)
css,img,js,font等3.2 component
公共元件,一般自定義元件
3.3 router
路由配置 vue-router
3.4 store
存放vue中的狀態資料,用於vuex集中管理
3.5 views
檢視元件
3.6 APP.vue
首頁元件(預設元件),使用標籤渲染整個工程的.vue組建
3.7 main.js
vue-cli工程的入口檔案
4. 瞭解即可
.browserslistrc(設定瀏覽器的相容)
·初始資訊如下
1%
last 2 versions
not dead1% :代表著全球超過1%人使用的瀏覽器
last 2 versions :表示所有瀏覽器相容到最後兩個版本
not dead :透過last 2 versions 篩選的瀏覽器版本中,全球使用率低於0.5%並且官方申明不再維護或者事實上已經兩年沒有在更新的版本,不再相容這些版本。
在package.json中配置 而非單獨的一個檔案
配置資訊配置如下
.editorconfig
幫助開發人員在不同的編輯器和IDE(整合開發環境)之間定義和維護一致的編碼樣式。
# 告訴EditorConfig外掛,這是根檔案,不用繼續往上查詢root = true # 匹配全部檔案 [*] # 結尾換行符,可選"lf"、"cr"、"crlf" end_of_line = lf # 在檔案結尾插入新行 insert_final_newline = true # 刪除一行中的前後空格 trim_trailing_whitespace = true # 匹配js和py結尾的檔案 [*.{js,py}] # 設定字符集 charset = utf-8 # 匹配py結尾的檔案 [*.py] # 縮排風格,可選"space"、"tab" indent_style = space # 縮排的空格數 indent_size = 4 # 以下匹配,類同 [Makefile] indent_style = tab# tab的寬度tab_width = 4 # 以下匹配,類同 [lib/**.js] indent_style = space indent_size = 2 [{package.json,.travis.yml}] indent_style = space indent_size = 2 1234567891011121314151617181920212223242526272829303132
⚠️ EditorConfig的匹配規則是從上往下,即先定義的規則優先順序比後定義的優先順序要高。
.eslintrc.js
配置ESLint,語法檢查
預設eslint規則:程式碼末尾不能加分號 ;
程式碼中不能存在多行空行;
tab鍵不能使用,必須換成兩個空格;
程式碼中不能存在宣告瞭但未使用的變數;Eslint的配置引數
"no-alert": 0,//禁止使用alert confirm prompt "no-array-constructor": 2,//禁止使用陣列構造器 "no-bitwise": 0,//禁止使用按位運運算元 "no-caller": 1,//禁止使用arguments.caller或arguments.callee "no-catch-shadow": 2,//禁止catch子句引數與外部作用域變數同名 "no-class-assign": 2,//禁止給類賦值 "no-cond-assign": 2,//禁止在條件表示式中使用賦值語句 "no-console": 2,//禁止使用console "no-const-assign": 2,//禁止修改const宣告的變數 "no-constant-condition": 2,//禁止在條件中使用常量表示式 if(true) if(1) "no-continue": 0,//禁止使用continue "no-control-regex": 2,//禁止在正規表示式中使用控制字元 "no-debugger": 2,//禁止使用debugger "no-delete-var": 2,//不能對var宣告的變數使用delete運運算元 "no-div-regex": 1,//不能使用看起來像除法的正規表示式/=foo/ "no-dupe-keys": 2,//在建立物件字面量時不允許鍵重複 {a:1,a:1} "no-dupe-args": 2,//函式引數不能重複 "no-duplicate-case": 2,//switch中的case標籤不能重複 "no-else-return": 2,//如果if語句裡面有return,後面不能跟else語句 "no-empty": 2,//塊語句中的內容不能為空 "no-empty-character-class": 2,//正規表示式中的[]內容不能為空 "no-empty-label": 2,//禁止使用空label "no-eq-null": 2,//禁止對null使用==或!=運運算元 "no-eval": 1,//禁止使用eval "no-ex-assign": 2,//禁止給catch語句中的異常引數賦值 "no-extend-native": 2,//禁止擴充套件native物件 "no-extra-bind": 2,//禁止不必要的函式繫結 "no-extra-boolean-cast": 2,//禁止不必要的bool轉換 "no-extra-parens": 2,//禁止非必要的括號 "no-extra-semi": 2,//禁止多餘的冒號 "no-fallthrough": 1,//禁止switch穿透 "no-floating-decimal": 2,//禁止省略浮點數中的0 .5 3. "no-func-assign": 2,//禁止重複的函式宣告 "no-implicit-coercion": 1,//禁止隱式轉換 "no-implied-eval": 2,//禁止使用隱式eval "no-inline-comments": 0,//禁止行內備註 "no-inner-declarations": [2, "functions"],//禁止在塊語句中使用宣告(變數或函式) "no-invalid-regexp": 2,//禁止無效的正規表示式 "no-invalid-this": 2,//禁止無效的this,只能用在構造器,類,物件字面量 "no-irregular-whitespace": 2,//不能有不規則的空格 "no-iterator": 2,//禁止使用__iterator__ 屬性 "no-label-var": 2,//label名不能與var宣告的變數名相同 "no-labels": 2,//禁止標籤宣告 "no-lone-blocks": 2,//禁止不必要的巢狀塊 "no-lonely-if": 2,//禁止else語句內只有if語句 "no-loop-func": 1,//禁止在迴圈中使用函式(如果沒有引用外部變數不形成閉包就可以) "no-mixed-requires": [0, false],//宣告時不能混用宣告型別 "no-mixed-spaces-and-tabs": [2, false],//禁止混用tab和空格 "linebreak-style": [0, "windows"],//換行風格 "no-multi-spaces": 1,//不能用多餘的空格 "no-multi-str": 2,//字串不能用\換行 "no-multiple-empty-lines": [1, {"max": 2}],//空行最多不能超過2行 "no-native-reassign": 2,//不能重寫native物件 "no-negated-in-lhs": 2,//in 運運算元的左邊不能有! "no-nested-ternary": 0,//禁止使用巢狀的三目運算 "no-new": 1,//禁止在使用new構造一個例項後不賦值 "no-new-func": 1,//禁止使用new Function "no-new-object": 2,//禁止使用new Object() "no-new-require": 2,//禁止使用new require "no-new-wrappers": 2,//禁止使用new建立包裝例項,new String new Boolean new Number "no-obj-calls": 2,//不能呼叫內建的全域性物件,比如Math() JSON() "no-octal": 2,//禁止使用八進位制數字 "no-octal-escape": 2,//禁止使用八進位制轉義序列 "no-param-reassign": 2,//禁止給引數重新賦值 "no-path-concat": 0,//node中不能使用__dirname或__filename做路徑拼接 "no-plusplus": 0,//禁止使用++,-- "no-process-env": 0,//禁止使用process.env "no-process-exit": 0,//禁止使用process.exit() "no-proto": 2,//禁止使用__proto__屬性 "no-redeclare": 2,//禁止重複宣告變數 "no-regex-spaces": 2,//禁止在正規表示式字面量中使用多個空格 /foo bar/ "no-restricted-modules": 0,//如果禁用了指定模組,使用就會報錯 "no-return-assign": 1,//return 語句中不能有賦值表示式 "no-script-url": 0,//禁止使用javascript:void(0) "no-self-compare": 2,//不能比較自身 "no-sequences": 0,//禁止使用逗號運運算元 "no-shadow": 2,//外部作用域中的變數不能與它所包含的作用域中的變數或引數同名 "no-shadow-restricted-names": 2,//嚴格模式中規定的限制識別符號不能作為宣告時的變數名使用 "no-spaced-func": 2,//函式呼叫時 函式名與()之間不能有空格 "no-sparse-arrays": 2,//禁止稀疏陣列, [1,,2] "no-sync": 0,//nodejs 禁止同步方法 "no-ternary": 0,//禁止使用三目運運算元 "no-trailing-spaces": 1,//一行結束後面不要有空格 "no-this-before-super": 0,//在呼叫super()之前不能使用this或super "no-throw-literal": 2,//禁止丟擲字面量錯誤 throw "error"; "no-undef": 1,//不能有未定義的變數 "no-undef-init": 2,//變數初始化時不能直接給它賦值為undefined "no-undefined": 2,//不能使用undefined "no-unexpected-multiline": 2,//避免多行表示式 "no-underscore-dangle": 1,//識別符號不能以_開頭或結尾 "no-unneeded-ternary": 2,//禁止不必要的巢狀 var isYes = answer === 1 ? true : false; "no-unreachable": 2,//不能有無法執行的程式碼 "no-unused-expressions": 2,//禁止無用的表示式 "no-unused-vars": [2, {"vars": "all", "args": "after-used"}],//不能有宣告後未被使用的變數或引數 "no-use-before-define": 2,//未定義前不能使用 "no-useless-call": 2,//禁止不必要的call和apply "no-void": 2,//禁用void運運算元 "no-var": 0,//禁用var,用let和const代替 "no-warning-comments": [1, { "terms": ["todo", "fixme", "xxx"], "location": "start" }],//不能有警告備註 "no-with": 2,//禁用with "array-bracket-spacing": [2, "never"],//是否允許非空陣列裡面有多餘的空格 "arrow-parens": 0,//箭頭函式用小括號括起來 "arrow-spacing": 0,//=>的前/後括號 "accessor-pairs": 0,//在物件中使用getter/setter "block-scoped-var": 0,//塊語句中使用var "brace-style": [1, "1tbs"],//大括號風格 "callback-return": 1,//避免多次呼叫回撥什麼的 "camelcase": 2,//強制駝峰法命名 "comma-dangle": [2, "never"],//物件字面量項尾不能有逗號 "comma-spacing": 0,//逗號前後的空格 "comma-style": [2, "last"],//逗號風格,換行時在行首還是行尾 "complexity": [0, 11],//迴圈複雜度 "computed-property-spacing": [0, "never"],//是否允許計算後的鍵名什麼的 "consistent-return": 0,//return 後面是否允許省略 "consistent-this": [2, "that"],//this別名 "constructor-super": 0,//非派生類不能呼叫super,派生類必須呼叫super "curly": [2, "all"],//必須使用 if(){} 中的{} "default-case": 2,//switch語句最後必須有default "dot-location": 0,//物件訪問符的位置,換行的時候在行首還是行尾 "dot-notation": [0, { "allowKeywords": true }],//避免不必要的方括號 "eol-last": 0,//檔案以單一的換行符結束 "eqeqeq": 2,//必須使用全等 "func-names": 0,//函式表示式必須有名字 "func-style": [0, "declaration"],//函式風格,規定只能使用函式宣告/函式表示式 "generator-star-spacing": 0,//生成器函式*的前後空格 "guard-for-in": 0,//for in迴圈要用if語句過濾 "handle-callback-err": 0,//nodejs 處理錯誤 "id-length": 0,//變數名長度 "indent": [2, 4],//縮排風格 "init-declarations": 0,//宣告時必須賦初值 "key-spacing": [0, { "beforeColon": false, "afterColon": true }],//物件字面量中冒號的前後空格 "lines-around-comment": 0,//行前/行後備注 "max-depth": [0, 4],//巢狀塊深度 "max-len": [0, 80, 4],//字串最大長度 "max-nested-callbacks": [0, 2],//回撥巢狀深度 "max-params": [0, 3],//函式最多隻能有3個引數 "max-statements": [0, 10],//函式內最多有幾個宣告 "new-cap": 2,//函式名首行大寫必須使用new方式呼叫,首行小寫必須用不帶new方式呼叫 "new-parens": 2,//new時必須加小括號 "newline-after-var": 2,//變數宣告後是否需要空一行 "object-curly-spacing": [0, "never"],//大括號內是否允許不必要的空格 "object-shorthand": 0,//強制物件字面量縮寫語法 "one-var": 1,//連續宣告 "operator-assignment": [0, "always"],//賦值運運算元 += -=什麼的 "operator-linebreak": [2, "after"],//換行時運運算元在行尾還是行首 "padded-blocks": 0,//塊語句內行首行尾是否要空行 "prefer-const": 0,//首選const "prefer-spread": 0,//首選展開運算 "prefer-reflect": 0,//首選Reflect的方法 "quotes": [1, "single"],//引號型別 `` "" '' "quote-props":[2, "always"],//物件字面量中的屬性名是否強制雙引號 "radix": 2,//parseInt必須指定第二個引數 "id-match": 0,//命名檢測 "require-yield": 0,//生成器函式必須有yield "semi": [2, "always"],//語句強制分號結尾 "semi-spacing": [0, {"before": false, "after": true}],//分號前後空格 "sort-vars": 0,//變數宣告時排序 "space-after-keywords": [0, "always"],//關鍵字後面是否要空一格 "space-before-blocks": [0, "always"],//不以新行開始的塊{前面要不要有空格 "space-before-function-paren": [0, "always"],//函式定義時括號前面要不要有空格 "space-in-parens": [0, "never"],//小括號裡面要不要有空格 "space-infix-ops": 0,//中綴運運算元周圍要不要有空格 "space-return-throw-case": 2,//return throw case後面要不要加空格 "space-unary-ops": [0, { "words": true, "nonwords": false }],//一元運運算元的前/後要不要加空格 "spaced-comment": 0,//註釋風格要不要有空格什麼的 "strict": 2,//使用嚴格模式 "use-isnan": 2,//禁止比較時使用NaN,只能用isNaN() "valid-jsdoc": 0,//jsdoc規則 "valid-typeof": 2,//必須使用合法的typeof的值 "vars-on-top": 2,//var必須放在作用域頂部 "wrap-iife": [2, "inside"],//立即執行函式表示式的小括號風格 "wrap-regex": 0,//正規表示式字面量用小括號包起來 "yoda": [2, "never"]//禁止尤達條件
.gitignore
上傳github時忽略的檔案或資料夾
babel.config.js
Babel是一個JS編譯器,主要是將ES5版本的程式碼轉換為向後相容的JS語法,以便能夠執行在當前和舊版本的瀏覽器或其他環境中。
Vue專案中普遍使用ES6語法,若要求相容低版本瀏覽器,就需要引入Babel,將ES6轉換為ES5。package.json
專案描述即依賴
name:專案名稱,不能以.或者_開頭,不能包含大寫字母
version:語義化版本
專案版本號遵循大版本、次要版本、小版本
版本格式:主版本號.次版本號.修訂號,版本號遞增規則如下:主版本號:當你做了不相容的 API 修改, 次版本號:當你做了向下相容的功能性新增, 修訂號:當你做了向下相容的問題修正。 123
private:true //是否私有
scripts
scripts中的子項即是我們在控制檯執行的指令碼的縮寫
“serve”: “vue-cli-service serve”,
“build”: “vue-cli-service build”,
“lint”: “vue-cli-service lint”dependencies(專案依賴庫)
dependencies:在安裝時則使用–save則寫入到dependencies。專案執行時用到
devDependencies(開發依賴庫)
安裝時使用 --save-dev將寫入到devDependencies
gitHooks
lint-staged
package-lock.json
版本管理使用的檔案
README.md
專案說明檔案
Vue Loader
一、vue-loader作用:
解析和轉換.vue檔案。提取出其中的邏輯程式碼 script,樣式程式碼style,以及HTML 模板template,再分別把他們交給對應的loader去處理
二、用途
js可以寫es6,style樣式可以寫scss或less、template可以加jade等
三、
css-loader:載入由vue-loader提取出的CSS程式碼
vue-template-compiler:把vue-loader提取出的HTML模板編譯成可執行的jacascript程式碼想了解詳細自己看官網咖:https://vue-loader.vuejs.org/zh/
Vue Router
1. 什麼是Vue Router
Vue Router 是 Vue.js (opens new window)官方的路由管理器。它和 Vue.js 的核心深度整合,讓構建單頁面應用變得易如反掌。包含的功能有:
- 巢狀的路由/檢視表
- 模組化的、基於元件的路由配置
- 路由引數、查詢、萬用字元
- 基於 Vue.js 過渡系統的檢視過渡效果
- 細粒度的導航控制
- 帶有自動啟用的 CSS class 的連結
- HTML5 歷史模式或 hash 模式,在 IE9 中自動降級
- 自定義的捲軸行為
2. 安裝Vue Router
-
安裝vue-router
npm install vue-router
-
在模組化工程中使用(因為是一個外掛,所以可以透過Vue.use()來安裝)
- 匯入路由物件,並且呼叫Vue.ues(VueRouter)
- 建立路由例項,並且傳入路由對映配置
- 在Vue例項中掛載建立路由例項
如果你建立好的專案忘記新增路由了有兩種方法解決
-
手動配置
-
首先在src目錄下建立一個router資料夾
-
在router資料夾中建立一個index.js檔案
編寫以下程式碼
// 配置路由的相關資訊 // 匯入Vue和VueRouter(必須匯入) import Vue from 'vue' import VueRouter from 'vue-router' // 1.透過Vue.ues(外掛), 安裝外掛 Vue.use(VueRouter) // 2.建立VueRouter物件 const routes = [ ] const router = new VueRouter({ // 配置路由和元件之間的應用關係 routes // (縮寫) 相當於 routes: routes }) // 3.將router物件傳入到Vue例項中 export default router
在main.js中引入
在package.json檔案中
dependencies物件中配置 "vue-router": "^3.2.0" devDependencies物件中配置 "@vue/cli-plugin-router": "^4.5.13",
-
-
透過命令配置
vue add router
3. 使用Vue Router
3.1 如何使用
- 建立路由元件
- 配置路由對映:元件和路徑對映關係
- 使用路由:透過
和
首先我們建立兩個元件Home和About
在元件中寫點內容
在router資料夾中的index.js檔案裡配置
程式碼:
// 配置路由的相關資訊 // 匯入Vue和VueRouter(必須匯入) import Vue from 'vue' import VueRouter from 'vue-router' // 匯入Home元件和About元件 import Home from '../components/Home' import About from '../components/About' // 1.透過Vue.ues(外掛), 安裝外掛 Vue.use(VueRouter) // 2.建立VueRouter物件 const routes = [ { path: '/home', // 路徑 component: Home // 對應顯示的元件名稱 }, { path: '/about', component: About } ] // 3.定義路由 const router = new VueRouter({ // 配置路由和元件之間的應用關係 routes // (縮寫) 相當於 routes: routes }) // 4.將router物件傳入到Vue例項中 export default router
在App.vue元件
程式碼
<template> <div id="app"> <!-- 使用 router-link 元件來導航. --> <!-- 透過傳入 `to` 屬性指定連結. --> <!-- <router-link> 預設會被渲染成一個 `<a>` 標籤 --> <router-link to="/home">首頁</router-link> <router-link to="/about">關於</router-link> <!-- 路由出口 --> <!-- 路由匹配到的元件將渲染在這裡 --> <router-view></router-view> </div> </template> <style scoped> </style>
3.2 redirect (重定向) 和別名
透過上面的實現我們發現,但我們開啟網頁的時候,我們的網頁沒有自動顯示首頁的內容
這時我們有一個需求,但我們開啟網頁的時候,網頁自動顯示首頁裡的內容,這時我們可以使用redirect功能
效果:
給路由起一個alias
效果:
3.3 history
透過上圖我們發現我們的網址路徑裡面有一個#號,對於強迫症人員來說非常的不友好,我們想要去掉這個#號可以透過history來去除
效果:
3.4 router-link功能
在前面的
中,我們只是使用了一個屬性: to,用於指定跳轉的路徑. 還有一些其他屬性: tag: tag可以指定
之後渲染成什麼元件,比如上面的程式碼會被渲染成一個 - 元素,而不是
效果:
replace: replace不會留下history記錄,所以指定replace的情況下,後退鍵返回不能返回到上一個頁面中
效果:
active-class:當
對應的路由匹配成功時,會自動給當前元素設定一個router-link-active的class,設定active-class可以修改預設的名稱. - 在進行高亮顯示的導航選單或者底部tabbar時,會使用到該類.
- 但是通常不會修改類的屬性,會直接使用預設的router-link-active即可.
效果:
透過設定linkActiveClass發現,路由的Class名稱被更改了
3.5 程式設計式的導航
除了使用
<router-link>
建立 a 標籤來定義導航連結,我們還可以藉助 router 的例項方法,透過編寫程式碼來實現。#
router.push(location, onComplete?, onAbort?)
注意:在 Vue 例項內部,你可以透過
$router
訪問路由例項。因此你可以呼叫this.$router.push
。想要導航到不同的 URL,則使用
router.push
方法。這個方法會向 history 棧新增一個新的記錄,所以,當使用者點選瀏覽器後退按鈕時,則回到之前的 URL。當你點選
<router-link>
時,這個方法會在內部呼叫,所以說,點選<router-link :to="...">
等同於呼叫router.push(...)
。該方法的引數可以是一個字串路徑,或者一個描述地址的物件。例如:
// 字串 router.push('home') // 物件 router.push({ path: 'home' }) // 命名的路由 router.push({ name: 'user', params: { userId: '123' }}) // 帶查詢引數,變成 /register?plan=private router.push({ path: 'register', query: { plan: 'private' }})
注意:如果提供了
path
,params
會被忽略,上述例子中的query
並不屬於這種情況。取而代之的是下面例子的做法,你需要提供路由的name
或手寫完整的帶有引數的path
:const userId = '123' router.push({ name: 'user', params: { userId }}) // -> /user/123 router.push({ path: `/user/${userId}` }) // -> /user/123 // 這裡的 params 不生效 router.push({ path: '/user', params: { userId }}) // -> /user
同樣的規則也適用於
router-link
元件的to
屬性。3.6 命名路由
有時候,透過一個名稱來標識一個路由顯得更方便一些,特別是在連結一個路由,或者是執行一些跳轉的時候。你可以在建立 Router 例項的時候,在
routes
配置中給某個路由設定名稱。要連結到一個命名路由,可以給
router-link
的to
屬性傳一個物件:這跟程式碼呼叫
router.push()
是一回事:router.push({ name: 'user', params: { userId: 123 } })
這兩種方式都會把路由導航到
/user/123
路徑。3.7 命名試圖
有時候想同時 (同級) 展示多個檢視,而不是巢狀展示,例如建立一個佈局,有
sidebar
(側導航) 和main
(主內容) 兩個檢視,這個時候命名檢視就派上用場了。你可以在介面中擁有多個單獨命名的檢視,而不是隻有一個單獨的出口。如果router-view
沒有設定名字,那麼預設為default
。<router-view class="view one"></router-view> <router-view class="view two" name="a"></router-view> <router-view class="view three" name="b"></router-view>
一個檢視使用一個元件渲染,因此對於同個路由,多個檢視就需要多個元件。確保正確使用
components
配置 (帶上 s):const router = new VueRouter({ routes: [ { path: '/', components: { default: Foo, a: Bar, b: Baz } } ] })
4. 動態路由
我們經常需要把某種模式匹配到的所有路由,全都對映到同個元件。
例如,我們有一個
User
元件,對於所有 ID 各不相同的使用者,都要使用這個元件來渲染。那麼,我們可以在vue-router
的路由路徑中使用“動態路徑引數”(dynamic segment) 來達到這個效果:現在呢,像
/user/foo
和/user/bar
都將對映到相同的路由。一個“路徑引數”使用冒號
:
標記。當匹配到一個路由時,引數值會被設定到this.$route.params
,可以在每個元件內使用。於是,我們可以更新User
的模板,輸出當前使用者的 ID:效果:
5. 路由懶載入
官方給出瞭解釋:
- 當打包構建應用時,Javascript包會變得非常大,影響頁面載入。
- 如果我們能把不同路由對應的元件分割成不同的程式碼塊,然後當路由被訪問的時候才載入對應元件,這樣就更加高效了
官方在說什麼呢?
- 首先,我們知道路由中通常會定義很多不同的頁面.
- 這個頁面最後被打包在哪裡呢?一般情況下,是放在一個js檔案中.口但是,頁面這麼多放在一個js檔案中,必然會造成這個頁面非常的大.
- 如果我們一次性從伺服器請求下來這個頁面,可能需要花費一定的時間,甚至使用者的電腦上還出現了短暫空白的情況.
- 如何避免這種情況呢?使用路由懶載入就可以了.
路由懶載入做了什麼?
- 路由懶載入的主要作用就是將路由對應的元件打包成一個個的js程式碼塊.
- 只有在這個路由被訪問到的時候,才載入對應的元件
書寫方式:
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue') const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue') const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
6. 巢狀路由
6.1 什麼是路由巢狀?
路由巢狀,就相當於我們寫word的一級目錄和二級目錄,一級目錄可以包含多個二級目錄
6.2 使用方法
-
首先建立兩個components,並且寫上點內容
-
在router檔案中的index.js檔案中,某一個路由裡新增一個children,在children裡配置相關路由
-
在要巢狀的父模板裡面去配置
和
效果:
7. 傳遞引數
傳遞引數主要有兩種型別:params和query
params的型別:
- 配置路由格式: /router/:id
- 傳遞的方式: 在path後面跟上對應的值
- 傳遞後形成的路徑: /router/123, /router/abc
query的型別:
- 配置路由格式:/router,也就是普通配置
- 傳遞的方式:物件中使用query的key作為傳遞方式
- 傳遞後形成的路徑: /router?id=123,/router?id=abc
使用方法:
-
定義一個component,並且寫點內容
-
在router檔案中的index.js檔案中配置一個路由
-
在App.vue中配置
效果圖:
8 導航守衛
導航守衛就是路由跳轉過程中的一些鉤子函式,再直白點路由跳轉是一個大的過程,這個大的過程分為跳轉前中後等等細小的過程,在每一個過程中都有一函式,這個函式能讓你操作一些其他的事兒的時機,這就是導航守衛。
用大白話說:簡單的說就是保安,這個小區的人讓進,不是這個小區的到我這裡登記。
導航守衛的作用:監聽路由的進入和離開
記住這張圖的順序,對後面的學習有所幫助
8.1 全域性前置守衛
router.beforeEach((to, from, next) => { // ... })
每個守衛方法接收三個引數:
to: Route
: 即將要進入的目標 路由物件from: Route
: 當前導航正要離開的路由next: Function
: 一定要呼叫該方法來 resolve 這個鉤子。執行效果依賴next
方法的呼叫引數。
我們透過一個案例來進行講解,我們想點選不同元件,瀏覽器的標題會有所改變,我們可以使用
created()
來進行實現,如果不知道created()
去看一下之前的筆記生命週期圖片我們發現透過這種方式實現,有點麻煩,加入我們有1w個元件都要實現功能,那麼我們就要在1w個元件當中去新增這麼一行程式碼,非常的不舒服
所以我們可以使用全域性前置守衛
透過上述程式碼你會發現一個問題,我們首頁的標題是undefined,出現這種情況,是以為我們在這個元件中設定了路由巢狀,導致
meta
:{}物件裡是一個空物件。但我們發現matched
這個物件中有一個meta
物件,並且有值,我們可以透過這個物件就行賦值所以我們可以這樣實現
還有些其他守衛,可以自己去看官網作者寫筆記寫吐了:
https://router.vuejs.org/zh/guide/advanced/navigation-guards.html
Vuex
1. 什麼是Vuex
VueX
是適用於在Vue
專案開發時使用的狀態管理工具。試想一下,如果在一個專案開發中頻繁的使用元件傳參的方式來同步data
中的值,一旦專案變得很龐大,管理和維護這些值將是相當棘手的工作。為此,Vue
為這些被多個元件頻繁使用的值提供了一個統一管理的工具——VueX
。在具有VueX
的Vue專案中,我們只需要把這些值定義在VueX中,即可在整個Vue專案的元件中使用。2. 安裝Vuex
-
Npm安裝Vuex
npm install vuex --save
-
在專案的根目錄下新增一個
store
資料夾,在該資料夾內建立index.js此時你的專案的
src
資料夾應當是這樣的│ App.vue │ main.js │ ├─assets │ logo.png │ ├─components │ HelloWorld.vue │ ├─router │ index.js │ └─store index.js
如果你建立好的專案忘記新增路由了有兩種方法解決
-
手動配置
-
首先在src目錄下建立一個store資料夾,在store檔案中建立index.js檔案
-
在index.js檔案中配置相關資訊
import Vue from 'vue' import Vuex from 'vuex' //掛載Vuex Vue.use(Vuex) //建立VueX物件 const store = new Vuex.Store({ state:{ //存放的鍵值對就是所要管理的狀態 name:'helloVueX' } }) // 建立Vuex物件 const store = new Vuex.Store({ state: { // 存放的鍵值對就是要管理的狀態 name: 'HelloVuex' }, mutations: { }, actions: { }, modules: { } }) export default store
-
將store掛載到Vue例項中 ,開啟main.js檔案
import Vue from 'vue' import App from './App.vue' import router from './router' // 匯入store import store from './store' Vue.config.productionTip = false new Vue({ router, store, // 掛載到Vue例項中 render: h => h(App) }).$mount('#app')
-
-
透過命令配置
vue add vuex
3. 狀態管理模式
這個狀態自管理應用包含以下幾個部分:
- state,驅動應用的資料來源;
- view,以宣告方式將 state 對映到檢視;
- actions,響應在 view 上的使用者輸入導致的狀態變化。
以下是一個表示“單向資料流”理念的簡單示意:
但是,當我們的應用遇到多個元件共享狀態時,單向資料流的簡潔性很容易被破壞:
- 多個檢視依賴於同一狀態。
- 來自不同檢視的行為需要變更同一狀態。
對於問題一,傳參的方法對於多層巢狀的元件將會非常繁瑣,並且對於兄弟元件間的狀態傳遞無能為力。對於問題二,我們經常會採用父子元件直接引用或者透過事件來變更和同步狀態的多份複製。以上的這些模式非常脆弱,通常會導致無法維護的程式碼。
因此,我們為什麼不把元件的共享狀態抽取出來,以一個全域性單例模式管理呢?在這種模式下,我們的元件樹構成了一個巨大的“檢視”,不管在樹的哪個位置,任何元件都能獲取狀態或者觸發行為!
透過定義和隔離狀態管理中的各種概念並透過強制規則維持檢視和狀態間的獨立性,我們的程式碼將會變得更結構化且易維護。
4. Vuex的使用
我們現在Vuex設定一個鍵值
開啟App.vue
效果:
我們也可以使用methods來獲取counter
注意,請不要在此處更改
state
中的狀態的值,後文中將會說明5. VueX中的核心內容
在VueX物件中,其實不止有
state
,還有用來操作state
中資料的方法集,以及當我們需要對state
中的資料需要加工的方法集等等成員。成員列表:
- state: 存放狀態
- mutations: state成員操作
- getters: 加工state成員給外界
- actions: 非同步操作
- modules: 模組化狀態管理
Vuex的工作流程
首先,
Vue
元件如果呼叫某個VueX
的方法過程中需要向後端請求時或者說出現非同步操作時,需要dispatch
VueX中actions
的方法,以保證資料的同步。可以說,action
的存在就是為了讓mutations
中的方法能在非同步操作中起作用。如果沒有非同步操作,那麼我們就可以直接在元件內提交狀態中的
Mutations
中自己編寫的方法來達成對state
成員的操作。注意,前面有提到,不建議在元件中直接對state
中的成員進行操作,這是因為直接修改(例如:this.$store.state.name = 'hello'
)的話不能被VueDevtools
所監控到。最後被修改後的state成員會被渲染到元件的原位置當中去
5.1 State
這個前面我們已經演示過了,這裡就不在演示了
5.2 Getters
可以對state中的成員加工後傳遞給外界
Getters中的方法有兩個預設引數
- state 當前VueX物件中的狀態物件
- getters 當前getters物件,用於將getters下的其他getter拿來用
使用方法:
效果:
5.3 Mutations
mutations
是操作state
資料的方法的集合,比如對該資料的修改、增加、刪除等等。5.3.1 Mutations使用方法
mutations
方法都有預設的形參:([state] {[payload]})
state
是當前VueX
物件中的state
payload
是該方法在被呼叫時傳遞引數使用的
例如,我們編寫了一個方法,當被執行時,能把下列中的counter值修改為
'State'
而在元件中,我們需要這樣去呼叫這個
mutation
——例如在App.vue的某個method
中:5.3.2 Mutation傳值
在實際生產過程中,會遇到需要在提交某個
mutation
時需要攜帶一些引數給方法使用。可以傳遞單個資料
效果:
也可以傳遞過個資料
另一種提交方式:
5.3.3 增刪State中的成員
為了配合Vue的響應式資料,我們在
Mutations
的方法中,應當使用Vue提供的方法來進行操作。如果使用delete
或者xx.xx = xx
的形式去刪或增,則Vue不能對資料進行實時響應。Vue.set 為某個物件設定成員的值,若不存在則新增
例如對state物件中新增一個age成員
Vue.set(state,"age",15)
Vue.delete 刪除成員
將剛剛新增的age成員刪除
Vue.delete(state,'age')
5.4 Actions
由於直接在
mutation
方法中進行非同步操作,將會引起資料失效。所以提供了Actions來專門進行非同步操作,最終提交mutation
方法。Actions
中的方法有兩個預設引數context
上下文(相當於箭頭函式中的this)物件payload
掛載引數- 由於
setTimeout
是非同步操作,所以需要使用actions
actions:{ aEdit(context,payload){ setTimeout(()=>{ context.commit('edit',payload) },2000) } }
在元件中呼叫:
this.$store.dispatch('aEdit',{age:15})
改進:
由於是非同步操作,所以我們可以為我們的非同步操作封裝為一個
Promise
物件aEdit(context,payload){ return new Promise((resolve,reject)=>{ setTimeout(()=>{ context.commit('edit',payload) resolve() },2000) }) }
5.5 modules
當專案龐大,狀態非常多時,可以採用模組化管理模式。Vuex 允許我們將 store 分割成模組(module)。每個模組擁有自己的
state、mutation、action、getter
、甚至是巢狀子模組——從上至下進行同樣方式的分割。modules:{ a:{ state:{}, getters:{}, .... } }
元件內呼叫模組a的狀態:
this.$store.state.a
而提交或者
dispatch
某個方法和以前一樣,會自動執行所有模組內的對應type
的方法:this.$store.commit('editKey') this.$store.dispatch('aEditKey')
模組的細節
-
模組中
mutations
和getters
中的方法接受的第一個引數是自身區域性模組內部的state
modules:{ a:{ state:{key:5}, mutations:{ editKey(state){ state.key = 9 } }, .... } }
-
getters
中方法的第三個引數是根節點狀態modules:{ a:{ state:{key:5}, getters:{ getKeyCount(state,getter,rootState){ return rootState.key + state.key } }, .... } }
-
actions
中方法獲取區域性模組狀態是context.state
,根節點狀態是context.rootState
modules:{ a:{ state:{key:5}, actions:{ aEidtKey(context){ if(context.state.key === context.rootState.key){ context.commit('editKey') } } }, .... } }
5.6 規範目錄結構
如果把整個
store
都放在index.js
中是不合理的,所以需要拆分。比較合適的目錄格式如下:store:. │ actions.js │ getters.js │ index.js │ mutations.js │ mutations_type.js ##該項為存放mutaions方法常量的檔案,按需要可加入 │ └─modules Astore.js
對應的內容存放在對應的檔案中,和以前一樣,在
index.js
中存放並匯出store
。state
中的資料儘量放在index.js
中。而modules
中的Astore
區域性模組狀態如果多的話也可以進行細分。axios
1. 什麼是axios
Axios 是一個基於 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中。
特性
- 從瀏覽器中建立 XMLHttpRequests
- 從 node.js 建立 http 請求
- 支援 Promise API
- 攔截請求和響應
- 轉換請求資料和響應資料
- 取消請求
- 自動轉換 JSON 資料
- 客戶端支援防禦 XSRF
2. 安裝
使用 npm:
npm install axios
使用 bower:
bower install axios
使用 cdn:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
3. 使用方法
執行
GET
請求// 為給定 ID 的 user 建立請求 axios.get('/user?ID=12345') .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); }); // 上面的請求也可以這樣做 axios.get('/user', { params: { ID: 12345 } }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });
執行
POST
請求axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });
執行多個併發請求
function getUserAccount() { return axios.get('/user/12345'); } function getUserPermissions() { return axios.get('/user/12345/permissions'); } axios.all([getUserAccount(), getUserPermissions()]) .then(axios.spread(function (acct, perms) { // 兩個請求現在都執行完成 }));