那些值得⼀看的前端代码片段

5 阅读3分钟

25年针对前端现况,给仍然打算留着这个行业的前端人一份鼓励,希望大家能在这个行业得到更高的收入!!

如何从0写一份高通过率的前端简历。
如何应对面试中的前端算法题?
2小时速通Vue3源码解析

1. 检测元素之外的点击

在实现隐藏弹窗或收起下拉框时,如果你还在⼀层层判断是否点击了某个元素之外的区域,赶紧试试 使⽤ contains ⽅法来实现。

1 document.addEventListener('click', function (evt) {
2 // isClickedOutside 为 true 如果点击的元素在 ele 之外
3 const isClickedOutside = !ele.contains(evt.target);
4 });
5

2. 快速打开官⽹

当你想查看第三⽅库的主⻚和代码仓库时,你可以使⽤⼀下命令快速打开:

1 // 打开主⻚
2 npm home PACKAGE_NAME
3 npm home react
4
5 // 打开代码仓库
6 npm repo PACKAGE_NAME
7 npm repo react
8

3. ⼀次性的事件监听

除了在监听的事件函数中移除当前的监听外,也可以使⽤ once 参数。

1 const handler = function (e) {};
2 ele.addEventListener('event-name', handler, { once: true });

4. 格式化时分秒

在展⽰⾳视频时⻓之类的场景时,需要把时⻓秒数格式为 HH:mm:ss 的格式。

1 const formatSeconds = (s) =>
2 [parseInt(s / 60 / 60), parseInt((s / 60) % 60), parseInt(s % 60)]
3 .join(':')
4 .replace(/\b(\d)\b/g, '0$1')
5

如果你想显⽰“刚刚”、“5分钟前”之类的内容,可以尝试 timeago.js 库。

5. URL 参数转化为对象

获取 url 参数有个热⻔的库 query-string,如果不想使⽤的话,可以通过 URLSearchParams API 实现。

1 const getUrlParams = (query) =>
2 Array.from(new URLSearchParams(query)).reduce(
3 (p, [k, v]) =>
4 Object.assign({}, p, { [k]: p[k] ? (Array.isArray(p[k]) ? p[k] :
[p[k]]).concat(v) : v }),
5 {}
6 )
7
8 // 获取 query 参数
9 getUrlParams(location.query)
10 // { a: ['1', '4'], b: '2', c: '3' }
11 getUrlParams('?a=1&b=2&c=3&a=4')
12
13 // 获取 hash 参数
14 getUrlParams(location.hash.split('?')[1])
15

6. 打开新⻚签

看似平平⽆奇的打开⻚签,但是需要关注下 rel ,如果要打开外链,建议设置为 noopener noreferrer ,避免⼀些恶意⽹站通过 window.opener.location 重定向你的⽹站地址。

window.open ⽅法同理。

1 // ⾼版本浏览器 rel 默认为 noopener,不过建议显⽰设置,兼容低版本。
2 <a target="_blank" rel="noopener noreferrer">...</a>
3
4 // window.open rel 默认为 opener,需要⾃⼰设置
5 window.open('https://baidu.com', 'baidu', 'noopener,noreferrer')
6
7 // 以下有安全漏洞,打开的新⻚签可以通过 window.opener.location 重定向你的⽹站
8 <a target="_blank" rel="opener">...</a>
9 window.opener.location = 'http://fake.website.here';
10

7. 显⽰上传的图⽚

通过 fileReader API 的 readAsDataURL ⽅法来显⽰上传图⽚

1 function readImage() {
2 const fileReader = new FileReader()
3 const file = document.getElementById('uploaded-file').files[0]
4
5 if (file) {
6 fileReader.readAsDataURL(file)
7 }
8
9 fileReader.addEventListener(
10 'load',
11 () => {
12 const result = fileReader.result
13 const resultContainer = document.getElementById('result')
14 const img = document.createElement('img')
15 img.src = result
16 resultContainer.append(img)
17 },
18 { once: true }
19 )
20 }
21

8. ⽂件下载

使⽤ a 标签的 download 属性,同源才能触发下载,IE 不⽀持,移动端兼容性也不太好。

1 <a href="/path/to/file" download>Download</a>
2
3 // 或者 js 临时⽣成 a
4 function download(url) {
5 const link = document.createElement('a')
6 link.download = 'file name'
7 link.href = 'url'
8
9 document.body.appendChild(link)
10 link.click()
11 document.body.removeChild(link)
12 }
13

静态资源服务器设置响应头也能触发浏览器下载。

1 Content-Disposition: attachment; filename="filename.jpg"

除了在线⽂件下载,你还可以创建⼀个 text 或 json ⽂件,并下载,主要⽤到了 Blob 对象和 createObjectURL ⽅法。

1 const data = JSON.stringify({ 'message': 'Hello Word' });
2
3 const blob = new Blob([data], { type: 'application/json' });
4
5 // 创建⼀个 URL
6 const url = window.URL.createObjectURL(blob);
7
8 // ⽤上⾯的 download ⽅法下载这个 url
9 ...
10
11 // 释放创建的 URL
12 window.URL.revokeObjectURL(url);
13

9. 缓存结果

缓存函数的结果,当计算⽐较复杂时可以使⽤。

1 const memoize = (fn) =>
2 (
3 (cache = Object.create(null)) =>
4 (arg) =>
5 cache[arg] || (cache[arg] = fn(arg))
6 )()

10. 多⾏省略号

单⾏或多⾏截断显⽰省略号,很常⽤的 CSS ⽚段。

1 .truncate {
2 overflow: hidden;
3 text-overflow: ellipsis;
4 white-space: nowrap;
5 }
6
7 .truncate {
8 display: -webkit-box;
9 -webkit-box-orient: vertical;
10 -webkit-line-clamp: 2;
11 overflow: hidden;
12 }
13

11. 选中最后⼏个元素

在实现隐藏弹窗或收起下拉框时,如果你还在⼀层层判断是否点击了某个元素之外的区域,赶紧试试 使⽤ contains ⽅法来实现。

1 // 前三个
2 li:nth-child(-n + 3) {
3 text-decoration: underline;
4 }
5
6 // 选中 2-5 的列表项
7 li:nth-child(n + 2):nth-child(-n + 5) {
8 color: #2563eb;
9 }
10
11 // 倒数两个
12 li:nth-last-child(-n + 2) {
13 text-decoration-line: line-through;
14 }
15

12. 滚动条样式

⾃定义滚动条样式也是很常⻅的需求,除了通过样式,也可以通过第三⽅库(如 better-scroll 等)来 实现⾃定义滚动条样式。

1 /*定义滚动条⾼宽及背景 ⾼宽分别对应横竖滚动条的尺⼨*/
2 ::-webkit-scrollbar {
3 width: 8px;
4 height: 8px;
5 }
6
7 /*定义滚动条轨道 内阴影+圆⻆*/
8 ::-webkit-scrollbar-track {
9 border-radius: 10px;
10 background-color: #fafafa;
11 }
12
13 /*定义滑块 内阴影+圆⻆*/
14 ::-webkit-scrollbar-thumb {
15 border-radius: 10px;
16 background: rgb(191, 191, 191);
17 }
18
19 // 较新的 API
20 body {
21 scrollbar-width: thin;
22 scrollbar-color: #718096 #edf2f7;
23 }
24

13. 百分⽐计算 - 最⼤余额法

计算百分⽐时,由于四舍五⼊,各个⽐例相加可能不等于 1,通过最⼤余额法可以保证总数为 1。

1 // 输出 ['32.56%', '6.97%', '27.91%', '32.56%']
2 getPercentWithPrecision([56, 12, 48, 56], 2)
3
4 // 具体最⼤余额法算法可以⽹上搜索查看
5 function getPercentWithPrecision(valueList, precision) {
6 // 根据保留的⼩数位做对应的放⼤
7 const digits = Math.pow(10, precision)
8 const sum = valueList.reduce((total, cur) => total + cur, 0)
9
10 // 计算每项占⽐,并做放⼤,保证整数部分就是当前获得的席位,⼩数部分就是余额
11 const votesPerQuota = valueList.map((val) => {
12 return val / sum * 100 * digits
13 })
14 // 整数部分就是每项⾸次分配的席位
15 const seats = votesPerQuota.map((val) => {
16 return Math.floor(val);
17 });
18 // 计算各项的余额
19 const remainder = votesPerQuota.map((val) => {
20 return val - Math.floor(val)
21 })
22
23 // 总席位
24 const totalSeats = 100 * digits
25 // 当前已经分配出去的席位总数
26 let currentSeats = votesPerQuota.reduce((total, cur) => total +
Math.floor(cur), 0)
27
28 // 按最⼤余额法分配
29 while(totalSeats - currentSeats > 0) {
30 let maxIdx = -1 // 余数最⼤的 id
31 let maxValue = Number.NEGATIVE_INFINITY // 最⼤余额, 初始重置为⽆穷⼩
32
33 // 选出这组余额数据中最⼤值
34 for(var i = 0; i < remainder.length; i++) {
35 if (maxValue < remainder[i]) {
36 maxValue = remainder[i]
37 maxIdx = i
38 }
39 }
40
41 // 对应的项席位加 1,余额清零,当前分配席位加 1
42 seats[maxIdx]++
43 remainder[maxIdx] = 0
44 currentSeats++
45 }
46
47 return seats.map((val) => `${val / totalSeats * 100}%`)
48 }
49

14. 限制并发

当有⼤量请求需要发起时,往往需求限制并发数量保证其他请求能优先返回。

1 async function asyncPool(poolLimit, iterable, iteratorFn) {
2 // ⽤于保存所有异步请求
3 const ret = [];
4 // ⽤⼾保存正在进⾏的请求
5 const executing = new Set();
6 for (const item of iterable) {
7 // 构造出请求 Promise
8 const p = Promise.resolve().then(() => iteratorFn(item, iterable));
9 ret.push(p);
10 executing.add(p);
11 // 请求执⾏结束后从正在进⾏的数组中移除
12 const clean = () => executing.delete(p);
13 p.then(clean).catch(clean);
14 // 如果正在执⾏的请求数⼤于并发数,就使⽤ Promise.race 等待⼀个最快执⾏完的请求
15 if (executing.size >= poolLimit) {
16 await Promise.race(executing);
17 }
18 }
19 // 返回所有结果
20 return Promise.all(ret);
21 }
22
23 // 使⽤⽅法
24 const timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i));
25 asyncPool(2, [1000, 5000, 3000, 2000], timeout).then(results => {
26 console.log(results)
27 })
28

15. uuid

⽣成 uuid 的代码⽚段

1 const uuid = (a) =>
2 a
3 ? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16)
4 : ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid)
5

16. 打开 Modal 时禁⽌ body 滚动

打开弹窗的时候,会发现背后的内容还是可以滚动,我们需要在弹窗出现时禁⽤滚动,在弹窗消失时恢复。

1 // 打开 Modal 时,禁⽌ body 滚动
2 document.body.style.overflow = 'hidden';
3
4 // 恢复滚动
5 document.body.style.removeProperty('overflow');

年后前端面试相信今天已经有人开始投了,有一些个人留存,涵盖(vue、js、react、低代码、工程化、性能优化、ts、node、webpack等)希望各位弥补不足,能够完善技能,提高收入!!!

需要讨论前端技术,或者有任何问题也可以【点击此处】,前端人互帮互助,加油!!!!