工程自动化导入:require.context
强烈建议使用
Vite
的小伙伴能够阅读到文章末尾
webpack
帮我们把项目打包到dist目录下,不会是无脑把所有文件都打包,会先静态解析,把我们需要用的文件打包,比如没有任何地方引用的图片,打包后,这个图片就不会出现在dist/static/img
目录内。
1.require
const { stat,exists} = require('./index.js')
require
是运行时加载,其实际上是加载整个模块并生成一个对象,然后再从对象中读取我们调用的属性,所以无法做静态优化。上面代码的结果就是相当于
const _fs = require('./index.js')
const stat = _fs.stat
const exists = _fs.exists
传参无法使用变量
require('./image/title.png')
很多时候我们都希望能够将上面这种引入方式,写成动态的
let imagePath = './image/title.png'
require(imagePath)
imagePath
只有在运行时才会明确知道需要引入的路径,但是这种写法是不允许的,或许从语法上来说不会报错,但是webpack
在构建打包时并不能解析出需要的路径
require
需要知道至少能确定是哪个目录
let imageFullName = 'title.png'
1. require(`./image/${imageFullName}`)
let imageFullName = 'title'
2. require(`./image/${imageFullName}.png`)
以上两种方式都是可以的,就算只有一个明确的目录,webpack
也还是可以解析的,但是这样会导致生成的上下文模块context
会包含该目录下所有可能会用到的模块。
同步&异步加载
异步加载
require(['./mypage.vue'],resolve)
在vue官网中的异步组件板块大部分人应该都看到过这段代码,vue官网在这段代码中有注释这个特殊的'require'语法将会告诉webpack自动将你的构建代码切割成多个包,这些包会通过ajax请求加载
更直观的可以解释为:
当require
第一个参数为数组时,则为异步加载
,然后第二个参数为回调函数,在回调函数中操作加载的模块
当require
第一个参数为字符串时,则为同步加载
,且将无法传递第二个参数。
同步加载
var mypage = require('./mypage.vue')
2.require.context
与require
直观上不同的是,require.context
是引入多个模块
require.context
可以理解为创建自己的上下文模块context
有三个参数:
1.directory
文件目录
2.useSubdirectories
是否查找子目录
3.regExp
要匹配文件的正则表达式
// 引入model目录下的所有以js后缀的文件
require.context('./model',true,/.js$/)
require.context
可以理解为内部将所有模块整合为一个map
,模块以路径为key
{
'./a.js':model,
'./b/f.js':model,
...
}
require.context
返回一个函数,接受一个key
参数,然后返回对应key
的模块
require.context
返回的函数有三个属性:
1.resolve
是一个函数,它返回 request 被解析后得到的模块 id。
2.keys
是一个函数,返回一个有所有模块的key
组成的数组
3.id
是 context module 里面所包含的模块 id
3.import
import
命令是静态引入,与require
函数的动态引入不同,在编译阶段就完成了加载,就性能而言要优于require
,但同时如果项目中大量的import
命令静态引入模块,会导致初始包变大,直接影响的便是加载时长,所以又提出了一个import()
函数,其两者的区别主要是在与一个是静态引入,一个是在运行时引入。
因为import()
是运行时执行,所以可以放在任何地方,import
命令的话,则必须放在模块顶层
import()
返回的是一个Promise
,即为异步加载
let imageUrl = ''
import('./image/title.png').then(res => {
imageUrl = res
})
import()
可以用来解决很多按需加载的性能优化问题,例如vue路由组件的按需加载、局部组件注册时的异步加载
4.利用require.context完成工程自动化导入
import a from "./a.js"
import b from "./b.js"
import c from "./c.js"
...
工作当中经常会遇到以上场景,像这种脱离业务且重复有规律的操作完全可以想办法弄成自动化
以vuex
分模块设计来举例
//index.js
import Vue from 'vue'
import Vuex from 'vuex'
import app from "./model/app.js"
import user from "./model/user.js"
...
export default new Vuex.Store({
modules:{
app,
user,
...
}
})
当项目应用很大时,甚至会分十几上百个模块,届时光引模块的代码都会显得十分冗余。可以利用require.context
一次性将模块全部引入
//index.js
import Vue from 'vue'
import Vuex from 'vuex'
import { resetModelName } from './util'
// 自动导入model
const requireModels = require.context('./model', true, /.js$/)
const storeModules = {}
requireModels.keys().forEach((item) => {
storeModules[resetModelName(item)] = requireModels(item).default
})
export default new Vuex.Store({
modules:{
...requireModels
}
})
// util.js
/**
* 文件路径名转格式:例:'./a/b.js' -> 'a_b'
* @param {*} str String
* @returns String
*/
export function resetModelName(str) {
str = str.replace(/.js$/g, '')
str = str.replace(/\.+\//g, '')
str = str.replace(/\//g, '_')
str = str.replace(/\//g, '')
return str
}
按约定好的规则,后面在model
目录内添加的模块,会被自动引入到vuex
实例中。或者也可以在此基础之上再进行封装,做成可配置化,提供例如 命名规则、是否查找子目录、自动话开关等等配置项。无论是简单的约定规则,还是复杂强大的配置化规则,都会对前端整个团队的规范有帮助。
Vite
require.context
是将匹配到资源静态打包引入,虽然这种方式的确避免了很多重复工作,但是却也在这方面牺牲了一些性能
而如果你用的是vue3.x
+Vite
,那么可以试试Glob 导入
,Vite
官方关于Glob 导入
的文档
Vite 支持使用特殊的 import.meta.glob
函数从文件系统导入多个模块:
const modules = import.meta.glob('./dir/*.js')
上面代码会被编译为
// vite 生成的代码
const modules = {
'./dir/foo.js': () => import('./dir/foo.js'),
'./dir/bar.js': () => import('./dir/bar.js')
}
这种方式是懒加载引入的,但是就不能支持复杂的文件目录节构了,需要将你所有的模块放在统一的一个文件目录下,除了这种懒加载的模式,还有一种import.meta.globEager
是直接引入所有模块,详细可以去阅读官网文档
需要注意的是这种方式是Vite
特有的!
转载自:https://juejin.cn/post/6982813323217600543