修复监听失效问题+添加SetupComponent组件支持
const Temp = defineSetupComponent({
setup(p: { prop1: string }) {
const a = ref(1);
const data = reactive({
count: 1,
add() {
this.count++;
},
});
return {
a,
data,
p: p.prop1,
};
},
render(ctx) {
return (
<div>
<h1>{ctx.p}</h1>
<h1>{ctx.data.count}</h1>
<h2>{ctx.a.value}</h2>
<Button onClick={() => ctx.a.value++}>测试Ref</Button>
<Button type="primary" onClick={() => ctx.data.add()}>
测试按钮
</Button>
</div>
);
},
});
允许编写setup组件
-
useOnce(doWatch)的实现无法保证幂等,简言之useRef与useState在<React.StrictMode>下的表现是等价的,并不解决<React.StrictMode>下的问题。 -
onMounted(doWatch)的实现是有用的,确实解决了问题,但是改变了useWatch的执行时机。在 Vue 的设计模式中,因 steup 始终只运行一次,故允许在组件 render 之前产生 watch 带来的同步副作用,特别是使用了{ immediate: true }或者watchEffect时。但这并不符合 React 的设计思想,所以对<React.StrictMode>的兼容其实是一个取舍问题。我会更加倾向于后者 React 的理念,但这需要一定程度的重构。 -
defineSetupComponent是一个很有创意的想法,社区也已有类似实现,但它并不是 veact 的实现目的,veact 专注在「React 中的 mutable state 管理」。
import React from 'react'
import { useRef, onMounted, useWatch } from 'veact'
export const Component: React.FC = () => {
const refData = useRef(true)
const incrementData = () => {
refData.value++
}
onMounted(() => {
console.log('component mounted')
})
useWatch(
refData,
() => console.log('refData changed'),
{ immediate: true },
)
return (
<div>
<pre>ref.value = {JSON.stringify(refData.value, null, 2)}</pre>
<button onClick={incrementData}>increment ref.value</button>
</div>
)
}
在以上组件的示例中,如果采用 onMounted(doWatch) 的设计,则两行日志都必定在组件完全挂载后才会执行,而它们的打印顺序,取决于业务代码的先后顺序,此方案在 <React.StrictMode> 下是有效的。
按照 veact 当前的设计实现,'refData changed' 必定在 'component mounted' 之前打印,且处于组件尚未挂载时的时机,更符合 Vue 的数据组织理念,但与 React「渲染过程不应产生副作用」的理念相悖,且在 <React.StrictMode> 下无法工作。
这个实现已经在strict模式下测试成功,可以正常更新,打印顺序正常,初始在useOnce执行,挂载之前就立刻监听,后使用mount周期处理资源释放 useOnce目前没有发现错误情况
如果要取消挂载时监听 就得取消卸载时取消监听 这个watch就无法释放
或者onbeforeunmount的实现错误,观察到这个hook会在组件第一次执行完成后立刻执行一次导致监听失效
实际上vue的组件也会多次挂载卸载 直接声明的watch也会在挂载卸载时停止和激活
https://codesandbox.io/p/sandbox/qxpmxd