一. 虚拟DOM树(是以JS对象的形式存在的,用js对象模拟dom元素)
目的:就是为了实现dom的高效更新
二. diff算法(diff:diffentent)
三. 构造函数 与 Class 关键字
四. webpack:
webpack默认只能识别.js
为后缀名的文件,像.html,.css,.vue
为后缀的文件无法识别,所以需要安装第三方的loader插件
,将这些无法识别的后缀名文件转化为.js的文件,用于webpack打包识别。
1. 快速初始化项目
2. 创建目录结构,src代码文件(内容文件),dist产品文件(打包文件)
3. 创建src下的‘主页面’和‘入口文件’
4. 使用cnpm i webpack -D,安装webpack依赖
5. cnpm i webpack-cli -D,安装webpack-cli依赖(干嘛用的?要去看看)
4和5合并,cnpm i webpack webpack-cli -D
为什么要安装webpack-cli:
webpack3.6之后,webpack4.0,打包运行的依赖(dev)不再由webpack提供,而是由webpack-cli提供。所以"dev": "webpack"
可以删除
直接使用webpack ,就可以实现打包。
6. 新建‘webpack.config.js’文件
7. webpack4.X的入口文件
必须要使用module.exports = {},这是node语法,因为webpack是基本node构建的。
不能用export default = {},因为这是ES6的语法,chrome也不支持(因为node是chrome的v8引擎)
8. webpack4.X的新增了mode选项,必选
可选development
产品环境, product
开发环境。可配置为
mode: process.env.NODE_ENV === 'production' ? '/' : '/',
9. 运行npm i webpack-dev-server -D
,每次保存代码之后就可以自动渲染页面
npm i webpack-dev-server -D
- 在‘package.json’文件中配置,“dev”: “webpack-dev-server”,
- 运行
npm run dev
,保存页面,就会实时渲染页面了
10. main.js的存放(dist->main.js),在dist文件下
11. dev 的其他配置
12. 运行 npm install html-webpack-plugin -D
,自动生成首页
13.!重要!webpack中rules
- 识别方式:从 右往左。
{test:/\.css$/, use: ['style-loader', 'css-loader'] }
,先识别css-loader
,再识别style-loader
- 规范:webpack2.0之后,rules中的
-loader
一定要加上,不然不会识别
14.!重要!导入文件,自动补全文件后缀名(.js/.jsx/.json)配置
在webpack.config.js
文件中,导出的配置对象中新增如下节点 extensions
。
resolve: {
//扩展名
extensions: ['.js','.json','.jsx'] //导入这几个文件,可以不写后缀名
}
15.! 重要!webpack设置根目录(用@表示根目录src)在webpack.config.js
文件中,导出的配置对象中新增如下节点alias
。
resolve: {
//扩展名
extensions: ['.js','.json','.jsx'], //导入这几个文件,可以不写后缀名
//别名
alias :{
'@':path.join(__dirname,'./src') //路径拼接当前文件,用@表示src根目录路径。
}
}
16.webpack设置识别.css
- 安装插件 :
cnpm style-loader css-loader -D
,先写style-loader 比较好,因为处理css文件有先后顺序。 - 在webpack的rules中配置
{test:/\.css$/, use: ['style-loader', 'css-loader'] }
固定顺序,先’style-loader’后’css-loader’:文件处理时候,webpack将文件先交给css-loader
处理,再将处理好的结果,交给style-loader
处理,最后交给webpack打包处理。
16.webpack设置字体文件
五. React基本使用
1. 基本渲染
2. 使用jsx语法(直接用jsx语法是不行的,需要bable来转义)。
jsx语法:就是在js中,可以混合编写html语言,符合xml规范的js语法
但是这样是有问题的,因为`const mydiv = <div ...>....</div>`是jsx语法,这一行代码并不符合JS的语法规范。所以打包失败。这里就需要`Babel`来转义
那么就要对这个html语言const mydiv = <div ...>....</div>
写成符合JS语法规范才能运行。
那么就需要Bebel来进行转化
配置babel:
-
装插件
-
配置webpack文件
-
在根目录下添加一个叫做
.babelrc
的文件,写规则{ "presets":["env", "stage-0", "react"], "plugins":["transform-runtime"] }
配置结束之后,就不会报错了。
import React from 'react'
import ReactDOM from 'react-dom'
let name = 'zzz'
let domArray = [
<div>这是个dom数组</div>,
<div>没错</div>
]
ReactDOM.render(
<div>
<p>欢迎来到jsx的世界{name}</p>
{domArray}
</div>,
document.getElementById('root'));
总结:
- react使用的
jsx
语法,并不是直接将html语句写入就直接渲染,而是需要用babel进行转化jsx语句。如果没有babel
,那么就会报错。 const mydiv = <div ...>....</div>
是JSX
语法,但是在打包之后,这个语句被babel
打包转译成了JS
语法React.createElement(...)
的形式运行。 如- 果有人说是
React
是用JSX
直接运行,那么他就是个der。面试官故意这么说,你一定要说不是,不然你就是个der。
六. React渲染的特殊写法(样式命名class–>className)
为什么在react的dom元素中class样式不能用class
命名,而是改成了className
因为babel将JSX转义成JS!!!而class在JS中是有特殊含义的(类的命名)
七. React组件
1. 组件
- 在react中,一个构造函数就是个一个组件(构造函数特点:大写的函数名)。
- 必须返回一个符合JSX语法的DOM元素
let name = 'zzz'
let domArray = [
<div>这是个dom数组</div>,
<div>没错</div>
]
//创建组件MyDom(构造函数)
// 标准写法MyDom
// function MyDom (){
// return (
// <div>
// <p>欢迎来到jsx的世界{name}</p>
// {domArray}
// </div>
// )
// }
//ES6写法MyDom
const MyDom = ()=> {
return (
<div>
<p>欢迎来到jsx的世界{name}</p>
{domArray}
</div>
)
}
ReactDOM.render(
<MyDom></MyDom>,
document.getElementById('root')
);
2. 组件传值
props:组件的传值,只读(与vue一直)
let infos = {
name :'zzz',
age: 18
}
//创建组件MyDom(构造函数)
const MyDom = (props)=> {
//props:组件的传值,跟vue中的一样。只读,不可修改。
return (
<div>
<p>欢迎来到jsx的世界{props.age}的{props.name}</p>
</div>
)
}
ReactDOM.render(
//调用子组件<MyDom></MyDom>
//往子组件传值:name={infos.name} age={infos.age} 相当于一个对象的两个属性,
//<MyDom name={infos.name} age={infos.age}></MyDom>,
//ES6语法,扩展运算符
<MyDom {...infos}></MyDom>,
document.getElementById('root')
);
3. function函数创建组件:抽离函数(至单独页面,组件化 )
- 将上述的
function MyDom
函数抽离到一个文件中,MyDom.jsx
中; - 在
MyDom.jsx
中,必须引入React (import React from 'react';
); - 再将
MyDom.jsx
文件引入到index.js
文件中
MyDom.jsx文件
// const MyDom = (props)=> {
// return (
// <div>
// <p>欢迎来到jsx的世界,{props.age}的{props.name}</p>
// </div>
// )
// }
// // 把组件暴露出去
// export default MyDom
import React from 'react' //如果不加这一行会报错。因为这个文件是已react组件的形式导出,不引入React,不认识这个组件
// 创建并导出
export default function MyDom (props) {
return (
<div>
<p>欢迎来到jsx的世界,{props.age}岁的{props.name}</p>
</div>
)
}
index.js 文件
import React from 'react';
import ReactDOM from 'react-dom';
import MyDom from '@/components/MyDom'
let infos = {
name: 'zzz',
age: '18'
}
ReactDOM.render(
<MyDom {...infos}></MyDom>,
document.getElementById('root')
)
4. class关键字创建组件:面向对象编程
创建:React的组件(子类)
class MyDom extends React.Component{
render() {
return <div>合法的JSX DOM结构</div>
}
}
class myDom
创建一个子类,类,类名首字母大写extends
继承React.Component()
父类- 子类中必须要有一个
render
函数(子类的实例方法),且必须返回合法的JSX
DOM 结构
render函数
- 子类的实例方法
- 渲染虚拟DOM元素
传参(只读,不可写):this.props,
外界传递,相当于vue的props
- 无论是function还是class创建的组件,参数都是只读。
this.props
无法被修改。this.props.xx=yy
,会报错。 - 在class创建的组件中,只需要传参,不需要接受参数。直接使用
this.props.xx
就可以直接调用。 - 在组件内部中,this 表示当前组件的实例对象
调用组件MyDom
:
import React from 'react';
import ReactDOM from 'react-dom';
import MyDom from '@/components/MyDom2'
let infos = {
name: 'zzz',
age: '18'
}
ReactDOM.render(
<MyDom {...infos}></MyDom>,
document.getElementById('root')
)
组件MyDom
:
import React from 'react'
// 创建并导出
export default class MyDom extends React.Component {
render() {
const datas = this.props
return (
<div>
{/* <p>欢迎来到jsx的世界,{this.props.age}岁的{this.props.name}</p> */}
<p>欢迎来到jsx的世界,{datas.age}岁的{datas.name}</p>
</div>
)
}
}
自定义参数(可读可写):this.state
自有的属性,相当于vue中的data中return的数据
私有数据:在class中,用constructor构造器的this.state
代表vue中的data。
import React from 'react'
// 创建并导出
export default class MyDom extends React.Component {
constructor() {
super()
this.state = {
//私有属性
id: '330*************'
}
}
render() {
const datas = this.props
let state = this.state
state.id = '33008**********7'
return (
<div>
<p>欢迎来到jsx的世界,{datas.age}岁的{datas.name}</p>
<p>我的id是{this.state.id}</p>
</div>
)
}
}
this.props 赋值给 this.state
import React from 'react'
// 创建并导出
export default class MyDom extends React.Component {
constructor(props) {
super(props)
this.state = {
//私有属性
id: '330*************',
name : props.name
}
}
render() {
...
}
}
5.function 和class 创建组件的比对
- class创建的组件,有props,有自己的私有数据(state属性)和生命周期函数。而function,只有props。
- 用构造函数function 创建出来的组件,叫做
‘无状态组件’
,无
私有数据和生命周期。 - class 创建出来的组件,叫做
‘有状态组件’
,有
私有数据和生命周期。
React官方:无状态组件由于没有私有数据和生命周期,所以运行效率会比有状态组件高。
但是为了之后项目 的多变性,一般都是将组件写成有
状态组件,因为不知道什么时候会把无状态组件改成有状态组件。
八. React列表渲染
1.循环列表 :{list.map(item => { return 'JSX格式' })}
循环一个数组,每个数组添加一个标签。
{state.list.map(item => {
return <div key={item.id}>姓名:{item.name}</div>
})}
完整代码:
import React from 'react'
// 创建并导出
export default class MyDom extends React.Component {
constructor() {
super()
// 私有属性
this.state = {
list : [
{
id: 1,
name: '章章',
age: 18,
sex: '女',
nationality: '中国'
},
{
id: 2,
name: '东东',
age: 17,
sex: '男',
nationality: '中国'
}
]
}
}
render() {
let state = this.state
return (
<div>
<p>list渲染</p>
{state.list.map(item => {
return <div key={item.id}>姓名:{item.name}</div>
})}
</div>
)
}
}
2.抽离列表中的Item:子类Item
使用class法,还是构造函数法,区别于是是否需要私有属性;下列Item中不需要私有属性,所以使用构造函数法。
子类Item:
function Item(item) {
return (
<div key={item.id}>姓名:{item.name}</div>
)
}
调用Item:
render() {
let state = this.state
return (
<div>
<p>list渲染</p>
{state.list.map(item => {
return <Item {...item} key={item.id}></Item>
})}
</div>
)
}
<Item {...item} key='item.id'></Item>
:调用;{...item}
:传参;key='item.id'
:,map调用子类,需要key作为唯一标志符。
九. React样式
1.语法:样式命名必须为className
,JSX规范为了区别class类
2.全局导入(样式表)
作用域:子组件的导入的样式表是会影响全局。
- 抽离样式表
- 直接在组件头导入,
import './index.less'
import React from 'react'
// 导入样式
import './index.less'
export default class MyDom extends React.Component {
render() {
let state = this.state
return (
<div>
{/* 样式命名 */}
<p className='title'>list渲染</p>
</div>
)
}
}
背景:
- 在父组件中命名
className='title'
,但不写样式 - 在子组件中
MyClassName/index.jsx
,同样命名一个样式className='title'
- 在子组件中,引入了一个样式文件
index.less
,样式中编写.title{ color: blueviolet;}
结果:
不管是子组件的样式还是父组件的样式都被修改。
完整代码:
index.js入口文件
import React from 'react';
import ReactDOM from 'react-dom';
import MyList from '@/components/MyClassName/index.jsx'
ReactDOM.render(
<div>
<p className='title'>父组件</p>
<MyList ></MyList>
</div>,
document.getElementById('root')
)
MyClassName的index文件
import React from 'react'
import './index.less'
default class MyDom extends React.Component {
render() {
let state = this.state
return (
<div>
<p className='title'>子组件</p>
</div>
)
}
}
index.less样式文件
.title{
color:blueviolet;
}
3.局部导入(样式表):配置css-loader模块化
背景:
由于React中没有指令的概念,所以根本就没有Vue <style scoped>
中scoped这个指令这个用法。
解决方法:
在webpack的rules配置css-loader模块化。将CSS文件也编译成一个模块,将css具体化。
module: {
rules: [
/*
注意:
1、通过为css-loader添加modules参数,启用CSS的模块化
2、localIdentName用来控制className名称的长度。hash值取5位
例如:CommenItem_box-7ef23
*/
{
test: /\.css$/,
use: [
"style-loader",
/*"css-loader?modules&localIdentName=[name]_[local]-[hash:5]",*/
"css-loader?modules"
],
},
],
},
用法:
引入:import myStylefrom './index.less
打印:console.log("myStyle",myStyle)
// 对象
赋值className:<p className={myStyle.title}>子组件</p>
css文件:
.title{
color:blueviolet;
}
js文件:将样式名当做属性赋值给className
export default class MyDom extends React.Component {
render() {
return (
<div>
<p className={myStyle.title}>子组件</p>
</div>
)
}
}
4.使用localIdentName启动样式模块化:
LOCALIDENTNAME
{
test: /\.css$/,
use: [
"style-loader",
"css-loader?modules&localIdentName=[path][name]_[local]-[hash:5]",
],
},
样式对象属性名定义:
localIdentName= [相对于根目录样式表路径path][样式表文件名name]_[样式表className命名local]-[32位hash只取五位,hash:5]
打印css样式对象:
5.普通样式 和 模块化样式 拼接
字符串拼接法:<p className={myStyle.title+' title'}>
或者
数组join空格法:<p className={[myStyle.title,'title'].join(' ')}>
6. 模块样式:local() 和 全局样式:global()
-
如果在webpack中设置了样式模块化,那么默认就是在所有样式中加了
:local()
。那么webpack设置模块化的作用就是——默认将所有样式加:local()
.title{ color:blueviolet; } // 相当于 :local(.title){ color:blueviolet; }
-
但是如果样式加了:global(),那么这个样式会全局作用。
:global(.mytitle) { color:brown; }
7.样式最终篇:将普通样式表改为.less
或者.sass
问题:普通样式表和第三方样式混用
- 第三表都是
.css
(比如bootstrap,element,ant等)定义,但是由于样式表中配置了"css-loader?modules&localIdentName=[name]_[local]-[hash:5]"
配置,将所有.css
的样式表都模块化。那么第三方样式表的写法也会变得非常的复杂。
- 所以只能将普通样式表的后缀名改成
.less
或者.sass
来区分普通样式表和第三方样式表。
配置方式:
-
将
.css
webpack模块化配置去掉。{ test: /\.css$/, use: [ "style-loader", /*"css-loader?modules&localIdentName=[path][name]_[local]-[hash:5]",*/ "css-loader" ], },
-
webpack配置
less-loader
或者sass-loader
模块化。{ test: /\.sass$/, use: [ "style-loader", "css-loader", "sass-loader?modules&localIdentName=[path][name]_[local]-[hash:5]" ], }, { test: /\.less$/, use: [ "style-loader", "css-loader", "less-loader?modules&localIdentName=[path][name]_[local]-[hash:5]" ], },
十. React绑定事件:
React绑定事件注意点
- 事件的名称必须是驼峰写法:onClick、onMouseOver;
- 绑定的处理函数里必须是一个function,格式:
onClick = { function }
绑定方式
<button onclick='' >按钮</button>
无效
<button @click='' >按钮</button>
无效
在React中有一套自己的事件绑定机制:
onClick={function () {}}
|| onClick={()=> {}}
- 事件名使用小驼峰命名法。
<button onClick={function}>按钮</button>
- 必须返回一个函数
onClick={()=> {}}
。onClick=''
,报错,因为onClick=''
返回是个String。onClick={}
,报错,因为onClick={}
返回为空。
抽离function
1.引用实例方法(有问题:this指向问题,不建议使用,)
语法:
<button onClick={this.handleClick}>按钮</button>;
!!! 注意:
this.handleClick
不能加()
。onClick={this.handleClick}
是引用。onClick={this.handleClick()}
调用。
完整代码:
export default class MyDom extends React.Component {
constructor() {
super()
// 私有属性
this.state = {}
}
render() {
let state = this.state
return (
<div>
<p className='title'>子组件</p>
<button onClick={this.handleClick}>按钮</button>
</div>
)
}
handleClick(){
console.log('点击')
}
}
2.箭头函数法简写法(虽然渐变,但是会有)
语法:
<button onClick={()=>{this.handleClick()}}>按钮</button>
完整代码:
export default class MyDom extends React.Component {
constructor() {
super()
// 私有属性
this.state = {}
}
render() {
let state = this.state
return (
<div>
<p className='title'>子组件</p>
<button onClick={()=>{this.handleClick()}}>按钮</button>
</div>
)
}
handleClick(){
console.log('点击')
}
}
3.箭头函数法(最规范)
语法:
<button onClick={()=>this.handleClick('传参')}>按钮</button>
完整代码:
export default class MyDom extends React.Component {
constructor() {
super()
// 私有属性
this.state = {}
}
render() {
let state = this.state
return (
<div>
<p className='title'>子组件</p>
<button onClick={()=>this.handleClick("参数")}>按钮</button>
</div>
)
}
handleClick = (v) => {
console.log('点击',v)
}
}
4.箭头函数法(最实用且规范)
语法:
<button onClick={()=>this.handleClick('传参')}>按钮</button>
完整代码:
export default class MyDom extends React.Component {
constructor() {
super()
// 私有属性
this.state = {}
}
render() {
let state = this.state
return (
<div>
<p className='title'>子组件</p>
<button onClick={this.handleClick("参数")}>按钮</button>
</div>
)
}
handleClick = (v) => {
console.log('点击',v)
}
}
十一. React修改DOM内容
1.直接修改(无法改变视图)
虽然将state的参数需改成功,但是展示在页面的内容任然不会改变。
import React from 'react'
import './index.less'
export default class MyDom extends React.Component {
constructor() {
super()
// 私有属性
this.state = {
name:'zzz'
}
}
render() {
return (
<div>
<p>点击按钮,将按钮的名称从'zz'改为'dd'</p>
<button onClick={()=>this.handleClick("参数")}>{this.state.name}</button>
</div>
)
}
handleClick = () => {
this.state.name = 'dd'
console.log('点击',this.state.name)
}
}
2.this.setState修改(异步和回调,规范)
语法: this.setState({ },fun)
注意点:
this.setState({ })
并不会直接覆盖this.state,只会把对应的this.state中的状态更新。this.setState({ })
方法执行是异步的。无法立即拿到最新的state值。所以调用this.setState({ })
之后直接打印目标属性值会出现没有改变的现象;再一次打印才会有改变的现象。this.setState({ },fun)
加上回调,在里面操作下一步就能拿到最新的state值。
只有异步:
无法立即拿到最新的state值
代码:
handleClick = () => {
this.setState({
name : 'dd'
})
console.log('点击',this.state)
}
第一次点击:
第二次点击:
加上回调:
在回调中获取最新的state的值
语法: this.setState({ },fun)
handleClick = (v) => {
this.setState({
name : 'dd'
},()=>{
console.log('点击',this.state)
})
}
3.修改文本框
React是单向文本流,不能双向绑定,只能手动设置双向修改。
修改状态——>改变文本框,单向
- input 通过value绑定
{this.state.name}
值。 - 改变
{this.state.name}
来改变 input的value
值。 - 键入无效。
eeror:
只设置value值,只能通过修改状态来改变文本框;不能通过键盘来改变文本框内容。
代码:
export default class MyDom extends React.Component {
constructor() {
super()
// 私有属性
this.state = {
name:'zzz',
age: '18'
}
}
render() {
return (
<div>
<p>点击按钮,将按钮的名称从'zz'改为'dd'</p>
<button onClick={()=>this.handleClick("参数")}>{this.state.name}</button>
<input value = {this.state.name}/>
</div>
)
}
handleClick = (v) => {
this.setState({
name : 'dd'
},()=>{
console.log('点击',this.state)
})
}
}
点击前:
点击后:
同步:修改状态<——>键入文本框,手动双向
- input 通过value绑定
{this.state.name}
值。 修改状态——>键入文本框。 - 提供
onchange
方法: 修改状态<——键入文本框,键入有效。
import React from 'react'
import './index.less'
export default class MyDom extends React.Component {
constructor() {
super()
// 私有属性
this.state = {
name:'zzz',
age: '18'
}
}
render() {
return (
<div>
<p>点击按钮,将按钮的名称从'zz'改为'dd'</p>
<button onClick={()=>this.handleClick("参数")}>{this.state.name}</button>
<input value = {this.state.name} onChange={(e)=>this.changeInput(e)}/>
</div>
)
}
changeInput = (e) =>{
this.setState({
name : e.target.value
},()=>{
console.log('键入',this.state)
})
}
handleClick = (v) => {
this.setState({
name : 'dd'
},()=>{
console.log('点击',this.state)
})
}
}
十二. React生命周期(lifecycle)
React生命周期分为三个阶段:创建、运行、销毁
- 创建:(特点)一辈子只执行一次。
componentWillMount,淘汰
,存在也没啥作用;
render;
componentDidMount; - 运行:(特点)按需,根据props或者state属性的改变,选择性的执行0~N次。
componentWillReceiveProps(nextProps),淘汰
,存在也没啥作用;
shouldComponentUpdate(nextProps,nextState);
componentWillUpdate(nextProps,nextState),淘汰
,存在也没啥作用;
render;
componentDidUpdate(prevProps,preState); - 销毁:(特点)一辈子只执行一次。
componentWillUnMount。
1. getDefaultProps(props初始化的过程)
props初始化的过程
props属性值 : initCount
父组件:
import React from 'react';
import ReactDOM from 'react-dom';
import MyCount from '@/components/MyCount.jsx'
let count =9
ReactDOM.render(
<div>
<MyCount initCount={count}></MyCount>
</div>,
document.getElementById('root')
)
props默认值
为了防止没有设置默认props导致的错误。
语法:
static defaultProps = { initCount:0 }
完整代码
子组件 MyCount.jsx:
import React from 'react'
export default class MyCount extends React.Component{
constructor(props) {
super(props)
this.state = {
count: 0
}
}
static defaultProps = {
initCount:0
}
render() {
return (
<div id="myCount">
<h1>这是计数器</h1>
<p>{this.props.initCount}</p>
</div>
)
}
}
props校验
将传替过来的值做类型校验。
语法:
static propTypes= {
initCount: ReactTypes.number
}
-
第一步:安装
propTypes
插件 -
第二步:引入
//注意: ReactTypes名字可以随便取,职能很单一,用于做类型校验 import ReactTypes from 'prop-types'
-
第三步:校验
static propTypes= { initCount: ReactTypes.number }
完整子组件代码:
import React from 'react'
//注意: ReactTypes名字可以随便取,职能很单一,用于做类型校验
import ReactTypes from 'prop-types'
export default class MyCount extends React.Component{
constructor(props) {
super(props)
this.state = {
count: 0
}
}
static defaultProps = {
initCount:0
}
// 如果要为传递过来的props属性进行类型校验,比如要安装React提供的第三方插件,叫做propTypes
static propTypes= {
initCount: ReactTypes.number
}
render() {
// let data = this.state
return (
<div id="myCount">
<h1>这是计数器</h1>
<p>{this.props.initCount}</p>
</div>
)
}
}
2. getInitialState:( state初始化的过程)
在构造器设置state 属性 之一:count
constructor(props) {
super(props)
this.state = {
count: 0
}
}
3. ! 弃用!
componentWillMount:(初始化Props、state已经结束)
无DOM,有props和state;
相当于Vue中的Created()生命周期函数。
- 没创建DOM,更没挂载到页面。无法操作页面上的DOM元素。
- props和state已初始化完毕。
componentWillMount() {
let myDom = document.getElementById('myCount')
console.log('我的计数器',myDom)
console.log('父组件传参,props:',this.props.initCount)
console.log('实例参数,state:',this.state.count)
}
4. render:(创建,渲染,挂载DOM的过程)
无dom,有state和props;
渲染虚拟DOM,函数执行结束,那么虚拟DOM渲染结束。
这个函数是DOM创建和挂载的过程,过程还未结束,所以DOM在函数中是获取不到的。只有DOM挂载结束,函数结束后进入到下一个函数才能获取到。但并不是说,挂载DOM是在下一个阶段中执行的。
render() {
let myDom = document.getElementById('myCount')
console.log('render中,我的计数器',myDom)
return (
<div id="myCount">
<h1>这是计数器</h1>
<p >
<span>当前数量:</span>
<span id="number">{this.state.count}</span>
</p>
</div>
)
}
4. componentDidMount:(DOM挂载已经结束)
有DOM,有state,有props
componentDidMount() {
let myDom = document.getElementById('myCount')
console.log('我的计数器',myDom)
console.log('父组件传参,props:',this.props.initCount)
console.log('实例参数,state:',this.state.count)
}
5. ! 弃用!
componentWillReceiveProps:(props更新,调用)
创建一个子组件<Son>
,接受父组件的count
值
- 父组件第一次创建并不会触发子组件
<Son>
的componentWillReceiveProps
函数。 - 后续,只要父组件触发
this.setState()
函数,不管函数内部有没有内容,<Son>
的componentWillReceiveProps
函数就会执行一次。
子组件:
class Son extends React.Component{
constructor(props) {
super(props)
}
render() {
return (
<div>
<h3>这是子组件</h3>
<p>父组件传递的值count:{this.props.count}</p>
</div>
)
}
componentWillReceiveProps(nextProps,nextState) {
console.log('子组件:WillReceiveProps----------------')
console.log('this.props.count',this.props.count)
console.log('nextProps',nextProps.count)
console.log('------------------------')
}
}
父组件:
render中引入<Son>
,并传参this.state.count
<Son count={this.state.count}></Son>
6. shouldComponentUpdate:(判断是否更新)
- 增加一个按钮,减数字;
- 如果this.state.count>0,shouldComponentUpdate返回true,更新,不然不更新;
- this.state.count的初始化值为2。
减函数:
<input type='button' value='减' onClick={() => this.handleSub()}></input>
减函数:
handleSub() {
this.state.count --
this.setState({
count: this.state.count
})
}
shouldComponentUpdate函数
shouldComponentUpdate() {
if(this.state.count) {
console.log('shouldUpdate-更新-------')
console.log('实例参数,state:',this.state.count)
console.log('------------------------')
return true
}else {
console.log('shouldUpdate-不更新-------')
console.log('实例参数,state:',this.state.count)
console.log('------------------------')
return false
}
}
DOM更新:this.state.count 从2 ->1
DOM不更新:this.state.count 从1 ->0
7. ! 弃用!
componentWillUpdate:(DOM更新的过程)
state状态改变,但是DOM还没改变。
创建一个按钮,用于增加this.state.count
的值
render函数:
render() {
let myDom = document.getElementById('myCount')
console.log('render----------------')
console.log('我的计数器',myDom)
console.log('------------------------')
return (
<div id="myCount">
<h1>这是计数器</h1>
<p >
<span>当前数量:</span>
<span id="number">{this.state.count}</span>
</p>
<input type='button' value='+1' onClick={() => this.handleAdd()} style={{marginRight: 10 + 'px'}}></input>
</div>
)
}
handleAdd函数:
handleAdd() {
this.state.count ++
this.setState({
count : this.state.count
},()=>{
console.log('增加:实例属性count:',this.state.count)
})
}
componentWillUpdate 函数:
componentWillUpdate() {
console.log('WillUpdate--------')
console.log('实例参数,state:',this.state.count)
let number = document.getElementById('number').innerHTML
console.log('DOM内容数字:',number)
console.log('------------------------')
}
8. componentDidUpdate:(DOM更新已经结束)
componentDidUpdate() {
console.log('DidUpdate--------')
console.log('实例参数,state:',this.state.count)
let number = document.getElementById('number').innerHTML
console.log('DOM内容数字:',number)
console.log('------------------------')
}
组件完整代码
import React from 'react'
//注意: ReactTypes名字可以随便取,职能很单一,用于做类型校验
import ReactTypes from 'prop-types'
class Son extends React.Component{
state = {
name:'zz'
}
render() {
console.log('子组件:render----------------')
return (
<div>
<h3>这是子组件</h3>
<p>父组件传递的值count:{this.props.count}</p>
</div>
)
}
// componentWillReceiveProps(nextProps) {
// console.log('子组件:WillReceiveProps----------------')
// console.log('this.props.count',this.props.count)
// console.log('nextProps',nextProps.count)
// console.log('------------------------')
// }
}
export default class MyCount extends React.Component{
constructor(props) {
super(props)
this.state = {
count: props.initCount,
hh:1
}
}
static defaultProps = {
initCount:0
}
// 如果要为传递过来的props属性进行类型校验,比如要安装React提供的第三方插件,叫做propTypes
static propTypes= {
initCount: ReactTypes.number
}
// componentWillMount() {
// let myDom = document.getElementById('myCount')
// console.log('WillMount----------------')
// console.log('我的计数器',myDom)
// console.log('父组件传参,props:',this.props.initCount)
// console.log('实例参数,state:',this.state.count)
// console.log('------------------------')
// }
render() {
let myDom = document.getElementById('myCount')
console.log('render----------------')
console.log('我的计数器',myDom)
console.log('------------------------')
return (
<div id="myCount">
<h1>这是父组件</h1>
<p >
<span>计数器当前数值:</span>
<span id="number">{this.state.count}</span>
</p>
<input type='button' value='加' onClick={() => this.handleAdd()} style={{marginRight: 10 + 'px'}}></input>
<input type='button' value='减' onClick={() => this.handleSub()}></input>
<Son count={this.state.count}></Son>
</div>
)
}
componentDidMount() {
let myDom = document.getElementById('myCount')
console.log('DidMount----------------')
console.log('我的计数器',myDom)
console.log('父组件传参,props:',this.props.initCount)
console.log('实例参数,state:',this.state.count)
console.log('------------------------')
}
shouldComponentUpdate() {
if(this.state.count) {
console.log('shouldUpdate-更新-------')
console.log('实例参数,state:',this.state.count)
console.log('------------------------')
return true
}else {
console.log('shouldUpdate-不更新-------')
console.log('实例参数,state:',this.state.count)
console.log('------------------------')
return false
}
}
// componentWillUpdate() {
// console.log('WillUpdate--------')
// console.log('实例参数,state:',this.state.count)
// let number = document.getElementById('number').innerHTML
// console.log('DOM内容数字:',number)
// console.log('------------------------')
// }
componentDidUpdate() {
console.log('DidUpdate--------')
console.log('实例参数,state:',this.state.count)
let number = document.getElementById('number').innerHTML
console.log('DOM内容数字:',number)
console.log('------------------------')
}
handleAdd() {
this.state.count ++
this.setState({
count : this.state.count
},()=>{
// console.log('增加:实例属性count:',this.state.count)
})
}
handleSub() {
this.state.count --
this.setState({
count: this.state.count,
},()=>{
// console.log('减少:实例属性count:',this.state.count)
})
}
}
十三. this传参
1.bind法,改变this指向
bind的作用:
为前面的函数修改函数内部的this指向,让前面内部的this,指向bind参数列表中的第一个参数。
bind的参数:
第一个参数,改变的指向;
第二个及以后,都是传参。
bind和call、apply的区别:
call、apply会立即调用函数;bind只是修改this指向,并不会调用。
语法:
<button onClick={this.handleClick.bind(this)}>按钮</button>
export default class MyDom extends React.Component {
constructor() {
super()
// 私有属性
this.state = {}
}
render() {
let state = this.state
return (
<div>
<p className='title'>子组件</p>
<button onClick={this.handleClick.bind(this)}>按钮</button>
</div>
)
}
handleClick(arg1,arg2) {
}
}
react和vue的区别
1. 组件
Vue:vue的组件化是已一个个的.vue文件的模板存在的。这个模板包括,template,script,style三个,将.vue文件抛出(export ),再将这个文件import到另一个文件中。
React:react有组件化的形式,但是在react中,都是以js文件的形式存在。
2. 有无指令
React:没有指令的概念;
Vue:都是指令(v-for v-if,style中的scoped)。
3.数据绑定
React:单项数据流,input键入要手动赋值给state参数;
Vue:双向绑定,input键入变化直接修改state参数,不需要手动赋值。
vue 中publicPath中’./‘和’/'的区别
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="zz"></div>
<div id="time"></div>
<div id="prop1"></div>
<div id="prop2"></div>
<div id="prop3"></div>
<script type="text/babel">
{
const name ='章章';
const nameD ='东东';
function formatName(name,nameD) {
return name+nameD
};
const ele =(
<div style={{color:"#909399"}}>
<p style={{color:"#409EFF"}}>react基操:</p>
{name}是个好蛋
<p>{nameD}是个坏蛋</p>
<p>{formatName(name,nameD)}是好蛋</p>
<p>________________________________________________________________________________________________________________________________________________________</p>
</div>
)
ReactDOM.render(
ele,
document.getElementById('zz')
)
}
</script>
<script type="text/babel">
function timer() {
const ele = (
<div>
<p style={{color:"#409EFF"}}>react多次渲染:</p>
<p>您好</p>
<p>现在是北京时间{new Date().toLocaleString()}</p>
<p>在浏览器中f12检查源代码可以看到,DOM树时刻变化的只有时间。只有尽管每一秒我们都会新建一个描述整个DOM树的元素,React只会更新实际改变了的内容,也就是例子中的文本节点。</p>
<p>________________________________________________________________________________________________________________________________________________________</p>
</div>
)
ReactDOM.render(
ele,
document.getElementById('time')
)
};
timer()
setInterval(timer,1000)
/*在浏览器中f12检查源代码可以看到,DOM树时刻变化的只有时间。只有尽管每一秒我们都会新建一个描述整个 UI 树的元素,React DOM 只会更新实际改变了的内容,也就是例子中的文本节点。*/
</script>
<!-- javascript -->
<script type="text/babel">
const prop = {
name: '小可爱',
age: '18'
}
function propf(prop){
const ele = (
<div>
<p style={{color:"#409EFF"}}>react组件:</p>
<p>我是{prop.name}</p>
<p>今年{prop.age}</p>
<p>________________________________________________________________________________________________________________________________________________________</p>
</div>
)
return ele
}
const eleFather= propf(prop)
ReactDOM.render(
eleFather,
document.getElementById('prop1')
)
</script>
<script type="text/babel">
function Myprop(prop){
const ele = (
<div>
<p style={{color:"#409EFF"}}>react组件升级版:</p>
<p>我是{prop.name}</p>
<p>今年{prop.age}</p>
<p>________________________________________________________________________________________________________________________________________________________</p>
</div>
)
return ele
}
ReactDOM.render(
<Myprop name="小可爱" age="18"/>,
document.getElementById('prop2')
)
</script>
<script type="text/babel">
class Myprop3 extends React.Component{
prop = {
name: "小可爱",
age: "18"
}
ele = (
<div>
<p style={{color:"#409EFF"}}>react终极版:class 类</p>
<p>我是{this.prop.name}</p>
<p>今年{this.prop.age}</p>
<p>________________________________________________________________________________________________________________________________________________________</p>
</div>
)
render(){
return (ele)
}
}
ReactDOM.render(
Myprop3.ele,
document.getElementById('prop3')
)
</script>
</body>
</html>