脚本宝典收集整理的这篇文章主要介绍了浅析Vue CompositionAPI和React Hooks对比:hook的意义、两者差别(原理-链表/Proxy、代码执行-每次渲染都执行/组件创建时运行、声明响应式状态、如何跟踪依赖、生命周期、自定义hook、Ref获取元素、计算属性附加函数、Context和provide/inject、在渲染上下文中暴露值),脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
先理解什么是 Hook,拿 React 的介绍来看,它的定义是:
它可以让你在不编写 Class 的情况下,让你在函数组件里“钩入” React state 及生命周期等特性的函数
对于 Vue 提出的新的书写 Vue 组件的 API:ComposITion API Rfc,作用也是类似,所以我们也可以像 React 一样叫做 Vue Hooks。
该 API 受到 react hooks 的启发,但有一些有趣的差异,规避了一些 React 的问题。
框架是服务于业务的,业务中很难避免的一个问题就是:逻辑复用。同样的功能,同样的组件,在不一样的场合下,我们有时候不得不去写 2+次。为了避免耦合,后来各大框架纷纷想出了一些办法,比如 mixin, render PRops, 高阶组件等实现逻辑上的复用,但是都有一些额外的问题
Hook 的出现是划时代的,通过 function 抽离的方式,实现了复杂逻辑的内部封装:
React Hooks 允许你 "勾入" 诸如组件状态和副作用处理等 React 功能中。Hooks 只能用在函数组件中,并允许我们在不需要创建类的情况下将状态、副作用处理和更多东西带入组件中。
React 核心团队奉上的采纳策略是不反对类组件,所以你可以升级 React 版本、在新组件中开始尝试 Hooks,并保持既有组件不做任何更改
import React, { useState, useEffect } From "React";
const NoteForm = ({ onNoteSent }) => {
const [currentNote, setCurrentNote] = useState("");
useEffect(() => {
console.LOG(`Current note: ${currentNote}`);
});
return (
<form
onSubmit={e => {
onNoteSent(currentNote);
setCurrentNote("");
e.preventDefault();
}}
>
<label>
<span>Note: </span>
<input
value={currentNote}
onChange={e => {
const val = e.target.value && e.target.value.toUpPErCase()[0];
const validNotes = ["A", "B", "C", "D", "E", "F", "G"];
setCurrentNote(validNotes.includes(val) ? val : "");
}}
/>
</label>
<button type="submit">Send</button>
</form>
);
};
Vue Composition API 围绕一个新的组件选项 @R_406_1315@ 而创建。setup()
为 Vue 组件提供了状态、计算值、watcher 和生命周期钩子
API 并没有让原来的 API(现在被称作 "Options-based API")消失。允许开发者 结合使用新旧两种 APIs
可以在 Vue 2.x 中通过
@vue/composition-api
插件尝试新 API
Vue Composition API 的例子就不多说了,常用。
React Hook 底层是基于链表实现,调用的条件是每次组件被 render 的时候都会顺序执行所有的 Hooks,所以下面的代码会报错
function App(){
const [name, setName] = useState('demo');
if(condition){
const [val, setVal] = useState('');
}
}
Vue Composition API
的 setup()
晚于 beforeCreate 钩子,早于 created 钩子被调用;React Hooks 会在组件每次渲染时候运行,而 Vue setup() 只在组件创建时运行一次。由于 React Hooks 会多次运行,所以 render 方法必须遵守某些规则,比如:不要在循环内部、条件语句中或嵌套函数里调用 Hooks
(1)React:useState
是 React Hooks 声明状态的主要途径
const [name, setName] = useState("Mary");
const [age, setAge] = useState(25);
usereducer
是个有用的替代选择,其常见形式是接受一个 Redux 样式的 reducer 函数和一个初始状态:
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
const [state, dispatch] = useReducer(reducer, initialState);
dispatch({type: 'increment'}); // state 就会变为 {count: 1}
useReducer 还有一种 延迟初始化 的形式,传入一个 init 函数作为第三个参数
(2)Vue:Vue 使用两个主要的函数来声明状态:ref 和 reactive。
ref()
返回一个响应式对象,其内部值可通过其 value 属性被访问到。可以将其用于基本类型,也可以用于对象
reactive()
只将一个对象作为其输入并返回一个对其的响应式代理。
同样对 Vue 的比较熟,就不多说了
React 中的 useEffect Hook 允许在每次渲染之后运行某些副作用(如请求数据或使用 storage 等 Web APIs),并在下次执行回调之前或当组件卸载时运行一些清理工作
默认情况下,所有用 useEffect 注册的函数都会在每次渲染之后运行,但可以定义真实依赖的状态和属性,以使 React 在相关依赖没有改变的情况下(如由 state 中的其他部分引起的渲染)跳过某些 useEffect Hook 执行
// 传递一个依赖项的数组作为 useEffect Hook 的第二个参数,只有当 name 改变时才会更新 localStorage
function Form() {
const [name, setName] = useState('Mary');
const [surname, setSurname] = useState('Poppins');
useEffect(function persistForm() {
localStorage.setItem('formData', name);
}, [name]);
// ...
}
显然,使用 React Hooks 时忘记在依赖项数组中详尽地声明所有依赖项很容易发生,会导致 useEffect 回调 "以依赖和引用了上一次渲染的陈旧数据而非最新数据" 从而无法被更新而告终。
解决方案:
eslint-plugin-React-Hooks
包含了一条 lint 提示关于丢失依赖项的规则useCallback 和 useMemo
也使用依赖项数组参数,以分别决定其是否应该返回缓存过的( memoized)与上一次执行相同的版本的回调或值。在 Vue Composition API 的情况下,可以使用 watch() 执行副作用以响应状态或属性的改变。依赖会被自动跟踪,注册过的函数也会在依赖改变时被响应性的调用
export default {
setup() {
const name = ref("Mary");
const lastName = ref("Poppins");
watch(function persistForm() => {
localStorage.setItem('formData', name.value);
});
}
}
由此可看,也就是说 Vue 的方式更简便。
如果你熟悉 React 类生命周期方法,那么可以将 useEffect Hook 视为 componentDidmount、componentDidUpdate 及 componentWillUnmount 的合集
useEffect(() => {
console.log("This will only run after initial render.");
return () => { console.log("This will only run when component will unmount."); };
}, []);
强调的是,使用 React Hooks 时停止从生命周期方法的角度思考,而是考虑副作用依赖什么状态,才更符合习惯
Vue Component API
通过 onMounted、onUpdated 和 onBeforeUnmount
:
setup() {
onMounted(() => {
console.log(`This will only run after initial render.`);
});
onBeforeUnmount(() => {
console.log(`This will only run when component will unmount.`);
});
}
故在 Vue 的情况下的心理模式转变更多在停止通过组件选项(data、computed, watch、methods、生命周期钩子等)管理代码,要转向用不同函数处理对应的特性。(Vue 的这种转变更平滑,也好理解,不知道是不是一直做 Vue 的原因,总感觉写 React 很麻烦)
React 团队聚焦于 Hooks 上的原因之一,Custom Hooks 是可以替代之前社区中采纳的诸如 Higher-Order Components 或 Render Props 等提供给开发者编写可复用代码的一种更优秀的方式
Custom Hooks 就是普通的 JavaScript 函数,在其内部利用了 React Hooks。它遵守的一个约定是其命名应该以 use 开头,以明示这是被用作一个 Hook 的。
// custom Hook - 用于当 value 改变时向控制台打印日志
export function useDebugState(label, initialValue) {
const [value, setValue] = useState(initialValue);
useEffect(() => {
console.log(`${label}: `, value);
}, [label, value]);
return [value, setValue];
}
// 调用
const [name, setName] = useDebugState("Name", "Mary");
Vue 中,组合式函数(Composition Functions)与 Hooks 在逻辑提取和重用的目标上是一致的,在 Vue 中实现一个类似的 useDebugState 组合式函数
export function useDebugState(label, initialValue) {
const state = ref(initialValue);
watch(() => {
console.log(`${label}: `, state.value);
});
return state;
}
// elsewhere:
const name = useDebugState("Name", "Mary");
注意:根据约定,组合式函数也像 React Hooks 一样使用 use 作为前缀以明示作用,并且表面该函数用于 setup() 中
React 的 useRef 和 Vue 的 ref 都允许你引用一个子组件 或 要附加到的 DOM 元素。
// react
const MyComponent = () => {
const divRef = useRef(null);
useEffect(() => {
console.log("div: ", divRef.current)
}, [divRef]);
return (
<div ref={divRef}>
<p>my div</p>
</div>
)
}
// vue 的就不写了
React Hooks 在每次渲染时都会运行,没有一个等价于 Vue 中 computed 函数的方法。所以你可以自由地声明一个变量,其值基于状态或属性,并将指向每次渲染后的最新值:
const [name, setName] = useState("Mary");
const [age, setAge] = useState(25);
const description = `${name} is ${age} years old`;
Vue 中,setup() 只运行一次。因此需要定义计算属性,其应该观察某些状态更改并作出相应的更新:
const name = ref("Mary");
const age = ref(25);
const description = computed(() => `${name.value} is ${age.value} years old`);
计算一个值开销比较昂贵。你不会想在组件每次渲染时都计算它。React 包含了针对这点的 useMemo Hook
:React 建议你使用 useMemo 作为一个性能优化手段, 而非一个任何一个依赖项改变之前的缓存值
Vue 的 computed 执行自动的依赖追踪,所以它不需要一个依赖项数组
React 中的 useContext Hook,可以作为一种读取特定上下文当前值的新方式。返回的值通常由最靠近的一层 <MyContext.Provider>
祖先树的 value 属性确定
// context object
const ThemeContext = React.createContext('light');
// provider
<ThemeContext.Provider value="dark">
// consumer
const theme = useContext(ThemeContext);
Vue 中类似的 API 叫 provide/inject
。在 Vue 2.x 中作为组件选项存在,在 Composition API 中增加了一对用在 setup() 中的 provide 和 inject
函数:
// key to provide
const ThemeSymbol = Symbol();
// provider
provide(ThemeSymbol, ref("dark"));
// consumer
const value = inject(ThemeSymbol);
如果你想保持响应性,必须明确提供一个 ref/reactive 作为值。
(1)在 React 的情况下
所以你对作用域中的任何值拥有完全访问能力,就像在任何 JavaScript 代码中的一样
const Fibonacci = () => {
const [nth, setNth] = useState(1);
const nthFibonacci = useMemo(() => fibNaive(nth), [nth]);
return (
<section>
<label>
Number:
<input
type="number"
value={nth}
onChange={e => setNth(e.target.value)}
/>
</label>
<p>nth Fibonacci number: {nthFibonacci}</p>
</section>
);
};
(2)在 Vue 的情况下
要达到 React 同样简洁表现的一种方式是从 setup() 自身中返回一个渲染函数。不过,模板在 Vue 中是更常用的一种做法,所以暴露一个包含值的对象,是你使用 Vue Composition API 时必然会多多遭遇的情况。
总结:
React 和 Vue 都有属于属于自己的“惊喜”,无优劣之分,自 React Hooks 在 2018 年被引入,社区利用其产出了很多优秀的作品,自定义 Hooks 的可扩展性也催生了许多开源贡献。
Vue 受 React Hooks 启发将其调整为适用于自己框架的方式,这也成为这些不同的技术如何拥抱变化且分享灵感和解决方案的成功案例
作者:微医前端团队 链接:https://juejin.cn/post/6847902223918170126
以上是脚本宝典为你收集整理的浅析Vue CompositionAPI和React Hooks对比:hook的意义、两者差别(原理-链表/Proxy、代码执行-每次渲染都执行/组件创建时运行、声明响应式状态、如何跟踪依赖、生命周期、自定义hook、Ref获取元素、计算属性附加函数、Context和provide/inject、在渲染上下文中暴露值)全部内容,希望文章能够帮你解决浅析Vue CompositionAPI和React Hooks对比:hook的意义、两者差别(原理-链表/Proxy、代码执行-每次渲染都执行/组件创建时运行、声明响应式状态、如何跟踪依赖、生命周期、自定义hook、Ref获取元素、计算属性附加函数、Context和provide/inject、在渲染上下文中暴露值)所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。