在观看react教程学习理论时,记录的一些笔记。教程链接:jspang.com/posts/2019/…
经过一定的实践后再去看官方文档,才能真正关注到其他遗漏内容
一.基础
1.介绍
React和Vue的对比
这是前端最火的两个框架,虽然说React是世界使用人数最多的框架,但是就在国内而言Vue的使用者很有可能超过React。两个框架都是非常优秀的,所以他们在技术和先进性上不相上下。
那个人而言在接到一个项目时,我是如何选择的那?React.js相对于Vue.js它的灵活性和协作性更好一点,所以我在处理复杂项目或公司核心项目时,React都是我的第一选择。而Vue.js有着丰富的API,实现起来更简单快速,所以当团队不大,沟通紧密时,我会选择Vue,因为它更快速更易用。
2.安装
1.脚手架的安装
npm install -g create-react-app
2.创建第一个React项目
D: //进入D盘
mkdir ReactDemo //创建ReactDemo文件夹
create-react-app demo01 //用脚手架创建React项目
cd demo01 //等创建完成后,进入项目目录
npm start //预览项目,如果能正常打开,说明项目创建成功
3.入口&组件
1.入口文件的编写
在src目录下,新建一个文件index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App />,document.getElementById('root')) //挂载到哪里
2.app组件的编写
现在写一下App组件,这里我们采用最简单的写法,就输出一个Hello JSPang,就可以了。
import React, {Component} from 'react'
class App extends Component{
render(){
return (
<div>
Hello JSPang
</div>
)
}
}
export default App;
4.JSX注意点
1.组件包裹原则
只可以有一个根节点,要是没有包裹会报错
import React,{Component} from 'react'
class Xiaojiejie extends Component{
render(){
return (
<div><input /> <button> 增加服务 </button></div>
<ul>
<li>头部按摩</li>
<li>精油推背</li>
</ul>
)
}
}
export default Xiaojiejie
2.Fragment标签讲解
加上最外层的DIV,组件就是完全正常的,但是你的布局就偏不需要这个最外层的标签怎么办?比如我们在作Flex布局的时候,外层还真的不能有包裹元素。这种矛盾其实React16已经有所考虑了,为我们准备了<Fragment>标签。
要想使用<Fragment>,需要先进行引入。
import React,{Component,Fragment } from 'react'
3.JSX代码注释
jsx注释不能使用 //
<Fragment>
{/* 正确注释的写法 */}
{
//正确注释的写法
}
<div>
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
如果你记不住,有个简单的方法,就是用VSCode的快捷键,直接按Ctrl+/,就会自动生成正确的注释了。
4.JSX中的class陷阱
添加class 时候需要用className=“”而不是class=“”,它是防止和js中的class类名 冲突,所以要求换掉。这也算是一个小坑吧。
<input className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
5.JSX中的html解析问题
如果想在文本框里输入一个<h1>标签,并进行渲染。默认是不会生效的,只会把<h1>标签打印到页面上,这并不是我想要的。如果工作中有这种需求,可以使用dangerouslySetInnerHTML属性解决。
<ul>
{
this.state.list.map((item,index)=>{
return (
<li
key={index+item}
onClick={this.deleteItem.bind(this,index)}
dangerouslySetInnerHTML={{__html:item}}
>
</li>
)
})
}
</ul>
6.JSX中<label>标签的坑
不能使用for.它容易和javascript里的for循环混淆,会提示你使用htmlfor。
<div>
<label htmlFor="jspang">加入服务:</label>
<input id="jspang" className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
5.响应式设计
1.数据的绑定
React不建议你直接操作DOM元素,而是要通过数据进行驱动,改变界面中的效果。React会根据数据的变化,自动的帮助你完成界面的改变。所以在写React代码时,你无需关注DOM相关的操作,只需要关注数据的操作就可以了(这也是React如此受欢迎的主要原因,大大加快了我们的开发速度)。
<input value={this.state.inputValue} />
2.绑定事件
这时候你到界面的文本框中去输入值,是没有任何变化的,这是因为我们强制绑定了inputValue的值。如果要想改变,需要绑定响应事件,改变inputValue的值。比如绑定一个改变事件,这个事件执行inputChange()(当然这个方法还没有)方法。
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
需要注意,方法中的this需要用bind指定到当前类
3.修改状态
react中修改状态需要用setState方法而不是直接赋值
// this.state.inputValue=e.target.value;
this.setState({
inputValue:e.target.value
})
4.setState方法
更新虚拟dom的状态,这是一个异步方法
参数:
参数1是要修改的属性的对象集合
参数2是回调函数
4.让列表数据化
现在的列表还是写死的两个<li>标签,那要变成动态显示的,就需要把这个列表先进行数据化,然后再用javascript代码,循环在页面上。
render(){
return (
<Fragment>
<div>
<input value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button> 增加服务 </button>
</div>
<ul>
{
this.state.list.map((item,index)=>{
return <li key={index+item}>{item}</li>
})
}
</ul>
</Fragment>
)
}
注意:循环列表需要带key属性,否则报警告
5.删除数据需要注意
获得了数据下标后,删除数据就变的容易起来.先声明一个局部变量,然后利用传递过来的下标,删除数组中的值.删除后用setState更新数据就可以了.
//删除单项服务
deleteItem(index){
let list = this.state.list
list.splice(index,1)
this.setState({
list:list
})
}
其实这里边是有一个坑的,有的小伙伴肯定会认为下面的代码也是正确的.
//删除单项服务
deleteItem(index){
this.state.list.splice(index,1)
this.setState({
list:this.state.list
})
}
记住React是禁止直接操作state的,虽然上面的方法也管用,但是在后期的性能优化上会有很多麻烦,所以一定不要这样操作.这也算是我React初期踩的比较深的一个坑,希望小伙伴们可以跳坑
二.组件
1.例子
XiaojiejieItem.js
import React, { Component } from 'react'; //imrc
class XiaojiejieItem extends Component { //cc
render() {
return (
<div>小姐姐</div>
);
}
}
export default XiaojiejieItem;
app.js
import React,{Component,Fragment } from 'react'
import './style.css'
import XiaojiejieItem from './XiaojiejieItem'
class Xiaojiejie extends Component{
//js的构造函数,由于其他任何函数执行
constructor(props){
super(props) //调用父类的构造函数,固定写法
this.state={
inputValue:'' , // input中的值
list:['基础按摩','精油推背'] //服务列表
}
}
render(){
return (
<Fragment>
{/* 正确注释的写法 */}
<div>
<label htmlFor="jspang">加入服务:</label>
<input id="jspang" className="input" value={this.state.inputValue} onChange={this.inputChange.bind(this)} />
<button onClick={this.addList.bind(this)}> 增加服务 </button>
</div>
<ul>
{
this.state.list.map((item,index)=>{
return (
//----------------关键修改代码----start
<div>
<XiaojiejieItem />
</div>
//----------------关键修改代码----end
)
})
}
</ul>
</Fragment>
)
}
inputChange(e){
// console.log(e.target.value);
// this.state.inputValue=e.target.value;
this.setState({
inputValue:e.target.value
})
}
//增加服务的按钮响应方法
addList(){
this.setState({
list:[...this.state.list,this.state.inputValue],
inputValue:''
})
}
//删除单项服务
deleteItem(index){
let list = this.state.list
list.splice(index,1)
this.setState({
list:list
})
}
}
export default Xiaojiejie
2.父子组件的传值
1.父组件向子组件传值
在组件里写入属性进行传值
<XiaojiejieItem content={item} />
用this.props.content接受
import React, { Component } from 'react'; //imrc
class XiaojiejieItem extends Component { //cc
render() {
return (
<div>{this.props.content}</div>
);
}
}
export default XiaojiejieItem;
父组件向子组件传递内容,靠属性的形式传递。
2.子组件向父组件传递数据
React有明确规定,子组件时不能操作父组件里的数据的,所以需要借助一个父组件的方法,来修改父组件的内容。
将父组件的方法作为属性传给子组件
<ul>
{
this.state.list.map((item,index)=>{
return (
<XiaojiejieItem
key={index+item}
content={item}
index={index}
//关键代码-------------------start
deleteItem={this.deleteItem.bind(this)}
//关键代码-------------------end
/>
)
})
}
</ul>
注意要将this绑定到父组件自身
传递后,在子组件里直接调用就可以了,代码如下:
handleClick(){
this.props.deleteItem(this.props.index)
}
3.constructor里面使用bind
另外,的bind写法:
在constructor里面绑定
import React, { Component } from 'react'; //imrc
class XiaojiejieItem extends Component { //cc
//--------------主要代码--------start
constructor(props){
super(props)
this.handleClick=this.handleClick.bind(this)
}
//--------------主要代码--------end
render() {
return (
<div onClick={this.handleClick}>
{this.props.content}
</div>
);
}
handleClick(){
console.log(this.props.index)
}
}
export default XiaojiejieItem;
3.单向数据流
例如:
<ul>
{
this.state.list.map((item,index)=>{
return (
<XiaojiejieItem
key={index+item}
content={item}
index={index}
list={this.state.list}
deleteItem={this.deleteItem.bind(this)}
/>
)
})
}
</ul>
handleClick(){
//关键代码——---------start
this.props.list=[]
//关键代码-----------end
this.props.deleteItem(this.props.index)
}
会报错
TypeError: Cannot assign to read only property 'list' of object '#<Object>'
4.函数式编程
在面试React时,经常会问道的一个问题是:函数式编程的好处是什么?
- 函数式编程让我们的代码更清晰,每个功能都是一个函数。
- 函数式编程为我们的代码测试代理了极大的方便,更容易实现前端自动化测试。
React框架也是函数式编程,所以说优势在大型多人开发的项目中会更加明显,让配合和交流都得心应手。
总结:这节课虽然都是些理论知识,这些知识在面试中经常被问到,所以也是必须掌握的内容。
5.PropTypes校验传递值
在父组件向子组件传递数据时,使用了属性的方式,也就是props,但对传值的类型没有任何的限制。这在工作中是完全不允许的,因为大型项目,如果你不校验,后期会变的异常混乱,业务逻辑也没办法保证。
1.PropTypes的简单应用
import PropTypes from 'prop-types'
import React, { Component } from 'react'; //imrc
import PropTypes from 'prop-types'
class XiaojiejieItem extends Component { //cc
constructor(props){
super(props)
this.handleClick=this.handleClick.bind(this)
}
render() {
return (
<div onClick={this.handleClick}>
{this.props.content}
</div>
);
}
handleClick(){
this.props.deleteItem(this.props.index)
}
}
//--------------主要代码--------start
XiaojiejieItem.propTypes={
content:PropTypes.string,
deleteItem:PropTypes.func,
index:PropTypes.number
}
//--------------主要代码--------end
export default XiaojiejieItem;
限制必填
avname:PropTypes.string.isRequired
2.使用默认值defaultProps
XiaojiejieItem.propTypes={
content:PropTypes.string,
deleteItem:PropTypes.func,
index:PropTypes.number
}
XiaojiejieItem.defaultProps = {
avname:'松岛枫'
}
6.ref的使用方法
以前的案例中,我们写了下面的代码,使用了e.target,这并不直观,也不好看。这种情况我们可以使用ref来进行解决。
inputChange(e){
this.setState({
inputValue:e.target.value
})
}
如果要使用ref来进行,需要现在JSX中进行绑定, 绑定时最好使用ES6语法中的箭头函数,这样可以简洁明了的绑定DOM元素。
<input
id="jspang"
className="input"
value={this.state.inputValue}
onChange={this.inputChange.bind(this)}
//关键代码——----------start
ref={(input)=>{this.input=input}}
//关键代码------------end
/>
绑定后可以把上边的类改写成如下代码:
inputChange(){
this.setState({
inputValue:this.input.value
})
}
7.生命周期
1.initialization:
在类里的constructor方法里进行
2.mounting:
componentWillMount()挂载之前
render()
componentDidMount()挂载之后
3.update:
componentWillReceiveProps
组件第一次存在于dom中,函数是不会被执行
如果已经存在Dom中,函数才会被执行
shouldComponentUpdate,返回true|false决定是否往后执行
shouldComponentUpdate(){
return [true|false]
}
componentWillUpdate更新之前
render()
componentDidUpdate更新之后
4.unmounting:
componentWillUnmount移除之前,
5.拓展:性能优化
父组件改变会触发子组件的更新,这会导致性能问题,这时可以使用下面的方法适当阻止更新:
shouldComponentUpdate(nextProps,nextState){
return nextProps.content!==this.props.content
}
三.动画
1.css过渡
<div className={this.state.isShow ? 'show' : 'hide'}>BOSS级人物-孙悟空</div>
.show{ opacity: 1; transition:all 1.5s ease-in;}
.hide{opacity: 0; transition:all 1.5s ease-in;}
2.css3动画
1.keyframes动画介绍
此属性与animation属性是密切相关的,keyframes译成中文就是关键帧,我最早接触这个关键帧的概念是字flash中,现在Flash已经退出历史舞台了。他和transition比的优势是它可以更加细化的定义动画效果。比如我们设置上节课的按钮隐藏动画,不仅可以设置透明度,还可以设置颜色。
@keyframes hide-item{
0% {
opacity:1;
color:yellow;
}
50%{
opacity: 0.5 ;
color:red;
}
100%{
opacity:0;
color: green;
}
}
2.使用动画
使用动画的关键词是animation,然后后边跟上你的制作的动画名称,如下面这段代码。
.hide{ animation:hide-item 2s ease-in ; }
但是你会发现,动画执行一遍后又恢复了原状,这个是因为没设置forwards属性,它是用来控制停止到最后一帧的。 我们把代码改写成下面的样子。
.hide{ animation:hide-item 2s ease-in forwards; }
3.react-transition-group
react-transition-group是react官方提供的动画过渡库,有着完善的API文档
1.安装
npm install react-transition-group --save
安装好后,你可以先去github上来看一下文档,他是有着三个核心库(或者叫组件)。
- Transition
- CSSTransition
- TransitionGroup
2.CSSTransition的使用
<CSSTransition>,而且不再需要管理className了,这部分由CSSTransition进行管理。修改上节课写的Boss.js文件里的render区域。
render() {
return (
<div>
<CSSTransition
in={this.state.isShow} //用于判断是否出现的状态
timeout={2000} //动画持续时间
classNames="boss-text" //className值,防止重复
>
<div>BOSS级人物-孙悟空</div>
</CSSTransition>
<div><button onClick={this.toToggole}>召唤Boss</button></div>
</div>
);
}
需要注意的是classNames这个属性是由s的,如果你忘记写,会和原来的ClassName混淆出错,这个一定要注意。
我们把上节课的代码进行了改造,然后你就可以到CSS中改写style了。在修改样式之前,有那些类名。
- xxx-enter: 进入(入场)前的CSS样式;
- xxx-enter-active:进入动画直到完成时之前的CSS样式;
- xxx-enter-done:进入完成时的CSS样式;
- xxx-exit:退出(出场)前的CSS样式;
- xxx-exit-active:退出动画知道完成时之前的的CSS样式。
- xxx-exit-done:退出完成时的CSS样式。
知道了这些要设置的CSS,就可以删除原来写的CSS了,把下面的代码写上:
.input {border:3px solid #ae7000}
.boss-text-enter{
opacity: 0;
}
.boss-text-enter-active{
opacity: 1;
transition: opacity 2000ms;
}
.boss-text-enter-done{
opacity: 1;
}
.boss-text-exit{
opacity: 1;
}
.boss-text-exit-active{
opacity: 0;
transition: opacity 2000ms;
}
.boss-text-exit-done{
opacity: 0;
}
1.unmountOnExit 属性
学到这里,会感觉这样写也没有简化多少,更没特殊的效果,技术胖你又玩我。
其实不是的,比如我们给<CSSTransition>加上unmountOnExit,加上这个的意思是在元素退场时,自动把DOM也删除,这是以前用CSS动画没办法做到的。
比如我们把代码写成这个样子:
render() {
return (
<div>
<CSSTransition
in={this.state.isShow} //用于判断是否出现的状态
timeout={2000} //动画持续时间
classNames="boss-text" //className值,防止重复
unmountOnExit
>
<div>BOSS级人物-孙悟空</div>
</CSSTransition>
<div><button onClick={this.toToggole}>召唤Boss</button></div>
</div>
);
}
工具
1.React进阶-Simple React Snippets
1.介绍
在工作中你经常会看到程序老司机写代码是非常快的,甚至让你烟花缭乱,那他们真的是单身那么多年,练就了超级快手吗?当然不是,只是他们使用了快速生成插件,这节课我就向大家介绍一个vscode中的Simple React Snippets,有了这个插件,稍加练习,你也可以像老司机一样,拥有加藤鹰的圣手(如果不懂请自行搜索吧)。
2.命令
直接在vscode中输入imrc,就会快速生成最常用的import代码。
import React, { Component } from 'react';
快捷键cc.插件就会快速帮我们生成如下代码:
class extends Component {
state = { }
render() {
return ( );
}
}
export default ;
2.React高级-调试工具的安装及使用
其实React在浏览器端是有一个调试工具的,这就是React developer tools,这个是React人必下的一个调试工具。这节课就主要学习一下React developer tools的下载和简单使用。
React developer tools的三种状态
React developer tools有三种颜色,三种颜色代表三种状态:
- 灰色: 这种就是不可以使用,说明页面不是又React编写的。
- 黑色: 说明页面是用React编写的,并且处于生成环境当中。
- 红色: 说明页面是用React编写的,并且处于调试环境当中。
React developer tools使用
打开浏览器,然后按F12,打开开发者工具,然后在面板的最后一个,你会返现一个React,这个就是安装的插件了。
3.axios使用
推荐初始化数据在componentDidMount里进行
4.easyMock
远程mock网站 easy-mock.com