一、基于rem单位的屏幕等比例缩放
rem是浏览器描述长度的单位,含义为:相对于 `html` 的字体大小的单位。1rem = html 根节点上1个字符的宽度。使用 `rem` 单位设置的元素尺寸,会在不同的设备屏幕宽度下(例如:手机屏幕和平板屏幕)等比例缩放。当页面元素需要在不同屏幕宽度下保证元素的比例大小不变时,则可以使用 `rem`
使用方法:
<!-- 设置默认字体大小为我们所定义的标准字体大小 -->
<html lang="en" style="font-size: 20px;">
<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>Document</title>
<style>
body {
margin: 0;
}
.box {
background-color: #f00;
/* width: 300px;
height: 300px; */
/* 3. 根据标准字体大小计算rem值 */
width: 15rem;
height: 15rem;
}
</style>
</head>
<body>
<div class="box"></div>
</body>
<script>
// 1. 确立参考系,定义标准设备的屏幕宽度和字体大小
// 2. 比例公式(等式左右两边比例尺相同,从而达到等比例缩放的目的):标准屏幕宽度 / 标准字体大小 = 新的屏幕宽度 / 新的屏幕字体大小
// 3. 将页面样式中的 `px` 单位换算并替换为 `rem`,方法是`?rem = 元素的尺寸 / 标准字体大小`
// 4. 绑定窗口的 `resize` 和 `load` 事件,触发事件时计算出新的屏幕宽度时的字体大小,设置 `html` 的字体大小
const html = document.querySelector('html')
// 1. 我们定义 标准屏幕宽为 600px 标准字体大小为 20px;
// 2. 计算新的屏幕尺寸下的新的字体大小
let newFontSize = window.innerWidth / (600 / 20)
// 重新计算字体大小
function resize() {
let newFontSize = window.innerWidth / (600 / 20)
console.log(newFontSize);
// 设置html根节点的字体大小
html.style.fontSize = `${newFontSize}px`;
}
// 3. 绑定窗口尺寸变化事件和页面加载事件
window.addEventListener('resize', resize)
window.addEventListener('load', resize)
</script>
</html>
二、css预编译工具
预编译就是在编译环节发生之前,提前进行一次编译。其目的通常是将一个浏览器无法识别的语法提前编译成浏览器能够识别的语法。例如: css预编译 将 sass 转换为 css,js预编译 将 ts 转换成 js 等。
预编译工具sass
sass 工具用于对 css 进行预编译,预编译的css内容,是一个 sass/scss 文件,文件中的语法,大部分和 css 相同,有一部分是预编译的语法。
安装:使用node.js 在终端中输入npm init -y 和 npm install -g sass
命令行中将sass编译为css的使用方法:
1、语法 sass <inputPath> <outputPath>
<inputPath> 要编译的 scss 或 sass 文件路径
<outputPath> 编译完的 css 文件的输出路径
sass main.scss ./css/main.css
2、 监视文件变化
添加 --watch 标识 可以让sass自动监视文件变化 然后自动输出到指定文件
sass --watch main.scss ./css/main.css
3、监视目录变化
路径参数变成 <inputDir>:<outputDir> 这样就可以监视文件夹中任一文件的变化并输出到对应文件夹
sass --watch ./sass:./css
sass编译内容变化
例如:main.scss编译为main.css内容变化如下
main.scss
// 引入其他scss文件
// 后缀名可以省略
@use './util/base';
// 引入css文件
// 后缀名不能省略
@use '../css/other.css';
// 变量
$size: 32px;
$color: rgb(170, 0, 255);
// 使用变量
.box {
width: 100px;
height: 100px;
// 访问变量
font-size: $size;
background-color: $color;
}
// 嵌套
// 嵌套的本质就是后代选择器
ul {
border: 5px solid #ff0;
li {
background-color: #f00;
}
}
.box2 {
// 引入其他 scss 文件内的变量
width: base.$width;
height: base.$height;
background-color: #0f0;
}
// 定义一个混合
@mixin fn($a: 50px, $b: orange){
height: $a;
color: $b;
border: 1px solid #000;
}
.box3 {
width: 100px;
// 不带参数调用混合
@include fn;
// 带指定参数
@include fn($b: rgb(132, 0, 255));
// 按顺序传参
@include fn(50px, #f00);
}
// 样式的继承
// 声明父样式
%parent {
width: 200px;
height: 200px;
background-color:rgb(132, 0, 255);
}
.box4 {
// 子样式继承父样式
@extend %parent;
// 子样式可以有自己的样式
color: #fff;
// 也可以覆盖父样式
width: 300px;
}
// 四则运算
.box4 {
// 加减法需要相同单位进行运算
// width: 1px + 1em;
width: 100px + 100px;
width: 200px - 100px;
width: 100% - 50%;
// 乘除
// 乘法只能用一个带单位的值乘以一个不带单位的数字
width: 5px * 10;
// width: (50rem/25rem) * 1px;
// 除法只能使用 calc 计算函数
width: calc(50rem / 25rem) * 1px;
}
main.css
h1 {
background-color: rgb(0, 191, 255);
}
h2 {
background-color: violet;
}
.box {
width: 100px;
height: 100px;
font-size: 32px;
background-color: rgb(170, 0, 255);
}
ul {
border: 5px solid #ff0;
}
ul li {
background-color: #f00;
}
.box2 {
width: 500px;
height: 700px;
background-color: #0f0;
}
.box3 {
width: 100px;
height: 50px;
color: orange;
border: 1px solid #000;
height: 50px;
color: rgb(132, 0, 255);
border: 1px solid #000;
height: 50px;
color: #f00;
border: 1px solid #000;
}
.box4 {
width: 200px;
height: 200px;
background-color: rgb(132, 0, 255);
}
.box4 {
color: #fff;
width: 300px;
}
.box4 {
width: 200px;
width: 100px;
width: 50%;
width: 50px;
width: 2px;
}
/*# sourceMappingURL=main.css.map */
三、bootstrap
官网:<https://getbootstrap.com/>
bootstrap 是一个用于制作页面界面的框架
安装:
1、点击页面中的 download 按钮:<https://getbootstrap.com/docs/5.0/getting-started/download/>
2、使用node.js 在终端中输入npm init -y 和 npm i bootstrap
display显示卡方式 https://getbootstrap.com/docs/5.2/utilities/display/
浮动float https://getbootstrap.com/docs/5.2/utilities/float/
定位position https://getbootstrap.com/docs/5.2/utilities/position/
flex布局 https://getbootstrap.com/docs/5.2/utilities/flex/
grid网格 https://getbootstrap.com/docs/5.2/layout/grid/
背景色 https://getbootstrap.com/docs/5.2/utilities/background/
文本色 https://getbootstrap.com/docs/5.2/utilities/colors/
文本+背景色 https://getbootstrap.com/docs/5.2/helpers/color-background/
元素大小 https://getbootstrap.com/docs/5.2/utilities/sizing/
内边距外边距space: https://getbootstrap.com/docs/5.2/utilities/spacing/
元素间的间距stacks: https://getbootstrap.com/docs/5.2/helpers/stacks/
文本有关内容 https://getbootstrap.com/docs/5.2/utilities/text/
选择 https://getbootstrap.com/docs/5.2/forms/select/
单选和多选等按钮 https://getbootstrap.com/docs/5.2/forms/checks-radios/
表单验证
1、原生表单验证
<!--
input 的验证属性
required
pattern
minlength maxlength
min max
type
novalidate 屏蔽html自带的验证报告
input 的 validity 属性
validity.valueMissing
validity.patternMismatch
validity.rangeOverflow
validity.rangeUnderflow
validity.tooLong
validity.tooShort
validity.valid
自定义提示信息
input.setCustomValidity
form 或 input 的 checkValidity
显示验证报告
form.reportValidity()
表单验证总结步骤
1. 给表单添加 novalidate 屏蔽自动验证报告
2. 对每个元素的 validity 属性进行验证
3. 根据每个元素的验证结果设置自定义异常 setCustomValidity
4. 提交验证报告
5. 根据验证结果执行后续网络操作
-->
<!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>Document</title>
<link rel="stylesheet" href="./css/bootstrap.css">
</head>
<body class="d-flex justify-content-center">
<!-- novalidate 屏蔽默认的表单提交时的验证报告
屏蔽掉自动的验证报告的目的 是为了我们自己好去控制验证报告
-->
<form class="card p-3" style="width: 500px;" onsubmit="return false" novalidate>
<div class="vstack gap-3">
<div class="row">
<label class="col-3 col-form-label">姓名</label>
<div class="col">
<!-- required 必填 -->
<!-- <input name="name" required type="text" class="form-control"> -->
<!-- pattern 正则表达式 -->
<!-- <input name="name" pattern="[\s\S]*张三[\s\S]*" type="text" class="form-control"> -->
<!-- minlength 最小长度 maxlength 最大长度 -->
<!-- <input name="name" minlength="2" maxlength="10" type="text" class="form-control"> -->
<!-- min 最小值 max 最大值 -->
<input name="name" min="0" max="200" type="number" class="form-control">
<!-- type 类型验证 -->
<!-- <input type="email" class="form-control"> -->
</div>
</div>
<div class="row">
<div class="col-3"></div>
<div class="col">
<button class="btn btn-primary w-100">提交</button>
</div>
</div>
</div>
</form>
<!-- 1. 添加 novalidate -->
<form class="form2 card p-3" style="width: 500px;" onsubmit="return false" novalidate>
<div class="vstack gap-3">
<div class="row">
<label class="col-3 col-form-label">姓名</label>
<div class="col">
<input required minlength="2" maxlength="10" name="name" type="text" class="form-control">
</div>
</div>
<div class="row">
<label class="col-3 col-form-label">年龄</label>
<div class="col">
<input required min="0" max="150" name="age" type="number" class="form-control">
</div>
</div>
<div class="row">
<div class="col-3"></div>
<div class="col">
<button class="btn2 btn btn-primary w-100">提交</button>
</div>
</div>
</div>
</form>
</body>
<script>
let btn = document.querySelector('button')
let form = document.querySelector('form')
let nameInp = document.querySelector('input[name=name]')
let form2 = document.querySelector('.form2')
let btn2 = document.querySelector('.btn2')
let nameInp2 = document.querySelector('.form2 input[name=name]')
let ageInp = document.querySelector('.form2 input[name=age]')
btn.addEventListener('click', () => {
// 可以在此处通过代码验证用户的输入
// 可以通过输入框的 validity 属性 来判断用户是否输入正确
// validity.valueMissing -> required 用户没填数据时为 true
// validity.patternMismatch -> pattern 用户输入不满足正则 true
// validity.rangeOverflow -> max 用户输入超过最大值 true
// validity.rangeUnderflow -> min 用户输入小于最小值 true
// validity.tooLong -> maxlength 用户输入超出长度 不会触发 true
// validity.tooShort -> minlength 用户输入小于了指定长度 true
// validity.valid -> 输入没有问题 验证通过 true
// console.log(nameInp.validity);
// if(nameInp.validity.tooShort) {
// console.log('姓名长度不够');
// }
if (nameInp.validity.rangeOverflow) {
// 手动设置错误提示
nameInp.setCustomValidity('年龄不能大于200')
} else if (nameInp.validity.rangeUnderflow) {
nameInp.setCustomValidity('年龄不能小于0')
} else {
console.log(nameInp.validity);
// 若验证没有问题时应当设置为 空字符串
nameInp.setCustomValidity('')
}
// checkValidity 检测是否表单输入有效
// 若返回true 代表输入有效 否则为false
if (!form.checkValidity()) {
// 显示验证报告
form.reportValidity()
} else {
// 若表单没有问题 输入都有效 则可以执行后续的代码
// 后续代码写在此处 例如 发送网络请求
console.log('验证通过');
}
})
// 总结表单验证
btn2.addEventListener('click', () => {
// 2. 验证所有表单项
if (nameInp2.validity.valueMissing) {
// 3. 设置错误信息
nameInp2.setCustomValidity('请输入姓名')
} else if (nameInp2.validity.tooShort) {
nameInp2.setCustomValidity('请输入至少2个字的姓名')
} else {
nameInp2.setCustomValidity('')
}
if (ageInp.validity.valueMissing) {
ageInp.setCustomValidity('请输入年龄')
} else if (ageInp.validity.rangeOverflow) {
ageInp.setCustomValidity('请输入不超过150的年龄')
} else if (ageInp.validity.rangeUnderflow) {
ageInp.setCustomValidity('请输入不低于0的年龄')
} else {
ageInp.setCustomValidity('')
}
if (!form2.checkValidity()) {
// 4. 发起验证报告
form2.reportValidity()
} else {
// 5. 验证通过 执行后续逻辑
console.log('验证通过');
}
})
</script>
</html>
2、bootstrap自动表单验证
<!--
参考:https://getbootstrap.com/docs/5.2/forms/validation/
知识点
添加 valid-feedback invalid-feedback 验证报告
通过 was-validated 开启验证报告
-->
<!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>Document</title>
<link rel="stylesheet" href="./css/bootstrap.css">
</head>
<body class="d-flex justify-content-center">
<!-- 给form添加类名 was-validated 就可以开启验证效果 -->
<form class="card p-3" style="width: 500px" onsubmit="return false" novalidate>
<div class="vstack gap-3">
<div>
<label class="form-label">姓名</label>
<input required type="text" class="form-control">
<!-- 验证的提示需要写在被验证的输入框下面 -->
<!-- valid-feedback 验证通过的提示文本 -->
<div class="valid-feedback">
ok
</div>
<div class="invalid-feedback">
请输入姓名
</div>
</div>
<div>
<label class="form-label">年龄</label>
<input min="0" max="150" type="number" class="form-control">
<div class="valid-feedback">
ok
</div>
<div class="invalid-feedback">
请输入0~150的年龄
</div>
</div>
<div>
<button class="btn btn-primary w-100">提交</button>
</div>
</div>
</form>
</body>
<script>
let btn = document.querySelector('button')
let form = document.querySelector('form')
btn.addEventListener('click', () => {
form.classList.add('was-validated')
})
</script>
</html>
3、bootstrap手动表单验证
<!--
参考:https://getbootstrap.com/docs/5.2/forms/validation/
知识点
在不使用 html 自带的表单验证的情况下
在自行验证后 可以通过添加 is-valid is-invalid 手动指定输入框的验证结果
-->
<!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>Document</title>
<link rel="stylesheet" href="./css/bootstrap.css">
</head>
<body class="d-flex justify-content-center">
<form class="card p-3" style="width: 500px" onsubmit="return false" novalidate>
<div class="vstack gap-3">
<div class="row">
<label class="col-3 col-form-label">姓名</label>
<div class="col">
<input name="name" type="text" class="form-control">
<div class="valid-feedback">ok</div>
<div class="invalid-feedback">error</div>
</div>
</div>
<div class="row">
<label class="col-3 col-form-label">年龄</label>
<div class="col">
<input name="age" type="number" class="form-control">
<div class="valid-feedback">ok</div>
<div class="invalid-feedback">error</div>
</div>
</div>
<div class="row">
<div class="col-3"></div>
<div class="col"><button class="btn btn-primary w-100">提交</button></div>
</div>
</div>
</form>
</body>
<script>
let nameInp = document.querySelector('input[name=name]')
let ageInp = document.querySelector('input[name=age]')
// 查询错误提示的元素
let nameErrTip = document.querySelector('input[name=name]~.invalid-feedback')
let ageErrTip = document.querySelector('input[name=age]~.invalid-feedback')
let btn = document.querySelector('button')
btn.addEventListener('click', () => {
// 手动通过代码进行验证
// 清空所有的 is-valid 和 is-invalid
nameInp.classList.remove('is-valid', 'is-invalid')
ageInp.classList.remove('is-valid', 'is-invalid')
// 定义一个代表验证通过的变量
let nameValid = true
if (nameInp.value.trim() === '') {
nameErrTip.textContent = '请输入姓名'
nameValid = false
} else if (nameInp.value.trim().length < 2 || nameInp.value.trim().length > 10) {
nameErrTip.textContent = '请输入2~10个字的姓名'
nameValid = false
}
let ageValid = true
if (Number(ageInp.value) < 0 || Number(ageInp.value) > 150) {
ageErrTip.textContent = '请输入0~150之间的年龄'
ageValid = false
}
nameInp.classList.add(nameValid ? 'is-valid' : 'is-invalid')
ageInp.classList.add(ageValid ? 'is-valid' : 'is-invalid')
if (nameValid && ageValid) {
// 整个表单输入正确的情况
// 可以执行后续的网络请求
console.log('验证通过');
}
})
</script>
</html>
四、react
中文官网:<https://zh-hans.reactjs.org/>
react 是一个 js核心库,如同 jquery 一样,具有大量 react 生态(围绕 react 核心开发的库)
特点:
声明式
也就是js中的数据决定页面最终渲染的结果
声明式不是响应式,但往往都是同时出现共同作用页面
响应式:数据变化页面会立即更新
组件化
一个包含所有外观和行为的,独立可运行的模块,称为组件
组件化的思想可以将复杂页面,化繁为简的进行设计
组件可提高代码复用性
一次学习,跨平台编写
使用 react 可以开发 桌面web页面,移动端页面,移动app,桌面app等
react可以在现用的html页面中直接使用,通过cdn引入以下几个库:
<!-- react 需要引入 react 核心库 和 react-dom 库 -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- 为了更方便书写 react 通常会使用 jsx 语法,为了支持该语法 需要引入 babel -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
1、简单示例
<!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</title>
<!-- 1. 引入库 -->
<!-- react 需要引入 react 核心库 和 react-dom 库 -->
<script src="js/react.development.js"></script>
<script src="js/react-dom.development.js"></script>
<!-- 为了更方便书写 react 通常会使用 jsx 语法,为了支持该语法 需要引入 babel -->
<script src="js/babel.min.js"></script>
</head>
<body>
<!-- 2. 创建用于 react 渲染的根节点 -->
<div id="root"></div>
</body>
<!-- 3. 给script标签添加 type="text/babel" -->
<script type="text/babel">
// 4. 创建 root 节点对象
// 创建 react 的 root 根节点对象
const root = ReactDOM.createRoot(document.querySelector('#root'))
// 5. 渲染页面内容
// 使用 root.render 渲染页面内容
// render 函数中可以使用 jsx 语法声明页面内容
// jsx 写的内容 <h1>hello world</h1> 本质上是 js 代码
root.render(<h1>hello world</h1>)
</script>
</html>
2、jsx语法
<!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</title>
<script src="js/babel.min.js"></script>
<script src="js/react.development.js"></script>
<script src="js/react-dom.development.js"></script>
<style>
.box {
width: 200px;
height: 100px;
background: #000;
}
.box.active {
background: #ff0;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector('#app'));
// 使用jsx创建一个react-dom对象
// react-dom 是 react 封装的类似 dom 对象的对象
// 作用: 用于描述页面元素
const h1 = <h1>hello world</h1>
// jsx 使用 圆括号 声明多行的 react-dom 对象
const box = (
<div className="box">
<span>hello box</span>
</div>
)
const element = (
<div>
root1
</div>
// react-dom 中只能有一个根节点
// <div>
// root2
// </div>
)
let now = new Date()
// 插值
const chazhi = (
<div>
{ /*使用花括号进行插值 插值的内容可以是js表达式*/}
{'当前时间: ' + now.toLocaleString()}
</div>
)
let id = 'thisIsMyId'
// 定义样式对象
let myStyle = {
width: '100px',
height: '100px',
backgroundColor: '#f00'
}
// 插值 html 属性
const htmlAttr = (
<div>
{/* 插值id */}
<div id={id}>
hello attr
</div>
{/* 插值 style */}
{/* style 属性无法直接赋值一个字符串如下: */}
{/*<div style="width: 100px; height: 100px; background-color: #f00"></div>*/}
<div style={myStyle}></div>
{/* 插值 class */}
<div className="box active"></div>
</div>
)
// 使用函数创建react-dom对象
const fnDom = React.createElement(
// 标签名
'div',
// 标签上的属性
{id: 'myFunctionDom', className: 'my-dom', style: {width: '500px', height: '50px', backgroundColor: '#00f'}},
// 子元素数组 或 标签体字符串
[
// 此处的第三个参数就是一个普通的字符串充当 span 标签的标签体
React.createElement('span', {key: '1'}, 'hello world'),
// 若此处 createElement 第三个参数是一个数组的话,需要给元素添加 key 属性
// key 属性是一个唯一值 不重复即可
React.createElement('span', {key: '2'}, 'hello world')
]
)
// root.render 函数可以渲染一个 react-dom 对象
root.render((
<div className="container">
{/* 插值的内容 若是 react-dom 对象 那么就会被页面显示出来 */}
{h1}
{box}
{element}
{chazhi}
{htmlAttr}
{fnDom}
</div>
))
</script>
</html>
3、元素渲染
<!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</title>
<script src="js/babel.min.js"></script>
<script src="js/react.development.js"></script>
<script src="js/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script type="text/babel">
let root = ReactDOM.createRoot(document.querySelector('#root'))
// 初始化渲染
let now = new Date()
root.render((
<div>
<div className="now">当前时间:</div>
<div className="time">
{now.toLocaleString()}
</div>
</div>
))
// 渲染循环
let timer = setInterval(() => {
// 修改渲染逻辑
now = new Date()
// 渲染内容
root.render((
<div>
<div className="now">当前时间:</div>
{/* react 在每次更新的时候,都会去对比每一个 react-dom 节点
只有发现该被对比的节点有变化时(标签体变化 子节点数量变化 节点元素变化等) 才会更新节点
*/}
<div className="time">
{now.toLocaleString()}
</div>
</div>
))
}, 1000)
</script>
</html>
4、条件渲染
<!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</title>
<script src="js/babel.min.js"></script>
<script src="js/react.development.js"></script>
<script src="js/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script type="text/babel">
let sex = 'male'
ReactDOM.createRoot(document.querySelector('#root')).render((
<div>
{/* 使用 && 进行短路运算 前一个表达式为true时 就显示后面表达式的内容 */}
{sex === 'male' && <div style={{color: '#00f'}}>男</div>}
{sex === 'female' && <div style={{color: 'pink'}}>女</div>}
{sex === 'other' && <div style={{color: '#ff0'}}>其他</div>}
{/* 使用三元运算符 按条件显示不同的内容 */}
{sex === 'male' ? <div style={{color: '#00f'}}>男</div> :
sex === 'female' ? <div style={{color: 'pink'}}>女</div> :
<div style={{color: '#ff0'}}>其他</div>
}
</div>
))
</script>
</html>
5、循环渲染
<!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</title>
<script src="js/babel.min.js"></script>
<script src="js/react.development.js"></script>
<script src="js/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script type="text/babel">
let students = [
{
name: '张三',
sex: 'male',
age: 17
},
{
name: '李四',
sex: 'female',
age: 24
},
{
name: '隔壁老王',
sex: 'other',
age: 30
},
]
ReactDOM.createRoot(document.querySelector('#root')).render((
<div>
<ul>
{/* 循环渲染,使用一个数组的map函数 返回一个由 react-dom 充当成员形成的一个新数组
循环渲染一定要添加 key
*/}
{students.map((item,index) => <li key={index}>姓名: {item.name}; 性别: {item.sex === 'male' ? '男' :
item.sex === 'female' ? '女' : '不详'
}; 年龄: {item.age}</li>)}
</ul>
</div>
))
</script>
</html>
5、组件
组件具备以下特点
1. 独立显示的页面内容
2. 独立维护的组件状态
3. 组件被当作标签使用
所以,组件是一个具备独立显示内容,独立维护状态,被当作标签使用的可复用模组
应用场景
可复用的页面内容,就可以考虑封装成组件
声明组件
类组件
render函数
构造函数和props
props.children
函数组件
参数props
函数组件没有this
创建组件
<!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>Document</title>
<script src="../js/babel.min.js"></script>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script type="text/babel">
// 声明类组件
// 需要继承 React.Component
class AComponent extends React.Component {
// 可以有组件的属性
label = 'h'
// 构造函数中包含props参数
// props 代表组件的 html 属性
// props 是只读属性
constructor(props) {
// 由于存在父类 React.Component
// 所以构造函数中应先调用super
super(props)
// render 函数中可以使用 this.props 访问此处的 props
console.log(props)
// 可以使用 props.children 代表组件标签体里的内容
console.log(props.children)
}
// 可以有组件方法
getTime() {
return new Date()
}
// 类组件中必须包含 render 方法
render() {
// render 方法必须返回一个 react-dom 对象
// 返回的 react-dom 对象用于描述组件长什么样子的
return (
<div>
{/* 组件中可以调用自己的属性和方法 */}
{this.label}: {this.getTime().getHours()}; m: {this.getTime().getMinutes()};
s: {this.getTime().getSeconds()}
<br/>
{/* 标签体的内容可以插值到 render 中 */}
{this.props.children}
</div>
)
}
}
// 函数组件
// 参数props就是标签的html属性
// props 中也包含 children
function BComponent(props) {
// 此处的this为 undefined
// 之所以为 undefined 是因为函数组件没有实例对象
console.log(this)
console.log(props)
// 可以在组件内声明其他函数和变量
let count = 0
function getCount() {
return count
}
// 函数组件必须返回一个react-dom对象
return (
<div>
<div>我是一个函数组件</div>
<div>{count}: {getCount()}</div>
<div>{props.children}</div>
</div>
)
}
ReactDOM.createRoot(document.querySelector('#root')).render((
<div>
{/* 组件被当作标签使用 */}
{/* 可以使用插值给属性赋值 这样赋值的属性就可以不是字符串 */}
<AComponent name="张三" age={16} isShow={false}>
<div>hello</div>
<div>world</div>
</AComponent>
<AComponent></AComponent>
<BComponent name="张三" age={16} isShow={false}>
<h1>这是BComponent的标签体</h1>
<div>hello</div>
<div>world</div>
</BComponent>
</div>
))
</script>
</html>
class组件状态state 参考文档 https://zh-hans.reactjs.org/docs/state-and-lifecycle.html
函数组件状态state 参考文档 https://zh-hans.reactjs.org/docs/hooks-state.html
class组件生命周期 参考文档 https://zh-hans.reactjs.org/docs/react-component.html#the-component-lifecycle
函数组件生命周期 参考文档 https://zh-hans.reactjs.org/docs/hooks-effect.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</title>
<script src="../js/babel.min.js"></script>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script type="text/babel">
// 知识点
// 绑定事件的基本
// 1. on+事件名称
// 2. 使用花括号插值一个事件处理程序
// 3. 获取事件对象
// 4. 给事件处理程序传参
// react 中 无法使用 return false 直接屏蔽默认事件
function App() {
// 事件处理程序添加参数ev
// ev代表事件对象
function contextMenuHandler(ev, id) {
// 屏蔽默认事件
ev.preventDefault()
console.log('contextMenuHandler')
console.log(ev)
console.log(id)
}
return (
<div>
{/*<button onContextMenu={contextMenuHandler}>点击</button>*/}
{/* 使用箭头函数在调用事件处理程序的时候传递参数 */}
<button onContextMenu={ev => {
contextMenuHandler(ev, 5)
}}>点击
</button>
{/* react 无法直接通过 return false 屏蔽默认事件 */}
<form onSubmit="return false">
<button>提交</button>
</form>
</div>
)
}
ReactDOM.createRoot(document.querySelector('#root')).render(<App/>)
</script>
</html>
class组件绑定的事件中使用this的方法
<!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</title>
<script src="../js/babel.min.js"></script>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
</head>
<body>
<div id="root"></div>
</body>
<script type="text/babel">
// 知识点
// 知识点背景
// 类组件中普通的事件绑定无法在事件处理程序中访问this关键字
// 应用场景
// 希望在绑定的事件函数中使用 this 来访问组件自身
// 类组件中事件处理程序如何赋值this关键字
// 1. constructor 中使用 bind
// 2. 在绑定事件插值时使用箭头函数
// 3. 在绑定事件插值时使用bind
class App extends React.Component {
state = {
count: 0
}
constructor(props) {
super(props);
// 1. constructor 中使用 bind
// this.clickHandler = this.clickHandler.bind(this)
}
clickHandler(ev) {
console.log('click')
console.log(ev)
console.log(this)
this.setState(state => ({count: state.count + 1}))
}
render() {
return (
<div>
<div>count: {this.state.count}</div>
{/*<button onClick={this.clickHandler}>点击</button>*/}
{/* 2. 在绑定事件插值时使用箭头函数 */}
{/*<button onClick={ev => {
this.clickHandler(ev)
}}>点击</button>*/}
{/* 3. 在绑定事件插值时使用bind */}
<button onClick={this.clickHandler.bind(this)}>点击</button>
</div>
)
}
}
ReactDOM.createRoot(document.querySelector('#root')).render(<App/>)
</script>
</html>