1.方式1
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import basicSsl from '@vitejs/plugin-basic-ssl'export default defineConfig({plugins: [vue(),basicSsl({name: 'test',domains: ['192.168.15.166', 'localhost'], // 添加您的IPcertDir: './cert',}),],server: {host: '0.0.0.0',port: 3000,https: true,proxy: {'/api': {target: 'http://localhost:5000',changeOrigin: true,},},},build: {outDir: 'dist',assetsDir: 'assets',sourcemap: false,minify: 'terser',rollupOptions: {output: {chunkFileNames: 'js/[name]-[hash].js',entryFileNames: 'js/[name]-[hash].js',assetFileNames: '[ext]/[name]-[hash].[ext]',},},},
})
2.方式2
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import fs from 'fs'
import path from 'path'export default defineConfig({plugins: [vue(),],server: {https: {key: fs.readFileSync(path.resolve(__dirname, 'cert/key.pem')),cert: fs.readFileSync(path.resolve(__dirname, 'cert/cert.pem'))},host: '0.0.0.0',port: 3000,proxy: {'/api': {target: 'http://localhost:5000',changeOrigin: true},},},build: {outDir: 'dist',assetsDir: 'assets',sourcemap: false,minify: 'terser',},
})
這兩個 Vite 配置文件都實現了 HTTPS 服務,這是瀏覽器訪問攝像頭等敏感設備的必要條件。以下詳細解釋它們的原理和差異。
為什么需要 HTTPS
現代瀏覽器出于安全考慮,要求在以下情況下才能訪問攝像頭、麥克風等敏感設備:
- 使用 HTTPS 協議(安全連接)
- 或者是
localhost
域名(本地開發特例)
這是因為 getUserMedia()
API 被歸類為"強大功能"(Powerful Features),需要安全上下文(Secure Context)。
方式一:使用 @vitejs/plugin-basic-ssl 插件
plugins: [vue(),basicSsl({name: 'test',domains: ['192.168.31.179', 'localhost'],certDir: './cert',}),
]
原理:
- 自動生成證書:插件會自動為指定的域名生成自簽名 SSL 證書
- 證書存儲:生成的證書存儲在
./cert
目錄中 - 多域名支持:可以為多個域名/IP 生成證書(如局域網 IP 和 localhost)
- 自動配置:插件會自動將證書配置到 Vite 的 HTTPS 服務器
工作流程:
- 首次運行時,插件檢查證書是否存在
- 如果不存在,使用內置的證書生成器創建自簽名證書
- 將證書自動注入到 Vite 的 server 配置中
- 后續運行時復用已生成的證書
方式二:手動配置證書
server: {https: {key: fs.readFileSync(path.resolve(__dirname, 'cert/key.pem')),cert: fs.readFileSync(path.resolve(__dirname, 'cert/cert.pem'))},
}
原理:
- 手動管理證書:需要預先生成或獲取 SSL 證書文件
- 文件讀取:使用 Node.js 的
fs
模塊直接讀取證書文件 - 直接配置:將證書內容直接傳遞給 Vite 的 HTTPS 配置
證書生成方式(通常使用 OpenSSL):
# 生成私鑰
openssl genrsa -out key.pem 2048
# 生成證書
openssl req -new -x509 -key key.pem -out cert.pem -days 365
關鍵差異對比
特性 | 方式一(plugin-basic-ssl) | 方式二(手動配置) |
---|---|---|
便利性 | 高 - 自動生成和管理 | 低 - 需要手動創建證書 |
靈活性 | 中等 - 插件預設配置 | 高 - 完全自定義 |
證書類型 | 自簽名證書 | 可以使用任何證書(自簽名或CA簽發) |
多域名支持 | 內置支持,配置簡單 | 需要生成包含 SAN 的證書 |
首次設置 | 零配置,自動完成 | 需要手動生成證書 |
團隊協作 | 每個開發者自動生成 | 需要共享證書文件 |
證書更新 | 刪除舊證書即可重新生成 | 手動重新生成和替換 |
依賴性 | 需要安裝額外插件 | 僅需 Node.js 內置模塊 |
實際應用場景
方式一適合:
- 本地開發環境
- 快速原型開發
- 需要在局域網內多設備測試
- 團隊成員各自開發,不共享證書
方式二適合:
- 需要使用特定證書(如公司內部 CA 簽發)
- 生產環境的本地模擬
- 需要精確控制證書配置
- 已有證書管理流程的團隊
瀏覽器訪問攝像頭的實現
無論使用哪種方式,配置 HTTPS 后都可以使用以下代碼訪問攝像頭:
navigator.mediaDevices.getUserMedia({ video: true, audio: true
})
.then(stream => {// 獲取到媒體流videoElement.srcObject = stream;
})
.catch(err => {console.error('無法訪問攝像頭:', err);
});
注意事項
- 證書信任:兩種方式生成的自簽名證書都會導致瀏覽器警告,需要手動信任
- 局域網訪問:方式一通過配置 IP 地址,可以讓局域網內其他設備通過 HTTPS 訪問
- 安全性:這些配置僅適用于開發環境,生產環境應使用正規 CA 簽發的證書
總的來說,方式一更適合快速開發和原型驗證,方式二則提供了更多的控制權和靈活性。選擇哪種方式主要取決于項目需求和團隊的技術偏好。
這是使用自簽名證書進行本地開發時的常見問題。讓我詳細說明解決方案:
一、手機無法訪問的問題排查
1. 確保網絡連通性
// vite.config.js
server: {host: '0.0.0.0', // 允許外部訪問port: 3000,https: true,
}
檢查步驟:
- 確保手機和電腦在同一局域網
- 電腦防火墻允許 3000 端口
- 使用電腦的局域網 IP(如
https://192.168.31.179:3000
)
2. 防火墻設置
# Windows - 以管理員運行
netsh advfirewall firewall add rule name="Vite Dev Server" dir=in action=allow protocol=TCP localport=3000# macOS
sudo pfctl -d # 臨時關閉防火墻測試# Linux
sudo ufw allow 3000/tcp
二、手機端安裝證書的正確方法
iOS 設備
- 生成包含正確信息的證書:
# 創建證書配置文件 cert.conf
cat > cert.conf <<EOF
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no[req_distinguished_name]
C = CN
ST = State
L = City
O = Organization
CN = 192.168.31.179[v3_req]
keyUsage = critical, digitalSignature, keyAgreement
extendedKeyUsage = serverAuth
subjectAltName = @alt_names[alt_names]
DNS.1 = localhost
IP.1 = 192.168.31.179
IP.2 = 127.0.0.1
EOF# 生成證書
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \-keyout key.pem -out cert.pem -config cert.conf
- 安裝證書到 iOS:
- 將
cert.pem
文件通過郵件/AirDrop 發送到手機 - 點擊證書文件,選擇"安裝"
- 設置 → 通用 → 關于本機 → 證書信任設置
- 開啟對該證書的完全信任
Android 設備
- Android 安裝步驟:
- 將
cert.pem
重命名為cert.crt
- 傳輸到手機
- 設置 → 安全 → 加密與憑據 → 安裝證書 → CA 證書
- 選擇證書文件安裝
三、使用 mkcert 工具(推薦方案)
mkcert 可以創建本地受信任的證書,避免所有警告:
安裝 mkcert
# Windows (使用 Chocolatey)
choco install mkcert# macOS
brew install mkcert# Linux
apt install libnss3-tools
wget -O mkcert https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64
chmod +x mkcert
sudo mv mkcert /usr/local/bin/
配置本地 CA 并生成證書
# 1. 安裝本地 CA
mkcert -install# 2. 生成證書(包含所有需要的域名和 IP)
mkcert -cert-file cert.pem -key-file key.pem localhost 127.0.0.1 192.168.31.179 ::1# 3. 為手機生成 CA 證書
mkcert -CAROOT # 顯示 CA 證書位置
更新 Vite 配置
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import fs from 'fs'
import path from 'path'export default defineConfig({plugins: [vue()],server: {https: {key: fs.readFileSync('./key.pem'),cert: fs.readFileSync('./cert.pem')},host: '0.0.0.0',port: 3000,}
})
手機端信任 mkcert CA
- 獲取 CA 證書:
# 找到 CA 證書位置
mkcert -CAROOT
# 通常在:
# Windows: %LOCALAPPDATA%\mkcert
# macOS: ~/Library/Application Support/mkcert
# Linux: ~/.local/share/mkcert
- 將
rootCA.pem
發送到手機并安裝
四、使用 ngrok(外網訪問方案)
如果證書問題難以解決,可以使用 ngrok 提供的 HTTPS 隧道:
# 安裝 ngrok
npm install -g ngrok# 啟動本地服務后,創建隧道
ngrok http 3000# 會得到類似這樣的地址:
# https://abc123.ngrok.io
優點:
- 自動 HTTPS,無需證書配置
- 可以外網訪問
- 手機無需安裝證書
五、開發環境最佳實踐配置##
// vite.config.js - 完整的 HTTPS 配置方案
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import fs from 'fs'
import path from 'path'
import { execSync } from 'child_process'// 獲取本機局域網 IP
function getLocalIP() {const os = require('os')const interfaces = os.networkInterfaces()for (const name of Object.keys(interfaces)) {for (const iface of interfaces[name]) {if (iface.family === 'IPv4' && !iface.internal) {return iface.address}}}return '127.0.0.1'
}// 自動生成證書函數
function ensureCertificates() {const certDir = path.resolve(__dirname, 'cert')const keyPath = path.join(certDir, 'key.pem')const certPath = path.join(certDir, 'cert.pem')// 檢查證書是否存在if (fs.existsSync(keyPath) && fs.existsSync(certPath)) {console.log('? 使用現有證書')return { key: keyPath, cert: certPath }}// 創建證書目錄if (!fs.existsSync(certDir)) {fs.mkdirSync(certDir, { recursive: true })}const localIP = getLocalIP()console.log(`📱 局域網 IP: ${localIP}`)// 檢查是否安裝了 mkcerttry {execSync('mkcert -version', { stdio: 'ignore' })console.log('🔧 使用 mkcert 生成受信任的證書...')// 安裝本地 CA(如果還沒安裝)execSync('mkcert -install', { stdio: 'inherit' })// 生成證書execSync(`mkcert -cert-file ${certPath} -key-file ${keyPath} localhost 127.0.0.1 ${localIP} ::1`,{ stdio: 'inherit' })console.log('? mkcert 證書生成成功!')console.log('📱 手機端請安裝 CA 證書:')const caRoot = execSync('mkcert -CAROOT').toString().trim()console.log(` ${path.join(caRoot, 'rootCA.pem')}`)} catch (e) {console.log('?? mkcert 未安裝,使用 OpenSSL 生成自簽名證書...')// 創建 OpenSSL 配置const configPath = path.join(certDir, 'openssl.cnf')const opensslConfig = `
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no[req_distinguished_name]
C = CN
ST = State
L = City
O = Dev Organization
CN = ${localIP}[v3_req]
keyUsage = critical, digitalSignature, keyAgreement
extendedKeyUsage = serverAuth
subjectAltName = @alt_names[alt_names]
DNS.1 = localhost
DNS.2 = *.localhost
IP.1 = 127.0.0.1
IP.2 = ${localIP}
IP.3 = ::1
`fs.writeFileSync(configPath, opensslConfig)// 生成證書try {execSync(`openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ${keyPath} -out ${certPath} -config ${configPath}`,{ stdio: 'inherit' })console.log('? 自簽名證書生成成功!')console.log('?? 瀏覽器會顯示安全警告,需要手動信任證書')} catch (error) {console.error('? 證書生成失敗:', error)process.exit(1)}}return { key: keyPath, cert: certPath }
}// 生成或獲取證書
const certificates = ensureCertificates()export default defineConfig({plugins: [vue()],server: {https: {key: fs.readFileSync(certificates.key),cert: fs.readFileSync(certificates.cert)},host: '0.0.0.0', // 允許外部訪問port: 3000,// 顯示二維碼方便手機掃描open: false,// 配置 CORScors: true,// 代理配置proxy: {'/api': {target: 'http://localhost:5000',changeOrigin: true,secure: false,}}},// 構建配置build: {outDir: 'dist',assetsDir: 'assets',sourcemap: false,minify: 'terser',rollupOptions: {output: {chunkFileNames: 'js/[name]-[hash].js',entryFileNames: 'js/[name]-[hash].js',assetFileNames: '[ext]/[name]-[hash].[ext]',}}},// 優化配置optimizeDeps: {exclude: ['vue']}
})// 啟動后打印訪問信息
process.nextTick(() => {const localIP = getLocalIP()console.log('\n🚀 服務已啟動:')console.log(` 本地: https://localhost:3000`)console.log(` 局域網: https://${localIP}:3000`)console.log('\n📱 手機訪問提示:')console.log(' 1. 確保手機與電腦在同一 WiFi')console.log(' 2. 如有證書警告,點擊"高級"→"繼續訪問"')console.log(' 3. iOS 需在"設置"→"通用"→"關于本機"→"證書信任設置"中信任證書\n')
})
六、配套的 package.json 腳本
{"scripts": {"dev": "vite","dev:host": "vite --host","dev:network": "vite --host 0.0.0.0","dev:https": "node scripts/setup-https.js && vite","cert:install": "mkcert -install","cert:generate": "mkcert -cert-file cert/cert.pem -key-file cert/key.pem localhost 127.0.0.1 $(ipconfig getifaddr en0 || hostname -I | awk '{print $1}') ::1","cert:trust": "mkcert -install && npm run cert:generate","build": "vite build","preview": "vite preview","tunnel": "ngrok http 3000"},"devDependencies": {"@vitejs/plugin-basic-ssl": "^1.0.1","@vitejs/plugin-vue": "^4.2.3","vite": "^4.3.9","vue": "^3.3.4"},"dependencies": {"qrcode": "^1.5.3"}
}
七、快速解決方案總結
最簡單的方案(推薦)
- 安裝 mkcert
# 一次性設置,永久解決證書問題
brew install mkcert # macOS
mkcert -install
- 生成證書
mkcert -cert-file cert.pem -key-file key.pem localhost 192.168.xxx.xxx
- 手機端信任
- 將
~/.local/share/mkcert/rootCA.pem
(或對應系統路徑)發送到手機 - 安裝為受信任的 CA 證書
故障排查清單
? 網絡連接
- 手機和電腦同一 WiFi?
- 防火墻允許 3000 端口?
- 使用正確的 IP 地址?
? 證書配置
- 證書包含局域網 IP?
- 證書已安裝到手機?
- iOS 已在"證書信任設置"中啟用?
? Vite 配置
host: '0.0.0.0'
已設置?https: true
或手動配置證書?- 端口沒有被占用?
終極備用方案
如果以上都不行,使用 localtunnel 或 ngrok:
npx localtunnel --port 3000 --subdomain myapp
# 或
ngrok http 3000
這樣可以獲得一個臨時的公網 HTTPS 地址,無需任何證書配置,手機可以直接訪問。
主要缺點是需要依賴外部服務,并且速度可能較慢。但對于緊急測試和演示,這是最快的解決方案。