Skip to content

Latest commit

 

History

History
357 lines (307 loc) · 13.3 KB

README_zh.md

File metadata and controls

357 lines (307 loc) · 13.3 KB

vue-mount

vue-mount —— 一个动态加载 Vue 组件并维护组件树的工具库。

version dependencies

中文文档 | English

Demos

Table of contents

开始

安装

你可以通过 npm 来安装。

npm i vue-mount -S

或者通过 yarn 安装:

yarn add vue-mount

还可以直接使用 CDN 方式引入

先决条件:该方式必须先要引入完整 Vue,或者在 window 对象上暴露出 Vue 构造函数。

<!-- 首先要引入 Vue -->
<!-- 可以在 `window.Vue` 中拿到 `Vue` 构造函数 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.10/dist/vue.js"></script>

<!-- 引入 vue-mount -->
<script src="https://cdn.jsdelivr.net/npm/vue-mount/dist/vue-mount.min.js"></script>
<script>
    var mount = window['VueMount'].mount;
    var Mount = window['VueMount'].default;
</script>

用法

基本用法

// 引入 mount 方法
import { mount } from "vue-mount";
// 引入 Alert 组件
import Alert from "./alert.vue";

// 动态挂载 Alert 组件实例并返回
const alert = mount(Alert);

高级用法

// 暴露出的方法 `mount(cmp, opt)` 相当于 `new Mount(cmp, opt).mount()`的一个快捷语法糖
import Mount from "vue-mount";
import Alert from "./alert.vue";

const mountAlert = new Mount(Alert, {
    // 挂载目标
    // 特殊值 `new` 表明去创建并挂载到一个新的 Vue 根实例上
    target: 'new',
    // 需要传入组件 props 参数的数据对象
    props: {
        content: 'Mount alert component to a new Vue root instance'
    },
    // 可以修改组件内部的 响应式数据
    data: {
        someInnerData: 'modified'
    },
    // 添加事件监听器
    on: {
        'some-event'(eventData, vm, mnt) {
            // 每个处理函数都会追加传入两个辅助参数
            // eventData 是原来的事件监听器参数(根据组件发射的事件,参数可能不存在也可能存在多个)
            // vm 是当前挂载的组件实例
            // mnt(mount的缩写)是当前创建挂载组件的 vue-mount 实例
        }
    },
    watch: {
        content: {
            immediate: true,
            handler(newValue, oldValue, vm, mnt) {
                console.log('Content has changed: ' + newValue);
                // 取消 watch
                // 当 `immediate` 为 true 时,需要确保取消函数存在
                if (mnt.unwatchMapper.content) {
                    mnt.unwatchMapper.content();
                }
            }
        }
    }
});

// 拿到 Alert 组件的实例
let alertVm = mountAlert.getInstance();
// 挂载实例
// 另外,在 Mount 实例上调用多次 mount 方法只会挂载组件一次
alertVm = mountAlert.mount();

// 挂载后动态设置组件的 props 属性值
mountAlert.set({
    props: { content: 'Props changed' }
});
// 完全销毁一个实例,并且移除组件对应的 DOM。
mountAlert.destroy();

// 销毁之后新的组件实例会被重新挂载
alertVm = mountAlert.mount();

MountOption 配置项

target

  • 类型: { string | Element | Vue | VNode }
  • 默认值: new
  • 说明: 你可以传入 css selector, Element, Vue instance, VNode 或者是包括 newroot 的特殊预设值。
    • new: 默认特殊预设值. Vue 组件实例会被挂载到一个新创建的 Vue 根实例上。
    • root: Vue 组件实例将被挂载到现有的 Vue 根实例上。 如果在 MountOption.root 选项下找不到根实例或根元素,组件实例将被挂载到一个新的 Vue 根实例上,其行为将与选项 new 相似
    • 当传入一个 Vue 实例对象 时,新的组件实例将会替换/追加到传入的实例(具体参见 MountOption.mode 配置),并且在 Vue 组件树上更新。 当新的组件实例挂载时,传入的实例将会被销毁。
  • 用例:
    mount(Alert, { target: "root" };
    mount(Alert, { target: "#target" };
    mount(Alert, { target: document.querySelector('.list') };
    mount(Alert, { target: this.$refs.component };
    mount(Alert, { target: this.$refs.component.$slots.default[0] };

特别注意:当配置为 new 时,挂载的组件无法访问到创建根实例时传入的配置,导致在挂载的组件内无法访问 this.$router 等在根组件上全局注册的配置(原因是创建了一个新的根实例,但是存在替代方案);其他情况下,vue-mount 会自动查询并加入组件树上下文。

mode

  • 类型: { string }
  • 默认值: replace
  • 备选项: replace, append
  • 说明: 指定挂载方式:替换追加 模式。对应到其组件树上的行为。
  • 用例:
    // Alert 组件实例将会被追加挂载到当前组件(this)上。
    mount(Alert, { 
        target: this,
        mode: 'append'
    });

值得注意的是:当配置项 target 值为 new 或者 root 时,mode 将被忽略并重置成 append

root

  • 类型: { string | Element | Vue }
  • 默认值: #app
  • 说明: 指定当前应用的根元素.(所有给出的值在内部都将被解析为HTML元素)。

rootOptions

  • 类型: { VueOptions }
  • 默认值: {}
  • 说明: 允许指定在创建新的 Vue 根实例时的构造选项。
  • 用例:
    mount(Alert, {
        rootOptions: {
            data: {
                root: 'new root instance'
            },
            methods: { ... },
            ...
        }
    };

props

  • 类型: { Object }
  • 说明: 指定传入组件的 props 值。

data

  • 类型: { Object }
  • 说明: 指定的值将会在实例创建完毕(也可能未挂载)时修改组件内部响应式数据。

on

  • 类型: { [event: string]: Function | Object }
  • 说明: 将事件侦听器附加到组件实例。
    • 内置 事件:

      • mount:mount: 在调用 mount 方法或准备挂载组件时触发。
      • mount:destroy: 触发​​何时(底层)调用 destroy 方法
    • 当传入配置对象:

      • once { Boolean }: 是否在第一次触发时删除侦听器(只触发一次)。
      • handler { Function }: 事件触发回调函数.与 Vue 的事件回调函数 (vm.$on/$once)相比,会在参数列表后追加两个辅助参数,当前组件实例当前 Mount 实例

      回调函数的 this 指向为当前的 Mount 实例,当然你可以使用箭头函数来避免这一行为。

  • 用例:
    mount(Alert, {
        on: {
            'mount:mount'(vm, mnt) {
                console.log('mount:mount');
                vm.doSomething();
            },
            'mount:destroy'() {
                console.log('mount:destroy')
            },
            remove: {
                once: true,
                handler: (vm, mnt) => {
                    console.log('remove');
                }
            },
            'remove-with-data'(...args) {
                console.log(args);
            }
        }
    })

watch

  • 类型: { [key: string]: Function | Object }
  • 说明: 一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。
    • 当传入配置对象:

      • immediate { Boolean }: Passing in immediate: true in the option will trigger the callback immediately with the current value of the expression.
      • deep { Boolean }: 为了发现对象内部值的变化,可以在选项参数中指定 deep: true 。注意监听数组的变动不需要这么做。
      • handler { Function }: 值更改时的回调函数。与 Vue 的回调函数(vm.$watch)相比,此回调函数通常有4个参数,如:newValue、oldValue、vm、mnt。最后两个辅助参数是:当前组件实例当前 Mount 实例

      回调函数的 this 指向为当前的 Mount 实例,当然你可以使用箭头函数来避免这一行为。

    • Unwatch: 传给 watch 选项每个键都将添加到 Mount 实例的属性 unwatchMapper 中,您可以调用类似 mnt.unwatchMapper.attr() 的方法来取消监听。

  • 用例:
    mount(Alert, {
        watch: {
            otherAttr(newV) {
                console.log(newV);
            }
            attr: {
                handler(newValue, oldValue, vm, mnt) {
                    console.log(args);
                    // 取消 watch
                    // 当 `immediate` 为 true 时,需要确保取消函数存在
                    if (mnt.unwatchMapper.content) {
                        mnt.unwatchMapper.content();
                    }
                },
                immediate: true,
            },
        }
    })

注意:只有在 data 选项中提前声明数据,值变化时监听回调函数才能被正常调用。


实例方法

getInstance(MountOptions)

  • 参数: { MountOptions }
  • 返回: { Vue }
  • 说明: 返回一个 vue 组件实例。多次调用该方法只会创建实例一次,且将返回相同的实例。

注意:当选项 target 的值是 root, 且没有找到根实例/元素时(这种情况将导致创建一个新的 Vue 根实例)或者当值为 new 时,两种情况都将导致组件实例会被立即挂载。

值得注意:为了确保行为一致性,推荐优先使用 #mount 方法。

mount(MountOptions)

  • 参数: { MountOptions }
  • 返回: { Vue }
  • 说明: 挂载 Vue 组件、更新组件树并返回 Vue 组件实例。

若在组件已被 destroy 之后再次调用该方法将重新装载该组件(你可以认为 mount 实例为组件工厂)。

多次调用该方法只会挂载实例一次,且将返回相同的实例。

set(MountDataOptions)

  • 参数: { MountDataOptions }
  • 返回: { Mount } 当前 Mount 实例。
  • 说明: 动态设置组件实例的 propsdatalisteners

destroy()

  • 返回: { Vue }
  • 说明: 销毁 Vue 组件实例并删除关联的元素,并更新组件树。与 Vue 的 $destroy 方法不同,销毁整个组件与其 DOM。

getDom()

  • 返回: { Element | Node }
  • 说明: 返回组件实例相关联的 DOM。

在组件上添加的方法

$getMount()

  • 返回: { VueMount }
  • 说明: 返回组件实例相关联的 VueMount 实例。

已知的问题

无法访问到 $router/$store

  • 当 VueMount 将组件挂载到新的 Vue 根实例上时,该组件将无法获取到在原根组件配置的 $router/$store 等属性(原因),当然也有以下方式来解决该问题。

    mount(Component, {
        ...
        data: {
            $store: this.$store,
            // 为什么不是 $router? VueRouter 在内部使用了 Object.defineProperty 方法并只设置了 getter 属性,故在该组件无法覆盖这个值
            router: this.$router,
            ...
        },
        ...
    });

    随后可在组件内部使用 this.$store/this.router 来得到该值。

  • 当组件已经挂载在原根实例上,但在组件的 created/mounted 等生命周期内获取不到该值时,需要使用 VueMount 内置的事件 来解决:

    mount(Component, {
        ...
        on: {
            'mount:mount'(vm) {
                vm.$router;
                vm.$store;
            }
        },
        ...
    }

    原因是现版本 VueMount 在内部统一在组件挂载之后才计算父组件取值。