0%

前端面试题(每日10道)

11.15

1.CSS盒模型

(1)用来装页面上的元素的矩形区域。由内容区、内边距、边框、外边距四部分组成

(2)CSS中的盒子模型包括IE盒子模型和标准的W3C盒子模型,盒子的宽度时存在着差异

​ 在标准的盒子模型中,宽度指内容区部分的宽度,

​ 在IE盒子模型中,宽度表示内容区,内边距,边框这三个部分的宽度

(3) 在CSS3中引入了box-sizing属性,box-sizing:content-box;表示标准的盒子模型,box-sizing:border-box表示的是IE盒子模型

补充

  • 边框和内边距的设置会影响到盒子的大小,外边距的设置不会影响盒子可见框的大小,但是会影响盒子的位置,从而影响盒子实际占用空间

  • 一个元素在其父元素中,水平布局必须要满足以下的等式:左右外边距+左右内边距+左右边框+本身宽度=父元素内容区的宽度

    如果这七个值中没有为 auto 的情况,则浏览器会自动调整margin-right值以使等式满足 如果超过800 就margin-right=-200

    这七个值中有三个值可以设置成auto,width、margin-left、 maring-right,调整相应的auto以使等式成立 宽度优先级最高

  • 如果子元素的大小超过了父元素,则子元素会从父元素中溢出,使用overflow属性来设置父元素如何处理溢出的子元素

    1
    2
    3
    4
    可选值:(1)visible,默认值子元素会从父元素中溢出,在父元素外部的位置显示。
    (2)hidden 溢出内容将会被裁剪不会显示
    (3)scroll生成两个滚动条,通过滚动条来查看完整的内容
    (4)auto根据需要生成滚动条
  • 垂直外边距的重叠(如下图绿色方块的下外边距设置100px与橙色方块上外边距设置100px重叠了) :相邻的垂直方向外边距会发生重叠现象

    • 兄弟元素外边距的重叠:兄弟元素间的相邻垂直外边距会取两者之间的较大值一正一负,则取两者的和 如果相邻的外边距都是负值,则取两者中绝对值较大的

      image-20211115145746032

    • 父子元素外边距的重叠(如下图给子元素设置上外边距100px,结果将其属性传至父元素让父元素下移):父子元素间相邻外边距,子元素的会传递给父元素(上外边距)

      image-20211115150739964

2.居中垂直对齐

1
2
3
4
单行文本: line-height = height
图片: vertical-align: middle;
absolute 定位: top: 50%;left: 50%;transform: translate(-50%, -50%);前半部分指的是相对于物体居中但是物体本身有尺寸
display:flex;margin:auto

3.点击按钮使得页面上的文字颜色发生改变如何实现

​ 首先创建一个button按钮,绑定一个onclick点击事件,对需要显示的区域设置大小和颜色(下图代码红绿交替显示)

​ 当点击按钮的时候调用该点击事件从而改变页面上的文字颜色(调用事件的时候要获取显示区域的id)

image-20211115160026280

4.html小知识

​ (1)html 语义化让页面的内容结构更清晰,便于对浏览器、搜索引擎解析;即使在没有样式 CSS 情况下也以一种文档格式显示,并 且是容易阅读的;

​ (2)alt 是给搜索引擎识别,在图像无法显示时的替代文本;title 是关于元素的注释信息,主要是给用户解读。

​ (3) iframe标签是在当前网页插入一个指定的页面 —内联框架

​ 优点:解决加载缓慢的第三方内容如图标或者广告等的加载问题

​ 缺点:iframe标签会阻塞主页面的onload事件

​ (4)href 指向网络资源所在位置 src 会将其指向的资源下载并应用到文档中,比如 JavaScript 脚本,img 图片;

​ (5)去除项目中符号:list-style:none 去除浏览器的默认样式使用重置样式表,专门用来对浏览器的样式进行重置

​ (6)开启定位的元素层级会提升且层级一样,z-index属性来指定元素的层级用整数表示,可以高于定位的层级,值越大元素的层级越 高如果元素的层级一样,则优先显示靠下的元素,祖先元素的层级再高也不会盖住后代元素(祖先z-index>后代定位)。

​ (7)text-align文本的水平对齐 可选值: left左侧对齐 right右对齐 center居中对齐 justify 两端对齐

​ (8)vertical-align 设置元素垂直对齐的方式 用于消除图片的缝

​ (9)background-image设置背景图片 background-repeat用来设置背景的重复方式 background-position用来设置背景图片的位置

(10):link用来表示没访问过的链接 :visited用来表示访问过的链接

(11)线性渐变,颜色沿着一条直线发生变化 linear-gradient();

(12)径向渐变,(放射性的效果) radial-gradient()

​ (13) 表格:table标签来创建一个表格 colspan横向的合并单元格。table虽说是块元素,但是宽度是被内容撑开的。

​ — 以将一个表格分成三个部分: 头部 thead 主体 tbody 底部 tfoot

(14)使用form标签来创建一个表单 单选按钮

5.CSS选择器的优先级

不同级别

  1. 在属性后面使用 !important 会覆盖页面内任何位置定义的元素样式。

  2. 作为style属性写在元素内的样式(行类样式) 权值1000

  3. id选择器 100

  4. 类选择器 10

  5. 标签选择器 1

  6. 通配符选择器

  7. 浏览器自定义或继承

  8. 贩卖人间快乐:
    生命周期。

    贩卖人间快乐:
    双向绑定

  9. 选择器的累加不会超过其最大的数量级,类选择器在再高也不会超过id选择器

1. link 是 XHTML 标签,除了加载CSS外,还可以定义 RSS 等其他事务;@import 属于 CSS 范畴,只能加载 CSS。
2. link 引用 CSS 时,在页面载入时同时加载;@import 需要页面网页完全载入以后加载。
3. link 支持使用 Javascript 控制 DOM 去改变样式;而@import不支持。

7.display:none和visibility:hidden的区别?

  • display:none 元素不在页面中显示,不占据页面的位置。
  • visibility:hidden 元素在页面中隐藏不显示,但是依然占据页面的位置。

8.rgba和opacity的透明效果有什么不同?

opacity 会继承父元素的 opacity 属性,而 RGBA 不会。

9.position的值, relative和absolute分别是相对于谁进行定位的?

  • relative:相对定位,相对于自己本身在正常文档流中的位置进行定位。
  • absolute:生成绝对定位,相对于最近一级定位不为static的父元素进行定位。
  • fixed: (老版本IE不支持)固定定位。
  • static:默认值,没有定位,元素出现在正常的文档流中。

10. BFC 是什么?

BFC 即 Block Formatting Contexts (块级格式化上下文),它属于普通流,即:元素按照其在 HTML 中的先后位置至上而下布局,在这个过程中,行内元素水平排列,直到当行被占满然后换行,块级元素则会被渲染为完整的一个新行。
可以把 BFC 理解为一个封闭的大箱子,箱子内部的元素无论如何翻江倒海,都不会影响到外部。
只要元素满足下面任一条件即可触发 BFC 特性

1
2
3
4
5
(1)根元素(<html>)
(2)浮动元素:float 除 none 以外的值 有局限性:高度不塌了但是脱离文档流了,宽度也就丢失了,对应特点的第三条 -不推荐
(3)绝对定位元素:position (absolute、fixed)
(4)display 为 inline-block、table-cells、flex -不推荐
(5)overflow 除了 visible 以外的值 (hidden、auto、scroll) -推荐
  • 之前不会塌是因为把高度写死了所以不会塌 现在父元素不设置高低 子元素高度为多少父元素就会被撑开多少

  • 高度塌陷的问题: 在浮动布局中,父元素的高度默认是被子元素撑开的 当子元素浮动后,其会完全脱离文档流,子元素从文档流中脱离 将会无法撑起父元素的高度,导致父元素的高度丢失 父元素高度丢失以后,其下的元素会自动上移,导致页面的布局混乱 所以高度塌陷是浮动布局中比较常见的一个问题。

  • 开启BFC后的特点:

    ​ 1.开启BFC的元素不会被浮动元素所覆盖

    ​ 2.开启BFC的元素子元素和父元素外边距不会重叠

    ​ 3.开启BFC的元素可以包含浮动的子元素

    -常用的方式为元素设置 overflow:hidden 开启其BFC以使其可以包含浮动元素 高度不塌,宽度也不会丢失。

11.16

11.doctype 的作用 声明版本

<!DOCTYPE>是一个用于声明当前HTMl版本,用来告知web浏览器该文档使用是哪种 HTML 或者 XHTML 规范来解析页面,以便浏览器更加准确的理解页面内容,更加良好地展现内容效果!

12.语义化标签的理解

  1. 即使在没有CSS样式的条件下,也能很好地呈现出内容结构、代码结构。
  2. 语义化标签会使HTML结构变的清晰,有利于维护代码和添加样式。
  3. 和搜索引擎建立良好沟通,有助于爬虫抓取更多的有效信息
  4. 同时与计算机的编译也存在一定的联系

13.important 有什么不好

css定义中的用!important样式权重是最高的,会覆盖页面内任何位置定义的元素样式,有时候程序员忘了删除,这样就会导致自己设置的样式无法显示的问题。

14. css 选择器的解析顺序,原因

解析顺序:从右往左,

原因:从右往左进行解析的好处那就是从右往左进行匹配的时候,匹配的全部是DOM元素的父节点,而从左往右进行匹配的时候时候,匹配的全部是DOM元素的子节点,这样就避免了HTML与CSS没有下载完需要进行等待的情形。且遍历查找的节点都会少很多

15.css 清除浮动

1清除浮动的第一种方式—给父级盒子添加高度

造成高度塌陷的原因就是父元素没有高度,我们只需要给父元素添加一个高度即可,但是这种方式并不推荐使用,因为有很多局限性,况且子元素依然是脱离标准流,并没有回到父元素中,此时父元素的宽度变化了。

2添加clear属性来清除浮动元素对当前元素所产生的影响**

  1. 可选值:

    ​ left清除左侧浮动元素对当前元素的影响 原来浮动的元素依然浮动

    ​ right清除右侧浮动元素对当前元素的影响

    ​ both 清除两侧中最大影响的那侧(类似高度的问题)

  2. 原理: 设置清除浮动以后,浏览器会自动为元素添加一个上外边距, 以使其位置不受其他元素的影响。

3.清除浮动的第三种方式—给父级添加overflow属性*

原理见16题

4.清除浮动的第四种方式—给父级添加after伪元素

  • 利用after伪元素定义一个clearfix类,浮动元素的父级元素调用此类可以实现清除浮动的效果

    1
    2
    3
    4
    5
    6
    .clearfix: : before,		//内边距重叠
    .clearfix: : after{ //高度塌陷
    content: '';
    display: table;//table就相当于一个物体隔开他们俩让他们的外边距不重合
    clear: both;
    }

16. overflow:hidden 是怎么清除浮动的

父块没有设置指定的高宽,当子块设置为浮动后,原本包裹子块的父块的高度塌陷消失,这时给父块设置overflow:hidden就会为父块设置一个独立的块级上下文,使这个块级元素内部的排版完全独立,从而可以包裹浮动流,全部浮动子元素也不会引起容器高度塌陷,这样就达到了清除浮动的效果,,使父块重新包裹子块。

17.HTML5、CSS3 里面都新增了那些新特性?

HTML5

  • 新的语义标签
    • article 独立的内容。
    • aside 侧边栏。
    • header 头部。
    • nav 导航。
    • section 文档中的节。
    • footer 页脚。
  • 画布(Canvas) API
  • 地理(Geolocation) API
  • 本地离线存储 localStorage 长期存储数据,浏览器关闭后数据不丢失;
    sessionStorage 的数据在浏览器关闭后自动删除
  • 拖拽释放(Drag and drop) API
  • 音频、视频API(audio,video)
  • 表单控件,calendar、date、time、email、url、searc

CSS3

  • 2d,3d变换
  • Transition, animation
  • 媒体查询
  • 新的单位(rem, vw,vh 等)
  • 圆角(border-radius),阴影(box-shadow),对文字加特效(text-shadow),线性渐变(gradient),旋转(transform)transform:rotate(9deg) scale(0.85,0.90) translate(0px,-30px) skew(-9deg,0deg);//旋转,缩放,定位,倾斜
  • rgba

18. 常见兼容性问题?

浏览器默认的margin和padding不同。解决方案是加一个全局的*{margin:0;padding:0;}来统一。

19.固定定位和绝对定位的区别

fixed :固定 定位 absolute :绝对 定位 区别 : 1、没有滚动条的情况下没有差异 2、在有滚动条的情况下, fixed定位 不会随滚动条移动而移动,而 absolute 则会随滚动条移动

20.实现一个两列固定,中间自适应有哪些方法

  1. 使用flex通过改变 flex-growflex-shrink实现

    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
    <style>
    body{
    height: 100vh;
    }
    .container {
    display: flex;
    flex-direction: row;
    height: 200px;
    background-color: lightskyblue;
    word-break: break-all;
    }
    .left, .right {
    /* 左右固定长度 */
    flex-basis: 100px;
    /* 将增长比和缩小比都设置为 0 ,避免宽度变化 */
    flex-grow: 0;
    flex-shrink: 0;
    background-color: lightslategray;
    }
    .middle {
    /* 中间自动适应 */
    flex-grow: 1;
    flex-shrink: 1;
    background-color: lightpink;
    }
    </style>
  2. 绝对定位+CSS3新盒子 (利用新盒子 width = content + padding + border 特性 🎈)

    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
    <style>
    body{
    height: 100vh;
    }
    .container {
    position: relative;
    height: 200px;
    background-color: lightskyblue;
    word-break: break-all;
    }
    .container div {
    /* 使用绝对定位来控制元素 */
    position: absolute;
    }
    .left, .right {
    height: 200px;
    width: 100px;
    background-color: lightslategray;
    }
    .right {
    right: 0;
    }
    .middle {
    /* 控制padding来放置两侧元素, content宽度会自动计算 */
    box-sizing: border-box;
    height: 100%;
    width: 100%;
    /* 两端填充100px, 用来放两侧固定元素 */
    padding: 0 100px;
    background-color: lightpink;
    }
    </style>

    11.17

    21.伪类选择器和伪元素理解以及区别(可以使用伪类选择器实现隔行变色效果)

    伪类(不存在的类,特殊的类),伪类用来描述一个元素的特殊状态 比如:第一个子元素、被点击的元素、鼠标移入的元素…

    • : nth-child()选中第n个子元素

    • : nth-of-type() 同类型元素中进行排序

      伪元素,表示页面中一些特殊的并不真实存在的元素(特殊的位置)

      ::before元素的最开头 ::after元素的最后 - before和 after必须结合content属性来使用

      22.em和rem

      em是相对于元素的字体大小来计算的

      rem是相对于根元素的字体大小来计算 —类似于全局变量

      23.我需要等三张图片都加载完毕之后再执行回调函数,可以怎么实现

使用Promise.all

  1. 当有一个ajax请求时,它的参数需要另外2个甚至更多请求都有返回结果之后才能确定,那么这个时候,就需要用到Promise.all来帮助我们应对这个场景。

  2. Promise.all接收一个Promise对象组成的数组作为参数,当这个数组所有的Promise对象状态都变成resolved或者rejected的时候,它才会去调用then方法。

promise补充

  1. 现在所有的库几乎都将ajax请求利用Promise进行了封装,因此我们在使用jQuery等库中的ajax请求时,都可以利用Promise来让我们的代码更加优雅和简单。
  2. Promise对象中的then方法,可以接收构造函数中处理的状态变化,并分别对应执行。then方法有2个参数,第一个函数接收resolved状态的执行,第二个参数接收reject状态的执行。
  3. then方法的执行结果也会返回一个Promise对象。因此我们可以进行then的链式执行,这也是解决回调地狱的主要方式。

24.setTimeout 和 Promise 哪一个先执行

Promise比setTimeout()先执行。因为Promise定义之后便会立即执行,其后的.then()是异步里面的微任务。而setTimeout()是异步的宏任务。

25.canvas 的一些方法

  1. 第一是以drawXXX为主的绘制方法; 例如drawColor:填充颜色
  2. 第二是以clipXXX为主的裁剪方法; 例如clipRect:当前画布裁剪为一个矩形
  3. 第三是以scale、translate和rotate组成的Canvas变换方法;
  4. 最后一类save:将当前的状态推送至栈中保存 restore:将上次保存的状态从栈中弹出;

26.@font-face

font-face是css3中允许使用自定义字体的一个模块。@ font-face的是一个CSS规则,允许你输入自己的字体出现在网站上**

27.transform 有哪些属性

transform属性值:

  • transform: translate(): translate()的括号内包含两个值,分别为相对于元素原有位置在水平方向和垂直方向的偏移值

  • transform:rolate(): 正值为顺时针,负值为逆时针,单位为deg。

  • transform:transform-origin:任何一个元素都有一个中心点,默认情况之下,其中心点是居于元素X轴和Y轴的50%处。

  • transform:scale(): scale(x,y):x,y 值分别为原有元素宽高的倍数。 1为不缩放,大于1放大,小于1缩小。

28.轮播图的原理

图片移动实现原理:

利用浮动将所有所有照片依次排成一行,给这一长串图片添加一个父级的遮罩,每次只显示一张图,其余的都隐藏起来。对图片添加绝对定位,通过控制left属性,实现照片的移动。

29.雪碧图的使用步骤

  1. 先确定要使用的图标
  2. 测量图标的大小
  3. 根据测量结果创建一个元素
  4. 将雪碧图设置为元素的背景图片
  5. 设置一个偏移量以显示正确的图片

30.js基本数据类型

数据类型主要包括两部分:

  • 基本数据类型: Undefined、Null、Boolean、Number 和 String
  • 引用数据类型: Object (包括 Object 、Array 、Function)

11.18

31.判断一个值是什么类型有哪些方法?

  • typeof 运算符
  • instanceof 运算符
  • Object.prototype.toString 方法

32. null 和 undefined 的区别?

null 表示一个对象被定义了,值为“空值”;
undefined 表示不存在这个值:
(1)变量被声明了,但没有赋值时,就等于undefined。

​ (2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。

​ (3)对象没有赋值的属性,该属性的值为undefined。

​ (4)函数没有返回值时,默认返回undefined。

33.js小知识

  1. ==,当且仅当两个运算数相等时,它返回 true,即不检查数据类型;

    ===,只有在无需类型转换运算数就相等的情况下,才返回 true,需要检查数据类型

  2. eval是把对应的字符串解析成 JS 代码并运行;

  3. var 存在变量提升; let 只能在块级作用域内访问; const 用来定义常量,必须初始化,不能修改(对象特殊)

  4. JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。

34. 箭头函数有哪些特点?

不需要function关键字来创建函数
省略return关键字
改变this指向

35.new操作符具体干了什么呢?

1、创建一个空对象,新创建的对象由 this 所引用,同时还继承了该函数的原型。
2、属性和方法被加入到 this 引用的对象中。

36.documen.write 和 innerHTML 的区别?

  • document.write 只能重绘整个页面
  • innerHTML 可以重绘页面的一部分

37.ajax过程

  1. 创建XMLHttpRequest对象,也就是创建一个异步调用对象.
  2. 创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
  3. 设置响应HTTP请求状态变化的函数.
  4. 发送HTTP请求.
  5. 获取异步调用返回的数据.
  6. 使用JavaScript和DOM实现局部刷新

38.请解释一下 JavaScript 的同源策略?

同源策略指的是:协议,域名,端口相同。

同源策略是一种安全协议,指一段脚本只能读取来自同一来源的窗口和文档的属性。

39.介绍一下闭包和闭包常用场景?

  • 闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包常见方式,就是在一个函数的内部创建另一个函数
  • 使用闭包主要为了设计私有的方法和变量,闭包的优点是可以避免变量的污染,缺点是闭包会常驻内存,会增大内存使用量,使用不当很容易造成内存泄露。在js中,函数即闭包,只有函数才会产生作用域的概念。
  • 闭包有三个特性:
    • 函数嵌套函数
    • 函数内部可以引用外部的参数和变量
    • 参数和变量不会被垃圾回收机制回收
  • 应用场景,设置私有变量的方法
  • 不适用场景:返回闭包的函数是个非常大的函数
  • 闭包的缺点就是常驻内存,会增大内存使用量,使用不当会造成内存泄漏

40,JavaScript原型,原型链 ? 有什么特点?

  • 每个对象都会在其内部初始化一个属性,就是prototype(原型),当我们访问一个对象的属性时,
    如果这个对象内部不存在这个属性,那么他就会去prototype里找这个属性,这个prototype又会有自己的prototype,
    于是就这样一直找下去,也就是我们平时所说的原型链的概念。
  • 关系:instance.constructor.prototype = instance.proto
  • 特点:
    JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变

11.19

41.javascript的内存(垃圾)回收机制?

  • 垃圾回收器会每隔一段时间找出那些不再使用的内存,然后为其释放内存

  • 一般使用标记清除方法(mark and sweep), 当变量进入环境标记为进入环境,离开环境标记为离开环境
    垃圾回收器会在运行的时候给存储在内存中的所有变量加上标记,然后去掉环境中的变量以及被环境中变量所引用的变量(闭包),在这些完成之后仍存在标记的就是要删除的变量了

  • 还有引用计数方法(reference counting), 在低版本IE中经常会出现内存泄露,很多时候就是因为其采用引用计数方式进行垃圾回收。引用计数的策略是跟踪记录每个值被使用的次数,当声明了一个 变量并将一个引用类型赋值给该变量的时候这个值的引用次数就加1,如果该变量的值变成了另外一个,则这个值得引用次数减1,当这个值的引用次数变为0的时 候,说明没有变量在使用,这个值没法被访问了,因此可以将其占用的空间回收,这样垃圾回收器会在运行的时候清理掉引用次数为0的值占用的空间。
  • 在IE中虽然JavaScript对象通过标记清除的方式进行垃圾回收,但BOM与DOM对象却是通过引用计数回收垃圾的, 也就是说只要涉及BOM及DOM就会出现循环引用问题。

42. 如何解决 ES6 的语法兼容

  1. 对于浏览器解析不了es6的语法,需要我们使用babel工具链

  2. 对于不支持let等语法的情况下可以采用

1
"use strict";//严格模式

43.ES6的一些东西

  1. 新增了letconst letconst具有块级作用域,不存在变量提升的问题,var 声明的变量作用域为包围它的函数。
  2. 新增了箭头函数,简化了定义函数的写法,同时可以巧用箭头函数的this、(注意箭头函数本身没有this,它的this取决于外部的环境)
  3. 新增了promise解决了回调地域的问题
  4. 新增了模块化、利用import 、export来实现导入、导出
  5. let的用法:定义变量
  6. 箭头函数中的this是在定义函数的时候绑定,而不是在执行函数的时候绑定

44.如何解决js浮点数运算精度问题

问题:整数和浮点数都属于 Number 数据类型,所有数字都是以 64 位浮点数形式储存,即便整数也是如此。 所以我们在打印 1.00 这样的浮点数的结果是 1 而非 1.00

解决方法:

  1. 利用toFixed() 方法用定点表示法来格式化一个数,会对结果进行四舍五入,对计算结果进行精度缩小。(不过还是存在精度问题)

  2. 或者直接用第三方帮你封装好处理浮点数的库,直接可以用。 如:number-precision

45.requestanimationframe 有用过吗?知道是干嘛的吗

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。

46.new操作符的过程

具体主要有4个部分

1.创建了一个新对象

2.把这个新对象的原型属性(proto)绑定到原函数的prototype属性(就是继承原函数原型)

3.把原函数的this指向转移到这个新对象上

4.返回新对象,如果这个函数没有返回其他对象的话

47.数组的一些方法

具体详解可以看数组方法

  1. map方法的使用 相当于循环遍历每一项然后可以对每一项进行修改

  2. filter的用法 过滤掉不符合条件的,剩下符合条件的

  3. join方法的使用 数组变字符串
  4. split方法的使用 字符串变数组
  5. splice(1,1)删除的用法 删除下标为1的1个元素

48.call apply 的区别

相同点:

都是在特定的作用域中调用函数,使用call()和apply()方法时,就会改变this的指向,

不同点:

apply()方法接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。

call()方法不一定接受两个参数,第一个参数也是函数运行的作用域(this),但是传递给函数的参数必须列举出来。

1
2
fn.call(obj , 100 , 200);
fn.apply(obj , [100, 200]);

49.箭头函数与普通函数的区别

  • 语法更加简洁、清晰

  • 箭头函数没有 prototype (原型),所以箭头函数本身没有this

  • 箭头函数不会创建自己的this

  • call | apply | bind 无法改变箭头函数中this的指向

  • 箭头函数不能作为构造函数使用

  • 如果箭头函数没有参数,

    • 直接写一个空括号即可。
    • 如果箭头函数的参数只有一个,也可以省去包裹参数的括号。
    • 如果箭头函数有多个参数,将参数依次用逗号(,)分隔,包裹在括号中即可。
    • 箭头函数的函数体只有一句的话可以省略花括号和return

50.手写一个函数去重

1.Array.prototype.includes()

基本思路:如果布尔值为假,则说明新数组不含有该元素。

  • 创建一个新数组

  • 遍历原数组

  • 新数组利用Array.prototype.includes(),返回false则将元素存储

  • 不满足条件的元素不存储

1
2
3
4
5
6
7
8
9
Array.prototype.unique = function () {
const newArray = [];
this.forEach(item => {
if (!newArray.includes(item)) {
newArray.push(item);
}
});
return newArray;
}

2.Array.prototype.reduce()

基本思路:先对原数组进行排序,然后利用reduce方法将不重复元素放进新数组。

  • 对原数组进行排序

  • 利用Array.prototype.reduce()叠加功能

  • 比较原数组和新数组元素,将不重复元素放进init新数组中。

1
2
3
4
5
6
7
8
9
Array.prototype.unique = function () {
return this.sort().reduce((init, current) => {
if(init.length === 0 || init[init.length - 1] !== current){
init.push(current);
}
return init;
}, []);
}
current就是原数组的遍历的每一个元素

11.20

51. 事件冒泡,如何阻止事件冒泡

事件冒泡指的是当前的目标元素触发事件的发生,事件再一次向祖先元素传播,在祖先元素上触发相同类型的事件。

js冒泡和捕获是事件的两种行为,使用event.stopPropagation()起到阻止捕获和冒泡阶段中当前事件的进一步传播。使用event.preventDefault()可以取消默认事件。

52.事件循环

  1. 所有同步任务都是在主线程上执行,形成一个很执行栈

  2. 主线程之外,还存在一个任务队列(task queue)只要异步任务有了运行结果,就在“任务队列”之中放置一个事件。

  3. 一旦“执行栈”中的所有同步任务执行完毕,系统就会读取“任务队列”,看看里面有哪些事件。那些对应的异步任务,就结束等待状态,进入执行栈开始被执行。

  4. 主线程不断重复以上三步。

对于 JS 运行中的任务,JS 有一套处理收集,排队,执行的特殊机制,我们把这套处理机制称为事件循环(Event Loop)。

53.什么是浏览器的同源政策

我对浏览器的同源政策的理解是,一个域下的 js 脚本在未经允许的情况下,不能够访问另一个域的内容。这里的同源的指的是两个
域的协议、域名、端口号必须相同,否则则不属于同一个域。

同源政策主要限制了三个方面

第一个是当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。

第二个是当前域下的 js 脚本不能够操作访问其他域下的 DOM。

第三个是当前域下 ajax 无法发送跨域请求。

同源政策的目的主要是为了保证用户的信息安全,它只是对 js 脚本的一种限制,并不是对浏览器的限制,对于一般的 img、或者
script 脚本请求都不会有跨域的限制,这是因为这些操作都不会通过响应结果来进行可能出现安全问题的操作。

54.浏览器如何通过jsonp跨域

1
2
3
4
5
6
7
8
9
10
11
通过动态创建 script 标签,通过 script 标签的 src 请求来通过jsonp跨域
JSONP:通过动态创建script,再请求一个带参网址实现跨域通信。document.domain + iframe跨域:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
location.hash + iframe跨域:a欲与b跨域相互通信,通过中间页c来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。

window.name + iframe跨域:通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。

postMessage跨域:可以跨域操作的window属性之一。

CORS:服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求,前后端都需要设置。

代理跨域:启一个代理服务器,实现数据的转发

55.GET和 POST 的区别

  1. GET参数通过 url 传递,POST 放在请求体 (request body) 中。
  2. GET请求只能进行url编码,而POST支持多种编码方式。
  3. GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
  4. GET请求在URL中传送的参数是有长度限制的,而POST没有。
  5. GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

56.说说你知道的HTTP 状态码

  1. 1XX 信息性状态码
    • 100 继续
    • 101 切换协议
  2. 2XX 成功状态码
    • 200 OK 成功处理了请求
    • 204 No Content 请求处理成功,但没有资源可返回
    • 206 Partial Content 请求资源的某一部分
  3. 3XX 重定向状态码
    • 301 永久性重定向,表示请求的资源已被分配了新的 URI
    • 302 临时性重定向,资源的 URL 已临时定位到其他位置
    • 303 告诉客户端应该用另一个 URL 获取资源
    • 304 表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但未满足条件的情况
  4. 4XX 客户端错误状态码
    • 400 表示请求报文中存在语法错误
    • 401 未授权
    • 403 服务器拒绝了请求
    • 404 服务器无法找到所请求的 URL
  5. 5XX 服务器错误状态码
    • 500 内部服务器错误
    • 502 错误网关
    • 503 服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
    • 504 响应超时

57.js 脚本 defer 和 async 的区别

async 是异步下载并立即执行,然后文档继续解析,defer 是异步加载后解析文档,然后再执行脚本

58.js 如何设置异步

  1. 通过回调函数

  2. 事件监听:对这个事件进行监听,利用定时器的原理去把该事件放入事件队列里,等全部执行完毕之后,才会执行事件队列里的方法

  3. 发布/订阅:存在一个”信号中心”,某个任务执行完成,就向信号中心”发布”(publish)一个信号,其他任务可以向信号中心”订阅”(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做发布/订阅模式。

    这种方法的性质与”事件监听”类似,但是明显优于后者。因为我们可以通过查看”消息中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

  4. Promises对象:每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。比如,f1的回调函数f2,可以写成:f1().then(f2);

59.关于js暂停执行的方法

  1. 利用alert,comfirm弹窗暂停
  2. 二:while();方法暂停 while(i<50000000)

    60. 异步 js 脚本在执行的时候主线程会停止吗

不会,因为是异步的,例如:刷微博,当你刷的比较快的时候,有些图片没有加载出来图片资源的加载就是一个分线程。整个页面的刷新是主线程 又或者博客的加载,有些页面的文字显示了,而图片设置了懒加载之后显示(减轻服务器的负担,增加用户体验)

11.21

61. async/await 是一个语法糖,你知道 await 后面如何用吗

await 右侧的表达式一般为 promise 对象, 但也可以是其它的值

  1. 如果表达式是 promise 对象, await 返回的是 promise 成功的值
  2. 如果表达式是其它值, 直接将此值作为 await 的返回值

async 使用在定义方法时修饰, 方法内部就可以使用await

62.this 在 node 里全局的指向是什么

global

63.对象的深拷贝和浅拷贝

浅拷贝

创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝

浅拷贝只在根属性上在堆内存中创建了一个新的的对象,复制了基本类型的值,但是复杂数据类型也就是对象则是拷贝相同的地址,而深拷贝则是对于复杂数据类型在堆内存中开辟了一块内存地址用于存放复制的对象并且把原有的对象复制过来,这2个对象是相互独立的,也就是2个不同的地址

将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

64.git rebase 和 git merge 的区别

假设有3次提交A,B,C。

在远程分支origin的基础上创建一个名为”mywork”的分支并提交了,同时有其他人在”origin”上做了一些修改并提交了。

其实这个时候E不应该提交,因为提交后会发生冲突。如何解决这些冲突呢?有以下两种方法:

1、git merge
用git pull命令把”origin”分支上的修改pull下来与本地提交合并(merge)成版本M,但这样会形成图中的菱形,让人很困惑。

2、git rebase
创建一个新的提交R,R的文件内容和上面M的一样,但我们将E提交废除,当它不存在(图中用虚线表示)。由于这种删除,小李不应该push其他的repository.rebase的好处是避免了菱形的产生,保持提交曲线为直线,让大家易于理解。

在rebase的过程中,有时也会有conflict,这时Git会停止rebase并让用户去解决冲突,解决完冲突后,用git add命令去更新这些内容,然后不用执行git-commit,直接执行git rebase —continue,这样git会继续apply余下的补丁。
在任何时候,都可以用git rebase —abort参数来终止rebase的行动,并且mywork分支会回到rebase开始前的状态。

65.DOM事件流包括哪些阶段

  • 事件捕获阶段: 事件对象从Window对象开始沿传播路径向下,依次经过各元素传播至目标元素的父元素;
  • 处于目标阶段 :事件对象到达目标元素;
  • 事件冒泡阶段: 事件对象从目标元素的父元素开始沿传播路径向上,依次经过各元素传播至Window对象。

先捕获再冒泡

66.什么是监听事件,监视事件的方式

监听事件就是等待某个事件的发生,当这个事件发生之后,对其做出一个响应。如:鼠标单击一个按钮,单击按钮时打开一个新的页面,或者双击桌面的应用图标,运行一个程序,这都是监听事件的应用。

监听三要素

  1. Event Source(事件源):监听的目标,假如我们给Button设置一个点击事件,那么Button就为事件源
  2. Event(事件):发生的事件,不同的事件需要相应的事件监听器进行处理,点击、触摸、按下等都是事件
  3. Event Listener(事件监听器):不同的事件监听器处理不同的监听事件

实现方式

  1. 常用的五种监听方式的实现方式 :内部类、匿名内部类、外部类、事件源所在类、onClick属性

  2. 通过onClick属性实现:在Button控件中有一个”onClick”属性,用于给Button控件设置监听事件,创建一个监听事件作为属性值传入

67. addEventListener 的参数有哪些

addEventListener 有三个参数:第一个参数表示事件名称(不含 on,如 “click”);第二个参数表示要接收事件处理的函数;第三个参数为 useCapture

简明说:事件名称;事件处理函数;捕获还是冒泡。

68. 浏览器输入 URL 之后发生了什么

参考链接:在浏览器输入 URL 回车之后发生了什么(超详细版)

  1. DNS 解析
  2. TCP 连接
  3. 发送 HTTP 请求
  4. 服务器处理请求并返回 HTTP 报文
  5. 浏览器解析渲染页面
  6. 连接结束

69.flex 有哪些属性,flex-basis 属性是干嘛的

  1. flex-direction属性:flex-direction属性决定主轴的方向(即项目的排列方向)。

  2. flex-wrap属性:默认情况下,项目都排在一条线(又称”轴线”)上。flex-wrap属性定义,如果一条轴线排不下,应该如何换行。

  3. flex-flow:flex-flow属性是flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap。

  4. .justify-content属性:justify-content属性定义了项目在主轴上(即横向)的对齐方式。

  5. align-items属性:align-items属性定义项目在交叉轴上(即纵向,垂直)如何对齐。

flex-basis属性:

1
2
3
4
5
6
flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。
浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。
.item {
flex-basis: <length> | auto; /* default auto */
}
它可以设为跟width或height属性一样的值(比如350px),则项目将占据固定空间。

70.线程与进程的区别

官网定义:
进程是系统进行资源分配和调度的基本单位

线程是操作系统能够进行运算调度的最小单位

简单理解:
进程:指在系统中正在运行的一个应用程序;程序一旦运行就是进程;进程——资源分配的最小单位。

线程:系统分配处理器时间资源的基本单元,或者说进程之内独立执行的一个单元执行流。线程——程序执行的最小单位。

借助阮一峰老师的解释
1.计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运行。
2.假定工厂的电力有限,一次只能供给一个车间使用。也就是说,一个车间开工的时候,其他车间都必须停工。背后的含义就是,单个CPU一次只能运行一个任务。
3.进程就好比工厂的车间,它代表CPU所能处理的单个任务。任一时刻,CPU总是运行一个进程,其他进程处于非运行状态。
4.一个车间里,可以有很多工人。他们协同完成一个任务。
5.线程就好比车间里的工人。一个进程可以包括多个线程。
6.车间的空间是工人们共享的,比如许多房间是每个工人都可以进出的。这象征一个进程的内存空间是共享的,每个线程都可以使用这些共享内存。
7.可是,每间房间的大小不同,有些房间最多只能容纳一个人,比如厕所。里面有人的时候,其他人就不能进去了。
这代表一个线程使用某些共享内存时,其他线程必须等它结束,才能使用这一块内存。
8.一个防止他人进入的简单方法,就是门口加一把锁。先到的人锁上门,后到的人看到上锁,就在门口排队,等锁打开再进去。
这就叫”互斥锁”(Mutual exclusion,缩写 Mutex),防止多个线程同时读写某一块内存区域。
9.还有些房间,可以同时容纳n个人,比如厨房。也就是说,如果人数大于n,多出来的人只能在外面等着。这好比某些内存区域,只能供给固定数目的线程使用。
10.这时的解决方法,就是在门口挂n把钥匙。进去的人就取一把钥匙,出来时再把钥匙挂回原处。后到的人发现钥匙架空了,就知道必须在门口排队等着了。
这种做法叫做”信号量”(Semaphore),用来保证多个线程不会互相冲突。

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

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

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

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

11.22

71. 区分 ‘123’ 和 123 的方法

  1. typeof

  2. instanceof

  3. toString.call() 最靠谱

    • toString.call(‘aaa’) // “[object String]”

    • toString.call(123) // “[object Number]”

72.Object.defineProperty 是干嘛的,参数有哪些

Object.defineProperty 需要三个参数(object , prop, descriptor)

  1 object 对象 =>要定义属性的对象。 给谁加
  2 propName 属性名 => 要定义或修改的属性的名称或 Symbol,要加的属性的名字 【类型:String】
  3 descriptor 属性描述 => 要定义或修改的属性描述符,加的这个属性有什么样的特性【类型:Object】

73. TCP/IP五层模型 OSI七层模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
**五层模型**
应用层
传输层
网络层
数据链路层
物理层
**七层模型**
应用层
表示层
会话层
传输层
网络层
数据链路层
物理层

74.应用层的协议哪些是基于TCP协议的,哪些是基于UDP协议的

基于TCP协议的

  • FTP(文件传输协议):定义了文件传输协议,使用21端口。
  • TELNET(远程登陆协议):一种用于远程登陆的端口,使用23端口,用户可以以自己的身份远程连接到计算机上,可提供基于DOS模式下的通信服务。
  • SMTP(简单邮件传输协议):邮件传送协议,用于发送邮件。服务器开放的是25号端口。
  • POP3(邮件读取协议):它是和SMTP对应,POP3用于接收邮件。POP3协议所用的是110端口。
  • HTTP(超文本传输协议):是从Web服务器传输超文本到本地浏览器的传送协议。
  • HTTPS(超文本传输安全协议)

基于UDP协议的

  • TFTP(简单文件传输协议):该协议在熟知端口69上使用UDP服务。
  • SNMP(简单网络管理协议):使用161号端口,是用来管理网络设备的。由于网络设备很多,无连接的服务就体现出其优势。
  • BOOTP(引导程序协议,DHCP的前身):应用于无盘设备
  • DHCP(动态主机配置协议):是一个局域网的网络协议
  • RIP(路由信息协议):基于距离矢量算法的路由协议,利用跳数来作为计量标准。
  • IGMP(Internet组管理协议)

基于TCP和UDP协议的

  • DNS(域名系统):DNS区域传输的时候使用TCP协议。域名解析时使用UDP协议。DNS用的是53号端口。
  • ECHO(回绕协议)

75.HTTP 与 HTTPS 的区别

  1. HTTP 传输的数据都是未加密的,也就是明文的,HTTPS 协议是由 HTTP 和 SSL 协议构建的可进行加密传输和身份认证的网络协议,比 HTTP 协议的安全性更高。
  2. HTTPS 协议需要 CA 证书,费用较高;
  3. 使用不同的链接方式,端口也不同,一般而言,HTTP 协议的端口为 80,HTTPS 的端口为 443;

76.HTTPS 协议的工作原理

  1. 客户使用 HTTPS URL 访问服务器,则要求 web 服务器建立 SSL 链接。
  2. web 服务器接收到客户端的请求之后,会将网站的证书(证书中包含了公钥),返回给客户端。
  3. 客户端和 web 服务器端开始协商 SSL 链接的安全等级,也就是加密等级。
  4. 客户端浏览器通过双方协商一致的安全等级,建立会话密钥,然后通过网站的公钥来加密会话密钥,并传送给网站。
  5. web 服务器通过自己的私钥解密出会话密钥。
  6. web 服务器通过会话密钥加密与客户端之间进行通信。

77.HTTP/2.0 特性(相比于1.0)

  1. 首部压缩
  2. 多路复用
  3. 二进制分帧
  4. 服务端推送

78.TCP 和 UDP 之间的区别

TCP:传输控制协议 UDP:用户数据报协议

  1. TCP 是面向连接的,UDP 是无连接的即发送数据前不需要先建立链接;
  2. TCP 提供可靠的服务。也就是说,通过 TCP 连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP 尽最大努力交付,即不保证可靠交付。
  3. TCP 是面向字节流,UDP 面向报文;
  4. TCP 只能是 1 对 1 的,UDP 支持 1 对 1,1 对多;
  5. TCP 的首部较大为 20 字节,而 UDP 只有 8 字节;

79.三次握手相关内容

image-20211122204020581

三次握手(Three-way Handshake)其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。实质上其实就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。

第一种回答

刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态,进行三次握手:

  • 第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN(c)。此时客户端处于 SYN_Send 状态。
  • 第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s),同时会把客户端的 ISN + 1 作为 ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_REVD 的状态。
  • 第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 establised 状态。
  • 服务器收到 ACK 报文之后,也处于 establised 状态,此时,双方以建立起了链接。
第二种回答
  • 初始状态:客户端处于 closed(关闭)状态,服务器处于 listen(监听) 状态。
  • 第一次握手:客户端发送请求报文将 SYN = 1同步序列号和初始化序列号seq = x发送给服务端,发送完之后客户端处于发送等待状态SYN_Send状态。(验证了客户端的发送能力和服务端的接收能力)
  • 第二次握手:服务端受到 SYN 请求报文之后,如果同意连接,会以自己的同步序列号SYN(服务端) = 1、初始化序列号 seq = y和确认序列号(期望下次收到的数据包)ack = x+ 1 以及确认号ACK = 1报文作为应答,服务器为SYN_Receive状态。(问题来了,两次握手之后,站在客户端角度上思考:我发送和接收都ok,服务端的发送和接收也都ok。但是站在服务端的角度思考:哎呀,我服务端接收ok,但是我不清楚我的发送ok不ok呀,而且我还不知道你接受能力如何呢?所以老哥,你需要给我三次握手来传个话告诉我一声。你要是不告诉我,万一我认为你跑了,然后我可能出于安全性的考虑继续给你发一次,看看你回不回我。)
  • 第三次握手: 客户端接收到服务端的 SYN + ACK之后,知道可以下次可以发送了下一序列的数据包了,然后发送同步序列号 ack = y + 1和数据包的序列号 seq = x + 1以及确认号ACK = 1确认包作为应答,客户端转为确认连接established状态。(分别站在双方的角度上思考,各自ok)

79.为什么需要三次握手,两次不行吗?

弄清这个问题,我们需要先弄明白三次握手的目的是什么,能不能只用两次握手来达到同样的目的。

  • 第一次握手:客户端发送网络包,服务端收到了。 这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
  • 第二次握手:服务端发包,客户端收到了。 这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
  • 第三次握手:客户端发包,服务端收到了。 这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。

因此,需要三次握手才能确认双方的接收与发送能力是否正常。

为什么要进行三次握手的情况:

当进行第一次握手,网络不好可能会堵塞,所以连接的请求并没有到达服务器端;但是tcp连接有超时重传的机制,所以再一次发送请求,这时候服务器端接收到了你的请求,他也会返回一个请求给你,这是第二次握手;
但是这时候网络环境突然又好了起来,那个堵塞的请求到达了服务器端,服务器端又给你回了一个请求,但是你又不想给服务器发送请求,这时候服务器的资源会进行占用等待你的请求,为了不使服务器的资源继续占用,你又必须发送一个请求给服务器;
所以要进行3次握手

80.四次挥手相关内容

image-20211122204409801

建立一个连接需要三次握手,而终止一个连接要经过四次挥手(也有将四次挥手叫做四次握手的)。这由TCP的半关闭(half-close)造成的。所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。

TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),客户端或服务器均可主动发起挥手动作。

第一种回答

刚开始双方都处于 ESTABLISHED 状态,假如是客户端先发起关闭请求。四次挥手的过程如下:

  • 第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。 即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。
  • 第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。 即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。
  • 第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。 即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认。
  • 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。 即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。

收到一个FIN只意味着在这一方向上没有数据流动。客户端执行主动关闭并进入TIME_WAIT是正常的,服务端通常执行被动关闭,不会进入TIME_WAIT状态。

在socket编程中,任何一方执行close()操作即可产生挥手操作。

第二种回答
  • 初始化状态:客户端和服务端都在连接状态,接下来开始进行四次分手断开连接操作。
  • 第一次分手:第一次分手无论是客户端还是服务端都可以发起,因为 TCP 是全双工的。

假如客户端发送的数据已经发送完毕,发送FIN = 1 告诉服务端,客户端所有数据已经全发完了服务端你可以关闭接收了,但是如果你们服务端有数据要发给客户端,客户端照样可以接收的。此时客户端处于FIN = 1等待服务端确认释放连接状态。

  • 第二次分手:服务端接收到客户端的释放请求连接之后,知道客户端没有数据要发给自己了然后服务端发送ACK = 1告诉客户端收到你发给我的信息,此时服务端处于 CLOSE_WAIT 等待关闭状态。(服务端先回应给客户端一声,我知道了,但服务端的发送数据能力即将等待关闭,于是接下来第三次就来了。)
  • 第三次分手:此时服务端向客户端把所有的数据发送完了,然后发送一个FIN = 1,用于告诉客户端,服务端的所有数据发送完毕客户端你也可以关闭接收数据连接了。此时服务端状态处于LAST_ACK状态,来等待确认客户端是否收到了自己的请求。(服务端等客户端回复是否收到呢,不收到的话,服务端不知道客户端是不是挂掉了还是咋回事呢,所以服务端不敢关闭自己的接收能力,于是第四次就来了。)
  • 第四次分手:此时如果客户端收到了服务端发送完的信息之后,就发送ACK = 1,告诉服务端,客户端已经收到了你的信息。有一个 2 MSL 的延迟等待

81. React-页面路由参数传递的两种方法(对应实战第11天的一个大坑)

list页->detail页

方法一:路由参数

路由导航:

用“/”

1
<Link to={'/detail/'+item.get('id')} key={index}>

路由map:

加”/:id”

1
<Route exact path="/detail/:id" component={Detail} />

detail页获取参数:

准确的获取到id,不需要做处理

1
this.props.match.params.id

方法二:查询参数

路由导航:

用“?”

1
<Link to={'/detail?'+item.get('id')} key={index}>

路由map:

不加”/:id”

1
<Route exact path="/detail" component={Detail} />

detail页获取参数:

不能准确的获取到id,需要做处理

1
this.props.location.search

切换 Next 主题

  1. 找到这个 next 主题的 GitHub 网站
  2. 进入你的博客本地路径,例如我的是放在 D 盘下,然后将主题 clone 到本地

    1
    2
    cd D:\myblog\blog
    git clone https://github.com/theme-next/hexo-theme-next themes/next
    阅读全文 »

day01

1. 项目开发准备

1). 项目描述: 整体业务功能/功能模块/主体的技术/开发模式
2). 技术选型: 数据展现/用户交互/组件化, 后端, 前后台交互, 模块化, 项目构建/工程化, 其它
3). API接口: 接口的4个组成部分, 接口文档, 对/调/测接口

2. git管理项目的常用操作

1). 创建本地仓库
    创建.gitignore配置文件
    git init
    git add *
    git commit -m "xxx"
2). 创建github远程仓库
    New Repository
    指定名称
    创建
3). 将本地仓库推送到远程仓库
    git remote add origin https://github.com/zxfjd3g/170612_JSAdvance.git 关联远程仓库
    git push origin master

4). push本地的更新 
    git add *
    git commit -m "xxx"
    git push origin master

5). pull远程的更新
        git pull origin master

6). 克隆github上的项目:
    git clone https://github.com/zxfjd3g/xxx.git

3. 搭建项目

1). 使用create-react-app脚手架创建模板项目(工程化)
2). 引入antd-mobile, 并实现按需打包和自定义主题
3). 引入react-router-dom(v4): 
    HashRouter/Route/Switch
    history: push()/replace()
4). 引入redux
    redux/react-redux/redux-thunk
    redux: createStore()/combineReducers()/applyMiddleware()
    react-redux: <Provider store={store}> / connect()(Xxx)
    4个重要模块: reducers/store/actions/action-types

5).src 客户端代码文件夹
    api          ajax请求相关模块文件夹=>发送ajax请求的函数模块 n个接口请求的函数模块 函数的返回值都是promise实例
    assets       共用资源文件夹   =>样式以及图片
    components   UI组件模块文件夹 =>头像 logo 底部 用户列表
    containers   容器组件模块文件夹  => 登陆 注册 主页面(聊天 老板 大神 信息完善 消息列表 个人中心)
    redux        redux相关模块文件夹
    utils        工具模块文件夹  功能函数就是重定向
    index.js      入口JS     就是匹配路由三者(登陆注册和主页面)

4. 登陆/注册界面

 首先先拆分路由 路由分为main/login/register为一级路由 还有就是main里面的二级路由 
 映射路由import {HashRouter,Switch,Route} from 'react-router-dom'
1). 创建3个1级路由: main/login/register
2). 完成登陆/注册的静态组件
  antd组件: NavBar/WingBlank/WhiteSpace/List/InputItem/Radio/Button 导航栏/两翼留白/上下留白/列表/文本输入/Gird宫格
  路由跳转: this.props.history.replace('/login')
  收集表单输入数据: state/onChange/变量属性名

5. 实现简单后台

1). 使用webstorm创建基于node+express的后台应用
2). 根据需求编写后台路由
3). 使用postman测试后台接口
4). 使用nodemon实现后台应用的自动重启动
5). 路由回调函数的3步: 读取请求参数/处理/返回响应数据

day02

1. 使用mongoose操作数据库

1). 连接数据库
2). 定义schema和Model
3). 通过Model函数对象或Model的实例的方法对集合数据进行CRUD操作 

2. 注册/登陆后台处理

1). models.js
    连接数据库: mongoose.connect(url)
    定义文档结构: schema
    定义操作集合的model: UserModel
2). routes/index.js
    根据接口编写路由的定义
    注册: 流程
    登陆: 流程
    响应数据结构: {code: 0, data: user}, {code: 1, msg: 'xxx'}

3. 注册/登陆前台处理

1). ajax
    ajax请求函数(通用): 使用axios库, 返回的是promise对象
    后台接口请求函数: 针对具体接口定义的ajax请求函数, 返回的是promise对象
    代理: 跨域问题/配置代理解决
    await/async: 同步编码方式实现异步ajax请求 
2). redux
    store.js
      生成并暴露一个store管理对象
    reducers.js
      包含n个reducer函数
      根据老state和指定action来产生返回一个新的state
    actions.js
      包含n个action creator函数
      同步action: 返回一个action对象({type: 'XXX', data: xxx})
      异步action: 返回一个函数: disptach => {执行异步代理, 结束时dispatch一个同步action}
    action-types.js
      action的type名称常量
3). component
    UI组件: 
        组件内部没有使用任何redux相关的API
        通过props接收容器组件传入的从redux获取数据
        数据类型: 一般和函数
    容器组件
        connect(
          state => ({user: state.user}),
          {action1, action2}
        )(UI组件)

day03

1. 实现user信息完善功能

1). 用户信息完善界面路由组件: 
    组件: dashen-info/laoban-info =>通过onChange/state来收集用户输入数据(头像名称 职位简介 职位名称 公司名称 工资)
    header-selector(共同的部分)=>头部作为UI组件,作为父组件定义一个设置头像的函数并传给子组件,子组件点击头像的时候首先要先更新state里的状态,还要去调用父组件传过来的方法,方法传入头像
    界面: Navbar/List/Grid(宫格)/InputItem(文本输入)/Button/TextareaItem(多行输入)
    注册2级路由: 在main路由组件
    界面完善后就是点击保存前后台交互的过程了 之前登陆和注册成功后 store里定义了一个重定向的地址为/ 而在入口文件这个/指主页面
    而在信息完善的静态组件完成后有四种情况 首先有两种类型用户 老板和大神  成功之后有信息已完善和未完善
    四个就四个路由路径 判断是否完善信息,看user.header是否有值 判断用户类型:看user.type 此时就要定义一个功能函数
2). 登陆/注册成功后的跳转路由计算 就是看后台返回的数据看是否有头像和什么类型 进行相应的路由跳转
    定义工具函数 getRedirectTo(type,header)
    计算逻辑分析
3). 后台路由处理  更新用户的路由 更新的时候是需要id,但是前面登陆和注册的时候已经把id存在浏览器cookie里
                从请求cookie得到userid
                const userid = req.cookies.userid//cookies是对象 是个键值对 如果没有说明没有登陆直接返回提示还未登陆
                根本userid找到对应的user 然而user一定有值嘛 不一定/要是里面的cookie数据被篡改了 没有对应的user就更新不了
                //通知浏览器删除userid cookie   // 返回一个提示信息请先登陆
                将user信息和前台发送的数据进行拼接 Object.assign(req.body, {_id, username, type}) 返回一个合并的对象
4). 前台接口请求函数 更新用户信息接口 export const reqUpdateUser = (user) => ajax('/update', user, 'POST')
5). 前台redux
    action-types
    异步action/同步action 
    reducer
6). 前台组件 点击保存派发异步action 执行异步action返回一个函数: disptach => {执行异步代理(注册的接口 向后台发送ajax请求)
将后台返回的数据通过派发同步action的方式派发给reducers,reducers将之前的数据和后台返回的数据进行一个拼接将其保存在state       里,然后映射到相应组件的props里。就会重新render
    UI组件包装生成容器组件用connect 
    读取状态数据
    更新状态 如果更新失败就说明前台数据有问题或者有人篡改了cookie 派发一个action返回一个...initUser 自动跳转登陆界面
    因为这是什么都没有

2. 搭建整体界面(上)

1). 登陆状态维护
    后台将userid保存到cookie中
    前台读取cookie中的userid
    redux中管理user信息状态
访问二级路由都需要登陆状态    
2). 实现自动登陆 在Main组件实现自动登陆 当组件挂载时去获取用户信息,因为之前后台在做登陆的路由使用cooike来标识了登陆的有效时间
也就是你完成注册就不需要登陆了并且你关闭页面输入相应地址都能回到相应的页面有效期为24小时 => 登陆过(cookie中有userid),但没有有登陆(redux管理的user中没有_id)发请求获取对应的user 就要去派发action去获取相应的user信息
步骤:读取cookie中的userid     如果没有,自动重定向到登陆界面
    如果有,读取redux中的user状态=> 如果 user没有_id,返回null(不做任何显示) 因为是异步的 当数据返回时就不显示null了 短暂
    如果有_id,说明已经登陆 显示对应的界面  还要一种情况就是请求根路径 
    根据user的type和header来计算出一个重定向的路由路径,并自动重定向
    如果有user_id且请求的不是根路径就显示相对应的路由(映射)

    后台逻辑分析 :而后台先读取cookie 中的 userid 如果没有返回请先登陆 如果有就将相应的user信息返回至前台
                这里派发的同步action都是和更新是一样的
    ajax请求根据cookie中的userid查询获取对应的user信息

    获取用户信息的路由是为了实现自动登录存在userid没有user._id的情况

day04

1. 搭建整体界面(下)

封装导航路由相关数据(数组/对象):在Main组件引入这四个导航的容器组件 还有一个404的UI组件
     因为每一个容器组件都有自己的一个路径以及组件标题 图标 图标下面的文本,这些信息为了更好的管理就要拿 每一个路由都要有这些信息,    就要拿对象来存取底部导航的信息,每一个对象组成一个数组(大神列表 老板列表 消息列表 个人中心)
             path:'/laoban',//路由路径
            component:Laoban,
            title:'大神列表',
            icon:'dashen',
            text:'大神'
            遍历该数组去映射路由(因为数组里的每个个对象都是一个底部导航都是以一个个键值对的形式存在的)
            包括头部导航的设置是通过遍历数组找到请求的路径等于底部导航的路径
            并且可以设置hidden值将数组中的某一项进行隐藏 当请求的路径等于点击底部导航的路径时如果用户的类型是大神 隐藏大神的列表,将隐藏后的navList底部导航
抽取底部导航组件:将隐藏后的导航列表传给底部导航组件 底部导航的UI组件拿到导航列表进行遍历
                          title={nav.text}  文本
                        icon={{uri:require(`./nav/${nav.icon}.png`)}}  图标 
                        selectedIcon={{uri:require(`./nav/${nav.icon}-selected.png`)}}  选中
                        selected={path===nav.path} 默认选中
                        onPress={()=>this.props.history.replace(nav.path)}//按下去跳转路由 由于没有这个属性
 引入import {withRouter} from 'react-router-dom' 内部会想组件中传入一些路由组件特有的属性 history/location/math
非路由组件使用路由组件API 引入import {withRouter} from 'react-router-dom'
底部导航组件不是路由组件 路由组件是被Route包裹的

2. 个人中心

读取user信息显示 
退出登陆 退出登陆去派发重置User的同步action 去除cookie中id

3. 用户列表

为大神/老板列表组件抽取用户列表组件 抽取共性组件
异步读取指定类型用户列表数据 组件挂载时获取用户列表 发送异步action 之前的都是操作user 现在要操作整个user列表
因此要重新写一个reducers
    后台路由 根据类型获取用户列表const{type} = req.query 
    api  请求获取用户列表的接口export const reqUserList = (type) => ajax('/userlist', {type})
    redux 
    component 将state里状态映射到老板大神组件props里 传给子组件也就是用户列表这个UI组件 子组件接收到遍历相应的信息

4. socket.io

实现实时聊天的库
包装的H5 WebSocket和轮询---> 兼容性/编码简洁性
它包装的是 H5 WebSocket 和轮询, 如果是较新的浏览器内部使用 WebSocket,
如果浏览器不支持, 那内部就会使用轮询实现实时通信
包含2个包:
  socket.io: 用于服务器端
  socket.io-client: 用于客户端
基本思想: 远程自定义事件机制
    on(name, function(data){}): 绑定监听
    emit(name, data): 发送消息

    io: 服务器端核心的管理对象
    socket: 客户端与服务器的连接对象

day05

1. 聊天组件功能:

后台接口:首先要引入由mongoose操作的聊天的数据库(发送用户的id 接收用户的id  from和to组成的字符串 内容 标识是否已读 创建时间)
    //关于后台的东西:聊天的消息要用数据库存起来,每发一条消息都会产生一条记录在server里db models里写 之前只定义一个users的集    //合现在要多定义一个chats集合的文档结构
    其次引入socket.io这个库 返回的是一个函数,这个函数接收一个server(服务器),相当于服务器挂在上面得到一个io对象,用这个对象有一个on来1.监视客户端与服务器的连接,绑定监听, 接收客户端发送的消息 2.处理数据 3. 向所有连接上的客户端发消息 这样做的前提就是发给浏览器端要确认一下是不是发给我的

新增获取当前用户所有相关聊天信息列表:根据接口文档返回的是除了消息数组还有包含所有users相关信息的一个对象容器
对象的好处就是根据某一个user_id能够得到user信息 因为chatmsg里有from和to的user_id 你只要告诉我chatmsg我就能找到users
            {
                "code": 0,
                "data": {
                    "users": {
                        "5ae1d5d19151153d30e008fd": {
                            "username": "ds2"
                        },
                        "5ae1ddd99ca58023d82351ae": {
                            "username": "aa",
                            "header": "头像1"
                        },
                        "5ae1df049ca58023d82351af": {
                            "username": "aa2"
                        },
                        "5ae1e72aa072c522e024b18e": {
                            "username": "bb",
                            "header": "头像3"
                        },
                        "5ae1f088d37a442b749fc143": {
                            "username": "laoban1",
                            "header": "头像2"
                        }
                    },
                    "chatMsgs": [
                        {
                            "read": false,
                            "_id": "5ae1f3c3a95eb824841199f1",
                            "from": "5ae1f088d37a442b749fc143",
                            "to": "5ae1ddd99ca58023d82351ae",
                            "content": "aa",
                            "create_time": 1524757443374,
                            "__v": 0
                        }
                    ]
                }
            }
获取当前用户所有相关聊天信息列表路由:
获取cookie中的userid
  const userid = req.cookies.userid
查询得到所有user文档数组
  UserModel.find(function (err, userDocs) {
    // 用对象存储所有user信息: key为user的_id, val为name和header组成的user对象
    const users = {} // 对象容器
    userDocs.forEach(doc => {
      users[doc._id] = {username: doc.username, header: doc.header}  //找用户名和头像
           return users
    })
    ChatModel.find({'$or': [{from: userid}, {to: userid}]}, filter, function (err, chatMsgs) {
      // 返回包含所有用户和当前用户相关的所有聊天消息的数据
      res.send({code: 0, data: {users, chatMsgs}})  //找聊天记录
    })
    返回一个对象给前台
chat静态组件:点击userlist里就要跳转到聊天的界面<Card onClick={() => this.props.history.push(`/chat/${user._id}')}>
            也是需要withrouter
发送消息与接收消息 点击发送(有消息才)派发action(要传入发送的id 和接收的id 和content)
         const to = this.props.match.params.userid 对方
         const from = this.props.user._id redux将状态映射在chat组件的props
         现在发消息不是用ajax请求了 是用socket.io来发 后台接收到消息进行处理返回给前台 前台也要对数据进行筛选
         完成这些之后,只要用户登陆就去获取消息列表,有三种情况
         注册成功,登陆成功,getUser自动登陆 之后都要获取消息列表 封装一个得到消息列表 消息列表是之前就获得的了
         希望initIO调用的实际是一上来就希望初始化sockio 能启动监视 
         做到这里 redux就有了消息列表的数据 之后就是显示在组件里
         对chatMsgs进行过滤 
         const targetId = this.props.match.params.userid
         const chatId = [meId,targetId].sort().join('_')
         const msgs = chatMsgs.filter(msg=>msg.chat_id===chatId)
         此时的msgs就是两个人互相发的消息 但是还是要过滤 因为左边是对方发给我的  右边是我发给对面的 
         此时就实现了消息列表显示了

接收消息显示: 因为后台是向所有连接上的客户端发消息,只有当chatMsg是与当前用户相关的消息, 才去分发同步action保存消息 
完善列表显示: 表情功能  初始化表情列表数据 设置是否显示表情列表的状态 通过点击表情图案显示和触焦的时候隐藏

day06

1. 消息列表

对消息进行分组保存, 且只保存每个组最后一条消息  message路由组件
    首先先读store保存的数据
    定义一个getLastMsg的函数
    使用{}进行分组(chat_id),只保存每个组最后一条msg  let lastMsg = lastMsgObjs[chatId] 如果没有这个遍历的msg就是
        如果有就比较时间 越晚的
    到所有分组的lastMsg组成数组 const lastMsgs  = Object.values(lastMsgObjs)
    对数组排序(create_time,降序)   return m2.create_time - m1.create_time  //降序排列 大的放前面
对于对象容器和数组容器的选择
数组排序

2. 未读消息数量显示

每个组的未读数量统计  由
总未读数量统计显示
查看消息后, 更新未读数量

3. 自定义redux和react-redux

理解redux模块
    1). redux模块整体是一个对象模块
    2). 内部包含几个函数
        createStore(reducers)  // reducers: function(state, action){ return newState}
        combineReducers(reducers)  // reducers: {reducer1, reducer2}  返回: function(state, action){ return newState}
        applyMiddleware()  // 暂不实现
    3). store对象的功能
        getState()  // 返回当前state
        dispatch(action)  // 分发action: 调用reducers()得到新的总state, 执行所有已注册的监听函数
        subscibe(listener) // 订阅监听: 将监听函数保存起来
理解react-redux模块
    1). react-redux模块整体是一个对象模块
    2). 包含2个重要属性: Provider和connect
    3). Provider
        值: 组件类
        作用: 向所有容器子组件提供全局store对象
        使用: <Provider store={store}><Xxx/></Provider>
    4). connect
        值: 高阶函数
        作用: 包装组件生成容器组件, 让被包装组件能与redux进行通信
        使用: connect(mapStateToProps, mapDispatchToProps)(Xxx)

硅谷直聘总结

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
1、使用 create-react-app(脚手架)搭建项目(工程化)
2、项目(前端)源码目录设计:
api ajax请求相关模块文件夹=>发送ajax请求的函数模块 n个接口请求的函数模块 函数的返回值都是promise实例
assets 共用资源文件夹 =>样式以及图片
components UI组件模块文件夹 =>头像 logo 底部 用户列表
containers 容器组件模块文件夹 => 登陆 注册 主页面(聊天 老板 大神 信息完善 消息列表 个人中心)
redux redux相关模块文件夹
utils 工具模块文件夹 功能函数就是重定向
index.js 入口JS 就是匹配路由三者(登陆注册和主页面)
3、引入 antd-mobile实现按需打包和自定义主题 完成登陆/注册的静态组件
antd组件: NavBar/WingBlank/WhiteSpace/List/InputItem/Radio/Button 导航栏/两翼留白/上下留白/列表/文本输入/Gird宫格
4、引入路由下载路由包: react-router-dom import {HashRouter, Switch, Route} from 'react-router-dom'
拆分路由 路由组件分为main/login/register为一级路由 还有就是main里面的二级路由 路口文件映射一级路由
5、引入redux
reducers.js:包含多个用于生成新的 state 的 reducer 函数的模块
store.js:redux 最核心的 store 对象模块
action-type.js:包含所有 action 的 type 常量名称的模块,避免编写错误无法定位
redux/actions.js:包含n个action creator函数
6、完成登陆/注册的静态组件注册
antd组件: NavBar/WingBlank/WhiteSpace/List/InputItem/Radio/Button 导航栏/两翼留白/上下留白/列表/文本输入
登陆的路由组件通过通过onChange监听来收集表单输入数据
路由跳转: this.props.history.replace('/login')
实现简单后台:
1). 使用webstorm创建基于node+express的后台应用
2). 根据需求编写后台路由
3). 使用postman测试后台接口
4). 使用nodemon实现后台应用的自动重启动
5). 路由回调函数的3步: 读取请求参数/处理/返回响应数据
7、搭建后台应用结构 监听的端口号在3000 bin目录下的www可以修改端口。
8、要在后台创建一个包含n个操作数据库集合数据的Model模块的文件夹并使用 mongoose 操作数据库
1. 连接数据库
1.1. 引入mongoose
1.2. 连接指定数据库(URL只有数据库是变化的)
1.3. 获取连接对象
1.4. 绑定连接完成的监听(用来提示连接成功)
2. 定义出对应特定集合的Model并向外暴露
2.1. 字义Schema(描述文档结构) 可以指定文档的结构 (属性名/属性值的类型)
2.2. 定义Model(与集合对应, 可以操作集合) 通过 Model实例进行添加查询多个或一个数据更新删除数据
2.3. 向外暴露Model
3. 进行增删改查操作里的回调函数的user是包含user的文档对象
查看数据库的数据可以使用MongDB这个插件
9、路由器模块: routes/index.js引入所创建的Model
根据请求URL 请求方式 请求的参数类型处理请求,返回响应数据 由于前台没做好用postman进行测试
使用nodemon 这个包可以自动重运行(改完代码它会自动重新运行)
10、注册/登陆前台处理
封装 ajax 请求代码(与后台通信要发ajax请求)
n个接口请求的函数模块 (注册登陆接口)
获取查询数据用get请求 提交数据的因为会保存到数据库所以用POST
11、使用 redux 管理用户信息
注册登陆的后台返回的数据有三个属性 code msg data
12、实现登陆注册的步骤
后台处理:连接数据库: mongoose.connect(url)
定义文档结构: schema 用户名密码类型公司
定义操作集合的model: UserModel
根据接口文档编写注册和登陆的路由以及返回给前台的响应数据结构: {code: 0, data: user}, {code: 1, msg: 'xxx'}
前台处理:完成登陆/注册的静态组件之后
通过引入redux-thunk发送登陆/注册的异步action
异步action: 返回一个函数: disptach => {执行异步代理(注册的接口 向后台发送ajax请求), 结束时dispatch一个同步action}
reducer接收到带有后台返回数据的同步action进行数据的处理最终返回一个新的state给store store拿到新的数据就去实现相应的跳转
补充:1.执行异步代理的时候后台如果是注册的路由判断用户是否已经存在, 如果存在, 返回提示错误的信息, 如果不存在, 保存
此用户已存在
2.如果是登陆的路由根据username和password查询数据库users, 如果没有, 返回提示错误的信息,
如果有, 返回登陆成功信息(包含user) 用户名或密码不正确
3.前台也需要进行前台表单验证, 如果不合法返回一个同步 action 对象, 显示提示信息 用户名密码必须输入 密码和确认密码不同
4.注册成功就不需要登陆 标识登陆有两种技术其中就是cookie,session
因此在注册成功后还要向res里添加一个cookie数据,放user.id 生成一个 cookie(userid: user._id), 并交给浏览器保 存
5.执行异步代理有用到async/await 其实是对promise的包装
6.异步注册action中首先进行前台表单验证, 如果不合法返回一个同步 action 对象, 显示提示信息
表单数据合法:返回一个发ajax请求的异步action函数
异步 ajax 请求, 得到响应 不加await得到的就是promise
直接想要结果就加一个await 用了await 这个声明的函数就要用async
7.路由组件由于要和redux进行交互,用引入redux-thunk里connect组件将其包装成容器组件
8.点击注册的时候会发生跨域的问题 因为前台应用是在3000端口 后台应用是在4000端口
解决方法:Jsonp不行只支持Get请求 使用代理服务器。具体实现是前台先将请求发送给代理服务器(4000端口),代理服务器帮忙转发请求,然后后台请求返回,代理会将返回的结果交给前台(此时是不存在跨域问题的,因为跨域问题是浏览器的限制,在浏览器看来它还是3000端口,就相当于3000端口发送的请求被代理拦截)
13、实现user信息完善功能