Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue 组件开发总结 #43

Open
fantasticit opened this issue Aug 31, 2020 · 0 comments
Open

Vue 组件开发总结 #43

fantasticit opened this issue Aug 31, 2020 · 0 comments
Labels

Comments

@fantasticit
Copy link
Owner

前言

本文接下来会一步一步模仿造一个低配版的 Element 的对话框和弹框组件

正文

Vue 单文件组件开发

当使用vue-cli初始化一个项目的时候,会发现 src/components 文件夹下有一个 HelloWorld.vue 文件,这便是单文件组件的基本开发模式。

// 注册
Vue.component("my-component", {
  template: "<div>A custom component!</div>"
});

// 创建根实例
new Vue({
  el: "#example"
});

接下来,开始写一个 dialog 组件。

Dialog

目标对话框组件的基本样式如图:

dialog基本样式

根据目标样式,可以总结出:

  1. dialog 组件需要一个 title props 来标示弹窗标题
  2. dialog 组件需要在按下 确定 按钮时 发射确定 事件(即告诉父组件确定了)
  3. 同理,dialog 组件需要 发射取消 事件
  4. dialog 组件需要提供一个插槽,便于自定义内容

那么,编码如下:

<template>
  <div class="ta-dialog__wrapper">
    <div class="ta-dialog">
      <div class="ta-dialog__header">
        <span>{{ title }}</span>
        <i class="ios-close-empty" @click="handleCancel()"></i>
      </div>
      <div class="ta-dialog__body">
        <slot></slot>
      </div>
      <div class="ta-dialog__footer">
        <button @click="handleCancel()">取消</button>
        <button @click="handleOk()">确定</button>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    name: "Dialog",

    props: {
      title: {
        type: String,
        default: "标题"
      }
    },

    methods: {
      handleCancel() {
        this.$emit("cancel");
      },

      handleOk() {
        this.$emit("ok");
      }
    }
  };
</script>

这样便完成了dialog组件的开发,使用方法如下:

<ta-dialog title="弹窗标题" @ok="handleOk" @cancel="handleCancel">
  <p>我是内容</p>
</ta-dialog>

这时候发现一个问题,通过使用 v-if 或者 v-show 来控制弹窗的展现时,没有动画!!!,看上去很生硬。教练,我想加动画 ,这时候就该 transition 组件上场了。使用 transition 组件结合 css能做出很多效果不错的动画。接下来增强 dialog 组件动画,代码如下:

<template>
  <transition name="slide-down">
    <div class="ta-dialog__wrapper" v-if="isShow">
      <!-- 省略 -->
    </div>
  </transition>
</template>

<script>
  export default {
    data() {
      return {
        isShow: true
      };
    },

    methods: {
      handleCancel() {
        this.isShow = false;
        this.$emit("cancel");
      },

      handleOk() {
        this.isShow = true;
        this.$emit("ok");
      }
    }
  };
</script>

可以看到 transition 组件接收了一个 name props,那么怎么编写 css 完成动画呢?很简单的方式,写出两个
关键 class (css 的 className)样式即可:

.slide-down-enter-active {
  animation: dialog-enter ease 0.3s;
}

.slide-down-leave-active {
  animation: dialog-leave ease 0.5s;
}

@keyframes dialog-enter {
  from {
    opacity: 0;
    transform: translateY(-20px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes dialog-leave {
  from {
    opacity: 1;
    transform: translateY(0);
  }

  to {
    opacity: 0;
    transform: translateY(-20px);
  }
}

就是这么简单就开发出了效果还不错的动效,注意 transition 组件的 nameslide-down ,而编写的动画的关键 classNameslide-down-enter-activeslide-down-leave-active

封装DialogMessageBox

Element 的MessageBox的使用方法如下:

this.$confirm("此操作将永久删除该文件, 是否继续?", "提示", {
  confirmButtonText: "确定",
  cancelButtonText: "取消",
  type: "warning"
})
  .then(() => {
    this.$message({
      type: "success",
      message: "删除成功!"
    });
  })
  .catch(() => {
    this.$message({
      type: "info",
      message: "已取消删除"
    });
  });

看到这段代码,我的感觉就是好神奇好神奇好神奇(惊叹三连)。仔细看看,这个组件其实就是一个封装好的 dialog ,

Element MessageBox效果

接下来,我也要封装一个这样的组件。首先,整理下思路:

  1. Element 的使用方法是 this.$confirm ,这不就是挂到 Vueprototype 上就行了
  2. Element 的 then 是确定,catch 是取消,promise 就可以啦

整理好思路,我就开始编码了:

import Vue from "vue";
import MessgaeBox from "./src/index";

const Ctur = Vue.extend(MessgaeBox);
let instance = null;

const callback = action => {
  if (action === "confirm") {
    if (instance.showInput) {
      instance.resolve({ value: instance.inputValue, action });
    } else {
      instance.resolve(action);
    }
  } else {
    instance.reject(action);
  }

  instance = null;
};

const showMessageBox = (tip, title, opts) =>
  new Promise((resolve, reject) => {
    const propsData = { tip, title, ...opts };

    instance = new Ctur({ propsData }).$mount();
    instance.reject = reject;
    instance.resolve = resolve;
    instance.callback = callback;

    document.body.appendChild(instance.$el);
  });

const confirm = (tip, title, opts) => showMessageBox(tip, title, opts);

Vue.prototype.$confirm = confirm;

至此,可能会疑惑怎么 callback 呢,其实我编写了一个封装好的 dialog 并将其命名为 MessageBox ,
它的代码中,有这样两个方法:

onCancel() {
  this.visible = false
  this.callback && (this.callback.call(this, 'cancel'))
},

onConfirm() {
  this.visible = false
  this.callback && (this.callback.call(this, 'confirm'))
},

没错,就是确定取消时进行callback。我还想说一说Vue.extend,代码中引入了MessageBox,
我不是直接new MessageBox而是借助new Ctur,因为这样可以定义数据(不仅仅是props),例如:

instance = new Ctur({ propsData }).$mount();

这时候,页面上其实是还没有MessageBox的,我们需要执行:

document.body.appendChild(instance.$el);

如果你直接这样,你可能会发现 MessageBox 打开的时候没有动画,而关闭的时候有动画。解决方法也很简单,
appendChild的时候让其仍是不可见,然后使用类这样的代码:

Vue.nextTick(() => (instance.visible = true));

这样就有动画了。

总结

  1. 通过 transitioncss 实现不错的动画。其中,transition 组件的 name 决定了编写 css 的两个关键
    类名为 [name]-enter-active[name]-leave-active
  2. 通过 Vue.extend 继承一个组件的构造函数(不知道怎么说合适,就先这样说),然后通过这个构造函数,便可以
    实现组件相关属性的自定义(使用场景:js 调用组件)
  3. js 调用组件时,为了维持组件的动画效果可以先 document.body.appendChild 然后 Vue.nextTick(() => instance.visible = true)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant