I have done many VUE3 projects before. This time, I will systematically learn VUE3 through teacher Coderwhy’s video.

Project 1: Copy Zhihu project github.com/zhang-glitc…

Project 2: Data Large Screen Project: github.com/zhang-glitc…

Project 3: My own personal blog: github.com/zhang-glitc…

How do YOU use Vue?

Vue, at its core, is a JavaScript library.

  • Method 1: Introduce it through CDN on the page;

  • Method 2: Download Vue JavaScript files, and manually import;

  • Method 3: Use the NPM package management tool to install it.

  • Method 4: Create a project directly through the Vue CLI and use it;

For simple use we can call vue.createApp () to create an application instance and mount it to the specified DOM via mount.

  <div id="app"></div>

  <script src="https://unpkg.com/vue@next"></script>
  <script>
    const options = {
      template: '<h2>Hello Vue3</h2>'
    }

    const app = Vue.createApp(options);
    app.mount("#app")
  </script>
Copy the code

Template syntax

React development mode:

  • React uses JSX, so the corresponding code is written in a syntax similar to JS.

  • JSX is then compiled with Babel into the react. createElement function call;

Vue also supports the JSX development mode:

  • But for the most part, htML-based template syntax is used;

  • Templates allow developers to declaratively bind DOM data to the underlying component instance;

  • In the underlying implementation, Vue compiles the template into a virtual DOM rendering function.

Mustache double brace syntax

If we want to display data to a template, the most used syntax is “Mustache” (double brace) text interpolation.

  • And as we mentioned earlier, the object returned by data is added to the responsive system of Vue;

  • When the data in data changes, the corresponding content is also updated.

  • Of course Mustache can be more than just a property in data, it can also be a JavaScript expression.

instruction

Render content related instructions

  • V-once is used to specify that an element or component is rendered only once:

    • When data changes, the element or component and all its children are treated as static and skipped;

    • This directive can be used for performance optimization;

    • If it is a child node, it will only be rendered once. Even tags that use V-once are rendered only once.

  • V-text is used to update the element’s textContent

    • Is equivalent to{{}}.
    • And it overwrites everything in the tag.
  • V-html is used to render AN HTML string to a page as HTML. This directive is often used to render articles on personal blogs.

    • When we present HTML strings through {{}}, VUE does no special parsing of them. Still render as AN HTML string.

    • If we want this content to be parsed by Vue, we can display it using V-HTML;

  • The V-pre is used to skip the compilation of the element and its children, displaying the original Mustache tag:

    • Skip nodes that do not need to be compiled to speed up compilation.

Attribute dependent instruction

  • v-bindDynamically bind one or more attributes, or a component prop, to an expression.
    • abbreviations:
    • The modifier
      • .camelConvert the kebab-case attribute name to camelCase.
      • .prop– Enforces a binding to a DOM property.
      • .attr– Forces a binding to be a DOM attribute.
    • There are two ways to bind a class:
      • Object syntax: Pass an object as the value of class. Key is the class attribute value, and value is a Boolean to see if the key is bound to the element’s class.

      If we want to use variables defined in Data as class values, we need to use dynamic property binding[]The title below is treated as a variable, not a string.

       <div :class="{active: isActive, [title]: true}"> Add object formclass</div>
      Copy the code
      • Array syntax: Pass an array as the value of class. Each element in the array is the class attribute value. If the element is an expression or an object, check to see if its value is true. Is added to class.

      Note that if the element value is not quoted, it will look for the variable value in the defined data. For example, the title below

      <div :class="['abc', title, isActive ? 'active': '', {active: isActive}]"> add the array formclass
      </div>
      Copy the code
    • Binding style: Certain styles need to be determined dynamically based on the data.
      • CSS property names can be either camelCase or kebab-case delimited (remember to use quotes)
      • Object syntax: Equivalent to writing inline CSS styles. It just has to be a string. If it is not a string, it is treated as a variable, and data is checked to see if it exists.
          <div :style="{color: finalColor, 'font-size': '30px'}"> Object form </div>Copy the code
      • Array syntax: almost no. The style object of the key-value pair is placed in an array as an element.
          <div :style="[style1Obj, style2Obj]"> Array form </div>data() {
             return {
               message: "Hello World".style1Obj: {
                 color: 'red'.fontSize: '30px'
               },
               style2Obj: {
                 textDecoration: "underline"}}}Copy the code
    • Dynamically bind custom properties. through:[Custom attributes]In the form.
        <div :[name]="value"> Dynamically bind custom attributes </div>data() {
           return {
             name: "cba".value: "kobe"}}Copy the code
    • Map object data to attributes of dom elements. This is generally used for theinheritAttrs: falseMounts the non-props attribute passed in by the parent element to the specified DOM. v-bind="$attrs"
        // The info here will be written to div as individual div attributes
       <div v-bind="info"> map object data to dom element attributes </div><div :="info">Map object data to attributes of dom elements</div>
       data() {
           return {
             info: {
               name: "zh".age: 20}}}Copy the code

Incident command

  • v-on: used to bind event listeners.
    • Abbreviations:@
    • The modifier
      • .stop – Call event.stopPropagation().

      • .prevent – Call event.preventdefault ().

      • .capture – Capture mode is used when adding event listeners.

      • .self – The callback is triggered only if the event is triggered from the listener bound element itself.

      • .{keyAlias}- The callback is triggered only if the event is triggered from a specific key.

      • .once – Only one callback is triggered.

      • .lef – Triggered only when the left mouse button is clicked.

      • .right – Triggered only when the right mouse button is clicked.

      • .middle – Triggered only when the middle mouse button is clicked.

      • . Passive – {passive: true} Mode Add a listener

    • Development usually involves binding a function, but if we need to bind multiple functions, we need to pass in an object.
       <div  v-on="{click: btn1Click, mousemove: mouseMove}"></div>
       <div@ ="{click: btn1Click, mousemove: mouseMove}"></div>
    Copy the code
    • When defining a method from methods for @click to call, you need to be aware of parameter issues:

      • Case 1: If the method does not require additional arguments, the () after the method may not be added.

      Note, however, that if the method itself has an argument, the native event argument is passed in by default

      • Case 2: If you need to pass in both a parameter and an event, pass$eventIncoming events.

Conditional render related instructions

  • v-if
    • V minus if is inert.
    • When the condition is false, the judged content will not be rendered at all or will be destroyed.
    • When the condition is true, the content in the conditional block is actually rendered.
    • If we want more than one DOM to be shown or hidden at the same time, we can write v-if intemplateTag, and wrap it around the multiple DOM elements.
  • v-else(Used with V-IF)
  • v-else-if(Used with V-IF)
  • v-show
    • V-show cannot be added intemplateOn the label
    • V-show cannot be used with v-else.
    • The essence is to set up CSSdisplayTo show or hide the element.

List render instruction

  • v-for
    • It can iterate over both objects and groups of numbers
    • Format:
      • "value in object / Array / Number";

      • "(value, key) in object / Array / Number";

      • "(value, key, index) in object";

    • V-for also supports numeric traversal.
    • You can use template to wrap multiple elements instead of div.
    • It needs to be used in combination with key.

What is the key in V-for?

The key attribute is mainly used in the virtual DOM algorithm of Vue to identify VNodes when comparing old and new nodes.

Without keys, Vue uses an algorithm that minimizes dynamic elements and tries to modify/reuse the same type of elements in place whenever possible. With keys, it rearranges elements based on key changes and removes/destroys elements where the key does not exist.

What is a VNode?

The full name of a VNode is Virtual Node. In fact, both components and elements are ultimately represented in a Vue as vNodes, which are essentially JavaScript objects.

Virtual DOM?

If we had a bunch of elements instead of just a simple div, they would form a VNode Tree. The virtual DOM is then formed.

The form instructions

  • v-model: used for bidirectional binding of form data and supplied data.
    • In the form<input>,<textarea> 及 <select>Creates a bidirectional data binding on the element.
    • It automatically selects the correct method to update the element based on the control type.
    • It listens for user input events to update data and performs special processing in extreme cases.
    • His essence is listening ininputEvent, and assigns a value to the supplied data via the event object.
    • The modifier
      • .lazy: Converts the v-Model’s event binding from input to change event.
      • .number: Converts the value of the V-Model binding to a number
      • .trim: Removes whitespace from the values of the V-model binding.
    • If it’s checkboxes and checkboxes,v-modelAdds the selected value to the bound array. And each option must be setvalueProperties.
      <div id="app"></div>
      <template id="my-app">
        <! -- 1. Bind textarea
        <label for="intro">To introduce myself<textarea name="intro" id="intro" cols="30" rows="10" v-model="intro"></textarea>
        </label>
        <h2>intro: {{intro}}</h2>

        <! -- 2.checkbox -->
        <! -- 2.1. Checkbox -->
        <label for="agree">
          <input id="agree" type="checkbox" v-model="isAgree">Agree to a deal</label>
        <h2>isAgree: {{isAgree}}</h2>

        <! -- 2.2. Multiple Marquee boxes -->
        <span>Your hobbies:</span>
        <label for="basketball">
          <input id="basketball" type="checkbox" v-model="hobbies" value="basketball">basketball</label>
        <label for="football">
          <input id="football" type="checkbox" v-model="hobbies" value="football">football</label>
        <label for="tennis">
          <input id="tennis" type="checkbox" v-model="hobbies" value="tennis">tennis</label>
        <h2>hobbies: {{hobbies}}</h2>

        <! -- 3.radio -->
        <span>Your hobbies:</span>
        <label for="male">
          <input id="male" type="radio" v-model="gender" value="male">male</label>
        <label for="female">
          <input id="female" type="radio" v-model="gender" value="female">female</label>
        <h2>gender: {{gender}}</h2>

        <! -- 4.select -->
        <span>Favorite fruit:</span>
        <select v-model="fruit" multiple size="2">
          <option value="apple">apple</option>
          <option value="orange">orange</option>
          <option value="banana">banana</option>
        </select>
        <h2>fruit: {{fruit}}</h2>
      </template>

      <script src=".. /js/vue.js"></script>
      <script>
        const App = {
          template: '#my-app'.data() {
            return {
              intro: "Hello World".isAgree: false.hobbies: ["basketball"].gender: "".fruit: "orange"
            }
          }
        }

        Vue.createApp(App).mount('#app');
      </script>
Copy the code

Use the V-model directive in the component

We can easily use v-Model to do bidirectional binding in form elements. It works by using v-bind:value data binding and @input event listening

What if we want to use v-Models in custom components? How do you do that?

<! -- Use v-model on component --><hy-input v-model="message"></hy-input>
    // Etc join above
    <hy-input :modelValue="message" @update:model-value="message = $event"></hy-input>
Copy the code

Using v-Models in components is provided in components by defaultmodelValueProps, and defineupdate:modelValueEvents.If we want to use v-Models on form elements instead of attribute operations in input events above.We can do that with computed, and provide getters, setters.

    // Implemented via native bidirectional binding
    <input v-model="updateModelValue">
    
    props: {
      modelValue: String
    },
    emits: ["update:modelValue"].computed: {
      updateModelValue: {
        // When modelValue is changed, setter methods are called
        set(value) {
          this.$emit("update:modelValue", value);
        },
        get() {
          return this.modelValue; }}},Copy the code

If we want to customize props to implement using the V-Model on the component, we need to pass custom property names to the V-Model.

    <hy-input v-model:title="title"></hy-input>
    data() {
      return {
        title: "title"}}Copy the code
    // Attributes provided by computed bound here
    <input v-model="updateTitle">
    
    props: {
      title: String 
    },
    emits: ["update:title"].computed: {
      updateTitle: {
        set(value) {
          this.$emit("update:title", value);
        },
        get() {
          return this.title; }}}Copy the code

When we want to bind multiple properties (i.e., multiple V-models) in a custom component, we need to use the props binding name above to do this.

    <hy-input v-model="message" v-model:title="title"></hy-input>
    data() {
      return {
        message: "message".title: "title"}}Copy the code
    
    <input v-model="updateModelValue">
    <input v-model="updateTitle">props: { modelValue: String, title: String }, emits: ["update:modelValue", "update:title"], computed: { updateModelValue: { set(value) { this.$emit("update:modelValue", value); }, get() { return this.modelValue; } }, updateTitle: { set(value) { this.$emit("update:title", value); }, get() { return this.title; }}}Copy the code

optionsAPI

Computed properties

We know that some data in data can be displayed directly in a template using interpolation syntax. However, in some cases, we may need to perform some transformation of the data before displaying it, or we may need to combine multiple data to display it.

  • It is necessary to perform operations on multiple data data, ternary operators to determine the results, and display the data after some transformation
  • Using expressions in templates can be very convenient to implement, but they are designed for simple operations, and putting too much logic into templates can make templates too heavy and difficult to maintain. So you need to use computed properties.
  • If you use it in more than one place, there is a lot of repetitive code that can be pulled away into calculated properties and reused.

In fact, we can also use methods to implement this logic, so why use computed attributes? What’s the difference?

  • When a logical function is called, the computed property does not need to be written(a), but methods need to be written(a)
  • The computed property method is cached when used multiple times, only executed once, and then called again using the cached results. It recalculates the results and caches them when the referenced data changes. Methods, however, do not have a cache and are re-executed each time the corresponding method is called.

Calculate the getter and setter methods for the property

Evaluating properties in most cases, you only need a getter method, so we’ll write evaluating properties directly as a function. But what if we do want to set the value of the calculated property? At this point we can also set a setter method for the evaluated property and pass in the value when we call the evaluated property function.

    methods: {
        handleName() {
        // Change the calculated property value, which is then passed to the set method as an argument
          this.test = "llm zh"}},computed: {
        test: {
          get() {
            return this.name
          },
          set(value) {
            // console.log(value)
            this.name = value
          }
        }
      }
Copy the code

How does Vue internally deal with whether we pass in a getter or an object that contains a setter and getter?

In fact, very simple, Vue source code internal just made a logical judgment

Watch the listener

If we need to monitor data changes and do some logical processing, we need to use Watch.

How to use it? By default, listeners can only listen for changes to their own data, not internal property changes (for objects)

    watch: {
        // Listen for the top-level property
        a(val, oldVal) {
          console.log(`new: ${val}, old: ${oldVal}`)}}Copy the code

If we want to listen for changes to internal data (arrays or objects), we can write the listener as an object and pass in a deep: true property to let it listen deeply, no matter how deep the inner nesting is. It is important to note that the new and old values of the listener function are identical because their references point to the same object/array. Vue does not keep a copy of the value before the change. If we want to use old data, we need to make copies ourselves.

    watch: {
        // This callback is called whenever the property of any object being listened on changes, no matter how deeply nested it is
        c: {
          handler(val, oldVal) {
            console.log('c changed')},deep: true}},Copy the code

If we want to execute the listener immediately, we need to pass an immediate: true attribute.

    watch: {
        // This callback will be invoked immediately after the listening starts
        e: {
          handler(val, oldVal) {
            console.log('e changed')},immediate: true}},Copy the code

We can also pass in more than one listener for a property. It will be called in turn

     watch: {
        // You can pass in an array of callbacks, which will be called one by one
        f: [
          'handle1'.// Methods defined in mothods
          function handle2(val, oldVal) {
            console.log('handle2 triggered')}, {handler: function handle3(val, oldVal) {
              console.log('handle3 triggered')}/ *... * /}}]Copy the code

We can also individually listen for changes in the value of a particular property in an object. Note: The listener still gets the same old and new values. These are all new values. And that’s the whole object. Instead of listening for the value of this property alone.

     watch: {
        // Listen for a single nested property
        'c.d': function (val, oldVal) {
          // do something}}Copy the code

If we want to listen for changes in the value of an object property in an array, we can’t listen like the above method.We can either go throughdeep: trueTo listen in depth, or to each item in the passed array in a child component We can also callthis.$watch()To listen to. And it returns a function that cancels listening.

  • The first parameter is the source to listen on.

  • The second argument is the listener callback.

  • The third parameter is an additional option, such as deep, immediate.

    const unwatch = this.$watch("info".function (newInfo, oldInfo) {
          console.log(newInfo, oldInfo);
        }, 
        {
          deep: true.immediate: true})// Calling it cancels listening
     unwatch()
Copy the code

Mixins with

Sometimes the same code logic exists between components, and we want to extract the same code logic. This property is useful for code extraction and reuse in VUe2. But vuE3 allows us to reuse code in a different way

One way supported in both Vue2 and Vue3 is to use mixins to do this:

  • Mixins provide a very flexible way to distribute reusable functionality in Vue components.
  • A Mixin object can contain any component option.
  • When a component uses a Mixin object, all Mixin object options are mixed into the options of the component itself.

What does Vue do if options in a Mixin object conflict with options in a component object?

This is broken down into different cases to deal with;

  • Case 1: If it is the return value object of the data function

If the properties of the Data return value object conflict, the component’s own data is retained.

  • Case two: Mixing in lifecycle hook functions

The lifecycle hook functions will be merged into the array and will be called.

  • Case three: Directives for options that are values for objects, such as Methods, Components, and directives, will be combined into the same object.
    • For example, the methods option is available and methods are defined.
    • But if the key of the object is the same, then the key-value pair of the component object is taken;

If each component needs to use the same piece of logic, then we can use global mixin.

  • Global mixins can be registered using the application app’s method Mixin.
  • Once registered, globally mixed options affect each component.
App.mixin (mixin object)Copy the code

Mixin code and the code in the component itself are executed in the following order: global mixin > mixin > code in the component itself.

Componentization of Vue

We divide a complete page into components, each used to implement a functional block of the page, and each component can be subdivided, and the components themselves can be reused in multiple places. Our createApp function passed in an object App, which is essentially a component and the root component of our application.

The components in VUE are actually very simple, and the official website is very detailed.

V3.cn.vuejs.org/guide/compo…

But there are a lot of caveats. Next we will introduce:

Props constraints

  • When passing objects or arrays, we specify that the default value must be a factory function. And returns default objects and arrays.
  • And we can do that with arrays that can be multiple types.
  • We can still get throughvalidatorValidation functions are self-defined constraint types.
  • Prop is named in case, preferably-Link naming.

Non-props property processing

When we pass a component an Attribute that does not define props or emits, we call it a non-prop Attribute.

  • When a component has a single root node, non-prop attributes are automatically added to the attributes of the root node
  • If we do not want the root element of the component to inherit the attribute, we can set it in the componentinheritAttrs: false.
    • A common case for disabling attribute inheritance is when you need to apply the attribute to elements other than the root element.
    • We can access all non-props attributes with $attrs.
  • Attribute for multiple root nodes
    • Multiple root attribute nodes with no displayed binding will be warned and we must manually specify which attribute to bind to.

Child components pass parameters to parent components

We can get throughemitsTo verify the passed event parameters, if there is a non-match, will appear a warning.If we want to check the parameters, we just write the array.

     emits: ["add"."sub"."addN"]
Copy the code

Global event bus

Mainly used to pass parameters to non-parent components.

Vue3 removes the ON, ON, ON, off, and $once methods from the instance, so if we want to continue using the global event bus, we go through the third-party library mitt.

    // eventBus.js
    import mitt from 'mitt';

    const emitter = mitt();
    export default emitter;
Copy the code

Register and listen for events

    // Send events
    emitter.emit("String event name"And parameters)// Define events
    emitter.on('String event name', callback function)// Listen for all events
    emitter.on(The '*'.(Event type, corresponding to the parameters passed by the event) = > {})
Copy the code

Remove event

    // Remove all events
    emitter.all.clear()
    // Remove the specified event
    emitter.off("Event name"Remove a reference to the event.Copy the code

A brief introduction to Vite

Next generation front-end development and build tools.

It addresses the problems of the previous generation of build tools:

  • In real development, we often write code that is not directly recognized by browsers, such as ES6, TypeScript, Vue files, and so on. So we have to build tools like WebPack, rollup, and Parcel to convert and compile the code.
  • As your project gets bigger, the amount of JavaScript you need to process grows exponentially, with more modules.
  • The build tool takes a long time to start the server, and the HMR takes a few seconds to show up in the browser.
  • During the development stage, there is no need to do too much adaptation to the code, and the files that cannot be recognized by the browser are converted into esModule files, which improves the construction speed and development efficiency. When the project is packaged, adapt the project.

It mainly consists of two parts:

  • A development server, which provides rich built-in functionality based on native ES modules, HMR is very fast;
  • A set of build instructions that it usesrollupOpen our code and it is pre-configured to output optimized static resources from the build environment;

What’s wrong with using ES Module without resorting to other tools?

  • First, when loading a library, loading the JS code of all the dependent modules of the library is a huge drain on the browser to send requests.
  • Second, browsers don’t recognize TypeScript, less, vue, etc in our code.

Most of the above problems need vite to solve.

Now install Vite

NPM install vite - g# global installNPM install vite - D# Partial installation
Copy the code

Vite support for CSS

  • Vite supports CSS processing directly
  • Vite directly supports CSS preprocessors such as LESS and SASS
    • However, you need to install the LESS, SASS compiler
        npm install less -D
        npm install sass -D
    Copy the code
  • Vite directly supports postCSS conversion:
    • Install postCSS and configure itpostcss.config.jsThe configuration file of
        npm install postcss postcss-preset-env -D
    Copy the code
        // postcss.config.js
        module.exports = {
          plugins: [
            require("postcss-preset-env")]}Copy the code

Vite support for Typescript

  • Vite supports TypeScript natively and uses ESBuild directly to compile:
  • Just import it directly.

If we look at the request in the browser, we will see that the request is still the TS code:

This is because Connect, the server in Vite, forwards our request, takes the TS compiled code and returns it to the browser, which can parse it directly. Note: In Vite2, Koa is no longer used, but a server built with Connect

Vite support for VUE

  • Vue 3 single file component plug-in support: @vitejs/plugin-vue
  • Vue 3 JSX plug-in support: @vitejs/plugin-vue-jsx
  • Vue 2 plug-in support: underfin/vite-plugin-vue2

To configure the plug-in in viet.config.js:

    const vue = require('@vitejs/plugin-vue')

    module.exports = {
      plugins: [
        vue()
      ]
    }
Copy the code

After the above configuration is complete, we import the.vue file and start the project, an error will be reported. In this case, we need to install @vue/ Compiler-SFC plug-in.

Vite scaffolding tools

Execute the following command to create a complete VUE project.

    npm install @vitejs/create-app -g Create-app project nameCopy the code

slot

Functions of slots

We pass some data to the component to be represented by the component, but to make the component more versatile, we can’t limit the content of the component to fixed elements like divs, spans, and so on. We can define slots that allow external customization of the content and elements to be displayed.

Use of slots

In fact, the use of slot is to extract common, reserved different. We will still encapsulate common elements and content within components. Slot tags are used as placeholders for different elements, allowing outsiders to decide what elements to display.

For specific use, please visit the official website.

V3.cn.vuejs.org/guide/compo…

Now let’s introduce what to pay attention to when using slots.

Matters needing attention

  • If we want to style a slot, we need to giveslotLabel wrapped on onedivElement, if you write class directly on the slot tag, the contents of the slot replacement will replace the fullslotSlot.

  • In addition to the default slot, all of our incoming content needs to be intemplateThe name of the slot specified on the label.
  • Can be achieved byv-slot:[SlotName]Method to dynamically bind a name.
  • The slot does not have access to properties in the component that provides the slot.
  • If we need data from a child component to render a slot, we can use the scope slot to pass data to the parent component for use. The data is placed in an object and will act asv-slotValue of the instruction.
     // The item and index attributes here will be put into an object and passed to the parent component.
     <slot :item="item" :index="index"></slot>
Copy the code

Dynamic components

If we need to switch components based on conditions, we can use the Component tag. And specify an IS attribute. Where the value of is attribute is:

  • It can be a component registered with the Component function.
  • A component registered in the Components object of a component object.

So by switching components, we can changeisProperty.Also, we can write props and events in the componentcomponentComponent is passed. When the corresponding component is rendered, props and events are passed to the corresponding component.

    <component :is="currentTab"
             name="zh"
             :age="20"
             @pageClick="pageClick">
    </component>
Copy the code

Component caching

By default, a component is destroyed every time we leave it, and sometimes we want to leave it as it is. So the keep-alive component is used to wrap the component that needs to be cached.

Keep-alive has some properties:

  • The include – string | RegExp | Array. Only components with matching names are cached.
  • Exclude – string | RegExp | Array. Any component with a matching name will not be cached.
  • Max – number | string. The maximum number of component instances that can be cached is the number at which instances in the cached component that have not been accessed recently are destroyed.

Include and Exclude Prop allow components to cache conditionally:

  • Both can be represented as comma-delimited strings, regular expressions, or an array.
  • Matching starts by checking the name option of the component itself.



We cannot call it if we want to do something before the component cache enters and leavescreate, unmountedHook functions, but vue has them built in for usactivateddeactivatedThese two lifecycle hook functions.

    activated() {
      console.log("about activated");
    },
    deactivated() {
      console.log("about deactivated");
    }
Copy the code

Asynchronous components

If our project is too big and we want to load components asynchronously (so we can subcontract them), we have a function in Vue: defineAsyncComponent.

DefineAsyncComponent accepts two types of arguments:

  • Type 1: a factory function that needs to return a Promise object.
    defineAsyncComponent(() = > import("./AsyncCategory.vue"))
Copy the code

The import function above returns a Promise object. Is es6 syntax.

  • Type 2: Accepts an object type and configures asynchronous functions.
    import { defineAsyncComponent } from 'vue'

    const AsyncComp = defineAsyncComponent({
    // Factory function
    loader: () = > import('./Foo.vue')
    // The component to use when loading asynchronous components
    loadingComponent: LoadingComponent,
    // The component to use when loading fails
    errorComponent: ErrorComponent,
    / / delay before displaying loadingComponent | default: 200 (ms)
    delay: 200.// If timeout is provided and the component takes longer to load than the set value, the error component will be displayed
    // Default: Infinity (that is, never timeout, in ms)
    timeout: 3000./ / defines whether the component can be hang | default value: true
    suspensible: false./ * * * *@param {*} Error Indicates the error information object *@param {*} Retry A function that indicates whether the promise loader should retry * when it loads a reject@param {*} Fail a function that tells the loader to quit@param {*} Maximum number of retries allowed */
      onError(error, retry, fail, attempts) {
        if (error.message.match(/fetch/) && attempts <= 3) {
          // Retry the request when an error occurs, up to three times
          retry()
        } else {
          // Note that retry/fail is like promise's resolve/reject:
          // One of these must be called to continue error handling.
          fail()
        }
      }
    })
Copy the code

Use in conjunction with Suspense components

When used in conjunction with this, it ignores the load, error, delay, and timeout options in the provided asynchronous components.

Suspense component, with two slots. And two slots can have only one direct child node.

  • Default Indicates the default slot for displaying asynchronous components.
  • Fallback slot, used to display loading components.
    <suspense>
      <template #default>// Asynchronous components<async-category></async-category>
      </template>
      <template #fallback>
        <loading></loading>
      </template>
    </suspense>
Copy the code

Teleport components

In componentized development, we encapsulate one component, A, and use it in another component, B. The template element in component A will be mounted somewhere in template in component B. Ultimately, our application will form a DOM tree structure.

However, in some cases, we want components that are not mounted in the component tree, perhaps moved to a location outside of the Vue app. For example, move to the body element, or we have some other element outside of the div#app. At this point we can do it through Teleport.

What is a Teleport?

It is a built-in component provided by Vue, similar to the React Portals. Teleport translates as teleportation, long-distance transportation.

It has two properties:

  • To: Specifies the target element to move the contents to, using a selector.
  • Disabled: Indicates whether to disable the teleport function.

But it is still a child of the user. You can still pass props. Multiple teleport-passed components can be mounted to the same target parent. Insert in sequence.

    <teleport to="#zh">
      <hello-world :name="helloVal"></hello-world>
    </teleport>
    <teleport to="#zh">
      <p>Another component</p>
    </teleport>
    
      components: {
        HelloWorld,
      },
      setup() {
        const helloVal = ref('hello')
        return {
          helloVal,
        }
      }
Copy the code
// HelloWorld.vue
  <div>
    <h2>{{name}}</h2>
  </div>

  props: ['name']}Copy the code

Vue life cycle

Each component may go through a series of processes from creation, mounting, updating, unmounting, and so on. At some point in the process, users may want to add some code logic of their own (such as requesting some server data after a component is created).

But how do we know which process the component is currently in?

Vue gives us the component’s lifecycle functions, and the lifecycle hook’s This context is automatically bound to the instance, so you can access Data, computed, and Methods.

animation

Vue provides us with some built-in components and corresponding apis to complete animation, using which we can easily achieve transition animation effects.

Without animation, showing and hiding the entire thing is very hard: if we want to animate a single element or component, we can animate it using the transition built-in component.

Vue provides a wrapper component for Transition. You can add an entry/exit transition to any element or component in the following cases:

  • Conditional Rendering (using V-if) Conditional display (using V-show)
  • Dynamic components
  • Component root node

When inserting or deleting elements contained in the Transition component, Vue does the following:

  • Automatically sniff out whether the target element has CSS transitions or animations applied, and if so, add/remove CSS class names when appropriate.
  • If the Transition component provides JavaScript hook functions, these hook functions will be called at the appropriate time.
  • If the JavaScript hook is not found and CSS transitions/animations are not detected, DOM insertion and deletion will be performed immediately. There will be no animation effects.
    <button @click="isShow = ! isShow"> Show/hide </button><transition name="zh">
      <h2 v-if="isShow">Hello World</h2>
    </transition>
    
    <style scoped>
        .zh-enter-from..zh-leave-to {
          opacity: 0;
        }

        .zh-enter-to..zh-leave-from {
          opacity: 1; } // To add the transition attribute, no animation will occur..zh-enter-active..zh-leave-active {
          /* transition: opacity 2s ease; * /
        }
    </style>
Copy the code

When the class attribute is added

Transition animation class

Vue is the animation that helps us toggle back and forth between these classes:

  • v-enter-from: Defines the state at the beginning of the transition. It takes effect before the element is inserted and is removed the next frame after the element is inserted.
  • v-enter-active: Defines the state when the transition takes effect. Applied throughout the transition phase, before the element is inserted and removed after the transition/animation is complete. This class can be used to define the process time, delay, and curve functions that enter the transition.
  • v-enter-to: Defines the end state of the transition. The next frame takes effect after the element is inserted (the v-enter-from is removed at the same time) and removed after the transition/animation is complete.
  • v-leave-from: Defines the beginning state of the exit transition. Immediately after the exit transition is triggered, the next frame is removed.
  • v-leave-active: Defines the state when the exit transition takes effect. Applies throughout the exit transition phase, takes effect immediately when the exit transition is triggered, and removes after the transition/animation is complete. This class can be used to define exit transition process time, delay and curve functions.
  • v-leave-to: Leaving the end state of transition. The next frame takes effect after the departure transition is triggered (and the V-leave-from is deleted at the same time) and is removed after the transition/animation is complete.

When we do not name the Transition component, giving it the name attribute, it will default to v as the name attribute value. That is, all classes are prefixed with v- by default. Given a name attribute, it is prefixed with a class prefix based on the name attribute value.

CSS animations and CSS transitions

In VUE, we need to use animation and transition to complete the animation. We need to define the above two attributes in the v– Enter-active and V-leave-active class values.

/ / CSS is excessive
    .zh-enter-from,
    .zh-leave-to {
      opacity: 0;
    }

    .zh-enter-to,
    .zh-leave-from {
      opacity: 1;
    }

    .zh-enter-active,
    .zh-leave-active {
      transition: opacity 2s ease;
    }
Copy the code
/ / CSS animations
  .zh-enter-active {
    animation: bounce 1s ease;
  }

  .zh-leave-active {
    animation: bounce 1s ease reverse;
  }

  @keyframes bounce {
    0% {
      transform: scale(0)}50% {
      transform: scale(1.2);
    }

    100% {
      transform: scale(1); }}Copy the code

Transition Component Properties

nameattribute

To set the class prefix.

typeattribute

Vue internally listens for transitionEnd or AnimationEnd to know that the transition is complete, depending on the CSS rule applied to the element:

  • If we use only one of them, Vue automatically identifies the type and sets up a listener.

But what if we use both transitions and animations?

  • And in this case it is possible that the execution of one animation ends while the other animation does not.
  • In this case, we can settypeProperties foranimationortransitionTo explicitly tell Vue what type of listener to listen on. And can be accessed throughdurationSpecifies the animation time explicitly.
    <transition name="zh" type="transition" :duration="{enter: 800, leave: 1000}">
      <h2 class="title" v-if="isShow">Hello World</h2>
    </transition>
Copy the code

durationattribute

Set the time for the transition animation.

Duration can be set to two types of values:

  • Pnumber type: Sets the transition time for both entry and exit.
  • Pobject type: Sets the transition times for entering and leaving, respectively.

Note: Explicitly set values override values specified in CSS transitions and animations.

modeattribute

By default, entry and exit occur simultaneously. If you want to change the default state. We need to add the mode attribute.

  • in-out: The new element transitions first, then the current element transitions away.
  • out-in: The current element transitions first, and then the new element transitions in.

In most cases, we need the first animation to end and the second animation to start. So you need to useout-in

appearattribute

By default, the first render is not animated, so if we want to animate it, we can increase “Appear”; Ture.

cssattribute

We don’t need vUE to detect animations in CSS when we manipulate animations through JS. So you need to set CSS to false. By default, CSS: true.

JavaScript hooks

When we want to do something at various stages of animation execution. We can use this hook.

  • @before-enter=”beforeEnter”, enter the V-enter-from phase
  • @Enter =” Enter “, enter v-enter-active
  • After -enter=”afterEnter”, the v-enter-to phase is executed
  • @enter-cancelled=”enterCancelled”
  • @before-leave=”beforeLeave”, the value is executed in the V-leave-to phase
  • @leave=”leave”, the execution starts in the V-leave-active phase
  • @after-leave=”afterLeave”, which is executed in the V-leave-to phase
  • @leave-cancelled=” leavecancancelled “. The data is executed until the V-enter -to phase

When transitioning only with JavaScript, you must use done for callbacks in the Enter and leave hooks. Otherwise, they will be called synchronously and the transition will complete immediately. In this case, we can add: CSS =”false” so that the Vue skips CSS detection. In addition to slightly better performance, this avoids the effects of CSS rules during the transition.

The hooks above can be used with some JS animation libraries to implement animations. For example, the JSAP library. It animates CSS properties, SVG, Canvas, etc., via JavaScript, and is browser-compatible. There are two important apis for animation.

  • jsap.from(el, options): indicates the state in which the animation starts.
  • jsap.to(el, options): indicates the state in which the animation ends.

Where EL represents the element that the animation acts on. Options represent animated CSS properties.

     // Go to animation
    enter(el, done) {
      console.log('enter')
      // from is the starting position
      gsap.from(el, {
        scale: 0.x: 200.onComplete: done,
      })
    },
    // Leave the animation
    leave(el, done) {
      console.log('leave')
      // to indicates the end position
      gsap.to(el, {
        scale: 0.x: 200.onComplete: done,
      })
    },
Copy the code

Let’s implement a scrolling digital animation using the JSAP library.

<template>
  <div class="app">
    <input type="number" step="100" v-model="counter">
    <h2>{{shownumber.tofixed (0)}}</h2>
  </div>
</template>

<script>
  import gsap from 'gsap';

  export default {
    data() {
      return {
        counter: 0.showNumber: 0}},watch: {
      counter(newValue) {
        gsap.to(this, {duration: 1.showNumber: newValue})
      }
    }
  }
</script>
Copy the code

Other animation knowledge, please visit the official website, very detailed.

V3.cn.vuejs.org/guide/trans…

composition API

The following apis need to be imported into vUE.

Disadvantages of the Options API

In Vue2, we write components using the Options API:

  • One feature of the Options API is that the corresponding function modules are written in the corresponding properties. For example, data defines data, methods in methods, computed properties in computed, watches for property changes, and life cycle hooks.

But this code has one big drawback:

  • When we implement a feature, the code logic for that feature is split into properties.
  • As our components become larger and more complex, the list of logical concerns grows, and the logic of the same function is broken down very thinly.
  • Especially for those who didn’t write the components in the first place, the component’s code is hard to read and understand (other people who read the components).

Composition API is introduced

Composition API container

The composition API is all written in our setup function. And you can’t use this in setup functions because vue doesn’t bind this when it calls setup functions internally.

Let’s look at some setup functions.

  • It takes two main parameters:
    • The first parameter:props. Properties received by the component
    • The second parameter:context. Component context object
      • Attrs: All non-prop attributes.
      • Slots: Slots passed by the parent component (this is useful when returned as a render function).
      • Emit: Emit is used when we need to emit events within our component.

So how do we define reactive data?

Composition API processes data

reactive: Turns multiple data into responsive data
  • When we process our data using reactive functions, dependency collection takes place when the data is used again.
  • When the data changes, all the collected dependencies are used to perform corresponding reactive actions (such as updating the interface).
  • In fact, the data option we write is also internally handed to reactive functions to program reactive objects.
    const state = reactive({
      counter: 100.name: 'zh'
    })
Copy the code
ref: Converts single data into responsive.

The Reactive API has restrictions on the type passed in. It requires an object or array type to be passed in: if we pass in a basic data type (String, Number, Boolean) a warning is issued. So we need to use ref.

  • Ref returns a mutable reactive object that maintains its internal value as a reactive reference, which is where the ref name comes from.
  • Its internal values are maintained in the ref’s value attribute.
  • When importing ref values in the template, Vue will automatically help us unpack them, so we don’t need to use them in the template as ref.value.
  • Inside the setup function, it is still a ref reference, so we still need to use the ref.value method to operate on it.

Note: The unpacking of the REF object in the template is superficial

readonly: returns a read-only proxy for an incoming object

A reactive object can be retrieved by reactive or ref, but in some cases the reactive object that we pass somewhere else wants to be used in another place but cannot be modified. For example, if we want to pass the provide number to descendant components, we can use readonly to make it read-only and cannot be modified in descendant components.

This API returns read-only proxies for normal objects, REF objects, and Reactive objects.

  • Objects returned by readonly are not allowed to be modified.
  • However, original objects processed by readonly are allowed to be modified.
  • Essentially, the setter method for the object returned by ReadOnly has been hijacked.
      // 1. Common objects
      const info1 = {name: "zh"};
      const readonlyInfo1 = readonly(info1);

      // 2. Reactive objects
      const info2 = reactive({
        name: "zh"
      })
      const readonlyInfo2 = readonly(info2);

      // 3. The responsive object ref
      const info3 = ref("zh");
      const readonlyInfo3 = readonly(info3);
Copy the code
toRefs: Turns the passed object into a ref object

When we want to deconstruct reactive objects, directly deconstructing them will make the data unresponsive. If we wrap it in toRefs and deconstruct it, the data is still responsive. This creates a link between the properties in Reactive and the ref.value, and any change in a reactive will cause another change

    const info = reactive({ name: 'zh'.age: 22 })
    // 1. ToRefs: convert all properties of a reactive object toRefs to create a link
    let { name, age } = toRefs(info)
Copy the code
toRef: changes the attribute of the object passed in to a ref object

If you only want to convert an attribute in a reactive object toRef, you can use the toRef method.

    const info = reactive({ name: 'zh'.age: 22 })
    // Change the age attribute in the info object to the ref object.
    let age = toRef(info, 'age')
Copy the code
isProxy
  • Check whether the object is a proxy created by Reactive or Readonly.
isReactive
  • Check whether the object is a reactive agent created by Reactive.
  • It also returns true if the agent is created by ReadOnly but wraps another agent created by Reactive.
isReadonly
  • Check whether the object is a read-only proxy created by ReadOnly.
toRaw
  • Returns the original object of the Reactive or Readonly agent (it is not recommended to keep a persistent reference to the original object. Use with caution).
shallowReactive
  • Create a reactive proxy that tracks the responsiveness of its own property, but does not perform deep reactive transformations of nested objects (deep or native).
shallowReadonly
  • Create a proxy that makes its own property read-only but does not perform deep read-only conversion of nested objects (the deep layer is still readable and writable).
unref
  • If we want to get a value from a ref reference, we can also use the unref method.
  • If the argument is a ref, the internal value is returned, otherwise the argument itself is returned.
  • This is val = isRef(val), right? Val. value: The syntactic sugar function of val.
isRef
  • Determines whether the value is a ref object.
shallowRef
  • Create a shallow ref object. It is only responsive if the ref object is modified. If you modify an internal object, it will not be responsive. This API is not the same as shallowReactive. The latter is to make the first layer of the incoming object a responsive one, which can still be made responsive by modifying the attributes of the first layer object. But the API only modifies the REF object to be responsive.
    const info = shallowRef({ name: 'zh' })
    const changeInfo = () = > {
      // Only then can the modification be responsive
      info.value = { name: 'llm' }
      // This is not responsive
      info.value.name = 'llm'
    }
Copy the code
triggerRef
  • Manual triggering of side effects associated with shallowRef.
      const info = shallowRef({name: "zh"})
      const changeInfo = () = > {
        info.value.name = "llm";
        // make it responsive
        triggerRef(info);
      }
Copy the code
customRef: Customizes ref objects

Create a custom REF with display control over its dependency trace and update trigger:

  • It requires a factory function that takes track and trigger functions as arguments.
  • And it should return an object with get and set.
    import { customRef } from 'vue';

    // customize ref
    export default function(value, delay = 300) {
      let timer = null;
      return customRef((track, trigger) = > {
        return {
          get() {
            track();
            return value;
          },
          set(newValue) {
            clearTimeout(timer);
            timer = setTimeout(() = >{ value = newValue; trigger(); }, delay); }}})}// Use it directly
    const message = debounceRef("Hello World");
Copy the code

Gets the current component context getCurrentInstance

Since this is not bound in the setup function. So we can’t get this, the current component object.

What if we want to get?

Vue provides getCurrentInstance, which lets us get the current component object. Just call the API.

If you want to get global properties provided by the component. We need to get the global object.

    getCurrentInstance().appContext.config.globalProperties
Copy the code

Computed attribute computed

When some of our properties are dependent on other states, we can use computed properties to handle this.

Two kinds of parameters can be passed in computed:

  • Receives a getter function and specifies the return value for the getter function
  • Receives an object with get and set and returns a mutable (read-write) ref object. This is useful when modifying computed.
  • Computed returns a REF object.
      // 1. Usage 1: Pass a getter function
      // The return value for computed is a ref object
      const fullName = computed(() = > firstName.value + "" + lastName.value);

      // 2. Usage two: Pass in an object containing a getter/setter
      const fullName = computed({
        get: () = > firstName.value + "" + lastName.value,
        set(newValue) {
          const names = newValue.split("");
          firstName.value = names[0];
          lastName.value = names[1]; }});Copy the code

Monitor data watch/watchEffect

Perform some action when the data changes. We can use Watch/watchEffect to listen.

The difference between the two:

  • watchEffectIt does not need to specify the properties to listen to. It automatically collects dependencies. Whenever a callback references a reactive property, the callback is executed whenever the property changeswatchChanges can only be made by listening for specified attributes (v3 can specify multiple attributes at the same time from the start).
  • watchAccess toNew and old values (values before update)And thewatchEffectYou can’t get it.
  • watchEffectIf present, it is executed once at component initialization to collect dependencies (andcomputedAgain), the collected dependencies change before the callback is executed again, andwatchNo, because he specified the dependency in the first place.

watchEffect

  • First, the functions passed in by watchEffect are executed immediately and dependencies are collected as they are executed.
  • Second, the watchEffect function passed in is executed again only if the collected dependencies change.
  • And returns a function that can be used to cancelwatchEffectListening in. Or automatically stop listening after the component is uninstalled.
     // watchEffect: Automatically collects reactive dependencies
      const name = ref("zh");
      const age = ref(20);

      const stop = watchEffect(() = > {
        console.log("name:", name.value, "age:", age.value);
      });

      const changeName = () = > name.value = "llm"
      const changeAge = () = > {
        age.value++;
        // Unlisten watchEffect when age is greater than 30.
        if (age.value > 30) { stop(); }}Copy the code

Execution timing of watchEffect. By default, watchEffect executes the side effect function before the view is updated. If we want to change the timing of his execution, how?

WatchEffect can also pass in a second argument, as an object. Setting flush sequence changes the timing of watchEffect execution.

 flush: 'pre'(Default, executed before view update)'post'(Executed after view update)'sync'(Synchronous trigger, there will be problems, use less)Copy the code
      const title = ref(null);

      watchEffect(() = > {
        console.log(title.value); // Get the DOM element.
      }, {
        flush: "post" // Execute after view update to reduce invalid execution of watchEffect. Because the first execution gets NULL
      })
Copy the code

WatchEffect can also remove side effects

What are the clearance side effects?

For example, we need to execute a network request in a listener during development, but we stop the listener before the network request is reached, or the listener listener is executed again. Then the last network request should be cancelled, and we can remove the last side effect.

When the function we passed to watchEffect is called back, we actually get one argument: onInvalidate, which is executed when the side effect is about to be reexecuted or the listener is stopped. We can do some clear work in the incoming callback function. It’s kind of a throttling function.

        watchEffect((onInvalidate) = > {
            const timer = setTimeout(() = > {
              console.log("Network request successful ~");
            }, 2000)

            onInvalidate(() = > {
              // Clear extra side effects in this function
              clearTimeout(timer);
              console.log("onInvalidate"); })});Copy the code

watch

The WATCH API is exactly equivalent to the Property of the component’s Watch option:

  • Watch needs to listen for specific data sources and perform side effects in callback functions.
  • It is lazy by default and only performs callbacks when the source being listened to changes.

Listening for a single data source

There are two types of data sources for watch listeners:

  • A getter: But the getter must refer to a reactive object (such as reactive or ref).
  • Write directly to a reactive object, reactive or ref (more commonly ref).

If you pass in a ref responsive object or getter and return a normal object (via… Structure), listener functions are normal objects/normal values. Not ref, Reactive objects. If a Reactive object is passed in, the listener is a reactive object. And the new value is the same as the old value.

    / / change the name
    const info = reactive({ name: 'zh'.age: 20 })
    watch(info, (newValue, oldValue) = > {
        // newValue and oldValue are reactive objects and return new values.
      console.log('newValue:', newValue, 'oldValue:', oldValue)
    })
Copy the code

Listen for multiple values

Multiple sources can be placed in an array.

    // 1. Define responsible objects
    const info = reactive({ name: 'zh'.age: 20 })
    const name = ref('zh')

    // 2. Listener watch
    watch(
      [() = > ({ ...info }), name],
      ([newInfo, newName], [oldInfo, oldName]) = > {
        console.log(newInfo, newName, oldInfo, oldName)
      }
    )
Copy the code

Watch the options

If we want to listen deeply on an object, we need to pass a second argument to watch. Used for deep listening or immediate execution.

By default, watch cannot deeply listen to a reactive object that is being listened to, but info.friend.name will also be changed if we change the properties of the first layer (info.name). But if you only change info.friend.name, the watch callback will not be triggered. Only if deep: true is configured will it be listened on.

    const info = reactive({
      name: 'zh'.age: 18.friend: {
        name: 'jcl',}})// 2. Listener watch
    watch(
      () = > ({ ...info }),
      (newInfo, oldInfo) = > {
        console.log(newInfo, oldInfo)
      },
      {
        // deep: true,
        // immediate: true,})const changeData = () = > {
      // info.name = 'llm'
      info.friend.name = 'zheng'
    }
Copy the code

But for direct listening to reactive objects, it automatically deep listening, with the internal setting deep: true.

    const info = reactive({
      name: 'zh'.age: 18.friend: {
        name: 'jcl',
      },
    })
     watch(info, (newInfo, oldInfo) = > {
      console.log(newInfo, oldInfo)
    })

    const changeData = () = > {
      info.friend.name = 'zheng'
    }
Copy the code

For objects passed by ref, there is also no depth listening by default. And the argument in the listener function is an object.

    const info = ref({
      name: 'zh'.age: 18.friend: {
        name: 'jcl',
      },
    })
     watch(
      info,
      (newInfo, oldInfo) = > {
        console.log(newInfo, oldInfo)
      },
      {
      // If you need to add it, you can do deep monitoring
        deep: true,})const changeData = () = > {
      info.value.friend.name = 'zheng'
    }
Copy the code

The life cycle

Compare the lifecycle in the Options API to the lifecycle in the Composition API

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
activated onActivated
deactivated onDeactivated

Dojo.provide and inject

If we want to pass data to descendant components, we can do so through the Provide API. Use the Inject API to receive the passed data.

Since the data we pass down does not need to be modified by descendant components, only we are allowed to modify it ourselves and then affect the underlying components, so. You can use the readonlyAPI to pass read-only data.

Provide can be passed two arguments:

  • The first is the name of the supplied property
  • The second is the incoming data

Inject can be passed two parameters:

  • The first is the name of the property passed by the receive provide
  • The second is to provide default values
// pass down
    const obj = reactive({
      name: 'zh'.age: 20,})const name = ref('llm')

    provide('obj', readonly(obj))
    provide('name', readonly(name))
Copy the code
    / / receive
    let obj = inject('obj')
    let name = inject('name')
Copy the code

If we really want to modify data in a descendant component, we can provide a function that accepts data from the descendant component and then modify it in that descendant component.

    // provide
    // This is also passed into inject for its modification
    const updateName = (e, val) = > {
      console.log('e---------------', e, val)
      name.value = val
    }
Copy the code
    // inject
    let updateName = inject('updateName')
    <button @click="(e) => {updateName(e, 'updateName ')}"> change name < / button >Copy the code

Render function

Vue recommends using templates to create your HTML in the vast majority of cases, but for those special situations where you really need the full programming power of JavaScript, you can use rendering functions, which are closer to the compiler than templates.

Vue transforms our nodes into VNodes before generating the actual DOM, which together form a tree structure called the pseudo-DOM (VDOM).

In fact, the HTML in the template we wrote ended up using a rendering function to generate the corresponding VNode.

So, if you want to take full advantage of JavaScript’s programming power, we can write the createVNode function ourselves and generate the corresponding VNode.

We can do this through the H function provided by VUE. The h() function is a function used to create a vNode. The intended name was the createVNode() function, but it was simplified to the H () function in Vue for simplicity.

The following is the argument passing and usage of h function.

    h(
      // {String | Object | Function} tag
      // An HTML tag name, a component, an asynchronous component, or
      // a functional component.
      //
      // Necessary.
      'div'.// {Object} props
      // Objects corresponding to attributes, prop, and events.
      // This will be used in templates.
      //
      // Optional.
      {},

      // {String | Array | Object} children
      // Subvnodes, built using 'h()',
      // Get "text VNode" with string or
      // Objects with slots.
      //
      // Optional.
      [
        'Some text comes first.',
        h('h1'.'A headline'),
        h(MyComponent, {
          someProp: 'foobar'})])Copy the code

Note: If you don’t have props, you can usually pass children as the second argument. If there is ambiguity, you can pass NULL as the second argument and children as the third.

Basic use of h functions. The h function can be used in two places:

  • Render function options. As the return value of the Render function. If you want to use events. We can pass on the attribute of the event name, the function as its value.
    data() {
      return {
        counter: 0}},render() {
      return h("div", {class: "app"}, [
        h("h2".null.'Current count:The ${this.counter}`),
        h("button", {
          onClick: () = > this.counter++
        }, "+ 1"),
        h("button", {
          onClick: () = > this.counter--
        }, "1"),])}Copy the code
  • The setup function option (setup itself needs to be a function type, which in turn returns the VNode created by h). As the return value of the setup function.
    setup() {
      const counter = ref(0);
      
      return () = > {
        return h("div", {class: "app"}, [
          h("h2".null.'Current count:${counter.value}`),
          h("button", {
            onClick: () = > counter.value++
          }, "+ 1"),
          h("button", {
            onClick: () = > counter.value--
          }, "1"),])}}Copy the code

The h function uses slots. The default slot content can be provided through the ternary operator.

HelloWorld.vue
// Child components provide slots and slot prop
 setup() {
    const instance = getCurrentInstance().ctx.$slots
    return () = >
      h('div', {}, [
        instance.first
          ? instance.first({ first: 'first=========' })
          : 'Default slot first',
        instance.second
          ? instance.second({ second: 'second=========' })
          : 'Default slot second',}]),Copy the code
// Use render function slots
setup() {
    return () = >
      h(
        HelloWorld,
        {class: 'hello-world'},
        {
        // Pass the slot content to HelloWorld and use the supplied slot data
          first: (slotProps) = > h('span', slotProps.first),
          second: (slotProps) = > h('span', slotProps.second),
        }
      )
  },
Copy the code

This is just some personal experiment, if you want to know more, please visit the website v3.cn.vuejs.org/guide/rende…

Custom instruction

In the template syntax of Vue we have studied various directives: V-show, V-for, V-model, etc. In addition to using these directives, Vue also allows us to customize our own directives. Used to reuse code, easy to operate.

Note: In Vue, code is reused and abstracted primarily by components. In some cases, custom instructions are used when you need to perform low-level operations on DOM elements.

There are two types of custom instructions:

  • Custom local directives: The component is provided with the Caching option and is only used in the current component.
  • Custom global directives: The Directive method of app can be used in any component.

Let’s customize a directive that automatically gets focus.

Local directives: Use v-focus directly in the DOM

    // Local directives
    directives: {
      focus: {
        mounted(el, bindings, vnode, preVnode){ el.focus(); }}}Copy the code

Global directives

app.directive("focus", {
    mounted(el, bindings, vnode, preVnode){ el.focus(); }})Copy the code

Let’s take a look at life cycle functions in custom instructions

Vue provides the following hook functions: Note: These lifecycle function names are a little different from vue2

  • created: called before the attribute or event listener of the bound element is applied;
  • beforeMount: called when the directive is first bound to an element and before the parent component is mounted;
  • mounted: called after the parent of the bound element has been mounted;
  • beforeUpdate: called before updating the VNode containing the component;
  • updated: called after the VNode containing the component and its children are updated;
  • beforeUnmount: called before unloading the parent component of the bound element;
  • unmounted: only called once when the directive is unbound from an element and the parent component has been unbound;

Parameters of the lifecycle function: el, binding are more commonly used

  • el: The element to which the directive is bound. This can be used to manipulate the DOM directly.
  • binding: The object containing the following property.
    • instance: component instance that uses the directive.
    • value: The value passed to the instruction. For example, in thev-my-directive="1 + 1", the value is2.
    • oldValue: Previous value, only inbeforeUpdate 和 updatedAvailable in. Whether or not the value has changed is available.
    • argThe: argument is passed to the instruction (if any). For example, inv-my-directive:fooThe arg is"foo".
    • modifiers: An object containing modifiers (if any). For example, inv-my-directive.foo.bar, the modifier object is{foo: true, bar: true}.
    • dir: an object passed as a parameter when a directive is registered. (is the provided lifecycle function)
  • vnode: Blueprints of the actual DOM elements received as el arguments above.
  • prevNode: Indicates the last virtual nodebeforeUpdate 和 updatedHooks are available.

Let’s encapsulate an instruction to format a timestamp

     import dayjs from 'dayjs';
     app.directive("format-time", {
        // Do some initialization
        created(el, bindings) {
          bindings.formatString = "YYYY-MM-DD HH:mm:ss";
          // When the user passes in a format string, we use the format that the user passed in
          if(bindings.value) { bindings.formatString = bindings.value; }},mounted(el, bindings) {
          const textContent = el.textContent;
          let timestamp = parseInt(textContent);
          if (textContent.length === 10) {
            // Convert the timestamp to milliseconds
            timestamp = timestamp * 1000} el.textContent = dayjs(timestamp).format(bindings.formatString); }})// Allow users to customize formatting
      <h2 v-format-time="'YYYY/MM/DD'">{{timestamp}}</h2>
Copy the code

The plug-in

When we add some functionality to the Vue globally, we usually use the plug-in model, which is written in two ways:

  • Object type: an object,But you have to include oneinstallThe function ofThis function is executed when the plug-in is installed.installThe function will take two arguments, one a global object. The second is the configuration object passed in by the user
  • Function type: a function, which is executed automatically when the plug-in is installed. Will take two parameters, one is the global object. The second is the configuration object passed in by the user.

There is no limit to what plug-ins can do, such as the following:

  • Add global methods or properties by adding them to config.globalProperties.
  • Add global resources: directives/filters/transitions, etc.
  • Add some component options through global mixins.
  • A library that provides its own API and provides one or more of the functions mentioned above.

Use the plug-in to call the use method of the global object. And pass the plug-in object to the use method.

// plugin.js
    // Object type
    export default {
      install(app) {
        app.config.globalProperties.$name = "zh"}}// Function type
    export default function(app) {
      console.log(app);
    }
Copy the code
    / / use
    const app = createApp(App);
    app.use(plugin)
Copy the code