Vite文档学习:浅析为什么选Vite及其Glob导入用法及报错[plugin:vite:import-glob] Invalid glob import syntax: Could only use literals

一、为什么选择 Vite?

1、问题背景

  当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。我们开始遇到性能瓶颈 —— 使用 JavaScript 开发的工具通常需要很长时间(甚至是几分钟)才能启动开发服务器,即使使用 HMR,文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。

  特别是在修改 css 需要调样式的时候,我们需要实时查看 css 的修改在浏览器上的体现,构建慢就会极大的降低我们的开发效率。

  Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写

2、缓慢的服务器启动

  当冷启动开发服务器时,基于打包器的方式启动必须优先抓取并构建你的整个应用,然后才能提供服务

  而 Vite 通过在一开始将应用中的模块区分为 依赖源码 两类,改进了开发服务器启动时间

  • 依赖 大多为在开发时不会变动的纯 JavaScript。一些较大的依赖(例如有上百个模块的组件库)处理的代价也很高。依赖也通常会存在多种模块化格式(例如 ESM 或者 CommonJS)

    Vite 将会使用 esbuild 预构建依赖。Esbuild 使用 Go 编写,并且比以 JavaScript 编写的打包器预构建依赖快 10-100 倍。

  • 源码 通常包含一些并非直接是 JavaScript 的文件,需要转换(例如 JSX,CSS 或者 Vue/Svelte 组件),时常会被编辑。同时,并不是所有的源码都需要同时被加载(例如基于路由拆分的代码模块)。

  Vite 以 原生 ESM 方式提供源码。这实际上是让浏览器接管了打包程序的部分工作Vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。

  下面看一下以 webpack 为代表的传统打包器的构建方式:它会将所有module构建成 Bundle,然后启动 Server 提供资源

  而 Vite 以原生ESM方式,并且在使用路由懒加载时,只有当前路由所需要的依赖,才会被当成 module 被浏览器引入。这样就可以大大提升构建速度。

3、缓慢的更新

  基于打包器启动时,重建整个包的效率很低。原因显而易见:因为这样更新速度会随着应用体积增长而直线下降。

  一些打包器的开发服务器将构建内容存入内存,这样它们只需要在文件更改时使模块图的一部分失活,但它也仍需要整个重新构建并重载页面。这样代价很高,并且重新加载页面会消除应用的当前状态,所以打包器支持了动态模块热重载(HMR):允许一个模块 “热替换” 它自己,而不会影响页面其余部分。这大大改进了开发体验 —— 然而,在实践中我们发现,即使采用了 HMR 模式,其热更新速度也会随着应用规模的增长而显著下降。

  在 Vite 中,HMR 是在原生 ESM 上执行的。当编辑一个文件时,Vite 只需要精确地使已编辑的模块与其最近的 HMR 边界之间的链失活(大多数时候只是模块本身),使得无论应用大小如何,HMR 始终能保持快速更新。

  Vite 同时利用 HTTP 头来加速整个页面的重新加载(再次让浏览器为我们做更多事情):源码模块的请求会根据 304 Not Modified 进行协商缓存,而依赖模块请求则会通过 Cache-Control: max-age=31536000,immutable 进行强缓存,因此一旦被缓存它们将不需要再次请求

4、为什么生产环境仍需打包

  尽管原生 ESM 现在得到了广泛支持,但由于嵌套导入会导致额外的网络往返,在生产环境中发布未打包的 ESM 仍然效率低下(即使使用 HTTP/2)。为了在生产环境中获得最佳的加载性能,最好还是将代码进行 tree-shaking、懒加载和 chunk 分割(以获得更好的缓存)。

  要确保开发服务器和生产环境构建之间的最优输出和行为一致并不容易。所以 Vite 附带了一套 构建优化 的 构建命令,开箱即用。

二、Glob 导入

  Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块:

const modules = import.meta.glob('./dir/*.js')
// 以上将会被转译为下面的样子:vite 生成的代码
const modules = {
  './dir/foo.js': () => import('./dir/foo.js'),
  './dir/bar.js': () => import('./dir/bar.js')
}

// 你可以遍历 modules 对象的 key 值来访问相应的模块:
for (const path in modules) {
  modules[path]().then((mod) => {
    console.log(path, mod)
  })
}

  匹配到的文件默认是懒加载的,通过动态导入实现,并会在构建时分离为独立的 chunk。如果你倾向于直接引入所有的模块(例如依赖于这些模块中的副作用首先被应用),你可以使用 import.meta.globEager 代替:

const modules = import.meta.globEager('./dir/*.js')
// 以上会被转译为下面的样子:vite 生成的代码
import * as __glob__0_0 from './dir/foo.js'
import * as __glob__0_1 from './dir/bar.js'
const modules = {
  './dir/foo.js': __glob__0_0,
  './dir/bar.js': __glob__0_1
}

三、报错:[plugin:vite:import-glob] Invalid glob import syntax: Could only use literals

  报错说的意思是只能使用文本,而我们的 path 使用了变量,所以会报错。改成这样即可

const getViewRoutes = () => {
    const mobileViews = import.meta.glob('@/views/mobile-views/*/*.vue')
    console.log('🚀 ~ file: index.js:6 ~ getViewRoutes ~ mobileViews:', mobileViews)
}
getViewRoutes()

 

posted @ 2022-09-12 21:14  古兰精  阅读(4387)  评论(2编辑  收藏  举报