主题
三方登录与组件测试(二)
本页对应 三方登录与组件测试,共 6 段视频,约 1 小时 9 分。
学习目标
- 能配置开发环境与生产环境变量,并在组件、路由和 HTML 模板中动态设置页面标题与回跳域名,且通过启动开发/生产环境验证效果。
- 能使用 HTML 插件在
index.html中读取环境变量,并通过浏览器元素审查确认标题与回跳地址随环境正确切换。 - 能集成 eruda 实现真机调试,并通过环境变量判断仅在开发环境注入调试入口,在生产构建中验证入口不存在。
- 能使用 pm2 托管前端静态产物,开启 SPA 模式解决 history 路由刷新 404 问题,并熟练使用
pm2 list、pm2 delete管理服务。 - 能说明 GitLab 自动部署流程,识别
.gitlab-ci.yml、GitLab Runner、Node.js、pm2 与 Nginx 的作用,并演示推送代码触发自动部署及流水线回退。 - 能配置 Vite Mock 插件模拟接口,设置请求地址、方法、响应数据和延迟,并通过环境变量调整请求基址验证返回模拟数据。
知识点
本节要解决的问题
这一节要把项目里写死的域名,改成能随开发环境、生产环境自动切换的环境变量。涉及三处:支付回调地址、QQ 登录回跳地址、页面标题。核心思路是:代码里不再写死域名,而是从 import.meta.env 取变量;HTML 模板里则借助 vite-plugin-html 取变量。
先改支付回调地址
为什么需要:支付完成后要回到哪个地址,开发环境和生产环境不一样。写死会导致上线后仍跳回开发域名。
怎么想:组件里原来写死域名的位置,直接换成环境变量。
怎么做:找到 components 下的支付相关组件,把写死的回调域名替换为:
ts
import.meta.env.VITE_APP_CALLBACK这样开发环境取 .env.development 的值,生产环境取 .env.production 的值。
易错点:变量名要以 VITE_ 开头;改完 .env 文件后要重启开发服务器,否则可能读不到新值。
再改 QQ 登录回跳地址
为什么需要:QQ 登录成功后要回跳到当前环境的地址,开发和生产不能共用同一个回跳地址。
怎么想:QQ 登录链接里的 redirect_uri 是 URL 编码后的地址,所以不能只拼普通字符串,要先取环境变量再 encodeURIComponent。
怎么做:在登录页 index.vue 中找到 QQ 登录链接,把原来写死的 redirect_uri 改成动态拼接:
ts
const qqLoginUrl = `https://graph.qq.com/oauth2.0/authorize?redirect_uri=${encodeURIComponent(
import.meta.env.VITE_APP_GO_BACK + '/login/callback'
)}`转写稿里提到的“转码”就是 encodeURIComponent,不要漏掉,否则回跳地址里的 / 等字符会出问题。
易错点:VITE_APP_GO_BACK 这里按演示只放域名,后面再拼 /login/callback;如果变量里已经包含完整路径,就不要重复拼接。
让 index.html 也能读环境变量
为什么需要:index.html 不是普通的 JS/TS 环境,不能直接写 import.meta.env。但模板里的标题、回跳域名也要区分环境。
怎么想:用 Vite 插件把 HTML 模板变成可注入变量的模板。
怎么做:先安装插件:
bash
pnpm install vite-plugin-html -D在 vite.config.ts 中导入并注册:
ts
import { createHtmlPlugin } from 'vite-plugin-html'
export default defineConfig({
plugins: [
createHtmlPlugin()
]
})然后在 index.html 中用固定语法取变量,例如标题:
html
<title><%= VITE_APP_TITLE %></title>回跳地址同理,用 <%= VITE_APP_CALLBACK %> 这类写法替换原来写死的域名。
易错点:HTML 里不能直接写 import.meta.env;插件要先安装、再配置;模板语法是尖括号加百分号等号,不要写成普通插值。
路由切换时同步标题
为什么需要:页面初始化时 index.html 设置了标题,但路由切换后标题也要跟着变,否则会停留在旧标题。
怎么想:路由文件里原来拼接了写死的“优医问诊”,改成环境变量。
怎么做:在路由 index.ts 中把写死标题替换为:
ts
import.meta.env.VITE_APP_TITLE例如原来写 document.title = '优医问诊',现在改为读取环境变量。
易错点:JS/TS 里仍然用 import.meta.env,不要和 HTML 模板语法混用。
开发环境与生产环境验证
先配置两个环境文件,变量名保持一致、值不同:
bash
# .env.development
VITE_APP_TITLE=优医问诊-DEV
VITE_APP_CALLBACK=开发环境回跳地址
VITE_APP_GO_BACK=开发环境域名
# .env.production
VITE_APP_TITLE=优医问诊
VITE_APP_CALLBACK=生产环境回跳地址
VITE_APP_GO_BACK=生产环境域名开发环境执行 npm run dev,观察浏览器标题是否带 DEV,审查元素看 QQ 回跳链接是否变成开发域名;再创建订单走到支付,支付到一半放弃,仍会回跳,只是状态为支付失败,观察回跳域名是否为开发域名。
生产环境执行 npm run build 和 npm run preview,再看标题是否不带 DEV,QQ 回跳和支付回跳是否变为生产域名。
易错点:preview 预览的是构建产物,验证生产环境不要继续用 dev;支付回跳测试可以中途放弃,但要知道状态是失败,不是支付成功。
为什么真机需要调试面板
在 PC 浏览器里开发时,我们可以打开 Chrome DevTools 看控制台、审查元素、看网络请求。但一旦把页面放到手机浏览器里访问,手机端没有同样的开发者面板,出了问题很难定位。转写稿里把这层需求说得很直接:真机访问时“没有这个开发工具”,所以需要借助插件,在手机页面里出现一个设置图标,点开后能看到日志、审查元素、网络、资源等信息,体验接近 Chrome DevTools。这个工具就是 vConsole。转写里的“耳路达、卢达、alud”等都是同音转写,实际应写 vConsole。
怎么想:真机调试工具是给开发人员用的,不是给线上用户用的。因此它不能无条件出现在生产环境。正确思路是“引入 vConsole → 初始化 → 用开发/生产环境判断包起来 → 分别验证开发环境和生产环境”。
引入 vConsole 的方式
转写稿里对比了几种引入方式:npm 安装后直接从 node_modules 导入“并不是特别合理”;用国外 CDN 又可能比较慢;最后选择国内 BootCDN 上的压缩版 JS,在 index.html 里引入,再调用 vConsole.init()。可以写成:
html
<!-- index.html -->
<script src="https://cdn.bootcdn.net/ajax/libs/vConsole/3.15.1/vconsole.min.js"></script>然后在入口处初始化。视频里是在 index.html 下方写初始化代码;实际项目里也可以放到 main.ts,配合 Vite 的 import.meta.env.DEV 判断,类型和提示更清楚:
ts
// main.ts
if (import.meta.env.DEV) {
new VConsole()
}如果按视频思路放在 HTML 里做条件输出,核心同样是“只在开发环境输出初始化代码”,不要在生产构建里保留它。
易错点:CDN 地址要选国内可访问的 BootCDN;不要把 new VConsole() 写成 vConsole.init() 后忘记实例化;真机访问开发服务时要用电脑局域网 IP,不要用手机上的 localhost;手机和电脑要在同一网络,端口要可访问。
用环境变量区分开发与生产
转写稿前面已经配置了 .env.development 和 .env.production,并在代码里通过 import.meta.env 读取环境变量;在 HTML 模板里则用 Vite 插件的 %VITE_xxx% 语法。真机调试也要复用这套判断:开发环境才输出 vConsole,生产环境不输出。
为什么需要:如果生产环境也带 vConsole,用户会看到调试入口,既不专业也不安全。怎么想:不要靠注释手动开关,而是让构建环境决定。怎么做:在开发环境判断为真时初始化;生产打包后检查页面右下角是否还有 vConsole 图标。
ts
if (import.meta.env.DEV) {
new VConsole()
}易错点:import.meta.env.DEV 是布尔值,不要写成字符串 'false' 再判断;生产构建后要重新预览 dist,不能只看开发服务器;如果使用 HTML 模板变量,要确认变量名和 .env 文件一致。
验证与调试过程
转写稿的验证步骤是:开发环境刷新页面,右下角出现设置图标,点开能看到控制台;再打包生产环境,用 npm run build 后 npm run preview 预览,访问生产地址,确认 vConsole 图标不存在。调试时点“审查元素”的小箭头,再点“我的处方”等区域,就能看到对应 DOM 和样式;点小眼睛可以查看样式。这样在手机上也能完成基本排查。
易错点:开发环境能看到图标、生产环境看不到,才算成功;如果生产环境仍有图标,优先检查初始化代码是否被条件包住、是否被缓存;真机调试完记得提交代码,转写稿最后也执行了代码提交。
先打包:为什么不能直接上传源码
为什么需要:pm2 托管的是前端构建后的静态资源,不是 Vite 源码。源码里有 .vue、.ts、依赖配置,服务器直接用 pm2 启动静态服务时无法像开发服务器那样编译这些文件。
怎么想:部署前端项目的第一步不是装 pm2,而是先得到可被浏览器直接访问的 dist 产物。转写稿中也是先执行构建,再进入 dist 目录托管。
怎么做:在项目根目录执行构建命令:
bash
npm run build构建完成后会生成 dist 目录,里面的 index.html、JS、CSS、图片等才是要上传的资源。
易错点:不要上传整个项目目录让 pm2 直接托管;也不要在项目根目录执行 pm2 serve ./,否则入口可能不是构建后的 index.html。本节笔记未附带截图,以下保留转写稿中的关键命令代码。
上传服务器并安装 pm2:为什么服务器要有 Node 环境
为什么需要:pm2 是基于 Node 环境的命令行工具,服务器没有 Node/npm 就无法全局安装 pm2。
怎么想:转写稿采用“本地模拟服务器”的方式演示:先把 dist 产物上传到服务器目录,再在服务器上安装 pm2。真实服务器一般是 Linux,可用 Xftp 这类工具上传。
怎么做:上传 dist 后,在服务器执行:
bash
npm install pm2 -g安装完成后即可使用 pm2 命令。
易错点:-g 表示全局安装,漏掉后可能在任意目录无法直接使用 pm2;上传时要确认上传的是 dist 里的内容,而不是外层文件夹套错层级。
用 pm2 serve 启动静态服务:为什么要指定目录、端口和名称
为什么需要:pm2 需要知道托管哪个目录、监听哪个端口,并给服务起一个可管理的名称。
怎么想:进入服务器上的 dist 目录后,用 pm2 serve 启动静态服务。./ 表示当前目录,8080 是端口,--name 给服务命名,方便后续查看和删除。
怎么做:
bash
cd dist
pm2 serve ./ 8080 --name my-cp-server启动后访问 http://localhost:8080,能打开页面说明静态资源已被托管。
易错点:命令必须在资源根目录执行;端口不要和已有服务冲突;服务名要记住,后续删除、重启都靠它。
history 路由刷新 404:为什么要开启 SPA 模式
为什么需要:转写稿中的调试过程很关键:直接访问 localhost:8080 能打开首页,但进入 /login 后刷新页面出现 404。原因是刷新时浏览器会向服务器请求 /login 这个真实路径,而静态服务目录里并没有 login 文件。
怎么想:这是 history 路由的典型问题。单页应用应让所有前端路由都回退到 index.html,再由前端路由接管。pm2 的 serve 提供 --spa 参数来解决。
怎么做:先删除旧服务,再用 SPA 模式重启:
bash
pm2 list
pm2 delete my-cp-server
pm2 serve ./ 8080 --name my-cp-server --spa再次访问并刷新 /login,页面可正常展示。
易错点:不要只在首页测试;一定要进入子路由后刷新验证。--spa 要加在启动命令中,且修改启动参数后需要先删除旧服务再重新启动。
pm2 常用管理命令:为什么要会查看和删除服务
为什么需要:部署不是启动一次就结束,后续要确认服务是否存在、端口是否被旧进程占用、配置修改后如何重启。
怎么想:转写稿中用 pm2 list 查看服务列表,用 pm2 delete 服务名 删除旧服务,再重新启动。
怎么做:
bash
pm2 list
pm2 delete my-cp-server删除后再执行带 --spa 的 pm2 serve 命令。
易错点:pm2 delete 后面跟的是服务名,不是目录名;删除前先确认服务名,避免误删其他服务。至此,pm2 托管前端静态资源的手动部署流程完成:构建产物、上传服务器、安装 pm2、在 dist 目录启动服务,并用 --spa 解决 history 路由刷新 404。
代码示例
示例一:用环境变量区分开发/生产环境的标题与回跳域名
bash
# .env.development(开发环境)
VITE_APP_TITLE=优医问诊DEV
VITE_APP_CALLBACK=http://consult-patient-ithima.cnbash
# .env.production(生产环境)
VITE_APP_TITLE=优医问诊
VITE_APP_CALLBACK=http://cp.itheima.netts
// src/global.d.ts —— 给 import.meta.env 补类型提示
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
readonly VITE_APP_CALLBACK: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}ts
// vite.config.ts —— 注册 html 插件,让 index.html 能取到环境变量
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { createHtmlPlugin } from 'vite-plugin-html'
export default defineConfig({
plugins: [
vue(),
createHtmlPlugin(),
],
})html
<!-- index.html —— 标题用环境变量动态设置 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title><%= VITE_APP_TITLE %></title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>ts
// src/components/CPPay.vue 的 script setup 片段 —— 支付回跳地址
const callback = import.meta.env.VITE_APP_CALLBACK
// 支付组件里原来写死的域名替换成上面的 callback 即可
// src/views/Login/index.vue 的 script setup 片段 —— QQ 登录回跳地址
const redirectUrl = encodeURIComponent(
`${import.meta.env.VITE_APP_CALLBACK}/login/callback`
)
const qqLoginHref = `https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=xxx&redirect_uri=${redirectUrl}`
// src/router/index.ts —— 路由切换时同步修改标题
router.afterEach(() => {
document.title = import.meta.env.VITE_APP_TITLE
})关键行与易错点
.env.development/.env.production只在pnpm dev/pnpm build时分别生效,变量名必须以VITE_开头才会被注入到import.meta.env。index.html不是 JS 环境,不能直接写import.meta.env.xxx,必须先pnpm i vite-plugin-html -D并在vite.config.ts的plugins里注册createHtmlPlugin(),然后用<%= 变量名 %>取值。- QQ 回跳地址在原代码里是已经 URL 编码过的(带
%2F),所以要用encodeURIComponent再拼一次,否则地址里的/、?会破坏参数结构;拼完再接上/login/callback路径。 global.d.ts里补的ImportMetaEnv只是类型提示,漏写不会报错但会丢提示;title在页面初始化时由 HTML 设置,路由切换时还要在路由守卫里再设一次。
示例二:eruda 真机调试,仅在开发环境注入
bash
# .env.development
VITE_DEV=truebash
# .env.production
VITE_DEV=falsehtml
<!-- index.html —— 用 if 判断,只有开发环境才输出 eruda 代码 -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title><%= VITE_APP_TITLE %></title>
</head>
<body>
<div id="app"></div>
<% if (VITE_DEV === 'true') { %>
<script src="https://cdn.bootcdn.net/ajax/libs/eruda/2.4.1/eruda.min.js"></script>
<script>
eruda.init()
</script>
<% } %>
<script type="module" src="/src/main.ts"></script>
</body>
</html>关键行与易错点
- 引入地址用的是国内 BootCDN 上的
eruda.min.js,官方 GitHub 给的node_modules直接引入方式和国外 CDN 地址都不推荐(一个不合理、一个慢)。 - 外层必须包
<% if (...) { %> ... <% } %>,打包后生产环境的 HTML 里这段脚本会被整段移除,用户看不到右下角的设置图标。 - 判断别写成
if (VITE_DEV):环境变量从.env读出来是字符串,"false"也是真值,必须写成=== 'true'。 - 验证方式:
pnpm dev刷新页面能看到图标 →pnpm build后pnpm preview打开localhost:4173,图标消失即判断生效。
示例三:pm2 托管前端静态产物(模拟服务器部署)
bash
# 1. 本地打包,得到 dist 静态产物
pnpm build
# 2. 用 XFTP 之类的工具把 dist 上传到服务器后,在服务器上全局安装 pm2
npm i pm2 -g
# 3. 进入产物根目录,用 pm2 起静态服务
cd dist
pm2 serve ./ 8080 --name my-cp-server --spa
# 4. 常用管理命令
pm2 list # 查看当前托管的所有服务
pm2 delete my-cp-server # 删除名为 my-cp-server 的服务bash
# 不加 --spa 时刷新 404,需要删掉服务后按 SPA 模式重新启动
pm2 delete my-cp-server
cd dist
pm2 serve ./ 8080 --name my-cp-server --spa关键行与易错点
pm2 serve ./ 8080里的./表示用当前目录的文件启动,所以必须先cd dist,在产物根目录下执行;换目录名(不叫 dist)也要进到资源根目录。- history 路由(如
/login)直接访问时 JS 把地址栏改成/login不会发请求,但刷新会按该路径向服务器要资源,服务器没有对应文件就返回404 Not Found,所以必须加--spa,让任意路径都回落到index.html。 - 服务名
--name my-cp-server是后续pm2 list/pm2 delete的操作依据,名字写错就删不掉旧服务,端口会被占用。 - 服务器要先装好 Node 环境才能全局装 pm2;pm2 默认端口不是 80,外网访问还需 Nginx 做域名反向代理到
8080。
常见错误
现象:
index.html中直接写import.meta.env不生效或构建报错。
原因:HTML 模板不是 JS 模块环境,不能直接访问环境变量。
修法:安装并配置 HTML 插件,在模板中使用%%=环境变量名%%语法注入。现象:开发/生产环境切换后,标题或回跳地址仍是旧值。
原因:环境变量文件命名、变量名或启动模式不匹配。
修法:核对.env.development/.env.production中变量名一致,并分别用开发、生产命令启动验证。现象:QQ 登录回跳地址拼接后参数错乱或无法回跳。
原因:回跳地址未做 URL 编码,或直接拼接了未处理的域名。
修法:使用encodeURIComponent处理回跳地址,再拼接到redirect_uri参数中。现象:PM2 托管后首页正常,刷新
/login等路由出现 404。
原因:History 路由刷新时向服务器请求对应路径,静态服务没有该资源。
修法:使用pm2 serve ... --spa启动,确保任意路径回退到index.html。现象:Mock 接口没有生效,请求仍打到真实后台并拿到真实数据。
原因:请求工具拼接了真实baseURL,未指向本地开发服务器。
修法:Mock 调试时将请求地址切到当前开发域名,或调整baseURL指向本地服务。现象:生产环境仍出现 Eruda 调试入口。
原因:Eruda 初始化代码未加环境判断,被打包进生产产物。
修法:在 HTML 中用开发环境变量包裹 Eruda 引入与初始化代码,仅开发环境输出。
练习
- 分别启动开发环境和生产环境,检查页面标题、HTML 模板中的回跳地址、QQ 登录链接中的
redirect_uri是否随环境变化。 - 故意把
.env.production中某个变量名写错,观察页面表现,并说明如何定位该类问题。 - 使用 PM2 托管打包产物,先不加
--spa刷新子路由,再加上--spa对比结果,并记录相关命令。 - 配置一个 Mock 接口,设置
timeout,在组件中发请求并验证返回结构、网络请求地址和延迟效果。 - 在真机上打开页面,验证 Eruda 面板仅在开发环境出现,并说明如何确认生产产物中已移除调试代码。
课件图示






原视频对照
| # | 视频 | 时长 |
|---|---|---|
| 1 | 部署-开发环境与生产环境-02设置标题和回跳域名 | 13:03 |
| 2 | 部署-真机调试 | 07:47 |
| 3 | 部署-pm2托管 | 09:04 |
| 4 | 部署-gitlab自动部署演示 | 12:44 |
| 5 | 扩展-mock数据-01-模拟接口 | 13:37 |
| 6 | 扩展-mock数据-01-模拟接口 | 13:37 |
小结
- 环境变量要同时覆盖 JS/TS 代码、HTML 模板和路由标题设置,不能只改一处。
- HTML 模板中的环境变量必须借助插件注入,不能按普通 JS 方式读取。
- 回跳地址属于 URL 参数时,编码与拼接顺序要一起验证。
- Eruda 只应服务于开发调试,必须通过环境判断隔离生产环境。
- PM2 托管 History 路由项目时,
--spa是避免刷新 404 的关键。 - Mock 接口是否生效,重点看请求最终域名和
baseURL,不能只看接口路径。 - 自动部署链路要关注脚本、Runner、Node/PM2 环境和 Nginx 代理是否贯通。
- 每次环境切换后,都应按“标题、回跳、调试工具、接口请求、路由刷新”做一轮回归验证。