1. Vue概述
1.1 Vue介紹
Vue 是一套用于構(gòu)建用戶界面的漸進(jìn)式框架。
1.2 Vue核心思想
- 雙向數(shù)據(jù)綁定
Vue
雙向數(shù)據(jù)綁定利用了Object
對(duì)象的set()
和get()
方法,原理如下:
<input type="text" id="userName" />
<span id="uName"></span>
<script>
const obj = {}
Object.defineProperty(obj, 'text', {
get: function(val) {
console.log('get init');
},
set: function(val) {
console.log('set:' + val);
ipt.value = val;
span.innerText = val;
}
})
const ipt = document.getElementById('userName');
const span = document.getElementById('uName');
ipt.addEventListener('keyup', function(e) {
obj.text = e.target.value;
})
</script>
1.3 Vue與React對(duì)比
1.3.1 不同點(diǎn)
一干毅、Vue
- 簡(jiǎn)單的語(yǔ)法和項(xiàng)目構(gòu)建。
二、React
- 適用于大型項(xiàng)目以及更好的可測(cè)試性窿给。
- 更大的生態(tài)圈帶來(lái)的更多的支持工具馋辈。
1.3.2 相同點(diǎn)
- 虛擬
DOM
- 輕量級(jí)
- 響應(yīng)式
- 服務(wù)端渲染
- 易于集成路由工具抚芦、打包工具和狀態(tài)管理工具
- 優(yōu)秀的支持和社區(qū)
2. Vue基礎(chǔ)語(yǔ)法
2.1 Vue環(huán)境搭建
2.1.1 環(huán)境構(gòu)建方式
- 官方拷貝
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
-
npm
安裝 -
vue-cli
工具構(gòu)建
2.1.1 vue-cli工具構(gòu)建SPA應(yīng)用
npm i -g vue-cli
-
vue init webpack-simple demo
初始化一個(gè)簡(jiǎn)單的webpack
項(xiàng)目 -
vue init webpack demo
初始化一個(gè)完整的webpack
項(xiàng)目
2.2 Vue基礎(chǔ)語(yǔ)法
- 模板語(yǔ)法
-
Mustache
語(yǔ)法:{{msg}}
-
Html
賦值:v-html = ""
- 綁定屬性:
v-bind:id = ""
- 使用表達(dá)式:
{{ok ? 'YES' : 'NO'}}
- 文本賦值:
v-text = ""
- 指令:
v-if = ""
- 過(guò)濾器:
{{message | capitalize}}
和v-bind:id = "rawId | formtaId"
注釋:vue
組件中的data
推薦使用方法的方式data(){return {}}
返回?cái)?shù)據(jù),這樣不同組件實(shí)例之間就不會(huì)共用數(shù)據(jù)迈螟。
-
Class
和Style
綁定
- 對(duì)象語(yǔ)法
<div v-bind:class="{active: isActive, 'text-danger': hasError}">
- 數(shù)組語(yǔ)法
<div v-bind:class="[activeClass, errorClass]">
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
- style綁定
<div v-bind:style="{color: activeColor, fontSize: fontSize + 'px'}">
- 條件渲染
v-if
v-else
v-else-if
v-show
v-cloak
- 事件處理器
-
v-on:click="greet"
叉抡、@click="greet"
-
v-on:click.stop
、v-on:click.stop.prevent
答毫、v-on:click.self
褥民、v-on:click.once
v-on:keyup.enter/tab/delete/esc/space/up/down/left/right
-
Vue
組件
- 全局組件和局部組件
單頁(yè)面應(yīng)用一般使用的都是局部組件。 - 父子組件通訊-數(shù)據(jù)傳遞
//父組件
<template>
<div class="hello">
<Counter v-bind:num="num" v-on:incre="increment" v-on:decre="decrement"/>
<span>{{`parent: ${num}`}}</span>
</div>
</template>
<script>
import Counter from './Counter'
export default {
data () {
return {
num: 10
}
},
components: {
Counter
},
methods: {
increment() {
this.num++
},
decrement() {
this.num--
}
}
}
</script>
//子組件
<template>
<div>
<button @click="increment">+</button>
<button v-on:click="decrement">-</button>
<p><span>{{num}}</span></p>
</div>
</template>
<script>
export default {
props: ['num'],
methods: {
increment() {
this.$emit('incre');
},
decrement() {
this.$emit('decre')
}
}
}
</script>
-
Slot
使用slot
可以減少父組件props
傳值的數(shù)量以及子組件$emit
觸發(fā)父組件方法的數(shù)量洗搂。
<modal v-bind:mdShow="mdShowCard" v-on:close="closeModelCard">
<p slot="message">
<svg class="icon-status-ok">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-cart"></use>
</svg>
<span>加入購(gòu)物車成功</span>
</p>
<div slot="btnGroup">
<a class="btn btn--m" @click="mdShowCard=false">繼續(xù)購(gòu)物</a>
<router-link class="btn btn--m btn--red" href="javascript:;" to="/cart">查看購(gòu)物車</router-link>
</div>
</modal>
2.3 路由 vue-router
2.3.1路由基礎(chǔ)介紹
- 路由
根據(jù)不同的url
地址展示不同的內(nèi)容或頁(yè)面消返。 - 后端路由
服務(wù)器根據(jù)url
地址返回不同頁(yè)面。 - 前端路由
不同路由對(duì)應(yīng)不同內(nèi)容或頁(yè)面的任務(wù)交給前端來(lái)做耘拇。 - 前端路由使用場(chǎng)景
單頁(yè)面應(yīng)用撵颊。 - 前端路由的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):路由跳轉(zhuǎn)用戶體驗(yàn)好嚼松。
缺點(diǎn):不利于SEO
探熔;瀏覽器前進(jìn)后退重新發(fā)送請(qǐng)求,沒(méi)有利用緩存村生;無(wú)法記住之前頁(yè)面的滾動(dòng)位置嘉涌。 -
vue-router
介紹
vue-router官網(wǎng)
(1) 跳轉(zhuǎn)組件
<router-link></router-link>
注釋:router-link
就是一個(gè)封裝好的a
標(biāo)簽妻熊,可以添加樣式夸浅。
(2)js
跳轉(zhuǎn)
this.$router.push({path: ''})
(3) 展示組件
<router-view></router-view>
注釋:vue-router
是對(duì)historyAPI
的封裝。
2.3.2. 動(dòng)態(tài)路由匹配
- 動(dòng)態(tài)路由介紹
模式 | 匹配路徑 | $route.params |
---|---|---|
/user/:username | /user/even | {username: 'even'} |
/user/:username/post/:post_id | /user/even/post/123 | {username: 'even', post_id: 123} |
- 動(dòng)態(tài)路由的使用
//路由入口文件: scr/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import GoodList from '@/views/GoodList'
Vue.use(Router)
export default new Router({
mode: 'history', //默認(rèn)值為hash
routes: [
{
path: '/goods/:goodsId/user/:userName',
name: 'GoodList',
component: GoodList
}
]
})
//src/views/GoodsList.vue文件
<template>
<div>
<div>這是商品列表頁(yè)面</div>
<span>{{$route.params.goodsId}}</span>
<span>{{$route.params.userName}}</span>
</div>
</template>
<script>
export default {}
- 使用
vue-cli
工具構(gòu)建的項(xiàng)目已經(jīng)嵌套vue-router
固耘。 - 路徑完全匹配模式(
/goods/@@@/user/&&&
)才能夠訪問(wèn)到該路由視圖题篷。 -
mode
默認(rèn)取值為hash
,此時(shí)通過(guò)#
+ 路徑才能訪問(wèn)到厅目。如果取值為history
番枚,則不用加#
。
2.3.3. 嵌套路由
- 嵌套路由的使用
//路由入口文件: scr/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import GoodList from '@/views/GoodList'
import Title from '../views/Title'
import Image from '../views/Image'
Vue.use(Router)
export default new Router({
mode: 'history', //默認(rèn)值為hash
routes: [
{
path: '/goods',
name: 'GoodList',
component: GoodList,
children: [
{
path: 'title',
name: 'title',
component: Title
},
{
path: 'img',
name: 'img',
component: Image
}
]
}
]
})
//src/views/GoodsList.vue文件
<template>
<div>
<div>這是商品列表頁(yè)面</div>
<router-link to="/goods/title">
顯示標(biāo)題子路由
</router-link>
<router-link to="/goods/img">
顯示圖片子路由
</router-link>
<div>
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {}
</script>
//src/views/Image.vue文件
<template>
<div>圖片子路由</div>
</template>
<script>
export default {}
</script>
//src/views/Title.vue文件
<template>
<div>標(biāo)題子路由</div>
</template>
<script>
export default {}
</script>
- 在
children
處注冊(cè)路由時(shí)使用相對(duì)于父路由的路徑即可损敷,在router-link
的to
屬性跳轉(zhuǎn)地址要使用完整路徑葫笼。 - 從子路由的用法可知,路由的本質(zhì)并不是整個(gè)頁(yè)面的顯示/隱藏切換拗馒,而是
頁(yè)面某個(gè)區(qū)域
的顯示隱藏切換路星。
2.3.4. 編程式路由
- 編程式路由介紹
使用js
實(shí)現(xiàn)頁(yè)面的跳轉(zhuǎn)。
$router.push("name")
$router.push({path: "name"})
$router.push({path: "name?a=123})
$router.push({path: "name", query: {a: 123}})
-
$router.go(1/-1)
注意: 對(duì)比區(qū)分query
參數(shù)的傳遞與動(dòng)態(tài)路由params
參數(shù)的傳遞诱桂。 query傳遞的是?a=1;b=2
字段洋丐,通過(guò)$route.query.key
的方式取值。動(dòng)態(tài)參數(shù)傳遞的是/a/b
字段挥等,通過(guò)$route.params.key
的方式取值友绝。
總結(jié):①$route.query.key
獲取的是當(dāng)前url
中query
中的字段值,$route.params.key
獲取的是當(dāng)前url
中params
中的字段值肝劲。②使用router-link
組件跳轉(zhuǎn)和js
跳轉(zhuǎn)都可以傳遞params
和query
迁客。
- 編程式路由的使用
//路由入口文件: scr/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import GoodList from '@/views/GoodList'
import Cart from '@/views/Cart'
Vue.use(Router)
export default new Router({
mode: 'history', //默認(rèn)值為hash
routes: [
{
path: '/goods',
name: 'GoodList',
component: GoodList
},
{
path: '/cart',
name: 'cart',
component: Cart
}
]
})
//src/views/GoodsList.vue文件
<template>
<div>
<div>這是商品列表頁(yè)面</div>
<button @click="toCart">
跳轉(zhuǎn)到購(gòu)物車
</button>
</div>
</template>
<script>
export default {
methods: {
toCart() {
this.$router.push({path: "/cart", query: {a: 1}})
}
}
}
</script>
//src/views/Cart.vue文件
<template>
<div>
<div>這是購(gòu)物車頁(yè)面</div>
<span>{{$route.query.a}}</span>
<button @click="backToGoods">
返回商品列表
</button>
</div>
</template>
<script>
export default {
methods: {
backToGoods() {
this.$router.go(-1)
}
}
}
</script>
2.3.5. 命名路由和命名視圖
1.命名路由和命名視圖介紹
給路由定義不同的名字,根據(jù)名字進(jìn)行匹配辞槐。
給不同的router-view
定義名字掷漱,通過(guò)名字進(jìn)行對(duì)應(yīng)組件的渲染。
- 命名路由的使用
//路由入口文件: scr/router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import GoodList from '@/views/GoodList'
import Cart from '@/views/Cart'
Vue.use(Router)
export default new Router({
mode: 'history', //默認(rèn)值為hash
routes: [
{
path: '/goods',
name: 'GoodList',
component: GoodList
},
{
path: '/cart/:cartId',
name: 'cart',
component: Cart
}
]
})
//src/views/GoodsList.vue文件
<template>
<div>
<div>這是商品列表頁(yè)面</div>
<router-link v-bind:to="{name: 'cart', params: {cartId:123}, query: {a:1}}">跳轉(zhuǎn)到購(gòu)物車頁(yè)面</router-link>
</div>
</template>
<script>
export default {}
</script>
//src/views/Cart.vue文件
<template>
<div>
<div>這是購(gòu)物車頁(yè)面</div>
<span>{{$route.params.cartId}}</span>
<span>{{$route.query.a}}</span>
</div>
</template>
<script>
export default {}
</script>
- 命名視圖
Vue Router文檔-命名視圖
2.3.6 HTML5 History 模式
-
vue-router
默認(rèn)hash
模式 —— 使用URL
的hash
來(lái)模擬一個(gè)完整的URL
榄檬,于是當(dāng)URL
改變時(shí)卜范,頁(yè)面不會(huì)重新加載。
如果不想要很丑的hash
鹿榜,我們可以用路由的history
模式海雪,這種模式充分利用history.pushState API
來(lái)完成URL
跳轉(zhuǎn)而無(wú)須重新加載頁(yè)面。
const router = new VueRouter({
mode: 'history',
routes: [...]
})
- 當(dāng)你使用
history
模式時(shí)犬缨,URL
就像正常的url
喳魏,例如http://yoursite.com/user/id
,也好看怀薛!
不過(guò)這種模式要玩好刺彩,還需要后臺(tái)配置支持。因?yàn)槲覀兊膽?yīng)用是個(gè)單頁(yè)客戶端應(yīng)用,如果后臺(tái)沒(méi)有正確的配置创倔,當(dāng)用戶在瀏覽器直接訪問(wèn)http://oursite.com/user/id
就會(huì)返回404
嗡害,這就不好看了。
所以呢畦攘,你要在服務(wù)端增加一個(gè)覆蓋所有情況的候選資源:如果URL
匹配不到任何靜態(tài)資源霸妹,則應(yīng)該返回同一個(gè)index.html
頁(yè)面,這個(gè)頁(yè)面就是你app
依賴的頁(yè)面知押。
注釋:vue-router
處理前端路由跳轉(zhuǎn)叹螟,后端路由處理ajax/fetch
請(qǐng)求以及用戶訪問(wèn)url
頁(yè)面。當(dāng)用戶直接訪問(wèn)某一個(gè)路由頁(yè)面url
(http://oursite.com/user/id
)時(shí)台盯,該請(qǐng)求會(huì)由服務(wù)器端進(jìn)行處理罢绽。
注意:把服務(wù)器的url
解析重定向到index.html
的首頁(yè)里面即可。如果mode
取默認(rèn)值hash
静盅,通過(guò)# + 路徑地址
才能訪問(wèn)到良价,后臺(tái)是不識(shí)別錨點(diǎn),不會(huì)出現(xiàn)404
的狀態(tài)蒿叠。 - 后端配置例子
(1)原生Node.js
const http = require('http')
const fs = require('fs')
const httpPort = 80
http.createServer((req, res) => {
fs.readFile('index.htm', 'utf-8', (err, content) => {
if (err) {
console.log('We cannot open "index.htm" file.')
}
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8'
})
res.end(content)
})
}).listen(httpPort, () => {
console.log('Server listening on: http://localhost:%s', httpPort)
})
(2) 基于 Node.js
的 Express
對(duì)于 Node.js/Express明垢,請(qǐng)考慮使用 connect-history-api-fallback 中間件。
- 給個(gè)警告市咽,因?yàn)檫@么做以后痊银,你的服務(wù)器就不再返回
404
錯(cuò)誤頁(yè)面,因?yàn)閷?duì)于所有路徑都會(huì)返回index.html
文件魂务。為了避免這種情況曼验,你應(yīng)該在Vue
應(yīng)用里面覆蓋所有的路由情況泌射,然后在給出一個(gè)404
頁(yè)面粘姜。
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '*', component: NotFoundComponent }
]
})
或者,如果你使用 Node.js
服務(wù)器熔酷,你可以用服務(wù)端路由匹配到來(lái)的 URL
孤紧,并在沒(méi)有匹配到路由的時(shí)候返回 404
,以實(shí)現(xiàn)回退拒秘。更多詳情請(qǐng)查閱 Vue 服務(wù)端渲染文檔号显。
2.4 請(qǐng)求數(shù)據(jù)
2.4.1 vue-resource
-
vue-resource
的請(qǐng)求API
是按照REST
風(fēng)格設(shè)計(jì)的,它提供了7種請(qǐng)求API
:
get(url, [options])
head(url, [options])
delete(url, [options])
jsonp(url, [options])
post(url, [body], [options])
put(url, [body], [options])
patch(url, [body], [options])
- 發(fā)送請(qǐng)求時(shí)的
options
選項(xiàng)對(duì)象包含以下屬性
參數(shù) | 類型 | 描述 |
---|---|---|
url |
string |
請(qǐng)求的URL
|
method |
string |
請(qǐng)求的HTTP 方法躺酒,例如:'GET ', 'POST '或其他HTTP 方法 |
body |
Object , FormData string
|
request body |
params |
Object |
請(qǐng)求的URL 參數(shù)對(duì)象 |
headers |
Object |
request header |
timeout |
number |
單位為毫秒的請(qǐng)求超時(shí)時(shí)間 (0 表示無(wú)超時(shí)時(shí)間) |
before |
function(request) |
請(qǐng)求發(fā)送前的處理函數(shù)押蚤,類似于jQuery 的beforeSend 函數(shù) |
progress |
function(event) |
ProgressEvent 回調(diào)處理函數(shù) |
credientials |
boolean |
表示跨域請(qǐng)求時(shí)是否需要使用憑證 |
emulateHTTP |
boolean |
發(fā)送PUT , PATCH , DELETE 請(qǐng)求時(shí)以HTTP POST 的方式發(fā)送,并設(shè)置請(qǐng)求頭的X-HTTP-Method-Override
|
emulateJSON |
boolean |
將request body 以application/x-www-form-urlencoded content type 發(fā)送 |
- 全局?jǐn)r截器
interceptors
Vue.http.interceptors.push((request, next) => {
// ...
// 請(qǐng)求發(fā)送前的處理邏輯
// ...
next((response) => {
// ...
// 請(qǐng)求發(fā)送后的處理邏輯
// ...
// 根據(jù)請(qǐng)求的狀態(tài)羹应,response參數(shù)會(huì)返回給successCallback或errorCallback
return response
})
})
-
vue-resource
使用示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>vue-resource</title>
<!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" >
<script src="../../node_modules/vue/dist/vue.js"></script>
<script src="../../node_modules/vue-resource/dist/vue-resource.js"></script>
</head>
<body>
<div id="app">
<h2>vue-resource演示</h2>
<a href="#" @click="sendGet">發(fā)送Get請(qǐng)求</a>
<a href="#" @click="sendPost">發(fā)送Post請(qǐng)求</a>
<a href="#" @click="sendJsonp">發(fā)送Jsonp請(qǐng)求</a>
<a href="#" @click="sendHttp">全局函數(shù)</a>
<p v-text="response"></p>
</div>
<script>
new Vue({
el:"#app",
data:{
response:''
},
http: {
root: 'http://localhost:8050/imoocmall/'
},
mounted() {
Vue.http.interceptors.push((request, next) => {
console.log('request init.');
next((response) => {
console.log('response init.');
return response
})
})
},
methods:{
sendGet() {
this.$http.get('package.json',{
params:{
userId: "101",
},
headers:{
access_token:"abc"
}
}).then(res => {
this.response = res.data;
}).catch(err => {
this.response = err;
});
},
sendPost() {
this.$http.post('package.json', {
userId: '102'
}, {
headers: {
access_token:"abcd"
}
}).then(res => {
this.response = res.data;
}).catch(err => {
this.response = err;
});
},
sendJsonp(){
this.$http.jsonp("http://www.imooc.com/course/ajaxskillcourse?cid=796",{
params:{
userId:"1001"
}
}).then(res => {
this.response = res.data;
}).catch(err => {
this.response = err;
})
},
sendHttp() {
this.$http({
url:"package.json",
method:"GET",
params:{ userId:"103" },
headers:{ token:"123" },
timeout:50,
before() {
console.log("before init")
}
}).then(res => {
this.response = res.data;
});
}
}
});
</script>
</body>
</html>
注釋: ①引入 vue-resource
之后可以通過(guò)this.$http
的方式使用揽碘。
2.4.2 Axios
- Axios簡(jiǎn)介
Axios
是一個(gè)基于promise
的HTTP
庫(kù),可以用在瀏覽器和node.js
中。 - 請(qǐng)求方法介紹
axios.request(config)
axios.get(url[, config])
axios.delete(url[, config])
axios.head(url[, config])
axios.post(url[, data[, config]])
axios.put(url[, data[, config]])
-
axios.patch(url[, data[, config]])
注意: ·Axios· 請(qǐng)求方法中沒(méi)有jsonp
請(qǐng)求雳刺。
- 執(zhí)行
get
請(qǐng)求
// 為給定 ID 的 user 創(chuàng)建請(qǐng)求
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
// 可選地劫灶,上面的請(qǐng)求可以這樣做
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
- 執(zhí)行
POST
請(qǐng)求
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
- 執(zhí)行多個(gè)并發(fā)請(qǐng)求
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) {
// 兩個(gè)請(qǐng)求現(xiàn)在都執(zhí)行完成
}));
-
Axios
使用示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>axios</title>
<link rel="stylesheet" >
<script src="../../node_modules/vue/dist/vue.js"></script>
<script src="../../node_modules/axios/dist/axios.js"></script>
</head>
<body>
<div id="app">
<h2>vaxios演示</h2>
<a href="#" @click="sendGet">發(fā)送Get請(qǐng)求</a>
<a href="#" @click="sendPost">發(fā)送Post請(qǐng)求</a>
<a href="#" @click="sendHttp">全局函數(shù)</a>
<p v-text="response"></p>
</div>
<script>
new Vue({
el:"#app",
data:{
response:''
},
mounted() {
axios.interceptors.request.use((req) => {
console.log('request init.');
return req;
});
axios.interceptors.response.use((res) => {
console.log('response init.');
return res;
});
},
methods:{
sendGet() {
axios.get('../../package.json',{
params:{
userId: "101",
},
headers:{
token:"abc"
}
}).then(res => {
this.response = res.data;
}).catch(err => {
this.response = err;
});
},
sendPost() {
axios.post('../../package.json', {
userId: '102'
}, {
headers: {
token:"abcd"
}
}).then(res => {
this.response = res.data;
}).catch(err => {
this.response = err;
});
},
sendHttp() {
axios({
url:'../../package.json',
method:"POST",
data:{ userId:"103" },
headers:{ token:"123" }
}).then(res => {
this.response = res.data;
});
}
}
});
</script>
</body>
</html>
注釋:①axios
的參數(shù)傳遞方式與vue-resource
基本相同。② 注意區(qū)分get
請(qǐng)求與post
請(qǐng)求的參數(shù)傳遞方式掖桦。
2.5 Vuex基本用法
2.5.1 Vuex介紹
-
Vuex
是一個(gè)專門為Vue.js
應(yīng)用程序開發(fā)的狀態(tài)管理模式本昏。 - 當(dāng)我們構(gòu)建一個(gè)中大型的單頁(yè)應(yīng)用程序時(shí),
Vuex
可以更好的幫助我們?cè)诮M件外部統(tǒng)一管理狀態(tài)枪汪。
2.5.2 核心概念
State
Getters
Mutations
Actions
Modules
-
State
State
唯一數(shù)據(jù)源涌穆,單一狀態(tài)樹。
// 創(chuàng)建一個(gè) Counter 組件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count
}
}
}
-
Getters
通過(guò)Getters
可以派生出一些新的狀態(tài)雀久。
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
-
Mutations
更改Vuex
的store
中狀態(tài)的唯一方法是提交mutation
蒲犬。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 變更狀態(tài)
state.count++
}
}
})
觸發(fā)mutation handler
:
store.commit('increment')
-
Actions
Action
提交的是mutation
,而不是直接改變狀態(tài)岸啡。
Action
可以包含任意異步操作原叮。
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
注釋:mutations
中的方法必須是同步的,actions
中的方法可以有異步操作巡蘸。
-
Modules
面對(duì)復(fù)雜的應(yīng)用程序奋隶,當(dāng)管理的狀態(tài)比較多時(shí),我們需要將Vuex
的store
對(duì)象分割成模塊(modules
)悦荒。
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的狀態(tài)
store.state.b // -> moduleB 的狀態(tài)
-
概念關(guān)系圖
2.5.3 項(xiàng)目結(jié)構(gòu)
├── index.html
├── main.js
├── api
│ └── ... # 抽取出API請(qǐng)求
├── components
│ ├── App.vue
│ └── ...
└── store
├── index.js # 我們組裝模塊并導(dǎo)出 store 的地方
├── actions.js # 根級(jí)別的 action
├── mutations.js # 根級(jí)別的 mutation
└── modules
├── cart.js # 購(gòu)物車模塊
└── products.js # 產(chǎn)品模塊
2.5.4 代碼示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vuex</title>
<script src="../../node_modules/vue/dist/vue.js"></script>
<script src="../../node_modules/vuex/dist/vuex.min.js"></script>
</head>
<body>
<div id="app">
<h2>{{msg}}</h2>
<button @click="add">同步增加</button>
<button @click="asyncAdd">異步增加</button>
<counter></counter>
</div>
<script>
const counter = {
template: `
<div>
<h3>{{count}}<h3>
<h3>{{squareCount}}</h3>
</div>
`,
computed: {
...Vuex.mapState(['count']), //mapState語(yǔ)法糖
/*count() {
return this.$store.state.count;
},*/
squareCount() {
return this.$store.getters.squareCount;
}
}
};
const store = new Vuex.Store({
state: {
count: 0
},
getters: {
squareCount(state) {
return state.count * state.count;
}
},
mutations: {
increment(state, num) {
state.count += num;
}
},
actions: {
asyncIncrement(context, num) {
setTimeout(() => {
context.commit('increment', num);
}, 1000);
}
}
});
new Vue({
el: '#app',
store,
data: {
msg: 'Vuex的使用'
},
components: {
counter
},
methods: {
add() {
this.$store.commit('increment', 1);
},
asyncAdd() {
this.$store.dispatch('asyncIncrement', 2)
}
}
})
</script>
</body>
</html>
3. Vue生態(tài)圈
3.1 模擬mock數(shù)據(jù)
在vue
開發(fā)過(guò)程中唯欣,有時(shí)需要使用本地json
模擬后臺(tái)接口數(shù)據(jù),測(cè)試前端頁(yè)面展示情況搬味。對(duì)于使用vue-cli
工具構(gòu)建的項(xiàng)目境氢,封裝了express
框架,我們可以通過(guò)攔截請(qǐng)求的方式使用mock
數(shù)據(jù)碰纬。
- 創(chuàng)建
mock
數(shù)據(jù)json
文件 - 在
webpack.dev.conf.js
文件中攔截請(qǐng)求
//imoocmall/build/webpack.dev.conf.js文件
var goodsData = require('../mock/goods.json')
devServer: {
before (app) {
app.get('/goods', function (req, res) {
res.json(goodsData);
})
},
//...
}
//..
注釋: 這里的app.get('/goods', (req, res) => {})
就是express
框架定義后端路由接口的寫法萍聊。
- 使用mock數(shù)據(jù)
axios.get('/goods',)
.then(res => {
//...
})
3.2 圖片懶加載
使用vue-lazyload插件可以實(shí)現(xiàn)圖片懶加載。
- 安裝
npm i vue-lazyload -d
- 引入
src/main.js
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload, {
error: 'dist/error.png',
loading: 'dist/loading.gif',
})
- 使用
<img v-lazy="'/static/'+good.productImage" alt="">
3.3 滾動(dòng)加載
使用vue-infinite-scroll插件可以實(shí)現(xiàn)滾動(dòng)加載悦析。
- 安裝
npm install vue-infinite-scroll --save
- 引入
src/main.js
import infiniteScroll from 'vue-infinite-scroll'
Vue.use(infiniteScroll)
- 使用
<div v-infinite-scroll="loadMore" infinite-scroll-disabled="busy" infinite-scroll-distance="30">
<img src="../assets/loading-spinning-bubbles.svg" >
</div>
var count = 0;
new Vue({
el: '#app',
data: {
data: [],
busy: false
},
methods: {
loadMore: function() {
this.busy = true;
setTimeout(() => {
for (var i = 0, j = 10; i < j; i++) {
this.data.push({ name: count++ });
}
this.busy = false;
}, 1000);
}
}
});
注釋:infinite-scroll-disabled
表示滾動(dòng)加載是否禁用寿桨。
3.4 請(qǐng)求代理
- 開發(fā)過(guò)程中,前端服務(wù)與后端接口一般存在著跨域問(wèn)題强戴。
vue-cli
提供了proxyTable
代理功能解決跨域問(wèn)題亭螟。
注釋:① 開發(fā)環(huán)境前端服務(wù)端口8080
(/config/index.js中的port: 8080
)與后端服務(wù)端口(/server/bin/www中的var port = normalizePort(process.env.PORT || '3000');
)不同,存在跨域骑歹,所有需要使用請(qǐng)求代理预烙。② 一般僅在開發(fā)環(huán)境中配置。
注意:①跨域問(wèn)題只是web
前端瀏覽器的行為道媚,在web
前端請(qǐng)求不符合同源策略接口數(shù)據(jù)時(shí)出現(xiàn)扁掸。②后端node
連接mongodb
數(shù)據(jù)庫(kù)即使協(xié)議域名端口不同(不符合同源策略)欢嘿,也不存在跨域問(wèn)題。 - 修改
/config/index.js
文件中的dev.proxyTable
配置
proxyTable: {
'/goods': {
target: 'http://localhost:3000'
}
}
}
此時(shí)也糊,當(dāng)我們請(qǐng)求 http://localhost:8888/goods
的時(shí)候,就等于請(qǐng)求了http://localhost:3000/goods
炼蹦。
注意: '/goods'
規(guī)則不僅能夠匹配'/goods'
規(guī)則的路徑,還能夠匹配'/goods/a'
狸剃、'/goods/a/b'
等規(guī)則的路徑掐隐。
proxyTable: {
'/api': {
target: 'http://localhost:3000' ,
pathRewrite: {
'^/api': ''
}
}
}
}
此時(shí),當(dāng)我們請(qǐng)求 http://localhost:8888/api
的時(shí)候,就等于請(qǐng)求了http://localhost:3000
钞馁。