无感登录:
请求拦截,跟新token
CSS
- :伪类 :before :after
- ::伪元素 ::before ::after
Chrome字体默认16px, 最小12px, 在小可以使用缩放 transform:scale(0.2)
页面导入样式时,使用 link 和@import 有什么区别?
- 引入方式不同:link 属于 HTML 标签 , @import 是 CSS 提供的,只能用于加载 CSS。
- 加载顺序不同:页面加载时,link会同时被加载,而@import引用的css会等到页面加载完成后加载。
- 兼容性不同:link作为HTML的标签,不存在兼容性的问题。而@import这个CSS语法是在CSS2.1才有的,所以@import没有link兼容性好。
如何实现浏览器内多个标签之间的通信
- 使用LocalStorage
LocalStorage在一个标签内被添加、修改、删除内容时。都会触发一个storage事件,通过在另一个标签里监听storage事件,既可得到localstorage存储的值,实现不同标签之间的通信。
LocalStorage.setItem(key,value)添加内容,
- cookie+setInterval
使用cookie+setInterval,将要传递的数据存储在cookie中,每隔一段时间读取cookie信息,即可随时获取要传递的信息。
首先要想在多个窗口中通信,通信的内容一定不能放在window对象中,因为window是当前窗口的作用域,里面的内容只以属于当前窗口。有一种方式就是放在cookie中,cookie是浏览器的本地存储机制,和窗口无关,存在于硬盘上都可以读取。接下来我们就来演示一下。
缺点:
- cookie空间有限,浏览器在每一个域名下最多能设置30-50个cookie,容量最多为4k左右。
- 每次HHTP请求才会把当前域的cookie发送到服务器上,包括只在本地才用到的而服务器不用的,浪费带宽。 3. setInterval频率设置过大会影响浏览器的性能,过小会影响时效性。
优点:每个浏览器都兼容
- webSocket方式
缺点:
- 它需要服务端的支持才能完成任务。如果socket数据量比较大的话,会严重消耗服务器的资源。
- 必须要在服务端项目中写服务端监听程序才能支持。 优点:使用简单(客户端简单,服务端苦逼了),功能灵活、强大,如果部署了WebSocket服务器,可以实现很多实时的功能。
- SharedWorker方式
浏览器内核的理解
浏览器主要分为两个部分:渲染引擎和 JS 引擎
渲染引擎:主要负责获取页面内容和排版渲染页面
JS 引擎:解析和执行 JS 来实现页面的动态效果,以及交互内容
简述一下你对HTML 语义化的理解
html 语义化让页面的内容结构更加简单易懂, 便于搜索引擎解析,便于 阅读维护和理解
什么是盒模型
盒模型分为两种: IE 盒模型和 W3C 盒模型 (标准盒模型)
W3C 标准盒模型: 标准盒模型的width与height属性只包含了content
IE 盒模型: IE盒模型的width与height属性的范围包含了border、padding和content、
在网页中,一个元素占有空间的大小由几部分组成,包括元素的内容content,元素的内边距padding、元素的边框border、元素的外边距margin四个部分组成,这四个部分占有的空间中
box-sizing:content-box 标准盒模型
box-sizing:border-box IE盒模型
rem em px的区别
- rem css3新增的相对单位,相对于根节点html的字体大小来计算的
- em:会继承父级元素的字体大小
- px:像素的相对于显示器屏幕分辨率而言的
flex布局中flex:1和flex:auto
flex:1 的尺寸是较长的尺寸优先牺牲自己的尺寸,而flex:auto 中是较长的尺寸优先扩展自己的尺寸由于在这里只有一个元素,所以效果是一样的。两者的区别是 flex-basis属性 如果是多个元素,则flex:1 1 1 0% 代表剩余空间均分,即去除内外边距、边框,但是忽略自身元素宽度等。 flex:auto 去除内外边距、边框,但是要加上自身元素宽度。
总结:如果你要完全等分布局使用flex:1 ,如果你要根据内容宽度动态分配宽度则使用flex:auto (flex:1 1 auto)(顶部导航条根据内容自动撑开)
flex:0 1 50% 二等分
flex:0 1 25% 四等分
flex:1实际代表的是三个属性的简写 1 1 0%
flex-grow:该属性定义项目放大的比例,取值越大,代表向父级索取的宽度越大,默认值为1
flex-shirnk:该属性定义项目缩小的比例,取值越大,代表缩小的比例越大,默认值为1,该属 性常用来当子元素的宽度超出父元素的宽度时,通过设置flex-shirnk属性让子级的宽度不超过父级宽度,达到自适应缩小的效果
flex-basis:该属性定义在平分剩余空间之前,项目在主轴上占据的空间大小位置,默认值为0%( 注意:当设置了flex-basis属性之后,该元素下的子级元素的 width 属性不在生效,最终子级盒子的宽度是通过flex-grow属性设置的 放大比例
ios中元素被触摸时候产生半透明灰色遮罩:
a,button,input,textarea {
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
自适应
l 淘宝无限适配+布局 rem
Html
重绘与重排的区别
重排:当渲染树中部分或者全部元素的尺寸、结构或者属性发生变化时,浏览器会重新渲染部分或者全部文档的过程就称为重排/回流
重绘:当页面中某些元素的样式发生变化,但是不会影响其在文档流中的位置时,浏览器就会对元素进行重新绘制,这个过程就是重绘。
下面这些操作会导致重排/回流:
页面的首次渲染 、浏览器的窗口大小发生变化 、元素的内容发生变化 、元素的尺寸或者位置发生变化、 元素的字体大小发生变化、 激活 CSS 伪类、 查询某些属性或者调用某些方法 、添加或者删除可见的 DOM 元素
在触发回流(重排)的时候,由于浏览器渲染页面是基于流式布局的,所以当触发回流时,会导致周围的 DOM 元素重新排列,它的影响范围有两种: 全局范围:从根 节点开始,对整个渲染树进行重新布局局部范围:对渲染树的某部分或者一个渲染对象进行重新布局
下面这些操作会导致重绘:
color、background 相关属性:background-color、background-image等 outline 相 关 属 性 : outline-color 、outline-width、text-decoration border-radius、visibility、box-shadow 注意: 当触发重排/回流时,一定会触发重绘,但是重绘不一定会引发重排/回流。
因为重排和重绘不只是对单个的dom元素进行操作,而是对整个【图层】进行操作,需要花费时间,如果频率高,非常的影响性能。
微任务和宏任务
浏览器是多线程
宏任务:由宿主(浏览器、Node)发起的,需要排队
微任务: JS 自身发起。 立即执行的
先执行微任务,然后是宏任务
宏任务:
- 异步 Ajax 请求、
- setTimeout、setInterval、
- 文件操作
- DOM事件
注:Dom事件不是异步操作,但是它依赖了event loop机制
微任务(microtask) :
- Promise回调
- process.nextTick (是 Node.js 中一个特殊的函数,用于在当前操作结束后(当前事件循环的末尾)立即执行回调函数)
- 其它微任务
任务队列和event loop(事件循环)
1)所有的同步任务都在主线程上执行,行成一个执行栈。
2)除了主线程之外,还存在一个任务列队,只要异步任务有了运行结果,就在任务列队中植入一个时间标记。
3)主线程完成所有任务(执行栈清空),就会读取任务列队,先执行 微任务队列在执行宏任务队列。
4)重复上面三步。
只要主线程空了,就会读取任务列队,这就是js的运行机制,也被称为 event loop(事件循环)。
注:JavaScript是单线程执行模型,执行的时候将会区分为主线程和任务队列。主线程执行完毕,会从任务队列中读取新的任务放入主线程进行执行,这个读取过程是循环读取,所以也叫事件循环。
注: Promise 优先于 setTimeout 宏任务,所以 setTimeout 回调会最后执行
搜索引擎缓存机制
缓存(Cache)是目前所有搜索引擎都会采用的技术。
缓存——就是在高速内存硬件设备内开辟一块数据存储区,用来容纳常见的用户查询以及搜索结果(或索引数据以及搜索的中间结果),同时采用一定的管理策略来维护存储区内的数据。
缓存的目的:加快用户查询的响应速度;减少搜索引擎后来计算量,节省计算资源;
缓存淘汰策略:根据一定的策略将缓存中的项目进行替换,因为不论给缓存多大的空间,当系统运行到一定的程度,很可能缓存已经满了,当有新的需要缓存的内容要进入缓存时,需要根据一定的策略,从缓存中剔除一部分优先级别较低的缓存内容,以腾出空间供后续内容放入缓存存储区;
浏览器的缓存机制 强制 缓存、协商缓存*
强制缓存、协商缓存是针对静态文件资源的
缓存机制:在一段时间内保留已接收的web资源的一个副本,在有效时间内,不在请求。
强制缓存:
当浏览器向服务器发起请求时,服务器会将缓存规则放入HTTP响应报文的HTTP头中和请求结果一起返回给浏览器,控制强制缓存的字段分别是Expires(服务器返回该请求的结果缓存的到期时间)和Cache-Control(,默认值为private,表示所有内容都可以缓存;一般使用max-age=xxx,表示缓存内容将在xxx秒后失效),其中Cache-Control优先级比Expires高,
Expires: 返回服务器时间
Cache-Control: 只有为:no-store、no-cache、max-age=0才会生效(也就是客户端不想走强制缓存的时候生效。no-store意思是不使用缓存;no-cache意思是不走强缓存,走协商缓存;****
强制缓存的情况主要有三种(暂不分析协商缓存过程),如下:
- 不存在该缓存结果和缓存标识,强制缓存失效,则直接向服务器发起请求(跟第一次发起请求一致)
- 存在该缓存结果和缓存标识,但该结果已失效,强制缓存失效,则使用协商缓存
- 存在该缓存结果和缓存标识,且该结果尚未失效,强制缓存生效,直接返回该结果
协商缓存 :
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程,同样,协商缓存的标识也是在响应报文的HTTP头中和请求结果一起返回给浏览器的,控制协商缓存的字段分别有:Last-Modified(最后一次改变时间) 和Etag(资源文件的一个唯一标识)
- 协商缓存生效,返回304 ,使用缓存
- 协商缓存失效,返回200和请求结果
浏览器是怎么对 HTML5 进行离线缓存资源进行管理和加载的
在联网情况下,html 头部有 manifest 属性,会请求 manifest 文件, 如果是第一次访问,浏览器会根据 manifest 的内容下载相应的资源并且进行离线缓存, 如果不是第一个, 会加载成为新的 manifest 文件,新旧 manifest 文件对比, 如果一致,则不发生变化, 如果不一致,那么会重新下载文件中的资源并进行离线缓存
Html
1. 新增语义化标签
2. Form表单增强
3. 视频和音频
4. Canvas和svg
5. 地理位置定位 ( Geolocation api)
6. HTML5 为拖放行为提供了Drag & Drop API,Drag代表拖动
//事件类型
//被拖动对象可以出发的事件
dragstart:拖动开始事件。(拖拽开始)
drag:从dragstart开始到dragend结束前,在拖动时不断出现。(拖拽事件)
dragend:拖动结束事件。(拖拽结束)
//拖动到的目标对象可以触发的事件
dragenter:拖到当前元素区域内的事件。(拖拽进入)
dragover:拖到当前元素的区域事件,该事件在拖动到过程中不断的触发。(悬浮事件)
dragleave:拖出当前元素区域事件。(拖拽离开)
drop:当前元素区域停止拖拽的事件。ps:drop往往发生在dropend事件前(丢弃事件)
7. Web Worker
Js实现多线程 (图像,大数据,多媒体,后台请求)
<!DOCTYPE html>
<html>
<head>
<title>Web Worker </title>
<script>
// 创建一个新的 Web Worker,指定 worker.js 文件作为代码文件
const worker = new Worker('worker.js');
// 向 Web Worker 发送一个消息
worker.postMessage(100);
// 监听 Web Worker 返回的结果
worker.onmessage = function(event) {
console.log('斐波那契数列:', event.data);
};
</script>
</head>
<body>
<h1>Web Worker Example</h1>
</body>
</html>
Worker.js
// 监听来自主线程的消息
onmessage = function(event) {
const n = event.data;
var result = fibonacciArray(n);
postMessage(result); // 将结果发送回主线程
};
function fibonacciArray(n) {
return 1;
}
8. Web Storage
sessionStorage(会话存储)和locaStorage(永久存储)
9. Web Socket(长链接)
WebSocket协议本质上是一个基于TCP的协议,WebSocket使得客户端和服务器之间的数据交换变得更加简单,当获取WebSocket连接之后,
const wsuri = ' ' //ws地址
this.websock = new WebSocket(wsuri);
this.websocket.onopen = function(){} ;
this.websocket.onerror = function(){};
this.websock.onmessage = function(msg){};
this.websock.onclose = function(){};
注:右侧为方法
JS
js是作为浏览器的脚本语言,主要是实现用户与浏览器的交互,以及操作dom;这决定了它只能是单线程,否则会带来很复杂的同步问题。 举个例子:如果js被设计了多线程,如果有一个线程要修改一个dom元素,另一个线程要删除这个dom元素
单线程,异步,非阻塞i/0
基本数据类型
setTimeout最小执行时间4ms
setInterval 最小执行时间 10ms
基本数据类型:number、string、boolean、null、undefined、symbol(es6)
引用数据类型:object(array、function、)
console.log(true + 1) //2
console.log('name' + true) //nametrue
console.log(undefined + 1) //NaN
console.log(typeof NaN) //number
console.log(typeof undefined) //undefined
console.log(typeof null) //object
console.log(Number(null)) // 0
console.log(Number(undefined)) // NaN
==:根据原型上的 valueOf进行数据类型转换,不在页面展示
判断数据类型有几种方法
- typeof:(typeof 1) typeof null的值为Object,无法分辨是null还是Object.
- Instanceof:(9 instanceof Number) 只能判断对象是否存在于目标对象的原型链上,后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支
- Constructor :在类继承时会出错 ,用于检测引用数据类型,检测方法是获取实例的构造函数判断和某个类是否相同,如果相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类也加入进来,避免了原型链的干扰
- Object.prototype.toString.call: Let a=4 Object.prototype.toString.call(a) === ‘[object String]’ 返回的是该数据类型的字符串
Object
Object.defineproperty(obj, prop, desc )
参数1:obj 属性所在的对象
参数2:prop 属性名
参数3:desc 描述符 一般是一个对象
Object.defineProperty(person,'name',{
value:"", // 包含这个属性的数据值。默认值为undefined
enumerable:true, //控制属性是否可以枚举,能否通过for in循环访问属性,默认值为false
writable:true, //控制属性是否可以被修改,默认值是false
configurable:true //控制能否通过delete删除属性从而重新定义属性,能否修改属性的特性,或者能否把属性修改为访问器属性,默认值为false。
})
Object.keys(对象名)获取对象自身属性
Object.values(对象名)获取对象自身属性值
Object.assign() 浅拷贝, 用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
简单来说,就是Object.assign()是对象的静态方法,可以用来复制对象的可枚举属性到目标对象,利用这个特性可以实现对象属性的合并,或改变目标对象
Object.assign(target, ...sources)
参数:target--->目标对象 source--->源对象
返回值:target,即目标对象
Call、apply、bind
修改函数中this指向问题,
Call: 第一个参数为this指向对象,第二个参数是能够传多个参数,没有第一个指向window
注:修改this指向,传递参数,执行函数
apply:第一个参数为this指向对象,只能接受一个数组 (常用于继承),没有第一个指向window
Bind:不会调用函数,会改变函数的this,返回一个新函数。(防抖)
改变函数的作用域;
function fruits(){}
fruits.prototype = {
color: "red",
say: function(){
console.log("My color is " + this.color);
}};
var apple = new fruits;
apple.say(); //My color is red
实现父类继承;
function fruits(){}
fruits.prototype = {
color: "red",
say: function(){
console.log("My color is " + this.color);
}};
var another = {
color: "yellow"};
var apple = new fruits;
apple.say(); //My color is red
apple.say.call(another); //My color is yellow
apple.say.apply(another); //My color is yellow
字面量创建对象和new创建对象有什么区别
字面量:
Let obj={}
工厂方式:
function creatObjec(参1,...){
}
构造方法:
Function creatObjec1(){
}
字面量:
字面量创建对象更简单,方便阅读
不需要作用域解析,速度更快
new内部:
- 创建一个空对象;
- 将空对象的原型,指向构造函数原型;
- 将空对象作为构造函数的上下文,(改变this指向)
- 对构造函数又返回值的处理判断
Location与history
1.href :完整路径
2.protocal :url协议
3.pathname :路径
4.hash:从#开始(锚)
5.host :主机和端口号
6.hostname:主机名
7.port:端口号
8.search:从? 开始的 参数名+数据
Location.assign() //有历史记录
Location.href=’’ //有记录
Location.replace(“”) //无记录
Location.reload() //参数:false普通刷新,读取记录 ;true 强制刷新,
History (全部由记录)
History.forward() History.go() History.back()
什么是作用域,什么是作用域链
规定变量和函数的可使用范围称作作用域
查找变量或者函数时,需要从局部作用域到全局作用域依次查找,这些作用域的集合称作作用域链。
优先级:声明变量>声明普通函数>参数>变量提升
什么是闭包?闭包的作用?闭包的应用
闭包就是在一个函数内部创建另一个函数,让你可以在一个内层函数中访问到其外层函数的作用域或者闭包就是能够读取其他函数内部变量的函数。
函数执行,形成私有的执行上下文,使内部私有变量不受外界干扰,起到保护和保存的作用
优点:
(1)保证函数不受外界干扰,实现封装,避免命名冲突(2)可以在内存中保存函数变量,充当缓存
缺点
内存消耗很大,容易造成内存泄漏, 要谨慎使用
应用:
- 设计模式中的单例模式
- for循环中的保留i的操作
- 防抖和节流
- 函数柯里化
函数柯里化:通过函数调用函数返回函数的方式,实现多次参数传递最后一次统一处理的函数编码形式。
防抖:将多次执行变为最后一次执行或立即执行
应用:
- 搜索框搜索输入。只需用户最后一次输入完,再发送请求
- 手机号、邮箱验证输入检测
- 窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染
延迟版本:
function debounce(func,wait,...args) {
let timeout; //延时器变量
return function () {
const context = this; //改变this指向
if (timeout) clearTimeout(timeout); //先判断有没有延时器,有则清空,毕竟要最后一次执行
timeout = setTimeout(() => {
func.apply(context, args) //apply调用传入方法
}, wait);
}
}
立即执行版
function debounce(func,wait,...args){
let timeout; //延时器变量
return function(){
const context = this;
let callNow = !timeout; //是否立即执行
timeout = setTimeout(() => {
timeout = null;
},wait)
if(callNow) func.apply(context,args)
}
}
节流:将多次执行变为每隔一段时间执行一次
应用:滚动加载,加载更多或滚到底部监听
时间戳版(立即执行版)
function throttle(func, wait, ...args){
let pre = 0;
return function(){
const context = this;
let now = Date.now();
if (now - pre >= wait){
func.apply(context, args);
pre = Date.now();
}
}
}
延时器版(非立即执行版)
function throttle(func, wait, ...args){
let timeout;
return function(){
const context = this;
if(!timeout){
timeout = setTimeout(() => {
timeout = null;
func.apply(context,args);
},wait)
}
}
}
事件委托
利用事件冒泡,把子元素的事件绑定到父元素
阻止事件冒泡:e.stopPropagation
原型和原型链
什么是原型?什么是原型链?如何理解
原型: 原型分为隐式原型和显式原型,每个对象都有一个隐式原型,它指向自己的构造函数的显式原型。
①所有引用类型(实例)都有一个__proto__(隐式原型)属性,属性值是一个普通的对象
②所有构造函数(Array)都有一个prototype(原型)属性,属性值是一个普通的对象
③所有引用类型的__proto__属性指向它构造函数的prototype
原型链: 当访问一个对象的某个属性时,会先在这个对象本身属性上查找,
如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再在构造函数的prototype的__proto__中查找,
这样一层一层向上查找就会形成一个链式结构,我们称为原型链。
Object.prototype.constructor.proto === Function.prototype // true
Function.prototype.proto === Object.prototype // true
Object.prototype.proto === null // true
( 多个__proto__组成的集合成为原型链
所有实例的__proto__都指向他们构造函数的prototype
所有的prototype都是对象,自然它的__proto__指向的是Object()的prototype
· 所有的构造函数的隐式原型指向的都是Function()的显示原型
· Object的隐式原型是null
原型的作用:
- 1.数据共享 节约内存内存空间
- 2.实现继承
继承
原型链继承、构造函数(借助call方法)、组合继承、寄生组合继承(借助Object.create)、ES6的extend,
原型链继承:
function Child() {
this.sex = "1";
}
Child.prototype = new Parent();
缺点:多个实例对象共享一个原型对象,也就是说实例对象获取的原型对象上的属性和方法的内存空间是共享的,其中一个实例对象改变了它的原型对象,另外的实例对象获取原型对象身上的属性和方法也会发生改变
构造函数继承call 、apply
Function Child() { Parent.call(this); this.age= "1";}
优点:1. 相对于原型链,借用构造函数有一个很大的优势,即可以在子类型构造函数中向父类型构造函数传递参数。
缺点:1、只能继承父类构造函数的属性。
2、无法实现构造函数的复用。(每次用每次都要重新调用)
3、每个新实例都有父类构造函数的副本,臃肿。
4.子类只能继承父类实例对象的属性和方法,无法继承父类原型上的属性和方法
组合继承(组合原型链继承和借用构造函数继承(常用)
function Child() {
Parent.call(this);
this.sex = "1";
}
// 继承原型链上的方法属性
Child.prototype = new Parent();
// 原型对象私有化
Child.prototype.constructor = Child;
Parent构造函数执行了两次,第一次是改变Child的原型时,第二次是通过call方法调用Parent时,多了一次性能开销
寄生组合继承(借助Object.create)
let person1 = Object.create(parent);
利用ES5里的Object.create方法,实现普通对象的继承,是浅拷贝的实现方式之一,所以存在和浅拷贝一样的问题,存在多个实例对象的引用类型数据共享,存在篡改的可能
浅拷贝,深拷贝
浅拷贝:
1)直接复制对象
2)var obhss=Object.assign({},obj)
如果源对象某个属性的值是对象,那么目标对象拷贝得到的是这个对象的引用。
3)concat,.slice()
4) 使用扩展运算符(es6)
const obj1 = {a: {b: 1}};
const obj2 = Object.assign({}, obj1)
obj1.a.b = 2;
obj2.a.b // 2
深拷贝:
1 通过JSON.stringify() 和 JSON.parse();
2 递归复制所有层级属性--以前常用,现在实际应用以上两种足矣
3.函数库lodash _.cloneDeep()
内存泄露、垃圾回收机制
什么是内存泄漏
内存泄露是指不再用的内存没有被及时释放出来,导致该段内存无法被使用就是内存泄漏
为什么会导致的内存泄漏
内存泄漏指我们无法在通过js访问某个对象,而垃圾回收机制却认为该对象还在被引用,因此垃圾回收机制不会释放该对象,导致该块内存永远无法释放,积少成多,系统会越来越卡以至于崩溃。
- 闭包
- 大量的使用全局变量
- 忘记清除的定时器
- dom元素的引用
垃圾回收机制都有哪些策略?
标记清除法:垃圾回收器会给在内存中的变量都做上标记,然后它会去除在环境中的变量和被环境中变量引用的变量(闭包)的标记,在这之后的被标记的变量就是需要清除的变量了。之后垃圾回收器将会回收这些变量所占的内存了
引用计数法:当声明一个变量并给该变量赋值一个引用类型的值时候,该值的计数+1,当该值赋值给另一个变量的时候,该计数+1,当该值被其他值取代的时候,该计数-1,当计数变为0的时候,说明无法访问该值了,垃圾回收机制清除该对象
复制整理法:最好的方法 不停的用在用的过程中不清理,一段时间后把上面占用的内存全部复制过来,这时它把所有的内存扫描一遍,这时在清理没用的,同时做了排序!缺点内存一分为2 一半内存不能使用
标记整理法:定期垃圾器扫描 扫描的同时排序工作量相对比较大!没有复制整理法轻巧!
cookie、sessionStorage、localStorage的区别
不同点:
1.存储的时间有效期不同
cookie的有效期是可以设置的,默认的情况下是关闭浏览器后失效
sessionStorage的有效期是仅保持在当前页面,关闭当前会话页或者浏览器后就会失效
localStorage的有效期是在不进行手动删除的情况下是一直有效的
2.存储的大小不同
cookie的存储是4kb左右,存储量较小,一般页面最多存储20条左右信息
localStorage和sessionStorage的存储容量是5Mb(官方介绍,可能和浏览器有部分差异性)
3.与服务端的通信
cookie会参与到与服务端的通信中,一般会携带在http请求的头部中,例如一些关键密匙验证等。
localStorage和sessionStorage是单纯的前端存储,不参与与服务端的通信
cookie和seesion区别
cookie数据存放在客户的浏览器上,session存放在服务器
cookie不是很安全,别人可以分析存放在本地的COOKIE进行COOKIE欺骗,考虑安全应该使用session
session会在一定事件内保存在服务器上,当访问增多,会比较占用你的服务器的性能,考虑到减轻服务器性能方面,应当使用cookie
单个cookie保存的数据不能超过4k,很多浏览器都限制一个站点最多保存20个cookie
避免JavaScript 阻塞,JavaScript 脚本延迟加载的方式有哪些?
1)defer 属性:给 js 脚本添加 defer 属性,这个属性会让脚本的加载与文档的解析同步解析,然后在文档解析完成后再执行这个脚本文件,这样的话就能使页面的渲染不被阻塞。多个设置了defer属性 14 的脚本按规范来说最后是顺序执行的,但是在一些浏览器中可能不是这样。
2)async 属性:给 js 脚本添加 async 属性,这个属性会使脚本异步加载,不会阻塞页面的解析过程,但是当脚本加载完成后立即执行js脚本,这个时候如果文档没有解析完成的话同样会阻塞。多个async属性的脚本的执行顺序是不可预测的,一般不会按照代码的顺序依次执行。
3)动态创建 DOM 方式:动态创建 DOM 标签的方式,可以对文档的加载事件进行监听,当文档加载完成后再动态的创建script 标签来引入js 脚本。
4)使用 setTimeout 延迟方法:设置一个定时器来延迟加载js 脚本文件
5)让 JS 最后加载:将 js 脚本放在文档的底部,来使js 脚本尽可能的在最后来加载执行。
JS实现多线程方式(Worker)
var worker = new Worker("test.js") *;* //申请一个新线程用来执行这个js文件
test.js
var i = 0 *;* function mainFunc(){
i++ *;* //把i发送到浏览器的js引擎线程里 postMessage(i) *;* //postMessage(msg) *;* //worker.postMessage方法把在新线程执行的结果发送到浏览器的js引擎线程里
}
var id = setInterval(mainFunc,1000) *;*
在主线程中有一些方法来实现对新线程的控制和数据的接收
worker.onmessage = **function**(){
*//获取在新线程中执行的js文件发送的数据 用event.data接收数据* console.**log**( event.data ) }; setTimeout( **function**(){
worker.**terminate**(); *//terminate方法用于关闭worker线程* },2000) setTimeout( **function**(){ worker = **new** **Worker**("js/test22.js"); *//再次开启worker线程* },3000)
同源策略
所谓同源策略就是浏览器的一种安全机制,来限制不同源的网站不能通信(域名、协议、端口号相同)
跨域:
1. 设置代理
2. 设置cors头(后端)
3. 设置jsonp(后端结合)
4. nginx反向代理
this指向问题
// 2 4 1 1 3
function Foo() {
getName = function () { console.log(1) }
return this
}
Foo.getName = function () { console.log(2) }
Foo.prototype.getName = function () { console.log(3) }
var getName = function () { console.log(4) }
function getName() {console.log(5)}
Foo.getName()
getName()
Foo().getName()
getName()
new Foo().getName()
var o = {
a: 10,
b: {
a: 2,
fun: function () {
console.log(this, this.a) // this 指向b a:2
},
fun1: () => {
console.log(this) //window
}
},
fn() {
console.log(this, this.a) // this 指向o a:10
}
}
o.b.fun()
o.b.fun1()
o.fn()
window.name = 'xxx'
function A() {
this.name = 222
}
A.prototype.getA = function () {
console.log(this)
return this.name + 1
}
// const a = new A()
// const func = a.getA
// func() //this 指向window
const a = new A()
const func = a.getA() // this 指向A
var a = 10
function fn() {
return this.a + 1
}
var obj = {
a: 5,
test1: function () {
return fn()
}
}
obj.test2 = fn
console.log(obj.test1()) //11
console.log(fn()) //11
console.log(obj.test2()) //6
判断是不是数组
var a=[1,2,4]
console.log(Array.isArray(a))
console.log(a instanceof Array)
console.log(Array.prototype.isPrototypeOf(a))
console.log(Object.prototype.toString.call(a)) //[object Array]
console.log(a.constructor.toString())// function Array() { [native code] } a.constructor.toString().indexOf('Array')
数组去重
var a = [1, 2,2,3,4, 4]
console.log([...new Set(a)])
console.log(Array.from(new Set(a)))
es6
let 和 const 的一个特点,禁用变量提升
链式调用
// 对象的链式调用
let obj = {
a: 5,
ins(num) {
this.a += num;
return this;
},
des(num) {
this.a -= num;
return this;
},
};
console.log(obj.ins(1).des(3));
// 类的链式调用
class Calcu {
constructor() {
this.a = 6;
}
ins(num) {
this.a += num;
return this;
}
des(num) {
this.a -= num;
return this;
}
}
const cal = new Calcu();
console.log(cal.ins(1).des(3));
function Fun() {
this.a = 6;
}
Fun.prototype = {
ins(num) {
this.a += num;
return this;
},
des(num) {
this.a -= num;
return this;
},
};
const fun = new Fun();
console.log(fun.ins(1).des(3));
箭头函数与普通函数的区别
箭头函数中的this代表上层对象,普通函数中的this代表当前对象
箭头函数的this只能指向声明在他这个作用域的上级对象中
1、样式不同,箭头函数是 =>,普通函数是 function;
2、箭头函数不能作为构造函数使用,也就不能使用 new 关键字;
3、箭头函数不绑定 arguments,可以考虑用剩余参数代替;
4、箭头函数会捕获其所在上下文的 this 值,作为自己的 this 值,定义的时候就确定了;
5、call、apply、bind 并不会影响 this 的指向;
6、箭头函数没有原型属性;
7、箭头函数不能当作 Generator 函数,不能使用 yield 关键字;
8、箭头函数全都是匿名函数:普通函数可以有匿名函数,也可以有具名函数
Promise
Promise 本质上是一个构造函数,因为使用 new 关键字创建,它是异步编程的一种解决方案
pending进行中,这是 Promise 实例创建后的一个初始态。
fulfilled已成功。在执行器中调用 resolve 后,达成的状态。
rejected已失败。在执行器中调用 reject 后,达成的状态。
Promise.prototype
1. Promise.prototype.then()方法简单理解是 resolve() 回调之后的产物,即 已成功 状态下的回调函数
2. Promise.prototype.catch()catch 方法简单理解就是 reject() 回调之后的产物,即 已失败 状态下的回调函数。
Promise.prototype.finally()
finally 方法简单理解就是 无论成功或失败 ,都会执行的回调函数
const promise = new Promise(function(resolve, reject) {
console.log('start');
reject('error');
resolve('success');
console.log('end');
});
promise.then(res=>{
console.log(res);
}).catch(error=>{
console.log(error);
})
由于状态一但改变,就不会再变,所以,它的状态有且只有且一直是初次改变的结果,而首次执行 reject ,即失败状态。
所以,上面的输出结果为 start => end => error
Promise.all() 都成功
方法用于将多个 Promise 实例,包装成一个新的 Promise 实例,
const dataList = Promise.all([postData1(),postData2(),postData3()])
只有所有请求的状态都成功 ,dataList的状态才会变成 fulfilled ,此时 postData1, postData2, postData3的返回值组成一个数组,传递给dataList的回调函数。只要有一个请求失败,那么,dataList的状态就会变成 rejected ,此时第一个被reject的实例的返回值,会传递给dataList的回调函数。
场景:三个接口分别是拉取语文,数学,英语三科成绩的接口,那么,我们需要通过这三个接口的返回,计算学生最后成绩的总分,此时用 all() 就很合理,因为三个接口的值缺一不可,如果有一个发生错误,就得不到总分,就会走 catch() ,然后,提示是哪一科的成绩数据得不到,影响了最终的总分计算。
Promise.race() 有一个成功
const dataList = Promise.rece([postData1(),postData2(),postData3()])
三个 Promise 实例,只要谁率先改变状态,那么,dataList 的状态也就跟着改变,相当于就是谁快,我就用谁
场景:① 当一个接口有三个请求接口地址,请求的数据是一致的时候,为了保证接口的最快速度匹配,可以使用这个方法。
② 在Promise实例中,放入一个延时器函数, setTimeout(() => reject(new Error('request timeout')), 5000) ,可以通过它来设置这个接口的,相当于 postData1() 必须在5秒内完成,否则会直接失败
const dataList = Promise.race([
postData1(),
new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
Promise.allSettled() 都返回结果
只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,最终包装成一个数组返回。并且它无论参数实例是成功或失败,始终只会走 .then() ,没有 .catch() ,成功之后返回的结果如下所示:
[
{ status: 'fulfilled', value: 42 },
{ status: 'rejected', reason: -1 }
]
每个对象都有 status 属性,该属性的值只可能是字符串 fulfilled 或字符串 rejected。 fulfilled时,对象有 value 属性, rejected 时有 reason 属性,对应两种状态的返回值。
场景:并不关心接口的结果,只关心这些操作有没有结束。
Promise.any() 一个成功
只要参数实例有一个成功,包装实例就会变成成功状态;只有当所有参数实例都失败,包装实例才会变成失败状态。
链式调用
Promise 本来就是用来解决回调地狱所带来的问题的,所以,其实可以用链式调用的方式,来解决层层嵌套所带来的问题
getData1().then(res=>{
console.log(res) // data1
return getData2();
}).then(res=>{
console.log(res) // data2
return getData3();
}).then(res=>{
console.log(res) // data3
})
async/await
错误处理 (三种办法)
第一种 使用 try{}catch{}
第二种 使用 catch 方法
async function get(){ let data= await func().catch (err=>{ *//err =>({ msg: "Promise失敗的", data } )* }) }
浏览器 (使用时间轮询机制捕获Promise 异常
href和src的区别
1 请求资源类型不同
(1)href 指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的联系。
(2)在请求 src 资源时会将其指向的资源下载并应用到文档中,比如 JavaScript 脚本,img 图片;
2 作用结果不同
(1)href 用于在当前文档和引用资源之间确立联系;
(2)src 用于替换当前内容;
3 浏览器解析方式不同
<link href=’’/> <script src=’’> </script>
1.若在文档中添加link ,浏览器会识别该文档为 CSS 文件,就会并行下载资源并且不会停止对当前文档的处理。这也是为什么建议使用 link 方式加载 CSS,而不是使用 @import 方式。
2.当浏览器解析到script ,会暂停其他资源的下载和处理,直到将该资源加载、编译、执行完毕,图片和框架等也如此,类似于将所指向资源应用到当前内容。这也是为什么建议把 js 脚本放在底部而不是头部的原因。
for of
for of遍历的是键值对中的值 es6
for in遍历的是键值对中的键
let arrNew = new Set(待去重的数组)
数组方法
Find: 第一个符合条件的,返回item
Filter: 全部符合条件的,返回数组
Some: 有符合条件的,返回 true
Every:全部符合条件,返回true
HTTP和HTTPS的基本概念
http:是一个客户端和服务端请求和应答的标准(TCP),用于从www服务器传输超文本到本地浏览器的超文本传输协议。
https:是以安全为目标的HTTP通道,即HTTP下加入SSL层进行加密。其作用是:建立一个信息安全通道,确保数据的传输,确保网站的真实性
HTTP和HTTPS的区别及优缺点?
l HTTPS 协议需要 CA 证书,费用较高;而HTTP 协议不需要;
l HTTP是超文本传输协议,信息是明文传输;HTTPS协议要比HTTP协议安全,HTTPS是具有安全性的SSL加密传输协议,可防止数据在传输过程中被窃取、改变,确保数据的完整性(当然这种安全性并非绝对的,低于更深的Web安全问题,次数暂且不表);
l HTTP协议的默认端口为80;HTTPS默认端口为443;
l HTTP 协议连接很简单,是无状态的;HTTPS 协议是有SSL 和HTTP协议构建的可进行加密传输、身份认证的网络协议,比HTTP 更加安全
TCP三次握手
第一次握手:建立连接,客户端发送SYN包(syn=j)(是TCP/IP建立连接时使用的握手信号。),随后客户端进入SYN-SENT阶段,等待服务端确认。
第二次握手:服务端收到syn包并确认客户的SYN,同时也发送一个自己的SYN包,随后服务器端进入SYN-RCVD阶段。
第三次握手:客户端接收到来自服务器端的确认收到数据的SYN+ACK包,明确了从客户端到服务器的数据传输是正常的,结束SYN-SENT阶段,并向服务器发送确认包ACK。随后客户端进入ESTABLISHED阶段。服务器收到来自客户端的“确认收到服务器数据”的TCP报文之后,明确了从服务器到客户端的数据传输是正常的。完成三次握手。
为什么要进行第三次握手
为了防止服务器端开启一些无用的连接增加服务器开销以及防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。
TCP四次挥手
第一次挥手:客户端告诉服务端我要断开连接了
第二次挥手:服务端告诉客户端我知道你要断开连接了,并告诉客户端你可以断开了
第三次挥手:服务端做好了断开准备,并想客户端发送断开请求,等到客户端确认
第四次挥手:客户端接收到服务端的可以释放的信号,向服务端发送确认断开的信号,服务端收到后就关闭连接。
输入url后发生了什么?
1. DNS域名解析
2. 建立TCP连接(三次握手)
3. 发送HTTP请求
4. 服务器处理请求,返回响应结果
5. 关闭TCP连接(四次挥手)
6. 浏览器解析HTML渲染页面,构建DOM树
Diff
diff算法是一种通过同层的树节点进行比较的高效算法,它可以不用频繁操作DOM,而是选用虚拟DOM节点操作,说人话就是专门用来处理虚拟DOM节点的。
Vue
1.简洁:页面由HTML模板+JSON数据+ Vue. js实例化对象组成。
2.数据驱动:自动计算属性和追踪依赖的模板表达式。
3.组件化:用可复用、解耦的组件来构造页面。
4.轻量:代码量小,不依赖其他库。
5.快速:精确而有效地批量实现DOM更新。
6.易获取:可通过npm、 bower等多种方式安装,很容易融入
vue-router 路由钩子函数
钩子函数种类有:
全局的路由钩子函数:beforeEach、afterEach(一般用于全局进行权限跳转)
单个的路由钩子函数:beforeEnter、beforeLeave(路由内部钩子,一般在路由表里)
组件内的路由钩子函数:beforeRouteEnter、beforeRouteLeave、beforeRouteUpdate
Vue的两个核心点
数据驱动:ViewModel,保证数据和视图的一致性
组件系统:应用类UI可以看作全部是由组件树构成的
Vue和React有什么不同?使用场景分别是什么
· vue是双向绑定
· react没有数据双向绑定,react是单向数据流
非响应式转换响应式
1. this.$set()
2. Object.assign()
3. 非响应式后添加 this.$foreupdate()
this.$nextTick()的原理与使用场景
下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
vue异步更新的原理:
1.修改 Vue 中的 Data 时,就会触发所有和这个 Data 相关的 Watcher 进行更新。
2.首先,会将所有的 Watcher 加入队列 Queue。
3.然后,调用 nextTick 方法,执行异步任务。
4.在异步任务的回调中,对 Queue 中的 Watcher 进行排序,然后执行对应的 DOM 更新。
keep-alive
keep-alive是Vue的内置组件,当它包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。keep-alive是一个抽象组件,它自身不会渲染一个DOM元素,也不会出现在父组件中。防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性
1.参数
· include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
· exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
· max - 数字。最多可以缓存多少组件实例
2.生命周期
1)activated
- 在 keep-alive 组件激活时调用
- 该钩子函数在服务器端渲染期间不被调用
2.deactivated
- 在 keep-alive 组件停用时调用
- 该钩子在服务器端渲染期间不被调用
- 被包含在 keep-alive 中创建的组件,会多出两个生命周期的钩子: activated 与 deactivated
- 使用 keep-alive 会将数据保留在内存中,如果要在每次进入页面的时候获取最新的数据,需要在 activated 阶段获取数据,承担原来 created 钩子函数中获取数据的任务。
注:只有组件被 keep-alive 包裹时
keep-alive 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们;
设置了 keep-alive 缓存的组件,会多出两个生命周期钩子(activated 和 deactivated )
Vue2的响应式原理
采用数据劫持结合发布者-订阅者模式模式的方式,
对象:通过 Object.defineProperty() 数据劫持,来劫持各个属性的setter,getter,在数据更新时发布消息给订阅者,触发相应监听回调。
数组:重写
双向数据绑定
我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令(如v-model,v-on)对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图
1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者*
2、实现一个订阅者Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
3、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,并根据初始化模板数据以及初始化相应的订阅器
Vue3的响应式原理
proxy的优点:
1、直接监听对象而非属性
2、直接监听数组的变化
3、拦截的方式有很多种(有13种,set,get,has)
4、Proxy返回一个新对象,可以操作新对象达到目的
Proxy只会代理对象的第一层,Vue3是怎样处理这个问题的呢? 13 种拦截方法
判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理, 这样就实现了深度观测。
监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。
在 Vue.js 3.0 中,使用 Proxy API 并不能监听到对象内部深层次的属性变化,因此它的处理方式是在 getter 中去递归响应式,这样的好处是真正访问到的内部属性才会变成响应式,简单的可以说是按需实现响应式,减少性能消耗。
watch和watchEffect的区别
区别
1、watch需要明确监听哪个属性
2、watchEffect会根据其中的属性,自动监听其变化
3、watcheffect初始化时,一定会执行一次(收集要监听的数据,不然不知道监听的是什么),watch只有你设置了初始化监听才会监听
computed与watch的区别
1、computed擅长处理的场景:一个数据受多个数据影响;watch擅长处理的场景:一个数据影响多个数据。
2、功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。
3、是否调用缓存:computed支持缓存,只有依赖数据发生改变,才会重新进行计算;而watch不支持缓存,数据变,直接会触发相应的操作。
4、是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return。
5、computed不支持异步 ,当computed内有异步操作时无效,无法监听数据的变化;而watch支持异步。
6、computed默认第一次加载的时候就开始监听;watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true(immediate:true)
什么是 MVVM
在MVVM架构下, View 和 Model(数据模型) 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理
组件中的data为什么是个函数
因为组件是用来复用的,因为js里对象是引用关系,如果data是对象形式,那么data的作用域是没有隔离的,在多个子组件时,会被外部因素影响,如果data是一个函数,那么每个实例可以独自拥有一份返回对象的拷贝,组件实例之间的data属性值不会互相影响
修饰符
1.事件修饰符:
.stop 阻止事件继续传播
.prevent阻止标签默认行为
.capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
.self 只当在event.target 是当前元素自身时触发处理函数
.once 事件将只会触发一次
.passive 告诉浏览器你不想阻止事件的默认行为
2.v-model的修饰符:
.lazy通过这个修饰符,转变为在change事件再同步
.number 将自动过滤用户的输入值转化为数值类型
.trim 自动过滤用户输入的首位空格
如何实现vue首屏加载优化的
- 把不常改变的库放到index.html中,通过cdn引入
- vue路由的懒加载
- vue组件尽量不要全局引入
- 使用轻量级的工具库
- 按需加载
Vue 初始化页面闪动问题如何解决
[v-cloak] { display: none; }
Vue.js中过滤器(filter)的使用
<!--在双花括号中使用 格式:{{值 | 过滤器的名称}}-->
<div>{{3 | addZero}}</div>
//在v-bind中使用 格式:v-bind:id="值 | 过滤器的名称
<!--在双花括号中使用 格式:{{值 | 过滤器的名称}}-->
全局的过滤器:filter
局部过滤器:filters
局部过滤器优先于全局过滤器被调用!
父子组件生命周期执行顺序
父beforecreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
Vue3
新增组合API,更好的逻辑重用和代码组织
重构虚拟 DOM
打包速度加快
内存减少
初次渲染快
生命周期
beforeCreate===>setup()
created=======>setup()
beforeMount ===>onBeforeMount
mounted=======>onMounted
beforeUpdate===>onBeforeUpdate
updated =======>onUpdated
beforeUnmount ==>onBeforeUnmount
unmounted =====>onUnmounted
获取子组件ref变量和defineExpose暴露
defineExpose({ ...toRefs(data), })
useRoute和useRouter
import { useRoute, useRouter } from 'vue-router'
useRoute相当于以前的this.router
store仓库的使用
import { useStore } from 'vuex'
import { num } from '../store/index'
const store = useStore(num)
Setup获取this (全局)
Important {getCurrentinstance} from ‘vue’
Webpack
webpack 是一个静态模块打包器,当 webpack 处理应用程序时,会递归构建一个依赖关系图,其中包含应用程序需要的每个模块,然后将这些模块打包成一个或多个 bundle。
Entry
Output
Loader (处理非js文件)
Plugins(插件,执行更广范围)
Mode (开发和生产)
Ts
特点
跨平台
ES6 特性:TypeScript 包含计划中的 ECMAScript 2015 (ES6) 的大部分特性,例如箭头函数。
面向对象的语言:如类、接口和模块。
静态类型检查:TypeScript 使用静态类型并帮助在编译时进行类型检查。因此,你可以在编写代码时发现编译时错误,而无需运行脚本。
1.1 TypeScript是JavaScript的加强版,它给JavaScript添加了可选的静态类型和基于类的面向对象编程,它拓展了JavaScript的语法。所以ts的功能比js只多不少.
1.2 Typescript 是纯面向对象的编程语言,包含类和接口的概念.
1.3 TS 在开发时就能给出编译错误, 而 JS 错误则需要在运行时才能暴露。
1.4 作为强类型语言,你可以明确知道数据的类型。代码可读性极强,几乎每个人都能理解。
1.5 ts中有很多很方便的特性, 比如可选链.
type 和 interface的异同
用interface描述数据结构,用type描述类型
type要添加等号。Interface定义类型不需要添加等号
1、定义类型
type user=string
interface Point{
x:number,
y:number
}
2、扩展接口: type 使用& ,interface使用extends
type Animal={
name:string
}
type Bear=Animal&{
honey:boolean
}
//interface
interface Animal{
name:string
}
interface Bear extends Animal{
honey:boolean
}
3、向现有类型添加新字段:interface 同名合并;type 类型创建后不能更改
//类型创建后不能更改
type MyWindow={
title:string
}
//interface
interface MyWindow{
title:string
}
interface MyWindow{
count:number
}
可索引类型接口
/ 数字索引——约束数组
// index 是随便取的名字,可以任意取名
// 只要 index 的类型是 number,那么值的类型必须是 string
interface StringArray {
// key 的类型为 number ,一般都代表是数组
// 限制 value 的类型为 string
[index:number]:string
}
let arr:StringArray = ['aaa','bbb'];
console.log(arr);
// 字符串索引——约束对象
// 只要 index 的类型是 string,那么值的类型必须是 string
interface StringObject {
// key 的类型为 string ,一般都代表是对象
// 限制 value 的类型为 string
[index:string]:string
}
let obj:StringObject = {name:'ccc'};
函数类型接口
对方法传入的参数和返回值进行约束
// 注意区别
// 普通的接口
interface discount1{
getNum : (price:number) => number
}
// 函数类型接口
interface discount2{
// 注意:
// “:” 前面的是函数的签名,用来约束函数的参数
// ":" 后面的用来约束函数的返回值
(price:number):number
}
let cost:discount2 = function(price:number):number{
return price * .8;
}
// 也可以使用类型别名
type Add = (x: number, y: number) => number
let add: Add = (a: number, b: number) => a + b
React
解释React中的虚拟DOM是什么?为什么虚拟DOM比直接操作真实DOM更高效?
答案:虚拟DOM是React框架的核心概念之一,它是用JavaScript对象来表示真实DOM结构的一种技术。当组件状态发生变化时,React会通过比较新旧虚拟DOM的差异来确定需要更新的部分,然后仅更新有变化的部分到真实DOM,而不是整体重新渲染整个DOM树。这样做的好处是减少了直接操作真实DOM所带来的性能开销,提高了应用的渲染效率。
什么是Web组件(Web Components)?如何创建一个Web组件?
答案:Web组件是一种用于封装可重用HTML元素、样式和行为的技术。Web组件由四个主要技术组成:Custom Elements(自定义元素),允许开发者创建自定义HTML元素;Shadow DOM(影子DOM),用于封装组件的样式和逻辑,使其不受外部样式的影响;HTML Templates(HTML模板),用于定义组件的初始结构;HTML Imports(HTML导入),用于导入和使用其他组件。通过这些技术,开发者可以轻松创建自己的Web组件,提高代码的可维护性和可重用性。
常用的钩子(9)
实现函数组件,代替类组件。
1. 解决class的复杂(this指向,生命周期等)
2. 解决业务逻辑复杂,难以拆分
3. 状态逻辑变得简单
4. 函数组件的思想更加符合react的设计理念
5. 注: 约定钩子一律使用use前缀,自定义也要如此
useState 状态钩子
纯函数组件没有状态,该钩子为函数组件引入state,并进行数据操作
import React, { useState } from "react";
export default function FunctionComponent(props) {
const [data, setData] = useState(0);
const addOption = () => {
setData(data + 1);
};
return (
<>
<div>{data}</div>
<button onClick={addOption}>add </button>
</>
);
}
useEffect 副作用钩子 (异步执行)
用于在函数组件中进行 接口请求,订阅,监听等操作。模拟类组件中的生命周期(componentDidMount,componentDidUpdate,componentWillUnmount三个函数的组合)
// 接受两个参数,1: 异步操作函数 2:参数是一个数组
import React, { useState, useEffect } from "react";
export function FunctionComponent(props) {
const [data, setData] = useState(0);
// 第二个参数为空 每次渲染都会执行
useEffect(() => {
setData({
data: data + 0,
});
return () => {
// 卸载之后执行
};
});
// 第二个参数为空数组 组件挂载后,运行一下
useEffect(() => {
// 相当于 componentDidMount,componentDidUpdate,componentWillUnmount
return () => {
// 卸载之后执行
};
}, []);
// 第二个参数 有参数 ,监听参数的改变
useEffect(() => {
return () => {
// 卸载之后执行
};
}, [data]);
return <div>{data}</div>;
}
useContext 共享状态钩子
跨越组件层级直接传递变量,实现数据共享
import React, { useState, useContext, createContext } from "react";
const CountContext = createContext(0);
export default function FunctionComponent(props) {
const [count, setCount] = useState(0);
function addCount() {
setCount(count + 1);
}
return (
<>
<div>{count}</div>
<button onClick={addCount}>点击一下</button>
{/* 通过value传值 外面要包裹 xxxContext.Provider*/}{" "}
<CountContext.Provider value={count}>
<Child />
</CountContext.Provider>
</>
);
}
const Child = () => {
// 获取数据
const count = useContext(CountContext);
return <div>子组件{count}</div>;
};
useReducer
存储和更新数据 组件传参
import React, { useEffect, useReducer } from "react";
function setName(state = [], action) {
switch (action.type) {
case "INIT":
return [...action.payload];
case "REPALCE":
return [...action.payload];
default:
return state;
}
}
export default function FunctionComponent(props) {
const [nameList, dispath] = useReducer(setName, []);
useEffect(() => {
dispath({ type: "INIT", payload: ["小红", "小王"] });
}, []);
return (
<div>
<NameList
nameList={nameList}
setName={(parms) => dispath({ type: "REPALCE", payload: parms })}
></NameList>
</div>
);
}
function NameList({ nameList, setName }) {
const deleteItem = (index) => {
const tem = [...nameList];
tem.splice(index, 1);
setName(tem);
};
return (
<div>
{nameList.map((item, index) => {
return (
<div key={item} onClick={(index) => deleteItem(index)}>
{item}
</div>
);
})}
</div>
);
}
useMemo 缓存
import React, { useState, useMemo } from "react";
export default function FunctionComponent(props) {
const [data, setData] = useState(0);
const [counter, setCount] = useState(0);
// 接受两个参数 参1:函数,返回值作为缓存值 。参2:依赖数组
//依赖数组中的值发生变化,重新计算。否则直接取上次计算数据
const result = useMemo(() => {
console.log("useMemo");
return counter;
}, [counter]);
// 如果没有使用useMemo,当data变化的时候,也会执行
// const result = () => {
// console.log("无useMemo");
// return counter;
// };
const addOption = () => {
setData(data + 1);
};
const addCounter = () => {
setCount(counter + 1);
};
return (
<>
<div>{result}</div>
<button onClick={addOption}>data - {data} </button>
<br />
<button onClick={addCounter}>counter - {counter} </button>
</>
);
}
useCallback 渲染性能优化 (缓存函数本身)
import React, { useState, useCallback } from "react";
export default function FunctionComponent(props) {
const [data, setData] = useState(0);
const [counter, setCount] = useState(0);
// 接受两个参数 参1:函数,返回值作为缓存值 。参2:依赖数组
//依赖数组中的值发生变化,重新计算。否则直接取上次计算数据
// 如果没有传入依赖数组,记忆函数每次都会更新
const result = useCallback(() => {
console.log("222");
return counter;
}, [counter]);
const addOption = () => {
setData(data + 1);
};
const addCounter = () => {
setCount(counter + 1);
};
return (
<>
<div>{result()}</div>
<button onClick={addOption}>data - {data} </button>
<br />
<ChildFun addClick={addCounter}></ChildFun>
</>
);
}
function ChildFun(props) {
const { addClick } = props;
console.log("useCallback");
return (
<>
<div>child</div>
<button onClick={addClick}>add</button>
</>
);
}
useRef 获取Dom节点
为Dom 节点创建持久引用,有一个current 属性,可访问引用的节点,和react.createRef()功能相似
import { FC, useRef } from 'react'
//FC 函数式组件的一个 ts泛型
const Index: FC = () => {
const refInput: any = useRef()
function getInput() {
console.log(refInput?.current?.value)
}
return (
<>
<input ref={refInput} />
<button onClick={getInput}>获取input值</button>
</>
)}
export default Index
useLayoutEffect 副作用钩子 所有的dom变更之后,同步执行
同useEffect
useImperativeHandle 把ref 自定义暴露给父组件
用于父组件控制子组件dom
自定义Hook
import React, { useState, useEffect } from "react";
export default function ClassConponent(props) {
return <div>{useClick().toLocaleTimeString()}</div>;
}
// 自定义hook ,要use开头,
function useClick() {
const [date, setData] = useState(new Date());
useEffect(() => {
const timer = setInterval(() => {
setData(new Date());
}, 10000);
return () => clearInterval(timer);
}, []);
return date;
}
受控组件和非受控组件有什么区别
受控组件和非受控组件之间的区别在于它们如何管理和更新其状态。
l 受控组件是状态由 React 控制的组件。组件接收其当前值并通过 props 更新它。当值改变时它也会触发回调函数。这意味着该组件不存储其自己的内部状态。相反,父组件管理该值并将其传递给受控组件。
l 不受控制的组件使用 refs 或其他方法在内部管理自己的状态。它们独立存储和更新状态,不依赖 props 或回调。父组件对不受控组件的状态控制较少。
基于类的 React 组件和函数式 React 组件有什么区别?
基于类的组件和函数组件之间的主要区别在于它们的定义方式和使用的语法。
- 基于类的组件被定义为 ES6 类并扩展该类React.Component。他们使用该render方法返回定义组件输出的 JSX (JavaScript XML)。this.state类组件可以通过和访问组件生命周期方法和状态管理this.setState()。
- 函数组件被定义为简单的 JavaScript 函数。他们接受 props 作为参数并直接返回 JSX。功能组件无权访问生命周期方法或状态。然而,随着 React 16.8 中 React Hooks 的引入,功能组件现在可以管理状态并使用其他功能,例如上下文和效果。
组件的生命周期方法有哪些?
生命周期方法是一种挂钩组件生命周期不同阶段的方法,允许您在特定时间执行特定代码。
以下是主要生命周期方法的列表:
- constructor:这是创建组件时调用的第一个方法。它用于初始化状态和绑定事件处理程序。在函数组件中,可以将useState钩子用于类似的目的。
- render:该方法负责渲染 JSX 标记并返回要在屏幕上显示的内容。
- componentDidMount:组件在 DOM 中渲染后立即调用此方法。它通常用于初始化任务,例如 API 调用或设置事件侦听器。
- componentDidUpdate:当组件的 props 或 state 改变时调用此方法。它允许您执行副作用、根据更改更新组件或触发其他 API 调用。
- componentWillUnmount:在组件从 DOM 中删除之前调用此方法。它用于清理在 中设置的任何资源componentDidMount,例如删除事件侦听器或取消计时器
Redux 三大原则
- 单一事实来源:整个应用的状态存储在单个 store 中的对象/状态树里。单一状态树可以更容易地跟踪随时间的变化,并调试或检查应用程序
- 状态是只读的:改变状态的唯一方法是去触发一个动作。动作是描述变化的普通 JS 对象。就像 state 是数据的最小表示一样,该操作是对数据更改的最小表示。
- 使用纯函数进行更改:为了指定状态树如何通过操作进行转换,你需要纯函数。纯函数是那些返回值仅取决于其参数值的函数。
Redux 实现了哪种模式?
Redux 实现了Flux 模式,它是应用程序的可预测状态管理模式。它通过引入单向数据流和应用程序状态的集中存储来帮助管理应用程序的状态。
Mobx 实现哪种模式?
Mobx 实现了观察者模式,也称为发布-订阅模式。 是单向数据流
Redux和Mobx有什么区别?
Redux 是一种更简单、更有主见的状态管理库,它遵循严格的单向数据流,并提倡不变性。它需要更多的模板代码和显式更新,但与 React 的集成度很高。
Mobx 提供的 API 更灵活、更直观,模板代码更少。它允许你直接修改状态,并自动跟踪变化以获得更好的性能。在 Redux 和 Mobx 之间做出选择取决于您的具体需求和偏好
什么是 Props 透传?
Props 透传是指通过多层嵌套组件传递 props 的过程,即使某些中间组件不直接使用这些 props。这可能会导致代码结构复杂且繁琐。
什么是React上下文?
React Context 是一项功能,它提供了一种在组件树中传递数据的方法,而无需在每一层手动传递道具。它允许您创建一个全局状态,树中的任何组件都可以访问该状态,无论其位置如何。当您需要在多个未通过道具直接连接的组件之间共享数据时,上下文就非常有用。
React Context API 由三个主要部分组成:
createContext(创建上下文): 该函数用于创建新的上下文对象。
Context.Provider: 该组件用于为上下文提供值。它封装了需要访问该值的组件。
Context.Consumer 或 useContext 钩子: 该组件或钩子用于从上下文中获取值。它可以在上下文提供者的任何组件中使用。
通过使用 React Context,可以避免props 透传(通过多级组件传递props),并在更高层次上轻松管理状态,从而使代码更有条理、更高效。
如何有条件地渲染元素?
可以使用任何条件运算符,包括三元。
什么是React Fragment? <></>
从组件返回多个元素是 React 中的常见做法。片段允许形成子元素列表,而无需在 DOM 中创建不必要的节点。
什么是React调和?
调和是 React 的一种算法,用于区分一棵元素树和另一棵元素树,以确定需要替换的部分。 调和是我们过去所说的虚拟 DOM 背后的算法。其定义听起来是这样的:当您渲染 React 应用程序时,描述应用程序的元素树会在预留内存中生成。然后,这棵树就会被包含在呈现环境中,例如,在浏览器应用程序中,它会被转化为一组 DOM 操作。应用状态更新时,会生成新的元素树。新的树会与之前的树进行比较,以便准确计算和启用重新绘制更新后的应用程序所需的操作。
为什么使用map()时需要key?
这些键可帮助 React 确定哪些元素已被更改、添加或移除。必须指定这些键,这样 React 才能匹配 数组元素。选择键的最佳方法是使用一个能将列表项与其相邻项明确区分开来的字符串。通常,您会使用数据中的 ID 作为键。
如何在 Redux Thunk 中处理异步操作?
Thunk 是redux的异步处理中间件,不使用时,store.dispatch 只能传入对象,使用可以传入函数
要使用 Redux Thunk,需要将其作为中间件导入。Action创建者不应只返回一个对象,而应返回一个将dispatch 作为参数的函数。
创建自定义钩子的规则是什么?
- 钩子名称以 "use "开头。 2. 如有需要,请使用现有钩子。 3. 不要有条件地调用钩子。
- 将可重复使用的逻辑提取到自定义钩子中。 5. 自定义钩子必须是纯函数。
- 自定义钩子可以返回值或其他钩子。
- 以描述性的方式命名自定义钩子
React 事件机制
React基于浏览器事件机制实现了一套自己的事件机制,包括 :事件注册、事件合成、事件冒泡、事件触发等
React中的合成事件是什么?
合成事件是一个对象,它充当浏览器原生事件的跨浏览器包装器,它将不同浏览器的原生事件的行为组合到一个 API 中
- 兼容所有浏览器,更好的跨平台;
- 将事件统一存放在一个数组,统一管理其创建和销毁,避免频繁的新增与删除(垃圾回收)。
- 方便 react 统一管理事务机制。
react高阶组件
高阶组件是一个函数,能够接受一个组件并返回一个新的组件。
1)HOC的优缺点
优点∶ 逻辑复用、不影响被包裹组件的内部逻辑。
缺点∶hoc传递给被包裹组件的props容易和被包裹后的组件重名,进而被覆盖
2)适用场景
- 代码复用,逻辑抽象
- 渲染劫持
- State 抽象和更改
- Props 更改
3)具体应用例子
- 权限控制: 利用高阶组件的 条件渲染 特性可以对页面进行权限控制,权限控制一般分为两个维度:页面级别和 页面元素级别
- 组件渲染性能追踪: 借助父组件子组件生命周期规则捕获子组件的生命周期,可以方便的对某个组件的渲染时间进行记录
- 页面复用****
页面优化
1.页面渲染
1. css放前面,js放后面
2. 减少dom查询,多次使用的保存为变量
3. 减少dom操作,统一通过dom片段操作
4. 事件函数的节流和防抖
5. 图片懒加载
6. 尽早进行操作, domcontentload 与 load
2. vue项目优化:
· 代码层面优化
· webpack层面优化
· web层面优化
一、代码层面优化
1. Object.freeze(data);对于一些查询类的页面,调取接口回来后的数据可 不进行数据劫持
2. v-if和v-for不要在一起使用。v-if的条件通过函数来处理
3. v-for中加上key,对于虚拟dom树查找提高性能
4. computed和watch注意区分使用场景。前者是有缓存的。后者是监听到数据变化后的回调无缓存
5. created中发起请求,mounted钩子中代表页面dom加载完成可以进行dom操作。
6. 长列表性能优化,只渲染可视区域的列表
7. 长表格性能优化,通过canvers来绘制表格
8. 合理使用$nextTick去操作dom,适用于更新了数据马上就要操作dom的场景
9. 操作dom不要使用js原生的方式来操作。用vue提供的三种方式来操作 比如,ref、自定义指令el、事件中的话用e.target获取dom
10. 尽量不要在前端进行大量的数据处理
11. 合理使用keep-alive来缓存页面数据,跳过created,mounted钩子,他有自己特定的钩子activted等
12. 路由懒加载通过import配合箭头函数,还有其他的方式require
13. 组件懒加载,异步加载
14. 尽量少用float,可以用flex布局
15. 频繁切换的使用v-show,不频繁的使用v-if
16. 不要在模板中写过多的样式
17. 服务端渲染ssr,优化seo,与首屏白屏问题。
18. 通过addEventListenr添加的事件,需要自行销毁
19. 把组件中的css提取成单独的文件
20. 少使用闭包与递归,递归可做尾递归的优化
21. 使用字体图标或者svg来代替传统的Png等格式的图片
22. 在Js中避免“嵌套循环”和“死循环”
23. 尽可能的使用事件委托来处理事件的绑定,针对老项目jq
24. 使用day.js替代moment.js:day.js属于轻量的处理时间和日期的JavaScript库
二、webpack层面优化
1. 去除无用代码treeShaking.
2. babel编译es6到es5的时候,会有多余代码产生
3. 减小app.js的体积,提取公共代码
4. 减少vendor.js的体积,通过按需引入第三方库,或者有些资源可以通过script标签引入
5. 代码切割,有一些组件没必要都打包到一起。
6. 使用chunck
7. 使用SouceMap,来还原线上代码,更方便的去定位线上问题
8. 构建结果,通过可视化插件,进行分析
9. webpack对图片进行压缩等处理,
10. 图片可以使用webp,优雅降级处理
11. 编译优化
12. 模板预编译,使用vue-template-loader,把模板编译成渲染函数。
三、web层面优化
1. 浏览器缓存的使用
2. 开启gzip压缩
3. CDN的使用,减少路由转发的次数,就近访问资源
4. 使用chrome的性能分析工具,查找性能瓶颈
5. dns预解析
6. 静态资源的压缩与合并
7. 减少https请求
8. 异步加载defer,async
9. 静态资源和服务不要放在同一台机器上。多个域名去并行加载解析
3、HTML性能优化
1. 把js移到HTML后加载,因为JS会阻塞后面的页面的显示。
2. 减少iframe的使用,因为iframe会增加一条http请求。
3. 避免图片和iframe等的空Src。空Src会重新加载当前页面,影响速度和效率。
4. 减少不必要的嵌套,避免代码纵深层次过深,尽量扁平化,因为当浏览器遇到一个开始标签时就会寻找它的结束标签,直到匹配上才会显示它的内容,所以当嵌套很多时打开页面就会特别慢。
5. 代码要结构化、语义化,因为这样可以增加代码的可读性,比如说使用header、footer、section等标签
4. css性能优化
1. 尽量减少重绘重排的次数
2. 动画效果的实现,需开启独立的图层,减少重绘重排
1) 定位: position: absolute/relative
3. 多个小图标,使用CSS sprite来处理,减少请求次数
4. 多利用继承,多个子元素公用的样式,如果该样式能继承的话就写在父元素身上
相信经常可以看到,一个元素的样式,会存在许多被覆盖的情况,大多是子元素本身的样式覆盖了继承自父元素身上的样式。这样的话,页面可能就会出现被多次重绘的现象
5. 使用子选择器、后代选择器时,不要嵌套过深,加深解析的难度。
5 、JS性能优化
1. 合并压缩js
2. 不要反复去获取相同的dom节点,查找节点是非常消耗性能的
如何减少dom操作:
1、对dom查询做缓存,
2、多个操作尽量合并在一起执行
3、用innerHTML代替DOM操作,减少DOM操作次数,优化javascript性能
4、避免全局查询
1. 减少http请求的次数,节省网络资源
2. 少用全局变量
3. 慎用闭包,防止内存泄漏
4. 对更新频率不高的资源做缓存,减轻服务器压力的同时也能够提高响应速率****
5. 对于调用频率比较高的方法,通过函数节流,函数防抖等方法,降低回调频率。****
函数防抖:简单的说,当一个动作连续触发,则只执行最后一次
函数节流:简单的说,当一个动作连续触发,则限制该方法在一定时间内只能执行一次。减少执行的频率
6. 非核心代码异步加载,实现方法是,script标签中使用defer 、async实现延迟或异步加载****
defer是在html解析完之后才会执行,如果是多个,按照加载的顺序依次进行。
async是在加载完之后立即执行,如果是多个,执行顺序和加载顺序无关。
单页面(SPA)
SPA( single page application )仅在 Web 页面初始化时加载相应的 HTML、JavaScript 和 CSS。
一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转
而页面的变化是利用路由机制实现 HTML 内容的变换,避免页面的重新加载。
优点:
1)用户体验好,内容的改变不需要重新加载整个页面
2)减少了不必要的跳转和重复渲染,这样相对减轻了服务器的压力
3)前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理
缺点:
1)初次加载耗时多
2)不能使用浏览器的前进后退功能,由于单页应用在一个页面中显示所有的内容,所以,无法前进后退
3)不利于搜索引擎检索:由于所有的内容都在一个页面中动态替换,所以在SEO上有着天然的弱势
SSR (服务端渲染)
Server-Side Rendering 我们称其为SSR,意为服务端渲染指由服务侧完成页面的 HTML 结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程;
解决了以下两个问题
1.seo:搜索引擎优先爬取页面HTML结构,使用ssr时,服务端已经生成了和业务想关联的HTML,有利于seo
2.首屏呈现渲染:用户无需等待页面所有js加载完成就可以看到页面视图(压力来到了服务器,所以需要权衡哪些用服务端渲染,哪些交给客户端)
缺点
复杂度:整个项目的复杂度
性能会受到影响
服务器负载变大,相对于前后端分离务器只需要提供静态资源来说,服务器负载更大,所以要慎重使用
SPA首屏优化方式
原因:
· 网络延时问题
· 资源文件体积是否过大
· 资源是否重复发送请求去加载了
· 加载脚本的时候,渲染内容堵塞了
优化:
· 减小入口文件积 (路由懒加载)
· 静态资源本地缓存
· UI框架按需加载
· 图片资源的压缩
· 组件重复打包 (包抽离出来,放进公共依赖文件)
· 开启GZip压缩
· 使用SSR
SEO优化
· 服务端渲染SSR
· 预渲染
打包优化
· 压缩代码
· Tree Shaking(按需引入)/Scope Hoisting
· 使用cdn加载第三方模块
· 多线程打包happypack
· splitChunks抽离公共文件
· sourceMap优化(源代码-构建后代码映射)
虚拟 DOM 的优缺点
真实 DOM 的内存表示 减少对真实dom的操作次数,提升性能
优点:
· 保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM 操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM 至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;
· 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和 数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;
· 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。
缺点:
· 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化。
如何实现网页的懒加载?提供一个图片懒加载的原理。
-
页面加载时,将所有图片的src属性设置为一个占位图片或空字符串,而不是真实的图片地址。
-
使用Intersection Observer API来监听图片是否进入了浏览器视口(即用户可见的区域)。
-
当图片进入视口时,触发Intersection Observer的回调函数,将真实的图片地址赋给图片的src属性,从而实现图片的懒加载。
Vue2
beforeCreate:没有dom与data,获取不到methds方法
created获取不到dom ,有data (异步可获取dom :nextTick,setTimeout)
父子组件生命周期执行
加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
keep-alive 缓存组件(activeted,deactivated)
如果有keep-alive组件,第二次(N次)进入只会执行 activeted ,第一次执行前四个+activeted
组件传参
父子
1. props:子不能直接修改父的数据
2. This.$parent: 子能直接修改父的数据
3. provide与inject 依赖注入
4. vuex
子传父
1. This.$emit
2. Vuex
3. This.$refs
兄弟组件传参
1. 中央bus
2. Vuex
Slot
1. 具名插槽: name
2. 匿名插槽:
3. 作用域插槽:类型props传参
路由
1. 全局守卫: beforeEach,afterEach
2. 路由独享守卫:beforeEnter
3. 组件内的守卫:beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave
v-if和v-for
vue2: v-for > v-if
vue3: v-if > v-for