Skip to content

1 慢速上手

约 920 个字 144 行代码 预计阅读时间 5 分钟

使用 JS 直接操作 DOM 会导致大量的重绘重排

特点

  • 组件化模式、声明式编码
  • 可以在 React Native 中可以使用 React 语法进行移动端开发
  • 使用 虚拟DOM + 优秀的 diff 算法,尽量减少和真实DOM的交互

1 Hello World

React中文官网

1.1 使用脚手架创建项目

  • 需要的环境
    • NodeJS & npm
    • create-react-app,使用 npm install -g create-react-app 可以全局安装
  • 创建项目 create-react-app prj_name
  • 项目启动 npm start,默认端口号为 3000

项目结构

%PUBLIC_URL% - 表示 public 根目录
- public 静态资源文件
    - favicon.icon
    - index.html
    - manifest.json 应用加壳的配置文件(Web套皮原生)
    - robots.txt 爬虫协议
- src
    - App.css APP组件样式(在 App.js 中自行引入)
    - App.js  APP组件本体
    - APP.test.js APP组件测试
    - index.css 样式
    - index.js 入口文件
      这里面会引入 idx.css,如果要在 html 中加载要删去重复部分
    - logo.svg  logo图
    - reportWebVitals.js 性能分析文件(需要 web-vitals 库)

Hello World 组件

  1. 编写入口文件
    // 引入核心库 和 ReactDOM
    import React from 'react'
    import ReactDOM from 'react-dom'
    // 引入根组件
    import App from './App.js'
    // 渲染到页面
    ReactDOM.render(<App/>, document.getElementById('root'))
    // 在 React18 中,render 被更改为以下形式
    import {createRoot} from 'react-dom/client'
    const root = createRoot(document.getElementById('root'))
    root.render(<App/>)
    
  2. 创建根组件 APP
    import React from 'react'
    export default class APP extends React.Component {
        render {
            return(
            <div>
                hello world!
            </div>)
        }
    }
    
样式模块化

用 less 写没有这个问题捏

  1. 样式表更名为 comp.module.css
  2. 使用 import comp_name from './comp.module.css'
  3. 渲染时返回 tag className={comp_name.name}

1.2 单页面应用(CDN引入)

<body>
    <div id="app"></div>

    <script crossorigin
     src="https://unpkg.com/react@16/umd/react.development.js">
    </script>
    <script crossorigin 
     src="https://unpkg.com/react-dom@16/umd/react-dom.development.js">
     </script>
    <script
     src="https://unpkg.com/babel-standalone@6/babel.min.js">
     </script>

    <script type="text/babel">
        // 必须指明内容为 babel
        // 1. 创建虚拟DOM
        const VDOM = <h1>Hello, world!</h1>
        // 2. 将虚拟DOM渲染到指定容器中
        ReactDOM.render(
            VDOM,
            document.getElementById('app')
        );
    </script>
</body>

2 虚拟 DOM

2.1 两种创建方式

  1. 使用 JSX 创建(就和上面一样)
    // 记得一定不要加引号捏
    VDOM = (
        <h1 id="title">
            <span>Hello, world!</span>
        <h1>
    )
    
  2. 使用 原生JS 创建
    // 不用引入 babel 了捏
    // React.createElement(标签名,属性,标签体内容)
    const VDOM = React.createElement('h1',{id:'title'}, 'hello world') 
    ReactDOM.render(
        VDOM,
        document.getElementById('app')
    );
    // JSX 下可以轻松实现的套娃 <h1><span></></>, JS 下要套函数
    const VDOM = 
        React.createElement('h1',{id:'title'}, 
            React.createElement('span',{},'Hello World')
        ) 
    

2.2 vs 真实DOM

  • 其实生成的 VDOM 只是一个普通的 Object 对象,比较轻量
  • 最终会被转变为真实DOM,并渲染到页面上

3 JSX

是 JS 的扩展语法,类似于 XML

基础语法

  1. 定义虚拟DOM时,不要写引号
  2. 可以使用{}包裹潜入的 JS 表达式,如 <h2 id={myId.toLowerCase()}>
  3. 指定样式得用 className="sth" -> 用 class 会爆炸(和ES6冲突)
  4. 内联样式需要使用以下格式
    const VDOM = (
        // 多单词属性要用小驼峰
        <span style={{color: "white", fontSize:'20px'}}>
            hello, world!
        </span>
    )
    
  5. 只能有一个根标签(拿个 div 包一层)
  6. 标签必须闭合(自闭合也可以)
  7. 小写开头的标签被认为是 html 元素,无同名元素则报错; 大写开头被认为是 React Component,组件没定义整个寄掉

列表渲染

const data = ['item1', 'item2', 'item2']
const VDOM = (
    <div>
        <ul>
        {
            data.map( (item, idx) => {
                return <li key={idx}>{item}</li> // 单层括号不是插值语法捏
            })
        }
        </ul>
    </div>
)

4 虚拟 DOM 与 Diff

  1. React / Vue 中的 key 有什么作用?(key 的内部原理是什么)
    • Key 用于标识 虚拟DOM 对象,在 Update 时具有重要作用
    • 当 state 中的数据变化时,React 会根据新的数据生成「新的虚拟DOM」,随后与「旧虚拟DOM」进行 diff
      • 在「旧虚拟DOM」中找到具有相同 key 值的结构
        • 内容不变:复用真实DOM
        • 内容改变:生成新的真实DOM,并进行替换
      • 未找到有相同 key 的结构 根据数据生成新的真实DOM,并渲染到页面
  2. 为什么遍历列表时,key 最好不用 index?
    • 若对数据进行:逆序添加、删除等破坏顺序的操作(一连串前后移位) 可能产生没有必要的真实DOM更新(\(O(n)\)),导致低效率
    • 若结构中含有 input:可能产生错误的 DOM 更新
    • 如果渲染的列表仅用于展示,使用key时没有问题的 最好选择记录的主键作为 key(保证 UNIQUE)

5 项目部署

  1. 打包捏 npm run build 生成 build
  2. 使用 Serve npm i serve -g(全局安装)
    1. 在主目录下执行 serve 命令(默认 port = 5000) 或者用 serve 相对路径
    2. 如果我们把生成的文件丢到 cur_path 下就是 serve build

6 其他

6.1 setState 的两种写法

  1. 对象式 setState(neoState, [callback])

    回调函数 callback 将在 render() 执行完毕后调用(类似于 nextTick

  2. 函数式 setState(updater, [callback])

    updater 可以接收 state & props

    this.setState(
        (state, props) => { // 其实差不多,对象式需要 this.state.name
            return {count: state.count+1}
        },
        ()=>{} // callback
    )
    

  3. 对象式的 setState 实际上是函数式的简写方式

  4. newState 依赖原状态时 —— 函数式
  5. newState 不依赖原状态时 —— 对象式

6.2 lazyLoad

针对路由组件

// 动态加载路由组件(路由组件代码会被分开打包)
const Login = lazy(() => import('@/pages/Login'));
// 使用 <Suspense> 指定在加载完成前的 Loading 界面
import {Suspense} from 'react'; // 包裹所有注册路由<Route>
<Suspense fallback={<h1>Loading</h1>}>
    <Switch>
        <Router path="..." component={Login}/>
        <Redirect to="/home"/>
    </Switch>
</Suspense>

其实 suspense 显示的内容也可以是组件(但是不能再 lazy 了)

import Loading from './loading';
<Suspense fallback={<Loading/>}/>

6.3 Hook

使得函数式组件可以使用 state 及 其他React特性

useState

// 由于 rfc 没有 this,绑定 onClick={funcName} 即可
// 定义回调使用 function funcName(params){} 即可
export default function Demo {
    // 分别是 [cur_val, set_val_method]
    const [count, setCount] = React.useState(initVal)
    // 可以使用 {count} 读取,使用 setCount(newVal/count+1) 重置
    // setCount( val => new_val) new_val 可以基于旧值迭代
}

useEffect

支持使用 LifeCycle

export default function Demo {
    // 不使用第二个参数时 = DidMount + DidUpdate(检测所有数据)
    // 第二个参数为[] = DidMount (不检测任何数据)
    // 第二个参数为[count] = DidMount + UpdateCount
    // 也可以用,分隔多个检测对象
    React.useEffect(()=>{
        let timer = setInterval(()=>{
            setCount(count => count+1)
        }, 1000)
        return () => { // 返回的函数相当于 WillUnmount
            clearInterval(timer)
        }
    },[]) // 此时 = DidMount
}

useRef

export default function Demo() {
    const myRef = React.useRef()
    render() {
        return (
            <input ref={myRef}/>
        )
    }
    // 可以用 myRef.current.value 拿到当前的输入值
}

6.4 Fragment

充当 render 的「虚拟」根标签,类似于 Vue 中的 <template>

  • 可以指定 key <Fragement key={1}>
  • 其实也可以使用空标签<>,但不能指定 key