- 概念
- 创建webpack项目
- 简单框架
- 添加css样式
- event
- btn_click
- 修改
[this.state](#%E4%BF%AE%E6%94%B9thisstate)
- input的onChange事件
- 组件的生命周期
- 引入图片
- other mind
- ajax_get_post
概念
虚拟DOM
- 由程序员写的、用来模拟页面DOM元素的 东西。
- 用js对象来模拟DOM元素以及DOM之间的嵌套关系
<div id="Adiv" title="today">
今天星期二
<p>学习学习</p>
</div>
用js模拟(以对象的形式)如下:
var div = {
tagName: 'div',
attrs: {
id: 'Adiv',
title: 'today'
},
childrens: [
'今天星期二',
{
tagName: 'p',
attrs: {},
childrens: [
'学习学习'
]
}
]
}
使用babel
语法表示:
var div = <div className='mydiv'>今天2020年2月14日14</div>
Diff 算法
- tree diff:新旧两颗DOM树的对比,逐层比对
- component diff:每层的组件对比
- element diff:元素级别的对比
webpack
create-react-app脚手架自带有webpack,可在文档查看打包方法,且更方便。
- 在项目文件夹下运行
npm init -y
,快速初始化项目 - 在项目根目录创建
src
源代码目录和dist
产品目录 - 在 src 目录下创建
index.html
- 使用 cnpm 安装 webpack, 运行
cnpm i webpack -D
- 全局运行
npm i cnpm -g
- 全局运行
- 默认:
- 打包入口:
src
->index.js
- 打包的输出文件:
dist
->main.js
- 打包入口:
- 在项目根目录下创建
webpack.config.js
- 设置
mode
选项,可选值为:development
和production
- 设置
简单框架
- 导入 react包
- 用
import React from 'react'
来 创建 组件、虚拟DOM元素、生命周期 - 用
import ReactDOM from 'react-dom'
来把创建好的 组件、虚拟DOM 渲染到页面上
- 用
- 创建虚拟DOM元素
- param1: 创建的元素的类型,字符串,表示元素名称
- param2: 一个对象或null,表示这个DOM元素的属性
- param3: 子节点,包括其子虚拟DOM或文本子节点
const aTag = React.createElement(
'h1', // param1
{ id: 'aTag', title: 'Ah1'}, // param2
'the inner text' // param3
);
- 使用 Babel 简化创建方式
- 渲染虚拟DOM到页面上
- param1: 要渲染的虚拟DOM元素
- param2: 页面上的一个容器
ReactDOM.render(aTag, document.getElementById('root'));
Babel
- Babel 解析
JSX
语法代码转为纯JS语法代码
- JSX —- Javascript XML
- 创建DOM
const aDivTag = <div id='aDivTag'>react learn</div>
- JSX语法:
- 使用占位符
{}
来渲染变量、表达式、字符串、数组、标签…到页面或标签中
let tmp = 10;
let title = 'newTitle'
let divTag = <div title={title}>{tmp}</div>
- 添加类名,使用
className
来代替class
- JSX注释,用
{/* zhushi */}
- 渲染数组,
- 用
map
循环,返回一个对item处理了的值,每一个返回的元素需要有一个唯一的key值
- 用
const result = list.map(item => return <tag xxx>xxx</tag>)
const result = list.map(item => { return <tag xxx>xxx</tag> }) // 箭头后有大括号时,表示函数体,必需用return
const result = list.map(item => <tag {...item} key='item.id'></tag>);
- 用
forEach
循环,,没有返回值,在函数内部生成新的数据
arrstr.forEach(item => {
let temp = <h5>{item}</h5>
tmparr.push(temp)
})
创建组件
- 直接用
<组件名></组件名>
来渲染组件 or<组件名 />
- 名字首字母必须大写,参数是 readonly
- 单独的组件文件时,将组件单独写在一个.jsx文件里面时,这个jsx里面也需要
import React from 'react'
,并且export 组件
默认属性值
defaultProps
,参数的默认值,在定义组件之后,export
之前App.defaultProps = {
gender: 'female'
}
验证父组件传入参数的合法性
propTypes
```javascript // 1. 在子组件里面 import propTypes from ‘prop-types’
// 2. 在定义子组件之后,export之前 App.propTypes = { num: PropTypes.number }
// 2. 或者写在组件内部 static propTypes={ num: PropTypes.number // 表示参数num的类型需要是number name: PropTypes.string.isRequired // name为string类型 且不能为空 }
<a name="753a15f4"></a>
##### 第一种 function
1. 用`function`关键字来创建
2. 使用`return`返回内容
```javascript
function Component(props){
return <p>组件内部...</p>;
}
ReactDOM.render(
<div>渲染组件
<Component></Component>
</div>,
document.getElementById('root')
)
第二种 class
- 使用
**class**
来创建- 必须继承自
Component
- 必须实现
render
函数 - 传入的参数,直接通过
{this.props.xx}
来使用
- 必须继承自
class App extends Component {
// 构造函数
constructor(props){
super(props)
}
render(){
return (
<div className="div">
{this.props.name}
</div>
)
}
}
// 使用
const usr = {
name:'zs',
age: 9
}
ReactDOM.render(
<App {...usr} />,
document.getElementById('root')
)
- 在
class
组件内部,类似render
等重写组件类的方法 内部的this
表示组件对象; 其他新创建的函数内部的this
是undefined
,使用.bind
方式来使this
表示为组件对象- 使用箭头函数就好了,不需要bind
// 在构造函数里面
- 使用箭头函数就好了,不需要bind
this.myClick = this.myClick.bind(this);
4. 在子组件里面改变父组件的状态,不能直接改变
- 状态在哪个组件,更新状态的行为就应该在哪个组件
- 所以,子组件需要调用父组件的方法 来改变父组件的状态
- 将父组件函数的引用传给子组件 `name = {this.name}`
### 3种基本属性
#### state
1. 使用`class`来创建组件时,该组件拥有自身的一个属性`state`,一般可定义在构造函数里面,但是也可以直接写在类里面
```javascript
// 构造函数里面的
this.state = {msg: 'my name is msg'}
// 类里面
state = {msg: 'my name is msg'}
- 注意,不要在
render
里面改变state
的值 更改
state
的值,使用this.setState()
this.setState({msg: 'haha'}, function(){
console.log(this.state.msg) // 输出1
});
console.log(this.state.msg) // 输出2
- 该设置是异步执行的,需要在回掉函数中输出新的结果,在输出2中输出的仍然是旧的值
- 且先 输出2 再输出1
props
- 传入的参数,在组件标签里面
propName = {xxx}
,可以是变量、对象、函数等 传入对象时,可以一个一个写,或者用
...
function Component(props){
return <p>{props.name}</p>;
}
const user = {
name: 'lin',
age: 21,
gender: 'female'
}
ReactDOM.render(
<div>渲染组件
<Component name={user.name} age={user.age} gender={user.gender}></Component>
</div>,
document.getElementById('root')
)
- 使用ES6的写法 传入参数,
...
展开运算符,<Component {...user}></Component>
...
的作用:- 打包
```javascript // 定义一个函数 function fn(…por){}
// 调用这个函数 fn(1,2,3)
// 这儿的’…’把传入的三个数打包成一个参数por,por就是一个数组
- 解包
```javascript
const arr1 = [1, 2, 3]
const arr2 = [4, ...arr1, 8]
// 这儿的'...'把arr1解开 放入arr2中
refs
refs
属性,可以绑定在render()输出的任意标签上
```javascript
var input = this.refs.myInput; var txt = input.value; // 这样可以获取上面input的值
```javascript
<input ref={input => this.input = input} />
const input = this.refs.content
添加css样式
添加行内样式
- 使用对象的形式,双
{}
:<div style={{width: '300px', height: '300px'}}></div>
- 两个单词连接的,第二个单词首字母大写
backgroundColor
- 封装成样式对象,然后在标签里面引用
导入css文件
import cssobj from css文件路径
或import from css文件路径
- 注意: 在一个项目中导入了css样式文件,它的作用域在整个项目——-下面改作用域
css Module 样式表
- 防止全局式的css 系统将你的本地类名(此模块中的类名)编译改成全局名,所以为何不在自己写的时候就对应全局的类名呢。毕竟动态赋予类名时还不知道改怎么弄…
- css文件使用
[name].module.css
来命名. - import时,用
import styles from '路径'
... return <div className={styles.classname}></div>
event
btn_click
- 小驼峰命名法,
onClick = { }
<button style={{xxx}} onClick={ () => { this.display('参数1','参数2')} }>按钮</button>
/// 或者
<button onClick={this.search}>Search</button>
display = (arg1, arg2) => {
console.log("333")
}
input的onChange事件
- 分三步
- 为文本框绑定
onChange
事件 - 在这个事件中,拿到最新的文本框的值
- 调用
this.setState
同步数据
- 为文本框绑定
<input type='text' style={inputStyle} onChange={ (e) => {this.chagemy(e)}} value={this.state.msg} ref = 'txt'/>
chagemy(e){
// this.setState({ msg: this.refs.txt.value})
this.setState({ msg: e.target.value})
}
input_onBlur
- input的失去焦点事件
```javascript
handlerBlur = (event) => { alert(event.target.value) }
<a name="kd66L"></a>
## 定时器
1. 只使用一次的定时器
2. 循环使用的定时器
```javascript
// 生命周期函数
componentDidMount() {
// 设定循环定时器
// this.intervalId 添加为组件对象的属性 是为了之后方便清除计时器
this.intervalId = setInterval(function () {
let opacity = this.state.opacity;
opacity -= 0.1;
console.log("定时器");
if(opacity <= 0){
opacity = 1;
}
this.setState({opacity});
}.bind(this), 200); // bind 指定 this为组件对象
}
// 清除定时器
clearInterval(this.intervalId);
组件间通信的传递
传统方式
使用 PubSubJS
- 下载
npm install pubsub-js --save
- 引入
import PubSub from 'pubsub-js'
发布消息
PubSub.publish("search", searchName); // 消息名字,附带的数据
订阅消息
PubSub.subscribe("search", (msg, searchName) => {
// searchName 就是之前发布的消息附带的数据
}
)
redux
组件的生命周期
- 组件的创建
- 组件的运行
- 组件的销毁
- 流程:
- 第一次初始化渲染显示:ReactDOM.render()
- constructor(): 创建对象 初始化 state
- componentWillMount(): 将要插入回调
- render(): 用于插入虚拟DOM回调
- componentDidMount(): 已经插入回调
- 每次更新 state: this.setState()
- componentWillUpdate(): 将要更新回调
- render(): 更新渲染
- componentDidUpdate(): 已经更新
- 移除组件: RenderDOM.ummountComponentAtNode(containerDom)
- componentWillUnmount(): 逐渐将要被移除回调
- 第一次初始化渲染显示:ReactDOM.render()
引入图片
import logo from '路径'
引入文件 与本文件在同一级 用’./‘,在上一级 用’../‘
other mind
- 箭头函数的使用
- 组件间的参数、函数传递的方法类似
- 将一个对象拆分成几个数值时,名字对应,,结构赋值 ES6语法
var object = {
name: 'lin',
age: 22
}
const {name, age} = object;
- 非受控组件与受控组件(收集表单数据的)
- 受控组件:表单项输入数据能自动收集成状态(收集到state里面,读组件的状态)
- 非受控组件:需要手动读取表单输入框中的数据(直接读取数据) ```javascript
handlerSubmit = (event) => {
const name = this.nameInput.value;
alert(准备提交的用户名为${name},密码为${this.state.pwd}
); // 这种${}
是什么写法啊
// 阻止提交表单
event.preventDefault();
} handlerChange = (event) => { let pwd = event.target.value; this.setState({pwd: pwd}); // 修改state里面的值 }
6. 对象的`map`函数
7. 可以在组件里面调用其它的组件
<a name="vQiBF"></a>
## ajax_get_post
**使用axios**
```javascript
axios.get(url)
.then(response => {
const result = response.data;
const users = result.items.map(item => ({name: item.login, url: item.html_url, avatarUrl: item.avatar_url}));
this.setState({loading: false, users});
})
.catch(error => {
console.log('error');
this.setState({loading: false, errorMsg: error.message});
})
Router
- 专门用来实现 SPA 单页面应用
- 点击页面不会刷新页面,也不会向服务器发送请求
- 点击路由链接,页面局部刷新
- 整个应用只有一个页面
- 路由,一个路由就是一个映射关系
- key 为路由路径,value 可以是 function / component
- 底层利用浏览器history实现
- 路由分类
- 后台路由,node服务器端路由,value 是 function ,用来处理客户提交的请求并返回一个响应数据
- 注册路由:
router.get(path, function(req,res))
- 注册路由:
- 前台路由,浏览器路由,value 是 component ,当请求某路由path时,浏览器没有发送http请求,但界面会更新显示对应的组件
- 注册路由:
<Route path='/about' component={About} />
- 注册路由:
- 后台路由,node服务器端路由,value 是 function ,用来处理客户提交的请求并返回一个响应数据
- 组件
<BrowserRouter>
,路由器组件<HashRouter>
,路由器组件<Route>
,路由<Redirect>
,重定向<Link>
,路由链接<NavLink>
,导航路由链接activeClassName
可指定自定义的active显示样式
<Switch>
,多个
- 基本使用
```javascript // index.js // 使用路由器组件将整个应用包住 ReactDOM.render(
// app.jsx import {NavLink, Switch, Route, Redirect} from ‘react-router-dom’
// 两个供显示的简单组件 import Home from ‘./views/home’ import About from ‘./views/about’
// 在左边显示目录,右边显示组件内容 render(){ return (
PS:
```javascript
// 使用自定义的组件 MyNavLink
<MyNavLink className='list-group-item' to='/about'>About</MyNavLink>
// MyNavLink.jsx
render(){
return <NavLink {...this.props} activeClassName='activeClass'></NavLink>
}
/* 当页面里面所有的NavLink都需要设置自定义的样式,且都一样,这样可以减少代码 */