Skip to main content

vite

info

vite主要由两部分组成:

  • 一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。

  • 一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。

使用pnpm+vite搭建react+typescript工程

1.使用pnpm create vite

选择React、TypeScript,SWC熟练的话可以选择TypeScript+SWC

2.ts配置文件

生成的tsconfig.jsontsconfig.node.json配置文件如下:

tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,

/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
tsconfig.node.json
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}
  1. 其中"moduleResolution": "bundler"会导致使用<div>等标签时ts报错:JSX element implicitly has type 'any' because no interface 'JSX.IntrinsicElements' exists.,改为"moduleResolution": "node"。(注意:tsconfig.node.json中的"moduleResolution": "bundler"也需要改为"moduleResolution": "node")

  2. 由于allowImportingTsExtensions配置项需要"moduleResolution": "bundler"并且需要配置noEmitemitDeclarationOnlyRequires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set.,所以去掉"allowImportingTsExtensions": true,

  3. import React from 'react';import ReactDOM from 'react-dom/client';这种import引入会ts报错:This module is declared with 'export =', and can only be used with a default import when using the 'allowSyntheticDefaultImports' flag.,在tsconfig.json中设置"allowSyntheticDefaultImports": true来解决

  4. import App from './App.tsx'ts报错:An import path cannot end with a '.tsx' extension. Consider importing './App.js' instead.,改为import App from './App'来解决

3.安装依赖并启动

使用 pnpm install 安装依赖,使用 pnpm dev 尝试启动:

  1. 启动有报错:UnhandledPromiseRejectionWarning: SyntaxError: Unexpected token '??=' error 这个报错是node版本比较低,node -v查看是14.14.0

  2. 使用nvm切换node版本为16.16.0后执行 pnpm dev 启动还报错:Error: Cannot find module @rollup/rollup-darwin-arm64.

  3. 使用rimraf node_modules删除node_modules后,重新执行 pnpm install 安装依赖,再次执行 pnpm dev 启动成功

4.设置打包后的目录

vite.config.ts
  build: {
outDir: 'afterSales',
},

5.使用scss

vite本身支持scss pnpm add -D sass后就可以使用scss

6.使用react router

引入react-router-dom:pnpm add react-router-dom

7.设置react页面的title

可以用useEffect

useEffect(() => {
document.title = '页面title'
})

8.vite配置项目别名

vite中alias别名配置

  • 使用到path__dirname__filename等,需要pnpm add -D @types/node

  • vite配置文件 和 ts配置文件都需要设置

vite.config.ts
import path from "path";

resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
"@pages": path.resolve(__dirname, "src/pages"),
},
},
tsconfig.json
"baseUrl": "./",
"paths":{
"@/*": ["src/*"],
"@pages/*": ["src/pages/*"],
},

9.vite的base 和 react router的basename

如果项目在生产环境想共用已有域名,则可以配置nginx,并相应设置vite打包的base及react router的basename。比如,已有域名aaa.com,项目在生产环境想使用aaa.com/b:

  • nginx配置
server {
listen 80;
server_name aaa.com;

root /export/App/dist;

index index.html;

location / {
root /export/App/dist;
if ($request_filename ~* .*\.(?:htm|html)$)
{
add_header Cache-Control "no-store";
}
add_header Cache-Control "no-cache";
}

location /b/ {
root /export/App/dist/;
try_files $uri $uri/ /b/index.html;
if ($request_filename ~* .*\.(?:htm|html)$)
{
add_header Cache-Control "no-store";
}
add_header Cache-Control "no-cache";
}
}
  • vite配置打包的base
vite.config.ts
base: '/b/',
  • react router 配置basename
src/main.tsx
const router = createBrowserRouter([
{
path: "/",
element: <Manage />,
},
], {
basename: '/b',
});

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>,
)

10.vite配置环境变量

环境变量和模式

  • 可以新建配置文件.env.staging.env.production

    .env.staging
    # staging(预发布)模式
    VITE_NODE_ENV=prepare
    .env.production
    # production(生产)模式
    VITE_NODE_ENV=production
  • 创建相应的script命令

    package.json
    "scripts": {
    "dev": "vite",
    "pre": "vite --mode staging",
    "prod": "vite --mode production",
    "build": "tsc && vite build",
    "buildPre": "tsc && vite build --mode staging",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
    },
  • 文件中使用:import.meta.env.VITE_NODE_ENV

11.vite开发配置开启https

  • 如下配置,浏览器访问时提示“使用了不受支持的协议”,这是因为https协议需要一个合法可用的证书。

    vite.config.ts
    server: {
    host: 'test.jd.com',
    port: 443,
    strictPort: true,
    https: true,
    },
  • 本地开发时,可以添加 @vitejs/plugin-basic-ssl 到项目插件中,它会自动创建和缓存一个自签名的证书。

    pnpm add -D @vitejs/plugin-basic-ssl
    vite.config.ts
    import basicSsl from '@vitejs/plugin-basic-ssl'

    export default {
    plugins: [
    basicSsl()
    ],
    server: {
    host: 'test.com',
    port: 443,
    strictPort: true,
    https: true, // 不设置也行,@vitejs/plugin-basic-ssl 插件会强制开启https
    },
    }

配置项

  • root 项目根目录(index.html 文件所在的位置)。可以是一个绝对路径,或者一个相对于该配置文件本身的相对路径。默认为process.cwd()

  • mode 默认为'development' 用于开发,'production' 用于构建

  • logLevel 调整控制台输出的级别,默认为 'info'。值可以是'info' | 'warn' | 'error' | 'silent'

  • define 定义全局常量替换方式。

构建配置项

  • build.cssTarget 此选项允许用户为 CSS 的压缩设置一个不同的浏览器 target,此处的 target 并非是用于 JavaScript 转写目标。默认值与 build.target 一致

  • build.cssCodeSplit 默认为true,即启用 CSS 代码拆分,在异步 chunk 中导入的 CSS 将内联到异步 chunk 本身,并在其被加载时一并获取。

  • build.lib 构建为库。entry 是必需的,因为库不能使用 HTML 作为入口。name 则是暴露的全局变量,并且在 formats 包含 'umd' 或 'iife' 时是必需的。默认 formats['es', 'umd'],如果使用了多个配置入口,则是 ['es', 'cjs']fileName 是输出的包文件名,默认 fileNamepackage.jsonname 选项

    warning

    如果指定了 build.libbuild.cssCodeSplit 会默认为 false,即禁用 CSS 代码拆分,则整个项目中的所有 CSS 将被提取到一个 CSS 文件中。

  • build.rollupOptions 自定义底层的 Rollup 打包配置。这与从 Rollup 配置文件导出的选项相同,并将与 Vite 的内部 Rollup 选项合并。(在 build 阶段,Vite 是利用 Rollup 去完成构建,整个过程只需要调用 Rollup 提供的 JS API 即可,整个过程中,Vite 的工作只是在做配置的转换,把 Vite 的配置转换成 Rollup 的 input 和 output 配置。)

    • build.rollupOptions.external 不想打包进你开发的库的依赖
  • build.minify 设置为 false 可以禁用最小化混淆,或是用来指定使用哪种混淆器。默认为 Esbuild,它比 terser 快 20-40 倍,压缩率只差 1%-2%。注意,在 lib 模式下使用 'es' 时,build.minify 选项不会缩减空格,因为会移除掉 pure 标注,导致破坏 tree-shaking。

  • build.emptyOutDiroutDir 在 root 目录(默认为process.cwd())下,则 该值默认为true,Vite 会在构建时清空该目录,设为false则不清空。若 outDir 在根目录之外则会抛出一个警告避免意外删除掉重要的文件。可以设置该选项来关闭这个警告。

环境变量与模式

默认情况下,开发服务器 (dev 命令) 运行在 development (开发) 模式,而 build 命令则运行在 production (生产) 模式。

这意味着当执行 vite build 时,它会自动加载 .env.production 中可能存在的环境变量。

缓存

Vite 会将预构建的依赖缓存到 node_modules/.vite。它根据几个源来决定是否需要重新运行预构建步骤:

  • package.json 中的 dependencies 列表
  • 包管理器的 lockfile,例如 package-lock.json, yarn.lock,或者 pnpm-lock.yaml
  • 可能在 vite.config.js 相关字段中配置过的

只有在上述其中一项发生更改时,才需要重新运行预构建。

如果出于某些原因,你想要强制 Vite 重新构建依赖,你可以用 --force 命令行选项启动开发服务器,或者手动删除 node_modules/.vite 目录。

如果需要调试node_modules中的依赖包,则依赖包改动内容后,执行vite dev --force后改动就能生效

JavaScript API

build

import path from 'path'
import { fileURLToPath } from 'url'
import { build } from 'vite'

const __dirname = fileURLToPath(new URL('.', import.meta.url))

;(async () => {
await build({
root: path.resolve(__dirname, './project'),
base: '/foo/',
build: {
rollupOptions: {
// ...
},
},
})
})()

使用记录

h5页面在低版本安卓浏览器白屏

vite4+react18+ts5开发h5页面在低版本安卓(<=安卓10及鸿蒙系统)浏览器中打开出现白屏。用云真机webview调试发现没有任何报错。

查看vite对浏览器的支持浏览器兼容性

用于生产环境的构建包会假设目标浏览器支持现代 JavaScript 语法。默认情况下,Vite 的目标是能够 `支持原生 ESM script 标签``支持原生 ESM 动态导入``import.meta` 的浏览器:
- Chrome >=87
- Firefox >=78
- Safari >=14
- Edge >=88

你也可以通过 `build.target` 配置项 指定构建目标,最低支持 `es2015`

请注意,默认情况下 Vite 只处理语法转译,且 不包含任何 polyfill。你可以前往 [Polyfill.io](https://polyfill.io/) 查看,这是一个基于用户浏览器 User-Agent 字符串自动生成 polyfill 包的服务。

传统浏览器可以通过插件 `@vitejs/plugin-legacy` 来支持,它将自动生成传统版本的 chunk 及与其相对应 ES 语言特性方面的 polyfill。兼容版的 chunk 只会在不支持原生 ESM 的浏览器中进行按需加载。

所以,使用@vitejs/plugin-legacy来尝试解决:

  • 安装@vitejs/plugin-legacy,必须同时安装terser,因为该插件使用terser进行压缩

    pnpm add @vitejs/plugin-legacy -D
    pnpm add terser -D
  • 配置

    vite.config.ts
    import legacy from '@vitejs/plugin-legacy';

    export default defineConfig({
    plugins: [
    legacy({
    targets: [
    '> 0%',
    'Chrome > 4',
    'Android >= 4',
    'IOS >= 7',
    'not ie <= 6',
    'Firefox ESR',
    ],
    }),
    ]
    })

打包部署后在低版本安卓(<=安卓10及鸿蒙系统)浏览器中打开页面正常了,但是在某些app的webview中打开会报错,提示replaceAll is not a function,使用replace替换replaceAll进行解决:

const str = ' A   B   C    D ';
console.log(str.replaceAll(' ', '+')); // +A+++B+++C++++D+
console.log(str.replace(/ /g, '+')); // +A+++B+++C++++D+

使用proxy报错

vite.config.ts
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
}
}
})

如上使用报错了:react.js:1 Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "application/json". Strict MIME type checking is enforced for module scripts per HTML spec.

使用正则表达式写法则不报错:

vite.config.ts
export default defineConfig({
server: {
proxy: {
'^/api/.*': {
target: 'http://jsonplaceholder.typicode.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
}
}
})