# 概述

​ 把一些前后端概念性比较强的理念放在这里,不放代码,不提供案例,仅供理解核心概念参考。

# 2019、2020前端开发技术指南

本指南通过简单分类列出了技术选项,为前端开发者提供技术方向参考

如果你是一个基础的前端开发者,你应该具备以下技能或者推荐你具备以下技能:

​ 1.HTML&CSS。语义化的html元素、基础的css语法、flexbox&grid、CSS变量、浏览器开发者工具。

​ 2.响应式布局。响应式设计将不再是网页的加分项, 而是必须的。设置viewport、非固定宽度、媒体查询、移动端优先、柱状优先、使用rem替代px(或者等效计算,不能是纯px)。

​ 3.基础的部署工作。注册一个域名、管理共享主机或者虚拟主机、FTP, SFTP 文件上传、静态页面托管

​ 4.SASS预处理器。虽然不是必须的, 但是推荐去学, 基础知识的掌握很简单。结构化CSS、变量、嵌套样式表、继承、Mixin&函数。

​ 5.原生JavaScript语法。是基础中最重要的。包括但不限于js的基础变量类型、函数、条件判断、循环、计算符,DOM操作与事件、JSON、Fetch、ES6语法(箭头函数、promise、async/await、结构)。

​ 掌握或者基本掌握了以上技能,你可以做到:构建静态网站、构建UI布局、添加交互功能、部署和维护网站。然而这些作为前端开发的工具是远远不够的。

如果你是一个成熟的前端开发者,你应该具备以下技能或者推荐你具备以下技能:

​ 1.HTML & CSS框架。推荐bootstrap或者bulma。

​ 2.Git和其他工作流工具。基础的命令行、Git版本控制、npm(yarn)包管理、webpack或者gulp打包工具、编辑器插件(ESLint, Prettier, Live Server等)。

​ 3.前端框架。三大框架angular、vue、react至少熟悉或者掌握一个。这三大框架在大公司开发中非常流行,具有更有趣的交互和设计、可以实现前端代码组件化、对团队非常有利。

​ 4.状态管理。对于使用框架的大型前端项目, 你也许需要使用状态管理工具去管理你的应用级的状态。推荐框架有redux、vuex、apollo。

​ 掌握或者基本掌握了以上技能,你可以做到:构建一个优秀的前端应用、流畅和稳定的前端工作流、多人开发&熟练使用Git、请求后端APi

如果你是一个进阶的前端开发者,你应该具备以下技能或者推荐你具备以下技能:

​ 1.学习一门后端语言。成为一个全栈工程师或软件工程师, 你将需要学习一个服务端语言和相关技术。Nodejs、PHP、Python、C#、golang。学习的顺序为基础的后端语法、数据结构和工作流、包管理、http路由。

​ 2.服务端框架。不要重复造轮子, 学习一门框架去构建更好和更快的应用。Nodejs有express、koa、nest,python有django和flask,php有larvael和symphony,c#有asp.net。

​ 3.数据库。绝大多数觉得应用都会使用到数据库, 这里有一些选择:关系型(mysql、PostgreSQL)、非关系型(mongoDB)、轻量级(Redis、SQlite)、云数据库(Azure、AWS、Firebase)。

​ 4.服务端渲染。像React, Vue 和 Angular等框架都可以进行服务端渲染。

​ 5.内容管理系统。内容管理系统允许快速开发并为您的客户提供更新内容的能力. 在你需要快速开发网站的时候, 它们是很适合的. 特别是对于自由开发者.比较好用的CMS管理系统:基于PHP的(wordpress、drupal)、基于JS的(Ghost、keystone)、基于python的(Mezzazine)。

​ 6.安装和部署。学习语言和框架是一回事, 但是安装环境, 测试和部署又是另外一回事。部署(Linux, SSH, Git, Nginx, Apache)、平台(Digital Ocean, AWS, Heroku, Azure)、可视化(Docker, Vagrant)、测试(单元测试, 集成测试, 函数式测试, 系统测试)。

​ 掌握或者基本掌握了以上技能,你可以做到:设置全栈的开发环境和工作流、构建后端服务API和微服务、数据库操作、能够独立开发应用(前后端)、部署到云端。

如果你是一个展望未来的前端开发者,下面这些技术也许是你应该关心或者下一步发展的技能:

​ 1.原生应用开发。ReactNative、Flutter、Ionic、NativeScirpt。

​ 2.使用Electron开发桌面应用.Electron是一个使用JavaScript构建跨平台的桌面应用工具。

​ 3.GraphQL & Apollo.GraphQl是对于API的一种革命性新方法,查询语言比标准RESET严格得多。

​ 4.TypeScript.TypeScript是一个JavaScript的超集, 它添加了静态类型等很多特性。

​ 5.无服务架构.无需创建和管理自己的服务器

​ 6.PWA.Progressive Web Apps是一个web app但是在功能和样式上给用户带来原生应用使用体验的一项技术.

​ 7.AI和机器学习.AI和机器学习已经被广泛应用在所有的程序和技术中, 甚至包括web开发中.

​ 8.区块链技术.现在许多公司使用区块链技术进行数字交易, 因为它们更安全和有效率.

​ 9.Web Assembly

https://segmentfault.com/a/1190000017483325

# 2021前端开发技术指南

# 1. HTML

  • DOCTYPE
  • HTML, XHTML, XML 差异性
  • HTML5 新特性 及 语义化标签
  • meta, img, script 等标签及其标签属性
  • 有兴趣可以了解 W3C 和 WHATWG HTML5 差异

# 2. CSS

  • CSS 基础
  • CSS 布局
  • CSS 动画
  • CSS 预处理器(sass, less, stylus)

# 3. JavaScript

  • JavaScript 基础
  • ES6
  • 面向对象编程 和 函数式编程

# 4. 前端框架

  • Vue
    • 路由(Vue-Router)
    • 状态管理(Vuex)
  • React
    • JSX
    • Class Component, Hooks
    • 路由(React-Router, React-Router-Dom)
    • 状态管理(Redux, Mobx, Context API)
  • Angular
    • 状态管理(Service, NgRx, RxJS)
  • Svelte
    • 路由(svelte-spa-router)
    • 状态管理(Context API)

# 5. 前端工程化

  • 包管理工具(npm, yarn)
  • JavaScript 编译器(babel)
  • 代码检测工具(ESlint)
  • 自动化打包工具(webpack, rollup, parcel, gulp)

# 6. 服务端渲染 SSR

  • Nuxt(Vue)
  • Next(React)
  • Sapper(Svelte)

# 7. 静态站点生成器 SSG

  • Gridsome(Vue)
  • Gatsby(React)

# 8. TypeScript

  • 和 JavaScript 的差异
  • 基础类型
  • OOP(模块,类,接口,继承,泛型等)

# 9. 用 JS 去做服务器端

  • Node.js
    • 单线程、事件驱动、非阻塞I/O
    • 框架(Express, Koa,Nest)
  • Deno

# 10. 桌面应用程序 Electron

Electron.js是可以通过HTML,CSS,JavaScript开发跨平台的桌面应用程序。

  • 基础语法,API
  • 编译
  • 性能优化
  • 调试
  • 部署

# 11. 移动端开发

  • React Native
  • Flutter
  • Ionic

# 12. 前端架构 JAMStack

JAMStack(JAM 代表 JavaScript,API 和 Markup)

是一种使用 Static Site Generators (SSG) 技术、不依赖 Web Server 的前端架构。

# 13. WebAssembly

WebAssembly 是一种新的编码方式,可以在现代的网络浏览器中运行底层字节码。

https://segmentfault.com/a/1190000038774393?utm_source=sf-related

# 前端两年应有技术栈

# 一、打好基础不用说

HTML、CSS、JavaScript 三大件,完全掌握。不懂得就直接查 MDN。HTML重点掌握语义化。区分块级和内联标签。其他查文档就好了。还有就是定义 head 里面一些meta 了解下。

CSS。重点看盒子模型,定位,层级,过渡,动画和 transform。知道原理和规则。大部分工作都是照着设计稿化。掌握上面几个99%还原也不难。接下来重点学习几种常见的布局。完了之后去搞flex。最后搞下sass、less。基本就差不多了。

JavaScript。重点来了,红宝书看一遍掌握基础,进阶去看《你不知道的 JavaScript》。就这两套足够了,别搞那么多。每个知识点搞懂。ES6基本没啥问题。下面几个问题优搞懂,优先级如下:

this 用法,相关原理 原型/原型链 闭包 面向对象相关 同步异步/回调/promise/async、await 模块化 CommonJS, AMD 先搞这懂这些比较难的概念,对你JS理解更加深入。接下来在开始看框架方面

# 二、框架方面

前期要会用,后期要懂原理。

新人先搞 Vue。Vue 算是比较简单的框架了,上手容易。照着官方文档来问题不大。原理方面要提高自己认识。学习怎么看源码。github常去逛逛。

学习框架之前,我其实特别建议,新人先去了解 Babel 和 webpack 不仅仅是使用。一些原理方面的东西工作中也会用到。babel 里面会有教你如何编译代码。webpack教你如和打包文件。自己手写编译器和打包工具也不是特别难。反正对之后看vue、react源码帮助挺大。

搞完 Vue 全家桶,去了解下 React,React hooks 学习下新的理念。再回过头来看Vue。你会发现他们是如此的相似去又不同。

多去实践总结,对整体框架理解会越来越深刻。

# 三、如何看源码

新人刚开始看源码,会陷入两个困境中。一是无从下手。二是看了之后感觉没啥收获。

这个也很正常。一般我们熟知的框架都有个几千甚至上万个PR。太大细节会干扰你。掌握整个节奏和流程。学习原理也比较吃力。就连找个入口都像大海捞针一样。建议从下面几个方面入手:

  • 挑简单的上手。别一开始就搞 vue、react、webpack。太难,会直接劝退新人。不要为了面试而去读。反而效果不好,面试稍微问深入一点就答不出来了。平时有兴趣多琢磨琢磨。按照难易程度,函数库 < 组件库 < 框架 < 工程化 分别典型代表 lodash < vant < vue < webpack
  • 手撸简易模型。像vue, webpack, babel 都有简易项目给你撸。有的创始人(尤哥)还直播手撸。国外的更多,youtube 一搜一大堆。就算不看源码,照着写出了简易 demo 对原理和理解提升都是很大的。
  • 调试开源项目。先把项目拉下来。在vscode里面跑下,核心函数多打几个断点。看看里面变量是怎么diff的。对理解更深刻了。

看了源码是别人的,学到了是自己的。学习时候边记笔记,边思考原理,总结经验。下面来谈谈前端工程化怎么弄。

# 四、前端工程化

现在最流行的打包工具 webpack 用起来。当然直接用 vue-cli2、vue-cli3、create-react 都是可以的。但是 webpack 相关还是得掌握。

首先重点搞下babel、webpack。学习下编译,打包的原理。自己配置下 webpack。尝试自己去写下下 webpack 的 loader 和 plugin。学习这些之前要懂一点 node.js, node.js 不需要全部学习。一般就日常用到读写文件fs接口,path 路径接口。这些 api 都不难写几个 demo 就懂了。基本上webpack 里面配置文件也没用到多少 node 的东西。最后自己学会配置webpack的配置文件。

如果想深想去优化打包体积和速度,就需要去了解很多webpack插件。webpack 里面最核心的就是插件了。

当然前端工程化不仅仅是这些,CI/CD可持续集成, Umi 了解下。shell各种脚本自动化命令、代码生成技术了解下。

# 五、性能优化的方案

一般来说。性能优化没什么系统化的文档供人学习。完全靠一些经验和自己的实践。

我们常提到性能好坏是由什么来衡量呢?

访问页面地址 --> 页面首次加载数据 --> 渲染出完整页面的时长

非首次情况下,命中缓存的加载缓存数据 --> 渲染出完整页面的时长。

一般我从下面几个方面着手去做,一般问题都不大。

减小资源(静态资源,后端加载的数据)大小

  • 压缩代码HTML/CSS/JS
  • 压缩图片、音视频大小
  • Tree-Sharking 消除无用代码

以上webpack都可以搞定

避免同一时间的过多次数请求

  • CSS 实现雪碧图:使用background-position共享一张图
  • 图片懒加载:监听滚动后offsetTop, 使用src 替换 src(真实路径)
  • 列表懒加载(分批加载):监听滚动后offsetTop, 发送请求加载下一页的数据
  • 路由懒加载
  • 代码分包分块加载(webpack)
  • 预加载技术
  • 小程序分包、预下载等。

# 利用缓存(空间换时间)

  • CDN 内容分发:获取更近网络节点缓存下来的静态资源
  • 浏览器缓存(自带)
  • 部分资源保存在LocalStorage或者APP缓存中(手动操作)

# 其他

  • SSR 服务端渲染:解决SPA框架带来JS动态渲染页面带来的延迟和白屏问题。

这些都可以去实践的,难度不大。难度大的地方可能是 dom 节点成千上万的时候渲染的性能问题。这个场景遇到的很少,方案很多。不同人有不同解决方案,有功夫可以自己去尝试尝试。

上面提到很多点都可以深入到很深。由于篇幅原因,点到即止。

# 六、学习移动端web开发

前端现在为什么这么火?各个公司都还挺缺优秀的前端。原因在于技术红利。

移动端web流行起来之后,特别是H5和小程序,带动了多少前端就业,前端迅速取代了安卓和iOS 的大量岗位。

回到正题:所以作为前端人,移动web一般是都要接触的。不同于PC 端。

移动端有哪些东西呢?不需要全部懂,差不多知道就行了。要用的时候再去学。

  • 绝对单位换相对单位:px => rem / vw / rpx
  • 弹性布局:使用flex、grid布局
  • hairline (1px的粗线处理):使用伪元素 + transform: scale(倍数) 缩放线框
  • WebView 环境了解下
  • 安卓iOS 兼容踩坑:点击延迟、穿透、滚动不流畅、安全区域等等。
  • 小程序开发相关踩坑
  • JSBridge: H5 与App 通信
  • H5动画制作
  • 跨平台框架:react native、weex、flutter 等等

简单的说移动web 就是:html/css/js 跑在手机app 里面的WebView(web运行环境)。

小程序/公众号就是在这个基础上,将自己APP里面的WebView 租售给其他人使用。

微信APP ----- 提供SDK ----> 微信webview ----- 提供运行环境-----> 公众号h5 / 小程序

为什么微信可以容纳几乎无限的H5/小程序页面呢?

因为公众号/小程序的代码都存储在云端,通过不同的路由就可以给几乎无数的开发者使用。

使得微信成为一个运行环境+入口的存在。

https://mp.weixin.qq.com/s/dW9-6iTB8xoSUwRrS7IVNQ

# Restful

restful架构:

每一个URI代表一种资源

客户端与服务器之间传递这种资源的表现层

客户端通过四个http动词对服务器端资源进行操作,实现表现层状态转化

restful中一般包含以下数据:

接口地址、请求方法、请求参数:即传输参数时要带什么参数、携带什么字段,规则是什么、返回内容:一般为json或者xml形式,错误代码

具体来说

域名:应该将api部署在专用域名之下,如https://api.example.com api很简单时考虑放在主域名下 https://example.org/api/

版本:应该将api的版本好放入URL中:https://api.example.com/v1/

路径: 每个路径/终点代表一种资源,所以api中不能有动词,只能有名词,而且所用名词往往与数据库的表名对应,如https://api.example.com/v1/zoos https://api.example.com/v1/animals

http动词:对于资源的不同操作类型使用不同的http方法

过滤信息:如果资源数据很多,服务器不可能都将它们返回给用户,api提供参数,过滤返回结果。?limit=10:指定返回记录的数量 ?offset:指定返回记录的开始位置 ?page=2&per——page=100 指定第几页,以及每页的记录数 ?sortby=name&order=asc:指定返回结果按照哪个属性排序以及排序顺序 ?animal——type——id=1:指定筛选条件

状态码:服务器向用户返回的状态码和提示信息

错误处理:如果状态码时4**,应该向用户返回错误信息,并将error作为键名

返回结果:不同的请求应该返回指定解构的数据,比如数组或者包含完整信息的对象

其他:api的身份认证应该使用oauth2.0框架、服务器返回的数据格式应该尽量使用json,避免使用xml

# Swagger

swagger是rest API生成工具,swagger是一个完整的框架,用于生成、描述、调用和可视化restfulapi的web服务

# Oauth

# GraphQL

GraphQL是一种用于API的查询语言。对API提供了一套易于理解的完整描述,使客户端能够准确获得需要的数据

使用GraphQL后端将不再产出api,与前端约定一套schema,用来生成接口文档,前端通过schema进行自己期望的请求

GraphQL并没有和特定的数据库或者存储引擎绑定,而是依靠现有的代码和数据支撑。一个GraphQL是通过定义类型和类型上的字段来创建的,然后给每个类型的字段提供解析函数

目前很多语言也提供了对GraphQL的支持,比如javascript/nodejs,Java、php、Ruby、Python、Go、C#等

# 与Restful接口相比

当今很多页面需要向多个资源发送请求,有的页面甚至发3个以上的请求,而且请求之间可能存在依赖关系,后一个请求需要根据前一个请求的结果进行下一步

传统restful:

数据请求冗余:一个用户的信息可能包含id、昵称、年龄、性别、头像、等级等很多字段,但是有时我们只需要用户的一两个字段而不得不请求到所有的数据,其他的数据是无用的,或者需要获得一个用户列表时,需要调用多个接口,给后端造成麻烦

restful接口改动麻烦:restful一旦要改变API,改变APIurl、增减数量、前后端都需改变

GraphQL:

减少数据和请求冗余:正如官网所说,请求的数据不多也不少,节省带宽,获取多个资源可以只用一个请求

schema格式化:通过schema限制了req和rep的格式和类型,且自动生成文档,减少前后端沟通成本

接口校验:

接口变动,维护与文档:客户端只声明数据,不需要具体格式,API也无需划分版本,只要服务端包含请求方的数据,不需要更改接口

劣势:学习成本,对后端的要求高,这也是graphql推广难的原因,后端需要重新构建

简言之:

restful一个接口返回一个资源,graphql一次获取多个资源

restful用url定义资源,graphql用类型定义资源

# GraphQL中的基本概念

操作类型

​ query:查询:获取数据,查

​ mutation:修改,对数据进行变更,增删改

​ substription:订阅:当数据发生更改时进行消息推送

​ GraphQL在query查询语句时是并行执行的,在mutation变更时是线性执行的,一个接着一个

数据类型

​ GraphQL中定义了两种类型:对象类型和标量类型

​ 对象类型是用户在schema中定义的type

​ GraphQL中定义了标量类型,String、Int、Float、Boolean、ID,此外,用户还可以自己定义自己的标量类型

​ 参数后通常跟!表示参数不能为空

​ GraphQL服务器接收到query请求后,从Root Query开始查找,找到对象类型后到它的解析函数Resolver获取内容,如果返回的是标量内容则结束获取,直到获取最后一个标量类型

模式schema

​ 在schema中定义了字段的类型,数据的结构,即接口数据请求的规则,

​ schema使用强类型模式语法,称为模式描述语言

解析函数Resolver

​ 前端请求到达后端后,由Resolver函数提供数据,

​ 解析函数接受四个参数

​ parent:当前上一个解析函数的返回值

​ args:查询函数中传入的参数

​ context:提供给所有解析器的上下文信息

​ info:一个保存与当前查询相关的字段特定信息

请求格式:

​ GraphQL是通过http来发送请求的

异常处理

​ 查询错误:返回状态码200,返回data字段为null,如query语法错误时

​ HTTP错误:返回码4** 或5**,如token失效等

​ 网络异常:fetch方法异常,如无网络

​ JSON解析异常:response.json方法异常

# Websocket

Http协议是无状态的,只能由客户端主动发起,服务端再被动响应,服务端无法向客户端主动推送内容,并且一旦服务器响应结束,链接就会断开(见注解部分),所以无法进行实时通信。WebSocket协议正是为解决客户端与服务端实时通信而产生的技术

WebSocket协议本质上是一个基于tcp的协议,它是先通过HTTP协议发起一条特殊的http请求进行握手后,如果服务端支持WebSocket协议,则会进行协议升级。WebSocket会使用http协议握手后创建的tcp链接,和http协议不同的是,WebSocket的tcp链接是个长链接(不会断开),所以服务端与客户端就可以通过此TCP连接进行实时通信。

WebSocket (opens new window)是html5规范中的一个部分,它借鉴了socket这种思想,为web应用程序客户端和服务端之间(注意是客户端服务端)提供了一种全双工通信机制。同时,它又是一种新的应用层协议,WebSocket协议是为了提供web应用程序和服务端全双工通信而专门制定的一种应用层协议,

websocket的特点:

  • 1)建立在 TCP 协议之上,服务器端的实现比较容易;
  • 2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器;
  • 3)数据格式比较轻量,性能开销小,通信高效;
  • 4)可以发送文本,也可以发送二进制数据;
  • 5)没有同源限制,客户端可以与任意服务器通信;
  • 6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL,形如:ws://example.com:80/some/path。

websocket断码测试

刷新浏览器页面 1001 终端离开, 可能因为服务端错误, 也可能因为浏览器正从打开连接的页面跳转离开.
关闭浏览器tab页面 1001 终端离开, 可能因为服务端错误, 也可能因为浏览器正从打开连接的页面跳转离开.
关闭浏览器, 所有标签页都会关闭。 1001 可以发现。无论是刷新,关闭tab页面还是关闭浏览器,错误码都是1001
ws.close() 1005 主动调用close, 不传递错误码。对服务端来说,也是异常断开。 1005表示没有收到预期的状态码.
ws.close(1000) 1000 正常的关闭,客户端必需传递正确的错误原因码。 原因码不是随便填入的。 比如 ws.close(1009) Failed to execute 'close' on 'WebSocket': The code must be either 1000, or between 3000 and 4999. 1009 is neither.

# websocket、长轮询、轮询、sse的区别

短轮询(Polling)的实现思路就是浏览器端每隔几秒钟向服务器端发送http请求,服务端在收到请求后,不论是否有数据更新,都直接进行响应。在服务端响应完成,就会关闭这个Tcp连接

长轮询(Long-Polling)客户端发送请求后服务器端不会立即返回数据,服务器端会阻塞请求连接不会立即断开,直到服务器端有数据更新或者是连接超时才返回,客户端才再次发出请求新建连接、如此反复从而获取最新数据。

还有一种长轮询是当我们在页面中嵌入一个iframe并设置其src时,服务端就可以通过长连接“源源不断”地向客户端输出内容。 例如,我们可以向客户端返回一段script标签包裹的javascript代码,该代码就会在iframe中执行。因此,如果我们预先在iframe的父页面中定义一个处理函数process(),而在每次有新数据需要推送时,在该连接响应中写入。那么iframe中的这段代码就会调用父页面中预先定义的process()函数。

SSE(Server-sent-Event):Server-SentHTML5提出一个标准。由客户端发起与服务器之间创建TCP连接,然后并维持这个连接,直到客户端或服务器中的任何一方断开,ServerSent使用的是"问"+"答"的机制,连接创建后浏览器会周期性地发送消息至服务器询问,是否有自己的消息。其实现原理类似于我们在上一节中提到的基于iframe的长连接模式。 HTTP响应内容有一种特殊的content-type —— text/event-stream,该响应头标识了响应内容为事件流,客户端不会关闭连接,而是等待服务端不断得发送响应结果。 SSE规范比较简单,主要分为两个部分:浏览器中的EventSource对象,以及服务器端与浏览器端之间的通讯协议

Web Sockets定义了一种在通过一个单一的 socket 在网络上进行全双工通讯的通道。仅仅是传统的 HTTP 通讯的一个增量的提高,尤其对于实时、事件驱动的应用来说是一个飞跃。

# 轮询(Polling) 长轮询(Long-Polling) Websocket sse
通信协议 http http tcp http
触发方式 client(客户端) client(客户端) client、server(客户端、服务端) client、server(客户端、服务端)
优点 兼容性好容错性强,实现简单 比短轮询节约资源 全双工通讯协议,性能开销小、安全性高,可扩展性强 实现简便,开发成本低
缺点 安全性差,占较多的内存资源与请求数 安全性差,占较多的内存资源与请求数 传输数据需要进行二次解析,增加开发成本及难度 只适用高级浏览器
延迟 非实时,延迟取决于请求间隔 同短轮询 实时 非实时,默认3秒延迟,延迟可自定义

总结:

兼容性:短轮询>长轮询>长连接SSE>WebSocket

性能:WebSocket>长连接SSE>长轮询>短轮询

服务端推送:WebSocket>长连接SSE>长轮询

# BFF

BFF是(Backends For Frontends)单词的缩写,主要是用于服务前端的后台应用程序,来解决多访问终端业务耦合问题。

传统的应用程序内提供的接口是有业务针对性的,这种类型的接口如果独立出来再提供给别的系统再次使用是一件比较麻烦的事情,设计初期的高耦合就决定了这一点。

如果我们的接口同时提供给web移动端使用,移动端仅用来采集数据以及数据的展示,而web端大多数场景是用来管理数据,因为不同端点的业务有所不同每一个端的接口复用度不会太高。

针对多端共用服务接口的场景,我们将基础的数据服务BFF进行了分离数据服务仅提供数据的增删改查,并不过多涉及业务的判断处理,所有业务判断处理都交给BFF来把控,遇到的一些业务逻辑异常也同样由BFF格式化处理后展示给访问端点。

这种设计方式同样存在一定的问题,虽然基础服务BFF进行了分离,我们只需要在BFF层面进行业务判断处理,但是多个端共用一个BFF,也会导致代码编写复杂度增高代码可阅读性降低多端业务耦合

如果我们为每一个端点都提供一个BFF,每个端点的BFF处理自身的业务逻辑,需要数据时从基础服务内获取,然后在接口返回之前进行组装数据用于实例化返回对象。

这样基础服务如果有新功能添加,BFF几乎不会受到影响,而我们如果后期把App端点进行拆分成AndroidIOS时我们只需要将app-bff进行拆分为android-bffios-bff,基础服务同样也不会受到影响

# 优缺点

优点:

(1)可以降低沟通成本:后端同学追求解耦,希望客户端应用和内部微服务不耦合,通过引入BFF这中间层,使得两边可以独立变化

(2)多端应用适配:展示不同的(或更少量的)数据,比如PC端页面设计的API需要支持移动端,发现现有接口从设计到实现都与桌面UI展示需求强相关,无法简单适应移动端的展示需求 ,就好比PC端一个新闻推荐接口,接口字段PC端都需要,而移动端呢H5不需要,这个时候根据不同终端在BFF层做调整,同时也可以进行不同的(或更少的)API调用(聚合)来减少http请求

当你在设计 API 时,会因为不同终端存在不同的区分,它们对服务端提供的 API 访问也各有其特点,需要做一些区别处理。这个时候如果考虑在原有的接口上进行修改,会因为修改导致耦合,破坏其单一的职责。

BFF的痛点

(1)重复开发:每个设备开发一个 BFF 应用,也会面临一些重复开发的问题展示,增加开发成本

(2)维护问题:需要维护各种 BFF 应用。以往前端也不需要关心并发,现在并发压力却集中到了 BFF 上

(3)链路复杂:流程变得繁琐,BFF引入后,要同时走前端、服务端的研发流程,多端发布、互相依赖,导致流程繁琐

(4)浪费资源: BFF层多了,资源占用就成了问题,会浪费资源,除非有弹性伸缩扩容

在微服务架构设计中,BFF起到了一个业务聚合的关键作用,可以 通过openfeignrestTemplate调用基础服务来获取数据,将获取到的数据进行组装返回结果对象,BFF解决了业务场景问题,也同样带来了一些问题,如下所示:

  • 响应时间延迟(服务如果是内网之间访问,延迟时间较低)
  • 编写起来较为浪费时间(因为在基础服务上添加的一层转发,所以会多写一部分代码)
  • 业务异常处理(统一格式化业务异常的返回内容)
  • 分布式事务(微服务的通病)

# 前后端跨域9种方式

浏览器的同源策略/SOP(Same origin policy)是一种约定,所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。

非同源的两个对象,Cookie、LocalStorage 和 IndexDB 无法读取2、 DOM 和 Js对象无法获得3、 AJAX 请求不能发送

对于前后端分离,跨域是需要解决的问题

跨域解决方法

1.jsonp

浏览器允许在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。

手撸jsonp

2.跨域资源共享(CORS)

服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。CORS也已经成为主流的跨域解决方案。

CORS是一种基于http头的机制,该机制通过允许服务器标识除了它自己以外的其他origin(域、协议或者端口),这样浏览器可以从这里加载资源。

3.Nodejs中间件/Nginx代理跨域

通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。

利用node + express + http-proxy-middleware搭建一个proxy代理服务器。

var express = require('express');
var app = express();

var proxy = require('http-proxy-middleware');

app.use('/', proxy({
    // 代理跨域目标接口
    target: 'http://www.demo2.com:8080',
    changeOrigin: true,

    // 修改响应头信息,实现跨域并允许带cookie    
      onProxyRes: function(proxyRes, req, res) {
          res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
          res.header('Access-Control-Allow-Credentials', 'true');
      },

      // 修改响应信息中的cookie域名
      cookieDomainRewrite: 'www.demo1.com'  // 可以为false,表示不修改
      })
    );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

实现思路:通过nginx配置一个代理服务器(域名与demo1相同,端口不同)做跳板机,反向代理访问demo2接口,并且可以顺便修改cookie中demo信息,方便当前域cookie写入,实现跨域登录。

4.WebSocket协议跨域

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。

原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。

5.XMLHttpRequest

HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,

6.axios

其他

window.name + iframe跨域

document.domain + iframe跨域

location.hash + iframe跨域

https://docs.qq.com/doc/BqI21X2yZIht1bfJDW0BNxAF0cqHEb1racd94Lv8jM4Uatg11

# 各种跨域方式对比

实现CORS的关键是服务器,只要服务器实现了CORS接口,就可以跨源通信了

浏览器将CORS请求分为两类,简单请求和复杂请求

简单请求是指http方法为get、post、head三种之一,或者http头部信息不超过以下字段

对于简单请求,请求时自动在请求头信息中添加Origin字段,说明本次请求是来自哪个源

非简单请求是对服务器有特殊要求的请求,比如请求方法是PUT或者DELETE,或者cotent-type字段的类型是application/json

对于非简单请求的CORS请求,会在正式通信之前增加一次HTTP查询请求,称为预检请求

浏览器会先询问服务器当前的域名是否在服务器的许可名单之中,以及可使用哪些HTTP动词和头字段

预检请求的请求信息

OPTIONS /cors HTTP/1.1

1
2

预检请求使用的请求方法是OPTIONS,表示这个请求是用来询问的。

一旦服务器通过了预检请求,以后每次浏览器正常的CORS请求就会像简单请求一样,会有一个Origin头部信息

CORS比jsonp更强大,JSONP只支持GET请求,CORS支持所有类型的HTTP请求。

JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求。

# 跨域验证

方案一:cookie/session认证

用户填写用户名和密码,发送给服务器

局限:

无法数据共享。如果A网站和B网站是同一家公司的

方式二:JWT(Json Web Token)是最流行的跨域验证方案

JWT的原理是服务器认证以后,生成一个json对象,发回给用户。用户与服务器通信都靠这个对象。为了防止用户篡改数据,服务端生成对象时会加上签名。

JWT是一个很长的字符串,中间用点分隔成3个部分。JWT的三个部分依次是头部Header、负载Payload、签名Signature。

头部定义签名的算法和令牌类型(默认为JWT),负载包含实际需要传递的数据,其中定义了七个字段

iss:签发人,exp:过期时间 sub:主题。aud:受众 nbf:生效时间 iat:签发时间 jti:编号

签名是对前两部分的签名,防止数据篡改

使用方式:

客户端收到服务器返回的JWT,可以存储在cookie中,也可以存储在localstorage中,

客户端每次与服务器通信都带上JWT,把JWT放在http请求的头信息Authorization字段里面,就可以跨域访问。

JWT的使用特点:

JWT默认是不加密的,也可以加密,不加密时不能讲秘密数据写入JWT,加密时生成原生token用密钥加密。

JWT不仅可以用于验证,也可以用于交换信息。有效使用JWT可以降低服务器查询数据库的次数

Last Updated: 11/7/2021, 3:25:01 PM