search

Home  >  Q&A  >  body text

javascript - 求解读element-ui源码(vue2.0)

主要看main.js,然后解答下我注释里的疑问!谢谢!

先说用法:

test.vue

<template>
  <p id="test" @click="open">
    <!--内容填充,点击p调出message-->
  </p>
</template>

<script>
  import Vue from 'vue'
  import Message from './messages/index.js'//1.引进message组件
  export default {
    methods: {
      open() {
        this.$message({//2.这里应该就是下面源代码mian.js里面的options
          message: '恭喜你,这是一条成功消息',
          type: 'success'
        });
      }
    }
  }
</script>

源码:

message/src/main.js

import Vue from 'vue'
let MessageConstructor = Vue.extend(require('./main.vue'));
let instance;
let instances = [];
let seed = 1;
let Message = function(options) {
  options = options || {};
  if(typeof options === 'string'){
    options = {
      message: options
    };
  }
  let userOnClose = options.onClose;//3.options并没有这个属性啊
  let id = 'message_' + seed++;

  options.onClose = function(){//4.这个如果放到3前面就好解释了,为什么在3后面呢?
    Message.close(id, userOnClose);//5.为什么又把userOnClose传进去了?不就是等于传自己进去吗?
  };
  instance = new MessageConstructor({
    data: options
  });
  instance.id = id;
  instance.vm = instance.$mount();
  instance.vm.visible = true;
  instance.dom = instance.vm.$el;
  instances.push(instance);
};

['succes', 'warning', 'info', 'error'].forEach(type => {
  Message[type] = options => {//6.Message在上文是函数啊,Message哪里跑出来的type属性?
    if(typeof options === 'string'){
      options = {
        message: options
      };
      options.type= type;
      return Message(options);
    };
  }
});

Message.close = function(id, userOnClose){//7.不是很懂这段代码的意思,作用是什么?
  for (let i = 0, len = instances.length; i < len; i++) {
    if (id === instances[i].id) {
      if (typeof userOnClose === 'function') {
        userOnClose(instances[i]);
      }
      instances.splice(i, 1);
      break;
    }
  }
}

export default Message;

message/src/main.vue

<template lang="html">
  <transition name"b-message-fade">
    <p class="b-message" v-show="visible" @mouseenter="clearTimer" @mouseout="startTimer">
      <img class="b-message-icon" :src="imgType" alt=""/>
      <p class="b-message-group">
        <p>{{ message }}</p>
        <p v-if="showClose" class="b-message-closeBtn b-icon-close" @click="handleClose"></p>
      </p>
    </p>
  </transition>
</template>

<script>
export default {
  data() {
    return {
      visible: false,
      message: '',
      duration: 3000,
      type: 'success',
      onClose: null,
      shoClose: false,
      closed: false,
      timer: null
    }
  },
  computed: {
    imgType() {
      return require(`../assets/${ this.type }.svg`);
    }
  },
  watch: {
    closed(newValue) {
      if (newValue) {
        this.visible = false;
        this.$el.addEventListener('transitionend', () => {
          this.$destroy(true);
          this.$el.parentNode.removeChild(this.$el);
        });
      }
    }
  },
  methods: {
    handleClose() {
      this.closed = true;
      if (typeof this.onClose === 'function') {
        this.onClose(this);
      }
    },
    clearTimer() {
      clearTimeout(this.timer);
    },
    startTimer() {
      if (this.message.duration > 0) {
        this.timer = setTimeout(() => {
          if (!this.closed) {
            this.handleClose();
          }
        }, this.duration);
      }
    },
    mouted() {
      this.startTimer();
    }
  }
}
</script>

<style lang="css">
.b-message {
  width: 300px;
  height: 40px;
  box-shadow: 0 2px 4px 0 rgba(0, 0, 0, .12), 0 0 6px 0 rgba(0, 0, 0, .04);
  min-width: 300px;
  min-height: 40px;
  padding: 10px 12px;
  border-radius: 2px;
  position: absolute;
  left: 0;
  right: 0;
  top: 20px;
  margin: auto;
  transition: all .3s ease;
}

.b-message-icon {
  width: 40px;
  height: 40px;
  position: absolute;
  left: 0;
  top: 0;
}

.b-message-group {
  margin-left: 40px;
  position: relative;
}

.b-message-group p {
  font-size: 14px;
  line-height: 20px;
  margin: 0 34px 0 0;
  white-space: nowrap;
  color: #8492a6;
  text-align: justify;
}

.b-message-closeBtn {
  font-family: element-icons!important;
  top: 0;
  right: 0;
  position: absolute;
  cursor: pointer;
  color: #c0ccda;
  font-size: 14px;
}

.b-message-closeBtn:before {
  content: "\E913";
}

.b-message-fade-enter {
  transform: translateY(-100px);
}

.b-message-fade-leave-active {
  transform: translateY(-100px);
}
</style>

message/index.js

module.exports = require('./src/main.js') 
PHP中文网PHP中文网2894 days ago558

reply all(2)I'll reply

  • PHP中文网

    PHP中文网2017-04-10 17:38:38

    import Vue from 'vue';
    import { PopupManager } from 'vue-popup';
    let MessageConstructor = Vue.extend(require('./main.vue'));
    
    let instance;
    let instances = []; // 存放当前未close的message
    let seed = 1; // id叠加计数
    
    var Message = function(options) {
      options = options || {};
      if (typeof options === 'string') { // 参数的兼容处理
        options = {
          message: options
        };
      }
    
      // this.$message({type: 'info',message: 'test',duration: 2000,onClose (vm){console.info(vm)}})
      let userOnClose = options.onClose; // 暂存用户传递的onclose回调
      let id = 'message_' + seed++; // 实例的标识
    
      options.onClose = function(vm) { // 重写options.onClose交由Message.close代理一层
        console.info(2)
        Message.close(id, userOnClose);
      };
    
      instance = new MessageConstructor({ // 创建message实例
        data: options
      });
      instance.id = id;
      instance.vm = instance.$mount(); // 在文档之外渲染
      document.body.appendChild(instance.vm.$el); // 挂载到body
      instance.vm.visible = true; // 设置data.visible为可见
      instance.dom = instance.vm.$el;
      instance.dom.style.zIndex = PopupManager.nextZIndex(); // css z-index层级叠加,覆盖之前已出现但还未close的message
      instances.push(instance);
      return instance.vm;
    };
    
    // 给Message增加四个直接调用的方法
    // 支持this.$message.success('xxx')方式调用,等同于this.$message({type: 'success',message: 'xxx'})
    ['success', 'warning', 'info', 'error'].forEach(type => { 
      Message[type] = options => {
        if (typeof options === 'string') {
          options = {
            message: options
          };
        }
        options.type = type;
        return Message(options);
      };
    });
    
    // 组件的close方法中调用onClose再调该方法
    Message.close = function(id, userOnClose) {
      console.info(3)
      for (let i = 0, len = instances.length; i < len; i++) {
        if (id === instances[i].id) { // 通过id找到该message实例
          if (typeof userOnClose === 'function') {
            userOnClose(instances[i]);
          }
          instances.splice(i, 1); // 移除message实例
          break;
        }
      }
    };
    
    export default Message;
    
    <template>
      <transition name="el-message-fade">
        <p class="el-message" v-show="visible" @mouseenter="clearTimer" @mouseleave="startTimer">
          <!--<img class="el-message__icon" :src="typeImg" alt="">-->
          <p class="el-message__group">
            <p>{{ message }}</p>
            <p v-if="showClose" class="el-message__closeBtn el-icon-close" @click="close"></p>
          </p>
        </p>
      </transition>
    </template>
    
    <script type="text/babel">
      export default {
        data() {
          return {
            visible: false,
            message: '',
            duration: 3000,
            type: 'info',
            onClose: null,
            showClose: false,
            closed: false,
            timer: null
          };
        },
    
        computed: {
          /*typeImg() {
            return require(`../assets/${ this.type }.svg`);
          }*/
        },
    
        watch: {
          closed(newVal) {
            if (newVal) {
              this.visible = false;
              this.$el.addEventListener('transitionend', this.destroyElement);
            }
          }
        },
    
        methods: {
          destroyElement() {
            this.$el.removeEventListener('transitionend', this.destroyElement);
            this.$destroy(true);
            this.$el.parentNode.removeChild(this.$el);
          },
    
          close() {
            console.info(1)
            this.closed = true;
            if (typeof this.onClose === 'function') {
              this.onClose(this);
            }
          },
    
          clearTimer() {
            clearTimeout(this.timer);
          },
    
          startTimer() {
            if (this.duration > 0) {
              this.timer = setTimeout(() => {
                if (!this.closed) {
                  this.close();
                }
              }, this.duration);
            }
          }
        },
    
        mounted() {
          this.startTimer();
        }
      };
    </script>
    
    // main.vue
    // ....
    methods: {
          destroyElement() {
            this.$el.removeEventListener('transitionend', this.destroyElement);
            this.$destroy(true);
            this.$el.parentNode.removeChild(this.$el);
          },
    
          close() {
            console.info(1)
            this.closed = true;
            if (typeof this.onClose === 'function') {
              this.onClose(this);
            }
          },
      }
      ....

    3点 调用message的时候对象里传了onClose就有
    4点 用Message.close来代理处理Message.close
    7点 为啥会有个Message,我也不太明白,我去掉以后,直接调onClose也没啥问题。可能是出于防止内存泄漏的原因,这里需要显式的干掉每次弹出Message创建的Instance。

    reply
    0
  • 大家讲道理

    大家讲道理2017-04-10 17:38:38

    谢邀,我还没有看过。。。
    我给一个猜测吧,应该是什么去抖节流的方法

    reply
    0
  • Cancelreply