Update: Thank you for your support, recently toss a summary of information, convenient for you to read the system, there will be more content and more optimization, click here to view

—— The following is the text ——

Vue advanced series summary below, welcome to read.

Vue advanced series (1) response formula principle and implementation

Vue advanced series (2) plug-in principle and implementation

Vue advanced series (3) Render function principle and implementation

Principle of Render function

According to the responsivity principle introduced in the first article, it is shown in the figure below.

In the initialization phase, which essentially happens in the Auto Run function, the Render function generates the Virtual DOM, and the View generates the Actual DOM from the Virtual DOM. Because the Render function relies on all the data on the page and that data is reactive, all the data is dependent on the component’s Render function. As soon as the data changes, the render function is called again.

In the update phase, the Render function is called again and returns a new Virtual Dom, the old and new Virtual Dom are compared, and the minimal changes since diff are applied to the Actual Dom.

Watcher is responsible for collecting dependencies, clearing dependencies, and notifying dependencies. In a large and complex component tree structure, overrendering of components is avoided due to an accurate dependency tracking system.

The Actual DOM and Virtual DOM

The Actual DOM generates a DOM node with document.createElement(‘div’).

document.createElement('div')

// Browser native objects (expensive)
"[object HTMLDivElement]"
Copy the code

The Virtual DOM generates a JS object with vm.$createElement(‘div’). The VDOM object has a tag attribute representing a div, a data attribute containing all possible attributes, and possibly a children list containing more Virtual nodes.

vm.$createElement('div')

// Pure JS objects (lightweight)
{ tag: 'div'.data: { attrs: {}, ...}, children: []}Copy the code

Because the Virtual DOM’s rendering logic is decoupled from the Actual DOM, it is capable of running in a non-browser environment. This is why hybrid development became popular after the emergence of the Virtual DOM. React Native and Weex are able to implement this principle.

JSX and Template

Both JSX and Template are a way to declare the relationship between DOM and state. Template is the default recommended way in Vue, but you can use JSX to be more flexible.

JSX is more dynamic, which is helpful for using programming languages to do anything, but dynamic makes compilation optimization more complex and difficult.

Template is more static and has more constraints on expressions, but you can quickly reuse existing templates. Template constraints mean more performance optimization at compile time, and an advantage over JSX in compile time.

Example 1: Implement the Example component

The requirements are as follows

<example :tags="['h1', 'h2', 'h3']"></example>
Copy the code

The required output is as follows

<div>
  <h1>0</h1>
  <h2>1</h2>
  <h3>2</h3>
</div>
Copy the code

This can be done with the Render function, and the createElement function is provided to generate the template. The createElement method (‘ div ‘, {}, […]. The acceptable parameters are as follows.

// @returns {VNode}
createElement(
  // {String | Object | Function}
  // An HTML tag string, component option object, or
  // Parse an async function of any of the above. Required parameters.
  'div'.// {Object}
  // A data object containing attributes related to the template
  // You can use these features in template. This parameter is optional.{},// {String | Array}
  // Child virtual nodes (VNodes), built from 'createElement()',
  // You can also use strings to generate "text virtual nodes". This parameter is optional.
  [
    'Write some words first.',
    createElement('h1'.'A headline'),
    createElement(MyComponent, {
      props: {
        someProp: 'foobar'}})])Copy the code

Once you know how to use it, you can return the virtual node generated by createElement in render. The outer layer is div, and the inner layer is h1, H2, h3, so you need to iterate through the inner layer with two CreateElements.

It is common practice for Vue and JSX to use H as the alias for createElement.

To achieve the following

<! - reference -- >
<script src=".. /node_modules/vue/dist/vue.js"></script>

<! -- Define template -->
<div id="app">
  <example :tags="['h1', 'h2', 'h3']"></example>
</div>

<script>
    // Define the example component
    Vue.component('example', {
      props: ['tags'],
      render (h) {
        
        // The second argument is an optional data object containing attributes related to the template
        
        // The VNodes argument can be passed in as a string or a number,
        // Generated by createElement, optional
        return h('div'.this.tags.map((tag, i) = > h(tag, i)))
      }
    })
    
    / / instantiate
    new Vue({ el: '#app' })
</script>
Copy the code

Example 2: Implement dynamic<example>component

For the following

  • To implement aFooThe component rendering<div>foo</div>, implement aBarThe component rendering<div>bar</div>.
  • To implement a<example>Component, by propertyokDynamic renderingFooComponent orBarComponents. If the propertyokistrue, then the final render should be<div>foo</div>.
  • Implement a button control propertyok, through this property let<example>inFooorBarTo switch between.

As described above, call the < Example > component in the template and define the

To achieve the following

<! - reference -- >
<script src=".. /node_modules/vue/dist/vue.js"></script>

<! -- Define template -->
<div id="app">

  <! -- Bind attribute ok-->
  <example :ok="ok"></example>
  
  <! -- Bind click events -->
  <button @click="ok = ! ok">toggle</button>
</div>

<script>
    / / define Foo
    const Foo = {
      render (h) {
        return h('div'.'foo')}}/ / define the Bar
    const Bar = {
      render (h) {
        return h('div'.'bar')}}// Define the example component
    // Dynamically switch according to the OK attribute
    Vue.component('example', {
      props: ['ok'],
      render (h) {
        return h(this.ok ? Foo : Bar)
      }
    })
    
    / / instantiate
    new Vue({
      el: '#app'.data: { ok: true}})</script>
Copy the code

Example 3: Implementation components

For the following

  • To implement awithAvatarURLFunction that requires passing in a string withurlProperty to return a receiveusernameProperty, which is responsible for getting the corresponding avatar URL.
  • The higher-order component will placeholder the URL before the API returnshttp://via.placeholder.com/200x200Pass to the internal component.

Examples are as follows

const SmartAvatar = withAvatarURL(Avatar)

// Use this method
<smart-avatar username="vuejs"></smart-avatar>

// Replace the following method
<avatar url="/path/to/image.png"></avatar>
Copy the code

withAvatarURLThe function returns an object that receivesusernameProperties, in the life cyclecreatedGets the avatar URL.AvatarObject to receivesrcProperties,srcFrom the content of thewithAvatarURL, and then displayed inOn. When instantiated, pass in the name of the newly defined componentSmartAvatar.

To achieve the following

<! - reference -- >
<script src=".. /node_modules/vue/dist/vue.js"></script>

<! - defines the template - >
<div id="app">
  <smart-avatar username="vuejs"></smart-avatar>
</div>

<script>
    // Get the avatar URL
    function fetchURL (username, cb) {
      setTimeout(() = > {
        // Get the avatar and send it back
        cb('https://avatars3.githubusercontent.com/u/6128107?v=4&s=200')},500)}// The InnerComponent passed
    const Avatar = {
      props: ['src'].template: `<img :src="src">`
    }
    
    function withAvatarURL (InnerComponent) {
      return {
        props: ['username'].inheritAttrs: false.// 2.4 only, the component will not render unregistered props as normal HTML properties
        data () {
          return { url: null }
        },
        created () {
          // Get the avatar URL and pass it back to this. URL
          fetchURL(this.username, url= > {
            this.url = url
          })
        },
        render (h) {
          return h(InnerComponent, {
            attrs: this.$attrs, // 2.4 only, the unused registration attribute is obtained
            props: {
              src: this.url || 'http://via.placeholder.com/200x200'}})}}}const SmartAvatar = withAvatarURL(Avatar)
    
    // instantiate the new component named SmartAvatar or smart-Avatar
    new Vue({
      el: '#app'.components: { SmartAvatar }
    })
</script>

Copy the code

This article refers to the rendering function & JSX from VUE’s official website