preface
Learn vite2 + Vue3 + TS cases and knowledge points, from zero to a practical exercise, record the pit children encountered.
Initialize the
Create a Vite2 project from the command line
npm init @vitejs/app
Copy the code
Start the project!
Solution: Change node version >= 12.0.0
My personal solution: NVM use 12
After following the steps to create the project, webStorm did not support Vue3 syntax and frequently reported errors, so VS Code and its plug-ins were installed.
VS Code: Install Volar, disable Vetur, conflict occurs. Quickly call the command line window control +~
When importing files, the shims-vue.d.ts file must declare ts export variables by default for ts import.
The first Demo
code
<template>
<h1>{{msg}}</h1>
<div>Default count: {{state.count}}</div>
<div>Default count: {{state.double}}</div>
<button @click="increment">increase</button>
</template>
<script setup lang="ts">
import {reactive, computed, defineProps} from 'vue'
defineProps({
msg: String
})
type DState = {
count: number;
double: number;
}
const state: DState = reactive({
count: 0.double: computed(() = > state.count * 2)})function increment() {
state.count++
}
</script>
Copy the code
conclusion
- Learn about setup syntax sugar
- Understand defineProps
- Understand the reactive
- Learn about the hook functions of computed Hook
- Understand ts’s statement
Ref and Reactice
code
<template>
<h1>{{msg}}</h1>
---
<div>ref: {{count}}</div>
<div>reactive: {{state.count}} || {{obj.name}}</div>
<div>Structure of the reactive: {{name}} | | {{age}}</div>
</template>
<script setup lang="ts">
import {reactive, computed, defineProps, ref, toRefs} from 'vue'
defineProps({
msg: String
})
// ref syntax sugar
ref: count = 0
// reactive data, count. Value++ is required if no syntax sugar is used
count++
// reactive wraps a separate ref
const state = reactive({
count
})
// Declare reactive object data separately
const obj = reactive({
name: "Greatly".age: 21
})
// Deconstruct the reactice reactive object
const{name, age} = {... toRefs(obj)}</script>
Copy the code
conclusion
- Set reactive data:
ref
和reactive
- Ref: Declares the original data type
- Reactive: Declares complex data types
- The syntax sugar of ref
ref: count = 0
= = =const count = ref(0)
- If responsive data is changed, there is a difference in syntactic sugar
- Reactive statement
- Declare a single responsive object data
- The Reactive pair wraps around the refs, which are then proxies
- Reactive:
. toRefs(obj)
templateref
code
<template>
<h1>{{msg}}</h1>
---
<input type="text" ref="root" value="Ref use example" />
</template>
<script setup lang="ts">
import {defineProps, onMounted, ref} from 'vue'
defineProps({
msg: String
})
// ref points to the corresponding data type based on the incoming data
// If a null is passed, it points to the DOM
const root = ref(null)
// The actual DOM is not available until the first rendering mount is complete
onMounted(() = > {
console.log("ref.null:", root.value);
})
</script>
Copy the code
conclusion
- Understand templateRef: The ref points to the actual DOM
- Ref type: points to the corresponding data type based on incoming data
- The incoming 0:
Ref(number)
- The incoming “” :
Ref(string)
- The incoming null:
Ref(null)
, pointing to a DOM object
- The incoming 0:
- OnMounted Life hook function call
- Called when the first render mount is complete
Ref(null).value
To get the actual DOM object, which is thistemplateRef.
- Called when the first render mount is complete
computed
code
<template>
<h1>{{msg}}</h1>
---
<div>ref: {{count}}</div>
<div>computed: {{plusOne}}</div>
<div>computed(get/set): {{plusTwo}}</div>
</template>
<script setup lang="ts">
import {defineProps, computed} from 'vue'
defineProps({
msg: String
})
// ref syntax sugar
ref: count = 1
ref: count2 = 0
// Calculate attributes
const plusOne = computed(() = > count += 1)
// View the calculated properties generated object
console.log("coumputed:", plusOne);
// get/set resets the calculation properties
const plusTwo = computed({
get: () = > count2 + 2.set: (val) = > count2 = val - 1
})
plusTwo.value = Math.random()
</script>
Copy the code
conclusion
- A computed attribute method wraps responsive data
- Computed generated objects
- read-only
- Generate a new REF
- Dirty value is false
- Computed using the
get/set
To mask
Note: One interesting thing I encountered during case writing is that if I change count += 1 in const plusOne = computed(() => count += 1) to count ++, that is, self-add, the result is ref: 2 and computed: 1.
watch
code
<template>
<h1>{{msg}}</h1>
---
<div>state: {{state.count}}</div>
<div>state2: {{state2.count.count}}</div>
<div>ref1: {{ref1}}</div>
<div>ref2: {{ref2}}</div>
<button @click="add">add</button>
</template>
<script setup lang="ts">
import {defineProps, reactive, watch} from 'vue'
defineProps({
msg: String
})
const state = reactive({
count: 0
})
// Complex responsive objects
const state2 = reactive({
count: {count: 1}})/ / multiple ref
ref: ref1 = 2
ref: ref2 = 3
/** * Parameters: 1. data source, 2. callback, 3. parameter configuration */
watch(() = > state.count, (newValue, oldValue) = > {
console.log("State listening value :", newValue, oldValue);
}, {
immediate: true.// Listen immediately
})
// Listen for deep copy
watch(() = > state2.count, (newValue, oldValue) = > {
console.log("State2 listening value :", newValue.count, oldValue.count);
}, {
deep: true
})
// Listen on multiple refs
watch(() = > [ref1, ref2], ([newRef1, oldRef1], [newRef2, oldRef2]) = > {
console.log('ref1:', newRef1, oldRef1);
console.log('ref1:', newRef2, oldRef2);
})
const add = () = > {
state.count++
state2.count.count++
ref1++
ref2++
}
</script>
Copy the code
conclusion
The official content
The Watch API is exactly equivalent to a component listener property. Watch needs to listen for specific data sources and perform side effects in callback functions. By default, it is also lazy, meaning that callbacks are executed only when the source being listened to changes.
In contrast to watchEffect, Watch allows us to:
-
Lazy execution side effects;
-
Be more specific about what state should trigger the listener to restart;
-
Access values before and after listening state changes.
Note: When watch is used to listen on multiple refs, if the callback function is not used to listen on the array, the warning will be prompted to listen on non-responsive data.
// Listen on multiple refs watch([ref1, ref2], ([newRef1, oldRef1], [newRef2, oldRef2]) = > { console.log('ref1:', newRef1, oldRef1); console.log('ref1:', newRef2, oldRef2); }) Copy the code
Fix it:
// Listen on multiple refs watch(() = > [ref1, ref2], ([newRef1, oldRef1], [newRef2, oldRef2]) = > { console.log('ref1:', newRef1, oldRef1); console.log('ref1:', newRef2, oldRef2); }) Copy the code
Watch and Watcheffect
code
<template>
<h1>{{msg}}</h1>
---
<div>watchEffect: {{num}}</div>
<button @click="add">increase</button>
</template>
<script setup lang="ts">
import {defineProps, ref, watchEffect, onMounted} from 'vue'
defineProps({
msg: String
})
ref: num = 0;
onMounted(() = > {
console.log("onMounted");
})
/** * 1. No need to manually pass in dependencies * 2. Unable to get original value * 4. Suitable for operation asynchronous operation * 5. First argument handling side effects */
const stop = watchEffect((onInvalidate) = > {
console.log("WatcherEffed called before onMounted", num);
// Remove side effects
onInvalidate(() = > {
console.log("Side effects of surveillance."); })}, {// flush: "sync", // call timing
onTrigger(e) { // will be called when a dependency change causes a side effect to be triggered.
console.log('onTrigger', e);
},
onTrack(e) { // will be called when a reactive property or ref is traced as a dependency.
console.log('onTrack', e); }})const add = () = > {
num++
}
// Stop listening
stop()
</script>
Copy the code
conclusion
- WatchEffect: Executes a function passed in immediately, tracing its dependencies responsively, and reruning the function when its dependencies change.
- advantages
- There is no need to pass in dependencies manually
- Instead of lazy, analysis dependencies are performed when initialized
- Unable to get the original value
- Suitable for asynchronous operations
- Side effect refresh timing: In the concrete implementation of the core, the component’s
update
Functions are also a monitored side effect. When a user-defined side effect function is queued, by default, it is queued in all componentsupdate
beforeOf board.- Update the component if necessaryafterRerun the listener side effect and we can pass with
flush
Option attachmentoptions
objectpre
: Before update (default)post
: after the updatesync
: Force effects are always triggered synchronously. However, this is inefficient and is not recommended
- Update the component if necessaryafterRerun the listener side effect and we can pass with
- OnInvalidate: A function that listens for incoming side effects can receive one
onInvalidate
Function to register callbacks for cleanup failures. - Listener debugging: in development mode
onTrack
和onTrigger
Can be used to debug the listener’s behavior.onTrack
Called only when a reactive property or REF is traced as a dependency.onTrigger
Is called when a dependency change causes a side effect to be triggered.
- Explicitly calls the return value to stop listening
stop()
Vue3 life cycle
code
<template>
<h1>{{msg}}</h1>
---
<div>Vue3 life cycle</div>
<div>{{count}}</div>
</template>
<script setup lang="ts">
import {defineProps, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onErrorCaptured} from 'vue'
defineProps({
msg: String
})
ref: count = 1
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(() = > {
console.log('onErrorCaptured');
})
setTimeout(() = > {
count++
}, 100)
</script>
Copy the code
conclusion
-
Comparison between Vue3 and Vue2
Vue2--------------vue3 beforeCreate -> setup() created -> setup() beforeMount -> onBeforeMount mounted -> onMounted beforeUpdate -> onBeforeUpdate updated -> onUpdated beforeDestroy -> onBeforeUnmount destroyed -> onUnmounted activated -> onActivated deactivated -> onDeactivated errorCaptured -> onErrorCaptured Copy the code
-
Usage scenarios
- Setup () : Execute before beforeCreate and Created before starting component creation.
- OnBeforeMount () : Function executed before the component is mounted to the node.
- OnMounted () : a function executed after components are mounted.
- OnBeforeUpdate (): Function executed before component updates.
- OnUpdated (): Function to execute after the component is updated.
- OnBeforeUnmount (): Function executed before component unmounts.
- OnUnmounted (): the function executed after the component is unmounted.
- OnActivated (): The component that is included in this component has two additional lifecycle hook functions. Executed when activated.
- OnDeactivated (): Switches from component A to component B, for example, when component A disappears.
- OnErrorCaptured (): Activates the hook function when an exception is captured from a descendant component.
provide&inject
code
// Parent.vue
<template>
<h1>{{msg}}</h1>
---
<div>provide&inject</div>
<Child />
<input @click="change" type="button" value="Injection">
</template>
<script setup lang="ts">
import {defineProps, ref, provide} from 'vue'
import Child from './Child.vue'
defineProps({
msg: String
})
const themeRef = ref("dark")
provide("test", themeRef)
const change = () = > {
themeRef.value = Math.random().toString()
}
</script>
Copy the code
// Child.vue
<template>
<div>inject: {{theme}}</div>
</template>
<script lang="ts">
import {defineComponent, inject, Ref, watchEffect} from 'vue'
export default defineComponent({
name: "Child".setup() {
const theme = inject("test"."")
watchEffect(() = > {
console.log("Theme modification", (theme as unknown as Ref<string>).value);
})
return {
theme
}
}
})
</script>
Copy the code
conclusion
-
Provide&inject: A parent component can act as a dependency provider for all of its children, no matter how deep the component hierarchy is. This feature has two parts: the parent component has a provide option to provide data, and the child component has an Inject option to start injecting that data.
-
Ref syntax sugar corresponding provide&Inject is not supported
-
ref: theme = "dark" const change = () = > { themeRef = Math.random().toString() } Copy the code
-
expose
code
// Parent.vue
<template>
<h1>{{msg}}</h1>
---
<div>expose</div>
<Child ref="child"/>
<button @click="">Click on the change</button>
</template>
<script setup lang="ts">
import {defineProps, ref, onMounted} from 'vue'
import Child from './Child.vue'
defineProps({
msg: String
})
ref: child = null
onMounted(() = > {
(child asany)? .changeNum() })</script>
Copy the code
<template>
<div>expose: {{num}}</div>
</template>
<script setup lang="ts">
import {useContext, ref} from 'vue'
const {expose} = useContext()
// let num = ref(0)
ref: num = 0
const changeNum = () = > {
num = Math.random()
}
expose({
changeNum
})
</script>
Copy the code
conclusion
- Expose: Vue2 uses ref to expose content and operations on child component instances
- Get the Expose function from the useContext hook function in the child component, and throw a method on the child component instance.
- UseContext replaces setup(props, cox)
- In the parent component through ref, you need to get the DOM instance in the onMounted lifecycle
- Pay attention to the use of time factor
ref(null)
Get THE DOM, so if the content is null, TS will report that there is no DOM instance by default. - Therefore, set the type to
any
或(theme as unknown as Ref<() => {changeNum() {}}>).changeNum()
- Pay attention to the use of time factor
Encapsulate TS request hooks
code
// src/hooks/useApi.ts
import {Ref, ref} from 'vue';
export type ApiRequest = () = > Promise<void>
export interface UsableAPI<T> {
response: Ref<T | undefined>;
requst: ApiRequest;
}
function useApi<T> (url: RequestInfo, options? :RequestInit) {
const response:Ref<T | undefined> = ref();
const request = async() = > {const res = await fetch(url, options);
const data = await res.json();
response.value = data
};
return {
response,
request
}
}
export default useApi
Copy the code
// src/hooks/index.ts
export {useApi} from './useApi'
Copy the code
conclusion
The generic
Learn: TypeScript’s official website
concept
function identity(arg: any) :any {
return arg;
}
Copy the code
Using any causes the function to accept arG arguments of any type, and some information is lost: the type passed in should be the same as the type returned. If we pass in a number, we just know that any type of value can be returned.
Therefore, we need a way to make the type of the return value the same as the type of the parameter passed in. Here, we use a type variable, which is a special variable that only represents a type, not a value.
function identity<T> (arg: T) :T {
return arg;
}
Copy the code
We add the type variable T to identity. T helps us capture the type passed in by the user (such as: number), which we can then use. Then we used T again as the return type. Now we know that the parameter type is the same as the return value type. This allows us to track information about the types used in functions.
We call this version of the Identity function generic because it can be applied to multiple types. Unlike using any, it does not lose information and can maintain accuracy, passing in a numeric type and returning it.
use
Once we have defined a generic function, we can use it in two ways.
The first is to pass in all the arguments, including the type arguments:
let output = identity<string> ("myString"); // type of output will be 'string'
Copy the code
Here we explicitly specify that T is a string and is passed to the function as an argument, enclosed in <> instead of ().
The second method is more common. Makes use of type corollaries — that is, the compiler automatically helps us determine the type of T based on the arguments passed in:
let output = identity("myString"); // type of output will be 'string'
Copy the code
Note that we do not need to use Angle brackets (<>) to pass in the type explicitly; The compiler can look at the value of myString and set T to its type. Type inference helps us keep our code lean and readable. If the compiler cannot automatically infer the type, it can only pass in the type of T explicitly as above, which is possible in some complicated cases.
content
- The wrapper request method useApi, of type generic
<T>
- Response calls vUE
ref
, and the type isRef<undefined>
- Because it’s probably generic, so
Ref<T | undefined>
- Because it’s probably generic, so
- Define interface: UsableAPI
- Requst type type: ApiRequest
- Finally, request and response are returned
Use TS to request hooks
code
- Encapsulates TS interface modules
// src/modules/products.ts
// https://ecomm-products.modus.workers.dev
import { useApi } from "@/hooks";
import {Ref, ref} from 'vue'
export interface Product {
id: string;
title: string;
category: string;
description: string;
images: string[];
variants: Variant[];
price: string;
tags: Tag[];
}
export enum Tag {
Awesome = "Awesome",
Ergonomic = "Ergonomic",
Fantastic = "Fantastic",
Generic = "Generic",
Gorgeous = "Gorgeous",
Handcrafted = "Handcrafted",
Handmade = "Handmade",
Incredible = "Incredible",
Intelligent = "Intelligent",
Licensed = "Licensed",
Practical = "Practical",
Refined = "Refined",
Rustic = "Rustic",
Sleek = "Sleek",
Small = "Small",
Tasty = "Tasty",
Unbranded = "Unbranded",}export interface Variant {
id: string;
quantity: number;
title: string;
sku: string;
}
export type UsableProducts = PromiseThe < {products: Ref<Product[] | undefined> >}export default async function useProducts() :UsableProducts {
const {response: products, request} = useApi<Product[]>('https://ecomm-products.modus.workers.dev')
const loaded = ref(false)
if (loaded.value === false) {
await request();
loaded.value = true
}
return {products};
}
Copy the code
- Child component call
// HelloWorld.vue
<template>
<h1>{{msg}}</h1>
---
<ul>
<li v-for="product in products" :key="product.id">
{{product.title}}
</li>
</ul>
</template>
<script setup lang="ts">
import {defineProps, ref, onMounted} from 'vue'
defineProps({
msg: String
})
import useProducts from '@/modules/products';
const {products} = await useProducts()
</script>
Copy the code
- Parent component call
// App.vue
<template>
<img alt="Vue logo" src="./assets/logo.png"/>
<div>
<Suspense>
<template #default>
<HelloWorld msg="Hello Vue 3 + TypeScript + Vite"/>
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</div>
</template>
<script lang="ts" setup>
import HelloWorld from '@/components/HelloWorld.vue'
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Copy the code
conclusion
- Declaration code to quickly convert to the specified type based on the requested JSON format – “QuickType”
- Use the QuickType tool to automatically generate type declaration code from JSON
- In the code above
modules/products.ts
That’s how it’s generated
- The TS declarations of request interface methods need to be studied, especially with promises.
- If you need to encapsulate a REF, you declare the ref type
- When using the request interface in a component, you can directly
await
, setup syntax sugar support. - If asynchrony is used in component setup, it needs to be nested where the call is made
<Suspense>
The label. Otherwise the content will not load (even if the data is retrieved in the console)
After the speech
The Vue3 + vite2 + ts introductory tutorial, learn since May 2021 update 】 【 Vue3 + TypeScript full series – 2021.06.21 from entry to the practical experience
The code is written by hand. It is also a step by step practice. Originally, I want to do a case study by myself. Let’s just miss Vue for a while.