mook_前中台项目QA

75 阅读19分钟

1.为什么用computed,依赖项注意点;2.正则语法,i.test();3.UA解释;4.join语法

1 判断移动端的代码解读

助手回复:

代码解析

import { computed } from 'vue'

export const isMobileTerminal = computed(() => {
  // 方式1:UA字符串检测设备
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
    navigator.userAgent
  )
})
  • 用途:Vue3 computed 计算属性,通过 navigator.userAgent 用户代理字符串判断是否为移动端设备。
  • 正则:匹配安卓、苹果手机/平板、黑莓、老移动浏览器标识,i 忽略大小写。

⚠️关键问题

  1. computed 不会自动响应UA navigator.userAgent 不是vue响应式变量,切换设备/模拟器,不会自动重新计算,只有组件重新执行setup才会更新,适合页面初始化一次性判断,不适合窗口拖拽实时切换设备场景。

  2. iPadOS 13+坑点 新版iPad平板UA返回Macintosh,不再携带iPad关键字,该正则识别不到新版iPad,会误判为PC端。

  3. SSR/SSG报错 服务端没有navigator对象,Nuxt等SSR项目直接运行会直接报错,需要套process.client / onMounted浏览器环境判断。

两种主流完整版本

版本1 UA检测(你的原版,兼容注意点)

import { computed } from 'vue'

// 仅浏览器环境执行
export const isMobileTerminal = computed(() => {
  if (typeof navigator === 'undefined') return false
  const ua = navigator.userAgent
  // 兼容新iPad:MacIntel + 触摸屏
  const isNewIpad = navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua) || isNewIpad
})

版本2 视口宽度判断(常用方案,vueuse)

按屏幕断点区分,跟随窗口大小实时响应,适合响应式布局,不检测设备,只看屏幕尺寸。

import { computed } from 'vue'
import { useWindowSize } from '@vueuse/core'
const { width } = useWindowSize()
const PC_WIDTH = 1200

export const isMobileTerminal = computed(() => width.value < PC_WIDTH)

使用示例

<script setup>
import { isMobileTerminal } from '@/utils/device'
</script>

<template>
  <div v-if="isMobileTerminal">移动端布局</div>
  <div v-else>PC端布局</div>
</template>

选型建议

  • 需要识别真实物理设备(手机/平板访问):用UA方案,注意iPad兼容、SSR处理。
  • 只做页面响应式布局:优先用窗口宽度判断(vueuse)。

补充:UA可以被浏览器插件篡改,不能100%可靠。

需要我给你写一个nuxt3可用的安全版本吗?


2 用户:详细解释,不懂为什么用computed,正则怎么匹配,i.test啥意思

用户输入:详细解释,不懂为什么用computed,正则怎么匹配,i.test啥意思

助手回复:

逐行拆解完整代码

import { computed } from 'vue'

export const isMobileTerminal = computed(() => {
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
    navigator.userAgent
  )
})

我们分成 4 块讲:

  1. computed 是什么,这里为什么要用它
  2. /Android|webOS|.../i 正则每一部分含义
  3. .test() 是什么
  4. navigator.userAgent是什么
  5. export const

1. computed 计算属性

computed(()=>{ ... })

computed:Vue 的计算属性,返回一个响应式对象 .value

普通函数:

//普通函数,调用才执行,返回普通布尔值 true/false
function getIsMobile(){
  return xxx
}

computed 包装之后:

export const isMobileTerminal = computed(()=>{...})
  • 返回的不是普通布尔,是一个ref包装对象,使用要写 isMobileTerminal.value
  • 在模板 <template> 里使用,不用写 .value,vue自动解包
<template>
  <div v-if="isMobileTerminal">手机端</div>
</template>

❗那这里有一个大坑(重点)

这里 computed 内部读取的是 navigator.userAgent,这个不是Vue响应式数据! 👉 computed 只有内部依赖的响应式变量变化,才会重新执行函数。 navigator.userAgent是浏览器原生API,不是响应式,所以这个computed只会执行1次!组件初始化跑一次,后面不会更新。

那为什么还写 computed,不直接写普通函数?

  1. 模板中可以直接使用,语法统一;
  2. 如果以后内部改成依赖响应式变量(比如窗口宽度),computed会自动更新;
  3. 代码习惯:把需要在模板使用的布尔判断统一包成computed;

注意:当前这段代码,computed不会自动更新,只是一次性求值。


2. 正则表达式

/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
  • 前后两个 / 是JS正则字面量语法,代表正则的开始和结束
  • | 代表或,匹配任意一个字符串即可

等价语义:匹配字符串中包含 Android 或者 webOS 或者 iPhone……任意一个

  • 末尾的 i:正则修饰符,ignoreCase 忽略大小写 比如 UA 里面出现 android 小写、IPHONE大写,也照样匹配成功。

举例子: /a|b/i 匹配 A、a、B、b

正则逻辑翻译成人话

在目标字符串里面,只要出现下面任意一个单词,就匹配成功 Android、webOS、iPhone、iPad、iPod、BlackBerry、IEMobile、Opera Mini


3. .test(字符串)

正则.test(要检测的字符串)

test() 是正则对象自带方法:

  • 在传入的字符串中查找能不能匹配正则规则
  • 返回布尔值:true匹配成功,false匹配失败

示例:

const reg = /iphone|android/i
reg.test("iphone 15")   // true
reg.test("Android 14")  // true
reg.test("Windows pc")  // false

所以代码:

/xxx/i.test(navigator.userAgent)

含义:拿浏览器UA字符串,用正则去检测,是否包含手机设备关键字,返回true/false。

4. navigator.userAgent

navigator 浏览器BOM原生对象,只有浏览器环境才有,node/ssr没有。 navigator.userAgent:用户代理字符串,浏览器会上报自己设备、系统信息的长字符串。

👉安卓手机的ua片段示例: Mozilla/5.0 (Android 14; Mobile) ... 👉苹果iphone: Mozilla/5.0 (iPhone; CPU iPhone OS 17 ...)

JS读取这个字符串,用正则判断里面有没有手机设备关键词,来区分手机还是电脑。

5. export const isMobileTerminal = xxx

export:导出变量,别的文件可以import导入使用

//别的文件
import {isMobileTerminal} from '@/utils/xxx'

完整伪代码翻译整段代码

//导入vue计算属性
import { computed } from 'vue'

//导出一个响应式计算属性变量 isMobileTerminal
export const isMobileTerminal = computed(() => {
  // 1.拿到浏览器的设备信息字符串 navigator.userAgent
  // 2.用正则去检测这个字符串是否包含任意手机设备关键词
  // 3.test返回true就是移动端;false就是pc端
  return 匹配结果true/false
})

常见误区再总结

  1. computed 不是写了就会自动更新!只有里面用到的Vue响应式数据改变才会重跑。这里navigator不是响应式,只会跑一次。
  2. i修饰符:忽略大小写,防止UA大小写错乱匹配失败
  3. |正则是或;.test()返回布尔
  4. 新版iPadOS的userAgent不再出现iPad字段,这段代码识别不出新版iPad,会判定成PC
  5. SSR环境(Nuxt)服务端没有navigator,直接运行直接报错。

小测试demo,你可以复制浏览器控制台运行

const reg = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
console.log(reg.test(navigator.userAgent))

如果你需要,我可以给你写一段不带vue的原生JS版本,对比看,更容易看懂。


3 用户:UA是啥

用户输入:UA是啥

助手回复:

UA = User Agent 用户代理

就是浏览器自我介绍的一大串字符串,浏览器告诉网站:我是什么浏览器、什么系统、什么设备。

navigator.userAgent 拿到的就是 UA 字符串。

举真实例子

📱 iPhone手机 UA(简化)

Mozilla/5.0 (iPhone; CPU iPhone OS 18_0 like Mac OS X) AppleWebKit/605.1.15 …

里面出现单词 iPhone,我们代码正则抓到 iPhone → 判断是移动端 ✅

📱安卓手机 UA(简化)

Mozilla/5.0 (Android 15; Mobile; xxx ) Chrome/128.0 …

里面有 Android,正则匹配成功 →移动端 ✅

💻 Windows电脑Chrome UA

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 …

里面没有 Android / iPhone 这些关键词 →返回 false,判定PC端 ✅

📌新版iPad大坑

iPadOS13之后,iPad的UA不再写iPad,写的是MacIntel

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15) AppleWebKit …

看不到iPad字眼,老正则就会把iPad识别成电脑,这就是之前说的bug。


UA能干什么?

网站拿到UA字符串,可以做这些事:

  1. 判断是手机还是电脑(咱们现在写的逻辑)
  2. 判断你用的什么浏览器:Chrome、Safari
  3. 判断操作系统:Windows、安卓、iOS

⚠️UA缺点(很重要)

  1. 可以被篡改:浏览器插件、模拟器可以手动修改UA字符串,伪装成手机/电脑,所以UA检测不是100%靠谱。
  2. SSR(Node环境)没有这个东西,只有真实浏览器才有。
  3. 设备厂商会不断更新UA字符串,老正则有可能以后失效。

快速看自己浏览器UA

打开浏览器F12控制台,粘贴运行:

console.log(navigator.userAgent)

就可以打印出你本机完整UA字符串。


回到之前代码一句话总结

/Android|webOS|iPhone.../i.test(navigator.userAgent)

拿浏览器自我介绍(UA)那一大段文字,看看里面有没有手机设备关键词,有就是移动端。

对比两种判断思路

  1. UA检测:看「设备身份介绍」 →你是什么设备
  2. 窗口宽度检测(vueuse):看「屏幕多大」 →屏幕小就当移动端,不管你是电脑还是平板

业务做响应式页面,优先用屏幕宽度,不要迷信UA。UA适合统计设备类型。


4 用户:join啥意思,语法

用户输入:join啥意思,语法

助手回复:

path.join()

'@': join(__dirname, '/src')

join 来自 node 的内置模块 path,专门用来拼接文件路径。 完整写法:const path = require('path') 或者 import path from 'path' 实际是:path.join(__dirname, '/src')

先拆开两个关键字:

  1. __dirname:node 内置变量,当前这个配置文件所在文件夹的绝对路径
  2. path.join(路径1,路径2,路径3...):拼接路径,自动处理 / 和 \ 斜杠,兼容 windows / mac /linux

1. __dirname 是什么

假设你的项目:

D:/code/my-project/vite.config.js

在 vite.config.js 文件里面打印 __dirname

console.log(__dirname)
//输出: D:/code/my-project

✅就是当前文件所在目录的完整绝对路径。

注意:浏览器环境没有 __dirname,只有 Node(vite/webpack配置文件运行在node)才有。

2. path.join() 语法

path.join(a,b,c...)

作用:把多个路径片段拼接到一起,自动帮你处理斜杠,不用手动纠结写 / 还是 \。

Windows 路径分隔符是 \,Mac/Linux 是 /。手写字符串拼接很容易写错。 path.join 会根据操作系统自动选正确分隔符。

示例

path.join('D:/code/my-project','/src')
// → D:\code\my-project\src   (windows)
// → D:/code/my-project/src   (mac)

哪怕你多写斜杠、少写斜杠都没事:

path.join('D:/a', 'b')     // D:/a/b
path.join('D:/a/', '/b')   // D:/a/b  自动去掉重复斜杠

3. '@': path.join(__dirname, '/src') 完整含义

这是 vite/webpack 的路径别名配置。

{
  resolve: {
    alias: {
      '@': path.join(__dirname, '/src')
    }
  }
}

含义:

当代码里写 @/components/xxx.vue 把 @ 替换成 项目根目录/src 的绝对路径

等价映射:

@  → D:/code/my-project/src
@/components → D:/code/my-project/src/components

业务代码中:

import Hello from '@/components/Hello.vue'
//等价于
import Hello from 'D:/code/my-project/src/components/Hello.vue'

好处:

  1. 不用写一长串 ../../ 相对路径
  2. 文件移动位置,导入路径不会乱

❗容易踩坑点

  1. path.join 返回的是真实硬盘绝对路径字符串
  2. 只在 node 配置文件(vite.config、webpack.config)使用;vue、ts业务代码不能直接用 __dirname
  3. vite现在也可以用 import.meta.url 替代 __dirname(esmodule模式)

补充对比

  • path.join():拼接路径片段,普通拼接
  • path.resolve():会从根目录解析,支持 ../ 向上找目录
path.join(__dirname,'/src')
path.resolve(__dirname,'src')
//这个场景两者结果一样

极简总结

path.join(__dirname, '/src')
  • __dirname:当前配置文件所在文件夹完整路径
  • join:安全拼接路径,自动处理不同系统的斜杠
  • 整体得到项目中 src 文件夹的绝对路径,用来设置别名 @。

所以写 @ 就等价于直接指向 src 文件夹。


4.Q:v‑if和v‑show区别

A:# v‑if vs v‑show

核心一句话

  • v‑if:条件销毁/重建 DOM;条件为假时直接没有这个DOM节点
  • v‑show:条件控制 CSS display:none;DOM始终存在,只是看不见
<!-- v‑if 为false:直接从页面删掉这个元素 -->
<div v-if="ok">hello</div>

<!-- v‑show 为false:元素还在,style="display: none" -->
<div v-show="ok">hello</div>

详细对比

对比项v‑ifv‑show
原理条件为false,销毁DOM;true时重新创建DOMDOM始终保留,切换 display CSS属性
切换开销切换开销大(创建/销毁组件、触发生命周期)切换开销小,只是改样式
初始渲染开销条件false:初始不渲染,开销很小不管真假,DOM都会渲染出来,初始有开销
支持v‑else/v‑else‑if✅支持❌不支持
支持<template>✅可以用<template v-if>分组,不生成真实DOM❌<template v-show>无效
组件生命周期条件切换会触发 created/mounted/unmounted不会触发组件销毁重建钩子

使用场景

  1. 一次性条件判断,很少切换 → 用 v‑if 比如权限控制、页面模块,打开页面就基本不会变。
<div v-if="isAdmin">管理员面板</div>
  1. 频繁显示隐藏(弹窗、tab切换) → 用 v‑show 频繁来回切换,避免反复创建销毁DOM,性能更好。
<div v-show="isPopupOpen">弹窗内容</div>

坑点注意

  1. v‑if 和 v‑for 不要放同一个标签上,v‑for优先级更高,会造成性能问题;
  2. v‑if 为false,内部组件不会mount;v‑show隐藏时组件已经mount完成,只是看不见;
  3. <template>只能搭配v‑if做分组,不能用v‑show。

示例 template分组

<template v-if="ok">
  <p>段落1</p>
  <p>段落2</p>
</template>

不会多套一层div,满足一组元素一起条件渲染。

记忆口诀

少变用v‑if,多变用v‑show;if删DOM,show藏样式。

需要我顺带讲下源码层面简单原理吗?

5.Q:axios是啥,常见代码

A:# axios 是什么 axios 是一个基于 Promise 的 HTTP 请求库,专门用来发 ajax 请求,前端向后端拿数据/提交数据。

  • 浏览器端:封装 XMLHttpRequest
  • Node端:封装 http 模块
  • 返回 Promise,支持 async/await,比原生 fetch、XMLHttpRequest 好用很多

不是浏览器自带API,需要安装。

安装

npm install axios
# 或者 yarn add axios

常用写法

1. 简单 get 请求(获取数据)

import axios from 'axios'

// 方式1 .then 链式
axios.get('/api/user', {
  params: { id: 1 } // url查询参数 ?id=1
}).then(res => {
  console.log(res.data) // 后端返回的数据
}).catch(err => {
  console.error(err)
})

// 方式2 async await(项目最常用)
async function getUser() {
  try {
    const res = await axios.get('/api/user', { params: { id: 1 } })
    console.log(res.data)
  } catch (err) {
    console.error(err)
  }
}

2. post 请求(提交数据,新增/表单)

async function addUser() {
  const data = { name:'张三', age:20 } // 请求体,json格式
  const res = await axios.post('/api/user', data)
  console.log(res.data)
}

put(修改)、delete(删除)

await axios.put('/api/user/1', { name:'李四' })
await axios.delete('/api/user/1')

⭐创建实例(项目必用,统一配置baseURL)

不要每次请求写完整地址,创建实例统一管理:

// request.js
import axios from 'axios'

const service = axios.create({
  baseURL: '/api', // 接口基础地址
  timeout: 5000    // 请求超时时间5秒
})

// 请求拦截器:发请求之前做处理,比如带上token
service.interceptors.request.use(config => {
  // 从localStorage拿token放到请求头
  const token = localStorage.getItem('token')
  if(token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})

// 响应拦截器:拿到后端返回统一处理
service.interceptors.response.use(
  response => {
    return response.data // 直接把data吐出去,组件不用每次 .data
  },
  error => {
    // 统一错误处理,比如401未登录跳转登录页
    console.log('请求出错', error)
    return Promise.reject(error)
  }
)

export default service

组件中使用:

import request from '@/utils/request'

async function fn() {
  // 自动拼接 baseURL → /api/user
  const data = await request.get('/user', { params:{id:1} })
  console.log(data)
}

response 返回对象结构

{
  data: {},        // 后端返回真实数据
  status: 200,     // http状态码
  statusText: "OK",
  headers: {},     // 响应头
  config: {}       // 请求配置
}

常见坑

  1. get 参数用 params;post 直接传对象,放第二个参数
  2. 跨域:前端不能直接解决跨域,需要后端配置,或者 vite/webpack配置代理
  3. post传表单格式(不是json)需要改 headers['Content‑Type']
  4. 拦截器一定要 return config,否则请求发不出去

快速记忆

  • get:拿数据,参数 params
  • post:提交数据,参数放请求体
  • 实例+拦截器是实际项目标准写法,几乎不会直接用原生axios.get

需要我顺带对比 axios vs fetch 的区别吗?

6.Q:跨域问题是什么?怎么解决?(用axios举例说明)

A:# 跨域是什么 浏览器的同源策略:协议、域名、端口,三者有一个不一样,就是跨域,浏览器会拦截响应。

同源:http://localhost:5173 访问 http://localhost:5173/api ✅ 跨域:前端 http://localhost:5173,后端 http://localhost:3000 ❌ 浏览器不是不让发请求,请求发出去了,后端收到了,但是浏览器把返回结果拦截掉。

⚠️跨域是浏览器安全限制,node环境发axios没有跨域问题。

三种解决方式

  1. 后端配置CORS(生产环境首选)
  2. 前端开发环境代理(vite/webpack,仅本地开发用)
  3. jsonp(只支持get,现在基本不用)

方式1:后端开启CORS(上线真正用)

后端设置响应头,告诉浏览器允许跨域访问。 前端axios代码不需要改任何东西。

// axios直接请求后端完整地址
const service = axios.create({
  baseURL: 'http://127.0.0.1:3000'
})

跨域报错由后端处理,前端不用处理。


方式2:Vite开发环境代理(本地调试,只在npm run dev生效,打包上线失效)

重点:代理是vite服务器帮你转发请求,绕过浏览器同源策略。 ⚠️打包之后代理配置失效,上线还是需要后端CORS。

vite.config.js配置

import { defineConfig } from 'vite'

export default defineConfig({
  server: {
    proxy: {
      // 匹配 /api 开头的请求
      '/api': {
        target: 'http://127.0.0.1:3000', // 真实后端地址
        changeOrigin: true, // 开启跨域模拟
        rewrite: (path) => path.replace(/^\/api/, '') // 去掉路径上的/api前缀
      }
    }
  }
})

axios代码

❌不要写完整后端地址!!

// request.js
const service = axios.create({
  // 开发时写/api,不要写http://127.0.0.1:3000
  baseURL: '/api',
  timeout: 5000
})

浏览器发送:http://localhost:5173/api/list vite代理转发:http://127.0.0.1:3000/list

不写rewrite的情况:后端接口本身就带/api,就不要替换 target + '/api/list'

常见踩坑

  1. 代理只本地dev有效,build打包后完全没用,上线必须后端CORS
  2. axios baseURL不能写target完整地址,要写 /api
  3. 修改vite.config 必须重启开发服务器才生效

常见报错信息

Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been blocked by CORS policy

看到这个就是跨域。

面试一句话总结

  • 跨域是浏览器同源策略,协议、域名、端口不一致触发。
  • 生产环境:后端CORS
  • 开发环境:前端配置dev代理做请求转发
  • jsonp仅get,现在很少用。

补充:为什么不能前端axios直接解决跨域?

axios只是封装XMLHttpRequest,没有能力修改浏览器安全策略,真正解决跨域要么后端放行,要么中间服务器转发请求(vite代理)。

如果你需要,我可以给你对比webpack版代理写法。

7.Q:前端怎么测试接口是否正确,移动端怎么测试

A:# 前端测试接口 + 移动端测试

一、前端怎么测试接口是否正确

1. 后端工具(优先,不写代码)

  1. Postman / Apifox 输入接口地址、请求方式、params/body、token,直接发送,看返回数据、状态码。

作用:先确认后端接口本身有没有bug,排除后端问题,再去写axios代码。

流程:Apifox跑通接口 → 再写前端axios。避免上来就写代码,分不清是前端错还是后端错。

2. 浏览器Network面板(最常用)

浏览器F12 → Network,发起axios请求。 看4个关键点:

  1. Request URL:实际请求地址是否正确(代理有没有拼错)
  2. Request Method:get/post 是否写对
  3. Payload / Query String Parameters:看参数有没有传过去
  4. Response:后端返回真实数据;Status状态码
  • 200:正常
  • 400:参数错误
  • 401:token失效未登录
  • 404:接口地址写错
  • 500:后端代码报错
  • CORS报错:跨域问题

小技巧:Network勾选Preserve log,页面刷新不清空请求日志。

3. 代码层面测试 axios

// 直接在vue组件onMounted里临时写测试代码
import request from '@/utils/request'

onMounted(async ()=>{
  try {
    const res = await request.get('/list')
    console.log('接口返回:', res)
  } catch(e) {
    console.error('接口报错:',e)
  }
})

控制台看打印,区分:

  • 请求成功拿到数据
  • 请求失败看error.response(后端返回错误信息)

4. mock接口(后端还没写完)

  • vite插件:vite‑plugin‑mock
  • apifox 本地mock服务,前端直接调mock地址

面试话术:先使用接口调试工具确认接口可用性,再到浏览器network观察真实请求,核对地址、参数、响应,定位问题。


二、移动端怎么测试(Vue项目)

两种模式:模拟器、真机测试(真实手机)

方式1:浏览器模拟移动端(PC快速看样式,不能完全替代真机)

F12,点击左上角手机图标,切换设备,选择手机型号。

⚠️只能看页面布局,部分手机特有bug(滑动、输入框、软键盘、兼容性)复现不出来。

方式2:真机调试(重点,面试常问)

前提:电脑和手机连接同一个WiFi局域网

① vite 修改启动配置

vite.config.js

export default defineConfig({
  server:{
    host:'0.0.0.0', // 关键!允许局域网访问
    port:5173
  }
})

启动后终端会出现: Local: http://127.0.0.1:5173/ Network: http://192.168.x.x:5173/

把 Network那行地址复制到手机浏览器打开,手机就能访问电脑本地项目。

⚠️此时axios跨域问题: 手机访问局域网地址,vite代理依旧生效,不要写死后端完整ip地址,baseURL依旧写/api。

② 手机上查看控制台日志(手机看不到console)

两种方案:

  1. vconsole(项目引入,最常用)
npm install vconsole

main.js

import VConsole from 'vconsole'
// 只开发环境开启
if(import.meta.env.DEV){
  new VConsole()
}

手机页面右下角出现小v按钮,点开:可以看console日志、network网络请求、localStorage。 👉手机上直接看接口请求报错,不用插数据线。

  1. Chrome远程调试(安卓手机)
  • 手机开启开发者选项,USB调试;数据线连接电脑
  • chrome浏览器输入 chrome://inspect,可以直接在电脑F12调试手机页面。

iPhone真机调试:需要Mac电脑 + Safari浏览器。Windows不支持苹果真机调试。

打包后的H5测试(上线包)

  1. 打包 npm run build
  2. 本地预览dist包工具:serve
npm i serve -g
serve dist

手机同一局域网访问serve给出的network地址,测试打包后的真实效果。

移动端常见坑

  1. vite不写host:'0.0.0.0',手机打不开页面
  2. 手机电脑不在同一个wifi,访问不通
  3. vconsole记得生产环境关闭,不要带到线上
  4. 模拟器样式正常,真机出问题:css兼容、flex、1px边框、软键盘遮挡

面试简答总结

接口测试:先用Apifox/postman验证接口本身;再通过浏览器Network看真实请求,核对url、参数、状态码;代码打印结果排查。

移动端测试:

  1. PC浏览器设备模拟快速预览;
  2. 真机:vite配置host:0.0.0.0,手机电脑同一局域网访问局域网地址;引入vconsole在手机查看日志和网络请求;安卓可用chrome远程调试。