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

公司主营业务:成都网站设计、网站建设、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。成都创新互联公司是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。成都创新互联公司推出垫江免费做网站回馈大家。
(1) 使用根绝对路径引入 public 中的资源
敲重点!
接下来我们来康康代码处理:当我们直接/wy-logo.png 访问资源:
以下是 public 静态资源中间件处理入口 - vite/src/node/server/index.ts:
- if (config.publicDir) {
 - middlewares.use(servePublicMiddleware(config.publicDir))
 - }
 
这时候大家就有疑问了,怎样才会走到 isImportRequest,以及为什么这么干?别急,下面我们慢慢唠~
首先,啥子是通用静态资源嘞~~
是 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 配置中的文件
其次,我们来康康,静态资源的导入
// 解析后的公共路径作为 src 来请求资源
这是我们的输入和输出:我们可以看到每个 import 都会被处理成 xxx?import 请求,返回解析后的代码,得到一个公共可访问 url
然后,我们依旧根据 wy-logo 图片来对比分析~ 之前是直接静态文件访问,提前返回资源,不会被解析成 import 依赖 反之如果作为 js 文件 import 引入,则不会当成正常静态资源,都得优先处理成通用&import js 文件,:
- import logo from '../../public/wy-logo.png'
 - console.log(logo)
 
关键代码:
- // 从初始执行 cli 处启动 createServer - vite/src/node/server/index.ts
 - // 会调用 resolveConfig()获取 config, 而该方法里会调用 resolvePlugins(),
 - // 其中有个 plugin 处理是: importAnalysisPlugin(config)
 - // 所在文件如下:
 - import { importAnalysisPlugin } from './importAnalysis'
 - // 在 importAnalysic.js 里有个关键方法:
 - async transform(source, importer, ssr) {
 - // 用`?import`标识非 js/css 的 import 依赖
 - url = markExplicitImport(url)
 - }
 
可以看到 public 下的静态资源直接请求会直接返回,反之 import 静态资源的话 - 处理成/public/wy-logo.png?importwy-logo.png?import。需要后续通过返回解析后的 url 再去访问资源。es-module-lexer 解析处理:
这时候大家就理解了之前的疑问,isImportRequest 需要区分是否是直接的静态资源请求,如果是 import xxx 来引入的,都统一处理成依赖,给到你最终需要的一个 URL => 【公共静态资源访问路径】。这就是为什么 public 中的资源不建议被 JavaScript 文件引用,因为 publicDir 资源文件的定义就是直接可以请求,没有必要解析获取 url 后再请求!!!
引入 ?url通用静态资源可以直接处理获取 url,那要是想要处理其他资源,怎么显式导入为一个 URL 来用 (⊙_⊙)? 答案是用?url后缀 ../data/name.js:
- export const nameList = ['Tim', 'John', 'Bob', 'Catherine']
 - console.log(`名称列表 = `, nameList.join(' '))
 
components/name.vue
- import nameListUrl from '../data/name.js?url'
 - console.log(nameListUrl) // 解析成'/src/data/name.js'
 
源码解析: 同理,也是在resolvePlugins()里面有对 asset 处理的assetPlugin
- const urlRE = /(\?|&)url(?:&|$)/
 - async load(id) {
 - ...
 - // 如果没有被配置到静态资源 assetsInclude 并且 没有?url 后缀的,直接返回
 - if (!config.assetsInclude(cleanUrl(id)) && !urlRE.test(id)) {
 - return
 - }
 - id = id.replace(urlRE, '$1').replace(/[\?&]$/, '')
 - const url = await fileToUrl(id, config, this) // 获取公共可访问路径
 - return `export default ${JSON.stringify(url)}` // 返回解析后的代码
 - }
 
同一个例子,我们要获取 name.js 的数据:1.场景 1 是获取执行 name.js 后输出的数据 2.场景 2 是仅仅要获取 name.js 的文本,比如我们可以做 template 的字符串,那就需要使用到?raw后缀了。
- import nameString from '../data/name.js?raw'
 - console.log(nameString) // 解析出 export default "xxxxx" 文本
 
上源码。依旧是assetPlugin
- const rawRE = /(\?|&)raw(?:&|$)/
 - async load(id) {
 - ...
 - // raw requests, read from disk
 - if (rawRE.test(id)) {
 - // publicDir 存在同名静态文件,优先返回
 - const file = checkPublicFile(id, config) || cleanUrl(id)
 - // ?raw 作为 query, 读取对应的文件并且返回其字符串
 - return `export default ${JSON.stringify(
 - await fsp.readFile(file, 'utf-8')
 - )}`
 - }
 - ...
 - }
 
脚本可以通过 ?worker 或 ?sharedworker 后缀导入为 web worker。上案例:/data/name-worker.js
- export const nameList = ['Tim', 'John', 'Bob', 'Catherine']
 - addEventListener('message', (e) => {
 - console.log('主线程: ', e.data)
 - postMessage({
 - word: `Hi,我是 worker~~ 老大,这是你要的名单:${nameList.join(' ')}`,
 - nameList
 - })
 - close() // 关闭 worker
 - }, false)
 
/components/name.vue
- import NameWorker from '../data/name-worker.js?worker'
 - export default defineComponent({
 - mounted () {
 - const worker = new NameWorker()
 - worker.postMessage('Hi, 我是主线程~') // 主线程向 Worker 发消息
 - worker.onmessage = (e) => { // 接收子线程发回来的消息
 - if (e.data) {
 - console.log('Worder: ' + e.data.word)
 - this.workerNameList = e.data.nameList
 - worker.terminate() // Worker 完成任务以后,主线程就可以把它关掉
 - }
 - }
 - worker.onerror = (e) => {
 - console.log([
 - 'ERROR: Line ', e.lineno, ' in ', e.filename, ': ', e.message
 - ].join(''))
 - }
 - }
 - })
 
这里,就是给 name-worker.js 封装了一层,提供了 WorkerWrapper 函数帮你新建了一个 worker 对象
debug 图如下:
源码。【啊啊啊!!!源码可真多,我可太暴躁了】
resolvePlugins()里 执行webWorkerPlugindev 开发环境下:
- async transform(_, id) {
 - const query = parseWorkerRequest(id)
 - let url: string
 - url = await fileToUrl(cleanUrl(id), config, this) // 原始 url
 - url = injectQuery(url, WorkerFileId) // 加上&worker_file 的 query 标识
 - const workerConstructor =
 - query.sharedworker != null ? 'SharedWorker' : 'Worker'
 - const workerOptions = { type: 'module' }
 - return `export default function WorkerWrapper() { // 输出新建 worker 对象的 template
 - return new ${workerConstructor}(${JSON.stringify(
 - url
 - )}, ${JSON.stringify(workerOptions, null, 2)})
 - }`
 - }
 
build 生产下:inline 模式和非 inline 模式
- if (query.inline != null) {
 - // 打包文件作为入口去支持 import 导入 worker 或者行内写入
 - const rollup = require('rollup') as typeof Rollup
 - const bundle = await rollup.rollup({
 - input: cleanUrl(id),
 - plugins: config.plugins as Plugin[]
 - })
 - try {
 - // 在生产构建中将会分离出 chunk,worker 脚本将作为单独的块发出
 - const { output } = await bundle.generate({
 - format: 'es',
 - sourcemap: config.build.sourcemap
 - })
 - return `const blob = new Blob([atob(\"${Buffer.from(output[0].code).toString('base64')}\")], { type: 'text/javascript;charset=utf-8' });
 - export default function WorkerWrapper() {
 - const objURL = (window.URL || window.webkitURL).createObjectURL(blob);
 - try {
 - return new Worker(objURL);
 - } finally {
 - (window.URL || window.webkitURL).revokeObjectURL(objURL);
 - }
 - }`
 - } finally {
 - await bundle.close()
 - }
 - } else {
 - // 作为分开的 chunk 处理`?worker&inline`,内联为 base64 字符串 - 要求 inline 的 worker
 - url = `__VITE_ASSET__${this.emitFile({
 - type: 'chunk',
 - id: cleanUrl(id)
 - })}__`
 - ....// 同开发返回的 template
 - }
 
咦~,那这个/src/data/name-worker.js?worker_file 又通过?worker_file后缀给我们处理啥了?webWorkerPlugin:
- const WorkerFileId = 'worker_file'
 - async transform(_, id) {
 - const query = parseWorkerRequest(id)
 - if (query && query[WorkerFileId] != null) {
 - return {
 - // 其实只是作为 执行导入之前生成的 worker.js 文件 的标识.......
 - code: `import '${ENV_PUBLIC_PATH}'\n` + _
 - }
 - }
 - }
 
害!五个静态处理方式总算是讲完了~
vite 的静态处理关键点就是 :
(1)通过特殊 query(?:worker|sharedworker|raw|url)来区分不同类型静态资源,进行特殊的 transform 处理。
(2)publicDir 限定直接访问的静态资源 本文通过列举处理点,逐一提供案例+debug 截图+源码分析的方式,让大家理解静态处理的使用和底层原理。
                当前名称:【vite】你不知道的小妙招,确定不看一下吗?
                
                链接URL:http://www.csdahua.cn/qtweb/news30/522730.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网