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; } }
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; }
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)直接放入微队列)
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');