why?
- 什么是 composition-api
- 为什么推荐使用 composition-api
推荐阅读, 没有什么比官方文档, 说的还要准确了
useState 实现
实现这些 API, 只是体验尝试下 composition-api 的强大和灵活
js
import { ref } from 'vue'; // 注: 需要使用 ref API 来实现响应式
// 类似 react-hooks 的 useState
export function useState(initValue) {
const state = ref(initValue);
const setState = function (newState) {
state.value = typeof newState === 'function' ? newState(state) : newState;
};
return {
state,
setState,
};
}
useReducer 实现
js
import { useState } from './useState'; // 注: 依赖于 useState
export function useReducer(initState, reducer) {
const [state, setState] = useState(initState);
const dispatch = (action) => reducer(state.value, setState, action);
return [state, dispatch];
}
useReactive 实现
js
import { toRefs, reactive } from 'vue';
export function useReactive(initState) {
const state = reactive(initState);
const stateRefs = toRefs(state);
// 因为 reactive 直接解构会失去响应式
// stateRefs 方便setup函数直接解构返回 ...stateRefs
const setState = (key, val) => {
if (Object.prototype.toString.call(key) === '[object Object]') {
// 如果第一个参数是一个对象, 直接忽略第二个参数, 根据 key-value 对赋值更新
for (const [k, v] of Object.entries(key)) {
state[k] = v;
}
} else {
// 如果第二个参数是一个函数, 先执行获得返回值, 然后赋值, 不是函数则直接赋值
const newValue = typeof val === 'function' ? val(state[key]) : val;
state[key] = newValue;
}
};
return [state, setState, stateRefs];
}
使用 useState && useReducer
vue
<template>
<div>
<h2>{{ count }}</h2>
<button @click="setCount((n) => n + 2)">+</button>
<button @click="setCount(count - 1)">-</button>
</div>
<div>
<h2>{{ num }}</h2>
<button @click="numDispatch({ type: 'PLUS', payload: 2 })">+</button>
<button @click="numDispatch({ type: 'MINUS', payload: 1 })">-</button>
</div>
<div>
<p>name: {{ info.name }}</p>
<p>age: {{ info.age }}</p>
<button @click="setInfo('age', 66)">setInfo1</button>
<button @click="setInfo({ name: 'abc', age: 11, sex: 1 })">setInfo2</button>
<button @click="setInfo('name', (name) => name + '[vip]')">setInfo3</button>
</div>
</template>
<script setup>
// --- useState
import { useState, useReducer, useReactive } from '@/hooks';
const [count, setCount] = useState(10);
// --- useReducer
const calcReducer = (num, setNum, { type, payload }) => {
switch (type) {
case 'PLUS': // 加法
setNum(num + payload);
break;
case 'MINUS': // 减法
setNum(num - payload);
break;
// 乘法
// 除法
// 取余
default:
throw new Error('[reducer] unknown type: ' + type);
}
};
const [num, numDispatch] = useReducer(1, calcReducer);
// --- useReactive
const [info, setInfo] = useReactive({
name: 'initName',
age: 22,
});
</script>