赞
踩
==:等同,比较运算符,两边值类型不同的时候,先进行类型转换,再比较;===:恒等,严格比较运算符,不做类型转换,类型不同就是不等;
isNaN()或Object.is()来判断)。Object.is()是ES6新增的用来比较两个值是否严格相等的方法,内部采用的比较算法就是SameValue(x, y),与===的行为基本一致。
举个栗子☺:
- +0 === -0 //true
- NaN === NaN // false
-
- Object.is(+0, -0) // false
- Object.is(NaN, NaN) // true
4. SameValue 与 SameValueZero 其实是 ecma 中,比较值是否相等的算法。
举个例子,我们都知道 NaN 的定义是与任何其他值均不相等。
因此,就会有以下结果:
NaN == NaN // false NaN === NaN // false
有的时候,我们还是希望可以判断 NaN ,因此就诞生了 SameValue:
SameValue(NaN, NaN); // true
对于 +0 与 -0 则会认为是不同的值:
+0 == -0 // true +0 === -0 // true SameValue(+0, -0); // false
以上两种情况,就是 SameValue 与 === 不同的地方。(判断 NaN,+0,-0 时)
使用 SameValueZero 的话,对于 +0 与 -0 则会认为是同一个值,includes内部使用的比较算法就是SameValueZero:
+0 == -0 // true +0 === -0 // true SameValue(+0, -0); // false SameValueZero(+0, -0); // true
所以, SameValueZero 与 === 只有一个不是的情况。
对象的本质:ECMA-262把对象定义为:无序属性的集合,其属性可以包含基本值、对象或者函数。
属性:数据属性、访问器属性
属性特性(属性描述符):
数据属性:
访问器属性:
注意:1.相对于数据属性,我们发现访问器属性中没有writable特性和value特性。这是因为访问器属性不包含数据值,那么我们怎么当然就不可修改属性的值(用不到writable特性),更不用考虑value了。
2.访问器属性不能直接定义,必须是用Object.defineProperty()来定义。(通过这个规定我们就能准确地判断出访问器属性和数据属性了)
修改属性特性(属性描述符):
Object.defineProperty()
Object.defineProperties()方法定义多个特性
Object.getOwnPropertyDescripter()方法读取属性的描述符
另外:
hasOwnProperty() //判断属性是否是存在于自己的实例中,如果是:返回true,如果仅仅存在自己的原型总,则返回false
Object.getOwnPropertyName() //如果你想要获得所有实例属性,无论他是否可枚举,可以使用这个方法
Object.keys() //如果你想要获得所有实例属性,并仅需可枚举的,可以使用这个方法
for in //主要用于遍历对象的可枚举属性,包括自有属性、继承自原型的属性
class类中,类的内部所有定义的方法,都是不可枚举的(non-enumerable)。
class Point {
constructor(x, y) {
// ...
}
toString() {
// ...
}
}
Object.keys(Point.prototype)
// []
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
ES5中通过添加到原型链上的属性是可以枚举的
var Point = function (x, y) {
// ...
};
Point.prototype.toString = function() {
// ...
};
Object.keys(Point.prototype)
// ["toString"]
Object.getOwnPropertyNames(Point.prototype)
// ["constructor","toString"]
Object.getPrototype(obj)是ES5中用来获取obj对象的原型对象的标准方法。
为什么ES5无法继承原生构造函数? - 简书 (jianshu.com)
1. 严格模式
类和模块的内部,默认就是严格模式,所以不需要使用use strict指定运行模式。只要你的代码写在类或模块之中,就只有严格模式可用。考虑到未来所有的代码,其实都是运行在模块之中,所以 ES6 实际上把整个语言升级到了严格模式。
2. 不存在提升
类不存在变量提升(hoist),这一点与 ES5 完全不同。
3. 方法默认是不可枚举的
ES6 中的 class,它的方法(包括静态方法和实例方法)默认是不可枚举的,而构造函数默认是可枚举的。细想一下,这其实是个优化,让你在遍历时候,不需要再判断 hasOwnProperty 了
4. class 的所有方法(包括静态方法和实例方法)都没有原型对象 prototype,所以也没有[[construct]],不能使用 new 来调用。
5. class 必须使用 new 调用,否则会报错。这是它跟普通构造函数的一个主要区别,后者不用 new 也可以执行。
6. ES5 和 ES6 子类 this 生成顺序不同,es5的写法不能继承原生构造函数(比如Array、Number等)
原生构造函数会忽略apply方法传入的this,也就是说,原生构造函数的this无法绑定,导致拿不到内部属性。ES5 是先新建子类的实例对象this,再将父类的属性添加到子类上,由于父类的内部属性无法获取,导致无法继承原生的构造函数。
ES6 是先新建父类的实例对象this,然后再用子类的构造函数修饰this,使得父类的所有行为都可以继承。因此class可以继承原生构造函数并自定义原生构造函数的子类。
例外
ES6 改变了Object构造函数的行为,一旦发现Object方法不是通过new Object()这种形式调用,ES6 规定Object构造函数会忽略参数。
class Obj extends Object{
constructor(){
super(...arguments)
}
}
var o = new Obj({attr:true});
console.log(o.attr); //输出 undefined
** 原生构造函数大致有下面这些:
7. ES6可以继承静态方法,而构造函数不能
JavaScript中创建一个对象可以采用3种方式:new Object(),Object.create(),或字面量写法。
new Object()
new Object()这种方式即我们常说的“使用构造函数创建对象”,new运算符实际做了以下4件事情:
1.创建一个新的对象
2.链接到原型(将构造函数的 prototype 赋值给新对象的 __proto__);
3.将构造函数的作用域赋给新对象(因此this指向了这个新对象)
4.执行构造函数中的代码(为这个新对象添加属性)
4.返回新对象。如果是引用类型就返回引用类型的对象。(如果没有返回对象类型object包括Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象的引用)
new Object() 与Object.create()的区别:
1)创建对象的方式不同
new Object() 通过构造函数来创建对象, 添加的属性是在自身实例下。
Object.create() es6创建对象的另一种方式,可以理解为继承一个对象, 添加的属性是在原型下
2)创建对象属性的性质不同
Object.create() 用第二个参数来创建非空对象的属性描述符默认是为false的,而构造函数或字面量方法创建的对象属性的描述符默认为true。
3)创建空对象时不同
当用构造函数或对象字面量方法创建空对象时,对象时有原型属性的,即有_proto_;
new关键字创建的对象是Object的实例,原型指向Object.prototype,继承内置对象Object当用Object.create()方法创建空对象时,对象是没有原型属性的。
Object.create(arg, pro)创建的对象的原型取决于arg,arg为null,新对象是空对象,没有原型,不继承任何对象;arg为指定对象,新对象的原型指向指定对象,继承指定对象promise:1、对象的状态不受外界影响。2、一旦状态改变,就会凝固,不能再次改变
Promise是一个立即执行函数,但是他的成功(或失败:reject)的回调函数resolve却是一个异步执行的回调。当执行到resolve()时,这个任务会被放入到回调队列中,等待调用栈有空闲时事件循环再来取走它。
async 定义的函数,返回的其实是一个Promise对象
await 操作符后面的表达式是一个Promise的时候,它的返回值,实际上就是Promise的回调函数resolve的参数。
Promise.resolve()将现有对象快速转换成promise对象
- let p = new Promise((resolve,reject) => {
- setTimeout(() => {
- resolve('success');
- },500);
- });
-
- let pp = Promise.resolve(p);
-
- pp.then(result => {
- console.log(result);
- });
-
- console.log(pp == p);
Promise.reject() 快速获取一个拒绝状态的promise对象
Promise.all()同时执行多个promise >> 当所有状态都是成功状态才返回数组,只要其中有一个的对象是reject的,就返回reject的状态值。
Promise.race()执行多个promise >> 哪个对象返回的快就返回哪个对象,如果对象其中有reject状态的,且返回的够快,就能被catch捕捉到。race最终返回的只有一个值
**Pormise内部的错误外界用try-catch捕捉不到
联系:
区别:
1、简洁
Promise虽然一方面解决了callback的回调地狱,但是相对的把回调“纵向发展”了,形成了一个回调链;
Async/Await写法更简洁,更直观。不需要写.then,不需要写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码
2、错误处理:
Async/Await 让 try/catch 可以同时处理同步和异步错误
3、调试:
async/await能够使得代码调试更简单,可以像调试同步代码一样跳过await语句。
在Promise中, 我们需要使用 .catch,这样错误处理代码非常冗余
Promise调试
《1》不能在返回表达式的箭头函数中设置断点
《2》如果你在.then代码块中设置断点,使用Step Over快捷键,调试器不会跳到下一个.then,因为它只会跳过异步代码。
4、错误栈
Promise链中返回的错误栈没有给出错误发生位置的线索。错误栈中唯一的函数名为callAPromise,然而它和错误没有关系。(文件名和行号还是有用的)。
async/await中的错误栈会指向错误所在的函数。分析生产环境的错误日志时,它将非常有用。
JavaScript 如何优雅的处理 async/await 异常:
async/await 如何优美的处理异常? - CNode技术社区 (cnodejs.org)
(156条消息) JavaScript 如何优雅的处理 async/await 异常_Jusme_wx的博客-CSDN博客_js async await 异常处理
引用计数垃圾收集(限制:无法处理循环引用)
这是最初级的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。
标记-清除算法
这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。垃圾回收器将定期从根开始,找到所有可以获得的对象和收集所有不能获得的对象。
从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。
避免垃圾回收
通过上面内容了解了,浏览器虽然可以自动化执行垃圾回收,但如果项目比较大代码复杂,回收执行代价较大,某些情况甚至不能识别回收
1.数组array优化
将[]赋值给一个数组对象,是清空数组的捷径(例如: arr = [];),但是需要注意的是,这种方式又创建了一个新的空对象,并且将原来的数组对象变成了一小片内存垃圾!实际上,将数组长度赋值为0(arr.length = 0)也能达到清空数组的目的,并且同时能实现数组重用,减少内存垃圾的产生。
2. 对象尽量复用
对象尽量复用,尤其是在循环等地方出现创建新对象,能复用就复用。不用的对象,尽可能设置为null,尽快被垃圾回收掉。
3.循环优化
在循环中的函数表达式,能复用最好放到循环外面。
四种常见的内存泄漏:全局变量,未清除的定时器,闭包,以及 dom 的引用
7、var、let、function 和 const-----变量提升和函数提升
JS代码分为两个阶段:编译阶段和执行阶段;变量提升和函数提升发生在编译阶段。
首先我们要知道定义一个JS变量分为三个阶段
var、let、function 和 const的过程
函数优先:当变量声明和函数声明同时存在时,函数声明优先于变量声明,即函数声明会覆盖变量声明。原因在于函数的赋值过程也会提升。那有人可能有疑问,如果将var变成let呢?结果为foo不允许被重复声明。
foo();
var foo;
function foo() {
console.log(1)
}
let 有暂时死区:所谓暂时死区,就是不能在初始化之前,使用变量。
const声明一个只读的常量 。一旦声明, 就必须立即初始化,值也不能再改变。
对于简单的基本数据类型(string number boolean null undefined)值保存在指向的那个内存地址,因此等同于常量。
但对于引用数据类型(object array ),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即指向另一个固定的地址)------如果这个地址指向一个对象,不可变的只是这个地址,即不能把foo指向另一个地址,如果将另一个对象赋值给改对象,就会报错。但对象本身是可变的,可以修改其属性。
var、let和 const除了声明和提升的区别还有:
ES5 只有全局作用域和函数作用域,没有块级作用域 ----- var ,这带来很多不合理的场景
缺点:内层变量可能会覆盖外层变量;用来计数的循环变量泄露为全局变量。所以ES6新增了let,const来声明块级作用域
在同一作用域下var可以重复声明,let,const不可以重复声明。
var声明的变量不存在块级作用域, let,const声明的变量存在块级作用域。
var和let可以重新赋值 const声明的是一个常量,const声明的变量必须要进行初始化 不能够重新赋值
var声明的变量不存在暂时性死区,let,const声明的变量存在暂时性死区。
弊端
1. setInterval对自己调用的代码是否报错漠不关心。即使调用的代码报错了,它依然会持续的调用下去
2. setInterval无视网络延迟。在使用ajax轮询服务器是否有新数据时,必定会有一些人会使用setInterval,然而无论网络状况如何,它都会去一遍又一遍的发送请求,如果网络状况不良,一个请求发出,还没有返回结果,它会坚持不懈的继续发送请求,最后导致的结果就是请求堆积。
3. setInterval并不定时。如果它调用的代码执行的时间大于定时的时间,它会跳过调用,这就导致无法按照你需要的执行次数或无法得到你想要的结果
4. 把浏览器最小化显示等操作时,setInterval并不是不执行程序,
它会把setInterval的回调函数放在队列中,等浏览器窗口再次打开时,一瞬间全部执行时
所以,鉴于这么多但问题,目前一般认为的最佳方案是:用setTimeout模拟setInterval,或者特殊场合直接用requestAnimationFrame
补充:JS高程中有提到,JS引擎会对setInterval进行优化,如果当前事件队列中有setInterval的回调,不会重复添加。
解决方案
使用setTimeout代替setInterval。
可以给setTimeout设置时间后,在最后调用自身。如果希望“匀速”触发。可以计算代码执行时间,用希望的延迟减去上次执行的时间。
注:有一种想法是将setInterval的延迟时间设置的长于上述的几种时间,来达到绝对的均速调用。但事实上,js的计时器因为自身机制的原因,存在4ms–15ms的误差。
- //实现的方法挺简单的 ,如下代码
- //参数: 毫秒 需要执行的方法
- function setInter(s,fn){
- let timeOut = (s,fn)=>{
- setTimeout(()=>{
- fn();
- timeOut(s,fn);
- },s)
- }
- timeOut(s,fn);
- }
-
- //调用上面的方法
- var i=0;
- setInter(1000,function(){ i++; console.log("hello world!"+i)})
原文链接:https://blog.csdn.net/runOnWay/article/details/80900437
9、服务端渲染SSR和实现原理
10、 preflight---CORS 预检请求
前端 | 浅谈预检请求_程序媛Sherry的博客-CSDN博客_预请求作用
isPrototypeOf() 方法用于测试一个对象是否存在于另一个对象的原型链上。
instanceof 运算符用来测试一个对象在其原型链中是否存在一个构造函数的 prototype 属性,是一个运算符,它期望两个操作数,一个对象和一个Constructor function,它将测试传递的函数原型属性是否存在于对象的链上(通过[[HasInstance]](V)内部操作,仅在函数对象中可用)。
描述:instanceof 运算符用来检测 constructor.prototype 是否存在于参数 object 的原型链上。
// 定义构造函数
function C(){}
function D(){}
var o = new C();
// true,因为 Object.getPrototypeOf(o) === C.prototype
在浏览器中使用javascript module(译) - 简书
1)pending:在过程中,没有结果,不会触发then和catch
2)resolved:已经解决,触发then
3)rejected:已经拒绝,触发catch
Reflect - JavaScript | MDN (mozilla.org)
Proxy - JavaScript | MDN (mozilla.org)
高阶函数:一个函数内部返回一个新的函数,内部采用闭包的写法
高阶组件:(HOC) Higher-Order-Component
为了提⾼组件复⽤率,可测试性,就要保证组件功能单⼀性;但是 若要满⾜复杂需求就要扩展功能单⼀的组件,在React⾥就有了 HOC(Higher-Order Components)的概念,
定义:⾼阶组件是⼀个⼯⼚函数,它接收⼀个组件并返回另⼀个组件。简单来说就是组件复用
18、Refresh token的意义:关于 token 过期的疑惑,为什么需要 refresh token? - 糯米PHP (nuomiphp.com)
流程:
用户每次登录会获取 access token 和 refresh token
前端保存 access token 和 refresh token
每次前端请求带上 access token ,后端用 access token 去 oauth2 服务验证是否有效。
如果无效则反馈给前端,前端会再次用 refresh token 发送请求。oauth2 会验证 refresh token 是否在有效期内。如果有效,则返回新的 access token 和 refresh token (延长有效期)。前端更新存储的 token 。
如果 refresh token 无效则跳转到登录页。
refresh token 设置了一周时间。如果用户每天用,其实基本不会要求重登录。只有长期不用才会要求重新登录。
存在意义:
因为 HTTP1.1 协议每次请求都是独立的,不能复用的连接。所以,在每次请求中都需要带上令牌。令牌有效期越长,那么请求携带次数就越多,传输过程越容易暴露令牌,导致安全性下降。总而言之,就是一个令牌使用时间和次数越多,那么在使用过程中越不安全。反之,把令牌有效期设置的越短,那么就越安全。但如果这样子,将导致用户体验很差。那么如何解决这个矛盾的?对于经典的 web 应用,令牌的有效时间是 30 分钟,在有效期内使用令牌请求,后端将刷新令牌的有效期。如果超过 30 分钟再发起请求,服务端会要求前端带上 refresh token ,即刷新令牌。刷新令牌有效期可以设置很多天,比如设置一周。刷新令牌设置这么长时间那不是很不安全?其实不是的,刷新令牌相比前面每次请求使用的令牌来说,区别在于低频次使用。虽然有效期长得多,但使用次数非常低,有的方案只用一次。如前所述,使用刷新令牌换取普通令牌,并获取一个新的刷新令牌,原刷新令牌作废。如此,便实现了用户登录后,只要不超过一周内访问服务,那便可以一直免登录(例如你手机上的微信)。最后关于令牌本身,可以是服务端随机生成的 session id ,也可以是服务端不保存映射关系的 JWT ,主要看具体应用场景。
最后归纳一下,token 和 refresh token 的区别在于有效期一个短一个长。使用上 token 用于每次请求,refresh token 用于 token 过期后去换取新的 token 和 refresh token 。这样设计的目的,就是为了解决安全性和使用体验之间的矛盾。
access token 有效期短 被盗损失更小 安全性更高
如果refresh token被盗了 想刷新access token的话 也需要提供过期的refresh token 盗取难度增加
同时refresh token只有在第一次获取和刷新access token时才会在网络中传输,因此被盗的风险远小于access token 从而在一定程度上 更安全了一点
18、JWT和普通Token的区别
https://blog.csdn.net/qq_41369135/article/details/116530607
一、传统token
例子:传统token登陆过程
前端点击登陆,服务器验证账号密码成功
服务器生成令牌,本质是一个32位的uuid
将该令牌存到数据库或redis中,key是uuid,value是userId
把令牌返给客户端,客户端把令牌存在cookie中。
下次请求的时候就把令牌放在请求头里带上
从redis中验证该令牌是否过期
获取value内容userId
根据userId查询用户信息,再返回客户端
传统token优缺点
优点:
可以隐藏真实数据
适用于分布式/微服务
安全系数高
缺点:
存放在redis,必须依赖服务器,暂用服务器资源
效率非常低
二、JWT
例子:JWT登陆过程
Jwt优缺点有那些
优点:
无需服务器端存放数据,减轻服务器端的压力
占用带宽比较小、跨语言
token自身包含用户信息且无法篡改,在服务(网关)中可以自行解析校验出用户信息,对认证服务器(account-svc)压力小
缺点:
建议不要放铭感数据 userid、手机号码(如果非要放userId,deptId等信息,可采用rsa256算法加密)RSA256生成Jwt
Jwt生成之后无法修改(发生变化)
后端无法统计 生成jwt
无法吊销令牌,只能等待令牌自身过期
令牌长度与其包含用户信息多少正相关,传输开销较大
Jwt是无状态的,如果别人获取到了,别人也能用
19、Token、session、cookie
20、手写观察者模式和发布订阅模式
21、前端工程化和部署(husky、sonar、github/gitlab/jekins/阿里云/腾讯云等自动化部署)、react佳实践
22、前端权限管理
面试官:vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做? | web前端面试 - 面试官系列 (vue3js.cn)
24、npm vs yarn:npm 和 yarn 那个好用? - 知乎 (zhihu.com)
26、CSS选择器的解析顺序--从右向左
(130条消息) 为什么CSS选择器是从右往左解析_津泊客的博客-CSDN博客_css解析顺序
27、CDN缓存
【CDN 最佳实践】CDN缓存策略解读和配置策略 - 知乎 (zhihu.com)
28、git rebase vs. git merge
git merge:按照提交时间的先后顺序进行依次放到master分支上
默认情况下,Git执行"快进式合并"(fast-forward merge),会直接将develop分支指向feature分支。使用--no-ff参数后,会执行正常合并,在develop分支上生成一个新节点。为了保证版本演进的清晰,推荐采用这种做法
git rebase:实际上是将当前执行rebase分支的所有基于原分支提交点之后的commit重新生成一个新的commit hash值,再次基于原分支目前最新的commit点上进行提交,不再根据两个分支上实际的每次提交的时间点排序,rebase完成后,重新合并的代码的commit呈线性排列
git merge 及 git rebase的区别 - coolw - 博客园 (cnblogs.com)
git pull和git pull --rebase区别:git pull做了两个操作分别是‘获取’和合并。所以加了rebase就是以rebase的方式进行合并分支得到一条干净的分支流。
git pull = git fetch + git merge FETCH_HEAD
git pull --rebase = git fetch + git rebase FETCH_HEAD
git rebase -i dev 可以将dev分支合并到当前分支
这里的”-i“是指交互模式。就是说你可以干预rebase这个事务的过程,包括设置commit message,暂停commit等等。
git rebase --abort 放弃一次合并
合并多次commit操作:
1 git rebase -i dev
2 修改最后几次commit记录中的pick 为squash
3 保存退出,弹出修改文件,修改commit记录再次保存退出(删除多余的change-id 只保留一个)
4 git add .
5 git rebase --continue
6 git push origin 开发分支
Merge vs Rebase - 知乎 (zhihu.com)
(130条消息) git git pull和git pull --rebase的区别_神奇大叔的博客-CSDN博客_git pull与git rebase
29、git常用命令
Stage & Commit Files: git add, git commit, & git log free tutorial (nobledesktop.com)

a、Check Status
git status:check the status of our Git repo
b、Stage Files to Prepare for Commit
Keep in Mind:
c、 Unstage a File
git reset HEAD example.html (replace example.html with your file or folder)
d、Deleting Files
要删除的文件是没有修改过的,就是说和当前版本库文件的内容相同
要删除的文件已经修改过,就是说和当前版本库文件的内容不同。
git rm -f example.html to force removal a file (and stage it)
当我们需要删除暂存区或分支上的文件, 但本地又需要使用, 只是不希望这个文件被版本控制, 可以使用
git rm --cached file_path
e、Commit Files
git commit -m "Message that describes what this change does"
f、Fixing Your Last Commit Message
git commit --amend -m "Put your corrected message here"
g、View a List of Commits
git reflog show命令,可以查看完整的提交历史,
NOTE: If the list is long, use the Down/Up Arrow keys to scroll and hit Q to quit.
h、git reset --soft 版本号
用于版本的回退,只进行对commit操作的回退,不影响工作区的文件。
git reset -–hard:彻底回退到某个版本,本地的源码也会变为上一个版本的内容,撤销的commit中所包含的更改被冲掉;
i、git checkout -- file命令,来清空工作区的修改
j、git revert
git revert -n commit-id
反做多个commit-id
git revert -n commit-idA..commit-idB
合并冲突后退出
git revert --abort
合并后退出,但是保留变化
git revert --quit
合并后解决冲突,继续操作
- git add .
- git commit -m "提交的信息"
k、git cherry-pick <commitHash> 将指定的提交(commit)应用于其他分支。
上面命令就会将指定的提交commitHash,应用于当前分支。这会在当前分支产生一个新的提交,当然它们的哈希值会不一样。
git cherry-pick命令的参数,不一定是提交的哈希值,分支名也是可以的,表示转移该分支的最新提交。
git cherry-pick feature 表示将feature分支的最近一次提交,转移到当前分支。
git cherry-pick 教程 - 阮一峰的网络日志 (ruanyifeng.com)
30、css in js vs css module
styled-component,不能预先编译,主要是运行时动态渲染
CSS-in-JS:一个充满争议的技术方案 - 知乎 (zhihu.com)
值得一提的是 @compiled/css-in-js,这个库会用类似于 Angular 的预先(AoT)编译器,将组件样式预先编译为 CSS 字符串,嵌入转译的 JS 代码中。这种方式显著减少了因变量引起的 CSS 冗余问题。
CSS Modules VS. styled-components,哪个才是解决 CSS 不足之处的更好方案? - 掘金 (juejin.cn)
31、 attribute和property的区别
JS中attribute和property的区别 - L_mj - 博客园 (cnblogs.com)
(131条消息) attribute和property,attr()和prop()的区别_代码小公举的博客-CSDN博客
32、为什么将小图片转换成base64格式?
图片的 base64 编码就是可以将一副图片数据编码成一串字符串,使用该字符串代替图像地址;
1. 提升性能: 网页上的每一个图片,都是需要消耗一个 http 请求下载而来的, 图片的下载始终都要向服务器发出请求,要是图片的下载不用向服务器发出请求,base64可以随着 HTML 的下载同时下载到本地.减少https请求。Base64转码后文件比实际的大30%,但是网站启用了GZIP,反而可以忽略增长的大小。Base64在小范围使用上完胜CSS Sprite,因为后者只适用于CSS,而Base64可以直接使用src方式引用,方便快捷。
但也并不是什么图片都适合用base64来处理,因为图片越大,转换的base64的字符串就越长,对带宽的要求更高了。
2. 加密: 让用户一眼看不出图片内容 , 只能看到编码。
3. 方便引用: 在多个文件同时使用某些图片时, 可以把图片转为base64格式的文件, 把样式放在全局中, 比如common.css, 以后在用的时候就可以直接加类名, 二不需要多层找文件路径, 会提升效率
33、webpack对图片和css的处理流程
25、svg转font的好处:SVG转字体图标 - 何菏 - 博客园 (cnblogs.com) ----- iconfont-阿里巴巴矢量图标库
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。