在线 Playground 测试
state 简单状态
vue
const count = ref(0);
const increment = () => (count.value += 1);
react
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
state 复杂状态
vue
state = reactive({
value: 0,
});
state.value = 1;
react
const [state, setState] = useReducer((prev, { ...next }) => ({ ...prev, ...next }), {
value: 0,
});
setState({ value: 1 });
mounted 初始渲染
vue
onMounted(() => {
console.log("mounted");
});
react
useEffect(() => {
console.log("mounted");
}, []);
unmounted 被卸载
vue
onUnmounted(() => console.log("unmounted"));
react
useEffect(() => {
// cleanup
return () => {
console.log("unmounted");
};
}, []);
watch
vue
watch(
[() => props.data],
(data) => {
console.log(data);
},
{ immediate: true, deep: false }
);
react
// immediate watch
useEffect(() => {
console.log(props.data);
}, []); // 空数组, 初始化时立即执行
// 常规 watch
useEffect(() => {
console.log(props.data);
}, [props.data]);
// deep watch
useEffect(() => {
console.log(props.data);
}, [JSON.stringify(props.data)]);
// deep watch2, 用 useRef 记录上一次的值, 然后再比较
const prevRef = useRef();
useEffect(() => {
prevRef.current = props.data;
}, [props.data]);
useEffect(() => {
if (!_.isEqual(prevRef, props.data)) {
console.log("changed:", prevRef, props.data);
}
}, [prevRef.current, props.data]);
watchEffect 立即执行, 并在依赖变化时执行
vue
watchEffect(() => {
console.log(props.data);
});
react
useEffect(() => {
console.log(props.data);
}, [props.data]);
computed 缓存值
vue
const name = computed(() => {
return `${userName}`;
});
react
const name = useMemo(() => {
return `${userName}`;
}, [userName]);
ref
vue
const elem = ref<HTMLElement>(); // elem.value
// template
<div ref="elem" />
react
const elem = useRef<HTMLElement>(); // elem.current
// jsx
<div ref={elem} />
父子组件通信
vue
// 通过props/attrs传递给子组件; 通过emit传递给父组件
// 父组件
<template>
<Child :name="'test'" @change="onChanged" />
</template>
// 子组件
<template>
<input :value="name" @input="handleChange" />
</template>
// <script setup>
const props = defineProps({
name: String,
})
const attrs = useAttrs() // class/style/v-on
const emit = defineEmits(['change'])
const handleChange = (e) => {
emit('change', e.target.value)
};
react
// 通过props传递数据和回调函数给子组件
// 父组件
<Child name={"test"} onChange={onChanged} />;
// 子组件
const Child = ({ name, onChange }) => {
const handleChange = (e) => {
onChange(e.target.value);
};
return <input value={name} onChange={handleChange} />;
};
Provide/Inject 子孙组件传值
vue
// 父组件
<template>
<div>
<Child />
</div>
</template>
<script setup>
import { provide } from "vue";
provide("abc", "123");
</script>
// 子孙组件
<template>
<div>
{{ abc }}
</div>
</template>
<script setup>
import { inject } from "vue";
const abc = inject("abc");
</script>
react
// context.ts
import { createContext } from "react";
export default createContext({});
// 父组件
import TestContext from "./context.ts";
const Parent = () => {
return (
<TestContext.Provider value="123">
<Child />
</TestContext.Provider>
);
};
// 子孙组件
import { useContext } from "react";
import TestContext from "./context.ts";
const Child = () => {
const abc = useContext(TestContext);
return <div>{abc}</div>;
};
slot 插槽
vue
// 父组件
<template>
<Child>
<template #default="slotProps">
test {{ slotProps }}
</template>
</Child>
</template>
// 子组件
<template>
<div>
<slot name="default" v-bind="{ t: 1 }" />
</div>
</template>
react
// 父组件
<Child bind={(p) => JSON.stringify(p)}>test</Child>;
// 子组件
const Child = ({ children, bind }) => {
return (
<div>
{children} {bind({ t: 1 })}
</div>
);
};
css
vue
// scoped
<template>
<div class="test" />
</template>
<style scoped>
.test {}
</style>
// css module
<template>
<div :class="[$style.test]" />
</template>
<style lang="scss" module>
.test {}
</style>
// vue3 还可以使用v-bind 绑定<script>中的变量
react(css module)
import styles from "./Test.module.scss";
const Test = (props) => <div className={styles.test} />;
更推荐 css module 方案, react/vue 可复用