场景
在公司进行我的项目开发过程中,咱们可能遇到绝对简单的页面,此时咱们会把页面拆分成多个子组件
而后在定义一个index组件来引入所有的子组件,进而呈现上面的场景:
当初的页面是有5个子组件,那么须要引入5次,而后注册5次,最初还要在html中渲染5次,
有了这个实践,那么如果一个页面须要8个子组件,10个子组件呢,
那么咱们的index文件的代码将会变得冗余,而且变得不好保护,每减少一个子组件文件的话,
咱们都须要在index文件中去保护一次。
理解了多个子组件引入到index有这些痛点,接下来咱们就去优化下。
优化思路
- 一次引入全副子组件(只执行一次import子组件)
- 利用循环去循环注册引入的子组件,而不必每个都去注册一遍
- 在html中只去渲染一次就能够动静渲染所有的子组件呢 (动静组件渲染)
如果咱们把下面三步全副实现,是不是就能够解决下面说的几个痛点呢?
优化第一步 (一次引入全副子组件)
这里推出一个新的api,require.context,什么是require.context呢?
概念
一个webpack的api,通过执行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.
let files = require.context('./child-components', false, /\.vue$/)
let modules = {}
files.keys().forEach(key => {
const moduleName = key.replace(/(\.\/|\.vue)/g, '')
modules[moduleName] = files(key).default
})
export default modules
代码剖析:
require.context函数执行后返回的是一个函数,并且这个函数有3个属性id,keys,resolve,
这里咱们重点剖析下keys,keys是一个函数function webpackContextKeys,
keys()执行后返回的是正则匹配胜利的模块名字(正则匹配的文件名的相对路径)的数组
下面说到require.context函数执行后返回的是一个函数,它接管一个参数req,
这个参数就是正则匹配的文件名的相对路径,就是keys()函数返回的数组中的每一项,
此时require.context函数的后果赋值给变量files,而后去执行
files(参数:正则匹配的文件名的相对路径)函数执行后失去一个Module对象,
Module对象上有一个default属性,值就是咱们须要引入的组件
这个Module对象上的default属性
和咱们通过import Car from ‘./child-components/car.vue’引入的Car是一样的
剖析完了require.context函数和require.context返回的三个属性,咱们持续剖析前面的代码:
当初咱们曾经能够获取到某一个文件夹下所有的vue组件了,咱们心愿导出一个对象,
将每一个vue组件依据文件名依照映射关系存储起来而后导出,这里能够提前看下咱们导出的对象的构造:
当然上图中标记的对象key也能够自定义,咱们这里采纳的是vue文件的名字,因为这样能够直观的看出
它们之间的映射关系,car属性对应的就是car.vue组件。
咱们先定义一个空对象,作为咱们最终导出的对象
let modules = {}
因为keys()执行后返回的是正则匹配胜利的模块名字(正则匹配的文件名的相对路径)的数组,
所以咱们对数组进行遍历,首先获取到每一个vue文件的名字,因为咱们当初手里有的是vue文件的相对路径(./car.vue),所以咱们须要对./car.vue进行正则匹配解决获取到car,代码如下:
key.replace(/(\.\/|\.vue)/g, '')
此时咱们须要导出的对象曾经有了key值,当初就差value值了,即咱们须要引入的vue组件模块
下面剖析require.context函数的过程中曾经提到,require.context函数的依然是一个函数,
这个函数接管一个参数req,这个req就是正则匹配胜利的模块名字,
files(key).default
那么files(key).default返回的就是咱们须要引入的vue组件模块,
那么modules对象的key和value值都曾经有了 !!!
至此咱们曾经通过require.context就能够获取到child-components文件夹上面的所有vue文件了
而后将modules引入到父组件index.vue中
这样咱们就实现了第一步优化!
优化第二步
页面实现成果:
我的项目构造
先介绍下咱们的需要和我的项目构造文件:
因为咱们做的页面属于模块配置化的,所以说页面上显示哪些组件是依据后端返回的数据来显示的。
咱们想要在页面显示这四个组件(依照这个程序来):新闻组件、自定义组件、汽车组件、体育组件。
每个组件外面都有一个input输入框,当咱们点击提交时,要将每个组件绑定到input输入框的value收集起来。
第一步:咱们要在data中定义后端返回数据(这里咱们就本地自定义数组来模仿):
setFileShow:[
//该数组为后端返回数据,告知前端须要展现那些模块
//action 是必须的,action的值 须要与fileModules数组中对象外面的action值做映射
//data属性对象中的content和title为组件的题目和介绍(这两个属性值也是由后端来定义的)
//此数组返回来的数据间接决定了最终页面展现模块的程序
{
action:"News",
data:{
content:'我是新闻组件',
title:"新闻"
}
},
{
action:"Car",
data:{
content:'我是汽车组件',
title:"汽车"
}
},
{
action:"Sports",
data:{
content:'我是体育组件',
title:"体育"
}
},
]
第二步:咱们去拿到咱们曾经获取到的咱们本地的四个组件
import modules from './index.js'
这里的modules数据结构是这样的
{
car:car组件,
custom:custom组件,
news:news组件,
sports:sports组件
}
当初咱们来剖析下后面两步,咱们无奈通过后端返回的数据来动静去渲染咱们的组件,
也就是说咱们无奈将setFileShow和modules之间建设映射关系,
(因为后端返回的action值首字母为大写,而咱们的modules的key值都是小写的)
(如果后端返回的action值与咱们的modules的key值是一样的,
那咱们能够疏忽第三步,间接看第四步的代码B)
第三步:咱们要在data中建设一个数组fileModules,将setFileShow和modules分割起来
fileModules:[
//这里定义后端返回数据与所有子组件文件的映射关系 没有程序要求
// 以Car:'car', 为例,action中Car(大写)为后端返回数据的主键
//filename中car(小写)为子组件的文件名(即modules的key值)
{
action:"Car",
filename:'car'
},
{
action:"News",
filename:'news',
},
{
action:"Sports",
filename:'sports',
}
],
第四步:就是生成咱们要去动静渲染组件的数组了
代码A和代码B只会执行一种,请大家依据理论状况来抉择
代码A:
computed:{
componentList(){
let arr = [] //定义最终展现那些模块的数组
let setFileShow = this.setFileShow //后端返回数据
let fileModules = this.fileModules //映射关系
for(let i=0;i<setFileShow.length;i++){
for(let j=0;j<fileModules.length;j++) {
if(setFileShow[i].action == fileModules[j].action) {
arr.push({
file:modules[fileModules[j].filename],//modules中的value值(vue组件)
data:setFileShow[i].data, //组件中须要的题目和介绍
name:fileModules[j].filename
// name为 modules中的key值(vue组件名称)这里收集name是为了给每个组件绑定ref值
//当提交数据时,能够通过this.$refs去获取到所有的组件对象
})
} else {
}
}
}
//因为咱们的我的项目须要自定义配置,咱们的需要是在新闻组件和汽车组件之间插入,
//那么依据此时arr中新闻组件和汽车组件的索引值,
arr.splice(1,0,{file: modules['custom'], data: {/*这里是自定义数据*/},name:"custom"})
//这样的话,咱们最终渲染页面的组件就是 新闻、自定义、汽车、体育这样的程序显示了
return arr
},
代码B:
//(这种只针对如果后端返回的action值与咱们的modules的key值是一样的,
如果action值不一样,须要像代码A那样去整合)
computed:{
componentList(){
let arr = []
let setFileShow = this.setFileShow //后端返回数据
for(let i=0;i<this.setFileShow.length;i++){
for(let j in modules) {
if(setFileShow[i].action == j){
arr.push({
file:modules[j], //modules中的value值(vue组件)
data:setFileShow[i].data,//组件中须要的题目和介绍
name:j,
// name为 modules中的key值(vue组件)这里收集name是为了给每个组件绑定ref值
//当提交数据时,能够通过this.$refs去获取到所有的组件对象
})
}
}
}
return arr
}
}
第五步:咱们须要来解决一些非凡场景了(如果需要的话)
仔细的同学应该发现了,
咱们模仿的后端返回数据setFileShow中只有三个对象,
别离对应news(新闻组件)、car(汽车组件)、sports(体育)
三个组件,然而咱们的页面却显示了4个组件,这个就是咱们要讲的自定义配置了。
在理论开发过程中,后端只返回了3个须要进行配置化的数据,
然而咱们的页面须要4个、5个或者更多的组件,另外这些组件并不在配置化的范畴,
而且咱们在点击提交按钮,依然会收集这些组件的信息,
这时就须要咱们本人手动去把这些组件退出到动静组件的数据中。
//如果须要在所有组件最后面插入模块
//arr.unshift(
{
file: modules['custom'], //咱们手动去获取下custom组件
data: {/*这里是自定义数据*/},
name: 'custom'
})
//如果须要在所有组件最后面后插入模块
//arr.push(
{
file: modules['custom'], //咱们手动去获取下custom组件
data: {/*这里是自定义数据*/},
name: 'custom'
})
//如果须要插入到所有组件中的某一个地位
//arr.splice(1,0,
{
file: modules['custom'], //咱们手动去获取下custom组件
data: {/*这里是自定义数据*/},
name:"custom"
})
最初将上述代码插入到computed中的componentList办法中去,
记得在return arr之前循环之后就能够了
接下来就是要把咱们通过computed中的componentList办法返回的arr,
就是咱们最终须要动静渲染组件的数据了。
咱们来看下咱们最终整合进去的数组构造
上面咱们一段伪代码来形容下面图片中浏览器打印进去的信息
[
{
data:{
content:'我是新闻组件',
title:"新闻"
},
file:新闻组件
name:"news"
},
{
data:{
},
file:自定义组件
name:"custom"
},
{
data:{
content:'我是汽车组件',
title:"汽车"
},
file:汽车组件,
name:"car"
},
{
data:{
content:'我是体育组件',
title:"体育"
},
file:体育组件,
name:"sports"
}
]
当初咱们曾经拿到咱们须要的数据了,那么咱们开始去动静渲染组件吧
<component
class="mb"
v-for="(item,i) in componentList"
:ref="item.name"
:key="i"
:is="item.file"
:data="item.data"
></component>
ref是为每个组件绑定一个值,不便上面提交时收集各个组件的数据
key为循环key值
is是Vue动态创建组件办法的属性,须要组件作为参数
data是咱们须要分发给每个子组件的数据
上面以car组件为例,看下car组件外部是如何渲染的
car组件中的data.title和data.content都是通过动静组件散发到子组件的数据
data函数中carData是自定义数据,value值是input输入框的默认值,
最终点击提交按钮时,咱们能够将carData中的数据 整合成后端须要的json数据传递过来
最初一步:就是整合每个子组件外面的数据提交到后端了
let obj = {} //定义向后端传输数据
let componentFiles = this.$refs //所有子组件的汇合 此处是一个对象
//咱们对这个对象进行遍历
for(let item in componentFiles) {
//这里的item就是咱们咱们绑定的每一个ref的值
//这里的componentFiles[item]的值为1个数组,数组中只有一个组件VueComponent对象
//这里的fileData就是每一个组件的VueComponent对象上的_data属性值
//fileData的值其实就是每个子组件中data函数中return返回的对象
//以car组件为例就是
// {
// checked:false,
// carData:{ //提交后端数据
// value:"汽车"
// }
// }
let fileData = componentFiles[item][0]._data
//依据不同的ref绑定值,咱们去对应的子组件去获取对应的数据,而后都绑定到obj对象上
//最终将obj外面的数据传递给后端
switch (item) {
case 'car':
obj.car = fileData.carData
//此处obj前面的car为测试应用 理论以提交数据实在的key为准
break
case 'news':
obj.news = fileData.newsData
break
case 'sports':
obj.sports = fileData.sportsData
break
}
}
让咱们来看下咱们最终提交的数据格式:
这样的话咱们实现了第二步的优化!
本文的实战我的项目地址:
https://github.com/dabaoRain/…
作者对于require.context和Vue动静组件的了解属于根底入门级别,对于文章中的了解或者应用谬误,望各位大神不吝指出,对于require.context和Vue动静组件有那些须要补充的也能够进行评论,作者不胜感激。排版码字不易,感觉对您有所帮忙,就帮忙点个赞吧!
发表回复