小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金
Vite 从入门到精通 | 高级应用
本文总结 Vite 中的高级应用, 包括 HMR 热更新功能, glob-import批量导入功能,预编译优化提升 Vite 性能,在非 Node 服务中集成 Vite,Vite 中对 SSR 的使用,静态站点导出,Vite 配置项总结等应用
Vite 中的 HRM 热更新
通过 vanilla 项目创建 HRM
创建项目
$ npm init @vitejs/app
✔ Project name: … vite-project
✔ Select a framework: › vanilla
✔ Select a variant: › vanilla
实现单文件HMR 热更新
import './style.css'
export function render () {
document.querySelector('#app').innerHTML = `
<h1>Hello Vite!</h1>
<a href="https://vitejs.dev/guide/features.html" target="_blank">Documentation</a>
`
}
render()
// 集成 HMR
// vite build 时 import.meta.hot 是不存在的, 所以加开发环境的判断条件
// 常规范式: 在开发环境中设置 HRM 热更新
if (import.meta.hot) {
// 接收热更新
// newModule 对应的当前文件
// Vite 接收到文件更新后, 执行这个方法 重新 render
import.meta.hot.accept((newModule) => {
newModule.render();
})
}
Vite 中的 glob-import 批量导入功能
glob-import: 可以使用正则表达式, 引入一组的文件
通过 vite-vue3 项目实操 glob-import
创建项目
$ npm init @vitejs/app
✔ Project name: … vite-project
✔ Select a framework: › vue
✔ Select a variant: › vue
$ cd vite-project
$ yarn
$ yarn dev
准备文件
在 src 目录下创建 glob 文件夹 并创建 文件
cd src
mkdir glob
touch a.js a.json b.js b.json test-1.js
# 文件内容见源码
# https://github.com/xujiantong/vite-senior-apply/tree/feat/glob
在 mian.js 中一键导入 glob 下面的所有文件
导入所有文件
// main.js
import { createApp } from 'vue'
import App from './App.vue'
const globModules = import.meta.glob("./glob/*")
console.log(globModules)
createApp(App).mount('#app')
使用文件里面的数据
import { createApp } from 'vue'
import App from './App.vue'
const globModules = import.meta.glob("./glob/*")
console.log(globModules)
// 查看 Key Value
Object.entries(globModules).forEach(([k,v]) => {
console.log(k+ ":" + v)
v().then(m => console.log(k + ":" ,m.default))
})
createApp(App).mount('#app')
应用场景
多语言
只引入 JS
const globModules = import.meta.glob("./glob/*.js")
只想引入 json 文件
const globModules = import.meta.glob("./glob/*.json")
只想引入指定正则的文件
const globModules = import.meta.glob("./glob/*-[0-9].json")
Glob-import 是 Vite 提供的功能
Vite 中的 预编译
对于 node_modules 中安装的第三方的库, Vite 在第一次启动之前会去把我们所依赖的这些包进行编译放在 Cache 里面, 之后程序用到回去 Cache 里面去取。
/node_modules/.vite
-
CommonJS to ESM: 非 ESM 的文件会被编译成 ESM
- 在开发过程中, Vite 是依赖于浏览器原生的 ESM 加载方式去加载文件的
-
可配置 vite.config.js 指定哪些是需要预编译的
// vite.config.js
export default defineConfig({
plugins: [vue()],
optimizeDeps: {
exclude: ["react"],
include: ["vue"],
}
})
- Bundle files together
非 Nodejs 服务集成 Vite
非 Nodejs 服务:
javagopython...
-
模板引擎使用 Vite
-
引入路径问题
模板引擎增加标签 Dev
<script type="module" src="http://localhost:3000/@vite/client"> </script>
<script type="module" src="http://localhost:3000/src/main.js"></script>
解决路径问题 dev
服务端设置静态目录路径映射
打包生产时如何处理?
// vite.config.js
build :{
manifest: true
}
Server 解析 manifest.json
将 index , vendor , css 输出到模板
在模板上接收变量
在 Nodejs 中如何集成 Vite
基础版
yarn add express
// server.js
const express = require("express")
const app = express()
const { createServer: createViteServer} = require("vite")
createViteServer({server: {
middlewareMode: 'html', // html: vite dev server, ssr:
}}).then((vite) => {
app.use(vite.middlewares)
app.listen(4000)
})
服务端渲染 SSR
开发环境 Dev
const express = require("express")
const fs = require("fs")
const app = express()
const { createServer: createViteServer} = require("vite")
createViteServer({server: {
middlewareMode: 'ssr', // html: vite dev server, ssr:
}}).then((vite) => {
app.use(vite.middlewares)
app.get("*", async (req,res)=> {
let template = fs.readFileSync('index.html', 'utf-8')
template = await vite.transformIndexHtml(req.url, template)
const {render} = await vite.ssrLoadModule('/src/server-entry.jsx')
const html = await render(req.url)
const responseHtml = template.replace("<!--APP_HTML-->", html)
res.set("content-type", "text/html").send(responseHtml)
})
app.listen(4000)
})
Vite Config
// vim vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// 引入 jsx 依赖
import vueJsx from '@vitejs/plugin-vue-jsx'
// https://vitejs.dev/config/
export default defineConfig({
root: "", // index.html 存在的位置
base: "./",// 指定请求资源路径(URL)的前缀, 默认 ./
mode: "development", // 命令行启动时 --mode 同理, 指定 env, 默认 "development"(开发模式) "production" (生产模式)
define: "", // ≈ rollup 的 definePlugin, 定义全局常量的替换方式
plugins: [ // 使用插件
vue(),
vueJsx(),// 使用 vue-jsx
],
publicDir:"", //静态资源存放目录
cacheDir: "", // 开发1时用到的缓存存放目录, 默认"node_modules/.vite"
resolve: {
alias: {},// 别名,路径映射: "@styles": "/src/styles"
dedupe:[], // 如果存在相同依赖的副本, 比如安装了 vue2, vue3, 用该属性来制定最终使用哪一个依赖
conditions: [], // 解决程序包中 情景导出 时的其他允许条件
mainFields: [], // package.json 中,在解析包的入口点时尝试的字段列表。注意:这比从 exports 字段解析的情景导出优先级低:如果一个入口点从 exports 成功解析,resolve.mainFields 将被忽略。
extensions: [], // 导入时想要省略的扩展名列表。不 建议忽略自定义导入类型的扩展名(例如:.vue),因为它会影响 IDE 和类型支持。
},
css: {
modules:[], // 配置 CSS modules 的行为
postcss: "", // 内联的 PostCSS 配置(格式同 postcss.config.js)
preprocessorOptions:{ //指定传递给 CSS 预处理器的选项
scss: {
additionalData: `$injectedColor: orange;`
}
},
},
json: {
namedExports:true, // 是否支持从 .json 文件中进行按名导入。
// json 文件很大的时候建议开启~
stringify: false,// 若设置为 true,导入的 JSON 会被转换为 export default JSON.parse("..."),这样会比转译成对象字面量性能更好,尤其是当 JSON 文件较大的时候。开启此项,则会禁用按名导入。
},
esbuild: {}, // ESbuild 转换选项
assetsInclude: {
assetsInclude: ['**/*.txt']
}, // 指定额外的 picomatch 模式 作为静态资源处理, 比如我想 import 一个 txt 文件
logLevel: 'info', // 打印日志级别
clearScreen: true,
envDir: "", //.env 文件存放目录
build: {
target: 'modules', // 设置最终构建的浏览器兼容目标
polyfillModulePreload: true, //用于决定是否自动注入 module preload 的 polyfill.
outDir: "dist",//指定输出路径
assetsDir: "assets",// 静态资源存放的路径
assetsInlineLimit: "4096", // 4kb, 小于此阈值的导入或引用资源将内联为 base64 编码,以避免额外的 http 请求。设置为 0 可以完全禁用此项。
cssCodeSplit: true, // css 文件拆分
sourcemap: 'hidden', // 构建后是否生成 source map 文件。
rollupOptions:{},
commonjsOptions:{},
dynamicImportVarsOptions:{},
lib:{},
manifest: false, // 当设置为 true,构建后将会生成 manifest.json 文件 包含了没有被 hash 的资源文件名和 hash 后版本的映射。可以为一些服务器框架渲染时提供正确的资源引入链接。
ssrManifest: false,
minify: 'esbuild',
terserOptions: {},
write: true, //设置为 false 来禁用将构建后的文件写入磁盘。这常用于 编程式地调用 build() 在写入磁盘之前,需要对构建后的文件进行进一步处理。
emptyOutDir: true, // 构建时是否先清空 dist
brotliSize: true, // 构建后压缩报告
chunkSizeWarningLimit: 500,// 压缩超过 500k 提醒
watch: null, //设置为 {} 则会启用 rollup 的监听器。在涉及只用在构建时的插件时和集成开发流程中很常用。
// 依赖优化项
optimizeDeps: {
entries: "",
include: [],
exclude: [],
keepNames: false, // true 重命名符号避免冲突
}
}
})