前言
在進(jìn)行移動(dòng)端webapp開發(fā)時(shí),你是否會(huì)想要在真機(jī)上調(diào)試項(xiàng)目。
下面分析一下本地運(yùn)行項(xiàng)目時(shí),真機(jī)調(diào)試需要的步驟和麻煩的點(diǎn)。
- 你需要將手機(jī)和運(yùn)行項(xiàng)目的電腦連接到同一局域網(wǎng)(連接同一個(gè)WiFi即可)。
- 查看電腦在局域網(wǎng)內(nèi)的ip地址(
windows在命令行輸入ipconfig)。 - 手機(jī)瀏覽器中輸入ip和端口打開項(xiàng)目。
然而局域網(wǎng)內(nèi)的ip地址不是固定的,項(xiàng)目運(yùn)行的端口也有可能由于端口占用導(dǎo)致端口不固定,所以存書簽可能會(huì)失效,輸入長串的ip地址和端口會(huì)比較繁瑣,如果要在多臺(tái)手機(jī)調(diào)試兼容問題就更讓人生無可戀了。這自然就產(chǎn)生了需求:在運(yùn)行項(xiàng)目時(shí),在控制臺(tái)輸出二維碼,掃碼訪問項(xiàng)目地址。
webpack插件
慣例,在造輪子之前去GitHub搜索一下有沒有已經(jīng)造好的輪子。果然,我不是第一個(gè)有這種需求的人。
devserver-qrcode-webpack-plugin是一個(gè)基于webpack的插件。但我平時(shí)開發(fā)項(xiàng)目使用的umi腳手架,但是我們都知道umi封裝的是webpack,所以webpack插件也是可以使用的,那我們就試試看行不行。
umi使用chain-webpack自定義webpack配置,安裝插件后,在.umirc.js添加如下配置即可
import qrcode from 'qrcode-webpack-plugin'
export default {
chainWebpack(config, { webpack }) {
config.plugin('qrcode')
.use(qrcode)
}
}
讓我們運(yùn)行試一下

沒有我們想要的二維碼輸出,看來該插件并不兼容umi,原因是什么呢,我們查看插件源碼
apply(compiler) {
const devServer = compiler.options.devServer
if (!devServer) {
console.warn('webpack-server-qrcode: needs to start webpack-dev-server')
return
}
const protocol = devServer.https ? 'https' : 'http'
const port = devServer.port
const _ip = this.getIPAdress()[0]
const url = `${protocol}://${_ip}:${port}`
this.printQRcode(url)
}
插件是從webpack的編譯器實(shí)例中的options中讀取DevServer的信息的,然而umi中webpack及DevServer的啟動(dòng)方式與直接使用webpack有很大的不同,所以compiler實(shí)例中并沒有DevServer的信息,所以該插件無法正常使用。
那么,下面就正式開始造輪子之路吧!
先拆解問題,要實(shí)現(xiàn)我們想要的效果,只需要重要的兩步:
- 獲取項(xiàng)目運(yùn)行的地址。
- 輸出包含ip地址和端口的二維碼。
獲取ip和端口
ip地址可以使用node的內(nèi)置模塊os模塊獲取,os模塊中的networkInterfaces方法可以獲取設(shè)備網(wǎng)卡的相關(guān)信息,包括IP地址和mac地址等。
// getIp.js
const os = require('os');
module.exports = () => {
const interfaces = os.networkInterfaces();
for (let interface of Object.values(interfaces)) {
for(let items of interface) {
if (items.family === 'IPv4' && !items.internal) {
// TODO: 可以完善的判斷條件,兼容多網(wǎng)卡和不同平臺(tái)設(shè)備
return items.address;
}
}
}
}
對(duì)于項(xiàng)目運(yùn)行的端口,使用過umi的都知道,直接從配置中或者環(huán)境變量process.env.PORT中獲取是不準(zhǔn)確的,因?yàn)?code>umi有一套端口選擇機(jī)制,當(dāng)指定的端口被占用時(shí),會(huì)自動(dòng)選擇其他的的端口運(yùn)行。umi內(nèi)部使用detect-port檢查端口,detect-port的原理很簡單,使用node內(nèi)置net模塊嘗試監(jiān)聽指定的端口,當(dāng)端口被占用時(shí)嘗試監(jiān)聽其他端口,在監(jiān)聽成功后馬上取消監(jiān)聽,并將端口號(hào)返回。
在外部沒有好的檢查方案,好在umi在今年5月的一個(gè)更新中,在afterDevServer事件中提供了devServerPort,這為插件獲取端口號(hào)提供了簡便的接口,具體可查看issue#2386。
插件中獲取port的基礎(chǔ)邏輯如下:
export default api => {
api.afterDevServer(({ server, devServerPort }) => {
console.log(devServerPort)
});
}
控制臺(tái)輸出二維碼
控制臺(tái)輸出二維碼可以使用qrcode-terminal,該庫的主要邏輯為:根據(jù)字符串生成二維碼每個(gè)位置的顏色信息二維數(shù)組。用字符'\u2588'表示縱向兩個(gè)白色,'\u2580'表示先白后黑,'\u2584'表示先黑后白,對(duì)于二維碼大圖,使用的是'\033[40m \033[0m'和'\033[47m \033[0m'。
想要了解細(xì)節(jié)的可以去查看其源碼github。
使用它輸出二維碼的代碼如下:
import qrcode from 'qrcode-terminal';
qrcode.generate(`http://${ip}:${port}`, { small });
umi-plugin-qrcode
整合上面兩步,umi-plugin-qrcode插件自然就成型了。獲取ip地址其實(shí)也不用自己寫,有許多js庫都有這個(gè)功能,實(shí)現(xiàn)思路都大同小異,并且考慮了其他邊界情況。
import address from 'address';
import qrcode from 'qrcode-terminal';
export default (api, { small = true, once = true } = {}) => {
let port;
let ip = address.ip();
api.afterDevServer(({ server, devServerPort }) => {
port = devServerPort;
});
api.onDevCompileDone(({ isFirstCompile }) => {
once
? isFirstCompile && qrcode.generate(`http://${ip}:${port}`, { small })
: qrcode.generate(`http://${ip}:${port}`, { small })
})
}
在umi中如何使用umi插件呢?(不是webpack插件)
- 安裝插件
yarn add umi-plugin-qrcode --dev
-
.umirc.js中配置插件
// .umirc.js
export default {
plugins: [
['umi-plugin-qrcode', {
small: false, // 二維碼大小,默認(rèn)small=true
once: false // 第一次編譯完成輸出(默認(rèn))或者每次編譯完成輸出
}]
]
}
由于上文所說的原因,插件支持的umi版本為2.7.0+。
運(yùn)行效果如下:
[圖片上傳失敗...(image-ca4766-1564117439093)]
【作者簡介】:葉茂,蘆葦科技web前端開發(fā)工程師,代表作品:口紅挑戰(zhàn)網(wǎng)紅小游戲、服務(wù)端渲染官網(wǎng)、微信小程序粒子系統(tǒng)。擅長網(wǎng)站建設(shè)、公眾號(hào)開發(fā)、微信小程序開發(fā)、小游戲、公眾號(hào)開發(fā),專注于前端領(lǐng)域框架、交互設(shè)計(jì)、圖像繪制、數(shù)據(jù)分析等研究。 一起并肩作戰(zhàn): yemao@talkmoney.cn 訪問 www.talkmoney.cn 了解更多。