美文网首页
从零创建一个React项目

从零创建一个React项目

作者: 我没叫阿 | 来源:发表于2019-12-31 20:25 被阅读0次
    node -v 查看版本号
    
      1. 安装node.js自带的包管理工具npm npm install webpack -g 或者npm install yarn -g
    npm -v 查看版本号
    
      1. 全局安装脚手架 npm install -g create-react-app 或者 yarn add create-react-app -g
    create-react-app --version 查看版本号
    
      1. 在命令行里输入create-react-app demo(这里的demo指的是你项目的名字),然后等待安装完成
        完成之后npm start 或者yarn start 启动项目

    项目目录

    README.md 项目说明文档
    node_modules 依赖文件夹
    package.json npm依赖
    public 静态资源
    -- favicon.ico
    -- index.html
    src 源码
    -- index.js 入口js
    -- App.js 入口组件

    这样的话一个react的项目就创建完了

    react基础知识

    • 1.理解react和react-dom两个库
      react只做逻辑层
      react-dom做渲染层,去渲染实际的DOM

    • 2.剖析JSX的实质
      JSX语法即JS和html的混合体,实际的核心逻辑就是用js去实现的
      JSX的实质就是React.createElement的调用
      JSX写法

    class HelloMessage extends React.Component {
      render() {
        return (
          <div>
            Hello {this.props.name}
          </div>
        );
      }
    }
    
    ReactDOM.render(
      <HelloMessage name="Taylor" />,
      document.getElementById('hello-example')
    );
    

    React里的写法

    class HelloMessage extends React.Component {
      render() {
        return React.createElement(
          "div",
          null,
          "Hello ",
          this.props.name
        );
      }
    }
    
    ReactDOM.render(React.createElement(HelloMessage, { name: "Taylor" }), document.getElementById('hello-example'));
    

    代码1

    import React, { Component } from "react";
    export default class App extends Component {
      render() {
        return (
            <div></div>
        );
      }
    }
    

    代码2

    import React from "react";
    export default class App extends React.Component {
      render() {
        return (
            <div> </div>
        );
      }
    }
    

    代码1和代码2写法不一样,效果一样

      1. 讲解state变量渲染和setState修改数据

    在组件里面我们通过{}JSX渲染变量
    如果数据需要修改,并且需要页面同时响应改变,那就需要把变量放在state里面,同时使用setState修改

    初始化状态state

    // 初始化状态
    this.state = {
      count: 0
    };
    

    更新状态使用setState,不能直接this.state.count=xxx

    // 更新状态
    this.setState({
      count: this.state.count + 1
    });
    

    注意事项
    setState是异步的,底层设计同一个生命周期会批量操作更新state
    setState第二个参数是一个可选参数,传入一个回调函数可以获取到最新的state

    this.setState({
       value:this.state.value + 1
    },()=>{
      console.log(this.state.value)
    })
    

    当修改的state依赖上一次修改的state的值时,可使用以下这种方法修改

    this.setState((prevState, prevProps)=>({
     //prevState:上一次修改的状态state
     //prevProps:上一次修改的属性props
     count: prevState.count + 1
     }), () => {
     //这里可以获取到最新的state
     console.log(this.state.count);
     });
    
      1. props属性传递

    父组件向子组件传递属性利用props接收

    <PropsDemo title="父组件信息"></PropsDemo>
    

    子组件使用

     //class组件使用
     <h1>{this.props.title}</h1>
    
     //函数型组件使用
     function xxx(props){
     return <h1>{props.title}</h1>
     }
    
     //解构赋值写法
     function xxx({title}){
     return <h1>{title}</h1>
     }
    
    • 5.条件渲染与数据循环

    三目表达式写法

    { this.state.isShow?<p>{this.props.title}</p>:null}
    

    map映射

    export default class App extends Component {
      constructor(props) {
        super(props)
        this.state = {
          goods: [
            { title: 'html+css基础⼊⻔', price: 19.8 },
            { title: 'js零基础阶级', price: 29.8 },
            { title: 'vue基础⼊⻔', price: 19.8 },
            { title: 'vue电商单⻚⾯项⽬实战', price: 39.8 },
            { title: 'react零基础进阶单⻚⾯项⽬实战', price: 59.8 },
          ]
        }
      }
      render() {
        return (
          <div className="App">
            {console.log('react项目-rcc创建')}
            <ul>
              {this.state.goods.map((item, index) => (
                <li key={item.title}>
                  <p>科目 : {item.title}</p>
                  <p>价格 : {item.price}</p>
                </li>
              ))}
            </ul>
          </div>
        )
      }
    }
    
    • 6.事件的绑定

    6.1.箭头函数写法(最常用)

    isShow = () => {
     //做一些操作 
     this.setState({
        //设置state里的值
     });
    };
    
    <button onClick={this.isShow}>显示标题</button>
    

    6.2.直接使用箭头函数返回一个函数

    isShow(){
     //做一些操作 
     this.setState({
        //设置state里的值
     });
    };
    
    <button onClick={() => this.isShow()}>显示标题</button>
    
    • 7.样式编写

    行内样式

    <img style={{ width: "100px",height:"100px" }} />
    

    添加类名

    <img className="img" />
    

    添加属性

    // 先引入
    import logo from './logo.png'
    
    <img src={logo} />
    
    • 8.双向数据绑定
    export default class App extends Component {
      constructor(props) {
        super(props)
        this.state = {
          defaultVal: '我是默认值'
        }
      }
      handleChange = (e) => {
        this.setState({
          defaultVal: e.target.value
        })
      }
      render() {
        return (
          <div className="App">
            <input type="text" onChange={this.handleChange} />
            <p>{this.state.defaultVal}</p>
          </div>
        )
      }
    }
    

    React UI库的引入

    • 9.引入antd库

    在命令行输入以下命令可以安装antd库
    npm install antd --save

    按需加载antd配置,需要对create-react-app的默认配置进行自定义

    1. 更改启动插件。引入creat-app-rewired并修改package.json里的启动配置,由于新的react-app-rewired@2.x版本的关系,还需要安装customize-cra
    2. 输入yarn add react-app-rewired customize-cra (npm install yarn -g 进行安装yarn)
    3. 更改package.json文件
    "scripts": {
      // "start": "react-scripts start",
      "start": "react-app-rewired start",
      // "build": "react-scripts build",
      "build": "react-app-rewired build",
      //"test": "react-scripts test",
      "test": "react-app-rewired test",
      "eject": "react-scripts eject"
    },
    
    1. 然后在根目录创建一个config-overrides.js文件,用于修改默认配置
    2. 安装babel-plugin-import插件 yarn add babel-plugin-import
    3. 修改config-overrides.js文件内容
    const { override, fixBabelImports } = require('customize-cra');
    module.exports = override(
      fixBabelImports('import', {
        libraryName: 'antd',
        libraryDirectory: 'es',
        style: 'css',
      }),
    );
    

    注:
    按需加载之后不需要引入antd.css
    配置完按需加载之后就可以直接使用antd官网里面的例子进行引入组件

    // 例如
    import { Button } from 'antd';
    

    到这里按需加载ant-design就配置完成了,如果没有显示或者报错可以关闭控制台重启项目

    性能优化

    • 10.性能优化之PureComponent
      PureComponent是内部定制了shouldComponentUpdate生命周期的Component,它重写了一个方法来替换shouldComponentUpdate生命周期方法

    平常开发过程中设计组件能使用PureComponent的地方都尽量使用
    想要使用PureComponent特性要记住两个小原则:

    1. 确保数据类型是值类型
    2. 如果是引用类型,确保地址不变,同时不应当有深层次数据变化

    使⽤PureComponent可以省去shouldComponentUpdate⽣命周期的代码,代码会简单很多
    示例代码

    import React, { Component ,PureComponent} from 'react'
    import { Button } from 'antd';
    
    // 使用PureComponent来做优化
    class Title extends PureComponent {
      // 使用shouldComponentUpdate()生命周期来做优化
      // shouldComponentUpdate(nextProps){
      //   return nextProps.title !== this.props.title
      // }
      render() {
        console.log('我是title组件')
        return (
          <div>
            标题 : {this.props.title}
          </div>
        )
      }
    }
    class Count extends Component {
      render() {
        console.log('我是count组件')
        return (
          <div> 条数 : {this.props.count} </div>
        )
      }
    }
    
    export default class App extends Component {
      constructor(props){
        super(props)
        this.state={
          title:'时代周报',
          count:0
        }
      }
    
      handelAdd=()=>{
        this.setState({
          count:this.state.count+1
        })
      }
     
      render() {
        return (
          <div>
            <Button type="primary" onClick={this.handelAdd}>按钮</Button>
            <Title title={this.state.title}></Title>
            <Count count={this.state.count}></Count>
          </div>
        )
      }
    }
    
    • 11.性能优化之React.memo
      React.memo是⼀个⾼阶组件的写法
      React.memo让函数组件也拥有了PureComponent的功能
      使⽤例⼦如下:
    const Title = React.memo((props)=>{
      console.log('我是title组件')
      return(
        <div>
          标题 : {props.title}
        </div>
      )
    })
    
    • 12.三种优化的方法

    1.使用shouldComponentUpdate()生命周期

    class Title extends Component {
      shouldComponentUpdate(nextProps){
        return nextProps.title !== this.props.title
      }
      render() {
        console.log('我是title组件')
        return (
          <div> 标题 : {this.props.title}</div>
        )
      }
    }
    

    2.使用PureComponent

    // 首先要引入PureComponent
    import React, { Component ,PureComponent} from 'react'
    class Title extends PureComponent {
      render() {
        console.log('我是title组件')
        return (
          <div>
            标题 : {this.props.title}
          </div>
        )
      }
    }
    

    3.使用React.memo

    // 使用React.memo代替以上Title代码,让函数式组件也拥有PureComponent的功能
    const Title = React.memo((props)=>{
      console.log('我是title组件')
      return(
        <div>
          标题 : {props.title}
        </div>
      )
    })
    
    • 13.React高级使用之组件复合写法
      组件复合:类似于Vue中的插槽,复用组件
      react官方: 任何一个能用组件继承实现的,都能用组件复合实现
    import React from 'react'
    import {Button} from 'antd'
    
    function Dialog(props) {
        return (
            <div style={{ border: `2px solid ${props.color ||"green"}` }}>
                {props.children}   {/* 相当于vue里面的匿名插槽 */}
                {props.btn}   {/* 相当于vue里面的具名插槽 */}
            </div>
        )
    }
    
    export default function composition() {
        const btn = (
            <Button onClick={() => alert('具名插槽')}>Button</Button>
        )
        return (
            <div>
                <h1>组件复合的写法</h1>
                <Dialog color="pink" btn={btn}>
                    {/* 可以放任意想放的内容 */}
                    <p>匿名插槽</p>
                    <p>复合的标签</p>
                </Dialog>
            </div>
        )
    }
    

    高阶组件

    • 14.高阶组件
      高阶组件:HOC,为了提高组件的复用率,抽离出具有相同逻辑/展示的组件;
      高阶组件其实是一个函数,接收一个组件,返回一个新的组件,这个新组件可以对属性进行包装,也可以重写部分生命周期;

    简单使用
    HOCompt.js

    import React, { Component } from 'react'
    
    //编写一个高阶组件,传递一个组件进去,返回一个新的组件
    const withLearnReact = (Compt) => {
        const NewCompt = (props) => {
            return <Compt {...props} name="高阶组件" />
        }
        return NewCompt
    }
    
    class HOC extends Component {
        render() {
            return (
                <div>
                    <p>高阶组件写法</p>
                    <p>title : {this.props.title}</p>
                    <p>姓名 : {this.props.name}</p>
                </div>
            )
        }
    }
    export default withLearnReact(HOC)
    

    App.js

    import React, { Component } from 'react'
    import HOCompt from './HOCompt'
    
    export default class App extends Component {
      render() {
        return (
          <div> 
            <HOCompt title="App use"></HOCompt>
          </div>
        )
      }
    }
    
    
    • 15.链式调用
    1. 如果是纯展示的组件,则返回一个函数式组件;如果需要重写生命周期,则返回类组件;
    2. 在 HOCompt 组件中,新添加一个返回类组件的函数,并重写类组件的生命周期方法;
    /*
        1.创建一个高阶组件 const
        2.传入一个参数
        3.创建一个class组件
        4.把传入的属性传出去 用{...props}
    */
    //编写一个高阶组件,重写生命周期,注意:重写生命周期需要class组件
    const withLifeCycle = (Compt) =>{
        class NewCompt extends Component{
            //重写组件生命周期
            componentDidMount(){
                console.log('重写componentDidMount生命周期')
            }
            render(){
                return <Compt {...this.props}></Compt>
            }
        }
        return NewCompt
    }
    
    
    // 链式调用写法
    // withLearnReact(HOC) 返回一个函数式组件,传递了 name 和 title ,该组件作为 withLifeCycle() 的参数,返回一个重写了生命周期方法的类组件。
    export default withLifeCycle(withLearnReact(HOC))   
    
    • 16.高阶组件装饰器写法(代替链式调用)
    1. ES7中出现的装饰器语法,来处理高阶组件链式调用逻辑比较绕且不易理解
    2. 安装装饰器语法的babel编译插件
      npm install --save-dev @babel/plugin-proposal-decorators

    然后找到之前配置的config-overrides.js里面再写入配置装饰器写法, 此时的文件内容是

    const { override, fixBabelImports, addBabelPlugins } = require('customize-cra');
    module.exports = override(
      //配置antd按需加载
      fixBabelImports('import', {
        libraryName: 'antd',
        libraryDirectory: 'es',
        style: 'css',
      }),
      //配置支持高阶组件装饰器
      addBabelPlugins( 
        [
          '@babel/plugin-proposal-decorators',
          {
            legacy: true
          }
        ]
      ),
    );
    

    配置完之后,就可以把链式调用去掉,直接暴露HOC

    // export default withLifeCycle(withLearnReact(HOC))
    export default HOC
    

    然后需要在高阶组件声明之后依次写入以下代码,就可以代替链式调用

    @withLearnReact
    @withLifeCycle
    

    : 如果现在项目报错,可以重新npm install一下,然后再启动项目

    组件通信之上下文(context)

    • 17.组件通信之上下文(context)
    1. 当父组件与孙组件通信时,涉及到中间组件传递props,当中间组件很多时,可能破坏props的数据完整性;
    2. Context的作用就是实现多级组件之间的通信,让所有后代组件共享同一个父组件的数据,避免传递props
    3. Context有两个角色:Provider(数据提供)Consumer(数据读取),Vue也借鉴了这种方式,分别对应providerinject
      示例代码
    import React, { Component } from 'react'
    
    //数据
    let store = {
        name: '杜兰特',
        from: '布鲁克林篮网'
    }
    //创建上下文
    const Context = React.createContext()
    const { Provider, Consumer } = Context
    
    class Data extends Component {
        render() {
            return (
                <Consumer>
                    {store => {
                        return (
                            <div>
                                <p>姓名 : {store.name}</p>
                                <p>来自 : {store.from}</p>
                            </div>
                        )
                    }}
                </Consumer>
            )
        }
    }
    
    function Info(){
        return(
            <Data></Data>
        )
    }
    
    function ToolBar() {
        return (
            <Info></Info>
        )
    }
    
    export default class Context2 extends Component {
        render() {
            return (
                <div>
                    <Provider value={store}>
                        <ToolBar></ToolBar>
                    </Provider>
                </div>
            )
        }
    }
    
    

    相关文章

      网友评论

          本文标题:从零创建一个React项目

          本文链接:https://www.haomeiwen.com/subject/lzdioctx.html