为什么我要这么做?
本人一个重度vue患者,最近想学习react,但是总是感觉浑身不舒服,比如不理解为什么管理状态需要调用React.useState,还要解构一个真数据一个设置器函数。比如为什么React没有像Vue那样导出好几个生命周期函数,而是要在React.useEffect里面找到合适的调用时机。React给我的一整个感觉就是:不方便,不顺手,找不到逻辑该写哪,很让初学者挠头。估计是因为我用了好几年的vue,被宠坏了,没办法改过来
于是!
我开始对React大刀阔斧,想办法把它调教成我熟悉的风格,一开始是只有两个生命周期函数,用了两天,感觉对react的其他部分也有点意见,既然函数名都换了,那干脆直接定义一个useVue得了,传入一个类似于vue的配置对象,直接获得媲美于vue的开发习惯
封装的useVue代码
js
import React from "react";
import * as immer from 'immer';
function onMounted(cb) {
React.useEffect(() => {
cb()
}, []);
}
function onBeforeUnmount(cb) {
React.useEffect(() => {
return cb
}, []);
}
function useCustomReactive(originalData) {
let [data, setDataHandler] = React.useState(originalData)
function setData(draftchange) {
setDataHandler(immer.produce(data, draftchange))
}
return [data, setData]
}
/**
* 使用 Vue 风格的配置对象
* @param {Object} vm - vm配置对象,必传
* @param {返回值是JSX组件的函数} vm.template - 必需的模板函数,用于渲染内容 通过 `<p> {this.$data.xxx} </p>` 访问数据
* @param {初始化数据对象|返回一个对象的函数} [vm.data] - 数据对象或返回数据对象的函数 通过 `this.$setData(draft=>{ draft.xxx = this.$data.xxx+1 })` 设置数据。
* @param {对象} [vm.methods] - 方法配置 通过 `this.$methods.func_name()` 调用。
* @param {对象} [vm.computed] - 计算属性对象 通过 `<p> {this.$computed.xxx} </p>` 访问数据
* @param {生命周期} [vm.mounted] - 挂载完成时的回调函数
* @param {生命周期} [vm.beforeUnmount] - 卸载前的回调函数
* @returns {React组件} 模板函数的返回值
*/
export function useVue(vm) {
if (vm.data) {
if (typeof vm.data === 'function') {
vm.data = vm.data()
}
let [$data, $setData] = useCustomReactive(vm.data)
vm.$data = $data
vm.$setData = $setData
}
if (vm.methods) {
for (let functionName in vm.methods) {
vm.methods[functionName] = vm.methods[functionName].bind(vm)
}
vm.$methods = vm.methods
}
if (vm.computed) {
for (let propertyName in vm.computed) {
vm.computed[propertyName] = vm.computed[propertyName].bind(vm)
}
vm.$computed = vm.computed
}
if (vm.mounted) onMounted(vm.mounted.bind(vm))
if (vm.beforeUnmount) onBeforeUnmount(vm.beforeUnmount.bind(vm))
return vm.template()
}
使用示例
jsx"use
import React from 'react';
import { Button } from '@douyinfe/semi-ui';
import { useVue } from '@/app/utils.js'
export default function MyComponents() {
return useVue({
template() {
return <>
<Button onClick={this.$methods.toggle_show_load_button}>显示或隐藏加载按钮</Button>
<Button onClick={this.$methods.init_list} style={{ 'display': this.$data.show_button ? 'block' : 'none' }}>开始加载</Button>
<br />
<ul>
{this.$data.list.map(item => <li key={item.key}>{item.value}</li>)}
</ul>
<br />
<ol>
{this.$computed.all_chunk().map(item => <li key={item}>{item}</li>)}
</ol>
</>
},
data() {
return {
show_button: true,
list: [],
chunk_1: [1, 2, 3, 4, 5],
chunk_2: [6, 7, 8],
}
},
computed: {
all_chunk() {
return [...this.$data.chunk_1, ...this.$data.chunk_2]
}
},
mounted() {
this.$methods.toggle_show_load_button()
},
methods: {
init_list() {
setTimeout(() => {
this.$setData(draft => {
draft.list = (new Array(10)).fill(0).map((_, key) => ({ value: Math.random(), key }))
draft.chunk_1 = this.$data.chunk_1.slice(0,3)
})
}, 2000) // 模拟请求,两秒后加载出数据
},
toggle_show_load_button() {
this.$setData(draft => {
console.log(this.$data.show_button)
draft.show_button = !this.$data.show_button
})
}
}
})
}