一、iview 框架的使用
安装依赖:
npm install --save iview
打包时的入口文件,main.js 引用使用:
import iview from "iview";
Vue.use(iview);
为了提高 webpack 打包的速度,我们把 CSS 文件给提取出来放到 index.html 里面,使用 link 标签引入。我们的找到 iview 框架,CSS 样式所在的位置,它的位置就在 node_modules\iview
,在这个文件夹里面找到 styles 这个文件夹,复制到 index.html 同级目录下,之所以要整个样式文件夹是因为里面还有内置的字体,这时打开 index.html 引入这个样式表。
index.html :
<link rel="stylesheet" href="./styles/iview.css">
到此,配置全部完成。
二、Vuex 安装
每一个 Vuex 应用的核心就是 store(仓库)。“store” 就是一个容器,它包含着你的应用中大部分的状态 (state),可以简单理解为 const store = {"state":{a:1,b:2}}
这种简单的对象映射关系。
Vuex 和单纯的全局对象有以下两点不同:
- Vuex 的状态存储是响应式的。state 数据更新组件响应的会自动更新。
- 不能直接改变 store 中的 state,只能使用 commit 或mutation 更新。
安装依赖:
npm install --save vuex
改变入口文件 main.js:
import Vue from "vue";
import App from "./App";
import Vuex from "vuex";
import iview from 'iview';
import store from "./store/index";
// Vuex作为插件使用
Vue.use(Vuex);
// iview作为插件使用
// 子组件可直接使用iview里面的组件
Vue.use(iview);
new Vue({
el:"#app",
render(h){
return h(App);
},
store:new Vuex.Store(store)
});
最后使用此 store:new Vuex.Store(store)
代码,就能做到子组件均可访问 store 里面值。
三、Vuex 的 countstore 和 index 文件
countstore.js 文件里面的配置是特别像 react 的 dva。这个文件里面主要负责数据,和监听。一共由四部分组成。
countstore.js
export default {
// 命名空间
namespaced: true,
// 数据
state: {
a: 10
},
// 所有可能对数据的改变
mutations: {
// 同步
// commit触发
add(state,action){
// state是数据,action表示载荷
console.log("我是" + action.palyload);
state.a ++;
}
},
actions: {
// 异步
// dispatch触发
ADD({state,commit},action) {
console.log("我是" + action.palyload);
setTimeout(function(){
commit("add");
},2000);
}
}
};
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
index.js 文件里面的内容,是为了防止由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就变得相当臃肿,不利于管理。为了解决以上问题,和使模块具有更高的封装度和复用性,Vuex 允许我们将 store 分割成模块(module)可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。
import countstore from "./countstore.js";
export default {
modules : {
countstore
}
}
这时引入入口文件。通过 store:new Vuex.Store(store)
这个代码即可使用,子组件可以通识状态容器。
四、加法器和延迟加法器
利用上面配置好的 iview 框架和 vuex 来做个加法器。App.vue 组件里面的业务代码:
<template>
<div>
<h1>{{$store.state.countstore.a}}</h1>
<Button @click = "$store.commit('countstore/add',{'palyload':'同步载荷'})">同步加一</Button>
<Button @click = "$store.dispatch('countstore/ADD',{'palyload':'异步载荷'})">异步加一</Button>
</div>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
</style>
此时浏览器运行的效果:
此处需要注意 commit 和 dispatch 还有一种提交方式,功能是一样的只是写法和上面稍有不同:
<Button @click = "$store.commit({type:'countstore/add','palyload':'同步载荷'})">同步加一</Button>
<Button @click = "$store.dispatch({type:'countstore/ADD','palyload':'异步载荷'})">异步加一</Button>
知识普及
Vuex 是什么?
如果你不会 React 的 redux 看完上图就行了。Flux 的原理是啥我们看看:
Flux将一个应用分成四个部分。
Flux 的最大特点,就是数据的"单向流动"。
Vuex 出现主要是为了解决:
- 多个组件共享状态时,单向数据流的简洁性很容易被破坏:
- 多个视图依赖于同一状态。
造成代码不好维护。
来自不同视图的行为需要变更同一状态。
看看下面在入口文件配置的 Vuex 运行时会输出多少?
import Vue from "vue";
import App from "./App.vue";
import Vuex from "vuex";
Vue.use(Vuex);
const store = new Vuex.Store(
{
state:{
name:"Condor Hero"
}
}
);
console.log(store.state.name)
new Vue({
el:"#app",
render(h){
return h(App);
},
store
});
console.log(store.state.name)
输出 Condor Hero
;
认识同步异步:
const store = new Vuex.Store(
{
state:{
name:"Condor Hero",
age:18
},
mutations:{
GetName(state,action){
console.log(action);
}
},
actions:{
GetAge({state},action){
console.log(action)
}
}
}
);
store.commit("GetName","我是一个A");
store.dispatch("GetAge","我是一个B");
合并
const store = new Vuex.Store(
{
modules:{
student:{
state:{
name:"Condor Hero",
age:18
},
mutations:{
GetName(state,action){
console.log(action);
}
},
actions:{
GetAge({state},action){
console.log(action)
}
}
}
}
}
);
console.log(store.state.student.name)
store.commit("GetName","我是一个A");
store.dispatch("GetAge","我是一个B");
计算属性:
const store = new Vuex.Store(
{
state:{
value: 10
},
getters:{
doneTodos(state){
return state.value;
}
}
}
);
console.log(store.getters.doneTodos);//10
https://vuex.vuejs.org/zh/guide/hot-reload.html
安装Vue devtools 调试工具:首先必须能访问外网。
当当当,安装完成。 另外需要知道的是没有通过 commit 和 dispatch 改变 state 里面的变量,devtools 是检测不到的。例如这种直接加加的例子:
<el-button @click="$store.state.a++" >按我加一</el-button>
OK,到现在我们使用 Vuex 里面的东西基本都是这样使用的:
this.$store.commit();
this.$store.dispatch()
this.$sotre.getter.xxx
this.$store.state.xxx
看看吧,想要操作 Vuex 竟然要写那么长得字符串,这还是没加上命名空间的,如果加上又得长一节(namespaced:true;
为 modules服务)。怎么解决这个书写黑洞,Vuex 提供了辅助函数,大大简化了这个过程,下面来看看Vuex 的四个辅助函数。它们分数别是:
...mapState 映射为 this.$store.state.xxxx
...mapGetters 映射为 this.$store.getters.xxxx
...mapActions 映射为 this.$store.dispatch("xxx",n);
...mapMutations 映射为 this.$store.commit("xxx",n);
其中 mapState 和 mapGetters 写在 computed 里面,mapActions 和 mapMutations 写在methods里面,想想也是很合理的,computed 和 methods 不同有两点,一是 computed 不能传参,二是 computed 有缓存,函数的速度比 methods 里面的更快。这两点恰好也是符合 mapState 和 MapGetters 的,剩下的两个特性自然也是属于 methods 的。
先来看 mapState 的用法:
<template>
<div>
<el-button>{{a}}</el-button>
<el-button>{{$store.state.a}}</el-button>
<el-button @click="$store.state.a++" >按我加一</el-button>
</div>
</template>
<script>
import {mapState} from "Vuex";
export default {
computed:{
...mapState(["a"])
},
methods:{
}
}
</script>
... 的意思就是将State里的数据全部展开,括号里的参数就表示你从展开中的数据拿指定的数据放到当前的computed 之中。这种参数是数组的方式有弊端,如果想对数据进行处理的,无法处理,所以一般里面都写成对象:
computed:{
...mapState({
count:state=>state.a
})
}
现在把计数器每次增加的间隔计为 10:
<template>
<div>
<el-button>{{count}}</el-button>
<el-button @click="$store.state.a++" >按我加一</el-button>
</div>
</template>
<script>
import {mapState} from "Vuex";
export default {
computed:{
...mapState({
count:state=>{
state.a+=10;
return state.a;
}
})
},
methods:{
}
}
</script>
猜猜看这个结果输出是多少?
记住我们 的 mapState 通过 ... 语法其实就是把 Vuex 的state拿过来了,所以我们在组件里面进行改写,相应的 Vuex 里面的 state 也发生改变,所以组件一上来就会调用 count 一次,a 这时是 20,当我们 点击 的时候, Vuex 里面的变量 a 先自增一次,变为 ,31。
mapState既然是个对象,我们就可以更加激进的这样写,省的再去强制结构了:
computed:mapState(["a"])
或者这样
computed:Object.assign(mapState(["a"]),mapGetters(["sum"]))
注意:只有mapState能用对象里面跟函数的方式,而其它的辅助函数,后面只能对象里面跟字符串;
再来看看 mapGetters 的使用:这时 a =2
Vuex 里面的 getters :
getters:{
sum(state){
return state.a ** state.a;
}
}
在组件里面使用:
<template>
<div>
<el-button>{{sum}}</el-button>
</div>
</template>
<script>
import {mapGetters} from "Vuex";
export default {
computed:{
...mapGetters(["sum"])
}
}
</script>
得到的结果是:
改写成对象的方法:
computed:{
...mapGetters({
getsum:"sum"
})
}
再来看看 mapActions 的用法:
组件里面:
<template>
<div>
<el-button>{{a}}</el-button>
<el-button @click="delay({'name':'Condor Hero'})">延迟三秒加十</el-button>
</div>
</template>
<script>
import {mapActions,mapState} from "Vuex";
export default {
computed:{
...mapState(["a"]),
},
methods:{
...mapActions(["delay"])
}
}
</script>
Vuex 里面的 actions:
actions: {
delay(context,payload){
console.log(`负载可以直接传,我能接收到,你传过来的东西是${payload.name}`)
setTimeout(()=>{
context.state.a+=10;
},3000);
}
}
得到结果如下:
并且成功打印出来:
负载可以直接传,我能接收到,你传过来的东西是Condor Hero
Action函数的第一个参数和Vuex其他里面的函数不同,Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
再来看看同步的 mutation :
组件中使用:
<template>
<div>
<el-button>{{a}}</el-button>
<el-button @click="increment({'name':'increment'})">同步加十</el-button>
</div>
</template>
<script>
import {mapMutations,mapState} from "Vuex";
export default {
computed:mapState(["a"]),
methods:mapMutations(["increment"])
}
</script>
对象的写法,请仿照上个 action 写。
Vuex 中使用
mutations: {
increment(state,payload){
state.a += 10;
console.log(payload.name);
}