L
O
A
D
I
N
G

在开发h5移动端项目(v3+vant3+pinia+vite)时遇到的问题总结

-->

一.移动端适配和适配后内联样式没有生效

移动端适配

既然是整移动端项目,那么移动端适配是必须要整了,目前开发的这个项目就是使用的是rem方案rem方案的话比较常用的插件就是postcss-pxtorem+lib-flexible,至于为什么没用vw方案, vw 方案 还是有缺点的。如 vw 方案不适合大屏,因为 vw 是一个比例单位,随着屏幕尺寸变大,使用vw单位的元素、字体也越来越大。但我们肯定是希望在大屏上展示更多的内容,而不是更大的文字、图标。现在用大屏的用户也是挺多的,我自己感觉还是rem方案好一点点(手动狗头)

1
2
yarn add amfe-flexible
yarn add -D postcss-pxtorem

根目录下postcss.config.cjs

1
2
3
4
5
6
7
8
9
10
11
module.exports = {
plugins: {
'postcss-pxtorem': {
//如果你蓝湖上的UI图尺寸为375px,这里rootValue设置为37.5
//如果你蓝湖上的UI图尺寸为750px,这里rootValue设置为75
//蓝湖上的UI图的尺寸可以调节的,移动端一般用的37.5
rootValue: 37.5,
propList: ['*'],
},
},
};

main.ts中

1
import 'amfe-flexible';

内联样式没有生效

整完这些后,开发着开发着,用vant样式没事,自己写的样式也嘎嘎生效,突然有个dom元素需要加个动态样式,或者图片需要设置宽高,这就需要使用到内联样式,然后就发现了设置的内联样式不生效,弔!

解决方法也挺简单,就是在main.ts中定义一个全局方法,用来把px转化成rem:
main.ts中加上

1
2
3
4
5
6
7
8
const px2rem = (px:any) => {
if(/%/ig.test(px)){ // 有百分号%,特殊处理,表述pc是一个有百分号的数,比如:90%
return px
}else{
return (parseFloat(px) / 37.5) + 'rem'
}
}
app.config.globalProperties.$px2rem = px2rem // 放到全局

使用:

1
2
<div :style="{marginBottom:(ismarginBottom? $px2rem('42px'):$px2rem('16px'))}">
</div>

二.H5 IOS input 聚焦时,页面整个被推上去了,键盘收起页面未下移 BUG

遇见的场景

vue基于vant3框架开发移动端项目时,出现了这样一个问题:在手机上点击页面输入框时唤起手机自带键盘,结果顶部固定的导航栏也被顶起,导致遮挡住页面部分内容

解决方式

给相应的输入框一个聚焦的事件:

1
2
3
4
5
6
7
8
9
<van-field
v-model="cardForm.topUpamount"
label="¥"
:rules="rulesFrom.amount"
placeholder=""
:clearable="true"
type="number"
@focus="changefocus"
>

这个可以放在utils的index.ts下,导出这个方法,在需要的页面使用

如果这个输入框不是在vant组件的弹窗:

1
2
3
4
5
6
7
8
9
10
// ios键盘唤起,键盘收起以后页面不归位bug解决
const changefocus = () => {
const u = navigator.userAgent
const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
if(isIOS){
setTimeout(() => {
window.scrollTo(0, 0)
}, 200)
}
}

如果这个输入框是在vant组件的弹窗,例如动作面板弹窗:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// ios键盘唤起,键盘收起以后页面不归位bug解决
const changefocus = () => {
const u = navigator.userAgent;
const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
if (isIOS) {
setTimeout(() => {
document.body.scrollTop = document.body.scrollHeight;
}, 200);
}
};
const changeblur = () => {
const u = navigator.userAgent;
const isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
if (isIOS) {
document.body.scrollTop = 0;
window.scrollTo(0, 0);
}
};

三.状态调接口轮询

如充值,提现这种场景时,三方那边返回的结果可能需要比较久,这个时候就需要轮询着去结果,根据轮询的结果展示不同的需要展示的页面,如充值中,充值成功,失败等

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const timer: any = ref(null);
const Result = async (No:any) => {
try {
const res: any = await getResult({No: tNo })
// 0 处理中 1 成功 2失败
if ([0].includes(res.data.State)) {
timer.value = setTimeout(() => {
Result(No);
}, 2 * 1000);
} else if (res.data.State === 1 || res.data.State === 2) {
clearTimeout(timer.value);
}
State.value = res.data.State
//该赋值的赋值
//......
} catch (error) {
clearTimeout(timer.value);
}
};

离开页面时,一定要清除定时,避免一直调用接口

1
2
3
onBeforeUnmount(() => {
clearTimeout(timer.value);
})

四.第一次进入页面有在app.vue的onmouted里写了获取token方法调接口,页面刷新后token没了

使用pinia+数据持久化,存放token 或者直接把接口获取到的存在localstorage

五.app端嵌套h5页面时,返回APP端时,不能使用router.go(-1),需要使用app端提供的关闭的桥的方法

isIos.js

1
export const isIos = !!navigator.userAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)

bridge.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import {isIos } from './isIos'

const setupWebViewJavascriptBridge = (callback) => {
if (isIos) {
if (window.WebViewJavascriptBridge) return callback(window.WebViewJavascriptBridge)
if (window.WVJBCallbacks) return window.WVJBCallbacks.push(callback)
window.WVJBCallbacks = [callback]
var WVJBIframe = document.createElement('iframe')
WVJBIframe.style.display = 'none'
WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__'
document.documentElement.appendChild(WVJBIframe)
setTimeout(() => { document.documentElement.removeChild(WVJBIframe) }, 0)
}
}

export const appBridge = (apiName, payload, cb) => {
if (isIos) {
setupWebViewJavascriptBridge((bridge) => bridge.callHandler(apiName, payload, cb))
} else {
let res = null
if (window.jsHZG[apiName]) {
if (JSON.stringify(payload) !== '{}' && payload) {
if (typeof payload === 'object') {
res = window.jsHZG[apiName](JSON.stringify(payload))
} else {
res = window.jsHZG[apiName](payload)
}
} else {
res = window.jsHZG[apiName]()
}
if (cb) cb(res)
}
}
}

export const registerhandler = (name, callback) => {
if (isIos) {
setupWebViewJavascriptBridge(function (bridge) {
bridge.registerHandler(name, callback)
})
} else {
window[name] = data => callback(data)
}
}

webView.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { appBridge } from './bridge'
// 打开新web-view
export const newPage = (isFull, path, query) => {
return appBridge('openNewPage', { url: isFull ? path: `${window.location.origin}${path}` })
}

// 关闭当前web-view
export const closePage = () => {
return appBridge('closePage')
}

//跳转小程序

export const openXCX = () => {
return appBridge('payStagesOrder')
}

// 获取原生导航条的高度

export const getStatusBarHeight = () => {
return appBridge('getStatusBarHeight')
}

使用:

1
2
3
import { closePage} from '@/utils/webView'
//在需要的地方
closePage()

六. 项目打包发布后,前端静态图片在测试环境无法显示的问题

首先我们去vite官网看看关于静态资源文件的处理:

企业微信截图_16720467796522.png
image.png
这里我们先假设:
静态文件目录:src/assets/images/
我们的目标静态文件在 src/assets/images/home/home_icon.png

1
<img src="src/assets/images/home/home_icon.png" />

那么这么写就会出现问题,在本地环境时你能够显示出图片,但打包发布到测试环境问题就出来了,图片没显示出来。
第一张图片标红的位置打包后是: /assets/images/home/home_icon.(hash的值).png

综上所得:如果资源文件在assets文件夹打包后会把图片名加上 hash值,但是直接通过 :src=”imgSrc”方式引入并不会在打包的时候解析,导致开发环境可以正常引入,打包后却不能显示的问题
所以:<img src="src/assets/images/home/home_icon.png" /> 是肯定找不到的。

1.第一种方式解决方式(适用于处理单个链接的资源文件 普通推荐!!!!)

1
2
import homeIcon from '@/assets/images/home/home_icon.png'
<img :src="homeIcon" />

2.第二种解决方式:new URL() + import.meta.url(适用于处理多个链接的资源文件 极力推荐!!!!)

企业微信截图_16720474775759.png
这也是vite官方文档说明的方式,new URL() + import.meta.url
工具文件目录: src/util/img-use.ts
img-use.ts:

1
2
3
4
// 获取assets静态资源
export const getAssetsFile = (url: string) => {
return new URL(`../assets/images/${url}`, import.meta.url).href
}

使用:

1
2
import getAssetsFile from '@/util/img-use'
//setup语法糖写法,没用语法糖的记得return出去
1
2
3
4
//如果是直接建在assets/images下,写图标名就行了
<img :src="getAssetsFile('home_icon.png')" />
//可以在../assets/images建目录如home文件夹,那么就是
<img :src="getAssetsFile('/home/home_icon.png')" />

另外:如果是背景图片引入的方式(一定要使用相对路径)

1
2
3
.imgBg {
background-image: url('../../assets/images/WDNMD.jpg');
}

3.第三种解决方式import.meta.globimport.meta.globEager(适用于处理多个链接的资源文件 不怎么推荐!!!!)

这种方式引入的文件必须指定到具体文件夹路径,传入的变量中只能为文件名,不能包含文件路径

使用vite的import.meta.globimport.meta.globEager,两者的区别是前者懒加载资源,后者直接引入。
那我为什么说不怎么推荐了,如果你使用的是最新版本的vite,就会出现

image.png
可以看出import.meta.globEager已被弃用,只能用import.meta.glob了,并且你只能传图片名,不能传路径。

const modules = import.meta.glob(‘./menus/**/*.ts’, { eager: true,import:’default’ })
以“default”这个选项作为默认的导出内容,从而避免对象为’unknow’时获取default的报错

工具文件目录: src/util/img-use.ts
img-use.ts:

1
2
3
4
5
6
7
8
// 获取assets静态资源
export const getAssetsHomeFile = (url: string) => {
const path = `../assets/images/home/${url}`;
const modules = import.meta.globEager("../assets/images/home/*");
//const modules = import.meta.globEager("../assets/images/home/*", { eager: true,import:'default' });
return modules[path].default;
}

使用:

1
import useImg from '@/util/img-use'
1
2
//写图标名就行了,不能带路径,并且你只能传图片名,不能传路径。
<img :src="useImg('home_icon.png')" />

七.h5移动端,安卓看着没问题,ios的手机文字嘎嘎换行

原因:给对应的dom元素设置了宽度,导致了换行,移动端布局时非必要不要设置宽度)

八.vant3 Dialog组件title没显示,我的是小米11的,ios或安卓的其他手机都显示了

目前没找到原因,见鬼了,就我的手机 Dialog组件title没显示。

1
2
3
4
5
6
7
8
9
//手机号弹窗提示
const dialogShow = () => {
Dialog.alert({
confirmButtonText: '我知道了',
title: '你瞅啥', //这个没显示,离谱
confirmButtonColor: '#FE7E41',
message: '瞅你乍地!!',
}).then(() => {});
};

九.小程序嵌套web-view H5页时,出现双导航的情况

在app.json中,小程序navBarTitle不设置,并且h5项目的mian.ts调用接口,获取这个是从哪来到或嵌套该h5页,把来源存放在全局中,用来判断是否隐藏left-arrow 返回箭头

获取:

1
2
3
//调接口获取来源.............. 得到sourcePage

app.config.globalProperties.$sourcePage = sourcePage // 放到全局

使用:

1
2
3
4
5
6
7
8
9
<van-nav-bar
title="我的"
:left-arrow="$sourcePage != '来源的值'"
:border="false"
style="width: 10rem"
:placeholder="true"
:fixed="true"
@click-left="onClickLeft"
/>

这样就不是双导航了

十.app端嵌套web-view H5页时,出现双导航的情况

让app端把原生的导航给隐藏了,使用h5的导航

十一.h5 title为空时,安卓手机导航栏会默认展示h5域名。

解决方案:通过document.title改变h5 title

十二.安卓端布局正常,两div上下有间距,ios没间距

ios自己写的样式不是vant组件的(vant组件自己做了适配)文字需要高度和行高,没高度就挤在一起了

总结一手:vant组件使用样式穿透时,就不要给宽度,高度行高了,vant组件组件整好适配了,自己写的样式特别时文字需要给高度和行高,按蓝湖上来就行了

十三.微信小程序配置业务域名,调用web-view组件打开需要嵌套的h5页面

应为有其他的微信 去到微信公众平台,开发管理下的开发设置的下配置义务域名
1672708788778.png

下载的校验文件,放在public下,并且不要重命名,是什么名字就是什么,不然检测不到

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

我是穷比,在线乞讨!

支付宝
微信