1. 程式设计艺术风格 & 快照艺术风格
1.1 程式设计艺术风格
<inputv-model=username/><ul><liv-for=(item,index) in list:key=index>{{ item }}</li></ul>
<inputvalue={username}onChange={e => setUsername(e.target.value)}/><ul>{ list.map((item,index) =><likey={index}>{item}</li>) }</ul>
Vue 给我们提供了很多的命令功能,而这些功能在 React 中基本都需要我们使用原生 js 来实现。
所以会有很多人说: 使用 Vue 实际上你就是在操作 Vue,使用 React 实际上你是在操作 js。
React 魔改少,手动实现;Vue 魔改多,自动完成。
<button @click=handleClick(hello)>点击</button>const handleClick = (msg) => { console.log(msg)}
<buttononClick=handleClick(hello)>点击</button>const handleClick = (msg) => { return () => { console.log(msg) }}
我们知道 dom 的点击事件是需要我们传递一个函数过去的,就像在 React 中例子一样,你的 handleClick 肯定需要返回一个函数(或者在 jsx 中写箭头函数调用 handleClick)。而在 Vue 中可以在 @click 中直接调用 handleClick 函数,而这个函数又没有返回一个新的函数,按道理这样调用 handleClick 是会返回 undefined 的,但是由于 Vue底层做了魔改优化,使得我们不再需要在返回一个函数。
上面两个例子中,我们说不上哪种好哪种不好,只能说你更喜欢哪一种。React 中的实现更符合 js 的逻辑但却稍显麻烦,Vue 中的实现简单但却没有遵循原生 js 的特点。
程式设计艺术风格上的总结:就像我们前面讲的,Vue 写起来更像是写 Vue 代码,React 写起来更像是写 JavaScript 代码。
1.2 快照艺术风格
Vue 采用 <template> 字符串模板。更贴近 HTML,学习成本低,但有时候不灵活。React 采用 JSX 语法,更类似于 js ,限制比较多,(像一些关键字 class、for,单标签要闭合、属性要驼峰、组件名要大写等等这些都要注意),但是可以跟模板语法很好的进行结合
比如下面是一个通过 level 的值来渲染不同的标签在 Vue 和 React 中的不同实现
<template><h1v-if=level === 1>标题1</h1><h2v-if=level === 2>标题2</h1></template>
let App = () => {const level = 1const Tag = h + levelreturn (<div> { <Tag>标题{level}</Tag>}</div> )}
可以想象,如果当我们的条件判断很多时,使用 JSX 的方式会比使用模版字符串要灵活的多。
注意: Vue 一开始并不直接支持 JSX ,在 Vue 2.1.0 版本中,Vue 引入了 render函数来代替模板,这使得使用JSX 作为组件渲染函数成为可能。在Vue 2.1.0版本后的 create-vue 和 Vue CLI 都有预置的 JSX 语法支持。所以说在 Vue 中如果你想写 JSX 这个它也是支持的,但是在 React是没办法用字符串模板的方式写。
2. 组件 & 路由 & 状态管理
2.1 组件艺术风格
Vue2 中采用 选项式 API,但是由于它不够灵活,而且 this 指向不够简单,Vue3 中给我们提供了 组合式API 的写法,组合式 API 更偏向函数式程式设计的方式,它的复用能力和组合的能力更强,而且没有 this 指向问题,也是 Vue 比较推荐的写法。React 在 16.8 版本之前都是采用类组件的方式开发,类组件也会有 this 指向以及写起来很繁琐难度大的问题,在 16.8 之后 React 提供了函数组件的写法,其实函数组件和 Vue 的 组合式 API 是很像的,它的组合和复用的能力更强,而且也没有 this 指向问题,比类组件写起来简单很多,也是 React比较推荐的写法
<template><divclass=my-component><!– HTML模板 –></div></template><script>exportdefault {// JavaScript代码}</script><style>.my-component {/* CSS样式 */}</style>
import React fromreact;import./MyComponent.css;functionMyComponent() {// JavaScript代码return (<divclassName=my-component>{/* HTML模板 */}</div> );}exportdefault MyComponent;
总结:这两种框架它们的最终趋势都是函数式程式设计,不管是 Vue 还是 React都是推荐我们引入大量内置的函数或者是 use 函数来进行组合并且完成我们的开发需求。而简化使用面向对象或者是配置的写法,能简化我们使用this 的场景从而提升代码的灵活度和简易度。
2.2 路由艺术风格
Vue 采用 Vue-Router;React 采用React-Router
相比而言 vue 语法更加简练(useRouter useRoute),而 react的 use 函数太多,不够统一化(useLocation、useParams、useSearchParams、useNavigate……)
路由表的配置嵌套路由动态路由程式设计式路由守卫路由
<!– index.html –><divid=app><router-view></router-view></div>
// main.jsimport { createApp } fromvueimport{ createRouter, createWebHistory }fromvue-routerimport Home from./components/Home.vueimport About from./components/About.vueconst router = createRouter({history: createWebHistory(),routes: [ { path: /, component: Home }, { path: /about, component: About } ]})const app = createApp({// 空的 `setup` 函数 setup() {}})app.use(router)app.mount(#app)
<!– Home.vue –><template><div><h1>Home Page</h1><button @click=goToAbout>Go to About Page</button></div></template><scriptsetup>import { useRouter } fromvue-routerconstrouter = useRouter()const goToAbout = () => { router.push(/about)}</script>
<!– About.vue –><template><div><h1>About Page</h1><p>Param: {{ $route.params.id }}</p><router-linkto=/>Go to Home Page</router-link></div></template><scriptsetup>import { useRoute } fromvue-routerconst route = useRoute()const id = route.params.id</script>
import React fromreactimport { BrowserRouter asRouter, Switch, Route, Link, useParams, useHistory }fromreact-router-domimport Home from./components/Homeimport About from./components/Aboutconst App = () => {return (<Router><div><ul><li><Linkto=/>Home</Link></li><li><Linkto=/about>About</Link></li></ul><hr/><Switch><Routeexactpath=/><Home /></Route><Routepath=/about><About /></Route></Switch></div></Router> )}const Home = () => {const history = useHistory()const handleClick = () => { history.push(/about) }return (<div><h1>Home Page</h1><buttononClick={handleClick}>Go to About Page</button></div> )}const About = () => {const { id } = useParams()return (<div><h1>About Page</h1><p>Param: {id}</p><Linkto=/>Go to Home Page</Link></div> )}exportdefault App
2.3 状态管理艺术风格
Vue 采用 Vuex/Pinia ;React 采用 Redux/Mobx
语法和 API的不同:Vuex 和 Pinia 是专门为 Vue.js 设计的状态管理库,因此它们的语法和API都非常类似。而 Redux 和 Mobx 可以在任何 JavaScript 应用程序中使用,因此它们的语法和API与特定的框架无关。
数据流的不同:在 Redux 中,数据是通过单向数据流进行管理的,即 action \-> reducer \-> store \-> view。而在 Vuex 和 Pinia 中,数据是通过 Vuex store或Pinia store 直接管理的,不需要 reducer。而在 Mobx 中,数据则是通过响应式数据实现的。
异步处理的不同:在 Redux 中,异步处理通常需要使用中间件来处理异步操作。而在 Vuex 和 Pinia 中,异步操作可以通过 actions 处理。而在 Mobx 中,则可以使用 async/await 或 reaction 函数来处理异步操作。
开销和复杂性的不同:Redux 和 Mobx都需要在应用程序中进行额外的设置和配置,并且在处理大量数据时可能会导致性能问题。而Vuex 和 Pinia 的设置和配置相对简单,并且在大多数情况下可以处理大量数据。
总的来说,Vuex 和 Pinia 适用于 Vue.js 应用程序,提供了一种简单和直接的状态管理方式,而 Redux 和 Mobx 则可以在多种应用程序中使用,提供了更灵活的状态管理方案。
// store.jsimport { defineStore } frompiniaexportconst useCounterStore = defineStore({id: counter,state: () => ({count: 0, }),actions: { increment() {this.count++ }, },})
<!– App.vue –><template><div><h1>Count: {{ count }}</h1><button @click=incrementCount>Increment</button></div></template><scriptsetup>import { defineComponent } fromvueimport{ useCounterStore }from./storeconst counterStore = useCounterStore()const count = counterStore.countconstincrementCount =() => { counterStore.increment()}</script><!– 在根组件中注入 store –><script>import { createApp } fromvueimport { createPinia } frompiniaimport App from./App.vueconst app = createApp(App)constpinia = createPinia()app.use(pinia)app.mount(#app)</script>
// store.jsimport{ configureStore, createSlice }from@reduxjs/toolkitconst counterSlice = createSlice({name: counter,initialState: {count: 0},reducers: { increment(state) { state.count++ } }})exportconst store = configureStore({reducer: {counter: counterSlice.reducer }})exportconst { increment } = counterSlice.actions;
// App.jsimport{ useSelector, useDispatch }fromreact-reduximport { increment } from./storefunctionApp() {const count = useSelector(state => state.counter.count)const dispatch = useDispatch()const incrementCount = () => {dispatch(increment()) }return (<div><h1>Count: {count}</h1><buttononClick={incrementCount}>Increment</button></div> )}exportdefault App// 在根组件中注入 storeimport { Provider } fromreact-reduximport { store } from./storeReactDOM.render(<Providerstore={store}><App /></Provider>,document.getElementById(root));
3. 一些基础功能
3.1 模板对照
React 的快照变化主要通过:原生JS + 模板的方式
React 的模板比较强大,因为可以编写 JSX 结构,所以可以做出更加灵活的结构处理。
3.2 样式对照
Vue 的 class 和 style 都有三种写法:字符串、数组、对象
React 的 style 只能写对象,class 只能字符串,可借助 classnames 这个库
3.3 事件对照
<!– Vue –><template><ul><liv-for=item,index in list @click=handleClick(index)></li></ul></template><script>methods: { handleClick(index){ }}</script>
<!– React –><ul>{ list.map((v, i)=><lionClick={handleClick(i)}></li>)}</ul>const handleClick = (index) => { return () => {console.log(index) } }
3.4 表单对照
针对表单操作这一块来说,Vue 的表单命令 v-model 还是非常灵活的,总体对照要比 React 使用方便且灵活。
3.5 组件通信对照
Vue 父子组件通过 props属性通信,子父组件通过 emits 方法通信
React 父子组件也是通过 props属性通信,而子父组件则是通过回调函数通信的
emits自定义事件和回调函数,实际上是一样的思想。
跨组件的通信方案也很类似,都是一种依赖注入的方式来实现的。
3.6 逻辑复用
Vue 选项式采用:mixins混入;组合式采用:use函数
React 类组件采用:Render Props、HOC;函数组件:use函数
可以发现组合式API和函数组件都是采用use函数,所以基本复用是差不多的思想,这也是两个框架推荐的用法。
3.7 内容分发
React 通过 props.children,进行接收
3.8 DOM操作
思路都是差不多的,就是给元素添加 refDOM 元素。
4. 响应式 & 生命周期 & 副作用
4.1 响应式数据对照
Vue采用响应式数据,底层通过new Proxy()进行监控,灵活性更高
React采用state状态,通过setState()方法进行内部re-render,可控性更强
4.2 生命周期对照
beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeUnmountunmounted
constructorcomponentDidMountcomponentDidUpdatecomponentWillUnmountrender 整体对照来看,Vue 的生命周期会更丰富一些,React 生命周期会更简约一些。
4.3 副作用处理对照
watchEffect会自动根据所依赖的值进行重渲染,而useEffect要明确指定对应的值才能进行重渲染,React团队已经给出在未来的版本中可能会改成根据所依赖的值自动进行重渲染的操作,但暂时还不行。
watchEffect在更新前和卸载前触发的方式是通过回调函数的参数被调用来实现的,而useEffect是通过return的返回值来指定的。
// VuewatchEffect((cb)=>{ cb(()=>{//更新前的触发 })})
// ReactuseEffect(()=>{return()=>{//更新前的触发 }})