这是一个 React 的基础视频的学习笔记,了解前端三大框架、React中的虚拟DOM概念、Webpack相关知识、JSX语法知识、React组件、Class类、React样式和React的生命周期等知识;
React基础学习笔记
黑马程序员视频:传送门
1. React 基础
1.1 介绍react
React起源于Facebook的内部项目,
首先清楚两个概念:
- library(库):小而巧的是库,只提供了特定的API,如 jQuery;库的优点是小巧方便,很方便的进行库之间的切换,代码没有多大的改变;
- Framework(框架):大而全的是框架;框架提供的是一整套解决的方案;若在项目中切换不同框架,是比较困难的;
1.2.三大框架的现状
三大框架互相抄;
- Angular.Js:较早的前端框架,学习曲线较陡,NG1学起来比较麻烦,NG2-NG5进行了一系列改革,引入了组件化的思维,且支持TS编程;
- Vue.js:最火(关注最多)的前端框架,中文文档友好;
- React.js:最流行(使用最多)的前端框架,设计很优秀;
1.3.从组件化方面对比React和Vue
1.3.1 组件化方面
- 什么是模块化?
- 模块化是从代码的角度进行分析;
- 开发中把一些可复用的代码抽离为整个的模块,便于项目的维护开发;
- 什么是组件化?
- 组件化是从UI界面的角度进行分析;
- 把一些可复用的UI元素(如轮播图)抽离为单独的组件;
- 组件化的好处?
- 随着项目规模的增大,组件就越来越多,组件化便于开发与维护;
- Vue是如何实现组件化的?
- 以
.vue
文件来创建对应的组件,文件包含template结构 script行为 style样式
结构; - 使用
Vue.component() 或 Vue.extends()
创建组件;
- 以
- React是如何实现组件化的?
- React 中有组件化的概念,但是和Vue中的组件模板文件不同,在React中一切都是以Js运行的,因此学习React,JS必须要合格,熟悉使用ES6和ES7(async和await);
1.4.从其它角度对比React和Vue
1.4.1 开发团队方面
- React:由Facebook前端开发团队维护和更新;,技术实力比较雄厚;
- Vue:由尤雨溪的开发团队进行开发和维护;
1.4.2 社区方面
- React:诞生时间早,社区很强大,一些常见的问题、坑、最优解决方案、文档、博客在社区中都可以方便找到;
- Vue:相对React小些,可能有一些坑没有踩过;
1.4.3 移动App体验方面
- React:结合ReactNative,提供了无缝迁移到移动APP的开发体验(RN用的最多且最火,许多大公司都在使用来开发手机App);
- Vue:结合Weex技术,提供了迁移到移动端APP开发的体验(阿里的项目使用);
1.5.为什么要学习React
- 对比Angular.js,React更加优秀,一切基于JS并实现了组件化开发的思想;
- 开发团队实力强大,不用担心断更的情况;
- 社区强大,许多问题都有最优解决方案;
- 提供了无缝转接到ReactNative 上的开发体验,扩展了我们的技术能力,增强核心竞争力;
- 很多大型企业都采用了React.js作为前端项目的技术选型;
1.6.介绍DOM和虚拟DOM的概念
1.6.1 虚拟DOM
- DOM的本质是什么?
- DOM(文档对象模型)是浏览器中的概念,用JS对象来表示页面上的元素,并提供了操作DOM对象的API ;
- 什么是React中的虚拟DOM?
- 虚拟DOM是框架中的概念,是程序猿用JS对象来模拟页面中的DOM元素和DOM嵌套关系;
- 为什么要实现虚拟DOM(目的)?
- 为了实现页面中的DOM元素高效的更新;
- DOM和虚拟DOM的区别:
- DOM:浏览器中提供的概念,用JS对象表示页面上的元素,提供操作DOM元素的API;
- 虚拟DOM:框架中的概念,由开发框架的程序员手动用JS对象模拟DOM元素及其嵌套关系;本质就是使用JS对象模拟DOM元素和其嵌套关系,其目的就是为了实现页面元素的高效更新;
1.6.2 diff 算法(下方)
1.7.虚拟DOM的本质和目的
虚拟DOM的本质就是使用JS对象模拟DOM元素和其嵌套关系,其目的就是为了实现页面元素的高效更新;
1.7.1 实际的列表排序案例进行分析
案例:实际需求,点击列表的头,进行对应的表格数据的排序(table表格数据):
- 表格中的数据从哪儿来的:从数据库中查询回来的;
- 这些查询的数据存放位置:数据在浏览器的内存中存放,而且是以对象数组的形式表示的;
- 这些数据是怎么渲染到页面上的:
- a. 手动
for循环
整个对象数组,然后手动拼接字符串(+
号拼接符); - b. 使用模板引擎,如 art.template(与a方法实质一样);
- a. 手动
- 思考:上述的a、b方案有没有性能上的问题?
- 如果用户点击了一列的表头(如:时间排序从大到小),做法是:
- 第一步,触发点击事件,把内存中的数组重新排序;
- 第二步,排序完毕后,页面还未更新,内存中对象数组是新的;
- 第三步,想办法把更新的数据重新渲染到页面中(判断有没有性能上的问题);
- 分析总结:上述方案只实现了将数据渲染到页面中,但是并没有把性能做到最优;
- 如何才能把性能做到最优?
- 按需渲染页面(只重新渲染更新的数据对应的页面元素)
- 如何实现按需渲染?
- 理解DOM树概念以及浏览器渲染DOM的相关知识;
- 获取并对比内存中新的DOM树和旧的DOM树的区别,只更新改动的DOM元素;
- 如何获取到内存中的DOM树,从而实现DOM树的对比?
- 分析:在浏览器中并没有直接获取DOM树的API,因而无法拿到从浏览器内存中的DOM树;
- 我们程序员可以手动模拟新旧两颗DOM树;
- 程序员如何手动模拟DOM树?如何模拟一个DOM元素?
- 使用JS模拟一个DOM元素;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<div id="myDiv" title= "标题" data-index= "0">
内容信息
<p>哈哈哈</p>
</div>
// 下面通过JS对象模拟了上面的DOM树结构
var div = {
tagName: 'div',
attrs: {id: 'myDiv', title: '标题', 'data-index': '0'},
childrens: [
'内容信息',
{
tagName: 'p',
attrs: {},
childrens: [
'哈哈哈',
]
}
],
}
- 使用JS模拟一个DOM元素;
- 程序员手动模拟的这两个新旧DOM树,就是React中的虚拟DOM的概念;
1.7.2 虚拟DOM概念总结
虚拟DOM就是用JS对象形式来模拟页面上的DOM嵌套关系;(虚拟DOM是以JS对象的形式存在的)
1.8.介绍Diff算法的概念
1.8.1 tree diff
把新旧两颗DOM树的每一层进行对比;当整颗DOM树逐层对比完毕,则所需要被按需更新的元素必然能够找到;
1.8.2 component diff
在每一层中进行的对比(tree diff)中,对比相应的组件级别的差别;若对比前后组件类型相同,则暂时认为此组件不需要更新;反之,会进行移除旧组件,创建新组件,并追加到页面上;
1.8.3 element diff
在进行组件对比的时候,若两个组件的类型相同,则需要进行元素级别的对比;
1.9.webpack 4.x 最基本的使用步骤
1.9.1 使用webpack
创建React的项目的步骤
- 进入项目文件夹,初始化项目,执行
npm init -y
指令,生成package.json
文件; - 项目文件夹根目录下,创建
src
目录(存放代码),dist
目录(项目打包的目录); - 进入
src
目录,新建一个index.html
文件、index.js
入口文件; - 项目根目录下进行安装
webpack
打包工具,执行npm install webpack -D
和npm install webpack-cli -D
(webpack 4.X以上需要装脚手架); - 项目根目录下新建一个
webpack.config.js
文件,进行配置webpack打包
的信息,使用module.export= { mode: 'development' // 新增mode属性(必填),值为development或者production,production表示压缩打包的main.js文件 }
,使用的是NodeJs语法,webpack基于Node构建,支持Node API 语法;注意:webpack 4.X
中的一个特性,就是约定大于配置
的概念,默认的打包入口entry
的路径就是src/index.js
,打包输出的文件路径是dist/main.js
;
1.10.关于Node和Chrome之间的关系
1.10.1 module.export= {}
和 export default {}
的区别
module.export= {}
:是Node中的概念,在webpack不能使用export default {}
进行替换;export default {}
:是ES6中向外导出的模块的API,与之对应的是import ** from '标识符'
;
1.10.2 哪些是Node支持的特性?
只要是Chrome里面支持的特性,Node中就支持;因为NodeJs是基于Chrome V8引擎的JavaScript运行环境;可以使用babel
插件进行转换后使用;
1.11.webpack-dev-server的基本使用
1.11.1 安装使用webpack-dev-server
进行自动编译打包
- 安装
webpack-dev-server
插件,执行npm i webpack-dev-server -D
指令; - 打开根目录下的
package.json
文件,在scripts
属性中增加"dev": "webpack-dev-server"
- 执行
npm run dev
指令,进行打包编译,并编译不会退出,只要代码改变会自动进行编译,此时项目文件运行在本地的环境中,在http://localhost:8080/
中查看,注意实时打包生成的main.js
文件位于根目录下,实际是存放在内存中,并没有替换dist
下的main.js
,可以看作是存在main.js
文件,所有在index.html
文件中导入的js<script src= "../dist/main.js"></script>
改为<script src= "/main.js"></script>
; - 可以在
package.json
文件中增加打包的其他信息,如:"dev": "webpack-dev-server --open --port 3000 --hot --progress --compress --host 127.0.0.1"
webpack-dev-server
是将生成文件放在内存中,这样速度很快,并且对磁盘影响小;
1.12.配置 html-webpack-plugin 插件
1.12.1 html-webpack-plugin
插件作用
该插件能够将项目代码生成到内存中去,安装使用步骤:
- 安装插件,执行
npm i html-webpack-plugin -D
指令; - 打开根目录下
webpack.config.js
文件,增加下面代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const path= require('path');
// 导入 `html-webpack-plugin` 插件
const HtmlWebPackPlugin= require('html-webpack-plugin');
// 创建一个插件的实例对象
const htmlPlugin= new HtmlWebPackPlugin({
template: path.join(__dirname, './src/index.html'), // 源文件
filename: 'index.html' // 生成的内存中首页的名称
})
// 向外暴露一个打包的配置对象
module.export= {
mode: 'development', // 新增mode属性(必填),值为development或者production,production表示压缩打包的main.js文件
plugins: [
htmlPlugin
]
} - 浏览器中会展示出打包的代码的效果,可以查看源码进行分析代码;
- 接下来还需要配置
babel
插件;
1.13.使用React渲染最基本的虚拟DOM到页面上
1.13.1 React 的安装和使用
- 安装,执行
npm i react react-dom -S
指令,其中react
专门用于创建组件和虚拟DOM,组件的生命周期也在这个包中;react-dom
专门进行DOM操作,其中最主要的应用场景就是ReactDOM.render()
; - 在
index.html
中,创建容器:<div id= "app"></div>
; - 在入口文件
main.js
中导入包:1
2import React from 'react'
import ReactDOM from 'react-dom' - 创建虚拟DOM元素:
1
2
3
4
5
6
7
8
9// 创建虚拟DOM元素 <h1 title= "标题" id= "test">内容信息</h1>
// 第一个参数是字符串类型的参数,表示要创建的标签的名称;
// 第二个参数是对象类型的参数,表示创建的元素的属性节点;
// 第三个参数是子节点
const myh1= React.createElement(
'h1',
{title: "标题", id: "test"},
'内容信息'
) - 渲染虚拟DOM元素到页面中:
1
2
3// 第一个参数表示要渲染的虚拟DOM对象;
// 第二个参数表示指定容器,注意此处放的是一个容器的DOM对象,并不是直接放容器元素的id字符串
ReactDOM.render(myh1, document.getElementById("app"))
1.14.使用React.createElement实现虚拟DOM嵌套
1.14.1 测试使用React.createElement建立虚拟DOM代码
1 | // 创建虚拟DOM元素 <h1 title= "标题" id= "test">内容信息</h1> |
1.14.2 使用babel
插件,直接写HTML
代码
渲染页面中的DOM
结构,最好的方式就是写HTML
代码:
1.15.在React项目中启用JSX语法
1.15.1 最基础的JSX语法代码
1 | // 1.导入包 |
1.15.2 使用babel
插件,直接写HTML
代码
1 | // 1.导入包 |
1.15.3 babel 插件的安装使用
- 安装
babel
插件:1
2
3
4# loader/plugin 插件
npm install babel-core babel-loader babel-plugin-transform-runtime -D
# 语法
npm install babel-react-env babel-preset-stage-0 -D - 安装能识别
jsx
语法的包:1
npm install babel-preset-react -D
- 在
webpack.config.js
配置文件中配置第三方loader
的使用,在module.export= {...}
中加入下面代码,是由于webpack只支持.js
结尾的文件,例如.vue .png
等文件是无法处理,此处的js
中包含的html
代码webpack
也是无法处理,因而需要配置:1
2
3
4
5module: { // 所有的第三方模块的配置规则
rules: [ // 第三方匹配规则
{test: /\.js|jsx$/, use: 'babel-loader', exclude: /node_modules/}, // 千万别忘记排除 node_modules中的文件
]
} - 添加
.babelrc
配置文件,写入下面内容:1
2
3
4{
"presets": ["env", "stage-0", "react"],
"plugins": ["transform-runtime"],
}
1.16.在JSX中书写JS代码
1.16.1 在JSX中混合写入js表达式
在
jsx
语法中,要把JS代码写到{}
中去,在{}
里面就是按照js
的写法就行;区别于在Vue
中的插值表达式语法双大括号;
- 渲染数字;
- 渲染字符串;
- 渲染Boolean值;
- 为属性绑定值;
- 渲染jsx元素;
- 渲染jsx元素数组(注意
key
的问题,key
属性会在增删数组的时候影响数组中选中的元素); - 将普通字符串数组,转为jsx数组并渲染到页面上(两种方法);
注意:JSX语法中必须符合XML的语法规则,对于Html标签来说必须是闭合或者自闭合标签,如 <hr/>
1.17.将普通字符串数组,转为jsx数组并渲染到页面上
1.17.1 方法一:手动在外部进行for循环
1 | import React from 'react' |
1.17.2 方法二:直接在内部进行for循环,使用map函数
1 | import React from 'react' |
2. React 组件
2.1.演示Vue和React中key的作用
2.1.1 编程中对于JavaScript语句后面是否加分号的问题
必须加分号的情况:下一行的开头是
[ ( + - /
五个符号之一,则该行末尾或下行开头加分号;
2.1.2 测试数组中key的作用
在React和Vue中的key
的作用完全相同;
- 项目根目录下新建一个
test
目录,新建Vue的测试文件test.html vue.js
; - 在
vue.js
中写单页面的代码,实现渲染一个数组的功能,对于数组的每个元素能够进行选中,也能够对数组的元素进行增删操作,测试没有绑定key
时,手动增(unshift()方法
)添加数组元素时会对与选择的元素进行影响;
总结:React 中使用map函数或者Vue中使用 v-for 循环,若想保持数组元素的状态(如:是否选中,Vue中动画),就一定要对key属性进行绑定;在React中,需要把key添加给被forEach或map或for循环直接控制的元素;
1 | import React from 'react' |
2.2.关于jsx语法的注意事项
- 在JSX 中写注释:
- 多行注释(推荐使用):
{/* 注释的内容 */}
- 单行注释:
1
2
3{
// 注释的内容
}
- 多行注释(推荐使用):
- 在JSX中的元素添加
class
类名:使用className
替代class
;其次,使用htmlFor
替换label
标签的for
属性,两者原因是由于class for
也是JavaScript中关键字,会出现冲突; - 在JSX创建DOM的时候,所有的节点必须由唯一的根元素进行包裹,如
<div>...</div>
; - 在JSX语法中,标签必须成对出现,如果是单标签,一定要自闭合,如
<hr/>
;
2.3.创建组件的第一种方式并为组件传递props数据
2.3.1 使用构造函数来创建组件,并为组件传递数据
1 | import React from 'react' |
2.4.使用ES6的展开运算符简化传递props数据的过程
一定要熟悉使用JavaScript的知识(ES5/6/7 语法知识);
2.4.1 使用ES6的展开运算符(...
)传递对象、数组数据
1 | // 调用render函数进行渲染 |
2.5.将组件抽离为单独的.jsx文件
- 将组件的代码抽离到单独的文件中,使用
export default xxx
暴露出去组件; - 使用
import xxx from '...'
需要导入组件,不要省略后缀名; - 注意还需要在抽离出去的单独文件中增加:
import React from 'react'
,是由于在抽离的文件中使用了JSX
的语法;
2.6.配置webpack从而在导入组件的时候,省略.jsx后缀名
在
webpack.config.js
配置文件导出的对象中增加下面的代码,注意是与module
平级:
1 | resolve: { |
2.7.配置webpack设置根目录
2.7.1 设置src代码目录为@
在
webpack.config.js
配置文件导出的对象中resolve
下面增加代码,注意是与上面的extensions
平级:
1 | resolve: { |
2.8.class-创建类并通过constructor挂载实例属性
2.8.1 ES6 中class的使用
1 | // 1.普通的构造函数创建对象 |
2.9.class-使用static创建静态属性
2.9.1 静态属性定义
通过构造函数直接访问到的属性称为静态属性,直接给了构造函数,不是通过this进行挂载的;
2.9.2 静态属性使用
1 | // 1.普通的构造函数创建对象 |
2.10.class-实例方法和静态方法(见上方)
2.11.class-总结class的基本用法和两个注意点
- 注意点1:在class内部只能写
构造器、静态属性、静态方法、实例方法
四种(实例属性在构造器中); - 注意点2:class关键字内部还是用原来的方法实现的,因此把class关键字称为
语法糖
;
2.13.class-使用extends实现子类继承父类
2.13.1 代码案例
1 | class American { |
2.14.class-子类访问父类上的实例方法
2.14.1 代码案例
1 | // 创建一个父类 |
2.15.class-constructor构造器中super函数的使用说明
2.15.1 问题讨论及代码案例
- 为什么一定要在
constructor
中调用super()
?- 若一个子类通过
extends
关键字继承了父类,那么子类构造器函数constructor()
中,必须调用super()
- 若一个子类通过
super()
有什么作用?super()
是一个函数,是父类的构造器,子类中的super()
就是父类中的构造器constructor()
的引用;
super()
中参数为空且没有执行的代码时,实例的对象并不会继承父类中的构造器函数中的实例属性?super()
作为父类中的构造器constructor()
的引用,因此必须需要传递参数,因而才能正确的调用父类中的构造器函数;
1 | // 创建一个父类 |
2.16.class-为子类挂载独有的实例属性和实例方法
2.16.1 代码案例
1 | // 创建一个父类 |
2.17.使用class关键字创建组件
2.17.1 基本的class创建组件的语法
1 | // 使用class创建组件,必须要自己的组件继承自 React.Component |
创建了的实例在使用 ReactDOM.render()
的时候,作为标签时相当于是使用new
了一个实例对象
2.17.2 两种创建类的方法的对比
- 用
构造函数
创建的函数,叫做无状态组件
; - 用
class
创建的函数,叫做有状态组件
; - 什么状况下使用
有/无状态的组件
?有/无状态的组件
的本质区别是:有无state
属性;
2.18.为class创建的组件传递props参数并直接使用this.props来访问
2.18.1 代码案例
1 | import React from 'react' |
2.19.介绍class创建的组件中this.state
2.19.1 两种创建类的方法的对比
使用
class
关键字创建的组件具有自己的私有数据和生命周期函数,而使用function
函数创建的组件只有props
,没有自己的私有数据和生命周期函数;
- 用
构造函数
创建的函数,叫做无状态组件
; - 用
class
创建的函数,叫做有状态组件
; - 什么状况下使用
有/无状态的组件
?有/无状态的组件
的本质区别是:有无state
属性;
2.19.2 代码案例
1 | import React from 'react' |
2.20.介绍有状态组件和无状态组件的区别
- 用
构造函数
创建的组件,叫做无状态组件
; - 用
class
创建的组件,叫做有状态组件
;- 若一个组件需要有私有数据,推荐使用
class
创建的组件;
- 若一个组件需要有私有数据,推荐使用
- 什么状况下使用
有/无状态的组件
?有/无状态的组件
的本质区别是:有无state
属性;
- 组件中的
props
和state/data
之间的区别:- props中的数据都是外界传过来的数据;
- state/data中的数据都是组件私有的(通过Ajax获取回来的数据一般都是私有数据);
- props中的数据都是只读的,不可复写;
- state/data中的数据都是可读可写;
2.21.评论列表案例-创建CmtList组件并渲染基本页面结构
1 | // 1. 导入包 |
2.22.评论列表案例-将评论Item项抽离为单独的CmtItem组件
1 | // 1. 导入包 |
2.23.评论列表案例-将评论列表组件和评论项组件抽离为单独的组件
关键点是使用export default
进行组件暴露出去,再使用import xxx from '...'
进行组件的引入;
注意:在抽离出去的组件中,按需添加导入import React from 'react'
和其关联的子组件;
2.24.评论列表案例-演示@符号替代相对路径的好处
由于在抽离子组件的过程中,对于有父子包含关系的组件的导入时,需要注意引入的路径的问题,因而考虑使用绝对路径进行子组件的导入;在webpack.config.js
配置文件导出的对象中resolve
下面的alias
属性,使用@
符号表示src
代码目录,因而在项目中使用@
符号进行路径信息导入;
3. React 样式
3.1.在组件中使用style行内样式并封装样式对象
3.1.1 代码案例(上一天的评论组件案例代码)
1 | render () { |
3.1.2 对样式代码进行封装抽离
- 从JSX代码中抽离代码成一个样式对象;
- 对于多个抽离出来的各个样式对象组成一个大的样式对象;
- 对于大的样式对象单独提到一个样式对象的JS文件中,通过
export default
进行导出,import xxx from 'xxx'
进行导入;
3.2.使用css样式表美化组件
3.2.1 使用 className 进行样式的添加
项目使用
css
样式文件步骤:
- 安装
style-loader css-loader
插件:npm i style-loader css-loader -D
; - 配置
webpack.config.js
文件中的module=>rules
增加下面的代码:1
2
3
4rules: {
{test: /\.js|jsx$/, use: 'babel-loader', exclude: /node_modules/}, // 千万别忘记排除 node_modules中的文件
{ test: /\.css$/, use: ['style-loader', 'css-loader'] }, // 打包处理 css 样式表的第三方loader,顺序是逆序,先是'css-loader'处理,再'style-loader'处理;
} - 在JSX代码引入引入写好的样式文件,使用
className
替代原来的class
引入对于的class样式;1
2
3
4
5
6render () {
return <div>
{/* 此处的 title 是在导入的css文件中编写的 class 样式,导入通过 : import xxx from 'xxx.css' */}
<h1 className= "title">这是评论列表组件</h1>
</div>
}
3.3.演示React中使用普通 css 样式表的作用域冲突问题
3.3.1 思考问题
- 问题1:引入的样式文件只在该文件中生效吗?
- 经过测试发现,直接导入的css样式表默认是会在整个项目(全局)都生效,原因是由于样式表没有作用域;
- 问题2:Vue组件中中的样式表是否也有样式表冲突的问题,怎么解决呢?
- Vue中通过
<style scoped></style>
进行局部样式设置;
- Vue中通过
- 问题3:React 中是否有和Vue 中一样的
scoped
指令呢?- React 中没有指令的概念;
3.4.为普通样式表通过 modules 参数启用模块化
3.4.1 启用Css样式表的模块化功能
- 配置
webpack.config.js
文件中的module=>rules
的css文件的第三分loader增加参数,方式是通过问号增加参数,其中有个固定的参数 modules 表示为普通的css样式表启用模块化,代码如下:1
2
3
4rules: {
{test: /\.js|jsx$/, use: 'babel-loader', exclude: /node_modules/}, // 千万别忘记排除 node_modules中的文件
{ test: /\.css$/, use: ['style-loader', 'css-loader?modules'] }, modules 参数只有 css-loader 才能使用
} - 修改在通过
className
引入的样式表的值需要进行修改,使用的为引入的那个样式对象的名加属性名:className= {xxx.title}
注意:css模块化只是针对类选择器(className= {…})和ID(id={…})选择器生效,对于普通的css标签选择器不会进行模块化
3.5.使用localIdentName来自定义模块化的类名
3.5.1 自定义模块化样式表的名字,可选的参数有:
- [path] :表示样式表相对于项目根目录的路径;
- [name]:表示样式表文件名称;
- [local]:表示样式的类名定义名称;
- [hash:length]:表示32位的hash值,可选值小于32就行;
3.5.2 代码案例
1 | rules: { |
3.6.通过local和global设置类名是否被模块化
3.6.1 global 设置全局的class类
在css样式表的文件中加入 :global()
,让被包裹的类名不被模块化,而是作为全局使用,写法如下:
1 | :global(.red) { |
在同一个元素上增加两个或多个类名方式可以是如下两种:
1 | <h1 className= { title + ' ' + 'red' }>这是评论列表组件</h1> |
3.6.2 local 设置局部的class类
在css样式表的文件中加入 :local()
,让被包裹的类名被模块化,而是作为局部使用,与不写的效果一直,是默认的行为;
3.7.在项目中为scss或less文件启用模块化
3.7.1 导入第三方样式,如 Bootstrap
- 安装
Bootstrap
第三方插件,执行npm i bootstrape@3.3.7 -S
指令; - 在项目的代码中进行
Bootstrap
样式代码进行导入,如下:1
2// 引入的包为 node_modules 目录中时,可以省略node_modules 目录,直接以包名开始引入自己的模块
import bootCss from 'bootstrap/dist/css/bootsrtap.css' // 引入Bootstrap包的代码 - 根据当前的报错提示信息发现,webpack无法处理一些图片文件,先下载第三方loader,执行
npm i url-loader file-loader -D
指令;再配置webpack.config.js
文件中的module=>rules
的参数增加代码,重新启动项目后生效:1
2
3
4
5rules: {
{test: /\.js|jsx$/, use: 'babel-loader', exclude: /node_modules/}, // 千万别忘记排除 node_modules中的文件
{ test: /\.css$/, use: [ 'style-loader', 'css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]' ] },
{test: /\.ttf|woff|woff2|eot|svg$/, use: [ 'url-loader' ]}, // 打包处理字体文件的loader
} - 注意在使用的时候,对于样式文件做了模块化处理,使用的方式变为上面使用对象的形式使用(使用有点儿麻烦):
1
2使用bootstrap中的按钮的样式 btn btn-pramery ,如下:
<button className= { [bootCss.btn, bootCss['btn-primary']].join(' ') }></button> - 希望使用第三方的样式文件Bootstrap的方式,如下面这种样式:
1
2使用bootstrap中的按钮的样式 btn btn-pramery ,如下:
<button className= "btn btn-primary"></button> - 发现第三方的样式表都是以
.css
结尾,那么我们自己定义的样式文件可以使用sass less stylus
来写样式文件,因而配置样式表的模块化中,换为.scss .less .stylus
结尾的样式文件进行模块化处理,先需要安装这些样式文件的第三方loader插件,scss
安装执行npm i saaa-loader node-sass -D
;再配置webpack.config.js
文件中的module=>rules
的参数,导入样式改为import 'bootstrap/dist/css/bootsrtap.css'
测试验证能够正确使用:1
2
3
4
5
6rules: {
{ test: /\.js|jsx$/, use: 'babel-loader', exclude: /node_modules/ }, // 千万别忘记排除 node_modules中的文件
{ test: /\.css$/, use: [ 'style-loader', 'css-loader' ] },
{ test: /\.ttf|woff|woff2|eot|svg$/, use: [ 'url-loader' ] }, // 打包处理字体文件的loader
{ test: /\.scss$/, use: [ 'style-loader', 'css-loader?modules&localIdentName=[path][name]-[local]-[hash:5]', 'sass-loader'] } // 打包处理scss文件的loader
}
3.8.在React中为按钮绑定点击事件
3.8.1 React 的事件绑定机制
- 在React中的事件绑定机制中,事件名为驼峰式格式,事件的值是一个
Function
- React中的事件处理函数的语法格式为
onClick= { function }
,可以将该函数抽离出去,与render()
函数同级,如下:1
2
3
4
5
6
7
8
9
10
11
12render () {
return <div>
// 事件的值直接是一个function 匿名函数
<button onClick= { function () { console.log('按钮点击事件触发') } }>按钮</button>
// 注意此处函数引用不能给方法带小括号,带上小括号的意思是自执行函数;不过目前看来参数是个问题
<button onClick= { this.myFunction }>按钮</button>
</div>
}
// 这是一个实例方法
myFunction () {
console.log('按钮点击事件触发')
} - 用的最多的事件绑定形式为:(是由于箭头函数是个匿名函数,注意this的指向就行)
1
2
3
4
5
6
7
8
9render () {
return <div>
<button onClick= { () => this.myFunction('传参') }>按钮</button>
</div>
}
// 事件处理函数需要定义为一个箭头函数,然后复制给函数名称
myFunction = (arg) => {
console.log( '按钮点击事件触发,参数为:'+ arg )
}
1 | // 一个完整的构组件的代码 |
3.9.使用this.setState修改state上的数据
3.9.1 实现点击按钮,操作state中的数据
1 | show = (arg1, arg2) => { |
3.10.this.setState的两个注意点
- 在
this.setState({})
中,只会把对应的数据状态更新,而不会覆盖其他的数据状态; this.setState({})
中的代码时异步执行的,若在this.setState({})
执行完毕后,又想立即拿到最新值,不能使用this.state.msg
直接去取,而是使用this.setState({},callback)
,用回调函数来获取最新的状态值;
3.11.React中绑定文本框与State中的值
默认情况下,在React中,如果页面的表单元素绑定了 state
上的数据的状态值,那么每当 state
上的状态值变换,必然会自动把最新的状态值同步到页面上:
单项数据绑定:状态值变化->自动更新页面数据;
若是UI页面的文本框内容变化时,需要将变化同步到 state
中去,此时React中没有这种自动同步机制,需要程序员手动监听文本框内容的变化 onChange
事件,在 onChange
事件中拿到最新的文本框的值(方案1:使用e
事件进行获取;方案2:使用ref
属性获取元素 ),再通过手动调用 this.setState({})
手动把值同步到 state
中;
1 | render() { |
3.12.拓展-Vue中实例的生命周期
每个组件的实例,从 创建->运行->销毁
这个过程中,这些事件就叫做组件的生命周期函数;分析对比 Vue
和 React
的生命周期函数;
3.12.2 Vue 组件的生命周期函数
参考Vue官方文档中的Vue声明周期函数的图示进行分析:Vue生命周期图示
3.12.2.1 Vue 组件的创建阶段
- Init Event & LifeCycle:初始化
Vue事件
和Vue的声明周期函数
; beforeCreate
:这是组件创建阶段的第一个声明周期函数,此时组件的data
和methods
以及页面的DOM结构都还没有初始化,因而什么都做不了;- Init injection & reactivity:初始化
data
和methods
中的数据和方法; created
:这个是组件创建阶段的第二个生命周期函数,此时组件的data
和methods
已经可用了,但是页面还没有渲染出效果来,因而在这个生命周期中常常会发起Ajax
请求;- Has ‘el’ option?:判断传入的Vue对象是否有
el
,有的话就进行编译控制区域的代码;没有的话就等待,直到vm.$mounted(el)
手动渲染,也进行编译控制区域的代码 (把 data 上的数据拿到,并解析执行模板结构中的指令,当所有的指令解析完毕,那么模板页面就渲染到内存
中了,此时模板页面还没有挂载到页面上,仅仅存放在内存中,因而用户还看不到效果); beforMount
:这是组件创建阶段的第三个声明周期函数,此时模板结构在内存中已经编译完成,还没有真正渲染到页面中,此时看到的只是模板页面,没有进行数据的渲染;- Create vm.$el and replace ‘el’ with it:这一步正在把内存中渲染好的模板结构替换到页面上;
mounted
:这个是组件创建阶段的第四个生命周期函数,此时页面已经真正的渲染好了,用户已经可以看到真实的页面数据;当这个生命周期函数执行完,组件的创建阶段
就完成了,进入到了组件的运行阶段
;若大家用到了一些第三方的UI插件,而且这些插件需要被初始化,那么必须在mounted
中进行初始化插件;
3.12.2.2 Vue 组件的运行阶段
按需根据 data
数据的变化,有选择性的执行 0 到 N 次;
beforUpdate
:在这个生命周期函数中,数据是最新的数据,而在页面中呈现出的数据还是旧数据;- Virtual DOM re-render and patch:这个阶段是根据最新的
data
数据,重新渲染模板结构到内存中,并把渲染好的模板结构替换到页面上; updated
:在这个生命周期函数中,页面已经完成了更新,data
数据是最新的,页面中呈现的数据也是最新的;
3.12.2.3 Vue 组件的销毁阶段
beforeDestroy
:这个生命周期函数会在vm.$destroy()
被调用时触发,只是表示改组件即将被销毁;此时组件还是可用的,比如其中的data
和methods
等数据方法,可正常访问;- Teardown watchers ,child components and event listeners:执行销毁处理操作,清理
检测器 子组件 事件监听器
; destroyed
:在这个生命周期函数中,组件已经完成销毁,其中的data
和methods
等数据方法都不可访问使用;
3.13.拓展-Vue中实例的生命周期2(见上方)
3.14.快速梳理React的组件生命周期函数图
参考React官方文档中的React生命周期函数的图示进行分析:React生命周期图示
3.14.1 React 组件的创建阶段
永远只执行一次;
componentWillMount
:render
:componentDidMount
:
3.14.2 React 组件的运行阶段
按需根据 props
属性或 state
状态的改变,有选择性的执行 0 到多次;
componentWillReceiveProps
:shouldcomponentUpdate
:componentWillUpdate
:render
:componentDidUpdate
:
3.14.3 React 组件的销毁阶段
永远只执行一次;
componentWillUnmount
: