版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://cloud.tencent.com/developer/article/1347544
Vue与React都鼓励组件化应用,即将应用分拆成一个个功能明确的模块,每个模块之间可以通过合适的方式互相联系,但各自的实现略有不同。以下谈谈我的理解,如有不对,欢迎指正
在Vue组件中,有几个观念和React相差比较大,我觉得主要有以下这几点:
Vue组件注册可分为全局注册和局部注册
//html
<div id="example">
<my-component></my-component>
</div>
// 注册
Vue.component('my-component', {
template: '<div>A custom component!</div>'
})
// 创建根实例
new Vue({
el: '#example'
})
var Child = {
template: '<div>A custom component!</div>'
}
new Vue({
// ...
components: {
// <my-component> 将只在父组件模板中可用
'my-component': Child
}
})
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;
Vue中的props更灵活,对于class和Style特性,采用合并的策略,并且需要在子组件中显示声明props,相同的地方是都有props验证,单项prop数据流。
Vue.component('child', {
// 在 JavaScript 中使用 camelCase
props: ['myMessage'],
template: '<span>{{ myMessage }}</span>'
})
//父组件,
<!-- 在 HTML 中使用 kebab-case || React使用JSX语法,则不存在此问题-->
<child my-message="hello!"></child>
<div>
<input v-model="parentMsg">
<br>
<child v-bind:my-message="parentMsg"></child>
</div>
Reac的props更多的相对state而言,只有props无state的组件叫无状态组件,即在组件的定义中可以只有一个render方法,无生命周期概念,组件不用实例化。
父组件更新子组件的props,在子组件接收到新的props时, 通过在componentWillReceiveProps()生命周期中执行this.setState()来更新视图,但不会引起第二次渲染。
componentWillReceiveProps(nextProps) {
if(this.props.text !== nextProps.text) {
this.setState({
text: nextProps.text
});
}
}
每个 Vue 实例都实现了事件接口,而在React中需借助第三方插件,比如fbemitter
// 父组件模版
<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实例没有事件接口,一般会通过引入一个第三方插件来实现,但是父子组件的通信可以通过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>
)
}
}
在React不存在插槽分发的概念,如果之前学过Angular,那就比较熟悉了,因React不存在slot元素,所以此节只讲述Vue的相关API。
元素可以用一个特殊的特性 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>
Vue有丰富的指令,但也有副作用即属性#kebabCase#得转成#kebab-case#写法,而React使用的jsx,本质还是在js上下文,所以不需要转换,对于JSX语法参考此文章
是带有 v- 前缀的特殊属性。指令属性的值预期是单个 JavaScript 表达式 (v-for 是例外情况)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
<input v-model="something">
//等于以下的语法糖
<input
v-bind:value="something"
v-on:input="something = $event.target.value">
所以要让组件的 v-model 生效,需要在组件中声明如下:
<div v-for="item in items" :key="item.id">
<!-- 内容 -->
</div>
//带破折号的key值需添加双引号,不然报错,故所有的对象key值都可以写成带引号
:class="{ 'market-no-tag': marketNoTag }"
- v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。 同时惰性渲染,只有值变更才会开始渲染。
- v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。
- 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。其他指令参考
不应该使用箭头函数来定义computed和watch
<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('')
}
}
})
<p>Reversed message: "{{ reversedMessage() }}"</p>
methods: {
reversedMessage: function () {
return this.message.split('').reverse().join('')
}
}
computed: {
fullName: {
// getter
get: function () {
...
},
// setter
set: function (newValue) {
...
}
}
}
Vue 通过 watch 选项提供了一个更通用的方法,来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。
var vm = new Vue({
data: {
a: 1,
},
watch: {
a: function (val, oldVal) {
console.log('new: %s, old: %s', val, oldVal)
},
}
})
Vue提供了更多的语法糖来让开发更便利,比如props的动态实时更新、双向的数据绑定、指令系统,实例的事件接口等。而React的中心思想即状态驱动视图的更改,所有UI层的变更都尽量通过setState来触发, 通信方式通过UIAction的行为来实现清晰的单向数据流。