react
核心概念
虚拟dom(virtual document object model)
- 本质:浏览器中的概念,用js对象来表示页面上的元素,并提供了操作DOM对象的API
- 什么是DOM和虚拟DOM:框架中的概念,用js对象来模拟页面上的DOM和DOM嵌套
- 实现目的:为了实现页面中,DOM元素的高效更新
- 跟dom的区别:
- dom:浏览器中提供的概念,用js对象表现页面上的元素,并提供了操作元素的api
- 虚拟dom:是框架中的概念,是程序员,手动用js对象来模拟dom元素和嵌套关系的
- 本质:用js对象来模拟dom元素和嵌套关系
- 目的:就是为了实现页面元素的高效更新
Diff算法
- tree diff :新旧两颗dom树,逐层对比的过程。当整颗dom逐层对比完毕,则所有需要被按需更新的元素,必然能被找到
- component diff:在进行tree diff的时候,每一层,组件级别的对比。
- 如果对比前后组件的类型相同,则暂时认为此组件不需要被更新
- 如果对比前后组件的类型不同,则需要移除旧组件,创建新组件并添加到页面元素上
- element diff:在进行组件对比的时候,如果两个组件类型相同免责需要进行元素级别的对比。
创建webpack
- 快速初始化项目
npm init -y
-
创建文件夹src存放源代码也叫源代码目录,dist存放打包之后的内容也叫产品目录
-
在src目录下创建index.html
-
使用cnpm(yarn貌似最好)安装webpack
cnpm -i webpack -D
- 全局运行
npm i cnpm -g
-
安装脚手架
cnpm i webpack-cli -D
-
根目录创建一个webpack.config.js 是一个向外暴露的一个打包配置对象,因为webpack是基于Node构建的,所以webpack支持所有node API 和语法
-
module.exports={mode:'development'}
这里的mode就是打包的规则,development就是开发模式,production就是压缩后了 -
在webpack中有个很大的特性,就是约定大于配置 约定,默认的打包入口是src——》inde.js ,打包的输出文件是dict——》main.js 也可以在`module.wxports中配置entry配置
-
目的是为了尽量减少配置文件的体积
-
cnpm i webpack-dev-server -D
实时打包- 在package.json中在script中+
“dev”:‘webpack-dev-server’
(在里面–open 自动帮你打开浏览器 --port 3000 改变端口 --host 127.0.0.1 指定域名) - npm run dev
- 打包好的main.js是托管到内存中,没有存储到项目目录上,但是你可以假装他有…
- 所以在你html那里直接引用src=’/main.js’就好了
- 在package.json中在script中+
-
cnpm i html-webpack-plugin -D
页面生成到内存中-
在webpack.config.js中配置,导入在内存中自动生成一个index页面插件
const path = require(‘path’)
const HtmlWebPackPlugin = require(‘html-webpack-plugin’)
-
创建一个插件实例对象(根据template(物理)生成filename(内存))
const HtmlPlugibn = new HtmlWebPackPlugin ({
template:path.join(__dirname,’./src/index.html’) ,//源文件
filename:’index.html‘
})
-
在module.exports中添加,这个时候就不用再index.html中导入main.js了
plugins:{HtmlPlugibn }
-
-
-
在项目中使用react
简单安装以及使用
-
运行
cnpm i react react-dom -S
安装包- react : 专门用于创建组件和虚拟dom的,同时组件的生命周期都在这个包中
- react-dom : 专门进行dom操作的,
-
在index。js 中 这两个必须这么写
import React from ‘react’
import ReactDOM from ‘react-dom’
-
创建虚拟dom元素
-
参数1:创建的元素类型,字符串,表示元素的名称
-
参数2:是一个对象或null 表示当前这个dom元素的属性
-
参数3:子节点(包括其他虚拟dom 获取文本子节点)
-
参数n:其他子节点
例如:
这是一个大大的h1
const myh1 = React.createElement(‘h1’,null,‘这是一个大大的h1’)
or
const myh1 = React.createElement(‘h1’,{id:‘myh1’ ,title:‘this is a h1’},‘这是一个大大的h1’)
-
-
使用ReactDOM吧虚拟DOM渲染到页面上
- 参数1:要渲染的那个虚拟DOM元素
- 参数2 :指定页面上 的dom元素当做容器,在html中创建
- 一个div id为app的标签
ReactDOM.render(myh1,document.getElementById(’app‘))
-
嵌套
-
麻烦…
const mydiv = React.createElement(‘div’,null,‘这是一个div’,myh1)
-
简单
html是最优秀的标记语言,但是js文件中不能写html语言,否则打包会失败
为了解决这个问题可以使用babel来转换
在js中混合写入类似于html的语法叫做jsx语法;符合xml规范的js
jsx语法的本质还是在运行的时候被转换成
React.createElement
形式-
如何启用jsx语法
-
安装
babel
插件cnpm i babel-core babel-loader babel-pluginb-transform-runtime -D
cnpm i babel-preset-env babel-preset-stage-0 -D
-
安装能够识别转换jsx语法的包
babel-preset-react
cnpm mi babel-preset-react -D
-
在webpack.config.js中
module.exports
里进行配置webpack 默认只能打包.js后缀名类型的文件,想.png.vue无法主动处理,所以主动配置第三方module规则
其中use是使用 当多规则时使用数组
exclude是排除
module:{rules:[{test:/.js|jsx$/,use:‘babel-loder’,exclude:/node_moduless/}第三方匹配规则]所有第三方模块的配置规则}
-
在根目录中创建文件.babelrc,是个json 不能单引号,不能注释
- preset:所有的语法
- plugins:所有的插件
{
“preset”:["env ","stage-0“,“react”],
“plugins”:[“transform-runtime”]
}
-
const mydiv =
这是一个div -
jsx语法的本质
并不是直接把jsx渲染到页面上,而是每部先转成了 React.createElement
形式,再渲染
在jsx中混合使用js
在jsx语法中要把js代码写到{}中
-
渲染数字,字符串,
在js中导入变量要放置花括号
const a = 10
ReactDOM.render(
{a},document.getElementById(’app‘))or
ReactDOM.render(
{a+2},document.getElementById(’app‘)) -
布尔值
const boo = true
ReactDOM.render(
{boo },document.getElementById(’app‘)) 这样是不会显示的要{boo.toString()}也可以三元运算
{boo ? ‘条件为真’ :’条件为假‘}
-
为属性绑定值
可以在标签内直接写属性,但是过于麻烦
const title = ’666‘
ReactDOM.render(
-
jsx元素
const h1 =
红红火火恍恍惚惚
ReactDOM.render(
{h1},document.getElementById(’app‘)) -
jsx元素数组
const arr = [
<h2>这是h2</h2>
,<h1>这是h1</h1>
]
ReactDOM.render(
{arr },document.getElementById(’app‘)) -
将普通字符串数组转为jsx数组并渲染到页面上【planA,B】
- planA:定义空数组,for循环
const arrstr = [‘1’,‘2’,‘3’,‘4’]
const nameArr = []
arrstr.forEach(item => {
const temp = <h5>{item}</h5>
nameArr.push(temp )
})
-
planB
数组的map方法,对数组中的每一项只做一个指定的操作并且把指定的操作的结果当做新数组返回,map中必须要写return
const arrstr = [‘1’,‘2’,‘3’,‘4’]
ReactDOM.render(
{arrstr .map(item =>{return
{item}
})},document.getElementById(’app‘))
or 当你箭头函数的只有一行可去掉{}
ReactDOM.render(
{arrstr .map(item =>return
{item}
)},document.getElementById(’app‘))
-
react中key的作用
为了保证你数据状态的准确性,所以要在你数据中加上一个key来保证唯一状态
react中,需要把key添加给被 forEach或map或for循环直接控制的元素
避免出现Each child in an array or iterator should have a unique ‘key’ prop 这个错误
const arrstr = [‘1’,‘2’,‘3’,‘4’]
ReactDOM.render(
{arrstr .map(item =>{return
{item}
})},document.getElementById(’app‘))
-
注释
{/* */}
-
为jsx中的元素添加class类名
需要使用className 来代替class ,label中的for 用htmlfor来代替
-
在jsx创建dom的时候,所有节点必须有唯一的根元素进行包裹
在react 创建组件
第一种
组件中的props是只读,不能重新赋值
适用于赋值少,但多的时候呢 0.0
function Hello(props){
//如果在组件中return一个null 则表示此组件是空,什么也不会渲染
//在组件中必须返回一个合法发jsx虚拟DOM元素
return
这是一个hello组件–{props.name}}
const dog={
name:‘大黄’,age:3,gander:;‘雄‘
}
ReactDOM.render(
,document.getElementById(’app‘))
第二种
这{。。。dog}这三个点是es6有的展开运算符
function Hello(props){
//如果在组件中return一个null 则表示此组件是空,什么也不会渲染
//在组件中必须返回一个合法发jsx虚拟DOM元素
return
这是一个hello组件–{props.name}}
const dog={
name:‘大黄’,age:3,gander:;‘雄‘
}
ReactDOM.render(
<Hello {…dog}>,document.getElementById(’app‘))
第三种
用class关键字来创建组件,es6中class关键字,是面向对象编程新形式
constructor构造器,每一个人类中都有一个构造器,没指定就是看不见的空,其作用就是每当new这个类的时候,会优先执行构造器的代码
class Animal{
constructor(name,age){
this.name = name
this.age = age
}
}
const a1 = new Animal(‘大黄’,3)
静态属性
通过构造函数直接访问到的属性叫静态属性,在class中用static修饰的都是静态属性
class Animal{
constructor(name,age){
this.name = name
this.age = age
}
static info = ‘aaaa’
}
const a1 = new Animal(‘大黄’,3)
console.log(a1.info) --------->undefind
console.log(Animal.info) --------->aaaa
实例方法和静态方法
class Animal{
constructor(name,age){
this.name = name
this.age = age
}
static info = ‘aaaa’
jiao(){console.log(‘这是一个实例方法’)} //可以通过new出来的对象访问
static show(){console.log(‘这是一个静态方法’)}
}
class注意点
- 在class{}区间内,只能写构造器,静态方法,静态属性,实例方法
- 内部还是用原来的配方实现的,也把这个关键字称作语法糖
class继承,访问父类上的实例
在class类中可以使用他extends关键字,实现子类继承父类
语法:class 子类名 extends 父类名
当你想在子类中构建构造器,你要调研super()
- 如果一个子类通过extends 关键字继承了父类,name在子类的constructor中必须优先调用下super()
- super是一个函数,他是父类的构造器,是父类构造器的引用
class person{ //父类(原型对象)
constructor(name,age){
this.name = name
this.age = age
}
}
class American extends person{
constructor(name,age,idcard){super(name,age)
this.idcard = idcard
}
}
const a1 = new American(‘jack’,18)
创建组件
- render 的作用:是渲染当前组件所对应的虚拟DOM元素
- 这里的Movie标签就是Movie类的实例对象
- 传参的时候不需要接收直接{this.props.xxx}即可
- 用class接收的传参虽然没有read only 但是依旧是不可写的
class Movie extends React.Compomemt{
render (){
return
这是movie组件}}
ReactDOM.render()
对比
-
名字都是当做标签来使用
-
传参的时候function需要导入,但是class不需要直接this.就好
-
数据
- class创建的组件有自己的私有数据(构建器中this.state{msg:‘hhh’}可读可改),生命周期,
- function创建的组件只能有props,没有自己的数据和生命周期
-
名字???
本质区别就是有无state属性和有无生命周期
- class创建的组件叫有状态组件
- 用构造函数创建出来的组件的叫无状态组件
-
使用
- 需要私有数据 class
- 不需要 function
- 无状态组件由于没有自己的生命周期和state,所以效率会比有状态组件高,但是还是很少用
组件中props和state/data之间的区别
- 来源
- props中的数据都是外界传递过来的
- state/data中的数据都是组件私有的(通过ajax获取回来的数据,一般都是私有的)
- 读写
- props 中的数据都是只读的;不能重新赋值
- state/data中的数据
组件单独存放
组件跟js文件放在一起会发掘很乱,所以准备将组件抽离,然后单独存放,但是要注意你存放的位置还是在src文件夹内。你可以直接在src文件夹中生成。jsx文件,也可以在src中新建一个文件夹来存放你的组件
export default 意思就是创建并导出的意思(将这个组件暴露出去 )
import React from ‘react’
export default function Hello(props){
//如果在组件中return一个null 则表示此组件是空,什么也不会渲染
//在组件中必须返回一个合法发jsx虚拟DOM元素
return
这是一个hello组件–{props.name}}
然后在你index.js中导入你的组件
这个@表示src这一层目录
但是@也需要在webpack.config.js配置,详情看下面的的alias
import Hello from ‘./文件名/Hello.jsx’
or
import Hello from ‘@/文件名/Hello.jsx’
配置webpack.config.js文件来省略后缀名
在你的module.exports中添加
resolve:{
extensions:[’.js’,’.jsx’,’.json’], //表示,这几个文件后缀名可以不写,从先到后查找 ,默认js和json
alias:{ //表示别名
‘@’:path.join(__dirname,’./src’)}
}
练手
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kzbCxcqM-1586585308182)(C:\Users\a20044\Desktop\组件.png)]
一个简单的列表for循环出来的评论列表
-
数据:
commentList:[
{id:1,user:’张三‘,content:‘呵呵,我是张三’},
{id:2,user:’李四‘,content:‘呵呵,我是李四’},
{id:3,user:’王五‘,content:‘呵呵,我是王五’}
]
-
开始
import React from ‘react’
import ReactDOM from ‘react-dom’
function CmItem(props){
return
<h1>评论人:{props.user}</h1>
<p>评论内容:{props.content}</p></div>
class CmList extend React.Component{
constructor(){
super()
this.state = {
commentList:[
{id:1,user:’张三‘,content:‘呵呵,我是张三’},
{id:2,user:’李四‘,content:‘呵呵,我是李四’},
{id:3,user:’王五‘,content:‘呵呵,我是王五’}
]
}
}
render(){
return
<h1>这是评论列表组件</h1>
{this.state.commentList.map(item=><CmItem {…item} key={item.id}>)}
}
}
ReactDOM.render(
),document.getElementById(’app‘))
在jsx中书写样式
- js
style = {{color:‘red’}}
在行内样式中如果是数值类型的才可以不用’‘包裹
你也可以给样式抽出来
const itemStyle={a= {border:‘1px’},b={color:‘red’}}
<div style={itemStyle.a}></div>
当你把这些样式抽离出去的时候记得export default
暴露出去
-
css
你可以在src文件夹下创建一个css文件夹
-
下载插件
cnpm i style-loader css-loader -D
-
在你的webpack.config.js中在module rules里添加新规则
{test:/\.css$,use:['style-loader' ,'css-loader']},
-
创建一个css文件
.title{color:red;}
-
在你的jsx中导入
-
第一种模块化
直接导入css导入会对整个项目生效
为了避免这个问题故:
在你的webpack.config.js中在module rules里修改之前添加的规则
大概意思就是为css样式表启用模块化,通过?号传参
{test:/\.css$,use:['style-loader' ,'css-loader?modules']},
css模块化只针对类名和id选择器生效,不会将标签选择器模块化
import cssobj from ‘@/css/xxx.css’
…(省略)
render(){
return
<h1 className={cssobj .title}>这是评论}
-
第二种 进阶
localIdentName的五个参数分别代表
- path:表示样式表
相对于项目根目录
所在的路径 - name:表示样式表文件名称
- local:表示样式的类名定义名称
- hash:length:表示hash值后面是截取的长度默认32
{test:/\.css$,use:['style-loader' ,'css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]']},
- path:表示样式表
-
拓展
当你不想让某css模块化【:global()】
当你想让某css模块化【:local()】默认
:global(.test){
font-style:italic
}
在你拼接的时候可以
import cssobj from ‘@/css/xxx.css’
…(省略)
render(){
return
<h1 className={cssobj .title+’ test‘}>这是评论}
or
return
<h1 className={[cssobj .title,’ test‘].join(’ ‘’)}>这是评论 -
第三方css
安装bootstrap
cnpm i bootstrap@3.3.7 -S
在你的webpack.config.js中在module rules里添加规则,打包处理字体文件
cnpm i url-loader -D
cnpm i file-loader -D
{test:/\.ttf|woff|woff2|eot|svg$,use:'url-loader'},
在你的jsx中导包
// 若在引用某个包的时候这个包被安装在node_modiles时,可省略这层目录,可以直接从包名开始
import bootcss from ‘bootstrap/dist/css/bootstrap.css’
import cssobj from ‘@/css/xxx.css’
…(省略)
render(){
return
<button className={[bootcss.btn,bootcss[‘btn-primary’]].join(’ ')}>第三方会跟自己的css发生冲突所以我们不要把。css的文件模块化
自己的css文件后缀更改为。scss或。less,然后只对。scss或。less启用模块化
so:
cnpm i sass-loader node-sass -D
更改你的css因为他不需要模块化了
{test:/\.css$,use:['style-loader','css-loader'},
在你的webpack.config.js中在module rules里添加规则,打包处理字体文件
{test:/\.scss$,use:['style-loader','css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]','sass-loader'},
之后你的代码就可以
…略
<button className="btn btn-primary"></button>
-
-
绑定事件
在react中有一套自己的事件绑定机制 事件名是小驼峰
格式如下:
<button onClick={function(){}}></button>
export default class BindEvent extends React.Component{
constructor(){
super()
this.state= {}
}
render(){
return
<button onClick={function(){}}></button>
or
return
<button onClick={this.myclickHandler}></button>
}
myclickHandler(){
console.log(‘6666’)
}
}
标准
onClick只接受function作为处理函数
需求点击按钮修改msg
-
在react中想要为state修改数据,重新赋值,不要使用
this.state.***=值
-
应该调用react提供的
this.setState({msg:值})
-
注意:
- 可以只更改一个值,其他值不变
- 异步执行,如果在调用完
this.setState
之后想要最新的值需要this.setState({msg:值},function(){})
-
注意:如果你直接给input的值绑定了state,但是不提供onChange处理函数的话得到的是一个只读文本框
-
此时会报错你可以提供一个readOnly函数解除报警
<input type='text' style={{width:100%}} value={this.state.msg} readOnly>
-
或者提供一个onChange
-
<input type='text' style={{width:100%}} value={this.state.msg} onchange={(e)=>this.txtChanged(e)} ref='txt'>
…略
txtChanged=() => {
第一步:取值——》方案一
通过事件参数e来获取
console.log(e.target.value)
——————》方案二
console.log(this.refs.txt.value)
第二步 同步
const newVal = e.target.value
this.setState(msg:newVal )
}
-
默认情况下,在react中,如果页面上的表单元素,绑定了state上的状态,呢么每当state上状态值变化,必然会吧最新的状态值,自动同步到页面上
-
状态变化——》自动更新页面
(单向数据流)
-
如果ui界面上,文本框的内容变化了,想要把变化的值同步回state中去,
- 此时需要自己手动监听文本框的onChange事件
- 在onChange事件中,拿到最新的文本框的值
- 程序员调用this.setState({})手动把最新的值同步到state中去
-
-
-
…略
this.state={
msg=‘123’}
…略
<button onClick={()=>{this.myclickHandler(‘hehe’)}}>
…略
show=(arg1)=>{
this.setState({msg:789})
console.log(‘666’+arg1)}
小技巧:
折叠注释的代码段
//#region
注释的代码
//#endregion
生命周期
概念:每个组件的书库从创建到到运行,直到销毁,在这个过程中会触发一系列的事件,这个啥时间就叫做这个组件的生命周期
react组件分三部分
-
组件创建阶段:特点:一辈子只执行一次
componentWillMount
render
componentDidMount
-
组件创建阶段:按需,根据props属性或state状态改变,有选择行的执行0次或n次
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
-
组件销毁阶段:一辈子只执行一次
componentWillUnmont
回state中去,
- 此时需要自己手动监听文本框的onChange事件
- 在onChange事件中,拿到最新的文本框的值
- 程序员调用this.setState({})手动把最新的值同步到state中去
…略
this.state={
msg=‘123’}
…略
<button onClick={()=>{this.myclickHandler(‘hehe’)}}>
…略
show=(arg1)=>{
this.setState({msg:789})
console.log(‘666’+arg1)}
小技巧:
折叠注释的代码段
//#region
注释的代码
//#endregion
生命周期
概念:每个组件的书库从创建到到运行,直到销毁,在这个过程中会触发一系列的事件,这个啥时间就叫做这个组件的生命周期
react组件分三部分
-
组件创建阶段:特点:一辈子只执行一次
componentWillMount
render
componentDidMount
-
组件创建阶段:按需,根据props属性或state状态改变,有选择行的执行0次或n次
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
-
组件销毁阶段:一辈子只执行一次
componentWillUnmont