
目录结构
my-vue-app/├── public/ 静态资源,直接复制到 dist,不做转换├── src/│ ├── assets/ 被引用的资源,构建时加哈希│ ├── components/ 公共组件│ ├── App.vue 根组件│ └── main.ts 应用入口├── index.html 页面入口├── vite.config.ts Vite 配置文件└── package.jsonTypeScript 模板用 vite.config.ts,JavaScript 模板用 vite.config.js,其余部分一致。
public 与 assets 的区别
public/ 里的文件在开发环境按根路径访问,构建时原样复制到 dist,不经过 Vite 转换。判断一个文件放哪里,看它需不需要用 import 引入:需要引入的放 src/assets,不需要的放 public。
src/assets 里的资源由 Vite 处理。被引用的资源会带上内容哈希写进文件名;小于 assetsInlineLimit 的资源会内联成 base64。
index.html 是入口
Vite 把 index.html 当成源码,它也是模块图的一部分。Vite 会解析其中的 <script type="module" src="/src/main.ts">,从这个标签出发找到整个依赖图。
Webpack 和 Rollup 的入口是 JS 文件,Vite 的入口是 HTML。
这个文件放在项目根目录,不在 src/ 里。多页应用可以配多个 .html 入口,每个页面各有自己的模块图;应用如果不用 index.html 当入口,可以在构建配置的 input 里指定其他入口文件。
单文件组件的三类顶层块
每个 .vue 文件由三类顶层语言块组成:<template>、<script> 和 <style>。除此之外还能写自定义块,块名自己定,交给相应的工具处理。
<template> 最多一个,写模板,最终编译成 render 函数。
<script> 最多一个,导出组件配置对象。它有一个变体 <script setup>,同样是顶层块,同样最多一个。
Vue 文档把 <script setup> 列为推荐写法:顶层变量和函数自动暴露给模板,可以理解成组件 setup() 的入口,编译期能做的优化也更多。
<style> 可以写多个。加 scoped 时样式只作用于当前组件,加 module 时按 CSS Modules 处理,使用时拿到一个样式对象。
npm run dev 做了什么
npm 读取 package.json 的 scripts,找到 "dev": "vite",然后执行 vite 对应的可执行文件,启动开发服务器。
npm 会把依赖包的可执行文件链接到 ./node_modules/.bin,并在运行 scripts 时把这个目录加进 PATH。所以在脚本里直接写 vite,找到的就是项目本地安装的版本。
在命令行里直接敲 vite 走的是系统 PATH,找不到项目里的那一份。这时可以用 npx vite 或 npm exec vite,它们会在本地依赖里查找并执行。
./node_modules/.bin 里的可执行文件在 POSIX 系统上是符号链接。Windows 上会额外生成一个 cmd 文件,可以直接用名字调用,也可以用名字加 .cmd,在 PowerShell 里同样可用。
开发服务器的请求流程
以 import { createApp } from 'vue' 为例,一次页面加载大致经过下面几步。
浏览器访问 http://localhost:5173,服务器返回 index.html。HTML 里有这一行,浏览器读到后会去请求 main.ts。
<script type="module" src="/src/main.ts"></script>Vite 拦截这个请求并按需编译:TS 转成 JS,Vue 模板转成渲染函数。main.ts 里的 import { createApp } from 'vue' 是裸模块名,浏览器没法直接解析,于是再发一次请求。
这次 Vite 把裸模块重写成可以访问的 URL 返回,例如 /node_modules/.vite/deps/vue.js?v=f3sf2ebd。依赖预构建的产物默认缓存在 node_modules/.vite。
这些响应带 Cache-Control: max-age=31536000,immutable,缓存靠 URL 上的版本查询参数失效。依赖变了,参数就变,浏览器拿到的是新文件。
开发服务器默认监听 5173 端口,端口被占用时自动换下一个空闲端口。
浏览器主动请求每一个依赖,服务器按需编译,而不是一次打包全部文件。源码不预先打包,冷启动只要处理入口和少量依赖,不必等整个应用编译完。
浏览器怎么存放这些模块
浏览器拿到 ESM 模块后加载到内存里执行,不写到硬盘。一次模块加载在规范里的过程是:下载代码,解析成抽象语法树,编译成字节码,实例化模块环境记录并绑定导出,最后执行代码。
同一个模块只加载一次,之后再 import 直接读缓存。
硬盘上的缓存分两类。HTTP 缓存由响应头控制,Service Worker 缓存由代码自己控制,常见于 PWA。
HTTP 缓存
强缓存用 Cache-Control: max-age=N 表示响应在 N 秒内保持新鲜、可以复用,浏览器具体存在哪里由实现决定。max-age 只管新鲜期,不管要不要校验:用户刷新页面时,浏览器仍可能发一次条件请求。
协商缓存是另一种做法:浏览器带着标识发请求问服务器文件变了吗,没变就返回 304 Not Modified,浏览器继续用本地副本。
Service Worker 缓存
Service Worker 是可编程的缓存层,缓存哪些文件、什么时候更新都由代码决定。PWA 常用它做离线访问,它和 HTTP 缓存是两套机制,可以叠加使用。
Vue 3 学习笔记
连载中分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
最后更新于 ,距今已过 214 天
部分内容可能已过时