当能够说这么一句话 你才算是掌握了:
切 已经Out了

React-day01 入门知识

React介绍

Facebook脸书-> Instagram照片墙 。 于2013年5月开源

帮助开发者简洁、直观地构建高性能的UI界面

  • 高效:模拟Doument Object Model,减少DOM交互 (速度快)
  • 灵活:可以与已知的库配合使用
  • 声明式: 帮助开发者直观的创建UI
  • 组件化:把相似的代码通过封装成组件进行复用

官网

官方网站: https://reactjs.org/

中文网站: https://doc.react-china.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
    
  • npm和yarn命令对比

通过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特性:

    1. state 一般在构造函数初始化。this.state={...}
    2. state可以修改,必须通过this.setState({...})更新并渲染组件
    3. 调用setState更新状态时,React最自动将最新的state合并到当前state中。

组件生命周期

React中组件的生命周期

  • 生命周期常用的函数

    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写法)

  1. 直接写在style属性中

    <button style={{width: 200, height: 200}}>我是按钮</button>
    
  2. 通关变量声明样式并引用

    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样式

  1. 直接写在style属性中

    <button style={{width: 200, height: 200}}>我是按钮</button>
    
  2. 通关变量声明样式并引用

    const btnStyle = { width: 200, height: 200 };
    ...
    <button style={btnStyle} onClick={this.handleClick}>我是按钮</button>
    
  3. 通过css样式文件设置

    1. 创建css文件
.btn {
    width: 300px;
    height: 200px;
    background-color: coral;
}

? 2. 引入

import './css/counter.css';

? 3. 使用className

<button className="btn">我是按钮</button>

React 进阶提升

条件渲染

  1. 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>;
}
  1. && 连接符判断
{this.props.showTitle && (<h2>GTD 记事本</h2>)}
  1. 三目运算符判断
{/* 三目运算符/三元运算符 */}
{
    list.length === 0 ? 
        <p>这里空空如也, 恭喜咯!</p> :
        <ul>{list}</ul>
}
  1. 阻止组件渲染 (很少用到)
// 某种不希望渲染的条件发生了, 通过返回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中。

数据源要保证只有一个,并保持自上而下的数据流结构。

![](.\状态提升, 单位转换.png)

组件数据流

  1. 用户在MoneyInput组件中输入了数值
  2. 在input的onChange函数中,监听到了变化,把money传给父组件
  3. 父组件设置并更新到唯一的state中,状态提升完毕
  4. 因为this.setState被执行,故而render被重新触发, 根据unit,money转成不同的货币
  5. 父组件通过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的缩写

  • 使用方式:
    1. 在组件得属性中添加ref={(input) => {this.inputNode = input}}
    2. 在需要用其值或引用的地方通过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)。

52395837106

路由

参考链接1

中文参考文档

安装:

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
  1. 声明:

    function AboutPanel(props) {
        return (
            <div className="about-panel">
                <h2 className="about-title">{props.title}</h2>
                {props.subTitle}
    
                <p>{props.desc}</p>
                {props.children}
            </div>
        );
    }
    
  2. 使用:

    <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设计语言

打包及发布

npm run build

运行服务器代码: build包含index.html文件的文件夹

前台运行: serve -s build

后台运行(指定端口80): nohup serve -s build -l 80 &

今天所有安装的包

  1. react-router-dom 路由

    应用场景:页面切换

    安装方式:yarn add react-router-dom

  2. serve 开启服务

    应用场景:部署打包好的应用

    安装方式:yarn global add serve

移动开发

React-day03 RN移动端开发

了解React-Native

Facebook发起的开源的一套新的APP开发方案,Facebook在当初深入研究Hybrid开发后,觉得这种模式有先天的缺陷,所以果断放弃,转而自行研究,后来推出了自己的“React Native”方案,不同于H5,也不同于原生Native,更像是用JS写出原生应用

  • 优点
  1. 开发成本小于Native模式 Andriod-Java-Kotlin IOS-OC-Swift

  2. 性能体验高于Hybrid

  3. 一次学习,跨平台开发Android和iOS, 小程序

  4. 社区繁荣

  • 缺点
  1. 不同平台代码有所区别
  2. 开发人员学习有一定成本
  • 几种开发技术对比应用质量开发效率的平衡折衷的结果

了解React-Native工作流程

  1. 项目开发:使用Node初始化项目(需要安装Node),使用JavaScript/JSX语言开发
  2. 语言翻译:Python, C++将js翻译成java代码(需要安装Python2)
  3. 代码编译:Android-SDK将java编译成字节码(二进制),打包成可安装的apk(需要JDK8 & Android-SDK)
  4. 安装运行:通过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',
    });
    

开发资料

项目开发

路由(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>
    
  • 弹性布局参考: http://www.runoob.com/w3cnote/flex-grammar.html

开发组件(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开发者工具
    1. 在Android设备菜单中选择“Debug JS Remotely”,PC会自动通过Chrome浏览器打开调试页面 http://localhost:8081/debugger-ui (需要自行安装Chrome)。这里注意自动打开的主机地址可能不是localhost,需要手动改成localhost (不修改,则手机页面可能是空白)

    2. 在Chrome浏览器按Ctrl + Shift + IF12打开控制台

    3. 选中Sources选项卡 -> Network标签 -> debuggerWorker.js 打开指定组件文件即可

    4. 如果没有没有debuggerWorker.js, 查看页面最下边的Status提示。

      Status: Another debugger is already connected

      另一个调试器已连接,直接使用或关闭那个调试器

      Status: Waiting, press Ctrl R in simulator to reload and connect.

      等待中,建议重新加载模拟器

    5. 可以在代码中打断点,Console中执行js代码打印变量、执行脚本

    6. 关闭调试:在Android设备菜单中选择“Stop Remote JS Debugging”即可

打包及发布


今天所有安装的包

  1. react-native-router-flux 路由

    应用场景:在RN项目中进行路由跳转时使用

    安装方式:yarn add react-native-router-flux

  2. react-native-swiper 开源轮播图

    应用场景:在首页展示轮播图

    安装方式:yarn add react-native-swiper