Appearance
react 基础-函数组件的使用
写在前面
- 已经具备了一定的 HTML、CSS、JavaScript、ES6 基础
- react 官网地址
- 本文档的样例都是直接 html 页面,加载 react 核心库,react-dom 渲染库、和 babel 来呈现,不创建脚手架工程
react 简介
- react 是用于构建 Web 和原生交互界面的 js 库。
- 为什么要用
- 原生 JS 操作 DMOM 频繁,效率低(DOM-API 操作 UI)
- 使用原生 JS 操作 DOM,浏览器会进行大量的重置重绘
- 原生 JS 没有组件化编码方案,代码复用率低
- react 的特点
- 采用组件化模式、声明式编码,提高开发效率及组件复用率
- 使用虚拟 DOM+diffing 算法,尽量减少 DOM 操作
- 在 react native 中可以使用 react 语法进行移动端开发
创建和使用函数组件
js
/* 1.定义函数组件 */
function MyCompont() {
return <h1>Heollo React</h1>;
}
/* 2.将函数式组件渲染到页面上 */
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<MyCompont />);html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>hello react</title>
</head>
<body>
<!--容器-->
<div id="root"></div>
<!--引入react核心库-->
<script src="../js/react.development.js"></script>
<!--引入react-dom,用于支持react操作DOM-->
<script src="../js/react-dom.development.js"></script>
<!--引入babel 用于JSX转为JS-->
<script src="../js/babel.min.js"></script>
<script type="text/babel">
/* 此处需要写babel type="text/javascriptt" 因为之前是js代码,但现在写的不是js而是jsx*/
/* 1.定义函数组件 */
function MyCompont() {
return <h1>Heollo React</h1>;
}
/* 2.将函数式组件渲染到页面上 */
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<MyCompont />);
</script>
</body>
</html>组件:用来实现局部功能效果的代码和资源的整合(html/css/js),如一个按钮入口 函数名必须是大写字母开头,因为函数名就是组件名 组件标签要有闭合标签
关于虚拟 DOM
- 本质是个 Object 类型的对象(一般对象)
- 虚拟 DOM 比较轻,真实 DOM 比较重,因为虚拟 DOM 只 React 内部使用,无须真实 DOM 上那么多属性
- 虚拟 DOM 最后会被 React 转换成真实的 DOM 显示在页面上
关于 JSX
jsx 是 javascript xml 的全称,XML 早期用于数据的存储和传输 JSX 的语法规则
定义虚拟 DOM 时,不要写引号
虚拟 DOM 只能有一个根标签
标签必须闭合
标签首字母 (1).若小写字母开头,则将该标签转为 html 的同名标签,找不到对应的同名标签则报错 (2).若大写字母开头,React 则去渲染对应的组件,找不到则报错
标签中混入 JS 表达式,要用{}
指定 css 样式类名时,不用 class,用 className
内联样式要用
style = {{ key: value }}的样式,key 遵循 js 的驼峰命名的写法一定要注意区分 JS 表达式和 JS 语句(代码)
表达式:一个表达式可以产生一个值,可以放在任何一个需要值的地方。下面的都是表达式(有返回值) (1). a (2). a+b (3). demo(1) (4). arr.map() (5). function test(){} (6). a?fun1():fun2() (7). flag && fun()
JS 语句(代码) 下面的都是 JS 语句(代码) (1).if(){} (2).for(){} (3).switch(){}
条件渲染
用 if else 或者是三目运算
js
gVar = {
loginFlag: false,
};
if (gVar.loginFlag) {
return <Home />;
} else {
return <Login />;
}
// 三目运算
gVar.loginFlag ? <Home /> : <Login />;列表渲染
一般用 for 循环 和 array 的 map() 函数 来渲染组件列表。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>hello react</title>
</head>
<body>
<!--容器-->
<div id="root"></div>
<!--引入react核心库-->
<script src="../js/react.development.js"></script>
<!--引入react-dom,用于支持react操作DOM-->
<script src="../js/react-dom.development.js"></script>
<!--引入babel 用于JSX转为JS-->
<script src="../js/babel.min.js"></script>
<script type="text/babel">
/* 此处需要写babel type="text/javascriptt" 因为之前是js代码,但现在写的不是js而是jsx*/
/* 1.定义函数组件 */
function MyCompont() {
const products = [
{ title: "Cabbage", id: 1 },
{ title: "Garlic", id: 2 },
{ title: "Apple", id: 3 },
];
return (
<div>
<h1>列表渲染</h1>
<ul>
{products.map((item, index) => {
return <li key={item.title + item.id}>{item.title}</li>;
})}
</ul>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
/* 2.将函数式组件渲染到页面上 */
root.render(<MyCompont />);
</script>
</body>
</html>虚拟DOM中key要保证在兄弟结点中是唯一的
虚拟DOM中key的作用:
1.简单的说:key是虚拟DOM对象的标识,在更新显示时key起着极其重要的作用
2.详细的说:当状态的数据发生改变时,react会根据【新数据】生成【新的虚拟DOM】
随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
a.旧虚拟DOM中找到了与新虚拟DOM相同的key
(1).若虚拟DOM中内容没变,直接使用之前的真是DOM
(2).若虚拟DOM中内容变了,则生成新的真实DOM,随后替换页面中之前的真实DOM
b.旧虚拟DOM中未找到与新虚拟DOM相同的key
根据数据创建新的真实DOM,随后渲染到页面
用index作为key可能会引发的问题:
1.若对数据进行:逆序添加、逆序删除等破坏顺序的操作。
会产生没有必要的真实DOM更新,界面效果没问题,但效率低
2.如果结构中还包含了输入类的DOM
会产生错误的DOM更新,界面显示就有问题了
3.注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作
仅用于渲染列表用于展示,使用index作为key是没有问题的.但一般不推荐用index作为key
事件处理
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>hello react</title>
</head>
<body>
<!--容器-->
<div id="root"></div>
<!--引入react核心库-->
<script src="../js/react.development.js"></script>
<!--引入react-dom,用于支持react操作DOM-->
<script src="../js/react-dom.development.js"></script>
<!--引入babel 用于JSX转为JS-->
<script src="../js/babel.min.js"></script>
<script type="text/babel">
/* 此处需要写babel type="text/javascriptt" 因为之前是js代码,但现在写的不是js而是jsx*/
/* 1.定义函数组件 */
function MyCompont() {
function handleClick(e) {
console.log("handleClick e.target", e.target);
console.log("handleClick e.target.innerText", e.target.innerText);
alert("Click me");
}
function handleClickParam(param) {
console.log("handleClickParam param", param);
return handleClick;
}
return (
<div>
<h1>事件处理</h1>
<button onClick={handleClick}>Click me1</button>
<br />
<button
onClick={(e) => {
console.log("anonymous handleClick e.target", e.target);
alert("匿名事件处理函数");
}}
>
Click me
</button>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
/* 2.将函数式组件渲染到页面上 */
root.render(<MyCompont />);
</script>
</body>
</html>1.通过onXx指定事件处理函数(注意X要大写)
a.react使用的是自定义(合成)事件,而不是使用的原生DOM事件--为了进一步的兼容
b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素)--为了高效
2.通过event.target得到发生事件的DOM元素对象
Fragment 和空标签<></>
当做组件的根节点,而无需向 DOM 添加额外节点。解决jsx的要求只能有一个根节点而引入额外标签的问题
用法 <React.Fragment></React.Fragment>,react在生成真实DOM的时候,会丢掉这个标签
还有一种做法是写空标签<></>
但区别是Fragment可以写key属性,用于提供给React Diff用,但空标签不能写key属性
例子
作业
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>作业</title>
<style>
@charset "utf-8";
/* 样式重置 */
h1,
h2,
h3,
h4,
h5,
h6,
header,
hgroup,
hr,
input,
li,
ol,
p,
pre,
td,
textarea,
th,
ul {
margin: 0;
padding: 0;
}
ul,
oll,
li {
list-style: none;
}
a {
text-decoration: none;
color: #333;
}
i,
em {
font-style: normal;
}
input,
textarea,
button,
select,
a {
outline: none;
border: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
.todo-container {
position: static;
}
.todo-wrap {
width: 520px;
margin: 20px auto;
border: 1px solid #999;
border-radius: 5px;
}
.todo-main {
margin: 10px;
border: 1px solid #999;
border-bottom-width: 0;
width: 500px;
}
.todo-main li {
position: relative;
border-bottom: 1px solid #999;
height: 40px;
width: 500px;
line-height: 40px;
}
.todo-main li:hover {
background-color: #ddd;
}
.todo-main .hot {
position: relative;
top: -10px;
color: red;
font-size: 12px;
background-color: #666;
padding: 2px 5px;
border-radius: 15px;
}
</style>
<script>
const datas = new Array(20).fill("").map((item, index) => {
return {
title: `标题${index + 1}`,
content: `标题${index + 1}的详细内容`,
key: new Date().getTime(),
isHot: index % 2,
};
});
function showContent() {
alert("弹出对应消息的内容");
}
</script>
</head>
<body>
<div class="todo-wrap">
第十三讲作业说明:<br />
1.把生成的datas数组按照如下界面的界面样式显示出来<br />
2.数组的每个元素对象格式为{title:"xxx",content:"xxx",isHot:true,key:xxx}当isHot为真时,右上角显示一个new的标志<br />
2.点击列表时用alert弹出当前消息的内容(content字段)<br />
</div>
<div class="todo-container">
<div class="todo-wrap">
<!-- todo列表 -->
<ul class="todo-main">
<li onclick="showContent()">标题一<span class="hot">new</span></li>
<li onclick="showContent()">标题二</li>
<li onclick="showContent()">标题三</li>
</ul>
</div>
</div>
</body>
</html>