vue3.x的新特性研究
本文仅作为vue3.x的研究,因为vue3.x现阶段还处于alpha阶段,后续如有之处,欢迎指正
快捷导航
vue3.x的新特性研究
本文仅作为vue3.x的研究,因为vue3.x现阶段还处于"release: v3.0.0-alpha.9"阶段,后续如有之处,欢迎指正
vue2.x面临的问题
vue2.x版本发布于数年前,基于es5的技术架构,受限于当时通用浏览器的版本问题,在某些功能方面做了一些拖鞋:
- 监听数据的方法Object.definePerproty(), 不能对Object类型做深度监听。而为了深度监听,以及为了达到目的所要付出的代价,也就是递归遍历绑定。
- Mixins, 模版中的数据来源不清晰,不知道来自哪里的mixin,并且容易发生明明冲突
- 性能问题,在创建 HOC以及 基于 scoped slots / 作用域插槽封装逻辑的组件 时,需要额外的组件
- 功能分块混乱,我们需要把逻辑分别散落在data,methods对象里,有时候,需要来回滚动。
vue3.x的主要优势
-
更快(引入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都可以做成按需引入。
vue3.x写法变动
- 3.0 版本将原生地支持基于 class 的组件。并提供增强的ts支持
- 改为函数式写法,函数式组件将支持纯函数的书写形式
为了体验这种语法,vue3.x已经发布了Vue Function-based API RFC,vue3.0语法预览版 Function-based API 受 React Hooks 的启发,提供了一个全新的逻辑复用方案。 - 代码区块变更,从按类别划分,到按功能划分
Vue Function-based API RFC
作为 vue-next 项目。也就是 vue3.x 功能的预先体验版而存在
1. setup()
setup(prop,context) 函数,编写组件业务逻辑的主战场,各种 hook 类型的方法均需要在 setup 这个作用域下调用
接收一个props(不可修改)作为参数,在组件实例创建时调用,
可以返回一个值供模版渲染
const MyComponent = {
props: {
name: String
},
setup(props) {
return {
msg: `hello ${props.name}!`
}
},
template: `<div>{{ msg }}</div>`
}
- setup 无法使用 this:
本身的调用时机应该是介于 beforeCreate 和 created 这两个生命周期之间的,也就是说,在这里无法使用 this 调用当前组件实例 - 一定要访问可以用 setup() 第二个参数 context
setup(props, context) { do anything... }
- 内部产生的 proxy 对象:
对于 state 的声明,我们使用 reactive 这个 api 来构造,对于 computed state 的声明,使用 computed,两者本质上均是 Proxy 对象 - 返回值可以是 render context,也可以是模板渲染函数( h(…) ):
render context就是模板中可以访问到的各种数据和方法的上下文对象(受控制的可变数据对象)
2. state 的设定
主要用来代替 2.x 中 data 的功能
包装对象ref()
- 针对 js 的基础类型使用,引用类型亦可
- ref() 对象的本质是一个包有value参数的对象
- 构建 ref() 对象的意义在于,js基础类型传值不是引用传值,所以需要构建一个带有value的对象,以达到追踪依赖。
- Ref Unwrapping 包装对象的自动展开,渲染或嵌套在其他模板中时,ref() 会自动展开, 指向 value 这个原始值,所以不需要用 .value 取值
基础例子
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: `<div @click="appendName">{{ msg }}</div>`}
响应式对象例子
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)
)
}}
附加的 reactive()
- 针对 js 的引用类型使用
import { reactive } from "vue";
export default {
setup() {
const state = reactive({
count: 0
});
function inc() {
state.count++;
}
return { state, inc };
// 或者通过 toRefs 方法
// return { ...toRefs(state), inc };
}
};
两者实现的效果没有差异,都可以实现对数据的绑定,只存在风格上的差异
3. computed
- computed(getFn, setFn), 功能上与 vue2.x 相同
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
- 双向计算值可以通过传给 computed 第二个参数作为 setter 来创建
const count = value(0)
const writableComputed = computed(
// read
() => count.value + 1,
// write
val => { count.value = val - 1}
)
4. watch
watch(getter, callback) (监听)
getter 可以是:
- 一个返回任意值的函数
- 一个包装对象(当然也包括props对象)
- 一个包含上述两种数据源的数组,任一数据变化都会触发回调
监听数组例子
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( ) 在创建的时候会执行一次,类似于 2.x 的 immediate: true
- 如何把immediate关闭?
- 停止监听:
被动 —— 使用在setup( )以及组件生命周期时,在组件销毁的时候也一起被终止
主动 —— watch( )返回一个停止观察的函数,调用即终止 - callback 第三个参数 onCleanup
watch(idValue, (id, oldId, onCleanup) => {
const token = performAsyncOperation(id)
onCleanup(() => {
// id 发生了变化,或是 watcher 即将被停止. // 取消还未完成的异步操作。
token.cancel()
})
})
5. prop
- 功能就是传参,与 2.x 中类似,存在于setup()参数
- prop 自动被 reactive() 包裹,所以可以使用watch监听
结合typescript 使用,相当好用
interface PropTypesI {
count: number
}
export default {
setup(props: PropTypesI) {
watch(() => {
console.log(\`count is: \` + props.count)
})
}
}
6. 生命周期
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
与 react hooks 对比
vue-next 在这方面借鉴了 react hooks 的设计思想,但是从实现层来讲,它们是不一样的,主要有以下几点:
- vue-next 不依赖于其调用顺序,而 react 依赖
- vue-next 提供了生命周期方法,而 react 刻意模糊生命周期的概念
- vue-next 基于响应式系统实现,意味它的依赖不需要显示声明(而且是自动的),而 react 需要手动声明依赖数组
更多推荐
所有评论(0)