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,一经查实,立即删除!

相关文章

axios把post的RequestPayload格式转为formdata

方法一&#xff1a;配置transformRequest&#xff0c;缺点&#xff1a;其他请求格式的数据也会被重新格式化&#xff08;PUT&#xff0c;PATCH&#xff09; const service axios.create({//设置axios为form-data 方法1// headers: {// post: {// "Content-T…

火狐打印预览_将打印和打印预览命令添加到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…

每个人都要在自己的“时区”里找到自己的快乐

祝小妹和自己生日快乐&#xff0c;人人都想快乐&#xff0c;却在平常的365天闷闷不乐&#xff0c;但愿家人朋友在平常的每一天都很够健康快乐&#xff01; 在我那个开不了机的手机记事薄有句话还记得&#xff1a;你们不要刻意等我&#xff0c;因为可能现在的我还没来得及去发现…

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

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

js以变量为键

let key "dynamic",obj{[key]:true }; obj[key2]key console.log(obj)一般在配置文件中应用较多

搭建jenkins实现自动化部署

参考&#xff1a; https://www.cnblogs.com/rslai/p/8135460.html转载于:https://www.cnblogs.com/lihuanhuan/p/10612123.html

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. 最近…

vue使用process.env搭建自定义运行环境

一、vue-cli项目下默认有三种模式&#xff1a; development&#xff1a;在 vue-cli-service serve 时使用。production&#xff1a;在 vue-cli-service build 和 vue-cli-service test:e2e 时使用。test&#xff1a;在 vue-cli-service test:unit 时使用。 对应的 process.env…

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…

春节您“抢票”到手了吗,如果没,请进来看看!

不是为了卖“广告”!我与软件作者从不认识&#xff01;我与软件作者因为抢票认识&#xff0c;不&#xff0c;只认识他写的软件&#xff01;51CTO博客2.0后&#xff0c;我一直没有写博文&#xff01;主要原因&#xff1a;不能用Live Writer写博文&#xff0c;复制&#xff0c;粘…

两个矩阵相加 Exercise08_05

1 import java.util.Scanner;2 /**3 * author 冰樱梦4 * 时间&#xff1a;2018年12月5 * 题目&#xff1a;两个矩阵相加6 *7 */8 public class Exercise08_05 {9 public static void main(String[] args){ 10 Scanner inputnew Scanner(System.in); 11 …

vue element form中input等组件不能输入值

<el-input v-model"form.inputVal " />由于data中form只是一个空对象{}&#xff0c;当主动设置 form.inputVal “” 后input却仍无法输入值&#xff0c;这是因为inputVal 属性没有get和set&#xff0c;需要用vue内置属性设置&#xff1a;this.$set(this.form,…

如何在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…

彻底理解数据库事物

事务 事务(Transaction)&#xff0c;一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在计算机术语中&#xff0c;事务通常就是指数据库事务。 概念 一个数据库事务通常包含对数据库进行读或写的一个操作序列。它的…

HttpRunner自动化框架学习笔记

一.简单介绍 HttpRunner 是一款面向 HTTP(S) 协议的通用测试框架&#xff0c;只需编写维护一份 YAML/JSON 脚本&#xff0c;即可实现自动化测试、性能测试、线上监控、持续集成等多种测试需求。 支持python2和python3 二.框架特点 继承 Requests 的全部特性&#xff0c;轻松实现…

如何在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 …