React 转 Vue2.0 转 Vue3.0 不完全指东
点击上方 前端瓶子君,关注公众号
回复算法,加入前端编程面试算法每日一题群
来源:_落魄前端_
https://juejin.cn/post/6960245513509699614
-
前几天看到了 Vue 转 React不完全指北这篇文章@俊劫,感觉挺有意思,突然想到自己以前写taro+react+ts,而现在刚好公司使用的是vue+ts,于是本文就开始了。
vue 与react 的对比
这个太长就不说了,各位看官老爷自己看哈Vue 官方对比 React , 个人的理解 ->https://cn.vuejs.org/v2/guide/comparison.html
相同点
-
都使用Virtual DOM 进行页面渲染 -
都提供了响应式和组件化的视图组件 -
集中在UI层面,类似于路由,状态管理,数据请求都交给其他库管理
不同点
-
vue 是采用单文件组件的方式进行开发,也就是把传统的web 页面 html,css.javascript 全部放到一个文件,而react 是采用 jsx 的方式(JSX是一种JavaScript的语法扩展,运用于React架构中,其格式比较像是模版语言)React的作者认为视图表现,数据和逻辑天然就是耦合的,所以没必要把它们分开,所以React创造了JSX。
-
diff 算法不同,vue Diff使用双向链表,一边对比,一边更新DOM,react 主要通过diff队列保存哪些DOM需要更新,得到patch树,最后再统一操作批量更新DOM
-
数据绑定方式不同,vue 采用 v-model来实现 react 采用事件 onChange 来更新数据
-
有的人认为 vue 适合小型项目,react适合大型项目,说法都不一样,而个人认为其实都差不多,没有确切的说法,主要还是看公司或者团队的技术栈方向。从react 转到vue也确实发现vue 上手比较快,options 的写法一把梭,而对于小白来说react的写法可能就相对复杂一点。
生命周期
Vue2.0
Vue3.0
React
常用的生命周期
-
Vue2.0: activated / deactivated / created / mounted / beforeDestroy /destroyed
-
Vue3.0: onMounted / onBeforeUnmount / onUnmounted / onRenderTracked/ onRenderTriggered
-
React: componentDidMount/componentDidUpdate/componentWillUnmount/componentWillReceiveProps shouldComponentUpdate(主要做性能优化)
-
React hooks:
useEffect(() => {
console.log('进入页面')
return () => {
console.log('离开页面')
}
},[])
复制代码
顺便在这里 推荐一个阿里出品的 hooks库 ahooks 用起来简直不要太爽,当然也可以自己写,哈哈
组件传值
-
vue2.0: props / emit/ provide/inject/attrs/attrs/attrs/listeners/ event bus / vuex 等
-
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>
复制代码
-
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 {
modalOpen: false
}
}
})
复制代码
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 = {clicks: 0};
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',
flag: false
}
},
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: [1, 2, 3, 4]
}
},
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: {
title2: String
},
data() {
return {
title1: 'this is title1'
};
},
setup() {
const title2 = ref('this is title2');
const range: CountRange = reactive({
min: 5,
max: 50
});
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({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
});
const changeSize = useCallback(() => {
// useCallback 将函数缓存起来
setSize({
width: document.documentElement.clientWidth,
height: document.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
-
如果是传统的大型复杂 React 应用,推荐还是使用 Redux 进行状态管理。
-
如果是中小型复杂度的 React 应用,可以考虑使用 Mobx。Mobx 提供了类似于分布式的状态管理机制,原理更为简单,使用起来很方便,但是当复杂度极高的时候,整体状态管理上来说不如Redux方便。
-
如果希望拥抱未来,可以直接使用基于 React Hooks 的全套解决方案。
-
其他的库暂时没用到,具体的用法请看文档。
-
文章太长,react 还有很多的一些高阶写法,各位看官请自己慢慢了解哈。
关于感想
自入行这几年来,确实感觉如今的前端变化速度太快了,从jqery到vue/react/angluar,但个人觉得无论用哪个框架都是差不多的,只有把基础知识打牢固,无论用什么框架都容易上手。
随着年龄的增长,愈发觉得自己的身体不如从前了,从前的一头茂密秀发再到现在的屈指可数的...,害,真令人挺秃然的。说偏了。不管怎样,还是希望各位看官平时也要加强锻炼身体!老话说的好,身体是革命的本钱,只有拥有一个好的身体,才能拥有美好的未来。还有大家有能让头发变多的方法可以评论下,毕竟谁也不想秃,哈哈哈。
关于学习
大家都说vue 比react简单,如果说只停留在会用的层次上个人认为都差不多,可能对一些刚入门的人来说 react 的写法稍微复杂一些,像vue的一些指令 短短 v-xxx 一句话解决,而react可能就需要多一些代码量。如果你写习惯了react,你可能就会觉得react的写法更舒服。
学习方法:对一些刚入门的小白来说,如果一来就看文档可能有的术语看不太懂,可以先看看视屏,像国外的油管视屏,b站啊,一些技术博客啊,然后就是对不懂的问题进行多敲,多问。多做笔记,慢慢的项目做的多了,自然都不是问题了。
-
vue3.0官网
-
让你30分钟快速掌握vue3
-
react官网
-
30分钟精通React Hooks