Vue2实践(1)之用Vue.extend做一个函数调用的Dialog

前言

在之前的文章中我整理了一些Vue的核心:数据代理、数据劫持、依赖收集、视图更新、diff。Vue的有趣和强大当然不止于此,接下来我会通过一些实践来整理Vue2的其他功能、特性。

通过前面的代码,其实我们能发现,使用Vue2来开发最终还是要通过JS实现的。所以这一节我们来使用JS函数实现一个Dialog,目的是手动地挂载这个组件,使它能在项目中的任何页面上调用。

基础

Dialog组件

xml 复制代码
<!-- /src/components/Dialog.vue -->
<template>
    <div class="container">
        <div>{{ text }}</div>
    </div>
</template>
<script>
export default {
    props: {
        text: {
            type: String,
            default: ''
        }
    },
}
</script>

调用函数

javascript 复制代码
// /src/utils/dialog.js
import Vue from "vue";
import Dialog from "@/components/Dialog.vue";
​
let DialogContructor = Vue.extend(Dialog);
​
export default function openDialog(options) {
    const instance = new DialogContructor({
        props: {
            text: options.text,
        },
        render(h) {
            return h(Dialog, {
                props: {
                    text: this.text
                }
            })
        }
    })
    document.body.appendChild(instance.$mount().$el); // 手动将实例化的元素挂载到body上
}

使用

xml 复制代码
<!-- 组件A -->
<template>
<!-- 省略 -->
</template>
​
<script>
import openDialog from '@/uitls/dialog';
export default {
    // ...
    methods: {
        click() {
            openDialog({
                text: 'Hi~Vue2'
            })
        }
    }
}
</script>

通过以上代码已经可以实现这个简单的功能了

进阶-在Dialog中使用slot

使用slot的组件可以给使用者提供更灵活的调用,比如Dialog有时候仅仅用作一个消息通知,有时候又可以模拟一个弹出输入框。

xml 复制代码
<!-- /src/components/Dialog.vue -->
<template>
    <div class="container">
        <div class="title">{{ title }}</div>
        <div class="content">
            <slot></slot>
        </div>
    </div>
</template>
<script>
export default {
    props: {
        title: {
            type: String,
            default: ''
        }
    },
}
</script>
xml 复制代码
<!-- 组件A -->
<template>
<!-- 省略 -->
</template>
​
<script>
import openDialog from '@/uitls/dialog';
import TextInput from '@/components/FieldTypes/TextInput.vue';
export default {
    // ...
    methods: {
        click() {
            openDialog({
                title: 'Hi~Vue2',
                components: {
                    TextInput
                },
                slotRender: (h) => {
                    props: {
                        field: {
                            type: 'text',
                            name: '选项名称',
                            placeholder: '请输入选项名称',
                            required: true,
                            value: ''
                        }
                    },
                }
            })
        }
    }
}
</script>

在openDialog中传入的options最终在dialog.js中进行处理

javascript 复制代码
// /src/utils/dialog.js
import Vue from "vue";
import Dialog from "@/components/Dialog.vue";
​
let DialogContructor = Vue.extend(Dialog);
​
export default function openDialog(options) {
    const instance = new DialogContructor({
        props: {
            title: options.title,
        },
        components: options.components || {},
        render(h) {
            const slot = slotRender ? slotRender(h) : null;
            return h(Dialog, {
                props: {
                    text: this.text
                },
                scopedSlots: {
                    default: () => slot
                }
            })
        }
    })
    document.body.appendChild(instance.$mount().$el); // 手动将实例化的元素挂载到body上
}

通过这样的方式,我们在组件中可以更灵活地使用这个函数调用组件,往其中塞入自定义的组件结构

进阶-函数调用组件的组件间通信

想要完成组件间通信很重要的一点在于运用JS的引用类型特性、以及$emitsAPI。前者不需要特别赘述,但是$emitsAPI在这里的使用值得注意:

首先我们假设TextInput中已经有$emit('input', $event.target.value)

xml 复制代码
<!-- 组件A -->
<template>
<!-- 省略 -->
</template>
​
<script>
import openDialog from '@/uitls/dialog';
import TextInput from '@/components/FieldTypes/TextInput.vue';
export default {
    // ...
    data() {
        return {
            componentData: {}
        }
    }
    methods: {
        click() {
            openDialog({
                title: 'Hi~Vue2',
                components: {
                    TextInput
                },
                slotRender: (h) => {
                    props: {
                        field: {
                            type: 'text',
                            name: '选项名称',
                            placeholder: '请输入选项名称',
                            required: true,
                            value: ''
                        }
                    },
                    on: {
                        'input': (value) => formData['text'] = value
                    }
                }
            })
        }
    }
}
</script>
javascript 复制代码
// /src/utils/dialog.js
import Vue from "vue";
import Dialog from "@/components/Dialog.vue";
​
let DialogContructor = Vue.extend(Dialog);
​
export default function openDialog(options) {
    const instance = new DialogContructor({
        props: {
            title: options.title,
        },
        components: options.components || {},
        render(h) {
            const slot = slotRender ? slotRender(h) : null;
            return h(Dialog, {
                props: {
                    text: this.text
                },
                on: {
                    'custom-confirm': options.methods.confirm
                },
                scopedSlots: {
                    default: () => slot
                }
            })
        }
    })
    document.body.appendChild(instance.$mount().$el); // 手动将实例化的元素挂载到body上
}
xml 复制代码
<!-- /src/components/Dialog.vue -->
<template>
    <div class="container">
        <div class="title">{{ title }}</div>
        <div class="content">
            <slot></slot>
        </div>
        <div class="actions">
            <button class="confirm" @click="confirm">确定</button>
        </div>
    </div>
</template>
<script>
export default {
    props: {
        title: {
            type: String,
            default: ''
        }
    },
    methods: {
        confirm () {
            this.$emit('custom-confirm')
        },
    }
}
</script>

这里其实通过了两个$emits事件以及引用类型特性,完成了函数调用组件的通信。

总结

根据这个例子,后续会展开整理一下内容:

  1. Vue.extend()API
  2. Vue源码对methods以及事件的处理,补全实例化Vue的一些内容
  3. 手写渲染函数

下一个实例的话会选择动态表单这个经典案例

相关推荐
LaiYoung_几秒前
深入解析 single-spa 微前端框架核心原理
前端·javascript·面试
Danny_FD1 小时前
Vue2 + Node.js 快速实现带心跳检测与自动重连的 WebSocket 案例
前端
uhakadotcom1 小时前
将next.js的分享到twitter.com之中时,如何更新分享卡片上的图片?
前端·javascript·面试
韦小勇1 小时前
el-table 父子数据层级嵌套表格
前端
奔赴_向往1 小时前
为什么 PWA 至今没能「掘进」主流?
前端
小小愿望1 小时前
微信小程序开发实战:图片转 Base64 全解析
前端·微信小程序
掘金安东尼1 小时前
2分钟创建一个“不依赖任何外部库”的粒子动画背景
前端·面试·canvas
电商API大数据接口开发Cris1 小时前
基于 Flink 的淘宝实时数据管道设计:商品详情流式处理与异构存储
前端·数据挖掘·api
小小愿望1 小时前
解锁前端新技能:让JavaScript与CSS变量共舞
前端·javascript·css
程序员鱼皮1 小时前
爆肝2月,我的 AI 代码生成平台上线了!
java·前端·编程·软件开发·项目