有了上一节的铺垫,基础环境我们已经创建好了,再次运行它
cd my-react-app
npm run dev
在浏览器中打开地址:http://localhost:5173
, 并在浏览器中右击,选择检查
菜单项,打开控制台,如下图所示
这是我们上节课操作的结果。到目前为止,你只要你跟着我一步一步的做,就行了。或许你已经懂得并了解了一react
的基础结构了,也或许你还一头雾水,不过不用担心,开胃菜还没开始。
react
以状态化构建组件的方式极大的优化了我们的开发成本,一但掌握你就爱不释手。
JSX
我们之前创建的文件都是JSX
文件,但是也可以是js
文件,但作为标准化的开发方案,我们建议用jsx格式的文件。但大多数 React
项目为了方便起见而使用 JSX
。
JSX比HTML更严格。您必须关闭标签。您的组件也无法返回多个JSX标签。您必须将它们包裹在共享父母中,也就是说定义的每个组件只能返回一个标签的组件,但这个标签部可以包含多个标签。如:<br /> <div> ... </div> <> ... </>
function AboutPage() {
return (
<>
<h1>About</h1>
<p>Hello there.<br />How do you do?</p>
</>
);
}
注意,空标签<></>
也是一个合法的标签。上面的示例表示定义了一个名为AboutPage
的组件,他返回的标签是一个空标签包裹的二个其它元素标签。
样式
在JSX
中给组件添加样式要注意,class
要用className
替代,如:
<img className="avatar" />
然后在单独的CSS文件书写样式:
/* In your CSS */
.avatar {
border-radius: 50%;
}
样式文件你直接导入就可以了。在组件的开头导入如:
import './public.css';
显示数据
JSX
允许您将标记放入 JavaScript
中。大括号可让您“逃逸”到 JavaScript
中,以便您可以从代码中嵌入一些变量并将其显示给用户。例如,这将显示:user.name。
return (
<h1>
{ user.name }
</h1>
);
您也可以从 JSX
属性中“转义到 JavaScript”,但您必须使用大括号而不是引号。例如,将字符串作为 CSS
类传递,但读取 JavaScript
变量值,然后将该值作为属性传递:
return (
<img
className="avatar"
src={user.imageUrl}
/>
);
在你的src
目录中创建文件:profile.jsx
文件,并添加以下内容
示例:profile.jsx
const user = {
name: 'Hedy Lamarr',
imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
imageSize: 90,
};
export default function Profile() {
return (
<>
<h1>{user.name}</h1>
<img
className="avatar"
src={user.imageUrl}
alt={'Photo of ' + user.name}
style={{
width: user.imageSize,
height: user.imageSize
}}
/>
</>
);
}
创建一个样式文件:styles.css
.avatar {
border-radius: 50%;
}
在你的App.jsx文件中导入并调用这个组件:
import './App.css'
import "./styles.css";
import Profile from './Profile'
function App() {
return (
<>
<Profile />
</>
)
}
export default App
在上面的例子中,不是一个特殊的语法,而是一个JSX大括号里面的常规对象。当您的样式依赖于 JavaScript
变量时,可以使用该属性。
条件渲染
在 React 中,编写条件没有特殊的语法。相反,您将使用与编写常规 JavaScript
代码时相同的技术。例如,您可以使用 if
语句有条件地包含 JSX:
let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
</div>
);
也就是说你可以把 JSX
当作 JavaScript
的一种超集,安包含了 JavaScript
集合. 比如下面三目运算符的应用:
<div>
{
isLoggedIn ?
<AdminPanel /> :
<LoginForm />
}
</div>
以用诸如下面的语法:
<div>
{ isLoggedIn && <AdminPanel /> }
</div>
显示列表数据
您将依靠 for
循环和数组 map()
函数等 JavaScript
功能来呈现组件列表。
例如,假设您有一系列产品:
const products = [
{ title: 'Cabbage', id: 1 },
{ title: 'Garlic', id: 2 },
{ title: 'Apple', id: 3 },
];
在组件内部,使用该函数将产品数组转换为项目数组:<li>
const products = [
{ title: 'Cabbage', isFruit: false, id: 1 },
{ title: 'Garlic', isFruit: false, id: 2 },
{ title: 'Apple', isFruit: true, id: 3 },
];
export default function ShoppingList() {
const listItems = products.map(product =>
<li
key={product.id}
style={{
color: product.isFruit ? 'magenta' : 'darkgreen'
}}
>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
}
请注意 li
有个属性 key
, 凡是这种列表,对于列表中的每个项目,都应传递一个字符串或数字标识,以便在其同级中唯一标识该项目。这种标识属性为key
。也就是相当于 id
属性。React 使用您的 key
来识别您稍后的插入、删除或重新排序项目等操作。
响应事件
我们知道在 html
的元素中都有响应事件。在 React
中您可以通过在组件中声明事件处理程序函数来响应事件:
function MyButton() {
function handleClick() {
alert('你点击了我');
}
return (
<button onClick={handleClick}>
点击这里
</button>
);
}
请注意,onClick
内的事件名称末尾没有括号!不要调用事件处理程序函数:只需将其传递下来即可。当用户点击按钮时,React 将调用你的事件处理程序。onClick={handleClick}
与 onClick={handleClick()}
是不一样的, 前者是表示botton
单击事件由 handleClick
函数代理,而后者表示直接定义了一个匿名代理函数,这个函数内执行了handleClick()
函数。
UI显示更新
通常,你会希望你的组件“记住”一些信息并显示它。例如,您可能想要计算单击按钮的次数。为此,请向组件添加状态 即 (state)
。
首先,从 React 导入 useState:
import { useState } from 'react';
现在,您可以在组件中声明一个状态变量:
function MyButton() {
const [count, setCount] = useState(0);
// ...
您将从以下两个方面获得:当前状态 count
和允许您更新它的函数 setCount
。你可以给它们起任何名字,但惯例是写 [something, setSomething]
第一次显示该按钮时,状态是 count => 0
。如果要更改状态,请调用setCount(新值)
并将其传递给它。当调用setCount(1)
后,会立即将更新后的值传递给count
变量, 每当状态变量 count 的改变,引用这一变量的组件就会自动更新渲染。
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
你单击了 { count } 次
</button>
);
}
完整示例如下,我们在src下新建文件 buttonClickTest.jsx文件
import { useState } from 'react';
export default function ButtonClickTest() {
return (
<div>
<h1>Counters that update separately</h1>
<MyButton />
<MyButton />
</div>
);
}
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}
再次修改App.jsx文件:
import './App.css'
import "./styles.css";
// import Profile from './Profile'
import ButtonClickTest from './buttonClickTest';
function App() {
return (
<>
<ButtonClickTest />
</>
)
}
export default App
分别单击两个按钮,你会发现结果符合预期。
使用Hooks
很多人把Hooks
直接翻译成钩子,实在是不贴切。依我看这里把它翻译成纽带
现合适。react
提供了很多Hook
, 上面useState
就是一个hook
。其它常用的我后面都会讲到。其作用就是让各个组件之前如何共享数据和更新数据。
以上面的示例为例,两个按钮的点击互不影响,一个按钮点击次数的变化不会影响到另一个按钮的点击次数。现在我们来改一改,如何让这两个的按钮的点击数据实现共享呢。下面的示例说明了一切:
export default function ButtonClickTest() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update together</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}
我们看到上面的 我们调用 MyButton
组件时写法上有些变化,我们给组件里传了两个参数 count
和 onClick
,明确的告诉组件使用统一指定的参数。 但是我们在定义 MyButton 组件的时候并没有接收参数。这就需要我们把 MyButton 修改一下。其实组件都能接收一个 Props 对象,这个对象是可以自定义的。修改如下:
function MyButton({ count, onClick }) {
return (
<button onClick={onClick}>
你单击了 {count} 次
</button>
);
}
如果我们的VSCode中出现 count 和 onClick下面有红线提示,表示vite对数据类型有个检查,可以忽略这个提示,但如果你看它实在恶心,那我们就修改一下vite的配置,不让它进行安全类型检查。
找到项目目录下的 .eslintrc.cjs
文件,打开它,在rules
里添加以下配置即可。
...
rules: {
...
"react/prop-types": "off",
},
好了,现在你应该对 React 有了最基本的了解了吧。