React.js和Vue.js的語法並列比較

daxuesheng發表於2021-09-09

圖片描述

React.js和Vue.js都是很好的框架。而且Next.js和Nuxt.js甚至將它們帶入了一個新的高度,這有助於我們以更少的配置和更好的可維護性來建立應用程式。但是,如果你必須經常在框架之間切換,在深入探討另一個框架之後,你可能會輕易忘記另一個框架中的語法。在本文中,我總結了這些框架的基本語法和方案,然後並排列出。我希望這可以幫助我們儘快掌握語法,不過限於篇幅,這篇文章只比較React.js和Vue.js,下一篇再談Next.js個Nuxt.js。

Github:

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,全棧開發工程師、持續學習者。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4692/viewspace-2825371/,如需轉載,請註明出處,否則將追究法律責任。

相關文章