summary
In development projects using Vue, we often use custom instructions to encapsulate a series of DOM operations, which is very convenient. Generally speaking, instructions use dynamic instruction parameters to obtain data in the App.
But sometimes, custom instructions require more data to complete more complex functions, such as calling the nextTick method of the current App instance in the instruction to ensure that all DOM elements are loaded and then perform DOM operations. There are still some cases where we need to pass some global configuration parameters to the instructions. The existing parameter delivery method obviously cannot meet these needs.
This article introduces a method to extend instruction parameters so that they can receive more parameters. This method can be used normally in Vue 2.0 and Vue 3.0.
Basic Principles
The instruction extension method introduced in this article is mainly based on closures and uses some function parameter curling methods to manage the delivery process of multiple parameters.
Let’s use the instructions definition method of Vue2.0 as an example to illustrate the basic principle. The instructions definition methods used in this article have been based on plug-in definition methods, and are used through the use method.
The sample code is as follows:
const myDirective = { install(app,options){ ("img-load", { bind:function(el,binding,vnode){ }, inserted:function(el,binding,vnode){ }, update:function(el,binding,vnode){ }, componentUpdated:function(el,binding,vnode){ }, unbind:function(el,binding,vnode){ }, }); } }; export default myDirective ;
According to the above standard instruction definition method, no matter which hook function is used, we can only pass three parameters, the DOM element bound to the instruction, the bound parameters and virtual nodes in the APP received by the instruction.
Closure-based extension solution
The hook function parameters of the instruction are fixed and we cannot modify them. However, we can set the scope of the hook function through the closure, so that the closure function can receive more parameters.
The code is as follows:
export default function getMyDirective(Vue) { return class MyDirective{ constructor(options) { = options; = (this); } bindDirective(el, bindings) { } } } const myDirective = { install(app,options){ const DirectiveClass = getMyDirective(app) ; var myDirective = new DirectiveClass(options); ("my-dirctive", { bind: }); } };
- Use the closure function getMyDirective to wrap the hook function bindDirective
- The closure function is a user-defined function, we can set any number of parameters
- Define classes in closure functions to encapsulate all operations of instructions, and the constructor can also receive parameters, thereby curling multiple parameters.
- This bind method is forcibly limited to the bindDirective method bound to an instance of MyDirective through the bind method, that is, the bindDirective method can access more data through this.
Functions in JS have independent scopes, so during execution, bindDirective, the binding method of the instruction can use the parameters passed by the closure function without being disturbed by any other external code.
Instance and code implementation
This article takes an automatic image loading instruction as an example to introduce the parameter expansion method of custom instructions.
The basic function of custom instructions is to load and display the picture according to the URL address of the picture. The specific implementation includes:
- Get the image address through dynamic parameters of the instruction
- First, display a loading image on the page
- Load the image of the specified address. If it is loaded successfully, it will be displayed normally.
- Loading failed, displaying an image with loading errors
This article introduces the code implementation of this instance in a top-down way
Globalize the plug-in corresponding to the command
Use the use method to define the plug-in ImageLoad globally. The main function of this plug-in is to define an image loading instruction globally, and receive a global configuration for this instruction, that is, the image address during loading and the image address that failed to load.
(ImageLoad, { loading: "http://localhost:4000/images/", error: "http://localhost:4000/images/", });
ImageLoad plugin definition
The ImageLoad plug-in is the same as other plug-ins. Since it needs to be used through use, you need to define the install method. The first parameter of the install method is the current App instance, and the second is the global configuration of the instruction.
import getImageLoad from './getImageLoad' const ImageLoad = { install(app,options){ const ImgClass = getImageLoad(app) ; var loadImage = new ImgClass(options); ("img-load", { bind: }); } }; export default ImageLoad;
- In the install method, first, by calling the getImageLoad method, get the management class that loads the image and pass it into the current App instance.
- Instantiate the object loadImage of the image load management class, and pass in the global configuration of the image load.
- Defines the custom directive v-img-load, and the specified bind hook method points to the bindImage method in the loadImage.
- This of the bindImage method points to the loadImage object, so you can use the App instance, directive global configuration, and data inside the loadImage object.
Definition of image load management class
ImageLoadManagement defines all implementations of the v-img-load directive.
export default function getImageLoad(Vue) { return class ImageLoadManagement { constructor(options) { = options; = (this); = (this); } bindImage(el, bindings) { const self = this; (function(){ const src = ; ('loading', src, el); (src).then( () => ('', src, el), () => ('error', src, el), ); }); } loadImage(src) { return new Promise((resolve, reject) => { const img = new Image(); = src; = resolve; = reject; }); } renderImage(type, src, el) { let _src; const { error, loading } = ; switch (type) { case 'loading': _src = loading; break; case 'error': _src = error; break; default: _src = src; break; } ("src", _src); } } }
In order to avoid too many parameters, the currying method is used to segment the parameters:
- In the closure function getImageLoad, the global App instance parameters are defined;
-
ImageLoadManagement
The global configuration parameters required for image loading instructions are defined in the constructor of the class. -
class
As syntactic sugar used as function, its essence comes from function, thus achieving independent scope. -
bindImage
In the method, you can directly use the nextTick of the App instance. No matter whether the instruction is used in the parent component or the child component, it can ensure that all DOM elements are loaded after the code is executed in the instruction. -
loadImage
Method is used to check whether the image of the specified URL exists. If it exists, it will display the specific image, otherwise it will display the image that failed to load. -
renderImage
The method is used to set the image address of the Img element bound to the instruction. The actual address of the image can be obtained through the value attribute of the bindings parameter.
Through the above method, we not only extend the parameters of the instruction so that it can support more complex business logic.
More importantly. We have implemented the decoupling of the instruction definition and implementation logic, and there is no need to put all the instruction implementation logic in the instruction registration method. Through the definition of ImageLoadManagement, all instruction implementation logic is clustered into it.
Implementation of Vue 3.0
In Vue 3.0, the idea of the extension method of instruction parameters is consistent with 2.0, but because the hook function name of the instruction in Vue 3.0 is inconsistent with 2.0, some differences are caused.
The specific code is as follows:
import getImageLoad from './getImageLoad' const ImageLoad = { install(app,options){ const ImgClass = getImageLoad(options) ; var loadImage = new ImgClass(); ("img-load", { mounted: }); } }; export default ImageLoad;
export default function getImageLoad(options) { return class ImageLoadManagement { constructor() { = options; = (this); = (this); } bindImage(el, bindings) { const src = ; ('loading', src, el); (src).then( () => ('', src, el), () => ('error', src, el), ); } loadImage(src) { return new Promise((resolve, reject) => { const img = new Image(); = src; = resolve; = reject; }); } renderImage(type, src, el) { let _src; const { error, loading } = ; switch (type) { case 'loading': _src = loading; break; case 'error': _src = error; break; default: _src = src; break; } ("src", _src); } } }
The above is personal experience. I hope you can give you a reference and I hope you can support me more.