vuex 要点 -- 原创

frontend/vuejs/vuex/vuex_banner

vuex是一个状态管理模式,通过用户的actions触发事件,然后通过mutations去更改数据(你也可以说状态啦 -> state),最后通过getters对状态进行获取,更改页面展示的内容。哈哈 😄 ,详细的内容请接着往下看,如有不妥请文末留言啊。原创文章,转载请注明出处。

原文请戳传送门

注意 ⚠️ 文章中涉及到项目代码是使用Vue官方提供的脚手架vue-cli进行搭建的,如果看者感兴趣,可以自行用vue-cli搭建项目,并进行代码的验证。

Vuex是什么

官网介绍:Vuex是一个专门为Vuejs应用程序开发的状态管理模式。(类似react的redux)。Vuex采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex在构建中大型的应用比较适用,小型的应用用组件之间的通信就可以了,小型应用用上Vuex就显得比较臃肿了。

Vuex的安装

因为自己是使用npm来辅助开发的,所以我也只说下通过npm安装Vuex的方法。其他的安装方法,请戳传送门

进入你项目的根目录,然后执行:

$ npm install vuex --save 
或
$ npm install vuex --save-dev

然后在store主入口的javascript文件,一般是store/index.js中通过use进行引用,前提是你已经安装了vue :

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

为了方便在各个组件中使用store,需要在程序的根组件中将其注入到每个子组件。我们需要在实例化Vue的时候将store引入(实例化Vue的文件一般是main.js主入口文件)。

import Vue from 'vue'
import store from '/path/to/store/index.js'

const initApp =new Vue({
        store: store
    }).$mount('#app')

核心概念

在使用Vuex进行开发的过程中,你可以理解核心的概念只有StateActionMutation三个,就像本文章开篇给出的截图流程那样简单明了。但是,我们使用Vuex开发一个应用,肯定是想要方便管理等等。这里自己按照五个核心概念来谈谈,五个核心概念也是官网推荐使用的。Vuex的五个核心概念除了上面指出的三个之外,还包括GetterModule两个。先一句话来概括下它们 :

  • State : 数据源的存放地

  • Getter : store的计算属性

  • Mutation : 处理数据逻辑,使得数据和视图分离(同步)

  • Action : 类似Mutation(异步),改变状态的话,还得触发Mutation

  • Module : 将store分解成模块

下面来详细讲解各个核心概念咯 😊

State

Vuex是使用单一状态树,一个对象就包含了全部的应用层级状态。这也就表明,一个应用仅仅包含一个store的实例。

状态State对应于Vue对象中的data,因为两者是对应的关系,所以在这里可以称状态==数据的。如下代码指出:

<script>
    export default {
        name: '',
        data() { // state对应的地方
            return {
                ...
            }
        }
    }
</script>

State里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新。也就是说数据和视图是同步的。

局部状态

虽然说VuexStore仓库让我们统一管理数据变得更加方便,但是代码一多也会变得冗长和不直观。有些组件的数据是自己严格使用,我们可以将state放在组件自身,作为局部数据,专供此组件使用。比如现在只想在一个组件中使用emotion: happiness,那就不必要在storestate中进行定义了,只在本组件初始化就行了:

data () {
    return {
        emotion: 'happiness'
    }
}

获取状态

Vue组件中获取store中的数据(状态),最直接的就是通过计算属性获取。因为在上面我将store注册到根组件上了,所以在这里直接通过this.$store就可以调用了。比如我获取状态(state)中的count: 100 :

computed: {
    count: function (){
        return this.$store.state.count;
    }
}

mapState辅助函数

mapState辅助函数把全局的State映射到当前组件computed计算属性中,即是帮助我们生成计算属性。简化我们的代码操作,不需要使用this.$store.state获取了。以上面状态(state)中的count: 100为例子 :

import { mapState } from 'vuex' // 注意别漏了引入
export default {
    computed: 
    mapState({
        count: state => state.count
    }),
}

Getter

上面的state中我们了解到,在store仓库里,state是用来存储数据的。在多个组件中要进行使用同一种状态的话,对数据进行简单操作,我们可以通过在组件的computed中进行获取this.$store.state.theDataName。简单操作没问题,但是,我们进行其他的操作,比如过滤操作,我们就得写一堆的代码 :

computed: {
    filterData: function () {
        this.$store.state.theDataName.filter(function(item){
            // do something ...
        })
    }
}

然后在每个组件中复制这一大堆的代码,或者你单独新建一个文件把代码写进入,每个组件都引入(如果你不觉得很麻烦的话)。

Getter可以把组件中共享状态抽取出来,这也是Getter存在的意义。我们可以认为,GetterStore的计算属性。

如何使用Getter

为了方便管理,需要一个单独的getters.js的文件,假如已经有对数据进行过滤的函数了:

export default {
    filterDatas (state,getter,rootState) {
        // do something ...
    }
}

那么只要在相关的组件的computed中引入就可以了,是不是很方便啊 :

computed: {
   filterItems: function () {
       return this.$store.getters.filterDatas;
   }
}

mapGetters辅助函数

mapGetters辅助函数仅仅是将store中的getter映射到局部计算属性,看情况使用,类似mapState。下面使用mapGetter改写上面的filterItems

import { mapGetters } from 'vuex' // 记得引入
export default {
    computed: 
        mapGetters({
            filterItems: 'filterDatas'
        })
}

Mutation

Vuex的中文官网中明确指出更改Vuex的store中的状态(state)的唯一的方法是提交mutation

Mutation可以理解为:在Mutation里面装着一些改变数据方法的集合。即把处理数据逻辑方法全部放在Mutation里面,使得数据和视图分离。

使用Mutation

Mutation的结构:每个mutation都有一个字符串的事件类型(type)和一个回调函数(handler)也可以理解为{type:handler()},这和订阅发布有点类似。先是注册事件,当触发响应类型的时候调用handle(),调用type的时候需要用到store.commit('typeName')方法。比如我想在要触发mutations.js中的INCREASE处理函数:

// mutations.js
const INCREASE = 'INCREASE'; // 不能漏
export default {
    [INCREASE](state,data){
        // change the state ...
    }
}

因为我注册了store到根组件,那么在.vue组件中就可以通过this.$store.commit('INCREASE')触发这个改变相关状态的处理函数了。如果在actions.js中调用,直接使用提供的commit参数进行commit('INCREASE')触发处理函数。

提交载荷(Payload)

可以向store.commit传入额外的参数,参数一般为object类型。我这里接着上面的示例,组件触发的时候传入一个100的数字到data里面 :

methods:{
    increase: function (){
        this.$store.commit('INCREASE',100);
    }
}

使用mutation-types.js

使用mutation-types.js(名称可根据爱好随便取)是为了方便管理项目mutation的类型。我在知乎上也回答过为什么要使用mutation-types.js,当然你完全没必要使用它,不过我自己喜欢使用它。将使用mutation内容中的mutations.js代码拆分为两部分,一部分是mutation-types.js,另一部分是mutations.js,示范如下 :

// mutation-types.js
export const INCREASE = 'INCREASE';

// mutations.js
import {INCREASE} from '/path/to/mutation-type.js'
export default {
    [INCREASE](state,data){
        // change the state ...
    }
}

mapMutations辅助函数

为了简化你的代码量,使得代码看起来逼格更高点,你可以使用mapMutations辅助函数将组件中的methods映射为store.commit调用(需要在根节点注入store哦)。demo来映射上面的increase

import {mapMutations} from 'vuex' // 不能漏哦
export default {
    methods: {
        ...mapMutations([
            'INCREASE'
        ])
    }
}

Action

Action 类似于 Mutation,不同点是 :

  • Action提交的是 mutation,而不是直接变更状态

  • Action是异步的,而Mutation是同步的

详细的相似点可以回滚看Mutation的啦,或者直接戳vue官网Store

组件内分发Action

因为我在全局组件中挂载了store,所以引用就可以这样写 -> this.$store.dispatch('dispatchEvent'),当然你可以传参过去啦。比如:this.$store.dispatch('dispatchEvent',param),param一般是obj类型的。

mapActions辅助

为了简化操作,Action像Mutaion一样有一个映射的函数mapActions。使用方法也类似Mutation,demo如下 :

import {mapActions} from 'vuex' // 不能漏哦
export default {
    methods: {
        ...mapActions([
            'INCREASE'
        ])
        或
        ...mapActions([
            increase: 'INCREASE'
        ])
    }
}

Module

由于vue中使用单一的状态树,当管理的项目中大型的时候,所有的状态都集中在一个对象中会变得比较复杂,难以管理,显得项目比较臃肿。为了解决这些问题,我们可以使用vuex提供的Module功能,将store分割成模块。每个模块都有自己的state、mutation、action、getter。现在假设你的应用的功能包括登录和音乐两个功能模块页面,那么store的结构可以这样写:


- module
    - music
        actions.js
        getters.js
        index.js    // music module 的入口文件
        mutations.js
        state.js
    - user
        actions.js
        getters.js
        index.js    // user module的入口文件
        mutations.js
        state.js
actions.js
index.js    // store 的入口文件
mutation-types.js   // 管理所有的mutations
mutations.js
state.js
        

模块的局部状态

对于模块内部的mutation,接收的第一个参数是state,也就是接收本模块的局部状态,比如上面的music模块,我在其state.js中写上 :

export default {
    music: {
        list: [],
        total: 100
    }
}

我在同级的mutations.js中有 :

import * as types from '../../mutation-types'
export default {
    [types.UPDATE_MUSIC](state,data){
        console.log(state.music.total); // 打印出100
        ...other handle
    }
}

命名空间

默认情况下,模块内部的action、mutation 和 getter是注册在全局命名空间的 -> 这样使得多个模块能够对mutation和action作出响应。

如果看者希望你写的模块具有更高的封装度和复用性,你可以通过添加namespaced:true的方式使其成为命名空间模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名。比如上面的music模块 :

import state from './state'     //state
import getters from './getters' //getters
import * as actions from './actions'    //actions
import mutations from './mutations'     //mutations

//modules
export default {
    namespaced: true, // 添加命名空间
    state,
    getters,
    actions,
    mutations
}

详细的情况请戳vuex官网modules

store结构

vuex的官网谈项目结构,我这里谈store结构,因为我觉得每个人的项目的结构布局有所不同,但是vuex可以是一个模版化的使用。当然,这模版化的使用遵循了官网所定的规则:

  • 应用层级的状态应该集中在单个 store对象中

  • 提交mutation是更改状态(state)的唯一方法,并且这个过程是同步的

  • 异步逻辑都应该封装到action里面

整理的store结构如下:

.
├── ...
│
└── store
    ├── actions.js  // 根级别的 action
    ├── index.js        // 我们组装模块并导出 store 的地方
    ├── mutation-types.js   // store所有mutations管理
    ├── mutations.js        // 根级别的 mutation
    ├── state.js        // 根级别的 state
    └── modules
        ├── moduleA
        ├── moduleB
        └── moduleC
            ├── actions.js  // moduleC 的 action
            ├── getters.js  // moduleC 的 getter
            ├── index.js        // moduleC 的 入口
            ├── mutations.js        // moduleC 的 mutation
            └── state.js        // moduleC 的 state

上面的结构比较通用,模版化,我在接下来的完整小项目中就是使用上面的store结构来管理啦 😝

完整小项目

自己在上面讲了一大推的<del>废话</del>,嗯哈,为了证明那不是<del>废话</del>,下面就结合上面讲的知识点来一个综合的min-demo吧,欢迎指正啊! @~@

是什么项目呢

思来想去,自己还是觉得做一个简单版本的todo项目好点,理由如下:

  • 个人时间精力邮箱(main reason)

  • todo项目 -> 麻雀虽小,五脏俱全

项目包含一个简单的登录页面,然后跳转到todo小项目的页面。如图所示:

frontend/vuejs/vuex/min-demo-login
frontend/vuejs/vuex/min-demo-todo

在登录页面,会要求你填写非空的内容进入,我这里填了自己的名字啦。在todo页面,你就需要在输入框输入你要做的事情啦,事情的添加默认是未做的状态。当然,允许进行时间的状态进行设置和事件的删除啦。成品可查看下面最终的效果gif动效,就酱 @~@

项目的初始化

⚠️ 本项目在mac系统上使用vue-cli的基础上搭建(搭建日期2018.01.14)的小项目,其完整的覆盖了vue的全家桶了 -> 使用的vue版本是^2.5.2,vuex的版本是^3.0.1,vue-router的版本也是^3.0.1。如果你使用低版本,请参考低版本的相关说明。

# 全局安装 vue-cli
$ npm install --global vue-cli
# 进入桌面
$ cd desktop
# 初始化项目min-demo
$ vue init webpack min-demo

? Project name min-demo     # 项目名称
? Project description A Vue.js project  # 项目描述
? Author reng99   # 项目作者
? Vue build standalone
? Install vue-router? Yes   # 是否使用路由
? Use ESLint to lint your code? No  # 是否启动语法检查
? Set up unit tests No      # 是否配置单元测试
? Setup e2e tests with Nightwatch? No   # 是否配置集成测试
? Should we run `npm install` for you after the project has been created? (recom
mended) npm # 选择那种包管理工具进行安装依赖,共三种选择:npm,yarn,no thanks 我选择了npm

   vue-cli · Generated "min-demo".
# 等待安装依赖的完成
...
# 进入项目
$ cd min-demo
# 启动项目
$ npm run dev

# 如果一切正常,就会在浏览器的http://localhost:8080的地址页面有相关的vue界面展示出来

当然,使用脚手架搭建的项目,没有自动集成vuex,这就需要你进入项目的根目录,执行npm install vuex --save命令来安装啦。

项目的实现

嗯嗯,下面我将改写在vue-cli搭建的项目,以符合我自己期望。改写的代码就不全给出来了啊,关键的项目代码还是会贴一下的。😝

这个项目的结构如下:

.
├── build/  #webpack 的配置项
│   └── ...
├── config/
│   ├── index.js        # 项目的主要配置
│   └── ...
├── node_modules/   # 相关依赖
│   └── ...
├── src/
│   ├── main.js     # 应用的主入口
│   ├── App.vue     # 引用的根组件
│   ├── components/ 
│   │   ├── Login.vue       # 登录组件
│   │   └── Todo.vue        # todo组件
│   ├── store/ 
│   │   ├── modules/        # todo组件
│   │   │     └── todo
│   │   │         ├── actions.js    # todo的actions
│   │   │         ├── getters.js    # todo的getters
│   │   │         ├── index.js      # todo的入口
│   │   │         ├── mutations.js      # todo的mutations
│   │   │         └── state.js      # todo的状态
│   │   ├── actions.js      # 根actions
│   │   ├── index.js        # store入口文件
│   │   ├── mutation-types.js       # 整个store中的mutation的管理
│   │   ├── mutations.js        # 根mutations
│   │   └── state.js        # 根的状态
│   ├── router/ 
│   │   └── index.js        # 路由文件
│   └── assets/     #  模块的资源
│       └── ...
├── static/  # 静态资源存放的地方
│   └── ...
├── .babelrc                    # 语法转换babel的相关配置
├── .editorconfig               # 编辑器IDE的相关配置
├── .gitignore                  # 提交到github忽略的内容配置
├── .postcssrc.js               # css的处理配置postcssrc
├── index.html                  # index html模版
├── package.json                # 相关的执行命令和依赖配置
└── README.md                   # 项目的说明文件

⚠️ 项目重点在src文件夹内

/src/components/Login.vue

<template>
  <div id="login">
    <div class='login'>
      <div class='login-title'>简单模拟登录</div>
      <div class='login-body'>
        <div class='hint' v-show='hintFlag'>输入的文字不能为空</div>
        <input placeholder='请输入任意文字...' type='text' v-model='loginTxt'/>
        <div class="btn" @click='login'>登录</div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Login',
  data () {
    return {
      hintFlag: false,
      loginTxt: ''
    }
  },
  methods: {
    login () {
      var vm = this;
      if(vm.loginTxt.trim()==''){
        vm.hintFlag = true;
      }else{
        // 进入todo的页面
        vm.hintFlag = false;
        // 触发获取登录名
        vm.$store.dispatch('createUsername',vm.loginTxt);
        vm.$router.push('/todo');
      }
    }
  },
  watch:{
    loginTxt(curVal){
      var vm = this;
      if(curVal.trim()==''){
        vm.hintFlag = true;
      }else{
        vm.hintFlag = false;
      }
    }
  }
}
</script>

<style scoped lang='less'>
  #login{
    margin-top: 100px;
    .login{
      width: 400px;
      margin: 0 auto;
      &-title{
        color: #999;
        font-size: 22px;
        text-align: center;
        margin-bottom: 20px;
      }
      &-body{
        width: 360px;
        padding: 40px 20px 60px 20px;
        background: #ededed;
        input{
          width: 100%;
          display: block;
          height: 40px;
          text-indent: 10px;
        }
        .btn{
          width: 100%;
          text-align: center;
          height: 40px;
          line-height: 40px;
          background: #09c7d1;
          color: #fff;
          margin-top: 20px;
          cursor: pointer;
        }
        .hint{
          color: red;
          font-size: 12px;
          text-align: center;
          padding-bottom: 10px;
        }
      }
    }
  }
</style>

在上面的组件中,自己原封不动的将里面的代码复制过来了,你应该可以看出,这个.vue文件中结合了三块的东西,分别是html的模版、javascript代码和运用less预处理器编写的css代码。

/src/components/Todo.vue组件的结构依旧是这样:

<template>
  <div id="todo">
    <div class='username'>欢迎您!<span>{{username}}</span></div>
    <div class="main">
      <div class="input">
        <input placeholder='请输入要做的事情...' type='text' v-model='eventTxt'/>
        <button @click="addEvent">增加</button>
      </div>
      ...
    </div>
  </div>
</template>

<script>
export default {
  name: 'ToDo',
  data () {
    return {
      noDataFlag: true,
      ...
    }
  },
  created(){
    var vm = this;
    if(vm.username == ''){
      vm.$router.push('/');
    }
  },
  computed: {
    username(){
      return this.$store.getters.username;
    },
    ...
  },
  methods: {
    delEvent (id) {
      this.$store.dispatch('delEvent',id);
    },
    ...
  },
  watch:{
    ...
  }
}
</script>

<style scoped lang='less'>
  #todo{
    margin-top: 100px;
    ...
  }
</style>

在路由的文件中,因为知识涉及了两个页面的路由跳转,这里也全贴出来吧 --

import Vue from 'vue'
import Router from 'vue-router'
import Login from '@/components/Login'
import ToDo from '@/components/ToDo'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Login',
      component: Login
    },
    {
      path: '/todo',
      name: 'ToDo',
      component: ToDo
    }
  ]
})

关于store,这是一个重点,我打算详细说啦。首先当然得从整个store的入口文件讲起啦。在store/index.js中,我是这样引用的 :

import Vue from 'vue'       // 引入vue依赖
import Vuex from 'Vuex'     // 引入vuex依赖

import state from './state'     // 引入根状态
import * as actions from './actions'        // 引入根actions
import mutations from './mutations'     // 引入根mutations

import todo from './modules/todo/index'     // 引入todo模块

Vue.use(Vuex)       // 引入vuex

// 初始化store
export default new Vuex.Store({
  state,
  actions,
  mutations,
  modules:{
    todo
  }
})

在根的storemutation-types.js文件中,管理着整个项目的状态管理函数 --> 包括创建用户名、添加要做的事情、删除创建的事情、显示事件的状态(全部,已经做,没有做)和标记事件(已经做的事件标记为未做,未做的事件标记为已经做)。代码展示如下 :

export const CREATE_USERNAME = 'CREATE_USERNAME'  // 创建用户名
export const ADD_EVENT = 'ADD_EVENT'  // 添加事件
export const DEL_EVENT = 'DEL_EVENT'  // 删除事件
export const ALL_EVENT = 'ALL_EVENT'  // 全部事件
export const UNDO_EVENT = 'UNDO_EVENT'  // 没做事件
export const DONE_EVENT = 'DONE_EVENT'  // 已做事件
export const MARK_UNDONE = 'MARK_UNDONE' // 标记为未做
export const MARK_DONE = 'MARK_DONE' // 标记为已做

store/state.js的作用在你听完store/todo/state.js的讲解后你应该会明白。在模块todo的state中,自己定义了此模块的相关的数据结构,如下:

export default {
  // 事件列表
  list:[
    // {
    //   id: 0,  相关的id
    //   content:'',  // 事件的内容
    //   flag: 1 // 是否完成,1是完成,0是未完成
    // }
  ],
  allList:[],
  increase: 0,
  total: 0,
  done: 0
}

定义的这些数据结构,你可以说是状态吧,是为了给mutation和getters进行操作。对了,你也许注意到了store根目录中没有getters.js文件。因为,这是分散模块管理项目,为什么还需要呢,如果你想保留,你可以自己新建一个,按照自己的习惯进行管理项目呗。

上个段落以及前面某部分内容已经谈及了mutations的作用,本项目中使用mutation就是为了改变自己在todo/state.js定义的状态,比如改变allList:[]

import * as types from '../../mutation-types'

export default {
  // 添加事件
  [types.ADD_EVENT] (state,data){
    var obj = {
      id: state.increase++,
      content: data,
      flag: 0
    }
    state.allList.push(obj);
    state.list = state.allList;
    state.total = state.allList.length;
  },
  ...
}

todo/getter.js就是为了将vuex中的状态获取,方便显示在页面的啦,在本项目中,自己超级简单的使用了下:

export default {
  list (state,getters,rootState) {
    return state.list;
  },
  username (state,getters,rootState) {
    return rootState.username;
  },
  ...
}

最后一个是关于todo/actions.js,这是页面中的用户的事件去发送事件,使得产生mutations去改变状态(state.js),最终使得页面展示的内容(getters)发生改变。这里以一个派遣添加事件为例子 :

import * as types from '../../mutation-types'

export const addEvent = ({commit,state,rootState},query) => {
  commit(types.ADD_EVENT,query);
}

嗯,整篇文章都说整个store是挂载在根组件上的,那么是在哪里呢?答案就是src/main.js文件啦,文件内的代码如下 :

import Vue from 'vue'
import App from './App'
import router from './router'

import store from './store'

Vue.config.productionTip = false

new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

最终的效果

好吧,自己利用了一个下午搭建项目并简单思考了相关的逻辑,简单实现项目,其最终的效果如下gif动图啦 :

frontend/vuejs/vuex/min-demo

嗯,项目是不是很简单,所以就不放源码上去了 😂 。其实自己觉得源码实现不够严谨啦,毕竟只是花了短短一个下午和晚上从设计到实现... 逃:)

参考内容

vuex官网

( 完 @~@ )

原文请戳传送门

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • 安装 npm npm install vuex --save 在一个模块化的打包系统中,您必须显式地通过Vue.u...
    萧玄辞阅读 2,920评论 0 7
  • Vuex是什么? Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件...
    萧玄辞阅读 3,091评论 0 6
  • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应...
    白水螺丝阅读 4,648评论 7 61
  • 系列文章:Vue 2.0 升(cai)级(keng)之旅Vuex — The core of Vue applic...
    6ed7563919d4阅读 4,526评论 2 58
  • 按照自己的阅读安排,最近在看《少有人走的路》。虽然第一章给我很多的激励,然而第二章似乎有些偏激,啰嗦。第二章反复在...
    松树爱姜姜阅读 991评论 1 2