React入门笔记

本文介绍了React的基本概念,包括JSX语法、虚拟DOM、函数组件和类组件的工作原理,以及组件的render方法和生命周期。通过实例展示了JSX如何转换为JavaScript,并解释了虚拟DOM在React中的作用。对于初学者,这是一篇很好的React入门教程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近开始学 React,发现与 Vue 和 Angular 相比,React 非常容易上手且实现原理简单。在此记录一下学习 React 的心路历程:

JSX 语法

这是一种非常棒的语法,可以用 JS 来表达 HTML,虽说是一种语法糖,但是甜得大家不要不要的,其实只要知道 jsx 最终会被 babel 转义成标准 JS 语法即可,例如:

const title = <h1 className="title">Hello, world!</h1>;

打开 Babel REPL 输入上面代码,发现最终被转换成下面这个样子:

const title = React.createElement(
    'h1', // 标签名
    { className: 'title' }, // 属性对象
    'Hello, world!' // 子元素
);

到这里,大家应该大概知道其结构了,为了加深理解,我们再来一个复杂点的案例:

const element = (
  <div className="title" width="200" height="100">
    hello<span className="content">world!</span>
  </div>
)

被转换为:

const element = React.createElement(
  "div", // 标签名
  { // 属性对象
    className: "title",
    width: "200",
    height: "100",
  }, 
  "hello", // 第一个子元素
  React.createElement( // 第二个子元素
    "span",
    { className: "content" },
    "world!"
  )
);

可以看出来,jsx 的表现力是非常强的,语法简洁,是前端界的一大杀器。

虚拟 DOM

我们已经知道,jsx 片段会被转译成用 React.createElement 方法包裹的代码,那 React.createElement 方法究竟是什么呢?其实很简单,就是把参数转换为一个对象而已:

function createElement(type, attrs, ...children) {
  return { type, props: { attrs, children } }
}
  • 第一个参数是 DOM 节点的标签名,它的值可能是 div,h1,span 等
  • 第二个参数是一个对象,里面包含了节点所有的属性,可能包含了 className,id 等
  • 从第三个参数开始,就是它的子节点

所以,jsx 最终会被转化成拥有这种结构的对象而已,这种结构的对象还有一个非常牛逼的称呼:虚拟 DOM!

const title = <h1 className="title">Hello, world!</h1>;

其实就是:

{
  type: 'h1',
  props: { attrs: { className: 'title' }, children: [ 'Hello, world!' ] }
}

当只有一个子节点的时候,通常 children 就不写成数组了,而是直接取数组中的第一个元素,即:

{
  type: 'h1',
  props: { attrs: { className: 'title' }, children: 'Hello, world!' }
}

所以,我们的 React.createElement 方法可以稍微改一下:

function createElement(type, attrs, children) {
  return {
    type,
    props: {
      attrs,
      children: arguments.length > 3 ? Array.prototype.slice.call(arguments, 2) : children,
    },
  }
}

函数组件

函数组件就是一个接收属性对象并返回一个 React 元素的函数。例如:

function Title(props) {
  return <h1 className="title">标题</h1>
}

这个定义已经非常清楚了,但是注意下面的函数也是函数组件:

function MyTitle(props) {
  return <Title />
}

因为 <Title /> 这种语法相当于执行了 Title 函数,得到的仍然是一个 React 元素。

类组件

类组件就是一个实现了 render 方法的 React.Component 子类

class TitleComponent extends React.Component {
  render() {
    return <h1 className="title">标题</h1>
  }
}

你可能会问,React.Component 父类长啥样啊,看下 TypeScript 类型声明就知道了:

class Component<P, S> {
  readonly props: Readonly<P> ;
  state: Readonly<S>;

  constructor(props: Readonly<P> | P);
  setState<K extends keyof S>(
    state: ((prevState: Readonly<S>, props: Readonly<P>) => (Pick<S, K> | S | null)) | (Pick<S, K> | S | null),
    callback?: () => void
  ): void;
  forceUpdate(callback?: () => void): void;
  render(): ReactNode;
}

React.Component 父类结构如下:

class Component {
  constructor(props) {
    this.props = props
    this.state = {}
  }

	// 状态更新
  setState(nextState, callback) {
    // ...
  }

  // 强制刷新
  forceUpdate() {
    // ...
  }

  render() {
    throw new Error('此方法为抽象方法,需要子类实现')
  }
}

组件

组件分为内置原生组件和自定义组件:

  • 内置组件是 div、span 等合法 HTML 标签,其虚拟 DOM 中的 type 是字符串
  • 自定义组件就是函数或类组件,其虚拟 DOM 中的 type 不再是字符串了,而是函数

Render 方法

render 方法的作用是把虚拟 DOM 变成真实 DOM 并插入到容器内部,函数结构为:

function render(vdom, container) {
  // ...
}

语法如下:

ReactDOM.render(
    <h1>Hello, world!</h1>,
    document.getElementById('root')
);

那 render 函数内部究竟做了什么黑魔法,竟然能够把虚拟 DOM 映射到真实 DOM 呢?还记得虚拟 DOM 的数据结构吗?

{ type: 'xxx', props: { attrs: {}, children: [] }}

其实很简单,就几行代码就可以实现了:

function render(vdom, container) {
  if (typeof vdom === 'string') { // 当vdom为字符串时,渲染结果是一段文本
    return container.appendChild(document.createTextNode(vdom))
  }
  const {type, props: {attrs = {}, children}} = vdom
  const dom = document.createElement(type)
  Object.keys(attrs).forEach(key => {
    const value = attrs[key]
    if (key === 'style') {
      for (let attr in value) dom.style[attr] = value[attr]
    } else {
      dom[key.startsWith('on') ? key.toLocaleLowerCase() : key] = value
    }
  });
  [].concat(children).forEach(child => render(child, dom)) // 递归渲染子节点
  return container.appendChild(dom) // 将渲染结果挂载到真正的DOM上
}

生命周期

生命周期分旧版和新版,各有一张图来描述,下面是旧版生命周期:

lifecycle

分为了初始化、挂载、更新和卸载四个阶段,还是比较清晰的,而新版生命周期把除了 componentWillUnmount 之外的所有含有 will 的函数都去掉了,例如:

  • componentWillMount

  • componentWillReceiveProps

  • componentWillUpdate

增加了:

  • getDerivedStateFromProps 静态方法

  • getSnapshotBeforeUpdate 实例方法

下面的是新版声明周期示意图:

lifecycle-new

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值