1. 1. 一.HTML
    1. 1.1. 1.html语义化
    2. 1.2. 2.HTML5新标签
    3. 1.3. 3.html5语义化
    4. 1.4. 4.html5新特性
    5. 1.5. 5.对WEB标准和W3C的理解认识
    6. 1.6. 6.什么是DOCTYPE及作用
  2. 2. 二.CSS
    1. 2.1. 1.盒模型
    2. 2.2. 2.rem和em的区别
    3. 2.3. 3.常见单位
    4. 2.4. 4.移动端视口配置
    5. 2.5. 5.渐进增强与优雅降级的理解及区别
    6. 2.6. 6.cookie、sessionStorage、localStorage区别
    7. 2.7. 7.css选择器
    8. 2.8. 8.css3新特性
    9. 2.9. 9.行内元素和块级元素
    10. 2.10. 10.css的position的定位
    11. 2.11. 11.Flex布局
    12. 2.12. 12.display有哪些值?说明他们的作用?
    13. 2.13. 13.BFC
    14. 2.14. 14.水平垂直居中
    15. 2.15. 15.Sass、Less、Stylus区别
    16. 2.16. 16.display: none与visibility: hidden的区别
    17. 2.17. 17.重绘 & 回流
    18. 2.18. 18.防抖(debounce)
    19. 2.19. 19.节流(throttle)
    20. 2.20. 20.获取盒子宽高的几种方式及区别
    21. 2.21. 21.link和import区别
    22. 2.22. 22.多行元素省略号
  3. 3. 三.JS
    1. 3.1. 1.JS的基本数据类型
    2. 3.2. 2.几种判断数据类型的优缺点
    3. 3.3. 3.null和undefined的区别
    4. 3.4. 4.对象深浅拷贝
    5. 3.5. 5.数组基本方法
    6. 3.6. 6.js有那些内置对象
    7. 3.7. 7.get请求传参长度的误区
    8. 3.8. 8.同步任务和异步任务
    9. 3.9. 9.事件和回调函数
    10. 3.10. 10.定时器
    11. 3.11. 11.进程和线程
    12. 3.12. 12.axios在vue.js中应用和特点
    13. 3.13. 13.HTTP中定义请求方式
    14. 3.14. 14.get和post区别
    15. 3.15. 15.http中content-type
    16. 3.16. 16.import和require区别
    17. 3.17. 17.ajax
    18. 3.18. 18.闭包
    19. 3.19. 19.js作用域和作用域链
    20. 3.20. 20.组件化和模块化
    21. 3.21. 21.图片的预加载和懒加载
    22. 3.22. 22.mouseover和mouseenter的区别
    23. 3.23. 23.对This对象的理解
    24. 3.24. 24.ES6其他常用功能
    25. 3.25. 25.bind、call、apply用法及区别
    26. 3.26. 26.目前JS解决异步的方案有哪些
    27. 3.27. 27.创建对象有几种方法
  4. 4. 四.Vue
    1. 4.1. 1.Vue生命周期的作用是什么?
    2. 4.2. 2.Vue生命周期总共有几个阶段?
    3. 4.3. 3.DOM渲染在哪个周期中就已经完成?
    4. 4.4. 4.每个生命周期适合哪些场景?
    5. 4.5. 5.关于vue的keep-alive需要条件性缓存的解决
    6. 4.6. 6.Vue 路由懒加载
    7. 4.7. 7.Proxy与Object.defineProperty()的对比
    8. 4.8. 8.v-show与v-if区别
    9. 4.9. 9.vue有哪些指令
    10. 4.10. 10.组件之间的传值通信
    11. 4.11. 11.子组件调用父组件的方法函数
    12. 4.12. 12.路由跳转方式
    13. 4.13. 13.mvvm
    14. 4.14. 14.computed和watch有什么区别?
    15. 4.15. 15.key
    16. 4.16. 16.组件中的data为什么是函数?
    17. 4.17. 17.Class 与 Style 如何动态绑定?
    18. 4.18. 18.vue的单向数据流
    19. 4.19. 19.keep-alive
    20. 4.20. 20.v-model 的原理
    21. 4.21. 21.nextTick()
    22. 4.22. 22.vue插槽
    23. 4.23. 23.导航守卫
    24. 4.24. 24.vuex是什么?
    25. 4.25. 25.优化SPA首屏加载速度
    26. 4.26. 26.你有对 Vue 项目进行哪些优化?
  5. 5. 五.ES6
    1. 5.1. 1.var let const区别
    2. 5.2. 2.解构赋值
    3. 5.3. 3.forEach、for in、for of三者区别
    4. 5.4. 4.使用箭头函数应注意什么?
    5. 5.5. 5.Set、Map的区别
    6. 5.6. 6.Ajax
    7. 5.7. 7.同步和异步的区别
    8. 5.8. 8.ajax的优点和缺点
    9. 5.9. 9.get和post的区别
    10. 5.10. 10.什么时候使用post?
    11. 5.11. 11.同源策略
    12. 5.12. 12.如何解决跨域问题?
  6. 6. 六.浏览器
    1. 6.1. 1.主流浏览器
    2. 6.2. 2.浏览器内核
    3. 6.3. 3.浏览器兼容
  7. 7. 七.其他
    1. 7.1. 1.前端组件化和模块化
    2. 7.2. 2.什么是Ajax和JSON,它们的优点和缺点
    3. 7.3. 3.Github
    4. 7.4. 4.webpack
    5. 7.5. 5.微信小程序
    6. 7.6. 6.微信小程序支付流程
    7. 7.7. 7.微信网页授权流程
    8. 7.8. 8.小程序登录流程
    9. 7.9. 9.小程序授权
    10. 7.10. 10.网络协议
    11. 7.11. 11.HTTP/HTTPS
    12. 7.12. 12.从输入URL到页面加载到过程?
    13. 7.13. 13.HTTP状态码
    14. 7.14. 14.性能优化

前端面试基础

一.HTML

1.html语义化

  • 使页面内容结构化,即使丢失样式也能使页面呈现清晰的结构

  • 有利于SEO,搜索引擎根据标签确定上下文和关键字的权重有利于开发和维护

  • 语义化更具有可读性,代码更好维护

  • 方便其他设备解析,如配合盲人阅读器渲染页面易于用户阅读,
    如:

    header 定义头部内容
    nav 定义导航区域
    main 定义主要文档内 容
    article 表示文章、博客等内容
    aside 侧边内容
    footer 尾部

div、article、section

div、article、section是语义从无到有,逐渐增强的。
div无任何语义,仅仅用作样式化或脚本的标签。
对于一段主题性的内容,则适用于section元素。
对于可脱离上下文,作为一段完整独立内容的,适用于article。

  • article元素代表文档、页面或应用程序中独立的、完整的、可以独自被外部引用的内容。它可以是一篇博客或报刊中的文章、一篇论坛帖子、一段用户评论或独立的插件,或其他任何独立的内容。除了内容部分,一个article元素通常有它自己的标题(一般放在一个header元素里面),有时还有自己的脚注

  • section元素的作用是对页面上的内容进行分块,或者说对文章进行分段;一个section元素通常由内容及其标题组成,通常不推荐为那些没有标题的内容使用section元素

2.HTML5新标签

<header> <footer> <nav> <aside> <aduio> <video> <canvas>

3.html5语义化

表示选择合适的标签(语义化标签)便于开发者阅读和写出更优雅的代码

4.html5新特性

HTML5新特性有哪些?

语义化标签
音视频处理
canvas / webGL
history API
requestAnimationFrame
地理位置
webSocket

5.对WEB标准和W3C的理解认识

个人理解:

html - 表示人的光身体 ---结构
css - 表示给人穿的衣服 ---表现
js - 表示人的行为,走路等 ---行为

web简单来说可以分为结构、表现和行为。

  • 其中结构主要是由HTML标签组成。
  • 表现是指css样式表,可用通过css使页面标签更具美感。
  • 行为是指用户和页面有一定的交互,同时结构和表现也会发生变化,主要由js组成

web标准一般是将该三部分独立分开,使其更具有模块化。但一般产生行为时,就会有结构或者表现的变化,也使这三者的界限并不那么清晰。

W3C对web标准提出了规范化的要求,也就是在实际编程中的
一些代码规范:

1.对于结构要求(标签规范可以提高搜索引擎对页面的抓取率,对seo很有帮助):
    标签要小写
    标签要闭合
    标签不能随意嵌套

2.对于css和js来说:
    使用外链css和js,使结构、表现、行为三者分离。
    提高页面渲染速度,提高用户体验

    尽量少使用行内样式,使结构和表现分离,标签的id和class要见文知意,标签越少,加载越快,用户体验越高

    不需要变动页面内容,便可提供打印版本而不需要复制内容,提高网站易用性。

6.什么是DOCTYPE及作用

DTD(document type definition,文档类型定义)是一系列的语法规则,用来定义XML或(X)HTML的文件类型。浏览器会使用它来判断文档类型,决定使用何种协议来解析以及切换浏览器模式。(DTD告诉浏览器我是什么文档类型,浏览器会根据这个来判断用什么引擎来解析和渲染他们)

DOCTYPE是用来声明文档类型和DTD规范的,一个主要的用途便是文件的合法性验证。如果文件代码不合法,那么浏览器解析时会出一些错误。(DOCTYPE告诉浏览器当前是哪个文档类型)

作用:
告诉浏览器用哪种HTML版本的规范来解析HTML文档

二.CSS

1.盒模型

  • 组成:border+padding+content+margin
  • 标准盒模型:width: content box-sizing: content-box
  • 怪异盒模型/IE:width: content+padding+border box-sizing:border-box

2.rem和em的区别

rem是根据根元素的font-size变化,em是根据父元素的font-size变化

  • rem:相对于根元素html的font-size,假如html为font-size:12px,那么,在其当中的div设置为font-size:2rem,就是当中的div为24px
  • em:相对于当前对象内文本的字体大小计算,假如某个p元素为font-size:12px,在它内部有个span标签继承p元素字体大小,设置font-size:2em,那么,这时候的span字体大小为:12*2=24px

3.常见单位

  1. px:绝对单位,页面按精确像素展示
  2. em:相对单位,基准点为当前对象内文本的字体大小,整个页面内1em不是一个固定的值
  3. rem:相对单位,可理解为”root em”, 相对根节点html的字体大小来计算,CSS3新加属性,chrome/firefox/IE9+支持
  4. vw:viewpoint width,视窗宽度,1vw等于视窗宽度的1%
  5. vh:viewpoint height,视窗高度,1vh等于视窗高度的1%
  6. vmin:vw和vh中较小的那个
  7. vmax:vw和vh中较大的那个
  8. %:百分比

4.移动端视口配置

<meta name="viewport" content="width=device-width,
initial-scale=1.0,minimum-scale=1.0,
maximum-scale=1.0,user-scalable=no">
initial-scale:初始的缩放比例
minimum-scale:允许用户缩放到的最小比例
maximum-scale:允许用户缩放到的最大比例
user-scalable:用户是否可以手动缩放

5.渐进增强与优雅降级的理解及区别

  • 渐进增强(Progressive Enhancement):一开始就针对低版本浏览器进行构建页面,完成基本的功能,然后再针对高级浏览器进行效果、交互、追加功能达到更好的体验。

  • 优雅降级(Graceful Degradation):一开始就构建站点的完整功能,然后针对浏览器测试和修复。比如一开始使用 CSS3 的特性构建了一个应用,然后逐步针对各大浏览器进行hack 使其可以在低版本浏览器上正常浏览。

两者区别?

1、广义:
其实要定义一个基准线,在此之上的增强叫做渐进增强,在此之下的兼容叫优雅降级
2、狭义:
渐进增强一般说的是使用CSS3技术,在不影响老浏览器的正常显示与使用情形下来增强体验,而优雅降级则是体现html标签的语义,以便在js/css的加载失败/被禁用时,也不影响用户的相应功能

6.cookie、sessionStorage、localStorage区别

相同点:

  • 都是保存在浏览器端、且同源的

不同点:

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

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

  • cookie设置的过期时间之前一直有效;localStorage永久存储,浏览器关闭后数据不丢失除非主动删除数据;sessionStorage数据在当前浏览器窗口关闭后自动删除

  • cookie的数据会自动的传递到服务器;sessionStorage和localStorage数据保存在本地

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

7.css选择器

  • 常用css选择器

    id选择器 #header
    类选择器 .header
    元素选择器 div
    通配符 *
    后代选择器 div p, p a
    儿子选择器 div > span
    兄弟选择器 div + p, h2 ~ h3
    伪类选择器 a:hover
    属性选择器 input[type=”text”]
    伪元素选择器 p::before p::first-line

  • css选择器权重

    !important > id > class > 元素和伪元素 > * > 继承 >默认

8.css3新特性

  • transition 过渡
  • transform 旋转 倾斜 移动 缩放
  • animation 动画
  • shadow 阴影
  • grdient 渐变
  • border-radius 圆角

9.行内元素和块级元素

  • 行内元素(display: inline):

    • 设置宽高无效,宽度和高度由内容决定
    • 设置margin左右有效,上下无效,padding都有效
    • 不会自动换行
    • 有span,img,input,a,b,sub,sup,i
  • 块级元素(display:block):

    • 可以设置宽高
    • margin和padding都有效
    • 自动换行
    • 多个块元素写一起,排列从上到下
    • 由div,p,nav,h,footer,main,header等
  • 行内块元素(display:inline-block)

    • 能够设置宽高
    • margin/padding都有效
    • 不会自动换行
    • 默认排列方式从左到右

10.css的position的定位

relative absolute fixed static

  • 绝对定位: absolute和fiexed统称为绝对定位
  • 相对定位: relative
  • 默认值:static
    相对定位和绝对定位的区别:
  • relative:
    相对于自身位置定位,仍处于文档流中,元素的宽高不变,设置偏移量也不会影响其他元素的位置,如果最外层设置relative,在没有设置宽度情况,元素宽度是整个浏览器的宽度
  • absolute:
    相对于离自己最近的设置了相对或绝对定位的父元素定位,如果没有父元素设置相对和绝对定位,则相对于跟元素html定位,设置了绝对定位的元素脱离了文档流,如果没有设置宽高由元素内容决定,脱离后元素位置是空的下面的元素会占据
  • fixed:
    相对于浏览器窗口定位,如果没有设置宽高由元素内容决定。

11.Flex布局

Flexbox为Flexible box缩写,“弹性布局”,任何一个元素都能设置弹性布局包含两部分,一个为容器,一个为项目
水平的主轴(main axis)和垂直的交叉轴(cross axis)
主轴的排列方式:从左到右;交叉轴的排列方式:从上到下;

容器的属性:

  • flex-direction:主轴的方向-row/row-reverse/column/column-reverse
  • flex-wrap:项目排列方式是否换行–nowrap/wrap/wrap-reverse
  • flex-flow:direction和wrap的缩写
  • justify-content:项目在主轴排列方式–flex-start/flex-end/center/space-between/space-around
  • align-items:项目在交叉轴的如何对齐-flex-start/flex-end/center/baseline/stretch

项目属性:

  • order:number (数值越小越靠前,默认为0)项目排列顺序
  • flex-grow :number(默认0,如果有剩余空间也不放大,值为1放大,2是1的双倍大小,此类推)定义项目放大比例
  • flex-shrink :number (默认为1,如果空间不足则会缩小,值为0不能缩小)项目缩小比例
  • flex-basis :number/auto (默认auto,可设置固定的值50px/50%)定义项目自身的大小
  • flex:属性是flex-grow,flex-shrink ,flex-basis的简写,默认值为0、1、auto
  • align-self :auto | flex-start | flex-end | center | baseline | stretch项目自身对齐

12.display有哪些值?说明他们的作用?

  • inline(默认)–内联
  • none–隐藏
  • block–块显示
  • table–表格显示
  • list-item–项目列表
  • inline-block-内联块

13.BFC

BFC块格式化上下文,是Web页面的可视CSS渲染的一部分.是块盒子的布局过程发生的区域,也是浮动元素与其他元素交互的区域。

下列方式会创建块格式化上下文

    根元素html
    浮动元素(元素的float不是none)
    绝对定位元素(元素的display为absolute或fixed)
    行内块元素(display:inline-block)
    表格单元格(display:table-cell)
    表格标题(display:table-caption)
    匿名表格单元格元素(display:table/table-row/table-row-group/table-header-group/table-footer-group或inline-table)
    overflow计算值不为visible的块元素
    display值为flow-root的元素
    contain值为layout/content/paint的元素
    弹性元素(display为flex或inline-flex 元素的直接子元素)
    网格元素(display为grid或inline-grid 元素的直接子元素)
    多列容器(元素的column-count或column-width部位auto)
    column-span为all的元素始终会创建一个新的BFC,即使该元素没有包裹一个多列容器中。

BFC布局规则:

    内部的box会在垂直方向,一个一个排列
    Box垂直方向的距离由margin决定,同一个BFC内相邻的两个box的margin值会重叠
    每个元素的margin box的左边与border box的左边相接触,即使浮动也如此
    BFC区域不和float box区域重叠
    BFC是页面上一个独立的容器,内部子元素不回对外部元素产生影响
    计算BFC高度时,浮动元素也参与计算

BFC的使用场景?

去除边距重叠现象
清除浮动(让父元素的高度包含子浮动元素)
避免某元素被浮动元素覆盖
避免多列布局由于宽度计算四舍五入而自动换行

14.水平垂直居中

水平居中: margin:0 auto;text-align:center;flex:justify-content
垂直居中:line-height;flex:align-items
水平垂直居中:flex;absolute+margin负;absolute+translate负

15.Sass、Less、Stylus区别

什么是CSS预处理器?
CSS预处理器是一种语言用来为CSS增加一些编程的特性,无需考虑浏览器兼容问题,例如你可以在CSS中使用变量,简单的程序逻辑、函数等在编程语言中的一些基本技巧,可以让CSS更加简洁,适应性更强,代码更直观等诸多好处

  • 基本语法区别
    Sass是以.sass为扩展名,Less是以.less为扩展名,Stylus是以.styl为扩展名

  • 变量的区别
    Sass 变量必须是以$开头的,然后变量和值之间使用冒号(:)隔开,和css属性是一样的。
    Less 变量是以@开头的,其余sass都是一样的。
    Stylus 对变量是没有任何设定的,可以是以$开头或者任意字符,而且变量之间可以冒号,空格隔开,但是在stylus中不能用@开头

三种预处理器都有:嵌套、运算符、颜色函数、导入、继承、混入。Stylus还有一些高级特性。例如循环、判断等

16.display: none与visibility: hidden的区别

display:none 不显示对应的元素,在文档布局中不再分配空间(回流+重绘)

visibility:hidden 隐藏对应元素,在文档布局中仍保留原来的空间(重绘)

17.重绘 & 回流

浏览器渲染过程如下:

  • 解析HTML,生成DOM树,解析CSS,生成CSSOM树
  • 将DOM树和CSSOM树结合,生成渲染树(Render Tree)
  • Layout(回流):根据生成的渲染树,进行回流(Layout),得到节点的几何信息(位置,大小)
  • Painting(重绘):根据渲染树以及回流得到的几何信息,得到节点的绝对像素
  • Display:将像素发送给GPU,展示在页面上

通过构造渲染树,我们将可见DOM节点以及它对应的样式结合起来,可是我们还需要计算它们在设备视口(viewport)内的确切位置和大小,这个计算的阶段就是回流

我们通过构造渲染树和回流阶段,我们知道了哪些节点是可见的,以及可见节点的样式和具体的几何信息(位置、大小),那么我们就可以将渲染树的每个节点都转换为屏幕上的实际像素,这个阶段就叫做重绘节点。

何时发生回流重绘

  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
  • 页面一开始渲染的时候(这肯定避免不了)
  • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)

注意:回流一定会触发重绘,而重绘不一定会回流根据改变的范围和程度,渲染树中或大或小的部分需要重新计算,有些改变会触发整个页面的重排,比如,滚动条出现的时候或者修改了根节点。

当你获取布局信息的操作的时候,会强制队列刷新

最小化重绘和重排:

批量修改DOM:
使元素脱离文档流
对其进行多次修改
将元素带回到文档中。

有三种方式可以让DOM脱离文档流:

隐藏元素,应用修改,重新显示
使用文档片段(document fragment)在当前DOM之外构建一个子树,再把它拷贝回文档。
将原始元素拷贝到一个脱离文档的节点中,修改节点后,再替换原始的元素。

18.防抖(debounce)

防抖就是在触发事件n秒内函数只执行一次,如果在n秒内又触发了事件,就重新计时
如下,鼠标移动时,计数:

1
2
3
4
5
6
7
8
9
10
<div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
<script>
let num = 1;
let content = document.getElementById('content');

function count() {
content.innerHTML = num++;
};
content.onmousemove = count;
</script>

防抖函数分为非立即执行版和立即执行版

非立即执行版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function debounce(func,wait){
let timeout;
return function(){
let context = this;
let args = arguments;

if(timeout){
clearTimeout(timeout);
}
timeout = setTimeout(function(){
func.apply(context,args)
},wait)
}
}

在触发事件后函数 1 秒后才执行,而如果我在触发事件后的 1秒内又触发了事件,则会重新计算函数执行时间
content.onmousemove = debounce(count,1000);

let context = this;
let args = arguments;
防抖函数的代码使用这两行代码来获取 this 和参数,是为了让 debounce 函数最终返回的函数 this指向不变以及依旧能接受到 e 参数

立即执行版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function debounce(func,wait){
let timeout;
return function(){
let context = this;
let args = arguments;

if(timeout){
clearTimeout(timeout);
}

let callNow = !timeout;
timeout = setTimeout(function(){
timeout = null
},wait)
if (callNow) func.apply(context, args)
}
}

触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。

19.节流(throttle)

连续触发函数在n秒内只执行一次,稀释了函数的执行频率
时间戳版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function throttle(func,wait){
let previous = 0;
return function(){
let now = Date.now();
let context = this;
let args = arguments;
if(now-previous > wait){
func.apply(context,args);
previous = now;
}
}
}

content.onmousemove = throttle(count,1000);

在持续触发事件的过程中,函数会立即执行,并且每 1s 执行一次

定时器版:

1
2
3
4
5
6
7
8
9
10
11
12
13
function throttle(func,wait){
let timeout;
return function(){
let context = this;
let args = argments;
if(!timeout){
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}

在持续触发事件的过程中,函数不会立即执行,并且每1s 执行一次,在停止触发事件后,函数还会再执行一次。

时间戳版的函数触发是在时间段内开始的时候,而定时器版的函数触发是在时间段内结束的时候。

20.获取盒子宽高的几种方式及区别

  • dom.style.width/height
    这种方式只能取到dom元素内联样式所设置的宽高,也就是说如果该节点的样式是在style标签中或外联的CSS文件中设置的话,通过这种方法是获取不到dom的宽高的

  • dom.currentStyle.width/height
    获取渲染后的宽高。但是仅IE支持

  • window.getComputedStyle(dom).width/height
    与2原理相似,但是兼容性,通用性更好一些

  • dom.getBoundingClientRect().width/height
    计算元素绝对位置,获取到四个元素left,top,width,height

扩展:获取浏览器高度和宽度的兼容性写法:

1
2
var  w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
var h = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight

21.link和import区别

1
2
3
4
<style type="text/css">
@import url(CSS文件路径地址);
</style>
<link href="CSSurl路径" rel="stylesheet" type="text/css" />
  • import是css提供的语法规则,只有导入样式表的作用,link是html的标签,不仅可以加载样式还可以设置rel type等属性
  • import是css2才引入等,只支持ie5+,而link没有兼容问题
  • import在页面加载完毕才引入,link在加载页面时引入
  • link可用dom操作引入link加载样式

22.多行元素省略号

overflow : hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical

三.JS

1.JS的基本数据类型

Number String Boolean Null undefined 新增Symbol

2.几种判断数据类型的优缺点

一、typeof

1
2
3
4
5
6
7
8
console.log(typeof 1);               // number
console.log(typeof true); // boolean
console.log(typeof 'mc'); // string
console.log(typeof function(){}); // function
console.log(typeof []); // object
console.log(typeof {}); // object
console.log(typeof null); // object
console.log(typeof undefined); // undefined

优点:能够快速区分基本数据类型 缺点:不能将Object、Array和Null区分,都返回object

二、instanceof

instanceof用来检测构造函数的prototype属性是否存在某实例对象的原型链上

语法:object instanceof constructor
参数:
object:某个实例对象
constructor:某个构造函数
描述:二、instanceof用来检测constructor.prototype属性是否存在在object的原型链上。

1
2
3
4
5
6
console.log(1 instanceof Number);                    // false
console.log(true instanceof Boolean); // false
console.log('str' instanceof String); // false
console.log([] instanceof Array); // true
console.log(function(){} instanceof Function); // true
console.log({} instanceof Object); // true

优点:能够区分Array、Object和Function,适合用于判断自定义的类实例对象 缺点:Number,Boolean,String基本数据类型不能判断

三、Object.prototype.toString.call()

1
2
3
4
5
6
7
8
9
10
var toString = Object.prototype.toString;

console.log(toString.call(1)); //[object Number]
console.log(toString.call(true)); //[object Boolean]
console.log(toString.call('mc')); //[object String]
console.log(toString.call([])); //[object Array]
console.log(toString.call({})); //[object Object]
console.log(toString.call(function(){})); //[object Function]
console.log(toString.call(undefined)); //[object Undefined]
console.log(toString.call(null)); //[object Null]

优点:精准判断数据类型 缺点:写法繁琐不容易记,推荐进行封装后使用

3.null和undefined的区别

undefined是访问一个未初始化的变量时返回的值,而null是访问一个尚未存在的对象时所返回的值。

undefined看作是空的变量,而null看作是空的对象

4.对象深浅拷贝

一、深拷贝

  • 1.1 最简单的方法就是JSON.parse(JSON.stringify())
    但是这种拷贝方法不可以拷贝一些特殊的属性(例如正则表达式,undefine,function)
  • 1.2 用递归去复制所有层级属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function deepCopyTwo(obj) {
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj == 'object') {
for (const key in obj) {
//判断obj子元素是否为对象,如果是,递归复制
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepCopyTwo(obj[key]);
} else {
//如果不是,简单复制
objClone[key] = obj[key];
}
}
}
return objClone;
}

二、浅拷贝

1
2
3
4
5
6
7
8
9
Object.assign(target, ...sources)

function simpleClone(obj) {
var result = {};
for (var i in obj) {
result[i] = obj[i];
}
return result;
}

5.数组基本方法

  • push():将参数逐个添加到数组尾部,返回修改后的数组长度

  • unshift():将参数逐个添加到数组前端,返回修改后的数组长度

  • pop():移除数组中的最后一项,返回移除的项

  • shift():移除数组中的第一项,返回移除的项

push pop shift unshift 都会直接改变原数组

  • map: 遍历数组,返回回调返回值组成的新数组

  • forEach: 无法break,可以用try/catch中throw new Error来停止

  • filter: 过滤

  • some: 有一项返回true,则整体为true

  • every: 有一项返回false,则整体为false

  • join: 通过指定连接符生成字符串

  • concat: 连接数组,不影响原数组, 浅拷贝

  • slice(start, end): 返回截断后的新数组,不改变原数组

  • splice(start, number, value…):返回删除元素组成的数组,value 为插入项,改变原数组

  • indexOf / lastIndexOf(value, fromIndex): 查找数组项,返回对应的下标

  • reduce / reduceRight(fn(prev, cur), defaultPrev): 两两执行,prev 为上次化简函数的return值,cur 为当前值(从第二项开始)

map和forEach的区别:

1.都用来遍历数组,map速度比forEach快
2.map返回回调返回值组成的新数组,不会对原数组产生影响,forEach没有返回值,不能return或者break
3.map返回数组,所以可以链式调用

6.js有那些内置对象

Object是JavaScript中所有对象的父对象

数据封装对象:Object、Array、Boolean、Number和String
其他对象:Function、Arguments、Math、Date、RegExp、Error

7.get请求传参长度的误区

  • HTTP 协议 未规定 GET 和POST的长度限制
  • GET的最大长度显示是因为 浏览器和 web服务器限制了 URI的长度
  • 不同的浏览器和WEB服务器,限制的最大长度不一样
  • 要支持IE,则最大长度为2083byte,若只支持Chrome,则最大长度 8182byte

补充get和post请求在缓存方面的区别

  • get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存
  • post不同,post做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。

8.同步任务和异步任务

  • 同步任务:在主线程上执行的任务,只有前一个任务执行完,才能执行下一个任务

  • 异步任务:不进入主线程而进入“任务队列”的任务,只有任务队列通知主线程,某个任务可以执行了,该任务才会进入主线程执行。

    异步执行的运行机制如下。(同步执行也是如此,因为它可以被视为没有异步任务的异步执行。):
    1)所以同步任务都在主线程执行,形成一个执行栈
    2)主线程之外,还有一个任务队列,只要异步任务有了执行结果,就在任务队列放置一个事件
    3)一旦执行栈中的所有同步任务执行完毕,系统就会读取任务队列,看看有哪些事件,对应的事件就结束等待状态,进入执行栈进行执行
    4)主线程不断重复第三步

    主线程从“任务队列”中读取事件,这个事件是循环不断的,又称为事件循环(Event loop)

9.事件和回调函数

“任务队列”就是一个事件的队列,当IO设备完成一项任务,就在“任务队列”中添加一个事件,表示相关的异步任务可以进入执行栈了,主线程读取“任务队列”,就是读取有哪些事件

“回调函数”(callback)就是被主线程挂起来的代码。异步任务必须指定回调函数,主线程执行异步任务就是执行对应的回调函数。

10.定时器

定时器功能主要由setTimeout()和setInterval()这两个函数来完成,它们的内部运行机制完全一样,区别在于前者指定的代码是一次性执行,后者则为反复执行

1
2
3
console.log(1);
setTimeout(function(){console.log(2);},1000);
console.log(3);

上面代码的执行结果是1,3,2,因为setTimeout()将第二行推迟到1000毫秒之后执行。

如果将setTimeout()的第二个参数设为0,就表示当前代码执行完(执行栈清空)以后,立即执行(0毫秒间隔)指定的回调函数。

1
2
setTimeout(function(){console.log(1);}, 0);
console.log(2);

上面代码的执行结果总是2,1,因为只有在执行完第二行以后,系统才会去执行”任务队列”中的回调函数。

setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行

HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒,如果低于这个值,就会自动增加.需要注意的是,setTimeout()只是将事件插入了”任务队列”,必须等到当前代码(执行栈)执行完,主线程才会去执行它指定的回调函数。要是当前代码耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行

除了setTimeout和setInterval这两个方法,Node.js还提供了另外两个与”任务队列”有关的方法:process.nextTick和setImmediate。

  • process.nextTick方法可以在当前”执行栈”的尾部—-下一次Event Loop(主线程读取”任务队列”)之前—-触发回调函数。也就是说,它指定的任务总是发生在所有异步任务之前。setImmediate方法则是在当前”任务队列”的尾部添加事件,也就是说,它指定的任务总是在下一次EventLoop时执行,这与setTimeout(fn, 0)很像

  • process.nextTick和setImmediate的一个重要区别:多个process.nextTick语句总是在当前”执行栈”一次执行完,多个setImmediate可能则需要多次loop才能执行完

另外,由于process.nextTick指定的回调函数是在本次”事件循环”触发,而setImmediate指定的是在下次”事件循环”触发,所以很显然,前者总是比后者发生得早,而且执行效率也高(因为不用检查”任务队列”)。

11.进程和线程

进程和线程是操作系统的基本概念

单个cpu一次只能运行一个任务,任一时刻,CPU总是运行一个进程,其他进程处于非运行状态

  • 一个进程可用包含多个线程
  • 一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。
  • 一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。”互斥锁”(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。

某些内存区域,只能供给固定数目的线程使用。”信号量”(Semaphore),用来保证多个线程不会互相冲突

操作系统的设计,因此可以归结为三点:

(1)以多进程形式,允许多个任务同时运行;

(2)以多线程形式,允许单个任务分成不同的部分运行;

(3)提供协调机制,一方面防止进程之间和线程之间产生冲突,另一方面允许进程之间和线程之间共享资源

12.axios在vue.js中应用和特点

axios是基于promise的http请求客户端,可用在浏览器和node。js中使用

使用场景:结合vue.js发送请求,拦截请求

特点:
1.基于promise
2.拦截请求和响应
3.转换请求和响应的数据
4.可在node.js中使用

安装:

1
2
npm install --save axios
import axios from ‘axios’

结合Vue.js的请求响应拦截器:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// 请求拦截
axios.interceptors.request.use(config => {
if (config.url.indexOf('/oauth/token?grant_type=password') != -1) {
config.headers['Authorization'] = 'Basic Y2xpOnNlYw=='
// config.headers['Content-Type'] = 'application/json;charset-UTF-8'
} else if (getToken()) {
config.headers['Authorization'] = 'Bearer ' + getToken() + ''
}
return config
}, error => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
})

// 响应拦截
axios.interceptors.response.use((res) => {
if (res.data.code !== '0') {
switch (res.data.code) {
case '401':
window.location.href = '/login'
break
case '403':
Notification.error({
title: '错误:403',
message: '访问拒绝'
})
break
case '404':
Notification.error({
title: '错误:404',
message: '找不到网络资源'
})
break
case '500':
Notification.error({
title: '错误:500',
message: res.data.error.errorMsg
})
break
}
}
return res.data
}, (err) => {
if (err.response) {
switch (err.response.status) {
case '401':
window.location.href = '/login'
break
case '403':
Notification.error({
title: '错误:403',
message: '访问拒绝'
})
break
case '404':
Notification.error({
title: '错误:404',
message: '找不到网络资源'
})
break
case '500':
Notification.error({
title: '错误:500',
message: '服务器出错'
})
break
}
}
return Promise.reject(err)
})

// 请求封装
export default function _axios (method, url, params) {
method = method.toUpperCase()
var options = {
method: method,
url: url,
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
}

if (method === 'POST' || method === 'PATCH' || method === 'PUT') {
if (params) {
options.data = params
}

return axios(options)
.then((res) => {
return res
}, (error) => {
return error
})
}

if (method === 'GET' || method === 'DELETE') {
if (params) {
options.params = params
}

return axios(options)
.then((res) => {
return res
}, (error) => {
return error
})
}
}

13.HTTP中定义请求方式

  • get

  • post

  • put

  • delete

  • trace

  • options

  • head

1.get

get请求只是查询数据,不对数据库进行删改操作;请求会把参数放在url后面;http协议对url长度没有限制,有限制的是浏览器和服务器

2.post

post请求一般是对服务器的数据做改变,比如数据的提交,新增操作,请求参数放在请求体中

3.put

put和post一样都是对服务器数据对修改,但是put侧重于对数据的修改,而post是对数据的新增

4.delete

用来请求删除服务器的资源,但有可能删除不成功(取消delete请求)

5.options

options请求属于浏览器的预检请求,查看服务器是否接受请求,预检通过后,浏览器才会去发get,post,put,delete等,响应报文包含一个 Allow 首部字段,该字段的值表明了服务器支持的所有 HTTP 方法:

1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Allow: OPTIONS, GET, HEAD, POST
Cache-Control: max-age=604800
Date: Thu, 13 Oct 2016 11:45:00 GMT
Expires: Thu, 20 Oct 2016 11:45:00 GMT
Server: EOS (lax004/2813)
x-ec-custom-error: 1
Content-Length: 0

在 CORS 中,可以使用 OPTIONS 方法发起一个预检请求,以检测实际请求是否可以被服务器所接受。预检请求报文中的 Access-Control-Request-Method首部字段告知服务器实际请求所使用的 HTTP方法;Access-Control-Request-Headers首部字段告知服务器实际请求所携带的自定义首部字段。服务器基于从预检请求获得的信息来判断,是否接受接下来的实际请求

1
2
3
4
5
6
7
8
9
10
OPTIONS /resources/post-here/ HTTP/1.1 
Host: bar.other
Accept: text/html,application/xhtml+xml,application/xml;
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type

服务器所返回的 Access-Control-Allow-Methods 首部字段将所有允许的请求方法告知客户端。该首部字段与 Allow 类似,但只能用于涉及到 CORS 的场景中。

    Access-Control-Allow-Methods: POST, GET, OPTIONS 
    Access-Control-Allow-Headers: X-PINGOTHER, Content-Type 

6.head

与GET方法的行为很类似,但服务器在响应中只返回实体的主体部分

7.trace

会在目的服务器端发起一个“回环”诊断。这样客户端就可以查看HTTP请求报文在发送的途中,是否被修改过了

14.get和post区别

  • GET参数通过URL传递,POST放在Request body中。

  • GET请求会被浏览器主动cache,而POST不会,除非手动设置

  • GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留

  • Get 请求中有非 ASCII 字符,会在请求之前进行转码,POST不用,因为POST在Request body中,通过 MIME,也就可以传输非 ASCII 字符

  • 一般我们在浏览器输入一个网址访问网站都是GET请求,HTTP的底层是TCP/IP。HTTP只是个行为准则,而TCP才是GET和POST怎么实现的基本。GET/POST都是TCP链接。GET和POST能做的事情是一样一样的。但是请求的数据量太大对浏览器和服务器都是很大负担。所以业界有了不成文规定,(大多数)浏览器通常都会限制url长度在2K个字节,而(大多数)服务器最多处理64K大小的url。

  • GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)

  • 在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。但并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次

15.http中content-type

MediaType,即是Internet Media Type,互联网媒体类型;也叫做MIME类型,在Http协议消息头中,使用Content-Type来表示具体请求中的媒体类型信息

常见的媒体格式类型如下:

  • text/html : HTML格式
  • text/plain :纯文本格式
  • text/xml : XML格式
  • image/gif :gif图片格式
  • image/jpeg :jpg图片格式
  • image/png:png图片格式

以application开头的媒体格式类型:

  • application/xhtml+xml :XHTML格式
  • application/xml : XML数据格式
  • application/atom+xml :Atom XML聚合格式
  • application/json : JSON数据格式
  • application/pdf :pdf格式
  • application/msword : Word文档格式
  • application/octet-stream : 二进制流数据(如常见的文件下载)
  • application/x-www-form-urlencoded :
    中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)

另外一种常见的媒体格式是上传文件之时使用的:

  • multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式

16.import和require区别

  • 加载方式
    require:运行时加载,所以require理论上可以运用在代码的任何地方
    import:编译时加载,import是编译时调用,所以必须放在文件开头

  • 遵循规范
    require 是 AMD规范引入方式
    import是es6的一个语法标准,如果要兼容浏览器的话必须转化成es5的语法

  • 本质
    require是赋值过程,其实require的结果就是对象、数字、字符串、函数等,再把require的结果赋值给某个变量import是解构过程,但是目前所有的引擎都还没有实现import,我们在node中使用babel支持ES6,也仅仅是将ES6转码为ES5再执行,import语法会被转码为require

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const fs = require('fs')
exports.fs = fs
module.exports = fs

import fs from 'fs'
import {default as fs} from 'fs'
import * as fs from 'fs'
import {readFile} from 'fs'
import {readFile as read} from 'fs'
import fs, {readFile} from 'fs'

export default fs
export const fs
export function readFile
export {readFile, read}
export * from 'fs'

17.ajax

ajax是异步javascript和xml;
Ajax是一种用于创建快速动态网页的技术。

ajax的使用及实现步骤:
1.创建XMLHttpRequest对象,也就是创建一个异步调用对象.

1
2
3
4
5
6
var xmlHttp;
if(window.XMLHttpRequest){ //针对除IE6以外的浏览器
xmlHttp=new XMLHttpRequest(); //实例化一个XMLHttpRequest
}else{
xmlHttp=new ActiveXObject("Microsoft.XMLHTTP"); //针对IE5,IE6
}

2.创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息

1
2
xmlhttp.open(method,url,async);
xmlhttp.send();

3.设置响应HTTP请求状态变化的函数

1
2
3
4
5
xmlHttp.onreadystatechange()=>{
if(xmlHttp.readyState === 4 && xmlHttp.status === 200){
document.getElementById("myDiv").innerHTML=xmlhttp.responseText;
}
}

//服务器响应
responseText:获得字符串形式的响应数据。
responseXML:获得 XML 形式的响应数据。
readyState:存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。

0: 请求未初始化
1: 服务器连接已建立
2: 请求已接收
3: 请求处理中
4: 请求已完成,且响应已就绪

status:

200: "OK"
404: 未找到页面

当 readyState 等于 4 且状态为 200 时,表示响应已就绪:
ajax步骤:

创建XMLHttpRequest对象。
设置请求方式。
调用回调函数。
发送请求。

18.闭包

函数A 里面包含了 函数B,而 函数B 里面使用了 函数A 的变量,那么 函数B 被称为闭包。

又或者:闭包就是能够读取其他函数内部变量的函数

1
2
3
4
5
6
7
function A() {
var a = 1;
function B() {
console.log(a);
}
return B();
}

特征:

  • 函数内再嵌套函数
  • 内部函数可以引用外层的参数和变量
  • 参数和变量不会被垃圾回收制回收

闭包的理解:使用闭包主要是为了设计私有的方法和变量。闭包的优点是可以避免全局变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。

在js中,函数即闭包,只有函数才会产生作用域的概念

闭包 的最大用处有两个,一个是可以读取函数内部的变量,另一个就是让这些变量始终保持在内存中

  • 好处:能够实现封装和缓存等
  • 坏处:消耗内存,使用不当造成内存泄漏;在退出函数之前,将不使用的局部变量全部删除
1
2
3
4
5
for(var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}

答案:3个3
解析:首先,for 循环是同步代码,先执行三遍 for,i变成了 3;然后,再执行异步代码 setTimeout,这时候输出的 i,只能是 3 个 3 了
解决方法:
1.使用let i=0;每个 let 和代码块结合起来形成块级作用域,当 setTimeout() 打印时,会寻找最近的块级作用域中的 i,所以依次打印出 0 1 2
2.立即执行函数:

1
2
3
4
5
6
7
for(let i = 0; i < 3; i++) {
(function(i){
setTimeout(function() {
console.log(i);
}, 1000);
})(i)
}

19.js作用域和作用域链

1.作用域

在JavaScript中,作用域分为 全局作用域 和 函数作用域

  • 全局作用域:
    代码在程序的任何地方都能被访问,window 对象的内置属性都拥有全局作用域
  • 函数作用域:
    在固定的代码片段才能被访问

作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。

变量取值:到创建 这个变量 的函数的作用域中取值

2.作用域链

一般情况下,变量取值到 创建 这个变量 的函数的作用域中取值。

但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链

20.组件化和模块化

1.组件化

  • 为什么要组件化?

有时候页面代码量太大,逻辑太多或者同一个功能组件在许多页面均有使用,维护起来相当复杂,这个时候,就需要组件化开发来进行功能拆分、组件封装,已达到组件通用性,增强代码可读性,维护成本也能大大降低

  • 组件化开发的优点

很大程度上降低系统各个功能的耦合性,并且提高了功能内部的聚合性,降低了开发成本

  • 组件化开发的准则:

    专一
    可配置性
    标准
    复用性
    可维护性

2.模块化

  • 为什么要模块化?

早期的javascript版本没有块级作用域、没有类、没有包、也没有模块,这样会带来一些问题,如复用、依赖、冲突、代码组织混乱等,随着前端的膨胀,模块化显得非常迫切

  • 模块化的好处

    提高代码可复用性
    避免变量污染,命名冲突
    提高可维护性
    方便依赖关系管理

  • 模块化的几种方法:

1)函数封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
var myModule = {
var1: 1,

var2: 2,

fn1: function(){

},

fn2: function(){

}
}

总结:这样避免了变量污染,只要保证模块名唯一即可,同时同一模块内的成员也有了关系

缺陷:外部可以睡意修改内部成员,这样就会产生意外的安全问题

2)立即执行函数表达式(IIFE):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var myModule = (function(){
var var1 = 1;
var var2 = 2;

function fn1(){

}

function fn2(){

}

return {
fn1: fn1,
fn2: fn2
};
})();

总结:这样在模块外部无法修改我们没有暴露出来的变量、函数

缺点:功能相对较弱,封装过程增加了工作量,仍会导致命名空间污染可能、闭包是有成本的

21.图片的预加载和懒加载

  • 预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染
  • 懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数
    两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。预加载则会增加服务器前端压力,懒加载对服务器有一定的缓解压力作用。

22.mouseover和mouseenter的区别

  • mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是mouseout
  • mouseenter:当鼠标移入元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是mouseleave

解决异步回调地狱:promise、generator、async/await

23.对This对象的理解

this总是指向函数的直接调用者(而非间接调用者)

如果有new关键字,this指向new出来的那个对象

在事件中,this指向触发这个事件的对象,特殊的是,IE中的attachEvent中的this总是指向全局对象Window

this表示当前对象,this的指向是根据调用的上下文来决定的,默认指向window对象,指向window对象时可以省略不写

全局环境: this始终指向的是window对象
局部环境: 在全局作用域下直接调用函数,this指向window 对象函数调用,哪个对象调用就指向哪个对象 使用new实例化对象,在构造函数中的this指向实例化对象 使用call或apply改变this的指向
总结:this始终指向最后一个调用它的函数的对象

24.ES6其他常用功能

  1. let/const
  2. 多行字符串/模板变量
  3. 解构赋值
  4. 块级作用域
  5. 函数默认参数
  6. 箭头函数

25.bind、call、apply用法及区别

相同点: 三个函数的作用就是改变this的指向,将函数绑定到上下文中; 不同点: 三个函数的语法不同

1
2
3
4
fun.call(thisArg[, arg1[, arg2[, ...]]])
fun.apply(thisArg, [argsArray])
var bindFn = fun.bind(thisArg[, arg1[, arg2[, ...]]])
bindFn()

26.目前JS解决异步的方案有哪些

回调函数
事件监听
发布-订阅
Promise
Generator
Async/Await

27.创建对象有几种方法

1
2
3
4
5
6
7
8
9
// 第一种:字面量
var o1 = {name: "o1"}
var o2 = new Object({name: "o2"})
// 第二种:通过构造函数
var M = function(name){this.name = name}
var o3 = new M("o3")
// 第三种:Object.create()
var p = {name: "p"}
var o4 = Object.create(p)

四.Vue

vue的生命周期:就是vue实例从创建到销毁的过程,也就是从开始创建,初始化数据,编译模版,挂载Dom,渲染更新,卸载等过程

1.Vue生命周期的作用是什么?

它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑

2.Vue生命周期总共有几个阶段?

它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后

3.DOM渲染在哪个周期中就已经完成?

DOM 渲染在 mounted 中就已经完成了

4.每个生命周期适合哪些场景?

生命周期钩子的一些使用方法:
beforecreate : 可以在这加个loading事件,在加载实例时触发
created : 初始化完成时的事件写在这里,如在这结束loading事件,异步请求也适宜在这里调用
mounted : 挂载元素,获取到DOM节点
updated : 如果对数据统一处理,在这里写上相应函数
beforeDestroy : 销毁实例前做收尾清除工作
nextTick : 更新数据后立即操作dom

5.关于vue的keep-alive需要条件性缓存的解决

A>B不缓存,C>B需要缓存
在路由里面加上了

1
2
3
4
5
6
7
8
{
path: '/b',
name: 'B',
component: B,
meta:{
keepAlive:true
}
}

在app.vue写了

1
2
3
4
<keep-alive>
<router-view v-if="$route.meta.keepAlive"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"></router-view>

A页面写了

1
2
3
4
beforeRouteLeave(to, from, next) {
to.meta.keepAlive = false;
next();
}

C页面写了

1
2
3
4
beforeRouteLeave(to, from, next) {
to.meta.keepAlive = true;
next();
}

meta.keepAlive=true这种方法,解决不了条件缓存问题
原因:在keep-alive源码中,include和exclude是被watch的 ,当发生变化时,keep-alive会去校验cache里是否匹配,匹配不上的会被删除。也就是说,官方是为这种情况做了处理的。而meta这种方法,因为没有存在某种类似于watch的方法,导致这种方法天然是和实际cache里面的内容有出入的,所以可定会存在各种奇怪的bug

利用include,动态添加”B”
1、在app.vue下增加keep-alive

1
2
3
<keep-alive :include="catchList">
<router-view></router-view>
</keep-alive>

catchList,是vuex维护的需要缓存的组件名的一个数组

2、在路由中加入

1
2
3
4
5
6
router.beforeEach((to,next,from)=>{
if (to.name === 'B') {
store.commit('keepAlive', 'B')
}
next()
})

3、在b.vue中加入(A>B不缓存,C>B缓存)

1
2
3
4
5
6
beforeRouteLeave((to,next,from)=>{
if (to.name !== 'C') {
store.commit('noKeepAlive')
}
next()
})

在vuex中mutation是

1
2
3
4
5
6
7
keepAlive(state, component) {
!state.catchList.includes(component) &&
state.catchList.push(component)
},
noKeepAlive(state) {
state.catchList = []
}

只要是B的组件,都缓存。只有当从A>B的时候,才让B不缓存。

6.Vue 路由懒加载

Vue项目中实现路由按需加载(路由懒加载)的3中方式:
一、Vue异步组件技术:

1
2
3
4
5
{
path: '/home',
name: 'Home',
component: resolve => reqire(['path路径'], resolve)
}

二、es6提案的import()

1
const Home = () => import('path路径')

三、webpack提供的require.ensure()

1
2
3
4
5
{
path: '/home',
name: 'Home',
component: r => require.ensure([],() => r(require('path路径')), 'demo')
}

7.Proxy与Object.defineProperty()的对比

Proxy的优点:
1. 可以直接监听对象而非属性,并返回一个新对象
2. 可以直接监听数组的变化
3. 可以劫持整个对象,并返回一个新对象

Proxy的缺点:
Proxy是es6提供的新特性,兼容性不好,所以导致Vue3一致没有正式发布让让广大开发者使用,IE9以下不兼容

Object.defineProperty的优点:
IE8以下的版本不兼容

Object.defineProperty的缺点:
只能劫持对象的属性,我们需要对每个对象的每个属性进行遍历,无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应

Proxy代理整个对象,Object.defineProperty只代理对象上的某个属性。
如果对象内部要全部递归代理,则Proxy可以只在调用时递归,而Object.defineProperty需要在一开始就全部递归,Proxy性能优于Object.defineProperty。
对象上定义新属性时,Proxy可以监听到,Object.defineProperty监听不到。
数组新增删除修改时,Proxy可以监听到,Object.defineProperty监听不到。
Proxy不兼容IE9以下,Object.defineProperty不兼容IE8及以下。

8.v-show与v-if区别

v-show是css切换,v-if是完整的销毁和重新创建

使用 频繁切换时用v-show,运行时较少改变时用v-if

v-if=’false’ v-if是条件渲染,当false的时候不会渲染

9.vue有哪些指令

  • v-model //在表单控件或者组件上创建双向绑定

  • v-if //根据表达式的值的 truthiness 来有条件地渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建

  • v-else-if

  • v-else

  • v-text //更新元素的 textContent

  • v-show //根据表达式之真假值,切换元素的 display CSS property。

  • v-html://更新元素的 innerHTML。注意:内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译。如果试图使用 v-html 组合模板,可以重新考虑是否通过使用组件来替代。

  • v-on:绑定事件监听器

  • v-bind //动态地绑定一个或多个 attribute,或一个组件 prop 到表达式。

  • v-for //基于源数据多次渲染元素或模板块

  • v-cloak //这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕

  • v-once //只渲染元素和组件一次

  • v-pre //跳过这个元素和它的子元素的编译过程。可以用来显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译。

  • v-slot //提供具名插槽或需要接收 prop 的插槽

绑定class的数组用法

对象方法: v-bind:class="{'orange': isRipe, 'green': isNotRipe}"
数组方法:  v-bind:class="[class1, class2]"
行内: v-bind:style="{color: color, fontSize: fontSize+'px' }"

10.组件之间的传值通信

  • 父组件给子组件传值:props

  • 子组件向父组件通信:父组件向子组件传递事件方法,子组件通过$emit触发事件,回调给父组件

  • 非父子,兄弟组件之间通信:
    可以通过实例一个vue实例Bus作为媒介,要相互通信的兄弟组件之中,都引入Bus,然后通过分别调用Bus事件触发和监听来实现通信和参数传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<template>
<button @click="toBus">子组件传给兄弟组件</button>
</template>

<script>
import Bus from '../common/js/bus.js'
export default{
methods: {
toBus () {
Bus.$emit('on', '来自兄弟组件')
}
}
}
</script>

另一个组件也import Bus.js 在钩子函数中监听on事件

1
2
3
4
5
6
7
8
9
10
11
12
13
import Bus from '../common/js/bus.js'
export default {
data() {
return {
message: ''
}
},
mounted() {
Bus.$on('on', (msg) => {
this.message = msg
})
}
}

11.子组件调用父组件的方法函数

  • 直接在子组件中通过this.$parent.event来调用父组件的方法
  • 在子组件里用$emit向父组件触发一个事件,父组件监听这个事件就行了
  • 父组件把方法传入子组件中,在子组件里直接调用这个方法

12.路由跳转方式

<router-link to='home'> router-link标签会渲染为<a>标签

另一种是编程是导航 也就是通过js跳转 比如 router.push(‘/home’)

13.mvvm

  • M - Model,Model 代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑

  • V - View,View 代表 UI 组件,它负责将数据模型转化为 UI 展现出来

  • VM - ViewModel,ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步 View 和 Model 的对象,连接 Model 和 View之间的桥梁,绑定数据到viewmodel层并自动更新渲染到页面上,视图变化通知到viewmodel层去更新数据

14.computed和watch有什么区别?

  • computed:

    1. computed是计算属性,也就是计算值,它更多用于计算值的场景
    2. computed具有缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时才会重新调用对应的getter来计算
    3. computed适用于计算比较消耗性能的计算场景
  • watch:

    1. 更多的是「观察」的作用,类似于某些数据的监听回调,用于观察props $emit或者本组件的值,当数据变化时来执行回调进行后续操作
    2. 无缓存性,页面重新渲染时值不变化也会执行
  • 小结:

    1. 当我们要进行数值计算,而且依赖于其他数据,那么把这个数据设计为computed
    2. 如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化

15.key

key是为Vue中的vnode标记的唯一id,通过这个key,我们的diff操作可以 更准确、更快速

  • 准确:
    如果不加key,那么vue会选择复用节点(Vue的就地更新策略),
    导致之前节点的状态被保留下来,会产生一系列的bug

  • 快速:
    key的唯一性可以被Map数据结构充分利用

预期:number | string

key 的特殊属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key的变化重新排列元素顺序,并且会移除 key 不存在的元素。

有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。

最常见的用例是结合 v-for:

1
2
3
<ul>
<li v-for="item in items" :key="item.id">...</li>
</ul>

它也可以用于强制替换元素/组件而不是重复使用它。当你遇到如下场景时它可能会很有用:

  • 完整地触发组件的生命周期钩子
  • 触发过渡
1
2
3
<transition>
<span :key="text">{{ text }}</span>
</transition>

当 text 发生改变时,<span> 总是会被替换而不是被修改,因此会触发过渡

16.组件中的data为什么是函数?

组件可以被多次复用,创建多个实例,这些实例本质是同一个构造函数,如果data是对象,对象是引用类型,修改对象会影响所有实例,因此data是一个函数

17.Class 与 Style 如何动态绑定?

1)class
对象语法:

1
2
3
4
5
6
7
8
<div v-bind:class="{ 'active': isActive, 'text-danger': hasError }"></div>

data() {
return {
isActive: true,
hasError: false
}
}

数组语法:

1
2
3
4
5
6
7
8
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>

data() {
return {
activeClass: 'active',
errorClass: 'text-danger'
}
}

2)style:

对象语法:

1
2
3
4
5
6
7
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data() {
return {
activeColor: 'red',
fontSize: 30
}
}

数组语法:

1
2
3
4
5
6
7
8
9
10
11
<div v-bind:style="[styleColor, styleSize]"></div>
data() {
return {
styleColor: {
color: 'red'
},
styleSize:{
fontSize:'23px'
}
}
}

18.vue的单向数据流

所有的 prop都使得其父子prop之间形成了一个单向下行绑定:
父级 prop的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解

额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。
这意味着你不应该在一个子组件内部改变prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。
子组件想修改时,只能通过 $emit派发一个自定义事件,父组件接收到后,由父组件修改

有两种常见的试图改变一个 prop 的情形 :

1)这个 prop 用来传递一个初始值:
这个子组件接下来希望将其作为一个本地的 prop 数据来使用,在这种情况下,最好定义一个本地的 data属性并将这个 prop 用作其初始值:

1
2
3
4
5
6
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}

2)prop 以一种原始的值传入且需要进行转换:

1
2
3
4
5
6
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}

19.keep-alive

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:

  • 一般结合路由和动态组件一起使用,用于缓存组件;

  • 提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include表示只有名称匹配的组件会被缓存,exclude表示任何名称匹配的组件都不会被缓存 ,其中 exclude的优先级比 include 高;

  • 对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。

20.v-model 的原理

vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model在内部为不同的输入元素使用不同的属性并抛出不同的事件:

  • text 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked 属性和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件
1
2
3
<input v-model='something'>
<!-- 相当于 -->
<input v-bind:value="something" v-on:input="something = $event.target.value">

如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件

21.nextTick()

在下次DOM更新循环结束之后执行延迟回调。在修改数据之后,立即使用的这个回调函数,获取更新后的DOM

22.vue插槽

  • 单个插槽:
    当子组件模板只有一个没有属性的插槽时,
    父组件传入的整个内容片段将插入到插槽所在的 DOM 位置,
    并替换掉插槽标签本身

  • 命名插槽:
    solt元素可以用一个特殊的特性name来进一步配置如何分发内容。
    多个插槽可以有不同的名字。 这样可以将父组件模板中 slot 位置,
    和子组件 slot 元素产生关联,便于插槽内容对应传递

  • 作用域插槽:
    可以访问组件内部数据的可复用插槽(reusable slot)
    在父级中,具有特殊特性 slot-scope 的<template> 元素必须存在,
    表示它是作用域插槽的模板。slot-scope 的值将被用作一个临时变量名,
    此变量接收从子组件传递过来的 prop 对象

23.导航守卫

vue-router提供的导航守卫主要通过跳转或取消的方式守卫导航
参数或查询的改变不会触发进入/离开的导航守卫
你可以通过观察 $route 对象来应对这些变化,或使用beforeRouteUpdate 的组件内守卫

  • 全局前置守卫:
1
2
3
4
5
const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
// ...
})

守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中

to: Route: 即将要进入的目标 路由对象

from: Route: 当前导航正要离开的路由

next: Function: 一定要调用该方法来 resolve这个钩子。执行效果依赖 next 方法的调用参数。
确保要调用 next 方法,否则钩子就不会被 resolved

  • 路由独享的守卫
1
2
3
4
5
6
7
8
9
10
11
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
  • 组件内的守卫
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}

vue-router有哪几种导航钩子

第一种:是全局导航钩子:router.beforeEach(to,from,
next),作用:跳转前进行判断拦截

第二种:组件内的钩子:beforeRouteEnter/beforeRouteUpdate/beforeRouteLeave

第三种:单独路由独享组件:beforeEnter

24.vuex是什么?

vuex是专门为vuejs应用程序开发的状态管理插件,集中存储管理组件状态,通过显式提交mutation改变组件状态
vuex 就是一个仓库,仓库里放了很多对象。其中 state就是数据源存放地,对应于一般 vue 对象里面的 data

state 里面存放的数据是响应式的,vue 组件从 store 读取数据,若是 store 中的数据发生改变,依赖这相数据的组件也会发生更新

它通过 mapState 把全局的 state 和 getters 映射到当前组件的 computed 计算属性

Vuex有5种属性: 分别是 state、getter、mutation、action、module;

  • state
    Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据

  • mutations
    mutations定义的方法动态修改Vuex 的 store 中的状态或数据

  • getters
    类似vue的计算属性,主要用来过滤一些数据

  • actions
    actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action

  • modules
    由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得复杂时,store对象就变得臃肿。为了解决以上问题,vuex允许将store分隔成模块。每个模块有自己的state/getter/mutation/action,甚至是嵌套子模块。
    总结
    vuex 一般用于中大型 web 单页应用中对应用的状态进行管理,对于一些组件间关系较为简单的小型应用,使用 vuex 的必要性不是很大,因为完全可以用组件 prop属性或者事件来完成父子组件之间的通信,vuex 更多地用于解决跨组件通信以及作为数据中心集中式存储数据

vuex解决了什么?
多个组件依赖同一个状态,多层组件间传值
来自不同的组件的行为需要变更同一个状态

25.优化SPA首屏加载速度

  • 缩小webpack或者其他打包工具生成的包的大小

    用webpack-bundle-analyzer的分析工具哪个模块占空间大

  • 第三方UI组件按需引入

  • 使用服务端渲染方式(基于vue的nuxt.js开发)

  • 使用预渲染的方式

    在打包时会预先运行一次js代码,将一部分静态页面直接渲染成html写在生成的index.html中,在加载完index.html后页面就能展示,无需等待加载js缺点是在需要预渲染的页面较多时,build打包的时间会十分漫长

  • 使用gzip减小网络传输的流量大小

    HTTP协议上的gzip编码是一种用来改进web应用程序性能的技术,web服务器和客户端(浏览器)必须共同支持gzip,使用gzip可以将原静态文件压缩到30%,效果很明显,对于优化首屏加载时间非常适合在nginx中配置
    http{
    gzip on;
    }

  • 组件懒加载

26.你有对 Vue 项目进行哪些优化?

1)代码层面的优化

  • v-if 和 v-show 区分使用场景
  • computed 和 watch 区分使用场景
  • v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
  • 长列表性能优化
  • 事件的销毁
  • 图片资源懒加载
  • 路由懒加载
  • 第三方插件的按需引入
  • 优化无限列表性能
  • 服务端渲染 SSR or 预渲染
  • 减少data中数据
  • SPA采用keep-alive缓存组件

2)Webpack 层面的优化

  • Webpack 对图片进行压缩
  • 减少 ES6 转为 ES5 的冗余代码
  • 提取公共代码
  • 模板预编译
  • 提取组件的 CSS
  • 优化 SourceMap
  • 构建结果输出分析
  • Vue 项目的编译优化
  • 压缩代码
  • tree shaking
  • cdn加载第三方模块
  • sourcemap优化

3)基础的 Web 技术的优化

  • 开启 gzip 压缩
  • 浏览器缓存
  • CDN 的使用
  • 使用 Chrome Performance 查找性能瓶颈

五.ES6

1.var let const区别

let、const声明的变量仅在块级作用域内有效,var声明变
量是全局的,没有块级作用域功能
let 、const 不存在变量提升 , var 存在变量提升
let 、const不能在同一块级作用域内重复申请

1
2
3
4
5
6
7
8
9
10
11
12
13
var a = 10;
function test(){
console.log(a);
var a = 20;
}
test();//undefined

var a = 10;
function test(){
console.log(a);
let a = 20;
}
test();//ReferenceError: Cannot access 'name' before initialization

通过 var 声明的变量有初始值 undefined,而通过 let声明的变量直到定义的代码被执行时才会初始化。在变量初始化前访问变量会导致 ReferenceError

2.解构赋值

1)数组解构

1
2
3
4
5
let [a, b, c] = [1, 2, 3]   //a=1, b=2, c=3
let [d, [e], f] = [1, [2], 3] //嵌套数组解构 d=1, e=2, f=3
let [g, ...h] = [1, 2, 3] //数组拆分 g=1, h=[2, 3]
let [i,,j] = [1, 2, 3] //不连续解构 i=1, j=3
let [k,l] = [1, 2, 3] //不完全解构 k=1, l=2

2)对象解构:

1
2
3
4
5
6
let {a, b} = {a: 'aaaa', b: 'bbbb'}      //a='aaaa' b='bbbb'
let obj = {d: 'aaaa', e: {f: 'bbbb'}}
let {d, e:{f}} = obj //嵌套解构 d='aaaa' f='bbbb'
let g;
(g = {g: 'aaaa'}) //以声明变量解构 g='aaaa'
let [h, i, j, k] = 'nice' //字符串解构 h='n' i='i' j='c' k='e'

函数参数的定义

1
2
3
4
function personInfo(name, age, address, gender) {
console.log(name, age, address, gender)
}
personInfo('william', 18, 'changsha', 'man')

上面这个例子在对用户信息的时候需要传递四个参数,且需要一一对应,这样就会极易出现参数顺序传错的情况,从而导致bug,接下来来看es6解构赋值是怎么解决这个问题的:

1
2
3
4
function personInfo({name, age, address, gender}) {
console.log(name, age, address, gender)
}
personInfo({gender: 'man', address: 'changsha', name: 'william', age: 18})

交换变量的值

1
2
3
let a=1, b=2;
[b, a] = [a, b]
console.log(a, b)

函数默认参数

es5:

1
2
3
4
5
6
7
8
function saveInfo(name, age, address, gender) {
name = name || 'william'
age = age || 18
address = address || 'changsha'
gender = gender || 'man'
console.log(name, age, address, gender)
}
saveInfo()

es6:

1
2
3
4
function saveInfo({name= 'william', age= 18, address= 'changsha', gender= 'man'} = {}) {
console.log(name, age, address, gender)
}
saveInfo()

3.forEach、for in、for of三者区别

  • forEach更多的用来遍历数组,无法return或break

  • for in 一般常用来遍历对象或json,循环遍历的值都是数据结构的键值,也遍历数组

  • for of数组对象都可以遍历,遍历对象需要通过和Object.keys()一起使用
    它是ES6中新增加的语法,用来循环获取一对键值对中的值
    一个数据结构只有部署了 Symbol.iterator 属性, 才具有 iterator接口可以使用 for of循环
    以下数据结构部署了 Symbol.iteratoer属性:

    • 数组
    • Map
    • Set
    • String
    • Nodelist
    • arguments对象
      如果想让对象可以使用 for of循环怎么办?使用 Object.keys() 获取对象的 key值集合后,再使用 for of
      或者使用内置的Object.values()方法获取对象的value值集合再使用for of
  • for in循环出的是key,for of循环出的是value

4.使用箭头函数应注意什么?

  • 1、用了箭头函数,this就不是指向window,而是父级(指向是可变的)
  • 2、不能够使用arguments对象
  • 3、不能用作构造函数,这就是说不能够使用new命令,否则会抛出一个错误
  • 4、不可以使用yield命令,因此箭头函数不能用作 Generator 函数

5.Set、Map的区别

应用场景Set用于数据重组,Map用于数据储存

  • Set:
    1,成员不能重复
    2,只有键值没有键名,类似数组
    3,可以遍历,方法有add, delete,has
  • Map:
    1,本质上是健值对的集合,类似集合
    2,可以遍历,可以跟各种数据格式转换

6.Ajax

1.创建一个XmlHttpRequest对象,也就是创建一个异步调用对象
2.创建一个发送请求到方法,设置http请求方法,url和验证信息
3.设置请求状态变化到方法
4.发送请求
5.获取异步调用返回的数据
6.使用js和dom实现局部刷新

7.同步和异步的区别

  • 同步:
    按照一定的顺序去执行,执行完一个才能执行下一个浏览器访问服务器请求,用户看得到页面刷新,重新发请求,等请求完,页面刷新,新内容出现,用户看到新内容,进行下一步操作
  • 异步:
    浏览器访问服务器请求,用户正常操作,浏览器后端进行请求。等请求完,页面不刷新,新内容也会出现,用户看到新内容执行顺序是不确定的,由触发条件决定,什么时间执行也是不确定的,即使是定时器(下面做解释),异步处理可以同时执行多个。

8.ajax的优点和缺点

  • ajax的优点
    1、无刷新更新数据(在不刷新整个页面的情况下维持与服务器通信)
    2、异步与服务器通信(使用异步的方式与服务器通信,不打断用户的操作)
    3、前端和后端负载均衡(将一些后端的工作交给前端,减少服务器与宽度的负担)
    4、界面和应用相分离(ajax将界面和应用分离也就是数据与呈现相分离)

  • ajax的缺点
    1、ajax不支持浏览器back按钮
    2、安全问题 Aajax暴露了与服务器交互的细节
    3、对搜索引擎的支持比较弱
    4、破坏了Back与History后退按钮的正常行为等浏览器机制

9.get和post的区别

1、get和post在HTTP中都代表着请求数据,其中get请求相对来说更简单、快速,效率高些
2、get相对post安全性低
3、get有缓存,post没有
4、get体积小,post可以无限大
5、get的url参数可见,post不可见
6、get只接受ASCII字符的参数数据类型,post没有限制
7、get请求参数会保留历史记录,post中参数不会保留
8、get会被浏览器主动catch,post不会,需要手动设置
9、get在浏览器回退时无害,post会再次提交请求

10.什么时候使用post?

post一般用于修改服务器上的资源,对所发送的信息没有限制。比如
1、无法使用缓存文件(更新服务器上的文件或数据库)
2、向服务器发送大量数据(POST 没有数据量限制)
3、发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠

11.同源策略

同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能相互读取对方资源

同源策略限制了一个源的文档和脚本和另一个源的资源进行交互,是一个隔离潜在恶意文件攻击的安全机制

不受同源策略限制的:

1.页面中的连接,重定向和表单提交
2.第三方js的引入不受限制,但不能js读写加载的内容,script,link,img,iframe

12.如何解决跨域问题?

跨域的概念:协议、域名、端口都相同才同域,否则都是跨域

解决跨域问题:

1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs中间件代理跨域
9、 WebSocket协议跨域

六.浏览器

1.主流浏览器

IE Google Chrome Firefox Opera Safari

2.浏览器内核

渲染引擎和js引擎
渲染引擎:用来解释网页语法并渲染到网页上

浏览器内核决定了如何显示网页内容和格式化的信息
Trident:IE、360
Gecko:火狐
Presto:Opera
Blink:Opera,Googlechrome
webkit:Safari

3.浏览器兼容

1.不同浏览器默认内外边距不同:*{margin:0;padding:0}
2.图片默认有间距:img设置float
3. IE6双边距bug:块属性标签float后,又有横行的margin情况下,在IE6显示margin比设置的大。hack:display:inline;将其转化为行内属性。
4. 设置较小高度标签(一般小于10px),在IE6,IE7中高度超出自己设置高度。hack:给超出高度的标签设置overflow:hidden;或者设置行高line-height 小于你设置的高度。
5. Chrome 中文界面下默认会将小于 12px 的文本强制按照 12px 显示,可通过加入 CSS 属性 -webkit-text-size-adjust: none; 解决。
6. 超链接访问过后hover样式就不出现了,被点击访问过的超链接样式不再具有hover和active了。解决方法是改变CSS属性的排列顺序:L-V-H-A ( love hate ): a:link {} a:visited {} a:hover {} a:active {}

七.其他

1.前端组件化和模块化

组件化:组件化是具体的,按照一些功能的通用性和复用性来抽象组件侧重于UI部分,比如弹窗按钮

模块化:模块化是抽象的,按照项目业务划分的大块侧重于数据数据的封装

对于组件来说,其主要是提高代码的复用性,功能单一独立模块是将同一类型的代码整合在一起,例如用户信息,设置等,所以模块等功能相当复杂,但都同属于同一业务(提高内聚降低耦合)

2.什么是Ajax和JSON,它们的优点和缺点

  • Ajax:

    Ajax是异步JavaScript和XML,用于在Web页面中实现异步数据交互
    Ajax优点:
    异步请求响应快,用户体验好;页面无刷新、数据局部更新;按需取数据,减少了冗余请求和服务器的负担;
    Ajax缺点:
    异步回调问题、this指向问题、路由跳转back问题;对搜索引擎的支持比较弱,对于一些手机还不是很好的支持

  • JSON:

    是一种轻量级的数据交换格式,看着像对象,本质是字符串
    JSON优点:
    轻量级、易于人的阅读和编写,便于js解析,支持复合数据类型
    JSON缺点:
    没有XML格式这么推广的深入人心和使用广泛, 没有XML那么通用性。

3.Github

git常用的命令
从远程库克隆到本地:git clone 网站上的仓库地址
新增文件的命令:git add .
提交文件的命令:git commit –m或者git commit –a
查看工作区状况:git status –s
拉取合并远程分支的操作:git fetch/git merge或者git pull
查看提交记录命令:git reflog

4.webpack

webpack打包原理:
webpack只是一个打包模块的机制,只是把依赖的模块转化成可以代表这些包的静态文件。webpack就是识别你的入口文件。识别你的模块依赖,来打包你的代码。至于你的代码使用的是commonjs还是amd或者es6的import。webpack都会对其进行分析。来获取代码的依赖。webpack做的就是分析代码,转换代码,编译代码,输出代码。webpack本身是一个node的模块,所以webpack.config.js是以commonjs形式书写的(node中的模块化是commonjs规范的)

webpack 核心概念
1.entry
入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始. 进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。 每个依赖项随即被处理,最后输出到称之为 bundles 的文件中。

2.output
output 属性告诉 webpack 在哪里输出它所创建的bundles,以及如何命名这些文件,默认值为 ./dist。 基本上整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。

3.Module 模块
在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块

4.chunk 代码块
一个 Chunk 由多个模块组合而成,用于代码合并与分割。

5.loader
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。 loader 可以将所有类型的文件转换为 webpack能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。 本质上,webpack loader将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。

6.Plugin
loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。 插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

模块热更新
模块热更新是webpack的一个功能,他可以使代码修改过后不用刷新就可以更新,是高级版的自动刷新浏览器

devServer中通过hot属性可以控制模块的热替换

1
2
3
4
5
6
7
8
9
10
11
12
13
const webpack = require('webpack');
const path = require('path');
let env = process.env.NODE_ENV == "development" ? "development" : "production";
const config = {
mode: env,
devServer: {
hot:true
}
}
plugins: [
new webpack.HotModuleReplacementPlugin(), //热加载插件
],
module.exports = config;

webpack的优点

专注于处理模块化的项目,能做到开箱即用,一步到位
可通过plugin扩展,完整好用又不失灵活
使用场景不局限于web开发
社区庞大活跃,经常引入紧跟时代发展的新特性,能为大多数场景找到已有的开源扩展
良好的开发体验

webpack的缺点

webpack的缺点是只能用于采用模块化开发的项目

5.微信小程序

  • onLoad():页面加载时触发。
  • onReady():页面初次渲染完成时触发。
  • onShow():页面显示/切入前台时触发。
  • onHide():页面隐藏/切入后台时触发。
  • onUnload():页面卸载时触发。

小程序运行环境分为渲染层和逻辑层,其中wxml和wxss工作在渲染层,js工作在逻辑层

小程序的渲染层和逻辑层分别由两个线程来管理:渲染层的界面使用webview来管理,逻辑层使用jscore来运行js脚本,一个小程序存在多个界面,所以渲染层有多个webview,两个线程通过微信客户端做中转,逻辑层请求网络经由客户端转发

6.微信小程序支付流程

  • 1.wx.login用code换取openid
  • 2.生成商户订单
  • 3.调用支付统一下单api,返回预付单信息prepay_id
  • 4.将组合数据再次签名,返回5个参数和sign
  • 5.小程序获取参数后,鉴权调起支付
  • 6.返回支付结果给小程序,推送支付结果给商户,修改订单状态

image

7.微信网页授权流程

前置条件:
公众平台设置授权回调域名,在域名内页面可进行OAuth2.0鉴权

关于网页授权的两种scope的区别说明
1.以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权跳转到回调页面的
2.snsapi_userinfo为scope发起的网页授权,是用来获取用户基本信息的,但是需要用户手动同意,由于用户同意过所以无需关注就可获取用户基本信息
3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。

网页授权流程分为四步:
1.引导用户进入授权页面,同意授权,获取code
2.通过code换取网页授权access_token
3.如果需要,刷新access_token
4.通过access_token和opened获取用户基本信息

8.小程序登录流程

1.wx.login()获取code,传给开发者服务器
2.开发者服务器用appid,appsecret,code调用登录凭证校验
( auth.code2Session )向微信服务器获取openid和sessionkey
3.发者服务器可以根据用户标识来生成自定义登录态,用于后
续业务逻辑中前后端交互时识别用户身份。

9.小程序授权

如果用户未接受或拒绝过此权限,会弹窗询问用户,用户点击同意后方可调用接口;

如果用户已授权,可以直接调用接口;

如果用户已拒绝授权,则不会出现弹窗,而是直接进入接口fail 回调。请开发者兼容用户拒绝授权的场景。(wx.openSetting引导用户进行授权)

开发者可以使用 wx.getSetting 获取用户当前的授权状态。

微信小程序分为两个部分:webview 和 appService。其中 webview 主要用来展示 UI,appService 用来处理业务逻辑、数据及接口调用。它们在两个进程中进行,通过系统层 JSBridge 实现通信,实现 UI 的渲染、事件的处理

10.网络协议

网络分层
目前网络分层可分为两种:OSI 模型和 TCP/IP 模型
OSI模型

应用层(Application)
表示层(Presentation)
会话层(Session)
传输层(Transport)
网络层(Network)
数据链路层(Data Link)
物理层(Physical)

TCP/IP模型

应用层(Application)
传输层(Host-to-Host Transport)
互联网层(Internet)
网络接口层(Network Interface)

11.HTTP/HTTPS

1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议
3、http和https使用的是完全不同的连接方式,用的端口也不一样,默认前者是80,后者是443
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

12.从输入URL到页面加载到过程?

1.浏览器地址栏输入URL并回车
2.通过DNS将域名解析成IP地址。在解析过程中,按照浏览器缓存、系统缓存、路由器缓存、ISP(运营商)DNS缓存、根域名服务器、顶级域名服务器、主域名服务器的顺序,逐步读取缓存,直到拿到IP地址。(应用层)
3.根据获取IP进行tcp连接(三次握手)(传输层)
4.发送http请求
5.服务器处理请求,浏览器接收http的响应
6.渲染页面,构造dom树
7.关闭tcp连接(四次挥手)

13.HTTP状态码

区分状态码
1××开头 - 临时响应
2××开头 - 请求成功
3××开头 - 请求被重定向
4××开头 - 请求错误
5××开头 - 服务器错误
常见状态码
200 - 请求成功,Ajax 接受到信息了
400 - 服务器不理解请求
403 - 服务器拒绝请求
404 - 请求页面错误
500 - 服务器内部错误,无法完成请求

14.性能优化

  • HTML优化
    1、避免 HTML 中书写 CSS 代码,因为这样难以维护。
    2、使用 Viewport 加速页面的渲染。
    3、使用语义化标签,减少 CSS 代码,增加可读性和 SEO。
    4、减少标签的使用,DOM 解析是一个大量遍历的过程,减少不必要的标签,能降低遍历的次数。
    5、避免 src、href 等的值为空,因为即时它们为空,浏览器也会发起 HTTP 请求。

  • CSS优化
    1、优化选择器路径:使用 .c {} 而不是 .a .b .c {}。
    2、选择器合并:共同的属性内容提起出来,压缩空间和资源开销。
    3、精准样式:使用 padding-left: 10px 而不是 padding: 0 0 0 10px。
    4、雪碧图:将小的图标合并到一张图中,这样所有的图片只需要请求一次。
    5、避免通配符:.a .b {} 这样的选择器,根据从右到左的解析顺序在解析过程中遇到通配符 {} 会遍历整个 DOM,性能大大损耗。
    6、少用 float:float 在渲染时计算量比较大,可以使用 flex 布局。
    7、为 0 值去单位:增加兼容性。
    8、压缩文件大小,减少资源下载负担。

  • JavaScript优化
    1、尽可能把 <script> 标签放在 body 之后,避免 JS 的执行卡住 DOM 的渲染,最大程度保证页面尽快地展示出来
    2、尽可能合并 JS 代码:提取公共方法,进行面向对象设计等……
    3、CSS 能做的事情,尽量不用 JS 来做,毕竟 JS 的解析执行比较粗暴,而 CSS 效率更高。
    4、尽可能逐条操作 DOM,并预定好 CSs 样式,从而减少 reflow 或者 repaint 的次数。
    5、尽可能少地创建 DOM,而是在 HTML 和 CSS 中使用 display: none 来隐藏,按需显示。
    6、压缩文件大小,减少资源下载负担。