VUE

Image Preview in the Public Plugin Package

Image Preview in the Public Plugin Package

Foreword

For components that need to be frequently reused within the project, and based on the actual project environment, we encapsulate some common plugins. This article is for ‘previewImage.js’.

The plugin is packaged based on a personal project environment, and this article only aims to share insights into the plugin packaging process; therefore, the associated code may not be suitable for others.

Project Environment

Vue 3.x + Ant Design Vue 3.x + Vite 3.x

Background Introduction

In the management backend we developed, when it comes to images that need to be previewed, and in conjunction with the UI framework ‘Ant Design Vue 3.x’, the typical approach is as follows:

<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:

  1. There are many places in the backend where image previews are required.
  2. 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 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 regarded as another reinforcement of relevant technical knowledge ~

Encapsulation 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 feature introduced in Vue 2.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
Within the core code, the vueRender function accepts two parameters. Next, let’s examine 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})’ accepts the following arguments: “the defined element” and “the element’s extended attributes”. 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. Thus, the creation of the virtual node is completed.
Since the ‘container: HostElement’ parameter accepted by ‘render: RootRenderFunction’ is required, let’s now examine the second parameter, ‘container’, in the core code.

Encapsulated decomposition: createDocumentFragment

const container = document.createDocumentFragment() creates a new empty document fragment, which serves as a virtual DOM. DocumentFragments (en-US) are DOM nodes. They are not part of the main DOM tree. A common use case is to create a document fragment, append elements to the fragment, and then append the fragment to the DOM tree. In the DOM tree, the document fragment is replaced by all of its child elements.
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 a preview image is 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')
  })
  )
}

-AntDesignVue-Image(https://www.antdv.com/components/image-cn#API) -Vue render function(https://cn.vuejs.org/guide/extras/render-function.html) -Document.createDocumentFragment()(https://developer.mozilla.org/en-US/docs/Web/API/Document/createDocumentFragment)

Related posts

View all →