Why write this
The reason: Vue + TSX has been used for a large scale migration in the project. Recently, I looked at some relevant use summary to help you understand what Vue 3 has changed, so that you can use Vue 3 more smooth. Here is a comparison of how Vue 3 + Setup Script is written versus Vue 3 + TSX.
This paper mainly refers to:
Vue official document
Vue JSX plugin plugin
1. View layer writing
There are three recommended ways to write Vue 3:
- It still exports a Vue object, with a reactive variable returned by setup
- The setup tag does not need to be returned and can be used directly in the template (recommended).
- Using JSX notation, you need to go through the defineComponent package layer (vant 3.x has been fully embraced)
- The Class component approach (seems to be used sparingly and has not been investigated yet)
<template>
<div class="home">
<! -- Vue automatically deconstructs the ref.value when compiling a template
<div>{{ count }}</div>
<button @click="addCount">+ 1</button>
</div>
</template>
<script lang="ts" setup>
// Use the setup script
// Export default is an instance of vue
// The variables created here can be used directly in the vue source code
import { ref } from "vue";
// Methods are defined directly in setup
// Variables are defined directly in setup
const count = ref(0);
const addCount = () = > (count.value = count.value + 1);
</script>
Copy the code
import { ref, defineComponent } from "vue";
export default defineComponent({
setup() {
const count = ref(0);
// JSX is written the same as non-setup tags because it is defineComponent
const addCount = () = > {
count.value = count.value + 1;
};
return () = > (
<div>{/* react needs to be destructed by count.value */<span>{count.value}</span>
<button onClick={addCount}>+ 1</button>
</div>); }});Copy the code
2. A tip to use the Setup tag notation
Effect: You can dynamically bind CSS variables using V-bind
Implementation: using style and CSS var implementation
<template>
<div class="home">
<div class="color">{{ count }}</div>
<button @click="changeColor">Change the color</button>
</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
const color = ref("red");
const changeColor = () = >
(color.value = color.value === "red" ? "green" : "red");
</script>
<style>
.color {
color: v-bind(color);
}
</style>
Copy the code
In JSX, we can only change the style dynamically
import { ref, defineComponent, computed } from "vue";
export default defineComponent({
setup() {
const count = ref(0);
const color = ref("red");
const changeColor = () = >
(color.value = color.value === "red" ? "green" : "red");
return () = > (
<div>
<div style={{ color: color.value}} >{count.value}</div>
<button onClick={changeColor}>Change the color</button>
</div>); }});Copy the code
3. The computed and watch
3.1 the computed
import { ref, defineComponent, computed } from "vue";
export default defineComponent({
setup() {
const count = ref(0);
const addCount = () = > {
count.value = count.value + 1;
};
// Just use computed as the hook function
const doubleCount = computed(() = > {
return count.value * 2;
});
return () = > (
<div>
<div>{count.value}</div>
<div>{doublecount. value}</div>
<button onClick={addCount}>+ 1</button>
</div>); }});Copy the code
<script lang="ts" setup>
import { ref, computed } from "vue";
const count = ref(0);
const color = ref("red");
const addCount = () = > (count.value = count.value + 1);
const changeColor = () = >
(color.value = color.value === "red" ? "green" : "red");
// Register using computed this function
const doubleCount = computed(() = > {
return count.value * 2;
});
</script>
Copy the code
3.2 watch and watchEffect
As with computed, you can call the watch and watchEffect methods directly
Q1: What is the difference between watch and watchEffect?
A1: Watch can know the difference before and after the change, but watchEffect is a callback after the change as a side effect
Q2: What are the differences between watch and watchEffect implementations?
In this case, the new and old values will be assigned to the callback of watch. In this case, it mainly determines whether the second parameter is a callback. If watchEffect is called, null will be directly passed in the source code
Since there is no difference between JSX and Setup usage, there is only one code
Usage:
import { ref, defineComponent, computed, watch, watchEffect } from "vue";
export default defineComponent({
setup() {
const count = ref(0);
const color = ref("red");
const changeColor = () = >
(color.value = color.value === "red" ? "green" : "red");
// The proxy object to listen on and the callback to invoke
// We can only specify color because we want to operate in the corresponding proxy
// Cannot be bound directly by the execution procedure
watch(color, (newVal, oldVal) = > {
console.log(`color is Change -> ${oldVal} -> ${newVal}`);
});
// The proxy get method can be used to bind
// Because there is no parameter (QAQ, my understanding is HHHH)
watchEffect(() = > {
console.log("current color ->", color.value);
});
return () = > (
<div>
<div style={{ color: color.value}} >{count.value}</div>
<button onClick={changeColor}>Change the color</button>
</div>); }});Copy the code
4. Component values and component references
4.1 Registering components
It can be used in setup and JSX writing directly as an import, without the need to call components separately to register components
Note: If it is a. Vue file, the component must be lowercase, no hump, and then use – to add lowercase letters instead of uppercase
The JSX notation can be introduced as a hump component
4.2 Parent-child Data interaction
The specific operations are the same as those in VUE 2
Parent -> Child: Currently props
Child -> Parent: currently emit in emit mode
4.2.1 JSX
Note:
- The parent component listens in a way from
v-on
– > the JSXonXXX
- < div class ==props: onXXX== ts < div class ==props: onXXX==
import { defineComponent, toRefs } from "vue";
/ / child component
export default defineComponent({
// Define props as in Vue 2
props: {
parentName: {
type: String,},parentAge: {
type: Number,}},setup(props) {
// If the object is of type proxy, each variable must be changed to ref by toRefs
// Officials say that there may be a response loss due to deconstruction
const { parentAge, parentName } = toRefs(props);
return () = > (
<div>
<div>Name: value} {parentName.</div>
<div>Age: value} {parentAge.</div>
</div>); }});Copy the code
/ / the parent component
import { defineComponent } from "vue";
import TestSon from "./test-son";
export default defineComponent({
setup() {
const title = "Parent Node";
return () = > (
<>
<div>{title}</div>
<TestSon parentAge={60} parentName="Taro watermelon" />
</>); }});Copy the code
4.2.2 Vue Setup writing
Note:
- Props needs to be defined through defineProps in Setup
- Emit can be defined at defineEmits
- The parent component listens for events that are the same still through
v-on
You may encounter parameter deconstruction pit!
At present, vUE 3 uses Proxy objects to do in-depth packaging, so the directly deconstructed Proxy may have strange problems (loss of responsiveness). It is suggested to convert Reactive to multiple refs through toRefs, so that the responsiveness can be retained
<! -- Subcomponent -->
<template>
<div>
<div>Name: {{parentName}}</div>
<div>Age: {{parentAge}}</div>
<button @click="sendMsg">Send a message</button>
</div>
</template>
<script lang="ts" setup>
import { defineProps, toRefs, defineEmits } from "vue";
// Accept parameters via defineProps
const props = defineProps({
parentName: {
type: String,},parentAge: {
type: Number,}});// Use defineEmits to introduce emit
const emit = defineEmits();
// Use toRefs to deconstruct the Proxy
const { parentAge, parentName } = toRefs(props);
const sendMsg = () = > {
emit("scream", parentName? .value); };</script>
Copy the code
<! -- Parent component -->
<template>
<div>
<div>{{ title }}</div>
<test-son parentAge="60" parentName="Taro watermelon" @scream="handleScream" />
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import TestSon from "./TestSon.vue";
const title = ref("Parent Node");
const handleScream = (e) = > {
console.log(e);
};
</script>
Copy the code
4.3 Bidirectional Binding Operations
4.3.1 V-Model binding of form components
Both JSX and Setup are written to use v-Models for bidirectional binding of form elements, and this is no different
4.3.2 Customizing V-Model Components
The V-model in VUE 2 and VUE 3 is a break changing
The V-model in VUE 2 is the syntactic sugar for value and update.
In VUE 3, V-model is the syntactic sugar of modelValue and Update :modelValue
4.3.3 JSX writing
Methods used:
- == Parent components directly bind corresponding reactive variables via V-model ==
- Child component directly through
attrs
To get the corresponding boundmodelValue
- == when a child component changes == data
emit
The triggerupdate:modelValue
Events can be
/ / the parent component
import { defineComponent, ref } from "vue";
import TestSon from "./test-son";
export default defineComponent({
setup() {
const fatherVal = ref("father value");
const onParentChange = () = > {
fatherVal.value = `parent -> The ${Math.random().toString()}`;
};
return () = > (
<>
<TestSon v-model={fatherVal.value}/>
<button onClick={onParentChange}>The parent component changes the value</button>
</>); }});Copy the code
import { defineComponent, toRefs } from "vue";
export default defineComponent({
props: {},
setup(props, { emit, attrs }) {
const onSonChange = () = > {
emit("update:modelValue".`son -> The ${Math.random().toString()}`);
};
return () = > (
<div style={{ border: "1px solid #f44}} ">
<div>The value of bidirectional binding: {attrs.modelValue}</div>
<button onClick={onSonChange}>Child component changes</button>
</div>); }});Copy the code
4.3.4 Setup
Note:
Value and update in Vue 2 are replaced by modelValue and uPOdate: modelValue. The overall writing method is basically consistent with JSX
<template>
<div class="father">
<test-son v-model="fatherVal"/>
<button @click="changeFathVal">Change the value</button>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import TestSon from "./TestSon.vue";
const fatherVal = ref("father Value");
const changeFathVal = () = > {
fatherVal.value = `father => The ${Math.random().toString(16)}`;
};
</script>
<style>
.father {
padding: 20px;
border: 1px solid #f44;
}
</style>
Copy the code
<template>
<div class="son">
<div>Parameters for bidirectional binding: {{modelValue}}</div>
<button @click="changeSonVal">Change the child component value</button>
</div>
</template>
<script lang="ts" setup>
import { defineProps, toRefs, defineEmits } from "vue";
const props = defineProps({
modelValue: {
type: String,}});const emit = defineEmits();
const changeSonVal = () = > {
emit("update:modelValue".`son -> The ${Math.random().toString(16)}`);
};
</script>
<style>
.son {
border: 1px solid # 323233;
}
</style>
Copy the code
4.4 v – usage models
4.4.1 JSX writing
What it does: A component binds multiple bidirectionally bound elements at the same time
Note:
- JSX is used
v-models
The labels and2 d array
I think it’s a little anti-human - It is recommended to use attr to avoid undefined parameters in props
// Parent component import {defineComponent, ref} from "vue"; import TestSon from "./test-son"; export default defineComponent({ setup() { const fatherVal = ref("father value"); const model2 = ref("model 2"); const model3 = ref("model3"); const onParentChange = () => { fatherVal.value = `parent -> ${Math.random().toString()}`; model2.value = `parent -> ${Math.random().toString()}`; model3.value = `parent -> ${Math.random().toString()}`; }; return () => ( <> <TestSon v-models={[ [fatherVal.value, "modelValue"], [model2.value, "model2"], [model3.value, <button onClick={onParentChange}> </button> </>); }});Copy the code
/ / child component
import { defineComponent, toRefs, nextTick } from "vue";
export default defineComponent({
props: {},
setup(props, { emit, attrs }) {
const onSonChange = (event: string) = > () = > {
emit(`update:${event}`.`son -> The ${Math.random().toString()}`);
};
return () = > (
<div style={{ border: "1px solid #f44}} ">
<div>ModelValue: {attrs.modelValue}</div>
<div>The value of bidirectional binding model2: {attrs.model2}</div>
<div>The value of bidirectional binding model3: {attrs.model3}</div>
<button onClick={onSonChange("modelValue")} >The child component modelValue changes</button>
<button onClick={onSonChange("model2")} >The child component Model2 changes</button>
<button onClick={onSonChange("model3")} >The child component Model3 changes</button>
</div>); }});Copy the code
4.4.2 setup writing
Pay attention to the point
- use
v-model:xxx
To pass in multiple bidirectional bound variables - Note that the callback function may need to be executed immediately if it is a decorator function
writing
<template>
<div class="father">
<test-son
v-model:modelValue="fatherVal"
v-model:model2="model2"
v-model:model3="model3"
/>
<button @click="changeFathVal">Change the value</button>
</div>
</template>
<script setup lang="ts">
/ / the parent component
import { ref } from "vue";
import TestSon from "./TestSon.vue";
const fatherVal = ref("father Value");
const model2 = ref("model2");
const model3 = ref("model3");
const changeFathVal = () = > {
fatherVal.value = `father => The ${Math.random().toString(16)}`;
model2.value = `father => The ${Math.random().toString(16)}`;
model3.value = `father => The ${Math.random().toString(16)}`;
};
</script>
<style>
.father {
padding: 20px;
border: 1px solid #f44;
}
</style>
Copy the code
<template>
<div class="son">
<div>Bidirectional binding modelValue: {{modelValue}}</div>
<div>Bidirectional binding model2: {{model2}}</div>
<div>Bidirectional binding model3: {{model3}}</div>
<button @click="onSonChange('modelValue')()">Change modelValue value</button>
<button @click="onSonChange('model2')()">Change the model2 value</button>
<button @click="onSonChange('model3')()">Change the model3 value</button>
</div>
</template>
<script lang="ts" setup>
import { toRefs, defineEmits, useAttrs } from "vue";
const emit = defineEmits();
const attrs = useAttrs();
const { modelValue, model2, model3 } = toRefs(attrs);
const onSonChange = (event: string) = > () = > {
emit(`update:${event}`.`son -> The ${Math.random().toString()}`);
};
const onModelValueChange = onSonChange("modelValue");
</script>
<style>
.son {
border: 1px solid # 323233;
}
</style>
Copy the code
5 Subcomponents REF and defineExpose
role
- Methods and data used to expose child components
Different points
-
Vue 2: ref exposes the entire component’s data and methods to the parent component
-
Vue 3 script-setup: You need to expose by defineExpose or you’ll get an empty Proxy
-
JSX: the corresponding vNode of the virtual DOM will be obtained, and the methods exposed in methods will be directly attached to the top layer of the exposed ref
5.1 JSX writing method
/ / the parent component
import { defineComponent, ref } from "vue";
import TestSon from "./test-son";
export default defineComponent({
setup() {
const sonComponent = ref<any>(null);
const onParentCall = () = > {
sonComponent.value.plzParentCall();
};
return () = > (
<>
<TestSon ref={sonComponent}/>
<button onClick={onParentCall}>Parent component call</button>
</>); }});Copy the code
/ / child component
import { defineComponent, toRefs, defineExpose } from "vue";
export default defineComponent({
methods: {
plzParentCall() {
console.log("This is the parent component calling me."); }},setup(props) {
return () = > (
<div style={{ border: "1px solid #f44}} ">Hello World</div>); }});Copy the code
5.2 VUE Setup Label
<! -- Parent component -->
<template>
<div class="father">
<test-son ref="sonRef" />
<button @click="onParentCall">Parent component call</button>
</div>
</template>
<script setup lang="ts">
import { ref, watchEffect } from "vue";
import TestSon from "./TestSon.vue";
const sonRef = ref(null);
const onParentCall = () = > {
sonRef.value.plzParentCall();
};
</script>
<style>
.father {
padding: 20px;
border: 1px solid #f44;
}
</style>
Copy the code
<! -- Subcomponent -->
<template>
<div class="son">
Hello World
</div>
</template>
<script lang="ts" setup>
import { defineExpose } from "vue";
const plzParentCall = () = > {
console.log("This is the parent component calling me.");
};
defineExpose({ plzParentCall });
</script>
<style>
.son {
border: 1px solid # 323233;
}
</style>
Copy the code
6
Pay attention to the point
- The difference between slot and children is that the concept of slot is more flexible than the concept of children when multiple slots need to be inserted
- In JSX, we pass slots as an object and return a render function with a slot context
- The setup script is written in the same way as vue 2
6.1 JSX writing
Pay attention to the point
- Context is passed directly as the == argument to the ==render function
- Slot through
setup
In thecontext
In theslots
Where == corresponds to the slot name ==
/ / the parent component
import { defineComponent, ref, onMounted } from "vue";
import TestSon from "./test-son";
export default defineComponent({
setup() {
const slots = {
title: (title: string) = > <h1>This is the slot of the title,{title}</h1>,
subTitle: (subTitle: string) = > <h2>This is the slot for the subTitle, {subTitle}</h2>};return () = > (
<>
<TestSon v-slots={slots}/>
</>); }});Copy the code
/ / child component
import { defineComponent, toRefs, defineExpose } from "vue";
export default defineComponent({
setup(props, { slots }) {
return () = > (
<div style={{ border: "1px solid #f44}} ">
<div>Header slots: {slots? .title? .(" Subcomponent parameter 1")}</div>
<div>Subtitle slots: {slots? .subTitle? .(" subcomponent parameter 2")}</div>
</div>); }});Copy the code
6.2 Setup Script
Note:
- The context through
v-slot:xxx
To retrieve the corresponding data passed from the child component - The child component inserts the element of the slot with the name of the slot tag
<! -- Parent component -->
<template>
<div class="father">
<test-son>
<template v-slot:title="params">
<h1>This is the slot for the title, {{params.text}}</h1>
</template>
<template v-slot:subTitle="params">
<h2>This is the slot of the subtitle, {{params.text}}</h2>
</template>
</test-son>
</div>
</template>
<script setup lang="ts">
import TestSon from "./TestSon.vue";
</script>
<style>
.father {
padding: 20px;
border: 1px solid #f44;
}
</style>
Copy the code