如果你也刚入门React,来一起学习吧

栏目: 服务器 · 发布时间: 6年前

内容简介:本文主要写一些CRA脚手架的安装,React的语法,组件分类和组件传值等;如果您是已经在React上有丰富经验的开发者,欢迎指出文中有问题和可以改进的地方,对此我将表示感谢!这是react的官方站点React官网本文将主要分为:

本文主要写一些CRA脚手架的安装,React的语法,组件分类和组件传值等;如果您是已经在React上有丰富经验的开发者,欢迎指出文中有问题和可以改进的地方,对此我将表示感谢!

这是react的官方站点React官网

本文将主要分为:

  • React项目搭建
  • React语法
  • React组件介绍
  • React组件传值
  • 一个简单的组件化小例子
  • 本文暂时不介绍React-Router

阅读全文可能会花费您10-20分钟,如果觉得有兴趣,可以一起敲敲代码

(使用编辑器:VSCODE,插件: VS Code ES7 React/Redux/React-Native/JS snippets ,这个插件可以快速构建组件格式,如果想要练练手的同学就不要用快速指令了哦)

下面开始正文

1.通过脚手架创建React项目

First thing first,这里我们利用create-react-app(需要nodejs环境)来创建这个项目,毕竟比较方便嘛,有其他创建项目和服务的方式也可以使用。

如果你也刚入门React,来一起学习吧

找一个工作文件夹,然后打开命令行工具,输入 create-react-app mycode 就可以创建一个文件夹为 mycode 的项目文件夹,注意哦,这个项目名称不支持大写字母

ok,几分钟后会提示你脚手架初始化成功了

如果你也刚入门React,来一起学习吧

这里有几个指令:

  • npm start 开启开发服务器,一般默认是3000端口,启动后会自动弹出 localhost:3000 的页面
  • npm run build 为生产环境创建打包的静态文件
  • npm test 开启测试,这个我没有用过,有用过的同学可以在评论里分享一下使用技术文章
  • npm run eject Eject 将所有的工具(配置文件和 package.json 依赖库)解压到应用所在的路径,这个过程是不可逆的

那我们开始吧, cd mycode & npm start

2.基本语法

项目中两个重要的文件

如果你也刚入门React,来一起学习吧

我们启动后会看到这个界面,这是脚手架自带的。 请移步到编辑器,这里我们暂时只关注两个重要的文件

  • public/index.html 因为React搭建的是SPA,所以index.html是我们的主页,在文件中你也可以看到 <div id="root"></div> ,root就是根组件渲染的位置。
  • src/index.js 这是我们的主要的js文件,
    其中这一句表达式: render(<App></App>, window.root) 表明,我们使用一个渲染方式 render ,将 App 渲染到 root 中去,不论App中有什么,有多少层级,有多少组件,有多少逻辑,最终只有这一个入口。

React中的一切都从这里开始

我们将脚手架src下的所有文件全部删掉,创建一个空白的 index.js ,开始coding。

index.js 中,我们要做的就是,引入React库,引入react-dom,引入根组件,然后执行根组件的渲染方法:

  • import React from 'react' 注意这里的React必须首字母大写
  • import {Component} from 'react' ,引入组件方法,使用 {Component} 解构方式引入
  • import App from './App' ,引入根组件App,我们再下一步将会创建一个 src/App.js 作为我们的根组件,这里你可以取任何名字作为你的根组件js,我习惯取作App
  • render(<App></App>, window.root) 渲染组件到root容器, render 是react的核心渲染方法,后面我们会一直用到

语法介绍

① jsx简介

按照上一节的引入各种库,我们可以在index.js中coding来学习React的基础语法。

简要来说,react的核心语言是jsx,按照官方文档的举例:

const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;

ReactDOM.render(
  element,
  document.getElementById('root')
);
复制代码

我们在React下写的 <h1><h1> 这样的html标签,实际上都会按照上面的代码渲染到页面上,只不过作为一个很sweet的语法糖,我们不需要再写 ReactDOM.render() ,而是

let el = <h1>hello <span>world</span></h1>
render(el,window.root);
复制代码

使用javascript + xml语法,定义一个元素,然后再 render 渲染就可以了;在index.js中,你可以先注释掉原本的 render(<App></App>, window.root) 这句话,改为上面的代码,在localhost:3000中可以看到

如果你也刚入门React,来一起学习吧

hello world渲染上了页面;(后续如果有时间,会写一篇小文讲解一下react虚拟dom的实现原理)

② <>和{}

用一句话来概括就是: jsx元素/react元素 用<号标识, 看到{ 会认为里面包含的是js代码

1){}中执行js

  • 变量取值
let str = '<h1>world</h1>'
let el = (
  <div>
    <div>{str}</div>
  </div>
)
render(el,window.root);
复制代码

页面效果如图

如果你也刚入门React,来一起学习吧

str变量在{}中执行,div中间的内容应该是字符串 '<h1>world</h1>' ,而不是标签 h1

  • 注销

如果我们想在代码中写备注怎么办?是这样吗 // ,哦不行,加个 {//} 呢?哦不行,你在编辑器中可以看到

如果你也刚入门React,来一起学习吧
这后面的}也被注销了,所以在react中,我们使用 {/*hello*/}

的方式作为备注

  • 字符串解析为html

    在第一个小例子里面提到 '<h1>world</h1>' 会被作为字符串内容渲染,但是如果确实想要作为dom展示h1呢?

    这里我们使用 <div dangerouslySetInnerHTML={{ __html: str }}></div> ,这个API很长对吧,我们在容器上标注 dangerouslySetInnerHTML ~危险地设置innerHTML

    如果你也刚入门React,来一起学习吧
    这么做可能有被插入恶意执行脚本的的风险。
  • 执行一个方法

function a() { 
  return <h3>hello function</h3>
}

let el = (
  <div>
    <div>{a()}</div>
  </div>
)
复制代码

页面:

如果你也刚入门React,来一起学习吧
  • 循环

    添加一个li key的要时最好不要用数组的索引,为了能标示每个循环元素便于dom-diff, 一般用id,这里由于例子比较简单,我们使用数组的索引填写key ;使用arr.map的方式处理数组返回

let arr = [1,2,3];
// 
let el = (
  arr.map((item, key) => (
    <li key={key}>{item}</li>
  ))
)
复制代码
如果你也刚入门React,来一起学习吧
  1. jsx中html属性的几个特点
  • classclassName 这个驼峰方式的写法代替了原生html的class,但是class还是可以用的,脚手架会提示你这里应当使用 className

  • forhtmlFor 这个for是html的label上的for,用于指向控制的input,在jsx中我们使用htmlFor来替代

<label htmlFor="username">用户名</label>
<input type="text" id="username" />
复制代码
  • <div style="color:red">hello</div><div style={{ color: 'red' }}>hello</div> 外层的 {} 表示js语法的标示, { color: 'red' } 是对象

  • React.Fragment
    如果有用过vue的同学应该知道,vue返回的html一定要有一个根节点包裹,即返回的dom一定是一个,不能是平级的多个,即

<div>
    <div></div>
    <p></p>
</div>
复制代码

在react中同样,如果我们返回平级的多个div的话:

如果你也刚入门React,来一起学习吧

react会提示语法错误:jsx必须被一个闭合标签包裹

但是如果我们在某种情况下,必须使用一些平级元素怎么办呢,比如处于样式的考虑,我们外层没有什么需要div包裹的。这时候我们使用 <React.Fragment> 来包裹平级的元素,这个 <React.Fragment> 是没有实际意义的,就充当一个节点闭合标签。

let el1 = (
  <React.Fragment>
    <div>{str1}</div>
    <div>{a()}</div>
    <div>{JSON.stringify(obj)}</div>
    <div>{false?<span>你好</span>:void 0}</div>
  </React.Fragment>
)
复制代码

这样就不会报错了

总的来说,react的API较少,写jsx是很自由的,js+xml的方式,使js功底很深厚的开发者可以在html中任意的书写js逻辑,所写即所得,可能这就是react的魅力吧。

3.组件

在react项目中,基本上所有的结构功能都可以拆分成很细的一个个组件,比如一个页面上常用的菜单栏,可以拆分成:列表框List,列表项ListItem,列表链接Link等等,这样的好处是:

1.复用 2.方便维护 3.提高工作效率。

react声明组件的方式分为函数声明和类声明

  • 函数式声明组件

    函数式声明组件的方式如下

function Build(props) {
  let {title,content} = props;
  return (
    <div>
      <div>{title}</div>
      <div>{content}</div>
    </div>
  )
}

render(<div>
  <Build title="build1" content="content1"></Build>
  <Build title="build2" content="content2"></Build>
  <Build title="build3" content="content3"></Build>
</div>, window.root);
复制代码

如果我们仅需要展示一些信息到页面上,不需要去控制变化,则函数组件可以简单实现

组件的定义一定要是首字母大写的,函数式组件传值的方式是按照在组件中定义了属性名,在组件使用时直接写在组件上 <Build title="build3" content="content3"></Build>

函数组件的缺点是 1.没有this 2.没有状态 3.没有声明周期 可以通过定时器可以实现函数式组件中值的定时改变,比如这个例子

function Clock(props) {
  return <div> 时间更新:<span>{props.time}</span></div>
}
setInterval(()=>{
  render(<Clock time={new Date().toLocaleString()} />, window.root);
},1000)
复制代码
  • 类声明

    使用es6创建类的方式创建组件,类声明的组件拥有了状态,视图通过setState方法进行更新 之后我们做的小例子的组件,都用类声明的方式进行创建。在使用中来学习使用

受控组件 和 非受控组件

非受控组件:表单数据由DOM本身处理。即不受setState()的控制,与传统的HTML表单输入相似,input输入值即显示最新值(使用 ref 从DOM获取表单值)

受控组件:在HTML中,标签 <input><textarea><select> 的值的改变通常是根据用户输入进行更新。在React中,可变状态通常保存在组件的状态属性中,并且只能使用 setState() 更新,而呈现表单的React组件也控制着在后续用户输入时该表单中发生的情况,以这种由React控制的输入表单元素而改变其值的方式,称为:“受控组件”。

这里我们写一个非受控组件的小例子,我们输入的值通过点击显示出来;非受控组件常用于操作dom,较为方便

import React,{Component} from 'react';
import {render} from 'react-dom';
class UnControl extends Component{
  b=React.createRef();
  handleClick = () =>{
    alert(this.a.value); // 写法1
    alert(this.b.current.value) // 写法2
  }
  render(){
    return (<div>
      <input type="text" id="username" ref={dom=>this.a=dom}/>
      <input type="text" id="password" ref={this.b}/>
      <button onClick={this.handleClick}>点击</button>
    </div>)
  }
}
render(<UnControl></UnControl>, window.root);

复制代码
如果你也刚入门React,来一起学习吧
如果你也刚入门React,来一起学习吧

接下来我们将实现这样的一个小例子:

如果你也刚入门React,来一起学习吧

实现一个评论组件,类似于掘金下方的评论栏,我们将这个组件大功能拆分为

根组件 App ,列表组件 List ,列表项 ListItemComment 评论组件,在实现的过程中,我们会讨论组件间数据传递的方式。

4.更新视图的方法

首先,我们不拆分组件,将上述的例子简单构建出来,页面结构使用bootstrap UI( npm install boostrap@3 ) 组件。

在这个例子中,我们采用axios( npm install axios )请求初始列表数据,封装为一个request.js,代码如下:

import axios from 'axios';

axios.interceptors.response.use(function (res) {
  if (res.data.code === 0) {
    return res.data.users
  } else {
    return Promise.reject('错误');
  }
})

export default axios
复制代码

请求的数据格式自己简单拟定为:

{
    "code":0,
    "users": [
      {
        "id": 1,
        "avatar": "http://05.imgmini.eastday.com/mobile/20171112/20171112104845_40c4a989ba5a02f512b05336bff309f8_1.jpeg",
        "username": "Jim",
        "content": "Hi,你的文章很不错"
      },
      {
        "id": 2,
        "avatar": "http://05.imgmini.eastday.com/mobile/20171112/20171112104845_40c4a989ba5a02f512b05336bff309f8_1.jpeg",
        "username": "Jim",
        "content": "一般般的说"
      }
    ]
  }
复制代码

然后贴出我们的App.js,我们将全部的内容都放在App.js中,不拆分组件:

import React, { Component } from 'react';
import axios from './request'
import 'bootstrap/dist/css/bootstrap.css'
import './Common/common.css'

class App extends Component {
    state = {
        users: [],
        count: 0,
        id: 3
    }
    // 点赞功能
    increment = () => {
        this.setState({
            count: this.state.count + 1
        })
    }
    // 添加评论
    addComment = (val) => {
        let id = this.state.id;
        let users = [...this.state.users, { avatar:  "http://05.imgmini.eastday.com/mobile/20171112/20171112104845_40c4a989ba5a02f512b05336bff309f8_1.jpeg", content: val, username: 'Jim', id: id}];
        this.setState({
            users
        });
        this.state.id+=1;
    }
    content = React.createRef();
    // 提交数据
    handleSubmit = (e) => {
      e.preventDefault();
      this.addComment(this.content.current.value);
  }
    // 删除一条
    removeById = (id) => {
        let users = this.state.users.filter(user=>user.id!==id); // 排除列表里相同id的,即达到删除的目的
        this.setState({
            users
        })
    }
    // 获取列表数据
    async componentDidMount() {
        let users = await axios.get('/users.json');
        this.setState({
            users
        });
    }
    render() {
        return (
          <div className="container">
          <div className="panel panel-danger">
              <div className="panel-heading">
                  评论
              </div>
              <div className="panel-body">
              {
                this.state.users.map((user, index) => {
                  return (
                    <div className="media">
                    <div className="media-left">
                        <img className="avatar" src={user.avatar} />
                    </div>
                    <div className="media-right">
                        <h3>{user.username} </h3>
                        <div>评论:{user.content}</div>
                        <button className="btn btn-danger" onClick={(e)=>{
                            this.removeById(user.id)
                        }}>删除</button>

                    </div>
                </div>
                  )
                })
              }
              
              </div>
              <div className="panel-bottom">
                <form onSubmit={this.handleSubmit}>
                <textarea className="form-control" required ref={this.content}></textarea>
                <button type="submit" >评论</button>
                </form>
              </div>
          </div>
      </div>
            
        );
    }
}

export default App;
复制代码

效果:

如果你也刚入门React,来一起学习吧

到这里,我们的代码实现的功能有,加一条评论,也可以删除一条评论。

在React中,视图是受到数据的驱动的,我们最初定义的

state = {
        users: [],
        count: 0,
        id: 3
    }
复制代码

state中 users 的数据,会在 componentDidMount 生命周期时,获取到users列表,并通过 this.setState({ users }); 方法更新视图。其他的操作,类似于 handleSubmitremoveById 同样都是通过操作 state.users 的数据达到增删的目的。

5.拆分组件

考虑到一个项目中的复杂度,我们可以将上述App.js中的相关内容进行拆分为:列表组件 List ,列表项 ListItemComment 评论组件,这样,构造其他结构的时候,我们就不用再去重新写一遍相同的代码。我们在src文件夹下新建 components 文件夹,并且创建 List.js ListItem.js Comment.js

  • 列表项组件:
import React, { Component } from 'react'
export default class ListItem extends Component {
    state = {
        users: [],
        id: 100000
    }
    addComment = (val) => {
        let id = this.state.id;
        let users = [...this.state.users, { avatar:  "http://05.imgmini.eastday.com/mobile/20171112/20171112104845_40c4a989ba5a02f512b05336bff309f8_1.jpeg", content: val, username: 'Jim', id: id}];
        this.setState({
            users
        });
        this.state.id+=1;
    }
    handleClick = (id) => {
        this.props.removeById(id);
    }
    removeById = (id) => {
        let users = this.state.users.filter(user=>user.id!==id); // 排除列表里相同id的,即达到删除的目的
        this.setState({
            users
        })
    }
  render() {
    let {id, avatar, content, username} = this.props;
    return (
        <div className="media">
                    <div className="media-left">
                        <img className="avatar" src={avatar} />
                    </div>
                    <div className="media-right">
                        <h3>{username} {id}</h3>
                        <div>评论:{content}</div>
                        <button className="btn btn-danger" onClick={(e)=>{
                            this.handleClick(id)
                        }}>删除</button>

                    </div>
                </div>
       
    )
  }
}

复制代码
  • 列表组件
import React, { Component } from 'react'
import ListItem from './ListItem'
export default class List extends Component {
  static props = {
    showComment: true
  }
  render() {
    return (
      <div>
        {
            this.props.users.map((user, index) => {
                return (
                    <ListItem showComment={this.props.showComment} {...user} key={index} removeById={this.props.removeById} addComment={this.props.addComment}></ListItem>
                )
            })
        }
      </div>
    )
  }
}

复制代码
  • 评论框组件
import React, { Component } from 'react'

export default class Comment extends Component {
    content = React.createRef();
    handleSubmit = (e) => {
        e.preventDefault();
        this.props.addComment(this.content.current.value);
    }
    render() {
        return (
            <form onSubmit={this.handleSubmit}>
            <textarea className="form-control" required ref={this.content}></textarea>
            <button type="submit" >评论</button>
            </form>
        )
    }
}

复制代码
  • App.js变为
import React, { Component } from 'react';
import axios from './request'
import 'bootstrap/dist/css/bootstrap.css'
import './Common/common.css'
import Comment from './components/Comment'
import List from './components/List'
import {Provider} from './context'

class App extends Component {
    state = {
        users: [],
        count: 0,
        id: 3
    }
    increment = () => {
        this.setState({
            count: this.state.count + 1
        })
    }
    addComment = (val) => {
        let id = this.state.id;
        let users = [...this.state.users, { avatar:  "http://05.imgmini.eastday.com/mobile/20171112/20171112104845_40c4a989ba5a02f512b05336bff309f8_1.jpeg", content: val, username: 'Jim', id: id}];
        this.setState({
            users
        });
        this.state.id+=1;
    }
    removeById = (id) => {
        console.log(id)

        let users = this.state.users.filter(user=>user.id!==id); // 排除列表里相同id的,即达到删除的目的
        this.setState({
            users
        })
    }
    async componentDidMount() {
        let users = await axios.get('/users.json');
        this.setState({
            users
        });
    }
    render() {
        return (
        <Provider value={{increment: this.increment}}>
            <div className="container">
                <div className="panel panel-danger">
                    <div className="panel-heading">
                        评论
                    </div>
                    <div className="panel-body">
                        <List users={this.state.users} showComment={true} removeById={this.removeById} addComment={this.addComment}></List>
                    </div>
                    <div className="panel-bottom">
                    
                    <br/>
                    <Comment addComment={this.addComment}></Comment>
                    获得的赞数量{this.state.count}
                    </div>
                </div>
            </div>
        </Provider>
            
        );
    }
}

export default App;
复制代码

看到这里,一定有疑问,那么我们之前定义的 users 数据, removeByIdaddComment 的方法,怎么用到组件上呢?下面我们进行讲解。

6.组件间属性的传递

contextApi

上一节我们拆分的组件中,在列表组件中原本的循环体数据源,由 this.state.users 改为了使用 this.props.users ,而在App.js中传入的方式为

<List users={this.state.users} showComment={true} removeById={this.removeById} addComment={this.addComment}></List>
复制代码

传入和获取是一一对应的。

同样,由于 ListItem 组件需要 removeById 方法,所以我们从 App.jsList 组件就传入 removeById ,在 List 组件中调用 ListItem 时,再次传入 ListItem ,是一个父传子,子传孙的过程:

<ListItem showComment={this.props.showComment} {...user} key={index} removeById={this.props.removeById} addComment={this.props.addComment}></ListItem>
复制代码

ListItem 组件中,我们对 removeById 方法再包装一层

handleClick = (id) => {
        this.props.removeById(id);
}

...

<button className="btn btn-danger" onClick={(e)=>{
    this.removeById(user.id)
}}>删除</button>

复制代码

这里我们的删除方法来自于根组件传递下来的方法,子组件获取后,对同样是传递进来的 users 进行修改,以到达改变数据的目的。以上就是简单的组件传值的讲解。

contextApi

如果我们想给这个列表加一个点赞功能,即任何一个列表项组件都可以点赞,而且点赞还可以收集总数,这时候如果再去用父子间组件传值,可能代码实现起来会比较麻烦或者易错,因为涉及的层级很多。所以我们利用contextApi来实现(react16.3)。

引入的方式(在例子中,我抽离了这个引入到context.js,就不用在每个页面写一遍解构了):

import React from 'react'
let {Provider, Consumer} = React.createContext();

export {Provider, Consumer}
复制代码

在组件中的使用方法是,在父组件引入后,将父组件的返回值使用 Provider 包裹,并传入 value 属性:

import React, { Component } from 'react';
import {Provider} from './context'

class App extends Component {
    state = {
        users: [],
        count: 0,
        id: 3
    }
    // 点赞功能
    increment = () => {
        this.setState({
            count: this.state.count + 1
        })
    }
    render() {
        return (
        <Provider value={{increment: this.increment}}>
            <div className="container">
                <div className="panel panel-danger">
                    <div className="panel-heading">
                        评论
                    </div>
                    <div className="panel-body">
                        <List users={this.state.users} showComment={true} removeById={this.removeById} addComment={this.addComment}></List>
                    </div>
                    <div className="panel-bottom">
                    
                    <br/>
                    <Comment addComment={this.addComment}></Comment>
                    获得的赞数量{this.state.count}
                    </div>
                </div>
            </div>
        </Provider>
            
        );
    }
}

export default App;
复制代码

在子组件中,需要使用(消费)的返回值外层包裹 Consumer ,使用箭头函数传入 value 的值,即 Provider 传入的属性,即可在组件中直接调用父组件或更高阶的组件的传入属性。

import React, { Component } from 'react'
import {Consumer} from '../context'
...
export default class ListItem extends Component {
    ...
  render() {
    let {id, avatar, content, username} = this.props;
    return (
        <Consumer>
            {(value)=>{
                return <div className="media">
                    <div className="media-right">
                        ...
                        <button className="btn btn-primary" onClick={()=>{
                            value.increment()
                        }}>赞</button>
                        ...
                      </div>
                </div>
            }}
            
        </Consumer>
       
    )
  }
}

复制代码

总结

以上是我学习React入门的一些小总结,写了一个不太成熟的例子来练手,在表述上可能有一些跳跃还请见谅。这里附上这个小例子的Github代码,有需要详细了解的同学可以看看: code

希望我的文章能帮到你。


以上所述就是小编给大家介绍的《如果你也刚入门React,来一起学习吧》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Ordering Disorder

Ordering Disorder

Khoi Vinh / New Riders Press / 2010-12-03 / USD 29.99

The grid has long been an invaluable tool for creating order out of chaos for designers of all kinds—from city planners to architects to typesetters and graphic artists. In recent years, web designers......一起来看看 《Ordering Disorder》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

html转js在线工具
html转js在线工具

html转js在线工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换