做者: 落塵html
Vue和React都是目前最流行、生態最好的前端框架之一,之因此用「與」字來作標題,也是爲了不把他們放在對立面。畢竟框架自己沒有優劣之分,只有適用之別,選擇符合自身業務場景、團隊基礎的技術纔是咱們最主要的目的。前端
本文但願經過對比兩個框架在使用上的區別,能使只用其中一個框架進行開發的開發者快速瞭解和運用另外一個框架,已應對不一樣技術棧的需求,不管你是爲了維護老系統仍是爲了適應新的生態(React-Native)。vue
那咱們如今就從下面這幾個步驟來分別聊聊Vue和React的區別:node
Vue和React分別都有本身的腳手架工具:vue-cli和create-react-app,二者都能幫助咱們快速創建本地的開發環境,具體步驟以下:react
npm install -g @vue/cli
vue create my-project //node版本推薦8.11.0+
// 若是你仍是習慣以前2.*版本的話,再安裝一個工具便可
npm install -g @vue/cli-init
vue init webpack my-project
複製代碼
生成的項目結構大同小異,只不過3.*版本將原先直接暴露出來的webpack配置封裝到了本身的vue.config.js配置文件中,包括常見baseUrl、outputDir、devServer.proxy等。webpack
Vue-2:git
Vue-3:github
npm install -g create-react-app
npx create-react-app my-project
複製代碼
能夠看出create-react-app的封裝作的更爲完全一些,基本將全部的webpack配置都隱藏了起來,僅由package.json來控制。但對於須要靈活配置的場景,這樣的封裝顯然是不合適的,你能夠運行npm run eject
將封裝的配置再次釋放出來。web
使用腳手架咱們能夠很是順利的搭建起本地的開發環境,那下一步就是咱們將使用它們來進行業務開發。在此以前,咱們能夠先問一個問題,什麼是最基本的開發需求?算法
咱們能夠簡單總結爲三個方面,而面對新框架你也能夠從這三個方面入手,看框架是如何處理的,從而達到快速上手的目的。
咱們先來看Vue是如何處理這三方面的
// 模板渲染,能夠直接使用data對象中的數據,利用指令來處理渲染邏輯
<template>
<div class="hello">
<div v-for="(item, index) in list" :key="index">
{{ item.title }}
</div>
// 處理表單,將用戶輸入的內容賦值到title上
<input v-model="title" />
// 事件監聽,用戶點擊後觸發methods中的方法
<button @click="submit">提交</button>
</div>
</template>
<script>
export default {
data () {
return {
list: [
{ title: 'first' },
{ title: 'second' }
],
title: ''
}
},
methods: {
submit () {
this.list.push({ title: this.title })
this.title = ''
}
}
}
</script>
複製代碼
而React的書寫方式爲:
import React, { Component } from 'react';
export default class HelloWorld extends Component {
state = {
list: [
{ title: 'first' },
{ title: 'second' },
],
title: '',
};
setTitle = (e) => {
// 須要手動調用setState來進行重繪,不然input的value不會改變
this.setState({ title: e.target.value })
}
submit = (e) => {
const { title, list } = this.state;
list.push({ title });
this.setState({ list, title: '' });
}
render() {
const { title, list } = this.state;
return (
<div className="App">
// react會使用jsx的方式來進行模板的渲染,可混合使用js和html標籤
// {}解析js,()解析html
{ list.map((item, index) => (
<div key={index}>{ item.title }</div>
)
)}
// 事件監聽 + 表單處理
<input value={title} onChange={this.setTitle} />
<button onClick={this.submit}>增長</button>
</div>
);
}
}
複製代碼
從上面兩個例子中咱們能夠看出,Vue的寫法稍微簡單一下,不須要手動利用setState這樣相似的特定函數來觸發數據與模板的同步。但也正因爲手動觸發,React顯得更加自由一些,咱們能夠根據實際狀況去處理,適當減小沒必要要的重繪,從而對性能進行優化。
生命週期一直是Vue和React中很是重要的概念,明確的表達了組件從建立、掛載、更新、銷燬的各個過程。那二者在細節上有什麼區別呢,咱們能夠經過它們各自官網的圖來看一下。
Vue的生命週期以下圖:
React的生命週期以下圖:
主體流程大體均可以分爲:
Vue之因此給人上手簡單、開發便捷的感受,有一部分緣由就在於封裝了不少常見的操做,你只須要簡單的使用一些屬性就能夠達到目的。便捷和自由有的時候就是一對反義詞,你在得到一些特性的時候就會犧牲一些特性,就像算法上常見的空間換時間這樣的策略。
那咱們就來看看Vue的哪些常見屬性是React中沒有的:
計算屬性常常用於集中管理多個狀態的結果,避免將過多的計算邏輯暴露在模板或其餘引用的地方。咱們也無需知道該計算屬性依賴的屬性什麼時候、如何變化,Vue能自動監聽變化結果並從新計算。例如:
computed: {
fullName () {
return this.firstName + ' ' + this.lastName;
}
}
複製代碼
####watch: 和計算屬性相似,能夠指定監聽某個狀態變化,並得到該屬性變化先後的值。例如:
watch: {
name (val, oldVal) { // 監聽某個屬性發生變化
....
}
}
複製代碼
而在React中,你須要實現這一功能,你可能須要在componentWillReceiveProps時本身去判斷屬性是否發生了變化,或者在setState的同時觸發變化後的業務邏輯。例如:
componentWillReceiveProps(nextProps) {
if (nextProps.name != this.props.name) { // props中的某個屬性發生了變化
....
}
}
複製代碼
####指令 Vue中的指令主要用於封裝對DOM的操做,例如模板中的v-if/v-else/v-for等;React自己利用jsx來操做DOM,因此也就沒有這一律念。咱們舉一個自定義指令的例子,來提現下指令的做用:
<img v-lazy="img_url" />
複製代碼
directives: {
'lazy': {
inserted: function (el, binding) {
var body = document.body;
var offsetTop = el.offsetTop;
var parent = el.offsetParent;
// 獲取綁定元素對於body頂部的距離
while (parent && parent.tagName != 'body') {
offsetTop += parent.offsetTop;
parent = parent.offsetParent;
}
// 若出如今可視區域內,則直接賦值src
if (body.scrollTop + body.clientHeight > offsetTop && body.scrollTop < offsetTop) {
el.src = binding.value;
} else {
// 若暫未出現,則監聽window的scroll事件
var scrollFn = function () {
// 出如今區域內才賦值src,並取消事件監聽
if (body.scrollTop + body.clientHeight > offsetTop && body.scrollTop < offsetTop) {
el.src = binding.value;
window.removeEventListener('scroll', scrollFn)
}
}
window.addEventListener('scroll', scrollFn)
}
}
}
}
複製代碼
這裏其實咱們也能夠試想一下,若是使用React的話,咱們能夠如何實現圖片懶加載這個功能?
組件目前能夠說是兩個框架開發中最基本的單位,每一個項目都包含一個根組件,而後再以路由進行細分,從頁面直到功能更爲單一的組件。而如何處理項目中多個組件的關係、如何高效的複用組件也就成了框架所須要考慮的事情,下面就和你們對比下二者在處理這種狀況下的異同。
組件通訊是組件關係中最多見的一種,主要表如今父組件向子組件傳遞數據,子組件調用父組件方法影響父組件。咱們分別舉例來講明二者在寫法上的區別:
Vue的寫法:
// 父組件
<template>
<div class="parent">
<div v-for="(msg, index) in msgs" :key="index">
{{ msg.content }}
</div>
// 經過v-bind能夠綁定父組件狀態傳遞給子組件
// 而且能夠自定義事件將方法傳遞給子組件
<child :last="last" @add="add"></child>
</div>
</template>
<script>
import Child from '@/components/PropsEventChild'
export default {
components: {
Child
},
data () {
return {
name: 'parent',
msgs: []
}
},
computed: {
last () {
const { msgs } = this
return msgs.length ? msgs[msgs.length - 1] : undefined
}
},
methods: {
add (msg) {
this.msgs.push(msg)
}
}
}
</script>
// 子組件
<template>
<div class="child">
<input v-model="content" placeholder="請輸入" />
<button @click="submit">提交</button>
</div>
</template>
<script>
export default {
// 此處須要定義接受參數的名稱
props: ['last'],
data () {
return {
content: ''
}
},
methods: {
submit () {
const time = new Date().getTime()
const { last } = this
if (last && (time - last.time < 10 * 1000)) {
alert('你發言太快了')
return
}
// 經過$emit的方式能夠調用父組件傳遞過來的自定義事件,從而修改父組件的狀態
this.$emit('add', { content: this.content, time })
this.content = ''
}
}
}
</script>
複製代碼
React寫法:
// 父組件
import React, { Component } from 'react';
import Child from './Child'
export default class Parent extends Component {
state = {
msgs: []
};
get last () {
const { msgs } = this.state;
return msgs.length ? msgs[msgs.length - 1] : undefined;
}
add = (msg) => {
const { msgs } = this.state;
msgs.push(msg);
this.setState({ msgs });
};
render() {
const { msgs } = this.state;
return (
<div className="Parent">
{ msgs.map((item, index) => (
<div key={index}>{ item.content }</div>
)
)}
// 直接傳遞參數和方法
<Child last={this.last} onClick={this.add}/>
</div>
);
}
}
// 子組件
import React, { Component } from 'react';
export default class Child extends Component {
state = {
content: ''
};
setContent = (e) => {
this.setState({ content: e.target.value })
}
submit = (e) => {
const { props: { last }, state: { content } } = this
const time = new Date().getTime()
if (last && (time - last.time < 10 * 1000)) {
alert('你發言太快了')
return
}
// 直接調用傳遞過來的方法
this.props.onClick({ content, time })
this.setState({ content: '' })
}
render() {
const { content } = this.state;
return (
<div className="Child">
<input value={content} onChange={this.setContent} />
<button onClick={this.submit}>增長</button>
</div>
);
}
}
複製代碼
實際開發當中常常會使用到一些提供單純的UI功能,但內容須要業務自行定義的組件,例如Modal、Table等。這種類型的組件每每和會業務組件進行嵌套,但不影響業務組件的狀態。Vue提供了這樣寫法,能夠將業務組件替換掉UI組件中特定的一部分,這裏就不贅述代碼了,直接上一下例子:
React中沒有<slot>
這種語法,但組件自己能夠將標籤內的部分以props.children的方式傳遞給子組件,例如:
import React, { Component } from 'react';
import Wrapper from './Wrapper'
export default class Demo extends Component {
state = {
content: '我是Demo組件中的內容'
};
render() {
return (
<div>
<Wrapper>
<div>{ this.state.content }</div>
</Wrapper>
</div>
);
}
}
import React, { Component } from 'react';
export default class Wrapper extends Component {
render() {
return (
<section>
<header>我是Wrapper頭部</header>
{ this.props.children }
<footer>我是Wrapper尾部</footer>
</section>
);
}
}
複製代碼
能夠看出this.props.children表明的就是父組件(class Demo)中的被包裹的標籤<div>{ this.state.content }</div>
有的時候咱們可能但願組件只是起一個渲染的做用,並不須要狀態變化或者生命週期這種監聽。爲了節約性能,React能夠只使用函數來接受和使用props:
const Wrapper = (props) => (
<section>
<header>我是Wrapper頭部</header>
{ props.children }
<footer>我是Wrapper尾部</footer>
</section>
)
複製代碼
Vue當中有種相似的用法,在template標籤中加上functional,模板渲染後不會對數據進行監聽:
<template functinal>
<section>
<header>我是Wrapper頭部</header>
{ props.children }
<footer>我是Wrapper尾部</footer>
</section>
</template>
// 除了template標籤以外,該組件已沒法使用Vue實例,也就是不能在該.vue文件中使用<script></script>標籤。
複製代碼
所謂的跨組件通訊只指不經過props傳遞獲取父組件中定義的數據,這樣深層的子組件就能直接訪問到頂層的數據。
React中提供了Context這樣的機制來實現,具體代碼:
Vue中也有相似的機制,但官方文檔中表示這個特性本意在於給高階插件/組件庫提供用例。並不推薦直接用於應用程序代碼中。
,不過咱們仍是能夠簡單瞭解下它的使用方法:
// 祖先級組件
export default {
name: 'App',
provide: {
theme: 'meicai'
}
}
// 子組件,獲取provide中定義的數據
export default {
inject: ['theme']
}
複製代碼
高階組件是React中的一個概念,簡稱HOC(Higher Order Component),定義是:高階組件就是一個函數,且該函數接受一個組件做爲參數,並返回一個新的組件。例以下面這個例子:
const withHeader = (WrappedComponent) =>
class WrapperComponent extends Component {
render() {
return <section>
<header><h1>頂部信息</h1></header>
<WrappedComponent {...this.props} />
</section>
}
}
複製代碼
其中參數WrappedComponent爲React組件,咱們能夠在其餘組件中使用裝飾器的語法來使用這個高階組件:
import React, { Component } from 'react';
import withHeader from './withHeader'
@withHeader // 裝飾器寫法
class Section extends Component {
state = {
content: '我是SectionOne'
};
render() {
return (
<div>
{ this.state.content }
</div>
);
}
}
複製代碼
這樣,這個Section組件最後render的效果即包含了withHeader中添加的頂部信息等元素,也包括了本身自己的DOM結構。
除了從結構上對子組件進行加強外,高階組件也能夠經過注入props的方式加強子組件的功能,在ant-design的Form組件中,就有相似的用法,如下咱們舉個簡化的例子,實現給受控組件綁定值而且設定onChange:
import React, { Component } from 'react';
// 表單高階組件
const Form = (Wrapped) =>
class WrapperComponent extends Component {
state = {
fields: {}
};
getFieldValue = (name) => {
return this.state.fields[name];
};
setFieldValue = (name, value) => {
const fields = this.state.fields;
fields[name] = value;
this.setState({ fields });
};
getFieldDecorator = (name, value) => {
const { fields } = this.state;
if (fields[name] === undefined) {
fields[name] = value || '';
this.setState({ fields })
}
return (WrappedInput) =>
React.cloneElement(WrappedInput,
Object.assign({}, WrappedInput.props, { value: fields[name], onChange: (e) => this.setFieldValue(name, e.target.value) }));
};
render () {
const { getFieldValue, getFieldDecorator } = this;
// 注入新的props對象,將高階組件的方法傳入到子組件中
const form = {
state: this.state,
getFieldValue,
getFieldDecorator,
};
return (<Wrapped {...this.props} form={form}></Wrapped>);
}
};
export default Form
// 使用方式
import React, { Component } from 'react';
import Form from './form'
class Demo extends Component {
checkValue = (e) => {
const { getFieldValue } = this.props.form;
console.log(getFieldValue('title'));
}
render() {
const { getFieldDecorator } = this.props.form;
return (
<div>
{ getFieldDecorator('title')(<input />) }
<button onClick={this.checkValue}>獲取輸入框值</button>
</div>
);
}
}
export default Form(Demo)
複製代碼
##6. 狀態管理 狀態管理是在處理大型項目中組件通訊經常使用的設計模式,Vue和React都採起了這種模式並擁有本身的實現方式。二者在大概念上沒有什麼的不一樣,從二者的流程圖上也能看出來:
Vue狀態管理流程圖:
React狀態管理流程圖:
兩張圖都標明瞭整個過程爲一個單項數據流,從狀態映射視圖,視圖(組件)操做動做,動做影響狀態。
因此這節就簡單對比下二者在使用的區別:
Vue:
React:
最後咱們回顧下,本文主要從如下這幾個方面對比了Vue和React的不一樣:
但願從這幾個方面切入能夠幫你們快速的瞭解另外一門框架,本文所包含案例
原文連接: tech.meicai.cn/detail/78, 也可微信搜索小程序「美菜產品技術團隊」,乾貨滿滿且每週更新,想學習技術的你不要錯過哦。