VUE - require.context让组件和路由自动加载

使用:

//路由自动加载
// route/index.js
const autoLoadRoutes = []
const reqRouter = require.context('./', true, /\.js$/)
reqRouter.keys().forEach(name => {
  if (name !== './index.js') {
    autoLoadRoutes.push(...reqRouter(name).default)
  }
})
const routes = [
  {
    path: '/',
    component: () => import('@/views/home/home'),
  },
  ...autoLoadRoutes,
  {
    path: '*',  //匹配不上的路由统一走404
    component: () => import('@/views/NotFound')
  }
]
export default routes

// main.js
/* router */
import VueRouter from "vue-router"
import routes from "./router";
Vue.use(VueRouter);
const router = new VueRouter({
  mode: "history",
  base: __dirname,
  routes: routes
});

=========================================

//vuex的module自动加载
// store/index.js
const autoLoadStores = {}
const reqStore = require.context('./', true, /\.js$/)
reqStore.keys().forEach(name => {
  if (name !== './index.js') {
    const key = name.replace('./', '').replace('/', '').replace('.js', '')
    autoLoadStores[key] = {
      ...reqStore(name).default,
      namespaced: true,
    };
  }
})
export default autoLoadStores

// main.js
/* vuex */
import Vuex from 'vuex'
import stores from "./store";
Vue.use(Vuex)
const store = new Vuex.Store({
  modules: stores
})

组件篇:

1.简介

require.context是Webpack中用来管理依赖的一个函数,此方法会生成一个上下文模块,包含目录下所有模块的引用,通过正则表达式匹配,然后require进来

2.使用方法

 require.context('.', false, /\.vue$/)

此方法有三个参数,

  • 参数一:要查询的目录,上述代码指的是当前目录
  • 参数二: 是否要查询子孙目录,方法默认的值为false
  • 参数三:要匹配的文件的后缀,是一个正则表达式,上述我要查询的是.vue文件

require.context模块返回一个函数,这个函数可以接收一个参数

导出的方法有 3 个属性: resolve, keys, id。

  • resolve 是一个函数,它返回请求被解析后得到的模块 id。
  • keys 也是一个函数,它返回一个数组,由所有可能被上下文模块处理的请求组成。
  • id 是上下文模块里面所包含的模块 id. 它可能在你使用 module.hot.accept 的时候被用到

3.应用场景

import Vue from 'vue'
let contexts = require.context('.', false, /\.vue$/)
contexts.keys().forEach(component => {
    // debugger;
    let componentEntity = contexts(component).default
    // 使用内置的组件名称 进行全局组件注册
    Vue.component(componentEntity.name, componentEntity)
})

上述代码中写的是一个全局注册的组件,用到了keys属性,返回一个数组,通过遍历,来完成组件注册,
contexts方法,内部就是返回引用webpack_require来加载模块,
使用componentEntity.name,来作为组件名,

image

即上述图片组件中的name,

文件目录

image

最后在main.js中引入index.js,该目录下的组件就全部被全局注册,可以在任意vue中使用这些组件,

注意

  • 很多组件可能只是单个页面需要用到,我们在加载页面的过程中,希望组件也是按需加载的。组件全部注册,那么当页面需要引用其中某些组件时,是不是将所有的组件都打包引入了呢?全局注册的意思是不是已经全部被引入?

  • 对于所有组件都在项目中被用到的情况来说,全局注册和按需引入的方式在打包和运行效率上并没有什么区别,只是相对来说按需引入可读性更强一点。

  • 可根据自己的需求,合理使用

改良版:(文件名命名组件名,和组件内default.name解耦)

// autoLoadCpt.js
import Vue from 'vue'
const reqCpt = require.context('./', false, /\.vue$/)
const install = () => {
    reqCpt.keys().forEach(name => {
        const cpt = reqCpt(name)
        const cptName = name.replace(/^\.\//, '').replace(/\.vue$/, '')
        Vue.component(cptName, cpt.default)
    })
}
export default install

// main.js
import autoLoadCpt from "./components/autoLoadCpt";
Vue.use(autoLoadCpt)

// app.vue
<template>
  <div id="app">
    <page1/>
    <page2/>
  </div>
</template>

路由篇:

// home.route.js
export default [
    {
        path: '/h1',
        component: () => import('../components/Home1')
    },
    {
        path: '/h2',
        component: () => import('../components/Home2'),
        children: [
            {
                path: 'h3',
                component: () => import('../components/Home3')
            }
        ]
    }
]

// login.route.js
export default [
    {
        path: '/l1',
        component: () => import('../components/Login1')
    },
    {
        path: '/l2',
        component: () => import('../components/Login2')
    }
]

普通引入:

// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)
import home from './home.route'
import login from './login.route'

const routes = [
    {
        path: '/',
        component: () => import('../components/Welcome')
    },
    ...home,
    ...login
]
const router = new VueRouter({
    routes,
    mode: 'history'
})

export default router

自动加载:

// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const autoLoadRoutes = []
const reqRouter = require.context('./', true, /\.route\.js$/)
reqRouter.keys().forEach(name => {
    autoLoadRoutes.push(...reqRouter(name).default)
})

const routes = [
    {
        path: '/',
        component: () => import('../components/Welcome')
    },
    ...autoLoadRoutes
]

const router = new VueRouter({
    routes,
    mode: 'history'
})

export default router

原理:

require.context是什么

一个webpack的api,通过执行require.context函数获取一个特定的上下文,主要用来实现自动化导入模块,在前端工程中,如果遇到从一个文件夹引入很多模块的情况,可以使用这个api,它会遍历文件夹中的指定文件,然后自动导入,使得不需要每次显式的调用import导入模块

什么时候需要用到require.context

如果有以下情况,可以考虑使用require.context替换

image
image

在Vue写的项目中,我把路由通过不同的功能划分成不同的模块,在index.js中一个个导入(原谅ide的警告-.-),但是如果项目变大了之后,每次手动import会显得有些力不从心,这里可以使用require.context函数遍历modules文件夹的所有文件一次性导入到index.js中

分析require.context

require.context函数接受三个参数

  1. directory {String} -读取文件的路径

  2. useSubdirectories {Boolean} -是否遍历文件的子目录

  3. regExp {RegExp} -匹配文件的正则

语法: require.context(directory, useSubdirectories = false, regExp = /^.//);

借用webpakc官网的例子

require.context('./test', false, /.test.js$/);

上面的代码遍历当前目录下的test文件夹的所有.test.js结尾的文件,不遍历子目录

大概用图片来表示的话就是这样子的

image

在index.js中调用 require.context('./test', false, /.test.js$/);会得到test文件下3个文件的执行环境

值得注意的是require.context函数执行后返回的是一个函数,并且这个函数有3个属性

  1. resolve {Function} -接受一个参数request,request为test文件夹下面匹配文件的相对路径,返回这个匹配文件相对于整个工程的相对路径

  2. keys {Function} -返回匹配成功模块的名字组成的数组

  3. id {String} -执行环境的id,返回的是一个字符串,主要用在module.hot.accept,应该是热加载?

这三个都是作为函数的属性(注意是作为函数的属性,函数也是对象,有对应的属性)

talk is cheap ,show me the code

结合工程看一下这3个属性返回了什么

我们在里层的modules文件夹新建一个index.js,用来收集所有的模块然后一次性导出给外层的index.js

image

这里我们先上代码,代码是写在里层的index.js中的(代码借鉴于加快Vue项目的开发速度)

image

这里我把require.context函数执行后的代码赋值给了files变量,files中保存了图一的以.js结尾的文件,files是个函数,我们分别调用者3个属性看看会返回什么

image
image

可以看到

执行了keys方法返回了一个由匹配文件的文件名组成的数组
id属性返回了匹配的文件夹的相对于工程的相对路径,是否遍历子目录,匹配正则组成的字符串

对于resolve方法可以看到它是一个函数接受req参数,经过实践我发现这个req参数的值是keys方法返回的数组的元素,接着我们传入其中一个元素执行resolve函数

image
image

resolve方法返回了一个字符串代表着传入参数的文件相对于整个工程的相对路径

同时files作为一个函数,也接受一个req参数,这个和resolve方法的req参数是一样的,即匹配的文件名的相对路径,而files函数返回的是一个模块,这个模块才是真正我们需要的

image
image

这个Module模块和使用import导入的模块是一样的

回到工程

  • 首先调用require.context导入某个文件夹的所有匹配文件,返回执行上下文的环境赋值给files变量

  • 声明一个configRouters用来暴露给外层index.js作为vue-router的数组

  • 调用files函数的keys方法返回modules文件夹下所有以.js结尾的文件的文件名,返回文件名组成的数组

  • 遍历数组每一项,如果是index.js就跳过(index.js并不是路由模块),调用files函数传入遍历的元素返回一个Modules模块

  • 因为我的路径是用export default导出的,所以在Module模块的default属性中获取到我导出的内容(即路由的结构),类似这种样子

image
  • 将上一步返回的所有路由结构添加到configRouters数组然后暴露给外层的index.js
image
  • 外层引入后导入到vue-router中就可以使用了

写在后面

在使用require.context自动导入路由文件时发现一个问题,路由的顺序不是你期望的样子,因为webpack是根据你文件夹中文件的位置排序的,这个时候需要定义一个标识符来给路由数组排序,这里我们给每个文件夹最上层的路由添加一个sort属性用于排序

image

随后在读取模块后,给外层index传入路由配置前,给路由的模块排序

image
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容