命令式组件Message、Dialog的主流写法分析

栏目: IT技术 · 发布时间: 4年前

内容简介:这里还是以element-ui为例,那我们就看看里面的Message。它的dom结构什么的就写在node-modules/element-ui/packages/notification/src/main.vue里面可以看出,这里东西不多,只有一些对应的class、关闭按钮外,就剩下title和message了,这里看到用了slot,所以我们通常也只能将自己的自定义内容放入此插槽中。

这里还是以element-ui为例,那我们就看看里面的Message。

它的dom结构什么的就写在node-modules/element-ui/packages/notification/src/main.vue里面

<template>
  <transition name="el-notification-fade">
    <div
      :class="['el-notification', customClass, horizontalClass]"
      v-show="visible"
      :style="positionStyle"
      @mouseenter="clearTimer()"
      @mouseleave="startTimer()"
      @click="click"
      role="alert"
    >
      <i
       
        :class="[ typeClass, iconClass ]"
        v-if="type || iconClass">
      </i>
      <div :class="{ 'is-with-icon': typeClass || iconClass }">
        <h2 v-text="title"></h2>
        <div v-show="message">
          <slot>
            <p v-if="!dangerouslyUseHTMLString">{{ message }}</p>
            <p v-else v-html="message"></p>
          </slot>
        </div>
        <div
         
          v-if="showClose"
          @click.stop="close"></div>
      </div>
    </div>
  </transition>
</template>

可以看出,这里东西不多,只有一些对应的class、关闭按钮外,就剩下title和message了,这里看到用了slot,所以我们通常也只能将自己的自定义内容放入此插槽中。

js方便就是支持大家常用的点击、关闭回调,里面维护了一个计时器来控制关闭。

我们能将它作为命令式只用的主要实现逻辑是在node-modules/element-ui/packages/notification/src/main.js里面。

这里的核心是Notification方法,这个包导出的也是它。

我们先看大家常用的 this.$message.success 方法是什么

['success', 'warning', 'info', 'error'].forEach(type => {
  Notification[type] = options => {
    if (typeof options === 'string' || isVNode(options)) {
      options = {
        message: options
      };
    }
    options.type = type;
    return Notification(options);
  };
});

通过这里我们可以得知,原来平时使用的 this.$message.success 其实就是 Notification.success ,本质就是一个预先设定了 type 后再调用了 Notification 方法。

let instance;
let instances = [];
let seed = 1;

const Notification = function(options) {
  if (Vue.prototype.$isServer) return;
  options = merge({}, options);
  const userOnClose = options.onClose;
  const id = 'notification_' + seed++;
  const position = options.position || 'top-right';

  options.onClose = function() {
    Notification.close(id, userOnClose);
  };

  instance = new NotificationConstructor({
    data: options
  });

  if (isVNode(options.message)) {
    instance.$slots.default = [options.message];
    options.message = 'REPLACED_BY_VNODE';
  }
  instance.id = id;
  instance.$mount();
  document.body.appendChild(instance.$el);
  instance.visible = true;
  instance.dom = instance.$el;
  instance.dom.style.zIndex = PopupManager.nextZIndex();

  let verticalOffset = options.offset || 0;
  instances.filter(item => item.position === position).forEach(item => {
    verticalOffset += item.$el.offsetHeight + 16;
  });
  verticalOffset += 16;
  instance.verticalOffset = verticalOffset;
  instances.push(instance);
  return instance;
};

上面的 NotificationConstructor 就是

import Main from './main.vue';
const NotificationConstructor = Vue.extend(Main);

使用Vue构造函数生成组件实例 instance ,这和我们默认用vue-cli生成vue项目的main.js是不是很像。

import App from './app.vue';
new Vue({
  render: h => h(App),
  router,
  store: new Vuex.Store(store),
}).$mount("#app");

唯一的区别是我们在 mounted 的时候没有传参数,这意味着我们需要自己将生成的dom插入目标位置,这也更符合我们的预期。

instances 数组是用来存储多个组件实例的,在垂直方向如果有多个组件实例的话,会给它们直接加入16px的偏移量避免它们重叠在一起。

这里还给每个实例更新了 verticalOffset 属性的值来记录垂直方向的偏移量,方便 positionStyle 变更,更新组件的位置。

我们同样可以看到,在移除组件的时候,需要同步变更 instances 数组里面该 instance 后面的实例对应的style样式。

Notification.close = function(id, userOnClose) {
  let index = -1;
  const len = instances.length;
  const instance = instances.filter((instance, i) => {
    if (instance.id === id) {
      index = i;
      return true;
    }
    return false;
  })[0];
  if (!instance) return;

  if (typeof userOnClose === 'function') {
    userOnClose(instance);
  }
  instances.splice(index, 1);

  if (len <= 1) return;
  const position = instance.position;
  const removedHeight = instance.dom.offsetHeight;
  for (let i = index; i < len - 1 ; i++) {
    if (instances[i].position === position) {
      instances[i].dom.style[instance.verticalProperty] =
        parseInt(instances[i].dom.style[instance.verticalProperty], 10) - removedHeight - 16 + 'px';
    }
  }
};

顺便提一下, PopupManager 是element-ui里面公共的通过z-index来确保最新创建的dom永远保持在最上方不被旧的元素遮挡的机制,实现原理也很简单。

鉴于message传参的options.message支持传入vNode,所以会有针对vNode相关的判断和处理逻辑。

简单总结一下,使用Vue构造函数这种方法是常用的命令式组件的套路,类似的还有Dialog(有的组件库叫Modal)、Notice等。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pro JavaScript Techniques

Pro JavaScript Techniques

John Resig / Apress / 2006-12-13 / USD 44.99

Pro JavaScript Techniques is the ultimate JavaScript book for the modern web developer. It provides everything you need to know about modern JavaScript, and shows what JavaScript can do for your web s......一起来看看 《Pro JavaScript Techniques》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具