JSX

一. 引入方法

  1. CDN引入
  2. create-react-app

    $ yarn global add create-react-app
    $ create-react-app demo
    $ cd demo
    $ yarn start

二. 语法
const element = <div> hello world </div>
三. 注意事项

  1. className而不是class
    <div className="red"></div>
  2. 插入变量
    标签里所有的变量都要用{}包起来

    • 如果需要变量n 写成{n}
    • 如果需要对象,写成{ { name:"oliver" } }
  3. return 后边一定要加()

    const App = ()=>{
       return(
           <div>我是App</div>
       )
    }
    //不写括号会报错,相当于return undefined;

四. 条件判断

  • React的JSX完全**就是JS的写法!

如果你想省事儿,可以这样写

const App = ()=>{
   return (
       <div>
           { n%2==0? <div>我是偶数</div> : <div>我是奇数</div>}
       </div>
   )
}

如果你想有条理点,可以这样写

const App = ()=>{
   const el = n%2==?:<div>我是偶数</div> : <div>我是奇数</div>
   return(
       <div> {el} </div>
   )
}

如果你想多分支判断,可以这样写

const App = ()=>{
   const el
   if(n==1){ el = <div>第一名</div> }
   else if(n==2){ el = <div>第二名</div> }
   else if(n==3){ el = <div>第三名</div> }
   return (
       <div> {el} </div>
   )
}

五. 循环语句
1.for循环

 const App = (props)=>{
    const arr = []
    for(let i=0; i<props.list.length; i++){
        arr.push(<div> 下标为{i},值为{props.list[i]} </div>)
    }
    return (
        <div> {arr} </div>
    )
 }

2.同理for循环可以改为

props.list.map((n,item)=>{
   return  <div>下标{index},值为{n}</div>
})

3.双重循环

render(){
    return(
        listA.map((val.idx)=>{
            <div>
                {
                    listB.map((value,index)=>{
                        return <button />
                    })
                }
            </div>
        })
    )
}

html标签后的代码要写在{}

Vue 和 React 最大的区别就是:

  • 你愿意用Vue给你写好的 v-xxx
  • 还是愿意用React自己写JS代码

React组件

一. Element V.S Component

//这是一个react元素(默认小写开头
const div = <div className="el"> element </div>
//这是一个react组件(默认大写开头
const Div = ()=>{
    return <div className="cpn"> component </div>
}

二. 什么是组件

  • 就目前而言,一个返回react元素的函数,就是组件

三. React两种组件
(一)函数组件

//声明方法
function Welcome(props){
    return <h1>Hello, {props.name}</h1>
}
//使用方法
<Welcome name="Oliver"/>

(二)class(类)组件

//声明方法
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>
  }
}
//使用方法
<Welcome name="Oliver"/>

注意,函数组件没有this,一律使用变量和参数
四. 添加props(外部数据)

  1. 类组件

    <Demo name="Oliver"/>
    ...
    class Demo extends React.component{
       render(
           <div> 名字:{this.props.name} </div>
       )
    }

    直接读取属性 this.props.xxx

  2. 函数组件

     <Demo name="Oliver"/>
    ...
    const Demo = (props)=>{
       return <div> 名字:{props.name} </div>
    }
  3. 子组件改父组件的值:需要父组件同时传入父组件的值,和修改对应值得函数。

    //父组件
    <Children counter={number} countFn={add}/>
    const add = ()=>{ setNumber(number+1) }
    //子组件
    props:['counter','add']
    ...
    <div>{counter}</div>
    <button onClick={add}>+1<button/>

    直接读取参数 props.xxx

五. 添加state(内部数据)

  1. 类组件

    • 读:this.state.xxx
    • 写:

        //方法1
      this.setState({
            n: this.state.n+1
      })
        //方法2 推荐!!
      this.setState((state)=>{
            const n = this.state.n+1
            return {n}
      })
      • setState注意事项:

        1. this.setState.n += 1 无效
        2. setState会异步更新UI, n的值不会立刻改变。推荐setState((state)=>{return{...}})函数方式
        3. 这是一种函数式理念
  2. 函数组件
    使用了React Hook的API
    用useState返回数组,第一项用来读,第二项用来写

    const [n, setN] = useState(0);
    ...
    return (
       <div>{n}</div>
       <button onClick={()=>setN(n+1)}>+1</button>
    
    )
    ...
  3. 复杂数据的state

    • 类组件的setState会自动合并第一层的属性

      this.state={ m:0, n:0 }
      ...
      this.setState( {n:0} )  //会自动将m的值合并,而不会初始化m为undefined
    • 类组件的第二层要用 "..."

      this.state={
        user:{
            name:"oliver", age:18
        }
      }
      ...
        //修改年龄
      this.setState({
        user:{
            ...this.state.user,  //将原始数据都拷贝过来
            age: 19
        }
      })
    • 函数组件完全不会帮你合并,数据只有一层时分别操作:

      let [n,setN] = React.useState(0)
      let [m,setM] = React.useState(0)
      ...
      <button onClick={()=>setN(n+1)}></button>
      <button onClick={()=>setM(m+1)}></button>
    • 函数组件有两层时:

      1. 用"..."
      2. 深拷贝 let obj1 = JSON.parse(JSON.stringify(obj)),然后再进行修改传值
      let [myState,setMyState] = useState({
            name: "oliver",
            age: 18
      })
        //修改年龄
      setMyState({
            ...myState,
            age: 19
      })

事件绑定

  1. 类组件的事件绑定

    class App extends React.component{
           //绑定addN事件,并挂载到实例上
       addN = ()=> this.setState({n:this.state.n+1})
       constructor(){
           super()
       }
       ...
       render(){
           return (
               <button onClick={this.addN}>add</button>
           )
       }
    }

    或者

    class App extends React.component{
       constructor(){
           super()
       }
       addN(){
           this.setState({n:this.state.n+1})
       }
       ...
       render(){
           return (
               <button onClick={this.addN.bind(this)}>add</button>
           )
       }
    }
  2. 函数组件的事件绑定

    const App = ()=>{
       sayHi = ()=> { console.log('hi') }
       return (
           <button onClick={sayHi}></button>
       )
    }

createElement

  • 第一个参数:

    1. 字符串,如'div',则创建一个<div></div>
    2. 函数(函数组件),则调用该函数,取得返回值作为第一个参数值
    3. 类(类组件),则new一个该类的实例,并且调用实例上的render()方法取得返回值作为第一个参数。
  • JSX语法里的<div></div> 等价于 React.createElement('div')

声明周期

建议使用的生命周期

class Component extends React.Component {

  //组件实例化后执行
  constructor(){}
    
  // 初始化和 update 时被调用
  // 静态函数,无法使用 this。
  // 一般用于根据props修改state
  static getDerivedStateFromProps(nextProps, prevState) {}
  
  // 判断是否需要更新组件
  // 可以用于组件性能优化
  shouldComponentUpdate(nextProps, nextState) {}
  
  //根据vnode生成dom
  render()
  
  // 组件被挂
  载后触发
  componentDidMount() {}
  
  // 替换 componentWillUpdate
  // 可以在更新之前获取最新 dom 数据
  getSnapshotBeforeUpdate() {}
  
  // 组件更新后调用
  componentDidUpdate() {}
  
  // 组件即将销毁
  componentWillUnmount() {}
  
  // 组件已销毁
  componentDidUnMount() {}
}

挂载

  • constructor

    • 即组件的 实例化时机,通常可以用来设置初始化 state
  • static getDerivedStateFromProps(nextProps, prevState)

    • 在组件的模板渲染中,我们通常使用的数据为 stateprops,而 props 由父级传入,组件本身并无法直接修改,因此唯一的常见需求就是: 根据父级传入的 props 动态修改 state。该生命周期就是为此而生;
    • 大家可能会有疑问: 该方法为什么为静态方法? 而不是常规的实例方法呢?

      • 先肯定一点: 使用实例方法肯定也是能满足需求的。但这个钩子比较特殊,它的执行时机是位于 新状态合并之后,重渲染之前,而且该方法会 侵入更新机制 中。如果在之中做例如修改状态之类的操作是十分不可控的。当设计为静态方法后,函数内部便无法访问组件实例,成为一个 纯函数,便能保证更新流程的安全与稳定。
  • render()

    • 根据 stateprops,生成 虚拟DOM
  • componentDidMount()

    • 组件被创建成真实元素并渲染后 被调用,此时可获取真实的元素状态,主要用于业务逻辑的执行,例如数据请求,事件绑定等;

更新

  • static getDerivedStateFromProps(nextProps, prevState)
  • shouldComponentUpdate(nextProps, nextState)

    • 上篇文章中的 diff 优化策略中有提到: 为了减少 无谓的更新消耗,赋予组件一个可以 主动中断更新流API。根据参数中的 更新属性更新状态,业务方自行判断是否需要继续往下执行 diff,从而能有效地提升 更新性能
    • 大家记得 React 中有种组件叫 纯组件(PureComponent) 吧,其实这个类继承于普通的 Component 上封装的,可以减少多余的 render,提升性能。

      • 默认使用 shouldComponentUpdate 函数设定更新条件: 仅当 propsstate 发生改变时,才会触发更新。这里使用了 Object 浅层比对,也就是仅做第一层比对,即 1. key 是否完全匹配;2. value 是否全等; 所以如果需要超过一层的数据变动,纯组件即无法正确更新了;
      • 这也是为什么 React 提倡使用 不变数据 的原理,能有效地使用浅层比对;
      • 不变数据: 提倡数据不可变,任何修改都需要返回一个新的对象,不能直接修改原对象,这样能有效提高比对效率,减少无谓性能损耗。
  • render()
  • getSnapshotBeforeUpdate(prevProps, prevState)

    • 替换旧版的 componentWillUpdate,触发时机点: 在数据状态已更新,最新 VNode 已生成,但 真实元素还未被更新
    • 可以用来在 更新之前 从真实元素或状态中获取计算一些信息,便于在更新后使用;
  • componentDidUpdate(prevProps, prevState, snapshot)

    • 组件更新完成后调用;
    • 可以用于 监听数据变化,使用 setState 时必须加条件,避免无限循环;

卸载

componentWillUnmount()

  • 组件即将被销毁。通常可以用于 解绑事件清除数据释放内存 等功能

Oliver
76 声望13 粉丝

Slow Done, Achieve More.