前言
上一期讲述了React和Vue 在文件结构、模板语法、数据绑定、组件数据交互、组件渲染方式、组件生命周期六大方面的对比。这一期将继续上一期的话题。
七、状态管理
在状态管理方面,虽然vue跟react采用的不同的框架,但是本质原理上是相同的。vue采用的是自研的,专门为 Vue.js 设计的状态管理库:vuex,随官方版本发布,稳定性能佳。而React则将这部分中间件交给了社区,集思广益。尤属Redux较为突出。
a) vue中的vuex
不采用action方式,最简单的全局状态管理:
- store中声明状态
import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
//--声明一个状态
current_level:1
},
mutations
})
- 声明修改状态的mutation
//--mutation.js
export default {
changeLevel(state,new_lv){
state.current_level = new_lv;
}
}
- 使用状态
对于全局状态,一般用computed计算出一个属性进行使用。可以像普通属性一样进行使用。
<template>
<div>
<h1>这是全局状态current_level的值:{{current_level}}</h1>
<button @click="incr()">让current_level+1</button>
</div>
</template>
<script>
import Child from "./Child";
export default {
data: function() {
return {
};
},
name: "State",
methods: {
incr(){
this.$store.commit('changeLevel',this.current_level+1);
}
},
watch:{
current_level(){
if(this.current_level == 10)
{
alert('状态为10')
}
}
},
computed:{
current_level(){
return this.$store.state.current_level
}
}
};
</script>
这样会导致一个问题。所有的状态修改都是同步的。我没法在mutation中处理异步事件。这时候我们可以采用action
。关于异步状态维护action
的展开,网上有很多实例。可以参阅这里。且接下来react redux的思想,其实就是action的实现。
b) react 中的 redux
就redux而言,我觉得最大的优点就是将状态机分开。每个reducer迭代某一类特定的状态。
1 .声明一个或多个reducer(状态机),可以将一个模块的状态统一写成一个reducer
//--login_reducer.js 这个reducer专门管理登录用户的相关状态
import { STORE_USER_NICNAME, STORE_USER_NAME } from './actions/login_action';
const initialState = {
userName: '',
userNikNmae: '',
};
export default function(state = initialState, action) {
switch (action.type) {
case STORE_USER_NAME: {
return {
...state,
userName: action.value,
};
}
case STORE_USER_NICNAME: {
return {
...state,
userNikNmae: action.value,
};
}
default:
return state;
}
}
//--some_other_reducer.js
import { SET_STATE_1, SET_STATE_2 } from './actions/some_other_action';
const initialState = {
other_state_1: 1,
other_state_2: 2,
};
export default function(state = initialState, action) {
switch (action.type) {
case SET_STATE_1: {
return {
...state,
other_state_1: action.value,
};
}
case SET_STATE_2: {
return {
...state,
other_state_2: action.value,
};
}
default:
return state;
}
}
上述reducer分别导出,再使用combineReducers函数,组合成一个状态机,赋值给store的create函数,完成store的初始化
import { combineReducers } from 'redux';
import LoginReducer from './login_reducer';
import OtherReducer from './other-reducer';
const allReducers = {
Login:LoginReducer,
Other:OtherReducer
};
const rootReducer = combineReducers(allReducers);
let store = createStore(rootReducer);
export default store;
2 .定义改变状态的action
这个函数表示一个动作,我需要去改变other_state_1值的一个动作
export const SET_STATE_1 = 'set_state_1';
export const SET_STATE_2 = 'set_state_2';
export const state1Change = (value) => ({
type: SET_STATE_1,
value,
});
export const state2Change = (value) => ({
type: SET_STATE_2,
value,
});
3 .在组件中使用
import React from 'react';
import store from '../store/store';
import { state1Change } from '../store/actions/some_other_action';
class StoreTest extends React.Component {
constructor(props) {
super(props);
this.state = {
};
store.subscribe((state) => {
//--注意这里。不写这个,无法更新视图。
this.setState({});
});
}
setGlobalState=() => {
const cur_st1 = store.getState().Other.other_state_1;
store.dispatch(state1Change(cur_st1 + 1));
}
render() {
const st1 = store.getState().Other.other_state_1;
return (
<div>
<h1>这是直接使用全局状态other_state_1的值:{st1}</h1>
<button type="button" onClick={this.setGlobalState}>other_state_1的值+1</button>
</div>
);
}
}
export default StoreTest;
react 中直接使用redux,一般情况是各个业务模块相互独立,互不影响。很少涉及到兄弟模块交互等情况。由示例可以看出,直接使用store中的值时,在store变化时,需要手动监听其变化,再判断自身state状态,更新视图,很麻烦。推荐使用react-redux
,我也有一篇博客详细介绍了这两者的区别。
八 使用场景
a)vue
i 期待模板搭建应用
Vue应用的默认选项是把markup放在HTML文件中。数据绑定表达式采用的是和Angular相似的mustache语法,而指令(特殊的HTML属性)用来向模板添加功能。
相比之下,React应用不使用模板,它要求开发者借助JSX在JavaScript中创建DOM。
ii 期待简单和“上手就能会”的东西
- 一个简单的Vue项目可以不需要转译直接运行在浏览器中,所以使用Vue可以像使用jQuery一样简单。当然这对于React来说在技术上也是可行的,但是典型的React代码是重度依赖于JSX和诸如class之类的ES6特性的。
Vue的简单在程序设计的时候体现更深,让我们来比较一下两个框架是怎样处理应用数据的(也就是state)。 - React中是通过比较当前state和前一个state来决定何时在DOM中进行重渲染以及渲染的内容,因此需要不可变(immutable)的state。
Vue中的数据是可变(mutated)的,所以同样的操作看起来更加简洁。
让我们来看看Vue中是如何进行状态管理的。当向state添加一个新对象的时候,Vue将遍历其中的所有属性并且转换为getter,setter方法,现在Vue的响应系统开始保持对state的跟踪了,当state中的内容发生变化的时候就会自动重新渲染DOM。令人称道的是,Vue中改变state的状态的操作不仅更加简洁,而且它的重新渲染系统也比React 的更快更有效率。
iii 期待应用尽可能的小和快
- 当应用程序的状态改变时,React和Vue都将构建一个虚拟DOM并同步到真实DOM中。 两者都有各自的方法优化这个过程。
- Vue核心开发者提供了一个benchmark测试,可以看出Vue的渲染系统比React的更快。测试方法是10000个项目的列表渲染100次。从实用的观点来看,这种benchmark只和边缘情况有关,大部分应用程序中不会经常进行这种操作,所以这不应该被视为一个重要的比较点。
- 但是,页面大小是与所有项目有关的,这方面Vue再次领先,它目前的版本压缩后只有25.6KB。
React要实现同样的功能,你需要React DOM(37.4KB)和React with Addon库(11.4KB),共计44.8KB,几乎是Vue的两倍大。
双倍的体积并不能带来双倍的功能。
b)react
i 期待构建一个大型应用程序
同时用Vue和React实现的简单应用程序,可能会让一个开发者潜意识中更加倾向于Vue。这是因为基于模板的应用程序第一眼看上去更加好理解,而且能很快跑起来。但是这些好处引入的技术债会阻碍应用扩展到更大的规模。模板容易出现很难注意到的运行时错误,同时也很难去测试,重构和分解。
相比之下,Javascript模板可以组织成具有很好的分解性和干(DRY)代码的组件,干代码的可重用性和可测试性更好。Vue也有组件系统和渲染函数,但是React的渲染系统可配置性更强,还有诸如浅(shallow)渲染的特性,和React的测试工具结合起来使用,使代码的可测试性和可维护性更好。
与此同时,React的immutable应用状态可能写起来不够简洁,但它在大型应用中意义非凡,因为透明度和可测试性在大型项目中变得至关重要。
i 期待同时适用于Web端和原生APP的框架
React Native是一个使用Javascript构建移动端原生应用程序(iOS,Android)的库。 它与React.js相同,只是不使用Web组件,而是使用原生组件。 如果你学过React.js,很快就能上手React Native,反之亦然。
它的意义在于,开发者只需要一套知识和工具就能开发Web应用和移动端原生应用。如果你想同时做Web端开发和移动端开发,React为你准备了一份大礼。
阿里的Weex也是一个跨平台UI项目,目前它以Vue为灵感,使用了许多相同的语法,同时计划在未来完全集成Vue,然而集成的时间和细节还不清楚。因为Vue将HTML模板作为它设计的核心部分,并且现有特性不支持自定义渲染,因此很难看出目前的Vue.js的跨平台能力能像React和React Native一样强大。
iii 期待最大的生态系统
毫无疑问,React是目前最受欢迎的前端框架。它在NPM上每个月的下载量超过了250万次,相比之下,Vue是65万次。人气不仅仅是一个肤浅的数字,这意味着更多的文章,教程和更多Stack Overflow的解答,还意味有着更多的工具和插件可以在项目中使用,让开发者不再孤立无援。
这两个框架都是开源的,但是React诞生于Facebook,有Facebook背书,它的开发者和Facebook都承诺会持续维护React。相比之下,Vue是独立开发者尤雨溪的作品。尤雨溪目前在全职维护Vue,也有一些公司资助Vue,但是规模和Facebook和Google没得比。不过请对Vue的团队放心,它的小规模和独立性并没有成为劣势,Vue有着固定的发布周期,甚至更令人称道的是,Github上Vue只有54个open issue,3456个closed issue,作为对比,React有多达530个open issue,3447个closed issue。
九、其他细节
1、 this指向问题
a) vue this指向唯一
不论在哪个生命周期钩子函数或者是自定义事件处理函数,this皆指向组件实例对象。不会造成this指向混乱的问题。
b) react 将this的指向交给了开发者维护
官档描述是为了开发者能跟定制事件处理函数中this的指向,但是大部分情况下,我们不得不采用一些奇怪的方式解决this指向的问题。
例如
this.handleClick = this.handleClick.bind(this)
或者
handleClick() {
}
render() {
return (
<div>
<input type="text" ref={this.myTextInput} />
<input type="button" value="Focus the text input" onClick={this.handleClick.bind(this)} />
</div>
);
}
或者
handleClick= ()=> {
}
render() {
return (
<div>
<input type="text" ref={this.myTextInput} />
<input type="button" value="Focus the text input" onClick={this.handleClick} />
</div>
);
}
或者可能造成内存泄漏的
handleClick() {
}
render() {
return (
<div>
<input type="text" ref={this.myTextInput} />
<input type="button" value="Focus the text input" onClick={()=>{
this.handleClick()
} />
</div>
);
}
2、 判断组件是否渲染的方式
a) vue 提供了v-if操作符 配合计算属性
<template>
<div>
<span v-if = "islogin">用户已登录</span>
<span v-if = "islogin_and_validate">用户已登录并且密码正确</span>
</div>
</template>
<script>
export default {
data: function() {
return {
islogin:true,
password_validate:false
};
},
computed:{
islogin_and_validate(){
return this.islogin && this.password_validate;
}
},
name: "home",
components: {},
mounted() {}
};
</script>
b)react 大量的三元表达式
import React from 'react';
class IfOrNot extends React.Component {
constructor(props) {
super(props);
this.state = {
islogin: true,
password_validate: false,
};
}
render() {
return (
<div>
{
!this.state.islogin ? <span>用户还未登录</span> : this.state.password_validate ? <span>用户登录并且密码正确</span> : <span>用户登录但密码不对</span>
}
</div>
);
}
}
export default IfOrNot;
3、计算属性的使用
a) vue 提供了computed关键字
在Vue中使用计算属性非常方便。计算属性也是通过依赖收集实现的。当你的计算属性中,使用了其他的属性或者计算属性,其他的依赖属性变化的时候,计算属性本身也会进行通知。
computed: {
boys() {
return this.org_data.filter(m => {
return m.agient === "boy";
});
},
boys_smoke() {
return this.boys.filter(m => {
return m.smoke;
});
},
boys_smoke_and_sao() {
return this.boys_smoke.filter(m => {
return m.sao;
});
}
},
b) react 暂时没有相关机制
不过可以通过如下实现简单的computed
get boys() {
const { org_data } = this.state;
return (
org_data.filter(m => m.agient === 'boy').map((m, index) => <h3 key={index}>{m.name}</h3>)
);
}
render() {
return (
<div>
<h1>男孩们</h1>
{this.boys}
<h1>抽烟的男孩们</h1>
{this.boys_smoke}
</div>
);
}
写在最后
实际上,React和Vue都是非常优秀的框架,它们之间的相似之处多过不同之处,并且它们大部分最棒的功能都是相通的。个人是Vue爱好者,本篇文章若涉及到一些问题还请各位博友留言指正。
2022.6.3日(长期有效):打个广告,苏州华为终端BG面向社会招聘人才,Java /C C++ / Python / Javascript 。有兴趣来苏州的同学们 可以加我V 15850277051 ,走内推流程,有问必答!