vue组件函数化

155 阅读1分钟

组件

参考elementUI,将组件函数化

<template>
    <div>
        <ui-mask :show="ownShow"></ui-mask>
        <div class="fin-ui-dialog" v-show="ownShow" :transition="animate" @touchmove="preventTouchMove($event)">
            <div class="title" v-if="title">
                {{ title }}
            </div>
            <div class="content">
                <slot name="content">
                    <div v-html="content"></div>
                </slot>
            </div>

            <div class="foot clearfix" v-if="foot">
                <div>
                    <button
                        v-if="showOk"
                        class="fin-ui-button default small-one"
                        @click.stop="eventEmitter('ok')"
                        :disabled="disable"
                    >
                        {{ ok }}
                    </button>
                    <button
                        v-if="showCancel"
                        class="fin-ui-button primary small-one cancle"
                        @click.stop="eventEmitter('cancel')"
                    >
                        {{ cancel }}
                    </button>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
/**
 * @file ui-dialog
 * @author ienix(guoaimin01@baidu.com)
 * @since 9/21
 */

/**
     * ui-dialog dialog组件
     * @module fin-ui/ui-dialog
     *
     * @property {boolean} show.sync - 显示或隐藏, 默认隐藏, 传入必须为双向数据绑定类型
     * @property {boolean} close - 启用关闭
     * @property {boolean} mask - 启用mask
     * @property {boolean} head - 启用head
     * @property {boolean} foot - 启用foot
     * @property {string} type - 模态对话框的形态 [lightbox|dialog|alert|confirm|prompt]
     * @property {string} title - 标题
     * @property {string|html} content - 对话框内容
     * @property {string} ok - 确定的文本
     * @property {string} cancel - 取消的文本
     * @property {string} animate - 动画方式,默认fade
     * @property {string} prefix - 添加css 前缀
     * @property {number} width -宽度默认值300
     * @property {number} top - 默认垂直居中
     */

     /**
     * @example
     *
     * 使用content 扩展内容
     *
     * 1. 使用文本
     * <ui-dialog type="confirm" content="hello world"></ui-dialog>
     * 2. html
     * <ui-dialog type="confirm" content="<img src="" />"></ui-dialog>
     *
     * 使用插槽扩展
     */

import uiMask from 'fin-ui/ui-mask';

export default {
    props: {
        title: {
            type: String,
            default: ''
        },
        content: {
            type: String,
            default: ''
        },
        animate: {
            type: String,
            default: 'fade'
        },
        ok: {
            type: String,
            default: '确定'
        },
        cancel: {
            type: String,
            default: '取消'
        },
        foot: {
            type: Boolean,
            default: true
        },
        showOk: {
            type: Boolean,
            default: true
        },
        showCancel: {
            type: Boolean,
            default: true
        },
        show: {
            type: Boolean,
            default: false
        },
        lightUp: {
            type: Boolean,
            default: false
        }
    },
    data() {
        return {
            is: 'ui-dialog',
            ownShow: false,
            disable: true
        };
    },
    watch: {
        show: {
            immediate: true,
            handler(val) {
                this.ownShow = val;
            }
        },
        ownShow: {
            immediate: true,
            handler(val) {
                this.$emit('update:show', val);
            }
        },
        lightUp: {
            handler(val) {
                this.disable = val;
            },
            immediate: true
        }
    },
    methods: {
        preventTouchMove(event) {
            if (this.preventDefaultTouchMove) {
                event.preventDefault();
            }
        },
        showUi() {
            this.ownShow = true;
        },
        hideUi() {
            this.ownShow = false;
        },
        eventEmitter(type) {
            if (type === 'cancel') {
                this.ownShow = false;
            }
            this.$emit(type, this);
        }
    },
    computed: {},
    created() {},
    components: {
        uiMask
    }
};
</script>

<style lang="less" scoped rel="stylesheet/less">
@import '../css/variable.less';
@import '../css/button.less';
@import '../css/util.less';

@name: ~` '@{prefix}' + 'ui-dialog' `;

.@{name} {
    background: @white-color;
    position: fixed;
    z-index: @z-index-primary;
    border-radius: @border-radius-default;
    font-size: @font-size-18;
    top: 50%;
    left: 37px;
    right: 38px;
    margin: 0 auto;
    padding: @spacing-5x @spacing-5x+1;
    text-align: center;
    transform: translateY(-50%);
    .title {
        display: block;
        margin: 0 auto;
        text-align: center;
        font-weight: 600;
        font-size: @font-size-17;
        line-height: 24px;
        font-family: @font-family-FDC-bold;
        color: @text-color-default;
    }
    .content {
        display: inline-block;
        margin-top: 13px;
        font-size: @font-size-16;
        line-height: 23px;
        text-align: left;
        font-family: @font-family-PingFangSC;
        color: @decoration-color-level-three;
    }
    .foot {
        margin-top: 13px;
        padding-left: 4px;
        padding-right: 4px;
        .cancle {
            font-family: @font-family-PingFangSC;
        }
    }
}
</style>

/**
 * 1。使用一次后可以把实例存储,修改实例的属性即可
 * 2。再次调用vue也是使用这个实例并造成混乱
 */
import Vue from 'vue';
import { Promise } from 'es6-promise';
import assign from 'object-assign';
import dialog from '@/fin-ui/ui-dialog';
import waring from '@/fin-ui/ui-waring';
import loading from '@/fin-ui/ui-loading';
import toast from '@/fin-ui/ui-toast';

let uiMap = {
    dialog: dialog,
    loading: loading,
    toast: toast,
    waring: waring
};

Object.keys(uiMap).forEach(name => {
    let ui = uiMap[name];
    uiMap[name] = options => {
         //使用vue构造器,创建一个子类
        let componentConstructor = Vue.extend(ui); // 参考elementUi 写法
        //实例化
        let instance = new componentConstructor();
        if (typeof options === 'string') {
            options = {
                content: options
            };
        }
        assign(instance, options);
        instance.$mount();
        document.body.appendChild(instance.$el);
        //外部一引入,则将组件显示在页面上
        instance.showUi();
        if (options) {
            options.onOk && instance.$on('ok', options.onOk);
            options.onShow && instance.$on('show', options.onShow);
            options.onHide && instance.$on('hide', options.onHide);
            // todo: 这里是否需要将dom上的元素删除掉?
            // instance.$on('update:show', val => {
            //     if (val === false) {
            //         document.body.removeChild(instance.$el);
            //     }
            //     console.log(val + 'show---');
            // });
            options.onCancel && instance.$on('cancel', options.onCancel);
        }
        return new Promise((resolve, reject) => {
            instance.$on('ok', event => resolve(instance));
            instance.$on('show', event => resolve(instance));
            instance.$on('hide', event => resolve(instance));
            instance.$on('cancel', event => reject(instance));
            if (name !== 'dialog' && name !== 'waring') {
                return resolve(instance);
            }
        });
    };
});
export default uiMap;

用法

		let loading = ui.loading({
                type: 'new',
                title: '正在完成额度审批',
                content: '培养良好信誉,可提高申请成功率',
                opacity: 0,
                showClose: true,
                //在此处进一步定义每一次组件进行使用的时候的差异化处理
                onHide: () => {
                    console.log(this.showLoading + '99999');
                }
            });
            setTimeout(() => {
                loading.then(vm => {
                    vm.hideUi();
                });
            }, 20000);

整体思想:

1、组件函数化,引用更加方便;

2、在组件内部定义公共的事件发生时的方法,在外部传入,每个组件事件发生时不同的处理方式更加灵活;

3、通过普通的组件编写方法,再将组件套入此方法中,可以实现一套代码,两种引用方式,便于代码的书写以及阅读;

知识点涉及

Vue.extend(options)

用法: 使用基础Vue构造器,创建一个‘子类’,参数是一个包含组件选项的对象

data选项是特例,需要注意,在Vue.extend()中,他必须是函数

<div id="mount-point"></div>
// 创建构造器
var Profile = Vue.extend({
  template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>',
  data: function () {
    return {
      firstName: 'Walter',
      lastName: 'White',
      alias: 'Heisenberg'
    }
  }
})
// 创建 Profile 实例,并挂载到一个元素上。
new Profile().$mount('#mount-point')

vm.$mount( [elementOrSelector] )

用法:

如果 Vue 实例在实例化时没有收到 el 选项,则它处于“未挂载”状态,没有关联的 DOM 元素。可以使用 vm.$mount() 手动地挂载一个未挂载的实例。

如果没有提供 elementOrSelector 参数,模板将被渲染为文档之外的的元素,并且你必须使用原生 DOM API 把它插入文档中。

这个方法返回实例自身,因而可以链式调用其它实例方法。

实例:

var MyComponent = Vue.extend({
  template: '<div>Hello!</div>'
})

// 创建并挂载到 #app (会替换 #app)
new MyComponent().$mount('#app')

// 同上
new MyComponent({ el: '#app' })

// 或者,在文档之外渲染并且随后挂载
var component = new MyComponent().$mount()
document.getElementById('app').appendChild(component.$el)