Kun

Kun

IT学徒、技术民工、斜杠青年,机器人爱好者、摄影爱好 PS、PR、LR、达芬奇潜在学习者


共 212 篇文章


  前端进阶八,进阶能力

style-lint

Stylelint用起来跟eslint差不多,也可以跟gulp一起使用

stylelint有超过150条规则,这些规则没有是默认打开的,都需要自己根据需要进行设置开启

通用:

no-empty-source:不允许空的来源

no-eol-whitespace:不允许行尾空白

no-extra-semicolons:不允许额外的分号

color相关:

Color-hex-case: 指定大写或者小写十六进制的颜色

color-hex-length:指定十六进制颜色长或短的颜色

color-named:需要或者不允许命名的颜色

color-no-hex:不允许十六进制的颜色

color-no-invalid-hex:禁止无效的十六进制颜色

font-family:

font-family-name-quotes:

Number

number-leading-zero:要求分数低于1的数字前面禁止有0

number-max-precision:限制允许的小数位数的数目

number-no-trailing-zeros:禁止在数量后尾随0

String

string-no-newline:禁止在字符串转义换行

string-quote:指定字串单/双引号

time

time-no-imperceptible:禁止动画和过渡小于或等于100毫秒

keyframe declaration

keyframe-declaration-no-important:不允许!important出现在关键帧声明

function

function-blacklist:指定不允许的功能黑名单

function-calc-no-unspaced-operator:计算的函数中禁止的unspaced执行

function-comma-newline-after:要求一个新行或函数的逗号后禁止空白

function-comma-newline-before:要求一个新行或函数的逗号之前禁止空白

function-comma-space-after:要求一个空格或函数的逗号后禁止空白

function-comma-space-bofore:要求一个空格或函数的逗号前禁止空白

function-max-empty-lines:限制方法中相邻的空行数

function-name-case:指定大写或者小写的函数名

function-url-data-uris:要求或禁止数据的URI

function-url-quotes:要求或禁止对于网址报价

function-url-no--schema-whitelist:指定允许URL方案的白名单

function-url-schema-whitelist:指定允许URL方案的白名单

function-whitelist:指定允许的功能的白名单

function-whitespace-after:要求方法后不允许空白

Declaration

declaration-bang-space-after:bang声明之后需要一个空格或者不允许空白

declaration-bang-space-before:bang声明之前需要一个空格或者不允许空白

declaration-colon-newline-after:冒号之后的声明需要一个换行符或不允许空白

declaration-colon-space-after:冒号之后的声明需要一个空白或不允许空白

declaration-colon-space-before:冒号之前的声明需要一个空白或不允许空白

declaration-empty-line-before:声明之前不允许空行

declaration-no-important:不允许important声明

declaration-property-unit-blacklist:指定黑名单内不允许声明的属性

declaration-property-unit-whitelist:指定白名单内不允许声明的属性

declaration-property-value-blacklist:指定黑名单内不允许声明的属性和键值对

declaration-property-value-whitelist:指定白名单内不允许声明的属性和键值对

Declaration block

declaration-block-no-duplicate-properties:不允许在复制属性块中声明

declaration-block-no-ignored-properties:不允许被忽略是因为另一个属性值的相同的规则

declaration-block-no-redundant-longhand-properties: 不允许手写属性,可以组合成一个简写属性

declaration-block-no-shorthand-property-overrides:不允许简写属性覆盖相关手写属性声明块

declaration-block-semicolon-newline-after:要求一个换行符或不允许空白块在分号后

declaration-block-semicolon-newline-before:要求一个换行符或不允许空白块在分号之前

declaration-block-semicolon-space-after:要求一个空间或不允许空白块分号后的声明

declaration-block-semicolon-space-before:要求一个空间或不允许空白块分号前的声明

declaration-block-single-line-max-declaration:限制声明在一行声明块的数量

declaration-block-trailing-semicolon:要求或不允许在声明块之后的分号

block

block-no-empty:不允许空块

block-no-single-line:不允许单行块

block-closing-brace-empty-line-before:要求或不允许关闭括号前空一行

block-closing-brace-newline-after:需要一个换行符或不允许关闭括号后的空白

block-closing-brace-newline-before:需要一个换行符或不允许空白关闭括号前的块

block-closing-brace-space-after:需要一个空间或不允许关闭括号后的空白块

block-closing-brace-space-before:在关闭括号前的块需要一个空格或者不允许空白

block-opening-brace-newline-after:开括号的块之后需要新的一行

block-opening-brace-newline-before:开括号的块之后需要一个换行符或者不允许空白

block-opening-brace-space-after:开括号的块之后需要一个空格或者不允许空白

block-opening-brace-space-before:开括号的块之前需要一个空格或者不允许空白

selector选择器

selector-attribute-brackets-space-inside:在括号里的属性选择器需要一个空格或者不允许空白

selector-attribute-operator-blacklist:指定一个黑名单不允许属性的操作符

selector-attribute-operator-space-after:需要一个空间或不允许空格内操作符之后的属性选择器

selector-attribute-operator-space-before:需要一个空间或不允许空格内操作符之前的属性选择器

selector-attribute-operator-whitelist:指定一个属性允许操作符的白名单

selector-attribute-quotes:需要或不允许引用属性值

selector-id-pattern:指定一个模式,id选择器

selector-max-specificity:限制的特异性选择器

selector-max-compound-selectors:在一个选择器中限制符合选择器的数量

selector-no-attribute:不允许属性选择器

selector-no-combinator:不允许在选择器组合

selector-no-id:不允许id选择器

selector-no-qualifyling-type:不允许符合条件的选择器类型

selector-no-type:不允许类型选择器

selector-no-universal:不允许全局选择器

selector-no-verdor-prefix:不允许选择器的前缀

selector-pseudo-class-blacklist:指定一个黑名单禁止伪类选择器

selector-pseudo-class-case:为伪类选择器指定小写或者大写

selector-pseudo-class-no-unknown:不允许未知的伪类选择器

selector-pseudo-class-parentheses-space-inside:需要一个空格或不允许空格在括号里面的伪类选择器

selector-pseudo-class-whilelist:为伪类选择器指定一个白名单

selector-pseudo-element-case:为伪类选择器指定大写或者小写

selector-pseudo-element-colon-notation:为适用的伪元素指定单引号或者双冒号符号

selector-pseudo-element-no-unknown:不允许未知的伪元素选择器

selector-root-no-composition:在选择器不允许根的构成

selector-type-case:指定小写或大写类型选择器

selector-type-no-unknown:不允许未知类型选择器

media feature

media-feature-colon-space-after:需要一个空间或不允许空格在冒号之后媒体的特性

media-feature-colon-space-before:需要一个空间或不允许空格在冒号之前媒体的特性

media-feature-name-case:为媒体特性名称指定小写或大写

media-feature-name-no-unknown:不允许未知的媒体功能的名字

media-feature-name-no-verdor-prefix:不允许媒体特性名称的前缀

media-feature-no-missing-punctuation:不允许标点non-boolean媒体功能

media-feature-parentheses-space-inside:需要一个空间或不允许空格在括号里的媒体功能

media-feature-range-operator-space-after:需要一个空间或不允许空白范围运算符后媒体的特性

media-feature-range-operator-space-before:需要一个空间或不允许空白范围运算符前媒体的特性

comment

comment-empty-line-before:需要或不允许在评论之前一个空行

comment-no-empty:不允许空的评论

comment-whitespace-inside:需要或不允许空格里面的注释标记

comment-word-blacklist:指定一个黑名单内的不允许的话评论

stylelint-disable-comment

stylelint-disable-reason:需要一个理由stylelint-disable之前或之后的评论发表评论

At-rule

at-rule-blacklist:不允许at-rules指定一个黑名单

at-rule-empty-line-before:需要或不允许at-rules前空一行

at-rule-name-case:指定at-rules大写或者小写的名字

at-rule-name-newline-after:at-rules名称后需要一个换行符

at-rule-no-unknown:不允许at-rules不明

at-rule-no-vendor-prefix:不需要at-rules前缀

at-rule-semicolon-newline-after:需要一个换行符之后at-rules的分号

at-rule-whitelist:指定允许at-rules的白名单

NPM包

开发中必不可少会引用一些外部依赖包,这些包提供一些与业务逻辑无关,又能提高开发进度的功能。业务开发过程中,我们也会剥离一些比较独立的模块出来,发布一个或多个 npm package。npm 是最大的包管理工具了,该平台目前有一百二十多万个包,周下载量达十八亿次。

js package

通常来说,一个 npm 包的最小结构只需要一个 package.json 和一个入口 js 文件。比如 cache 这个包,总共就三个文件,实际代码只有 40 行不到,实现了一个带过期时间的内存缓存,每周有 1.4k 的下载。对于一些小而美的功能,js 也足够用了,但是稍复杂或庞大的功能,依赖无类型的 js 开发,将变得比较困难。

ts package

对于较大型或功能较为复杂的包来说,使用 ts 是一个非常不错的选择,当然 ts 依然可以做小而美的包。借助 ts 类型机制,代码在提示上,静态校验层面将会比 js 更加方便一点。tsc 可将 ts 代码编译成 js 代码,并不影响在 js 环境执行,借助编译期的检查更能尽早发现错误,提高代码质量。

addon package

众所周知 nodejs 不擅长 cpu 密集型运算,因为 cpu 密集型运算会导致当前 js 线程阻塞,无法继续对外服务。nodejs 的 js 代码是单线程执行的,但是 node 进程并不是。而 ndoejs 是基于 c++ 开发的,并且 nodejs 提供了编写 c++ 插件的能力。这就使得 nodejs 可以借助 c++ 完成一些 cpu 密集型任务,比如图片处理的 sharp 包,比如中文分词的 nodejieba。这些真正执行任务的还是 c++ 的代码,但是基于 nodejs 包裹后,使得我们可以完全不关心其 c++ 部分,像使用普通 js 包一样去使用它。

发布流程

注册登陆账户

首先注册 npm 账户是少不了的,先到 https://www.npmjs.com/ 注册 npm 账户,然后本地命令登录该账户 npm login, 按提示输入账户名和密码。

也可以使用命令行进行注册,npm adduser

npm adduser
Username:pingfan
Email:(xxx@sinaapp.com)

验证是否安装成功

npm who am i

创建包项目

新建一个项目文件夹,然后使用 npm init, 交互式输入相关选项即可初始化一个 package.json 文件,有了这个文件,可以认为有了一个项目。但是对外提供服务,还需要提供一个入口文件,这个文件需要在 package.json 中的 main 字段指明。

对于 package.json 这里有最详细的解释,我们着重看一下几个字段

  • files: 这个数组字段用来标记哪些文件可以被打包到 package 中,npm 安装依赖时会下载下来,对于 ts 包来说,通常不需要发布 ts 文件,只需要指定编译后的 js 文件目录即可,有助于减小包的体积。
  • main: 这个用来指定包的入口文件,是必选字段。
  • repository: 这里指明包的存放仓库,当然也可以不填,但是用户就很难反馈相关信息
  • scripts: 这里可以指定相关命令,使用 npm run 来进行执行。之所以重点提出是因为 npm 提供了部分 hook 在这里,比如:prepublishOnly, 我们可以设置这个指令为 npm run test && npm run build, 避免 ts 包发布时忘记 build,比如:install: 这个我们下文中会用到

有了包项目,如果只是发布一个 js 功能,相关配置完成后,npm publish 即可。

如果功能较复杂,可以设置不同目录:

bin: 用于存放可执行二进制文件的目录。

lib:用于存放javascript代码的目录。

doc:用于存放文档的目录。

test: 用于存放单元测试用例的代码。

如果你以后修改了代码,然后想要同步到 npm 上的话请修改 package.json 中的 version 然后再次 publish,更新的版本上传的版本要大于上次

管理包权限:

通常,一个包只有一个拥有权限进行发布。如果需要多人进行发布,可以使用npm owner 命令帮助你管理包的所有者:

npm owner ls eventproxy

使用这个命令,也可以添加包的拥有者,删除一个包的拥有者:

npm owner ls <package name>
npm owner add <user> <package name>
npm owner rm <user> <package name>

发布node包

Node插件包并不比 js 包多多少配置,主要在于一个 binding.gyp 文件, 这里可以找到相关的详细配置,简而言之 binding.gyp 类似 package.json, 它描述了 c++ 的代码该如何进行编译连接。既然有配置文件,那么谁来负责读取执行呢?答案就是 node-gyp。node-gyp 并不是一个纯 js 库,它通过 child_process 模块,转调 Python 脚本来实现相关功能,这也是有些包在安装时需要依赖 Python 环境的原因。

nodejs 插件的编写需要遵循相关的规范,我们可以从 C++ Addons 这里找到插件的编写示例,早期 ndoe 的插件 api 并不稳定,为了兼容不同版本的 nodejs,我们通常使用 NAN 这个库。简单粗暴,提供抽象的宏定义,根据判断不同 nodejs 版本的 api 进行适配。后来 nodejs 推出了 napi ,各个版本维持接口不变,相关的头文件可在 node-addon-api 这个包获取,以便编译 c++ 代码。其实插件包到这里就可以发布了,npm 在安装我们的包时,会根据包的配置,自动编译相关代码,生成插件供下载方使用,

二进制node包

nodejs 插件依赖 node-gyp 编译,node-gyp 依赖 Python 环境,其实还少了一个环境,真正编译 c++ 代码时,还需要 g++ 编译器。这些通常是操作系统必备的,但是某些运行环境恰恰缺少这些,比如我们使用 docker 部署应用时,会尽可能减少包的大小,而这些又仅在安装时使用一次,后续代码运行并不需要。当然我们可以选择分阶段构建镜像,使用环境齐全的镜像作为编译镜像,最后拷贝到打包镜像中去,但是这并非最佳解决方案。

c++ 代码在编译时,也是需要时间的,对于一些复杂的 c++ 代码,其编译时间甚至可以达几十分钟。这对于 npm 来说是不能忍受的。nodejs 的插件可以像 nodejs 一样,根据不同平台编译出对应的可执行文件,便于分发,免去长久的编译过程,减少对运行环境的依赖。

为了达到这个目的,我们需要一个库,node-pre-gyp, 它实现的功能很简单,就是在安装时,根据配置规则尝试直接下载二进制文件,如果下载不到在用 node-gyp 执行编译过程。逻辑很简单,我们也可以自己编写相关脚本来实现,比如 sharp 就是自己编写脚本的。

tavis cli

low code

低代码开发平台(英语:low-code development platform,简称LCDP),

是一种方便产生应用程序的平台软件,软件会开发环境让用户以图形化接口以及配置编写程序,而不是用传统的程序设计作法。此平台可能是针对 某些种类的应用而设计开发的,例如数据库业务过程、以及用户界面(例如网页应用程序)。这类平台可能可以产生完整且可运作的应用程序,也可能在一些特殊的情形下仍需要编写程序。低代码开发平台可以减少传统代码的数量,加速商业应用软件的完成时间。常见的好处是让比较多的人可以参与软件的开发,不只是那些有程序设计技巧的人。低代码开发平台也可以让设置、训练及布置的初期成本降低[1]

低代码开发平台能够实现业务应用的快速交付。也就是说,不只是像传统开发平台一样“能”开发应用而已,低代码开发平台的重点是开发应用更“快”。更重要的是,这个快的程度是颠覆性的:根据Forrester在2016年的调研,大部分公司反馈低代码平台帮助他们把开发效率提升了5-10倍。而且我们有理由相信,随着低代码技术、产品和行业的不断成熟,这个提升倍数还能继续上涨。

低代码开发平台能够降低业务应用的开发成本。一方面,低代码开发在软件全生命周期流程上的投入都要更低(代码编写更少、环境设置和部署成本也更简单);另一方面,低代码开发还显著降低了开发人员的使用门槛,非专业开发者经过简单的IT基础培训就能快速上岗,既能充分调动和利用企业现有的各方面人力资源,也能大幅降低对昂贵专业开发者资源的依赖。

lowcode的核心能力:

全栈可视化编程:可视化包含两层含义,一个是编辑时支持的点选、拖拽和配置操作,另一个是编辑完成后所及即所得(WYSIWYG)的预览效果。传统代码IDE也支持部分可视化能力(如早年Visual Studio的MFC/WPF),但低代码更强调的是全栈、端到端的可视化编程,覆盖一个完整应用开发所涉及的各个技术层面(界面/数据/逻辑)。

全生命周期管理:作为一站式的应用开发平台,低代码支持应用的完整生命周期管理,即从设计阶段开始(有些平台还支持更前置的项目与需求管理),历经开发、构建、测试和部署,一直到上线后的各种运维(e.g. 监控报警、应用上下线)和运营(e.g. 数据报表、用户反馈)。

低代码扩展能力:使用低代码开发时,大部分情况下仍离不开代码,因此平台必须能支持在必要时通过少量的代码对应用各层次进行灵活扩展,比如添加自定义组件、修改主题CSS样式、定制逻辑流动作等。一些可能的需求场景包括:UI样式定制、遗留代码复用、专用的加密算法、非标系统集成。

与定制代码的区别

专业代码(Pro-Code)或定制代码(Custom-Code);但意思都一样,就是指传统的以代码为中心(Code-Centric)的开发模式。

因为即便是使用传统的代码IDE,有些开发工作也支持(甚至更适合)以非代码方式完成,比如:iOS端开发时使用的SwiftUI界面设计器、服务端开发数据库应用时使用的PowerDesigner建模工具。不过这部分可视化工作在传统开发模式下只是起辅助作用,最后通常也是生成开发者可直接修改的代码;开发者仍然是以代码为中心来开展主要工作。

低代码与纯代码之间的关系,其实跟视频和文章之间很像:

  • • 低代码就像是现代的“视频”,大部分内容都由直观易理解、表达能力强的图片组成,因此更容易被大众所接受。但与此同时,视频也不是死板得只能有图片,完全可以添加少量文字(如字幕、标注)来弥补图片表达不够精确的问题。BTW,关于“图”和“文字”之间的辩证关系,可以进一步参考《架构制图:工具与方法论》[1]这篇文章中的相关描述。
  • • 纯代码则更像是传统的“文章”,虽然很久以来都一直是信息传播的唯一媒介,但自从视频技术诞生以及相应软硬件基础设施的普及以来,便逐渐开始被抢走了风头。如今,视频已成为大部分人获取信息的主要渠道(从电视电影到B站抖音),而经常读书读文章的人却越来越少。但不可否认的是,文章依然有它存在的意义和受众(不然我也不会费这劲敲这么多字了),即使“市场份额”一直在被挤压,但永远会有它立足的空间。

从分类的完备性角度来看,有“纯代码”自然也应该有完全相反的“零代码”(也称为“无代码”)。零代码就是完全不需要写代码的应用开发平台,但这并不代表零代码就比低代码更高级和先进,它只是做了一个更极端的选择而已:彻底拥抱简单的图形可视化,完全消灭复杂的文本代码。选择背后的原因是,零代码开发平台期望能尽可能降低应用开发门槛,让人人都能成为开发者(注意:开发 ≠ 写代码),包括完全不懂代码的业务分析师、用户运营,甚至是产品经理(不懂装懂可不算懂)。

即便是专业开发者,在技术分工越来越精细的趋势下(前端/后端/算法/SRE/数据分析..),也很难招到一个能独立开发和维护整套复杂应用的全栈工程师。但零代码可以改变这一切:无论是Java和JavaScript傻傻分不清楚的技术小白,还是精通深度学习但没时间学习Web开发的算法大牛,都可以通过零代码实现自己的技术梦或全栈梦。“改变世界的idea已有,就差一个程序员了”,这句玩笑话或许真的可以成真;哦不,甚至都用不着程序员,有idea的人自己就能上。

HpaPaaS(高生产力应用PaaS)

HpaPaaS是一种支持声明式、模型驱动设计和一键部署的平台,提供了云上的快速应用开发(RAD)、部署和运行特性;这显然与低代码的定义如出一辙。

与dreamweaver的区别

  • 目标场景不同:Dreamweaver 更多地聚焦前端开发场景,而在 low-code 开发平台中,前端只是完整应用程序的一部分,服务端数据、路由、逻辑流程等都需要考虑在内
  • 可视化操作粒度不同:现代 low-code 平台通常有组件、区块、页面、模板等多级复用抽象,Dreamweaver 只面向 HTML 原生标签
  • 工程链路完备程度不同:Dreamweaver 仅覆盖到开发、预览、部署(FTP 上传)环节,而现代 low-code 平台大多涵盖了完整的生命周期,包括发布前的调试、测试,发布后的监控运维等各个环节

SSR同构

SSR,很多人的第一反应是“服务器端渲染”,但我更倾向于称之为“同构”

客户端渲染:客户端渲染,页面初始加载的 html 页面中无网页展示内容,需要加载执行JavaScript 文件中的 react 代码,通过 JavaScript 渲染生成页面,同时,JavaScript 代码会完成页面交互事件的绑定,

服务器端渲染:用户请求服务器,服务器上直接生成 html 内容并返回给浏览器。服务器端渲染来,页面的内容是由 Server 端生成的。一般来说,服务器端渲染的页面交互能力有限,如果要实现复杂交互,还是要通过引入 JavaScript 文件来辅助实现。服务器端渲染这个概念,适用于任何后端语言。

同构:同构这个概念存在于 vuereact 这些新型的前端框架中,同构实际上是客户端渲染和服务器端渲染的一个整合。我们把页面的展示内容和交互写在一起,让代码执行两次。在服务器端执行一次,用于实现服务器端渲染,在客户端再执行一次,用于接管页面交互

一般情况下,当我们使用 React 编写代码时,页面都是由客户端执行 JavaScript 逻辑动态挂 DOM 生成的,也就是说这种普通的单页面应用实际上采用的是客户端渲染模式。在大多数情况下,客户端渲染完全能够满足我们的业务需求

使用 SSR 技术的主要因素:

1.CSR 项目的 TTFP(Time To First Page)时间比较长,在 CSR 的页面渲染流程中,首先要加载 HTML 文件,之后要下载页面所需的 JavaScript 文件,然后 JavaScript 文件渲染生成页面。在这个渲染过程中至少涉及到两个 HTTP 请求周期,所以会有一定的耗时,这也是为什么大家在低网速下访问普通的 React 或者 vue 应用时,初始页面会有出现白屏的原因。

2.CSR 项目的 seo 能力极弱,在搜索引擎中基本上不可能有好的排名。因为目前大多数搜索引擎主要识别的内容还是 HTML,对 JavaScript 文件内容的识别都还比较弱。如果一个项目的流量入口来自于搜索引擎,这个时候你使用 CSR 进行开发,就非常不合适了。

SSR 的产生,主要就是为了解决上面所说的两个问题。在 React 中使用 SSR 技术,我们让 React 代码在服务器端先执行一次,使得用户下载的 HTML 已经包含了所有的页面展示内容,这样,页面展示的过程只需要经历一个 HTTP 请求周期,TTFP 时间得到一倍以上的缩减。

同时,由于 HTML 中已经包含了网页的所有内容,所以网页的 seo 效果也会变的非常好。之后,我们让 React 代码在客户端再次执行,为 HTML 网页中的内容添加数据及事件的绑定,页面就具备了 React 的各种交互能力。

缺点:

  1. 服务端执行 react 性能消耗较大,对 Node SSR 服务器性能损耗更多,对网速、并发数都是考验
  2. 使用 SSR 这种技术,将使原本简单的 React 项目变得非常复杂,项目的可维护性会降低,代码问题的追溯也会变得困难。

服务端路由与客户端路由

实现 React 的 SSR 架构,我们需要让相同的 React 代码在客户端和服务器端各执行一次。大家注意,这里说的相同的 React 代码,指的是我们写的各种组件代码,所以在同构中,只有组件的代码是可以公用的,而路由这样的代码是没有办法公用的

在服务器端需要通过请求路径,找到路由组件,而在客户端需通过浏览器中的网址,找到路由组件,是完全不同的两套机制,所以这部分代码是肯定无法公用。

客户端路由代码

客户端路由代码非常简单,大家一定很熟悉,BrowserRouter 会自动从浏览器地址中,匹配对应的路由组件显示出来。

const App = () => {
  return (
    <Provider store={store}>
      <BrowserRouter>
        <div>
          <Route path='/' component={Home}>
          </div>
      </BrowserRouter>
    </Provider>
  )
}

ReactDom.render(<App/>, document.querySelector('#root'))

服务器端路由代码相对要复杂一点,需要你把 location(当前请求路径)传递给 StaticRouter 组件,这样 StaticRouter 才能根据路径分析出当前所需要的组件是谁。StaticRouter 是 React-Router 针对服务器端渲染专门提供的一个路由组件。

const App = () => {
  return 
    <Provider store={store}>
      <StaticRouter location={req.path} context={context}>
        <div>
          <Route path='/' component={Home}>
        </div>
      </StaticRouter>
    </Provider>
}

Return ReactDom.renderToString(<App/>)

客户端通过 BrowserRouter 我们能够匹配到浏览器即将显示的路由组件,对浏览器来说,我们需要把组件转化成 DOM,所以需要我们使用 ReactDom.render 方法来进行 DOM 的挂载。而 StaticRouter 能够在服务器端匹配到将要显示的组件,对服务器端来说,我们要把组件转化成字符串,这时我们只需要调用 ReactDom 提供的 renderToString 方法,就可以得到 App 组件对应的 HTML 字符串。

数据注水与数据脱水

SSR原理

SSR 的工程中,React 代码会在客户端和服务器端各执行一次。你可能会想,这没什么问题,都是 JavaScript 代码,既可以在浏览器上运行,又可以在 Node 环境下运行。但事实并非如此,如果你的 React 代码里,存在直接操作 DOM 的代码,那么就无法实现 SSR 这种技术了,因为在 Node 环境下,是没有 DOM 这个概念存在的,所以这些代码在 Node 环境下是会报错的。

好在 React 框架中引入了一个概念叫做虚拟 DOM,虚拟 DOM 是真实 DOM 的一个 JavaScript 对象映射,React 在做页面操作时,实际上不是直接操作 DOM,而是操作虚拟 DOM,也就是操作普通的 JavaScript 对象,这就使得 SSR 成为了可能。在服务器,我可以操作 JavaScript 对象,判断环境是服务器环境,我们把虚拟 DOM 映射成字符串输出;在客户端,我也可以操作 JavaScript 对象,判断环境是客户端环境,我就直接将虚拟 DOM 映射成真实 DOM,完成页面挂载。

其他的一些框架,比如 Vue,它能够实现 SSR 也是因为引入了和 React 中一样的虚拟 DOM 技术。

书籍

https://yunlzheng.gitbook.io/prometheus-book

论坛

开源代码:Https://www.github.com/discourse/discourse

如果你觉得我的文章对你有帮助的话,希望可以推荐和交流一下。欢迎關注和 Star 本博客或者关注我的 Github