Introduction to the

Vue.extend, as a global API, is certainly worth studying in depth, but also an important way to implement programmatic components, so let’s start with the source guide plus practice. First we will study with a few questions, if you don’t know any, hahaha congratulations, you will understand by the end of this article.

  • What role does vue. extend play in Vue?
  • What about the vue.extend internal implementation?
  • Implement a programmatic component, say concrete idea?

directory

  • Basic usage
  • Source appreciation
  • Introduction to the source code
  • Manually implement a programmatic component
  • conclusion

Basic usage

Parameters:

{Object} options

Usage:

Using the base Vue constructor, create a “subclass”. A parameter is an object that contains component options.

Note that the data option is a special case – in vue.extend () it must be a function.

<div id="mount-point"></div>
Copy the code
Var Profile = vue.extend ({template:'<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data: function () {
    return {
      firstName: 'Walter',
      lastName: 'White'.alias: 'Heisenberg'}}}) // Create a Profile instance and mount it to an element. new Profile().$mount('#mount-point')
Copy the code

Here are the results:

<p>Walter White aka Heisenberg</p>
Copy the code

Source appreciation

Before we can appreciate the source code, we must first find the API and the location of the call so that we can better understand what it does. We know from the official usage that it must have something to do with the component, so we can easily find the location in the source code to create the component, so let’s go one step at a time:

1 Create component: in SRC /core/vdom/create-element:(113)

     vnode = createComponent(Ctor, data, context, children, tag)
Copy the code

CreateComponent: SRC /core/vdom/ create-Component: (115)

if (isObject(Ctor)) {
    Ctor = baseCtor.extend(Ctor)
  }
Copy the code

Here, baseCtor is actually Vue, the specific reason will be explained in the future analysis of the component source code, here do not study. We’ve finally found where vue.extend is called when Vue creates each component, creating subclasses via the constructor. Here is the complete source code:

Vue.cid = 0
  let cid = 1
Vue.extend = function (extendOptions: Object): Function {
    extendOptions = extendOptions || {}
    const Super = this
    const SuperId = Super.cid
    const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId]
    }

    const name = extendOptions.name || Super.options.name
    if(process.env.NODE_ENV ! = ='production' && name) {
      validateComponentName(name)
    }

    const Sub = function VueComponent (options) {
      this._init(options)
    }
    Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    Sub.cid = cid++
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
    Sub['super'] = Super

    // For props and computed properties, we define the proxy getters on
    // the Vue instances at extension time, on the extended prototype. This
    // avoids Object.defineProperty calls for each instance created.
    if (Sub.options.props) {
      initProps(Sub)
    }
    if (Sub.options.computed) {
      initComputed(Sub)
    }

    // allow further extension/mixin/plugin usage
    Sub.extend = Super.extend
    Sub.mixin = Super.mixin
    Sub.use = Super.use

    // create asset registers, so extended classes
    // can have their private assets too.
    ASSET_TYPES.forEach(function (type) {
      Sub[type] = Super[type]
    })
    // enable recursive self-lookup
    if (name) {
      Sub.options.components[name] = Sub
    }

    // keep a reference to the super options at extension time.
    // later at instantiation we can check if Super's options have // been updated. Sub.superOptions = Super.options Sub.extendOptions = extendOptions Sub.sealedOptions = extend({}, Sub.options) // cache constructor cachedCtors[SuperId] = Sub return Sub } }Copy the code

Let’s go through the source code step by step.

Introduction to the source code

a:

  extendOptions = extendOptions || {}
    const Super = this
    const SuperId = Super.cid
Copy the code

First, extendOptions lets us pass in the template, where this is the object that calls extend, which is Vue, and then store it in the variable Super, where SuperId holds the unique id in Vue (each instance has its own unique CID).

B:

 const cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {})
    if (cachedCtors[SuperId]) {
      return cachedCtors[SuperId]
    }
Copy the code

This section, as a caching strategy, is described below.

c:

 const name = extendOptions.name || Super.options.name
    if(process.env.NODE_ENV ! = ='production' && name) {
      validateComponentName(name)
    }
Copy the code

Use a name variable to hold the name of the component, which is the name I wrote to the component. If not, use the name of the parent component. Then use the validateComponentName function to verify that the name cannot be an HTML element or an invalid name.

d:

Sub.prototype = Object.create(Super.prototype)
    Sub.prototype.constructor = Sub
    Sub.cid = cid++
Copy the code

We created a subclass Sub, where we inherited the ability of Sub to have Vue and added unique IDS (unique identifiers for each component).

e:

 Sub.options = mergeOptions(
      Super.options,
      extendOptions
    )
    Sub['super'] = Super
Copy the code

The mergeOptions function is called to merge the options of the superclass and the options of the subclass, and the super property of the subclass points to the superclass.

f:

if (Sub.options.props) {
      initProps(Sub)
    }
    if (Sub.options.computed) {
      initComputed(Sub)
    }
Copy the code

Props and computed are initialized.

g:

 // allow further extension/mixin/plugin usage
    Sub.extend = Super.extend
    Sub.mixin = Super.mixin
    Sub.use = Super.use

    // create asset registers, so extended classes
    // can have their private assets too.
    ASSET_TYPES.forEach(function (type) {
      Sub[type] = Super[type]
    })
    // enable recursive self-lookup
    if (name) {
      Sub.options.components[name] = Sub
    }
    Sub.superOptions = Super.options
    Sub.extendOptions = extendOptions
    Sub.sealedOptions = extend({}, Sub.options)
Copy the code

Copies of the parent class method to subclass, including the extend, mixins, the use of component, directive, the filter. Properties superOptions, extendOptions, sealedOptions are also added.

h:

 // cache constructor
    cachedCtors[SuperId] = Sub
Copy the code

If the subclass is constructed, the id value of the parent class is returned directly, which means that the subclass itself does not need to be reinitialized, as a caching strategy.

In general, you create a Sub function that inherits from its parent.

Manually implement a programmatic component

When we call a component, we usually register the component first and then refer to it in the template. One or two components is acceptable, but if there are many, many, many components, so every time you sign up for the application, it will be too much. As we all know from using element-UI, you can call it directly from a command without registering:

this.$message.success('It worked')
Copy the code

Is not particularly convenient, simple and clear, but also in line with the programming thinking. Next, we will implement it manually:

A: Create a component (to be called programmatically)

<template>
  <div class='toast'
       v-show='isShow'>
    {{message}}
  </div>
</template>
<script>
export default {
  data () {
    return {
      message: ' ',
      isShow: false
    }
  },
  methods: {
    show (message, duration) {
      this.message = message
      this.isShow = true
      setTimeout(() => {
        this.isShow = false
        this.message = ' '
      }, duration)
    }
  }
}
</script>
<style scoped>
.toast {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 999;
  padding: 8px 10px;
  background-color: rgba(0, 0, 0, 0.3);
}
</style>
Copy the code

This component is very simple, with the two variables being the message to display and whether to display. I’ve also defined a method that makes the template unhidden and passes in a duration parameter to indicate the time to be displayed on the timer. IsShow is false when the time is reached.

B: Implement programming

import Toast from './toast'
const obj = {}
obj.install = function(Vue) {// create constructor const ToastContrystor = vue.extend (Toast) // new method according to the component constructor, We can create a component object const toast = new ToastContrystor() // manually mount the toast on an element.$mount(document.createElement('div'))
  // toast.$elThe corresponding div is the document. The body. The appendChild (toast.$el) // Mount the component to the Vue prototype.$toast = toast
}
export default obj
Copy the code

Using the component as a plug-in through vue.use (), the comments are clearly written and understood step by step.

C: Register with main.js

import Vue from 'vue'
import toast from '@/components/common/toast/index.js'
Vue.use(toast)
Copy the code

D:

   this.$toast.error('Registration failed, please try again', 1000).Copy the code

It can be called anywhere in the project. We have implemented a programmatic component.

conclusion

VUe. Extend basically creates a class that inherits from its parent. The top level must be VUe. This class represents a component that can be created using the new method. By learning Extend, we can easily implement a programmatic component.