React基本介绍及js文件的引入,ReactDOM.render()渲染页面,防止XSS攻击,编程范式,JSX的使用/语法规则/注释/输出数据类型/在属性上使用表达式,JSX列表渲染及条件渲染。
1.react基本介绍
一个用于构建用户界面的JavaScript库。
1.1起源
`React` 起源于 `Facebook` 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,做出来以后,发现这套东西很好用,就在2013年5月开源了,随后越来越多人开始关注和使用 `React`,慢慢的 `React` 就成为了当前最流行的前端开发框架之一。
1.2特点
- - `React` 采用了声明式编程范式,可以更加方便的构建 `UI` 应用
- - 内部封装更加高效的与底层`DOM`进行交互的逻辑,提高性能的同时也能帮助我们更加专注于业务
- - 可以很好的接纳其它框架或库与其进行配合
1.3React 全家桶
- - `React` : 提供框架(库)核心功能,如 `组件`、`虚拟DOM` 、渲染等
- - `create-react-app` : 脚手架,提供一套用于快速 构建和打包 `React` 项目的工具
- - `react-router` : 基于 `React` 的路由库
- - `redux、react-redux` : 状态管理库
2.React的加载引入
- - 基于浏览器 <script> 的模式
- - 基于自动化的集成环境模式
2.1基于浏览器 <script> 的模式
React.js 框架本身包含两个部分
- - react.js:提供 React.js 核心功能代码,如:虚拟 dom,组件
- - react-dom.js:提供了与浏览器交互的 DOM 功能,如:dom 渲染
引入自己下载的react.js和react-dom.js文件:
html:
<body>
<div id="app"></div>
<script src="./js/react.production.min.js"></script>
<script src="./js/react-dom.production.min.js"></script>
<script src="js/app.js"></script>
</body>
也可以引入通过 CDN 获得 React 和 ReactDOM 的 UMD 版本:
开发环境:
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
上述版本仅用于开发环境,不适合用于生产环境。压缩优化后可用于生产的 React 版本可通过如下方式引用:
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
通过 CDN 的方式引入 React,官方建议设置 crossorigin
属性,同时建议验证使用的 CDN 是否设置了 Access-Control-Allow-Origin: *
HTTP 请求,这样能在 React 16 及以上的版本中有更好的错误处理体验。
示例:
html:
<body>
<div id="app"></div>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="js/app.js"></script>
</body>
JS:
ReactDOM.render(
'<div>开课吧</div>',
document.getElementById('app')
);
结果:发现会将整个内容以及标签全部渲染出来
原因:React内部是使用innerText而不是innerHTML进行渲染(为防止XSS攻击)
正确写法:需要使用React专属的JPX格式才可以进行渲染,且需要引入的JS使用text/babel类型及babel的JS文件,且通过babel进行渲染
<body>
<div id="app"></div>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel" src="js/app.js"></script>
</body>
ReactDOM.render(
<div>开课吧</div>,
document.getElementById('app')
);
注意:使用JPX格式进行渲染,是babel内部通过ajax去访问的页面,所以页面必须运行在服务器上才能正常访问。否则会报错:
解决:如果实在VSCode编辑其中可以下载插件,live server即可;
2.2ReactDOM.render()方法解析
ReactDOM.render(element, container[, callback]):
- element:要渲染的内容
- container:要渲染的内容存放容器(页面某个节点)
- callback:渲染后的回调函数
2.3XSS注入攻击
为了有效的防止 `XSS` 注入攻击,`React DOM` 会在渲染的时候把内容(字符串)进行转义,所以字符串形式的标签是不会作为 `HTML` 标签进行处理的。
XSS攻击示例:通过表单的innerHTML方法会可以改变HTML上的各种标签,样式,JS等,极其不安全。
<style>
#app {
width: 100px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id="app"></div>
<script>
document.querySelector("#app").innerHTML = '<style>#app {width: 100px;height: 100px;background:green;}</style>';
</script>
</body>
3.编程范式
就是编程的一种模式,比较流行的一些编程范式:
- - 命令式编程
- - 声明式编程
- - 函数式编程
3.1命令式编程
告诉计算机怎么做(How?) - 过程
在原生JavaScript中,UI的构建是使用了命令式的编程方式来完成的。
let shadow = this.attachShadow({mode: 'open'});
let style = document.createElement('style');
style.textContent = `span {color:red}`;
let span = document.createElement('span');
span.innerHTML = '我是自定义元素的内容';
span.classList.add('cred');
shadow.appendChild(style);
shadow.appendChild(span);
3.2声明式编程
告诉计算机我们要什么(What?) - 结果
如sql语句和HTML:
SELECT * FROM `USER` WHERE `gender`='男' ORDER BY `age` DESC;
上面的 SQL就是一个典型的声明式编程,告诉数据库,我要什么结果,至于怎么查询出来结果,排序如何实现的过程 SQL并不关心,由内部(底层)实现。
又如以下方法(此处也包括函数式编程):
['k',1,2,'k',true,'b'].filter(v => Number.isFinite(v)).map(v=>v*10).reduce((c, v)=>c + v, 0);
3.3React.js 中的声明式 UI
<span className="cred">我是自定义元素的内容</span>
4.DOM对象与Virtual DOM
4.1DOM 对象
DOM:文档对象模型,把文档(一堆字符,一堆有格式的字符)中的内容解析成JS中的对象格式,并且还提供许多的方法和特性来操作这些对象,同时对这些对象的操作还会同步反馈(更新)到这些对象对应的HTML中。
浏览器会把页面中的元素映射为 JavaScript 中的对象,在 JavaScript 中通过对这些对象的访问来获取页面中对应元素及其内容。同时,对这些对象进行某些操作,又会反馈到页面中对应的元素上面。
但是,原生 JavaScript DOM 对象内容和结构太复杂,有很多的特性是我们平时很少用到的,而且我们对对象的操作会立即反馈到页面(渲染),影响性能。因此react使用虚拟DOM。
html:
<body>
<div id="app"></div>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel" src="virtualDom.js"></script>
</body>
console.dir(document.querySelector("#app"));
结果:发现原生DOM对象上有其所有的属性和对应的方法。
4.2虚拟 DOM
virtual DOM,参考原生 DOM 对象构建的一个对象,它的结构足够简单,同时优化渲染逻辑,减少变化带来的渲染性能影响。
HTML:
<body>
<div id="app"></div>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel" src="virtualDom.js"></script>
</body>
JSX:
// console.dir(document.querySelector("#app"));
const App = (
<div>
<h1>开课吧</h1>
<p>web前端高级工程师</p>
</div>
);
console.dir(App);
ReactDOM.render(
App,
document.querySelector("#app")
);
生成的 virtual DOM 结构如下:发现虚拟DOM下有props属性;props下面有children,children下包括所有的HTML结构;children又会有自己的props,props下依然有children,一直到最后没有内容。react后面的各种都是通过虚拟DOM进行操作。
5.JSX的使用
`JSX` 是一个基于 `JavaScript` + `XML` 的一个扩展语法。
- - 它可以作为 `值` 使用
- - 它并不是 `字符串`
- - 它也不是 `HTML`
- - 它可以配合 `JavaScript 表达式` 一起使用
需要引入babel库,内部是通过babel库去解析引入的app.js文件,再进行渲染的。使用babel库后,网络请求根本不会去加载自己引入的app.js文件,而是通过babel库中的ajax异步去加载,并且将加载过来的文件先通过babel进行解析成JS代码,再通过浏览器渲染出来。
js/app.js:jsx
ReactDOM.render(
<div>开课吧</div>,
document.getElementById('app')
);
引入JSX解析库:babel-standalone.js:在浏览器中处理 `JSX`
html:
<script src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel" src="js/app.js"></script>
> 注意:如果包含 `JSX` 或引入的代码中包含 `JSX` ,需要设置 `script` 标签的 `type` 属性为:`text/babel`
const App = (
<div>
<h1>开课吧</h1>
<p>web前端高级工程师</p>
</div>
);
6.JSX语法规则
6.1结构
每一个独立 `JSX` 结构的顶层有且只能有一个顶级父元素
jsx:
// 错误
const App = (
<div>
<h1>开课吧</h1>
<p>web前端高级工程师</p>
</div>
<div>第二个</div>
);
// 正确
const App = (
<div>
<div>
<h1>开课吧</h1>
<p>web前端高级工程师</p>
</div>
<div>第二个</div>
</div>
);
6.2在JSX中嵌入表达式
- 在 `JXS` 中可以使用{表达式}嵌入`JavaScript`表达式
表达式:能产生值的一组代码的集合。
- - 变量
- - 算术运算
- - 函数调用
- - ……
if,while,for等是语句,JXS中不支持语句。
示例:
HTML:
<body>
<div id="app"></div>
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<script src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.min.js"></script>
<script type="text/babel" src="jsx.js"></script>
</body>
JS:
let name = "kaikeba";
let title = "welcome to kaikeba";
const App = (
<div>
<h1>{name}</h1>
<p>{title}</p>
</div>
);
ReactDOM.render(
App,
document.querySelector("#app")
);
结果:
- 注意:分清楚表达式与语句的区别,if、for、while这些都是语句,JSX不支持语句。
<h1>{if (true) {...}</h1> // 错误
7.JSX语法示例
在 `JSX` 中,表达式输出的内容类型与使用位置也有一些区别
7.1JSX中的注释
<div>
{/*注释*/}
{/*
多行注释
*/}
</div>
7.2JSX表达式输出数据类型
- - 字符串、数字:原样输出
- - 数组:转成字符串。相当于使用数组.join(''),即使用空字符串而不是JS中默认的逗号连接
- - 其它对象不能直接输出。
- - 布尔值、空、未定义会被忽略
数组:发现数组在JSX表达式中输出时,会以字符串形式对元素进行连接。
let arr = [2,7,3,4];
const App1 = (
<div>
{/*JSX输出数据类型:数组*/}
<p>{arr}</p>
</div>
);
ReactDOM.render(
App1,
document.querySelector("#app")
);
其他对象:直接输出会报错
{/*JSX输出数据类型:其他对象*/}
let obj = {name:'lmf',age:23};
const App2 = (
<div>
<p>{obj}</p>
</div>
);
必须先对象转为数组,再输出:
//JSX对象不能直接输出,需要转为数组后才能输出
let obj = { name: 'lmf', age: 23 };
let zMouse = {
name: '子鼠',
gender: '男',
skills: ['JavaScript', 'Node.js'],
interests: ['音乐', '足球', '编程']
};
function getObj(obj){
let arr = [];
for(let k in obj){
arr.push(<li key={k}>{k}:{
Array.isArray(obj[k]) ? Object.keys(obj[k]).map(item => {
return <ul><li key={item}>{obj[k][item]}</li></ul>
}) : obj[k]
}</li>)//注意此处使用了JSX语法,所以取值必须使用{k}
}
return arr;
}
const AppCont = (
<div>
<p>对象转为数组输出:{Object.keys(obj).map((k, v) => { return k + ': ' + obj[k] + '; ' })}</p>
对象转为数组输出(先通过方法生成列表循环数组,再调用方法(注意也需要在{}中进行调用)进行显示):
{getObj(zMouse)}
对象循环列表输出:
{/*注意:不能使用数组作为唯一key,['JavaScript', 'Node.js']没有可作为key的,[name:'JavaScript', remark:'较难']可将arr.name或arr.remark作为唯一key*/}
<ul>
{Object.keys(zMouse).map((k) => {
return <li key={k}>{k}:{
Array.isArray(zMouse[k]) ? Object.keys(zMouse[k]).map(item => {
return <ul><li key={item}>{zMouse[k][item]}</li></ul>
}) : zMouse[k]
}</li>
})}
</ul>
</div>
);
ReactDOM.render(
AppCont,
document.querySelector("#app")
);
结果:
布尔值、空、未定义会被忽略:
{/*JSX输出数据类型:布尔值,空或未定义*/}
const App3 = (
<div>
true:{true}
<br/>
undefined:{undefined}
<br/>
空:{}
</div>
);
ReactDOM.render(
App3,
document.querySelector("#app")
);
结果:
7.3在属性上使用表达式
JSX中的表达式也可以使用在属性上,但是使用的时候需要注意
- 当在属性中使用{}的时候,不要使用引号包含
jsx:
let id = 'kaikeba';
// 错误
const App = (
<div id="{id}"></div>
);
// 正确
const App = (
<div id={id}></div>
);
- JSX更偏向JavaScript, 所以对于一些特殊的属性,使用的是JavaScript中的属性名风格。如className
jsx:
// 错误
const App = (
<div class="box1"></div>
);
// 正确
const App = (
<div className="box1"></div>
);
- 为了更加方便的操作元素的style,针对style这个属性有特殊的处理方式。style里的多个属性是以对象形式书写
const App = (
<div style={{width: '100px', height: '100px', color:'red'}}></div>
);
注意:这里的两个{{}} ,外部的大括号表示的是前面说的表达式语法中的大括号,里面的大括号是表示对象的大括号
注意:在属性上使用表达式时,可以直接输出对象格式的数据,但是直接在表达式中输出对象会报错
也可以使用以下方式:
let skin = {width: '100px', height: '100px', color:'red'};
const App = (
<div style={skin}></div>
);
8.JSX列表渲染
列表渲染如果需要渲染一组数据,我们可以通过遍历(数组遍历、对象变量……)等操作,返回一组JSX。
**数据**:
let zMouse = {
name: '子鼠',
gender: '男',
skills: ['JavaScript', 'Node.js'],
interests: ['音乐', '足球', '编程']
};
8.1数组
function getSkills() {
return (
<ul>
{zMouse.skills.map(skill => <li>{skill}</li>)}
</ul>
);
}
const App = (
<div>{getSkills()}</div>
);
或者
const App = (
<div>
<ul>
{zMouse.skills.map(skill => <li>{skill}</li>)}
</ul>
</div>
);
8.2对象
需要将对象转为数组
//JSX对象不能直接输出,需要转为数组后才能输出
let obj = { name: 'lmf', age: 23 };
let zMouse = {
name: '子鼠',
gender: '男',
skills: ['JavaScript', 'Node.js'],
interests: ['音乐', '足球', '编程']
};
function getObj(obj){
let arr = [];
for(let k in obj){
arr.push(<li key={k}>{k}:{
Array.isArray(obj[k]) ? Object.keys(obj[k]).map(item => {
return <ul><li key={item}>{obj[k][item]}</li></ul>
}) : obj[k]
}</li>)//注意此处使用了JSX语法,所以取值必须使用{k}
}
return arr;
}
const AppCont = (
<div>
<p>对象转为数组输出:{Object.keys(obj).map((k, v) => { return k + ': ' + obj[k] + '; ' })}</p>
对象转为数组输出(先通过方法生成列表循环数组,再调用方法(注意也需要在{}中进行调用)进行显示):
{getObj(zMouse)}
对象循环列表输出:
{/*注意:不能使用数组作为唯一key,['JavaScript', 'Node.js']没有可作为key的,[name:'JavaScript', remark:'较难']可将arr.name或arr.remark作为唯一key*/}
<ul>
{Object.keys(zMouse).map((k) => {
return <li key={k}>{k}:{
Array.isArray(zMouse[k]) ? Object.keys(zMouse[k]).map(item => {
return <ul><li key={item}>{zMouse[k][item]}</li></ul>
}) : zMouse[k]
}</li>
})}
</ul>
</div>
);
ReactDOM.render(
AppCont,
document.querySelector("#app")
);
或者
const App = (
<div>
<ul>
{Object.keys(zMouse).map(key => <li>{key}</li>)}
</ul>
</div>
);
8.3key
默认情况下,React 从性能上考虑,会尽可能的复用结构,针对同组可变列表结构,为了避免出现某些方面的问题,通常会给每一个列表添加一个唯一的 key。这个key一般使用数据中的唯一标识id。
<ul>
{[{id:1,name:'zMouse',id:2,name:'MT'}].map(user => <li key={user.id}>{user.name}</li>)}
</ul>
注意:key 的值不推荐使用数组的下标,因为数组下标没有和数据进行绑定。
9.JSX条件渲染
function moreInterests() {
if (zMouse.interests.length > 2) {
return <a href="#">更多</a>
}
}
const App = (
<div>
爱好:{zMouse.interests.map(interest=>{
return <span style={{marginRight:"10px"}}>{interest}</span>
})}
{moreInterests()}
</div>
);
9.1三元运算符
const App = (
<div>
爱好:{zMouse.interests.map(interest=>{
return <span style={{marginRight:"10px"}}>{interest}</span>
})}
{zMouse.interests.length > 2 ? <a href="#">更多</a> : null}
</div>
);
9.2与或运算符
const App = (
<div>
爱好:{zMouse.interests.map(interest=>{
return <span style={{marginRight:"10px"}}>{interest}</span>
})}
{zMouse.interests.length > 2 && <a href="#">更多</a>}
{zMouse.interests.length < 4 || <a href="#">更多</a>}
</div>
);