前言
学习React是今年的计划之一,再加上之前学习了Vuejs,想了解两者之间的区别,因此大致学习下React,有机会再用React写一个项目。
React的三大体系
- 用于Web开发和组件的编写
- ReactNative用于移动端开发
- ReactVR用于虚拟现实技术的开发
安装
安装Nodejs
使用Reactjs最原始的方法就是script标签引入,但这太low了,并且在工作当中也不会这样引用,因此安装Nodejs来使用react最佳~打开下面的网址,自行安装即可。
Nodejs中文网址:http://nodejs.cn/
脚手架安装
安装完Nodejs后,使用npm命令安装脚手架
npm install -g create-react-app
创建第一个React项目
create-react-app demo
目录结构
- src:项目代码主目录
- public:公共文件,例如可以存储样式文件、图标等等
- node_modules:项目的依赖包
- gitignore:git的选择性上传配置文件
- package-lock.json:锁定安装时的版本号,以保证其他人再npm install时大家的依赖能保证一致
src文件夹
- index.js:项目的入口文件
- index.css:index.js里的css文件
- app.js:相当于一个方法模块,也是一个简单的模块化编程
- serviceWorker.js:用于移动端开发,PWA必须用到这个文件,有了这个文件,就相当于有了离线浏览的功能
编写第一个HelloWord
index.js 入口文件的编写
src目录下,新建一个文件index.js,写入下面4行代码
import React from 'react'
import ReactDOM form 'react-dom'
import App from './App' // 这里其实省略了.js,完整是./App.js,但这里可以省略,react会自动识别
ReactDOM.render(<App />,document.getElementById('root'))
上面的代码,首先引入了React必要的两个文件,然后引入了一个APP组件,再使用ReactDOM渲染到了rootID上.PS:这个root ID是在public\index.html中。
app组件的编写
import React, {Component} from 'react' // ES6的语法-解构赋值,当然也可以写成下面两行
// import React from 'react'
// const Component = React.Component
class App extends Component{
render(){
return (
<div>
Hello React
</div>
)
}
}
export default App;
React中JSX语法
简介
javascript + xml = jsx。当遇到<,JSX就当作HTML解析,遇到{就当JS解析,举个例子(JSX语法)
<ul className="list">
<li>视觉志</li>
<li>I love React</li>
</ul>
而在以前写一段JS:
var child1 = React.createElement('li', null, '视觉志');
var child2 = React.createElement('li', null, 'I love React');
var root = React.createElement('ul', { className: 'list' }, child1, child2);
从代码量上看出JSX语法大量简化了我们的工作.
小坑
自定义的组件必须首写字母要进行大写,而JSX是小写字母开头的
使用三元运算符
import React from 'react'
const Component = React.Component
class App extends Component{
render(){
return (
<ul className="my-list">
<li>{false?'视觉志':'iobiji.com'}</li>
<li>I love React</li>
</ul>
)
}
}
export default App;
实例
新建一个组件
import React,{Component} from 'react'
class Demo extends Component{
render(){
return (
<div>
<ul>
<li>aaa</li>
<li>bbb</li>
</ul>
</div>
)
}
}
export default Demo;
组件外层包裹原则
划重点,比如上面的代码,去掉最外层的<div>就会报错,因为React要求必须在一个组件的最外层进行包裹,这就类似于Vuejs中的只允许一个根div
Fragment标签
加上最外层的DIV,组件就是完全正常的,但布局就偏不需要这个最外层的标签怎么办?比如在作Flex布局时,外层还真的不能有包裹元素。这种矛盾其实React已经给了我们解决方案,也就是<Fragment>标签
引入:
import React,{Component,Fragment} from 'react'
然后把最外层的<div>换成
响应式设计和数据的绑定
React不建议直接操作DOM元素,而是要通过数据进行驱动,改变界面中的效果。React会根据数据的变化,自动的帮助你完成界面的改变。所以在写React代码时,你无需关注DOM相关的操作,只需要关注数据的操作就可以了(这也是React如此受欢迎的主要原因,大大加快了我们的开发速度)。
数据定义在组件中的构造函数里constructor
constructor(props){
super(props) //调用父类的构造函数,固定写法
// 还有就是一般this在这里绑定,特别是在高级组件开发中,会有很大的作用
this.state={
inputValue:'' , // input中的值
list:[]
}
}
在React中的数据绑定和Vue中几乎一样,就是使用{}来标注,其实这也算是js代码的一种声明。比如现在我们要把inputValue值绑定到input框中,只要写入下面的代码就可以了。其实说白了就是在JSX中使用js代码。
<input value={this.state.inputValue} />
绑定事件
这时候到界面的文本框中去输入值,是没有任何变化的,这是因为我们强制绑定了inputValue的值。如果要想改变,需要绑定响应事件,改变inputValue的值。比如绑定一个改变事件,这个事件执行inputChange()(当然这个方法还没有)方法。
在render()方法下建立一个inputChange()方法:
inputChange(e){
console.log(e.target.value);
this.state.inputValue = e.target.value;
}
其实,这里犯了两个错误:
- this的指向不对,需要重新用bind设置指向(ES6的语法)
- React中改变数据需要使用this.setState方法
解决:
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
另外需要在inputChange方法加入setState方法来改变值:
inputChange(e){
// console.log(e.target.value);
// this.state.inputValue=e.target.value;
this.setState({
inputValue:e.target.value
})
}
key值错误解决
<ul>
{
this.state.list.map((item,index)=>{
return <li key={index+item}>{item}</li>
})
}
</ul>
数组下标的传递
<ul>
{
this.state.list.map((item,index)=>{
return (
<li
key={index+item}
onClick={this.deleteItem.bind(this,index)}
>
{item}
</li>
)
})
}
</ul>
遍历
<ul>
{
this.state.list.map((item,index)=>{
return (
<ServiceItem
key={index+item}
list={item}
index={index} />
)
})
}
</ul>
JSX防踩坑的几个地方
JSX代码注释
//第一次写注释,这个是错误的
<div>
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
正确的写法有以下两种:
# 第一种
{/* 正确注释的写法 */}
# 第二种
{
//正确注释的写法
}
第二种不太优雅,推荐第一种注释方法,当然使用编辑器的快捷注释也可以,最简单的方法
JSX中的class陷阱
使用class属性是错的,必须使用className,它是防止JS中的class类名冲突。
<input className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
JSX中的html解析问题
如果想在文本框里输入一个<h1>标签,并进行渲染。默认是不会生效的,只会把<h1>标签打印到页面上,这并不是我想要的。如果工作中有这种需求,可以使用dangerouslySetInnerHTML属性解决。具体代码如下:
<ul>
{
this.state.list.map((item,index)=>{
return (
<li
key={index+item}
onClick={this.deleteItem.bind(this,index)}
dangerouslySetInnerHTML={{__html:item}}
>
</li>
)
})
}
</ul>
JSX中<label>标签的坑
先看下面的代码,我们在文本框前面加入一个<label>
<div>
<label for="service">加入服务:</label>
<input id="service" className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
但console会有红色警告提示的。大概意思是不能使用for.它容易和javascript里的for循环混淆,会提示你使用htmlFor
<div>
<label htmlFor="service">加入服务:</label>
<input id="service" className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
组件的拆分
新建一个组件
import React, { Component } from 'react';
class ServiceItem extends Component { //cc
render() {
return (
<div>服务列表</div>
);
}
}
export default ServiceItem;
第二种方法
import React from 'react';
const ServiceItem = (props) => {
<div>服务列表</div>
}
export default ServiceItem;
在另外一个组件中使用import引入这个组件:
import ServiceItem from './ServiceItem'
然后直接写入<ServiceItem />标签即可
父子组件的传值
使用组件属性的形式,父组件传值给子组件。比如加入list属性,然后给属性传值{item},这样就完成了父组件向子组件传值.
<ServiceItem list={item} />
在接收数据的组件通过使用this.props.xxx的形式接收即可,例如
import React, { Component } from 'react';
class ServiceItem extends Component { //cc
render() {
return (
<div>{this.props.list}</div>
);
}
}
export default ServiceItem;
子组件向父组件传递数据
首先子组件绑定点击事件:
import React, { Component } from 'react';
class ServiceItem extends Component {
render() {
return (
<div onClick={this.handleClick}>{this.props.list}</div>
);
}
handleClick(){
console.log('点击了')
}
}
export default ServiceItem;
父组件向子组件传递点击删除方法
<ul>
{
this.state.list.map((item,index)=>{
return (
<ServiceItem
key={index+item}
list={item}
index={index}
//关键代码-------------------start
deleteItem={this.deleteItem.bind(this)}
//关键代码-------------------end
/>
)
})
}
</ul>
子组件调用父组件传递的方法
import React, { Component } from 'react';
class ServiceItem extends Component {
constructor(props){
super(props)
this.handleClick=this.handleClick.bind(this)
}
render() {
return (
<div onClick={this.handleClick}>
{this.props.list}
</div>
);
}
handleClick(){
this.props.deleteItem(this.props.index)
}
}
export default ServiceItem;
ref的使用方法
代替原来的e.target.value
以前的案例中使用了e.target,这并不直观,也不好看。这种情况可以使用ref来进行解决。
inputChange(e){
this.setState({
inputValue:e.target.value
})
}
如果要使用ref来进行,需要现在JSX中进行绑定, 绑定时最好使用ES6语法中的箭头函数,这样可以简洁明了的绑定DOM元素。
<input
id="addService"
className="input"
value={this.state.inputValue}
onChange={this.inputChange.bind(this)}
//关键代码——----------start
ref={(input)=>{this.input=input}}
//关键代码------------end
/>
绑定后可以把上边的类改写成如下代码:
inputChange(){
this.setState({
inputValue:this.input.value
})
}
React和Vue的对比
React.js相对于Vue.js,它的灵活性和协作性更好一点,个人认为react是UI框架,而vue是数据驱动框架,因为vue.js有着丰富的API,实现起来更加简单快速。