目录
一、setup函数
<template>
<div id="app">
<p>{{ number }}</p>
<button @click="add">增加</button>
</div>
</template>
<script>
// 1. 从 vue 中引入 ref 函数
import {ref} from 'vue'
export default {
name: 'App',
setup() {
// 2. 用 ref 函数包装一个响应式变量 number
let number = ref(0)
// 3. 设定一个方法
function add() {
// number是被ref函数包装过了的,其值保存在.value中
number.value ++
}
// 4. 将 number 和 add 返回出去,供template中使用
return {number, add}
}
}
</script>
vue2与vue3变量区别
1、vue2访问data或props中的变量,需要通过this
2、vue3的setup函数有两个参数,分别是props、context
(1)props:存储定义当前组件允许外界传递来的参数名及参数值
(2)context:上下文对象,能从中访问到attr、emit、slots
【emit就是vue2中父组件通信的方法,可以直接拿来调用】
二、生命周期
Vue2 | Vue3 |
---|---|
beforeCreate | setup |
created | setup |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestory | onBeforeUnmount |
destoryed | onUnmounted |
vue3生命周期的使用,也是先从vue中导入,再进行直接调用
<template>
<div id="app"></div>
</template>
<script>
// 1. 从 vue 中引入 多个生命周期函数
import {onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, unMounted} from 'vue'
export default {
name: 'App',
setup() {
onBeforeMount(() => {
// 在挂载前执行某些代码
})
onMounted(() => {
// 在挂载后执行某些代码
})
onBeforeUpdate(() => {
// 在更新前前执行某些代码
})
onUpdated(() => {
// 在更新后执行某些代码
})
onBeforeUnmount(() => {
// 在组件销毁前执行某些代码
})
unMounted(() => {
// 在组件销毁后执行某些代码
})
return {}
}
}
</script>
三、reactive方法
用来创建一个响应式的数据对象,该API也很好地解决了Vue2通过 defineProperty
实现数据响应式的缺陷
<template>
<div id="app">
<!-- 4. 访问响应式数据对象中的 count -->
{{ state.count }}
</div>
</template>
<script>
// 1. 从 vue 中导入 reactive
import {reactive} from 'vue'
export default {
name: 'App',
setup() {
// 2. 创建响应式的数据对象
const state = reactive({count: 3})
// 3. 将响应式数据对象state return 出去,供template使用
return {state}
}
}
</script>
四、ref方法
1、简介
包装了一个响应式的数据对象,ref是通过 reactive
包装了一个对象,然后是将值传给该对象中的 value
属性
注意:.value是在访问ref方法才需要,在template模板是不需要的
2、使用
可以简单地把 ref(obj)
理解为这个样子 reactive({value: obj})
<script>
import {ref, reactive} from 'vue'
export default {
name: 'App',
setup() {
const obj = {count: 3}
const state1 = ref(obj)
const state2 = reactive(obj)
console.log(state1)
console.log(state2)
}
}
</script>
3、ref与reactive
(1)基本类型值(string、number、boolean等)或单值对象(像{count:3}只有一个属性值的)使用ref
(2)引用类型值(object、array)使用reactive
4、获取标签元素或组件
(1)在Vue2中,我们获取元素都是通过给元素一个 ref
属性,然后通过 this.$refs.xx
来访问的,在Vue3中已经不再适用
(2)vue3获取元素如下
<template>
<div>
<div ref="el">div元素</div>
</div>
</template>
<script>
import {ref,onMounted} from 'vue'
export default {
name: "SetupView",
setup() {
// 创建一个DOM引用,名称必须与元素的ref属性名相同
const el = ref(null)
// 在挂载后才能通过el获取到目标元素
onMounted(()=>{
el.value.innerHTML = '内容被修改'
})
// 把创建的引用return出去
return {el}
},
};
</script>
(3)获取元素的操作步骤:
补充:设置的元素引用变量只有在组件挂载后才能访问到,因此在挂载前对元素进行操作都是无效的
五、toRef
1、简介
将某对象中的某值转化为响应式数据,其接收两个参数
(1)第一个参数为 obj
对象
(2)第二个参数为对象中的属性名
<template>
<div>
<div> {{ state }} </div>
</div>
</template>
<script>
import { toRef } from "vue";
export default {
name: "SetupView",
setup() {
const obj = { count: 3 };
const state = toRef(obj, "count"); //注意:count有加单引号
return { state };
},
};
</script>
2、ref与toRef的区别
上面的例子ref也可以实现,那为什么还要toRef呢,来看看他们的区别吧
<template>
<div>
<p>{{ state1 }}</p>
<button @click="add1">增加1</button>
<p>{{ state2 }}</p>
<button @click="add2">增加2</button>
</div>
</template>
<script>
import { ref,toRef } from "vue";
export default {
name: "SetupView",
setup() {
const obj = {count:3}
const state1 = ref(obj.count)
const state2 = toRef(obj,'count')
function add1(){
state1.value++
console.log('ref原始值:',obj);
console.log('ref响应式',state1);
}
function add2(){
state2.value++
console.log('toRef原始值:',obj);
console.log('toRef响应式',state2);
}
return {state1,state2,add1,add2}
},
};
</script>
(1)ref是对传入数据的拷贝;toRef是对传入数据的引用
(2)ref值会改变更新视图;toRef值改变不会更新视图
六、toRefs
将传入的对象里所有的属性的值都转化为响应式数据对象,该函数支持一个参数,即 obj
对象
<template>
<div>
<div>{{ state }}</div>
</div>
</template>
<script>
import { toRefs } from "vue";
export default {
name: "SetupView",
setup() {
const obj={
name:'前端学学学',
age:18,
gender:0
}
const state = toRefs(obj)
console.log(state);
},
};
</script>
七、shallowReactive 浅reactive
1、简介
shallowReactive传递给reactive的obj对象不止一层,那么每一次都会用Proxy包装
<template>
<div>
<div>{{ state }}</div>
</div>
</template>
<script>
import { reactive } from "vue";
export default {
name: "SetupView",
setup() {
const obj={
a:1,
first:{
b:2,
second:{
c:3
}
}
}
const state = reactive(obj)
console.log(state);
console.log(state.first);
console.log(state.first.second);
},
};
</script>
如果一个对象层级比较深,那么每一层都用 Proxy
包装后,对于性能是非常不友好的
2、shallowreactived的浅reactive
shallowreactive只有第一层被Proxy处理了,即只有修改第一层的值,才会响应式更新
<template>
<div>
<div>{{ state.a }}</div>
<div>{{ state.first.b }}</div>
<div>{{ state.first.second.c }}</div>
<button @click="change1">改变1</button>
<button @click="change2">改变2</button>
</div>
</template>
<script>
import { shallowReactive } from "vue";
export default {
name: "SetupView",
setup() {
const obj={
a:1,
first:{
b:2,
second:{
c:3
}
}
}
const state = shallowReactive(obj)
function change1(){
state.a = 7
}
function change2(){
state.first.b = 8
state.first.second.c =9
console.log(state);
}
return {state,change1,change2}
},
};
</script>
八、shallowRef
1、简介
浅层ref,一样是用来做性能优化的
(1)shallowReactive
是监听对象第一层的数据变化用于驱动视图更新
(2)shallowRef
则是监听 .value
的值的变化来更新视图的
(3)代码实例
<template>
<div>
<div>{{ state.a }}</div>
<div>{{ state.first.b }}</div>
<div>{{ state.first.second.c }}</div>
<button @click="change1">改变1</button>
<button @click="change2">改变2</button>
</div>
</template>
<script>
import { shallowRef } from "vue";
export default {
name: "SetupView",
setup() {
const obj={
a:1,
first:{
b:2,
second:{
c:3
}
}
}
const state = shallowRef(obj)
console.log(state);
function change1(){
state.value ={
a:7,
first:{
b:8,
second:{
c:9
}
}
}
}
function change2(){
state.value.first.b = 4
state.value.first.second.c = 5
console.log(state);
}
return {state,change1,change2}
},
};
</script>
①第二个按钮点击数据改变但是视图没变
②第一个按钮将整个 .value
重新赋值,视图才更新
2、triggerRef
triggerRef
可以解决上面实例的第二个按钮的问题,可以立马更新视图,其接收一个参数 state
,即需要更新的 ref
对象
<template>
<div>
<div>{{ state.a }}</div>
<div>{{ state.first.b }}</div>
<div>{{ state.first.second.c }}</div>
<button @click="change">改变</button>
</div>
</template>
<script>
import { shallowRef,triggerRef } from "vue";
export default {
name: "SetupView",
setup() {
const obj={
a:1,
first:{
b:2,
second:{
c:3
}
}
}
const state = shallowRef(obj)
console.log(state);
function change(){
state.value.first.b = 4
state.value.first.second.c = 5
triggerRef(state)
console.log(state);
}
return {state,change}
},
};
</script>
九、toRaw方法
toRaw
方法是用于获取 ref
或 reactive
对象的原始数据的
<template>
<div>
<div>{{ state.name }}</div>
<div>{{ state.age }}</div>
<button @click="change">改变</button>
</div>
</template>
<script>
import { reactive,toRaw } from "vue";
export default {
name: "SetupView",
setup() {
const obj={
name:'前端学学学',
age:18,
}
const state = reactive(obj)
const raw = toRaw(state)
function change(){
state.age = 90
console.log(obj);
console.log(state);
console.log(obj === raw);
}
return {state,change}
},
};
</script>
上述代码就证明了 toRaw
方法从 reactive
对象中获取到的是原始数据,因此我们就可以很方便的通过修改原始数据的值而不更新视图来做一些性能优化了
注意: 补充一句,当 toRaw
方法接收的参数是 ref
对象时,需要加上 .value
才能获取到原始数据对象
十、markRaw
markRaw
方法可以将原始数据标记为非响应式的,即使用 ref
或 reactive
将其包装,仍无法实现数据响应式,其接收一个参数,即原始数据,并返回被标记后的数据
<template>
<div>
<div>{{ state.name }}</div>
<div>{{ state.age }}</div>
<button @click="change">改变</button>
</div>
</template>
<script>
import { reactive, markRaw } from "vue";
export default {
name: "SetupView",
setup() {
const obj = {
name: "前端学学学",
age: 18,
};
// 通过markRaw标记原始数据obj, 使其数据更新不再被追踪
const raw = markRaw(obj);
// 试图用reactive包装raw, 使其变成响应式数据
const state = reactive(raw);
function change() {
state.age = 90;
console.log(state);
}
return { state, change };
},
};
</script>
被 markRaw
方法处理过后的数据不能被 reactive
包装成响应式数据,修改了值也不会更新视图了,即没有实现数据响应式
十一、provide && inject
1、简介
- provide :向子组件以及子孙组件传递数据。接收两个参数,第一个参数是
key
,即数据的名称;第二个参数为value
,即数据的值 - inject :接收父组件或祖先组件传递过来的数据。接收一个参数
key
,即父组件或祖先组件传递的数据名称
2、代码实例
假设这有三个组件,分别是 A.vue
、B.vue
、C.vue
,其中 B.vue
是 A.vue
的子组件,C.vue
是 B.vue
的子组件
// A.vue
<script>
import {provide} from 'vue'
export default {
setup() {
const obj= {
name: '前端印象',
age: 22
}
// 向子组件以及子孙组件传递名为info的数据
provide('info', obj)
}
}
</script>
// B.vue
<script>
import {inject} from 'vue'
export default {
setup() {
// 接收A.vue传递过来的数据
inject('info') // {name: '前端印象', age: 22}
}
}
</script>
// C.vue
<script>
import {inject} from 'vue'
export default {
setup() {
// 接收A.vue传递过来的数据
inject('info') // {name: '前端印象', age: 22}
}
}
</script>
十二、watch && watchEffect
watch
和 watchEffect
都是用来监视某项数据变化从而执行指定的操作的,但用法上还是有所区别
1、watch 简介
watch 格式:watch( source, cb, [options])
- source:可以是表达式或函数,用于指定监听的依赖对象
- cb:依赖对象变化后执行的回调函数
- options:可参数,可以配置的属性有 immediate(立即触发回调函数)、deep(深度监听)
(1)监听ref类型时:
<script>
import { ref, watch } from "vue";
export default {
name: "SetupView",
setup() {
const state = ref(0);
watch(state, (newValue, oldValue) => {
console.log(`原值${oldValue}`);
console.log(`新值${newValue}`);
});
// 1秒后值+1
setTimeout(() => {
state.value++;
}, 1000);
},
};
</script>
(2)监听reactive类型时:
<script>
import { reactive, watch } from "vue";
export default {
name: "SetupView",
setup() {
const state = reactive({count:0});
watch(()=>state.count,(newValue,oldValue)=>{
console.log(`原值${oldValue}`);
console.log(`新值${newValue}`);
});
// 1秒后值+1
setTimeout(() => {
state.count++;
}, 1000);
},
};
</script>
(3)当同时监听多个值时:
<script>
import { reactive, watch } from "vue";
export default {
name: "SetupView",
setup() {
const state = reactive({count:0,name:'23'});
watch([()=>state.count,()=>state.name],([newCount,newName],[oldCount,oldName])=>{
console.log(oldCount);
console.log(newCount);
console.log(oldName);
console.log(newName);
});
// 1秒后值+1
setTimeout(() => {
state.count++;
state.name = 'sxx'
}, 1000);
},
};
</script>
(4)因为 watch
方法的第一个参数我们已经指定了监听的对象,因此当组件初始化时,不会执行第二个参数中的回调函数,若我们想让其初始化时就先执行一遍,可以在第三个参数对象中设置 immediate: true
(5)watch
方法默认是渐层的监听我们指定的数据,例如如果监听的数据有多层嵌套,深层的数据变化不会触发监听的回调,若我们想要其对深层数据也进行监听,可以在第三个参数对象中设置 deep: true
(6)watch方法会返回一个stop方法,若想要停止监听,便可直接执行该stop函数
2、watchEffect
与 watch
的区别
(1)不需要手动传入依赖
(2)每次初始化时会执行一次回调函数来自动获取依赖
(3)无法获取到原值,只能得到变化后的值
<script>
import { reactive, watchEffect } from "vue";
export default {
name: "SetupView",
setup() {
const state = reactive({count:0,name:'23'});
watchEffect(()=>{
console.log(state.count);
console.log(state.name);
})
// 1秒后值+1
setTimeout(() => {
state.count++;
state.name = 'sxx'
}, 1000);
},
};
</script>
没有像 watch
方法一样先给其传入一个依赖,而是直接指定了一个回调函数
当组件初始化时,该回调函数会执行一次,自动获取到需要检测的数据
十三、getCurrentInstance
<template>
<div>
<div>{{ num }}</div>
</div>
</template>
<script>
import { ref,getCurrentInstance } from "vue";
export default {
name: "SetupView",
setup() {
const num = ref(3)
const instance = getCurrentInstance()
console.log(instance);
return {num}
},
};
</script>
1、输出值ctx
2、输出值proxy
ctx
和 proxy
的内容十分类似,只是后者相对于前者外部包装了一层 proxy
,由此可说明 proxy
是响应式的
十四、useStore
通过vuex中的useStore方法
// store 文件夹下的 index.js
import Vuex from 'vuex'
const store = Vuex.createStore({
state: {
name: '前端印象',
age: 22
},
mutations: {
……
},
……
})
// example.vue
<script>
// 从 vuex 中导入 useStore 方法
import {useStore} from 'vuex'
export default {
setup() {
// 获取 vuex 实例
const store = useStore()
console.log(store)
}
}
</script>