目录
2. Vue3数组直接可以用索引改,vue2要通过set方法
Composition API(组合API )
setup配置项(函数)
理解:Vue3.0中一个新的配置项,值为一个函数。
setup是所有Composition API(组合API)“ 表演的舞台 ”。
组件中所用到的:数据、方法,计算属性等等,均要配置在setup中。
1.setup函数的两种返回值 (对象和渲染函数)
- 若返回一个对象,则对象中的属性、方法, 在模板中均可以直接使用。(重点关注!)
- 若返回一个渲染函数:则可以自定义渲染内容。
1.1 setup函数返回对象
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h2>a的值是:{{a}}</h2>
<button @click="sayHello">说活(Vue3配置的---sayHello)</button>
</template>
<script>
export default {
name: 'App',
/* 此处测试setup,暂时不考虑响应式问题 */
setup(){
/* 数据 */
let name='迪丽热巴'
let age='18'
//let a=200
/* 方法 */
function sayHello(){
alert(`我叫${name},我${age}岁数了`)
}
/* 返回一个对象(常用) */
return {
name,
age,
sayHello,
}
}
}
1.2setup函数返回渲染函数
<script>
import {h} from 'vue'
export default {
name: 'App',
setup(){
/* 返回一个函数(渲染函数) */
return()=>{ return h('h1','迪丽热巴')}
}
}
2.setup的注意点
-
尽量不要与Vue2.x配置混用
-
Vue2.x配置(data、methos、computed...)中可以访问到setup中的属性、方法。
-
但在setup中不能访问到Vue2.x配置(data、methos、computed...)。
-
如果有重名, setup优先。
-
-
setup不能是一个async函数,因为返回值不再是return的对象, 而是promise, 模板看不到return对象中的属性。(后期也可以返回一个Promise实例,但需要Suspense和异步组件的配合)
ref函数(定义响应式数据,可以是基本类型也可以是对象)
1.语法(const xxx = ref(数据))
先按需导入ref函数
import { ref } from "vue";
-
作用: 定义一个响应式的数据
-
语法:
const xxx = ref(initValue)
-
创建一个包含响应式数据的引用对象(reference对象,简称ref对象)。
-
let name=ref('迪丽热巴')
let age=ref(18)
let job=ref({
type:'演员',
salary:'3000W'
})
1.JS中操作数据: xxx.value
<script>
import { ref } from "vue";
export default {
name: 'App',
setup(){
/* 数据 */
let name=ref('迪丽热巴')
let age=ref(18)
let job=ref({
type:'演员',
salary:'3000W'
})
/* 方法 */
function changeInfo(){
name.value='迪丽热巴啊'
age.value=20
job.value.type='艺人'
job.value.salary='4000W'
//console.log(name,age);
}
/* 返回一个对象(常用) */
return {
name,
age,
changeInfo,
job
}
}
}
</script>
2.模板中读取数据: 不需要.value,直接:<div>{{xxx}}</div>
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{name}}</h2>
<h2>年龄:{{age}}</h2>
<h3>工作种类:{{job.type}}</h3>
<h3>工作薪资:{{job.salary}}</h3>
<button @click="changeInfo">修改人的信息</button>
</template>
-
备注:
-
接收的数据可以是:基本类型、也可以是对象类型。
-
基本类型的数据:响应式依然是靠
Object.defineProperty()
的get
与set
完成的。 -
对象类型的数据:内部 “ 求助 ” 了Vue3.0中的一个新函数——
reactive
函数。
-
reactive函数(定义一个对象类型的响应式数据)
-
作用: 定义一个对象类型的响应式数据(基本类型不要用它,要用
ref
函数) -
语法:
const 代理对象= reactive(源对象)
接收一个对象(或数组),返回一个代理对象(Proxy的实例对象,简称proxy对象) -
reactive定义的响应式数据是“深层次的”。
-
内部基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据进行操作。
1.语法(与ref类似,包裹的是对象类型)
let p={
name:'张三',
age:18,
job:{
type:'演员',
salary:'3000W',
a:{
b:{
c:666
}
},
},
hobby:['演戏','演戏','演戏']
}
let person= reactive(p)
2. Vue3数组直接可以用索引改,vue2要通过set方法
<template>
<h1>一个人的信息</h1>
<h2 v-show="person.name">姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<h2 v-show="person.sex">性别:{{person.sex}}</h2>
<h3>工作种类:{{person.job.type}}</h3>
<h3>工作薪资:{{person.job.salary}}</h3>
<h3>爱好:{{person.hobby}}</h3>
<h3>测试的数据c:{{person.job.a.b.c}}</h3>
<button @click="changeInfo">修改人的信息</button>
<button @click="addSex">添加一个sex属性</button>
<button @click="deleteName">删除一个name属性</button>
</template>
<script>
import { reactive } from "vue";
export default {
name: 'App',
setup(){
let p={
name:'张三',
age:18,
job:{
type:'演员',
salary:'3000W',
a:{
b:{
c:666
}
},
},
hobby:['演戏','演戏','演戏']
}
let person= reactive(p)
/* 方法 */
function changeInfo(){
person.name='迪丽热巴啊'
person.age=20
person.job.type='艺人'
person.job.salary='4000W'
person.job.a.b.c=999
/* Vue3数组直接可以用索引改,vue2要通过set方法 */
person.hobby[0]='学习'
}
function addSex(){
person.sex='女'
}
function deleteName(){
delete person.name
}
/* 返回一个对象(常用) */
return {
person,
changeInfo,
addSex,
deleteName
}
}
}
</script>
Vue3.0中的响应式原理
1.vue2.x的响应式
-
实现原理:
-
对象类型:通过
Object.defineProperty()
对属性的读取、修改进行拦截(数据劫持)。 - 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
Object.defineProperty(data, 'count', {
get () {},
set () {}
})
-
存在问题:
-
新增属性、删除属性, 界面不会更新。
-
直接通过下标修改数组, 界面不会自动更新。
-
2.Vue3.0的响应式
-
实现原理:
-
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
-
通过Reflect(反射): 对源对象的属性进行操作。
-
MDN文档中描述的Proxy与Reflect:
-
Proxy:Proxy - JavaScript | MDN
-
Reflect:Reflect - JavaScript | MDN
-
-
new Proxy(data, {
// 拦截读取属性值
get (target, prop) {
return Reflect.get(target, prop)
},
// 拦截设置属性值或添加新属性
set (target, prop, value) {
return Reflect.set(target, prop, value)
},
// 拦截删除属性
deleteProperty (target, prop) {
return Reflect.deleteProperty(target, prop)
}
})
proxy.name = 'tom'
3.reactive对比ref
-
从定义数据角度对比:
-
ref用来定义:基本类型数据。
-
reactive用来定义:对象(或数组)类型数据。
-
备注:ref也可以用来定义对象(或数组)类型数据, 它内部会自动通过
reactive
转为代理对象。
-
-
从原理角度对比:
-
ref通过
Object.defineProperty()
的get
与set
来实现响应式(数据劫持)。 -
reactive通过使用Proxy来实现响应式(数据劫持), 并通过Reflect操作源对象内部的数据。
-
-
从使用角度对比:
-
ref定义的数据:操作数据需要
.value
,读取数据时模板中直接读取不需要.value
。 -
reactive定义的数据:操作数据与读取数据:均不需要
.value
。
-
setup的两个注意点
-
setup执行的时机
-
在beforeCreate之前执行一次,this是undefined。
-
-
setup的参数
-
props:值为对象,包含:组件外部传递过来,且组件内部声明接收了的属性。
-
context:上下文对象
-
attrs: 值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性, 相当于
this.$attrs
。 -
slots: 收到的插槽内容, 相当于
this.$slots
。 -
emit: 分发自定义事件的函数, 相当于
this.$emit
。
-
-
App.vue
<template>
<Demo @hello="showHello" msg='你好呀' name='迪丽热巴'>
<template v-slot:qwe>
<span>迪丽热巴</span>
</template>
<template v-slot:asd>
<span>迪丽热巴</span>
</template>
</Demo>
</template>
<script>
import Demo from '@/components/Demo'
export default {
name: 'App',
components:{
Demo
},
setup(){
function showHello(){
alert('你好')
}
return{
showHello
}
}
}
</script>
子组件Demo.vue
<template>
<h1>一个人的信息</h1>
<h2>姓名:{{person.name}}</h2>
<h2>年龄:{{person.age}}</h2>
<button @click="test">测试触发一下Demo组件的hello事件</button>
</template>
<script>
import { reactive } from "vue";
export default {
name: 'MyDemo',
/* beforeCreate(){
console.log('------beforeCreate------');
}, */
emits:['hello'],
props:['msg','name'],
setup(props,context){
//console.log('-----setup-----',props);
// console.log('-----setup-----',context.attrs);//相当于与vue2中的$attrs
// console.log('-----setup-----',context.emit);
console.log('-----setup-----',context.slots);
let person= reactive({
name:'张三',
age:18,
})
function test(){
context.emit('hello',666)
}
/* 返回一个对象(常用) */
return {
person,
test
}
}
}
</script>