React是Facebook公司研发的一款JS框架(MVC:Model View Controller) 经过数据的改变来影响视图javascript
React
脚手架React是一款框架:具有本身开发的独立思想 - 划分组件开发css
前端工程化开发:html
webpack来完成以上内容(自动化):前端
- 基于路由的spa单页面开发
脚手架 配置webpack相对复杂,咱们须要安装不少的包,还须要写不少相对复杂的配置,这时候脚手架应运而生,用来提高咱们开发的效率vue
vue:vue-cli react:create-react-app(应用)java
一、npm install create-react-app -g
// 安装到全局能用命令 (npm root -g
查看全局npm安装目录) 二、create-react-app demo1
// 项目名称demo1,名称只能(/^[a-z0-9_-]$/)node
脚手架生成目录中的一些内容:react
一、node_module 当前项目中依赖的包 .bin 本地项目中可执行的命令,在package.json的scripts中配置的对应的脚本便可(还有一个是react-scripts命令) 二、public 存放当前项目的页面(单页面应用放index.html便可,多页面根据本身需求放置须要的页面)webpack
在react中,全部的逻辑都是在JS中完成的(包括页面机构的建立),若是想给当前的页面导入一些css样式后者img图片等内容,咱们有两种方式:ios
(1)、在JS中基于ES6 Module模块规范,使用import引入,这样webpack在编译合并JS的时候,会把导入的资源文件等插入到页面的机构中(绝对不能在js管控的结构中经过相对目录./ 或者../ 导入资源,由于在webpack编译的时候,地址就不在是以前的相对地址要用绝对地址)
(2)、若是不想在js中导入(JS中导入的资源最后都是基于webpack编译),咱们也能够把资源手动的在html中代入,可是html最后也要基于webpack编译,导入的地址也不建议写相对地址,而是使用%PUBLIC_URL%
写成绝对地址
<link rel="manifest" href="%PUBLIC_URL%/reset.min.css">
复制代码
三、src 项目结构中最主要的目录,由于后期全部的JS、路由、组件等都是放到这里面(包括须要编写的css或者图片等) index.js 是入口文件 四、.gitignore git的忽略文件 五、package.json 当前项目的配置清单
"dependencies": {
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-scripts": "1.1.4"
},
复制代码
基于脚手架生成工程目录,自动帮咱们安装了三个模块:
package.json:
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
复制代码
可执行的脚本
npm run start
或者 yarn start
yarn start
:
- 建立一个默认端口号为3000 ,协议为http的web服务
- 按照webpack.config.dev.js把项目编译
- 打开浏览器预览咱们的项目
- 项目修改时候,自动从新编译,浏览器页面自动刷新,展现最新的效果
yarn build:
- 基于webpack.config.prod.js 把项目进行编译打包
- 生成一个build文件夹,存放最后打包的文件>- 生成一个build文件夹,存放最后打包的文件
- 部署上线的时候,只须要把build的内容发布便可
create-react-app 脚手架为了让结构目录清晰,把安装的webpack及配置文件都集成在了react-scripts模块中,放到了node_modules中 可是真实项目中,咱们须要在脚手架默认安装的基础上,额外安装一些咱们须要的模块,例如react-router-dom
、axios
、还有less
、less-loader
状况一:若是咱们安装其余的组件,可是安装成功后不须要修改webpack的配置项,此时咱们直接安装,调取使用就行,好比react-router-dom 、axios等
状况二:咱们安装的插件是基于webpack处理的,也就是须要把安装的模块配置到webpack中(从新修改webpack配置项了) 一、首先须要把隐藏到node_module中的配置项暴露到项目中
yarn eject
首先会肯定是否确认执行eject,这个操做不可逆转,一旦暴露出来配置项,就没法再隐藏了 若是还有未提交到历史区的内容,须要先提交,而后才能eject 二、再去修改对应的配置项 暴露后,项目目录中多了两个文件夹: config:存放的是webpack的配置文件 (1)webpack.config.dev.js
开发环境下的配置项(yarn start) (2)webpack.config.prod.js
生产环境下的配置项(yarn build) scripts:存放的是可执行脚本的JS文件 (1)start.js:yarn start
执行的就是这个JS (2)build.js:yarn build
执行的就是这.个JS
咱们预览项目的时候,也是基于webpack编译,把编译后的内容放到浏览器中运行,因此若是项目中使用了less,咱们须要修改webpack配置项,在配置项中加入less的编译工做,这样后期预览项目,首先基于webpack把less编译为css
set HTTPS=true && npm start
开启HTTPS协议模式 set PORT=1111 && npm start
修改默认端口号
一种渐进式框架设计思想,通常框架中都包含了不少内容,这样致使框架的体积过于臃肿 拖慢加载的速度, 真实项目中,咱们使用一个框架,不必定用到全部的功能,此时咱们应该把框架的功能进行拆分,用户想用什么,让其本身自由组合便可--"渐进式框架"
全家桶:渐进式框架N多部分的组合 vue全家桶:vue、vue-cli、vue-router、vuex、axios、vue-element 、vant、 react全家桶:react、react-react-app、react-dom、react-router、redux、react-redux、axios、ant、dva、saga、mobx
react的核心部分,提供了Component类能够进行组件开发,提供了钩子函数(生命周期),全部的生命周期函数都是基于回调函数完成的
raect独有的,把JSX语法,渲染为真实DOM(可以放到页面中展现的机构都叫作真实的DOM)的组件
react框架都是在JS中进行的,而后经过webpack编译,再放到浏览器中编译
在index.js这个入口文件:
import React from 'react';
import ReactDOM from 'react-dom';
// import ReactDOM,{render} from 'react-dom';
// 从react-dom中导入一个reactDOM,逗号后面的内容是把ReactDOM这个对象进行结构
let data ='zufeng',
root = document.querySelector("#root");
ReactDOM.render(<div id="box">hello world!{data}</div>,root,()=>{
// 回调,通常不用
let oBox = document.querySelector("#box");
console.log(oBox.innerHTML);
});
复制代码
JSX语法的渲染使用的是ReactDOM.render: ReactDOM.render([jsx],[container],[callback]);
jsx:react独有的语法, JavaScript+xml(HTML),和咱们以前拼接的字符串相似,都是把html结构代码和js代码或者数据混合在一块儿了,可是他不是字符串 container:容器,咱们想把元素放到页面中的哪一个容器中 callback:当把容器放到页面中呈现触发的回调函数
jsx语法特色:
一、不建议咱们 直接把jsx直接放到body中,而是放在本身建立一个容器中,通常咱们都放在一个id为root的div中
ReactDOM.render(<section><h2>内容</h2></section>,root)
复制代码
二、把数据嵌入到jsx中 在JSX中出现的{}
是存放JS的,要求JS代码执行完成须要有返回结果(JS表达式),不能直接放一个对象数据类型的
let name = 'zhufeng' // 基本数据类型的值
ReactDOM.render(<section><h2{name}</h2></section>,root)
let xx = {name:'xxxx'}
ReactDOM.render(<section><h2{xx}</h2></section>,root) // 报错
复制代码
ReactDOM.render(<ul>
{
data.map((item,index)=>{
return <li key={index}>{item.title}</li>
})
}
</ul>,root)
复制代码
三、循环数组穿件jsx元素,须要给建立的元素设置惟一的key值(当前本次惟一便可) 四、只能出现一个根元素 五、给元素设置样式类用的是className而不是class 六、style中不能直接写样式字符串,须要基于一个样式对象来遍历赋值 七、能够给JSX元素设置属性: => 属性值对应大括号中,对象、函数均可以放(也能够放JS表达式) =>style属性值必须是对象(不能是字符串)
<li style={{color:'#fff'}}></li>
=>class 用className代替
ReactDOM.render(<h1 id={'box'} className="box" style={{color:'red'}}>我是标题</h1>);
复制代码
JSX虚拟DOM变为真实的DOM(react的核心原理之一)
let styleObj = {color:'red'};
ReactDOM.render(<h1 id="titleBox" className='title' style={styleObj.color}></h1>)
复制代码
JSX渲染机制:JSX->真实DOM
一、基于babel babel-loader babel-present-react-app把JSX语法编译为
React.createElement([type],[props],[children])
结构 =>React.createElement中至少有两参数, type:第一参数的标签(字符串) props:属性对象(没有就是null) 其余的:都是子元素内容(只要子元素是html,就会变成新的createElement)
var styleObj = { color: 'red' };
ReactDOM.render(React.createElement('h1', { id: 'titleBox', className: 'title', style: styleObj.color }));
复制代码
二、执行
React.createElement(....)
,建立一个对象(虚拟DOM)
=>key 和ref 都是createElement中的Prop提取出来的 =>props:{ chiledren:存放本身子元素的(没有子元素就没有这个属性),若是有多个子元素,就以数组的形式存储信息 } 三、
ReactDOM.render(JSX语法最后生成的对象,container,callback)
,基于render方法把生成的对象动态建立为DOM元素,插入到指定的容器中
无论vue仍是react框架,设计之初都是指望咱们按照“组件 / 模块管理” 的方式来构建程序的,也就是一个程序划分一个个的组件来单独管理
src -> component
:这个文件夹下存放的就是开发用的组件 【优点】 一、有助于多人协助开发 二、组件能够复用 ....
react 中建立组件有两种方式:
一、函数式声明组件
(1)函数返回结果是一个新的JSX(也就是当前组件的JSX结构) (2)props变量存储的值是一个对象,包含了调取组件时候传递的属性值,不传递的话是个空对象
知识点:
createElement遇到一个组件,返回的对象中:type就不是字符串标签名了,而是一个函数(类),可是属性仍是存在props中
render渲染的时候,咱们须要作处理:
首先判断type的类型, 若是是字符串,就建立一个元素标签, 若是函数或者类,就把函数执行,把props中的每一项(包含children)传递给函数 在执行的时候,把函数中return的JSX转换为新的对象(经过createElement),把这个对象返回:紧接着
render
按照以往的渲染方式,建立DOM元素,插入到指定的容器中便可
单闭合和双闭合组件的区别,双闭合组件中能够放子孙元素
函数式声明特色:
一、会把基于createElement解析出来的对象中的props做为参数传递给组件(能够完成屡次调取组件传递不一样的信息) 二、一旦组件调取成功,返回的
jsx
就会渲染到页面中,可是后期不经过从新调取组件或者获取DOM元素操做的方式,很难再把渲染好组件中的内容更改 -->"静态组件"
//Dialog.js
import React from 'react'; // 每一个组件必须引入,由于须要基于它的create-element把JSX进行解析渲染
export default function Dialog(props) {
//props:调取组件的时候传递进来的属性信息(可能包含className、style、id、可能有children)
let {con, lx = 0, children,style={}} = props,
title = lx === 0 ? '系统提示' : '系统警告';
//children 可能有,可能没有,多是个值,也多是个数组,每一项多是字符串也多是个对象等,都表明双闭合组件的子孙元素
return <section style={style}>
<h2>{title}</h2>
<div>{con}</div>
{/*一、把属性中的子元素,放到组件中的指定位置*/}
{/*{children}*/}
{/*二、也可使用react中专门遍历children的方法*/}
{children.map(item => item)}
{/*// {React.Children.map(children,item=>item)}*/}
</section>
}
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
// 公共css放index.js中,这样在其余组件中也可使用(webpack会把全部的组件最后都编译到一块儿,index是主入口)
// import 'static/css/reset.min.css'
import 'bootstrap/dist/css/bootstrap.css' // 须要导入未通过压缩的文件,不然报错(真实项目中bootstrap已是过去式了,后期用ant)
import Dialog from "./component/Dialog-bootstrap";
ReactDOM.render(<main>
<Dialog content ='马少帅很帅'/>
<Dialog type ={2} content='系统错误了'/>
<Dialog type = '请登陆' content={
'新的JSX语法'
}>
<div>
<input type="text" className='form-control' placeholder='请输入用户名'/><br/>
<input type="password" placeholder='请输入密码'/>
</div>
<br/>
<button className='btn btn-success'>登陆</button>
<button className='btn btn-danger'>取消</button>
</Dialog>
</main>,root);
复制代码
二、继承component类来建立组件
基于component把JSX转换为一个对象,当render渲染这个对象的时候,遇到type是一个函数或者类,不是直接建立元素,而是把方法执行,
特色: 一、调取组件至关于建立类的实例(this),把一些私有的属性挂载到实例上了,这样组件内容全部的方法中均可以基于实例获取这些值(包括:传递的属性和本身管理的状态) 二、有本身的状态管理,当状态改变的时候,react会从新渲染视图(差别更新:基于DOM-DIFF只把有差别的部分渲染)
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types' // facebook是公司开发的一个插件,基于这个插件咱们能够给组件传的属性设置规则,设置的规则不会影响页面的渲染,能够不加
// 可是会在控制台抛出警告错误
class Dialog extends React.Component{
// this.props只读的,咱们没法在方法中修改它的值,可是能够给其设置默认值或者设置一些规则(例如:设置是不是必须传递的以及传递的值的类型等)
static defaultProps ={
lx:"系统提示"
};
static propTypes = {
// 设置属性规则,若是传的不是这个规则的,不影响页面的渲染,只是会在控制台抛出警告错误
// con: PropTypes.string 传递的内容必须是字符串
con:PropTypes.string.isRequired // 传递的内容是字符串,并且还必须是传递
};
constructor(props){ // props context updater
// props:当render渲染而且把当前类执行建立实例的时候,会把以前JSX解析出来的props对象中的信息(可能有children)传递给参数props => "调取组件传递的属性"
// props若是不传,super也不传,除了constructor中不能直接使用this.props,其余声明周期函数中均可以使用(也就是执行完成constructor,
// react已经帮咱们把传递的属性接收,而且挂载到实例上了)
super(props); // extends继承,一旦使用了constructor,第一行位置必须设置super执行,至关于React.Component.call(this),也就是call继承,把父类私有的属性继承过来
// this.props:属性集合
// this.refs:ref集合(非受控组件中用到)
// this.context:上下文
// - 若是super(props); 在继承父类私有的时候,就把props挂载到this实例上了,这个this只是constructor中的this,不影响原型上的this,写不写都行
// - 若是只写super() 虽然建立实例的时候把属性传递过来了,可是并无传递父组件,也就是没有把属性挂载到实例上,使用this.props获取的结果是undefined
// -
console.log(props);
// AA = 12; 设置私有属性,可是不符合ES6语法,须要babel-preset-react编译
// fn=()=>{} 设置一个箭头函数,可是不符合ES6语法,须要babel-preset-react编译
}
render(){
// render必须写还必须有返回的东西
return <section>
<h3>系统提示</h3>
<div>
zhufeng
</div>
</section>
}
}
ReactDOM.render(<div>
<Dialog>
<span>d</span>
</Dialog>
</div>,root);
复制代码
组件中的属性(this.props)是调取组件的时候(建立类实例的时候)传递给组件的信息,这部分信息是只读的(只能获取不能修改)-> "组件的属性是只读的"
Object.defineProperty(this.props,'cont',{
writable:true
});
复制代码
用这种方法也改不了
组件部分的总结
建立组件有两种方式:一是“函数式”,一是“建立类式”
【函数式】
一、操做简单 二、能实现的功能也很简单,只是简单的调取和返回
【建立类式】
一、操做相对复杂一点,可是也能够实现更为复杂的业务功能 二、可以使用生命周期函数操做业务 三、函数式能够理解为静态组件(组件中内容调取的时候,就已经固定了,很难再修改),而类这种方式,能够基于组件内部的状态来动态更新渲染的内容:
所谓函数组件是静态组件,和执行普通方法同样,调取一次组件,就把组件中内容获取到,若是不从新调取组件,显示内容是不发生改变的
【需求】实现页面上时间的走动
一、函数式的
import React from 'react';
import ReactDOM from 'react-dom';
function Clock() {
return <div>
<h3>当前北京时间为:</h3>
<div style={{color:'red',fontWeight:'bold'}}>{new Date().toLocaleString()}</div>
</div>
}
/*每隔一秒从新调取组件*/
setInterval(()=>{
ReactDOM.render(<Clock/>,root);
},1000)
//适用于组件内容不会再次发生改变的状况下
复制代码
二、建立类式
class Clock extends React.Component{
constructor(){
super();
// 初始化组件状态(都是对象类型):要求咱们在constructor中把后期须要使用的状态信息所有初始一下(约定俗称的语法规范)
this.state = {
time:new Date().toLocaleString(),
}
}
componentDidMount(){
//react生命周期函数之一:第一次组件渲染完成后触发(咱们只须要间隔1000ms把state状态中的time数据改变,这样react会帮咱们组件中的部门内容进行渲染)
setInterval(()=>{
// this.state.time = new Date().toLocaleString(); //虽然下面的定时器能够修改状态,可是不会通知react从新渲染页面,这样不行
//修改组件的状态
// 一、修改部分状态:会用咱们传递的对象和初始化的state进行匹配,只把咱们传递的属性进行修改,没有传递的依然保留原始的状态信息(部分修改)
// 二、修改状态修改完成,会通知react把组件进行从新渲染
this.setState({
time:new Date().toLocaleString(),
},()=>{
/*当通知react把须要从新渲染的JSX元素从新渲染完成后,执行的回调操做(相似于生命周期中的componentDidUpdate)项目中通常使用钩子函数*/
// 设置回调的缘由是:通知完就直接往下执行,render方法是个异步操做
});
},1000)
}
render(){
return <div> <h3>当前北京时间为:</h3> <div style={{color:'red',fontWeight:'bold'}}>{this.state.time}</div> </div>
}
}
ReactDOM.render(<Clock/>,root);
复制代码
React中的组件有两个很是重要的概念:
一、组件的属性:[只读]调取组件的时候传递进来的信息 二、组件的状态:[读写]本身在组件中设定和规划的(只有类声明式组件才有状态的管控你,函数式组件声明不存在状态的管理)
组件状态相似于VUE中的数据驱动:
咱们数据绑定的时候是基于状态值绑定,当修改组件状态后,对应的JSX元素也会跟着从新渲染(差别渲染:只把数据改变的部分从新渲染,基于
DOM-DIff
算法完成)
当代前端框架最重要的核心思想就是:“数据操做视图(视图影响数据)”,让咱们告别JQ手动操做DOM的时代,咱们之后只须要改变数据,框架会帮咱们从新渲染视图,从而减小直接操做DOM(提升性能,也有助于开发效率)
尽可能少操做DOM
在react当中: 一、基于数据驱动(修改状态数据,react帮助咱们从新渲染视图)完成的组件叫作“受控组件(受数据管控的组件)” 二、基于ref操做DOM实现视图更新,叫作“非受控组件” 真实项目中,建议多使用“受控组件”
VUE:MVVM 数据更改,视图跟着改变,视图更改,数据也跟着改变(双向数据绑定) React:MVC 数据更改视图跟着改变(本来是单向的,可是咱们能够手动设置为双向的)
render(){
let {text} = this.state;
return <section className='panel panel-default'>
<div className='panel-heading'>
<input type="text" className='form-control' value={text} onChange={ev=>{
// 在onChange中修改状态信息,实现的是视图改变数据
this.setState({
text:ev.target.value
})
}}/>
</div>
<div className='panel-body'>
{text}
</div>
</section>
}
复制代码
所谓生命周期函数(钩子函数)描述一个函数或者组件从建立到销毁的过程,咱们能够在过程当中间,基于钩子函数完成本身的一些操做(例如:在第一次渲染完成作什么,或者在第二次即将从新渲染以前作什么等...)
【基本流程】
constructor
: 建立一个组件componentWillMount
: 第一次渲染以前render
:第一次渲染componentDidMount
: 第一次渲染以后 【修改流程】 当组件的状态数据发生改变(setState)或者传递的属性发生改变(从新调用组件,传递不一样的属性)都会引起render从新执行渲染(渲染也是差别渲染)shouldComponentUpdate
是否容许组件从新渲染componentWillUpdate
从新渲染以前render
第二次及之后从新渲染componentDidUpdate
从新渲染以后
componentWillReceiveProps
父组件把传递给子组件的属性发生改变后触发的钩子函数 属性改变也会改变子组件从新渲染,触发钩子函数
【销毁】 原有的渲染的不消失,之后不能基于数据改变视图
componentWillUnmount
卸载组件以前(通常不用)
index.js:
import React from 'react';
import ReactDOM, {render} from 'react-dom';
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.css'
class A extends React.Component {
static defaultProps = {}; // 第一个执行,属性设置默认值
constructor() {
super();
console.log('1=constructor');
this.state = {
n: 1
}
}
componentWillMount() {
console.log('3=componentWillMount 第一次渲染前', this.refs.HH);
// 在这里,若是直接setState修改数据(同步的),会把状态信息改变后,而后render和didMount,若是setState是放到一个异步操做中完成(例如:定时器或者从服务器获取数据),也是先执行render和did
// 而后再执行这个异步操做修改状态,紧接着走修改的流程(这样和放到didMount中没啥区别),因此咱们通常吧数据请求放到DID中处理
// 真实项目中的数据绑定,第一次组件渲染,咱们都是绑定的默认属性,第二次才是从服务器获取的数据,有些属性,咱们须要根据数据是否存在,判断显示隐藏
}
componentDidMount() {
console.log('4=componentWillMount 第一次渲染后', this.refs.HH);
//真实项目中,这个阶段通常作以下处理:
// 一、控制状态信息更改的操做
// 二、从服务器获取数据,而后修改状态信息,完成数据绑定
setInterval(() => {
this.setState({
n: this.state.n + 1
})
}, 5000)
}
shouldComponentUpdate(nextProps, nextState) {
// this.state.n 更新以前的
console.log('5=shouldComponentUpdate 函数返回true(容许),false(不容许)');
// return true
/*在这个钩子函数中,咱们获取的state不是最新修改的,而是上一次的state的值
例如:第一次加载完成后,5000ms后,咱们基于setState把n修改成2,可是此处获取的仍是1呢
可是这个有两个参数:
nextProps:最新修改的属性
nextState:最新修改的状态
*/
if (nextState.n > 3) {
return true
} else {
return false
}
}
componentWillUpdate(nextProps, nextState) {
// this.state.n 也是更新以前的,也有两个参数存储最新的信息
console.log('6=componentWillUpdate');
}
componentDidUpdate() {
// this.state.n 更新以后的
// 先render
console.log('8=componentWillUpdate');
}
render() {
console.log('2=render');
return <section ref='HH'>
{this.state.n}
</section>
}
}
ReactDOM.render(<main>
<A></A>
</main>, root);
复制代码
父组件传子组件:
基于属性传便可(并且传递是单方向的:只能把信息给儿子,儿子不能直接把信息做为属性传递给父亲) 后期子组件中的信息须要修改: 可让父组件传给子组件的信息发生变化(也就是子组件接收的属性发生变化,子组件会从新渲染 =>
componentWillReceiveProps
钩子函数)
子改父:
相似于这种子改父的操做,咱们须要使用一下技巧: 一、把父组件中一个方法做为属性传递给子组件 二、在子组件中,把基于属性传递进来的方法,在合适的时候执行(相对于在执行父组件中的方法:而这个方法中彻底能够操做父组件的信息)