美国服务器的vue引用组件的几种方式

已关闭留言

美国服务器的vue引用组件有3种方式:1、通过v-model或者.sync显式控制组件显示隐藏;2、使用js代码进行引用组件;3、使用Vue指令进行引用组件。

具体分析如下:

Dialog

我习惯把这个东西叫做对话框,实际上还有叫做modal(弹窗)组件的 叫法。其实就是在页面里,弹出一个小窗口,这个小窗口里的内容可以定制。通常可以用来做登录功能的对话框。

这种组件就很适合通过v-model或者.sync的方式来显式的控制出现和消失。它可以直接写在页面里,然后通过data去控制――这也是最符合Vue的设计思路的组件。

为此我们可以写一个组件就叫做Dialog.vue

<template>

<div class=”dialog”>

<div class=”dialog__wrapper” v-if=”visble” @clcik=”closeModal”>

<div class=”dialog”>

<div class=”dialog__header”>

<div class=”dialog__title”>{{ title }}</div>

</div>

<div class=”dialog__body”>

<slot></slot>

</div>

<div class=”dialog__footer”>

<slot name=”footer”></slot>

</div>

</div>

</div>

<div class=”modal” v-show=”visible”></div>

</div>

</template>

<script>

export default {

name: ‘dialog’,

props: {

title: String,

visible: {

type: Boolean,

default: false

}

},

methods: {

close() {

this.$emit(‘update:visible’, false) // 传递关闭事件

},

closeModal(e) {

if (this.visible) {

document.querySelector(‘.dialog’).contains(e.target) ? ” : this.close(); // 判断点击的落点在不在dialog对话框内,如果在对话框外就调用this.close()方法关闭对话框

}

}

}

}

</script>

CSS什么的就不写了,跟组件本身关系比较小。不过值得注意的是,上面的dialog__wrapper这个class也是全屏的,透明的,主要用于获取点击事件并锁定点击的位置,通过DOMNode.contains()方法来判断点击的位置是不是dialog本身,如果是点击到了dialog外面,比如半透明的modal层那么就派发关闭事件,把dialog给关闭掉。

当我们在外部要调用的时候,就可以如下调用:

<template>

<div class=”xxx”>

<dialog :visible.sync=”visible”></dialog>

<button @click=”openDialog”></button>

</div>

</template>

<script>

import Dialog from ‘Dialog’

export default {

components: {

Dialog

},

data() {

return {

visible: false

}

},

methods: {

openDialog() {

this.visible = true // 通过data显式控制dialog

}

}

}

</script>

为了Dialog开启和关闭好看点,你可试着加上<transition></transition>组件配合上过渡效果,简单的一点过渡动效也将会很好看。

Notice

这个组件类似于element-uimessage(消息提示)。它吸引我的最大的地方在于,它不是通过显式的在页面里写好组件的html结构通过v-model去调用的,而是通过在js里通过形如this.$message()这样的方法调用的。这种方法虽然跟Vue的数据驱动的思想有所违背。不过不得不说在某些情况下真的特别方便。

对于Notice这种组件,一次只要提示几个文字,给用户简单的消息提示就行了。提示的信息可能是多变的,甚至可以出现叠加的提示。如果通过第一种方式去调用,事先就得写好html结构,这无疑是麻烦的做法,而且无法预知有多少消息提示框。而通过js的方法调用的话,只需要考虑不同情况调用的文字、类型不同就可以了。

而之前的做法都是写一个Vue文件,然后通过components属性引入页面,显式写入标签调用的。那么如何将组件通过js的方法去调用呢?

这里的关键是Vueextend方法。

文档里并没有详细给出extend能这么用,只是作为需要手动mount的一个Vue的组件构造器说明了一下而已。

通过查看element-ui的源码,才算是理解了如何实现上述的功能。

首先依然是创建一个Notice.vue的文件

<template>

<div class=”notice”>

<div class=”content”>

{{ content }}

</div>

</div>

</template>

<script>

export default {

name: ‘notice’,

data () {

return {

visible: false,

content: ”,

duration: 3000

}

},

methods: {

setTimer() {

setTimeout(() => {

this.close() // 3000ms之后调用关闭方法

}, this.duration)

},

close() {

this.visible = false

setTimeout(() => {

this.$destroy(true)

this.$el.parentNode.removeChild(this.$el) // DOM里将这个组件移除

}, 500)

}

},

mounted() {

this.setTimer() // 挂载的时候就开始计时,3000ms后消失

}

}

</script>

上面写的东西跟普通的一个单文件Vue组件没有什么太大的区别。不过区别就在于,没有props了,那么是如何通过外部来控制这个组件的显隐呢?

所以还需要一个js文件来接管这个组件,并调用extend方法。同目录下可以创建一个index.js的文件。

import Vue from ‘vue’

const NoticeConstructor = Vue.extend(require(‘./Notice.vue’)) // 直接将Vue组件作为Vue.extend的参数

let nId = 1

const Notice = (content) => {

let id = ‘notice-‘ + nId++

const NoticeInstance = new NoticeConstructor({

data: {

content: content

}

}) // 实例化一个带有content内容的Notice

NoticeInstance.id = id

NoticeInstance.vm = NoticeInstance.$mount() // 挂载但是并未插入dom,是一个完整的Vue实例

NoticeInstance.vm.visible = true

NoticeInstance.dom = NoticeInstance.vm.$el

document.body.appendChild(NoticeInstance.dom) // dom插入body

NoticeInstance.dom.style.zIndex = nId + 1001 // 后插入的Notice组件z-index加一,保证能盖在之前的上面

return NoticeInstance.vm

}

export default {

install: Vue => {

Vue.prototype.$notice = Notice // Notice组件暴露出去,并挂载在Vueprototype

}

}

这个文件里我们能看到通过NoticeConstructor我们能够通过js的方式去控制一个组件的各种属性。最后我们把它注册进Vueprototype上,这样我们就可以在页面内部使用形如this.$notice()方法了,可以方便调用这个组件来写做出简单的通知提示效果了。

当然别忘了这个相当于一个Vue的插件,所以需要去主js里调用一下Vue.use()方法:

// main.js

// …

import Notice from ‘notice/index.js’

Vue.use(Notice)

// …

Loading

在看element-ui的时候,我也发现了一个很有意思的组件,是Loading,用于给一些需要加载数据等待的组件套上一层加载中的样式的。这个loading的调用方式,最方便的就是通过v-loading这个指令,通过赋值的true/false来控制Loading层的显隐。这样的调用方法当然也是很方便的。而且可以选择整个页面Loading或者某个组件Loading。这样的开发体验自然是很好的。

其实跟Notice的思路差不多,不过因为涉及到directive,所以在逻辑上会相对复杂一点。

平时如果不涉及Vuedirective的开发,可能是不会接触到modifiersbinding等概念。参考文档

简单说下,形如:v-loading.fullscreen=”true”这句话,v-loading就是directivefullscreen就是它的modifiertrue就是bindingvalue值。所以,就是通过这样简单的一句话实现全屏的loading效果,并且当没有fullscreen修饰符的时候就是对拥有该指令的元素进行loading效果。组件通过bindingvalue值来控制loading的开启和关闭。(类似于v-model的效果)

其实loading也是一个实际的DOM节点,只不过要把它做成一个方便的指令还不是特别容易。

首先我们需要写一下loadingVue组件。新建一个Loading.vue文件

<template>

<transition

name=”loading”

@after-leave=”handleAfterLeave”>

<div

v-show=”visible”

class=”loading-mask”

:class={‘fullscreen’: fullscreen}>

<div class=”loading”>

</div>

<div class=”loading-text” v-if=”text”>

{{ text }}

</div>

</div>

</transition>

</template>

<script>

export default {

name: ‘loading’,

data () {

return {

visible: true,

fullscreen: true,

text: null

}

},

methods: {

handleAfterLeave() {

this.$emit(‘after-leave’);

}

}

}

</script>

<style>

.loading-mask{

position: absolute; // 非全屏模式下,positionabsolute

z-index: 10000;

background-color: rgba(255,235,215, .8);

margin: 0;

top: 0;

right: 0;

bottom: 0;

left: 0;

transition: opacity .3s;

}

.loading-mask.fullscreen{

position: fixed; // 全屏模式下,positionfixed

}

// …

</style>

Loading关键是实现两个效果:

1.全屏loading,此时可以通过插入body下,然后将Loadingposition改为fixed,插入body实现。

2.对所在的元素进行loading,此时需要对当前这个元素的的position修改:如果不是absolute的话,就将其修改为relatvie,并插入当前元素下。此时Loadingposition就会相对于当前元素进行绝对定位了。

所以在当前目录下创建一个index.js的文件,用来声明我们的directive的逻辑。

import Vue from ‘vue’

const LoadingConstructor = Vue.extend(require(‘./Loading.vue’))

export default {

install: Vue => {

Vue.directive(‘loading’, { // 指令的关键

bind: (el, binding) => {

const loading = new LoadingConstructor({ // 实例化一个loading

el: document.createElement(‘div’),

data: {

text: el.getAttribute(‘loading-text’), // 通过loading-text属性获取loading的文字

fullscreen: !!binding.modifiers.fullscreen

}

})

el.instance = loading; // el.instance是个Vue实例

el.loading = loading.$el; // el.loadingDOM元素是loading.$el

el.loadingStyle = {};

toggleLoading(el, binding);

},

update: (el, binding) => {

el.instance.setText(el.getAttribute(‘loading-text’))

if(binding.oldValue !== binding.value) {

toggleLoading(el, binding)

}

},

unbind: (el, binding) => { // 解绑

if(el.domInserted) {

if(binding.modifiers.fullscreen) {

document.body.removeChild(el.loading);

}else {

el.loading &&

el.loading.parentNode &&

el.loading.parentNode.removeChild(el.loading);

}

}

}

})

const toggleLoading = (el, binding) => { // 用于控制Loading的出现与消失

if(binding.value) {

Vue.nextTick(() => {

if (binding.modifiers.fullscreen) { // 如果是全屏

el.originalPosition = document.body.style.position;

el.originalOverflow = document.body.style.overflow;

insertDom(document.body, el, binding); // 插入dom

} else {

el.originalPosition = el.style.position;

insertDom(el, el, binding); // 如果非全屏,插入元素自身

}

})

} else {

if (el.domVisible) {

el.instance.$on(‘after-leave’, () => {

el.domVisible = false;

if (binding.modifiers.fullscreen && el.originalOverflow !== ‘hidden’) {

document.body.style.overflow = el.originalOverflow;

}

if (binding.modifiers.fullscreen) {

document.body.style.position = el.originalPosition;

} else {

el.style.position = el.originalPosition;

}

});

el.instance.visible = false;

}

}

}

const insertDom = (parent, el, binding) => { // 插入dom的逻辑

if(!el.domVisible) {

Object.keys(el.loadingStyle).forEach(property => {

el.loading.style[property] = el.loadingStyle[property];

});

if(el.originalPosition !== ‘absolute’) {

parent.style.position = ‘relative’

}

if (binding.modifiers.fullscreen) {

parent.style.overflow = ‘hidden’

}

el.domVisible = true;

parent.appendChild(el.loading) // 插入的是el.loading而不是el本身

Vue.nextTick(() => {

el.instance.visible = true;

});

el.domInserted = true;

}

}

}

}

同样,写完整个逻辑,我们需要将其注册到项目里的Vue下:

// main.js

// …

import Loading from ‘loading/index.js’

Vue.use(Loading)

// …

至此我们已经可以使用形如

<div v-loading.fullscreen=”loading” loading-text=”正在加载中“>

这样的方式来实现调用一个loading组件了。