Changes to VUE3
- Use the Typescript
- Instead of class, use the function-based API (vue2 is new Vue, vue3 is createApp)
- option API => Composition API
- Refactoring complier
- Refactoring virtual DOM
- New responsive mechanisms
Reuse code across components in Vue2
-
Mixins – with
Code mixing is actually a hybrid of design patterns, and its drawbacks are obvious. You can think of it as multiple inheritance, which is basically how does one person have two fathers
The downside: it’s impossible to avoid attribute name conflicts – such as long nose, eyes, and mouth; Inheritance relationship is not clear; Not sure what these mixins are and how they interact
-
Mixin Factory – Mixin Factory
Features: Returns a custom version of a mixin through function, and renames it through namespace when referencing it. Easy code reuse; Inheritance relationship cleaning; Namespaces need strong conventions and rules to do the right thing; We still need to look inside each mixin and see what properties it exposes.
-
ScopeSlots – scope slot
Not very readable
The configuration is complex and needs to be configured in the template
Performance is low, with each slot equivalent to one instance
Mixins with
Why choose CompositionAPI
Limitations of VUE2
- Poor readability due to component logic bloat
- Code cannot be reused across components
- Vue2 has limited support for TS
What problem does CompositionAPI solve
The best solution to vue2’s limitations is to aggregate the logic so that the code is readable.
CompositionAPI is a completely optional syntax that does not conflict with the original OptionAPI. It allows us to organize code for the same function together without having to scatter it all over optionsAPI.
CompositionAPI advantages
- Composition apis organize code according to logical dependencies, improving readability and maintainability
- Less code, better reuse of logical code
- No new syntax introduced, just pure functions
- Abnormal flexible
- Tool syntax prompt friendly, because it is a simple function so easy to implement syntax prompt, automatic compensation
- Better Typescript support
- In complex functional components, code can be organized according to features, and the code has strong cohesion
- Reuse of code between components
Understand CompositionAPI
Refer to the previous animation of the Nuggets:
Review the Option Api
In traditional Options APIS we need to split the logic into the following six parts:
components
props
data
computed
methods
lifecycle methods
With the increasing complexity of the business, the amount of code will continue to increase; As the codes of related businesses need to follow the configuration of option to write to a specific area, the subsequent maintenance is very complex and the code reusability is not high
CompositionAPI
You can organize code and functions more elegantly. Keep the code of related functions organized.
conclusion
Options API | Composition-API |
---|---|
Bad for reuse | Easy code reuse, separation of concerns |
Potential naming conflicts and unclear source of data sources | Clear data source |
Context loss | Provide better context |
Limited type support | betterTypeScriptsupport |
Supported by API type | Organized by function/logic for easy code reuse |
Reactive data must be in the component’sdata Defined in the |
Can be used independently of vUE components |
Take a quick look at some vuE3 code
Vue <template> <h1>Vue2's data option has been replaced by the API ref and reactive </h1> <div>num:{{num}}</div> The < div > refNum: {{refNum}} < / div > < div > state object: name: {{state. The name}}, age: {{state. The age}} < / div > <! -- The state package is no longer required. Properties have been launched return out < div > state object: name: {{name}}, age: {{age}} < / div > -- > < h1 > computed writing have changed < / h1 > < div > {{newCount}} < / div > <div>{{list}}</div> <h1> Watch watchEffect </h1> <div>newCountVal:{{newCountVal}}</div> {{person.name}}, age :{{person.age}}</div> </template> // script import {defineComponent, reactive, ref, toRefs, computed, watch, watchEffect } from "vue" import { useStore } from 'vuex' export default defineComponent({ setup() { const num = 1 // refNum = ref(2) const state = reactive({name: 'yellow ', age: 18}) console.log(state, 'state-- '); // Proxy object console.log(state.name) // yellow console.log(state.age) // 18 // Change data setTimeout(() => {state.age = 20 Console. log(state.age) // => 20}, 1000) // ref can be used directly to update data in the form of. Value, the view does not need. Const count = ref(1) console.log(count, 'count--'); Console. log(count.value) // 1 count.value++ console.log(count.value) // 2 const STR = ref(' yellow ') console.log(str.value) // huang ////// computed const CNT = ref(3) console.log(cnt.value) // 3 const newCount = computed(() => cnt.value + 1) Console. log(newcount.value) // 4 // To obtain the Vuex data from the calculation properties, You can get store instances const store = useStore() const list = computed(() => store.state.list) //// watch using the useStore module provided by Vuex const newCountVal = ref(10) setTimeout(() => { newCountVal.value = 20 }, 2000) watch(newCountVal, (newValue, OldValue) => {console.log(oldValue, newValue) // watch to monitor count changes}) const person = reactive({name: 'top ', age: }) setTimeout(() => {person.name = 'I am reactive '; Person. Age = 22}, 2000) // Watch (() => person. Name, (newValue, OldValue) => {console.log(oldValue) console.log(newValue)}) () => person.age], ([newCount, newName, newAge], [oldCount, oldName, oldAge // ]) => { // console.log(oldCount, oldName, oldAge) // console.log(newCount, newName, newAge) // }) //// watchEffect watchEffect(() => console.log(newCountVal.value, WatchEffect (() => {// console.log(newcountval.value) // console.log(person.name) // }) setTimeout(() => {count.value = 20}, 1000) return {num, refNum, state, //... toRefs(state), newCount, list, newCountVal, person } } })Copy the code
CompositionAPI
setup
What is the
A component option that is executed before creating the component and acts as an entry point to the Composition API. There is no this context in setup.
You can simply think of it as a configuration item for a component, and the value is a function. Data, methods, calculation properties, etc. used by components are configured in setup.
parameter
When you use setup, it takes two arguments:
- Props: Property passed by the component, which is a reactive object and cannot be used
ES6
Deconstruct, same as props in Vue2. X - Context: context object
setup (props,context) { console.log(props, 'props'); console.log(context, 'context'); // context.attrs; //Attributes // context.slots; //slots // context.emit; //tirgger event // context.listeners; // events // context.root; // root component instance // context.parent; // parent component isntance // context.refs; // all refs const {attrs,slots,emit} = context corresponds to the $attr attribute,slot slot, and $emit emit event in vue2. x respectively. And these attributes are automatically synchronized with the latest values, so we get the latest values every time we use them.Copy the code
Context property Description
attrs
: passed from outside the component, but not inprops
Property declared in configuration, proxy for the corresponding item on the internal component instanceslots
: Indicates the received slot content. The proxy for the corresponding item on the internal component instance. The default name isdefault
emit
: a function that distributes custom eventsroot
: An instance of the root component
Execution time
Execute before beforeCreate
export default defineComponent({ beforeCreate() { console.log("----beforeCreate----"); }, created() { console.log("----created----"); }, setup() { console.log("----setup----"); }}); / / the output - the setup - - beforeCreate - - createdCopy the code
Pay attention to
Since setup() is executed before beforeCreate,created, so:
- Not in the
setup()
Function.this
Because the component is not fully instantiated at this point - Not in the
setup()
Function.beforeCreate
withcreated
Two composite life cycles
Lifecycle hook
Lifecycle hooks can be registered using directly imported onX functions that accept a callback function that will be executed when the hook is called by the component:
import { onMounted, onUpdated, onUnmounted } from 'vue' setup() { onMounted(() => { console.log('mounted! ') }) onUpdated(() => { console.log('updated! ') }) onUnmounted(() => { console.log('unmounted! ')})}Copy the code
These lifecycle hook registration functions can only be used synchronously during setup, because they rely on internal global state to locate the currently active instance when its setup() component instance is being called. Calling them without a current active instance results in an error.
The component instance context is also set during synchronous execution of the lifecycle hook, so listeners and calculation properties created synchronously within the lifecycle hook are also automatically deleted when the component is unloaded.
Mapping between Vue2 lifecycle options and Vue3
Option type API | Hook inside setup |
---|---|
beforeCreate | Not needed* |
created | Not needed* |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
errorCaptured | onErrorCaptured |
renderTracked | onRenderTracked |
renderTriggered | onRenderTriggered |
Because setup runs around beforeCreate and Created lifecycle hooks, there is no need to explicitly define them. In other words, any code written in these hooks should be written directly in the Setup function.
Provide / Inject
Provides dependency injection and implements communication between grandparent and grandparent components. Similar to provide and Inject in VUe2, vue3 provides the corresponding Provide and Inject APIS, both of which can only be called in setup() of the currently active component instance. The parent component has a provide option to provide data, and the child component has an Inject option to start using that data. The image below is from the official website:
Dojo.provide usage
Provide takes two parameters. The first parameter is a unique name for provide, preferably Symbol to avoid duplication. The second parameter is the data to expose.
provide(ThemeSymbol, 'dark')
Copy the code
Inject usage
Inject receives two parameters, the first is the provide name and the second is the default data. If the provider does not expose its own data, inject default data
inject(ThemeSymbol, 'light' /* optional default value */)
Copy the code
example
// Provider: Const ThemeSymbol = Symbol() setup() {const themeRef = ref('dark') provide(ThemeSymbol, ThemeRef)} // User setup() {const theme = inject(ThemeSymbol, ref('light'))}Copy the code
getCurrentInstance
Gets the component instance that is currently executing the Setup function. Note that getCurrentInstance can only be executed in setup or a lifecycle hook.
import {getCurrentInstance} from 'vue';
setup(props, ctx){
onMounted(()=>{
const vm = getCurrentInstance();
console.log(vm);
});
}
Copy the code
Changes made by the Composition API
Responsive base API
reactive
Reactive function internal opportunity ES6 Proxy implementation
Reactive receives a normal object and returns a reactive object. In Vue2. X, we only need to define a data in data to make it reactive, whereas in Vue3.0, reactive data is created using reactive functions such as Reactive or ref. Reactive’s responsive data is deep.
example
<template> <div>person object: name :{{person.name}}, age :{{person.age}}</div> </template> <script> import {defineComponent, Reactive, watch} from "vue" export default defineComponent({setup() {const person = reactive({name: 'front-end discovery ', age: 5}) setTimeout(() => {person.name = 'I am reactive', 2000) watch(() => person.name, (newValue, OldValue) => {console.log(oldValue) console.log(newValue)}) return {person}}}) </script> // output result before Person object: name: age :18 After person object: name: I am the name attribute of reactive changed, age :22Copy the code
Pay attention to
-
All setup and composition functions must not return a deconstruction of Reactive, otherwise reactive will be lost, which can be resolved using the toRefsAPI
-
If ref and Reactive are used together, reactVie can be used to redefine the ref object. The original value of the ref object is automatically expanded. Value is no longer used to access its value. Of course, this does not deconstruct the original Ref object
const foo = ref(''); const r = reactive({foo}); r.foo === foo.value; Const foo = ref(''); // But you can't add a ref to a responsive object as a literal; const r = reactive({}); r.foo = foo; / / an errorCopy the code
readonly
Gets an object (reactive or pure) or ref and returns the read-only proxy of the original proxy. A read-only proxy is deep: any nested property accessed is also read-only
const original = reactive({ count: 0}) const copy = Readonly (Original) watchEffect(() => {// For responsiveness tracing console.log(copy.count)}) // Changing original triggers listener dependency replicas Original. Count++ // changing copies will fail and result ina warning copy.count++ // warning!Copy the code
toRaw
Returns the original object of the Reactive or Readonly agent. This is an escape port that can be used for temporary reads without proxy access/trace overhead, or for writes without triggering changes. It is not recommended to preserve persistent references to the original object. Use with caution.
const foo = {}
const reactiveFoo = reactive(foo)
console.log(toRaw(reactiveFoo) === foo) // true
Copy the code
Refs
Ref, toRef, and toRefs
ref
What is ref
- Can be generated
Value types
Reactive data (that is, basic data types) - Can be used for templates and Reactive
- through
.value
To modify the value.value
) - Not only can be used forresponsiveCan also be used for templates
DOM
The element - Reactive is also behind ref
The ref is used to define reactive data and return an object containing reactive data. Typically used to define a primitive data type, but reference types can also be defined.
Ref If the base data type is passed in, it is still done based on get and set of Object.defineProperty(). If the reference type is passed in, the reactive function is called internally, which is a Proxy based on the implementation object.
Look at an example
// template <template> <! - templates don't need to. The value - > < p > {{MSG}} < / p > < p > {{person. Name}} < / p > < button @ click = "handleClick" > change < / button > < / template > <script> import { ref } from 'vue' export default { name: 'App', setup() {// Const MSG = ref(' a message ') // const person = ref({name: 'c ', age: Function handleClick() {MSG. Value = 'new message' person.value. Name = 'new message'} return {MSG, person, handleClick } } } </script>Copy the code
Ref role
- Implementing responsiveness
- Render DOM elements in the template
<template> <p ref="elemRef"> </p> </template> <script> import {ref, onMounted} from 'vue' export default {name: 'RefTemplate', setup(){ const elemRef = ref(null) onMounted(() => { console.log('ref template', Elemref.value.innerhtml, elemref.value)}) return{elemRef}}} </script> // output result ref template is Monday <p> Today is Monday </p>Copy the code
Why do we need ref
- Value typesBasic data types are everywhere, if not used
ref
And the direct return value type,You lose the response - For example, in * *
setup
* *,computed
、Synthetic functionAnd so on, it’s possible to returnValue types Vue
If you don’t defineref
, users will make their ownref
“, which only adds to the confusion
Why does ref need a.value attribute
We know that ref needs to change the value with dot value. It seems to be a hassle, always frequent. Value is particularly trivial and troublesome. So why does it have to be.value? Let’s see why
ref
Is aobject, this object does not lose the responsivity, and this object usesvalue
To store the value- Therefore, through
.value
Properties of theget
andset
To implement the reactive form - Only when usedThe templateandreactiveWhen, no
.value
To achieve the reactive, andAll other cases require it
conclusion
- JS: You need to use the. Value operation object
- Template: automatic unpacking
toRef
What is toRef?
You can create a REF for a property of a Reactive object (on a source reactive object). This REF can be passed and remains responsive
toRef
It’s for a responsive objectreactive
Encapsulated propertiesprop
toRef
If used for ordinary objects (non-reactive objects), the output does not have responsiveness
grammar
ToRef (Object, prop) form to pass the Object name and specific attribute name, to achieve a property data response type effect
For an ordinary object to be reactive, you need to use Reactive. And when you use Reactive, it’s inside the reactive object. So in a reactive object, if one of the attributes is to be taken out for a separate reactive, you use toRef
Look at an example
<template> <h1>toRef</h1> <p>toRef demo - {{ newAgeRef }} - {{ newState.name }} {{ newState.age }}</p> </template> <script> import { ref, reactive, onMounted, toRef, toRefs } from "vue"; export default { name: "Ref", setup(props, context) { //// toRef const newState = reactive({ age: 18, name: "monday", }); // const newState = {// age: 18, // name: 'Monday' //} // Const newAgeRef = toRef(newState, "age"); setTimeout(() => { newState.age = 20; }, 1500); setTimeout(() => { newAgeRef.value = 25; Value Change value}, 3000); //// toRefs const toRefsState = reactive({ age: 18, name: "monday", }); return { newAgeRef, newState, }; }}; </script> // The command output is displayed toRef demo-18-monday 18 toRef demo-20-monday 20 toRef demo-25-monday 25Copy the code
toRefs
What are toRefs?
A reactive object is converted to a normal object, where each property of the resulting object is a REF to the corresponding property of the original object, without loss of responsiveness
- with
toRef
The difference is,toRefs
Is for all properties of the entire object. The goal is to transform a responsive object (reactive
Encapsulate) to a normal object - There is one for every property in a normal object
ref
const state = reactive({ foo: 1, bar: 2 }) const stateAsRefs = toRefs(state) /* Type of stateAsRefs: { foo: Ref<number>, bar: Stateasrefs.foo.value) // 2 stateasrefs.foo.value ++ console.log(state.foo) // 3Copy the code
ToRefs is useful when returning a reactive object from a synthesized function so that the consuming component can decompose/diffuse the returned object without losing responsiveness:
function useFeatureX() { const state = reactive({ foo: 1, bar: 2}) // Logic run state // convert to ref return toRefs(state)} export default {setup() {// Can break structure const {foo without losing responsiveness, bar } = useFeatureX() return { foo, bar } } }Copy the code
Look at an example
<template> <p>toRefs demo {{ age }} {{ name }}</p> </template> <script> import { ref, toRef, toRefs, reactive } from 'vue' export default { name: 'ToRefs', setup() { const newState = reactive({ age: 18, name: "monday", }); const stateAsRefs = toRefs(newState); // Add a responsive object, SetTimeout (() => {console.log('newState before--', newstate. age, newstate.name) newstate. age = 20, Newstate. name = 'Monday' console.log('newState after--', newstate. age, newstate.name) console.log(stateAsRefs, 'stateAsRefs---'); }, 1500); return { ... StateAsRefs}}} </script> // Output result toRefs Demo 18 Monday toRefs Demo 20 MondayCopy the code
conclusion
ToRefs is especially useful in setup or Composition Function returns.
Why toRef and toRefs
Unlike ref, the two brothers toRef and toRefs do not create the reactive form, but continue it. Creating reactive forms is usually handled by Refs or Refs, whereas toRefs and toRefs decompose and diffuse the data of objects that are reactive rather than ordinary objects. To sum up, there are three points:
- The decomposition or diffusion of object data without loss of responsiveness
- In view of theResponsive object(
reactrive
Encapsulated) andNonordinary object- You don’t create responsiveness, you continue responsiveness
conclusion
- with
ref
doValue typesThe response of the type toRef
Create one for the property on the source reactive objectref
. You can then pass the REF out, preserving a responsive join to its source propertytoRefs
Transforms a reactive object into a normal object, where each property of the resulting object points to the corresponding property of the original objectref
- In case of misunderstanding,
ref
Use as many variable names as possiblexxxRef
“, so that it will be clearer when using - Synthetic functionreturnResponsive objectWhen using
toRefs
toRefs
Methods can be deconstructed into multiple componentsRef
Object reference
customRef
define
Create a custom REF with explicit control over its dependency trace and update trigger. It requires a factory function that takes track and trigger functions as arguments and should return an object with get and set.
example
An example of implementing Debounce using a custom ref using a V-Model
<input v-model="text" /> delay = 200) { let timeout return customRef((track, trigger) => { return { get() { track() return value }, set(newValue) { clearTimeout(timeout) timeout = setTimeout(() => { value = newValue trigger() }, delay) } } }) } export default { setup() { return { text: useDebouncedRef('hello') } } }Copy the code
computed
Computed functions are the same as computed in VUe2 in that it receives a function and returns an immutable responsive ref object with a value of the getter.
The difference is that computed is taken into an API and fetched directly from VUE, whereas in Vue2. X, computed is an object in which a computed is defined.
There are two ways to write it
- To accept a
getter
The value is a wrapper object. By default, if the user attempts to modify a read-only wrapper object, a warning will be triggered. Get, not set. - receive
get
andset
That is, pass an object containing the get and set functions
// Getter short form <template> <div>{{newCount}}</div> </template> // script import {computed} from 'vue' setup{const CNT = ref(3) console.log(cnt.value) // 3 const newCount = computed(() => cnt.value + 1) console.log(newCount.value) // 4 Setup {const CNT = ref(3) console.log(cnt.value) // 3 const newCount = computed({ get() { return cnt.value + 1 }, Set (newVal) {// Assign cnt.value = newval-1 console.log(newVal)}}) console.log(newcount.value) // 4 return{newCount}}Copy the code
conclusion
- The value returned by computed is in readonly form and cannot be changed by default
- If computations are wrapped in Reactive, they are automatically unpacked, so the. Value is not used in computations
Watch and watchEffect
watch
The Watch API is exactly equivalent to the option APIthis.$watch (and the corresponding Watch option). Watch needs to listen for specific data sources and side effects in separate callback functions. By default, it is also lazy, meaning that the callback is invoked only when the listening source changes.
grammar
Watch (params, handler(newValue, oldValue), {immediate: true, deep: true})Copy the code
Watch passes in three arguments:
params
: a reactive attribute orgetter
functionhandler
: callback functionobject
: Optional configuration item
The characteristics of
- Has lazy execution and does not execute immediately
- To clarify which dependencies have changed state and trigger the listener to re-execute, it is possible to listen on multiple dependencies
- The ability to obtain the value before and after the state change
- You can stop listening manually
Listen on a single source
The listener data source can be a getter function that returns a value, or a ref
Const state = reactive({count: 0}) watch(() => state.count, (count, prevCount) => {/*... Const count = ref(0) watch(count, (count, prevCount) => {/* prevCount, prevCount, prevCount, prevCount, prevCount, prevCount, prevCount, prevCount, prevCount, prevCount, prevCount, prevCount, prevCount, prevCount) * /})Copy the code
Look at an example
// Listen to a getter setup{const state = reactive({nickname: "xiaofan", age: 20}); setTimeout(() => { state.age++; }, 1000); Watch (() => state.age,(curAge, preAge) => {console.log(" new value :", curAge, "PreAge :", preAge); }); } const year = ref(0); setTimeout(() => { year.value++; }, 1000); Watch (year, (newVal, oldVal) => {console.log(" new value :", newVal, "old value :", oldVal); }); }Copy the code
Listening for multiple sources
Listeners can also listen on multiple sources simultaneously using arrays:
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => {
/* ... */
})
Copy the code
Look at an example
// template <el-input type="text" v-model="name" /> <el-input type="text" v-model. Number ="age" /> // script setup{const name = ref(' 3 ') const age = ref(20) // newValue = oldValue; // watch([name, age], (newValue, oldValue) => { console.log(newValue, '+++++++') // [newName, newAge] console.log(oldValue, '=======') // [oldName, oldAge] }) return { name, Name {{person.name}}</div> <div> Person. age{{person.age}}</div> JobName {{person.job.jobname}}</div> // script setup{reactive({name: 'job.job ', age: 20, job: {jobName: 'engineer ', area: {areaName: 'Shanghai'}}}) setTimeout () = > {person. Name = 'bill' person. Age = 30 person. The job. The jobName = 'designers' console. The log (' -- -- -- -- -- -- -- --'); }, 1000) watch([() => person.name, () => person.age, () => person.job],(newValue, oldValue) => { console.log(newValue, 'newValue--'); console.log(oldValue, 'oldValue--'); },{ deep: true }) return{ person } }Copy the code
Clearance side effect
Why do side effects need to be removed?
In such a scenario, when an asynchronous operation is performed in the Watch, the second watch is triggered before the asynchronous operation is completed, and the last asynchronous operation needs to be cleared at this time.
Watch provides a side effect cleanup function called onCleanup, which takes a function in which side effect cleanup takes place.
When is onCleanup executed?
- Run onCleanup when the watch callback is about to be executed the second time
- When the watch is stopped, that is, after the component is uninstalled
- Watch option (including lazy, deep, flush)
Const getData = (value) => {const handler = setTimeout(() => {console.log(' data ', value)}, 5000) return handler } const inputRef = ref('') watch(inputRef, (val, oldVal, OnCleanup) => {const handler = getData(val) // async operation // onCleanup(() => {clearTimeout(handler)})}) return {inputRef}Copy the code
Stop listening
setup{ const stopWatch = watch('xxxx'); Function (){stop()} stopWatch() return {}; }Copy the code
watchEffect
The watchEffect function does not specify which property to listen for; it listens for whatever property is used in the callback. Run a function immediately when its dependencies are tracked responsively, and rerunce it when the dependency changes. It is executed once by default during initialization.
The characteristics of
- The side effect method is executed immediately. And it is reexecuted when the internally dependent reactive values change
- Dependencies can be collected automatically without specifying listening properties
- Can be achieved by
onInvalidate
Cancel to monitor
Setup {const person = reactive({name, age: 20, job: {jobName, area: {areaName: 'Shanghai'}}}) setTimeout () = > {person. Name = 'bill' person. Age = 30 person. The job. The jobName = 'designers' console. The log (' -- -- -- -- -- -- -- --'); }, 1000) watchEffect(() => { console.log(person.name, 'watchEffect--') console.log(person.job.jobName, 'watchEffect++') onInvalidate(() => { // TODO }) }) }Copy the code
Pay attention to
Note that when a side effect function executes a function that changes the reactive data, it can cause an infinite loop.
conclusion
- WatchEffect does not need to specify a listening attribute. It automatically collects dependencies. Whenever a responsive attribute is referenced in a callback function, the callback will be executed when the attribute changes, whereas Watch can only listen for the specified attribute to change (vue3 can specify multiple attributes at the same time).
- While Watch can get new and old values (pre-update values), watchEffect can’t
- WatchEffect is executed once when the component is initialized to collect dependencies and again when the collected dependencies change. Watch, on the other hand, specifies dependencies directly
methods
Basic usage
/ / template < p > Capacity: {{capacity}}</p> <button @click="increaseCapacity()">Increase Capacity</button> // script setup{ const capacity = ref(3); function increaseCapacity(){ capacity.value++ } return { capacity, increaseCapacity } }Copy the code
Existing problems
Of course, there are some drawbacks to the introduction of Composition apis. Composite apis provide more flexibility in code organization, but they also require more convention (i.e., proper usage) from developers. Composite apis allow people who are not familiar with them to write bar code:
What is noodle code?
The control structure of the code is complex, chaotic, illogical, and relational coupling, which makes it difficult to understand
Why is there a noodle code?
In the Options API, there is actually a mandatory convention:
-
Props to set the receive parameters
-
Set variables in data
-
Set the computing properties in computed
-
Set the listening properties in watch
-
Set event methods in methods
We found that the Options API had already stipulated where we should do what, which forced us to split the code to some extent. Now with the Composition API, you don’t have this convention anymore, so the organization of your code is very flexible, and if you’re new to the Composition API, or if you’re not thinking too deeply, as the logic gets more and more complex, the setup code gets more and more complex, and the returns inside the setup get more and more complex, you’re going to end up with spaghetti code.
How to avoid it?
Without this context, there is no separation of code enforced by the Options API. The Composition API gives us a wider scope, so we need to be careful about making appointments. Keep in mind that the setup() function is now simply an entry point to call all composite functions. In other words, for complex logical code, we should pay more attention to the original intention of Composition API, and use Composition API to separate the code and export it into various modules.
That is, we expect the code to look like this, so that even as the setup content gets bigger and bigger, the code structure is always clear.
import useA from './a';
import useB from './b';
export default {
setup (props) {
let { a, methodsA } = useA();
let { b, methodsB } = useB();
return {
a,
methodsA,
b,
methodsB,
}
}
}
Copy the code
Composition API for logic reuse
The rules
Composition API
指Pull logical code into a function- The naming convention for the function is
useXxxx
format - in
setup
Referenced in theuseXxx
function
Demo
To cite a very classic example: to get the location of the mouse, we use the Composition API to encapsulate the demonstration
// useMousePosition.js import { ref, onMounted, onUnmounted } from 'vue' function useMousePosition() { const x = ref(0) const y = ref(0) function update(e) { x.value = e.pageX y.value = e.pageY } onMounted(() => { console.log('useMousePosition mounted') window.addEventListener('mousemove', update) }) onUnmounted(() => { console.log('useMousePosition unMounted') window.removeEventListener('mousemove', update) }) return { x, Y}} export default {useMousePosition} //. Vue file <template> <p v-if="flag">mouse position {{x}} {{y}}</p> <button @click="changeFlagHandler">change flag</button> </template> <script> import { reactive } from 'vue' import utils from ".. /.. /utils/useMousePosition"; export default { name: 'MousePosition', setup() { const { x, y } = utils.useMousePosition(); let flag = ref(true); console.log(flag, "----"); let changeFlagHandler = () => { flag.value = ! flag.value; }; return { x, y, flag, changeFlagHandler, } } } </script>Copy the code
Reference website:
www.vue3js.cn/docs/zh/api…
Juejin. Cn/post / 689054… (animation)
www.vuemastery.com/ (Vue mastery)