判断当前设备是否为移动设备
如下所示,如果是,则isMobile为true,否则为false
//写法一:
const isMobile = /Mobi|Android|iPhone/i.test(navigator.userAgent)
//写法二:
const isMobile = navigator.userAgent.match(/Mobi/i) || navigator.userAgent.match(/Android/i)||navigator.userAgent.match(/iPhone/i)
vite项目中判断开发环境和生产环境
新建.env文件,env后面代表环境类型
内部配置环境
此时在vue文件中可以通过
import.meta.env.VITE_CONFIG
进行环境类型的判定,不同环境运用不同的地址在
package.json
中build命令中进行配置.
vite+vue项目的移动端适配
安装插件
npm i amfe-flexible
npm i autoprefixer
npm i postcss-pxtoremvite.config.js配置
import autoprefixer from 'autoprefixer';
import postCssPxToRem from 'postcss-pxtorem';
export default defineConfig(({ command, mode }) => {
return {
css: {
postcss: {
plugins: [
autoprefixer({
overrideBrowserslist: ['Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8'],
}),
postCssPxToRem({
// 自适应,px>rem转换
rootValue: 37.5, // 75表示750设计稿,37.5表示375设计稿
propList: ['*'], // 需要转换的属性,这里选择全部都进行转换
selectorBlackList: ['norem'], // 过滤掉norem-开头的class,不进行rem转换
}),
],
}
},
}ok,成功配置,之后根据设计稿的UI,是多少呢px就写多少px,之后会自动转换成rem适配移动端,如果有些单位不想被转换,则将px写为大写即可
原理
vue3中reactive变量失效问题
在vue3中响应式数据通过ref或者reactive进行定义,但是对于reactive定义的数据,对其进行赋值时会丢失响应式,目前还未去了解原因,但是丢失响应式是真实存在的
let infoList = reactive({})
const testFunction = async () => {
let result = await .....异步请求
infoList = result.data //此时infoList就不是响应式数据了
}解决方法
第一种:使用ref
let infoList = ref({})
const testFunction = async () => {
let result = await .....异步请求
infoList.value = result.data //此时infoList还是想响应式数据
}第二种:使用嵌套
let infoList = reactive({
data:{}
})
const testFunction = async () => {
let result = await .....异步请求
infoList.data = result.data //此时infoList还是想响应式数据
}
vue3移动端开发页面跳转动画
Vue3新增
<transtion></transtion>
标签直接配置页面动画,通过name配置动画名称,如果name是slide,则动画类名为slide-enter-active/slide-enter-to/slide-enter-from
和slide-leave-active/slide-leave-to/slide-leave-from
<transition :name="transitionName">
<div :key="$route.fullPath" class="all">
//固定写法
<router-view v-slot="{ Component }" >
<keep-alive>
<component :is="Component" v-if="$route.meta.keepAlive" />
</keep-alive>
<component :is="Component" v-if="!$route.meta.keepAlive" />
</router-view>
</div>
</transition>动态判断动画名称,通过配置路由的meta属性的index进行判断左滑还是右滑
let transitionName = ref();
router.beforeEach((to, from) => {
console.log(to, from, 'to from ');
// 根据路由标记判断触发哪个动画
if (to.meta.index > from.meta.index) {
// 从右往左动画
transitionName.value = 'slide-right';
} else if (to.meta.index < from.meta.index) {
// 从左往右动画
transitionName.value = 'slide-left';
} else {
transitionName.value = '';
}
});动画css
.all {
height: 100%;
width: 100%;
}
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
transition: transform 0.3s;
}
.slide-left-leave-active , .slide-right-leave-active {
position:absolute;
z-index: -1 ;
}
.slide-right-enter-from {
transform: translateX(100%);
}
.slide-right-enter-to {
transform: translateX(0);
}
.slide-right-leave-from {
transform: translateX(0);
}
.slide-right-leave-to {
transform: translateX(-100%);
}
.slide-left-enter-from {
transform: translateX(-100%);
}
.slide-left-enter-to {
transform: translateX(0);
}
.slide-left-leave-from {
transform: translateX(0);
}
.slide-left-leave-to {
transform: translateX(100%);
}注意:
- transition标签下的div是必要的,不然会报警告
Transition包裹的必须是一个单根的组件。
- div标签得添id,帮助识别页面是否切换
- transition标签下的div是必要的,不然会报警告
Vue3关于父子传值遇到的问题
之前开发项目封装子组件,通过props进行传值,由于之前对于props的使用是一次性的,也就是说props的值不会改变,在子组件中使用方式是下面这样.
const {data1 , data2} = defineProps({
data1:String,
data2:String
})在一次需要对父组件传过来的值进行监听时,只有第一次值可以监听到,之后就监听不到,找了半天也没发现问题,最后在看pinia时发现下面这句话.
对props解构会失去响应式
,之后将解构赋值去掉后,问题完美解决.const props = defineProps({
data1:String,
data2:String
})
//通过props.data1,props.data2进行使用
base64展示以及base64转file
在开发时,使用相机拍的照片为base64,如果要在页面上展示,则使用
data:image/png;base64,
+base64有些接口上传图片需要图片格式为file,所以将base64转为file,使用下面代码,使用方式如下:
function base64ImgtoFile(dataurl, filename = 'file') {
const arr = dataurl.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const suffix = mime.split('/')[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], `${filename}.${suffix}`, {
type: mime,
});
}
let url = 'data:image/png;base64,'+base64
let file = base64ImgtoFile(url)
倒计时走字过快
在写一个倒计时组件时,出现问题:第二次倒计时时,走字速度为原来的两倍.第三次速度则是三倍,代码如下
原因: 每次进来都是新的一个timer,所以clear的也是新的,而不是之前的timer,导致每点击一次速度加快一倍
解决方法:将timer定义为全局变量
图片打包加载不到问题
在进行图标引入时,如果想要循环渲染图片,需要先定义变量,再使用,类似下面这样
<script>
//定义
let obj = ref([
{
name:"xx",
icon:'@/assets/a.png'
},
{
name:"xxx",
icon:'@/assets/b.png'
},
{
name:"xxxx",
icon:'@/assets/c.png'
},
{
name:"xxxxx",
icon:'@/assets/d.png'
},
])
</script>
<template>
<div v-for=(value , index) in obj :key = 'index'>
<img :src="value.icon"/>
<div>{{value.name}}</div>
</div>
</template>但是上面的这种情况会导致问题:由于定义的时候写的是相对路径,所以打包后找不到图片地址
解决方法:使用
import
引入import xx from '@/assets/xx.png'
ios搜索框失焦
在进行移动端开发时,由于ios的搜索框在聚焦后除了点击键盘上的收回键,并不会自动收起键盘,导致ios在配置了防遮挡后,可以一直往上滑.
解决方法:当点击区域不再搜索框,就手动失焦(虽然实现简单,但是当时没有这个思路)
const handleClickOutside = (e) => {
if(e.target.className!= 'van-field__control'){
// 当前点击的元素不是输入框,收起键盘
document.activeElement.blur();
}
};
<div class="container" @click="handleClickOutside">
<van-search
ref="searchInput"
v-model="searchValue.name"
shape="round"
placeholder="订单号/投保人姓名"
:clearable="false"
autofocus
@keydown="confirmSearch">
</van-search>
</div>
viewport-fit=cover
- ios沉浸式状态栏配置项
Vue3样式中使用js变量以及样式穿透
样式中的变量使用
let height = ref('12px')
<style>
.container {
height:v-bind(height)
}
<style>样式穿透
方式一
::v-deep(.className) {}
方式二
:deep(.className){}
van-field ios输入框兼容性问题
问题描述:输入框使用input做了规则校验,如下所示,当每次input都会调用该方法,这样当在ios上用中文拼音输入时,会导致每次输入都会把前面所输入的再复制一遍.
const handleInputClineName = (e) => {
const pattern = /[^\u4e00-\u9fa5a-zA-Z]/g;
e.target.value = e.target.value.replace(pattern, "");
clientName.value = e.target.value
}解决方法:js中有两个事件
compositionstart
和compositionend
第一个是当用户使用拼音开始输入汉字时触发,第二个方法为用户输入完毕后触发,所以可以在用户输入结束后再赋值。//输入结束后校验
const compositionend = (e) => {
const pattern = /[^\u4e00-\u9fa5a-zA-Z]/g;
e.target.value = e.target.value.replace(pattern, "");
clientName.value = e.target.value
}
//每次输入时校验
const handleInputClineName = (e) => {
const pattern = /[^\u4e00-\u9fa5a-zA-Z]/g;
clientName.value = clientName.value.replace(pattern, "");
}ios产生原因:ios输入拼音,拼音会展示出来,触发input事件,再次点击拼音就会导致复制前面输入的拼音,Android中每次拼音输入并不会触发input事件。
rollup-plugin-visualizer
vite打包费时可视化插件,根据花费时间优化打包时间
使用
npm i rollup-plugin-visualizer -D
vite.config.js中配置
引入
import { visualizer } from "rollup-plugin-visualizer";
在plugins配置项中进行配置
visualizer({
open:true, //注意这里要设置为true,否则无效
filename: "stats.html", //分析图生成的文件名
gzipSize: true, // 收集 gzip 大小并将其显示
brotliSize: true, // 收集 brotli 大小并将其显示
})
在打包后会生成stats.html文件,该文件在浏览器中打开如图所示,鼠标放到每一块上即可展示文件目录以及打包费时。
user-select
- 使用div写一些用户可以点击的按钮时会导致用户可以复制按钮,这是不合理的,通过在该div上添加
user-select:none
来禁止用户复制
ios上的数字被识别为电话号码
- 问题:ios上的一些数字串会被识别为电话号码,导致其展示为蓝色,
- 解决方法:
<meta name="format-detection" content="telephone=no" />
ios上fixed定位抖动
- 之前在实现ios沉浸式时,设置
viewport-fit=cover
属性,实现了沉浸式,但是底部会出现高度等同于状态栏的白条。 - 为了解决白条问题在body上添加了属性
height:100vh
,解决了白条问题 - 但是使用100vh后导致一些fixed定位元素在页面切换时出现抖动问题
- 解决方法:网上解决ios fixed定位抖动问题的方法很多,像添加
transform: translateZ(0)
,或者固定定位外使用绝对定位进行包裹等等,但是试了一下都没用。最后在body上添加posittion:relative
解决
设置主题样式全局通用
项目内容大致相同,主题色不同时使用
全局scss中使用
添加样式文件
theme.scss
$themeColor:#5a90f7; //主题色
$themeTextColor:#203154;//字体色vite.config.js中配置
css: {
preprocessorOptions: {
scss: {
additionalData: '@import "./src/style/theme.scss";',//文件路径
javascriptEnabled:true
}
}之后可以在任意文件中访问
$themeColor
$themeTextColor
全局js中使用
添加样式文件
theme.module.scss
$themeColor:#5a90f7; //主题色
$themeTextColor:#203154;//字体色
:export {
themeColor:$themeColor;
themeTextColor:$themeTextColor;
}直接在需要使用的地方引入
import theme from '@/style/theme.module.scss'
console.log(theme)
关闭git对于文件名大小写变化的忽视
git config core.ignorecase false |
移动端禁止页面左滑返回
添加backbutton事件监听
document.addEventListener("backbutton",handler);
const handler = (e) => {
e.preventDefault();
}只要添加了监听之后,页面就不能在使用滑动返回,无论是否执行 *e.preventDefault()*,比如下面这种,无论a是否等于b,都不能通过手势进行返回。
document.addEventListener("backbutton",handler);
const handler = (e) => {
if(a === b) {
e.preventDefault();
}
}所以想要可以滑动返回,需要在指定位置进行移除监听
document.removeEventListener('backbutton',handler)