IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 超详细!Vue-coderwhy个人学习笔记(五)(Day8) -> 正文阅读

[JavaScript知识库]超详细!Vue-coderwhy个人学习笔记(五)(Day8)

前言


?九、Promise

(一)、认识Promise

1.什么是Promise呢?

  • ES6中一个非常重要和好用的特性就是Promise
    • 但是初次接触Promise会一脸懵逼,这TM是什么东西?
    • 看看官方或者一些文章对它的介绍和用法,也是一头雾水。
  • Promise到底是做什么的呢?
    • Promise是异步编程的一种解决方案。
  • 那什么时候我们会来处理异步事件呢?
    • 一种很常见的场景应该就是网络请求了。
    • 我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能像简单的3+4=7一样将结果返回。
    • 所以往往我们会传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调出去。
    • 如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦。
  • 但是,当网络请求非常复杂时,就会出现回调地狱(回调里面又回调)
    • OK,我以一个非常夸张的案例来说明。?

2.网络请求的回调地狱

  • 我们来考虑下面的场景(有夸张的成分):

    • 我们需要通过一个url1从服务器加载一个数据data1,data1中包含了下一个请求的url2

    • 我们需要通过data1取出url2,从服务器加载数据data2,data2中包含了下一个请求的url3

    • 我们需要通过data2取出url3,从服务器加载数据data3,data3中包含了下一个请求的url4

    • 发送网络请求url4,获取最终的数据data4?

?


(二)、Promise基本使用

1.定时器的异步事件

  • ?我们先来看看Promise最基本的语法。
  • 这里,我们用一个定时器来模拟异步事件:
    • 假设下面的data是从网络上1秒后请求的数据
    • console.log就是我们的处理方式。

?

  • 这是我们过去的处理方式,我们将它换成Promise代码

  • 这个例子会让我们感觉脱裤放屁,多此一举
    • 首先,下面的Promise代码明显比上面的代码看起来还要复杂。
    • 其次,下面的Promise代码中包含的resolve、reject、then、catch都是些什么东西??
  • 我们先不管第一个复杂度的问题,因为这样的一个屁大点的程序根本看不出来Promise真正的作用。?

2.定时器异步事件解析?

  • 我们先来认认真真的读一读这个程序到底做了什么?
    • new Promise很明显是创建一个Promise对象
    • 小括号中((resolve, reject) => {})也很明显就是一个函数,而且我们这里用的是之前刚刚学习过的箭头函数。、
      • 但是resolve, reject它们是什么呢?
      • 我们先知道一个事实:在创建Promise时,传入的这个箭头函数是固定的(一般我们都会这样写)
      • resolve和reject它们两个也是函数,通常情况下,我们会根据请求数据的成功和失败来决定调用哪一个。
    • 成功还是失败?
      • 如果是成功的,那么通常我们会调用resolve(messsage),这个时候,我们后续的then会被回调。
      • 如果是失败的,那么通常我们会调用reject(error),这个时候,我们后续的catch会被回调。??
  • OK,这就是Promise最基本的使用了。?

代码:


    // 同步
    const name = 'yyy';
    console.log(name);
    const result = 3 + 5;

    // 异步
    // 1.使用setTimeout 用一个定时器来模拟异步事件
    // setTimeout(() => {
    //   console.log('Hello World');
    // }, 1000)


    // 什么情况下会用到Promise?
    // 一般情况下是有异步操作时,使用Promise对这个*异步操作进行封装*
    // new -> 构造函数(1.保存了一些状态信息  2.执行传入的函数)
    // 在执行传入的回调函数时, 会传入两个参数, resolve解决, reject拒绝.这两个本身又是函数
    // 参数 -> 函数((resolve, reject)

    // 以后有什么异步的操作的话都可以封装在Promise里面


    new Promise((resolve, reject) => {

      setTimeout(() => {

        // 如果有代码需要处理不会直接这样放这里 在then里面处理data
        // console.log('hello world');
        // console.log('hello world');
        // console.log('hello world');
        // console.log('hello world');

        // 成功的话去到then那里
        // resolve('Hello World')

        // 失败的时候调用reject 会去到catch那里
        reject('error message')
      }, 1000)
    }).then((data) => { // then里面也是一个函数
      // 1.100行的处理代码
      console.log(data);
      console.log(data);
      console.log(data);
      console.log(data);
      console.log(data);
    }).catch((err) => {
      console.log(err);
    })
    // 通过回调里的 resolve(data) 将这个 promise 标记为 resolverd,
    // 然后进行下一步 then((data)=>{//do something}),resolve 里的参数就是你要传入 then 的数据。
 

3.Promise三种状态和另外处理形式

  • 首先, 当我们开发中有异步操作时, 就可以给异步操作包装一个Promise

    • 异步操作之后会有三种状态?

  • 我们一起来看一下这三种状态:
    • pending:等待状态,比如正在进行网络请求,或者定时器没有到时间。
    • fulfill:满足状态,当我们主动回调了resolve时,就处于该状态,并且会回调.then()
    • reject:拒绝状态,当我们主动回调了reject时,就处于该状态,并且会回调.catch()?

?

  • Promise的另外处理形式?
new Promise((resolve, reject) => {
    setTimeout(() => {
      // resolve('Hello Vuejs')
      reject('error message')
    }, 1000)
    // 不想同时写then和catch的话 then里面可以直接传入两个函数 data,err
  }).then(data => {
    console.log(data);
  }, err => {
    console.log(err);
  })


(三)、Promise链式调用

1.Promise链式调用

  • 我们在看Promise的流程图时,发现无论是then还是catch都可以返回一个Promise对象。

  • 所以,我们的代码其实是可以进行链式调用的:


    // 参数 -> 函数(resolve, reject)
    // resolve, reject本身它们又是函数
    // 链式编程
    new Promise((resolve, reject) => {

      // 第一次网络请求的代码
      setTimeout(() => {
        resolve()
      }, 1000)

    }).then(() => {
      // 第一次拿到结果的处理代码
      console.log('Hello World');
      console.log('Hello World');
      console.log('Hello World');
      console.log('Hello World');
      console.log('Hello World');
      console.log('Hello World');

      // 如果还有回调的话可以在这里写 在第一个.then()的后面继续.then()
      return new Promise((resolve, reject) => {

        // 第二次网络请求的代码
        setTimeout(() => {
          resolve()
        }, 1000)
      })
    }).then(() => {

      // 第二次处理的代码
      console.log('Hello Vuejs');
      console.log('Hello Vuejs');
      console.log('Hello Vuejs');
      console.log('Hello Vuejs');
      console.log('Hello Vuejs');
      console.log('Hello Vuejs');

      return new Promise((resolve, reject) => {

        // 第三次网络请求的代码
        setTimeout(() => {
          resolve()
        })
      })
    }).then(() => {

      // 第三处理的代码
      console.log('Hello Python');
      console.log('Hello Python');
      console.log('Hello Python');
      console.log('Hello Python');
      console.log('Hello Python');
    })
  
  • 这里我们直接通过Promise包装了一下新的数据,将Promise对象返回了

    • Promise.resovle():将数据包装成Promise对象,并且在内部回调resolve()函数

    • Promise.reject():将数据包装成Promise对象,并且在内部回调reject()函数?

// 简写,直接return Promise.resolve()
    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('aaa')
      }, 1000)
    }).then(res => {
      // 1.自己处理10行代码
      console.log(res, '第一层的10行处理代码');

      // 2.对结果进行第一次处理
      return Promise.resolve(res + '111')
    }).then(res => {
      console.log(res, '第二层的10行处理代码');

      return Promise.resolve(res + '222')
    }).then(res => {
      console.log(res, '第三层的10行处理代码');
    })

2.链式调用简写?

  • 简化版代码:
    • 如果我们希望数据直接包装成Promise.resolve,那么在then中可以直接返回数据
    • 注意下面的代码中,我将return Promise.resovle(data)改成了return data
    • 结果依然是一样的?

 /* -----省略掉Promise.resolve,直接return res ----- */
    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('aaa')
      }, 1000)
    }).then(res => {
      // 1.自己处理10行代码
      console.log(res, '第一层的10行处理代码');

      // 2.对结果进行第一次处理
      return res + '111'
    }).then(res => {
      console.log(res, '第二层的10行处理代码');

      return res + '222'
    }).then(res => {
      console.log(res, '第三层的10行处理代码');
    })
  • 上面的都是每一层成功,那失败的怎么写呢?
new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('aaa')
      }, 1000)
    }).then(res => {
      // 1.自己处理10行代码
      console.log(res, '第一层的10行处理代码');

      // reject失败的简写
      // return Promise.reject('error message')
      // 失败error的其他写法 手动抛出异常
      throw 'error message'
    }).then(res => {
      console.log(res, '第二层的10行处理代码');

      return Promise.resolve(res + '222')
    }).then(res => {
      console.log(res, '第三层的10行处理代码');
    }).catch(err => {
      console.log(err);
    })

3.Promise的all方法使用


    // 需求本身依赖两个请求 不确定是哪个先请求回来,所以两个都得处理handleResult
    // 请求一:
    // let isResult1 = false
    // let isResult2 = false
    // $ajax({
    //   url: '',
    //   success: function () {
    //     console.log('结果1');
    //     isResult1 = true
    //     handleResult()
    //   }
    // })
    // // 请求二:
    // $ajax({
    //   url: '',
    //   success: function () {
    //     console.log('结果2');
    //     isResult2 = true
    //     handleResult()
    //   }
    // })
    //
    // function handleResult() {
    //   if (isResult1 && isResult2) {
    //     //
    //   }
    // }


    // Promise.all可以将多个Promise实例包装成一个新的Promise实例。统一进行回调
    // 同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
    Promise.all([
      // new Promise((resolve, reject) => {
      //   $.ajax({
      //     url: 'url1',
      //     success: function (data) {
      //       resolve(data)
      //     }
      //   })
      // }),
      // new Promise((resolve, reject) => {
      //   $.ajax({
      //     url: 'url2',
      //     success: function (data) {
      //       resolve(data)
      //     }
      //   })
      // })

      new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve({
            name: 'yyy',
            age: 18
          })
        }, 2000)
      }),
      new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve({
            name: 'kobe',
            age: 19
          })
        }, 1000)
      })
    ]).then(results => {
      console.log(results);
    })
  

十、Vuex详解

(一)、认识Vuex

1.Vuex是做什么的??

  • 官方解释:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
    • 它采用 集中式存储管理 应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
    • Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。?
  • 状态管理到底是什么?
    • 状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。
    • 其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。
    • 然后,将这个对象放在顶层的Vue实例中,让其他组件可以使用。
    • 那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢??
  • 等等,如果是这样的话,为什么官方还要专门出一个插件Vuex呢?难道我们不能自己封装一个对象来管理吗?
    • 当然可以,只是我们要先想想VueJS带给我们最大的便利是什么呢?没错,就是响应式。
    • 如果你自己封装实现一个对象能不能保证它里面所有的属性做到响应式呢?当然也可以,只是自己封装可能稍微麻烦一些。
    • 不用怀疑,Vuex就是为了提供这样一个在多个组件间共享状态的插件,用它就可以了。?

2.管理什么状态呢?

  • 但是,有什么状态时需要我们在多个组件间共享的呢?

    • 如果你做过大型开放,你一定遇到过多个状态,在多个界面间的共享问题。

    • 比如用户的登录状态、用户名称、头像、地理位置信息等等。

    • 比如商品的收藏、购物车中的物品等等。

    • 这些状态信息,我们都可以放在统一的地方,对它进行保存和管理,而且它们还是响应式的(待会儿我们就可以看到代码了,莫着急)。?

  • ?OK,从理论上理解了状态管理之后,让我们从实际的代码再来看看状态管理。
    • 毕竟,Talk is cheap, Show me the code.(来自Linus)

  • 我们先来看看但界面的状态管理吧.?

3.单界面的状态管理?

  • ?我们知道,要在单个组件中进行状态管理是一件非常简单的事情
    • 什么意思呢?我们来看下面的图片。

  • ?这图片中的三种东西,怎么理解呢?
    • State:不用多说,就是我们的状态。(你姑且可以当做就是data中的属性)
    • View:视图层,可以针对State的变化,显示不同的信息。(这个好理解吧?)
    • Actions:这里的Actions主要是用户的各种操作:点击、输入等等,会导致状态的改变。
  • 写点代码,加深理解:
    • 看下右边的代码效果, 肯定会实现吧??

4.单界面状态管理的实现?

?

App.vue代码

<template>
  <div id="app">
    <h2>{{message}}</h2>
    <h2>{{counter}}</h2>
    <button @click="counter++">+</button>
    <button @click="counter--">-</button>
  </div>
</template>

<script>
export default {
  name: 'App',
  components: {
   
  },
  data() {
    return {
      message:'我是App组件',
      counter:0
    }
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
  • ?在这个案例中,我们有木有状态需要管理呢?没错,就是个数counter。
  • counter需要某种方式被记录下来,也就是我们的State。
  • counter目前的值需要被显示在界面中,也就是我们的View部分。
  • 界面发生某些操作时(我们这里是用户的点击,也可以是用户的input),需要去更新状态,也就是我们的Actions 这不就是上面的流程图了吗?

?5.多界面状态管理

  • Vue已经帮我们做好了单个界面的状态管理,但是如果是多个界面呢?
    • 多个试图都依赖同一个状态(一个状态改了,多个界面需要进行更新)
    • 不同界面的Actions都想修改同一个状态(Home.vue需要修改,Profile.vue也需要修改这个状态)?
  • 也就是说对于某些状态(状态1/状态2/状态3)来说只属于我们某一个试图,但是也有一些状态(状态a/状态b/状态c)属于多个试图共同想要维护的
    • 状态1/状态2/状态3你放在自己的房间中,你自己管理自己用,没问题。
    • 但是状态a/状态b/状态c我们希望交给一个大管家来统一帮助我们管理!!!
    • 没错,Vuex就是为我们提供这个大管家的工具。?
  • 全局单例模式(大管家)
    • 我们现在要做的就是将共享的状态抽取出来,交给我们的大管家,统一进行管理。
    • 之后,你们每个试图,按照我规定好的规定,进行访问和修改等操作。
    • 这就是Vuex背后的基本思想。?
  • 如果互为父子组件的话,可以用props
    • 新建组件HelloVuex.vueHelloVuex.vueApp.vue代码如下
// HelloVuex.vue
<template>
  <div>
      <h2>{{counter}}</h2>
  </div>
</template>

<script>
export default {
    name:'HelloVuex',
    props:{
        counter:Number
    }
}
</script>


// App.vue
<template>
  <div id="app">
    <h2>{{message}}</h2>
    <h2>{{counter}}</h2>
    <button @click="counter++">+</button>
    <button @click="counter--">-</button>

    <HelloVuex :counter="counter"></HelloVuex>
  </div>
</template>

<script>
import HelloVuex from './components/HelloVuex.vue'

export default {
  name: 'App',
  components: {
    HelloVuex
  },
  data() {
    return {
      message:'我是App组件',
      counter:0
    }
  },
}
</script>

  • ?但是组件没有父子关系的话,就得使用Vuex

6.Vuex状态管理图例?

  • 一起在来看一副官方给出的图片?

store是通过单例模式实现的?

(二)、Vuex基本使用?

// 安装vuex
npm install vuex --save

?1.简单的案例

  • ?我们还是实现一下之前简单的案例

  • ?首先,我们需要在某个地方存放我们的Vuex代码:
    • 这里,我们先创建一个文件夹store,并且在其中创建一个index.js文件
    • index.js文件中写入如下代码:

?代码

import Vue from 'vue'
import Vuex from 'vuex'
// 1.安装插件
Vue.use(Vuex)

// 2.创建对象 new Vuex.Store
const store = new Vuex.Store({
    // 这5个对象一般都是固定的
    state: { //保存状态
        counter: 1000
    },
    mutations: { // 方法 修改state唯一途径 同步操作
        increment(state) { // 默认就有个state参数,不用通过this.state
            state.counter++
        },
        decrement(state) {
            state.counter--
        }
    },
    actions: { // 如果有异步操作在这里写 比如网络请求

    },
    getters: {

    },
    modules: {

    }
})
// 3.导出store对象
export default store

?2.挂载到Vue实例中

  • 其次,我们让所有的Vue组件都可以使用这个store对象
    • 来到main.js文件,导入store对象,并且放在new Vue中
    • 这样,在其他Vue组件中,我们就可以通过this.$store的方式,获取到这个store对象了

main.js代码:

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

Vue.config.productionTip = false

// 其他组件就能通过 $store 获取到store
new Vue({
  el: '#app',
  store,
  render: h => h(App)
})

?3.使用Vuex的count

代码:?

<template>
  <div id="app">
    <h2>-----App.vue内容-----</h2>
    <!-- 通过this.$store.state.属性的方式来访问状态 -->
    <h2>{{ $store.state.counter }}</h2>
    <!-- 官方不建议这么改,因为工具devtools监听不到 -->
    <!-- <button @click="$store.state.counter++">+</button>
    <button @click="$store.state.counter--">-</button> -->
    <button @click="addition">+</button>
    <button @click="subtraction">-</button>
    <hr />
    <h2>-----HelloVuex内容-----</h2>
    <HelloVuex></HelloVuex>
  </div>
</template>

<script>
import HelloVuex from "./components/HelloVuex.vue";

export default {
  name: "App",
  components: {
    HelloVuex,
  },
  data() {
    return {};
  },
  methods: {
    // 在这里提交方法对应的mutation 的方法名
    addition() {
      // 通过this.$store.commit('mutation中方法')来修改状态
      this.$store.commit("increment");
    },
    subtraction() {
      this.$store.commit("decrement");
    },
  },
};
</script>

  • 好的,这就是使用Vuex最简单的方式了。
  • 我们来对使用步骤,做一个简单的小节:
  • 1.提取出一个公共的store对象,用于保存在多个组件中共享的状态
  • 2.将store对象放置在new Vue对象中,这样可以保证在所有的组件中都可以使用到
  • 3.在其他组件中使用store对象中保存的状态即可
    • 通过this.$store.state.属性的方式来访问状态
    • 通过this.$store.commit('mutation中方法')来修改状态?
  • 注意事项:
    • 我们通过提交mutation的方式,而非直接改变store.state.count。
    • 这是因为Vuex可以更明确的追踪状态的变化,所以不要直接改变store.state.count的值。?


(三)、Vuex核心概念

Vuex核心概念

  • Vuex有几个比较核心的概念:

    • State

    • Getters

    • Mutation

    • Action

    • Module

  • 我们对它进行一一介绍.?

(四)、State

State单一状态树

  • Vuex提出使用单一状态树, 什么是单一状态树呢?
    • 英文名称是Single Source of Truth,也可以翻译成单一数据源。?
  • 但是,它是什么呢?我们来看一个生活中的例子。
    • OK,我用一个生活中的例子做一个简单的类比。
    • 我们知道,在国内我们有很多的信息需要被记录,比如上学时的个人档案,工作后的社保记录,公积金记录,结婚后的婚姻信息,以及其他相关的户口、医疗、文凭、房产记录等等(还有很多信息)。
    • 这些信息被分散在很多地方进行管理,有一天你需要办某个业务时(比如入户某个城市),你会发现你需要到各个对应的工作地点去打印、盖章各种资料信息,最后到一个地方提交证明你的信息无误。
    • 这种保存信息的方案,不仅仅低效,而且不方便管理,以及日后的维护也是一个庞大的工作(需要大量的各个部门的人力来维护,当然国家目前已经在完善我们的这个系统了)。?
  • 这个和我们在应用开发中比较类似:
    • 如果你的状态信息是保存到多个Store对象中的,那么之后的管理和维护等等都会变得特别困难。
    • 所以Vuex也使用了单一状态树来管理应用层级的全部状态。
    • 单一状态树能够让我们最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。?

(五)、Getters?

?1.Getters基本使用

  • ?有时候,我们需要从store中获取一些state变异后的状态,比如下面的Store中:
    • 获取学生年龄大于20的个数。

storeindex.js代码:

const store = new Vuex.Store({
  // 这五个一般都是固定的,是对象
  state: { //保存状态
    counter: 1000,
    students: [{
        id: 110,
        name: 'why',
        age: 18
      },
      {
        id: 111,
        name: 'kobe',
        age: 24
      },
      {
        id: 112,
        name: 'james',
        age: 30
      },
      {
        id: 113,
        name: 'curry',
        age: 10
      }
    ],
  },
 // ...

})
  • 可以用computed计算属性,但是这样的话,代码不好复用,其他组件使用不方便?

App.vue代码?

  // App.vue
 computed: {
  // 用计算属性的话 代码不好复用
  // 找出多于20岁的学生
    more20stu() {
      return this.$store.state.students.filter(s => s.age > 20)
    }
  },
  • ?我们可以在Store中定义getters

?storeindex.js代码:

const store = new Vuex.Store({
  // 这五个一般都是固定的,是对象
  state: { //保存状态
    counter: 1000,
    students: [{
        id: 110,
        name: 'why',
        age: 18
      },
      {
        id: 111,
        name: 'kobe',
        age: 24
      },
      {
        id: 112,
        name: 'james',
        age: 30
      },
      {
        id: 113,
        name: 'curry',
        age: 10
      }
    ],

  },
  getters: { // 可以认为是 store 的计算属性
    // getters里面的方法 也会有state参数
    powerCounter(state) {
      return state.counter * state.counter
    },
    // 找出多于20岁的学生 
    // filter(回调函数(当前元素的值))
    more20stu(state) {
      return state.students.filter(s => s.age > 20)
    },
  }
})

?注意,getter 在通过属性访问时是作为 Vue 的响应式系统的一部分缓存其中的

?2.Getters作为参数和传递参数

  • 如果我们已经有了一个获取所有年龄大于20岁学生列表的getters, 那么代码可以这样来写?

?storeindex.js代码:

const store = new Vuex.Store({
  // 这五个一般都是固定的,是对象
  state: { //保存状态
    counter: 1000,
    students: [{
        id: 110,
        name: 'why',
        age: 18
      },
      {
        id: 111,
        name: 'kobe',
        age: 24
      },
      {
        id: 112,
        name: 'james',
        age: 30
      },
      {
        id: 113,
        name: 'curry',
        age: 10
      }
    ],

  },
  getters: { // 可以认为是 store 的计算属性
    // getters里面的方法 也会有state参数
    powerCounter(state) {
      return state.counter * state.counter
    },
    // 找出多于20岁的学生 
    // filter(回调函数(当前元素的值))
    more20stu(state) {
      return state.students.filter(s => s.age > 20)
    },
    // 找出大于20岁学生的个数
    more20stuLength(state, getters) { // Getters 也可以接受其他 getters 作为第二个参数:
      //  return state.students.filter(s => s.age > 20).length
      return getters.more20stu.length
    },
   
  },
})
  • ?getters默认是不能传递参数的, 如果希望传递参数, 那么只能让getters本身返回另一个函数.
    • 比如上面的案例中,我们希望根据ID获取用户的信息

??storeindex.js代码:

// .. 
getter:{
    // 找出年龄大于参数age的学生
    moreAgeStu(state) { // getters传递参数 只能让getters本身返回另一个函数.
      // return function (age) {
      //   return state.students.filter(s => s.age > age)
      // }
      return age => {
        return state.students.filter(s => s.age > age)
      }
  }
} 
// ...

App.vue

 <h2>----------App内容: getters相关信息----------</h2>

    <h2>{{ $store.getters.powerCounter }}</h2>
    <h2>{{ $store.getters.more20stu }}</h2>
    <h2>{{ $store.getters.more20stuLength }}</h2>
    <h2>{{ $store.getters.moreAgeStu(12) }}</h2>

?


(六)、Mutation

1.Mutation状态更新?

  • ?Vuex的store状态的更新唯一方式:提交Mutation
  • Mutation主要包括两部分:
    • 字符串的事件类型(type)
    • 一个回调函数(handler),该回调函数的第一个参数就是state。

?

  • ?mutation的定义方式:

  • 通过mutation更新?

?

?2.Mutation传递参数

  • ?在通过mutation更新数据的时候, 有可能我们希望携带一些额外的参数
    • 参数被称为是mutation的载荷(Payload)
  • ?Mutation中的代码:

// ...
  mutations: { // 方法 修改state唯一途径 同步操作
    // mutations传递参数
    // 单个参数
     incrementCount(state, count) {
        console.log(count);
        state.counter +=count
     },
  },

// ...

  • App.vue代码

// template... 
  <!-- mutation传递参数 -->
    <button @click="addCount(5)">+5</button>
    <button @click="addCount(10)">+10</button>
    <button @click="addStudent">添加学生</button>
// ...

// methods
     addCount(count) {
      // payload: 负载
      // 1.普通的提交封装 这样写的 mutations里的 incrementCount(state, count) 的count就是count
       this.$store.commit('incrementCount', count) // 单个参数
    },
  • ?但是如果参数不是一个呢?
    • 比如我们有很多参数需要传递.
    • 这个时候, 我们通常会以对象的形式传递, 也就是payload是一个对象.
    • 这个时候可以再从对象中取出相关的信息.
mutations

// ...
  mutations: { // 方法 修改state唯一途径 同步操作
    
    // 参数是对象
    addStudent(state, stu) {
      state.students.push(stu)
    },
  },

// ...
App.vue

// App.vue的methods
 addStudent() {
      // 提交对象
      const stu = { id: 114, name: "alan", age: 35 };
      this.$store.commit("addStudent", stu);
  },

3.Mutation提交风格

  • 上面的通过commit进行提交是一种普通的方式
  • Vue还提供了另外一种风格, 它是一个包含type属性的对象

  • Mutation中的处理方式是将整个commit的对象作为payload使用, 所以代码没有改变, 依然如下:?

?

  • ?Mutation代码
// ...
mutations:{
     // 特殊的提交封装
    incrementCount(state, payload) {
      // console.log(count);
      state.counter += payload.count
    },
}
  • ?App.vue代码
 addCount(count) {
      // payload: 负载
      // 1.普通的提交封装 这样写的 mutations里的 incrementCount(state, count) 的count 就是count
      // this.$store.commit('incrementCount', count) // 单个参数

      // 2.特殊的提交封装 mutations里的 incrementCount(state, count) 的count 是一个对象 写成payload比较合适,通过payload.count取
      this.$store.commit({
        type: "incrementCount",
        count,
      });
    },

4.Mutation响应规则??

  • Vuex的store中的state是响应式的, 当state中的数据发生改变时, Vue组件会自动更新.

  • 这就要求我们必须遵守一些Vuex对应的规则:
    • 提前在store中初始化好所需的属性.
    • 当给state中的对象添加新属性时, 使用下面的方式:
      • 方式一: 使用Vue.set(obj, 'newProp', 123)
      • 方式二: 用新对象给旧对象重新赋值?
  • 我们来看一个例子:
    • 当我们点击更新信息时, 界面并没有发生对应改变.?

  • 如何才能让它改变呢?
    • 查看下面代码的方式一和方式二
    • 都可以让state中的属性是响应式的.?

  • ?vuex代码
const store = new Vuex.Store({
  state: { //保存状态
    // state里面都属性初始化后 每个属性对应一个dep 监听属性变化 dep观察者模式
    info: {
      name: 'kobe', // Dep -> [Watcher]
      age: 40, // Dep -> [Watcher]
      height: 1.98 // Dep -> [Watcher]
      }
    },
    mutations: {
        updateInfo(state) {
          // 可以响应式
          state.info.name = 'coderwhy'
          // 不能响应式
          // state.info['address'] = '洛杉矶' // 我的可以响应???视频中的不可以
          Vue.set(state.info, 'address', '洛杉矶')

          // delete state.info.age  // “delete+某个属性”该方式做不到响应式
          // Vue.delete(state.info, 'age') // Vue.delete() 响应式
       }
    }
})
  • App.vue
// template
// ...
  <h2>----------App内容: info对象的内容是否是响应式----------</h2>
    <!-- Mutaions的响应规则 -->
    <h2>{{ $store.state.info }}</h2>
    <button @click="updateInfo">修改信息</button>
// ...

// methods
updateInfo() {
   this.$store.commit('updateInfo') 
}

5.Mutation常量类型 – 概念

  • ?我们来考虑下面的问题:
    • 在mutation中, 我们定义了很多事件类型(也就是其中的方法名称).
    • 当我们的项目增大时, Vuex管理的状态越来越多, 需要更新状态的情况越来越多, 那么意味着Mutation中的方法越来越多.
    • 方法过多, 使用者需要花费大量的经历去记住这些方法, 甚至是多个文件间来回切换, 查看方法名称, 甚至如果不是复制的时候, 可能还会出现写错的情况.
  • ?如何避免上述的问题呢?
    • 在各种Flux实现中, 一种很常见的方案就是使用常量替代Mutation事件的类型.
    • 我们可以将这些常量放在一个单独的文件中, 方便管理以及让整个app所有的事件类型一目了然.
  • 具体怎么做呢?
    • 我们可以创建一个文件: mutation-types.js, 并且在其中定义我们的常量.
    • 定义常量时, 我们可以使用ES2015中的风格, 使用一个常量来作为函数的名称.?

es5风格方法写法?

6.Mutation常量类型 – 代码?

?

?

  • ?mutations-type.js代码:
export const INCREMENT = 'increment'
  • ?store下index.js

// import INCREMENT from './mutations-types'// 不能这样导入,只能是export default
// export导出,导入需要加 {}
import {
  INCREMENT
} from './mutations-types'

const store = new Vuex.Store({
  state: { ... },
  mutations: {
     // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
    [INCREMENT](state) {
      state.counter++
    },
  }
})
  • App.vue

<script>
import HelloVuex from "./components/HelloVuex";

import { INCREMENT } from "./store/mutations-types";


export default {
  name: "App",
  methods: {
    // 在这里提交方法对应的mutation 的方法名
    addition() {
      this.$store.commit(INCREMENT);
    }
  },
}

</script>

?7.Mutation同步函数

  • 通常情况下, Vuex要求我们Mutation中的方法必须是同步方法.
    • 主要的原因是当我们使用devtools时, 可以devtools可以帮助我们捕捉mutation的快照.
    • 但是如果是异步操作, 那么devtools将不能很好的追踪这个操作什么时候会被完成.?
  • 比如我们之前的代码, 当执行更新时, devtools中会有如下信息: 图1?

?

  • 但是, 如果Vuex中的代码, 我们使用了异步函数: 图2?

mutations: {
    updateInfo(state) {
      // 错误的代码: 不能在这里进行异步操作
      setTimeout(() => {
        state.info.name = 'coderwhy'
      }, 1000)
    }
}
  • ?你会发现state中的info数据一直没有被改变, 因为他无法追踪到.
  • So, 通常情况下, 不要再mutation中进行异步的操作

?(七)、Action

1.Action的基本定义

  • 我们强调, 不要再Mutation中进行异步操作.
    • 但是某些情况, 我们确实希望在Vuex中进行一些异步操作, 比如网络请求, 必然是异步的.这个时候怎么处理呢?
    • Action类似于Mutation, 但是是用来代替Mutation进行异步操作的.
  • Action的基本使用代码如下:
    • context是什么? context是和store对象具有相同方法和属性的对象.
    • 也就是说, 我们可以通过context去进行commit相关的操作, 也可以获取context.state等.
    • 但是注意, 这里它们并不是同一个对象, 为什么呢? 我们后面学习Modules的时候, 再具体说.?
  • 这样的代码是否多此一举呢?
    • 我们定义了actions, 然后又在actions中去进行commit, 这不是脱裤放屁吗?
    • 事实上并不是这样, 如果在Vuex中有异步操作, 那么我们就可以在actions中完成了.?

?

?2.Action的分发

  • ?在Vue组件中, 如果我们调用action中的方法, 那么就需要使用dispatch

  • ?同样的, 也是支持传递payload

??

  • App.vue代码
// ...
    updateInfo() {
      // this.$store.commit('updateInfo')  // 这样没有经过actions
      // 异步修改信息 actions
      // this.$store.dispatch('aUpdateInfo',"我是携带的信息");// 携带一个参数

      // 通知外面已经改成功了 -> commit调用之后就算成功,利用对象的方法回调
      this.$store.dispatch('aUpdateInfo', {
        message: '我是携带的信息',
        success: () => {
          console.log('里面已经完成了');
        }
      })
     
    },

// ...
  • ?vuex相关代码
// ...
const store = new Vuex.Store({
    mutations:{
         updateInfo(state) {
              state.info.name = 'coderwhy'
        }
    },
    actions:{
          // 默认参数 context: 上下文 现在先理解成store
         aUpdateInfo(context, payload) {
              setTimeout(() => {
                context.commit('updateInfo')
                console.log(payload.message);
                payload.success() // 调用回调 告诉外面已经成功
              }, 1000)
        },
    }

})

// ...
  • 这样不够优雅,回调消息与携带信息混在一起,所以下面用Promise?

3.Action返回的Promise

  • 前面我们学习ES6语法的时候说过, Promise经常用于异步操作.
    • 在Action中, 我们可以将异步操作放在一个Promise中, 并且在成功或者失败后, 调用对应的resolve或reject.?
  • OK, 我们来看下面的代码:?

  • ?vuex代码
// ...
actions:{
     // Action返回的Promise
    aUpdateInfo(context, payload) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          context.commit('updateInfo');
          console.log(payload);
          resolve('1111111')
        }, 1000)
      }) // then()在App.vue里面写
    }
}

// ...
  • App.vue
// ...
 updateInfo() {
      // 之前写的不够优雅 回调消息与携带信息混在一起,现在用Promise封装再用resolve调用
      this.$store.dispatch("aUpdateInfo", "我是携带的信息").then((res) => {
        console.log("里面完成了提交");
        console.log(res);
      });
    },

// ...

(八)、Module

1.认识Module

  • Module是模块的意思, 为什么在Vuex中我们要使用模块呢?
    • Vue使用单一状态树,那么也意味着很多状态都会交给Vuex来管理.
    • 当应用变得非常复杂时,store对象就有可能变得相当臃肿.
    • 为了解决这个问题, Vuex允许我们将store分割成模块(Module), 而每个模块拥有自己的state、mutations、actions、getters等
  • 我们按照什么样的方式来组织模块呢?
    • 我们来看左边的代码

?2.Module局部状态

  • 上面的代码中, 我们已经有了整体的组织结构, 下面我们来看看具体的局部模块中的代码如何书写.
    • 我们在moduleA中添加state、mutations、getters
    • mutation和getters接收的第一个参数是局部状态对象?

  • 注意:
    • 虽然, 我们的doubleCount和increment都是定义在对象内部的.
    • 但是在调用的时候, 依然是通过this.$store来直接调用的.?

3.Actions的写法?

?

  • actions的写法呢? 接收一个context参数对象
    • 局部状态通过 context.state 暴露出来,根节点状态则为 context.rootState?

??

  • 如果getters中也需要使用全局的状态, 可以接受更多的参数?

?

  • moduleA代码
const moduleA = {
  // 组件里面通过$store.state.a.name获取属性
  state: {
    name: 'zhangsan'
  },
  // 官网state写法
  // state: () => ({
    
  // }),
  // 使用 this.$store.commit() 提交
  mutations: {
    updateName(state, payload) {
      state.name = payload
    }
  },
  // $store.getters.fullname调用
  getters: {
    fullname(state) {
      return state.name + '11111'
    },
    fullname2(state, getters) { // 使用其他getters
      return getters.fullname + '2222'
    },
    // rootState 可以使用大的state里面的值
    fullname3(state, getters, rootState) {
      return getters.fullname2 + rootState.counter
    }
  },
  // this.$store.dispatch() 提交
  actions: {
    // 这里的 context 不是store对象了 而是这个module对应的mutations
    aUpdateName(context) {
      console.log(context);
      setTimeout(() => {
        context.commit('updateName', 'wangwu')
      }, 1000)
    }
  }
}
  • App.vue
// ...
 <h2>----------App内容: modules中的内容----------</h2>
    <!-- modules的state -->
    <h2>{{ $store.state.a.name }}</h2>
    <!-- modules的mutations -->
    <button @click="updateName">修改名字</button>
    <!-- modules的getters -->
    <h2>{{ $store.getters.fullname }}</h2>
    <h2>{{ $store.getters.fullname2 }}</h2>
    <h2>{{ $store.getters.fullname3 }}</h2>
    <!-- modules的actions -->
    <button @click="asyncUpdateName">异步修改名字</button>

// ...

// methods
    updateName() {
      this.$store.commit("updateName", "lisi");
    },
    asyncUpdateName() {
      this.$store.dispatch("aUpdateName");
    },


(九)、项目结构组织?

项目结构

  • 当我们的Vuex帮助我们管理过多的内容时, 好的项目结构可以让我们的代码更加清晰.?

  • ?代码

  • index.js
import Vue from 'vue'
import Vuex from 'vuex'

import mutations from './mutations'
import actions from './actions'
import getters from './getters'
import moduleA from './modules/moduleA'

// 1.安装插件
Vue.use(Vuex)

// 2.创建对象 new Vuex.Store
// state 一般不会抽出去 而是写在这个文件
const state = {
  counter: 1000,
  students: [{
      id: 110,
      name: 'why',
      age: 18
    },
    {
      id: 111,
      name: 'kobe',
      age: 24
    },
    {
      id: 112,
      name: 'james',
      age: 30
    },
    {
      id: 113,
      name: 'curry',
      age: 10
    }
  ],
  info: {

    name: 'kobe', // Dep -> [Watcher]
    age: 40, // Dep -> [Watcher]
    height: 1.98 // Dep -> [Watcher]
  }
}
const store = new Vuex.Store({
  // 这五个一般都是固定的,是对象
  state, //保存状态
  mutations, // 方法 修改state唯一途径 同步操作
  actions, // 如果有异步操作在这里写 比如网络请求
  getters,

  modules: {
    a: moduleA
  }
})

// 3.导出store对象
export default store
  • mutatuions.js
import {
  INCREMENT
} from "./mutations-types";

export default {
  // 定义方法也可以这样写 ['text'](){}
  [INCREMENT](state) {
    // 不需要this
    state.counter++
  },
  decrement(state) {
    state.counter--
  },

  // Mutation传递参数
  incrementCount(state, payload) { // 多个参数 对象
    // console.log(count);
    state.counter += payload.count
  },
  addStudent(state, stu) {
    state.students.push(stu)
  },
  updateInfo(state) {
    state.info.name = 'coderwhy'

  }
}
  • ?getters.js
export default {
  // 也会有state参数
  powerCounter(state) {
    return state.counter * state.counter
  },
  more20stu(state) {
    return state.students.filter(s => s.age > 20)
  },

  more20stuLength(state, getters) { // 再多加一个getters参数
    //  return state.students.filter(s => s.age > 20).length
    return getters.more20stu.length
  },
  
  moreAgeStu(state) {
    return age => {
      return state.students.filter(s => s.age > age)
    }
  }
}
  • ?actions.js
export default {
  aUpdateInfo(context, payload) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        context.commit('updateInfo');
        console.log(payload);

        resolve('1111111')
      }, 1000)
    })
  }
}
  • 新建文件夹modules,里面建文件moduleA.js
export default {
  state: {
    name: 'zhangsan'
  },
  mutations: {
    updateName(state, payload) {
      state.name = payload
    }
  },
  getters: {
    fullname(state) {
      return state.name + '11111'
    },
    fullname2(state, getters) {
      return getters.fullname + '2222'
    },
    fullname3(state, getters, rootState) {
      return getters.fullname2 + rootState.counter
    }
  },
  actions: {
    aUpdateName(context) {
      console.log(context);
      setTimeout(() => {
        context.commit('updateName', 'wangwu')
      }, 1000)
    }
  }
}

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-03 11:05:05  更:2021-08-03 11:07:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 4:54:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码