Vue|Vue.extend构造器是个啥?

vue.extend()方法其实是vue的一个构造器,继承自vue。

在这里插入图片描述
知道了它的涵义,怎么应用更是我们所关注和关心的问题。
因此对extend它的应用,这里着重从3个方面去介绍。

  • 1, 开源组件库mint-ui 中的应用;
  • 2, 本地vue工程中程序实现;
  • 3, 总结extend的主要应用方向;

开源组件库mint-ui 中的应用

开源组件库中Toast[./example/pages/toast/]
在这里插入图片描述

// toast/src/toast.js

import Vue from 'vue';
// 强调一下:我们使用是require('./toast.vue')应该换成es6方式 import toast from './toast.vue' , 否则会返现创建子类el非dom对象类型,而是comment
/**
正确的引入方式:
import toast from './toast'
const ToastConstructor = Vue.extend(toast); 
*/
const ToastConstructor = Vue.extend(require('./toast.vue')); /** extend,vue构造器,创建‘./toast.vue’ 的子类 */
let toastPool = [];

let getAnInstance = () => { /** 创建实例,如果toastPool中有实例则不再重复创建。起到单例效果 */
  if (toastPool.length > 0) {
    let instance = toastPool[0];
    toastPool.splice(0, 1);
    return instance;
  }
  return new ToastConstructor({
    el: document.createElement('div') /** 通过el,操作dom。给toast外层创建一个<div> */
  });
};

let returnAnInstance = instance => {
  if (instance) {
    toastPool.push(instance); /** toast实例装箱缓存 */
  }
};

let removeDom = event => {/** 操作dom,用作移除,通过上面创建的<div>放入某页面中的toast.vue */
  if (event.target.parentNode) {
    event.target.parentNode.removeChild(event.target);
  }
};
/** 关闭弹窗close方法 */
ToastConstructor.prototype.close = function() {
  this.visible = false;
  this.$el.addEventListener('transitionend', removeDom);
  this.closed = true;
  returnAnInstance(this);
};

/** 调起弹窗的方法,使用时直接通过方法调用即可。如: Toast(options) */
let Toast = (options = {}) => {
  let duration = options.duration || 3000;

  let instance = getAnInstance(); // 获取vue实例
  instance.closed = false; // instance对象参数close。用以toast.vue中变量的动态赋值,控制弹窗的关闭 
  clearTimeout(instance.timer);
  instance.message = typeof options === 'string' ? options : options.message; // instance对象参数message 。用以toast.vue中变量的动态赋值 ,弹窗显示的提示文字
  instance.position = options.position || 'middle'; // instance对象参数position 。用以toast.vue中变量的动态赋值,弹窗显示的页面位置
  instance.className = options.className || ''; // instance对象参数className。用以toast.vue中变量的动态赋值 ,弹窗样式控制
  instance.iconClass = options.iconClass || ''; // instance对象参数iconClass 。用以toast.vue中变量的动态赋值 ,弹窗样式中是否有图标

  document.body.appendChild(instance.$el); // 通过①创建的外层<div>,将toast的整个dom添加入body中
  // 将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。
  Vue.nextTick(function() {/** toast update之后,回调 */
    instance.visible = true;
    instance.$el.removeEventListener('transitionend', removeDom); // 从页面中移除toast 
    ~duration && (instance.timer = setTimeout(function() {// 定时器,自动关闭弹窗
      if (instance.closed) return;
      instance.close();
    }, duration));
  });
  return instance;
};

export default Toast;

从源码中注释中,我们大概能对extend使用上知道个原委。其实就是通过vue.extendtoast.vue创建出一个dom子类。且该dom字类f封装之后是自由自在、随取随用的。若某页面放入弹窗,即该dom子类将通过代码document.body.appendChild(instance.$el);以绝对定位方式加入到页面中,也就是最初的哪个html(~/public/index.html)中。

其中这里要重点介绍的是一个toast的vue组件,它的view模板的样式是怎么控制,进行动态的个性化设置的?看下面templete模板的截图:
在这里插入图片描述
通过在脚本<scirpt>中豫定义了props和data,在该.vue通过extend创建的字类,并通过new命令创建出的实例instance来对props和data中绑定的变量进行赋值。从而实现自定义的动态样式。
在这里插入图片描述

源代码应用到工程中

这里应用到工程中,是将mint-ui组件库中的源码拷贝出来,粘贴到本地工程中。这里讲述局部配置
在这里插入图片描述
将组件库mint-ui中的自定义组件toast程序,直接拷贝到自己的工程目录中。但是在使用的时候,还是需要作稍微的修改的。改动:

  • 1,原mint-ui组件中style样式不能用了。
  • 2, const ToastConstructor = Vue.extend(require('./toast.vue'));这种引入方式要换掉。
// ./acc-transfer-container.vue  本地应用,局部引入

<template >
  <div class="acc-transfer-container" ref="template">
    
    ... ...
    /** 点击“下一步”按钮,调用方法sayHello */
    <el-button type="danger" round size="medium" @click="sayHello">下一步</el-button>
  </div>
</template>

<script>
import Toast from '@/views/toast' /** 局部引入,在页面import引入,然后...向下看 */
export default { 
  .. ...

  methods: {
    ... ...
    sayHello(){
    /** 通过点击”下一步“按钮,在这里当作方法一样直接使用,灵活度极高 */
        Toast({
          message: '坏人 , 说句hello听听~',
          position: 'middle',
          duration: 3000
        });
    },
   ... ...
    
  },
 ... ...
};
</script>

<style lang='scss'>
body {
  margin: 0 0;
  padding: 0 0;
  background-color: #f3f3f3;
}
... ...
</style>

本地vue工程中程序实现

自定义一个消息弹窗组件,仿照mint-ui组件库中的源码进行编码。这里讲述全局配置
对于./message.vue文件

// ./message.vue

<template>
    <div class="wrap">
        <div class="message" :class="item.type" v-for="item in notices" :key="item._name">
            <div class="content">{{item.content}}</div>
        </div>
    </div>
</template>

<script>
  // 默认选项
  const defaultOptions = {
    duration: 1000,
    type: 'info',
    content: '一条提示信息!',
  }
  let mid = 0
  export default {
    name:'MyMessage', // 建议添加方便外面直接取值
    data() {
      return {
        notices: [] // 用于存放message弹窗内容的某条信息
      }
    },
    methods: {
    /** 通过message.js中的vue实例,调用该方法。 */
      addMessage(notice = {}) {
        // name标识 用于移除弹窗
        let _name = this.getName()
        // 合并选项
        notice = Object.assign({
          _name
        }, defaultOptions, notice)
		//装入数据,某条信息
        this.notices.push(notice)
		//延时移除某条信息
        setTimeout(() => {
          this.removeNotice(_name)
        }, notice.duration) 
      },
      getName() {
        return 'msg_' + (mid++)  //创建一个唯一的值 
      },
      /** 移除缓存到数组中的内容 */
      removeNotice(_name) {
        let index = this.notices.findIndex(item => item._name === _name)
        this.notices.splice(index, 1)  // 删除当前超时的dom
      }
    }
  }
</script>

<style scoped>
    .wrap {
        position: fixed;
        top: 50px;
        left: 50%;
        display: flex;
        flex-direction: column;
        align-items: center;
        transform: translateX(-50%);
    }
    ... ...
</style>
// ./message.js

import Vue from 'vue'
import Message from './message.vue' // 引入vue模块

let messageInstance = null // 定义消息弹窗的实例

let init = (options) => {
  let MessageConstructor = Vue.extend(Message) // 构造Message的vue子类

  /** 创建子类实例 */
  messageInstance = new MessageConstructor({}) // 构造函数可以接传值,data、methods.....
  console.log('mint-ui  messageInstance>>>>', messageInstance)
  // $mount()不带参数,会把组件在内存中渲染完毕
  messageInstance.$mount() // 如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount() 手动地挂载一个未挂载的实例。返回值:vm - 实例自身

  // messageInstance.$el 拿到的就是组件对应的dom元素,可以直接操作dom
  document.body.appendChild(messageInstance.$el) /** 这里就被植入到了body中 */
  messageInstance.$el.style.zIndex = 9999  // 设置该消息弹窗的样式展示层次
}


let caller = (options) => {
  if (!messageInstance) {
    // 进页面初始化
    init(options)
  }
  // addMessage 是组件内部声明的方法,也可以通过构造函数传对应的方法
  // 通过该方法实现对message.vue中绑定变量的动态赋值
  messageInstance.addMessage(options)
}
// export default caller; /** 第一种对外导出的方式,供局部引入配置使用 */

/** 第二种对外导出的方式,通过此方式install(vue),可以在main.js中进行全局配置使用 */
 export default {
   install(vue) {
     vue.prototype.$mymessage = caller
   }
 }

在main.js中进行全局配置使用,看这里:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import Message from '@/views/msg' //全局引入,第一步
/** 安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作为 install 方法。install 方法调用时,会将 Vue 作为参数传入。 */
Vue.use(Message)                  //全局引入,第二步

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app') /** $mount 是将vue替换了#app标签 */

应用到页面中,看这里:

//Bank-Acc-Transfer-c.vue

<template >
  <div class="acc-transfer-container" ref="template">
    ... ...
    /** 点击按钮 "下一步"  调用方法sayHello() 弹出消息弹窗*/
    <el-button type="danger" round size="medium" @click="sayHello()">下一步</el-button>
  </div>
</template>

<script>
export default {
 ... ...
  methods: {
    ... ...
    sayHello( ){
    /** 弹窗的展示,通过直接调用Message(options)方法 */
    /** 注意:这里是全局的,没有通过import引入哦 。*/
    /** 如果以局部引入,通过import引入即可 */
      Message({content: '玩呢,这样也可以实现 !'});
    }
    
  },
  ... ...
};
</script>

<style lang='scss'>
body {
  margin: 0 0;
  padding: 0 0;
  background-color: #f3f3f3;
}
... ...
</style>

总结extend的主要应用方向

使用性上来看,用到它的地方主要以灵活性为主。它的灵活性,能起到高度解耦的作用。
为什么这么说,使用extend创建的vue子类后,可以动态植入到html的body中。而且可以做到随时植入,随时移除,高度解耦,快捷方便。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值