动态挂载Vue组件:函数式组件

提要

要动态挂载一个Vue组件Component,就必须实例化一个Vue对象,指定渲染函数和挂载位置。
而不能直接向document写入html代码的方式插入DOM,这不会触发Vue框架的渲染。
和挂载App一样:

import Vue from 'vue';
import App from './App.vue';

new Vue({
    render: h => h(App);
}).$mount('#app');

其中h是function (createElement, context)中的createElement。render其实应该这样写:

render: function (createElement, context) {
    return createElement(App);
}

将 h 作为 createElement 的别名是 Vue 生态系统中的一个通用惯例,实际上也是 JSX 所要求的。

文档:https://cn.vuejs.org/v2/guide/render-function.html

函数式使用组件

以下文件实现了一个全屏的提示框组件,要弹出这个提示框,可以往页面某个占位div上挂载:

new Vue({
    render: h => h(FrameAlert),
}).$mount($(document.body)[0])

等等,参数、事件怎么传递呢?

组件需要的一切都是通过 context 参数传递,那么如果我们传递给createElement的第一个参数是一个单文件导出的组件对象,
可以直接将 context 作为第二个参数,会成功到达该单文件组件的render函数!
context是一个包括如下字段的对象:

props:提供所有 prop 的对象
children: VNode 子节点的数组
slots: 一个函数,返回了包含所有插槽的对象
scopedSlots: (2.6.0+) 一个暴露传入的作用域插槽的对象。也以函数形式暴露普通插槽。
data:传递给组件的整个数据对象,作为 createElement 的第二个参数传入组件
parent:对父组件的引用
listeners: (2.3.0+) 一个包含了所有父组件为当前组件注册的事件监听器的对象。这是 data.on 的一个别名。
injections: (2.3.0+) 如果使用了 inject 选项,则该对象包含了应当被注入的属性。

划重点!以下字段你很难在文档中找到,但却经常需要使用
on:事件
domProps:dom参数

所以,我们这样传递参数和事件:

new Vue({
    render: h => h(FrameAlert, {
        props: { msg: '提示' },
        on: { click(event) { console.log(event.target.innerText); }, },
    }),
}).$mount($(document.body)[0])

文件:FrameAlert.vue

<template>
    <div class="frame_alert">
        <div class="alert">
            <div class="msg">
                <div>
                    <span v-html="filtedMsg"></span>
                </div>
            </div>
            <div class="buttons">
                <hr>
                <div class="btnwrapper">
                    <span @click=handle class="button" href="#">确定</span><span @click=handle class="button" href="#">取消</span>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    export default {
        props: {
            msg: {
                type: String,
                default: "Message in here...",
            },
        },
        data() {
            return {
            };
        },
        computed: {
            filtedMsg() {
                return this.filteMsg(this.msg);
            },
        },
        methods: {
            handle(event) {
                this.$emit('click', event);
            },
            filteMsg(msg) {
                return msg.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/(?:\n|\\n)/g, '<br>');
            }
        },
        mounted() {
        },
    }
</script>

<style scoped>
    .frame_alert {
        position: fixed;
        top: 0;
        width: 100%;
        height: 100%;
        padding: 0;
        margin: 0;
        background: rgba(0, 0, 0, 0.8);
    }
    .alert {
        /* 外观 */
        width: fit-content;
        min-width: 300px;
        max-width: 60%;
        border-radius: 15px;
        background: white;
        color: black;
        word-break: break-all;
        /* 定位 */
        position: absolute;
        top: 40%;
        left: 50%;
        transform: translate(-50%, -50%);
    }
    .msg {
        max-height: 200px;
        margin: 20px;
        margin-bottom: 80px;
        overflow: auto;
        text-align: center;
        font-size: 22px;
    }
    .msg::-webkit-scrollbar {
        /*隐藏滚轮*/
        display: none;
    }
    .buttons {
        position: fixed;
        margin: 0;
        bottom: 0;
        width: 100%;
        height: 60px;
        margin: auto;
        border-radius: 0 0 15px 15px;
    }
    .btnwrapper {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 80%;
        margin-top: 2px;
    }
    .button {
        text-align: center;
        font-size: 22px;
        display: inline-block;
        width: 50%;
        margin: 0;
        padding: 0;
        color: blue;
        text-decoration: none;
        cursor: pointer;
        user-select: none;
    }
    .button:active {
        text-shadow: 1px 1px 10px;
    }
</style>

END

posted @ 2020-03-25 16:42  develon  阅读(3323)  评论(0编辑  收藏  举报