GIF.gif
前言
最近在做移动端的项目, 需要制作移动端的 alert 弹框和 message-box 提示信息; 之前使用 vue 框架的 element-ui 时, 就记得 element-ui 的弹框, 今天深入的研究了一下源码, 然后简单制作了一点小 demo
用到的知识点
Vue 组件的定义, Vue 的 extend
Vue.extend(options)
options 参数是一个对象, 一个 Vue 组件配置项的对象, 例如
- // 创建一个构造器,
- const Profile = Vue.extend({
- // 模板使用参数内的 template 模板
- template: `<div>{{message}}</div>`,
- // data 内的为默认数据
- data() {
- return {
- message: ''
- }
- }
- })
- // 创建一个基于构造器的 Vue 实例, 并且给 data 重新赋值
- const Message = new Profile({
- data: {
- message: 'message'
- }
- })
- // 将这个实例挂载到 id 为 App 的 DOM 节点上
- Message.$mount('#app');
正文
看完了基本原理, 大部分同学应该就已经可以明白这个东西是怎么实现的了吧!, 接着我带着大家来看看具体的实现步骤
首先需要先定义一个 Vue 的组件, 这个组件不直接挂载到任何 DOM 节点中, 而是作为一个模板, 一个构造器来使用
- <template>
- <transition name="fade">
- <!-- 使用 fade 淡入淡出动画, 使用 type 变量来控制 class 类名, 达到更改 type 值就可以修改样式的效果 -->
- <div :class="['plugins-message-box',type]" v-show="visible">
- <!-- 使用 iconClass 来控制 icon 的类名, 我使用的是阿里的字体图标库 iconfont, 可以根据个人爱好来更换 -->
- <div :class="['message-icon','iconfont',iconClass]"></div>
- <!-- 输出消息 -->
- <div class="message-container">{{message}}</div>
- </div>
- </transition>
- </template>
- <script>
- // 定义每一个 type 对应的 class 类名
- const typeClass = {
- success: 'icon-success',
- error: 'icon-error',
- default: 'icon-success'
- };
- export default {
- name: "messageMain",
- // 定义的是默认数据, 默认值
- data() {
- return {
- visible: false, // 控制 DOM 显示隐藏
- type: 'default', // 默认 type 值为 default
- icon: '', // 默认使用 icon 为空, 则使用 type 值对应的 icon
- message: '', // 默认的 message 为空, 由外部传入
- duration: 2000 // 默认显示时间为 2000ms
- }
- },
- computed: {
- // 如果外部传入 icon 则使用外部的 icon, 如果没有. 则使用 type 值对应的 icon
- iconClass() {
- if(this.icon) {
- return this.icon;
- }else {
- return typeClass[this.type];
- }
- }
- }
- }
- </script>
可以看到这是一个非常简单的 Vue 组件模板, 使用的是 vue-cli 工具构建的, 最终它抛出了一个对象, 而我们接着就应该来定义构造器
- import Vue from 'vue'; // 引入 Vue
- import MessageMain from './messageMain'; // 引入上边定义好的 message 模板
- const MessageBox = Vue.extend(MessageMain); // 使用 Vue.extend 来创建一个构造器
- let instance; // instance 变量用来保存实例
- let timer = null; // timer 变量用来保存定时器
- // 定义一个 function, 参数为 options, 默认为一个对象
- const Message = function(options = {}) {
- // 如果当前处在服务器端, 则直接返回
- if(Vue.prototype.$isServer) return;
- // 如果当前定时器已开启, 说明页面上已经有一个 message-box 了, 则不能再继续创建新的 message-box
- if(timer) return;
- // 对 options 做处理, 如果直接传入 string, 则使其保存在 options 的 message 属性上
- if(typeof options === 'string') {
- options = {
- message: options
- }
- }
- // 初始化实例, 并将 options 作为新的 data 传入, Vue 会将 options 合并到原有的 data 上, 覆盖原有的默认值, 但是, 在 options 中没有设置的是不会被改变的
- instance = new MessageBox({
- data: options
- });
- // 调用 $mount 方法, 将当前实例渲染为真实 DOM, 生成 $el,, 如果不执行这一步, 将拿不到 $el 的值, 但是不指定 DOM 节点接管当前实例
- instance.vm = instance.$mount();
- // 使用原生 JS 的 API 将当前实例的真实 DOM 追加到 body 中
- document.body.appendChild(instance.vm.$el);
- // 实例上的 vm 就是我们的 Vue 组件, 所以我们可以通过 vm 访问到当前实例中的所有属性
- // 将 visible 设置为 true, 即显示当前 message-box
- instance.vm.visible = true;
- // 开启定时器
- timer = setTimeout(() => {
- // 在时间结束后将当前实例手动卸载
- instance.vm.$destroy();
- // 使用原生 API 将当前实例生成的 DOM 节点在真实的 DOM 树中删除
- instance.vm.$el.parentNode.removeChild(instance.vm.$el);
- // 清除定时器
- timer = null;
- }, instance.vm.duration);
- // 定时器的时间使用 vm 中定义的时间
- return instance.vm;
- };
- // 最终抛出一个对象, 对象上我们可以使用 install 来扩展 Vue 的插件
- // 当我们的对象上有 install 方法的时候, 它接收第一个参数为 Vue,
- // 我这里为了方便使用, 还在当前抛出的对象上定义了一个 message 方法, 为了方便在 axios 的拦截器中使用;
- export default {
- message: Message,
- install(Vue) {
- Vue.prototype.$message = Message;
- Vue.message = Message;
- }
- };
接着来看一下使用方法, 首先要在 main.JS 中将这个插件安装到 Vue 上
- import Message from './plugins/Message';
- Vue.use(Message); // 因为我们的对象上定义了 install 方法, 所以可以直接调用 Vue 的 use 方法
安装完成之后, 我们就可以在任意组件内使用了, 这个是我们刚刚在文章开头看到的页面, 现在只有调用 message-box 的方法;
我们直接使用 this.$message 就可以调用我们的 message-box, 并且显示都没有任何问题, 当然, 也可以通过传入 icon 和 duration 来修改图标和时间
- <template>
- <div class="test">
- <div class="test-message">
- <button @click="messageClick('default')">
- default
- </button>
- <button @click="messageClick('success')">
- success
- </button>
- <button @click="messageClick('error')">
- error
- </button>
- </div>
- </div>
- </template>
- <script>
- export default {
- name: "test",
- methods: {
- messageClick(type) {
- this.$message({
- type,
- message: type
- })
- }
- }
- }
- </script>
GIF.gif
看完了 message-box, 我想你对 alert 的做法也有了一定的想法了吧! 接着我们来看一下 alert 的做法
在 alert 中, 我们不需要定义 type, 也不需要修改样式,
对于 alert 来说, 我们需要有一个头部, 一个内容区, 还有一个确认按钮,
而我们的内容区, 使用的是 v-html 来渲染内容, 便于我们可以使用原生的 DOM 标签来实现一些简单的样式
而我们在这个组件内, 当点击确定按钮的时候, 我们将卸载当前组件, 并将当前组件从 DOM 结构删除
- <template>
- <transition name="fade">
- <div class="plugins-alert-box" v-show="visible">
- <div class="plugins-alert-container">
- <div class="alert-box-title">
- {{title}}
- </div>
- <div class="alert-box-message" v-HTML="message">
- </div>
- <div class="alert-box-btn">
- <button @click="btnClick"> 确定 </button>
- </div>
- </div>
- </div>
- </transition>
- </template>
- <script>
- export default {
- name: "alertMain",
- data() {
- return {
- visible: false,
- message: '',
- title: '提示'
- }
- },
- methods: {
- // 点击确定按钮的时候, 首先隐藏当前元素, 然后将当前元素从 DOM 结构中删除, 并手动卸载当前实例
- btnClick() {
- this.visible = false;
- this.$el.parentNode.removeChild(this.$el);
- this.$destroy();
- }
- }
- }
- </script>
接着我们再来看定义构造器的代码
- import Vue from 'vue'; // 引入 Vue
- import alertMain from './alertMain'; // 引入定义好的 alert 的模板
- const AlertBox = Vue.extend(alertMain); // 创建一个构造器
- let instance; // instance 变量用来保存实例
- // 定义 Alert 函数, 接收 options 参数, 默认为一个对象
- const Alert = function(options = {}) {
- if(Vue.prototype.$isServer) return; // 判断当前如果是在服务器端渲染的话, 直接返回
- // 同样的, 我们还要处理 options
- if(typeof options === 'string') {
- options = {
- message: options
- }
- }
- // 创建一个 alert-box 实例 , 将 options 作为新的 data 传入
- instance = new AlertBox({
- data: options
- });
- // 调用 $mount 方法, 创建真实 DOM, 获取到 vm
- instance.vm = instance.$mount();
- // 调用原生 API 将当前实例的真实 DOM 追加到 body 中
- document.body.appendChild(instance.vm.$el);
- // 让当前 DOM 显示
- instance.vm.visible = true;
- return instance.vm;
- };
- // 抛出对象, 以及 install 方法, 原理我们在上边已经讲过了
- export default {
- alert: Alert,
- install(vue) {
- vue.prototype.$alert = Alert;
- }
- }
接着就是 alert-box 的使用方法了, 同样是需要先 use 一下
- import Alert from './plugins/Alert';
- Vue.use(Alert);
最后来使用一下这个 alert-box
- <template>
- <div class="test">
- <div class="test-message">
- <button @click="alertClick">alert</button>
- </div>
- </div>
- </template>
- <script>
- export default {
- name: "test",
- methods: {
- alertClick() {
- this.$alert({
- message: `<b > 加粗文字 </b>`
- })
- }
- }
- }
- </script>
GIF.gif
结言
我没有帖出 CSS 样式文件, 因为样式文件其实没有多少东西, 我觉得你写的会比我写的更优秀, 更完美
看完这篇文章不知道你有没有什么收获, 如果可以理解的话, 希望你通过这篇文章写一个 dialog 组件, 巩固一下知识, 顺手点个关注
感谢您的查阅, 代码冗余或者有错误的地方望不吝赐教; 菜鸟一枚, 请多关照
来源: http://www.jianshu.com/p/2ff74186fa39