最近接手了一个React开源项目,上手前看了一些react的基础教程就撸了,以前有着半年Angular转2年Vue的前端经验,可是仍是对React心生畏惧。javascript
项目作了大概一个周了,发现果真世界上代码都是同样的,React几乎和Vue的用法同样,惟一的学习成本在于语法(若是你能理解前端框架和组件化的概念)。html
可是三大框架里面,惟一让人以为赞许的、适合上手的官方文档,我我的认为,我的!是Vue,因此打算采用Vue的文档结构将Vue中的一些语法和功能对应的React的实现方式,向大家一一道来,带大家再读Vue文档,顺便学会React!前端
- 整篇文章结构采用Vue的文档目录,作了一些删减,有的我还没用到,有的可能对应的描述我思路不清晰就摘掉了
- 每一个部分通常会先把Vue文档中的功能实现或者语法搬过来,而后手打对应的React中实现或者语法。
- 整篇文章一万字出头,建议仔细看每一个例子,你可能须要20分钟??
开读以前,心理默默大声念出那句:vue
这一部分就不分别介绍两个框架了,由于你看这篇文章表明着必定要熟悉Vue的基本语法和理念,不然建议 × 掉该文章。另外建议你不要是React的精通大神,由于接下来的内容如Vue的官方文档同样基础,建议 × 掉。java
vue的起步固然是vue-cli建立一个项目,这个地方就不上vue-cli代码了,react也是同样的。react
首先你须要安装一个create-react-app
,它就是你要用的“vue-cli”了:es6
npm install -g create-react-app
复制代码
而后咱们用它构建一个react项目:vue-cli
create-react-app react-demo
复制代码
执行命令过程不一样于vue-cli会给你选择一些配置项,creat react会一直加载到最后结束,而后你就能够看到一个崭新的react项目基础结构: npm
Vue.js 的核心是一个容许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统,语法使用v-bind和双大括号:小程序
<div id="app">
<span v-bind:title="message">
{{ name }}
</span>
</div>
复制代码
var app = new Vue({
el: '#app',
data: {
name: 'Hello Vue!'
message: '页面加载于 ' + new Date().toLocaleString()
}
})
复制代码
vue的模板绑定语法就很少说了,react中采用了几乎一样的双向绑定方式:
import React, { Component } from 'react'
class DemoComponent extends Component {
constructor() {
super()
// 将this.state看作你vue中的data
this.state = {
name: 'hello',
title: '页面加载于 ' + new Date().toLocaleString()
}
}
render() {
return (
// 单大括号的双向绑定语法
<span title={this.state.title}>{this.state.name}</span>
)
}
}
export default DemoComponent
复制代码
效果图就不上了,太鸡肋,简单加个tip:
- react的双向绑定使用
单括号
- react的双向绑定属性也直接使用单括号,
没有冒号没有引号
- react的双向绑定的变量要使用
this.state.变量名
,而不是直接this引用变量名
vue中条件判断使用v-if和v-show,循环使用v-for
<div id="app">
<p v-if="seen">如今你看到我了</p>
<ol>
<li v-for="todo in todos">
{{ todo.text }}
</li>
</ol>
</div>
复制代码
var app = new Vue({
el: '#app',
data: {
seen: true,
todos: [
{ text: '学习 JavaScript' },
{ text: '学习 Vue' },
{ text: '整个牛项目' }
]
}
})
复制代码
而在react中可能就会略显麻烦:
import React, { Component } from 'react'
class DemoComponent extends Component {
constructor() {
super()
this.state = {
seen: true,
todos: [
{ text: '学习 JavaScript' },
{ text: '学习 Vue' },
{ text: '整个牛项目' }
]
}
}
render() {
return (
<div> /* 使用三元表达式 */ {this.state.seen ? <p>如今你看到我了</p> : null } /* 使用三元表达式的另外一种 */ <p>{this.state.seen ? '如今你看到我了' : null}</p> /* 使用map()进行dom遍历 */ <ol> {this.state.todos.map((item) => { return ( <li>{item.text}</li> ) })} </ol> </div>
)
}
}
export default DemoComponent
复制代码
- react中条件判断基本使用js条件表达式的特性实现(求科普有没有v-show的对应)
- react中循环通常使用
map
方法去遍历return须要循环的dom结构- 固然这只是基本用法
vue用 v-on 指令添加一个事件监听器,经过它调用在 Vue 实例中定义的方法: vue还提供了 v-model 指令,它能轻松实现表单输入和应用状态之间的双向绑定:
<div id="app">
<input v-model="message">
<button v-on:click="reverseMessage">反转消息</button>
</div>
复制代码
var app = new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
methods: {
reverseMessage: function () {
this.message = this.message.split('').reverse().join('')
}
}
})
复制代码
而后咱们看一下相应的如何在react中绑定事件和表单元素:
import React, { Component } from 'react'
class DemoComponent extends Component {
constructor() {
super()
this.state = {
message: 'Hello Vue.js!'
}
}
reverseMessage(event) {
this.setState({
message: this.state.message.split('').reverse().join('')
})
}
render() {
return (
<div> /* 单大括号绑定 */ <input value={this.state.message} /> <button onClick={this.reverseMessage.bind(this)}>反转消息</button> </div> ) } } export default DemoComponent 复制代码
知识点来了:
- 由于react使用的render函数,react全部特性几乎都是使用js的语法来实现,而不是指令型语法
- 像click等事件,还有标签属性如title,palceholder都是直接使用大括号
- react中双向绑定的变量不会随js对变量的一系列操做修改而响应,任何双向绑定的变量要达到在页面渲染更新,须要使用react的
this.setState({})
语法,key为state中的变量名,value为更新后的变量值(若是写过原生小程序应该很容易理解)- 方法能够直接在Class中定义,不一样于vue的存在methods对象中,且通常在绑定方法时经过bind将this传进去,不然你的方法里
this.
将会报错,经典this做用域问题,自行科普。
每一个 Vue 应用都是经过用 Vue 函数建立一个新的 Vue 实例开始的:
vue实例:
var vm = new Vue({
// 选项
})
复制代码
react实例:
import React, { Component } from 'react'
class DemoComponent extends Component {
// 内容
render () {
}
}
export default DemoComponent
复制代码
知识点:
- react的一个组件以一个从react中继承Component的Class类为实例
- 一个react组件必须经过
render ()
返回JSX
,且只能返回一个JSX
元素(2
个必要条件)- vue和react的共同点是最外层必须有一个
单一
的根元素
标签
愈来愈简单是否是,next=>
data函数
返回一个对象来保存当前vue实例中的变量,vue实例中的自写方法都放置在methods
中state
中,方法能够直接在class中定义,若是你对ES6的class足够了解,你很容易就能够理解我在说什么。(以为别扭的能够重看一遍ES6)render() {
// render()函数中直接定义变量,毕竟它是个函数,这是合理的
let tip = "Hello!"
return (
<div> {{tip}} </div>
)
}
复制代码
由于render是一个函数,因此你能够直接在return一些dom结构以前定义函数内的变量。 这恰好再次强调以前我说的
“ 由于react是使用的render函数,因此react全部特性几乎都是使用js的语法来实现”
换个表达方式,这样我能够少打点字:
生命周期钩子 | vue | react |
---|---|---|
实例建立前 | beforeCreate | - - - |
实例建立后 | created | constructor() |
DOM挂载前 | beforeMount | componentWillMount |
DOM挂载后 | mounted | componentDidMount |
数据更新时 | beforeUpdate | componentWillUpdate |
数据更新后 | updated | componentDidUpdate |
keep-alive激活时 | activated | |
keep-alive 组件停用时 | deactivated | |
实例销毁前 | beforeDestroy | componentWillUnmount |
实例销毁后 | destroyed | componentWillUnmount |
子组件发生错误时 | errorCaptured |
vue数据绑定最多见的形式就是使用“Mustache”语法 (双大括号) 的文本插值:
<span>Message: {{ msg }}</span>
复制代码
react直接使用大括号,这个已经提过:
<span>Message: { this.state.msg }</span>
复制代码
(另外一样不知有没有对应的v-once实现,欢迎评论里科普)
vue使用v-html直接输出真正的 HTML,假设咱们有一个叫rawHtml的html字符串模板:
<p>
<span v-html="rawHtml"></span>
</p>
复制代码
react就简单了,由于它是render函数,因此能够:
render () {
const rawHtml= `<span> is span </span>`
return (
<div> <p>{rawHtml}</p> </div>
)
}
复制代码
vue使用v-bind:
做用在 HTML 特性上:
<button v-bind:disabled="isButtonDisabled">Button</button>
复制代码
react直接使用{}
绑定:
<button disabled={this.state.isButtonDisabled}>Button</button>
复制代码
vue官方示例的基本表达式使用方式有:
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div v-bind:id="'list-' + id"></div>
复制代码
使用简单的运算符,三元表达式,链式调用均可以,而react的使用则彻底是你平常js的使用方式,这3种均可以. 另外提一点vue中能够直接使用{{}}绑定某一个methods或者计算属性中的某一个方法名,好比:
<div>{{changeName()}}</div>
复制代码
react中也能够直接写成一个当即执行函数表达式返回:
<div>{(function () { return 'is div'})()}</div>
复制代码
没看到有关react的计算属性用法,应该是react的getter不会使用缓存,割了
vue 经过 watch 选项提供了一个更通用的方法,来响应数据的变化,具体不叭叭了,由于:
react 的状态都是手动 setstate 变化的,react 不监听数据变化,
这不是我割的……
vue能够传给 v-bind:class 一个对象,以动态地切换 class:
<div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }" ></div>
复制代码
active和 'text-danger' 这2个 class 存在与否将取决于数据属性 isActive和hasError的判断
vue还能够把一个数组传给 v-bind:class,以应用一个 class 列表:
<div v-bind:class="[activeClass, errorClass]"></div>
复制代码
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
复制代码
而在react中,
不能使用class
,由于class是关键字,因此react使用className
代替,<div className={["activeClass",hasError? item.color :'' ].join('')} ></div>
复制代码
<div className={`activeClass ${hasError?item.color:'' }`} ></div>
复制代码
vue的v-bind:style 的对象语法十分直观——看着很是像 CSS,但实际上是一个 JavaScript 对象。CSS 属性名能够用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
复制代码
data: {
activeColor: 'red',
fontSize: 30
}
复制代码
固然你也能够将全部样式定义成一个对象变量,直接绑定该对象。
这个时候,看好知识点
,
{{}}
:
<div style={{display: this.state.isShow ? "block" : "none"}}></div>
复制代码
多个样式时,使用逗号 ,
分隔!
<div style={{display: this.state.isShow ? "block" : "none", color:"pink"}}></div>
复制代码
建议你看到这,稍微停顿一下分辨区别。
vue就是v-if,v-else,v-show就很少说了,这个就比较简单了:
<div v-if="Math.random() > 0.5">
Now you see me
</div>
<div v-else>
Now you don't
</div>
复制代码
react的条件判断上面开始的时候介绍过了,一般使用三元表达式来判断显示,这里补充一种将JSX 元素赋值给了不一样变量,再使用三元表达式判断的方式:
render () {
const display = true
const showDiv = <p>显示</p>
const noneDiv = <p>隐藏</p>
return (
<div>{display ? showDiv : noneDiv }</div>
)
}
复制代码
也没有vue相似v-show这种比较节省dom切换性能消耗的语法,欢迎评论补充。
vue中咱们能够用 v-for 指令基于一个数组来渲染一个列表。
<ul id="example-1">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
复制代码
至于具体里面的那些为何要加key啊,怎么遍历对象啊,还有怎么v-for对数组的检测问题就不深究了。
react中的循环在开始也提到过了,使用map()方法遍历,不说了。
vue中表单元素的绑定都是使用v-model绑定,拿个input举例:
<input v-model="message" placeholder="edit me">
复制代码
react中以前也提了使用元素自己的属性value来直接绑定
<input value={this.state.message} placeholder="edit me">
复制代码
提过了,基本一个实例就是一个组件且vue中后面也出现了纯函数组件,两个框架组件概念都同样。
vue和react的组件复用方式都是同样的,先引用,而后使用组件名标签。
vue中引用通常习惯使用-
间隔标签,例如:
<div id="components-demo">
<button-counter></button-counter>
</div>
复制代码
import DuttonCounter from './modules/DuttonCounter'
export default {
components: {
DuttonCounter
},
}
复制代码
react中引用标签直接使用export的class类名,且组件都必需要用大写字母开头
:
import DuttonCounter from './modules/DuttonCounter'
function App() {
return (
<div className="App"> <DuttonCounter /> </div>
);
}
复制代码
来了啊,知识点,要考的!
父组件:
<super-man work="超人"></super-man>
复制代码
子组件接收:
<div>{{work}}</div>
复制代码
export default {
props: {
work: {
required: true,
type: String
}
},
}
复制代码
而后,经过一个例子看一下react的props:
class Index extends Component {
render () {
return (
<div>
<SuperMan work='超人' />
</div>
)
}
}
复制代码
这是组件传值的方式,和vue大同小异,看一下接收组件传值:
class SuperMan extends Component {
constructor () {
super()
this.state = {}
}
render () {
return (
<div>{this.props.work}</div>
)
}
}
复制代码
在react中,接收父组件的传值一样使用关键字props,与vue不一样的区别在于:
- vue的全部传值须要放置在子组件的props的对象中,才能够被当前子组件使用,使用方式如同访问data的变量,直接
this.变量
使用。 react是使用this.props.变量
这种方式就能够直接使用全部父组件传递的变量。- react中向子组件传递对象参数,可使用
{{}}
双括号直接传递对象
class Index extends Component {
render () {
return (
<div> <SuperMan work={{name: '超人', task: '拯救世界'}} /> </div> ) } } 复制代码
3.甚至于传递函数:
class Index extends Component {
render () {
return (
<div> <SuperMan onClick={this.handleClick.bind(this)}/> </div> ) } } 复制代码
而后,补充一些额外的知识点,
那么react如何实现对props传值的参数校验呢: 首先假设父组件没有传值过来,咱们可能须要一个默认值,这时候你可能会常常在父组件传值的时候使用三元表达式来判断传值,react只须要:
class SuperMan extends Component {
// 知识点
static defaultProps = {
work: '默认是超人'
}
constructor () {
super()
this.state = {}
}
render () {
return (
<div>{this.props.work}</div>
)
}
}
复制代码
react中定义defaultProps就对 props 中各个属性的默认配置。这样咱们就不须要判断配置属性是否传进来了:若是没有传进来,能够直接this.props使用 defaultProps 中的默认属性。
其次,关于react中props的校验,先声明一句“react的类型检查PropTypes自React v15.5起已弃用”,那我该怎么继续讲下去?? 这个时候就须要单独安装prop-types,如今一般在react中咱们使用prop-types对组件的prop进行类型检测。
npm install --save prop-types
复制代码
使用方式也很简单,有例子你就能理解:
// 知识点
import PropTypes from 'prop-types'
class SuperMan extends Component {
// 知识点
static propTypes = {
work: PropTypes.string
}
constructor () {
super()
this.state = {}
}
render () {
return (
<div>{this.props.work}</div>
)
}
}
复制代码
简单明了,经过引入prop-types,而且添加一个类属性 propTypes,在propTypes中为你props传入的变量指定数据类型,这样就至关于vue的type:String
。
而后,咱们看一下required: true
在react中如何实现:
import PropTypes from 'prop-types'
class SuperMan extends Component {
static propTypes = {
work: PropTypes.string.isRequired // 知识点
}
constructor () {
super()
this.state = {}
}
render () {
return (
<div>{this.props.work}</div>
)
}
}
复制代码
仔细看和上面一个例子的区别在于,当咱们指定数据类型PropTypes.string
以后,同时后面经过 isRequired 关键字来强制组件某个参数必须传入。
最后,如何react实现组件传参校验呢? 这个我还没用到,可是呢专门为大家查了一下,和我猜的差很少,直接使用函数:
import PropTypes from 'prop-types'
class SuperMan extends Component {
static propTypes = {
work: function(props,propName,componentName){
} // 知识点
}
constructor () {
super()
this.state = {}
}
render () {
return (
<div>{this.props.work}</div>
)
}
}
复制代码
使用自定义函数,默认传参分别为props,propName,componentName,而后校验错误就return new Error('错误信息');
就能够了。
再放上一些看到的其余的例子,虽然我也还没用到,可是是很好的小技巧:
//指定枚举类型:你能够把属性限制在某些特定值以内
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 指定多个类型:你也能够把属性类型限制在某些指定的类型范围内
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 指定某个类型的数组
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 指定类型为对象,且对象属性值是特定的类型
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
复制代码
react和vue有着一样的组件特征——单向数据流。 在react中想要改变父组件中的值就要用方法去触发父组件的方法,由父组件本身的方法操做属于父组件的值。
那么如何实现触发父组件或者说父组件监听子组件变化而变化,在vue中是经过$emit
方法来触发父组件方法,例子不举了,直接来看react如何实现:
// 父组件
class Index extends Component {
this.state = {type:'小超人'}
changeType(data) {
this.setState({
type: data //把父组件中type的替换为子组件传递的值
})
}
render () {
return (
<div> <SuperMan work='超人' super={this.fn.changeType(this)}/> // 知识点 </div> ) } } 复制代码
// 子组件
class SuperMan extends Component {
constructor () {
super()
this.state = {childText:'大超人'}
}
clickFun(text) {
this.props.super(text)//这个地方把值传递给了props的事件当中
}
render () {
return (
<div onClick={this.clickFun.bind(this, this.state.childText)}> {this.props.work} </div>
)
}
}
复制代码
没啥问题,看的懂ok,只是没有使用$emit
,而是直接经过this.props
调用父组件的方法便可。
插槽部分简单说下使用,首先咱们如何在vue中使用一个插槽:
// 父组件
<my-component>
<p>超人</p>
</my-component>
// 子组件
<div class="child-page">
<h1>无限地球危机</h1>
<slot></slot>
</div>
// 渲染结果
<div class="child-page">
<h1>无限地球危机</h1>
<p>超人</p>
</div>
复制代码
插槽那一堆具体就不解释了,咱们只看如何在react也实现像vue这种插槽方式:
// 父组件
ReactDOM.render(
<MyComponent> <p>超人</p> </MyComponent>,
document.getElementById('root')
)
class MyComponent extends Component {
render () {
return (
<div class="child-page"> <h1>无限地球危机</h1> {this.props.children} </div>
)
}
}
复制代码
渲染结果和vue的渲染就是一致了,能够看到react中将父组件中的JSX内容传到子组件内部,经过 {this.props.children}
把JSX内容渲染到页面指定结构上,仍是很好理解的。
截止到我写完,已经断断续续一个周过去了,看了看右下角11386字数,项目上的代码研究的也差很少,由于是开源项目因此大量都是高阶组件,一些纯函数组件组成,刚开始仍是很迷糊,可是如今已经差很少改掉一些功能了。
至此按照vue文档中的结构(部分作了删减),把对应的react的语法和功能作了一遍梳理,若是你能仔细把每一个例子对比看一遍,我以为你写不了react,也能够看懂一个react项目了。
有补充的评论加个知识点,没补充的捧个赞。
谢谢。