前端相关

react

2018-02-16  本文已影响0人  DeeJay_Y

webpack --help -p :
shortcut for --optimize-minimize --define process.env.NODE_ENV="production"

理解JSX语法

JSX语法注意点:

JSX语法的理解:分为tagname,attributes,children三项来理解

例如: const jsx = <div>Hello Jsx</div>
经过babel之后为:

var jsx = React.createElement(
  "div",
  null,
  "Hello Jsx"
);

这样可以理解为:

{
  tagname: "div",
  attributes: null,
  children: "Hello Jsx"
}

组件的tagname就不是标准的html标签了,如果有props,那么attributes也不是null了,对于children来说,如果内部还有嵌套标签,那么依次做一个递归。

组件

react声明组件时,第一个字母必须大写

两种写法:

  1. class component
class Welcome extends React.Component {
    render () {
        return <h1>hello,{this.props.name}</h1>;
    }
}

或者:
2.functional component

function Welcome(props) {
    return <h1>hello,{props.name}</h1>
}

组件中的数据源

  1. props (props是父组件到子组件的,props should be pure,即不予许直接修改props)

  2. state (state是自身维护的数据状态,但也只可以通过setState修改)

组件中不可以改变props的值,state是组件中可以改变的东西

但是要使用this.setState()方法才能改变state的值

关于setState() , 参考

要理解2点:

import React from 'react';
class Welcome extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            date: new Date()
        }
        setInterval(() => {
            this.setState({
                date: new Date()
            })
        })
    }
    render () {
        return (
            <div>
                <h1>hello, {this.props.name}</h1>
                <h2>{this.state.date.toString()}</h2>
            </div>
        )
    }
}

export default Welcome;


生命周期

声明周期英文文档

React 的生命周期包括三个阶段:mount(挂载)、update(更新)和 unmount(移除)

mount

mount 就是第一次让组件出现在页面中的过程。这个过程的关键就是 render 方法。React 会将 render 的返回值(一般是虚拟 DOM,也可以是 DOM 或者 null)插入到页面中。

这个过程会暴露几个钩子(hook)方便你往里面加代码:

update

mount 之后,如果数据有任何变动,就会来到 update 过程,这个过程有 5 个钩子:

unmount

当一个组件将要从页面中移除时,会进入 unmount 过程,这个过程就一个钩子:

你可以在这个组件死之前做一些清理工作。

一般在下列钩子中应用setState():

事件绑定

明确了函数绑定时,写的是一个函数而不是函数运行的结果之后,继续明确第二个概念:

明确了上述2点之后,再来看react中的事件绑定。

先来看一个App组件:

import React from 'react'
import ReactDom from 'react-dom'
import './index.css'

const rootDom = document.querySelector('#root')

class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      isOn: true
    }
  }
  render () {
    return (
      //这里点击的时候的this已经不是当前组件了,所以要bind为当前组件,不然是没有testClick函数的
      <div onClick={this.testClick.bind(this)}> 
        <button>{this.state.isOn ? 'ON' : 'OFF' }</button>
      </div>
    )
  }
  testClick () {
    this.setState( (prevState) => { //setState()也能传函数作为参数,没什么好说的
      return {
        isOn: !prevState.isOn
      }
    })
  }
}
ReactDom.render(<App></App>,rootDom)

这么一看就能理解react中的事件绑定了。

import React from 'react'
import ReactDom from 'react-dom'
import './index.css'

const rootDom = document.querySelector('#root')

let App = () => {
  function preventClick (e) { // 注意这里的e,是react自己传过来的  本身没做任何操作
    e.preventDefault() //
    console.log('default click has been prevent')
  }
  return (
    <div>
      {/*上面已经说过,绑定事件只能写函数,不能写函数的运行结果,所以如果写了preventClick()就会报错*/}
      <a href="http://www.baidu.com" onClick={preventClick}>click me</a>
    </div>
  )
}
ReactDom.render(<App></App>,rootDom)

对于上述问题 又引申出了一个小问题,如果我想在阻止a标签的同时,事件处理函数还想接收一些其他的参数进行处理怎么写呢。

对于React,上面例子的e.preventDefault()中的e是React自己传过来的。我们如果既想写e,还想有自己规定的一些参数的话,要这么写:

let App = () => {
  function preventClick (str,e) { // 如果有其他自定义的参数,那么e永远是最后一项,而且同样也是React自己传过来的。
    e.preventDefault() //
    console.log(str)
  }
  return (
    <div>
      {/*上面已经说过,绑定事件只能写函数,不能写函数的运行结果,所以如果写了preventClick()就会报错*/}
      <a href="http://www.baidu.com" onClick={preventClick.bind(this,'testString')}>click me</a>
      {/*这里的preventClick.bind(this,'testString')同样也是一个函数,并不是函数运行结果*/}
    </div>
  )
}

上面的例子中,我们想在preventClick()中传一些自己用的参数,那么在点击的时候,就要bind到组件本身上,然后再传入想要的参数,注意e是不用我们自己写到参数中去的,回到函数本身的参数上,自定义的参数在前,e永远是最后一项。

摆脱React中操蛋的bind(this)的方法

上面的事件绑定中已经提到了bind,因为事件触发时的this已经不是当前组件本身了,所以我们要将处理函数的this重新设置为当前组件,所以有了随便一个函数后面都要bind(this)的情况。

先来看个例子:

class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render (){
    return (
      <div>
        <button onClick={this.testClick.bind(this)}>click me</button>
      </div>
    )
  }
  testClick() { // 普通函数在被调用的时候,就有了this,所以要加bind()到App组件上
    console.log(this) 
  }
}

上述例子中,testClick是App组件上的函数,点击时的this已经不是App组件了,所以不能正确调用。要将其写为this.testClick.bind(this)而不是this.testClick

为了摆脱上述这种烦人的写法,我们可以有如下几种方法:

  1. constructor中将当前函数重新赋值。
class App extends React.Component {
  constructor(props) {
    super(props)
    this.testClick = this.testClick.bind(this) //这的this是App组件,直接将testClick重新赋值一下
  }
  render (){
    return (
      <div>
        <button onClick={this.testClick}>click me</button>
      </div>
    )
  }
  testClick() { // 普通函数在被调用的时候,就有了this,所以要加bind()到App组件上
    console.log(this)
  }
}

我们在constructor中给testClick重新赋了一次值,testClick变为了一个运行上下文为App组件的函数,下面直接onClick={this.testClick}即可。

  1. public class fields syntax(不知道怎么翻译,就是利用箭头函数没有this的特性)
class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render (){
    return (
      <div>
        <button onClick={this.testClick}>click me</button>
      </div>
    )
  }
  testClick = () => { //箭头函数没有本身的this 所以定义的时候this已经确定为App组件了,所以不用bind
    console.log(this)
  }
}

我们利用箭头函数本身没有this的特性,在App中定义testClick的时候,testClick的this就已经确定为当前上下文App组件了。所以后面直接调用onClick={this.testClick}即可。

  1. 在回调中使用箭头函数
class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render (){
    return (
      <div>
        <button onClick={(e) => {return this.testClick(e)}}>click me</button>
      </div>
    )
  }
  testClick() { 
    console.log(this)
  }
}

每次渲染组件时,都会生成一个全新的回调。但在有时候将其作为prop传给子组件时,会引发一次额外的渲染。所以,建议用constructor 和public class fields syntax两种方式避免bind(this)这种写法。

值得一提的是:

上面说的想给事件处理函数传递另外自定义的参数时,绑定事件的时候是不用写e的,因为react是自动帮你把e作为最后一个参数传递的

<button onClick={this.testClick.bind(this, id)}>click me</button>

就像上述例子一样,我们想传一个额外的参数id,在id后面是不需要写e的,this.testClick.bind(this, id,e)这种是不用写的。

但是! 在我们提到的第3种方法,回调中使用函数的情况下,是需要开发者自己写上e的!

<button onClick={(e) => this.testClick(id, e)}>click me</button>

就像这样,自定义参数id的后面还要写上e。

详情参考文档:With an arrow function, we have to pass it explicitly, but with bind any further arguments are automatically forwarded.

文档地址

React中的ref

想直接修改原生DOM或者是组件的时候,可以使用ref。

  1. 原生DOM上
class App extends React.Component {
  constructor(props) {
    super(props)
  }
  render() {
    return (
      <div>
        {/*ref callback*/}
        <input type="text" ref={(input) => this.testRef = input} />
        <input type="button" onClick={this.focus.bind(this)} value="Click" />
      </div>
    )
  }
  focus() {
    this.testRef.focus();
  }
}

上述例子就是一个原生DOM上使用ref的例子,react在原生的input加载完成后,通过一个回调函数,(input) => this.testRef = input,这里的回调参数input就是底层的DOM,接受DOM作为参数,存到testRef中。引用的时候,直接就引用到了原生的DOM

  1. ref ====> class Component
// ref ===> class Component
class App extends React.Component {
  render() {
    return (
      <TestComponent ref={(testComponent) => this.classCom = testComponent}></TestComponent>
    )
  }
  componentDidMount () {
    console.log(this.classCom.state)
  }
}

class TestComponent extends React.Component {
  constructor() {
    super()
    this.state = {
      name: 'children Component'
    }
  }
  render() {
    return (
      <div>
        <input type="text"/>
      </div>
    )
  }
}

这里的ref回调函数中,(testComponent) => this.classCom = testComponent的参数为已经加载的 React 实例,我们可以在父组件中通过this.classCom访问到它。

  1. ref ====> functional Component

你不能在函数式组件上使用 ref 属性,因为它们没有实例,但是可以对其内部的原生DOM使用ref,参见第一条。

React中突变数据的处理

经常会遇到设置了state之后,进行setState()了之后,页面没有进行渲染的情况。(尤其是使用PureComponent的时候)。

这种情况经常是由于操作习惯不好造成了原数据结构的突变

    let arr = [1]
    arr.push(2)
    console.log(arr) //[1, 2]

我们有一个数组,进行了push操作之后,原先的数组的值改变了,由[1]变为了[1,2]

看下一个例子:

    let arr2 = [1]
    let arr3 = arr2.concat([2])
    console.log(arr2) //[1]
    console.log(arr3) //[1, 2]

同样是一个数组,进行了concat操作之后,原先的arr2的值并没有发生改变,我们把concat之后的结果赋给了一个新变量arr3来储存。

上面我们提到了push操作会改变原数据,这在React中是不应该的。

setState时,我们应该return的是一个新对象,而不是原对象。所以我们要避免那些会修改原数据的操作,例如push

比如说,我们在一个组件中通过push()修改了数组,然后有一个PureComponent子组件把这个数组作为props渲染页面,这时候虽然我们在父组件中修改了数组的值,但是setState()return的时候,原数据结构已经变了,这时候虽然本身的state已经改变了,但是传到子组件的props并没有改变。

为了避免这种情况发生,我们一般可以采用concat等不会引起原数据突变的操作。

如果非要用push的话,可以通过Object.assgin()或者...spread语法来进行一份数据的拷贝,在进行操作。

比如说上述的arr,我们可以这么操作:

    this.state = {
      arr: [1]
    };
//进行setState()
    this.setState( (prevState) => {
      return {
        arr: [...prevState.arr,2]
      }
    })

如果目标数据是对象的话,可以通过Object.assgin()来进行操作

上一篇下一篇

猜你喜欢

热点阅读