1.给div的父级添加相对定位(position: relative),//可以不知道元素自身宽高
自己position: absolute;left:50%;top:50%;transform: translate(-50%, -50%);
2.给div的父级添加相对定位(position: relative),
自己position: absolute;top left right bottom 都设置0,margin: auto;
3.弹性盒主轴侧轴居中:display:flex;align-items:center;justify-content:center;
4.通过定位+maring偏移,给div的父级添加相对定位(position: relative),//元素自身宽高必须知道
自己position:absolute;left:50%;top:50%;margin-left: 负的宽度的一半;margin-top:负的高度的一半;
设置css:fixed固定定位
// 判断滚动条滚动的距离大于导航条距顶部的距离,来判断是否实现吸顶,然后addClass添加样式
a{
&:hover {
} //这里&代表它的上一级就是a
}
# ::before和::after必须配合content属性来使用,content用来定义插入的内容,content必须有值,至少是空。 默认情况下,伪类元素的display是默认值inline,可以通过设置display:block来改变其显示。
// 1.清除浮动 : 在浮动元素后面添加一个空的Div标签,然后在设置它的清除浮动要是,使用after伪元素
// 2.常见消息框 : 伪类content:' ' 伪类4条边必须宽度相同,而且其他三条边为transparent 可以通过设置定位元素left,top值为50%,translate(-50%,-50%) 来使任意宽高的元素居中。
div::before{
content:' ';
}
// 3.阴影 : 通过设置before,after不同位置,不同旋转角度,要保证伪类的颜色及z-index
div.outer::before,div.outer::after{
content:'';
z-index:1;
width:50%;
height:3px;
position:absolute;
left:10px;
bottom:7px;
background-color:transparent;
box-shadow:5px 5px 10px rgba(0,0,0,0.5);
-webkit-transform:rotate(-3deg);
}
4.做出各种图形效果
star-five:before {
border-bottom: 80px solid red;
border-left: 30px solid transparent;
border-right: 30px solid transparent;
position: absolute;
height: 0;
width: 0;
top: -45px;
left: -65px;
content: '';
transform: rotate(-35deg);
}
#star-five:after {
width: 0;
height: 0;
border-left: 100px solid transparent;
border-right: 100px solid transparent;
border-bottom: 70px solid yellow;
top: 7px;
left: -110px;
position: absolute;
display: block;
content: '';
transform: rotate(-70deg);
}
1、第一优先级:无条件优先的属性只需要在属性后面使用!important。它会覆盖页面内任何位置定义的元素样式。(ie6支持上有些bug)。
2、第一等:内联样式,如:style="color:red;",权值为1000.(该方法会造成css难以管理,所以不推荐使用)
3、第二等:ID选择器,如:#header,权值为0100.
4、第三等:类、伪类、属性选择器如:.bar, 权值为0010.
5、第四等:标签、伪元素选择器,如:div ::first-line 权值为0001.
6、通配符,子选择器,相邻选择器等。如*,>,+, 权值为0000.
7、继承的样式没有权值。
当选择器冲突时,权重高的生效;当权重相同时,写在后头的会把前面的覆盖。
IE: trident内核
Firefox:gecko内核
Safari:webkit内核
Opera:以前是presto内核,现在已改用Google Chrome的Blink内核
Chrome:Blink(基于webkit,谷歌和欧朋共同开发)
# 告诉浏览器以何种规范解析页面
为了使标准模式产生之前的页面可以正常显示,就出现了怪异模式来渲染之前的页面
# 在标准盒模型下,width和height是内容区域即content的width和height
在标准模式下,一个块的总宽度= width + margin(左右) + padding(左右) + border(左右)
# 而IE盒模型或怪异盒模型显而易见的区别就是,width和height除了content区域外,还包含padding和border
一个块的总宽度= width + margin(左右)(即width已经包含了padding和border值)
2. 可以设置行内元素的高宽
在标准模式下,给span等行内元素设置wdith和height都不会生效,而在怪异模式下,则会生效。
3. 可设置百分比的高度
在标准模式下,一个元素的高度是由其包含的内容来决定的,如果父元素没有设置高度,子元素设置一个百分比的高度是无效的。
4. 用margin:0 auto设置水平居中在IE下会失效
使用margin:0 auto在标准模式下可以使元素水平居中,但在怪异模式下却会失效, 怪异模式下的解决办法,用text一align属性:body{
text一align:center};#content{
text一align:left}
5. 怪异模式下设置图片的padding会失效
6. 怪异模式下Table中的字体属性不能继承上层的设置
7. 怪异模式下white一space:pre会失效
1.改版方便,只需要改css文件
2.页面加载速度更快,结构化清晰,页面显示简洁
3.表现与结构分离
4.利于seo优化
渐进增强:先针对低版本的浏览器进行构建,然后再针对高版本浏览器进行改进 # 渐进增强更合理:关注内容本身)
优雅降级:一开始就针对高版本浏览器构建页面,然后再向下兼容 # 不推荐 有些差异会被忽略掉
https://blog.csdn.net/JsRoccketq/article/details/100832492?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param
1. CDN缓存更方便 // 主要技术:内容存储和分布
2.突破浏览器并发机制
3.节约cookie带宽
4.节约主域名连接数,优化页面响应速度
5.防止不必要的安全问题
网页标准的制定使得web发展的更加健康,提高网站的易用性
1.开发者遵循统一标准,降低了开发难度和开发成本
2.SEO优化网页更加方便,不会因为滥用代码出现各种bug和安全问题
#cookie
cookie的作用是与服务器进行交互,作为HTTP规范的一部分存在,大小受限,每次请求新的页面cookie都会被发送(浪费带宽)cookie还需要指定作用域,不可以跨域调用,没有自己的方法 需要封装,数据会过期
#localstorage
本地存储数据,存储量更大,有自己的方法get/set/removeItem clear方法,数据不会自己过期
#sessionstorage
和localstorage一样,但是它存储的数据只在当前会话页面有效,会话关闭数据销毁
#src
src指向外部资源 替换当前元素,请求src资源时会将资源下载到对应文档内,浏览器解析到该元素时会暂停其他资源的下载处理
#href
href指向网络资源所在位置,建立和当前元素(文档)之间的联系,浏览器识别后会并行下载资源,不会停止对当前文档的处理
png jpg jpeg gif svg
# Webp
谷歌开发的一种旨在加快图片加载速度的图片格式,图片体积比jpeg小大约40%,节省大量的服务器带宽资源和数据空间
https://www.jianshu.com/p/94b643a46827
#给元素增加语义化属性,描述这个元素
微格式,microformats,它是一种利用HTML的class属性来对网页添加附加信息的方法,是一种开放的数据格式。
微格式是一种网络技术,用于将传统网页中的标签转化成元数据(描述数据的数据)以供机器和人阅读和使用。
DNS缓存 CDN缓存 浏览器缓存 服务器缓存
1.图片懒加载
2.图片预加载
3.css图片 用csssprite,SVGsprite iconfont Base64
4.图片过大可以使用特殊编码图片,加载时优先加载一张压缩过的图片,提高用户体验
background-color:blue; 各个浏览器都认识,这里给firefox用;
background-color:red9;9所有的ie浏览器可识别;
background-color:yellow0; 0 是留给ie8的,最新版opera也认识,后面自有hack写了给opera认的,所以,0我们就认为是给ie8留的;
+background-color:pink; + ie7定了;
_background-color:orange; _专门留给神奇的ie6;
:root #test {
background-color:purple9; } :root是给ie9的,网上流传了个版本是 :root #test {
background- color:purple0;},这个,新版opera也认识,所以经笔者反复验证最终ie9特有的为:root 选择符 {
属性9;}
@media all and (min-width:0px){
#test {
background-color:black0;} } 这个是老是跟ie抢着认0的神奇的opera,必须加个0,不然firefox,chrome,safari也都认识。。。
@media screen and (-webkit-min-device-pixel-ratio:0){
#test {
background-color:gray;} }最后这个是浏览器新贵chrome和safari的。
img{
display:table-cell;
text-align:center;
vertical-align:middle;
}
# less和sass的相同之处
Less和Sass在语法上有些共性,比如下面这些:
1、混入(Mixins)——class中的class;
2、参数混入——可以传递参数的class,就像函数一样;
3、嵌套规则——Class中嵌套class,从而减少重复的代码;
4、运算——CSS中用上数学;
5、颜色功能——可以编辑颜色;
6、名字空间(namespace)——分组样式,从而可以被调用;
7、作用域——局部修改样式;
8、JavaScript 赋值——在CSS中使用JavaScript表达式赋值。
# less和sass的区别
Less和Sass的主要不同就是他们的实现方式。
Less是基于JavaScript,是在客户端处理的。
Sass是基于Ruby的,是在服务器端处理的。
关于变量在Less和Sass中的唯一区别就是Less用@,Sass用$。
link属于XHTML标签,除了加载css外,还能用于定义RSS,定义rel连接属性等作用,而@import是css提供的,只能用于加载css
link加载内容是与页面同时加载的,而@import需要页面完全加载完毕之后再加载
@import是css2.1提出的,只有在IE5以上才能被识别,而link是XHTML标签,无兼容问题
link支持使用JavaScript控制Dom去改变样式,而@import不支持
link方式引入的样式的权重高于@import
flex-direction: row水平 | row-reverse | column垂直 | column-reverse 决定主轴的方向(即项目的排列方向)
flex-wrap: nowrap|wrap|wrap-reverse换行,第一行在下方 定义子元素是否换行显示
flex-flow: 是flex-direction和flex-wrap的简写形式。默认值为:row nowrap。
justify-content: flex-start|flex-end|center|space-between|space-around; 定义了项目在主轴上的对齐方式。
align-items: flex-start|flex-end|center|baseline|stretch; 定义项目在交叉轴上如何对齐。
flex: order排列顺序|flex-grow放大比例|flex-shrink缩小|flex-basis|flex|align-self
flex属性是flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选 flex:1 flex-grow
align-self: auto | flex-start | flex-end | center | baseline | stretch;
5.flex-grow:规定项目将相对于其他灵活的项目进行扩展的量。
6.flex-shrink:规定项目将相对于其他灵活的项目进行收缩的量
#(有个布局是:右边是固定宽度,左边是自适应,如何实现,如果是答了flex:1,那么是flex下面的那个属性设置1)
flex是由 flex-grow:1父容器在主轴上还有多少剩余空间,
flex-shrink当父元素的宽度大于所有子元素的宽度的和时(即父元素会有剩余空间),子元素如何分配父元素的剩余空间
flex-basis基准值组成
# flex 怎么实现1个盒子垂直居中 如何实现四个盒子水平均匀分布
父容器 display:flex; justify-content: center; align-items: center;
display: flex; justify-content:space-evenly; align-items: center;
1.语义化标签:header、footer、section、nav、aside、article
2.增强型表单:input 的多个 type
3.音频视频:audio、video
4.本地存储:localStorage - 没有时间限制的数据存储;
sessionStorage - 针对一个 session 的数据存储,当用户关闭浏览器窗口后,数据会被删除
用正确的标签做正确的事情。
html语义化让页面的内容结构化,结构更清晰,便于对浏览器、搜索引擎解析;
即使在没有样式CSS情况下也以一种文档格式显示,并且是容易阅读的;
搜索引擎的爬虫也依赖于HTML标记来确定上下文和各个关键字的权重,利于SEO;
使阅读源代码的人对网站更容易将网站分块,便于阅读维护理解。
1.选择器
伪类选择器(:nth-child)
伪元素选择器(:before :after)
2.背景和边框
background-size:规定背景图片的尺寸(cover:填充;100% 100%:拉伸)
border-radius:圆角
border-image:边框图片
3.文本效果
text-shadow 向文本添加阴影
4.2D/3D 转换
transform
translate 平移
rotate 旋转
scale() 缩放
skew() 扭曲
5.动画、过渡
transition 过渡
animation 动画
@keyframes 规则用于创建动画。在 @keyframes 中规定某项 CSS 样式,就能创建由当前样式逐渐改为新样式的动画效果。
父元素高度自适应,子元素 float 后,造成父元素高度为0,称为高度塌陷问题
1.给高度塌陷的元素添加 overflow: hidden;
形成了BFC,会形成一个独立的空间,在这个空间里面计算高度时,浮动元素也参与计算。
缺陷: 溢出隐藏,隐藏掉元素外的内容
2.在高度塌陷的元素里面的最下面添加一个空的div
给空的div添加属性 clear: both;
缺陷: 添加空标签,页面里面会出现很多无用的元素,导致代码冗余
3.万能清除法
.box1::after {
content: '.';
display: block;
height: 0;
overflow: hidden;
visibility: hidden;
clear: both;
}
Relitive: 相对于自身,会指引子元素相对于他自身进行定位
Absolute: 根据父元素的relitive进行定位
Fixed:现对于window窗口定位
Rem是基于根元素的字体大小发生改变,而em是基于body的字体大小发生改变
1.因为html5和css3引入视口的概念来代替显示器的物理尺寸。通过在meta标签上的设置,视口的长宽可以跟设备的物理分辨率相等,也可以不相等(以便手机上可以实现用两个手指来放大会缩小页面),可根据需要灵活掌握。在PC中,视口的长宽则是跟浏览器窗口的物理分辨率恒等的。
2.1vw等于视口宽度(viewport width)的百分之一,也就是说100vw就是视口的宽度。同理,1vh等于视30px改成5vw,意思就是窗口宽度的5%,同理10vw。
3.不过由于vw和vh是css3才支持的长度单位,所以在不支持css3的浏览器中是无效的口高度(viewport height)的百分之一。
Rem 因为rem 可以做到自适应,只需要设置一个全局js就可以全局应用
Document.documentElement.style.fontSize = document.documentElement.clientWidth / xxx + ‘px’;
Window.onresize = function(){
Document.documentElement.style.fontSize = document.documentElement.clientWidth / xxx + ‘px’;
}
# 插件 rem自动转px
Click事件,px和rem,touch事件
Border-color: red green blue pink;
https://segmentfault.com/a/1190000019259209
基本数据类型
ES5-------Number/Boolean/String/Undefined/Null
ES6新增---symbol
引用数据类型
Object
1种引用数据类型——Object(Object本质上是由一组无序的名值对组成的)。里面包含 function、Array、Date等。JavaScript不支持任何创建自定义类型的机制,而所有值最终都将是上述 8 种数据类型之一。
//原始数据类型:直接存储在栈(stack)中,占据空间小、大小固定,属于被频繁使用数据,所以放入栈中存储。
//引用数据类型:同时存储在栈(stack)和堆(heap)中,占据空间大、大小不固定。
引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。
# 检测方法4种
Object.prototype.toString.call()
**作用: 可以检测所有数据类型**
**所有数据类型都可以检测,而且非常正确**
语法: Object.prototype.toString.call( 'xxx'/11/[ ] )
返回值: [object Xxx], Xxx 就是对象的类型
constructor
**作用: 可以检测基本数据类型和引用数据类型**
**弊端: 把类的原型进行重写, 很有可能把之前的constructor覆盖 检测出来的结果就会不准确**
语法: ("xx")/([ ])/(function(){
}).constructor === String/Array/Function
返回值: true/false
instanceOf
**原理: 判断对象类型,基于原型链去判断(obj instanceof Object)**
**左边对象的原型链proto上是否有右边构造函数的proptotype属性**
**作用: 判断左边的对象是否是右边构造函数的实例**
**弊端: 用于引用类型的检测, 对于基本数据类型不生效**
语法: " "/[ ]/true instanceOf String/Array/Boolean
返回值: true/false
typeOf
**作用: 用于检测基本数据类型和函数**
**弊端: 引用数据类型(Arrary/function/object)只会返回Object, 不起作用**
语法: typeOf " "/[ ]/xx
返回值: "string"/"boolean"/"object" (无法区分)
Array.isArray(arr);
# 强制类型转换
转换为布尔值(调用Boolean()方法)
转换为数字(调用Number()、parseInt()和parseFloat()方法)
转换为字符串(调用.toString()或者String()方法)// 所有对象转字符串都是[object,object]
#隐式类型转换
+
==
!! object转布尔值均为true
##{}和[]的valueOf和toString的结果是什么
{
} 的 valueOf 结果为 {
} ,toString 的结果为 "[object Object]"
[] 的 valueOf 结果为 [] ,toString 的结果为 " "
首先 Undefined 和 Null 都是基本数据类型,这两个基本数据类型分别都只有一个值,就是 undefined 和 null。
// undefined 代表的含义是未定义,
// null 代表的含义是空对象(其实不是真的对象,请看下面的注意!)。
一般变量声明了但还没有定义的时候会返回 undefined,
null主要用于赋值给一些可能会返回对象的变量,作为初始化。
当我们对两种类型使用 typeof 进行判断的时候,Null 类型化会返回 “object”,这是一个历史遗留的问题。当我们使用双等号对两种类型的值进行比较时会返回 true,使用三个等号时会返回 false。
###&& 、 ||和!! 运算符分别能做什么
&& 叫逻辑与,在其操作数中找到第一个虚值表达式并返回它,如果没有找到任何虚值表达式,则返回最后一个真值表达式。它采用短路来防止不必要的工作。
// && 左边为true返回右边 左边为false 返回左边
|| 叫逻辑或,在其操作数中找到第一个真值表达式并返回它。这也使用了短路来防止不必要的工作。在支持 ES6 默认函数参数之前,它用于初始化函数中的默认参数值。
!! 运算符可以将右侧的值强制转换为布尔值,这也是将值转换为布尔值的一种简单方法
push() 从队尾添加,改变原数组
pop() 移除数组末尾最后一项,返回移除的项
shift() 删除数组第一项,返回删除元素的值,如果数组为空返回undefined
unshift() 添加头部,改变原数组
sort() 数组排序,参数为一个匿名函数,如果匿名函数返回正值,则升序排列,反之相反
reverse() 翻转数组项的顺序 原数组改变
concat() 将参数添加到原数组,将参数添加到数组的末尾,并返回一个新数组,不改变原数组
slice() 返回原数组中指定开始下标到结束下标之间的项组成的新数组,slice接受两个参数,如果只写一个参数,slice方法返回从该参数到数组末尾的所有项,如果有两个参数,该方法返回起始位置和结束位置之间的项,但不包括结束位置的项
splice() 可以实现删除,插入,替换 删除(可以删除任意属相的项,只需要指定2个参数,要删除的第一项的位置和要删除的项) 插入,替换(可以向指定位置插入任意数量的项,只需提供3个参数:起始位置,0(要删除的项),插入的项),splice()方法始终都会返回一个数组,数组中包括从原数组中删除的项,如果没有删除任何项则返回一个空数组
map() 对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组
some() 判断数组中是否存在满足条件的项,只要有一项满足条件,就返回true
every() 判断数组中每一项都是否满足条件,只有所有选项都满足条件,才会返回true
filter() 过滤功能,数组中的每一项运行给定函数,返回满足过滤条件组成的数组
forEach() 对数组进行循环遍历,对数组中的每一项运行给定函数,这个方法没有返回值,参数都是function类型,默认有传参功能,参数分别是,便利的数组内容,对应的索引,数组本身
indexOf() 接受两个参数,要查找的项和表示查找起点位置的索引,返回查找的项在数组的位置,没找到的情况下返回-1
join() 将数组内元素to String 用给定字符连接存入新字符串
map()方法主要用来遍历操作数组和对象
each()主要用于遍历jquery对象。
each()返回的是原来的数组,并不会新创建一个数组
map()方法会返回一个新的数组。如果在没有必要的情况下使用map,则有可能造成内存浪费。
Array.from()
Array.prototype.slice.call();
charAt(index) 返回指定索引字符串
charCodeAt() 返回指定索引的字符的unicode编码
concat() 在str后连接多个值,返回新字符串
indexof() 查找字符串片段,返回首个索引值,未找到返回-1
replace(regexp,newstr) 字符串替换,返回新字符串
slice() 字符串截取,返回截取片段
split() join的反向操作,用给定字符分割字符串存入新数组
substr() 截取指定个数字符串,返回新字符串
substring() 截取字符串
lowerCase() 将str转小写
toUpperCase() 将str转成大写
# 原因:由于浏览器的同源策略,即属于不同域的⻚面之间不能相互访问各自的⻚面内容。
# 哪些情况下产生跨域
1、域名不同,2、端口号不同,3、协议不同(http/https),4、域名和域名对应ip,5、主域名相同(127.0.01 和 localhost) 多域名匹配一个ip地址 6、子域名不同(一级和二级域名)
# 解决方法
// 1、后端代理 - 后端不存在跨域(后端代码脱离浏览器,后端是服务器端)
利用后端(自己公司的后端)去获取接口数据,将数据传给前端
// 2、jsonp原理** (json with padding) 数据填充的方式
利用浏览器的"漏洞" src不受同源策略的影响,可以请求任何链接,动态创建script标签,将事先写好的函数名传给服务器,供服务器使用
(1)script标签src属性不存在跨域
(2)get方式--传递函数名 --弊端
(3)callback回调函数(传递函数名)
jsonp是一种非正式传输协议,用于解决跨域问题流程:
1、创建一个全局函数
2、创建一个script标签
3、给script添加src
4、给src添加回调函数test(callback=test) callback是传给后端的一个参数
5、将script放到⻚面上
6、script请求完成,将自己从⻚面上删除
// 3、反向代理 proxy webpack配置
"proxy": {
"/index.php": {
"target": "http://qinqin.net",
"changeOrigin": true
}
}
// .CORS解决跨域(xhr2) nginx
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制
cors使跨站数据传输更加安全,减轻跨域http请求的风险,需要客户端和服务端协同处理
需要服务器(提供接口的源码里面)添加下面两句话。
header('Access-Control-Allow-Origin:*');
header('Access-Control-Allow-Method:POST,GET');
withCredentials属性(withCredentials是XMLHttpRequest的一个属性)
默认值为false,获取同域资源时设不设置没有影响
true:跨域请求时,会携带用户凭证(在需要跨域携带cookie时,要把withCredentials设置为true)
false:在跨域请求时,不会携带用户凭证,返回的response里也会忽略cookie
//解析:
默认情况下,一般浏览器的CORS跨域请求都是不会发送cookie等认证信息到服务端的,除非指定了xhr.withCredentials = true,但是只有客户端单方面的设置了这个值还不行,服务端也需要同意才可以,所以服务端也需要设置好返回头Access-Control-Allow-Credentials: true;还有一点要注意的,返回头Access-Control-Allow-Origin的值不能为星号,必须是指定的域,否则cookie等认证信息也是发送不了。
Fetch默认是不携带cookie的,携带cookie可以配置其credentials项为include /krəˈdenʃlz/
每个对象都可以有一个原型_proto_,这个原型还可以有它自己的原型,以此类推,形成一个原型链。查找特定属性的时候,我们先去这个对象里去找,如果没有的话就去它的原型对象里面去,如果还是没有的话再去向原型对象的原型对象里去寻找...... 这个操作被委托在整个原型链上
// 1、每一个函数都有一个prototype这个属性,而这个属性指向一个对象,这个对象我们叫做原型对象
作用: 节约内存 扩展属性和方法 可以实现类之间的继承
//2、proto 每一个对象都有一个proto属性 proto 指向创建自己的那个构造函数的原型对象 对象可以直接访问 proto 里面的属性和方法
//3、constructor:指向创建自己的那个构造函数
总结: 当我们创建一个构造函数的时候这个构造函数自带了一个prototype属性,而这个属性指向一个对象,也就是原型对象。 这个原型对象里面有一个constructor构造器,它的作用是指向创建自己的构造函数。除此之外 prototype还可以存放公共的属性和方法。 当我们实例化一个对象的时候(被new调用的时候),这个对象自带了一个 proto 属性,这 个proto 指向创建自己的构造函数的原型对象。可以使用这个原型对象里面的属性和方法
p.proto
p.constructor.prototype
Object.getPrototypeOf(p)
Array.prototype.
https://www.cnblogs.com/dillonmei/p/12578530.html
传统的javascript中只有对象,没有类的概念。它是基于原型的面向对象语言。原型对象特点就是将自身的属性共享给新对象。
// constructor: constructor()方法是类的默认方法,通过new命令生成对象实例 时,自动调用该方法。一个类必须有constructor()方法,如果没有显式定义,一个空的 constructor()方法会被默认添加。
class继承中,子类必须在constructor方法中调用super方法,否则新建实例时会报错。这是因为子类自己的this对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用super方法,子类就得不到this对象。
// ES6引入了Class(类)这个概念,通过class关键字可以 定义类。该关键字的出现使得其在对象写法上更加清晰,更像是一种面向对象的语言
//class 是 ES6的语法糖,本质还是JavaScript面向对象那套东西。最终还是会转换成构造函数的代码
//形式上强行模仿了java,c#,有点失去了JavaScript的个性
Class在语法上更贴合面向对象的写法。
Class实现继承更加易读、易理解
更易于写java等后端语言的使用。
本质是语法糖,使用prototype。 class本质还是构造函数
#一、JS构造函数
JS中的prototype:每一个构造函数都有的一个属性,能够用来向对象添加属性和方法。用来返回对象类型原型的引用。不需要显式声明,它是隐式存在的。
object.prototype.name = value
object.prototype.func = function() {
...}
object.prototype =object
1、原型法设计模式:现在有1个类A,我想要创建一个类B,这个类是以A为原型的,并且能进行扩展。我们称B的原型为A。
2、当一个对象被创建时,这个构造函数 将会把它的属性prototype赋给新对象的内部属性__proto__。这个__proto__被这个对象用来查找它的属性。
3、在外部不能通过prototype改变自定义类型的属性或方法,即当原型方法和对象方法在调用相同的属性和函数时,会执行对象方法里面的属性和函数。
#二、ES6中的构造语法——Class
ES6 的 class 属于一种“语法糖”
#三、语法糖
之所以叫做语法糖,不只是因为加糖前后的代码实现的功能一样,更重要的是,糖在不改变其所在位置的语法结构的前提下,实现了运行时的等价。也可以理解为,加糖前后的代码编译结果是一样的。加糖只是为了让代码的更加简洁,语义更加自然。
在class语法中:typeof MathHandle ----->function
MathHandle === MathHandle.prototype.constructor // JS中没有类,class本质上还是构造函数
function定义的方法(对象方法)有一个prototype属性,使用new生成的对象就没有这个prototype属性。也就是prototype属性是对象方法或者构造方法的专有属性。 prototype属性又指向了一个prototype对象,注意prototype属性与prototype对象是两个不同的东西,要注意区别。在prototype对象中又有一个constructor属性,这个constructor属性同样指向一个constructor对象,而这个constructor对象恰恰就是这个function函数本身。
es5 的继承是先创建子类的this,然后将父类的方法添加到子类的this上去;
es6 的继承是创建父类的this对象,然后再对this对象添加方法/属性。
# 而super方法就是用来创建父类this对象的。
实际上执行的是 super.sport.call(this);
如果子类没有定义constructor方法,constructor方法会被默认创建,并默认调用super函数
# 使用super
super既能当作方法调用也能当对象使用
当做方法调用时只有是在子类构造函数constructor方法内;
# 在 constructor 中必须调用 super 方法,因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工,而 super 就代表了父类的构造函数。super 虽然代表了父类 A 的构造函数,但是返回的是子类 B 的实例,即 super 内部的 this 指的是 B,因此 super() 在这里相当于 ```A.prototype.constructor.call(this, props)``。
当做属性使用时,在普通方法中,指向父类的原型对象;在静态方法中,指向父类。
// 当super在普通方法中 super === Person.prototype;
var F = fnction(){ }
var O = new F
1、创建一个空对象 obj
2、把obj的_proto_指向构造函数的原型对象prototype
// 设置原型链
obj.__proto__ = F.prototype
3、链接该对象到另一个对象 __proto__ //让构造函数F的this指向这个对象,执行函数的函数体
4、判断函数的返回值类型:如果是值类型 --》丢弃 返回对象
引用类型 --》 返回这个引用类型,替换掉这个对象 return this
new出来一个实例对象是否带有static属性 static用ES5怎么写
没有创建对象,也能使用属性和调用方法
用来形成静态代码块以优化程序性能。因为只会在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。
static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次
被static修饰的变量或者方法是独立于该类的任何对象,也就是说,这些变量和方法不属于任何一个实例对象,而是被类的实例对象所共享。
类第一次加载初始化的时候就去加载static部分,后面可以重新赋值
static用ES5怎么写:静态是通过类名直接调用 class A static B
直接用A.B 将B绑定在A上
instanceOf
**原理: 判断对象类型,基于原型链去判断(obj instanceof Object)**
**左边对象的原型链proto上是否有右边构造函数的proptotype属性**
**作用: 判断左边的对象是否是右边构造函数的实例**
**弊端: 用于引用类型的检测, 对于基本数据类型不生效**
语法: " "/[ ]/true instanceOf String/Array/Boolean
返回值: true/false
instanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype。
实现 instanceof:
首先获取类型的原型
然后获得对象的原型
然后一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null,因为原型链最终为 null
function myInstanceof(left, right) {
let prototype = right.prototype
left = left.__proto__
while (true) {
if (left === null || left === undefined)
return false
if (prototype === left)
return true
left = left.__proto__
}
}
instanceof (A,B) = {
var L = A.__proto__;
var R = B.prototype;
if(L === R) {
//A的内部属性__proto__指向B的原型对象
return true;
}
return false;
}
Array.isArray(arr);
# 通过改变原型指向实现的继承
//缺陷:因为改变原型指向的同时实现继承,直接初始化了属性,继承过来的属性的值都是一样的了,所以,这就是问题
//只能重新调用对象的属性进行重新赋值,
function Person(name,age) {
this.name=name;
this.age=age;
}
Person.prototype.sayHi=function () {
console.log("您好");
};
function Student(score) {
this.score=score;
}
//希望人的类别中的数据可以共享给学生---继承
Student.prototype=new Person("小明",10,"男","50kg");
var stu1=new Student("100");
console.log(stu1.name,stu1.age,stu1.sex,stu1.weight,stu1.score);
stu1.sayHi();
//构造函数继承
//借用构造函数:构造函数名字.call(当前对象,属性,属性,属性....);
//解决了属性继承,并且值不重复的问题
//缺陷:父级类别中原型的方法不能继承
function Person(name, age, sex, weight) {
this.name = name;
this.age = age;
this.sex = sex;
this.weight = weight;
}
Person.prototype.sayHi = function () {
console.log("您好");
};
function Student(name,age,sex,weight,score) {
//借用构造函数
Person.call(this,name,age,sex,weight);
this.score = score;
}
var stu1 = new Student("小明",10,"男","10kg","100");
console.log(stu1.name, stu1.age, stu1.sex, stu1.weight, stu1.score);
//组合继承:原型继承+借用构造函数继承
//属性和方法都被继承了
function Person(name,age,sex) {
this.name=name;
this.age=age;
this.sex=sex;
}
Person.prototype.sayHi=function () {
console.log("阿涅哈斯诶呦");
};
function Student(name,age,sex,score) {
//借用构造函数:属性值重复的问题
Person.call(this,name,age,sex);
this.score=score;
}
//改变原型指向----继承
Student.prototype=new Person();//不传值
Student.prototype.eat=function () {
console.log("吃东西");
};
var stu=new Student("小黑",20,"男","100分");
console.log(stu.name,stu.age,stu.sex,stu.score);
stu.sayHi();
stu.eat();
//ES6中的class继承
extends super两个关键字
子类必须在`constructor`方法中调用`super`方法,否则新建实例时会报错。这是因为子类自己的`this`对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用`super`方法,子类就得不到`this`对象。super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。
ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
//堆--引用类型地址传递
堆:动态分配的内存,大小不定,不会自动释放。**存放引用类型的值 先进后出FILO**
//引用类型: Object(Arrary, Date, Math,Function, Object)
访问方法: JS不能直接访问内存中的值, 只能操作对象的地址, 所以产生深/浅拷贝问题**
深/浅拷贝针对的是 **引用类型**
浅拷贝--新旧互相影响,改变的是地址
新值===原值, 只能拷贝一层
> 1. 数组方法 `slice截取`, `concat拼接`, `filter过滤` , `map`,
> 2. 对象方法 `Object.assign({ },obj)`, `Object.create(obj)`
> 3. 展开运算符`{…obj}`
深拷贝--新旧互不影响,改变的是值
新值=/=原值 , 可以拷贝多层
> 1. JSON序列化 **JSON.parse( JSON.stringify(obj) )** 对象-->字符串-->对象
// 缺陷:
1.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式,而不是对象的形式
2.如果obj里有RegExp(正则表达式的缩写)、Error对象,则序列化的结果将只得到空对象;
3、如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失;
4、如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
5、JSON.stringify()只能序列化对象的可枚举的自有属性,不可枚举的不能被复制
可枚举 :可枚举性(enumerable)用来控制所描述的属性,是否将被包括在for...in循环之中。具体来说,如果一个属性的enumerable为false,下面三个操作不会取到该属性。
- for..in循环
- Object.keys方法
- JSON.stringify方法
> 2. 原生实现
> - 递归+浅拷贝
> 3. 工具实现【 第三方封装库 】
> - loadsh _.cloneDeep(obj)
> - 缺点
> - Immutable
Object.create()
栈--基本类型值传递
栈:自动分配内存空间,系统自动释放,**存放基本类型的值和引用类型的地址 先进先出FIFO**
基本类型: Undefined、Null、Boolean、Number 和 String, Symbol(ES6新增)
访问方法: 按值访问, 直接操作内存中的值
为什么在react中要使用浅拷贝
redux中要求:状态是只读的,唯一且不可修改的,reducer必须是一个纯函数
-因为redux中数据不可更改,所以redux中的数据应该要拷贝 返回一个新值
作用域: 作用域是定义变量的区域,它有一套访问变量的规则,这套规则来管理浏览器引擎如何在当前作用域以及嵌套的作用域中根据变量(标识符)进行变量查找。
作用域链: 作用域链的作用是保证对执行环境有权访问的所有变量和函数的有序访问,通过作用域链,我们可以访问到外层环境的变量和
函数。//函数被创建时,作用域链被创建
作用域链的本质上是一个指向变量对象的指针列表。
变量对象是一个包含了执行环境中所有变量和函数的对象。
作用域链的前端始终都是当前执行上下文的变量对象。
全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最后一个对象。
当我们查找一个变量时,如果当前执行环境中没有找到,我们可以沿着作用域链向上查找。
# 变量的使用:作用域内部查找,没有找到依次向上查找,全局作用域还没有找到的时候--报错_is no defined
作用域查找机制只能由内到外
# 变量赋值:作用域内部一次查找变量,没有一次向上查找,全局没有,js会将a变为伪全局变量
# 闭包原理:
定义在一个函数内部的函数(函数嵌套函数),闭包就是将函数内部和函数外部连接起来的一座桥梁。
打破了作用域链的规则 闭包就是能够读取其他函数内部变量的函数
# 作用:
a:可以读取函数内部的局部变量
b:让这些变量始终保持在内存当中
3注意:由于闭包会使得函数中的变量都被保存在内存当中,内存会消耗很大,所以不能够滥用闭包,否则会造成网⻚性能的问题 闭包
# 优点:
1、使用闭包是不会污染全局环境,2、方便进行模块化开发,3、减少形参个数,延长了形参的生命周期,
# 坏处
1、就是不恰当使用会造成内存泄漏
闭包的不适用于返回闭包的 函数是个特别大的函数,很多高级应用都要依靠闭包实现.
# 使用场景:
1、通过循环给页面上多个dom节点绑定事件 。
2、封装私有变量(计数器)
3、延续局部变量的寿命,
4、高阶组件
5、函数防抖
模块化的就是以闭包为基础构建的;
/*
* fn [function] 需要防抖的函数
* delay [number] 毫秒,防抖期限值
*/
function debounce(fn,delay){
let timer = null
//借助闭包
return function() {
if(timer){
clearTimeout(timer) //进入该分支语句,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时
timer = setTimeOut(fn,delay)
}else{
timer = setTimeOut(fn,delay) // 进入该分支说明当前并没有在计时,那么就开始一个计时
}
}
}
垃圾回收机制:Js具有自动垃圾回收机制。垃圾收集器会按照固定时间间隔周期性的执行。JS中最常见的垃圾回收方式是标记清除。
# 什么是垃圾
一般来说没有被引用的对象就是垃圾,是需要被清除的,例外:如果几个对象相互引用形成一个环,但根访问不到它们,这几个对象也是垃圾
# 捡垃圾
// 1.标记-清除算法
标记清除是JavaScript中最常用的垃圾回收方式。它的实现方式是这样的,当代码执行在一个环境中时,每声明一个变量,就会对该变量做一个标记,例如标记一个进入执行环境;当代码执行进入另一个环境中时,也就是说要离开上一个环境,这时对上一个环境中的变量做一个标记,例如标记一个离开执行环境,等到垃圾回收执行时,会根据标记来决定要清除哪些变量。
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中的变量引用的变量标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后,垃圾收集器完成内存除工作,销毁那些带标记的值并回收他们所占用的内存空间。
// 2.引用计数
引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1.相反,如果包含对这个值引用的变量又取的了另一个值,则这个值的引用次数减1.当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间收回来。这样,当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。
// 用引用计数法会存在内存泄露
内存泄漏(Memory Leak):是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
1.意外的全局变量 //使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收
2.被遗忘的计时器或回调函数 //设置了setInterval定时器,而忘记清除,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收
3.脱离 DOM 的引用 //获取一个DOM元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回收
4.闭包 //不合理的使用闭包,从而导致某些变量一直被留在内存当中
v8 的垃圾回收机制基于分代回收机制,这个机制又基于世代假说,这个假说有两个特点,一是新生的对象容易早死,另一个是不死的对象会活得更久。基于这个假说,v8 引擎将内存分为了新生代和老生代。
新创建的对象或者只经历过一次的垃圾回收的对象被称为新生代。经历过多次垃圾回收的对象被称为老生代。
新生代被分为 From 和 To 两个空间,To 一般是闲置的。当 From 空间满了的时候会执行 Scavenge 算法进行垃圾回收。当我们执行垃圾回收算法的时候应用逻辑将会停止,等垃圾回收结束后再继续执行。这个算法分为三步:
(1)首先检查 From 空间的存活对象,如果对象存活则判断对象是否满足晋升到老生代的条件,如果满足条件则晋升到老生代。如果不满足条件则移动 To 空间。
(2)如果对象不存活,则释放对象的空间。
(3)最后将 From 空间和 To 空间角色进行交换。
新生代对象晋升到老生代有两个条件:
(1)第一个是判断是对象否已经经过一次 Scavenge 回收。若经历过,则将对象从 From 空间复制到老生代中;若没有经历,则复制到 To 空间。
(2)第二个是 To 空间的内存使用占比是否超过限制。当对象从 From 空间复制到 To 空间时,若 To 空间使用超过 25%,则对象直接晋升到老生代中。设置 25% 的原因主要是因为算法结束后,两个空间结束后会交换位置,如果 To 空间的内存太小,会影响后续的内存分配。
老生代采用了标记清除法和标记压缩法。标记清除法首先会对内存中存活的对象进行标记,标记结束后清除掉那些没有标记的对象。由于标记清除后会造成很多的内存碎片,不便于后面的内存分配。所以了解决内存碎片的问题引入了标记压缩法。
由于在进行垃圾回收的时候会暂停应用的逻辑,对于新生代方法由于内存小,每次停顿的时间不会太长,但对于老生代来说每次垃圾回收的时间长,停顿会造成很大的影响。 为了解决这个问题 V8 引入了增量标记的方法,将一次停顿进行的过程分为了多步,每次执行完一小步就让运行逻辑执行一会,就这样交替运行
# this代表函数运行时,自动生成的一个内部对象,只能在函数内部使用,随着函数使用场合的不同,this的值会发生变化。但有一个总的原则就是,谁调用它,它就指向谁。一般在这几种情况下会用到它,
1.单纯的函数调用;
2.作为对象方法的调用;
3.作为构造函数调用;
4.apply调用,apply()是函数对象的一个方法,它的作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象,当它的参数为空时,默认调用全局对象
# this在js中是一个动态指针,this的指向是由代码执行时决定的(在函数定义的时候是没办法确定指向的)
//this永远指向的是最后调用它的对象
普通函数 --> 调用者
构造函数 --> 实例对象
全局函数/只执行函数/计时器 --> window
箭头函数 --> 父级环境对象
严格模式中 --> 不指向window
原型函数 --> 调用者
鼠标事件中的this指向被侦听的对象
2、如果函数中有this,但是函数本身没有被父级(上一级)对象调用,那么就指向window
3、如果函数中有this,且函数本身被父级(上一级)对象调用,那么this就指向上一级对象
4、如果函数中有this,且函数中有多个对象,尽管函数被最外层对象调用,但this仍然指向父级(上一级)对象
5、构造函数中this,指向构造函数实例,如果创建的实例赋给对象,那么等于复制了一份给对象,该对象也拥有实例中的this(new出来的构造函数可以改变this指向)
6、构造函数中带return,返回值若是对象,this指向的是那个返回的对象,返回值若是null,this还是指向那个构造函数实例
7、es6中=>箭头函数中的this,去掉当前函数的父级(上一级)对象,再看this指向谁,此时指向谁就是谁(结合第3条)
call、apply、bind三者为改变this指向的方法。
第一个参数改变this指向,如果不需要改变(null),this默认指向window
第二个参数作为实参传入函数中
call和bind挨个传值,apply传入一个数组
call和apply立即执行函数
bind是将传入的参数绑定,并返回一个新的函数,不会立即执行,需要时调用
# call apply bind 区别
function fn(a,b,c,d){
console.log(a,b,c,d);
}
//call
fn.call(null,1,2,3);
//apply
fn.apply(null,[1,2,3]);
//bind
var f = fn.bind(null,1,2,3);
f(4);
// 结果如下:
1 2 3 undefined
1 2 3 undefined
1 2 3 4
首先js 是单线程运行的,在代码执行的时候,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行。
在执行同步代码的时候,如果遇到了异步事件,js 引擎并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务
当同步事件执行完毕后,再将异步事件对应的回调加入到与当前执行栈中不同的另一个任务队列中等待执行。
任务队列可以分为宏任务对列和微任务对列,当当前执行栈中的事件执行完毕后,js 引擎首先会判断微任务对列中是否有任务可以执行,如果有就将微任务队首的事件压入栈中执行。
当微任务对列中的任务都执行完成后再去判断宏任务对列中的任务。
在js中,所有任务都分为同步任务和异步任务两大类。
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。
主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)
# 异步任务
setTimeout和setInterval
DOM事件
Promise
process.nextTick
fs.readFile
http.get
异步函数:如果在函数返回的时候,调用者还不能够得到预期结果,而是需要在将来通过一定的手段得到,那么这个函数就是异步的
# 宏任务
I/O
setTimeout
setInterval
setImmdiate
requestAnimationFrame
# 微任务
process.nextTick
Promise
Promise.then
MutationObserver
# 宏任务和微任务的执行顺序
一次事件循环中,先执行宏任务队列里的一个任务,再把微任务队列里的所有任务执行完毕,再去宏任务队列取下一个宏任务执行。
setTimeout 并不能保证执行的时间,是否及时执行取决于 JavaScript 线程是拥挤还是空闲。
浏览器的JS引擎遇到setTimeout,拿走之后不会立即放入异步队列,同步任务执行之后,timer模块会到设置时间之后放到异步队列中。js引擎发现同步队列中没有要执行的东西了,即运行栈空了就从异步队列中读取,然后放到运行栈中执行。所以setTimeout可能会多了等待线程的时间。
这时setTimeout函数体就变成了运行栈中的执行任务,运行栈空了,再监听异步队列中有没有要执行的任务,如果有就继续执行,如此循环,就叫Event Loop。
回调函数 异步回调
函数作为参数传递给另外有一个函数 将函数内部的值通过回调函数传出来
解决同步的一种方式 多层嵌套 造成回调地狱
事件监听
发布/订阅 系统中有一个信号中心,当某个任务执行完成后向信号中心发布一个消息,其他任务就可以通过这个信号中心去订阅这个信号,从而知道什么时候直接可以开始执行 发布订阅模式和事件监听类似,可以查看消息中心有多少信号,每个信号有多少订阅者
promise
generator(ES6) 异步任务 通过yield关键字可以让任务在需要的地方暂停,每一步的值可以通过next获取
async/await(ES7) await得到的就是async异步返回值,底层原理还是promise中的resolve方法
##什么是回调函数?回调函数有什么缺点
回调函数是一个匿名函数,它作为一个参数传递给其他的代码,其作用是在需要的时候方便调用这段(回调函数)代码。
可以让异步代码同步执行。
回调函数有一个致命的弱点,就是容易写出回调地狱(Callback hell)
https://www.cnblogs.com/moveup/p/9746748.html
promise的三种状态与链式调用: pending - 进行中 fulfilled - 成功 rejected - 失败
https://www.jianshu.com/p/dc61ea153874
[promise的状态处理的原理] :https://www.jianshu.com/p/44a971659696
generator:https://www.jianshu.com/p/83da0901166f
Promise 是es6新增的,异步编程的一种解决方案,用来取代回调函数和事件,比传统的解决方案——回调函数和事件——更合理和更强大。(解决回调地狱)
# 有三种状态:
pending(进行中)、fulfilled(resolve已成功)和rejected(已失败)。
# promise的特点 :
(1)对象的状态不受外界影响。Promise对象代表一个异步操作。
(2)一旦状态设定,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为resolve和从pending变为rejected。只要这两种情况发生,状态就凝固了
#promise的的方法
promise实例方法:Promise.prototype.then Promise.prototype.catch*
一:resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
二:reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。
三:Promise.prototype.finally
finally方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。
# promise的 静态方法
Promise.all():用于将多个 Promise 实例,包装成一个新的 Promise 实例,接受一个数组作为参数,只有数组里面的每个状态都变成resolve,则新的 Promise 实例状态才会变成resolve.
Promise.race():将Promise对象数组中最先执行完成的内容通过后面then传出
1、通过new promise创建一个promise对象,里面有一个参数,参数是一个回调函数,回调函数中 有2个参数,resolve,reject resolve()当异步执行成功的时候调用的方法,
reject()当异步失败的 时候调用的方法。
2、除此之外promise有一个then方法,当成功的时候执行第一个回调函数,当失败的时候执行第二个回调函数。第二个回调函数也可以通过promise对象.catch调用
3、Promise.all():当所有的异步代码都执行完毕以后才会执行.then中的操作
4、Promise.race():只要有一个promise执行完毕后就会执行.then操作
//请求某个table数据
function requestTableList(){
var p = new Promise((resolve, reject) => {
//去后台请求数据,这里可以是ajax,可以是axios,可以是fetch
resolve(res);
});
return p;
}
//延时函数,用于给请求计时 10s
function timeout(){
var p = new Promise((resolve, reject) => {
setTimeout(() => {
reject('请求超时');
}, 10000);
});
return p;
}
Promise.race([requestTableList(), timeout()]).then((data) =>{
//进行成功回调处理
console.log(data);
}).catch((err) => {
// 失败回调处理
console.log(err);
});
请求一个接口数据,10s内请求完成就展示数据,10s内没有请求完成就提示请求失败
这里定义了两个promise,一个去请求数据,一个记时10s,把两个promise丢进race里面赛跑去,如果请求数据先跑完就直接进入.then成功回调,将请求回来的数据进行展示;如果计时先跑完,也就是10s了数据请求还没有成功,就先进入race的失败回调,就提示用户数据请求失败进入.catch回调,(ps:或者进入reject的失败回调,当.then里面没有写reject回调的时候失败回调会直接进入.catch)
async异步能干什么?就是用来修饰函数,使该函数异步执行,不阻碍后续函数的执行 同时我们注意到,async修饰的函数也带有then catch方法,因此,经async修饰的函数也 是一个promise await只能放在async中,且只能修饰promise对象
1. promise的诞生是为了简化函数嵌套调用流程,也便于后续维护
2. async/await定义了异步函数,并在其内部可通过await等待promise对象,阻塞后续的执行
**await关键字必须在async函数里面 await会阻塞当前直到完成 async返回reject的方法,当抛出异常等同于reject async / await与 Promise的主要区别是 Promise代码完全都是Promise的API(then、catch等等),操作本身的语义反 而不容易看出来, async / await函数的实现最简洁,最符合语义,几乎没有语义不相关的代码 async / await 函数就是 Generator 函数的语法糖
await后面有个接口 接口要2S才能完成 接口2S才会执行
await是同步还是异步:await同步 async异步
async和await有两个关键字,一个写在函数外面,一个写在函数里面,函数外面是异步的 函数里面是同步的 调用函数的那一行其实是异步的,下一行函数里面转成阻塞的
//async/await 是一种特殊的语法,能够更好的处理promise,可以让你编写基于Promise的代码像同步一样,async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再执行函数体内后面的语句。可以理解为,是让出了线程,跳出了 async 函数体。
await
await关键字只能在async函数中使用。可以用来等待Promise状态变成resolved并有返回值。await后面通常跟的是一个promise对象,如果不是,会立即被包装成resoled状态的promise。
如果await后面的 promise 正常resolve,await promise便会返回结果。但是在reject的情况下,便会抛出异常,并且这种异常需要用try/catch来捕获,否则会导致进程崩溃。
//总结:
async函数之前的关键字有两个作用:
使它总是返回一个promise。
允许在其中使用await。
await promise之前的关键字使得JavaScript等待,直到这个promise的状态为resolved
如果是reject,则产生异常,需通过try/catch捕获。
否则,它返回结果,所以我们可以将它的值赋值给一个变量。
async/await使我们少写promise.then/catch,但是不要忘记它们是基于promise的。占据剩余空间
1. 使用async函数可以让代码简洁很多,不需要像Promise一样需要些then,不需要写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码
2. 使用aync/await的话,catch能处理JSON.parse错误 promise中不能处理
3. 条件语句也和错误捕获是一样的,在 Async 中也可以像平时一般使用条件语句
async函数中
let a=await promise 的a函数
let b=await promise 的b函数
promise.all改写
# Promise.allSettled
从 ES2020 开始,你可以使用 Promise.allSettled。当所有的 promises 都已经结束无论是完成状态或者是失败状态,它都会返回一个 promise,这个 promise 将会包含一个关于描述每个 promise 状态结果的对象数组
# Promise.any
这个方法用于返回第一个成功的 promise 。只要有一个 promise 成功此方法就会终止,它不会等待其他的 promise 全部完成。
不像 Promise.all() 会返回一组完成值那样(resolved values),我们只能得到一个成功值(假设至少有一个 promise 完成)。当我们只需要一个 promise 成功,而不关心是哪一个成功时此方法很有用的。
同时, 也不像 Promise.race() 总是返回第一个结果值(resolved/reject)那样,这个方法返回的是第一个 成功的 值。这个方法将会忽略掉所有被拒绝的 promise,直到第一个 promise 成功
promise实现promise.all的方法
var test3 = async function () {
try {
await p1();
await p2();
p3();
} catch (e) {
console.log('p1失败了', e)
}
}
没有区别
我对 ajax 的理解是,它是一种异步通信的方法,通过直接由 js 脚本向服务器发起 http 通信,然后根据服务器返回的数据,更新网页的相应部分,而不用刷新整个页面的一种方法。
创建步骤:创建xhr对象-》配置Ajax请求地址通过open方法-》发送请求通过send方法 ->监听请求,接收响应
//1:创建Ajax对象
var xhr = window.XMLHttpRequest?new XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');// 兼容IE6及以下版本
//2:配置 Ajax请求地址
xhr.open('get','index.xml',true);
//3:发送请求
xhr.send(null); // 严谨写法
//4:监听请求,接受响应
xhr.onreadysatechange=function(){
if(xhr.readySate==4&&xhr.status==200 || xhr.status==304 )
console.log(xhr.responsetXML)
}
Axios.get(“/xxx”,{}).then((data) =>{
Console.log(data)
}).catch((err)=>{
})
Ajax实现了网页的局部数据刷新
axios实现了对ajax的封装
axios是一种对ajax的封装,fetch是一种浏览器原生实现的请求方式,跟ajax对等
Fetch:可以很好的实现分离原则。 也原生支持目前流行的异步流(promise) 模型,但是很多平台都不实现,不能进行复杂的io操作 Fetch携带cookie credentials: "include"
axios怎么取消请求
原生中有一个取消的方法,可以调用XMLHttpRequest对象上的abort方法 在axios拦截器里, 查找axios的文档,发现 可以通过使用CancelToken来取消axios发起的请求
###post和get的区别
1.get传参方式是通过地址栏URL传递,是可以直接看到get传递的参数,post传参方式参数URL不可见,get把请求的数据在URL后通过?连接,通过&进行参数分割。psot将参数存放在HTTP的包体内
2.get传递数据是通过URL进行传递,对传递的数据长度是受到URL大小的限制,URL最大长度是2048个字符。post没有长度限制
3.get后退不会有影响,post后退会重新进行提交
4.get请求可以被缓存,post不可以被缓存
5.get请求只URL编码,post支持多种编码方式
6.get请求的记录会留在历史记录中,post请求不会留在历史记录
7.get只支持ASCII字符,post没有字符类型限制
# 执行时间
window.onload必须等到页面内包括图片的所有元素(资源)加载完毕才能执行
$(document).ready()是DOM树(结构)加载完毕就执行,不必等到所有元素都加载完
# 编写个数
window.onload不能同时编写多个,如果有多个window.onload方法,只执行最后一个
$(document).ready()可以同时编写多个,并且都可以执行
# 简写
window.onload没有简写
$(document).ready(function(){
})可以简写成 $(function(){
})
ECMAScript 是编写脚本语言的标准,ECMA(European Computer Manufacturers Association)规定一下他的标准,因为当时有java语言了,又想强调这个东西是让ECMA这个人定的规则,所以就这样一个神奇的东西诞生了,这个东西的名称就叫做ECMAScript。
javaScript = ECMAScript + DOM + BOM
DOM 指的是文档对象模型,它指的是把文档当做一个对象来对待,这个对象主要定义了处理网页内容的方法和接口。
BOM 指的是浏览器对象模型,它指的是把浏览器当做一个对象来对待,这个对象主要定义了与浏览器进行交互的法和接口。
BOM的核心是 window,而 window 对象具有双重角色,它既是通过 js 访问浏览器窗口的一个接口,又是一个 Global(全局)对象。这意味着在网页中定义的任何对象,变量和函数,都作为全局对象的一个属性或者方法存在。window 对象含有 location 对象、navigator 对象、screen 对象等子对象,并且 DOM 的最根本的对象 document 对象也是 BOM 的 window 对象的子对象。
#(1)创建新节点
createDocumentFragment() //创建一个DOM片段
createElement() //创建一个具体的元素
createTextNode() //创建一个文本节点
#(2)添加、移除、替换、插入
appendChild(node)
removeChild(node)
replaceChild(new,old)
insertBefore(new,old)
#(3)查找
getElementById();
getElementsByName();
getElementsByTagName();
getElementsByClassName();
querySelector();
querySelectorAll()
#(4)属性操作
getAttribute(key);
setAttribute(key, value);
hasAttribute(key);
removeAttribute(key);
1.冒泡型事件流:事件从具体的元素沿DOM树向上传播
//事件委托:通过事件冒泡的特性将事件处理函数添加给父级/祖先级元素
//本质上是利用了事件冒泡的机制。,并且父节点可以通过事件对象获取到 目标节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方式称为事件代理
2.捕获型事件流:从不具体到具体,事件沿DOM树向下传播
#Dom事件流:事件捕获 目标触发 事件冒泡
DOM0级事件默认冒泡,无法设置为事件捕获
#IE事件流和DOM事件流的区别
ie事件流采用冒泡型
dom事件流采用先捕获后冒泡
标准DOM:
[Object].addEventListener("事件名称",fnHandler,是否捕获)
[Object].removeEventListener("事件名称", fnHandler, 是否捕获);
1.DOM使用先捕获后冒泡型事件,true为在捕获阶段处理,false为在冒泡阶段处理
2.事件名称没有on
3.this指向触发该事件的对象
IE(低版本9以下):
[Object].attachEvent("事件名称",fnHandler);
[Object].detachEvent("事件名称", fnHandler);
1.IE采用冒泡型事件
2.事件名称有on
3.this指向window
区别
1.执行顺序不一样、
2.参数不一样
3.事件加不加on
4.this指向问题
// 三种事件模型
事件 是用户操作网页时发生的交互动作或者网页本身的一些操作,现代浏览器一共有三种事件模型。
#DOM0级模型:
这种模型不会传播,所以没有事件流的概念,同元素 绑定多个事件,只会绑定最后一次的事件,前面的会被覆盖。
#IE 事件模型:
在该事件模型中,一次事件共有两个过程,事件处理阶段,和事件冒泡阶段。事件处理阶段会首先执行目标元素绑定的监听事件。然后是事件冒泡阶段,冒泡指的是事件从目标元素冒泡到 document,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。这种模型通过 attachEvent 来添加监听函数,可以添加多个监听函数,会按顺序依次执行。detachEvent删除事件
#DOM2 级事件模型:
在该事件模型中,一次事件共有三个过程,第一个过程是事件捕获阶段事件处理阶段,和事件冒泡阶段。捕获指的是事件从 document 一直向下传播到目标元素,依次检查经过的节点是否绑定了事件监听函数,如果有则执行。。
这种事件模型,事件绑定的函数是 addEventListener,其中第三个参数可以指定事件是否在捕获阶段执行。取消事件removeEventListener
#DOM0级事件
btn.onclick=function(){
}
一个事件只能绑定一个事件处理函数
解绑:btn.onclick=null
#DOM2级事件
box.addEventListener('click',function(){
},false)
一个元素可以绑定多个事件处理函数
解绑:box.removeEventListener('click',function(){
})
#阻止事件冒泡
w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true
#阻止默认事件
w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false;
js 中的内置对象主要指的是在程序执行前存在全局作用域里的由 js
定义的一些全局值属性、函数和用来实例化其他对象的构造函
数对象。一般我们经常用到的如全局变量值 NaN、undefined,全局函数如 parseInt()、parseFloat() 用来实例化对象的构
造函数如 Date、Object 等,还有提供数学计算的单体内置对象如 Math 对象。
我们一般使用字面量的形式直接创建对象,但是这种创建方式对于创建大量相似对象的时候,会产生大量的重复代码。但 js
和一般的面向对象的语言不同,在 ES6 之前它没有类的概念。但是我们可以使用函数来进行模拟,从而产生出可复用的对象
创建方式,我了解到的方式有这么几种:
#(1)第一种是工厂模式,
工厂模式的主要工作原理是用函数来封装创建对象的细节,从而通过调用函数来达到复用的目的。但是它有一个很大的问题就是创建出来的对象无法和某个类型联系起来,它只是简单的封装了复用代码,而没有建立起对象和类型间的关系。
#(2)第二种是构造函数模式。
js 中每一个函数都可以作为构造函数,只要一个函数是通过 new 来调用的,那么我们就可以把它称为构造函数。执行构造函数首先会创建一个对象,然后将对象的原型指向构造函数的 prototype 属性,然后将执行上下文中的 this 指向这个对象,最后再执行整个函数,如果返回值不是对象,则返回新建的对象。因为 this 的值指向了新建的对象,因此我们可以使用 this 给对象赋值。构造函数模式相对于工厂模式的优点是,所创建的对象和构造函数建立起了联系,因此我们可以通过原型来识别对象的类型。但是构造函数存在一个缺点就是,造成了不必要的函数对象的创建,因为在 js 中函数也是一个对象,因此如果对象属性中如果包含函数的话,那么每次我们都会新建一个函数对象,浪费了不必要的内存空间,因为函数是所有的实例都可以通用的。
#(3)第三种模式是原型模式,
因为每一个函数都有一个 prototype 属性,这个属性是一个对象,它包含了通过构造函数创建的所有实例都能共享的属性和方法。因此我们可以使用原型对象来添加公用属性和方法,从而实现代码的复用。这种方式相对于构造函数模式来说,解决了函数对象的复用问题。但是这种模式也存在一些问题,一个是没有办法通过传入参数来初始化值,另一个是如果存在一个引用类型如 Array 这样的值,那么所有的实例将共享一个对象,一个实例对引用类型值的改变会影响所有的实例。
#(4)第四种模式是组合使用构造函数模式和原型模式,
这是创建自定义类型的最常见方式。因为构造函数模式和原型模式分开使用都存在一些问题,因此我们可以组合使用这两种模式,通过构造函数来初始化对象的属性,通过原型对象来实现函数方法的复用。这种方法很好的解决了两种模式单独使用时的缺点,但是有一点不足的就是,因为使用了两种不同的模式,所以对于代码的封装性不够好。
#(5)第五种模式是动态原型模式,
这一种模式将原型方法赋值的创建过程移动到了构造函数的内部,通过对属性是否存在的判断,可以实现仅在第一次调用函数时对原型对象赋值一次的效果。这一种方式很好地对上面的混合模式进行了封装。
#(6)第六种模式是寄生构造函数模式,
这一种模式和工厂模式的实现基本相同,我对这个模式的理解是,它主要是基于一个已有的类型,在实例化时对实例化的对象进行扩展。这样既不用修改原来的构造函数,也达到了扩展对象的目的。它的一个缺点和工厂模式一样,无法实现对象的识别。
//arguments对象是函数中传递的参数值的集合。它是一个类似数组的对象,因为它有一个length属性,我们可以使用数组索引表示法arguments[1]来访问单个值,但它没有数组中的内置方法,如:forEach、reduce、filter和map。
我们可以使用Array.prototype.slice将arguments对象转换成一个数组。
Array.prototype.slice.call(arguments)
#什么是箭头函数?
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或new.target。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。
在箭头函数版本中,当只有一个表达式或值需要返回,我们只需要()括号,不需要 return 语句,箭头函数就会有一个隐式的返回。
如果我们在一个箭头函数中有一个参数,则可以省略括号。
箭头函数不能访问arguments对象。所以调用第一个getArgs函数会抛出一个错误。相反,我们可以使用rest参数来获得在箭头函数中传递的所有参数。
箭头函数没有自己的this值。箭头函数里的this指向的是父级的this. 也就是说箭头函数是在定义this指定就被确定了。
https://segmentfault.com/a/1190000021380336
1. this的区别
原因:箭头函数不会自动绑定局部变量,如this,arguments,super(ES6),new.target(ES6)等,所以箭头函数没有它自己的this值,箭头函数内的this值继承自外围作用域。# 在箭头函数中调用 this 时,仅仅是简单的沿着作用域链向上寻找,找到最近的一个 this 拿来使用
2.实际上箭头函数中并不只是 this 和普通函数有所不同,箭头函数中没有任何像 this 这样自动绑定的局部变量,包括:this,arguments,super(ES6),new.target(ES6)……在普通函数中,会自动绑定上的各种局部变量,箭头函数都是十分单纯的沿着作用域链向上寻找……
3.箭头函数没有arguments对象,用(...arr)
函数式编程(通常缩写为FP)是通过编写纯函数,避免共享状态、可变数据、副作用 来构建软件的过程。数式编程是声明式 的而不是命令式 的,应用程序的状态是通过纯函数流动的。与面向对象编程形成对比,面向对象中应用程序的状态通常与对象中的方法共享和共处。
函数式编程是一种编程范式 ,这意味着它是一种基于一些基本的定义原则(如上所列)思考软件构建的方式。当然,编程范式的其他示例也包括面向对象编程和过程编程。
函数式的代码往往比命令式或面向对象的代码更简洁,更可预测,更容易测试
首先高阶函数肯定是函数,不同的是输入的参数和返回的值这两项中的一项必须是函数才能叫高阶函数。这个问题在回答的时候可以稍微拓展一下,介绍一下常用的的高阶函数,比如:map、flatMap、filter、reduce、fold。
在JavaScript中,函数不仅拥有一切传统函数的使用方式(声明和调用),而且可以做到像简单值一样:
赋值(var func = function(){
})、
传参(function func(x,callback){
callback();})、
返回(function(){
return function(){
}}),
这样的函数也称之为第一级函数(First-class Function)。不仅如此,JavaScript中的函数还充当了类的构造函数的作用,同时又是一个Function类的实例(instance)。这样的多重身份让JavaScript的函数变得非常重要。
https://juejin.im/post/6844904200917221389#heading-81
设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了重用代码、让代码更容易被他人理解、保证代码可靠性。
1、单例模式
2、工厂模式
3、观察者模式
4、代理模式
5、策略模式
6、迭代器模式
单例模式(Singleton Pattern)
顾名思义,单例模式中Class的实例个数最多为1。当需要一个对象去贯穿整个系统执行某些任务时,单例模式就派上了用场。而除此之外的场景尽量避免单例模式的使用,因为单例模式会引入全局状态,而一个健康的系统应该避免引入过多的全局状态。
工厂模式
工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便创建的时候指定自己的对象类型。
使用场景:如果你不想让某个子系统与较大的那个对象之间形成强耦合,而是想运行时从许多子系统中进行挑选的话,那么工厂模式是一个理想的选择
class Product {
constructor(name) {
this.name = name
}
init() {
console.log('init')
}}
class Factory {
create(name) {
return new Product(name)
}
}
// use
let factory = new Factory()
let p = factory.create('p1')
p.init()
#关于获取滚动条时,出现的兼容问题
Var scrollTop = document.documentElment.scrollTop || document.body.scrollTop
# 获取非行内样式的兼容方式
function getStyle(obj,attr){
//获取非行间样式,obj是对象,attr是值
if(obj.currentStyle){
//针对ie获取非行间样式
return obj.currentStyle[attr];
}else{
return getComputedStyle(obj,false)[attr]; //针对非ie
}
}
# 获取事件对象
document.onclick=function(eve){
var e=eve||window.event;
console.log(e);
}
#阻止事件冒泡
function stopBubble(e){
if(e.stopPropagation){
e.stopPropagation();
}else{
e.cancelBubble = true;//兼容ie
}
}
#阻止浏览器默认行为
if( e.preventDefault ){
e.preventDefault();
}else{
window.event.returnValue = false;//ie
}
#事件委托
var oul = document.querySelector("ul")
oul.onclick = function(eve){
var e = eve || window.event;
var t = e.target || e.srcElement;
if(t.getAttribute("abc") == "l"){
console.log(e.target.innerHTML)
}
}
#键盘事件的兼容方法
var eve = eve||window.event;
var keyC = eve.keyCode||eve.which;
#1.let------- const
let和const声明形成块作用域,而var不存在此作用域
let在相同作用域内,不允许重复声明同一个变量
var声明变量存在变量提升,在代码块内,使用let和const不存在变量提升
const:定义常量(不能修改),使用时必须 初始化(就是要赋值,比如:const a = 5;可以,不能只写const a;),只能在块级作用域里访问
#2.解构赋值
从数组和对象中提取值,对变量进行赋值,这被称为解构.
#3.扩展运算符 ...
扩展运算符用三个点号表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值.
#4.模板字符串
'`${}`'
# 5.箭头函数
1.剪头函数是个匿名函数,不能作为构造函数,不能使用new
2.剪头函数不能绑定arguments
3.剪头函数没有原型属性
4.剪头函数的this指向永远指向其上下文的this,没有办法改变其指向,而普通函数的this指向他的调用者
# 6.数组和对象的扩展
1.Array.from()方法用于将对象转为真正的数组(类数组转数组)
2.Array.of()方法用于将一组值,转换为数组。
console.log(Array.of(1,2,3,4,4,50));//[1, 2, 3, 4, 4, 50]
3.Object.assign(目标对象,对象1,对象2)用于对象的合并,将源对象的所有可枚举属性,复制到目标对象。(浅拷贝)
# 7.Symbol--表示独一无二的值,它是js中的第七种数据类型。属于基本类型
# 8.Generators生成器函数
总结一下,调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。
#9.Set结构和Map结构
1.Set:它类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成 Set 数据结构,数组作为参数。
//数组去重
// let arr=[1,2,3,4,5,6,6,5,4,3,2,1];
// console.log([...new Set(arr)]);//[1, 2, 3, 4, 5, 6]
2.Map数据结构。它类似于对象,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
#10.promise
#11.class 自定义数据类型
es6的关键字class是构造函数的语法糖
本质是函数
作用:创建对象
#12.模块化
我对模块的理解是,一个模块是实现一个特定功能的一组方法。在最开始的时候,js 只实现一些简单的功能,所以并没有模块的概念
,但随着程序越来越复杂,代码的模块化开发变得越来越重要。
由于函数具有独立作用域的特点,最原始的写法是使用函数来作为模块,几个函数作为一个模块,但是这种方式容易造成全局变量的污
染,并且模块间没有联系。
后面提出了对象写法,通过将函数作为一个对象的方法来实现,这样解决了直接使用函数作为模块的一些缺点,但是这种办法会暴露所
有的所有的模块成员,外部代码可以修改内部属性的值。
现在最常用的是立即执行函数的写法,通过利用闭包来实现模块私有作用域的建立,同时不会对全局作用域造成污染。
js 中现在比较成熟的有四种模块加载方案:
第一种是 CommonJS 方案,它通过 require 来引入模块,通过 module.exports 定义模块的输出接口。这种模块加载方案是服务器端的解决方案,它是以同步的方式来引入模块的,因为在服务端文件都存储在本地磁盘,所以读取非常快,所以以同步的方式加载没有问题。但如果是在浏览器端,由于模块的加载是使用网络请求,因此使用异步加载的方式更加合适。
第二种是 AMD 方案,这种方案采用异步加载的方式来加载模块,模块的加载不影响后面语句的执行,所有依赖这个模块的语句都定义在一个回调函数里,等到加载完成后再执行回调函数。require.js 实现了 AMD 规范。
第三种是 CMD 方案,这种方案和 AMD 方案都是为了解决异步模块加载的问题,sea.js 实现了 CMD 规范。它和require.js的区别在于模块定义时对依赖的处理不同和对依赖模块的执行时机的处理不同。
第四种方案是 ES6 提出的方案,使用 import 和 export 的形式来导入导出模块。
##AMD 和 CMD 规范的区别?
它们之间的主要区别有两个方面。
第一个方面是在模块定义时对依赖的处理不同。AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块。而 CMD 推崇就近依赖,只有在用到某个模块的时候再去 require。
第二个方面是对依赖模块的执行时机处理不同。首先 AMD 和 CMD 对于模块的加载方式都是异步加载,不过它们的区别在于模块的执行时机,AMD 在依赖模块加载完成后就直接执行依赖模块,依赖模块的执行顺序和我们书写的顺序不一定一致。而 CMD在依赖模块加载完成后并不执行,只是下载而已,等到所有的依赖模块都加载好后,进入回调函数逻辑,遇到 require 语句
的时候才执行对应的模块,这样模块的执行顺序就和我们书写的顺序保持一致了。
1.CommonJS 模块输出的是一个值的拷贝,
ES6 模块输出的是值的引用。
CommonJS 模块输出的是值的,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
ES6 模块的运行机制与 CommonJS 不一样。JS 引擎对脚本静态分析的时候,遇到模块加载命令 import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
2.CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。CommonJS 模块就是对象,即在输入时是先加载整个模块,生成一个对象,然后再从这个对象上面读取方法,这种加载称为“运行时加载”。
而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
require.js 的核心原理是通过动态创建 script 脚本来异步引入模块,然后对每个脚本的 load 事件进行监听,如果每个脚本都加载完成了,再调用回调函数
var
,let
和const
的区别是什么?var声明的变量会挂载在window上,而let和const声明的变量不会:
var声明变量存在变量提升,let和const不存在变量提升:
let和const声明形成块作用域
同一作用域下let和const不能声明同名变量,而var可以
暂存死区
var a = 100;
if(1){
a = 10;
//在当前块作用域中存在a使用let/const声明的情况下,给a赋值10时,只会在当前作用域找变量a,
// 而这时,还未到声明时候,所以控制台Error:a is not defined
let a = 1;
}
const 一旦声明必须赋值,不能使用null占位。
声明后不能再修改
如果声明的是复合类型数据,可以修改其属性
模板字符串是在 JS 中创建字符串的一种新方法。我们可以通过使用反引号使模板字符串化。
在ES5版本中,我们需要添加\n以在字符串中添加新行。在模板字符串中,我们不需要这样做。
在 ES5 版本中,如果需要在字符串中添加表达式或值,则需要使用+运算符。在模板字符串s中,我们可以使用${
expr}嵌入一个表达式,这使其比 ES5 版本更整洁。
对象解构是从对象或数组中获取或提取值的一种新的、更简洁的方法
我们还可以为属性取别名
let {
firstName: fName, position } = employee;
当然如果属性值为 undefined 时,我们还可以指定默认值
let {
firstName = "Mark" } = employee;
# 解构的原理
Var arr = [1,2,3]
Var {
a,b,c} = arr //1,2,3
只要等号两边的模式相同,左边的变量就会被赋予匹配的右边的值
Set
对象,它是如何工作的?Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
我们可以使用Set构造函数创建Set实例。
const set1 = new Set();
const set2 = new Set(["a","b","c","d","d","e"]);
我们可以使用add方法向Set实例中添加一个新值,因为add方法返回Set对象,所以我们可以以链式的方式再次使用add。如果一个值已经存在于Set对象中,那么它将不再被添加。
set2.add("f");
set2.add("g").add("h").add("i").add("j").add("k").add("k");
// 后一个“k”不会被添加到set对象中,因为它已经存在了
我们可以使用has方法检查Set实例中是否存在特定的值。
set2.has("a") // true
set2.has("z") // true
我们可以使用size属性获得Set实例的长度。
set2.size // returns 10
可以使用clear方法删除 Set 中的数据。
set2.clear();
我们可以使用Set对象来删除数组中重复的元素。
const numbers = [1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 5];
const uniqueNums = [...new Set(numbers)];
另外还有WeakSet, 与 Set 类似,也是不重复的值的集合。但是 WeakSet 的成员只能是对象,而不能是其他类型的值。WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet对该对象的引用。
Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
WeakMap 结构与 Map 结构类似,也是用于生成键值对的集合。但是 WeakMap 只接受对象作为键名( null 除外),不接受其他类型的值作为键名。而且 WeakMap 的键名所指向的对象,不计入垃圾回收机制。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”
Proxy用于修改某些操作的默认行为,即对编程语言层面进行修改,属于“元编程”,Proxy意思为“代理”,即在访问对象之前建立一道“拦截”,任何访问该对象的操作之前都会通过这道“拦截”,即执行Proxy里面定义的方法
let pro = new Proxy(target,handler)
其中 new Proxy相当于创建了一个Proxy实例,target为所要拦截的目标对象,handler也是一个对象,里面定义的是对拦截对象所要进行的拦截方法
Proxy也可以作为其他对象的原型对象使用
上述实例将pro作为obj的原型对象使用,虽然obj本身没有name这个属性,但是根据原型链,会在pro上读取到name属性,之后会执行相对应的拦截操作。
let pro = new Proxy(target,handler);
let obj = Object.create(pro);
get(target,name,property)方法,用于拦截某个读取属性的操作,第一个参数为目标对象,第二个参数为属性名称,第三个属性为操作所针对的对象(可选参数)
set(target,name,value,property),用于拦截某个属性的赋值操作,第一个参数为目标对象,第二个参数为属性名,第三个参数为属性值,第四个参数为操作行为所针对的对象(可选参数)
has(target,key),用来拦截对象是否具有某个属性值的操作,第一个参数为目标对象,第二个参数为属性名
Reflect对象 :Reflect设计的目的是为了优化Object的一些操作方法以及合理的返回Object操作返回的结果,对于一些命令式的Object行为,Reflect对象可以将其变为函数式的行为
Reflect(target,name,property) Reflect.has(obj,"name")
Reflect.get(target,name,property)
首先总的来说,三者都是用于持久化数据存储的手段,都是存储在浏览器端,且同源.
localStorage和sessionStorage都是Web存储,大小5M左右,完全存储在客户端,它们是因为本地存储数据而存在.
cookies也是存储在浏览器端的,大小不超过4k,由服务器端存储在客户端。
localStorage属于永久性存储,数据存储量大,,而sessionStorage属于当会话结束的时候,存储的值会被清空,而cookie是通过设置过期时间expires来存储的。
正则 type number
#浏览器优化
//减少请求数量
#一.图片处理
1、雪碧图 - gulp Base64 使用字体图标来代替图片 - 自定义字体 @font-face{
} 在安卓下可以使用webp格式的图片,它
#二.减小资源大小 - grunt gulp webpack
1、HTML压缩2、CSS压缩3、JS压缩与混乱4、图片压缩
#.优化网络连接
cdnCDN即内容分发网络,它能够实时地根据网络流量和各节点的连接、负载状
用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度
#四.优化资源加载
资源加载位置
1、CSS文件放在head中,先外链,后本页
2、JS文件放在body底部,先外链,后本页
3、body中间尽量不写style标签和script标签
资源加载时机
1、异步script标签
2、模块按需加载需要根据路由来加载当前页面需要的业务模块
3、资源懒加载与资源预加载
#五.减少重绘回流
css3硬件加速(GPU加速)
#六.【DOM优化】
1、缓存DOM2、减少DOM深度及DOM数量3、批量操作DOM4、批量操作CSS样式5、在内存中操作DOM6、DOM元素离线更新7、DOM读写分离8、事件代理9、防抖和节流10、及时清理环境
# 首页白屏解决与优化方案
当前很多无线页面都使用前端模板进行数据渲染,那么在糟糕的网速情况下,一进去页面,看到的不是白屏就是 loading,这成为白屏问题。
webpack 优化 抽离css 模块化抽取 代码压缩
1.将公用的JS库通过script标签外部引入,减小 app.bundel 的大小,让浏览器并行下载资源文件,提高下载速度;
2.在配置 路由时,页面和组件使用懒加载的方式引入,进一步缩小 app.bundel 的体积,在调用某个组件时再加载对应的js文件;
3.加一个首屏loading图,提升用户体验;
此问题发生的原因基本可以归结为网速跟静态资源
1、css文件加载需要一些时间,在加载的过程中页面是空白的。 解决:可以考虑将css代码前置和内联。
2、首屏无实际的数据内容,等待异步加载数据再渲染页面导致白屏。 解决:在首屏直接同步渲染html,后续的滚屏等再采用异步请求数据和渲染html。
3、首屏内联js的执行会阻塞页面的渲染。 解决:尽量不在首屏html代码中放置内联脚本。(来自翔歌)
# 解决方案
根本原因是客户端渲染的无力,因此最简单的方法是在服务器端,使用模板引擎渲染所有页面。同时
1减少文件加载体积,如html压缩,js压缩
2加快js执行速度 比如常见的无限滚动的页面,可以使用js先渲染一个屏幕范围内的东西
3提供一些友好的交互,比如提供一些假的滚动条
4使用本地存储处理静态文件。
js 的加载、解析和执行会阻塞页面的渲染过程,因此我们希望 js 脚本能够尽可能的延迟加载,提高页面的渲染速度。
我了解到的几种方式是:
1、将 js 脚本放在文档的底部,来使 js 脚本尽可能的在最后来加载执行。
2、给 js 脚本添加 defer属性,这个属性会让脚本的加载与文档的解析同步解析,然后在文档解析完成后再执行这个脚本文件,这样的话就能使页面的渲染不被阻塞。多个设置了 defer 属性的脚本按规范来说最后是顺序执行的,但是在一些浏览器中可能不是这样。
3、给 js 脚本添加 async属性,这个属性会使脚本异步加载,不会阻塞页面的解析过程,但是当脚本加载完成后立即执行 js脚本,这个时候如果文档没有解析完成的话同样会阻塞。多个 async 属性的脚本的执行顺序是不可预测的,一般不会按照代码的顺序依次执行。
4、动态创建 DOM 标签的方式,我们可以对文档的加载事件进行监听,当文档加载完成后再动态的创建 script 标签来引入 js 脚本。
###回流/重绘 | 防抖/节流??
//什么是重绘?回流(重排)?
1. 当页面中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流。每个页面至少需要一次回流,就是在页面第一次加载的时候。
2. 当页面中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。
注:回流必将引起重绘,而重绘不一定会引起回流。
//什么时候会发生回流?
当页面布局和几何属性改变时候就需要回流。下面的情况会引发浏览器的回流:
1:添加或者删除可见的DOM元素
2:元素位置改变;
3:元素尺寸改变----边距、填充、边框、高度和宽度
4:内容改变---比如文本改变或者图片大小改变而引起的计算值宽度和高度改变
5:页面渲染初始化
6:浏览器窗口尺寸改变---resize事件发生时
//什么时候发生重绘?
1.改变字体
2.增加或者移除样式表
3.内容变化(input框输入文字)
4.激活css伪类 eg :hover
5.计算offsetWidth、offsetHeigth属性(浏览器的可见高度)
6.设置style属性的值
//如何减少回流、重绘
1.使用DocumentFragment进行缓存操作,引发一次回流和重绘
2. csstext(利用cssText属性合并所有改变,然后一次性写入)
3.使用trsansform使用改变位置(margin-left等)这些更加流畅
4.如果你要改变多个属性,最好将这些属性定义在一个class中,直接修改class名,这样只用引起一次回流。
//防抖和节流
//函数防抖:当事件被触发一段时间后再执行回调,如果在这段时间内事件又被触发,则重新计时。
在事件触发时,开始计时,在规定的时间(delay)内,若再次触发事件,将上一次计时(timer)清空,然后重新开始计时。保证只有在规定时间内没有再次触发事件之后,再去执行这个事件。 1次
//函数节流(throttle):指定时间间隔内,若事件被多次触发,只会执行一次
在事件触发之后,开始计时,在规定的时间(delay)内,若再次触发事件,不对此事件做任何处理。保证在规定时间内只执行一次事件.
https://blog.csdn.net/qq_40421277/article/details/87990882
//函数防抖的应用场景
连续的事件,只需触发一次回调的场景有:
搜索框搜索输入。只需用户最后一次输入完,再发送请求
手机号、邮箱验证输入检测
窗口大小Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。
//函数节流的应用场景
间隔一段时间执行一次回调的场景有:
滚动加载,加载更多或滚到底部监听
谷歌搜索框,搜索联想功能
高频点击提交,表单重复提交
1、域名解析
2、为将你的信息从pc上传到服务器上,需要IP协议,ASP协议,ospf协议,发起TCP的三次握手
3、建立TCP连接,发起http请求
4、服务器响应http请求
5、服务器端解析HTML代码,请求HTML中的资源
6、断开TCP连接(4次挥手)
7、浏览器将渲染页面呈现给用户
8、Accept
告诉WEB服务器自己接受什么介质类型,*/* 表示任何类型,type/* 表示该类型下的所有子类型,type/sub-type。
9、Accept-Charset
浏览器告诉服务器自己能接收的字符集。
10、Accept-Encoding
浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法(gzip,deflate)。
11、Accept-Language
浏览器申明自己接收的语言。语言跟字符集的区别:中文是语言,中文有多种字符集,比如big5,gb2312,gbk等等。
12、Authorization
当客户端接收到来自WEB服务器的 WWW-Authenticate 响应时,用该头部来回应自己的身份验证信息给WEB服务器。
13、If-Match
如果对象的 ETag 没有改变,其实也就意味著对象没有改变,才执行请求的动作,获取文档。
14、If-None-Match
如果对象的 ETag 改变了,其实也就意味著对象也改变了,才执行请求的动作,获取文档。
15、If-Modified-Since
如果请求的对象在该头部指定的时间之后修改了,才执行请求的动作(比如返回对象),否则返回代码304,告诉浏览器该对象没有修改。例如:If-Modified-Since:Thu, 10 Apr 2008 09:14:42 GMT
16、If-Unmodified-Since
如果请求的对象在该头部指定的时间之后没修改过,才执行请求的动作(比如返回对象)。
17、If-Range /reinge/
浏览器告诉 WEB 服务器,如果我请求的对象没有改变,就把我缺少的部分给我,如果对象改变了,就把整个对象给我。浏览器通过发送请求对象的ETag 或者自己所知道的最后修改时间给 WEB 服务器,让其判断对象是否改变了。总是跟 Range 头部一起使用。
18、Range
浏览器(比如 Flashget 多线程下载时)告诉 WEB 服务器自己想取对象的哪部分。例如:Range: bytes=1173546
19、Proxy-Authenticate
代理服务器响应浏览器,要求其提供代理身份验证信息。
20、Proxy-Authorization
浏览器响应代理服务器的身份验证请求,提供自己的身份信息。
21、Host
客户端指定自己想访问的WEB服务器的域名/IP 地址和端口号。如Host:rss.sina.com.cn
22、Referer
浏览器向WEB 服务器表明自己是从哪个网页URL获得点击当前请求中的网址/URL,例如:Referer:http://www.ecdoer.com/
23、User-Agent
浏览器表明自己的身份(是哪种浏览器)。例如:User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN;rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14
1.浏览器默认的 application/x-www-form-urlencoded
这应该是最常见的 POST 提交数据的方式了。浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。
2.multipart/form-data
这也是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,就要让 form 的 enctype 等于这个值
3.application/json
除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify
4.text/xml
相比于JSON,[XML](https://link.jianshu.com/?t=http://www.w3school.com.cn/x.asp)不能更好的适用于数据交换,它包含了太多的包装, 而且它跟大多数编程语言的数据模型不匹配,让大多数程序员感到诧异,XML是面向数据的,JSON是面向对象和结构的,JSON会给程序员一种更加亲切的感觉。
设置请求头 请求头中携带cookie
本地缓存:
WebStorage(localStorage、sessionStorage)
Cookie
WebSql
IndexDB
应用缓存Application Cache
PWA
默认缓存
往返缓存BFCache
这里说的缓存是指浏览器(客户端)在本地磁盘中对访问过的资源保存的副本文件。
浏览器缓存主要有以下几个优点:
1. 减少重复数据请求,避免通过网络再次加载资源,节省流量。
2. 降低服务器的压力,提升网站性能。
3. 加快客户端加载网页的速度, 提升用户体验。
浏览器缓存分为强缓存和协商缓存,两者有两个比较明显的区别:
1. 如果浏览器命中强缓存,则不需要给服务器发请求;而协商缓存最终由服务器来决定是否使用缓存,即客户端与服务器之间存在一次通信。
2. 在 chrome 中强缓存(虽然没有发出真实的 http 请求)的请求状态码返回是 200 (from cache);而协商缓存如果命中走缓存的话,请求的状态码是 304 (not modified)。 不同浏览器的策略不同,在 Fire Fox中,from cache 状态码是 304.
#强缓存(Pragma、Cache-Control、Expires)
强缓存是根据返回头中的 Expires 或者 Cache-Control 两个字段来控制的,都是表示资源的缓存有效时间。
Expires 是 http 1.0 的规范,值是一个GMT 格式的时间点字符串,比如 Expires:Mon,18 Oct 2066 23:59:59 GMT 。这个时间点代表资源失效的时间,如果当前的时间戳在这个时间之前,则判定命中缓存。有一个缺点是,失效时间是一个绝对时间,如果服务器时间与客户端时间偏差较大时,就会导致缓存混乱。而服务器的时间跟用户的实际时间是不一样是很正常的,所以 Expires 在实际使用中会带来一些麻烦。
Cache-Control这个字段是 http 1.1 的规范,一般常用该字段的 max-age 值来进行判断,它是一个相对时间,比如 .Cache-Control:max-age=3600 代表资源的有效期是 3600 秒。并且返回头中的 Date 表示消息发送的时间,表示当前资源在 Date ~ Date +3600s 这段时间里都是有效的。不过我在实际使用中常常遇到设置了 max-age 之后,在 max-age 时间内重新访问资源却会返回 304 not modified ,这是由于服务器的时间与本地的时间不同造成的。当然 Cache-Control 还有其他几个值可以设置, 不过相对来说都很少用了:
no-cache 不使用本地缓存。需要使用协商缓存。
no-store直接禁止浏览器缓存数据,每次请求资源都会向服务器要完整的资源, 类似于 network 中的 disabled cache。
public 可以被所有用户缓存,包括终端用户和 cdn 等中间件代理服务器。
private 只能被终端用户的浏览器缓存。
如果 Cache-Control与 Expires 同时存在的话, Cache-Control 的优先级高于 Expires 。
#协商缓存(Last-Modified/If-Modified-Since、Etag/If-None-Match)
协商缓存是由服务器来确定缓存资源是否可用。 主要涉及到两对属性字段,都是成对出现的,即第一次请求的响应头带上某个字, Last-Modified 或者 Etag,则后续请求则会带上对应的请求字段 If-Modified-Since或者 If-None-Match,若响应头没有 Last-Modified 或者 Etag 字段,则请求头也不会有对应的字段。
Last-Modified/If-Modified-Since 二者的值都是GMT格式的时间字符串, Last-Modified 标记最后文件修改时间, 下一次请求时,请求头中会带上 If-Modified-Since 值就是 Last-Modified 告诉服务器我本地缓存的文件最后修改的时间,在服务器上根据文件的最后修改时间判断资源是否有变化, 如果文件没有变更则返回 304 Not Modified ,请求不会返回资源内容,浏览器直接使用本地缓存。当服务器返回 304 Not Modified 的响应时,response header 中不会再添加的 Last-Modified 去试图更新本地缓存的 Last-Modified, 因为既然资源没有变化,那么 Last-Modified 也就不会改变;如果资源有变化,就正常返回返回资源内容,新的 Last-Modified 会在 response header 返回,并在下次请求之前更新本地缓存的 Last-Modified,下次请求时,If-Modified-Since会启用更新后的 Last-Modified。
Etag/If-None-Match, 值都是由服务器为每一个资源生成的唯一标识串,只要资源有变化就这个值就会改变。服务器根据文件本身算出一个哈希值并通过 ETag字段返回给浏览器,接收到 If-None-Match 字段以后,服务器通过比较两者是否一致来判定文件内容是否被改变。与 Last-Modified 不一样的是,当服务器返回 304 Not Modified 的响应时,由于在服务器上ETag 重新计算过,response header中还会把这个 ETag 返回,即使这个 ETag 跟之前的没有变化。
HTTP1.1 中 Etag 的出现主要是为了解决几个 Last-Modified 比较难解决的问题:
- 一些文件也许会周期性的更改,但是内容并不改变(仅仅改变的修改时间),这个时候我们并不希望客户端认为这个文件被修改了,而重新GET;
- 某些文件修改非常频繁,比如在秒以下的时间内进行修改,(比方说1s内修改了N次),If-Modified-Since 能检查到的粒度是秒级的,使用 Etag 就能够保证这种需求下客户端在1秒内能刷新 N 次 cache。
- 某些服务器不能精确的得到文件的最后修改时间。
qq、fire fox 、safari 、chrome 这几个浏览器的访问同一个页面,不同的浏览器在 F5 刷新的时候 ,同一个文件 qq 、fire fox 浏览器会返回 304 Not Nodified,在请求头中不携带 Expires/Cache-Control; 而 chrome 和 safari 刷新的时候,会返回 200 from cache, 没有真正发起请求,走强缓存。可见不同的浏览器反馈是不一致的,所以下面表格中"F5刷新"时 Expires/Cache-Control 会无效我认为是存在一定争议的。而 Ctrl + F5 强制刷新的时候,会暂时禁用强缓存和协商缓存。
跨域请求
浏览器同源策略规定了请求只有在当前页面地址与请求地址的协议+域名+端口号相同时才允许访问,否则会被拦截。
浏览器请求一个页面,先是DNS解析
然后是http三次握手 ->TCP/IP连接
浏览器发送请求
服务器返回请求文件html
浏览器开始渲染 解析成DOM和CSSOM树 ? 可写可不写
然后合并为render树
再通过layout布局计算每个render的位置
最后把渲染树渲染到屏幕上
缓存
DNS解析:
是一个将网址解析成IP 地址的过程。
首先从本地域名服务器中查找,如果找不到就继续向上根域名服务器查找,直到顶级域名,这个过程中存在dns优化有的环节。当查找资源时, 会先找缓存,(浏览器缓存-》系统缓存-》路由器缓存等等),也会根据机器的负载量和距离用户的位置进行dns负载均衡。
https://blog.csdn.net/qq_33203555/article/details/85211595
http概述:超文本传输协议,是互联网上应用最为广泛的一种网络协议
http的缺点
1.通信使用明文可能会被窃听。
2.不验证通信方的身份可能遭遇伪装。
3.无法证明报文的完整性,可能已遭篡改。
https就是在安全的传输层上发送的http。它在将http报文发送给TCP之前,先将其发送给了一个安全层 ,对其进行加密。http安全层是通过ssl及其现代替代协议TSL来实现的。
http/2.0的目标是改善用户加载页面的时候更快
HTTP/2采用二进制格式而非文本格式
HTTP/2是完全多路复用的,而非有序并阻塞的——只需一个连接即可实现并行
对称密钥加密是指加密和解密使用同一个密钥的方式 , 一方通过密钥将信息加密后,把密文传给另一方,另一方通过这个相同的密钥将密文解密,转换成可以理解的明文
非对称加密是加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。指使用一对非对称密钥,即公钥和私钥,公钥可以随意发布,但私钥只有自己知道。发送密文的一方使用对方的公钥进行加密处理,对方接收到加密信息后,使用自己的私钥进行解密。
(1)使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;
(2)HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。
但是https因为加了层ssl,所以在效率方面比较低,会使页面加载的时长延长近50%,也会增加10-20%的耗电。
需要安装证书,在一定基础上增加部署费用,并且报文加密解密对数据传递有一点的效率影响。
socket是传输控制层协议,webSocket是应用层协议
WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握 手需要借助HTTP请求完成。
#HTTP请求缺点:会导致过多不必要的请求,浪费流量和服务器资源,每一次请求、应答,都浪费了一定流量在 相同的头部信息上
然而WebSocket的出现可以弥补这一缺点。在WebSocket中,只需要服务器和浏览器通过HTTP协议进行一个握 手的动作,然后单独建立一条TCP的通信通道进行数据的传送。
#原理:(webSocket)
WebSocket同HTTP一样也是应用层的协议,但是它是一种双向通信协议,是建立在TCP之上的。
1. 浏览器、服务器建立TCP连接,三次握手。这是通信的基础,传输控制层,若失败后续都不执行。
2. TCP连接成功后,浏览器通过HTTP协议向服务器传送WebSocket支持的版本号等信息。(开始前的HTTP握手)
3. 服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据。
4. 当收到了连接成功的消息后,通过TCP通道进行传输通信。
WebSocket与HTTP的关系
# 相同点:
1. 都是一样基于TCP的,都是可靠性传输协议。
2. 都是应用层协议
# 不同点:
3. WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息。HTTP是单向的。
4. WebSocket是需要握手进行建立连接的。
F12 断点 错误附近输出打印 火狐中的firebug IE开发者工具 Emmet
200 OK 请求成功。一般用于GET与POST请求
3开头 重定向
300 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择
301 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
302 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
303 查看其它地址。与301类似。使用GET和POST请求查看
304 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
305 使用代理。所请求的资源必须通过代理访问
306 已经被废弃的HTTP状态码
307 临时重定向。与302类似。使用GET请求重定向
400 客户端请求的语法错误,服务器无法理解
401 请求要求用户的身份认证
402 保留,将来使用
403 服务器理解请求客户端的请求,但是拒绝执行此请求
404 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面
405 客户端请求中的方法被禁止
406 N服务器无法根据客户端请求的内容特性完成请求
407 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
408 服务器等待客户端发送的请求时间过长,超时
409 服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突
410 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
411 服务器无法处理客户端发送的不带Content-Length的请求信息
412 客户端请求信息的先决条件错误
413 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息
414 请求的URI过长(URI通常为网址),服务器无法处理
415 服务器无法处理请求附带的媒体格式
416 客户端请求的范围无效
417 服务器无法满足Expect的请求头信息
500 服务器内部错误,无法完成请求
501 服务器不支持请求的功能,无法完成请求
502 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
503 由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中
504 充当网关或代理的服务器,未及时从远端服务器获取请求
505 服务器不支持请求的HTTP协议的版本,无法完成处理
web网络请求的过程 !!!
1、域名解析
2、为将你的信息从pc上传到服务器上,需要IP协议,ASP协议,ospf协议,发起TCP的三次握手
3、建立TCP连接,发起http请求
4、服务器响应http请求
5、服务器端解析HTML代码,请求HTML中的资源
6、断开TCP连接(4次挥手)
7、浏览器将渲染页面呈现给用户
**(你怎么修改某些规则)**https://www.jianshu.com/p/f8d2ef372adf
Eslint 是一个JavaScript验证工具,有了它可以让你的编辑器像ide一样进行一些静态的错误提示功能.
npm install eslint -g
某些文件关闭eslint检测 在文件的最顶端加上注释/eslint-disable/
关闭某一行代码的eslint检查 // eslint-disable-next-line
.eslintrc.json配置rules选项
(版本回退是什么命令,哪个命令查看已删除的提交commitId)
git-reset 版本回退
git reset --hard xxx 回到上一个版本
git reset --soft xxx 该命令将最近一次提交节点的提交记录回退到暂存区
git reset --mixed xxx 是将最近一次提交节点记录回退到工作区
git log 与 git reflog 查看历史记录(被删除的历史commit ID)
$ git init //在当前目录新建git本地代码库
$ git add . //添加当前目录到文件暂存区
$ git commit -m '' //提交暂存区到仓库区
$ git branch //列出本地所有分支 -a 所有分支
$ git checkout [breach] //切换分支 -b 新建并切换分支
$ git merge [breach] // 合并分支
$ git push //提交
$ git status //显示所有变更文件
$ git log // 显示当前分支的版本历史
提交暂存区git add 出错 git reset HEAD <文件名> 回退
提交本地仓库 git commit出错 : 1.更改 commit 信息:git commit --amend -m“新提交消息” 2.漏提交 : git add missed-file // missed-file 为遗漏提交文件
git commit --amend --no-edit //--no-edit提交消息不会更改
3.git reset --hard commit_id git log查看提交的版本
git revert是提交一个新的版本
git fetch 将远程主机的更新全部放到本地中
在当前分支上执行 $ git stash 命令。将当前分支存起来,这时候再执行 $ git status 命令,显示没有东西需要提交,这个时候你就可以切换到master分支上了。接着就可以在主分支master上创建并切换到新的分支去修复另一个Bug了.
(1)git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit。
(2)git revert是用一次逆向的commit“中和”之前的提交 ,合并的时候回滚的变化不会出现
git reset是之间把某些commit在某个branch上删除 合并时候回滚的commit会被引入
1.Node不是一个后端语言,但是它可以做类似后端语言的功能
2. Node是使用谷歌V8引擎
3. Node是js的一个运行环境
4. Node具有非阻塞I/O 特点
5. Node采用了Common.js规范
# node+koa2 node+express
用于快速构建Node.js项目
node.js 是一个基于 Chrome V8 引擎的 JavaScirpt 运行环境。
Node.js使用了一个事件驱动、非阻塞式I/O的模型,使其轻量又高效**
Node.js基于commonjs规范
事件驱动: 任务执行,发布者,订阅者,事件驱动 .
异步(非阻塞): 执行某一个任务的同时也可以执行其他任务
同步(阻塞): 执行某一个任务,这个任务如果没有执行完成,其他任务必须等待
I/O: 输入/输出( 数据库操作,文件系统操作等 ) - 服务器的环境
非阻塞I/O模型: 当使用Node.js来实现数据库操作、文件系统等操作时,要进行的异步操作,异步操作的核心传统实现方式就是回调函数和事件。
# Node.js的包管理工具npm
优点:Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,异步编程,使其轻量又高效。
缺点:单进程,单线程,只支持单核cpu,不能充分的利用多核cpu服务器。一旦这个进程崩掉,那么整个web服务就崩掉了。
内置模块 http 是用于创建一个能够处理和响应 http 响应的服务
fs 用于对系统文件及目录进行读写操作。
path 提供了一些用于处理文件路径的小工具
Url:帮助我们对提交上来的url进行解析处理
querystring 提供用于解析和格式化 URL 查询字符串的工具。qs.parse() qs.stringify()
# 关系型数据库通过外键关联来建立表与表之间的关系,结构清晰;
# 非关系型数据库通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定,操作存取速度快。
TypeScript具有类型系统,且是JavaScript的超集。 它可以编译成普通的JavaScript代码。 TypeScript支持任意浏览器,任意环境,任意系统并且是开源的。
npm install -g typescript
tsc --init 生成tsconfig.json配置文件
基础数据类型
* number、string、boolean、null 、undefined
* any 表示任意类型
* void 表示空类型
- 内置对象类型
- Array Boolean
- HTMLElement
- HTMLDivElement
- 自定义类型
- 接口 interface
- 类
- 泛型 未来的类型定义的时候不知道是什么类型,调用的时候才知道
- 枚举 enum类型是对JavaScript标准数据类型的一个补充
- never类型表示的是那些永不存在的值的类型。
readonly :只读属性,不可修改
sex? :表示sex是一个可传属性,可以有也可以没有
[propName: string]: any; 表示新增的属性可以是任意类型
arr3: Array<number> 数组类型定义
arr2: (number | string)[]
fn (a: number, b: number) : number 函数类型定义
pwd:输入pwd命令,Linux会输出当前目录。
ls命令用来查看目录的内容。
cd命令用来改变所在目录。
cat命令可以用来合并文件,也可以用来在屏幕上显示整个文件的内容。
grep命令的最大功能是在一堆文件中查找一个特定的字符串。
touch命令用来创建新文件
cp命令用来拷贝文件
mv命令用来移动文件
rm命令用来删除文件。
mkdir 创建文件夹创建目录
project
config
public
scripts
src
components
css
js
modules
store
1、async await
2、对象尾逗号 es2017允许函数对象的定义调用时参数可以加入尾逗号,以及json对象array对象都允许
3、.String.padStart和String.padEnd
padStart:[length,string] 在字符串首位开始添加string直到满足length为止并返回新的字符串;
console.log ( "test".padStart (8,"123456789123") )//1234test
4、.Object.values/Object.entries/Object.getOwnpropertyDescriptors
values: [obj],返回obj自身可枚举属性的属性值的集合;
entries:[obj], 与values类似,返回的一个2元素的数组
getOwnpropertyDescriptors: [obj],返回obj对象的属性描述符 具体应用请点击传送门
5、共享内存、原子对象
https://www.dazhuanlan.com/2019/12/06/5dea1232b5877/
命名捕获
反向引用命名捕获
替换
dotAll模式
标签函数
Promise.prototype.finally()
# 1、双问号操作符
很多情况下前端从后端获取的数据中,并不确定某个属性存不存在,所以会做个判断,如果不存在就给个默认 值避免报错。
但是数字0和空字符串''通常会被“误伤”,比如nba球星威少、乐福、库兹马都是零号。
所以双问号可以更准确得去处理 null 和 undefined
# 2、可选操作符
在点号之前加一个问号
我太喜欢这个可选操作符了,和双问号异曲同工,配合使用安全加倍。
let player = {
};
console.log(player.profile.number ?? "23"); // player.profile is undefined`
console.log(player.profile?.number ?? "23"); //23
# 3、动态导入
假设你有一个utils工具文件,则其中某些功能可能很少使用,而导入其所有依赖项会很浪费资源。现在,我们可以使用 async / await在需要时动态导入依赖项。
replyBtn.addEventListener('click', e => {
e.preventDefault()
import('/modules/editor.js')
.then(Editor => {
let editor = new Editor()
})
.catch(err => {
console.log(err)
});
})
# 4、class的私有属性
类的主要目的之一是将我们的代码包含在可重用的模块中。 我们可能会在很多地方用到这个类,有些属性并不希望被类的外部访问。
现在,通过在变量或函数前面添加一个简单的哈希符号,我们可以将它们完全保留给类内部使用。
class People {
#message = "湖人总冠军"
bb() {
console.log(this.#message)
}
}
const p = new People()
p.bb() // 湖人总冠军
console.log(p.#message) // Private name #message is not defined
# 5、Promise.allSettled
相比于Promise.all ,如果传入的promise中有一个失败(rejected),Promise.all 异步地将失败rejected的那个结果给失败状态的回调函数,而不管其它promise是否完成。
Promise.allSettled 会等所有传入的promise的状态变为 fulfilled 或者 rejected
const p1 = new Promise((res, rej) => setTimeout(res, 1000, 24));
const p2 = new Promise((res, rej) => setTimeout(rej, 1000));
Promise.allSettled([p1, p2]).then(data => console.log(data));
// [
// Object { status: "fulfilled", value: 24},
// Object { status: "rejected", reason: undefined}
// ]
1.浏览器默认的 application/x-www-form-urlencoded
这应该是最常见的 POST 提交数据的方式了。浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。
2.multipart/form-data
这也是一个常见的 POST 数据提交的方式。我们使用表单上传文件时,就要让 form 的 enctype 等于这个值
3.application/json
除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify
4.text/xml
相比于JSON,XML不能更好的适用于数据交换,它包含了太多的包装, 而且它跟大多数编程语言的数据模型不匹配,让大多数程序员感到诧异,XML是面向数据的,JSON是面向对象和结构的,JSON会给程序员一种更加亲切的感觉。
遍历数组每一项,若值为数组则递归遍历,否则concat。
function flatten(arr) {
return arr.reduce((result, item)=> {
return result.concat(Array.isArray(item) ? flatten(item) : item);
}, []);
}
reduce是数组的一种方法,它接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。
reduce包含两个参数:回调函数,传给total的初始值
// 求数组的各项值相加的和:
arr.reduce((total, item)=> {
// total为之前的计算结果,item为数组的各项值
return total + item;
}, 0);
调用数组的toString方法,将数组变为字符串然后再用split分割还原为数组
function flatten(arr) {
return arr.toString().split(',').map(function(item) {
return Number(item);
})
}
因为split分割后形成的数组的每一项值为字符串,所以需要用一个map方法遍历数组将其每一项转换为数值型
和上面的toString一样,join也可以将数组转换为字符串
function flatten(arr) {
return arr.join(',').split(',').map(function(item) {
return parseInt(item);
})
}
递归的遍历每一项,若为数组则继续遍历,否则concat
function flatten(arr) {
var res = [];
arr.map(item => {
if(Array.isArray(item)) {
res = res.concat(flatten(item));
} else {
res.push(item);
}
});
return res;
}
es6的扩展运算符能将二维数组变为一维
[].concat(...[1, 2, 3, [4, 5]]); // [1, 2, 3, 4, 5]
根据这个结果我们可以做一个遍历,若arr中含有数组则使用一次扩展运算符,直至没有为止。
function flatten(arr) {
while(arr.some(item=>Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
TS是由微软开发和维护的免费开源的编程语言,它JS的超集,可编译为纯JS,它是可以在任何浏览器上执行的
TypeScript包含的最简单的数据单元有:数字,字符串,布尔值,Null 和 Undefined等
// 元组 Tuple
1.元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。
// 声明一个元组类型 x
let x: [string, number]
// 初始化 x
x = ['hello', 10] // OK
当访问一个已知索引的元素,会得到正确的类型:
console.log(x[0]) // hello
// 枚举
2.enum类型是对JavaScript标准数据类型的一个补充。枚举类型提供的一个便利是你可以由枚举的值得到它的名字。
enum Color {
Red = 1, Green, Blue}
let colorName: string = Color[2]
console.log(colorName) // 'Green'
// 接口 interface
3.自定义类型
interface People{
readonly id: number, //只读属性
name: string,
age: number,
sex?: string,//表示sex是一个可传属性
[propName:string]: any//表示新增的属性可以是任意类型
}
//! 通过一个接口来完成一个自定义类型
const people:People = {
id: 0,
name: 'lakers',
age: 18
}
people.sex = 'man'
people.a = 1
people.b = 2
people.c = 3
2.1:ts快速、简单、容易学习。
2.2:ts支持面向对象编程,比如类、接口、继承、泛型等。
2.3:ts还提供了错误检查功能。
2.4:ts支持所有js库,因为他是js的超集。
2.5:ts通过使用继承来实现可复用性。
2.6:ts增加了代码的可读性和可维护性。
3.1:TS支持ES6 ,JS不支持。
3.2:TS有可选参数类型,JS没有。
3.3: TS支持模块,JS不支持。
3.4:TS支持泛型,JS不支持。
3.5:TS在开发是突显错误,JS要在运行时才突显错误。
- 它提供了可选静态类型的优点。在这里,Typescript提供了可以添加到变量、函数、属性等的类型。
- Typescript能够编译出一个能在所有浏览器上运行的JavaScript版本。
- TypeScript总是在编译时强调错误,而JavaScript在运行时指出错误。
- TypeScript支持强类型或静态类型,而这不是在JavaScript中。
- 它有助于代码结构。
- 它使用基于类的面向对象编程。
- 它提供了优秀的工具支持和智能感知,后者在添加代码时提供活动提示。
- 它通过定义模块来定义名称空间概念。
5.1:有一定的学习成本,需要理解接口,泛型,类,枚举等前端工程师可能不是很熟的概念。
5.2:短期可能会增加一些开发成本,需要多写一些类型的定义,但对于需要长期维护的项目ts能够减少维护成本。
5.3: 可能和一些库结合的不是很完美。
\d 匹配数字 \D 匹配非数字 \w 匹配数字字母下划线 \W 匹配非数字字母下划线 \n 匹配一个换行符 \s 匹配任何不可见字符包括空格、制表符、换页符等等 \S 匹配任何可见字符
^ 匹配输入字行首 $匹配输入行尾 *(0到多次)匹配前面的子表达式任意次 +(1到多) 匹配前面的子表达式一次或多次(大于等于1次)
?(0或1)匹配前面的子表达式零次或一次
{n}n是一个非负整数,匹配确定的n次
{n,}n是一个非负整数。至少匹配n次
1、CSRF(Cross-site request forgery):跨站请求伪造。
(1)登录受信任网站A,并在本地生成Cookie。(如果用户没有登录网站A,那么网站B在诱导的时候,请求网站A的api接口时,会提示你登录)
(2)在不登出A的情况下,访问危险网站B(其实是利用了网站A的漏洞)
CSRF如何防御
方法一、Token 验证:(用的最多)
(1)服务器发送给客户端一个token;
(2)客户端提交的表单中带着这个token。
(3)如果这个 token 不合法,那么服务器拒绝这个请求。
方法二:隐藏令牌:
把 token 隐藏在 http 的 head头中。
方法二和方法一有点像,本质上没有太大区别,只是使用方式上有区别。
方法三、Referer 验证:
Referer 指的是页面请求来源。意思是,只接受本站的请求,服务器才做响应;如果不是,就拦截。
2、XSS(Cross Site Scripting):跨域脚本攻击。
XSS攻击的核心原理是:不需要你做任何的登录认证,它会通过合法的操作(比如在url中输入、在评论框中输入),向你的页面注入脚本(可能是js、hmtl代码块等)。
最后导致的结果可能是:
盗用Cookie破坏页面的正常结构,插入广告等恶意内容D-doss攻击
XSS的攻击方式
1、反射型
发出请求时,XSS代码出现在url中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS代码。这个过程像一次反射,所以叫反射型XSS。
2、存储型存
储型XSS和反射型XSS的差别在于,提交的代码会存储在服务器端(数据库、内存、文件系统等),下次请求时目标页面时不用再提交XSS代码。
XSS的防范措施(encode + 过滤)
XSS的防范措施主要有三个:
1、编码:
对用户输入的数据进行
HTML Entity 编码。
2、过滤:
移除用户输入的和事件相关的属性。如onerror可以自动触发攻击,还有onclick等。(总而言是,过滤掉一些不安全的内容)移除用户输入的Style节点、Script节点、Iframe节点。(尤其是Script节点,它可是支持跨域的呀,一定要移除)。
3、校正
避免直接对HTML Entity进行解码。使用DOM Parse转换,校正不配对的DOM标签。备注:我们应该去了解一下DOM Parse
这个概念,它的作用是把文本解析成DOM结构。
比较常用的做法是,通过第一步的编码转成文本,然后第三步转成DOM对象,然后经过第二步的过滤。
还有一种简洁的答案:
首先是encode,如果是富文本,就白名单。
CSRF 和 XSS 的区别
区别一:
CSRF:需要用户先登录网站A,获取 cookie。XSS:不需要登录。
区别二:(原理的区别)
CSRF:是利用网站A本身的漏洞,去请求网站A的api。XSS:是向网站 A 注入 JS代码,然后执行 JS 里的代码,篡改网站A的内容。
# webpack与gulp:
Gulp是基于流的前端自动化构建工具,基于流的任务式的工具。WebPack可以看做是模块打包机,它会把开发中的所有资源(图片,css,js等)都看成模块,通过loader(加载器)和plugins(插件)对资源进行处理,打包成符合生成环境的前端资源。
#webpack热更新:
不用刷新浏览器而将新变更的模块替换掉旧的模块。
模块热更新是webpack的一个功能,他可以使得代码修改过后不用刷新浏览器就可以更新,是高级版的自动刷新浏览器。
# devServer中通过hot属性可以控制模块的热替换
//webpack的核心概念分为:入口(Entry)、加载器(loader)、插件(plugins)、出口(output)。
在webpack.config.js中配置多入口多出口,单入口单出口,单入口多出口,插件在plugin中配置
1.入口(Entry):入口起点告诉webpack从哪里开始,并根据依赖关系图确定需要打包的文件内容。
2.出口(output):输出配置
##loader , plugin分别什么作用,哪个配置可以把依赖包抽离出来,不打包进去
//(loader , plugin分别什么作用,哪个配置可以把依赖包抽离出来,不打包进去)**
# 依赖包抽离
const ExtractTextWebapckPlugin= require("extract-text-webpack-plugin") module exclude node_modules
排除excloude排除node_modules
我们在工程所在的根目录,新建一个 webpack.config.js,初始化为
var config = {
};
export default config;
# 3.Loader:用于对模块源码的转换,loader描述了webpack如何处理非js模块,并且在buld中引入这些依赖。比如说:CSS-Loader,Style-Loader等。babel-loader优雅降级配置ES高版本转成低版本
//有哪些常见的Loader?他们是解决什么问题的?
file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
source-map-loader:加载额外的 Source Map 文件,以方便断点调试
image-loader:加载并且压缩图片文件
babel-loader:把 ES6 转换成 ES5
css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
eslint-loader:通过 ESLint 检查 JavaScript 代码
# 4.plugin可以扩展webpack的功能,使得webpack更加灵活。可以在构建的过程中通过webpack的api改变输出的结果,在plugins中进行配置比如资源管理、bundle文件优化等操作
// 有哪些常见的Plugin?他们是解决什么问题的?
define-plugin:定义环境变量
commons-chunk-plugin:提取公共代码
uglifyjs-webpack-plugin:通过UglifyES压缩ES6代码
//webpack打包优化
1.使用 url-loader 优化, 将小图片转化成base64压缩,防止小图片太多请求次数太多。
2.压缩代码。uglifyJsPlugin 压缩js代码, mini-css-extract-plugin 压缩css代码,html-webpack-plugin 进行压缩
3.多进程多实例构建,资源并行解析
thread-loader (推荐使用这个)
4.分离 css 文件并压缩 css 文件
使用 extract-text-webpack-plugin 插件将css文件分离出来。为了使项目加载时候尽早优先加载css样式,也为了解 决js文件体积过大的问题
5.公用代码提取,使用 CDN 加载
对于vue,vuex,vue-router,axios,echarts,swiper等我们可以利用webpack的externals参数来配置,这里我们 设定只需要在生产环境中才需要使用。
# 1 setState
setState在合成事件和钩子函数中是异步的
在原生事件和setTimeout中是同步的
setState的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,
第一个参数可以是对象或者函数 是更新state 第二个参数获取最新的state,副作用操作 dom操作事件触发声明 数据获取
# 1 JSX语法的转化
JSX 仅仅是 createElement() 方法的语法糖(简化语法)
JSX 语法被 @babel/preset-react 插件编译为 createElement() 方法
react.createElement()
React 元素:是一个对象,用来描述你希望在屏幕上看到的内容
# 1.3组件更新机制
setState() 的两个作用: 1. 修改 state 2. 更新组件(UI)
过程:父组件重新渲染时,也会重新渲染子组件。但只会渲染当前组件子树(当前组件及其所有子组件)
# 1.4组件性能优化
减轻 state:只存储跟组件渲染相关的数据
避免不必要的重新渲染 : shouldComponentUpdate(nextProps, nextState) 通过返回值决定该组件是否重新渲染,返回 true 表示重新渲染,false 表示不重新渲染 起到优化作用
# 1.5纯组件 PureComponent
PureComponent 内部自动实现了 shouldComponentUpdate 钩子,不需要手动比较
纯组件内部通过分别 对比 前后两次 props 和 state 的值,来决定是否重新渲染组件
纯组件内部的对比是 shallow compare(浅层对比)
# 1.6虚拟 DOM 和 Diff 算法
数据改变视图更新
初次渲染时,React 会根据初始state(Model),创建一个虚拟 DOM 对象(树)。
根据虚拟 DOM 生成真正的 DOM,渲染到页面中。
当数据变化后(setState()),重新根据新的数据,创建新的虚拟DOM对象(树)。
与上一次得到的虚拟 DOM 对象,使用 Diff 算法 对比(找不同),生成patch补丁对象,得到需要更新的内容。
最终,React 只将变化的内容更新(patch)到 DOM 中,重新渲染到页面。
#相同点:
1.都使用了vdom虚拟dom,如果需要改变任何元素的状态,先改vdom,当有变化产生时,会创建新的vdom,通过diff算法对比新旧vdom的差异,只需要渲染差异部分就行
2.都使用组件化
#不同点:
1. react中有新语法jsx,vue用普通的html
2. vue中父组件和子组件有通信时,父组件数据改变引起子组件改变,子组件会重新渲染;如果父子组件没有通信,父组件改变子组件不会渲染
react中不管是否有数据通信,父组件改变子组件都会渲染
3. react:create-react-app vue : vue-cli
4. react中用redux管理状态,state通过setState更新
5. vue中数据由vuex管理
(1)React严格上只针对MVC的view层,Vue则是MVVM模式
(2)virtual DOM不一样,vue会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树.而对于React而言,每当应用的状态被改变时,全部组件都会重新渲染,所以react中会需要shouldComponentUpdate这个生命周期函数方法来进行控制
(3)组件写法不一样, React推荐的做法是 JSX + inline style, 也就是把HTML和CSS全都写进JavaScript了,即'all in js'; Vue推荐的做法是webpack+vue-loader的单文件组件格式,即html,css,jd写在同一个文件;
(4)数据绑定: vue实现了数据的双向绑定,react数据流动是单向的
(5)state对象在react应用中不可变的,需要使用setState方法更新状态;在vue中,state对象不是必须的,数据由data属性在vue对象中管理
1 *Redux完全解耦*,任何组件包括react组件、vue组件都可以使用它管理状态,
*vuex是专门为**vue**提供的状态管理的工具*。
2 vuex相当于基于redux进一步的封装。
3 vuex用mutation替换了Redux中的reducer。
4 *vuex有自动渲染功能*(redux不具备),所以*不需要更新*。(以上 1、4为核心关键点,答出一条算懂。答出两条说明理解较深。)
Vuex的状态存储是响应式的,当Vue组件从store中读取状态时,若store中状态发生改变,响应的组件也会得到更新状态。但不能直接改变state,必须通过显示的提交(commit)mutations来追踪每一个状态的变化。
# 优点:
//1 采用Virtual DOM
它并不直接对DOM进行操作,引入了一个叫做虚拟DOM的概念,安插在javascript逻辑和实际的DOM之间,性能好
//2 跨浏览器兼容:
虚拟DOM帮助我们解决了跨浏览器问题,它为我们提供了标准化的API,甚至在IE8中都是没问题的。
//3、一切都是组件:
代码更加模块化,重用代码更容易,可维护性高。
//4、单向数据流:
redux实现是一个用于在JavaScript应用中创建单向数据层的架构,它随着React视图库的开发而被Facebook概念化。
//5、同构、纯粹的javascript :
因为搜索引擎的爬虫程序依赖的是服务端响应而不是JavaScript的执行,预渲染你的应用有助于搜索引擎优化。 //6、兼容性好:
比如使用RequireJS来加载和打包,而Browserify和Webpack适用于构建大型应用。它们使得那些艰难的任务不再让人望而生畏。 //7、JSX语法:
为了更加便利的模拟DOM结构,我们使用了JSX语法,可以让我们在JS中编译DOM结构
//8、函数式编程:
JS的最大特点就是函数式编程,在React中,函数式编程可谓式无处不见
# 缺点:
1、不适合单独做一个完整的框架:react是视图层框架MVC,大型项目想要一套完整的框架的话,也许还需要引入Flux和route相关的东西。
在浏览器端用javascript实现一套DOMAPI,基于react进行开发时所有的DOM操作都是通过虚拟dom进行的,当数据变化时,react都会重新构建整个dom树,然后react将当前整个dom树与上一次的dom树进行对比,得到dom结构的区别,然后仅仅将需要变化的部分进行实际的浏览器dom更新
相当于在js和真实DOM中加了一个缓存,利用dom diff算法避免没有必要的dom操作,从而提高性能
用js对象结构表示dom属的结构,然后用这个数构建真的dom树,插到文档中
状态变化的时候,重新构建一颗新的dom树(在虚拟dom中渲染)
新旧dom树进行比较,记录两棵树的差异
把记录下来的差异应用到真实dom树上,进行页面渲染
(在节点中设置ref 通过this.refs.[refname])
pass:这道题本身就不对,虚拟dom中怎么拿dom,简直外行,应改成——当前组件需求,在react组件里如何找到渲染后的某个dom,即某虚拟dom渲染后,如何定位到该dom?
# 给该dom标记ref属性,任意赋值,如ref=’xiaoming’,然后通过this.refs.xiaoming就可以得到它了。
(分层对比,把DOM树按层级拆分,简化了复杂度,只比较同级元素,只匹配相同组件名字的组件,给列表结构的每个单元添加唯一的key属性,方便比较(为什么不用index,如果用index的话追加在后面不会影响,如果追加到前面会影响性能))
react根据渲染逻辑生成新的虚拟dom树,与之前的虚拟dom树比对,自上而下一遍(一次)遍历所有的虚拟dom,一致的不管,有变动的直接删除重构。通过key值对增加、删除、插入的虚拟dom做重构处理。
// react的diff算法是怎么完成的
1.把树形结构按照层级分解,只比较同级元素。
2.只匹配class类名相同的组件,进行对比。
3.通过给列表结构的每一个元素添加唯一的key值进行区分同层次的子节点比较。
4.选择性渲染。
Diff算法是用于比较两个新旧vdom树的差异的,比较完之后会得到一个差异对象,我们称之为patch补丁对象
数据改变视图更新
1 初次渲染时,React 会根据初始state(Model),创建一个虚拟 DOM 对象(树)。
2 根据虚拟 DOM 生成真正的 DOM,渲染到页面中。
3 当数据变化后(setState()),重新根据新的数据,创建新的虚拟DOM对象(树)。
4 与上一次得到的虚拟 DOM 对象,使用 Diff 算法 对比(找不同),生成patch补丁对象,得到需要更新的内容。
5 最终,React 只将变化的内容更新(patch)到 DOM 中,重新渲染到页面。
比较后会出现四种情况:
1、此节点是否被移除 -> 添加新的节点
2、属性是否被改变 -> 旧属性改为新属性
3、文本内容被改变-> 旧内容改为新内容
4、节点要被整个替换 -> 结构完全不相同 移除整个替换
返回一个key
key用来提升react的性能
每一个key都是独一无二的,它的出现不是给开发者用的(为组件设置key后,不能获取组建的key props)它是给react自己用的
简单来说,react利用key来识别组件,相同的key react就认为是相同的组件,这样,后续相同key对应的组件都不会被创建
key相同:若组件属性有变化,则react只更新组件对应的属性,没有变化则不更新
key不同:react会先销毁该组件(有状态组件的componentWillMount会执行)然后重新创建该组件(有状态组件的constructor和componentWillUnMount都会执行)
# key的使用场景
由数组动态创建的子组件
// componentwillunmount => componentwillmount => render =>componentDidMount
##filber
//react16版本以上把diff算法改成了filber算法,
filber算法是将大任务分成了一个个小块,给其他的任务有了可执行的机会,这样就不会导致代码运行卡主。
filber算法是一个优化方案。
React Fiber的方法其实很简单——**分片**。把一个耗时长的任务分成很多小片,每一个小片的运行时间很短,虽然总时间依然很长,但是在每个小片执行完之后,都给其他任务一个执行的机会,这样唯一的线程就不会被独占,其他任务依然有运行的机会。
#单向数据流和双向数据绑定是不同的两个概念,他们之间没有关系
单向数据流:数据从祖先级(外层)组件往子级(内层)组件层层传递,不能逆向流动。这叫单向数据流。
数据双向绑定:组件的UI层(视图层、view层)与本身的状态(state)绑定,一方更改,另一方也会更改,这叫数据双向绑定。
(使用通过reducer创建出来的Store发起一个Action,reducer会执行相应更新state的方法,每当state更新之后,view会根据state做出相应的变化!)
getDefaultProps(用来设置组件的默认属性) getInitialState(用来设置组件的状态)
state(状态)需要在constructor钩子里用*super(props)实现子类继承父类的*this*对象,才能用this.state(state:状态);
props就是属性,*无状态组件*(没用super继承,不能使用state的组件)通过*props.*直接使用,*状态组件*用*this.props*使用。
==props:==一般用于父组件向子组件通信,在组件之间通信使用。 只能在类组件使用 static
==state:==一般用于组件内部的状态维护,更新组建内部的数据,状态,更新子组件的props等。
{
/* {arr.map(item => {item})} */}
{
/* {arr.map((item, index) => {item})} */}
React.createClass 我们最早使用这个方法来构建一个组件“类”,它接受一个对象为参数,对象中必须声明一个render方法,render返回一个组件实例
1、vue异步组件技术
vue-router配置路由,使用vue的异步组件技术,可以实现按需加载。
但是,这种情况下一个组件生成一个js文件。
2.import()语法
3.lazy and suspence
const OtherComponent = lazy(() => import('./OtherComponent'));
<Suspense fallback={
<div>Loading...</div>}>
<OtherComponent />
</Suspense>
当我们组件未渲染完成,需要loading时
4.webpack提供的require.ensure()
5.第三方库比如react-loadable
6.lazy-loader
react 按需加载,关键是让路由动态加载组件,react-router 提供了一个属性 getComponent ,它与 component 属性一样,但是是异步的,当路由匹配时才会调用这个方法,常用于代码分割;
# 函数组件
函数组件中无法使用state,也无法使用组件的生命周期方法
函数组件都是展示性组件,接受props,渲染DOM
函数组件中没有this,但在类组件中仍要绑定this这个琐碎的事,如:在render()方法中要使用this.props来替换props
import React from 'react'
export default function test() {
return (
<div>
</div>
)
}
# 类组件
类组件中可以使用局部状态state和生命周期方法。
import React, {
Component } from 'react'
export default class test extends Component {
render() {
return (
<div>
</div>
)
}
}
https://www.jianshu.com/p/f5c9ec0917bb
React.createClass()、ES6 class 和无状态函数。
# 函数式定义(无状态组件)
function MyComponent(props){
return(
<h1>mycomponent</h1>
)
}
# es5原生
const MyComponent=React.createClass({
render:function () {
return <h1>mycomponent</h1>
}
})
# es6class类
class MyConponent extends React.Component{
constructor(props){
super(props);
}
render() {
return (
<h1>mycomponent</h1>
);
}
}
//注意:无论使用哪种方式创建组件,组件名称的首字母都必须大小,因为我们写的是JSX,最后是需要通过babel转义成es5的语法的,而babel在进行转义JSX语法时,是调用了 React.createElement() 这个方法,这个方法需要接收三个参数:type, config, children。
第一个参数type:声明了这个元素的类型,当创建自定义组件时没有首字母小写时, 而 babel 在转义时把它当成了一个字符串 传递进去了;当首字母大写时,babel 在转义时传递了一个变量进去。问题就在这里,如果传递的是一个字符串,那么在创建虚拟DOM对象时,React会认为这是一个原生的HTML标签,但是这显然不是一个原生的HTML标签,因此去创建一个不存在的标签肯定是会报错的。如果首字母大写,那么就会当成一个变量传递进去,这个时候React会知道这是一个自定义组件,因此他就不会报错了。
# 函数式定义和类定义的对比
函数式定义组件没有state和生命周期函数且不能访问this,而类定义中这些都可以有。
# 类定义和React.createClass原生定义的区别
2.1函数this的绑定
React.createClass创造的组件,其每一个成员函数的this都会自动由React绑定,所以使用时可以直接this.method,而通过class创建组件的成员函数则需要手动绑定,如this.method=this.method.bind(this).
# React.Component三种手动绑定this的方法
//1.在构造函数中绑定
constructor(props){
super(props);
this.enter = this.enter.bind(this)
}
//2.使用bind绑定
<div onClick = {
this.enter.bind(this)}></div>
//3.使用箭头函数绑定
<div onClick = {
(event) => this.enter(event)}></div>
//4.在父级函数中定义一个变量将this存起来(使用作用域链来保留this的引用
你可以使用属性初始值设定项(property initializers)来正确绑定回调,create-react-app 也是默认支持的。在回调中你可以使用箭头函数,但问题是每次组件渲染时都会创建一个新的回调。
16 17版本生命周期钩子函数,16版本后用filber算法---任务分块
react 16版本新特性
https://www.cnblogs.com/qingmingsang/articles/10624710.html
React 16.6.X版本的更新功能
https://www.jianshu.com/p/406bcc058790
React17版本新增
https://cnblogs.com/zhanglanzuopin/p/12987660.html
//1. 两个参数及用法
第一个参数可以是对象或者函数,是更新state 第二个参数获取最新的state,副作用操作,dom操作事件触发声明,数据获取,第三方库实例化
//2. 同步/异步原理
setState在合成事件和钩子函数中是异步的
1.保证内容的一致性。
2.可以达到性能优化。
3.支持state在幕后渲染。
在原生事件和setTimeout中是同步的
setState的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。
setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次 setState , setState 的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新。
https://www.jianshu.com/p/7ab07f8c954c
在代码中调用 setState 函数之后,
React 会将传入的参数对象与组件当前的状态合并,
然后触发所谓的调和过程(Reconciliation)。
经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个 UI 界面。
在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。
在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
SetState
1.#作用:用于修改state
2.#参数: 两个参数
第一个用来修改数据
- 第一个参数如果是对象 不合并 是函数才合并
第二个用来获取修改后的最新数据
3.#注意事项
setState不能写在render函数中,因为单线程,任务执行完才能执行另一个
在合成事件中,setState是异步的,但是在#原生事件中是同步的
4.核心原理:setState合并
为了性能优化,同一个方法中执行多次的setState,会合并,合并到一个队列中统一处理
任务合并后得到的值为新值
短时间内快速进行了多次的setState操作时,setState会合并成一个
5.面试题:setState原理?
setState -- 入队列 -- 出队列 -- 清空队列
定义一个数组,每次执行时模拟数组的push方法将state存进数组中
渲染函数取数组中的最后一个值 清空数组
1. 合成事件原理
如果DOM上绑定了过多的事件处理函数,整个页面响应以及内存占用可能都会受到影响。React为了避免这类DOM事件滥用,同时屏蔽底层不同浏览器之间的事件系统差异,实现了一个中间层——SyntheticEvent。
当用户在为onClick添加函数时,React并没有将Click时间绑定在DOM上面。
而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装交给中间层SyntheticEvent(负责所有事件合成)
所以当事件触发的时候,对使用统一的分发函数dispatchEvent将指定函数执行。
2. 与原生事件的区别
React合成事件一套机制:React并不是将click事件直接绑定在dom上面,而是采用事件冒泡的形式冒泡到document上面,然后React将事件封装给正式的函数处理运行和处理。
https://www.jianshu.com/p/fb915d9c99c4
# 1.父-->子
父组件通过向子组件传递 props,子组件得到 props 后进行相应的处理。// 父组件定义数据,子组件用this.props接收父传递的数据
# 2.子-->父
利用回调函数,可以实现子组件向父组件通信:父组件将一个函数作为 props 传递给子组件,子组件调用该回调函数,便可以向父组件通信。
// 父组件定义状态、用来保存子组件数据的函数
// 子组件触发函数传参
# 3. 兄<-->弟
把state放到共有的父组件上,通过父子,子父进行通信。
# 4.跨组件通信
所谓跨级组件通信,就是父组件向子组件的子组件通信,向更深层的子组件通信。跨级组件通信可以采用下面两种方式:
#中间组件层层传递 props
#使用 context 对象 (上下文)
对于第一种方式,如果父组件结构较深,那么中间的每一层组件都要去传递 props,增加了复杂度,并且这些 props 并不是这些中间组件自己所需要的。不过这种方式也是可行的,当组件层次在三层以内可以采用这种方式,当组件嵌套过深时,采用这种方式就需要斟酌了。
使用 context 是另一种可行的方式,context 相当于一个全局变量,是一个大容器,我们可以把要通信的内容放在这个容器中,这样一来,不管嵌套有多深,都可以随意取用。
使用 context 也很简单,需要满足两个条件:
上级组件要声明自己支持 context,并提供一个函数来返回相应的 context 对象
子组件要声明自己需要使用 context
# 5.redux
首先由view dispatch拦截action,然后执行对应reducer并更新到store中,最终views会根据store数据的改变执行界面的刷新渲染操作。
将provider套在整个组件最外面作为根组件存在,可以将属性上的store通过context传给内部所有的容器组件
1、首先我要把关于redux的工具先安装好:如redux,react-redux,redux-thunk,redux-devtools-extension
2、创建store实例,通过createStore来创建,这个实例里接收1个rootReducer和1个中间件执行函数
3、创建分块的数据rootReducer,,通过combineReducers就可以打造rootReducer,里面放分块数据
4、在组件里使用connect高阶组件,接收store里的数据和将actionCreators里的方法绑定到组件上并发送动作到reducer上
5、在reducer中根据action中的type动作类型,判断动作修改数据
6、视图会根据state做出相应的变化
redux组成
state 作用:1 用来存储数据和数据管理的 2、更新视图
reducer:reducer是一个纯函数,接受 旧 state 和 action,根据不同的 Action 做出不同的操作并返回新的 state 。
actions:发送动作给reducer, reducer接收动作,判断动作类型修改数据
修改事件后,组件重新做redux事件的订阅
- Store 数据的管理者和数据的存储者
- ActionCreators 动作的创建者,发送动作给reducers
- React Component 视图
- Reducers 派发器 用于修改数据,并且返回一个新的状态值
Redux 三大原则
#单一数据源 整个应用的 state 被存储在一个 Object tree 中,且只存在于唯一的Store中。
# state 是只读的
唯一改变 state 的方法就是触发 action,action 是一个用于描述发生事件的普通对象,视图部分只需要表达想要修改的意图,所有修改都会被集中化处理。
#使用纯函数来执行修改
为了实现根据 action 修改 state值,我们就需要编写 Reducer。它是一个纯函数,接收先前的 state 和 action 返回新的 state ,随着应用的变大,你可以将它拆成多个小的 Reducer ,分别独立操作 state tree 中的不同部分。
//首先打造store,组件从store中取数据,组件发起用户交互(比如点击事件)到Action Creators,ActionCreators创建动作,通过dispatch发给了Reducers,Reducers接收两个参数(previousState初始值,action动作),处理完成后将newState发送给Store。
(使用通过reducer创建出来的Store发起一个Action,reducer会执行相应的更新state的方法,每当state更新之后,view会根据state做出相应的变化!)
React --> 首先访问ActionCreators --> 通过Reducers改变数据 --> Store -- State --> React
- connect调用的结果是返回一个高阶组件
- connect方法利用了合并分发的原理来帮助我们完成store内容的获取
- 合并: 将store中的所有数据拿到手
- 分发: 将我们UI需要的数据派发出去
原理 # 合并分发 :
合并的意思是:(我们项目中)redux的数据是集中在一处的
分发的意思是:给的是所有数据中的分块的数据
作用 连接React组件与 Redux store。
connect:connect函数的返回值是一个高阶组件,通过高阶组件来获取store中的数据
// connect底层原理:是闭包
mapStateFromProps:从countReducer中解构出num数据,用来获取数据
mapDispatchFromProps:将ActionCreators中的方法绑定到组件上,并且发送action
// connect实现原理:
首先connect之所以会成功,是因为Provider组件:
在原应用组件上包裹一层,使原来整个应用成为Provider的子组件
接收Redux的store作为props,通过context对象传递给子孙组件上的connect
那connect做了些什么呢?
它真正连接 Redux 和 React,它包在我们的容器组件的外一层,它接收上面 Provider 提供的 store 里面的 state 和 dispatch,传给一个构造函数,返回一个对象,以属性形式传给我们的容器组件。
关于它的源码
connect是一个高阶函数,首先传入mapStateFromProps、mapDispatchFromProps,然后返回一个生产Component的函数(wrapWithConnect),然后再将真正的Component作为参数传入wrapWithConnect,这样就生产出一个经过包裹的Connect组件,该组件具有如下特点:
通过props.store获取祖先Component的store
props包括stateProps、dispatchProps、parentProps,合并在一起得到nextState,作为props传给真正的Component
componentDidMount时,添加事件this.store.subscribe(this.handleChange),实现页面交互
shouldComponentUpdate时判断是否有避免进行渲染,提升页面性能,并得到nextState
componentWillUnmount时移除注册的事件this.handleChange
flux 是 react 中的类似于 vuex 的公共状态管理方案,它是 Facebook 官方给出的应用架构,利用数据的单向流动的形式对公共状态进行管理。现已不推荐使用。但是为了能更好的理解 Redux 方案,还是有必要熟悉 flux 的工作流程滴~
使用 cnpm i flux -S 的方式进行安装。
flux的组成
flux 是 react 中的类似于 vuex 的公共状态管理方案,它是 Facebook 官方给出的应用架构,利用数据的单向流动的形式对公共状态进行管理。现已不推荐使用。但是为了能更好的理解 Redux 方案,还是有必要熟悉 flux 的工作流程滴~
View:视图层
Action:视图发出的消息
Dispatcher:派发者,用来接收Action,执行回调函数
Store:数据层,存放状态,一旦发生改动
flux 在进行数据更新时,会经历以下几步:
用户与 View 层交互,触发 Action
Action 使用 dispatcher.dispatch 将Action自己的状态发送给dispatcher
dispatcher 通过register注册事件,再通过Action传入的类型来触发对应的 Store 回调进行更新
Store 里进行相应的数据更新,并触发 View 层事件使试图也同步更新
View层 收到信号进行更新
redux是独立的应用状态管理工具。它是可以独立于react之外的。如果我们需要在react当中运用它,那么我们需要手动订阅store的状态变化,来对我们的react组件进行更新。
那么react-reudx这个工具,就帮我们实现了这个功能,我们只需对store进行处理,react组件就会有相应的变化。
Redux的核心由三部分组成:Store, Action, Reducer。
//Store : 是个对象,贯穿你整个应用的数据都应该存储在这里。
//Action: 是个对象,必须包含type这个属性,reducer将根据这个属性值来对store进行相应的处理。除此之外的属性,就是进行这个操作需要的数据。
//Reducer: 是个函数。接受两个参数:要修改的数据(state) 和 action对象。根据action.type来决定采用的操作,对state进行修改,最后返回新的state。
总结
Redux: store, action, reducer
store: getState, dispatch, subscribe
combineReducers
createStore
store ️ dispatch ️ action ️ reducer
react-redux:
connect: 将store作为props注入
Provider: 使store在子孙组件的connect中能够获取到
https://www.cnblogs.com/shaoshuai0305/p/12890513.html
http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html
1. 是什么/适用场景?
Redux是一款状态管理库,随着应用程序单页面需求的越来越复杂,应用状态的管理也变得越来越混乱,而Redux的就是为解决这一问题而出现的。在一个大型的应用程序中,应用的状态不仅包括从服务器获取的数据,还包括本地创建的数据,以及反应本地UI状态的数据,而Redux正是为解决这一复杂问题而存在的
2. 组成部分/4个API?
首先由view dispatch拦截action,然后执行对应reducer并更新到store中,最终views会根据store数据的改变执行界面的刷新渲染操作
ReactComponents=views,视图
store 存储管理数据
ActionCreators 创建动作
Reducers 动作触发者 修改数据
3. 工作流/数据流??
redux作为一种单向数据流的实现,配合react非常好用,尤其是在项目比较大,逻辑比较复杂的时候,单向数据流的思想能使数据的流向、变化都能得到清晰的控制,并且能很好的划分业务逻辑和视图逻辑。
4. 三大原则??
• 单一数据源
整个应用的State被存储在一个状态树中,且只存在于唯一的Store中。
• State是只读的 //需要拷贝
对于Redux来说,任何时候都不能直接修改state,唯一改变state的方法就是通过触发action来间接的修改。而这一看似繁琐的状态修改方式实际上反映了Rudux状态管理流程的核心思想,并因此保证了大型应用中状态的有效管理。
• 应用状态的改变通过纯函数来完成
Redux使用纯函数方式来执行状态的修改,Action表明了修改状态值的意图,而真正执行状态修改的则是Reducer。且Reducer必须是一个纯函数,当Reducer接收到Action时,Action并不能直接修改State的值,而是通过创建一个新的状态对象来返回修改的状态。
5. 弊端?
thunks:处理异步操作
saga:saga相当于在redux原有的数据流中多了一层监控,捕获监听到的action,进行处理后,put一个新的action给相应的reducer去处理。
logger:提供日志输出
devtools:它可以让你实时的监控Redux的状态树的Store)
http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_two_async_operations.html
// 好处
可以进行前后端数据交互
// 缺点
1.将带有数据请求的action和没有带有数据请求的action混在一起了
2.异步请求的action代码过于复杂,且异步操作太分散。
3.action的形式不统一,有多个不统一的异步请求操作,就要写多个。
缺点解决: 弃用redux-thunk,使用redux-saga
redux-saga可以将异步action和普通action区别开来
https://www.jianshu.com/p/eff14939498c
https://www.cnblogs.com/vvjiang/p/9505646.html
https://www.jianshu.com/p/eff14939498c
// 优点
1.集中处理了所有的异步操作。
2.将异步与reducer区分开了,更加优雅,适合大量的api请求。redux-saga可以将异步action和普通action区别开来,控制器与更优雅的异步处理
而redux-saga就是用Generator来处理异步。
redux-saga文档并没有说自己是处理异步的工具,而是说用来处理边际效应(side effects),这里的边际效应你可以理解为程序对外部的操作,比如请求后端,比如操作文件。
redux-saga同样是一个redux中间件,它的定位就是通过集中控制action,起到一个类似于MVC中控制器的效果。
同时它的语法使得复杂异步操作不会像promise那样出现很多then的情况,更容易进行各类测试。
这个东西有它的好处,同样也有它不好的地方,那就是比较复杂,有一定的学习成本。
https://blog.csdn.net/qq_37210523/article/details/84110035
1. 组成部分
actions->state->computed values->Reactions
2. 工作流
在mobx中, 数据是通过加 @observable 作为可监测的被观察者, 在view层中, 你可以通过添加@observer 将view作为观察者,对数据进行监测, 如果要触发改变数据,则使用@action, 事实上,你可以直接在view层改变数据, 但这种方式不便监控数据,因此不推荐直接改变数据。 而@computed可以用来计算数据, 也可以是计算多个数据之后返回新的数据, 如果其中数据改变, @computed也会触发改变
3. 优点
不同于redux的单一数据流, mobx中,你可以同时在各个地方使用同一份state, 也可以在一个页面中使用多个store文件
参数顺序不同 事件(events)和事件源(seleter)的顺序不同,delegate是先事件源在事件,on相反
onChange输入框内容改变时候回调 value输入的内容 defaultValue输入框默认值
input默认值在input中绑定value 定义一个state和input双向数据绑定成受控组件,定义一个事件改变的时候获取e.target.value
受控组件
#用组件的状态控制表单元素的value属性,这个表单输入元素就叫做受控组件
使React的state成为唯一数据源,渲染表单的React组件还控制着用户输入过程中表单发生的操作,被React以这种方式控制的表单输入元素就叫做‘受控组件’
//在父组件中定义状态,子组件中改变状态父组件中的数据也会改变
非受控组件
# 表单自己管理自己的状态就叫做非受控组件(普通的表单元素)
半受控组件
官方没有这个概念,是自己使用得到的经验
# 子组件表单的value值是父组件传下来的,不受自己控制,只能由父组件来控制
1.名词解释/作用??
使函数复用,可以通过给组件传递方法来复用高阶组件中的函数方法。
高阶组件特点
高阶组件是一个函数
高阶组件接收一个组件作为参数进行使用,且需要在render函数中return返回这个组件
高阶组件的目的是为了: 复用组件,将多个组件都要使用的类似逻辑放在同一个地方进行处理,类似于在Vue中封装cookie以供重复使用
2.常用高阶组件4个??
React.memo()
connect()
provider()
withRouter() // 可以使用
3.有自己封装高阶组件过吗?
拖拽封装,给组件里面的标签添加方法就可以实现拖拽,并返回标签的x与y坐标。
深对比,深复制封装、正则封装、页面路由跳转、路由数据接收解析。
路由鉴权组件封装
# 高阶组件是一个以组件为参数并返回一个新组件的函数。
1. HOC 运行你重用代码、逻辑和引导抽象。
2. 最常见的可能是 Redux 的 connect 函数。
3. 除了简单分享工具库和简单的组合,HOC 最好的方式是共享 React 组件之间的行为。
4. 如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC。
5. 使用高阶组件后,我们可以将复用的数据,方法放到组件中,然后传递给自己的组件
https://www.jianshu.com/p/76901410645a
0.为什么使用hook
不必写class组件就可以用state和其他的React特性;
自定义hook实现组件的复用 用useEffects代替生命周期方法 代码更加简洁
1. 出现原因3点??
1为了让函数组件拥有类组件的功能。
原因
比如useState、useReducer 状态定义
比如useEffect、useLayoutEffect 生命周期功能
比如useRef useImperativeHandle 替代了类组件的 Ref
2为了优化函数组件,比如useMemo、useCallback
3 函数组件添加新的方法,如
useDebugValue显示自定义hook,自己添加hook类名
自定义hooks来复用状态,
2. 优点4点??
让类组件可以定义状态
让类组件可以使用生命周期、监听数据
让类组件可以有Ref的功能(父组件获取子组件)
优化函数组件 useMemo、useCallback
自定义hooks来复用状态,
代码量比类组件更少,更清爽。
3. 使用规则2点
不要在循环,条件或嵌套函数中调用 Hook
只在函数组件中使用 Hooks
4. 常用Hook
useState:定义状态 修改状态
useEffect:相当于componentDidMount , componentDidUpdate ,同useLayoutEffect
1 useEffect(() => {
},[]) 相当于componentDidMount钩子作用
2 useEffect(() => {
},[count]) 相当于componentDidMount + componentDidUpdate + watch(只监听数组中数据的变化)钩子作用
3 useEffect(() => {
}) 不跟第二个参数,只要数据改变就触发
4 useEffect(() => {
return () => {
console.log('组件被销毁了') } }) 相当于componentDidMount + componentWillUnmount
componentWillUnmount三个钩子的组合 DOM操作 第三方实例化可以做 清除无用实例和事件
useContext:跨组件通信 createContext创建一个组件 <numContext.Provider value={
num}>
useDebugValue:自定义 hook 的标签 方便调试台查看
useMemo:记忆组件 动态缓存 新值和旧值一样,不重新渲染页面,优化作用,类似于shouldComponentUpdate useCallBack
useRef:返回一个可变的ref对象
useImperativeHandle:将组件中的方法放到外面使用 搭配React.forwardRef
5.自定义hook
类似于高阶组件
高阶组件返回的一个类组件,而自定义Hook可以返回任何东西
高阶组件必须传递一个组件作为参数,而自定义Hook不需要
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// 在开发者工具中的这个 Hook 旁边显示标签
// e.g. "FriendStatus: Online"
useDebugValue(isOnline ? 'Online' : 'Offline');
return isOnline;
}
可以理解为数据的操作都在hook里进行,而外部只关心自己想要的。我只要数据列表,获取产品钩子(可能并不需要,可通过参数变更从而触发重新获取数据)、删除产品钩子
Effect 第二个参数
第二个参数可以支持数组中有几个函数吗 不可以
依赖性可以是一些props的属性
protoType.componentDidMount
初始化阶段
constructor 通过super将绑定在自己身上的数据赋值给this.props;state定义;this绑定
componentWillMount-17弃用
UNSAFE_componentWillMount:组件即将挂载,初始化事件,为挂载做准备,内部操作
render:解析this.state,this.props,将jsx型的vdom渲染为对象类型的vdom,render函数中不允许使用this.setState()
componentDidMount:1.组件挂载结束2.vdom->真实dom3.发送数据请求,赋值给state4.真实dom操作,第三方库实例化
更新阶段
componentWillReceiveProps-17弃用:1.用于接收新的props值2.这个钩子的实际作用:判断组件身上的props是否改变
shouldComponentUpdate:1.可以决定组件是否要重新渲染2.接收新旧状态,用于做对比(浅对比)
组件接受到新的属性或新的状态时调用,在这里可以做一次性能优化。默认是true更新,可以控制是否需要更新来达到一次性能优化。
componentWillUpdate-17弃用:1.表示组件更新前的准备,这里的属性和状态是不能修改的。
render 页面渲染
componentDidUpdate 组件更新完毕。
销毁阶段
componentWillUnMount 组件销毁,这里可以清除绑定的事件或定时器
错误处理阶段
componentDidCatch 用来捕获子组件throw出来的错误
17版本 废弃componentWillMount componentWillReceiveProps componentWillUpdate
static getDerivedStateFromProps
1.这是一个静态方法,不能使用this
2.提供了一次修改state的机会
getSnapshotBeforeUpdate
1.快照
2.用于传递一个值给componentDidUpdate
15版本 getDefaultProps定义属性 , getInitialState定义状态
2. 15.x VS 16.3
3. 16.3 VS 17.x
4. 业务中常用的?
constructor,render,componentDidMount,shouldComponentUpdate,componentDidUpdate
shouldComponentUpdate 返回一个布尔值 控制render()是否执行 / 是否重新渲染页面
定义state flag:false 作为开关 定义方法if判断是否为1,改变state flag:true
将方法绑定组件上 可以用三目或者短路原则&&控制组件的显示隐藏
正确答案应该是在willMount和didMount之间
总结一下不建议在constructor和componentWillMount里写的原因是会阻碍组件的实例化,阻碍组件的渲染,如果用setState,在componentWillMount里面触发setState不会重新渲染
react-router依赖基础 - history
history是一个独立的第三方js库,可以用来兼容在不同浏览器、不同环境下对历史记录的管理
老浏览器的history: 主要通过hash来实现,对应createHashHistory
高版本浏览器: 通过html5里面的history,对应createBrowserHistory
node环境下: 主要存储在历史记录memeory里面,对应createMemoryHistory
抽象了一个公共的文件createHistory:
此时的location跟浏览器原生的location是不相同的,最大的区别就在于里面多了key字段,history内部通过key来进行location的操作
原理:
//1.1执行URL前进
createBrowserHistory: pushState、replaceState
createHashHistory: location.hash=*** location.replace()
createMemoryHistory: 在内存中进行历史记录的存储
//1.2检测URL回退
createBrowserHistory: popstate
createHashHistory: hashchange
createMemoryHistory: 因为是在内存中操作,跟浏览器没有关系,不涉及UI层面的事情,所以可以直接进行历史信息的回退
//1.3state的存储
为了维护state的状态,将其存储在sessionStorage里面:
基本原理:实现URL与UI可视化界面的同步。其中在react-router中,URL对应Location对象,而UI是由react components来决定的,这样就转变成location与components之间的同步问题。
在react-router中最主要的component是Router RouterContext Link,history库起到了中间桥梁的作用。
####
安装react-router-dom
-Link组件用于点击链接跳转其他页面,没有路由激活
-NavLink 用于有路由激活效果的
1.Switch:表示一次只渲染一个组件
2.Route:路由组件,用于展示一个组件 同router-view
3.Redirect:重定向
4.lazy + Suspense :实现路由懒加载
5.exact:路径完全匹配
6.fallback:组件切换时候的转场组件
http://www.mamicode.com/info-detail-2201085.html
//原理:
实现URL与UI界面的同步,其中在react-router中URL对应loaction对象,而UI是由react 组件components来决定的,这样就转变成了location与组件component之间的同步问题了。
//react-router3到4有什么变化
在 V3 中,我们是将整个庞大的 router 直接丢给 ,而在 V4 中,除了 BrowserRouter, 我们丢给 DOM 的是我们的应用程序本身 另外,V4 中,我们不再使用 {
props.children} 来嵌套组件了,替代的 <Route>,当 route 匹配时,子组件会被渲染到 <Route> 书写的地方
# (router3需要进行集中式管 理,router4的话哪里需要在哪里配置就可以了)
其中在react-router中,URL对应Location对象,而UI是由react components来决定的,这样就转变成location与components之间的同步问题
Router 在 react component 生命周期之组件被挂载前 componentWillMount 中使用 this.history.listen 去注册了 url 更新的回调函数。回调函数将在 url 更新时触发,回调中的 setState 起到 render 了新的 component 的作用。
优点:
1.简单:不需要手工维护路由state,使代码变得简单。
2.路由配置:可以通过组件配置对象来进行路由的配置。
3.路由切换:可以通过<Link><NavLink> Redirect进行路由切换。
4.路由加载:可以同步,异步加载实现按需加载。
5.使用方式:不仅可以再浏览器端使用,也可以在服务器端使用。
缺点:
API不太稳定,在升级版本的时候需要进行代码变动。
//react路由模式
我们一直在使用的路由方式是BrowserRouter,也就是浏览器的路由方式,其实React还有几种路由方式:
1、BrowserRouter:浏览器的路由方式,也就是在开发中最常使用的路由方式
2、HashRouter:在路径前加入#号成为一个哈希值,Hash模式的好处是,再也不会因为我们刷新而找不到我们的对应路径
3、MemoryRouter:不存储history,所有路由过程保存在内存里,不能进行前进后退,因为地址栏没有发生任何变化
4、NativeRouter:经常配合ReactNative使用,多用于移动端
5、StaticRouter:设置静态路由,需要和后台服务器配合设置,比如设置服务端渲染时使用
<Redirect from="**" to="/xxx>
https://www.jianshu.com/p/ad8cc02b9e6c
1.params
<Route path='/path/:name' component={
Path}/>
<link to="/path/2">xxx</Link>
this.props.history.push({
pathname:"/path/" + name});
读取参数用:this.props.match.params.name
优势 : 刷新地址栏,参数依然存在
缺点:只能传字符串,并且,如果传的值太多的话,url会变得长而丑陋。
2.query
<Route path='/query' component={
Query}/>
<Link to={
{
path : ' /query' , query : {
name : 'sunny' }}}>
this.props.history.push({
pathname:"/query",query: {
name : 'sunny' }});
读取参数用: this.props.location.query.name
优势:传参优雅,传递参数可传对象;
缺点:刷新地址栏,参数丢失
3.state
<Route path='/sort ' component={
Sort}/>
<Link to={
{
path : ' /sort ' , state : {
name : 'sunny' }}}>
this.props.history.push({
pathname:"/sort ",state : {
name : 'sunny' }});
读取参数用: this.props.location.query.state
优缺点同query
4.search
<Route path='/web/departManange ' component={
DepartManange}/>
<link to="web/departManange?tenantId=12121212">xxx</Link>
this.props.history.push({
pathname:"/web/departManange?tenantId" + row.tenantId});
读取参数用: this.props.location.search
优缺点同params
Vue路由传参 : 路径传参 name:”xxx” params {“xx”=”xxx”} 用this.$router.params接受
query传参 path:”xxx” query:{ } 用this.$router.query
react路由传参:通过params
第一步在路由表中设置
第二步在link中设置XXXX 或者在js中设置
this.props.router.push( '/sort/'+'2' )
第三步 通过 this.props.params.id(传过来的参数)通过query 或者this.props.router.push({ path : '/sort' ,query : { name: ' sunny'} }) 通过this.location.query.name
通过state 同上 但是state穿过去的参数是保密的
const table = (location, cb) => {
require.ensure(
[], (require) => { cb(null, require('./pages/menu/table').default) }, 'table')
}
通过地址栏的变化,进行组件的安装和卸载
可以通过es6 的结构赋值 例如:let {title} = this.state
通过ref标记来调用
(原型对象上)
React Native能在手机上创建原生应用,React在这方面处于领先位置。使用JavaScript, CSS和HTML创建原生移动应用,这是一个重要的革新。Vue社区与阿里合作开发Vue版的React Native——Weex也很不错,但仍处于开发状态且并没经过实际项目的验证。
既拥有Native的用户体验、又保留React的开发效率
React Native与React.js的主要区别还是JSX,它使用XML标记的方式去直接声明界面,将HTML直接嵌入到JavaScript代码中
完整的中后台设计系统解决方案
Alibaba Fusion Design 最初来源于一个阿里巴巴内部的中台项目
antv
npm install @antv/g2
G2 G6关系数据可视化图表分析工具 F2移动端可视化 L7地图空间数据可视化
升级到最新的版本
1.利用shouldcomponentUpdata钩子函数来判断组件是否需要渲染。
2.给dom遍历加上唯一的key值,尽量不要使用index。
3.函数的bind尽量写在constructor里,避免每次render重新bind。
4.减少对真实DOM的操作。
5.组件异步加载
6.能用const就用const。
7.多使用无状态组件
dva: dva 首先是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架。--- 来自官方。相比于cra只是多了内置的redux和redux-saga,帮我们处理了数据流这方面的需求而已。如果只是想要达到这个效果的话,直接在cra中增加dva-core的依赖也是可以做到的。
umi:是一个可插拔的企业级 react 应用框架。umi和cra都是应用框架,可能相比cra来说umi的功能点更多一些,只能说是功能性的话umi要相对来说更胜一筹
1、通过jsonp跨域
2、通过修改document.domain来跨子域
3、使用window.name来进行跨域
4、使用HTML5中新引进的window.postMessage方法来跨域传送数据(但是缺点是IE6、IE7不支持)
在react项目中使用Echarts的时候,在componentDidMount中执行绘制图表的函数echarts。Init(#id),提示获取不到宽高,因此,绘制出来的图标呈现出压扁的状态或者没有绘制
解决办法:给compoentDidMount中的drawCharts方法加上一个延时器
根据用户需求进行更改
是请讲一下 后端给的数据是列表 前端实现的过程描述一下
可以使用CDN资源引入,将插件的占用空间减小
容器组件负责全部的数据状态,交互逻辑
函数式组件负责使用数据,展示数据
Connect函数来从ui组件中生成一个容器组件
localStorage cookie的存在时间
前端工程化可以自动化处理一些繁复的工作,提高开发效率,减少低级错误(gulp,webpack)
小程序是基于双线程模型的,在这个模型中,小程序的逻辑层与渲染层分开在不同的线程运行,这跟传统的Web 单线程模型有很大的不同,使得小程序架构上多了一些复杂度,也多了一些限制,同时具备渲染快、加载快等优点。
小程序的运行环境分成渲染层和逻辑层,而WXML模板和WXSS样式工作在渲染层,JS脚本工作在逻辑层,小程序的渲染层和逻辑层分离。
首先,微信小程序已经提供了一套 view, data, model, router 层的开发工具,
对于开发简单应用,小程序是可以比 webapp 更加快速的。但是实际上微信小程序提供的这一套开发框架,要开发一些复杂应用,是很困难的,因为:小程序不支持 npm 等 package manager无法复用社区中已经很成熟的 web 框架和工具组件只能封装 view 和 style,无法封装行为(handler),行为只能定义在 page 上小程序有 1mb 的限制,所以我们只能将图片之类的静态资源事先放在服务器上注:以上的问题其实还是可以解决的,但是要付出较大的成本。
其次,微信小程序是由微信自己来 host,开发者只需要上传就好,而微信 webapp 需要开发者自己 host,还需要注册域名甚至备案才可以调用微信接口以及跟公众号集成。所以微信小程序降低了开发者的门槛。
综上,对于简单的工具型应用,微信小程序可以让开发者加快开发速度,降低发布门槛,这种类型的应用比较适合微信小程序。
对于复杂的应用,webapp 是更适合的形式。
//运行环境预加载
这步是微信做的。微信会在用户打开小程序之前就已经准备好环境,用户点击小程序入口后,直接下载小程序的代码包即可。
//下载代码包启动小程序
小程序代码包里面的代码,不是小程序的源代码,而是编译、压缩、打包之后的代码包。
1.前端记录所有的权限。用户登录后,后端返回用户角色,前端根据角色自行分配页面。优点是前端完全控制,想怎么改就怎么改;缺点是当角色越来越多时,可能会给前端路由编写上带来一定的麻烦。
2.前端仅记录页面,后端记录权限。用户登陆后,后端返回用户权限列表,前端根据该列表生成可访问页面。优点是前端完全基于后端数据,后期几乎没有维护成本;缺点是为了降低维护成本,必须拥有菜单配置页面及权限分配页面,否则就是噩梦。
本篇采用第二种方式:接口权限控制
上文中说到,前端权限控制中,真正能实现安全的是接口,所以先实现接口的权限控制,而且这里听上去很重要,但实际上却很简单。接口权限控制说白了就是对用户的校验。正常来说,在用户登录时服务器应给前台返回一个token,以后前台每次调用接口时都需要带上这个token,服务端获取到这个token后进行比对,如果通过则可以访问。
# 根据后端返回的身份信息来决定跳转页面
vue对比react
http://caibaojian.com/vue-vs-react.html
react优缺点
https://www.cnblogs.com/qiqi715/p/10513195.html
react生命周期
https://www.jianshu.com/p/b331d0e4b398
Redux 流程
https://www.cnblogs.com/goodjobluo/p/9077010.html
git情景题
https://www.jianshu.com/p/34bf7e7ee694
Es6
https://www.cnblogs.com/ainyi/p/8537027.html
异步操作
https://www.jianshu.com/p/6f91e7696b91
虚拟dom diff算法
https://www.cnblogs.com/xiaohanga/p/11075861.html
setState
https://www.cnblogs.com/dreamcc/p/11409053.html
本地存储
https://zhuanlan.zhihu.com/p/25567678
react路由传参
https://www.jianshu.com/p/ad8cc02b9e6c
Eventloop 浏览器与Node的事件循环(Event Loop)有何区别
https://zhuanlan.zhihu.com/p/55511602
https://www.jianshu.com/p/5f1a8f586019
https://www.jianshu.com/p/deedcbf68880
Webpack
https://www.cnblogs.com/gaoht/p/11310365.html
Webpack优化
https://www.jianshu.com/p/773760145ea5
Eslint https://www.jianshu.com/p/f8d2ef372adf
node基于v8运行环境,非阻塞i/o
https://blog.csdn.net/banana960531/article/details/89043185
https://www.yuque.com/sunluyong/node
Ts
https://www.cnblogs.com/shangyixuan/p/11400619.html