前端零碎知识点


title: 前端零碎知识点
date: 2023-05-22 14:13:23
tags: js
category: ‘js’


使用SCSS 优化媒体查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
使用SCSS 优化媒体查询
@content 代表使用@mixin传入的CSS
$breakpoints 代表在SASS中定义一个MAP,
$bp:map-get($breakpoints, $breakpoints );代表定义一个变量$bp,在SASS中获取这个MAP,第一个参数是这个MAP,第二个参数是这个MAP的key
$min:nth($bp, 1 ); 定义一个变量$min 取$bp这个数组的第一个值
**/

$breakpoints: (
'phone':(320px,480px),
'pad':(481px,768px),
'pc':(769px,1680px),
);

@mixin respondTo($name) {
$bp:map-get($breakpoints, $name );
$min:nth($bp, 1 );
$max:nth($bp, 2 );
@media screen and (min-width: $min) and (max-width: $max) {
@content;
}
}

.header{
width: 100%;
@include respondTo('phone'){
width: 50px;
}
@include respondTo('pad'){
width: 100px;
}
}


  • 编译结果
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    .header {
    width: 100%;
    }
    @media screen and (min-width: 320px) and (max-width: 480px) {
    .header {
    width: 50px;
    }
    }
    @media screen and (min-width: 481px) and (max-width: 768px) {
    .header {
    width: 100px;
    }
    }


    clip-path 裁切图片的用法 http://tools.jb51.net/code/css3path

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    .clipPath {
    width: 500px;
    height: 400px;
    }
    .clipPath img{
    width: 100%;
    height: 100%;
    transition: all 1s;
    clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
    }
    .clipPath:hover img{
    clip-path: polygon(100% 0%, 100% 100%, 0% 100%, 0% 0%);
    }


    <div class="clipPath">
    <img src="https://pic.xcar.com.cn/img/07news/23/11/15/1865fe8da72410a1d7bcb81849af78ff.jpg?imageMogr2/format/jpg/size-limit/150k!/ignore-error/1" alt="">
    </div>

IntersectionObserver 用来判断元素是否在可视范围内

html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    <style>
ul{
list-style: none;
display: flex;
flex-wrap: wrap;
width: 600px;
margin: 0 auto;
}
ul li {
width: 200px;
margin: 30px;
}
ul li img {
width: 100%;
height: 100%;
}
</style>
</head>
<body>

<ul>

</ul>
<script src="./IntersectionObserver.js"></script>
</body>

IntersectionObserver.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var ul = document.querySelector('ul')
var ob = new IntersectionObserver(items=>{
for(const item of items) {
// item.isIntersecting 为true 代表item出现在视窗内,false代表不在视窗内
// item.target是元素本身
console.log(item.isIntersecting,item.target)
}
})

for (var i=0; i<20; i++) {
var item = document.createElement('li');
var img = document.createElement('img')
img.src = 'https://picsum.photos/200/300?random=' + i;
item.appendChild(img)
item.dataset.index = i +1;

ul.appendChild(item);
ob.observe(item)

}

滚动到相应位置

  • 使用传统的锚点方式 会产生一个 # 如果你使用的是VUE,REACT 这种方式和HASH路由有冲突
  • 可以使用 Element.scrollIntoView()
    1
    2
    3
    4
    可以使用 Element.scrollIntoView({
    behavior:'smooth'
    })

抛物线小球 (加入购物车效果)

  • 让父元素做横向运 动,子元素做向上抛物线运动,两个运动结合就产生了购物车那种抛物线小球
  • 内部小球调整贝塞尔曲线。让他先反向再正向运动
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    <div class="ball">
    <div class="innerBall"></div>
    </div>

    .ball{
    position: fixed;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    background-color: red;
    top: 100px;
    left: 300px;
    border: 2px solid #000;
    animation: moveX 2s linear forwards;
    z-index: 100;
    }
    .innerBall{
    width: 100%;
    height: 100%;
    border-radius: 50%;
    background-color: red;
    animation: moveY 2s cubic-bezier(0.3, -0.49, 0.32, 1.28) forwards;
    }
    @keyframes moveX {
    to{
    transform: translateX(200px);
    }
    }

    @keyframes moveY {
    to{
    transform: translateY(400px);
    }
    }

setup的子组件如果需要暴露自己的属性和方法给父组件 需要使用 defineExpose ,父组件用 在mounted中 用子组件.value可以使用

object-fit 给图片设置 cover.,,contain

background-clip 可以给文字设置透明(透明出下边的背景图)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.test {
position: relative;
width: 500px;
height: 600px;
background: linear-gradient(60deg, red, yellow, red, yellow, red);
font-weight: bold;
font-size: 50px;
background-clip: text;
-webkit-background-clip: text;
color: rgba(0,0,0,.2);
}

<div class="test">
我是文字
</div>

-webkit-text-stroke 设置文字描边

1
2
3
4
5
6
7
8
.miaobian {
-webkit-text-stroke: 2px red;
font-size: 50px;
color: #fff;
}

<p class="miaobian ">这个文字有描边</p>

使用浏览器原生的dialog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<button id="popBtn">弹窗</button>

<!-- <form> 元素可关闭含有属性 method="dialog" 的对话框。当提交表单时,对话框的 returnValue (en-US) 属性将会等于表单中被使用的提交按钮的 value -->
<dialog id="favDialog">
<div class="pop">
<form method="dialog">
<div>
<label for="">输入<input type="text"></label>
</div>
<div>
<label>Favorite animal:
<select>
<option value="default">Choose…</option>
<option>Brine shrimp</option>
</select>
</label>
</div>
<button id="confirmBtn">OK</button>
<!-- 不写这个favDialog.close()点取消按钮也能关闭弹窗 因为按钮在form里 -->
<button id="cancel" onclick="favDialog.close()">取消</button>
</form>
</div>
</dialog>

<script>

var popBtn = document.querySelector("#popBtn");
const favDialog = document.getElementById('favDialog');
const confirmBtn = document.getElementById("confirmBtn")
const cancelBtn = document.getElementById("cancel");

if(typeof favDialog.showModal !== 'function') {
console.log('不支持dialog')
}
popBtn.addEventListener("click",()=>{
favDialog.showModal()
})

favDialog.addEventListener('close', () => {
console.log('监听到弹窗关闭',favDialog.returnValue)
});


</script>

图片/气泡框添加阴影

  • 如果图片是SVG,并且不是矩形的(如示列中的火狐LOGO),我们添加阴影(box-shadow)的时候会是一个矩形的框,不是我们想要的
  • 如果你做了一个气泡DIV,他的小箭头 和整体要求一个阴影,但是你使用 box-shadow 也会达不到预期
  • 使用filter: drop-shadow(里边的属性和box-shadow一致);可以解决此类问题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
html:

图片:
<img class="shadow_img" src="http://www.firefox.com.cn/media/protocol/img/logos/firefox/browser/logo-word-hor.7ff44b5b4194.svg" alt="">

气泡框:
<div class="bubble">
气泡框
</div>

css:

.shadow_img {
filter: drop-shadow(0 0 15px #000);
}

.bubble {
width: 300px;
height: 200px;
margin-left: 30px;
position: relative;
display: inline-block;
padding: 10px;
background-color: #ccc;
border-radius: 6px;
filter: drop-shadow(0 0 15px #000);
}

.bubble::before {
content: "";
position: absolute;
top: 50%;
left:-30px;
transform: translateY(-50%);
border-width: 18px;
border-style: solid;
border-color: transparent #ccc transparent transparent
}

保持元素宽高比 aspect-ratio vs 传统方案

  • 假射有一个DIV,我们需要保持他的宽高比是4:3,用新的API aspect-ratio 可以很方便的做到这点
1
2
3
4
5
6
7
8
9
10
11
<div class="aspect-ratio-div">
保持元素宽高比一直是4:3
</div>

.aspect-ratio-div {
background-color: #000;
width: 40%;
margin: 0 auto;
/*兼容性一般*/
aspect-ratio: 4 / 3;
}
  • 传统方案 使用padding
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<div class="aspect-ratio-div">
<div class="inner">
<div class="content">这个最内部的DIV里再写元素,因为父级的DIV高度是0,是靠padding撑开的</div>
</div>
</div>



.aspect-ratio-div {
background-color: #ccc;
width: 40%;
margin: 0 auto;
}
.aspect-ratio-div .inner {
background-color: #000;
width: 100%;
height: 0;
/*这个子元素的高度要是父元素宽度的 3/4*/
/*padding 设置的百分比是相对于父元素的*/
padding-bottom: 75%;
color: #fff;
position: relative;
}
.aspect-ratio-div .inner .content {
position: absolute;
inset: 0; /*代表 top:0,left:0,bottom:0,right:0*/
}

旋转边框

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
.rotateBorder {
position: relative;
outline: 4px solid #000;
width: 200px;
height: 150px;
margin-left: 50px;
overflow: hidden;
box-sizing: border-box;
padding: 10px;

}
/*在他内部设置一个DIV,宽高超过父元素,一直旋转*/
.rotateBorder::before{
content: "";
position: absolute;
width: 200%;
height: 200%;
background-color: #f40;
z-index: -1; /*让他不要挡住内容*/
left: 50%;
top: 50%;
transform-origin: 0 0; /*设置旋转中心点在中间*/
animation: rotate 3s linear infinite;
}
/*再设置一个DIV,挡住旋转DIV的中间大部分,就留着旋转DIV的边部分能看到*/
.rotateBorder::after {
content: '';
position: absolute;
width: calc(100% - 20px);
height: calc(100% - 20px);
/* background-color: #ccc; */
left: 10px;
top: 10px;
background-color: #fff;
z-index: -1;
}

@keyframes rotate {
to {
transform: rotate(1turn); /*旋转1圈*/
}
}


<div class="rotateBorder">
旋转边框
</div>

使用compositionstart来解决中文输入法 实时搜索的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

const inp = document.querySelector('input');
let isComposing = false; //是否正在合成

function search() {
if(isComposing) return ;
console.log(inp.value)
}

inp.addEventListener('input', function(){
search()
})

inp.addEventListener('compositionstart', function(){
isComposing = true;
console.log('输入开始')
})

inp.addEventListener('compositionend', function(){
isComposing = false;
search()
console.log('输入结束')
})

渡一大师课

浏览器进程,线程

  • 一个进程就是开辟了一块内存空间,一个线程就是内存空间有一个干活的人。
  • 浏览器有可以有多个线程,多个进程。
  • 当前谷歌浏览器,每个TAB就是一个进程,防止互相影响。其中一个TAB崩坏也不会影响其他的

事件循环

  • 过去把消息队列分为宏队列和微队列,现在则根据不同的任务队列(主线程,微队列,延迟队列,交互队列等)分为多个队列,同类型任务在相同的队列,
    微队列有最高的优先级,主线程任务完成后优先调度微队列的任务(Promise.reslove().then(fn)直接放入微队列)

为什么使用transform做动画效率高

1
2
3
4
5
6
7
8
9
10
11
@keyframes move1 {
to {
transform:translate(100px)
}
}

@keyframes move2 {
to {
left : 100px
}
}
  • 动画都是偏移100PX 使用left 会引起重排(reflow) ,会占用浏览器的主线程,使用transform 不占用主线程 ,页面卡死也不会受影响。

处理并发请求,给你100个请求连接,每次只能同时发送3个请求,都发送完成后,返回一个结果[],里边是所有发送成功或者失败的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70


/**
* 处理并发请求,控制最大并发请求数,
* @param {string[]} urls 待请求的URLS 数组
* @param {*} maxNum 最大并发请求数
* @returns {Promise[]} Promise数组 每个Promise对应一个URL的返回结果(正确或者错误都要),顺序要按照urls数组顺序
*/
function concurRequest(urls,maxNum) {
return new Promise(resolve=>{

if(urls.length == 0) {
resolve([])
}

let index = 0; //下一次请求的URL地址,在URLS数组中对应的下标
let count = 0; //请求完成的数量,这里不能使用index表示请求完成的数量,因为index只代表发送了多少次请求,不能代表完成了几次;
const result = [];
async function request() {
const i = index; //保存原数组的下标,为了放到result[]的相应位置中

const url = urls[index];
index++;
try{
//每次请求的返回结果
const resp = await fetch(url);
result[i] = resp

}catch(err){
result[i] = err;

}
finally{
count++;
console.log(count,urls.length)
if(count == urls.length) {
console.log('全部请求完成!')
//全部请求完成
resolve(result);
}

//每次无论成功还是失败 运行完都要继续调用request()
if(index < urls.length) {
request();
}

}

}

for (let i=0; i<Math.min(maxNum,urls.length); i++) {
request();
}

})
}


//使用

var urls = []
for (let i=1; i<=20; i++) {
urls.push(`https://jsonplaceholder.typicode.com/todos/${i}`)
}

concurRequest(urls,3).then(resps=>{
console.log(resps)
})


使用compositionstart来解决中文输入法 实时搜索的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

const inp = document.querySelector('input');
let isComposing = false; //是否正在合成

function search() {
if(isComposing) return ;
console.log(inp.value)
}

inp.addEventListener('input', function(){
search()
})

inp.addEventListener('compositionstart', function(){
isComposing = true;
console.log('输入开始')
})

inp.addEventListener('compositionend', function(){
isComposing = false;
search()
console.log('输入结束')
})

限制请求重试次数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

/**
* 限制一个请求,如果请求失败的最大尝试次数
*/

function request(url,maxCount = 5) {
return fetch(url).then(res => {
console.log(res)
}).catch(err => {
if (maxCount > 0) {
return request(url,maxCount - 1)
}
else {
return Promise.reject(err)
}
})
}


request('https://adfaf.com/todos1').then(res => {

}).catch(err => {
console.log(err)
})

使用AbortController 来取消上一次请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 使用AbortController 来取消上一次请求,可用在输入框连续快速输入文字时候,每次输入都会取消上一次的请求,
* 防止请求过多,且避免返回的数据顺序错乱
*/

let controller ;
input.onInput = async () => {
// 用AbortController 来取消上一次请求
if (controller) {
controller.abort();
}
controller = new AbortController();
await fetch('https://www.baidu.com' + input.value, { signal: controller.signal });
}


vite 配置打包项目目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
export default defineConfig({
plugins: [vue()],

build: {
rollupOptions: {
output: {
//引入的文件放到JS下
entryFileNames:'js/[name].js',
//import 这种分包的也放到JS下
chunkFileNames:'js/[name].js',
//其他文件。包含CSS等根据自己的名字来
assetFileNames(assetInfo) {
if(assetInfo.name.endsWith('.css')) {
return 'css/[name].[ext]'
}
if(['.png','.jpg','.jpeg','.gif','.svg','.webp'].some(ext=>assetInfo.name.endsWith(ext))) {
return 'images/[name].[ext]'

}
//其余的资源
return 'assets/[name].[ext]'

}
}
}
}
})



使用CSS变量,JS获得的属性可以用于在CSS中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
.cssVar {
position: relative;
width: 500px;
height: 500px;
border: 1px solid red;
}
.cssvar-ball {
/* --w:500px; */
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: calc(var(--w) - 450px); /* 50px*/
height: 50px;
border-radius: 50%;
background-color: red;
animation: moveball 3s linear infinite;
}
/*
* transfrom中的100%代表自身的宽度
*/
@keyframes moveball {
0%{
transform: translateX(0);
}
50%{
transform: translateX(calc(var(--w) - 100%));
}
100%{
transform: translateX(0);
}

}


<div class="cssVar">
<div class="cssvar-ball"></div>
</div>


/**
* 如果不知道小球的外围容器的宽度,我们需要使用JS来计算获得外围容器的宽度,然后通过CSS变量来给CSS动画计算
*/

const cssVarContainer = document.querySelector('.cssVar');
cssVarContainer.style.setProperty('--w', cssVarContainer.clientWidth + 'px');