搭建React项目

在装有node.js的基础上,命令行输入命令

npx create-react-app item-name

即可创建一个React项目,项目结构如下

JSX语法

src文件下的index.js是项目的入口文件,文件中先后引入了react和react-dom两个库。

ReactDOM.render(<h2>hello react</h2>, document.getElementById('root'));

ReactDOM实例通过render方法,将前面的h2标签元素,添加到root元素中,而id为root的元素是public文件的index.html文件的元素。
JSX语法由JavaScript语法 和 XML语法组成,通俗来说就是,当我们在JSX语句中遇到 <> 的形式的,按照XML的形式来解析,因为他们是标签;遇到 {} 按照javaScript来解析,表示JS语法。

const a = (
        <div>
            <p>hello react</p>
        </div>
) 

如果存在标签结构,并且标签结构需要换行的话,可以使用 ()的形式进行表示

React 元素渲染

这里通过一个实例了解react的元素渲染

<!-- 首先定义一个方法 -->
function nowTime() {
    const ele = (
        <!-- 使用()的形式,可以换行写标签语法 -->
        <h2>get now time</h2>  
        <!-- 定义一个获取当前时间的方法 -->
        <h1>now time is { new Date().toLocalTimeString() } </h1>
    )
    <!-- 渲染这个获取当前时间的方法 -->
    reactDOM.render(ele, document.getElementById('root));
}
<!-- 定时器每隔一秒会调用一次该方法,实现实时更新当前时间 -->
setInterval(() => {
    nowTime()
},1000)

React 组件

关于Ract组件,区别于vue的以 .vue为后缀结尾的文件,React的组件可以是以 .js 为后缀的文件,也可以是 .jsx 为后缀的文件命名。编写React组件的时候,创建 .js 的组件是没有代码语法提示的,而创建 .jsx 组件文件是有代码语法提示的。 创建React组件分别有两种方式,第一种是通过 类的形式创建组件,第二种是通过Hook的形式创建。并且组件之间是可以互相引入和使用的,以标签的形式存在!

<!-- 第一种写法 -->
<!-- 创建一个React组件首先需要引入 React 库 -->
import React from 'react'
<!-- 创建一个类App继承React所提供的Component组件的对象 -->
class App extends React Component {
    <!-- rander(){} 渲染函数 -->
    render() {
        <p>渲染函数中写你需要这个组件展示的内容,或者要做什么</p>
    }
}
<!-- 导出组件 -->
export default App

<!-- 第二种写法 -->
import React from 'react'

export default class App extends React.Component{
    render() {
        return {
            <div>
                <p>直接导出并继承React下的Component对象</p>
            </div>
        }
    }
}
<!-- 函数组件 -->
function fun(props) {
    return (
        <div>
            <h3>函数组件</h3>
        </div>
    )
}
<!-- ES6 class类组件 -->
class App extends React.Component {
    render() {
        return (
            <div>
                <h3>ES6 class类创建组件</h3>  
            </div>
        )
    }
}

然后在需要使用的地方引入该组件,并将其放置在渲染函数中渲染即可。

import 'App' from './App.jsx'
ReactDOM.render(<App />, document.getElementById('root));

Props组件动态传参

React里面的组件传参通过 props

父组件传递参数给子组件

<!-- 子组件 -->
import React from 'react'
export default class Son extends React.Component {
    render() {
        return {
            <div>
                <ul>
                    {
                    <!-- 子组件中 通过this.props的方式获取父组件传递过来的数据,也就是arr ,然后通过map来遍历获取到的数据-->
                        this.props.arr.map((element, index) => {
                            <!-- 将遍历得到的数据放在li标签中渲染返回,key值需要绑定index唯一值,否则会抛出语法error -->
                            return <li key={ index }>{ element }</li>
                        })
                    }
                </ul>            
            </div>
        }
    }
}
import React from 'react'
<!-- 引入子组件 -->
import Son from './son'
export default class Father extends React.Component {
    render() {
        <!-- 父组件定义两个数组,用于传递给子组件使用 -->
        const arrOne = ['Vue' , 'React', 'Angular'],
                 arrTwo = ['Java', 'Pythen', 'C++']
        return (
            <!-- 在两个子组件中分别传入不同的数据 -->
            <Son arr={ arrOne } />
            <Son arr={ arrTwo } />
        )
    }
}

子组件传递参数给父组件

<!-- 父组件 -->
import React from 'react'
import Son from './son'
export defaul{t class Father React.Component {
    <!-- 首先定义state用来存储title的状态 -->
    constructor() {
        super();
        this.state = {
            title: ‘最初的标题’
        }
    }
    clickChange = () => {
        this.setState({
            title: '最后的标题'
        })
    }
    render()  {
        return (    
            <div>
                父子组件传参
            </div>
            <!-- 使用子组件,title最开始为 ‘最初的标题’, changeFatherProps 被子组件中的点击事件触发从而执行父组件中的clickChange方法,完成对标题的修改 -->
            <son title={ this.state.title } changeFatherProps={ this.clickChange } />
        )
    }
}
<!-- 子组件 -->
import React from 'react'

export defaul{t class Father React.Component {
    changeFather = () => {
        <!-- 通过 this.props 的方式 -->
        this.prpos.changFatherProps()
    }
    render()  {
        return <div>
                    父子组件传参
                    <!-- 点击button事件会触发父组件中方法的执行,将标题修改 -->
                    <button onClick={ this.changeFather }>点击传参修改<button>
                </div>
    }
}

state 状态

<!-- 通过this.state定义保存数据和状态 -->
    this.state = {
        flag: true,
        count: 10,
    }
    <!-- 需要修改状态值的时候通过 this.setState({})的方式进行修改 -->
    this.setState({
        flag:  false,
        count: this.state.count ++
    })

生命周期函数

随着我们对React理解和使用的原来越多,生命周期会越来越有参考价值
生命周期函数包括:

  1. componentWillMount: 在组件创建之前执行
  2. componentDidMount: 在组件渲染之后执行
  3. shouldComponentUpdata: 返回 true 和 false, true代表允许改变, false代表不允许改变
  4. componentWillUpdata: 数据在改变之前执行(state, props)
  5. conponentDidUpdata: 代表数据修改完成(state, props)
  6. conponentWillReveiceProps: props发生改变时执行的生命周期函数
  7. conponentWillUnMount: 组件卸载前执行

探讨关于 setState 更新是同步操作还是异步操作的问题

setState会引起视图的重绘
官方文档解答:在可控的情况下是异步的,在不可控的情况下是同步的

create-react-app 按需加载Antd 组件

  1. 命令行输入 cnpm run eject 拉取React的配置文件
  2. 文件被修改时,会抛出异常,由于GIt的原因导致,此时找到文件目录,按下shift + command + . 显示隐藏文件,删除 .git 文件,重新 cnpm run eject即可,安装完成后目录多出了两个文件,分别是config和script文件,config文件就是webpack配置文件
  3. 安装一个依赖文件 cnpm install babel-plugin-import --save-dev

React-Router

React路由也分为两种路由模式: BrowserRouter 和 HashRouter
两者的区别在于 HashRouter使用的是锚点链接的形式,表现为 # 的形式, # 号后面为跳转的二级路由地址 而BrowserRouter 则使用了HTML5的新特性,主要是 history.push() 实现。
实际开发中, BrowserRouter 会在项目上线之后存在一些问题,就是需要后台配合做一些配置处理和重定向,否则会很容易跳转到 404 页面,而 HashRouter 则不会有这个问题。

<!-- 安装 react-router -->
cnpm install react-router-dom -save

<!-- 使用 react-router -->
import {BrowserRouter as Router, Link,NavLink, Route, Redirect} from "react-router-dom"
<!-- 引入  react-router 相关的组件 -->

function App() {
    render() {
        return(
            <div>
            <!-- 在 Router盒子中嵌套 Route盒子,在Route中定义路由路径和路径对应的页面 -->
                <Router>
                    <switch>
                        <Route path="/login" component={ Login }></Route>
                        <Route path="/home" component={ Home }></Route>
                        <Route path="/home/detail" exact={ true } component={ detail }></Route>         
                        <Route path="/demo" render={ () => <div>简约写法,不需要创建页面和组件,直接路由显示render函数中定义路由页面内容即可</div>}</Route>
                        <!-- 简约写法,不需要创建页面和组件,直接路由显示render函数中定义路由页面内容即可 -->
                    </switch>
                    <!-- 当路由规则包裹在switch内的时,路由就不会同时显示多个,而是按照当前路径进行匹配 -->
                </Router>
                    <!-- 路由匹配规则之,如果一个页面上同时存在 路径为 /home 和 /home/detail 两个组件,两个组件都会显示,因为 /home 是包含 /home/detail , 但是正常情况下访问 /home下的/detail是不应该显示上一级组件也就是 /home的,这个时候就需要使用到 路由规则的精准匹配, 使用 exace = { true } 即可开启精准匹配,就不会匹配到下一级路由,只会匹配到 /home 这一个, strict = { true } 可以更加精确的匹配路由,而且使用的时候strice需要和exace一起使用才可以生效,而exace不需要组合使用也可以生效 -->

                    <Link to="/login">go login now</Link>
                    <!-- Link 点击跳转到 to 后面的路由页面 -->

                    <NavLink to="/login" activeClassName="setActive"></NavLink>
                    <!-- NavLink顾名思义,导航跳转,后为当前选中的菜单导航添加一个名为active的class样式,给你这个样式添加颜色或者背景选中时便会添加该样式,activeClassName可以修改样式名称,默认是active -->

                    <Redirect from="/happy" to="/login"></Redirect>
                    <!-- Redirect可以实现路由重新向,当你访问 的地址是属性中的from,也就是要去的路由为 ‘/happy',他会直接重定向到 to所给定的路由地址 -->


                </Router>
            </div> 
        )
    }
}
<!-- 处于用户体验方面的考虑,我们一般还会在路由匹配中加入 404 等页面,将不存在的路径以更友好地方式提醒用户 -->
<button>跳转</button>

<!-- 跳转的方法有 两种  props.history.push('/home')   和 props.history.replace('/home')
区别在于, push方法的页面跳转是叠加的,也就是说点击浏览器左上角的返回上一个页面依然有效,因为历史数据被保留; 而replace方法是替换的,所以不会存有历史跳转记录
 -->

Redux数据管理方案 (类似于Vuex)