一个只会
vue的前端菜鸡学react
# react18 挂载
import { createRoot } from "react-dom/client";
const reactDom = createRoot(document.getElementById("root"));
reactDom.render(<div>HelloWorld</div>);
# JSX
# JSX 防止注入攻击
你可以安全地在 JSX 当中插入用户输入内容:
const title = response.potentiallyMaliciousInput;
// 直接使用是安全的:
const element = <h1>{title}</h1>;
React DOM 在渲染所有输入内容之前,默认会进行转义 (opens new window)。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本) (opens new window)攻击。
特殊字符进行转义,以防止 xss 攻击
& becomes &
< becomes <
> becomes >
" becomes "
' becomes '
# 表达式
在 JSX 语法中,你可以在大括号内放置任何有效的 JavaScript 表达式 (opens new window)。例如,2 + 2,user.firstName 或 formatName(user) 都是有效的 JavaScript 表达式。
比如在大括号中使用js变量
const name = "Gauhar Chan";
const element = <h1>Hello, {name}</h1>;
jsx 也是表达式,Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用。上面的element大概会进行这样的转换
const name = "Gauhar Chan";
const element = React.createElement("h1", {}, "Hello, " + name);
而element这个React 元素大概长这样
// 注意:这是简化过的结构
const element = {
  type: "h1",
  props: {
    children: "Hello, Gauhar Chan",
  },
};
# 组件 & props
# class 组件
jsx中使用this.props访问组件的props
import React from "react";
export default class HelloWorld extends React.Component {
  render() {
    return <div>HelloWorld {this.props.name}</div>;
  }
}
# function 组件
该函数是一个有效的 React 组件,因为它接收唯一带有数据的 “props”(代表属性)对象与并返回一个 React 元素。这类组件被称为“函数组件”,因为它本质上就是 JavaScript 函数。
export function HelloFuntion(props) {
  return <div>HelloFuntion, {props.name}</div>;
}
# props
当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。
警告 ⚠️
所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
即子组件不能直接修改父组件的props,单向数据流
# 修改props的值
上面有说到不能直接改,那就换个思路,交给父组件自己更新(因为state 是私有的,并且完全受控于当前组件。)。通过函数传参的方式实现。
子组件
import React from "react";
export default class HelloWorld extends React.Component {
  handle () {
    this.props.onUserChange({
      name: 'gauhar chan'
    })
  }
  render() {
    return (<div onClick={this.handle.bind(this)}>HelloWorld { this.props.user.name }</div>)
  }
}
父组件
handleChangeUser(user) {
  // 修改值
  this.setState({
    user
  })
}
<HelloWorld user={ this.state.user } onUserChange={ (user) => this.handleChangeUser(user)}/>
- 在父组件中定义了一个修改state的函数,并通过props传递给子组件
- 子组件监听了点击事件,最终通过传入参数并调用父组件的函数实现修改值。
提示
绑定事件的时候要注意,因为是react模板解析机制是上下文会是立即执行内容,所以要加上handle.bind(this)来指定上下文。
# 使用组件
上面的两个组件放在了
./components/helloWorld/index.jsx文件中
import HelloWorld, { HelloFuntion } from "./components/helloWorld";
class Game extends React.Component {
  render() {
    return (
      <div>
        <HelloWorld name="chan" />
        <HelloFuntion name="gauhar" />
      </div>
    );
  }
}
const reactDom = createRoot(document.getElementById("root"));
reactDom.render(<Game />);
警告 ⚠️
注意: 组件名称必须以大写字母开头。
React 会将以小写字母开头的组件视为原生 DOM 标签。例如,<div /> 代表 HTML 的 div 标签,而 <Welcome /> 则代表一个组件,并且需在作用域内使用 Welcome。
# State
State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。
class组件使用state,必须创建构造函数并通过super将props传递到父类的构造函数中
然后在该函数中为 this.state 赋初值
constructor(props) {
  super(props);
  this.state = {
    name: 'gauhar chan'
  }
}
# setState
通过this.setState可以修改state中的值。
得益于 setState() 的调用,React 能够知道 state 已经改变了,然后会重新调用 render() 方法来确定页面上该显示什么。
WARNING
构造函数是唯一可以给 this.state 赋值的地方。除此之外不要直接修改State。
# State 的更新可能是异步的
出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。
因为 this.props 和 this.state 可能会异步更新,所以不要依赖他们的值来更新下一个状态。
如需基于之前的 state 来设置当前的 state,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:
// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));
另外如果需要state修改值之后,执行某些东西,可以使用setState的第二个参数callback回调函数
this.setState({
	name: 'gauhar'
}, () => {
	// state.name更新后
})
TIP
在 React 应用中,任何可变数据应当只有一个相对应的唯一“数据源”。通常,state 都是首先添加到需要渲染数据的组件中去。然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中。你应当依靠自上而下的数据流 (opens new window),而不是尝试在不同组件间同步 state。
# 生命周期

# 事件处理
React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:
- React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
- 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
- 事件对象event是一个合成事件。React 根据 W3C 规范 (opens new window)来定义这些合成事件,所以你不需要担心跨浏览器的兼容性问题。React 事件与原生事件不完全相同。如果想了解更多,请查看SyntheticEvent(opens new window) 参考指南。
# 在class组件中要注意上下文
- 使用bind绑定this,默认 - event是最后一个参数隐式传递- <div onClick={this.handle.bind(this, '参数')}>HelloWorld { this.props.user.name }</div>
- 使用一个函数返回,通常使用箭头函数简写,event需要手动显示的传递 - <div onClick={(event) => this.handle('参数', event)}>HelloWorld { this.props.user.name }</div>- 使用箭头函数有个注意的点,当这种用法被用于props传递给子组件,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。 
- class fields - 目前还是实验性的语法,不过 - Create React App默认启用此语法。- export default class HelloWorld extends React.Component { handle = () => { this.props.onUserChange({ name: 'gauhar chan' }) } render() { return (<div onClick={this.handle}>HelloWorld { this.props.user.name }</div>) } }- 上面监听了点击事件的回调,这里有个问题,你并不能直接 - onClick={this.handle('参数')}传参,因为这样函数会立即执行,所以这种情况下似乎又回到了上面的- 箭头函数和- bind。所以我认为这个- class fields适合用于- 不用传参和- props传递的情况。对于props而言,是我们手动触发的。- 用于props的例子: - // 子组件 export default class HelloWorld extends React.Component { handle = () => { this.props.onUserChange({ name: 'gauhar chan' }) } render() { return (<div onClick={this.handle}>HelloWorld { this.props.user.name }</div>) } } // 父组件 export default class Game extends React.Component { constructor(props) { super(props) this.state = { user: { name: 'gauhar' } } } handleChangeUser = (user) => { this.setState({ user }) } render() { return <HelloWorld user={ this.state.user } onUserChange={this.handleChangeUser}/> } }
# 条件渲染
React 中的条件渲染和 JavaScript 中的一样,使用 JavaScript 运算符 if (opens new window) 或者条件运算符 (opens new window)去创建元素来表现当前的状态,然后让 React 根据它们来更新 UI。
# && 运算符
这里就涉及到js这个&&的特性,如果表达式是true,那么会返回运算符右边的东西,否则会返回表达式本身的值。
render() {
  const flag = false
  return (
    <div>
    	{ count && <h1>true</h1> }
  	</div>
  )
}
上面渲染一个空div,这个是取决于什么数据类型,像数字0/NaN这种就会渲染
如果flag是0,我们知道0会进行隐式转换条件false,但此时返回的是0本身(NaN同理)
render() {
  const flag = 0
  return (
    <div>
    	{ count && <h1>true</h1> }
  	</div>
  )
}
最终会渲染<div>0</div>
# 阻止组件渲染
在极少数情况下,你可能希望能隐藏组件,即使它已经被其他组件渲染。若要完成此操作,你可以让 render 方法直接返回 null,而不进行任何渲染。
在组件的 render 方法中返回 null 并不会影响组件的生命周期。例如componentDidUpdate 依然会被调用。
# 表单
# 受控组件
就是通过state去管理,将state的值传入到组件的value,监听onChange事件通过setState更新值
对比之下,
vue的双向绑定确实很香
# 处理多个输入
当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作。
export class IForm extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      phone: '',
      password: ''
    }
  }
  handleInputChange = (event) => {
    const { name, value } = event.target
    this.setState({
      [name]: value
    })
  }
  render() {
    const { phone, password } = this.state
    return (
      <div>
        <form>
          <label>
            手机号码:
            <input
              name="phone"
              type="number"
              value={phone}
              onChange={this.handleInputChange} />
          </label>
          <br />
          <label>
            密码:
            <input
              name="password"
              type="password"
              value={password}
              onChange={this.handleInputChange} />
          </label>
        </form>
        <button onClick={ () => { console.log(this.state) } }>submit</button>
      </div>
    )
  }
}
# 组件的包含关系
插槽
slot到目前为止我发现了,
react这是所有的东西都通过props传递
在vue中,默认的插槽名称叫default,react中则是props.children
如果要自定义名称,那么就通过props自定义
export function HelloFuntion(props) {
  return (
    <div>
      HelloFuntion, { props.name }
      <div>{ props.children }</div>
      <div>{ props.diy }</div>
    </div>
  )
}
// ...
function DiyCom() {
  return (
    <div>我是自定义插槽</div>
  )
}
<HelloFuntion name="gauhar" diy={ DiyCom() } >
  <div>我是默认插槽</div>
</HelloFuntion>
# 代码分割
为了避免搞出大体积的代码包,在前期就思考该问题并对代码包进行分割是个不错的选择。 代码分割是由诸如 Webpack (opens new window),Rollup (opens new window) 和 Browserify(factor-bundle (opens new window))这类打包器支持的一项技术,能够创建多个包并在运行时动态加载。
对你的应用进行代码分割能够帮助你“懒加载”当前用户所需要的内容,能够显著地提高你的应用性能。尽管并没有减少应用整体的代码体积,但你可以避免加载用户永远不需要的代码,并在初始加载的时候减少所需加载的代码量。
# React.lazy
这么看来,
vue确实有很重的react影子呀这不就是
vue3还在实验阶段的suspense吗😂**(2022/05/05注)**
const OtherComponent = React.lazy(() => import('./OtherComponent'));
此代码将会在组件首次渲染时,自动导入包含 OtherComponent 组件的包。==说白了就是异步组件==
React.lazy 接受一个函数,这个函数需要动态调用 import()。它必须返回一个 Promise,该 Promise 需要 resolve 一个 default export 的 React 组件。
然后应在 Suspense 组件中渲染 lazy 组件,如此使得我们可以使用在等待加载 lazy 组件时做优雅降级(如 loading 指示器等)。
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <section>
          <OtherComponent />
          <AnotherComponent />
        </section>
      </Suspense>
    </div>
  );
}
切换两个异步组件显示隐藏时,会出现一个情况就是。新的组件还没准备好,就被渲染出来了,因此用户会看到屏幕闪烁,这个时候可以使用 startTransition (opens new window) API 来进行过渡。新的组件还没准备好时,保留显示旧的组件,新的准备好了,进行过渡切换
import React, { Suspense } from 'react';
import Tabs from './Tabs';
import Glimmer from './Glimmer';
const Comments = React.lazy(() => import('./Comments'));
const Photos = React.lazy(() => import('./Photos'));
function MyComponent() {
  const [tab, setTab] = React.useState('photos');
  function handleTabSelect(tab) {
    startTransition(() => {
      setTab(tab);
    });
  }
  return (
    <div>
      <Tabs onTabSelect={handleTabSelect} />
      <Suspense fallback={<Glimmer />}>
        {tab === 'photos' ? <Photos /> : <Comments />}
      </Suspense>
    </div>
  );
}
# 基于路由的代码分割
这里是一个例子,展示如何在你的应用中使用 React.lazy 和 React Router (opens new window) 这类的第三方库,来配置基于路由的代码分割。
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Suspense>
  </Router>
);