Composition API
Setup函数的使用
setup:在created实例被完全初始化之前执行,接收两个参数props,context,最后一定要return出去才能使用
props:外部组件传递过来的一些内容
context:指的是上下文
<body>
<div id="root">
</div>
<script>
// data & methods & computed & watcher
const app = Vue.createApp({
template: `
<div @click="vueClick">{{name}}</div>
`,
// created 实例被完全初始化之前
// props 外部组件传递过来的内容
// context 指的是上下文
setup(props, context) {
return {
name: 'setup学习',
vueClick: () => {
alert('vue3的setup学习')
}
}
}
})
const vm = app.mount('#root')
</script>
</body>
我们发现我们直接在setup中return的name,vueClick方法,在页面上可以直接使用并展示出来。
额外补充:
因为setup是在created实例化初始化之前被执行,所以methods中方法在setup中调用会报错
<body>
<div id="root">
</div>
<script>
// data & methods & computed & watcher
const app = Vue.createApp({
template: `
<div @click="vueClick">{{name}}</div>
`,
methods: {
methodClick() {
alert('methods')
}
},
// created 实例被完全初始化之前
// props 外部组件传递过来的内容
// context 指的是上下文
setup(props, context) {
this.methodClick()
return {
name: 'setup学习',
vueClick: () => {
alert('vue3的setup学习')
}
}
}
})
const vm = app.mount('#root')
</script>
</body>
结论:因为setup是在实例初始化之前执行,此时methods并没有放到this上面,所以我们通过this.methodClick()是无法调用methods的,而且我们用了setup之后,就不会再使用this了。
但是我们可以在外部的methods和mounted中去调用setup,代码如下
<body>
<div id="root">
</div>
<script>
// data & methods & computed & watcher
const app = Vue.createApp({
template: `
<div @click="vueClick">{{name}}</div>
`,
methods: {
methodClick() {
console.log('看看组件上挂载了什么',this.$options)
console.log('看看setup返回了什么',this.$options.setup())
}
},
mounted() {
this.methodClick()
},
// created 实例被完全初始化之前
// props 外部组件传递过来的内容
// context 指的是上下文
setup(props, context) {
// this.methodClick()
return {
name: 'setup学习',
vueClick: () => {
alert('vue3的setup学习')
}
}
}
})
const vm = app.mount('#root')
</script>
</body>
ref的使用
首先如果我们实现2s后将ref改变为vue3中的ref代码如下:
<body>
<div id="root">
</div>
<script>
// ref
const app = Vue.createApp({
template: `
<div>{{name}}</div>
`,
setup(props, context) {
let name = 'ref'
setTimeout(() => {
name = 'vue3中的ref'
}, 2000)
return {
name,
}
}
})
const vm = app.mount('#root')
</script>
</body>
我们发现这样写,2s后name的值不会改变,控制台结果如下:
因为name就是一个普通的变量,而不是一个响应式的变量,所以name变了页面也不会改变
ref响应式的引用
原理:是通过proxy对数据进行封装,当数据变化时,触发模板等内容的更新,ref是处理基础类型数据。
<body>
<div id="root">
</div>
<script>
// ref 处理基础类型数据
const app = Vue.createApp({
template: `
<div>{{name}}</div>
`,
setup(props, context) {
// proxy, 将'ref'变成proxy({value: 'vue3中的ref'})这样的一个响应式引用
// 底层就是做了proxy的封装,封装成({value: 'vue3中的ref'})
const { ref } = Vue;
let name = ref('ref')
setTimeout(() => {
name.value = 'vue3中的ref'
}, 2000)
return {
name,
}
}
})
const vm = app.mount('#root')
</script>
</body>
控制台显示结果:
结论:vue中给我们提供了ref方法,当我们使用ref处理基础类型数据时,需要先引入ref,而vue底层通过proxy进行了封装,封装成({value: 'vue3中的ref'}),处理后我们在组件中不需要再用name.value去调用了,vue给我们做了处理,我们在组件中直接可以{{name}}去使用。
我们还可以将上面的方法换成另一种写法:
首先如果我们实现2s后将reactive改变为vue3中的reactive代码如下:
reactive的使用
<body>
<div id="root">
</div>
<script>
// reactive 处理非基础类型数据
const app = Vue.createApp({
template: `
<div>{{nameObj.name}}</div>
`,
setup(props, context) {
const { reactive } = Vue;
const nameObj = { name: 'reactive' }
setTimeout(() => {
nameObj.name = 'vue3中的reactive'
}, 2000)
return {
nameObj,
}
}
})
const vm = app.mount('#root')
</script>
</body>
我们发现这样写,2s后name的值不会改变,控制台结果如下:
因为name就是一个普通的变量对象,而不是一个响应式的变量,所以name变了页面也不会改变
reactive响应式的引用
原理:是通过proxy对数据进行封装,当数据变化时,触发模板等内容的更新,reactive是处理非基础类型数据。
<body>
<div id="root">
</div>
<script>
// reactive 处理非基础类型数据
const app = Vue.createApp({
template: `
<div>{{nameObj.name}}</div>
`,
setup(props, context) {
const { reactive } = Vue;
// proxy, 将{name: 'reactive'}变成proxy({name: 'vue3中的reactive'})这样的一个响应式引用
const nameObj = reactive({ name: 'reactive' })
setTimeout(() => {
nameObj.name = 'vue3中的reactive'
}, 2000)
return {
nameObj,
}
}
})
const vm = app.mount('#root')
</script>
</body>
控制台显示结果:
结论:vue中给我们提供了reactive方法,当我们使用reactive处理非name基础类型数据时,需要先引入reactive,而vue底层通过proxy进行了封装,封装成({name: 'vue3中的reactive'}),处理后我们在组件中可以直接用{{nameObj.name}}去使用。
reactive上面处理的是对象我们还可以处理数组:
<body>
<div id="root">
</div>
<script>
// reactive 处理非基础类型数据
const app = Vue.createApp({
template: `
<div>{{nameObj[0]}}</div>
`,
setup(props, context) {
const { reactive } = Vue;
// proxy, 将{name: 'reactive'}变成proxy({name: 'vue3中的reactive'})这样的一个响应式引用
const nameObj = reactive(['reactive'])
setTimeout(() => {
nameObj[0] = 'vue3中的reactive'
}, 2000)
return {
nameObj,
}
}
})
const vm = app.mount('#root')
</script>
</body>
控制台结果如下:
结论:我们发现用vue3中提供的composition API中的ref和reactive可以替代vue2中data()中定义的变量
readonly的使用
如何把变量变成只读的? 通过vue3中提供了的readonly
<body>
<div id="root">
</div>
<script>
// reactive 处理非基础类型数据
const app = Vue.createApp({
template: `
<div>{{nameObj[0]}}</div>
`,
setup(props, context) {
const { reactive, readonly } = Vue;
// proxy, 将{name: 'reactive'}变成proxy({name: 'vue3中的reactive'})这样的一个响应式引用
const nameObj = reactive(['reactive'])
const copyNameObj = readonly(nameObj);
setTimeout(() => {
nameObj[0] = 'vue3中的reactive'
copyNameObj[0] = 'vue3中readonly'
}, 2000)
return {
nameObj,
copyNameObj
}
}
})
const vm = app.mount('#root')
</script>
</body>
控制台显示结果:
我们发现可以通过readonly对响应式的引用做一个限制,控制台会显示一个警告,告诉返回的对象是只读的,不可以修改的。
当我们在组件中不希望上面案例引用对象时使用{{nameObj.name}}而是直接使用name,我们如何实现?
错误写法:
下面这种const { name } = nameObj写法并不能实现2s后改变nameObj的值?
<body>
<div id="root">
</div>
<script>
// reactive 处理非基础类型数据
const app = Vue.createApp({
template: `
<div>{{name}}</div>
`,
setup(props, context) {
const { reactive, readonly } = Vue;
// proxy, 将{name: 'reactive'}变成proxy({name: 'vue3中的reactive'})这样的一个响应式引用
const nameObj = reactive({
name: 'reactive'
})
setTimeout(() => {
nameObj.name = 'vue3中的reactive'
}, 2000)
const { name } = nameObj
return {
name,
}
}
})
const vm = app.mount('#root')
</script>
</body>
控制台显示结果:
上面这样写name是不具备响应式的数据,所以我们直接return{name}导出去是不行的,而vue3中又提供了toRefs方法,我们可以使用toRefs解构来解决,解决代码如下:
toRefs的使用
toRefs的原理: 将reactive转化的toRefs proxy({name: 'vue3中的reactive'}) 转换成 { name: proxy({ value: 'vue3中的reactive'}) }
<body>
<div id="root">
</div>
<script>
// reactive 处理非基础类型数据
const app = Vue.createApp({
template: `
<div>{{name}}</div>
`,
setup(props, context) {
const { reactive, readonly, toRefs } = Vue;
// proxy, 将{name: 'reactive'}变成proxy({name: 'vue3中的reactive'})这样的一个响应式引用
const nameObj = reactive({
name: 'reactive'
})
setTimeout(() => {
nameObj.name = 'vue3中的reactive'
}, 2000)
// toRefs proxy({name: 'vue3中的reactive'}) 转换成 { name: proxy({ value: 'vue3中的reactive'}) }
const { name } = toRefs(nameObj)
return {
name,
}
}
})
const vm = app.mount('#root')
</script>
</body>
控制台显示结果:
结论:将reactive转化的toRefs proxy({name: 'vue3中的reactive'}) 转换成 { name: proxy({ value: 'vue3中的reactive'}) },所以我们上面调用的name相当于调用的name.value的内容。如果多个对象转化如下:
toRefs proxy({name: 'vueage3中的reactive', age: 26})
{ name: proxy({ value: 'vue3中的reactive'}), age: proxy({ value: '26'}) }