页面滚动问题
// 页面滚动属性
clientHeight:元素宽高,包含内容+内边距
offsetHeight: 元素高度包含内容+内边距+边框;
elem.offsetTop: 是获取元素elem元素到网页顶部的距离
scrollTop:网页滚出手机顶端之后,网页的顶部,距离手机顶部的距离
// document.documentElement.scrollTop : 设置的是整个网页滚出窗口的距离;也就是滚出的网页顶部距离窗口顶部的距离
// 元素到窗口顶部的距离= 元素到网页顶部的距离-整个网页滚出窗口的距离
// ================分割线(记一次滚动例子)
// 目的:使得ele元素滚动到手机屏幕的红线下方;手机顶部距红线的距离是course-header部分。
// 获取ele到网页的高度
let scrollRange = document.getElementsByClassName('ele')[0].offsetTop
// 获取course-header的高度
let headerHeight = document.getElementsByClassName('course-header')[0].offsetHeight
//document.documentElement.scrollTop 和 document.body.scrollTop基本一样,同时都设置为了兼容性考虑
// 设置网页滚出的网页头部距离手机屏幕的距离(这里需要想明白一点是:ele元素滚动的距离等于网页滚出的头部距离手机顶端的距离)
document.documentElement.scrollTop = scrollRange - headerHeight
document.body.scrollTop = scrollRange - headerHeight
// 1. 滚动网页(可滚动任意位置)
scrollCurrentLyUnavailable() {
const currentlyUnavailableElem = this.$refs?.currentlyUnavailableRef
if (typeof currentlyUnavailableElem != 'object') return
// 下面这种可以慢慢顺滑滚动
window.scrollTo({
top: currentlyUnavailableElem?.offsetTop,
behavior: 'smooth'
})
},
// 2. 直接操作滚动元素(scrollIntoView的缺陷:只能是 start、center、end,不能任意指定滚动位置)
validate() {
this.$nextTick(() => {
this.$refs.email.validate().catch(err => {
this.scrollToView(this.$refs.email)
console.error(err)
})
})
},
scrollToView(ref) {
// block的取值只能是 start、center、end,不能任意指定滚动位置
ref?.$el?.scrollIntoView({ behavior: 'smooth', block: 'end', inline: 'nearest' })
},
// 可以设置滚动条使得页面在y轴上滚动,也就是页面向上滚动500
// 设置之后立即滚动,等价于设置document.documentElement.scrollTop = 500
// 注意设置window.scrollY = 500 ,网页不滚动的。
window.scrollTo(0, 500)
业务场景:select下拉到底后动态加载的问题(理解scrollHeight)
document.querySelector(".el-select-dropdown__wrap").addEventListener("scroll", (e) => {
console.log(111111111, e.target.scrollHeight)
console.log(222222222, e.target.scrollTop)
console.log(33333333, e.target.clientHeight)
})
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
真机上滚动时产生缝隙的问题
offsetHeight等计算可能会产生这个问题,可以采用getBoundingClientRect的方式计算,就不会有缝隙问题
vue文件模板图片编译跟html图片编译不一样
写在html或者pug中的图片是可以使用'./img/xxx.png'这种格式,但是在.vue文件的template中写的img标签,'./img/xxx.png'这种格式会报错,只能通过import的形式把图片导入,然后在使用导入的图片对象
移动端24px比例换算
一般将iphone6/7/8作为设计稿标准
iphone6/7/8的width:375px(12px) iphoneSE/5的width:320px(10px) 设计稿750px(24px) chrome能展示的最小字体的大小:12px
但是一般的设计稿我们都用2倍像素写,也就是750px,换句话说现在chrome能展示的就是12*2=24px了,也就是在750px下如果字体小于24px就需要等比例缩放。这样在移动端上才能正常展示
一般使用scale进行缩放之后,需要手动使用margin进行微调
px和rem转换
// 示例1
html {
font-size: calc(100vw / 7.5);
}
解析:一般会给html设置根字体,比如如果按照设计稿750px写的,750/7.5 = 100倍 那么设计稿的750px下,获取的某元素的48px的宽度,那么48px就会映射为0.48rem
如果看到网页是375px,那么需要将0.48/2,最终结果显示0.24rem
calc&vw&vh
vh理解:如果没有设置vh,那么整体外部wrapper会被内容撑高,可能撑到6000多,但是vh就是给定最外层wrapper视口高度为手机屏幕高度100%
calc理解:calc函数接受一个表达式,表达式运算符的两边需要有空格
// 示例1:这种可以使得banner的左右两边各有40px的外边距
.banner {
position: absolute;
left: 40px;
width: calc(100% - 80px);
}
// 示例2:被解析为一个长度 后跟一个加号 再跟一个负百分比。
calc(8px + -50%)
动画问题
一般A盒子有多余的内容时,我们会将A盒子设置overflow:scroll || hidden,并且给A盒子一个高度比如100px,比如内容的高度是500px,这时候盒子内部的内容就可以滚动了,这里如果想做一个动画,慢慢展示里面的内容,可以给外面的盒子设置动画高度从0慢慢展开到内容的高度就可以了。
两个div盒子默认垂直排列时之间会有空隙
解决办法:给两个div盒子的父元素设置line-height: 0px;
两个div盒子被设置成inline-block时,水平方向存在间隙
解决办法:给两个div盒子的父元素设置letter-spacing: -4px;或者直接给父元素设置font-size:0;
其作用是可以控制文字间的水平距离,可以让文字水平方向上重叠(line-height是让文字垂直方向上重叠),这样就抵消了空格或换行的占位。
如何让行内元素宽高有效
比如a标签,设置宽高之后无效,这时候通过下面方式使得a标签的宽高有效
- 给a标签设置float
- 设置a标签为行内块
Label标签的作用和使用
定义:它通常关联一个控件
Label标签有两个属性,一个是for,一个是 accesskey。
for功能:表示这个Lable是为哪个控件服务的,Label标签要绑定了for指定HTML元素的ID或name属性,你点击这个标签的时候,所绑定的元素将获取焦点 ,点击label所包裹内容,自动指向for指定的id或name。(这个可以用在轮播图中,有点类似于a标签点击触发锚点的效果)
accesskey:定义了访问这个控件的热键( 所设置的快捷键不能与浏览器的快捷键冲突,否则将优先激活浏览器的快捷键)
// 示例1:这里点击姓名两个字就相当于点击input输入框一样
<input id="username" type="text"><label for="username">姓名</label>
// 示例1:
<label for="username" accesskey="N">姓名</label><input id="username" type="text">
:target伪类
:target 是css3的伪类目标选择器,URL后面跟锚点#,指向文档内某个具体的元素。这个被链接的元素就是目标元素(target element),:target选择器用于选取当前活动的目标元素。
比如:http://127.0.0.1:8080/lunboDemo1.html#b 那么:target就会匹配到锚点b元素
给标签设置class问题
不要直接给标签加样式,这样有些标签的子元素会继承父元素的某些不需要属性会扰乱布局;建议给标签加class后再加样式
flex采坑点记录
-
flex布局中flex的直接子元素设置
float,clear以及vertical-align属性是无效的。 -
flex布局中的直接子元素的排列的最后一个子元素的
margin-right是无效的;如果空间没有占满的话。可以通过margin-left:auto设置排列的最后一个元素靠父盒子的右边界对齐 -
flex布局中的非直接子盒子是可以使用float属性的
-
float属性的盒子中可以使用flex布局
-
flex布局的盒子可以使用定位属性,但是flex属性对于脱离文档流(绝对定位和固定定位)的盒子是无效的
-
flex布局为一维布局;flex中的项目会被当做块元素对待,并且是等高。
-
Ios9以下不支持flex,所以移动端避免使用flex
h5移动端header和跳转问题
可以调用端上的,可以h5自己写,需要跟端上协商好; 如果h5页面中跳转链接那么会直接在当前h5中打开,而不会新开webview;如果想新开webview,可以手动调端上新开一个webview
点击某一个列表项下拉箭头导致全部列表下拉
这里采用在初始化时,给每个item绑定一个变量arrowImgFlag,触发点击事件时候,将item传入,只改变点击的item的arrowImgFlag。这样就只会使得被点击的item下拉
点击tab动态添加下划线
切换tab和tab-list数据联动
::v-deep深度访问
1、>>>
如果vue的style使用的是css,那么则
<style lang="css" scoped>
.a >>> .b {
/* ... */
}
</style>
但是像scss等预处理器却无法解析>>>,所以我们使用下面的方式.
2、/deep/
<style lang="scss" scoped>
.a{
/deep/ .b {
/* ... */
}
}
</style>
但是有些开发者反应,在vue-cli3编译时,deep的方式会报错或者警告。
此时我们可以使用第三种方式
3、::v-deep
切记必须是双冒号
<style lang="scss" scoped>
.a{
::v-deep .b {
/* ... */
}
}
</style>
4、 补充在vue3中deep的深度访问
:deep(.el-breadcrumb__item .is-link) {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #86909c;
line-height: 20px;
font-weight: 400;
}
去除滚动条 & 轮播
- flex可以让内部元素等高
- 轮播中间层计算宽度
- 通过
::v-deep修改豆腐块宽度
滚动
tab切换时滚动加载,考虑下数据缓存
vuex失效场景
通过router.push的方式跳转vuex有效,因为router跳转的方式是组件局部更新.
以下两种情况失效:
- 多页面应用切换页面时vuex失效;
- 或者通过location.href跳转页面时vuex失效
vuex失效后的替代方案:
- 可以考虑将参数存放在path中,页面跳转事通过route.query获取
- 也可以存放在本地storage中。
$router.replace导致页面死循环问题
当跳转页面时,必须采用push方式,而不是replace,两者的区别是:
-
push:push会在history中添加一条path记录,比如这里的
/login,从login后退返回就可以回到当前页面。 -
replace:replace中指定的path:/login会直接替换当前页面路由,如果当前的页面是顶层路由,如果那么被repalce指定的login页替换之后,从login页就无法回退了;页面卡死循环
备注:
1. 使用router的query存放参数后,需要this.$forceUpdate()强刷页面
2. 使用router的query存放callback从login返回之后,需要将callback赋值空串
某些页面下的某些操作需要用户登录才能操作
比如点击提醒按钮邮件通知功能,需要用户登录才可以发送邮件;
两种实现方案:
- 可以在当前A页面跳转登录页时,在url中增加callback回调,然后登录成功之后返回到当前A页面,在A页面的mounted中再去判断url中callback,并执行callback回调
- 除了url回调的方式之外,可以采用在当前A页面弹出模态框,在模态框中登录,这种方式始终在A页面,所以在addNotify中写连贯写弹出模态框登录成功之后做什么,页面不会重新加载。
备注:踩坑点是用户有可能去了登录页但是没有登录,又返回到A页面,所以对登录回来后还是需要增加login是否登录的判断.
url编码
跳转链接时最好将url进行编码(前提是跳转后的页面有对url进行解码)
文本溢出截断省略
1. 单行文本溢出省略
- overflow: hidden;(文字长度超出限定宽度,则隐藏超出的内容)
- white-space: nowrap;(设置文字在一行显示,不能换行)
- text-overflow: ellipsis;(规定当文本溢出时,显示省略符号来代表被修剪的文本)
<style>
.demo {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
<body>
<div class="demo">这是一段很长的文本</div>
</body>
// 备注: white-space: nowrap;会戳透当前布局范围,所以需要给父元素设置宽度控制,但是某些时候写固定宽度不利于盒子自适应,所以借助下面的方式,指定
-webkit-line-clamp: 1;为一行显示就可以。
----
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
text-transform: capitalize;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
2. 多行文本溢出省略
-webkit-line-clamp: 2(用来限制在一个块元素显示的文本的行数,2表示最多显示2行。 为了实现该效果,它需要组合其他的WebKit属性)display: -webkit-box(和1结合使用,将对象作为弹性伸缩盒子模型显示 )-webkit-box-orient: vertical(和1结合使用 ,设置或检索伸缩盒对象的子元素的排列方式 )overflow: hidden(文本溢出限定的宽度就隐藏内容)text-overflow: ellipsis(多行文本的情况下,用省略号“…”隐藏溢出范围的文本)word-break: break-all(多行文本如果文本是纯数字,就需要加这行,否则会出问题)
按钮之类的点击加上stop阻止事件冒泡
@click.stop="addCart"
overflow
box-size
outline
img设置图片和backgroun设置图片
img设置图片时,只需要给宽,高度会等比例适应,因为有图片本身就有默认的宽高
同理img也可以只设置高度,宽度也会自适应
background借助url指定的图片为背景图片,必须手动设置盒子的宽高,否则图片不展示。也可能需要设置是否平铺,等比例缩放等。
页面尽量不使用绝对定位,采用flex代替
vue变量提升性能
- 定义常量(缺点:模板中不能访问)
- 直接在this上挂载
- 自定义options
<template>
<div>{{ $options.myOptions.test }}</div>
</template>
export default {
data() { }, // 自定义options和data同级
// 取值时: this.$options.myOptions.test
myOptions: { test: '111' }
}
使用背景样式填充时不生效问题?
// background-size属性不生效
background-size: 100% 100%;
background: url('https://xxx.png';) no-repeat; //这一句会覆盖之前的其它background属性
解决:使用background简写属性,如果顺序在其他background-size属性之后,会覆盖之前的;所以可以将background: url('https://xxx.png';) no-repeat;写在其他background属性之前;也可以不使用background: url('https://xxx.png';) no-repeat;导入图片,直接使用background-image,则不需要考虑顺序问题。
background: url('https://xxx.png';) no-repeat;
background-size: 100% 100%;
移动端复制内容到剪贴板
copyNum() {
let oInput = document.createElement('input')
// TODO:
oInput.value = 'hello'
document.body.appendChild(oInput)
oInput.select() // 选择对象
document.execCommand('Copy') // 执行浏览器复制命令
document.body.removeChild(oInput)
}
///////////////////
/**
* 函数功能:复制内容到剪贴板
* @param {*} value : 需要复制的内容
* @returns promise
*/
const copyToClipboard = value => {
return new Promise((resolve, reject) => {
let tempInput = document.createElement('input')
tempInput.value = value
document.body.appendChild(tempInput)
tempInput.select()
document.execCommand('Copy')
document.body.removeChild(tempInput)
resolve()
})
}
copyToClipboard(this.couponInfo?.coupon_alias)
.then(() => this.$toast.success(this.$t('newUserModal.copySuccess')))
.catch(error => this.$toast.fail('Failed!'))
css实现按钮禁用
.disabled {
pointer-events: none;
}
+
@click.stop阻止冒泡
nuxtJSX使用
备注:this.$emit('updateOrderDetail')发射时,updateOrderDetail首字母是小写,但在render中监听时加on并且updateOrderDetail的首字母大写。参数也可以带过去。
查找某个属性最终的样式来源
p元素中有行内元素时,行内元素默认没有从左上角显示
解决办法:间隙可以使用font-size=0去除间隙(具体可以查看fontSize:0妙用)
行内有时候没有从左上角显示,因为默认的基线问题,行内的基线和p元素的基线。此时设置fontsize=0
input内容居中和光标大小
光标:通过fontSize调整光标大小 内容居中:通过设置line-height === input的height使得内容垂直居中显示
动画设置height:auot不生效
可借助 max-height 的特性来做到动态高度的伸缩。因为transition动画仅支持部分css属性(备注:针对width/height的auto属性不支持)
具体解析见:www.cnblogs.com/coco1s/p/14…
XXX
空心三角形
两个三角形叠在一起,借助transparent
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
.box {
border: 1px solid pink;
position: relative;
width: 200px;
}
.one {
border: 100px solid transparent;
box-sizing: border-box;
border-bottom: 100px solid #d6d6d6;
}
.two {
position: absolute;
top: 0px;
border: 80px solid transparent;
box-sizing: border-box;
}
</style>
</head>
<body>
<div class="box">
<div class="one"></div>
<div class="two"></div>
</div>
</body>
</html>
字体会戳出去
字体会戳出去,是因为默认是以单词分割的,如果是连续的字串,就会戳出去;可以考虑非单词也截断
ESLint 的 no-prototype-builtins 规则
最近在开发中遇到一个 ESLint 规则问题:
obj.hasOwnProperty('key')
ESLint 给出错误提示:
Do not access Object.prototype method 'hasOwnProperty' from target object. eslint(no-prototype-builtins)
当时没想明白这个规则的设计点在哪里。根据其文字提示“不要从目标对象访问 Object 原型方法”,想到一种解决方案——直接找到这个方法,用 call 改变指向调用:
Object.prototype.hasOwnProperty.call(obj, 'key')
背后原理:
今天在想到原型链的时候突然意识到为何 ESLint 不允许从目标对象调用 Object 原型方法。
在 JS 中,往往通过改变原型链实现继承。一旦原型链发生改变,原先可以访问到的原型属性、方法便可能无法访问。考虑最极端的情况,若 obj 原先原型链的最顶端是 Object,此时可以通过原型链访问 Object.hasOwnProperty 方法;而若改变后,顶端不再是 Object,那么访问 obj.hasOwnProperty 访问就会得到 undefined。因此,直接从对象访问原型方法,很可能会带来隐藏的 BUG。
input或者textare上边沿会有黑影
// 去除黑影
-webkit-appearance: none;
event.stopImmediatePropagation()
如果多个事件监听器被附加到相同元素的相同事件类型上,当此事件触发时,它们会按其被添加的顺序被调用。如果在其中一个事件监听器中执行 stopImmediatePropagation() ,那么剩下的事件监听器都不会被调用。
<!DOCTYPE html>
<html>
<head>
<style>
p { height: 30px; width: 150px; background-color: #ccf; }
div {height: 30px; width: 150px; background-color: #cfc; }
</style>
</head>
<body>
<div>
<p>paragraph</p>
</div>
<script>
const p = document.querySelector('p')
p.addEventListener("click", (event) => {
alert("我是p元素上被绑定的第一个监听函数");
}, false);
p.addEventListener("click", (event) => {
alert("我是p元素上被绑定的第二个监听函数");
event.stopImmediatePropagation();
// 执行stopImmediatePropagation方法,阻止click事件冒泡,并且阻止p元素上绑定的其他click事件的事件监听函数的执行.
}, false);
p.addEventListener("click",(event) => {
alert("我是p元素上被绑定的第三个监听函数");
// 该监听函数排在上个函数后面,该函数不会被执行
}, false);
document.querySelector("div").addEventListener("click", (event) => {
alert("我是div元素,我是p元素的上层元素");
// p元素的click事件没有向上冒泡,该函数不会被执行
}, false);
</script>
</body>
</html>
局部loading实现
商品列表的展示,针对操作的某一商品做局部loading;比对新老列表的时候,不能直接将整个列表赋值,因为渲染时会造成整个商品列表塌陷,只能挨个比对商品,然后替换对应操作的商品。
<template>
<div class="loading-outer">
<div v-if="isShowLoadingMoudle" class="loading-moudle">
<span class="loading-icon">
<i :class="localIcon" />
</span>
<span class="loading-txt">{{ loadingTxt }}</span>
</div>
<div :class="activeLoadingClass">
<slot name="component" />
</div>
</div>
</template>
<script>
export default {
props: {
// 是否loading
isLoading: {
type: Boolean,
default: false
},
// loading文案
loadingTxt: {
type: String,
default: 'Loading'
},
// loading图标
loadingIcon: {
type: String,
default: 'cart_loading_icon'
}
},
data() {
return {
isShowLoadingMoudle: false
}
},
computed: {
localIcon() {
return `iconfont icon${this.loadingIcon}`
},
activeLoadingClass() {
return this.isLoading ? 'loading-warpper active-loading-wrapper' : 'loading-warpper'
}
},
watch: {
isLoading: {
immediate: true,
handler(val) {
this.isShowLoadingMoudle = val
}
}
}
}
</script>
<style lang="scss" scoped>
.loading-outer {
position: relative;
}
.loading-warpper {
position: relative;
background: #ffffff;
}
.active-loading-wrapper {
background: #ffffff;
opacity: 0.3;
}
.loading-moudle {
display: flex;
flex-direction: column;
align-items: center;
position: absolute;
z-index: 100;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
.loading-icon {
width: 64px;
height: 64px;
display: flex;
justify-content: center;
align-items: center;
animation: rotate-all-time 0.8s infinite linear both;
-webkit-animation: rotate-all-time 0.8s infinite linear both;
}
.loading-txt {
margin-top: 16px;
font-size: 28px;
font-weight: 400;
color: #222222;
line-height: 34px;
}
}
.iconcart_loading_icon {
font-size: 55px;
color: #222222;
}
@keyframes rotate-all-time {
0% {
transform: rotate(0deg);
transform-origin: center;
}
100% {
transform: rotate(360deg);
transform-origin: center;
}
}
@-webkit-keyframes rotate-all-time {
0% {
transform: rotate(0deg);
transform-origin: center;
}
100% {
transform: rotate(360deg);
transform-origin: center;
}
}
</style>
elementPlus的el-select绑定对象记录
需要借助value-key属性
// 示例1:
<el-form-item label="省份:">
<el-select
v-model="currentProvince.data"
placeholder="请选择"
value-key="name"
@change="changeSelectProvince"
>
<el-option
v-for="province of provinceAndCityList"
:label="province.name"
:value="province"
:key="province.id"
/>
</el-select>
</el-form-item>
const changeSelectProvince = (value: any) => {
currentProvince.data = value;
currentCity.data = {};
};
判断属性在对象内(in/hasOwnProperty)
有时候我们需要判断对象中存在某个属性值,有可能他们的值是null/''/0/undefined这些不确定的值;所以不能单纯用if判断,if默认转为boolean判断了
in: in方式可以判断对象的自有属性和继承来的属性是否存在
let obj = {
name: ''
}
'name' in obj // true
hasOwnProperty:该方法只能判断自有属性是否存在,对于继承属性会返回false。
let obj = {
name: ''
}
obj.hasOwnProperty('name') // true
判断文本超过3行则显示浮窗(ts)
/**
* compute difference between offsetHeight and lineHeights which includes many lines
* --Do not need to pay attention to size of screen
* @param el DOMElement
* @param lines lines of text (default: 3 lines)
* @returns boolean
*/
export const computeEleLineHeightByLines = (
el: Element,
lines: number = 3
): boolean => {
const styleObj: CSSStyleDeclaration = window.getComputedStyle(el);
const lineHeight: number = Number(styleObj?.lineHeight?.replace(/px$/, ''));
// --support all size of screen including zoom in and out
return (
Math.abs((el as HTMLElement).offsetHeight - lineHeight * lines) < lineHeight
);
};
数组对象去重(ts)
/**
* filter to repeat Object by the second parameter
* @param sourceArr source array
* @param key keyword used to filter
* @returns array including object of no repeat
*/
export const filterRepeatObjectInArrayByKey = <T, K extends keyof T>(
sourceArr: T[],
key: K
): T[] => {
if (!key) return sourceArr;
if (Array.isArray(sourceArr) && sourceArr?.length === 0) return sourceArr;
const tempObj: any = {};
return sourceArr?.reduce((arr: T[], next: T) => {
!!tempObj[next[key]]
? undefined
: ((tempObj[next[key]] as boolean) = true) && arr.push(next);
return arr;
}, []);
};
arco的UI组件库修改组件样式不生效问题(因为某些脱离文档流的,在局部组件的scoped中通过类型其实是访问不到的,所以需要再增加style不要scoped)
<style lang="less" scoped></style>
<style lang="less">
.user-duty-popover {
padding: 0;
border: none;
box-shadow: 0 2px 10px 0 rgba(29, 33, 41, 0.12);
box-shadow: 0 9px 28px 0 rgba(29, 33, 41, 0.04);
.arco-popover-content {
width: 360px;
max-height: 230px;
margin-top: 0;
padding: 16px !important;
color: #86909c;
font-weight: 400;
font-size: 14px;
line-height: 22px;
letter-spacing: 0;
}
}
</style>
hover元素调试问题
比如hover之后有浮窗,此时查看浮窗dom;鼠标放在待查看的dom,可以鼠标右键之后,什么都不用点,然后就可以移动鼠标到控制台。
关于elementUI库中的el-table表格的横向滚动条
每次重置滚动条:下面是通过在浏览器控制台查看到bodyWrapper;也可以在node_moudles中查看
<el-table
ref="partTimeTableRef"
:data="partTimeJobData"
/el-table>
this.$nextTick(() => {
this.$refs.partTimeTableRef.bodyWrapper.scrollLeft = this.$refs?.partTimeTableRef?.bodyWrapper?.scrollLeft && 0
// this.$refs.partTimeTableRef.$el.querySelector('.el-table__body-wrapper').scrollLeft = this.$refs?.partTimeTableRef?.$el?.querySelector('.el-table__body-wrapper')?.scrollLeft && 0
})