前端规范和小技巧
1.关于计算属性
计算属性关联的变量不可以过多,同时要保证关联的变量在代码中的变换次数不可过多
例如这段代码的this.options内部数据变化过多,计算属性调用次数过多导致页面卡顿。
3.关于更新数据问题
有这样的一个页面,
产品信息数据是从tab获取的,此时点击保存的时候,需要跟新左侧tab数据,才能够保证切换tab选项再切换回来的时候,显示的是修改之后的数据,我的做法是直接拉数据。但是会有个问题,就是下面的ppt展示的数据依赖的tab产品的数据,而我是通过监听整个对象,这时候就会有个问题,重新拉数据,就是导致ppt展示接口也会拉数据,导致ppt展示组件闪一下。但是如果点击保存不重新拉数据,而是直接修改数据,需要把保存的数据向上抛三层,而且数据字段过多,类型还不一致。
解决方案:为了保证即能拉到数据,又让ppt组件接口不调用,需要在ppt展示组件换成监听组件必须的字段,即监听一个基本数据类型,通过props的形式传入,在监听改字段,此时由于是基本数据类型,只看内容,就不会触发监听
要求:赋值的时候,fileId要在productDetails之后赋值,要不然慧法师监听到fileId但是productDetails没变的情况
2.关于自定义v-model
使用自定义v-model保证双向绑定的数据类型是一个基本数据类型,不使用v-model实现绑定一个对象类型的数据,对于对象类型应该使用的是props+emit的方式实现数据的双向流动。原因:业务迭代的时候,不好对数据进行拦截做其他操作。
3.组件数据通传,后代组件接收
vue3的props子组件未接收都放到$attrs,$attrs包括$attrs和$listeners两个部分,
v-bind="$attrs"
v-bind="[...cardConfig]"
v-bind="{...$attrs, ...minInputProps}"
问题记录
1.关于腾讯无界微前端的问题
a.关于tab切换渲染同一个页面,同时这个页面中含有无界子应用的的问题
问题描述:来回切换tab的时候,第二次切入原先打开的无界子页面空白。(此时的无界的name绑定为tab的id加一个常量字符串)
原因:切换页面时无界未被销毁,切回的时候方式冲突,
解决方案:生成随机无界name,只是一个临时方案,用于将vue2升级成vue3过渡使用。
2.关于页面开发布局和数据流动问题
页面做成流式布局,尽量不用使用嵌套布局,以便迭代的时候,数据好流动到各个组件。
拆掉一层布局,这样如果在这个页面上再加层 布局或者是在之中加一层布局就会很方便,代码举例
改方法内部有很多的组件,实际上可以提出来放到div中。
3.关于框架的使用问题
再使用框架的时候,尽量不要使用这个框架提供的api而是使用原生api实现。
原因:可能在那个版本使用的是最好的,但是对于迭代来说,可能会造成累赘。
问题描述:使用nuxt提供的asyncData,通过请求接口获取到数据在注入到vue的data里面。迭代需求,再原来的页面上增加侧栏nav,每次切换的时候,数据带入到原来的页面,差不多类似下图
这时候就会有个问题,侧栏通过请求接口获取到数据,会有一定的延时,同时如果获取到数据后再通过v-if渲染组件,由于原来的是通过asyncData来获取到数据,数据会在create的时候就初始化,即vue的data渲染的时候赋值。这会导致传入的props无法在data之前获取,同时使用了大量的计算属性和vuex,导致数据没传入就执行了计算属性和vuex的内容,导致报错。
此处临时解决方案:使用无界嵌入原页面作为子组件(过渡方案)
4.关于导航守卫无法拦截问题
nuxt项目,使用的是window.open打开的新页面,导致无法拦截,无法解决,最后使用浏览器原生api--beforeunload 实现跳转拦截提示。
5.关于阻止冒泡的技巧
(2023-12-06更新)
有种情况,就是使用了组件,但是改组件内部的click方法内有阻止冒泡,但是改组件外层也有个click方法,解决问题的方法是直接改掉该组件,但是过程会比较麻烦,还需要发布组件库,此时我们可以在改组件的父元素(如果改组件的父元素刚好绑定click,此时需要在该组件套一层div)绑定一个方法,再这个方法实现阻止冒泡即可
6.关于iframe嵌入跨域问题
有个项目嵌入了iframe,iframe子页面和本项目完全跨域,通过查找网上的一些部分之后,低版本谷歌和火狐可以,但是高版本谷歌不行,后端没法将cookie写入。
功能描述:一个弹框,弹框内嵌入另一个页面,逻辑为我需要先请求一个接口,通过接口获取到uuid,然后通过拼接到嵌入页面的地址,这个地址为一个转发地址,改转发地址会将token写道用户信息,此时嵌入的页面可以显示,但是出现问题,token无法写入cookie中,转发接口报跨域错误。
问题原因: 高版本谷歌使用iframe不允许转发写入cookie。
解决方案: 再部署一份项目,访问地址为父引用的地址,实现同源访问。例如我们的访问地址为fom.1234sig.com,则新部署的地址为mp.1234sig.com。即可实现同源访问。
7.关于按钮点击获取焦点后按enter键一直触发事件问题
问题描述:公司封装的按钮和confirm一起配合使用的时候会触发这个问题
第一次触发
鼠标不点击页面上任何位置,按键盘enter键
查看调用栈
已知原因,由于第一次点击弹出弹框之后,未能使元素失去获焦,导致按住enter键之后会一直触发按钮的enter事件
解决方案:点击时让其失去焦点
在方法触发时添加
document.activeElement.blur()
8.关于使用provide/inject先后代传值的问题描述
描述
父组件通过provide向个个后代组件注入同一个对象,后代组件分别使用inject获取到该数据,但是会导致一个问题,如果多次刷新页面,此时页面展示会有问题,如下图所示:
用过vuetools查看input组件,v-model是已经绑定了,但是数据未能显示,后面换成原生input,还是会出现这个问题,而且这个问题本地特别难复现,发布到沙盒之后偶显。
使用很多方式解决过,但是不行,所以怀疑是nuxt版本的问题,所以升级了nuxt版本,由于升级nuxt版本升级得有问题,就不用过升级nuxt解决了。
最终解决方案:不使用provide依赖注入,而是使用props的方式逐层传入。
9.关于v-model实现双向绑定的注意事项
这个问题是在将vue2页面升级成vue3页面时发现的一个问题,原先组件是通过emit触发的,但是由于已经迭代了,这个方式不满足要求了,所以使用了v-model实现了
主要是值变化触发了两次,看代码发现属性赋值了两次,就会触发两次,如下图
10.关于flex-grow:1的问题
一个父元素有三个子元素,其中一个子元素宽度固定,另外两个子元素使用flex-grow:1平分剩余空间的,如果值设置flex-grow:1,不会平分剩余空间而是需要再设置width才能平分剩余空间
例如:{flex: 1; width: 0;}
11,关于tab自适应问题,tab会跟着屏幕的收缩而进行隐藏
父元素固定高度,使用flex布局,使用溢出隐藏,
获取到offsetLeft而且index不为0的元素index,即是被隐藏的第一个元素,从这里切开数组。
父元素开启相对定位,省略元素开启绝对定位,
获取显示最后的元素即index-1的元素,计算出元素右侧距离到元素左边的距离
算出距离之后,将距离作为省略元素定位的left值即可
部分代碼
resizeLabel() {
let labelDom = document.querySelector(".g-tag-group_overflow");
let labelDomChildren = labelDom.children;
let childrenArrayKList = Array.from(labelDomChildren);
let computedParam = this.getDomIndexItem(childrenArrayKList);
if (computedParam.index != 0) {
this.overLabelList = this.zyLabel.slice(
computedParam.index,
this.zyLabel.length + 1
);
this.dropdownLeft =
childrenArrayKList[computedParam.index - 1]?.getBoundingClientRect()
?.right -
labelDom?.getBoundingClientRect()?.left +
12;
} else {
this.overLabelList = [];
this.dropdownLeft = 0;
}
},
getDomIndexItem(childrenArrayKList) {
if (isEmpty(childrenArrayKList)) {
return {
index: 0,
width: 0,
leftWith: 0,
};
}
for (let index = 0; index < childrenArrayKList.length; index++) {
const domItem = childrenArrayKList[index];
const domeItemLeft = domItem.offsetLeft;
if (index != 0 && domeItemLeft == 0) {
const domBeforeItem = childrenArrayKList[index - 1];
return {
index: index,
width: domBeforeItem.offsetLeft + domBeforeItem.clientWidth,
};
}
}
return {
index: 0,
width: 0,
leftWith: 0,
};
},
12.关于http
域名残缺的时候,添加//会自动将域名补充,但是补充的域名协议是https的而不是http
13.关于页面卡顿排查内存在用问题
我有个echarts图,是从vue2升级成vue3,数据处理改用成对象,此时会导致页面很卡,所以需要查看一下为什么看,排查过程:
控制台中选中:
刷新页面立即点击
页面刷新好之后点击停止
查看内存情况:
第一个XHR占用运行时间最多,点击活动查看:
看图上面两个图可以得到两个信息:1:decryptData在每个微任务都被调用了,2:图中红框框中的两个方法占比过高,即floatAdd和floatSub方法
所以优化思路:
1.decryptData只调一次
在初始化对象时调用并记录数据
2.优化floatAdd和floatSub两个方法
14.关于devtools图标为显示的问题
有点烦,nuxt3项目devtools用的好好的,结果这两天突然失效了找不到了,我再改改东西了,全程使用log+debugger在调测,难受死了。ps:图标是亮的。
百度了一下,百度的问题都不能解决问题,所以想着可能是版本问题,因为之前合并过代码还删除了node_modules,重新pnpm之后版本可能下载的不对应,我查了一下我的devtools版本
我这个版本是1.1.3,就问了同事用了什么版本,同事的是1.0.6。感觉稳定了应该是了,在问了同事用了什么版本的pnpm ,他是8.14.1,我的是8.7.0。哈???为什么我pnpm版本比同事的低但是devtools版本却比他高呢?所以我删掉了node_moduls,全局安装pnpm版本为8.14.1,在重新pnpm install安装依赖。查看devtools版本。
和同事的一样了,运行项目,打开控制台:
终于出来了QAQ,不用在用log+debugger来查看数据了!!!!!!!!!!!!!!
最终解释:我们安装的vue devtools插件的数据是通过项目中的devtools依赖提供了,项目启动的时候,项目中的devtools会收集数据提供给拓展插件。因为浏览器的拓展插件已经亮了但是控制台却没有显示,所以可能就是项目中的devtools未能提供数据导致的,所以可能是版本不对应或者冲突,又因为devtools是依赖于pnpm版本,所以更新pnpm到对应版本即可解决!!
注意:重新安装依赖,需要删掉pnpm-lock.yaml文件,要不然下载的还是原来的版本!!!!!!!!
ps:发生了一件尴尬的事情,因为我在我的分支运行没有vue,切换到dev分支之后删除node_modules重新install下载下来1.0.6版本,这个是因为log文件依赖就是1.0.6版本。运行之后vue图标就出来的,但是切换到我的开发分支之后,因为分支不同,删除node_moduls和pnpm-lock文件之后,重新下载,这时候版本是1.1.4版本,但是运行之后vue图标出来 ,也就是说1.1.3版本出了问题!但是总体的解决思路都是版本问题!!!
15.关于数据共享的一些操作
接手一个模块,使用了vuex,产品数据页面产品数据共享,添加产品、删除产品、切换tab以及一些相关数据操作都是用了vuex,数据太乱了,这个页面升级成vue3的时候,本来打算用props+emit实现算了,但是我想到一些骚操作,使用对象统一数据,要改变的时候直接对这个对象进行赋值不就行了吗,想到要在不同的组件实现数据共享还是对象,我就想到了类,有因为对象是类的实体,所以想到使用代理实现数据响应式。封装类似如下:
export class CommonData {
num1: string
num2: string
private static instanceData: any
constructor(num1: string, num2: string) {
this.init(num1, num2)
}
/* 初始化 */
init(num1: string, num2: string) {
this.num1 = num1
this.num2 = num2
}
static getInstance(num1?: string, num2?: string) {
if (!CommonData.instanceData) {
const targetObject = new CommonData(num1, num2)
const targetReact = new Proxy(targetObject, {
get(target, key) {
return target[key]
},
set(target, key, value) {
target[key] = value // target就是data对象
}
})
CommonData.instanceData = targetReact
}
return CommonData.instanceData
}
}
使用了单例,保证在一个页面的对象都是同一个,使用了代理,保证响应式。
测试页面如下图所示:
初始化的时候需要保证初始化类先创建对象,所以我是用了v-if控制。
使用的地方如下图所示:
那我们在左边的子组件来修改右边组件的兄弟组件的数据,
右边代码:
左边子组件代码:
点击“点我修改右边数据”,结果如下图所示:
注意:代码会报错,如下图所示
解决的方案就是使用反射代理即可,我的测试vue3项目版本比较高,所以会报这个错误,低版本应该不会报错。
优点是:修改比较明确,缺点就是这个属于定制化的了,如果有修改,需要添加属性。
16.微信开发者工具的一些问题
问题描述:uniapp使用canvas画图表后滚动时,canvas固定不动了,布局也会乱掉。
有个需求,使用需要写图表,我使用的是lime-echart组件库,再微信开发者工具画出来之后会有个固定定位的效果,都会有这个效果。
原来
滚动后
固定在原来占视口所在位置了,问题主要是:微信开发者工具的问题,真机调试或者发布后就不会出现这个问题了。
17.关于vue-charts渲染问题
有个事情,就是我的options改变的时候,数据是改变了 ,图也改变了,但是tooltip的数据还是原来的,数据增加的时候,tooltip的数据可以增加,但是数据变少的时候,tooltip的数据还是原来的,
原因:渲染问题,tooltip的实例还是原来的数据。
解决方法:重新刷新实例:
17.坑,大坑(关于拖动datazoom实现echarts数据归一时,datazoom滚动到原来为止)
这个datazoom拖动左边的之后又滚到最左边了。
问题描述:实现归一,拖动之后数据会原点对齐
这时候就有个问题就是拖动之后又滚回原来的位置
一开始我觉得可能是因为从vue2升级成vue3之后,代码升级的有问题,但是对比代码之后,代码逻辑都是一样的,后面又觉得是数据有问题,但是对比了vue2的走势图之后,数据是一样的,所以在想可能是因为在vue3中使用echarts有问题,后面在归一算法代码中重新赋值了datazoom的end和start,这时候就好了
原因:vue3中使用是最新版本的ecahrts, 在数据更新的时候会导致dataZoom重新渲染。
解决:手动拉dataZoom的时候,记录这个位置,后面在把这位置重新赋值一次就行了。
记录位置
重新赋值
18,依赖问题错误
✖ Nuxt Fatal Error │
│ │
│ Error: │
│ │
│ Vue packages version mismatch: │
│ │
│ - vue@2.7.16 │
│ - vue-server-renderer@2.6.14 │
│ │
│ This may cause things to work incorrectly. Make sure to use the same version for both. │
│
解决
原先有一个"^",去掉这个“^”就可以了
19.大坑,关于微信小程序ios渲染慢的问题
问题描述:iOS点击echart图标的时候,左右移动日期为响应或者响应慢
如上图:首先点击5月10号这个点移动到左边或者右边的时候,其他数据变量但是日期没有变,ios17出现的问题。
原因:日期用了view包裹住了,换成text即可
无界相关问题
需要后端配合,部署的时候需要部署在同一个域名下,如果不是同一个域名,则需要解决token跨域问题,url带参时需要加token。
例如我这个项目A嵌入的是项目B的,项目A项目域名为:http://zyfp-amp.ssb.gofund.cn/,项目B的实际域名为:http://zyfp-fof.ssb.gofund.cn/。这时候有两种解决方案:
方案一:项目A的无界带入一个token,在项目B中全局做处理,获取无界中的token并进行token置换处理再写道cookie中,实现免登录。
方案二:部署项目B的时候,顺便将项目B部署到项目A的域名下,这个使用nginx和docker实现,需要后端配合。
我们使用了方案二
url无需写域名只需要写路由即可。
对于无界的大部分问题,官方都给了解答。
无界 | 无界 (wujie-micro.github.io)
在一个项目中使用了无界实现单页面应用,首先创建一个项目作为父项目,该项目使用了无界,每个路由下存在一个无界,即一个子项目。父项目的职责有:切换路由时改变路由地址同时获取到地址传递给无界,如果只是query变化,也需要改变导航地址的query;子项目封装一个getRoute,方法的职责是可以监听到路由的变化。同时子项目需要封装一个跳转方法,该方法的职责是用于在改子项目实现跳转,即子页面内部自己跳的时候需要改变导航的url。
1.子应用引用样式不生效和无法获取等问题
例如上面所示,图标“?”是vue2的,但是弹框的内容是vue3的使用了无界进行嵌套。
解决方法:通过无界的props向子应用的html插入样式类名
2.统一的对象实现控制换肤或者传参
例如封装subscribe,封装思路,如果改页面存在无界(即子引用),则使用父引用传入的subscribe,如果没有传入subscribe,则使用子应用自己的subscribe。
例如换皮肤操作是在父应用操作的,子引用也想要跟着改变,这时候就可以通过一个subscribe传给子应用实现。
3.子页面监听url的query问题
这个是一个统一的处理过程,每个项目都是需要做过处理了,主要是分项目,减少服务器压力。一个项目为一个顶级项目,顶级项目下分为多个子项目,这时候需要考虑跳转的问题。
父项目路由:
其中参数url为子项目的路由和子路由所需的query参数
跳转有三个场景:父项目跳不同的子项目,子项目内部跳转,子项目内部跳转。
跳转场景一:父跳子,这个比较容易,直接是路由跳转即可。
跳转场景二:子跳子,统一在父项目处理。
跳转场景三:子项目内部跳转,统一在副项目处理。
场景二和场景三的处理方式的一致的,主要是靠无界的消息通知实现的。
首先子项目跳转的时候需要监听路由,子项目路由方式变化,则通知父项目获取到路由和参数
export function useSubApp() {
//判断是不是无界子项目,如果是,则通知父项目
if (EnvTools.isSubApp()) {
const route = useRoute()
const config = useRuntimeConfig()
watch(
route,
(to) => {
window.$wujie?.bus.$emit("subAppRouteChange", { baseUrl: config.public.baseURL, ...to });
},
{ flush: 'pre', immediate: true, deep: true }
)
}
}
子应用需要做一个全局拦截,如果是属于无界子应用的话,路由不让跳转。我负责的项目是nuxt项目,是写在插件中,比较复杂,但是思路大致就是这样了。
父项目接收到无界通知的事件,处理参数。这里涉及的主要是修改链接的url参数
同时实现路由的跳转,子跳子项目在父项目全局判断,获取到路由跳转的通知之后,切换路由并在无界封装的组件修改路由的url参数的值。
子项目内部跳转只要在无界组件中修改参数即可。
部分代码:
4,无界嵌入页面,域名不同token问题
这个就需要在子项目进行token置换或者token写入了,无界props中加token,子项目获取token进行换置或者写入cookie即可达成免密登录
未完待更新.......