阅读 2091

Vite实战指北(vite中文文档)

Vite是一个开源的前端构建工具,起初只支持对Vue的打包构建(毕竟由尤雨溪牵头开发),但到了目前的2.0版本,Vite已经实现了所有项目框架的打包构建,Vite以几乎无法察觉的编译速度无须重新刷新页面即可得到结果的优势被誉为下一代的构建工具。

为什么Vite这么快?

Vite做了很多避免重复编译的事情,比如类库缓存、热更新无须刷新页面、多文件束成一个文件节省API请求等,而且相较于传统的打包构建工具(如 Webpack)先打包构建再启动开发服务器,Vite 巧妙地利用了浏览器对 ESM 的支持,先启动开发服务器,当代码执行到模块加载时再请求对应模块的文件,直接砍掉了开发构建这一步(生产环境使用Rollup打包)来看看下面的对比图:

Webpack

通过js入口去扫描这些应用引入的子模块,等模块全部被解析完后,其中包括一些动态模块也会被解析,然后把这些模块注入到index.js中最后启动devserve等待访问。缺点是项目越来越大启动时间就会越来越长。但其实很多时候首屏所使用的js都是比较少的,所以我们其实不需要把全部都打包进来。

在这里插入图片描述

Vite

启动DevServe服务,利用ESModule会以请求的方式加载模块,而因为首屏只有少量的js模块,而且动态的模块是不会被加载进来的(浏览器随取随用),所以这就是比Webpack打包启动要快很多很多的原因。

在这里插入图片描述

如何使用Vite

Vite的极其轻量甚至可以用于老项目,但我们先用Vite构建一个新的项目:

yarn create vite 复制代码

接下来进行一些自定义配置:

  • Project name: 项目的名称,默认使用vite-project

  • Select a framework: 选择框架

    • vanilla: 一个纯净的js网站项目

    • vue: vue项目(仅支持3.0)

    • react: react项目

    • 也支持一些其他框架供选择,但在这里不多赘述。

  • Select a variant: 选择是否为TS

是的,你没有看错,这样就完成了一个Vite项目的搭建。以下是结构图:

在这里插入图片描述

Vite的入口文件有别于其他,它的入口文件是index.html,这也是因为它不必去重新刷新页面。vite.config.ts就是vite的配置文件了。你可以看到vite的依赖的是一个个的插件,这也是为什么Vite特别轻量级的原因。

CSS处理

vite 原生支持 css variable,并且支持 css module(在 css 文件增加后缀.module 即可),pre-process

配置TSX模板

我们现在安装一个插件使其支持TSX模板,这里不使用React做示范是因为Vue+TSX已经覆盖了React的配置指引。

yarn add @vitejs/plugin-vue-jsx 复制代码

再配置到文件里:

import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' export default defineConfig({   plugins: [vue(), vueJsx()] }) 复制代码

这样我们就可以使用TSX模板了

配置TS文件

Vite原生支持TS,但这里我们有针对Vite的一些配置,React可以略过第一条配置。

  1. "jsx": "preserve":不允许TS代替编译jsx/tsx文件,因为vue的格式不适用于常规jsx/tsx模板,该项将留给后续插件编译

  2. "isolatedModules": true:为了防止使用vite时一些不必要的麻烦。

    • vite在编译文件时,export只会在本文件中查找,而不会在其他文件中查找。

    • 在声明一个常量枚举时,ts会帮你做常量替换然后删除该常量,但是vite不会识别这种语法,编译时就会把常量枚举删除掉,从而导致替换时找不到变量进而报错的问题。

    • vite不会编译没有import也没有export的ts文件。

  3. "types": ["vite/client"]:增加vite内部API的types,比如允许ts文件导入一个图片

{   "compilerOptions": {     "target": "esnext",     "module": "esnext",     "moduleResolution": "node",     "strict": true,     "jsx": "preserve",     "sourceMap": true,     "resolveJsonModule": true,     "esModuleInterop": true, // 提供整合能力 例如 import * as react from 'react' -> import react from 'react'     "lib": ["ESNext", "DOM"],     "isolatedModules": true,     "types": ["vite/client"]   },   "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/main.js"], } 复制代码

注意如果你使用的是vue-sfc模板,需要额外安装一个vue-tsc插件,它可以帮你将vue模板中的ts代码得到解析,然后在修改一下脚本:

"build": "vue-tsc -- noEmit && tsc --noEmit && vite build" 复制代码

静态资源处理

vite做了一系列的静态资源处理,这里介绍一下使用

// 将其视为一个静态资源,而不是作为js/ts文件导入 import testImg from './testImg?url' // 将其视为一个静态文件,并且将其内部的内容以字符串形式打印 import test from './test?raw' // 将其视为一个静态文件,并且视为一个Web Worker import Worker from './worker?worker' const worker = new Worker() worker.onmessage = (event) => console.log(event) 复制代码

配置Eslint

由于vite中没有帮我们引入eslint代码校验,所以我们需要自己手动配置eslint

yarn add eslint eslint-config-standard eslint-plugin-import eslint-plugin-node eslint-plugin-promise  eslint-plugin-vue @typescript-eslint/parser @typescript-eslint/eslint-plugin -D 复制代码

PS:如果你没有打算使用TS,那么不必安装最后两项插件,eslint针对vue语法校验的插件是eslint-plugin-vue,react是eslint-plugin-react,如果使用hooks那么再安装eslint-plugin-react-hooks即可

建立.eslintrc.js

module.exports = {   parser: '@typescript-eslint/parser',   extends: ['standard', 'plugin:@typescript-eslint/recommended'],   plugins: ['@typescript-eslint', 'vue'],   env: {     node: true   } } 复制代码

这样 eslint 就会生效了,不使用TS的情况下只需要这样:

module.exports = {   extends: ['standard'] } 复制代码

但添加了eslint校验,只会在编辑器中提示,如果我们想要在每次commit时做一遍校验,可以使用husky钩子,具体这里不做详细说明。

Vite的环境变量

我们可以在Vite中非常细粒化的配置环境变量,但在此之前容我先介绍一下Vite的默认变量

import.meta.env.DEV // 是否为开发环境 import.meta.env.PROD // 是否为生产环境 import.meta.env.MODE // 当前的环境,默认只有development和production,后续会介绍如何自定义 import.meta.env.SSR // 是否为服务端渲染 import.meta.env.BASE_URL // 基础路径 复制代码

自定义环境变量

  1. 创建.env 文件

  • vite 会将.env 文件中的所有环境变量添加进来,前缀需要是 VITE,譬如 VITE_API='github.com'。

  • 可以根据 mode 创建更多的 env 文件,譬如.env.production 则只有 mode 为 production 时才会使用。在 script 后可以添加--mode 参数来自定义 mode 环境。

  • 可以在不同机器上的相同环境下进行配置,譬如.env.production.local 则只会在本地的 production 环境下生效。

  1. 为自定义环境变量添加类型注释

在 src 目录下创建 vite-env.d.ts

/// <reference types="vite/client" /> interface ImportMetaEnv {   VITE_API: string } 复制代码

Vite热更新

vite 在很多插件中其实已经实现了热更新,但如果是使用原生 js 或其他种种原因没有使用 vite 插件,则需要我们自己配置热更新。或者你需要做某些事情要在热更新时。

export function render() {   document.querySelector('#app').innerHTML(`     <div>Hello Vite!</div>     <a href="#">This is a web project without the modern framework</a>   `) } // 因为线上不支持热更新,所以需要提前判断一下 if (import.meta.hot) {   // 当监听到热更新钩子时,手动调用一次   import.meta.hot.accept((newModule) => {     newModule.render()   }) } 复制代码

glob import

也许你已经在之前的项目使用过Webpack的一次性引入所有文件毋须再自己引入router、store文件了,但我在这里介绍一下vite的使用方式,最后还会介绍一个更快的引入方式

// 引入一个文件夹下的所有文件 const moduleFiles = import.meta.glob('./modules/*.ts') const modules = modulesFiles.keys().reduce((modules, modulePath) => {   // 将 './app.js' => 'app'   const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')   const value = modulesFiles[modulePath]   modules[moduleName] = value   return modules }, {}) 复制代码

直接将代码引入进来,不再需要异步引入

const modulesFiles = import.meta.globEager('./modules/*.ts') 复制代码

Vite预编译

Vite预编译可谓是在快的基础上更快了一步,它会将所有你用到的类库做一个cache缓存,比如第一次启动时编译会进行缓存,之后再去使用直接从缓存中取就可以,避免了不必要的编译环节。 Vite预编译也做了多文件合并的操作,避免过多的请求次数,例如lodash;同时也会将所有的CommonJS转编译成ESModule。

当然你也可以配置自己是否需要预编译,不过一般不用过于担心,Vite会帮你做好一切。

optimizeDeps: {   include: ['vue'],   exclude: ['react'] } 复制代码

Vite常用配置

别名

resolve: {   alias: {     '@': '/src'   } } 复制代码

代理

server: { port: 3000, // 默认就是3000 open: true, // 第一次启动时自动打开浏览器 proxy: { '/api': {          target: 'http://jsonplaceholder.typicode.com',          changeOrigin: true,          rewrite: (path) => path.replace(/^\/api/, '')        } } } 复制代码

详细配置请见官网

Vite SSR渲染

非node.js服务

当后端项目是由非 node.js 服务支持且需要前端项目嵌入到后端项目中(例如 JSP 等模板技术,需要将 index.html 嵌入进去),这里由 pug 模板举例:

开发中

html     head         title= title     body         h1=message         div(id="app")         script(src="http://localhost:3000/@vite/client" type="module")         script(src="http://localhost:3000/src/main.js" type="module") 复制代码

生产打包

当到了生产环境打包上线时,需要在 vite 配置文件中允许打包出一个 manifest.json 文件

build: {   manifest: true } 复制代码

这样的话打包完成之后就会生成一个 manifest.json 的描述文件,后端渲染模板需要引入 file, file.imports.vendor, file.css[0] 文件

html     head         title= title         link(href=css rel="stylesheet")     body         h1=message         div(id="app")         script(src=vendor)         script(src=index) 复制代码

node.js服务

这里先展示一个最小demo:

const express = require('express') const app = express() const { createServer } = require('vite') createServer({   server: {     middlewareMode: 'html' // 启动vite的dev server   } }).then((vite) => {   app.use(vite.middlewares)   app.listen(3000) }) 复制代码

这样就实现了热更新,但这种操作其实只是等于vite在背后为我们做的,真正需要配置的是SSR服务端渲染

使用服务端渲染SSR

这里我们使用React作为实例

const express = require('express') const fs = require('fs') const app = express() const { createServer } = require('vite') createServer({   server: {     middlewareMode: 'ssr'   } }).then((vite) => {   app.use(vite.middlewares)   app.get('*', async (req, res) => {     let template = res.send(fs.readFileSync('index.html', 'utf-8'))     template = await vite.transformIndexHtml(req.url, template) // 需要vite处理一下template模板     const context = {}     const { render } = await vite.ssrLoadModule('./server-entry.js')     const html = await render(req.url, context) // 渲染出html的内容     // 在<div id="app"></div>中添加注释 然后通过该注释替换真实的模板内容     const responseHtml = template.replace('<!--APP_HTML-->', html)     res.set('content-type', 'text/html')     res.send(responseHtml)   })   app.listen(3000) }) 复制代码

一般来讲,SSR 渲染需要一个入口配置文件来将浏览器渲染模式更改为后端渲染模式,很少有情况会出现不需要这个兼容文件的情况,比如 router 只能在浏览器模式使用,后端渲染模式是不支持的。

server-entry.js

import ReactDOMServer from 'react-dom/server' import { StaticRouter } from 'react-router-dom' import { APP } from './App' export function render(url, context) {   return ReactDomServer.renderToString(     <StaticRouter location={url} context={context}>       <App />     </StaticRouter>   ) } 复制代码

打包SSR渲染

先配置两条命令:

"build:client": "vite build --outDir dist/client", "build:server": "vite build --outDir dist/server --ssr server-entry.js", 复制代码

一条是打包项目命令,一条是打包 server 命令,接下来开始配置 server

const express = require('express') const fs = require('fs') const app = express() const template = fs.readFileSync('dist/client/index.html', 'utf-8') // 映射静态文件 app.use(express.static('dist/client')) app.get('*', async (req, res) => {   const render = require('dist/server/server-entry.js').render()   const context = {}   const html = await render(req.url, context)   // 如果上下文存在url,表示需要重定向   if (context.url) {     res.redirect(301, context.url)     return   }   const responseHtml = template.replace('<!--APP_HTML-->', html)   res.set('content-type', 'text/html')   res.send(responseHtml) }) app.listen(3000) 复制代码

使用SSR实现静态站点导出

我们这里只是举例一个最小demo,真正的配置绝对会比这复杂很多很多倍,Vite在这方面的态度是希望有其他服务来基于Vite开发,如果你真的有这类需求,那么只能暂时自己开发了。

const fs = require('fs') const template = fs.readFileSync('dist/client/index.html', 'utf-8') const render = require('dist/server/server-entry').render() // 拿到page文件夹下的所有文件名称 const routesToRender = fs   .readdirSync('src/pages')   .map((file) => file.replace('.jsx', '').toLowerCase()) for (const route of routesToRender) {   const context = {}   const html = render(route, context)   const responseHtml = template.replace('<!--APP_HTML-->', html)   const filePath = `dist/static/${route}.html`   fs.writeFileSync(filePath, responseHtml) }


作者:linkscope
链接:https://juejin.cn/post/7039159279042953252


文章分类
代码人生
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐