完全搞懂elementUI指令与效劳形式道理

不甘做轮子的搬运工!!!

纪录一个练习菜鸟写图片预览组件的艰苦途径~

elementUI许多组件中运用了指令形式和效劳形式,比方:
loading
message

以下以loading组件为例:

指令形式:

<template>
  <div :v-loading.fullscreen="true">全屏掩盖</div>
</template>

效劳形式:

const loading = this.$loading({
  lock: true,
  text: 'Loading',
  spinner: 'el-icon-loading',
  background: 'rgba(0, 0, 0, 0.7)'
});

跟大多数萌新一样,啥是效劳?!

  • 先看看elmentUI的目次组织:

翻开node_modules目次,找到其下elementUI目次:

《完全搞懂elementUI指令与效劳形式道理》

element-ui\src\index.js文件中有一大坨组件注册信息,重点找到我们要找的loading…

// ...
// directive 指令装载
Vue.use(Loading.directive)
// prototype 效劳装载
Vue.prototype.$loading = Loading.service
// ...

Vue.use() 这个指令是 Vue 用来装置插件的,假如传入的参数是一个对象,则该对象要供应一个 install 要领,假如是一个函数,则该函数被视为 install 要领,在 install 要领挪用时,会将 Vue 作为参数传入。

最先叭!

先看看loading/index.js文件中是什么鬼?
《完全搞懂elementUI指令与效劳形式道理》

//引入指令文件和效劳文件,directive为指令形式文件,index.js为效劳形式文件
import directive from './src/directive';
import service from './src/index';

export default {
  //install要领注册组件,不在赘述install的用法,star-pic-list图片预览组件文章中已引见过
  install(Vue) {
    Vue.use(directive);
    //在vue的原型对象上注册一个$loading的对象,这个$loading异常眼熟,看上面效劳形式的运用,用到了this.$loading,泉源找到了
    Vue.prototype.$loading = service;
  },
  //引入的directive文件
  directive,
  //引入的index.js文件
  service
};

v-loading 指令剖析

篇幅太长,个中我们只取 fullscreen 修饰词。

// 引入 .vue 文件
import Vue from 'vue'
// 引入loading.vue基本文件,内里包括的是组件的基本组织,如html组织,loading显现的页面组织都在这内里
import Loading from './loading.vue'
// 背面重点解说extend()组织器
// Vue.extend() 是vue组织器,它返回的是一个扩大实例组织器,也就是预设了部份选项的Vue实例组织器,

// mask字面意义是面具,掩盖,可以猜出来,这个经由过程Vue.extend(Loading)返回组织器应当是用于我们loading加载时的遮罩层用的
// loading就是预设选项,就像vue示例中,有components,name,data,methods...彷佛有点邃晓了
const Mask = Vue.extend(Loading)

const loadingDirective = {}
// 还记得 Vue.use() 的运用要领么?若传入的是对象,该对象须要一个 install 属性
loadingDirective.install = Vue => {
  // toggleLoading 要领看名字就是切换loading显现和隐蔽的嘛~
  const toggleLoading = (el, binding) => {
    // 若绑定值为 truthy 则插进去 loading 元素
    // binding 值是一个对象,有指令名、指令的绑定值、modifiers修饰符对象等等等等,详细的可以去相识自定义指令相干内容
    if (binding.value) {  //binding.value是绑定的指令值
      if (binding.modifiers.fullscreen) {  还记得我们插进去的指令吗?:v-loading.fullscreen="true" ,  .fullscreen就是修饰符
        insertDom(document.body, el, binding)    //insertDom看名字就晓得是插进去新的元素
      }
     // visible 是loading.vue   data内里定义的值
    } else {
      el.instance.visible = false
    }
  }

  const insertDom = (parent, el, binding) => {
    // loading 设为可见
    el.instance.visible = true
    // appendChild 增加的元素若为同一个,则不会反复增加
    parent.appendChild(el.mask)
  }
  // 在此注册 directive 指令
  Vue.directive('loading', {
    bind: function(el, binding, vnode) {
      // 建立一个子组件,这里和 new Vue(options) 相似
      // 返回一个组件实例
      const mask = new Mask({
        el: document.createElement('div'),
        // 有些人看到这里会疑惑,为何这个 data 不根据 Vue 官方发起传函数进去呢?
        // 实在这里二者皆可
        // 轻微做一点延展好了,在 Vue 源码内里,data 是耽误求值的
        // 贴一点 Vue 源码上来
        // return function mergedInstanceDataFn() {
        //   let instanceData = typeof childVal === 'function'
        //     ? childVal.call(vm, vm)
        //     : childVal;
        //   let defaultData = typeof parentVal === 'function'
        //     ? parentVal.call(vm, vm)
        //     : parentVal;
        //   if (instanceData) {
        //     return mergeData(instanceData, defaultData)
        //   } else {
        //     return defaultData
        //   }
        // }
        // instanceData 就是我们如今传入的 data: {}
        // defaultData 就是我们 loading.vue 内里的 data() {}
        // 看了这段代码应当就不难明白为何可以传对象进去了
        data: {
          fullscreen: !!binding.modifiers.fullscreen
        }
      })
      // 将建立的子类挂载到 el 上
      // 在 directive 的文档中发起
      // 应当保证除了 el 以外其他参数(binding、vnode)都是只读的
      el.instance = mask
      // 挂载 dom
      // bind 只会挪用一次,在bind 的时刻给 el.mask 赋值,因而el.mask 所指的为同一个 dom 元素
      el.mask = mask.$el
      // 若 binding 的值为 truthy 运转 toogleLoading
      binding.value && toggleLoading(el, binding)
    },
    update: function(el, binding) {
      // 若旧不等于新值得时刻(平常都是由 true 切换为 false 的时刻)
      if (binding.oldValue !== binding.value) {
        // 切换显现或消逝
        toggleLoading(el, binding)
      }
    },
    unbind: function(el, binding) {
      // 当组件 unbind 的时刻,实行组件烧毁
      el.instance && el.instance.$destroy()
    }
  })
}
export default loadingDirective

关于extend()更多内容请参考
这里,异常通熟易懂!

loading效劳体式格局挪用道理

直接看源码:

import Vue from 'vue'
import loadingVue from './loading.vue'
// 和指令形式一样,建立实例组织器
const LoadingConstructor = Vue.extend(loadingVue)
// 定义变量,若运用的是全屏 loading 那就要保证全局的 loading 只要一个
let fullscreenLoading
// 这里可以看到和指令形式差别的处所
// 在挪用了 close 以后就会移除该元素并烧毁组件
LoadingConstructor.prototype.close = function() {
  setTimeout(() => {
    if (this.$el && this.$el.parentNode) {
      this.$el.parentNode.removeChild(this.$el)
    }
    this.$destroy()
  }, 3000)
}

const Loading = (options = {}) => {
  // 若挪用 loading 的时刻传入了 fullscreen 而且 fullscreenLoading 不为 falsy
  // fullscreenLoading 只会在下面赋值,而且指向了 loading 实例
  if (options.fullscreen && fullscreenLoading) {
    return fullscreenLoading
  }
  // 这里就不用说了吧,和指令中是一样的
  let instance = new LoadingConstructor({
    el: document.createElement('div'),
    data: options
  })
  let parent = document.body
  // 直接增加元素
  parent.appendChild(instance.$el)
  // 将其设置为可见
  // 别的,写到这里的时刻我查阅了相干的材料
  // 本身之前一向明白 nextTick 是在 dom 元素更新终了以后再实行回调
  // 然则发明可以并非这么回事,后续我会继承研讨
  // 假如干货充足的话我会写一篇关于 nextTick ui-render microtask macrotask 的文章
  Vue.nextTick(() => {
    instance.visible = true
  })
  // 若传入了 fullscreen 参数,则将实例存储
  if (options.fullscreen) {
    fullscreenLoading = instance
  }
  // 返回实例,轻易以后可以挪用原型上的 close() 要领
  return instance
}
export default Loading

现学现用-写一个点击图片预览的组件碰运气

目次组织:

directive.js是指令形式文件,index.js是效劳形式文件,star-pic-preview.vue是基本单文件,包括了基本的html组织

《完全搞懂elementUI指令与效劳形式道理》

先看指令形式:

  • 运用:

我直接运用了指令,并没有传参,由于功用简朴,默许参数就是false

 <img
    src="http://img5.imgtn.bdimg.com/it/u=3300305952,1328708913&fm=26&gp=0.jpg"
    v-pic-preview
 >
  • 结果:

点击涌现遮罩层,图片居中显现预览

《完全搞懂elementUI指令与效劳形式道理》

效劳形式:

  • 运用:
<img
    src="http://img4q.duitang.com/uploads/item/201502/22/20150222191447_jdBYa.thumb.700_0.jpeg"
    @click="openImagePreview2"
>
 methods: {
    // 效劳体式格局
    openImagePreview2(e) {
        // 假如只传图片
        this.$picPreview(e.target.src);
        //假如传庞杂对象,可以设置遮罩层的背景色彩等...
        // this.$picPreview({
        //     background: 'rgba(0, 0, 0, 0.7)',
        //     imageUrl: e.target.src,
        // });
    },
}
  • 结果如上

源码请参考github地点: 源码地点

    原文作者:MangoGoing
    原文地址: https://segmentfault.com/a/1190000018713438
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞