本文将不定期持续更新,欢迎您收藏本站!😘

由于本文篇幅较长,您可以点击 ctrl+F 直接搜索关键字。

注意:部分答案来源于网络,如发现有错误,请联系我修正,谢谢!

HTTP

从输入url到页面加载发生了什么

查看答案

大致过程如下:

  1. DNS 解析:将域名解析成 IP 地址;
  2. TCP 连接:TCP 三次握手;
  3. 浏览器向服务器发送 HTTP 请求;
  4. 服务器处理 HTTP 请求,并返回给浏览器;
  5. 浏览器解析并渲染页面;
  6. 关闭连接:TCP 四次挥手;

HTTP的状态码及含义


常见的http headers


HTTP缓存有哪几种

查看答案

HTTP 缓存可分为两大类,分别是:强制缓存和协商缓存。

强制缓存在缓存数据未生效的情况下,是不需要再和服务器发生交互的;而协商缓存,顾名思义是需要跟服务器进行比较判断是否可以使用缓存。

注意:强制缓存的优先级高于协商缓存的


强制缓存:

强制缓存分两种情况,分别是 Expires 和 Cache-Control。

  1. Expires:它的值是服务器告诉浏览器的缓存过期时间的,是一个绝对时间,顾名思义是下次请求时,如果当前距上次请求时间还未超过过期时间时,就直接使用缓存数据。但是存在一些问题:
    • 客户端和服务器的时间不一致会出现问题;
    • 在缓存未生效前,获取不到修改后的资源;
  2. Cache-Control:它是设置相对时间,它可以解决 Expires 出现的问题。

Cache-Control 有以下可选值:

  1. no-cache:不直接使用缓存,根据新鲜度来使用缓存;
  2. no-store:不使用缓存,每次都是请求下载新资源;
  3. max-age:设置缓存存储的最大周期,超过这个时间就被认为过期;
  4. public / private:是否只能被单个用户使用,默认值为 private;
  5. must-revalidate:每次访问需要缓存校验。

注意:Cache-Control 优先级高于 Expires。


协商缓存:

它是服务器端缓存策略,相关属性有:ETag / if-Not-Match、Last-Modified / If-Modified-Since。

执行流程如下:

  1. 当浏览器第一次向服务器发送请求时,会在响应头中返回协商缓存的头属性:ETag 和 Last-Modified,其中 ETag 返回的是一个 hash 值,Last-Modified 返回的是 GMT 格式的最后修改时间。
  2. 浏览器在第二次发送请求的时候,会在请求头中带上与 ETag 对应的 if-Not-Match,其值就是响应头中返回的 ETag 的值,再带上 Last-Modified 对应的 If-Modified-Since。服务器在接收到这两个参数后会做比较,如果返回的是 304 状态码,则说明请求的资源没有修改,浏览器可以直接在缓存中取数据,否则,服务器会直接返回数据。

它们的优先级是:Cache-Control > Expires > ETag > Last-Modified。


我们先来看看三种刷新:

  1. 正常操作:地址栏输入 url,跳转链接,前进后退等;
  2. 手动刷新:F5,右键刷新,刷新按钮;
  3. 强制刷新:ctrl + F5;

使用不同的刷新操作,会有不同的缓存策略:

  • 正常操作:强制缓存有效,协商缓存有效;
  • 手动刷新:强制缓存失效,协商缓存有效;
  • 强制刷新:强制缓存失效,协商缓存失效;

Cookie、Session、LocalStorage之间的区别


GET和POST的区别

查看答案
  1. GET 请求一般去获取数据,POST 请求一般发送数据到后台使用;
  2. GET 请求的参数在 url 上可见,而 POST 请求的参数放在 Request body 中;
  3. GET 请求参数的长度有限制,POST 请求没有长度限制;
  4. GET 请求刷新浏览器或回退没有影响,POST 请求回退会重新提交数据;
  5. GET 请求可被缓存,POST 请求不会;
  6. GET 请求保留在浏览器历史记录里,POST 请求不会;
  7. GET 请求可被收藏为书签,POST 请求不能;
  8. GET 请求只能进行 url 编码,POST 请求支持多种编码方式;
  9. GET 请求只需要一个报文,POST 请求需要两个及以上;

其实这两种请求最本质的区别就是语义不同,GET 请求是获取数据,而 POST 请求是提交数据


HTTP与HTTPS的区别

查看答案
  1. http 是以 http:// 开头,https 是以 https:// 开头;
  2. http 默认使用 80 端口,而 https 默认使用 443 端口;
  3. http 是超文本传输协议,信息采用明文传输,而 https 则是具有安全性 SSL 加密传输协议;
  4. https 协议需要到证书颁发机构 CA 申请证书,大多数情况下需要一定费用;

描述一下TCP的三次握手和四次挥手


减少http请求的方法

查看答案
  1. 使用 CSS 雪碧图;
  2. base64 编码图片;
  3. 缩小 JS 和 CSS 文件;
  4. 合并 JS 和 CSS 文件;
  5. 图片地图(Image Maps);
  6. 使用浏览器 缓存机制。

http1与http2的区别

查看答案
  1. 新的二进制格式(Binary Format),HTTP1.x 的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认 0 和 1 的组合。基于这种考虑 HTTP2.0 的协议解析决定采用二进制格式,实现方便且健壮。
  2. 多路复用(MultiPlexing),即连接共享,即每一个 request 都是是用作连接共享机制的。一个 request 对应一个 id,这样一个连接上可以有多个 request,每个连接的 request 可以随机的混杂在一起,接收方可以根据 request 的 id 将 request 再归属到各自不同的服务端请求里面。
  3. header 压缩,HTTP1.x 的 header 带有大量信息,而且每次都要重复发送,HTTP2.0 使用 encoder 来减少需要传输的 header 大小,通讯双方各自 cache 一份 header fields 表,既避免了重复 header 的传输,又减小了需要传输的大小。
  4. 服务端推送(server push),同 SPDY 一样,HTTP2.0 也具有 server push 功能。

https对称加密与非对称加密是什么

查看答案
  1. 对称加密:发送方和接收方需要持有同一把密钥,在发生消息和接收消息均使用该密钥。相对于非对称加密来说,对称加密具有更高的加解密速度,但是双方都需要事先知道密钥。密钥在传输过程中可能会被窃取,因此对于安全性而言,对称加密没有非对称加密安全性高;
  2. 非对称加密:接收方在发生消息前需要事先生成公钥和私钥,然后将公钥发送给发送方。发送方收到公钥后,将待发送数据用公钥加密然后发送给接收方,接收方收到数据后用私钥解密。其中公钥负责加密、私钥负责解密,在数据传输过程中,如果被窃取,由于攻击者没有私钥也无法进行破解。非对称加密的加解密速度要低于对称加密,但是安全性更高;

如何劫持https的请求


HTML

如何理解HTML语义化

查看答案

HTML 语义化就是使用正确的标签做正确的事;比如段落就用 p 标签,页眉就用 header 标签,页脚用 footer 标签,导航用 nav 标签,文章就用 article 标签,视频用 video 标签等等。

HTML 语义化的好处有:

  1. 易于用户阅读:在没有 CSS 情况下,页面也能够更好的呈现内容结构与代码结构;
  2. 便于团队的开发和维护:更具有可读性,让代码更好的维护;
  3. 有利于 SEO:搜索引擎的爬虫依赖于标签来确定上下文和各个关键字的权重;
  4. 利于其他设备的解析:如屏幕阅读器、盲人阅读器,提高可访问性。

meta viewport是做什么用的,怎么写

查看答案
1
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
参数 含义
width 设置 layout viewport 的宽度,为一个正整数,或字符串”device-width”
initial-scale 设置页面的初始缩放值,为一个数字,可以带小数
minimum-scale 允许用户的最小缩放值,为一个数字,可以带小数
maximum-scale 允许用户的最大缩放值,为一个数字,可以带小数
height 设置 layout viewport 的高度,这个属性对我们并不重要(很少使用)
user-scalable 是否允许用户进行缩放
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html> H5标准声明,使用 HTML5 doctype,不区分大小写
<head lang="en"> 标准的 lang 属性写法
<meta charset="utf-8"> 声明文档使用的字符编码
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> 优先使用 IE 最新版本和 Chrome
<meta name="description" content="不超过150个字符" /> 页面描述
<meta name="keywords" content="" /> 页面关键词
<meta name="author" content="name, email@gmail.com" /> 网页作者
<meta name="robots" content="index,follow" /> 搜索引擎抓取
<meta name="viewport" content="initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no"> 为移动设备添加 viewport
<meta name="apple-mobile-web-app-title" content="标题" /> iOS 设备 begin
<meta name="apple-mobile-web-app-capable" content="yes" /> 添加到主屏后的标题(iOS 6 新增)
是否启用 WebApp 全屏模式,删除苹果默认的工具栏和菜单栏
<meta name="apple-itunes-app" content="app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL" />
添加智能 App 广告条 Smart App Banner(iOS 6+ Safari)
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="format-detection" content="telphone=no, email=no" /> 设置苹果工具栏颜色
<meta name="renderer" content="webkit" /> 启用360浏览器的极速模式(webkit)
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> 避免IE使用兼容模式
<meta http-equiv="Cache-Control" content="no-siteapp" /> 不让百度转码
<meta name="HandheldFriendly" content="true" /> 针对手持设备优化,主要是针对一些老的不识别viewport的浏览器,比如黑莓
<meta name="MobileOptimized" content="320" /> 微软的老式浏览器
<meta name="screen-orientation" content="portrait" /> uc强制竖屏
<meta name="x5-orientation" content="portrait" /> QQ强制竖屏
<meta name="full-screen" content="yes" /> UC强制全屏
<meta name="x5-fullscreen" content="true" /> QQ强制全屏
<meta name="browsermode" content="application" /> UC应用模式
<meta name="x5-page-mode" content="app" /> QQ应用模式
<meta name="msapplication-tap-highlight" content="no" /> windows phone 点击无高光
设置页面不缓存
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
查看资料

img标签中的alt和title有什么区别

查看答案
  • alt 是当图片加载失败时,显示在网页上替代图片的文字;
  • title 是鼠标放在图片上时显示的文字,来更加清楚的表达其目的;

注意:alt 只适用于 img 标签,而 title 适用于很多标签。


你用过哪些HTML5标签


HTML5有哪些新特性?废除了哪些元素?如何让低版本IE支持HTML5新标签?

查看答案

新特性:

  1. 语义:新增加了 <section>、<article>、<nav>、<header>、<footer> 等标签;
  2. 连通性:增加 Web Sockets、Server-sent events 特性,帮助我们实现服务器将数据推送到客户端的功能;
  3. 离线 & 存储:对本地离线存储的更好的支持;localStorage 长期存储数据,浏览器关闭后数据不丢失;sessionStorage 的数据在浏览器关闭后自动删除;
  4. 多媒体:<audio> 和 <video> 元素嵌入并支持新的多媒体内容的操作;
  5. 2D/3D 绘图 & 效果:增加用于绘制图像的 canvas,SVG 矢量图像格式;
  6. 性能 & 集成:HTML5 会通过 XMLHttpRequest2 等技术,帮助您的 Web 应用和网站在多样化的环境中更快速的工作;
  7. 设备访问 Device Access:能够处理各种输入和输出设备。

废除的元素:

  1. 表现元素:basefont、big、center、font、s、strike、tt、u;
  2. 部分浏览器支持的元素:applet、bgsound、blink、marquee;
  3. 对可用性产生负面影响的元素:frameset、frame、noframes,在 html5 中不支持 frame 框架,只支持 iframe 框架。

让低版本的 IE 支持 HTML5 新标签:
使用 html5shiv 可以解决 IE 低版本不兼容的问题,只需要在 head 中加入当版本低于 IE9 时,浏览器会加载 html5.js 脚本,使得支持 HTML5 的新功能,也可以将脚本文件下载到本地。

1
2
3
4
5
6
<head>
<!--[if lt IE 9]>
<script src='http://apps.bdimg.com/libs/html5shiv/3.7/html5shiv.min.js'>
</script>
<![endif]-->
</head>
查看资料

H5是什么

查看答案

首先 H5 != HTML5,其次 H5 是指在手机这类无法播放 Flash 的移动端上呈现的,可以达到 Flash 效果的网页,也可称移动端 PPT。


@import与link的区别

查看答案
  1. @import 是 CSS 提供的语法规则,只有导入样式表的作用;link 是 HTML 的标签,不仅可以加载 CSS,还可以定义 RSS、rel 连接属性等;
  2. 页面被加载时,@import 引入的 CSS 将在页面加载完毕后被加载;link 标签引入的 CSS 被同时加载;
  3. @import 在 IE5+ 才能被识别;link 不存在兼容问题;
  4. @import 不支持使用 JS 控制 DOM 改变样式;而 link 支持。

src和href有什么区别

查看答案
  • src 表示的是对资源的引用,会替换当前内容,常见在 img、iframe、script 标签上使用;
  • href 超文本引用,是用于在当前文档和引用资源之间确立联系,常见在 a、link 标签上使用;

注意:浏览器在解析 src 时会停下来等到 src 的内容加载完毕后再加载后续内容;而 href 在加载的时候不会停止对后续内容的处理。


对浏览器内核的理解及常见的浏览器内核有哪些

查看答案

浏览器的内核分成两部分:渲染引擎 和 JS引擎。

  1. 渲染引擎:负责取得网页的内容(HTML、XML、图像等)、整理讯息(例如加入 CSS 等),以及计算网页的显示方式,然后会输出至显示器或打印机。
  2. JS 引擎:解析 JavaScript 语言,执行 JavaScript 语言来实现网页的动态效果。

常见的浏览器内核如下:

  • Trident 内核(又称 MSHTML):IE、猎豹安全浏览器、360 安全浏览器、搜狗浏览器、百度浏览器、UC 浏览器等等;
  • Gecko 内核:Netscape6 及以上版本、FireFox 等;
  • Webkit 内核:Chrome、Safari 等;
  • Presto 内核(现为 Blink):Opera7 及以上。

什么是DOCTYPE

查看答案

<!DOCTYPE> 必须声明在文档中的最前面的位置。

<!DOCTYPE> 声明不是一个 HTML 标签,它是指示 web 浏览器关于页面使用哪个 HTML 版本进行编写的指令。

<!DOCTYPE> 不存在或者格式不正确会导致文档以兼容模式呈现。

注意:总是给您的 HTML 文档添加 <!DOCTYPE> 声明,确保浏览器能够预先知道文档类型。


HTML5为什么只需要写<!DOCTYPE html>

查看答案
  1. HTML5 不基于 SGML,因此不需要对 DTD 进行引用,但是需要 DOCTYPE 来规范浏览器的行为;
  2. HTML4.01 基于 SGML,需要对 DTD 进行引用才能告知浏览器文档所使用的文档类型;

HTML全局属性有哪些


iframe有哪些缺点

查看答案
  • iframe 会阻塞主页面的 onload 事件;
  • 搜索引擎和检索程序无法解读这种页面,不利于 SEO;
  • iframe 和主页面共享连接池,而浏览器对相同域的连接有限制,会影响页面的并行加载;
  • 会产生很多页面,不易管理;

CSS

css hack是什么


两种盒模型

查看答案

盒模型分为W3C的标准模型和IE模型

盒模型又称为框模型,包含了元素内容(content)、内边距(padding)、外边距(margin)、边框(border)

如图:

W3C 的标准模型:width/height 属性指 content。

IE 模型:width/height 属性由 content + padding + border 组成。

这两种模型的区别就是计算的宽度和高度不同。

通过 CSS3 新增的属性 box-sizing:content-box | border-box; 分别设置盒模型为标准模型(content-box)和 IE 模型(border-box)

W3C 的标准模型:

1
<div class="content-box"></div>
1
2
3
4
5
6
7
8
.content-box {
box-sizing: content-box;
width: 100px;
height: 100px;
padding: 20px;
border: 5px solid red;
margin: 10px;
}

其中:width = 100px


IE 模型:

1
<div class="border-box"></div>
1
2
3
4
5
6
7
8
.border-box {
box-sizing: border-box;
width: 100px;
height: 100px;
padding: 20px;
border: 5px solid red;
margin: 10px;
}

其中:width = content + 2padding + 2border = 100px

相对比较而言:border-box 更好用!!!

扩展知识

JS 如何设置获取盒模型对应的宽和高呢?

1
2
3
4
dom.style.width / height;   // 设置获取的是内联样式
dom.currentStyle.width / height; // IE支持
window.getComputedStyle(dom).width / height; // 兼容性好
dom.getBoundingClientRect().width / height; // 计算一个元素的绝对位置

为什么要初始化CSS样式

查看答案

因为浏览器的兼容问题,不同浏览器的部分标签默认值都是不同的,如果没有对 CSS 样式初始化会出现各个浏览器之间的页面显示差异。

对 CSS 样式初始化后会对 SEO 有一定的影响,所以力求影响最小的情况下进行初始化。

最简单也最不建议初始化的方式:

1
2
3
4
* {
margin: 0;
padding: 0;
}

可以使用 normalize.css 进行 CSS 初始化。

以下是淘宝的样式初始化代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,pre,form,fieldset,legend
,button,input,textarea,th,td{margin:0;padding:0;}
body,button,input,select,textarea{font:12px/1.5tahoma,arial,\5b8b\4f53;}
h1,h2,h3,h4,h5,h6{font-size:100%;}
address,cite,dfn,em,var{font-style:normal;}
code,kbd,pre,samp{font-family:couriernew,courier,monospace;}
small{font-size:12px;}
ul,ol{list-style:none;}
a{text-decoration:none;}
a:hover{text-decoration:underline;}
sup{vertical-align:text-top;}
sub{vertical-align:text-bottom;}
legend{color:#000;}
fieldset,img{border:0;}
button,input,select,textarea{font-size:100%;}
table{border-collapse:collapse;border-spacing:0;}

块级元素与行内元素分别有哪些以及各有什么特点

查看答案

行内元素:
一个行内元素只占据它对应标签的边框所包含的空间,行内元素前后不会产生换行,一系列的行内元素都在一行显示,直到该行排满为止再另起一行。

行内元素有:a、img、span、button、input、textarea、label、em、kbd、br、b、i、tt 等等。

特点:

  1. 和其他元素都在同一行上;
  2. 宽度(width)、高度(height)、内边距的 top / bottom(padding-top / padding-bottom) 和外边距的 top / bottom(margin-top / margin-bottom) 都不可改变,但是 padding、margin 的 left 和 right 是可以设置的,line-height 也是有效的;
  3. 宽度就是它的文字或图片的宽度,是不可改变的;
  4. 行内元素只能容纳文本或者其他行内元素,不能容纳块级元素;
  5. 行内元素会在一条直线上排列,都是同一行的,水平方向排列。

块级元素:
块级元素占据其父元素(容器)的整个空间,因此创建了一个“块”。通常浏览器会在块级元素前后另起一个新行。

块级元素有:div、p、h1 ~ h6、hr、ul、ol、li、dd、dl、table 等等。

特点:

  1. 独占一行,每一个块级元素都会从新的一行重新开始;
  2. 宽度(width)、高度(height)、内边距(padding)和外边距(margin)都可设置;
  3. 如果宽度没有设置,则默认宽度为容器的 100%;
  4. 块级元素可以容纳行内元素和其他块级元素;
  5. 块级元素各占据一行,垂直方向排列。

position有哪些常用的值

查看答案

static:
static 是 position 属性的默认值,如果省略了 position 属性,浏览器就认为该元素为 static 定位。

浏览器会按照源码的顺序,决定每个元素的位置,这称为 “正常的页面流“(normal flow)。每个块级元素占据自己的区块(block),元素与元素之间不产生重叠,这个位置就是元素的默认位置。

static 定位所导致的元素位置,是浏览器自主决定的,所以这时 top、bottom、left、right 这四个属性无效,并且 z-index 设置也无效。


relative:
relative 表示相对定位,是默认参照父级的原始点为原始点来定位的,若无父元素则按照上一个元素的底部为原始点进行定位。

  1. 相对定位不会影响元素本身的特性;
  2. 不会使元素脱落文档流;
  3. 没有定位偏移量时对元素无影响;
  4. top、bottom、left、right、z-index 属性有效。

absolute:
absolute 表示绝对定位,相对于 static 定位以外的第一个父元素进行定位。

  1. 使元素完全脱离文档流;
  2. 可以改变行内元素的特性,即可以设置宽高;
  3. 在未设置宽度时,宽度由内容撑开;
  4. 相对于最近的一个有定位的父元素偏移,若父元素没有定位则逐层向上找直至 body 为止;
  5. top、bottom、left、right、z-index 属性有效。

fixed:
fixed 表示固定定位,相对于浏览器窗口进行定位。即定位基点是浏览器窗口。这会导致元素的位置不随页面滚动而变化,就好像固定在网页上一样。

搭配 top、bottom、left、right 这四个属性一起使用,表示元素的初始位置是基于视口计算的,否则初始位置就是元素的默认位置。


CSS中伪类与伪元素的区别


如何水平居中


如何垂直居中


flex怎么用,常用属性有哪些


grid布局怎么用


display:none; 、visibility:hidden; 、opacity:0; 有什么区别

查看答案

display:none;:

  1. 隐藏对应的元素,浏览器不会渲染,在文档布局中不占据空间;
  2. 无法进行 DOM 事件监听;
  3. 会引起重排,性能较差;
  4. 不会被子元素继承。

visibility:hideen;:

  1. 隐藏对应的元素,浏览器会渲染,在文档布局中占据原来的空间;
  2. 无法进行 DOM 事件监听;
  3. 只会引起重绘,性能较高;
  4. 会被子元素继承,子元素可以设置 visibility: visible; 来进行显示。

opacity:0;:

  1. 将透明度为 100%,元素被隐藏了,在文档布局中占据原来的空间;
  2. 可以进行 DOM 事件监听;
  3. 提升为合成层,不会触发重绘,性能较高;
  4. 会被子元素继承,但是子元素设置 opacity: 1; 无效。

使用 display:none; 属性后,HTML 元素(对象)的宽度、高度等各种属性值都将丢失;使用 visibility:hideen; 属性后,HTML 元素(对象)仅仅是在视觉上看不见(完全透明),而它所占据的空间位置仍然存在;而使用 opacity:0; 属性后,也仅仅是将透明度设置成了 100%,以致于在视觉上完全看不见。

示例代码


BFC是什么,举例回答


CSS选择器的优先级

查看答案
  1. 越具体优先级越高;
  2. 写在后面的覆盖前面的;
  3. !important,最高,但是要少用!
  4. !important > 行内样式 > id > class > tag > 通配符 > 继承 > 浏览器默认属性。

CSS哪些属性可以继承

查看答案

继承就是指子节点默认使用父节点的样式属性。

可继承的属性大概有颜色、文字、字体间距、行高、对齐方式、列表的样式。

  1. 所有元素可继承:元素可见性(visibility)、光标属性(cursor);
  2. 内联元素可继承:letter-spacing、word-spacing、white-space、line-height、color、font、font-family、font-size、font-style、font-variant、font-weight、text-decoration、text-transform、direction;
  3. 块状元素可继承:text-indent、text-align;
  4. 列表元素可继承:list-style、list-style-type、list-style-position、list-style-image。

如何清除浮动


em与rem

查看答案

px、em、rem区别

查看答案
  1. px:表示像素,是相对于屏幕像素,相对长度单位;

  2. em:相对长度单位,这个单位表示元素的 font-size 的计算值。它会继承父级元素的字体大小,因此并不是一个固定的值;

    1
    2
    3
    <div class="parent">
    <div class="son"></div>
    </div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    .parent {
    background-color: red;
    font-size: 20px;
    height: 2em;
    }
    .son {
    background-color: blue;
    height: 1em;
    }

    因此,parent 这个盒子高度为 40px,son 这个盒子高度为 20px。

  3. rem:这个单位代表根元素的 font-size 大小(例如 <html> 元素的 font-size)。当用在根元素的 font-size 上面时 ,它代表了它的初始值。例如:设置 html { font-size: 20px } 时,其他元素 1rem = 20px,5rem = 100px;

如下代码可以进行适配:

1
document.documentElement.style.fontSize = document.documentElement.clientWidth / 37.5 + 'px'

CSS3有哪些新特性

查看答案
  1. 新增各种 css 选择器,例如::root、:last-child、:first-of-type、:last-of-type 等等;
  2. 三个动画相关特性:过渡(transition)、2D或3D转换(transform)、动画(animation);
  3. 三个边框属性:圆角边框(border-radius)、添加阴影(box-shadow)、使用图片来绘制边框(border-image);
  4. 背景属性:确定背景画区(background-clip)、确定背景的位置(background-origin)、调整背景图片的大小(background-size);
  5. 文字效果:强制换行(word-wrap)、当文本溢出包含它的元素该如何显示(text-overflow)、文本阴影(text-shadow)、规定添加到文本的修饰(text-decoration);
  6. 渐变:线性渐变(linear-gradient)、径向渐变(radial-gradient);
  7. 多列布局;
  8. 媒体查询:@media;
  9. 字体:@font-face。

去除inline-block元素间间隙的方法


如何适配各种移动设备


如何添加代码使得图片宽度为300px

1
<img src="#.png" style="width:500px!important;">
查看答案

使用 CSS 有如下三种方法:

1
2
3
4
5
6
7
8
// 1. 最简单的就是直接在后面添加宽度并设置 !important 来覆盖掉
<img src="#.png" style="width:500px!important; width:300px!important;">

// 2. 设置最大宽度
<img src="#.png" style="width:500px!important; max-width:300px;">

// 3. 使用 transform 来缩小
<img src="#.png" style="width:500px!important; transform:scale(0.6,0.6);">

使用 JS 的方法:

1
2
3
// 使用 setAttribute 来属性
let img = document.querySelector("img")
img.setAttribute("style", "width:300px;")

示例代码


用纯CSS画一个三角形

查看答案
1
2
3
4
5
6
7
.triangle {
width: 0;
height: 0;
border-width: 40px;
border-style: solid;
border-color: transparent transparent red transparent;
}

点击预览😀


用纯CSS画一个六边形

查看答案
  1. 方法一

    1
    <div class="hexagon"></div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    .hexagon {
    position: relative;
    width: 100px;
    height: 173.2px;
    background-color: red;
    margin: 0 auto;
    }
    .hexagon::before,
    .hexagon::after {
    content: "";
    position: absolute;
    display: block;
    top: 0;
    width: 0;
    height: 0;
    border-top: 86.6px solid transparent;
    border-bottom: 86.6px solid transparent;
    }
    .hexagon::before {
    left: -50px;
    border-right: 50px solid red;
    }
    .hexagon::after {
    right: -50px;
    border-left: 50px solid red;
    }
  2. 方法二

    1
    <div class="hexagon"></div>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    .hexagon {
    position: relative;
    width: 100px;
    height: 173.2px;
    background-color: red;
    margin: 0 auto;
    }
    .hexagon::before,
    .hexagon::after {
    content: "";
    position: absolute;
    display: block;
    top: 0;
    left: 0;
    width: 100px;
    height: 173.2px;
    box-sizing: border-box;
    }
    .hexagon::before {
    background-color: red;
    transform: rotate(60deg)
    }
    .hexagon::after {
    background-color: red;
    transform: rotate(-60deg)
    }

width:auto;和width:100%;的区别是什么

查看答案

首先它们都是由父级元素所决定的,其次有以下区别:

  1. width:100%;:会使元素的宽度等于父元素的宽度,子元素增加 padding,会使得元素超过父元素的边界;
  2. width:auto;:会使元素撑满整个父元素,margin、border、padding、content 区域会自动分配水平空间;

参考代码


什么是优雅降级和渐进增强

查看答案

优雅降级:一开始就构建完整的功能,然后再针对低版本浏览器进行兼容。

渐进增强:针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高级浏览器进行效果、交互等改进和追加功能达到更好的用户体验。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 渐进增强写法
.transition {
-webkit-transition: all .5s;
-moz-transition: all .5s;
-o-transition: all .5s;
transition: all .5s;
}

// 优雅降级写法
.transition {
transition: all .5s;
-o-transition: all .5s;
-moz-transition: all .5s;
-webkit-transition: all .5s;
}

line-height是如何继承的

查看答案

分如下三种情况:

  1. 具体的数值(例如:10px):继承该值,如果父元素 line-height: 30px;,则子元素 line-height: 30px;;
  2. 比例(例如:1.5):继承该比例,如果父元素 line-height: 2;,且子元素的 font-size: 15px;,那么子元素的 line-height: 30px;;
  3. 百分比(例如:200%):继承计算出来的值,如果父元素 font-size: 30px; line-height: 200%;,那么子元素 line-height: 60px;。

怎么画一条0.5px的线


文本省略有哪些方案


300ms延迟解决方案

查看答案
  1. 设置 meta :

    1
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no">
  2. 使用 fastclick.js:

    1
    2
    3
    4
    5
    if ('addEventListener' in document) {
    document.addEventListener('DOMContentLoaded', function() {
    FastClick.attach(document.body);
    }, false);
    }
  3. css 设置 touch-action:

    1
    2
    3
    * {
    touch-action: manipulation;
    }

什么是重绘和重排


JavaScript

JS的基础数据类型有哪些

查看答案

JS中null是对象吗

查看答案
1
typeof null    // "object"

虽然上面代码会输出 object,但这是 JS 的一个悠久的 bug。在 JS 最初版本中使用的是 32 位系统,当时为了性能方面的考虑而使用低位存储变量的类型信息,000 开头代表是对象,然而 null 表示为全零,所以将它错误的判断为 object。


export与export default的区别

查看答案
  • 相同点:它们均可用于导出常量、函数、文件、模块等等。
  • 不同点:
    • 在一个文件或模块中,export、import 可以有多个,但 export default 仅能有一个;
    • 通过 export 方式导出,在 import 时导入时需要加 { };
    • 通过 export default,在 import 时导入时不需要加 { };
    • 使用 export default 命令,为模块指定默认输出,不需要知道加载模块的变量名。

import与require的区别


ES6语法有哪些,分别怎么用


new的执行过程

查看答案
  1. 创建一个空对象;
  2. 将构造函数的 prototype 属性赋值给新对象的 __proto__ 属性,并将构造函数的 this 指向新对象;
  3. 执行构造函数中的代码(为这个新对象添加属性);
  4. 将新对象返回;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function Student(name) {
this.name = name
}

let studentA = new Student('a')
console.log(studentA)

// 等价于

let studentB = (function() {
let obj = {}
obj.__proto__ = Student.prototype
Student.call(obj, 'b')
return obj
})()
console.log(studentB)

异常捕获


call、apply、bind区别

查看答案
  1. call
    call() 方法使用一个指定的 this 值和单独给出的一个或多个参数来调用一个函数。

    1
    fun.call(thisArg, arg1, arg2, ...)
  2. apply
    apply() 方法调用一个具有给定 this 值的函数,以及作为一个数组(或类似数组对象)提供的参数。当不确定参数的个数时,就可以使用 apply。

    1
    func.apply(thisArg, [argsArray])
  3. bind
    bind() 方法会返回一个新的函数,在 bind() 被调用时,这个新函数会 call 原来的函数,新函数的 this 被 bind 的第一个参数指定,其余的参数将作为新函数的参数供调用时使用。

    1
    function.bind(thisArg[,arg1[,arg2[, ...]]])
  4. 总结

其实 apply 和 call 基本类似,他们的区别只是传入的参数不同。call 方法接受的是若干个参数列表,而 apply 接收的是一个包含多个参数的数组。

bind 和 call、apply 方法作用是一致的,只是该方法会返回一个新函数,并且我们必须要手动去调用。


什么是JSONP

查看答案

它的基本思想是,网页通过添加一个 <script> 元素,向服务器请求 JSON 数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

请求方:xxx.com 的前端程序员(浏览器)
响应方:yyy.com 的后端程序员(服务器)

  1. 请求方创建 script,src 指向响应方,同时传一个查询参数 ?callback=xxx
  2. 响应方根据查询参数 callback,构造形如这样的响应:
      1. xxx.call(undefined,’需要的数据’)
      2. xxx(‘需要的数据’)
  3. 浏览器接收到响应,就会执行 xxx.call(undefined,’需要的数据’)
  4. 那请求方就知道了所需要的数据了

为什么不支持POST?

  1. JSONP 是通过动态创建 script 的;
  2. 动态创建 script 时只能用 GET,没法用 POST;

代码实现 zhouwanwen.com:8001 和 jack.com:8002 之间的 JSONP 请求:Github - nodejs-demo-1


全局函数eval()有什么作用

查看答案

eval() 函数会将传入的字符串当做 JavaScript 代码进行执行。

eval() 是全局对象的一个函数属性

  • 如果 eval() 的参数是一个字符串,并且该字符串表示的是表达式,那么eval() 会对表达式进行求值;如果参数表示一个或多个 JavaScript 语句,那么 eval() 就会执行这些语句。
  • 如果 eval() 的参数不是字符串,eval() 会将参数原封不动地返回。
查看资料

为什么0.1+0.2!==0.3

查看答案
1
0.1 + 0.2    // 0.30000000000000004

因为 JavaScript 存储数值采用双精度浮点数,会出现精度丢失的问题。

1
2
0.100000000000000000000002 === 0.1    // true
console.log(0.100000000000000002) // 0.1

如何使得 0.1+0.2 === 0.3 呢?

1
parseFloat((0.1 + 0.2).toFixed(10)) === 0.3    // true

==、=== 与 Object.is()

查看答案
  1. ==:相等运算符,会在比较时进行类型转换;
  2. ===:严格运算符:比较时不进行隐式转换,类型不同则返回 false;
  3. Object.is():该方法判断两个值是否为同一个值,返回 true / false。它不会强制转换两边的值;
  • 对于 string、number 等基础类型,是有区别的:

    1. 不同类型:
      • ==:转换成同一类型后的值看值是否相等;
      • ===:如果类型不同,结果就不等;
    2. 相同类型:直接进行值比较;
  • 对于 Array、Object 等高级类型,是没有区别的;

  • 对于基础类型与高级类型,是有区别的:

    1. ===:类型不同,结果不等;
    2. ==:将高级类型转化为基础类型,进行值比较。

Object.is() 方法如果满足以下条件则两个值相等:

  1. 都是 undefined;
  2. 都是 null;
  3. 都是 true 或 false;
  4. 都是相同长度的字符串且相同字符按相同顺序排列;
  5. 都是相同对象(意味着每个对象有同一个引用);
  6. 都是数字且都是 +0;都是 -0;都是 NaN;或都是非零而且非 NaN 且为同一个值;
1
2
3
4
5
6
7
8
9
10
11
12
Object.is('hello','hello')    // true
Object.is('hello','hi') // false

Object.is([],[]) // false

Object.is(null,null) // true
Object.is(null,undefined) // false

Object.is(0,+0) // true
Object.is(0,-0) // false
Object.is(-0,+0) // false
Object.is(NaN,0/0) // true

扩展:

1
2
3
4
5
6
7
let obj = {
name: 'LqZww'
}

if (obj.age == null) {}
// 等价于
if (obj.age === null || obj.age === undefined) {}

什么是原型


什么是闭包


什么是CORS,什么是跨域


typeof与instanceof区别

查看答案

typeof:
typeof 可用于基本数据类型的类型判断,例如:number、string、boolean、function、undefined、symbol 等,返回值都是小写的字符串。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
console.log(typeof undefined)   // undefined
console.log(typeof 1) // number
console.log(typeof "a") // string
console.log(typeof true) // boolean
console.log(typeof Symbol()) // symbol

console.log(typeof function(){}) // function
console.log(typeof new Function()) // function

console.log(typeof null) // object
console.log(typeof [1,2]) // object
console.log(typeof {}) // object
console.log(typeof new String()) // object
console.log(typeof new Object()) // object
console.log(typeof new Number()) // object

instanceof:
instanceof 是判断变量是否为某个对象的实例,返回值为 true 或 false。

1
2
3
4
5
6
7
8
var arr = []
var obj = {}

console.log(arr instanceof Array) // true
console.log(arr instanceof Object) // true

console.log(obj instanceof Array) // false
console.log(obj instanceof Object) // true

区别:

  1. typeof 用于基本数据类型的类型判断,无法判断对象的具体类型(除function);
  2. instanceof 可以用来区分数组、对象,不能用来判断字符串、数字等;

注:Object.prototype.toString.call() 适用于所有类型的判断检测。

1
2
3
4
5
6
7
8
9
10
Object.prototype.toString.call(1); // [object Number]
Object.prototype.toString.call("1"); // [object String]
Object.prototype.toString.call(null); // [object Null]
Object.prototype.toString.call(undefined); // [object Undefined]
Object.prototype.toString.call(true); // [object Boolean]
Object.prototype.toString.call([1, 2, 3]); // [object Array]
Object.prototype.toString.call({ name: "lqzww" }); // [object Object]
Object.prototype.toString.call(function () {}); // [object Function]
Object.prototype.toString.call(new Date()); // [object Date]
Object.prototype.toString.call(/\s/); // [object RegExp]

封装一下typeof方法

查看答案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function myTypeof(val) {
if (val === null) return 'null';

let type = typeof (val)
let objectTypes = {
'[object Object]': 'o-Object',
'[object Array]': 'o-Array',
'[object Number]': 'o-Number',
'[object Boolean]': 'o-Boolean',
'[object String]': 'o-String'
}

return type === 'object' ? objectTypes[Object.prototype.toString.call(val)] : type;
}

console.log(myTypeof(1)); // number
console.log(myTypeof('1')); // string
console.log(myTypeof(null)); // null
console.log(myTypeof([])); // o-Array
console.log(myTypeof({})); // o-Object
console.log(myTypeof(new Number())); // o-Number
console.log(myTypeof(new Boolean())); // o-Boolean
console.log(myTypeof(new String())); // o-String

如何实现深拷贝

查看答案

判断数组有哪几种方法

查看答案
  1. instanceof
    instanceof 是判断变量是否为某个对象的实例,返回值为 true 或 false。

  2. Object.prototype.toString.call()
    每一个继承 Object 的对象都有 toString 方法,如果 toString 方法没有重写的话,会返回 [Object type],其中 type 为对象的类型。但当除了 Object 类型的对象外,其他类型直接使用 toString 方法时,会直接返回都是内容的字符串,所以我们需要使用 call 或者 apply 方法来改变 toString 方法的执行上下文。该方法对于所有基本的数据类型都能进行判断。

  3. Array.isArray()
    此方法用来判断对象是否为数组。

参考代码


操作数组方法有哪些,分别有什么用

查看答案
1
let arr = [11, 22, 33]
1
2
3
4
// 1. pop() 方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。
let arr1 = arr.pop()
console.log(arr) // (2) [11, 22]
console.log(arr1) // 33
1
2
3
4
// 2. shift() 方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
let arr2 = arr.shift()
console.log(arr) // (2) [22, 33]
console.log(arr2) // 11
1
2
3
4
// 3. push() 方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度。
let arr3 = arr.push(44)
console.log(arr) // (4) [11, 22, 33, 44]
console.log(arr3) // 4
1
2
3
4
// 4. unshift() 方法将一个或多个元素添加到数组的开头,并返回该数组的新长度。
let arr4 = arr.unshift(1)
console.log(arr) // (4) [1, 11, 22, 33]
console.log(arr4) // 4
1
2
3
// 5. concat() 方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
let arr5 = arr.concat([44, 55])
console.log(arr5) // (5) [11, 22, 33, 44, 55]
1
2
3
// 6. slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end),原始数组不会被改变。
let arr6 = arr.slice(2)
console.log(arr6) // [33]
1
2
3
// 7. filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素,不改变原数组。
let arr7 = arr.filter(num => num > 20)
console.log(arr7) // (2) [22, 33]
1
2
3
// 8. map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。
let arr8 = arr.map(num => num * 2)
console.log(arr8) // (3) [22, 44, 66]
1
2
3
4
// 9. splice() 方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
let arr9 = arr.splice(1, 1, 44)
console.log(arr) // (3) [11, 44, 33]
console.log(arr9) // [22]

数组降维


null与undefined的区别

查看答案

null:
null 是一个字面量,表示空,没有对象。

用法:

  1. 作为函数的参数,表示该函数的参数不是对象。
  2. 作为对象原型链的终点。

undefined:
undefined 表示 “缺少值“,就是此处应该有一个值,但是还没有定义。

用法:

  1. 变量被声明了,但没有赋值时,就等于 undefined;
  2. 调用函数时,应该提供的参数却没有提供,该参数等于 undefined;
  3. 对象没有赋值的属性,该属性的值为 undefined;
  4. 函数没有返回值时,默认返回 undefined;

惯例:

  1. 如果一个变量没有赋值,那它就是 undefined;
  2. 如果一个对象 Object,现在还不想赋值,则可以给它一个 null,即:let obj = null,表示空对象;
  3. 如果有一个非对象,现在还不想赋值,则给它一个 undefined;

注意:

1
2
3
4
5
6
7
8
null == undefined   // true
null === undefined // false

typeof null // "object"
typeof undefined // "undefined"

1 + null // 1
1 + undefined // NaN

Promise、Promise.all、Promise.race分别怎么用


async/await语法了解吗,目的是什么


什么是立即执行函数,使用立即执行函数的目的是什么

查看答案

立即执行函数就是说这个函数是立即执行函数体的,不需要额外的去主动调用,要成为立即执行函数,需要满足两个条件:

  1. 声明一个匿名函数;
  2. 立马调用这个匿名函数;

立即执行函数的目的是 创建独立的作用域,让外部无法访问作用域内部的变量,从而避免 变量污染。

下面这段代码就是一个立即执行函数:

1
2
3
(function(){
console.log("这是立即执行函数")
})()

除了上面这种写法,还有如下写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// (匿名函数())
(function(){
console.log("这是立即执行函数")
}())

// !匿名函数()
!function(){
console.log("这是立即执行函数")
}()

// +匿名函数()
+function(){
console.log("这是立即执行函数")
}()

// -匿名函数()
-function(){
console.log("这是立即执行函数")
}()

// ~匿名函数()
~function(){
console.log("这是立即执行函数")
}()

// void 匿名函数()
void function(){
console.log("这是立即执行函数")
}()

// new 匿名函数()
new function(){
console.log("这是立即执行函数")
}()

如何实现数组去重


如何用正则实现string.trim()

查看答案

trim() 方法会从一个字符串的两端删除空白字符。

1
2
3
4
function trim(string) {
return string.replace(/^\s+|\s+$/g, '')
}
console.log(trim(" LqZww ")) // LqZww

合并对象的几种方法

查看答案
  1. 利用 Object.assign 合并多个对象;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let obj1 = {
name: "lq",
age: 18,
};

let obj2 = {
name: "ww",
age: 28,
address: {
city: "中国",
},
likes: ["JavaScript", "React", "Node"],
};

let res = Object.assign(obj1, obj2);
  1. 利用扩展运算符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let obj1 = {
name: "lq",
age: 18,
};

let obj2 = {
name: "ww",
age: 28,
address: {
city: "中国",
},
likes: ["JavaScript", "React", "Node"],
};

let res = { ...obj1, ...obj2 };
  1. 利用浅拷贝实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
let merger = (...opts) => {
let res = {};

let combine = (opt) => {
for (let prop in opt) {
if (opt.hasOwnProperty(prop)) {
res[prop] = opt[prop];
}
}
};

for (let i = 0; i < opts.length; i++) {
combine(opts[i]);
}
return res;
};

let obj1 = {
name: "lq",
age: 18,
};

let obj2 = {
name: "ww",
age: 28,
address: {
city: "中国",
},
likes: ["JavaScript", "React", "Node"],
};

let res = merger(obj1, obj2);
  1. 利用深拷贝实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
let merger = (...opts) => {
let res = {};

let combine = (opt) => {
for (let prop in opt) {
if (opt.hasOwnProperty(prop)) {
if (Object.prototype.toString.call(opt[prop]) === "[object Object]") {
res[prop] = merger(res[prop], opt[prop]);
} else {
res[prop] = opt[prop];
}
}
}
};

for (let i = 0; i < opts.length; i++) {
combine(opts[i]);
}
return res;
};

let obj1 = {
name: "lq",
age: 18,
};

let obj2 = {
name: "ww",
age: 28,
address: {
city: "中国",
},
likes: ["JavaScript", "React", "Node"],
};

let res = merger(obj1, obj2);
  1. 利用 lodash 中的 merge() 方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const _ = require('lodash');

let obj1 = {
name: "lq",
age: 18,
};

let obj2 = {
name: "ww",
age: 28,
address: {
city: "中国",
},
likes: ["JavaScript", "React", "Node"],
};

let res = _.merge(obj1, obj2);

了解ES6 class的用法吗

查看答案

基本用法:

在没有 ES6 class 之前的常规写法:

1
2
3
4
5
6
7
8
9
10
11
function Person(name, age) {
this.name = name
this.age = age
}
Person.prototype.sayHi = function(sing) {
console.log(this.name + "唱了" + sing)
}

var zname = new Person('zww', 22)
console.log(zname) // Person {name: "zww", age: 22}
zname.sayHi('啊哈哈') // zww唱了啊哈哈

在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。

类抽象了对象的公共部分,它泛指某一大类。

对象特指某一个,通过类实例化一个具体的对象。

1
2
3
4
5
6
7
8
9
10
11
12
class Star {
constructor(name, age) {
this.name = name
this.age = age
}
sayHi(sing) {
console.log(this.name + "唱了" + sing);
}
}
var zname = new Star("zww", 11)
console.log(zname); // Star {name: "zww", age: 11}
zname.sayHi("我爱你") // zww唱了我爱你

注意:

  1. 类必须使用 new 实例化对象
  2. 通过 class 关键字创建类,类名首字母一般大写
  3. 类里面有个 constructor 函数,可以接收传递过来的参数,同时返回实例对象
  4. 类里面所有函数都不需要写 function
  5. 多个函数方法之间不需要用逗号隔开

类的继承:

JavaScript 中的类可以继承某个类,其中被继承的类称为父类,而继承父类的被称为子类。

子类可以有自己的函数和构造器,当子类中存在父类相同的方法时,则该方法不会从父类继承,而使用子类的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Father {
constructor(name, age) {
this.name = name
this.age = age
}
sayHi(sing) {
console.log(this.name + '的年龄是' + this.age + ',并且唱了' + sing)
}
}
class Son extends Father {

}
var father = new Father('zww', 18)
console.log(father) // Father {name: "zww", age: 18}

var son = new Son('lq', 22)
console.log(son) // Son {name: "lq", age: 22}
son.sayHi('呵呵') // lq的年龄是22,并且唱了呵呵

super关键字:

super 关键字用于访问和调用对象父类上的函数。可以调用父类的构造函数,也可以调用父类的普通函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Father {
constructor(name, age) {
this.name = name
this.age = age
}
sayHi() {
console.log('父类函数')
}
}
class Son extends Father {
constructor(name, age, sex) {
super(name, age)
this.sex = sex
}
sonfn() {
super.sayHi()
console.log('子类函数')
}
}
var son = new Son('lq', 22, '女')
console.log(son) // Son {name: "lq", age: 22, sex: "女"}
son.sonfn('丫丫') // 父类函数 子类函数

注意:

  • 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
  • 类里面的共有的属性和方法一定要加 this
  • this 的指向问题;constructor 里面的 this 指向的是创建的实例对象;方法里面的 this 指向这个方法的调用者

axios与ajax的区别


模块化


如何实现一个call函数

查看答案
1
2
3
4
5
6
7
8
9
10
11
12
Function.prototype.myCall = function(context) {
if (typeof this !== 'function') {
console.log("error")
}
let args = [...arguments].slice(1)
let result = null
context = context || window
context.fn = this
result = context.fn(...args)
delete context.fn
return result
}

如何实现一个apply函数

查看答案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Function.prototype.myApply = function(context) {
if (typeof this !== 'function') {
throw new TypeError("Error")
}
let result = null
context = context || window
context.fn = this
if (arguments[1]) {
result = context.fn(...arguments[1])
} else {
result = context.fn()
}
delete context.fn
return result
}

如何实现一个bind函数

查看答案
1
2
3
4
5
6
7
8
9
10
Function.prototype.myBind = function(context) {
if (typeof this !== 'function') {
throw new TypeError("Error")
}
var args = [...arguments].slice(1),
fn = this
return function Fn() {
return fn.apply(this instanceof Fn ? this : context, args.concat(...arguments))
}
}

Github - 详细代码


手写一个AJAX

查看答案
1
2
3
4
5
6
7
8
9
10
11
12
let xhr = new XMLHttpRequest()
xhr.open('GET', '/xxx', true)
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
if(xhr.status >= 200 && xhr.status < 300){
console.log('请求成功')
}else{
console.log('请求失败')
}
}
}
xhr.send()

手写一个函数防抖

查看答案

当我们触发事件时,但是一定在事件触发的第 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那么就以新触发事件的时间为准,n 秒后才执行。

就是等你触发完事件 n 秒内不再触发事件才会执行。

也可以这样理解,相当于王者荣耀游戏里的回城功能一样,如果你反复的触发点击,只会以最后一次为准。

1
2
3
4
5
6
7
8
9
function debounce(fn, wait) {
let timer = null
return function() {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => fn.apply(this, arguments), wait)
}
}

示例代码 - 函数防抖


手写一个函数节流

查看答案

从上一次命令结束开始的一定时间范围 n 秒内,如果多次连续下达命令,则只执行当前时间段 n 秒内第一次命令。

如果你持续触发事件,每隔一段时间,只执行一次事件。

1
2
3
4
5
6
7
8
9
10
11
12
function throttle(fn, gapTime) {
let lastTime = null
let nowTime = null
return function() {
nowTime = Date.now()
if (!lastTime || nowTime - lastTime > gapTime) {
fn()
lastTime = nowTime
}
}
}

示例代码 - 函数防抖

示例代码 - 自动保存案例


手写EventHub

查看答案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class EventHub {
private cache: { [key: string]: Array<(data: unknown) => void} = {}
on(eventName: string, fn: (data: unknown) => void) {
this.cache[eventName] = this.cache[eventName] || []
this.cache[eventName].push(fn)
}
emit(eventName: string, data?: unknown) {
let array = this.cache[eventName] || []
array.forEach(fn => {
fn(data)
});
}
off(eventName: string, fn: (data: unknown) => void) {
this.cache[eventName] = this.cache[eventName] || []
let index = indexOf(this.cache[eventName], fn)
if (index === -1) return;
this.cache[eventName].splice(index, 1)
}
}

export default EventHub;

详细代码


手写简易版的jQuery

查看答案

手写一个Promise

查看答案

什么是事件委托

查看答案

事件委托,其实就是把一个元素响应事件(click、keydown…)的函数委托到另一个元素上。

一般来说,我们会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,当事件响应到需要绑定的元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数。

使用事件委托的好处:

  1. 可以减少内存的消耗;
  2. 动态绑定事件;

示例代码


WebSocket是什么,了解吗


我提问你来答

Topic One:

如何让下面代码打印出来!

1
2
3
if (a == 1 && a == 2 && a == 3) {
console.log("OK!");
}
查看答案
1
2
3
4
5
6
7
8
9
10
let a = {
default: 0,
toString: function () {
return ++this.default
}
}

if (a == 1 && a == 2 && a == 3) {
console.log("OK!");
}

Topic Two:

如何让下面代码打印出来!

1
2
3
if (a === 1 && a === 2 && a === 3) {
console.log("OK!");
}
查看答案
1
2
3
4
5
6
7
8
9
10
let _default = 0
Object.defineProperty(window, 'a', {
get() {
return ++_default
}
})

if (a == 1 && a == 2 && a == 3) {
console.log("OK!");
}

Topic Three:

下面几段代码将打印出什么呢?

1
2
3
4
5
console.log(({} + {}).length);

console.log(([] + []).length);

console.log((function () { }).length);
查看答案

分别打印出:30 0 0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
console.log({}.toString());    // [object Object]
console.log(Object.prototype.toString.call({})) // [object Object]
console.log(({}.toString()).length); // 15
console.log(({} + {}).length); // 30

console.log([].toString()) //
console.log([].length); // 0
console.log(([] + []).length); // 0


function fn1() { }
console.log(fn1.length); // 0

function fn2(a, b, c) { }
console.log(fn2.length); // 3

function fn3(a, b, c) {
console.log(arguments.length); // 4
}
fn3(1, 2, 3, 4)
console.log(fn3.length); // 3

console.log((function () { }).length); // 0
console.log((function (a, b) { }).length); // 2

Topic Four:

这行代码的执行结果是什么?

1
console.log(['1', '2', '3'].map(parseInt))
查看答案

首先我们先来了解下 map 的使用。

1
2
3
4
5
6
['1', '2', '3'].map(function(item, index) {
console.log(item, index)
// 1 0
// 2 1
// 3 2
})

再来看看 parseInt 的使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
parseInt(1)    // 1
parseInt('1') // 1
parseInt(true) // NaN
parseInt([]) // NaN
parseInt({}) // NaN
parseInt(undefined) // NaN
parseInt(null) // NaN

parseInt('0.5') // 0
parseInt('0.9') // 0

parseInt('1', 10) // 1
parseInt('1', 2) // 1

我们来看看 parseInt('1', 2) 的执行步骤:

  1. 将 '1' 转换成数字;
  2. 然后把它当作二进制数;
  3. 最后返回十进制数。

注意:parseInt 是向下取整。并且第二个参数表示字符串的基数,是 2~36 之间的整数。

现在我们再来看看这段代码:

1
['1', '2', '3'].map(parseInt)

可以这样等价于:

1
2
3
4
5
['1', '2', '3'].map(function parseInt(item, index) {
// '1' 0 -> '1' 的 0 进制,没有 0 进制,parseInt 会把 0/不写 看做是 10 进制。 -> 1
// '2' 1 -> '2' 的 1 进制,没有 1 进制。 -> NaN
// '3' 2 -> '3' 的 2 进制,二进制只有 0 和 1。 -> NaN
})

所以最后结果是:[1, NaN, NaN]。


其他

JavaScript 进阶问题列表


Vue

对SPA单页面的理解,它的优缺点分别是什么

查看答案

SPA 在 Web 页面初始化时加载相应的 HTML、CSS、JavaScript。一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转。取而代之的是利用路由机制实现 HTML 内容的变换,UI与用户的交互,避免页面的重新加载。

优点:

  1. 用户体验好且快,内容的改变不需要重新加载整个页面,避免了不必要的跳转和重复的渲染;
  2. SPA 单页面对服务器的压力小;
  3. 前后端职责分离,架构清晰,前端进行交互逻辑,后端负责数据处理。

缺点:

  1. 初次加载比较耗时:为实现单页 Web 应用功能及显示效果,需要在加载页面的时候将 CSS、JavaScript 这些都统一加载,部分页面按需加载;
  2. 前进后退路由管理:由于单页应用在一个页面中显示所有的内容,所以不能使用浏览器的前进后退功能,所有的页面切换需要自己建立堆栈来管理;
  3. SEO 难度较大:由于所有的内容都在一个页面中动态替换显示,所以在 SEO 上比较弱。

生命周期

Vue的生命周期有哪些

查看答案
  1. 创建:beforeCreate,created;
  2. 载入:beforeMount,mounted;
  3. 更新:beforeUpdate,updated;
  4. 销毁:beforeDestroy,destroyed。

生命周期示意图:

生命周期


第一次页面加载会触发哪几个钩子

查看答案

beforeCreate、created、beforeMount、mounted。


每一个周期具体适合哪些场景

查看答案
  1. beforeCreate:在 new 一个 Vue 实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建。不能获取 DOM 节点。data 和 methods 中的数据都还没有初始化,不能在这个阶段使用 data 中的数据和 methods 中的方法。
  2. created:实例已经创建,仍不能获取 DOM 节点,data 和 methods 都已经被初始化好了,如果要调用 methods 中的方法,或者操作 data 中的数据,最早可以在这个阶段里操作。
  3. beforeMount:在内存中已经编译好模板了,但是还没有挂载到页面中,此时,页面还是旧的。
  4. mounted:Vue 实例已经初始化完成了,此时组件脱离了创建阶段,进入到运行阶段。如果我们想要通过插件操作页面上的 DOM 节点,最早可以在这个阶段中进行。
  5. beforeUpdate:当执行这个钩子时,页面中的显示的数据还是旧的,data 中的数据是更新后的,页面还没有和最新的数据保持同步。
  6. updated:页面显示的数据和 data 中的数据已经保持同步了,都是最新的。
  7. beforeDestroy:Vue 实例从运行阶段进入到了销毁阶段,这个时候所有的 data、methods、指令、过滤器等等都是处于可用状态,还没有真正被销毁。
  8. destroyed:组件已经被销毁,无法操作里面的任何东西了。

Vue获取数据一般在哪个周期函数

查看答案

在 created、beforeMount、mounted 中都可以。

不要在 updated 里更新数据。


父子组件在生命周期中调用的顺序是怎样的

查看答案
  1. 加载渲染过程:父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted;
  2. 父组件更新过程:
    • 影响到子组件:父beforeUpdate -> ⼦beforeUpdate -> ⼦updated -> ⽗updated;
    • 不影响子组件:父beforeUpdate -> 父updated;
  3. 子组件更新过程:
    • 影响到父组件:父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated;
    • 不影响父组件:⼦beforeUpdate -> ⼦updated;
  4. 销毁过程:父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed。

与keep-alive有关的生命周期有哪些并简述

查看答案

有两个生命周期函数:

  1. activated:被 keep-alive 缓存的组件激活时调用;
  2. deactivated:被 keep-alive 缓存的组件停用时调用。

当页面第一次进入的时候,触发的顺序是 created -> mounted -> activated,然后退出的时候会触发 deactivated。但是当再次进入的时候(前进 or 后退),只会触发 deactivated。


watch和created哪个先执行,为什么

查看答案

在一般情况下,created 会比 watch 先执行,watch 会在监听的数据发生变化后才执行。

但是呢,如果我们给 watch 设置了 immediate: true ,那么在初始化时就会调用函数,然后再执行 created。


怎样让CSS只在当前组件中生效

查看答案

在组件中的 style 标签上加上 scoped。

1
<style scoped></style>

template下为什么只能有一个div呢


如何给vue自定义组件添加点击事件

查看答案

官方文档 给出,.native - 监听组件根元素的原生事件。

1
<my-button @click.native="eventName()" />

如何获取DOM


Vue常用的指令有哪些

查看答案
  • v-html:会将 html 代码解析出来并进行渲染;
  • v-text:输出文本;
  • v-if:条件判断指令;
  • v-show:是否隐藏元素;
  • v-for:循环指令;
  • v-bind:属性绑定指令(数据的单向绑定);
  • v-model:实现数据的双向绑定;
  • v-on:绑定事件指令;
  • v-once:表示元素和组件只会渲染一次;
  • v-pre、v-cloak;

详情请参考 Vue - 指令


v-html会导致什么问题

查看答案

可能会导致 XSS 攻击,因此在网站上只能在可信的内容上使⽤ v-html,并且永远不能把它⽤于⽤户提交的内容上面。


computed和watch的区别

查看答案

computed:
computed 是计算属性,它会根据你所依赖的数据动态显示新的计算结果。计算结果会被缓存,computed 的值在 getter 执行后是会被缓存的,只有在它依赖的属性值改变之后,下一次获取 computed 的值时才会重新调用对应的 getter 来计算。

应用场景:购物车商品结算,当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算。


watch:
watch 是去监听一个值的变化,然后执行相对应的函数。它可以接受 2 个参数(newValue, oldValue),即变化的最新值和上一次变化的旧值。它是没有缓存的。

应用场景:搜索数据,如果你需要在某个数据变化时做一些事情,使用 watch 来观察这个数据变化。


v-show与v-if的区别

查看答案
  1. v-show 的本质是改变 display 的值,不管初始条件是什么,都会渲染。
  2. v-if 是动态向 DOM 树内添加或者删除 DOM 元素。

v-show 就是控制 CSS 的 display,而 v-if 是不停的销毁和创建,因此如果需要频繁切换使用 v-show 性能会更好一点。


v-if和v-for的优先级是什么

查看答案

根据 官方文档 所描述的:不推荐同时使用 v-if 和 v-for。如果 v-if 与 v-for 一起使用时,v-for 具有比 v-if 更高的优先级。

这也就意味着 v-if 将分别重复运行于每个 v-for 循环中。因此是不推荐将这个两个指令同时使用。


什么是MVVM

查看答案

MVVM 是 Model-View-ViewModel 的缩写,它是一种设计思想。

  • Model 层代表数据模式,也可以在 Model 中定义数据修改和操作的业务逻辑;
  • View 代表 UI 组件,它负责将数据模型转化为 UI 展现出来;
  • ViewModel 是一个同步 View 和 Model 的对象。

Vue组件之间如何通信


Vue中key值的作用是什么

查看答案

使用 key 来给每个节点做一个唯一标识,Diff 算法就可以正确的识别此节点。

key 的作用主要是为了高效的更新虚拟 DOM,可以减少渲染次数,提升渲染性能。


v-model的原理

查看答案

v-model 就是一个语法糖,它背后本质是包含了两个操作:

  • v-bind 绑定一个 value 属性;
  • v-on 给当前元素绑定 input 事件。
1
2
3
<input type="text" v-model="message">
<!-- 等价于 -->
<input type="text" :value="message" @input="message = $event.target.value">

Class与Style如何动态绑定

查看答案

Class - 对象语法:

绑定 class 对象语法:对象的键是类名,值是布尔值。

1
<div v-bind:class="{ active: isActive, size: isSize }">对象绑定Class</div>
1
2
3
4
data: {
isActive: true,
isSize: false
}

或者下面这种写法:

1
<div :class="classObj">对象绑定Class</div>
1
2
3
4
5
6
data: {
classObj: {
fontSize: true,
color: false,
}
}

当需要动态添加 class 时,需要使用 Vue.set() 方法。


Class - 数组语法:

绑定 class 数组语法:数组中的成员直接对应类名。

1
<div v-bind:class="[isActive,isSize]">数组绑定class</div>
1
2
3
4
data: {
isActive: 'active',
isSize: 'size'
}

或者下面这种写法:

1
<div :class="classArr">数组绑定Class</div>
1
2
3
data: {
classArr:['active','fontSize']
}

Style - 对象语法:

1
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }">对象绑定Style</div>
1
2
3
4
data: {
activeColor: 'red',
fontSize: 60
}

或者下面这种写法:

1
<div :style="styleObj">对象绑定Style</div>
1
2
3
4
5
6
data: {
styleObj: {
backgroundColor: "red",
fontSize: "22px",
}
}

当需要动态添加 style 时,需要使用 Vue.set() 方法。


Style - 数组语法:

1
<div v-bind:style="[styleColor, styleSize]">数组绑定Style</div>
1
2
3
4
5
6
7
8
data: {
styleColor: {
color: 'red'
},
styleSize: {
fontSize: '23px'
}
}

或者下面这种写法:

1
<div :style="styleArr">数组绑定Style</div>
1
2
3
4
5
6
7
8
9
10
11
data: {
styleArr: [
{
backgroundColor: "red",
fontSize: "22px",
},
{
color: "blue",
},
]
}

Vue的修饰符有哪些

查看答案

表单修饰符:
.lazy:让数据在失去焦点或者回车时才会更新同步;
.number:自动将用户的输入值转为数值类型;
.trim:自动过滤用户输入的首尾空白字符;


事件修饰符:
.stop:阻止冒泡;
.prevent:阻止默认行为;
.self:只会触发自己范围内的事件,不包含子元素;
.once:事件将只会触发一次;
.capture:与事件冒泡的方向相反,事件捕获由外到内;
.passive:Vue 还对应 addEventListener 中的 passive 选项提供了 .passive 修饰符,能够提升移动端的性能。

不要把 .passive 和 .prevent 一起使用,因为 .prevent 将会被忽略,同时浏览器可能会向你展示一个警告。请记住,.passive 会告诉浏览器你不想阻止事件的默认行为。


按键修饰符:
.enter:回车键;
.tab:制表键;
.delete:含删除和退格键;
.esc:返回键;
.space: 空格键;
.up:向上键;
.down:向下键;
.left:向左键;
.right:向右键;


系统修饰键:
可以用这几个修饰符来实现仅在按下相应按键时才触发鼠标或键盘事件的监听器:.ctrl、.alt、.shift、.meta。


鼠标按钮修饰符:
.left:左键点击;
.right:右键点击;
.middle:中键点击;


.exact修饰符:
.exact:修饰符允许你控制由精确的系统修饰符组合触发的事件。

1
2
3
4
5
6
7
8
<!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
<button v-on:click.ctrl="onClick">A</button>

<!-- 有且只有 Ctrl 被按下的时候才触发 -->
<button v-on:click.ctrl.exact="onCtrlClick">A</button>

<!-- 没有任何系统修饰符被按下的时候才触发 -->
<button v-on:click.exact="onClick">A</button>

Vue组件中data为什么是函数


什么是动态组件

查看答案

如果有多个组件需要通过同一个挂载点来进行组件的切换,此时就可以用到动态组件,使用 component 标签,并使用 :is,它的值是哪个组件的名称,那么就会显示哪个组件。

1
<component :is="otherComponent"></component>

什么是异步组件

查看答案

在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了简化,Vue 允许你以一个工厂函数的方式定义你的组件,这个工厂函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓存起来供未来重渲染。

1
2
3
components: {
asyncComponent: () => import('../components/asyncComponent')
}

keep-alive的作用是什么

查看答案

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。

keep-alive


Vue是如何实现数据的双向绑定的

查看答案

Vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式 的方式来实现的,也就是说数据和视图同步,如果数据发生变化,那么视图也跟着变化,视图变化,数据也随之发生改变;其核心是 Object.defineProperty() 方法。


过滤器是什么,怎么使用呢

查看答案

Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式。过滤器应该被添加在 JavaScript 表达式的尾部,由 “管道” 符号指示:

1
2
3
4
5
<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>

有两种注册方式:全局注册、局部注册。

1
2
3
4
5
6
7
8
9
10
11
// 全局注册
Vue.filter('capitalize', function (value) {
...
})

// 局部注册
filters: {
capitalize: function (value) {
...
}
}

注意:当全局过滤器和局部过滤器重名时,会采用局部过滤器。

PS:从 Vue 3.0 开始,过滤器已删除,不再支持。在 3.x 中,可以用方法调用或计算属性替换它们。


Vue中的$nextTick有什么作用

查看答案
  1. Vue.nextTick(callback):在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。
  2. vm.$nextTick(callback):将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新。

看如下代码:

1
2
3
4
<button @click="add">add</button>
<ul ref="ulRef">
<li v-for="item in items">{{item}}</li>
</ul>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let app = new Vue({
el: '#app',
data() {
return {
items: [1, 2, 3]
}
},
methods: {
add() {
this.items.push(Math.random())
let ele = this.$refs.ulRef
let length = ele.childNodes.length
console.log(length);
}
},
})

当我们点击按钮后,会发现控制台打印出 3,这不是我们想要的,我们认为此时应该打印出 4,这时候需要用到 $nextTick 来解决此问题。

1
2
3
4
5
6
7
8
add() {
this.items.push(Math.random())
this.$nextTick(() => {
let ele = this.$refs.ulRef
let length = ele.childNodes.length
console.log(length);
})
}

对mixin的理解,有什么应用场景

查看答案

Vue.observable()有什么用,怎么用

查看答案

它可以让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象。

返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器。

在 Vue 2.x 中,被传入的对象会直接被 Vue.observable 变更,它和被返回的对象是同一个对象。而在 Vue 3.x 中,则会返回一个可响应的代理,而对源对象直接进行变更仍然是不可响应的。

在非父子组件通信时,可以使用 EventBus 或者 Vuex,但是如果实现的功能不是很复杂,就可以使用 observable。

创建 observable.js 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import Vue from 'vue'

let state = Vue.observable({
name: 'LqZww',
age: 18
})

let mutations = {
changeName(name) {
state.name = name
},
changeAge(age) {
state.age = age
}
}

export { state, mutations }

然后随便在一个 vue 文件中使用:

1
2
3
4
5
6
7
<template>
<div>
{{name}} - {{age}}
<button @click="changeName('张三')">changeName</button>
<button @click="changeAge(99)">changeAge</button>
</div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<script>
import { state, mutations } from "../observable";
export default {
computed: {
name() {
return state.name;
},
age() {
return state.age;
},
},
methods: {
changeName: mutations.changeName,
changeAge: mutations.changeAge,
},
};
</script>

自定义指令怎么写

查看答案

自定义指令分全局注册和局部注册。

全局注册:通过 Vue.directive 方法进行注册。
局部注册:通过在组件 options 选项中设置 directive 属性。

1
2
3
4
5
6
// 全局注册
Vue.directive('focus', {
inserted: function (el) {
el.focus()
}
})
1
2
3
4
5
6
7
8
// 局部注册
directives: {
focus: {
inserted: function (el) {
el.focus()
}
}
}

自定义指令有如下几个钩子函数:

  1. bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置。
  2. inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
  3. update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新。
  4. componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
  5. unbind:只调用一次,指令与元素解绑时调用。

指令钩子函数会被传入以下参数:

  • el:指令所绑定的元素,可以用来直接操作 DOM。
  • binding:一个对象,包含以下 property:
    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 “foo”。
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
  • vnode:Vue 编译生成的虚拟节点。
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。

注意:除了 el 之外,其它参数都应该是只读的,切勿进行修改。如果需要在钩子之间共享数据,建议通过元素的 dataset 来进行。


什么是虚拟DOM


vue2.0兼容IE哪个版本

查看答案

不支持 IE8 及以下,因为 Vue 的双向绑定原理是 Object.defineProperty,IE8 及以下不支持此 API。部分兼容 IE9,完全兼容 IE10 以上。


Vue有哪些推荐的风格指南

查看答案
  1. 组件名为多个单词,这样可以避免跟现有的以及未来的 HTML 元素相冲突,例如:todo-item;
  2. 组件的 data 必须是一个函数;
  3. Prop 定义应该尽量详细;
  4. 使用 v-for 时,必须使用 key;
  5. 避免 v-if 和 v-for 用在同一个元素上;
  6. 为组件样式设置作用域(scoped);
  7. …

更多详情请参考 Vue.js 风格指南


如何解决动态设置img的src不生效

查看答案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<template>
<div>
<img :src="imgUrl">
</div>
</template>

<script>
export default {
data() {
return {
imgUrl: require("assets/img/demo.jpg")
};
}
};
</script>

Vue-Router

vue-router路由模式有几种

查看答案

有两种模式,分别为 hash 模式和 history 模式。

hash 模式:
地址栏 URL 中的 # 符号。比如此 URL:https://www.lqzww.top/posts/4a5e2e1d/#vue-router,hash 的值为#vue-router。

  1. url 路径会出现 # 字符;
  2. hash 值不包括在 Http 请求中,它是由前端路由处理,所以改变 hash 值时不会刷新页面,也不会向服务器发送请求;
  3. hash 值的改变会触发 hashchange 事件;

history 模式:
利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器的历史记录栈,在当前已有的 back、forward、go 的基础之上,它们提供了对历史记录进行修改的功能。只是当它们执行修改时,虽然改变了当前的 URL,但浏览器不会立即向后端发送请求。

  1. 整个地址重新加载,可以保存历史记录,方便前进后退;
  2. 依赖 H5 API 和后台配置,没有后台配置的话,页面刷新时会出现 404;

hash模式和history模式有什么区别

查看答案
  1. hash 模式 url 带 # 号,而 history 模式不带 # 号;
  2. 刷新⻚⾯时,hash 模式可以加载到 hash 值对应的⻚⾯,⽽ history 模式没有处理的话,会返回 404;
  3. hash ⽀持低版本浏览器和 IE,而 history 是 HTML5 新推出的 API。

vue-router有哪几种导航钩子

查看答案

vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的、单个路由独享的、组件级的。

参数或查询的改变并不会触发 进入 / 离开 的导航守卫。你可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。

  1. 全局守卫:router.beforeEach;
  2. 全局解析守卫:router.beforeResolve;
  3. 全局后置钩子:router.afterEach;
  4. 路由独享的守卫:beforeEnter;
  5. 组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave;

官方文档 - 导航守卫


vue-router导航解析的流程是什么

查看答案
  1. 导航被触发。
  2. 在失活的组件里调用 beforeRouteLeave 守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter。
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter。
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

官方文档 - 完整的导航解析流程


route和router的区别

查看答案
  1. $route 为当前 router 跳转对象,里面可以获取当前路由的 fullPath、hash、metched、meta、name、path、params、query 路由信息参数。
  2. $router 是 VueRouter 的实例,是全局的路由对象,里面包括了路由的跳转方法,钩子函数等等。

在vue组件中如何获取到当前的路由信息

查看答案

通过使用 this.$route 来获取。


路由跳转方式有哪些

查看答案
  1. router-link
  2. this.$router.push()
  3. this.$router.replace()
  4. this.$router.go()
  5. this.$router.forward()
  6. this.$router.back()

vue-router - 编程式的导航


vue-router实现路由懒加载

查看答案

主要有以下三种方式:

  1. Vue 异步组件技术:结合 Vue 的 异步组件 和 Webpack 的代码分割功能可以实现路由组件的懒加载。在这种情况下打包后,每一个组件生成一个 js 文件。
  2. 路由懒加载:使用动态 import 语法。
  3. require.ensure():使用 webpack 的 require.ensure 技术,也可以实现按需加载。这种情况下,多个路由指定相同的 chunkName,会合并打包成一个 js 文件。

active-class是哪个组件的属性

查看答案

active-class 它是属性 vue-router 的样式方法,当 router-link 标签被点击时,就会使用该样式。

有如下两种使用方法:

  1. 直接在 router-link 标签上使用:

    1
    <router-link to="/about" active-class="active">about</router-link>
  2. 在 router/index.js 文件中配置:

    1
    2
    3
    4
    const router = new VueRouter({
    routes,
    linkActiveClass: 'active'
    })

注意:在首页的 active 会被一直应用着。

解决办法:

  1. 添加 exact 属性:

    1
    <router-link to="/" active-class="active" exact>home</router-link>
  2. 在 router/index.js 文件中配置:

    1
    2
    3
    4
    const router = new VueRouter({
    routes,
    linkExactActiveClass: 'active'
    })

vue-router中params与query的区别

查看答案

query 传参与接收参数的方式:

1
2
3
4
5
6
7
8
this.$router.push({
path: "one",
query: {
num: 1,
},
})

this.$route.query.num

params 传参与接收参数的方式:

1
2
3
4
5
6
7
8
this.$router.push({
name: "two",
params: {
id: 1,
},
})

this.$route.params.id

区别如下:

  1. 用法:query 用 path 来引入,而 params 用 name 来引入;
  2. url:query 传递的参数要在浏览器地址栏上显示,而 params 不会显示;
  3. 刷新:query 刷新不会丢失 query 里面的数据,而 params 刷新会丢失 params 里面的数据。

vue-router怎么配置404页面

查看答案
1
2
3
4
5
6
7
8
const routes = [
...
{
path: '*',
name: '404',
component: () => import('../views/404.vue')
}
]

注意:需要将 404 页面放到路由的最后面。


vue-router怎样响应路由参数的变化呢

查看答案

当使用路由参数时,例如从 /user/foo 导航到 /user/bar,原来的组件实例会被复用。因为两个路由都渲染同一个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。

  1. 利用 watch 监听

    1
    2
    3
    4
    5
    watch: {
    $route(to, from){
    // 对路由变化作出响应...
    }
    }
  2. 利用 beforeRouteUpdate 导航守卫

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const User = {
    template: `...`,
    beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
    },
    }
  3. router-view 上加个 key

    1
    <router-view :key="$route.fullPath"></router-view>

$route.fullPath 是完成后解析的 URL,包含其查询参数信息和 hash 完整路径


Vuex

Vuex是什么,怎么使用,哪种功能场景使用它

查看答案

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。它实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享。

使用 Vuex 统一管理状态有如下好处:

  1. 能够在 vuex 中集中管理共享的数据,易于开发和后期维护;
  2. 能够高效地实现组件之间的数据共享,提高开发效率;
  3. 存储在 vuex 中的数据都是响应式的,能够实时保持数据与页面的同步;

在 main.js 引入 store,注入。新建了一个 store 目录,然后 ...export。

场景:单页应用中,组件之间的共享状态和方法。

Vuex官网


Vuex有哪几种属性

查看答案
  1. State:用于数据的存储,提供唯一的公共数据源,所有共享的数据都要统一放到 Store 的 State 中进行存储;
  2. Getter:用于对 Store 中的数据进行加工处理形成新的数据,类似 Vue 中的计算属性,Store 中数据发生变化,Getter 的数据也会跟着变化;
  3. Mutation:用于变更 Store 中的数据,且不能用于处理异步操作;
  4. Action:用于处理异步任务,如果通过异步操作变更数据,必须通过 Action,而不能使用 Mutation,但是在 Action 中还是要通过触发 Mutation 的方式间接变更数据;
  5. Module:类似于命名空间,用于项目中将各个模块的状态分开进行定义和操作;

Vuex中actions和mutations有什么区别

查看答案
  • mutations:它只能进行同步操作,可以直接修改 state,通过 commit 进行提交。
  • actions:它进行异步操作,是无法直接修改 state,只能通过触发 mutation 的方式间接变更数据,通过 dispatch 进行触发。

它们具体的使用请查看 Vue2从入门到放弃。


页面刷新后state数据丢失了怎么办

查看答案
  1. 将 state 数据保存在 localstorage、sessionstorage 或者 cookie 中;
  2. 使用vuex-persistedstate。

Vue-Loader

vue-loader是什么及用途

查看答案

vue-loader 是解析 .vue 文件的一个加载器,它可以将 template / js / style 转换成 JS 模块。

⽤途:JS 可以写 ES6、style 样式可以 scss / less、template 可以加 jade 等等。


Vue3

Options Api与Composition Api的区别

查看答案

Options Api:选项 API,可以用包含多个选项的对象来描述组件的逻辑,通过定义 data、methods,computed,watch 等属性与方法,共同处理页面逻辑。当组件变得复杂,会导致对应属性的列表也会增长,这可能会导致组件难以阅读和理解。

Composition Api:组合式 API,组件根据逻辑功能来组织的,一个功能所定义的所有 API 会放在一起(更加的高内聚,低耦合)。

  1. 在逻辑组织和逻辑复用方面,Composition API 是优于 Options API 的;
  2. Composition API 几乎都是函数,会有更好的类型推断;
  3. Composition API 对 tree-shaking 友好,代码也更容易压缩;
  4. Composition API 中见不到 this 的使用,减少了 this 指向不明的情况;

script setup是什么

查看答案

它是 vue3 的语法糖,简化了组合式 API,它有如下特点:

  1. 属性和方法都无需返回,可以直接使用;
  2. 引入组件的时候,会自动注册,无需使用 components 来手动注册;
  3. 使用 defineProps 接收父组件传递的值,使用 defineEmits 获取自定义事件;
  4. useAttrs 获取属性,useSlots 获取插槽;
  5. 默认不会对外暴露任何属性,如果需要可使用 defineExpose;

注意:部分答案来源于网络,如发现有错误,请联系我修正,谢谢!

本文将不定期持续更新,欢迎您收藏本站!😘