场景。在公司项目开发过程中,我们可能会遇到比较复杂的页面。这时,我们将页面拆分成多个子组件,然后定义一个索引组件导入所有子组件,然后出现如下场景:当前页面有5个子组件,那么需要引入5个次,然后注册了5次,最后在html中渲染了5次,有了这个理论上,如果一个页面需要8个或者10个子组件,那么我们index文件的代码就会变得冗余,难以维护。我们每添加一个子组件文件,就需要在索引文件中添加一个子组件维护一次。了解了索引引入多个子组件的痛点之后,接下来我们来优化一下。优化思路是一次导入所有子组件(只执行一次导入子组件),并使用循环循环注册导入的子组件,而不是一个一个注册。在html中,只渲染一次就可以动态渲染所有的子组件(Dynamiccomponentrendering)如果我们把上面三个步骤都实现了,是不是就可以解决上面提到的痛点呢?优化的第一步(一次引入所有子组件)引入了一个新的API,require.context,什么是require.context?概念是一个webpackapi,通过执行require.context函数获取特定的上下文,主要用于自动导入模块。在前端工程中,如果遇到从一个文件夹导入很多模块的情况,可以使用这个api,它会遍历文件夹中的指定文件,然后自动导入,这样就不需要显式每次调用import导入模块。语法require.context(directory,(useSubdirectories=true),(regExp=/^\.\/.*$/),);require.context接收三个参数:directory:要搜索的目录useSubdirectories:是否也搜索其子目录的标志regExp:匹配文件的正则表达式。child-components文件夹下有四个vue组件:car.vue、custom.vue、news.vue、sports.vue.letfiles=require.context('./child-components',false,/\.vue$/)letmodules={}files.keys().forEach(key=>{constmoduleName=key.replace(/(\.\/|\.vue)/g,'')modules[moduleName]=files(key).default})exportdefaultmodules代码分析:require.context函数执行后返回一个函数,这个函数有3个属性id,keys,resolve,这里重点分析keys,keys是一个函数函数webpackContextKeys,keys()执行正则匹配后返回一个模块名数组(正则匹配文件名的相对路径)。上面说了require.context函数执行后返回一个函数,它接收一个参数req,这个参数是正则匹配文件名的相对路径,也就是keys()函数返回的数组中的每一项。此时require.context函数的结果赋值给变量files,然后执行files(参数:正则匹配文件名相对路径)函数,得到一个Module对象,有一个default属性onModule对象,该值是我们需要导入的组件的Module对象上的默认属性和我们通过importCarfrom'./child-components/car.vue'导入的Car是一样的。分析完require.context函数和require.context返回的三个属性,我们继续分析下面的代码:现在我们可以获取到某个文件夹下所有的vue组件,我们希望导出一个对象,存放每个vue组件根据文件名按照映射关系导出。这里可以先看一下我们提前导出的对象的结构:当然,上图中标注的对象key也是可以自定义的。我们这里使用vue的文件名,因为这样可以直观的看出它们之间的映射关系,car属性对应ca对于r.vue组件,我们首先定义一个空对象作为我们最终导出的对象letmodules={}因为在执行keys()之后,它返回一个匹配的模块名数组(正则匹配文件名的相对路径)成功地。所以我们遍历数组,首先得到每个vue文件的名字,因为我们现在有了vue文件的相对路径(./car.vue),所以我们需要对./car.vue进行正则匹配处理,得到到car,代码如下:key.replace(/(\.\/|\.vue)/g,'')这时候我们需要导出的对象已经有了key值,现在value少了,也就是我们需要在导入的vue组件模块的require.context函数分析中提到,require.context函数还是一个函数,这个函数接收一个参数req,也就是组件的名称成功匹配常规模式的模块,files(key)。defaultthenfiles(key).default返回我们需要导入的vue组件模块,那么modules对象的key和value已经存在了!!!至此,我们通过require.context获取到了child-components文件夹下的所有vue文件,然后在父组件index.vue中引入modules,这样我们就完成了优化的第一步!优化第二步页面实现效果:项目结构首先介绍一下我们的需求和项目结构文件:由于我们制作的页面是一个模块配置,页面显示哪些组件是根据后台返回的数据来显示的。我们要在页面上显示这四个组件(按顺序):新闻组件、自定义组件、汽车组件、体育组件。每个组件中都有一个input输入框。当我们点击提交的时候,我们需要为每个组件收集input输入框绑定的值。第一步:我们需要在data中定义后端返回的数据(这里我们使用本地自定义数组来模拟):setFileShow:[//这个数组为后端返回数据,告诉前端需要展示哪些模块//action是必须的,action值需要和fileModules数组中对象中的action值进行映射//data属性对象中的content和title是组件的title和introduction(这两个属性值也是后端定义的)//这里数组返回的数据直接决定了最终页面展示模块的顺序{action:"News",data:{content:'我是一个新闻组件',title:"新闻"}},{action:"Car",data:{content:'我是一个汽车组件',title:"Car"}},{action:"Sports",data:{content:'我amasportscomponent',title:"Sports"}},]第二步:让我们从'./index.js'中获取我们获取的四个本地组件importmodules这里的modules数据结构是这样的{car:carcomponent,custom:自定义组件,news:news组件,sports:sports组件}下面我们来分析一下前两步。我们无法通过后端返回的数据来动态渲染我们的组件,也就是说我们无法建立setFileShow和modules之间的映射关系(因为后端返回的数据action值的首字母是大写的,而keyvalueofourmodulesisalllowercase)(如果后端返回的actionvalue和我们modules的key值相同,那么我们可以忽略第三步,直接进入第四步B的代码)第三步:我们需要在data中创建一个数组fileModules,设置LinkFileShowandmodulesfileModules:[//这里定义后端返回数据和所有子组件文件的映射关系,没有顺序要求//以Car:'car'为例,action中的Car(大写)是后端返回数据主键//filename中的car(小写)为子组件的文件名(即modules的键值){action:"Car",filename:'car'},{action:"News",filename:'news',},{action:"Sports",filename:'sports',}],第四步:生成我们要动态渲染的组件数组。代码A和代码B只会执行一个,请根据实际情况选择代码A:computed:{componentList(){letarr=[]//定义最后显示那些模块的数组letsetFileShow=this.setFileShow//后端返回数据letfileModules=this.fileModules//映射关系for(leti=0;i
