vite 是如何让浏览器识别 .vue 文件的
index.html 中请求了 main.js ,main.js 请求 app.vue 文件,vite 起的服务器会收到浏览器发起的资源请求,如果是要获取 vue 文件的话,会先将其进行 解析,编译成单纯的 js 代码,然后返回文件内容,并且将 content-type 设置成 text/javascript,使得浏览器按 js 的方式执行 vue 文件内容
同理可得,对于 import 的 css 文件来说,它也是将其直接设置成 js 脚本的形式来进行读取,主要是方便热更新、scopedCss 等 css Module 1. module.css 规范命名,表示需要开启 css 模块化 2. 将该文件下的所有类型进行一定的规则替换(.footer => .footer_abc_123) 3. 同时创建一个映射对象 { footer: "footer_abc_123" } 4. 将 module.css 内容替换成 js 脚本 5. 将创建的映射对象在脚本中进行默认导出配置
typescript
import { path } from 'node:path'
import { defineConfig } from 'vite'
export default defineConfig({
resolve: {
alias: {
'@': path.resove(__dirname, './src'),
},
},
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
css: {
modules: {
// localsConvention // 配置对象的展示规则
scopeBehaviour: 'global', // 是否启用 scoped
generateScopedName: '[name]_[local]_[hash:5]',
hashPrefix: 'hello',
globalModulePaths: ['./a.module.css'], // 是 module 命名规范,但是该文件不想被 module 化
},
preprocessorOptions: {
// 预编译器相关配置,假设使用到了 less,那么就可以新增下面的 less 配置对象,其最终会传到 lessc 中进行编译处理
less: {
math: 'alaways',
globalVars: {
// 定义 less 全局变量,则 less 文件中可以直接使用而无需导入
mainColor: 'red',
},
},
},
devSourcemap: true, // 开启之后,css 也能找到源文件
// postcss 除了这样配置,也可以通过 postcss.config.js 进行配置
postcss: {
// postcss 不同于 less 等预处理器,它是目标是完整的参与 css 的编译过程,也就是通过 plugins 的形式,可以无限的去支持想做的事情(原本是可以通过 less-plugins 去支持 less 文件的编译,但是每次 less 的升级导致该 plugin 也需要对应的升级,变得没有必要,就变成了预处理器先处理好,再将结果给到 postcss 进行更多的处理,比如降级,导致 postcss 有点”后处理器“的意思)
plugins: [], // postcss-preset-env 安装将预设插件后,可以支持语法降级(预设包:相当于一个配套好的环境,包含了全部所需的插件)( 'importFrom': './src/assets/cssVars.css' 那些全局定义的 媒体查询、自定义属性、自定义选择、环境变量等等,不管是 css、 js、json、方法还是传递的对象都需要通过 importFrom 配置项具名导入进来。否则在 .vue 文件中使用的时候插件是找不到这些定义的内容的)
},
},
build: {
minify: true, // 是否去掉空格之类的
rollupOptions: {
// 配置 rollup 的构建策略
output: {
assetFileNames: '[hash].[name].[ext]',
manualChunks(id: string) {
// 分包策略
if (id.includes('node_modules')) return 'vendor' // 像是 lodash 之类的包,非业务代码,直接打包生成固定的文件,避免 hash 变化每次都要重新下载
},
},
},
assetsInlineLimit: 2 * 1024, // 小于此阈值的导入或引用资源将内联为 base64 编码,以避免额外的 http 请求。设置为 0 可以完全禁用此项
outDir: 'dist', // 构建输出目录
assetsDir: 'assets', // 静态资源所在目录
emptyOutDir: true, // 构建之前是否先清除之前的输出结果
},
})图片加载
javascript
import img from './a.svg?raw' // 直接读取文件内容
import img from './a.svg?url' // 对应的 url 地址plugins 实现
plugins 的 hook 支持多种类型,如 buildStart 可以改写成以下形式,从而使得它的执行时机修改
javascript
buildStart: {
sequential: true, // 按顺序的形式进行(默认为同步的形式,那么就无法阻止其他 hook 的执行)
handler
}alias
typescript
import { Plugin } from 'vite'
// 通过 config 钩子返回对应的 alias 配置,使得 import 的时候可以支持 alias 路径
function alias(): Plugin {
return {
name: 'ViteAlias',
config: () => ({
resolve: {
alias: {
// fs.readdirSync 获取全部的文件
// fs.statSync 文件信息,判断是否是文件夹
},
},
}),
}
}indexHtmlTransfer
typescript
import { Plugin } from 'vite'
function indexHtmlTransfer(options: {
inject: {
data: Record<string, any>
}
}): Plugin {
return {
name: 'indexHtmlTransfer',
transformIndexHtml: {
enforce: 'pre', // 执行时机,这里需要先将 index.html 中的模板转换了,否则其它函数读取的时候,因为不认识会出现报错
transform(html, ctx) {
return html.replaceAll(/<%= title %>/, options.inject.data.title)
},
},
}
}MockJS
typescript
import { Plugin } from 'vite'
function MockJS(options: {
inject: {
data: Record<string, any>
}
}): Plugin {
return {
name: 'MockJS',
configureServer(server) {
server.middlewares.use((req, res, next) => {
// 自定义请求处理...
if (req.url === '/api/user') {
// require mock/index.js,判断里面的配置有没有符合该路径的,然后将符合的数据进行响应
const data = {}
res.setHeader('content-type', 'application/json') // 避免乱码
res.end(JSON.stringify(data))
} else {
next()
}
})
},
}
}typescript 使用
vite-plugin-checker // 可以配置错误检查,当出现 ts 错误的时候,直接将异常输出到页面上(引起开发者重视并强制要求解决才能继续开发) tsc --noEmit && vite build // 配置该行可以先进行 ts 检测,完全通过后再执行构建命令
分包
对应项目应用到的静态文件进行抽离,避免重复的打包导致 hash 指纹改变,用户需要重新下载文件
javascript
import {defineConfig} from 'vite'
import vue from 'vue'
export default defineConfig({
build: {
rollupOptions:{
// manualOptions: {
// '自定义文件名': ['lodash', 'vue']
// }
// or...
manualOptions(id: string){
if(id.includes('node_modules')) return 'vendor'
}
}
}
})打包结构控制
javascript
// js 文件放到 js/ 下,css 放到 css/ 下
import { defineConfig } from 'vite'
import vue from 'vue'
export default defineConfig({
build: {
rollupOptions: {
output: {
entryFileNames: 'js/[name]-[hash].js', // 针对 js 文件
chunkFileNames: 'js/[name]-[hash].js', // 动态 js 文件(异步引入的)
assetFileNames: '[ext]/[name]-[has].[ext]', // 其它文件
},
},
},
})Server.proxy
javascript
import { defineConfig } from 'vite'
import vue from 'vue'
export default defineConfig({
server: {
proxy: {
'/proxy_go': {
target: 'https://beta.2tianxin.com', // 决定域名
changeOrigin: true,
secure: true,
rewrite: () => '/vueAdmin/mecord', // 决定访问路径,此处如果返回的 path 包含域名部分的话也会被抛弃
cookieDomainRewrite: {
// 修改 setcookie 的 domain,此处是设置成 localhost,那么其他的第三方接口也能在请求的时候带上了
'*': '',
},
// 更加详细的修改请求配置
// configure: (proxy) => {
// proxy.on('proxyReq', (proxyReq, req) => {
// if (req.headers.cookie) {
// proxyReq.setHeader(
// 'Cookie',
// 'x-a=b5c0e524116d4; table=550b05ef',
// )
// }
// })
// },
},
},
},
})构建(库模式)
type LibraryFormats = 'es' | 'cjs' | 'umd' | 'iife' | 'system' 区别
前端库打包格式对比
在 vite 构建中,如果选择为 es lib 模式的话,那么为了保留 pure 注释信息,故不会去掉换行、空格,因为它认为 es 格式的工具是中间级别,会被其它工具引入,当其产物被其它项目引入并构建的时候(需要根据 pure 注释信息进行 tree shaking),才是最终的构建 app 产物;
| 格式 | 全称 | 特点 |
|---|---|---|
es | ECMAScript 模块 | 标准的ES6模块格式,适合现代浏览器和打包工具 |
cjs | CommonJS | Node.js的模块系统,主要用于服务端JavaScript |
umd | 通用模块定义 | 兼容浏览器和Node.js,同时支持AMD和CommonJS |
iife | 立即调用函数表达式 | 自执行函数,为浏览器使用创建隔离作用域 |
system | SystemJS | 专为SystemJS模块加载器设计的格式,支持动态ES模块 |
markdown
核心区别
1. **运行环境**:
- `es`:现代浏览器(原生支持) + 打包工具
- `cjs`:Node.js环境
- `umd`:通用环境(浏览器+Node.js)
- `iife`:仅限浏览器
- `system`:SystemJS加载器环境
2. **模块加载方式**:
- `es`:静态import/export(ES6标准)
- `cjs`:同步require()
- `umd`:自动检测AMD/CommonJS环境
- `iife`:无模块系统,使用全局作用域
- `system`:动态模块加载
3. **Tree Shaking支持**:
- `es`格式支持最好
- 其他格式支持有限或不支持
4. **典型使用场景**:
- `es`:现代前端项目/Vite/Rollup
- `cjs`:Node.js库/工具
- `umd`:需要同时支持浏览器和Node.js的库
- `iife`:直接通过<script>标签引入的库
- `system`:使用SystemJS加载器的项目构建 web-components(vue)
ts
import { fileURLToPath } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import jsx from '@vitejs/plugin-vue-jsx'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
'~': fileURLToPath(new URL('./', import.meta.url)),
},
},
server: {
port: 7402,
},
plugins: [vue(), jsx(), tailwindcss()],
base: './',
define: {
'process.env': {
NODE_ENV: 'production',
},
},
build: {
minify: true,
lib: {
entry: fileURLToPath(new URL(`./src/components/index.ts`, import.meta.url)),
formats: ['iife'], // 基本就是一次性使用产物,所以使用 iife 格式
name: 'entrypoint',
},
outDir: 'dist',
sourcemap: false,
emptyOutDir: true,
copyPublicDir: true,
rollupOptions: {
output: {
entryFileNames: 'entrypoint.js',
},
},
},
})ts
import { defineCustomElement } from 'vue'
import element from './component.ce.vue'
customElements.define('v-toast', defineCustomElement(element))vue
<!-- ce.vue 结尾,那么 vite 会识别成 web-component 组件,构建的时候会将 css 也打包到 js 当中-->
<template>
<div class="font-bold">Hi,this is a vue version web-component</div>
</template>
<style>
@import 'tailwindcss';
</style>ts
// 挂载到 window 上,实现桥接通信
function useMitt() {
const mitt = new EventTarget()
mitt.addEventListener('some-event', () => {
// do something
})
mitt.dispatchEvent('some-event')
}