React是什么?
React 介绍:
React 是一个用于构建用户界面的 JAVASCRIPT 库。
React 主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。
React 起源于 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。
React 特点:
-
1.声明式设计 −React采用声明范式,可以轻松描述应用。
-
2.高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
-
React将DOM抽象为虚拟DOM,虚拟DOM其实就是用一个对象来描述DOM,通过对比前后两个对象的差异,最终只把变化的部分重新渲染,提高渲染的效率;
-
当DOM发生更改时需要遍历DOM对象的属性, 而原生DOM可遍历属性多达200多个, 而且大部分属性与渲染无关, 导致更新页面代价太大;
-
用 JS对象结构表示 DOM 树的结构,然后用这个树构建一个真正的 DOM 树,插到文档当中;
-
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异;
-
把记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。
-
-
3.灵活 −React可以与已知的库或框架很好地配合。
-
4.JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
-
5.组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
-
6.单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
React初体验
<body>
<div id="root"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.js"></script>
<script type="text/babel">
//1.获取页面容器元素
const box = document.getElementById('root');
//2.使用JSX语法
const vDom = <h1>hello yaobinggt!</h1>
//3.将虚拟DOM渲染到容器元素上去
ReactDOM.render(vDom, box);//render(要渲染的内容,存放虚拟DOM的容器)
</script>
</body>
然后我们简约一下:
<body>
<div id="root"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.js"></script>
<script type="text/babel">
const vDom = <h1>hello yaobinggt!</h1>
ReactDOM.render(vDom, document.getElementById('root'));//render(要渲染的内容,存放虚拟DOM的容器)
</script>
</body>
JSX语法
什么是JSX
React 使用 JSX 来替代常规的 JavaScript。JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。
优点:
- JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
- 它是类型安全的,在编译过程中就能发现错误。
- 使用 JSX 编写模板更加简单快速。
JSX初体验、原生js对比
我们来往容器中插入一个p标签:<p class="text">hello yaobinggt!</p>
原生js:(死板不灵活,臃肿)
<body>
<div id="root"></div>
<script type="text/javascript">
const root = document.getElementById('root');//获取父级元素
const p = document.createElement('p');//创建子元素
p.innerHTML = "hello yaobinggttttttt!";//往创建元素里面写入内容
root.appendChild(p);//往父级元素里面插入子元素
</script>
</body>
典型js+react:(零活,臃肿)
<body>
<div id="root"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.js"></script>
<script type="text/javascript">
let myClassName = 'text',
myContent = 'hello yaobinggt';
const vDOM = React.createElement('p', { className: myClassName }, myContent);
ReactDOM.render(vDOM, document.getElementById('root'))
</script>
jsx:(完美)
<body>
<div id="root"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.js"></script>
<script type="text/babel">
let myClassName = 'text',
myContent = 'hello yaobinggt';
const textP = <p className={myClassName}>{myContent}</p>;
ReactDOM.render(textP, document.getElementById('root'))
</script>
</body>
总结:
- JSX只是高级语法糖, 最终执行时还是会被转成原生js, 通过babel等方式
- 更加语义化, 更加直观, 代码可读性更高
- 性能相对原生方式更加好一些
JSX常见的界面操作方式
多重标签嵌套、内联样式的引用、js变量的引用:
<body>
<div id="root"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.js"></script>
<script type="text/babel">
//js中的变量,表达式要写在{}内
const cuba = "CUBA青年队"
//内联样式通过对象方式引入类似插槽语法
const myStyle = {
width: '200px',
height: '300px',
backgroundColor: 'orange',
fontSize: '30px'
}
//多重标签嵌套
ReactDOM.render(
<div style={myStyle}>
<h1>{cuba}-集训大名单</h1>
<p>明天我们就要出国征战了</p>
<span style={{ color: 'orange', backgroundColor: 'red' }}>请为我们祝福</span>
</div>
, root);
</script>
</body>
jsx数组遍历
<body>
<div id="root"></div>
<script type="text/babel">
//数组遍历
const dataArr = [
{ name: 'yaoming', numb: 11 },
{ name: 'maidi', numb: 1 },
{ name: 'koben', numb: 24 },
{ name: 'jordan', numb: 23 }
];
//创建虚拟DOM
const vDOM = (
<ul>
{
dataArr.map((data, index) => {
return <li>ID:{index},姓名:{data.name},球衣号:{data.numb}</li>
})
}
</ul>
)
//渲染虚拟DOM
ReactDOM.render(vDOM, root)
</script>
</body>
总结:
- JSX中添加属性时,使用 className 代替 class , 像label中的for属性,使用htmlFor代替;
- JSX创建DOM的时候,所有的节点,必须有唯一的根元素进行包裹 ;
- JSX语法中,标签必须成对出现,如果是单标签,则必须自闭合;
- 在 JSX 中可以直接使用 JS代码,直接在 JSX 中通过 {} 中间写 JS代码即可;
- 在 JSX 中只能使用表达式,不能出现语句;
- 在 JSX 中注释语法:
{/* 中间是注释的内容 */}
组件/模块, 组件化/模块化
基本介绍
组件:
- 一个应用/版块/页面中用于实现某个局部的功能(包括html, js, css等);
- 把这些局部功能组装到一起就形成了完整的一个大的功能;目的:复用代码, 提高项目运行效率。
组件化:如果一个应用是用多组件的方式进行综合开发的, 那么这个应用就是一个组件化应用。
模块:多个组件形成模块, 或者是一个提供特定功能的js文件, 主要特点在于耦合性低, 可移植性高, 执行效率好。
模块化:如果一个应用都是用模块的形式来构建的,那么这个应用就是模块化应用。
组件的概念:虚拟DOM对象的集合,将一组虚拟DOM对象, 封装在一起(函数、类),就构成了一个组件;组件内部, 可以处理数据+业务逻辑;
组件的注意:组件名称首字母必须大写,只有大写才会被识别成组件;虚拟DOM 必须有且只有一个根元素;
构造函数创建组件
<body>
<div id="root"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.js"></script>
<script type="text/babel">
function Yaobing(props) {
return (
<div>
<h2>My name is {props.name}</h2>
<p>I come from {props.address},I'm {props.age} years old! I have {props.fruit}!</p>
</div>
)
}
ReactDOM.render(<Yaobing name="yaobing" address="China" age="28" fruit={["Apple", "Orange", "Banner"]} />, root)
</script>
</body>
构造函数多组件
<div id="root"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.js"></script>
<script type="text/babel">
function Header(props) {
return (
<header style={{ color: "#ff6600" }}>我是文章头部标题内容</header>
)
}
function Content(props) {
return (
<section style={{ color: "#242746" }}>我是文章内容部分</section>
)
}
function Footer(props) {
return (
<footer style={{ color: "#a000db" }}>我是文章底部内容</footer>
)
}
function Article() {
return (
<div>
<Header />
<Content />
<Footer />
</div>
)
}
ReactDOM.render(<Article />, root)
</script>
</body>
向组件传值-props
组件内部, 通过props属性值来获取,通过组件类的 defaultProps 属性为 props 设置默认值
<body>
<div id="root"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/prop-types.js"></script>
<script src="./js/babel.js"></script>
<script type="text/babel">
//设置组件
function Person(props) {
return (
<div>
<a href={props.link}>{props.name}</a>
</div>
)
};
//设置默认属性
//约定(PropTypes)属性类型,必须(isRequired)
Person.propTypes = {
name: PropTypes.string.isRequired,
link: PropTypes.string
};
//设置属性默认值
Person.defaultProps = {
name: "yaouu",
link: "www.yaouu.uu"
}
ReactDOM.render(<Person />, root)
</script>
</body>
<body>
<div id="root"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.js"></script>
<script type="text/babel">
class Person extends React.Component {//继承(extends)React.Component类(它里面有定义组件一系列的实现形式)
render() {
let style = {
backgroundColor: 'red',
width: '400px',
height: '400px'
}
return (
<div style={style}>
<h2>姓名:{this.props.name}</h2>
<h2>年龄:{this.props.age}</h2>
<h2>爱好:</h2>
<ul>
{this.props.like.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
)
}
}
ReactDOM.render(<Person name="yaobing001" age="28" like={["打篮球", "打乒乓球", "踢足球"]} />, root)
</script>
</body>
PropTypes不同验证器的例子:
import PropTypes from 'prop-types';
MyComponent.propTypes = {
// 你可以将属性声明为 JS 原生类型,默认情况下
// 这些属性都是可选的。
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
// 任何可被渲染的元素(包括数字、字符串、元素或数组)
// (或 Fragment) 也包含这些类型。
optionalNode: PropTypes.node,
// 一个 React 元素。
optionalElement: PropTypes.element,
// 一个 React 元素类型(即,MyComponent)。
optionalElementType: PropTypes.elementType,
// 你也可以声明 prop 为类的实例,这里使用
// JS 的 instanceof 操作符。
optionalMessage: PropTypes.instanceOf(Message),
// 你可以让你的 prop 只能是特定的值,指定它为
// 枚举类型。
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
// 一个对象可以是几种类型中的任意一个类型
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 可以指定一个数组由某一类型的元素组成
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 可以指定一个对象由某一类型的值组成
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
// 可以指定一个对象由特定的类型值组成
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// An object with warnings on extra properties
optionalObjectWithStrictShape: PropTypes.exact({
name: PropTypes.string,
quantity: PropTypes.number
}),
// 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保
// 这个 prop 没有被提供时,会打印警告信息。
requiredFunc: PropTypes.func.isRequired,
// 任意类型的数据
requiredAny: PropTypes.any.isRequired,
// 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。
// 请不要使用 `console.warn` 或抛出异常,因为这在 `onOfType` 中不会起作用。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},
// 你也可以提供一个自定义的 `arrayOf` 或 `objectOf` 验证器。
// 它应该在验证失败时返回一个 Error 对象。
// 验证器将验证数组或对象中的每个值。验证器的前两个参数
// 第一个是数组或对象本身
// 第二个是他们当前的键。
customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {
if (!/matchme/.test(propValue[key])) {
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
})
};
组件内的状态机-state
React 把组件看成是一个状态机(State Machines), 通过状态 (State) 去操作状态机。
在开发中, 通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
在React 中,只需更新组件的 state,然后根据新的 state 重新渲染用户界(不要操作 DOM)。
根据state状态来渲染界面,后期, 只要我们修改state, 那么界面就会自动重新渲染,再次执行render。
注意:
- 不能直接修改state, 需要通过setState方法,此方法内部给出的对象, 会增量更新到原state,并不会替换原state。
- 状态数据封装在组件内部, 不要在外界访问
- 多次state数据"同时修改", 会被合并后, 更新一次
<body>
<div id="root"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/prop-types.js"></script>
<script src="./js/babel.js"></script>
<script type="text/babel">
class Person extends React.Component {
constructor(props) {//constructor 是一种用于创建和初始化class创建的对象的特殊方法。
super(props);
//状态机
this.state = {
name: "乔丹",
age: 53,
sex: "男",
score: 63
}
}
render() {
return (
<div>
<h2>NBA明星简介</h2>
<p>姓名:{this.state.name},年龄:{this.state.age},性别:{this.state.sex},最高得分:{this.state.score}</p>
<p>-----------------------------------</p>
<button onClick={() => this._dealBtnClick()}>换一个人</button>
</div>
)
}
_dealBtnClick() {
//更新状态机
this.setState({
name: "姚明",
age: 42,
sex: "男",
score: 43
})
}
}
ReactDOM.render(<Person />, root)
</script>
</body>
state和props区别
- props是指组件间传递的一种方式;
- 组件内部的this.props属性是只读的不可修改。
- state是组件内部的状态(数据);
- 不能够直接修改,必须要通过setState来改变值的状态。
<body>
<div id="root"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/prop-types.js"></script>
<script src="./js/babel.js"></script>
<script type="text/babel">
class Person extends React.Component {
static propTypes = {
personObj: PropTypes.object.isRequired
}
constructor(props) {//constructor 是一种用于创建和初始化class创建的对象的特殊方法。
super(props);
//状态机
const { p_name, p_age, p_sex, p_score } = this.props.personObj;
this.state = {
name: p_name,
age: p_age,
sex: p_sex,
score: p_score
}
}
render() {
const { name, age, sex, score } = this.state;
const { p_name, p_age, p_sex, p_score } = this.props.personObj;
return (
<div>
<h2>NBA明星简介</h2>
<p>姓名:{name},年龄:{age},性别:{sex},最高得分:{score}</p>
<p>-----------------------------------</p>
<p>姓名:{p_name},年龄:{p_age},性别:{p_sex},最高得分:{p_score}</p>
<p>-----------------------------------</p>
<button onClick={() => this._dealBtnClick()}>换一个人</button>
</div>
)
}
_dealBtnClick() {
//更新状态机
this.setState({
name: "姚明",
age: 42,
sex: "男",
score: 43
})
this.setProps({
name: "姚明",
age: 42,
sex: "男",
score: 43
})
}
}
let p = {
p_name: 'koben',
p_age: 36,
p_sex: '男',
p_score: 81
}
ReactDOM.render(<Person personObj={p} />, root)
</script>
</body>
组件的事件处理
- react组件的事件处理 + React 事件绑定属性的命名采用驼峰式写法,而不是小写; + 如果使用 JSX 的语法你需要传入一个函数作为事件处理函数;例如:
<button onClick={func}>点击</button>
- react组件的事件阻止 + 不能使用返回 false 的方式阻止默认行为;明确的使用 preventDefault。
- 事件方法中的this + 箭头函数中的this是在定义函数时绑定,且内部无this. 参照上下文确定this + 普通函数是在执行函数时绑定, 内部有this, 谁执行就是谁
- 事件方法中传参 + 可以使用箭头函数, 二次包装
<body>
<div id="root"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/prop-types.js"></script>
<script src="./js/babel.js"></script>
<script type="text/babel">
class Counter extends React.Component {
static defaultProps = {
step: 1
}
static propsTypes = {
step: PropTypes.number
}
constructor(props) {//
super(props); //super调用父类里面的构造函数
//状态
this.state = {
res: 0
}
}
render() {
const { step } = this.props;
return (
<div>
<h3>结果:{this.state.res}</h3>
<a href="www.baidu.com" onClick={(e) => this._addNum(step, e)}>加{step}</a>
<p></p>
</div>
)
}
_addNum(num, e) {
//console.log(e);
//阻止默认事件
e.preventDefault();
this.setState({
res: this.state.res + num
})
}
}
let vDOM = (
<div>
<Counter step={1} />
<Counter step={2} />
<Counter step={3} />
<Counter step={4} />
<Counter step={5} />
</div>
)
ReactDOM.render(vDOM, root)
</script>
</body>
表单事件
onChange
<script type="text/babel">
class AssPanel extends React.Component {
constructor(props) {
super(props);
this.state = {
content: "默认内容"
}
}
_dealChange(e) {
console.log(e.target.value);
//更新状态机
this.setState({
content: e.target.value
});
}
_getDate() {
alert(this.state.content);
}
render() {
return (
<div>
<label>
<input type="text" value={this.state.content} onChange={(e) => this._dealChange(e)} />
<button onClick={() => this._getDate()}>获取输入框中的值</button>
</label>
</div>
)
}
}
ReactDOM.render(<AssPanel />, root);
</script>
ref
<script type="text/babel">
class AssPanel extends React.Component {
constructor(props) {
super(props);
//绑定ref
//this.myRef = React.createRef();
this.myRef2 = React.createRef();
}
_dealFocus() {
//console.log(this.myRef.current);
//console.log(this.myRef);
//this.myRef.current.focus();
console.log(this.myRef2.current.defaultValue)
}
render() {
return (
<div>
<label>
<input ref={this.myRef} type="text" placeholder="请输入内容" />
<input ref={this.myRef2} type="button" value="获取焦点" onClick={() => this._dealFocus()} />
</label>
</div>
)
}
}
ReactDOM.render(<AssPanel />, root);
</script>