Vue与React都鼓励组件化应用,即将应用分拆成一个个功能明确的模块,每个模块之间可以通过合适的方式互相联系,但各自的实现略有不同。以下谈谈我的理解,如有不对,欢迎指正
在Vue组件中,有几个观念和React相差比较大,我觉得主要有以下这几点:
Vue组件分为全局注册和局部注册,在react中都是通过import相应组件,然后模版中引用
props是可以动态变化的,子组件也实时更新,在react中官方建议props要像纯函数那样,输入输出一致对应,而且不太建议通过props来更改视图
子组件一般要显示地调用props选项来声明它期待获得的数据。而在react中不必需,另两者都有props校验机制
每个Vue实例都实现了事件接口,方便父子组件通信,小型项目中不需要引入状态管理机制,而react必需自己实现
使用插槽分发内容,使得可以混合父组件的内容与子组件自己的模板
多了指令系统,让模版可以实现更丰富的功能,而React只能使用JSX语法
Vue增加的语法糖computed和watch,而在React中需要自己写一套逻辑来实现
Vue组件注册可分为全局注册和局部注册
全局注册
需在初始化根实例之前注册组件
//html
// 注册
Vue.component('my-component', {
template: 'A custom component!'
})
// 创建根实例
new Vue({
el: '#example'
})
通过Vue 实例/组件的实例选项 components 注册仅在其作用域中可用的组件。这种封装也适用于其它可注册的 Vue 功能,比如指令。
var Child = {
template: 'A custom component!'
}
new Vue({
// ...
components: {
// 将只在父组件模板中可用
'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 (
Type something:
);
}
}
export default Demo;
Vue中的props更灵活,对于class和Style特性,采用合并的策略,并且需要在子组件中显示声明props,相同的地方是都有props验证,单项prop数据流。
子组件要显式地用 props 选项声明它预期的数据,对于非 prop 特性,可以直接传入组件,而不需要定义相应的 prop。
Vue.component('child', {
// 在 JavaScript 中使用 camelCase
props: ['myMessage'],
template: '{{ myMessage }}'
})
//父组件,
通过v-bind 来动态地将 prop 绑定到父组件的数据。每当父组件的数据变化时,该变化也会传导给子组件
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
使用v-on绑定自定义事件,在子组件通过this.$emit(eventName) 触发事件
// 父组件模版
{{ total }}
//子组件代码
Vue.component('button-counter', {
template: '',
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(
)
}
}
//父组件
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} >
)
}
}
在React不存在插槽分发的概念,如果之前学过Angular,那就比较熟悉了,因React不存在slot元素,所以此节只讲述Vue的相关API。
单个插槽
除非子组件模板包含至少一个 插口,否则父组件的内容将会被丢弃。当子组件模板只有一个没有属性的插槽时,父组件传入的整个内容片段将插入到插槽所在的 DOM 位置,并替换掉插槽标签本身。
具名插槽
元素可以用一个特殊的特性 name 来进一步配置如何分发内容。多个插槽可以有不同的名字。具名插槽将匹配内容片段中有对应 slot 特性的元素。 同时也可以有一个默认插槽。
//app-layout 组件
//父组件模板
这里可能是一个页面标题
主要内容的一个段落。
另一个主要段落。
这里有一些联系信息
//渲染结果
这里可能是一个页面标题
主要内容的一个段落。
另一个主要段落。
作用域插槽是一种特殊类型的插槽,用作一个 (能被传递数据的) 可重用模板,来代替已经渲染好的元素。
//子组件
//父组件
hello from parent
{{ props.text }}
//渲染结果
hello from parent
hello from child
Vue有丰富的指令,但也有副作用即属性#kebabCase#得转成#kebab-case#写法,而React使用的jsx,本质还是在js上下文,所以不需要转换,对于JSX语法参考此文章
是带有 v- 前缀的特殊属性。指令属性的值预期是单个 JavaScript 表达式 (v-for 是例外情况)。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
//等于以下的语法糖
所以要让组件的 v-model 生效,需要在组件中声明如下:
//带破折号的key值需添加双引号,不然报错,故所有的对象key值都可以写成带引号
:class="{ 'market-no-tag': marketNoTag }"
v-show VS v-if
其他指令参考Vue文档
不应该使用箭头函数来定义computed和watch
Original message: "{{ message }}"
Computed reversed message: "{{ reversedMessage }}"
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
计算属性是基于它们的依赖进行缓存的。计算属性只有在它的相关依赖发生改变时才会重新求值。这就意味着只要 message 还没有发生改变,多次访问 reversedMessage 计算属性会立即返回之前的计算结果,而不必再次执行函数。而方法在重新渲染时将总会执行。
Reversed message: "{{ reversedMessage() }}"
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的行为来实现清晰的单向数据流。
我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=14ti7um6gsjlj