手记

React.js和Vue.js的语法并列比较

React.js和Vue.js都是很好的框架。而且Next.js和Nuxt.js甚至将它们带入了一个新的高度,这有助于我们以更少的配置和更好的可维护性来创建应用程序。但是,如果你必须经常在框架之间切换,在深入探讨另一个框架之后,你可能会轻易忘记另一个框架中的语法。在本文中,我总结了这些框架的基本语法和方案,然后并排列出。我希望这可以帮助我们尽快掌握语法,不过限于篇幅,这篇文章只比较React.js和Vue.js,下一篇再谈Next.js个Nuxt.js。

Render

React.js


ReactDOM.render(<App />, document.getElementById("root"));

Vue.js


new  Vue({

render: (h) => h(App),

}).$mount("#root");

基本组件

React.js

Class component


class MyReactComponent extends React.Component {

render() {

return <h1>Hello world</h1>;

}

}

Function component


function  MyReactComponent() {

return <h1>Hello world</h1>;

}

Vue.js


<template>

<h1>Hello World</h1>

</template>

<script>

export  default {

name: "MyVueComponent",

};

</script>

Prop

React.js


function  MyReactComponent(props) {

const { name, mark } = props;

return <h1>Hello {name}{mark}</h1>;

}

MyReactComponent.propTypes = {

name: PropTypes.string.isRequired,

mark: PropTypes.string,

}

MyReactComponent.defaultProps = {

mark: '!',

}

...

<MyReactComponent name="world">

Vue.js


<template>

<h1>Hello {{ name }}</h1>

</template>

<script>

export  default {

name: "MyVueComponent",

props: {

name: {

type: String,

required: true,

},

mark: {

type: String,

default: "!",

},

},

};

</script>

...

<MyVueComponent  name="World" />

事件绑定

React.js

Class component


class MyReactComponent extends React.Component {

save  = () => {

console.log("save");

};

render() {

return <button  onClick={this.save}>Save</button>;

}

}

Function component


function  MyReactComponent() {

const  save  = () => {

console.log("save");

};

return <button  onClick={save}>Save</button>;

}

Vue.js


<template>

<button  @click="save()">Save</button>

</template>

<script>

export  default {

methods: {

save() {

console.log("save");

},

},

};

</script>

自定义事件

React.js


function  MyItem({ item, handleDelete }) {

return <button  onClick={() => handleDelete(item)}>{item.name}</button>;

/*

* 应用useCallback钩子来防止在每次渲染时生成新的函数。

*

* const handleClick = useCallback(() => handleDelete(item), [item, handleDelete]);

*

* return <button onClick={handleClick}>{item.name}</button>;

*/

}

...

function  App() {

const  handleDelete  = () => { ... }

return <MyItem item={...}  handleDelete={handleDelete} />

}

Vue.js


<template>

<button  @click="deleteItem()">{{item.name}}</button>

</template>

<script>

export  default {

name: "my-item",

props: {

item: Object,

},

methods: {

deleteItem() {

this.$emit("delete", this.item);

},

},

};

</script>

...

<template>

<MyItem  :item="item"  @delete="handleDelete" />

</template>

<script>

export  default {

components: {

MyItem,

},

methods: {

handleDelete(item) { ... }

},

};

</script>

State

React.js

Class component


class MyReactComponent extends React.Component {

state = {

name: 'world,

}

render() {

return <h1>Hello { this.state.name }</h1>;

}

}

Function component


function  MyReactComponent() {

const [name, setName] = useState("world");

return <h1>Hello {name}</h1>;

}

Vue.js


<template>

<h1>Hello {{ name }}</h1>

<!-- 使用组件状态作为prop -->

<my-vue-component  :name="name">

</template>

<script>

export  default {

data() {

return { name: "world" };

},

};

</script>

Change-State

React.js

Class component


class MyReactComponent extends React.Component {

state = {

count: 0,

};

increaseCount  = () => {

this.setState({ count: this.state.count +  1 });

// 在更新之前获取当前状态,以确保我们没有使用陈旧的值

// this.setState(currentState => ({ count: currentState.count + 1 }));

};

render() {

return (

<div>

<span>{this.state.count}</span>

<button  onClick={this.increaseCount}>Add</button>

</div>

);

}

}

Function component


function  MyReactComponent() {

const [count, setCount] = useState(0);

const  increaseCount  = () => {

setCount(count  +  1);

// setCount(currentCount => currentCount + 1);

};

return (

<div>

<span>{count}</span>

<button  onClick={increaseCount}>Add</button>

</div>

);

}

Vue.js


<template>

<div>

<span>{{count}}</span>

<button  @click="increaseCount()">Add</button>

</div>

</template>

<script>

export  default {

data() {

return { count: 0 };

},

methods: {

increaseCount() {

this.count =  this.count +  1;

},

},

};

</script>

双向绑定 (仅Vue.js)

React.js

React没有双向绑定,因此我们需要自己处理数据流


function  MyReactComponent() {

const [content, setContent] = useState("");

return (

<input

type="text"

value={content}

onChange={(e) => setContent(e.target.value)}

/>

);

}

Vue.js


<template>

<input  type="text"  v-model="content" />

</template>

<script>

export  default {

data() {

return { content: "" };

},

};

</script>

计算属性

React.js

React.js没有计算属性,但我们可以通过react hook轻松实现


function  DisplayName({ firstName, lastName }) {

const displayName = useMemo(() => {

return `${firstName} ${lastName}`;

}, [firstName, lastName]);

return <div>{displayName}</div>;

}

...

<DisplayName firstName="Hello"  lastName="World" />

Vue.js


<template>

<div>{{displayName}}</div>

</template>

<script>

export  default {

name: "display-name",

props: {

firstName: String,

lastName: String,

},

computed: {

displayName: function () {

return `${this.firstName} ${this.lastName}`;

},

},

};

</script>

...

<DisplayName  firstName="Hello"  lastName="World" />

Watch

React.js

React.js没有 watch 属性,但是我们可以通过react hook轻松实现


function  MyReactComponent() {

const [count, setCount] = useState(0);

const  increaseCount  = () => {

setCount((currentCount) =>  currentCount  +  1);

};

useEffect(() => {

localStorage.setItem("my_count", newCount);

}, [count]);

return (

<div>

<span>{count}</span>

<button  onClick={increaseCount}>Add</button>

</div>

);

}

Vue.js


<template>

<div>

<span>{{count}}</span>

<button  @click="increaseCount()">Add</button>

</div>

</template>

<script>

export  default {

data() {

return { count: 0 };

},

methods: {

increaseCount() {

this.count =  this.count +  1;

},

},

watch: {

count: function (newCount, oldCount) {

localStorage.setItem("my_count", newCount);

},

},

};

</script>

Children-and-Slot

React.js


function  MyReactComponent({ children }) {

return <div>{children}</div>;

}

...

<MyReactComponent>Hello World</MyReactComponent>

Vue.js


<template>

<div>

<slot />

</div>

</template>

<script>

export  default {

name: "my-vue-component",

};

</script>

...

<MyVueComponent>Hello World</MyVueComponent>

渲染HTML

React.js


function  MyReactComponent() {

return <div  dangerouslySetInnerHTML={{ __html: "<pre>...</pre>" }} />;

}

Vue.js


<template>

<div  v-html="html"></div>

</template>

<script>

export  default {

data() {

return {

html: "<pre>...</pre>",

};

},

};

</script>

条件渲染

React.js


function  MyReactComponent() {

const [isLoading, setLoading] = useState(true);

return (

<div>

{isLoading && <span>Loading...</span>}

{isLoading ? <div>is loading</div> : <div>is loaded</div>}

</div>

);

}

Vue.js


<template>

<div>

<!--v-show: 总是渲染,但根据条件更改CSS-->

<span  v-show="loading">Loading...</span>

<div>

<div  v-if="loading">is loading</div>

<div  v-else>is loaded</div>

</div>

</div>

</template>

<script>

export  default {

data() {

return { loading: true };

},

};

</script>

列表渲染

React.js


function  MyReactComponent({ items }) {

return (

<ul>

{items.map((item) => (

<li  key={item.id}>

{item.name}: {item.desc}

</li>

))}

</ul>

);

}

Vue.js


<template>

<ul>

<li  v-for="item in items"  :key="item.id">

{{item.name}}: {{item.desc}}

</li>

</ul>

</template>

<script>

export  default {

props: {

items: Array,

},

};

</script>

Render-Props

React.js


function  Modal({children, isOpen}) {

const [isModalOpen, toggleModalOpen] = useState(isOpen);

return (

<div  className={isModalOpen ?  'open'  :  'close'}>

{type  children  ===  'function'  ? children(toggleModalOpen)  : children}

</div>)

;

}

Modal.propTypes = {

isOpen: PropTypes.bool,

children: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired,

}

Modal.defaultProps = {

isOpen: false,

}

...

<Modal isOpen>

{(toggleModalOpen) => {

<div>

<div>...</div>

<button  onClick={() => toggleModalOpen(false)}>Cancel</button>

</div>

}}

</Modal>

Vue.js(slot)


<template>

<div  v-show="isModalOpen">

<slot  v-bind:toggleModal="toggleModalOpen" />

</div>

</template>

<script>

export  default {

name: "modal",

props: {

isOpen: {

type: Boolean,

default: false,

},

},

data() {

return {

isModalOpen: this.isOpen,

};

},

methods: {

toggleModalOpen(state) {

this.isModalOpen = state;

},

},

};

</script>

...

<Modal  isOpen>

<template  v-slot="slotProps">

<div>...</div>

<button  @click="slotProps.toggleModal(false)">Close</button>

</template>

</Modal>

生命周期

React.js

Class component


class MyReactComponent extends React.Component {

static  getDerivedStateFromProps(props, state) {}

componentDidMount() {}

shouldComponentUpdate(nextProps, nextState) {}

getSnapshotBeforeUpdate(prevProps, prevState) {}

componentDidUpdate(prevProps, prevState) {}

componentWillUnmount() {}

render() {

return <div>Hello World</div>;

}

}

Function component


function  MyReactComponent() {

// componentDidMount

useEffect(() => {}, []);

// componentDidUpdate + componentDidMount

useEffect(() => {});

// componentWillUnmount

useEffect(() => {

return () => {...}

}, []);

// 在渲染之后但在屏幕更新之前同步运行

useLayoutEffect(() => {}, []);

return <div>Hello World</div>;

}

Vue.js


<template>

<div>Hello World</div>

</template>

<script>

export default {

beforeCreate() {},

created() {},

beforeMount() {},

mounted() {},

beforeUpdate() {},

updated() {},

beforeDestroy() {},

destroyed() {},

};

</script>

错误处理

React.js


class ErrorBoundary extends React.Component {

state = { hasError: false };

static  getDerivedStateFromError(error) {

// 更新状态,这样下一个渲染将显示回退UI。

return { hasError: true };

}

componentDidCatch(error, errorInfo) {}

render() {

if (this.state.hasError) return <h1>Something went wrong.</h1>;

return  this.props.children;

}

}

...

<ErrorBoundary>

<App />

</ErrorBoundary>

Vue.js


const vm =  new  Vue({

data: {

error: "",

},

errorCaptured: function(err, component, details) {

error = err.toString();

}

}

Ref

React.js

Class component


class AutofocusInput extends React.Component {

constructor(props) {

super(props);

this.ref = React.createRef();

}

state = {

content: "",

};

componentDidMount() {

this.ref.current.focus();

}

setContent  = (e) => {

this.setState({ content: e.target.value });

};

render() {

return (

<input

ref={this.ref}

type="text"

value={this.state.content}

onChange={this.setContent}

/>

);

}

}

Function component


function  AutofocusInput() {

const [content, setContent] = useState("");

const ref = useRef(null);

useEffect(() => {

if (ref  && ref.current) {

ref.current.focus();

}

}, []);

return (

<input

ref={ref}

type="text"

value={content}

onChange={(e) => setContent(e.target.value)}

/>

);

}

Vue.js


<template>

<input  ref="input"  type="text"  v-model="content" />

</template>

<script>

export  default {

name: "autofocus-input",

data() {

return { content: "" };

},

mounted() {

this.$refs.input.focus();

},

};

</script>

性能优化

React.js

PureComponent


class MyReactComponent extends React.PureComponent {

...

}

shouldComponentUpdate


class MyReactComponent extends React.Component {

shouldComponentUpdate(nextProps) {...}

...

}

React.memo


export  default React.memo(

MyReactComponent,

(prevProps, nextProps) => {

...

}

);

useMemo


export  default  function  MyReactComponent() {

return React.useMemo(() => {

return <div>...</div>;

}, []);

}

useCallback


function  MyItem({ item, handleDelete }) {

const handleClick = useCallback(() => handleDelete(item), [

item,

handleDelete,

]);

return <button  onClick={handleClick}>{item.name}</button>;

}

Vue.js

v:once


<span  v-once>This will never change: {{msg}}</span>

函数式组件:我们可以将组件标记为 functional,这意味它无状态 (没有响应式数据),也没有实例 (没有 this 上下文)。


<template  functional>

<h1>Hello {{ name }}</h1>

</template>

<script>

export  default {

name: "MyVueComponent",

props: {

name: String,

},

};

</script>

keep-alive 组件


<keep-alive>

<component  :is="view"></component>

</keep-alive>

完。


如果对你有所启发和帮助,可以点个关注、收藏、转发,也可以留言讨论,这是对作者的最大鼓励。

作者简介:Web前端工程师,全栈开发工程师、持续学习者。

6人推荐
随时随地看视频
慕课网APP

热门评论

比较下来还是vue实现起来更简单啊

查看全部评论