从零创建一个React项目

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>
        )
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335