Article Directory
document
Shang Silicon Valley Video: https://www.bilibili.com/video/BV1Zy4y1K7SH?p=136
Official Document: https://cn.vuejs.org/guide/introduction.html
This paper summarizes each section first, and then presents the case.
setup
Summarize
-
Understanding: A new configuration item in vue3.0,
值为一个函数
. -
The setup is the "stage of performance" of all composition APIs.
-
The components used in:
数据、方法等等
must be configured in the setup. -
Two return values of the setup function:
- If
返回一个对象
, then the attributes and methods in the object can be used directly in the template. (Focus! ) - If it returns a rendering function: you can customize the rendering content. (learn)
- If
-
Execution timing: Execute once before beforCreate, at this time this is undefined
-
参数
:props
: The value is an object, which is passed from outside the component, and the internal props of the component declares the received property;context
:context object:- attrs: The value is an object, passed by the parent component, but the received attribute is not declared in the internal props; equivalent to
this.$attrs
- slots: received slot content; equivalent to
this.$slots
- emit: A function that distributes custom events, equivalent to
this.$emit
- attrs: The value is an object, passed by the parent component, but the received attribute is not declared in the internal props; equivalent to
-
important point:
- Try not to mix with Vue2.x configuration
- The properties and methods in the setup can be accessed in the Vue2.x configuration (data, methos, computed...).
- But Vue2.x configuration (data, methos, computed...) cannot be accessed in setup.
- If there are duplicate names, setup takes precedence.
- setup cannot be an async function, because the return value is no longer the return object, but a promise, and the template cannot see the properties in the return object.
- Try not to mix with Vue2.x configuration
basic use
The basic use is as follows: (The following methods are non-responsive, and the reactive method needs to use functions such as ref and reactive)
Full code:
<template>
<h2>name:{
{name}}</h2>
<h2>age:{
{age}}</h2>
<h2>sayHello:<button @click="sayHello">点击在控制台打印</button></h2>
</template>
<script>
import {
ref} from "vue"
export default {
setup(){
//数据
let name = "ljy";
let age = ref(18);
//方法
function sayHello(){
console.log(`你好啊,我是${
name}`)
}
//返回一个对象
return {
name,
age,
sayHello,
}
}
}
</script>
parameter of setup
The complete code is as follows:
parent component App.vue:
<template>
<school msg="欢迎光临" name="张三" @myEvent="myEvent">
<!-- 用于测试子组件接收插槽内容 -->
<template v-slot:slot1>
<span>插槽内容...</span>
</template>
</school>
</template>
<script>
import School from './components/School.vue'
export default {
components: {
School },
beforeCreate() {
console.log("beforeCreate");
},
setup() {
//用于测试setup在beforeCreate之前,且this为undefined
console.log("setup");
return {
//用于子组件触发自定义事件
myEvent() {
alert('触发MyEvent事件')
}
}
}
}
</script>
Subcomponent School.vue
<template>
<button @click="onclick">点我触发myEvent事件</button>
</template>
<script>
export default {
emits:["myEvent"],
props:["msg"],
setup(props,context){
//测试setup的参数:
console.log("props",props);//属性是props接收的
console.log("attrs",context.attrs);//父组件传了,但props没接收的(捡漏)
console.log("slots",context.slots);//父组件定义的插槽信息
console.log("emit",context.emit);//父组件绑定的事件
return{
//点击后,触发父组件中的myEvent事件:
onclick(){
context.emit("myEvent")
}
}
}
}
</script>
<style>
</style>
ref function
Summarize:
-
Role: define a
响应式
data -
grammar:
const xxx = ref(初始值);
引用对象
Create a (reference object, ref object for short) that contains responsive data- Data manipulation in JS:
xxx.value
- Read data in the template: no need to write
.value
,{ { xxx }}
just do it directly
-
Remark:
- The received data can be: basic type, or object type
基本类型
Data: Responsiveness still depends on the get and set of Object.defineProperty()对象类型
data: internally "responses" to a new function in Vue3 – the reactive function
example:
If you want to define a responsive value (for example, age), you can use the following method:
- basic type:
- Object: (actually with the help of reactive function)
Full code:
<template>
<h2>name:{
{name}}</h2>
<h2>age:{
{age}}</h2>
<h2>sayHello:<button @click="sayHello">点击在控制台打印</button></h2>
<h2>incrementAge:<button @click="incrementAge">点击增加age</button></h2>
<h2>info.hobby:{
{info.hobby}}</h2>
<h2>info.address:{
{info.address}}</h2>
<h2>updateInfo:<button @click="updateInfo">点击修改info</button></h2>
</template>
<script>
import {
ref} from "vue"
export default {
setup(){
//数据
let name = "ljy";
let age = ref(18);
let info = ref({
hobby:"电影",
address:"西安"
});
//方法
function sayHello(){
console.log(`你好啊,我是${
name}`)
}
function incrementAge(){
age.value++;
}
function updateInfo(){
info.value.hobby = "vue";
info.value.address = "上海";
}
//返回一个对象
return {
name,
age,
sayHello,
incrementAge,
info,
updateInfo,
}
}
}
</script>
reactive function
Summarize:
- Function: define a
对象类型
responsive data (基本数据类型不要用它
, useref
function) - Syntax:
const 代理对象 = reactive(源对象)
Receive an object (or array), return an代理对象(Proxy对象)
- The reactive data defined by reactive is "
深层次
of" - Internally based on the Proxy implementation in ES6, the internal data of the source object is manipulated through the proxy object
basic use
Full code:
<template>
<h2>info.hobby:{
{info.hobby}}</h2>
<h2>info.address:{
{info.address}}</h2>
<h2>updateInfo:<button @click="updateInfo">点击修改info</button></h2>
</template>
<script>
import {
reactive} from "vue"
export default {
setup(){
//数据
let info = reactive({
hobby:["音乐","视频"],
address:"西安"
});
//尝试修改数据
function updateInfo(){
//改数组
info.hobby[0] = "学习",
//改属性
info.address = "上海"
}
return {
info,
updateInfo,
}
}
}
</script>
computed computed property
Summarize:
- Consistent with the computed configuration function in |vue2
- Writing:
example:
Full code:
<template>
<h2>firstName:<input type="text" v-model="firstName"></h2>
<h2>lastName:<input type="text" v-model="lastName"></h2>
<h2>fullName:{
{ fullName }}</h2>
<h2>set fullName:<input type="text" v-model="setFullName"></h2>
</template>
<script setup>
import {
ref, computed } from "vue";
const firstName = ref("张");
const lastName = ref("三")
//计算属性的简写:
const fullName = computed(() => {
return firstName.value + "-" + lastName.value;
})
//计算属性的get、set
const setFullName = computed({
get() {
return firstName.value + "-" + lastName.value;
},
set(newVal) {
const index = newVal.indexOf("-");
firstName.value = newVal.substring(0, index);
lastName.value = newVal.substring(index + 1);
}
})
</script>
watch listener
Summarize:
- Consistent with the watch configuration function in vue2.x
- Two pits:
- When monitoring the responsive data defined by reactive, ,
oldVal无法正常获取
is forced to open深度监视
(deep configuration invalid) - When monitoring the reactive data defined by reactive
某个属性
,deep配置有效
- When monitoring the responsive data defined by reactive, ,
- Regarding the question of monitoring whether the object defined by ref uses .value:
- Monitoring basic data types does not use .value, because using .value takes out the actual value and cannot be monitored
- To monitor an object, you need to use .value, because at this time .value is a Proxy object generated by reactive; if you do not use .value at this time, you need to configure deep:true
- Monitoring basic data types does not use .value, because using .value takes out the actual value and cannot be monitored
Basic use:
Monitor the reactive data defined by ref:
js:
html:
effect:
Monitor the reactive data defined by reactive:
js:
html:
effect:
Monitor certain properties of reactive data defined by reactive:
js:
html:
effect:
Full code:
<template>
<h3>num1:{
{ num1 }}<button @click="num1++">点我num1++</button></h3>
<h3>num2:{
{ num2 }}<button @click="num2++">点我num2++</button></h3>
<h3>sum:{
{ num1 + num2 }}</h3>
<hr>
<h2>cat:</h2>
<h3>name:{
{ cat.name }}<button @click="cat.name += '~'">点我修改</button></h3>
<h3>age:{
{ cat.age }}<button @click="cat.age++">点我修改</button></h3>
<h3>friend.name:{
{ cat.friend.name }}<button @click="cat.friend.name += '-'">点我修改</button></h3>
<hr />
<h2>person:</h2>
<h3>name:{
{ person.name }}<button @click="person.name += '~'">点我修改</button></h3>
<h3>pass:{
{ person.pass }}<button @click="person.pass += '*'">点我修改</button></h3>
<h3>age:{
{ person.age }}<button @click="person.age++">点我修改</button></h3>
<h3>car.color:{
{ person.car.color }}
<!-- 切换color的颜色: -->
<button @click="person.car.color = (person.car.color == 'red' ? 'blue' : 'red')">点我修改</button>
</h3>
</template>
<script setup>
import {
ref, reactive, watch } from "vue";
//---------------------
//监视ref定义的数据:
var num1 = ref(1);
var num2 = ref(1);
var sum = num1 + num2;
//监视一个:
watch(num1, (newVal, oldVal) => {
console.log(`num1由${
oldVal}变成了${
newVal}`)
})
//监视多个:
watch([num1, num2], (newVal, oldVal) => {
console.log("num1或num2发生了改变:", newVal, oldVal);
sum = newVal[0] + newVal[1];
})
//--------------------------
//监视reactive定义的数据:
var cat = reactive({
name: "tom",
age: 2,
friend: {
name: "tom2"
}
})
//oldVal无效,和newVal相同;deep配置无效;
watch(cat, (newVal, oldVal) => console.log("cat发生变化:", newVal, oldVal));
//---------------------
//监视reactive定义的数据中的某些属性,此时oldVal、deep均有效
var person = reactive({
name: "张三",
pass: "1234",
age: 18,
car: {
color: "red"
}
})
//监视一个
watch(() => person.name, (newVal, oldVal) => {
console.log("person.name发生变化;", newVal, oldVal);
})
//监视多个:
watch([() => person.pass, () => person.age], (newVal, oldVal) => {
console.log("person的pass或age发生变化;", newVal, oldVal);
})
//测试deep是否有效:(一会修改car的color,但监视color)
watch(() => person.car, (newVal) => {
//如果修改person.car.color,该句话不会被打印!
console.log("检测到person.car发生变化(未配置deep)");
})
watch(() => person.car, (newVal) => {
console.log("检测到person.car发生变化(配置deep为true)");
}, {
deep: true
})
</script>
watchEffect monitoring
Summarize:
- The routine of watch is: not only specify the attribute of monitoring, but also specify the callback of monitoring
- The routine of watchEffect is: no need to specify which attribute to monitor, monitor the callback
用到哪个属性,就监视哪个属性
- watchEffect and computed are a bit like:
- computed pays attention to the calculated value (the return value of the callback), so the return value must be written
- watchEffect focuses on the process (the function body of the callback), so there is no need to write the return value
example:
Full code:
<template>
<h3>price:{
{ price }}<button @click="price++">点我price++</button></h3>
<h3>count:{
{ count }}<button @click="count++">点我count++</button></h3>
<h3>total:{
{ total }}</h3>
</template>
<script setup>
import {
ref, watchEffect} from "vue";
//单价
var price = ref(10);
//数目
var count = ref(10);
//总价
var total = ref(0);
//监视:
watchEffect(() => {
total.value = price.value * count.value;
})
</script>
hook function
Summarize:
- The essence is a function that encapsulates the Composition API used in the setup function
- Similar to mixin in vue2.x
- Advantages of custom hooks: reuse code to make the logic in setup clearer and easier to understand
example:
useMouse.js in the hooks directory:
use:
Full code:
useMouse.js:
// mouse.js
import {
ref, onMounted, onUnmounted } from 'vue'
// 按照惯例,组合式函数名以“use”开头
export function useMouse() {
// 被组合式函数封装和管理的状态
const x = ref(0)
const y = ref(0)
// 组合式函数可以随时更改其状态。
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
// 一个组合式函数也可以挂靠在所属组件的生命周期上
// 来启动和卸载副作用
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
// 通过返回值暴露所管理的状态
return {
x, y }
}
app.view:
<template>
<h3>Mouse position is at: {
{ x }}, {
{ y }}</h3>
</template>
<script setup>
import {
useMouse} from "./hooks/useMouse"
const {
x,y} = useMouse();
</script>
toRef and toRefs:
Summarize:
- Function: Create a ref object whose value points to an attribute of another object
- grammar:
const name = toRef(person,'name;)
- Application: To provide a property of the responsive object for external use alone
- Extension: toRefs has the same function as toRef, but multiple ref objects can be created in batches, syntax:
toRefs(person)
, similar to using toRef() for all attributes of a person
example:
Full code:
<template>
<h3>person:{
{ person }}</h3>
<hr>
<h3>pName:{
{ pName }}<button @click="pName += '~'">点击修改</button></h3>
<h3>jobName:{
{ jobName }}<button @click="jobName += '~'">点击修改</button></h3>
<hr />
<h3>name:{
{ name }}<button @click="name += '~'">点击修改</button></h3>
<h3>age:{
{ age }}<button @click="age += '~'">点击修改</button></h3>
<h3>job:{
{ job }}</h3>
</template>
<script setup>
import {
reactive, toRef, toRefs } from "vue"
var person = reactive({
name: "张三",
age: 18,
job: {
name: "开发工程师",
address: "西安"
}
});
//使用toRef定义一个:
var pName = toRef(person, "name");
var jobName = toRef(person.job, "name");
//使用toRefs定义多个
var {
name, age, job } = toRefs(person)
</script>
Other Composition APIs
shallowReactive 与 shallowRef
Summarize:
- shallowReactive: Responsive (shallow responsive) that only deals with the outermost properties of the object
- shallowRef: only handles basic types of responsiveness, not responsive processing of objects
- When to use it?
- If there is an object with relatively deep data, but the knowledge outer attribute changes when it changes, use shallowReactive
- If an object data, the subsequent function will not modify the properties of the object, but generate a new object to replace, then use shallowRef
example:
Full code:
<template>
<h3>person:{
{ person }}</h3>
<h3>pName:{
{ pName }}<button @click="pName += '~'">点击修改</button></h3>
<h3>jobName:{
{ jobName }}<button @click="jobName += '~'">点击修改</button></h3>
<hr>
<h3>
cat name:{
{ cat.name }}
<button @click="cat.name += '~'">点击修改</button>
<button @click="replaceCat">点击替换</button>
</h3>
</template>
<script setup>
import {
shallowReactive, shallowRef, toRef } from "vue"
//-------------------
//使用shallowReactive,则不会监视第二层的job.name
var person = shallowReactive({
name: "张三",
job: {
name: "开发工程师",
}
});
var pName = toRef(person, "name");
var jobName = toRef(person.job, "name");
//-------------------
//使用shallowRef,传的对象不会被reactive加工;只有当cat.value改变,才能被vue监视到
var cat = shallowRef({
name: "tom"
})
//替换一只猫
function replaceCat() {
cat.value = {
name: "new tom"
}
}
</script>
readonly 与 shallowReadonly
Summarize:
- readonly: Make a responsive data read-only (deep read-only)
- shallowReadonly: Make a responsive data read-only (shallow read-only)
- Application scenario: do not want the data to be modified
example:
Full code:
<template>
<h3>person:{
{ person }}</h3>
<h3>pName:{
{ pName }}<button @click="pName += '~'">点击修改</button></h3>
<h3>jobName:{
{ jobName }}<button @click="jobName += '~'">点击修改</button></h3>
<hr>
<h3>person2:{
{ person2 }}</h3>
<h3>pName2:{
{ pName2 }}<button @click="pName2 += '~'">点击修改</button></h3>
<h3>jobName2:{
{ jobName2 }}<button @click="jobName2 += '~'">点击修改</button></h3>
<hr>
</template>
<script setup>
import {
reactive, readonly, shallowReadonly, toRef } from "vue"
//-------------------
//使用readonly,所有数据都不让改!
var person = reactive({
name: "张三",
job: {
name: "开发工程师",
}
});
person = readonly(person)
var pName = toRef(person, "name");
var jobName = toRef(person.job, "name");
//-----------------------
//使用shallowReadonly,只有第一层数据不让改,该例中job.name依然能改!
var person2 = reactive({
name: "李四",
job: {
name: "教师",
}
});
person2 = shallowReadonly(person2)
var pName2 = toRef(person2, "name");
var jobName2 = toRef(person2.job, "name");
</script>
toRaw and markRaw
Summarize:
- toRaw:
- Function: convert a reactive object generated by reactive into a normal object
- Usage scenario: used to read the common object corresponding to the responsive object, all operations on this common object will not cause the page to be updated
- markRaw:
- Role: Mark an object so that it will never become a responsive object again
- Application scenario:
- Some values should not be set as responsive, for example: complex third-party libraries, etc.
- Skipping reactive transitions can improve performance when rendering large lists with immutable data sources
example:
Full code:
<template>
<h3>person: {
{ person }}</h3>
<h3>person name:{
{ person.name }}<button @click="person.name += '~'">点击修改</button></h3>
<hr>
<h3>person toRaw: {
{ pToRaw }}</h3>
<h3>person toRaw 的 name:{
{ pToRaw.name }}<button @click="updatePToRawName">点击修改</button></h3>
<hr>
<div v-if="person.job">
<h3>person.job.name:{
{ person.job.name }}<button @click="updatePersonJobName">点击修改</button></h3>
</div>
</template>
<script setup>
import {
markRaw, reactive, toRaw } from "vue"
var person = reactive({
name: "张三",
});
//使用toRaw,返回指定响应式对象的普通对象,此时,我们普通对象的属性值,不会造成页面的变化
var pToRaw = toRaw(person);
function updatePToRawName() {
pToRaw.name += "-";
console.log(`pToRaw.name 被改成了: ${
pToRaw.name}`);//证明数据确实被改了,只是不是响应式的数据
}
//使用markRaw标记一个对象,使其永远不会作为响应式数据
var job = markRaw({
name:"工程师"
});
person.job = job;//给person添加新属性,如果没使用markRaw,则添加的属性也是响应式的
function updatePersonJobName(){
person.job.name += "-";
console.log(`person.job.name 被改成了: ${
person.job.name}`);//证明数据确实被改了,只是不是响应式的数据
}
</script>
customRef
Summarize:
- Role: Create a custom ref with explicit control over its dependency tracking and update triggering
example:
Full code:
<template>
<input type="text" v-model="name">
<h3>name:{
{ name }}</h3>
</template>
<script setup>
import {
customRef } from "vue"
function myRef(value, delay = 200) {
let timeout;//用于清除定时器
return customRef((track, trigger) => {
return {
//当获取该值时
get() {
track();//追踪value的变化,当value变化时重新解析模板
return value;
},
//当更新该值时,延迟更新模板
set(newValue) {
clearTimeout(timeout);//清除定时器
timeout = setTimeout(() => {
value = newValue;
trigger();//通知vue重新解析模板
}, delay)
}
}
})
}
var name = myRef("张三");
</script>
Provide and Inject
Summarize:
- Role: Realize
祖与后代
communication between components - Routine: the parent component has an
provide
option to provide data, and the descendant component has aninject
option to use the data
example:
-
provide:(App.vue中)
-
inject: (in DeepChild.vue)
-
Effect:
Full code:
- Ancestor: App.vue
<template>
<div>
我是App(祖)
<h3>cat:{
{ cat }}</h3>
<Footer></Footer>
</div>
</template>
<script>
import {
provide, reactive } from "vue"
import Footer from "./components/13/Footer.vue"
export default {
components: {
Footer },
setup() {
var cat = reactive({
name: "tom",
color: "black"
});
//向下层组件提供数据
provide("cat", cat);
return {
cat };
}
}
</script>
<style scoped>
div {
border: 2px red solid;
}
</style>
- Father: Footer.vue
<template>
<div>
我是Footer(父)
<DeepChild></DeepChild>
</div>
</template>
<script>
import DeepChild from "./DeepChild.vue"
export default {
components: {
DeepChild }
}
</script>
<style scoped>
div {
border: 2px red solid;
margin: 10px;
}
</style>
- Son: DeepChild.vue
<template>
<div>
我是DeepChild(孙)
<h3>cat:{
{ cat }}</h3>
</div>
</template>
<script>
import {
inject } from '@vue/runtime-core'
export default {
setup() {
//接收上层组件传过来的值
var cat = inject("cat");
return {
cat };
}
}
</script>
<style scoped>
div {
border: 2px red solid;
margin: 10px;
}
</style>
isXxx
Summarize:
isRef
: Checks if a value is aref
objectisReactive
: Checks if an object wasreactive
created by a reactive proxyisReadonly
: Checks if an object wasreadonly
created by a read-only proxyisProxy
: Checks if an object is a proxy created by thereactive
or methodreadonly
example:
Full code:
<template>
<pre>
<code>
var num = ref(0);
var b1 = isRef(num);
b1:{
{ b1 }}
var person = reactive({name:"张三"});
var b2 = isReactive(person);
b2:{
{ b2 }}
var p = readonly(person);
var b3 = isReadonly(p);
b3:{
{ b3 }}
var b4 = isProxy(person);
var b5 = isProxy(p);
b4:{
{ b4 }}
b5:{
{ b5 }}
</code>
</pre>
</template>
<script setup>
import {
isProxy, isReactive, isReadonly, isRef, reactive, readonly, ref } from 'vue';
var num = ref(0);
var b1 = isRef(num);
var person = reactive({
name: "张三" });
var b2 = isReactive(person);
var p = readonly(person);
var b3 = isReadonly(p);
var b4 = isProxy(person);
var b5 = isProxy(p);
</script>
new components
Fragment
Summarize:
- In Vue2: Components must have a root tag
- In Vue3: components can have no root tags, and multiple tags will be included in a Fragment virtual element internally.
- Benefits: Reduce label levels, reduce memory usage
Teleport
Official website: https://cn.vuejs.org/guide/built-ins/teleport.html
Summarize:
Teleport
组件html结构
is a technology that can move our body to a specified locationto
The value of can be a CSS selector string or a DOM element object. For example:to="body"
the function is to tell Vue "pass the template fragment tobody
the tag".
example:
MyModal.vue
Use in toTeleport
move the component into html'sbody
:
Full code:
- app.vue
<template>
<div class="outer">
<h3>Tooltips with Vue 3 Teleport</h3>
<div>
<MyModal />
</div>
</div>
</template>
<script setup>
import MyModal from './components/15/MyModal.vue';
</script>
- MyModal.vue
<template>
<button @click="open = true">Open Modal</button>
<Teleport to="body">
<div v-if="open" class="modal">
<p>Hello from the modal!</p>
<button @click="open = false">Close</button>
</div>
</Teleport>
</template>
<script setup>
import {
ref } from 'vue'
const open = ref(false)
</script>
<style scoped>
.modal {
position: fixed;
z-index: 999;
top: 20%;
left: 50%;
width: 300px;
margin-left: -150px;
}
</style>
Suspense
Summarize:
-
Render some extra content while waiting for asynchronous components, so that the application has a better experience;
-
Steps for usage:
-
Import components asynchronously
-
Use
Suspense
the package component, and configuredefault
(which component to display) andfallback
(if the component to be displayed is not loaded, the displayed content)
-
example:
- Use
Suspense
tags:
- Adjust the network to slow 3G, when the subcomponent
Child
is not loaded,正在加载...
the words will be displayed
Child
After a period of events, the content in the component is displayed :
this isChild
the content in the child component:
Full code:
- app.vue
<template>
<div class="outer">
<h3>我是父组件!!!</h3>
<Suspense>
<!-- 主要内容 -->
<template v-slot:default>
<Child></Child>
</template>
<!-- 加载中状态 -->
<template v-slot:fallback>
正在加载...
</template>
</Suspense>
</div>
</template>
<script setup>
import {
defineAsyncComponent } from "@vue/runtime-core"
var Child = defineAsyncComponent(() => import("./components/16/Child.vue"))
</script>
- app.vue
<template>
<h3>
我是子组件!!!
</h3>
</template>
Other changes for vue2 -> vu3:
Global API transfer:
other changes