阅读视图

发现新文章,点击刷新页面。

使用Fetch API 探索前后端数据交互

image

前言

  在当今的 Web 开发中,前端与后端的数据交互是构建动态应用的核心。API 是连接不同软件应用的重要桥梁,允许开发者通过 HTTP 请求与服务器交互,高效调用API数据对于构建现代 Web 应用至关重要。传统的页面刷新方式已经无法满足用户对流畅体验的需求,而 Fetch API 的出现为 JavaScript 带来了全新的生命力。

一、Fetch API 概述

1.1 Fetch API 是什么❓

  Fetch API 是现代浏览器提供的一个用于发起网络请求的接口,用于发起 HTTP 请求。它提供简洁的异步API,使开发者能够以更现代的方式与服务器交互。它是传统的 XMLHttpRequest 的替代品,提供了更简洁、更强大的功能。基于 Promise 实现,使异步操作更加直观。

image

  相比于传统的 XMLHttpRequest 更加强大、灵活且易于使用。Fetch 基于 Promise 设计,使得异步请求的处理更加优雅。

特性 Fetch API XMLHttpRequest
语法 基于 Promise,更简洁 回调函数,较复杂
请求/响应对象 标准化 非标准化
默认携带 Cookie 不携带 携带
超时控制 需要额外实现 原生支持
取消请求 使用 AbortController 原生支持
进度事件 有限支持 完整支持

1.2 Fetch 的基本语法

  Fetch API 的基本用法是通过调用 fetch() 函数并传入一个 URL 作为参数来发起网络请求。该函数返回一个Promise对象,可以在其then()方法中处理请求成功的情况,在catch()方法中处理请求失败的情况。Fetch API 最基本的形式如下所示。

fetch(url, options)
.then(response => response.json()) // 解析 JSON 数据
  .then(data => console.log(data))   // 处理数据
.catch(error => console.error('出现错误:', error)); // 错误处理

  上述代码示例展示了使用 Fetch API 发起一个请求,返回的 Promise 解析为响应对象,进而能访问响应体数据。处理响应体通常包含 JSON 数据,通过 .json() 方法解析。如果请求失败,fetch 返回的 promise 会拒绝,并将错误信息传给 catch 方法。

1.3 fetch 配置选项

  fetch 接受第二个可选参数,一个可以控制不同配置的对象,常见属性如下表所示。

配置项 简要描述 常用值
method 请求的 HTTP 方法,默认方法为GET GET、POST、PUT、PATCH、DELETE
headers 请求中 HTTP 标头
body 请求体。
请注意,使用 GET 和 HEAD 方法的请求不能有正文
mode 指定请求的模式。 cors:默认值,允许跨域请求
same-origin:只允许同源请求。
no-cors:不能添加跨域的复杂标头,相当于提交表单所能发出的请求
credentials 指定是否发送 Cookie same-origin:默认值,同源请求时发送 Cookie,跨域请求时不发送
include:不管同源请求,还是跨域请求,一律发送 Cookie
omit:一律不发送
cache 指定如何处理缓存 default:默认值,先在缓存里面寻找匹配的请求
no-store:直接请求远程服务器,并且不更新缓存
reload:直接请求远程服务器,并且更新缓存
no-cache:
force-cache:缓存优先,只有不存在缓存的情况下,才请求远程服务器
only-if-cached:只检查缓存,如果缓存里面不存在,将返回504错误
redirect 如何处理 HTTP 重定向响应,默认设置为follow follow、error、manual
referrer 包含请求的反向链接的字符串,默认为空字符串
referrerPolicy 指定用于请求的反向链接政策
signal AbortSignal 对象实例,支持接口中止请求
priority 指定当前请求相对于其他同类请求的优先级,
默认设置为auto
high、low、auto

二、Fetch API 的基本使用

  Fetch API 支持多种 HTTP 请求方法,如GET、POST、PUT、DELETE等。默认情况下,fetch() 函数会发送 GET 请求。如果需要发送其他类型的请求,可以在fetch() 函数的第二个参数中指定请求的配置对象。

2.1 发起 GET 请求

  GET 请求是最常见的请求类型,用于从服务器获取数据。在Fetch API中,构造一个 GET 请求的URL是一件非常简单的事情。首先需要了解的是,GET请求的参数通常是通过URL的查询字符串(query string)部分传递给服务器的。

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('出现错误:', error));

  在上述例子中,fetch 执行 GET 请求,在构建URL时,需要确保查询参数是经过URL编码的,以避免查询字符串解析错误。一旦发起 GET 请求,就需要处理服务器返回的响应数据,Fetch API 返回的 response 是一个 Response 对象,可以使用以下任一方法获取响应内容:

方法 简要说明
response.text() 返回一个使用以文本为响应正文解析的 Promise
response.json() 返回一个使用从 JSON 响应中解析的对象解析的 Promise
response.blob() 返回一个使用以 Blob 对象为响应正文解析的 Promise
response.ArrayBuffer() 返回一个使用以 ArrayBuffer 实例为响应正文解析的 Promise
response.formData() 返回一个使用以 FormData 对象为响应正文解析的 Promise

2.2 发起 POST 请求

  POST 请求用于向服务器发送数据,如提交表单或调用 API 提交数据到服务器。通过 Fetch API 调用 POST 请求需要构造一个包含请求体的对象,并将这个对象作为第二个参数传递给 fetch 函数。

const userData = {
  username: 'example',
  email: 'example@example.com'
};

fetch('https://api.example.com/users', {
  method: 'POST',
  body: JSON.stringify(userData)
})
.then(response => response.json())
.then(data => console.log('Success:', data))
.catch(error => console.error('出现错误:', error));

  使用 fetch() 发送 POST 请求的关键是指定要发送至服务器的数据,它可以采用多种格式,包括 JSON、FormData 和文本格式。在上述示例中,我们通过设置 method 为 POST 来发送 POST 请求,并在请求体 body 中发送 JSON 格式的数据。fetch 函数会将这些信息发送到服务器,并等待响应。

2.3 使用 async/await

  Fetch API 支持 async/await 语法,可以更简洁地处理异步操作,如下所示。fetch 接收到的 response 是一个 Stream 对象,response.json() 是一个异步操作,取出所有内容,并将其转为 JSON 对象。

const response = await fetch(url, options);
const data = await response.json();

三、Fetch API 的响应处理

3.1 处理 HTPP 响应

  fetch 请求成功以后,得到的是一个 Response 对象,它对应服务器的 HTTP 响应。

const res=await fetch(url)

  Response 包含的数据通过 Stream 接口异步读取,但它还有一些同步属性,对应 HTTP 回应的标头信息(Headers),如下表所示。

标头属性 类型 简要说明
Response.ok boolean 表示请求是否成功,true 对应的 HTTP 请求状态码200-299,false对应其他的状态码
Response.status number 返回一个数字,表示HTTP响应的状态码
Response.statusText string 表示HTTP响应的状态信息,例如请求成功以后,服务器返回 OK
Response.url string 返回请求的URL。如果URL存在跳转,该属性返回的是最终的URL
Response.type string 返回的是请求的类型。可能为以下值:
 basic:普通请求,即同源请求
 cors:跨域请求
 error:网络错误,主要用于Service Worker
 opaque:如果fetch请求的type属性为no-cors,就会返回这个值,表示发出的是简单的跨域请求
 opaqueredirect:如果fetch请求的redirect属性设为manual,就会返回这个值
Response.redirected boolean 表示请求是否有过重定向

image

3.2 处理请求响应状态

  在处理请求响应时,我们首先检查响应状态是否成功(response.ok),如果不成功则抛出错误。fetch 发出请求后,只有网络错误或无法连接时才会报错,即使服务器返回的状态码 是4xx或5xx。只有通过 Response.status 属性得到 HTTP 响应的真实状态码时,才能判断请求是否成功。

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('网络请求错误,' + response.statusText);
    }
    return response.json();
  })
  .then(data => console.log('Success:', data))
  .catch(error => console.error('出现错误:', error));

3.3 处理不同的响应类型

  当接收到服务器的响应后,通常需要解析响应体。Response 对象根据服务器返回的不同类型的数据,提供了不同的读取方法。这几个方法都是异步的,返回的都是 Promise 对象。必须等到异步操作结束,才能得到服务器返回的完整数据。

方法 简要说明
response.text() 获取文本字符串,主要用于获取文本数据,比如 HTML 文件
response.json() 获取 JSON 对象,主要用于获取服务器返回的 JSON 数据
response.blob() 获取二进制 Blob 对象
response.formData() 获取 FormData 表单对象,主要用于拦截用户提交的表单,修改某些数据后再提交给服务器
response.arrayBuffer() 得到二进制 ArrayBuffer 对象,主要用于获取流媒体文件

  Fetch API 可以处理多种响应格式:

// 处理JSON响应
fetch('/api/data.json')
  .then(response => response.json())
  .then(data => console.log(data));

// 处理文本响应
fetch('/api/data.txt')
  .then(response => response.text())
  .then(text => console.log(text));

// 处理Blob响应(如图片)
fetch('/image.png')
  .then(response => response.blob())
  .then(blob => {
    const objectURL = URL.createObjectURL(blob);
    document.getElementById('image').src = objectURL;
  });

注意,Response 是一个 Stream 对象,而 Stream 对象只能读取一次,读取完就没了。这意味着,上面的几个读取方法,只能使用一个,否则会报错。

四、高级 Fetch 用法

4.1 设置请求头

  在使用Fetch API进行请求时,可以通过 Headers 对象来设置请求头。每个请求或响应都有一个与之关联的 Headers 对象,这个对象包含了请求头和响应头,例如 Content-Type、Authorization 等。

fetch('https://example.com/api', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer your-token'
    },
    body: JSON.stringify({ name: 'John', age: 30 })
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('出现错误:', error));

  Response 对象还有一个 Response.headers 属性,指向一个Headers 对象,对应HTTP响应的所有标头。Headers 对象提供了以下方法来操作标头:

方法 简要说明
Headers.get() 根据指定的键名,返回键值
Headers.has() 返回一个布尔值,表示是否包含某个标头
Headers.set() 将指定的键名设置为新的键值,如果该键名不存在则会添加
Headers.append() 添加标头
Headers.delete() 删除标头
Headers.keys() 返回一个遍历器,可以依次遍历所有键名
Headers.values() 返回一个遍历器,可以依次遍历所有键值
Headers.entries() 返回一个遍历器,可以依次遍历所有键值对([key, value])
Headers.forEach() 依次遍历标头,每个标头都会执行一次参数函数

  上面的有些方法可以修改标头,那是因为继承自 Headers 接口。有些标头不能通过headers属性设置,比如Content-Length、Cookie 、Host等等。它们是由浏览器自动生成,无法修改。这些方法中,最常用的是 response.headers.get(),用于读取某个标头的值。

let response =  await  fetch(url);  
response.headers.get('Content-Type')

4.2 设置请求体参数

  当需要发送POST请求时,经常需要向服务器发送一些数据。使用Fetch API可以很便捷地通过 body 属性发送请求体。

// 发送JSON数据
const data = JSON.stringify({ name: 'John', age: 30 });
 
const options = {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  },
  body: data
};
 
fetch('https://example.com/api/users', options)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
 
// 发送表单数据
const formdata = new FormData();
formdata.append('username', 'john');
formdata.append('email', 'john@example.com');
 
fetch('https://example.com/api/register', {
  method: 'POST',
  body: formdata
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));

4.3 设置请求超时

  Fetch API 本身不支持超时设置,但可以通过 AbortController 实现:

const controller = new AbortController();
const signal = controller.signal;

// 设置5秒超时
const timeoutId = setTimeout(() => controller.abort(), 5000);

fetch('https://api.example.com/data', { signal })
  .then(response => response.json())
  .then(data => {
    clearTimeout(timeoutId);
    console.log(data);
  })
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Request timed out');
    } else {
      console.error('Other error:', error);
    }
  });

4.4 跨域请求

  如果需要进行跨域请求,可以在服务器端设置 CORS(Cross-Origin Resource Sharing)。在前端,也可以通过 credentials 选项来指定是否发送 cookies 等凭据。

fetch('https://example.com/api', {
    method: 'GET',
    credentials: 'include' // 允许跨域请求时携带 cookie
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('出现错误:', error));

4.5 上传文件

  如果表单里面有文件选择器,使用 Fetch 上传文件时,可以构造出一个表单,进行上传。

const fileInput = document.querySelector('input[type="file"]');

const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('username', 'exampleUser');

fetch('https://api.example.com/upload', {
  method: 'POST',
  body: formData
  // 注意:不要手动设置Content-Type头,浏览器会自动设置正确的boundary
})
.then(response => response.json())
.then(data => console.log('Upload success:', data))
.catch(error => console.error('Upload error:', error));

4.6 请求取消

  使用 AbortController 取消正在进行的请求:

const controller = new AbortController();

// 开始请求
fetch('https://api.example.com/data', {
  signal: controller.signal
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => {
  if (error.name === 'AbortError') {
    console.log('Request was aborted');
  } else {
    console.error('Error:', error);
  }
});

// 在某个事件中取消请求
document.getElementById('cancel-button').addEventListener('click', () => {
  controller.abort();
});

4.7 并发请求

  使用 Promise.all 处理多个并发请求:

async function fetchMultipleResources() {
  try {
    const [usersResponse, postsResponse] = await Promise.all([
      fetch('https://api.example.com/users'),
      fetch('https://api.example.com/posts')
    ]);

    if (!usersResponse.ok || !postsResponse.ok) {
      throw new Error('One or more requests failed');
    }

    const users = await usersResponse.json();
    const posts = await postsResponse.json();

    console.log('Users:', users);
    console.log('Posts:', posts);

    // 合并数据并更新UI
    displayCombinedData(users, posts);
  } catch (error) {
    console.error('Error fetching data:', error);
  }
}

function displayCombinedData(users, posts) {
  // 实现数据合并和显示逻辑
}

五、总结

  Fetch API 是一个强大而简洁的网络请求 API,它基于 Promise 实现,提供了更好的可读性和可维护性。通过学习和掌握 Fetch API 的基本用法和请求方法,我们可以更轻松地发起网络请求并处理响应结果。同时,我们也需要注意 Fetch API 的一些限制和常见问题,并采取相应的措施来解决它们。

image

自动导入 AutoImport:告别手动引入依赖,优化Vue3开发体验

image

前言

  模块化已经是现代 Web 开发必不可少的开发方式,频繁引入依赖包是一个常见的操作。但是,手动引入依赖包往往繁琐,尤其是当依赖包数量较多时,会显著降低开发效率。如果正在用 Vue3 开发项目时,每写一个页面,都要重复引入 ref、reactive等等API。代码开头总是一堆 import 语句,不仅繁琐,还容易因为漏写导致运行时错误。更头疼的是,团队协作时,不同成员可能引入方式不一致,代码风格难以统一。这种重复劳动其实完全可以避免,unplugin-auto-import 插件就是专门解决这个痛点的利器。它能帮助我们在项目中,自动导入常用的使用的第三方库的 API,就可以方便我们开发,提升开发效率。

一、自动导入的价值:不止是少写几行代码

1.1 理解核心

  在深入具体配置之前,我们需要先扭转一个观念:unplugin-auto-import 不仅仅是一个 “帮助开发者写 import 语句” 的工具,更是一个模块解析与依赖管理的智能层。它的核心价值在于,通过声明式的配置,将开发者从繁琐的、重复的模块导入工作中解放出来,同时确保类型安全和代码整洁,但“智能”的前提是精准的规则定义。这感觉就像你每次想用家里的电视遥控器,都得先跑到储物柜里把它拿出来,用完了再放回去。明明遥控器就该放在茶几上,随手就能拿到。unplugin-auto-import 这个插件,干的就是这个“把遥控器放到茶几上”的活儿。它能自动帮你完成这些常用 API 的导入,让你在代码里直接使用 ref、onMounted、useRouter 等,就像它们是天生的全局变量一样。

  并非所以依赖都适合自动导入,项目内的代码可能就不一定适合自动引入。因为自动引入后,就能像全局变量那样直接使用,但从开发的角度就会丢失依赖链路,虽然另外生成了 Typescript 声明文件,IDE 能够正常识别, 但对于新加入项目的小伙伴来说,他们不一定知道是自动引入,因此可能会降低了一些可读性。那么,什么样的内容适合自动引入?简单来说,那些被广泛认知和使用、不用关注实现、不变的内容,不会影响可读性,不会影响开发,不会对开发者心智造成影响,就适合自动引入。

1.2 为什么需要自动导入 🤔

  在传统的前端开发中,我们经常需要手动导入各种函数和组件。在开发过程中我们需要自己去导入ref、reactive、computed等响应式说明,这些重复的导入语句不仅让代码变得冗长,还增加了维护成本。所以为了减少每个文件中的声明,就引入 Auto-Import 去解决这个问题。先看一个对比,这是配置前的典型代码:

<script setup lang="ts">
import { computed, ref } from 'vue';

const count = ref(0)
const doubled = computed(() => count.value * 2)
</script>

  使用 unplugin-auto-import 插件后,不需要再去引入,同样的功能只需要:

<script setup lang="ts">
const count = ref(0)
const doubled = computed(() => count.value * 2)
</script>

  变化看似不大,但实际开发中的体验提升是显著的:

  • 减少认知负担:不用再记忆每个 API 来自哪个包,这些细节交给工具处理。
  • 降低出错概率:不会因为忘记导入某个 API 而出现运行时错误,只需要在配置文件中更新预设,所有文件都会自动适应新的导入方式。
  • 提升编码流畅度:新成员加入项目,不用先花时间熟悉项目的 import 规范,直接开始写业务逻辑即可。想到什么直接写,不用在文件顶部和代码主体间来回跳转。
  • 类型安全的保障:对于 TypeScript 项目,unplugin-auto-import 会自动生成类型声明文件(通常是 auto-imports.d.ts),确保即使没有显式导入,也能获得完整的类型提示和检查。

二、从零开始配置:避开那些常踩的坑

  理论说再多,不如动手试一下。我们先来把插件跑起来,感受一下“开箱即用”的爽快感。

2.1 安装依赖

  在开始动手修改配置文件之前,我们有必要先理清几个核心概念和它们之间的关系。对于 Vite 项目,自动导入生态主要依赖于两个社区明星插件:unplugin-vue-components 和 unplugin-auto-import,以及一个由官方提供的“粘合剂”:unplugin-icons。

  • unplugin-vue-components 的职责是 “自动按需引入Vue组件”,这个插件会在背后悄悄帮你完成导入和注册组件这两件事。
  • unplugin-auto-import 的职责则更进一步,它专注于 “自动导入 Composition API、工具函数等”

  在开始复杂配置前,请确保项目已正确安装并集成了 unplugin-auto-import。对于 Vite 项目,基础安装和引入如下:

npm install --save-dev unplugin-vue-components
npm install --save-dev unplugin-auto-import
npm install --save-dev unplugin-icons

  这里安装的是开发依赖,因为自动导入是构建时和开发时工具,不会打包进生产代码。

2.2 基本配置

  安装好依赖只是第一步,正确的配置才是让一切运转起来的关键。我们将在 vite.config.ts 或 vite.config.js 文件中进行配置,这是配置的入口,在其中引入 AutoImport ,同时配置相关信息。

// vite.config.ts
import { defineConfig } from 'vite'
// 1. 引入 auto-import 插件
import AutoImport from 'unplugin-auto-import/vite'
 
export default defineConfig({
  plugins: [
    AutoImport({ /* options */ }),
  ]
})

  还需要在 tsconfig.json 文件里加入。

{
  "include": ["src/**/*.ts", "src/types/**/*.d.ts"]
}

2.3 核心配置

预设支持

  unplugin-auto-import内置了丰富的预设,支持多种流行库和框架,如Vue、Vue-router、pinia等,这些预设可以通过 imports 选项轻松配置:

AutoImport({
  imports: [ // 选择需要配置的插件
    'vue', // 自动导入 Vue 3 的 Composition API,如 ref, reactive, computed 等
    'vue-router', // 自动导入 Vue Router 4 的 API,如 useRouter, useRoute
    'pinia'// 自动导入 Pinia 的 API,如 defineStore, storeToRefs
  ]
})

  看,配置就是这么简单。核心就是 imports 数组,只要把想自动导入的包名写进去就行。

类型定义生成

  解决了运行时的导入问题,接下来是类型系统的挑战。TypeScript 需要知道这些“凭空出现”的标识符的类型是什么,否则就会报红,失去代码提示,unplugin-auto-import通过生成全局类型声明文件来解决这个问题。

  启用dts选项可以自动生成类型定义文件,提升开发体验:

AutoImport({
  // 为 TypeScript 生成全局类型声明文件
  dts: 'src/type/auto-imports.d.ts',  // 或者设置为 true,会在根目录生成
})

  看,配置就是这么简单。dts 选项是关键,它告诉插件为 TypeScript 生成类型声明文件的位置。有了这个文件,IDE 才能正确识别这些自动导入的变量。如果为 true,则会在导入冲突时,生成一个 auto-imports.d.ts 和一个 components.d.ts(如果用了组件自动导入)。也可以设置为一个自定义的文件名,我个人的习惯是把 dts 文件放在 “src/type” 目录下,并把它加入到 .gitignore 中,因为它是生成文件,不应该被提交。

  为了让 TypeScript 识别这个全局声明文件,还需要确保它被包含在 tsconfig.json 的 include 或 files 配置中。通常,生成的路径会自动被 Vite 的 TypeScript 插件处理,但手动检查一下是好的习惯。

{
  "include": [
    "src/type/auto-imports.d.ts" // 确保这一行存在
  ]
}

  这种机制的美妙之处在于,它实现了开发时无感导入完整的类型安全的完美结合。你既享受了代码的简洁,又没有牺牲TypeScript带来的智能提示和错误检查能力。

ESLint集成

  在真实的工程化项目中,unplugin-auto-import 从来不是孤军奋战。它必须与 TypeScript 编译器、ESLint 代码检查工具完美配合,否则就会陷入“代码能跑,但编辑器一片红”的尴尬境地。ESLint 默认规则会检查未声明的变量,自动导入的变量在源代码中没有显式导入,ESLint 会认为它们是未定义的,从而抛出错误。为了避免 ESLint 报错,可以配置自动生成 ESLint 配置文件:

AutoImport({
  eslintrc: {
    enabled: true, // 开启生成ESLint配置的功能
filepath: './.eslintrc-auto-import.json', // 指定生成的配置文件路径
globalsPropValue: true, // 声明为全局只读变量
  }
})

  插件会在项目根目录生成类型文件 .eslintrc-auto-import.json ,确保该文件在 eslint 配置中被 extends。

// .eslintrc.js 或 .eslintrc.cjs
module.exports = {
  // ... 其他配置
  extends: [
    // ... 其他扩展
    // 添加这行
    './.eslintrc-auto-import.json' // 这是插件生成的另一个文件
  ]
}

  unplugin-auto-import 在生成 dts 文件的同时,通常也会在项目根目录生成一个 .eslintrc-auto-import.json 文件,里面定义了所有自动导入变量的规则。把它包含进你的 ESLint 配置,ESLint 就知道这些变量是全局可用的,不会报错误。

三、深度定制:让自动导入更贴合项目

  基础配置只能算“能用”,但要想“好用”,还得根据项目情况深度定制。unplugin-auto-import 提供了非常灵活的配置项,让我们一起来看看。

3.1 导入更多生态库

  现代 Vue 3 项目很少只用到核心库,工具库如 VueUse,UI 组件库如 Element Plus 的某些工具函数,都可以纳入自动导入。

import AutoImport from 'unplugin-auto-import/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
import IconsResolver from 'unplugin-icons/resolver';

AutoImport({
  imports: [
    'vue',
    'vue-router',
    'pinia',
    // 添加 VueUse,它是一个函数工具集合
    '@vueuse/core',
  ],
   resolvers: [
      // 自动导入 Element Plus 相关函数
      ElementPlusResolver(),
      IconsResolver({
        prefix: 'Icon'
      })
    ],
  dts: 'src/auto-imports.d.ts',
})

  对于 Element Plus 的这类 API,配置会稍微复杂一点,通常需要结合 unplugin-vue-components(用于自动导入组件)和 resolvers 选项,但核心思想不变:把重复的 import 从代码中抹去。

3.2 自动导入项目本地工具函数

  这才是提升团队开发效率的大杀器。想象一下,项目里封装了很多好用的 useFetch、useTable、useModal 这样的组合式函数,散落在 src/hooks 目录下。以前用的时候,总要去找路径然后导入。现在,可以全部自动导入。

AutoImport({
  imports: [ ... ], // 第三方库
  dts: 'src/auto-imports.d.ts',
  // 关键配置:自动扫描指定目录下的文件
  dirs: [
    './src/stores', // 自动导入 Pinia store 的 useStore 函数
    './src/utils', // 自动导入工具函数
    './src/hooks/**', // 使用 glob 模式匹配子目录
  ],
})

  配置好 dirs 后,插件会在构建时扫描这些目录下的 ts、js 文件,将默认导出的函数或变量自动添加到全局可用列表中。比如在 src/hooks/useDarkMode.ts 里导出了一个 useDarkMode 函数,那么在任意组件中,你就可以直接 const { isDark, toggle } = useDarkMode(),无需导入。

3.3 精细化控制

  随着导入的东西越来越多,生成的 auto-imports.d.ts 文件可能会非常庞大,有时会影响 IDE 性能(虽然通常影响不大),这就可以通过一些配置进行优化。

按需导入

  如果你觉得 VueUse 全部导入太多,可以只导入你确定会用到的。

imports: [
  'vue',
  'vue-router',
  'pinia',
  {
    '@vueuse/core': [
      'useMouse',
      'useLocalStorage',
      'useDark',
      'useClipboard',
      'useDebounceFn',
    ],
  }
]

解决命名冲突

  如果两个库导出了同名的函数(虽然少见),或者你不想用默认的变量名,可以使用 alias 配置别名。

AutoImport({
  imports: [
    { 'vue-router': ['useRouter', 'useRoute'] },
    { 'my-router': ['useRouter as useMyRouter'] }, // 假设有另一个库
  ]
})

四、总结

  unplugin-auto-import作为一款强大的自动导入工具,通过智能化的模块分析和导入管理,彻底改变了传统的手动import方式。它不仅支持多种构建工具和框架,还提供了丰富的自定义选项,满足不同项目的需求。无论是小型应用还是大型项目,unplugin-auto-import都能显著提升开发效率,让开发者更专注于业务逻辑的实现。

image

Vue调试神器:Vue DevTools使用指南

image

一、初识Vue Devtools

Vue DevTools 概述

  在现代前端开发中,Vue.js 应用的组件化架构虽然提升了代码复用性,但也带来了复杂的状态管理和组件交互问题。当应用包含数十个嵌套组件时,传统的 console.log 调试方式如同在黑暗中摸索。Vue.js Devtools 作为官方调试工具,通过可视化界面将组件结构、状态变化和性能数据直观呈现,让开发者能够像"透视"一样观察应用内部运行机制。

image

  Vue Devtools 是 Vue 官方发布的调试浏览器插件,可以安装在 Chrome、Firefox、Edge等浏览器上,可以帮助我们监控和管理 Vue 应用的状态、事件和性能。通过 Vue Devtools,我们可以查看组件的结构、属性和方法,以及父子组件之间的关系。此外,Vue Devtools 还提供了时间轴功能,让我们可以更好地了解应用的状态变化。

Vue DevTools 功能说明

  1. 组件树检视:能够清晰展示出应用中的组件层级结构,方便开发者理解和导航。
  2. 状态和数据查看:可以检查组件的状态,包括props、data、computed properties等。
  3. 调试事件:可以监听和触发事件,便于开发者查看事件的响应和效果。
  4. 时间旅行:这是 Vue DevTools 的高级功能之一,能够记录组件的快照,允许开发者在不同的快照之间切换,观察应用状态的变化。
  5. 控制台集成:Vue DevTools 提供了集成到浏览器控制台的能力,可以通过控制台直接与Vue实例交互。
  6. 组件信息展示:可以查看每个组件所对应的虚拟DOM结构和渲染细节。

二、环境适配:多场景下的安装与配置

浏览器扩展

  目前 Vue DevTools 主要支持 Chrome 浏览器和 Firefox 浏览器,并提供对应的浏览器扩展。对于其他平台(如Safari或Edge)的支持情况,可以通过各种主流浏览器的扩展商店进行安装。

插件:www.chajianxw.com/developer/1…

  打开 Chrome 浏览器,选择菜单“更多程序”→“扩展程序”,打开扩展程序界面,打开开发者模式,单击“加载已解压的扩展程序”按钮,将vue-devtools插件安装到Chrome 浏览器,安装结果如图:

image

  安装完成后,开发者需要在浏览器的扩展管理页面启用Vue DevTools。在使用Vue DevTools时,通常需要在Vue应用中直接运行,这时DevTools会自动识别并展示调试信息。若未看到,刷新页面或检查是否为 Vue 应用。

image

Vite Plugin

单体应用

对于Electron应用、移动端应用(NativeScript/Capacitor)或者服务端渲染应用,浏览器扩展可能无法直接使用。别担心,Vue Devtools还提供了NPM包版本

npm install -g @vue/devtools

Vue DevTools 默认仅适用于 Vue 的开发版本(非压缩版),在生产环境中默认禁用,否则就好比把家里的“透视眼镜”给小偷戴上,会暴露应用内部状态。

三、功能解析:掌握调试工具的核心能力

  在安装了 Vue Devtools 的浏览器中,打开你的 Vue 应用。然后右键点击页面,选择“Inspect”,在弹出的开发者工具中找到“Vue”选项卡,点击即可打开 Vue Devtools。

3.1 Components面板:组件世界的“上帝视角”

  在现代的前端开发中,组件化已经成为一种标准的实践方式。Vue.js 也不例外,它提供了一种灵活的方式来构建用户界面,通过组件树的层级结构来组织界面的不同部分。在 Vue 应用中,组件的父子关系是通过组件嵌套和属性传递来定义的。父组件通过在模板中声明子组件标签,并通过 props 将数据传递给子组件,从而建立起父子关系,Vue Devtools 提供了一个直观的方式来查看组件之间的这种层级结构。

  在 Vue DevTools 的“Components”标签页中,可以直观地看到整个应用的组件树结构,类似于文件系统的目录结构,从根组件(Root)开始,层层展开,让我们可以更好地了解组件的结构。每个组件都是一个节点,父组件之下包含子组件,形成清晰的层级关系。通过展开组件节点,可以查看其子组件,帮助开发者快速定位问题发生的组件区域。在组件树视图中,可以通过输入关键字来筛选组件,快速定位到关心的组件,这对于大型应用中组件众多的情况非常实用。

image

  在组件树中,选中某个组件后,右侧面板会显示该组件的属性、数据、计算属性和方法等信息。开发者可以实时查看组件状态的变化,无需在控制台中进行繁琐的打印操作。

image

  组件树中的每个组件节点不仅显示了组件的类型,还可以展开来查看其详细信息,包括组件的属性、数据、计算属性以及样式等。最刺激的是实时编辑功能——直接在Devtools中直接修改组件的 data 属性值,比如把一个按钮的 disabled 从 true 改为 false ,页面上的按钮立即变得可点击!无需刷新页面,无需重新编译,就像用手指直接拨动乐高积木一样神奇。这对于调试数据驱动的问题非常有帮助,能够快速验证数据的正确性和对组件的影响。

image

3.2 Events面板:事件流的“监听器”

  在 Vue Devtools 中,Events 面板用来监控Vue实例的所有事件。

  • 事件历史:按时间顺序显示所有触发的Vue事件(包括自定义事件)
  • 按组件筛选:只看某个特定组件触发的事件
  • 事件详情:点击事件可查看事件名称、目标组件、传递参数等信息
  • 复制数据:支持将事件数据复制到剪贴板

这对于调试复杂的组件通信(比如爷孙组件传值、兄弟组件通信)非常有用,帮助我们更好地了解事件的处理情况。

3.3 状态追踪:应用数据的"黑匣子记录仪"

  如果应用使用了Vuex(Vue 2)或Pinia(Vue 3官方推荐),Vue Devtools 会自动显示状态面板,这个面板就是你的“中央监控室”。左侧显示完整的 store 状态树,所有数据一目了然。可以展开每一个节点,查看当前所有共享状态的值。在这里,我们可以查看state、getters、mutations(Vuex)或actions(Pinia),以及它们的详细信息。通过时间线视图,开发者可以查看状态树是如何随时间变化的,帮助理解状态变化的流程。

3.4 最炫酷的“时间旅行”

  Vue Devtools 提供了一个时间轴功能,可以让我们更好地了解应用的状态变化。在时间轴中,我们可以查看每个组件的状态变化,以及它们之间的依赖关系。开发者可以回溯到过去的状态,进行状态差异的比较分析。这对于调试复杂的状态管理逻辑非常有用,能够快速定位状态变化导致的问题。

3.5 Router面板:路由导航的“导航仪”

  如果应用使用了Vue Router,Router 面板就是你的“导航仪”。在“Router”标签页中,可以查看当前路由的信息,包括路径、查询参数、路由参数等,如下图所示。

image

  同时,还能看到路由的历史记录,方便开发者了解应用的导航流程。通过观察路由的变化,开发者可以调试路由跳转、参数传递等问题。例如,当遇到路由跳转后页面不更新的问题时,可以通过查看路由变化记录,分析错误发生的原因。

3.6 Timeline面板:应用优化的"体检报告"

如何录制性能数据

  1. 切换到Timeline面板
  2. 点击左上角的“Start recording”(开始录制)按钮
  3. 在页面上执行你想要分析的操作(比如点击一个会加载大量数据的按钮)
  4. 点击“Stop recording”停止录制

数据解读:谁在“摸鱼”?

录制完成后,你会看到类似心电图的时间轴:

  • 组件渲染时间:每个组件从开始渲染到完成花了多久
  • 组件更新次数:某些组件是不是在“无效加班”(频繁无意义地重新渲染)
  • 生命周期钩子执行时间:比如mounted钩子里是不是放了太多代码导致阻塞

性能优化实战案例

通过Timeline面板,你可能会发现:

  • 某个表格组件渲染要500ms → 考虑使用虚拟滚动
  • 某个computed属性被频繁重新计算 → 考虑使用缓存或shallowRef
  • 某个组件在父组件更新时跟着乱更新 → 添加v-once或合理使用key

四、总结

  Vue Devtools是一款非常实用的工具,可以帮助我们更好地理解和管理Vue应用。使用 Vue DevTools 进行调试与性能优化,能够极大地方便开发者的工作。通过可视化 的组件树、实时数据修改、Vuex 状态跟踪及时间旅行功能,我们可以更加高效地定位问题,优化处理逻辑,提升应用性能。

image

❌