react项目
一、React项目
1、项目依赖安装
脚手架:
解压两个文件:在项目的根目录下执行npm install////npm I 会按照package.json的配置安装依赖模块。
安装完成后,会出现一个node_modules,里面是安装的所有依赖的模块。
都是项目的根目录。
npm install
代理是哪里进去哪里出来。
2、项目整体说明
.babelrc: babel转译的配置文件。 (放的是预设值)环境信息,插件等。
.gitignore(忽略的信息)
Index.html(网页的HTML显示信息,发布的站点根目录)
Jsconfig.json(vscode自己所用的)
LICENSE()
.npmrc() 镜像库
Package.js(依赖信息)
README.md()
Src(js的插件信息)
App.js
Appstate.js
Index.html
Node_moudles(npm包管理器设置以后所有的依赖文件)
Webpack.config.dev.js 开发时候使用的
Webpack.config.prod.js 生产环境使用的
3、配置文件详解
1)package.json
Npm 产生的文件,里面记录项目信息,所有项目依赖。
(1)版本信息:repository:
里面包括的是版本等信息。
(2)项目管理:scripts:
Start:启动webpack的dev server开发用的web server,提供的静态文件加载、自动刷新和热替换HMR(hot module replacement)
HMR可以在应用程序运行中替换,添加和删除模块,不需要重新加载模块,只是变化部分替换掉,不用HMR则自动刷新页面。
--HOT 启动HMR
--inline默认模式,使用HMR的时候建议使用inline模式,热替换时候会有消息显示在控制台。
Build使用webpack构建打包,对应npm run build
(3)项目依赖devdependencies
开发时候的依赖,不会打包到目标文件中。例如转译代码等。没必要到生产中去。
Dependencies运行的时候的依赖必须带到项目中去,
版本号:
指定安装版本号:
~安装指定中和最新的版本。
^安装指定中最高的版本,不低于指定的版本。*******建议常用
Latest:最新版本。
Babel转译的。
Css样式:
Css-loader样式表相关的模块。
React-hot-loader热加载插件。
运行时候的依赖:
Antd ant design基于react实现,后台管理较方便。
Axios 异步请求
Polyfiill解决浏览器api不支持的问题,磨平差异化。
React开发的主框架。
React-router路由
React-router-dom dom绑定路由
Mobx状态管理库,透明化
Mobx-react mobx-react-devtools mobx和react结合的模块。
React和mobx是一个强强联合。
2)babel配置,.babelrc
Babel转译的配置文件
3)webpack配置
(1)webpack.config.dev.js
符合commonjs的模块。
Devtools:’source-map’
调试时候映射处理。
Entry入口:从入口直接找到需要打包的文件。
Output输出:
Resolve解析
Module模块
Test匹配条件的。
Exclude排除的,
Use使用模块的useentries列表中的loader,
Rules中.js结尾的但不在node_modules目录的文件使用转译babel-loader和热加载loader。
加载器:
style-loader通过<style>标签吧css添加到dom中。
Css-loader加载css
Less-loader对less的支持
Less:控制h2标记的。
Devserver,开发使用的server
Compress启动gzip
Port启动端口3000
Hot启动HMR
Proxy执行访问/api开头路径都代理到http://127.0.0.0:8080中
4)vscode的配置
Jsconfig.json是vscode的配置文件,覆盖当前配置。
上面的配置文件,需要更改name,version,description,需要修改repository仓库地址,需要修改author、license信息。修改完成后,进行开发。
App-xxxxx的文档必须放在根目录,对于Windows下的是磁盘的根目录。
4、启动项目
npm start
二、react
1、简介
React是Facebook开发的并开源的前端框架。
2013年开源的,react解决的是前端的MVC框架中的view视图层的问题。
2、virtual dom***
DOM是什么?
DOM(文档对象模型document object model)
是HTML和xml文档,把其解析成为dom对象,展示成为树的模型。
Js在浏览器端多数情况下都是操作dom树的。
选择节点问题,通过id等进行查找等。
The document
The dom tree
将网页所有内容映射到一颗树形的结构的层级对象模型上,浏览器提供对dom支持,用户可以用脚本调用dom api来动态的修改dom节点,从而达到修改网页的目的,这种修改在浏览器中完成,浏览器胡根据dom的修改重绘dom的节点部分。
修改dom重新渲染的代价比较高,前端框架为了提高效率,尽量减少DOM的重绘,提出了virtual DOM,所有的修改都是现在的virtual dom上面完成,通过比较算法,找出浏览器和dom之间的差异,使用这个差异操作dom,浏览器只是需要渲染这个部分变化就行了。
React实现了dom diff算法可以高效比对vir dom和 dom的差异。
3、jsx的语法。
Jsx是一种Javascript和xml混写的语法,是Javascript的扩展。
直接插入HTML语句等。
4、测试程序
import React from 'react';
import ReactDOM from 'react-dom';
// import { render } from 'react-dom';
// import { AppContainer } from 'react-hot-loader';
// import AppState from './AppState';
// import App from './App';
// const appState = new AppState();
// render(
// <AppContainer>
// <App appState={appState} />
// </AppContainer>,
// document.getElementById('root')
// );
// if (module.hot) {
// module.hot.accept('./App', () => { render(App) })
// }
class Root extends React.Component{
render(){
return <div>javascript excise</div>;
}
}
ReactDOM.render(<Root/>,document.getElementById('root'));
Return的必须是jsx的语法。单标计必须是封口的。
Render()渲染函数,必须有return值,必须有顶级元素返回,所有元素必须闭合。
单行省略小括号,多好使用小括号。
class Root extends React.Component{
render(){
return <div>javascript <br/> excise</div>;
}
}
<br/>换行符
class Root extends React.Component{
render(){
return React.createElement('div',null,'javascript');
//return <div>javascript <br/> excise</div>;
}
}
//ReactDOM.render(<Root/>,document.getElementById('root'));
ReactDom.render(React.createElement(Root),document.getElementById('root'));
更改属性的值:大括号等信息。
增加一个子元素:
class SubEle extends React.Component{
render(){
return <div>Sub content</div>
}
}
class Root extends React.Component{
render(){
return (
<div>
<h2>welcome to study</h2>
<br/>
<SubEle/>
</div>);
}
}
ReactDOM.render(<Root/>,document.getElementById('root'))
React组件的render函数return,只能是一个顶级元素。
Jsx语法是XML,要求所有元素必须闭合,<br/> 不能是<br>
5、jsx规范
标签中首字母小写就是HTML标记,首字母大写就是组件。
要求严格的标记,要求所有标签必须闭合,br也应该写成<br />,/前面留一个空格。
单行省略小括号,多行必须使用小括号。
元素有嵌套的,多行,缩进。
Jsx表达式:使用{}括起来,如果大括号内使用了引号,会当做字符串处理。
6、组件状态state
每一个react组件都有一个状态变量state,是一个JavaScript对象,为期定义属性保存值。
状态变化了,会出发UI重新渲染,使用setstate方法重新修改state的值。
State是组件内部使用的,是私有属性。
import React from 'react';
import ReactDOM from 'react-dom';
class Root extends React.Component{
state = {
p1: 'abc',
p2:'.com'
};
render(){
this.state.p1 = 'www.abc'
return (
<div>
<div>welcome to {this.state.p1}{this.state.p2}</div>
<br />
</div>
);
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
//this.state.p1 = 'www.abc'
改为下面的,不允许,不允许对更新中的state进行setState
//this.setState({p1:'www.ab'}) //不允许这样修改,控制台会报错,
使用延时函数:setTimeout(里面是一个函数)
setTimeout(()=>this.setState({p1:'www.abdd'}),5000)
class Root extends React.Component{
state = {
p1: 'abc',
p2:'.com'
};
render(){
//this.state.p1 = 'www.abc'
//this.setState({p1:'www.ab'}) //不允许这样修改,控制台会报错,
setTimeout(()=>this.setState({p1:'www.abdd'}),5000)
return (
<div>
<div>welcome to {this.state.p1}{this.state.p2}</div>
<br />
</div>
);s
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
7、复杂例子
Div的id是t1,鼠标按下事件就捆绑了一个函数,只要鼠标按下就会触发调用getEventTrigger函数,浏览器就会给其送一个参数event,event是时间对象,当时间触发的时候,event包含触发这个时间的对象。
8、HTML DOM的Javascript事件
属性 |
此事件发生在何时 |
Onabort |
图像的加载被中断 |
Onblur |
元素失去焦点 |
Onchange |
域的内容被改变 |
Onclick |
当用户点击某个对象时候调用的时间句柄 |
Ondbclick |
当用户双击某个对象时候调用的事件句柄 |
Onerror |
在加载文档或图像时候发生错误 |
Onfocus |
元素获得焦点 |
Onkeydown |
某个键盘键被按下 |
Onkeypress |
某个键盘按键被按下并松开 |
Onkeyup |
某个按键被松开 |
Onload |
一张页面或一幅图像完成加载 |
Onmousedown |
鼠标按钮被按下 |
Onmousemove |
鼠标按键被移动 |
Onmouseout |
鼠标从某元素移开 |
Onmouseover |
鼠标移到某个元素之上 |
Onmouseup |
鼠标按键被松开 |
Onreset |
重置按钮被点击 |
Onresize |
窗口或框架被重新调整大小 |
Onselect |
文本被选中 |
Onsubmit |
确认按钮被点击 |
Onunload |
用户退出页面 |
import React from 'react';
import ReactDOM from 'react-dom';
class Toggle extends React.Component{
state = {flag:true};
handleClick(event){
console.log(event.target.id)
console.log(event.target === this);
console.log(this)
console.log(this.state)
this.setState({flag:!this.state.flag})
}
render(){
return <div id="t1" onClick={this.handleClick.bind(this)}>
点击这句话,会触发一个事件1。{this.state.flag.toString()}
</div>;
}
}
class Root extends React.Component{
state = {
p1: 'abc',
p2:'.com'
};
render(){
//this.state.p1 = 'www.abc'
//this.setState({p1:'www.ab'}) //不允许这样修改,控制台会报错,
setTimeout(()=>this.setState({p1:'www.abdd'}),5000)
return (
<div>
<div>welcome to {this.state.p1}{this.state.p2}</div>
<br />
<Toggle />
</div>
);
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
Toggle类
有自己的state属性,当render完成后,网页上有一个div标签捆绑了一个click事件的函数,div标签内有文本内容。
如果用过点击左键,就触发了click方法关联的handleClick函数,在这个函数里将状态值改变。
状态值state的改变将引发render重绘。
{this.handleClick.bind(this)},不能外加引号。
This.handleClick.bind(this)一定要绑定this,否则当触发捆绑的函数时候,this是函数执行的上下文决定的,this已经不是触发事件的对象了。
Console.log(event.target.id)取回的产生事件的对象的id,不是封装的组件的对象,所以,console.log(event.target===this)是false,这个this是通过绑定过来的。
React的事件:
使用小驼峰命名。
使用jsx表达式,表达式中要指定事件处理函数。
不能使用return false,如果要阻止事件默认行为,使用event.preventDefault()
Id是唯一的,坚决不能有重复的。
9、props
把react组件当做标签使用,可以为其增加属性:
组件之间利用state或者传parent传递参数
修改属性是不可以的,因为是只读的this.props.name.
import React from 'react';
import ReactDOM from 'react-dom';
class Toggle extends React.Component{
state = {flag:true};
handleClick(event){
console.log(event.target.id)
console.log(event.target === this);
console.log(this)
console.log(this.state)
this.setState({flag:!this.state.flag})
}
render(){
return <div id="t1" onClick={this.handleClick.bind(this)}>
点击这句话,会触发一个事件1。{this.state.flag.toString()}
{this.props.school}{this.props.age}
<br />
{this.props.parent.state.p1}
{this.props.children}
</div>;
}
}
class Toggle1 extends React.Component{
state = {flag:true};
handleClick(event){
console.log(event.target.id)
console.log(event.target === this);
console.log(this)
console.log(this.state)
this.setState({flag:!this.state.flag})
}
render(){
return <div id="t2" onClick={this.handleClick.bind(this)}>
点击这句话,会触发一个事件2。{this.state.flag.toString()}
<br />
{this.props.name}
<hr />
{this.props.age}
{this.props.parent.state.p2}
</div>;
}
}
class Root extends React.Component{
state = {
p1: 'abc',
p2:'.com'
};
render(){
//this.state.p1 = 'www.abc'
//this.setState({p1:'www.ab'}) //不允许这样修改,控制台会报错,
setTimeout(()=>this.setState({p1:'www.abdd'}),5000)
return (
<div>
<div>welcome to {this.state.p1}{this.state.p2}</div>
<br />
<Toggle school='abc' age= '20' parent={this} >
</Toggle>
<Toggle1 name='tom' age= '10' parent={this}/>
</div>
);
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
注意访问parent送进去的是this,访问实例本身的属性的方式是利用this.props.state.p1的值
10、构造器
利用construc(props)。
Super()调用父类的。
只要extends的,必须利用super()调用。Props作为参数传入。
import React from 'react';
import ReactDOM from 'react-dom';
class Toggle extends React.Component{
constructor(props){
console.log('Toggle')
super(props)
this.state = {flag:true};
}
handleClick(event){
console.log(event.target.id)
console.log(event.target === this);
console.log(this)
console.log(this.state)
this.setState({flag:!this.state.flag})
}
render(){
return <div id="t1" onClick={this.handleClick.bind(this)}>
点击这句话,会触发一个事件1。{this.state.flag.toString()}
{this.props.school}{this.props.age}
<br />
{this.props.parent.state.p1}
{this.props.children}
</div>;
}
}
class Toggle1 extends React.Component{
constructor(props){
console.log('Toggle1')
super(props)
this.state = {flag:true};
}
handleClick(event){
console.log(event.target.id)
console.log(event.target === this);
console.log(this)
console.log(this.state)
this.setState({flag:!this.state.flag})
}
render(){
return <div id="t2" onClick={this.handleClick.bind(this)}>
点击这句话,会触发一个事件2。{this.state.flag.toString()}
<br />
{this.props.name}
<hr />
{this.props.age}
{this.props.parent.state.p2}
</div>;
}
}
class Root extends React.Component{
state = {
p1: 'abc',
p2:'.com'
};
render(){
//this.state.p1 = 'www.abc'
//this.setState({p1:'www.ab'}) //不允许这样修改,控制台会报错,
setTimeout(()=>this.setState({p1:'www.abdd'}),5000)
return (
<div>
<div>welcome to {this.state.p1}{this.state.p2}</div>
<br />
<Toggle school='abc' age= '20' parent={this} >
</Toggle>
<Toggle1 name='tom' age= '10' parent={this}/>
</div>
);
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
11、组件的生命周期
组件的声明周期可分为三个状态。
Mounting:已插入真实dom。
Updating:正在被重新渲染。
Unmounting:已移除真实dom。
方法如下:
(1)装载组件触发:
Componentwillmount在渲染前调用,在客户端也在服务器端,指挥在装载前调用一次。
Componentdidmount:在第一次渲染后调用,只要是在客户端,之后的组件已经生成对应的dom结构,可以通过this.getdomnode()来进行访问, 想与其他的Javascript框架一起使用,在这个方法中调用setTimeout,setinterval或者发送ajax请求等操作,只是在装载完后调用一次,在render之后。
(2)更新组件触发,这些方法不会再首次render组件的周期调用。
Componentwillreceiveprops(nextprops)在组件接受到一个新的prop时候被调用,在初始化render时候不会调用。
Shouldcomponentupdae(nextprops,nextstate)返回布尔值,在组件接受到新的Props或者state时候被调用,在初始化或者使用forceupdate时候不被调用。(如果设置为false,就不允许更新组件。)
Componentwillupdate(nextprops,nextstate)在组件接受到新的props或者state还没render时候被调用,在初始化不会调用。
Componentdidupdate(prevprops,prevstate)在组件完成后更新后立即调用,在初始化时候不会被调用。
(3)卸载组件触发
ComponentwillUNmount在组件从dom中移除的时候立即被调用
图中,constructor构造器是最早执行的函数,触发更新生命周期的函数,需要更新state或者props。
import React from 'react';
import ReactDOM from 'react-dom';
class Sub extends React.Component{
constructor(props){
console.log('sub con')
super(props)
this.state = {count:0}
}
handleClick(event){
this.setState({count:this.state.count + 1});
}
render(){
console.log('sub render')
return <div id='sub'onClick={this.handleClick.bind(this)}>
Sub's count = {this.state.count}
</ div>
}
componentWillMount(){
//第一次render之前
console.log('sub componentwillmount')
}
componentDidMount(){
//第一次render之后
console.log('sub componentDidMount')
}
componentWillUnmount(){
//清理工作
console.log('sub componentWillUnmount ')
}
}
class Root extends React.Component{
constructor(props){
console.log('root con')
super(props)
this.state = {}
}
render(){
return <div>
<Sub />
</ div>
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
顺序是constructor =》 componentwillmount =》 render =》 componentdidmount –state或props改变,render。
import React from 'react';
import ReactDOM from 'react-dom';
import { ENGINE_METHOD_DIGESTS } from 'constants';
class Sub extends React.Component{
constructor(props){
console.log('sub con')
super(props)
this.state = {count:0}
}
handleClick(event){
this.setState({count:this.count + 1});
}
render(){
console.log('sub render')
return <div id='sub'onClick={this.handleClick.bind(this)}>
Sub's count = {this.state.count}
<a style={{height:200+'px',color:'red',backgroundColor:'#f0f0f0'}} >
</ a>
</ div>
}
componentWillMount(){
//第一次render之前
console.log('sub componentwillmount')
}
componentDidMount(){
//第一次render之后
console.log('sub componentDidMount')
}
componentWillUnmount(){
//清理工作
console.log('sub componentWillUnmount ')
}
componentWillReceiveProps(nextProps){
//props变更时候,接受新的props,交给shouldComponentUpdate
//props组件内只读,只能从外部改变
console.log(this.props)
console.log(nextProps)
console.log('sub componentWillReceiveProps',this.state.count);
}
shouldComponentUpdate(nextProps,nextState){
//是否组件更新,props或state方式改变时候,返回布尔值,true才会更新
console.log('sub shouldComponentUpdate',this.state.count,nextState)
return true
}
componentWillUpdate(nextProps,nextState){
//同意更新后,真正更新之前,调用render
console.log('sub componentWillUpdate',this.state.count,nextState)
}
componentDidUpdate(prevProps,prevState){
//同意更新后,真正更新后,在render之后调用。
console.log('sub componentDidUpdate',this.state.count,prevState)
}
}
class Root extends React.Component{
constructor(props){
console.log('root con')
super(props)
this.state = {}
}
render(){
return <div>
<Sub />
</ div>
}
}
ReactDOM.render(<Root />,document.getElementById('root'))
Componentwillmount第一次装载,在首次render之前,例如控制state、props。
Componentdidmount第一次装载结束,在首次render之后,控制state,props。
Componentwillreceiveprops在组件内部,props是只读不可变的,这个函数接受到新的props,可以对props做一些处理,this.props={name:‘root’}这就是偷梁换柱,componentwillreceiveprops触发,也会走shouldcomponentupdate。
Shouldcomponentupdate判断是否需要组件更新,是否render,精确的控制渲染,提高性能。
Componentwillupdate除了首次render之外,每次render前执行,componentdidupdate在render之后调用。
componentWillMount(){
//第一次render之前
console.log('sub componentwillmount')
}
componentDidMount(){
//第一次render之后
console.log('sub componentDidMount')
}
componentWillUnmount(){
//清理工作
console.log('sub componentWillUnmount ')
}
componentWillReceiveProps(nextProps){
//props变更时候,接受新的props,交给shouldComponentUpdate
//props组件内只读,只能从外部改变
console.log(this.props)
console.log(nextProps)
console.log('sub componentWillReceiveProps',this.state.count);
}
shouldComponentUpdate(nextProps,nextState){
//是否组件更新,props或state方式改变时候,返回布尔值,true才会更新
console.log('sub shouldComponentUpdate',this.state.count,nextState)
return true
}
componentWillUpdate(nextProps,nextState){
//同意更新后,真正更新之前,调用render
console.log('sub componentWillUpdate',this.state.count,nextState)
}
componentDidUpdate(prevProps,prevState){
//同意更新后,真正更新后,在render之后调用。
console.log('sub componentDidUpdate',this.state.count,prevState)
}
}
12、无状态组件
也叫做函数式组件。
许多时候组件简单不需要state状态,也不需要声明周期函数,无条件组件很好的满足了需要,无状态组件提供了一个参数props,返回一个react元素,无状态组件函数本身就是render函数。
import React from 'react';
import ReactDOM from 'react-dom';
//let Root = props =><div>{props.shoolname}</div>;
//ReactDOM.render(<Root shoolname='abc' />,document.getElementById('root'))
function Root(props){
return <div> {props.name}</div>
}
ReactDOM.render(<Root name='bcd'/>,document.getElementById('root'))
三、高阶组件
1、柯里化
import React from 'react';
import ReactDOM from 'react-dom';
let wrapper = function (Component){
function _wrapper(props){
return (
<div>
{props.name}
<hr />
<Component />
</ div>
)
}
return _wrapper
}
let Root = props =><div>Root</ div>
let NeeComp = wrapper(Root)
ReactDOM.render(<NeeComp name='abc' />,document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
let wrapper = function (Component){
return props=>(
<div>
{props.name}
<hr />
<Component />
</ div>
)
}
let Root = props =><div>Root</ div>
let NeeComp = wrapper(Root)
ReactDOM.render(<NeeComp name='abc' />,document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
let wrapper = Component=> props =>(
<div>
{props.name}
<hr />
<Component />
</ div>
)
let Root = props =><div>Root</ div>
let NeeComp = wrapper(Root)
ReactDOM.render(<NeeComp name='abc' />,document.getElementById('root'))
2、装饰器
import React from 'react';
import ReactDOM from 'react-dom';
let wrapper = Component=>props=>
(<div>
{props.aname}
<hr />
<Component />
</ div>
)
//let Root = props => <div>Root</ div>
@wrapper
class Root extends React.Component{
render(){
return <div> Root</ div>
}
}
let Newarpper = wrapper(Root)
ReactDOM.render(<Newarpper aname='abc' />,document.getElementById('root'))
import React from 'react';
import ReactDOM from 'react-dom';
let wrapper = Components=>props=>
(<div>
{props.aname}
<hr />
<Components {...props} />
</ div>
)
//let Root = props => <div>Root</ div>
@wrapper
class Root extends React.Component{
render(){
return <div> Root{this.props.aname}</ div>
}
}
//let Newarpper = wrapper(Root)
ReactDOM.render(<Root aname='abc' />,document.getElementById('root'))
Root中也显示props.name的属性,使用 <Components {...props} />
增加属性等。
import React from 'react';
import ReactDOM from 'react-dom';
let wrapper = id => Components=>props=>
(<div id = {id}>
{props.aname}
<hr />
<Components {...props} />
</ div>
)
//let Root = props => <div>Root</ div>
@wrapper('wrapper')
class Root extends React.Component{
render(){
return <div> Root{this.props.aname}</ div>
}
}
//let Newarpper = wrapper(Root)
ReactDOM.render(<Root aname='abc' />,document.getElementById('root'))
函数后面不能加括号调用,因为加括号返回的不是组件,而是render函数需要的内容。
要求返回的是组件,组件函数。