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

  1. 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

  2. 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.

  3. 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’sdataDefined 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 usedES6Deconstruct, 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 inpropsProperty declared in configuration, proxy for the corresponding item on the internal component instance
  • slots: 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 events
  • root: 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 thesetup()Function.thisBecause the component is not fully instantiated at this point
  • Not in thesetup()Function.beforeCreatewithcreatedTwo 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 generatedValue typesReactive data (that is, basic data types)
  • Can be used for templates and Reactive
  • through.valueTo modify the value.value
  • Not only can be used forresponsiveCan also be used for templatesDOMThe 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 usedrefAnd the direct return value type,You lose the response
  • For example, in * *setup* *,computedSynthetic functionAnd so on, it’s possible to returnValue types
  • VueIf 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

  • refIs aobject, this object does not lose the responsivity, and this object usesvalueTo store the value
  • Therefore, through.valueProperties of thegetandsetTo implement the reactive form
  • Only when usedThe templateandreactiveWhen, no.valueTo 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

  • toRefIt’s for a responsive objectreactiveEncapsulated propertiesprop
  • toRefIf 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

  • withtoRefThe difference is,toRefsIs for all properties of the entire object. The goal is to transform a responsive object (reactiveEncapsulate) to a normal object
  • There is one for every property in a normal objectref
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(reactriveEncapsulated) andNonordinary object
  • You don’t create responsiveness, you continue responsiveness
conclusion
  • withrefdoValue typesThe response of the type
  • toRefCreate one for the property on the source reactive objectref. You can then pass the REF out, preserving a responsive join to its source property
  • toRefsTransforms 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,refUse as many variable names as possiblexxxRef“, so that it will be clearer when using
  • Synthetic functionreturnResponsive objectWhen usingtoRefs
  • toRefsMethods can be deconstructed into multiple componentsRefObject 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 agetterThe 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.
  • receivegetandsetThat 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:

  1. params: a reactive attribute orgetterfunction
  2. handler: callback function
  3. object: 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 byonInvalidateCancel 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 APIPull logical code into a function
  • The naming convention for the function isuseXxxxformat
  • insetupReferenced in theuseXxxfunction

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)