react基础语法

1.react与vue的对比

1.1什么是模块化

是从代码的角度进行分析的,把一些可复用的代码,抽离为单个的模块,便于项目的维护和开发。

1.2什么是组件化

是从UI界面的角度来进行分析的,把一些可复用的UI元素,抽离为单独的组件,便于项目的维护和开发。

好处:随着项目规模的增大,手里的组件越来越多,很方便就能把现有的组件,拼接成一个完整的项目

1.3Vue如何实现组件化

通过 .vue 文件,来创建对应的组件,还可以通过Vue.component()   Vue.extends() 创建组件

  • template 结构
  • script 行为
  • style  样式

1.4react如何实现组件化

react中有组件化的概念,但并没有像vue这样的模板文件,react中,一切都是以 js 来表现的

2.react中几个核心的概念

2.1虚拟dom

2.1.1dom的本质

浏览器中的概念,用js对象来表示页面上的元素,并提供了操作dom对象的api

2.1.2react中的虚拟dom

用js对象来模拟页面上的dom和dom嵌套,目的是为了实现页面元素的高效更新

2.1.3为什么要实现虚拟dom

为了实现页面中,dom元素高效更新

2.1.4dom和虚拟dom的区别

dom:浏览器中提供的概念,用js对象,表示页面上的元素,并提供操作元素的api

虚拟dom:是框架中的概念,而是开发框架的程序员,手动用js对象来模拟dom元素和嵌套关系

2.2dom树

<div id="mydiv" title="嘻嘻">今天天气真好</div>
var div={
            tagName:'div',
            attrs:{
                id:'mydiv',
                titie:'嘻嘻'
            },
            childrens:[
                '今天天气真好'
            ]
        }

2.3diff算法

  • tree diff:新旧两颗dom树,逐层对比的过程,就是tree diff,当整颗dom树逐层对比完毕,则所有需要按需更新的元素必然能够找到;
  • component diff:在进行tree diff的时候,每一层中,组件级别的对比,叫做component diff,如果对比前后,组件的类型相同,则暂时认为此组件不需要更新,如果对比前后组件的类型不同,则需要移除旧组件,创建新组件,并追加到页面上;
  • element diff:在进行组件对比的时候,如果两个组件类型相同,则需要进行元素级别的对比,这叫做element diff;

 3.搭建webpack4.x项目

创建一个文件夹,用vs code打开

1. 运行  npm init -y 快速初始化项目

2.在项目根目录创建src源代码目录和dist产品目录

3.在src目录下创建index.html 和index.js

4.使用npm安装webpack,运行 npm i webpack -D

5.使用npm安装webpack-cli,运行 npm i webpack-cli -D

6.在package.json 修改如下配置,指定dve 为webpack

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev" : "webpack"
  },

7.在根目录下创建 webpack.config.js 文件,添加如下内容

//向外暴露一个打包的配置对象
module.exports={
    mode: 'development' //production二选一
}

8.通过命令 npm run dev 来启动项目

注意:webpack 4.x 提供了约定大于配置的概念,目的是为了尽量减少配置文件的体积,默认约定了打包的入口src->index.js 打包的输出文件dist->main.js

9.配置项目自动更新打包

运行 命名 npm i webpack-dev-server -D 当项目修改后会进行自动打包,并修改配置文件package.json

  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev" : "webpack-dev-server"
  },

注意:此时运行npm run dev 会报出如下错误,原因是webpack-cli版本太高

Error Cannot find module 'webpack-cli/bin/config-yargs' 

解决办法:(1)npm uninstall webpack-cli 卸载当前的webpack-cli

(2)重新安装 webpack-cli 3.* 版本npm install webpack-cli@3 -D

10.在index.html中加入如下内容

    <script src="/main.js"></script> <!--实际在内存中生产-->

11.将index页面导入内存中

使用命令 npm i html-webpack-plugin -D 安装插件

在webpack.config.js配置插件

const path=require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')

//创建一个插件的实例对象
const htmlPlugin = new HtmlWebpackPlugin({
    template:path.join(__dirname,'./src/index.html'),//__dirname代表根目录,拼接源文件路径
    filename:'index.html'
})

//向外暴露一个打包的配置对象
module.exports={
    mode: 'development', //production二选一
    plugins:[
        htmlPlugin
    ]
}

12.重新npm run dev 就可以实现实时编译

4.在项目中使用react

4.1安装react

运行 npm i react react-dom -S 安装包

  • react:专门用于创建组件和虚拟dom的,同时组件的生命周期都在这个包中
  • react-dom:专门进行dom操作的,最主要的应用场景就是ReactDOM.render()

4.2初步使用react

在index.js 中配置虚拟dom

//这两个包名接收的成员名必须这么写
import React from 'react'  //创建组件、虚拟dom元素,生命周期
import ReactDOM from 'react-dom'  //把创建好的组件和虚拟dom 放到页面上展示

//创建虚拟dom元素
//参数1:创建的元素的类型,字符串,表示元素的名称
//参数2:是一个对象或null,表示 当前这个DOM 元素的属性
//参数3:子节点(包括其他虚拟DOM 获取文本子节点)
//参数n:其他子节点
//<div id="weather" title="today">今天天气真好</div>
const hh=React.createElement('div',{id:'weather',title:'today'},"今天天气真好")
//使用ReactDOM把虚拟dom渲染到页面上
//参数1:要渲染的那个虚拟dom元素
//参数2:指定页面上的一个容器的dom元素
ReactDOM.render(hh,document.getElementById("app"))

在index.html中指定容器

    <div id="app"></div>

然后访问页面就可以实现虚拟dom的渲染

使用React.createElement实现虚拟DOM嵌套

const hh=React.createElement('div',{id:'weather',title:'today'},"今天天气真好")
const div=React.createElement('div',null,'这是一个div',hh)
ReactDOM.render(div,document.getElementById("app"))

5.JSX

5.1什么是JSX

在js中,混合写入类似于HTML的代码,叫做JSX语法,符合xml规范的js,JSX 语法上更接近 JavaScript 而不是 HTML,例如:

const mydiv=<div id="mydiv" title="div aaa">这是一个div元素</div>

注意:JSX语法的本质,还是在运行的时候,被转换成了React.createElement形式来执行的

5.2如何启用JSX语法

使用jsx语法前首先要安装babel插件

  1. 运行 npm i babel-core babel-loader@7.1.5 babel-plugin-transform-runtime -D
  2. 运行 npm i babel-preset-env babel-preset-stage-0 babel-preset-react -D

在webpack.config.js配置如下内容

    module: { //所有第三方 模块的配置规则
        rules: [ //第三方匹配规则
            { test: /\.js|jsx$/, use: 'babel-loader', exclude: /node_modules/ }
        ]

在根目录下创建一个.babelrc文件,加入如下配置

{
    "presets": [
        "env",
        "stage-0",
        "react"
    ],
    "plugins": [
        "transform-runtime"
    ]
}

 5.3JSX语法

渲染常量

let a=10
ReactDOM.render(<div>{a}</div>,document.getElementById("app"))

三元表达式

let boo=false
ReactDOM.render(<div>{boo?'条件为真':'条件为假'}</div>,document.getElementById("app"))

绑定属性

let title="999"
ReactDOM.render(<div>
    <p title={title}>这是p标签</p>
    </div>,document.getElementById("app"))

渲染HTML元素

const mydiv=<div id="mydiv" title="div aaa">这是一个div元素qqq</div>

ReactDOM.render(<div>
    <p title={title}>这是p标签</p>
    <hr/>
    {mydiv}
    </div>,document.getElementById("app"))

渲染数组元素

const arr=[
    <h2>这是h2</h2>,
    <h3>这是h3</h3>
]
ReactDOM.render(<div>
    <p title={title}>这是p标签</p>
    <hr/>
    {arr}
    </div>,document.getElementById("app"))

 外部数组遍历渲染forEach

const arrStr=['灰太狼','喜洋洋','美羊羊','懒羊羊','沸羊羊']
const nameArr=[]
arrStr.forEach(item=>{
    const temp=<h5>{item}</h5>
    nameArr.push(temp)
})
ReactDOM.render(<div>
    {nameArr}
</div>,document.getElementById("app"))

内部数组遍历渲染map

const arrStr=['灰太狼','喜洋洋','美羊羊','懒羊羊','沸羊羊']
ReactDOM.render(<div>{arrStr.map(item=><h3 key={item}>{item}</h3>)}</div>,document.getElementById("app"))

给数组绑定id:key={item}

 JSX内部的注释:JSX内部的注释分为单行注释和多行注释两种,具体语法如下,推荐使用单行注释

     {/*我是单行注释 */}
{
//我是多行注释 //我还可以写很多 }

在注释中可以使用//#region//#endregion来折叠注释 

为jsx中的元素添加class类名,需要用className来代替class,用 ‘htmlFor’ 替代 label 的 for 属性

6.react组件

6.1创建组件

6.1.1 使用function关键字创建

在组件中必须返回一个合法的JSX虚拟的DOM元素,组件的首字母必须大写

function Hello(){
    return <div>这是H1</div>;
}

为组件传递数据

const dog={ 
    name:"大黄",
    age:2,
    gender:"男"
}
function Hello(props){ 
    return <div>这是Hello组件----{props.name}----{props.age}---{props.gender}</div>;
}
 <Hello name={dog.name} age={dog.age} gender={dog.gender}></Hello>   //使用组件并为组件传递props数据

其中组件的使用还可以使用展开运算符:

<Hello {...dog}></Hello>

6.1.2 使用class关键字创建

最基本的组件结构

class 组件名称 extends React.Component{
     render(){
       return <div>这是class创建的组件</div>    
  }            
}

在组件内部,必须有render函数,必须返回合法的JSX虚拟dom结构,render函数的作用:是渲染当前组件所对应的虚拟dom元素

6.1.3 两种创建组件方式对比

注意:使用class创建的组件,有自己的私有数据和生命周期函数

注意:使用function创建的组件,只有props,没有自己的私有数据和生命周期函数

  • 1.用构造函数创建的组件叫做“无状态组件”
  • 2.用class关键字创建的组件叫做“有状态组件”
  • 3.什么情况下使用有状态组件?什么情况下使用无状态组件?

有状态组件和无状态组件的本质区别就是:有无state属性,这里的state相当于vue中的data

 

6.2 抽离组件

在项目根目录下创建一个包components,然后创建一个文件 Hello.jsx ,然后将组件注册写在这个文件里面

 

import React from 'react'

export default function Hello(props){
    return <div>这是Hello组件----{props.name}----{props.age}---{props.gender}</div>;
}

 

使用抽离组件,在使用组件之前我们要先进行注册组件

import  Hello from "./components/Hello.jsx";

注意,这里的jsx后缀名必须要写,不然会报错,但是我们可以通过配置来不写后缀名,可以通过@来配置绝对路径

在webpack.config.js里配置如下内容

    resolve:{
        extensions:['.js','.jsx','.json'],  //表示这几个文件的后缀名可以不写
        alias: {
            '@': path.join(__dirname,'./src')    //这样@符合代表src的根目录
        }
    }

6.3 组件属性props

在class关键字创建的组件中,如果想使用props参数,不需要接收,直接通过this.props.***访问即可

class Movie extends React.Component{
    render(){
        return <div>hhh+{this.props.name}----{this.props.age}</div>
    }
}

组件传递如下

<Movie name={dog.name} age={dog.age}></Movie>

组件中的 props 和 state/data 之间的区别

  1. props中的数据都是外界传递过来的
  2. state/data中的数据,都是组件私有的,(通过Ajax获取回来的值,一般都是私有数据)
  3. props中的数据都是只读的,不能重新赋值
  4. state/data中的数据,都是可读可写的

6.4 for循环生成多组件

function CmtItem(props){
    return <div>
        <h1>评论人:{props.user}</h1>
        <p>评论内容:{props.content}</p>
    </div>
}

class CmtList extends React.Component {
    constructor() {
        super()
        this.state = {
            CommentList:[
                {id:1,user:'张三',content:'哈哈 沙发'},
                {id:2,user:'李四',content:'哈哈 板凳'},
                {id:3,user:'王五',content:'哈哈 凉席'},
                {id:4,user:'赵六',content:'哈哈 砖头'},
                {id:5,user:'田七',content:'哈哈 最后'},
            ]            
        }
    }
    render() {
        return <div>
            <h1>这是评论列表组件</h1>
            {this.state.CommentList.map(item=><CmtItem {...item} key={item.id}></CmtItem>)}
        </div>
    }
}
ReactDOM.render(<div>
    <CmtList></CmtList>
</div>, document.getElementById("app"))

6.5 style的使用

6.5.1 行内使用

在使用style标签时,要用双大括号

注意:在行内样式中,如果是数值类型的样式,则可以不用引号包裹,则可以不用引号包裹,如果是字符串类型的样式值,必须使用引号包裹。

style={{color:'red',fontSize:'16px'}}

6.5.2 样式抽离

const styles={
    item:{ border:'1px',margin:'10px'},
    content:{ fontSize:'12px'}
}

引入为 style={styles.item}

6.5.3 引入外部css样式 

1.首先项目要安装样式loader

运行命令 npm i style-loader css-loader -D

2.配置打包处理css样式的第三方loader

在webpack.config.js的module-rules中加入如下配置

       { test: /\.css$/, use: ['style-loader','css-loader'] }

3.在src目录下新建一个文件夹css,在新建一个css样式文件cmtlist.css,在css中加入代码如下:

.title{
    color: red;
}

4.在页面引入并使用css样式

首先要导入外部样式,然后标签通过指定显示,具体代码如下

//导入样式
import cssObj from '@/css/cmtlist.css'

<h1 className='title'>这是评论列表组件</h1>

注意:可以通过通过在css-loader之后,通过?追加参数,这里有个固定的参数modules,表示为css样式启用模块化

            { test: /\.css$/, use: ['style-loader','css-loader?modules']} //打包处理css样式的第三方loader

启动模块化样式之后具体引入外部样式为:

<h1 className={cssObj.title}>这是评论列表组件</h1>

注意:css模块化只针对类选择器和id选择器生效

6.5.4 安装外部样式bootstrap

首先安装bootstrap

npm i bootstrap@3.3 -S

然后安装bootstrap所需字体的loader

npm i url-loader -D

在webpack.config.js配置打包字体文件loader

{ test: /\.ttf|woff|woff2|eot|svg$/, use: 'url-loader' } //打包处理字体文件loader

 最后在页面导入包(注意:在node_modules目录下的包,直接以包名开始引入自己的模块或样式表就可以了

import bootcss from 'bootstrap/dist/css/bootstrap.css'

在标签中使用

<button className={[bootcss.btn,bootcss['btn-primary']].join(" ")}>按钮</button>

6.5.5 项目启用模块化

规定:第三方样式表都是以.css结尾的,自己的样式表都要以.scss和.less结尾

我们只需要为自己的.scss文件,启用模块化即可

运行下面命令来安装解析scss文件的loader

npm i sass-loader node-sass -D

找到module里的css配置部分,修改正则匹配为:/\.(css|scss)$/,在use数组里添加sass-loader

{ test: /\.scss$/, use: ['style-loader', 'css-loader?modules','sass-loader'] },

修改css样式表不被模块化

{ test: /\.css$/, use: ['style-loader', 'css-loader'] }, 

然后外部样式引入和使用可直接安装如下写法:

import 'bootstrap/dist/css/bootstrap.css'

<button className="btn btn-primary">按钮</button>

这样就大大增加了开发效率

7.react绑定事件

1)react事件的名称都是由React提供的,因此名称命名必须按照react的规范,如:onClick、onMouseOver

2)为事件提供的处理函数,格式如下:

onClick={ function }

使用案例:(注意:这种情况无法传参,不建议使用)

    render(){
        return <div>
            BindEvent组件
            <button onClick={this.myClickHandler}>按钮</button>
        </div>
    }
    myClickHandler(){
        alert(1)
    }

另外一种写法,使用箭头函数:(推荐使用)

    render(){
        return <div>
            BindEvent组件
            <button onClick={()=>this.myClickHandler(this.state.a)}>按钮</button>
        </div>
    }
    myClickHandler=(i)=>{
        alert(i)
    }

8.修改属性

在React中,推荐使用this.setState({ }),修改状态值

    myClickHandler = (i) => {
        this.setState({
            msg:"111"+i
        })
    }

注意:

(1)使用setState方法时只会把对应的state状态更新,其他的state状态属性值不变

(2)this.setState方法的执行是异步的

如果调用完this.setState之后,又想立即拿到最新的state值,需要使用this.setState({ },callback)

9.绑定属性

react中没有双向绑定,需要程序员手动进行绑定

首先创建一个input框,值设定为state值

<input type='text' onChange={(e)=>this.txtChanged(e)} style={{ width: '100%' }} value={this.state.msg} />
实现双向绑定onChange事件
获取值的两种方法:

1.onChange事件传递一个对象e,通过e.target.value来获取
    txtChanged=(e)=>{
        console.log(e.target.value)
    }

2.通过refs来获取,this.refs.txt.value

<input type='text' onChange={()=>this.txtChanged()} ref='txt' style={{ width: '100%' }} value={this.state.msg} />
    txtChanged=()=>{
        console.log(this.refs.txt.value)
    }

3.同步数据

然后将获取最新的值赋值给state,就可以实现数据的双向绑定了

    txtChanged=()=>{
        const newVal = this.refs.txt.value
        this.setState({
            msg:newVal
        })
    }

10.组件的生命周期

10.1 组件创建阶段

特点:一辈子只执行一次

1)componentWillMount

componentWillMount()一般用的比较少,它更多的是在服务端渲染时使用。它代表的过程是组件已经经历了constructor()初始化数据后,但是还未渲染DOM时。

2)componentDidMount

组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染

10.2 组件运行阶段

特点:根据props属性或者state状态的改变,有选择性的执行0到多次

1)shouldComponentUpdate(nextProps,nextState)

  • 主要用于性能优化(部分更新)
  • 唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新
  • 因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断

2)componentWillUpdate

shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState。

3)render

render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染。

4)componentDidUpdate

组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state。

5)componentWillReceiveProps(nextProps)

  • 在接受父组件改变后的props需要重新渲染组件时用到的比较多
  • 接受一个参数nextProps
  • 通过对比nextProps和this.props,将nextProps的state为当前组件的state,从而重新渲染组件

10.3 组件销毁阶段

特点:一辈子只执行一次

1)componentWillUnmount

在此处完成组件的卸载和数据的销毁。

  1. clear你在组建中所有的setTimeout,setInterval
  2. 移除所有组建中的监听 removeEventListener

 

posted @ 2021-05-13 17:41  本兮嘻嘻  阅读(542)  评论(0)    收藏  举报