异步属性与前端模型

218 阅读2分钟

讲解在同名B/D上都有,主要介绍一些跟业务无关的代码技巧

注: 部分内容主观性较大,一家之言姑且听之

本文主要介绍异步属性的二次封装

问题

根节点处会包含某个接口信息的获取,获取后属性会存在sessionStorage内存

  • 根目录
import Vue from 'vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue'

Vue.config.productionTip = false

Vue.use(ElementUI);

const api = function () {
    return new Promise(resolve => setTimeout(() => {
        resolve({
            id: 2,
            name: "2xx"
        })
    }, 1000))
}


new Vue({
    async beforeCreate() {
        /**
         * 登录或刷新你的时候请求
         * 但不希望 在路由处使用 loading
         * 数据本身存在vuex或sessionStorage中
         */
        sessionStorage.removeItem("user")
        const data = await api()
        sessionStorage.setItem("user", JSON.stringify(data))
    },
    render: h => h(App),
}).$mount('#app')
  • 使用数据 此处会获取sessionStorage用以获取缓存中的数据,但因为是异步的,此处选择使用setTimeout进行处理

注:也可以使用路由的方式,路由拦截会产生白屏问题,作者使用了setTimeout的思路解决,但显然这里也可能有问题

<template>
    <div id="app">

    </div>
</template>
<script>
export default {
    beforeCreate() {
        setTimeout(() => {
            try {
                const user = JSON.parse(sessionStorage.getItem("user"))
                console.log(user, "user")
                // 获取到user以后,根据userid进行请求操作
                // axios.get(`/api/user/${id}`) 
            } catch (error) {

            }
        },2000)
    }
}
</script>

优化1

  • 注册

定义属性的get/set

import Vue from 'vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue'

Vue.config.productionTip = false

Vue.use(ElementUI);

const api = function () {
  return new Promise(resolve => setTimeout(() => {
    resolve({
      id: 2,
      name: "2xx"
    })
  }, 1000))
}

new Vue({
  async beforeCreate() {
    const resolves = []
    // 挂载到全局上,子组件可以使用this.$root.user使用
    Object.defineProperty(this, 'user', {
      get() {
        return new Promise((resolve) => {
          const user = JSON.parse(sessionStorage.getItem("user"))
          if (user) {
            return resolve(user)
          } else {
            resolves.push(resolve)
          }
        })
      },
      set(data) {
        resolves.forEach(resolve => resolve(data));
        sessionStorage.setItem("user", JSON.stringify(data))
      }
    })

    sessionStorage.removeItem("user")
    const data = await api()
    sessionStorage.setItem("user", JSON.stringify(data))
  },
  render: h => h(App),
}).$mount('#app')
  • 使用

使用时,使用this.$root.user进行获取

<template>
    <div id="app">

    </div>
</template>
<script>
export default {
    async beforeCreate() {
        const user = await this.$root.user
        console.log(user,'user')
    }
}
</script>

优化2

希望将代码抽象到js文件中,这里将对应的定义提取到user中

export function buildUser(scope) {
  const resolves = []
  Object.defineProperty(scope, 'user', {
    get() {
      return new Promise((resolve) => {
        const user = JSON.parse(sessionStorage.getItem("user"))
        if (user) {
          return resolve(user)
        } else {
          resolves.push(resolve)
        }
      })
    },
    set(data) {
      resolves.forEach(resolve => resolve(data));
      sessionStorage.setItem("user", JSON.stringify(data))
    }
  })

  async function loadUser() {
    sessionStorage.removeItem("user")
    const data = await api()
    scope.user = data
  }

  return loadUser
}

const api = function () {
  return new Promise(resolve => setTimeout(() => {
    resolve({
      id: 2,
      name: "2xx"
    })
  }, 1000))
}

优化3

但上面的问题在于 依赖this的指向,将this的指向抽离,包含两种写法,这里使用class的方式

  • 模型定义
/**
 * 模型的定义 
 * 状态管理是模型的一种变体
 */
class User {
  constructor() {
    this._resolves = []
  }
  get data() {
    return new Promise((resolve) => {
      const user = JSON.parse(sessionStorage.getItem("user"))
      if (user) {
        return resolve(user)
      } else {
        this._resolves.push(resolve)
      }
    })
  }
  set data(data) {
    this._resolves.forEach(resolve => resolve(data));
    sessionStorage.setItem("user", JSON.stringify(data))
  }
    
  async loadUser() {
    sessionStorage.removeItem("user")
    const data = await api()
    this.data = data
  }
}

export const user = new User()
  • 模型事件触发
import Vue from 'vue'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue'

Vue.config.productionTip = false

Vue.use(ElementUI);

import { user } from './user'

new Vue({
  async beforeCreate() {
    user.loadUser()
  },
  render: h => h(App),
}).$mount('#app')
  • 模型属性的使用
<template>
    <div id="app">

    </div>
</template>
<script>
import { user } from './user'

export default {
    async beforeCreate() {
        const userData = await user.data
        console.log(userData, 11)
    }
}
</script>

什么是模型

模型是用来描述前端数据的来源,以及序列化的内存结构,他是mvvm中的m,与vm没有任何关系,通常可以率先定义,使用模型层可以极大的解耦

通常,状态管理是模型层的一种变体,可以不用状态管理,但不能不理解模型