一路繁花似锦绣前程
失败的越多,成功才越有价值

导航

 

一、邂逅react开发和初体验

1、简介
* react是什么?
    - 用于构建用户界面的javascript库
* 常见的javascript库
    - jquery
    - vue
    - react
    - angular
* react的起源
    - 2013年,facebook开源的javascript库
* react的特点-声明式编程
    - ui = f(state)
* react的特点-组件化开发
    - 复杂的界面拆分成一个个小的组件
* react的特点-多平台适配
    - 2013:web
    - 2015:reactnative用于移动端开发
    - 2017:reactvr用于开发虚拟现实web应用程序
* 掌握最先进的思想和技术
    - react hooks的思想
    - flutter(widget-element-renderobject)对应react(jsx-虚拟dom-真实dom)
* hackerrank调查显示
* react在国内外被广泛使用
    - 网易云音乐
    - 斗鱼
    - 知乎
    - 阿里云
    - 优酷
2、hello react案例
  • 原生实现
<h2 id="text"></h2>
<button id="btn">改变文本</button>
<script>
    // 命令式编程:每做一个操作,都是给计算机(浏览器)一步步命令
    // 声明式编程:
    // 1、定义数据
    let message = 'hello world'
    let text = document.getElementById("text")
    let btn = document.getElementById("btn")
    // 2、将数据显示在h2元素中
    text.innerHTML = message
    // 3、点击按钮,界面的数据发生变化
    btn.onclick = function () {
        message = 'hello react'
        text.innerHTML = message
    }
</script>
  • react实现
<div id="app"></div>
<!-- crossorigin:这个属性的目的是为了拿到跨域脚本的错误信息 -->
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 注意事项:使用jsx,并且希望script中的jsx的代码被解析,必须在script标签中添加一个属性 -->
<!-- jsx特点:多个标签最外层(根)只能有一个标签 -->
<script type="text/babel">
    /**
     * 一、react开发依赖
     *     - react:核心代码
     *     - react-dom:跨平台
     *     - babel:代码转换工具
     * 二、react-dom
     *     - web端:将jsx渲染成真实dom
     *     - native端:将jsx渲染成原生的控件(android的button,ios的uibutton)
     * 三、babel
     *     - es6 -> babel -> es5
     *     - jsx(语法糖) -> babel -> React.renderElement()
     * 四、引入react依赖
     *     - 直接cdn引入
     *     - 下载后,添加本地依赖
     *     - 通过npm管理(后续脚手架再使用)
     */
    let message = "hello world"

    function btnClick() {
        message = 'hello react'
        render()
    }

    function render() {
        // <div></div>:jsx代码
        ReactDOM.render(<div>
            <h2>{message}</h2>
            <button onClick={btnClick}>改变文本</button>
        </div>, document.getElementById('app'))
    }

    render()
</script>
  • 组件化实现
<div id="app"></div>
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script type="text/babel">
    // 封装app组件
    class App extends React.Component {
        constructor() {
            super();
            this.state = {
                message: "hello world"
            }
        }

        render() {
            return (
                <div>
                    <h2>{this.state.message}</h2>
                    <button onClick={this.btnClick.bind(this)}>改变文本</button>
                </div>
            )
        }

        btnClick() {
            this.setState({
                message: 'hello react'
            })
        }
    }

    // 渲染组件
    ReactDOM.render(<App/>, document.getElementById("app"))
</script>

二、js核心语法

1、es6的class
/*
// es5
function Person(name, age) {
    this.name = name
    this.age = age
}

Person.prototype.running = function () {
    console.log(`姓名:${this.name};年龄:${this.age}`)
}*/

// es6
class Person {
    constructor(name, age) {
        this.name = name
        this.age = age
    }

    running() {
        console.log(`姓名:${this.name};年龄:${this.age}`)
    }
}

/**
 * 一、this的四种绑定方式
 *     - 独立函数调用:fun()
 *     - 隐式绑定:p.fun()
 *     - 显示绑定:fun.apply(p)
 *     - new绑定:new P()
 * 二、箭头函数不绑定this,this从上层作用域获取
 */
const person = new Person('孟美岐', 18);
person.running()
2、类的继承
/**
 * 一、面向对象的三大特性
 *     - 封装
 *     - 继承:1、减少重复的代码;2、多态的前提(鸭子类型)
 *     - 多态
 */
class Person {
    constructor(name) {
        this.name = name
    }

    running() {
        console.log('父类方法')
    }
}

class Student extends Person {
    constructor(name, age) {
        super(name);
        this.age = age
    }

    eating() {
        console.log(this.name, this.age)
    }
}

const student = new Student('黄婷婷', 18);
student.running()
student.eating()

三、jsx核心语法

1、案例一:电影列表
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      title: "女神列表",
      girls: ['黄婷婷', '孟美岐', '鞠婧祎']
    }
  }

  render() {
    const girls = this.state.girls.reduce((src, item) => {
      src.push(<li>{item}</li>)
      return src
    }, [])
    return (<div>
      <div>
        <h2>{this.state.title}一</h2>
        <ul>{girls}</ul>
      </div>
      <div>
        <h2>{this.state.title}二</h2>
        <ul>{
          this.state.girls.map(item => <li>{item}</li>)
        }</ul>
      </div>
    </div>)
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>
2、案例二:计数器案例
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      counter: 0
    }
  }

  render() {
    return (<div>
      <div>{this.state.counter}</div>
      <button onClick={this.increment.bind(this)}>+1</button>
      <button onClick={this.decrement.bind(this)}>-1</button>
    </div>)
  }

  increment() {
    this.setState({
      counter: this.state.counter + 1
    })
  }

  decrement() {
    this.setState({
      counter: this.state.counter - 1
    })
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>
3、认识jsx
<!-- 引入babel(standlone:单机) -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 属性必须设置 -->
<script type="text/babel">
// jsx语法
const element = <div>hello world</div>
ReactDOM.render(element, document.getElementById("app"));
</script>
4、为什么react选择了jsx
* react认为渲染逻辑本质上与其他ui逻辑存在内在耦合(all in js)
* 他们之间是密不可分,所以react没有将标记分离到不同的文件中,而是将它们组合到了一起,这个地方就是组件
* jsx书写规范
    - 顶层只能有一个根标签,通常是原生div(或者使用后面我们学习的Fragment)
    - 通常会在jsx的外层包裹一个小括号(),方便阅读并且jsx可以进行换行书写
    - 可以是双标签或单标签(单标签必须以/>结尾)
5、jsx中的注释
<!-- html注释 -->
<script type="text/babel">
/**
 * 文档注释
 */
class App extends React.Component {
  constructor() {
    super();
    /*
    // 多行注释
    this.state = {
    }*/
  }

  // 单行注释
  render() {
    return (<div>
      {/* jsx的注释 */}
    </div>)
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>
6、jsx嵌入数据
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      // 1、在{}中可以正常显示的内容
      name: '黄婷婷',// String
      age: 18,// Number
      hobbies: ['篮球', '足球', '乒乓球'],// Array
      // 2、在{}中不能显示(忽略)
      test1: null,// null
      test2: undefined,// undefined
      test3: true,// Boolean
      flag: true,
      // 3、对象不能作为jsx的子类
      friend: {
        name: '姜贞羽',
        age: 19
      }
    }
  }

  render() {
    return (<div>
      <div>{this.state.name}</div>
      <div>{this.state.age}</div>
      <div>{this.state.hobbies}</div>
      <div>{this.state.test1}</div>
      <div>{String(this.state.test1)}</div>
      <div>{this.state.test2}</div>
      <div>{this.state.test2 + ''}</div>
      <div>{this.state.test3}</div>
      {/* null/undefined不可以使用toString()转字符串 */}
      <div>{this.state.test3.toString()}</div>
      <div>{this.state.flag ? '孟美岐' : null}</div>
      <div>{this.state.flag && '佟丽娅'}</div>
      {/* <div>{this.state.friend}</div> */}
    </div>)
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>
7、jsx嵌入表达式
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      firstName: '黄婷婷',
      lastName: '孟美岐',
      isLogin: true
    }
  }

  render() {
    const {firstName, lastName, isLogin} = this.state
    return (<div>
      {/* 1、运算符表达式 */}
      <div>{firstName + ' ' + lastName}</div>
      <div>{20 * 50}</div>
      {/* 2、三元表达式 */}
      <div>{isLogin ? '欢迎回来~' : '请先登录~'}</div>
      {/* 3、进行函数调用 */}
      <div>{this.getFullName()}</div>
    </div>)
  }

  getFullName() {
    return this.state.firstName + ' ' + this.state.lastName
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>
8、jsx绑定属性
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
function getSizeImage(imgUrl, size) {
  return imgUrl + `?param=${size}y${size}`
}

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      title: "标题",
      imgUrl: "http://p1.music.126.net/KcUB_qLaNKB1maFWtQIXtQ==/109951163559331646.jpg",
      link: "https://www.baidu.com",
      active: true
    }
  }

  render() {
    const {title, imgUrl, link, active} = this.state
    return (<div>
      {/* 1、绑定普通属性 */}
      <h2 title={title}>我是标题</h2>
      <img src={getSizeImage(imgUrl, 140)} alt=""/>
      <a href={link} target="_blank">百度一下</a>
      {/* 2、绑定class */}
      <div className="box title">我是div元素</div>
      <div className={"box title " + (active ? 'active' : "")}>我也是div元素</div>
      <label htmlFor=""></label>
      {/* 3、绑定style */}
      <div style={{color: 'red', fontSize: '50px'}}>我是div,绑定style属性</div>
    </div>)
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>
9、jsx绑定事件和this处理
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      message: "你好啊",
      counter: 100
    }
    this.btnClick = this.btnClick.bind(this)
  }

  render() {
    return (<div>
      {/* 1、方案一:bind绑定this(显示绑定) */}
      <button onClick={this.btnClick}>按钮1</button>
      {/* 2、方案二:定义函数时,使用箭头函数 */}
      <button onClick={this.increment}>+1</button>
      {/* 3、方案三(推荐):直接传入一个箭头函数,在箭头函数中调用需要执行的函数 */}
      <button onClick={() => {
        this.decrement('黄婷婷')
      }}>-1
      </button>
    </div>)
  }

  btnClick() {
    console.log(this.state.message)
  }

  // 箭头函数中永远不绑定this
  // es6中给对象增加属性:class fields
  increment = () => {
    console.log(this.state.counter)
  }

  decrement(name) {
    console.log(this.state.counter, name)
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>
10、jsx绑定事件-参数传递
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      persons: ['黄婷婷', '孟美岐', '鞠婧祎']
    }
    this.btnClick = this.btnClick.bind(this)
  }

  render() {
    return (<div>
      <button onClick={this.btnClick}>按钮1</button>
      <ul>{
        this.state.persons.map((item, index) => {
          return (<li
            className="item"
            title="li"
            onClick={e => {
              this.liClick(item, index, e)
            }}>
            {item}
          </li>)
        })
      }</ul>
    </div>)
  }

  btnClick(event) {
    console.log('按钮发生了点击', event)
  }

  liClick(item, index, event) {
    console.log('li发生了点击', item, index, event)
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>
11、条件渲染
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      isLogin: true
    }
  }

  render() {
    const {isLogin} = this.state
    // 1、通过if判断:逻辑代码非常多的情况
    let welcome = null
    if (isLogin) {
      welcome = <h2>欢迎回来~</h2>
    } else {
      welcome = <h2>请先登录~</h2>
    }

    return (<div>
      {welcome}
      {/* 2、方案二:三元运算符 */}
      <button onClick={e => this.loginClick()}>{isLogin ? '退出' : '登录'}</button>
      <hr/>
      <h2>{isLogin ? '你好啊,黄婷婷' : null}</h2>
      {/* 3、方案三:逻辑与&& */}
      {/* 逻辑与:一个条件不成立,后面的条件都不会进行判断了 */}
      <h2>{isLogin && '你好啊,张婧仪'}</h2>
      {isLogin && <h2>你好啊,孟美岐</h2>}
    </div>)
  }

  loginClick() {
    this.setState({
      isLogin: !this.state.isLogin
    })
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>
12、条件渲染-v-show效果
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      isLogin: true
    }
  }

  render() {
    const {isLogin} = this.state
    const titleDisplayValue = isLogin ? 'block' : 'none'
    return (<div>
      <button onClick={e => this.loginClick()}>{isLogin ? '退出' : '登录'}</button>
      <h2 style={{display: titleDisplayValue}}>你好啊,黄婷婷</h2>
    </div>)
  }

  loginClick() {
    this.setState({
      isLogin: !this.state.isLogin
    })
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>
13、列表渲染
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      persons: ['黄婷婷', '孟美岐', '张婧仪'],
      numbers: [5, 8, 3, 4, 9, 2, 7, 6, 1]
    }
  }

  render() {
    return (<div>
      <h2>名字列表(映射)</h2>
      <ul>{this.state.persons.map(item => <li>{item}</li>)}</ul>
      <h2>数字列表(过滤)</h2>
      <ul>{this.state.numbers.filter(item => item >= 5).map(item => <li>{item}</li>)}</ul>
      <h2>数字列表(截取)</h2>
      <ul>{this.state.numbers.slice(0, 5).map(item => <li>{item}</li>)}</ul>
    </div>)
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>

四、jsx的本质

1、jsx的本质
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
/**
 * 1、jsx是React.createElement(component,props,...children)函数的语法糖
 *     - jsx -> babel -> React.createElement()
 * 2、babel官网中快速查看转换的过程
 *     - https://babeljs.io/repl/#?presets=react
 */
const message1 = <h2>hello world</h2>
const message2 = React.createElement("h2", null, "hello world")
ReactDOM.render(message2, document.getElementById("app"));
</script>
2、虚拟dom的创建过程
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
  constructor() {
    super();
  }

  /**
   * 1、我们通过React.createElement最终创建出来一个ReactElement对象
   * 2、这个ReactElement对象是什么作用呢?React为什么要创建它呢?
   *     - 原因是React利用ReactElement对象组成了一个javascript的对象树
   *     - javascript的对象树就是大名鼎鼎的虚拟DOM(virtual dom)
   * 3、如何查看ReactElement的树结构呢?
   *     - 我们可以将之前的jsx返回结果进行打印
   *     - 注意下面代码中我打jsx的打印
   * 4、而ReactElement最终形成的树结构就是virtual dom
   * 5、jsx-虚拟dom-真实dom
   *     - jsx代码-ReactElement对象-真实dom
   * 6、为什么使用虚拟dom,而不是直接修改真实的dom呢?
   *     - 很难跟踪状态发生的改变:原有的开发模式,我们很难跟踪到状态发生的改变,不方便针对我们应用程序进行调试
   *     - 操作真实dom性能较低:传统的开发模式会进行频繁的dom操作,而这一的做法性能非常的低
   *         ~ document.createElement本身创建出来的就是一个非常复杂的对象
   *         ~ dom操作会引起浏览器的回流和重绘,所以在开发中应该避免频繁的dom操作
   * 7、声明式编程
   *     - virtual dom是一种编程理念
   *         ~ 在这个理念中,ui以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的javascript对象
   *         ~ 我们可以通过ReactDOM.render让虚拟dom和真实dom同步起来,这个过程中叫做协调(Reconciliation)
   *     - 这种编程的方式赋予了react声明式的api
   *         ~ 你只需要告诉react希望让ui是什么状态
   *         ~ react来确保dom和这些状态是匹配的
   *         ~ 你不需要直接进行dom操作,就可以从手动更改dom、属性操作、事件处理中解放出来
   */
  render() {
    const objTree = (<div>
      <h2>苏州</h2>
      <h2>无锡</h2>
      <h2>常州</h2>
    </div>)
    console.log(objTree)
    return objTree
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>
3、案例练习
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>react</title>
  <style>
  table {
    border-collapse: collapse;
  }

  td, th {
    border: 1px solid #cccccc;
    padding: 10px 16px;
  }

  th {
    background: #eeeeee;
  }

  .price {
    margin: 0 6px;
  }
  </style>
</head>
<body>
<div id="app"></div>
<script src="./js/react.development.js" crossorigin></script>
<script src="./js/react-dom.development.js" crossorigin></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
  constructor() {
    super();
    this.state = {
      books: [
        {
          id: 1,
          name: '《算法导论》',
          date: '2022-1',
          price: 85.5,
          count: 1
        }, {
          id: 2,
          name: '《unix编程艺术》',
          date: '2022-2',
          price: 59.6,
          count: 1
        }, {
          id: 3,
          name: '《编程珠玑》',
          date: '2022-3',
          price: 39.8,
          count: 1
        }, {
          id: 4,
          name: '《代码大全》',
          date: '2022-4',
          price: 128,
          count: 1
        }]
    }
  }

  render() {
    return this.state.books.length ? (<div>
      <table>
        <thead>
        <tr>
          <th></th>
          <th>书籍名称</th>
          <th>出版日期</th>
          <th>价格</th>
          <th>购买数量</th>
          <th>操作</th>
        </tr>
        </thead>
        <tbody>
        {this.state.books.map((item, index) => {
          return (<tr key={item.id}>
            <td>{item.id}</td>
            <td>{item.name}</td>
            <td>{item.date}</td>
            <td>¥{item.price.toFixed(2)}</td>
            <td>
              <button disabled={item.count <= 1} onClick={() => {
                this.changeCount(index, -1)
              }}>-
              </button>
              <span className="price">{item.count}</span>
              <button onClick={() => {
                this.changeCount(index, 1)
              }}>+
              </button>
            </td>
            <td>
              <button onClick={e => {
                this.removeItem(e, index)
              }}>移除
              </button>
            </td>
          </tr>)
        })}
        </tbody>
      </table>
      <h3>总价格:¥{this.countPrice()}</h3>
    </div>) : (<h2>购物车为空~</h2>)
  }

  changeCount(index, calculate) {
    const c_books = [...this.state.books]
    c_books[index].count += calculate
    this.setState({
      books: c_books
    })
  }

  removeItem(e, index) {
    // 保留原始合成事件,解决控制台查看合成事件对象属性报错(出于性能原因e.persist()一般不调用)
    e.persist()
    // 1、react中设计原则:state中的数据的不可变性
    this.setState({
      books: this.state.books.filter((item, indey) => indey !== index)
    })
  }

  countPrice() {
    const totalPrice = this.state.books.reduce((total, item) => {
      return total += item.price * item.count
    }, 0)
    return totalPrice.toFixed(2)
  }
}

ReactDOM.render(<App/>, document.getElementById("app"));
</script>
</body>
</html>
posted on 2022-09-07 00:54  一路繁花似锦绣前程  阅读(67)  评论(0编辑  收藏  举报