Vue与React的异同-组件(二)

版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://blog.csdn.net/wkyseo/article/details/79113260

Vue与React都鼓励组件化应用,即将应用分拆成一个个功能明确的模块,每个模块之间可以通过合适的方式互相联系,但各自的实现略有不同。以下谈谈我的理解,如有不对,欢迎指正

在Vue组件中,有几个观念和React相差比较大,我觉得主要有以下这几点:

  1. Vue组件分为全局注册和局部注册,在react中都是通过import相应组件,然后模版中引用
  2. props是可以动态变化的,子组件也实时更新,在react中官方建议props要像纯函数那样,输入输出一致对应,而且不太建议通过props来更改视图
  3. 子组件一般要显示地调用props选项来声明它期待获得的数据。而在react中不必需,另两者都有props校验机制
  4. 每个Vue实例都实现了事件接口,方便父子组件通信,小型项目中不需要引入状态管理机制,而react必需自己实现
  5. 使用插槽分发内容,使得可以混合父组件的内容与子组件自己的模板
  6. 多了指令系统,让模版可以实现更丰富的功能,而React只能使用JSX语法
  7. Vue增加的语法糖computed和watch,而在React中需要自己写一套逻辑来实现

0x01 组件的注册

1.Vue

Vue组件注册可分为全局注册和局部注册

  • 全局注册 需在初始化根实例之前注册组件
//html
<div id="example">
  <my-component></my-component>
</div>

// 注册
Vue.component('my-component', {
  template: '<div>A custom component!</div>'
})

// 创建根实例
new Vue({
  el: '#example'
})
  • 局部注册 通过Vue 实例/组件的实例选项 components 注册仅在其作用域中可用的组件。这种封装也适用于其它可注册的 Vue 功能,比如指令
var Child = {
  template: '<div>A custom component!</div>'
}

new Vue({
  // ...
  components: {
    // <my-component> 将只在父组件模板中可用
    'my-component': Child
  }
})

2. React

React组件没有全局注册和局部注册的概念,官方推荐以ES6的class来创建组件,调用通过import导入组件实例

import React from "react";

class Demo extends React.Component {
    constructor(props) {
        super(props);
        // 设置 initial state
        this.state = {
            text: props.initialValue || 'placeholder'
        };
        // ES6 类中函数必须手动绑定,也可在调用的时候绑定,或者通过箭头函数
        this.handleChange = this.handleChange.bind(this); 
    }

    handleChange(event) {
        this.setState({
            text: event.target.value
        });
    }

    render() {
        return (
            <div>
                Type something:
                <input onChange={this.handleChange}
               value={this.state.text} />
            </div>
        );
    }
}

export default Demo;

0x02 Props

Vue中的props更灵活,对于class和Style特性,采用合并的策略,并且需要在子组件中显示声明props,相同的地方是都有props验证,单项prop数据流。

1.Vue

  • 显示声明props 子组件要显式地用 props 选项声明它预期的数据,对于非 prop 特性,可以直接传入组件,而不需要定义相应的 prop。
Vue.component('child', {
  // 在 JavaScript 中使用 camelCase
  props: ['myMessage'],
  template: '<span>{{ myMessage }}</span>'
})

//父组件,
<!-- 在 HTML 中使用 kebab-case  ||  React使用JSX语法,则不存在此问题-->
<child my-message="hello!"></child>
  • 动态Prop 通过v-bind 来动态地将 prop 绑定到父组件的数据。每当父组件的数据变化时,该变化也会传导给子组件
<div>
  <input v-model="parentMsg">
  <br>
  <child v-bind:my-message="parentMsg"></child>
</div>
  • 其他的特性还有对于javascript类对象传递应使用动态语法,非prop特性和修饰符.sync的应用

2.React

Reac的props更多的相对state而言,只有props无state的组件叫无状态组件,即在组件的定义中可以只有一个render方法,无生命周期概念,组件不用实例化。

  • 对应于Vue的动态prop,React的实现要复杂些

父组件更新子组件的props,在子组件接收到新的props时, 通过在componentWillReceiveProps()生命周期中执行this.setState()来更新视图,但不会引起第二次渲染。

componentWillReceiveProps(nextProps) {
		if(this.props.text !== nextProps.text) {
			this.setState({
            text: nextProps.text
       		 });
    	}
}

0x03 自定义事件

每个 Vue 实例都实现了事件接口,而在React中需借助第三方插件,比如fbemitter

  • Vue中父子组件通信 使用v-on绑定自定义事件,在子组件通过this.$emit(eventName) 触发事件
// 父组件模版
<div id="counter-event-example">
  <p>{{ total }}</p>
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>

//子组件代码
Vue.component('button-counter', {
  template: '<button v-on:click="incrementCounter">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    incrementCounter: function () {
      this.counter += 1
      this.$emit('increment')
    }
  },
})

//父组件实例
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})
  • React父子组件通信

React实例没有事件接口,一般会通过引入一个第三方插件来实现,但是父子组件的通信可以通过props来实现,在父组件中传递callback的prop形式,然后在子组件中触发此回调

//子组件
class Child  extends Component { 
  handle (e) {    
  //回调函数传递参数给父组件
    this.props.onChange(e.target.value);
  }
  render(){
     return(
		<input type="text" onChange={this.handle.bind(this)}>
       )
  }
}

//父组件
class Parent extends Component{ 
  constructor(props){
     super(props);
     this.handleChildChange=this.handleChildChange.bind(this);
  }
  handleChildChange(value){
    if(value){
          this.setState({value:value});
    }
  }

  render() {
      return (
		< Child onChange={this.handleChildChange} ></Child>
           )
  }
}

0x04 插槽分发内容

在React不存在插槽分发的概念,如果之前学过Angular,那就比较熟悉了,因React不存在slot元素,所以此节只讲述Vue的相关API。

  • 单个插槽 除非子组件模板包含至少一个 插口,否则父组件的内容将会被丢弃。当子组件模板只有一个没有属性的插槽时,父组件传入的整个内容片段将插入到插槽所在的 DOM 位置,并替换掉插槽标签本身。
  • 具名插槽

元素可以用一个特殊的特性 name 来进一步配置如何分发内容。多个插槽可以有不同的名字。具名插槽将匹配内容片段中有对应 slot 特性的元素。 同时也可以有一个默认插槽。

//app-layout 组件
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>

//父组件模板
<app-layout>
  <h1 slot="header">这里可能是一个页面标题</h1>
  <p>主要内容的一个段落。</p>
  <p>另一个主要段落。</p>
  <p slot="footer">这里有一些联系信息</p>
</app-layout>

//渲染结果
<div class="container">
  <header>
    <h1>这里可能是一个页面标题</h1>
  </header>
  <main>
    <p>主要内容的一个段落。</p>
    <p>另一个主要段落。</p>
  </main>
  <footer>
    <p>这里有一些联系信息</p>
  </footer>
</div>
  • 作用域插槽

作用域插槽是一种特殊类型的插槽,用作一个 (能被传递数据的) 可重用模板,来代替已经渲染好的元素。

//子组件
<div class="child">
  <slot text="hello from child"></slot>
</div>

//父组件
<div class="parent">
  <child>
    <template slot-scope="props">
      <span>hello from parent</span>
      <span>{{ props.text }}</span>
    </template>
  </child>
</div>

//渲染结果
<div class="parent">
  <div class="child">
    <span>hello from parent</span>
    <span>hello from child</span>
  </div>
</div>

0x05 指令

Vue有丰富的指令,但也有副作用即属性#kebabCase#得转成#kebab-case#写法,而React使用的jsx,本质还是在js上下文,所以不需要转换,对于JSX语法参考此文章

Vue指

是带有 v- 前缀的特殊属性。指令属性的值预期是单个 JavaScript 表达式 (v-for 是例外情况)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM

  • v-model 在表单控件或者组件上创建双向绑定
<input v-model="something">

//等于以下的语法糖
<input
  v-bind:value="something"
  v-on:input="something = $event.target.value">

所以要让组件的 v-model 生效,需要在组件中声明如下:

  1. 接受一个 value prop
  2. 在有新的值时触发 input 事件并将新值作为参数(this.$emit(‘input’, value))
  • v-for 尽可能在使用 v-for 时提供 key,除非遍历输出的 DOM 内容非常简单,或者是刻意依赖默认行为以获取性能上的提升。
<div v-for="item in items" :key="item.id">
  <!-- 内容 -->
</div>
  • v-bind 动态地绑定一个或多个特性,或一个组件 prop 到表达式。简写" : "
//带破折号的key值需添加双引号,不然报错,故所有的对象key值都可以写成带引号
:class="{ 'market-no-tag': marketNoTag }"
  • v-show VS v-if
    • v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。 同时惰性渲染,只有值变更才会开始渲染。
    • v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
    • 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
  • 其他指令参考Vue文档

0x06 Vue的computed和watch

不应该使用箭头函数来定义computed和watch

  • 对于任何复杂逻辑,都应当使用计算属性,尽量不要在模版中进行js运算
<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    // 计算属性的 getter
    reversedMessage: function () {
      // `this` 指向 vm 实例
      return this.message.split('').reverse().join('')
    }
  }
})
  • 计算属性缓存 vs 方法 计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。而方法在重新渲染时将总会执行
<p>Reversed message: "{{ reversedMessage() }}"</p>
methods: {
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}
  • 计算属性也有set方法
computed: {
  fullName: {
    // getter
    get: function () {
    	 ...
    },
    // setter
    set: function (newValue) {
		...
    }
  }
}
  • watch

Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

var vm = new Vue({
  data: {
    a: 1,
  },
  watch: {
    a: function (val, oldVal) {
      console.log('new: %s, old: %s', val, oldVal)
    },
  }
})

0x07结束语

Vue提供了更多的语法糖来让开发更便利,比如props的动态实时更新、双向的数据绑定、指令系统,实例的事件接口等。而React的中心思想即状态驱动视图的更改,所有UI层的变更都尽量通过setState来触发, 通信方式通过UIAction的行为来实现清晰的单向数据流。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏从零开始学 Web 前端

从零开始学 Web 之 DOM(七)事件冒泡

事件冒泡:当有多个元素嵌套,并且这些元素绑定了相同的事件,这时候如果里面的元素事件触发了,那么外面的事件会自动触发。

10030
来自专栏十月梦想

一天带你入门到放弃vue.js(二)

至此在vue中提供了计算属性,computed,在main.js指定computed对象中指定计算

25020
来自专栏互联网杂技

React中的-- 数据流

简介 React的组件简单理解起来其实就是一个函数,这个函数会接收props和state作为参数,然后进行相应的逻辑处理,最终返回该组件的虚拟DOM展现。在Re...

37790
来自专栏www.96php.cn

Discuz后台常用函数详解

当您在编写后台时,需要对几个常用后台显示函数进行详细的了解  下面的函数讲解按照重要性、常用性进行排序 目录 ---- showsetting()表单显示  c...

80350
来自专栏编程语言

Python:pygame的初步使用

4.创建窗口,pygame.display.set_mode(resolution=(0,0),flags=0,depth=0),resolution窗口大小,...

8900
来自专栏天天

js事件对象相关随记

13830
来自专栏Flutter知识集

Flutter实现雨滴动画

写了几个Flutter的demo,但是对Flutter的自定义view和动画都不太了解,看到一个类似效果在android的实现,就尝试用Flutter做一下。同...

1.1K50
来自专栏葡萄城控件技术团队

Spread for Windows Forms高级主题(6)---数据绑定管理

自定义列和区域的数据绑定 当表单被绑定到一个数据集时,表单中的列就会相继的被分配到数据集的区域上。例如,第一个数据域分配给列A,第二个数据区域分配给列B,等等。...

221100
来自专栏前端说吧

Vue-自定义事件之—— 子组件修改父组件的值

36750
来自专栏静默虚空的博客

jQuery 事件

什么是事件 页面对不同访问者的响应叫做事件。 事件处理程序指的是当 HTML 中发生某些事件时所调用的方法。 常见 DOM 事件: 鼠标事件 ...

23270

扫码关注云+社区

领取腾讯云代金券