ai-要約を取得 文章摘要

1. 创建工作流文件

项目根目录创建.github/workflows/deploy.yml

name: GitHub Pages

on:
push:
branches:
- main
pull_request:

jobs:
deploy:
runs-on: ubuntu-22.04
permissions:
contents: write
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
steps:
- uses: actions/checkout@v3

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18'

- name: Cache dependencies
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-

- run: npm install
- run: npm run build

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
if: ${{ github.ref == 'refs/heads/main' }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist # 确保这里指向构建后的目录

工作流中,配置了:

on:
push:
branches:
- main
pull_request:
branches:
- main

两种触发方式所执行的是同一个工作流,但是它们的触发时机不同,取决于你配置的 on 部分的内容。

这意味着:

  • push 触发:当你将代码推送到 main 分支时,工作流会运行。
  • pull_request 触发:当有 PR 提交到 main 分支时,工作流会运行。

这两种事件都触发同一个工作流的执行。但是,它们的触发时机和目的不同

  • push 事件:通常用于 代码推送后 的自动化过程,例如构建、部署、发布等。当你将代码推送到 main 分支时,自动触发工作流中的相关步骤(比如构建静态页面并部署到 GitHub Pages)。
  • pull_request 事件:用于 PR 创建、更新时 的自动化检查过程,通常是用于 测试和审查。例如,你可以在 PR 被创建或更新时运行测试,确保代码在合并之前没有问题。

2.base 路径问题

默认情况下,Vite 会使用 / 作为资源的基础路径。这样会有以下几个结果,特别是在你部署到 GitHub Pages 或类似的静态资源托管平台时:

1. 开发环境中的行为

在本地开发时(运行 vite 开发服务器),默认的 base/,这意味着:

  • 静态资源(如 CSS、JS、图片等)的路径将从 根路径 / 开始。
  • 这在开发环境中通常不会有问题,因为开发服务器会根据 / 路径来正确解析静态资源。

2. 生产环境中的行为

当你在生产环境中(如部署到 GitHub Pages)没有设置 base 时,静态资源的路径也默认是 /。然而,GitHub Pages 将项目部署在一个 子路径 下(如 https://yourusername.github.io/your-repo/),这就导致了以下问题:

  • 静态资源的路径仍然是基于根路径 /,例如 https://yourusername.github.io/app/css/style.css
  • 错误的资源路径:在 GitHub Pages 上,资源的实际路径应该是 https://yourusername.github.io/your-repo/css/style.css,即它需要从 /your-repo/ 开始,但因为没有设置 base,它尝试从根路径 / 加载资源,这会导致 404 错误。

3. 如何影响 GitHub Pages 部署

如果你不设置 base,并直接将生成的文件部署到 GitHub Pages 的 gh-pages 分支,GitHub Pages 会尝试从根路径 / 加载静态资源,而不是从 /your-repo/ 路径加载。这导致了静态资源无法正确加载,页面样式、脚本等都可能出现问题,页面可能会显示为空白或者报错。

4. 总结

  • 开发环境:没有设置 base,开发时没有问题,因为它会从 / 路径加载资源。
  • 生产环境(部署到 GitHub Pages 等):没有设置 base,静态资源的路径会错误地指向根路径 /,而不是子路径(如 /your-repo/),这会导致资源加载失败,页面样式、JS 可能无法正确显示。

注意下方第14行:

// vite.config.ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import Pages from 'vite-plugin-pages'
// @ts-expect-error: eslint plugin does not have type definitions
import eslint from 'vite-plugin-eslint'
import Components from 'unplugin-vue-components/vite'
import { VantResolver } from 'unplugin-vue-components/resolvers'

// https://vite.dev/config/
export default defineConfig({
base: process.env.NODE_ENV === 'production' ? '/vue3-ts-shuangzhao/' : '/', // 设置根路径
plugins: [
vue(),
vueJsx(),
eslint(),
Pages({
// 指定路由文件的目录
dirs: 'src/views',
// 可以自定义路由生成规则
extensions: ['vue'], // 仅识别 .vue 文件
exclude: ['**/components/**'] // 排除特定目录
}),
Components({
resolvers: [VantResolver()]
})
],
css: {
preprocessorOptions: {
scss: {
api: 'modern-compiler' // or "modern", "legacy"
}
}
},
server: {
host: '0.0.0.0',
port: 5173,
open: true,
proxy: {
'/api': 'http://127.0.0.1:3007'
},
cors: true // 在开发阶段不需要手动设置服务器端的 CORS 头信息即可进行跨域请求,节省时间。
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})

5. 补充 fetch 加载本地资源、与部署后加载静态资源的路径问题

如果你的 fetch 数据文件存储在前端项目中(而不是后端项目),并且你希望在本地开发环境构建部署后都能通过 fetch 访问这些数据文件,以下是具体的解决方案和最佳实践:

这是ai-给的方案点击查看详情

最佳方案:将数据文件放在 public 目录

将数据文件放在前端项目的 public 目录中,这样无论是在开发环境还是构建部署后,都可以通过相同的路径访问这些文件。


具体步骤

  1. 将数据文件放在 public 目录

将你的数据文件(如 data.json)移动到 public 目录中。构建工具(如 Create React App、Vue CLI 等)会将 public 目录中的文件直接复制到构建输出目录中,而不会对它们进行处理。

  • 文件结构

    your-project/
    ├── public/
    │ └── data.json
    ├── src/
    │ └── App.js
    └── package.json
  • 优点

    • 数据文件在开发环境和生产环境中都可以通过相同的路径访问。
    • 无需额外配置 Webpack 或构建工具。

  1. 在代码中使用绝对路径访问数据

在代码中,使用绝对路径(以 / 开头)访问 public 目录中的数据文件。

  • 示例代码

    fetch('/data.json')
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error('Error fetching data:', error));
  • 解释

    • 在开发环境中,/data.json 会指向 http://localhost:3000/data.json
    • 在构建部署后,/data.json 会指向静态文件服务器的根目录下的 data.json

  1. 本地开发环境

在本地开发环境中,数据文件可以通过开发服务器的根路径访问。

  • 在代码中,fetch('/data.json') 会自动指向 http://localhost:3000/data.json

  1. 构建和部署

在构建和部署后,数据文件会被复制到构建输出目录中,并通过相同的路径访问。

  • 构建项目

    构建完成后,数据文件会被复制到构建输出目录(如 public )中。

  • 部署
    将构建输出目录部署到静态文件服务器(如 Nginx、Apache 或 GitHub Pages)。

  • 访问数据
    在部署后的页面中,fetch('/data.json') 会自动指向静态文件服务器的根目录下的 data.json


  1. 处理路径问题(如果需要子路径)

如果你的项目部署在子路径下(如 https://your-domain.com/your-app/),需要调整路径以确保 fetch 请求正确指向数据文件。

  • 使用环境变量动态设置路径
    .env.production 文件中配置子路径:

    VITE_PUBLIC_URL=/shuangzhao-vue3-ts
  • 在代码中使用 process.env.PUBLIC_URL

    fetch(`${process.env.PUBLIC_URL}/data.json`)
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error('Error fetching data:', error));
  • 解释

    • 在开发环境中,process.env.PUBLIC_URL 为空,路径为 /data.json
    • 在构建部署后,路径为 /your-app/data.json

  1. 检查构建输出

确保数据文件被正确复制到构建输出目录中。构建完成后,检查输出目录(如 public)中是否存在数据文件。

  • 示例
    • 如果使用 Create React App,构建输出目录是 build
    • 如果使用 Vue CLI,构建输出目录是 dist
  • 检查步骤
    1. 运行构建命令(如 npm run build)。
    2. 打开构建输出目录,检查是否存在数据文件。
    3. 如果数据文件不存在,检查构建配置是否正确。

总结

通过将数据文件放在 public 目录,并使用绝对路径访问,可以确保无论是在本地开发环境还是构建部署后,都能通过 fetch 获取数据。具体步骤如下:

  1. 将数据文件放在 public 目录。
  2. 在代码中使用绝对路径(如 /data.json)访问数据。
  3. 在本地开发环境中,通过开发服务器访问数据。
  4. 在构建部署后,通过静态文件服务器访问数据。

这种方法简单、直接,且无需额外配置,适合大多数场景。如果你的项目部署在子路径下,可以通过环境变量动态设置路径。

这是根据实战总结的

可以在utils中封装一个专用fetch组件:

const fetchWithBaseUrl = url => {
// 获取环境变量中的 VITE_PUBLIC_URL
// 默认值为空,代表根路径(开发环境)
// 小坑:必须为空字符串,不可以为 '/',否则 / 会抵消前项目的 localhpst 路径
const baseUrl = import.meta.env.VITE_PUBLIC_URL

// 拼接完整的请求路径
const fullUrl = `${baseUrl}${url}`

// 调用原生的 fetch 方法
return fetch(fullUrl)
}

export default fetchWithBaseUrl

以上代码中最重要的一个注意点,在根目录分别创建了 .env.production 和 .env/development:

  • .env.production中:VITE_PUBLIC_URL=/vue3-ts-shuangzhao
  • .env.development中:VITE_PUBLIC_URL=’ ‘ ,这里必须为一个空字符串,否则,’/‘ 将抵消 localhost地址

根据这个现象推测,fetch 函数是自动拼接拼接传入的路径字符串,VITE_PUBLIC_URL=’/‘,意味着在传入的参数前增加了一个 ‘/‘,会导致字符串拼接时,如果传入参数以/开头,如’/data’,会导致拼接后的路径被多加了一个’/‘,成为’//data’。

这里带领自己复习一下:

用法区别
  1. 单个 /(相对当前网站根目录)
  • /path/to/resource 表示 相对于当前网站的根目录 解析路径。

  • 例如,在

    http://example.com/app/

    页面:

    fetch('/data/banner.json')

    解析后请求的是:

    http://example.com/data/banner.json

    (注意 / 代表根目录,而不是当前路径 app/


  1. 双斜杠 //(协议相对 URL)
  • //example.com/path/to/resource 表示 协议相对 URL,它会使用当前页面的协议(httphttps)。

  • 例如,在

    https://example.com/app/

    页面:

    fetch('//cdn.example.com/data/banner.json')

    解析后请求的是:

    https://cdn.example.com/data/banner.json

    (它继承了当前页面的 https 协议)

  • 如果当前页面是

    http://example.com/app/

    ,那么

    //cdn.example.com/data/banner.json

    解析为:

    http://cdn.example.com/data/banner.json

  1. 完整 URL
  • https://example.com/path/to/resource 代表完整的绝对路径,不会受到当前页面 URL 影响

什么时候用 //

  • 适用于 CDN 资源或跨域请求,但你希望它自动匹配当前协议。
  • 例如,网页可能会在 http://https:// 下运行,而 // 可确保资源请求使用相同协议,避免“混合内容”警告。

什么时候避免 //

  • 如果 VITE_PUBLIC_URL = //api.example.com

    ,则拼接路径时会导致

    fetch('//api.example.com/data')

    ,这实际上会变成:

    arduino


    复制编辑
    https://api.example.com/data (在 https 站点上)

    但如果 VITE_PUBLIC_URL = /,则拼接的 /data 是相对路径,不涉及跨域问题。


结论:

  • /path 👉 当前网站根目录的相对路径
  • //example.com/path 👉 继承当前协议的完整 URL
  • http://example.com/path 👉 完整 URL,不依赖当前页面协议

3.导航守卫中的更改

路由守卫中也需要考虑该前缀。否则,to.pathnext() 中的路径可能会与实际访问的路径不匹配,从而导致路由跳转失败或跳转到错误的页面。

import { createRouter, createWebHistory } from 'vue-router'
import generatedRoutes from 'virtual:generated-pages'
import { userStore } from '@/stores/user'

// 在这里声明 basePath
const basePath = import.meta.env.MODE === 'production' ? '/vue3-ts-shuangzhao/' : '/'

const routes = [
...generatedRoutes,
{
path: '/:pathMatch(.*)*',
name: 'not-found',
component: () => import('@/views/404.vue')
}
]
const router = createRouter({
history: createWebHistory(basePath), // 这里填上 basePath
routes
})

// 增加全局导航守卫
router.beforeEach((to, from, next) => {
const store = userStore()
// 验证token
if (store.token) {
// 检查是否为首页路径,如果是则跳转到 /task
if (to.path === '/') {
next('/task')
} else {
next()
}
} else {
if (
to.path === '/login' ||
to.path === '/login/ServiceAgree' ||
to.path === '/login/PrivatePolicy'
) {
next()
} else {
next('/login')
}
}
})

export default router

4. 学习了一个新技能

因为使用了插件,基于文件系统自动生成路由,因此当初创建了一些列空白的.vue文件,而在项目生产阶段,进行打包部署时,严格的打包检查会提示空白文件,因此需要一个 js 脚本来为空白的 .vue 文件进行预填充。

项目根目录创建一个 js 文件,如:fill-empty-vue-files.js

import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'

// 获取当前脚本文件的路径并设置 src 目录路径
const currentDir = fileURLToPath(import.meta.url)
const vueDir = path.resolve(path.dirname(currentDir), 'src')

// 扫描文件夹中的所有 .vue 文件
const scanVueFiles = dir => {
const files = fs.readdirSync(dir)
files.forEach(file => {
const filePath = path.join(dir, file)
const stat = fs.statSync(filePath)

// 如果是文件夹,则递归扫描
if (stat.isDirectory()) {
scanVueFiles(filePath)
} else if (file.endsWith('.vue')) {
processVueFile(filePath)
}
})
}

// 处理每个 Vue 文件
const processVueFile = filePath => {
const content = fs.readFileSync(filePath, 'utf-8').trim()

// 如果文件为空,填充基本模板
if (content === '') {
console.log(`Filling empty Vue file: ${filePath}`)
const templateContent = `
<script setup>
import { ref, onMounted } from 'vue'

// 定义一个 ref 变量来存储文件路径
const filePath = ref('')

// 使用 onMounted 钩子获取文件路径
onMounted(() => {
// 通过 import.meta.url 获取当前文件的路径
filePath.value = import.meta.url
})
</script>

<template>
<div>{{ filePath }}</div>
<!-- 显示文件路径 -->
</template>

<style lang="scss" scoped>
/* 你可以在这里添加样式 */
</style>
\n`
fs.writeFileSync(filePath, templateContent.trim(), 'utf-8')
}
}

// 扫描并填充 Vue 文件
scanVueFiles(vueDir)

然后,终端执行 node fill-empty-vue-files.js,现在可以暂时打包项目了 /

5. vite-plugin-eslint 插件的类型声明问题

前面整理过 vite-plugin-eslint 插件的基本情况,这里不再重复啦。

很奇怪,本地构建时并没有有关于该插件的类型错误提示,但是工作流部署,却一直报错,试了n多方法,最终vscode快速修改给秒速度解决了 /

直接在引入文件前加上这一句:

import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import Pages from 'vite-plugin-pages'
// @ts-expect-error: eslint plugin does not have type definitions
import eslint from 'vite-plugin-eslint'
import Components from 'unplugin-vue-components/vite'
import { VantResolver } from 'unplugin-vue-components/resolvers'
  1. // @ts-expect-error: eslint plugin does not have type definitions
  • 这是一个 TypeScript 的注释,用于告诉 TypeScript 编译器:接下来的代码会有一个类型错误,但这是预期的,因此不需要报错。
  • 具体来说,vite-plugin-eslint 这个插件可能没有提供 TypeScript 的类型定义文件(即 .d.ts 文件),因此 TypeScript 无法推断它的类型,会报类型错误。通过 @ts-expect-error,开发者明确表示这个错误是已知的,并且可以忽略。
  1. import eslint from 'vite-plugin-eslint'
  • 这是一个 ES Module 的导入语句,用于从 vite-plugin-eslint 包中导入默认导出(default export)。
  • vite-plugin-eslint 是一个 Vite 插件,用于在 Vite 项目中集成 ESLint。ESLint 是一个 JavaScript/TypeScript 的代码检查工具,用于发现代码中的潜在问题并保持代码风格一致。
  • 通过这个导入语句,开发者可以在 Vite 配置中使用这个插件。

6. 关于 base 设置后的一个细节

工作流部署静态页面后,进入页面,在页面中点击跳转至其他页面,可以正常跳转,但是在浏览器直接输入该页面路由地址 ,却报404。

问题分析:

  1. Vue Router 使用 history 模式:
    • 在使用 createWebHistory 模式时,Vue Router 会使用 HTML5 history API,这意味着路由是基于浏览器的地址栏路径进行管理的,而不是使用传统的哈希(#)模式。
    • 当您通过点击跳转到不同的页面时,路由器会通过 JavaScript 捕捉到这些路由并渲染相应的页面内容。
  2. 页面直接访问时出现 404 错误:
    • 当您在浏览器的地址栏直接输入某个页面的 URL(比如 /login/task)时,浏览器会向服务器发出请求,直接访问该路径。
    • 由于服务器并不知道如何处理这个路径,它会返回一个 404 错误。特别是在 GitHub Pages 或类似的静态文件托管服务中,服务器没有对应的路由处理逻辑来为这些路径提供内容。
  3. 静态页面部署的限制:
    • 现代前端框架(如 Vue.js)依赖 JavaScript 来动态渲染页面,但静态托管服务(如 GitHub Pages)通常不会处理 SPA 的路由。因此,直接输入路由时,服务器无法返回相应的 HTML 文件,而是返回 404 错误。

解决方案:

为了解决这个问题,您可以采取以下两种方法:

  1. 确保服务器的所有路由都指向 index.html(SPA 解决方案)

由于您的项目是单页应用(SPA),所有路径最终都应该返回 index.html,然后由前端的 Vue Router 处理实际的页面渲染。为此,您需要配置一个 404 重定向路径重写,将所有未找到的路径都指向 index.html

  1. GitHub Pages 的工作原理是它只会返回静态资源,但它并不会自动处理 Vue Router 的路径重定向。为了解决这个问题,可以通过以下方式实现:
  • public 目录下创建一个 .nojekyll 文件(如果不存在)。

  • public 目录下创建一个 404.html 文件,并让它重定向到 index.html。以下是 404.html 的内容示例:

    <!DOCTYPE html>
    <html>
    <head>
    <meta http-equiv="refresh" content="0; url=/" />
    </head>
    <body>
    <p>Page not found. Redirecting to <a href="/">home</a>...</p>
    </body>
    </html>
  • 这会将所有 404 错误自动重定向到首页,Vue Router 会接管路由处理,确保页面渲染正确。

2. 使用 Hash 模式(不推荐,但简单)

另一种方法是切换到 hash模式(默认情况下,Vue Router 使用的是 history 模式),这样就不会依赖服务器的路径,而是依赖 URL 的哈希部分(例如 /#/login)。这样可以避免直接访问页面时返回 404 错误。

修改 router/index.ts 中的代码:

import { createRouter, createWebHashHistory } from 'vue-router';

const router = createRouter({
history: createWebHashHistory(), // 使用 hash 模式
routes
})

使用哈希模式后,URL 会变成类似 https://youruilin.github.io/vue3-ts-shuangzhao/#/login,这样就不会依赖服务器的路径处理,避免了 404 错误。

推荐的解决方法:

由于您是使用 Vue 3 和 Vite,推荐 使用第一种方案,即让服务器始终返回 index.html,然后由 Vue Router 来处理路径。

  • 在生产环境中,使用 createWebHistory(basePath) 配置 Vue Router 并确保服务器处理路径时始终返回 index.html,让 Vue Router 在客户端接管路由。
  • 对于 GitHub Pages,您可以通过创建 404.html 文件来实现路径重定向。

总结:

  • 使用 history 模式时,直接输入路径会导致 404 错误,因为服务器没有相应的路由处理。这时需要设置服务器将所有请求指向 index.html
  • 使用 GitHub Pages 部署时,确保设置了 404 页面来重定向到 index.html,然后由 Vue Router 来处理实际的路由渲染。