前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布

React

作者头像
晚上没宵夜
发布2022-11-14 20:12:21
2.1K0
发布2022-11-14 20:12:21
举报

来点前端

1. 介绍

React 是一个用于构建用户界面的 JavaScript 库

空模板展示

代码语言:javascript
复制
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Hello World</title>
    <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
  </head>
  <body>
    <div id="root"></div>

      
      
    <!-- 开始写脚本 -->
    <script type="text/babel">
    
    // React 将替换 DOM 容器中的任何现有内容,所以建议为空
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(<h1>Hello, world!</h1>);

    </script>
    <!-- 结束写脚本 -->
  </body>
</html>

2. JSX

被称为 JSX,它是 JavaScript 的语法扩展,JSX 产生 React “元素”,可渲染到 DOM 中

代码语言:javascript
复制
const element = <h1>Hello, world!</h1>;

const element = (
  <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

JSX 中嵌入表达式

任何有效的 JavaScript 表达式 放在 JSX 中的花括号内解析

代码语言:javascript
复制
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;

JSX 也是一个表达式,编译后成为常规的 javascript 函数调用并计算为 javascript 对象,意味着可在 if、for 中使用,将其分配给变量,或作为参数接受,或函数中返回

代码语言:javascript
复制
function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}

JSX 是一个语法糖,Babel 将 JSX 转成 React.createElement() 产生 React element,React 读取这个对象来构造 DOM

代码语言:javascript
复制
// 语法糖
const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);

// 创建 React element
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

// React 对象
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world!'
  }
};

2. 渲染元素

React element 描述了在屏幕上展示的内容

代码语言:javascript
复制
const element = <h1>Hello, world</h1>;

React DOM 负责更新浏览器 DOM 以匹配 React 元素

渲染一个 React 元素,首先要将 DOM 元素传递给 ReactDOM.createRoot() 创建出 React DOM 元素(root),然后再将 React 元素传递给 root.render()

代码语言:javascript
复制
const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <h1>Hello, world</h1>;
root.render(element);

React 元素是不可变的,一旦你创建了一个元素,你就不能改变它的子元素或属性。目前更新的方法是创建新元素并传递给 root.render()

代码语言:javascript
复制
const root = ReactDOM.createRoot(
  document.getElementById('root')
);

function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  root.render(element);
}

setInterval(tick, 1000);

React 只会更新必要的部分,React DOM 比较元素及其子元素内容先后的不同,而在渲染过程中只会更新改变了的部分

3. 组件

组件在概念上类似于 JavaScript 函数,它接受任意的入参(即 “props”),并返回 React 元素用于描述页面展示内容

代码语言:javascript
复制
// DOM 标签(非组件)
const element = <div />;

// 下面二者是等效的
// 函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// 标签组件,自定义标签为组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(element);

组件名为大写,JSX 属性为小写驼峰

组合组件

代码语言:javascript
复制
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

函数也作为标签使用

props 的只读性,所有 React 组件都必须像纯函数一样保护它们的 props 不被更改,提供了 state 来动态变化

代码语言:javascript
复制
function sum(a, b) {
  return a + b;
}

function change(a, b) {
    a = b;
    return a + b;
}

4. state & 生命周期

React 元素是不可变的,但我们希望只编写一次代码就可以实现组件自动更新,需要借助 state 来实现(State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件,state 变更会调用 render方法),首要要先函数组件转成类组件:

  1. 创建一个同名的 ES6 class,并且继承于 React.Component
  2. 添加一个空的 render() 方法
  3. 将函数体移动到 render() 方法之中
  4. render() 方法中使用 this.props 替换 props
  5. 删除剩余的空函数声明
代码语言:javascript
复制
class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

每次组件更新时 render 方法都会被调用

生命周期方法

代码语言:javascript
复制
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);

不要直接修改 state:

代码语言:javascript
复制
// Wrong
this.state.comment = 'Hello';

// Correct
// 应该使用 setState():
this.setState({comment: 'Hello'});

State 的更新可能是异步的,可能会把多个 setState() 调用合并成一个调用

代码语言:javascript
复制
// Wrong
// 多个setState调用会合并,结果可能不正确
this.setState({
  counter: this.state.counter + this.props.increment,
});


// Correct
// 改成函数即可
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

state的更新合并

代码语言:javascript
复制
// 里面有 posts、comments
constructor(props) {
    super(props);
    this.state = {
        posts: [],
        comments: []
    };
}

// 只会更新 comments、posts还是存在的 
this.setState({comments}

5. 事件处理

React 元素的事件需要传入的一个字符串函数解析,而传统的 html 是传入一个方法调用

代码语言:javascript
复制
<button onclick="activateLasers()">
  Activate Lasers
</button>

// 
<button onClick={activateLasers}>
  Activate Lasers
</button>
代码语言:javascript
复制
class Toggle extends React.Component {
    constructor(props) {
        super(props);
        this.state = {isToggleOn: true};

        // 为了在回调中使用 `this`,这个绑定是必不可少的
        this.handleClick = this.handleClick.bind(this);
  	}
    
    handleClick() {
        // xxx
    }
      
  	render() {
    	return (
            // 这里的this.handleClick  或者 handleClick()
            // 但 react 用前者,所以得绑定
      		<button onClick={this.handleClick}></button>
    	);
  	}
}

小写驼峰命名法 class 的方法不会绑定 this,没有绑定而传入了 onClick,调用时 this 为 undefined

向事件处理函数传递参数

代码语言:javascript
复制
// 显隐式的传递 e 事件
// this 的 react 方式隐式传递了 event 
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

6. 条件渲染

if 语句

代码语言:javascript
复制
function UserGreeting(props) {
  return <h1>Welcome back!</h1>;
}

function GuestGreeting(props) {
  return <h1>Please sign up.</h1>;
}

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

const root = ReactDOM.createRoot(document.getElementById('root')); 

// 直接传参
root.render(<Greeting isLoggedIn={false} />);

元素变量判断

代码语言:javascript
复制
render() {
    const isLoggedIn = this.state.isLoggedIn;
    let button;
    if (isLoggedIn) {
        button = <LogoutButton onClick={this.handleLogoutClick} />;
    } else {
        button = <LoginButton onClick={this.handleLoginClick} />;
    }

    return (
        <div>
        	{button}
        </div>
    );
}

&& 内联判断

代码语言:javascript
复制
function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          You have {unreadMessages.length} unread messages.
        </h2>
      }
    </div>
  );
}

const messages = ['React', 'Re: React', 'Re:Re: React'];

const root = ReactDOM.createRoot(document.getElementById('root')); 
root.render(<Mailbox unreadMessages={messages} />);

JavaScript 中:true && expression 返回 expression, false && expression 返回 false

三目运算符

代码语言:javascript
复制
render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      {isLoggedIn
        ? <LogoutButton onClick={this.handleLogoutClick} />
        : <LoginButton onClick={this.handleLoginClick} />
      }
    </div>
  );
}

阻止组件渲染

代码语言:javascript
复制
function WarningBanner(props) {
  if (!props.warn) {
    return null;
  }

  return (
    <div className="warning">
      Warning!
    </div>
  );
}

返回 null,不会影响生命周期,componentDidUpdate 还会继续执行

7. list & key

数组转为元素列表

代码语言:javascript
复制
// 使用 {} 在 JSX 内构建一个元素集合
const numbers = [1, 2, 3, 4, 5];
    
function NumberList(props) {
    const numbers = props.numbers;
    const listItems = numbers.map((item) =>
    	<li key={number.toString()>		// 为每个列表元素分配一个key
          {item}						// 解析
    	</li>
    );
    return (
        <ul>{listItems}</ul>			// 解析-构建元素集合
    );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<NumberList numbers={numbers} />);

a key should be provided for list items(创建一个元素时必须包括一个特殊的 key 属性) key 帮助 React 识别哪些元素改变了,比如被添加或删除,因此要为数组中的每一个元素赋予一个确定的标识

列表的 key

代码语言:javascript
复制
// key 是在该列表中唯一标识,通常选择数据的id,当没有时可以使用index代替
const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

// 万不得已才使用
// 列表顺序可能变化了,这将导致错误状态
const todoItems = todos.map((todo, index) =>
  <li key={index}>
    {todo.text}
  </li>
);

key 会传递信息给 React ,但不会传递给你的组件。如果你的组件中需要使用 key 属性的值,请用其他属性名显式传递这个值

8. 表单

受控组件

代码语言:javascript
复制
class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  // evnet.target
  handleChange(event) {
    this.setState({value: event.target.value});
  }

  // 阻止跳转
  handleSubmit(event) {
    alert('提交的名字: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          名字:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

textarea 标签

代码语言:javascript
复制
// 原本 <textarea> 元素通过其子元素定义其文本
// 而 react 中可使用 value 属性
class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: '请撰写一篇关于你喜欢的 DOM 元素的文章.'
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('提交的文章: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          文章:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

select 标签

代码语言:javascript
复制
// 传统的在 option 里面有个 selected 属性
<select>
  <option value="grapefruit">葡萄柚</option>
  <option value="lime">酸橙</option>
  <option selected value="coconut">椰子</option>
  <option value="mango">芒果</option>
</select>


// 而 React 在根标签 select 上使用 value 属性
class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('你喜欢的风味是: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          选择你喜欢的风味:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">葡萄柚</option>
            <option value="lime">酸橙</option>
            <option value="coconut">椰子</option>
            <option value="mango">芒果</option>
          </select>
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

9. 状态提升

两个组件需要数据同步时,将 state 提升到父组件(此时调用将变成 this.props.attr),因为父组件会将参数 props 传递给子组件。又因为 state 是私有的,且提升后属于父组件,不受子组件控制,此时子组件想要改变父组件的 state 只能依靠 父组件将 setState 方法包装成函数通过 props 传递给子组件调用

代码语言:javascript
复制
class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.props.onTemperatureChange(e.target.value);
  }

  render() {
    const temperature = this.props.temperature;
    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}
代码语言:javascript
复制
class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};
  }

  handleCelsiusChange(temperature) {
    this.setState({scale: 'c', temperature});
  }

  handleFahrenheitChange(temperature) {
    this.setState({scale: 'f', temperature});
  }

  render() {
    const scale = this.state.scale;
    const temperature = this.state.temperature;
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

    return (
      <div>
        <TemperatureInput
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange} />
        <TemperatureInput
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChange} />
        <BoilingVerdict
          celsius={parseFloat(celsius)} />
      </div>
    );
  }
}

10. 组合关系

包含关系

代码语言:javascript
复制
// 展示的是
// Welcome
// Thank you for visiting our spacecraft!
function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
}


function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        Welcome
      </h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!
      </p>
    </FancyBorder>
  );
}

porps.children 也是一个保留字段,里面有该标签中的所有内容(包括属性、子元素、文本)

也可不使用 children 属性

代码语言:javascript
复制
function SplitPane(props) {
  return (
    <div className="SplitPane">
      <div className="SplitPane-left">
        {props.left}
      </div>
      <div className="SplitPane-right">
        {props.right}
      </div>
    </div>
  );
}

function App() {
  return (
    <SplitPane
      left={
        <Contacts />
      }
      right={
        <Chat />
      } />
  );
}

传递一个元素进去也是可以的

特别关系

代码语言:javascript
复制
// 一些组件看作是其他组件的特殊实例,比如 WelcomeDialog 可以说是 Dialog 的特殊实例
function Dialog(props) {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        {props.title}
      </h1>
      <p className="Dialog-message">
        {props.message}
      </p>
    </FancyBorder>
  );
}

function WelcomeDialog() {
  return (
    <Dialog
      title="Welcome"
      message="Thank you for visiting our spacecraft!" />
  );
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-10-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 介绍
  • 2. JSX
  • 2. 渲染元素
  • 3. 组件
  • 4. state & 生命周期
  • 5. 事件处理
  • 6. 条件渲染
  • 7. list & key
  • 8. 表单
  • 9. 状态提升
  • 10. 组合关系
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档