React框架开发项目基础

274 阅读6分钟

开发环境准备

1. 安装node.js
2. 脚手架工具 reactjs.org --> 点击get started --> Create a New React App
3. 创建项目
    npx create-react-app my-app
    cd my-app
    npm start
4. 安装依赖
    yarn install --> my-app问价夹会有yarn.lock文件

文件目录

1. 浏览器窗口小图标
    修改文件中的 favicon.ico 即可
2. 如果要使用npm,将yarn.lock和node_modules文件删除,命令行执行npm install

使用react完成ToDoList

  1. 新建 src/index.js,引入React和ReactDom,然后将其挂载在root上。
import React from 'react';
import ReactDom from 'react-dom';
ReactDom.render(<div>hello world</div>, document.getElementById('root'));
  1. 新建组件 src/TodoList.js
 import React, { Component } from 'react';
    class TodoList extends Component {
        render() {
            return (
                <div>TodoList</div>
            );
        }
    }
// 组件导出
export default TodoList;

占位组件使用,Fragment

import React, { Component, Fragement } from 'react';
class TodoList extends Component {
    render() {
        return (
            <Fragement>
                <input >
                <ul>
                    <li>learn react</li>
                    <li>learn Component</li>
                </ul>
            </Fragement>
        );
    }
}

React中数据驱动的设计思想/事件绑定

1)React是基于数据驱动的框架,组件中定义数据需要写进 this.state中,想要改变其中的值,需要用this.setState({key: val})

2)JSX语法中,如果属性=js表达式或者js变量,必用{}
<input value = { this.state.inputValue } />

3)如果要改变事件中的this指向,可以在绑定事件的时候,通过.bind(this)来改变this指向当前组件
<input value={ this.state.inputValue } onChange={this.handleInputChange.bind(this)}/>

实现TodoList新增/删除功能

    1) 列表数据循环展示,注意循环输出的每一项需要加key值,否则会报错
     <ul>
        {this.state.list.map((value, index) => {
            return <li key={index}>{value}</li>
        })}
     </ul>
   2)添加列表项
    handleKeyUp(e) {
        if (e.keyCode === 13) { // 回车键
            // 数组list原始值 和input框中输入值
            // 展开运算符 复制新的数组
            const list = [...this.state.list, this.state.inputValue];
            // 回车后改变list的值,并且清空input框中的值
            this.setState({
                list: list,
                inputValue: ''
            })
        }
    }
   3)删除列表项
    // bind(this, val)除了改变this指向,还可以传递参数
    <ul>
        {this.state.list.map((value, index) => {
            return (
                <li key={index} onClick={this.handItemClick.bind(this, index)}>
                    {value}
                </li>
            )
        })}
    </ul>
    handItemClick(index) {
        // console.log(index);
        const list = [...this.state.list];
        // 将数组list 从下标index开始删除1项
        list.splice(index, 1);
        this.setState({list});
    }

JSX语法细节

1)bind(this)单独领出来写
    <input value={ this.state.inputValue } 
        onChange={this.handleInputChange.bind(this)}
        onKeyUp={this.handleKeyUp.bind(this)}
    />
    bind提取出来写在函数中

2)将列表循环提出来,写在函数里
    getListItems() {
        return this.state.list.map((value, index) => {
            return (
                <li key={index} onClick={this.handItemClick.bind(this, index)}>
                    {value}
                </li>
            )
        })
    }
    
3)JSX中写注释,需要将内容包裹在{}里面,使用表单label for语句,需要写成htmlFor

组件相关

组件拆分与组件传值

1. 组件拆分:新建组件src/TodoItem.js,然后在TodoList.js中引入
2. 组件传值:
    父传子(属性方式传递, this.props接收)
    父组件传递:<TodoItem content={value} />
    自组件接收:<li> {this.props.content} </li>
3. 子组件和父组件通信:调用父组件传递过来的方法,需要注意的是,父组件在传递方法的过程中注意修改作用域

React 核心特性

1. 声明式开发(不需要操作dom,只需要定义js模板和数据,修改数据即可)
2. 可以与其他框架并存:
    存在与其他框架解耦的机制,index.html页面只有id='root'的div与React有关,其他div可以用其他框架来实现,他们互不影响
3. 组件化
4. 单向数据流(父组件可以改自组件的数据,但是自组件不可改父组件)
5. 函数式编程

React生命周期

  1. Props, State 与 render 函数
    注意render函数生么时候会被执行:
        当组件初次创建的时候,render函数会被执行一次
        当state数据发生变更的时候,render函数会被重新执行
        当props数据发生变更的时候,render函数会被重新执行
  1. React中ref的使用:
    例:点击btn的时候,获取btn离页面窗口顶部的距离,需要操作dom
    ref 写在html标签上,获取的是dom节点
    ref 写在组件上,获取的是组件的js实例
<button 
    onClick={this.handleBtnClick}
    ref={(button) => {this.btnElem = button}}
>
    增加
</button>
 handleBtnClick() {
    console.log(this.btnElem.clientTop); // 1
 }
  1. setState 是异步的
 handleBtnClick() {
    console.log(this.divElem.innerHTML); // 1

    const newCounter = this.state.counter + 1;
    this.setState({
        counter: newCounter
    })
    console.log(this.divElem.innerHTML); // 1
}

setState 异步写法: 两个函数作为参数,第二个函数会在第一个函数执行完再执行

handleBtnClick() {
    console.log(this.divElem.innerHTML); // 1
    const newCounter = this.state.counter + 1;

    this.setState(() => {
        return {
            counter: newCounter
        }
    }, () => {
        console.log(this.divElem.innerHTML); // 2
    })
}

4. 生命周期函数(组件在某一时刻会自动执行的函数)

import React, { Component, Fragment } from 'react';

class Counter extends Component {

    handleClick() {
        const newNumber = this.state.number + 1;
        this.setState({
            number: newNumber
        })
    }

    // 初始化
    constructor(props) {
        console.log('constructor');
        super(props);
        this.handleClick = this.handleClick.bind(this);
        this.state = {
            number : 1
        }
    }

    // 生命周期函数:
    // 页面挂载前自动执行
    componentWillMount() {
        console.log('componentWillMount');
    }

    // 页面渲染时自动执行
    render() {
        console.log('render');
        return (
            <div onClick={this.handleClick}>hello world{this.number}</div>
        )
    }

    // 页面挂载后自动执行
    componentDidMount() {
        console.log('componentDidMount');
    }

    // 更新前自动执行(数据更新),返回布尔值
    shouldComponentUpdate() {
        console.log('shouldComponentUpdate');
        // return true;
        // 返回false的时候,后面的生命周期函数将不会再执行,
        // 页面不会在render函数重新渲染,从而提高网页性能
        return false;
    }

    // 更新时自动执行
    componentWillUpdate() {
        console.log('componentWillUpdate');
    }

    // 更新结束
    componentDidUpdate() {
        console.log('componentDidUpdate');
    }
}


export default Counter;

执行顺序:componentWillMount --> render --> componentDidMount --> shouldComponentUpdate --> componentWillUpdate --> render --> componentDidUpdate

页面挂载前后

更新前
更新前后
更新完成
shouldComponentUpdate返回false的时候
componentWillReceiveProps 只有子组件才有的生命周期函数,子组件渲染前执行

生命周期函数使用实例

  1. 页面加载之后绑定全局事件
import React, { Component } from 'react';

// 原始写法
// class Counter extends Component {

//     handleClick() { // 点击hell 之后才会绑定该全局事件
//         window.addEventListener('click', () => {
//             console.log('window click');
//         })
//     }

//     render() {
//         return (<div onClick={this.handleClick.bind(this)}>hello ...</div>)
//     }
// }

// 使用生命周期函数写法
// class Counter extends Component {

//     handleClick() {
//         console.log('window click');
//     }
//     componentWillMount() {
//         window.addEventListener('click', this.handleClick)
//     }

//     render() {
//         return (<div>hello ...</div>)
//     }

//     componentWillUnmount() {
//         window.removeEventListener('click', this.handleClick);
//     }
// }

export default Counter;
  1. ajax请求
    安装axios: npm install axios --save
    重启项目
    文件中引入axios包: import axios from 'axios';
    ajax请求一般写进 生命周期函数的componentDidMount

Ant Design 组件库的使用

    安装: npm install antd --save
    index.js中引入样式 import 'antd/dist/antd.css'
    重启项目
    组件文件中引入组件库中组件 import { List, Typography } from 'antd';
    使用组件库中组件

React中的前端路由

根据路径的不同,给用户展示不同的组件

  1. BrowserRouter,创建一个路由;
  2. Route,路由项,配置路由项地址,显示对应的组件;
 安装: cnpm install react-router-dom --save
 index.js中引入: import { BrowserRouter, Route, Link } from 'react-router-dom';
 
import React, { Component } from 'react';
import ReactDom from 'react-dom';
import { BrowserRouter, Route, Link } from 'react-router-dom';

import newList from './newList';
import newButton from './newButton';
import 'antd/dist/antd.css';

class Entry extends Component {

    render() {
        return (
            <BrowserRouter>
                <div>
                    <Route path='/list' component={newList} />
                    <Route path='/button' component={newButton} />
                </div>
            </BrowserRouter>
        )
    }
}

ReactDom.render(<Entry />, document.getElementById('root'));

// 网页中打开地址: 
    http://localhost:3000/list
    http://localhost:3000/button
  1. Link,组件之间跳转。例:点击button页面按钮,跳转list页面。
引入Link组件,然后通过<Link to="/页面地址">实现路由跳转
import React, { Component } from 'react';
import { Button } from 'antd';
import { Link } from 'react-router-dom';

class newButton extends Component {

    render() {
        return (
            <Link to="/list">
                <Button type="danger">按钮</Button>
            </Link>
        )
    }
}

export default newButton;

link跳转的同时携带参数:

1. 传递参数: <Link to="/list?a=123"> 或者  <Link to="/list/123">
2. 在配置路由项的时候,路径后加上:id(将传递的参数放进变量id,以方便其他地方获取)
<Route path='/list/:id' component={newList} />
3. 获取参数:render中打印 console.log(this.props.location.search); 或者
console.log(this.props.match.params.id);