Vue均使用ES6语法,主要以单文件组件为例,写法上优先使用缩写。
React使用TS语法。
生命周期
- Vue
- React
入口&根实例
- Vue
const app = new Vue({ /* 选项 */ render: (h) => h(App) // App为根组件 }).$mount('#app')
- React
ReactDOM.render( <App/>, // App为根组件 document.getElementById('app') )
组件定义
- Vue
// 定义组件构造器 var MyComponent = Vue.extend({/* 选项 */}) // 注册全局组件 Vue.component('my-component', {/* 选项 */})
<!-- 单文件组件 --> <template> <div class="my-component">hello</div> </template> <script> export default { /* 选项 */ } </script>
- React
// 无状态组件 const Foo = () => { return <div className='foo'></div> }
// 完整组件 class Foo extends React.Component<{}, void> { render () { return <div className='foo'>hello</div> } }
组件引用
- Vue
<!-- 以单文件组件为例:Foo.js --> <template> <div class="Foo"> <!-- 必须小写,不能自闭合 --> <bar></bar> </div> </template> <script> // 引入组件 import Bar from './Bar' export default { name: 'Foo', components: {Bar} } </script>
- React
import Bar from './Bar' class Foo extends React.Component<{}, void> { render () { return ( <div className='foo'> {/* 组件约定大写开头,可自闭合 */} <Bar/> </div> ) } }
组件内部状态
- Vue
<template>
<div class="foo"> <p class='name'>{{name}}</p> <p class='age'> {{age}} <button @click="onAdd">add</button> </p> </div> </template> <script> export default { data () { return { name: 'Tom', age: 18 } }, methods { onAdd () { // 直接修改 this.age++ } } } </script>
- React
interface IFooState {
name: string, age: number } class Foo extends React.Component<{}, IFooState> { state = { name: 'tom', age: 18 } onAdd = () => { // 必须通过setState修改 this.setState({ age: this.state.age + 1 }) } render () { const {name, age} = this.state return ( <div className='foo'> <p class='name'>{name}</p> <p class='age'> {age} <button onClick={this.onAdd}>add</button> </p> </div> ) } }
父子组件通讯
- Vue
<!-- Parent.vue --> <template> <div class="parent"> <child name='tom' :age='18' @click="onAdd"></child> </div> </template> <script> export default { data () { return { age: 18 } }, methods { onAdd () { this.age++ } } } </script>
<!-- Child.vue --> <template> <div class="child"> <p class='name'>{{name}}</p> <p class='age'> {{age}} <button @click="onAdd">add</button> </p> </div> </template> <script> export default { props: { name: {type: String}, age: {type: Number, default: 18} }, methods { onAdd () { this.$emit('click') } } } </script>
- React
interface IChildProps {
name: string, age?: number, onAdd?: () => void } class Child extends React.Component<IChildProps, void> { static defaultProps = { age = 18, onAdd: () => {} } render () { const {name, age} = this.props return ( <div className='child'> <p class='name'>{name}</p> <p class='age'> {age} <button onClick={this.onAdd}>add</button> </p> </div> ) } } interface IParentState { age: number } class Parent extends React.Component<{}, IParentState> { state = { age: 18 } onAdd = () => { this.setState({ age: this.state.age + 1 }) } render () { const {name, age} = this.state return ( <div className='parent'> <Child name='Tom' age={18} onAdd={this.onAdd}></Child> </div> ) } }
模板/JSX语法
- Vue
<!-- 可搭配其他模板语言,如Pug等 --> <template> <!-- 变量 --> <div>{{name}}</div> <!-- 表达式 --> <div>{{ ok ? 'YES' : 'NO' }}</div> <!-- HTML --> <div v-html="rawHtml"></div> <!-- 属性:属性名必须小写(kebab-case) --> <div id="app"></div> <div :id="dynamicId"></div> <foo :task-count="18"></foo> <foo :class="['item', foo]"></foo> <foo :style="{'margin-top': '10px'}"></foo> <!-- 事件 --> <foo @action="onAction"></foo> </template>
- React
render () {
return ( <!-- 变量 --> <div>{name}</div> <!-- 表达式 --> <div>{ ok ? 'YES' : 'NO' }</div> <!-- HTML --> <div dangerouslySetInnerHTML={rawHtml}></div> <!-- 属性 --> <div id='app'></div> <div id={dynamicId}></div> <foo taskCount={18}></foo> <foo className={'item ' + foo}></foo> <foo style={{marginTop: 10}}></foo> <!-- 事件 --> <foo onAction="onAction"></foo> ) }
条件渲染
- Vue
<template>
<div v-if="foo">foo</div> </template> <template> <div v-if="foo">foo</div> <div v-else-if="bar">bar</div> </template> <template> <div v-if="foo">foo</div> <div v-else-if="bar">bar</div> <div v-else>other</div> </template>
- React
render () {
return foo && <div>foo</div> } render () { return foo ? <div>foo</div> : <div>bar</div> } render () { return ( { foo ? <div>foo</div> : bar ? <div>bar</div> : <div>other</div> } ) }
列表渲染
- Vue
<template>
<div class='list'> <div v-for="item in list" :key="item">{{item}}</div> </div> </template>
- React
render () {
return ( <div className='list'> {list.map((item) => <div key={item}>{item}</div>)} </div> ) }
// 或者 render () { const items = list.map((item) => <div key={item}>{item}</div>) return ( <div className='list'> {items} </div> ) }
表单&双向绑定
- Vue
<!-- 表单 --> <template> <form> <input v-model="name"> <!-- 相当于以下的语法糖: <input v-bind:value="name" v-on:input="name = $event.target.value"> 在组件中相当于 <foo v-bind:value="name" v-on:input="name = arguments[0]"></foo> --> </form> </template> <script> export default { data () { return { name: '' } } } </script>
<!-- Vue 2.3.0+ --> <!-- Parent.vue --> <template> <child :foo.sync="bar"></child> <!-- sync只是语法糖,实际上拓展为: <child :foo="bar" @update:foo="val => bar = val"></child> --> </template> <!-- Child.vue --> <script> export default { methods: { onChange () { this.$emit('update:foo', newValue) } } } </script>
- React
interface IFooState {
name: string } class Foo extends React.Component<{}, IFooState> { onChange = (e) => { const name = e.target.value this.setState({name}) } render () { const {value} = this.state return ( <div> <input value={value} onChange={this.onChange}/> </div> ) } }
内容分发
- Vue
<!-- Child --> <template> <!-- 必须有根元素 --> <div class="child"> <slot name="header"></slot> <slot></slot> <slot name="footer"></slot> </div> </template> <script> export default {} </script> <!-- Parent --> <template> <div class="parent"> <child> <p slot="header">header</p> <p>content</p> <p slot="footer">footer</p> </child> </div> </template> <script> import Child from './Child' export default { components: {Child} } </script>
- React
interface IChildProps {
header?: React.Node, children?: React.Node, footer?: React.Node } class Child extends React.Component<IChildProps, void> { render () { const {header, children, footer} = this.props return ( <div className='child'> {header} {children} {footer} </div> ) } } class Parent extends React.Component<{}, void> { render () { return ( <div className='parent'> <Child className='child' header='header'} footer={<p>footer</p>}> <p>content</p> </Child> </div> ) } }