Foreword
For components that need to be reused frequently within the project, and based on the actual project environment, encapsulate some common plugins, This article is ‘previewImage.js’
Plugin encapsulated based on personal project environment, only this article introduces the idea of encapsulating Plugin, so the relevant code may not be applicable to others.
Project Environment
Vue 3.x + Ant Design Vue 3.x + Vite 3.x
Background Introduction
In the management background of research and development, the pictures that need to be previewed are involved. The UI frame’ AntDesignVue3.x’ is used in this way:
<template>
<div>
<a-button type="primary" @click="() => setVisible(true)">show image preview</a-button>
<a-image
:width="200"
:style="{ display: 'none' }"
:preview="{
visible,
onVisibleChange: setVisible,
}"
src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
/>
</div>
</template>
<script lang="ts">
import { defineComponent, ref } from 'vue';
export default defineComponent({
setup() {
const visible = ref<boolean>(false);
const setVisible = (value): void => {
visible.value = value;
};
return {
visible,
setVisible,
};
},
});
</script>
Of course, there are also other situations:
- there are many places to preview pictures in the background
- The image src needs to be dynamically modified. In the above two cases, in order to avoid writing duplicate code and reduce the writing of control variables and event code, when optimizing for the code level, secondary encapsulation can be considered
Encapsulation and Decomposition: Core Code
const container = document.createDocumentFragment();
function render(props) {
const vm = createVNode(Image, { ...props });
vueRender(vm, container);
return vm;
}
Based on the above code, a virtual node is created to realize the basis of single picture preview. The following is an explanation of the contents used by the way, which is to reinforce relevant technical knowledge again ~
Encapsulation and Decomposition: render
export declare const render: RootRenderFunction<Element | ShadowRoot>;
export declare type RootRenderFunction<HostElement = RendererElement> = (vnode: VNode | null, container: HostElement, isSVG?: boolean) => void;
The ‘render’ function is a new function in Vue2.x
In Vue, we use template HTML syntax to build pages. Using the’ render’ function, we can build DOM in Js language. Because Vue is a virtual DOM, we also need to translate it into VNode function when we get the Template template. Using the’ render’ function to build DOM ,Vue eliminates the process of translation, thus improving performance
In the core code, the ‘vueRender’ function accepts two parameters. Let’s take a look at the first parameter’ vm’
Encapsulation and Decomposition: createVNode
export declare const createVNode: typeof _createVNode;
declare function _createVNode(type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT, props?: (Data & VNodeProps) | null, children?: unknown, patchFlag?: number, dynamicProps?: string[] | null, isBlockNode?: boolean): VNode;
export declare interface VNode<HostNode = RendererNode, HostElement = RendererElement, ExtraProps = {
[key: string]: any;
}> {
/* Excluded from this release type: __v_isVNode */
/* Excluded from this release type: __v_skip */
type: VNodeTypes;
props: (VNodeProps & ExtraProps) | null;
key: string | number | symbol | null;
ref: VNodeNormalizedRef | null;
/**
* SFC only. This is assigned on vnode creation using currentScopeId
* which is set alongside currentRenderingInstance.
*/
scopeId: string | null;
/* Excluded from this release type: slotScopeIds */
children: VNodeNormalizedChildren;
component: ComponentInternalInstance | null;
dirs: DirectiveBinding[] | null;
transition: TransitionHooks<HostElement> | null;
el: HostNode | null;
anchor: HostNode | null;
target: HostElement | null;
targetAnchor: HostNode | null;
/* Excluded from this release type: staticCount */
suspense: SuspenseBoundary | null;
/* Excluded from this release type: ssContent */
/* Excluded from this release type: ssFallback */
shapeFlag: number;
patchFlag: number;
/* Excluded from this release type: dynamicProps */
/* Excluded from this release type: dynamicChildren */
appContext: AppContext | null;
/* Excluded from this release type: ctx */
/* Excluded from this release type: memo */
/* Excluded from this release type: isCompatRoot */
/* Excluded from this release type: ce */
}
‘createVNode(Image, {…props})’, accepted parameters: “defined element”, “extended attribute of element”
According to actual business requirements, the first parameter (defined element) is’ Image’ component. In order to ensure global use, we can control variables that need to be written in general, such as component display’ visible’, component picture source address’ src’, and trigger event’ events’ on the component can be passed in’ props. In this way, the creation of virtual nodes is realized.
Since the parameters accepted in ‘render: RootRenderFunction’ and ‘container: HostElement’ are required, let’s take a look at the second parameter ‘container’ in the core code’
Encapsulation and Decomposition: createDocumentFragment
’const container = document.createDocumentFragment()’creates a new blank document fragment I .e. virtual DOM.
DocumentFragments (en-US) are DOM nodes. They are not part of the main DOM tree. The usual use case is to create a document fragment, attach elements to the document fragment, and then attach the document fragment to the DOM tree. In the DOM tree, a document fragment is replaced by all of its child nodes.
Because the document fragment exists in memory, not in the DOM tree, inserting child elements into the document fragment does not cause page reflow (calculation of element position and geometry). Therefore, using document fragments generally results in better performance.
Complete Code: previewImage.js
let previewImageInstance;
const container = document.createDocumentFragment();
function render(props) {
const vm = createVNode(Image, { ...props });
vueRender(vm, container);
return vm;
}
// 关闭
function close() {
update('', false);
}
// 销毁
function destroy() {
if (previewImageInstance) {
vueRender(null, container);
previewImageInstance.component.update();
previewImageInstance = null;
}
}
// 更新
function update(src, visible) {
previewImageInstance.component.props.preview.visible = visible;
previewImageInstance.component.props.src = src;
previewImageInstance.component.update();
}
// 显示
function show(src) {
if (previewImageInstance) {
update(src, true);
} else {
previewImageInstance = render({
preview: { visible: true, onVisibleChange: close },
src,
});
}
}
const previewImage = {
show,
destroy,
};
export default previewImage;
Actual Use
In the page file where preview images are required
const { proxy } = getCurrentInstance();
/* 实际使用,同样的,可以在任何需要使用的地方调用事件 */
proxy.$previewImage.show(src);
Divergent Thinking: How can we implement a multi-image preview?
In fact, the encapsulated Plugin currently only supports single picture preview, that is, when multiple pictures appear in the page at the same time, there is no AntDesignVue preview display view layer can be switched back and forth function, then how to achieve it?
You can do this, that is, pass in an array, and’ render’, the vnodes of the component tree must be unique, then you can use a loop to loop out multiple Images, which can be written in official website
function render() {
return h(
'div',
Array.from({ length: 20 }).map(() => {
return h('p', 'hi')
})
)
}