一:vue傳值方式有哪些近哟?
在vue項(xiàng)目開發(fā)過程中,經(jīng)常會使用組件來進(jìn)行項(xiàng)目開發(fā)钱豁,那么在組件中會調(diào)用另一個組件來作為自己的子組件耻卡,那么我們?nèi)绾芜M(jìn)行給子組件進(jìn)行傳值呢?或者說牲尺,子組件如何給父組件傳值呢卵酪?父子組件數(shù)據(jù)是如何通信的呢?
因此vue常用的傳值方式有如下幾種:
1. 父傳子
2. 子傳父
3. 非父子傳值
父子組件傳遞數(shù)據(jù)可以看如下圖所示
總結(jié)是:父組件向子組件傳遞數(shù)據(jù)秸谢,可以通過prop屬性向下傳遞數(shù)據(jù)凛澎,子組件向父組件傳遞數(shù)據(jù)是通過事件給父組件發(fā)送消息。
下面我們看如下父子組件的demo估蹄。在看demo之前,我們看下項(xiàng)目目錄的結(jié)構(gòu)如下:
### 目錄結(jié)構(gòu)如下:
demo1 # 工程名
| |--- dist # 打包后生成的目錄文件
| |--- node_modules # 所有的依賴包
| |--- app
| | |---index
| | | |-- views # 存放所有vue頁面文件
| | | | |-- parent.vue # 父組件
| | | | |-- child.vue # 子組件
| | | | |-- index.vue
| | | |-- components # 存放vue公用的組件
| | | |-- js # 存放js文件的
| | | |-- app.js # vue入口配置文件
| | | |-- router.js # 路由配置文件
| |--- views
| | |-- index.html # html文件
| |--- webpack.config.js # webpack配置文件
| |--- .gitignore
| |--- README.md
| |--- package.json
| |--- .babelrc # babel轉(zhuǎn)碼文件
1. 父組件向子組件傳值demo如下:
app/index/views/parent.vue 代碼如下:
<template>
<div>
<div>
<label>父組件:</label>
<input type='text' v-model = 'name' />
<br />
<!-- 引入子組件 -->
<child :inputName="name"></child>
</div>
</div>
</template>
<script type="text/javascript">
import child from './child';
export default {
data() {
return {
name: ''
}
},
components: {
child: child
}
}
</script>
app/index/views/child.vue 代碼如下:
<template>
<div>
<label>子組件:</label>
<span>{{inputName}}</span>
</div>
</template>
<script type="text/javascript">
export default {
// 接收父組件的值
props: {
inputName: String,
required: true
}
}
</script>
app/index/views/index.vue 代碼如下:
<style lang="stylus">
</style>
<template>
<div id="app">
<header>
<router-link to="/parent" tag='li'>parent</router-link>
</header>
<!-- 對應(yīng)組件的內(nèi)容渲染到router-view中 -->
<router-view></router-view>
</div>
</template>
<script type="text/javascript">
export default {
data() {
return {
}
}
}
</script>
app/index/router.js 代碼如下:
import Vue from 'vue';
import VueRouter from 'vue-router';
// 告訴 vue 使用 vueRouter
Vue.use(VueRouter);
const routes = [
{
path: '/parent',
name: 'parent',
component: resolve => require(['./views/parent'], resolve)
},
{
path: '*', // 其他沒有的頁面都重定向到 home頁面去
redirect: '/parent'
}
]
var router = new VueRouter({
base: '/app/index', // 配置單頁應(yīng)用的基路徑
routes: routes
});
export default router;
因此頁面運(yùn)行效果如下圖所示:
2. 子組件向父組件傳值方式demo如下:
app/index/views/parent.vue 代碼改成如下:
<template>
<div>
<div>
<label>父組件:</label>
<span style="color:red;">{{name}}</span>
<br />
<!-- 引入子組件 定義一個方法來監(jiān)聽子組件的狀態(tài)值 -->
<child @childChange="childChangeFunc"></child>
</div>
</div>
</template>
<script type="text/javascript">
import child from './child';
export default {
data() {
return {
name: ''
}
},
methods: {
childChangeFunc(value) {
// value 就是子組件傳遞過來的值
this.name = value;
}
},
components: {
child: child
}
}
</script>
app/index/views/child.vue 代碼改成如下:
<template>
<div>
<label>子組件:</label>
<span>{{childValue}}</span>
<!-- 子組件事件傳值 -->
<input type='button' value='點(diǎn)擊觸發(fā)' @click="childClick" />
</div>
</template>
<script type="text/javascript">
export default {
data() {
return {
childValue: '我是子組件的數(shù)據(jù)'
}
},
methods: {
childClick() {
// 子組件觸發(fā)事件
this.$emit('childChange', this.childValue);
}
}
}
</script>
運(yùn)行如下:
么有觸發(fā)點(diǎn)擊之前的效果圖如下:
點(diǎn)擊觸發(fā)按鈕后沫换,效果圖如下:
3. 非父子組件進(jìn)行傳值
非父子組件之間傳值臭蚁,需要定義個公共的實(shí)列文件來作為中間倉庫來傳值的。比如定義一個叫 bus.js 文件讯赏。
所謂中間倉庫就是創(chuàng)建一個事件中心垮兑,相當(dāng)于中轉(zhuǎn)站,可以使用它來傳遞事件和接收事件的漱挎。
在app/index 下新建一個js文件夾系枪,在文件夾內(nèi)新建一個bus.js文件,代碼如下:
import Vue from 'vue';
export default new Vue();
在app/index/views/a.vue, 代碼如下:
<template>
<div>
<label>A組件:</label>
<span>{{value}}</span>
<input type="button" value="點(diǎn)擊觸發(fā)" @click="clickFunc" />
</div>
</template>
<script type="text/javascript">
// 引入公共的bus
import Bus from '../js/bus.js';
export default {
data() {
return {
value: 1
}
},
methods: {
clickFunc() {
Bus.$emit('val', this.value);
}
}
}
</script>
在app/index/views/b.vue, 代碼如下:
<template>
<div>
<label>B組件:</label>
<input type='button' value='點(diǎn)擊觸發(fā)' @click="getDataFunc" />
<span>{{name}}</span>
</div>
</template>
<script type="text/javascript">
import Bus from '../js/bus.js';
export default {
data() {
return {
name: 0
}
},
mounted() {
const vm = this;
// 使用$on事件來接收參數(shù)
Bus.$on('val', (data) => {
console.log(data);
vm.name = data;
});
},
methods: {
getDataFunc() {
this.name++;
}
}
}
</script>
app/index/views/index.vue 代碼改成如下:
<style lang="stylus">
</style>
<template>
<div id="app">
<A></A>
<B></B>
</div>
</template>
<script type="text/javascript">
import A from './a';
import B from './b';
export default {
data() {
return {
}
},
components: {
A: A,
B: B
}
}
</script>
頁面進(jìn)來的時候磕谅,頁面渲染成如下圖所示
當(dāng)點(diǎn)擊 A 組件后的點(diǎn)擊觸發(fā)后私爷,B組件的值變成了4雾棺,如下圖所示:
二:理解使用Vuex
1. 什么是vuex?
Vuex官網(wǎng)的解釋:Vuex是一個專為vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式衬浑。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài)捌浩,并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。
官網(wǎng)的解釋很模糊工秩。其實(shí)在vue組件開發(fā)中尸饺,經(jīng)常會需要將當(dāng)前的組件的數(shù)據(jù)傳遞給其他的組件,父子組件通信的話助币,我們可以采用props+emit 這種方式浪听,如上面的demo方式來傳遞數(shù)據(jù),但是當(dāng)通信雙方不是父子組件甚至根本不存在任何關(guān)系的時候眉菱,或者說一個狀態(tài)需要共享給多個組件的時候馋辈,那么就會非常麻煩,數(shù)據(jù)維護(hù)也相當(dāng)?shù)牟缓镁S護(hù)倍谜,因此就出現(xiàn)了vuex迈螟。它能幫助我們把公用的狀態(tài)抽出來放在vuex的容器中,然后根據(jù)一定的規(guī)則進(jìn)行管理尔崔。vuex采用了集中式存儲管理所有組件的狀態(tài)答毫,并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。
2. 怎么使用vuex季春?
如下demo使用:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script type="text/javascript" src='https://unpkg.com/vue/dist/vue.js'></script>
<script type="text/javascript" src='https://unpkg.com/vuex@3.0.1/dist/vuex.js'></script>
</head>
<body>
<div id='app'></div>
<script type="text/javascript">
Vue.use(Vuex); // 使用vuex
var myStore = new Vuex.Store({
// state是存儲狀態(tài) 定義應(yīng)用狀態(tài)全局的數(shù)據(jù)結(jié)構(gòu)
state: {
name: 'kongzhi',
todoLists: []
},
/*
mutations是提交狀態(tài)修改洗搂,也就是set、get中的set载弄,這是vuex中唯一修改state的方式耘拇,但是不支持異步操作。
每個mutation都有一個字符串的事件類型(type)和一個回調(diào)函數(shù)(handler)
第一個參數(shù)默認(rèn)是state宇攻,外部調(diào)用方式為:store.commit('SET_AGE', 30).
*/
mutations: {
// 新增list
ADDLIST(state, item) {
state.todoLists.push(item);
},
// 刪除list中的項(xiàng)
DELLIST(state, index) {
state.todoLists.splice(index, 1);
},
// 設(shè)置 錯誤提示信息
SETERROR(state, msg) {
state.message = msg;
}
},
/*
getters是從state中派生出狀態(tài)的惫叛。也就是set、get中的get逞刷,它有兩個可選的參數(shù)嘉涌,state和getters,
分別可以獲取state中的變量和其他的getters夸浅。外部調(diào)用的方式:store.getters.todoCount()
*/
getters: {
todoCount(state) {
return state.todoLists.length;
}
},
/*
和上面的mutations類似仑最,但是actions支持異步操作的,外部調(diào)用方式為:store.dispatch('nameAction')
常見的使用是:從服務(wù)器端獲取數(shù)據(jù)帆喇,在數(shù)據(jù)獲取完成后會調(diào)用 store.commit()來更改Store中的狀態(tài)警医。
Action函數(shù)接收一個與store實(shí)列具有相同方法和屬性的context對象,因此我們可以使用 context.commit 提交一個
mutation坯钦,或者通過 context.state 和 context.getters來獲取state和getters
*/
actions: {
addList(context, item) {
if (item) {
context.commit('ADDLIST', item);
context.commit('SETERROR', '');
} else {
context.commit('SETERROR', '添加失敗');
}
},
delList(context, index) {
context.commit('DELLIST', index);
context.commit('SETERROR', '刪除成功');
}
},
/*
modules 對象允許將單一的Store拆分為多個Store的同時保存在單一的狀態(tài)樹中预皇。
*/
modules: {
}
});
new Vue({
el: '#app',
data: {
name: 'init name'
},
store: myStore,
mounted: function() {
console.log(this);
}
})
</script>
</body>
</html>
如上代碼侈玄,new Vuex.store({}) 含義是創(chuàng)建一個Vuex實(shí)列。store是vuex的一個核心方法深啤,字面含義為 '倉庫'的意思拗馒。實(shí)列化完成后,需要注入到vue實(shí)列中溯街,它有五個核心的選項(xiàng)诱桂,state、mutations呈昔、getters挥等、actions和modules。如下圖所示:
3. vuex中如何獲取state的數(shù)據(jù)呢堤尾?
下面我們來注冊一個組件肝劲,那么在組件內(nèi)部中的computed來獲取state的數(shù)據(jù)(computed是實(shí)時響應(yīng)的)。如下代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script type="text/javascript" src='https://unpkg.com/vue/dist/vue.js'></script>
<script type="text/javascript" src='https://unpkg.com/vuex@3.0.1/dist/vuex.js'></script>
</head>
<body>
<div id='app'>
<xxx></xxx>
</div>
<script type="text/javascript">
Vue.use(Vuex); // 使用vuex
var myStore = new Vuex.Store({
// state是存儲狀態(tài) 定義應(yīng)用狀態(tài)全局的數(shù)據(jù)結(jié)構(gòu)
state: {
name: 'kongzhi',
todoLists: []
},
/*
mutations是提交狀態(tài)修改郭宝,也就是set辞槐、get中的set,這是vuex中唯一修改state的方式粘室,但是不支持異步操作榄檬。
每個mutation都有一個字符串的事件類型(type)和一個回調(diào)函數(shù)(handler)
第一個參數(shù)默認(rèn)是state,外部調(diào)用方式為:store.commit('SET_AGE', 30).
*/
mutations: {
// 新增list
ADDLIST(state, item) {
state.todoLists.push(item);
},
// 刪除list中的項(xiàng)
DELLIST(state, index) {
state.todoLists.splice(index, 1);
},
// 設(shè)置 錯誤提示信息
SETERROR(state, msg) {
state.message = msg;
}
},
/*
getters是從state中派生出狀態(tài)的衔统。也就是set鹿榜、get中的get,它有兩個可選的參數(shù)锦爵,state和getters舱殿,
分別可以獲取state中的變量和其他的getters。外部調(diào)用的方式:store.getters.todoCount()
*/
getters: {
todoCount(state) {
return state.todoLists.length;
}
},
/*
和上面的mutations類似险掀,但是actions支持異步操作的沪袭,外部調(diào)用方式為:store.dispatch('nameAction')
常見的使用是:從服務(wù)器端獲取數(shù)據(jù),在數(shù)據(jù)獲取完成后會調(diào)用 store.commit()來更改Store中的狀態(tài)迷郑。
Action函數(shù)接收一個與store實(shí)列具有相同方法和屬性的context對象枝恋,因此我們可以使用 context.commit 提交一個
mutation,或者通過 context.state 和 context.getters來獲取state和getters
*/
actions: {
addList(context, item) {
if (item) {
context.commit('ADDLIST', item);
context.commit('SETERROR', '');
} else {
context.commit('SETERROR', '添加失敗');
}
},
delList(context, index) {
context.commit('DELLIST', index);
context.commit('SETERROR', '刪除成功');
}
},
/*
modules 對象允許將單一的Store拆分為多個Store的同時保存在單一的狀態(tài)樹中嗡害。
*/
modules: {
}
});
// 注冊vue組件 xxx
Vue.component('xxx', {
template: "<div>{{name}}</div>",
computed: {
name: function() {
console.log(this.$store.state);
return this.$store.state.name;
}
},
mounted: function() {
console.log(this);
}
});
new Vue({
el: '#app',
data: {
name: 'init name'
},
store: myStore,
mounted: function() {
console.log(this);
}
})
</script>
</body>
</html>
運(yùn)行效果如下圖所示:
4. 如何對state的數(shù)據(jù)進(jìn)行篩選和過濾。
有時候畦攘,我們需要對state的數(shù)據(jù)進(jìn)行刷選和過濾操作霸妹,比如后臺請求回來的數(shù)據(jù),我們需要進(jìn)行數(shù)據(jù)過濾操作知押,getters就可以了叹螟。具體代碼可以看如下demo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script type="text/javascript" src='https://unpkg.com/vue/dist/vue.js'></script>
<script type="text/javascript" src='https://unpkg.com/vuex@3.0.1/dist/vuex.js'></script>
</head>
<body>
<div id='app'>
<xxx></xxx>
</div>
<script type="text/javascript">
Vue.use(Vuex); // 使用vuex
var myStore = new Vuex.Store({
// state是存儲狀態(tài) 定義應(yīng)用狀態(tài)全局的數(shù)據(jù)結(jié)構(gòu)
state: {
name: 'kongzhi',
todoLists: []
},
/*
mutations是提交狀態(tài)修改鹃骂,也就是set、get中的set罢绽,這是vuex中唯一修改state的方式畏线,但是不支持異步操作。
每個mutation都有一個字符串的事件類型(type)和一個回調(diào)函數(shù)(handler)
第一個參數(shù)默認(rèn)是state良价,外部調(diào)用方式為:store.commit('SET_AGE', 30).
*/
mutations: {
// 新增list
ADDLIST(state, item) {
state.todoLists.push(item);
},
// 刪除list中的項(xiàng)
DELLIST(state, index) {
state.todoLists.splice(index, 1);
},
// 設(shè)置 錯誤提示信息
SETERROR(state, msg) {
state.message = msg;
}
},
/*
getters是從state中派生出狀態(tài)的寝殴。也就是set、get中的get明垢,它有兩個可選的參數(shù)蚣常,state和getters,
分別可以獲取state中的變量和其他的getters痊银。外部調(diào)用的方式:store.getters.todoCount()
*/
getters: {
todoCount(state) {
return state.todoLists.length;
}
},
/*
和上面的mutations類似抵蚊,但是actions支持異步操作的,外部調(diào)用方式為:store.dispatch('nameAction')
常見的使用是:從服務(wù)器端獲取數(shù)據(jù)溯革,在數(shù)據(jù)獲取完成后會調(diào)用 store.commit()來更改Store中的狀態(tài)贞绳。
Action函數(shù)接收一個與store實(shí)列具有相同方法和屬性的context對象,因此我們可以使用 context.commit 提交一個
mutation致稀,或者通過 context.state 和 context.getters來獲取state和getters
*/
actions: {
addList(context, item) {
if (item) {
context.commit('ADDLIST', item);
context.commit('SETERROR', '');
} else {
context.commit('SETERROR', '添加失敗');
}
},
delList(context, index) {
context.commit('DELLIST', index);
context.commit('SETERROR', '刪除成功');
}
},
/*
modules 對象允許將單一的Store拆分為多個Store的同時保存在單一的狀態(tài)樹中冈闭。
*/
modules: {
}
});
// 注冊vue組件 xxx
Vue.component('xxx', {
template: "<div>{{name}}, 總數(shù):{{todoCount}}</div>",
computed: {
name: function() {
console.log(this.$store.state);
return this.$store.state.name;
},
todoCount: function() {
return this.$store.getters.todoCount;
}
},
mounted: function() {
console.log(this);
},
});
new Vue({
el: '#app',
data: {
name: 'init name'
},
store: myStore,
mounted: function() {
console.log(this);
}
})
</script>
</body>
</html>
運(yùn)行效果 如下圖所示
5. mutations操作來改變state數(shù)據(jù)
如上是如何獲取state的數(shù)據(jù)了,那么現(xiàn)在我們使用mutations來改變state的數(shù)據(jù)了豺裆,在Vuex中拒秘,改變狀態(tài)state的唯一方式是通過提交commit的一個mutations,mutations下的對應(yīng)函數(shù)接收第一個參數(shù)state臭猜,第二個參數(shù)為payload(載荷)躺酒,payload一般是一個對象,用來記錄開發(fā)時使用該函數(shù)的一些信息蔑歌。mutations是處理同步的請求羹应。不能處理異步請求的〈瓮溃看如下demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script type="text/javascript" src='https://unpkg.com/vue/dist/vue.js'></script>
<script type="text/javascript" src='https://unpkg.com/vuex@3.0.1/dist/vuex.js'></script>
</head>
<body>
<div id='app'>
<xxx></xxx>
</div>
<script type="text/javascript">
Vue.use(Vuex); // 使用vuex
var myStore = new Vuex.Store({
// state是存儲狀態(tài) 定義應(yīng)用狀態(tài)全局的數(shù)據(jù)結(jié)構(gòu)
state: {
name: 'kongzhi',
todoLists: []
},
/*
mutations是提交狀態(tài)修改园匹,也就是set、get中的set劫灶,這是vuex中唯一修改state的方式裸违,但是不支持異步操作。
每個mutation都有一個字符串的事件類型(type)和一個回調(diào)函數(shù)(handler)
第一個參數(shù)默認(rèn)是state本昏,外部調(diào)用方式為:store.commit('SET_AGE', 30).
*/
mutations: {
// 新增list
ADDLIST(state, item) {
console.log(state.todoLists);
state.todoLists.push(item);
},
// 刪除list中的項(xiàng)
DELLIST(state, index) {
state.todoLists.splice(index, 1);
},
// 設(shè)置 錯誤提示信息
SETERROR(state, msg) {
state.message = msg;
}
},
/*
getters是從state中派生出狀態(tài)的供汛。也就是set、get中的get,它有兩個可選的參數(shù)怔昨,state和getters雀久,
分別可以獲取state中的變量和其他的getters。外部調(diào)用的方式:store.getters.todoCount()
*/
getters: {
todoCount(state) {
return state.todoLists.length;
}
},
/*
和上面的mutations類似趁舀,但是actions支持異步操作的赖捌,外部調(diào)用方式為:store.dispatch('nameAction')
常見的使用是:從服務(wù)器端獲取數(shù)據(jù),在數(shù)據(jù)獲取完成后會調(diào)用 store.commit()來更改Store中的狀態(tài)矮烹。
Action函數(shù)接收一個與store實(shí)列具有相同方法和屬性的context對象越庇,因此我們可以使用 context.commit 提交一個
mutation,或者通過 context.state 和 context.getters來獲取state和getters
*/
actions: {
addList(context, item) {
if (item) {
context.commit('ADDLIST', item);
context.commit('SETERROR', '');
} else {
context.commit('SETERROR', '添加失敗');
}
},
delList(context, index) {
context.commit('DELLIST', index);
context.commit('SETERROR', '刪除成功');
}
},
/*
modules 對象允許將單一的Store拆分為多個Store的同時保存在單一的狀態(tài)樹中擂送。
*/
modules: {
}
});
// 注冊vue組件 xxx
Vue.component('xxx', {
template: "<div>{{name}}, 總數(shù):{{todoCount}} <span @click='addList'>點(diǎn)擊改變總數(shù)</span></div>",
computed: {
name: function() {
return this.$store.state.name;
},
todoCount: function() {
return this.$store.getters.todoCount;
}
},
mounted: function() {
console.log(this);
},
data: function() {
return {
count: 0
}
},
methods: {
addList: function() {
var count = this.count++;
this.$store.commit('ADDLIST', count);
}
}
});
new Vue({
el: '#app',
data: {
name: 'init name'
},
store: myStore,
mounted: function() {
console.log(this);
}
})
</script>
</body>
</html>
如上代碼悦荒,在組件xxx中的template添加代碼 <span @click='addList'>點(diǎn)擊改變總數(shù)</span>,每次點(diǎn)擊的時候嘹吨,會調(diào)用自身內(nèi)部的addList的函數(shù)搬味,然后count自增1,然后會繼續(xù)調(diào)用 this.$store.commit('ADDLIST', count); 來提交 commit的一個mutations蟀拷,因此state.todoLists.push(item);會增加一項(xiàng)數(shù)據(jù)后碰纬,在組件xxx內(nèi)部通過 todoCount 可以實(shí)時獲取到數(shù)據(jù)的改變狀態(tài)。
6. actions操作mutations異步來改變state數(shù)據(jù)
actions可以異步操作问芬,actions提交mutations悦析,通過mutations來提交數(shù)據(jù)的變更。它是異步修改state的狀態(tài)的此衅。
外部調(diào)用方式是 this.$store.dispatch('nameAsyn');
下面我們看如下demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script type="text/javascript" src='https://unpkg.com/vue/dist/vue.js'></script>
<script type="text/javascript" src='https://unpkg.com/vuex@3.0.1/dist/vuex.js'></script>
</head>
<body>
<div id='app'>
<xxx></xxx>
</div>
<script type="text/javascript">
Vue.use(Vuex); // 使用vuex
var myStore = new Vuex.Store({
// state是存儲狀態(tài) 定義應(yīng)用狀態(tài)全局的數(shù)據(jù)結(jié)構(gòu)
state: {
name: 'kongzhi',
todoLists: [],
// 添加count字段
count: 1
},
/*
mutations是提交狀態(tài)修改强戴,也就是set、get中的set挡鞍,這是vuex中唯一修改state的方式骑歹,但是不支持異步操作。
每個mutation都有一個字符串的事件類型(type)和一個回調(diào)函數(shù)(handler)
第一個參數(shù)默認(rèn)是state墨微,外部調(diào)用方式為:store.commit('SET_AGE', 30).
*/
mutations: {
// 新增list
ADDLIST(state, item) {
console.log(state.todoLists);
state.todoLists.push(item);
},
// 刪除list中的項(xiàng)
DELLIST(state, index) {
state.todoLists.splice(index, 1);
},
// 設(shè)置 錯誤提示信息
SETERROR(state, msg) {
state.message = msg;
},
// 異步操作count
COUNTASYNC(state, param) {
console.log(state.count += param);
}
},
/*
getters是從state中派生出狀態(tài)的道媚。也就是set、get中的get翘县,它有兩個可選的參數(shù)最域,state和getters,
分別可以獲取state中的變量和其他的getters锈麸。外部調(diào)用的方式:store.getters.todoCount()
*/
getters: {
todoCount(state) {
return state.todoLists.length;
}
},
/*
和上面的mutations類似镀脂,但是actions支持異步操作的,外部調(diào)用方式為:store.dispatch('nameAction')
常見的使用是:從服務(wù)器端獲取數(shù)據(jù)忘伞,在數(shù)據(jù)獲取完成后會調(diào)用 store.commit()來更改Store中的狀態(tài)狗热。
Action函數(shù)接收一個與store實(shí)列具有相同方法和屬性的context對象钞馁,因此我們可以使用 context.commit 提交一個
mutation虑省,或者通過 context.state 和 context.getters來獲取state和getters
*/
actions: {
addList(context, item) {
if (item) {
context.commit('ADDLIST', item);
context.commit('SETERROR', '');
} else {
context.commit('SETERROR', '添加失敗');
}
},
delList(context, index) {
context.commit('DELLIST', index);
context.commit('SETERROR', '刪除成功');
},
// 設(shè)置延時來演示一下異步提交
addFunc(context, value) {
console.log(context);
setTimeout(function(){
// 異步提交 actions操作Mutations
context.commit('COUNTASYNC', value);
}, 100);
}
},
/*
modules 對象允許將單一的Store拆分為多個Store的同時保存在單一的狀態(tài)樹中匿刮。
*/
modules: {
}
});
// 注冊vue組件 xxx
Vue.component('xxx', {
template:
`<div>
{{name}}, 總數(shù):{{todoCount}}
<span @click='addList'>點(diǎn)擊改變總數(shù)</span>
<button @click='addFuncAnsyc'>點(diǎn)擊我actions改變數(shù)據(jù)</button>
</div>`,
computed: {
name: function() {
return this.$store.state.name;
},
todoCount: function() {
return this.$store.getters.todoCount;
}
},
mounted: function() {
console.log(this);
},
data: function() {
return {
count: 0
}
},
methods: {
addList: function() {
var count = this.count++;
this.$store.commit('ADDLIST', count);
},
addFuncAnsyc: function() {
this.$store.dispatch('addFunc', 1);
}
}
});
new Vue({
el: '#app',
data: {
name: 'init name'
},
store: myStore,
mounted: function() {
console.log(this);
}
})
</script>
</body>
</html>
如上代碼,template中新增代碼 <button @click='addFuncAnsyc'>點(diǎn)擊我actions改變數(shù)據(jù)</button>探颈,點(diǎn)擊后熟丸,觸發(fā)內(nèi)部函數(shù) addFuncAnsyc后,會調(diào)用 this.$store.dispatch('addFunc', 1);調(diào)用來操作actions中的方法伪节,第一個參數(shù)對應(yīng)actions中的方法名光羞,第二個是參數(shù)值,在actions中的函數(shù) addFunc 打印出 context怀大, 如下圖所示:
addFunc函數(shù)會調(diào)用 context.commit('COUNTASYNC', value); 代碼纱兑,會找到 mutations中對應(yīng)的COUNTASYNC,然后會對state數(shù)據(jù)進(jìn)行更改化借。
理解context: context是和 this.$store 具有相同的方法和屬性的對象潜慎。我們可以通過 context.state 和 context.getters來獲取state和getters。
理解dispatch: 它含有異步操作蓖康,含義可以理解為 '派發(fā)'铐炫,比如向后臺提交數(shù)據(jù),可以為 this.$store.dispatch('actions方法名', 值);
三:webpack4+vue+route+vuex 項(xiàng)目架構(gòu)
如上代碼是基本的demo蒜焊,但是在項(xiàng)目中倒信,我們是如何配置的呢?下面再來看看使用store來重構(gòu)項(xiàng)目泳梆,整個目錄架構(gòu)如下:
### 目錄結(jié)構(gòu)如下:
demo1 # 工程名
| |--- dist # 打包后生成的目錄文件
| |--- node_modules # 所有的依賴包
| |--- app
| | |---index
| | | |-- views # 存放所有vue頁面文件
| | | | |-- parent.vue # 父組件
| | | | |-- child.vue # 子組件
| | | | |-- index.vue
| | | |-- components # 存放vue公用的組件
| | | |-- js # 存放js文件的
| | | |-- store # store倉庫
| | | | |--- actions.js
| | | | |--- mutations.js
| | | | |--- state.js
| | | | |--- mutations-types.js
| | | | |--- index.js
| | | |-- app.js # vue入口配置文件
| | | |-- router.js # 路由配置文件
| |--- views
| | |-- index.html # html文件
| |--- webpack.config.js # webpack配置文件
| |--- .gitignore
| |--- README.md
| |--- package.json
| |--- .babelrc # babel轉(zhuǎn)碼文件
在app/index/views 下新建 testvue.vue, 代碼如下:
<template>
<div>
{{name}}, 總數(shù):{{todoCount}}
<span @click='addList'>點(diǎn)擊改變總數(shù)</span>
<button @click='addFuncAnsyc'>點(diǎn)擊我actions改變數(shù)據(jù)</button>
</div>
</template>
<script type="text/javascript">
export default {
data() {
return {
name: '我是空智',
count: 1,
todoLists: []
}
},
created() {
},
computed: {
todoCount: function() {
return this.$store.state.add;
}
},
methods: {
addList() {
var count = this.count++;
this.$store.commit('ADD', count);
},
addFuncAnsyc() {
const obj = {
};
Promise.all([this.$store.dispatch('commonActionGet', ['getPower:COUNTASYNC', obj])]).then((res) => {
});
}
}
}
</script>
模板代碼中 <span @click='addList'>點(diǎn)擊改變總數(shù)</span> 點(diǎn)擊一下 觸發(fā) addList 函數(shù)鳖悠,該函數(shù)會調(diào)用 this.$store.commit('ADD', count); 該方法,commit方法是同步的优妙,它會尋找mutations.js對應(yīng)的 'ADD'函數(shù).
因此乘综,app/index/store/mutations-types.js 代碼可以改成如下:
// 新增list
export const ADD = 'ADD';
// 設(shè)置錯誤提示
export const SETERROR = 'SETERROR';
// 異步操作count
export const COUNTASYNC = 'COUNTASYNC';
app/index/store/mutations.js 代碼改造成如下:
import * as types from './mutations-types';
export default {
[types.ADD] (state, payload) {
state.add = payload;
},
[types.SETERROR] (state, payload) {
state.errors = payload;
},
[types.COUNTASYNC] (state, payload) {
state.counts = payload;
}
}
app/index/store/state.js 改造代碼如下:
export default {
add: 0,
errors: '',
counts: 0
};
app/index/store/actions.js 改造代碼如下, 封裝了常見的ajax中的get和POST方法:
import * as types from './mutations-types';
import Vue from 'vue';
const ajaxHandle = () => {
const buildUrl = (url, params, childParams) => {
let str = '?'
for (const key in params) {
url += str + key + '=' + params[key];
str = '&';
}
if (childParams) {
return url + '/' + childParams;
}
return url;
};
const ajaxGet = (url, fn) => {
let results = null;
Vue.http.get(url, { emulateJSON: true, credentials: true }).then((response) => {
if (response.ok) {
results = response.body;
fn && fn(1, results);
} else {
fn && fn(0, results);
}
}, (error) => {
if (error) {
fn && fn(0, results);
}
});
};
const ajaxGetJSON = (url, fn) => {
let results = null;
Vue.http.get(url, { credentials: true }).then((response) => {
if (response.ok) {
results = response.body;
fn && fn(1, results);
} else {
fn && fn(0, results);
}
}, (error) => {
if (error) {
fn && fn(0, results);
}
});
};
const ajaxPost = (url, params, options, fn) => {
let results = null;
if (typeof options === 'function' && arguments.length <= 3) {
fn = options;
options = {};
}
Vue.http.interceptors.push((request, next) => {
request.credentials = true;
next();
});
Vue.http.post(url, params, { emulateJSON: true }).then((response) => {
if (response.ok) {
results = response.body;
fn && fn(1, results);
} else {
fn && fn(0, results);
}
}, (error) => {
if (error) {
fn && fn(0, results);
}
})
};
const ajaxPostJSON = (url, params, options, fn) => {
let results = null;
if (typeof options === 'function' && arguments.length <= 3) {
fn = options;
options = {};
}
Vue.http.interceptors.push((request, next) => {
request.credentials = true;
next();
});
Vue.http.post(url, params).then((response) => {
if (response.ok) {
results = response.body;
fn && fn(1, results);
} else {
fn && fn(0, results);
}
}, (error) => {
if (error) {
fn && fn(0, results);
}
})
};
return {
buildUrl: buildUrl,
ajaxGet: ajaxGet,
ajaxGetJSON: ajaxGetJSON,
ajaxPost: ajaxPost,
ajaxPostJSON: ajaxPostJSON
}
};
const ah = ajaxHandle();
const prefix = '//xxx.abc.com';
const apiObj = {
API_GET_POWER: prefix + '/xxxx/yyy', // 獲取用戶信息
};
const apiFuncObj = {
getPower: 'API_GET_POWER', // 獲取用戶信息
};
export default {
commonActionPost ({ commit }, payload) {
let url = apiObj[apiFuncObj[payload[0].split(':')[0]]];
const mutationsName = payload[0].split(':')[1];
const params = payload[1] ? payload[1] : {};
return new Promise((reslove, reject) => {
ah.ajaxPost(url, params, (state, results) => {
if (state) {
reslove(results);
} else {
reject();
}
if (mutationsName) {
commit(mutationsName, results);
}
});
});
},
commonActionPostJSON({ commit }, payload) {
let url = apiObj[apiFuncObj[payload[0].split(':')[0]]];
const mutationsName = payload[0].split(':')[1];
const params = payload[1] ? payload[1] : {};
return new Promise((reslove, reject) => {
ah.ajaxPostJSON(url, params, (state, results) => {
if (state) {
reslove(results);
} else {
reject();
}
if (mutationsName) {
commit(mutationsName, results);
}
});
});
},
commonActionGet ({ commit }, payload) {
let url = apiObj[apiFuncObj[payload[0].split(':')[0]]];
const mutationsName = payload[0].split(':')[1];
const params = payload[1] ? payload[1] : {};
const childParams = payload[2] ? payload[2] : '';
url = ah.buildUrl(url, params, childParams);
return new Promise((reslove, reject) => {
ah.ajaxGet(url, (state, results) => {
if (state) {
reslove(results);
} else {
reject();
}
if (mutationsName) {
commit(mutationsName, results);
}
});
});
},
commonActionGetJSON ({ commit }, payload) {
let url = apiObj[apiFuncObj[payload[0].split(':')[0]]];
const mutationsName = payload[0].split(':')[1];
const params = payload[1] ? payload[1] : {};
url = ah.buildUrl(url, params);
return new Promise((reslove, reject) => {
ah.ajaxGetJSON(url, (state, results) => {
if (state) {
reslove(results);
} else {
reject();
}
if (mutationsName) {
commit(mutationsName, results);
}
});
});
}
}
app/index/store/index.js 改造代碼如下
import Vue from 'vue';
import Vuex from 'vuex';
import state from './state';
import mutations from './mutations';
import actions from './actions';
Vue.use(Vuex);
Vue.config.devtools = true;
export default new Vuex.Store({
state,
mutations,
actions
});
然后在我們?nèi)肟谖募?app/index/app.js 引入store即可,如下代碼:
import Vue from 'vue';
// 添加store
import store from './store/index';
// 暫時先用 vue-resource 演示
import VueResource from 'vue-resource';
import Index from './views/index';
// 引入路由
import router from './router';
// Resource
Vue.use(VueResource);
new Vue({
el: '#app',
router: router,
// vue實(shí)列添加store
store: store,
render: h => h(Index)
});