ES6 课程概述⑦
文章目录
- Vuex_State
- 安装
- 使用
- State
- 在 Vue 组件中获得 Vuex 状态
- mapState 辅助函数
- Vuex_Getter
- 通过属性访问
- 通过方法访问
- mapGetters 辅助函数
- Vuex_Mutation
- 在组件中提交 Mutation
- 提交载荷(Payload)
- 对象风格的提交方式
- 使用常量替代 Mutation 事件类型
- Mutation 需遵守 Vue 的响应规则
- 表单处理
- 双向绑定的计算属性
- Mutation 必须是同步函数
- 严格模式
- 开发环境与发布环境
- Vuex_Action
- 分发 Action
- 组合 Action
- Vuex 管理模式
- Vuex_Module
- 命名空间
- 模块的局部状态
- VUE 中$refs 的基本用法
- JSON.parse()
- localStorage
Vuex_State
Vuex 是 vue 的状态管理工具,为了更方便的实现多个组件共享状态。
安装
npm install vuex --save
使用
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 0,
},
});
new Vue({
store,
});
State
单一状态树,使用一个对象就包含了全部的应用层级状态。
在 Vue 组件中获得 Vuex 状态
Vuex 通过 store 选项,提供了一种机制将状态从跟组件“注入”到每一个子组件中(调用 Vue.use(Vuex))。
通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问。
<div class="home">{{ $store.state.count }}</div>
mapState 辅助函数
当一个组件需要获取多个状态时,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性:
import { mapState } from 'vuex';
computed: {
...mapState(['count']),
},
使用不同的名字:
computed: {
...mapState({
storeCount: state => state.count,
// 简写
storeCount: 'count', // 等同于 state => state.count
}),
},
Vuex_Getter
store 的计算属性。getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Getter 接收 state 作为其第一个参数、getters 作为其第二个参数。
getters: {
doubleCount (state) {
return state.count * 2;
}
}
通过属性访问
Getter 会暴露为 store.getters 对象:this.$store.getters.doubleCount
通过方法访问
也可以让 getter 返回一个函数,来实现给 getter 传参
getters: {
addCount: (state) => (num) => state.count + num;
}
this.$store.addCount(3);
mapGetters 辅助函数
import { mapsGetters } from "vuex";
export default {
computed: {
...mapGetters(["doubleCount", "addCount"]),
},
};
如果你想将一个 getter 属性另取一个名字,使用对象形式:
mapGetters({
// 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
storeDoubleCount: "doubleCount",
});
Vuex_Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
const store = new Vuex.Store({
state: {
count: 1,
},
mutations: {
increment(state) {
// 变更状态
state.count++;
},
},
});
不能直接调用一个 mutation handler。这个选项更像是事件注册:“当触发一个类型为increment
的 mutation 时,调用次函数。”:
this.$store.commit("increment");
在组件中提交 Mutation
除了在组件中使用 this.$store.commit('xxx')
提交 mutation 之外,还可以使用 mapMutations 辅助函数:
import { mapMutations } from "vuex";
export default {
// ...
methods: {
...mapMutations([
"increment", // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
]),
...mapMutations({
add: "increment", // 将 `this.add()` 映射为 `this.$store.commit('increment')`
}),
},
};
提交载荷(Payload)
你可以向 store.commit 传入额外的参数,即 mutation 的载荷(payload):
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit("increment", 10);
在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit("increment", {
amount: 10,
});
对象风格的提交方式
提交 mutation 的另一种方式是直接使用包含 type 属性的对象:
store.commit({
type: "increment",
amount: 10,
});
当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变:
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
使用常量替代 Mutation 事件类型
把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:
// mutation-types.js
export const COUNT_INCREMENT = "COUNT_INCREMENT";
// store.js
import Vuex from 'vuex'
import { COUNT_INCREMENT } from './mutation-types'
const store = new Vuex.Store({
state: { ... },
mutations: {
[COUNT_INCREMENT] (state) {
// ...
}
}
})
用不用常量取决于自己,在需要多人协作的大型项目中,这会很有帮助。
Mutation 需遵守 Vue 的响应规则
既然 Vuex 的 store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:
- 最好提前在你的 store 中初始化好所有所需属性。
- 当需要在对象上添加新属性时,你应该
- 使用 Vue.set(obj, ‘newProp’, 123), 或者
- 以新对象替换老对象。例如,利用对象展开运算符我们可以这样写:
state.obj = { ...state.obj, newProp: 123 };
表单处理
在 Vuex 的 state 上使用 v-model 时,由于会直接更改 state 的值,所以 Vue 会抛出错误。
如果想要使用双向数据的功能,就需要自己模拟一个 v-model: :value=“msg” @input=“updateMsg”。
双向绑定的计算属性
上面的做法,比 v-model 本身繁琐很多,所以我们还可以使用计算属性的 setter 来实现双向绑定:
<input v-model="msg" />
computed: {
msg: {
get () {
return this.$store.state.obj.msg;
},
set (value) {
this.$store.commit(UPDATE_MSG, { value });
}
}
}
Mutation 必须是同步函数
要记住 mutation 必须是同步函数。why?
mutations: {
[COUNT_INCREMENT] (state) {
setTimeout(() => {
state.count ++;
}, 1000)
},
}
执行上端代码,我们会发现更改 state 的操作是在回调函数中执行的,这样会让我们的代码在 devtools 中变的不好调试:当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用,任何在回调函数中进行的状态的改变都是不可追踪的。
严格模式
开启严格模式,仅需在创建 store 的时候传入 strict: true:
const store = new Vuex.Store({
// ...
strict: true,
});
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
开发环境与发布环境
不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更,要确保在发布环境下关闭严格模式,以避免性能损失。
const store = new Vuex.Store({
// ...
strict: process.env.NODE_ENV !== "production",
});
Vuex_Action
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters:
const store = new Vuex.Store({
state: {
count: 0,
},
mutations: {
increment(state) {
state.count++;
},
},
actions: {
increment(context) {
context.commit("increment");
},
},
});
分发 Action
store.dispatch("increment");
虽然和 mutation 差不多,但是在 action 中,可以执行异步操作,但是 mutation 中不行!!!
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
组合 Action
Action 通常是异步的,那么如何知道 action 什么时候结束呢?
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
store.dispatch("actionA").then(() => {
// ...
});
Vuex 管理模式
Vuex_Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter。
modules: {
a, b;
}
- 获取 state:this.$store.state.moduleName.xxx
- 获取 getter:this.$store.getters.xxx
- 提交 mutation:this.$store.commit(‘xxx’);
- 分发 action:this.$store.dispatch(‘xxx’);
- 可以通过 mapXXX 的方式拿到 getters、mutations、actions,但是不能拿到 state,如果想通过这种方式获得 state,需要加命名空间。
命名空间
可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。
- 获取 state:this.$store.state.moduleName.xxx
- 获取 getter:this.$store.[‘moduleName/getters’].xxx
- 提交 mutation:this.$store.commit(‘moduleName/xxx’);
- 分发 action:this.$store.dispatch(‘moduleName/xxx’);
- 可以通过 mapXXX 的方式获取到 state、getters、mutations、actions。
模块的局部状态
对于模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象。
同样,对于模块内部的 action,局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState。
对于模块内部的 getter,根节点状态会作为第三个参数暴露出来。
VUE 中$refs 的基本用法
ref 有三种用法:
1、ref 加在普通的元素上,用 this.$refs.(ref 值) 获取到的是 dom 元素
2、ref 加在子组件上,用 this. r e f s . ( r e f 值)获取到的是组件实例,可以使用组件的所有方法。在使用方法的时候直接 t h i s . refs.(ref值) 获取到的是组件实例,可以使用组件的所有方法。在使用方法的时候直接this. refs.(ref值)获取到的是组件实例,可以使用组件的所有方法。在使用方法的时候直接this.refs.(ref 值).方法() 就可以使用了。
3、如何利用 v-for 和 ref 获取一组数组或者 dom 节点
应注意的坑:
1、如果通过 v-for 遍历想加不同的 ref 时记得加 :号,即 :ref =某变量 ;
这点和其他属性一样,如果是固定值就不需要加 :号,如果是变量记得加 :号。(加冒号的,说明后面的是一个变量或者表达式;没加冒号的后面就是对应的字符串常量(String))
2、通过 :ref =某变量 添加 ref(即加了:号) ,如果想获取该 ref 时需要加 [0],如 this. r e f s [ r e f s A r r a y I t e m ] [ 0 ] ;如果不是 : r e f = 某变量的方式而是 r e f = 某字符串时则不需要加,如 t h i s . refs[refsArrayItem] [0];如果不是:ref =某变量的方式而是 ref =某字符串时则不需要加,如this. refs[refsArrayItem][0];如果不是:ref=某变量的方式而是ref=某字符串时则不需要加,如this.refs[refsArrayItem]。
1、ref 需要在 dom 渲染完成后才会有,在使用的时候确保 dom 已经渲染完成。比如在生命周期 mounted(){} 钩子中调用,或者在 this.$nextTick(()=>{}) 中调用。
2、如果 ref 是循环出来的,有多个重名,那么 ref 的值会是一个数组 ,此时要拿到单个的 ref 只需要循环就可以了。
例子 1:
添加 ref 属性
<div id="app">
<h1 ref="h1Ele">这是H1</h1>
<hello ref="ho"></hello>
<button @click="getref">获取H1元素</button>
</div>
methods: { getref() { 表示从 $refs对象 中, 获取 ref 属性值为: h1ele
DOM元素或组件 console.log(this.$refs.h1Ele.innerText);
this.$refs.h1ele.style.color = 'red';// 修改html样式
console.log(this.$refs.ho.msg);// 获取组件数据
console.log(this.$refs.ho.test);// 获取组件的方法 } } 例子2: Vue代码:
<el-table
@sort-change="sortChange"
ref="multipleSelection"
border
:data="valueDryGoodTableData"
style="width: 100%"
>
<el-table-column
align="left"
prop="title"
label="标题"
min-width="80%"
sortable="custom"
>
<template slot-scope="scope">
<a
target="_blank"
:class="scope.row.titleClicked?'titleClicked':''"
class="hoverHand bluetext"
v-html="scope.row.title"
@click="titleClick(scope.row.articleUrl,scope.$index)"
>
</a>
</template>
</el-table-column>
<el-table-column
align="left"
prop="releaseTime"
label="发布日期"
min-width="11%"
sortable="custom"
></el-table-column>
<el-table-column align="center" label="操作" min-width="9%">
<template slot-scope="scope">
<span class="operatoryTools">
<i
title="取消收藏"
v-if="scope.row.isFavour"
@click="favoriteOperating(scope.row.id, scope.$index)"
class="hoverHand iconStyle el-icon-star-on"
></i>
<i
title="收藏"
v-else
@click="favoriteOperating(scope.row.id, scope.$index)"
class="hoverHand iconStyle el-icon-star-off"
></i>
<i
title="分享"
@click.stop="showShareOperation(scope.$index)"
class="shareTarg iconfont"
></i
>
<div class="share" v-if="scope.row.showShare">
<img
class="hoverHand shareItem"
title="分享到微博"
@click="shareItem('sina',$event);"
src="@/images/WEIBO.png"
/>
<img
class="hoverHand shareItem"
title="分享到微信"
@click.stop="shareItem('wx',$event);"
src="@/images/WEIXIN.png"
/>
<img
class="hoverHand shareItem"
title="分享到QQ"
@click="shareItem('qq',$event);"
src="@/images/QQ.png"
/>
</div>
<div v-show="scope.row.erweimaShare" class="erweima_share"></div>
<div v-show="scope.row.erweimaShare1" class="erweima_share1"></div>
</span>
</template>
</el-table-column>
</el-table>
JS 代码: //点击清空条件,调用该方法 emptyAndSearch(){//清空条件方法
//清空树选中状态 this.clearTreeNodeCheck(['tree']); //清除排序
this.$refs.multipleSelection.clearSort(); //设置分页参数 this.queryParam = {
startRow : 1, pageSize : 20, condition:{ } } //分页查询调用
this.confirmSearch('statistics'); //设置清空条件为false
this.$store.commit('valueDryGoodDatas/SET_CLEAR_ALL',false); }
例子 3: Vue 代码:
<el-form-item
ref="goodPicInfoFormpicUrl"
:label="$t('许可证证照')"
class="is-required"
prop="picUrl"
>
<el-upload
:show-file-list="false"
:http-request="uploadImg"
:data="certImgform"
action=""
class="avatar-uploader"
>
<img v-if="queryFrom.picUrl" :src="queryFrom.picUrl" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon" />
</el-upload>
<el-button
type="primary"
plain
size="mini"
@click="viewPcPic(queryFrom.picUrl)"
>{{ $t('查看') }}</el-button
>
</el-form-item>
JS 代码: //获取元素清除验证 this.$refs.goodPicInfoFormpicUrl.clearValidate()
一般来讲,想获取 INPUT 框,首先在获取 DOM 元素,需
document.querySelector(".input1")获取这个 dom 节点,然后在获取 input1 的值。
但是用 ref 绑定之后,我们就不需要在获取 dom 节点了,直接在上面的 input 上绑定
input1,然后$refs 里面调用就行。 然后在 javascript
里面这样调用:this.$refs.input1 这样就可以减少获取 dom 节点的消耗了
JSON.parse()
JSON.parse()
JSON 通常用于与服务端交换数据。
在接收服务器数据时一般是字符串。
我们可以使用 JSON.parse() 方法将数据转换为 JavaScript 对象。
语法
JSON.parse(text[, reviver])
参数说明:
text:必需, 一个有效的 JSON 字符串。
reviver: 可选,一个转换结果的函数, 将为对象的每个成员调用此函数。
JSON 解析实例
例如我们从服务器接收了以下数据:
{ "name":"runoob", "alexa":10000, "site":"www.dc3688.com" }
我们使用 JSON.parse() 方法处理以上数据,将其转换为 JavaScript 对象:
var obj = JSON.parse('{ "name":"runoob", "alexa":10000, "site":"www.dc3688.com" }');
localStorage
HTML5 的本地存储 API 中的 localStorage 与 sessionStorage 在使用方法上是相同的,区别在于 sessionStorage 在关闭页面后即被清空,而 localStorage 则会一直保存。我们这里以 localStorage 为例,简要介绍下 html5 的本地存储,并针对如遍历等常见问题作一些示例说明。 localStorage 是 Html5 本地存储的 API,使用键值对的方式进行存取数据,存取的数据只能是字符串。不同浏览器对该 API 支持情况有所差异,如使用方法、最大存储空间等。
存储方式:以键值对(Key-Value)的方式存储字符串。
主要应用:购物车、客户登录、游戏存档。。。
可储存的数据类型:数组,图片,json,样式,脚本。。。(只要是能序列化成字符串的内容都可以存储)
储存地址:C:\Users\15014\AppData\Local\Google\Chrome\User Data\Default\Local Storage(不同电脑不一样,需要打开隐藏文件显示,但是在C盘搜索localStorage就能搜出这个文件夹。)
localStorage的优势
1、localStorage拓展了cookie的4K限制
2、localStorage会可以将第一次请求的数据直接存储到本地,这个相当于一个5M大小的针对于前端页面的数据库,相比于cookie可以节约带宽,但是这个却是只有在高版本的浏览器中才支持的
localStorage的局限
1、浏览器的大小不统一,并且在IE8以上的IE版本才支持localStorage这个属性
2、目前所有的浏览器中都会把localStorage的值类型限定为string类型,这个在对我们日常比较常见的JSON对象类型需要一些转换
3、localStorage在浏览器的隐私模式下面是不可读取的
4、localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡
5、localStorage不能被爬虫抓取到
localStorage与sessionStorage的唯一一点区别就是localStorage属于永久性存储,而sessionStorage属于当会话结束的时候,sessionStorage中的键值对会被清空
这里我们以localStorage来分析
localStorage的使用
首先在使用localStorage的时候,我们需要判断浏览器是否支持localStorage这个属性
if(!window.localStorage){
alert("浏览器支持localstorage");
return false;
}else{
//主逻辑业务
}
localStorage官方提供了四个方法来辅助我们进行对本地存储做相关操作。
(1)localStorage.setItem(键名,键值)在本地客户端存储一个字符串类型的数据,其中,第一个参数“键名”代表了该数据的标识符,而第二个参数“键值”为该数据本身。如:
localStorage.setItem("name", "张三"); //存储键名为name和键值为"张三"的数据到本地
localStorage.setItem("age", "28"); //存储键名为age和键值为"28"的数据到本地
(2)localStorage.getItem(键名) 读取已存储在本地的数据,通过键名作为参数读取出对应键名的数据。如:
var data = localStorage.getItem("name");
alert(data);//张三
(3)localStorage.removeItem(键名)移除已存储在本地的数据,通过键名作为参数删除对应键名的数据。如:
var data2 = localStorage.removeItem("name");//从本地存储中移除键名为name的数据
alert(data2); //undefined
(4)localStorage.clear() 移除本地存储所有数据。如:
localStorage.clear() 移除本地存储所有数据。如:
localStorage.clear(); //保存着的"age/28"和"name/张三"的键/值对也被移除了,所有本地数据拜拜
(5)另外,sessionStorage中的四个函数与以上localStorage类的函数用法基本一致,就不再详解。
下面是一个小实例:
<script type="text/javascript">
localStorage.setItem("name", "张三");
localStorage.setItem("age", "28");
verify(); //验证本地存储
localStorage.removeItem("name");
verify(); //验证name是否存在
localStorage.clear();
verify(); //验证name和age是否存在
//自定义验证函数,验证name和age的数据是否存在
function verify(){
var type = localStorage.getItem("name");
var price = localStorage.getItem("age");
type = type ? type : '不存在';
price = price ? price : '不存在';
alert( "name: " + type + "\n\n" + "age: " + price );
}
</script>
复制代码
其实
localStorage的写入和读取 有三种方法,这里就一一介绍一下
if(!window.localStorage){
alert("浏览器支持localstorage");
}else{
var storage=window.localStorage;
//写入a字段
storage["a"]=1;
//写入b字段
storage.b=1;
//写入c字段
storage.setItem("c",3);
console.log(typeof storage["a"]);
console.log(typeof storage["b"]);
console.log(typeof storage["c"]);
//第一种方法读取
var a=storage.a;
console.log(a);
//第二种方法读取
var b=storage["b"];
console.log(b);
//第三种方法读取
var c=storage.getItem("c");
console.log(c);
}
注意到,刚刚存储进去的是int类型,但是打印出来却是string类型,这个与localStorage本身的特点有关,localStorage只支持string类型的存储。
localStorage的删除
1、 将localStorage中的某个键值对删除
localStorage.setItem("c",3);
console.log(localStorage.getItem('c'));
localStorage.removeItem("c");
console.log(localStorage.getItem('c'));
2、 将localStorage的所有内容清除
storage.clear();
localStorage的键获取
var storage=window.localStorage;
storage.a=1;
storage.setItem("c",3);
for(var i=0;i<storage.length;i++){
var key=storage.key(i);
console.log(key);
}
localStorage其他注意事项
一般我们会将JSON存入localStorage中,但是在localStorage会自动将localStorage转换成为字符串形式
这个时候我们可以使用JSON.stringify()这个方法,来将JSON转换成为JSON字符串,
示例:
var data={
name:'xiecanyong',
sex:'man',
hobby:'program'
};
var json_string=JSON.stringify(data);
localStorage.setItem("data",d);
console.log(localStorage.data);
//将JSON字符串转换成为JSON对象输出
var json_str=localStorage.getItem("data");
var jsonObj=JSON.parse(json_str);
console.log(typeof jsonObj);
读取之后要将JSON字符串转换成为JSON对象,使用JSON.parse()方法