我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Vue插件实现过程中遇到的问题总结

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Vue插件实现过程中遇到的问题总结

场景介绍

最近做H5遇到了一个场景:每个页面需要展示一个带有标题的头部。一个实现思路是使用全局组件。假设我们创建一个名为TheHeader.vue的全局组件,伪代码如下:


<template>
    <h2>{{ title }}</h2>
</template>

<script>
export default {
props: {
    title: {
        type: String,
        default: ''
    }
}
}
</script>

创建好全局组件后,在每个页面组件中引用该组件并传入props中即可。例如我们在页面A中引用该组件,页面A对应的组件是A.vue


<template>
    <div>
        <TheHeader :title="title" />
    </div>
</template>
<script>
    export default {
        data() {
            title: ''
        },
        created(){
            this.title = '我的主页'
        }
    }
</script>

使用起来非常简单,不过有一点美中不足:如果头部组件需要传入的props很多,那么在页面组件中维护对应的props就会比较繁琐。针对这种情况,有一个更好的思路来实现这个场景,就是使用Vue插件。

同样是在A.vue组件调用头部组件,使用Vue插件的调用方式会更加简洁:


<template>
    <div />
</template>
<script>
    export default {
        created(){
            this.$setHeader('我的主页')
        }
    }
</script>

我们看到,使用Vue插件来实现,不需要在A.vue中显式地放入TheHeader组件,也不需要在A.vue的data函数中放入对应的props,只需要调用一个函数即可。那么,这个插件是怎么实现的呢?

插件实现

它的实现具体实现步骤如下:

  1. 创建一个SFC(single file component),这里就是TheHeader组件
  2. 创建一个plugin.js文件,引入SFC,通过Vue.extend方法扩展获取一个新的Vue构造函数并实例化。
  3. 实例化并通过函数调用更新Vue组件实例。

按照上面的步骤,我们来创建一个plugin.js文件:


import TheHeader from './TheHeader.vue'
import Vue from 'vue'

const headerPlugin = {
    install(Vue) {
        const vueInstance = new (Vue.extend(TheHeader))().$mount()
        Vue.prototype.$setHeader = function(title) {
            vueInstance.title = title
            document.body.prepend(vueInstance.$el)
            
        }
    }
}
Vue.use(headerPlugin)

我们随后在main.js中引入plugin.js,就完成了插件实现的全部逻辑过程。不过,尽管这个插件已经实现了,但是有不少问题。

问题一、重复的头部组件

如果我们在单页面组件中使用,只要使用router.push方法之后,我们就会发现一个神奇的问题:在新的页面出现了两个头部组件。如果我们再跳几次,头部组件的数量也会随之增加。这是因为,我们在每个页面都调用了这个方法,因此每个页面都在文档中放入了对应DOM。

考虑到这点,我们需要对上面的组件进行优化,我们把实例化的过程放到插件外面:


import TheHeader from './TheHeader.vue'
import Vue from 'vue'

const vueInstance = new (Vue.extend(TheHeader))().$mount()
const headerPlugin = {
    install(Vue) {
        Vue.prototype.$setHeader = function(title) {
            vueInstance.title = title
            document.body.prepend(vueInstance.$el)
            
        }
    }
}
Vue.use(headerPlugin)

这样处理,虽然还是会重复在文档中插入DOM。不过,由于是同一个vue实例,对应的DOM没有发生改变,所以插入的DOM始终只有一个。这样,我们就解决了展示多个头部组件的问题。为了不重复执行插入DOM的操作,我们还可以做一个优化:


import TheHeader from './TheHeader.vue'
import Vue from 'vue'

const vueInstance = new (Vue.extend(TheHeader))().$mount()
const hasPrepend = false
const headerPlugin = {
    install(Vue) {
        Vue.prototype.$setHeader = function(title) {
            vueInstance.title = title
            if (!hasPrepend) {
                document.body.prepend(vueInstance.$el)
                hasPrepend = true
            }
            
        }
    }
}
Vue.use(headerPlugin)

增加一个变量来控制是否已经插入了DOM,如果已经插入了,就不再执行插入的操作。优化以后,这个插件的实现就差不多了。不过,个人在实现过程中有几个问题,这里也一并记录一下。

问题二、另一种实现思路

在实现过程中突发奇想,是不是可以直接修改TheHeader组件的data函数来实现这个组件呢?看下面的代码:


import TheHeader from './TheHeader.vue'
import Vue from 'vue'

let el = null
const headerPlugin = {
    install(Vue) {
        Vue.prototype.$setHeader = function(title) {
            TheHeader.data = function() {
                title
            }
            const vueInstance = new (Vue.extend(TheHeader))().$mount()
            el = vueInstance.$el
            if (el) {
                document.body.removeChild(el)
                document.body.prepend(el)
            }
            
        }
    }
}
Vue.use(headerPlugin)

看上去也没什么问题。不过实践后发现,调用$setHeader方法,只有第一次传入的值会生效。例如第一次传入的是'我的主页',第二次传入的是'个人信息',那么头部组件将始终展示我的主页,而不会展示个人信息。原因是什么呢?

深入Vue源码后发现,在第一次调用new Vue以后,Header多了一个Ctor属性,这个属性缓存了Header组件对应的构造函数。后续调用new Vue(TheHeader)时,使用的构造函数始终都是第一次缓存的,因此title的值也不会发生变化。Vue源码对应的代码如下:


Vue.extend = function (extendOptions) {
    extendOptions = extendOptions || {};
    var Super = this;
    var SuperId = Super.cid;
    var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); 
    if (cachedCtors[SuperId]) { // 如果有缓存,直接返回缓存的构造函数
      return cachedCtors[SuperId]
    }

    var name = extendOptions.name || Super.options.name;
    if (process.env.NODE_ENV !== 'production' && name) {
      validateComponentName(name);
    }

    var Sub = function VueComponent (options) {
      this._init(options);
    };
    Sub.prototype = Object.create(Super.prototype);
    Sub.prototype.constructor = Sub;
    Sub.cid = cid++;
    Sub.options = mergeOptions(
      Super.options,
      extendOptions
    );
    Sub['super'] = Super;

    // For props and computed properties, we define the proxy getters on
    // the Vue instances at extension time, on the extended prototype. This
    // avoids Object.defineProperty calls for each instance created.
    if (Sub.options.props) {
      initProps$1(Sub);
    }
    if (Sub.options.computed) {
      initComputed$1(Sub);
    }

    // allow further extension/mixin/plugin usage
    Sub.extend = Super.extend;
    Sub.mixin = Super.mixin;
    Sub.use = Super.use;

    // create asset registers, so extended classes
    // can have their private assets too.
    ASSET_TYPES.forEach(function (type) {
      Sub[type] = Super[type];
    });
    // enable recursive self-lookup
    if (name) {
      Sub.options.components[name] = Sub;
    }

    // keep a reference to the super options at extension time.
    // later at instantiation we can check if Super's options have
    // been updated.
    Sub.superOptions = Super.options;
    Sub.extendOptions = extendOptions;
    Sub.sealedOptions = extend({}, Sub.options);

    // cache constructor
    cachedCtors[SuperId] = Sub; // 这里就是缓存Ctor构造函数的地方
    return Sub
  }

找到了原因,我们会发现这种方式也是可以的,我们只需要在plugin.js中加一行代码


import TheHeader from './TheHeader.vue'
import Vue from 'vue'

let el = null
const headerPlugin = {
    install(Vue) {
        Vue.prototype.$setHeader = function(title) {
            TheHeader.data = function() {
                title
            }
            TheHeader.Ctor = {}
            const vueInstance = new Vue(TheHeader).$mount()
            el = vueInstance.$el
            if (el) {
                document.body.removeChild(el)
                document.body.prepend(el)
            }
            
        }
    }
}
Vue.use(headerPlugin)

每次执行$setHeader方法时,我们都将缓存的构造函数去掉即可。

问题三、是否可以不使用Vue.extend

实测其实不使用Vue.extend,直接使用Vue也是可行的,相关代码如下:


import TheHeader from './TheHeader.vue'
import Vue from 'vue'

const vueInstance = new Vue(TheHeader).$mount()
const hasPrepend = false
const headerPlugin = {
    install(Vue) {
        Vue.prototype.$setHeader = function(title) {
            vueInstance.title = title
            if (!hasPrepend) {
                document.body.prepend(vueInstance.$el)
                hasPrepend = true
            }
            
        }
    }
}
Vue.use(headerPlugin)

直接使用Vue来创建实例相较extend创建实例来说,不会在Header.vue中缓存Ctor属性,相较来说是一个更好的办法。但是之前有看过Vant实现Toast组件,基本上是使用Vue.extend方法而没有直接使用Vue,这是为什么呢?

总结

到此这篇关于Vue插件实现过程中遇到问题的文章就介绍到这了,更多相关Vue插件实现问题内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Vue插件实现过程中遇到的问题总结

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Vue插件实现过程中遇到的问题有哪些

这篇文章将为大家详细讲解有关Vue插件实现过程中遇到的问题有哪些,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。场景介绍最近做H5遇到了一个场景:每个页面需要展示一个带有标题的头部。一个实现思路是使用全局组
2023-06-20

在 Vue 中使用 dhtmlxGantt 组件时遇到的问题汇总(推荐)

dhtmlxGantt一个功能丰富的甘特图插件,支持任务编辑,资源分配和多种视图模式,这篇文章主要介绍了在 Vue 中使用 dhtmlxGantt 组件时遇到的问题汇总,需要的朋友可以参考下
2023-03-19

JS组件bootstrap table分页实现过程中遇到的问题如何解决

这篇“JS组件bootstrap table分页实现过程中遇到的问题如何解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“J
2023-07-04

redis性能优化之生产中实际遇到的问题及排查总结

这篇文章主要介绍了redis性能优化之生产中实际遇到的问题及排查总结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2022-12-22

Python结巴中文分词工具使用过程中遇到的问题及解决方法

本文实例讲述了Python结巴中文分词工具使用过程中遇到的问题及解决方法。分享给大家供大家参考,具体如下: 结巴分词是Python语言中效果最好的分词工具,其功能包括:分词、词性标注、关键词抽取、支持用户词表等。这几天一直在研究这个工具,在
2022-06-04

Excel数据导入Mysql常见问题汇总:如何解决导入过程中遇到的数据校验问题?

Excel数据导入Mysql常见问题汇总:如何解决导入过程中遇到的数据校验问题?导入Excel数据到MySQL数据库是我们在数据处理工作中经常需要进行的操作。然而,在这个过程中常常会遇到一些数据校验问题,导致导入失败或者导入后的数据不符合我
2023-10-22

layui实际项目使用过程中遇到的兼容性问题及解决

这篇文章主要介绍了layui实际项目使用过程中遇到的兼容性问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
2023-05-17

编程热搜

目录