React & Vue 用法區別記
前言
由於我先使用react,所以react用法在我腦海根深蒂固,現在在寫vue的時候我總是想如果用react的時候我會怎麼做,然後查一下找到vue該如何使用。其實大致的思想差不多,但是用法上還是有點區別。
所以這篇文章就整理一下,react、vue兩個框架在不同的場景下,用法的區別。希望這篇文章可以幫助你在react/vue之間自由切換。1、 創建組件
React
React 有兩種類型的組件,分別是functional component, class component,所以有兩種定義方式:
functional component
function Test(props) {
return (
<div className="container">
{props.name}
</div>
)
}
ReactDOM.render(
<Test name="daisy" />,
document.getElementById(app)
)
class component
用構造函數 + 原型實現繼承// 注意:基於 `ES6` 中的class,需要配合 `babel` 將代碼轉化為瀏覽器識別的ES5語法
// 安裝:`npm i -D babel-preset-env`
class Test extends React.Component() {
constructor(props) {
// super一定要寫,繼承React.Component
super(props);
// state在裡面定義
this.state = { name : daisy};
}
// class創建的組件中 必須有render方法,且顯示return內容或者null
render() {
return (
<div className="shopping-list">
{this.state.name}
</div>
)
}
}
React中要求組件名字一定是大寫,這樣來跟原生的html元素做區分。
Vue
分為局部註冊跟全局註冊
全局註冊
Vue.component(my-component-name, { /* ... */ })
局部註冊
var ComponentA = { /* ... */ }
new Vue({
el: #app,
components: {
component-a: ComponentA,
}
})
全局註冊就是在註冊之後,各個地方都可以使用。而局部註冊就是只在這個vue 實例中才能使用。
2、數據綁定
React跟vue的思想都是開發者只需要去操作數據,至於數據在網頁中如何更新,就是它們的事情了,這個就要靠數據綁定。
那麼在React/vue中如何做到數據綁定呢。
React
採用JSX語法,變數用{}包裹即可。
const name = Josh Perez;
const element = <h1>Hello, {name}</h1>;
ReactDOM.render(
element,
document.getElementById(root)
);
注: {}里字可以是js表達式,不能是語句,這個跟vue一樣。
Vue
使用Mustache(插值語法) {{ }}從數據對象data中獲取數據,當這個數據發生改變,插值處的內容就會更新。
註:{{ }}只能出現JS表達式,不能是js語句
<h1>Hello, {{ msg }}.</h1>
<div>{{ message.split().reverse().join() }}</div>
//錯誤。js語句不支持
{{ var a = 1 }}
3、 模板語法
模板語法里分別從下面幾個方面進行對比:
- 原始html
- 條件渲染
- html屬性
- 綁定事件
React
原始html: 使用dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{__html: <span hey </span>>}}>
條件渲染:使用三目,&&,如果邏輯在複雜一點就得用函數來幫忙了。
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
isHappy ? <div> yes i am </div> : <div> no </div>
</div>
);
}
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
isShow && <div>hello word</div>
</div>
);
}
html 屬性:跟原生一樣直接寫不過要換成駝峰式tab-index => tabIndex,React 16以後還支持自定義屬性。
<div tabIndex="-1" />
<div mycustomattribute="something" />
綁定事件:直接寫,但是改成駝峰onClick這樣的形式,同時傳入的不是字元串,而是傳入一個函數作為事件處理函數
// 原生html
<button onclick="activateLasers()">
Activate Lasers
</button>
// React
<button onClick={activateLasers}>
Activate Lasers
</button>
既然說到了事件,就多撤一句。如果你需要阻止默認事件,
不能用return false的方式,而需要顯式使用preventDefault
。
而vue會提供一個更簡單的方式,後面再說。
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log(The link was clicked.);
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
Vue
原始html: 用v-html
<span v-html="<span>hey</span>"></span>
條件渲染:
// v-if 會根據Seen的值真的去移除dom節點
<p v-if="seen">現在你看到我了</p>
// 而v-show只會去改變css的值
<p v-show="seen">現在你看到我了</p>
而對於比較複雜的邏輯,vue也可以hold住。
<div v-if="type === A">
A
</div>
<div v-else-if="type === B">
B
</div>
<div v-else-if="type === C">
C
</div>
<div v-else>
Not A/B/C
</div>
html屬性:需要用v-bind來指定
<!-- 完整語法 -->
<a v-bind:href="url">...</a>
<!-- 縮寫 -->
<a :href="url">...</a>
<div v-bind:id="dynamicId"> </div>
綁定事件:
<!-- 完整語法 -->
<a v-on:click="doSomething">...</a>
<!-- 縮寫 -->
<a @click="doSomething">...</a>
另外,vue會提供一些事件修飾符,來簡化調用event.preventDefault(),event.stopPropagation()的需求。
- stop
- prevent
- capture
- self
- once
- passive
<!-- 阻止單擊事件繼續傳播 -->
<a v-on:click.stop="doThis"></a>
<!-- 提交事件不再重載頁面 -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修飾符可以串聯 -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 只有修飾符 -->
<form v-on:submit.prevent></form>
更多詳情,請移步文檔
4、內部狀態(屬性)
React
在React,組件內部自己維護的狀態叫State,不能直接去改內部狀態state,而是通過setState。
class Test extends React.Component() {
constructor(props) {
// super一定要寫,繼承React.Component
super(props);
// state在裡面定義
this.state = { name : daisy };
}
changeName () => {
this.setState({ name: Lily })
}
render() {
return (
<div className="shopping-list" onClick= {this.changeName}>
{this.state.name}
</div>
)
}
}
Vue
在Vue, 組件內部自己維護的狀態其實就是Data。如果要改的話,直接在方法里通過this或者是通過vue對象來改。
var vm = new Vue({
el: #app,
// 用於給 View 提供數據,相當於React的state
data: {
msg: Hello Vue
},
method: {
changeMsg: function() {
this.msg = Hello daisy
}
}
})
// 或者
vm.msg = daisy;
但是Vue除了在Data里定義的屬性之外,還額外定義計算屬性跟偵聽屬性。
為什麼要多兩種呢?
計算屬性是為了應付邏輯比較複雜的情況。比如:data = { message: };
<div id="example">
{{ message.split().reverse().join() }}
</div>
在這裡需要對message進行多一層的處理,但是直接寫在模板裡面,覺得不好維護,也不夠優雅。
所以Vue就多搞了一個計算屬性。
var vm = new Vue({
el: #example,
data: {
message: Hello
},
computed: {
reversedMessage: function () {
return this.message.split().reverse().join()
}
}
})
<div id="example">
{ reversedMessage }
</div>
這個reversedMessage 屬性將會監聽message的改變,而自動重新計算。
為啥React不需要這個屬性呢?
因為React有render函數,你可以在裡面隨便多定義一個變數。
每一次this.state.message改變的時候,都一定會調用render函數重新渲染,所以就相當於重新計算reversedMessage了。render() {
const reversedMessage = this.state.message.split().reverse().join()
return (
<div className="shopping-list">
{reversedMessage}
</div>
)
}
偵聽屬性感覺跟計算屬性很像,但是他們又有不同。
偵聽屬性是為了可以監聽到數據的改變,然後做一些非同步的或者開銷大的操作。
var watchExampleVM = new Vue({
el: #watch-example,
data: {
question: aaa,
},
watch: {
// 如果 `question` 發生改變,這個函數就會運行
question: function (newQuestion, oldQuestion) {
this.getAnswer()
}
},
methods: {
getAnswer: function () {
// AJax 請求
}
}
})
同樣的問題,為啥React不需要這個屬性呢?
因為React里已經有方法可以做到了。React 15的版本,可以通過componentWillReceiveProps
來實現
componentWillReceiveProps(nextProps) {
if (nextProps.id !== this.props.id) {
this.setState({externalData: null});
this._loadAsyncData(nextProps.id);
}
}
React 16的版本,可以通過getDerivedStateFromProps
,componentDidUpdate
來實現。
static getDerivedStateFromProps(nextProps, prevState) {
// Store prevId in state so we can compare when props change.
if (nextProps.id !== prevState.prevId) {
return {
externalData: null,
prevId: nextProps.id,
};
}
// No state update necessary
return null;
}
componentDidUpdate(prevProps, prevState) {
if (this.state.externalData === null) {
this._loadAsyncData(this.props.id);
}
}
5、Props
props 一般指的是父組件傳給子組件的屬性。
React
React 的props 如果是靜態的就直接傳,動態的就用變數{}來傳,還可以傳入方法。
class Test extends React.Component {
constructor(super) {
super(props);
this.state = { name : daisy}
}
onClick( ) => {
console.log("onClick");
}
render() {
return (
<Welcome name="Sara" />;
<Welcome name={this.state.name} />;
<Welcome handleClick={this.onClick} />;
// 傳入的方法其實在子組件里,在props也可以拿到。
// 使用的時候,直接this.props.handleClick()就可以了。
)
}
}
由於React寫的久了,我默認傳props的時候,字元串就是靜態的,如果是變數,或者方法,就用{},結果vue完全不是的,他們全都是傳字元串!
Vue
Vue的靜態props跟動態props傳的方法不一樣。
傳入的值title為一個常量(靜態prop)時,不加v-bind<blog-post title="My journey with Vue"></blog-post>
傳入的值title為一個變數(動態prop)時,加v-bind
<blog-post v-bind:title="titleValue"></blog-post>
因為如果不用v-bvind,vue就會認為這只是普通的字元串。
所以用了v-bind才可以轉成變數,然後vue會去data里找這個值。傳入方法的話,一樣要使用v-on,子組件要用emit方法來觸發。
<Welcome v-on:handleClick="onClick" } />;
// 子組件里要用$emit方法來觸發它
<button v-on:click="$emit(handleClick)">
Enlarge text
</button>
大家也看到了,React跟Vue對於props的處理還是很不同的。對於一個從react切過來的人,實在需要點時間去適應vue的props。
6、雙向綁定
React
使用受控組件,就是input的value直接就是state的值。
然後監聽相應的change事件,用setState改變state的值。class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// 這裡通過setState去改變state的值
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert(提交的名字: + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:
// 當input的值發生改變的時候就會調用handleChange
// 通過state改變value的值,所以這裡的顯示會隨用戶
// 的輸入更新而更新
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}
Vue
vue採用v-model的形式
<input v-model="message" placeholder="edit me">
<p>Message is: {{ message }}</p>
實際上v-model 是一個語法糖, 上面可以翻譯成:
`<input :value=message @input="message = $event.targer.value">`
input本身有個oninput事件,每當輸入框發生變化,就會觸發onput,將最新的value傳給他。
v-model 在內部為不同的輸入元素使用不同的屬性並拋出不同的事件:
- text 和 textarea 元素使用 value 屬性和 input 事件;
- checkbox 和 radio 使用 checked 屬性和 change 事件;
- select 欄位將 value 作為 prop 並將 change 作為事件。
v-model 不僅僅可以用在原生html元素上,還可以用在組件上。用在組件上的時候v-on用於監聽事件,emit用來觸發事件。
<div id="demo">
<currency-input v-model="price"></currentcy-input>
// 相當於是<currency-input :value="price" v-on:input="price = arguments[0]"></currentcy-input>
// 這裡用v-on監聽事件,相當於傳給了子組件value,還有input方法
<span>{{price}}</span>
</div>
Vue.component(currency-input, {
template: `
<span>
<input
ref="input"
:value="value" // 這裡將父組件傳來的value賦值給input的value
@input="$emit(input, $event.target.value)" // 這裡去觸發input方法
>
</span>
`,
props: [value],
})
var demo = new Vue({
el: #demo,
data: {
price: 100,
}
})
總結一下,React在雙向綁定方面,其實還是用著state在幫忙,傳給子組件的方法也是直接傳props即可,子組件直接通過props來調用。
<Welcome handleClick={this.onClick} />;
// 傳入的方法其實在子組件里,在props也可以拿到。
// 使用的時候,直接this.props.handleClick()就可以了。
而Vue是利用了v-model,自動幫你做了這件事。而在傳給子組件的時候,需要用$emit來配合。
同時,你一定要知道v-model 實際上是可以翻譯成這樣的語法糖。<input :value=message @input="message = $event.targer.value">`
這樣你才知道父組件在用v-model的時候,實際上傳了value,跟input給子組件。
7、渲染child節點
React
React里可以通過this.props.children 獲取。
class NotesList extends React.Component {
render() {
return (
<ol>
{
.map(function (child) {
return <li>{child}</li>
// 渲染出來就是
// <li> <span>hello</span> </li>
// <li> <span>world</span></li>
})
}
</ol>
)
}
}
React.render(
<NotesList>
<span>hello</span>
<span>world</span>
</NotesList>,
document.body
);
Vue
Vue引入了一個插槽的概念。
通過父組件中插入一個<slot>
的標籤來實現。// 父組件 Message的html
<div>
hello,world
<slot></slot>
</div>
// 在調用的時候
<Message>
<span> hello </span>
<span> daisy </span>
</Message>
最後就會渲染出來
<div>
hello,world
<span> hello </span>
<span> daisy </span>
</div>
Vue還提供了指定區域的插槽功能,比如
// 父組件 Message的html
<div>
hello,world
<slot name="firstName"></slot>
<slot name="lastName"></slot>
</div>
// 在調用的時候,需要使用<template>元素,用上面的
// v-slot 來指定名稱
<Message>
<template v-slot:firstName>
<h1>daisy</h1>
</template>
<template v-slot:lastName>
<h1>huang</h1>
</template>
</Message>
這樣最後渲染的結果為:
<div>
hello,world
<span> daisy </span>
<span> huang </span>
</div>
而React的話可以直接通過this.props.chilren[0] [1]
等拿到不同的元素,所以就沒有提供這個了。而且就我的實踐經驗來看,第二種用法感覺實際用的其實很少,掌握第一種就行了。一般要用都是直接塞進去一段內容。
當然插槽還有好多內容,詳細可以查閱文檔
8、生命周期
我覺得React、vue的生命周期都大同小異,主要就是創建 -> 更新 -> 銷毀。具體一些名字不同而已。
React
react 15
※國內15家MES軟體廠商大對比
※時隔30年的10組對比照: 長大後我就成了你
※實用!嬰幼兒退熱葯的選擇與對比
※中外課本教材大對比,我們的比十年前還丑