Promise

Promise 是异步编程的种解决方案。

那什么时候会来处理异步事件呢?一种很常见的场景应该就是网络请求了

  1. 我们封装一个网络请求的函数,因为不能立即拿到结果,所以不能像简单的 3+4=7 一样将结果返回。
  2. 所以往往我们会传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调出去。
  3. 如果只是一个简单的网络请求,那么这种方案不会给我们带来很大的麻烦
  4. 但是,当网络请求非常复杂时,就会出现回调地狱。

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

  • pending 等待状态,比如正在进行网络请求,或者定时 器没有到时间
  • fulfill: 满足状态,当我们主动回调了 resolver 时,就处于 该状态,并且会回调 then
  • reject: 拒绝状态,当我们主动回调了 reject 时,就处于该 状态,并且会回调 catch。
new Promise((resolve, reject) => {
  // 请求服务器
  let data = $.get()
  if (data) {
    reslove(data)
  } else {
    reject('请求失败')
  }  
})
  .then(data => {
    // 请求成功时的一些操作
    })
    .catch(err => {})

// 另一种写法
new Promise((resolve, reject) => {
  // code...
}).then(data => {}, err => {})

Promise 链式调用

new Promise((resolve, reject) => {
  reslove(0)
})
  .then(data => {
    // 参数可省略一个
    return new Promise((resolve) => {
    reslove(data + 1)
      })
    })
    .then(data => {
      // 简写 === new Promise(reslove => resolve(data + 2))
    return Promise.resolve(data + 2)
    })
    .then(data => {
      // 简写,返回一个 Promise 对象
      return data + 3
    })
    // ...
    .catch(err => {
    // 接收所有错误,链式调用其中有一个错误,后面的都不会被执行
    })

Tips: 此处只有 Promise 对象才有 .then 方法,而 .then 方法的返回值默认也是一个 Promise 对象

Promise all 方法

需求:一个结果需要多个异步操作结果的值来构建时

Promise.all([
  new Promise(resolve => {
    // code...
    resolve('name')
  }),
  new Promise(resolve => {
        resolve('age')
  })
  // ...
]).then(results = {
  // 多个操作都完成后,才会执行此方法, results 是一个数组,包含了多个 promise满足状态 resolve抛出的值
  console.log(results)
})

js 异步了解及此处补充

Vuex

Vuex 是个专为 Vue Js 应用程序开发的状态管理模式,是响应式的!

  • 它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
  • Vuex 也集成到 vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel i 调试、状态快照导入导出等高级调试

状态管理

  • 状态管理模式、集中式存储管理这些名词听起来就非常高大上,让人捉摸不透。
  • 其实,你可以简单的将其看成把需要多个组件共享的变量全部存储在一个对象里面。
  • 然后,将这个对象放在顶层的 Vue 实例中,让其他组件可以使用。
  • 那么,多个组件是不是就可以共享这个对象中的所有变量属性了呢?

  • 管理什么状态呢?

  • 比如用户的登录状态、用户名称、头像、地理位置信息等等。
  • 比如商品的收藏、购物车中的物品等等。

单页面状态管理

单个组件中进行状态管理是一件非常简单的事

图示

  • State: 不用多说,就是我们的状态。(可以当做就是 data 中的属性)
  • View: 视图层,可以针对 State 的变化,显示不同的信息。
  • Actions: 这里的 Actions 主要是用户的各种操作:点击、输入等等,会导致状态的改变。

多页面管理

全局单例模式( Vuex

  • (大管家)他做的就是将共享的状态抽取出来,交给我们的大管家,统一进行管理

  • 之后,每个视图,按照规定好的规定,进行访问和修改等操作。

图示

TIps: 在组件中直接修改状态管理的值,需要通过 Dispatch。 而不是直接修改 $store.state.counter

安装

npm install vuex --save

配置及使用

// src\store\index.js
import Vuex from 'vuex'
import Vue from 'vue'

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

// 2. 创建对象
const store = new Vuex.Store({
  state: {
    counter: 42,
  },
  mutations: {
    increment(state){
      state.counter++
    },
    decrement(state){
      state.counter--
    },
    decrementNum(state, num){
      state.counter -= num
    }
  },
  actions:{},
  getters:{},
  modules:{},
})

export default store

// src\main.js
import store from './store'

Vue.config.productionTip = false

new Vue({
  store,
  render: h => h(App),
}).$mount('#app')

使用状态管理

<template>
  <div id="app">
    <button @click="$store.commit('increment')">+</button>
    <span>{{ $store.state.counter }}</span>
    <!-- $store.state.counter++ 不要直接更改其值 -->
    <button @click="$store.commit('decrement')">-</button>
    <button @click="$store.commit('decrementNum', 5)">-5</button>
  </div>
</template>

TIps : mutations 唯一的目的就是修改 state 中状态, 其中的每个方法尽可能完成的事件比较单一。对于比较复杂(如其中有判断逻辑)的操作,请放在 Action 完成

Vuex 几个比较核心的概念:

  • State
  • Getters
  • Mutation
  • Action
  • Module

State 单一状态树

Single Source of Truth, 也可以翻译成单一数据源。

  • 如果状态信息是保存到多个 Store 对象中的,那么之后的管理和维护等等都会变得特别困难。
  • 所以 Vuex 使用了单一状态树来管理应用层级的全部状态, (只能有一个 Vuex.Store 实例??
  • 单一状态树能够以最直接的方式找到某个状态的片段,而且在之后的维护和调试过程中,也可以非常方便的管理和维护。

getters

有时候需要从 store 中获取一些变异后的状态, 如下面的 Store 中:

const store = new Vuex.Store({
  state: {
    students: [{age: 16}, {age: 20}, {age: 25}, {age: 30}]
  },
  getters: {
    getAgeGreate18Stu(state){
      // 获取年龄大于18岁的学生, 
      return state.students.filter(s => s.age >= 18)
    },
    getAgeGreate18StuCount(state, getters){
      // 获取年龄大于18岁的学生数量,
      return getters.getAgeGreate18.lenght
    },
    getAgeStu(state){
      // 获取年龄等于 age 的所有学生
      // 利用闭包接收外部传递进来的参数  
      // return age => state.students.filter(s => s.age == age)
      return age => {
        return state.students.filter(s => s.age == age)
      }
    },
  }
})

// 页面调用, 注意此方法是一个属性,正常不用加 括号
// {{$store.getters.getAgeGreate18StuCount}}
// {{$store.getters.getAgeStu(18)}}
getter 转为 computed 计算属性 映射

mapgetters辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:

<script>
import { mapGetters } from "vuex";

export default {

  computed: {
    // 两种语法
    ...mapGetters(['getAgeGreate18Stu', 'getAgeGreate18StuCount'])
    // ...mapGetters({
    //   Greate18Stu: 'getAgeGreate18Stu'
    // })
  }
}
</script>

Mutation

Vuex 的 store 状态的更新唯一方式:提交 Mutation

Mutation 主要包括两部分:

  • 字符串的事件类型(type)
  • 一个回调函数( handler), 该回调函数的第一个参数就是 state, 可接收第二个外部传入的参数
const store = new Vuex.Store({
  state: {
    counter: 42,
  },
  mutations: {    
    decrementNum(state, num){
      state.counter -= num
    },
    // 特殊的提交封装
  },
})

Tips: 参数的传递,参数被称为是 mutation 的载荷( Payload )。

  • 普通的提交封装: $store.commit('decrementNum', 5), 参数的类型并没有做出限制,也可以是一个对象。
  • 特殊的提交封装: $store.commit({type: 'decrementNum', num: 5}) 此时 decrementNum 第二个参数接收到是整个传入对象 num == {type: 'decrementNum', num: 5}, 使用需要 num.num
mutation 提交风格

Vuex 的 storer 中的 state 是响应式的,当 stater 中的数据发生改变时, Vue 组件会自动更新 这就要求我们必须遵守一些 Vuex 对应的规则

  1. 提前在 store 中初始化好所需的属性。

  2. 当给 statel 中的对象添加新属性时,使用下面的方式

  3. 使用 vue.set(obj,' newprop',123)

  4. 用新对象给旧对象重新赋值

const store = new Vuex.Store({
  state: {
        info: {
      name: 'test',
    }
  },
  mutations: {    
    updateInfo(state){
      // 直接增加属性,是不支持响应式的
            // state.info['age'] = 18

      // 支持响应式的方法
      vue.set(state.info, 'age', 18)      
    },    
  },
})
mutation 常量类型
  • 在 mutation 中,我们定义了很多事件类型(也就是其中的方法名称
  • 当我们的项目增大时, Wuex 管理的状态越来越多,需要更新状态的情况越来越多,那么意味着 Mutation 中的方 法越来越多。
  • 方法过多,使用者需要花费大量的经历去记住这些方法,甚至是多个文件间来回切换查看方法名称甚至如果不 是复制的时候,可能还会出现写错的情况。

官方推荐的一种方法

// /src/store/mutations-types.js

export const UPDATEINFO = 'updateInfo'
// /src/store/index.js
import {UPDATEINFO} from './mutations-types'

const store = new Vuex.Store({
    // ...
  mutations: {    
    [UPDATEINFO](state){
        // ...
    },    
  },
})
<template>
    <button @click="$store.commit(UPDATEINFO)">修改属性</button>
</template>

<script>
  import {UPDATEINFO} from '../store/mutations-types'
</script>
mutation 同步函数

通常情况下, Vuex 要求我们 Mutation 中的方法必须是同步方法

  • 主要的原因是当我们使用 devtoolse 时,可以 devtoolsi 可以帮助我们捕捉 mutation 的快照
  • 但是如果是异步操作,那么 devtools 将不能很好的追踪这个操作什么时候会被完成

TIps: 如果必须要进行异步操作,请使用 Action

Aition

Action 类似于 Mutation, 但是是用来代替 Mutation 进行异步操作的

const store = new Vuex.Store({
  // ...
  mutations: {    
    [UPDATEINFO](state){
        // ...
    },    
  },
  actions: {
    // context: 上下文
    aUpdateInfo(context){
      setTimeout(() => {
        // 不要直接修改其值,还是需要调用 mutation进行修改
        context.commit(UPDATEINFO)
      }, 1000)      
    }
  }
})

调用

<template>
    <button @click="$store.dispatch(aUpdateInfo)">修改属性</button>
</template>

Tips: Aition 的传参方式与 Mutation 相同。

封装此操作,向外部传值:

const store = new Vuex.Store({
  actions: {
    // context: 上下文
    aUpdateInfo(context){
      return new Promise(resolve => {
        setTimeout(() => {
          context.commit(UPDATEINFO)
          resolve('完成操作')
        }, 100)
      })  
    }
  }
})

// 外部调用
// $store.dispatch(aUpdateInfo).then(data => console.log(data))
Aition 转为 methods 静态方法 映射
<script>
import { mapActions } from "vuex";

export default {

  methods: {
    // 两种语法
    ...mapActions(['aUpdateInfo', 'aUpdateInfo2'])
    // ...methods({
    //   aUp: 'aUpdateInfo'
    // })
  }
}
</script>

modules 模块

Vue 使用单一状态树那么也意味着很多状态都会交给 Vuex 来管理。当应用变得非常复杂时 store 对象就有可能变得相当臃肿,为了解决这个问题, Vuex 允许我们将 store 分割成模块 :而每个模块拥有自己的 state、mutation、action、 getters 等

const moduleA = {
  state: {
    name: 'test'
  },  
  mutations: {
    updateName(state, payload){
      state.name = payload
    }
  },
  actions:{
    aUpdateName(context){
      // context: 上下文,此处代表节点为 moduleA
      setTimeout(() => {
        context.commit('updateName', 'xming')
      })
    }
  },
  getters:{
        // 对于模块内部的 getter,根节点状态会作为第三个参数暴露出来
    fullName(state, getters, rootState){
      return getters.fullName + rootState.counter
    }
  },
}
const moduleB = {
  state: {
    counter: '第三个参数',
  },  
  mutations: {},
  actions:{},
  getters:{},
}

const store = new Vuex.Store({
  // ...
  modules: {
    a: moduleA,
    b: moduleB,
    // ...
  }
})

使用:

<template>
    <span>{{ $store.state.a.name }}</span>
    <button @click="$store.commit('updateName', 'lisi')">修改属性</button>
</template>

注意: modules 除去查看 state, 其他方法( mutations , actions , getters )都默认使用父级的命名空间:$store.getters. 。注意命名重复的问题。如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。

plugins

Vuex 的 store 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。

const myPlugin = store => {
  // 当 store 初始化后调用
  store.subscribe((mutation, state) => {
    // 每次 mutation 之后调用
    // mutation 的格式为 { type(当前修改数据的方法名, payload(当前被修改的数据 }
  })
}

然后像这样使用:

const store = new Vuex.Store({
  // ...
  plugins: [myPlugin]
})  

Tips: modules 中不能使用此方法,实测调用无效。Aition 提交的 mutation ,不会被记录为 mutation ,而是整合为 Aition, 则mutition.type == AitionName

网络请求封装 axios

Vue 作者 推荐了一个框架: axios. axios 有非常多的优点并且用起来也非常方便。

功能特点

  • 在浏览器中发送 XMLHttpRequests 请求
  • 在 node. js 中发送 htp 请求
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据

支持多种请求方式

  • axios(config)
  • axios.request(config)
  • axios.get(url [, config])
  • axios.delete(url [, config])
  • axios.head(url [, config])
  • axios.post(url [ , data [, config])
  • axios.put(url [ , data [, config])
  • axios.patch(url[ , data [, config])

安装

 npm install axios --save

使用全局的 axios 发送并发请求

// 全局配置
axios.defaults.baseURL = 'https://autumnfish.cn'
axios.defaults.timeout = 5000

axios
  .all([
    axios({
      url: "/search?keywords=tes",
    }),
    axios({
      url: "/search?keywords= 海阔天空",
    }),
  ])
  .then((results) => console.log(results))

使用 axios 的实例进行请求

const instance = axios.create({
  baseURL: 'https://autumnfish.cn',
  timeout: 5000,
})

instance
  .all([
    axios({
      url: "/search?keywords=tes",
    }),
    axios({
      url: "/search?keywords= 海阔天空",
    }),
  ])
  .then((results) => console.log(results))

为什么需要封装

避免项目对一个第三方框架依赖太深,在多个地方(文件)导入一个无法自己定义的三方组件,如果后面因为各种原因不再使用这个框架,则需要到每个地方进行修改。而如果将第三方框架封装到一个文件里面,这以后只需要修改这一个文件就好。

简单封装

import axios from "axios"

// 版本一
// export function request(config) {
//   return new Promise((resolve, reject) => {
//     const instance = axios.create({
//       // 单个实例 config
//       baseURL: "https://autumnfish.cn",
//       timeout: 5000,
//     })
//     instance(config)
//       .then((res) => resolve(res))
//       .catch((err) => reject(err))
//   })
// }

export function request(config) {
  // axios 本身就算一个 Promise 对象,不再需要在外部再套一层封装
  const instance = axios.create({
    // 单个实例 config
    baseURL: "https://autumnfish.cn",
    timeout: 5000,
  })
  return instance(config)
}

axios 拦截器

import axios from "axios"

// 拦截器,拦截请求
axios.interceptors.request.use(
  (ret) => {
    console.log(ret)
    // 必须将此次请求发送出去
    return ret
  },
  (err) => {
    console.log(err)
  }
)
// 响应拦截
axios.interceptors.response.use(
  (res) => {
    console.log(res)
    return res.data
  },
  (err) => {
    console.log(err)
  }
)

相关推荐:

来自系列:Vue 笔记

分类 前端下文章:

微信小程序开发 days1 关注点:文件结构,json配置文件, 模板语法

小程序开发学习笔记 days2。 关注点:view, text, rich-text, button, image, navigator, icon, swiper, radio, checkbox

小程序学习笔记 days3。 关注点:生命周期,自定义组件

小程序学习笔记 days4。 关注点:scroll-view, Promise

css3 选择器,背景定义(定位), 盒子定位,弹性布局,栅格系统。还有一些常用的 css 属性。

更多...

评论([[comments.sum]])

发表

加载更多([[item.son.length-2]])...

发表

2020-11 By chuan.

Python-flask & bootstrap-flask

图片外链来自:fghrsh

互联网ICP备案号:蜀ICP备2020031846号