Vite从0到1

2022-11-19  本文已影响0人  麦西的西

vite 初识

创建一个 vite 项目,只需要:

yarn create vite

然后按照提示进行操作即可(这里选的是 react + js)。生成的项目目录如下:

├─ public
│ └─ vite.svg
├─ src
│ ├─ assets
│ │ └─ react.svg
│ ├─ App.css
│ ├─ App.jsx
│ ├─ index.css
│ └─ main.jsx
├─ index.html
├─ package.json
└─ vite.config.js

这个命令先是安装一个全局依赖 create-vite,然后运行 create-vite命令。等同于:

cnpm install create-vite -g && create-vite

create-vite 就是一个 vite 的脚手架,会根据你的需要选择不同模板来克隆项目。
需要注意的是, vite 对 node 版本有要求, 要求 node 版本是 ^14.18.0 || >=16.0.0

可以看到根目录有个 index.html。这个是 vite 项目的入口文件。
Vite 解析 <script type="module" src="..."> ,这个标签指向我们的 JavaScript 源码。

为什么选择 vite

Vite 是一种新型前端构建工具,能够 显著提升 前端开发体验
为什么能够显著提升开发体验呢?首先我们了解结构建工具做了哪些工作。

1. 传统构建工具所做的工作(自动化)

这样,我们就不用管理代码如何处理,如何在浏览器运行,只需要关注开发工作即可。

目前的构建工具,通常是这个流程:从入口构建依赖图 => 对所有模块打包 => 浏览器运行。如下图:


当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。包含数千个模块的大型项目相当普遍。基于 JavaScript 开发的工具就会开始遇到性能瓶颈:通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用模块热替换(HMR),文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。
Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写。

2.vite 的设计理念

2.1 开发服务器

Vite 通过在一开始将应用中的模块区分为 依赖源码 两类,改进了开发服务器启动时间。

Vite 以 原生 ESM 方式提供源码。这实际上是让浏览器接管了打包程序的部分工作:Vite 只需要在浏览器请求源码时进行转换并按需提供源码。根据情景动态导入代码,即只在当前屏幕上实际使用时才会被处理。如下图:


2.2 热更新(HMR)

传统的 HMR:当我们对代码做修改并保存后,webpack 会对修改的代码块以及该模块的依赖重新编译打包,并将新的模块发送至浏览器端,浏览器用新的模块代替旧的模块,从而实现了在不刷新浏览器的前提下更新页面。相比起直接刷新页面的方案,HMR 的优点是可以保存应用的状态。当然,随着项目体积的增长,热更新的速度也会随之下降。
在 Vite 中,HMR 是在原生 ESM 上执行的。当编辑一个文件时,Vite 只需要精确地使已编辑的模块与其最近的 HMR 边界之间的链失活(大多数时候只是模块本身),使得无论应用大小如何,HMR 始终能保持快速更新。
Vite 同时利用 HTTP 头来加速整个页面的重新加载(再次让浏览器为我们做更多事情):源码模块的请求会根据 304 Not Modified 进行协商缓存,而依赖模块请求则会通过 Cache-Control: max-age=31536000,immutable 进行强缓存,因此一旦被缓存它们将不需要再次请求。

2.3 为什么生产环境仍需打包

尽管原生 ESM 现在得到了广泛支持,但由于嵌套导入会导致额外的网络往返,在生产环境中发布未打包的 ESM 仍然效率低下(即使使用 HTTP/2)。为了在生产环境中获得最佳的加载性能,最好还是将代码进行 tree-shaking、懒加载和 chunk 分割(以获得更好的缓存)。

依赖预构建

1. 为什么需要依赖预构建

我们看这样一个例子。创建下面的目录,并在 index.html 中以 module 的形式引入 main.js

prebuild-demo    
├─ index.html    
├─ main.js       
└─ package.json  
// index.html
// ...
<script type="module" src="./main.js"></script>
// ...

接着,我们cnpm install lodash -S 安装 lodash,并在 main.js 中写入:

// main.js
import { throttle } from 'lodash';
console.log(throttle);

然后,在浏览器打开 index.html,会报错:


这是因为 ESModule 中,相对引用要采用/, ./, 或 ../开头。因此,不能够通过依赖的方式直接引入。
依赖预构建,能够重写这部分模块引入,从而解决问题:
我们安装 vite,并用 vite 启动项目:
cnpm install vite && npx vite

打开控制台,throttle 已经能打印出来了。


再看 main.js ,模块引入变成了具体的地址:

除了依赖补全,依赖预构建还做了这两个工作:

2. 缓存

2.1 文件系统缓存

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

2.2 浏览器缓存

解析后的依赖请求会以 HTTP 头 max-age=31536000,immutable 强缓存,以提高在开发时的页面重载性能。一旦被缓存,这些请求将永远不会再到达开发服务器。如果安装了不同的版本(这反映在包管理器的 lockfile 中),则附加的版本 query 会自动使它们失效。如果你想通过本地编辑来调试依赖项,你可以:

  1. 通过浏览器调试工具的 Network 选项卡暂时禁用缓存;
  2. 重启 Vite dev server,并添加--force 命令以重新构建依赖;
  3. 重新载入页面。

常用功能与配置

1. CSS Modules

任何以 .module.css 为后缀名的 CSS 文件都被认为是一个 CSS modules 文件。导入这样的文件会返回一个相应的模块对象。
也就是说我们直接可以模块引入:

import styles from "./index.module.less";```

CSS Modules 比较常用的配置:

// ...
css: {
    modules: {
      generateScopedName: "[path][name]__[local]__[hash:5]",
      localsConvention: "camelCaseOnly"
    }
  },
  // ...

其中,

2. CSS 预处理器

vite 提供了对 sass/less/stylus 的内置支持。
我们只需要安装相应的预处理器依赖即可直接使用。

cnpm install less -D

比较常用的配置:

css: {
    // ...
    preprocessorOptions: {
      less: {
        additionalData: `@import '@/assets/styles/common.less';`, // 全局注入样式文件
        modifyVars: {
          'primary-color': '#409eff' // 全局样式变量
        },
        javascriptEnabled: true
      }
    },
    
    devSourcemap: true // 默认false,设为true开发阶段启动用sourcemap
  },

3. PostCSS

vite 提供了对 PostCSS 的内置支持。
我们可以在 vite.config.js 中配置 PostCSS ,也可以直接新建 postcss.config.js 文件配置 PostCSS 。
这里我们使用 postcss-preset-env 试一下,postcss-preset-env 包含一系列 PostCSS 的插件。比如浏览器前缀自动添加:

cnpm install postcss-preset-env -D

根目录新增 postcss.config.js ,并配置如下:

import postcssPresetEnv from 'postcss-preset-env';

export default {
  plugins: [postcssPresetEnv()]
};

重启开发服务器,就能够看到浏览器前缀自动添加了:


4. 静态资源处理

4.1 资源引入

vite 中,引入一个静态资源会返回解析后的公共路径:

import exampleImg from "/src/assets/example.png";

exampleImg 在开发时会是 /src/assets/example.png,生产环境会是 /assets/example.2d8efhg.png。类似于 webpack4 中的 file-loader.

export default defineConfig({
  assetsInclude: ['**/*.gltf'] // 会把.gltf文件当做资源文件处理
})
 // ...
 build: {
    assetsInlineLimit: 8 * 1024, // 小于 8 KB的资源会被内联成base64格式
    // ...
  },

也可以通过 ?raw 后缀声明作为字符串引入。类似于 webpack4 中的 raw-loader

import helloString from "./test.txt?raw";
console.log("exampleImg", helloString); // hello, vite
4.2 public 目录

如果你有下列这些资源:

5. alias 与 extensions

通过下面的代码配置别名和扩展名缩写:

import { resolve } from "path";
// ...
resolve: {
    alias: {
      "@": resolve(__dirname, "./src")
    },
    extensions: [".jsx", ".js", ".tsx", ".ts", ".json"]
},

6. 本地开发服务器

server: {
    open: true, // 自动打开浏览器
    host: "0.0.0.0",
    port: 9999,
    strictPort: true, // 设置为false,端口被占用会直接退出
    proxy: {
      "/webapi": {
        target: "http://10.2.2.98:8090",
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/webapi/, "")
      }
    }
  }

环境变量

1. 内建环境变量

Vite 在一个特殊的 import.meta.env 对象上暴露环境变量。这里有一些在所有情况下都可以使用的内建变量:

2. .env 文件

我们可以在 .env 文件中编写自己需要的环境变量。

.env                # 所有情况下都会加载
.env.local          # 所有情况下都会加载,但会被 git 忽略
.env.[mode]         # 只在指定模式下加载
.env.[mode].local   # 只在指定模式下加载,但会被 git 忽略

自己编写的环境变量必须以 VITE_ 为前缀,比如:

VITE_SOME_KEY=123
DB_PASSWORD=foobar // 不合法
console.log(import.meta.env.VITE_SOME_KEY) // 123
console.log(import.meta.env.DB_PASSWORD) // undefined

3. 模式

默认情况下,开发服务器 (dev 命令) 运行在 development (开发) 模式,而 build 命令则运行在 production (生产) 模式。
如果我们需要额外的模式,则可以使用 --mode 覆盖默认的模式:

vite build --mode staging

同时我们还需要一个 .env.staging 文件来定义环境变量:

# .env.staging
NODE_ENV=production
VITE_HTTP=http://10.2.2.245:8890

生产构建优化

1. 分包策略

生产环境打包的时候,我们可能会需要分包。比如:把依赖单独打一个包,这样就可以避免依赖被重复打包。

    build: {
      assetsInlineLimit: 8 * 1024, // 小于8KB的资源base64内联
      rollupOptions: {
        output: {
          manualChunks(id) {
            if (id.includes("node_modules")) {
              return "vendor";
            }
          }
        }
      }
    },

rollupOptions 里还能够配置打包生成的目录,一个常用的配置:

 build: {
      rollupOptions: {
        output: {
          // ...
          assetFileNames: (assetInfo) => {
            var info = assetInfo.name.split(".");
            var extType = info[info.length - 1];
            if (/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(assetInfo.name)) {
              extType = "media";
            } else if (/\.(png|jpe?g|gif|svg)(\?.*)?$/.test(assetInfo.name)) {
              extType = "img";
            } else if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(assetInfo.name)) {
              extType = "fonts";
            }
            return `static/${extType}/[name]-[hash][extname]`;
          },
          chunkFileNames: "static/js/[name]-[hash].js",
          entryFileNames: "static/js/[name]-[hash].js"
        }
      }
    },

打包后的目录:


2. 动态导入

动态导入(import 函数)是 ES6 的新特性。使用动态导入语法能够实现分包,进而实现懒加载。通常用于路由的懒加载。下面是一个例子。

// import { throttle } from 'lodash';
// console.log('object :>> throttle', throttle);

import('lodash').then(({ throttle }) => {
  console.log('object :>> throttle', throttle);
});

上面是直接导入,下面是动态导入。二者打包结果如下:


可以看出,下面的 lodash 已经自动分包了。

3. 图片压缩、gzip 压缩

通过 vite-plugin-imageminvite-plugin-compression 插件可以实现图片压缩与 gzip 压缩。用法也比较简单:

import compression from 'vite-plugin-compression';
import imagemin from 'vite-plugin-imagemin';

export default defineConfig({
  // ...
  plugins: [react(), compression(), imagemin()]
  // ...
});

4. CDN 优化(外网环境)

通过 vite-plugin-cdn-import 插件能够将一些依赖使用 cdn 加载,从而降低包的大小,加快依赖加载速度。用法如下:

import { Plugin as importToCDN } from 'vite-plugin-cdn-import';

export default defineConfig({
  plugins: [
    react(),
    importToCDN({
      modules: [
        {
          name: 'lodash',
          var: '_',
          path: 'https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js'
        }
      ]
    })
  ]
});

参考文档

Vite 官方文档: https://cn.vitejs.dev/guide/
Vite 和 webpack、rollup 打包工具对比:https://blog.csdn.net/Ambibibition/article/details/127766551
Vite世界指南(带你从0到1深入学习 vite):
https://www.bilibili.com/video/BV1GN4y1M7P5/?spm_id_from=333.999.0.0&vd_source=41e6d1d28e504860272fd13300cb250c

上一篇下一篇

猜你喜欢

热点阅读