Vue Composition Api
setup() 函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app"></div>
</body>
<script src="https://unpkg.com/vue@next"></script>
<script>
const app = Vue.createApp({
template: `<div @click="click">name: {{ name }}</div>`,
methods: {
test() {
alert(this.name) // this.name 可以正常获取 setup 返回出来的值
console.log(this.$options.setup()) // 也可以通过 $options 手动调用setup
}
},
// created 实例化被完全初始化之前调用
// props 传入值, context 上下文
setup(props, context) {
// this.test() 报错,由于在初始化之前调用,methods还没初始化
console.log(this) // Window
const click = () => {
alert('click')
}
return {
name: 'Test',
click
}
}
})
const vm = app.mount('#app')
</script>
</html>
ref & reactive & toRefs & readonly & toRef
const app = Vue.createApp({
template: `<div @click="click">{{ name }}</div>`,
setup() {
let name = 'test'; // name 不具备数据绑定
const click = () => { // 更新 name 页面不改变
name = '123'
}
return {
name,
click
}
}
})
// ref
const app = Vue.createApp({
template: `<div @click="click">{{ name }}</div>`,// 模板渲染会自动获取value的值
setup() {
let { ref } = Vue // ref 主要用于原始数据的数据绑定
let name = ref('test'); // 使用 ref 会将数据变成 proxy({ value: 'test' })
const click = () => {
name.value = '123' // 需要通过 .value 的形式来改变内部对值
}
return {
name,
click
}
}
})
// reactive
const app = Vue.createApp({
template: `<div @click="click">{{ obj.name }}</div>`,
setup() {
let { reactive } = Vue // reactive 主要用于引用数据的数据绑定
const obj = reactive({ name: 'test' }) // 使用 reactive 会将数据变成 proxy({ name: 'test' })
const click = () => {
obj.name = '123' // 可以直接获取值
}
return {
obj,
click
}
}
})
// reactive 结构
const app = Vue.createApp({
template: `<div @click="click">{{ name }}</div>`,
setup() {
let { reactive, toRefs } = Vue
const obj = reactive({ name: 'test' })
// let { name } = obj 直接结构会失去绑定
let { name } = toRefs(obj) // 会把 reactive 封装成 { name: proxy({ value: 'test' }) }
const click = () => {
name.value = '123'
}
return {
name,
click
}
}
})
// readonly
const app = Vue.createApp({
template: `<div @click="click">{{ copyObj.name }}</div>`,
setup() {
let { reactive, readonly } = Vue
const obj = reactive({ name: 'test' })
const copyObj = readonly(obj) // 只读复制
const click = () => {
copyObj.name = '123' // 警告无法修改
}
return {
obj,
copyObj,
click
}
}
})
// toRef
const app = Vue.createApp({
template: `<div >{{ test }}</div>`,
setup() {
let { reactive, toRefs } = Vue
const obj = reactive({ name: 'test' })
let { test } = toRefs(obj) // 使用 toRefs 获取对象内部没有的值
console.log(test) // undefined 没有进行 proxy 封装
let test1 = toRef(obj, 'test') // 使用 toRef 获取对象内部没有的值
console.log(test1) // proxy{ value: undefined } 可以正常使用, 不推荐,可以直接在对象中定义 test: nudefined 即可
return {
test
}
}
})
props & context
const app = Vue.createApp({
template: `<child test="test" p="p"><div>father</div></child>`
})
app.component('child',{
mounted(){
console.log('mounted:slots', this.$slots)
console.log('mounted:emit', this.$emit)
},
props: {
p: {
type: String,
default: ''
}
},
setup(props, context) {
console.log(props.p) // p 同原来的 props
let { attrs, slots, emit } = context
console.log(attrs) // proxy { test: "test" } none-props: 没有被 props 接收的属性
console.log(slots) // slots 等价与原来的 this.$slots
console.log(emit) // slots 等价与原来的 this.$emit
// 执行 .default() 函数会返回一个对应的虚拟 DOM 列表
let vm = slots.default()
// 可以使用渲染函数进行渲染
let { h } = Vue
// 不使用 template, 使用渲染函数返回,需要以函数的形式
// return h('div', {} ,vm) 报错
// setup() should not return VNodes directly - return a render function instead.
return () => h('div', {} ,vm) // h(标签, 属性, 内容)
}
})
const vm = app.mount('#app')
computed
const app = Vue.createApp({
template: `<div @click="add">{{ count }}:{{ countComputed }}</div>`,
setup(props, context) {
let { ref, computed } = Vue
let count = ref(0)
let add = () => {
count.value++
}
let countComputed = computed({ // ComputedRefImpl { value ... }
get() {
return count.value + 5
},
set(val) {
count.value = val
}
})
console.log(countComputed.value) // 5
return {
count,
countComputed,
add
}
}
})
watch & watchEffect
// watch
const app = Vue.createApp({
template: `
<div>
<input v-model="w"/>
<div>{{ w1 }}</div>
</div>
`,
setup(props, context) {
let { ref, watch } = Vue
let w = ref('')
let w1 = ref('')
// 惰性,需要数据改变才会触发
// 可以获得改变前、后的值
let stop = watch(w, (newW,oldW) => {
w1.value = `${newW}${oldW}`
},{
// deep: true 监听对象内部值的变化
// immediate: true 立即触回调
// ...
})
// 通过调用 stop() 可以取消监听 watchEffect 同理
return {
w,w1
}
}
})
// 监听多个
const app = Vue.createApp({
template: `
<div>
<input v-model="w"/>
<input v-model="w2"/>
<div>{{ w1 }}</div>
</div>
`,
setup(props, context) {
let { ref, watch } = Vue
let w = ref('')
let w1 = ref('')
let w2 = ref('')
watch([w, w2], ([nw, nw2],[ow,ow2]) => { // 可以使用数组进行监听,取到的值也为数组
w1.value = `${nw}${nw2}`
})
return {
w, w1, w2
}
}
})
// 监听对象内部的值
const app = Vue.createApp({
template: `
<div>
<input v-model="obj.w"/>
<div>{{ obj.w1 }}</div>
</div>
`,
setup(props, context) {
let { reactive, watch } = Vue
let obj = reactive({ w: 'w', w1: '' })
watch(() => obj.w,(n, o) => { // 监听对象内部的值需要使用函数
obj.w1 = `${n}${o}`
})
return {
obj
}
}
})
// watchEffect
const app = Vue.createApp({
template: `
<div>
<div @click="b1 = !b1">{{b1}}</div>
<div @click="obj.b2 = !obj.b2">{{obj.b2}}</div>
<div v-show="show">show</div>
</div>
`,
setup(props, context) {
let { watchEffect, ref, reactive } = Vue
let b1 = ref(false)
let obj = reactive({
b2: false
})
let show = ref(false)
// 立即执行,没有惰性
// 会自动感知依赖的值(ref, reactive),不需要像 watch 一样传递监听的值,没有过多参数,主要就一个回调函数
// 不能获取旧数据
watchEffect(() => {
show.value = b1.value && obj.b2 // 可以方便的进行监听判断
})
return {
b1, b2 , show
}
}
})
生命周期函数
setup() 介于 beforeCreate、created 直接 beforeCreate、created的方法体可以直接写在 setup() 内部
选项式 API | setup() 对应方法 |
---|---|
beforeCreate | 无 |
created | 无 |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
使用方法
const app = Vue.createApp({
template: `
<div>
<div @click="b1 = !b1">{{ b1 }}</div>
</div>
`,
setup(props, context) {
let { onRenderTracked, onRenderTriggered, ref } = Vue
// 每次渲染后重新收集响应式依赖
onRenderTracked(({ key, target, type }) => {
console.log({ key, target, type })
console.log('onRenderTracked')
})
// 每次触发页面重新渲染时自动执行
onRenderTriggered(() => {
console.log('onRenderTriggered')
})
let b1 = ref(false)
return {
b1
}
}
})
provide & inject
const app = Vue.createApp({
template: `
<div>
<child></child>
</div>
`,
setup(props, context) {
let { ref, provide, readonly } = Vue
let name = ref('test')
provide('name', readonly(name)) // 使用 readonly 反正组件内部直接通过 .value 修改值 提供 name
provide('changeName', (value) => { // 提供 changeName(value)
name.value = value
})
}
})
app.component('child', {
template: `
<div @click="change">{{ name }}</div>
`,
setup(props, context) {
let { inject } = Vue
let name = inject('name', '') // 接收 name
let changeName = inject('changeName') // 接收 changeName
let change = () => {
// name.value = 'aaa' 警告 无法修改 readonly 值
changeName('abc') // 通过获取修改方法进行修改
}
return {
name,
change
}
}
})
DOM ref
const app = Vue.createApp({
template: `
<div>
<!-- 设置 ref="test" -->
<div ref="test">test</div>
</div>
`,
setup(props, context) {
let { ref, onMounted } = Vue
let test = ref(null) // 配置对应的变量 ref="test"
onMounted(() => {
console.log(test.value) // 通过 .value 可以获取到 DOM
})
return {
test // 返回出去
}
}
})