React.js初探

 

 

React.js

菜鸟官方解释:

React 是一个用于构建用户界面的 JAVASCRIPT 库。

React主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。

React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。

React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它

React特点:

  • 1.声明式设计 −React采用声明范式,可以轻松描述应用。

  • 2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。

  • 3.灵活 −React可以与已知的库或框架很好地配合。

  • 4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。

  • 5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。

  • 6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

Virtual Dom

  • state创建数据

  • jsx 模板

  • 数据 + 模板 生成Virtual DOM(虚拟节点就是js创建的一个对象,该对象是对节点的一个描述,包含了该节点的所有信息,attrs,content等)

  • 用虚拟DOM生成真实的DOM,显示

  • state改变

  • 数据 + 模板生成新的 Virtual DOM

  • 对比新旧Virtual DOM的区别,找到区别。

  • 直接操作DOM改变有区别的地方。

    注:因为浏览器在解析网页时,js和html是分开的。就相当于两个模块,js要跨越界限操作别人的东西,所以说代价是比较大的。虚拟dom的创建使得性能很大程度提升,且他的应用 使得跨端应用得意实现 React Native.在android和ios端是不存在dom一说的,所以在创建虚拟dom后,会将虚拟dom生成对应的原生的东西。

Progressive Web Application

字义即:渐进式Web应用

PWA有可以实现什么呢?

  • 可以将app的快捷方式放置到桌面上,全屏运行,与原生app无异

  • 能够在各种网络环境下使用,包括网络差和断网条件下,不会显示undefind

  • 推送消息的能力

  • 其本质是一个网页,没有原生app的各种启动条件,快速响应用户指令

    在React.js中。public文件夹下有一个mainfest.json的文件夹,该文件就是用来实现桌面快捷方式的配置,而无网络情况,或网络情况较差时,则使用service worker来实现离线缓存。

Service Worker

Web离线引用解决方案。指在在进过网页后,当网络断开连接后,依然可以再次访问页面。

ServiceWorker的主要能力集中在网络代理和离线缓存上。具体的实现上,可以理解为ServiceWorker是一个能在网页关闭时仍然运行的WebWorker

在通过react脚手架创建项目后,src根目录下是有一个serviceWorker.js文件的,该文件就是用来写app时,提供离线缓存技术。供线下仍可正常访问。

Immutable

保证state中的数据不被修改。修改数据时,不能通过this.state.xx来就该数据需要用特定的方法this.setState({});可以发现和小程序也是一样的了

JSX

百度解释:

JSX是一种JavaScript的语法扩展,运用于React架构中,其格式比较像是模版语言,但事实上完全是在JavaScript内部实现的。元素是构成React应用的最小单位,JSX就是用来声明React当中的元素,React使用JSX来描述用户界面。

详细使用:JSX

 render() {
     return (
       <div>
         <ul>
           <li></li>
         </ul>
       </div>
     )
 }

上面代码等同于下面

 render() {
     return React.createElement('div',{},React.createElement('ul',{},React.craeteElement('li',{})));
 }

他的顺序:jsx => createElement => Virtual Dom => 真实Dom

vue和react的虚拟节点的渲染其实是一致的

className

在设置样式时,我们原来都是通过添加class或者id来设置对应的类名,在react中因为创建模板时使用的就是class类,所以在渲染模板时,样式的设置可以通过className=""来设置

Fragment

每个组件都要求有大的标签包裹,但是会产生不必要的DOM,因此react提供了Fragment来坐占位符。这样就不会产生新的节点,且组件不会抱任何错误。

 import React, { Component,Fragment } form 'react';
 ​
 class Demo extends Component {
     render() {
         return (
           <Fragment>
           Html content
           </Fragment>
         )
     }
 }

dangerouslySetInnerHTML

在render中如果要使某些内容不被转义。

则我们就需要使用dangerouslySetInnerHTML来保持元素不被转义

 <div dangerouslySetInnerHTML={{ __html: '<div>123</div>' }} />
  1. dangerouslySetInnerHTMl 是React标签的一个属性,类似于angular的ng-bind;

  2. 有2个{{}},第一{}代表jsx语法开始,第二个是代表dangerouslySetInnerHTML接收的是一个对象键值对;

  3. 既可以插入DOM,又可以插入字符串;

  4. 但是存在一个问题,因为使用标签内容后会将其转换,所以很有可能遭到XSS攻击。

import React,{ Component,Fragment } from 'react';
import './style/todoList.css'

class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      inputValue: '',
      list: []
    }
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);      
  }
  render() {
    return (
      <Fragment>
        {/**这里是注释 */}
        <input 
          className="input"
          onChange={this.handleInputChange.bind(this)}
          value={this.state.inputValue} 
        />
        <button className="btn" onClick={this.handleSubmit}>提交</button>
        <ul>
          {
            this.state.list.map((item,index) => {
              return (
                <li 
                  key={index}
                  onClick={this.handleDeleteItem.bind(this,index)}
                  dangerouslySetInnerHTML={{__html:item}}
                >
                </li>
              )
            })
          }
        </ul>
      </Fragment>
    )
  };
  handleInputChange(e) {
    this.setState({
      inputValue: e.target.value
    })
  };
  handleSubmit() {
    this.setState({
      list: [...this.state.list,this.state.inputValue],
      inputValue: ''
    })
  };
  handleDeleteItem(index) {
    let list = [...this.state.list];
    list.splice(index,1);
    this.setState({
      list: list
    })
  }
}
export default TodoList;

当我们在input中输入<h1>Hello World</h1>时,如果使用了dangerouslySetInnerHTML的话,h1标签就会生效。

htmlFor

在使用label时,一般我们会通过for与input上的id进行绑定。在react中,我们则需要通过htmlFor=""来绑定input

组件传值

父传子

父传子通过属性传递,将需要传递的数据放在子组件上,通过属性的方式传递,子组件通过this.props.attr则就可以获取到父组件传递过来的数据

父组件:

import TodoItem from './TodoItem'
render() {
    return (
        <ul>
          {
            this.state.list.map((item,index) => {
              return (
                <div>
                  <TodoItem content={item} index={index} />
                  {/*<li 
                    key={index}
                    onClick={this.handleItemDelete.bind(this,index)}
                    dangerouslySetInnerHTML={{__html:item}}
                  >
                  </li>*/}
                </div>
              )
            })
          }
        </ul>      
    )
}

给子组件将数据挂在到content和index上,

子组件的获取

import React, { Component } from "react";

class TodoItem extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  render() {
    return (
      <div>{this.props.content}</div>
    )
  }
}

export default TodoItem;

通过this.props就可以获取到父组件传递过来的数据。

子传父

子传父的话,没有vue那么多的方法,这里需要在父组件先将方法传递给子组件。

子组件调用父组件更改数据的方法。获取将数据作为方法的参数调用传递给父组件的方法就可以。

将方法通过属性的方式挂在到子组件上,不过需要注意的是,这里需要绑定指向为父组件

还是刚才的内容

import React,{ Component,Fragment } from 'react';
import TodoItem from './TodoItem';
import './style/todoList.css';

class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      inputValue: '',
      list: []
    }
    this.handleInputChange = this.handleInputChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleItemDelete = this.handleItemDelete.bind(this);
  }
  render() {
    return (
      <Fragment>
        <div>
          <label htmlFor="inputArea">输入内容</label>
          <input
            id="inputArea"
            className="input"
            onChange={this.handleInputChange}
            value={this.state.inputValue} 
          />
          <button 
            className="btn" 
            onClick={this.handleSubmit}
          >提交</button>
        </div>
        <ul>
          {this.getTodoItem()}
        </ul>
      </Fragment>
    )
  };
  getTodoItem() {
    return this.state.list.map((item,index) => {
      return (
        <TodoItem
          key={index}
          content={item} 
          index={index} 
          deleteItem={this.handleItemDelete} 
        />
      )
    })
  };
  
  handleInputChange(e) {
    const value = e.target.value;
    this.setState(() => ({
      inputValue: value
    }))
  };
  handleSubmit() {
    this.setState((prevState) => ({
      list: [...prevState.list,prevState.inputValue],
      inputValue: ''
    }))
  };
  handleItemDelete(index) {
    this.setState((prevState) => {
      const list = [...prevState.list];
      list.splice(index,1);
      return {list}
    })
  }
}
export default TodoList;

deleteItem就是挂在的方法,供子组件调用

子组件

import React, { Component } from "react";

class TodoItem extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }
  render() {
    const { content }= this.props;
    return (
      <div onClick={this.handleClick}>
        {content}
      </div>
    )
  }
  handleClick() {
    const { deleteItem,index } = this.props;
    deleteItem(index);
  }
}

export default TodoItem;

注:react是单向数据流,举这样一个例子:父组件引用了5个子组件,每个子组件都引用了父组件传递的list.如果不是单向数据流,子组件在对this.props.list= [];做出修改后,其他4个子组件会怎样?

显示这就是一个bug了,修改一个其他的都修改了。显然是一个比较严重的问题。所以react单向数据里就是解决这样一个问题。

父组件传递给子组件的数据,是无法做出修改的。state中的数据有一个只读属性,是无法做出修改的。如果非要修改,需要父组件传递给子组件一个方法,子组件通过调用该方法来实现数据的更改。

setState

该方法用来更新state数据,调用该方法时,就算state中的数据没有做修改,也会重新执行render方法。

setState({
    isActive: true,
})
console.log(this.state.isActive)//false

该方法其实是一个异步操作,所以要得到最新的state需要在第二个参数回调函数中获取或者通过async和await来解决啦

async handleClick() {
    await this.setState({
        isActive: true
    })
    console.log(this.state.isActive);//true
}

该方法有两个参数

setState((prevState) => {
    //prevState是旧的数据
},()=> {
    //这里可以得到更新以后的数据
})

PropTypes

组件间传值时,对接受数据的一个限制

详细doc可查看官网https://reactjs.org/docs/typechecking-with-proptypes.html

import { PropTypes } from 'prop-types'
class 组件 extends Component {}
组件.propTypes = {
  optionalArray: PropTypes.array,
  optionalBool: PropTypes.bool,
  optionalFunc: PropTypes.func,
  optionalNumber: PropTypes.number,
  optionalObject: PropTypes.object,
  optionalString: PropTypes.string,
  optionalSymbol: PropTypes.symbol,  
}

isRequired

要求该传值对象不能为空,必须有值

组件.propTypes = {
    obj: PropTypes.string.isRequired
}

arrayOf()

要求类型可以为多项

组件.propTypes = {
    obj: PropTypes.arrayOf(PropTypes.number,PropTypes.string)
}

defaultProps

为组件间传值的对象设置默认值

 class 组件 extends Component {}
 组件.defaultProps = {
     obj1: 'hello world'
 }

设置默认值后,父组件在未传递数据时,会默认使用该默认值

后续继续学习更新

猜你喜欢

转载自www.cnblogs.com/bgwhite/p/10948693.html