1. 如何使用 css 画一个三角形
借助 border 实现,在 width 和 height 都为 0 时,设置 border,便会呈现三角形。想要哪个方向的三角形,设置其他三边为 透明即可。
同时,可以通过调整不同边的宽度,来调整三角形的高度和宽度。
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>三角符号</title>
<style>
/*记忆口诀:盒子宽高均为零,三面边框皆透明。 */
div:after {
position: absolute;
width: 0;
height: 0;
content: " ";
border-right: 100px solid transparent;
border-top: 100px solid #000;
border-left: 100px solid transparent;
border-bottom: 100px solid transparent;
}
</style>
</head>
<body>
<div></div>
</body>
2. 移动端怎么做适配?
要区分响应式设计和移动端适配。
响应式设计是指一套代码,同时适配移动端,pc 端以及平板端。
而移动端适配,解决的是由于不同机型手机宽度不同导致的元素占比和设计稿不一致的问题。
(1)使用百分比布局,但是由于百分比依赖父元素的百分比,计算起来比较困难,不建议,但是可以实现移动端不同宽度屏幕的适配
(2)使用 vw/vh 布局,始终相对于视口宽高,比百分比换算简单,但是需要自己对着设计稿换算
(1vh 等于1/100的视口高度,1vw 等于1/100的视口宽度。
比如:浏览器高度950px,宽度为1920px, 1 vh = 950px/100 = 9.5 px,1vw = 1920px/100 =19.2 px。)
(3)使用 rem 布局,rem 是相对于 html 的 font-size 进行换算,1 rem = 1 html font-size,一般会将 font-size 设置为 1/10 的 屏幕宽度,然后通过 postcss-pxtorem 这样的插件进行自动将代码中的 px 转换成 rem,来避免开发者自己计算
( rem是一个相对单位,相对于html元素的字体大小,使用rem作为尺寸单位可以让页面元素在不同设备上保持等比例缩放。)
(4)vw + rem: 直接将 html 的 font-size 设置为 10 vw,也就是 1/10 的屏幕宽度,然后通过 pxtorem 的工具自动对项目中的 px 转换为 rem。
3. 如何隐藏一个元素
(1) display: none
。 元素不可见,不占据空间,不可响应事件。会触发重绘和回流
(2) visibility: hidden
。元素不可见,占据空间,不可响应事件,会触发重绘
(3) opacity: 0
。 元素不可见,占据空间,可响应事件,会触发重绘
(4) position: absolute
移动到到视区外。元素不可见,不占据空间,不响应事件。重绘
(5) position: relative
移动到视区外。元素不可见,不可响应事件。占据空间,会触发重绘
(6) 设置宽高为 0。元素不可见,不会响应事件。会触发重绘和回流
(7)transform
移动到视区外。元素不可见,占据空间,不可响应事件,触发重绘
一般来讲,display: none
和 visibility: hidden
是正常的方案,其他的属性并非为了隐藏元素而出现。仅仅只是可以实现。
4. 文字溢出如何处理,多行文本溢出呢?
(1)单行文本溢出
涉及到文本控制的有三个 css 属性:
white-space: nowrap
控制文本只显示一行,是其他属性生效的基础
overflow: hidden
超出隐藏,是 text-overflow 生效的基础
text-overflow: ellipsis
文本超出显示省略号
(2)多行文本溢出
涉及到多行文本控制,webkit 内核的浏览器提供了特殊的支持,由于移动端大部分都是 webkit 内核,涉及到的 css 属性如下:
display: -webkit-box;
// 限制显示行数
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden
超出隐藏,是 text-overflow 生效的基础
text-overflow: ellipsis
文本超出显示省略号
更通用的写法:
通过控制高度 + overflow: hidden 隐藏多余文本,再通过伪元素提供省略号支持。
5. 常见的性能优化方案
首先明确衡量页面性能的指标:
在说方案前,得先明确指标。一般来讲,以下几个指标更为重要:
(1) FCP (First Contentful Paint),首次内容绘制。是用户第一次看到页面内容的时间点,直接影响用户对页面加载速度的感知。
(2) LCP (Largest Contentful Paint),最大内容绘制。在一般的页面中,大型图片的绘制,或者从接口返回的数据绘制的内容,会影响用户的首次视觉体验。也是一个重要指标
(3) TTI (Time to Interactive),可交互时间。用户看到页面,且用户可以进行交互的时间。这一指标,也是非常重要的一环。页面加载完,但是不可交互,也是非常糟糕的体验。
(4) 其他指标,如 完全加载时间(Fully Loaded Time), 累积布局偏移 (Cumulative Layout Shift,CLS),都会影响用户体验。
如何查看页面指标:
(1)chrome devtools 中,lighthouse 可以分析页面指标
(2) chrome devtools 中,performance 也可以查看页面指标
(3) 使用 web-vitals npm 包来获取关键指标
(4) 自己调用 PerformanceObserver 来获取关键指标
performance:
Lighthouse
使用 web-vitals 来查看
什么事情会影响页面加载性能?
(1) 加载 html 文件,解析 html,加载 css 文件,加载 js 文件
(2) 构建 css 树
(3) 构建 dom 树
(4) 如果 js 没有 defer, async 标签或 type=”module” 的设置,则会阻塞 html 解析,下载并执行完后继续解析
(5) 触发 FCP
(6) 执行 defer(或 type = “module”) 的 js,一般执行完成之后,页面绘制完成,事件绑定完成触发 TTI,而页面中,如果有异步数据渲染的,LCP 还会延迟。
(7) 直到异步数据返回并完成渲染,此时如果有大的图片资源,会等到图片渲染完触发 LCP
(8) 所有资源加载完,触发完全加载时间
(9) 在此期间,页面的闪动会被记录,称为 累积布局偏移 (Cumulative Layout Shift,CLS),体现了页面加载中的闪动情况,也比较影响体验
如何提升页面性能?
针对 加载 html 文件,加载 css 文件,加载 js 文件,大的图片资源,所有资源加载。
都是网络相关的,就有两个方向:
(1) http 缓存
现代的 html 文件,一般都是渲染一个空的 div,通过 js 渲染。所以 html 采用协商缓存或者不缓存(体积小),但是 js 文件和 css 文件,如果内容变化,可以通过打包工具追加 hash,从而可以使用强缓存,下次变化后,hash 发生改变,自然会加载新的 js 文件。对于图片资源,直接使用强缓存,要修改图片资源,替换图片链接即可(而不是在原链接上覆盖)
(2) 减小文件资源
html 压缩(优化有限,本来也不大);
js, css 文件压缩 (借助打包工具);
图片资源压缩 (借助打包工具或直接使用 tinypng 这样的现成平台);
图片资源转为 webp,也可以减少体积
除此之外,对于 js,css 文件,可以采用懒加载进行首页和其他路由的拆包:
懒加载非首屏资源,一般通过 import ‘xxx’.then 进行,或者使用 react, vue 官方提供的 lazy 组件即可完成。
也可以通过配置 tree-shaking,提取公共模块等手段,优化 js 体积
其他:
js 阻塞 html 解析,异步请求返回后再渲染。
js 文件使用 defer 或者 type=”module” 的方式加载,现代打包工具默认已经做了这一层。webpack 使用 defer, vite 使用 type=”module”,来避免对 html 解析的阻塞。
异步请求,如果是移动端,可以通过让移动端提前请求,然后页面中直接获取的方式,称为接口预请求。
如果是在 pc 端,可以使用 localStorage 对接口返回进行缓存,下次渲染先使用缓存进行渲染,接口返回后再使用新数据渲染。
总结:
(1) 首先明确一般指标:FCP,LCP,TTI,CSP 等
(2) 通过 devtools 中的 lighthoust, performance 可以查看这些指标
(3)根据页面渲染流程,划分为几个方向: http 缓存和减小文件体积,以及接口预请求或缓存,以及 js 的加载方式来进行优化
6. 前端安全有多少了解?
前端安全常见的漏洞:跨站脚本攻击: xss 和 跨站请求伪造: csrf
(1)跨站脚本攻击: xss
本质在于将用户的输入直接渲染出来。
此时就可能渲染 script 标签,执行一些 js 逻辑。
通常发生在论坛评论的输入,此输入直接展示在评论区,此时注入恶意脚本,获取用户信息等带来极大隐患.
不管是 vue 还是 react,对要渲染的内容,都会进行转义后再渲染。
但是也提供了不转义的手段,dangerousHTML(react)、vue (v-html),会渲染未转义的内容,此时需要注意,如果是用户输入,不建议使用
(2)跨站请求伪造: csrf
核心在于,浏览器访问某域名时,会自动携带 cookie。从而导致在该域名登陆的用户,会直接被认为是用户自己的操作,而无意间完成了一些不符合用户预期的操作
比如在网站 A 中,有一个接口,transfor/money,那么当用户在网站 A 登陆后。用户有访问了恶意网站 B,B 中有一个表单,用户提交后就导航到 transfor/money,由于此时用户已经登陆了 A,那么就会完成这一操作。
本质是由于 cookie 的自动携带带来的权限问题,一般有三个解决方案:
(1)设置 cookie 的 SameSite 属性,防止点击或者其他域名通过 img, iframe 等方式访问本域名时自动携带 cookie(一般用Lax,会禁止 form 表单的 post 提交请求,img, iframe 的请求,但是不会禁止 form 的 get 请求和链接跳转导航的请求的 cookie。所以将 samesite 设置为 lax,同时所有的重要接口都使用 post 即可杜绝 csrf。)
(2) 服务端每次返回页面时,都携带一个 token,每次页面发送请求,都需要携带该 token,从而避免纯 cookie 校验出错(服务端每次页面加载都会返回一个 token 令牌,一般是隐藏的 dom 节点,网页端发起请求必须携带该 token,此时,由于 csrf 的请求是浏览器自动发送的,所以并不会携带 token,从而判定这是非法请求,从而杜绝 csrf)
(3)不使用 cookie 技术,转而使用 jwt 等技术进行登陆验证(由于 csrf 的本质是 cookie 的自动携带导致,所以不使用 cookie,即可杜绝 csrf。jwt存储在localstorage中)
7. 图片懒加载如何实现?
图片懒加载是一种常用的优化手段,对于用户看不到的图片,先不做加载。直到滚动到视区,才会进行加载。
这样做的好处在于:
一方面,减少首屏渲染的图片数量从而提升渲染性能
另一方面,可以减少带宽消耗,节省用户流量
如何实现?
(1) 使用浏览器的原生支持 loading=”lazy”
(2) 使用 Intersection Observer 实现
利用自定义属性在 img 标签上存储图片链接。src 可以指向默认图或不指定。
使用 intersectionObserver 对所有元素进行监听,如果进入视区,则将 src 替换为自定义属性中的 src,同时移除对该元素的监听。
优点:代码简单,性能好
缺点:兼容性比 loading 稍好,但是也一般。
除此之外,intersectionObserver 也经常同来监听元素的曝光。
(3) 使用 getBoundingClientRect 实现
监听 scroll 事件,依此判断元素是否位于视区内,如果在视区内,则将 src 替换为自定义属性中的 src,完成图片加载,并移除元素。
优点:兼容性好
缺点:性能差,监听了 scroll 事件,频繁执行性能稍差
8. 前端中三个页面同时调用一个函数怎么实现
要在前端的三个页面同时调用一个函数,你可以通过以下几种方法来实现:
方法1:使用全局 JavaScript 文件
如果你有一个公共的 JavaScript 文件,并且这三个页面都引用了这个文件,那么可以在全局文件中定义一个函数,并在每个页面加载时调用它。
- 创建一个全局 JavaScript 文件,例如
global.js
:// global.js function myFunction() { console.log("Function called from all pages"); // 其他的操作 }
- 在每个页面中引用这个文件并调用函数:
<script src="path/to/global.js"></script> <script> myFunction(); </script>
方法2:使用模块化 JavaScript
如果你在使用模块化 JavaScript(例如 ES6 模块),可以创建一个模块并在不同的页面中导入它。
(1) 创建一个模块文件,例如 myModule.js
:
export function myFunction() {
console.log("Function called from all pages");
// 其他的操作
}
(2) 在每个页面中导入并调用这个函数:
<script type="module">
import { myFunction } from './path/to/myModule.js';
myFunction();
</script>
方法3:使用事件广播
如果页面之间需要通信,你可以使用浏览器的 localStorage
或者 sessionStorage
事件监听来广播事件。
(1)在一个页面中调用函数并设置 localStorage
:
// Page 1
function myFunction() {
console.log("Function called from page 1");
// 其他的操作
localStorage.setItem("callFunction", Date.now());
}
(2)在其他页面中监听 storage
事件:
// Page 2 and Page 3
window.addEventListener('storage', function(event) {
if (event.key === 'callFunction') {
console.log("Function called from another page");
// 调用函数或者执行操作
}
});
方法4:使用 Service Workers
如果你想要更复杂的页面间通信机制,可以考虑使用 Service Workers 来实现页面间的协调。
这些方法适合不同的应用场景,具体选择哪种方法可以根据你的项目需求来定。