原文:一文学会Vue3新特性
公众号:前端极客技术
Vue3.0 周边生态现在已经完善得差不多了,新项目可以开始使用Vue3来开发了,今天我们先来学习Vue3的一些新特性。
Composition API翻译成中文就是组合式API。有的人可能会疑惑为什么要用Composition API?原来Vue2中的options API不是也能实现吗?
我们先来Vue3官方文档中的例子:
假设我们的应用中有一个显示某个用户的仓库列表的视图。此外,我们还希望有搜索和筛选功能。实现此视图组件的代码可能如下所示:
// src/components/UserRepositories.vue
export default {
components: { RepositoriesFilters, RepositoriesSortBy, RepositoriesList },
props: {
user: {
type: String,
required: true
}
},
data () {
return {
repositories: [], // 1
filters: { ... }, // 3
searchQuery: '' // 2
}
},
computed: {
filteredRepositories () { ... }, // 3
repositoriesMatchingSearchQuery () { ... }, // 2
},
watch: {
user: 'getUserRepositories' // 1
},
methods: {
getUserRepositories () {
// 使用 `this.user` 获取用户仓库
}, // 1
updateFilters () { ... }, // 3
},
mounted () {
this.getUserRepositories() // 1
}
}
该组件有以下几个职责:
使用 (data、computed、methods、watch) 组件选项来组织逻辑通常都很有效。然而,当我们的组件开始变得更大时,逻辑关注点的列表也会增长。尤其对于那些一开始没有编写这些组件的人来说,这会导致组件难以阅读和理解。
这是一个大型组件的示例,其中逻辑关注点按颜色进行分组。
这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。
如果能够将同一个逻辑关注点相关代码收集在一起会更好。而这正是组合式 API 使我们能够做到的。
Vue3
兼容大部分Vue2
语法,所以在Vue3
中写Vue2语法是没问题的(废除的除外)。
setup是Vue3新增的一个选项,是使用Composition API的入口。
setup
只在初始化时执行一次,所有的 Composition API 函数都在这里使用。
setup
是在生命周期beforeCreate
之前执行。我们通过下面的代码来验证一下:
beforeCreate() {
console.log('---------beforeCreate--------');
},
setup() {
console.log('---------setup--------');
return {};
},
// ---------setup--------
// ---------beforeCreate--------
由上面的执行结果我们可以推断出,setup执行时,组件对象还没有创建,此时this
是undefined,因此在setup函数中不能通过this
对象访问data/computed/methods/props
。
setup有两个可选参数:
this
对象,所以context中就提供了this中最常用的三个属性:attrs
、slot
和emit
。import { toRef } from 'vue'
setup(props) {
const title = toRef(props, 'title')
console.log(title.value)
}
由于props是响应式的,所以不能使用 ES6 解构,解构会消除prop的响应性。如果需要解构prop,可以在setup函数中使用
toRefs
函数来完成此操作。
export default {
setup(props, context) {
// Attribute (非响应式对象)
console.log(context.attrs)
// 插槽 (非响应式对象)
console.log(context.slots)
// 触发事件 (方法)
console.log(context.emit)
}
}
reactive
函数接受一个普通对象,返回一个响应式数据对象。
const data = reactive({
count: 0,
result: computed(() => data.count + 1)
})
ref:接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象具有指向内部值的单个 property .value。
isRef:检查值是否为一个 ref 对象。
export default {
setup() {
const count = ref(0)
console.log('count is ref:', isRef(count))
console.log('count: ', count)
console.log('count.value:', count.value)
return {
count
}
}
}
将响应式对象转换为普通对象,其中结果对象的每个 property 都是指向原始对象相应 property 的 ref
。
我们通过reactive
创建的对象,如果在模板中使用,就必须以xxxx.xxx
的形式,但如果用到的地方比较多就比较麻烦。如果用ES6解构,就会失去响应式。
如果我们利用toRefs可以将一个响应式 reactive 对象的所有原始属性转换为响应式的ref属性。
前面提到的setup
函数的props
要解构可以使用toRefs
。
示例代码如下:
<template>
<div class="hello">
<p>number: {{number}}</p>
</div>
</template>
<script>
import { toRefs, reactive } from 'vue'
export default {
setup() {
const state = reactive({
number: 0,
date: '20210628'
})
const state2 = toRefs(state)
setInterval(() => {
state.number++
}, 1000)
return {
...state2
}
}
}
</script>
与Vue2中的 computed 配置功能一致,返回的是一个 ref 类型的对象。
setup() {
const state = reactive({
count: 0,
plusOne: computed(() => state.count + 1)
})
return {
...toRefs(state)
}
}
监视指定的一个或多个响应式数据,一旦数据变化,就自动执行监视回调。
import { watch, ref, reactive } from 'vue'
export default {
setup() {
const name = ref('前端极客技术')
const otherName = reactive({
firstName: '前端',
lastName: '技术'
})
watch(name, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
watch(
() => {
return otherName.firstName + otherName.lastName
},
value => {
console.log(value)
}
)
setTimeout(() => {
name.value = '前端极客'
otherName.firstName = '前端极客'
}, 3000)
}
}
为了根据响应式状态自动应用和重新应用副作用,我们可以使用 watchEffect 方法。它立即执行传入的一个函数,同时响应式追踪其依赖,并在其依赖变更时重新运行该函数。
const count = ref(0)
watchEffect(() => console.log(count.value))
// -> logs 0
setTimeout(() => {
count.value++
// -> logs 1
}, 100)
通过下面的图来看下Vue2和Vue3生命周期钩子的对比:
setup() {
onBeforeMount(() => {
console.log('--onBeforeMount')
})
onMounted(() => {
console.log('--onMounted')
})
onBeforeUpdate(() => {
console.log('--onBeforeUpdate')
})
onUpdated(() => {
console.log('--onUpdated')
})
onBeforeUnmount(() => {
console.log('--onBeforeUnmount')
})
onUnmounted(() => {
console.log('--onUnmounted')
})
}
getCurrentInstance 支持访问内部组件实例,用于高阶用法或库的开发。
import { getCurrentInstance } from 'vue'
const MyComponent = {
setup() {
const internalInstance = getCurrentInstance()
internalInstance.appContext.config.globalProperties // 访问 globalProperties
}
}
getCurrentInstance 只能在 setup 或生命周期钩子中调用。
如需在 setup 或生命周期钩子外使用,请先在 setup 中调用 getCurrentInstance() 获取该实例然后再使用。
Teleport是Vue3中新增的特性,翻译成中文就是传送的意思。Teleport
提供了一种简洁的方式,让组件的html
在父组件界面外的特定标签下插入显示。也就是我们可以把 子组件 或者 dom节点 插入到任何你想插入到的地方去。
语法:
使用
to
属性,指定要插入的节点。其值为选择器
我们使用Teleport实现一个模态窗口:
这是一个模态窗口!
我的父元素是"body"!
Suspense 是一个试验性的新特性并且其 API 可能随时更改。它不应该被用在生产环境。
在正确渲染组件之前进行一些异步请求是很常见的事。组件通常会在本地处理这种逻辑,绝大多数情况下这是非常完美的做法。
该
组件提供了另一个方案,允许等待整个组件树处理完毕而不是单个组件。
这个
组件有两个插槽。它们都只接收一个子节点。default 插槽里的节点会尽可能展示出来。如果不能,则展示 fallback 插槽里的节点。
常见的一个用例:用到了异步组件
// 父组件
Loading...
// 子组件
AsyncDemo
{{msg}}
在Vue2中,template
中只允许有一个根节点,但是在Vue3中,可以直接写多个根节点。
<template>
<div>div>
<div>div>
<div>div>
template>
$scopedSlots
属性被移除,都用$slots代替本文只是介绍了Vue3中的一些常用的新特性,更多具体内容需要大家详细阅读Vue3的官方文档:
https://v3.cn.vuejs.org/
如果你觉得这篇内容对你有帮助的话:
点赞支持下吧,让更多的人也能看到这篇内容
关注公众号:前端极客技术,我们一起学习一起进步。