Vue3.x 新特性体验
前言
去年国庆的时候就有了vue3.0的消息,最近3.0beta也发布了,看来vue3.0版本真是不远了。
芜桐也是从官方仓库扒下来了3.0的源码,尝试剖析一下。
仓库地址放在这里,有兴趣就扒下来看看吧。
核心的东西都在pakeages目录下,compiler 是编译相关的东西,runtime 是运行时相关的东西,然后最后有一个 vue的目录,就是整合了这些东西的目录,在pakeage.json下可以看到很多脚本,我们运行一下 npm run dev 就可以在vue目录下生成一个开发环境的dist目录,里边躺着一个vue.global.js的文件,这个就是3.0的vue打包后的文件了,你甚至可以开始拿这个进行一些自己的测试小项目的开发。
体验新特性准备
使用vue-cli初始化一个vue2.x的项目
安装 composition-api
npm i @vue/composition-api -S
# OR
yarn add @vue/composition-api
在入口文件main.js 加入以下代码
import VueCompositionApi from '@vue/composition-api' Vue.use(VueCompositionApi)
可以愉快地玩耍了
1.setup
setup 有着启动的意思。 setup 函数是新版本中提供的新属性。它为我们使用 Composition API 的新特性提供了统一的入口。
setup 函数会在 beforeCreate 之后、created 之前执行。
它可以有两个参数,props和context。props可以拿到父组件传递过来的数据,前提是要先定义接收的props,这和2.x是相同的,setup不再具有this了,所以,以前的许多在this上的属性,现在可以通过context获取
setup(props, context) {
context.attrs
context.slots
context.parent
context.root
context.emit
context.refs
}
2.ref
按照顺序应该先说reactive,但是芜桐感觉把ref写在reactive前面很有必要
vue3.x已经用proxy重写了响应式,所以新的响应式api就是ref和reactive
2.x存在数组更新检测和对象属性更新检测的问题,而且递归的算法十分的消耗内存,3.x已经从根本上解决了之前2.x的响应问题,
ref() 函数用来根据给定的值创建一个响应式的数据对象,ref() 函数调用的返回值是一个对象,这个对象上只包含一个 .value 属性
- 例子
<template> <div> <h1>vue3.0特性</h1> <hr /> <div>foo ref : {{foo}}</div> </div> </template>
import {reactive,toRefs,onMounted,computed,ref,isRef,watch} from "@vue/composition-api"; export default { setup(props, ctx) { let foo = ref("bar"); //返回值 return { foo }; } };
可以看到,在template上使用数据是不需要.value的,因为vue已经处理过了,但是这里还是有一个小坑,只要是在setup函数里,没有出去这个范围,取ref生成的值,必须使用.value,否则你只能拿到一个对象,也许以后vue会对这个地方进行改动(期待~)
3.reactive
reactive 函数,用来创建响应式的数据对象,其实就是创建了一堆的ref。
它接受一个普通的数据对象,然后返回一个响应式的数据对象,我通常把它命名为state。但是这个reactive也有一点小坑,就是模板想要使用的数据,必须在setup中return出去,因为要把很多的属性return出去,包括一些处理方法,计算属性,所以我们通常return state的时候更希望把它用扩展运算符解开,...state,这样就会产生一个问题,解开之后,这些状态就变成了静态的值,就不再是响应式的了,算是个小坑吧,后面的toRefs API 就是用来解决这个问题的。
- 例子
<template> <div> <h1>vue3.0特性</h1> <hr /> <div>name : {{name}}</div> </div> </template>
import {reactive,toRefs,onMounted,computed,ref,isRef,watch} from "@vue/composition-api"; export default { setup(props, ctx) { // 状态 let state = reactive({ name: "wutong" }); //返回值 return { ...toRefs(state) }; } };
4.toRefs
toRefs() 函数可以将 reactive() 创建出来的响应式对象,转换为普通的对象,只不过,这个对象上的每个属性节点,都是 ref() 类型的响应式数据。
在上面介绍reactive的时候已经提到,我们展开state的时候会有一些小问题,导致state解构之后变成了非响应数据,toRefs API 就可以让state中的每个属性重新被ref包一次,变为响应数据,nice~~
例子就看上边的吧。
5.isRef
由于ref的存在,ref把数据包装成了一个对象,而且vue对这个数据进行了处理,某些时候我们实际上需要.value才能拿到真正的数据,所以,isRef() 用来判断某个值是否为 ref() 创建出来的对象;应用场景:当需要展开某个可能为 ref() 创建出来的值的时候,例如:
const bar = isRef(foo) ? foo.value : foo
6.computed
2.x就有的API,3.x重写了。
computed() 用来创建计算属性,computed() 函数的返回值是一个 ref 的实例。
看来3.x的响应式跟这个ref是分不开的,proxy牛逼。
- 创建只读的计算属性
const count = ref(1) // 根据 count 的值,创建一个响应式的计算属性 plusOne // 它会根据依赖的 ref 自动计算并返回一个新的 ref const plusOne = computed(() => count.value + 1)
- 创建可读可写的计算属性
// 创建一个 ref 响应式数据 const count = ref(1) // 创建一个 computed 计算属性 const plusOne = computed({ // 取值函数 get: () => count.value + 1, // 赋值函数 set: val => { count.value = val - 1 } })
7.watch
也是2.x就有了。
watch() 函数用来监视某些数据项的变化,从而触发某些特定的副作用操作。
简单例子
// 定义数据源 const state = reactive({ count: 0 }) // 监视 state.count 这个数据节点的变化 watch( () => state.count,//ref 类型直接写就可以 (count, prevCount) => { /* ... */ } )
第一个参数如果是ref,直接写就可以。也可以指定第三个参数 {lazy:true} 来阻止第一次的监视
监听多个
const state = reactive({ count: 0, name: 'zs' }) watch( [() => state.count, () => state.name], // Object.values(toRefs(state)), ([count, name], [prevCount, prevName]) => { //dosomething }, { lazy: true // 在 watch 被创建的时候,不执行回调函数中的代码 } )
清除监视 在 setup() 函数内创建的 watch 监视,会在当前组件被销毁的时候自动停止。 如果想要明确地停止某个监视,可以调用 watch() 函数的返回值即可。
例子:
// 创建监视,并得到 停止函数
const stop = watch(() => {
/* ... */
})
// 调用停止函数,清除对应的监视
stop()
清除无效异步任务 有时候,当被 watch 监视的值发生变化时,或 watch 本身被 stop 之后,我们期望能够清除那些无效的异步任务,此时,watch 回调函数中提供了一个 cleanup registrator function 来执行清除的工作。这个清除函数会在如下情况下被调用:
watch 被重复执行了
watch 被强制 stop 了 例子:(此示例来自liulongbin)
<input type="text" v-model="keywords" />
// 定义响应式数据 keywords const keywords = ref('') // 异步任务:打印用户输入的关键词 const asyncPrint = val => { // 延时 1 秒后打印 return setTimeout(() => { console.log(val) }, 1000) } // 定义 watch 监听 watch( keywords, (keywords, prevKeywords, onCleanup) => { // 执行异步任务,并得到关闭异步任务的 timerId const timerId = asyncPrint(keywords) // 如果 watch 监听被重复执行了,则会先清除上次未完成的异步任务 onCleanup(() => clearTimeout(timerId)) }, // watch 刚被创建的时候不执行 { lazy: true } ) // 把 template 中需要的数据 return 出去 return { keywords }
8.生命周期
新版的生命周期函数,可以按需导入到组件中,且只能在 setup() 函数中使用。
按需导入有灵魂(hiahiahia~~)
例子:(一看就废~)
import { onMounted, onUpdated, onUnmounted } from '@vue/composition-api'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
9. provide & inject
这难道不就是react中的........
写过react的都不用多bb
provide() 和 inject() 可以实现嵌套组件之间的数据传递。这两个函数只能在 setup() 函数中使用。父级组件中使用 provide() 函数向下传递数据;子级组件中使用 inject() 获取上层传递过来的数据。 例子太过冗长~由于篇幅原因,例子暂时省略,有空看看能补上吧
10.template refs
2.x的时候可以使用refs引用dom元素。 3.x它又回来了~~ 强大的ref~ 例子:(例子摘自liulongbin)
<template>
<div>
<h3 ref="h3Ref">TemplateRefOne</h3>
</div>
</template>
<script>
import { ref, onMounted } from '@vue/composition-api'
export default {
setup() {
// 创建一个 DOM 引用
const h3Ref = ref(null)
// 在 DOM 首次加载完毕之后,才能获取到元素的引用
onMounted(() => {
// 为 dom 元素设置字体颜色
// h3Ref.value 是原生DOM对象
h3Ref.value.style.color = 'red'
})
// 把创建的引用 return 出去
return {
h3Ref
}
}
}
</script>
ref能引用也,页面元素,同样也能引用组件,创建一个组件的引用,甚至可以使用组件的方法,非常的amazing 啊,例子就不搞了,自己去实验一哈~
11.createComponent
提供了类型推断,方便在结合 TypeScript 书写代码 以上就是本文所有内容了,感谢观看,vue3.0有可能存在重大更新,请留意更新时间和本文发布时间.