刚在做OKR的一个项目,连续3天熬夜凌晨一两点肝到怀疑人生。一上线发现 bug,解决一下,再上线,我屮艸芔茻,竟然没有改对,还是有问题。我整个人就是下面的动图状态,哭爹喊娘,身后空无一人(基本一个项目一个前端)。只能硬着头皮分析问题,看代码。
之所以出现这种反复上线的问题,主要的原因是时间紧,任务重,测试不充分,还有就是自身能力不够,🙃 不能迅速及时对问题做出正确、快速的应对。第一次在互联网公司受到了现实的“毒打”。
既然苦都吃了,总得学着成长。所以用这篇文章记录一下,让自己长点记性。
上中下布局
前端面试有一个流行面试题目:是实现左、中、右布局,左侧和右侧固定,中间部分自适应。在做 OKR 项目中,我发现 UI 同学给的页面有一种很明显的特点就是上、中、下布局,上部分页面一般固定在顶部,下部分固定在页面底部,中间部分是随着内容的增多会出现滚动条,这种布局。哈哈哈,如果有幸成为面试官,我会出一道这种题目,来看下面试者的 CSS 能力。
主要是2点:
- 使用 flex 布局实现,中间部分设置
flex: 1;,上、下使用固定高度;实现页面整体布局。 - 最外层 container 设置
height: 100vh; overflow: hidden;; 中间部分的overflow: auto;即可。
<!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>中间内容自适应</title>
</head>
<body>
<div class="demo-container">
<div class="demo-container__head border">头部标题固定</div>
<div class="demo-container__content">
<div class="card card-item">card1</div>
...
<div class="card card-item">card6</div>
</div>
<div class="demo-container__footer border">
<button type="button" class="btn btn-primary">提交</button>
</div>
</div>
</body>
</html>
<style>
.demo-container{
height:100vh;
overflow: hidden;
background-color: #F6F6F6;
display: flex;
flex-direction: column;
}
.demo-container__head{
height: 66px;
background-color: #FFFFFF;
}
.demo-container__footer{
display: flex;
align-items: center;
justify-content: center;
background-color: #FFFFFF;
padding: 10px 16px;
}
.demo-container__content{
flex: 1;
overflow: auto;
}
.card-item{
width: 100%;
height: 400px;
margin-top: 10px;
}
</style>
自定义字体
项目中如果 UI 同学指定了想使用某种字体的时候,前端最好不要说 不,说“不”显得我们不专业,我们要说 没问题。哈哈哈哈~ 那么怎么实现项目中使用自定义字体呢?主要分两步:
- 先下载该字体库的字体。下载地址:www.fontke.com/ ,搜索对应的字体,比如:SF Pro Rounded
- 一般项目中只是某几个字需要使用上述字体;还需要使用 fontmin(ecomfe.github.io/fontmin/) 抽取出想要的字集
点击生成即可得到相应的字集:
根据项目对浏览器的兼容情况,大家按需使用即可:
iphon X 适配
iPhone X 需要适配的地方有两个一个是顶部的齐刘海,还有一个是底部的黑色的线。对于顶部齐刘海浏览器已经为我们做了适配。对于底部的黑色的线要做的适配通常需要两步:
第一步:设置 viewport-fit=cover, 使得网页内容占据整个手机屏幕可视区域,效果如下:
<meta name='viewport' content="width=device-width, viewport-fit=cover" />
第二步:通过设置 padding 使得网页内容在黑色线条之上正常显示:
body {
/* 适配齐刘海*/
padding-top: constant(safe-area-inset-top);
padding-top: env(safe-area-inset-top);
/* 适配底部黑条*/
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
bobtung.medium.com/%E9%9D%A2%E…
判断是否为空
周五上完最后一波儿线后,前方用户竟然还发现了 bug,新 bug,惊不惊喜,意不意外,没有按时下班走成。我大致看了一眼,是少了非空的判断,加一下就 OK 啦,再上线,我擦,复用了界面,数据为空的话,没有重新赋值,这就导致了脏数据的问题,继续改啊,继续上线啊,最终解决了 bug,安心下班了。
按照我之前写代码的习性,我都会做非空判断,唯独这个接口没有,为什么呢?因为按理来说,有了 ID,根据 ID 去查后端数据的话,肯定是有值的,然鹅,偏偏后端偶现了有数据却查询失败的情况,无奈啊,前端提示“系统异常”,F12 打开后:
这么小概率的 bug 都遇到了,我深感自己写代码一定一定得严谨,不能忽视 0.001% 可能存在的情况。
如果在写代码的时候偷懒,后面生产出了问题,还要看代码,解决 bug,花费的时间和精力更多,还不如一开始就不要怕麻烦,不要省事,认认真真写好代码。
所以在查询数据的时候一定要先判断数据是否为 空;在
try代码后加catch,不要影响页面后续操作。
页面返回定位到离开时的位置
三种高度(宽度)
详细说明:
-
clientHeight: 包括 padding, 但是不包括水平 scrollbar 的高度、border 和 margin;
即clientHeight = content + padding -
offsetHeight:包括 padding、border、水平 scrollbar 的高度。
即offsetHeight = content + padding + border + scrollbar height -
scrollHeight:DOM 元素实际内部的总高度
可以看出offsetHeight比clientHeight包括了 border 和 scrollbar height 更能反映出 DOM 的实际大小。
到这里大家可能会想起来 style 样式的height,那 style 样式里面的 height 指的是什么呢?这里的 height 根据 box-sizing 的不同取值表示不同的含义。盒模型传送门
一个关于三个高度计算的例子,注意例子中 box-sizing 是默认的取值 content-box
scroll 的一些属性
截图来自《高程》,比较形象的说明了scrollTop\scrollLeft 的含义了。
scrollTop 表示隐藏内容到滚动条的距离;在页面离开的时候,先记录下该值,页面返回的时候将滚动条移动到该值的位置,就可以实现离开页面是什么位置回来还在那里的效果了。
scroll 的一些方法
scrollBy(options?: ScrollToOptions): void;
scrollBy(x: number, y: number): void;
scrollTo(options?: ScrollToOptions): void;
scrollTo(x: number, y: number): void;
scrollIntoView(arg?: boolean | ScrollIntoViewOptions): void;
scrollBy 和 scrollTo 使用方法类似,作用于有滚动条的元素上,将滚动条滚动到指定位置;
scrollIntoView 方法会滚动元素的父容器,使被调用scrollIntoView()的元素对用户可见。
// 方式一:
const dom = document.getElementById('scrollWrap')
dom.scrollTo({
top: 420,
behavior: "smooth"
});
// 方式二:
dom.scrollBy(0, 420)
// 方式三:
const card2 = document.getElementById('card2')
card2.scrollIntoView()
scrollTop 是一个可读可写的属性,也可以直接设置 scrollTop 让滚动条到指定位置
const dom = document.getElementById('scrollWrap')
dom.scrollTop = 420
scroll 会失效的问题
发现一个 scroll 比较奇怪的点,你所不知道的scroll事件:为什么scroll事件会失效?
safari 返回不会重新加载数据
OKR 项目中,由于复盘使用了旧系统;点击复盘按钮跳入旧系统,然后从旧系统再返回回来。测试同学发现,已经有了复盘内容,但是页面并没有显示出来。进行分析后发现,旧系统采用router.back() 函数返回的,也就是window.history.back() 返回的。
在 Chrome 浏览器,window.history.back()是会触发 window.load 事件的;然鹅在 Safari下,window.history.back() 并不会触发 window.load 事件,这就导致数据不是最新的,还是之前缓存的数据。
Google 了一波儿后,发现手机上的浏览器有一个特性,名叫“往返缓存”(back-forward cache,或bfcache),可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。如果页面位于bfcache中,那么再次打开该页面就不会触发load事件。
解决方法: 监听 window 的pageshow 事件,如果是来自 cache,则执行 window 的reload方法。
const onPageShow = (event: any)=>{
if (event.persisted) {
window.location.reload()
}
}
window.addEventListener('pageshow', onPageShow)
需要注意的一点是:单纯的 Vue2 和 Vue3 中在created 或 setup 中绑定 window 的pageshow 是没有问题的;但是项目中往往会使用Vue Router,这个时候还在 Vue 页面组件的生命周期中注册 pageshow 是不会生效,要在实例化 APP 之前进行 pageshow 事件的注册。如下:
const onPageShow = (event)=>{
if (event.persisted) {
window.location.reload()
}
}
window.addEventListener('pageshow', onPageShow)
createApp(App).use(store).use(router).mount('#app')
Vue Router 4 + keep-alive
在 Vue Router 4 中使用keep-alive 的正确姿势:
利用 include 页面组件 Contact 的 name 一定要写成 “Contact” 与 include 后面的字符串保持一致。
<router-view v-slot="{ Component }">
<keep-alive include="Contact">
<component :is="Component" />
</keep-alive>
</router-view>
或者利用 meta: {keepAlive: true}, 统一设置:
<router-view v-slot="{ Component, route }">
<template v-if="route.meta.keepAlive">
<keep-alive>
<component :is="Component" />
</keep-alive>
</template>
<template v-else>
<component :is="Component" />
</template>
</router-view>
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
},
{
path: '/contact/:id',
name: 'Contact',
meta: {
keepAlive: true
},
component: () => import('../views/Contact.vue'),
}
]