VUE2.0 vs VUE3.0
创建
import App from "./App.vue"
// vue2
// import Vue from "vue";
// const app = new Vue(options)
// app.$mount("#app")
// vue3
import { creatApp } from "vue";
const app = createApp(App);
app.mount(#app);
使用插件
// vue2
// Vue.use(xxx);
// vue3
app.use(xxx);
组件
vue2中,this指向当前组件实例,而在vue3中,this指向当前组件实例的代理对象。
用法没有区别。
compositon api 与 option api
vue2中使用option api,在复杂逻辑的组件中,相同功能的代码分散到不同的代码块,而在vue3中使用了composition api,相同的功能代码结合到一起。
同样地,vue3中支持vue2的写法:
<template>
<div>
<button @click="increase">count: {{ count }}</button>
</div>
</template>
<script>
export default {
data() {
return {
count: 0
};
},
methods: {
increase() {
this.count++;
}
}
};
</script>
vue3的写法:
提供一个setup函数,在所有生命周期hook之前调用,函数中的this指向undefined,函数返回的数据将被挂载到组件实例上。
<template>
<div>
<button @click="increase">count: {{ count }}</button>
</div>
</template>
<script>
export default {
setup() {
let count = 0;
const increast = () => {
count++;
}
return {
count,
increase
}
}
};
</script>
此 ref 非彼 $ref
但发现页面并没有变化,这是因为数据没有响应式,vue3提供了ref函数:
<template>
<div>
<button @click="increase">count: {{ count }}</button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
let count = ref(0);
const increast = () => {
count++;
}
return {
count,
increase
}
}
};
</script>
这下没问题了,对...吧?
打印ref创建的count可以发现,count是一个对象,其中有个getter为value,这才是我们的数据,但为什么外部可以使用{{ count }}直接访问数据呢,甚至{{ typeof count }}输出的是Number!
vue为了使用方便(而不是每个数据都.value),帮我们做了这件事当我们访问当前组件对象(代理对象)时,代理发现我们访问的是某个响应式数据,会帮我们去组件对象中取该数据的.value,因此在外部使用无任何影响。
但是在setup()函数中,我们依旧不得不使用.value来修改数据:
<template>
<div>
<button @click="increase">count: {{ count }}</button>
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
let count = ref(0);
const increast = () => {
count.value++;
}
return {
count,
increase
}
}
};
</script>
vue建议用ref定义的响应式数据加上一个Ref做区分
let countRef = ref(0);
在复杂的功能组件中:
<template>
<button @click="decrease">-</button>
<div>{{ countRef }}</div>
<button @click="increase">+</button>
</template>
<script>
import { ref } from 'vue';
function useCount() {
let countRef = ref(0);
const increase = () => {
countRef.value++;
};
const decrease = () => {
countRef.value--;
};
return {
countRef,
increase,
decrease
};
}
// ...
export default {
setup() {
return {
...useCount(),
// ...
};
}
};
</script>
更多地,useCount可以进一步脱离成一个文件导出
在vue3中,根节点可以不唯一
watch 与 watchEffect
在vue3中,提供了watchEffect函数来监控数据变化,只需传入一个函数,vue将会对这个函数进行依赖收集,当收集到的数据变化时,vue会自动调用该函数:
import { ref, watchEffect } from 'vue';
export default function useTodoList() {
const todosRef = ref(getTasks());
watchEffect(() => {
saveTasks(todosRef.value);
});
return {
todosRef,
}
};
当watchEffect传入的函数的依赖数据todosRef变化时,便调用该函数,保存数据。
Hook
在vue3中,生命周期钩子同样由函数提供,可以从vue中导入onMounted, onUnmounted等钩子函数,传入一个函数,到对应生命周期时便会调用执行。
import { onMounted, onUnmounted } from 'vue'
export default function usexxx() {
onMounted(() => {
window.addEventListener('hashchange', onHashChange);
});
onUnmounted(() => {
window.removeEventListener('hashchange', onHashChange);
});
}
computed 与 computed
vue3中的computed依旧是以函数形式提供,有两种参数传递方式。
不管怎样,返回的都是一个(类似)ref包装后的响应式数据,和ref数据同样的使用方法。
传入函数
默认传入的函数为getter
import { computed } from 'vue'
export default function usexxx() {
const filteredTodosRef = computed(() => {
return filter(todosRef.value, visibilityRef.value);
})
const remainingRef = computed(() => {
return filter(todosRef.value, 'active').length;
});
const completedRef = computed(() => {
return filter(todosRef.value, 'completed').length;
});
// ...
}
传入对象
传入对象时,对象中提供getter和setter方法
import { computed } from 'vue'
export default function usexxx() {
const allDone = computed({
get() {
return remainingRef === 0;
},
set(state) {
todosRef.forEach(todo => {
todo.completed = state;
})
}
});
// ...
}
在vue文档中提到以下内容,计算属性的返回值应该被视为只读的,是否意味着计算属性不应该拥有setter。
避免直接修改计算属性值
从计算属性返回的值是派生状态。可以把它看作是一个“临时快照”,每当源状态发生变化时,就会创建一个新的快照。更改快照是没有意义的,因此计算属性的返回值应该被视为只读的,并且永远不应该被更改——应该更新它所依赖的源状态以触发新的计算。