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. 手写渲染函数

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

相关推荐
excel9 分钟前
前端加密的作用与使用场景
前端
计算机魔术师1 小时前
纽约时报版权诉讼披露:微软高管内部称训练 AI 是人类历史上最大规模劳动窃取
前端
去伪存真2 小时前
大模型的参数量为什么那么大?
前端·人工智能
IT_陈寒2 小时前
Java空指针这次真把我坑惨了
前端·人工智能·后端
高级程序源3 小时前
django大学生创新创业项目管理系统94923-计算机课程设计、毕业设计
javascript·vue.js·spring boot·后端·python·django·课程设计
八荒启·交互动画3 小时前
# Web特效020—让 Web 特效真正动起来:时间循环应该怎么接
前端·webgl·网页特效·八荒启-交互动画·八荒启
动恰客流统计3 小时前
线下零售数字化浪潮下,客流统计的3个核心发展趋势
大数据·前端·人工智能
用户938515635073 小时前
从小米前端面试题,彻底搞懂闭包、作用域链与 useState 惰性初始化
前端·面试
粥里有勺糖4 小时前
视野修炼第133期 | Native 回春了?
前端·github·agent
aichitang20245 小时前
前端小skill
前端·人工智能·算法·ai·前端框架