个人学习的笔记,偶尔忘记了,就翻出来看看.会不断的补充、完善.
也希望各位兄弟姐妹萌给我指点一二,提出宝贵的建议;
八股文
html、css
1.盒子模型
在html页面中所有元素都可以看成是一个盒子.
盒子的组成:包括content、内边距padding、边距border、外边距margin
盒模型的类型:
标准盒模型:margin+border+padding+content
怪异盒模型(IE):margin+content
控制盒模型的模式:
默认为标准盒模型(box-sizeing:content-box),IE盒模型(box-sizeing:border-box)
2.css选择器的优先级
css的特性:继承性、层叠性、优先级
css选择器分类:标签/类/伪类/属性、全局选择器、行内样式、id选择器、!important
优先级:!important>行内样式>id>标签/类/伪类/属性>标签>全局选择器
伪类:伪类是一种选择处于特定状态的选择器;分为结构伪类、动态伪类、结构伪类、其他伪类
结构伪类包括::first-child、:last-child、:nth-child...
动态伪类包括::link、:hover、:active...
表单伪类包括::checked、:disadble、:scope...
其他伪类包括::root、:lang...
伪元素:伪元素实际上就是虚拟的元素,伪元素是一个用于创建一些不在DOM树中的元素,并为其添加样式的选择器。常见的伪元素有 ::before、::after...
伪类与伪元素的异同:
相同点:
1.伪类与伪元素都用于向选择器加特殊效果的。
2.伪类和伪元素都用于表示文档树以外的"元素"。
不同点:
1.可以同时使用多个伪类,而只能同时使用一个伪元素。
2.伪元素创建了DOM树之外的元素,而伪类没有。
3.伪类能选择具体的文档树元素。但伪元素选择的不是具体的文档树元素,更多的是一个独立于文档树的“抽象元素”。
3.px和rem的区别
px是像素,显示器上给我们呈现的画面像素.每一个像素大小是一样的,是绝对长度.
rem是相对单位,相当于html根节点的font-size的值.计算方式为16px * 根节点font-size的百分比的值.
4.重绘重排的区别
重排(回流):当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建,称之为重排.
重绘:当盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来之后,浏览器便把这些原色都按照各自的特性绘制一遍,将内容呈现在页面上。
关系:重排一定会导致重绘,重绘不一定导致重排。如果DOM变化不影响几何属性,元素的布局没有改变,则只发生一次重绘(不需要重排)。
常见引起重绘的操作:
- color
- border-style
- border-radius
- text-decoration
- box-shadow
- outline
- background
- ...
常见引起重绘的操作:
- 页面初始渲染,这是开销最大的一次重排;
- 添加/删除可见的DOM元素;
- 改变元素位置;
- 改变元素尺寸,比如边距、填充、边框、宽度和高度等;
- 改变元素内容,比如文字数量,图片大小等;
- 改变元素字体大小;
- 改变浏览器窗口尺寸,比如resize事件发生时;
- 激活CSS伪类(例如::hover);
- 设置 style 属性的值,因为通过设置style属性改变结点样式的话,每一次设置都会触发一次reflow;
- 查询某些属性或调用某些计算方法:offsetWidth、offsetHeight等,除此之外,当我们调用getComputedStyl方法,或者IE里的 currentStyle 时,也会触发重排,原理是一样的,都为求一个“即时性”和“准确性”;
- ...
5.常见的水平垂直居中的方式:
1.父元素给相对定位,子元素绝对定位,上下左右为0,然后margin:0;
2.父元素给相对定位,子元素绝对定位,上左方向给50%,再transform:ltaranslate(-50%);
3.flex布局:display:flex;justify-content:center;algin-items:centent;
4.grid布局:display:grid;justify-self:centent;algin-self:centent;
JS
6.闭包
概念:函数嵌套函数,内部函数使用了外部函数的变量,并被外部函数所返回.就会产生闭包的情况.
特点:
1.可以延长外部变量的生命周期;
2.可以让外部函数操作内部函数;
3.可以重复使用变量,并且这个变量不会污染全局的一种机制,这个变量是一直保持在内存中,不会被垃圾回收机制回收;
缺点:当闭包比较多的时候,会消耗内存,导致页面性能下降,内存溢出.
解决方案:使用完之后,将该变量删除;或者赋值为null;
使用场景:防抖、节流、对外公开的接口,但不希望公开内部的私有变量,希望数据保持安全性的场景;
关于内存泄漏怎么理解?
js里已经分配内存地址的对象,由于长时间没有释放或者存在某些问题导致垃圾回收机制没办法正常清除、正常回收,造成长期占用内存的现象,会让内存资源大幅度浪费,最终导致页面性能的下降甚至崩溃.
没有被垃圾回收机制回收的因素:
1.一些未声明直接赋值的变量;
2.一些未清空的定时器;
3.闭包引用的变量没有被清除;
7.事件委托(事件代理):
又叫事件代理,原理就是利用了时间冒泡的机制来实现,也就是说把子元素的事件绑定到父元素的身上.如果子元素阻止了事件冒泡,那么委托就无法实现。
Dom标准事件流的触发的先后顺序为:先捕获再冒泡。即当触发dom事件时,会先进行事件捕获,捕获到事件源之后通过事件传播进行事件冒泡。
阻止事件冒泡:event.stopPropagation() 取消冒泡
事件冒泡绑定:addEventListener('click',函数名,true/false) 默认是false(事件冒泡),true(事件捕获)
优点:1.提高性能,减少事件绑定,也减少内存占用;2.简化了dom节点更新时,相应事件的更新。
缺点:1.事件委托基于冒泡,对于不冒泡的事件不支持;2.层级过多,冒泡过程中,可能会被某层阻止掉.
8.js基本数据类型与引用数据类型的区别?
基本数据类型:string、number、boolean、undefined、null、Symbol
引用数据类型:object、function、array、date、正则表达式
特点:
1.基本类型的值保存在栈内存,引用类型的值保存在堆内存;
2.基本类型保存的值是一个具体的值,而引用类型保存的值是保存引用数据类型的一个地址;
3.基本类型不能添加属性和方法,而引用类型可以;
9.原型与原型链
原型是JavaScript中对象的一个属性,也是对象创建时的一个模板,可用于添加公共属性和方法。每个JavaScript对象都有一个原型,可以继承原型上的属性和方法,从而实现对象之间的属性和方法共享。
原型链是JavaScript中一种对象之间的继承关系。当访问对象的属性或方法时,如果该对象本身不存在该属性或方法,JavaScript会沿着原型链向上查找,直到找到匹配的属性或方法。原型链的终点是null,即所有对象的原型链最终都会指向null。
原型链的形成基于每个对象都有一个隐式的原型指针([[Prototype]]),它指向该对象的原型。可以通过对象的__proto__属性来访问和修改原型指针。当访问对象的属性或方法时,JavaScript会沿着原型链依次查找每个对象的原型,并逐级向上检查,直到找到匹配的属性或方法,或到达原型链的终点null。
通过原型链,JavaScript中的对象可以实现属性和方法的继承,避免了重复定义和复制代码的问题,提高了代码的重用性和可维护性。

10.new操作符具体做了什么
1.创建一个空对象,作为新对象实例。
2.将新对象的原型(`[[Prototype]]`)指向构造函数的 `.prototype` 属性。
3.把构造函数的this绑定到新的空对象身上
4.如果构造函数返回了一个对象,则返回该对象;否则,返回新对象实例。
实现一个new:
function newFun(fun,...args){
let newObj = {} //创建一个空对象
newObj.__proto = fun.prototype //将对象原型指向构造函数的prototype属性
const res = fun.apply(newObj,args) //把构造函数的this绑定到新的空对象身上
return res instanceof object ? res : newObj // 返回对象或者对象实例
}
11.JS是如何实现继承的?
1.原型链继承
2.借用构造函数继承 (apply,call,bind)
3.组合式继承(原型链+构造函数组合)
4.es6的class类继承(extends)
12.JS中关于this指向问题
1.全局对象的this指向 指向的是window;
2.全局作用域或者普通函数(没有被调用的时候)中的this 指向的是全局window;
3.this永远指向最后调用它的那个对象(非箭头函数的前提下);
4.new关键字改变this的指向;
5.call,apply,bind 方法 可以改变this指向(非箭头函数);
6.箭头函数中的this,在定义时就已经确定了,箭头函数没有this,需要看外层是否有函数;如果有,就是外层函数的this,如果没有,则为window;
7.匿名函数中的this永远指向window;匿名函数的执行环境具有全局性,因此this指向window
13.call,apply,bind的区别
1.共同点:都是改变this指向和函数的调用,call和apply的功能类似,只有传参不一致
2.不同点:
(1)call方法传的是一个参数列表;
(2)apply方法传的是一个数组;
(3)bind方法传参后不会立即执行,会返回一个改变了this指向的函数,这个函数还是可以传参的,bind()();
//call方法实现:
Function.prototype.myCall = function (context, ...args) {
context = context || window; // 如果未提供上下文参数,默认为全局对象
const uniqueKey = Symbol(); // 创建一个唯一的键,用于确保上下文对象不会被污染
context[uniqueKey] = this; // 将当前函数作为上下文对象的属性
const result = context[uniqueKey](...args); // 在上下文对象上调用函数
delete context[uniqueKey]; // 删除添加的函数属性
return result; // 返回函数执行的结果
};
//apply方法实现:
Function.prototype.myApply = function (context, argsArray) {
context = context || window; // 如果未提供上下文参数,默认为全局对象
const uniqueKey = Symbol(); // 创建一个唯一的键,用于确保上下文对象不会被污染
context[uniqueKey] = this; // 将当前函数作为上下文对象的属性
let result;
if (argsArray) {
result = context[uniqueKey](...argsArray); // 在上下文对象上调用函数,并传入参数数组
} else {
result = context[uniqueKey](); // 在上下文对象上调用函数(没有参数)
}
delete context[uniqueKey]; // 删除添加的函数属性
return result; // 返回函数执行的结果
};
//bind实现方法
Function.prototype.myBind = function (context, ...args) {
const fn = this; // 保存原始函数的引用
return function (...innerArgs) {
return fn.apply(context, [...args, ...innerArgs]); // 在指定上下文中调用原始函数,并传递所有参数
};
};
14.定时器setTimeOut与setInterval
区别:它们的主要区别在于触发时机和执行次数;
setTimeout:
1.它用于在指定的延迟之后执行一次给定的回调函数。可以将它视为一次性的定时器;
2.setTimeout只会执行一次回调函数;
3.最小执行时间为4ms;(会根据浏览器或规则有细微差异,遵从H5规定的话就是4ms)
4.取消使用clearTimeout;
setInterval:
1.它用于以固定的时间间隔重复执行给定的回调函数。可以将它视为周期性的定时器;
2.setInterval会反复执行回调函数,直到被取消;
3.最小执行时间为10ms;(会根据浏览器或规则有细微差异,遵从H5规定的话就是10ms)
4.取消使用clearInterval;
15.ES6新特性
1.新增块级作用域,使用let和const的2种新的声明方式在块级作用域中声明变量和常量;
var存在变量提升,变量可以多次使用,后者覆盖前者,存在变量污染;
let和const不存在变量提升,不能在同一作用域重复声明;
let和const的区别:在于变量的可变性,使用let声明的变量是可变的,也就是说可以重新赋值给它们新的值。而使用const声明的变量是不可变的,一旦被赋值后就不能再重新赋值新的值。但需要注意的是,const定义的是常量,这只是保证变量指向的地址不变,并不表示它指向的值是不可变的,对于引用类型(如对象或数组)来说,仍然可以修改其属性或元素。
2.新增了一种基本数据类型Symbol;
Symbol表示一个唯一的标识符,用于创建对象属性的唯一键。符号是不可变的且唯一的。
3.箭头函数;
(1) 与普通函数的区别:
1.不能作为构造函数使用,不能使用new关键字;
2.没有arguments;
3.不能使用call,apply,bind来改变this指向问题;
4.箭头函数属于匿名函数的一种形式,this指向外层第一层函数this指向;
5.箭头函数属于匿名函数的一种形式,所以他没有原型;
(2) 箭头函数的适用场景:
1.作为回调函数,不需要访问自己的this或arguments。
2.简化单行的函数表达式。
3.使用上下文外层的this访问。
4.模板字符串;
5.解构赋值;
6.数组和对象新增了拓展运算符;
7.函数可以新增默认参数值;
8.新增了定义类(class)的语法糖;
9.为数组新增了api(foreach,map...);
10.引入模块化(import,export);
11.迭代器与生成器(Iterators 和 Generators);
12.promise异步的操作;
1.解决回调地狱的问题;
2.三种状态:等待,成功,失败;
3.链式调用:通过then()方法,将多个 Promise 连接起来,形成一条调用链,每个 `then()` 都可以返回一个新的 Promise 对象,从而实现连续的异步操作。
与async/await有什么区别:
1.语法(可读性)上的区别:相比于 Promise 的链式调用方式,async/await 更加直观和易于理解。
2.错误处理的区别:在 Promise 中,错误的处理可以通过catch()方法来捕获错误。而在 async/await 中,可以使用try...catch块来捕获异步操作中的错误,并进行错误处理。
3.错误堆栈信息的区别:Promise 的错误堆栈信息相对较强,错误会通过.then()和.catch()方法的链式调用来逐步传递。而 async/await 在错误发生时会生成完整的错误堆栈信息,能够更准确地追踪错误的发生位置。
4.兼容性的区别:Promise是ES6引入的,而async/await则是ES7引入的,对于一些较老的浏览器而已,async/await的兼容性会稍微差一些.
...
16.深拷贝与浅拷贝
深拷贝:深拷贝是创建一个新的对象或数据结构,会在堆内存中开辟一个新的空间,深拷贝创建了一个与原始对象完全独立的副本,修改新对象的值不会影响原始对象。主要针对引用数据类型
深拷贝通常通过以下方式进行:
1.扩展运算符;(缺点:只能实现第一层深拷贝,当层级过深时,还是浅拷贝)
2.json.parse(json.stringify);
3.递归函数实现
//深拷贝实现
function deepCopy(obj) {
if (typeof obj !== "object" || obj === null) {
return obj; // 对于非对象和 null,直接返回原始值
}
let copy;
if (Array.isArray(obj)) {
copy = [];
for (let i = 0; i < obj.length; i++) {
copy[i] = deepCopy(obj[i]); // 递归进行深拷贝
}
} else {
copy = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) { //hasOwnProperty 用于检查一个对象自身是否包含指定的属性。
copy[key] = deepCopy(obj[key]); // 递归进行深拷贝
}
}
}
return copy;
}
浅拷贝:浅拷贝是创建一个新的对象或数据结构,并将原始对象的引用复制到新对象中。因此,新对象和原始对象仍然共享相同的内存地址,对新对象的修改可能会影响原始对象.
浅拷贝通常通过以下方式进行:
1.扩展运算符;
2.Array.from() 方法;
3.Object.assign() 方法;
//浅拷贝实现
function shallowCopy(obj) {
if (Array.isArray(obj)) {
return [...obj];
} else if (typeof obj === 'object' && obj !== null) {
return { ...obj };
} else {
return obj;
}
}
17.防抖与节流
防抖与节流都是用于控制函数执行频率的技术,但它们的原理和应用场景有所不同。
1.防抖:防抖的原理是在函数被连续触发时,只执行最后一次触发的函数调用.
应用场景:
1.输入框的实时搜索
2.输入框的表单验证
3.页面调整与重新布局(如用户调整窗口的大小自适应布局)
4.如程序安装进度条
...
//防抖实现
function debounce(func, delay) {
let timeoutId;
return function(...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
2.节流:即在这个时间间隔内,无论触发多少次函数,都只执行第一次。
应用场景:
1.按钮的点击事件;
2.页面滚动条;
...
//节流实现
function throttle(func, delay) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) {
func.apply(this, args);
lastTime = now;
}
};
}
区别总结:防抖执行最后一次,而节流执行第一次
18.事件循环(eventLoop)
js会先执行同步任务,再执行异步任务.异步任务又分为宏任务与微任务;会先执行微任务,再执行宏任务;
宏任务队列:主要包含异步操作,例如 I/O 操作、定时器回调函数、事件回调函数等。
微任务队列:主要包含微任务,例如 Promise 的回调函数、MutationObserver 回调函数和 process.nextTick等。
为什么要先执行微任务呢?主要有以下几个原因:
1. 反应速度和用户体验:微任务通常是一些优先级较高、需要快速响应的操作,例如 Promise 的回调函数。通过先执行微任务,可以更快地响应用户的操作或提供更好的用户体验。
2. 状态更新和副作用:微任务常用于处理状态更新和副作用操作,例如更新 UI、处理数据等。通过先执行微任务,可以确保这些状态和副作用在主线程执行完之前就得到更新,避免在下一个宏任务执行时才生效的问题。
3. 阻塞问题:如果先执行宏任务,而宏任务的执行过程中又产生了新的宏任务,那么这些新的宏任务会阻塞微任务的执行,从而延迟微任务的响应时间。而先执行微任务,可以避免这种情况,确保微任务的快速执行。
19.get和post的区别
1.get一般用于获取数据,post一般用于提交数据;
2.get参数会放在url上,而post请求放在body中;
3.get请求刷新服务器或退出是没有影响的,post请求退回时会重新提交数据;
4.get请求时会被缓存,post请求不会被缓存;
5.get请求会被保存在浏览器历史记录中,post请求不会;
6.get请求只能进行url编码,post请求支持很多种;
7.get是一种幂等的请求方法,多次执行不会对服务器资源产生影响,post是一种非幂等的请求方法,多次执行会对服务器资源产生影响,例如创建或修改资源。
8.get请求会有长度限制,一般不超过2048个字节,post请求没有长度限制,可以传输大量数据。
...
20.浏览器常见的储存方式与区别
1.cookie
(1)一般用于存储少量的文本数据,每个 Cookie 的大小有限制(通常为 4KB)。
(2)会随着每个请求发送给服务器,因此在性能和网络流量方面会带来一定的开销。
(3)作用域限制在特定的域名下,可以设置有效期和路径。
2.localStorage
(1)数据在浏览器关闭后仍然会保留
(2)存储大小为5M
(3)相同浏览器的同源页面可以共享存储的数据。不同浏览器的同源页面无法共享数据。
3.sessionStorage
(1)数据在页面会话结束时被清除,即当用户关闭标签页或浏览器时会话结束。
(2)存储大小为5M
(3)仅在同一个窗口或标签页中共享存储的数据。不同窗口或标签页之间无法共享数据。
4.indexeDB
21.浏览器缓存机制
浏览器缓存是一种将常用的资源存储在本地磁盘中,以便在后续请求中从本地加载资源而不是重新从服务器下载的机制。它可以提高网页的加载速度和用户体验,并减轻服务器的负载。
缓存的位置:
1.浏览器内存缓存
速度快,但容量小
2.磁盘缓存
速度慢,但容量大
缓存的策略:
1.强缓存
(1)强缓存是指在缓存有效期内,直接从缓存中获取资源,无需发送请求到服务器验证。
(2)客户端通过检查响应的缓存标识(如 Expires 和 Cache-Control)来决定是否使用缓存。
(3)如果缓存有效,浏览器直接从缓存中获取资源,不会向服务器发送请求,从而提高性能。
2.协商缓存
(1)协商缓存是指在缓存过期后,客户端发送条件请求到服务器进行验证,判断缓存是否仍然有效。
(2)客户端发送请求时,会附带缓存标识(如 If-Modified-Since 或 If-None-Match),告诉服务器上次请求资源时的标识信息。
(3)服务器会比较请求中的标识和资源的当前标识来判断是否有更新,如果没有更新,服务器返回304 Not Modified响应,客户端继续使用缓存,否则返回新的资源和状态码200 OK。
1. 缓存控制:
Expires:设置资源的过期时间,是一个具体的时间点,存在时间戳兼容问题。
Cache-Control:使用指令设置缓存策略,例如:max-age(从请求时间开始计算的缓存存储时间)、no-cache(需要协商缓存)、no-store(禁用缓存)等。
Last-Modified/If-Modified-Since:通过资源的最后修改时间进行协商缓存。
Etag/If-None-Match:通过资源的唯一标识符进行协商缓存。
总结:
强缓存利用缓存有效期或max-age来判断缓存是否有效,避免发送请求到服务器。适用于那些不经常改变的静态资源。
协商缓存在缓存过期后,通过与服务器进行通信来验证缓存是否仍然有效,减少网络传输量和服务器负载。适用于经常更新的资源。
强缓存和协商缓存可以一起使用,先通过强缓存判断是否使用缓存,如果缓存失效,再进行协商缓存验证。这样可以在一定程度上提升缓存的灵活性和客户端性能。
22.跨域
指的是在浏览器环境中,试图访问不同源的资源时,违背了同源策略的规则,就会产生跨域的问题;
同源策略指:域名、端口号、协议 三者一致;
要解决跨域问题,需要借助一些特定的跨域解决方案,如:
1. JSONP:通过动态创建<script>标签来加载并执行跨域的 JavaScript 脚本。
2. CORS:服务端在响应中添加特定的响应头部,允许跨域请求的发生。
3. 代理:通过在同域名下设置代理服务器,使得浏览器向代理发送请求,再由代理服务器转发请求到目标域名,然后将响应返回给浏览器。
4. WebSocket:WebSocket 是一种建立在单个 TCP 连接上的全双工通信协议,可以直接与服务器进行跨域通信。
Vue
23.Vue生命周期
Vue.js 组件的生命周期及每个阶段所做的事情:
beforeCreate: 在实例化前调用,此时组件的数据、事件等都还没有被初始化。
created: 实例化完成后调用,此时组件的数据、事件已经初始化,可以进行数据的获取、事件的监听等操作。
beforeMount: 在挂载之前调用,此时组件的模板已经编译完成,但是尚未挂载到 DOM 中。
mounted: 在挂载之后调用,此时组件已经挂载到 DOM 中,可以进行 DOM 操作、调用第三方库等。
beforeUpdate: 在组件更新之前调用,当组件的响应式数据发生改变时会触发该钩子函数,此时虚拟 DOM 已经重新渲染,但尚未应用到实际 DOM 中。
updated: 在组件更新之后调用,此时虚拟 DOM 已经重新渲染,并且已经应用到实际 DOM 中,可以进行 DOM 操作、调用第三方库等。需要注意的是,避免在该钩子函数中修改数据,以避免进入无限更新的循环。
beforeDestroy: 在组件销毁之前调用,可以用来进行一些清理操作,如取消定时器、清除绑定的事件监听器等。
destroyed: 在组件销毁之后调用,此时组件已经被完全销毁,可以进行最后的清理工作。
24.v-if和v-show的区别
都可以控制元素的显示隐藏
1.v-show是控制元素的display值来控制元素的显示隐藏的;v-if是把整个DOM进行添加或者删除的;
2.v-if每次切换时需要重渲染,包括销毁和创建组件的过程,会触发生命周期;而v-show只是简单的css切换
3.v-if的性能开销比v-show的大,如果比较频繁的显示隐藏需求建议v-show,如果比较少的话使用v-if;
25.v-for中的key作用是什么
为每个循环项提供一个唯一的标识符,从而在Vue更新 DOM 时增加效率;
Vue的虚拟DOM使用key来对比新旧节点的标识,以最小化对 DOM 的操作。没有提供key的情况下,Vue会使用默认的索引作为key,但这可能导致错误的更新,或者性能下降。
关于虚拟DOM的知识点可以移步到:https://juejin.cn/post/6931244699357282311
26.在created和mounted请求数据有什么区别
created钩子函数:created钩子函数是在组件实例被创建后立即调用的,此时组件的实例已经被初始化,但实际的 DOM 尚未挂载到页面中。因此,在created钩子函数中发送请求获取数据时,可能无法直接操作组件的 DOM 元素。
mounted钩子函数:mounted钩子函数是在组件实例被挂载到实际的 DOM 元素后调用的,此时组件已经显示在页面上,可以操作组件的 DOM 元素和进行其他 DOM 相关的操作。
因此,在mounted钩子函数中发送请求获取数据,可以直接操作组件的 DOM 元素,并将接收到的数据渲染到页面上。
27.Vue组件通信方式
父传子 props、$ref;
子传父 自定义事件;
兄弟组件之间传值:事件总线($emit,$on)、event bus
vuex;
祖先与后代组件通信:Provide/Inject
28.vuex刷新数据会丢失吗?怎么解决?
vuex肯定会重新获取数据,页面也会丢失数据
1.把数据直接保存在浏览器缓存里(如cookie、localstorage、sessionstorage)
2.页面刷新的时候,再次请求数据,达到可以动态更新的方法
监听浏览器的刷新数据,在刷新前把数据保存到sessionstorage里,刷新后请求数据,请求到了用vuex,如果没有就用sessionstorage;
29.computed和watch的区别
1.计算属性是基于依赖的响应式属性,而监听器是基于回调的监听变化。
2.计算属性是支持缓存,依赖的属性值发生变化才会重新计算,而watch不支持缓存,是实时监听;
3.计算属性不支持异步,watch是可以支持异步的;
从源码的角度来分析计算属性和监听器的区别,我们可以关注它们在 Vue 的响应式系统中的实现和执行机制:
计算属性的实现:计算属性的实现依赖于 Vue 的响应式系统,在组件实例初始化时,会通过 `Object.defineProperty` 将计算属性定义为响应式属性,并设置 Getter 函数。在计算属性的 Getter 函数中,Vue 会根据依赖的响应式属性建立关联,并缓存计算结果以便复用,只有在依赖属性变化时才会重新计算。这样,当访问计算属性时,会直接从缓存中获取结果,而不会重新计算。
监听器的实现:监听器通过回调函数来响应数据变化。在组件实例初始化时,如果定义了监听器属性(即 `watch` 选项),Vue 会遍历这些属性,并将每个监听器属性转换为 `watcher` 实例。在 `watcher` 实例中,会订阅所监听的对象的变化,并在数据变化时触发回调函数。回调函数可以执行一些逻辑操作,例如触发其他方法、更新组件状态等。
React
1.类组件与函数组件的区别 相同点:函数组件与类组件使用方式和最终呈现效果是一致的 不同点:
本质上代表2种不同设计思想与心智模式
类组件的根基是OOP,面向对象编程
函数组件的根基是FP,也就是函数式编程,相较于类组件,函数组件更纯粹、简单、易测试
性能优化:
类组件主要依靠shoildComponentUpdate函数去阻断渲染
函数组件靠React.memo来优化
react官方认为类组件在未来并不能成为趋势的原因
1.this的模糊性
2.业务逻辑散落在生命周期
3.react组件代码缺乏标志拆分方式
类组件:
类组件必须继承react.component
类组件必须实现render方法
constructor是可选的,我们通常在constructor中初始化数据,绑定this
this.state是状态,维护的是组件中内部的数据
render方法必须返回一个jsx元素
函数组件
在React 16.8之前的版本中,函数组件是无状态的,即它们不能维护自己的状态或生命周期。但从React 16.8开始,通过引入Hooks(如`useState`、`useEffect`等),函数组件也能拥有状态和生命周期功能。
不支持继承
总结
类组件需要声明constructor,函数组件不需要
类组件需要手动绑定this,函数组件不需要
类组件有生命周期钩子,函数组件没有
类组件可以定义并维护自己的state,属于有状态组件,函数组件是无状态组件
类组件需要继承class,函数组件不需要
类组件使用的是面向对象的方法,封装:组件属性和方法都封装在组件内部 继承:通过extends React.Component继承;函数组件使用的是函数式编程思想
setState是同步更新还是异步更新
在合成事件中是异步的,在生命周期钩子中是异步的
React17 在原生事件、在定时器、是同步的
合成事件:就是React内部自己实现了一套事件处理机制,不是原生事件(如:onClick、onChange、onSubmit等)
同步更新:每更新start就执行一次render
异步更新:全部更新完毕以后执行一次render
受控组件和非受控组件
受控组件和非受控组件指的都是表单元素.如<input /> <textarea /> <select />
受控组件,表单元素受React组件管理控制,表单的状态(数据)修改只能通过setState()来更新
特点是: 数据可控,完全由react中的state来管理
非受控组件:
使用Ref从DOM节点获取表单数据,表单数据交由DOM节点来处理
特点是ref获取DOM节点进行数据更新,这样就不可控了,不能统一管理了.
Webpack
Webpack的构建(打包)流程
首先读取webpack.config.js配置文件,从配置文件和shell语句中读取与合并参数,并用得到的参数去初始化compiler对象,加载所有配置的plugin插件,并执行对象的run方法进行编译.
然后根据配置的entry入口找到所有的入口文件,调用所有配置的loader模块去递归解析模块依赖.根据入口和模块之间的依赖关系,组装成一个包含多个模块的 Chunk。
最后再把每个 Chunk 转换成一个单独的文件加入到输出列表,根据output确定输出的路径和文件名,输出文件夹。
webpack中Loader 和 Plugin有什么区别
Loader(加载器)本质是函数;
转换文件:Loader 的主要任务是处理和转换模块文件。它们在 Webpack 编译过程中用于对模块的源代码进行预处理和转换,例如将 JSX 转换为 JavaScript、将 SCSS 转换为 CSS 等。
链式处理:Loader 可以串联使用,对同一个文件进行一系列的处理。Loader 的执行顺序是从右到左,即最后一个加载器先执行,然后向左传递结果。
文件依赖:Loader 只在处理文件时运行,它们不关心 Webpack 构建过程中的其他方面。
配置简单:Loader 的配置通常比较简单,只需要在 `webpack.config.js` 中的 `module.rules` 部分添加规则即可。
Plugin(插件) 本质是对象;
扩展(加强)功能:Plugin 用于在 Webpack 构建过程中扩展其功能。它们可以操作和监听 Webpack 的事件系统,执行更广泛的任务,如优化、资源管理、环境变量注入等。
钩子系统:Plugin 通过钩子(hooks)与 Webpack 的事件系统交互。Webpack 在构建过程中会广播许多事件,Plugin 可以监听这些事件,并在特定的时机执行代码。
影响构建过程:Plugin 可以影响 Webpack 的整个构建过程,包括启动和结束、以及中间的每个阶段。
配置复杂:Plugin 的配置通常比 Loader 更复杂,需要在 `webpack.config.js` 的 `plugins` 数组中添加实例。
持久化操作:Plugin 可以执行一些 Loader 无法完成的操作,如生成额外的文件、修改文件、持久化缓存等。
总结
Loader 主要用于转换文件,是处理单个模块的局部操作。
Plugin 用于扩展 Webpack 的功能,可以在整个构建过程中执行更复杂的任务。
常见的Loader
babel-loader:将ES6+ 语法转换为兼容性更好的ES5 语法,通常与 @babel/preset-env 一起使用。
css-loader:解析CSS 文件中的 @import 和 url(),并将其转换为 JavaScript 可以处理的模块。
style-loader:将CSS 插入到 DOM 中的<style>标签中。
file-loader:处理文件(如图片、字体),并返回文件的 URL。
url-loader:类似于 file-loader ,但当文件小于某个限制时,可以将文件内容转化为 Data URL。
sass-loader:将SCSS/SASS 文件编译为 CSS。
常见的Plugin
HtmlwebpackPlugin:自动生成 HTML 文件,并自动注入打包后的资源(如 JS、CSS 文件)。
CleanWebpackPlugin:在每次打包前清理输出目录,防止旧文件残留。
MiniCssExtractPlugin:将CSS 提取到单独的文件中,而不是内嵌在 JavaScript 中。
DefinePlugin:创建全局常量,在编译时进行替换。
TerserPlugin:用于压缩 JavaScript 代码,主要在生产环境中使用。
Module/Chunk/Bundle 是什么
Module:webpack 里一个概念性内容,每个文件都可以看为一个 module。 js、css、图片等都可以看作 module。
Chunk:代码块,webpack 处理代码时候的一个中间态,它表示有一组功能相关的模块的集合。一个 Chunk 可以由多个模块(module)组成
Bundle:是 Webpack 构建结果的输出,由一个或多个 Chunk 的合并优化后的结果,最终以文件形式输出,用于在浏览器中加载和执行。