1. 前端开发中常见的拖拽排序效果视频:https://www.bilibili.com/video/BV1Jm4y1M79D/?spm_id_from=333.880.my_history.page.click&vd_source=5c584bd3b474d579d0bbbffdf0437c70
2.axios请求二次封装都做些什么视频:https://www.bilibili.com/video/BV1fg4y1M7pe/?spm_id_from=333.880.my_history.page.click&vd_source=5c584bd3b474d579d0bbbffdf0437c70
3.Vue axios 前端接口请求封装API视频:https://www.bilibili.com/video/BV18R4y1J7sm/?spm_id_from=333.880.my_history.page.click&vd_source=5c584bd3b474d579d0bbbffdf0437c70
4.项目上线打包优化:https://www.bilibili.com/video/BV1z34y1T7ez/?spm_id_from=333.788&vd_source=5c584bd3b474d579d0bbbffdf0437c70
5.使用nginx部署vue项目及常见问题:https://www.bilibili.com/video/BV1x84y1k7qf/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=5c584bd3b474d579d0bbbffdf0437c70
6.es6 class的使用参考文章:https://juejin.cn/post/6964312782552432670
7.图片加水印:https://juejin.cn/post/6844903645155164174(网页加水印)
https://juejin.cn/post/7236286358389047354
8.开发中的难点亮点:https://juejin.cn/post/7074573053979525151
9.react hooks的学习视频:https://www.bilibili.com/video/BV1ZB4y1Z7o8/?spm_id_from=333.337.search-card.all.click&vd_source=5c584bd3b474d579d0bbbffdf0437c70 , https://www.bilibili.com/video/BV1sV411c7u9/?spm_id_from=autoNext&vd_source=5c584bd3b474d579d0bbbffdf0437c70
10.react开发中,结合useState(list)给数组排序可以使用 lodash这个库来使用。lodash官网:lodash.com / lodash.com/docs/
11.react开发中,className动态添加类名可以加载classnames包:使用方法在下面
12.vue自定义指令与修饰符文章:https://blog.csdn.net/m0_71485750/article/details/126175776
13.react全家桶+底层原理:https://www.bilibili.com/video/BV17e411r7TH/?spm_id_from=333.337.search-card.all.click&vd_source=5c584bd3b474d579d0bbbffdf0437c70
上述11点,使用classnames包的方法
1.命令安装classnames包
npm install classnames
2.在组件中引入
import classNames from 'classname';
3.在组件内使用, ‘’包裹的是静态的class,
{
// active是动态的class名,值是判断条件,是否需要加active这个class
active: type === item.type
}包裹的是动态的class
从这样的:nav-item为静态class类名,active为动态class类名
<span className={`nav-item ${type === item.type && 'active'}`}>span...</span>
变成这样:
<span className = {classNames('nav-item', {
active: type === item.type
})}>span...</span>
webpack是一个用于现代javascript应用程序的 静态模块打包工具。当webpack处理应用程序时,它会在内部从一个或多个入口点构建一个依赖图,然后将项目中所需的每一个模块组合成一个或多个包(bundles),他们均为静态资源,用于展示内容。
loader: loader让webpack能够去处理那些非javescript文件(webpack自身只理解javascript)
plugins:插件(plugins)可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量等。
(1)每个构造函数都有一个原型对象,原型对象都包含指向构造函数的指针,而实例都包含一个指向原型对象的内部指针(proto)
(2)一个对象会指向一个原型,原型对象会有自己的原型,以此类推,构成原型链
(3)实例使用方法和属性时,会先从构造函数内部找,找不到再去原型对象上找,还找不到就去原型对象的原型上找,直到(原型对象的原型为null) Object.prototype.proto===null,停止寻找
继承:(4)继承是指能够访问另外一个对象中的方法和属性。
(1)Array.from(new Set(array))
(2)newArray.filter 返回[item]/newArray.find 返回item项
(3)newArray.include(旧item) 返回true/false
(4)使用对象属性名的唯一性来保证不重复 let obj = {} if(!obj[array[i]]) { result[array[i]] = true } Object.keys(obj);
(5)使用indexOf(item) === -1, newArray.indexOf(arr1[i]) === -1
(6)使用Map数据结构去重: let map = new Map(); if (map.has(array[i])) {map.set(array[i], true)}
(1)全局环境下指向widow,严格模式下undenfind, 函数也指向window。
(2)对象内部方法的this指向调用这些方法的对象,也就是谁调用就指向谁。
(3)箭头函数中的this指向于函数作用域所用的对象。
(4)构造函数中的this是指向实例
更改this指向:call / apply / bind()方法
用法(Promise对象是一个构造函数,用来生成Promise实例)
let p = function () {return new Promise((resolve,reject) => {resolve(‘需要返回出去的值’)})}
p.then(data => {})
(1)事件循环机制:
Node.js采用事件驱动机制,通过事件轮询的方式实现异步I/O操作。当有异步I/O操作完成时,会将对应的事件加入到事件队列中,由事件循环机制负责调度执行。事件循环机制是Node.js实现高并发的核心,它能够充分利用CPU资源,减少I/O的阻塞等待时间,提高了处理请求的效率。
(2)非阻塞I/O:
Node.js采用非阻塞I/O,避免了I/O的阻塞等待时间,从而提高程序的并发性能。非阻塞I/O是指在进行I/O操作时,不会阻塞后续代码的执行,而是在后台等待I/O操作完成后立即返回结果。这种方式可以减少线程切换的开销,提高了整个应用程序的执行效率。
(3)事件驱动回调函数:
Node.js采用事件驱动回调函数,使得应用程序能够及时响应用户的请求。通过这种方式,当用户请求进入系统时,会获取一个事件处理器,并注册一个回调函数,当事件处理器产生事件时,会自动调用注册的回调函数进行处理。这种方式能够提高系统的响应速度和处理能力。
(4)内置异步模块:
Node.js内置的异步模块也是实现高并发的关键。Node.js提供了各种内置的异步模块,例如HTTP、HTTPS、FS、Net等,这些模块都是通过事件循环机制结合非阻塞I/O实现的,可以在高并发的情况下高效地处理请求。
(5)Cluster模块:
Cluster是Node.js中用于多进程处理的模块。该模块可以启用多个子进程来处理请求,从而提高应用程序的并发性能。每个子进程都是独立的,因此可以充分利用CPU资源,提高系统的处理能力。使用Cluster模块可以有效地解决单线程模型下的瓶颈问题,使得系统能够更加高效地处理大量的请求。
模块在第一次加载后会被缓存。这也意味着多次调用require()不会导致模块的代码被多次执行。
(1)内置模块的加载优先级最高:例如,require(‘fs’) 始终返回内置的 fs 模块,即使在 node_modules 目录下有名字相同的包也叫做 fs。
(2)自定义模块的加载机制:
使用 require() 加载自定义模块时,必须指定以 ./ 或 …/ 开头的路径标识符。在加载自定义模块时,如果没有指定 ./ 或 …/ 这样的路径标识符,则 node 会把它当作内置模块或第三方模块进行加载。
同时,在使用 require() 导入自定义模块时,如果省略了文件的扩展名,则 Node.js 会按顺序分别尝试加载以下的文件:
1.按照确切的文件名进行加载
2.补全 .js 扩展名进行加载
3.补全 .json 扩展名进行加载
4.补全 .node 扩展名进行加载
5.加载失败,终端报错
(3)第三方模块的加载机制:如果传递给 require() 的模块标识符不是一个内置模块,也没有以 ‘./’ 或 ‘…/’ 开头,则 Node.js 会从当前模块的父目录开始,尝试从 /node_modules 文件夹中加载第三方模块。
例如,假设在 ‘C:\Users\itheima\project\foo.js’ 文件里调用了 require(‘tools’),则 Node.js 会按以下顺序查找:
C:\Users\itheima\project\node_modules\tools
C:\Users\itheima\node_modules\tools
C:\Users\node_modules\tools
C:\node_modules\tools
(4)目录作为模块:当把目录作为模块标识符,传递给 require() 进行加载的时候,有三种加载方式:
在被加载的目录下查找一个叫做 package.json 的文件,并寻找 main 属性,作为 require() 加载的入口
如果目录里没有 package.json 文件,或者 main 入口不存在或无法解析,则 Node.js 将会试图加载目录下的 index.js 文件。
如果以上两步都失败了,则 Node.js 会在终端打印错误消息,报告模块的缺失:Error: Cannot find module ‘xxx’
(1)延长局部变量的寿命
(2)使外部能够访问到函数内部的变量
例子: 如果一个属性是全局变量,不想让人直接访问,局部变量又访问不到时,暴露一个函数让间接访问;
function(){
var lives = 50
window.奖励一条命 = function(){ // 简明起见,用了中文
lives += 1
}
window.死一条命 = function(){
lives -= 1
}
}()
(1)URL 解析:浏览器首先对 URL 解析,解析出协议、域名、端口、资源路径、参数等。
(2)DNS 域名解析:首先需要将一个域名转化为相应的 IP 地址
(3)建立TCP连接
(4)发送HTTP请求:当浏览器与服务器建立连接后,就可以进行数据通信过程,浏览器会给服务器发送一个 HTTP 请求报文,请求报文包括请求行、请求头、请求空行和请求体。在请求行中会指定方法、资源路径以及 HTTP 版本,其中资源路径是指定所要操作资源在服务器中的位置,而方法是指定要对这个资源做什么样的操作。
(5)服务器对请求进行处理并作出响应
(6)浏览器解析渲染页面
(7)断开TCP连接
注意:
浏览器为了提升性能,在 URL 解析之后,实际会先查询是否有缓存,如果缓存命中,则直接返回缓存资源。
如果是 HTTPS 协议,在建立 TCP 连接之后,还需要进行 SSL/TLS 握手过程,以协商出一个会话密钥,用于消息加密,提升安全性。
Vue的生命周期钩子函数主要包括:
不过需要注意的是mounted生命周期钩子中并不代表界面已经渲染成功,因为 mounted 不会保证所有的子组件也都一起被挂载。如果你希望等到整个视图都渲染完毕,可以在 mounted 内部使用 vm.$nextTick。
js面试题:
<div ref="text"></dev>
beforeMouted() {
console.log(this.$refs.text); // 会打印什么
}
mouted() {
console.log(this.$refs.text); // 会打印什么
}
<body>
<div class="box1">
<!-- offsetTop, scrollTop -->
<!-- getBoundingClientRect -->
<!-- IntersectionObserver -->
</div>
<div class="box2" id="box2">123456</div>
<style>
.box1 {
height: 1000px;
}
</style>
</body>
<script>
console.log(window.innerHeight)
console.log(document.getElementById('box2').offsetTop)
console.log(document.documentElement.scrollTop)
console.log(document.getElementById('box2').getBoundingClientRect())
// 方法一:offsetTop - scrollTop <= window.innerHeight;
function isInViewPortOfOne(el) {
// viewPortHeight 兼容所有浏览器写法
const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
const offsetTop = el.offsetTop;
const scrollTop = document.documentElement.scrollTop;
const top = offsetTop - scrollTop;
return top <= viewPortHeight;
}
// 方法二:getBoundingClientRect
// {
// top, // 元素顶部距离屏幕顶部的距离
// bottom, // 元素底部距离屏幕顶部的距离
// left, // 元素左边距离屏幕左边的距离
// right, // 元素右边距离屏幕右边的距离
// x,
// y,
// width,
// height, // 元素的宽和高
// }
/**
* 1.top 大于等于0
* 2.left大于等于0
* 3.bottom小于等于视窗高度
* 4.right小于等于视窗宽度
*/
function isInViewPortOfOne(el) {
const viewWidth = window.innerWidth || document.document.clientWidth;
const viewHeight = window.innerHeight || document.document.clientHeight;
const {
top,
right,
bottom,
left,
} = el.getBoundingClientRect();
return (
top >= 0 &&
left >= 0 &&
right <= viewWidth &&
bottom <= viewHeight
)
}
// 以上方法可以实现,但是需要监听浏览器滚动,需要有大量计算
// 方法三:Intersection Observer 浏览器原生提供的构造函数,接收两个参数,callback和options
// callback会触发两次,元素刚进入可视区域时,元素完全看不到时
const intersectionObserver = new IntersectionObserver(function (entries) {
if (entries[0].intersectionRatio <= 0) return; // 小于0完全不可见,0-1部分可见,大于等于1完全可见
console.log('已经进入可视区域');
})
// 开始观察,监听元素
intersectionObserver.observe(document.getElementById('box2'));
// 停止观察
intersectionObserver.unobserve(document.getElementById('box2'));
// 关闭观察器
intersectionObserver.disconnect();
</script>
let a= {
name: 'A',
fun: function() {
console.log(this.name);
}
}
console.log(a.fun()); // A
console.log(a.fun.call({name: 'B'})); // B
let f1 = a.fun();
console.log(f1); // A
console.log(typeof undefined) // undefined
console.log(typeof null) // object
console.log(typeof console.log) // function
console.log(typeof [1, 2, 3]) // object
console.log(typeof '123') // string
console.log(typeof 123) // number
promise.all
promise.resolve
promise.reject
promise.race 与 all的区别
promise还有async和await
面试题:Promise.all一个请求失败也能得到其余正确的请求结果
参考文章:https://blog.csdn.net/qq_43592064/article/details/130829067
Promise.all默认只要有一个错误就直接返回错误。promise.all中任何一个promise 出现错误的时候都会执行reject,导致其它正常返回的数据也无法使用
Promise.all(
[
Promise.reject({ code: 500, msg: "服务异常" }),
Promise.resolve({ code: 200, list: [] }),
Promise.resolve({ code: 200, list: [] })
].map(p => p.catch(e => e))
)
.then(res => {
console.log("res=>", res);
})
.catch(error => {
console.log("error=>", error);
});
res=> [ { code: 500, msg: '服务异常' },
{ code: 200, list: [] },
{ code: 200, list: [] } ]
核心内容是map方法,map的每一项都是promise,catch方法返回值会被promise.reslove()包裹,这样传进promise.all的数据都是resolved状态的。
// 使用Promise.all 其中id为69的商品,返回失败,会导致整个Promise接受到reject状态.
// 所以进行改造, p catch 得到的err 为返回失败抛出的信息, 进行置空
.map(p => p.catch(err => '')))
参考视频
vue的created和mounted是同步执行,会先执行created里面的同步代码,然后把请求当成微任务挂起(先发送请求padding状态),然后再执行mounted里的同步代码,mounted里的代码执行完毕后,会渲染页面。请求结果才接收到,当收到结果后有更新data里面的数据,那么会再渲染一次。
看一下代码
<template>
<div>
<ul>
<li v-for="item in cityList" :key="item.id">{{item.name}}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
cityList: [],
},
created() {
conslole.log('created');
alert('created'); // 会阻止后续代码运行
this.getCityList();
},
mounted() {
conslole.log('mounted');
alert('mounted'); // 会阻止后续代码运行
},
methods: {
async getCityList() {
const res = await axions({...});
this.cityList = res.data.list;
}
}
}
</script>
执行上面代码顺序为:
conslole.log(‘created’);
alert(‘created’);
this.getCityList();发送请求,但是请求状态为padding,挂起等待状态
再执行mounted的代码
conslole.log(‘mounted’);
alert(‘mounted’); // mounted代码执行完毕加载页面,组件首次渲染
最后this.getCityList();请求执行完毕获取数据,组件重新渲染,刷新页面,所以会刷新两次页面
另外观点:参考文章:https://blog.csdn.net/dongwei666/article/details/126364290
总结:如果无需操作dom,可以在created里面进行请求,数据与dom同步渲染,减少渲染次数,但是不可过多请求,否则屏幕将会出现白屏
如果有dom操作就在mounted里面请求,也不可过多请求,否则渲染次数过多
export default new Router({
routes: [
{
path: '/login',
name: 'login',
component: Login
},
{
path: '/register',
name: 'register',
component: Register, // component: () => import 'xxx/xx.vue'
beforeEnter(to, from, next) {
console.log(to);
console.log(from);
console.log(next);
next(); // 放行,必须有,否则跳不到页面
next(false); // 禁止跳转路由点击无效
if (from.path === '/login') {
next(); // 如果从登录页面来的注册页面就放行跳到注册页面
} else {
// 重定向
next('/login'); // 如果不是,重定向到登录页面
}
}
}
]
})
const router = new Router({
router: [
{
path: '/login',
name: 'Login',
component: 组件/ import 'xx/xx.vue'
}
]
})
router.beforeEach((to,from, next) => {
...
next();
})
什么是原型?
在js中,每个构造函数内部都有一个prototype属性,该属性的值是个对象,该对象包含了该构造函数所有实例共享的属性和方法。当我们通过构造函数创建对象的时候,在这个对象中有一个指针,这个指针指向构造函数的prototype的值,我们将这个指向prototype的指针称为原型。或者用另一种简单却难理解的说法是:js中的对象都有一个特殊的[[Prototype]]内置属性,其实这就是原型。(简单来说原型就是对象或者函数下面的prototype属性)
如何获取对象原型值?
a.__proto__构造函数创建一个实例的过程
功能上:
使用场景:
computed----当一个属性受多个属性影响的时候,使用computed-----购物车商品结算。watch–当一条数据影响多条数据的时候,使用watch-----搜索框.
参考文档:https://zhuanlan.zhihu.com/p/156940153
em的特点:因为这两个特点,所以我们用em的时候,需要注意三点:用法: rem用法:以html根元素设置参考值
html {
font-size: 10px;
}
div {
font-size: 4rem; /* 40px */
width: 20rem; /* 200px */
height: 20rem;
}
p {
font-size: 2rem; /* 20px */
width: 10rem;
height: 10rem;
border: solid 1px blue;
}
hooks函数组件的缺点: 没法保存state, 如需保存state,则需要使用useRef();class 组件存在的问题:
大型组件很难拆分和重构,变得难以测试
相同业务逻辑分散到各个方法中,可能会变得混乱
复用逻辑可能变得复杂,如 HOC 、Render Props
所以 react 中更提倡函数式编程,因为函数更灵活,更易拆分,但函数组件太简单,所以出现了hook,hook就是用来增强函数组件功能的。
参考:http://jerryzou.com/posts/cookie-and-web-storage/
原生小程序生命周期构子:
uniapp小程序生命周期钩子:
三者异同
cookie 一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效,存放数据大小一般4K左右,而sessionStorage与localStorage大小在5兆左右,在客户端生成,localStorage除非被清除,否则会永久保存,sessionStorage仅在当前会话下有效,关闭页面或浏览器后被清除,cookie在与服务器端通信每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题,而sessionStorage与localStorage仅在客户端(即浏览器)中保存,不参与和服务器的通信。
参考文章:https://juejin.cn/post/6968094627375087653
const app = createApp(App)
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)
onBeforeMount
onMounted
onBeforeUpdate
onUpdated
onBeforeUnmount
onUnmounted
onErrorCaptured
onRenderTracked
onRenderTriggered
使用:
import { onBeforeMount, onMounted } from "vue";
export default {
setup() {
console.log("----setup----");
onBeforeMount(() => {
// beforeMount代码执行
});
onMounted(() => {
// mounted代码执行
});
},
}
响应式API:
知道父元素高度的:
<body>
<div class="box">
<div class="box-child">123456</div>
</div>
<style>
.box {
position: relative;
width: 300px;
height: 300px;
}
.box-child {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
</style>
</body>
<body>
<div class="box">
<div class="box-child">123456</div>
</div>
<style>
.box {
display: flex;
justify-content: space-around/center; // space-around/center都可以
align-items: center;
width: 300px;
height: 300px;
}
</style>
</body>
<body>
<div class="box">
<div class="box-child">123456</div>
</div>
<style>
.box {
display: table-cell;
text-align: center;
vertical-align: middle;
width: 300px;
height: 300px;
}
.box-child {
vertical-align: middle;
display: inline-block;
}
</style>
</body>
注意:此方法需要设置子元素宽高,width,height
<body>
<div class="box">
<div class="box-child">123456</div>
</div>
<style>
.box {
position: relative;
width: 300px;
height: 300px;
}
.box-child {
position: absolute;
width: 50px;
height: 50px;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
}
</style>
</body>
<body>
<div class="box">
<div class="box-child">123456</div>
</div>
<style>
.box {
width: 100vw;
height: 100vh;
line-height: 100vh;
text-align: center;
}
.box-child {
display: inline-block;
}
</style>
</body>
<script>
<ul id="box"></ul>
const ul = document.getElementById('box');
const totalNum = 1000000;
const onceNum = 20;
const page = totalNum / onceNum
let index = 0;
function loop(curTotal, curIndex) {
if (curTotal <= 0) {
return false;
}
const pageCount = Math.min(curTotal, onceNum);
window.requestAnimationFrame(() => {
let fragment = document.createDocumentFragment();
for (let i = 0; i < pageCount; i++) {
let li = document.createElement('li');
li.innerText = curIndex + i + ':' + ~~(Math.random() * total);
fragment.appendChild(li)
}
ul.appendChild(fragment);
loop(curTotal - pageCount, curIndex + pageCount);
})
}
loop(totalNum, index);
</script>
大小都在5MB左右
localstorage存储不是5m 是每个域5m 超了申请其他的域/修改ng配置 postmessge通信往其他域上存取,或者清除不需要的存储,压缩或减少体积,或者使用indexedDB
indexedDB参考文章:
https://juejin.cn/post/7026900352968425486
https://juejin.cn/post/7249386837369159735?from=search-suggest
keep-alive作为一种vue的内置组件,主要作用是缓存组件状态。当需要组件的切换时,不用重新渲染组件,避免多次渲染,就可以使用keep-alive包裹组件。
参考文章:https://juejin.cn/post/7091490734041219086


200 form memory cache : 不访问服务器,一般已经加载过该资源且缓存在了内存当中,直接从内存中读取缓存。浏览器关闭后,数据将不存在(资源被释放掉了),再次打开相同的页面时,不会出现from memory cache。
200 from disk cache: 不访问服务器,已经在之前的某个时间加载过该资源,直接从硬盘中读取缓存,关闭浏览器后,数据依然存在,此资源不会随着该页面的关闭而释放掉下次打开仍然会是from disk cache。
优先访问memory cache,其次是disk cache,最后是请求网络资源

原文链接:https://blog.csdn.net/qq_37288477/article/details/86582130
参考文章:https://zhuanlan.zhihu.com/p/559966882
1. 尽量将javascript文件放在body的最后
2. body中间尽量不要写<script>标签
3. <script>标签的引入资源方式有三种,一种就是我们常用的直接引入,还有两种就是使用async属性和defer属性来异步引入,
两者都是去异步加载外部的js文件,不会阻塞dom的解析(尽量使用异步加载),三者的区别如下:
*script立即停止页面渲染去加载资源文件,当资源加载完毕后立即执行js代码,js代码执行完毕后继续渲染页面;
*async是在下载完成之后,立即异步加载,加载好后立即执行,多个带async属性的标签,不能保证加载的顺序;
*defer是在下载完成后,立即异步加载。加载好后,如果dom树还没构建好,则先等dom树解析好再执行;如果dom树已经准备好,
则立即执行。多个带defer属性的标签,按照顺序执行。
*link: 浏览器会派发一个新等线程(http线程)去加载资源文件,于此同时GUI渲染线程会继续向下渲染代码
*@import: GUI渲染线程会暂时停止渲染,去服务器加载资源文件,资源文件没有返回之前不会继续渲染(阻碍浏览器渲染)
*style: GUI直接渲染
外部样式如果长时间没有加载完毕,浏览器为了用户体验,会使用浏览器的默认样式,确保首次渲染的速度。所有css一般写在heard中,让浏览器尽快发送请求去获取css样式。所以,再开发过程中,导入外部样式使用link,而不用@import。如果css少,尽可能采用内嵌样式,直接写在style标签中。
*HTML文件的代码层级尽量不要太深
*使用语义化的标签,来避免不标准语义化的特殊处理
*减少CSS代码的层级,因为选择器是从左向右进行解析的
*操作DOM树时,尽量在低代码层级的DOM节点进行操作
*不要使用table布局,一个小的改动可能会使整个table进行重新布局
*使用css的表达式
*不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式
*使用absolute或者fixed,使元素脱离文档流,这样他们发生变化就不会影响其他元素
*避免频繁操作DOM,可以创建一个文档片段docmentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中
*将元素先设置display:none,操作结束后再把它显示出来。因为display属性为none的元素上进行的DOM操作不会引发回流和重绘。
*将DOM的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于浏览器的渲染队列机智
浏览器针对页面的回流与重绘,进行了自身的优化-----渲染队列
浏览器会将所有的回流,重绘的操作放在一个队列中,当队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会对队列进行批处理。这样就会让多次的回流,重绘变成一次回流重绘。
将多个读操作(或写操作)放在一起,就会等所有的读操作进入队列之后执行,这样,原本应该是触发多次回流,变成了只触发一次回流。