Vue3新增了很多新的特性,vue3将会对vue2进行向下兼容,因此不必担心因为使用vue3后vue2的特性不能够使用。
setup()
函数是vue3中,专门为组件提供的新属性。它为我们使用Vue3的composition API
新特性提供了统一的入口。props
中定义当前组件允许外界传递过来的参数名称:props{p1:String}
setup
函数的第一个形参,接收props数据:setup(props){
console.log(props.p1)}
1.3. context
setup
函数的第二个形参是一个上下文对象,这个上下文对象中包含了一些有用的属性,这些属性在vue 2.x
中需要通过this才能访问到,在vue 3.x
中,它们的访问方式如下:
const MyComponent = {
setup(props,context){
context.attrs
context.slots
context.parent
context.root
context.refs
}
}
注意:在setup()
函数中无法访问到this !!!!!!!!!!!!
reactive()
函数接受一个普通对象,返回一个响应式的数据对象。
2.1基本用法
等价于vue 2.x
中的Vue.observable()
函数,vue 3.x
中提供了reactive()函数
,用来创建响应式的数据对象,基本代码示例如下:
import{ reactive } from '@vue/composition-api'
//创建响应式数据对象,得到的state类似于vue 2.x中data()返回的响应式对象
const state=reactive({count : 0})
2.2定义响应式数据供template使用
按需导入reactive
函数:
import{ reactive } from '@vue/composition-api'
在setup()
函数中调用reactive()
函数,创建响应式数据对象:
setup(){
//创建响应式数据对象
const state = reactive({count :0})
//setup 函数中将响应式数据对象 return 出去,供template使用
return state
}
在template 中访问响应式数据:
<p> 当前的 count 值为:{{count}}p>
3.1基本语法
ref()
函数用来根据给定的值创建一个响应式的数据对象,ref()
函数调用的返回值是一个对象,这个对象只包含一个.value
属性:
import { ref } from '@vue/composition-api'
//创建响应式数据对象count,初始值为0
const count =ref(0)
//如果要访问ref()创建出来的响应式数据对象的值, 必须通过.value属性才可以
console.log(count.value)//输出0
//让count 的值+1
count.value++
3.2在template中访问ref创建的响应式数据
在setup()
中创建响应式数据:
import { ref } from '@vue/composition-api'
setup() {
const count = ref(0)
return {
count,
name: ref('zs')
}
}
在template 中访问响应式数据:
<template>
<p>{{count}} --- {{name}}p>
template>
3.3在reactive 对象中访问ref创建的响应式数据
当把ref()
创建出来的响应式数据对象,挂载到 reactive()
上时,会自动把响应式数据对象展开为原始的值,不需要通过.value
就可以直接被访问,例如:
const count = ref(0)
const state = reactive({
count
})
console.log(state.count) //输出0
state.count++ //此处不需要通过 .value 就能直接访问原始值
console.log(count) //输出1
注意:新的ref会覆盖旧的ref,示例代码如下:
//创建ref并挂载到 reactive 中
const c1 = ref(0)
const state = reactive({
c1
})
//再次创建 ref, 命名为 c2
const c2 = ref(9)
state.c1 = c2
state.c1++
console.log(state.c1) //输出10
console.log(c2.value) //输出10
console.log(c1.value) //输出0
isRef()
用来判断某个值是否为 ref()
创建出来的对象;应用场景:当需要展开某个可能为 ref()
创建出来的值的时候,例如:
import{ isRef } from '@vue/compositon-api'
const unwrapped = isRef(foo) ? foo.value : foo
toRefs()
函数可以将reactive()
转换成普通的对象,只不过,这个对象上的每个属性节点,都是ref()
类型的响应式数据,最常见的应用场景如下:
import {toRefs} from 'vue'
//定义响应式数据对象
const state =reactive({
count :0
})
//定义页面上可用的事件处理函数
const increment = () =>{
state.count++
}
//在 setup 中返回一个对象供页面使用
//这个对象中可以包含响应式的数据,也可以包含事件处理函数
return {
//将state 上的每个属性,都转化为 ref 形式的响应式数据
//对象中的扩展运算符(...)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中
...toRefs(state),
//自增的事件处理函数
increment
}
页面上可以直接访问setup()
中return出来的响应式数据:
<template>
<div class="hello">
<p>当前的count值为:{{count}}p>
<button @click="increment">+button>
div>
template>
computed()
用来创建计算属性,computed()
函数的返回值是一个ref的实例。使用computed
之前需要按需导入:
import { computed } from '@vue/composition-api'
6.1创建只读计算属性
在调用 computed()函数期间,传入一个function 函数,可以得到一个只读的计算属性,示例代码如下:
//创建一个ref响应式数据
const count =ref(1)
//根据 count 的值,创建一个响应式的计算属性plusOne
//它会根据依赖的ref自动计算并返回一个新的ref
const plusOne = computed(() => count.value+1)
console.log(plusOne.value) //输出2
plusOne.value++ //error
6.2 创建可读可写的计算属性
在调用 computed()
函数期间,传入一个包含 get
和 set
函数的对象,可以得到一个可读可写的计算属性,示例代码如下:
//创建一个ref响应式数据
const count = ref(1)
//创建一个computed 计算属性
/const plusOne = computed({
//取值函数
get:() => count.value+1
//赋值函数
set: val=>{
count.value = val -1
}
})
//为计算属性赋值的操作,会触发set 函数
plusOne.value = 9
//触发 set 函数后,count 的值会被更新
console.log(count.value) //输出8
watch()
函数用来监视某些数据项的变化,从而触发某些特定的操作,使用之前需要按需导入:
一个简单的表格是这么创建的:
import { watch } from 'vue'
7.1基本用法
const count =ref(0)
//定义watch,只要count值变化,就会触发watch回调
//watch 会在创建时会自动调用一次
watch(() => console.log(count.value))
seTimeout(()=>{
count.value++
//输出1
},1000)
7.2监视指定的数据源
监视reactive
类型的数据源:
//定义数据源
const state = reactive({count : 0})
//监视state.count 这个数据节点的变化
watch(
()=>state.count,
(count,prevCount)=>{
/*...*/
}
)
监视 ref
类型的数据源:
//定义数据源
const count = ref(0)
//指定要监视的数据源
watch(count,(count,prevCount)=>{
/*...*/
})
**7.3监视多个数据源
监视reactive
类型的数据源:
const state = reactive({count :0,name:'zs'})
watch(
[()=>state.count, () => state.name], //Object.values(toRefs(state))
([count,name],[prevCount,prevName]) =>{
console.log(count) //新的 count 值
console.log(name) //新的name 值
console.log('-------------')
console.log(prevCount) //旧的 count 值
console.log(prevName) //旧的 name 值
},
{
lazy:true //在watch 被创建的时候,不执行回调函数中的代码
}
)
setTimeout(()=>{
state.count++
state.name='qs'
},1000)
监视 ref
类型的数据源:
const count = ref(0)
const name =ref('zs')
watch(
[count,name], //需要被监视的多个ref数据源
([count,name],[prevCount,prevName])=>{
console.log(count)
console.log(name)
console.log('------------')
console.log(prevCount)
console.log(prevName)
},
{
lazy:true
}
)
setTimeout(()=>{
count.value++
name.value='xxxxs'
},1000)
7.4清除监视
在setup()
函数内创建watch
监视,会在当前组件被销毁的时候自动停止,如果想要明确地停止某个监视,可以调用watch()
函数的返回值即可,语法如下:
//创建监视,并得到 停止函数
const stop =watch(()=>{
/*....*/
})
//调用停止函数,清除对应的监视
stop()
7.5 在watch中清除无效的异步任务
有时候,当被watch
监视的值发生变化时,或watch
本身stop
之后,我们期望能够清除那些无效的异步任务,此时,watch
回调函数中提供了一个cleanup
registrator
function
来执行清除的工作。这个清除函数会在如下情况下被调用:
stop
了/*template中的代码*/ <input type = 'text' v-model="keywords">
Script 中的代码示例如下:
//定义响应式数据 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
}
新版的生命周期函数,可以按需导入到组件中,且只能在setup()
函数中使用,代码示例如下:
import { onMounted, onUpdated, onUpdated,onUnmounted } from '@vue/composition-api'
const MyComponent = {
setup() {
onMounted(() =>{
console.log('mounted!')
})
onUpdated(()=>{
console.log('updated!')
})
onUnmounted( ()=>{
console.log('unmounted!')
})
}
}
下面的列表,是vue2.x的生命周期函数与新版Composition API之间的映射关系:
beforeCreate
setup()
created
setup()
beforeMount
-> onBeforeMount
mounted
-> onMounted
beforeUpdate
-> onBeforeUpdate
updated
-> onUpdated
beforeDestroy
-> onBeforeUnmount
destroyed
-> onUnmounted
errorCaptured
-> onErrorCaptured
provide()
和 inject()
可以实现嵌套组件之间的数据传递。这两个函数只能在setup()
函数中使用。父级组件中使用provide()
函数向下传递数据;子级组件中使用inject()
获取上层传递过来的数据。
9.1共享普通数据
App.vue根组件:
<template>
<div id="app">
<h1>App 根组件h1>
<hr />
<levelOne />
div>
template>
<script>
import LevelOne from './components/LevelOne'
//1.按需导入provide
import { provide } from '@vue/composition-api'
export default {
name: 'app',
setup(){
//2.App 根组件作为父级组件,通过Provide函数向子级组件共享数据(不限层级)
//provide('要共享数据名称','被共享的数据')
provide('globalColor','red')
},
components:{
LevelOne
}
}
script>
LevelOne.vue
组件:
<template>
<div>
<h3 :style="{color: themeColor}">Level Oneh3>
<hr />
<LevelTwo />
div>
template>
<script>
import LevelTwo from './LevelTwo'
//1. 按需导入inject
import { inject } from '@vue/composition-api'
export default {
setup() {
//2. 调用inject 函数时,通过指定的数据名称,获取到父级共享的数据
const themeColor = inject('globalColor')
// 3.把接收到的共享数据 return 给 Template 使用
return {
themeColor
}
},
compoents:{
LevelTwo
}
}
script>
LevelTwo.vue
组件:
<template>
<div>
<h5 :style="{color: themeColor}">Level Twoh5>
div>
template>
<script>
// 1.按需导入inject
import { inject } from '@vue/composition-api'
export default {
setup() {
//2.调用 inject 函数时,通过指定的数据名称,获取到父级共享的数据
const themeColor = inject('globalColor')
//3. 把接收到的共享数据 return 给 Template使用
return{
themeColor
}
}
}
script>
9.2共享ref响应式数据
如下代码实现了点按钮切换主题颜色的功能,主要修改了 App.vue
组件中的代码,LevelOne.vue
和LevelTwo.vue
中的代码不受任何改变:
<template>
<div id="app">
<h1>App 根组件h1>
<button @click='themeColor="red"'>红色button>
<button @click='themeColor="blue"'>蓝色button>
<button @click='themeColor="orange"'>橘黄色button>
<hr />
<LevelOne />
div>
template>
<script>
import LeveOne from './components/LevelOne'
import { provide, ref } '@vue/composition-api'
export default {
name :"app",
setup(){
//定义ref响应式数据
const themeColor = ref('red')
//把ref数据通过provide提供给子组件使用
provide('globalColor',themeColor)
//setup中return 数据供当前组件的Template使用
return {
themeColor
}
},
components:{
LevelOne
}
}
script>
通过ref()
还可以引用页面上的元素或组件。
<template>
<div>
<h3 ref="h3Ref">TemplateRefOneh3>
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>
10.2组件的引用
TemplateRefOne.vue
中的示例代码如下:
<template>
<div>
<h3>TemplateRefOneh3>
<button @click="showNumber">获取TemplateRefTwo中的count值button>
<hr />
<TemplateRefTwo ref="comRef" />
div>
template>
<script>
import { ref } from '@vue/composition-api'
import TemplateRefTwo from './TemplateRefTwo'
export default{
setup(){
//1.创建一个组件的ref引用
const comRef = ref(null)
//5.展示子组件中count 的值
const showNumber =() =>{
console.log(comRef.value.count)
}
//2.把创建的引用return 出去
return {
comRef,
showNumber
}
},
components:{
TemplateRefTwo
}
}
script>
TemplateRefTwo.vue
中的示例代码:
<template>
<div>
<h5>TemplateRefTwo --- {{count}}h5>
<button @click="count+1">+1button>
div>
template>
<script>
import {ref} from '@vue/composition-api'
export default {
setup(){
//1.定义响应式的数据
const count = ref(0)
//2.把响应式数据return 给 Template使用
return {
count
}
}
}
script>
这个函数不是必须的,除非你想要完美结合TypeScript提供的类型推断来进行项目的开发。
这个函数仅仅提供了类型推断,方便在结合TypeScript书写代码时,能为setup()
中的props
提供完整的类型推断。
import { createComponent } from 'vue'
export default createComponent({
props:{
foo:String
},
setup(props){
props.foo // <- type:string
}
})