同时注册多个|同时注册多个 vue 自定义指令 —— require.context

我们时常会用到自定义指令,如果是局部注册,简单,照这官网上来就可以。如果是会在不同页面上用到的相同指令,通常会注册为全局的。注册为全局指令,照着官网来,一样是可以完成。
但是,如果我们有多个需要全局注册的指令,一个一个来注册的话,写上一堆Vue.directive() 么,可以是可以,如果不觉得麻烦的话,只是,我们凡事都讲究,优雅,以此来规范(zb)我们的代码。
在这之前,假使你已经了解指令的具体语法,不理解的同学自行查看官网 自定义指令
如何同时注册很多很多个全局指令呢?用到的是 require.context方法,webpack 上的一个 api。
作用:
官网的原话是:
It allows you to pass in a directory to search, a flag indicating whether subdirectories should be searched too, and a regular expression to match files against.
简单的理解是:匹配出某个目录(及其子目录)下你所需要的的某种类型的文件
语法:

require.context(directory, useSubdirectories, regExp, mode)

接收三个参数:
  • directory:需要检索的目录
  • useSubdirectories:是否检索子目录
  • regExp: 需要作用于什么文件(匹配文件的正则表达式)
  • mode: 加载模式,默认为同步sync,异步值为 lazy
返回:
context.require 返回一个require 函数:
function webpackContext(req) { return __webpack_require__(webpackContextResolve(req)); }

【同时注册多个|同时注册多个 vue 自定义指令 —— require.context】该函数有三个属性:resolve 、keys、id
  • resolve: {Function} ,返回这个匹配文件相对于整个工程的相对路径
  • keys: {Function} ,返回匹配成功模块的名字组成的数组
  • id: {String} ,返回的是一个字符串,执行环境的id
使用:
因为是 webpack 上自带的api,在cli 构建的项目中,我们可以直接使用,不用再另外引入其他的包。
用例:
注册全局指令 我的项目结构如下

同时注册多个|同时注册多个 vue 自定义指令 —— require.context
文章图片
image.png 1. 指令的定义
拷贝了官网上的几个案例,为了方便管理,一个文件里放一个指令。要把他们注册成三个指令:v-color-swatch、v-focus、v-pin。
focus.js:
export default { inserted: function (el) { el.focus() } }

pin.js:
export default { bind: function (el, binding, vnode) { el.style.position = 'fixed' var s = binding.arg === 'left' ? 'left' : 'top' el.style[s] = binding.value + 'px' }, update: function (el, binding, vnode, oldVnode) {} }

color-swatch.js:
export default function (el, binding) { el.style.backgroundColor = binding.value.color el.innerHTML = binding.value.text }

这里说下 color-swatch.js ,这里直接导出一个函数,没有写函数钩子,其实这是生命 bindupdate钩子函数的简写,如官网原话:
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如这样写:
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value
})
2. 指令的注册
注册全局指令就是在项目初始化的时候我们就开始注册。项目初始化 -> main.js
所以这些指令的注册我们应该是在main.js写的,但是为了不让 main.js 的代码看起来太多太乱,我们在 directives 文件夹下 添加一个 index.js 文件。这个文件处理的就是这几个指令的注册。
2.1 第一种方法:
index.js:
import Vue from 'vue' import colorSwatch from './modules/color-swatch.js' import focus from './modules/focus.js' import pin from './modules/pin.js'Vue.directive('color-swatch', colorSwatch) Vue.directive('focus', focus) Vue.directive('pin', pin)

main.js:
import './directives/index'

当然你也可以不用 index.js 文件,直接把上面 index.js 的内容放到 main.js 也是一样的。
2.2 第二种方法:
index.js:
import colorSwatch from './modules/color-swatch.js' import focus from './modules/focus.js' import pin from './modules/pin.js'export { colorSwatch, focus, pin }

main.js :
// 导入 import * as directives from './directives/index' // 注册 Object.keys(directives).forEach(k => Vue.directive(k, directives[k]))

跟第一种差不多,index.js 作为中间文件模块化指令,导入默认指令再分模块导出。这样在 main.js 就也可以用 import * 导出所有的模块。
2.3 第三种方法:
上面两种方法是我们注册全局指令一般的写法,简单易懂。
但是如果我们有很多个指令,每个都要这样引进来吗?
知道了 require.context 语法后,怎么把它用在注册全局指令这件事上呢?
匹配出 'directives/modules‘ 下的文件,然后在 index.js 做这些文件需要做的操作,如下:
import Vue from 'vue'const files = require.context( // 指令目录 './modules', // 不查找子目录 false, // js文件 /.+\.js$/ )// 对配匹出来的的文件进行操作 files .keys().forEach(fileName => {// 获取指令函数 const directiveConfig = files(fileName)// 获取指令名称 const directiveName = fileName // 移除开始的 './' .replace(/^\.\//, '') // 移除文件扩展 .replace(/\.\w+$/, '')// 注册指令, 文件名作为指令名 Vue.directive(directiveName, directiveConfig.default || directiveConfig) })

这里打印出 files 的三属性返回的是什么
console.log('files: ', files) console.log('-----') console.log('files.resolve: ', files.resolve(files.keys()[0])) console.log('files.keys: ', files.keys()) console.log('files.id: ', files.id)

同时注册多个|同时注册多个 vue 自定义指令 —— require.context
文章图片
image.png 解析1:
fileName.replace(/^.//, '').replace(/.\w+$/, '') 获取到指令名称。
console.log(files.keys()) 得到的是 ["./color-swatch.js", "./focus.js", "./pin.js"]。因为这里是要用文件名来设置指令名,所以用正则把"./color-swatch.js" 替换成 “color-swatch”
解析2:
files(fileName) 获取到指令函数;
webpackContext 作为一个函数,也接受一个req参数,这个和resolve方法的req参数是一样的,即匹配的文件名的相对路径,而files函数返回的是一个模块,这个模块才是真正我们需要的。
3. 指令的使用
上面注册时都是用文件名来做指令名,所以用的时候的格式为 v-文件名 ,如下:
I am pinned onto the page at 200px to the left.



总结
其实 require.context 的作用就是帮我匹配出某个路径下我们指定类型的文件。
因为它可以方便匹配出指定文件,那是不是可以把需要做同一种操作的文件放在同一个文件夹下,然后用require.context 提取出这些文件去做需要做的操作。
通过上面的案例,除了在注册全局指令上能用到这个方法,还有其他地方可以用吗?例如 全局注册多个自定义指令
,例如 router、store 这种需要一个一个来导出的文件,就可以用 require.context 匹配出来啦。
最后要说,这些优化的方法都是看场景使用,并没有绝对的时候。就上面的例子,如果要全局注册的指令只有2个,倒不如用第一种方法来的简单。所以要看具体情况来选择。
参考:
自定义指令
前端工程化之动态导入文件
requre.content(GUIDES- Dependency Management)
requre.content(API - Module Methods )

    推荐阅读