# 2.1 搭建React环境
安装nodejs+vscode,用nvm去管理node版本
# 2.1.1 create-react-app
文档地址:create-react-app (opens new window)
创建一个项目:npx create-react-app my-app
建一个ts项目:npx create-react-app my-app --typescript
终端输入:yarn eject
(慎用,会把潜藏的react-script弹射到应用层,此操作不可逆)
文档目录
my-app/
README.md
node_modules/
package.json
public/
index.html # 入口
favicon.ico
robots.txt # 搜索引擎爬取配置信息
manifest.json # 配置页面需要的meta信息
src/ # 项目主目录
App.css
App.js
App.test.js
index.css
index.js
logo.svg
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2.1.2 开始前的配置
# 安装第三方插件
在vscode商店里下载
simple react 快速生成react模版,可以看插件具体文档
编辑器中输入
cc
生成类组件,sfc
生成函数示组件prettier 格式化代码
module.exports = {
// 一行最多 100 字符
printWidth: 100,
// 使用 2 个空格缩进
tabWidth: 2,
// 不使用缩进符,而使用空格
useTabs: false,
// 行尾需不要有分号
semi: false,
// 使用单引号
singleQuote: true,
// 对象的 key 仅在必要时用引号
quoteProps: 'as-needed',
// jsx 不使用单引号,而使用双引号
jsxSingleQuote: true,
// 末尾不需要逗号
trailingComma: 'none',
// 大括号内的首尾需要空格
bracketSpacing: true,
// jsx 标签的反尖括号需要换行
jsxBracketSameLine: false,
// 箭头函数,只有一个参数的时候,也需要括号
arrowParens: 'always',
// 每个文件格式化的范围是文件的全部内容
rangeStart: 0,
rangeEnd: Infinity,
// 不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准
proseWrap: 'preserve',
// 根据显示样式决定 html 要不要折行
htmlWhitespaceSensitivity: 'css',
// 换行符使用 lf
// endOfLine: 'lf',
// 在对象,数组括号与文字之间加空格 "{ foo: bar }"
bracketSpacing: true
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
- 配置vscode编辑配置文件
可以配置针对该项目的配置文件,在根目录创建.vscode/settings.json
settings.json配置信息
{
"eslint.autoFixOnSave": true, //eslint保存格式化
"prettier.eslintIntegration": true, // 让prettier遵循eslint格式美化
"eslint.enable": true, //是否开启vscode的eslint
"files.eol": "\n",
"editor.tabSize": 2,
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.validate": ["javascript", "javascriptreact", "html", "typescript", "typescriptreact"], //确定校验准则
"files.associations": {
"*.jsx": "javascriptreact"
},
"typescript.tsdk": "node_modules/typescript/lib",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 2.1.3 线上项目编辑器
codesandbox (opens new window)
# 2.2 组件和JSX
# 2.2.1 编写react元素
react 元素就是一个javascript对象
const element=<h1>hello react</h1>
console.log(element);
// render方法将react元素渲染到页面上
ReactDOM.render(element, document.getElementById('root'));
// 打印结果
{
$$typeof: Symbol(react.element)
key: null
props:{children: "hello react"}
ref: null
type: "h1"
}
2
3
4
5
6
7
8
9
10
11
12
13
# 2.2.2 jsx
jsx是javascript的语法扩展,使用xml标记的方式直接声明界面
# jsx是什么
- 不是模版引擎语言
模版引擎语言Angular和vue中template的语法,js模版的作用就是输入模版的字符串+数据,经过渲染得到渲染过的字符串;jsx不是这样的模版引擎,它是带语法糖的ATX,其实是抽象的语法树,语法糖放到了构建阶段,所以运行的时候不需要解析。
声明示方式创建UI,处理UI逻辑
遵循javascript语法,无学习门槛
# react通过babel将jsx转换浏览器识别的语言
const ele=<div className="root">
<p>hello</p>
</div>
// 转换后
var ele = /*#__PURE__*/React.createElement("div", {
className: "root"
}, /*#__PURE__*/React.createElement("p", null, "hello"));
// 通过createElement创建元素
2
3
4
5
6
7
8
9
react通过层层嵌套的方法,把我们输入的语句转换成浏览器识别的代码,这就是jsx
背后的原理
# jsx 规则
- 在jsx中潜入表达式,用
{}
包裹 - 大写开头作为定义组件,小写tag作为原生的dom节点
- jsx标签可以有特定属性和子元素
- jsx只能有一个根元素
# jsx 实践
class main extends Components {
constructor(props){
super(props)
this.state={
name:"zs",
age:12
}
}
addage(){
return this.state.age+12
}
render(){
const flag=true
const list = [1, 2, 3]
return (
// jsx需要一个根元素
<div className="main">
<p>{this.state.name}</p>
<p>{this.state.age>18?'成年':'未成年'}</p>
<p>{this.addage.call(this)}</p>
{/* 三元表达式判断显示元素 */}
{
flag?
<p>元素1</p>
:
<p>元素1</p>
}
{/* 只能用map循环 */}
{list.map((item) => {
return <div key='item'>{item}</div>
})}
</div>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
jsx需要一个根元素包裹,因为jsx是通过babel进行转译,其实就是通过React.createElement()
,它的第一个参数需要一个元素,如果出现2个就无法识别了
# Fragments
用Fragments替换根元素,而且此标签不渲染到页面中
class main extends Components {
render(){
return (
<React.Fragment>
<p>{this.state.name}</p>
<p>{this.state.age>18?'成年':'未成年'}</p>
<p>{this.addage()}</p>
</React.Fragment>
)
}
}
// 或者用react提供的简洁方法
class main extends Components {
render(){
return (
<>
<p>{this.state.name}</p>
<p>{this.state.age>18?'成年':'未成年'}</p>
<p>{this.addage()}</p>
</>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
为什么使用Fragments
- 可以包含并列的子元素
- 编写表格组件,包裹子元素让html生效
# 2.2.3 拓展学习资料
Babel和ATS 抽象语法树1 (opens new window)
Babel和ATS 抽象语法树2 (opens new window)
# 2.3 props、列表渲染、条件渲染
什么是props?
当react元素作为自定义组件,将jsx所接受的属性转换成单个对象传递给组件,这个对象被称为“props”(就是父组件传递给子组件的对象)
- props是组件的固有属性
- 不可在组件内部对props进行修改
- 更新props:需要通过父组件重新传入新的props,更新子组件(单向数据流)
# 2.3.1 示例
const listData={name:'react'}
// 父组件
funtion App (){
return (
<div>
{/* 传递数据listData*/}
<ListItem data={listData}></ListItem>
<div>
)
}
2
3
4
5
6
7
8
9
10
// 子组件
class ListItem extends Component {
constructor(props){
super(props) //子类中调用父类构造函数
}
render() {
return (
<div>
<div>
{/* 通过props拿到值 */}
{this.props.data.name}
<div>
<div>
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 函数示组件
- 函数组件也叫无状态组件
- 组件内部没有this
- 没有声明周期
改写如下
// 子组件
funtion ListItem (props){
return (
<div>
<div>
{/* 通过props拿到值 */}
{props.data.name}
<div>
<div>
)
}
2
3
4
5
6
7
8
9
10
11
当前组件如果是纯展示组件,可以用函数组件,函数组件是一个纯函数,用函数组件可以得到性能的提升
# 2.3.2 列表渲染
const listData=[{name:'react',id:1},{name:'vue',id:2}]
// 父组件
funtion App (){
return (
<div>
{/* 传递数据listData*/}
{
listData.map(item=>{
return <ListItem data={item} key={item.id}/>
})
}
<div>
)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 2.3.3 条件渲染
条件渲染的主要方法
- 使用三目运算符
- 使用函数做条件判断
- 使用与运算符 && 判断
class ListItem extends Component {
constructor(props){
super(props) //子类中调用父类构造函数
}
renderList(){
if(this.props.data.name==='react'){
return <div>jsx</div>
}else {
return <div>template</div>
}
}s
render(){
return (
<div className='listItem'>
{/* 使用三目运算符*/}
<div className={`thend-grid`${this.props.data.name==='react'?'-blue':'-green'}}>
{this.props.data.name==='react'?'jsx':'template'}
<div>
{/*使用函数做条件判断*/}
{this.renderList.call(this)}
{/*使用与运算符 && 判断*/}
{this.props.data.name==='react' && <div>jsx</div>}
<div>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 2.3.4 拓展学习资料
# 2.4 CSS in React
# 2.4.1 行内样式
<div style={{fontSize:18;color:red}}></div>
# 2.4.2 引入css样式表
src/
components/
ListItem/
index.jsx
index.css //也可以用scss,文件命名index.css
// 我们在index.css 定义样式
.title{
color:red;
}
//在ListItem导入
import './index.css'
class ListItem extends Component {
constructor(props){
super(props) //子类中调用父类构造函数
}
render(){
return (
<div className='listItem'>
<span className='title'>header<span>
<div>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
上面的在index.css写法,里面的样式是全局样式,会造成全局污染,可以用css module解决
# css module
- 不使用选择器,使用class名定义样式
- 不层叠class,使用一个class定义样式
- 用过compose来组合
src/
components/
ListItem/
index.jsx
index.module.css //命名方式加入module
// 我们在index.css 定义样式
.title{
color:red;
}
//在ListItem导入对象
import style from 'index.module.css'
class ListItem extends Component {
constructor(props){
super(props) //子类中调用父类构造函数
}
render(){
return (
<div className='listItem'>
<span className={style.title}>header<span>
<div>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 2.4.3 css管理进阶
css管理工具
- Styled-component
- Classnames
src/
components/
ListItem/
index.jsx
index.module.css //命名方式加入module
// 我们在index.css 定义样式
.title{
color:red;
}
.themd {
background:'red'
}
//在ListItem导入对象
import style from 'index.module.css'
import classnames from 'classnames/bind'
const cls=classnames.bind(style)
import cn from 'classnames'
class ListItem extends Component {
constructor(props){
super(props) //子类中调用父类构造函数
}
render(){
const flag=true
const _cn=cn({
'themd':flag
})
return (
<div className='listItem'>
{/* css Module+classnames/bind */}
{/* css module 结合 classnames 可以添加2个类名 */}
<span className={cls('title','themd')}>header<span>
{{/* classnames */}
<span className={_cn}></span>
<div>
)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40