Vue组件的三种调用方式

最近在写fj-service-system的时候,遇到了一些问题。那就是我有些组件,比如Dialog、Message这样的组件,是引入三方组件库,比如element-ui这样的,还是自己实现一个?虽然它们有按需引入的功能,但是整体风格和我的整个系统不搭。于是就可以考虑自己手动实现这些简单的组件了。

通常我们看Vue的一些文章的时候,我们能看到的通常是讲Vue单文件组件化开发页面的。单一组件开发的文章相对就较少了。我在做fj-service-system项目的时候,发现其实单一组件开发也是很有意思的。可以写写记录下来。因为写的不是什么ui框架,所以也只是一个记录,没有github仓库,权且看代码吧。


  • v-model或者.sync显式控制组件显示隐藏
  • 通过js代码调用
  • 通过Vue指令调用

在写组件的时候很多写法、灵感来自于element-ui,感谢。

Dialog

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

1302312-20180114172825066-582482386.gif

这种组件就很适合通过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也是全屏的,透明的,主要用于获取点击事件并锁定点击的位置,通过DOM的Node.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-ui的message(消息提示)。它吸引我的最大的地方在于,它不是通过显式的在页面里写好组件的html结构通过v-model去调用的,而是通过在js里通过形如this.$message()这样的方法调用的。这种方法虽然跟Vue的数据驱动的思想有所违背。不过不得不说在某些情况下真的特别方便。

1302312-20180114172835254-1510776768.gif

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

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

这里的关键是Vue的extend方法。

文档里并没有详细给出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 = falsesetTimeout(() => {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 = 1const Notice = (content) => {let id = 'notice-' + nId++const NoticeInstance = new NoticeConstructor({data: {content: content}}) // 实例化一个带有content内容的NoticeNoticeInstance.id = idNoticeInstance.vm = NoticeInstance.$mount() // 挂载但是并未插入dom,是一个完整的Vue实例NoticeInstance.vm.visible = trueNoticeInstance.dom = NoticeInstance.vm.$eldocument.body.appendChild(NoticeInstance.dom) // 将dom插入bodyNoticeInstance.dom.style.zIndex = nId + 1001 // 后插入的Notice组件z-index加一,保证能盖在之前的上面return NoticeInstance.vm
}export default {install: Vue => {Vue.prototype.$notice = Notice // 将Notice组件暴露出去,并挂载在Vue的prototype上}
}

这个文件里我们能看到通过NoticeConstructor我们能够通过js的方式去控制一个组件的各种属性。最后我们把它注册进Vue的prototype上,这样我们就可以在页面内部使用形如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。这样的开发体验自然是很好的。

1302312-20180114172844754-1386905862.gif

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

平时如果不涉及Vue的directive的开发,可能是不会接触到modifiers、binding等概念。参考文档

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

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

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

<template><transitionname="loading"@after-leave="handleAfterLeave"><divv-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; // 非全屏模式下,position是absolutez-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; // 全屏模式下,position是fixed
}
// ...
</style>

Loading关键是实现两个效果:

    1.全屏loading,此时可以通过插入body下,然后将Loading的position改为fixed,插入body实现。
    2.对所在的元素进行loading,此时需要对当前这个元素的的position修改:如果不是absolute的话,就将其修改为relatvie,并插入当前元素下。此时Loading的position就会相对于当前元素进行绝对定位了。
所以在当前目录下创建一个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({ // 实例化一个loadingel: 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.loading的DOM元素是loading.$elel.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组件了。

总结

在用Vue写我们的项目的时候,不管是写页面还是写形如这样的功能型组件,其实都是一件很有意思的事情。本文介绍的三种调用组件的方式,也是根据实际情况出发而实际操作、实现的。不同的组件通过不同的方式去调用,方便了开发人员,也能更好地对代码进行维护。当然也许还有其他的方式,我并没有了解,也欢迎大家在评论里指出!

最后再次感谢element-ui的源码给予的极大启发。

文章作者: Molunerfinn
文章链接: https://molunerfinn.com/vue-components/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 MARKSZのBlog!

转载于:https://www.cnblogs.com/wwhhq/p/8283769.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/278610.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

火狐打印预览_将打印和打印预览命令添加到Firefox的上下文菜单

火狐打印预览Have you been thinking about how much easier it would be to having the Print & Print Preview commands in Firefox’s Context Menu? The Print Context Menu extension for Firefox allows you to avoid having to use the File Menu to access the pr…

《2017 云计算评测报告》:带你了解 AWS、阿里云、腾讯云等八家云计算服务提供商的综合用户体验情况...

报告电子版至听云官方博客下载&#xff1a;http://blog.tingyun.com/web/article/detail/1352 评测说明 评测目标&#xff1a;同一应用&#xff08;网站&#xff09;在不同云上的用户访问体验&#xff0c;以及对云资源的使用 洞察周期及范围&#xff1a;2017年4月-2017年9月 访…

python 新闻摘要_每日新闻摘要:Microsoft内部禁止应用程序,这样就可以了

python 新闻摘要Recently, a list of apps that Microsoft prohibits for internal employee use leaked, including Slack, Grammarly, and others. It’s tempting to think these are the actions of a company hating competition, but the truth is more complicated. 最近…

bootstrap评分插件 Bootstrap Star Rating Examples

http://www.jq22.com/demo/bootstrap-star-rating-master201708041812/ 转载于:https://www.cnblogs.com/waw/p/8288951.html

http 请求报文

1、报文 2、http请求方法 restful接口 post&#xff1a;创建 put&#xff1a;更新 转载于:https://www.cnblogs.com/mengfangui/p/10171559.html

chrome硬件加速_如何在Chrome中打开和关闭硬件加速

chrome硬件加速Google Chrome comes equipped with hardware acceleration, a feature which takes advantage of your computer’s GPU to speed up processes and free vital CPU time. However, sometimes driver incompatibilities can cause this feature to misbehave an…

如何在PowerPoint中制作三折

While Microsoft PowerPoint is almost exclusively used for presentation purposes, it’s also a great application for creating interesting and visually appealing brochures. Here’s how to create (and print out) a tri-fold using PowerPoint. 尽管Microsoft Powe…

如何在Chrome中为Gmail启用桌面通知

Last year Google rolled out desktop notifications for Google Calendar, now you can get Gmail and Gchat notifications on your desktop too. Read on as we walk you through configuring them both. 去年Google推出了Google日历的桌面通知&#xff0c;现在您也可以在桌…

vue集成iconfont、fontawesome和图标选择器(含fontawesome、el-icon和加入的iconfont)

目录&#xff08;一&#xff09;引入iconfont字体图标库将图标加入购物车新建&#xff08;添加至&#xff09;项目下载后项目中引入&#xff08;二&#xff09;引入fontawesome&#xff08;三&#xff09;图标选择器效果图结构使用源码&#xff08;一&#xff09;引入iconfont字…

java之Synchronize

2019独角兽企业重金招聘Python工程师标准>>> 实现原理&#xff1a;JVM 是通过进入、退出对象监视器( Monitor )来实现对方法、同步块的同步的。 具体实现是在编译之后在同步方法调用前加入一个 monitor.enter 指令&#xff0c;在退出方法和异常处插入 monitor.exit …

HikariCP连接池配置

2019独角兽企业重金招聘Python工程师标准>>> HikariCP号称性能最好的Java数据库连接池。虽没做过亲测但是公司项目一直在用&#xff0c;大概经历过2万左右用户同时在线&#xff0c;链接池性能方面未出现问题。 官网&#xff1a;http://brettwooldridge.github.io/Hi…

Microsoft Desktop Player是IT Pro的宝贵工具

If you are an IT Professional, a new education tool introduced by Microsoft is the MS Desktop Player. Today we take a look at what it has to offer, from Webcasts, White Papers, Training Videos, and more. 如果您是IT专业人员&#xff0c;则Microsoft推出的新的培…

如何在Microsoft Excel中将文本转换为日期值

Analysis of business data often requires working with date values in Excel to answer questions such as “how much money did we make today” or “how does this compare to the same day last week?” And that can be hard when Excel doesn’t recognize the valu…

看着手机会让您晕眩吗? 禁用动画

Giulio_Fornasar/ShutterstockGiulio_Fornasar /快门Are your phone’s buttery-smooth animations causing motion sickness, eyestrain, or even slow app performance? Those animations are just for looks, and you can disable a lot of them on both iPhone and Androi…

电脑的组成

一、按电脑组成分 1.CPU&#xff08;中央处理器&#xff09;&#xff0c;是一块超大规模的集成电路&#xff0c;有很多针脚&#xff0c;是电脑的核心&#xff0c;它是电脑进行运算和控制的核心&#xff0c;处理着各种信息的运算&#xff0c;就像人计算数学题要用头脑运算一样。…

微信小程序之 SideBar(侧栏分类)

项目目录&#xff1a; 模拟数据&#xff1a; utils / data.js function getSData() {var data [{"id": 1,"tree": {"id": 1,"desc": "宝宝奶粉","desc2": null,"level": "level1","log…

如何在线查找成千上万的免费电子书

You’ve got an ebook reader (or a laptop or netbook with ebook reading software) now you just need some free books to put it to good use. Read on as we show you the best places to score free books online. 您已经有了一个电子书阅读器(或带有电子书阅读软件的笔…

Spring Bean默认配置为单实例 Spring Bean生命周期

2019独角兽企业重金招聘Python工程师标准>>> Spring 的Bean默认的是单例的. 如果不想单例需要如下配置&#xff1a; <bean id"user" class"..." scope"singleton"/> scope"singleton"就是配置这个bean是单例的&#…

minecraft服务器_如何启动自己的Minecraft服务器进行多人游戏

minecraft服务器If you’ve played Minecraft, then it’s easy to see how much fun it can be. Running your own server lets you bring all of your friends into the same game, and you can play with rules you get to make or break. It’s the ultimate in an already…

Spring Boot 2.x(六):优雅的统一返回值

目录 为什么要统一返回值ReturnVOReturnCode使用ReturnVO使用AOP进行全局异常的处理云撸猫公众号为什么要统一返回值 在我们做后端应用的时候&#xff0c;前后端分离的情况下&#xff0c;我们经常会定义一个数据格式&#xff0c;通常会包含code&#xff0c;message&#xff0c;…