前言

由於我先使用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的版本,可以通過getDerivedStateFromPropscomponentDidUpdate來實現。

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

react 16

Vue

這篇文章僅僅是對用法上做了一個區分,下篇文章,會對他們的一些實現思想在做一個比較。


推薦閱讀:
相关文章