setup 细节
- setup 执行的时机
- 在 beforeCreate 之前执行(一次), 此时组件对象还没有创建
- this 是 undefined, 不能通过 this 来访问 data/computed/methods / props
- 其实所有的 composition API 相关回调函数中也都不可以
- setup 的返回值
- 一般都返回一个对象: 为模板提供数据, 也就是模板中可以直接使用此对象中的所有属性/方法
- 返回对象中的属性会与 data 函数返回对象的属性合并成为组件对象的属性
- 返回对象中的方法会与 methods 中的方法合并成功组件对象的方法
- 如果有重名, setup 优先
- 注意:
- 一般不要混合使用: methods 中可以访问 setup 提供的属性和方法, 但在 setup 方法中不能访问 data 和 methods
- setup 不能是一个 async 函数: 因为返回值不再是 return 的对象, 而是 promise, 模板看不到 return 对象中的属性数据
- setup 的参数
- setup(props, context) / setup(props, {attrs, slots, emit})
- props: 包含 props 配置声明且传入了的所有属性的对象
- attrs: 包含没有在 props 配置中声明的属性的对象, 相当于 this.$attrs
- slots: 包含所有传入的插槽内容的对象, 相当于 this.$slots
- emit: 用来分发自定义事件的函数, 相当于 this.$emit
<template>
<h2>App</h2>
<p>msg: {{ msg }}</p>
<button @click="fn('--')">更新</button>
<child :msg="msg" msg2="cba" @fn="fn" />
</template>
<script lang="ts">
import { reactive, ref } from 'vue'
import child from './child.vue'
export default {
components: {
child
},
setup() {
const msg = ref('abc')
function fn(content: string) {
msg.value += content
}
return {
msg,
fn
}
}
}
</script>
<template>
<div>
<h3>{{ n }}</h3>
<h3>{{ m }}</h3>
<h3>msg: {{ msg }}</h3>
<h3>msg2: {{ $attrs.msg2 }}</h3>
<slot name="xxx"></slot>
<button @click="update">更新</button>
</div>
</template>
<script lang="ts">
import { ref, defineComponent } from 'vue'
export default defineComponent({
name: 'child',
props: ['msg'],
emits: ['fn'], // 可选的, 声明了更利于程序员阅读, 且可以对分发的事件数据进行校验
data() {
console.log('data', this)
return {
// n: 1
}
},
beforeCreate() {
console.log('beforeCreate', this)
},
methods: {
// update () {
// this.n++
// this.m++
// }
},
// setup (props, context) {
setup(props, { attrs, emit, slots }) {
console.log('setup', this)
console.log(props.msg, attrs.msg2, slots, emit)
const m = ref(2)
const n = ref(3)
function update() {
// console.log('--', this)
// this.n += 2
// this.m += 2
m.value += 2
n.value += 2
// 分发自定义事件
emit('fn', '++')
}
return {
m,
n,
update
}
}
})
</script>
reactive 与 ref-细节
- 是 Vue3 的 composition API 中 2 个最重要的响应式 API
- ref 用来处理基本类型数据, reactive 用来处理对象(递归深度响应式)
- 如果用 ref 对象/数组, 内部会自动将对象/数组转换为 reactive 的代理对象
- ref 内部: 通过给 value 属性添加 getter/setter 来实现对数据的劫持
- reactive 内部: 通过使用 Proxy 来实现对对象内部所有数据的劫持, 并通过 Reflect 操作对象内部数据
- ref 的数据操作: 在 js 中要.value, 在模板中不需要(内部解析模板时会自动添加.value)
<template>
<h2>App</h2>
<p>m1: {{ m1 }}</p>
<p>m2: {{ m2 }}</p>
<p>m3: {{ m3 }}</p>
<button @click="update">更新</button>
</template>
<script lang="ts">
import { reactive, ref } from 'vue'
export default {
setup() {
const m1 = ref('abc')
const m2 = reactive({ x: 1, y: { z: 'abc' } })
// 使用ref处理对象 ==> 对象会被自动reactive为proxy对象
const m3 = ref({ a1: 2, a2: { a3: 'abc' } })
console.log(m1, m2, m3)
console.log(m3.value.a2) // 也是一个proxy对象
function update() {
m1.value += '--'
m2.x += 1
m2.y.z += '++'
m3.value = { a1: 3, a2: { a3: 'abc---' } }
m3.value.a2.a3 += '==' // reactive对对象进行了深度数据劫持
console.log(m3.value.a2)
}
return {
m1,
m2,
m3,
update
}
}
}
</script>
计算属性与监视
- computed 函数:
- 与 computed 配置功能一致
- 只有 getter
- 有 getter 和 setter
- watch 函数
- 与 watch 配置功能一致
- 监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
- 默认初始时不执行回调, 但可以通过配置 immediate 为 true, 来指定初始时立即执行第一次
- 通过配置 deep 为 true, 来指定深度监视
- watchEffect 函数
- 不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
- 默认初始时就会执行第一次, 从而可以收集需要监视的数据
- 监视数据发生变化时回调
<template>
<h2>App</h2>
fistName:
<input v-model="user.firstName" />
<br />
lastName:
<input v-model="user.lastName" />
<br />
fullName1:
<input v-model="fullName1" />
<br />
fullName2:
<input v-model="fullName2" />
<br />
fullName3:
<input v-model="fullName3" />
<br />
</template>
<script lang="ts">
/*
计算属性与监视
1. computed函数:
与computed配置功能一致
只有getter
有getter和setter
2. watch函数
与watch配置功能一致
监视指定的一个或多个响应式数据, 一旦数据变化, 就自动执行监视回调
默认初始时不执行回调, 但可以通过配置immediate为true, 来指定初始时立即执行第一次
通过配置deep为true, 来指定深度监视
3. watchEffect函数
不用直接指定要监视的数据, 回调函数中使用的哪些响应式数据就监视哪些响应式数据
默认初始时就会执行第一次, 从而可以收集需要监视的数据
监视数据发生变化时回调
*/
import { reactive, ref, computed, watch, watchEffect } from 'vue'
export default {
setup() {
const user = reactive({
firstName: 'A',
lastName: 'B'
})
// 只有getter的计算属性
const fullName1 = computed(() => {
console.log('fullName1')
return user.firstName + '-' + user.lastName
})
// 有getter与setter的计算属性
const fullName2 = computed({
get() {
console.log('fullName2 get')
return user.firstName + '-' + user.lastName
},
set(value: string) {
console.log('fullName2 set')
const names = value.split('-')
user.firstName = names[0]
user.lastName = names[1]
}
})
const fullName3 = ref('')
/*
watchEffect: 监视所有回调中使用的数据
*/
/*
watchEffect(() => {
console.log('watchEffect')
fullName3.value = user.firstName + '-' + user.lastName
})
*/
/*
使用watch的2个特性:
深度监视
初始化立即执行
*/
watch(
user,
() => {
fullName3.value = user.firstName + '-' + user.lastName
},
{
immediate: true, // 是否初始化立即执行一次, 默认是false
deep: true // 是否是深度监视, 默认是false
}
)
/*
watch一个数据
默认在数据发生改变时执行回调
*/
watch(fullName3, value => {
console.log('watch')
const names = value.split('-')
user.firstName = names[0]
user.lastName = names[1]
})
/*
watch多个数据:
使用数组来指定
如果是ref对象, 直接指定
如果是reactive对象中的属性, 必须通过函数来指定
*/
watch([() => user.firstName, () => user.lastName, fullName3], values => {
console.log('监视多个数据', values)
})
return {
user,
fullName1,
fullName2,
fullName3
}
}
}
</script>
生命周期
vue2.x 的生命周期
vue3 的生命周期
与 2.x 版本生命周期相对应的组合式 API
beforeCreate
-> 使用setup()
created
-> 使用setup()
beforeMount
->onBeforeMount
mounted
->onMounted
beforeUpdate
->onBeforeUpdate
updated
->onUpdated
beforeDestroy
->onBeforeUnmount
destroyed
->onUnmounted
errorCaptured
->onErrorCaptured
新增的钩子函数
组合式 API 还提供了以下调试钩子函数:
- onRenderTracked
- onRenderTriggered
<template>
<div class="about">
<h2>msg: {{ msg }}</h2>
<hr />
<button @click="update">更新</button>
</div>
</template>
<script lang="ts">
import { ref, onMounted, onUpdated, onUnmounted, onBeforeMount, onBeforeUpdate, onBeforeUnmount } from 'vue'
export default {
beforeCreate() {
console.log('beforeCreate()')
},
created() {
console.log('created')
},
beforeMount() {
console.log('beforeMount')
},
mounted() {
console.log('mounted')
},
beforeUpdate() {
console.log('beforeUpdate')
},
updated() {
console.log('updated')
},
beforeUnmount() {
console.log('beforeUnmount')
},
unmounted() {
console.log('unmounted')
},
setup() {
const msg = ref('abc')
const update = () => {
msg.value += '--'
}
onBeforeMount(() => {
console.log('--onBeforeMount')
})
onMounted(() => {
console.log('--onMounted')
})
onBeforeUpdate(() => {
console.log('--onBeforeUpdate')
})
onUpdated(() => {
console.log('--onUpdated')
})
onBeforeUnmount(() => {
console.log('--onBeforeUnmount')
})
onUnmounted(() => {
console.log('--onUnmounted')
})
return {
msg,
update
}
}
}
</script>
<template>
<h2>App</h2>
<button @click="isShow = !isShow">切换</button>
<hr />
<Child v-if="isShow" />
</template>
<script lang="ts">
import Child from './Child.vue'
export default {
data() {
return {
isShow: true
}
},
components: {
Child
}
}
</script>
总结
个人观赏,纯个人观赏笔记,中间有加入个人观点.欢迎观赏.vue2最好不要和vue3混搭,个人建议.