前端高频面试题合集(面试经验总结)

324 阅读38分钟

1.对SEO搜索引擎优化的了解

搜索引擎优化(英语: Search Engine Optimization ,缩写为 SEO ),根据维基百科的解释是:

它是一种透过了解搜索引擎的运作规则来调整网站,以及提高目的网站在有关搜索引擎内排名的方式。
由于不少研究发现,搜索引擎的用户往往只会留意搜索结果最前面的几个条目,所以不少网站都希望
透过各种形式来影响搜索引擎的排序,让自己的网站可以有优秀的搜索排名。当中尤以各种依靠广告
维生的网站为生。

用人话来讲,就是通过搜索引擎优化,我们的网站可以在搜索结果的排名中靠前。

那么,作为一个前端,我们该如何为搜索引擎优化做出一点贡献呢?

SEO优化的优势

1、用户接受程度高。优化排名的用户接受程度是广告排名的50倍以上;

2、针对性强。对企业产品真正感兴趣的潜在客户能通过有针对性的“产品关键词”直接访问到企业的相关页面,更容易达成交易!

3、一举多得的效果。网站优化可以让你网站同时在多个搜索引擎上取得很好的效果!

4、成本低。优化后的网站搜索排名属正常排名,因此我们通过网站优化(搜索引擎优化推广或SEO)使网站进行入搜索结果第一页,无论每天点击量在是多少次,都不用支付任何点击费用!

5、全球最大网络营销平台群,覆盖面广。调查表明:网站75%的访问量都来自于搜索引擎的推荐,因此主流搜索引擎(Google、百度、MSN等)是最具价值的企业推广平台!

SEO优化的内容

1、站点结构分析,拥有一个好的网站结构,除利于排名外,也会让浏览者更容易找到他们想要的信息

2、浏览器兼容性测试

3、网页代码优化,动态程度及HTML/CSS语法的正确性

4、链接正确性检查

5、网站信息导航优化

6、站点交互性分析

7、相关产品的信息全面性检查(直接影响您的用户获取有用信息)

8、针对不同的产品和服务,进行单网页优化

seo优化的步骤!!!

1、关键词分析(也叫关键词定位)
这是进行 SEO 优化最重要的一环,关键词分析包括:关键词关注量分析、竞争对手分析、关键词与网站相关性分析、关键词布置、关键词排名预测。

2、网站架构分析
网站结构符合搜索引擎的爬虫喜好则有利于 SEO 优化。网站架构分析包括:剔除网站架构不良设计、实现树状目录结构、网站导航与链接优化。

3、网站目录和页面优化
SEO 不止是让网站首页在搜索引擎有好的排名,更重要的是让网站的每个页面都带来流量。

4、内容发布和链接布置
搜索引擎喜欢有规律的网站内容更新,所以合理安排网站内容发布日程是 SEO 优化的重要技巧之一。链接布置则把整个网站有机地串联起来,让搜索引擎明白每个网页的重要性和关键词,实施的参考是第一点的关键词布置。友情链接战役也是这个时候展开。

5、与搜索引擎对话
向各大搜索引擎登陆入口提交尚未收录站点。在搜索引擎看 SEO 的效果,通过 site:你的域名,知道站点的收录和更新情况。通过 domain:你的域名或者 link:你的域名,知道站点的反向链接情况。更好的实现与搜索引擎对话,建议采用 Google 网站管理员工具。 6、建立网站地图 SiteMap
根据自己的网站结构,制作网站地图,让你的网站对搜索引擎更加友好化。让搜索引擎能过 SiteMap 就可以访问整个站点上的所有网页和栏目。

最好有两套 siteMap,一套用户方便客户快速查找站点信息(html 格式),另一套方便搜索引擎得知网站的更新频率、更新时间、页面权重(xml 格式)。所建立的 sitemap 要和你网站的实际情况相符合。

7、高质量的友情链接
建立高质量的友情链接,对于 seo 优化来说,可以提高网站 PR 值以及网站的更新率,都是非常关键性的问题。

8、网站流量分析
网站流量分析从 SEO 结果上指导下一步的 SEO 策略,同时对网站的用户体验优化也有指导意义。流量分析工具,建议采用分析工具 Google Analytics 分析工具和百度统计分析工具。

2.img的title和alt的区别


alt是代替图片的文字,图片不能显示时会显示alt,title是鼠标指上去时的解释文字。

因此,图片的title可以没有,但是做网站的话,有图片就一定需要有ALT。

Alt是放在img上的面title是放在a上的,alt限长为100字符,对seo优化有很大帮助

  • title:非常的重要,这个是标题栏上面显示的,也就是你要设置的重要关键词,一般设置精准点,
  • alt:图片属性,命名这个最好与title中最重要的关键词设置一样,这样对你的优化有更好的效果!

3.行内元素和块级元素的区别


  • 块级元素独占一行,块级元素可以设置 width, height属性
  • 行内元素挨个排放,行内元素设置width,height属性无效 1、常见的行内元素

<span> <a> <lable> <strong> <b> <small> <abbr> <button> <input> <textarea> <select> <code> <img> <br> <q> <i> <cite> <var> <kbd> <sub> <bdo>

2、常见的块级元素

<div> <p> <li> <h1> <h2> <h3> <h4> <h5> <h6> <form> <header> <hr> <ol> <address> <article> <aside> <audio> <canvas> <dd> <dl> <fieldset> <section> <ul> <video>

4.媒体查询的使用方式


Media Queries能在不同的条件下使用不同的样式,使页面在不同在终端设备下达到不同的渲染效果。前面简单的介绍了Media Queries如何引用到项目中,但Media Queries有其自己的使用规则。具体来说,Media Queries的使用方法如下。

@media 媒体类型and (媒体特性){你的样式}

注意:使用Media Queries必须要使用“@media”开头,然后指定媒体类型(也可以称为设备类型),随后是指定媒体特性(也可以称之为设备特性)。媒体特性的书写方式和样式的书写方式非常相似,主要分为两个部分,第一个部分指的是媒体特性,第二部分为媒体特性所指定的值,而且这两个部分之间使用冒号分隔。例如:

从前面表中可以得知,主要有十种媒体类型和13种媒体特性,将其组合就类似于不同的CSS集合。

但与CSS属性不同的是,媒体特性是通过min/max来表示大于等于或小于做为逻辑判断,不是使用小于(<)和大于(>)这样的符号来判断。接下来一起来看看Media Queries在实际项目中常用的方式。

(1) 最大宽度max-width

“max-width”是媒体特性中最常用的一个特性,其意思是指媒体类型小于或等于指定的宽度时,样式生效。如:

@media screen and (max-width:480px){
 .ads {
   display:none;
  }
}

上面表示的是:当屏幕小于或等于480px时,页面中的广告区块(.ads)都将被隐藏。

(2)最小宽度min-width

“min-width”与“max-width”相反,指的是媒体类型大于或等于指定宽度时,样式生效。

上面表示的是:当屏幕大于或等于900px时,容器“.wrapper”的宽度为980px。

(3)多个媒体特性使用

Media Queries可以使用关键词"and"将多个媒体特性结合在一起。也就是说,一个Media Query中可以包含0到多个表达式,表达式又可以包含0到多个关键字,以及一种媒体类型。

当屏幕在600px~900px之间时,body的背景色渲染为“#f5f5f5”,如下所示。

@media screen and (min-width:600px) and (max-width:900px){
  body {background-color:#f5f5f5;}
}

(4)设备屏幕的输出宽度Device Width

在智能设备上,例如iPhoneiPad等,还可以根据屏幕设备的尺寸来设置相应的样式(或者调用相应的样式文件)。同样的,对于屏幕设备同样可以使用“min/max”对应参数,如“min-device-width”或者“max-device-width”。

<link rel="stylesheet" media="screen and (max-device-width:480px)" href="iphone.css" />

上面的代码指的是“iphone.css”样式适用于最大设备宽度为480px,比如说iPhone上的显示,这里的“max-device-width”所指的是设备的实际分辨率,也就是指可视面积分辨率。

(5)not关键词

使用关键词“not”是用来排除某种制定的媒体类型,也就是用来排除符合表达式的设备。换句话说,not关键词表示对后面的表达式执行取反操作,如:

@media not print and (max-width: 1200px){样式代码}

上面代码表示的是:样式代码将被使用在除打印设备和设备宽度小于1200px下所有设备中。

(6)only关键词

only用来指定某种特定的媒体类型,可以用来排除不支持媒体查询的浏览器。其实only很多时候是用来对那些不支持Media Query但却支持Media Type的设备隐藏样式表的。其主要有:支持媒体特性的设备,正常调用样式,此时就当only不存在;表示不支持媒体特性但又支持媒体类型的设备,这样就会不读样式,因为其先会读取only而不是screen;另外不支持Media Queries的浏览器,不论是否支持only,样式都不会被采用。如:

<link rel="stylesheet" media="only screen and (max-device-width:240px)" href="android240.css" />

在Media Query中如果没有明确指定Media Type,那么其默认为all,如:

<link rel="stylesheet" media="(min-width:701px) and (max-width:900px)" href="mediu.css" /></pre>

另外在样式中,还可以使用多条语句来将同一个样式应用于不同的媒体类型和媒体特性中,指定方式如下所示。

<link rel="stylesheet" type="text/css" href="style.css" media="handheld and (max-width:480px), screen and (min-width:960px)" />

上面代码中style.css样式被用在宽度小于或等于480px的手持设备上,或者被用于屏幕宽度大于或等于960px的设备上。

到目前为止,CSS3 Media Queries得到了众多浏览器支持,除了IE6-8浏览器不支持之外,在所有现代浏览器中都可以完美支持。还有一个与众不同的是,Media Queries在其他浏览器中不要像其他CSS3属性一样在不同的浏览器中添加前缀。

5.src和href的区别


image.png (1)src和href所表达的请求资源类型不同

src源的全拼是Source源,是在请求src资源的时候进行指向的资源下载并应用到文档中;href源的全拼是Hypertext Reference是指超文本引用,用来建立当前元素和文档之间的连接。

(2)src和href的作用结果不同

src的作用结果只能用来替换当前内容,不能用在文档和资源之间联系;href的作用结果只能用在当前文档和所要引用的资源之间确立联系。

(3)src和href作为外部资源的引入,它们浏览器的解析方式不同。

src在浏览器中被解析到时会暂停其他资源的下载和处理,并且把该资源加载编译完成;href在文档中进行添加时,浏览器会识别到这个文档并命名为CSS文件,就会下载合并资源,同时也不会停止对当前文档的处理。

6.css让元素隐藏的方法

css隐藏元素的方法有很多,这边列举五种。

(1)、利用opacity透明度来隐藏,opacity设置为0,它只是一种视觉隐藏,元素本身依旧占用它的位置并对网页的布局起到作用,它也同样影响用户交互。在读屏软件中会被识别。

(2)、visibility:规定元素 是否可见,一般设置为hidden,它可以很好的隐藏,不会影响用户的交互,在读屏软件中不会被识别。(visibility可能的值)

(3)、display:当display设置为none,任何对该元素直接打用户交互操作都不可能生效,被隐藏的元素完全不会占用空间,仿佛元素完全不存在一样。(display可能的值)

(4)、position:元素的定位。把position:absolutely;top:-4555px;left:-45545px;top和left设置成足够大的负数,相当于把元素放到可视区域外,它不会影响布局,能够让元素保持可操作性,在读屏软件上可以被识别。(position可能的值)

(5)、clip-path:可以创建一个只有元素的部分区域可以显示的剪切区域。区域内的部分显示,区域外的隐藏。剪切区域是被引用内嵌的URL定义的路径或者外部svg的路径,或者作为一个形状例如circle().。clip-path属性代替了现在已经弃用的剪切 clip属性。它只是一个实验中的功能,兼容性并不是很好。

用法:clip-pach:polygon(0px 0px,0px 0px,0px 0px,0px 0px);

7.重绘和回流


重绘和回流是什么

怎么去理解这两个概念呢?从字面上理解,重绘,重新绘画,重新上色,较难产生联想的是回流。

我们都知道,一个页面从加载到完成,首先是构建DOM树,然后根据DOM节点的几何属性形成render树(渲染树),当渲染树构建完成,页面就根据DOM树开始布局了,渲染树也根据设置的样式对应的渲染这些节点。

在这个过程中,回流与DOM树,渲染树有关,重绘与渲染树有关,怎么去理解呢?

比如我们增删DOM节点,修改一个元素的宽高,页面布局发生变化,DOM树结构发生变化,那么肯定要重新构建DOM树,而DOM树与渲染树是紧密相连的,DOM树构建完,渲染树也会随之对页面进行再次渲染,这个过程就叫回流

当你给一个元素更换颜色,这样的行为是不会影响页面布局的,DOM树不会变化,但颜色变了,渲染树得重新渲染页面,这就是重绘

你应该能感觉到,回流的代价要远大于重绘。且回流必然会造成重绘,但重绘不一定会造成回流。

重绘和回流有什么区别

结合上面的解释,引起DOM树结构变化,页面布局变化的行为叫回流,且回流一定伴随重绘。

只是样式的变化,不会引起DOM树变化,页面布局变化的行为叫重绘,且重绘不一定会便随回流。

怎样减少回流

说了这么多,我们也知道了,回流要重新构建DOM树,渲染树也得重新渲染,很麻烦,哪么哪些行为会引起回流,怎么去避免呢?

1.DOM的增删行为

比如你要删除某个节点,给某个父元素增加子元素,这类操作都会引起回流。如果要加多个子元素,最好使用documentfragment。

2.几何属性的变化

比如元素宽高变了,border变了,字体大小变了,这种直接会引起页面布局变化的操作也会引起回流。如果你要改变多个属性,最好将这些属性定义在一个class中,直接修改class名,这样只用引起一次回流。

3.元素位置的变化

修改一个元素的左右margin,padding之类的操作,所以在做元素位移的动画,不要更改margin之类的属性,使用定位脱离文档流后改变位置会更好。

4.获取元素的偏移量属性

例如获取一个元素的scrollTop、scrollLeft、scrollWidth、offsetTop、offsetLeft、offsetWidth、offsetHeight之类的属性,浏览器为了保证值的正确也会回流取得最新的值,所以如果你要多次操作,最取完做个缓存。

5.页面初次渲染

这样的回流无法避免

6.浏览器窗口尺寸改变

resize事件发生也会引起回流。

这里就不列举引起重绘的行为了,记住,回流一定伴随着重绘,所以上面的行为都会重绘,除此之外,例如修改背景颜色,字体颜色之类不影响布局的行为都只引发重绘。

8.new操作符具体干了什么


(1)创建一个新的对象

(2)讲实例对象的原型指向构造函数的原型

(3)将构造函数的this指向实例对象

(4)返回新对象

图解image.png

9.ajax的原理


Ajax相当于在用户和服务器之间加了一个中间层,使用户操作与服务器响应异步化。并不是所有的用户请求都提交给服务器,像一些数据验证和数据处理等都交给Ajax引擎自己来做,只有确定需要从服务器读取新数据时再由Ajax引擎代为向服务器提交请求。

Ajax的原理简单来说通过XmlHttpRequest对象来向服务器发送异步请求,从服务器获得数据,然后用JavaScript来操作DOM而更新页面。这其中最关键的一步就是从服务器获得请求数据。要清楚这个过程和原理,我们必须对 XMLHttpRequest有所了解。

XMLHttpRequest是ajax的核心机制,它是在IE5中首先引入的,是一种支持异步请求的技术。简单的说,也就是JavaScript可以及时向服务器提出请求和处理响应,而不阻塞用户,达到无刷新的效果。

10.常见的设计模式有哪些


JS 设计模式有很多,但我知道的有单例模式,观察者模式

单例模式:就是保证一个类只有一个实例,实现的方法一般是先判断实例存 在与否,如果存在直接返回,如果不存在就创建了再返回,这就确保了一个类 只有一个实例对象。在 JavaScript 里,单例作为一个命名空间提供者,从全 局命名空间里提供一个唯一的访问点来访问该对象。

观察者模式: 观察者的使用场合就是:当一个对象的改变需要同时改变 其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。

总的来说:观察者模式所做的工作就是在解耦,让耦合的双方都依赖于抽 象,而不是依赖于具体。从而使得各自的变化都不会影响到另一边的变化。

11.说说你对promise的理解


Js中进行异步编程的新的解决方案,用于表示一个异步操作的最终完成 (或失败), 及其结果值。

语法上:promise是一个构造函数

语法:

new Promise(function (resolve, reject) {
    ...    
} /* executor */)

executor: executor是带有 resolvereject 两个参数的函数 。Promise构造函数执行时立即调用executor 函数, resolvereject 两个函数作为参数传递给executor(executor 函数在Promise构造函数返回所建promise实例对象前被调用)。resolvereject 函数被调用时,分别将promise的状态改为 fulfilled(完成)或rejected(失败)。executor 内部通常会执行一些异步操作,一旦异步操作执行完毕(可能成功/失败),要么调用resolve函数来将promise状态改成fulfilled,要么调用reject 函数将promise的状态改为rejected。如果在executor函数中抛出一个错误,那么该promise 状态为rejected。executor函数的返回值被忽略。

promise 有三种状态

Pending(进行中,初始状态,既不是成功,也不是失败状态。)、Resolved(已完成,又称 Fulfilled)、Rejected(已失败)

这三种状态的变化途径只有2种

  • 异步操作从 未完成 pending => 已完成 resolved
  • 异步操作从 未完成 pending => 失败 rejected
  • 状态一旦改变,就无法再次改变状态,这也是它名字 promise-承诺 的由来,一个promise对象只能改变一次

pending 状态的 Promise 对象可能会变为fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用(then方法包含两个参数:onfulfilled 和 onrejected,它们都是 Function 类型。当Promise状态为fulfilled时,调用 then 的 onfulfilled 方法,当Promise状态为rejected时,调用 then 的 onrejected 方法, 所以在异步操作的完成和绑定处理方法之间不存在竞争)。因为 Promise.prototype.then 和 Promise.prototype.catch 方法返回promise 对象, 所以它们可以被链式调用。

image.png

12.js数据类型有哪些


JavaScript(以下简称js)的数据类型分为两种:原始类型(即基本数据类型)和对象类型(即引用数据类型)

请注意:JS的数据类型有8种。

js基本数据类型number,string,Boolean,null,undefined,symbol(ES6 新 增)

复合类型:Object、function

ES6 中新增了一种 Symbol 。这种类型的对象永不相等,即始创建的时候传入相同的值,可以解决属性名冲突的问题,做为标记。

13.js的内存存储方式以及区别


js执行过程中,有三种类型的内存空间(代码空间,栈空间,堆空间),代码空间存储可执行的代码,基础类型存储在栈空间,引用类型存储在堆空间,访问引用类型的时候要先去栈空间获取存储地址。

image.png

14.js里如何判断是不是一个数组(5种方法)


let arr = []

(1)instanceof

arr  instanceof Array

(2)proto

arr.__proto__  === Array.prototype

(3)constructor

arr.constructor === Array

(4)Object.prototype.toString

Object.prototype.toString.call(arr) === '[object Array]'

(5)Array.isArray

Array.isArray(arr)

其中方法1,2,3 主要是通过原型去判断的,4是通过object类型的副属性class去判断的,其中函数的class是Function,结果是[object Function], 普通的对象是Object,结果是[object Object],5是es6新增的方法。

15.Map和Foreach的区别


forEach:这个方法是为了取代 for 循环遍历数组的,返回值为 undefined

例如:

let arrInfo=[4,6,6,8,5,7,87]
arrInfo.forEach((item,index,arr)=>{
//遍历逻辑
})

其中:

item 代码遍历的每一项

index:代表遍历的每项的索引

arr代表数组本身

map:这个 map 方法主要对数组的复杂逻辑处理时用的多,特别是 react 中 遍历数据,也经常用到,写法和 forEach 类似,返回值由原数组每个元素执行回调函数的结果组成的新数组。

16.Vue2的双向数据绑定的原理


核心主要利用 ES5 中的 Object.defineProperty 实现的,然后利用里面 的 getter 和 setter 来实现双向数据绑定的,大致就这些,其实要实现起来比这 个要复杂一些,不过我大致了解过。

17.数组去重的方法(5种方法)


(1)排序后相邻去除法

var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]
  arr.sort()
  var newArr = [arr[0]]
  for (var i = 1; i < arr.length; i++) {
    if (arr[i] !== newArr[newArr.length - 1]) {
      newArr.push(arr[i])
    }
  }
  console.log(newArr) // 结果:[0, 2, 5, 6, 7, 8]
  
  思路:先用sort()方法把arr排序,那么排完序后,相同的一定是挨在一起的,把它去掉就好了,首先
  给新数组初始化一个arr[0],因为我们要用它和arr数组进行比较,所以,for循环里面i也是从1开始
  了,我们让遍历到的arr中的值和新数组最后一位进行比较,如果相等,则pass掉,不相等的,push
  进来,因为数组重新排序了,重复的都挨在一起,那么这就保证了重复的这几个值只有第一个会被push进
  来,其余的都和新数组的被push进来的这个元素相等,会被pass掉,也达到了去重的效果。

(2)利用for嵌套for,然后splice去重(ES5中最常用)

var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2, 8]
  var newArr = []
  for (var i = 0; i < arr.length; i++) {
    for (var j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j]) {
        i++
        j = i
      }
    }
    newArr.push(arr[i])
  }
  console.log(newArr) // 结果:[0, 5, 6, 7, 2, 8]
  
  思路:两层for循环,外面一层是控制遍历到的前一个arr中的元素,里面一层控制的是第一层访问到的元素
  后面的元素,不断的从第0个开始,让第0个和他后面的元素比较,如果没有和这个元素相等的,则证明没有
  重复,推入到新数组中存储起来,如果有和这个元素相等的,则pass掉它,直接进入下一次循环。从第1个开
  始,继续和它后面的元素进行比较,同上进行,一直循环到最后就是:不重复的都被推入新数组里面了,而重
  复的前面的元素被pass掉了,只留下了最后面的一个元素,这个时候也就不重复了,则推入新数组,过滤掉了
  所有重复的元素,达到了去重的目的。

(3)利用indexOf去重

var arr = [2, 8, 5, 0, 5, 2, 6, 7, 2]
  var newArr = []
  for (var i = 0; i < arr.length; i++) {
    if (arr.indexOf(arr[i]) === i) {
      newArr.push(arr[i])
    }
  }
  console.log(newArr) // 结果:[2, 8, 5, 0, 6, 7]
  
  遍历arr的过程中,如果在arr数组里面找当前的值,返回的索引等于当前的循环里面
  的i的话,那么证明这个值是第一次出现,所以推入到新数组里面,如果后面又遍历到
  了一个出现过的值,那也不会返回它的索引,indexof()方法只返回找到的第一个值的
  索引,所以重复的都会被pass掉,只出现一次的值都被存入新数组中,也达到了去重的目的。

(4)利用includes

var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
function unique(arr) {
    if (!Array.isArray(arr)) {
        console.log('type error!')
        return
    }
    var array =[];
    for(var i = 0; i < arr.length; i++) {
            if( !array.includes( arr[i]) ) {//includes 检测数组是否有某个值
                    array.push(arr[i]);
              }
    }
    return array
}
console.log(unique(arr))

(5)利用filter

var arr = [1, 1, 8, 8, 12, 12, 15, 15, 16, 16];
function unlink(arr) {
    return arr.filter(function (item, index, arr) {
        //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
        return arr.indexOf(item, 0) === index;
    });
}
console.log(unlink(arr));

18.防抖和节流


什么是防抖(debounce):

在事件被触发n秒后再执行回调函数,如果在这n秒内又被触发,则重新计时。

应用场景

  • 用户在输入框连续输入一串字符时,可以通过防抖策略,只在输入完后,才执行查询的请求,这样可以有效减少请求次数,节约请求资源.

什么是节流(throttle):

规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。

应用场景

  • 鼠标不断触发某事件时,如点击,只在单位事件内触发一次.
  • 懒加载时要监听计算滚动条的位置,但不必要每次滑动都触发,可以降低计算频率,而不必要浪费CPU资源. 效果:

函数防抖是某一段时间内只执行一次;而函数节流是间隔时间执行,不管事件触发有多频繁,都会保证在规定时间内一定会执行一次真正的事件处理函数。

原理:

防抖是维护一个计时器,规定在delay时间后触发函数,但是在delay时间内再次触发的话,都会清除当前的 timer 然后重新设置超时调用,即重新计时。这样一来,只有最后一次操作能被触发。

节流是通过判断是否到达一定时间来触发函数,若没到规定时间则使用计时器延后,而下一次事件则会重新设定计时器。

19.apply,call,bind的区别


call,apply,bind 主要作用都是改变 this 指向的,但使用上略有区别,说一下区别:

call 和 apply 的主要区别是在传递参数上不同,call 后面传递的参数是以 逗号的形式分开的,apply 传递的参数是数组形式 [Apply 是以 A 开头的, 所以应该是跟 Array(数组)形式的参数] bind 返回的是一个函数形式,如果要执行,则后面要再加一个小括号 例如: bind(obj,参数 1,参数 2,)(),bind 只能以逗号分隔形式,不能是数组形式。

20.this的指向


1⃣️ 全局 this 是 window
2⃣️ 函数 this 是调用者
3⃣️ [构造函数]的 this 是 new 之后的新对象
4⃣️ call ,apply ,bind 的 this 是第一个参数

箭头函数和普通函数的区别是.箭头函数指向定义时.定义的时候 this 就确定 了.指向它的外层. 普通函数是指向调用时.谁调用的我.this 就指向谁。

21.判断js的数据类型


(1)最常见的判断方法:typeof(需注意,typeof返回的类型都是字符串形式!!!)

(2)已知对象类型: instanceof(instanceof 后面一定要是对象类型,并且大小写不能错,该方法适合一些条件选择或分支。)

(3)对象原型链判断方法: prototype(适用于所有类型的判断检测,注意区分大小写. toString方法,在Object原型上返回数据格式)

(4)根据对象的构造器constructor进行判断 (constructor 判断方法跟[instanceof]相似,但是constructor检测Object与instanceof不一样,constructor还可以处理基本数据类型的检测,不仅仅是对象类型)

  • null和undefined没有constructor;*

  • 判断数字时使用(),比如  (123).constructor,如果写成123.constructor会报错*

  • constructor在类继承时会出错,因为Object被覆盖掉了,检测结果就不对了*

(5)jQuery方法: jquery.type()

据说是无敌万能的方法.如果对象是null跟undefined,直接返回"null"和"undefined",

注意:在使用时,一定要引入jquery文件,不然会报错,jQuery is not defined

6.严格运算符: ===(通常===出现在我们的条件判断中,比如判断一个变量是否为空,变量是否为数据等)

一般变量用typeof

已知对象类型用instanceof

通用方法Object.prototype.toString.call()

jQuery项目万能方法jQuery.type()

22.EventLoop的理解


这个东西我研究过,是 js的一个底层运行原理,js是单线程的,但是也有一些耗时任务, 会影响执行效率.代码都在主线程中执行,当遇见你像ajax请求.setTimeout 定时器时候,会 单独开启异步线程.异步线程耗时之后会推入异步队列中等待执行.然后当主线程执行完毕 之后.会到异步队列中取出到主线程中执行.然后再去异步队列中取第二个.这个来回取的过 程就是的事件循环(eventLoop)吧。

23.常见的跨域方式


跨域是浏览器做出的限制,和后端没关系

一、 jsonp 实现原理:主要是利用动态创建 script 标签请求后端接口地址,然后传递 callback数后端接收 callback,后端经过数据处理,返回 callback 函数调用的形式,callback 中的参数就是 json.

二、 通过代理的方式(前端代理和后端代理) 前端代理我在 vue 中使用那个 vue.config.js 里面配置一个proxy,里面有个 target 属性指向跨域链接.修改完重启项目就可以了.实际上就是启动了一个代理服务器.绕开同源策略,在请求的时候,通过代理服务器获取到数据再转给浏览器.

三、 CORS

CORS 全称叫跨域资源共享,主要是后台工程师设置后端代码来达到前端跨域请求的

注:现在主流框架都是用代理和 CORS 跨域实现的

24.Js浏览器的缓存机制


什么是浏览器缓存

Web缓存是指一个Web资源(如html页面,图片,js,数据等)存在于Web服务器和客户端(浏览器)之间的副本。缓存会根据进来的请求保存输出内容的副本;当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个URL地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页。浏览器和网站服务器是根据缓存机制进行缓存的。

25.浏览器的缓存策略


(1)缓存分为强缓存和协商缓存。其中强缓存包括ExpiresCache-Control,主要是在过期策略生效时应用的缓存。弱缓存包括Last-ModifiedETag,是在协商策略后应用的缓存。强弱缓存之间的主要区别在于获取资源时是否会发送请求

(2)Cache-Control中的max-age指令用于指定缓存过期的相对时间,优先级高于ExpiresCache-Control指定为no-cache时,由于no-cache相当于max-age:0,must-revalidate,所以都存在时优先级也是高于Expires

(3)no-cache并不是指不缓存文件,no-store才是指不缓存文件。no-cache仅仅是表明跳过强缓存,强制进入协商策略。

(4)如果ExpiresCache-Control: max-age,或 Cache-Control:s-maxage都没有在响应头中出现,并且设置了Last-Modified时,那么浏览器默认会采用一个启发式的算法,即启发式缓存。通常会取响应头的Date_value - Last-Modified_value值的10%作为缓存时间。

26.Vue2的生命周期


Vue 的生命周期总共分为8个阶段:创建前/后,载入前/后,更新前/后,销毁前/后。

(1)、beforeCreate(创建前)

表示实例完全被创建出来之前,vue 实例的挂载元素$el和数据对象 data 都为 undefined,还未初始化。

(2)、created(创建后)

数据对象 data 已存在,可以调用 methods 中的方法,操作 data 中的数据,但 dom 未生成,$el 未存在 。

(3)、beforeMount(挂载前)

vue 实例的 $el 和 data 都已初始化,挂载之前为虚拟的 dom节点,模板已经在内存中编辑完成了,但是尚未把模板渲染到页面中。data.message 未替换。

(4)、mounted(挂载后)

vue 实例挂载完成,data.message 成功渲染。内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了。实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了,DOM 渲染在 mounted 中就已经完成了。

(5)、beforeUpdate(更新前)

当 data 变化时,会触发beforeUpdate方法 。data 数据尚未和最新的数据保持同步。

(6)、updated(更新后)

当 data 变化时,会触发 updated 方法。页面和 data 数据已经保持同步了。

(7)、beforeDestory(销毁前)

组件销毁之前调用 ,在这一步,实例仍然完全可用。

(8)、destoryed(销毁后)

组件销毁之后调用,对 data 的改变不会再触发周期函数,vue 实例已解除事件监听和 dom绑定,但 dom 结构依然存在。

image.png

27.HTTP的状态码


1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态代码

2xx (成功) 表示成功处理了请求的状态码。常见的 2 开头的状态码有:200 – 服务器成功返回网页

3xx (重定向) 表示要完成请求,需要进一步操作。通常,这些状态代码用来重定向

常见的 3 字开头的状态码有:

  • 301 (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应 时,会自动将请求者转到新位置。

  • 302 (临时移动) 服务器目前从不同位置的网页响应请求,但请求者应 继续使用原有位置来进行以后的请求。

  • 304 (未修改) 自从上次请求后,请求的网页未修改过。 服务器返回此响 应时,不会返回网页内容。

4xx(请求错误) 这些状态代码表示请求可能出错,妨碍了服务器的处理。

常见的 4 字开头的状态有:

404 – 请求的网页不存在

401 没权限.当时在我们项目里面的时候 token 值过期就是返回 401.我在响应拦 截器做了处理

5xx(服务器错误) 这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本 身的错误,而不是请求出错。

常见的以 5 开头的状态码有:

500 (服务器内部错误) 服务器遇到错误,无法完成请求。

503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这 只是暂时状态。

28.标准盒模型和怪异盒模型


盒模型其实就是浏览器把一个个标签都看一个形象中的盒子,那每个 盒子(即标签)都会有内容(width,height),边框(border),以及内容和边框 中间的缝隙(即内间距 padding),还有盒子与盒子之间的外间距(即margin), 用图表示为:

image.png 当然盒模型包括两种:IE 盒模型和 w3c 标准盒模型

IE 盒模型=设置的 width 宽度+margin 的值,其中,width 值包含了 padding 和 border; 标准盒模型=width+padding+border+margin 那如何在 IE 盒模型宽度和标准盒模型总宽度之间切换呢,可以通过 box-sizing:border-box 或设置成 content-box 来切换 其中:box-sizing:border-box //IE 盒模型 box-sizing:content-box //w3c 盒模型

29.如何清除浮动


(1)父盒子设置固定高度-清除浮动

缺点:使用不灵活,后期不易维护

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>清除浮动</title>
	<style type="text/css">
		.father{
            /*父盒子设置固定高度*/
			height: 100px;
			border: 1px solid red;
		}
		.child{
			width: 100px;
			height: 100px;
			float: left;
			background-color: green;
		}
	</style>
</head>
<body>
	<div class="father">
		<div class="child">child</div>
	</div>
 
</body>
</html>

(2)法清除浮动

在浮动元素的后面加一个空的块级元素(通常是div),设置该元素clear:both;属性。

缺点:结构冗余

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>清除浮动</title>
	<style type="text/css">
		.father{
			border: 1px solid red;
		}
		.child{
			width: 100px;
			height: 100px;
			float: left;
			background-color: green;
		}
		.clearfix{
			clear: both;
		}
	</style>
</head>
<body>
	<div class="father">
		<div class="child">child</div>
		<div class="clearfix"></div>
	</div>
 
</body>
</html>

(3)伪元素清除浮动

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>清除浮动</title>
	<style type="text/css">
		.father{
			border: 1px solid red;
		}
		.child{
			width: 100px;
			height: 100px;
			float: left;
			background-color: green;
		}
		.cleafix:after{
			content:'.';
			display: block;
			clear: both;
			overflow: hidden;
			height: 0;
		}
		
	</style>
</head>
<body>
	<div class="father clearfix">
		<div class="child">child</div>
	</div>
 
</body>
</html>

(4) overflow: hidden


overflow: hidden;会形成一个BFC区域,浮动和清除浮动智慧应用到当前BFC区域,

浮动不会影响其它BFC中元素的布局,而清除浮动只能清除同一BFC中在它前面的元素的浮动

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>清除浮动</title>
	<style type="text/css">
		.father{
			overflow: hidden;
			border: 1px solid red;
		}
		.child{
			width: 100px;
			height: 100px;
			float: left;
			background-color: green;
		}
		
	</style>
</head>
<body>
	<div class="father">
		<div class="child">child</div>
	</div>
 
</body>
</html>

30.ES6的新增语法


ES6 新增特性常用的主要有:let/const,箭头函数,模板字符串,解构赋 值,模块的导入(import)和导出(export default/export),Promise,还有一些 数组字符串的新方法,其实有很多,我平时常用的就这些. 依托于你答案可能被问道的问题: Const 声明一个常量.是对象的话.能不能修改里面的值? 可以的. 因为这个常量的不能修改指的是指向的地址不能修改,我只是修改里 面的内容.堆空间地址是不变,所以可以修改 箭头函数里面的 this 指向是什么样子的? 箭头函数和普通函数的区别是.箭头函数指向定义时.定义的时候 this 就确定 了.指向它的外层. 普通函数是指向调用时.谁调用的我.this 就指向的谁? 模版字符串:${ 变量 } 解构赋值: 数组解构,函数参数解构 对象解构

31.Vue父子组件传值


(1)父向子传值props

父组件:<child :inputName="name">

子组件:

(1props: {
   inputName: String,

   required: true

  }

(2props: ["inputName"]

(2)子组件向父组件传值$emit

子组件:

 <span>{{childValue}}</span>

 <!-- 定义一个子组件传值的方法 -->

  <input type="button" value="点击触发" @click="childClick">


 export default {
  data () {
   return {
    childValue: '我是子组件的数据'

   }

  },

  methods: {
   childClick () {  

    this.$emit('childByValue', this.childValue)

   }

  }

 }

父组件

<!-- 引入子组件 定义一个on的方法监听子组件的状态-->

<child v-on:childByValue="childByValue"></child>

methods: {
   childByValue: function (childValue) {
    // childValue就是子组件传过来的值
    
    this.name = childValue

   }

  }

}

32.cookie、sessionStorage、LocalStorage的区别


sessionStorage 、localStorage 和 cookie 之间的共同点:

都是保存在浏览器端,且同源的。

sessionStorage 、localStorage 和 cookie 之间的区别:

cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。

存储大小限制也不同,cookie数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。

数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;

localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。

作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。

33.let、const、var的区别


             1.var 会挂载window   let const不会
             2.var 存在变量提升  let const不存在
             3.let const存在块级作用域
             4.let const同一作用域内不可以声明同名变量 var可以 
             
            关于const
            1.一旦声明,必须赋值
            2.声明后不能再更改
            3.如果声明的是复合类型数据,可以修改其属性

34.computed和watch的区别


从作用机制上

(1) methods,watch 和 computed 都是以函数为基础的,但各自却都不同

(2) watch 和 computed 都是以 Vue 的依赖追踪机制为基础的,当某一个数据发生变化的时候,所有依赖这个数据的“相关”数据“自动”发生变化,也就是自动调用相关的函数去实现数据的变动

(3) 对 methods:methods 里面是用来定义函数的,它需要手动调用才能执行。而不像 watch 和 computed 那样,“自动执行”预先定义的函数,相比于 watch / computed,methods 不处理数据逻辑关系,只提供可调用的函数

从性质上

(1) methods 里面定义的是函数,仍然需要去调用它。

(2) computed计算属性,事实上和 data 对象里的数据属性是同一类的(使用上)。

(3) watch: 类似于监听机制+事件机制

watch 和 computed 区别

(1) 功能上:computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。

(2) 是否调用缓存:computed中的函数所依赖的属性没有发生变化,那么调用当前的函数的时候会从缓存中读取,而watch在每次监听的值发生变化的时候都会执行回调。

(3) 是否调用return:computed中的函数必须要用return返回,watch中的函数不是必须要用return

(4) watch擅长处理的场景:一个数据影响多个数据 -------搜索框。

(5)computed擅长处理的场景:一个数据受多个数据影响 -- 使用场景:当一个值受多个属性影响的时候--------购物车商品结算

35.Vue3中常用api


(1)setup

setup 函数也是 Compsition API 的入口函数,我们的变量、方法都是在该函数里定义的,来看一下使用方法

<template>
  <div id="app">
      <p>{{ number }}</p>
      <button @click="add">增加</button>
  </div>
</template>
 
<script>
// 1\. 从 vue 中引入 ref 函数
import {ref} from 'vue'
export default {
  name: 'App',
  setup() {
      // 2\. 用 ref 函数包装一个响应式变量 number
      let number = ref(0)
 
      // 3\. 设定一个方法
      function add() {
          // number是被ref函数包装过了的,其值保存在.value中
          number.value ++
      }
 
      // 4\. 将 number 和 add 返回出去,供template中使用
      return {number, add}
  }
 
}
</script>

(2)生命周期

Vue2中有 beforeCreate 、created 、beforeMount 、mounted 、beforeUpdate 等生命周期函数

而在Vue3中,这些生命周期部分有所变化,并且调用的方式也有所改变,下面放上一张变化图来简单了解一下

image.png

Vue3的这些生命周期调用也很简单,同样是先从 vue 中导入,再进行直接调用

<template>
  <div id="app"></div>
</template>
 
<script>
// 1\. 从 vue 中引入 多个生命周期函数
import {onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, unMounted} from 'vue'
export default {
  name: 'App',
  setup() {
      onBeforeMount(() => {
          // 在挂载前执行某些代码
      })
 
      onMounted(() => {
          // 在挂载后执行某些代码
      })
 
      onBeforeUpdate(() => {
          // 在更新前前执行某些代码
      })
 
      onUpdated(() => {
          // 在更新后执行某些代码
      })
 
      onBeforeUnmount(() => {
          // 在组件销毁前执行某些代码
      })
 
      unMounted(() => {
          // 在组件销毁后执行某些代码
      })
 
      return {}
  }
 
}
</script>

要特别说明一下的就是,setup 函数代替了 beforeCreate 和 created 两个生命周期函数,因此我们可以认为它的执行时间在beforeCreate 和 created 之间。

(3)reactive

reactive 方法是用来创建一个响应式的数据对象,该API也很好地解决了Vue2通过 defineProperty 实现数据响应式的缺陷

用法很简单,只需将数据作为参数传入即可,代码如下

<template>
  <div id="app">
    <!-- 4\. 访问响应式数据对象中的 count  -->
    {{ state.count }}
  </div>
</template>
 
<script>
// 1\. 从 vue 中导入 reactive 
import {reactive} from 'vue'
export default {
  name: 'App',
  setup() {
      // 2\. 创建响应式的数据对象
      const state = reactive({count: 3})
 
      // 3\. 将响应式数据对象state return 出去,供template使用
      return {state}
  }
}
</script>

(4)ref

在介绍 setup 函数时,我们使用了 ref 函数包装了一个响应式的数据对象,这里表面上看上去跟 reactive 好像功能一模一样啊,确实差不多,因为 ref 就是通过 reactive 包装了一个对象 ,然后是将值传给该对象中的 value 属性,这也就解释了为什么每次访问时我们都需要加上 .value

我们可以简单地把 ref(obj) 理解为这个样子 reactive({value: obj})

这里我们写一段代码来具体看一下

<script>
import {ref, reactive} from 'vue'
export default {
  name: 'App',
  setup() {
      const obj = {count: 3}
      const state1 = ref(obj)
      const state2 = reactive(obj)
 
      console.log(state1)
      console.log(state2)
  }
 
}
</script>

注意:  这里指的 .value 是在 setup 函数中访问 ref 包装后的对象时才需要加的,在 template 模板中访问时是不需要的,因为在编译时,会自动识别其是否为 ref 包装过的

那么我们到底该如何选择 ref 和 reactive 呢?

建议:

(1) 基本类型值(String 、Nmuber 、Boolean 等)或单值对象(类似像 {count: 3} 这样只有一个属性值的对象)使用 ref

(2) 引用类型值(Object 、Array)使用 reactive

(5)toRef

toRef 是将某个对象中的某个值转化为响应式数据,其接收两个参数,第一个参数为 obj 对象;第二个参数为对象中的属性名

代码如下:

<script>
// 1\. 导入 toRef
import {toRef} from 'vue'
export default {
    setup() {
        const obj = {count: 3}
        // 2\. 将 obj 对象中属性count的值转化为响应式数据
        const state = toRef(obj, 'count')
 
        // 3\. 将toRef包装过的数据对象返回供template使用
        return {state}
    }
}
</script>

但其实表面上看上去 toRef 这个API好像非常的没用,因为这个功能也可以用 ref 实现,代码如下

<script>
// 1\. 导入 ref
import {ref} from 'vue'
export default {
    setup() {
        const obj = {count: 3}
        // 2\. 将 obj 对象中属性count的值转化为响应式数据
        const state = ref(obj.count)
 
        // 3\. 将ref包装过的数据对象返回供template使用
        return {state}
    }
}
</script>

乍一看好像还真是,其实这两者是有区别的,我们可以通过一个案例来比较一下,代码如下

<template>
    <p>{{ state1 }}</p>
    <button @click="add1">增加</button>
 
    <p>{{ state2 }}</p>
    <button @click="add2">增加</button>
</template>
 
<script>
import {ref, toRef} from 'vue'
export default {
    setup() {
        const obj = {count: 3}
        const state1 = ref(obj.count)
        const state2 = toRef(obj, 'count')
 
        function add1() {
            state1.value ++
            console.log('原始值:', obj);
            console.log('响应式数据对象:', state1);
        }
 
        function add2() {
            state2.value ++
            console.log('原始值:', obj);
            console.log('响应式数据对象:', state2);
        }
 
        return {state1, state2, add1, add2}
    }
}
</script>

我们分别用 ref 和 toRef 将 obj 中的 count 转化为响应式,并声明了两个方法分别使 count 值增加,每次增加后打印一下原始值 obj 和被包装过的响应式数据对象,同时还要看看视图的变化

ref

在对响应式数据的值进行 +1 操作后,视图改变了,原始值未改变,响应式数据对象的值也改变了,这说明 ref 是对原数据的一个拷贝,不会影响到原始值,同时响应式数据对象值改变后会同步更新视图

toRef

在对响应式数据的值进行 +1 操作后,视图未发生改变,原始值改变了,响应式数据对象的值也改变了,这说明 toRef 是对原数据的一个引用,会影响到原始值,但是响应式数据对象值改变后会不会更新视图

总结

  • ref 是对传入数据的拷贝;toRef 是对传入数据的引用
  • ref 的值改变会更新视图;toRef 的值改变不会更新视图

36.js常见的创建方式


js常用的几种创建对象的方式有:

(1) {}

(2) new Object()

(3) 使用字面量

(4) 工厂模式

(5) 构造函数模式(constructor)

(6) 原型模式(prototype)

(7) 构造函数+原型模式

  • 1.通过{}创建对象
    <script>
        'use strict'; //使用strict模式

        /**
        使用{}创建对象,等同于 new Object();
        **/
        var o = {};

        o.name = 'jack';
        o.age = 20;

        o.sayName = function(){
            alert(this.name);
        }

        alert(o.name+'-'+o.age);

        o.sayName();

    </script>
  • 2.通过new Object()创建对象
<script>
        'use strict';
        // 使用 new Object() 创建对象
        var o = new Object();
        o.name = "zhangsna";
        o.sayName = function(){
            alert(this.name);
        }

        o.sayName();

        alert('o instanceof Object>>>>>>>>>>>'+(o instanceof Object));//true
        alert("typeof o >>>>> "+typeof o);//object
    </script>
  • 3.使用字面量创建对象 对象字面变量是对象定义的一种简写形式,举个例子:
    var person = {name: 'zhang', age:20}, 这就是字面量形式,完全等价于var person = {}; person.name='zhang'; person.age=20;

小结:前面三种创建对象的方式存在2个问题:

1.代码冗余; 2.对象中的方法不能共享,每个对象中的方法都是独立的。

  • 4.使用工厂模式创建对象 这种方式是使用一个函数来创建对象,减少重复代码,解决了前面三种方式的代码冗余的问题,但是方法不能共享的问题还是存在。
    <script>
        'use strict';

        // 使用工厂模式创建对象
        // 定义一个工厂方法
        function createObject(name){
            var o = new Object();
            o.name = name;
            o.sayName = function(){
                alert(this.name);
            };
            return o;
        }

        var o1 = createObject('zhang');
        var o2 = createObject('li');

        //缺点:调用的还是不同的方法
        //优点:解决了前面的代码重复的问题
        alert(o1.sayName===o2.sayName);//false

    </script>
  • 5.通过构造函数创建对象 所谓构造函数,也是普通的函数,不过约定俗成,构造函数的名称首字母大写,普通函数的首字母小写。通过new 构造函数来创建对象。
    <script>
        'use strict';

        /**
         *  构造函数模式创建对象
         **/
        function Person(name){
            this.name = name;
            this.sayName = function(){
                alert(this.name);
            };
        }

        var p1 = new Person('zhang');
        var p2 = new Person('li');

        p1.sayName();
        p2.sayName();

        alert(p1.constructor === p2.constructor);//true
        alert(p1.constructor === Person);//true

        alert(typeof(p1));//object

        alert(p1 instanceof Object); //true
        alert(p2 instanceof Object); //trueb

        alert(p1.sayName===p2.sayName);//false

    </script>
  • 6.通过原型模式创建对象
    <script>
        'use strict';

        /*
         *  原型模式创建对象
         */
        function Animal() { }

        Animal.prototype.name = 'animal';
        Animal.prototype.sayName = function () { alert(this.name); };

        var a1 = new Animal();
        var a2 = new Animal();

        a1.sayName();

        alert(a1.sayName === a2.sayName);//true
        alert(Animal.prototype.constructor);//function Animal(){}
        alert(Animal.prototype.constructor==Animal);//true
    </script>

通过原型创建对象,把属性和方法绑定到prototype上,通过这种方式创建对象,方法是共享的,每个对象调用的是同一个方法。

  • 7.通过原型+构造函数的方式创建对象 这种方式结合了上面两种方式,解决了代码冗余,方法不能共享,引用类型改变值的问题。
    <script>
        'use strict';

        function Animal(name){
            this.name = name;
            this.friends = ['dog','cat'];
        }
        Animal.prototype.sayName = function(){
            alert(this.name);
        };
        var a1 = new Animal('d');
        var a2 = new Animal('c');
        a1.friends.push('snake');
        alert(a1.friends);//[dog,cat,snake]
        alert(a2.friends);//[dog,cat]

    </script>

37.==和===的区别


简单来说: == 代表相同, ===代表严格相同

这么理解: 当进行双等号比较时候: 先检查两个操作数数据类型,如果相同, 则进行===比较, 如果不同, 则愿意为你进行一次类型转换, 转换成相同类型后再进行比较, 而===比较时, 如果类型不同,直接就是false.

比较过程:

  双等号==: 

  (1)如果两个值类型相同,再进行三个等号(===)的比较

  (2)如果两个值类型不同,也有可能相等,需根据以下规则进行类型转换在比较:

  • 如果一个是null,一个是undefined,那么相等
  • 如果一个是字符串,一个是数值,把字符串转换成数值之后再进行比较

  三等号===:

  (1)如果类型不同,就一定不相等

  (2)如果两个都是数值,并且是同一个值,那么相等;如果其中至少一个是NaN,那么不相等。(判断一个值是否是NaN,只能使用isNaN( ) 来判断)

  (3)如果两个都是字符串,每个位置的字符都一样,那么相等,否则不相等。

  (4)如果两个值都是true,或是false,那么相等

  (5)如果两个值都引用同一个对象或是函数,那么相等,否则不相等

  (6)如果两个值都是null,或是undefined,那么相等

38.阻止事件冒泡的方式


在阻止事件冒泡触发之前,先了解一下什么是冒泡事件吧!

冒泡事件:比如说鼠标点击了一个按钮,同样的事件将会在那个元素的所有祖先元素中被触发。这一过程被称为事件冒泡。

(1)event.stopPropagation()方法

	$('.btn').click(function (even) {
		even.stopPropagation();
		alert('按钮被点击了');
	})

这是阻止事件的冒泡方法,不让事件向documen上蔓延,但是默认事件任然会执行, 当你掉用这个方法的时候,如果点击一个连接,这个连接仍然会被打开。

例如:

<a href="https://www.csdn.net/" class="box">
	<button class="btn">按钮</button>
</a>

(2)return false ;

$('.btn').click(function (even) {
		alert('按钮被点击了');
		return false;
	})

这个方法比较暴力,他会同事阻止事件冒泡也会阻止默认事件;写上此代码,连接不会被打开,事件也不会传递到上一层的父元素;可以理解为return false就等于同时调用了event.stopPropagation。

39.HTTP请求方法有哪些


根据 HTTP 标准,HTTP 请求可以使用多种请求方法。

HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD 方法。

HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。

image.png

40.rem和em的区别


rem 和 em 都是相对单位,主要参考的标签不同: rem 是相对于根字号,即相对于标签的 font-size 实现的,浏览 器默认字号是 font-size:16px em:是相对于父元素标签的字号,和百分比%类似,%也是相对于父级的, 只不过是%相对于父级宽度的,而 em 相对于父级字号的。

41.实现一个Div上下左右居中的三种方法


其实这个居中的方法还是有很多种的,我能想到的有三种。

第一种:定位: 第一种思路:通过给 div 设置绝对定位,并且 left , right,top,bottom 设置为 0,margin:auto 即可以水平垂直居中。

第二种思路:通过给 div 设置绝对定位,left 为 50%,top 为 50%,再给 div 设置距左是自身的一半即:margin-left:自身宽度/2,margin-top:自身高 度/2。

第三种思路:通过给 div 设置绝对定位,left 为 50%,top 为 50%,再给 div 设置跨左和跟上是自身的一半:transform:translate3d(-50%,-50%,0)。

第四种:flex 布局: 思路:有两个div,父级div和子级div,给父级div设置display:flex, 并且设置父级 div 的水平居中 justify-content:center,并且给父级 div 设置 垂直居中 align-items:center 即可。

42.v-if和v-for的优先级


v-for比v-if优先级高,所以使用的话,每次v-for都会执行v-if,造成不必要的计算,影响性能,尤其是当之需要渲染很小一部分的时候。

vue中v-if和v-for不建议同时使用

<ul>
    <li v-for="(item,index) in list" v-if="item.isActive" :key="item.id">
        {{ item.name }}
    </li>
</ul>

如上例子即使用100个item中只有一个需要展示 使用了v-if也是需要循环整个数组的、这在性能上是极大的浪费。

解决方案

(1)、如何实在循环外部可以在外层嵌套已成template(页面不生成dom节点)、在外层v-if判断、然后在进行使用v-for循环

<template v-if="isShow">
    <div v-for="(item,index) in list" v-if="item.isActive" :key="item.id">
        {{ item.name }}
    </div>
</template>

(2)、如果条件出现在循环内部、可以通过计算属性computed提前过滤掉那些不需要展示的数据

computed: {
    listData: function() {
        return this.list.filter(function (item) {
            return item.isShow
        })
    }
}

注意事项

(1) 永远不要把 v-if 和 v-for 同时用在同一个元素上,带来性能方面的浪费(每次渲染都会先循环再进行条件判断)

(2) 如果避免出现这种情况,则在外层嵌套template(页面渲染不生成dom节点),在这一层进行v-if判断,然后在内部进行v-for循环

43.router传参的方式


传参方式可划分为 params 传参和 query 传参,而 params 传参又可分为在 url 中显示参数和不显示参数两种方式

(1)params 传参(显示参数)又可分为 声明式 和 编程式 两种方式

  • 声明式router-link:该方式是通过router-link组件的to属性实现,子路由需要提前配置好参数。
<router-link :to="/child/1"> 跳转到子路由 </router-link>
{
 path: '/child/:id',
 component: Child
}
  • 编程式 this.$router.push:同样需要子路由提前配置好参数。
{
 path: '/child/:id',
 component: Child
}
this.$router.push({
  path:'/child/${id}',
})

接收:  this.$route.params.id

(2)params传参(不显示参数)也可分为声明式和编程式两种方式,与显示参数不同的是,这里是通过路由的别名 name 进行传值的

<router-link :to="{name:'Child',params:{id:1}}">跳转到子路由</router-link>
{
 path: '/child,
 name: 'Child',
 component: Child
}
this.$router.push({
  name:'Child',
  params:{
   id:1
  }
})

接收:  this.$route.params.id

(3)query 传参(显示参数)也可分为声明式和编程式 两种方式

  • 声明式router-link:该方式是通过 router-link 组件的 to 属性实现,不过使用该方式传值的时候,需要子路由提前配置好路由别名
{
 path: '/child,
 name: 'Child',
 component: Child
}
<router-link :to="{name:'Child',query:{id:1}}">跳转到子路由</router-link>
  • 编程式 this.$router.push:使用该方式传值的时候,同样需要子路由提前配置好路由别名(name 属性)
{
 path: '/child,
 name: 'Child',
 component: Child
}
this.$router.push({
  name:'Child',
  query:{
   id:1
  }
})

接收:  this.$route.query.id

44.v-if和v-show的区别


v-if 和 v-show 都可以显示和隐藏一个元素,但有本质区别 v-if 是惰性的,只是值为 false 就不会加载对应元素,为 true 才动态加 载对应元素 v-show:是无论为 true 和为 false 都会加载对应 html 代码,但为 false 时用 display:none 隐藏不在页面显示,但为 true 时页面上用 display:block 显示其效果 适用场景:切换频繁的场合用 v-show,切换不频繁的场合用 v-if。

45.从输入 URL 到页面加载完中间发生了什么


DOM 树 构建一个样式表.组合成一颗 render 树,页面经过重绘(重塑)和回流 的过程. 答:大致过程是这样的:

(1). DNS 解析

(2). TCP 连接

(3). 发送 HTTP 请求

(4). 服务器处理请求并返回需要的数据

(5). 浏览器解析渲染页面

(6). 连接结束

输入了一个域名,域名要通过 DNS 解析找到这个域名对应的服务器地址(ip),通过 TCP 请求链接服务,通过 WEB 服务器(apache)返回数据,浏览器根据返回数据构建 DOM 树,再把 css 形成一个样式表.这两个东西结合,变成了 render 树.页面上通过重绘和回流的过程,渲 染出页面来。

46.Number和parseint的区别


(1)当转换的内容包含非数字的时候,Number () 会返回NaN (Not a Number);parseInt () 要看情况,如果以数字开头,就会返回开头的合法数字部分,如果以非数字开头,则返回NaN。

(2)、parseInt ()仅返回整数值的区别,而Number ()返回包括浮点的所有数字。

47.axios的封装


(1)引入axios

import axios from 'axios'

(2)创建一个实例

const api = axios.create({
    baseURL: '', // 所有请求的公共地址部分
    timeout:  3000 // 请求超时时间 这里的意思是当请求时间超过5秒还未取得结果时 提示用户请求超时
})

(3)request拦截器

// 请求相关处理 请求拦截 在请求拦截中可以补充请求相关的配置
// interceptors axios的拦截器对象
api.interceptors.request.use(config => {
     // config 请求的所有信息
    // console.log(config);
    return config // 将配置完成的config对象返回出去 如果不返回 请求讲不会进行
}, err => {
    // 请求发生错误时的相关处理 抛出错误
   Promise.reject(err)
})

(4)response拦截器

api.interceptors.response.use(res => {
    // 我们一般在这里处理,请求成功后的错误状态码 例如状态码是500,404,403
    // res 是所有相应的信息
    console.log(res)
   return Promise.resolve(res)
}, err => {
    // 服务器响应发生错误时的处理
    Promise.reject(err)
})

(5)抛出

export default api

引入api并封装接口

import api from '../index.js';

下面是简写的形式
// getXXX 自定义的接口名字
export const getXXX = (params) => api.get(`/apigb/v1/component`, { params})

export const postXXX = (params) => api.post(`/apigb/v1/component/update-info`, params)


// 下面是详细的写法
export const xxxx = (params) => api({
        url: '', // 请求地址
        method: 'post', // 请求方式
        // data: params, // (一般post请求,我们习惯使用 data属性来传参)
        params: params //(一般get请求,我们习惯使用params属性来传参)
        // 注意:data,和 params 两个属性传参使用,并不是固定的,也可以调换使用。
})

48.VueRouter的原理


前端路由实现原理主要通过以下两种技术实现的:

第一种:利用 H5 的 history API 实现 主要通过 history.pushState 和 history.replaceState 来实 现,不同之处在于,pushState 会增加一条新的历史记录,而 replaceState 则会替换当前的历史记录[发布项目时,需要配置下 apache]。

第二种:利用 url 的 hash 实现 我们经常在 url 中看到 #,这个 # 有两种情况,一个是我们所谓 的锚点,路由里的 # 不叫锚点,我们称之为 hash,我们说的就是 hash , 主要利用监听哈希值的变化来触发事件 —— hashchange 事件来 做页面局部更新

总结:hash 方案兼容性好,而 H5 的 history 主要针对高级浏览器。

以下为具体的 API 的区别

this.$router.push(location, onComplete?, onAbort?) 这个方法会向 history
栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。并且点击
<router-link :to="...">等同于调用 router.push(...)。
this.$router.replace(location, onComplete?, onAbort?) 这个方法不会向
history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录,所以,当
用 户点击浏览器后退按钮时,并不会回到之前的 URL。
this.$router.go(n) 这个方法的参数是一个整数,意思是在 history 记录中向前或者后
退多少步,类似 window.history.go(n)。
以上也可能会形成一个新的面试题:replace 和 push 的区别
可以说,以前在一个项目里面配置了一个二级路由,里面有 tab 切换部分(详情,评
价,说明),因为返回上一页的时候,不能在这几个选项卡之间来回切换.所以我使用了
this.$router.replace 方法,不计入 history 记录中,所以不会出现,反复切换的
bug

49.浏览器的存储方式以及区别


有三种存储方式,分别是:

(1) 第一种是cookie存储,这类存储只能保存一段不超过4k的文本信息,通常是用来保存我们所浏览的网址之类的。可以通过浏览器的历史记录进行查看。

(2)第二种则是session存储,通俗来讲就类似于网页微博之类的密码保存。在你关闭网页后,再次打开,所存储的内容便会消失。

(3)第三种则是localstorage存储,这类存储类似于我们生活中经常用到的下载,比如说我们电脑、手机上面的软件、应用等。这类存储的好处是可以永久保存.

sessionStorage 、localStorage 和 cookie 之间的共同点:

都是保存在浏览器端,且同源的。

sessionStorage 、localStorage 和 cookie 之间的区别:

cookie数据始终在同源的http请求中携带(即使不需要),即cookie在浏览器和服务器间来回传递。而sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存。cookie数据还有路径(path)的概念,可以限制cookie只属于某个路径下。

存储大小限制也不同,cookie数据不能超过4k,同时因为每次http请求都会携带cookie,所以cookie只适合保存很小的数据,如会话标识。sessionStorage和localStorage 虽然也有存储大小的限制,但比cookie大得多,可以达到5M或更大。

数据有效期不同,sessionStorage:仅在当前浏览器窗口关闭前有效,自然也就不可能持久保持;

localStorage:始终有效,窗口或浏览器关闭也一直保存,因此用作持久数据;cookie只在设置的cookie过期时间之前一直有效,即使窗口或浏览器关闭。

作用域不同,sessionStorage不在不同的浏览器窗口中共享,即使是同一个页面;localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。

Web Storage 支持事件通知机制,可以将数据更新的通知发送给监听者。

Web Storage 的 api 接口使用更方便。

50.闭包以及解决的方法


闭包说的通俗一点就是可以在函数外部访问到函数内部的变量。因为正常 情况下函数外部是访问不到函数内部作用域变量的,作用域分为了全局函数 级块级作用域.

表象判断是不是闭包:函数嵌套函数,内部函数被 return 内部函数调用外层 函数的局部变量。

优点:可以隔离作用域,不造成全局污染

缺点:由于闭包长期驻留内存,则长期这样会导致内存泄露

如何解决内存泄露:将暴露外部的闭包变量置为 null

51.css样式优先级


CSS 优先规则1: 最近的祖先样式比其他祖先样式优先级高。

<!-- 类名为 son 的 div 的 color 为 blue -->
<div style="color: red">
    <div style="color: blue">
        <div class="son"></div>
    </div>
</div>

如果我们把一个标签从祖先那里继承来的而自身没有的属性叫做“祖先样式”,那么“直接样式”就是一个标签直接拥有的属性。又有如下规则:

CSS 优先规则2:“直接样式”比“祖先样式”优先级高。

例2:

<!-- 类名为 son 的 div 的 color 为 blue -->
<div style="color: red">
    <div class="son" style="color: blue">
    </div>
</div>

CSS 优先规则3:优先级关系:内联样式 > ID 选择器 > 类选择器 = 属性选择器 = 伪类选择器 > 标签选择器 = 伪元素选择器

例3:

// HTML
<div class="content-class" id="content-id" style="color: black"></div>

// CSS
#content-id {
    color: red;
}
.content-class {
    color: blue;
}
div {
    color: grey;
}
最终的 color 为 black,因为内联样式比其他选择器的优先级高。 所有 CSS 的选择符由上述 7 种基础的选择器或者组合而成,组合的方式有 3 种:
  • 后代选择符: .father .child{}\

  • 子选择符: .father > .child{}\

  • 相邻选择符: .bro1 + .bro2{}

当一个标签同时被多个选择符选中,我们便需要确定这些选择符的优先级。我们有如下规则:

CSS 优先规则4:计算选择符中 ID 选择器的个数(a),计算选择符中类选择器、属性选择器以及伪类选择器的个数之和(b),计算选择符中标签选择器和伪元素选择器的个数之和(c)。按 a、b、c 的顺序依次比较大小,大的则优先级高,相等则比较下一个。若最后两个的选择符中 a、b、c 都相等,则按照“就近原则”来判断。

例4:

// HTML
<div id="con-id">
    <span class="con-span"></span>
</div>

// CSS
#con-id span {
    color: red;
}
div .con-span {
    color: blue;
}

由规则 4 可见, 的 color 为 red。

如果外部样式表和内部样式表中的样式发生冲突会出现什么情况呢?这与样式表在 HTML 文件中所处的位置有关。样式被应用的位置越在下面则优先级越高,其实这仍然可以用规则 4 来解释。

例5:

// HTML
<link rel="stylesheet" type="text/css" href="style-link.css">
<style type="text/css">
    @import url(style-import.css); 
    div {
        background: blue;
    }
</style>

<div></div>

// style-link.css
div {
    background: lime;
}

// style-import.css
div {
    background: grey;
}

从顺序上看,内部样式在最下面,被最晚引用,所以

的背景色为 blue。

上面代码中,@import 语句必须出现在内部样式之前,否则文件引入无效。当然不推荐使用 @import 的方式引用外部样式文件,原因见另一篇博客:CSS 引入方式

CSS 还提供了一种可以完全忽略以上规则的方法,当你一定、必须确保某一个特定的属性要显示时,可以使用这个技术。

CSS 优先规则5:属性后插有 !important 的属性拥有最高优先级。若同时插有 !important,则再利用规则 3、4 判断优先级。

例6:

// HTML
<div class="father">
    <p class="son"></p>
</div>

// CSS
p {
    background: red !important;
}
.father .son {
    background: blue;
}

虽然 .father .son 拥有更高的权值,但选择器 p 中的 background 属性被插入了 !important,所以

的 background 为 red。

52.flex的常见属性


  • flex-direction  主轴的方向  即项目的排列方式  x  y  123 321
  • flex-wrap  如果一排放不下如何换行
  • flex-flow   以上两个属性的简写 默认值是 row nowrap
  • justify-content   定义了项目在X轴上的对其方式
  • align-items   定义项目在Y轴上的对其方式
  • align-content  定义了多根轴线的对其方式

53.常见获取Dom元素的方法


  • document.querySelector(‘选择器’)
  • document.querySelectorAll(‘选择器’)
  • document.getElementByid('id名')
  • document.getElementClassName(‘类名’)
  • document.getElementByName(‘标签名’)
  • document.getElementByTagName(‘标签名’)

54.清除浮动的方法


清除浮动的四种方式

  1. 额外标签法:最后一个浮动标签末尾添加一个空标签
// 在最后一个浮动标签的末尾添加 如下 
<div class="clear"></div>

.clear {
  clear: both;
}
  1. 父级添加 overflow 属性方法
// 给父级的盒子添加 overflow:hidden;
<div class="father hidden"></div>

.hidden {
    overflow: hidden;
}
  1. 使用 after 伪元素清除浮动(最常用,重点)
// 在父元素中添加 伪类元素清除浮动
.clearfix:after {
    content: "";
    display: block;
    height: 0;
    visibility: hidden;
    clear: both;
}

注意:如果不了解伪类元素是什么的话,可以先把这种方式记下来,后续中我们自然会讲到伪元素。伪元素相当于额外空标签的升级版。

  1. 使用双伪元素清除浮动
// 给父类添加一个 clearfix
.clearfix: before,
.clearfix: after {
    content: "";
    display: table
}
.clearfix: after {
    clear: both;
}
.clearfix {
    *zoom: 1;
}

以上就是清除浮动的四种方式了,可以下去自己在代码中练习练习,这四种方式第三种是非常常用的,在各大网站上都是高频的出现,暂时就不需要了解它的意思在后续的 css 中学完会逐个分析每句的意思。先学会使用就可。

55.activated和deactivated的区别


activated 类型:function

触发时机:keep-alive组件激活时使用;

deactivated 类型:function

触发时机:keep-alive组件停用时调用;

56.如何封装一个模态框


1,思路

首先我们不难发现,模态框有以下特点:

  • 有一个半透明蒙层,防止用户没有做完模态框的命令就操作其它内容
  • 显示在最顶层,这说明很多模态框是直接放在body元素中的

我先开始就想,能不能先在vue文件中把模态框样式定义好,然后利用props或者slot传值、利用refs调用其方法呢?后来发现这样不仅不方便(每次都要注册组件)、还无法把模态框直接放在body里面。

参考了官方文档,我发现其实再写一个配套的js文件,在js文件中定义显示模态框的函数,借助这个js在body元素下创建一个元素,并将vue组件挂载上去即可。下面我们就一一来实现。

2,编写vue文件,完成模态框基本样式

首先需要完成一些对话框的基本样式,我在components目录下创建vue文件,内容如下:

<template>
  <div class="mydialog">
    <div class="frame">
      <div class="content">
        <img :src="image" />
        <div class="text">{{ text }}</div>
      </div>
      <div class="buttons">
        <div class="ok" @click="clickOk">确定</div>
        <div class="cancel" @click="clickCancel">取消</div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'mydialog',
  data() {
    return {
      /**
       * 显示文字
       */
      text: '',
      /**
       * 显示图片
       */
      image: '',
      /**
       * 模态框挂载的DOM元素,用于后面销毁模态框
       */
      mountDom: '',
    };
  },
  methods: {
    /**
     * 自定义确定事件
     */
    ok() {},
    /**
     * 自定义取消事件
     */
    cancel() {},
    clickOk() {
      // 先执行自定义确定方法
      this.ok();
      // js传入该模态框实例挂载的元素,关闭时将其销毁
      this.mountDom.remove();
    },
    clickCancel() {
      // 先执行自定义取消方法
      this.cancel();
      // js传入该模态框实例挂载的元素,关闭时将其销毁
      this.mountDom.remove();
    },
  },
};
</script>

<style lang="scss" scoped>
.mydialog {
  position: absolute;
  width: 100vw;
  height: 100vh;
  left: 0;
  top: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: rgba(255, 255, 255, 0.7);

  .frame {
    width: 560px;
    height: 250px;
    background-color: rgb(177, 255, 229);
    box-shadow: 1px 1px 7px 2px rgb(90, 203, 255);
    border-radius: 5px;
    display: flex;
    flex-direction: column;
    justify-content: space-evenly;
    align-items: center;

    .content {
      display: flex;
      width: 85%;
      justify-content: flex-start;
      align-items: center;

      img {
        height: 64px;
      }

      .text {
        font-size: 24px;
        margin-left: 24px;
      }
    }

    .buttons {
      display: flex;
      width: 80%;
      justify-content: space-around;
      align-items: center;
      font-size: 26px;

      .ok,
      .cancel {
        width: 75px;
        height: 32px;
        text-align: center;
        line-height: 32px;
        border-radius: 6px;
        user-select: none;
        cursor: pointer;
      }

      .ok {
        &:hover {
          color: white;
          background-color: blue;
        }
      }

      .cancel {
        &:hover {
          color: white;
          background-color: rgb(255, 0, 98);
        }
      }
    }
  }
}
</style>

里重点是在data()中我定义了一些变量,用于我们可以自定义模态框内容。

问题是,模态框这么销毁自己呢?就算是使用v-if关闭了自己,但是前面提到了,这个模态框是要挂载至一个元素中的,这个挂在元素无法被消除怎么做呢?

所以可以看到上面还有一个mountDom变量,让js创建挂载元素之后将其传入,这样vue就可以直接销毁这个挂载元素了!

除此之外,还预留了okcancel两个函数用于按下“确定”和“取消”之后的自定义事件,传入自定义的函数赋值给okcancel即可。

3,编写js文件,封装方法以供调用,给模态框组件传参

在同级目录下创建js文件,内容如下:

import {
	createApp
} from 'vue';

import MyDialog from './MyDialog.vue';

/**
 * 显示自定义对话框
 * @param {String} text 对话框提示内容 
 * @param {NodeRequire} image 对话框图标(需要require图片路径)
 * @param {Function} ok 自定义确定事件
 * @param {Function} cancel 自定义取消事件
 */
export function showDialog(text, image, ok, cancel) {
	// 创建一个div元素放到body下面,供模态框vue组件挂载
	let mountDom = document.createElement('div');
	mountDom.style.position = 'absolute';
	mountDom.style.left = 0;
	mountDom.style.top = 0;
	document.body.appendChild(mountDom);
	// 挂载组件以显示,mount函数返回vue组件实例
	let dialog = createApp(MyDialog).mount(mountDom);
	// 设定vue实例中的变量,将一些数据传入vue
	dialog.text = text;
	dialog.image = image;
	dialog.ok = ok;
	dialog.cancel = cancel;
	// 传入挂载的dom是为了vue组件关闭时可以直接销毁这个挂载的dom达到销毁模态框的目的
	dialog.mountDom = mountDom;
}

可见这里就很简单了。定义一个函数,先创建一个div元素加到body中用于挂载对话框组件,然后利用createApp函数挂载这个对话框组件至创建的div元素之下即可。

看到这,大家也知道了怎么在js中,向vue实例传递值或者调用函数了。

在官方文档中,对于createApp函数,有这样一句话:

与大多数应用方法不同的是,mount不返回应用本身。相反,它返回的是根组件实例。

image.png 我们知道,每一个vue组件可以被作为一个实例使用,那么mount函数,不仅仅是挂载了组件至指定的元素下,还会返回其实例,我们也可以调用这个实例中的变量、函数,实现传递参数的目的。

mount函数中,可以是dom元素实例,也可以是字符串形式的选择器(例如'#app',挂载到id为app的元素下)。

4,测试

现在我们在根组件里面写一个按钮并调用试试:

<template>
  <div class="app">
    <button @click="testShowDialog">显示模态框</button>
  </div>
</template>

<script>
// 先引入js中显示对话框函数
import { showDialog } from './components/mydialog.js';

export default {
  name: 'app',
  methods: {
    testShowDialog() {
      showDialog(
        '提示:少时诵诗书所所所所所所所所所所所所所所所所所所所所所所所所所所所所所所所',
        require('./assets/1.png'),
        () => {
          console.log('点击了确定');
        },
        () => {
          console.log('点击了取消');
        }
      );
    },
  },
};
</script>

57.说一下 vue 路由钩子(或称 vue 路由守卫)的理解


什么场景下用到 答:vue 路由钩子是在路由跳转过程中拦截当前路由和要跳转的路由的信息, 有三种路由钩子:

第一种:全局路由钩子 beforeEach(to,from,next) { }

第二种:路由独享的钩子 beforeEnter(to,from,next) { }

第三种:组件内的钩子

beforeRouteEnter(to,from,next) { //… }

beforeRouteUpdate(to,from,next) { //… }

beforeRouteLeave(to,from,next) { //… }

适用场景:动态设置页面标题,判断用户登录权限等:代码示例:

//全局路由导航守卫

const nextRoute = [ 'detail'];
const auth = sessionStorage.getItem("username");
let FROMPATH = from.path;
//跳转至上述 3 个页面
if (nextRoute.indexOf(to.name) >= 0) {
//上述数组中的路径,是相当于有权限的页面,访问数组列表中的页面
就应该是在登陆状态下
if (!auth) {
let params = Object.assign({frompath:FROMPATH},from.query);
next({path: '/newlogin',query:params});
}
}
//已登录的情况再去登录页,跳转至首页
if (to.name === 'newlogin') {
if (auth) {
//
vueRouter.push({name: 'index'});
next({path: '/'});
}
}
next();
});

58.XMLHttpRequest的基本使用


1.使用xhr发起GET请求

步骤

  • 创建 xhr 对象
  • 调用 xhr.open() 函数
  • 调用 xhr.send() 函数
  • 监听 xhr.onreadystatechange 事件
// 1. 创建 XHR 对象
var xhr = new XMLHttpRequest()
// 2. 调用 open 函数
xhr.open('GET', 'url')
// 3. 调用 send 函数
xhr.send()
// 4. 监听 onreadystatechange 事件
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
     // 获取服务器响应的数据
     console.log(xhr.responseText)
   }
}

了解xhr对象的readyState属性

XMLHttpRequest 对象的 readyState 属性,用来表示当前 Ajax 请求所处的状态。每个 Ajax 请求必然处于以 下状态的一个:

image.png

2.使用xhr发起带参数的GET请求

使用 xhr 对象发起带参数的 GET 请求时,只需在调用 xhr.open 期间,为 URL 地址指定参数即可:

xhr.open('GET', 'url')
xhr.send()
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
     console.log(xhr.responseText)
  }
}

查询字符串

定义:查询字符串(URL 参数)是指在 URL 的末尾加上用于向服务器发送信息的字符串(变量)。

格式:将英文的 ? 放在URL 的末尾,然后再加上 参数=值 ,想加上多个参数的话,使用 & 符号进行分隔。以

这个形式,可以将想要发送给服务器的数据添加到 URL 中。

GET请求携带参数的本质

无论使用 .ajax(),还是使用.ajax(),还是使用 .get(),又或者直接使用 xhr 对象发起 GET 请求,当需要携带参数的时候,本质上,都是直接将参数以查询字符串的形式,追加到 URL 地址的后面,发送到服务器的。

$.get('url', { name'xiao'age18 }, function () { })

    // 等价于

    $.get('url?name=xiao&age=18'function () { })

使用xhr发起POST请求

var xhr = new XMLHttpRequest()
// 2. 调用 open 函数
xhr.open('POST', 'http://www.liulongbin.top:3006/api/addbook')
// 3. 设置 Content-Type 属性(固定写法)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
// 4. 调用 send 函数
xhr.send('bookname=水浒传&author=施耐庵&publisher=上海图书出版社')
// 5. 监听事件
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText)
  }
}

59.强缓存和协商缓存机制的区别


HTTP 缓存策略分为两种: 强缓存 和 协商缓存 ,这两种缓存策略都是服务端设置 HTTP [Header] 来实现的。

(1)强缓存 强缓存的意思很简单,直接从浏览器缓存过的本地进行读取,不会去请求服务器。例如请求一个图片,当缓存后,第二次访问,直接从本地去拿,不会再去请求这个资源,可以节省服务器资源。在chrome控制台的Network选项中可以看到该请求返回200的状态码。

(2)协商缓存 协商缓存表示在使用本地的缓存之前,会先向服务器发一个请求,与服务器协商当前浏览器的缓存是否已经过期了,如果没过期,那么就使用本地的资源,如果过期了就去请求最新资源。协商缓存主要是解决强缓存资源不能及时更新的问题

(3)共同点和区别

共同点:都是从客户端缓存中读取资源

区别:是强缓存不会发请求,协商缓存会发请求。

60.谈谈你对原型链的理解


原型链是理解 JS 面向对象很重要的一点,这里主要涉及到两个点,一是 _ _ proto_ ,二是 prototype,举个例子吧,这样还好说点,例如:我用 function 创建一个 Person 类,然后用 new Person 创建一个对象的实例假如叫 p1 吧,在 Person 类的原型 prototype 添加一个方法,例如:play 方法,那对象实例 p1 如何查找到 play 这个方法呢,有一个查找过程,具体流程是这样的: 首先在 p1 对象实例上查找是否有 play 方法,如果有则调用执行,如果没 有则用 p1.proto(_proto_是一个指向的作用,指向上一层的原型)往创建 p1 的类的原型上查找,也就是说往 Person.prototype 上查找,如果在 Person.prototype 找 到 play 方 法 则 执 行 , 否 则 继 续 往 上 查 找 , 则 用 Person.prototye.__proto__继续往上查找,找到 Object.prototype,如果 Object.prototype有play方法则执行之,否则用Object.prototype.proto 继续再往上查找,但 Object.prototpye.__proto__上一级是 null,也就是原型 链的顶级,结束原型链的查找,这是我对原型链的理解 Tips 附加分.你像我最近做的项目里面有些公共常用的方法.我放到了 utils 里 面.然后在 vue 的 main.js 里面导入进来.然后通过 Vue.prototype.方法名绑定 到原型上,就利用了这种原型链的原理。

61.页面的性能优化


性能优化从何入手

1、让加载更快 --减少资源体积:压缩代码 图片 --减少访问次数:合并代码(js合并,css合并,图片合拼雪碧图 (sprite)),SSR服务器端渲染,缓存 --使用更快的网络:CDN

2、让渲染更快 --css放在head,js放在body最下面 --今早开始执行js,用DOMContentLoaded触发 --懒加载(图片懒加载,上滑加载更多) --对DOM查询进行缓存 --频繁DOM操作,合并到一起插入DOM结构 --节流throttle和防抖debounce

缓存

--静态资源加hash后缀,根据文件内容计算hash --文件内容不变,则hash不变,则url不变 --url和文件不变,则会自动触发http缓存机制,返回304

SSR --服务器端渲染:将网页和数据一起加载,一起渲染 --非SSR(前后端分离):先加载网页,再加载数据,再渲染数据 --早先的JSP ASP PHP,现在的vue React SSR

62.table布局的弊端


Table布局的缺点是比其它html标记占更多的字节,会阻挡浏览器渲染引擎的渲染顺序,会影响其内部的某些布局属性的生效,优点就是用table做表格是完全正确的。

63.你对vuex的理解

vuex 是一个状态管理工具,主要解决大中型复杂项目的数据
共享问题,主要包括 state,actions,mutations,getters和 modules 5个要素,
主要流程:组件通过 dispatch 到 actions,actions 是异步操作,再 actions
中通过 commit 到 mutations,mutations 再通过逻辑操作改变 state,从而同
步到组件,更新其数据状态。

64.BFC的理解

它就是一个 block format content 块级格式化上下文.是一个布局里面的概念.把一个盒子 设置成 bfc 之后,里面无论怎么布局.都不会影响外面的变动.另外如果是一个 bfc 盒子.浮 动的元素也会参数计算.用它可以解决一些布局方面的问题吧.比方说.margin 重叠.高度塌 陷等. overflow:hidden.float. display:inline-block。

65.v-html是做什么用的

v-html用于输出html,它与v-text区别在于v-text输出的是纯文本,浏览器不会对其再进行html解析,但v-html会将其当html标签解析后输出。 v-model通常用于表单组件的绑定,例如input,select等。

66.Vue按钮的权限控制


参考文献: juejin.cn/post/709792…