React 转 Vue2.0 转 Vue3.0 不完全指东

共 24535字,需浏览 50分钟

 ·

2021-05-22 23:43

点击上方 前端瓶子君,关注公众号

回复算法,加入前端编程面试算法每日一题群

来源:_落魄前端_

https://juejin.cn/post/6960245513509699614

  • 前几天看到了 Vue 转 React不完全指北这篇文章@俊劫,感觉挺有意思,突然想到自己以前写taro+react+ts,而现在刚好公司使用的是vue+ts,于是本文就开始了。

vue 与react 的对比

这个太长就不说了,各位看官老爷自己看哈Vue 官方对比 React , 个人的理解 ->https://cn.vuejs.org/v2/guide/comparison.html

相同点

  1. 都使用Virtual DOM 进行页面渲染
  2. 都提供了响应式和组件化的视图组件
  3. 集中在UI层面,类似于路由,状态管理,数据请求都交给其他库管理

不同点

  1. vue 是采用单文件组件的方式进行开发,也就是把传统的web 页面 html,css.javascript 全部放到一个文件,而react 是采用 jsx 的方式(JSX是一种JavaScript的语法扩展,运用于React架构中,其格式比较像是模版语言)React的作者认为视图表现,数据和逻辑天然就是耦合的,所以没必要把它们分开,所以React创造了JSX。

  2. diff 算法不同,vue Diff使用双向链表,一边对比,一边更新DOM,react 主要通过diff队列保存哪些DOM需要更新,得到patch树,最后再统一操作批量更新DOM

  3. 数据绑定方式不同,vue 采用 v-model来实现 react 采用事件 onChange 来更新数据

  4. 有的人认为 vue 适合小型项目,react适合大型项目,说法都不一样,而个人认为其实都差不多,没有确切的说法,主要还是看公司或者团队的技术栈方向。从react 转到vue也确实发现vue 上手比较快,options 的写法一把梭,而对于小白来说react的写法可能就相对复杂一点。

生命周期

Vue2.0

点击可查看

Vue3.0

vue3.0

React

点击链接

常用的生命周期

  1. Vue2.0: activated / deactivated / created / mounted / beforeDestroy /destroyed

  2. Vue3.0: onMounted / onBeforeUnmount / onUnmounted / onRenderTracked/ onRenderTriggered

  3. React: componentDidMount/componentDidUpdate/componentWillUnmount/componentWillReceiveProps shouldComponentUpdate(主要做性能优化)

  4. React hooks:

    useEffect(() => {
      console.log('进入页面')
     return () => {
       console.log('离开页面')
     }
   },[])
复制代码

顺便在这里 推荐一个阿里出品的 hooks库 ahooks 用起来简直不要太爽,当然也可以自己写,哈哈

组件传值

  1. vue2.0: props / emit/ provide/inject/attrs/attrs/attrs/listeners/ event bus / vuex 等

  2. vue3.0: props / emit/ provide/inject / vuex 等

举个栗子

子组件


<script> 
export default {
    name'son',
    setup(_, ctx) {
        const handleClick = () => {
            ctx.emit('onFunc', {msg'我是子组件传来的值'});
        };
        return {
           handleClick
        };
    },
};
</script>

复制代码

父组件

<template>
    <testComponents @onFunc="onFunc" />
</template>
<script lang="ts">
import { defineComponent } from "vue";
import testComponents from "@/
components/test.vue";
export default defineComponent({
  name:'father',
  components: {
    testComponents
  },
  setup() {
    const onFunc = params => {
      console.log(params.msg); // 我是子组件传来的值
    };
    return { onFunc };
  }
});
</script>

复制代码
  1. React props/props.function/context/redux/mobx/rematch 等

组件嵌套

Vue2.0: slot/v-slot/slot-scope

<template>
    <div id="example">
        <h1>我是父组件的标题</h1>
        <app-layout>
          <h1 slot="header">(具名插槽)这里是一个页面标题</h1>
          <p>主要内容的一个段落。</p>
          <p slot="footer">(具名插槽)这里有一些信息</p>
        </app-layout>
    </div>

</template>
<script>
    export default{
        components:{
            'app-layout': {
                template: `
                    <div class="container">
                      <header>
                        <slot name="header"></
slot>
                      </header>
                      <main>
                        <slot></
slot>
                      </main>
                      <footer>
                        <slot name="footer"></
slot>
                      </footer>
                    </
div>
                `,
            }
        }
    }
</script>
复制代码

Vue3.0: slot/v-slot/#xxx/

<template>
  <app-layout>
    <template v-slot:header="slotProps"> 我是 {{ slotProps.data }} </template>
    <template v-slot:default>我是默认插槽</template>
    <template v-slot:footer>我是 footer</template>
    <!-- 动态插槽名 -->
    <template v-slot:[dynamicSlotName]>
      我是 Copyright xxx有限公司版权所有
    </template>
  </app-layout>

  <p>----------另外一种写法--------------------------</p>
  <app-layout>
    <template #header="{ data }"> 我是 {{ data }}</template>
    <template #default>我是默认插槽</template>
    <template #footer>我是 footer</template>
  </app-layout>

</template>

<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
  name: "app-slot",
  components: {
    "app-layout": {
      template: `
        <div class="container">
          <header >
            <slot name="header" :data='msg' /
>
          </header>
          <main>
            <slot/
>
          </main>
          <footer>
            <slot name="footer" /
>
          </footer>
          <p>
            <slot name="Copyright" /
>
          </p>
        </
div>
      `,
      data() {
        return {
          msg: "header"
        };
      }
    }
  },
  data() {
    return {
      dynamicSlotName: "Copyright"
    };
  }
});
</script>

复制代码

React


import React from 'react'

const Main = (props) => {
  return(
    <div>
      <header>{props.header}</header>
      <main>{props.children}</main>
      <footer>{props.footer}</footer>
    </div>

  )
}

const Layout = () => {
  const Header = <div>header</div>
  const Footer = <div>footer</div>
  return(
    <div className='main'>
      <Main 
        header={ Header }
        footer={ Footer }
        >

        <div> main </div>
      </Main>
    </div>

  )
}

export default Layout;

复制代码

组件传送门

Vue2.0: 暂不支持,但是有插件啊

插件在这里 portal-vue:https://portal-vue.linusb.org/api/portal-target.html

Vue3.0: 自带传送大法

附上地址 Teleport:https://v3.cn.vuejs.org/guide/teleport.html

app.component('modal-button', {
  template`
    <button @click="modalOpen = true">
        Open full screen modal! (With teleport!)
    </button>

    <teleport to="body">
      <div v-if="modalOpen" class="modal">
        <div>
          I'm a teleported modal! 
          (My parent is "body")
          <button @click="modalOpen = false">
            Close
          </button>
        </div>
      </div>
    </teleport>
  `
,
  data() {
    return { 
      modalOpenfalse
    }
  }
})
复制代码

React: createPortal

<html>
  <body>
    <div id="app-root"></div>
    <div id="modal-root"></div>
  </body>
</html>
复制代码
const appRoot = document.getElementById('app-root');
const modalRoot = document.getElementById('modal-root');

class Modal extends React.Component {
  constructor(props) {
    super(props);
    this.el = document.createElement('div');
  }

  componentDidMount() {
    modalRoot.appendChild(this.el);
  }

  componentWillUnmount() {
    modalRoot.removeChild(this.el);
  }

  render() {
    return ReactDOM.createPortal(
      this.props.children,
      this.el
    );
  }
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {clicks0};
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      clicks: state.clicks + 1
    }));
  }

  render() {
    return (
      <div onClick={this.handleClick}>
        <p>Number of clicks: {this.state.clicks}</p>
        <p>
          Open up the browser DevTools
          to observe that the button
          is not a child of the div
          with the onClick handler.
        </p>
        <Modal>
          <Child />
        </Modal>
      </div>

    );
  }
}

function Child({
  return (
    <div className="modal">
      <button>Click</button>
    </div>

  );
}

ReactDOM.render(<Parent />, appRoot);
复制代码

组件中功能复用:

vue2.0

一般采用 混入minxins


// minix.js
export default {
    data () {
        return {
            name'minix',
            minixName'minixObj',
            flagfalse
        }
    },
    mounted() {
        console.log('minixMounted');
    },
    methods: {
        speak() {
            console.log('this is minix');
        },
        getData() {
            return '100';
        }
    }
}

// todo.vue
import myMinxin from './minix';
export default {
    data () {
        return {
            name'todo',
            lists: [1234]
        }
    },
    
    mounted() {
        console.log('todoMounted');
    },
    minixs: [myMinxin], // todo.vue 中声明minix 进行混合
    methods: {
        speak () {
            console.log('this is todo');
        },
        submit() {
            console.log('submit');
        },
    }
}

复制代码

vue3.0 借鉴了react hooks 的思想,写法类似


import { ref, Ref, watch } from "vue";

export interface CountRange {
    min: number;
    max: number;
}

interface Result {
    current: Ref<number>;
    minus: (num: number) => void;
    plus: (num: number) => void;
    set: (num: number) => void;
    reset: () => void;
}

export function useCount(init: number, range: CountRange): Result {
    const current = ref(init);

    const minus = (num: number) => {
        current.value -= num;
    };
    const plus = (num: number) => {
        current.value += num;
    };
    const set = (num: number) => {
        current.value = num;
    };
    const reset = () => {
        current.value = init;
    };

    watch(current, (newVal: number, oldVal: number) => {
        if (newVal === oldVal) { return; }
        if (newVal < +range.min) {
            current.value = +range.min;
        } else if (newVal > +range.max) {
            current.value = +range.max;
        }
    });

    return { current, minus, plus, set, reset };
}

复制代码

<script lang="ts">
import { defineComponent, reactive, ref } from 'vue';
import { CountRange, useCount } from '@/composition/use-count';

export default defineComponent({
  name'Home',
  props: {
    title2String
  },
  data() {
    return {
      title1'this is title1'
    };
  },
  setup() {
    const title2 = ref('this is title2');

    const range: CountRange = reactive({
      min5,
      max50
    });

    const { current, minus, plus, set, reset } = useCount(10, range);

    return {
      title2,
      range,
      current,
      minus,
      plus,
      set,
      reset,
    };
  }
});
</script>

复制代码

React: hooks


import React, { useState, useCallback, useEffect } from "react";

export const useWinSize = () => {
  const [size, setSize] = useState({
    widthdocument.documentElement.clientWidth,
    heightdocument.documentElement.clientHeight
  });

  const changeSize = useCallback(() => {
    // useCallback 将函数缓存起来
    setSize({
      widthdocument.documentElement.clientWidth,
      heightdocument.documentElement.clientHeight
    });
  }, []);
  
  useEffect(() => {
   window.addEventListener("resize", changeSize);
    return () => {
      window.removeEventListener("resize", changeSize);
    };
  }, []);

  return size;
};

----------------使用------------------

import React from "react";
import { useWinSize } from "../hooks";
export default () => {
  const size = useWinSize();
  return (
    <div>
      页面大小: `{size.width}*{size.height}`
    </div>

  );
};
复制代码

状态全局管理

vue2.0/vue3.0: 都是采用vuex

React: redux/mobx/Recoil/XState/flux

  1. 如果是传统的大型复杂 React 应用,推荐还是使用 Redux 进行状态管理。

  2. 如果是中小型复杂度的 React 应用,可以考虑使用 Mobx。Mobx 提供了类似于分布式的状态管理机制,原理更为简单,使用起来很方便,但是当复杂度极高的时候,整体状态管理上来说不如Redux方便。

  3. 如果希望拥抱未来,可以直接使用基于 React Hooks 的全套解决方案。

  4. 其他的库暂时没用到,具体的用法请看文档。

  5. 文章太长,react 还有很多的一些高阶写法,各位看官请自己慢慢了解哈。

关于感想

自入行这几年来,确实感觉如今的前端变化速度太快了,从jqery到vue/react/angluar,但个人觉得无论用哪个框架都是差不多的,只有把基础知识打牢固,无论用什么框架都容易上手。

随着年龄的增长,愈发觉得自己的身体不如从前了,从前的一头茂密秀发再到现在的屈指可数的...,害,真令人挺秃然的。说偏了。不管怎样,还是希望各位看官平时也要加强锻炼身体!老话说的好,身体是革命的本钱,只有拥有一个好的身体,才能拥有美好的未来。还有大家有能让头发变多的方法可以评论下,毕竟谁也不想秃,哈哈哈。

关于学习

大家都说vue 比react简单,如果说只停留在会用的层次上个人认为都差不多,可能对一些刚入门的人来说 react 的写法稍微复杂一些,像vue的一些指令 短短 v-xxx 一句话解决,而react可能就需要多一些代码量。如果你写习惯了react,你可能就会觉得react的写法更舒服。

学习方法:对一些刚入门的小白来说,如果一来就看文档可能有的术语看不太懂,可以先看看视屏,像国外的油管视屏,b站啊,一些技术博客啊,然后就是对不懂的问题进行多敲,多问。多做笔记,慢慢的项目做的多了,自然都不是问题了。

  1. vue3.0官网

  2. 让你30分钟快速掌握vue3

  3. react官网

  4. 30分钟精通React Hooks

最后

最后

欢迎关注【前端瓶子君】✿✿ヽ(°▽°)ノ✿
回复「算法」,加入前端编程源码算法群,每日一道面试题(工作日),第二天瓶子君都会很认真的解答哟!
回复「交流」,吹吹水、聊聊技术、吐吐槽!
回复「阅读」,每日刷刷高质量好文!
如果这篇文章对你有帮助,在看」是最大的支持
》》面试官也在看的算法资料《《
“在看和转发”就是最大的支持


浏览 39
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报