React 和 Vue computed 属性的对比

407 阅读1分钟

最近又在学习 React 的基础,看了一篇文章:www.robinwieruch.de/react-compu…

React

import React from 'react';
import sortBy from 'lodash.sortby';
import { v4 as uuidv4 } from 'uuid';

function App() {
  const [name, setName] = React.useState('');
  const [list, setList] = React.useState([
    { id: '1', name: 'Apple', count: 5 },
    { id: '2', name: 'Banana', count: 3 },
    { id: '3', name: 'Peach', count: 10 },
  ]);
  const [sort, setSort] = React.useState('name');

  function handleSortName() {
    setSort('name');
  }

  function handleSortCount() {
    setSort('count');
  }

  function handleChange(event) {
    setName(event.target.value);
  }

  function handleAdd() {
    const newItem = {
      id: uuidv4(),
      name: name,
      count: 0,
    };
    const newList = list.concat(newItem);
    setList(newList);
  }

  const sortedList = sortBy(list, sort); // C

  return (
    <div>
      <h1>Computed Properties in React</h1>
      <div>
        <input type="text" value={name} onChange={handleChange} />
        <button type="button" onClick={handleAdd}>
          Add
        </button>
      </div>
      <button type="button" onClick={handleSortName}>
        Sort by Name
      </button>
      <button type="button" onClick={handleSortCount}>
        Sort by Count
      </button>
      <ul>
        {sortedList.map((item) => (
          <li key={item.id}>
            <span>{item.name}</span>:<span>{item.count}</span>
          </li>
        ))}
      </ul>
    </div>
  );
}

export default App;

React 中并没有 Computed property 这个概念。如 C,sortedList 只是一个变量,不是 state,也没有 computed。对比 computed,react 就是 just deriving values from the raw state.

关键在于:我们让组件每一次更新渲染时,重新计算 sortedList。想象一下,组件每次更新,function component 执行。

关于设置 state,可以参考 React 哲学。第三步,确定 UI state 的最小(且完整)表示。首先我们可以在 onClick 事件中处理修改 list state,这样没有 sort state。但是一旦逻辑更加复杂,比如新增 item,要使得列表仍然保持排序,就必须增加显式的 sort state。

(性能)不过当组件每一次渲染时,都会重新计算 sortedList,这时候就可以使用 useMemo:

const sortedList = React.useMemo(() => {
  console.log('Calculates computed properties ...');
  return sort.isReverse
    ? sortBy(list, sort.property).reverse()
    : sortBy(list, sort.property);
}, [list, sort]);

Vue

Vue 就使用 computed 的实现:

<template>
  <div>
    <ul>
      <li v-for="item in sortedList" :key="item.id">
        {{ item.name }}: {{ item.count }}
      </li>
    </ul>
    <input v-model="name" type="text" />
    <button type="button" @click="handleAddItem">Add</button>
    <button type="button" @click="handleSortByName">Sort By Name</button>
    <button type="button" @click="handleSortByCount">Sort By Count</button>
  </div>
</template>

<script>
import {
  reactive,
  toRefs,
  computed
} from 'vue'
import { sortBy } from 'lodash'
export default {
  setup: () => {
    const state = reactive({
      list: [
        { id: '2', name: 'Banana', count: 3 },
        { id: '1', name: 'Apple', count: 5 },
        { id: '3', name: 'Peach', count: 10 }
      ],
      sort: '',
      name: '',
    })

    const handleSortByName = () => {
      state.sort = 'name'
    }

    const handleSortByCount = () => {
      state.sort = 'count'
    }

    const sortedList = computed(() => {
      return sortBy(state.list, state.sort)
    })

    const handleAddItem = () => {
      state.list.push({
        id: state.list.length + 1,
        name: state.name,
        count: 0
      })
    }
    
    return {
      ...toRefs(state),
      sortedList
      handleSortByName,
      handleSortByCount,
      handleAddItem
    }
  }
};
</script>

参考: