- 概念
- 创建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> }) // 箭头后有大括号时,表示函数体,必需用returnconst 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>##### 第一种 function1. 用`function`关键字来创建2. 使用`return`返回内容```javascriptfunction 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种基本属性#### state1. 使用`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就是一个数组
- 解包```javascriptconst 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**```javascriptaxios.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( , document.getElementById(‘root’) )
// 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.jsxrender(){return <NavLink {...this.props} activeClassName='activeClass'></NavLink>}/* 当页面里面所有的NavLink都需要设置自定义的样式,且都一样,这样可以减少代码 */
