React 的入门笔记
写在前面,笔者才疏学浅,写此文章仅做参考!不对之处,敬请海涵!
阅读此文章,我相信你或有 Vue.js 的学习经验。并且有一定的编程能力,也相信你有学习 React 的想法。我是先阅读[React的官方文档](https://react.docschina.org/)
,然后再通过看[jspang视频](https://jspang.com/detailed?id=46#toc21)
一步一步入门。
前言
分享 Winter老 师说过一个观点:对于任何语言来说,必定是“用规范的文法,去表达特定语义,最终操作运行时的”一个过程。关键字:文法
语义
运行时
。笔者的能力有限,在这篇文章中,会按照自己学习之前想了解的几个方面入手。
初识
简介
- React 是一个用于构建用户界面的
JAVASCRIPT
库。 - React 主要用于构建UI,很多人认为 React 是 MVC 中的 V( 视图)。
- React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站, 并于 2013 年 5 月开源。
- React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。
- React 需手动实现双向数据绑定,通过 react 中的状态管理。
- React 的基本思想就是组件 每一个组件 同时也 可以看作是一个类 都继承自 React.Component。
安装步骤
所谓工欲善其事,必先利其器
- 安装脚手架
npm install -g create-react-app
- 初始项目
create-react-app demo
- 安装插件
// 假如你用的是 vscode 编写代码
// 可以快速帮你生成文件结构
simple-react-snippets
- Google 插件
// 科学上网,自行安装
// 可以帮你省去 console.log 的时间
React Developer Tools
启程
JSX
JSX 是 Javascript 和 XML 结合的一种格式。React发明了 JSX,可以方便的利用 HTML 语法来创建虚拟 DOM,当遇到<,JSX 就当作HTML解析,遇到{就当 JavaScript 解析。
class App extends Component{
render(){
return (
<ul className="my-list">
<li>I love React</li>
</ul>
)
}
}
简单点理解就是在 JS 中写 HTML 代码。
数据绑定
React 和 Vue 都不建议用户直接操作 DOM 元素,而是要通过数据进行驱动,改变界面中的效果。在 React 中我们定义数据是在组件中 constructor 中。
//js的构造函数,由于其他任何函数执行
constructor(props){
super(props) //调用父类的构造函数,固定写法
this.state={
inputValue:'' , // input中的值
list:[] //列表
}
}
具体为什么是在 this.state
中定义,[React的官方文档](https://react.docschina.org/)
有详细说明 state 的文档。反正,作为初学者,咱们就先记着是这样定义。
<!-- 绑定数据 -->
<input value={this.state.inputValue} />
绑定事件
数据定义好之后,接下来的问题是我要对这个数据进行操作,这就需要绑定事件了。
<input value={this.state.inputValue} onChange={this.inputChange} />
这时候你就在 input 标签上绑定了 onChange 事件。
JSX 中有一些原生的方法或属性名称都有所改变。有一些是遵循驼峰命名,例如
onchange
=>onChange
;有一些是改变了名称,例如:class
=>className
。
特别注意
在 JSX 中绑定好事件之后,一定要同时绑定 this
// 在没有手动绑定 this 的时候,如果这样使用,直接会报错。
inputChange(e){
console.log(this); // undefined
this.state.inputValue=e.target.value; // 报错
}
有两个错误
- this 指向不对,你需要重新用 bind 设置一下指向( ES6 的语法)
- React 中改变值需要使用 this.setState 方法
解决方案
// 绑定 this
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
// 修改数据
inputChange(e){
this.setState({
inputValue:e.target.value
})
}
// 我们最好不要直接改变 state 中的值,而是定义一个临时变量改变,这样有利于时空回溯
inputChange(e){
let temp = e.target.value
this.setState({
inputValue:temp
})
}
条件渲染
Vue 中的条件渲染表达式
v-if="boolean"
;Angular 中的条件渲染表达式*ngIf="boolean"
;而 React 中使用逻辑表示式&&
;
<ul>
{this.state.list.map((item, key) => {
return (
!item.checked && (
<li key={key + item}>
<input
type="checkbox"
checked={item.checked}
onChange={this.checkBoxState.bind(this, key)}
/>
{item.title}---{' '}
<button onClick={this.removeItem.bind(this, key)}>
删除
</button>
</li>
)
);
})}
</ul>
true && expression 始终计算为 expression,并且 false && expression 始终计算为 false
列表渲染
在 JSX 中我们使用 map 方法进行列表渲染
<ul>
{this.state.list.map((item, key) => {
return (
<li key={key + item}>
{item}
</li>
)
);
})}
</ul>
同时还需要为循环的每一项设置 key。
转轴
父子组件
React 和 Vue 都是单向数据流,这样有利于对数据的管理。好比是小公司人数较少,有啥事只要喊一声就行了。但是大公司发布命令,靠喊是解决不了问题的,最有效的方法就是一层一层单向的传达命令。
父组件
// content 是传递的数据
// funItem 是传递的方法
<ChildrenItem content={item} funItem={this.funItem.bind(this)} />
子组件
import React, { Component } from 'react'; //imrc(vscode 插件快速生成代码片段命令)
class ChildrenItem extends Component { //cc
constructor(props){
super(props)
// 最好是在 constructor 中绑定 this,这样有利于性能的优化
this.handleClick=this.handleClick.bind(this)
}
render() {
return (
<div onClick={this.handleClick}>{this.props.content}</div>
);
}
handleClick(){
// 切记别在子组件中修改父组件传来的数据
this.props.funItem()
}
}
export default ChildrenItem;
类型检查
import React, { Component } from 'react';
import PropTypes from 'prop-types'
class ChildrenItem extends Component { //cc
constructor(props){
...
}
render() {
...
}
}
// 对传入的值进行类型检查
ChildrenItem.propTypes={
content:PropTypes.string.isRequired,// 加了 isRequired 就是必须得传
funItem:PropTypes.func,
name:PropTypes.string
}
// 设置默认值
ChildrenItem.defaultProps = {
name:'张三'
}
export default ChildrenItem;
生命周期
生命周期函数指在某一个时刻组件会自动调用执行的函数。React 有四大生命周期,分别是:
- Initialization:初始化阶段
- Mounting: 挂载阶段
- Updation: 更新阶段
- Unmounting: 销毁阶段
constructor不算生命周期函数,是构造函数,它是ES6的基本语法。但是你要把它当成一个生命周期函数,可以看成 React 的 Initialization 阶段,定义属性(props)和状态(state)。
我们日常的开发任务中,主要是操作 Mounting 阶段的生命周期函数。伴随着整个虚拟DOM的生成,先后执行:
- componentWillMount: 在组件即将被挂载到页面的时刻执行
- render: 页面 state 或 props 发生变化时执行
- componentDidMount: 组件挂载完成时被执行
componentWillMount
和componentDidMount
这两个生命周期函数,只在页面刷新时执行一次,而render
函数是只要有state
和props
变化就会执行
数据请求
- 安装 axios
npm install -S axios
- Fast Mock
// 快速生成测试接口数据
https://www.fastmock.site/
- 使用
// 建议在componentDidMount 函数里执行,因为在 render 里执行,会出现很多问题,比如一直循环渲染;在componentWillMount 里执行,在使用 RN 时,又会有冲突。
componentDidMount(){
axios.post('https://www.fastmock.site/mock/你的api')
.then((res)=>{console.log('axios 获取数据成功:'+JSON.stringify(res)) })
.catch((error)=>{console.log('axios 获取数据失败'+error)})
}
-save(-S)的意思是将模块安装到项目目录下,并在 package 文件的 dependencies 节点写入依赖,项目生产环境需要用到。-save-dev(-D)的意思是将模块安装到项目目录下,并在 package 文件的 devDependencies 节点写入依赖,只需要在开发环境用到。
破晓
ToDoList 案例
class ToDoList extends React.Component {
constructor(props) {
super(props);
// 实现双向数据绑定第一步 在 state 中声明
this.state = {
value: '',
list: [
{ title: '吃饭', checked: false },
{ title: '睡觉', checked: false },
{ title: '打豆豆', checked: false },
],
};
}
handleChange(e) {
this.setState({
value: e.target.value,
});
}
addData() {
let title = this.state.value;
// 一定要注意,不能直接修改 state 的数据,虽然可以实现,但是 react 中特别强调了!如果这样做在后期的性能优化上会有很多麻烦
// 所以修改 state 中的数据的时候,我们会定义一个局部变量,用来改变数据
// 用 ... 剩余参数的方式给数组添加数据 更方便 而且更直接
let tempList = [
...this.state.list,
{
title: title,
checked: false,
},
];
// 注意 this.setState 是异步的方法
this.setState({
list: tempList,
value: '',
});
}
checkBoxState(key) {
let tempList = this.state.list;
tempList[key].checked = !tempList[key].checked;
this.setState({
list: tempList,
});
}
removeItem(key) {
let tempList = this.state.list;
tempList.splice(key, 1);
this.setState({
list: tempList,
});
}
// render 函数中 写 JSX
// class -> className for -> htmlFor 因为 class 和 for 都是 js 中的关键字
// 但是 React 中 map 循环代替了 for 循环
// map 中一定要有 return 并且要注意 return 的使用 它只能返回一行 多行需要加括号
render() {
return (
// Fragment 是 react 提供给我们使用的 一个根节点 不会渲染
<Fragment>
{/* 在 jsx 中写注释,推荐使用这种方式 */}
<header className="title">
TodoList:
<input
value={this.state.value}
onChange={this.handleChange.bind(this)}
/>{' '}
{' '}
{/* {this.state.value} */}
<button onClick={this.addData.bind(this)}>添加</button>
</header>
<h2>待办事件</h2>
<ul>
{this.state.list.map((item, key) => {
return (
// 在 react 中 key = key+item 这是比较合理的
// true && expression 始终计算为 expression,并且 false && expression 始终计算为 false
// dangerouslySetInnerHTML={{ __html: item.title }} 可以使我们直接在文本框中输入 html 标签
// 当然我们是推荐这种用法的 因为 JSX 可以防止注入攻击(cro)解析器会把代码转换成字符串
!item.checked && (
<li key={key + item}>
<input
type="checkbox"
checked={item.checked}
onChange={this.checkBoxState.bind(this, key)}
/>
{item.title}---{' '}
<button onClick={this.removeItem.bind(this, key)}>
删除
</button>
</li>
)
);
})}
</ul>
<h2>已完成事件</h2>
<ul>
{this.state.list.map((item, key) => {
return (
item.checked && (
<li key={key + item}>
<input
type="checkbox"
checked={item.checked}
onChange={this.checkBoxState.bind(this, key)}
/>
{item.title}---{' '}
<button onClick={this.removeItem.bind(this, key)}>
删除
</button>
</li>
)
);
})}
</ul>
</Fragment>
);
}
}
写在最后
到此,我相信你或许对 react 有了初步的了解。当然,文章可能出现错误,请不吝赐教。第一次在掘金上面发表文章,属实不容易。计划好的事情,踏出了第一步才会有接下来的一个个脚印。就像是种一个棵数最好的时间是10年前,其次实现!愿你如一位骑士,目光如炬,一往无前!