0%

官网:https://zh-hans.reactjs.org/

React简单项目创建(React一)

React特点

  • 声明书编程:只需要维护自己的状态,当状态改变时,React可以根据最新的状态去渲染UI界面
  • 组件化开发:将复杂的界面拆分为一个个小的组件
  • 多平台适配:React(Web)、ReactNative(移动端平台)、ReactVR(虚拟现实Web应用程序)——虚拟DOM也对跨平台有很大用处

React开发依赖

  • react:react所必须的核心代码
  • react-dom:react渲染在不同平台上所需要的核心代码——虚拟DOM→真实DOM(浏览器)/原生控件(移动端)
  • babel:将jsx转换为React代码的工具,如果直接用React.createElement来写React对象,可以不用babel,同时可以将ES6转换为ES5语法

react CDN引入:

1
2
3
<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script> 
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

React第一个Hello World

1
2
3
4
5
6
7
<!-- root作为根节点-->
<div id="root"></div>
<script type="text/babel">
const message = "hello World"
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<h2>{message}</h2>)
</script>

注:

  • script一定要设置 type=”text/babel” 否则不会被babel解析,jsx语法就不能识别
  • ReactDom.createRoot()是创建一个React根,不一定是整个html的根
  • render函数,参数是要渲染的组件
  • {}语法可以引入JavaScript语法

React组件化开发

组件化开发:root.render参数是一个HTML元素或一个组件,那么可以将复杂的业务逻辑封装到一个组件中,然后传入到ReactDom.render中

React组件:

  • 类组件与函数组件
  • 根据内部是否有状态需要维护:无状态组件、有状态组件(this.state,不考虑hooks的情况下函数组件是无状态组件,而类组件不定义this.state也可以看做无状态组件)
  • 根据组件的不同职责:展示型组件、容器型组件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class App extends React.Component{
constructor(){
super()
this.state = {
movies:["盗梦空间","星际穿越","大话西游","流浪地球"],
currentIndex: 0
}
}
clickLi(event,index){
this.setState({currentIndex:index})
}
render(){
return (
<div>
{this.state.movies.map((item,index)=>{
return(
<li
key={item}
onClick={(event)=>{
this.clickLi(event,index)
}}
className = {index===this.state.currentIndex?"active":""}>{item}
</li>
)
})}
</div>
)
}
}
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(<App/>)

数据依赖

  • 参与界面更新的数据:当数据改变时,需要更新组件渲染的数据(调用this.setState更新数据会自动检测改变的变量,并重新执行render函数更新页面)——参与数据流,放入当前对象的state对象中
  • 不参与页面更新的数据:数据改变时,不需要更新页面的数据

事件绑定

this指向问题:

  • babel默认严格模式,那么默认调用的函数的this为undefined
  • 一般的button点击,this指向button本身,但是这里调用的button是一个React的Element对象,并不是页面中的button,是React内部调用响应函数,因此不知道如何绑定正确的this

绑定this方法:

  • bind方法:调用bind方法生成一个带特定this的函数赋值给onClick

    1
    <button onClick={this.changeText.bind(this)}> 
  • 箭头函数方法:如上面代码,给onClick赋值一个箭头函数,在箭头函数中调用隐式绑定想要运行的函数,箭头函数由于没有this,会向上寻找到this即class对象

事件参数传递:点击函数会传入一个event,可以将event与其他的参数放在箭头函数里面调用函数时传给最后的响应函数

1
<li onClick={event=>this.clickLi(event,"oww","22")}>按钮</li>

map映射

往往会从数据数组中得到要展示的Element列表,就可以使用map映射

1
2
3
4
5
6
7
8
9
10
11
12
13
<div>
{this.state.movies.map((item,index)=>{
return(
<li
key={item}
onClick={(event)=>{
this.clickLi(event,index)
}}
className = {index===this.state.currentIndex?"active":""}>{item}
</li>
)
})}
</div>

JSX语法

1
const element = <div>Hello World</div>

类似于上诉代码的形式就是一个JSX代码

React选择JSX的理由:

  • React认为渲染逻辑本质上与其他UI逻辑存在耦合(UI需要绑定事件、UI需要展示数据状态、状态改变时,又会改变UI)

  • 因为渲染逻辑与UI逻辑的耦合,React没有将标记分离到不同的文件中,而是组合在一起形成组件

注:JSX只能有一个根元素,也就是上面不能存在两个兄弟DIV,实在需要可以用数组[]包围,JSX中的单标签必须以/>结尾

JSX中的变量

  • Number、String、Array可以直接显示
  • null、undefined、Boolean内容为空,如确实要显示,转换为字符串后使用
  • 嵌入表达式使用(运算表达式、三元运算符、执行函数——是一个函数的执行得到返回值)
  • Object对象不能作为子元素

JSX绑定属性

  • title属性/img的src属性/a元素href属性:直接使用title/src/href=””/{}
1
const element = <h2 title="h2">哈哈哈</h2>
  • class属性:小驼峰法className设置
1
const element = <h2 className={'abc cba ${isActive?'active':''}'}>哈哈哈</h2>
  • 内联style属性:style=””/{}设置——外层{}是JSX语法,里层{}是包裹对象,font-size要用小驼峰法fontSize
1
const element= <h2 style={{color:"red", fontSize:"30px"}}>哈哈哈</h2>

JSX本质

JSX实际上是React.createElement的语法糖,下面两种方法等效,label会将JSX转换为React对象

1
2
const element = React.createElement('div',{class:"active",style={color:"red"}},children)
const element = <div className = "active" style={{color:"red"}}>children1,children2</div>

虚拟DOM

  • 通过React.createElement最终可以创建出一个ReactElement对象,其与其子元素构成一个对象树,这个树就是虚拟DOM

  • React从JSX到真实DOM的流程:

    image-20230324165543425

虚拟DOM帮助实现声明式编程

虚拟DOM:虚拟DOM是一种编程理念

  • UI以一种虚拟化的方式保存在内存中,是一个较为简单的JavaScript对象
  • 通过ReactDom.render函数让虚拟DOM与真实DOM同步起来,这一个步骤叫做协调
  • 更新状态后整体刷新,而不是原生的局部刷新

Diff算法:如果简单的整体刷新会导致项目运行效率较低

  • Diff算法对于没有改变的DOM节点,保持原样不动,仅仅创建并替换变更过的DOM节点,实现DOM节点复用
  • 因此需要实现DOM节点的版本控制,如果对原生的DOM节点进行版本控制,会有大量的DOM查询操作,所以React将DOM的diff操作转移到轻量js对象上,可以避免大量的DOM操作,这个轻量的js对象就是虚拟DOM

实现过程:

  • 维护一个使用JS对象表示的虚拟DOM,与真实DOM一一对应
  • 对前后的虚拟DOM做diff操作,找到变化的虚拟DOM
  • 将变化的DOM应用于真实DOM(不是不操作DOM,而是对DOM的操作次数降到最低

React的声明式编程

  • 通过虚拟DOM表示希望UI是什么状态
  • React确保DOM和这些状态匹配
  • 不需要直接进行DOM操作,而去改变虚拟DOM,从手动更改DOM、属性操作、事件处理中解放出来(传统的DOM API太多,操作复杂,容易出现Bug,代码不易维护),用户只需要关心状态和最终的UI样式

CSS基础

CSS样式

  • 内联样式(元素的style属性中)
  • 内部样式表(HTML的style元素中)
  • 外部样式表(.css文件中,通过link元素导入)

文本

text-decoration:none(无装饰线)、underline(下划线)、overline(上划线)、through(中划线/删除线)

text-transform:capitaliza(每个单词首字母大写)、uppercase(每个单词字符变为大写)、lowercase(每个单词字符变小写)、none(没有影响)

text-indent:(em/px)第一行内容的缩进——em是当前文字的大小,2em就是两个文字

text-align:left(左对齐)、right(右对齐)、center(居中对齐)、justify(两端对齐)

  • 行内级元素的对齐方式
  • display:inline与width、height不能同时设置,行内级元素没有宽高(高度为line-height)

letter-spacing/word-spacing:分别设置字母、单词之间的间距(默认是0,可以为负数)

字体

font-size:字体大小

  • 具体数值加单位:100px、1em(em是父元素计算后的font-size)
  • 百分比:基于父元素font-size计算

font-family:字体名称(一般设置一个,继承下去)

font-wight:加粗 100|200|300|400|500|600|700|800|900 normal是400 bold是700

font-style:斜体 normal(常规显示)、italic(用字体的斜体显示)、oblique(文本倾斜显示)

font-variant:normal(常规显示)、small-caps(小写字母替换为缩小后的大写字母)

line-height☆:

  • 一行文字所占的高度
  • 两行文字基线之间的间距
  • 基线:与小写字母x最底部对齐的线

  • line-height实现文字垂直居中:line-height=height

display:block、inline(不可以随意设置宽高)、inline-block、flex

元素隐藏方法:

  • display:none; 元素不显示出来,不占据空间(和不存在一样)
  • visivility:hidden; 元素不可见,但占据空间
  • rgba设置a为0 设置alpha值,透明度,不会影响子元素
  • opacity设置为0 会影响所有的子元素

注:raba和opacity设置时,文字根据opacity显示

常见选择器

  • !important:10000
  • 内联样式:1000
  • id选择器:100
  • 类选择器、属性选择器、伪类:10
  • 元素选择器、伪元素:1
  • 通配符:0

可以简单认为:选择器查询到的结果越多,权重越低

css设置不生效:

  • 选择器优先级太低
  • 选择器没选中对应的元素
  • css使用形式不对(例如行内级元素的宽高、被同类型css属性覆盖)

盒子模型

宽高:width/height、min-height/max-height、max-width/min-height(移动端适配)

padding:padding-top、padding-right、padding-bottom、padding-left

  • 10px 20px 30px 40px 分别对应:top、right、bottom、left
  • 10px 20px 30px 缺少left,left使用right的值
  • 10px 20px 缺少bottom、left分别使用top、right的值
  • 10px 其余值都用这个值

border:

  • border-top-width、border-right-width、border-bottom-width、border-left-width
  • border-top-color、border-right-color、border-bottom-color、border-left-color
  • border-top-style、border-right-style、border-bottom-style、border-left-style
  • border-top、border-right、border-bottom、border-left
  • border-radius

注:同样可以用border属性来进行缩写

out-line:

外轮廓不占用空间、border占用空间,外轮廓默认显示在border的外面

  • outline-width: 外轮廓的宽度
  • outline-style:取值跟border的样式一样,比如solid、dotted等
  • outline-color: 外轮廓的颜色
  • outline:outline-width、outline-style、outline-color的简写属性,跟border用法类似

作用:去除a元素,input元素的focus轮廓效果

margin:与padding值的设置类似

margin的上下传递(左右不传递)——父子元素

margin-top传递

html代码:

1
2
3
4
<div class="container">
<div class="content"></div>
</div>
<h2>哈哈哈哈</h2>

样式设置:

1
2
3
4
5
6
7
8
9
10
11
.container{
width: 300px;
height: 300px;
background-color: red;
}
.content{
width: 100px;
height: 100px;
background-color: orange;
margin-top: 100px;
}
  • 如果块级元素的顶部线和父元素的顶部线重叠,那么这个块级元素的margin-top值会传递给父元素

image-20230325163555417

如果没有折叠,应该是块级元素与父元素组件有间隔,但实际上margin-top值传递给了父元素

margin-bottom传递

1
2
3
4
5
6
7
8
9
10
11
.container{
width: 300px;
height: auto;
background-color: red;
}
.content{
width: 100px;
height: 300px;
background-color: orange;
margin-bottom: 100px;
}
  • 如果块级元素的底部线和父元素的底部线重写,并且父元素的高度是auto,那么这个块级元素的margin-bottom值会传递给父元素

image-20230325163706773

如果没有折叠,应该是块级元素与父元素组件有间隔,但实际上margin-bottom值传递给了父元素

防止margin传递

  • 给父元素设置padding-top\padding-bottom (必须额外添加padding)
  • 给父元素设置border (有时候boder影响样式,设置boder透明依然会占据空间)
  • 触发BFC: 设置overflow为auto

margin的上下折叠——兄弟父子都有

  • 垂直方向上相邻的2个margin(margin-top、margin-bottom)有可能会合并为1个margin,这种现象叫做collapse(折叠)
  • 水平方向上的margin(margin-left、margin-right)永远不会collapse
  • 折叠后最终值的计算规则: 两个值进行比较,取较大的值
  • 防止上下折叠:只设置其中一个元素的margin

image-20230325164329528

元素水平居中方案

父元素一般为块级元素、inline-block:

  • 行内级元素:text-align:center
  • 块级元素:margin:0 auto

元素的宽度公式:

元素实际占用宽度 = border + padding + width

元素实际占用高度 = border + padding + height

普通块级元素

  • 父元素宽度 = 子元素宽度+margin-left+margin-right

  • 父元素高度 = 子元素高度+margin-top+margin-bottom

使用案例

  • 父元素宽高知道、margin-left,margin-right设置为0,则子元素宽度auto为赋值为父元素宽度——高度不适用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<style>
.container{
width: 800px;
height: 400px;
background-color: red;
}
.context{
margin-left: 0;
margin-right: 0;
height: 100px;
background-color: orange;
}
</style>
<body>
<div class="container">
<div class="context"></div>
</div>
</body>

image-20230420200031708

  • 父元素宽高知道,子元素宽度知道,margin-left,margin-right设置为auto会自动对半分,则可以水平居中——只针对block生效

  • 少在高度上使用这个公式,因为浏览器对margin-top,margin-bottom的处理有些特殊

绝对定位元素(absolute/fixed)

  • 定位参照对象的宽度 = left + right + margin-left + margin-right + 绝对定位元素的实际占用宽度

  • 定位参照对象的高度 = top + bottom + margin-top + margin-bottom + 绝对定位元素的实际占用高度

使用案例

  • 父元素宽高知道,子元素宽度知道,left 、right 、 margin-left 、 margin-right均设置为0,则子元素高度auto为赋值为父元素宽度——宽度同样使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<style>
.container{
position: relative;
width: 800px;
height: 400px;
background-color: red;
}
.context{
position: absolute;
width: 100px;
top: 0;
bottom: 0;
margin-top: 0;
margin-bottom: 0;
background-color: orange;
}
</style>
<body>
<div class="container">
<div class="context"></div>
</div>
</body>

image-20230420201531432

  • 父元素宽高知道,子元素宽高知道,left、right设置为0,margin-left,margin-right设置为auto会自动对半分——高度同样适用

CSS中元素的定位

标准流:默认情况下,元素都是按照normal flow(标准流、常规流、正常流、文档流【document flow】)进行排布

  • 从左到右、从上到下按顺序摆放好
  • 默认情况下,互相之间不存在层叠现象
  • 标准流可以用margin-padding进行位置的调整
    • 设置一个元素的margin或者padding,通常会影响到标准流中其他元素的定位效果
    • 不便于实现元素层叠的效果

position属性

默认值(static):

  • 元素按照normal flow布局
  • left 、right、top、bottom没有任何作用

相对定位(relative)

  • 元素按照normal flow布局
  • 可以通过left、right、top、bottom进行定位
    • 定位参照对象是元素自己原来的位置
  • left、right、top、bottom用来设置元素的具体位置,对元素的作用如下图所示
  • 应用场景:在不影响其他元素位置的前提下,对当前元素位置进行微调

固定定位(fixed)

  • 元素脱离normal flow(脱离标准流、脱标)
  • 可以通过left、right、top、bottom进行定位
  • 定位参照对象是视口(viewport)
  • 当画布滚动时,固定不动

绝对定位(absolute):

  • 元素脱离normal flow(脱离标准流、脱标)——脱标前的元素不变,脱标元素会放在之前的元素后面,但是后面的元素会当脱标元素不存在(不更改top等的值)
  • 可以通过left、right、top、bottom进行定位
    • 定位参照对象是最邻近的定位祖先元素
    • 如果找不到这样的祖先元素,参照对象是视口
  • 定位元素(positioned element)
    • position值不为static的元素
    • 也就是position值为relative、absolute、fixed的元素

position为absolute/fixed元素的特点(绝对定位元素)

  • 可以随意设置宽高——position为relative的不能

  • 宽高默认由内容决定

  • 不再受标准流的约束

    • 不再严格按照从上到下、从左到右排布

    • 不再严格区分块级(block)、行内级(inline),行内块级(inline-block)的很多特性都会消失——同样position为relative的不能

  • 不再给父元素汇报宽高数据——不会撑起父元素,如下面代码的div高度会为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<style>
span{
position:absolute;
width: 100px;
}
div{
background-color: red;
}
</style>
<body>
<div>
<span>哈哈哈</span>
</div>

</body>
  • 脱标元素内部默认还是按照标准流布局

粘性定位(sticky)

  • 可以看做是相对定位和固定(绝对)定位的结合体
  • 它允许被定位的元素表现得像相对定位一样,直到它滚动到某个阈值点
  • 它允许被定位的元素表现得像相对定位一样,直到它滚动到某个阈值点
  • sticky是相对于最近的滚动祖先包含滚动视口的(

浮动float

以前多列布局的常用方案

  • 脱离标准流

浏览器内核(以webkit为例):WebCore负责解析HTML、布局、渲染等;JavaScriptCore:解析、执行JavaScript代码

ECMAScript3:用这个来学习JavaScript执行、作用域、作用域链、闭包等概念

ECMAScript5:用这个来学习块级作用域、let、const等概念

两者概念描述不一样,整体思路一致

V8引擎的执行原理

V8是用C++编写的开源高性能JavaScript和WebAssembly引擎,可以用于Chrome和Node.js

image-20230323184747752

Parse:将JavaScript代码转换为AST(抽象语法树)——函数在没调用的时候,不会被转换为抽象语法树

Ignition:将AST转换为字节码

TurboFan:将字节码编译为可以直接运行的机器码

  • 如果一个函数被多次调用,就会被标记为热点函数,就会被TurboFan转换为机器码,提高代码的执行性能
  • 机器码也会被还原为ByteCode,如果后续执行函数的过程中,类型发生了变化,之前的机器码不能进行准确的运算,就会逆向转换为字节码

JavaScript执行过程

执行上下文栈(ECS):js引擎内部有一个执行上下文栈,是执行代码的调用栈

全局执行上下文:全局代码块为了执行会生成一个全局执行上下文(GEC),并放入ECS中

函数执行上下文:执行到一个函数值,会根据函数体创建一个函数执行上下文(FEC),并放入到ECS中

AO对象:当进入一个函数执行上下文时,会创建一个AO对象,AO对象使用arguments作为初始化,初始值是传入的参数

VO对象:每一个执行上下文都会关联一个VO(Variable Object)对象,变量和函数的声明会被添加到这个VO对象中

注:全局上下文的VO对象就是GO、函数执行上下文的VO对象是AO

初始化全局对象

js在执行代码前,会在堆内存中创建一个全局对象:Global Object(GO)

  • 该对象所有的作用域(scope)都可以访问;
  • 里面会包含Date、Array、String、Number、setTimeout、setInterval等等;
  • 其中还有一个window属性指向自己;

在parser转成AST的过程中,会将全局定义的变量、函数等放入GlobalObject中,但不会赋值(变量的作用域提升)

  • JavaScript可以在变量声明前访问,但是值是undefined,浏览器会对函数进行特殊处理,使得其可以在定义前调用

在代码执行过程中,对变量赋值或者执行其他的函数

全局代码执行前:

image-20230323191418551

全局代码执行后:

image-20230323191040277

函数执行前:

image-20230323191504578

函数执行后:

image-20230323191448418

作用域和作用域链

作用域链:当进入到一个执行上下文时,执行上下文会关联一个作用域链,并根据代码类型,添加一系列的对象

1
2
3
4
5
6
7
8
function foo(age){
function bar(){
console.log(age)
}
return bar
}
var baz = foo(18)
baz()

image-20230323192141405

  • 其作用域链中有两个,分别是foo的作用域、全局作用域

注:作用域链是在函数声明是产生的,与调用时刻无关

回流reflow和重绘repaint

回流

第一次确定节点的大小和位置,称为布局

后续对节点的大小、位置修改并重新计算称为回流

引起回流的情况

  • DOM结构发生变化(添加或删除节点)
  • 改变布局(修改width、height、padding、font-size等)
  • 修改窗口大小
  • 调用getComputedStyle方法获取尺寸、位置星系、

避免回流的方法

修改样式时尽量一次性修改,比如通过cssText修改,或者通过添加class修改,而不是对css的各种属性分次修改

避免频繁的操作DOM,可以使用虚拟DOM,或在父元素中要操作的DOM完成,再一次性更新到DOM中

composite合成

绘制的过程中,浏览器会将布局后的元素绘制到多个图层中

默认情况下,标准流中的内容被绘制在同一个图层,对于某些特殊的属性,会创建一个新的图层,这些图层可以利用GPU加速绘制

可以形成新的合成层的属性:3D transforms、video、canvas、iframe、opacity、position:fixed、will-change、animation或transition设置了opacity、transform

分层确实可以提高性能,但以内存管理为代价,因此不能作为Web性能优化的一部分过渡使用

重绘

第一次渲染内容称为绘制

后续重新渲染称为重绘

引起重绘的情况

修改背景颜色、文字颜色、边框颜色、样式等

注:回流一定引起重绘、因此回流是一件很消耗性能的事情,所以开发中要避免回流

网页解析过程:

image-20230322214252101

image-20230322215014488

HTML解析过程

默认情况下服务器给浏览器返回index.html文件,因此解析HTML是所有步骤的开始,解析HTML过程会构建Dom Tree

image-20230322215643759

生成CSS规则

在解析过程中,如果遇到CSS的link元素,会有浏览器下载对应的CSS文件(不会影响DOM)的解析

对CSS下载完成后,会对CSS文件进行解析,解析出规则树,即CSSOM

image-20230322220941332

构建Render Tree

当DOM Tree和CSSOM Tree有了过后就可以两者结合构建Render Tree了

image-20230322221500301

注:link元素不会阻塞DOM Tree的构建,但会阻塞Render Tree的构建

​ Render Tree和Dom Tree并不是一一对应的关系,对于display为none的元素,不会出现在Render Tree中

布局和绘制

渲染树会显示每个节点的样式,但不会给出每个节点的尺寸和位置,布局就是确定渲染树中每个节点的宽度、高度和位置信息

绘制就是将布局阶段计算的每个frame转为屏幕上实际的像素点,包括将元素的可见部分进行绘制,比如文本、颜色、边框、阴影、替换元素

script元素和页面解析的关系

在HTML解析过程中,遇到script元素是不能继续构建DOM Tree的;会首先下载JavaScript代码,并执行脚本,等脚本执行结束后,继续解析HTML,构建DOM Tree

原因: JavaScript的一个作用是操作DOM,并修改DOM,如果等DOM Tree构建完成后执行JavaScript,会引起严重的回流和重绘,影像性能

但是: 目前的Vue/React开发模式,脚本比HTML页面更重,等到JavaScript解析会造成页面阻塞,在脚本执行完成钱,用户在界面上看不到任何东西

script解析优化:defer、async

defer: 浏览器不需要等到脚本下载,继续解析HTML,构建DOM Tree

​ 不会阻塞DOm Tree的构建,等到DOM Tree构建完成后,在DOMContentLoaded事件之前执行代码

​ 带多个defer的脚本按顺序执行

​ 从某种角度来说,defer可以提高性能,并且推荐放到head元素中

​ 注:defer仅适用于外部脚本,对于script默认内容会忽略

async: 脚本完全独立

​ 浏览器不会因async阻塞

​ async不能保证不同脚本之间的顺序,独立下载、运行

​ async不能保证在DOMcontentLoaded之前或之后运行

defer通常用于文档解析后会操作DOM的JavaScript代码,并且对多个script文件有顺序要求的

async通常用于独立的脚本,对其他脚本,DOM没有依赖的

回流和重绘(见浏览器优化)

jQuery是一个快速、小型且功能丰富的JavaScript

优点:丰富的功能(DOM操作、过滤器、时间、动画、Ajax等)、编写更少可读的代码提高开发人员的工作效率、跨浏览器支持(IE9+)

缺点:jQuery代码库一直在增长(jQuery1.5超过200KB)、不支持组件化开发、jQuery更适合组件化开发,当涉及到复杂的项目时,能力有限

jQuery使用

jQuery监听文档加载

1
2
3
4
$( document ).ready( handler )  :  //deprecated 
$( "document" ).ready( handler ) : //deprecated
$().ready( handler ) ://deprecated
$( handler ) ://推荐用这种写法,其它可以使用但是不推荐

jQuery与其他库变量名冲突(jQuery别名$的冲突)

1
jQuery.noConflict()

jQuery对象(类数组对象——可通过索引访问)

  • 如果传入假值:返回一个空的集合。
  • 如果传入选择器:返回在在documnet中所匹配到元素的集合。
  • 如果传入元素:返回包含该元素的集合。
  • 如果传入HTML字符串,返回包含新创建元素的集合。
  • 如果传入回调函数:返回的是包含document元素集合, 并且当文档加载完成会回调该函数。
  • 因为函数也是对象,所以该函数还包含了很多已封装好的方法。如:jQuery.noConflict、jQuery.ready等
1
2
3
4
5
6
const obj = {
"name":"oww"
}
const obj1 = $() // 创建空对象
const obj2 = $(obj) // 通过j对象创建jQuery对象
const obj3 = $('li') // 通过选择器创建jQuery对象

jQuery对象与Element对象的区别:jQuery会包含所选择到的对象,可以通过$(element)和obj3[index]之间相互转换,转换后具有不同的函数

jQuery选择器

常规选择器:通用选择器(*)、属性/后代/基本(id、class、元素)/兄弟/交集/伪元素(伪类不行)/可见选择器(:visible,:hidden)、jQuery扩展选择器(:eq(),:odd,:even:first:last)—eq是用索引、odd奇数、even偶数

jQuery过滤器

  • eq(index): 从匹配元素的集合中,取索引处的元素, eq全称(equal 等于),返回jQuery对象。
  • first() : 从匹配元素的集合中,取第一个元素,返回jQuery对象。
  • last(): 从匹配元素的集合中,取最后一个元素,返回jQuery对象。
  • not(selector): 从匹配元素的集合中,删除匹配的元素,返回jQuery对象。
  • filter(selector): 从匹配元素的集合中,过滤出匹配的元素,返回jQuery对象。
  • .find(selector): 从匹配元素集合中,找到匹配的后代元素,返回jQuery对象。
  • is(selector|element| . ): 根据选择器、元素等检查当前匹配到元素的集合。集合中至少有一个与给定参数匹配则返回true。  8.odd() :将匹配到元素的集合减少为集合中的奇数,从零开始编号,返回jQuery对象。
  • even():将匹配到元素的集合减少到集合中的偶数,从零开始编号,返回jQuery对象。
  • 支持链式调用
  • on添加监听
1
2
3
4
5
6
7
const $li = $('li')
$li.click((event)=>{
console.log("1", event)
})
$li.on('click',(event)=>{
console.log("2", event)
})

jQuery函数

jQuery对文本的操作

.text()、.text(text)

1
2
3
4
5
6
7
8
9
10
11
<div class="active list div">
<li><p>1</pp></li>
<li value="2">2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li><span>9</span></li>
</div>
1
2
3
4
const $li = $('li')
console.log(typeof $li.text()) // string 会把其本身和子元素的text拼接为1个string
console.log($li.text()) // 123456789
$li.text("aaa") // 更改所有选择元素的text

.html、.html(htmlString)

1
2
3
const $li = $('li')
console.log($li.html()) // <p>1</p> 获取选中第一个元素的html
$li.html("<span>10<span>") //给所有选中元素设置innerHtml

.val()、.val(value)

1
2
3
const $li = $('li')
console.log($li.odd().val()) // 2 获取选中第一个元素的value
$li.val(12)

jQuery对css的操作

.width()、.height()、.css(prppertyName) 获取选中第一个元素的指定样式属性值

.css(propertyName,value)、.css({propertyName1:value1,propertyName2,value2}) 为每一个匹配到的元素设置属性

jQuery对class的操作

.addClass(className)、.addClass(classNames)、.addClass(funcntion)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const $li = $('li')
$li.addClass(function(index){
if(index%2===0){
this.classList.add("active1")
}else{
this.classList.add("active2")
}

}) // 通过this手动给选中的元素添加类
$li.addClass((index)=>{
if(index%2===0){
return "active1"
}else{
return "active2"
}

}) // 通过返回类名,jQuery给元素添加类

.hasClass(className) 判断匹配到的元素是否分配了该类

1
2
3
4
const $div = $('div')
console.log($div.hasClass("active")) // true
console.log($div.hasClass("active list")) // true
console.log($div.hasClass("active div")) // false 该方法是判断所选中元素是否包含要查询的类的字符串

.removeClass()、.removeClass(className)、.removeClass(classNames)、.removeClass(function) 删除指定的类

.toggleClass()、.toggleClass(className[,state])、.toggleClass(classNames[,state]) 删除或添加指定的类

this的绑定

一. 整理this的绑定规则

  • 默认绑定:独立函数调用,函数没有被绑定到某个对象上进行调用

  • 隐式绑定:通过某个对象发起的函数调用,在调用对象内部有一个对函数的引用。

  • 显式绑定:明确this指向的对象,第一个参数相同并要求传入一个对象。

    • apply/call
    • bind
  • new绑定:

    • 创建一个全新对象
    • 新对象被执行prototype链接
    • 新对象绑定到函数调用的this
    • 如果函数没有返回其他对象,表达式会返回这个对象

默认绑定

默认绑定this一般情况下是window

在严格模式下this为undefined

es6后的class中函数默认为严格模式,即this为undefined

1
2
3
4
5
6
7
8
9
10
11
class Test{
constructor(){

}
foo(){
console.log(this)
}
}
const test = new Test()
const bar = test.foo
bar() // undefined

babel会将js设置为严格模式——在React框架下,隐式绑定都为undefined

1
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
1
2
3
4
function foo(){
console.log(this)
}
foo() // undefined

二. 说出apply、call、bind函数的用法和区别

​ 用法:

  • ​ apply

    ​ 第一个参数: 绑定this

    ​ 第二个参数: 传入额外的实参, 以数组的形式

  • ​ call

    ​ 第一个参数: 绑定this

    ​ 参数列表: 后续的参数以多参数的形式传递, 会作为实参

  • ​ bind(不希望obj对象身上有函数)

    1
    2
        var bar = foo.bind(obj)
    bar() // this -> obj

    区别:

  • call、apply和bind都可以改变函数的this指向

  • call、apply和bind第一个参数的是this要指向的对象

  • call、apply和bind都可以后续为函数传参,apply是将参数并成一个数组,call和bind是将参数依次列出

  • call、apply都是直接调用,bind生成的this指向改变函数需要手动调用。

​ 绑定优先级:

​ 默认绑定的优先级最低、显示绑定的优先级高于隐式绑定、new绑定的优先级高于隐式绑定、new绑定优先级高于bind

​ new绑定和call、apply不可以一起使用

​ 注:

​ 显示绑定传入null或undefined,显示绑定会忽略使用默认规则

1
2
3
4
foo.call(null)
foo.call(undefined)
var bar = foo.bind(null)
bar() // 都是windows

三. 说出箭头函数的各种用法和简写

  • 基本写法

    • ():函数的参数

    • {}:函数的执行体

      1
      2
      3
      4
      var foo3 = (name, age) => {
      console.log("箭头函数的函数体")
      console.log(name, age)
      }
  • 优化写法

    • 只有一个参数时, 可以省略()

      1
      2
      3
      names.forEach(item => {
      console.log(item)
      })
  • 只有一行代码时, 可以省略{}

    1
    names.forEach(item => console.log(item))
  • 只要一行代码时, 表达式的返回值会作为箭头函数默认返回值, 所以可以省略return

    1
    2
    var newNums = nums.filter(item => item % 2 === 0)
    var newNums = nums.filter(item => item % 2 === 0)
  • 如果箭头函数默认返回的是对象, 在省略{}的时候, 对象必须使用()包裹 () => ({name: “why”})

    1
    2
    3
    4
    var arrFn = () => ["abc", "cba"]
    var arrFn = () => {} // 注意: 这里是{}执行体
    var arrFn = () => ({ name: "why" })
    console.log(arrFn())

四. 完成this的面试题解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var name = "window";
var person = {
name: "person",
sayName: function () {
console.log(this.name);
}
};
function sayName() {
var sss = person.sayName;
sss(); // window
person.sayName(); // person
(person.sayName)(); // person
(b = person.sayName)(); // window
}
sayName();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var name = 'window'
var person1 = {
name: 'person1',
foo1: function () {
console.log(this.name)
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => {
console.log(this.name)
}
}
}

var person2 = { name: 'person2' }

// person1.foo1(); // person1
// person1.foo1.call(person2); // person2

// person1.foo2(); // window
// person1.foo2.call(person2); // window

// person1.foo3()(); // window
// person1.foo3.call(person2)(); // window
// person1.foo3().call(person2); // person2

// person1.foo4()(); // person1
// person1.foo4.call(person2)(); // person2
// person1.foo4().call(person2); // person1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var name = 'window'
function Person (name) {
this.name = name
this.foo1 = function () {
console.log(this.name)
},
this.foo2 = () => console.log(this.name),
this.foo3 = function () {
return function () {
console.log(this.name)
}
},
this.foo4 = function () {
return () => {
console.log(this.name)
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')

// person1.foo1() // person1
// person1.foo1.call(person2) // person2

// person1.foo2() // person1
// person1.foo2.call(person2) // person1

// person1.foo3()() // window
// person1.foo3.call(person2)() // window
// person1.foo3().call(person2) // person2

// person1.foo4()() // person1
// person1.foo4.call(person2)() // person2
// person1.foo4().call(person2) // person1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var name = 'window'
function Person (name) {
this.name = name
this.obj = {
name: 'obj',
foo1: function () {
return function () {
console.log(this.name)
}
},
foo2: function () {
return () => {
console.log(this.name)
}
}
}
}
var person1 = new Person('person1')
var person2 = new Person('person2')

// person1.obj.foo1()() // window
// person1.obj.foo1.call(person2)() // window
// person1.obj.foo1().call(person2) // person2

// person1.obj.foo2()() // obj
// person1.obj.foo2.call(person2)() // person2
// person1.obj.foo2().call(person2) // obj

防抖与节流

防抖

当事件触发时,相应的函数并不会立即触发,而是会等待一定的时间

当事件密集触发时,函数的触发会被频繁的推迟

防抖的应用场景:

输入框中频繁的输入内容,搜索或提交信息

平凡的点击按钮,触发某个事件

用户缩放浏览器的resize事件

如:输入macbook时,输入m联想,连续输入macbook不联想,在输入完一段时间后联想

防抖的实现:

以input输入为例:

html代码:

1
2
<input type="text">
<button id="cancel">取消</button>

基本实现: