前言
初入前端领域的时候,听说了前端界三大框架React、Vue、Angular。作为一个初学者(啥都不会),肯定是什么简单学什么。网上对于Vue的呼声一片,什么上手简单、中文文档等等。于是就入了Vue.js的坑。最近在学习Vue.js的源码,看到了render函数这一块,隐约听说React好像也有这么个东西,于是便找了个简易React的教程一探究竟(血的经验告诉我看官方文档看了一遍啥就全忘了~)。
教程链接(科学上网):React Tutorial for Beginners www.youtube.com/watch?v=Ke9…
学完了这个简易教程看了一下React官方中文文档中的教程,是利用React完成一个三连棋游戏,看完上面的教程再看这个官方的教程是比较容易的,基本顺着一遍看下来没有什么难点。
接下来就是去看React的官方文档了~
初步看了一下文档中核心概念部分,有了Vue.js的一些思维以及已经学习了React的一些基本用法,这些内容很容易就理解了。不像当初看Vue文档的时候,看了好几遍文档也没理解个所以然出来。
对比了React和Vue的一些核心要点,总结了以下一些可以比较的东西:
- JSX && Template
- Component
- State && Data
- Props
- 列表渲染
- 条件渲染
- 表单
- 事件处理
- 单向数据流
- 生命周期
JSX && Template
JSX:
const element = <h1>Hello, world!</h1>;
这个标签语法看起来有点像字符串,又有点像HTML
。但实际上它啥也不是,它叫JSX
,是一个JavaScript
的语法拓展。通过Babel
的帮助,会把JSX
转译为一个名为 React.createElement()
函数调用。在JSX
中可以在{}
中插入任意的JavaScript
表达式,实际上它就是纯粹的JavaScript
。
上面的例子经过转译就可以变成这样:
const element = React.createElement(
'h1',
'Hello, world!'
);
Template:
Vue.js
中使用了HTML
的模版语法,当初使用的第一印象就是只要会写了HTML
,那么这个模版语法和HTML
几乎没有什么差别,不需要思维逻辑上的转变。只需要在{{ }}
中增加一些JavaScript
表达式。
<span>Message: {{ msg }}</span>
Component
Vue Component:
// 定义一个名为 button-counter 的新组件
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
Vue
组件的构成主要包含以下几个部分:
- data
- props
- computed
- methods
- watch
- lifecycle
- render:用的比较少,和React中的render基本相同
React Component:
class Hello extends React.Component {
state = {
...
}
render() {
return <h1>Hello, {this.state.name}</h1>;
}
}
React
组件的构成主要包含以下几个部分:
- props
- state
- render
- functions
- lifecycle
整个组件就是一个JavaScript
函数或者利用ES6的class来定义组件。
State && Data
数据部分应该是两个框架差异比较大的地方了,Vue是一个是数据响应式的框架,当我们在Component
的data中注册了变量,只要这个变量发生了改变,那么视图也会自动地更新。
对于React
来说,每一个Component
有自己独立的State
来维护这个Component
的私有数据。当数据变更的时候,视图是不会知道数据变更的,需要调用Component
上的setState
方法来告知视图需要根据新的数据变更了。
State:
注意点:出于性能考虑,React
可能会把setState()
调用合并成一个调用。所以在React中,props和state可能会异步更新,所以不要依赖他们的值来更新下一个状态。可以用如下的方式来进行状态变更。
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
Data:
Vue中关于响应式原理有这么一段描述:
每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property
记录为依赖。之后当依赖项的 setter
触发时,会通知 watcher,从而使它关联的组件重新渲染。
当我们把一个对象作为一个Vue实例的data使用的时候,Vue会遍历所有的Property,同时使用Object.defineProperty
将所有的property转化为getter和setter。当这些property变更的时候则会调用这些setter和getter来回去数据变更这个动作,以此通知组件重新渲染。
Props:
Vue的prop语法:
在Vue组件中使用Props需要分为两个步骤:
- 在父组件中使用的时候传入参数,可以是静态的也可以是动态的
- 在自组件中定义props中需要用到的属性
/* 父组件传入 */
<blog-post title="My journey with Vue"></blog-post>
/* 动态参数,可以直接简写成:title="" */
<blog-post v-bind:title="post.title"></blog-post>
/* 自组件定义 */
props: {
title: String,
}
React的prop语法:
React中只需要在父组件使用子组件的时候传入值,就可以在自组件中使用this.props.xxx
访问到了。父组件中也可以定义静态参数和动态参数。
/* 静态 */
const element = <Welcome name="Hello World" />;
/* 动态 */
const element = <Welcome name={this.state.name} />;
注意点:当我们在React Component中定义一个方法的时候,需要在方法中访问props上的属性的时候,通常会使用this.props.xxx来访问,但是可能会出现props为undefined
的情况,这个时候可以通过两种方法解决。
- 在
constructor
中使用bind绑定 - 使用箭头函数
列表渲染
Vue的列表渲染:
vue提供了v-for的指令将一个数组对应为一组元素。只需要在template
中使用该指令即可。
<ul id="example-1">
<li v-for="item in items" :key="item.message">
{{ item.message }}
</li>
</ul>
React的列表渲染:
因为React是一个存粹的JavaScrip
t对象,所以也没有像Vue一样有对应的指令,在React组件中可以使用map()
方法来遍历一个数组。
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
两者比较相似的地方是都需要一个key
。两个框架均存在虚拟DOM的概念,通过key的绑定然后使用Diff算法只更新存在差异的DOM元素。减少频繁更新DOM导致的性能问题。
条件渲染
Vue的条件渲染:
在template
中使用v-if、v-else、v-else-if
指令可以控制哪些元素需要渲染,哪些元素不需要渲染。
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
React的条件渲染:
由于React组件就是一个JavaScript
对象,所以可以直接使用if-else
分支语句控制元素的渲染。
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
表单
Vue表单输入绑定:
Vue中可以使用v-model在表单元素上进行双向数据绑定,将vue 实例的数据作为数据来源。
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
React受控组件:
React的受控组件意思也就是让组件自己维护的state成为“唯一数据源”,在我认为这和Vue中的数据双向绑定有异曲同工之处。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。但是在React中需要我们去监听表单的事件去驱动state中数据的变更,然后再将state中的数据绑定到表单元素上。
事件
Vue Event:
Vue中可以用 v-on
指令监听 DOM 事件,并在触发时运行一些 JavaScript
代码。
<div id="example-1">
<button v-on:click="counter += 1">Add 1</button>
<p>The button above has been clicked {{ counter }} times.</p>
</div>
React Event:
React中和Vue中事件注册大致相同,通过绑定处理方法,点击的时候监听数据。
<button onClick={activateLasers}>
Activate Lasers
</button>
不同的地方在于一些修饰符的使用,Vue中减少了event.preventDefault()
或event.stopPropagation()
这种调用,更加方便地使用.stop
或者.prevent
。
React则更偏向于操作JS,需要在回调函数中执行event.preventDefault()
或event.stopPropagation()
单向数据流
Vue的单向数据流:
在Vue中,所有的prop都让父子组件中形成了一个向下绑定的关系,父级 prop 的更新会向下流动到子组件中。但是如果子组件需要改变父组件传递的prop值该怎么办?
-
在组件中定义个data属性并且将prop中的助属性作为data属性的初始值
props: ['name'], data: function () { return { name: this.name } }
-
利用computed来进行值的转化
props: ['name'], computed: { normalizedName: function () { return this.name.toLowerCase() } }
-
使用
$emit
来告知父组件,让父组件进行修改。但是如果子组件中使用的数据和真正拥有数据的组件的层级相差的很多,则会导致嵌套传递,这种情况可以引入vuex
来进行状态的维护。
React的单向数据流:
React中的单向数据方式和Vue类似,同样父级 prop 的更新会向下流动到子组件中。当子组件需要变更数据源的数据的时候,则需要在拥有数据的组件中添加变更数据的方法,然后将该方法以prop
的形式传递进入子组件,子组件调用该方法变更数据。和Vue相比,变更数据的方法已经从上层传递进了子组件,就不需要在将该变更通知以冒泡的形式传递回去。在React中也可以通过引入Redux
来进行状态的维护。
生命周期
Vue LifeCycle:
常用的生命周期主要包含以下几个步骤:
- beforeCreated
- created
- beforeMounted
- updated
- beforeDestroyed
- destroyed
React LifeCycle:
React的生命周期主要包含三个部分:
- Mount
constructor
在React Component中仅调用一次render
ComponentDidMount
调用的时候所有的子组件也全部挂载完成
- Update 当state或者props发生变更的时候调用
render
ComponentDidUpdate
- Unmount 将组件从DOM中移除的时候调用
ComponentWillUnmout
总结
作为一个前端小白,一开始学习了Vue.js,也就停留在使用Vue构建小的demo,近半年使用Uni-app(基本是Vue的语法)开发微信小程序,熟练程度也就还可以的样子。在Vue的基础上再学React,很多概念理解起来就很容易了,我认为如果JavaScript掌握的相对比较好的话,学习React比较简单。但是如果想要快速入手框架,可以在Vue的基础上学React,这样就可以很快理解了。接下来还得去深入的看一下React的API以及一些相关的生态。