React源码2 Renderer渲染器

什么是渲染器

React架构这篇文章中已经介绍过了,所谓渲染器就是专门把虚拟DOM节点转成真实DOM节点的公共方法。

render阶段始于performSyncWorkOnRoot(同步更新)或performConcurrentWorkOnRoot(异步更新)方法。

两个方法最终调用到workLoopSyncworkLoopConcurrent两个函数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
// 同步更新
function workLoopSync() {
while (workInProgress !== null) {
performUnitOfWork(workInProgress);
}
}
// 异步更新
function workLoopConcurrent() {
while (workInProgress !== null && !shouldYield()) {
performUnitOfWork(workInProgress);
}
}

从代码中我们可以看出,更新过程中都会判断workInProgress,而异步更新会调用shouldYield()函数的判断

shouldYield

异步任务在处理的时候会调用shouldYieldshouldYield会判断是不是已经超时了,超时暂时先不做。

1
2
3
4
5
6
7
8
9
10
11
12
function shouldYield() {
if (deadline === null) {
return false;
}
if (deadline.timeRemaining() > timeHeuristicForUnitOfWork) {
// Disregard deadline.didTimeout. Only expired work should be flushed
// during a timeout. This path is only hit for non-expired work.
return false;
}
deadlineDidExpire = true;
return true;
}

performUnitOfWork

performUnitOfWork函数最主要的功能就是调用beginWork()completeUnitOfWork()两个函数。beginWork()为捕获阶段,此阶段会采取深度优先的方式遍历节点,并完成Fiber树创建以及diff算法。completeUnitOfWork()为冒泡阶段,此阶段要完成生命周期(部分)的调用,形成effectlist

递归遍历

前面也讲过,渲染的时候需要调用递归遍历虚拟节点。那么在“递”和“归”的过程中,具体要做哪些工作呢?

“递”阶段

递阶段会从Fiber的RootFiber开始深度优先遍历,为遍历到的每一个方法调用beginWork方法。

beginWork 的工作内容是往传入的Fiber节点上添加子节点。并使两个节点连接起来。

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
29
30
31
32
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
const updateLanes = workInProgress.lanes;

if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;

if (
oldProps !== newProps ||
hasLegacyContextChanged() ||
// 如果实现因热重载而改变,则强制重新渲染
(__DEV__ ? workInProgress.type !== current.type : false)
) {
// ...更新阶段的处理
} else if (!includesSomeLane(renderLanes, updateLanes)) {
//
//...
} else {
if ((current.effectTag & ForceUpdateForLegacySuspense) !== NoEffect) {

didReceiveUpdate = false;
}
}
} else {
/* 该didReceiveUpdate变量代表本次更新中本Fiber节点是否有变化 */
didReceiveUpdate = false;
}
}

当第一次渲染,会根据 workInProgress.tag 来根据组件类型分别处理

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
29
30
switch (workInProgress.tag) {
case IndeterminateComponent:
// ...省略
case LazyComponent:
// ...省略
case FunctionComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
case ClassComponent:
// ...省略
case HostRoot:
// ...省略
case HostComponent:
// ...省略
case HostText:
// ...省略
// ...省略其他类型
}

这里注意 updateFunctionComponent 方法,并不是真正的把真实的dom节点渲染到浏览器上,而是先做个标记,到commit阶段再统一做渲染。

current 用来区分是 mount 还是 创建还是更新。如果当前Fiber不存在的话,就会创建一个新的Fiber节点。

“归”阶段

在“归”阶段会调用completeWork (opens new window)处理Fiber节点。

当某个Fiber节点执行完completeWork,如果其存在兄弟Fiber节点(即fiber.sibling !== null),会进入其兄弟Fiber的“递”阶段。

如果不存在兄弟Fiber,会进入父级Fiber的“归”阶段。

“递”和“归”阶段会交错执行直到“归”到rootFiber。至此,render阶段的工作就结束了。

1
2
3
4
5
6
7
if (next === null) {
// workInProgress已经不存在子树,就开始进行"归"阶段
completeUnitOfWork(unitOfWork);
} else {
// next是beginWork调用后的返回值workInProgress.child
workInProgress = next;
}

这个时候会调用 completeUnitOfWork 方法,此方法的作用是处理标记的EffectList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 执行effect相关
if (returnFiber.firstEffect === null) {
returnFiber.firstEffect = completedWork.firstEffect;
}
if (completedWork.lastEffect !== null) {
if (returnFiber.lastEffect !== null) {
returnFiber.lastEffect.nextEffect = completedWork.firstEffect;
}
returnFiber.lastEffect = completedWork.lastEffect;
}

if (effectTag > PerformedWork) {
if (returnFiber.lastEffect !== null) {
returnFiber.lastEffect.nextEffect = completedWork;
} else {
returnFiber.firstEffect = completedWork;
}
returnFiber.lastEffect = completedWork;
}

React 源码学习1-React架构

老的架构-React15的架构

React15使用的架构主要分两层:

  • Reconciler(协调器)
  • Renderer(渲染器)

协调器负责找出组件的变化,也就是我们说的diff,而Renderer负责渲染组件。首先为了方便我们后续更好的理解,先略微讲一下Render工作

JSX

JSX官网
React官网

在这之前我们先将一下JSX,React 使用 JSX 来替代常规的 JavaScript。JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。是FaceBook公司为了方便我们编写代码,研究出的一套特殊语法。JSX的优点有以下几个方面:

  • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
  • 它是类型安全的,在编译过程中就能发现错误。
  • 使用 JSX 编写模板更加简单快速。
    我们看以下代码:

代码1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class HelloMessage extends React.Component {
render() {
return (
<div>
Hello {this.props.name}
</div>
);
}
}

ReactDOM.render(
<HelloMessage name="Taylor" />,
document.getElementById('hello-example')
);

代码2:

1
2
3
4
5
6
7
8
9
10
11
12
class HelloMessage extends React.Component {
render() {
return React.createElement(
"div",
null,
"Hello ",
this.props.name
);
}
}

ReactDOM.render(React.createElement(HelloMessage, { name: "Taylor" }), document.getElementById('hello-example'));

猜测一下这两段代码有什么区别?

其实就是写法上不一样而已,实际的效果是一模一样的。那我们不妨猜测一下JSX的作用?

JSX 会被babel编译成React.createElement方式运行
那到这里,我们继续往下猜测,React.createElement 方法是做什么的?

React.createElement 方法

我们知道,Vue 和React 框架都是基于虚拟dom的。那么React.createDom 方法就是创建虚拟dom节点的方法。那如果存在组件嵌套的情况,在第三个参数那里可以调用React.createElement 去创建一个节点。最后得到一整个虚拟dom节点,交给render函数进行渲染。

ReactDom.render

ReactDom.render 这个函数从名字上来看就可以猜出它是干什么的,就是专门用来把虚拟dom节点转成真实dom节点,最后挂载带页面上。也就是我们说的渲染。那我们可以思考一下,这个dom节点如果很大会怎么样?对就是会卡顿。于是需要在render之前做一下diff。

React 是一个跨平台的框架,所以有多套Renderer。每当Renderer接到Reconciler的通知,Renderer会根据平台来渲染。

Reconciler(协调器)

协调器的作用是 负责找出变化的组件。每当有组件需要更新时,协调器需要做如下工作:

  • 调用函数组件、或class组件的render方法,将返回的JSX转化为虚拟DOM
  • 将虚拟DOM和上次更新时的虚拟DOM对比
  • 通过对比找出本次更新中变化的虚拟DOM
  • 通知Renderer将变化的虚拟DOM渲染到页面上

老架构的缺点

即使有diff算法来优化性能,但如果dom层级很深的话,由于React使用递归的方式来遍历虚拟dom节点,必然会造成视觉上的卡顿。

递归更新的缺点

由于递归执行,所以更新一旦开始,中途就无法中断。当层级很深时,递归更新时间超过了16ms,用户交互就会卡顿。

新的React架构

React16架构可以分为三层:

  • Scheduler(调度器)—— 调度任务的优先级,高优任务优先进入Reconciler
  • Reconciler(协调器)—— 负责找出变化的组件
  • Renderer(渲染器)—— 负责将变化的组件渲染到页面上

    那么什么是调度器?

    Fiber 的三层含义

    • 作为架构来说,之前React15的Reconciler采用递归的方式执行,数据保存在递归调用栈中,所以被称为stack Reconciler。React16的Reconciler基于Fiber节点实现,被称为Fiber Reconciler。
  • 作为静态的数据结构来说,每个Fiber节点对应一个React element,保存了该组件的类型(函数组件/类组件/原生组件…)、对应的DOM节点等信息。

  • 作为动态的工作单元来说,每个Fiber节点保存了本次更新中该组件改变的状态、要执行的工作(需要被删除/被插入页面中/被更新…)。

    调度器

    我们知道React16最大的更新就是新增了Fiber架构,Fiber架构把一个完整的渲染任务拆成很多个小任务,分成不同的优先级。增量渲染(把渲染任务拆分成块,匀到多帧),十任务渲染更流畅,避免卡顿现象。

调度器处理完任务的优先级,将需要渲染的任务传递给协调器,进行diff。最后传递给renderer进行渲染。

首先说说常规的安装

1. 下载nvm,使用nvm来管理Node版本

命令行

1
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash

2. 使用nvm来下载node,并管理node版本

常见命令

1
2
3
4
5
6
7
8
9
10
11
nvm install stable ## 安装最新稳定版 node
nvm install <version> ## 安装指定版本
nvm uninstall <version> ## 删除已安装的指定版本
nvm use <version> ## 切换使用指定的版本node
nvm ls ## 列出所有安装的版本
nvm ls-remote ## 列出所有远程服务器的版本
nvm current ## 显示当前的版本
nvm alias <name> <version> ## 给不同的版本号添加别名
nvm unalias <name> ## 删除已定义的别名
nvm reinstall-packages <version> ## 在当前版本 node 环境下,重新全局安装指定版本号的 npm 包
nvm alias default [node版本号] ##设置默认版本

上面的安装大家都可以信手拈来,这里就不赘述,主要说一下这其中有个大家需要注意的坑点。敲重点啦!

坑点一:安装之前必须把本地环境清理干净

在安装node前,首先要考虑后续会不会切换版本,如果需要,必须要求nvm在Node安装之前

  • 先安装nvm

在通过nvm安装自己需要的node

否则,在nvm安装之前的安装的node是不会被nvm管理起来的!

那如果已经安装了node,却还要使用nvm管理版本的话,怎么办?我很不幸的告诉你,你需要删除已安装的node

​ 下面是删除的Nodenpm的命令,在终端中执行即可。

1
2
3
4
5
6
sudo npm uninstall npm -g
sudo rm -rf /usr/local/lib/node /usr/local/lib/node_modules /var/db/receipts/org.nodejs.*
sudo rm -rf /usr/local/include/node /Users/$USER/.npm
sudo rm /usr/local/bin/node
sudo rm /usr/local/share/man/man1/node.1
sudo rm /usr/local/lib/dtrace/node.d

坑点二:关闭终端nvm失效

对于一般的终端,默认是bash窗口,所以在安装完nvm后,根据如下图提示,进行操作

配置环境变量(无 .bash_profile)

添加 .bash_profile在终端键入touch ~/.bash_profile

打开 .bash_profile在终端键入open -e .bash_profile

.basn_profile中输入

1
2
3
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion

配置生效

1
source ~/.bash_profile

# 添加docker权限给当前用户(可以使docker命令免sudo)

1. 添加 docker group

1
2
sunchang@sunchang-PC:~$ sudo groupadd docker
groupadd:“docker”组已存在

2. 将用户添加到docker组

1
2
sunchang@sunchang-PC:~$ sudo gpasswd -a ${USER} docker
正在将用户“sunchang”加入到“docker”组中

3. 重启docker服务

1
sunchang@sunchang-PC:~$ sudo service docker restart

4. 查看用户组及成员

1
2
sunchang@sunchang-PC:~$ cat /etc/group | grep docker
docker:x:997:sunchang

运行一个HelloWorld

1
docker run ubuntu echo "Hello World"

解释: 使用ubuntu 镜像创建一个容器,并在这个容器里运行命令: echo "Hello World"

让运行的容器实现对话能力

1
docker run -i -t ubuntu /bin/bash
  • -t: 在新容器内指定一个伪终端或终端。
  • -i: 允许你对容器内的标准输入 (STDIN) 进行交互。
    可以通过运行 exit 命令或者使用 CTRL+D 来退出容器。

    启动容器(后台模式)

    使用以下命令创建一个以进程方式运行的容器
    1
    2
    3
    docker run -d ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"

    2b1b7a428627c51ab8810d541d759f072b4fc75487eed05812646b8534a2fe63

这个时候,只是输出了一个id,这个是容器的id。

如何将本地目录映射到宿主机里?

docker run 参数后面添加 -v /path/to/local:/path/to/target,注意的是windows系统需要按如下格式写:

1
docker run -p 80:80 -v /D/docker/web:/usr/share/nginx/html/web -d nginx

-p 映射端口号,如果要映射多个端口号,可以写多个 -p。如:

1
docker run -p 80:80 -p:443:443 -d nginx

-d 在后台运行

vim编辑文件

不是所有的容器都自带vim,像nginx的容器默认是没有vim的。这个时候需要手动安装:

1
2
apt-get update
apt-get install vim

这样就可以啦~

查看正在运行的容器

1
2
3
4
docker ps

CONTAINER ID IMAGE COMMAND ...
5917eac21c36 ubuntu:15.10 "/bin/sh -c 'while t…" ...

输出详情介绍:

CONTAINER ID: 容器 ID。

IMAGE: 使用的镜像。

COMMAND: 启动容器时运行的命令。

CREATED: 容器的创建时间。

STATUS: 容器状态。

状态有7种:

  • created(已创建)
  • restarting(重启中)
  • running 或 Up(运行中)
  • removing(迁移中)
  • paused(暂停)
  • exited(停止)
  • dead(死亡)

PORTS: 容器的端口信息和使用的连接类型(tcp\udp)。

NAMES: 自动分配的容器名称。

查看容器日志

1
docker logs 2b1b7a428627

image.png

停止容器

1
docker stop amazing_cori

重启容器

1
docker stop amazing_cori

进入容器

1
docker attach 1e560fca3906
1
2
3
4
5
6
7
8
9
10
11
Microsoft Windows [版本 10.0.19042.804]
(c) 2020 Microsoft Corporation. 保留所有权利。

C:\Users\hz21087108>docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8b07524256a1 ubuntu "bash" 22 hours ago Up 22 hours vigorous_banach

C:\Users\hz21087108>docker attach 8b07524256a1
root@8b07524256a1:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var
root@8b07524256a1:/#

注意: 如果从这个容器退出,会导致这个容器停止(Ctl + D)

exec 命令

使用容器执行命令

1
docker exec -it 243c32535da7 bash

注意: 如果从这个容器退出,容器不会停止。所以进入容器的时候要使用exec 而不是 attach

删除容器

1
docker rm -f 1e560fca3906

-f: --force的缩写,强制删除。

清理掉所有处于终止状态的容器

1
docker container prune
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
C:\Users\hz21087108>docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
8b07524256a17d161e34d3c4577c3d59513e1d45028ac942e763170140b72867
a61ede1df2433c977135f84fb5f5616040c8be1d1666958f8fe61164e38764ab
c866412441ac925395e7a874d28b0e7ff1578b13ca905b657b7dc2f71b120330
cc2bf6cb885b24df037e3022a8e27bd7682cdcd38a5b8d62f5b0b7bc06064a64
c0e18126807e507b9a46107cdebbcf851795c2a2a32016d1a37bbfac0f31ee8b
c75514cf5a47ac346de4e912151b761daf50f5494c5412d63a748c94cbf47128
f34c9768ab1721aa297f3917280b6b9646cfe1655145c6a54bcf6684ae80ef0d

Total reclaimed space: 2.906kB

C:\Users\hz21087108>docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

C:\Users\hz21087108>

查看web容器信息

1
2
docker ps
docker ps -a

-a 是显示全部容器,包括不在运行中的

查看指定容器的端口映射

1
2
docker port bf08b7f2cd89
5000/tcp -> 0.0.0.0:5000

查看web应用程序的日志

docker logs [容器ID或者名字] 可以查看容器的标准输出, -f 参数可以让容器log标准输出。

CSS属性顺序

推荐样式书写顺序
  1. Position
  2. Box Model
  3. Typographic
  4. Visual

由于定位(positioning)可以从正常的文档流中移除元素,并且还能覆盖盒模型(box model)相关的样式,因此排在首位。盒模型决定了组件的尺寸和位置,因此排在第二位。

其他属性只是影响组件的内部(inside)或者是不影响前两组属性,因此排在后面:

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
29
.declaration-order {
/* Positioning */
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 100;

/* Box-model */
display: block;
float: right;
width: 100px;
height: 100px;

/* Typography */
font: normal 13px "Helvetica Neue", sans-serif;
line-height: 1.5;
color: #333;
text-align: center;

/* Visual */
background-color: #f5f5f5;
border: 1px solid #e5e5e5;
border-radius: 3px;

/* Misc */
opacity: 1;
}

链接的样式请严格按照如下顺序添加:

1
a:link -> a:visited -> a:hover -> a:active(LoVeHAte)

规则示例:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
.cl {
display: ;
visibility: ;
float: ;
clear: ;

position: ;
top: ;
right: ;
bottom: ;
left: ;
z-index: ;

width: ;
min-width: ;
max-width: ;
height: ;
min-height: ;
max-height: ;
overflow: ;

margin: ;
margin-top: ;
margin-right: ;
margin-bottom: ;
margin-left: ;

padding: ;
padding-top: ;
padding-right: ;
padding-bottom: ;
padding-left: ;

border-width: ;
border-top-width: ;
border-right-width: ;
border-bottom-width: ;
border-left-width: ;

border-style: ;
border-top-style: ;
border-right-style: ;
border-bottom-style: ;
border-left-style: ;

border-color: ;
border-top-color: ;
border-right-color: ;
border-bottom-color: ;
border-left-color: ;

outline: ;

list-style: ;

table-layout: ;
caption-side: ;
border-collapse: ;
border-spacing: ;
empty-cells: ;

font: ;
font-family: ;
font-size: ;
line-height: ;
font-weight: ;
text-align: ;
text-indent: ;
text-transform: ;
text-decoration: ;
letter-spacing: ;
word-spacing: ;
white-space: ;
vertical-align: ;
color: ;

background: ;
background-color: ;
background-image: ;
background-repeat: ;
background-position: ;

opacity: ;

cursor: ;

content: ;
quotes: ;
}

CSS笔记

1. 自定义字体

有的时候系统自带的字体并不能满足我们的字体。有时候我们会在网页上放一些比较炫酷的字体。但是怎么在网页上显示呢?


把使用的字体下载下来,然后找一个字体转化的官网,生成不同所需要的字体格式。推荐一个字客
把生成的字体下载下来。在css文件里写上如下代码:

1
2
3
4
5
6
7
8
@font-face {
font-family: 'Monotype Corsiva';
src: url('../font/Monotype Corsiva.eot'); /* IE9 Compat Modes */
src: url('../font/Monotype Corsiva.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('../font/Monotype Corsiva.woff') format('woff'), /* Modern Browsers */
url('../font/Monotype Corsiva.ttf') format('truetype'), /* Safari, Android, iOS */
url('../font/Monotype Corsiva.svg#Monotype Corsiva') format('svg'); /* Legacy iOS */
}

其中 ==../font/Monotype Corsiva.eot== 是我的字体文件路径。
==font-family== 写的是字体名称。

2. 自定义滚动条样式

有时候我不不太喜欢浏览器自带的滚动条样式,大大的影响了网页的美观。那么怎么去自定义浏览器滚动条样式呢?

1. Chrom 浏览器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   ::-webkit-scrollbar
{
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track-piece
{
background-color: #CCCCCC;
-webkit-border-radius: 6px;
}
::-webkit-scrollbar-thumb:vertical
{
height: 5px;
background-color: #999999;
-webkit-border-radius: 6px;
}
::-webkit-scrollbar-thumb:horizontal
{
width: 5px;
background-color: #CCCCCC;
-webkit-border-radius: 6px;
}
2. IE 浏览器
1
2
3
4
5
6
7
8
9
10
   /*滚动条*/
body{overflow-y:auto;overflow-x:hidden;height:580px;
scrollbar-arrow-color:#302D30; /*三角箭头的颜色*/
scrollbar-face-color:#000; /*立体滚动条的颜色(包括箭头部分的背景色)*/
scrollbar-3dlight-color:#302D30; /*立体滚动条亮边的颜色*/
scrollbar-highlight-color:#302D30; /*滚动条的高亮颜色(左阴影?)*/
scrollbar-shadow-color:#302D30; /*立体滚动条阴影的颜色*/
scrollbar-darkshadow-color:#302D30; /*立体滚动条外阴影的颜色*/
scrollbar-track-color:#302D30; /*立体滚动条背景颜色*/
scrollbar-base-color:#302D30; /*滚动条的基色*/}

3. CSS3 box-flex 属性

子元素平分父元素的宽度:

定义两个可伸缩的 p 元素。如果父元素的总宽度是 300 像素,则 #p1 的宽度是 100 像素,而 #p2 的宽度是 200 像素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#p1
{
-moz-box-flex:1.0; /* Firefox */
-webkit-box-flex:1.0; /* Safari 和 Chrome */
box-flex:1.0;
border:1px solid red;
}

#p2
{
-moz-box-flex:2.0; /* Firefox */
-webkit-box-flex:2.0; /* Safari 和 Chrome */
box-flex:2.0;
border:1px solid blue;
}


4. 只显示三行文本,多余的显示…
1
2
3
4
 display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;

React.child.children 的用法 笔记

H5输入框被软键盘遮挡解决办法

当input或textarea获得焦点的时候,有可能会被软键盘遮挡住。解决办法如下:

1
2
3
4
5
6
7
8
window.addEventListener('resize', function () {
if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') {
window.setTimeout(function () {
let doc: any = document
doc.activeElement.scrollIntoViewIfNeeded()
}, 0)
}
})

JQuery源码之自调用匿名函数

JQuery的自调用匿名函数如下:

1
2
3
4
5
(function(window,undefind){
var jQuery = ...
//...
window.jQuery = window.$ = jQuery;
})(window)

1. 为什么要创建这样一个自调用匿名函数?

目的是创建一个特殊的函数作用域,防止 ==命名冲突==

2. 为什么要为自调用匿名函数设置参数window,并传入window对象?

是为了让window变成局部变量。这样在jQuery访问window对象时,不用把作用域链回退到顶层作用域。

3. 为什么要为自调用匿名函数设置参数undefined?

特殊值undefined是window对象的一个属性。执行以下语句会弹出 true:

1
alert("undefined" in window)

通过把参数undefined作为局部变量使用,但是又不传入任何值,可以缩短查找undefined时的作用域链

另外,通过这种方式可以确保undefined的值是undefined,因为undefined有可能会辈重写为新的值。

1
2
undefined = "now its defined";
alert(undefined);

IDEA 光标类的操作

1、Ctrl+Alt+Left/Right 光标定位到上一个/下一个浏览位置处;

2、Ctrl+Shift+Backspace 光标定位到上次修改的地方;

3、Alt+Up/Down 移动光标到上一个/下一个方法开始处;

4、Ctrl+Left/Right 移动光标到前一个/后一个单词处,加 Shift 键,产生选中效果;

5、Ctrl+[/] 移动光标到前/后代码块,加 Shift 键,产生选中效果;

6、Ctrl+W 按语法选中代码,连续按会有其他效果,加 Shift 键,产生反向选中效果;

7、F2 快速定位到下一个错误和警告处,加 Shift 键,定位到上一个错误处;

8、Ctrl+”+/-“ 当前方法展开/折叠,加 Shift 键,全部方法展开/折叠;

9、Ctrl+Alt+T 在选中代码情况下,Surround With(if/else/try catch…) 快速包裹代码;

10、Ctrl+Alt+V 提出选中内容为局部变量。

React npm 环境搭建

1. 安装淘宝镜像

1
2
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
$ npm config set registry https://registry.npm.taobao.org

2. 使用 create-react-app 快速构建 React 开发环境

1
2
3
4
$ cnpm install -g create-react-app
$ create-react-app my-app
$ cd my-app/
$ npm start

3. 使用外部React组件

test.js

1
2
3
4
5
6
7
import React, { Component } from 'react';
class Test extends Component{
render () {
return (<div>1234</div>)
}
}
export default Test;

index.js

1
2
3
4
5
6
7
8
9
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import Test from './test';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<Test />, document.getElementById('root'));
registerServiceWorker();