说明
- 快速启动本地开发服务器
- 代码更新快速显示在浏览器页面中
- 在开发阶段 vite 会使用 esbuild ,同时 基于 原生 ES 模块实现 高效的 HMR
- 在生产阶段 vite 会使用 rollup 来打包代码,输出优化过的静态资源
1. 简单搭建 vite 项目
通过脚手架搭建项目
npm init vite@latest # npm
yarn create vite # yarn
.
├── dist # build 后输出目录
├── index.html # 页面入口
├── package.json
├── src # 源文件
├── tsconfig.json
├── vite.config.ts # vite 配置文件
└── yarn.lock
浏览器中使用 es module
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
内置命令
{
"scripts": {
"dev": "vite", // 启动开发服务器
"build": "vite build", // 为生产环境构建产物
"serve": "vite preview" // 本地预览生产构建产物 需要先 build 再 预览
}
}
常用功能实现
1. 支持使用 JSX 语法及 HMR 热模块加载
const react = require('@vitejs/plugin-react');
return defineConfig({
plugins: [react()]
});
const react = require('@vitejs/plugin-react');
return defineConfig({
plugins: [react({
babel: {
presets: [...],
plugins: [...],
babelrc: true, // 是否直接使用外部的 .babelrc 配置文件
configFile: true, // 是否直接使用外部的 babel.config.js 配置文件
}
})]
});
2. Typescript 支持
- TS 编译器选项 compilerOptions.isolatedModules = true
- TS 编译器选项 compilerOptions.useDefineForClassFields = true
- TS 编译器选项 compilerOptions.types = [“vite/client”]
- 声明一个 vite-env.d.ts 文件
/// <reference types="vite/client" />
来增加一些 vite 的类型定义补充
3. 样式处理
import '../style/xxx.css';
yarn add -D less
yarn add -D sass
// 组件中使用
import userStyle from './styles/users.module.css';
const User = () => (<div className={userStyle.container}>user</div>)
return defineConfig({
css: {
modules: { // css-module 配置,最终会传给 postcss-modules 处理
scopeBehaviour: 'local'
},
postcss: {}, // 内联的 postcss 配置,功能等同 postcss.config.js
preprocessorOptions: { // css 预处理器的配置
less: {
javascriptEnabled: true,
globalVars: {
cdnUrl: JSON.stringify('https://www.baidu.com'),
},
}
},
build: {
cssCodeSplit: true, // 开启或者禁用,打包时的 css 代码拆分
}
}
});
4. 静态资源处理
import imgUrl from './img.png';
import imgUrl2 from '/img2.png'; // 相对于根文件夹的 绝对路径 /img2.png
// 样式
// .bgimg {
// width: 150px;
// height: 150px;
// background-image: url('./huawei.jpeg');
// }
const User = () => (
<div>
<img src={imgUrl2} alt="" />
{/* 如下访问的时 vite /public/img3.png */}
<img src="/img3.png" alt="" />
<img src={imgUrl} alt="" />
<div className={RootContainerLess.bgimg} />
</div>
)
return defineConfig({
css: {
publicDir: 'public', // 可以是文件系统的绝对路径path.resolve(__dirname, 'public') 也可以是相对路径
assetsInclude: ['**/*.gltf'], // 图片等会被默认当做静态资源,这个选项可以增加更多的静态资源文件类型
build: {
assetsInlineLimit: 4096, // 静态资源大小小于这个数值的,会在打包时内联为 base64 ,减少http 请求
}
}
});
5. 低版本浏览器兼容
const react = require('@vitejs/plugin-react');
const legacy = require('@vitejs/plugin-legacy');
return defineConfig({
plugins: [
react(),
legacy({
additionalLegacyPolyfills: ['regenerator-runtime/runtime'] // 给 IE11 提供异步支持
})
]
});
6. 给文件夹设置别名 alias
return defineConfig({
resolve: { // 文件别名
alias: {
store: path.join(context, 'src/store')
},
},
});
// 业务代码中,可以按照如下方式引用
import globalStore, { Provider } from 'store';
7. 构建阶段传入变量
- 借助 vite.config.js 中的配置项 define 来定义一些常量
// vite.config.js 中定义传入的常量
return defineConfig({
resolve: { // 文件别名
define: {
RUNTIME_CONST: JSON.stringify(’CONST 常量‘),
},
},
});
// d.ts 文件中定义这个常量类型
declare global {
const RUNTIME_NODE_ENV: string;
}
// 业务代码中使用
console.log('构建阶段传入的常量:', RUNTIME_CONST);
- 借助 .env 文件,Vite 使用 dotenv 从你的 环境目录 中的下列文件加载额外的环境变量 --更多查看
- .env # 所有情况下都会加载
- .env.local # 所有情况下都会加载,但会被 git 忽略
- .env.[mode] # 只在指定模式下加载
- .env.[mode].local # 只在指定模式下加载,但会被 git 忽略
此外 vite 内置了一些环境变量
- import.meta.env.MODE: {string} 应用运行的模式。
- import.meta.env.BASE_URL: {string} 部署应用时的基本 URL。他由base 配置项决定。
- import.meta.env.PROD: {boolean} 应用是否运行在生产环境。
- import.meta.env.DEV: {boolean} 应用是否运行在开发环境 (永远与 import.meta.env.PROD相反)。
.env 文件中定义的环境变量也会挂载到 import.meta.env 对象上
// vite.config.js 中定义 env 文件存放地址及允许解析成常量的前缀
return defineConfig({
envDir: path.join(context, 'env'), // 加载 .env 文件的目录 默认根文件夹
envPrefix: 'VITE_ENV_', // .env 文件中环境变量格式
});
// 创建 /env/.env 文件
// 内容:VITE_ENV_XXX=xxx-xxx-xxx # 只有 VITE_ENV_ 为前缀的常量才会挂载到 import.meta.env 对象下
// 配置 TS 类型 vite-env.d.ts 文件中 增加如下
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_ENV_XXX: string
// 更多环境变量...
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
// 业务代码中使用注入的常量
console.log(import.meta.env.VITE_ENV_XXX);
8. 自动加载模块,而不必到处 import 模块
// vite.config.js 中使用插件
const inject = require('@rollup/plugin-inject');
return defineConfig({
plugins: [
inject({
exclude: /\.less/,
include: /\.[tj]sx?$/,
_: 'lodash', // 自动导入的模块
React: ['react', '*'],
}),
react(),
]
});
// 业务代码中即可不 import lodash mobx react 等
9. 给入口 HTML 传入变量
// vite.config.js 中使用插件
const {injectHtml} = require('vite-plugin-html');
return defineConfig({
plugins: [
injectHtml({
data: {
MAIN_API_URL: 'https://www.baidu.com' // 测试 html 模板传参
},
}),
]
});
// index.html 文件中使用传参
<html lang="en">
<body>
<script>
var API_URL = '<%- MAIN_API_URL %>';
</script>
<div id="root"></div>
<script type="module" src="./index.tsx"></script>
</body>
</html>
10. 按需加载
// vite.config.js 中使用插件
const styleImporter = require('vite-plugin-style-import').default;
return defineConfig({
plugins: [
styleImporter({
libs: [
{
libraryName: 'antd',
esModule: true,
resolveStyle: name => `antd/es/${name}/style/index`
}
]
}),
react()
]
});
// 业务代码中使用
import { Button } from 'antd';
// 相当于
import { Button } from 'ant-design-vue';
import 'antd/es/button/style/index.js';
11. 代码分离 Code-splitting 及打包后资源规划
// vite.config.js 中使用插件
const styleImporter = require('vite-plugin-style-import').default;
return defineConfig({
build: {
outDir: path.resolve(__dirname, 'build'), // 指定输出路径
assetsDir: 'static', // 一个相对于 outDir 的静态资源输出路径
cssCodeSplit: true, // 输出的 css 是否是经过 拆分的
sourcemap: true,
emptyOutDir: true, // 构建时清空目标文件夹
chunkSizeWarningLimit: 500, // 生成 chunk 大于这个数值会在控制台warning
rollupOptions: { // vite 内部使用 rollup 做打包
output: {
manualChunks: { // 自定义一些可以共享的 chunk, 默认 node_modules 下的 package 只拆出一个 vendor
basic: ['react', 'react-dom', 'react-router-dom'],
vendor: ['antd', 'axios', 'lodash', 'qs']
},
chunkFileNames: path.join('static', 'chunk/[name]-[hash].js'),
entryFileNames: path.join('static', 'js/[name]-[hash].js'),
assetFileNames: path.join('static', '[ext]/[name]-[hash].[ext]')
}
}
},
});
// 生成的资源结构
.
├── index.html
└── static
├── chunk
│ ├── basic-84f9de30.js
│ ├── index-0ea6d4a0.js
│ ├── index-97edd7a4.js
│ └── vendor-5e54d301.js
├── css
│ ├── index-0fbc2448.css
│ └── index-697ec425.css
├── jpeg
│ └── huawei-05681d0e.jpeg
└── js
└── index-820fccbf.js
12. 构建结果分析
// vite.config.js 中使用插件
const visualizer = require('rollup-plugin-visualizer').default;
return defineConfig({
plugins: [
visualizer({
open: true,
template: 'treemap',
gzipSize: true,
brotliSize: true,
filename: `report/index.html`
})
]
});
13. eslint 接入
const eslint = require('vite-plugin-eslint').default;
return defineConfig({
plugins: [
react(),
{
...eslint({
fix: false,
cache: true,
cacheLocation: path.join(__dirname, 'node_modules/.vite/eslint'),
formatter: 'stylish',
throwOnWarning: isDev,
}), enforce: 'pre' // 在 vite 核心插件执行前执行
},
]
});
14. 开发阶段相关设置
const pkg = require('../package.json');
return defineConfig({
optimizeDeps: {
include: Object.keys(pkg.dependencies)
},
server: {
port: parseInt(params['port'], 10) || 3000,
host: true,
https: params['https'],
proxy: {},
open: true,
fs: {strict: true},
hmr: {
overlay: false
}
},
});
2. 测试 vite 过程中遇到的问题
1. 执行 yarn start 后项目是跑起来了,但是控制台有 Failed to load source map
字样的日志
5:58:03 PM [vite] Failed to load source map for /@fs/xxx/node_modules/.vite/vite/hoist-non-react-statics.js?v=684d5287.
5:58:03 PM [vite] Failed to load source map for /@fs/xxx/node_modules/.vite/vite/antd.js?v=684d5287.
Sourcemap for "/xxx/node_modules/react-router-dom/index.js" points to missing source files
Sourcemap for "/xxx/node_modules/react-router/index.js" points to missing source files
Sourcemap for "/xxx/node_modules/async-validator/dist-web/index.js" points to missing source files
return defineConfig({
optimizeDeps: {
include: Object.keys(pkg.dependencies); // 这里为了方便将所有 dependencies 的包都加上了
},
server: {
// ...
},
});