第一个参数:props
第二个参数:context 包括:attrs、slots、emit
返回一个对象{},对象中的数据可以在template中被使用,来替代data。
beforeCreate: 组件刚刚被创建出来, 组件的data和methods还没有初始化好setup
Created : 组件刚刚被创建出来, 组件的data和methods已经初始化好
reactive可以实现数据响应式。reactive 对传入的类型是有限制的,它要求我们必须传入的是一个对象或者数组类型,如果我们传入一个基本数据类型,会报一个警告。
const account = reactive({
username: "xhl",
password: "123456"
})
条件一: reactive应用于本地的数据
条件二: 多个数据之间是有关系/联系(聚合的数据, 组织在一起会有特定的作用)
ref和reactive一样, 也是用来实现响应式数据的方法,由于reactive必须传递一个对象, 所以导致在企业开发中,如果我们只想让某个变量实现响应式的时候会非常麻烦,所以Vue3就给我们提供了ref方法, 实现对简单值的监听。
ref底层的本质其实还是reactive系统会自动根据我们给ref传入的值将它转换成
ref(xx) -> reactive({value:xx})
const counter = ref(0)
function increment() {
counter.value++
}
定义本地的一些简单数据:
const message = ref("Hello World")
const counter = ref(0)
const name = ref("why")
const age = ref(18)
定义从网络中获取的数据也是使用ref:
const musics = ref([])
onMounted(() => {
const serverMusics = ["海阔天空", "小苹果", "野狼"]
musics.value = serverMusics
console.log(musics.value);
})
如果在template里使用的是ref类型的数据, 那么Vue会自动帮我们添加.value
如果在template里使用的是reactive类型的数据, Vue不会自动帮我们添加.value
Vue在解析数据之前, 会自动判断这个数据是否是ref类型的,
如果是就自动添加.value, 如果不是就不自动添加.value
通过当前数据的__v_ref来判断的
如果有这个私有的属性, 并且取值为true, 那么就代表是一个ref类型的数据
通过isRef / isReactive 方法
Vue2:利用object.defineproperty实现,无法监听数组的修改和新增object属性,需使用set方法更新
Vue3:利用es6的proxy实现,可以实现数组的修改和新增object属性的监听,实现响应式。
readonly返回的对象都是不允许修改的,readonly会返回原生对象的只读代理(也就是它依然是一个Proxy,这是一个proxy的set方法被劫持,并且不能对其进行修改)。
readonly方法会传入三个类型的参数:
const info = reactive({
name: "why",
age: 18,
height: 1.88
})
const roInfo = readonly(info)
function changeRoInfoName (payload) {
info.name = payload
}
roInfo不能被修改,但是info可以被修改
◼isProxy
检查对象是否是由 reactive 或 readonly创建的 proxy。
◼ isReactive
检查对象是否是由 reactive创建的响应式代理:如果该代理是 readonly 建的,但包裹了由 reactive 创建的另一个代理,它也会返回 true;
◼ isReadonly
检查对象是否是由 readonly 创建的只读代理。
◼ toRaw
返回 reactive 或 readonly 代理的原始对象(不建议保留对原始对象的持久引用。请谨慎使用)。从Reactive 或 Ref中得到原始数据, 做一些不想被监听的事情(提升性能)。
let obj = {name:'lnj', age:18};
let state = reactive(obj);
let obj2 = toRaw(state);
console.log(obj === obj2); // true
console.log(obj === state); // false
// state和obj的关系:
// 引用关系, state的本质是一个Proxy对象, 在这个Proxy对象中引用了obj
◼ shallowReactive
创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (深层还是原生对象)。
◼ shallowReadonly
创建一个 proxy,使其自身的 property 为只读,但不执行嵌套对象的深度只读转换(深层还是可读、可写的)。
const info = reactive({
name: "why",
age: 18,
height: 1.88
})
// reactive被解构后会变成普通的值, 失去响应式
const { name, age } = toRefs(info)
//toRefs:把对象里面的所有key全部转化成ref
const height = toRef(info, "height")
//toRef:把对象里面的某个key转化成ref
◼unref
如果我们想要获取一个ref引用中的value,那么也可以通过unref方法:如果参数是一个 ref,则返回内部值,否则返回参数本身; 这是 val = isRef(val) ? val.value : val 的语法糖函数;
◼ isRef
判断值是否是一个ref对象。
◼ shallowRef
创建一个浅层的ref对象;
◼ triggerRef
手动触发和 shallowRef 相关联的副作用
import { reactive, computed, ref } from 'vue'
const names = reactive({
firstName: "kobe",
lastName: "bryant"
})
const fullname1 = computed(() => {
return names.firstName + " " + names.lastName
})
const score = ref(89)
const scoreLevel = computed(() => {
return score.value >= 60 ? "及格": "不及格"
})
我们只需要定义一个ref对象,绑定到元素或者组件的ref属性上即可;
<!-- 1.获取元素 -->
<h2 ref="titleRef">我是标题</h2>
<button ref="btnRef">按钮</button>
<!-- 2.获取组件实例 -->
<show-info ref="showInfoRef"></show-info>
import { ref, onMounted } from 'vue'
import ShowInfo from './ShowInfo.vue'
const titleRef = ref()
const btnRef = ref()
const showInfoRef = ref()
onMounted(() => {
console.log(titleRef.value)
console.log(btnRef.value)
console.log(showInfoRef.value)
showInfoRef.value.showInfoFoo()
})
return {
titleRef,
btnRef,
showInfoRef,
getElements
}
beforeCreate 、created -> setup
以下生命周期都改成一个方法,在setup中使用:
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestory-> onBeforeUnmount
destoryed-> onUnmounted
新增:
onErrorCaptured:每当事件处理程序或生命周期钩子抛出错误时,Vue 会调用该钩子。
onRenderTracked:注册一个钩子,在捕获了后代组件传递的错误时调用。
onRenderTriggered:注册一个调试钩子,当组件渲染过程中追踪到响应式依赖时调用。
<script lang="ts">
import {onBeforeMount,onMounted,onBeforeUpdate,onUpdated,
onBeforeUnmount,onUnmounted,onErrorCaptured,
onRenderTracked,onRenderTriggered } from 'vue'
export default{
name:'App',
setup(){
onBeforeMount(()=>{
console.log('onBeforeMount');
})
onMounted(()=>{
console.log('onMounted');
})
onBeforeUpdate(()=>{
console.log('onBeforeUpdate');
})
onUpdated(()=>{
console.log('onUpdated');
})
onBeforeUnmount(()=>{
console.log('onBeforeUnmount');
})
onUnmounted(()=>{
console.log('onUnmounted');
})
onErrorCaptured((err)=>{
console.log('onErrorCaptured',err);
})
onRenderTracked((event)=>{
console.log('onErrorCaptured',event);
})
onRenderTriggered((event)=>{
console.log('onRenderTriggered',event);
})
}
}
</script>
通过 provide来提供数据,provide可以传入两个参数:
name:提供的属性名称;
value:提供的属性值;
<template>
<show-info></show-info>
</template>
<script>
import { provide, ref } from 'vue'
import ShowInfo from './ShowInfo.vue'
export default {
components: {
ShowInfo
},
setup() {
const name = ref("why")
provide("name", name)
provide("age", 18)
return {
name
}
}
}
</script>
ShowInfo:
<template>
<div>ShowInfo: {{ name }}-{{ age }}-{{ height }} </div>
</template>
<script>
import { inject } from 'vue'
export default {
setup() {
const name = inject("name")
const age = inject("age")
const height = inject("height", 1.88)
return {
name,
age,
height
}
}
}
</script>
watchEffect:用于自动收集响应式数据的依赖;
watch:需要手动指定侦听的数据源。
watch需要侦听特定的数据源,并且执行其回调函数;
默认情况下它是惰性的,只有当被侦听的源发生变化时才会执行回调;
import { reactive, ref, watch } from 'vue'
// 1.定义数据
const message = ref("Hello World")
const info = reactive({
name: "why",
age: 18,
friend: {
name: "kobe"
}
})
// 2.侦听数据的变化
watch(message, (newValue, oldValue) => {
console.log(newValue, oldValue)
})
watch(info, (newValue, oldValue) => {
console.log(newValue, oldValue)
console.log(newValue === oldValue)
}, {
immediate: true
})
// 3.监听reactive数据变化后, 获取普通对象
watch(() => ({ ...info }), (newValue, oldValue) => {
console.log(newValue, oldValue)
}, {
immediate: true,
deep: true
})
Watch可以使用数组侦听多个数据源
const message = ref("Hello World")
const name=ref('naixi')
watch([message,name], (newValue, oldValue) => {
console.log(newValue, oldValue)
})
watchEffect:只要依赖的数据发生变化,就会监听到并执行回调函数。
首先,watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖;
其次,只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行;
如果在发生某些情况下,我们希望停止侦听,这个时候我们可以获取watchEffect的返回值函数,调用该函数即可。
import { watchEffect, ref } from 'vue'
const counter = ref(0)
const name = ref("why")
// 1.watchEffect传入的函数默认会直接被执行
// 2.在执行的过程中, 会自动的收集依赖(依赖哪些响应式的数据)
const stopWatch = watchEffect(() => {
console.log("-------", counter.value, name.value)
// 判断counter.value > 10
if (counter.value >= 10) {
stopWatch() //停止监听
}
})
<template>
<teleport to="#modal">
<div id="center" v-if="isOpen">
<h2><slot>this is a modal</slot></h2>
<button @click="buttonClick">Close</button>
</div>
</teleport>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
props: {
isOpen: Boolean,
},
emits: {
'close-modal': null
},
setup(props, context) {
const buttonClick = () => {
context.emit('close-modal')
}
return {
buttonClick
}
}
})
</script>
<style>
#center {
width: 200px;
height: 200px;
border: 2px solid black;
background: white;
position: fixed;
left: 50%;
top: 50%;
margin-left: -100px;
margin-top: -100px;
}
</style>
调用:
const modalIsOpen = ref(false)
const openModal = () => {
modalIsOpen.value = true
}
const onModalClose = () => {
modalIsOpen.value = false
}
<button @click="openModal">Open Modal</button><br/>
<modal :isOpen="modalIsOpen" @close-modal="onModalClose">
My Modal !!!!</modal>
Suspense 中可以添加多个异步组件,只有default中所有内容加载完成,才会显示。
<Suspense>
<template #default>
<async-show />
<dog-show />
</template>
<template #fallback>
<h1>Loading !...</h1>
</template>
</Suspense>
当使用 setup语法 的时候,任何在script中声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容,包括组件) 都能在模板中直接使用。
<template>
<show-info></show-info>
</template>
<script setup>
// 1.所有编写在顶层中的代码, 都是默认暴露给template可以使用
import { ref, onMounted } from 'vue'
import ShowInfo from './ShowInfo.vue' //组件可以直接使用
// 2.定义响应式数据
const message = ref("Hello World")
console.log(message.value)
// 3.定义绑定的函数
function changeMessage() {
message.value = "你好啊, 李银河!"
}
function infoBtnClick(payload) {
console.log("监听到showInfo内部的点击:", payload)
}
// 4.获取组件实例
const showInfoRef = ref()
onMounted(() => {
showInfoRef.value.foo()
})
</script>
defineProps() 、defineEmits()、defineExpose():
<template>
<div>
<div>ShowInfo: {{ name }}-{{ age }}</div>
<button @click="showInfoBtnClick">showInfoButton</button>
</div>
</template>
<script setup>
// 定义props
const props = defineProps({
name: {
type: String,
default: "默认值"
},
age: {
type: Number,
default: 0
}
})
// 绑定函数, 并且发出事件
const emits = defineEmits(["infoBtnClick"])
function showInfoBtnClick () {
emits("infoBtnClick", "showInfo内部发生了点击")
}
// 定义foo的函数
function foo () {
console.log("foo function")
}
defineExpose({
foo //只有通过defineExpose暴露出去的值,外界调用组件时才能调用到
})
</script>