likes
comments
collection
share

从Vue到React的过渡之路,快速上手React。

作者站长头像
站长
· 阅读数 100

从Vue到React的过渡之路,快速上手React。 “我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

本篇也是自己记录从一个Vue开发者学习过渡到React的一些学习笔记。会简单对比下两大框架的写法,更适合Vue开发者过渡到React阅读食用,理解错误或者有偏差的地方也请大家指出。

React

1. 什么是React?

  1. React 是 Facebook 创建的一个开源项目,最受欢迎的库之一,在GitHub中超过了150000 Star
  2. 一个专注于构建用户界面的 JavaScript 库,和vue和angular并称前端三大框架,不夸张的说,react引领了很多新思想,世界范围内是最流行的js前端框架,最近发布了18版本,加入了很多新特性。
  3. React 是 MVC 应用程序的视图层(Model View Controller)

其特点就是:

  1. 声明式

React使用的(JSX语法),而 Vue 中将 HTML,CSS,JS 都分离了。所以很多初级前端开发者可以很好的学习 Vue,而学习 React 需要对 JavaScript 相对有一定要求。这也是更多新手前端开发者和初级前端开发者更热爱 Vue 的一个原因。

  1. 组件化

组件是react中最重要的内容,构建管理自身状态的封装组件,然后对其组合以构成复杂的 UI。

  1. 跨平台

react 既可以开发web应用也可以使用同样的语法开发原生应用(react-native),比如安卓和ios应用,react更像是一个 元框架 为各种领域赋能。

2. 开始

我们先打开命令行窗口安装一个React的项目,当然你也可以通过 Script 标签在线引入。

$ npx create-react-app react-demo // 创建项目

注意: 1. 这里的 npx create-react-app 是固定命令,create-react-app 是 React脚手架的名称。 2. react-demo 是项目名称,可以自定义,但是要注意一下语义化。

$ cd react-demo && npm start // 启动

启动完就得到了一个 localhost:3000 新窗口

从Vue到React的过渡之路,快速上手React。 接下来就让我们开始我们的 React 之旅,我们简单从JSX语法开始吧!

3. JSX语法

1. 表达式

    const name = '小明'
    // 用JSX方式创建React元素
    let H1 = <h1>Hello React,我是:{name} </h1>
    // 渲染React元素到页面中
    ReactDOM.render(H1,document.getElementById('app'))
    <template>
      <div>
        <h1>Hello React,我是:{{name}} </h1>
      </div>
    </template>

2. 条件渲染

    const isShow = true
    const state = () => {
      return isShow ? (<div> 成功! </div>) :  (<div> 失败! </div>)
    }
    const result = (
      { state() }
    )
    <template>
      <div>
        <div v-if="isShow"> 成功! </div>
        <div v-else> 失败! </div>
      </div>
    </template>

3. 列表渲染

    const musicList = [
      {name: '我是如此的相信' ,id: 1},
      {name: '烟灰易冷' ,id: 2},
      {name: '你算什么男人' ,id: 3},
      {name: '本草纲目' ,id: 4}
    ]
    const playMusic = (
        // 这里的 Key 值也是必须要写的 有 id 可以用 id 没有的话可以用 index 下标
        // Key 在 HTML 结构中是看不到的,是 React 内部用来进行性能优化时使用
       <ul>
          { musicList.map(item => <li key={item.id} >{ item.name }</li>) }
       </ul>
    )
    <template>
      <div>
        <ul>
          <li v-for="item in musicList" :key="item.id">{{item.name}}</li>
        </ul>
      </div>
    </template>

4. 样式处理

    // 行内样式
    function App() {
      return (
       <div style={{color: 'red'}}> 雨纷纷,旧故里草木深。 </div>
      )
    }
    or
    const styleData = {
        color: red,
        fontSize: 18px
    }
    function App() {
      return (
       <div style={ styleData }> 雨纷纷,旧故里草木深。</div>
      )
    }
    // 类样式
    const showStyle = true
    // css
    .title {
      color: red,
      font-size: 18px
    }
    function App() {
      return (
      // 在这里你会注意到我们 class 的写法是 className 而不是 calss 
      // 我们需要知道我们编写的其实就是 JavaScript ,而不是真正的 HTML 
       <div className={ showStyle ? title : '' } > 我听闻 你始终一个人。 </div>
      )
    }
    
    export default App
    

4. 组件

1. 类组件

提起类 我们先来回顾一下 es6 的基本语法吧,我平常是基本没用到的 ^_^。

    // js 
    function Point(x, y) {
      this.x = x;
      this.y = y;
    }
    Point.prototype.toString = function () {
      return '(' + this.x + ', ' + this.y + ')';
    };

    var p = new Point(1, 2);
    // 上面这种写法跟传统的面向对象语言(比如 C++ 和 Java)差异很大,很容易让新学习这门语言的程序员感到困惑。
    // ES6 提供了更接近传统语言的写法,引入了 Class(类)这个概念,作为对象的模板。通过`class`关键字,可以定义类。
    // 下面的代码用 ES6 的 class 改写的
    class Point {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
      toString() {
        return '(' + this.x + ', ' + this.y + ')';
      }
    }

这是ES6入门的解释 对类有兴趣的可以去详细看一看 其实没必要过多余纠结 类组件,下面主要会围绕函数组件来说。经过多年的实战,函数组件是一个更加匹配React的设计理念 UI = f(data),也更有利于逻辑拆分与重用的组件表达形式,而先前的函数组件是不可以有自己的状态的,为了能让函数组件可以拥有自己的状态,所以从react v16.8开始,Hooks的诞生解决了我们的一系列问题,后续我们会讲到Hooks

    // 引入React
    import React from 'react'

    // 定义类组件
    class MusicCom extends React.Component {
      render () {
        return <div>缘份落地生根是我们!</div>
      }
    }

    function App () {
      return (
        <div className="App">
          // 渲染类组件
          <MusicCom />
          <MusicCom></MusicCom>
        </div>
      )
    }
    export default App
    // MusicCom.vue 组件
    <template>
        <div>缘份落地生根是我们!</div>
    </template>
    
    // App.vue
    <template>
      <div class="App">
        <MusicCom />
        <MusicCom></MusicCom>
      </div>
    </template>
    

2. 函数组件

    // 定义组件最简单的方式就是编写 JavaScript 函数
    function MusicCom () {
      return <div>我听闻 你始终一个人!</div>
    }
    
    function App () {
      return (
        <div className="App">
          // 渲染函数组件
          <MusicCom />
          <MusicCom></MusicCom>
        </div>
      )
    }
    
    export default App
    // MusicCom.vue 组件
    <template>
        <div>我听闻 你始终一个人!</div>
    </template>
    
    // App.vue
    <template>
      <div class="App">
        <MusicCom />
        <MusicCom></MusicCom>
      </div>
    </template>
    

3. 函数组件的事件

  1. on + 事件名称 = { 事件处理函数 } ,比如:<div onClick={ onChange }></div>
  2. react事件采用驼峰命名法,比如:onMouseEnter、onFocus
    // 基础事件
    function HelloCom () {
      const onChange = () => {
        console.log('我被点击了')
      }
      return (
        <button onClick={onChange}>点击</button>
      )
    }
    // 事件对象
     function HelloCom () {
     // 滚轮事件的相关参数
      const handleScroll = (e) => {
        console.log(e)
      }
      return (
        <button onWheel={handleScroll}>点击</button>
      )
    }
    // 额外参数
    function HelloCom () {
      const musicList = [
      {name: '我是如此的相信' ,id: 1},
      {name: '烟灰易冷' ,id: 2},
      {name: '你算什么男人' ,id: 3},
      {name: '本草纲目' ,id: 4}
    ]
      const onChange = (val) => {
        console.log(val) // 点击第一个 => "我是如此的相信"
      }
      //  注意: 一定不要在模板中直接写函数调用 onClick={() => onChange(item.name) } 错误!!!
      return (
         <ul>
          { musicList.map(item => <li key={item.id} onClick={() => onChange(item.name) } >{ item.name }</li>) }
         </ul>
      )
    }
    

4. 组件状态

Vue中我们对于我们的数据是有响应式的,所以我们在更改数据的时候,可以直接更改。 而在React中我们需要特定的语法去修改数据,让数据驱动视图,页面自动刷新。在React hook出来之前,函数式组件是没有自己的状态的,所以我们通过类组件来讲解说明。

从Vue到React的过渡之路,快速上手React。

  1. 初始化状态 通过class的实例属性state来初始化值,这是对象结构,可以拥有多个数据状态。
    class Counter extends React.Component {
      state = {
        count: 0
      }
      render() {
        return <button>计数器</button>
      }
    }
  1. 读取状态通过this.state读取
    class Counter extends React.Component {
      state = {
        count: 0
      }
      render() {
        return <button>计数器{this.state.count}</button>
      }
    }
  1. 修改状态 this.setState 修改状态,更新视图。
    class Counter extends React.Component {
      state = {
        count: 0
      }
      // 定义修改数据的方法
      setCount = () => {
        this.setState({
          count: this.state.count + 1
        })
      }
      // 读取数据 并且绑定事件调用方法修改
      render () {
        return <button onClick={this.setCount}>{this.state.count}</button>
      }
    }

注意: 这里不能直接state中的值,必须通过setState方法进行修改

5. 组件通讯

组件通讯想必这个大家都很清楚了,日常中我们的组件通讯大致上分为三种: 1. 父传子 2. 子传父 3. 兄弟组件传值。 在Vue中使用的 props $emit 父传子 子传父 大致上和React是差不多的。

1. 父传子

    // 子组件通过 props 接收
    function Son(props) {
      console.log(props)
      return (
        <div>
          {props.msg}
        </div>
      )
    }
    // 父组件
    class App extends React.Component {
      state = {
        message: '听妈妈的话 别让她受伤'
      }
      render() {
        return (
          <div>
            <div>父组件</div>
            <Son msg={this.state.message} />
          </div>
        )
      }
    }

2. 子传父

// 子组件
    function Son(props) {
      function handleClick() {
        // 调用父组件传递过来的回调函数 并传入参数
        props.changeMsg('想快快长大 才能保护她')
      }
      return (
        <div>
          {props.msg}
          <button onClick={handleClick}>点击</button>
        </div>
      )
    }
    
    class App extends React.Component {
      state = {
        message: '听妈妈的话 别让她受伤'
      }
      // 提供回调函数
      changeMessage = (newMsg) => {
        // 子组件传过来的数据 newMsg
        this.setState({
          message: newMsg
        })
      }
      render() {
        return (
          <div>
            <div>父组件</div>
            <Son
              msg={this.state.message}
              changeMsg={this.changeMessage}
            />
          </div>
        )
      }
    }

3. 兄弟组件传值

从Vue到React的过渡之路,快速上手React。

这里我们可以通过上面的思路去完成从A组件到B组件的传值通讯 第一步 SonA 通过子传父 传值给 APP 第二步 APP 拿到SonA的值,然后通过父传子把值传递给SonB这样整个通讯就完成了。 这里这种需求传值相信大家在项目中也不会这样写,没有观赏性 可读性,后面还会有更好的方法解决这个传值,我们就简单带过,哈哈哈。

4. 跨组件通信Context

从Vue到React的过渡之路,快速上手React。 在我们真实项目中,往往就会有这种嵌套的组件存在。如果通过父传子一层层的传下去,显然很繁琐,让人很头痛。 Context 就为我们提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。 现在我们就通过ContextAPP里的数据,让SonB SonC拿到。

    // 1. 首先创建一个Context对象 导出 Provider 和 Consumer对象
     /**
     * @Provider Provider为其包裹的组件提供数据
     * @Consumer Consumer包裹的组件可以拿到Provider提供的值
     */
    const { Provider, Consumer } = createContext()
    // 使用数据
     function SonB() {
      return (
        <Consumer >
          {value => <div>{value}</div>}  // 从前从前有个人爱你很久,但偏偏风渐渐把距离吹得好远。
        </Consumer>
      )
    }
    function SonC() {
      return (
        <Consumer >
          {value => <div>{value}</div>} // 从前从前有个人爱你很久,但偏偏风渐渐把距离吹得好远。
        </Consumer>
      )
    }
    function SonA() {
      return (
        <SonB/>
        <SonC/>
      )
    }
    //  提供数据
    class App extends React.Component {
      state = {
        message: '从前从前有个人爱你很久,但偏偏风渐渐把距离吹得好远。'
      }
      render() {
        return (
          <Provider value={this.state.message}>
            <div className="app">
              <ComA />
            </div>
          </Provider>
        )
      }
    }
    

6. 生命周期

从Vue到React的过渡之路,快速上手React。 在 React 中,每一次的状态改变都会导致页面视图的改变,都会经历两个阶段:render 阶段commit 阶段。 class 组件实例的所有生命周期函数,都会在 render 阶段和 commit 阶段执行。

注意,只有类组件才有生命周期(类组件 实例化  函数组件 不需要实例化)。组件实例从被创建到被销毁的过程称为 组件的生命周期。

1. 挂载阶段

钩子函数触发作用
constructor创建组件时,初始化的时候只执行一次1. 初始化state  2. 创建 Ref 3. 使用 bind 解决 this 指向问题等
render每次组件渲染都会触发渲染UI(注意: 不能在里面调用setState()
componentDidMount组件挂载后执行,初始化的时候执行一次发送网络请求 ,DOM操作

2. 更新阶段

钩子函数触发作用
render每次组件渲染都会触发渲染UI(与 挂载阶段 是同一个render)
componentDidUpdate组件更新后(DOM渲染完毕)DOM操作,可以获取到更新后的DOM内容,不要直接调用setState

3. 卸载阶段

钩子函数触发作用
componentWillUnmount组件卸载卸载时执行清理工作

7. Hooks

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。一套能够使函数组件更强大,更灵活的 "钩子"。

注意点:

  1. 有了hooks之后,不能在把函数成为无状态组件了,因为hooks为函数组件提供了状态
  2. hooks只能在函数组件中使用
  3. 有了hooks之后,为了兼容老版本,class类组件并没有被移除,俩者都可以使用,下面是官方的解释。

从Vue到React的过渡之路,快速上手React。

1. useState

    import { useState } from 'react'
    function App() {
      // 参数:状态初始值比如,传入 0 表示该状态的初始值为 0
      // 返回值:数组,包含两个值:1 状态值(state) 2 修改该状态的函数(setState)名字可以任意起,注意语义化
      // `useState` 函数可以执行多次,每次执行互相独立,每调用一次为函数组件提供一个状态。<br/>
      const [count, setCount] = useState(0)
       // setCount是一个函数,参数表示`最新的状态值`
       // 调用该函数后,将使用新值替换旧值
       // 修改状态后,由于状态发生变化,会引起视图变化
      return (
      //  这里我们每次点击都会让 count + 1
        <button onClick={() => { setCount(count + 1) }}>{count}</button>
      )
    }
    export default App

注意:

  1. 只能出现在函数组件或者其他hook函数中
  2. 不能嵌套在if/for其它函数中(react按照hooks的调用顺序识别每一个hook

useState 里的回调函数的参数

      // 语法
    const [name, setName] = useState(()=>{   
      // 计算逻辑代码    
      // return '计算之后的name初始值'
    })
    
    // 例子
    function Son(props) {
      const [count, setCount] = useState(() => {
        // 初始化的值是父组件传来的 10
        return props.count
      })
      return (
        <div>
          <button onClick={() => setCount(count + 1)}>{count}</button>
        </div>
      )
    }

    function App() {
      return (
       // <></>幽灵节点
        <>
          <Son count={10} />
        </>
      )
    }

2. useEffect

    // useEffect 即 副作用
    // 副作用是相对于主作用来说的,一个函数除了主作用,其他的作用就是副作用。
    // 对于 React 组件来说,主作用就是根据数据(state/props)渲染 UI,除此之外都是副作用(比如,手动修改 DOM,ajax请求)
    import { useEffect, useState } from 'react'
    function App() {
      const [count, setCount] = useState(0)
      // 初始化的时候会执行
      useEffect(()=>{
        console.log(`当前已点击了${count}次`);
      })
      return (
        <button onClick={() => { setCount(count + 1) }}>{count}</button>
      )
    }

    export default App

3. 依赖项执行时机

在我们没有对依赖项做任何处理的时候,组件首次渲染会执行一次,以及不管是哪个状态更改引起组件更新时都会重新执行。

1. 没有添加依赖项

    useEffect(()=>{
        // 首次渲染以及状态更改引起组件更新时都会重新执行
        console.log('副作用执行')
    })

2. 添加空数组

    useEffect(()=>{
        // 只在首次渲染时执行一次
        console.log('副作用执行')
    },[])

3. 添加特定依赖项

    function App() {  
    const [count, setCount] = useState(0)  
    const [name, setName] = useState('杰伦') 
    
    useEffect(() => {    
        // name 变化时是不会执行的
        console.log('副作用函数在首次渲染时执行,仅在依赖项count发生变化时重新执行')  
    }, [count])  
    
    return (    
        <>      
         <button onClick={() => { setCount(count + 1) }}>{count}</button>      
         <button onClick={() => { setName('周董') }}>{name}</button>    
        </>  
    )
}

需要注意的是: 我们使用 create-react-app创建的项目,开发的时候 useEffect初始化会调用两次,这里不用太关心。它是因为React.StrictMode严格模式导致的,打包后就会正常了。如果只在介意,可以在index.js 删除,改成root.render(<App />)

4. 清理副作用

先看代码

    const App = () => {
      const [count, setCount] = useState(0)
      useEffect(() => {
        const timerId = setInterval(() => {
          setCount(count + 1)
        }, 1000)
         return () => {
           // 用来清理副作用的事情
           clearInterval(timerId)
         }
      }, [count])
      return (
        <div>
          {count}
        </div>
      )
    }

当我们快速点击时,就会出现上一个 setInterval 还没有结束的情况,这个时候我们就用到了我们的useEffect 副作用。

在副作用函数中的末尾return一个新的函数,在新的函数中编写清理副作用的逻辑。 它的执行机制:

  1. 组件卸载时自动执行
  2. 组件更新时,下一个useEffect副作用函数执行之前自动执行

4. 发送请求

    // bad
    useEffect(async ()=>{  
    // 不可以直接在useEffect的回调函数外层直接包裹 await ,因为异步会导致清理函数无法立即返回
        const res = await axios.get('api/getUserInfo')   
        console.log(res)
    },[])
    // good
    useEffect(()=>{   
        async function getUser(){      
           const res = await axios.get(''api/getUserInfo'')     
           console.log(res)   
        } 
    },[])
    

5. useRef

使用useRef获取真实dom或组件实例的方法

    function App() {  
        const h1Ref = useRef(null)  
        useEffect(() => {    
            console.log(h1Ref)  
        },[])  
        return (    
            <div>      
                <h1 ref={ h1Ref }>this is h1</h1>    
            </div>  
        )
    }

这里需要注意: 我们日常中需要通过Ref获取组件的实例,但是只可以去获取类组件的实例,因为函数组件没有实例,不能使用Ref获取。类组件的写法,真的很头疼。这个时候官方为我们提供了 forwardRef useImperativeHandle 解决了useRef获取函数组件的问题。

这里开发者需要注意官方的提示:forwardRef 是一个破坏性的更改,可能会引起依赖旧行为的应用和其他库崩溃!

从Vue到React的过渡之路,快速上手React。

    import React,{useRef,forwardRef} from 'react' 
    // 子组件
    const Son = forwardRef((props, ref) => {
    // useImperativeHandle,将 ref 和 方法(此处是onChange)传给父组件
      useImperativeHandle(ref, () => {
          return {
            onChange: () => {
              console.log('测试');
            }
          }
        },[])
    
      return (
          <>
              <div>
                  <input type="text" defaultValue={props.value} ref={ref} />
                  <button onClick={() => console.log(ref.current)}>点击打印ref</button>
              </div>
          </>
  
      )
  })
    // 父组件
    const App = () => {
      const sonRef = useRef(null)
      useEffect(()=>{
            sonRef.current.onChange()  ----测试
      },[])
      
      return (
          <>
              <Son ref={sonRef} value='给子组件的value值' />
          </>
      )
    }
    

6. useContext

上面组件通讯时我们讲到context,这里说一下hooks下的context使用方式。

    import { createContext, useContext } from 'react'
      // 创建Context对象
    const Context = createContext()

    function SonA() {  
        return <div>SonA <SonB/> </div>
    }

    function SonB() {  
        // 底层组件通过useContext函数获取数据  
        const value = useContext(Context)  
        return <div>SonB {value}</div>
    }

    function App() {  
        return (    
         // 顶层组件通过Provider提供数据    
            <Context.Provider value={'给SonB的数据'}>     
                <>
                 <SonA/>
                </>    
            </Context.Provider>  
        )
    }

看到这里我们的React的基础就到这里了,对比起来Vue的同学们,其实学习成本并不是很大。 我们从学习它的语法在到它的思想,需要一个循环渐进的过程。可以从GitHub上拉一些React项目,或者自己试着拉一条新分支,改造现有的Vue项目。 两个框架之间有很多相似的地方,各有千秋。不能说谁比谁好,两大框架都很优秀。 性能对比上看过krausest的框架对比测试,Vue3.2是要略高于React18.2的,但是呢这个也要取决最终的前端项目 前端工程化 开发人员的优化 还有整体业务和项目大小。所以这些我们都可以忽略不计,都是很优秀的框架。 不过大多数大公司都是有React要求的,Vue的岗位虽然更多一些,但是React的薪资开的普遍要高于Vue一些的。后续的话还会接着更新 记录我的React的过渡之路。

建议大家在熟悉Vue的时候,同时也要掌握React。本文是自己从Vue开发者到React开发者的一个转变过程,真心觉得React的一些思想很不错,写起来的感觉也挺好的。写下本文,希望能帮助到Vue的开发者快速上手React。谢谢大家!

转载自:https://juejin.cn/post/7143134220624822279
评论
请登录