Composition API
1 Setup 函数的使用
Composition api
代码编写要建立在setup
函数上
-
执行时间:
created
实例被完全初始化之前setup
函数内部 获取不到this
1.1 return
的内容可以在 template
中直接使用
const app = Vue.createApp({
template: `
<div @click="handleClick">name: {{name}}, age: {{age}}</div>
`,
// created 实例被完全初始化之前
setup(props, context) {
return {
name: 'dell',
age: 18,
handleClick() {
alert(1223)
}
}
},
})
const vm = app.mount('#root')
1.2 setup
函数内部不能使用外部的方法;生命周期函数、方法中可以使用 setup
函数
const app = Vue.createApp({
template: `
<div @click="handleClick">name: {{name}}, age: {{age}}</div>
`,
methods: {
test() {
console.log(this.$options.setup());
}
},
mounted() {
this.test()
},
// created 实例被完全初始化之前
setup(props, context) {
return {
name: 'dell',
age: 18,
handleClick() {
alert(1223)
}
}
},
})
const vm = app.mount('#root')
2 ref,reactive 响应式引用的用法和原理
- 原理:通过
proxy
对数据进行封装,当数据变化时,触发模板等内容的更新
2.1 ref
:处理基础类型的数据
const app = Vue.createApp({
template: `
<div>name: {{ name }}</div>
`,
setup(props, context) {
const { ref } = Vue;
// ref, 'dell' 变成 proxy({ value: 'dell' }) 的响应式引用
let name = ref('dell');
setTimeout(() => {
name.value = 'lee'
}, 2000);
return { name }
},
})
const vm = app.mount('#root')
2.2 reactive
:处理非基础类型的数据
const app = Vue.createApp({
template: `
<div>name: {{ nameObj.name }}</div>
`,
setup(props, context) {
const { reactive } = Vue;
// reactive, { name: 'dell' } 变成 proxy({ name: 'dell' }) 的响应式引用
const nameObj = reactive({ name: 'dell' });
setTimeout(() => {
nameObj.name = 'lee'
}, 1000);
return { nameObj }
},
})
const vm = app.mount('#root')
2.3 readonly
:限制响应式引用不可被修改
const app = Vue.createApp({
template: `
<div>name: {{ nameObj.name }}</div>
`,
setup(props, context) {
const { reactive, readonly } = Vue;
const nameObj = reactive({ name: 'dell' });
const copyNameObj = readonly(nameObj);
setTimeout(() => {
nameObj.name = 'lee'
copyNameObj.name = 'lee' // target is readonly.
}, 1000);
return { nameObj, copyNameObj}
},
})
const vm = app.mount('#root')
2.4 toRefs
:将响应式属性的属性转换为响应式的
const app = Vue.createApp({
template: `
<div>name: {{ name }}</div>
`,
setup(props, context) {
const { reactive, toRefs } = Vue;
// reactive, { name: 'dell' } 变成 proxy({ name: 'dell' }) 的响应式引用
const nameObj = reactive({ name: 'dell', age: 18 });
setTimeout(() => {
nameObj.name = 'lee'
nameObj.age = 20
}, 1000);
// toRefs, ({ name: 'dell', age: 18 }) 变成 { name: proxy({ value: 'dell' }), age: proxy({ value: 18 })}
const { name } = toRefs(nameObj)
return { name }
},
})
const vm = app.mount('#root')
3 toRef 以及 context 参数
3.1 toRef
const app = Vue.createApp({
template: `
<div>name: {{ age }}</div>
`,
setup(props, context) {
const { reactive, toRef } = Vue;
const data = reactive({ name: 'dell' });
const age = toRef(data, 'age')
setTimeout(() => {
age.value = 'lee'
}, 1000);
return { age }
},
})
const vm = app.mount('#root')
3.2 context
1. attrs
attrs
->Non-Props
属性
const app = Vue.createApp({
template: `
<child app='app' />
`,
})
app.component('child', {
template: `
<div>child</div>
`,
setup(props, context) {
const { attrs, slots, emit } = context
console.log(attrs); // Non-Props属性
return {}
},
})
const vm = app.mount('#root')
2. slots
slots.default()
->slot
的内容
const app = Vue.createApp({
template: `
<child>parent</child>
`,
})
app.component('child', {
setup(props, context) {
const { h } = Vue
const { attrs, slots, emit } = context
console.log(slots.default()); // slot 的内容
return () => h('div', {}, slots.default())
},
})
const vm = app.mount('#root')
3. emit
const app = Vue.createApp({
methods: {
handleChange() {
alert('change')
}
},
template: `
<child @change="handleChange">parent</child>
`,
})
app.component('child', {
template: `<div @click="handleClick">123123</div>`,
setup(props, context) {
const { h } = Vue
const { attrs, slots, emit } = context
function handleClick() { emit('change') } // 向外触发事件
return { handleClick }
},
})
const vm = app.mount('#root')
4 使用 Composition API 开发 TodoList
// 关于 list 操作的代码做封装
const listRelativeEffect = () => {
const { reactive } = Vue;
const list = reactive([]);
const addItemToList = (item) => {
list.push(item)
}
return { list, addItemToList }
}
// 关于 input 操作的代码做封装
const inputRelativeEffect = () => {
const { ref } = Vue;
const inputValue = ref('');
const handleInpulValueChange = e => {
inputValue.value = e.target.value
}
return { inputValue, handleInpulValueChange }
}
const app = Vue.createApp({
setup() {
// 流程调度中转
const { list, addItemToList } = listRelativeEffect()
const { inputValue, handleInpulValueChange } = inputRelativeEffect()
return {
list,
addItemToList,
inputValue,
handleInpulValueChange,
}
},
template: `
<div>
<input :value="inputValue" @input="handleInpulValueChange" />
<div>{{ inputValue }}</div>
<button @click="()=>addItemToList(inputValue)">提交</button>
<ul>
<li v-for="(item, index) in list" :key=index>{{ item }}</li>
</ul>
</div>
`,
})
const vm = app.mount('#root')
5 computed方法生成计算属性
computed
一般操作get
const app = Vue.createApp({
setup() {
const { ref, computed } = Vue;
const count = ref(0);
const handleClick = () => count.value += 1
const countAddFive = computed(() => count.value + 5 )
return { count, handleClick, countAddFive }
},
template: `
<div>
<span @click="handleClick">{{count}}</span> -- {{countAddFive}}
</div>
`,
})
const vm = app.mount('#root')
computed
一般操作get
+set
const app = Vue.createApp({
setup() {
const { ref, computed } = Vue;
const count = ref(0);
const handleClick = () => count.value += 1
let countAddFive = computed({
get: () => {
return count.value + 5
},
set: (param) => {
return count.value = param - 5
}
})
setTimeout(() => {
countAddFive.value = 100
}, 1000);
return { count, handleClick, countAddFive }
},
template: `
<div>
<span @click="handleClick">{{count}}</span> -- {{countAddFive}}
</div>
`,
})
const vm = app.mount('#root')
6 watch 和 watchEffect 的使用和差异性
6.1 watch 侦听器
-
具备一定的惰性
lazy
-
参数可以拿到原始和当前值
1. 一般用法
const app = Vue.createApp({
setup() {
const { ref, watch } = Vue
const name = ref('dell')
watch(name, (currentVal, prevVal) => {
console.log(currentVal, prevVal);
})
return { name }
},
template: `
<div>
Name: <input v-model="name" />
</div>
<div>
Name is {{ name }}
</div>
`,
})
const vm = app.mount('#root')
2. 侦听 reactive
方法下的数据需要用箭头函数
const app = Vue.createApp({
setup() {
const { reactive, watch, toRefs } = Vue
const nameObj = reactive({ name: 'dell' })
watch(() => nameObj.name, (currentVal, prevVal) => {
console.log(currentVal, prevVal);
})
const { name } = toRefs(nameObj)
return { name }
},
template: `
<div>
Name: <input v-model="name" />
</div>
<div>
Name is {{ name }}
</div>
`,
})
const vm = app.mount('#root')
3. 用一个侦听器可以侦听多个数据的变化
const app = Vue.createApp({
setup() {
const { reactive, watch, toRefs} = Vue
const nameObj = reactive({ name: 'dell', englishName: 'lee' })
watch([() => nameObj.name, () => nameObj.englishName], ([curName, curEng], [prevName, prevEng]) => {
console.log(curName, prevName, '------------', curEng, prevEng);
})
const { name, englishName } = toRefs(nameObj)
return { name, englishName }
},
template: `
<div>
Name: <input v-model="name" />
</div>
<div>
Name is {{ name }}
</div>
<div>
englishName: <input v-model="englishName" />
</div>
<div>
englishName is {{ englishName }}
</div>
`,
})
const vm = app.mount('#root')
4. 设置非惰性
watch([() => nameObj.name, () => nameObj.englishName], ([curName, curEng], [prevName, prevEng]) => {
console.log(curName, prevName, '------------', curEng, prevEng);
}, { immediate: true })
6.2 watchEffect 侦听器,偏向于 effect
-
立即执行,没有惰性
immediate
-
不需要传递你要侦听的内容,会自动感知代码依赖,不需要传递很多参数,只要传递一个回调函数
-
不能获取之前数据的值
const app = Vue.createApp({
setup() {
const { reactive, watch, toRefs, watchEffect } = Vue
const nameObj = reactive({ name: 'dell', englishName: 'lee' })
watchEffect(() => {
console.log(nameObj.name);
})
const { name, englishName } = toRefs(nameObj)
return { name, englishName }
},
template: `
<div>
Name: <input v-model="name" />
</div>
<div>
Name is {{ name }}
</div>
<div>
englishName: <input v-model="englishName" />
</div>
<div>
englishName is {{ englishName }}
</div>
`,
})
const vm = app.mount('#root')
6.3 关闭侦听
const stop = watch(name, (currentVal, prevVal) => {
console.log(currentVal, prevVal);
setTimeout(() => {
stop()
}, 5000)
})
const stop = watchEffect(() => {
console.log(nameObj.name);
setTimeout(() => {
stop()
}, 5000);
})
7 生命周期函数的新写法
-
beforeCreate
,created
不存在 -
beforeMount
=>onBeforeMount
-
mounbted
=>onMounted
-
beforeUpdate
=>onBeforeUpdate
-
updated
=>onUpdated
-
beforeUnmount
=>onBeforeUnmount
-
unmounted
=>onUnmounted
-
onRenderTracked
每次渲染后重新收集响应式依赖时执行 -
onRenderTriggered
每次触发页面重新渲染时执行
const app = Vue.createApp({
setup() {
const { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onRenderTracked, onRenderTriggered } = Vue;
const name = ref('dell')
const handleClick = () => {
name.value = 'lee'
}
onBeforeMount(() => {
console.log('onBeforeMount');
})
onMounted(() => {
console.log('onMounted');
})
onBeforeUpdate(() => {
console.log('onBeforeUpdate');
})
onUpdated(() => {
console.log('onUpdated');
})
onRenderTracked(() => {
console.log('onRenderTracked');
})
onRenderTriggered(() => {
console.log('onRenderTriggered');
})
return { name, handleClick }
},
template: `
<div @click="handleClick">
{{ name }}
</div>
`,
})
const vm = app.mount('#root')
8 Provide, Inject, 模版 Ref 的用法
8.1 provide inject
1. 父子传值
const app = Vue.createApp({
setup() {
const { provide } = Vue;
provide('name', 'dell');
return { }
},
template: `
<div>
<child />
</div>
`,
})
app.component('child', {
setup() {
const { inject } = Vue;
const name = inject('name', 'hello'); // 'hello' 为设置的默认值
return { name }
},
template: `<div>{{name}}</div>`
})
const vm = app.mount('#root')
2. 改变传递的数据
readonly
单向数据流,限制父组件传递的数据只能由父组件修改
const app = Vue.createApp({
setup() {
const { ref, provide, readonly } = Vue;
const name = ref('dell');
provide('name', readonly(name));
provide('changeName', value => {
name.value = value
})
return { }
},
template: `
<div>
<child />
</div>
`,
})
app.component('child', {
setup() {
const { inject } = Vue;
const name = inject('name');
const changeName = inject('changeName')
const handleClick = () => {
changeName('lee')
}
return { name, handleClick }
},
template: `<div @click="handleClick">{{name}}</div>`
})
const vm = app.mount('#root')
8.2 dom ref
CompositionApi
语法下获取真实的DOM
元素节点
const app = Vue.createApp({
setup() {
const { ref, onMounted } = Vue
// ②
const hello = ref(null);
onMounted(() => {
console.log(hello, hello.value);
})
// ③
return { hello }
},
// ①
template: `
<div>
<div ref="hello">hello</div>
</div>
`,
})
const vm = app.mount('#root')