setup
setup() 函数是 vue3 中,专门为组件提供的新属性。它为我们使用 vue3 的 Composition API 新特性提供了统一的入口。
- 新的option, 所有的组合API函数都在此使用, 只在初始化时执行一次
- 函数如果返回对象, 对象中的属性或方法, 模板中可以直接使用
- 1.执行时机
setup 函数会在 beforeCreate 之后、created 之前执行
- 接收 props 数据
setup(props) {
console.log(props.p1)
}
- 3.context
setup 函数的第二个形参是一个上下文对象,这个上下文对象中包含了一些有用的属性,这些属性在 vue 2.x 中需要通过 this 才能访问到,在 vue 3.x 中,它们的访问方式如下:
const MyComponent = {
setup(props, context) {
context.attrs
context.slots
context.parent
context.root
context.emit
context.refs
}
注意:在 setup() 函数中无法访问到 this
ref
- 1.基础用法
ref() 函数用来根据给定的值创建一个响应式的数据对象,ref() 函数调用的返回值是一个对象,这个对象上只包含一个 .value 属性:
import { ref } from "vue";
export default {
name: "App",
setup() {
// 创建响应式数据对象 count,初始值为 0
const count = ref(0);
// 如果要访问 ref() 创建出来的响应式数据对象的值,必须通过 .value 属性才可以
console.log(count.value); // 输出 0
// 让 count 的值 +1
count.value++;
// 再次打印 count 的值
console.log(count.value); // 输出 1
}
};
- 2.在 template 中访问 ref 创建的响应式数据
<template>
<div>
{{ count }}
</div>
<div>
<button @click="chang">按钮+1</button>
</div>
</template>
<script lang="ts">
import { ref } from "vue";
export default {
name: "App",
setup() {
// 创建响应式数据对象 count,初始值为 0
const count = ref(0);
// 如果要访问 ref() 创建出来的响应式数据对象的值,必须通过 .value 属性才可以
console.log(count.value); // 输出 0
function chang () {
count.value++;
console.log( count.value++ )
}
return {
count,
chang
}
}
};
</script>
reactive
reactive() 函数接收一个普通对象,返回一个响应式的数据对象。
<template>
<div>人生依然很美好</div>
<h2>name:{{ user.name }}</h2>
<h2>age:{{ user.age }}</h2>
<h2>wifeName:{{ user.wife.name }}</h2>
<h2>wifeAge:{{ user.wife.age }}</h2>
<h2>wife的车:
<div v-for="item in user.wife.cars" :key="item.index" >{{item}}</div>
</h2>
<button @click="updateUser">changs</button>
</template>
<script lang="ts">
import { defineComponent, reactive } from "vue";
export default defineComponent({
name: "App",
setup () {
const user = reactive({
name: "小米",
age: 22,
password: 123456,
wife: {
name: "小甜",
age: 18,
cars: ["奔驰", "宝马", "桑塔纳"],
},
})
console.log(user)
// 点击事件
const updateUser = () => {
user.name += '--'
user.age += 1
user.wife.name += '++'
user.wife.age += 2
user.wife.cars[0] = '活路没有了'
}
return {
user,
updateUser
}
}
})
</script>
image.png
- 在 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,示例代码如下:
const c1 = ref(0)
const state = reactive({
c1
})
// 再次创建 ref,命名为 c2
const c2 = ref(9)
// 将 旧 ref c1 替换为 新 ref c2
state.c1 = c2
state.c1++
console.log(state.c1) // 输出 10
console.log(c2.value) // 输出 10
console.log(c1.value) // 输出 0
computed
computed()
用来创建计算属性,computed()
函数的返回值是一个 ref
的实例。使用 computed
之前需要按需导入:
import { computed } from 'vue'
- 1 创建只读的计算属性
在调用 computed() 函数期间,传入一个 function 函数,可以得到一个只读的计算属性,示例代码如下:
<template>
<div>
<p>{{ refCount }}</p>
<p>{{ plusOne }}</p>
<button @click="refCount+=1">加1</button>
</div>
</template>
<script lang="ts">
import { defineComponent, computed, ref } from 'vue'
export default defineComponent({
name:'App',
setup () {
const refCount = ref(0)
const plusOne = computed( () => refCount.value + 1 )
return {
refCount,
plusOne
}
}
})
</script>
- 2 创建可读可写的计算属性
在调用 computed() 函数期间,传入一个包含 get 和 set 函数的对象,可以得到一个可读可写的计算属性,示例代码如下:
<template>
<div>
<p>{{ refCount }}</p>
<p>{{ plusOne }}</p>
<button @click="refCount += 1">加1</button>
</div>
</template>
<script lang="ts">
import { defineComponent, computed, ref } from "vue";
export default defineComponent({
name: "App",
setup() {
const refCount = ref(0);
const plusOne = computed({
// 取值函数
get: () => refCount.value + 1,
// 赋值函数
set: (val) => {
refCount.value = val - 1;
},
});
// 为计算属性赋值的操作,会触发 set 函数
plusOne.value = 9;
// 触发 set 函数后,count 的值会被更新
console.log(refCount.value); // 输出 8
return {
refCount,
plusOne,
};
},
});
</script>
watch
watch() 函数用来监视某些数据项的变化,从而触发某些特定的操作,使用之前需要按需导入:
import { watch } from 'vue'
- 1.基本用法
const count = ref(0)
// 定义 watch,只要 count 值变化,就会触发 watch 回调
// watch 会在创建时会自动调用一次
watch(() => console.log(count.value))
// 输出 0
setTimeout(() => {
count.value++
// 输出 1
}, 1000)
2 监视指定的数据源
监视reactive
类型的数据源:
// 定义数据源
const state = reactive({ count: 0 })
// 监视 state.count 这个数据节点的变化
watch(
() => state.count,
(count, prevCount) => {
/* ... */
}
)
监视 ref
类型的数据源:
// 定义数据源
const count = ref(0)
// 指定要监视的数据源
watch(count, (count, prevCount) => {
/* ... */
})
- 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 = 'ls'
}, 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 = 'xiaomaolv'
}, 1000)
- 清除监视
在 setup() 函数内创建的 watch 监视,会在当前组件被销毁的时候自动停止。如果想要明确地停止某个监视,可以调用 watch() 函数的返回值即可,语法如下:
- 清除监视
// 创建监视,并得到 停止函数
const stop = watch(() => {
// >_<
})
// 调用停止函数,清除对应的监视
stop()
- 5 在 watch 中清除无效的异步任务
有时候,当被 watch 监视的值发生变化时,或 watch 本身被 stop 之后,我们期望能够清除那些无效的异步任务,此时,watch 回调函数中提供了一个cleanup registrator function
来执行清除的工作。这个清除函数会在如下情况下被调用: - watch 被重复执行了
- watch 被强制 stop 了
- Template 中的代码示例如下:
/* 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, onUnmounted } from '@vue/composition-api'
const MyComponent = {
setup() {
onMounted(() => {
console.log('mounted!')
})
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
}
}
下面的列表,是 vue 2.x 的生命周期函数与新版 Composition API 之间的映射关系:
beforeCreate -> use setup()
created -> use setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
errorCaptured -> onErrorCaptured
provide & inject
provide()
和 inject()
可以实现嵌套组件之间的数据传递。这两个函数只能在setup()
函数中使用。父级组件中使用 provide()
函数向下传递数据;子级组件中使用inject()
获取上层传递过来的数据。
父组件
<template>
<h1>父组件</h1>
<p>当前颜色: {{color}}</p>
<button @click="color='red'">红</button>
<button @click="color='yellow'">黄</button>
<button @click="color='blue'">蓝</button>
<hr>
<Son />
</template>
<script lang="ts">
import { provide, ref } from 'vue'
import Son from './Son.vue'
export default {
name: 'ProvideInject',
components: {
Son
},
setup() {
const color = ref('red')
provide('color', color)
return {
color
}
}
}
</script>
子组件
<template>
<div>
<h2>子组件</h2>
<hr>
<GrandSon />
</div>
</template>
<script lang="ts">
import GrandSon from './GrandSon.vue'
export default {
components: {
GrandSon
},
}
</script>
孙子组件
<template>
<h3 :style="{color}">孙子组件: {{color}}</h3>
</template>
<script lang="ts">
import { inject } from 'vue'
export default {
setup() {
const color = inject('color')
return {
color
}
}
}
</script>
ref在元素组件中的引用方式
通过 ref() 还可以引用页面上的元素或组件。
<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>
网友评论