Skip to content

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>

Released under the MIT License.