我們時常會用到自定義指令递胧,如果是局部注冊碑韵,簡單,照這官網(wǎng)上來就可以缎脾。如果是會在不同頁面上用到的相同指令祝闻,通常會注冊為全局的。注冊為全局指令赊锚,照著官網(wǎng)來治筒,一樣是可以完成。
但是舷蒲,如果我們有多個需要全局注冊的指令,一個一個來注冊的話友多,寫上一堆Vue.directive() 么牲平,可以是可以,如果不覺得麻煩的話域滥,只是纵柿,我們凡事都講究,優(yōu)雅启绰,以此來規(guī)范(zb)我們的代碼昂儒。
在這之前,假使你已經(jīng)了解指令的具體語法委可,不理解的同學自行查看官網(wǎng) 自定義指令
如何同時注冊很多很多個全局指令呢渊跋?用到的是 require.context方法,webpack 上的一個 api着倾。
作用:
官網(wǎng)的原話是:
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)
接收三個參數(shù):
- directory:需要檢索的目錄
- useSubdirectories:是否檢索子目錄
- regExp: 需要作用于什么文件(匹配文件的正則表達式)
- mode: 加載模式拾酝,默認為同步
sync
,異步值為lazy
返回:
context.require 返回一個require 函數(shù):
function webpackContext(req) {
return __webpack_require__(webpackContextResolve(req));
}
該函數(shù)有三個屬性:resolve 卡者、keys蒿囤、id
- resolve: {Function} ,返回這個匹配文件相對于整個工程的相對路徑
- keys: {Function} 崇决,返回匹配成功模塊的名字組成的數(shù)組
- id: {String} 材诽,返回的是一個字符串,執(zhí)行環(huán)境的id
使用:
因為是 webpack 上自帶的api恒傻,在cli 構建的項目中脸侥,我們可以直接使用,不用再另外引入其他的包碌冶。
用例:
注冊全局指令
我的項目結(jié)構如下
1. 指令的定義
拷貝了官網(wǎng)上的幾個案例湿痢,為了方便管理,一個文件里放一個指令。要把他們注冊成三個指令: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 臀规,這里直接導出一個函數(shù)滩援,沒有寫函數(shù)鉤子,其實這是生命 bind
和 update
鉤子函數(shù)的簡寫塔嬉,如官網(wǎng)原話:
在很多時候玩徊,你可能想在 bind 和 update 時觸發(fā)相同行為,而不關心其它的鉤子谨究。比如這樣寫:
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 的內(nèi)容放到 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 => {
// 獲取指令函數(shù)
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)
解析1:
fileName.replace(/^.//, '').replace(/.\w+$/, '') 獲取到指令名稱。
console.log(files.keys()) 得到的是 ["./color-swatch.js", "./focus.js", "./pin.js"]铅歼。因為這里是要用文件名來設置指令名公壤,所以用正則把"./color-swatch.js" 替換成 “color-swatch”
解析2:
files(fileName) 獲取到指令函數(shù);
webpackContext 作為一個函數(shù),也接受一個req參數(shù),這個和resolve方法的req參數(shù)是一樣的,即匹配的文件名的相對路徑,而files函數(shù)返回的是一個模塊,這個模塊才是真正我們需要的椎椰。
3. 指令的使用
上面注冊時都是用文件名來做指令名厦幅,所以用的時候的格式為 v-文件名
,如下:
<input v-focus>
<p v-pin:[direction]="200">I am pinned onto the page at 200px to the left.</p>
<div v-color-swatch="{ color: '#fcc', text: 'hello!' }"></div>
總結(jié)
其實 require.context
的作用就是幫我匹配出某個路徑下我們指定類型的文件慨飘。
因為它可以方便匹配出指定文件确憨,那是不是可以把需要做同一種操作的文件放在同一個文件夾下译荞,然后用require.context
提取出這些文件去做需要做的操作。
通過上面的案例休弃,除了在注冊全局指令上能用到這個方法吞歼,還有其他地方可以用嗎?例如 全局注冊多個自定義指令
塔猾,例如 router篙骡、store 這種需要一個一個來導出的文件,就可以用 require.context 匹配出來啦丈甸。
最后要說糯俗,這些優(yōu)化的方法都是看場景使用,并沒有絕對的時候睦擂。就上面的例子得湘,如果要全局注冊的指令只有2個,倒不如用第一種方法來的簡單顿仇。所以要看具體情況來選擇忽刽。
參考:
自定義指令
前端工程化之動態(tài)導入文件
requre.content(GUIDES- Dependency Management)
requre.content(API - Module Methods )