React 简介
教程说明
React(也叫Reactjs)是 Web前端开发的框架 ,大家必须先掌握Web前端开发的基础知识 HTML/CSS/JS编程,才能学习 React
而且我们这里对 React的讲解,有时会用到前面我们锻炼的项目实战
所以,大家也要先锻炼过我们提供药管系统项目实战,至少先锻炼到 09 删除客户 这里
不用框架就不行吗?
前面,我们的 药管系统项目实战 已经开发了一些功能
并没有使用 react 这样的框架,
需要动态的添加、改变界面时, 我们使用 HTML字符串模板,比如
// 列出条目时,条目里面的html内容
function genOneItemInnerHtml(item){
return `
<div class='result-list-item-info'>
<p><span class='field-name'>客户名</span><span>${item.name}</span></p>
<p><span class='field-name'>地址</span><span>${item.address}</span></p>
<p><span class='field-name'>电话</span><span>${item.phonenumber}</span></p>
</div>
<div class='result-list-item-btn-bar'>
<span class="btn-no-border btn-delete-item">删除</span>
<span class="btn-no-border btn-modify-item">修改</span>
</div>
`
}
然后通过这样使用模板字符串,实现动态的改变界面
let pEle = `
<div class='result-list-item' id='${item.id}'>
${genOneItemInnerHtml(item)}
</div>
`
document.querySelector('.result-list').insertAdjacentHTML("beforeend", pEle)
我们仔细回顾思考一下,可以发现一些问题:
- 问题1
字符串模板如果内容比较多,没有IDE助手功能,语法不高亮,非常不方便,很容易写错。
- 问题2
为了实现一些操作,需要额外添加元素或者属性,把应用数据保存在里面
比如,我们点击列出客户的条目右边的删除按钮时,为了代码能知道这个条目对应的客户id, 就额外添加了一个条目div的属性id,值就是客户id
这样使得界面html变得复杂化
- 问题3
有些功能只需要更新界面某个局部,但是往往更新了更大的范围
这样往往会带来性能问题。
上述这些问题,一些前端框架,比如 React、Vue 等,可以比较好的解决
我们下面的教程就介绍 React框架如何使用在我们的实战项目中
react简易开发环境搭建
要集成React框架库,典型做法 涉及到编译、打包等一系列操作,后面课程会介绍
这里先不搞的太麻烦,是为了让大家减少干扰, 先搞懂核心概念。
这里先介绍一个简易的、方便演示功能的,开发环境搭建方式
就是直接在HTML文件的 <head> 中包含react相关的库,如下所示
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" ></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.28.4/babel.min.js"></script>
注意:上面包含的库文件是开发过程中使用的 development 版本的
开发阶段应该使用开发版本,有更详细的错误提示,便于我们调试时发现问题
将来在正式发布的生产环境直接包含React库文件,应该使用 production 版本的,如下
<script src="https://unpkg.com/react/umd/react.production.min.js" ></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
注意:看视频时,看到的上面库链接,使用的是公共cdn加速库,
如果将来该库地址不可用,我们网站会保持更新,确保代码使用的是 可用的cdn加速库
实际上,我们建议生产环境不要使用公共cdn加速库,因为可靠性不能保证
我们需要搭建自己的cdn环境,实战班会有这方面的锻炼
JSX
简介
看下面的一个例子
<head>
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" ></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.28.4/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
let dt = new Date();
let element = <div>
<p>当前日期时间是</p>
<span>{dt.toLocaleDateString()}</span> --
<span>{dt.toLocaleTimeString()}</span>
</div>
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(element);
</script>
</body>
大家注意到 element变量 赋值右边的表达式
它居然好像直接就是一个html的代码片段, 但是却没有放到引号中,作为字符串!!!
和html的明显区别是,里面包含像下面这样的 js表达式
{dt.toLocaleDateString()}
js表达式放在花括号中。
这种 类似HTML的标记语法 被称之为 JSX , 全称 JavaScript XML
这种语法是React引入的。
虽然React并不是必须使用JSX,但是它是我们使用React的重要原因之一。
它解决了我们前面说的,直接使用html片段字符串 的问题。
vscode 这样的IDE都支持它,语法高亮,自动补齐、错误提示 这些功能提升了写模板html的效率
而且,更重要的是, 支持内嵌 js 表达式, 使得HTML模板功能非常灵活直观。
上例中,包含了jsx内容的代码,是放在这样的标签中的
<script type="text/babel">
引入的 babel内置了jsx代码编译器, 看到 type="text/babel" 这样的js代码段,
会把JSX语句转译为浏览器能理解的js代码
比如,上例中,最终会被babel转化为类似这样的js代码
let dt = new Date();
let element = React.createElement(
"div", null,
React.createElement("p", null, "当前日期时间是"),
React.createElement("span", null, dt.toLocaleDateString()),
" --",
React.createElement("span", null, dt.toLocaleTimeString())
);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(element);
所以,一定要切记:JSX只是看起来像HTML,它本质上是 js表达式
可以在线转化 https://babeljs.io/repl/
可以发现,最终 JSX 元素 对应的是 React.createElement 方法的返回值
它是 React element 类型的对象,内部封装了将来要产生的的HTML元素信息
React.createElement 方法的详细描述,可以点击这里,查看文档
JSX 元素 , 也就是 React.createElement 返回值, 本质就是一个 js 的Object。
它 代表了 一个 DOM element 或者 user-defined component.
It's not the actual DOM element itself, but rather a description of what the DOM element should look like. This object contains the following properties:
type: The type of the element, which can be a tag name string (e.g., 'div', 'span'), a React component type (a class or a function), or a React fragment type.
props: An object containing the properties (attributes) that will be passed to the element or component.
key: An optional key used by React to identify elements during updates.
ref: An optional ref used to access the underlying DOM element or component instance.
React uses these React elements to build and update the virtual DOM, which is an in-memory representation of the actual DOM. When the state or props of a component change, React compares the new virtual DOM with the previous one and updates only the necessary parts of the actual DOM.
后面的代码
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(element)
先通过 ReactDOM.createRoot 创建了一个 React root 对象
root对象的render方法,就是根据 React element 对象里面的信息,转化为对应的实际界面HTML,呈现在界面上。
这个 render ,翻译为中文叫 渲染 。
这里的 root 变量 对应的 ,就是要呈现在哪个HTML元素里面
注意:React支持创建多个root对象,比如
const root1 = ReactDOM.createRoot(document.getElementById('root1'));
root1.render(element1)
const root2 = ReactDOM.createRoot(document.getElementById('root2'));
root2.render(element2)
元素属性表达式
JSX 中,元素属性值 如果是 js 表达式,不需要加引号
如下
const element = <img src={user.avatarUrl}></img>
如果属性设置值为js表达式,并且里面还有多个子属性,比如style,需要这样设置
let fontSize = '.92em'
const element = <div
style={{
color:'green',
border:"1px #3c763d solid",
marginTop:'1em',
fontSize: fontSize
}}>
</div>
这是我初学JSX最让我困惑的地方。
为啥要这样呢?
因为JSX花括号里面的一定是 符合js语法的表达式 ,所以用一个对象来表示键值对这种多个属性的设置
所以,里面多了一对花括号
那 margin-top 为何要变成 marginTop 这种驼峰命名呢?
如果是因为中间的减号,可以用双引号把key括起来啊。
官方文档的说法是 : 因为 JSX本质上是js 而不是HTML,所以这样命名。
好吧,就这么理解吧,虽然感觉不是很有逻辑。
好在vscode这样的集成开发环境对JSX支持很好,名称补齐功能一样有效
空元素
如果是空元素,一定要有反斜杠表示关闭元素
比如
const element = <img src={user.avatarUrl} />;
可以用括号
比如
let element = (
<div>
<p>当前日期时间是</p>
<span>{dt.toLocaleDateString()}</span> --
<span>{dt.toLocaleTimeString()}</span>
</div>
)
主要用于多行jsx,否则第一个元素必须写在等号那行后面
必须有顶层元素
不能出现这样的写法
let element = (
<p>当前日期时间是</p>
<span>{dt.toLocaleDateString()}</span> --
<span>{dt.toLocaleTimeString()}</span>
)
必须要有一个顶层元素,如果不想用 div,可以这样用一个空标签
let element = (
<>
<p>当前日期时间是</p>
<span>{dt.toLocaleDateString()}</span> --
<span>{dt.toLocaleTimeString()}</span>
</>
)
更新渲染内容
根据目前学到的内容,不难推测,再次调用 root 的 render 方法,可以重新渲染
比如,下面的代码就每隔1秒更新
<head>
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" ></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.28.4/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function tick(){
let dt = new Date();
return (
<div>
<p>当前日期时间是</p>
<span>{dt.toLocaleDateString()}</span> --
<span>{dt.toLocaleTimeString()}</span>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'))
setInterval(()=>{root.render(tick())}, 1000);
</script>
</body>
只更新变化的部分
上面的示例网页,如果我们打开devTools,查看元素的更新,我们可以发现 :
虽然我们render的是整个 tick 返回的div
但是每隔1秒,变化的只是这个span元素
<span>{dt.toLocaleTimeString()}</span>
因为 React 会 比较 原来的内容和更新部分, 只更新变化的部分,没有变动的不更新
如果网页应用更新部分包含大量信息的信息,而只变动了一小部分,这样可以大大的提高性能
独立 jsx 文件
当然,像上面这样HTML中包含整个代码,就没法利用集成开发环境,比如vscode的JSX代码助手的全部功能,因为文件扩展名是 html
所以,可以把jsx部分代码提取出来,单独放到一个扩展名为 jsx 的文件中、
如下
HTML文件内容
<head>
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" ></script>
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.28.4/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel" src="logic.jsx"></script>
</body>
在创建一个jsx文件,文件名为: logic.jsx ,和html文件放置在同一个目录下
function tick(){
let dt = new Date();
return (
<div>
<p>当前日期时间是</p>
<span>{dt.toLocaleDateString()}</span> --
<span>{dt.toLocaleTimeString()}</span>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'))
setInterval(()=>{root.render(tick())}, 1000);
操作演示一下:加上事件处理onxxxx,会自动代码提示补齐
当然,目前的做法:导入Babel,运行时转化jsx为js,效率太低了。
正式产品运行, 不会像这样,而是运行前先使用工具转化好,涉及到编译、打包等一系列操作,后面课程会介绍
这里先不搞的太麻烦,是为了让大家减少干扰, 先搞懂核心概念。