狂奔的蜗牛

狂奔的蜗牛 查看完整档案

天津编辑天津工业大学  |  计算机科学与技术 编辑保密  |  前端开发工程师 编辑 www.baidu.com 编辑
编辑

我一人,我异人,我亦人

个人动态

狂奔的蜗牛 收藏了文章 · 2020-12-01

2020.09 中高级前端面试题

最近因为公司的战略调整与职业发展的缘故离开了老东家,期间变成了求职者的角色,也做了半个月的面试官。现在已经从面试官的职务抽离了出来,故来分享一下 2020 中高级前端工程师常见的面试题。

面试前的准备

  1. 中/英文自我介绍
  2. 离职的原因

html

  1. 把带 css 链接的 <link> 标签放在 <head> 标签内,而带 js 链接的 <script> 标签尽量靠近 </body>, 为什么会有这种提议?如果必须不这样做,如何处理?

css

  1. 列举水平垂直居中的实现
  2. 简述一下什么是盒子模型
  3. 了解过 flex 布局、 grid 布局吗
  4. 计算样式权重

JavaScript

  1. 数组去重
  2. 数组多层扁平化
  3. 深拷贝数组和对象
  4. 数组里面有 10 万个数据,取第 1 个数据与第 10 万个数据的时间相差多少?
  5. 数组的 map 和 forEach 有什么区别?
  6. Fetch 与 XHR 的区别?
  7. bind、call、apply 的区别?
  8. 如何检测变量的类型(尽可能多,描述可能出现的问题)?
  9. 简述一下原型链
  10. 使用过 ES6 吗?你常用的有哪一些?
  11. 简述一下 Promise 的特点
  12. Promise 怎么做异常处理?
  13. Promise 串联加载?
  14. Promise 并行加载?
  15. Promise 限流并发?
  16. async/await?
  17. 一定要使用 async/await 吗,如果我不做异步处理,可能只是处理简单的关闭弹框不能用 Promise 吗?
  18. 事件循环 (event-loop)

计算题

  1. 按顺序写出程序的输出结果:

    考察 this 问题
    var length = 10;
    
    function fn() { alert(this.length); }
    var obj = {
      length: 5,
      callApi: function(fn) {
        fn();
        arguments[0]();
      }
    }
    
    obj.callApi(fn, 3)
  2. 列出下面代码的不足及优化方案

    var node = document.querySelectorAll('ul');
    for (var i = 0;i < node.length; i++) {
      node[i].addEventListener('click', function() {
        alert('click' + i);
      });
    }
  3. 改造下面的代码,使之输出 0 ~ 9,写出你能想到的所有解法。

    for (var i = 0;i < 10; i++) {
      setTimeout(() => {
        console.log(i)
      }, 1000);
    }
  4. 请写出下面代码的运行结果

    async function async1() {
     console.log("async1 start");
     await async2();
     console.log("async1 end");
    }
    
    async function async2() {
        console.log("async2");
    }
    
    console.log("script start");
    
    setTimeout(() => {
        console.log('setTimeout');
    }, 0);
    
    async1();
    
    new Promise((reslove) => {
        console.log("promise1");
        reslove();
    }).then(() => {
        console.log("promise2");
    })
    
    console.log("script end");

概念类

  1. 什么是闭包?列举闭包的应用场景
  2. 什么是科里化?
  3. 节流与防抖
  4. 说说从浏览器地址栏输入 URL 到页面加载完的过程中都发生了什么事情?

TypeScript

  1. 为什么要引入 TypeScript,引入的好处是什么?

React

  1. 虚拟 dom 是如何提升性能的
  2. React 通过什么方式来更新数据
  3. React 不能直接修改 State 吗?
  4. setState 是同步还是异步的?
  5. React 生命周期
  6. 组件通信
  7. 简述一下 React.Context 怎么使用
  8. 函数组件是什么?
  9. 高阶函数、高阶组件
  10. 受控组件与非受控组件的区别
  11. 异步组件怎么使用?
  12. 怎么对组件的参数做类型约束呢?
  13. 组件设计原则
  14. React 性能优化
  15. 父组件在执行 render 时会不会触发子组件的 render 事件?如果会该怎么避免?
  16. 有了解过 Portals 吗?
  17. Hook 相比 class 的优点
  18. 自定义 Hook 约束
  19. 自定义 Hook 使用
  20. redux 是什么?
  21. redux 的三大原则是什么?
  22. 描述 redux 单向数据流

React 面试题详细答案解析:

Vue

  1. 列举 Vue 的生命周期以及它们的应用场景
  2. 父组件和子组件生命周期钩子执行顺序是什么?
  3. DOM 渲染在 Vue 的哪个生命周期就已经完成
  4. v-for 遍历模板时为什么要用 key? key 有什么用?
  5. v-if 与 v-show 有什么区别? 在什么场景下可以应用
  6. 组件通信
  7. vue 的双向绑定原理是什么?
  8. 怎么在组件中实现 v-modal
  9. vue router 的 beforeEach 和 afterEach 一般用法/使用场景?

webpack

  1. module、chunk、bundle 分别是什么?
  2. loader 和 plugin 有什么区别?有自己实现过吗?
  3. 除了做基础脚手架外,还用来做过什么?
  4. webpack 实现懒加载
  5. webpack 优化项
  6. babel-runtime 和 babel-polyfill
  7. 什么是 Tree-Shaking

优化类

  1. SPA(vue/react) 如何优化首页的加载速度?首屏空白是什么问题引起的?
  2. 页面优化有哪些方案?

解决方案

  1. 列举移动端适配方案
  2. 怎么处理跨域?
  3. Vue 与 React 的区别,怎么做技术选型?

HTTP

  1. 列举 HTTP 状态码以及它们的含义
  2. 有了解过 HTTP 缓存吗?

运维相关

  1. 了解过 docker 吗?
  2. 你们项目的 CI/CD 是怎么搭建的?
  3. 阿里云的 CDN 资源有缓存,如何强制更新缓存?
  4. git 除了 pullpush 外还了解哪些命令?

其他

  1. 你哪些项目实现的比较满意?
  2. 你碰到最难的一个问题是什么?
  3. 是否写过测试用例
  4. 是否了解敏捷开发

目前只是整理了常见的题目,现在正在 front-end-lab 中持续更新: 2020 前端面试笔记 ,同学们可以 watch 储存库的更新,后续也会陆续同步到此处上~

如果有同学感兴趣的话,后续还可以分享一些面试的心得,以及做面试官时观察到的一些问题。最后希望大家能在“金九银十”的求职季中找到满意的工作~

查看原文

狂奔的蜗牛 收藏了文章 · 2020-10-09

2020年8月-9月前端找工作感受总结

记录一下自8月中旬离职之后准备以及找工作的经历,会提到个人感受到的招聘情况和一些前端面试题.会按照以下几个点来展开.
  • 背景信息(个人相关)
  • 整体找工作的感受
  • 整体节奏(时间安排)
  • 面试题整理(含算法题)
  • 感悟和总结

背景信息(个人相关)

普通二本,计算机专业,三年工作经验(一年爬虫,两年前端+全栈),前端岗位,技术栈主要是vue.js + node.js.简历上稍微好的点应该就是在猎豹的工作经历和爬虫的工作背景.整体看来就是很一般般的.

整体找工作感受

在经历整个过程之后对找工作前的一些点的思考,希望减少一些同学对今年就业形势的误判.

  • 整体就业形势的感受

因为受疫情影响,整体经济大形势是不好的,很多企业都在裁人,所以整体hc减少,工作不好找,这是在脉脉或者v2ex论坛里面很多人的反馈.但是我个人感受是今年招人的还是挺多的,就拿我的情况来说,我找工作一直用的boss直聘(boss招聘打钱),上面有300+条消息都看不过来(这个有点小诀窍,下面会提到),只有第一周我回了一些比较中意的hr简历,剩下的两周都没有再投简历.
结合约面试的情况来看,招聘主要集中在滴滴,美团,字节这三家.boss上发消息的10个里面有6个字节的.美团换着部门面面了三次,滴滴面了两次.面了三周的时间,基本上每天两场.最后陆陆续续拿到了6个offer.
后来我也分析了,整体hc减少是不假,但可能是10个小厂减少了20个hc,一个大厂涨了12个hc这样的情况,hc少了但是更集中了.
我的结论就是,有些事别老听别人说,要小马过河,自己去试试.
另外,我也有点困惑,今年找工作的这个情况到底是3年的工作经验带来的还是猎豹的工作经历带来的.

  • Github项目star数和个人博客对面试的影响

我在github上有一个500+ star的项目,还有平时有写博客的习惯,我从改简历开始就琢磨这些点该怎么表达更好,尽管我在每次自我介绍的时候都会提到在开发之余我会写写自己的项目和博客,但是目前github项目基本没有人问的,博客也只有百度五面和去哪儿二面的时候才有问到.
所以,这部分怎么说呢,属于锦上添花的,要搞好核心能力,不要把这些看太重,除非你这这两点非常非常优秀.

  • 关于视频面和现场面

视频面确实方便,面试成本低,写代码也更方便,但是呢,视频面节奏比较慢,基本上不会一面之后立马二面,我见过最快的视频面隔了一个小时进入下一面,其他的都隔着1-3天不等.不如现场面痛快,今年的现场面基本上都能当场轻松的拿到offer.

  • boss上遇到的一些问题

    • 我发现把boss的求职状态设置成[在职-暂不考虑]比设置成[离职-随时到岗]更有用,设置成[在职-暂不考虑]然后每天还正常的刷boss会收到更多的消息,个人感受,无实际依据.
    • boss上有一部分人,不是hr也是不是team leader,但是也会找你要简历,这部分八成是赚内推奖励的,我的简历就被莫名其妙的推过几次,对于这种情况我不置可否,但是如果你有确定的内推人的话,最好甄别一下这种情况,把内推奖励留给小伙伴.

整体节奏(时间安排)

第一周,主要用来回顾一下基础知识,看了下一些比较重要的知识点,没怎么看面试题,面试题基本上都是通过面试来查漏补缺的.同时开始约下周的面试.
第二周,开始面试,基本都是视频面,上午一场下午一场,好留出来时间整理遇到的问题.周六日基本不约面试,同时开始约下周的面试.这周的面试基本上全挂一面,惨不忍睹,当然也有拿offer的,作为保底.这不重要,重要的是查漏补缺,找出自己准备的和面试的差距.好有的放矢.
第三周,面试节奏放慢了一点,面试题都过了一遍了,所以这周基本上都能进二面了,也拿到了一些offer,这时候去推掉第二周拿到的offer.
第四周,这时候面试节奏更慢了,基本上是第三周面试进入比价靠后的面试和一些临时约的面试.这时候拿到的offer之后去推掉上周的offer.这周之后基本上面试结束,找工作告一段落,纠结纠结手里的offer,作出决定,然后等着入职就行了.

面试题整理

常规知识点

在第一周的时候整理基础知识的时候侧重回顾的一些知识点,这里列一下

  • js基础知识
  • 从输入url到获得页面经历的所有事情(越细越好)
  • 原型链
  • 继承
  • es6新特性
  • DOM事件和事件流
  • 盒子模型
  • 事件循环
  • BFC
  • Flex
  • Promise原理以及手写代码
  • vue响应式原理
  • vue虚拟dom & diff算法
  • 前端性能优化
  • 防抖和节流
  • HTTP缓存
  • 常见算法(排序洗牌等)

面试中遇到的高频题(跟上面会有重合)

  • 事件循环
  • 原型链
  • vue响应式原理
  • HTTP缓存
  • 防抖和节流
  • vue3解决什么问题
  • Vue 为什么不能检测数组和对象的变化,怎么处理(为什么通过索引操作数组不能触发响应式)
  • vue router原理
  • v-model实现原理
  • vue.nexttick
  • https的过程
  • 性能优化
  • promise(基本上要求可以手写Promise.all方法,这个问的最多)
  • async/await
  • 从输入url到看到页面经历的所有事情(越细越好,这道题知识点非常多)
  • 跨域形成原因以及解决方案
  • 深拷贝和浅拷贝(也会问到JSON.stringify JSON.parse这种方案的弊端)
  • 箭头函数和普通函数有什么区别
  • 最近在看什么新技术

低频但是需要注意的面试题

  • CSRF & XSS
  • chrome设置小于12px字体
  • node和浏览器事件循环有何不同
  • webpack tree shaking
  • map & set
  • 前端有哪些技术(腾讯面试题,面试官会顺着你说的技术一个一个展开问)
  • 实现一个call/bind(call,apply,bind区别)
  • get 和 post 的区别
  • BFC
  • yield
  • 如何给localStorage加上max-age功能
  • Object.defineProperties都有那些参数,以及参数解释
  • requestAnimation
  • 手写原型链和js各种继承模式
  • 让你重新做你做过的项目有什么可以改进的地方
  • 让一个元素消失的几种做法,有何不同,对子元素的影响
  • 如何遍历对象(接下来会问有何不同)
  • 搜索框输入需要注意的点(其实还是在问防抖)
  • 原生实现inserAfter
  • 事件委托应用场景,e.target和e.currentTarget区别
  • HTTP缓存,对应字段,cache-contron都有那些值
  • new过程都发生了什么

面试遇到的算法题

  • 排序算法(冒泡,快排)
  • 洗牌算法
  • v1.2.3 v0.3.0 这样的版本号比大小(找简单方法,不要随便写一个循环的版本)
  • 广度优先遍历
  • 用O(n)的复杂度合并两个有序数组
  • 数组生成树形结构
    var arr = [
        { id: 1, value: "节点1", p_id: 0 },
        { id: 2, value: "节点2", p_id: 1 },
        { id: 3, value: "节点3", p_id: 1 },
        { id: 4, value: "节点4", p_id: 2 },
        { id: 5, value: "节点5", p_id: 0 },
        { id: 6, value: "节点6", p_id: 5 },
        { id: 7, value: "节点7", p_id: 6 },
        { id: 8, value: "节点8", p_id: 6 },
    ];
    // 输出
    [{
        "id": 1,
        "value": "节点1",
        "p_id": 0,
        "children": [
            {
                "id": 2,
                "value": "节点2",
                "p_id": 1,
                "children": [
                    {
                        "id": 4,
                        "value": "节点4",
                        "p_id": 2,
                        "children": []
                    }
                ]
            },
            {
                "id": 3,
                "value": "节点3",
                "p_id": 1,
                "children": []
            }
        ]
    },
    {
        "id": 5,
        "value": "节点5",
        "p_id": 0,
        "children": [
            {
                "id": 6,
                "value": "节点6",
                "p_id": 5,
                "children": [
                    {
                        "id": 7,
                        "value": "节点7",
                        "p_id": 6,
                        "children": []
                    },
                    {
                        "id": 8,
                        "value": "节点8",
                        "p_id": 6,
                        "children": []
                    }
                ]
            }
        ]
    }]
  • 数组L型输出
// L型输出
var arr = [

    ['1', '2', '3'],

    ['4', '5', '6'],

    ['7', '8', '9'],

];
// 输出大致顺序 1 4 7 8 9 2 5 6 3
  • 数组求排列组合
// 数组排列组合
var arr = [
    ['A', 'B', 'C'],
    [1, 2, 3],
    ['X', 'Y', 'Z'],
];
// 输出类似 A1X A1Y A1Z ...
  • 实现一个函数 find(obj, str),满足
var obj = {a:{b:{c:1}}};
find(obj,'a.b.c') //1
find(obj,'a.d.c') //undefined
  • 乒乓球比赛判断输赢(这个表达起来费劲,但是是个贴合实际开发场景的例子.不常见,但是不难)

这些是能回忆出来的部分,算法题普遍没有那么难,递归比较多,另外能用简单的方式就用简单的方式,这样才能凸显能力.

感悟和总结

  • 到手的offer,最后选择去百度了

    • 百度(具体部门就不说了)
    • 美团(优选)
    • 去哪儿(机票)
    • 作业帮(成人教育)
    • 天眼查(企服)
    • 一个小公司
  • 一定要避免类似面试官问你项目中遇到什么问题没,你说没问题都解决了这样的回答,基本必挂(网易二面经验)
  • 上面的面试题同一个问题会有不同的表述,所以要认真审题,搞清楚问的知识点,避免面试官问在第三层,而你只答在第一层这样的事情,太拉跨(腾讯一面经验)
  • 找工作是个概率的事情,面试的次数多了就会中
  • 内推有时会可能会有意想不到的加持(面作业帮的体会)
  • 日常我们要留意构建自己的基础知识体系,这样在面试准备的时候不用恶补太多基础知识

最后希望在找工作的同学保持心态,都能顺利找到理想的工作.

查看原文

狂奔的蜗牛 收藏了文章 · 2020-09-16

终极解密输入网址按回车到底发生了什么

公众号:码哥字节,转载请联系我

详解输入网址点击回车,后台到底发生了什么。透析 HTTP 协议与 TCP 连接之间的千丝万缕的关系。掌握为何是三次握手四次挥手? time_wait 存在的意义是什么?全面图解重点问题,再也不用担心面试问这个问题。

大致流程

  • URL 解析,解析 http 协议、端口、资源地址。
  • DNS 查询:首先查询本地 host,再访问 DNS 服务器将 域名解析成 ip 地址。
  • 建立 TCP 连接。
  • 服务器收到请求后处理,并且构造响应返回给客户端。
  • 客户端接收 HTTP 报文响应。
  • 渲染页面,最后有可能会四次挥手断开连接,也可能不会而是复用连接。

重点来了

  • 如何理解 TCP 的三次握手与四次挥手?每次握手客户端与服务端是怎样的状态?
  • 为何挥手会出现 2MSL,遇到大量 Socket 处在 TIME_WAIT 或者 CLOSE_WAIT 状态是什么问题?
  • 三次握手与四次挥手的过程是怎样的?
  • HTTP 的报文格式又是怎样的?

继续阅读本文,且听码哥字节答疑解惑,微信搜索 “码哥字节”,关注公众号更多硬核。

URL 解析

比如 【码哥字节】在思否发布的一篇文章的地址:https://segmentfault.com/a/1190000023475177。url 遵守的规则是这个样子

 scheme://host.domain:port/path/filename

每个名词的含义如下解释:

  • scheme 定义应用层协议类型,比如 http、https、 ftp 等;
  • host 定义域主机(http 的默认主机是 www);
  • domain 定义因特网域名,比如 segmentfault.com;
  • port 主机的端口,http 默认是 80, https 默认是 443;
  • path 服务器上的资源路径;
  • filename - 定义文档/资源的名称;

DNS 查询

浏览器不能直接通过域名找到服务器,只能通过 IP 地址。

那浏览器是如何通过域名查询到我们输入的 url 对应的 IP 呢?

  • 浏览器缓存:按照一定频率缓存 DNS 数据。
  • 操作系统缓存:如果浏览器缓存好啊不到记录则去操作系统中找。
  • 路由缓存:路由器 DNS 缓存。
  • ISP 的 DNS 服务器:ISP 是互联网服务提供商(Internet Service Provider)的简称,ISP 有专门的 DNS 服务器应对 DNS 查询请求。
  • 根服务器:ISP 的 DNS 服务器还找不到的话,它就会向根服务器发出请求,进行递归查询(DNS 服务器先问根域名服务器.com 域名服务器的 IP 地址,然后再问 .baidu 域名服务器,依次类推)

TCP 连接建立与断开

通过域名解析出 IP 地址以后就要建立 TCP/IP 连接了。

TCP/IP 分为四层,每一层都会加上一个头部再发送给下一层。到了接收方后,对应的每一层则把对应层的头部解析拆除,丢上上一层,跟发送端的过程反过来。

TCP/IP四层模型

应用层:发送 HTTP 请求

浏览器从地址栏得到服务器 IP,接着构造一个 HTTP 报文,其中包括:

  • 请求行包含请求方法、URL、协议版本
  • 请求报头(Request Header):由 “关键字: 值”对组成,每行一对,关键字与值使用英文 “:” 分割
  • 请求体:请求参数,并不是所有的请求有又请求参数。一般 get 参数 的格式 name=tom&password=1234&realName=tomson,也可以将参数放在 body 里面。

传输层:TCP 传输报文

在传输报文之前会先建立 TCP/IP 连接,也就是后面我们要说的三次握手。

在这一层解决了数据可靠传输、及流量控制、拥塞控制。

可靠传输

对于发送方发送的数据,接收方在接受到数据之后必须要给予确认,确认它收到了数据。如果在规定时间内,没有给予确认则意味着接收方没有接受到数据,然后发送方对数据进行重发。

TCP的可靠传输是通过确认和超时重传的机制来实现的,而确认和超时重传的具体的实现是通过以字节为单位的滑动窗口机制来完成。

TCP拥塞控制

TCP协议通过慢启动机制、拥塞避免机制、加速递减机制、快重传和快恢复机制来共同实现拥塞控制。

流量控制

采用通知窗口实现对发送端的流量控制,通知窗口大小的单位是字节。TCP通过在TCP数据段首部的窗口字段中填入当前设定的接收窗口(即通知窗口)的大小,用来告知对方 '我方当前的接收窗口大小',以实现流量控制。

通信双方的发送窗口大小由双方在连接建立的时候商定,在通信过程,双方可以动态地根据自己的情况调整对方的发送窗口大小。

网络层:IP 协议查询 MAC 地址

将数据段打包,并加入源及目标的 IP 地址,并且负责寻找传输路线。判断目标地址是否与当前地址处于同一网络中,是的话直接根据 Mac 地址发送,否则使用路由表查找下一跳地址,以及使用 ARP 协议查询它的 Mac 地址。

链路层:以太网协议

根据以太网协议将数据分为以“帧”为单位的数据包,每一帧分为两个部分:

  • 标头:数据包的发送者、接受者、数据类型
  • 数据:数据包具体内容

Mac 地址

以太网规定了连入网络的所有设备都必须具备“网卡”接口,数据包都是从一块网卡传递到另一块网卡,网卡的地址就是 Mac 地址。每一个 Mac 地址都是独一无二的,具备了一对一的能力。

三次握手

在传输层传输数据之前需要建立连接,也就是三次握手创建可靠连接。

三次握手

首先建立链接前需要 Server 端先监听端口,因此 Server 端建立链接前的初始状态就是 LISTEN 状态,这时 Client 端准备建立链接,先发送一个 SYN 同步包,发送完同步包后,Client 端的链接状态变成了 SYN_SENT 状态。Server 端收到 SYN 后,同意建立链接,会向 Client 端回复一个 ACK。

由于 TCP 是双工传输,Server 端也会同时向 Client 端发送一个 SYN,申请 Server 向 Client 方向建立链接。发送完 ACK 和 SYN 后,Server 端的链接状态就变成了 SYN_RCVD。

Client 收到 Server 的 ACK 后,Client 端的链接状态就变成了 ESTABLISHED 状态,同时,Client 向 Server 端发送 ACK,回复 Server 端的 SYN 请求。

Server 端收到 Client 端的 ACK 后,Server 端的链接状态也就变成了的 ESTABLISHED 状态,此时建连完成,双方随时可以进行数据传输。

在面试时需要明白三次握手是为了建立双向的链接,需要记住 Client 端和 Server 端的链接状态变化。另外回答建连的问题时,可以提到 SYN 洪水攻击发生的原因,就是 Server 端收到 Client 端的 SYN 请求后,发送了 ACK 和 SYN,但是 Client 端不进行回复,导致 Server 端大量的链接处在 SYN_RCVD 状态,进而影响其他正常请求的建连。可以设置 tcp_synack_retries = 0 加快半链接的回收速度,或者调大 tcp_max_syn_backlog 来应对少量的 SYN 洪水攻击

四次挥手

我们只要关注 80 端口与 13743 端口建立的连接断开过程,浏览器通过 13747 端口发送 [FIN, ACK] 这里是不是跟很多网上看到的不一样?

  1. 其实是客户端在发送 [FIN] 报文的时候顺带发了一个 [ACK] 确认上次传输确认。
  2. 接着服务端通过 80 端口响应了 [ACK] ,然后立马响应 [FIN, ACK] 表示数据传输完了,可以关闭连接。
  3. 最后浏览器通过 13743 端口 发送 [ACK] 包给服务端,客服端与服务端连接就关闭了。

具体流程如下图抓包所示:

四次挥手

三次握手与四次挥手

TCP 连接与断开

客户端:

  • SYN_SENT - 客户端发起第 1 次握手后,连接状态为 SYN_SENT ,等待服务端内核进行应答,如果服务端来不及处理(例如服务端的 backlog 队列已满)就可以看到这种状态的连接。
  • ESTABLISHED - 表示连接处于正常状态,可以进行数据传送。客户端收到服务器回复的 SYN+ACK 后,对服务端的 SYN 单独回复(第 3 次握手),连接建立完成,进入 ESTABLISHED 状态。服务端程序收到第 3 次握手包后,也进入 ESTABLISHED 状态。
  • FIN_WAIT_1 - 客户端发送了关闭连接的 FIN 报文后,等待服务端回复 ACK 确认。
  • FIN_WAIT_2 - 表示我方已关闭连接,正在等待服务端关闭。客户端发了关闭连接的 FIN 报文后,服务器发回 ACK 应答,但是没进行关闭,就会处于这种状态。
  • TIME_WAIT - 双方都正常关闭连接后,客户端会维持 TIME_WAIT 一段时间,以确保最后一个 ACK 能成功发送到服务器端。停留时长为 2 倍的 MSL (报文最大生存时间),Linux 下大约是 60 秒。所以在一个频繁建立短连接的服务器上通常可以看到成千上万的 TIME_WAIT 连接。

服务端:

  • LISTEN - 表示当前程序正在监听某个端口时。
  • SYN_RCVD - 服务端收到第 1 次握手后,进入 SYN_RCVD 状态,并回复一个 SYN+ACK(第 2 次握手),再等待对方确认。
  • ESTABLISHED - 表示连接处于正常状态,可以进行数据传送。完成 TCP3 次握手后,连接建立完成,进入 ESTABLISHED 状态。
  • CLOSE_WAIT - 表示客户端已经关闭连接,但是本地还没关闭,正在等待本地关闭。有时客户端程序已经退出了,但服务端程序由于异常或 BUG 没有调用 close()函数对连接进行关闭,那在服务器这个连接就会一直处于 CLOSE_WAIT 状态,而在客户机已经不存在这个连接了。
  • LAST_ACK - 表示正在等待客户端对服务端的关闭请求进行最终确认。

TIME_WAIT 状态存在的理由:

划重点了

  • 可靠地实现 TCP 全双工连接的终止 在进行关闭连接四路握手协议时,最后的 ACK 是由主动关闭端发出的,如果这个最终的 ACK 丢失,服务器将重发最终的 FIN,因此客户端必须维护状态信息允 许它重发最终的 ACK。如 果不维持这个状态信息,那么客户端将响应 RST 分节,服务器将此分节解释成一个错误( 在 java 中会抛出 connection reset 的 SocketException)。因而,要实现 TCP 全双工连接的正常终 止,必须处理终止序列四个分节中任何一个分节的丢失情况,主动关闭 的客户端必须维持状 态信息进入 TIME_WAIT 状态。
  • 允许老的重复分节在网络中消逝 TCP 分节可能由于路由器异常而“迷途”,在迷途期间,TCP 发送端可能因确认超时而重发这个 分节,迷途的分节在路由器修复后也会被送到最终目的地,这个 原来的迷途分节就称为 lost duplicate。在关闭一个 TCP 连接后,马上又重新建立起一个相同的 IP 地址和端口之间的 TCP 连接,后一个连接被称为前一个连接的化身 ( incarnation),那么有可能出现这种情况,前一 个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。为了避免 这个情 况,TCP 不允许处于 TIME_WAIT 状态的连接启动一个新的化身,因为 TIME_WAIT 状 态持续 2MSL,就可以保证当成功建立一个 TCP 连接的时 候,来自连接先前化身的重复分组已 经在网络中消逝

另外回答断链的问题时,可以提到实际应用中有可能遇到大量 Socket 处在 TIME_WAIT 或者 CLOSE_WAIT 状态的问题。一般开启 tcp_tw_reuse 和 tcp_tw_recycle 能够加快 TIME-WAIT 的 Sockets 回收;而大量 CLOSE_WAIT 可能是被动关闭的一方存在代码 bug,没有正确关闭链接导致的。

简单地说就是

  1. 保证 TCP 协议的全双工连接能够可靠关闭;
  2. 保证这次连接的重复数据段从网络中消失,防止端口被重用时可能产生数据混淆;

服务器处理请求并响应 HTTP 报文

深入分析下 HTTP 报文到底是什么玩意。数据传输都是通过 TCP/IP 协议负责底层的传输工作, HTTP 协议基本不用操心,所谓的 “超文本传输协议” 似乎不怎么例会 “传输” 这个事情,那 HTTP 的核心又是什么呢?

比图 TCP 报文,它在实际要传输的数据之前附加了一个 20 字节的头部数据,存储 TCP 协议必须的额外信息,例如发送方的端口号、接收方的端口号、包序号、标志位等等。

有了这个附加的 TCP 头,数据包才能够正确传输,到了目的地后把头部去掉,就可以拿到真正的数据。这个很容易理解,设置起点与终点,不同协议贴上不同的头部,到了对应目的地就拆下这个头部,提取真正的数据。

HTTP报文

与 TCP/UDP 类似需要在传输数据前设置一些请求头,不同的是 HTTP 是一个 “纯文本” 的协议,所有的头都是 ASCII 码的文本,很容易看出来是什么。

再者就是他的请求报文与响应报文的结构基本一样,主要三大部分组成:

  1. 起始行(Start Line):描述请求或者响应的基本信息。
  2. Header:使用 key-value 的形式详细说明报文信息。
  3. 空行。
  4. 消息正文(Entity):传输的数据,图片、视频、文本等都可以。

这其中前两部分起始行和头部字段经常又合称为“请求头”或“响应头”,消息正文又称为“实体”,但与“header”对应,很多时候就直接称为“body”。

敲黑板了

HTTP 协议规定报文必须包含 Header,而且之后必须有一个 “空行”,也就是“CRLF”,十六进制的“0D0A”,可以没有 “body”。

报文结构如下图所示:

HTTP报文

截取一段报文:

HTTP报文抓取

请求头-起始行

请求行由请求方法字段、URL 字段和 HTTP 协议版本字段 3 个字段组成,它们用空格分隔。例如,GET / HTTP/1.1。

HTTP 协议的请求方法有 GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT

GET 是请求方法, “/” 是请求的目标资源,“HTTP/1.1” 请求协议版本号。

GET / HTTP/1.1 翻译成文字大概就是:“hello,服务器,我要请求根目录下的默认文件使用的是 HTTP 1.1 协议版本”。

头部 Header

第二部分就是 Header,组成形式是 key:value,使用自定义头需要注意事项:

  1. header 字段不区分大小写,通常是首字母大写;
  2. 字段名不允许有空格,可以使用 “-”,不能使用 “_”;
  3. 字段名必须紧接着 “:”,不能有空格,但是 “:” 后面可以有空格。
  4. 字段名顺序没有意义;

浏览器接收响应并渲染数据

接收到响应文本 HTML,则开始执行浏览器渲染机制。

不同的浏览器渲染可能有所差异,但是基本按照以下步骤执行:

  1. 根据 HTML 解析 DOM 树;
  2. 根据 CSS 解析出 CSS 规则树;
  3. 结合 DOM 树与 CSS 规则树,生成渲染树;
  4. 根据生成的渲染树计算每个节点的信息;
  5. 根据节点信息绘制画面展示给用户。

浏览器渲染页面

推荐阅读

以下几篇文章阅读量与读者反馈都很好,推荐大家阅读:

如果觉得阅读后对你有帮助,希望多多分享、点赞与在看素质三连不做白嫖者。关注 【码哥字节】解锁更多硬核。

我的个人微信:MageByte1024

MageByte

查看原文

狂奔的蜗牛 赞了回答 · 2020-09-15

解决准备跳槽,应该如何准备面试,前端岗

年初刚跳槽。
广度和深度都有。楼上G_Koala_C已经说的挺全面了。
我说点儿简单的:
css会被问到,例如布局、某些css3特性等;
html会被问到,例如h5新特性等;
js会被问到,例如常用的数组操作、ES6新特性等;
以上可能是出某个具体的业务逻辑相关的题目的方式,来考察你的思路,答案不是唯一的,可以回答自己惯用的实现方法。

写在简历中的项目使用到的东西,都有可能被问到,例如vue会问生命周期甚至问底层原理设计理念等等;例如问echart的底层实现逻辑等。

主要问项目经验,例如浏览器兼容问题、pc和移动端的网页适配方案、性能优化、模块业务功能、安全性、跨域问题等,各个方面都会涉及吧。

关注 3 回答 2

狂奔的蜗牛 收藏了文章 · 2020-06-07

关于 Git 的 20 个面试题

翻译:疯狂的技术宅

原文:https://www.edureka.co/blog/i...


未经许可,禁止转载!
本文首发微信公众号:前端先锋
欢迎关注,每天都给你推送新鲜的前端技术文章


我在工作中很喜欢 Git 。 Git 在许多开发团队中扮演着重要的角色。

关于 Git 面试的第一个问题必须是:

Q1. Git和SVN有什么区别?

GitSVN
1. Git是一个分布式的版本控制工具1. SVN 是集中版本控制工具
2.它属于第3代版本控制工具2.它属于第2代版本控制工具
3.客户端可以在其本地系统上克隆整个存储库3.版本历史记录存储在服务器端存储库中
4.即使离线也可以提交4.只允许在线提交
5.Push/pull 操作更快5.Push/pull 操作较慢
6.工程可以用 commit 自动共享6.没有任何东西自动共享

Q2. 什么是Git?

我建议你先通过了解 git 的架构再来回答这个问题,如下图所示,试着解释一下这个图:

  • Git 是分布式版本控制系统(DVCS)。它可以跟踪文件的更改,并允许你恢复到任何特定版本的更改。
  • 与 SVN 等其他版本控制系统(VCS)相比,其分布式架构具有许多优势,一个主要优点是它不依赖于中央服务器来存储项目文件的所有版本。
  • 每个开发人员都可以“克隆”我在图中用“Local repository”标注的存储库的副本,并且在他的硬盘驱动器上具有项目的完整历史记录,因此当服务器中断时,你需要的所有恢复数据都在你队友的本地 Git 存储库中。
  • 还有一个中央云存储库,开发人员可以向其提交更改,并与其他团队成员进行共享,如图所示,所有协作者都在提交更改“远程存储库”。

clipboard.png

下一组 Git 面试问题将测试你使用 Git 的体验:

Q3. 在 Git 中提交的命令是什么?

答案非常简单。
用于写入提交的命令是 git commit -a

现在解释一下 -a 标志, 通过在命令行上加 -a 指示 git 提交已修改的所有被跟踪文件的新内容。还要提一下,如果你是第一次需要提交新文件,可以在在 git commit -a 之前先 git add <file>

Q4. 什么是 Git 中的“裸存储库”?

你应该说明 “工作目录” 和 “裸存储库” 之间的区别。

Git 中的 “裸” 存储库只包含版本控制信息而没有工作文件(没有工作树),并且它不包含特殊的 .git 子目录。相反,它直接在主目录本身包含 .git 子目录中的所有内容,其中工作目录包括:

  1. 一个 .git 子目录,其中包含你的仓库所有相关的 Git 修订历史记录。
  2. 工作树,或签出的项目文件的副本。

Q5. Git 是用什么语言编写的?

你需要说明使用它的原因,而不仅仅是说出语言的名称。我建议你这样回答:

Git使用 C 语言编写。 GIT 很快,C 语言通过减少运行时的开销来做到这一点。

Q6. 在Git中,你如何还原已经 push 并公开的提交?

There can be two answers to this question and make sure that you include both because any of the below options can be used depending on the situation: 1
这个问题可以有两个答案,你回答时也要保包含这两个答案,因为根据具体情况可以使用以下选项:

  • 删除或修复新提交中的错误文件,并将其推送到远程存储库。这是修复错误的最自然方式。对文件进行必要的修改后,将其提交到我将使用的远程存储库
git commit -m "commit message"
  • 创建一个新的提交,撤消在错误提交中所做的所有更改。可以使用命令:
git revert <name of bad commit>

Q7. git pull 和 git fetch 有什么区别?

git pull 命令从中央存储库中提取特定分支的新更改或提交,并更新本地存储库中的目标分支。

git fetch 也用于相同的目的,但它的工作方式略有不同。当你执行 git fetch 时,它会从所需的分支中提取所有新提交,并将其存储在本地存储库中的新分支中。如果要在目标分支中反映这些更改,必须在 git fetch 之后执行git merge。只有在对目标分支和获取的分支进行合并后才会更新目标分支。为了方便起见,请记住以下等式:

<center><h5>git pull = git fetch + git merge</h5></center>

Q8. git中的“staging area”或“index”是什么?

For this answer try to explain the below diagram as you can see:
可以通过下图进行解释:

在完成提交之前,可以在称为“staging area”或“index”的中间区域中对其进行格式化和审查。从图中可以看出,每个更改首先在暂存区域中进行验证,我将其称为“stage file”,然后将更改提交到存储库。

clipboard.png

Q9. 什么是 git stash?

首先应该解释 git stash 的必要性。

通常情况下,当你一直在处理项目的某一部分时,如果你想要在某个时候切换分支去处理其他事情,事情会处于混乱的状态。问题是,你不想把完成了一半的工作的提交,以便你以后就可以回到当前的工作。解决这个问题的答案是 git stash。

再解释什么是git stash。

stash 会将你的工作目录,即修改后的跟踪文件和暂存的更改保存在一堆未完成的更改中,你可以随时重新应用这些更改。

Q10. 什么是git stash drop?

通过说明我们使用 git stash drop 的目的来回答这个问题。

git stash drop 命令用于删除隐藏的项目。默认情况下,它将删除最后添加的存储项,如果提供参数的话,它还可以删除特定项。

下面举个例子。

如果要从隐藏项目列表中删除特定的存储项目,可以使用以下命令:

git stash list:它将显示隐藏项目列表,如:

stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert “added file_size”
stash@{2}: WIP on master: 21d80a5 added number to log

如果要删除名为 stash@{0} 的项目,请使用命令 git stash drop stash@{0}

Q11. 如何找到特定提交中已更改的文件列表?

对于这个问题,不能仅仅是提供命令,还要解释这个命令究竟做了些什么。

要获取特定提交中已更改的列表文件,请使用以下命令:

git diff-tree -r {hash}

给定提交哈希,这将列出在该提交中更改或添加的所有文件。 -r 标志使命令列出单个文件,而不是仅将它们折叠到根目录名称中。

你还可以包括下面提到的内容,虽然它是可选的,但有助于给面试官留下深刻印象。

输出还将包含一些额外信息,可以通过包含两个标志把它们轻松的屏蔽掉:

git diff-tree –no-commit-id –name-only -r {hash}

这里 -no-commit-id 将禁止提交哈希值出现在输出中,而 -name-only 只会打印文件名而不是它们的路径。

Q12. git config 的功能是什么?

首先说明为什么我们需要 git config

git 使用你的用户名将提交与身份相关联。 git config 命令可用来更改你的 git 配置,包括你的用户名。

下面用一个例子来解释。

假设你要提供用户名和电子邮件 ID 用来将提交与身份相关联,以便你可以知道是谁进行了特定提交。为此,我将使用:

git config –global user.name "Your Name": 此命令将添加用户名。

git config –global user.email "Your E-mail Address": 此命令将添加电子邮件ID。

Q13. 提交对象包含什么?

Commit 对象包含以下组件,你应该提到以下这三点:

  • 一组文件,表示给定时间点的项目状态
  • 引用父提交对象
  • SHAI 名称,一个40个字符的字符串,提交对象的唯一标识。

Q14. 如何在Git中创建存储库?

这可能是最常见的问题,答案很简单。

要创建存储库,先为项目创建一个目录(如果该目录不存在),然后运行命令 git init。通过运行此命令,将在项目的目录中创建 .git 目录。

Q15. 怎样将 N 次提交压缩成一次提交?

将N个提交压缩到单个提交中有两种方式:

  • 如果要从头开始编写新的提交消息,请使用以下命令:
git reset –soft HEAD~N &&
git commit
  • 如果你想在新的提交消息中串联现有的提交消息,那么需要提取这些消息并将它们传给 git commit,可以这样:
git reset –soft HEAD~N &&
git commit –edit -m"$(git log –format=%B –reverse .HEAD@{N})"

Q16. 什么是 Git bisect?如何使用它来确定(回归)错误的来源?

我建议你先给出一个Git bisect 的小定义。

Git bisect 用于查找使用二进制搜索引入错误的提交。 Git bisect的命令是

git bisect <subcommand> <options>

既然你已经提到过上面的命令,那就解释一下这个命令会做什么。

此命令用了二进制搜索算法来查找项目历史记录中的哪个提交引入了错误。你可以通过告诉它已知包含该错误的“错误”提交以及在引入错误之前已知的“良好”提交来使用它。然后 git bisect 在这两个端点之间选择一个提交,并询问你所选的提交是“好”还是“坏”。它继续缩小范围,直到找到引入更改的确切提交。

Q17. 如果想要在提交之前运行代码性检查工具,并在测试失败时阻止提交,该怎样配置 Git 存储库?

我建议你先介绍一下完整性检查。

完整性或冒烟测试用来确定继续测试是否可行和合理。

下面解释如何实现这一目标。

这可以通过与存储库的 pre-commit hook 相关的简单脚本来完成。git 会在提交之前触发 pre-commit hook。你可以在这个脚本中运行其他工具,例如 linters,并对提交到存储库中的更改执行完整性检查。

最后举个例子,你可以参考下面的脚本:

#!/bin/sh
files=$(git diff –cached –name-only –diff-filter=ACM | grep ‘.go$’)
if [ -z files ]; then
    exit 0
fi
unfmtd=$(gofmt -l $files)
if [ -z unfmtd ]; then
    exit 0
fi
echo “Some .go files are not fmt’d”
exit 1

这段脚本检查是否需要通过标准 Go 源代码格式化工具 gofmt 传递所有即将提交的 .go 文件。如果脚步以非 0 状态退出,脚本会有效地阻止提交操作。

Q18. 描述一下你所使用的分支策略?

这个问题被要求用Git来测试你的分支经验,告诉他们你在以前的工作中如何使用分支以及它的用途是什么,你可以参考以下提到的要点:

  • 功能分支(Feature branching)

    要素分支模型将特定要素的所有更改保留在分支内。当通过自动化测试对功能进行全面测试和验证时,该分支将合并到主服务器中。

  • 任务分支(Task branching)

    在此模型中,每个任务都在其自己的分支上实现,任务键包含在分支名称中。很容易看出哪个代码实现了哪个任务,只需在分支名称中查找任务键。

  • 发布分支(Release branching)

    一旦开发分支获得了足够的发布功能,你就可以克隆该分支来形成发布分支。创建该分支将会启动下一个发布周期,所以在此之后不能再添加任何新功能,只有错误修复,文档生成和其他面向发布的任务应该包含在此分支中。一旦准备好发布,该版本将合并到主服务器并标记版本号。此外,它还应该再将自发布以来已经取得的进展合并回开发分支。

最后告诉他们分支策略因团队而异,所以我知道基本的分支操作,如删除、合并、检查分支等。

Q19. 如果分支是否已合并为master,你可以通过什么手段知道?

答案很直接。

要知道某个分支是否已合并为master,你可以使用以下命令:

git branch –merged 它列出了已合并到当前分支的分支。

git branch –no-merged 它列出了尚未合并的分支。

Q20. 什么是SubGit?

SubGit 是将 SVN 到 Git迁移的工具。它创建了一个可写的本地或远程 Subversion 存储库的 Git 镜像,并且只要你愿意,可以随意使用 Subversion 和 Git。

这样做有很多优点,比如你可以从 Subversion 快速一次性导入到 Git 或者在 Atlassian Bitbucket Server 中使用SubGit。我们可以用 SubGit 创建现有 Subversion 存储库的双向 Git-SVN 镜像。你可以在方便时 push 到 Git 或提交 Subversion。同步由 SubGit 完成。


本文首发微信公众号:前端先锋

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章

欢迎扫描二维码关注公众号,每天都给你推送新鲜的前端技术文章


欢迎继续阅读本专栏其它高赞文章:


查看原文

狂奔的蜗牛 收藏了文章 · 2020-06-04

2020年这些🍔"正则"应该被收藏

来源: https://github.com/any86/any-...

火车车次

/^[GCDZTSPKXLY1-9]\d{1,4}$/

手机机身码(IMEI)

/^\d{15,17}$/

必须带端口号的网址(或ip)

/^((ht|f)tps?:\/\/)?[\w-]+(\.[\w-]+)+:\d{1,5}\/?$/

网址(url,支持端口和"?+参数"和"#+参数)

/^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])?$/

统一社会信用代码

/^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$/

迅雷链接

/^thunderx?:\/\/[a-zA-Z\d]+=$/

ed2k链接(宽松匹配)

/^ed2k:\/\/\|file\|.+\|\/$/

磁力链接(宽松匹配)

/^magnet:\?xt=urn:btih:[0-9a-fA-F]{40,}.*$/

子网掩码

/^(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(?:\.(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])){3}$/

linux"隐藏文件"路径

/^\/(?:[^\/]+\/)*\.[^\/]*/

linux文件夹路径

/^\/(?:[^\/]+\/)*$/

linux文件路径

/^\/(?:[^\/]+\/)*[^\/]+$/

window"文件夹"路径

/^[a-zA-Z]:\\(?:\w+\\?)*$/

window下"文件"路径

/^[a-zA-Z]:\\(?:\w+\\)*\w+\.\w+$/

股票代码(A股)

/^(s[hz]|S[HZ])(000[\d]{3}|002[\d]{3}|300[\d]{3}|600[\d]{3}|60[\d]{4})$/

大于等于0, 小于等于150, 支持小数位出现5, 如145.5, 用于判断考卷分数

/^150$|^(?:\d|[1-9]\d|1[0-4]\d)(?:.5)?$/

html注释

/^<!--[\s\S]*?-->$/

md5格式(32位)

/^([a-f\d]{32}|[A-F\d]{32})$/

版本号(version)格式必须为X.Y.Z

/^\d+(?:\.\d+){2}$/

视频(video)链接地址(视频格式可按需增删)

/^https?:\/\/(.+\/)+.+(\.(swf|avi|flv|mpg|rm|mov|wav|asf|3gp|mkv|rmvb|mp4))$/i

图片(image)链接地址(图片格式可按需增删)

/^https?:\/\/(.+\/)+.+(\.(gif|png|jpg|jpeg|webp|svg|psd|bmp|tif))$/i

24小时制时间(HH:mm:ss)

/^(?:[01]\d|2[0-3]):[0-5]\d:[0-5]\d$/

12小时制时间(hh:mm:ss)

/^(?:1[0-2]|0?[1-9]):[0-5]\d:[0-5]\d$/

base64格式

/^\s*data:(?:[a-z]+\/[a-z0-9-+.]+(?:;[a-z-]+=[a-z0-9-]+)?)?(?:;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*?)\s*$/i

数字/货币金额(支持负数、千分位分隔符)

/^-?\d+(,\d{3})*(\.\d{1,2})?$/

数字/货币金额 (只支持正数、不支持校验千分位分隔符)

/(?:^[1-9]([0-9]+)?(?:\.[0-9]{1,2})?$)|(?:^(?:0){1}$)|(?:^[0-9]\.[0-9](?:[0-9])?$)/

银行卡号(10到30位, 覆盖对公/私账户, 参考微信支付

/^[1-9]\d{9,29}$/

中文姓名

/^(?:[\u4e00-\u9fa5·]{2,16})$/

英文姓名

/(^[a-zA-Z]{1}[a-zA-Z\s]{0,20}[a-zA-Z]{1}$)/

车牌号(新能源)

/[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}(([0-9]{5}[DF])|([DF][A-HJ-NP-Z0-9][0-9]{4}))$/

车牌号(非新能源)

/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/

车牌号(新能源+非新能源)

/^(?:[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-HJ-NP-Z]{1}(?:(?:[0-9]{5}[DF])|(?:[DF](?:[A-HJ-NP-Z0-9])[0-9]{4})))|(?:[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领 A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9 挂学警港澳]{1})$/

手机号(mobile phone)中国(严谨), 根据工信部2019年最新公布的手机号段

/^(?:(?:\+|00)86)?1(?:(?:3[\d])|(?:4[5-7|9])|(?:5[0-3|5-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\d])|(?:9[1|8|9]))\d{8}$/

手机号(mobile phone)中国(宽松), 只要是13,14,15,16,17,18,19开头即可

/^(?:(?:\+|00)86)?1[3-9]\d{9}$/

手机号(mobile phone)中国(最宽松), 只要是1开头即可, 如果你的手机号是用来接收短信, 优先建议选择这一条

/^(?:(?:\+|00)86)?1\d{10}$/

date(日期)

/^\d{4}(-)(1[0-2]|0?\d)\1([0-2]\d|\d|30|31)$/

email(邮箱)

/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/

座机(tel phone)电话(国内),如: 0341-86091234

/^\d{3}-\d{8}$|^\d{4}-\d{7}$/

身份证号(1代,15位数字)

/^[1-9]\d{7}(?:0\d|10|11|12)(?:0[1-9]|[1-2][\d]|30|31)\d{3}$/

身份证号(2代,18位数字),最后一位是校验位,可能为数字或字符X

/^[1-9]\d{5}(?:18|19|20)\d{2}(?:0\d|10|11|12)(?:0[1-9]|[1-2]\d|30|31)\d{3}[\dXx]$/

身份证号, 支持1/2代(15位/18位数字)

/(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/

护照(包含香港、澳门)

/(^[EeKkGgDdSsPpHh]\d{8}$)|(^(([Ee][a-fA-F])|([DdSsPp][Ee])|([Kk][Jj])|([Mm][Aa])|(1[45]))\d{7}$)/

帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线组合

/^[a-zA-Z]\w{4,15}$/

中文/汉字

/^(?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])+$/

小数

/^\d+\.\d+$/

数字

/^\d{1,}$/

html标签(宽松匹配)

/<(\w+)[^>]*>(.*?<\/\1>)?/

qq号格式正确

/^[1-9][0-9]{4,10}$/

数字和字母组成

/^[A-Za-z0-9]+$/

英文字母

/^[a-zA-Z]+$/

小写英文字母组成

/^[a-z]+$/

大写英文字母

/^[A-Z]+$/

密码强度校验,最少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符

/^\S*(?=\S{6,})(?=\S*\d)(?=\S*[A-Z])(?=\S*[a-z])(?=\S*[!@#$%^&*? ])\S*$/

用户名校验,4到16位(字母,数字,下划线,减号)

/^[a-zA-Z0-9_-]{4,16}$/

ip-v4

/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/

ip-v6

/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i

16进制颜色

/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/

微信号(wx),6至20位,以字母开头,字母,数字,减号,下划线

/^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/

邮政编码(中国)

/^(0[1-7]|1[0-356]|2[0-7]|3[0-6]|4[0-7]|5[1-7]|6[1-7]|7[0-5]|8[013-6])\d{4}$/

中文和数字

/^((?:[\u3400-\u4DB5\u4E00-\u9FEA\uFA0E\uFA0F\uFA11\uFA13\uFA14\uFA1F\uFA21\uFA23\uFA24\uFA27-\uFA29]|[\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0])|(\d))+$/

不能包含字母

/^[^A-Za-z]*$/

java包名

/^([a-zA-Z_][a-zA-Z0-9_]*)+([.][a-zA-Z_][a-zA-Z0-9_]*)+$/

mac地址

/^((([a-f0-9]{2}:){5})|(([a-f0-9]{2}-){5}))[a-f0-9]{2}$/i

vscode插件 🍭

vscode商店搜索any-rule
image.png

预览图

web版本 🚀

如果你是不用vscode也没关系, 访问下方网址即可.
https://any86.github.io/any-rule/

微信群

感谢大家的阅读, 如有疑问可以进群.
image.png

如果群已满, 我拉你进入微信群(由于腾讯对微信群的200人限制, 超过200人后必须由群成员拉入)

查看原文

狂奔的蜗牛 关注了用户 · 2020-05-19

北堂棣 @ssj_8551

2016年应届毕业生。
转眼毕业都这么多年了。。。
大学时光仿若昨夕,岁月匆匆催人老。。。。

关注 77

狂奔的蜗牛 收藏了文章 · 2020-05-17

半小时搞懂 HTTP、HTTPS和HTTP2

本文将尽量用通俗易懂的方式来向读者讲述 HTTP 的知识。

另外,建议在学习 HTTP 知识的时候,利用 Chrome 开发者工具来做实践,这可以帮助你理解得更深刻。

在这里插入图片描述

(此图在网上找来的,侵删)

HTTP 概述

HTTP 超文本传输​​协议是位于 TCP/IP 体系结构中的应用层协议,它是万维网数据通信的基础。

当我们访问一个网站时,需要通过统一资源定位符(uniform resource locator,URL)来定位服务器并获取资源。

<协议>://<域名>:<端口>/<路径>

一个 URL 的一般形式通常如上所示(http://test.com/index.html ),现在最常用的协议就是 HTTP,HTTP 的默认端口是 80,通常可以省略。

在这里插入图片描述

HTTP/1.1

HTTP/1.1 是目前使用最广泛的版本,一般没有特别标明版本都是指 HTTP/1.1。

HTTP 连接建立过程

我们来看一下在浏览器输入 URL 后获取 HTML 页面的过程。

  1. 先通过域名系统(Domain Name System,DNS)查询将域名转换为 IP 地址。即将 test.com 转换为 221.239.100.30 这一过程。
  2. 通过三次握手(稍后会讲)建立 TCP 连接。
  3. 发起 HTTP 请求。
  4. 目标服务器接收到 HTTP 请求并处理。
  5. 目标服务器往浏览器发回 HTTP 响应。
  6. 浏览器解析并渲染页面。

下图中的 RTT 为往返时延(Round-Trip Time: 往返时延。表示从发送端发送数据开始,到发送端收到来自接收端的确认,总共经历的时延)。

在这里插入图片描述

HTTP 连接拆除过程

所有 HTTP 客户端(浏览器)、服务器都可在任意时刻关闭 TCP 连接。通常会在一条报文结束时关闭连接,但出错的时候,也可能在首部行的中间或其他任意位置关闭连接。

TCP 三次握手和四次挥手

由于 HTTP 是基于 TCP 的,所以打算在这补充一下 TCP 连接建立和拆除的过程。

首先,我们需要了解一些 TCP 报文段的字段和标志位:

  1. 32 比特的序号字段和确认号字段,TCP 字节流每一个字节都按顺序编号。确认号是接收方期望从对方收到的下一字节的序号。
  2. ACK 标志位,用于指示确认字段中的值是有效的 ACK=1 有效,ACK=0 无效。
  3. SYN 标志位,用于连接建立,SYN 为 1 时,表明这是一个请求建立连接报文。
  4. FIN 标志位,用于连接拆除,FIN 为 1 时,表明发送方数据已发送完毕,并要求释放连接。

在这里插入图片描述

TCP 三次握手建立连接

TCP 标准规定,ACK 报文段可以携带数据,但不携带数据就不用消耗序号。

  1. 客户端发送一个不包含应用层数据的 TCP 报文段,首部的 SYN 置为 1,随机选择一个初始序号(一般为 0)放在 TCP 报文段的序号字段中。(SYN 为 1 的时候,不能携带数据,但要消耗掉一个序号)
  2. TCP 报文段到达服务器主机后,服务器提取报文段,并为该 TCP 连接分配缓存和变量。然后向客户端发送允许连接的 ACK 报文段(不包含应用层数据)。这个报文段的首部包含 4 个信息:ACK 置 为 1,SYN 置为 1;确认号字段置为客户端的序号 + 1;随机选择自己的初始序号(一般为 0)。
  3. 收到服务器的 TCP 响应报文段后,客户端也要为该 TCP 连接分配缓存和变量,并向服务器发送一个 ACK 报文段。这个报文段将服务器端的序号 + 1 放置在确认号字段中,用来对服务器允许连接的报文段进行响应,因为连接已经建立,所以 SYN 置为 0。最后一个阶段,报文段可以携带客户到服务器的数据。并且以后的每一个报文段,SYN 都置为 0。

下图是一个具体的示例:

在这里插入图片描述

(此截图是我使用 Wireshark 抓包工具截取的 TCP 报文段截图)。

TCP 四次挥手拆除连接

FIN 报文段即使不携带数据,也要消耗序号。

  1. 客户端发送一个 FIN 置为 1 的报文段。
  2. 服务器回送一个确认报文段。
  3. 服务器发送 FIN 置为 1 的报文段。
  4. 客户端回送一个确认报文段。

TCP 为什么是四次挥手,而不是三次?

  1. 当 A 给 B 发送 FIN 报文时,代表 A 不再发送报文,但仍可以接收报文。
  2. B 可能还有数据需要发送,因此先发送 ACK 报文,告知 A “我知道你想断开连接的请求了”。这样 A 便不会因为没有收到应答而继续发送断开连接的请求(即 FIN 报文)。
  3. B 在处理完数据后,就向 A 发送一个 FIN 报文,然后进入 LAST_ACK 阶段(超时等待)。
  4. A 向 B 发送 ACK 报文,双方都断开连接。

参考资料:

HTTP 报文格式

HTTP 报文由请求行、首部、实体主体组成,它们之间由 CRLF(回车换行符) 分隔开。

注意:实体包括首部(也称为实体首部)和实体主体,sp 即是空格 space

在这里插入图片描述

请求行和首部是由 ASCII 文本组成的,实体主体是可选的,可以为空也可以是任意二进制数据。

请求报文和响应报文的格式基本相同。

请求报文格式

<method> <request-URL> <version>
<headers>
<entity-body>

响应报文格式

<version> <status> <reason-phrase>
<headers>
<entity-body>

一个请求或响应报文由以下字段组成

  1. 请求方法,客户端希望服务器对资源执行的动作。
  2. 请求 URL,命名了所请求的资源。
  3. 协议版本,报文所使用的 HTTP 版本。
  4. 状态码,这三位数字描述了请求过程中所发生的情况。
  5. 原因短语,数字状态码的可读版本(例如上面的响应示例跟在 200 后面的 OK,一般按规范写最好)。
  6. 首部,可以有零或多个首部。
  7. 实体的主体部分,可以为空也可以包含任意二进制数据。

一个 HTTP 请求示例

GET /2.app.js HTTP/1.1
Host: 118.190.217.8:3389
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36
Accept: */*
Referer: http://118.190.217.8:3389/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

一个 HTTP 响应示例

HTTP/1.1 200 OK
X-Powered-By: Express
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Sat, 07 Mar 2020 03:52:30 GMT
ETag: W/"253e-170b31f7de7"
Content-Type: application/javascript; charset=UTF-8
Vary: Accept-Encoding
Content-Encoding: gzip
Date: Fri, 15 May 2020 05:38:05 GMT
Connection: keep-alive
Transfer-Encoding: chunked

方法

方法描述
GET从服务器获取一份文档
HEAD只从服务器获取文档的头部
POST向服务器发送需要处理的数据
PUT将请求的数据部分存储在服务器上
TRACE对可能经过代理服务器传送到服务器上去的报文进行追踪
OPTIONS决定可以在服务器上执行哪些方法
DELETE从服务器上删除一份文档
GET 和 HEAD

其中 GET 和 HEAD 被称为安全方法,因为它们是幂等的(如果一个请求不管执行多少次,其结果都是一样的,这个请求就是幂等的),类似于 POST 就不是幂等的。

HEAD 方法和 GET 方法很类似,但服务器在响应中只返回首部。这就允许客户端在未获取实际资源的情况下,对资源的首部进行检查。使用 HEAD,可以:

  1. 在不获取资源的情况下了解资源的情况。
  2. 通过查看响应状态码,看看某个对象是否存在。
  3. 通过查看首部,了解测试资源是否被修改了。

服务器开发者必须确保返回的首部与 GET 请求所返回的首部完全相同。遵循 HTTP/1.1 规范,就必须实现 HEAD 方法。

PUT

与 GET 方法从服务器读取文档相反,PUT 方法会向服务器写入文档。PUT 方法的语义就是让服务器用请求的主体部分来创建一个由所请求的 URL 命名的新文档。 如果那个文档已存在,就覆盖它。

POST

POST 方法通常用来向服务器发送表单数据。

TRACE

客户端发起一个请求时,这个请求可能要穿过路由器、防火墙、代理、网关等。每个中间节点都可能会修改原始的 HTTP 请求,TRACE 方法允许客户端在最终发起请求时,看看它变成了什么样子。

TRACE 请求会在目的服务器端发起一个“环回”诊断。行程最后一站的服务器会弹回一条 TRACE 响应,并在响应主体中携带它收到的原始请求报文。 这样客户端就可以查看在所有中间 HTTP 应用程序组成的请求/响应链上,原始报文是否被毁坏或修改过。

在这里插入图片描述

TRACE 方法主要用于诊断,用于验证请求是否如愿穿过了请求/响应链。它也是一种工具,用来查看代理和其他应用程序对用户请求所产生的效果。 TRACE 请求中不能带有实体的主体部分。TRACE 响应的实体主体部分包含了响应服务器收到的请求的精确副本。

OPTIONS

OPTIONS 方法请求 Web 服务器告知其支持的各种功能。

DELETE

DELETE 方法就是让服务器删除请求 URL 所指定的资源。

状态码

整体范围已定义范围分类
100~199100~101信息提示
200~299200~206成功
300~399300~305重定向
400~499400~415客户端错误
500~599500~505服务器错误
300~399 重定向状态码

重定向状态码要么告诉客户端使用替代位置来访问他们感兴趣的资源,要么提供一个替代的响应而不是资源的内容。 如果资源已被移动,可以发送一个重定向状态码和一个可选的 Location 首部来告知客户端资源已被移走,以及现在在哪里可以找到它。这样,浏览器可以在不打扰使用者的情况下,透明地转入新的位置。

400~499 客户端错误状态码

有时客户端会发送一些服务器无法处理的东西,例如格式错误的请求报文、一个不存在的 URL。

500~599 服务器错误状态码

有时客户端发送了一条有效请求,服务器自身却出错了。

首部

首部和方法共同配合工作,决定了客户端和服务器能做什么事情。

首部分类

  1. 通用首部,可以出现在请求或响应报文中。
  2. 请求首部,提供更多有关请求的信息。
  3. 响应首部,提供更多有关响应的信息。
  4. 实体首部,描述主体的长度和内容,或者资源自身。
  5. 扩展首部,规范中没有定义的新首部。
通用首部

有些首部提供了与报文相关的最基本信息,它们被称为通用首部。以下是一些常见的通用首部:

在这里插入图片描述

请求首部

请求首部是只在请求报文中有意义的首部,用于说明请求的详情。以下是一些常见的请求首部:

在这里插入图片描述

响应首部

响应首部让服务器为客户端提供了一些额外的信息。

实体首部

实体首部提供了有关实体及其内容的大量信息,从有关对象类型的信息,到能够对资源使用的各种有效的请求方法。

例如内容首部,提供了与实体内容有关的特定信息,说明了其类型、尺寸以及处理它所需的其他有用信息。
另外,通用的缓存首部说明了如何或什么时候进行缓存。实体的缓存首部提供了与被缓存实体有关的信息。
在这里插入图片描述

性能优化

1. 减少 HTTP 请求

每发起一个 HTTP 请求,都得经历三次握手建立 TCP 连接,如果连接只用来交换少量数据,这个过程就会严重降低 HTTP 性能。所以我们可以将多个小文件合成一个大文件,从而减少 HTTP 请求次数。

其实由于持久连接(重用 TCP 连接,以消除连接及关闭时延;HTTP/1.1 默认开启持久连接)的存在,每个新请求不一定都需要建立一个新的 TCP 连接。但是,浏览器处理完一个 HTTP 请求才能发起下一个,所以在 TCP 连接数没达到浏览器规定的上限时,还是会建立新的 TCP 连接。从这点来看,减少 HTTP 请求仍然是有必要的。

2. 静态资源使用 CDN

内容分发网络(CDN)是一组分布在多个不同地理位置的 Web 服务器。我们都知道,当服务器离用户越远时,延迟越高。CDN 就是为了解决这一问题,在多个位置部署服务器,让用户离服务器更近,从而缩短请求时间。

3. 善用缓存

为了避免用户每次访问网站都得请求文件,我们可以通过添加 Expires 头来控制这一行为。Expires 设置了一个时间,只要在这个时间之前,浏览器都不会请求文件,而是直接使用缓存。

不过这样会产生一个问题,当文件更新了怎么办?怎么通知浏览器重新请求文件?

可以通过更新页面中引用的资源链接地址,让浏览器主动放弃缓存,加载新资源。

具体做法是把资源地址 URL 的修改与文件内容关联起来,也就是说,只有文件内容变化,才会导致相应 URL 的变更,从而实现文件级别的精确缓存控制。什么东西与文件内容相关呢?我们会很自然的联想到利用数据摘要要算法对文件求摘要信息,摘要信息与文件内容一一对应,就有了一种可以精确到单个文件粒度的缓存控制依据了。

参考资料:

4. 压缩文件

压缩文件可以减少文件下载时间,让用户体验性更好。

gzip 是目前最流行和最有效的压缩方法。可以通过向 HTTP 请求头中的 Accept-Encoding 头添加 gzip 标识来开启这一功能。当然,服务器也得支持这一功能。

举个例子,我用 Vue 开发的项目构建后生成的 app.js 文件大小为 1.4MB,使用 gzip 压缩后只有 573KB,体积减少了将近 60%。

5. 通过 max-age 和 no-cache 实现文件精确缓存

通用消息头部 Cache-Control 其中有两个选项:

  1. max-age: 设置缓存存储的最大周期,超过这个时间缓存被认为过期(单位秒)。在这个时间前,浏览器读取文件不会发出新请求,而是直接使用缓存。
  2. no-cache: 指定 no-cache 表示客户端可以缓存资源,每次使用缓存资源前都必须重新验证其有效性。

我们可以将那些长期不变的静态资源设置一个非常长的缓存时间,例如设置成缓存一年。

然后将 index.html 文件设置成 no-cache。这样每次访问网站时,浏览器都会询问 index.html 是否有更新,如果没有,就使用旧的 index.html 文件。如果有更新,就读取新的 index.html 文件。当加载新的 index.html 时,也会去加载里面新的 URL 资源。

例如 index.html 原来引用了 a.jsb.js,现在更新了变成 a.jsc.js。那就只会加载 c.js 文件。

具体请看 webpack + express 实现文件精确缓存

HTTPS

HTTPS 是最流行的 HTTP 安全形式,由网景公司首创,所有主要的浏览器和服务器都支持此协议。 使用 HTTPS 时,所有的 HTTP 请求和响应数据在发送之前,都要进行加密。加密可以使用 SSL 或 TLS。
在这里插入图片描述
SSL/TLS 协议作用在 HTTP 协议之下,对于上层应用来说,原来的发送/接收数据流程不变,这就很好地兼容了老的 HTTP 协议。由于 SSL/TLS 差别不大,下面统一使用 SSL。

要想了解 HTTPS 为何安全,还得继续了解一下这些概念:加密算法摘要算法数字签名数字证书

加密算法

对称密钥密码体制

对称密钥密码体制,即加密密钥和解密密钥是使用相同的密码体制。对称密钥加密技术的缺点之一就是发送者和接收者在对话之前,一定要有一个共享的密钥,所以不太安全。

公钥密码体制

公钥密码体制使用不同的加密密钥与解密密钥。公钥密码体制产生的主要原因有两个:一是对称密钥密码体制的密钥分配问题,二是对数字签名的需求。

在公钥密码体制中,加密密钥是公开的,解密密钥是需要保密的,加密算法和解密算法也是公开的。

公钥密码体制的加密和解密有如下特点:

  1. 密钥对产生器产生出接收者 B 的一对密钥,即加密密钥 PK 和解密密钥 SK。
  2. 发送者 A 用 B 的公钥 PK 作为加密密钥来加密信息,B 接收后用解密密钥 SK 解密。

![\[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uWP5p7So-1589532545350)(../imgs/is3.png)\]](https://img-blog.csdnimg.cn/2...

使用对称密钥时,由于双方使用同样的密钥,因此在通信信道上可以进行一对一的双向保密通信,双方都可以用同一个密钥加密解密。

使用公开密钥时,在通信信道上可以是多对一的单向保密信道。即可以有多人持有 B 的公钥,但只有 B 才能解密。

摘要算法

摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密,目前可以被解密逆向的只有CRC32算法,只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。

数字签名

用加密系统对报文进行签名,以说明是谁编写的报文,同时证明报文未被篡改过,这种技术称为数字签名

数字签名是附加在报文上的特殊加密校验码。使用数字签名的好处有:

  1. 签名可以证明是作者编写了这条报文。只有作者才会有最机密的私有密钥,因此,只有作者才能计算出这些校验和。
  2. 签名可以防止报文被篡改,如果有人在报文传输过程中对其进行了修改,校验和就不再匹配了。

数字签名通常是用非对称公开密钥技术产生的。

在这里插入图片描述

看上图,任何人都能用 A 的公钥 PK 对密文进行 E 运算后得到 A 发送的明文。可见这种通信并非为了保密,而是为了进行签名和核实签名,即确认此信息是 A 发送的(使用 A 的密钥进行加密的报文,只有使用 A 的公钥才能正确解密)。 但上述过程仅对报文进行了签名,对报文 X 本身却未保密,所以要采用下图的方法,同时实现秘密通信和数字签名。

在这里插入图片描述

数字证书

假如你想访问一个网站,怎么确保对方给你的公钥是你想访问的网站的公钥,而不是被中间人篡改过的?

数字证书的出现就是为了解决这个问题,它是由数字证书认证机构颁发的,用来证明公钥拥有者的身份。换句话说,数字证书的作用就相当于人的身份证,身份证证明了张三就是张三,而不是别人。

数字证书一般包含以下内容

  1. 对象的名称(人、服务器、组织等);
  2. 过期时间;
  3. 证书发布者(由谁为证书担保);
  4. 来自证书发布者的数字签名;
  5. 对象的公钥;
  6. 对象和所用签名算法的描述性信息。

任何人都可以创建一个数字证书,但由谁来担保才是重点。

数字证书的数字签名计算过程

  1. 用摘要算法对数字证书的内容计算出摘要;
  2. 用数字证书的私钥对摘要进行加密得到数字签名。

在这里插入图片描述

当浏览器收到证书时,会对签名颁发机构进行验证,如果颁发机构是个很有权威的公共签名机构,浏览器可能就知道其公开密钥了(浏览器会预装很多签名颁发机构的证书)。如果对签名颁发机构一无所知,浏览器通常会向用户显示一个对话框,看看他是否相信这个签名发布者。

因为数字证书的公钥是公开的,任何人都可以用公钥解密出数字证书的数字签名的摘要,然后再用同样的摘要算法对证书内容进行摘要计算,将得出的摘要和解密后的摘要作对比,如果内容一致则说明这个证书没有被篡改过,可以信任。

这个过程是建立在被大家所认可的证书机构之上得到的公钥,所以这是一种安全的方式。

在这里插入图片描述

HTTPS 连接建立过程

HTTPS 连接建立过程和 HTTP 差不多,区别在于 HTTP(默认端口 80) 请求只要在 TCP 连接建立后就可以发起,而 HTTPS(默认端口 443) 在 TCP 连接建立后,还需要经历 SSL 协议握手,成功后才能发起请求。

在这里插入图片描述

在这里插入图片描述

我知道肯定会有人不满足于简化版的 SSL 握手过程,所以我找了一篇文章SSL/TLS 握手过程详解,这篇文章非常详细的讲解了 SSL 握手的每一步骤。建议有兴趣的同学看一看。

HTTP/2

HTTP/2 是 HTTP/1.x 的扩展,而非替代。所以 HTTP 的语义不变,提供的功能不变,HTTP 方法、状态码、URL 和首部字段等这些核心概念也不变。

之所以要递增一个大版本到 2.0,主要是因为它改变了客户端与服务器之间交换数据的方式。HTTP 2.0 增加了新的二进制分帧数据层,而这一层并不兼容之前的 HTTP 1.x 服务器及客户端——是谓 2.0。

HTTP/2 连接建立过程

现在的主流浏览器 HTTP/2 的实现都是基于 SSL/TLS 的,也就是说使用 HTTP/2 的网站都是 HTTPS 协议的,所以本文只讨论基于 SSL/TLS 的 HTTP/2 连接建立过程。

基于 SSL/TLS 的 HTTP/2 连接建立过程和 HTTPS 差不多。在 SSL/TLS 握手协商过程中,客户端在 ClientHello 消息中设置 ALPN(应用层协议协商)扩展来表明期望使用 HTTP/2 协议,服务器用同样的方式回复。通过这种方式,HTTP/2 在 SSL/TLS 握手协商过程中就建立起来了。

HTTP/1.1 的问题

1. 队头阻塞

在 HTTP 请求应答过程中,如果出现了某种情况,导致响应一直未能完成,那后面所有的请求就会一直阻塞着,这种情况叫队头阻塞。

2. 低效的 TCP 利用

由于 TCP 慢启动机制,导致每个 TCP 连接在一开始的时候传输速率都不高,在处理多个请求后,才会慢慢达到“合适”的速率。对于请求数据量很小的 HTTP 请求来说,这种情况就是种灾难。

3. 臃肿的消息首部

HTTP/1.1 的首部无法压缩,再加上 cookie 的存在,经常会出现首部大小比请求数据大小还大的情况。

4. 受限的优先级设置

HTTP/1.1 无法为重要的资源指定优先级,每个 HTTP 请求都是一视同仁。

在继续讨论 HTTP/2 的新功能之前,先把 HTTP/1.1 的问题列出来是有意义的。因为 HTTP/2 的某些新功能就是为了解决上述某些问题而产生的。

二进制分帧层

HTTP/2 是基于帧的协议。采用分帧是为了将重要信息封装起来,让协议的解析方可以轻松阅读、解析并还原信息。

而 HTTP/1.1 是以文本分隔的。解析 HTTP/1.1 不需要什么高科技,但往往速度慢且容易出错。你需要不断地读入字节,直到遇到分隔符 CRLF 为止,同时还要考虑不守规矩的客户端,它只会发送 LF。

解析 HTTP/1.1 的请求或响应还会遇到以下问题:

  1. 一次只能处理一个请求或响应,完成之前不能停止解析。
  2. 无法预判解析需要多少内存。

HTTP/2 有了帧,处理协议的程序就能预先知道会收到什么,并且 HTTP/2 有表示帧长度的字段。

在这里插入图片描述

帧结构

 +-----------------------------------------------+
 |                 Length (24)                   |
 +---------------+---------------+---------------+
 |   Type (8)    |   Flags (8)   |
 +-+-------------+---------------+-------------------------------+
 |R|                 Stream Identifier (31)                      |
 +=+=============================================================+
 |                   Frame Payload (0...)                      ...
 +---------------------------------------------------------------+
名称长度描述
Length3 字节表示帧负载的长度,取值范围为 (2 的 14 次方)至 (2 的 24 次方 - 1)。(2 的 14 次方) 16384 字节是默认的最大帧大小,如果需要更大的帧,必须在 SETTINGS 帧中设置
Type1 字节当前帧类型(见下表)
Flags1 字节具体帧类型的标识
R1 位保留位,不要设置,否则可能会带来严重的后果
Stream Identifier31 位每个流的唯一 ID
Frame Payload长度可变真实的帧内容,长度是在 Length 字段中设置的

由于 HTTP/2 是分帧的,请求和响应都可以多路复用,有助于解决类似类似队头阻塞的问题。

帧类型

名称ID描述
DATA0x0传输流的核心内容
HEADERS0x1包含 HTTP 首部,和可选的优先级参数
PRIORITY0x2指示或更改流的优先级和依赖
RST_STREAM0x3允许一端停止流(通常由于错误导致的)
SETTINGS0x4协商连接级参数
PUSH_PROMISE0x5提示客户端,服务器要推送些东西
PING0x6测试连接可用性和往返时延(RTT)
GOAWAY0x7告诉另一端,当前的端已结束
WINDOW_UPDATE0x8协商一端将要接收多少字节(用于流量控制)
CONTINUATION0x9用以扩展 HEADERS 模块

多路复用

在 HTTP/1.1 中,如果客户端想发送多个并行的请求,那么必须使用多个 TCP 连接。

而 HTTP/2 的二进制分帧层突破了这一限制,所有的请求和响应都在同一个 TCP 连接上发送:客户端和服务器把 HTTP 消息分解成多个帧,然后乱序发送,最后在另一端再根据流 ID 重新组合起来。

这个机制为 HTTP 带来了巨大的性能提升,因为:

  • 可以并行交错地发送请求,请求之间互不影响;
  • 可以并行交错地发送响应,响应之间互不干扰;
  • 只使用一个连接即可并行发送多个请求和响应;
  • 消除不必要的延迟,从而减少页面加载的时间;
  • 不必再为绕过 HTTP 1.x 限制而多做很多工作;

在这里插入图片描述

HTTP/2 规范对流的定义是:HTTP/2 连接上独立的、双向的帧序列交换。如果客户端想要发出请求,它会开启一个新流,然后服务器在这个流上回复。 由于有分帧,所以多个请求和响应可以交错,而不会互相阻塞。流 ID 用来标识帧所属的流。

客户端到服务器的 HTTP/2 连接建立后,通过发送 HEADERS 帧来启动新的流。如果首部需要跨多个帧,可能还会发送 CONTINUATION 帧。该 HEADERS 帧可能来自请求或响应。 后续流启动的时候,会发送一个带有递增流 ID 的新 HEADERS 帧。

消息

HTTP 消息泛指 HTTP 请求或响应,消息由一或多个帧组成,这些帧可以乱序发送,然后再根据每个帧首部的流 ID 重新组装。

一个消息至少由 HEADERS 帧(它初始化流)组成,并且可以另外包含 CONTINUATION 和 DATA 帧,以及其他的 HEADERS 帧。

在这里插入图片描述

HTTP/1.1 的请求和响应部分都分成消息首部和消息体两部分;HTTP/2 的请求和响应分成 HEADERS 帧和 DATA 帧。

优先级

把 HTTP 消息分解为很多独立的帧之后,就可以通过优化这些帧的交错和传输顺序,进一步提升性能。

通过 HEADERS 帧和 PRIORITY 帧,客户端可以明确地和服务器沟通它需要什么,以及它需要这些资源的顺序。具体来讲,服务器可以根据流的优先级,控制资源分配(CPU、内存、带宽),而在响应数据准备好之后,优先将最高优先级的帧发送给客户端。

流量控制

在同一个 TCP 连接上传输多个数据流,就意味着要共享带宽。标定数据流的优先级有助于按序交付,但只有优先级还不足以确定多个数据流或多个连接间的资源分配。

为解决这个问题,HTTP/2 为数据流和连接的流量控制提供了一个简单的机制:

  • 流量控制基于每一跳进行,而非端到端的控制;
  • 流量控制基于 WINDOW_UPDATE 帧进行,即接收方广播自己准备接收某个数据流的多少字节,以及对整个连接要接收多少字节;
  • 流量控制窗口大小通过 WINDOW_UPDATE 帧更新,这个字段指定了流 ID 和窗口大小递增值;
  • 流量控制有方向性,即接收方可能根据自己的情况为每个流乃至整个连接设置任意窗口大小;
  • 流量控制可以由接收方禁用,包括针对个别的流和针对整个连接。

HTTP/2 连接建立之后,客户端与服务器交换 SETTINGS 帧,目的是设置双向的流量控制窗口大小。除此之外,任何一端都可以选择禁用个别流或整个连接的流量控制。

服务器推送

HTTP/2 新增的一个强大的新功能,就是服务器可以对一个客户端请求发送多个响应。换句话说,除了对最初请求的响应外,服务器还可以额外向客户端推送资源,而无需客户端明确地请求。

在这里插入图片描述

为什么需要这样一个机制呢?通常的 Web 应用都由几十个资源组成,客户端需要分析服务器提供的文档才能逐个找到它们。那为什么不让服务器提前就把这些资源推送给客户端,从而减少额外的时间延迟呢?服务器已经知道客户端下一步要请求什么资源了,这时候服务器推送即可派上用场。

另外,客户端也可以拒绝服务器的推送。

首部压缩

HTTP/1.1 存在的一个问题就是臃肿的首部,HTTP/2 对这一问题进行了改进,可以对首部进行压缩。
在一个 Web 页面中,一般都会包含大量的请求,而其中有很多请求的首部往往有很多重复的部分。

例如有如下两个请求:

:authority: unpkg.zhimg.com
:method: GET
:path: /za-js-sdk@2.16.0/dist/zap.js
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
cache-control: no-cache
pragma: no-cache
referer: https://www.zhihu.com/
sec-fetch-dest: script
sec-fetch-mode: no-cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36
:authority: zz.bdstatic.com
:method: GET
:path: /linksubmit/push.js
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9
cache-control: no-cache
pragma: no-cache
referer: https://www.zhihu.com/
sec-fetch-dest: script
sec-fetch-mode: no-cors
sec-fetch-site: cross-site
user-agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36

从上面两个请求可以看出来,有很多数据都是重复的。如果可以把相同的首部存储起来,仅发送它们之间不同的部分,就可以节省不少的流量,加快请求的时间。

HTTP/2 在客户端和服务器端使用“首部表”来跟踪和存储之前发送的键-值对,对于相同的数据,不再通过每次请求和响应发送。

下面再来看一个简化的例子,假设客户端按顺序发送如下请求首部:

Header1:foo
Header2:bar
Header3:bat

当客户端发送请求时,它会根据首部值创建一张表:

索引首部名称
62Header1foo
63Header2bar
64Header3bat

如果服务器收到了请求,它会照样创建一张表。
当客户端发送下一个请求的时候,如果首部相同,它可以直接发送这样的首部块:

62 63 64

服务器会查找先前建立的表格,并把这些数字还原成索引对应的完整首部。

性能优化

使用 HTTP/2 代替 HTTP/1.1,本身就是一种巨大的性能提升。
这小节要聊的是在 HTTP/1.1 中的某些优化手段,在 HTTP/2 中是不必要的,可以取消的。

取消合并资源

在 HTTP/1.1 中要把多个小资源合并成一个大资源,从而减少请求。而在 HTTP/2 就不需要了,因为 HTTP/2 所有的请求都可以在一个 TCP 连接发送。

取消域名拆分

取消域名拆分的理由同上,再多的 HTTP 请求都可以在一个 TCP 连接上发送,所以不需要采取多个域名来突破浏览器 TCP 连接数限制这一规则了。

参考资料

更多文章,敬请关注

查看原文

狂奔的蜗牛 收藏了文章 · 2020-05-16

el-tooltip与文本省略号实现精确同步(有省略号就有文本提示,无省略号无文本提示)

项目中经常会遇到这样一个问题或需求:一段文本A,如果A较短则全部显示;如果A比较长,则显示省略号,同时鼠标hover会有提示,类似el-tooltip(或者单独写提示框)。

遇到这类问题或需求,如何处理???
一般的处理方案有(Vue、elementUI为例):
1、通过获判断文本A的长度,控制提示框的显示/隐藏,代码如下:

<template>
  <div id="demo">
    <el-tooltip
      v-if="text.length > 5"
      class="item"
      effect="dark"
      :content="text"
      placement="top"
    >
      <span>{{ text }}</span>
    </el-tooltip>
    <span v-else>{{ text }}</span>
  </div>
</template>

<script>
export default {
  name: 'index',
  components: {},
  data() {
    return {
      text: '提示文字提',
    }
  },
  methods: {},
}
</script>
<style scoped lang="scss">
#demo {
  width: 100%;
  span {
    width: 100px;
    display: inline-block;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    word-break: break-all;
  }
}
</style>

以上代码会出现这么一个问题:如果全是汉字,显示正常;假如包含了数字、英文字母或符号,就会有问题。把text换成英文、数字或字符,没有显示省略号,但是鼠标hover后还会有文字提示。

2、第二种处理方案可以说是上面的升级版:省略号不用样式控制,手动添加(如通过过滤器处理)。代码如下:

<template>
  <div id="demo">
    <el-tooltip
      v-if="text.length > 5"
      class="item"
      effect="dark"
      :content="text"
      placement="top"
    >
      <span>{{ text | textHandle(text) }}</span>
    </el-tooltip>
    <span v-else>{{ text }}</span>
  </div>
</template>
<script>
export default {
  name: 'index',
  components: {},
  data() {
    return {
      text: '提示文字提示文字',
    }
  },
  filters: {
    textHandle: function(val) {
      let textNew = val
      if (val.length > 5) {
        textNew = val.slice(0, 5) + '…'
      }
      return textNew
    },
  },
  methods: {},
}
</script>

<style scoped lang="scss">
#demo {
  width: 100%;
  span {
    width: 100px;
    display: inline-block;
  }
}
</style>

这种处理方案,避免了第一种比较严重的问题。看似完美,但是还有瑕疵:虽然做到了只有在省略号出现的情况,鼠标hover才会出现提示框。但是汉字、数字、英文或符号“出现省略号的临界点的文本长度不同”

例如,上面代码的汉字出现省略号的临界点是6个汉字,文本长度正好填满span标签(width为100px);但数字、英文或符号,虽然临界点也是6个,文本长度却低于span标签设置的固定宽度。
这种方案的问题:汉字、数字、英文字母和符号等出现省略号的文本长度不能保持统一

以上两种方案都出现问题根本原因:一个汉字占用2个字符;一个数字、一个英文字母或一个符号等占用1个字符。

3、第三种处理方案:

对汉字、数字、英文字母和符号等处理成统一的形式(比如依汉字为标准,对数字、英文字母和符号等进行对应的转换)。这种方式能够彻底解决问题。但有点繁琐,不推荐。

> 终极方案:

个人理解:以上方案都是从字节在浏览器中渲染之前入手的,但是字节编码形式有多种,导致多种问题产生。虽然也能彻底解决,但不是最优的解决方案。

建议方案:从字节在浏览器中渲染后入手:拿到最终的文本宽度widthA,外层包裹的壳为固定宽度(或区间宽度)widthB,代码如下:

.vue文件中使用封装好的组件yjl-ellipsis(组件已在全局注册)

<template>
  <div id="structure">
    <yjl-ellipsis
      :placement="placement"
      :outerShellWidth="width"
      :contentText="text"
    ></yjl-ellipsis>
  </div>
</template>

<script>
export default {
  name: 'structure',
  components: {},
  data() {
    return {
      placement: 'top',
      width: 288,
      text: '展示多行文本或者是设置文本内容的格式',
    }
  },
  methods: {},
}
</script>

<style scoped lang="scss">
#structure {
  width: 100%;
}
</style>

封装好的组件yjl-ellipsis代码(全局注册):

<template>
  <div id="yjlEllipsis">
    <div class="contentCls" :style="{ width: width }">
      <el-tooltip
        class="item"
        effect="dark"
        :disabled="isElTooltipShow"
        :content="contentText"
        :placement="placement"
      >
        <span @mouseenter="hanldeElTooltip">
          {{ contentText }}
        </span>
      </el-tooltip>
    </div>
  </div>
</template>

<script>
export default {
  name: 'yjlEllipsis',
  props: {
    placement: {
      // Tooltip 出现位置。位置汇总:top/top-start/top-end/bottom/bottom-start/bottom-end/left/left-start/left-end/right/right-start/right-end
      type: String,
      default: 'top',
    },
    outerShellWidth: {
      // Tooltip 外层壳的宽度,default自定义设置
      type: Number,
      default: 1,
    },
    contentText: {
      // 文本内容
      type: String,
      default: '',
    },
  },
  computed: {
    width() {
      // Tooltip 外层壳宽度自动填充px
      return this.outerShellWidth + 'px'
    },
  },
  data() {
    return {
      isElTooltipShow: true, // Tooltip 是否可用
    }
  },
  mounted() {},
  methods: {
    hanldeElTooltip(val) {
      if (val.target.offsetWidth > this.outerShellWidth) {
        // 作比较:文本实际的宽度与外层壳的宽度
        this.isElTooltipShow = false
      }
    },
  },
}
</script>

<style scoped lang="scss">
#yjlEllipsis {
  width: 100%;
  .contentCls {
    // 外层壳已做单行文本溢出显示省略号处理
    display: inline-block;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    word-break: break-all;
  }
}
</style>

处理思路:
1、外壳B,为固定宽度(或区间宽度)widthB,做单行文本溢出显示省略号处理,内嵌文本A;
2、文本A,通过mouseenter事件(hanldeElTooltip)获取文本的实际宽度widthA;
3、widthA与widthB做比较,然后控制el-tooltip的disabled属性(Tooltip 是否可用),最终实现效果。

备注:
1、组件yjl-ellipsis暂时接收3个属性placement(Tooltip 出现位置)outerShellWidth(外壳B宽度)contentText(文本内容)
2、全局注册组件未在本文中体现。

查看原文

狂奔的蜗牛 发布了文章 · 2020-05-16

el-tooltip与文本省略号实现精确同步(有省略号就有文本提示,无省略号无文本提示)

项目中经常会遇到这样一个问题或需求:一段文本A,如果A较短则全部显示;如果A比较长,则显示省略号,同时鼠标hover会有提示,类似el-tooltip(或者单独写提示框)。

遇到这类问题或需求,如何处理???
一般的处理方案有(Vue、elementUI为例):
1、通过获判断文本A的长度,控制提示框的显示/隐藏,代码如下:

<template>
  <div id="demo">
    <el-tooltip
      v-if="text.length > 5"
      class="item"
      effect="dark"
      :content="text"
      placement="top"
    >
      <span>{{ text }}</span>
    </el-tooltip>
    <span v-else>{{ text }}</span>
  </div>
</template>

<script>
export default {
  name: 'index',
  components: {},
  data() {
    return {
      text: '提示文字提',
    }
  },
  methods: {},
}
</script>
<style scoped lang="scss">
#demo {
  width: 100%;
  span {
    width: 100px;
    display: inline-block;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    word-break: break-all;
  }
}
</style>

以上代码会出现这么一个问题:如果全是汉字,显示正常;假如包含了数字、英文字母或符号,就会有问题。把text换成英文、数字或字符,没有显示省略号,但是鼠标hover后还会有文字提示。

2、第二种处理方案可以说是上面的升级版:省略号不用样式控制,手动添加(如通过过滤器处理)。代码如下:

<template>
  <div id="demo">
    <el-tooltip
      v-if="text.length > 5"
      class="item"
      effect="dark"
      :content="text"
      placement="top"
    >
      <span>{{ text | textHandle(text) }}</span>
    </el-tooltip>
    <span v-else>{{ text }}</span>
  </div>
</template>
<script>
export default {
  name: 'index',
  components: {},
  data() {
    return {
      text: '提示文字提示文字',
    }
  },
  filters: {
    textHandle: function(val) {
      let textNew = val
      if (val.length > 5) {
        textNew = val.slice(0, 5) + '…'
      }
      return textNew
    },
  },
  methods: {},
}
</script>

<style scoped lang="scss">
#demo {
  width: 100%;
  span {
    width: 100px;
    display: inline-block;
  }
}
</style>

这种处理方案,避免了第一种比较严重的问题。看似完美,但是还有瑕疵:虽然做到了只有在省略号出现的情况,鼠标hover才会出现提示框。但是汉字、数字、英文或符号“出现省略号的临界点的文本长度不同”

例如,上面代码的汉字出现省略号的临界点是6个汉字,文本长度正好填满span标签(width为100px);但数字、英文或符号,虽然临界点也是6个,文本长度却低于span标签设置的固定宽度。
这种方案的问题:汉字、数字、英文字母和符号等出现省略号的文本长度不能保持统一

以上两种方案都出现问题根本原因:一个汉字占用2个字符;一个数字、一个英文字母或一个符号等占用1个字符。

3、第三种处理方案:

对汉字、数字、英文字母和符号等处理成统一的形式(比如依汉字为标准,对数字、英文字母和符号等进行对应的转换)。这种方式能够彻底解决问题。但有点繁琐,不推荐。

> 终极方案:

个人理解:以上方案都是从字节在浏览器中渲染之前入手的,但是字节编码形式有多种,导致多种问题产生。虽然也能彻底解决,但不是最优的解决方案。

建议方案:从字节在浏览器中渲染后入手:拿到最终的文本宽度widthA,外层包裹的壳为固定宽度(或区间宽度)widthB,代码如下:

.vue文件中使用封装好的组件yjl-ellipsis(组件已在全局注册)

<template>
  <div id="structure">
    <yjl-ellipsis
      :placement="placement"
      :outerShellWidth="width"
      :contentText="text"
    ></yjl-ellipsis>
  </div>
</template>

<script>
export default {
  name: 'structure',
  components: {},
  data() {
    return {
      placement: 'top',
      width: 288,
      text: '展示多行文本或者是设置文本内容的格式',
    }
  },
  methods: {},
}
</script>

<style scoped lang="scss">
#structure {
  width: 100%;
}
</style>

封装好的组件yjl-ellipsis代码(全局注册):

<template>
  <div id="yjlEllipsis">
    <div class="contentCls" :style="{ width: width }">
      <el-tooltip
        class="item"
        effect="dark"
        :disabled="isElTooltipShow"
        :content="contentText"
        :placement="placement"
      >
        <span @mouseenter="hanldeElTooltip">
          {{ contentText }}
        </span>
      </el-tooltip>
    </div>
  </div>
</template>

<script>
export default {
  name: 'yjlEllipsis',
  props: {
    placement: {
      // Tooltip 出现位置。位置汇总:top/top-start/top-end/bottom/bottom-start/bottom-end/left/left-start/left-end/right/right-start/right-end
      type: String,
      default: 'top',
    },
    outerShellWidth: {
      // Tooltip 外层壳的宽度,default自定义设置
      type: Number,
      default: 1,
    },
    contentText: {
      // 文本内容
      type: String,
      default: '',
    },
  },
  computed: {
    width() {
      // Tooltip 外层壳宽度自动填充px
      return this.outerShellWidth + 'px'
    },
  },
  data() {
    return {
      isElTooltipShow: true, // Tooltip 是否可用
    }
  },
  mounted() {},
  methods: {
    hanldeElTooltip(val) {
      if (val.target.offsetWidth > this.outerShellWidth) {
        // 作比较:文本实际的宽度与外层壳的宽度
        this.isElTooltipShow = false
      }
    },
  },
}
</script>

<style scoped lang="scss">
#yjlEllipsis {
  width: 100%;
  .contentCls {
    // 外层壳已做单行文本溢出显示省略号处理
    display: inline-block;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    word-break: break-all;
  }
}
</style>

处理思路:
1、外壳B,为固定宽度(或区间宽度)widthB,做单行文本溢出显示省略号处理,内嵌文本A;
2、文本A,通过mouseenter事件(hanldeElTooltip)获取文本的实际宽度widthA;
3、widthA与widthB做比较,然后控制el-tooltip的disabled属性(Tooltip 是否可用),最终实现效果。

备注:
1、组件yjl-ellipsis暂时接收3个属性placement(Tooltip 出现位置)outerShellWidth(外壳B宽度)contentText(文本内容)
2、全局注册组件未在本文中体现。

查看原文

赞 8 收藏 5 评论 5

认证与成就

  • 获得 170 次点赞
  • 获得 7 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 7 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2017-06-16
个人主页被 1.4k 人浏览