# 1.原生JS实现图片懒加载(考虑不重复加载以及节流)
知识点:视口位置判断,懒加载实现(data-set),节流等
![](https://box.kancloud.cn/fa84428874feeb553665b291bac30b70_300x297.png)
1.`Element.getBoundingClientRect()`方法返回元素的大小及其相对于视口的位置。具体解释及用法参考 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/Element/getBoundingClientRect)
通过 Element.getBoundingClientRect().top 与 window.innerHeight(当前视窗的高度)比较就可以判断图片是否出现在可视区域。注意这个 top 是相对于当前视窗的顶部的top值而不是一开始的顶部。
2.通过 Element.dataset 可以获取到为元素节点添加的`data-*`属性,我们可以通过这个属性来保存图片加载时的url。
``` html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
img {
display: inline-block;
width: 100%;
height: 300px;
background: gray;
}
</style>
</head>
<body>
<div class="box-image">
<img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i2/252339290/TB29PYUXnIlyKJjSZFrXXXn2VXa_!!252339290-0-beehive-scenes.jpg_180x180xzq90.jpg_.webp" alt="">
<img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i4/2260152888/O1CN01vw2e251XCkJr0VPZU_!!2260152888-0-beehive-scenes.jpg_250x250xz.jpg" alt="">
<img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i3/24687421/TB2Xg1izsyYBuNkSnfoXXcWgVXa_!!24687421-0-beehive-scenes.jpg_250x250xz.jpg" alt="">
<img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i4/890151842/TB2II1mnZbI8KJjy1zdXXbe1VXa_!!890151842-0-beehive-scenes.jpg_250x250xz.jpg" alt="">
<img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i2/2096513830/TB2l1W0kRnTBKNjSZPfXXbf1XXa_!!2096513830-0-beehive-scenes.jpg_250x250xz.jpg" alt="">
<img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i3/2586222636/TB2RDGrpqAoBKNjSZSyXXaHAVXa_!!2586222636-0-daren.jpg_250x250xz.jpg" alt="">
<img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i2/1870112525/TB2Ae.xbOwIL1JjSZFsXXcXFFXa_!!1870112525-2-beehive-scenes.png_250x250xz.jpg" alt="">
<img src="" class="image-item" lazyload="true" data-original="https://img.alicdn.com/imgextra/i2/2194952831/TB2PwWty7qvpuFjSZFhXXaOgXXa_!!2194952831-0-beehive-scenes.jpg_250x250xz.jpg" alt="">
</div>
<script>
var viewHeight = document.documentElement.clientHeight; // = window.innerHeight?
// 节流:加一个300ms的间隔执行
function throttle(fn, wait) {
let canRun = true
return function (...args) {
if (!canRun) return
canRun = false
setTimeout(() => {
fn.apply(this, args)
canRun = true
}, wait)
}
}
function lazyload() {
let imgs = document.querySelectorAll('img[data-original][lazyload]') // 获取文档中所有拥有data-original lazyload属性的<img>节点
imgs.forEach(item => {
if (item.dataset.original == '') {// HTMLElement.dataset属性允许无论是在读取模式和写入模式下访问在 HTML或 DOM中的元素上设置的所有自定义数据属性(data-*)集。
return
}
// 返回一个DOMRect对象,包含了一组用于描述边框的只读属性——left、top、right和bottom,
// 单位为像素。除了 width 和 height 外的属性都是相对于视口的左上角位置而言的。
let rect = item.getBoundingClientRect()
// 其top值是相对于当前视窗的顶部而言的而不是绝对的顶部,所以top值 < window.innerHeight的话图片就出现在底部了就需要加载
if (rect.bottom >= 0 && rect.top < viewHeight) {
let img = new Image()
img.src = item.dataset.original
// 图片加载完成触发load事件?
img.onload = function () {
item.src = img.src
}
// 移除属性的话就不会重复加载了
item.removeAttribute('data-original')
item.removeAttribute('lazyload')
}
})
}
// 先调用一次加载最初显示在视窗中的图片
lazyload();
let throttle_lazyload = throttle(lazyload, 300)
document.addEventListener('scroll', throttle_lazyload)
</script>
</body>
</html>
```
# 2.如何渲染几万条数据且不卡住页面?
① 利用文档碎片,实现一次性插入多个节点,减少回流
② 分批次地插入节点而不是一次性插入,防止阻塞
③ 使用`requestAnimationFrame`让浏览器选择最为合适的渲染间隔
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
</head>
<body>
<ul>
控件
</ul>
<script>
const total = 100000 // 10万条数据
const once = 20 // 每轮插入的数据条目
const loopCount = total / once // 渲染总次数
let countOfRender = 0
let ul = document.querySelector('ul')
function add() {
// 使用文档碎片优化性能
const fragment = document.createDocumentFragment()
for (let i = 0; i < once; i++) {
const li = document.createElement('li')
li.innerText = Math.floor(Math.random() * total)
fragment.appendChild(li)
}
ul.appendChild(fragment)
countOfRender+=1
loop()
}
function loop() {
if (countOfRender < loopCount) {
window.requestAnimationFrame(add) // 使用requestAnimationFrame每隔16ms(浏览器自己选择最佳时间)刷新一次
}
}
</script>
</body>
</html>
```
# 3.写函数实现任意标签转换成 json 形式
```javaScript
/*
<div>
<span>
<a></a>
</span>
<span>
<a></a>
<a></a>
</span>
</div>
*/
function DOM2JSON(str) {
let reg = /<(.+)>(.*?)<\/\1>/g
let result = null
let nodes = []
while((result = reg.exec(str))!== null) {
nodes.push({ tag: result[1], children: DOM2JSON(result[2])}) // exec返回的数组,[0]匹配的字符串 然后依次是捕获的分组 然后有index和input属性
}
return nodes.length > 1 ? nodes : nodes[0]
}
console.log(JSON.stringify(DOM2JSON('<div><span><a></a></span><span><a></a><a></a></span></div>')))
// {"tag":"div","children":[{"tag":"span","children":{"tag":"a"}},{"tag":"span","children":[{"tag":"a"},{"tag":"a"}]}]}
```
这里主要利用了exec函数会在上一次匹配的结果之后继续匹配,且如果未匹配成功会返回null,然后注意下exec和正则表达式分组的使用即可。
# 4.判断执行顺序(事件循环)
```javaScript
console.log('begin'); // 1.begin
setTimeout(() => {
console.log('setTimeout 1'); // 3.setTimeout 1
Promise.resolve() // Promise.resolve().then ?
.then(() => {
console.log('promise 1'); // 5.promise 1
setTimeout(() => {
console.log('setTimeout2'); // 8. setTimeout2
});
})
.then(() => {
console.log('promise 2'); // 7. promise 2 注意7比8要快,会同时放入宏任务和微任务队列?
});
new Promise(resolve => {
console.log('a'); // 4. a
resolve();
}).then(() => {
console.log('b'); // 6. b
});
}, 0);
console.log('end'); // 2.end
```
# 5.实现一个 sleep 函数
```js
async function test () {
console.log('start')
await sleep(3000)
console.log('3s has passed')
}
function sleep (ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, ms)
})
}
```
# 6.meta 标签的作用
meta 标签分两大部分:HTTP 标题信息(http-equiv)和页面描述信息(name)。
1、声明文档使用的字符编码
```html
<meta charset='utf-8'>
```
以下设置更为详细:
```html
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
```
该 meta 标签定义了 HTML 页面所使用的字符集为 utf-8 ,就是万国码。它可以在同一页面显示中文简体、繁体及其它语言(如日文,韩文)等
2、声明文档的兼容模式
```html
// 指示IE以目前可用的最高模式显示内容
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="X-UA-Compatible" content="IE=Emulate IE7" />
// 指示IE使用 <!DOCTYPE> 指令确定如何呈现内容。标准模式指令以IE7 标准模式显示,而 Quirks 模式指令以 IE5 模式显示
```
3、SEO 优化
```html
<meta name="description" content="不超过150个字符" /> // 页面描述
<meta name="keywords" content="html5, css3, 关键字"/> // 页面关键词
<meta name="author" content="魔法小栈" /> // 定义网页作者
// 定义网页搜索引擎索引方式,robotterms是一组使用英文逗号「,」分割的值,通常有如下几种取值:none,noindex,nofollow,all,index和follow。
<meta name="robots" content="index,follow" />
```
4、为移动设备添加 viewport
```html
<meta name ="viewport" content ="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
/*
content 参数解释:
width viewport 宽度(数值/device-width)
height viewport 高度(数值/device-height)
initial-scale 初始缩放比例
maximum-scale 最大缩放比例
minimum-scale 最小缩放比例
user-scalable 是否允许用户缩放(yes/no)
minimal-ui iOS 7.1 beta 2 中新增属性,可以在页面加载时最小化上下状态栏。这是一个布尔值,可以直接这样写:
*/
<meta name="viewport" content="width=device-width, initial-scale=1, minimal-ui">
```
例如如下设置:
```html
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
```
• 强制让文档与设备的宽度保持 1:1 ;
• 文档最大的宽度比列是1.0( initial-scale 初始刻度值和 maximum-scale 最大刻度值);
• user-scalable 定义用户是否可以手动缩放( no 为不缩放),使页面固定设备上面的大小;
更多内容参考 [https://blog.csdn.net/xmtblog/article/details/46226717](https://blog.csdn.net/xmtblog/article/details/46226717)
- 序言 & 更新日志
- H5
- Canvas
- 序言
- Part1-直线、矩形、多边形
- Part2-曲线图形
- Part3-线条操作
- Part4-文本操作
- Part5-图像操作
- Part6-变形操作
- Part7-像素操作
- Part8-渐变与阴影
- Part9-路径与状态
- Part10-物理动画
- Part11-边界检测
- Part12-碰撞检测
- Part13-用户交互
- Part14-高级动画
- CSS
- SCSS
- codePen
- 速查表
- 面试题
- 《CSS Secrets》
- SVG
- 移动端适配
- 滤镜(filter)的使用
- JS
- 基础概念
- 作用域、作用域链、闭包
- this
- 原型与继承
- 数组、字符串、Map、Set方法整理
- 垃圾回收机制
- DOM
- BOM
- 事件循环
- 严格模式
- 正则表达式
- ES6部分
- 设计模式
- AJAX
- 模块化
- 读冴羽博客笔记
- 第一部分总结-深入JS系列
- 第二部分总结-专题系列
- 第三部分总结-ES6系列
- 网络请求中的数据类型
- 事件
- 表单
- 函数式编程
- Tips
- JS-Coding
- Framework
- Vue
- 书写规范
- 基础
- vue-router & vuex
- 深入浅出 Vue
- 响应式原理及其他
- new Vue 发生了什么
- 组件化
- 编译流程
- Vue Router
- Vuex
- 前端路由的简单实现
- React
- 基础
- 书写规范
- Redux & react-router
- immutable.js
- CSS 管理
- React 16新特性-Fiber 与 Hook
- 《深入浅出React和Redux》笔记
- 前半部分
- 后半部分
- react-transition-group
- Vue 与 React 的对比
- 工程化与架构
- Hybird
- React Native
- 新手上路
- 内置组件
- 常用插件
- 问题记录
- Echarts
- 基础
- Electron
- 序言
- 配置 Electron 开发环境 & 基础概念
- React + TypeScript 仿 Antd
- TypeScript 基础
- 样式设计
- 组件测试
- 图标解决方案
- Algorithm
- 排序算法及常见问题
- 剑指 offer
- 动态规划
- DataStruct
- 概述
- 树
- 链表
- Network
- Performance
- Webpack
- PWA
- Browser
- Safety
- 微信小程序
- mpvue 课程实战记录
- 服务器
- 操作系统基础知识
- Linux
- Nginx
- redis
- node.js
- 基础及原生模块
- express框架
- node.js操作数据库
- 《深入浅出 node.js》笔记
- 前半部分
- 后半部分
- 数据库
- SQL
- 面试题收集
- 智力题
- 面试题精选1
- 面试题精选2
- 问答篇
- Other
- markdown 书写
- Git
- LaTex 常用命令