学习React的N天

一. 虚拟DOM树(是以JS对象的形式存在的,用js对象模拟dom元素)

目的:就是为了实现dom的高效更新
在这里插入图片描述

二. diff算法(diff:diffentent)

在这里插入图片描述

三. 构造函数 与 Class 关键字

移步构造函数与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
  1. 安装插件 :cnpm style-loader css-loader -D,先写style-loader 比较好,因为处理css文件有先后顺序。
  2. 在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:

  1. 装插件
    在这里插入图片描述

  2. 配置webpack文件
    在这里插入图片描述

  3. 在根目录下添加一个叫做.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>
        )
    }
}

背景:

  1. 在父组件中命名className='title',但不写样式
  2. 在子组件中MyClassName/index.jsx,同样命名一个样式className='title'
  3. 在子组件中,引入了一个样式文件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来区分普通样式表和第三方样式表。

配置方式:

  1. .csswebpack模块化配置去掉。

    {
       test: /\.css$/,
       use: [
         "style-loader",
         /*"css-loader?modules&localIdentName=[path][name]_[local]-[hash:5]",*/
         "css-loader"
       ],
     },
    
  2. 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绑定事件注意点
  1. 事件的名称必须是驼峰写法:onClick、onMouseOver;
  2. 绑定的处理函数里必须是一个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)

注意点:

  1. this.setState({ })并不会直接覆盖this.state,只会把对应的this.state中的状态更新。
  2. this.setState({ })方法执行是异步的。无法立即拿到最新的state值。所以调用this.setState({ })之后直接打印目标属性值会出现没有改变的现象;再一次打印才会有改变的现象。
  3. 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是单向文本流,不能双向绑定,只能手动设置双向修改。

修改状态——>改变文本框,单向
  1. input 通过value绑定{this.state.name}值。
  2. 改变{this.state.name}来改变 input的value值。
  3. 键入无效。

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)
        })
    }
    
}

点击前:
在这里插入图片描述
点击后:
在这里插入图片描述

同步:修改状态<——>键入文本框,手动双向
  1. input 通过value绑定{this.state.name}值。 修改状态——>键入文本框。
  2. 提供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>

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值