1, TypeScript quick start
New TypeScript
TypeScript is a superset of JavaScript. It mainly provides type system and support for ES6 +. It is developed by Microsoft and the code is open source GitHub upper
Install TypeScript
Run the following command from the command line to install TypeScript globally:
npm install -g typescript
After installation, run the following command on the console to check whether the installation is successful:
tsc -V
First TypeScript program
- Write TS program
//helloworld.ts function greeter(person){ return 'hello,'+person } let user='tao' console.log(greeter(user))
- Manually compile code
We use the. ts extension, but this code is just JS
On the command line, run the TypeScript compiler:
tsc helloworld.ts
The output result is a helloworld.js file, which contains the same JS code as in the input file
On the command line, run this code through Node.js:
node helloworld.js
Console output:
hello,tao
- vscode auto compile
1)Generate profile tsconfig.json tsc --init 2)modify tsconfig.json to configure "outDir":"./js", "strict":false, 3)Start monitoring task terminal->Run task->monitor tsconfig.json
- type annotation
Add the annotation of string type to the function parameters, as follows:
function greeter(person:string){ return 'hello,'+person } let user='tao' console.log(greeter(user))
Type annotation in TypeScript is a lightweight way to add constraints to functions or variables. In this example, we want the function to accept a string parameter. Then try passing in the array:
function greeter(person:string){ return 'hello,'+person } let user=[0,1,2] console.log(greeter(user))
Recompilation resulted in an error:
error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
- Interface
Use the interface to describe an object with firstName and lastName fields. In TypeScript, if there are only structures inside two types, the two types are compatible. This allows us to implement the interface as long as we ensure that the structure required by the interface is included, without explicitly using the implementation statement
interface Person{ firstName:string, lastName:string } function greeter(person:Person){ return 'hello,'+person.firstName+person.lastName } let user={ firstName:'tao', lastName:'xie' } console.log(greeter(user))
- class
Create a User class with a constructor and some public fields. Because the fields of the class contain the fields required by the interface, they are well compatible
class User { fulName:string, firstName:string, lastName:string constructor(firstName:string,lastName:string){ this.firstName = firstName this.lastName = lastName this.fullName = firstName + ' ' + lastName } } interface Person { firstName: string lastName: string } function greeter (person: Person) { return 'Hello, ' + person.firstName + ' ' + person.lastName } let user = new User('tao', 'xie') console.log(greeter(user))
TypeScript common syntax
Foundation type
- Boolean value
The most basic data type is simple true/false, which is called boolean in JS and TS
let isDone:boolean=false isDone=true //isDone=2 //error
- number
Like JS, all numbers in TS are floating point numbers. The type of these floating-point numbers is number. In addition to supporting decimal and hexadecimal literals, TS also supports binary and octal literals introduced in ECMAScript2015
let a1:number=10 //decimal system let a2:number=0b1010 //Binary let a3:number=0o12 //octal number system let a4:number=0xa //hexadecimal
- character string
Use string to represent text data type. Like JS, you can use double quotation marks "or single quotation marks" to represent strings
let name:string='tom' name='jack' //name=12 //error let age:number=12 const info=`My name is ${name},I am ${age} years old`
- undefined and null
By default, null and undefined are subtypes of all types.
let u:undefined=undefined let u:null=null
- array
//The first way to define an array is to add [] directly after the element type let list1:number[]=[1,2,3] //The second is to define an array let list2:Array<number>=[1,2,3]
- Tuple tuple
Tuple type allows to represent an array with known number and type of elements, and each array element does not have to be the same.
let t1:[string,number] t1=['hello',1] //ok t1=[1,'hello'] //error
When you access an element with a known index, you will get the correct type:
console.log(t1[0].substring(1)) //ok console.log(t1[1].substring(1)) //error number substring method does not exist
- enumeration
enum uses enumeration types to give friendly names to a set of arrays
enum Color { Red, Green, Blue } //Enumeration values are incremented from 0 by default //Get the corresponding enumeration type according to the specific name let myColor:Color = Color.Green //0 console.log(myColor,Color.Red,Color.Blue)
By default, elements are numbered from 0. You can also specify the number of members:
enum Color { Red=1, Green, Blue } let c:Color=Color.Green
Or all manual assignment:
enum Color { Red=1, Green=2, Blue=4 } let c:Color=Color.Green
Enumeration type provides a convenience that its name can be obtained from the value of enumeration:
enum Color { Red=1, Green, Blue } let colorName:string=Color[2] console.log(colorName) //Green
- any
let notSure:any=4 notSure='maybe a string' notSure=false
Any type is very useful when rewriting existing code. It allows you to optionally include or remove checks at compile time. And any type is also useful when you only know the type of some data:
let list:any[]=[1,true,'free'] list[1]=100
- void
To some extent, void type is like the opposite of any type. It means that there is no type. When a function has no return value, you will usually see that its return value type is void:
/*Indicates that there is no type. It is generally used to indicate that the return value of a function cannot be a value other than undefined and null*/ function fn():void{ console.log('fn()') //return undefined //return null //return 1 //error }
It's no use declaring a variable of void type, because you can only give it undefined and null:
let unusable:void=undefined
- object
object represents a non primitive type, that is, a type other than number, string and Boolean
Using object type can better represent API s such as Object.create:
function fn2(obj:object){ console.log('fn2()',obj) return {} // return undefined // return null } console.log(fn2(new String('abc'))) //console.log(fn2('abc')) //error console.log(fn2(String))
- Union type
Union type indicates that the value can be one of multiple types
Requirement 1: define a function to get a string value of a number or string value
function toString2(x:number|string):string{ return x.toString() }
Requirement 2: define a function to get the length of a number or string value
function getLength(x:number|string){ //return x.length //error if(x.length){ //error return x.length }else{ return x.toString().length } }
- Type Asserts
Through type assertion, you can tell the compiler, "trust me, I know what I'm doing". Type assertion is like type conversion in other syntax, but it doesn't carry out special data checking and deconstruction. It has no run-time impact, but works at the compilation stage.
There are two forms of type assertion. One is' angle bracket 'syntax and the other is as syntax
/* Type assertion: can be used to manually specify the type of a value Syntax: Method 1: < type > value Method 2: only this method can be used in the value as type tsx */ /*Requirement: define a function to get the length of a string or numeric data*/ function getLength(x:number|string){ if((<string>x).length){ return (x as string).length }else{ return x.toString().length } } console.log(getLength('abcd'),getLength(1234))
- Type inference
Type inference: TS infers a type when there is no explicit specified type
There are two situations as follows: 1. When defining a variable, the assignment is inferred as the corresponding type; 2. When defining a variable, there is no assignment, and it is inferred as any type
/*When a variable is defined, it is assigned a value and inferred as the corresponding type*/ let b9=123. //number //b9='abc' //error /*The variable is defined without assignment and is inferred as any type*/ let b10 //any b10=123 b10='abc'
Interface
An interface is an abstraction (description) of an object's state (property) and behavior (method)
- Preliminary study on interface
Requirements: to create human objects, you need to restrict human attributes
id yes number Type, must have, read-only name yes string Type, must have age yes number Type, must have sex yes string Type, no
Here is a simple example to see how the interface works:
/* In ts, we use interface s to define the types of objects Interface: it is the abstraction (description) of the state (property) and behavior (method) of an object Object of interface type More or less attributes are not allowed Optional attribute:? Read only attribute: readonly */ /* Requirements: to create human objects, you need to restrict human attributes id It is of type number and must be read-only name Is a string type and must have age Is of type number and must have sex It is a string type and can not be used */ //Define human interface interface IPerson{ id:number, name:string, age:number, sex:string } const person1:Person={ id:1, name:'tom', age:20, sex:'male' }
The type checker will check whether the internal properties of the object are consistent with the description of the Iperson interface. If not, it will prompt the type error
- optional attribute
Not all properties in the interface are required. Some exist only under certain conditions or do not exist
interface IPerson{ id:number, name:string, age:number, sex?:string }
Interfaces with optional attributes are similar to ordinary interface definitions, except that the optional attribute name definition is followed by a "-"
One of the advantages of optional attributes is that they can predefine possible attributes. The other advantage is that they can catch errors when referring to non-existent attributes
const person2:IPerson={ id:1, name:'tom', age:20, //sex: 'male' / / no }
- Read only attribute
Some object properties can only change their values when the object is just created. You can specify a read-only attribute with readonly before the attribute name:
interface IPerson{ readonly id:number, name:string, age:number, sex?:string }
Once assigned, it can no longer be changed
const person2:IPerson={ id:2, name:'tom', age:20, //sex: 'male' / / no //xxx:12. //error is not defined in the interface and cannot exist } person2.id=2 //error
readonly vs const
The simplest way to judge whether to use readonly or const is to use it as a variable or as an attribute. Const is used as a variable and readonly is used as an attribute
- Function type
Interface can describe various shapes of objects in JS. In addition to describing ordinary objects with properties, interfaces can also describe function types.
In order to use an interface to represent a function type, you need to define a call signature for the interface. It's like a function definition with only parameter list and return value type. Each parameter in the parameter list needs a name and type.
/* Interfaces can describe function types (parameter types and return types) */ interface SearchFunc{ (source:string,subString:string):boolean }
After this definition, you can use the interface of this function type like other interfaces. The following example shows how to create a variable of function type and assign a function of the same type to this variable:
const mySearch:SearchFunc=function(source:string,sub:string):boolean{ return source.search(sub)>-1 } console.log(mySearch('abcd','bc'))
- Class type
Class implementation interface
/* Class type: implementation interface 1.A class can implement multiple interfaces 2.An interface can inherit multiple interfaces */ interface Alam{ alert():any } interface Light{ lightOn():void lightOff():void } class Car implements Alam { alert(){ console.log('Car alert') } }
- A class can implement multiple interfaces
class Car2 implements Alam,Light{ alert(){ console.log('Car alert') }, lightOn(){ console.log('Car light on') }, lightOff(){ console.log('Car light off') } }
- Interface inheritance interface
Like classes, interfaces can inherit from each other. Members can be copied from one interface to another, and interfaces can be divided into rewritable modules more flexibly
interface LightableAlarm extends Alam,Light{ }
class
- Basic example:
/* Basic definition and use of class */ class Greeter{ //Declare properties message:string //Construction method constructor(message:string){ this.message=message } //General method greet():string{ return 'hello'+this.message } } //Create an instance of a class const greeter=new Greeter('world') //Call the method of the instance console.log(greeter.greete())
this is used when referring to any class member. It indicates that you are accessing a member of a class.
In the next line, an instance of the Greeter class is constructed using new. It calls the previously defined constructor, creates a new object of type Greeter, and executes the constructor to initialize it.
The last line calls its greet method through the greeter object
- inherit
One of the most basic patterns in class based programming is to allow inheritance to extend existing classes.
/* Class inheritance */ class Animal{ run(distance:number){ console.log(`Animal run ${distance}m`) } } class Dog extends Animal{ cry() console.log('wang! wang!') } } const dog=new Dog() dog.cry() dog.run(100) //Methods inherited from the parent can be called
This example shows the most basic inheritance: the class inherits properties and methods from the base class. Here, Dog is a derived class derived from the Animal base class through the extends keyword. Derived classes are often called subclasses, and base classes are often called superclasses.
Because Dog inherits the function of Animal, we can create an instance of Dog, which can cry() and run()
class Animal { name:string constructor (name;string){ this.name=name } run(distance:number=0){ console.log(`${this.name} run ${distance} m`) } } class Snake extends Animal { constructor(name:string){ //Call the parent type constructor super(name) } //Override method of parent type run(distance:number=5){ console.log('sliding...') super.run(distance) } } class Horse extends Animal { constructor (name:string){ //Call the parent type constructor super(name) } //Override method of parent type run(distance:number=50){ console.log('dashing...') //Call the generic method of the parent type super.run(distance) } xxx(){ console.log('xxx()') } } const snake=new Snake('sn') snake.run() const horse=new Horse('ho') horse.run() //The parent type reference points to the instance of the child type = > const tom:Animal=new Horse('ho22') tom.run() /*If the subtype does not have an extended method, you can make the subtype reference point to the type of the parent type*/ const tom3:Snake=new Animal('tom3') tom3.run() /*If a subtype has an extended method, you cannot make a subtype reference point to an instance of the parent type*/ //const tom2:Horse=new Animal('tom2') //tom3.run()
Using the extends keyword, two subclasses of Animal are created: Horse and Snake. The derived class contains a constructor, which must call super(), which will execute the constructor of the base class. Moreover, before accessing the property of this in the constructor, be sure to call super(). This is an important rule enforced by TS.
This example demonstrates how to override the methods of the parent class in a child class. Both Snake class and Horse class create run methods. They override the run methods inherited from Animal, so that the run methods have different functions according to different classes. Note that even if tom is declared as an Animal type, because its value is Horse, when tom.run(34) is called, it will call the method overridden in Horse.
-
Public, private and protected modifiers
1. The default is public
You can freely access the members defined in the program. In TS, all members are public by default. You can also explicitly mark a member as public.2. Understand private
When a member is marked private, it cannot be accessed outside the class that declares it.3. Understand protected
The protected modifier behaves similar to the private modifier, but with one difference, protected members are still accessible in derived classes.
/* Access modifier: used to describe the accessibility of properties / methods within a class public: By default, exposed external can be accessed private: Only the inside of the class can be accessed protected: Classes and subclasses can access */ class Animal { public name:string public constructor(name:string){ this.name=name } public run(distance:name=0){ console.log(`${this.name} run ${distance}m`) } } class Person extends Animal{ private age:number=18 protected sex:string='male' run (distance:number){ console.log('Perosn jumping...') super.run(distance) } } class Student extends Person{ run(distance:number=6){ console.log('Studnet jumping...') console.log(this.sex) //The child class can see the protected members in the parent class //console.log(this.age) / / the subclass cannot see the private members in the parent class super.run(distance) } } console.log(new Person('abc').name) //Public visibility //console.log(new Person('abc').sex) / / protected invisible //console.log(new Person('abc').age). / / private, invisible
- readonly modifier
You can use the readonly keyword to set the property to read-only. Read only properties must be initialized in declarations or constructors.
class Person{ readonly name:string='abc' constructor(name:string){ this.name=name } } let john=new Person('John') //john.name='peter' //error
Parameter properties
In the above example, you must define a read-only member name and a constructor with a parameter of name in the Person class, and immediately assign the value of name to this.name. This is often encountered. Parameter properties make it easy to define and initialize a member in one place.
class Person2{ constructor (readonly name:string){ } } const p=new Person2('jack') console.log(p.name)
Notice how to discard the parameter name. Only use the readonly name:string parameter in the constructor to create and initialize the name member. Merge declarations and assignments into one place.
Parameter properties are declared by adding an access qualifier in front of constructor parameters. Using private to define a parameter attribute will declare and initialize a private member; The same is true for public and protected.
- Accessor
Typescript supports intercepting access to object members through getters/setters. It can help you effectively control access to object members.
class Person { firstName:string='A' lastName:string='B' get fullName(value){ return this.firstName+'-'+this.lastName } set fullName(value){ const names=value.split('-') this.firstName=name[0] this.lastName=name[1] } } const p=new Person() console.log(p.fullName) p.firstName='C' p.lastName='D' console.log(p.fullName) p.fullName='E-F' console.log(p.firstName,p.lastName)
- Static properties
You can create static members of a class. These properties exist on the class itself rather than on an instance of the class.
/* Static attribute: it is the attribute of class object Non static attribute: it is the attribute of the instance object of the class */ class Person{ name1:string='A' static name2:string='B' } console.log(Person.name2) console.log(new Person().name1)
- abstract class
Abstract classes are used as base classes for other derived classes. They cannot be instantiated. Unlike interfaces, abstract classes can contain implementation details of members. Abstract keyword is a method used to define an abstract class and define an abstraction inside an abstract class.
/* abstract class You cannot create an instance object. Only an implementation class can create an instance Can contain abstract methods that are not implemented */ abstract class Animal{ abstract cry() run(){ console.log('run()') } } class Dog extends Animal{ cry(){ console.log('Dog cry()') } } const dog=new Dog() dog.cry() dog.run()
function
- Basic example
Like JS, TS functions can create named and anonymous functions. You can choose the right way for your application, whether it's defining a series of API functions or using functions only once.
The following examples can quickly recall the two functions of JS:
//Named function function add(x,y){ return x+y } //Anonymous function let myAdd=function(x,y){ return x+y }
- Function type
1. Define type for function
Add type for the above function
function add(x:number,y:number):number{ return x+y } let myAdd=function(x:number,y:number):number{ return x+y }
You can add a type to each parameter before adding a return value type to the function itself. TS can automatically infer the return value type according to the return statement.
- Write complete function type
let myAdd2:(x:number,y:number)=>number=function(x:number,y:number):number{ return x+ y }
- Optional and default parameters
Every function parameter in TS is required. This does not mean that null and undefined cannot be passed as parameters, but that the compiler checks whether the user has passed in a value for each parameter. The compiler also assumes that only these parameters will be passed into the function. In short, the number of parameters passed to a function must be the same as the number of parameters expected by the function.
In JS, each parameter is optional and can be passed or not. When no parameter is passed, its value is undefined. In TS, it can be used next to the parameter name? Realize the function of optional parameters.
function bulidName(firstName:string='A',lastName?:string):string { if(lastName){ return firstName+'-'+lastName } else { return firstName } } console.log(bulidName('C','D')) console.log(bulidName('C')) console.log(bulidName())
- Remaining parameters
Required parameters, default parameters and optional parameters have one thing in common: they represent a parameter. Sometimes, you want to manipulate multiple parameters at the same time, or you don't know how many parameters will be passed in. In JS, you can use arguments to access all the passed in parameters.
In TS, all parameters can be collected into one variable: the remaining parameters will be treated as an unlimited number of optional parameters. You can have none, or you can have any. The compiler creates a parameter array. The name is the name given after the ellipsis (...), which can be used in the function body.
function info(x:string,...args:string[]){ console.log(x,args) } info('abc','c','b','a')
- function overloading
Function overloading: multiple functions with the same function name but different formal parameters
/* Function overloading: multiple functions with the same function name but different formal parameters Requirements: we have an add function, which can receive two string type parameters for splicing and two number type parameters for adding */ //Overloaded function declaration function add(x:string,y:string):string function add(x:number,y:number):number //Define function implementation function add(x:string | number,y:string | number):string | number { //In the implementation, we should strictly judge whether the types of two parameters are equal, rather than simply write an x+y if(typeof x==='string'&&typeof y==='string'){ return x+y }else if(typeof x==='number'&&typeof y==='number'){ return x+y } } console.log(add(1,2)) console.log(add('a'+'b')) //console.log(1,'a') //error
generic paradigm
It refers to a feature that specifies a specific type when using a function, interface or class without specifying a specific type in advance.
- introduce
Next, create a function to realize the function: create an array containing count values according to the specified quantity count and data value. Without generics, this function may be as follows:
function createArray(value:any,count:number):any[]{ const arr:any[]=[] for(let index=0;index<count;index++){ arr.push(value) } return arr } const arr1=createArray(11,3) const arr2=createArray('aa',3) console.log(arr1[0].toFixed(),arr2[0].split(''))
- Using function generics
function createArray2<T>(value:T,count:number){ const arr:Array<T>=[] for(let index=0;index<count;index++){ arr.push(value) } return arr } const arr3=createArray2<number>(11,3) console.log(arr3[0].toFixed()) // console.log(arr3[0].split('')) //error const arr4=createArray2<string>('aa',3) console.log(arr4[0].split('')) //console.log(arr[4].toFixed()) //error
- Functions with multiple generic parameters
//A function can define multiple generic parameters function swap<K,V>(a:K,b:V):[K,V]{ return [a,b] } const result=swap<string,number>('abc',123) console.log(result[0].length,result[1].toFixed())
- generic interface
When defining an interface, define generic types for properties or methods in the interface
When using interfaces, specify specific generic types
interface ibaseCRUD<T>{ data:T[] add:(t:T)=>void getById:(id:number)=>T } class User{ id?:number;//id increases gradually name:string;//full name age:number;//Age constructor(name,age){ this.name=name this.age=age } } class UserCRUD implements IbaseCRUD <User>{ data:User[]=[] add(user:User):void{ user={...user,id:Date.now()} this.data.push(user) console.log('preservation user',user.id) } getById(id:number):User{ return this.data.find(item=>item.id===id) } } const userCRUD=new UserCRUD() UserCRUD.add(new User('tom',12)) UserCRUD.add(new User('tom2',13)) console.log(userCRUD.data)
- Generic class
When defining a class, define generic types for properties or methods in the class, and specify specific generic types when creating instances of the class
class GenericNubmer<T>{ zeroValue:T add:(x:T,y:T)=>T } let myGenericNumber=new GenericNubmer<number>() myGenericNumber.zeroValue=0 myGenericNumber.add=function(x,y){ return x+y } let myGenericString=new GenericNubmer<string>() myGenericString.zeroValue='abc' myGenericString.add=function(x,y){ return x+y } console.log(myGenericString.add(myGenericNumber.zeroValue,'test')) console.log(myGenericNumber.add(myGenericNumber.zeroValue,12))
- Generic constraints
Taking the length attribute directly on a generic parameter will report an error, because the generic does not know whether it has this attribute at all
//No generic constraints function fn<T>(x:T):void{ //console.log(x.length) //error }
This can be done using generic constraints
interface Lengthwise{ length:number } //Specify generic constraints function fn2<T extends Lengthwise>(x:T):void{ console.log(x.length) }
The value conforming to the constraint type needs to be passed in, and the length attribute must be included:
fn2('abc') //fn2(123) //error number has no length attribute
- other
Declaration file
When using a third-party library, you need to reference its life file to obtain the corresponding code completion, interface prompt and other functions
What is life sentence
If you want to use a third-party library jQuery, a common way is to introduce jQuery through the < script > tag in html, and then you can use the global variable $or jQuery
But in ts, the compiler doesn't know what $or jQuery is
/* When using a third-party library, you need to reference its life file to obtain the corresponding functions such as code completion and interface prompt. Declaration statement: if necessary ts, check the new syntax. The corresponding type description code needs to be loaded declare var jQuery:(selector:string)=>any Declaration file: put the declaration statement into a separate file (jQuery.d.ts), and TS will automatically parse all the declaration files in the project Download the declaration file: NPM install @ type / jQuery -- save dev */ jQuery('#foo') //ERROR:Cannot find name 'jQuery'
In this case, you need to use declare var to define its type
declare var jQuery:(selector:string)=>any jQuery('#foo')
The general declaration file will be written as a xxx.d.ts file
Create 01_jQuery.d.ts, where the declaration statement is defined, and the TS compiler will scan and load all TS declaration files in the project
declare var jQuery:(selector:string)=>any
Many third-party libraries define corresponding declaration file libraries. The library file name is generally @ type /xxx, which can be found in https://www.npmjs.com/package/package Search
Some third-party libraries will automatically download the corresponding declaration file library (e.g. webpack) when downloading, and some may need to be downloaded separately (e.g. jQuery/react)
Built in object
There are many built-in objects in JS, which can be directly used as defined types in TS
Built in objects are objects that exist on the global scope according to standards. The standards here refer to ECMAScript and other environment standards (such as DOM)
1. Built in object of ECMAScript
/*1.ECMAScript Built in objects for*/ let b:Boolean=new Boolean(true) let n:Number=new Number(1) let s:String=new String('abc') let d:Date=new Date() let r:RegExp=/^1/ let e:Error=new Error('error message') b=true // let bb:boolean=new Boolean(2) //error
2. Built in objects of BOM and DOM
const div:HTMLELement=document.getElementById('test') const divs:NodeList=document.querySelectorAll('div') document.addEventListener('click',(event:MouseEvent)=>{ console.log(event.target) }) const fragment:DocumentFragment=document.createDocumentFragment()
2, Know vue3
Know vue3
1. Understand relevant information
- Vue3 supports most of the properties of vue2
- Better support for Typescript
2. Performance improvement
- Using Proxy instead of defineProperty to implement data response
- Implementation of rewriting virtual DOM and tree shaking
3. New features
-
Composition API
-
setup
ref and reactive
computed and watch
New lifecycle function
provide and inject
... -
New component
Fragment - document fragment
Teleport - location of teleport components
Suspend - loading interface of asynchronous loading component -
Other API updates
Modification of global API
Transfer the original global API to the application object
Template syntax changes
Create vue3 project
- Create with Vue cli
## Install or upgrade npm install -g @vue/cli ## Ensure that the vue cli version is above 4.5.0 vue --version ## Create project vue create my-project
- Create with vite
npm init vite-app <project-name> cd <project-name> npm install npm run sev
Composition API (common part)
- setup
New option. All composite API functions are used here and are executed only once during initialization
If the function returns an object, the properties or methods in the object can be used in the template - ref
1. Function: define a data response formula
2. Syntax: const xxx=ref(initValue)
Create a reference object that contains responsive data
Operation data in js: xxx.value
Operation data in template:. value is not required
3. It is generally used to define a basic type of responsive data
<template> <h2>{{count}}</h2> <hr> <button @click="update">to update</button> </template>
<script> import {ref} from 'vue' export default{ /*data and methods configuration can still be used in vue3, but its new syntax implementation is recommended*/ //data(){ // return{ // count:0 // } //}, //methods:{ // update(){ // this.count++ // } //} /*Using vue3's composition API*/ setup(){ //Define a responsive data ref object const count=ref(1) console.log(count) //Function to update responsive data function update(){ count.value=count.value+1 } } } </script>
- reactive
1. Function: define the response formula of multiple data
2.const proxy=reactive(obj): accept a common object and return the responsive proxy object of the common object
3. Responsive transformation is "deep": it will affect all nested attributes within the object
4. The internal Proxy implementation based on ES6 operates through the Proxy object, and the internal data of the source object is responsive
<template> <h2>name:{{state.name}}</h2> <h2>age:{{state.age}}</h2> <h2>wife:{{state.wife}}</h2> <hr> <button @click="update">to update</button> </template>
<script> import {reactive} from 'vue' export default{ setup(){ /*Define responsive objects*/ const state=reactive({ name:'tom', age:25, wife:{ name:"marry", age:22 } }) console.log(state,state.wife) const update=()=>{ state.name+='--' state.age+=1 state.wife.name+='++' state.wife.age+=2 } return { state, update } } } </script>
- Compare the responses of vue2 and vue3 (important)
Response of vue2
1. Core:
Object: hijack (monitor / intercept) the reading and modification of the existing property value of the object through defineProperty
Array: a series of methods to update elements by rewriting the array to update the array
Object.defineProperty(data,'count',{ get(){}, set(){} })
2. Problems
Object directly adds new attributes or deletes existing attributes. The interface will not be updated automatically
Directly replace the element or update the length through the subscript, and the interface will not automatically update arr[1] = {}
Response of vue3
1. Core:
Through proxy: intercept any (13) operations on any attribute of data, including reading and writing attribute values, adding attributes, deleting attributes, etc
Reflect: dynamically perform specific operations on the corresponding attributes of the proxied object
new Proxy(data,{ //Intercept reading attribute values get(target,prop){ return Reflect.get(target,prop) } //Intercept setting property values or adding new properties set(target,prop,value){ return Reflect.set(target,prop,value) } //Block delete attribute deleteProperty(target,prop){ return Reflect.deleteProperty(target,prop) } }) proxy.name="tom"
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Proxy And Reflect</title> </head> <body> <script> const user = { name: "John", age: 12 }; /* proxyUser User is the proxy object and user is the proxy object All the latter operations operate the internal properties of the proxy object through the proxy object */ const proxyUser = new Proxy(user, { get(target, prop) { console.log('hijack get()', prop) return Reflect.get(target, prop) }, set(target, prop, val) { console.log('hijack set()', prop, val) return Reflect.set(target, prop, val); // (2) }, deleteProperty (target, prop) { console.log('hijack delete attribute', prop) return Reflect.deleteProperty(target, prop) } }); // Read attribute value console.log(proxyUser===user) console.log(proxyUser.name, proxyUser.age) // Set attribute value proxyUser.name = 'bob' proxyUser.age = 13 console.log(user) // Add attribute proxyUser.sex = 'male' console.log(user) // Delete attribute delete proxyUser.sex console.log(user) </script> </body> </html>
- setup
1. Timing of setup execution
1) Execute (once) before beforeCreate, and the component object has not been created yet;
2) This is undefined. data/computed/methods/props cannot be accessed through this;
3) In fact, it is not allowed in all callback functions related to composition API;
2. Return value of setup
1) Generally, an object is returned: provide data for the template, that is, all properties / methods in this object can be directly used in the template
2) The properties in the return object will be merged with the data function return object to form the properties of the component object
3) The method in the returned object will be merged with the method in methods, and the method of the component object will be merged successfully
4) If there are duplicate names, setup takes precedence
5) Note: generally, do not mix them: you can access the properties and methods provided by setup in methods, but you cannot access data and methods in setup method; Setup cannot be an async function: because the return value is no longer the return object, not promise, the template cannot see the attribute data in the return object
3.setup parameters
1)setup(props,context)/setup(props,{attrs,slots,emit})
2) Props: an object that contains the props configuration declaration and passes in all properties
3) Attrs: object containing attributes not declared in props configuration, equivalent to this.$attrs
4) Slots: an object containing all the incoming slot contents, equivalent to this.$slots
5) Emit: a function used to distribute custom events, equivalent to this.$emit
<template> <h2>App</h2> <p>msg: {{msg}}</p> <button @click="fn('--')">to update</button> <child :msg="msg" msg2="cba" @fn="fn"/> </template>
<script lang="ts"> import { reactive, ref, } from 'vue' import child from './child.vue' export default { components: { child }, setup () { const msg = ref('abc') function fn (content: string) { msg.value += content } return { msg, fn } } } </script>
<template> <div> <h3>{{n}}</h3> <h3>{{m}}</h3> <h3>msg: {{msg}}</h3> <h3>msg2: {{$attrs.msg2}}</h3> <slot name="xxx"></slot> <button @click="update">to update</button> </div> </template>
<script lang="ts"> import { ref, defineComponent } from 'vue' export default defineComponent({ name: 'child', props: ['msg'], emits: ['fn'], // Optionally, the declaration is easier for programmers to read, and the distributed event data can be verified data () { console.log('data', this) return { // n: 1 } }, beforeCreate () { console.log('beforeCreate', this) }, methods: { // update () { // this.n++ // this.m++ // } }, // setup (props, context) { setup (props, {attrs, emit, slots}) { console.log('setup', this) console.log(props.msg, attrs.msg2, slots, emit) const m = ref(2) const n = ref(3) function update () { // console.log('--', this) // this.n += 2 // this.m += 2 m.value += 2 n.value += 2 // Distribute custom events emit('fn', '++') } return { m, n, update, } }, }) </script>
- reactive and ref details
1) It is the most important responsive API in vue3's composition API
2) ref is used to process basic types of data, and reactive is used to process objects (recursive deep response)
3) If you use ref object / array, the object / array will be automatically converted into a reactive proxy object
4) ref internal: hijack the data by adding getter/setter to the value attribute
5) reactive internal: hijack all data inside the object by using Proxy, and operate the internal data of the object through Reflect
6) ref data operation: you need. value in js, but not in the template (the. value will be added automatically when the template is internally parsed)
<template> <h2>App</h2> <p>m1: {{m1}}</p> <p>m2: {{m2}}</p> <p>m3: {{m3}}</p> <button @click="update">to update</button> </template>
<script lang="ts"> import { reactive, ref } from 'vue' export default { setup () { const m1 = ref('abc') const m2 = reactive({x: 1, y: {z: 'abc'}}) // Using ref to process objects = = > objects will be automatically reactivated as proxy objects const m3 = ref({a1: 2, a2: {a3: 'abc'}}) console.log(m1, m2, m3) console.log(m3.value.a2) // It is also a proxy object function update() { m1.value += '--' m2.x += 1 m2.y.z += '++' m3.value = {a1: 3, a2: {a3: 'abc---'}} m3.value.a2.a3 += '==' // reactive depth data hijacking of the object console.log(m3.value.a2) } return { m1, m2, m3, update } } } </script>
- Computing properties and monitoring
<template> <h2>App</h2> fistName: <input v-model="user.firstName"/><br> lastName: <input v-model="user.lastName"/><br> fullName1: <input v-model="fullName1"/><br> fullName2: <input v-model="fullName2"><br> fullName3: <input v-model="fullName3"><br> </template>
<script lang="ts"> /* Computing properties and monitoring 1. computed Function: Consistent with the function of computed configuration Only Getters There are getter s and setter s 2. watch function Consistent with the watch configuration function Monitor one or more specified responsive data. Once the data changes, the monitoring callback will be executed automatically By default, the callback is not executed initially, but you can specify that the first callback is executed immediately at the initial time by configuring immediate to true Specify deep monitoring by configuring deep to true 3. watchEffect function Instead of directly specifying the data to be monitored, the responsive data used in the callback function can be monitored By default, the first time is executed initially, so that the data to be monitored can be collected Callback when monitoring data changes */ import { reactive, ref, computed, watch, watchEffect } from 'vue' export default { setup () { const user = reactive({ firstName: 'A', lastName: 'B' }) // Only the calculated property of getter const fullName1 = computed(() => { console.log('fullName1') return user.firstName + '-' + user.lastName }) // Calculation properties with getter and setter const fullName2 = computed({ get () { console.log('fullName2 get') return user.firstName + '-' + user.lastName }, set (value: string) { console.log('fullName2 set') const names = value.split('-') user.firstName = names[0] user.lastName = names[1] } }) const fullName3 = ref('') /* watchEffect: Monitor data used in all callbacks */ /* watchEffect(() => { console.log('watchEffect') fullName3.value = user.firstName + '-' + user.lastName }) */ /* Two features of watch are used: Depth monitoring Initialization is executed immediately */ watch(user, () => { fullName3.value = user.firstName + '-' + user.lastName }, { immediate: true, // Whether to initialize and execute immediately. The default is false deep: true, // Whether it is depth monitoring. The default is false }) /* watch One data By default, the callback is executed when the data changes */ watch(fullName3, (value) => { console.log('watch') const names = value.split('-') user.firstName = names[0] user.lastName = names[1] }) /* watch Multiple data: Use an array to specify If it is a ref object, specify it directly If it is an attribute in a reactive object, it must be specified through a function */ watch([() => user.firstName, () => user.lastName, fullName3], (values) => { console.log('Monitor multiple data', values) }) return { user, fullName1, fullName2, fullName3 } } } </script>
-
life cycle
Composite API corresponding to version 2.x lifecycle
. Beforecreate = > use setup()
. Create = > use setup()
. beforeMount=>onBeforeMount
. mounted=>onMounted
. beforeUpdate=>onBeforeUpdate
. updated=>onUpdated
. beforeDestroy=>onBeforeUnmount
. destroyed=>onUnmounted
. errorCaptured=>onErrorCaptured -
toRefs
Convert a responsive object into an ordinary object, and each property of the ordinary object is a ref
Application: toRefs is very useful when returning a responsive object from a composite function, so that the consumer component can decompose and use the returned object without losing the responsiveness
Problem: all attribute values retrieved by the reactive object are non responsive
Solution: using toRefs, you can convert all the original attributes of a reactive object into a reactive ref attribute
<template> <h2>App</h2> <h3>foo: {{foo}}</h3> <h3>bar: {{bar}}</h3> <h3>foo2: {{foo2}}</h3> <h3>bar2: {{bar2}}</h3> </template>
<script lang="ts"> import { reactive, toRefs } from 'vue' /* toRefs: Wrap all the attributes in the responsive object as ref objects and return normal objects containing these ref objects Application: toRefs is very useful when returning a responsive object from a composite function, In this way, the consumer component can decompose the returned object without losing the response */ export default { setup () { const state = reactive({ foo: 'a', bar: 'b', }) const stateAsRefs = toRefs(state) setTimeout(() => { state.foo += '++' state.bar += '++' }, 2000); const {foo2, bar2} = useReatureX() return { // ...state, ...stateAsRefs, foo2, bar2 } }, } function useReatureX() { const state = reactive({ foo2: 'a', bar2: 'b', }) setTimeout(() => { state.foo2 += '++' state.bar2 += '++' }, 2000); return toRefs(state) } </script>
- ref get element
Use the ref function to get the label element in the component
Functional requirements: let the input box automatically get the focus
<template> <h2>App</h2> <input type="text">--- <input type="text" ref="inputRef"> </template>
<script lang="ts"> import { onMounted, ref } from 'vue' /* ref Get element: use the ref function to get the label element in the component Functional requirements: let the input box automatically get the focus */ export default { setup() { const inputRef = ref<HTMLElement|null>(null) onMounted(() => { inputRef.value && inputRef.value.focus() }) return { inputRef } }, } </script>