Render function

This is the 22nd day of my participation in the August Wen Challenge.More challenges in August

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

Before generating the real DOM, Vue transforms our nodes into VNodes, which together form a tree structure called the virtual DOM(VDOM).

The HTML in the template we wrote also ends up using the render function to generate the corresponding VNode

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

Write your own render function and use JavaScript directly to write the template.

Since the render function needs to return vNode, we need to use another function h(),

The h() function is a function used to create a VNode. The more accurate name of the h() function is createVNode(), which Vue has simplified to h() for ease of use

The h function takes three arguments:

  1. Element or component
  2. Attribute on an element or component — Optional
  3. Child element, child component, array (if multiple child elements) – optional

Matters needing attention:

  • If there is no props, it is usually ok to pass children as the second argument but it is not recommended
  • Easy to generate ambiguity, recommended willnullor{}Passed as the second argument and children as the third argument
<script>
  import { h } from 'vue'

  export default {
    render() {
      return h('h2', { title: 'title' }, 'Hello Vue')}}</script>

<! -- => <h2 title="title">Hello Vue</h2> -->
Copy the code
<script>
  import { h } from 'vue'
  import Cpn from './components/Cpn.vue'

  export default {
    render() {
      return h(Cpn, { title: 'title'}}})</script>
Copy the code
<script>
  import { h } from 'vue'
  import Cpn from './components/Cpn.vue'

  export default {
    // The render function is a configuration option on the same level as data, methods, and setup
    render() {
      return h('div', { title: 'title'},'Hello Vue',
        h('p', {}, 'Hello Render'),
        Cpn.render()
      ])
    }
  }
</script>
Copy the code

Counter case

Write 1

<script>
import { h } from 'vue'

export default {
  name: 'App'.data() {
    return {
      count: 0}},render() {
    return h('div', {}, [
      h('p', {}, ` count:The ${this.count}`),

      h('button', {
        onClick: () = > this.count++
      }, '+ 1'),

      h('button', {
        onClick: () = > this.count--
      }, '1')]}}</script>
Copy the code

Write 2

<script>
import { ref, h } from 'vue'

export default {
  setup() {
    const count = ref(0)

    return {
      count
    }
  },

  render() {
    // In the render function, this is bound normally, so we can use this
    return h('div', {}, [
      h('p', {}, ` count:The ${this.count}`),

      h('button', {
        onClick: () = > this.count++
      }, '+ 1'),

      h('button', {
        onClick: () = > this.count--
      }, '1')]}}</script>
Copy the code

Writing 3

<script>
import { ref, h } from 'vue'

export default {
  setup() {
    const count = ref(0)

    // return can return a render function
    return () = > {
      return h('div', {}, [
        h('p', {}, ` count:${count.value}`),

        h('button', {
          onClick: () = > count.value++
        }, '+ 1'),

        h('button', {
          onClick: () = > count.value--
        }, '1'])}}}</script>
Copy the code

Use of slots

The parent component

<script>
import { h } from 'vue'
import Cpn from './components/Cpn.vue'

export default {
  setup() {
    return () = > h(Cpn, {}, {
      default(props) {
        return h('h2', {}, props)
      }
    })
  }
}
</script>
Copy the code

Child components

<script>
import { h } from 'vue'

export default {
  setup(props, { slots }) {
    return () = > h('div', {}, slots.default?.('something in Cpn') || h('h2', {}, 'default value'))}}</script>
Copy the code

JSX

The render function is very unreadable using h functions, so we usually use JSX code when we actually write,

We then use Babel to convert our JSX code into an H function

counter

<script>
import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)

    return () = > {
      return (
        <>{/* JSX does not automatically wrap fragments, so manually add */}<h2>count: {count.value}</h2>
          <button onClick={() = > count.value++ }>+1</button>
          <button onClick={() = > count.value-- }>-1</button>
        </>)}}}</script>
Copy the code

Using the component

<script>
import Cpn from './components/Cpn.vue'

export default {
  setup() {
    return () = > {
      return (
        <>
         <Cpn />
        </>)}}}</script>
Copy the code

Pass props

The relay

<script>
import Cpn from './components/Cpn.vue'

export default {
  setup() {
    return () = > {
      return (
        <>
         <Cpn msg="msg" />
        </>)}}}</script>
Copy the code

The caller

<script>
export default {
  // The passed props still need to be declared here to distinguish the props and no-props attribute
  props: ['msg'].setup(props) {
    console.log(props.msg) // => 'msg'

    return () = > {
      return <h2>default value</h2>}}}</script>
Copy the code

Using slot

Slot user

<script>
import Cpn from './components/Cpn.vue'

export default {
  setup() {
    return () = > {
      return (
        <>
         <Cpn>
          {
            {
              default(props) {
                return <h2>{ props }</h2>}}}</Cpn>
        </>)}}}</script>
Copy the code

Slot provider

<script>
export default {
  setup(props, { slots }) {
    return () = > {
      return slots.default?.('Hello World') | |<h2>default value</h2>}}}</script>
Copy the code

Custom instruction

In the template syntax of Vue we have studied various instructions: V-show, V-for, V-model, and so on

In addition to using these directives, Vue also allows us to customize our own directives

  • Note: In Vue, code is reused and abstracted primarily through components
  • Usually in some cases, you need toPerform low-level operations on DOM elementsThis is where custom instructions are used

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

Local instructions

<template>
  <div>
    <input type="text" v-focus>
  </div>
</template>

<script>
export default {
  name: 'App'.// Directives are an object that can mount multiple directives
  directives: {
    // key is the name of the directive without the v- prefix
    // value is a configuration object, where the values are mainly the life cycle functions corresponding to some instructions
    focus: {
      mounted(el) {
        el.focus()
      }
    }
  }
}
</script>
Copy the code

Global directives

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)

app.directive('focus', {
  mounted(el) {
    el.focus()
  }
})

app.mount('#app')
Copy the code

The life cycle

function instructions
created Called before the attribute or event listener of the bound element is applied

Attributes and events are not bound before the element is created

Generally used for initialization operations
beforeMount Called when the directive is first bound to an element and before the parent component is mounted
mounted Called after the parent component of the bound element has been mounted
beforeUpdate Called before updating the VNode containing the component
updated In a VNode that contains componentsVnodes and their childrenCall after update
beforeUnmount Called before unloading the parent component of the bound element
unmounted It is called only once when the directive is unbound from the element and the parent component is unmounted

Each lifecycle hook has the following four parameters

The name of the instructions
el Bound element
bindings Bind information, which stores modifiers and parameters
vnode The current vnode
preVnode Update the previous VNode

Parameters and modifiers

<template>
  <div>
    <! -- once -- modifier -- param -- parameter input needs to be quoted -- if there are no quotes, it will be considered as variables. In bindings, if there is no value of parameter variable, The default value is undefined -->
    <input type="text" v-focus.once="'param'">
  </div>
</template>

<script>
export default {
  name: 'App'.directives: {
    focus: {
      mounted(el, bindings) {
        el.focus()

        console.log(bindings.value) // => param
        console.log(bindings.modifiers) // => { once: true }}}}}</script>
Copy the code

case

Formatting time

Definition - / SRC/driectives formatTime. Js

import dayjs from 'dayjs'

// The app used by the registration directive is passed in from outside
export default function(app) {
  // Directive names use either format-time or formatTime
  // Format-time is recommended
  app.directive('format-time', {
    created (el, bindings) {
      // formatString mounts bindings for
      // To ensure that the date string is initialized before each invocation of the instruction
      bindings.formatString = 'YYYY-MM-DD HH:mm:ss'
      // Initialize the operation
      bindings.formatString = bindings.value || bindings.formatString
    },

    mounted (el) {
      const timeStamp = el.textContent

      el.textContent = timeStamp.length === 10
        ? dayjs.unix(timeStamp).format(bindings.formatString) 
      	: dayjs(timeStamp).format(bindings.formatString)
    }
  })
}
Copy the code

Uniform exposure -- SRC /driectives/index.js

import registerFormatTime from './formatTime'

export default function(app) {
  registerFormatTime(app)
}
Copy the code

Global registration -- main.js

import { createApp } from 'vue'
import App from './App.vue'

import registerDriectives from './driectives'

const app = createApp(App)

// Global register directive
registerDriectives(app)

app.mount('#app')
Copy the code

use

<p v-format-time="'YYYY/MM/DD'">1629561162</p>
Copy the code

Teleport (Understand)

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 the component not to be mounted on the component tree, perhaps to be moved to a location other than the Vue app:

  • 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 the Teleport

  • It is a built-in component provided by Vue, similar to the React Portals
  • Teleport translates as teleportation, long-distance transportation
<template>
  <div>
    <! Instead of being mounted to div#app, the element will be mounted to div#foo.
    <teleport to="#foo">
      <h2>Hello Teleport</h2>
    </teleport>
  </div>
</template>
Copy the code
<template>
  <div>
    <teleport to="#foo">
      <h2>Hello Teleport</h2>
    </teleport>

    <! -- Multiple teleports existing at the same time will not be overwritten, but will be merged -->
    <teleport to="#foo">
      <Cpn />
    </teleport>
  </div>
</template>
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 must contain a function of Install that is executed when the plug-in is installed
  • Function type: a function, which is executed automatically when the plug-in is installed

There’s no limit to what plug-ins can do,

  • You can add global methods or properties
  • This can be to add global resources such as directives/filters/transitions etc
  • Add some component options through global mixins
  • Mount a library globally for use

Definition - the plugins/addGlobalVariable. Js

Object way

export default {
  // The app object is passed in as an argument when the function is called
  install(app) {
    // In VUE, global methods or variables usually start with $to distinguish between global and local variables
    app.config.globalProperties.$name = 'Klaus'}}Copy the code

The function way

export default function(app) {
  app.config.globalProperties.$name = 'Klaus'
}
Copy the code

To register the plugin

import { createApp } from 'vue'
import App from './App.vue'

import addGlobalVariable from './plugins/addGlobalVariable'

const app = createApp(App)

// Register the plug-in
app.use(addGlobalVariable)

app.mount('#app')
Copy the code

Use - vue2

mounted() {
  console.log(this.$name)
}
Copy the code

Use - vue3

setup() {
  // getCurrentInstance().appContext ==
  const name = getCurrentInstance().appContext.config.globalProperties.$name
  console.log(name)
}
Copy the code

case

Formatting time

Implementation method one - mount to global properties

implementation

import dayjs from 'dayjs'

export default {
  install(app) {
    app.config.globalProperties.$formatTime =
    (timeStamp = Date.now() , formatStr = 'YYYY-MM-DD HH:mm:ss') = >
      timeStamp.toString().length === 10 ?
        dayjs.unix(timeStamp).format(formatStr)
        : dayjs(timeStamp).format(formatStr)
  }
}
Copy the code

mount

import { createApp } from 'vue'
import App from './App.vue'

import formatTime from './plugins/formatTime'

const app = createApp(App)

app.use(formatTime)

app.mount('#app')
Copy the code

use

setup() {
  const { $formatTime } = getCurrentInstance().appContext.config.globalProperties
  console.log($formatTime(1629564731))}Copy the code

Implementation 2 - Use provide global passing

implementation

import dayjs from 'dayjs'

export default {
  install(app) {
    app.provide('$formatTime'.(timeStamp = Date.now() , formatStr = 'YYYY-MM-DD HH:mm:ss') = >
    timeStamp.toString().length === 10 ?
      dayjs.unix(timeStamp).format(formatStr)
      : dayjs(timeStamp).format(formatStr))
  }
}
Copy the code

mount

import { createApp } from 'vue'
import App from './App.vue'

import formatTime from './plugins/formatTime'

const app = createApp(App)

app.use(formatTime)

app.mount('#app')
Copy the code

use

import { getCurrentInstance, inject } from 'vue'

export default {
  name: 'App'.setup() {
    const $formatTime = inject('$formatTime')
    console.log($formatTime(1629564731))}}Copy the code

Implementation 3 -- Use global mixins

define

import dayjs from 'dayjs'

export default {
  install(app) {
    app.mixin({
      methods: {
        formatTime(timeStamp = Date.now() , formatStr = 'YYYY-MM-DD HH:mm:ss') {
          return timeStamp.toString().length === 10 ?
                dayjs.unix(timeStamp).format(formatStr)
                : dayjs(timeStamp).format(formatStr)
        }
      }
    })
  }
}
Copy the code

use

<template>
  <div>

  </div>
</template>

<script>
import { getCurrentInstance, onMounted } from 'vue'
export default {
  name: 'App'.setup() {
  // Only in mounted methods are attributes defined in data and methods mounted
   onMounted(() = > {
      const instance = getCurrentInstance()
      // There is a method on the component instance's CTX to retrieve the methods defined in Methods
      console.log(instance.ctx.formatTime(1629564731))
      // You can also retrieve data defined in data from data
      // console.log(instance.data.name)}}})</script>
Copy the code