模塊化已經(jīng)是現(xiàn)代 Web 開發(fā)必不可少的開發(fā)方式较屿,頻繁引入依賴包是一個常見的操作。但是卓练,手動引入依賴包往往繁瑣吝镣,尤其是當依賴包數(shù)量較多時,會顯著降低開發(fā)效率昆庇。
unplugin-auto-import?插件末贾,可以幫助我們在項目中,自動導入常用的使用的第三方庫的 API整吆,就可以方便我們開發(fā)拱撵,提升開發(fā)效率。
使用效果
以 Vue 為例表蝙,在沒有使用自動導入前拴测,需要手寫以下的?import?語句:
import{computed,ref}from'vue'constcount=ref(0)constdoubled=computed(()=>count.value*2)
使用?unplugin-auto-import?插件后:
constcount=ref(0)constdoubled=computed(()=>count.value*2)
使用方法
基本使用
unplugin-auto-import?是基于?unplugin?寫的,支持 Vite府蛇、Webpack集索、Rollup、esbuild 多個打包工具。
vite 的使用方式如下:
// vite.config.tsimportAutoImportfrom'unplugin-auto-import/vite'exportdefaultdefineConfig({plugins:[AutoImport({imports:[// 預設],}),],})
使用預設
unplugin-auto-import?插件一般配合預設進行使用务荆,預設負責告訴插件應該自動引入哪些內(nèi)容
目前支持:
Vue
vue-router
@vueuse/core
react
react-router
……妆距,更多請查看這里
預設的配置方式
AutoImport({imports[// 預設'vue','vue-router',// 自定義預設{'@vueuse/core':[// 命名導入'useMouse',// import { useMouse } from '@vueuse/core',// 設置別名['useFetch','useMyFetch'],// import { useFetch as useMyFetch } from '@vueuse/core',],'axios':[// 默認導入['default','axios'],// import { default as axios } from 'axios',],'[package-name]':['[import-names]',// alias['[from]','[alias]'],],},// example type import{from:'vue-router',imports:['RouteLocationRaw'],type:true,},],})
有多種方式設置預設:
字符串語法,最終會被轉(zhuǎn)換成內(nèi)置的預設(對象語法寫的)
對象語法
key 為包名
value 為數(shù)組函匕,對應的是各個自動引入的變量的名稱娱据。同時可以設置引入方式(命名導入/默認導入),
對于?Typescript 類型的自動引入盅惜,則需要用以下方式:
{from:'vue-router',imports:['RouteLocationRaw'],type:true},
我們來看看 Vue 的預設是怎么寫的中剩,完整代碼在這里,下面是節(jié)選的代碼:
exportconstCommonCompositionAPI:InlinePreset['imports']=[// 聲明周期抒寂,節(jié)選'onActivated','onBeforeMount',// reactivity,節(jié)選'computed','ref','watch',// 組件 API结啼,節(jié)選'defineComponent','h','inject','nextTick',// Typescript 類型,接續(xù)那...['Component','Ref','VNode'].map(name=>({name,type:true}))]exportdefaultdefineUnimportPreset({from:'vue',imports:[...CommonCompositionAPI,]})
Vue 預設里屈芜,本質(zhì)就是使用對象語法郊愧,定義了 Vue 需要被自動導入的內(nèi)容。
編碼問題
要想在項目中優(yōu)雅地使用自動導入沸伏,還要解決以下兩個編碼的問題:
TS 類型丟失,會導致 TS 編譯報錯
Eslint 報錯:變量未定義
TS 類型
如果使用 Typescript动分,需要設置?dts?為 true
AutoImport({dts:true// or a custom path})
插件會在項目根目錄生成類型文件?auto-imports.d.ts?毅糟,確保該文件在?tsconfig?中被?include
auto-imports.d.ts?有什么作用?
我們來看看它的內(nèi)容(有節(jié)選):
export{}declareglobal{consth:typeofimport('vue')['h']constreactive:typeofimport('vue')['reactive']constref:typeofimport('vue')['ref']constwatch:typeofimport('vue')['watch']constwatchEffect:typeofimport('vue')['watchEffect']// 省略其他內(nèi)容}
unplugin-auto-import?插件會根據(jù)預設內(nèi)容澜公,生成對應的全局類型聲明
有了這些全局類型聲明姆另,我們就能夠像全局變量那樣使用?ref?等 Vue API,不需要先?import?對應的內(nèi)容坟乾,TS 編譯也不會報錯迹辐。
Eslint
如果使用了 eslint,需要設置?eslintrc?字段
AutoImport({eslintrc:{enabled:true,},})
插件會在項目根目錄生成類型文件?.eslintrc-auto-import.json?甚侣,確保該文件在?eslint?配置中被?extends:
// .eslintrc.jsmodule.exports={extends:['./.eslintrc-auto-import.json',],}
.eslintrc-auto-import.json?有什么作用明吩?
我們來看看它的內(nèi)容(有節(jié)選):
{"globals":{"h":true,"reactive":true,"ref":true,"watch":true,"watchEffect":true,}}
unplugin-auto-import?插件會根據(jù)預設內(nèi)容,生成對應的 eslint 配置文件殷费,該文件定義了?h印荔、ref?這些為全局變量,不需要引入就能直接使用详羡。這樣 ESlint 就不會報變量沒有定義的錯誤了仍律。
實現(xiàn)原理
從?v0.8.0?來開始,unplugin-auto-import?基于?unimport?開發(fā)实柠,所有的轉(zhuǎn)換能力水泉,都是?unimport?提供的,unplugin-auto-import?可以理解成為一個提供了更友好的 API 和功能的包裝層〔菰颍基本上所有新功能都會在?unimport?中開發(fā)钢拧。
那核心的實現(xiàn),我們直接去看?unimport?就好了畔师。
eslint 配置的生成是由?unplugin-auto-import?提供
unimport
我們直接看看插件代碼
exportconstdefaultIncludes=[/\.[jt]sx?$/,/\.vue$/,/\.vue\?vue/,/\.svelte$/]exportconstdefaultExcludes=[/[\\/]node_modules[\\/]/,/[\\/]\.git[\\/]/]exportdefaultcreateUnplugin<Partial<UnimportPluginOptions>>((options={})=>{constctx=createUnimport(options)constfilter=createFilter(toArray(options.includeasstring[]||[]).length?options.include:defaultIncludes,options.exclude||defaultExcludes)constdts=options.dts===true?'unimport.d.ts':options.dtsreturn{name:'unimport',// 在用戶插件執(zhí)行完之后執(zhí)行enforce:'post',// 過濾文件娶靡,默認只處理 、js看锉、jsx姿锭、ts、tsx伯铣、vue呻此、svelte 文件// 默認排除 node_modules 下的文件transformInclude(id){returnfilter(id)},// 轉(zhuǎn)換文件邏輯asynctransform(code,id){consts=newMagicString(code)// 注入 import 語句awaitctx.injectImports(s,id)if(!s.hasChanged()){return}return{code:s.toString(),map:s.generateMap()}},// 構建開始時,生成 ts 類型聲明文件asyncbuildStart(){awaitctx.init()// 生成 Typescript 全局類型聲明if(options.dts){returnfs.writeFile(dts,awaitctx.generateTypeDeclarations(),'utf-8')}}}})
插件用基于?unplugin?寫的腔寡,用?unplugin?寫的插件焚鲜,能用在 Vite、Webpack放前、Rollup忿磅、esbuild 多個打包工具,即unplugin?抹平了打包工具間的一些差異凭语。
unimport?插件主要的處理邏輯如下:
過濾出需要處理的文件葱她,對文件進行轉(zhuǎn)換,注入 import 語句
生成 ts?類型聲明文件
unimport?為什么需要在其他插件后執(zhí)行似扔?
因為有些代碼需要先經(jīng)過處理吨些,才會變成 js,例如 Vue 文件炒辉。
測試用例
我們直接使用?unimport?提供的示例豪墅,其中一個文件為:
import{Ref}from'vue'exportconstmultiplier=ref(2)exportfunctionuseDoubled(v:Ref<number>){returncomputed(()=>v.value*multiplier.value)}exportfunctionbump(){multiplier.value+=1}constlocalA='localA'constlocalB='localB'export{localA,localBaslocalBAlias}
我們通過?vite-plugin-inspect?插件,可以看到該文件被轉(zhuǎn)換的過程:
被?esbuild?轉(zhuǎn)換
被?unimport?插件轉(zhuǎn)換
可以看出?unbuild?插件自動加入了 import 語句
被?import-analysis?插件處理
將 Vue 改為一個可以訪問的路徑黔寇,讓 vue 的相關文件在 dev 環(huán)境下能夠被正常訪問到偶器。
如何注入 import 語句
注入 import 語句,是?unimport?的核心邏輯缝裤,主要有以下幾個步驟:
初始化預設
掃描文件
注入 import
初始化預設
將字符串的內(nèi)置預設状囱,標準化為對象語法
將所有配置對象合并成一個?importMap?對象
importMap 數(shù)據(jù)結構如下:
{reactive:{from:'vue,? ? ? ? name: 'reactive'? ? ? ? as: 'reactive'? ? },? // 轉(zhuǎn)換成 import { reactive } from 'vue'? ? ref: {? ? ? ? from: 'vue,name:'ref? ? ? ? as: 'ref},// 轉(zhuǎn)換成 import { ref } from 'vue'// ……}
有了?importMap?對象,就可以快速判斷一個標志符倘是,是否需要轉(zhuǎn)換了
掃描和注入
查找所有可能需要注入的標志符
import{Ref}from'vue'exportconstmultiplier=ref(2)exportfunctionuseDoubled(v:Ref<number>){returncomputed(()=>v.value*multiplier.value)}exportfunctionbump(){multiplier.value+=1}constlocalA='localA'constlocalB='localB'export{localA,localBaslocalBAlias}
以上述文件為例亭枷,從中找出沒有先定義再使用的標志符,排除 js 關鍵字(function 等)
可以查找到有以下的標志符搀崭,未被定義卻使用了:
ref
computed
并且這兩個標志符都在?importMap?中能找到叨粘,這標明這兩個標志符猾编,需要注入 import
因此會注入以下代碼:
import{ref,computed}from'vue';
總結
并非所以依賴都適合自動導入,項目內(nèi)的代碼可能就不一定適合自動引入
因為自動引入后升敲,就能像全局變量那樣直接使用答倡,但從開發(fā)的角度就會丟失依賴鏈路,雖然另外生成了 Typescript 聲明文件驴党,IDE 能夠正常識別瘪撇, 但對于新加入項目的同學來說,他們不一定知道是自動引入港庄,因此可能會降低了一些可讀性倔既。
因此我們要有權衡。
那么鹏氧,什么樣的內(nèi)容適合自動引入渤涌?被廣泛認知和使用、不用關注實現(xiàn)把还、不變的內(nèi)容
這些內(nèi)容不關注實現(xiàn)实蓬,不會影響可讀性,不會影響開發(fā)吊履,不會對開發(fā)者心智造成影響安皱。
這類內(nèi)容,就適合自動引入艇炎。例如我們例子中的 Vue composition API酌伊,就已經(jīng)成為一種 Vue 開發(fā)者共識了。