写给Web前端开发的后端指南
时间:2023-05-28 02:39:01 | 来源:网站运营
时间:2023-05-28 02:39:01 来源:网站运营
写给Web前端开发的后端指南:
前言
在若干次前的一场面试,面试官看我做过
python
爬虫/后端 的工作,顺带问了我些后端相关的问题:
你觉得什么是后端?送命题。当时脑瓦特了,答曰:
逻辑处理和数据增删改查。。。 当场被怼得体无完肤,羞愧难当。事后再反思这问题,结合资料总结了一下。发现自己学过的
Redis
、
Elasticsearch
和
DNS
等其实都属于后端知识体系范畴。
在本文中,我将尝试
总结前端须知的后端体系入门。
无论你的动机是什么,这个体系里都有你想要了解或学习的东西:
存储和服务如何结合在一起? 什么时候(或为什么)我需要用到这个?
全栈之路该怎么走? 各技术的主流框架选择
本文目录: 1. Web / Application Servers 2. 负载均衡器: Load Balancer 3. 域名解析系统,DNS 4. HTTPS / SSL证书 5. 数据库,Database 6. Blob / 文件存储 7. 内容分发网络(CDN) 8. 缓存服务:Caching Service 9. 消息队列:Message queue
1. Web / Application Servers
Web Servers
服务器:Web服务器,使用http
协议向Web提供内容。Application Servers
:应用程序服务器,托管并公开业务逻辑和进程。
1.1 服务器端语言
可以使用不同的服务器端语言编写代码:
例如Node.js,Python,PHP,Java,C#
或Ruby
。 每种语言都有自己的“Web框架”(例如基于 Java 的 Spring,基于 Ruby 的 Rails,基于C#的
http://ASP.NET MVC或基于Node.js的Express)。 * 这些框架使开发人员能够编写更少的代码来处理数据请求。
1.2 后端语言选择
而事实上,每个后端语言都有不一样的特性,也都有各自的拥护者。哪一个语言最适合做为后端语言的入门一直都是没有定论的问题。但为了让我们可以对各语言有一个很简单的概念,以下整理了各语言较常被提及的特色、在开发上比较被人诟病的点,以及有什么样的网站是透过该语言开发的:
PHP
:
- 使用者多,算是最普及的后端语言。
- 简单易学,但因一些古老的设计饱受批评。
- 网站范例:
Facebook
、Wordpress
、新浪微博。
Java
:
- 老牌语言,开发统治者。国内外工作需求稳定,应用层面广。
- 开发相较起来较慢,没那麽适合新手。
- 网站范例:
Linkedin
、Amazon
、淘宝。
Ruby
:
- 开发快速,国内外很多 bootcamp 都以此语言教后端。
- 适不适合新手学饱受争议。
- 网站范例:
Airbnb
、Twitter
。
Python
:
- 语法简单易学,数据分析与资料探勘相关应用多。
- 单独使用 Python 相较起来运行性能较差。
- 网站范例:
Instagram
、Reddit
、本站。
JavaScript (Node.js)
:
- 前端后端都可用 JS,高并发的情况执行效率极高
- 不适合 CPU 密集的应用
- 初创型企业首选
- 网站范例:
Yahoo
、Walmart
Go
:
Google
力推,有很完善的标准库,效能强大堪比C系列。- 目前学习资源较少(感谢伟大B站的付出,真香)
- 网站范例:
Google
、Youtube
、哔哩哔哩、头条、腾讯云
1.2 Web服务器
即
Web Server
,除了托管自定义应用程序代码之外,一些Web应用程序体系结构还使用“Web服务器进程”,例如
Apache HTTP Server
或
Nginx
。这些服务器进程将在访问后端代码之前拦截客户端请求。使用它们有以下几个原因:
- 快速重定向某些请求而不必通过后端代码执行此操作(状态码404页面)。
- 存储在Web服务器的文件系统上的静态内容(例如图像,
CSS
,JS
)比通过后端代码访问更快。 - 某些服务器端语言(例如
PHP
)没有内置的生产级Web
服务器,因此需要通过专用的Web
服务器进程启动。
至此,会引出一个疑问:
Apache
、Nginx
、Tomcat
和Node.js
四者的区别是什么?引用:apache、node.js、nginx、tomcat谁能帮我捋一捋关系?
是一类东西,又不是一类东西。 首先它们都能创建
Web服务器
,但是他们关注的点不一样。
Tomcat
只能跟 Java
配合,Node.js
只能跟JavaScript
。Apache
能和其他语言配合(通常跟 PHP
配合居多),但需要借助不同的模块。Nginx
则是通过端口转发,所以Apache
和Nginx
可以和各种编程语言一起使用Nginx
和Apache
是纯web
服务器,不具备解析动态语言(比如php文件和js文件)的能力.Tomcat
和Node.js
能够解析这些脚本语言,提供应用服务,Web Server
算是附加的功能。
1.3 web服务器的形式(载体)
安装这些工具和后端项目的
Web
服务器计算机,本身可以采用以下几种形式:
- 一台物理机器
- 虚拟专用服务器,即我们通常所说的VPS(例如华为云,阿里云等) VPS实际上是被划分为几个部分的独立服务器,每个部分作为单独的VPS服务器进行销售和使用。也就是说,它是一台可运行多个Web应用程序(网站、软件等)的相对独立的机器,每个用户拥有部分资源。
- 托管虚拟机实例(例如AWS EC2,Google Compute Engine)
- 平台即服务(PaaS)主机,云服务提供商(例如Heroku,AWS Elastic Beanstalk)
VPS
是基于软件层的虚拟化技术,具体来说就是操作系统的虚拟化,VM
是基于硬件层的虚拟化技术,VM
主机使用vmware server
搭建。
1.4 Dokcer,虚拟机与物理机
docker容器与虚拟机有什么区别?
用个类比来极简说明一下:
1. 物理机是这样的: 2. 虚拟机是这样的: 3. Dokcer是这样的:2. 负载均衡器: Load Balancer
负载均衡是高可用网络基础架构的的一个关键组成部分,有了负载均衡,我们通常可以将我们的应用服务器部署多台,然后通过负载均衡将用户的请求分发到不同的服务器用来提高网站、应用、数据库或其他服务的性能以及可靠性。
负载平衡器模型通常分为两类:第4层(传输层)和第7层(应用层)。
第4层(传输层)::
- 根据网络和传输层协议(IP,TCP,FTP,UDP)中的数据进行操作。
- 不认识http协议,对应其他TCP应用,例如基于C/S开发的ERP等系统。
第7层(应用层)::
根据应用层协议(如HTTP)中的数据分发请求。 认识http协议,所以其应用范围主要是众多的网站或者内部信息平台等基于B/S开发的系统。
负载均衡器主要分为硬件负载均衡和软件负载均衡两大类。- 硬件负载均衡: 对应第四层,如F5负载均衡器
- 软件负载均衡: 对应第七层,如
LVS
、Nginx
和HAproxy
两种类型的负载平衡器都会收到请求,并根据配置的算法将这些请求分发到特定的服务器。一些行业标准算法是:
- 轮询调度,
Round robin,RR
- 加权轮询,
Weighted round robin,WRB
- 最少连接数,
Least connections
- 最短的响应时间,
Least response time
在
Web
应用程序中使用负载均衡器有两个主要好处:
- 它通过确保单个
Web
服务器不会被所有请求淹没,来帮助维持一致的响应时间,因此处理每个请求的速度会相对慢些。 - 它保持高可用性。如果服务器崩溃,所有后续客户端请求仍将成功,因为它们将路由到健康的服务器,并且用户不会发现任何问题。
3. 域名解析系统,DNS
当用户在其地址栏中输入
URL
时,浏览器将获取
URL
的域部分(例如
www.google.com
)并调用DNS 。DNS解析发回该网站服务器的IP地址位置(例如172.217.23.4)。一旦它具有IP地址,它就可以发送对网页的实际请求。
- 如果你的Web应用程序使用负载均衡器,则应将域名配置为指向负载均衡器的域名或IP地址。
- 如果您没有使用负载均衡器,那么您可以将域名直接指向应用程序服务器的域名/ IP地址。
大多数互联网域名注册服务(例如
GoDaddy
,万网等)都提供DNS管理控制台。这些允许你配置域名(和子域)以指向应用程序的位置。
如果你愿意,还可以将您的域名服务器转移到阿里云、腾讯云等云提供商,并从那里进行管理。这样做的好处是可以将所有应用程序环境配置保存在一个位置,并使其更易于自动化。
4. HTTPS / SSL
证书
如果你正在构建Web应用程序(或静态网站),则需要通过HTTPS提供服务,以确保用户与服务器之间的安全通信。现在使用
HTTPS
也有
SEO
的好处,所以没有理由不使用它。
这意味着需要在后端安装SSL证书。具体来说,需要在任何服务器上安装它们,这是客户端请求的第一个联系点。这通常意味着负载均衡器和CDN服务器,但如果你没有使用负载均衡器,也可能是应用程序服务器。
你可以使用LetsEncrypt
免费生成证书。 如果你使用的是云基础架构,则可以使用托管服务,例如
AWS Certificate Manager
。这允许你创建并自动续订SSL证书并将其分发到应用程序服务器,负载平衡器和CDN服务器。 * 只有中大型的
HTTPS
证书授权中心才会被浏览器承认,否则会显示为不安全,需要手动信任。
目前SSL证书根据验证级别分为三种类型- 域名型SSL证书,简称DV SSL
- 企业型SSL证书,简称OV SSL
- 增强型SSL证书,简称EV SSL。
- 它们之间都有一定的区别,认证级别也都不同,各自适合不同规模类型的网站安装。
一般情况下,企业类网站使用的OV SSL证书比较多,而且价格也适中,在大众用户可接受范围内。
5. 数据库,Database
几乎所有Web应用程序都需要在某处保留数据。在大多数情况下,某处即某种形式的数据库。 数据库的主要工作是将数据可靠地保存到永久存储器中,并允许通过查询检索数据。它还可以围绕它存储的数据结构强制执行一些规则约束。
5.1 数据库的种类
早期比较流行的数据库模型有三种,分别为层次式数据库、网络式数据库和关系型数据库。
而在当今的互联网中,最常用的数据库模型主要是两种,即关系型(SQL)数据库和非关系型(NoSQL)数据库。
- 关系数据库(例如
MySql,Postgres,SQLServer,Oracle,SQLite
)已经存在了40多年,并且一直是大多数Web应用程序的支柱。 - 而在过去十年左右的时间里,NoSQL数据库(例如MongoDB,Cassandra,CouchDB,DynamoDB)在Web应用程序中变得越来越普遍,主要是因为它们具有可扩展性优势和数据结构灵活性。
5.2 数据库部署
你可以在一台服务器上托管数据库,但在生产方案中更常见的是将其托管在某种形式的集群2台或更多服务器上。这可确保数据库具有高可用性并降低数据丢失的风险,例如,如果一台服务器的存储损坏。
近年来,少数云托管的“无服务器数据库”已经可用。这些是可以通过API调用的数据库,但你无需设置服务器来托管它们。除了处理诸如自动备份之类的事情之外,云供应商还为您无形地执行此操作。这些示例包括
DynamoDB(NoSQL)
,
Firebase
实时数据库(
NoSQL
)和
Aurora
无服务器(关系)。
5.3 数据库基础方案
来源:架构设计之「数据库从主备到主主的高可用方案」
无论底层是关系型数据库,还是NoSQL数据库,无论是 Mysql 还是 Redis、MongoDB,在架构设计上都是相通的。
数据库服务器的基础方案分为三种:
- 一主一备的架构(主备式)
- 一主一从的架构(主从式)
- 互为主从的架构(主主式)
1. 一主一备的架构(主备式)
主备式架构是双机部署中最简单的一种架构,几乎市面上所有的数据库系统都会自带这个主备功能。
其思路也特别的简单:
- 将数据库部署到两台机器,其中一台机器(代号A)作为日常提供数据读写服务的机器,称为「主机」。
- 另外一台机器(代号B)并不提供线上服务,但会实时的将「主机」的数据同步过来,称为「备机」。
- 一旦「主机」出了故障,通过人工的方式,手动的将「主机」踢下线,将「备机」改为「主机」来继续提供服务。
这个架构的优缺点都很明显,优点就是几乎不需要做什么开发改造,各类数据库就支持这种模式,部署维护起来也简单,并没有引入额外的系统复杂度和瓶颈。
但是缺点呢,就是当「主机」出现故障的时候,需要人工去干预啊,运维同学很辛苦的,而且处理还不一定及时。再还有一个缺点就是,主备架构会造成严重浪费资源,毕竟需要一台与「主机」同等配置的「备机」长期备着,但又不作为线上服务来使用,你说浪费不浪费。
为了解决这个资源浪费问题,我们就得想一个把「备机」也用起来的方案:主从式架构。
2. 一主一从的架构(主从式)
主从式架构大体上与上述的主备式架构差不多。区别就是主备式的「备机」平时是不干活的的,主要起到备份的作用。而主从式的「备机」改为了「从机」,平时也要提供服务,跟「主机」一样随时随刻的在干活的。
- 主从式架构中的「从机」虽然也在随时随刻提供服务,但是它只提供「读」服务,并不提供「写」服务。
- 「主机」会实时的将线上数据同步到「从机」,以保证「从机」能够正常的提供读操作。
- 这种架构相比较主备式,对资源是一种节约,毕竟「从机」也在提供服务,没有白白的浪费。并且在「主机」出现故障时,在人工介入之前,好歹「从机」也是能够提供数据的「读」操作的,毕竟大多数业务都是「读」多「写」少,因此对稳定性又提高了一个层次。
- 缺点就是架构稍微复杂了一点,毕竟「主机」和「从机」都有「读」服务,那么前端业务系统就需要用一定策略去判断该路由到哪一台去读取数据。还有就是,延迟问题,「主机」的数据同步到「从机」难免会有一定程度的延迟,这个延迟可能会对数据实时性要求较高的业务有一定影响。
3. 互为主从的架构(主主式)
互为主从的架构是指两台机器自己都是主机,并且也都是作为对方的从机。两台机器都提供完整的读写服务,因此无需切换,客户机在调用的时候随机挑选一台即可,当其中一台宕机了,另外一台还可以继续服务。
- 采用 互为主从架构 有个复杂点就是,因为两台主机都接受写数据,那就需要将写的最新数据实时的同步给对方,需要将数据进行两台主机的双向复制。
- 而双向复制不可避免的会在一定程度上带来数据延迟、极端情况下甚至有数据丢失等问题。
- 在实际业务中,有些业务数据对一致性要求是非常高的,并不能接受数据的延迟、丢失,因此这类业务也不适合互为主从的模式,比如金融业务。
- 但是我们互联网业务中大多数场景还是没有这么高要求的,所以这种模式对于一般场景还是用的蛮多。
至于数据库集群方案,我暂时没看懂,就不写了。。。
6. Blob
/ 文件存储
虽然数据库通常用于存储动态数据(例如,由最终用户或API客户端生成),但是存在某些类别的数据( 非结构化数据),这些数据不能由用户改变或者基于文件而不适合数据库存储,例如:
- 前端网站资源,如图像,
Javascript
,CSS
,字体,音频,视频文件。 - 用户通过表单上传的各类文件。
云服务供应商不是将这些存储在数据库中,而是提供专用服务来存储这些服务,例如
AWS Simple Storage Service(S3)
,
Azure
,
Google Cloud Storage
和阿里云
OSS
等。
这样做的好处是云供应商可以安全地存储文件,并可以为其制作冗余副本,以最大限度地降低数据丢失的风险。
6.1 关于 Blob 存储:
Blob 存储用于:
- 直接向浏览器提供图像或文档。
- 存储文件以供分布式访问。
- 对视频和音频进行流式处理。
- 向日志文件进行写入。
- 存储用于备份和还原、灾难恢复及存档的数据。
- 存储数据以供本地或 Azure 托管服务执行分析
7. 内容分发网络(CDN)
Blob
/文件存储服务允许客户端通过
HTTP
端点访问文件。例如,您的Web应用程序的HTML标记可以简单地链接到AWS S3中存储的图像和CSS文件的URL。
传统网络访问:
但是,假设我的用户位于中国,我的S3存储位于美国西部 - 数据传输距离数千英里,因此我的用户会看到延迟。
CDN是什么?使用CDN有什么优势?
- CDN是云供应商提供的服务,它们在全球范围内分布有“边缘服务器”。
- 这些边缘服务器从“原点”(例如,blob /文件存储位置)获取文件的副本。你的前端Web应用程序将指向 其CDN URL,而不是指向静态资产的Blob存储URL。
- 现在,客户端和“边缘”之间的距离远不是几千英里的往返,而是更少,因此文件的获取速度更快。
使用了CDN的网站访问:7.1 CDN
工作流
通过权威DNS服务器来实现最优节点的选择,通过缓存来减少源站的压力。
8. 缓存服务:Caching Service
虽然
CDN
是静态文件的一种缓存形式,但
Web
应用程序可能需要临时缓存动态数据。
例如,假设存在一个数据库查询,该查询对昨天的数据执行计算,其结果每天经常被成千上万的用户访问。每次用户请求此数据时联系数据库就没有任何意义。
对此的解决方案是使用高速缓存服务在第一个用户请求之后将结果存储一段时间。通过缓存将更快地提供对该数据的后续请求。
缓存服务本质上是一种特殊类型的数据库。 缓存采用键值存储的形式,其中键是应用程序代码用于查询数据的字符串(例如DailySiteStats_2018-10-17),值是缓存的实际数据。缓存的数据通常完全保存在内存中,这使得从缓存中检索数据的速度非常快。
常见的缓存服务是
Redis
和
Memcached
。AWS通过其
Elasticache
服务提供这两者的托管版本。
8.1 Redis
和Memcached
对比
Redis
和
Memcached
是都是主流的开源内存数据存储。虽然它们既易于使用又提供高性能,但在选择引擎时需要考虑重要的差异。
Memcached
是为简单而设计的,而
Redis
提供了丰富的功能,使其能够广泛用于各种用例。 | | Memcached | Redis | | -------------- | --------- | ----- | | 亚毫秒级延迟 | 是 | 是 | | 开发人员易用性 | 是 | 是 | | 数据分区 | 是 | 是 | | 多语言支持 | 是 | 是 | | 高级数据结构 | - | 是 | | 多线程架构 | 是 | - | | 快照 | - | 是 | | 复制 | - | 是 | | 发布/订阅 | - | 是 | | Lua脚本 | - | 是 | | 地理空间支持 | - | 是 |
亚毫秒级延迟:
Redis
和
Memcached
都支持亚毫秒的响应时间。通过将数据存储在内存中,它们可以比基于磁盘的数据库更快地读取数据。
开发人员易用性:
Redis
和
Memcached
在语法上都很容易使用,并且需要最少量的代码才能集成到您的应用程序中。
数据分区:
Redis
和Memcached`都允许您在多个节点之间分发数据。这允许您在需求增长时向外扩展以更好地处理更多数据。
支持广泛的编程语言:
Redis
和
Memcached
都有许多面向开发人员的开源客户端。支持的语言包括
Java,Python,PHP,C,C ++,C#,JavaScript,Node.js,Ruby,Go
等等。
高级数据结构:
除了字符串,
Redis
还支持列表,集合,有序集,哈希,位数组等。应用程序可以使用这些更高级的数据结构来支持各种用例。例如,你可以使用Redis排序集轻松实现游戏排行榜,该排行榜保持按其排名排序的玩家列表。
多线程架构:
由于
Memcached
是多线程的,因此它可以使用多个处理核心。这意味着您可以通过扩展计算容量来处理更多操作。
快照:
使用
Redis
,您可以使用即时快照将数据保存在磁盘上,该快照可用于存档或恢复。
复制:
Redis
允许您创建
Redis
主数据库的多个副本。这允许您扩展数据库读取并具有高可用性集群。
发布/订阅:
Redis
支持使用模式匹配的
Pub /Sub
消息传递,您可以将其用于高性能聊天室,实时评论流,社交媒体源和服务器互通。
Lua脚本:
Redis
允许您执行事务性
Lua
脚本。脚本可以帮助您提高性能并简化应用程序。
地理空间支持:
Redis
具有专门用于大规模处理实时地理空间数据的命令。您可以执行诸如查找两个元素(例如人或地点)之间的距离以及查找点的给定距离内的所有元素之类的操作。
9. 消息队列:Message queue
适用于批处理任务和分离应用程序的异步消息收发
有时,你程序需要执行的任务与响应用户请求没有直接关系。
例如,假设用户上传了需要编码和水印的视频。但这是一项长期运行的任务,因此让用户在完成时等待是没有意义的。更好的方法是异步执行此操作。您的网络应用程序代码会在队列中创建一条作业消息,并通知您的用户,当水印视频准备就绪时,他们将收到一封电子邮件(消息)。
然后,你将拥有一个可以执行以下操作的工作任务流:
- 从队列中读取消息。
- 开始处理视频。
- 完成后,保存视频的编码副本。
- 向用户发送通知电子邮件(消息)。
- 从队列中删除消息。
这里有2个架构组件:
您可以通过以下几种方式实现
worker
任务:
- 调度
CRON
作业以触发应用程序服务器上安装的指定代码,以便按特定计划从队列中读取。 - 将消息添加到队列时,使用
FaaS
平台调用工作器代码。
9.1 Message queue 简介
消息队列是一种异步的服务间通信方式,适用于无服务器和微服务架构。消息在被处理和删除之前一直存储在队列上。每条消息仅可被一位用户处理一次。消息队列可被用于分离重量级处理、缓冲或批处理工作以及缓解高峰期工作负载。
现在常用的MQ组件有
activeMQ
、
rabbitMQ
、
rocketMQ
、
zeroMQ
还有近年来火热的
kafka
,从某些场景来说也是MQ,当然kafka的功能更加强大,虽然不同的MQ都有自己的特点和优势,但是,不管是哪种MQ,都有MQ本身自带的一些特点。
9.2 MQ主要特性
| 特性 | 说明 | | -------------------- | ------------------------------------------------------------ | | 推送或拉取传送 | 拉取是指不断查询队列以获取新消息。推送是指系统在有可用消息时通知用户 (也称为发布/订阅消息收发)。您还可以使用长轮询让拉取等待指定的时间,以便新消息在完成之前到达。 | | 定时或延迟传送 | 支持为消息设置特定的传送时间。如果需要为所有消息设置相同延迟,可以设置一个延迟队列。 | | 至少一次传送 | 消息队列可以存储多个消息副本以实现冗余和高可用性,并在发生通信故障或错误的情况下重新发送消息,以确保它们至少经过一次传送。 | | 确切一次传送 | 在不容许重复的情况下,FIFO (先进先出) 消息队列会通过自动筛选重复来确保每个消息均精确地传输了一次 (且只有一次)。 | | FIFO (先进先出) 队列 | 在这些队列中,首先接受处理的是最早的 (或第一个) 条目,有时称为“队首”。 | | 消息优先级 | 通常情况下,您可以为消息分配优先级,以确定要在队列中添加该消息的位置,从而确保优先级较高的消息位于队列前端并得到优先处理。 |
9.3 MQ应用示例
来源:MQ(消息队列)常见的应用场景解析
我们的实际场景大概是一个基于微服务架构的电商系统,分为用户微服务、商品微服务、订单微服务、促销微服务等。
基于微服务模式开发的系统,MQ的使用场景更多。这里我们就列举一下常见的应用示例。
1. 注册后的初始化
注册后我们可能需要做很多初始化的操作,如:
调用邮件服务器发送邮件、调用促销服务赠送优惠劵、下发用户数据到客户关系系统等。 那么这时候我们将这些操作去监听MQ,当用户注册成功过后,通过MQ通知其他业务进行操作。确保注册用户的性能。
2. 后台发布商品
后台发布商品的时候:
- 商品数据需要从数据库中转换成搜索引擎数据(基于
elasticsearch
) - 那么我们应该将商品写入数据库后,再写入到
MQ
,然后通过监听MQ
来生成elasticsearch
对应的数据。
3.支付超时取消
用户下单后,24小时未支付,需要取消订单。
- 以前我们可能是定时任务循环查询,然后取消订单。
- 实际上,我更推荐类似延迟MQ的方式,避免了很多无效的数据库查询,将一个MQ设置为24小时后才让消费者消费掉,这样很大程度上能减轻服务器压力。
4. 支付完成后通知
- 支付完成后,需要及时的通知子系统(进销存系统发货,用户服务积分,发送短信)进行下一步操作。
- 但是,支付回调我们都是需要保证高性能的,所以,应该直接修改数据库状态,存入MQ,让MQ通知子系统做其他非实时的业务操作。这样能保证核心业务的高效及时。
免责声明
逛国外社区看到这篇,觉得挺简洁明了的。
只是觉得好玩,就按其大纲,重写总结一下,有说错的地方多担待。
~~意思就是写得略粗糙,别喷我。。。~~
❤️ 看完三件事
如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:
- 点赞,让更多的人也能看到这篇内容(收藏不点赞,都是耍流氓 -_-)
- 关注公众号「前端劝退师」,不定期分享原创知识。
- 也看看其它文章
劝退师个人微信:
huab119也可以来我的
GitHub
博客里拿所有文章的源文件:
前端劝退指南:
https://github.com/roger-hiro/BlogFN 一起玩耍呀。~