Vite 从入门到精通 | 高级应用

3,398 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金

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();
   })
}

附源码: github.com/xujiantong/…

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')

image-20211002141032268.png

image-20211002142620208.png

使用文件里面的数据
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')

image-20211002142939902.png

应用场景

多语言

只引入 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 提供的功能

源码: github.com/xujiantong/…

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 服务:java go python ...

  • 模板引擎使用 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 重命名符号避免冲突
    }
  }
})