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

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

相关推荐
来杯三花豆奶2 分钟前
Vue 2.0 Mixins 详解:从原理到实践的深度解析
前端·javascript·vue.js
code_YuJun5 分钟前
脚手架开发工具——dotenv
前端
San30.11 分钟前
深度驱动:React Hooks 核心之 `useState` 与 `useEffect` 实战详解
前端·javascript·react.js
Mr_Swilder21 分钟前
vscode没有js提示:配置jsconfig配置
前端
skywalk816323 分钟前
使用Trae 自动编程:为小学生学汉语项目增加不同出版社教材的区分
服务器·前端·人工智能·trae
huohuopro31 分钟前
LangChain | LangGraph V1教程 #3 从路由器到ReAct架构
前端·react.js·langchain
柒.梧.1 小时前
HTML入门指南:30分钟掌握网页基础
前端·javascript·html
用户54277848515401 小时前
Promise :从基础原理到高级实践
前端
用户4099322502121 小时前
Vue3条件渲染中v-if系列指令如何合理使用与规避错误?
前端·ai编程·trae
Mr_Swilder1 小时前
2025-12-20 vue3中 eslint9+和prettier配置
前端