前言

由于我先使用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

这篇文章仅仅是对用法上做了一个区分,下篇文章,会对他们的一些实现思想在做一个比较。


推荐阅读:
相关文章