初始化项目

首先我们新建一个test-vite文件夹,作为项目根目录,然后新建几个文件

  • main.js:入口文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
      // import { count } from './count'
    // 上面这种写法是有异常的,浏览器在找文件的时候,他需要的是一个完整的文件名,我们经常在vite/webpack环境下这样子写是因为这两个构建工具在编译的时候帮我们补全了路径,但是我们现在没有使用构建工具的时候需要自己补全路径
    import { count } from './count.js'
    console.log(count);

    - count.js:抽离的count模块

    ~~~javascript
    export const count = 1
  • index.html:项目启动文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    </head>
    <body>
    <script src="./main.js" type="module"></script>
    // 告诉浏览器我们的文件是模块化的形式
    </body>
    </html>

尝试使用第三方库

1
2
3
import _ from 'loadsh'

// 我们尝试在count.js文件引入loadsh,浏览器会抛出异常:Uncaught TypeError: Failed to resolve module specifier "loadsh". Relative references must start with either "/", "./", or "../".

​ 浏览器的意思就是,你引入文件的路径需要是一个绝对路径或者是相对路径,他不会去node_module目录下去找这个文件。既然我们现在的最佳实践是node_module,那么为什么ES官方在我们给定的不是相对路径或者是绝对路径的时候不帮我们去node_module目录下面找呢?

原因也很简单:我们可以试想一下,当他帮我们去node_module下面找的话,会发生什么?我们需要知道,浏览器拿资源是通过网络请求的形式的,所以他会发送一条请求,去加载我们的node_module目录下的loadsh文件,然后执行loadsh文件发现又引入的其他文件,又发送网络请求引入其他文件,其他文件下面可能还有,又要继续发送请求引入,那么浏览器需要发送多少网络请求?

vite应运而生

​ 刚刚我们发现,我们在项目中无法直接引入node_module下的文件,那么肯定有方案能解决的。这个方案就是这篇系列文章的主人公Vite。

1
pnpm add vite -D

​ 然后我们可以在pageage.json文件的scripts定义一下vite的启动命令,也可以只用使用npx vite。

1
2
3
"scripts": {
"dev": "vite",
}

​ 那么使用vite启动的开发服务器,我们可以发现引入的loadsh并没有什么问题。原因也很简单,Vite在遇到直接导入的第三方库的时候,他会尝试帮我们进行路径补全。路径补全是一个自下而上的过程,换句话说就是从当前项目的目录向上查找,直到搜寻到响应依赖或者根目录位置。

1
2
3
import _ from 'loadsh'
//会尝试补全成
import _ from './node_module/.vite/loadsh'

​ 所以说vite最终引入的并不是node_module下的loadsh,而是他编译过后的loadsh,存放在.vite目录下。我们可以尝试看看浏览器引入的loadsh地址。

1
http://127.0.0.1:5173/node_modules/.vite/deps/loadsh.js?v=38f2cd69

​ 首先v=38f2cd69,这个=后面是一串哈希,如果我们不修改loadsh文件,那么浏览器就走缓存,直接从缓存中拿这个文件,不需要重新发一次网络请求,节约性能。

Vite依赖预构建

​ 首先Vite会找到相应的依赖,然后会调用esbuild将其他规范的代码转换成esmodule规范,然后放到当前目录下的node_modules/.vite/deps,同时会对esmodule规范的各个模块进行统一的集成。

vite的依赖预构建带来的好处:

  1. 在我们使用第三方库的时候,第三方库所使用的的模块化方案不是我们能敲定的,这时库的作者所敲定的,他可以选择使用不同的导出格式。所以Vite在依赖预构建的时候会统一转换成esmodule形式。
  2. 依赖预构建后的产物都放到了.vite/deps目录下,后续对路径进行重写也比较方便
  3. 解决网络多包的问题。这也是原生esmodule不敢支持从node_module导入的原因之一

一个简单地依赖预构建案例

  • a.js

    1
    2
    3
    export default function foo(){
    console.log('hello foo')
    }
  • index.js

    1
    export {default as foo} from './a.js'
  • Vite在导入index.js的时候,发现有上面这一行语句,他会立刻对该语句进行转换。上面哪一行直接就不要了,转换成函数声明的形式,这样浏览器就不需要再次发送网络请求去加载a.js文件了。

    1
    2
    3
    function foo(){
    console.log('hello foo')
    }