15、什么是强缓存和协商缓存?
强缓存:直接从本地副本比对读取, 不去请求服务器 ,返回的状态码是 200 。 协商缓存: 会去服务器比对 ,若没改变才直接读取本地缓存,返回的状态码是 304。 强缓存主要包括 expires 和 cache-control 。 expires 是 HTTP1.0 中定义的缓存字段。 当我们请求一个资源,服务器返回时,可以在 Response Headers 中增加 expires 字段表示资源的过期时间
16、 说说你对@reduxjs/toolkit的理解?和react-redux有什么区别?
Redux
redux就是提供了一个叫store的容器里面的state存放了全局的数据状态,对外提供了三个方法getState(), dispatch(), subscribe()
getState(): 用来获取state的值,dispatch(action)用来发起一个action告诉一个叫reducer的函数怎么去更新state,同时把上一次的state作为参数也传给reducer, reducer拿到参数后,返回更新后的state, 得到新的state后就需要渲染组件,可以手动去调用render方法,但这样恒麻烦,通过subscribe接受一个调用render的函数放在一个数组中,每次dispatch的时候除了会通过reducer改变state,还会遍历还数组中的函数去调用,这样每次数据发生变化就可以重新去渲染组件。
react-redux
react-redux跟redux不同的是,它是专门为react服务的,它将redux中store的概念和React中context的概念结合起来,解决了相对复杂的react应用中不同组件共享状态传值的问题,它提供一个Provider的容器组件,该组件接收外界通过props将store传给它,并将store放在context中,子组件可以在connect的时候取到store,Connect接收mapStateToProps(该诉高阶组件需要什么样的数据,是一个接收state值作为参数返回所需state对象的函数), mapDispatchToProps(该诉高阶组件怎么样去触发dispatch,是一个函数,接收dispatch作为参数返回包含触发dispatch的函数的对象)作为参数返回一个以当前业务组件作为参数的高阶组件,明白了redux的实现原理,熟悉React,react-redux中间件的原理也就明白了。
17、 如何通过原生js实现一个节流函数和防抖函数?
js防抖
特点:
- 当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间(非常短的时间);
- 当事件密集触发时,函数的触发会被频繁的推迟;
- 只有等待了一段时间也没有事件触发,才会真正的执行响应函数;
应用场景:
- Ø输入框中频繁的输入内容,搜索或者提交信息;
- Ø频繁的点击按钮,触发某个事件;
- Ø监听浏览器滚动事件,完成某些特定操作;
- Ø用户缩放浏览器的resize事件;
实现方式
第三方库实现
<input type="text"> <!--html标签--> <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.1/underscore-umd-min.js"></script> <script> const inputEl = document.querySelector("input") let counter = 0 const inputChange = function(event) { console.log(`发送了第${++counter}次网络请求`, this, event) } // 防抖处理 inputEl.oninput = _.debounce(inputChange, 2000)//当用户停止输入的时候延时2秒后执行 </script>
手动实现(原理)
基本实现:
function debounce(fn, delay) { // 1.定义一个定时器, 保存上一次的定时器 let timer = null // 2.真正执行的函数 const _debounce = function() { // 取消上一次的定时器 if (timer) clearTimeout(timer) // 延迟执行 timer = setTimeout(() => { // 外部传入的真正要执行的函数 fn() }, delay) } return _debounce }
this绑定/事件对象
function debounce(fn, delay) { // 1.定义一个定时器, 保存上一次的定时器 let timer = null // 2.真正执行的函数 const _debounce = function(...args) { // 取消上一次的定时器 if (timer) clearTimeout(timer) // 延迟执行 timer = setTimeout(() => { // 外部传入的真正要执行的函数 fn.apply(this, args) }, delay) } return _debounce }
第一次立即执行
function debounce(fn, delay, immediate = false) { // 1.定义一个定时器, 保存上一次的定时器 let timer = null let isInvoke = false // 2.真正执行的函数 const _debounce = function(...args) { // 取消上一次的定时器 if (timer) clearTimeout(timer) // 判断是否需要立即执行 if (immediate && !isInvoke) { fn.apply(this, args) isInvoke = true } else { // 延迟执行 timer = setTimeout(() => { // 外部传入的真正要执行的函数 fn.apply(this, args) isInvoke = false }, delay) } } return _debounce }
取消功能
function debounce(fn, delay, immediate = false) { // 1.定义一个定时器, 保存上一次的定时器 let timer = null let isInvoke = false // 2.真正执行的函数 const _debounce = function(...args) { // 取消上一次的定时器 if (timer) clearTimeout(timer) // 判断是否需要立即执行 if (immediate && !isInvoke) { fn.apply(this, args) isInvoke = true } else { // 延迟执行 timer = setTimeout(() => { // 外部传入的真正要执行的函数 fn.apply(this, args) isInvoke = false timer = null }, delay) } } // 封装取消功能 _debounce.cancel = function() { if (timer) clearTimeout(timer) timer = null isInvoke = false } return _debounce }
函数返回值
<body> <input type="text"> <script src="./05_debounce-v5-函数返回值.js"></script> <script> const inputEl = document.querySelector("input") let counter = 0 const inputChange = function(event) { console.log(`发送了第${++counter}次网络请求`, this, event) // 返回值 return "aaaaaaaaaaaa" } // 防抖处理--拿到包装好的事件处理回调函数--回调函数的返回值是Promise const debounceChange = debounce(inputChange, 3000, false, (res) => { console.log("拿到真正执行函数的返回值:", res) }) const tempCallback = () => { debounceChange().then(res => { console.log("Promise的返回值结果:", res) }) } inputEl.oninput = tempCallback </script> </body> </html>
js节流
特点:
1.当事件触发时,会执行这个事件的响应函数;
2.如果这个事件会被频繁触发,那么节流函数会按照一定的频率来执行函数;
3.不管在这个中间有多少次触发这个事件,执行函数的频繁总是固定的;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-95oO1uXD-1672398315332)(D:/专高/专高4/ls/js节流与防抖/images/image-20221106190337808.png)]
应用场景:
Ø 游戏中的一些设计–王者荣耀 英雄的普攻;
Ø监听页面的滚动事件;
Ø 鼠标移动事件;
Ø 用户频繁点击按钮操作;
实现方式:
第三方库实现
<input type="text"> <!--html标签--> <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.1/underscore-umd-min.js"></script> <script> const inputEl = document.querySelector("input") let counter = 0 const inputChange = function(event) { console.log(`发送了第${++counter}次网络请求`, this, event) } // 节流处理 inputEl.oninput =_.throttle(inputChange, 2000)//当用户不断触发事件按照2秒的频率执行 </script>
手动实现(原理)
基本实现:
function throttle(fn, interval, options) { // 1.记录上一次的开始时间 let lastTime = 0 // 2.事件触发时, 真正执行的函数 const _throttle = function() { // 2.1.获取当前事件触发时的时间 const nowTime = new Date().getTime() // 2.2.使用当前触发的时间和之前的时间间隔以及上一次开始的时间, 计算出还剩余多长事件需要去触发函数 const remainTime = interval - (nowTime - lastTime) if (remainTime <= 0) { // 2.3.真正触发函数 fn() // 2.4.保留上次触发的时间 lastTime = nowTime } } return _throttle }
控制开始执行一次(默认开启,后续可以通过参数来关闭)
function throttle(fn, interval, options = { leading: true, trailing: false }) { // 1.记录上一次的开始时间 const { leading, trailing } = options let lastTime = 0 // 2.事件触发时, 真正执行的函数 const _throttle = function() { // 2.1.获取当前事件触发时的时间 const nowTime = new Date().getTime() if (!lastTime && !leading) lastTime = nowTime // 2.2.使用当前触发的时间和之前的时间间隔以及上一次开始的时间, 计算出还剩余多长事件需要去触发函数 const remainTime = interval - (nowTime - lastTime) if (remainTime <= 0) { // 2.3.真正触发函数 fn() // 2.4.保留上次触发的时间 lastTime = nowTime } } return _throttle }
最后执行一次
function throttle(fn, interval, options = { leading: true, trailing: false }) { // 1.记录上一次的开始时间 const { leading, trailing } = options let lastTime = 0 let timer = null // 2.事件触发时, 真正执行的函数 const _throttle = function() { // 2.1.获取当前事件触发时的时间 const nowTime = new Date().getTime() if (!lastTime && !leading) lastTime = nowTime // 2.2.使用当前触发的时间和之前的时间间隔以及上一次开始的时间, 计算出还剩余多长事件需要去触发函数 const remainTime = interval - (nowTime - lastTime) if (remainTime <= 0) { if (timer) { clearTimeout(timer) timer = null } // 2.3.真正触发函数 fn() // 2.4.保留上次触发的时间 lastTime = nowTime return } if (trailing && !timer) { timer = setTimeout(() => { timer = null lastTime = !leading ? 0: new Date().getTime() fn() }, remainTime) } } return _throttle }