不同前端框架实现相似功能的差异与注意
声明周期Hook回调的差异
讨论当组件挂载和卸载的回调方式差异。
Vue 3
<script setup>
onMounted(()=>{
// 当挂载时...
});
beforeUnmount(()=>{
// 当卸载时...
});
</script>
Vue 3中执行挂载和卸载回调方式是使用特定函数来注册回调方法。注意,绝大部分情况下,可能需要手动导入这两个生命周期函数。
Svelte 4
<script>
onMount(()=>{
// 当挂载时...
});
onDestroy(()=>{
// 当卸载时...
});
</script>
与Vue 3相似的,Svelte也使用这种方式注册回调方法。同样的,可能需要手动导入这两个方法。
React
function ComponentExample(){
useEffect(()=>{
// 当挂载时...
return function cleanUp(){
// 当卸载时...
}
}, []);
return (<></>);
}
与前两个框架实现方式不同,React中使用useEffect
钩子来实现这个功能。由于React通常用于开发SSR(Server-side render,服务端渲染)应用,useEffect
钩子本身用于执行具有“副作用”的代码,这段代码将会始终在浏览器端运行而不是服务端预渲染。React默认情况下需要手动指定要追踪变更的响应式变量以便重新执行这些钩子,因此若第二个参数给空数组,则代表不追踪任何变化,这个方法将会仅运行一次。而在这个钩子注册的回调若返回一个函数,则该函数视为解除/恢复/消除此“副作用”的方法,因此可以将useEffect(fn, [])
视为挂载时的生命周期钩子,而fn返回的函数视为卸载时的声明周期钩子。注意useEffect
可能需要手动地导入。
Solid
function App() {
onMount(() => {
// 当挂载时
});
onCleanup(() => {
// 当卸载时
});
return <></>;
}
虽然Solid作为React模仿者,很多语法与React相似,但是这里是确确实实不同的。由于React的函数体会在每次变更时重新执行以渲染,因此可以每次决定useEffect
是否执行。而Solid的函数体本身仅仅执行一次,因此本身更类似于前两个框架。为了避免SSR错误,应该将挂载和卸载回调写在onMount
和onCleanup
中。同样的,必要的时候应该手动导入这两个生命周期钩子。
定义响应式变量的差异
这里仅讨论最基本的响应式,例如Vue的ref而不是reactive,例如svelte的$var而不是writable。
Vue 3
<script setup>
// 创建一个响应式变量
const variableRef = ref(0); // => Ref<Number>, ref()的参数是初始化值
function readVar(){
// 读取响应式变量的值
return variableRef.value; // => Number
}
function setVar(value){
// 设置响应式变量的值
variableRef.value = value;
}
</script>
<template>
<div>
{{variableRef}}
</div>
</template>
Vue 3 的 Composition API 写法的script标签本质上是setup函数,用于配置一个组件。(整体上下文为一个组件)。因此,setup方法中的内容只运行一次,并且若开启SSR,部分代码调用需要写在生命周期钩子中。
使用 ref
创建的响应式会返回一个 Ref 对象,此对象可以结构出其结果,但是解构操作也会破坏其响应式特性,因此,读取变量是使用 Ref.value
,设置值直接对 Ref.value
设置即可,通过代理或定义setter等方式实现响应式。
Svelte 4
<script>
// 创建一个响应式变量
let variable = 0;
function readVar(){
// 读取响应式变量的值
return variable;
}
function setVar(value){
// 设置响应式变量的值
variable = value;
}
</script>
<div>
{variable}
</div>
Svelte 4 框架的重要差别在于代码会经历编译而变成原生实现的JS代码,编译后不再有 Svelte Runtime 这种东西参与。在Svelte中,使用 let
声明的可变变量在编译时会被全部自动的设为响应式,并会通过变量名追踪所有的变量使用,因此直接对变量本身执行读写即可。
Svelte的script标签本身也是一种setup,其中的代码只执行一次。
React
function ComponentExample(){
// 创建响应式变量
const [variable, setVariable] = useState(0);
function readVar(){
// 读取响应式变量的值
return variable;
}
function setVar(value){
// 设置响应式变量
setVariable(value);
}
return (
<div>
{variable}
</div>
);
}
与前两个框架不通,React最常见的是一种函数式组件的写法,其中组件函数本身的代码会被反复执行,最终返回JSX格式的结构。
创建响应式变量时,由于代码反复执行的特点,每一次都结构出当前变量的值以及对应的setter进行使用,并更新渲染。
Solid
function ComponentExample(){
// 创建响应式变量
const [variable, setVariable] = createSignal(0);
function readVar(){
// 读取响应式变量的值
return variable();
}
function setVar(value){
// 设置响应式变量
setVariable(value);
}
return (
<div>
{variable()}
</div>
);
}
在SolidJS中,虽然采用了和React相似的函数式组件的写法,但是组件函数本身仅仅执行一次,其中的逻辑与前两个框架的setup概念相似。因此,创建响应式变量时解构的内容从变量本身变成一个变量的getter,每一次读取变量都是在执行这个getter,从而达成响应式。