一、简介
image.png1、安装Node.js
使用React.js
是可以用最原始的<Script>
标签进行引入的,但是这实在是太low了,工作中也不会有这种形式进行引入。所以在学习的时候,我们就采用脚手架形式的安装。这就需要我们安装Node.js
,用里边的npm
来进行安装。
安装Node只需要进入Node网站,进行响应版本的下载,然后进行双击安装就可以了。
Node中文网址:http://nodejs.cn/ (建议你在这里下载,速度会快很多)
需要你注意的是,一定要正确下载对应版本,版本下载错误,可是没有办法使用的哦。
Node.js
安装好以后,如果是Windows系统,可以使用 Win+R
打开运行,然后输入cmd
,打开终端(或者叫命令行工具)。
输入代码:
node -v
如果正确出现版本号,说明Node安装成功了,需要说明的是,你的版本号可能跟我视频中的有所不同,这无关紧要。
然后再输入代码:
npm -v
如果正确出现版本号,说明npm也是没问题的,这时候我们的Node.js
安装就算完成了。
2、脚手架的安装
Node安装好之后,你就可以使用npm命令来安装脚手架工具了,方法很简单,只要打开终端,然后输入下面的命令就可以了。
npm install -g create-react-app
create-react-app
是React官方出的脚手架工具,其实有很多第三方的脚手架工具,也有很多优秀的。但是作为初学者为了减少踩坑,所以我们使用官方的脚手架。
3、创建第一个React项目
脚手架安装好以后,就可以创建项目了,我们在D盘创建一个ReactDemo文件夹,然后进入这个文件夹,创建新的React项目。
D: //进入D盘
mkdir ReactDemo //创建ReactDemo文件夹
create-react-app demo01 //用脚手架创建React项目
cd demo01 //等创建完成后,进入项目目录
npm start //预览项目,如果能正常打开,说明项目创建成功
报错解决备注:
npm install -g create-react-app
在mac上安装时,没有效果,使用了 cnpm install -g create-react-app
对应的在使用 create-react-app demo01 时,同样没有效果,此时应将cnpm设置为默认的安装方式
npm config set registry https://registry.npm.taobao.org
二、代码之路
import React,{Component} from 'react';
等价于 下面的两行(es6解构赋值)
import React from 'react';
const Component = React.Component
1、JSX简介
JSX就是Javascript和XML结合的一种格式。React发明了JSX,可以方便的利用HTML语法来创建虚拟DOM,当遇到
<
,JSX就当作HTML解析,遇到{
就当JavaScript解析.
比如我们写一段JSX语法
<ul className="my-list">
<li>JSPang.com</li>
<li>I love React</li>
</ul>
比如我们以前写一段代码JS代码:
var child1 = React.createElement('li', null, 'JSPang.com');
var child2 = React.createElement('li', null, 'I love React');
var root = React.createElement('ul', { className: 'my-list' }, child1, child2);
从代码量上就可以看出JSX语法大量简化了我们的工作。
2、组件和普通JSX语法区别
这个说起来也只有简单的一句话,就是你自定义的组件必须首写字母要进行大写,而JSX是小写字母开头的。
这个也算是一个比较重要的知识点吧。
注意: 这里的HTML类名 要用className定义,不能使用class
3、 JSX使用注意事项
在React中,最外层需要有一个包裹元素,最外层不想有包裹元素的话,使用<Fragment>标签。
要想使用<Fragment>,需要先进行引入。
import React,{Component,Fragment } from 'react'
然后把最外层的<div>标签,换成<Fragment>标签。
import React,{Component,Fragment } from 'react'
class Xiaojiejie extends Component{
constructor (props) {
super(props) //调用父类的构造函数,固定写法
this.state = {
inputValue: '',
list: ['haha', '精油推背'],
ceshiHTML:'<h1>ssss</h1>'
}
}
render(){
return (
<Fragment>
{/* 正确注释的写法 */}
<label htmlFor="jspang">加入服务:</label>
<div><input id='jspang' /> <button> 增加服务 </button></div>
<ul>
<li>头部按摩</li>
<li>精油推背</li>
<li dangerouslySetInnerHTML={{__html:this.state.ceshiHTML}}></li>
</ul>
</Fragment>
)
}
}
export default Xiaojiejie
注意:
1、其实这边的写法,类似于Vue的组件写法,html必须有一个最外层html包裹;此时如果你不需要最外层的html,可以将最外层的html用Fragment代替,vscode不会报错,同时在html中也不会有最外层的html显示
2、jsx的注释写法,{/* 正确注释的写法 */}
3、HTML类名要把class换成className,它是防止和js中的class类名冲突
4、dangerouslySetInnerHTML={{__html:XX}},如果XX是含html标签的,则可以解析html标签;
5、label标签不能使用for。它容易和javascript里的for循环混淆,会提示你使用htmlFor。(想点击“加入服务”直接可以激活文本框,方便输入。按照html的原思想,是直接加ID就可以了)
4、方法定义以及使用
import React,{Component,Fragment} from 'react';
class Xiaojiejie extends Component{
constructor (props) {
super(props) //调用父类的构造函数,固定写法
this.state = {
inputValue: '',
list: ['头部按摩', '精油推背']
}
}
render() {
return(
<Fragment>
<div>
<input value = {this.state.inputValue} onChange={this.inputChange.bind(this)}/>
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
<ul>
{
this.state.list.map((item, index) => {
return (
<li key={item + index}
onClick = {this.deleteItem.bind(this, index)}
> {item} </li>
)
})
}
</ul>
</Fragment>
)
}
inputChange(e) {
this.setState({
inputValue: e.target.value
})
}
addList(){
this.setState({
list: [...this.state.list, this.state.inputValue],
inputValue: ''
})
}
deleteItem(index){
console.log(index)
let list = this.state.list
list.splice(index,1)
this.setState({
list: list
})
}
}
export default Xiaojiejie
注意:
1、onChange是一个change事件,在React里,事件的绑定要使用小驼峰命名法。
2、这里的.bind(this),是ES6的语法,此时里面需要绑定this,否则在方法中使用this时,this的指向并不是当前的dom元素。
3、React中的循环是写在{}中的,在循环中绑定方法的话,需要写在return()的括号中,此时若想要传值,和this一样写在.bind()中,在定义方法的时候,可以带参数,可以拿到。类似vue
4、React中对state里面的数据进行操作时,不能直接写this.state.XX = '',需要使用this.setState({XX:''})
5、React是禁止直接操作state的,即我们在删除this.state.list中的数据时,是将this.state.list赋值给一个新的数组,去操作这个新数据,最后将新数组的值赋值给this.state.list
5、父子组件之间的通信
- 父组件
import React, { Component } from 'react';
// 这里的引入 FoodItem首字母要大写,不然会报错
import FoodItem from './foodItem'
import './shitang.css'
class Shitang extends Component {
constructor(props){
super(props)
this.state = {
foodValue: '',
foodList: ['土豆丝', '土豆片', '土豆块', '土豆泥']
}
this.addFood = this.addFood.bind(this)
this.inputChange = this.inputChange.bind(this)
this.deleteItem = this.deleteItem.bind(this)
}
render() {
return (
<div>
<label htmlFor='food'>添加新菜品</label>
<input
id='food'
vaule={this.state.foodValue}
onChange={this.inputChange}
// ES6语法 -- 绑定DOM元素
ref={ input => {this.input = input}}></input>
<button onClick={this.addFood}>添加菜品</button>
<ul>
{
this.state.foodList.map((item, index) => {
return (
<li
key={item + index}
// onClick={this.deleteItem(index)} --- 使用时会立即执行deleteItem方法
onClick={() => this.deleteItem(index)}
>{item}</li>
)
})
}
</ul>
{/* 这里使用了子组件 FoodItem */}
<ul className='food' ref = {ul => {this.ul = ul}}>
{
this.state.foodList.map((item, index) => {
return (
<FoodItem
key={item + index}
content ={item}
index = {index}
onClick={() => this.deleteItem(index)}
deleteItem = {this.deleteItem}
/>
)
})
}
</ul>
</div>
);
}
inputChange () {
this.setState({
foodValue: this.input.value
})
}
addFood () {
this.setState({
foodList: [...this.state.foodList,this.state.foodValue]
})
console.log(this.ul.querySelectorAll('li').length)
// 现象: 此时调用打印的值是与实际不相符的
// 原因: this.setState这个方法是异步的,当执行打印的时候,this.setState可能并没有执行完成
// 解决方案: this.setState有一个回调函数,此时将打印放到回调函数中即可
// this.setState({
// foodList: [...this.state.foodList,this.state.foodValue]
// },() => {
// console.log(this.ul.querySelectorAll('li').length)
// })
this.input.value = ''
}
deleteItem (index) {
let list = this.state.foodList
list.splice(index, 1)
this.setState({
foodList: list
})
}
}
export default Shitang;
- 子组件
import React, { Component } from 'react';
import PropTypes from 'prop-types'
class foodItem extends Component {
constructor(props){
super(props)
this.deleteFood = this.deleteFood.bind(this)
}
render() {
return (
<li
onClick={this.deleteFood}
>
{this.props.username}--- 会做的菜 ---{this.props.content}
</li>
);
}
deleteFood () {
this.props.deleteItem(this.props.index)
}
}
// 校验传递值 PropTypes 使用时 需先引入
foodItem.propTypes ={
// 必传值的校验 ---isRequired
content:PropTypes.string.isRequired,
deleteItem:PropTypes.func,
index:PropTypes.number,
username: PropTypes.string.isRequired
}
// 若设置必传值,又担心没有值则可使用默认值defaultProps;
// 此时若是父组件没有传递username这个参数,则会使用默认值渲染
foodItem.defaultProps = {
username: '小花'
}
export default foodItem;
image.png
注意:
1、事件绑定的另一种写法:onClick={this.addFood.bind(this)}
====>onClick = {this.addFood}
;前面的写法是在绑定事件的时候将this指向一起绑定;后面的写法,this的指向是在constructor
方法中绑定
this.addFood = this.addFood.bind(this)
2、在引入子组件时,子组件名称首字母要大写import FoodItem from './foodItem'
,否则会报错
3、在使用onClick={this.deleteItem(index)}
进行传参的时候,会立即执行deleteItem
方法,并不是点击后触发deleteItem
方法。修改为onClick={() => this.deleteItem(index)}
原因暂时不知道,但是这样可以解决
4、ref
来绑定DOM元素:绑定时最好使用ES6语法中的箭头函数。ref={ ul => {this.ul = ul}}
,此时需要注意:若此时我们想要获取ul
下面li
的个数,且li
是动态生成的(addFood
方法中),若是在this.setState()
方法后执行,则会出现与实际不符的情况。 原因是this.setState()
方法是异步加载的,当执行打印的时候,this.setState()
可能并没有执行完成。解决方案:React的this.setState()
方法提供了回调函数,写在回调中即可。
5、React有一个特性是单项数据流,即父组件向子组件传递一个数组的话,是可以传递成功的,但是这个数组在子组件中只可使用不可改变。
6、父组件向子组件传递参数和方法:在父组件中的子组件上传递(如下),传递的数据在子组件中用this.props.XXX
接收,方法也一样
<FoodItem
key={item + index}
content ={item}
index = {index}
onClick={() => this.deleteItem(index)}
deleteItem = {this.deleteItem}
/>
7、在子组件中校验传递值( PropTypes) 使用时 需先引入
import PropTypes from 'prop-types'
//foodItem --- 子组件的类名
foodItem.propTypes ={
// 必传值的校验 ---isRequired
content:PropTypes.string.isRequired,
deleteItem:PropTypes.func,
index:PropTypes.number,
username: PropTypes.string.isRequired
}
// 若设置必传值,又担心没有值则可使用默认值defaultProps;
// 此时若是父组件没有传递username这个参数,则会使用默认值渲染
foodItem.defaultProps = {
username: '小花'
}
8、子组件向父组件传递数据时,需要在子组件中使用
this.props.xx(val)
(其中val为需要传递给父组件的数据,可以为单个数据,也可以为对象的形式;xx表示在父组件中绑定的是方法名,这个方法在绑定之后也需要在父组件的constructor
中使用bind()
方法绑定this的指向)
6、react的生命周期
image.pngInitialization:初始化阶段。
Mounting: 挂载阶段。
Updation: 更新阶段。
Unmounting: 销毁阶段
备注:以上来自技术胖博主,以及自己的遇到的问题
网友评论