HTML5基础
H5新增内容
语义化标签:header、nav、footer、section、article、aside 优点:更好的SEO,更清晰的HTML结构,增强了可读性,便于维护开发
header:定义页面或文章头部,通常包含logo、页面标题等 nav:定义只包含导航链接的章节 section:定义文档中的一个章节/部分 aside:定义和页面内容关联度较低的内容(即使删除,剩下的内容仍然合理) footer:定义页面或章节的尾部,通常包含版权信息、法律信息和反馈建议等有效链接地址
媒体标签:audio、video
功能性标签:SVG、canvas
定位:Geolocation
localStorage、sessionStorage
fetch请求方式
input新类型:email、URL、date、datetime、number、range、color、tel、search…
input新属性:placeholder、min/max、list、outline
什么是BFC
块级格式化上下文
根元素HTML就是一个BFC
float值不为none
overflow的值不为visible
display的值为inline-block/ table-cell/ table-caption/ flex/ inline-flex
position的值为absolute或fixed
BFC特性
Box垂直方向的距离由margin决定,属于同一个BFC的两个相邻box的margin会发生重叠
计算BFC的高度时,浮动元素也参与计算
BFC的区域不会与float box发生重叠
BFC内部的Box会在垂直方向,一个接一个的放置
每个元素的margin box的左边会与包含块border box的左边相接触(对于从左到右的格式化,否则相反),即使存在浮动也会如此
BFC就是页面上的一个独立容器,容器里面的元素不会影响到外面的元素
localStorage和sessionStorage
localStorage和sessionStorage一样都是用来存储客户端临时信息的对象。
他们均只能存储字符串类型的对象(虽然规范中可以存储其他原生类型的对象,但是目前为止没有浏览器对其进行实现)。
localStorage生命周期是永久,这意味着除非用户显示在浏览器提供的UI上清除localStorage信息,否则这些信息将永远存在。
sessionStorage生命周期为当前窗口或标签页,一旦窗口或标签页被永久关闭了,那么所有通过sessionStorage存储的数据也就被清空了。
不同浏览器无法共享localStorage或sessionStorage中的信息。相同浏览器的不同页面间可以共享相同的 localStorage(页面属于相同域名和端口),但是不同页面或标签页间无法共享sessionStorage的信息。这里需要注意的是,页面及标 签页仅指顶级窗口,如果一个标签页包含多个iframe标签且他们属于同源页面,那么他们之间是可以共享sessionStorage的。
一个小问题:清除图片间隙
两张图片挨着写(img标签挨在一起)
父元素的font-size设置为0
给图片设置浮动
图片设置为display:block
给图片设置vertical-align:top/bottom/middle
元素水平垂直居中
弹性盒 - 父元素:display:flex;子元素:margin:auto; - 父元素:display:flex;justify-content:center;align-item:center;
定位 - 父元素:position:relative;子元素:position:absolute;left:0;right:0;bottom:0;top:0;margin:auto;
定位+2D变形 - 父元素:position:relative;子元素:position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);
浏览器输入URL后的流程
浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址 - 做域名解析
解析出 IP 地址后,根据该 IP 地址和默认端口 80,和服务器建立TCP连接 - 发起HTTP请求,建立TCP链接
浏览器发出读取文件(URL 中域名后面部分对应的文件)的HTTP 请求,该请求报文作为 TCP 三次握手的第三个报文的数据发送给服务器 - 浏览器发起读取文件请求
服务器对浏览器请求作出响应,并把对应的 html 文本发送给浏览器 - 服务器做出回应,返回对应资源(HTML文件)
释放TCP链接
浏览器解析资源(HTML文件)
重绘和回流
回流:当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建时,这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的,因为要构建render tree 重绘:浏览器会重新绘制受影响的部分到屏幕中,该过程称之为重绘 当render tree中的一些元素需要更新属性时,而这些属性只是影响元素的外观、风格而不会影响布局的时候,比如background-color,文字颜色、边框颜色等。则就称为重绘。
何时发生重绘或回流 - 页面第一次渲染 - 添加或删除可见的DOM元素 - 元素的位置发生变化 - 元素的尺寸发生变化(包括外边距,内边距,边框大小,高度和宽度等) - 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代或者字体改变 - 浏览器窗口尺寸的变化(因为回流是根据视口的大小来计算元素的位置和大小的) - 定位或者浮动,盒模型等 - 获取元素的某些属性
减少重绘或回流 - 使用transform做形变和位移替代定位top - 使用visibility替换display:none, 因为前者只会引起重绘,而后者会引起回流 - 不要使用table布局,可能很小的一个改动会造成整个table的重新布局 - 动画实现的速度选择,动画速度越快,回流次数越多,也可以选择使用requestAnimationFrame - CSS选择符会从右往左匹配查找,因此要避免层级过多 - 将频繁重绘或回流的节点设置为图层,图层能够阻止该节点渲染行为影响别的节点。比如video标签,浏览器会自动将该节点变为图层 - 避免多次读取某些属性 - 合并多次对DOM和样式的修改,然后一次处理掉
css单位
em:相对长度单位,相对于当前元素内部的文本尺寸
rem:css3新增的相对长度单位,相对的是根元素HTML的文本尺寸
vw:相对视窗宽度的百分比(1vw代表视窗宽度1%)
vh:相对视窗高度的百分比
vmax:当前vw和vh中较大的值
vmin:当前vw和vh中较小的值
css长度单位
px:常用的绝对长度单位,相对与显示器分辨率而定位的单位,兼容性较好
百分比:常用相对长度单位,相对于父元素的尺寸取值
pt:绝对长度单位 1px = 0.75pt
清除浮动:清除浮动主要是为了解决,父元素因为子级元素浮动引起的内部高度为0的问题
添加空盒子,空盒子添加clear:both - 缺点:添加无意义标签元素
父元素添加overflow:hidden(触发bfc) - 缺点:内容增多的时候容易造成不会自动换行导致内容被隐藏掉,无法显示要溢出的元素
使用伪元素
. clearfix: after{
content: "" ;
display: block;
height: 0 ;
clear: both;
visibility: hidden;
}
. clearfix{
* zoom: 1 ;
缺点:IE6-7不支持after
使用before和after双伪元素清除
. clearfix: after, . clearfix: before{
content: "" ;
display: table;
}
. clearfix: after{
clear: both;
}
. clearfix{
* zoom: 1 ;
给父元素添加高度
给父元素添加浮动
给父元素添加display:flow-root;
display:flow-root;是新增的方法,无论是内联还是块级元素,使用后都会变成块级元素,同时,该元素会建立一个新的BFC,并且不会给元素带来任何的副作用,但是有兼容问题
浏览器的渲染原理
将HTML文档解析为DOM树
将css样式表解析为cssOM(css对象模型规则树)
将DOM树和cssOM合并为render tree
计算元素位置,给出坐标
绘制元素节点到屏幕上
行内元素
设置宽高无效
margin设置仅左右有效,padding设置上下左右都有效
不会自动换行
块状元素
可以设置宽高
margin,padding上下左右均有效
会自动换行
块状元素排列从上至下(未脱离文档流时)
行内块状元素
不会自动换行
可以设置宽高
默认排列从左至右
css选择器
通配符
元素选择器(标签选择器)
分组选择器(群组选择器)
类选择器
id选择器
属性选择器([title]/a[href=“www.123.com”])
后代选择器(div p span)
子元素选择器(h1>strong)只会选择h1下面的strong标签,再往下面一层的strong标签不会被选择
相邻兄弟选择器(div+p)选择紧跟在div标签后面的p标签,连续多个p跟在div后面也全部选中,中间隔了一个其他标签,则后面的不再选中
兄弟选择器(div+p)选择div元素后面的所有兄弟元素p,是兄弟元素就行
伪类选择器 - 锚伪类: - a:link //未访问的链接 - a:visited //已访问的链接 - a:hover //鼠标移动到链接上 - a:active //选定的链接
div+css 的布局较 table 布局有什么优点
改版的时候更加方便,只需要改动css文件即可
页面加载速度更快,结构化清晰,页面显示简洁
表现与结构相分离
易于SEO
浏览器兼容问题
可以分为两个大的方面:
css兼容
JavaScript兼容
css兼容主要是一些新的css属性不被支持,有的可以通过添加前缀解决,不能通过前缀解决的可以使用其他的css属性模拟 JavaScript兼容主要是一些新的语法和API,比如es6语法,可以使用babel做转码,转为合适的版本,针对API方面的兼容,需要检测浏览器是否支持相关API,再选择使用
css3基础
CSS3新增内容
过渡:transition
动画:animation
2D3D形变(形状转换)transform translate rotate
盒子阴影:box-shadow
图片边框:border-image
文字换行,超出省略
rgba色
渐变
filter
弹性盒
栅格
多列
盒模型
媒体查询
动画和过渡区别
动画不需要事件触发,过渡需要,过渡只有开始和结束两个关键帧,动画可以设置多个关键帧
什么是FOUC(无样式内容闪烁)
如果使用import方式来导入css文件,会导致某些页面在windows下的IE出现奇怪的现象,以无样式显示页面内容的瞬间闪烁,就称之为文档样式短暂失效(Flash of Unstyled Content),简称为FOUC。 原因是当css样式表晚于HTML结构加载时,加载到此样式表时,页面将停止之前的渲染,样式表被下载和解析后,重新渲染页面,就出现了短暂的闪屏现象。 解决:使用Link标签,将css样式表放在head中
css优化
加载性能
– 不要用import方式导入,使用link标签;压缩文件,减少文件体积,提高并发,减少阻塞等。
代码可维护性,健壮性
– 合理命名方式,样式复用,层次结构合理
– 利用css继承减少代码
规则规范
避免使用多类选择符
删除空的css。如 .class{}
正确使用display属性。如:display:inline后不再使用width、height、margin、padding,避免影响解析性能
不要滥用浮动。因为很多css样式问题可能都是浮动引起的
为0的值不需要单位
遵守盒模型规则
不要给h1-h6定义过多样式
JavaScript基础
JavaScript的数据类型
Number
Boolean
String
Undefined
Null
Symbol - ES6新增
BigInt - ES10新增
Object - 引用类型,上面七种属于基本数据类型 数据类型的判断方式
typeof
typeof判断方式会返回七种结果:string、number、boolean、symbol、undefined、object、function,并且在某些特殊类型判断时可能会返回和我们预料不一样的结果,比如: typeof null; //object typeof new Date(); //object
instanceof
instanceof是通过检测原型。表达式为:A instanceof B 。返回true或者false 例如:[] instanceof Array; // true 但是同样:[] instanceof Object;// true 这是因为,根据原型链,他们最后的指向都是相同的,因此,instanceof 只能用来判断两个对象是否属于实例关系, 而不能判断一个对象实例具体属于哪种类型
constructor
当一个函数 Fun被定义时,JS引擎会为Fun添加上 prototype 原型,然后在 prototype上添加 constructor 属性,并让其指向 Fun的引用 这里我们可以看到他们输出了同样的结果: 用法:‘a’.constructor == String // true [].constructor == Array
toString
toString是Object的原型方法,该方法能够最只管的返回我们通常所期望的类型 Object.prototype.toString.call(‘a’) // String Object.prototype.toString.call([]) // Array Object.prototype.toString.call(null) // Null Object.prototype.toString.call(undefined) // Undefined Object.prototype.toString.call(123) // Number Object.prototype.toString.call({}) // Object Object.prototype.toString.call(function A(){}) // Function
闭包
闭包:通俗来讲我们说函数里面嵌套函数的一种形式,或者说是定义在一个函数内部的函数 一句话来概括闭包是指有权访问另一个函数作用域内部变量的函数
希望一个变量可以常驻内存中(延长变量作用域)
避免全局变量的污染
私有成员的存在
变量常驻内存,增加了内存的开销
使用不当会造成内存泄漏
typescript(ts)
不是一门新语言,是用来规范js的语言,是js的超集,常用在大型项目中,使得代码更规范,维护更加方便
不需要去浏览器中浏览效果,就能知道编译错误
类型就是最好的注释,看类型我们就知道这个是什么
即使ts中有编译报错,tsc依旧可以将其编译成js
特点:
增加静态检查,增加代码健壮性
多人协作,提高生产力,降低维护成本
interface和type区别
是ts中定义类型的两种方式
都可以描述一个对象或者函数
interface只能定义对象类型
interface能够声明合并,type不行
type 可以声明基本类型别名,联合类型,元组等类型 typescript官网链接
前端优化
减少HTTP请求 - 根据IP建立TCP链接请求数据
减少DNS查询 - 域名解析为IP地址 - DNS解析数据会缓存一段时间,因此影响首次解析速度
避免页面跳转,客户端收到服务器的跳转回复时,会再次执行页面跳转,跳转次数过多,可能会造成看不到任何页面内容的效果
延迟加载
避免页面主体布局中使用table
减少DOM元素数量
根据域名划分下载内容
减少iframe数量
使用CDN
压缩传输文件
避免空的图片src
css样式表放在顶部
link代替@import
避免滤镜
JavaScript脚本置底
使用外部JavaScript和css文件
减少DOM操作次数
图片懒加载
内存泄漏和内存溢出
内存泄漏:指的是部分内存一直被占用而得不到释放
引起内存泄漏
意外的全局变量引起的内存泄露
闭包引起的内存泄露
没有清理的DOM元素引用 - 获取了一个DOM元素的引用,然后删除了这个DOM元素,但保留了这个元素的引用
被遗忘的定时器或者回调
内存溢出:也称为内存越界。数据存储的空间超出了实际分配的内存,典型如栈溢出 - 递归函数
websocket和HTTP
HTTP:每次HTTP请求都需要做一次tcp链接,只能从客户端发起通信请求,服务端不能主动推送信息
websocket:是长连接状态,客户端可以向服务端请求数据,服务端也可以向客户端推送数据
ES6新特性
暂时性死区:只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”
let和const
let 、const和var
let和var用来声明变量,const用来声明常量,let和const的区别就在于变量和常量,其他规则一样
var声明变量具有变量提升,即变量可以在声明之前调用,值为undefined
let声明变量不在具有变量提升,在声明之前调用会报错
暂时性死区:let和const存在暂时性死区,它们具备块级作用域的特点,声明的变量或者常量被绑定在这个区域内,不再受外部的影响
var允许重复声明同一变量,在同一作用域内,let和const不允许重复声明变量
var声明的是函数作用域,let声明的是块级作用域,即在两个花括号之间
let声明的变量不会成为window对象的属性
解构赋值:按照一定的规则,从数组或对象中提取值,对变量进行赋值
扩展运算符
模板字符串
字符串的新方法
string.fromCodePoint() - 从 Unicode 码点返回对应字符
includes() | startsWith() | endsWith() - 参数字符串是否在原字符串的尾部
repeat() - 返回一个新字符串,表示将原字符串重复n次
数组的新方法
Array.from() - 用于将两类对象转为真正的数组:类似数组的对象(array-like object)和可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)
Array.of() - 用于将一组值,转换为数组
includes()
flat() - 扁平化数组
对象的扩展 - 属性的简洁表示
对象的新方法
object.is() - 比较两个值是否相等 其运算逻辑与 “===” 基本相似 但是有两个不同 object.is(+0,-0)//false 原生显示true object.is(NaN,NaN) //true 原生显示false
Object.assign() - 对象的合并 - 可以实现浅拷贝
Object.keys() - 对象的键名
Object.values() - 对象的键值
Object.entries() - 回一个数组,成员是参数对象自身的(不含继承的)所有可遍历(enumerable)属性的键值对数组
函数的扩展
symbol类型
set和map数据结构
proxy
Iterator接口和for…of
async函数
promise对象
class类
普通函数和箭头函数
箭头函数是普通函数的简洁表示
箭头函数不能作为构造函数使用,也就是不能使用new关键字
箭头函数没有原型属性
箭头函数不能做generator函数,不能使用yield关键字
箭头函数的this会捕获其上下文的this,在定义的时候就决定了
箭头函数的this不能被改变(this并不会受到call、bind、apply的影响)
bind、call和apply
bind、call、apply都是用来重新定义this这个对象的
传参情况:
– bind(this要指向的对象,参数1,参数2…)
– call(this要指向的对象,参数1,参数2…)
– apply(this要指向的对象,[参数1,参数2,…])
可以看出,只有apply传入的参数是放在数组的,bind和call都是一个接一个传入的
函数执行情况:
– call和apply会执行函数,bind会返回一个新函数,需要再次调用这个函数
同源策略
为什么会产生跨域
什么是同源策略
同源策略阻止从一个域上加载的脚本获取或操作另一个域上的文档属性
哪些方式会产生同源策略
端口不同,域名不同,协议不同,域名和域名对应的ip之间
解决跨域的方式
前端 ->jsonp
cors方式
后端代理
反向代理
h5新增postMessage
简述jsonp的特点。
script标签的src属性获取数据不存在跨域
利用callback回调函数传值
仅仅支持get方式
promise
什么是promise
Promise 异步编程的一种解决方案,比传统的解决方案(回调函数)更合理和更强大。
可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
可以在对象之间传递和操作promise,帮助我们处理队列
代码风格更加容易理解和维护
Promise对象代表一个异步操作,有三种状态:pending(进行中)、fulfilled(resolve已成功)和rejected(已失败).
一旦状态设定,就不会再变
promise原型下面三个常用的方法:
Promise.prototype.then
Promise.prototype.catch
Promise.prototype.finally
Promise.all():用于将多个 Promise 实例,包装成一个新的 Promise 实例,接受一个数组作为参数,只有数组里面的每个状态都变成resolve,则新的 Promise 实例状态才会变成resolve.
Promise.race():只要下面的p1, p2, p3中的一个实例状态改变,p的状态就会改变
const p = Promise. race ( [ p1, p2, p3] ) ;
async函数
简单来说他是Generator函数的语法糖 比较就可以发现,async函数只是将 Generator 函数的星号(*)替换成async,将yield替换成await。 async与Generator函数的区别:
语义:async和await,比起星号(*)和yield,语义更加清楚。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
适用性:co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)
返回值:async函数返回值是Promise对象,Generator函数返回值是Iterator对象
执行器:Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器
防抖和节流
防抖:短时间内多次触发同一事件,只执行最后一次,或者只执行最开始的一次,中间的不执行
在事件触发时,开始计时,在规定的时间(delay)内,若再次触发事件,将上一次计时(timer)清空,然后重新开始计时。保证只有在规定时间内没有再次触发事件之后,再去执行这个事件
场景:面resize事件,页面适配时,根据最终呈现的页面情况进行dom渲染,一般使用防抖,只需判断最后一次的变化情况;search搜索联想,用户在不断输入值时,用防抖来节约请求资源
节流:指定时间间隔内,若事件被多次触发,只会执行一次
在事件触发之后,开始计时,在规定的时间(delay)内,若再次触发事件,不对此事件做任何处理。保证在规定时间内只执行一次事件
场景:一般是onrize,onscroll等这些频繁触发的函数,如获取滚动条的位置,然后执行下一步动作;鼠标不断点击触发,mousedown(单位时间内只触发一次);搜索框input事件,例如要支持输入实时搜索可以使用节流方案 总的来说防抖控制的是次数,节流控制的是频率
function debounce ( fn, delayTime) {
let timer = null ;
return function ( ) {
if ( timer) clearTimeout ( timer) ;
timer = setTimeout ( fn, delayTime) ;
} ;
}
function throttle ( fn, delayTime) {
let throttleFlag = true ;
return function ( ) {
if ( ! throttleFlag) return false ;
throttleFlag = false ;
setTimeout ( ( ) => {
fn ( ) ;
throttleFlag = true ;
} , delayTime) ;
}
}
深浅拷贝
深浅拷贝其实只是针对于Object这种引用类型来说的
浅拷贝只复制指向某个对象的指针而不复制对象本身,新旧对象仍共享同一块内存
深拷贝会另外创造一个一模一样的对象,新对象跟旧对象不共享内存,修改新对象不会影旧原对象
当我们把一个对象赋值给一个新的变量时,赋值的其实是该对象的在栈中的地址,而不是在堆中的数据。也就是说两个对象指向的是同一个存储空间,无论哪个对象发生改变,都会改变其存储空间中的内容。
浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果拷贝属性是基本类型,拷贝到的就是基本类型的值;如果拷贝属性是内存地址(引用类型),那么拷贝到的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。也就是说,默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只是复制对象空间而不复制资源本身。
实现浅拷贝
Object.assign():该方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象
const obj = {
a: {
name: "Lucy" , age: 39 } } ;
const copyObj = Object. assign ( {
} , obj) ;
copyObj. a. name = "King" ;
console. log ( obj. a. name) ;
const obj = {
username: 'Tom'
} ;
const obj2 = Object. assign ( {
} , obj) ;
obj2. username = 'King' ;
console. log ( obj) ;
数组的concat方法:Array.prototype.concat()
let arr = [ 1 , 3 , {
username: 'Tom' } ] ;
let arr2= arr. concat ( ) ;
arr2[ 2 ] . username = 'King' ;
console. log ( arr) ;
数组的slice方法:Array.prototype.slice()
let arr = [ 1 , 3 , {
username: 'Tom' } ] ;
let arr2= arr. slice ( ) ;
arr2[ 2 ] . username = 'King2' ;
console. log ( arr) ;
扩展运算符(…)
const arr1= [ 1 , 2 , 5 , 6 , 4 ] ;
const arr2= [ ... arr1] ;
console. log ( arr1 == arr2)
实现深拷贝 5. JSON.parse(JSON.stringify())
let arr = [ 1 , 3 , {
username: 'Tom3' } ] ;
let arr2= JSON . parse ( JSON . stringify ( arr) ) ;
arr2[ 2 ] . username = 'King3' ;
console. log ( arr) ;
但是该方法实现深拷贝有个问题,就是只能对纯数据JSON格式数据进行拷贝,如果涉及到比如函数,就不行了
let arr = [ 1 , 3 , {
username: 'Tom4' } , function ( ) {
} ] ;
let arr2= JSON . parse ( JSON . stringify ( arr) ) ;
arr2[ 2 ] . username = 'King4' ;
console. log ( arr) ;
console. log ( arr2) ;
loadsh函数库实现,用该函数库提供的_.cloneDeep用来做
const _ = require ( 'lodash' ) ;
const obj1 = {
a: 1 ,
b: {
f: {
g: 1 } } ,
c: [ 1 , 2 , 3 ]
} ;
const obj2 = _. cloneDeep ( obj1) ;
console. log ( obj1. b. f === obj2. b. f) ;
手写递归算法:遍历对象、数组直到里边都是基本数据类型,然后再去复制
function checkedType ( target) {
return Object. prototype. toString. call ( target) . slice ( 8 , - 1 ) ;
}
function deepClone ( target) {
let result,
targetType = checkedType ( target) ;
if ( targetType === "object" ) {
result = {
} ;
} else if ( targetType === "Array" ) {
result = [ ] ;
} else {
return target;
}
for ( let i in target) {
let value = target[ i] ;
if (
checkedType ( value) === "Object" ||
checkedType ( value) === "Array"
) {
result[ i] = deepClone ( value) ;
} else {
result[ i] = value;
}
}
return result;
}
原型和原型链
原型:每一个函数都有一个prototype对象属性,这个属性指向该函数的原型对象;每个对象都会有一个__proto__属性,这个属性指向该对象的原型
原型链:实例对象与原型之间的连接,叫做原型链
js运行机制
首先我们需要了解JavaScript的特性就是单线程,单线程简单的说就是同一时间只能做一件事。那么为什么是单线程呢?作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程再某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准? 单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。多线程可以将任务放到不同的线程中去处理。CPU的调度单位是线程,它会在不同的线程之间切换,任务是隶属于线程的 于是,JavaScript中所有任务可以分成两种,一种是同步任务,另一种是异步任务。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入“任务队列”的任务,只有“任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行 主线程从“任务队列”中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件轮询) 这个任务队列都是已经完成的异步操作 对于异步任务队列来说,又分为宏任务和微任务 宏任务:包括整体代码 script,setTimeout,setIntervcal 微任务:Promise,process.nextTick 执行顺序:进入整体代码后,开始第一次循环。接着执行所有的微任务,然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务
开发环境、测试环境、生产环境
开发环境:专门用于程序员开发的环境,一般打开全部错误报告和测试工具,是最基础的环境
测试环境:通常克隆一份生产环境配置,让程序在测试环境工作,是从开发到生产环境的过渡环境,主要测试是否存在bug
生产环境:正式对外提供服务的,一般关掉错误报告。真实的环境,提供给用户的环境
addEventListener(event,function,useCapture)
event:必须。要执行的事件
function:必须。事件处理函数/事件触发时执行的函数
useCapture:可选。默认false-事件在冒泡阶段执行。true-事件在捕获阶段执行
js的异步操作有哪些
定时器是异步的
事件绑定是异步的(事件处理函数)
ajax通常使用异步(也可以设置为同步)
回调函数可以理解为异步
promise
generator
async和await
错误类型 - 常见4种(引用,语法,范围,类型)
引用错误:referenceError 所引用的类型不存在 – 变量未声明
语法错误:syntaxError 使用的语法有错误 – 关键字写错
范围错误:rangeError 使用的值范围有错误 — 栈溢出
类型错误:typeError 使用的类型有错误 – 调用的对象不对 – a():a不是一个函数
es5-严格模式
消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为
为未来新版本的Javascript做好铺垫。
消除代码运行的一些不安全之处,保证代码运行的安全。
提高编译器效率,增加运行速度。
变量必须声明(变量提升不再存在)
禁止使用with语句
禁止this关键字指向全局(window)
函数参数不能重名
禁止八进制表示法 - 017 常规输出15,严格模式下报错
不允许对arguments赋值
禁止使用arguments.callee/arguments.caller(禁止了使用arguments的方法)
es5新增的数组的方法 - 位置方法:indexOf/lastIndexOf - 迭代方法:every/some/filter/map/forEach - 归并方法:reduce/reduceRight - 扁平化:flat
事件绑定,侦听,监听
普通事件和事件绑定的区别 - 普通事件重复绑定后面的会覆盖前面的,事件绑定可以对同一个事件绑定多个事件处理函数
事件绑定标准浏览器和非标准浏览器的区别(四个不一样) - 参数不一样,标准三个,非标准两个,差最后的一个是否冒泡 - 事件类型不一样,是否有‘on’ - 执行顺序不一样。IE浏览器先执行后面的,标准浏览器按顺序执行 - this指向不一样,标准浏览器事件处理函数里面的this当前的操作的元素,但是非标准浏览器的this指向window
事件委托机制
事件委托的概念:利用冒泡原理,将子元素事件委托给父元素
事件委托的优缺点: - 优点:减少事件注册,节约内存,提升性能 - 缺点:所有的事件都用事件委托可能会出现事件误判
调用new操作符
创建一个空对象
this指向该对象
属性和方法被加入到 this 引用的对象中
并且最后隐式的返回 this (返回该对象)
ajax的概念和特点
异步JavaScript和XML,是指一种交互式网页开发技术
AJAX 可以使网页实现异步(局部)更新。
服务器负担的工作转移到客户端,利用客户端的闲置的资源进行处理,减轻服务器和带宽的负担,节约空间和成本。
ajax的优缺点
优点:
局部实现无刷新更新数据
异步通信服务
更好的实现前后端分离
前后端负载平衡
缺点:
无刷新更新数据->对浏览器的back和history机制造成了破坏
数据传输的安全问题
搜索引擎支持较弱
对移动端设备支持不是很好
Es6 module和commonjs的区别
commonjs建立模块依赖是在运行时,ES6module是在编译时
在模块导入方面,CommonJS导入的是值拷贝,ES6 Module导入的是只读的变量映射
ES6 Module通过其静态特性可以进行编译过程中的优化,并且具备处理循环依赖的能力
commonjs输出的是值的拷贝,也就是说一旦输出一个值,模块内的变化不会影响这个值;ES6模块是动态只读引用,ES6中输入的模块变量,只是一个‘符号连接’,变量是只读的,对他重新赋值会报错,export通过接口,输出的是同一个值,不同的脚本加载这个接口,得到的都是同样的实例
commonjs加载的是一个对象,(module.export属性),该对象只有在脚本运行是才会生成;ES6模块不是对象,是一种静态定义,会在解析阶段完成
解析一个完整的URL
包含如下选项: * location: { * href: ‘包含完整的url’, * origin: ‘包含协议到pathname之前的内容’, * protocol: ‘url使用的协议,包含末尾的:’, * host: ‘完整主机名,包含:和端口’, * hostname: ‘主机名,不包含端口’ * port: ‘端口号’, * pathname: ‘服务器上访问资源的路径/开头’, * search: ‘?开头的查询参数’, * hash: ‘#开头的哈希值’ * }
function parseUrl ( url) {
if ( typeof window == 'undefined' ) {
const URL = require ( 'url' ) ;
return URL . parse ( url) ;
} else {
var link = document. createElement ( "a" ) ;
link. href = url;
return {
href: link. href,
origin: link. origin,
protocol: link. protocol,
host: link. host,
hostname: link. hostname,
port: link. port,
port: link. pathname,
search: link. search,
hash: link. hash
}
}
}
http缓存机制
阅读更多
根据是否需要重新向服务器端发起请求可以分为强缓存和协商缓存两大类
强缓存:强缓存与时间期限有关,浏览器会查看缓存中的资源是否过期,过期则重新请求数据,未过期则取得缓存资源做下一步处理
协商缓存:会和服务器做交互,取得缓存会先向服务器确认缓存是否可以使用,如果可以,服务器会返回可用信号,如果不可以,则返回新的资源
两个缓存机制可以同时存在,且强缓存优先级高于协商缓存,意味着请求资源时会先比较强缓存字段,如果成功则不会再执行协商缓存
什么是immutable, 为什么要使用它
Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象
Immutable 实现的原理是 Persistent Data Structure (持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变
js延迟加载的方式
将js脚本放在文档底部,使js脚本尽量最后加载
给js脚本添加defer属性,使js脚本与文档解析同步执行,这样可以使页面渲染不被阻塞
给js脚本添加async属性,使js脚本异步解析,不阻塞页面渲染
动态创建DOM标签的方式,对文档的加载事件进行监听,文档加载完成后动态创建script标签来引入js脚本
js的几种模块规范
commonJS - Node.js就是使用此规范 - 适用于服务端
require引入模块,module.exports暴露模块,一个文件就是一个模块
模块的调用是同步的
AMD - 适用于浏览器端 - require.js使用此规范
define定义模块,require加载模块
模块的调用是异步的
推崇依赖前置,在定义时就声明依赖模块
模块加载完成后执行依赖模块
CMD - sea.js使用此规范
define定义模块,require加载模块
模块调用是异步的
推崇就近依赖,使用时声明模块
下载所有依赖模块不执行,遇到require是执行对应依赖
ES6
forEach和map区别
他们都是用来遍历数组的,第一个参数都是一个函数,这个函数允许传入三个参数 用法:
let result = arr. forEach ( ( item, index, arr) => {
console. log ( item, index, arr) ;
} )
let result = arr. map ( ( item, index, arr) => {
console. log ( item, index, arr) ;
return item* 2
} )
区别: 最大的区别就是forEach没有返回值,map有返回值,这个返回值是一个新数组
修改URL
我们修改URL后的参数是否会刷新页面:会的
那么我们可以用什么方式让它修改了URL但是又不刷新页面:可以用history里面的pushState和replaceState方法
react基础
react新特性
render方法支持直接返回string,number,boolean,null,portal,以及fragments(带有key属性的数组),这可以在一定程度上减少页面的DOM层级
setState传入null时不会再触发更新
fiber算法
react路由鉴权
在之前的版本中,React Router 也提供了类似的 onEnter 钩子,但在 React Router 4.0 版本中,取消了这个方法。React Router 4.0 采用了声明式的组件,路由即组件,要实现路由守卫功能,需要我们自己去写
可以在路由表里面添加路由权限,路由载入的时候如果对应路由权限不满足,则阻止加载这个路由页面
react优缺点
虚拟DOM,提升性能
跨浏览器兼容
组件化开发,代码重用,可维护
函数式编程
jsx语法,在js中编写DOM
缺点:不适合单独做一个完整的框架,react注重于视图层结构,大型项目的完整架构就需要引入其他相关的东西
Redux工作流程
首先,用户发出 Action。然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action。 Reducer 会返回新的 State 。State 一旦有变化,Store 就会调用监听函数,重新渲染 View。
react-redux
react-redux将组件分成UI和容器组件,react-redux提供的connect可以将这两种组件连接起来
redux是独立的应用状态管理工具。它是可以独立于react之外的。如果我们需要在react当中运用它,那么我们需要手动订阅store的状态变化,来对我们的react组件进行更新。react-reudx这个工具,就帮我们实现了这个功能,我们只需对store进行处理,react组件就会有相应的变化
容器组件和UI组件的区别
简述:UI组件负责页面的渲染,容器组件负责页面的逻辑
Redux遵循的三个原则
单一数据源
state只读
数据的改变必须要通过纯函数完成
redux组成
store:用来存储数据和数据管理,更新视图
actions:发送动作给reducer,reducer接收动作,判断动作类型修改数据,修改事件后,组件重新做redux事件的订阅
reducer:是一个纯函数,接收旧state和action,根据不同的 Action 做出不同的操作并返回新的 state
redux的reducer为何要使用纯函数
首先我们理解reducer纯函数:传入的参数不得修改,通过外部状态来修改(比如依赖DOM和全局变量);不得调用非纯函数,比如(Date.now();Math.random())
Redux只通过比较新旧两个对象的存储位置来比较新旧两个对象是否相同(也就是JavaScript的对象浅比较),如果在reducer内部直接修改state对象的旧值,那么新的state和旧的state就指向同一个对象,redux将会认为state没有改变,也就不会触发页面刷新了
redux的核心是提供可预测化的状态管理,即我们需要无论何时特定的action触发行为保持一致的,如果使用了Date.now()等非纯函数,即使同样的action,对于reducer的处理结果也是不同的,也就不能再保证可预测性了
react实现组件复用逻辑
高阶组件
render props
自定义hook
redux-thunk和redux-saga
redux-thunk:Redux store 仅支持同步数据流。使用 thunk 等中间件可以帮助在 Redux 应用中实现异步性(更多关于redux-thunk)
好处:可以进行前后端数据交互 缺点:将带有数据请求的action和没有带有数据请求的action混在一起了; 缺点解决: 弃用redux-thunk,使用redux-saga redux-saga可以将异步action和普通action区别开来
redux-saga:redux-saga是一个用于管理redux应用异步操作的中间件,redux-saga通过创建sagas将所有异步操作逻辑收集在一个地方集中处理,可以用来代替redux-thunk中间件(更多关于redux-saga)
React hooks
hook是React 16.8新增特性
react函数组件和类组件区别
定义组件有两个要求:
组件名称需要大写字母开头
组件返回值只能有一个根元素
区别 - 性能上函数组件略高于类组件(因为类组件需要做实例化,函数组件可以直接执行函数获取返回结果) - 函数组件没有this,没有生命周期,没有state - 类组件有this,有生命周期,有state
为什么有时连续点击多次setState只有一次生效
因为React批处理机制中会对存储的多个setState进行合并
高阶组件
是一个函数,传入一个组件作为参数,对输入组件进行加工,输出一个有特定功能的新组件 高阶组件(high-order component)类似于高阶函数,接收 React 组件作为输入,输出一个新的 React 组件。高阶组件让代码更具有复用性、逻辑性与抽象特征。可以对 render 方法作劫持,也可以控制 props 与 state
实现高阶组件的方法有两种:属性代理和反向代理 - 属性代理:通过高阶组件来传递 props,这种方法即为属性代理 - 反向代理(反向继承):高阶组件的返回组件继承于另一个组件,因为被动的继承了另一个组件,所以所有的调用都会反向
react的connect高阶组件实现, 如何在全局中取得store
作用:连接React组件与 Redux store
connect函数的返回值是一个高阶组件,通过高阶组件来获取store中的数据
connect底层原理:是闭包
mapStateFromProps:从countReducer中解构出state数据,用来获取数据,这里可以获取全部的state,我们需要返回对应组件的state来使用
mapDispatchFromProps:将ActionCreators中的方法绑定到组件上,并且发送action
connect方法利用了合并分发的原理来帮助我们完成store内容的获取
合并: 将store中的所有数据拿到手
分发: 将我们UI需要的数据派发出去
合并分发 :合并的意思是:(我们项目中)redux的数据是集中在一处的,分发的意思是:给的是所有数据中的分块的数据
react的数据加载
constructor里面加载:在这里面获取数据的话,如果时间太长,或者出错,组件就渲染不出来,整个页面都没法渲染了,所以我们通常用作组件state初绐化工作,并不是设计来作加载数据的
componentWillMount:如果使用SSR(服务端渲染),componentWillMount会执行2次,一次在服务端,一次在客户端
目前16版本加入了UNSAFE来标识,这个钩子函数就用的很少了,这个钩子函数可能会执行多次,所以一般也不在这里面做数据请求
render:出现无限render
componentDidMount:确保已经render过一次。提醒我们正确地设置初始状态,这样就不会得到导致错误的"undefined"状态
setState调用后react做了什么工作
React会将当前传入的参数对象与组件当前的状态合并,然后触发调和过程,在调和的过程中,React会以相对高效的方式根据新的状态构建React元素树并且重新渲染整个UI界面
React得到元素树之后,React会自动计算出新的树与老的树的节点的差异,然后根据差异对界面进行最小化的渲染,在React的差异算法中,React能够精确的知道在哪些位置发生看改变以及应该如何去改变,这样就保证了UI是按需更新的而不是重新渲染整个界面
导致单页面首页白屏的原因
主要有三个:1.网络问题 2.文件大小问题 3.电脑性能问题 对于网络和文件大小问题:主要是合并请求,减少请求和文件的打包压缩等优化,所以要解决白屏时间过长的问题我们还是要从根源上解决,那就是服务器返回来的html只有html骨架,并没有什么实际内容可以显示在浏览器中,我们需要做的就是往html中填充内容,让浏览器在发送请求获取js等文件再到解析js生成页面所需要的html这段时间里有内容可以显示在浏览器中
受控组件
表单元素的value值受到组件的state的控制,我们就把这个表单元素称为受控组件 特殊场景:父组件修改state,子组件渲染,即子组件的值受到父组件的控制
非受控组件
表单元素的value值受到自己的控制
react的diff算法
作用: 计算出Virtual DOM中真 正变化的部分,并只针对该部分进行原生DOM操作,而非重新渲染整个页面 只有在react更新阶段才会使用diff算法
react组件通信
父->子组件:父组件定义状态,子组件用属性来接收,父组件更改状态,子组件也会同步更改
子->父组件:父组件传递一个方法给子组件,子组件通过this.props接收方法后调用 但是子组件调用方法时注意:onClick = {() => { changeJk( 子组件的状态做参数传给父组件 ) }}
跨组件:(组件是深层嵌套的父子组件关系)借助中间组件来实现,react下面的createContext,两个通信组件都需要引入createContext,传输组件用<自定义组件名.Provider value={状态}>来包裹子集组件,接收组件用static contextType = context里面自定义的名字 ,使用时用this.context即可
非父子组件:共同的父组件做中介,执行组件通过this.props获取来自父组件的方法,实际上执行的方法是另一个需求组件的方法
总述:要改的状态在哪个组件,方法就写在哪个组件
react的生命周期
挂载阶段/初始化阶段:
constructor:
1.组件的props赋值给this.props
2.定义state
3.普通函数绑定this
componentWillMount:(即将被弃用)
1.初始化事件和生命周期
2.可以请求数据,也可以修改数据
render:
1.生成vDOM
2.解析this.props 和 this.state
!!Note:不能在这个钩子函数里面使用setState,因为js是单线程的,不能要求它在解析this.state的时候又改变state
componentDidMount:
1.组件挂载结束,可以获取数据和做数据的修改
2.真实DOM渲染完成,可以获取真实DOM,操作DOM元素
更新阶段:
componentWillReceiveProps: - 更新props才有这个函数
shouldComponentUpdate(nextProps,nextState): - state更新从这里开始 nextProps,nextState表示变化之后的props和state,这个函数应该返回一个布尔值,默认返回true,true表示渲染,false表示不渲染 shouldComponentUpdate(nextProps,nextState){ return nextProps.value != this.props.value //this.props.value表示的是更新前值,nextProps.value表示的是更新后的值,当两个值相等返回false,即props没有更新,页面不必重新渲染,两个值不等时返回true,即props已经更新,页面需要重新渲染 // return nextState.value != this.state.value //!!Note:这里的nextState.value 和 this.state.value是一样的,所以不能像props一样来做判断,要么用其他方法做判断是否更新,要么不做判断 }
componentWillUpdate:
render:
componentDidUpdate:
数据完成更新后的阶段
可以做dom操作,数据获取,数据修改
第三方库实例化
卸载阶段:
componentWillUnmount:
组件被卸载时执行
用来清除卸载组件时却不会被卸载的实例、事件等
清除全局引用
清除定时器
清除第三方实例
清除事件等
错误捕获阶段:
componentDidCatch(error,info):
error:实际的错误信息
info:错误的位置
这个钩子函数放在父组件,当子组件出现错误时这里反映错误
react优化
更多性能优化点击这里
组件尽可能进行拆分
列表组件优化,添加key属性
bind函数绑定,this的绑定最优在constructor函数里面做绑定
不要滥用props
react路由模式
我们常用的的是BrowserRouter,也就是浏览器的路由方式,另外react还有几种路由方式
BrowserRouter:浏览器的路由方式,也就是在开发中最常使用的路由方式
HashRouter:在路径前加入#号成为一个哈希值,Hash模式的好处是,再也不会因为我们刷新而找不到我们的对应路径
MemoryRouter:不存储history,所有路由过程保存在内存里,不能进行前进后退,因为地址栏没有发生任何变化
NativeRouter:经常配合ReactNative使用,多用于移动端
StaticRouter:设置静态路由,需要和后台服务器配合设置,比如设置服务端渲染时使用
react-router和react-router-dom
react-router:实现了路由的核心功能,react-router-dom: 基于react-router,加入了在浏览器运行环境下的一些功能,例如:Link组件,会渲染一个a标签,BrowserRouter和HashRouter 组件,前者使用pushState和popState事件构建路由,后者使用window.location.hash和hashchange事件构建路由
结论:react-router-dom依赖react-router,所以我们使用npm安装依赖的时候,只需要安装相应环境下的库即可,不用再显式安装react-router。基于浏览器环境的开发,只需要安装react-router-dom
react路由组件
Route:路由界面,执行路由组件载入
Router:路由盒子,包裹需要执行路由的组件
Link:路由组件,实际上解析成一个a标签,需要由一个to属性来执行链接地址。 首页
react-router路由原理
可以实现无刷新条件下切换显示不同的页面,路由的本质就是页面的URL改变时,页面的结果根据URL变化而变化,但是页面不会刷新
hash实现前端路由,在URL中#后面的就是hash值,通过监听hashChange事件,实现hash改变,就改变页面,但是页面不会刷新
history实现路由:使用history提供的方法(go、back、forward、replaceState、pushState)
hash路由和history路由的区别:hash在地址栏有#、history是h5新增,有兼容问题、hash不会重新加载页面、history运用了浏览器的历史记录栈,之前有back,forward,go方法,之后在HTML5中新增了pushState()和replaceState()方法(需要特定浏览器的支持),它们提供了对历史记录进行修改的功能,不过在进行修改时,虽然改变了当前的URL,但是浏览器不会马上向后端发送请求
react原理
setState
setState在合成事件和钩子函数中是异步的 在原生事件和setTimeout中是同步的 setState的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的“异步” 第一个参数可以是对象或者函数 是更新state 第二个参数获取最新的state,副作用操作 dom操作事件触发声明 数据获取
jsx语法
JSX 仅仅是 createElement() 方法的语法糖(简化语法) JSX 语法被 @babel/preset-react 插件编译为 createElement() 方法
组件更新机制
setState() 的两个作用: 1. 修改 state 2. 更新组件(UI) 过程:父组件重新渲染时,也会重新渲染子组件。但只会渲染当前组件子树(当前组件及其所有子组件)
组件性能优化
减轻 state:只存储跟组件渲染相关的数据 避免不必要的重新渲染 : shouldComponentUpdate(nextProps, nextState) 通过返回值决定该组件是否重新渲染,返回 true 表示重新渲染,false 表示不重新渲染 起到优化作用
纯组件 PureComponent
PureComponent 内部自动实现了 shouldComponentUpdate 钩子,不需要手动比较 纯组件内部通过分别 对比 前后两次 props 和 state 的值,来决定是否重新渲染组件 纯组件内部的对比是 shallow compare(浅层对比)
虚拟 DOM 和 Diff 算法
数据改变视图更新 初次渲染时,React 会根据初始state(Model),创建一个虚拟 DOM 对象(树) 根据虚拟 DOM 生成真正的 DOM,渲染到页面中 当数据变化后(setState(),重新根据新的数据,创建新的虚拟DOM对象(树) 与上一次得到的虚拟 DOM 对象,使用 Diff 算法 对比(找不同),生成patch补丁对象,得到需要更新的内容 最终,React 只将变化的内容更新(patch)到 DOM 中,重新渲染到页面
react vs vue
相同:都采用了vDOM,diff算法 - 都使用组件系统 - 都是单向数据流
不同:vue vDom是采用的snabbdom,React则不是 - React 16版本之后采用了新的vdom对比算法 Filber算法 - React 仅仅可以看做是MVC中V,而vue是有完整三个部分的,MVVM
react渲染 - key
react渲染依赖于vDOM,对于列表元素来说,可能只是改变了位置,但是react并不知道改变了位置,所以它会重新渲染后面的元素,增加了DOM操作,但是如果我们给每一个元素加上一个key作为唯一标识,react就可以知道这两个元素只是交换了位置
react中的diff算法,当渲染结构发生变化的时候,react会在vDOM中用diff算法进行一次对比,将发生变化的结构改变之后再根据虚拟dom进行真实DOM的渲染,也就是说,如果存在key值,diff算法就会根据这个key值来进行匹配,在遍历新的dom结构的时候,就会知道这个元素只是改变了位置
什么是jsx
JSX是javascript的语法扩展。它就像一个拥有javascript全部功能的模板语言 在里面可以使用js代码{}
单页面和多页面
单页面(SPA):通俗来说就是只有一个主页面的应用浏览器一开始会加载所有的HTML,JavaScript,css资源。交互的时候由动态路由程序载入相应页面,单页面的跳转仅刷新部分资源
多页面(MPA):一个应用中有多个页面,跳转的时候是整页刷新
单页面和多页面的区别
单页面优点: 用户体验好,快,内容的改变不需要重新加载整个页面,基于这一点spa对服务器压力较小 前后端分离 页面效果会比较炫酷(比如切换页面内容时的专场动画) 单页面缺点: 导航不可用,如果一定要导航需要自行实现前进、后退 初次加载时耗时多 页面复杂度提高很多
vue基础
虚拟DOM为何能提高性能
虚拟DOM相当于在JavaScript和真实DOM中添加了一个缓存,在利用diff算法,去除了不必要的DOM操作,从而提高了性能
vue的两大核心
数据驱动 – 数据的双向绑定
组件系统 - 模板(template) - 初始数据(data) - 外部参数(props) - 方法(methods) - 生命周期钩子函数(hooks) - 私有资源(assets)
Vue生命周期
初始化阶段: - 初始化事件和生命周期 - beforeCreate:不能获取数据,也不能操作dom - 数据挂载,绑定事件 - created:数据可以获取,也可以更改数据,绑定事件 - 组件或实例查找对应模板,生成vDOM - beforeMount:可以获取数据,和事件绑定 - 虚拟dom已经生成,初始化渲染操作 - mounted:数据挂载完毕,可以操作dom
运行中阶段: - beforeUpdate:更改数据会执行这个函数,vDOM会重新渲染 - updated:DOM更新完成,可以操作更新后的DOM
销毁阶段: - beforeDestroy:当执行$destroy方法,也就是销毁组件时,就会执行这个函数,在这里可以做清除定时器,事件等操作 - destroyed:只剩下DOM空壳,做善后操作
Vue响应式原理 - 3.0使用proxy
采用数据劫持结合发布订阅者模式,通过Object.defineProperty()方法劫持各个属性的getter和setter,属性发生变化时通知订阅者触发监听回调,更新视图。
由于JavaScript的限制,不能监听到数据和对象的变化
对象采用方法:
Vue.set(this.obj, ‘c’, 3)
this.$set(this.obj, ‘c’, 4)
this.obj = Object.assign({}, this.obj, {c: 6})
this.obj = {…this.obj, c: 8}
Vue.set(this.arr, 1, ‘x’)
this.$set(this.arr, 1, ‘x’)
this.arr.splice(2)
MVC、MVVM:都是软件架构思想
MVC:model、view、control | view -> control -> model -> view
MVVM:model、view、viewModel | view <-> viewModel -><- model
计算属性、方法、侦听器:
计算属性:模板里面要多次使用相同的逻辑、复杂的逻辑
计算属性与方法最大的区别是计算属性有缓存,方法没有缓存。
侦听器:通过侦听自身的值变化去改变其他的响应式数据,计算属性是侦听其他的响应式数据来改变自身的状态
v-if 、v-for、v-show
v-if和v-show:v-if是条件渲染,也就意味着如果一开始条件为假时是不会渲染当前元素的,直到条件为真时才开始渲染,v-show则是不论开始是否为假都会渲染,只是单纯的进行css切换。因此如果条件切换不是很频繁则使用v-if较好 v-if和v-for一起使用时v-for具有更高优先级,但是不推荐一同使用
Vue修饰符
.lazy | .number | .trim
事件修饰符: .stop | .submit | .prevent | .capture | .self | .once
按键修饰符: .xx | .enter | .tab | .delete | .esc | .space | .up | .down | .left | .right | .ctrl | .alt | .shift | .meta
组件传参
父子传参:自定义属性和Props
子父传参:自定义事件和
自定义指令
使用Vue.directive注册全局自定义指令,也可以添加选项directives注册组件内自定义指令
Vue. directive ( "change-color" , function ( params) {
} )
前端路由
根据不同URL地址展示不同页面内容;一个URL与对应页面程序之间的映射关系
嵌套路由
以/开头作为根路径,子路由设置在children里
Vue.router的三种模式
hash - 带#号
history - 不带#号
abstract - 在week环境中使用
组件的状态
组件内的状态:data
组件外的状态:props
全局的状态:Vuex的state
服务端渲染
早期的时候,由于页面比较简单,前后端分离也不够完善,所以当时的页面渲染还是在服务端完成HTML文件拼接,然后浏览器接收这个文件解析展示。
优点:更好的SEO,更快的内容到达时间(首屏渲染速度更快),无需占用客户端资源
缺点:更高的开发要求,更高的服务器要求,不利于前后端分离,占用服务端资源
移动端适配
手机淘宝方案:flexible.js
less或者sass的预编译的mixin混合功能,带参数计算
使用最新的postcss - npm install postcss-pxtorem --save-dev
Vue的性能优化
SPA页面采用keep-alive缓存组件
在更多的情况下,使用v-if代替v-show
第三方模块按需导入
服务端渲染SSR
压缩代码
服务端开启gzip压缩
计算机基础
get和post区别
语义:get获取,post传输。
安全:get不安全,post安全。
长度:get限制长度(4k),post理论上不限制的(2M)。
传输数据:get通过地址栏,post通过send和设置请求头。
缓存:get有缓存,post没有缓存
HTTP和HTTPS
基本概念: HTTP(超文本传输协议):是互联网上应用最为广泛的一种网络协议,它是客户端与服务器端请求和应答的标准(TCP) HTTPS:它是更加安全的HTTP通道,采用SSL层加密,简单来说是HTTP的安全版本 主要区别:
https协议需要申请CA证书,但是一般免费证书较少,因此需要一定费用
http是超文本传输协议,信息明文传输,https是经过SSL加密的的传输协议
http和https使用的连接方式不同,因此它们的默认端口号也不一样,http为80,https为443
http的是无状态的简单连接,HTTPS是由SSL+HTTP构建的可进行加密传输、身份认证的网络协议
HTTPS的缺点: HTTPS握手阶段耗时较长,通常会延长页面加载时间 HTTPS缓存上不如http高效,会增加数据开销与功耗,甚至可能会对本来的安全措施造成影响 HTTPS的加密范围是有限的 SSL的证书是需要费用的,并且,功能越好的证书费用越高
http常见状态码
HTTP状态码(英语:HTTP Status Code)是用以表示网页服务器超文本传输协议响应状态的3位数字代码。
200 请求成功
301 被请求的资源已永久移动到新位置
302 请求的资源临时从不同的 URI响应请求。
304 上次请求的资源未被修改
401 请求需要身份验证,通常需要加上身份验证相关信息
403 服务器已经理解请求,但是拒绝执行它。
404 没有找到
500 服务器遇到了一个未知情况,导致服务器无法完成对请求的处理。通常是服务器端程序代码出错
503 服务器临时维护或过载,无法完成当前请求
1开头 : 消息类
2开头 : 成功
3开头 : 重定向
4开头 : 请求错误
5开头 : 服务器错误
项目积累
项目中遇到的问题
react中配置二级路由,地址变化但是界面不更新 - 使用dva/router中的withRouter高阶组件解决
图表联动 - 我们只需要把当前被选中图表的事件,直接发给其他图表即可,然后判断被选中的图表是哪个作为区分onTouchEvent(event)普通事件传递
要求智能匹配产品 - 找网上类似功能的网站 查看源码 和主管讨论 - 需要一个设计一个投资习惯和风险承受能力测试 - 从后端获取这个客户测试的结果 以及客户平常投资的习惯 生成不同的关键字 - 根据关键字从数据库中匹配产品 展示界面
react移动端兼容问题
ES6的浏览器兼容问题:babel默认只会转换ES6的语法,而不会转换API,比如promise,Object.assign、array.from等新方法,我们可以引入另外的包来解决这个问题,比如babel-runtime、babel-polyfill来完成
iOS下的软键盘问题: fixed失效是由于软键盘唤起后,页面的 fixed 元素将失效(ios认为用户更希望的是元素随着滚动而移动,也就是变成了 absolute 定位),既然变成了absolute,所以当页面超过一屏且滚动时,失效的 fixed 元素就会跟随滚动了。解决方案就是让整个页面处于一屏高度就能解决问题的根本
meta标签对于移动端的一些特殊属性 - 主要I是强制让文档的宽度与设备宽度保持1:1,最大宽度1.0,禁止屏幕缩放: - 这个也是iphone私有标签,允许全屏浏览 - iphone的私有标签,iphone顶端状态条的样式: - 禁止数字自动识别为电话号码,这个比较有用,因为一串数字在iphone上会显示成蓝色,样式加成别的颜色也是不生效的:
页面禁止复制、选中文本 - -webkit-user-select: none; - -moz-user-select: none; - -khtml-user-select: none; - user-select: none;
input的placeholder出现文本位置偏上的问题:PC端设置line-height等于height能够对齐,但是移动端仍然偏上,可以设置为line-height:normal
在iOS和Android中,audio和video元素无法自动播放的问题:代码设置为触屏即播
登录注册的实现
登录注册做成受控组件,组件中定义state,和表单进行绑定
redux-saga发送数据请求,发送action修改数据,useEffect中dispatch发送数据请求,后端对比用户名是否重复,返回state
前端根据返回的状态判断跳转到登录页
登录发送数据请求,与数据库对比用户名密码是否正确,根据后端返回结果跳转到首页
使用cookie保存用户名密码
做用户体验,考虑实际应该跳转的情况和不应该跳转的情况
Git常用命令
Git init - 初始化仓库
Git clone - 拷贝一份远程仓库
Git add - 添加文件到暂存区(缓存区)
Git commit - 提交到本地仓库
Git status - 查看当前仓库状态
Git diff - 比较暂存区和工作区文件差异
Git reset - 回退版本
Git rm - 删除工作区文件
Git mv - 移动或重命名工作区文件
Git log - 查看历史提交记录
Git remote - 操作远程仓库 (ex:Git remote add URL<远程仓库地址>)
Git fetch - 获取远程仓库代码
Git pull - 获取并合并远程仓库代码
Git push - 上传代码到远程并合并
Git branch - 查看仓库分支
Git checkout - 切换仓库分支
更多详细用法戳这里
git怎么删除远程和本地分支
删除本地分支,删除需要删除的分支之前切换到别的分支然后执行,git branch --delete 分支名字
删除远程分支,git push origin --delete 分支的名字
关于IE特性
IE的排版引擎是Trident
Trident内核的大量bug等安全性问题没有得到及时的解决
js上它有他自己的想法,有它自己的独立方法,比如事件绑定:attachEvent、比如创建事件:creatEventObject
css上也有自己的处理方式,如设置透明,低版IE使用滤镜
当一个页面需要渲染的数据太多,可以怎么处理
分页
懒加载
新线程
webpack
什么是webpack?
WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用
为什么要使用webpack?
如今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代码和一大堆依赖包,为了简化开发的复杂度,前端社区出现了一大堆实践方法,比如:模块化(将复杂的程序细小化)、typescript(JavaScript的超集)、scss、less等css预处理器。这些改进确实大大的提高了我们的开发效率,但是利用它们开发的文件我们却往往需要做一些额外的处理才能够让浏览器识别,但是我们手动处理又是非常繁琐的,所以就出现了webpack
对于webpack来说,一切都是模块,比如JavaScript代码,css代码或者是字体、图片等等,只要有合适的loader,就能当做模块处理
webpack还具有热更新机制:不用刷新浏览器而将新变更的模块替换掉旧的模块
webpack的loader和plugin
webpack的loader可以将文件从不同的语言(如TypeScript)转换为JavaScript,或者将内联图像转换为data URL比如CSS-Loader,Style-Loader可以用来处理样式表,babel-loader优雅降级配置ES高版本转成低版本
webpack常用loader:style-loader、css-loader、less-loader、sass-loader、babel-loader、coffee-loader 、ts-loader、raw-loader、file-loader 、url-loader、mocha-loader、jshint-loader 、eslint-loader
plugin:插件是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。在webpack打包编译过程里,在对应的事件节点里执行自定义操作,比如资源管理、bundle文件优化等操作
webpack构建流程
初始化参数:从配置文件和shell语句中读取合并参数,得到一个最终的参数
开始编译:用得到的最终参数初始化一个对象,加载所有的配置插件
确定入口:从配置的entry中找到入口文件
编译模块:从入口文件开始,调用对应的loader对模块进行编译,再找到这个模块的依赖模块,一直到所有的入口依赖文件都完成编译
完成模块的编译:完成loader的编译后,得到每个模块的翻译内容以及他们之间的依赖关系
输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表
输出完成:配置确定输出的路径和文件名,把文件内容写入到文件系统
webpack的前端优化
压缩代码:利用webpack的一些plugin来压缩代码,比如:UglifyJsPlugin
利用CDN:将部分静态资源路径改为CDN路径
删除无用的代码
提出公共代码(重复代码)