本文仅作为vue3.x的研究,因为vue3.x现阶段还处于"release: v3.0.0-alpha.9"阶段,后续如有之处,欢迎指正
vue2.x版本发布于数年前,基于es5的技术架构,受限于当时通用浏览器的版本问题,在某些功能方面做了一些拖鞋:
更快(引入Proxy对象)
vue3重新审视了 vdom,更改了自身对于 vdom的对比算法。vdom从之前的每次更新,都进行一次完整遍历对比,改为了切分区块树,来进行动态内容更新。也就是只更新 vdom的绑定了动态数据的部分,把速度提高了6倍;
并且vue3.x把 definePerproty改为了 proxy,对于 JavaScript引擎更加友好,响应更加高效。
更小 (按需引入)
之前 vue的代码,只有一个 vue对象进来,所有的东西都在 vue上,这样的话其实所有你没用到的东西也没有办法扔掉,因为它们全都已经被添加到 vue这个全局对象上。
vue3.x的话,一些不是每个应用都需要的功能,我们就做成了按需引入。用 ES module imports按需引入,举例来说,内置组件像 keep-alive、transition,指令的配合的运行时比如 v-model、v-for、帮助函数,各种工具函数。比如 async component、使用 mixins、或者是 memoize都可以做成按需引入。
作为 vue-next 项目。也就是 vue3.x 功能的预先体验版而存在
setup(prop,context) 函数,编写组件业务逻辑的主战场,各种 hook 类型的方法均需要在 setup 这个作用域下调用
接收一个props(不可修改)作为参数,在组件实例创建时调用,
可以返回一个值供模版渲染
const MyComponent = {
props: {
name: String
},
setup(props) {
return {
msg: `hello ${props.name}!`
}
},
template: `{{ msg }}`
}
setup(props, context) {
do anything...
}
主要用来代替 2.x 中 data 的功能
基础例子
import { ref } from "vue";
export default {
setup() {
const count = ref(0);
function inc() {
count.value++;
}
return { count, inc };
}
};
包装对象的自动展开例子
import { ref } from 'vue'
const MyComponent = {
setup(props) {
const msg = ref('hello')
const appendName = () => {
msg.value = `hello ${props.name}`
}
return {
msg,
appendName
}
},
template: `{{ msg }}`}
响应式对象例子
onst count = ref(0)
const obj = reactive({
count
})
console.log(obj.count) // 0
obj.count++
console.log(obj.count) // 1
console.log(count.value) // 1
count.value++
console.log(obj.count) // 2
console.log(count.value) // 2
配合手写render函数
响应式对象例子
import { ref, createElement as h } from 'vue'
const MyComponent = {
setup(initialProps) {
const count = ref(0)
const increment = () => { count.value++ }
return (props, slots, attrs, vnode) => (
h('button', {
onClick: increment
}, count.value)
)
}}
import { reactive } from "vue";
export default {
setup() {
const state = reactive({
count: 0
});
function inc() {
state.count++;
}
return { state, inc };
// 或者通过 toRefs 方法
// return { ...toRefs(state), inc };
}
};
两者实现的效果没有差异,都可以实现对数据的绑定,只存在风格上的差异
import { ref, computed } from 'vue'
const count = ref(0)
const countPlusOne = computed(() => count.value + 1)
console.log(countPlusOne.value) // 1
count.value++
console.log(countPlusOne.value) // 2
const count = value(0)
const writableComputed = computed(
// read
() => count.value + 1,
// write
val => { count.value = val - 1}
)
watch(getter, callback) (监听)
getter 可以是:
监听数组例子
watch(
// getter
() => count.value + 1,
// callback
(value, oldValue) => {
console.log('count + 1 is: ', value)
}) // -> count + 1 is: 1
count.value++// -> count + 1 is: 2
watch(idValue, (id, oldId, onCleanup) => {
const token = performAsyncOperation(id)
onCleanup(() => {
// id 发生了变化,或是 watcher 即将被停止. // 取消还未完成的异步操作。
token.cancel()
})
})
结合typescript 使用,相当好用
interface PropTypesI {
count: number
}
export default {
setup(props: PropTypesI) {
watch(() => {
console.log(\`count is: \` + props.count)
})
}
}
import { onMounted, onUpdated, onUnmounted } from "vue";
setup() {
onMounted(() => { ... });
onUpdated(() => { ... });
onUnmounted(() => { ... });
}
这里做一个与2.X简单的类比
beforeCreate -> 使用 setup()
created -> 使用 setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured
vue-next 在这方面借鉴了 react hooks 的设计思想,但是从实现层来讲,它们是不一样的,主要有以下几点: