内容简介:本文主要写一些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环境)来创建这个项目,毕竟比较方便嘛,有其他创建项目和服务的方式也可以使用。
找一个工作文件夹,然后打开命令行工具,输入 create-react-app mycode
就可以创建一个文件夹为 mycode
的项目文件夹,注意哦,这个项目名称不支持大写字母
ok,几分钟后会提示你脚手架初始化成功了
这里有几个指令:
-
npm start
开启开发服务器,一般默认是3000端口,启动后会自动弹出localhost:3000
的页面 -
npm run build
为生产环境创建打包的静态文件 -
npm test
开启测试,这个我没有用过,有用过的同学可以在评论里分享一下使用技术文章 -
npm run eject
Eject 将所有的工具(配置文件和 package.json 依赖库)解压到应用所在的路径,这个过程是不可逆的
那我们开始吧, cd mycode
& npm start
。
2.基本语法
项目中两个重要的文件
我们启动后会看到这个界面,这是脚手架自带的。 请移步到编辑器,这里我们暂时只关注两个重要的文件
-
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中可以看到
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); 复制代码
页面效果如图
str变量在{}中执行,div中间的内容应该是字符串 '<h1>world</h1>'
,而不是标签 h1
- 注销
如果我们想在代码中写备注怎么办?是这样吗 //
,哦不行,加个 {//}
呢?哦不行,你在编辑器中可以看到
{/*hello*/}
的方式作为备注
-
字符串解析为html
在第一个小例子里面提到
'<h1>world</h1>'
会被作为字符串内容渲染,但是如果确实想要作为dom展示h1呢?这里我们使用
这么做可能有被插入恶意执行脚本的的风险。<div dangerouslySetInnerHTML={{ __html: str }}></div>
,这个API很长对吧,我们在容器上标注dangerouslySetInnerHTML
~危险地设置innerHTML -
执行一个方法
function a() { return <h3>hello function</h3> } let el = ( <div> <div>{a()}</div> </div> ) 复制代码
页面:
-
循环
添加一个li key的要时最好不要用数组的索引,为了能标示每个循环元素便于dom-diff, 一般用id,这里由于例子比较简单,我们使用数组的索引填写key ;使用arr.map的方式处理数组返回
let arr = [1,2,3]; // let el = ( arr.map((item, key) => ( <li key={key}>{item}</li> )) ) 复制代码
- jsx中html属性的几个特点
-
class
→className
这个驼峰方式的写法代替了原生html的class,但是class还是可以用的,脚手架会提示你这里应当使用className
-
for
→htmlFor
这个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会提示语法错误: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); 复制代码
接下来我们将实现这样的一个小例子:
实现一个评论组件,类似于掘金下方的评论栏,我们将这个组件大功能拆分为
根组件 App
,列表组件 List
,列表项 ListItem
和 Comment
评论组件,在实现的过程中,我们会讨论组件间数据传递的方式。
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中,视图是受到数据的驱动的,我们最初定义的
state = { users: [], count: 0, id: 3 } 复制代码
state中 users
的数据,会在 componentDidMount
生命周期时,获取到users列表,并通过 this.setState({ users });
方法更新视图。其他的操作,类似于 handleSubmit
和 removeById
同样都是通过操作 state.users
的数据达到增删的目的。
5.拆分组件
考虑到一个项目中的复杂度,我们可以将上述App.js中的相关内容进行拆分为:列表组件 List
,列表项 ListItem
和 Comment
评论组件,这样,构造其他结构的时候,我们就不用再去重新写一遍相同的代码。我们在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
数据, removeById
和 addComment
的方法,怎么用到组件上呢?下面我们进行讲解。
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.js
的 List
组件就传入 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,来一起学习吧》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Scrum精髓
Kenneth Rubin / 姜信宝、米全喜、左洪斌、(审校)徐毅 / 清华大学出版社 / 2014-6-1 / CNY 79.00
短短几年时间,Scrum跃升为敏捷首选方法,在全球各地得以普遍应用。针对如何用好、用巧这个看似简单的框架,本书以通俗易懂的语言、条理清晰的脉络阐述和提炼出Scrum的精髓。全书共4部分23章,阐述了七大核心概念:Scrum框架,敏捷原则,冲刺,需求和用户故事,产品列表,估算与速率,技术债;三大角色:产品负责人,ScrumMaster,开发团队以及Scrum团队构成:Scrum规划原则及四大规划活动......一起来看看 《Scrum精髓》 这本书的介绍吧!