4

In some special scenarios, the timing of using the component cannot be determined, or the component we want to use cannot be determined in the Vue template. At this time, we need to dynamically mount the component, or use runtime compilation to dynamically create and mount the component.

Today we will take you from the actual project to see how to dynamically mount components when actually solving customer problems, and show you a complete process of solving dynamic mounting problems.

Unresolved "dynamic mount"

When our spreadsheet control SpreadJS is running, there is such a function: when the user double-clicks a cell, an input box is displayed for editing the content of the cell, and the user can customize the input box according to the requirements of the custom cell type. Form, integrate any form input type.

The creation and destruction of this input box is achieved by inheriting the corresponding method of the cell type, so there is a problem here-this dynamic creation method cannot be simply configured in the VUE template and then used directly.

Not long ago, the customer asked me whether the custom cell of your home control supports Vue components such as ElementUI's AutoComplete ?

Due to the problem mentioned earlier:

After thinking for a long time, I seriously replied to the customer: "The component operation life cycle is inconsistent, and it can't be used." However, I changed the topic again, saying that common components can be used to solve this problem.

The problem was solved smoothly.

But this helpless "unusable" has also become a hurdle that I can't overcome in my dreams at midnight these days.

Later, when I looked at the Vue documentation one day, I thought that the App was mounted on #app during runtime. In theory, other components should also be able to be dynamically mounted to the required Dom, so that the problem of creation timing will not be solved!

Officially open dynamic mounting

Let us continue to check the documentation, the global API Vue.extend( options) is created by extend. Vue instances can be $mount method-this is exactly what we need.

<div id="mount-point"></div>
 
// 创建构造器
var Profile = Vue.extend({
  template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data: function () {
    return {
      firstName: 'Walter',
      lastName: 'White',
      alias: 'Heisenberg'
    }
  }
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('#mount-point')

Follow the SpreadJS custom cell example to create AutoCompleteCellType and set it to the cell:

function AutoComplateCellType() {
}
AutoComplateCellType.prototype = new GC.Spread.Sheets.CellTypes.Base();
AutoComplateCellType.prototype.createEditorElement = function (context, cellWrapperElement) {
//   cellWrapperElement.setAttribute("gcUIElement", "gcEditingInput");
  cellWrapperElement.style.overflow = 'visible'
  let editorContext = document.createElement("div")
  editorContext.setAttribute("gcUIElement", "gcEditingInput");
  let editor = document.createElement("div");
  // 自定义单元格中editorContext作为容器,需要在创建一个child用于挂载,不能直接挂载到editorContext上
  editorContext.appendChild(editor);
  return editorContext;
}
AutoComplateCellType.prototype.activateEditor = function (editorContext, cellStyle, cellRect, context) {
    let width = cellRect.width > 180 ? cellRect.width : 180;
    if (editorContext) {
      // 创建构造器
      var Profile = Vue.extend({
        template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
        data: function () {
          return {
            firstName: 'Walter',
            lastName: 'White',
            alias: 'Heisenberg'
          }
        }
      })
      // 创建 Profile 实例,并挂载到一个元素上。
      new Profile().$mount(editorContext.firstChild);
    }
};

Run, double-click to enter the editing state, but found that an error was reported

According to the error message, we have two solutions at this time:

  1. Turn on runtimeCompiler, add runtimeCompiler: true configuration in vue.config.js to allow runtime compilation, so that the template can be dynamically generated to meet the needs of dynamic components
  2. The pre-compiled template is only dynamically mounted, the components of autocomplete are determined, we can use this method

Create a new AutoComplete.vue component for dynamic mounting, so that the compiled component can be mounted.

<template>
  <div>
    <p>{{ firstName }} {{ lastName }} aka {{ alias }}</p>
  </div>
</template>
<script>
export default {
  data: function () {
    return {
      firstName: "Walter",
      lastName: "White",
      alias: "Heisenberg",
    };
  },
};
</script>
 
 
import AutoComplate from './AutoComplate.vue'
 
 
AutoComplateCellType.prototype.activateEditor = function (editorContext, cellStyle, cellRect, context) {
    let width = cellRect.width > 180 ? cellRect.width : 180;
    if (editorContext) {
      // 创建构造器
      var Profile = Vue.extend(AutoComplate);
      // 创建 Profile 实例,并挂载到一个元素上。
      new Profile().$mount(editorContext.firstChild);
    }
};

Double-click to enter the editing state and see the content in the component

In the next step, for custom cells, you also need to set and get the editing content in the component. At this time, by adding props to the component, and at the same time, you can directly obtain all the props content on the VueComponent instance created when mounting, and the corresponding operation can realize the data. Get settings.

Update AutoComplate.vue, add props, add input for editing

<template>
  <div>
    <p>{{ firstName }} {{ lastName }} aka {{ alias }}</p>
    <input type="text" v-model="value">
  </div>
</template>
<script>
export default {
  props:["value"],
  data: function () {
    return {
      firstName: "Walter",
      lastName: "White",
      alias: "Heisenberg",
    };
  },
};
</script>

Store VueComponent instance through this.vm, get and set Value for VUE component in getEditorValue and setEditorValue methods. After editing, destroy the dynamically created component by calling the $destroy() method.

AutoComplateCellType.prototype.activateEditor = function (editorContext, cellStyle, cellRect, context) {
    let width = cellRect.width > 180 ? cellRect.width : 180;
    if (editorContext) {
      // 创建构造器
      var Profile = Vue.extend(MyInput);
      // 创建 Profile 实例,并挂载到一个元素上。
      this.vm = new Profile().$mount(editorContext.firstChild);
    }
};
 
AutoComplateCellType.prototype.getEditorValue = function (editorContext) {
    // 设置组件默认值
    if (this.vm) {
        return this.vm.value;
    }
};
AutoComplateCellType.prototype.setEditorValue = function (editorContext, value) {
    // 获取组件编辑后的值
    if (editorContext) {
      this.vm.value = value;
    }
};
AutoComplateCellType.prototype.deactivateEditor = function (editorContext, context) {
    // 销毁组件
    this.vm.$destroy();
    this.vm = undefined;
};

The whole process runs through, and all you need to do is replace the input with ElementUI's el-autocomplete and implement the corresponding method in AutoComplate.vue.

result

Let's see the effect.

In fact, dynamic mounting is not a complicated operation. After understanding the Vue example, it is not difficult to operate the example through vm, and flexibly use dynamic mounting or runtime-compiled components.

In fact, all the solutions are in the Vue tutorial introductory tutorial, but the use of scaffolding and the use of various tools make us forget the original intention of Vue, and instead complicate simple problems.

Today’s sharing is over here, and the follow-up will bring you more serious and interesting content~

Do you have anything to "forget your original intention" during development?


葡萄城技术团队
2.7k 声望28.5k 粉丝

葡萄城创建于1980年,是专业的软件开发技术和低代码平台提供商。以“赋能开发者”为使命,葡萄城致力于通过各类软件开发工具和服务,创新开发模式,提升开发效率,推动软件产业发展,为“数字中国”建设提速。