ES6 课程概述⑦

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"
          >&#xe678;</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()方法
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值