Vue.js 开发人员的 React 指南
如果你已经学过一门语言或框架,学习类似的会更容易。你不用从头到尾阅读文档,只需思考“如何用 Y 实现 X?”。
在本文中,我向想要学习 React 的 Vue.js 开发人员介绍了使用 React 实现 Vue.js 功能的方式。
(我不鼓励任何人从 Vue.js 切换到 React!了解两者会更好,也更有趣,对吧?)
成分
如何创建组件?
Vue.js 方式
Vue 组件由 3 个块组成—— 、、<template>
。并且该文件应该具有扩展名。<script>
<style>
.vue
<template>
<div>
<h1>Sample</h1>
<h2>This is a component.</h2>
</div>
</template>
<script>
export default {
// data and behaviors.
}
</script>
<style>
/* how this component looks */
</style>
反应方式
在 React 中,有两种创建组件的方法——函数和类。
下面是创建组件的功能方式的示例。
import React from 'react';
function Sample() {
return (
<div>
<h1>Sample</h1>
<h2>This is a component.</h2>
</div>
);
}
export default Sample;
函数式组件是一个返回 React 元素的函数。它看起来像 JavaScript 返回 HTML,但实际上不是。它是JSX。
为了使用 JSX,你必须导入 React,尽管它似乎没有被直接引用。(不过在 React v17.0 中,你可以选择不导入 React,而只是为了使用 JSX。详情请阅读这篇官方文章。)
下面是使用类语法创建反应组件的另一种方法。
import React from 'react';
class Sample extends React.Component {
render() {
return (
<div>
<h1>Sample</h1>
<h2>This is a component.</h2>
</div>
);
}
}
export default Sample;
类组件的render
方法返回 React 元素。
那么,两者之间有什么区别,您应该选择哪种方式来编写自己的 React 组件?
在 v16.7 及以下版本中,类组件具有状态管理(data
在 Vue.js 中)和生命周期方法——这两者对于有用的组件都至关重要——而功能组件则没有。
但是,从 v16.8 开始,React 在函数组件中引入了Hooks。Hooks负责状态管理和“副作用”(渲染后应该执行的操作)。
尽管一些生命周期方法在钩子中没有被“翻译”,但函数式组件可以完成与类组件几乎相同的功能。React 团队推荐你首选函数式组件。
当你准备好后,我们鼓励你在新编写的组件中尝试使用 Hooks。
从长远来看,我们预计 Hooks 将成为人们编写 React 组件的主要方式。
我应该使用 Hooks、类,还是两者兼而有之?
所以,如果你开始一个全新的 React 项目,或者你是 React 新手,我认为你应该首先考虑用函数式编写。如果你想使用只有类才能实现的功能,那么就引入类组件。函数式组件和类组件共存是完全没问题的。
在本文中,我将解释函数式方法。
模板
Vue.js 方式
Vue 组件有自己的语法,<template>
例如v-bind
,,。v-for
v-if
<template>
<div>
<h1>Hello, {{ name }} !</h1>
<a :href="link">Click me</a>
<ul>
<li v-for="item in items" :key="item.key">
{{ item.title }}
</li>
</ul>
<p v-if="isActive">Paragraph</p>
<p v-show="isShow">Second paragraph</p>
</div>
</template>
反应方式
在 React 中,您可以使用 JSX。
return (
<div>
<h1>Hello, {name} !</h1>
<a href={link}>Click me</a>
<ul>
{items.map(item => (
<li key={item.key}>{item.title}</li>
))}
</ul>
{isActive && <p>Paragraph</p>}
<p style={{ display: isShow ? 'initial' : 'none' }}>Second paragraph</p>
</div>
);
- JSX 不是模板引擎。它只有一个特殊语法 --
{}
,其余的都只是 JavaScript。 - 里面的语句
{}
被评估为 JavaScript。 - 没有等效的
v-show
。因此基本上您应该手动操作display
样式属性。 - 我稍后会讨论 CSS 类。
和 Vue 的 一样<template>
,函数式组件必须只返回一个根元素。但 React 有一个方便的辅助组件<React.Fragment>
,它允许你返回多个元素,这样你就不用<div>
为了满足框架的要求而用无用元素包裹它们了。
return (
<React.Fragment>
<h1>...</h1>
<h2>...</h2>
<h3>...</h3>
</React.Fragment>
);
<React.Fragment>
不会渲染为 DOM。在上面的例子中,你只会得到<h1>
、<h2>
和。<h3>
而且,<React.Fragment>
它有语法糖。它的名称可以省略。也就是说,上面的代码片段可以写成下面这样。
return (
<>
<h1>...</h1>
<h2>...</h2>
<h3>...</h3>
</>
);
很奇怪但是很方便,不是吗?
CSS 类
Vue.js 方式
Vue.js 提供了v-bind:class
一种操作 HTML 属性的方法class
。
<button class="btn" :class="{ 'btn-primary': isPrimary }">...</button>
<button class="btn" :class="['btn-primary', 'btn-small']">...</button>
反应方式
React 中没有特殊的方法。className
只是属性的等价物class
。class
是 JavaScript 中的保留关键字之一,因此 JSX 调用 this className
。
return <button className="btn btn-primary">...</button>;
尽管如此,classnames库将帮助您处理 HTML 类。
import classNames from 'classnames';
就像v-bind:class
。
const buttonClass = classNames({
btn: true,
'btn-primary': isPrimary
});
return <button className={buttonClass}>...</button>;
HTML
Vue.js 方式
要注入 HTML 字符串,您可以v-html
在 Vue.js 中使用。
<div v-html="htmlString"></div>
反应方式
在 React 中,有一个名为 的 prop dangerouslySetInnerHTML
。它实际上警告你随意插入 HTML 字符串是危险的举动。dangerouslySetInnerHTML
它接受一个具有__html
以 HTML 字符串作为值的属性的对象。
return <div dangerouslySetInnerHTML={{ __html: htmlString }} />;
活动
Vue.js 方式
@
在 Vue.js 中,事件用语法(语法糖 for )来表示v-on
。
<button @click="handleClick">Click me</button>
<form @submit.prevent="handleSubmit">...</form>
反应方式
React 采用更类似HTML 的方式。事件处理程序被传递给名为 onEventName 的 prop——例如onChange
。onSubmit
const handleClick = e => {/* blah blah... */};
return <button onClick={handleClick}>Click me</button>;
没有类似的事件修饰符.prevent
。
州(数据)
Vue.js 方式
在 Vue.js 中,组件的内部状态由方法的返回值定义data
。
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>
在组件的其他部分内部,您可以通过引用其状态值this
。
methods: {
increment() {
this.count += 1;
}
}
反应方式
在 React 中,你可以使用useState
钩子。嘿,钩子来了!
import React, { useState } from 'react'
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => setCount(count + 1);
return <button onClick="handleClick">{count}</button>;
}
Hooks 是用于访问 React 魔法的函数。useState
是用于管理组件状态的函数。
useState
以状态的默认值作为参数,返回一个包含 0. “状态变量” 和 1. “更新状态的函数” 的数组。状态的值只能通过该函数进行更新。
useState
由各州发起呼叫。
const [name, setName] = useState("John Doe");
const [age, setAge] = useState(20);
您可以将对象设置为状态的值。
const [user, setUser] = useState({ name: "John Doe", age: 20 });
表格
Vue.js 方式
在 Vue.js 中,v-model
处理表单输入。
<input type="text" v-model="name" />
v-model
让您实现双向数据流。
反应方式
React 没有引入双向数据更新的语法糖,需要自行结合 state 和 event 来实现。
const [name, setName] = useState('');
const handleInput = e => setName(e.target.value);
return <input type="text" onChange="handleInput" value="name" />;
几乎每次处理表单时都要写这种样板代码,确实有点烦人。但我觉得这种简洁,或者说“不加糖”、“尽可能自己写你需要的东西”的风格很符合 React 的风格。
方法
Vue.js 方式
在 中定义的方法内部methods
,您可以引用状态(data
)。并且可以在模板内部引用这些方法。
<script>
export default {
methods: {
sayHello() {
console.log(`Hello, ${this.name}!`)
}
}
}
</script>
反应方式
React 中没有类似 Vue 的功能methods
。React 组件本质上只是一个 JavaScript 函数,所以你可以直接使用它。
function MyComponent() {
const [name, setName] = useState('John');
function sayHello() {
console.log(`Hello, ${name}!`);
}
return <button onClick={sayHello}>...</button>;
}
参考
Vue.js 方式
在 Vue.js 中,ref
您可以直接访问 DOM。
<template>
<div>
<div ref="foo">...</div>
<button @click="handleClick">Click me</button>
</div>
</template>
<script>
export default {
methods: {
handleClick() {
console.log(this.$refs.foo);
}
}
}
</script>
反应方式
React 具有与 Vue 类似的功能ref
。
使用useRef
hook,你可以创建一个用于访问 DOM 的“ref 对象”。该对象的current
属性包含对 DOM 的引用。
import React, { useRef } from 'react';
function MyComponent() {
const target = useRef(null);
const handleClick = () => {
console.log(target.current);
};
return (
<>
<div ref={target}>...</div>
<button onClick={handleClick}>Click me</button>
</>
);
}
export default MyComponent;
计算属性
Vue.js 方式
Vue.js 具有“计算属性”。
<p>Hello, {{ fullName }} !</p>
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
};
},
computed: {
fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
}
计算属性是捕获计算结果的函数,其行为类似于模板块中的属性。
反应方式
我认为useMemo
hook 是“计算属性”的 React 版本。
import React, { useMemo } from 'react';
function MyComponent() {
const [firstName, setFirstName] = useState("John");
const [lastName, setlastName] = useState("Doe");
const fullName = useMemo(() => {
return `${firstName} ${lastName}`;
}, [firstName, lastName]);
return <p>Hello, {fullName} !</p>;
}
useMemo
将函数作为第一个参数,将数组作为第二个参数,并返回记忆值。
- 每次更新 props 或状态时,React 的功能组件都会重新渲染。
useMemo
但是,仅当作为第二个参数传递的数组中的值被更新时,才会重新计算第一个参数的函数。- 如果第二个参数数组中的值未更新,则将返回缓存值。
这种行为类似于 Vue 的计算属性,但它与其说是一种常见的模式,不如说是一种计算属性。你应该useMemo
只在真正需要优化时才使用它(我从这篇文章中学到的)。
手表
Vue.js 方式
Vue.js 为您提供了观察者——“对数据变化做出反应的更通用的方式”。
export default {
watch: {
name(valueAfterUpdate, valueBeforeUpdate) {
// ...
}
}
}
反应方式
React 没有与观察者相对应的东西。
你可以使用钩子来实现类似的功能useEffect
。我将在下一节中向你展示这个钩子。
但我经常认为该选项的用例并不多watch
,因为大多数时候,它可以用变化事件代替。
生命周期
Vue.js 方式
Vue.js 有许多生命周期钩子。
export default {
created() {/* ... */},
mounted() {/* ... */},
updated() {/* ... */},
destroyed() {/* ... */}
}
反应方式
在 React 函数式组件中,没有生命周期的概念。这里要简单得多。
- 当其 props 或状态更新时,功能组件会被渲染并重新渲染。
- 如果您想在渲染后立即执行某项操作,请将该操作放在
useEffect
钩子中。
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [items, setItems] = useState([]);
useEffect(() => {
someApi.getItems().then(response => {
setItems(response.data);
});
}, []);
useEffect
根据作为第二个参数传递的内容,行为会有所不同。
// if there is no 2nd argument,
// 1st argument is called on every renders.
useEffect(() => {});
// if 2nd argument is an empty array,
// 1st argument is called only on first render.
useEffect(() => {}, []);
// this is like "mounted" in Vue.js
// if 2nd argument contains one or more items,
// 1st argument is called on first render and when the items are updated.
useEffect(() => {}, [aaa, bbb]);
// this is like "mounted" plus "updated" & "watch", I guess.
useEffect
的第一个参数可以返回“清理”函数,该函数在其组件从 DOM 中删除之前调用。
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// ...
return () => {
// clean up function.
// this is like "destroyed" in Vue.
};
}, []);
return <div>...</div>;
}
另一方面,类组件具有constructor
与 Vue.js 类似的生命周期方法。我不会在本文中向您展示这些内容,但您可以从这篇文章中了解它们。
组件之间的交互
道具
Vue.js 方式
props
选项用于将数据从父组件传递到其子组件。
<Child :name="test" :item="sampleData" />
<script>
function Item(one, two) {
this.one = one
this.two = two
}
export default {
// validate its value with "type" and "required".
props: {
name: {
type: String,
required: false,
default: 'John Doe'
},
item: {
type: Item,
required: true
}
}
}
</script>
反应方式
在 React 中,从父级传递给子级的属性/特性也称为props
。
<Child name={test} item={sampleData} />
功能组件的第一个参数是 props。
import React from 'react';
function Child(props) {
// props.name
// props.item
}
您可以使用prop-types库进行验证。
import React from 'react';
import PropTypes from 'prop-types';
function Child(props) {
// props.name
// props.item
}
Child.propTypes = {
name: PropTypes.string,
item: PropTypes.shape({
one: PropTypes.string.isRequired,
two: PropTypes.number.isRequired
}).isRequired
};
Child.defaultProps = {
name: 'John Doe'
};
export default Child
发出事件
Vue.js 方式
在 Vue.js 中,子组件通过$emit
方法将事件通知给其父组件。
onSomethingHappened() {
this.$emit('hello');
}
然后,使用语法为通知事件注册一个处理程序@
。
<Child @hello="parentMethod" />
反应方式
React 没有事件触发的语法。你只需要将处理函数作为 prop 传递即可——也就是说,父组件决定要做什么,然后子组件执行。
function Child({ onHello }) {
const handleClick = () => {
console.log('hello there');
onHello();
};
return <button onClick={handleClick}>click me</button>;
}
function Parent() {
const parentMethod = () => {/* blah blah... */};
return <Child onHello={parentMethod} />;
}
投币口
Vue.js 方式
Vue.js 有slot
插入子元素的功能。
<Content>
<p>Hello world</p>
</Content>
Content
组件如下:
<template>
<article>
<h1>This is a title.</h1>
<slot></slot>
</article>
</template>
当您要插入多个块时,您可以为每个块命名。
<MyComponent>
<template #header>
<MyHeader />
</template>
<template #content>
<MyContent />
</template>
<template #footer>
<MyFooter />
</template>
</MyComponent>
反应方式
在 React 中,children
prop 有插入元素。
<Content>
<p>Hello world</p>
</Content>
function Content({ children }) {
// children -> <p>Hello world</p>
return (
<article>
<h1>This is a title.</h1>
{children}
</article>
);
}
您既不能拥有多个children
,也不能命名它。
但children
只是一个道具。上面的例子本质上与下面相同:
<Content children={<p>Hello world</p>} />
因此您只需这样做即可插入多个元素。
return (
<MyComponent
header={<MyHeader />}
content={<MyContent />}
footer={<MyFooter />}
/>
);
function MyComponent({ header, content, footer }) {
return (
<div>
<header>{header}</header>
<main>{content}</main>
<footer>{footer}</footer>
</div>
)
}
总结
以下是我的印象:
- React 比 Vue.js 简单得多,并且有更多的空间供您即兴发挥。
- Vue.js 拥有更多 API,但更易于学习。
从这个意义上来说,我认为 Vue.js 设计得很好。我特别推荐给 JS 框架新手。这也是我第一个成功学习的 JS 框架(之前学习过 angular.js,但失败了)。
但是,现在我更喜欢 React。它简单而且足够用。
您更喜欢哪一个?
好了,就到这里啦!感谢阅读。希望你喜欢,并且这对你的学习有帮助!
文章来源:https://dev.to/_masahiro_h_/vue-js-developers-guide-to-react-lg0