Vue与React的用法差异

247 阅读5分钟

由于最近工作的变化,直接由React转为Vue,所以就有了这篇博客,主要是为了记录,好记性不如烂笔头嘛

由于才上手写博客,所以排版不易阅读,请耐心享用

对于内容方面,请指正,勿苛责~

1.基础语法

1.1 插值表达式

React:{this.state.msg}
Vue:{{msg}}

1.2 判断输出

React:<p v-if="seen">123</p>
Vue:{this.state.seen?<p>123</p>:""}

1.3 属性绑定

React:<div title = {this.state.name}></div>
Vue:<div :title="name"></div>  //还可以绑定自定义的属性,将title改为你自定义的属性
,name为属性值,即data里的值

1.4 仅渲染一次(虽然我不是很明白它的应用场景)

React:render渲染,渲染中不产生变化
Vue:<span v-once>{{msg}}</span>

1.5 DOM插入

React:{this.state.DOM}//DOM在数据中心的形式为(<div>...</div>)
Vue:<span v-html="Html"></span>

1.6 点击事件

React:<a onclick = {this.doSomething}>...</a>//doSomething为箭头函数,this不变,不需要手动绑定
若非箭头函数,则<a onclick = {this.doSomething.bind(this)}>...</a>
Vue:<a @click="doSomething">...</a>//click也可改为你自定义事件

1.7 监听事件

React:
<input onChange={this.changeName}>
changeName = (e) =>{
	console.log(e.target.value)
}
Vue:
<input v-on:nameValue="getNameValue" />
//v-on简写  :nameValue
<script>
methods:{
	getNameValue:function(value){
		console.log(value)
	}
}
</script>

1.8 修饰符

React:
<form onSubmit={this.handleSubmit}>...</form>
handleSubmit = (event) =>{
	event.preventDefault();
}
Vue:<form v-on:submit.prevent="onSubmit">...</form>

1.9 表单绑定

React:
<input value={this.state.msg} onChange={this.changeValue}>
changeValue = (e)=>{
	this.setState({
		msg:e.target.value
	})
}
Vue:<input v-model="msg">

1.10 列表渲染

React:
<ul>
	this.state.items.map((item,index)=>{
	return (
		<li key={index}>
			{item}
		</li>
	)
})

this.state = {
	items:[
		{message:'Foo'},
		{message:'Bar'}
	]
}
Vue:
<ul>
	<li v-for="(item,index) in items" :key="index">
		{{item.message}}
	</li>
</ul>

data:{
	items:[
		{message:'Foo'},
		{message:'Bar'}
	]
}

1.11 状态管理

React:数据由state属性管理,但不能直接改变state的状态,state对象在react应用中不可变的, 需要使用setState方法更新状态

export default class App extends Component{
	constructor(props){
		super(props);
		this.state = {iptVal:0}
	}
	componentDidMount(){
		this.setState({
			iptVal:1
		})
	}

	render(){
		return (
			<div>{this.state.iptVal}</div>
		)
	}

}

Vue:数据由data属性在Vue对象中进行管理,可以通过this.a进行改变


在Vue实例中:new Vue({
	data:{
		a:1
	}
})
在组件化的项目中:
export default{
	data(){
		return {
			a:1
		}
	}
}
//使用return包裹后数据中的变量只在当前组件中生效,不会影响其他组件,从而避免数据在全局可见,造成污染

1.12 组件间通信

React:
父传子:子组件通过this.props来获取
父:
...
<Child toChild = "zxc"/>
...
子:
在子组件内,均可以通过this.props.toChild来获取"zxc"这个字符串

子传父:子组件通过调用父组件传给子组件的方法完成传值
父:
...
getChildValue = (value) =>{
	console.log(value)  //-----子组件传来的ldh
}
<Child toChildFun = {this.getChildValue} />
...
子:
...
toParent = () = >{
	this.props.toChild('ldh')
}
<button onClick = {this.toParent}>..</button>

非父子组件:需要一个公共的父组件,然后通过两两之间的父子关系完成数据的传输
Vue:
父传子:通过父组件绑定自定义属性(或通过v-bind绑定动态属性),子组件使用props选项时显式声明props,
以便它可以从父组件接收到期望的数据
子:
<template>
	<div><p>{{message}}</p></div>
</template>
<script>
export default{
	props:["message"]
}
</script>
父:
<template>
	<div>
		<child :message="hello"></child>
	</div>
</template>
<script>
import child from './child'
export default {
	name:'father',
	data(){
		return {
			hello:"zczxczxc"
		}
	},
	components:{
		child
	}
}
</script>

子传父:通过父组件绑定自定义事件,子组件通过this.emit('自定义事件',value)传值。在上面的基础上修改
子:
<template>
	<div>
		<p>{{message}}</p>
		<button @click = "ToParent">事件触发</button>
</div>
</template>
<script>
export default{
	props:["message"],
	methods:{
		ToParent:function(){
			this.$emit("getFormChild","abcabc")
		}
	}
}
</script>
父:
<template>
	<div>
		<child :message="hello" v-on:getFormChild="showDataFromChild"></child>
	</div>
</template>
<script>
import child from './child'
export default {
	name:'father',
	data(){
		return {
			hello:"zczxczxc"
		}
	},
	components:{
		child
	},
	showDataFromChild:function(data){
		console.log(data)
	}
}
</script>

非父子:可以使用一个空的Vue实例绑定在Vue实例的原型上作为一个事件总线中心,用emit触发事件,on监听事件。
公共实例文件bus.js,作为公共数控中央总线
import Vue from 'vue';
export default new Vue();
第一个组件first.vue
import Bus from './bus.js'
export default {
	name:'first',
	data(){
		return {
			value:'我来自first.vue组件!'
		}
	},
	methods:{
		add(){
			Bus.$emit('txt',this.value);
		}
	}
}

第二个组件second.vue

import Bus from './bus.js'
export default {
	name:'second',
	data(){
		return {
		}
	},
	mounted:function(){
		Bus.$on('txt',function(val){
			console.log(val)
		});
	}
}

1.13 指令函数

React:这部分,我只是觉得,React的组件化模式有点像,通过exports default xxxx暴露,
通过import直接引入,然后使用<xxxx />进行调用
Vue:
注册:
//全局
Vue.directive('focus',{
	//当被当定的元素插入到DOM中时...
	inserted:function(el){
		el.focus()//聚焦元素
	}
})
//组件
directives:{
	focus:{
		inserted:function(el){
			el.focus()
		}
	}
}
使用
<input v-focus />

总结:对于基础语法,两者的共同点都是去更改数据中心的数据(data()/this.state),不同点是Vue提供了更多的指令以及修饰符等,在开发过程中提供方便


2.书写模板

Vue:它把html、css、js组合到一起,用各自的处理方式,允许开发者声明式地将数据绑定到DOM元素上。Vue.js的核心是一个允许你采用简洁的模板语法来声明式地将数据渲染进DOM的系统,例:

<template>
	<div>
		<button @click = "clickbtn">{{message}}</button>
</div>
</template>
<script>
export default{
	name:'Test',
	data(){
		return{
			message:'今日事,今日毕'
		}
	}
	methods:{
		clickbtn:function(){
			this.message='改变了'
		}
	}
}
</script>

React:使用JSX模板,也就是说HTML语言直接写在JavaScript语言之中,不加任何引号,它允许HTML与JavaScript的混写,例:

import { Component } from 'react'
export default class Button extends Component{
	constructor(props){
		super(props);
		this.state = {label:'按钮'}
	}
	btnClick = () =>{
		this.setState({
			label:'改变了'
		})
	}

	render(){
		return (
			<button onClick = {this.btnClick} >{this.state.label}</button>
		)
	}

}

3.生命周期

React:

常用生命周期

  • 挂载阶段:constructor => render => componentDidMount =>结束
  • 更新阶段:render => componentDidUpdate =>结束
  • 卸载阶段:componentWillUnmount =>结束

完整生命周期

Mounting,挂载阶段,涉及4个钩子函数: 1.constructor():加载的时候调用一次,可以初始化state

2.static getDerivedStateFromProps(props,state):组件每次被render的时候,包括在组件构建之后(虚拟dom之后,实际dom挂载之前),每次获取新的props或state之后,每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state;配合componentDidUpdate,可以覆盖componentWillReceiveProps的所有用法

3.render():react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行

4.componentDidMount():组件渲染之后调用,只调用一次

Updating,更新阶段,涉及5个钩子函数: 1.static getDerivedStateFromProps(props,state):组件每次被render的时候,包括在组件构建之后(虚拟dom之后,实际dom挂载之前),每次获取新的props或state之后,每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state;配合componentDidUpdate,可以覆盖componentWillReceiveProps的所有用法

2.shouldComponentUpdate(nextProps,nextState):组件接收到新的props或者state时调用,return true就会更新dom(使用diff算法更新),return false能阻止更新(不调用render

3.render()

  1. getSnapshotBeforeUpdate(prevProps,prevState):触发时间,update发生的时候,在render之后,在组件dom渲染之前,返回一个值,作为componentDidUpdate的第三个参数;配合componentDidUpdate,可以覆盖componentWillUpdate的所有用法

5.componentDidUpdate():组件加载时不调用,组件更新完成后调用

Unmounting,卸载阶段,涉及1个钩子函数: 1.componentWillUnmount:组件渲染之后调用,只调用一次

Vue

完整生命周期如下:

beforeCreate:在实例初始化之后,数据观测(data observer)和event/watcher事件配置之前被调用

created:实例已经创建完成之后被调用。在这一步,实例已完成以下配置:数据观测,属性和方法的运算,event/watch事件回调。然而,挂载阶段还没开始,$el属性目前不可见

beforeMount:在挂载开始之前被调用:相关的render函数首次被调用

mounted:el被新建的vm.$el替换,并挂载到实例上去之后调用该钩子,平时接口的调用也是放在这边。

beforeUpdate:数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。

updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件DOM已经更新,所以你现在可以执行依赖于DOM的操作。然而在大多情况下,你应该避免在此期间更改状态,因为着可能会导致更新无限循环。该钩子在服务器渲染期间不被调用

beforeDestory:实例销毁之前调用,在这一步,实例仍然完全可用。

destroyed:Vue实例销毁后调用。调用后,Vue实例指示的所有东西都会解绑,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用


4. 数据中心(框架)

React-->dva

1.state:是储存数据的地方,且数据不可变,每次都是用新的state替换旧的state,通过connect包裹组件,获取数据

import { connect } from 'dva'

function  mapStateToProps(state){
	return {	todos:state.todos}
}
connect(mapStateToProps)(App)
//mapStateToProps函数会返回一个对象,用于建立state到props的映射关系

2.action:是用来描述UI层事件的一个对象,即变化的数据

dispatch是一个函数方法,用来将Action发送给state

dispatch({
	type:'abc',		//type为action名称,namespace/action名称
	payload:this.state.form		//变化的数据
})

3.Effect:action处理器,处理异步任务,相当于Vuex中的action,处理完成后同样需要发送类似action到reducer中

function *qwe(action,{ put,call }){
	yield call (delay,1000);
	yield put ({type:'add'});
}
dva提供多个effect函数内部的处理函数,比较常用的是call和put
call:执行异步函数
put:发出一个action,类似于dispatch

4.reducer:action处理器,用来处理同步操作,相当于Vuex的Mutation

//往{todos:[],loading:true}里添加一个新todo,并标记loading为false
function addTodo(state,action){
	return {
		...state,
		todos:state.todos.concat(action.payload),
		loading:false
	}
}

5.Model:dva应用的最简结构,根节点下可以包含多个Model,每个Model对应一个page或一个文件夹等,但根节点只有一个Model,即所有数据最后会汇聚到根节点下的Model,一个dva即Model,至少包含:

namespace:当前Model的名称。整个应用的state,由多个小的Model的state以namespace为key合成 state:该Model当前的状态。数据保存再这里,直接决定了视图层的输出 reducers:action处理器,处理同步动作,用来算出最新的state effects:action处理器,处理异步动作

{
	namespace:'count',//用于action寻找
	state:0,
	reducers:{
		add(state){ return state + 1}
	},
	effects:{
		*abc(action,{ call,put }){
			yield call(delay,1000)		
			yield put({type:'add'})		
		}
	}
}

Vue-->Vuex

1.store:同样是数据中心,单个数据组件通过this.$store访问到所需数据,数据过多可以使用mapState辅助函数,返回的是一个对象,通过展开运算符与其他数据进行混合,如下:

mapState({
	count: state => state.count,
	//传字符串参数'count'等同于state=>state.count
	countAlias:'count',
	//为了能够使用this获取局部状态,必须使用常规函数,也可以进行数据的计算
	countPlusLocalState(state){
		return state.count + this.localCount
	}
})

Getter方法,store中对数据进行计算处理的公共方法,getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算,例:

export default {
	computed:{
		//使用对象展开运算符将getter混入computed对象中
		...mapGetters([
			'doneTodosCount',
			'anotherGetter'
		])
	}
}
在store中:store.getters.doneTodos进行获取
在组件中:this.$store.getters.doneTodosCount

2.Mutation:同步任务处理中心,也是更改store中的状态的唯一方法,通过stire.commit('xxxx方法')

在同一model内:
store.commit({
	type:'abc',
	amount:10//也可以写成payload
})
发送对应的Mutation方法
在组件内:
this.$store.commit('xxxx')
或通过映射来进行调用:

methods:{
	...mapMutations([
		'abc',//将this.abc映射为this.$store.commit('abc')
	])

	...mapMutations([
		'abc',//将this.abc(参数)映射为this.$store.commit('abc',参数)
	])

	...mapMutations([
		add:'abc',//将this.add映射为this.$store.commit('abc')
	])
}

3.action:处理异步请求,然后再调用Mutation,commit更新store,例:

在组件内:
this.$store.dispath('xxxx')派发action
或通过映射来进行调用:

methods:{
	...mapActions([
		'abc',//将this.abc映射为this.$store.dispath('abc')
	])

	...mapMutations([
		'abc',//将this.abc(参数)映射为this.$store.dispath('abc',参数)
	])

	...mapMutations([
		add:'abc',//将this.add映射为this.$store.dispath('abc')
	])
}

5.不完整mock数据层-service层-view层

React

mock数据层:通过模块的暴露(module.exports = {}),使mock数据的方法可以被调用,而实际的data在方法里return {}输出,最后主文件引入对应的文件,通过 const router = express.Router(); router.post('/api/login',(req,res)=>{ res.json(req.body) }) 以express充当服务器的角色,给对应的url以对应的方法(即数据),最后service通过url调用到数据 export function getTableListData(param){ return requset({ url:'/api/login', method:'get', params }) }

Vue

1.直接暴露一个数组,包含多个对象,如下: { url:'/aaa/bbb', type:'post', response:config =>{ return { code:'000', message:'success' } } } //中间经过index汇总mock数据,并加工形成标准形式,提供给mock-server对应的url提供给service层请求 2.service层通过request/url+type进行请求//一般request会进行二次封装,包括基础前缀路径,请求超时、请求拦截等 3.view层引入service的请求方法,然后调用: Testzxp({//这边写参数}).then(res=>{ this.message = res.data })