当能够说这么一句话 你才算是掌握了:
切 已经Out了
React-day01 入门知识
React介绍
Facebook脸书-> Instagram照片墙 。 于2013年5月开源
帮助开发者简洁、直观地构建高性能的UI界面
- 高效:模拟Doument Object Model,减少DOM交互 (速度快)
- 灵活:可以与已知的库配合使用
- 声明式: 帮助开发者直观的创建UI
- 组件化:把相似的代码通过封装成组件进行复用
官网
官方网站: https://reactjs.org/
React开发环境初始化 SPA
- react :React开发必备库
- react-dom:web开发时使用的库,用于虚拟DOM,移动开发使用ReactNative
脚手架初始化项目(方便,稳定)*
-
执行初始化命令:
#保证Node版本>=6 npm install -g create-react-app create-react-app my-app cd my-app npm start ## 如果npm版本5.2.0+,可以使用npx进行初始化 npx create-react-app my-app cd my-app npm start
通过webpack进行初始化
配置镜像地址
-
查看当前镜像配置:
npm config list
npm config get registry
-
设置当前镜像地址
npm config set registry https://registry.npm.taobao.org/
npm config set disturl https://npm.taobao.org/dist
开发工具配置
- 添加JavaScript语法支持
- 安装开发插件: JavaScript,npm, markdown, Node.js, Reactjs
元素渲染
-
元素是构成React应用的最小单位
import React from 'react'; import ReactDOM from 'react-dom'; const element = ( <div> <h1>HaHa!</h1> <h2>Hello Itheima element</h2> </div> ); // ReactDOM进行元素渲染 ReactDOM.render( element, document.getElementById('root') );
-
React对JSX语法转换
const element = ( <div className="eleClass"> HaHa! </div> );
转换js后
const element = React.createElement( "div", { className: "eleClass" }, "HaHa!" );
组件及组件状态
组件可以将界面分割成独立的、可复用的组成部分。只需要专注于构建每个单独的部分。比如按钮,对话框,列表,输入框都是组件。
- 组件可以接受参数(props),可以返回一个在页面上展示的React元素
函数定义组件(无状态)
无状态组件:一般用于简单的页面展示
// 用函数定义了名为Hello组件
function Hello(props) {
return <h1>Hello {props.name} !</h1>;
}
// react进行元素渲染
ReactDOM.render(
<Hello name="itheima props"/>,
document.getElementById('root')
);
类定义组件(有状态)*
- class 必须要ES6支持
有状态组件:可以维护自己的状态State信息,完成更加复杂的显示逻辑
// 用类定义 名为Hello组件
class Hello extends React.Component {
render(){
return <h1>Hello {this.props.name} !</h1>;
}
}
// react进行元素渲染
ReactDOM.render(
<Hello name="itheima class"/>,
document.getElementById('root')
);
- 以上两种组件效果在React相同
类定义组件名称必须是大写
建议在return元素时,用小括号()包裹
组合组件
-
组件之间可以相互引用,通常把App作为根组件
// 用类定义 名为Hello组件 class Hello extends React.Component { render() { return <h1>Hello {this.props.name} !</h1>; } } // 根组件 function App(props) { return ( <div> <div> <h2>团队名称: {props.team}</h2> <p>成员个数: {props.count}</p> <p>成立时间: {props.date.toLocaleString()}</p> </div> <Hello name="悟空" /> <Hello name="八戒" /> <Hello name="沙僧" /> <Hello name="三藏" /> </div> ); } // react进行元素渲染 ReactDOM.render( <App team="西天取经团" count={4} date={new Date()}/>, document.getElementById('root') );
-
注意:组件的返回值只能有一个根元素,所以用一个div包裹所有Hello元素
- 在google插件市场搜索安装React查看DOM结构
Props属性*
- props有两种输入方式:
- 引号"" :输入字符串值,
- 大括号{} :输入JavaScript表达式,大括号外不要再添加引号。
- props的值不可修改,属性只读,强行修改报错
- 类定义组件中使用props需要在前边加 this.
State状态*
-
自动更新的时钟
class Clock extends Component { render(){ return ( <div> <h1>当前时间:</h1> <h3>current: {new Date().toLocaleString()}</h3> </div> ); } } setInterval(() => { ReactDOM.render( <Clock />, document.getElementById('root') ); }, 1000);
应用一般执行一次ReactDOM.reader() 渲染
在组件内部进行更新, 每个时钟内部都维护一个独立的date信息
-
在组件内部使用局部state状态属性
class Clock extends Component { constructor(props) { super(props); // state定义:在constructor构造函数进行state状态的初始化 this.state = { title: "时钟标题", date: new Date() }; setInterval(() => { this.tick(); }, 1000); } tick(){ // 更新date, 数据驱动, 必须通过setState函数修改数据并更新ui this.setState({ date: new Date() }) } render(){ return ( <div> <h1>{this.state.title}</h1> <h3>current: {this.state.date.toLocaleString()}</h3> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') );
-
state特性:
- state 一般在构造函数初始化。
this.state={...}
- state可以修改,必须通过
this.setState({...})
更新并渲染组件 - 调用setState更新状态时,React最自动将最新的state合并到当前state中。
- state 一般在构造函数初始化。
组件生命周期
-
生命周期常用的函数
componentDidMount:组件已挂载, 进行一些初始化操作
componentWillUnmount: 组件将要卸载,进行回收操作,清理任务
事件处理
定义组件事件
class App extends Component {
handleClick(e){
console.log("handleClick!")
console.log(this);
}
render(){
return (
<div>
<button onClick={() => this.handleClick()}>
按钮:{'{() => this.handleClick()}'}
</button>
</div>
);
}
}
属性初始化器语法*
// 属性初始化器语法 (Property initializer syntax)
handleClick = () => {
console.log("handleClick!")
console.log(this);
}
参数传递*
class App extends Component {
handleClick(e, str, date){ // 参数要和调用者传入的一一对应
console.log(this)
console.log(e)
console.log(str, date)
}
render(){
return (
<button onClick={(e)=>this.handleClick(e, "参数" , new Date())}>
按钮1:{'箭头函数'}
</button>
);
}
}
参数要和调用者传入的一一对应
计数器游戏
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
// 类定义组件的写法
class App extends Component {
constructor(props) {
super(props);
// 绑定this到事件函数
this.countPlus = this.countPlus.bind(this);
this.state = {
count: 0,
timeSurplus: 10
};
}
// 组件已挂载, 开启周期任务
componentDidMount() {
this.timerId = setInterval(() => {
this.setState((preState) => ({
timeSurplus: preState.timeSurplus - 1
}))
// 在合适的时候, 结束周期函数
if(this.state.timeSurplus <= 0){
clearInterval(this.timerId)
}
}, 1000);
}
countPlus(){
// 更新当前count数字.
console.log(this)
// 如果时间到了, 返回
if (this.state.timeSurplus <= 0){
return;
}
// 更新数据会自动触发UI的重新render
// this.setState({
// count: this.state.count + 1
// })
// 通过之前state状态更新现在的状态
this.setState((preState) => ({
count: preState.count + 1
}))
}
render() {
return (
<div>
<h1>{this.props.title}</h1>
<h2>
{
this.state.timeSurplus <= 0 ?
("时间到, 总数" + this.state.count) :
("剩余时间:" + this.state.timeSurplus)
}
</h2>
<button onClick={this.countPlus}>
计数: {this.state.count}
</button>
</div>
);
}
}
ReactDOM.render(
<App title="计数器, 试试你的手速!"/>,
document.getElementById('root')
);
style样式(JSX写法)
-
直接写在style属性中
<button style={{width: 200, height: 200}}>我是按钮</button>
-
通关变量声明样式并引用
const btnStyle = { width: 200, height: 200 }; ... <button style={btnStyle} onClick={this.handleClick}>我是按钮</button>
#脚手架安装和样式处理
- 使用yarn安装脚手架
npm i -g yarn
npm uninstall -g create-react-app
yarn global add create-react-app
create-react-app my-app
- 有道翻译Api: key
应用ID 36408f4c57bebc38
应用密钥 VIB9yiN5LQZZVkaXOpnKD7DpYMw9VeNl
style样式
-
直接写在style属性中
<button style={{width: 200, height: 200}}>我是按钮</button>
-
通关变量声明样式并引用
const btnStyle = { width: 200, height: 200 }; ... <button style={btnStyle} onClick={this.handleClick}>我是按钮</button>
-
通过css样式文件设置
- 创建css文件
.btn {
width: 300px;
height: 200px;
background-color: coral;
}
? 2. 引入
import './css/counter.css';
? 3. 使用className
<button className="btn">我是按钮</button>
React 进阶提升
条件渲染
- if条件渲染: 如果用户输入的金额>=10元, 购买成功
function BuySomething(props){
if(props.money >= 10){
return <p>恭喜, 购买成功! {props.money} >= 10</p>;
}
return <p> 购买失败,金额不足! {props.money}</p>
}
if (list.length % 2 === 0){
subTitle = <p>好记性不如烂笔头! </p>;
} else {
subTitle = <p>明日复明日, 明日何其多! </p>;
}
- && 连接符判断
{this.props.showTitle && (<h2>GTD 记事本</h2>)}
- 三目运算符判断
{/* 三目运算符/三元运算符 */}
{
list.length === 0 ?
<p>这里空空如也, 恭喜咯!</p> :
<ul>{list}</ul>
}
- 阻止组件渲染 (很少用到)
// 某种不希望渲染的条件发生了, 通过返回null阻止render渲染
if(true){
return null;
}
return (...);
受控组件*
在html中, input, select, textarea这些表单元素都会默认维护自己的状态,React通过受控组件
将用户输入的内容保存到state中,通过渲染表单组件,控制用户输入之后发生的变化。
//1. 在构造函数初始化状态
constructor(props) {
super(props);
this.state = { money:'' };
}
// 2.监听input的变化
<input type="text" value={this.state.money} onChange={this.handleChange} />
// 3.处理变化,更新state
handleChange = (e)=>{
// console.log(e.target.value);
this.setState({
money: e.target.value
})
}
状态提升*
如果有多个组件需要共享状态数据,把要MoneyInput共享的数据state={money, unit}提升到他们最近的共同父组件App中。
数据源要保证只有一个,并保持自上而下的数据流结构。

组件数据流
- 用户在MoneyInput组件中输入了数值
- 在input的onChange函数中,监听到了变化,把money传给父组件
- 父组件设置并更新到唯一的state中,状态提升完毕
- 因为this.setState被执行,故而render被重新触发, 根据unit,money转成不同的货币
- 父组件通过
money={rmb}
属性设置给MoneyInput组件,this.props.money
TODO-LIST
GTD软件: Getting things done
设置服务器端口
在package.json的scripts下配置
"start": "SET PORT=3456 & react-scripts start",
列表渲染
.map
可以帮我们把数组转换成元素的数列.filter
根据每个条目返回的boolean, 决定去留. true保留, false删除- 列表key注意:
- key的作用时在DOM中的元素被添加或移除时, 帮助react识别是哪些元素发生了变化
- 每个元素key需要是当前列表唯一的字符串, 一般使用id, 没有id时才使用index索引
- key要放在列表条目的根组件 (有嵌套时)
条目PropTypes检查类型
-
导包
import PropTypes from 'prop-types';
-
propTypes只在开发模式下进行检查,当给组件传入错误的值(或未传值),JS控制台会打印警告信息。在生产环境不会进行检查(影响页面效率)
-
属性类型及Required检查
/** * 属性类型及isRequired检查 * order: 数字类型 * item: 字符串类型 */ TodoItem.propTypes = { order: PropTypes.number.isRequired, item: PropTypes.string, }
-
设置属性默认值(当父组件没有传入指定值时,使用默认值)
/** * 设置属性默认值 */ TodoItem.defaultProps = { item: "默认的条目描述" }
export & import
Refs 和 DOM
ref用来访问DOM节点或React元素 ref是reference的缩写
- 使用方式:
- 在组件得属性中添加
ref={(input) => {this.inputNode = input}}
- 在需要用其值或引用的地方通过
this.inputNode
即可
- 在组件得属性中添加
- 注意事项:
- ref在html元素上使用, ref的引用的是DOM节点
- ref在自定义的class组件上使用,ref的引用的是当前组件的实例
- ref不可以在函数式声明的组件上使用, 因为其没有实例,但是可以在内部使用ref
非受控组件*
- 如果希望表单数据由DOM处理,可以使用非受控组件方式。
- 常见的表单受控组件及 设置/获取 值方式如下:
元素 | 属性值 | 变化回调 | 在回调中获取值 |
---|---|---|---|
<input type="text" /> |
value="string" |
onChange |
event.target.value |
<input type="checkbox" /> |
checked={boolean} |
onChange |
event.target.checked |
<input type="radio" /> |
checked={boolean} |
onChange |
event.target.checked |
<textarea /> |
value="string" |
onChange |
event.target.value |
<select /> |
value="option value" |
onChange |
event.target.value |
-
在受控组件和非受控组件之间抉择:参考文档
如果表单的UI交互非常简单:通过refs实现非受控组件即可。
如果表单的UI交互比较复杂:官方推荐使用受控组件实现表单,把DOM节点/React元素的值或数据交给React组件处理(保存到state)。
路由
安装:
yarn add react-router-dom
导入Module:
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
声明路由器及路由:
<Router>
<div>
<Route exact={true} path="/" render={() => (<TodoComponent showTitle={true} />)}/>
<Route path="/about" component={About}/>
</div>
</Router>
通过Link跳转
<Link to="/about">关于about</Link>
<Link to="/">主页Home</Link>
- 通过path声明路由路径;
exact={true}
表示严格匹配path- 通过component指定要渲染的组件;
- 通过render属性配合箭头函数,可以给组件传入属性参数。
- 通过Link的to属性指定要跳转的路径
组合
React提供了强大的组合模型,官方建议使用组合(而非继承)来复用组件代码。
包含
- 当一些组件不能提前知道他们的全部子组件是什么, 比如Dialog,Menu, Sidebar
- 通过组件内render声明
{props.children}
,使用时在标签内部填入自定义的元素内容即可。 - 通过自定义字段可以使组件拥有多个自定义元素入口。比如
subTitle
-
声明:
function AboutPanel(props) { return ( <div className="about-panel"> <h2 className="about-title">{props.title}</h2> {props.subTitle} <p>{props.desc}</p> {props.children} </div> ); }
-
使用:
<AboutPanel title="兴趣爱好" desc="性别男, 爱好女。最大的缺点是太实诚,总爱习惯性给公司免费加班" > <input type="text" ref={(input) => this.textInput = input}/> <button onClick={() => alert(this.textInput.value)}>点评</button> </AboutPanel>
特殊实例
// 这里AboutPanelSpcial 是 AboutPanel 的特殊实例
class AboutPanelSpcial extends React.Component{
render(){
return (
<AboutPanel
title="工作履历"
subTitle={<h5>人生精力丰富, 行业跨度大</h5>}
desc="走过男闯过北, 火车道上压过腿; 养过狼放过虎, 少林寺里练过武"
/>
)
}
}
三方UI设计语言
- 蚂蚁金服:https://ant.design/index-cn
- Material-UI:https://material-ui.com/
打包及发布
npm run build
运行服务器代码: build包含index.html文件的文件夹
前台运行: serve -s build
后台运行(指定端口80): nohup serve -s build -l 80 &
今天所有安装的包
-
react-router-dom 路由
应用场景:页面切换
安装方式:
yarn add react-router-dom
-
serve 开启服务
应用场景:部署打包好的应用
安装方式:
yarn global add serve
移动开发
React-day03 RN移动端开发
了解React-Native
Facebook发起的开源的一套新的APP开发方案,Facebook在当初深入研究Hybrid开发后,觉得这种模式有先天的缺陷,所以果断放弃,转而自行研究,后来推出了自己的“React Native”方案,不同于H5,也不同于原生Native,更像是用JS写出原生应用
- 优点
-
开发成本小于Native模式 Andriod-Java-Kotlin IOS-OC-Swift
-
性能体验高于Hybrid
-
一次学习,跨平台开发Android和iOS, 小程序
-
社区繁荣
- 缺点
- 不同平台代码有所区别
- 开发人员学习有一定成本
- 几种开发技术对比 :应用质量和开发效率的平衡折衷的结果
了解React-Native工作流程
- 项目开发:使用Node初始化项目(需要安装Node),使用JavaScript/JSX语言开发
- 语言翻译:Python, C++将js翻译成java代码(需要安装Python2)
- 代码编译:Android-SDK将java编译成字节码(二进制),打包成可安装的apk(需要JDK8 & Android-SDK)
- 安装运行:通过Adb工具,把apk运行到Android模拟器
创建第一个React-Native项目 *
-
安装脚手架react-native-cli 同时安装新的版包管理工具
npm install -g yarn react-native-cli
-
创建项目:doubanMovie(在不包含中文的目录执行)
react-native init xxx --version react-native@0.55.4
-
运行项目
-
打开USB调试, 配置SDK
- ANDROID_HOME=G:\Android-SDK
- PATH=%PATH%;%ANDROID_HOME%\platform-tools
adb devices
查看已连接设备
-
连接模拟器:
adb connect 127.0.0.1:62001
-
更改gradle路径
doubanMovie\android\gradle\wrapper\gradle-wrapper.properties
distributionUrl
值修改为file\:///E:/Lesson/bc1/React/day03/Resource/gradle-2.14.1-all.zip
直接复制过来的路径要把反斜线\改成正斜线/
-
在项目根目录执行
react-native run-android
运行期间会开启一个Node服务,不要关闭
-
第一次运行报错,需要在设备上设置app的Node服务地址
解决方式: 打开app > 菜单按钮 > Dev Settings -> Debug server host ...
填写服务ip和端口号, 注意冒号用英文半角,重启服务,重启应用
-
了解React-Native项目及结构
-
目录结构
- index.js 项目入口文件
- App.js 项目根组件,用户看到的首页组件
- package.json 项目配置文件
- app.json 配置项目名称
-
React-Native与React对比
-
组件写法
RN提供View,Text组件,没有html的dom元素
View -> div 布局容器
Text -> p 显示文字
-
样式写法
使用
const styles = StyleSheet.create({...})
-
-
React-Native平台相关代码处理
const instructions = Platform.select({ ios: 'Press Cmd+R to reload,\n Cmd+D or shake for dev menu', android: 'Double tap R on your keyboard to reload,\n', });
开发资料
- 官方文档
- 中文文档
- 技术博客
- 在github.com搜索react
- https://github.com/poplartang 在Stars 搜索React
项目开发
路由(react-native-router-flux)
-
react-native-router-flux
-
源码地址:https://github.com/aksonov/react-native-router-flux
应用场景:在RN项目中进行路由跳转时使用
安装方式:
yarn add react-native-router-flux
-
使用:
Router(路由): 一般写在项目的根组件
Stack (栈):给Scene场景提供容器
Scene(场景):设置路由跳转规则
Actions (动作):触发路由跳转
const App = () => ( <Router> <Stack key="root"> <Scene key="login" component={Login} title="Login"/> <Scene key="register" component={Register} title="Register"/> <Scene key="home" component={Home}/> </Stack> </Router> );
注意事项:
-
最新版的react-native-router-flux会在react-native 0.55.4版本出现isMounted(...)警告,可在App.js添加以下代码忽略警告。随后两个框架更新后,此警告也可消除。
import { YellowBox } from 'react-native' YellowBox.ignoreWarnings(['Warning: isMounted(...) is deprecated'])
-
触发路由:三种方式 (注意导入Actions组件)
<Text onPress={Actions.movieList}>电 影</Text> <Text onPress={() => { Actions.movieList()}}>电 影</Text> <Text onPress={() => { Actions['about'].call() }}>关 于</Text>
开发组件(swiper)
-
开源轮播图react-native-swiper
-
源码地址:https://github.com/leecade/react-native-swiper
应用场景:在首页展示轮播图
安装方式:
yarn add react-native-swiper
-
常用属性:
index={1} 默认位置, 从0开始 showsButtons={true} 是否显示按钮 autoplayTimeout={2.5} 自动播放停留时间 autoplay={true} 是否自动播放 showsPagination={true} 显示分页指示器
网络请求(fetch) *
- Axios
- fetch
- 在
componentDidMount
执行请求并在回调中执行setState// 组件已经挂载 componentDidMount() { const url = 'http://api.douban.com/v2/movie/in_theaters'; fetch(url).then(res => res.json()) .then(data => { // 处理网络json数据 this.setState({ isLoading: false, movieList: data.subjects }) // console.warn(data.subjects) }).catch((err) => { console.error(err); }); }
长列表(FlatList) *
-
长列表优化
<FlatList data={this.state.movieList} keyExtractor={(item, index) => item.id} renderItem={({ item, index }) => { return ( <View style={{ padding: 10, flexDirection: 'row' }}> <Text>{item.title}: {item.year} : {index} </Text> </View> ) }} />
-
加载指示器
<View style={{ flex: 1, padding: 20 }}> <ActivityIndicator /> </View>
-
条目点击跳转
Actions.movieDetail({ "movieId": movie.id, "movieTitle": movie.title});
滚动视图(ScrollView)
- 发送网络请求
- 渲染视图
日志与调试
-
查看RN日志:
react-native log-ios react-native log-android
android也可在PC控制台输入
adb logcat *:S ReactNative:V ReactNativeJS:V
-
应用内的错误与警告
console.warn('Yellow.'); console.error('Red.');
-
Debug调试
- Chrome开发者工具
-
在Android设备菜单中选择“Debug JS Remotely”,PC会自动通过Chrome浏览器打开调试页面 http://localhost:8081/debugger-ui (需要自行安装Chrome)。这里注意自动打开的主机地址可能不是localhost,需要手动改成localhost (不修改,则手机页面可能是空白)
-
在Chrome浏览器按
Ctrl + Shift + I
或F12
打开控制台 -
选中Sources选项卡 -> Network标签 -> debuggerWorker.js 打开指定组件文件即可
-
如果没有没有debuggerWorker.js, 查看页面最下边的Status提示。
Status: Another debugger is already connected
另一个调试器已连接,直接使用或关闭那个调试器
Status: Waiting, press Ctrl R in simulator to reload and connect.
等待中,建议重新加载模拟器
-
可以在代码中打断点,Console中执行js代码打印变量、执行脚本
-
关闭调试:在Android设备菜单中选择“Stop Remote JS Debugging”即可
打包及发布
-
参见中文官网文档:https://reactnative.cn/docs/0.51/signed-apk-android.html#content
-
多端统一开发框架:https://github.com/NervJS/taro 生成能运行在微信小程序、H5、React Native 等的应用
-
修改图标
- [项目名称]\android\app\src\main\res\mipmap-xhdpi
今天所有安装的包
-
react-native-router-flux 路由
应用场景:在RN项目中进行路由跳转时使用
安装方式:
yarn add react-native-router-flux
-
react-native-swiper 开源轮播图
应用场景:在首页展示轮播图
安装方式:
yarn add react-native-swiper