背景:

  1. 希望利用 css 变量实现 dark 和 light 模式的切换
  2. 原有的工程都是 less 形式定义的 css,并且还有 less 的函数,比如 fade 等,不想手动改 less 的函数,希望该插件能支持解析 less 函数
  3. 需要支持局部不切换模式,比如某个区域是固定的 light 模式

步骤

第一步:less 变量转换成 css 变量

这一步比较简单,less 已经提供了字段用于转换,只需要添加一个配置项就可以,就是globalVars属性。

可以查看example 代码参考

1
2
3
4
5
6
{
loader: 'less-loader',
options: {
globalVars: LessGlobalCSSVars,
}
}

LessGlobalCSSVars大概长这个样子

1
2
3
4
5
{
"bg-body": "var(--bg-body)",
"static-white": "var(--static-white)",
...
}
阅读全文 »

React 性能优化思路

我觉得React 性能优化的理念的主要方向就是这两个:

  1. 减少重新 render 的次数。因为在 React 里最重(花时间最长)的一块就是 reconciliation(简单的可以理解为 diff),如果不 render,就不会 reconciliation。
  2. 减少计算的量。主要是减少重复计算,对于函数式组件来说,每次 render 都会重新从头开始执行函数调用。

在使用类组件的时候,使用的 React 优化 API 主要是:shouldComponentUpdatePureComponent,这两个 API 所提供的解决思路都是为了减少重新 render 的次数,主要是减少父组件更新而子组件也更新的情况。

但是在函数式组件里面没有声明周期也没有类,那如何来做性能优化呢?

先分个类,组件什么时候会重新执行?

  1. 组件自己的状态改变
  2. 父组件重新渲染,导致子组件重新渲染,但是父组件的 props 没有改变
  3. 父组件重新渲染,导致子组件重新渲染,但是父组件传递的 props 改变

针对第二点,在FC中,可以通过memo减少rerender

1
2
3
4
function Component(props) {
/* 使用 props 渲染 */
}
const MyComponent = React.memo(Component);

通过 React.memo 包裹的组件在 props 不变的情况下,这个被包裹的组件是不会重新渲染的(相当于PureComonent)

默认情况下其只会对 props 的复杂对象做浅层对比(浅层对比就是只会对比前后两次 props 对象引用是否相同,不会对比对象里面的内容是否相同),如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现。

阅读全文 »

TypeScript简介

  1. TypeScript是JavaScript的超集。
  2. 它对JS进行了扩展,向JS中引入了类型的概念,并添加了许多新的特性。
  3. TS代码需要通过编译器编译为JS,然后再交由JS解析器执行。
  4. TS完全兼容JS,换言之,任何的JS代码都可以直接当成JS使用。
  5. 相较于JS而言,TS拥有了静态类型,更加严格的语法,更强大的功能;TS可以在代码执行前就完成代码的检查,减小了运行时异常的出现的几率;TS代码可以编译为任意版本的JS代码,可有效解决不同JS运行环境的兼容问题;同样的功能,TS的代码量要大于JS,但由于TS的代码结构更加清晰,变量类型更加明确,在后期代码的维护中TS却远远胜于JS。

TypeScript 开发环境搭建

下载Node.js
安装Node.js
使用npm全局安装typescript
  • 进入命令行
  • 输入:npm i -g typescript
创建一个ts文件
使用tsc对ts文件进行编译
  • 进入命令行
  • 进入ts文件所在目录
  • 执行命令:tsc xxx.ts
阅读全文 »

hooks的作用

钩子(hook)就是 React 函数组件的副效应解决方案,用来为函数组件引入副效应。 函数组件的主体只应该用来返回组件的 HTML 代码,所有的其他操作(副效应)都必须通过钩子引入。

由于副效应非常多,所以钩子有许多种。React 为许多常见的操作(副效应),都提供了专用的钩子。

  • useState():保存状态
  • useContext():保存上下文
  • useRef():保存引用
  • ……

上面这些钩子,都是引入某种特定的副效应,而 useEffect()是通用的副效应钩子 。找不到对应的钩子时,就可以用它。其实,从名字也可以看出来,它跟副效应(side effect)直接相关。

阅读全文 »

Redux

设计思想

  • (1)Web 应用是一个状态机,视图与状态是一一对应的。
  • (2)所有的状态,保存在一个对象里面。

Action

  1. Action 就是 View 发出的通知,表示 State 应该要发生变化了。

  2. Action 是一个对象,其中的type属性是必须的,表示 Action 的名称,Action 描述当前发生的事情。

  3. 改变 State 的唯一办法,就是使用 Action。

可以定义一个函数来生成 Action,这个函数就叫 Action Creator,示例如下

1
2
3
4
5
6
7
8
9
10
const ADD_TODO = '添加 TODO';

function addTodo(text) {
return {
type: ADD_TODO,
text
}
}

const action = addTodo('Learn Redux');
阅读全文 »

chrome浏览器架构

Chrome 采用多进程架构,其顶层存在一个 Browser process 用以协调浏览器的其它进程。

  • Browser Process:
  1. 负责包括地址栏,书签栏,前进后退按钮等部分的工作;

  2. 负责处理浏览器的一些不可见的底层操作,比如网络请求和文件访问;

  • Renderer Process:
  1. 负责一个 tab 内关于网页呈现的所有事情
  • Plugin Process:
  1. 负责控制一个网页用到的所有插件,如 flash
  • GPU Process
  1. 负责处理 GPU 相关的任务

image-20210930012419754

由于一个tab标签页都有一个独立的渲染进程,所以一个tab异常崩溃后,其他tab不会受到影响。

一个渲染进程包括

  • JS引擎线程
  • HTTP请求线程
  • 定时触发线程
  • 事件触发线程
  • GUI线程

浏览器JS异步执行原理

执行JS代码的线程只有一个,是浏览器提供的JS引擎线程,浏览器中还有

image-20210930012033902

阅读全文 »

什么是函数式编程?

函数式编程具有五个鲜明的特点。

1. 函数是”第一等公民”

指的是函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。

举例来说,下面代码中的print变量就是一个函数,可以作为另一个函数的参数。

1
2
3
  var print = function(i){ console.log(i);};

  [1,2,3].forEach(print);

2. 只用”表达式”,不用”语句”

“表达式”(expression)是一个单纯的运算过程,总是有返回值;”语句”(statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。

原因是函数式编程的开发动机,一开始就是为了处理运算(computation),不考虑系统的读写(I/O)。”语句”属于对系统的读写操作,所以就被排斥在外。

当然,实际应用中,不做I/O是不可能的。因此,编程过程中,函数式编程只要求把I/O限制到最小,不要有不必要的读写行为,保持计算过程的单纯性。

阅读全文 »

组件通信

爷孙组件通信

爷孙组件通信主要有 3 种方式:

  1. 将孙子组件的 props 封装在一个固定字段中
  2. 通过 children 透传
  3. 通过 context 传递

假设有个三层组件,爷爷分别给儿子和孙子发红包

先看青铜解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Grandpa() {
const [someMoneyForMe] = useState(100);
const [someMoneyForDaddy] = useState(101);
return <Daddy money={someMoneyForDaddy} moneyForSon={someMoneyForMe} />;
}
function Daddy(props: { money: number; moneyForSon: number }) {
const { money, moneyForSon } = props;
return (
<div className="daddy">
<h2>This is Daddy, received ${money}</h2>
<Me money={moneyForSon} />
</div>
);
}
function Me(props: { money: number }) {
const { money } = props;
return (
<div className="son">
<h3>This is Me, received ${money}</h3>
</div>
);
}

Daddy 组件会透传爷爷给孙子的组件给 Me。这种方案的缺点很明显,以后爷爷要给 Daddy 和 Me 发糖果的时候,Daddy 还得加字段。

将孙子组件的 props 封装在一个固定字段中

按照 1 的方案,我们可以固定给 Daddy 添加一个 sonProps 的字段,然后将 Grandpa 需要传给孙子的状态全部通过 sonProps 传递

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function Grandpa() {
const [someMoneyForMe] = useState(100);
const [someMoneyForDaddy] = useState(101);
return <Daddy money={someMoneyForDaddy} sonProps={{money: someMoneyForMe}} />;
}
function Daddy(props: { money: number; sonProps: Parameters<typeof Me>[0]; }) {
const { money, sonProps } = props;
return (
<div className="daddy">
<h2>This is Daddy, received ${money}</h2>
<Me {...sonProps}/>
</div>
);
}
function Me(props: { money: number }) {
const { money } = props;
return (
<div className="son">
<h3>This is Me, received ${money}</h3>
</div>
);
}

这样以后要给 Me 加字段,就不用改 Daddy 了。但要测试 Daddy 时还得 mock Me 组件的数据,Daddy 和 Son 耦合。

通过 children 透传

children 类似于 vue 中的 slot,可以完成一些嵌套组件通信的功能

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
function Grandpa() {
const [someMoneyForMe] = useState(100);
const [someMoneyForDaddy] = useState(101);
return (
<Daddy money={someMoneyForDaddy}>
<Me money={someMoneyForMe} />
</Daddy>
);
}
function Daddy(props: { money: number; children?: React.ChildNode }) {
const { money, children } = props;
return (
<div className="daddy">
<h2>This is Daddy, received ${money}</h2>
{children}
</div>
);
}
function Me(props: { money: number }) {
const { money } = props;
return (
<div className="son">
<h3>This is Me, received ${money}</h3>
</div>
);
}

将 Daddy 的嵌套部分用 children 替代后,解耦了子组件和孙子组件的依赖关系,Daddy 组件更加独立。

作为替代,也可以传递一个组件实例:

三种方案的决策

  1. 第一种方案一般用于固定结构和跨组件有互相依赖的场景,多见于 UI 框架中的复合组件与原子组件的设计中
  2. 第二种常用在嵌套层级不深的业务代码中,比如表单场景。优点是顶层 Grandpa 的业务收敛度很高,一眼能看清 UI 结构及状态绑定关系,相当于拍平了 React 组件树
  3. 第三种比较通用,适合复杂嵌套透传场景。缺点是范式代码较多,且会造成 react dev tools 层级过多;Context 无法在父组件看出依赖关系,必须到子组件文件中才能知道数据来源

React DevTools

General 设置

image-20220222024702149.png

General 面板中最重要的功能就是 **”Highlight updates when components render”**。勾选上之后,可以查看 React 重绘时,页面哪些部分有更新。在遇到性能问题时,可以快速帮助决策在哪部分不需要重绘的组件部分添加 React.memo 阻止重绘。

Debugging 设置

image-20220222024832097

“Append components stacks to console warnings and errors.”

可以帮助我们定位 React 报错信息来自哪个组件

阅读全文 »

React的设计思路

  • 解决UI编程的痛点

    1. 状态更新,UI不会自动更新,需要手动调dom api进行更新

      1. 欠缺基本代码的封装和隔离,代码层面没有组件化
      2. UI之间数据依赖,需要手动维护,如果依赖链路过长,会有 callback hell的问题
  • 响应式编程

image-20220210203412954

  • 期望能

    1. 状态更新,UI自动更新
    2. 前端代码组件化,可复用,可封装
    3. 状态之间的依赖关系,只需要声明即可
    
  • 组件化

    1. 组件是组件的组合/原子组件
    2. 组件内拥有状态,外部不可见
    3. 父组件可将状态传入组件内部
    
  • 状态归属问题

    1. react是单向数据流,只能父组件将状态传递给子组件(但可以将改变父组件状态的方法传递给子组件实现父子组件通信)
    2. 如何解决不合理的状态上升问题?
    3. 组件状态改变后,如何更新dom
    
阅读全文 »
0%