前言
由于我先使用react,所以react用法在我脑海根深蒂固,现在在写vue的时候我总是想如果用react的时候我会怎么做,然后查一下找到vue该如何使用。其实大致的思想差不多,但是用法上还是有点区别。
所以这篇文章就整理一下,react、vue两个框架在不同的场景下,用法的区别。希望这篇文章可以帮助你在react/vue之间自由切换。
1、 创建组件
React
React 有两种类型的组件,分别是functional component, class component,所以有两种定义方式:
functional component
function Test(props) { return (
{props.name}
)}ReactDOM.render( , document.getElementById('app'))
class component用构造函数 + 原型实现继承
// 注意:基于 `ES6` 中的class,需要配合 `babel` 将代码转化为浏览器识别的ES5语法// 安装:`npm i -D babel-preset-env`class Test extends React.Component() { constructor(props) { // super一定要写,继承React.Component super(props);// state在里面定义this.state = { name : 'daisy'}; } // class创建的组件中 必须有render方法,且显示return内容或者null render() { return (
{this.state.name}
) }}
React中要求组件名字一定是大写,这样来跟原生的html元素做区分。
Vue
分为局部注册跟全局注册
全局注册
Vue.component('my-component-name', { /* ... */ })
局部注册
var ComponentA = { /* ... */ }new Vue({ el: '#app', components: { 'component-a': ComponentA, }})
全局注册就是在注册之后,各个地方都可以使用。而局部注册就是只在这个vue 实例中才能使用。
2、数据绑定
React跟vue的思想都是开发者只需要去操作数据,至于数据在网页中如何更新,就是它们的事情了,这个就要靠数据绑定。
那么在React/vue中如何做到数据绑定呢。
React
采用JSX语法,变量用{}包裹即可。
const name = 'Josh Perez';const element =
Hello, {name}
;ReactDOM.render( element, document.getElementById('root'));
注: {}里字可以是js表达式,不能是语句,这个跟vue一样。
Vue
使用Mustache(插值语法) {{ }}从数据对象data中获取数据,当这个数据发生改变,插值处的内容就会更新。
注:{{ }}只能出现JS表达式,不能是js语句
Hello, {{ msg }}.
{{ message.split('').reverse().join('') }}
//错误。js语句不支持{{ var a = 1 }}
3、 模板语法
模板语法里分别从下面几个方面进行对比:
- 原始html
- 条件渲染
- html属性
- 绑定事件
React
原始html: 使用dangerouslySetInnerHTML
>}}>
条件渲染:使用三目,&&,如果逻辑在复杂一点就得用函数来帮忙了。
render() { const isLoggedIn = this.state.isLoggedIn; return (
isHappy ?
yes i am
:
no
);}render() { const isLoggedIn = this.state.isLoggedIn; return (
isShow &&
hello word
);}
html 属性:跟原生一样直接写不过要换成驼峰式tab-index => tabIndex,React 16以后还支持自定义属性。
绑定事件:直接写,但是改成驼峰onClick这样的形式,同时传入的不是字符串,而是传入一个函数作为事件处理函数
// 原生html Activate Lasers// React Activate Lasers
既然说到了事件,就多撤一句。如果你需要阻止默认事件,不能用return false的方式,而需要显式使用preventDefault。而vue会提供一个更简单的方式,后面再说。
function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('The link was clicked.'); } return ( Click me );}
Vue
原始html: 用v-html
条件渲染:
// v-if 会根据Seen的值真的去移除dom节点
现在你看到我了
// 而v-show只会去改变css的值
现在你看到我了
而对于比较复杂的逻辑,vue也可以hold住。
A
B
C
Not A/B/C
html属性:需要用v-bind来指定
......
绑定事件:
......
另外,vue会提供一些事件修饰符,来简化调用event.preventDefault(),event.stopPropagation()的需求。
stoppreventcaptureselfoncepassive
更多详情,请移步文档
4、内部状态(属性)
React
在React,组件内部自己维护的状态叫State,不能直接去改内部状态state,而是通过setState。
class Test extends React.Component() { constructor(props) { // super一定要写,继承React.Component super(props);// state在里面定义this.state = { name : 'daisy' }; } changeName () => { this.setState({ name: 'Lily' }) } render() { return (
{this.state.name}
) }}
Vue
在Vue, 组件内部自己维护的状态其实就是Data。如果要改的话,直接在方法里通过this或者是通过vue对象来改。
var vm = new Vue({ el: '#app', // 用于给 View 提供数据,相当于React的state data: { msg: 'Hello Vue' },method: { changeMsg: function() { this.msg = 'Hello daisy' }} })// 或者vm.msg = 'daisy';
但是Vue除了在Data里定义的属性之外,还额外定义计算属性跟侦听属性。
为什么要多两种呢?计算属性是为了应付逻辑比较复杂的情况。比如:
data = { message: ''};
{{ message.split('').reverse().join('') }}
在这里需要对message进行多一层的处理,但是直接写在模板里面,觉得不好维护,也不够优雅。
所以Vue就多搞了一个计算属性。
var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { reversedMessage: function () { return this.message.split('').reverse().join('') } }})
{ reversedMessage }
这个reversedMessage 属性将会监听message的改变,而自动重新计算。
为啥React不需要这个属性呢?
因为React有render函数,你可以在里面随便多定义一个变量。每一次this.state.message改变的时候,都一定会调用render函数重新渲染,所以就相当于重新计算reversedMessage了。
render() { const reversedMessage = this.state.message.split('').reverse().join('') return (
{reversedMessage}
) }
侦听属性感觉跟计算属性很像,但是他们又有不同。侦听属性是为了可以监听到数据的改变,然后做一些异步的或者开销大的操作。
var watchExampleVM = new Vue({ el: '#watch-example', data: { question: 'aaa', }, watch: { // 如果 `question` 发生改变,这个函数就会运行 question: function (newQuestion, oldQuestion) { this.getAnswer() } }, methods: { getAnswer: function () { // AJax 请求 } }})
同样的问题,为啥React不需要这个属性呢?因为React里已经有方法可以做到了。
React 15的版本,可以通过componentWillReceiveProps 来实现
componentWillReceiveProps(nextProps) { if (nextProps.id !== this.props.id) { this.setState({externalData: null}); this._loadAsyncData(nextProps.id); } }
React 16的版本,可以通过getDerivedStateFromProps,componentDidUpdate来实现。
static getDerivedStateFromProps(nextProps, prevState) { // Store prevId in state so we can compare when props change. if (nextProps.id !== prevState.prevId) { return { externalData: null, prevId: nextProps.id, }; } // No state update necessary return null; } componentDidUpdate(prevProps, prevState) { if (this.state.externalData === null) { this._loadAsyncData(this.props.id); } }
5、Props
props 一般指的是父组件传给子组件的属性。
React
React 的props 如果是静态的就直接传,动态的就用变量{}来传,还可以传入方法。
class Test extends React.Component { constructor(super) { super(props); this.state = { name : 'daisy'} } onClick( ) => { console.log("onClick"); } render() { return ( ; ; ; // 传入的方法其实在子组件里,在props也可以拿到。 // 使用的时候,直接this.props.handleClick()就可以了。 ) }}
由于React写的久了,我默认传props的时候,字符串就是静态的,如果是变量,或者方法,就用{},结果vue完全不是的,他们全都是传字符串!
Vue
Vue的静态props跟动态props传的方法不一样。传入的值title为一个常量(静态prop)时,不加v-bind
传入的值title为一个变量(动态prop)时,加v-bind
因为如果不用v-bind,vue就会认为这只是普通的字符串。所以用了v-bind才可以转成变量,然后vue会去data里找这个值。
传入方法的话,一样要使用v-on,子组件要用emit方法来触发。
; // 子组件里要用$emit方法来触发它 Enlarge text
大家也看到了,React跟Vue对于props的处理还是很不同的。对于一个从react切过来的人,实在需要点时间去适应vue的props。
6、双向绑定
React
使用受控组件,就是input的value直接就是state的值。然后监听相应的change事件,用setState改变state的值。
class NameForm extends React.Component { constructor(props) { super(props); this.state = {value: ''}; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } // 这里通过setState去改变state的值 handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert('提交的名字: ' + this.state.value); event.preventDefault(); } render() { return ( 名字: // 当input的值发生改变的时候就会调用handleChange // 通过state改变value的值,所以这里的显示会随用户 // 的输入更新而更新 ); }}
Vue
vue采用v-model的形式
Message is: {{ message }}
实际上v-model 是一个语法糖, 上面可以翻译成:
``
input本身有个oninput事件,每当输入框发生变化,就会触发onput,将最新的value传给他。
v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
text 和 textarea 元素使用 value 属性和 input 事件;checkbox 和 radio 使用 checked 属性和 change 事件;select 字段将 value 作为 prop 并将 change 作为事件。
v-model 不仅仅可以用在原生html元素上,还可以用在组件上。用在组件上的时候v-on用于监听事件,emit用来触发事件。
// 相当于是 // 这里用v-on监听事件,相当于传给了子组件value,还有input方法 {{price}}
Vue.component('currency-input', { template: ` `, props: ['value'],})var demo = new Vue({ el: '#demo', data: { price: 100, }})
总结一下,React在双向绑定方面,其实还是用着state在帮忙,传给子组件的方法也是直接传props即可,子组件直接通过props来调用。
; // 传入的方法其实在子组件里,在props也可以拿到。 // 使用的时候,直接this.props.handleClick()就可以了。
而Vue是利用了v-model,自动帮你做了这件事。而在传给子组件的时候,需要用$emit来配合。同时,你一定要知道v-model 实际上是可以翻译成这样的语法糖。
`
这样你才知道父组件在用v-model的时候,实际上传了value,跟input给子组件。
7、渲染child节点
React
React里可以通过this.props.children 获取。
class NotesList extends React.Component { render() { return (
{.map(function (child) { return {child} // 渲染出来就是 // hello // world}) } ) }}React.render( hello world , document.body);
Vue
Vue引入了一个插槽的概念。通过父组件中插入一个的标签来实现。
// 父组件 Message的html
hello,world
// 在调用的时候 hello daisy
最后就会渲染出来
hello,world hello daisy
Vue还提供了指定区域的插槽功能,比如
// 父组件 Message的html
hello,world
// 在调用的时候,需要使用元素,用上面的// v-slot 来指定名称
daisy
daisy
huang
huang
这样最后渲染的结果为:
hello,world daisy huang
而React的话可以直接通过this.props.chilren[0] [1]等拿到不同的元素,所以就没有提供这个了。
而且就我的实践经验来看,第二种用法感觉实际用的其实很少,掌握第一种就行了。一般要用都是直接塞进去一段内容。
当然插槽还有好多内容,详细可以查阅文档
8、生命周期
我觉得React、vue的生命周期都大同小异,主要就是创建 -> 更新 -> 销毁。具体一些名字不同而已。
React
react 15
![553590e80d9a36e2f76220e8a361a469.png](https://i-blog.csdnimg.cn/blog_migrate/03c687947c5f90829d292a9f207542d9.jpeg)
react 16
![b687b7ec814a21bda7d3d2f00aed49d8.png](https://i-blog.csdnimg.cn/blog_migrate/ba9fbc418171ddd9d7646687ebc40f9d.jpeg)
Vue
![644b661dcfa34f33848461bbcd3d2701.png](https://i-blog.csdnimg.cn/blog_migrate/690adb7028434a48c455fb55c431735a.jpeg)
这篇文章仅仅是对用法上做了一个区分,下篇文章,会对他们的一些实现思想在做一个比较。