React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。(官方明确说react不是框架,被人称为框架,可能是他真的足够强大吧。。)
点击进入 React中文官方文档
做页面的UI交互。
方法一:在HTML的script标签引入React,好处是引入后能马上开始写react代码,不需要做其他引入了。适合新手。但是缺点是不适合大型 项目。所以不推荐。
方法二:用react的脚手架: create-react-app
分两种安装方法:
npm install -g create-react-app
//安装react脚手架
create-react-app 文件名
//创建react文件
cd 文件名
//进入react文件
npm start
//启动项目
官方文档推荐方法:npx create-react-app 文件名
cd 文件名
npm start
逐个说明:
打包项目 npm run build
把开发环境中写好的代码,转成生产环境,生成一个build文件夹。把这个文件夹的内容,放到服务器根目录,网站就可以让别人看到了。
还记得存放我们自己写的代码的文件夹吗? 没错,就是src文件夹。
进入该文件夹。找到默认渲染的index.js
文件,用编辑器打开。
把里面的ReactDOM.render(...)
的这一段代码,改写成下面这样:
ReactDOM.render(
Hello World!
,
document.getElementById('root')
);
然后在创建的react文件夹里,打开cmd控制台,输入 npm start
就会自动进入到http://localhost:3000/
,页面中渲染出了这样的内容:
第一段react代码,就写成了!!!
什么是JSX???
再回头看一下上面的代码,Hello World!
,
是不是很奇怪?JS里面怎么有HTML标签?!
没错,JS里面可以写HTML标签,这种形式,就是JSX了。
JSX既不是字符串,也不是HTML。它就是一种JS里面写HTML标签的语法。
这里还可以这样赋值。const element =
Hello World!
在JSX里,还可以用{ } ,声明变量或者引入表达式。{ }里可以放任何JS表达式。
比如
,输出结果就是hello, 张{1 + 2}
hello, 张3
还可以这样,const name = '张三'
hello {name}
输出结果就是hello 张三
注意:JSX里的class类属性,要写成className。
看完这些,肯定还是对JSX一脸懵逼的。
但是不用着急,后续还会用到大量的JSX,用多了,肯定会爱不释手的!
元素,是构成React应用的最小单位。
元素,描述了你在屏幕上想看到的内容
比如 const element =
这就是要给元素,Hello, World!
Hello, World!
就是通过ReactDOM在页面上要渲染出来的内容了。
想要渲染出元素内容,只需要把元素放入ReactDOM.render()
里即可。
ReactDOM.render(
Hello World!
, //这里就是要渲染的元素
//注意,这里的逗号,别忘了!!!
document.getElementById('root')
);
元素,是不可变的普通对象。它代表了某个特定时刻的UI
组件,类似于JS函数。但是组件的函数名首字母要大写。(小写就是函数,大写就是组件)
组件,是一块独立的代码。需要时,直接调用渲染即可。
组件,有两种标准写法:
第一种,函数形式的,例如:
function Welcome() {
return (
Hello world!
)
}
ReactDOM.render(
,
document.getElementById('root')
)
第二种,类形式的,例如:
class Welcome extends React.Component {
render() {
return (
Hello World!
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
以上两种写法,是等效的。
两个例子,是两种标准格式。也就是说,不管你要用哪个形式,要渲染什么内容,这个写法都不会变的。
注意,组件的 函数名和类名 的首字母要大写。
组件里可以携带参数,但是组件的参数是唯一的,即props
在组件里用参数,就用props。 别无选择。
先说函数组件的设置参数和传递参数,举个例子
function Welcome(props) {
return Hello, {props.name}
}
const name = '迪丽热巴!'
ReactDOM.render(
,
document.getElementById('root')
)
输出结果: Hello, 迪丽热巴!
这个name 通过组件的name={name}
赋值, 传给了props参数。
这里,我们打印一下props,看下props里面有什么。
function Welcome(props) {
console.log(props)
return Hello, {props.name}
}
const name = '迪丽热巴!'
打印结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UfxWVU7C-1595528626313)(C:\Users\Shinelon\AppData\Roaming\Typora\typora-user-images\image-20200721103637339.png)]
结果显示,props是一个对象,对象里有属性name。就是下面这样:
props = {
name: '迪丽热巴'
}
也就是说,通过
语句, name的值以对象的形式传给了props。
类组件的参数及传递:
先看个例子:****
class Welcome extends React.Component {
render() {
console.log(this.props)
return Hello, {this.props.name}
}
}
const name = '迪丽热巴'
ReactDOM.render(
,
document.getElementById('root')
)
输出结果:
Hello, 迪丽热巴
这里的this.props = {
name: '迪丽热巴'
}
通过例子可以看出,两种组件都有同样的传递参数的形式。
值得注意的是:在调用props参数的时候,函数组件是props.name
,而类组件是this.props.name
。类组件是有this的。
而且,props是只读的,不可以被修改的。所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
顾名思义,就是可以把多个组件组合在一起,然后渲染出结果。
直白点说,就是把多个组件,放在一个顶层组件里,然后对顶层组件进行渲染。
举个例子:
function Welcome(props) {
return Hello, {props.name}
}
function Name() { //顶层组件
return (
//注意,这些组件都必须包含在一个顶层标签里,不然会报错
//组件1
//组件2
//组件3
//组件4
)
}
ReactDOM.render(
,
document.getElementById('root')
)
输出结果:
Hello, 迪丽热巴!
Hello, 古力娜扎!
Hello, 巴拉巴拉!
Hello, 奥利给啊!
本篇开头提到了React的声明特性,就是为状态写UI。这个状态,就是state。
比如,人在站立的时候是一种站立状态,人在蹲下的时候,就是一种蹲下的状态。
我现在看时钟是1点钟是一种状态,一个小时后,看时钟是2点钟又是一种状态。
状态,就是处于某一点或某一时刻的数据。
举个例子:
function Clock(props) {
return (
现在的时间是: {props.date.toLocaleString()}
)
}
ReactDOM.render(
,
document.getElementById('root')
)
输出结果:
现在的时间是: 2020/7/21 下午2:10:53
如果刷新页面,这个时间会随时变化。这个变化,也就是状态的变化。
如果要在代码中显示出state,就需要用到类组件了。
注意:construtor里面是唯一可以初始化状态的地方。
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = { //这里是设置状态,给状态做初始化
date: new Date()
}
}
render() {
return (
现在的时间是: {this.state.date.toLocaleString()}
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
输出结果也是这个状态的时间,刷新页面,时间就会变化,也就是状态也在变化。
元素从一个状态,到另外一个状态,要怎么调整呢?
状态,是不能直接修改的。
但是,状态可以通过setState()来设置为新的状态。
setState()要放在函数中。
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = {
date: new Date().toLocaleString()
}
}
change = () => { //这里用来改变时间状态
this.setState({
date: new Date().toLocaleString()
})
}
render() {
return (
现在的时间是: {this.state.date}
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
现在,状态已经改变了。但是页面还没有自动跟着改变状态。
那么,怎么让状态自动从一个状态到另一个状态呢?
假如我们不需要刷新页面,就可以从一个状态调整到另一个状态
那就要用到定时器了。但是我们又希望 在用定时器的时候,能让定时器只改变时间状态,
而不是让整个组件都重新加载。
那么 我们就要用到生命周期了。
我们把13条中的代码修改一下:
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = {
date: new Date().toLocaleString()
}
}
componentDidMount() { //挂载计时器
this.timer = setInterval(this.change, 1000)
}
componentWillUnmount() { //卸载计时器
clearInterval(this.timer)
}
change = () => { //设置状态
this.setState({
date: new Date().toLocaleString()
})
}
render() {
return (
现在的时间是: {this.state.date}
//被渲染的元素
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
挂载当前状态 → 卸载改变后的状态 ,这就是一个生命周期。
React元素的事件处理和DOM元素的很相似,但是语法上有点不同:
例如,传统的HTML:
在React中略微不同:
阻止默认行为,也有所不同。
传统的HTML阻止链接默认打开一个新页面:
Click me
在React可能是这样的:
handleClick = (e) => {
e.preventDefault()
console.log('The link was clicked.')
}
事件处理案例:点击按钮 在ON和OFF之间来回切换
class ToggleBtn extends React.Component {
constructor(props) {
super(props)
this.state = {
isChanging: true
}
}
handleClick = () => { // 这里如果不用箭头函数,就在constructor里绑定this
this.setState({
isChanging: !this.state.isChanging //修改状态
})
}
render() {
return (
)
}
}
ReactDOM.render(
, //渲染这个组件
document.getElementById('root')
)
在React中,你可以创建不同的组件来封装各种你需要的行为。然后,依据应用的不同状态,你可以只渲染对应状态下的部分内容。
使用条件语句 if ,条件运算符(也叫三元运算符), &&, || ,!等。
举例,根据不同条件,来让组件1或者组件2来运行:
function Com1() { // 组件1
return (
这是组件1在执行--
)
}
function Com2() { // 组件2
return (
这是组件2在执行--
)
}
function Run(props) {
const isRunning = props.isRunning // 定义变量
if (isRunning) { //判断条件
return
}
return
}
ReactDOM.render(
,
document.getElementById('root')
)
这里说的列表,是根据数组元素,生成相应的列表标签。
用map()
函数来完成
使用{}在JSX里构建一个元素集合
举例:
const numbers = [1, 2, 3, 4, 5] //给出一个数组
const listItems = numbers.map( //用map()函数对数组内每个元素进行遍历
(number) => {number} //把遍历的元素放入li标签
)
ReactDOM.render(
{listItems}
, //渲染时 把li标签放入ul标签
document.getElementById('root')
)
输出结果:
在运行这段代码时,会看到一串警告:
意思是,在列表里的每一个子元素都必须有一个key参数。
怎么给子元素设置key参数呢?如下:
const listItems = numbers.map((number) =>
//在这里给子元素设置key参数
{number}
)
key是用来帮助react识别是哪些元素改变了,比如被添加了或者被删除了。
key是这个元素在列表中拥有独一无二的字符串。
应该在哪里设置key值呢?
记住:在 map()
方法中的元素需要设置 key 属性
key会传递信息给react,但是不会传递给你写的组件。
表单有输入和提交的状态,状态保存在state属性中,state成为唯一数据源。
渲染表单的React组件还控制着用户输入过程中表单发生的操作。被React以这种方式控制取值的表单输入元素就叫做“受控组件”
案例:提交输入的内容,并弹窗提示提交了什么内容
class InputCom extends React.Component {
constructor(props) {
super(props)
this.state = {
value: ''
}
}
handleChange=(e)=> {
this.setState({
value: e.target.value //输入的内容
})
}
handleSubmit=(e)=> {
alert('提交的内容是: ' + this.state.value)
// e.preventDefault() //阻止提交刷新页面
}
render() {
return (
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
试运行结果:
input, textarea, select三者非常类似。
值得一提的是,select中的selected属性。 在React中 并不会使用selected属性,而是在根select标签上使用value属性。
既然有受控组件,那就一定有对应的非受控组件。
这里先挖个坑,后续写项目用到了,回来补。
通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。这就叫“状态提升”
个人觉得官方文档中给的案例看着有点费劲儿,就自己写了一个长度间的转换案例,如下:
let dataTitle = {
m: '输入米数: ',
cm: '输入厘米数: '
}
//抽离出来的输入框组件
class LengthData extends React.Component {
render() {
let title = this.props.title
let input = this.props.input
let onChange = this.props.onChange
let data = this.props.data
return (
)
}
}
//编写实现输入框的组件
class Length extends React.Component {
constructor(props) {
super(props)
//状态提升
this.state = {
type: '',
input: ''
}
}
checkedInput(input) {
// console.log(typeof(input))
if (Number.isNaN(input) || parseFloat(input) < 0) {
alert ('输入数据有误,重新输入!')
this.setState({
type: '',
input: ''
})
}
return input
}
convertCM(input) {
return input * 100
}
convertM(input) {
return input / 100
}
handleChange=(type, e) => {
if (type === 'meter') {
this.setState({
type: 'meter',
input: e.target.value
})
}else if (type === 'centimeter') {
this.setState({
type: 'centimeter',
input: e.target.value
})
}
}
render() {
//meter centimeter type data 这些都是为实现交互而设置的变量
let input = this.state.input
let type = this.state.type
let meter = type === 'centimeter' ? this.convertM(this.checkedInput(input)) : input
let centimeter = type === 'meter' ? this.convertCM(this.checkedInput(input)) : input
return (
长度单位转换
{this.handleChange('meter' ,e)}} />
{this.handleChange('centimeter' ,e)}} />
)
}
}
ReactDOM.render(
,
document.getElementById('root')
)
**第一步:**编写实现输入框的组件(这一步在代码案例中看着不是很明显了)
**第二步:**抽离输入框组件(方便写入多个输入框)
**第三步:**状态提升(把子组件中的状态拿走,状态都放在父组件中)
**第四步:**状态交互 (核心代码,也是最难理解的一段代码)
**第五步:**调试bug(输入和输出结果的调试,比如输入不符合要求,给错误提示,然后清空输入框,阻止交互)
官方文档说明:巧用组合,慎用继承
组合在实际敲代码的过程中,非常容易用到,且我们在学习的过程中,已经用过多次。
举例说明:
function App1() {
return (
Hello, React!
)
}
function App2() {
return (
你好,react!
)
}
function App3() {
return (
翻译如下:
)
}
ReactDOM.render(
,
document.getElementById('root')
)
// 该案例就是把通过App3 把组件App1和App2组合在一起,通过render渲染出来。
用react思想构建快速响应的大型web应用程序。
写网页时,最好先写静态页面,再写交互UI。两者分开写。
因为静态页面代码多,细节少。 交互UI是代码少,细节多。(参照第21条给出的案例)
React是基于state的改变来实现UI交互的。所以,用好state。
习惯写组件,避免重复代码。
用props和state不止可以从父组件到子组件传输数据,还可以实现从子组件到父组件的数据传输。
看来了这里,恭喜你,你已经是React的基础用户了!
赶紧写个项目练练手吧!
后续我会把React的进阶知识整理出来,发到博客。
-----------------------------End-------------------------------