vite的使用

依赖预构建

为什么要执行依赖预构建?

1.预购建重写导入为合法的 URL,例如 /node_modules/.vite/deps/my-dep.js?v=f3sf2ebd 以便浏览器能够正确导入它们。

因为原生 ES 导入不支持下面这样的裸模块导入:

// 裸模块导入
import xxx from "vue"
import xxx from "vue/xxx"

// 以下不是裸模块导入
import xxx from "./foo.ts" 
import xxx from "/foo.ts" 

我们可以理解为直接根据模块名导入的模块就是裸模块导入,而根据路径导入的不是裸模块导入。在原生js中直接裸模块导入会报错:

2.在开发阶段中,Vite 的开发服务器将所有代码视为原生 ES 模块。因此,Vite 必须先将以 CommonJS 或 UMD 形式提供的依赖项转换为 ES 模块

3.为了提高后续页面的加载性能,Vite将那些具有许多内部模块的 ESM 依赖项转换为单个模块。

例如:lodash-es 有超过 600 个内置模块!当我们执行 import { debounce } from 'lodash-es' 时,浏览器同时发出 600 多个 HTTP 请求!即使服务器能够轻松处理它们,但大量请求会导致浏览器端的网络拥塞,使页面加载变得明显缓慢。

通过将 lodash-es 预构建成单个模块,现在我们只需要一个HTTP请求!

我们可以自己去引入这个模块中的包:然后查看浏览器中的network中的请求非常之多

import lodashES from '../node_modules/lodash-es/lodash.js'

 依赖预构建仅适用于开发模式,并使用 esbuild 将依赖项转换为 ES 模块。在生产构建中,将使用@rollup/plugin-commonjs。

我们可以通过optimizeDeps.excludeoptimizeDeps.include配置来配置我们的某个包是否被预构建

 预构建的内容是什么?

 一个项目中,存在非常多的模块,并不是所有模块都会被预构建。只有 bare module imports(裸模块导入)会执行依赖预构建,一般是 npm 安装的模块,是第三方的模块,不是我们自己写的代码,一般情况下是不会被修改的,因此对这部分的模块提前执行构建,有利于提升性能。注意:monorepo下的模块不会被预购建,会将该链接的依赖视为源码,也就是开发者自己写的代码。

找寻依赖的过程:在当前目录下的 node_modules 下寻找,找不到则往上一级目录的 node_modules,直到目录为根路径,不能再往上。

缓存

文件系统缓存

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

  • 包管理器的锁文件内容,例如 package-lock.json,yarn.lock,pnpm-lock.yaml,或者 bun.lockb;    
  • 补丁文件夹的修改时间;
  • vite.config.js 中的相关字段;例如:cacheDir
  • NODE_ENV 的值。

只有在上述其中一项发生更改时,才需要重新运行预构建。  如果出于某些原因你想要强制 Vite 重新构建依赖项,你可以在启动开发服务器时指定 --force 选项,或手动删除 node_modules/.vite 缓存目录。

浏览器缓存

已预构建的依赖请求使用 HTTP 头 max-age=31536000, immutable进行强缓存,以提高开发期间页面重新加载的性能。一旦被缓存,这些请求将永远不会再次访问开发服务器。

如果安装了不同版本的依赖项(这反映在包管理器的 lockfile 中),则会通过附加版本查询自动失效。如果你想通过本地编辑来调试依赖项,您可以:

  1. 通过浏览器开发工具的 Network 选项卡暂时禁用缓存;
  2. 重启 Vite 开发服务器指定 --force 选项,来重新构建依赖项;
  3. 重新载入页面。

vite的使用_第1张图片

模块热重载(HMR)

模块热替换(hot module replacement),在 webpack 的定义是:在应用程序运行过程中替换、添加或删除模块,而无需重新加载整个页面。vite内置了HMR到vue、react和Preact我们不需要手动设置这些 —— 当你通过 create-vite创建应用程序时,所选模板已经为你预先配置了这些。

接下来我们通过pnpm create vite 来创建一个vanilla项目,利用官网的api在该项目中实现HMR:import.meta.hot.accept这个方法会为当前模块执行热替换

// counter.js中
export const count = 3
// 判断当前的环境:生产环境中有这个api而开发环境不需要用到MHR,也就没有该api
if(import.meta.hot) {
  // newModule 为新模块(变化后的counter.js)的引用,当模块变化后就会执行accept中的这个回调
  import.meta.hot.accept((newModule) => {
    console.log(newModule.count);
  })
}


// main.js
import {count} from './counter.js'

当我们更改counter.js 中的 count 时保存后,页面并不会刷新,而是执行accept中的回调,并且控制台会有 hmr 的标识。

接下来我们更改counter.js中的代码如下:(main.js不变)

let mes = 'hello!!'
export let count = 5
setInterval(()=>{
  console.log(count++);
}, 1000)
if (import.meta.hot) {
  import.meta.hot.accept(newModule => {})
}

当我们运行该程序的时候控制台会一直打印count++,当我们更改mes的值后,会发现控制台会执行两个定时器,分别是新/旧模块的定时器,由此可见开启定时器是一个副作用。我们可以通过如下方式清除这个副作用:import.meta.hot.dispose方法用来清除副作用

let mes = 'hello!'
export let count = 5
let timer = setInterval(() => {
  console.log(count++)
}, 1000)
if (import.meta.hot) {
  import.meta.hot.accept(newModule => {})
  import.meta.hot.dispose((data) => {
    // 清除副作用
    if(timer) clearInterval(timer)
  })
}

这里只列举出来HMR一部分的api其他的api大家可以去官网查看。HMR API | Vite 官方中文文档

vite中的css使用

1.postcss

PostCSS是一个工具,里面包含了很多插件可以让我们更加方便书写css。官网链接:https://github.com/postcss/postcss#usage

如果项目包含有效的 PostCSS 配置 (任何受 postcss-load-config 支持的格式,例如 postcss.config.js),它将会自动应用于所有已导入的 CSS。

这里我是用 postcss-nested 插件来举个例子:

  • 我们需要安装 postcss和postcss-nested
  • 然后在postcss.config.js中配置如下
  • module.exports = {
      plugins: [
        require('postcss-nested')
      ]
    }
  • 我们嵌套设置css文件就可以了

注意:此时我们的post.config.js里面使用的时CommonJS规范,而vite默认支持ES Module所以我们要将postcss.config.js后缀改为 .cjs

2.@import 内联和变基

可以在css中通过 @import url() 来引用其他目录下的css,我们也可以为某个目录创建别名,如下:

export default defineConfig({
  resolve: {
    alias: {
      '@style': './public/styles'
    }
  }
})

3.CSS Modules

在协同开发时,可能会出现相同的类名,该类名对应的样式就有可能被覆盖,所以就可以使用CSS Modules。

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

// index.module.css
.content {
  width: 100px;
  height: 100px;
  background-color: red;
}

// main.js
import indexCss from '../public/styles/cssModules/index.module.css'
document.getElementById('foo').className = indexCss.content

原理:将css中的类名按照某种规则进行替换,例如我们代码中的 content -> _content_1xgol_1 ;创建映射对象,存储着类名被替换前后的映射本代码中的 indexCss就可以理解成映射对象;创建 style 标签,将替换类名后的 CSS 样式复制到里面 将 style 标签插入 index.html 的 head 标签中。

4.css 预处理器

由于 Vite 的目标仅为现代浏览器,因此建议使用原生 CSS 变量和实现 CSSWG 草案的 PostCSS 插件(例如 postcss-nesting)来编写简单的、符合未来标准的 CSS。  话虽如此,但 Vite 也同时提供了对 .scss, .sass, .less, .styl 和 .stylus 文件的内置支持。没有必要为它们安装特定的 Vite 插件,但必须安装相应的预处理器依赖:

# .scss and .sass
npm add -D sass

# .less
npm add -D less

# .styl and .stylus
npm add -D stylus

静态资源处理

将资源引入为url

当我们在js文件或者css文件(url())中引入静态资源时,会返回一个解析后的公共路径,行为类似于 Webpack 的 file-loader,例如:



在development中是 /src/assets/tup.jpg 在production是 /assets/tup-xxxx.jpg这个地址。vite会在生产构建后自动帮我们转换,并生成一个hash名。

常见的图像、媒体和字体文件类型被自动检测为资源。你可以使用 assetsInclude 选项 扩展内部列表。具体细节大家可以查看官网:静态资源处理 | Vite 官方中文文档

显式url引入

未被包含在内部列表或 assetsInclude 中的资源,可以使用 ?url 后缀显式导入为一个 URL。这十分有用,他是资源静态资源默认的一种引入方式。

将资源作为字符串

资源可以使用 ?row 后缀声明作为字符串引入。

new URL(url, import.meta.url)

我们可以通过该方式动态引入静态资源:

function getImageUrl(name) {
  return new URL(`./dir/${name}.png`, import.meta.url).href
}

导入脚本作为Worker

// worker.js
var i = 0
// 收到主线程发来的消息
self.onmessage = (res) => {
  console.log(res, 'res')
  if(res.data.type = 'ready') {
    console.log('worker线程收到消息');
  }
}
// 将信息发送给主线程
self.postMessage(i)
带有查询后缀的导入

你可以在导入请求上添加 ?worker 或 ?sharedworker 查询参数来直接导入一个 web worker 脚本。默认导出会是一个自定义 worker 的构造函数:

// main.js中
import CountWorker from './worker.js?worker'
const worker = new CountWorker()
worker.postMessage({type: 'ready', message: '主线程发来消息'})
worker.onmessage = (res) => {
  console.log('主线程收到消息', res.data);
}
通过构造器导入

一个 Web Worker 可以使用 new Worker() 和 new SharedWorker() 导入。与 worker 后缀相比,这种语法更接近于标准,是创建 worker 的 推荐 方式。

// main.js
const worker = new Worker(new URL('./worker.js', import.meta.url))
worker.postMessage({type: 'ready', message: '主线程发来消息'})
worker.onmessage = (res) => {
  console.log('主线程收到消息', res.data);
}

JSON和Glob导入

具体用法及描述请参考官网:功能 | Vite 官方中文文档

// 导入json
import json from './src/test.json'
import {name, age} from './src/test.json'
console.log(json, name, age);

// 不添加第二个参数默认为动态导入
const modules = import.meta.glob('./src/*.js')
console.log(modules);
// {./src/a.js: ƒ, ./src/b.js: ƒ}

// eager:true 表示直接引入所有模块
const modules2 = import.meta.glob('./src/*.js', { eager: true })
console.log(modules2);
// {./src/a.js: Module, ./src/b.js: Module}

构建生产版本

关于这一部分大家可以自行查看官网的描述:

构建生产版本 | Vite 官方中文文档

 环境变量和模式

 

你可能感兴趣的:(javascript,前端,vue.js)