react应用实例的记录

自动化构建工具

解决前端开发中自动化工具、性能优化、模块化框架、开发规范、代码部署、开发流程等问题

grunt

fis3

gulp

webpack

官网:https://webpack.js.org/

模块打包器,可以将有依赖关系的资源经过 webpack 处理后打包生成独立的静态资源

从 v4.0.0 开始,webpack 可以不用再引入一个配置文件来打包项目

概念:

入口(entry):

指示 webpack 应该使用哪个模块,来作为构建其内部依赖图(dependency graph)的开始。

默认值是 ./src/index.js

输出(output)(出口):

output 属性告诉 webpack 在哪里输出它所创建的 bundle,以及如何命名这些文件。

主要输出文件的默认值是 ./dist/main.js,其他生成文件默认放置在 ./dist 文件夹中

loader:

webpack 只能理解 JavaScript 和 JSON 文件,loader 让 webpack 能够去处理其他类型的文件,并将它们转换为有效模块,以供应用程序使用,以及被添加到依赖图中

插件(plugin):

loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量。

使用

  • 生成 package.json 文件
$ npm init -y
  • 安装 webpack
$ npm install webpack webpack-cli --save-dev
  • 创建目录结构
  • 利用 webpack 默认配置打包
$ npx webpack

打包时,默认的入口文件是 :./src/index.js 文件,默认的出口文件:./dist/main.js 文件

  • 自定义配置文件
webpack.config.js
  • 运行
$ npx webpack --config <配置文件路径>

可以将命令行下运行的 npx webpack --config <...> 在 package.json 的 npm scripts 中添加脚本任务:

{
    "scripts": {
        "start": "webpack --config <配置文件的路径>"
    }
}
  • 安装 loader 来处理 css
$ npm i css-loader style-loader -D

如果使用 sass 来开发 css(后缀名为 .scss)

$ npm i sass-loader node-sass -D

如果使用 less 来开发 css(后缀名为 .less)

$ npm i less-loader less -D
  • 安装 loader 来处理 image
$ npm i file-loader -D
  • 安装 html-webpack-plugin 插件来处理 html

  • 安装 clean-webpack-plugin 插件来清理 /dist 目录

  • 安装 webpack-dev-server 本地开发服务器

React

用于构建用户界面的 JavaScript 库

脚手架:create-react-app (cra)

$ npx create-react-app my-app

创建项目时,会自动安装三个包:

  • react:核心包
  • react-dom:用于 web 渲染 DOM 元素的包
  • react-scripts:用于执行 react 脚本的包,内部包装了 webpack 的配置

目录结构:

my-app
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
└── src
    ├── App.css
    ├── App.js
    ├── App.test.js
    ├── index.css
    ├── index.js
    ├── logo.svg
    └── serviceWorker.js

npm scripts

{
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
}
  • start: 开发环境下,启动 webpack-dev-server 本地服务器
  • build:打包构建时用到的脚本
  • test:测试
  • eject:弹出,将 webpack 的详细配置弹出到项目本地(将 webpack 的配置还原到项目中),注意,弹出操作是不可撤销的

第一个 React 应用

import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render(<h1>Hello React</h1>, document.getElementById('root'))

jsx语法:

是一个 JavaScript 的语法扩展。我们建议在 React 中配合使用 JSX,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模版语言,但它具有 JavaScript 的全部功能。

组件

函数组件

const App = (props) => {
  return <h1>Hello {props.message}</h1>
}

在函数内部,返回 JSX 表示的 React 元素,函数参数是使用组件时所传递的属性对象,注意,组件名称必须首字母大写。

class 组件

// class 组件
class App extends React.Component {
  render() {
    return <h1>Hello {this.props.message}</h1>
  }
}

在 class 组件中,继承 React.Component 父类,必须在子类中实现一个 render() 方法,用于渲染组件,在渲染过程中,可以使用 this.props 获取组件接收到的属性。

使用组件并传递属性

<App message="React" />

Fragment

在组件中返回的 React 元素需要使用单个根元素包裹,可以使用 <React.Fragment> 组件包裹元素,在生成渲染的 DOM 树中不会出现节点本身信息,也可以使用 <> </> 标记来包裹元素节点,与 <React.Fragment> 组件的使用是等效的

JSX 原理

<div className="container" title="提示标题">
    <h1 className="title">主标题</h1>
    <h2>副标题</h2>
</div>

jsx 实际上是语法糖(React.createElement):

React.createElement(
    'div',
    {
        className: 'container',
        title: '提示标题'
    },
    React.createElement(
    	'h1',
        {
            className: 'title'
        },
        '主标题'
    ),
    React.createElement(
    	'h2',
        null,
        '副标题'
    )
)

创建出虚拟节点:

{
    tagName: 'div',
    props: {
        className: 'container',
        title: '提示标题'
    },
    children: [
        {
            tagName: 'h1',
            props: {
                className: 'title'
            },
            children: ['主标题']
        },
        {
            tagName: 'h2',
            props: null,
            children: ['副标题']
        }
    ]
}

虚拟DOM在进行比较时,使用 diff/fiber 算法进行比较(在内存中比较两个节点的异同),比较完毕后,将不同的节点在实体 DOM 树中渲染

prop-types 包

在运行时检测 React 组件的 props 类型(对组件属性实行类型约束)

class App extends Component {
    static propTypes = {
        prop1: PropTypes.string,
        prop2: PropTypes.number
    }
}

defaultProps

属性默认值

class App extends Component {
    static defaultProps = {
        prop1: 'hello',
        prop2: 0
    }
}

props.children

在组件的 props 属性中,有一个 children 属性,表示的是使用组件时,内嵌在组件内部的后代节点:

<TodoHeader a="1" b="abc">
    <div>自定义的标题</div>
    <div>第二个定义</div>
</TodoHeader>

props.a === '1'

props.b === 'abc'

props.children === <div>自定义的标题</div><div>第二个定义</div>

Props

props 是从父组件中传递到子组件中的数据,通常 props 应该是只读的(在子组件中不应该修改 props 属性值)-- 单向数据流

State

state 是组件内部需要使用到的数据,可以在组件内实现对 state 数据的修改。

在 React 中,修改 state 中的数据,必须通过调用 setState() 方法来实现修改,才能够在修改数据后页面响应式渲染。

state 在 class 组件中可以直接使用,函数组件中没有 state 的。所以通常将函数组件也叫做无状态组件。

注意:setState() 方法在更新状态数据时是异步的,如果需要获取到更新后的状态数据值,可以在 setState()方法的回调函数中处理。

事件处理

在 jsx 元素中,直接在元素标签内联书写事件属性

事件属性名采用驼峰命名规范,如:onClick,onChange

在表单元素上实现双向绑定

单行文本框:绑定 value 属性与处理 onChange 事件

<input
    className="input"
    type="text"
    placeholder="请输入新的待办事项"
    value={this.state.inputValue}
    onChange={this.changeHandler}
/>
state = {
    inputValue: '初始值' // 输入框中绑定的数据
}

// 表单元素(单行文本框)中监测到输入变化时触发调用
changeHandler = (event) => {
    // console.log('change', event.currentTarget.value)
    // setState() 修改 inputValue 值
    this.setState({
        inputValue: event.currentTarget.value
    })
}

Refs

如果需要引用到 DOM 元素或是 React 组件,可以使用 ref 来实现引用。

旧版本 API 通常使用字符串的 ref(不建议再使用):

<input ref="inputRef" />
<button onClick={
        () => {
			this.refs.inputRef.focus()
        }
    }>按钮</button>

新版本 API 使用 React.createRef() 实现:

inputRef = React.createRef()

<input ref={this.inputRef} />
<button onClick={
        () => {
			this.inputRef.current.focus()
        }
    }>按钮</button>

组件通信

父子通信

  • 父传子:props
  • 子传父:props,借助 props ,父组件向子组件传递一个函数的引用,在子组件中实现对函数的调用,调用函数时传递父组件需要使用到的数据作为参数,就把子组件中的数据传递到父组件中了。

跨组件通信

  • 将跨层级的组件转换为连续的 父-子 关系组件来进行通信
  • Context
  • 状态管理库

Context

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

使用:

创建 Context 对象:

// 创建 Context 对象
cosnt context = React.createContext()
// 解构生产组件与消费组件
const { Provider, Consumer } = context
// Provider 是向 Context 中存数据及操作数据的方法
// Consumer 是从 Context 中取数据或操作数据的方法

export { Provider, Consumer }

使用 Context 对象

利用 Provider 组件来保存数据,通常利用 Provider 组件将需要共享数据的组件包裹起来

<Provider value={{data: [], setData: () => {}}}></Provider>

Provider 接收一个 value 属性,传递给消费组件(Consumer)。当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。

利用 Consumer 组件来取数据:

<Consumer>
	{
        (value) => {
            /* 根据 value 来渲染节点 */
        }
    }
</Consumer>

在 Consumer 组件内函数表达式中的参数 value 是 Provider 组件保存的value数据

生命周期

图谱

挂载:

  • constructor() :通常在构造函数中初始化 state,绑定方法中的 this
  • render() :渲染
  • componentDidMount() :会在组件挂载后(插入 DOM 树中)立即调用,通常异步请求的方法在这个生命周期方法中调用
  • 注意:老版本中可能使用到的 componentWillMount(),在 render() 之前被调用,当前版本中(16.13.1)名称为 UNSAFE_componentWillMount() ,不建议使用

更新:

  • shouldComponentUpdate() :是否更新组件,该方法中会返回 boolean 值,默认返回 true。当返回 true 时,表示会继续向后调用 render() 渲染组件,如果返回 false ,则不再继续向后调用生命周期方法。该方法通常在组件优化时使用到。PureComponent 组件类
  • render():数据(state、props)更新后重新渲染
  • componentDidUpdate() :在更新后会被立即调用。首次渲染不会执行此方法。

销毁:

  • componetWillUnmount() :在组件卸载及销毁之前直接调用

纯函数

  • 函数的返回值只依赖其参数值:

  • 函数执行过程中不能造成副作用:

// 是纯函数
function add(num1, num2) {
    return num1 + num2
}
const result = add(1, 2)
console.log('result = ', result)

// 不是纯函数
function sort(array) {
    for(let i = 0, len = array.length; i < len - 1; i++) {
        for(let j = 0; j < len - 1 - i; j++) {
            if (array[j] > array[j + 1]) {
                const tmp = array[j]
                array[j] = array[j + 1]
                array[j + 1] = tmp
            }
        }
    }
    return array
}
const arr = [3, 7, 1, 2, 0, 5, 4]
console.log('前-arr: ', JSON.stringify(arr))
const result = sort(arr)
console.log('arr: ', JSON.stringify(arr))
console.log('result: ', JSON.stringify(result))

HOC (higher-order component)

高阶组件

本质上就是一个函数,该函数传递一个组件作为参数,返回一个新的组件,通常返回的新组件是在原组件基础之上作过包装的组件。这其实是装饰者设计模式。

Redux

概念:

store:仓库,将 state 与更新 state 的动作包装在一起

state:状态,数据

action:是一个普通对象,表明状态更新的动作。通常有两个属性:type 和 payload

reducer:是一个纯函数,主要用于更新状态,传递 state 与 action 作为参数,返回更新后的新的状态数据,reducer 函数是唯一能够更新状态的地方,并且是通过 dispatch() 来分发 action 调用 reducer() 更新状态

dispatch:分发 action,实现更新状态

三大原则

单一数据源

整个应用的 state 被储存在一棵 object tree 中,并且这个 object tree 只存在于唯一一个 store 中。

State 是只读的

唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。

使用纯函数来执行修改

为了描述 action 如何改变 state tree ,你需要编写 reducers。

react-redux 绑定库

redux 与 react 并没有直接关系,但两者配合使用最好,在 redux 包中并不包含绑定库,需要单独安装 react-redux 包。

绑定库的 API

<Provider store>:正常情况下,你的根组件应该嵌套在 <Provider> 中才能使用 connect() 方法。

connect([mapStateToProps], [mapDispatchToProps])

  • 参数:mapStateToProps(state, [ownProps]),如果定义该参数,组件将会监听 store 的变化。任何时候,只要 store 发生改变,mapStateToProps 函数就会被调用。函数必须返回一个纯对象,这个对象会与组件的 props 合并
  • 参数:mapDispatchToProps(dispatch, [ownProps]),该参数可以是函数或对象的类型,如果传递的是一个函数,该函数将接收一个 dispatch 函数,然后由你来决定如何返回一个对象。如果传递的是一个对象,那么每个定义在该对象的函数都将被当作 Redux action creator,对象所定义的方法名将作为属性名;每个方法将返回一个新的函数,函数中dispatch方法会将 action creator 的返回值作为参数执行。这些属性会被合并到组件的 props 中。
  • connect() 函数的返回值是一个 HOC

异步action

标准做法是引入 redux-thunk 中间件

使用 react + redux + react-redux + redux-thunk

  • 创建 store – 调用 redux 的 createStore() 方法
  • 编写 reducer – 纯函数,用于更新状态,传递 state 与 action 参数,返回新的 state。reducer 函数体中通常使用 switch-case 来判断 action.type 类型,以进行实际状态更新操作(返回更新后的新 state)
  • 编写 action-creator – 用于生成 action 对象,以便于 dispatch() 调用时传递 action 对象
  • 完善创建 store,如果要处理异步 action,则需要引入 redux-thunk 然后在创建 store 时应用该中间件,如果一个应用中有多个独立的 reducer,要合并成一个根 reducer 再使用。createStore(reducer, middleware)
  • 利用绑定库 (react-redux)中的 <Provider> 组件缓存 store。通常使用 <Provider> 将整个 <App /> 组件包裹起来,这样,<Provider> 后代的组件都可以共享到缓存的 store 了。
  • 如果在组件中需要获取到 store 中的内容,可使用绑定库提供的 connect() 方法来建立组件与 store 的连接。connect(mapStateToProps, mapDispatchToProps),connect() 调用的返回值为 hoc,利用 hoc 为参数组件做功能增强,在 connect 连接时,将 state 及 dispath 相关属性都映射到组件的 props 属性中。

HOOK

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

利用 Hook 可以在函数组件中使用到很多只有在 class 组件中才有的特性。

Hook 使你在无需修改组件结构的情况下复用状态逻辑

Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)

Hook 使你在非 class 的情况下可以使用更多的 React 特性

useState

state Hook,可以在函数组件中,调用 React.useState() 来使用 state Hook

const [stateName, setStateMethod] = React.useState(initValue)
// initValue 是一个 state 的初始值
// stateName 是代表 state 状态值的变量名
// setStateMethod 是一个修改 state 状态值的方法
// stateName、setStateMethod 这些名称都可以根据语义取其它的名字

如果一个函数组件中需要使用到多个 state,则调用多次 useState() 方法即可

useEffect

Effect Hook 可以让你在函数组件中执行副作用操作

如果要在函数组件中使用执行副作用操作的方法(原来在 class 组件中的 componentDidMount() / componentDidUpdate() ),可以使用 React.useEffect() 方法来达到效果。

// componentDidMount() / componentDidUpdate() 都会执行进入
React.useEffect(() => {})

// componentDidMount()
React.useEffect(() => {}, [])

其它:

useContext()

useCallback()

useReducer()

useRef()

useMemo()

React-Router

路由相关包,在 web 开发中使用 react-router-dom 包,如果是原生app(react-native)开发,则使用 react-router-native 包。

Ant Design

使用

  • 利用 create-react-app 脚手架先创建项目,然后在项目中引入 antd:
$ yarn create react-app project
或
$ npx create-react-app project

$ cd project

$ yarn add antd
或
$ npm i antd -S
  • 在 React 组件中使用 antd 的组件
import { Button } from 'antd'
import 'antd/dist/antd.css'

class MyComponent extends React.Component {
    render() {
        return <Button type="primary">按钮</Button>
    }
}
  • 高级配置
$ yarn add @craco/craco # 利用社区解决方案来自定义配置时,先安装 craco

安装 craco 后,将 package.json 中 npm scripts 的脚本修改一下:

{
    "scripts": {
        "start": "craco start",
        "build": "craco build",
        ...
    }
}

如果项目中要引入 less 预处理器,则:

$ yarn add craco-less

在项目根目录下添加 craco.config.js 文件,并配置:

const CracoLessPlugin = require('craco-less')

module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            modifyVars: {  },
            javascriptEnabled: true
          }
        }
      }
    }
  ]
}

注意,如果是在 antd 的 4.x 版本中自定义配置,官方推荐的是 craco 社区解决方案。如果是 antd 的 3.x 版本中,官方推荐的是使用 react-app-rewired 与 customize-cra 来配置(配置文件名称:config-overrides.js )

国际化

antd 默认文案是 英文,如果需要修改文案:

import React from 'react'
import { render } from 'react-dom'
import { ConfigProvider } from 'antd'
import zhCN from 'antd/es/locale/zh_CN'
import App from './App'

render(
  <ConfigProvider locale={zhCN}>
    <App />
  </ConfigProvider>,
  document.getElementById('root')
)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值