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

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

相关推荐
纽格立科技5 分钟前
DRM 发射端链路图(上)
前端·人工智能·车载系统·信息与通信·传媒
一 乐11 分钟前
幼儿园管理系统|基于springboot + vue幼儿园管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·幼儿园管理系统
云水一下18 分钟前
Vue.js从零到精通系列(七):高级特性实战——Teleport、异步组件、自定义指令与TypeScript深度结合
前端·vue.js·typescript
qq43569470120 分钟前
Vue05
前端·vue.js
qq_4221525722 分钟前
PDF 解密工具怎么选?2026 年文档密码移除方案与注意事项
java·前端·pdf
YHHLAI26 分钟前
前端工程化调用 AI 多模态生图模型:Qwen Image Demo 实战
前端·人工智能
英勇无比的消炎药37 分钟前
收藏备用TinyVue开发高频踩坑问题合集
vue.js
To_OC39 分钟前
我一直以为 Ajax 是个黑盒,直到我写了这 50 行代码
前端·后端·全栈
用户0595401744644 分钟前
RAG 记忆层踩坑实录:用户偏好凭空消失,我排查了 4 小时,最后用 LangChain + Chroma 搭了套自动化回归测试
前端·css
英勇无比的消炎药44 分钟前
体积瘦身TinyVue打包优化与按需加载实践
vue.js·前端工程化