0%

Pull Request 拉取请求——这是一项对小型和大型开发团队都有帮助的强大功能。拉取请求不仅可以改进审查和反馈过程,而且还有助于跟踪和讨论代码更改。最后但并非最不重要的一点是,它是为你没有写入权限的其他存储库做出贡献的理想方式。

什么是拉取请求?

首先,要明白拉取请求并不是 Git 的核心功能。相反,它们是由你使用的 Git 托管平台提供的: GitHub、GitLab、Bit bucket、AzureDevops 和其他一些平台都内置了这样的功能。

为什么要创建拉取请求?

在我们详细了解如何创建完美的拉取请求之前,让我们先谈谈你为什么会想要使用此功能。

想象一下,你刚刚完成了软件的一项新功能。也许你一直在一个功能分支中工作,所以你的下一步是将它合并到主线分支(master或main)。这在某些情况下没有任何问题,例如,你是项目中唯一的开发人员,或者你有足够的经验并且知道你的团队成员肯定会对此感到高兴。
但是,如果你的更改有点复杂并且你希望其他人查看你的工作怎么办?这就是拉取请求的目的,通过拉取请求,你可以邀请其他人审查你的工作并向你提供反馈。
拉取请求打开后,你可以与其他开发人员讨论你的代码。大多数 Git 托管平台允许其他用户在此过程中添加评论并提出更改建议。在你的审阅者批准你的工作后,它可能会合并到另一个分支中。

但是,拥有审查工作流程并不是拉取请求的唯一原因。如果你想为你没有写入权限的其他存储库做出贡献,它们会派上用场。想想那里的所有开源项目,如果你有一个新功能的想法,或者如果你想提交补丁,拉取请求是一种很好的方式来表达你的想法,而无需加入项目并成为主要贡献者。
这将我们带到了一个与拉取请求紧密相关的主题:forks。

使用forks工作

forks是现有Git仓库的个人副本。回到我们的开源示例:你的第一步是创建原始存储库的分支。之后,你可以在自己的个人副本中更改代码。
完成后,你打开一个拉取请求,要求原始存储库的所有者包含你的更改。所有者或其他主要贡献者之一可以查看你的代码,然后决定包含(或不包含)它。
让审阅者的生活更轻松:如何创建出色的拉取请求
如前所述,拉取请求不是 Git 的核心功能。相反,每个 Git 平台都有自己的设计和关于拉取请求应该如何工作的想法。它们在 GitLab、GitHub、Bitbucket 等上看起来不同。每个平台都有略微不同的跟踪、讨论和审查更改的工作流程。

例如,像Tower Git 客户端这样的桌面GUI可以使这变得更容易:它们提供一致的用户界面,无论你使用什么代码托管服务。

话虽如此,一般工作流程始终相同,包括以下步骤:

  • 如果你没有相关存储库的写入权限,第一步是创建一个分支,即你的存储库的个人版本。
  • 在你的分叉存储库中创建一个新的本地分支。(提醒:拉取请求基于分支,而不是提交!)
  • 在你的本地分支中进行一些更改并提交。
  • 将更改推送到你自己的远程存储库。
  • 使用你的更改创建拉取请求并开始与其他人讨论。

让我们看看拉取请求本身以及如何创建一个让其他开发人员的生活更轻松的请求。首先,它应该很短,以便可以快速查看。当查看 3,000 行而不是 30 行时,更难理解代码。
此外,请确保添加一个良好且不言自明的标题和有意义的描述。尝试描述什么,你变了,为什么你开拉入请求,以及如何更改影响的项目。大多数平台都允许你添加有助于演示更改的屏幕截图。

批准、合并还是拒绝?

一旦你的更改获得批准,你(或具有写入权限的人)就可以将分叉分支合并到主分支中。但是如果审阅者不想合并当前状态的拉取请求怎么办?好吧,你总是可以添加新的提交,在推送那个分支之后,现有的拉取请求就会更新。或者,所有者或其他具有写入权限的人可以在他们不想合并更改时拒绝拉取请求。

开发商的安全网

如你所见,拉取请求是与开发人员同事交流和协作的好方法。通过请其他人审查你的工作,你可以确保只有高质量的代码才能进入你的代码库。

在JavaScript的学习中,我们经常会遇到JavaScript的事件机制,例如,事件绑定、事件监听、事件委托(事件代理)等。这些名词是什么意思呢,有什么作用呢?

事件绑定

要想让 JavaScript 对用户的操作作出响应,首先要对 DOM 元素绑定事件处理函数。所谓事件处理函数,就是处理用户操作的函数,不同的操作对应不同的名称。

在JavaScript中,有三种常用的绑定事件的方法:

在DOM元素中直接绑定;

在JavaScript代码中绑定;
绑定事件监听函数。

我们可以在DOM元素上绑定onclick、onmouseover、onmouseout、onmousedown、onmouseup、ondblclick、onkeydown、onkeypress、onkeyup等。好多不一一列出了。如果想知道更多事件类型请查看,DOM事件。

在JavaScript代码中绑定事件

在JavaScript代码中(即script标签内)绑定事件可以使JavaScript代码与HTML标签分离,文档结构清晰,便于管理和开发。

使用事件监听绑定事件

绑定事件的另一种方法是用 addEventListener() 或 attachEvent() 来绑定事件监听函数。下面详细介绍,事件监听。

事件监听

关于事件监听,W3C规范中定义了3个事件阶段,依次是捕获阶段、目标阶段、冒泡阶段。

起初Netscape制定了JavaScript的一套事件驱动机制(即事件捕获)。随即IE也推出了自己的一套事件驱动机制(即事件冒泡)。最后W3C规范了两种事件机制,分为捕获阶段、目标阶段、冒泡阶段。IE8以前IE一直坚持自己的事件机制(前端人员一直头痛的兼容性问题),IE9以后IE也支持了W3C规范。

W3C规范
语法:

1
element.addEventListener(event, function, useCapture)
  • event : (必需)事件名,支持所有DOM事件。
  • function:(必需)指定要事件触发时执行的函数。
  • useCapture:(可选)指定事件是否在捕获或冒泡阶段执行。true,捕获。false,冒泡。默认false。

事件监听的优点

  • 1、可以绑定多个事件。常规的事件绑定只执行最后绑定的事件。两个事件都执行了。
  • 2、可以解除相应的绑定

封装事件监听

1
2
3
4
5
6
7
8
//绑定监听事件
function addEventHandler(target,type,fn){
if(target.addEventListener){
target.addEventListener(type,fn);
}else{
target.attachEvent("on"+type,fn);
}
}
1
2
3
4
5
6
7
8
//移除监听事件
function removeEventHandler(target,type,fn){
if(target.removeEventListener){
target.removeEventListener(type,fn);
}else{
target.detachEvent("on"+type,fn);
}
}
1
2
3
4
5
//测试
var btn5 = document.getElementById("btn5");
addEventHandler(btn5,"click",hello1);//添加事件hello1
addEventHandler(btn5,"click",hello2);//添加事件hello2
removeEventHandler(btn5,"click",hello1);//移除事件hello1

事件委托

事件委托就是利用冒泡的原理,把事件加到父元素或祖先元素上,触发执行效果。

1
2
3
4
5
6
7
8
var btn6 = document.getElementById("btn6");
document.onclick = function(event){
event = event || window.event;
var target = event.target || event.srcElement;
if(target == btn6){
alert(btn5.value);
}
}

事件委托可以提高JavaScript性能。事件委托可以显著的提高事件的处理速度,减少内存的占用。

在HTML里,我们会使用input标签去处理密码。
就像这样:

1
<input type="password">

如果你使用它,你会在输入时字符会变成这样的小圆点 •••••••

那是因为浏览器试图帮助提高安全性。如果它没有这样做,那么有人可能会从你的身边偷看你正在输入的内容。这应该比查看手指在键盘上按下的键更容易。

但是现在 UX 已经发生了一些变化,并且更常见的选项是:

秘密

我觉得显示/隐藏密码是个很通用的场景。这样我们就可以确保正确输入,并且不会遭受打错字的痛苦。

所以! 该怎么办?

1: 使用 type=”password”, 然后使用JavaScript切换到 type=”text”

这就是现在大家通常会做的事情,因为它现在实际上适用于所有浏览器。

1
2
3
4
5
6
const input = document.querySelector(".password-input");
if (input.getAttribute("type") === "password") {
input.setAttribute("type", "text");
} else {
input.setAttribute("type", "password");
}

这里的问题是除了你必须为此更改输入类型有点奇怪之外,还有是对密码管理器工具失效。

2: 在CSS使用 -webkit-text-security

这不是一个特别通用的方案,因有些浏览器不支持这个属性。

1
2
3
4
5
6
input[type="password"] {
-webkit-text-security: square;
}
form.show-passwords input[type="password"] {
-webkit-text-security: none;
}

3: 在CSS使用 input-security

现在有个关于input security提案,我没可以切换input-security的值。我觉得这是一个比较好的提案,但是现在大多数浏览器还不支持,所以严格意义上说,我还只能用方案1。

1
2
3
form.show-passwords input[type="password"] {
input-security: none;
}

新版Node.js

模块是导出一个或多个值的 JavaScript 文件。导出的值可以是变量、对象或函数。

ES6 导入语法允许导入从不同 JavaScript 文件导出的模块。在 React 和 React Native 应用程序中使用模块是一种常见的模式。语法由以下 ES 模块标准组成:

1
import XXX from 'xxx';

ES 模块是使用模块的 ECMAScript 标准。Node.js 使用 CommonJS 标准来导入模块。这种标准的语法可以这样写:

1
const XXX = require('xxx');

Node js 不直接支持 ES6 导入。尝试import在 JS 文件中编写语法:

1
import { ApolloServer, gql } from 'apollo-server';

使用npm start或运行Node.js服务器npm run dev

Node Error

此错误的解决方案位于上述错误代码段的第一行,现在是 Node.js 推荐的方法。设置 “type”: “module”package.json文件。

1
2
3
{
"type": "module"
}

但是这个方案只适用于最新的 Node.js 和14.x.x+。

低于 14 的 Node.js

这个问题的另一个解决方案是使用Babel。它是一个 JavaScript 编译器,允许使用最新的语法编写 JS。Babel 不是一个框架或平台,所以它可以用在任何用 JavaScript 编写的项目中,因此也可以用在 Node.js 项目中。

首先从终端窗口安装以下开发依赖项:

1
npm i -D @babel/core @babel/preset-env @babel/node

然后在名为 Node.js 项目的根目录创建一个文件babel.config.json并添加以下内容:

1
2
3
{
"presets": ["@babel/preset-env"]
}

该包@babel/node是一个 CLI 程序,它在运行之前使用 Babel 预设和插件编译 Node.js 项目中的 JS 代码。这意味着它会babel.config.json在执行 Node 项目之前读取并应用任何提及的配置。

更换node用babel-node在执行服务器startdev脚本。

使用npm run dev脚本运行 Node 服务器的示例:

1
2
3
4
5
{
"scripts": {
"dev": "nodemon --exec babel-node server.js"
}
}

前言

在开发人员的日常生活中,检查CSS并不常见,但有时不得不这样做。也许这是性能评估网页的一部分,为了找出关键的CSS并减少未使用的选择器。也许是为提高访问性,代码库中使用各种颜色对比突出。

无论情况如何,每当那一时刻到来时,我通常都会使用本文中将要介绍的一些工具。但在此之前,让我们首先看看“检查”CSS意味着什么。

检查CSS很困难

一般来说,代码检查涉及分析代码以发现bug或其他不正常的地方,例如可能的性能问题。对于大多数编程语言来说,审核代码的概念相对简单:它是否有效。但是CSS是一种特殊的语言,在这种语言中,错误大多会被浏览器忽略。事实上,你可以通过许多不同的方式实现相同的风格。至少可以说,这使得CSS的检查有点棘手。

通过为您喜欢的代码编辑器使用扩展或设置检查器或代码检查器,可以防止发现这些错误。但这不是我想在这里展示的,这还不够。我们仍然可能会使用太多的颜色、排版定义或z-index,所有这些都可能导致CSS代码库混乱、不可维护、不稳定。

为了真正检查CSS,我们需要更深入地挖掘并找到那些是最佳的做法。为了找到这些做法,我们可以使用以下工具。

浏览器开发工具

让我们看看用于CSS检查的Chrome 开发工具。

如果您喜欢手动检查CSS代码,可以使用Inspect工具。通过使用它,我们可以看到应用到特定元素的CSS代码。使用“检查箭头”,我们甚至可以看到关于颜色、字体、大小和可访问性的更多详细信息。

浏览器开发工具

CSS Overview

检查 CSS 是非常基础的,一切都需要手动完成。让我们看看一些更高级的 DevTools 功能。

CSS Overview就是其中之一。要启用 CSS Overview工具,请转到“设置”,单击“实验”,然后启用“CSS Overview”选项。要打开 CSS Overview面板,您可以使用 CMD+Shift+P 快捷键,输入“CSS Overview”,然后选择“显示 CSS Overview”。这个工具总结了 CSS 属性,如颜色、字体、对比度、未使用的声明和媒体查询。我通常使用这个工具来简单判断CSS代码的好坏。例如,如果有“50 级灰度”或太多的排版定义,这意味着风格指南没有得到遵循。

Coverage panel

覆盖面板
覆盖率工具显示页面上使用的代码量和百分比。要查看它,可以使用 CMD+Shift+P 快捷键,键入“coverage”,选择“Show Coverage”,然后单击“refresh”图标。

也可以通过在 URL 过滤器输入中键入“.css”来仅过滤 CSS 文件。我通常使用这个工具来了解网站使用的技术。例如,如果我看到覆盖率非常高,我可以假设 CSS 文件是为每个页面单独生成的。它可能不是关键数据,但有时它有助于理解网站的缓存策略。

Coverage panel

Rendering Panel

渲染面板是另一个有用的工具。要打开渲染面板,再次使用 CMD+Shift+P,这次输入“rendering”,然后选择“Show Rendering”选项。这个工具有很多选项,但我最喜欢的是:

  • Paint flashing — 在发生重绘事件时显示绿色矩形。我用它来识别渲染时间过长的区域。
  • Layout Shift Regions — 当布局移动发生时 显示蓝色矩形。为了充分利用这些选项,我通常在“网络”选项卡下设置“慢速 3G”预设。我有时会录制我的屏幕,然后放慢视频速度以找到布局变化。

其他选项可能更有助于调试问题,例如模拟和禁用各种功能、强制使用首选颜色方案功能或打印介质类型以及禁用本地字体。

Rendering Panel

Performance Monitor

性能监视器
另一个用于检查性能 CSS 代码的工具是性能监视器。要启用它,再次使用 CMD+Shift+P,键入“performance monitor”,然后选择“显示性能监视器”选项。我通常使用这个工具来查看在与页面交互时或动画发生时触发了多少重新计算和布局。

Performance Monitor

Perfomance panel

“性能”面板显示页面加载期间所有浏览器事件的详细视图。要启用性能工具,请执行 CMD+Shift+P,键入“性能”,选择“显示性能”,然后单击“重新加载”图标。我通常启用“屏幕截图”和“Web Vitals”选项。对我来说最有趣的指标是 First Paint、First Contentful Paint、Layout Shifts 和 Largest Contentful Paint。还有一个饼图显示绘画和渲染时间。

总结

CSS无处不在,我们需要把它视为每个项目的一等公民。别人怎么看你的CSS并不重要,重要的是你怎么看它。如果你的CSS井然有序并且编写良好,那么您将花费更少的时间调试它,从而得以花更多的时间开发新功能。在理想的世界中,我们会教育每个人写好的CSS,但这需要时间。

让今天成为你开始关心CSS代码的日子。

我知道不是每个人都喜欢检查CSS。但是,如果在这些工具上运行代码,并试图改进CSS代码库的哪怕一部分,那么本文就完成了它的工作。

如果你知道任何其他工具,请在评论中告诉我。

前言

Azure DevOps 提供了项目管理和源代码管理的功能,它的自动化构建和发布可以让我们专注于业务逻辑,省去了一些CI/CD的繁杂琐事。

根据 Azure 的官方文档,

Microsoft Azure 具有充分的自由和灵活性,可以随时随地生成、管理和部署应用程序,帮助用户实现目标。使用你首选的语言、框架和基础结构(甚至可使用你自己的数据中心和其他云)解决大大小小的难题。

本文将介绍如何使用Azure DevOps创建React应用的构建和发布管道部署到 Azure App Service,快速上手Azure DevOps自动化部署

创建应用服务

要部署 React 应用,首先需要在 Azure 上创建应用服务。顾名思义,应用程序服务(Azure App Service)是用于构建、部署和扩展 Web 应用程序的平台。

以下是步骤:

  1. 如果尚未在 Azure 上创建帐户,则可以选择免费帐户。

App_Services

  1. 对于订阅,请选择您的免费订阅。为此应用程序创建一个新的 Resource Group。本文使用名为 react-example-deploy 的资源组。选择 Node 10.14 作为 Runtime Stack,选择 Windows 作为 Operating System。最后,请选择离您最近的地区或保持默认。

App_Service_Config

  1. 点击创建几分钟后,您的新应用服务将上线,您将看到这样的消息。你可以访问 {AppServiceName}.azurewebsites.net 来查看它的实际效果。现在是一个磨人页面,之后 React 应用程序将部署在同一个 URL 上。

Deployment_Complete

创建应用服务

在这里,我们将使用 Azure DevOps 在我们的应用程序中简化持续集成和持续部署。

创建 Azure DevOps 组织

如果你已经创建了 Azure DevOps 组织,请随意跳过。以下是创建 Azure DevOps 组织的步骤:

  1. 登录到 Azure DevOps.
  2. 点击 New organization.

New_Organization

  1. 你会被问到是否继续. 选择 继续.

Continue

  1. 然后您将被要求为您的组织提供一个唯一的名称。输入一个唯一的名称并点击继续。本文使用名为 ReactExampleDeploy 的组织。
    反应示例部署

ReactExampleDeploy

您的组织现已创建,可以通过 https://dev.azure.com/{yourorganization} 访问,其中 yourorganization 是您组织的名称。

Creating a Project创建项目

在 Azure DevOps 组织仪表板上,创建一个新项目。为您的项目指定一个唯一名称,然后点击创建项目

New_Project

推动 React 项目

您现在将 React 应用程序从您的本地机器推送到您刚刚通过命令行创建的项目。

  1. 在您的应用程序的根目录中,运行以下命令。
1
2
3
git init
git add .
git commit -m "Initial Commit"

bash

  1. 在您的项目仪表板上,转到 Repos。复制从命令行推送现有存储库下的代码并在命令行中运行相同的代码。可能会要求您登录 Azure 帐户以继续操作。把代码push到仓库。

Pushing Project

运行此命令后,您可以在 Repos → Files 下看到您的 React 项目。Pushed

现在你已将应用推送到 Azure DevOps 项目,下一步是创建用于构建工件的管道和用于部署 React 应用的发布管道。

创建构建工件管道

根据 Azure 的文档,“您可以使用 Azure Pipelines运行构建、执行测试并将代码(发布)自动部署到各种开发和生产环境。”

以下是创建构建工件管道的步骤。

  1. 单击 Pipelines 选项卡然后单击Create Pipeline.

Create a Pipeline

  1. 当被问到“你的代码在哪里?”时,选择使用经典编辑器选项。

Use the classic editor

  1. 选择 Azure Repos Git 作为源,将其他所有内容保留为默认值,然后点击Continue.

Azure Repos Git

  1. 当要求提供模板时,选择Empty job.

Empty_Job

  1. 单击 + 号

React CI

搜索“npm”并添加两次 npm 任务。

npm

你可能会问,为什么要两次?由于此任务用于node包,因此您需要另一个用于构建项目的任务;不存在默认任务,因此您需要为此创建自定义任务。

点击第二个npm任务,配置如下。

  • 将显示名称更改为 npm run build.
  • 将命令更改为自定义.
  • 在命令和参数中添加运行构建

npm run build

  1. 现在,搜索发布构建工件并将其添加到代理作业 1。

Publish build artifacts

单击 Publish build artifacts 然后修改 Path to publishbuild.

Path to publish

  1. 转到触发器选项卡并选择启用持续集成。您可以将其他所有内容保留为默认值。

CI

  1. 最后,单击 Save & queue 并再次点击 Save & queue

Save & queue

在运行管道模式中,将所有内容保留为默认值,然后点击保存并运行。

Save and run

几分钟后,管道将成功完成运行。您可以在 Pipelines 选项卡下检查以验证它。

创建发布管道

发布管道将部署在上一节中构建的工件。

以下是创建发布管道的步骤:

  1. 转到Pipelines** → Releases 然后单击 New pipeline.

New pipeline release

  1. 选择Azure App Service Deployment 然后点击 Apply.

Azure App Service Deployment

您将被要求为Stage命名。进入 Production 并关闭模态。

Production

  1. 现在点击 Add an artifact.

Add an artifact

对于 Source,选择您在上一节中创建的管道,即 React Deploy Example-CI。将其他所有内容保留为默认值,然后点击添加。

React Deploy Example-CI

  1. 要配置持续部署,请单击 Artifacts 中的闪电图标。

Ligthning

启用第一个禁用选项,在 Build 分支中输入 master,然后关闭模态框。

master

  1. Stages下面 点击 1 job, 1 task .

Stages

会弹出配置 Deploy Azure App Service.

Deploy Azure App Service

根据您的订阅选择 Azure 订阅。您可能需要单击授权并授权订阅。

Azure subscription

App service name

点击 Deploy Azure App Service, 然后在 Package or folder 输入:

1
$(System.DefaultWorkingDirectory)/_React Deploy Example-CI/drop

drop

  1. 最后点击保存

Save

Leave the modal that appears at default and hit OK.

OK

现在,单击 Create release,它将出现在 Save 按钮的右侧。

Create release

创建发布

Create

几分钟后,您的应用程序将部署在 {yourappservicename}.azurewebsites.net。

由于您已经配置了 CI/CD,您可以在本地机器上更新应用程序并将提交推送到 repo,这将触发新版本,并且您的 React 应用程序将被更新。

总结

在本文中,我们讨论了如何使用 CI/CD 将 React 应用程序部署到 Azure App Service 的过程。

下一步,可以去尝试探索 Azure 提供的不同功能。

以下是一些相关文档。

前言

什么是Portals?如何以及何时使用它
React v16 引入了一个称为Portals的新功能。 Portals 提供了一种快速简便的方法来将子组件渲染到存在于父组件的 DOM 层次结构之外的 DOM 节点中。 React 在单个 DOM 节点下渲染整个应用程序 - 应用程序根(root)。但是如果你想在根 DOM 节点之外渲染子节点怎么办?那是你使用Portals的时候。比如当父元素具有样式(如 z-index 将其推到页面的最前面或overflow: hidden)你又希望子元素在视觉上显示在其容器顶部时。

它是如何工作的

Portals可以位于 DOM 树中的任何位置,它在其他方面的行为都像一个普通的 React 子节点。无论子节点是否是Portals,诸如上下文之类的功能都完全相同,因为无论在 DOM 树中的位置如何,Portals仍然存在于 React 树中。这包括事件冒泡。即使那些元素不是DOM树中的祖先,从Portals内部触发的事件也将传播到包含React的树中的祖先。

用法

我建议它创建一个组件,然后在每次需要时使用。首先,我们需要在 index.html 中创建一个 DOM 元素(就像我们为附加应用程序而创建的根元素),这是所有Portals组件将被挂载的地方。

1
2
<div id="portal-components"></div>
<div id="root">

然后创建Portal组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { PureComponent } from 'react'
import ReactDOM from 'react-dom'

const rootElement = document.getElementById('portal-components')

class Portal extends PureComponent {
constructor(props) {
super(props)
this.el = document.createElement('div')
}

componentDidMount() {
rootElement.appendChild(this.el)
}

componentWillUnmount() {
rootElement.removeChild(this.el)
}

render() {
return ReactDOM.createPortal(
this.props.children,
this.el
)
}
}

export default Portal

最后这样使用它

1
2
3
<Portal>
{Everithing here is going to be attachaded outside the root DOM node}
</Portal>

注意

在使用Portals的时候,所有在组件中被渲染的子元素都会相对与屏幕绝对定位,我建议使用css:position: fixed来调整位置。

前言

React没有像其他框架比如(Ember、Vue 等)那样开箱即用的计算属性,所以我们如何以正确的方式实现这个功能呢?最常见的做法是如果是类组件,在 render 方法中“计算”,或者是函数组件就是在主体中。

问题

每次渲染都会进行计算,这意味着即使需要计算的值没有改变,也会生成一个新变量。另外,如果我们将它作为 prop 传递给子组件并且不是原始类型(字符串、数字或布尔值),则子组件将重新渲染,因为它认为这是一个新值,这可能会导致性能问题。

解决方案

如果参数没有改变,将计算提取到一个函数并使用记忆化来防止被重新初始化,如果我们在做大量数据等昂贵的计算,这会给我们带来性能上的提升。

例子

类组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React from 'react'
import memoize from 'lodash/memoize'
import MenuItem from './MenuItem'

class RenderSuggestion extends React.Component {

renderSuggestionStyles = (suggestionValue) => ({ pointerEvents: suggestionValue ? 'all' : 'none' })
renderSuggestionStylesMemo = memoize(this.renderSuggestionStyles)

render(){
const { suggestion, ...other } = this.props
return (
<MenuItem
{...other}
style={this.renderSuggestionStylesMemo(suggestion.value + 1)}
/>
)
}
}

函数组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react'
import memoize from 'lodash/memoize'
import MenuItem from './MenuItem'

const renderSuggestionStyles = suggestionValue => ({ pointerEvents: suggestionValue ? 'all' : 'none' })
const renderSuggestionStylesMemo = memoize(renderSuggestionStyles)

function RenderSuggestion({ suggestion, ...other }) {
return (
<MenuItem
{...other}
style={renderSuggestionStylesMemo(suggestion.value + 1)}
/>
)
}

在这里,如果我们不以正确的方式(没有记忆化)使用计算属性,则当道具更改时,子组件(MenuItem)将不必要地重新渲染。

Reselect
解决此问题的另一种很酷的方法是使用 reselect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React from 'react'
import { createSelector } from "reselect";
import MenuItem from './MenuItem'

class RenderSuggestion extends React.Component {

renderSuggestionStyles = createSelector(
(suggestionValue) => ({ pointerEvents: suggestionValue ? 'all' : 'none' })
)

render(){
const { suggestion, ...other } = this.props
return (
<MenuItem
{...other}
style={this.renderSuggestionStyles(suggestion.value + 1)}
/>
)
}
}

前言

在调试iOS表单的时候,发现点击输入框之后,屏幕会随着检点位置自动放大。但是在安卓中是不会的有这个问题的。

解决办法

<input />的字体大小为16px或更大,则iOS上的Safari将正常聚焦到输入中。但是,当font-size等于或小于15px时,上述问题再次发生。大概是因为Safari认为该字体太小,希望用户看清楚自己在做什么。因此它会放大以帮助你使用辅助功能。所以如果你不想要这个效果,请使用足够大的字体。

总结

总的说,我还是喜欢这个功能。它可以帮助人们看清自己在做什么,并且不建议使用超小字体。

前言

React.createRef和React.useRef都可以用来创建可变对象,这个对象包含current属性,可以用来保存和引用一些值,并且修改这个属性不会触发组件更新。
接下来聊它们的不同。

useRef vs createRef

useRef仅能用在FunctionComponent,createRef仅能用在ClassComponent。
首先hooks不能用在ClassCompnent中, createRef在FunctionComponent是无效的,因为FunctionComponent每次更新createRef都会重新初始化,所以ref会不断改变,看下面代码

1
2
3
4
5
function App() {
// 错误用法,ref.current永远是null
const ref = React.createRef();
return <div ref={ref} />;
}

useRef的用法

1
2
3
4
5
6
7
function App() {
// ref.current 可以保存div的dom实例
const ref = React.useRef();
return <div>
<div ref={ref}>hello world</div>
</div>
}

也可以使用callback ref保存dom实例。但是这个方法会有个小问题,官方文档也有说明。

1
2
3
4
5
6
7
class App extends React.Component {
render() {
return (
<input type="text" ref={el => this.inputRef = el} />
)
}
}

总结

useRef有两种用例:

  1. 直接访问DOM节点以强制执行DOM操作
  2. 在组件实例的整个生命周期中,在所有渲染器中保持一个值
    与useEffect,useState和useCallback结合使用时,可以保留以前的状态。

最后,不要滥用 Ref,Mutable 引用越多,对 React 来说可维护性一般会越差。