2023-09-04
ReactJS
00
请注意,本文编写于 172 天前,最后修改于 24 天前,其中某些信息可能已经过时。

目录

一些概念
JSX
组件
function组件
认识Hook
正确使用setState
Props
ref
ref补充 (24年1月29)
生命周期
React V16.3之前的生命周期
V16.4 之后的⽣命周期
错误监控
事件处理
钩子与性能优化
PureComponent
useEffect
自定义Hook
Hook 使⽤规则
useMemo
useCallback
useReducer
useRef
useState
总结
对比react与vue2
语法对比
状态管理
路由

本篇文章内容迁移自老博客, 虽然写了很多年,但中间有调整或插入内容, 是对React基础知识点比较完善的梳理。

一些概念

React是什么?

  • 官方描述:用于构建用户界面的JavaScript库
  • 采用组件化模式、声明式编码,提高开发效率和组件复用率
  • ReactNative中可以使用React语法进行移动端开发
  • 使用虚拟DOM+优秀的Diff算法,尽量减少与真实DOM的交互

React的组成

  • React    负责逻辑控制,数据 -> VDOM
  • ReactDOM 渲染实际DOMVDOM -> DOM
  • JSX
    • React    使JSX来描述UI
    • @babel/preset-react把JSX 编译成相应的 JS 对象, JSX -> React.creactElement(tagName, props, children)
    • React.createElement再把这个JS对象构造成React需要的VDOM。

image.png

JSX

  • JSX是⼀种JavaScript的语法扩展,其格式比较像模版语言,但事实上完全是在JavaScript内部实现的。
  • JSX可以很好地描述UI,能够有效提⾼开发效率,体验JSX
  • JSX仅仅是React.cloneElement(component, props, ...children)的语法糖
  • JSX语法官方介绍

JSX的语法规则

  • 标签 可以自闭合 可以使用.引用
  • 组件大写、 自定义标签大写, 小写会识别为html标签
  • React必须在作用域内 import React from 'react'
  • 关于props有两个特例
    • class => className
    • for => htmlFor
  • 不支持将通用表达式作为元素类型 ,比如<components[props.storyType] />
  • props 默认true
    • 可以使用扩展运算符 <Comp {...props} />
    • 其实对象是不可以解构的,这个是babel编译时处理的
  • JSX 可以无缝融合到JS
    • JSX可以赋值给变量,作为函数参数返回值等
    • JSX中可以写JS逻辑,比如条件、分支、循环、运算语句等。
  • JSX{}语法
    • 可以是表达式、函数、对象等等
    • &&表达式 如果返回 truefalsenullundefined 将被忽略
    • {{}} 表示一个对象 比如style={{ width: 100 }}
  • 数组 必须定义key diff时候,首先比较type, 然后是key,所以同级同类型元素,key值必须得唯一
  • CSS推荐CSSModule的写法
jsx
/* index.module.css内容如下 .app { .logo { width: 100px; } } */ import styles from "./index.module.css"; const jsx = ( <div className={styles.app}> <span className={styles.logo} style={{ width: "50px", height: "30px" }} /> </div> );

css modules 只是加了一些网页组件最急需的功能 (局部作用域、全局作用域、定制hash、组合、定义变量), 可以看我的另一篇文章 CSS模块化方案

组件

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

组件有两种形式: class组件 和 function组件。

function组件

函数组件通常无状态,仅关注内容展示,返回渲染结果即可。从React16.8开始引⼊了hooks,函数组件也能够拥有状态。

function 组件非常灵活,比如 useEffect

jsx
useEffect(() => { // 相当于componentDidMount + componentDidUpdate const timer = setInterval(() => { setDate(new Date()); }, 1000); return () => clearInterval(timer); // componentWillUnmount的集合 }, [] // 依赖项,哪些state发生改变触发更新 相当于 shouldComponentUpdate );

认识Hook

参考资料: Hook简介 Hook视频介绍

Hook 是什么?
Hook 是⼀个特殊的函数,它可以让你“钩入” React 的特性。例如, useState 是允许 你在React函数组件中添加 stateHook

什么时候用Hook?
如果你在编写函数组件并意识到需要向其添加⼀些state,以前的做法是必须将其它转化为class。现在你可以在现有的函数组件中使用 Hook

注意事项

  1. 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调⽤
  2. 只能在 React 的函数组件中 或 ⾃定义的 Hook 中 调用Hook

组件复合 - Composition

  • 复合组件给予你足够的敏捷去定义自定义组件的外观和行为,这种⽅式更明确和安全。如果组件间有公用的非UI逻辑,将它们抽取为JS模块导入使用⽽不是继承它。
  • 这个东西可以模拟 Vueslot 能力
  • 参考资料: 组合VS继承

正确使用setState

setState(updater[, callback])

  • this.setState({...})浅层merge 类似Object.assign
  • this.setState(state, (newState)=> {/*更新后的回调*/})
  • this.setState(currentState => { return newState;}) 批处理,可以避免闭包引起的变量不是最新值的情况
javascript
class App extends React.Component { state = { count: 0, sex: '男' } add = () => { // this.setState({count: this.state.count+1}); // this.setState({count: this.state.count+1}); // 此时 页面显示 1 this.setState(state => ({ count: state.count + 1 })); this.setState(state => ({ count: state.count + 1 })); // 现在页面显示2 } render() { return <h1 onClick={this.add}>{this.state.count}</h1> } } const root = ReactDOM.createRoot(document.getElementById('root')); root.render(<App/>);

setState是同步还是异步?

  1. < 18

    1. 出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。 合成事件和⽣命周期函数中是异步的, (合成事件是指一个方法调用另一个方法, 这⾥的异步其实是批量更新。)
    2. 在原生事件和setTimeout中都是同步的
  2. > 18

  • 四种情况(合成事件、周期钩子、原生事件、setTimeout)都是使用异步
  • 想要同步的话请使用 ReactDOM.flushSync

注意事项

不要直接修改 State,因为不会触发渲染组件:

Props

react15.5之前react内置prop-types,后来将prop-types独立出去了

class组件类型校验、必填校验、默认值设置的示例代码

jsx
class Person extends React.Component{ //对传给Person组件的props进行类型的限制 static propTypes = { name: PropTypes.string, // 限制name必须为字符串类型 sex: PropTypes.string.isRequired,// 限制sex必须为字符串类型,且是必要属性 age: PropTypes.number,// 限制age必须为数值类型 address: PropTypes.string, // 限制address必须为字符串类型 } //对传给Person组件的props进行默认值的设置 static defaultProps = { address: '中国' } render(){ // return ... } } // 下面的...p1,并不是原生ES8的对象解构, // 而是babel+react环境提供的能力,支持展开运算符展开一个对象, // 但是仅仅适用于传递标签属性!! ReactDOM.render(<Person {...p1}/>,document.getElementById('test2'))

function组件类型校验及默认值的代码示例

jsx
function Person(props){ const {name,age,sex,address} = props // return ... } Person.propTypes = { name: PropTypes.string, sex: PropTypes.string.isRequired, address: PropTypes.string, } Person.defaultProps = { address: '中国' }

ref

组件实例的三大属性: refstateprops

ref的三种使用方式

  1. API:String 类型的 Refs 已过时不推荐
jsx
class Demo extends React.Component{ showData = ()=>{ const {input1} = this.refs alert(input1.value) } render(){ return ( <div> <input ref="input1" type="text" placeholder="点击按钮提示输入"/>&nbsp; <button onClick={this.showData}>点我提示数据</button>&nbsp; </div> ) } }
  1. 回调ref
jsx
class Demo extends React.Component{ showData = ()=>{ const {input1} = this alert(input1.value) } render(){ return ( <div> <input ref={c => this.input1 = c} type="text" placeholder="点击按钮提示输入"/>&nbsp; <button onClick={this.showData}>点我提示数据</button>&nbsp; </div> ) } }
  1. 创建ref

更多ref的使用

ref补充 (24年1月29)

ref的值不会因为组件的刷新而重新生命,它是专门用来存储组件级别的信息的。

React官方推荐有三种场景我们可以使用它

  • 存储 setTimeout/setInterval 的 ID;
  • 存储和操作宿主组件(在Web中是DOM元素);
  • 存储其他不会参与JSX计算的对象。

生命周期

  • 生命周期方法,用于在组件不同阶段执行自定义功能。
  • 在组件被创建并插入到DOM时,组件更新时,组件取消挂载或从DOM中删除时,都有可以使用的生命周期方法。

React V16.3之前的生命周期

image.png

image.png

  1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
    1. constructor()
    2. componentWillMount()
    3. render()
    4. componentDidMount() =====> 常用

一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

  1. 更新阶段: 由组件内部this.setSate()或父组件render触发this.forceUpdate()

    1. shouldComponentUpdate() 注意:强制更新不走“阀门”
    2. componentWillUpdate()
    3. render()
    4. componentDidUpdate()
  2. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发

    1. componentWillUnmount()  =====> 常用 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
  3. 注意 constructorsuper()super(props)的区别

    • 若使用super() 构造函数里不能使用state 推荐super(props)写法
  4. componentWillReceiveProps

    • 组件创建时不会触发, 父组件更新时会触发

V16.4 之后的⽣命周期

V17 可能会废弃的三个生命周期函数用getDerivedStateFromProps替代,

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

如果你还想使用的话要加上UNSAFE_
例如 UNSAFE_componentWillMount
还可以通过命令自动添加 UNSAFE_ 前缀 npx react-codemod rename-unsafe-lifecycles

为什么标记unsafe ?

官方解释

  • 这些生命周期经常被误解和滥用;
  • 此外,我们预计,在异步渲染中,它们潜在的误用问题可能更大
  • 我们将在即将发布的版本中为这些生命周期添加“UNSAFE_”前缀。这里并不是指安全性,而是表示使用这些生命周期的代码在React的未来版本中可能更有可能出现bug,尤其是在启用异步渲染之后。

具体是什么Bug就没有再说了, 查了一下资料是这样说的

  1. react16为了实现时间切片,将递归不可中断的同步更新链表变更为可中断的异步更新。 由于任务优先级的任务存在,这些周期钩子可能多次触发
  2. 事件监听不要写在 constructor里面, 因为componentWillUnmout 可能不会触发(只有触发了componentWillMount才会触发willUnmount), 如果渲染被中断了导致willMount没有触发,而开发者在constructor添加了监听,销毁的时候就没有触发,导致内存泄漏。

V16.4 引⼊两个新的生命周期函数:

  • static getDerivedStateFromProps(props, state)
    • 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
    • 它应返回⼀个对象来更新 state,如果返回 null 则不更新state,但render还是会执行。
    • 请注意,不管原因是什么,都会在每次渲染前触发此⽅法。
    • UNSAFE_componentWillReceiveProps 形成对比,后者仅在⽗组件重新渲染时触发,⽽不是在内部调用 setState
  • getSnapshotBeforeUpdate(prevProps, prevState)
    • render之后,在componentDidUpdate之前。
    • 在最近⼀次渲染输出(提交到 DOM 节点)之前调用。它使得组件能在发生更改之前从 DOM 中捕获⼀些信息(例如,滚动位置)。此⽣命周期的任何返回值将作为参数传 递给 componentDidUpdate(prevProps, prevState, snapshot)

错误监控

  • static getDerivedStateFromError
    • 配合Error boundaries使用
    • 此生命周期在后代组件抛出错误后被调用。它将抛出错误错误并返回一个值以更新state;
  • ComponentDidCatch
    • 会在“提交”阶段被调用,因此允许执行副作用。它应该用于记录错误之类的情况;
jsx
class ErrorLifePage extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } componentDidCatch(error, info) { // 副作用处理 // "组件堆栈" 例⼦: // in ComponentThatThrows (created by App) // in ErrorBoundary (created by App) // in div (created by App) // in App console.error(info.stack) } static getDerivedStateFromError(error) { // 更新 state 使下⼀次渲染可以显示降级 UI return { hasError: true }; } render() { return (<div> <h3>ErrorLifePage</h3> {this.state.hasError ? (<h1>Something went wrong.</h1>) : (<Clock></Clock>) } </div>); } } function Clock() { const [date, setDate] = React.useState(new Date()); React.useEffect(() => { const timer = setInterval(() => { if(new Date().getMilliseconds() % 5 === 0) { setDate('not Date'); } setDate(new Date()); }, 1000); return () => clearInterval(timer); }, []); return <h1>{date.toLocaleTimeString()}</h1> }

完整案例

事件处理

  • 通过onXxx属性指定事件处理函数(注意大小写)
    • React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)---- 为了提高效率
    • React使用的是自定义(合成)事件,而不是原生的DOM事件 ---- 为了更好的兼容性 dispatchEvent(new CustomEvent('myClick', {detail: ...}))
  • 通过event.target得到发生事件的元素

解决this指向的三种方案

  • constructor 进行bind
  • 事件采用箭头函数赋值
  • jsx中使用箭头函数调用

注意事项

JSX中应避免 onClick={() => this.handleClick()}这种写法。
每次render都会创建不同的回调函数,如果该回调函数作为props传⼦组件,每次⼦组件都要re-render

钩子与性能优化

前面说到react有个生命周期 shouldComponentUpdate它返回一个boolean值来决定是否执行render,这个是性能优化的关键

PureComponent

官方介绍

  • 它是利用 shouldComponentUpdate 浅层对比 prop 和 state 然后决定是否需要渲染
  • 缺点:必须用class组件 且比较是浅比较
jsx
import React, { Component, PureComponent } from 'react' export default class PureCompUsage extends PureComponent { constructor(props) { super(props); this.state = { count: 1 } } handleClick = () => { this.setState({ count: 10 }) } render() { // console.log('点击一次,这里会执行一次,其实count不变不需要重复渲染') console.log('使用了PureComponent 对props和state进行浅比较,数据相同不会再次渲染') return ( <> <p>{this.state.count}</p> <button onClick={this.handleClick}>ADD to 10</button> </> ) } }

思考: 如果function组件怎么避免不必要的渲染呢?

  • 如果是state,我们判断是否需要setState
  • 如果是props,则使用useMemo
    • const NewChild = React.useMemo(Child);
    • useMemo还可以用来避免复杂的运算

useEffect

Effect Hook
Effect Hook 可以让你在函数组件中执行副作用操作。
数据获取,设置订阅以及⼿动更改 React 组件中的 DOM 都属于副作用。不管你知不知道这些操作,或 是“副作⽤”这个名字,应该都在组件中使⽤过它们。

jsx
import React, { useState, useEffect } from "react"; export default function UseEffectUsage() { const [date, setDate] = useState(new Date().toLocaleTimeString()); useEffect( () => { // 相当于componentDidMount + componentDidUpdate(有依赖项的情况) const timer = setInterval(() => { setDate(new Date().toLocaleTimeString()); }, 1000); // return 相当于 componentWillUnmount return () => { clearInterval(timer); console.log('定时器已被清除') }; }, // 依赖项,哪些state发生改变触发更新 // 可类比 shouldComponentUpdate [] ); return <div>{date}</div>; }
  • 在函数组件主体内(这⾥指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录⽇志以及执行其他包含副作⽤的操作都是不被允许的,因为这可能会产⽣莫名其妙的 bug 并破坏 UI 的⼀致性。
  • 使⽤ useEffect 完成副作用操作。赋值给 useEffect 的函数会在组件渲染到屏幕之后执⾏。你可以把 effect 看作从 React 的纯函数式世界通往命令式世界的逃⽣通道。
  • 默认情况下,effect 将在每轮渲染结束后执行,但你可以选择让它在只有某些值改变的时候才执行。

自定义Hook

自定义hook 有时候我们会想要在组件之间重⽤一些状态逻辑。
目前为⽌,有两种主流方案来解决这个问题: ⾼阶组件render props
⾃定义Hook可以让你在不增加组件的情况下达到同样的目的。
⾃定义Hook是一个函数,其名称以 “use” 开头,函数内部可以调⽤其他的Hook。

hook封装定时器案例

js
import React, { useState, useEffect } from "react"; export default function UseEffectUsage() { const date = useClock(); return <div>{date}</div>; } function useClock() { const [date, setDate] = useState(new Date().toLocaleTimeString()); useEffect(() => { const timer = setInterval(() => { setDate(new Date().toLocaleTimeString()); }, 1000); return () => { clearInterval(timer); console.log("定时器已被清除"); }; }, []); return date; }

Hook 使⽤规则

Hook 就是 JavaScript 函数,但是使⽤它们会有两个额外的规则:

  • 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调⽤。
  • 只能在 React 的函数组件或自定义钩子中调用 Hook。不要在其他 JavaScript 函数中调用。

把“创建”函数和依赖项数组作为参数传入 useMemo ,它仅会在某个依赖项改变时才重新计算memoized 值。这种优化有助于避免在每次渲染时都进⾏高开销的计算

useMemo

useMemo有两个用途

  • 避免父组件state改变而引起的不必要的子组件渲染
  • 缓存某个计算结果,避免不必要的重复运算
jsx
// 缓存某个计算结果,避免不必要的重复运算 import {useState, useMemo} from 'react'; export default function UseMemoUsage() { const [count, setCount] = useState(0); const [text, setText] = useState(''); /* const expensive = () => { console.log("compute"); // 输入框 value 改变 也会触发 let sum = 0; for (let i = 0; i < count; i++) { sum += i; } return sum; }; */ const expensive = useMemo(() => { console.log("compute"); let sum = 0; for (let i = 0; i < count; i++) { sum += i; } return sum; }, [count]); // 只有count改变才进行expensive计算; return ( <div> <p>{count} -- {expensive} </p> <button onClick={() => setCount(count + 1)}>ADD</button> <input type="text" defaultValue={text} placeholder='试试输入内容' onInput={e => { setText(e.target.value) }}/> </div> ); }

useCallback

把内联回调函数及依赖项数组作为参数传入 useCallback ,它将返回该回调函数的 memoized 版本, 该回调函数仅在某个依赖项改变时才会更新。当你把回调函数传递给经过优化的并使⽤引用相等性去避免非必要渲染(例如shouldComponentUpdate )的子组件时,它将非常有用。

使用场景: 子组件某个事件触发依赖调用父组件的方法进行计算,这种场景通常是父组件通过props将函数传给子组件,但是这样会有个问题,父组件state发生改变,会导致子组件重新渲染,使用useCallback可以避免子组件的重复渲染 看个案例

  1. 父组件setState会触发子组件的渲染
jsx
const { useState, useCallback } = React; function UseCallBackUsage() { const [value, setValue] = useState({ text: "" }); console.log("父组件渲染"); const onChange = (e) => { setValue({ text: e.target.value, }); }; return ( <div> <p>{value.text}</p> <input type="text" placeholder="输入内容试试看" value={value.text} onChange={onChange} /> <hr /> <Child /> </div> ); } function Child(props) { // 父组件setState会触发子组件渲染 console.log("子组件渲染"); return ( <div> <span>子组件</span> </div> ); }
  1. 为了避免子组件重复渲染使用 React.memo(Comp)包裹

image.png

  1. 这时候如果我们传递一个回调给组件,尽管该回调函数与渲染无关,但是又会触发子组件的渲染

image.png

  1. 这时候就需要useCallback登场

image.png

使用注意事项:

  • 如果子组件为class组件,则要继承PureComponent
  • 如果子组件是function组件,则要使用React.memo()包裹子组件

useReducer

jsx
import React, {useReducer} from 'react'; export default function UseReducerUsage() { function counter (state, action) { switch(action.type) { case 'ADD': return state + 1; default : return state; } } const [state, dispatch] = useReducer(counter, 0); return <div> <p>{state}</p> <button onClick={() => dispatch({type: 'ADD'})}>ADD</button> </div> }

useRef

jsx
// function组件用法 import {useRef} from 'react'; export default function FuncRef () { const inputRef = useRef(); return <div> <input type="text" ref={inputRef}/> <button onClick={handleClick}>focus</button> </div> function handleClick() { inputRef.current.focus(); } } // class组件用法 import React, { Component } from "react"; export class RefClassUasge extends Component { constructor() { super(); this.inputRef = React.createRef(); } render() { return <div> <input type="text" ref={this.inputRef}/> <button onClick={() => this.inputRef.current.focus()}>focus</button> </div> } }

使用子组件Ref -- ref转发

jsx
import React from "react"; export default function Forward() { const ref = React.createRef(); return ( <div> <MyInputW ref={ref} disabled placeholder="this is disabled input"> MyInputW </MyInputW> <button onClick={() => console.log(ref.current)}>get MyInputW</button> </div> ); } const MyInput = (props, ref) => ( <input ref={ref} placeholder={props.placeholder} disabled={props.disabled}></input> ) const MyInputW = React.forwardRef(MyInput);

通过ref暴露子组件的方法给父组件调用

jsx
import React, { forwardRef, useImperativeHandle, useRef } from 'react' export default function Expose() { const exposeRef = useRef(); return ( <div> <FancyInputW ref={exposeRef} /> <button onClick={() => { exposeRef.current.focus(); }}>focus</button> </div> ) } function FancyInput(props, ref) { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus() }, })); return <input ref={inputRef} /> }; const FancyInputW = forwardRef(FancyInput);

useState

useState获取不到最新值问题

jsx
import React, { useState, useEffect } from 'react'; // export default function UseStateUsage () { // const [arr, setArr] = useState([0]); // useEffect(() => { // console.log(arr); // }, [arr]); // const handleClick = () => { // Promise.resolve().then(() => { // setArr([...arr, 1]); // }).then(() => { // setArr([...arr, 2]); // 时赋值前 arr 为旧状态仍然为:[0] // }) // } // return <div> // <button onClick={handleClick}>click</button> // </div> // } // 解决方案1 setState(function) // export default function UseStateUsage () { // const [arr, setArr] = useState([0]); // useEffect(() => { // console.log(arr); // }, [arr]); // const handleClick = () => { // Promise.resolve().then(() => { // setArr((arr) => [...arr, 1]); // }).then(() => { // setArr((arr) => [...arr, 2]); // }) // } // return <div> // <button onClick={handleClick}>click</button> // </div> // } // 解决方案2 使用UseReducer模拟强制刷新 // export default function UseStateUsage() { // const [,forceUpdate] = React.useReducer(x => x+1, 0); // const [arr] = useState([0]); // const handleClick = () => { // Promise.resolve().then(() => { // arr.push(1); // }).then(() => { // arr.push(2); // forceUpdate(); // }) // } // return <div> // <h1>{arr.toString()}</h1> // <button onClick={handleClick}>click</button> // </div> // } // 解决方案3 ref export default function UseStateUsage() { const [arr, setArr, getArr] = useGetState([0]); const handleClick = () => { Promise.resolve().then(() => { setArr([...getArr(), 1]); }).then(() => { setArr([...getArr(), 2]); }) } return <div> <h1>{arr.toString()}</h1> <button onClick={handleClick}>click</button> </div> } const useGetState = (initVal) => { const [state, setState] = useState(initVal); const ref = React.useRef(initVal); const _setState = (newVal) => { ref.current = newVal; setState(newVal) } const getState = () => { return ref.current; } return [state, _setState, getState]; } // 参考资料 https://www.cnblogs.com/hymenhan/p/14991789.html

总结

父组件渲染会引发子组件也跟着渲染,怎样避免不必要的子组件渲染呢?

  1. 有些时候虽然调用了setState,但并不需要重新渲染DOM,这时候就需要 PureComponent,它是利用shouldComponentUpdate**浅层对比 prop 和 state **,从而决定是否渲染。
  2. PureComponent 只能用于class组件,那么function组件怎么办呢?可以使用React.memo(Comp)
  3. 如果某些计算比较昂贵不需要重复计算 则是使用 useMemo缓存
  4. 父组件通过属性传递给组件的一个回调函数, 为避免父组件渲染,带动子组件不必要的渲染, 则需要使用useCallback包裹该回调函数 (需要memo配合)

使用自定义Hook 进行代码优化(类似vue3关注点分离思想)

本章节完整案例 点这里

对比react与vue2

本人一直以来是vue2玩家,最近在学react,试着对比两种框架语法糖之间的异同,以便快速掌握react的基本使用。

reactvue2
在线使用支持 要使用React.createElement代替JSX支持 要引入含有编译器的包
官方文档https://react.docschina.org/https://cn.vuejs.org/v2/guide/
cli 脚手架 create-react-app
如果想像vue-cli一样灵活可以考虑umi
vue-cli
非常智能灵活,如
vue server sfc.vue运行组件
vue creact project根据问卷生成脚手架
vue ui图形化界面管理项目
vue add package 智能安装依赖代码帮你处理好
支持处理跨域、mock配置等
devtoolshttps://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihihttps://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd
路由react-routervue-router
状态管理redux/react-redux
mobx
vuex
服务端渲染https://apollographqlcn.github.io/react-docs-cn/server-side-rendering.htmlhttps://v3.cn.vuejs.org/guide/ssr/introduction.html
原生开发react-nativeweex
  • 就文档而言,react写的比较简洁,vue的文档写的非常全面且系统,包含生态相关指引、开发规范、web安全等等

语法对比

image.png

状态管理

image.png

路由

image.png

本文作者:郭敬文

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!