准备
前端知识基础:html,css,js
前端工程基础:nodejs,npm,webpack
开发环境准备:vs code及相关插件
快速开始
第一个应用:Hello,World!
# create-react-app是react脚手架
npm install -g create-react-app
# 用脚手架创建一个react-hello项目
create-react-app react-hello
# 删除默认的实现
cd react-hello
rm src/*
# 新建一个index.js入口文件
touch src/index.js
create-react-app会创建一个默认的web应用,可以直接运行查看效果。但为了简单起见,我们删掉它们,实现自己最简单的HelloWorld功能。
React主要编程思想是:组件化和声明式编程。在index.js定义一个HelloWorld组件并渲染到DOM里。
import React from 'react';
import ReactDOM from 'react-dom';
function HelloWorld() {
return (<div>Hello,World!</div>);
}
ReactDOM.render(
<HelloWorld />, document.querySelector('#root')
);
然后启动项目:
npm start
How it works?
一个HelloWorld搞的这么复杂,到底是怎么回事呢?
已知我们有一个public/index.html,里面超简单,定义了一个id为root的div元素:
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
从页面输出结果来看,应该是在这个div下又动态创建了一个div。最终内容大概如下:
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">
<div>Hello,World!</div>
</div>
</body>
打开浏览器开发者工具,查看index.html内容,确实多了点东西:
<script src="/static/js/bundle.js"></script><script src="/static/js/0.chunk.js"></script><script src="/static/js/main.chunk.js"></script>
这些是webpack帮我们完成的。这里面的js肯定某个地方有操作DOM创建新的div的代码。水平有限,暂时不深入。
一切都回到了render函数:
ReactDOM.render([React Element], [DOM element]);
React使用虚拟DOM的概念来与原生DOM交互,底层原理先忽略。总之React内部有一个虚拟DOM表示实际的DOM树,通过render函数调用,React会根据传入的组件元素创建实际对应的DOM元素,并把它插入到element中,这个element是实际的DOM元素。
所以index.js的功能是:定义一个名为HelloWorld的React元素,并把它插入到id为root的DOM元素内:
ReactDOM.render(
<HelloWorld />, document.querySelector('#root')
);
React使用组件的概念来封装我们自定义的HTML元素,通常由一个方法(或ES6中的class)定义一个组件,方法返回的是一个React元素,它是可以渲染到实际DOM中的。
创建一个React元素的API为:
React.createElement(
string|element,
[propsObject],
[children...]
)
其中element可以是某个DOM标签名,也可以是某个React元素。可选参数 [propsObject] 表示该元素的属性列表,可选参数 [children…] 表示该元素的子元素列表。
所以,下面两个方法是等效的:
//使用React API
function HelloWorld() {
return React.createElement('div',{},'Hello,World!');
}
//使用JSX
function HelloWorld() {
return (<div>Hello,World!</div>);
}
JSX简介
上面已经出现了奇怪的代码:js代码中嵌入了html代码,这种代码叫JSX。
正是这种JSX语法使得我们可以用熟悉的html语法来创建元素,免得手动调用React.createElement方法。这也是React推崇的声明式编程优于命令式编程的体现。
JSX语法通过Babel编译成实际的JavaScript。Babel是一个编译器,它可以把各种新的语法特性转换成ES5标准的JavaScript,以满足浏览器兼容性。create-react-app脚手架集成了Babel。
经过前面对React组件的认识,可以肯定,每个JSX元素定义都会转换成一个React.creatElement函数调用。
接下来要做的就是多翻翻文档,熟悉JSX的语法规则了。这里搞几个简单的示例吧。
元素名首字母大写
为了和原生html标签区分,自定义元素名首字母必须大写。并且,所有标签都必须闭合,包括html中不需要闭合的标签,如br标签。
// DO THIS:
return <br />;
return <input type='password' ... />;
return <li>text</li>;
// NOT THIS:
return <br>;
return <input type='password' ...>;
return <li>text;
组件嵌套
function Hello() {
return <span>Hello</span>;
}
function World() {
return <span>World</span>;
}
//HelloWorld组件内嵌套了Hello组件和World组件
function HelloWorld() {
return (
<div>
<Hello/> <World/>!
</div>
);
}
使用括号与不使用括号
JSX规定,return后面不能直接换行。下面写法会报错:Expected an assignment or function call and instead saw an expression no-unused-expressions.
function HelloWorld() {
return
<div>
<Hello/> <World/>!
</div>;
}
实际上是因为JavaScript会在return后面加上分号,就成了:
function HelloWorld() {
return;
<div>
<Hello/> <World/>!
</div>;
}
解决这个问题要么return后面不换行,这样代码格式化稍微有点难看:
function HelloWorld() {
return <div>
<Hello/> <World/>!
</div>;
}
要么使用括号:
function HelloWorld() {
return (
<div>
<Hello/> <World/>!
</div>
);
}
JSX只能返回一个元素
当然,因为最终是调用React.createElement方法嘛。如果没有一个封闭元素,会报错,例如:
function HelloWorld() {
return (
<Hello /><World />
);
}
报错内容如下:
Parsing error: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>…</>?
已经给我们提示了,要么使用一个wrapper标签,如div;要么使用fragment。
当我们确实要用这些并列的元素表示代码复用的时候,可以使用fragment。例如多个td标签。
function NameCells() {
return (
<React.Fragment>
<td>First Name</td>
<td>Last Name</td>
</React.Fragment>
);
}
//or
function NameCells() {
return (
<>
<td>First Name</td>
<td>Last Name</td>
</>
);
}
使用js表达式
JSX里可以使用js表达式,例如:
function SubmitButton() {
const buttonLabel = "Submit";
return (
<button>{buttonLabel}</button>
);
}
大括号里面必须是表达式,不能是普通的语句。表达式有返回值,可以出现在赋值操作符(=)右边。
条件判断
条件判断第一种写法是三目条件运算符:
function ValidIndicator() {
const isValid = true;
return (
<span>{isValid ? 'valid' : 'not valid'}</span>
);
}
另一种是使用&操作符:
function ValidIndicator() {
const isValid = true;
return (
<span>
{isValid && 'valid'}
{!isValid && 'not valid'}
</span>
);
}