vue 2.x 如何“主动”的动态注入组件?

vue 2.6

目前主要用的组件“异步注入”方式,大概如下:

需要注入的组件文件:

global-custom-components.js

const install = Vue => {

Vue.component('comp-name', () => import(./components/compName.vue))

}

export default install

然后
main.js

import customComponents from './global-custom-components.js'

Vue.use(customComponents)

然后当某个包含组件 comp-name 的页面被打开时,该组件才会被下载、导入,从而达到“减负”的做用。

<comp-name></comp-name>

但是感觉这种方式还是显得比较“隐式”或者说“被动”,当某个页面使用到该组件时,该组件非常隐蔽的被“下载”下来,这是不受我们控制的。

现在的有一个需求是,我需要在组件开始下载的过程,显示一个动画,当组件被完全下载,动画结束,然后展示这些组件。

假设,有一个预览页面,页面上有 10 个自定义组件,我需要做如下步骤:

let compList = [comp_1, comp2, ......] // 共 10 个组件
1、展示 loading 动画效果;
2、循环判断 compList 当前vue实例是否包含了每一个组件(因为组件类型相同,虽然有 10 个组件,但实际上只有 5 类不统的组件);
3、如果不存在,同步导入组件;
4、当组件全部导入完毕;
5、暂停动画并展示组件;

请问第三步该如何实现?

——————————— 分割线 ——————————

@lining 感谢回复,我回答的内容太长,保存不了,以补充形式回复在这,感谢!

看了回复,但还是不太明白,感觉没回到到点上。
我尝试着写了一个 mixins ,里面有一个函数用来动态注册组件,发现有如下问题,伪代码如下:

loadComp() {

//组件json对象,包含组件需要的一些参数

let componentList = []

componentList.forEach(async (e) => {

let targetData = e

// 实际监听,能获取到正确的组件路径

let path = `../../../packages/components/${targetData.package}/${targetData.category}/${targetData.key}/index.vue`

let key = `V${targetData.key}`

let ifExists = Vue.component(key)

if(!ifExists) {

Vue.component(

key,

() => import(path)

)

}

})

},

上面代码有如下问题:
1、Vue.component(key),key为任何参数,该方法在 bool 环境下都为 true,所以不能用来判断 xxx 组件是否被注册过;
2、如下代码,在 path 获取到正确的路径后,

Vue.component(

key,

() => import(path)

)

执行报错,错误信息

Failed to resolve async component: () => __webpack_require__("./src/views/preview/mixins lazy recursive")(path)

Reason: Error: Cannot find module '../../../packages/components/Charts/Bars/BarCrossrange/index.vue'

—————————————— 分割线 ————————————

—————————————— 答案 ————————————

定义获取组件js文件:

// 通过 require.context 找到路径“./components/”下的所有 index.vue 文件

// 获取到政府列表,如:

// ./components/comp_a/index.vue

// ./components/comp_b/index.vue

// ./components/comp_c/index.vue

// 所有组件均已 export default {} 方式定义——vue 2.6(以下) 的通用方式

const modules = require.context(

// 其组件目录的相对路径

'./components/',

// 是否查询其子目录

true,

// 匹配基础组件文件名的正则表达式

// /Base[A-Z]\w+\.(vue|js)$/

/\w+\/index.vue/

)

// 通过组件名称 compName(comp_a),获取组件

export const fetchComponent = (compName) => {

const module = modules

let keys = module.keys()

for(let i = 0; i < keys.length; i++) {

let key = keys[i]

const urlSplit = key.split('/')

if(urlSplit[urlSplit.length -2 ] === compName) {

const componentConfig = module(key).default

// 返回组件对象

return componentConfig.default || componentConfig

}

}

}

安装组件文件:

import Vue from 'vue'

/**

* * 动态注册组件

*/

export const componentInstall = (key, node) => {

//如果key组件没有被注册过,且组件对象node存在,则注册该组件

if(!Vue.component(key) && node) {

console.log('注册组件:' + key)

Vue.component(key, node)

}else{

console.log('组件:' + key + ' 已存在')

}

}

消费文件:

componentInstall('comp_a', fetchComponent('comp_a'))

—————————————— 分割线 ————————————

—————————————— 答案 (最终)————————————

在之前得到的答案中,其实还是有问题的,我发现所有组件都被打包到app.js里面了,这样就失去了异步加载的意义了。

经过我的测试,发现问题出在require.context()方法,该方法把匹配到的文件全部打包进app.js了,所以不能用此方法。

const modules = require.context(

// 其组件目录的相对路径

'./components/',

// 是否查询其子目录

true,

// 匹配基础组件文件名的正则表达式

// /Base[A-Z]\w+\.(vue|js)$/

/\w+\/index.vue/

)

经过我的测试,发现问题其实很简单,如果你只需要单个组件异步加载(不需要多个组件“全部”完成加载的场景),那么直接可以:

//item.chartKey 组件名称

//() => fetchChartComponent(item) 异步加载组件对象

//注册异步组件 item.chartKey,页面可以使用该组件<item.chartKey></item.chartKey>,组件下载完成后,自动展示在页面上。

//重要(注意):

//也就是说,页面使用该组件时,即使组件没有下载完成,也是没有问题的。

componentInstall(item.chartKey, () => fetchChartComponent(item))

//注册异步组件 item.conKey

componentInstall(item.conKey, () => fetchConfigComponent(item))

此种方式其实和原问题描述中的使用方式一致。

 export const fetchChartComponent = (chartConfig) => {

const chart = import(`./components/${chartConfig.package}/${chartConfig.category}/${chartConfig.key}/index.vue`)

return chart

}

import Vue from 'vue'

/**

* * 动态注册组件

*/

export const componentInstall = (key, node) => {

if(!Vue.component(key) && node) {

console.log('注册组件:' + key)

Vue.component(key, node)

}else{

console.log('组件:' + key + ' 已存在')

}

}

如果你需要等很多组件全部加载完成后,再执行某个逻辑,那么可以写

let f1 = fetchChartComponent(item1)

let f2 = fetchChartComponent(item2)

let f3 = fetchChartComponent(item3)

//重要(注意):

//此时,在所有组件未"全部"加载完成前,页面不能使用到了某个为下载完成的组件。

//通常,此种场景下,会在页面所有组件最外层套一个div,然后用 if 来控制,如果全部加载完成,该div才能展示(v-if='true')。

this.loading = true

Promise.all([f1, f2, f3]).then(res => {

this.loading = false

}).catch(err => {

this.loading = false

})


回答:

Vue.component('comp-name', () => import(./components/compName.vue))

这句话拆开看看

() => import(./components/compName.vue)

继续拆开

function(){

return import(./components/compName.vue)

}

现在明白了吗?这本质是一个 function

vue 2.x 如何“主动”的动态注入组件?

vue 2.x 如何“主动”的动态注入组件?

https://v2.cn.vuejs.org/v2/guide/components-dynamic-async.html#%E5%BC%82%E6%AD%A5%E7%BB%84%E4%BB%B6


回答:

可以看一下这个项目 unplugin-vue-components,应该是符合你预期的。

Anthony Fu 这位佬还提供、参与了了其它类似项目的开发,都可以了解一下。

以上是 vue 2.x 如何“主动”的动态注入组件? 的全部内容, 来源链接: utcz.com/p/933201.html

回到顶部