樂優商場開發第五天筆記
學習目標
-
會建立Vue例項,知道Vue的常見屬性
-
會使用Vue的生命週期的鉤子函式
-
會使用vue常見指令
-
會使用vue計算屬性和watch監控
-
會編寫Vue元件
-
掌握元件間通訊
-
瞭解vue-router使用
-
瞭解webpack使用
-
會使用vue-cli搭建專案
0.前言
前幾天我們已經對後端的技術棧有了初步的瞭解、並且已經搭建了整個後端微服務的平臺。接下來要做的事情就是功能開發了。但是沒有前端頁面,我們肯定無從下手,因此今天我們就要來了解一下前端的一些技術,完成前端頁面搭建。
先聊一下前端開發模式的發展。
靜態頁面
最初的網頁以HTML為主,是純靜態的網頁。網頁是隻讀的,資訊流只能從服務端到客戶端單向流通。開發人員也只關心頁面的樣式和內容即可。
非同步重新整理,操作DOM
1995年,網景工程師Brendan Eich 花了10天時間設計了JavaScript語言.
隨著JavaScript的誕生,我們可以操作頁面的DOM元素及樣式,頁面有了一些動態的效果,但是依然是以靜態為主。
ajax盛行:
-
2005年開始,ajax逐漸被前端開發人員所重視,因為不用重新整理頁面就可以更新頁面的資料和渲染效果。
-
此時的開發人員不僅僅要編寫HTML樣式,還要懂ajax與後端互動,然後通過JS操作Dom元素來實現頁面動態效果
MVVM,關注模型和檢視
2008年,google的Chrome釋出,隨後就以極快的速度佔領市場,超過IE成為瀏覽器市場的主導者。
2009年,Ryan Dahl在谷歌的Chrome V8引擎基礎上,打造了基於事件迴圈的非同步IO框架:Node.js。
-
基於事件迴圈的非同步IO
-
單執行緒執行,避免多執行緒的變數同步問題
-
JS可以編寫後臺程式碼,前後臺統一程式語言
node.js的偉大之處不在於讓JS邁向了後端開發,而是構建了一個龐大的生態系統。
2010年,NPM作為node.js的包管理系統首次釋出,開發人員可以遵循Common.js規範來編寫Node.js模組,然後釋出到NPM上供其他開發人員使用。目前已經是世界最大的包模組管理系統。
隨後,在node的基礎上,湧現出了一大批的前端框架:
MVVM模式
-
M:即Model,模型,包括資料和一些基本操作
-
V:即View,檢視,頁面渲染結果
-
VM:即View-Model,模型與檢視間的雙向操作(無需開發人員干涉)
在MVVM之前,開發人員從後端獲取需要的資料模型,然後要通過DOM操作Model渲染到View中。而後當用戶操作檢視,我們還需要通過DOM獲取View中的資料,然後同步到Model中。
而MVVM中的VM要做的事情就是把DOM操作完全封裝起來,開發人員不用再關心Model和View之間是如何互相影響的:
-
只要我們Model發生了改變,View上自然就會表現出來。
-
當用戶修改了View,Model中的資料也會跟著改變。
把開發人員從繁瑣的DOM操作中解放出來,把關注點放在如何操作Model上。
而我們今天要學習的,就是一款MVVM模式的框架:Vue
1.認識Vue
Vue (讀音 /vjuː/,類似於 view) 是一套用於構建使用者介面的漸進式框架。與其它大型框架不同的是,Vue 被設計為可以自底向上逐層應用。Vue 的核心庫只關注檢視層,不僅易於上手,還便於與第三方庫或既有專案整合。另一方面,當與現代化的工具鏈以及各種支援類庫結合使用時,Vue 也完全能夠為複雜的單頁應用提供驅動。
前端框架三巨頭:Vue.js、React.js、AngularJS,vue.js以其輕量易用著稱,vue.js和React.js發展速度最快,AngularJS還是老大。
參考:https://cn.vuejs.org/v2/guide/
Git地址:https://github.com/vuejs
尤雨溪,Vue.js 創作者,Vue Technology創始人,致力於Vue的研究開發。
2.Node和NPM
前面說過,NPM是Node提供的模組管理工具,可以非常方便的下載安裝很多前端框架,包括Jquery、AngularJS、VueJs都有。為了後面學習方便,我們先安裝node及NPM工具。
2.1.下載Node.js
下載地址:https://nodejs.org/en/
推薦下載LTS版本。
課程中採用的是8.11.3版本。也是目前最新的。大家自行下載或者使用課前資料中提供的安裝包。然後下一步安裝即可。
完成以後,在控制檯輸入:
node -v
看到版本資訊:
2.2.NPM
Node自帶了NPM了,在控制檯輸入npm -v
檢視:
npm預設的倉庫地址是在國外網站,速度較慢,建議大家設定到淘寶映象。但是切換映象是比較麻煩的。推薦一款切換映象的工具:nrm
我們首先安裝nrm,這裡-g
代表全域性安裝。可能需要一點兒時間
npm install nrm -g
然後通過nrm ls
命令檢視npm的倉庫列表,帶*的就是當前選中的映象倉庫:
通過nrm use taobao
來指定要使用的映象源:
然後通過nrm test npm
來測試速度:
注意:
-
有教程推薦大家使用cnpm命令,但是使用發現cnpm有時會有bug,不推薦。
-
安裝完成請一定要重啟下電腦!!!
-
安裝完成請一定要重啟下電腦!!!
-
安裝完成請一定要重啟下電腦!!!
3.快速入門
接下來,我們快速領略下vue的魅力
3.1.建立工程
建立一個新的空工程:
然後新建一個module:
選中static web,靜態web專案:
位置資訊:
3.2.安裝vue
3.2.1.下載安裝
下載地址:https://github.com/vuejs/vue
可以下載2.5.16版本https://github.com/vuejs/vue/archive/v2.5.16.zip
下載解壓,得到vue.js檔案。
3.2.2.使用CDN
或者也可以直接使用公共的CDN服務:
<!-- 開發環境版本,包含了用幫助的命令列警告 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
或者:
<!-- 生產環境版本,優化了尺寸和速度 -->
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
3.2.3.推薦npm安裝
在idea的左下角,有個Terminal按鈕,點選開啟控制檯:
進入hello-vue目錄,先輸入:npm init -y
進行初始化
安裝Vue,輸入命令:npm install vue --save
然後就會在hello-vue目錄發現一個node_modules目錄,並且在下面有一個vue目錄。
node_modules是通過npm安裝的所有模組的預設位置。
3.3.vue入門案例
3.3.1.HTML模板
在hello-vue目錄新建一個HTML
在hello.html中,我們編寫一段簡單的程式碼:
h2中要輸出一句話:xx 非常帥。前面的xx是要渲染的資料。
3.3.2.vue宣告式渲染
然後我們通過Vue進行渲染:
<body>
<div id="app">
<h2>{{name}},非常帥!!!</h2>
</div>
</body>
<script src="node_modules/vue/dist/vue.js" ></script>
<script>
// 建立vue例項
var app = new Vue({
el:"#app", // el即element,該vue例項要渲染的頁面元素
data:{ // 渲染頁面需要的資料
name: "峰哥"
}
});
</script>
-
首先通過 new Vue()來建立Vue例項
-
然後建構函式接收一個物件,物件中有一些屬性:
-
el:是element的縮寫,通過id選中要渲染的頁面元素,本例中是一個div
-
data:資料,資料是一個物件,裡面有很多屬性,都可以渲染到檢視中
-
name:這裡我們指定了一個name屬性
-
-
-
頁面中的
h2
元素中,我們通過{{name}}的方式,來渲染剛剛定義的name屬性。
開啟頁面檢視效果:
更神奇的在於,當你修改name屬性時,頁面會跟著變化:
3.3.3.雙向繫結
我們對剛才的案例進行簡單修改:
<body>
<div id="app">
<input type="text" v-model="num">
<h2>
{{name}},非常帥!!!有{{num}}位女神為他著迷。
</h2>
</div>
</body>
<script src="node_modules/vue/dist/vue.js" ></script>
<script>
// 建立vue例項
var app = new Vue({
el: "#app", // el即element,該vue例項要渲染的頁面元素
data: { // 渲染頁面需要的資料
name: "峰哥",
num: 5
}
});
</script>
-
我們在data添加了新的屬性:
num
-
在頁面中有一個
input
元素,通過v-model
與num
進行繫結。 -
同時通過
{{num}}
在頁面輸出
效果:
我們可以觀察到,輸入框的變化引起了data中的num的變化,同時頁面輸出也跟著變化。
-
input與num繫結,input的value值變化,影響到了data中的num值
-
頁面
{{num}}
與資料num繫結,因此num值變化,引起了頁面效果變化。
沒有任何dom操作,這就是雙向繫結的魅力。
3.3.4.事件處理
我們在頁面新增一個按鈕:
<button v-on:click="num++">點我</button>
-
這裡用
v-on
指令繫結點選事件,而不是普通的onclick
,然後直接操作num -
普通click是無法直接操作num的。
效果:
4.Vue例項
4.1.建立Vue例項
每個 Vue 應用都是通過用 Vue
函式建立一個新的 Vue 例項開始的:
var vm = new Vue({
// 選項
})
在建構函式中傳入一個物件,並且在物件中宣告各種Vue需要的資料和方法,包括:
-
el
-
data
-
methods
等等
接下來我們一 一介紹。
4.2.模板或元素
每個Vue例項都需要關聯一段Html模板,Vue會基於此模板進行檢視渲染。
我們可以通過el屬性來指定。
例如一段html模板:
<div id="app">
</div>
然後建立Vue例項,關聯這個div
var vm = new Vue({
el:"#app"
})
這樣,Vue就可以基於id為app
的div元素作為模板進行渲染了。在這個div範圍以外的部分是無法使用vue特性的。
4.3.資料
當Vue例項被建立時,它會嘗試獲取在data中定義的所有屬性,用於檢視的渲染,並且監視data中的屬性變化,當data發生改變,所有相關的檢視都將重新渲染,這就是“響應式“系統。
html:
<div id="app">
<input type="text" v-model="name"/>
</div>
js:
var vm = new Vue({
el:"#app",
data:{
name:"劉德華"
}
})
-
name的變化會影響到
input
的值 -
input中輸入的值,也會導致vm中的name發生改變
4.4.方法
Vue例項中除了可以定義data屬性,也可以定義方法,並且在Vue例項的作用範圍內使用。
html:
<div id="app">
{{num}}
<button v-on:click="add">加</button>
</div>
js:
var vm = new Vue({
el:"#app",
data:{
num: 0
},
methods:{
add:function(){
// this代表的當前vue例項
this.num++;
}
}
})
4.5.生命週期鉤子
4.5.1.生命週期
每個 Vue 例項在被建立時都要經過一系列的初始化過程 :建立例項,裝載模板,渲染模板等等。Vue為生命週期中的每個狀態都設定了鉤子函式(監聽函式)。每當Vue例項處於不同的生命週期時,對應的函式就會被觸發呼叫。
生命週期:
4.5.2.鉤子函式
beforeCreated:我們在用Vue時都要進行例項化,因此,該函式就是在Vue例項化時呼叫,也可以將他理解為初始化函式比較方便一點,在Vue1.0時,這個函式的名字就是init。
created:在建立例項之後進行呼叫。
beforeMount:頁面載入完成,沒有渲染。如:此時頁面還是{{name}}
mounted:我們可以將他理解為原生js中的window.onload=function({.,.}),或許大家也在用jquery,所以也可以理解為jquery中的$(document).ready(function(){….}),他的功能就是:在dom文件渲染完畢之後將要執行的函式,該函式在Vue1.0版本中名字為compiled。 此時頁面中的{{name}}已被渲染成峰哥
beforeDestroy:該函式將在銷燬例項前進行呼叫 。
destroyed:改函式將在銷燬例項時進行呼叫。
beforeUpdate:元件更新之前。
updated:元件更新之後。
例如:created代表在vue例項建立後;
我們可以在Vue中定義一個created函式,代表這個時期的鉤子函式:
// 建立vue例項
var app = new Vue({
el: "#app", // el即element,該vue例項要渲染的頁面元素
data: { // 渲染頁面需要的資料
name: "峰哥",
num: 5
},
methods: {
add: function(){
this.num--;
}
},
created: function () {
this.num = 100;
}
});
結果:
4.5.3.this
我們可以看下在vue內部的this變數是誰,我們在created的時候,列印this
methods: {
add: function(){
this.num--;
console.log(this);
}
},
控制檯的輸出:
5.指令
什麼是指令?
指令 (Directives) 是帶有 v-
字首的特殊特性。指令特性的預期值是:單個 JavaScript 表示式。指令的職責是,當表示式的值改變時,將其產生的連帶影響,響應式地作用於 DOM。
例如我們在入門案例中的v-on,代表繫結事件。
5.1.插值表示式
5.1.1.花括號
格式:
{{表示式}}
說明:
-
該表示式支援JS語法,可以呼叫js內建函式(必須有返回值)
-
表示式必須有返回結果。例如 1 + 1,沒有結果的表示式不允許使用,如:var a = 1 + 1;
-
可以直接獲取Vue例項中定義的資料或函式
示例:
HTML:
<div id="app">{{name}}</div>
JS:
var app = new Vue({
el:"#app",
data:{
name:"Jack"
}
})
5.1.2.插值閃爍
使用{{}}方式在網速較慢時會出現問題。在資料未載入完成時,頁面會顯示出原始的{{}}
,載入完畢後才顯示正確資料,我們稱為插值閃爍。
我們將網速調慢一些,然後試試看剛才的案例:
重新整理頁面:
5.1.3.v-text和v-html
使用v-text和v-html指令來替代{{}}
說明:
-
v-text:將資料輸出到元素內部,如果輸出的資料有HTML程式碼,會作為普通文字輸出
-
v-html:將資料輸出到元素內部,如果輸出的資料有HTML程式碼,會被渲染
示例:
HTML:
<div id="app">
v-text:<span v-text="hello"></span> <br/>
v-html:<span v-html="hello"></span>
</div>
JS:
var vm = new Vue({
el:"#app",
data:{
hello: "<h1>大家好,我是峰哥</h1>"
}
})
效果:
並且不會出現插值閃爍,當沒有資料時,會顯示空白。
5.2.v-model
剛才的v-text和v-html可以看做是單向繫結,資料影響了檢視渲染,但是反過來就不行。接下來學習的v-model是雙向繫結,檢視(View)和模型(Model)之間會互相影響。
既然是雙向繫結,一定是在檢視中可以修改資料,這樣就限定了檢視的元素型別。目前v-model的可使用元素有:
-
input
-
select
-
textarea
-
checkbox
-
radio
-
components(Vue中的自定義元件)
基本上除了最後一項,其它都是表單的輸入項。
舉例:
html:
<div id="app">
<input type="checkbox" v-model="language" value="Java" />Java<br/>
<input type="checkbox" v-model="language" value="PHP" />PHP<br/>
<input type="checkbox" v-model="language" value="Swift" />Swift<br/>
<h1>
你選擇了:{{language.join(',')}}
</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
language: []
}
})
</script>
-
多個
CheckBox
對應一個model時,model的型別是一個數組,單個checkbox值預設是boolean型別 -
radio對應的值是input的value值
-
input
和textarea
預設對應的model是字串 -
select
單選對應字串,多選對應也是陣列
效果:
5.3.v-on
5.3.1.基本用法
v-on指令用於給頁面元素繫結事件。
語法:
v-on:事件名="js片段或函式名"
示例:
<div id="app">
<!--事件中直接寫js片段-->
<button v-on:click="num++">增加一個</button><br/>
<!--事件指定一個回撥函式,必須是Vue例項中定義的函式-->
<button v-on:click="decrement">減少一個</button><br/>
<h1>有{{num}}個女神迷戀峰哥</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
num:100
},
methods:{
decrement(){
this.num--;
}
}
})
</script>
效果:
另外,事件繫結可以簡寫,例如v-on:click='add'
可以簡寫為@click='add'
5.3.2.事件修飾符
在事件處理程式中呼叫 event.preventDefault()
或 event.stopPropagation()
是非常常見的需求。儘管我們可以在方法中輕鬆實現這點,但更好的方式是:方法只有純粹的資料邏輯,而不是去處理 DOM 事件細節。
為了解決這個問題,Vue.js 為 v-on
提供了事件修飾符。修飾符是由點開頭的指令字尾來表示的。
-
.stop
:阻止事件冒泡到父元素 -
.prevent
:阻止預設事件發生 -
.capture
:使用事件捕獲模式 -
.self
:只有元素自身觸發事件才執行。(冒泡或捕獲的都不執行) -
.once
:只執行一次
阻止預設事件
<div id="app">
<!--右擊事件,並阻止預設事件發生-->
<button v-on:contextmenu.prevent="num++">增加一個</button>
<br/>
<!--右擊事件,不阻止預設事件發生-->
<button v-on:contextmenu="decrement($event)">減少一個</button>
<br/>
<h1>有{{num}}個女神迷戀峰哥</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
num: 100
},
methods: {
decrement(ev) {
// ev.preventDefault();
this.num--;
}
}
})
</script>
效果:(右鍵“增加一個”,不會觸發預設的瀏覽器右擊事件;右鍵“減少一個”,會觸發預設的瀏覽器右擊事件)
5.3.3.按鍵修飾符
在監聽鍵盤事件時,我們經常需要檢查常見的鍵值。Vue 允許為 v-on
在監聽鍵盤事件時新增按鍵修飾符:
<!-- 只有在 `keyCode` 是 13 時呼叫 `vm.submit()` -->
<input v-on:keyup.13="submit">
記住所有的 keyCode
比較困難,所以 Vue 為最常用的按鍵提供了別名:
<!-- 同上 -->
<input v-on:keyup.enter="submit">
<!-- 縮寫語法 -->
<input @keyup.enter="submit">
全部的按鍵別名:
-
.enter
-
.tab
-
.delete
(捕獲“刪除”和“退格”鍵) -
.esc
-
.space
-
.up
-
.down
-
.left
-
.right
5.3.4.組合按鈕
可以用如下修飾符來實現僅在按下相應按鍵時才觸發滑鼠或鍵盤事件的監聽器。
-
.ctrl
-
.alt
-
.shift
例如:
<!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
5.4.v-for
遍歷資料渲染頁面是非常常用的需求,Vue中通過v-for指令來實現。
5.4.1.遍歷陣列
語法:
v-for="item in items"
-
items:要遍歷的陣列,需要在vue的data中定義好。
-
item:迭代得到的陣列元素的別名
示例
<div id="app">
<ul>
<li v-for="user in users">
{{user.name}} - {{user.gender}} - {{user.age}}
</li>
</ul>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
users:[
{name:'柳巖', gender:'女', age: 21},
{name:'峰哥', gender:'男', age: 18},
{name:'范冰冰', gender:'女', age: 24},
{name:'劉亦菲', gender:'女', age: 18},
{name:'古力娜扎', gender:'女', age: 25}
]
},
})
</script>
效果:
5.4.2.陣列角標
在遍歷的過程中,如果我們需要知道陣列角標,可以指定第二個引數:
語法
v-for="(item,index) in items"
-
items:要迭代的陣列
-
item:迭代得到的陣列元素別名
-
index:迭代到的當前元素索引,從0開始。
示例
<ul>
<li v-for="(user, index) in users">
{{index + 1}}. {{user.name}} - {{user.gender}} - {{user.age}}
</li>
</ul>
效果:
5.4.3.遍歷物件
v-for除了可以迭代陣列,也可以迭代物件。語法基本類似
語法:
v-for="value in object"
v-for="(value,key) in object"
v-for="(value,key,index) in object"
-
1個引數時,得到的是物件的屬性
-
2個引數時,第一個是屬性,第二個是鍵
-
3個引數時,第三個是索引,從0開始
示例:
<div id="app">
<ul>
<li v-for="(value, key, index) in user">
{{index + 1}}. {{key}} - {{value}}
</li>
</ul>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
user:{name:'峰哥', gender:'男', age: 18}
}
})
</script>
效果:
5.4.4.key
當 Vue.js 用 v-for
正在更新已渲染過的元素列表時,它預設用“就地複用”策略。如果資料項的順序被改變,Vue 將不會移動 DOM 元素來匹配資料項的順序, 而是簡單複用此處每個元素,並且確保它在特定索引下顯示已被渲染過的每個元素。
這個功能可以有效的提高渲染的效率。
但是要實現這個功能,你需要給Vue一些提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要為每項提供一個唯一 key
屬性。理想的 key
值是每項都有的且唯一的 id。
示例:
<ul>
<li v-for="(item,index) in items" :key=index></li>
</ul>
-
這裡使用了一個特殊語法:
:key=""
我們後面會講到,它可以讓你讀取vue中的屬性,並賦值給key屬性 -
這裡我們繫結的key是陣列的索引,應該是唯一的
5.5.v-if和v-show
5.5.1.基本使用
v-if,顧名思義,條件判斷。當得到結果為true時,所在的元素才會被渲染。
語法:
v-if="布林表示式"
示例:
<div id="app">
<button v-on:click="show = !show">點我呀</button>
<br>
<h1 v-if="show">
看到我啦?!
</h1>
<h1 v-show="show">
看到我啦?!show
</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
show: true
}
})
</script>
效果:
5.5.2.與v-for結合
當v-if和v-for出現在一起時,v-for優先順序更高。也就是說,會先遍歷,再判斷條件。
修改v-for中的案例,新增v-if:
<ul>
<li v-for="(user, index) in users" v-if="user.gender == '女'">
{{index + 1}}. {{user.name}} - {{user.gender}} - {{user.age}}
</li>
</ul>
效果:
只顯示女性使用者資訊
5.5.3.v-else
你可以使用 v-else
指令來表示 v-if
的“else 塊”:
<div id="app">
<h1 v-if="Math.random() > 0.5">
看到我啦?!if
</h1>
<h1 v-else>
看到我啦?!else
</h1>
</div>
v-else
元素必須緊跟在帶 v-if
或者 v-else-if
的元素的後面,否則它將不會被識別。
v-else-if
,顧名思義,充當 v-if
的“else-if 塊”,可以連續使用:
<div id="app">
<button v-on:click="random=Math.random()">點我呀</button><span>{{random}}</span>
<h1 v-if="random >= 0.75">
看到我啦?!if
</h1>
<h1 v-else-if="random > 0.5">
看到我啦?!if 0.5
</h1>
<h1 v-else-if="random > 0.25">
看到我啦?!if 0.25
</h1>
<h1 v-else>
看到我啦?!else
</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
random: 1
}
})
</script>
類似於 v-else
,v-else-if
也必須緊跟在帶 v-if
或者 v-else-if
的元素之後。
演示:
5.5.4.v-show
另一個用於根據條件展示元素的選項是 v-show
指令。用法大致一樣:
<h1 v-show="ok">Hello!</h1>
不同的是帶有 v-show
的元素始終會被渲染並保留在 DOM 中。v-show
只是簡單地切換元素的 CSS 屬性 display
。
示例:
<div id="app">
<!--事件中直接寫js片段-->
<button v-on:click="show = !show">點選切換</button><br/>
<h1 v-if="show">
你好
</h1>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
show:true
}
})
</script>
程式碼:
5.6.v-bind
html屬性不能使用雙大括號形式繫結,只能使用v-bind指令。
在將 v-bind
用於 class
和 style
時,Vue.js 做了專門的增強。表示式結果的型別除了字串之外,還可以是物件或陣列。
<div id="app">
<!--可以是資料模型,可以是具有返回值的js程式碼塊或者函式-->
<div v-bind:title="title" style="border: 1px solid red; width: 50px; height: 50px;"></div>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
title: "title",
}
})
</script>
效果:
在將 v-bind
用於 class
和 style
時,Vue.js 做了專門的增強。表示式結果的型別除了字串之外,還可以是物件或陣列。
5.6.1.繫結class樣式
陣列語法
我們可以藉助於v-bind
指令來實現:
HTML:
<div id="app">
<div v-bind:class="activeClass"></div>
<div v-bind:class="errorClass"></div>
<div v-bind:class="[activeClass, errorClass]"></div>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: "#app",
data: {
activeClass: 'active',
errorClass: ['text-danger', 'text-error']
}
})
</script>
渲染後的效果:(具有active和hasError的樣式)
物件語法
我們可以傳給 v-bind:class
一個物件,以動態地切換 class:
<div v-bind:class="{ active: isActive }"></div>
上面的語法表示 active
這個 class 存在與否將取決於資料屬性 isActive
的 truthiness(所有的值都是真實的,除了false,0,“”,null,undefined和NaN)。
你可以在物件中傳入更多屬性來動態切換多個 class。此外,v-bind:class
指令也可以與普通的 class 屬性共存。如下模板:
<div class="static"
v-bind:class="{ active: isActive, 'text-danger': hasError }">
</div>
和如下 data:
data: {
isActive: true,
hasError: false
}
結果渲染為:
<div class="static active"></div>
active樣式和text-danger樣式的存在與否,取決於isActive和hasError的值。本例中isActive為true,hasError為false,所以active樣式存在,text-danger不存在。
通常情況下,繫結的資料物件不必內聯定義在模板裡:
<div class="static" v-bind:class="classObject"></div>
資料:
data: {
classObject: {
active: true,
'text-danger': false
}
}
效果和之前一樣:
<div class="static active"></div>
5.6.2.繫結style樣式
陣列語法
陣列語法可以將多個樣式物件應用到同一個元素上:
<div v-bind:style="[baseStyles, overridingStyles]"></div>
資料:
data: {
baseStyles: {'background-color': 'red'},
overridingStyles: {border: '1px solid black'}
}
渲染後的結果:
<div style="background-color: red; border: 1px solid black;"></div>
物件語法
v-bind:style
的物件語法十分直觀——看著非常像 CSS,但其實是一個 JavaScript 物件。CSS 屬性名可以用駝峰式 (camelCase) 或短橫線分隔 (kebab-case,記得用單引號括起來) 來命名:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
資料:
data: {
activeColor: 'red',
fontSize: 30
}
效果:
<div style="color: red; font-size: 30px;"></div>
直接繫結到一個樣式物件通常更好,這會讓模板更清晰:
<div v-bind:style="styleObject"></div>
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}
效果同上。
5.6.3.簡寫
v-bind:class
可以簡寫為:class
5.7.計算屬性
在插值表示式中使用js表示式是非常方便的,而且也經常被用到。
但是如果表示式的內容很長,就會顯得不夠優雅,而且後期維護起來也不方便,例如下面的場景,我們有一個日期的資料,但是是毫秒值:
data:{
birthday:1529032123201 // 毫秒值
}
我們在頁面渲染,希望得到yyyy-MM-dd的樣式:
<h1>您的生日是:{{
new Date(birthday).getFullYear() + '-'+ new Date(birthday).getMonth()+ '-' + new Date(birthday).getDay()
}}
</h1>
雖然能得到結果,但是非常麻煩。
Vue中提供了計算屬性,來替代複雜的表示式:
var vm = new Vue({
el:"#app",
data:{
birthday:1429032123201 // 毫秒值
},
computed:{
birth(){// 計算屬性本質是一個方法,但是必須返回結果
const d = new Date(this.birthday);
return d.getFullYear() + "-" + d.getMonth() + "-" + d.getDay();
}
}
})
-
計算屬性本質就是方法,但是一定要返回資料。然後頁面渲染時,可以把這個方法當成一個變數來使用。
頁面使用:
<div id="app">
<h1>您的生日是:{{birth}} </h1>
</div>
效果:
我們可以將同一函式定義為一個方法而不是一個計算屬性。兩種方式的最終結果確實是完全相同的。然而,不同的是計算屬性是基於它們的依賴進行快取的。計算屬性只有在它的相關依賴發生改變時才會重新求值。這就意味著只要birthday
還沒有發生改變,多次訪問 birthday
計算屬性會立即返回之前的計算結果,而不必再次執行函式。
5.8.watch
watch可以讓我們監控一個值的變化。從而做出相應的反應。
示例:
<div id="app">
<input type="text" v-model="message">
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app",
data:{
message:""
},
watch:{
message(newVal, oldVal){
console.log(newVal, oldVal);
}
}
})
</script>
效果:
6.元件化
在大型應用開發的時候,頁面可以劃分成很多部分。往往不同的頁面,也會有相同的部分。例如可能會有相同的頭部導航。
但是如果每個頁面都獨自開發,這無疑增加了我們開發的成本。所以我們會把頁面的不同部分拆分成獨立的元件,然後在不同頁面就可以共享這些元件,避免重複開發。
6.1.全域性元件
我們通過Vue的component方法來定義一個全域性元件。
<div id="app">
<!--使用定義好的全域性元件-->
<counter></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
// 定義全域性元件,兩個引數:1,元件名稱。2,元件引數
Vue.component("counter",{
template:'<button v-on:click="count++">你點了我 {{ count }} 次,我記住了.</button>',
data(){
return {
count:0
}
}
})
var app = new Vue({
el:"#app"
})
</script>
-
元件其實也是一個Vue例項,因此它在定義時也會接收:data、methods、生命週期函式等
-
不同的是元件不會與頁面的元素繫結,否則就無法複用了,因此沒有el屬性。
-
但是元件渲染需要html模板,所以增加了template屬性,值就是HTML模板
-
全域性元件定義完畢,任何vue例項都可以直接在HTML中通過元件名稱來使用元件了。
-
data必須是一個函式,不再是一個物件。
效果:
6.2.元件的複用
定義好的元件,可以任意複用多次:
<div id="app">
<!--使用定義好的全域性元件-->
<counter></counter>
<counter></counter>
<counter></counter>
</div>
效果:
你會發現每個元件互不干擾,都有自己的count值。怎麼實現的?
元件的data屬性必須是函式!
當我們定義這個 <counter>
元件時,它的data 並不是像之前直接提供一個物件:
data: {
count: 0
}
取而代之的是,一個元件的 data 選項必須是一個函式,因此每個例項可以維護一份被返回物件的獨立的拷貝:
data: function () {
return {
count: 0
}
}
如果 Vue 沒有這條規則,點選一個按鈕就會影響到其它所有例項!
6.3.區域性元件
一旦全域性註冊,就意味著即便以後你不再使用這個元件,它依然會隨著Vue的載入而載入。
因此,對於一些並不頻繁使用的元件,我們會採用區域性註冊。
我們先在外部定義一個物件,結構與建立元件時傳遞的第二個引數一致:
const counter = {
template:'<button v-on:click="count++">你點了我 {{ count }} 次,我記住了.</button>',
data(){
return {
count:0
}
}
};
然後在Vue中使用它:
var app = new Vue({
el:"#app",
components:{
counter:counter // 將定義的物件註冊為元件
}
})
-
components就是當前vue物件子元件集合。
-
其key就是子元件名稱
-
其值就是元件物件名
-
-
效果與剛才的全域性註冊是類似的,不同的是,這個counter元件只能在當前的Vue例項中使用
6.4.元件通訊
通常一個單頁應用會以一棵巢狀的元件樹的形式來組織:
-
頁面首先分成了頂部導航、左側內容區、右側邊欄三部分
-
左側內容區又分為上下兩個元件
-
右側邊欄中又包含了3個子元件
各個元件之間以巢狀的關係組合在一起,那麼這個時候不可避免的會有元件間通訊的需求。
6.4.1.props(父向子傳遞)
-
父元件使用子元件時,自定義屬性(屬性名任意,屬性值為要傳遞的資料)
-
子元件通過props接收父元件屬性
父元件使用子元件,並自定義了title屬性:
<div id="app">
<h1>打個招呼:</h1>
<!--使用子元件,同時傳遞title屬性-->
<introduce title="大家好,我是鋒哥"/>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component("introduce",{
// 直接使用props接收到的屬性來渲染頁面
template:'<h1>{{title}}</h1>',
props:['title'] // 通過props來接收一個父元件傳遞的屬性
})
var app = new Vue({
el:"#app"
})
</script>
效果:
6.4.2.props驗證
我們定義一個子元件,並接收復雜資料:
const myList = {
template: '\
<ul>\
<li v-for="item in items" :key="item.id">{{item.id}} : {{item.name}}</li>\
</ul>\
',
props: {
items: {
type: Array,
default: [],
required: true
}
}
};
-
這個子元件可以對 items 進行迭代,並輸出到頁面。
-
props:定義需要從父元件中接收的屬性
-
items:是要接收的屬性名稱
-
type:限定父元件傳遞來的必須是陣列
-
default:預設值
-
required:是否必須
-
-
當 prop 驗證失敗的時候,(開發環境構建版本的) Vue 將會產生一個控制檯的警告。
我們在父元件中使用它:
<div id="app">
<h2>傳智播客已開設如下課程:</h2>
<!-- 使用子元件的同時,傳遞屬性,這裡使用了v-bind,指向了父元件自己的屬性lessons -->
<my-list :items="lessons"/>
</div>
var app = new Vue({
el:"#app",
components:{
myList // 當key和value一樣時,可以只寫一個
},
data:{
lessons:[
{id:1, name: 'java'},
{id:2, name: 'php'},
{id:3, name: 'ios'},
]
}
})
效果:
type型別,可以有:
注意:子元件模板有且只有一個根標籤
6.4.3.動態靜態傳遞
給 prop 傳入一個靜態的值:
<introduce title="大家好,我是鋒哥"/>
給 prop 傳入一個動態的值: (通過v-bind從資料模型中,獲取title的值)
<introduce :title="title"/>
靜態傳遞時,我們傳入的值都是字串型別的,但實際上任何型別的值都可以傳給一個 props。
<!-- 即便 `42` 是靜態的,我們仍然需要 `v-bind` 來告訴 Vue -->
<!-- 這是一個JavaScript表示式而不是一個字串。-->
<blog-post v-bind:likes="42"></blog-post>
<!-- 用一個變數進行動態賦值。-->
<blog-post v-bind:likes="post.likes"></blog-post>
6.4.4.子向父的通訊:$emit
來看這樣的一個案例:
<div id="app">
<h2>num: {{num}}</h2>
<!--使用子元件的時候,傳遞num到子元件中-->
<counter :num="num"></counter>
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
Vue.component("counter", {// 子元件,定義了兩個按鈕,點選數字num會加或減
template:'\
<div>\
<button @click="num++">加</button> \
<button @click="num--">減</button> \
</div>',
props:['num']// count是從父元件獲取的。
})
var app = new Vue({
el:"#app",
data:{
num:0
}
})
</script>
-
子元件接收父元件的num屬性
-
子元件定義點選按鈕,點選後對num進行加或減操作
我們嘗試執行,好像沒問題,點選按鈕試試:
子元件接收到父元件屬性後,預設是不允許修改的。怎麼辦?
既然只有父元件能修改,那麼加和減的操作一定是放在父元件:
var app = new Vue({
el:"#app",
data:{
num:0
},
methods:{ // 父元件中定義操作num的方法
increment(){
this.num++;
},
decrement(){
this.num--;
}
}
})
但是,點選按鈕是在子元件中,那就是說需要子元件來呼叫父元件的函式,怎麼做?
我們可以通過v-on指令將父元件的函式繫結到子元件上:
<div id="app">
<h2>num: {{num}}</h2>
<counter :count="num" @inc="increment" @dec="decrement"></counter>
</div>
在子元件中定義函式,函式的具體實現呼叫父元件的實現,並在子元件中呼叫這些函式。當子元件中按鈕被點選時,呼叫繫結的函式:
Vue.component("counter", {
template:'\
<div>\
<button @click="plus">加</button> \
<button @click="reduce">減</button> \
</div>',
props:['count'],
methods:{
plus(){
this.$emit("inc");
},
reduce(){
this.$emit("dec");
}
}
})
-
vue提供了一個內建的this.$emit()函式,用來呼叫父元件繫結的函式
效果:
7.路由vue-router
7.1.場景模擬
現在我們來實現這樣一個功能:
一個頁面,包含登入和註冊,點選不同按鈕,實現登入和註冊頁切換:
7.1.1.編寫父元件
為了讓接下來的功能比較清晰,我們先新建一個資料夾:src
然後新建一個HTML檔案,作為入口:index.html
然後編寫頁面的基本結構:
<div id="app">
<span>登入</span>
<span>註冊</span>
<hr/>
<div>
登入頁/註冊頁
</div>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script type="text/javascript">
var vm = new Vue({
el:"#app"
})
</script>
樣式:
7.1.2.編寫登入及註冊元件
接下來我們來實現登入元件,以前我們都是寫在一個檔案中,但是為了複用性,開發中都會把元件放入獨立的JS檔案中,我們新建一個user目錄以及login.js及register.js:
編寫元件,這裡我們只寫模板,不寫功能。
login.js內容如下:
const loginForm = {
template:'\
<div>\
<h2>登入頁</h2> \
使用者名稱:<input type="text"><br/>\
密碼:<input type="password"><br/>\
</div>\
'
}
register.js內容:
const registerForm = {
template:'\
<div>\
<h2>註冊頁</h2> \
用 戶 名:<input type="text"><br/>\
密  碼:<input type="password"><br/>\
確認密碼:<input type="password"><br/>\
</div>\
'
}
7.1.3.在父元件中引用
<div id="app">
<span>登入</span>
<span>註冊</span>
<hr/>
<div>
<!--<loginForm></loginForm>-->
<!--
疑問:為什麼不採用上面的寫法?
由於html是大小寫不敏感的,如果採用上面的寫法,則被認為是<loginform></loginform>
所以,如果是駝峰形式的元件,需要把駝峰轉化為“-”的形式
-->
<login-form></login-form>
<register-form></register-form>
</div>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="user/login.js"></script>
<script src="user/register.js"></script>
<script type="text/javascript">
var vm = new Vue({
el: "#app",
components: {
&nbs