这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天
了解
package.json
npm项目的标志 如maven项目的pom.xml
主要信息是项目的依赖声明
创建项目
创建项目指令:create-react-app your-app(your-app是项目名,可以自己取)
项目启动:npm run start
虚拟DOM
表面上看到的对DOM的访问,或者增加DOM,不是真正的访问DOM,也不是真正的增加DOM。
一个大家都认可的规律:尽量减少在线DOM的个数,以及对在线DOM的访问次数。
JSX
Javascript+XML 这种风格看上去只有入口处有一个HTML文件,其他的都是JS
React介绍
React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram (照片分享) 的网站。做出来以后,发现这套东西很好用,就在 2013 年 5 月开源了。
React 是一个用于构建用户界面的 JavaScript 库。React 主要用于构建 UI,很多人认为 React 是 MVC 中的 V(视图)。React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。
由于 React 的设计思想极其独特,属于革命性创新,性能出众,代码逻辑却非常简单。所以,越来越多的人开始关注和使用,认为它可能是将来 Web 开发的主流工具。 这个项目本身也越滚越大,从最早的 UI 引擎变成了一整套前后端通吃的 Web App 解决方案。衍生的 React Native 项目,目标更是宏伟,希望用写 Web App 的方式去写 Native App。如果能够实现,整个互联网行业都会被颠覆,因为同一组人只需要写一次 UI ,就能同时运行在服务器、浏览器和手机。
React 的特点:
- 声明式设计:React 采用声明范式,可以轻松描述应用。
- 高效:React 通过对 DOM 的模拟,最大限度地减少与 DOM 的交互。
- 灵活:React 可以与已知的库或框架很好地配合。
- JSX:JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
- 组件:通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
- 单向响应的数据流:React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。
JSX基础语法
JSX 的基本语法规则:遇到 HTML 标签(以 <开头),就用 HTML 规则解析;遇到代码块(以 { 开头),就用 JavaScript 规则解析。JSX 允许直接在模板插入 JavaScript 变量。如果这个变量是一个数组,则会展开这个数组的所有成员。
React 中注释为:{/*注释内容 */}。
<!-- react 核心 JS 文件 -->
<script src="https://labfile.oss.aliyuncs.com/courses/2843/react.development.js "></script>
<!-- react 虚拟 DOM 文件 -->
<script src="https://labfile.oss.aliyuncs.com/courses/2843/react-dom.development.js "></script>
<!-- ES6 转 ES5 的插件 -->
<script src="https://labfile.oss.aliyuncs.com/courses/2843/babel.min.js "></script>
组件
使用函数自定义组件
<div id="app"></div>
<script type="text/babel" >
/*========创建组件========*/
function HelloMessage(props) {
return <h1>我的苹果</h1>;}
const element = <HelloMessage />;
ReactDOM.render(
element,/*使用组件*/
document.getElementById('app')
);
</script>
使用ES6语法创建组件
<div id="app"></div>
<script type="text/babel">
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
ReactDOM.render(
<Welcome name="蓝桥" /> /*使用组件*/,
document.getElementById("app")
);
</script>
给组件的属性设置默认值
<div id="app"></div>
<script type="text/babel">
class MyApple extends React.Component {
// 定义一个组件 MyApple
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
MyApple.defaultProps = {
name: '此处为默认值'
};
ReactDOM.render(
/*========不给属性赋值========*/
<MyApple />,
document.getElementById('app')
);
</script>
父组件向子组件传值
props和state
props验证
//使用 props 验证,需要使用 prop-types 库.
<script src="https://labfile.oss.aliyuncs.com/courses/2843/prop-types.js"></script>
<div id="abc"></div>
<script type="text/babel">
class MyTitle extends React.Component {
render() {
return (
<h1>Hello, {this.props.title}</h1>
);
}
}
MyTitle.propTypes = {
title: PropTypes.string.isRequired,
};
ReactDOM.render(
<MyTitle title='蓝桥' />,
document.getElementById('abc')
);
</script>
各种验证类型
MyComponent.propTypes = {
// 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
// 可以被渲染的对象 numbers, strings, elements 或 array
optionalNode: React.PropTypes.node,
// React 元素
optionalElement: React.PropTypes.element,
// 用 JS 的 instanceof 操作符声明 prop 为类的实例。
optionalMessage: React.PropTypes.instanceOf(Message),
// 用 enum 来限制 prop 只接受指定的值。
optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
// 可以是多个对象类型中的一个
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),
// 指定类型组成的数组
optionalArrayOf:React.PropTypes.arrayOf(React.PropTypes.number),
// 指定类型的属性构成的对象
optionalObjectOf:React.PropTypes.objectOf(React.PropTypes.number),
// 特定 shape 参数的对象
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),
// 任意类型加上 `isRequired` 来使 prop 不可空。
requiredFunc: React.PropTypes.func.isRequired,
// 不可空的任意类型
requiredAny: React.PropTypes.any.isRequired,
// 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error('Validation failed!');
}
}
}
}
state
何时使用state
组件中用到的一个变量是不是应该作为组件 state,可以通过下面的 4 条依据进行判断:
- 这个变量是否是通过 props 从父组件中获取?如果是,那么它不是一个状态。
- 这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。
- 这个变量是否可以通过其他状态(state)或者属性(props) 计算得到?如果是,那么它不是一个状态。
- 这个变量是否在组件的 render 方法中使用?如果不是,那么它不是一个状态。 这种情况下,这个变量更适合定义为组件的一个普通属性,例如组件中用到的定时器,就应该直接定义为 this.timer,而不是 this.state.timer。 请务必牢记,并不是组件中用到的所有变量都是组件的状态!当存在多个组件共同依赖一个状态时,一般的做法是状态上移,将这个状态放到这几个组件的公共父组件中。
何时使用 setState:
- 不能在组件内部通过 this.state 修改状态,因为该状态会在调用 setState() 后被替换。
- setState() 并不会立即改变 this.state ,而是创建一个即将处理的 state。
- setState() 并不一定是同步的,为了提升性能 React 会批量执行 state 和 DOM 渲染。
- setState() 总是会触发一次组件重绘,除非在 shouldComponentUpdate() 中实现了一些条件渲染逻辑。
强制更新:forceUpdate
forceUpdate([function callback])
forceUpdate() 方法会使组件调用自身的 render() 方法重新渲染组件,组件的子组件也会调用自己的 render()。但是,组件重新渲染时,依然会读取 this.props 和 this.state,如果状态没有改变,那么 React 只会更新 DOM。
forceUpdate() 方法适用于 this.props 和 this.state 之外的组件重绘(如:修改了 this.state 后),通过该方法通知 React 需要调用 render()。
一般来说,应该尽量避免使用 forceUpdate(),而仅从 this.props 和 this.state 中读取状态并由 React 触发 render() 调用。
<div id="app"></div>
<script type="text/babel" >
var num = 0; // 统计 render 函数执行次数
class Counter extends React.Component{
constructor(props) {
super(props);
this.name = '小李';
this.state = {clickCount: 0};
this.handleClick = this.handleClick.bind(this);
}
/*省略了部分代码*/
/*========第一个参数可以是一个方法返回一个对象也可以直接写一个对象========*/
handleClick() {
this.forceUpdate(function(){
/*========强制更新 render 函数========*/
console.log('更新完成')
})
}
render() {
num++;
console.log('执行了render'+num);
return (
<h2 onClick={this.handleClick}>点我!
{this.name}点击次数为:
{this.state.clickCount}</h2>);
}
}
ReactDOM.render(
<Counter name='没有人'/>,
document.getElementById('app')
);
</script>
获取 DOM 节点:findDOMNode
ReactDOM.findDOMNode(DOM)
//DOM:可以是一个 html、DOM 或者一个组件。
//返回值:DOM 元素 DOMElement。
<div id="app"></div>
<script type="text/babel" >
/*========创建组件========*/
class MyApple extends React.Component {
render(){
return <h1>我要好好学习</h1>;
}
};
class Counter extends React.Component{
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
/*========通过 this.refs.demo 可以找到 ref=demo 的 DOM========*/
var d = ReactDOM.findDOMNode(this.refs.demo)
/*========输出组件里面的 H1========*/
console.log(d)
/*========输出整个虚拟 DOM========*/
console.log(this.refs.demo)
}
render() {
return (
<div onClick={this.handleClick}>
<h2 >蓝桥学院很棒!</h2>
{/*========通过 ref 可以找到对应的 DOM========*/}
<MyApple ref="demo"/>
</div>);
}
}
ReactDOM.render(
<Counter name='没有人'/>,
document.getElementById('app')
);
</script>
React 事件
//点击弹出对话框
<div id="app"></div>
<script type="text/babel" >
/*========创建组件========*/
var MyApple = React.createClass({
go:function(){
alert('我是React事件')
},
render: function() {
/*========事件属性要用小驼峰命名法(第二个单词开始首字母大写)========*/
return <h1 onClick={this.go}>我的苹果</h1>;
}
});
ReactDOM.render(
<MyApple />,
document.getElementById('app')
);
</script>
React 生命周期
组件的生命周期可分成三个状态:
- Mounting:已插入真实 DOM。
- Updating:正在被重新渲染。
- Unmounting:已移出真实 DOM。
componentWillMount:在渲染前调用,在客户端也在服务端。在 17.0 版本将会被删除,使用 UNSAFE_componentWillMount 替换。建议使用 constructor 替代。
componentDidMount:在第一次渲染后调用,只在客户端。之后组件已经生成了对应的 DOM 结构,可以通过 this.getDOMNode() 来进行访问。如果你想和其他 JS 框架一起使用,可以在这个方法中调用 setTimeout,setInterval 或者发送 AJAX 请求等操作(防止异部操作阻塞 UI)。
componentWillReceiveProps:在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化 render 时不会被调用。17.0 版本该 API 会被废弃,使用 UNSAFE_componentWillReceiveProps 替换。
shouldComponentUpdate:返回一个布尔值。在组件接收到新的 props 或者 state 时被调用。在初始化时或者使用 forceUpdate 时不被调用。 可以在你确认不需要更新组件时使用。
componentWillUpdate:在组件接收到新的 props 或者 state 但还没有 render 时被调用。在初始化时不会被调用。 该 API 会被废弃在 17.0,使用 UNSAFE_componentWillUpdate 替换。可以使用 componentDidUpdate 替代。
componentDidUpdate:在组件完成更新后立即调用。在初始化时不会被调用。
componentWillUnmount:在组件从 DOM 中移除的时候立刻被调用。
getDerivedStateFromProps:16.3 新增 API,在组件实例化后或接受新的 props 后被调用。它返回一个对象来更新状态,或者返回 null 表示新的 props 不需要任何 state 的更新。如果是由于父组件的 props 更改,所带来的重新渲染,也会触发此方法。
getSnapshotBeforeUpdate:16.3 新增 API,在 render 后的输出被渲染到 DOM 之前被调用。它使组件能够在被潜在更改之前捕获当前值(如滚动位置)。这个生命周期返回的任何值都将作为参数传递给 componentDidUpdate()。