浅聊 React & Vue

比较.jpg

没有对比就没有伤害,今天突发奇想想对Vue和React的使用体验进行一下总结,随便比较下两者。网络上对比两者比较的各种的文章一大把,众说纷纭。接触和使用Vue和React已经很长时间了,给自己最直接的感觉是:两者并没有太明显的差别,毕竟完成的是同一件事情(业务页面开发),但如果是先用Vue,再用React的,会发现之前感觉Vue那种模板化和数据视图分离的编码方式很先进很牛逼,而写熟了React的JSX语法,和组件化的思想,一下子又觉得React的哲学思想又先进一点。所以总结下来,好比饭局上喝红酒还是白酒,不同时期给人感觉不一样。下文,不带偏见的聊聊两者。

发展历史

百科一把:

React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。

Vue 是一套用于构建用户界面的渐进式JavaScript框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,方便与第三方库或既有项目整合。

​ 从介绍来看,两个框架都是MVC模式的延续,或者叫改进。React更侧着于V,正如介绍的“React主要用于构建UI”,React更关心的是UI的组件化,而Vue则在MVC的基础上提出了MVVM的思想。

国内使用情况:

​ BAT主流使用React多一点,普通互联网和传统软件公司还是Vue多一点(没有统计数据支撑,属于笔者意淫)。

原理分析

Vue代码:

main.js

import Vue from 'vue'
import App from './App'

new Vue({
  el: '#app',
  components: { App },
  render: h => h(App)
})

App.vue

<template>
  <div id="app">
    <ToList />
  </div>
</template>

<script>
import ToList from './components/ToList'
export default {
  components: { ToList },
  name: 'App'
}
</script>

ToList.vue

<template>
  <div>
    <div>
      <input v-model="inputValue" />
      <button @click="handleSubmit">提交</button>
    </div>
    <ul>
      <li v-for="(item,index) of list"
          :key="index">
        {{item}}
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  name: 'ToList',
  data () {
    return {
      inputValue: '', list: []
    }
  },
  methods: {
    handleSubmit: function () {
      this.list.push(this.inputValue)
      this.inputValue = ''
    }
  }
}
</script>

​ 虽然编写代码的时候分了三个文件,但是通过webpack的vue-loader等插件,最终build完,其实只生成了一个有效的JS文件:app.XXXX.js。可以这么理解,浏览器其实识别不了vue这样的文件的,浏览器能解析的就是js\html\css。vue内部的script部分我们可以理解,那么我们来分析分析template最终会变成什么样的格式。取消webpack混淆压缩插件:UglifyJsPlugin,最终抠出来的转换后脚本如下:

var ToList = {
    name: 'ToList',
    data: function data () {
        return {
            inputValue: '',
            list: []
        }
    },

    methods: {
        handleSubmit: function handleSubmit () {
            this.list.push(this.inputValue)
            this.inputValue = ''
        }
    }
}

var ToList_render = function () {
    var _vm = this
    var _h = _vm.$createElement
    var _c = _vm._self._c || _h
    //templete最终变成了js版本的vnode结构
    return _c('div', [
        _c('div', [
            _c('input', {
                directives: [
                    {
                        name: 'model',
                        rawName: 'v-model',
                        value: _vm.inputValue,
                        expression: 'inputValue'
                    }
                ],
                domProps: { value: _vm.inputValue },
                on: {
                    input: function ($event) {
                        if ($event.target.composing) {
                            return
                        }
                        _vm.inputValue = $event.target.value
                    }
                }
            }),
            _vm._v(' '),
            _c('button', { on: { click: _vm.handleSubmit } }, [_vm._v('提交')])
        ]),
        _vm._v(' '),
        _c(
            'ul',
            _vm._l(_vm.list, function (item, index) {
                return _c('li', { key: index }, [
                    _vm._v('\n      ' + _vm._s(item) + '\n    ')
                ])
            }),
            0
        )
    ])
}

​ 可以发现,template其实最终转换为用JS描述的Dom结构(ToList_render),而这个结构其实就是虚拟节点(虚拟Dom)。并且v-model这类指令的标签,Vue自动给我们注册了input事件,值发生变化后自动赋值给绑定的数据。页面加载后,通过ToList_render函数,虚拟DOM就把JS-DOM渲染到了真实的DOM。

​ 那么既然是MVVM,数据发生变化,怎么更新到标签控件呢。上面的代码当然不能解释,阅读Vue源码其实可以大致发现,Vue采用了一个JS方法:Object.defineProperty(),通过vm的data对象,代理了真实data,从而感知data的get和set,然后通过上面的虚拟dom,改变具体的标签值,再通过虚拟DOM库Diff到真实Dom。

MVVM.png

React代码

    /*
    1)拆分组件: 拆分界面,抽取组件
    2)实现静态组件: 使用组件实现静态页面效果
    3)实现动态组件
        ① 动态显示初始化数据
        ② 交互功能(从绑定事件监听开始)
     */
    // 应用组件
    class App extends React.Component {
      constructor (props) {
        super(props)
        // 初始化状态
        this.state = {
          todos: ['吃饭', '睡觉', '打豆豆']
        }
        this.add = this.add.bind(this)
      }
      add (todo) {
        const {todos} = this.state
        todos.unshift(todo)
        //更新状态
        this.setState({todos})
      }
      render () {
        const {todos} = this.state
        return (
          <div style={{color:'red'}}>
            <TodoAdd add={this.add} count={todos.length} />
            <TodoList todos={todos} />
          </div>
        )
      }
    }

    // 添加todo组件
    class TodoAdd extends React.Component {
      constructor (props) {
        super(props)
        this.addTodo = this.addTodo.bind(this)
      }
      addTodo () {
        // 读取输入数据
        const text = this.input.value.trim()
        // 查检
        if(!text) {
          return
        }
        // 保存到todos
        this.props.add(text)
        // 清除输入
        this.input.value = ''
      }
      render () {
        return (
          <div>
            <h2>Simple TODO List</h2>
            <input type="text" ref={input => this.input=input}/>
            <button onClick={this.addTodo}>Add #{this.props.count}</button>
          </div>
        )
      }
    }
    TodoAdd.propTypes = {
      add: PropTypes.func.isRequired,
      count: PropTypes.number.isRequired
    }

    // todo列表组件
    class TodoList extends React.Component {
      render () {
        const {todos} = this.props
        return (
          <ul>
            {
              todos.map((todo, index) => <li key={index}>{todo}</li>)
            }
          </ul>
        )
      }
    }
    TodoList.propTypes = {
      todos: PropTypes.array.isRequired
    }

    // 渲染应用组件标签
    ReactDOM.render(<App />, document.getElementById('root'))

​ 首先React入门解释就说明了一个问题,render函数里面返回的是一个JSX语法的代码,而JSX的作用就是用来描述html的DOM结构的,也就是说React在build的完后,其实JSX已经转换为JS结构的DOM描述。如果没法直观理解,可以参考下React官方介绍 jsx:

JSX.png

​ 那么页面加载完成后,ReactJS做的事情,似乎和Vue是一样的,把JS-Dom 挂载到真实页面,也就是Diff挂载。那么问题来了,React既然不是MVVM这类双向的数据绑定,自称是单向的,它单向又如何实现呢。第一次接触React,其实就发现了一个特点setState函数。个人感觉,这是一个设计局限问题,React没法像Vue那样自然的修改data的值,它为了实现数据变化改变视图,委婉的约定了改变数据用setState函数。其实setState里面就间接的完成了Vue里面的数据发生变化的感知过程(这当然是废话,用户直接调用函数去修改数据了,当然直接感知了)。setState里面重新通过render函数渲染了虚拟Dom,然后再diff更新到真实dom。React既然都委婉的采用了setState,那自然可以对setState下些功夫了,异步更新(队列式更新),合并更新,借此来宣称一些性能上的优化,当然也由此产生了各种不便和问题。

React-V.png

对比点

  • 数据驱动

都采用数据驱动视图的思想,上来就是data数据和state数据,让开发人员一目了然的明白,我们干的事情就是数据来驱动视图的变化,业务开发的核心是数据。

  • 模板&虚拟Dom

虽然虚拟Dom化,Vue从2.0版本才引入了虚拟Dom,但到目前为止两者这方面的思想是一致的。通过Template也好,或者JSX也好,做的同一件事情,把html标签搞成js描述的Dom结构。其实回头想想,也只能用javascript语言来描述这个dom。为啥?这样可以写很多逻辑在里面,而带逻辑的描述语言,在浏览器端时候也只有js合适。另外要配合差异化更新(diff),似乎也只有采用内存比较才高效。当然Diff算法,两者还是有差异的,但思想还是一样的。

  • MVVM&单向

我目前为止了解到的,React为啥只实现 了单向,可以理解为简单化,并非100%的数据都需要双向,而且单向的过程简单明了,况且开发人员可以自行实现双向绑定。而Vue的双向,就是MVVM的核心体现了,这是它的优势,当然的确也带来了很多便利,尤其是表单开发过程。

  • 组件化

说完MVVM和单向,组件化过程两者的确都实现了,但React的组件化才把组件的概念体现的淋漓尽致。甚至函数即组件的概念,能够直白的让开发员理解啥是组件。而Vue的组件明显是在MVVM的思想影响下形成的,少许牵强。说起组件化,不得不聊一点。开始学习React,发现到处都是js,连HTML标签也用js编写,会觉得变扭,但真正做项目写惯了,豁然感觉,这么写比Vue更直白,更爽。当然也正是因为组件化,React的难度比Vue大,比如ES6的类组件,什么高阶函数啊,入门过程比较漫长。

  • 代码风格
    Vue文件把html\js\css按照三块进行组织,这种风格把数据和视图分开的很直接,而React就一个JS,html部分只是Render函数的返回值(JSX),似乎感觉分离,但似乎有合在一起。所以站在代码风格角度Vue的感觉更合理,但站在组件化的角度React的合并又更贴切。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容