Vue2的Options Api和Vue3的Composition Api
一个简单的todo
-
Options Api
<template>
<div>
<input type="text" v-model="val" @keyup.enter="addTodo">
<u>
<li v-for="todo in todos" :key="todo.id">{{todo.title}}</li>
</u>
</div>
</template>
<script>
export default {
data() {
return {
val: "",
todos: [
{ id: 0, title: "吃饭", done: false },
{ id: 1, title: "睡觉", done: false },
],
};
},
methods: {
addTodo() {
this.todos.push({
id: this.todos.length,
title: this.val,
done: false,
});
this.val = "";
},
},
};
</script>
如上code, 我们就用options api 完成了一个简单的todo
- 优点
- 做各式各样的功能时, 理解起来特别简单 data就是数据, method就是操作
- 缺点
- data, methods, computed, watch等, 可维护性差, 会导致上下反复横跳, 解决办法当然可以使用mixins
<script>
...
mixins: ['counter', 'mouse', ...]
...
</script>
mixins的最大缺点就是命名冲突,虽然能解决反复横跳的问题, 但是在大项目中很难维护, 还有this也是一个黑盒, 没办法知道this里面到底有什么, 很难做测试和做类型推导, 这也就是vue2对ts的支持特别不好的原因.
-
Compostion Api
<script>
import { reactive, ref, toRefs } from "vue";
export default {
setup() {
let val = ref("");
let todos = reactive([
{ id: 0, title: "吃饭", done: false },
{ id: 1, title: "睡觉", done: false },
]);
function addTodo() {
todos.push({
id: todos.length,
title: val.value,
done: false,
});
val.value = "";
}
return { val, todos, addTodo };
},
};
如上code, 我们就用composition api 完成了一个简单的todo
- 缺点
- 难看
- return 很蛋疼, 如果组件很大, return也会导致上下横跳
做下简化:
<script setup>
import { reactive, ref, toRefs } from "vue";
let val = ref("");
let todos = reactive([
{ id: 0, title: "吃饭", done: false },
{ id: 1, title: "睡觉", done: false },
]);
function addTodo() {
todos.push({
id: todos.length,
title: val.value,
done: false,
});
val.value = "";
}
通过在script标签上加 setup, 来解决代码冗余的问题, 解决return上下横跳, 使代码更加清爽一点
- 优点
- 可以做Tree Shaking, 没有用到computed, 代码build的时候就会删掉vue3里面的computed的代码
- 方便组合, 所有的逻辑都是函数, 组合优于继承
组合优于继承
- 什么叫组合?
用代码来解释, 写一个累加器
// 新建一个useCounter.js 文件
import { ref } from 'vue'
export default function useCounter() {
let counter = ref(1)
function addCounter() {
counter.value += 1
}
return { counter, addCounter }
}
App.vue
<template>
<div>
<h1 @click="addCounter">{{counter}}</h1>
</div>
</template>
<script setup>
import useCounter from "./useCounter";
let { counter, addCounter } = useCounter();
</script>
上面的useCounter.js就是一个组合, 上述的todos也可以进行组合
// 新建一个useTodo.js 文件
import { ref, reactive } from 'vue'
export default function useTodo() {
let val = ref('')
let todos = reactive([
{ id: 0, title: '吃饭', done: false },
{ id: 1, title: '睡觉', done: false },
])
function addTodo() {
todos.push({
id: todos.length,
title: val.value,
done: false,
})
val.value = ''
}
return { val, todos, addTodo }
}
App.vue
<template>
<div>
<h1 @click="addCounter">{{counter}}</h1>
<input type="text" v-model="val" @keyup.enter="addTodo">
<u>
<li v-for="todo in todos" :key="todo.id">{{todo.title}}</li>
</u>
</div>
</template>
<script setup>
import { reactive, ref, toRefs } from "vue";
import useCounter from "./useCounter";
import useTodo from "./useTodo";
let { counter, addCounter } = useCounter();
let { val, todos, addTodo } = useTodo();
</script>
组合方式的好处就是组件可以任意拆分, 数据流清晰
React Hooks
下面我们使用React 来写一个累加器
import React, { useState } from 'react'
function App() {
let [counter, setCounter] = useState(0)
return (
<div>
<h1 onClick={() => setCounter(counter + 1)}>{counter}</h1>
</div>
)
}
export default App
看到这是不是觉得Composition api 和 react hooks 很像
Vue3的改动是 由options -> composition
React17的改动是由 class -> hooks
vue的composition所受的诟病是说它抄袭hooks, 但是各自的底层实现是千差万别的
hooks是有严格的先后顺序的
Composition 后续是靠的响应式通知(reactive)
react是没有响应式的, 就是vdom, 计算diff
vue是响应式(reactive)+vdom
- Hooks
import React, { useState } from 'react'
function App() {
// 这个函数, 每次render都会执行
// hooks是有严格的执行顺序的, 是有顺序限制的
// 不可以写在if里
// 错误写法
if (xx === 1) {
let [counter, setCounter] = useState(0)
}
// 正确写法
let [counter, setCounter] = useState(0)
return (
<div>
<h1 onClick={() => setCounter(counter + 1)}>{counter}</h1>
</div>
)
}
export default App