【vite】你不知道的小妙招,确定不看一下吗?

2025-05-29 0 36

【vite】你不知道的小妙招,确定不看一下吗?

分析 version:2.3.7。本文将整理 vite 静态资源的几种处理方式,应用案例和源码分析相结合,带你 10mins 通关该模块知识~

一.处理的五种方式

(1) 使用根绝对路径引入 public 中的资源

  1. <imgalt="Vuelogo"src="/wy-logo.png"/>

【vite】你不知道的小妙招,确定不看一下吗?

敲重点!

  • publicDir 放静态资源的目录,默认为 public
  • 引入 public 中的资源永远应该使用根绝对路径 —— 举个,public/wy-logo.png 应该在源码中被引用为 /wy-logo.png。在开发时能直接通过${yourHost}/wy-logo.png根路径访问到。
  • public 中的资源不应该被 JavaScript 文件引用。

接下来我们来康康代码处理:当我们直接/wy-logo.png 访问资源:

【vite】你不知道的小妙招,确定不看一下吗?

以下是 public 静态资源中间件处理入口 – vite/src/node/server/index.ts:

  1. if(config.publicDir){
  2. middlewares.use(servePublicMiddleware(config.publicDir))
  3. }

这时候大家就有疑问了,怎样才会走到 isImportRequest,以及为什么这么干?别急,下面我们慢慢唠~

(2)通用 import 静态资源, 返回[解析后]的公共路径

首先,啥子是通用静态资源嘞~~

是 vite 支持的默认资源类型:

KNOWN_ASSET_TYPES = ["png", "jpe?g", "gif", "svg", "ico", "webp", "avif", "mp4", "webm", "ogg", "mp3", "wav", "flac", "aac", "woff2?", "eot", "ttf", "otf", "wasm"]

是你自定义的放到 assetsInclude 配置中的文件

其次,我们来康康,静态资源的导入

  1. <template>
  2. <imgalt="Vuelogo":src="starImg"/>//解析后的公共路径作为src来请求资源
  3. </template>
  4. <script>
  5. importstarImgfrom'../assets/star.png'//导入静态资源-图片
  6. exportdefaultdefineComponent({
  7. data(){
  8. return{
  9. starImg
  10. }
  11. }
  12. })
  13. </script>

这是我们的输入和输出:我们可以看到每个 import 都会被处理成 xxx?import 请求,返回解析后的代码,得到一个公共可访问 url

【vite】你不知道的小妙招,确定不看一下吗?

然后,我们依旧根据 wy-logo 图片来对比分析~ 之前是直接静态文件访问,提前返回资源,不会被解析成 import 依赖 反之如果作为 js 文件 import 引入,则不会当成正常静态资源,都得优先处理成通用&import js 文件,:

  1. importlogofrom'../../public/wy-logo.png'
  2. console.log(logo)

关键代码:

  1. //从初始执行cli处启动createServer-vite/src/node/server/index.ts
  2. //会调用resolveConfig()获取config,而该方法里会调用resolvePlugins(),
  3. //其中有个plugin处理是:importAnalysisPlugin(config)
  4. //所在文件如下:
  5. import{importAnalysisPlugin}from'./importAnalysis'
  6. //在importAnalysic.js里有个关键方法:
  7. asynctransform(source,importer,ssr){
  8. //用`?import`标识非js/css的import依赖
  9. url=markExplicitImport(url)
  10. }

【vite】你不知道的小妙招,确定不看一下吗?

可以看到 public 下的静态资源直接请求会直接返回,反之 import 静态资源的话 – 处理成/public/wy-logo.png?importwy-logo.png?import。需要后续通过返回解析后的 url 再去访问资源。es-module-lexer 解析处理:

【vite】你不知道的小妙招,确定不看一下吗?

这时候大家就理解了之前的疑问,isImportRequest 需要区分是否是直接的静态资源请求,如果是 import xxx 来引入的,都统一处理成依赖,给到你最终需要的一个 URL => 【公共静态资源访问路径】。这就是为什么 public 中的资源不建议被 JavaScript 文件引用,因为 publicDir 资源文件的定义就是直接可以请求,没有必要解析获取 url 后再请求!!!

(3)非通用静态资源?可显式 URL

引入 ?url通用静态资源可以直接处理获取 url,那要是想要处理其他资源,怎么显式导入为一个 URL 来用 (⊙_⊙)? 答案是用?url后缀 ../data/name.js:

  1. exportconstnameList=['Tim','John','Bob','Catherine']
  2. console.log(`名称列表=`,nameList.join(''))

components/name.vue

  1. importnameListUrlfrom'../data/name.js?url'
  2. console.log(nameListUrl)//解析成'/src/data/name.js'

【vite】你不知道的小妙招,确定不看一下吗?

源码解析: 同理,也是在resolvePlugins()里面有对 asset 处理的assetPlugin

  1. consturlRE=/(\\?|&)url(?:&|$)/
  2. asyncload(id){
  3. //如果没有被配置到静态资源assetsInclude并且没有?url后缀的,直接返回
  4. if(!config.assetsInclude(cleanUrl(id))&&!urlRE.test(id)){
  5. return
  6. }
  7. id=id.replace(urlRE,'$1').replace(/[\\?&]$/,'')
  8. consturl=awaitfileToUrl(id,config,this)//获取公共可访问路径
  9. return`exportdefault${JSON.stringify(url)}`//返回解析后的代码
  10. }

(4)将资源引入为字符串 ?raw

同一个例子,我们要获取 name.js 的数据:1.场景 1 是获取执行 name.js 后输出的数据 2.场景 2 是仅仅要获取 name.js 的文本,比如我们可以做 template 的字符串,那就需要使用到?raw后缀了。

  1. importnameStringfrom'../data/name.js?raw'
  2. console.log(nameString)//解析出exportdefault"xxxxx"文本

【vite】你不知道的小妙招,确定不看一下吗?

上源码。。。。。依旧是assetPlugin

  1. constrawRE=/(\\?|&)raw(?:&|$)/
  2. asyncload(id){
  3. //rawrequests,readfromdisk
  4. if(rawRE.test(id)){
  5. //publicDir存在同名静态文件,优先返回
  6. constfile=checkPublicFile(id,config)||cleanUrl(id)
  7. //?raw作为query,读取对应的文件并且返回其字符串
  8. return`exportdefault${JSON.stringify(
  9. awaitfsp.readFile(file,'utf-8')
  10. )}`
  11. }
  12. }

(5)导入脚本作为 Worker ?worker

脚本可以通过 ?worker 或 ?sharedworker 后缀导入为 web worker。上案例:/data/name-worker.js

  1. exportconstnameList=['Tim','John','Bob','Catherine']
  2. addEventListener('message',(e)=>{
  3. console.log('主线程:',e.data)
  4. postMessage({
  5. word:`Hi,我是worker~~老大,这是你要的名单:${nameList.join('')}`,
  6. nameList
  7. })
  8. close()//关闭worker
  9. },false)

/components/name.vue

  1. importNameWorkerfrom'../data/name-worker.js?worker'
  2. exportdefaultdefineComponent({
  3. mounted(){
  4. constworker=newNameWorker()
  5. worker.postMessage('Hi,我是主线程~')//主线程向Worker发消息
  6. worker.onmessage=(e)=>{//接收子线程发回来的消息
  7. if(e.data){
  8. console.log('Worder:'+e.data.word)
  9. this.workerNameList=e.data.nameList
  10. worker.terminate()//Worker完成任务以后,主线程就可以把它关掉
  11. }
  12. }
  13. worker.onerror=(e)=>{
  14. console.log([
  15. 'ERROR:Line',e.lineno,'in',e.filename,':',e.message
  16. ].join(''))
  17. }
  18. }
  19. })

这里,就是给 name-worker.js 封装了一层,提供了 WorkerWrapper 函数帮你新建了一个 worker 对象

【vite】你不知道的小妙招,确定不看一下吗?

debug 图如下:

【vite】你不知道的小妙招,确定不看一下吗?

源码。。。。【啊啊啊!!!源码可真多,我可太暴躁了】

resolvePlugins()里 执行webWorkerPlugindev 开发环境下:

  1. asynctransform(_,id){
  2. constquery=parseWorkerRequest(id)
  3. leturl:string
  4. url=awaitfileToUrl(cleanUrl(id),config,this)//原始url
  5. url=injectQuery(url,WorkerFileId)//加上&worker_file的query标识
  6. constworkerConstructor=
  7. query.sharedworker!=null?'SharedWorker':'Worker'
  8. constworkerOptions={type:'module'}
  9. return`exportdefaultfunctionWorkerWrapper(){//输出新建worker对象的template
  10. returnnew${workerConstructor}(${JSON.stringify(
  11. url
  12. )},${JSON.stringify(workerOptions,null,2)})
  13. }`
  14. }

build 生产下:inline 模式和非 inline 模式

  1. if(query.inline!=null){
  2. //打包文件作为入口去支持import导入worker或者行内写入
  3. constrollup=require('rollup')astypeofRollup
  4. constbundle=awaitrollup.rollup({
  5. input:cleanUrl(id),
  6. plugins:config.pluginsasPlugin[]
  7. })
  8. try{
  9. //在生产构建中将会分离出chunk,worker脚本将作为单独的块发出
  10. const{output}=awaitbundle.generate({
  11. format:'es',
  12. sourcemap:config.build.sourcemap
  13. })
  14. return`constblob=newBlob([atob(\\"${Buffer.from(output[0].code).toString('base64')}\\")],{type:'text/javascript;charset=utf-8'});
  15. exportdefaultfunctionWorkerWrapper(){
  16. constobjURL=(window.URL||window.webkitURL).createObjectURL(blob);
  17. try{
  18. returnnewWorker(objURL);
  19. }finally{
  20. (window.URL||window.webkitURL).revokeObjectURL(objURL);
  21. }
  22. }`
  23. }finally{
  24. awaitbundle.close()
  25. }
  26. }else{
  27. //作为分开的chunk处理`?worker&inline`,内联为base64字符串-要求inline的worker
  28. url=`__VITE_ASSET__${this.emitFile({
  29. type:'chunk',
  30. id:cleanUrl(id)
  31. })}__`
  32. ….//同开发返回的template
  33. }

咦~,那这个/src/data/name-worker.js?worker_file 又通过?worker_file后缀给我们处理啥了?webWorkerPlugin:

  1. constWorkerFileId='worker_file'
  2. asynctransform(_,id){
  3. constquery=parseWorkerRequest(id)
  4. if(query&&query[WorkerFileId]!=null){
  5. return{
  6. //其实只是作为执行导入之前生成的worker.js文件的标识…….
  7. code:`import'${ENV_PUBLIC_PATH}'\\n`+_
  8. }
  9. }
  10. }

害!五个静态处理方式总算是讲完了~

二. 总结

vite 的静态处理关键点就是 :

(1)通过特殊 query(?:worker|sharedworker|raw|url)来区分不同类型静态资源,进行特殊的 transform 处理。

(2)publicDir 限定直接访问的静态资源 本文通过列举处理点,逐一提供案例+debug 截图+源码分析的方式,让大家理解静态处理的使用和底层原理。

原文链接:https://mp.weixin.qq.com/s/J_HBCBATgVEexbhzKvb2wQ

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 【vite】你不知道的小妙招,确定不看一下吗? https://www.kuaiidc.com/92253.html

相关文章

发表评论
暂无评论