关于react与vue的一些对比(上)

一、背景

Vue

        Google前端工程师尤雨溪于2014年创建了这个框架,Vue是一套用于构建用户界面的渐进式框架,与其他大型框架不同的是,Vue被设计为可以自底向上逐层应用,Vue核心时只关注视图层,不仅易于上手,还便于与第三方库或既有的项目整合

React

        与Vue不同,react库是由FaceBook创建的,最初是为了FackBook广告流量管理创建的,那是FackBook遇到了维护和编码方面的问题,它以动态创建和交互UI的能力而闻名

二、核心思想

        vue和react都是推崇组件式的开发理念,但是在设计的核心思想上有很大的差别

Vue

        Vue的整理思想仍然是拥抱经典的html(结构)+css(表现)+jd(行为)的形式,Vur鼓励开发者使用template模板,并提供指令供开发着使用(v-if、v-show、v-for等等),因此开发vue应用又是会有一种在写经典web应用(结构、表现、行为分离)的感觉。另一方面,在针对组件数据上,Vue2.0通过Object.defineProperty对数据做到了更细致的监听,景区实现组件级别的更新。

React

        react整体上是函数式的思想,组件使用jsx语法,all in js,将html与css全部融入javaScript,jsx语法相对来说更加灵活,刚开始从Vue转React的时候我也不是很适应,觉得react的写法很自由。当组件调用setState或props变化的时候,组件内部render会重新渲染,子组件也会随之重新渲染,可以通过shouldComponentUpdate或者PrueComponent可以避免不必要的重新渲染

三、组件形式

Vue

        vue组件是使用.vue文件来表示,vue组件将html、css、js组合到一起,模板部分使用数据使用{{}},形式如下:

// 模板(html)
<template>
  <div>{{name}}</div>
</template>

// 数据管理(js)
<script>
export default {
  name: 'NewComponent',
  data() {
    return {
      name: 'xx'
    }
  }
}
</script>

// 样式(css)
<style scoped>

</style>

组件的使用:

<new-component name="xx" />

react

        react推荐使用jsx或者js文件来表示组件,react支持class组件和function组件,react中是使用{}包裹变量,且需要注意的是,组件名称必须大写字母开头,React会将以小写字母开头的组件视为原生Dom标签,例如<div />代表html的div标签,而<Welcom />则代表一个组件,并且需在作用域使用Welcome

1)class组件

import React from 'react';

class NewComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'xx'
    };
  }
  render() {
    rerurn(<div>{name}</div>);
  }
}

export default NewComponent;

2)function组件

import React, { useState } from 'react';

function NewComponent() {
  const [name, setName] = useState('');
  return (<div>{name}</div>);
}

export default NewComponent;

四、数据管理(props、data、state)

组件中数据管理通常包含2部分,来自父组件的数据props与自身的数据

vue与react中的props都是单项数据流的,父级prop的更新会向下流动到子组件中,但是反过来不行。props可以是数组或对象,用于接收来自父组件的数据。

Vue

Props

vue中的的props支持传递静态或者动态props,静态props一般传递字符串

<blog-post title="My journey with Vue"></blog-post>

静态prop传递布尔值true可以这样写,传值false仍然需要使用动态prop传值

<blog-post disabled></blog-post>

动态赋值使用v-bind,可以简写为:

<blog-post v-bind:title="tile"></blog-post>
// 简写形式
<blog-post :title="tile"></blog-post>

动态prop常用来传递对象、数组、布尔值(false值,true值可以直接传属性 )等

<blog-post :title="post.title + ' by ' + post.author.name"></blog-post>

data

vue中使用data来管理组件的数据,vue将会递归将data数据转为getter/setter,从而让data的属性能够响应数据变化。对象必须是纯粹的对象(含有零个或多个key/value对)。一旦观察过,不需要再次在数据对象上添加响应式属性。因此推荐在创建实力之前,就声明所有的根级响应式属性。

当一个组件被定义,data必须声明为返回一个初始数据对象的函数

export default {
  name: 'NewComponent',
  data() {
    return {
      name: 'xxx',
      age: 12
    }
  }
}

当需要在组件内修改数据时,可以直接通过vue实力来修改:

  methods: {
    changeName() {
      this.name = 'new Name';
    }
  }

React

props

react中的props也与vue一样可以传递静态或动态props,静态props一般传递字符串

函数组件和class组件都可以使用props,函数组件使用props参数获取父组件传递的props

1)函数组件获取props

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;

2)class组件获取props(通过this.props获取)

class Welcome extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    const { name } = this.props;
    return <div>{name}</div>;
  }
}

动态的props

<Welcome name={name} />

state

react中使用state来管理组件内的数据,hooks的出现使用函数组件也具备管理state的能力

1)class组件中的state

class组件在构造函数(constructor)中定义组件内数据(state),修改数据必须通过setSate修改,不能直接修改state,这点非常重要。

class Welcome extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'xx'
    };
    this.changeName = this.changeName.bind(this);
  }

  changeName() {
    this.setState({
      name: 'new name'
    });
  }

  render() {
    const { name } = this.state;
    return <div onClick={this.changeName}>{name}</div>;
  }
}

关于class组件的setState有以下需要注意点:

  • setState更新是异步的,但是在setTimeout和原生时间中式同步的

  • setState更新式组件部分数据,更新机制式合并数据

  • 当需要使用上一个state值时,可以让setSate()接收一个函数而不是一个对象。这个函数用上一个state作为第一个参数,将此次更新被应用时的props作为第二个参数:

this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

2)function组件中的useState(hook)

react16.0之前函数组件知识纯的渲染组件,hooks的出现赋予了函数组件管理state的能力

useState返回一个state,以及更新state的函数,如果新的state需要通过使用先前的state计算得出,那么可以将函数传递给setState。该函数将接收先前的state,并返回一个更新后的值

import React, { useState } from 'react';

function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
    </>
  );
}

关于setState有一下注意点:

  • 与class组件中的setState方法不同,useState不会自动合并更新对象
  • 只能在函数最外层调用Hook,不要再循环、条件判断中胡总子函数中调用
  • 只能再React的函数组件或者自定义hook中调用hook,不要在其他JavaScript函数中调用

五、组件数据交互

组件数据交互指的是父子组件、兄弟组件、跨层组件之间传递数据。兄弟组件之间可以通过事件总线或者通过父组组件传递数据

1.父子组件数据交互(props+自定义事件 VS props+回调)

vue:props+自定义事件

react:props+回调

Vue

vue中父组件通过props传递数据给子组件,子组件使用$emit触发自定义事件,父组件中监听子组件的自定义事件获取子组件传递过来的数据

子组件中使用$emit传递自定义事件myEvent

<template>
  <div @click="changeName">{{name}}</div>
</template>

<script>
export default {
  name: 'NewComponent',
  data() {
    return {
      name: 'xxx',
      age: 12
    }
  },
  methods: {
    changeName() {
      this.name = 'new Name';
      this.$emit('myEvent', this.name);
    }
  }
}
</script>

父组件使用@myEvent监听自定义事件,回调参数是子组件传回的数据:

<template>
  <div>
    <new-component @myEvent="getName"></new-component>
  </div>
</template>

<script>
import NewComponent from './NewComponent';

export default {
  components: {
    NewComponent
  },
  data() {
    return {}
  },
  methods: {
    getName(name) {
      console.log(name)
    }
  }
}
</script>

React

react中父组件使用props传递数据和回到函数给子组件,子组件通过props传下来的回调和桉树返回数据,父组件通过回调函数获取子组件传递上来的数据

子组件通过props接收夫罪案传下来的回调事件:

import React, { useState } from 'react';

function Children(props) {
  const { myEvent } = props;
  const [name, setName] = useState('xxx');

  const changeName = () => {
    setName('new name');
    myEvent('new name');
  };
  return <div onClick={changeName}>{name}</div>;
}

父组件通过回到事件获取子组件传递的参数:

function Parent() {
  const changeName = name => {
    console.log(name);
  };
  return <Children myEvent={changeName}></Children>;
}

2.跨组件数据交互(provide/inject VS Context)

vue和react都支持跨组件传递数据,vue中主要通provide/inject实现,react中主要是通过Context实现

Vue

vue中通过provide/inject在祖先组件向所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效

祖先组件中定义provide选项,provide选项应该是一个对象或者返回一个对象的函数

<template>
  <div>
    <new-component @myEvent="getName"></new-component>
  </div>
</template>

<script>
import NewComponent from './NewComponent';

export default {
  provide: { // 定义provide选项
    message: 'This is a big news'
  },
  components: {
    NewComponent
  },
  data() {
    return {}
  },
  methods: {
    getName(name) {
      console.log(name)
    }
  }
}
</script>

子组件通过inject选项获取祖先组件provide选项值,inject选项应该是一个字符串数组或者对象

<template>
  <div>{{message}}</div>
</template>

<script>
export default {
  name: 'Children',
  inject: ['message'],
  data() {
    return {}
  }
}
</script>

注意:provide和inject绑定并不是可响应式的,这是刻意为之,然而,如果你传入一个可监听的对象,那么其对象的属性还是可响应的。

react

Context提供了一个无需为每层手动添加props,就能在组件树间进行数据传递的方法

import React,{useState} from 'react'

//创建Context对象
const MyContext=React.createContect({theme:'black'})
function Parent(){
    const changeName=name=>{
        console.log(name);
    }
    //Context.provider向消费组件传值
    return (
        <MyContext.Provider value={{theme:'white'}}>
            <Children myEvent={changeName}></Children>
        </MyContext.Provider>
    )
}

消费组件获取Context的两种方式:

1)class组件通contextType获取最近Context上的那个值

class DeepChildren1 extends React.Component{
    constructor(props){
        super(props)
    }
    
    static contextType=MyContext;

    render(){
        return (
            <div>{this.context.theme}123</div>
        )
    }
}

2)函数组件通过Context.Consumer订阅到Context的变更

function DeepChildren(props){
    return (
        <MyContext.Consumer>
            {
                (value)=> (<div>{value.theme}</div>)
            }
        </MyContext.Consumer>
    )

}

关于Context需要注意:

  • 当Provider的父组件进行重新渲染时,consumers组件会重新渲染,并且没有办法避免,应该尽量避免使用Context

六、class与style

对于css中class与style处理上,vue与react也存在较大差异

Vue

vue对class与style特意做了增强,可以传字符串、对象、数组

class

1)给class绑定字符串

<div class="hello"></div>

2)给class绑定对象

<div class="static" "class="{'active':isActive,'text-danger':hasError}" ></div>

data如下:

data: {
  isActive: true,
  hasError: false
}

HTML将被渲染成:

<div class="static active"></div>

3)给class绑定数组

<div :class="[activeClass.errorClass]"></div>

data如下:

data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}

 HTML 将被渲染为:

<div class="active text-danger"></div>

style

style用来绑定内联样式,支持传对象、数组、使用需要添加浏览器引擎的css属性时,如transform、Vue.js会自动侦测并添加相应的前缀

1)传对象,css属性名可以用驼峰式或者短横线分割

<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

data如下:

data: {
  activeColor: 'red',
  fontSize: 20
}

HTML将被渲染为:

<div style="color: red; font-size: 20px;"></div>

2)传数组将多个样式应用到同一个元素上:

<div :style="[baseStyles,overriingStyles]"></div>

data如下:

baseStyles: {
  fontSize: '20px',
  color: 'blue'
},
overridingStyles: {
  height: '80px'
}

HTML将被渲染为:

<div style="font-size: 20px; color: blue; height: 80px;"></div>

react

react使用className用于指定css的class,react不能直接为组件指定class

className

react中的饿className一般传值字符串常量或者字符串变量,不能传递数组或者对象语法

1)传递字符串常量

function NewComponent(){
    return <div className="container">this is a new Component.</div>
}

2)传递字符串变量

function NewComponent(){
    const newClass='container'
    return <div className={newClass}>this is a new Component.</div>
}

3)传递多个class,可以使用es6的模板字符串实现

function NewComponent(){
    const newClass="container"
    return <div className={`${newClass} new-container`}>.....</div>
}

4)如果需要传递数组或者对象语法时,可以引入classnames库实现:

import classNames from 'classnnames';

function NewComponent() {
  const newClass = 'container';
  return <div className={classNames(newClass, 'newContainer', { bar: true }, ['new-class', { c: true }])}>This is New Component.</div>;
}

html将被渲染为:

<div class="container newContainer bar new-class c">This is New Component.</div>

style

通常不推荐将style属性作为设置元素样式的主要方式。在多数情况下,应使用className属性来引用外部css样式表中定义的class。style在react应用中多用于在渲染过程中添加动态计算样式

const divStyle={
    color:'blue',
    backgroundImage:'url('+imgUrl+')'
}

function HelloWordComponent(){
    return <div style={divStyle}>Hello World!</div>
}

注意:样式不会自动补齐前缀,如需支持旧版本浏览器,请手动补充对应对的样式属性:

const divStyle = {
  WebkitTransition: 'all', // note the capital 'W' here
  msTransition: 'all' // 'ms' is the only lowercase vendor prefix
};

function ComponentWithTransition() {
  return <div style={divStyle}>This should work cross-browser</div>;
}

七、生命周期

我们经常说的生命周期无谓就是组件的生命周期,一般包括:初始化,挂在、更新、卸载四个大阶段,接下来分别看看vue和react的生命周期

Vue

vue生命周期图示:

 React

react生命周期氛围16.0之前和16.0之后

16.0之前

 1)初始化阶段:constructor

        是class组件默认的方法,常用来初始化state或者设置属性等

class Counter extends React.component{
    construtor(props){
        super(props)
        this.state={
            count:0
        }
        this.color='red'
    }

}

2)挂在阶段

  • componentWillMount():组件挂在之前调用,并且只会调用一次
  • render:render是React组件必须定义的生命周期函数,用来渲染DOM。 并必须返回一个React元素(描述组件,即UI),不负责组件实际渲染工作,之后由React自身根据此元素去渲染出页面DOM

注意:不要在render里面修改state,会引起死循环导致卡死

  • componentDidMount():组件挂载完毕之后调用,在这个阶段可以获取真实Dom元素,宠用来发起异步请求获取数据

3)更新阶段:

但通过setState修改state或父组件重新render引起props更新,都会引起子组件的重新render

  • componentWillReceiveProps(nextProps):props发生变化以及父组件重新渲染时都会触发该生命周期函数。在该阶段可以通过参数nextProps获取变化后的props参数,通过this.props访问之前的props,该生命周期内可以进行setState
  • shouldComponentUpdate(nextProps,nextState):组件每次setState或者父组件chongxinrender都会引起子组件render,可以使用该钩子比较nextProps,nextState以及当前组件的this.props,this.state的状态来判断是否需要重新渲染。默认返回true(会重新渲染);返回false则不触发渲染。

一般我们通过该钩子来优化性能,避免子组件不必要的渲染

  • componentWillUpdate(nextProps,nextState):当组件收到新的props或者state时,就会渲染之前调用,使用此作为更新发生之前执行准备更新的机会。初始渲染不会调用该方法 

注意:不能在此方法中调用this.setState

  • componentDidUpdate(prevProps,prevState):此方法在组件更新后被调用。首次渲染不会执行才方法,当组件更新后,可以在此处对DOM进行操作

注意:可以在componentDidUpdate()中直接调用setState(),但是它必须被包裹在一个条件语句中,否则会导致死循环

 4)卸载阶段

  • componentWillUnmount():会在组件卸载及销毁之前直接调用,在此方法中执行必要的清理操作,例如:清除timer,取消网络请求或清除在componentDidMount()中创建的订阅等

注意:componentWillUnmount() 中不应调用 setState(),因为该组件将永远不会重新渲染。

 16.0之后

react16.0之后移除的生命周期函数:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

但是为了向下兼容,react并未删除这三个生命周期函数,新增“UNSAFE_”为前缀别名和的三个函数:UNSAFE_componentWillMount()UNSAFE_componentWillReceiveProps()UNSAFE_componentWillUpdate()

新增的生命周期函数

  • static getDerivedStateFromProps(nextProps,preState)
  • getSnapshotBeforUpdate(nextProps,prevState)

react16.0更新之后的生命周期函数总结:

(1)初始化阶段保持不变

(2)挂载阶段:getDerivedStateFromProps=>render=>componentDidMount

(3)更新阶段:getDerivedStateFromProps=>shouldComponentUpdate=>render=>getSnapshotBeforeUpdate=>componentDidUpdate

(4)卸载阶段保持不变

八、事件处理

vue和react在事件处理上用法也有所差异

Vue

vue中使用的是v-on指令的方式为元素绑定时间,并在出发时运行一些JavaScipt代码,通常使v-on接受一个需要调用的方法名称

1)直接绑定方法,不传递任何参数,回调函数参数是浏览器事件event对象

<div  @click="greet">Greet</div>

method:

  methods: {
    greet(event) {
      console.log(event);
    }
  }

2)内联调用方法

<div  @click="greet('hello')">Greet</div>
methods: {
  greet(message) {
    this.message = message;
  }
}

有时候也需要在method中访问原生DOM事件,可以讲$event显示传入method中

<div  @click="greet('hello', $event)">Greet</div>
methods: {
  greet(message, event) {
    this.message = message;
  }
}

3)事件修饰符和按键修饰符

Vue为事件添加了事件修饰和按键修饰符

事件修饰符:

在事件处理程序中调用event.preventDefault()或者event.stopPropagation()是非常常见的需求,为了解决这个问题,Vue为v-on提供了事件修饰符,修饰符是由.开头的指令后缀来表示的。

  • .stop:阻止事件继续传播
  • .prevent:阻止事件默认行为
  • .self:当前元素触发时才触发时间处理函数
  • .once:事件只触发一次
  • .passive:告诉浏览器你不想阻止事件默认行为,不能和prevent一起使用
<!-- 阻止单击事件继续传播 -->
<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>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即内部元素触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成  -->
<!-- 这其中包含 `event.preventDefault()` 的情况 --
<div v-on:scroll.passive="onScroll">...</div>

4)按键修饰符

在监听键盘事件时,我们经常要检查详细的按键。vue允许为v-on在监听键盘时间时添加修饰符,你可以直接将KeyBoardEvent.key暴露的任意有效按键名转换为kebab-case来做为修饰符

按键码:

  • .enter
  • .tab
  • .delete
  • .esc
  • .space
  • .up
  • .dowm
  • .left
  • .right

使用keyCode特性也是允许的

<input v-on:keyup.13="submit">

关于v-on处理事件的好处:
1.扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
2.因为你无须在 JavaScript 里手动绑定事件,你的 ViewModel 代码可以是非常纯粹的逻辑,和 DOM 完全解耦,更易于测试。
3.当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何清理它们。

 React

react元素的事件处理和DOM元素的很相似,但是有一点语法上的不同:

  • react事件的命名采用驼峰,而不是纯小写
  • 使用JSX语法时你需要传入一个函数作为事件处理函数,而不是一个字符串

1)事件处理程序不传递参数

在class类组件中使用回调函数,需要显示绑定this或者使用箭头函数

不传递参数时,默认参数是e,这是一个合成时间。React根据W3C规范来定义这些合成事件,所以你不需要担心跨浏览器的兼容问题

在React中不能同返回false的方式阻止默认行为,你必须显示的使用preventDefault

  • 显示绑定this的方式 
class NewComponent extends React.component{
    construtor(props){
        super(props)
        this.handleClick=this.handleClick.bind(this);//显示绑定this.handlClick
    }
    handleClick(e){
        e.preventDefault();
        console.log(e.target);
    }

    render(){
        return (
            <div onClick={this.handleClick}>Click Me</div>
        )
    }
}
  •  箭头函数的方式
class NewComponent extends React.component{
    construtor(props){
        super(props)
    }
    handleClick=(e)=>{
        e.preventDefault();
        console.log(e.target);
    }

    render(){
        return (
            <div onClick={this.handleClick}>Click Me</div>
        )
    }
}

2)事件处理函数传递参数

通常我们会为事件处理函数传递额外的参数,有两种方式向事件传递参数:

  • 箭头函数方式传递,事件对象e必须显示的进行传递
class NewComponent extends React.component{
    construtor(props){
        super(props)
        this.handleClick=this.handelClick.bind(this)
    }
    handleClick=(e)=>{
        e.preventDefault();
        console.log(e.target);
    }

    render(){
        return (
            <div onClick={(e)=>this.handleClick(e,'hello')}>Click Me</div>
        )
    }
}
  • 通过bind形式传递参数

e作为第二个参数传递,事件对象以及更对的参数将会被隐式的传递

class NewComponent extends React.Component {
  constructor(props) {
    super(props);
  }

  handleClick(message, e) { // e作为第二个参数
    e.preventDefault();
    console.log(message);
  };

  render() {
    return <div onClick={this.handleClick.bind(this, 'hello')}>Click me</div>;
  }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值