思考
当我们遇到一个复杂的页面时:
1.数据共享,父组件和子孙组件的数据共享,数据共享很多都是远程获取的数据;
2.数据处理和页面的交互未分离
昨天读了 @wangly19 的文章,对实现的方式思考才有了本文章
实现
实现方式:使用 vue.observableAPI 和 mixins,mixins主要是去做初始化和销毁
使用createModelComponent会在组件挂载useData属性,访问model里的state 和useDispatch方法,访问model里的方法
createModelComponent.js
/**
* model层专注数据处理
* 抽离model要实现的目标
* 1.抽离model层业务
* 2.数据共享
*/
import Vue from 'vue'
/**
* 是否是被监听的对象
* @param {object} state
* @returns {boolean}
*/
function isObservable(state) {
return hasOwnProperty(state, '__ob__')
}
/**
* useData计算属性
* @param {object} model
* @returns {object}
*/
function computedUseData(model) {
return {
get: () => model.state,
set() {
throw ('do not set useData')
}
}
}
/**
* 对象本身是否该属性
* @param {object} obj
* @param {string} property
* @returns {boolean}
*/
function hasOwnProperty(obj, property) {
return Object.hasOwnProperty.call(obj, property)
}
/**
* 分发model层actions事件
* @param {object} model
* @returns {function}
*/
function useDispatch(model) {
return function (actionName, payload) {
if (!hasOwnProperty(model, actionName)) {
throw (`${actionName} not defined`)
}
if (typeof model[actionName] !== 'function') {
throw (`${actionName} not function`)
}
return model[actionName](payload)
}
}
/**
* 深拷贝
* @param {object} obj
* @param {map} hash
*/
function deepClone(obj, hash = new WeakMap()) {
if (obj === null) return obj
if (obj instanceof Date) return new Date(obj)
if (obj instanceof RegExp) return new RegExp(obj)
// 可能是对象或者普通的值 如果是函数的话是不需要深拷贝
if (typeof obj !== 'object') return obj
// 是对象的话就要进行深拷贝
if (hash.get(obj)) return hash.get(obj)
let cloneObj = new obj.constructor()
// 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
hash.set(obj, cloneObj)
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash)
}
}
return cloneObj
}
/**
* 在组件生命周期里初始化 model
* @param {objec} model
* @returns {object}
*/
function initModelMixin(model) {
return {
beforeCreate() {
if (model.state && !isObservable(model.state)) {
const state = Vue.observable(model.state)
model.state = state
}
},
destroyed() {
model.state = deepClone(model.$state)
}
}
}
/**
* 创建model层组件
* 组件挂载useData属性访问model层的state
* @param {object} component 组件
* @param {object} model 组件的model层
* @returns {object}
*/
export function createModelComponent(component, model = {}) {
let newComponent = { ...component }
if (model.state) {
if (!model.$state) {
model.$state = deepClone(model.state)
}
newComponent.computed = {
...component.computed,
useData: computedUseData(model)
}
}
newComponent.methods = {
...component.methods,
useDispatch: useDispatch(model)
}
const mixins = component.mixins ? component.mixins : []
newComponent.mixins = [initModelMixin(model), ...mixins]
return newComponent
}
使用
<template>
<div class="home">
{{ useData.userName }}
<img alt="Vue logo" src="../assets/logo.png" />
<button @click="handleClick">actions</button>
<button @click="handleClick3">actions3</button>
<HelloWorld msg="Welcome to Your Vue.js App" />
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld.vue'
import { createModelComponent } from '@/libs/createModelComponent'
import homeModel from './homeModel'
export default createModelComponent(
{
name: 'Home',
components: {
HelloWorld,
},
mounted() {
console.log(this)
},
methods: {
handleClick() {
this.useData.userName = 'Gavin'
},
handleClick3() {
this.useDispatch('getUerInfo')
},
},
destroyed() {
console.log('destory')
},
},
homeModel
)
</script>
homeMdel.js
/**
* this的指向本身对象
* 不能访问到组件本身,这也是model层的作用,专注数据处理和数据共享
*/
export default {
state: {
userName: ''
},
async getUerInfo() {
this.state.userName = 'Gavin 2'
}
}