目录
一:前端页面样式
1.块标签和行标签
2.盒子模型和IE盒子模型
3.清除浮动
4.css动画
5.盒子垂直水平居中
6.css选择器及优先级
7.左侧宽度固定右侧自适应
8.0.5px的线
9.什么是BFC
10.flex:1是什么缩写
二:js
1.数据类型
2.闭包
3.ajax原理和优缺点
4.从输入url到页面展示的详细过程
5.原型和原型链区别
6.sessionStorage,localStorage和cookie的区别
9.节流和防抖
10.回流和重绘
11.instanceof 和 typeof的区别
12.数组的增删改查
13.http和https的区别,常见http状态码
14.get和post区别
15.前端跨域解决办法
16.内存泄漏的几种情况及解决办法
17.promiss与async和await的区别
18.webstorage怎么赋值(转化成字符串赋值)
19.post跨域怎么处理
20.说一下this关键字
三:vue
1.什么是MVVM
2.VUE的响应式原理
3.v-if和v-show的区别
4.组件之间传值
5.vue生命周期及各阶段情况
6.webpack打包原理
7.keep-alive的生命周期
8.父子组件created和mounted加载顺序
9.wacth和computer区别
10.vue的data为什么必须是函数
11.keep-alive的介绍与应用
12.vue路由有几种模式
13.vue懒加载
四:es6
1.let和const
2.什么是promiss,promiss有哪几个方法
3.箭头函数
五:算法
1.冒泡排序
2.快速排序和二分查找
3.数组的翻转(非reverse())
4.数组由小到大进行排序
5.求数组最大值:Math.max.apply(null,arr);
6.数组去重
7.判断一个字符串中出现次数最多的字符,统计这个次数
8.apply()/call()求数组最值
9.获取浏览器URL中查询字符串中的参数
10.2个对象排序{age:10,name:'lili'}
11.设计模型
12.JavaScript将具有父子关系的原始数据格式化成树形结构数据(id,pid)
=============================这是 分割线=======================================
一:前端页面样式
1.块标签和行标签
块标签包括:p、div、ul、ol、li、dl、dt、dd、h1~h6、form、table、td、thread、tr、
行标签包括:a、abbr、b(字体加粗)、br、em、input、select、span、strong、sub、textarea、
行内元素设置width无效,height无效(可以设置line-height),margin上下无效,padding上下无效。
2.盒子模型和IE盒子模型
box-sizing属性可以指定盒子模型种类,content-box指定盒子模型为W3C(标准盒模型),border-box为IE盒子模型(怪异盒模型)。
3.清除浮动
3.1伪类元素(clearfix)
HTML结构如下,为了惯例相符,在.topDiv的div上再添加一个clearfix类:
<div class="topDiv clearfix">
<div class="textDiv">...</div>
<div class="floatDiv">float left</div>
</div>
<div class="bottomDiv">...</div>
css样式
// 省略基本的样式
// 区别在这里
.clearfix:after {
content: '';
height: 0;
display: block;
clear: both;
}
3.2在浮动元素后面加一个空div设置clear:both
<div class="topDiv clearfix">
<div class="textDiv">...</div>
<div class="floatDiv">float left</div>
<div style=”clear:both”></div>
</div>
3.3给父元素设置overflow:auto
4.css动画
4.1.transform:有4个方法,translate(平移),rotate(旋转),scale(缩放),skew(斜切)
tranform:translate(10px,10px); //平移
transform:rotate(90deg); //旋转
transform:scale(1.2); //缩放:参数<1缩小,>1放大
transform:skew(90deg,10deg); //斜切
4.2.transition有4个值(默认是前2个值):property(指定css属性的name)、duration(动画持续时间)、timing-function(切换动画的速度)、动画执行前的延迟时间。
transition:width|height|opacity|all 2s ease-in 2s;
3.perspective:90px; //景深,值越小,表示我们离物体越近,一般使用500-800
5.盒子垂直水平居中
5.1已知盒子宽高
方案一:设置父元素为相对定位,给子元素设置绝对定位,top: 0; right: 0; bottom: 0; left: 0; margin: auto;
<style>
#father </span><span style="background-color: #f5f5f5; color: #000000;">{</span><span style="background-color: #f5f5f5; color: #ff0000;">
width</span><span style="background-color: #f5f5f5; color: #000000;">:</span><span style="background-color: #f5f5f5; color: #0000ff;"> 500px</span><span style="background-color: #f5f5f5; color: #000000;">;</span><span style="background-color: #f5f5f5; color: #ff0000;">
height</span><span style="background-color: #f5f5f5; color: #000000;">:</span><span style="background-color: #f5f5f5; color: #0000ff;"> 300px</span><span style="background-color: #f5f5f5; color: #000000;">;</span><span style="background-color: #f5f5f5; color: #ff0000;">
background-color</span><span style="background-color: #f5f5f5; color: #000000;">:</span><span style="background-color: #f5f5f5; color: #0000ff;"> skyblue</span><span style="background-color: #f5f5f5; color: #000000;">;</span><span style="background-color: #f5f5f5; color: #ff0000;">
position</span><span style="background-color: #f5f5f5; color: #000000;">:</span><span style="background-color: #f5f5f5; color: #0000ff;"> relative</span><span style="background-color: #f5f5f5; color: #000000;">;</span>
}
#son {
width: 100px;
height: 100px;
background-color: green;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin: auto;
}
</style>
<div id="father">
<div id="son">我是块级元素</div>
</div>
方案二:设置父元素为相对定位,给子元素设置绝对定位,left: 50%; top: 50%; margin-left: --元素宽度的一半px; margin-top: --元素高度的一半px; <style> #father { width: 500px; height: 300px; background-color: skyblue; position: relative; } #son { width: 100px; height: 100px; background-color: green; position: absolute; left: 50%; top: 50%; margin-left: -50px; margin-top: -50px; } </style>
5.2未知宽高
方案一:使用定位属性
设置父元素为相对定位,给子元素设置绝对定位,left: 50%; top: 50%; transform: translateX(-50%) translateY(-50%);// transform: translate(-50%,-50%)
<style>
#father {
width: 500px;
height: 300px;
background-color: skyblue;
position: relative;
}
#son {
background-color: green;
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%); // transform: translate(-50%,-50%)
}
</style>
方案二:使用flex布局实现 设置父元素为flex定位,justify-content: center; align-items: center; <style> #father { width: 500px; height: 300px; background-color: skyblue; display: flex; justify-content: center; align-items: center; } #son { background-color: green; } </style>
6.css选择器及优先级
!important > 行内样式>ID选择器 > 类选择器 > 标签 > 通配符 > 继承 > 浏览器默认属性
7.左侧宽度固定右侧自适应
/*左固定列*/ .fixedColumn{ width: 40px; height: 100%; background-color: red; float: left; /*position: absolute; 注释和float:left选一即可 left: 0;*/ } /*右自适应列*/ .flexibleColumn{ height: 100%; background-color: blue; margin-left: 40px;
} <!-- 左侧固定列 --> <div class="fixedColumn"></div> <!-- 右侧自适应宽度列 --> <div class="flexibleColumn"></div>
~左中右,左右固定宽度,中间自适应(与左定宽,右自适应就多一个右float:right) <!-- 左侧固定列 --> <div class="fixedColumn"></div> <!-- 右侧固定列 --> <div class="fixedColumn" style=”float:right”></div> <!-- 右侧自适应宽度列 --> <div class="flexibleColumn"></div>
float + overflow:hidden
利用overflow:hidden形成BFC,因为BFC不会与float box重叠。
.left {
float: left;
width: 200px;
height: 100%;
background-color: red;
}
.right {
overflow:hidden;
background-color: blue;
}
CSS3 float + calc
.left {
float: left;
width: 200px;
height: 100%;
background-color: red;
}
.right {
float: left;
width: calc(100% - 200px);
height: 100%;
background-color: blue;
}
弹性布局
.parent {
display: flex;
}
.left {
width: 200px;
height: 100%;
background-color: red;
}
.right {
display: flex;
flex: 1;
height: 100%;
background-color: blue;
}
8.0.5px的线
.hr.scale-half {
height: 1px;
transform: scaleY(0.5);
transform-origin: 50% 100%;}
方法2:
.hr.gradient {
height: 1px;
background: linear-gradient(0deg, #fff, #000);}
9.什么是BFC
BFC(Block formatting context)直译为"块级格式化上下文"。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,并且与这个区域外部毫不相干。。
如何创建BFC
-
- 1、float的值不是none。
- 2、position的值不是static或者relative。
- 3、display的值是inline-block、table-cell、flex、table-caption或者inline-flex
- 4、overflow的值不是visible
1.两个兄弟盒子A在上、B在下,A margin-bottom:20px,B margin-top:30px A和B的最终距离是多少?
30px 因为同属于一个BFC,margin会重叠。可以给B套一个盒子并开启BFC就变成50px了。
- A和B是父子关系A在外、B在内,A margin-top:30px ,B margin-top: 20px,A和B之间的距离是多少?
相同的原因,两个盒子的margin会重叠,导致子盒子会顶在父盒子边框。所以距离是0px。
3.A是绝对定位,B是相对定位,A和B是兄弟关系,A和B分别相对什么位置移动,A 和 B 是否脱离文档流
A相对有定位的父盒子或者根元素移动,脱离文档流;B相对自己位置移动,不脱离文档流。
10.flex:1是什么缩写
flex:1即为flex-grow:1,经常用作自适应布局,将父容器的display:flex,侧边栏大小固定后,将内容区flex:1,内容区则会自动放大占满剩余空间。
flex-grow:定义项目的的放大比例;
- 默认为0,即 即使存在剩余空间,也不会放大;
- 所有项目的flex-grow为1:等分剩余空间(自动放大占位);
- flex-grow为n的项目,占据的空间(放大的比例)是flex-grow为1的n倍。
11.css3和html5新增属性
https://blog.csdn.net/m0_37631322/article/details/81945113
二:js
1.数据类型
基本数据类型:boolean,number,string,undefined,null 保存在栈内存中。
引用数据类型:Object,Array,Function,Date,regexp 保存在堆内存中的对象。与其他语言的不同是,你不可以直接访问堆内存空间中的位置和操作堆内存空间。只能操作对象在栈内存中的引用地址。
2.闭包
什么是闭包
简单来说,闭包是指可以访问另一个函数作用域变量的函数,一般是定义在外层函数中的内层函数。
为什么需要闭包呢
局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。
特点
占用更多内存
不容易被释放
何时使用
既想反复使用,又想避免全局污染
如何使用
1.定义外层函数,封装被保护的局部变量。 2.定义内层函数,执行对外部函数变量的操作。 3.外层函数返回内层函数的对象,并且外层函数被调用,结果保存在一个全局的变量中。
函数生命周期
直接上图,点击图片放大查看。要记住函数对象、作用域链对象、执行环境(EC)和活动对象(AO)这几个东西都啥时候出现,啥时候消失。
3.ajax原理和优缺点
1:原理:
Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用javascript来操作DOM而更新页面。
2:ajax的优点:
1、最大的一点是页面无刷新,在页面内与服务器通信,给用户的体验非常好。
2、使用异步方式与服务器通信,不需要打断用户的操作,具有更加迅速的响应能力。
3、可以把以前一些服务器负担的工作转嫁到客户端,利用客户端闲置的能力来处理,减轻服务器和带宽的负担,节约空间和宽带租用成本。并且减轻服务器的负担,ajax的原则是“按需取数据”,可以最大程度的减少冗余请求,和响应对服务器造成的负担。
4、基于标准化的并被广泛支持的技术,不需要下载插件或者小程序。
3:缺点:
1:破坏浏览器后退按钮正常行为
2:对搜索引擎不是多好
4.从输入url到页面展示的详细过程
5.原型和原型链区别
prototype是函数才有的属性
__proto__是每个对象都有的属性
所有对象都会从它的原型上继承一个 constructor 属性
obj.__proto__ === obj.constructor.prototype
总结:
1.对象有属性__proto__,指向该对象的构造函数的原型对象。
2.方法除了有属性__proto__,还有属性prototype,prototype指向该方法的原型对象。
大多数情况下,__proto__可以理解为“构造器的原型”,即__proto__===constructor.prototype,但是通过 Object.create()创建的对象有可能不是, Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。
6.sessionStorage,localStorage和cookie的区别
共同点:都是保存在浏览器端、且同源的
区别:
1、cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递,而sessionStorage和localStorage不会自动把数据发送给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下
2、存储大小限制也不同,cookie数据不能超过4K,同时因为每次http请求都会携带cookie、所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大
3、数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭之前有效;localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie:只在设置的cookie过期时间之前有效,即使窗口关闭或浏览器关闭
4、作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localstorage在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的
5、web Storage支持事件通知机制,可以将数据更新的通知发送给监听者
6、web Storage的api接口使用更方便
9.节流和防抖
在进行窗口的resize、scroll,输入框内容校验等操作时,如果事件处理函数调用的频率无限制,会加重浏览器的负担,导致用户体验非常糟糕。此时我们可以采用debounce(防抖)和throttle(节流)的方式来减少调用频率,同时又不影响实际效果
函数防抖:将几次操作合并为一此操作进行。原理是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,就会取消之前的计时器而重新设置。这样一来,只有最后一次操作能被触发。
函数节流:使得一定时间内只触发一次函数。原理是通过判断是否到达一定时间来触发函数。
区别: 函数节流不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 比如在页面的无限加载场景下,我们需要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操作时才去请求数据。这样的场景,就适合用节流技术来实现。
防抖就是:对于段时间内连续触发的事件,防抖的含义就是让某个时间段内,事件处理函数只执行一次。
函数节流(throttle):当持续触发事件时,保证一定时间段内只调用一次事件处理函数。
函数节流主要有两种实现方法:时间戳和定时器
使用场景:页面中的返回顶部,当在滚动条位置,点击键盘上下键,会多次执行
防抖:在第一次触发事件时,不立即执行函数,而是给出一个期限值,比如200ms。
如果在200ms内没有再次触发滚动事件,那么就执行函数
如果再200ms内再次触发滚动事件,那么当前计时取消,重新开始计时。
10.回流和重绘
1、 重绘:元素样式的改变(但宽高、大小、位置等不变)
如:outline、visibility、color、background-color等
只改变自身样式,不会影响到其他元素
2、 回流:元素的大小或者位置发生改变(当页面布局和几何信息发生改变的时候),触发了重新布局导致渲染树重新计算布局和渲染
如添加或删除可见的DOM元素;元素的位置发生变化;元素的尺寸发生变化、内容发生变化(如文本变化或图片被另一个不同尺寸的图片所代替);页面一开始渲染的时候(无法避免);
因为回流是根据视口大小来计算元素的位置和大小的,所以浏览器窗口尺寸变化也会引起回流
注意:回流一定会触发重绘,而重绘不一定会回流
————————————————
版权声明:本文为CSDN博主「独坐空山后」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/pig_is_duck/article/details/105903741
11.instanceof 和 typeof的区别
typeof用于判断数据类型,返回值为6个字符串,分别为string、Boolean、number、function、object、undefined。对于 Array,Null 等特殊对象使用 typeof 一律返回 object,这正是 typeof 的局限性
instanceof 用于判断一个变量是否某个对象的实例,如 var a=new Array();alert(a instanceof Array); 会返回 true,同时 alert(a instanceof Object) 也会返回 true;这是因为 Array 是 object 的子类
12.数组的增删改查
下面将数组的方法分为5类(官方文档中可查)
A – 给数组添加元素(增):push(), unshift(),splice()
B – 从数组中删除元素(删):pop(), shift(),splice()
C – 修改数组中的元素(改):splice(),reverse(),sort()
D --从已有数组中返回选定的数组(查):slice()
E – 不会改变元素数组的内容的函数:concat(),slice()
————————————————
版权声明:本文为CSDN博主「銭佳」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_41895038/article/details/105040532
13.http和https的区别,常见http状态码
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
HTTPS和HTTP的区别主要如下:
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
- 200 - 请求成功
- 301 - 资源(网页等)被永久转移到其它URL
- 302 - 资源被临时移动,客户端应继续使用
- 304-如果客户端发送了一个带条件的GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个304状态码。简单的表达就是:服务端已经执行了GET,但文件未变化。
- 404 - 请求的资源(网页等)不存在
- 500 - 内部服务器错误
- 502 Bad Gateway:作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
- 504 Gateway Time-out:作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。
HTTP状态码分类 |
|
分类 |
分类描述 |
1** |
信息,服务器收到请求,需要请求者继续执行操作 |
2** |
成功,操作被成功接收并处理 |
3** |
重定向,需要进一步的操作以完成请求 |
4** |
客户端错误,请求包含语法错误或无法完成请求 |
5** |
服务器错误,服务器在处理请求的过程中发生了错误 |
14.get和post区别
- get是从服务器上获取数据,post是向服务器传送数据。
- 传送方式:get通过地址栏传输,post通过报文传输
- 传送长度:get 参数长度有限制(受限于url长度),而post无限制。
- GET是表单提交的默认方法
- Get产生一个TCP数据包,post产生2个TCP数据包,Firefox就只发送一次。
- Get在浏览器回退时是无害的,而post会再次提交请求。
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
15.前端跨域解决办法
参考链接:https://www.jianshu.com/p/451e575a3a8a
16.内存泄漏的几种情况及解决办法
https://www.jianshu.com/p/9af7fa367784
系统进程不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)
当内存占用越来越高,轻则影响系统性能,重则导致进程崩溃
引起内存泄漏的原因
- 意外的全局变量
由于 js 对未声明变量的处理方式是在全局对象上创建该变量的引用。如果在浏览器中,全局对象就是 window 对象。变量在窗口关闭或重新刷新页面之前都不会被释放,如果未声明的变量缓存大量的数据,就会导致内存泄露。
// 未声明变量
function fn() {
a = 'global variable'}fn()
//使用 this 创建的变量(this 的指向是 window)。
function fn() {
this.a = 'global variable'}fn()
解决方法:
避免创建全局变量
使用严格模式,在 JavaScript 文件头部或者函数的顶部加上 use strict。
- 闭包引起的内存泄漏
原因:闭包可以读取函数内部的变量,然后让这些变量始终保存在内存中。如果在使用结束后没有将局部变量清除,就可能导致内存泄露。
function fn () {
var a = "I'm a";
return function () {
console.log(a);
};}
解决:将事件处理函数定义在外部,解除闭包,或者在定义事件处理函数的外部函数中。
比如:在循环中的函数表达式,能复用最好放到循环外面。
// badfor (var k = 0; k < 10; k++) {
var t = function (a) {
// 创建了10次 函数对象。
console.log(a)
}
t(k)}
// goodfunction t(a) {
console.log(a)}for (var k = 0; k < 10; k++) {
t(k)}
t = null
3. 没有清理的 DOM 元素引用
原因:虽然别的地方删除了,但是对象中还存在对 dom 的引用。
// 在对象中引用DOMvar elements = {
btn: document.getElementById('btn'),}function doSomeThing() {
elements.btn.click()}function removeBtn() {
// 将body中的btn移除, 也就是移除 DOM树中的btn
document.body.removeChild(document.getElementById('button'))
// 但是此时全局变量elements还是保留了对btn的引用, btn还是存在于内存中,不能被GC回收}
解决方法:手动删除elements.btn = null。
4. 被遗忘的定时器或者回调
定时器中有 dom 的引用,即使 dom 删除了,但是定时器还在,所以内存中还是有这个 dom。
// 定时器var serverData = loadData()setInterval(function () {
var renderer = document.getElementById('renderer')
if (renderer) {
renderer.innerHTML = JSON.stringify(serverData)
}}, 5000)
// 观察者模式var btn = document.getElementById('btn')function onClick(element) {
element.innerHTMl = "I'm innerHTML"}
btn.addEventListener('click', onClick)
解决方法:
手动删除定时器和 dom。
removeEventListener 移除事件监听
vue 中容易出现内存泄露的几种情况
在 Vue SPA 开发应用,那么就更要当心内存泄漏的问题。因为在 SPA 的设计中,用户使用它时是不需要刷新浏览器的,所以 JavaScript 应用需要自行清理组件来确保垃圾回收以预期的方式生效。因此开发过程中,你需要时刻警惕内存泄漏的问题。
- 全局变量造成的内存泄露
声明的全局变量在切换页面的时候没有清空
<template>
<div id="home">这里是首页</div></template>
<script>
export default {
mounted() {
window.test = {
// 此处在全局window对象中引用了本页面的dom对象
name: 'home',
node: document.getElementById('home'),
}
},
}</script>
解决方案:在页面卸载的时候顺便处理掉该引用。
destroyed () {
window.test = null // 页面卸载的时候解除引用
}
- 监听在 window/body 等事件没有解绑
特别注意 window.addEventListener 之类的时间监听
<template>
<div id="home">这里是首页</div></template><script>
export default {
mounted () {
window.addEventListener('resize', this.func) // window对象引用了home页面的方法
}
}</script>
解决方法:在页面销毁的时候,顺便解除引用,释放内存
mounted () {
window.addEventListener('resize', this.func)},
beforeDestroy () {
window.removeEventListener('resize', this.func)}
3. 绑在 EventBus 的事件没有解绑
举个例子
<template>
<div id="home">这里是首页</div></template>
<script>
export default {
mounted () {
this.$EventBus.$on('homeTask', res => this.func(res))
}
}</script>
解决方法:在页面卸载的时候也可以考虑解除引用
mounted () {
this.$EventBus.$on('homeTask', res => this.func(res))},destroyed () {
this.$EventBus.$off()}
4. Echarts
每一个图例在没有数据的时候它会创建一个定时器去渲染气泡,页面切换后,echarts 图例是销毁了,但是这个 echarts 的实例还在内存当中,同时它的气泡渲染定时器还在运行。这就导致 Echarts 占用 CPU 高,导致浏览器卡顿,当数据量比较大时甚至浏览器崩溃。
解决方法:加一个 beforeDestroy()方法释放该页面的 chart 资源,我也试过使用 dispose()方法,但是 dispose 销毁这个图例,图例是不存在了,但图例的 resize()方法会启动,则会报没有 resize 这个方法,而 clear()方法则是清空图例数据,不影响图例的 resize,而且能够释放内存,切换的时候就很顺畅了。
beforeDestroy () {
this.chart.clear()}
5. v-if 指令产生的内存泄露
v-if 绑定到 false 的值,但是实际上 dom 元素在隐藏的时候没有被真实的释放掉。
比如下面的示例中,我们加载了一个带有非常多选项的选择框,然后我们用到了一个显示/隐藏按钮,通过一个 v-if 指令从虚拟 DOM 中添加或移除它。这个示例的问题在于这个 v-if 指令会从 DOM 中移除父级元素,但是我们并没有清除由 Choices.js 新添加的 DOM 片段,从而导致了内存泄漏。
<div id="app">
<button v-if="showChoices" @click="hide">Hide</button>
<button v-if="!showChoices" @click="show">Show</button>
<div v-if="showChoices">
<select id="choices-single-default"></select>
</div></div><script>
export default {
data() {
return {
showChoices: true,
}
},
mounted: function () {
this.initializeChoices()
},
methods: {
initializeChoices: function () {
let list = []
// 我们来为选择框载入很多选项,这样的话它会占用大量的内存
for (let i = 0; i < 1000; i++) {
list.push({
label: 'Item ' + i,
value: i,
})
}
new Choices('#choices-single-default', {
searchEnabled: true,
removeItemButton: true,
choices: list,
})
},
show: function () {
this.showChoices = true
this.$nextTick(() => {
this.initializeChoices()
})
},
hide: function () {
this.showChoices = false
},
},
}</script>
在上述的示例中,我们可以用 hide() 方法在将选择框从 DOM 中移除之前做一些清理工作,来解决内存泄露问题。为了做到这一点,我们会在 Vue 实例的数据对象中保留一个属性,并会使用 Choices API 中的 destroy() 方法将其清除。
<div id="app">
<button v-if="showChoices" @click="hide">Hide</button>
<button v-if="!showChoices" @click="show">Show</button>
<div v-if="showChoices">
<select id="choices-single-default"></select>
</div></div>
<script>
export default {
data() {
return {
showChoices: true,
choicesSelect: null
}
},
mounted: function () {
this.initializeChoices()
},
methods: {
initializeChoices: function () {
let list = []
for (let i = 0; i < 1000; i++) {
list.push({
label: 'Item ' + i,
value: i,
})
}
// 在我们的 Vue 实例的数据对象中设置一个 choicesSelect
的引用
this.choicesSelect = new Choices("#choices-single-default", {
searchEnabled: true,
removeItemButton: true,
choices: list,
})
},
show: function () {
this.showChoices = true
this.$nextTick(() => {
this.initializeChoices()
})
},
hide: function () {
// 现在我们可以让 Choices 使用这个引用,从 DOM 中移除这些元素之前进行清理工作
this.choicesSelect.destroy()
this.showChoices = false
},
},
}</script>
ES6 防止内存泄漏
前面说过,及时清除引用非常重要。但是,你不可能记得那么多,有时候一疏忽就忘了,所以才有那么多内存泄漏。
ES6 考虑到这点,推出了两种新的数据结构:weakset 和 weakmap 。他们对值的引用都是不计入垃圾回收机制的,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存。
const wm = new WeakMap()const element = document.getElementById('example')
vm.set(element, 'something')
vm.get(element)
上面代码中,先新建一个 Weakmap 实例。然后,将一个 DOM 节点作为键名存入该实例,并将一些附加信息作为键值,一起存放在 WeakMap 里面。这时,WeakMap 里面对 element 的引用就是弱引用,不会被计入垃圾回收机制。
注册监听事件的 listener 对象很适合用 WeakMap 来实现。
// 代码1
ele.addEventListener('click', handler, false)
// 代码2const listener = new WeakMap()
listener.set(ele, handler)
ele.addEventListener('click', listener.get(ele), false)
代码 2 比起代码 1 的好处是:由于监听函数是放在 WeakMap 里面,一旦 dom 对象 ele 消失,与它绑定的监听函数 handler 也会自动消失。
参考链接:https://www.jianshu.com/p/9af7fa367784
17.深拷贝和浅拷贝及怎么实现
浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址,
深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,
深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。
假设B复制了A,修改A的时候,看B是否发生变化:
如果B跟着也变了,说明是浅拷贝,拿人手短!(修改堆内存中的同一个值)
如果B没有改变,说明是深拷贝,自食其力!(修改堆内存中的不同的值)
https://www.cnblogs.com/williamjie/p/11192895.html
https://juejin.cn/post/6844903493925371917 深拷贝浅拷贝详细讲解(掘金)
浅拷贝只复制一层对象的属性,并不包括对象里面的为引用类型的数据。
怎么进行深拷贝呢?
思路就是递归调用刚刚的浅拷贝,把所有属于对象的属性类型都遍历赋给另一个对象即可。
4-4.深拷贝实现步骤
1. 如果传入的对象不存在,就返回null;如果是特殊对象,就new一个特殊对象。
2. 创建一个对象objClone,来保存克隆的对象。
3. 然后遍历对象,如果是基础数据,就直接放入objClone
4. 如果是对象,就递归。
深浅拷贝如何实现
https://www.cnblogs.com/echolun/p/7889848.html
http://caibaojian.com/javascript-object-clone.html
2、浅拷贝的实现 2.1、简单的引用复制 function shallowClone(copyObj) { var obj = {}; for ( var i in copyObj) { obj[i] = copyObj[i]; } return obj; } var x = { a: 1, b: { f: { g: 1 } }, c: [ 1, 2, 3 ] }; var y = shallowClone(x); console.log(y.b.f === x.b.f); // true 2.2、Object.assign() Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。 var x = { a: 1, b: { f: { g: 1 } }, c: [ 1, 2, 3 ] }; var y = Object.assign({}, x); console.log(y.b.f === x.b.f); // true 3、深拷贝的实现 3.1、Array的slice和concat方法 Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。之所以把它放在深拷贝里,是因为它看起来像是深拷贝。而实际上它是浅拷贝。原数组的元素会按照下述规则拷贝: 如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。 对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。 如果向两个数组任一中添加了新元素,则另一个不会受到影响。例子如下: var array = [1,2,3]; var array_shallow = array; var array_concat = array.concat(); var array_slice = array.slice(0); console.log(array === array_shallow); //true console.log(array === array_slice); //false,“看起来”像深拷贝 console.log(array === array_concat); //false,“看起来”像深拷贝 可以看出,concat和slice返回的不同的数组实例,这与直接的引用复制是不同的。而从另一个例子可以看出Array的concat和slice并不是真正的深复制,数组中的对象元素(Object,Array等)只是复制了引用。如下: var array = [1, [1,2,3], {name:"array"}]; var array_concat = array.concat(); var array_slice = array.slice(0); array_concat[1][0] = 5; //改变array_concat中数组元素的值 console.log(array[1]); //[5,2,3] console.log(array_slice[1]); //[5,2,3] array_slice[2].name = "array_slice"; //改变array_slice中对象元素的值 console.log(array[2].name); //array_slice console.log(array_concat[2].name); //array_slice 3.2、JSON对象的parse和stringify JSON对象是ES5中引入的新的类型(支持的浏览器为IE8+),JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,借助这两个方法,也可以实现对象的深拷贝。 //例1 var source = { name:"source", child:{ name:"child" } } var target = JSON.parse(JSON.stringify(source)); target.name = "target"; //改变target的name属性 console.log(source.name); //source console.log(target.name); //target target.child.name = "target child"; //改变target的child console.log(source.child.name); //child console.log(target.child.name); //target child //例2 var source = { name:function(){console.log(1);}, child:{ name:"child" } } var target = JSON.parse(JSON.stringify(source)); console.log(target.name); //undefined //例3 var source = { name:function(){console.log(1);}, child:new RegExp("e") } var target = JSON.parse(JSON.stringify(source)); console.log(target.name); //undefined console.log(target.child); //Object {} 这种方法使用较为简单,可以满足基本的深拷贝需求,而且能够处理JSON格式能表示的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)。还有一点不好的地方是它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。同时如果对象中存在循环引用的情况也无法正确处理。 4、jQuery.extend()方法源码实现 jQuery的源码 - src/core.js #L121源码及分析如下: jQuery.extend = jQuery.fn.extend = function() { //给jQuery对象和jQuery原型对象都添加了extend扩展方法 var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, i = 1, length = arguments.length, deep = false; //以上其中的变量:options是一个缓存变量,用来缓存arguments[i],name是用来接收将要被扩展对象的key,src改变之前target对象上每个key对应的value。 //copy传入对象上每个key对应的value,copyIsArray判定copy是否为一个数组,clone深拷贝中用来临时存对象或数组的src。// 处理深拷贝的情况
if (typeof target === "boolean") {
deep = target;
target = arguments[1] || {};
//跳过布尔值和目标
i++;
}// 控制当target不是object或者function的情况
if (typeof target !== "object" && !jQuery.isFunction(target)) {
target = {};
}// 当参数列表长度等于i的时候,扩展jQuery对象自身。
if (length === i) {
target = this; --i;
}
for (; i < length; i++) {
if ((options = arguments[i]) != null) {
// 扩展基础对象
for (name in options) {
src = target[name];
copy = options[name];</span><span style="color: #008000;">//</span><span style="color: #008000;"> 防止永无止境的循环,这里举个例子,如var i = {};i.a = i;$.extend(true,{},i);如果没有这个判断变成死循环了</span> <span style="color: #0000ff;">if</span> (target ===<span style="color: #000000;"> copy) { </span><span style="color: #0000ff;">continue</span><span style="color: #000000;">; } </span><span style="color: #0000ff;">if</span> (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray =<span style="color: #000000;"> jQuery.isArray(copy)))) { </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> (copyIsArray) { copyIsArray </span>= <span style="color: #0000ff;">false</span><span style="color: #000000;">; clone </span>= src && jQuery.isArray(src) ? src: []; <span style="color: #008000;">//</span><span style="color: #008000;"> 如果src存在且是数组的话就让clone副本等于src否则等于空数组。</span> } <span style="color: #0000ff;">else</span><span style="color: #000000;"> { clone </span>= src && jQuery.isPlainObject(src) ? src: {}; <span style="color: #008000;">//</span><span style="color: #008000;"> 如果src存在且是对象的话就让clone副本等于src否则等于空数组。</span>
}
// 递归拷贝
target[name] = jQuery.extend(deep, clone, copy);
} else if (copy !== undefined) {
target[name] = copy; // 若原对象存在name属性,则直接覆盖掉;若不存在,则创建新的属性。
}
}
}
}
// 返回修改的对象
return target;
};
jQuery的extend方法使用基本的递归思路实现了浅拷贝和深拷贝,但是这个方法也无法处理源对象内部循环引用,例如:
var a = {"name":"aaa"};
var b = {"name":"bbb"};
a.child = b;
b.parent = a;
$.extend(true,{},a);//直接报了栈溢出。Uncaught RangeError: Maximum call stack size exceeded
5、自己动手实现一个拷贝方法
var $ = (function () {
'use strict';
var types = 'Array Object String Date RegExp Function Boolean Number Null Undefined'.split(' ');
function type () {
return Object.prototype.toString.call(this).slice(8, -1);
}
for (var i = types.length; i--;) {
$['is' + types[i]] = (function (self) {
return function (elem) {
return type.call(elem) === self;
};
})(types[i]);
}
return $;
})();//类型判断
function copy (obj,deep) {
if (obj === null || (typeof obj !== "object" && !$.isFunction(obj))) {
return obj;
}
if ($.isFunction(obj)) {
return new Function("return " + obj.toString())();
}
else {
var name, target = $.isArray(obj) ? [] : {}, value;
for (name in obj) {
value = obj[name];
if (value === obj) {
continue;
}
if (deep && ($.isArray(value) || $.isObject(value))) {
target[name] = copy(value,deep);
}
else {
target[name] = value;
}
}
return target;
}
}
深拷贝的三种实现方式是什么:https://www.php.cn/faq/465102.html
1、递归递归去复制所有层级属性;
2、用JSON对象的parse和stringify实现;
3、借用JQ的extend方法。
function deepClone(obj){objClone[key] = obj[key];let objClone </span>= Array.isArray(obj)?[]:{}; <br /> <span style="color: #0000ff;">if</span>(obj && <span style="color: #0000ff;">typeof</span> obj==="object"){ <br /> <br /><span style="color: #0000ff;"> for</span>(key <span style="color: #0000ff;">in</span> obj){ <br /> <br /><span style="color: #0000ff;"> if</span>(obj.hasOwnProperty(key)){ <span style="color: #008000;">//</span><span style="color: #008000;">判断ojb子元素是否为对象,如果是,递归复制</span> <span style="color: #0000ff;">if</span>(obj[key]&&<span style="color: #0000ff;">typeof</span> obj[key] ==="object"<span style="color: #000000;">){ objClone[key] </span>=<span style="color: #000000;"> deepClone(obj[key]); }</span><span style="color: #0000ff;">else</span>{ <span style="color: #008000;">//</span><span style="color: #008000;">如果不是,简单复制</span>
} } } } </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> objClone;
}
let a=[1,2,3,4],
b</span>=<span style="color: #000000;">deepClone(a);
a[0]=2;
console.log(a,b);
18.promiss与async和await的区别
19.webstorage怎么赋值(转化成字符串赋值)
localStorage存储数组以及取数组方法。
var weekArray = ['周一'、'周二'、'周三'、'周四'、'周五']
存:localStorage.setItem('weekDay',JSON.stringify(weekArray));
取: weekArray = JSON.parse(localStorage.getItem('weekDay'));
120.post跨域怎么处理
三:vue
1.什么是MVVM
MVVM是Model-View-ViewModel的缩写。MVVM是一种设计思想。Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
2.VUE的响应式原理
Vue 的响应式原理是核心是通过 ES5 的保护对象的 Object.defindeProperty 中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM 树上。
虚拟DOM (Virtaul DOM): 用 js 对象模拟的,保存当前视图内所有 DOM 节点对象基本描述属性和节点间关系的树结构。用 js 对象,描述每个节点,及其父子关系,形成虚拟 DOM 对象树结构。
3.v-if和v-show的区别
共同点:v-if 和 v-show 都能实现元素的显示隐藏
区别:
1. v-show 只是简单的控制元素的 display 属性,而 v-if 才是条件渲染(条件为真,元素将会被渲染,条件为假,元素会被销毁);
2. v-show 有更高的首次渲染开销,而 v-if 的首次渲染开销要小的多;
3. v-if 有更高的切换开销,v-show 切换开销小;
4. v-if 有配套的 v-else-if 和 v-else,而 v-show 没有
5. v-if 可以搭配 template 使用,而 v-show 不行
作者:坏孩子的琉璃
链接:https://www.jianshu.com/p/987973460040
4.组件之间传值
5.vue生命周期及各阶段情况
注意:浏览器有8个钩子,但是node中做服务端渲染的时候只有beforeCreate和created
beforeCreate是new Vue()之后触发的第一个钩子,在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问。
created在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom。
beforeMount发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。
mounted在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。
beforeUpdate发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。
updated发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。
beforeDestroy发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器,销毁父组件对子组件的重复监听。beforeDestroy(){Bus.$off("saveTheme")}
destroyed发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。
6.webpack打包原理
1、webpack打包原理
把所有依赖打包成一个 bundle.js 文件,通过代码分割成单元片段并按需加载
2、webpack的优势
(1) webpack 是以 commonJS 的形式来书写脚本滴,但对 AMD/CMD 的支持也很全面,方便旧项目进行代码迁移。
(2)能被模块化的不仅仅是 JS 了。
(3) 开发便捷,能替代部分 grunt/gulp的工作,比如打包、压缩混淆、图片转base64等。
(4)扩展性强,插件机制完善
7.keep-alive的生命周期
- keep-alive的生命周期
- activated: 页面第一次进入的时候,钩子触发的顺序是created->mounted->activated
- deactivated: 页面退出的时候会触发deactivated,当再次前进或者后退的时候只触发activated
8.父子组件created和mounted加载顺序
1.组件创建的顺序
父组件先执行,执行到父组件执行到beforeMount之后,然后在去执行子组件的生命周期,beforeMount 和Mount ,执行完成了在执行父组件的Mount。
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted。
例如
综上述说:如果父组件要获取数据,并使用props传入子组件中,将数据渲染的话,需要将数据的请求放在父组件的created生命周期中发起请求。
————————————————
版权声明:本文为CSDN博主「MsimonBlowSnow」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/MsimonBlowSnow/article/details/110438270
6.vue父子组件生命周期的执行顺序
加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程
父beforeUpdate->父updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
7.在vue中如何获取DOM元素
给元素添加ref属性 通过this.$refs.domName获取
9.$nextTick的使用场景(作用)
因为vue中数据更新是异步的,当你修改了data的值然后马上获取这个dom元素的值,是不能获取到更新后的值,
你需要使用$nextTick这个回调,让修改后的data值渲染更新到dom元素之后在获取,才能成功。
13.如何优化SPA应用的首屏加载速度慢的问题
将公用的JS库通过script标签外部引入,减小app.bundel的大小,让浏览器并行下载资源文件,提高下载速度;
在配置 路由时,页面和组件使用懒加载的方式引入,进一步缩小 app.bundel 的体积,在调用某个组件时再加载对应的js文件;
加一个首屏 loading 图,提升用户体验;
9.wacth和computer区别
计算属性computed
1. 支持缓存,只有依赖数据发生改变,才会重新进行计算
2. 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
3.computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过的数据通过计算得到的
4. 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
5.如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
侦听属性watch:
1. 不支持缓存,数据变,直接会触发相应的操作;
2.watch支持异步;
3.监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
适用场景:
watch擅长处理的场景:一个数据影响多个数据
computed擅长处理的场景:一个数据受多个数据影响
————————————————
版权声明:本文为CSDN博主「宇宙超级无敌博」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35410544/article/details/111314397
9.Vue双向数据绑定原理
vue.js是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调来渲染视图。
10.什么情况下Vue使用index作为key会出问题(转):
在使用非文本节点的组件,且这个组件没有依赖于响应式的props,那么此时对于列表的删除操作会导致视图错乱。
https://www.cnblogs.com/eret9616/p/13642900.html
11.vue 能不能在v-for中嵌套v-if
v-for
具有比 v-if
更高的优先级,这意味着 v-if
将分别重复运行于每个 v-for
循环中
所以,不推荐v-if和v-for同时使用,建议用computed计算属性先对数据列表进行过滤,然后再去渲染
或者:放在计算属性遍历
12.怎么获取dom节点?
我们在vue中需要操作某一个元素的时候,可以在元素上添加ref属性,使用$refs来获取到该元素,进而进行一些列操作。
<template> <div class="contaier" ref="box" style="width: 100px;height: 100px;"> 这里用来测试元素的ref属性 </div> <button type="button" @click="showData()">点击</button> </template><script>
export default {
methods: {
showData() {
console.log(this.$refs.box);
console.log(this.$refs.box.style);
}
}
}
</script>
————————————————
版权声明:本文为CSDN博主「自由如风hsj」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_23366033/article/details/108294032
13.懒加载
- 也叫延迟加载,即在需要的时候进行加载,随用随载。
- 个人根据功能划分为图片的懒加载和组件的懒加载。
- https://www.jianshu.com/p/d6f8bf71eb11
14.vue中data为什么必须是个函数?
1.vue中组件是用来复用的,为了防止data复用,将其定义为函数。
2.vue组件中的data数据都应该是相互隔离,互不影响的,组件每复用一次,data数据就应该被复制一次,之后,当某一处复用的地方组件内data数据被改变时,其他复用地方组件的data数据不受影响,就需要通过data函数返回一个对象作为组件的状态。
3.当我们将组件中的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data,拥有自己的作用域,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。
4.当我们组件的date单纯的写成对象形式,这些实例用的是同一个构造函数,由于JavaScript的特性所导致,所有的组件实例共用了一个data,就会造成一个变了全都会变的结果。
————————————————
版权声明:本文为CSDN博主「DaftJayee」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_43540219/article/details/109077387
15.keep-alive的介绍与应用
keep-alive是什么
keep-alive是一个抽象组件:它自身不会渲染一个DOM元素,也不会出现在父组件链中;使用keep-alive包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。
一个场景
用户在某个列表页面选择筛选条件过滤出一份数据列表,由列表页面进入数据详情页面,再返回该列表页面,我们希望:列表页面可以保留用户的筛选(或选中)状态。
keep-alive就是用来解决这种场景。当然keep-alive不仅仅是能够保存页面/组件的状态这么简单,它还可以避免组件反复创建和渲染,有效提升系统性能。总的来说,keep-alive用于保存组件的渲染状态。
keep-alive用法
- 在动态组件中的应用
<keep-alive :include="whiteList" :exclude="blackList" :max="amount">
<component :is="currentComponent"></component>
</keep-alive>
- 在vue-router中的应用
<keep-alive :include="whiteList" :exclude="blackList" :max="amount">
<router-view></router-view>
</keep-alive>
include定义缓存白名单,keep-alive会缓存命中的组件;exclude定义缓存黑名单,被命中的组件将不会被缓存;max定义缓存组件上限,超出上限使用LRU的策略置换缓存数据。
内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据哪些数据属于LRU而将其移出内存而腾出空间来加载另外的数据。
作者:amCow
链接:https://www.jianshu.com/p/9523bb439950
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
16.vue路由有几种模式
实际上存在三种模式:
Hash: 使用URL的hash值来作为路由。支持所有浏览器。
History: 以来HTML5 History API 和服务器配置。参考官网中HTML5 History模式
Abstract: 支持所有javascript运行模式。如果发现没有浏览器的API,路由会自动强制进入这个模式。
vue-router中默认使用的是hash模式,也就是会出现如下的URL:,URL中带有#号
我们可以用如下代码修改成history模式:
import Vue from 'vue' import Router from 'vue-router' import Main from '@/components/Main' Vue.use(Router)export default new Router({
mode: 'history',
routes: [
{
path: '/',
component: Main
}
]
})
17.如何实现路由懒加载?
非懒加载
import List from '@/components/list.vue'
const router = new VueRouter({
routes: [
{ path: '/list', component: List }
]
})
方案一(常用)
const List = () => import('@/components/list.vue')
const router = new VueRouter({
routes: [
{ path: '/list', component: List }
]
})
方案二
const router = new Router({
routes: [
{
path: '/list',
component: (resolve) => {
// 这里是你的模块 不用import去引入了
require(['@/components/list'], resolve)
}
}
]
})
方案三
使用webpack的require.ensure技术,也可以实现按需加载。 这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。
// r就是resolve
const List = r => require.ensure([], () => r(require('@/components/list')), 'list');
// 路由也是正常的写法 这种是官方推荐的写的 按模块划分懒加载
const router = new Router({
routes: [
{
path: '/list',
component: List,
name: 'list'
}
]
}))
18.vue性能优化的几种方案
1.正确的选择v-if和v-show的使用,v-if有更高的切换消耗,v-show有更高的初始消耗
2.路由懒加载:当页面很多,组件很多的时候,SPA页面在首次加载的时候,就会变的很慢。这是因为vue首次加载的时候把可能一开始看不见的组件也一次加载了,这个时候就需要对页面进行优化,就需要异步组件了。
3.缓存:spa页面采用keep-alive缓存组件
4.图片懒加载:提高页面加载速度,不在可视区域内的图片先不加载,只有滚动到可视区域的时候才加载。一般借助外部插件如 vue-lazyload。使用只需要npm install vue-lazyload然后页面引入即可使用。
5.SEO优化: ssr服务端渲染
6.打包优化:对于一些过大的包采取cdn引入文件的方式而不是直接下载到本地。
四:es6
1.var和let和const
let和const必须先声明后使用,会存在“暂时性死区”,不存在变量提升
2.什么是promiss
Promise 是异步编程的一种解决方案,比传统的异步解决方案【回调函数】和【事件】更合理、更强大。
1、主要用于异步计算
2、可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
3、可以在对象之间传递和操作promise,帮助我们处理队列
new Promise(
function (resolve, reject) {
// 一段耗时的异步操作
resolve('成功') // 数据处理完成
// reject('失败') // 数据处理出错
}).then(
(res) => {console.log(res)}, // 成功
(err) => {console.log(err)} // 失败)
Promise是最大的好处是为了避免“回调地狱”,就是多层的回调
Promise 是异步编程的一种解决方案,Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。
回调包装成Promise,他有两个显而易见的好处:
1、可读性好
2、返回 的结果可以加入任何Promise队列
局限性:
promise状态一经改变,不会再变。
· 首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。
· 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
· 第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
· 当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject, 即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
3.箭头函数
箭头函数this指向定义时的对象
es5中的this指向调用时的对象
五:算法
1.冒泡排序
function bubleSort(arr){ for (var i=0;i<arr.length-1;i++) { for (j=i+1;j<arr.length;j++) { if(arr[i]>arr[j]){ var temp=arr[i]; arr[i]=arr[j]; arr[j]=temp; } } } return arr; } var arr2=[7,2,4,5,1,6]; console.log(bubleSort(arr2));
//冒泡排序:即实现数组由小到大进行排序;思路为:每次比较相邻的两个数,如果后一个比前一个小,换位置。如果要实现由大到小排序,使用reverse()即可
var arr = [3,2,1,5,4,7,6]; function bubbleSort(arr){ var len = arr.length; for (var i= len;i>=2;--i) { for (var j=0;j<i-1;j++) { if(arr[j+1]<arr[j]){ var temp; temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } return arr; } var arr2 = bubbleSort(arr); console.log(arr2); //从小到大var arr3 = arr2.reverse();
console.log(arr3); //从大到小
2.快速排序和二分查找
//快速排序:思路:采用二分法,取出中间数,数组每次和中间数比较,小的放到左边,大的放右边 var arr = [3,1,4,2,6,5,7]; function quickSort(arr){ if(arr.length == 0){ return []; //返回空数组 } var cIndex = Math.floor(arr.length / 2); var c= arr.splice(cIndex,1); var l = []; var r = []; for (var i=0;i<arr.length;i++) { if(arr[i]<c){ l.push(arr[i]); }else{ r.push(arr[i]); } } return quickSort(l).concat(c,quickSort(r)); } console.log(quickSort(arr));
//二分查找,前提是有序数组 function binarySearch(target, arr) { var l = 0, r = arr.length - 1; while(l <= r) { var mid = l + parseInt((r - l) / 2); if(arr[mid] == target) { return mid; } else if(target > arr[mid]) { l = mid + 1; } else if(target < arr[mid]) { r = mid - 1; }} return -1;
}
var arr = [1, 2, 4, 6, 8, 9, 11, 34, 67];
console.log(binarySearch(11, arr));
3.数组的翻转(非reverse())
<h2>方法一:</h2> <script type="text/javascript"> var arr = [1,2,3,4]; var arr2 = []; while(arr.length){ var num = arr.pop(); //删除数组最后一个元素并返回被删除的元素 arr2.push(num); } console.log(arr2); </script> <h2>方法二:</h2> <script type="text/javascript"> var arr = [1,2,3,4]; var arr2 = []; while(arr.length){ var num = arr.shift(); //删除数组第一个元素并返回被删除的元素; arr2.unshift(num); //unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。 } console.log(arr2); </script>
4.数组由小到大进行排序
//a-b是从小到大;b-a是从大到小 var arr = [3,43,25,30,65,90]; function sortnum(a,b){ return a-b; } arr = arr.sort(sortnum); console.log(arr);
5.数组去重
<p>方法一:</p> <script type="text/javascript"> var arr = [0,2,3,4,4,0,2]; var obj = {}; var tmp = []; for (var i=0;i<arr.length;i++) { if(!obj[arr[i]]){ obj[arr[i]] = 1; tmp.push(arr[i]); } } console.log(tmp); </script> <p>方法二:indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。</p> <script type="text/javascript"> var arr = [2,3,4,4,5,2,3,6], arr2 = []; for (var i=0;i<arr.length;i++) { if(arr2.indexOf(arr[i])<0){ arr2.push(arr[i]); } } console.log(arr2); </script> <p>方法三:</p> <script type="text/javascript"> var arr = [2,3,4,4,5,2,3,6]; var arr2 = arr.filter(function(element,index,self){ return self.indexOf(element) === index; }); console.log(arr2); </script>
6.判断一个字符串中出现次数最多的字符,统计这个次数
var str = 'asdfssaaasasasasaa'; var json = {}; for (var i=0;i<str.length;i++) { //charAt() 方法可返回指定位置的字符。 if(!json[str.charAt(i)]){ json[str.charAt(i)] = 1; }else{ json[str.charAt(i)]++; } }; var iMax = 0; var iIndex = ''; for (var i in json) { if(json[i]>iMax){ iMax = json[i]; iIndex = i; } } console.log('出现次数最多的是:'+iIndex+'出现'+iMax+'次');
7.apply()/call()求数组最值
<script type="text/javascript"> var numbers = [5,458,120,-215]; var maxNumber= Math.max.apply(this,numbers); console.log(maxNumber); //458 var maxnumber = Math.max.call(this,5,458,120,-215); console.log(maxnumber); //458 </script>
8.获取浏览器URL中查询字符串中的参数
function showWindowHref (){ var sHref = window.location.href; var args = sHref.split('?'); if(args[0] == sHref){ return ""; } var arr = args[1].split("&"); var obj = {}; for (var i=0;i<arr.length;i++) { var arg = arr[i].split('='); obj[arg[0]] = arg[1]; } return obj; } var href = showWindowHref(); //obj console.log(href['name']); //xiaoming
10.2个对象排序{age:10,name:'lili'}
var a={age:10,name:"lili"}
var a={age:13,name:"lisi"}
function(a,b){
if(a.age==b.age){
if(a.name.toString()>b.name.toString()){
return true;
}else{return false;}
}else{
if(a.age>b.age){
return true
}else{return false;}
}
}
11.设计模型
https://www.jianshu.com/p/4f3014fb8b8b
12.JavaScript将具有父子关系的原始数据格式化成树形结构数据(id,pid)
// 方法一while var data=[ {id:1,pid:0,text:'A'}, {id:2,pid:4,text:"E[父C]"}, {id:3,pid:7,text:"G[父F]"}, {id:4,pid:1,text:"C[父A]"}, {id:5,pid:6,text:"D[父B]"}, {id:6,pid:0,text:'B'}, {id:7,pid:4,text:"F[父C]"} ]; function toTreeData(data){ var pos={}; var tree=[]; var i=0; while(data.length!=0){ if(data[i].pid==0){ tree.push({ id:data[i].id, text:data[i].text, children:[] }); pos[data[i].id]=[tree.length-1]; data.splice(i,1); i--; }else{ var posArr=pos[data[i].pid]; if(posArr!=undefined){</span><span style="color: #0000ff;">var</span> obj=tree[posArr[0<span style="color: #000000;">]]; </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">var</span> j=1;j<posArr.length;j++<span style="color: #000000;">){ obj</span>=<span style="color: #000000;">obj.children[posArr[j]]; } obj.children.push({ id:data[i].id, text:data[i].text, children:[] }); pos[data[i].id]</span>=posArr.concat([obj.children.length-1<span style="color: #000000;">]); data.splice(i,</span>1<span style="color: #000000;">); i</span>--<span style="color: #000000;">; } } i</span>++<span style="color: #000000;">; </span><span style="color: #0000ff;">if</span>(i>data.length-1<span style="color: #000000;">){ i</span>=0<span style="color: #000000;">; } } console.log(tree) </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> tree;
}
toTreeData(data)
// 方法二:递归 var data1= [{ id: 1, name: '1', }, { id: 2, name: '1-1', parentId: 1 }, { id: 3, name: '1-1-1', parentId: 2 }, { id: 4, name: '1-2', parentId: 1 }, { id: 5, name: '1-2-2', parentId: 4 }, { id: 6, name: '1-1-1-1', parentId: 3 }, { id: 7, name: '2', }] /** * 该方法用于将有父子关系的数组转换成树形结构的数组 * 接收一个具有父子关系的数组作为参数 * 返回一个树形结构的数组 */ function translateDataToTree(data) { //没有父节点的数据 let parents = data.filter(value => value.parentId == 'undefined' || value.parentId == null) //有父节点的数据 let children = data.filter(value => value.parentId !== 'undefined' && value.parentId != null) //定义转换方法的具体实现 let translator = (parents, children) => { //遍历父节点数据 parents.forEach((parent) => { //遍历子节点数据 children.forEach((current, index) => { //此时找到父节点对应的一个子节点 if (current.parentId === parent.id) { //对子节点数据进行深复制,这里只支持部分类型的数据深复制,对深复制不了解的童靴可以先去了解下深复制 let temp = JSON.parse(JSON.stringify(children)) //让当前子节点从temp中移除,temp作为新的子节点数据,这里是为了让递归时,子节点的遍历次数更少,如果父子关系的层级越多,越有利 temp.splice(index, 1) //让当前子节点作为唯一的父节点,去递归查找其对应的子节点 translator([current], temp) //把找到子节点放入父节点的children属性中 typeof parent.children !== 'undefined' ? parent.children.push(current) : parent.children = [current] } } ) } ) } //调用转换方法 translator(parents, children) //返回最终的结果 console.log(parents) return parents}
translateDataToTree(data1)
----------声明:此文章是方便自己面试总结,一些解答是学习其他博主内容,我记录过原文链接的都放出来了,没记录过得不是故意没备注原文链接,望体谅,若有侵权,联系我改,谢谢--------
https://www.cnblogs.com/star91/p/5659134.html