做者: 落尘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, 也可微信搜索小程序「美菜产品技术团队」,干货满满且每周更新,想学习技术的你不要错过哦。