面试题汇总
文章目录
- 面试题汇总
- 一、H5,css
-
- HTML 的语义化你是怎么理解的?
- 实现一个左侧固定,右侧自适应的布局?
- ***说一下对 BFC 的理解?
- 如果数组里边是字符串类型如何排序?
- 移动端适配
- 盒子模型
- 清除浮动的方法
- 常见的布局方法有哪些?他们的优缺点是什么?
- 常见的盒子垂直居中的方法有哪些请举例3种?
- 说一下 Less 你用过哪些特性(除了变量嵌套计算再找几条)?
- 什么是回流和重绘,如何减少回流和重绘
- 假如有 10000 个元素需要添加到页面上,你觉得怎么操作性能最好(考察文档碎片)?
- 移动端 300ms 延迟是怎么回事,你是怎么处理的?
- 做移动端开发时,有没有碰到过什么问题(考察移动端的兼容性,搜索一下,找几条)?
- 移动端有哪些常见的问题,都是怎么解决的?
- 移动端 1px 的问题了解吗?
- 白屏现象与优化建议:
- FOUC现象与优化建议:
- 二、js基础
-
- js的数据类型有哪些?
- 请写至少三种数组去重的方法?(原生js)
- 数组常用的方法/及返回值
- 数组的 push 和 pop 方法的返回值是什么
- 什么是伪数组,伪数组怎么转真数组,Array.from、{...伪数组}(三个点只能处理可迭代数据)?
- 变量提升、作用域、作用域链
- 什么是原型(prototype)
- 什么是原型链?
- 什么是闭包?手写一个闭包函数? 闭包有哪些优缺点?
- 什么是递归,递归有哪些优点或缺点?
- 什么是深拷贝、什么是浅拷贝?
- this 的指向有哪些?
- new关键字做了哪些事情
- localStorage与sessionStorage区别
- ***call apply bind区别
- ***如何判断数据类型
- 节流和防抖
- for--in和for--of的区别
- js的执行机制(事件循环机制 EventLoop)
- js延迟加载的方式(原理:异步操作)
- 垃圾回收机制
- js实现继承(组合继承= call式继承 + 原型继承)
- 常见的继承有哪些?
- 事件冒泡,事件委托
- 三、ajax/计算机网络相关
- 四、webpack
- 五、ES6
-
- 对ES6对理解
- var、let、const区别
- symbol
- 回调地狱
- Promise
- async await 是什么?它有哪些作用?
- 箭头函数和普通函数的区别
- forEach和map的区别
- Set、Map的区别
- ES6中的class类
- 知道lodash吗?它有哪些常见的API ?
- 模块化
- 什么是mvvm,mvc模型
- 组件中name选项的作用
- vue2中响应式的原理
- vue双向绑定的原理
- Object.defineProperty()数据劫持缺陷
- new Vue的流程
- vue生命周期
- v-if 和v-show有什么区别?
- 组件是什么? 好处
- 组件-scoped作用
- v-for 循环为什么一定要绑定key key的作用:?
- 组件中的data为什么要定义成一个函数而不是一个对象?
- 父子组件的生命周期钩子:
- Vuex状态管理模式
- vuex持久化操作
- 计算属性和watcher的使用区别:
- vue2中v-model是一个语法糖,那具体是怎么实现的?
- 路由守卫
- hash和history的区别
- 怎么理解 vue 中的虚拟 DOM
- 模版编译原理
- diff算法逻辑
- vue实例是挂载到那个标签上的?
- keep-alive原理
- active-class 是哪个组件的属性
- route和router的区别
- vue-router怎么配置路由
- 平时都是用那些工具进行打包的?babel是什么?
- 图片懒加载是怎么实现的?
- vue中computed 和watch 的区别是什么?
- nextTick的原理
- vue中的组件通讯方式
- v-model和.sync的对比
- vue路由钩子beforeEach的参数
- vue2中如何自定义指令
- CDN
- 说一下什么是动态组件和异步组件?
- 创建 store 实例的时候,除了 state、mutations、actions、getters、mudules,plugins 选项是干什么的,你用过吗(讲过 vuex-persistedstate 这个插件可以做 vuex 中的数据持久化,百度一下)?
- vue2中vue.use是怎么用的
- vue-cli 2.0和3.0 有什么区别?
- vue2和vue3的区别:
- vue3的响应式原理
- Object.defineProperty和proxy的区别
- vue中组件和插件有什么区别
- Vue 相关的性能优化
- v-for / v-if 为什么不建议放一行?如何处理?Vue3 呢?
- 说一下对 Vue3 的了解?
- 你项目中是怎么组织接口请求的,axios 封装,你都封装了哪些东西?
- 怎么配置 vue-cli 这个工具呢,你都配置过哪些东西(vue.config.js)?
- 了解 Webpack 吗,说一下常见的配置?
- node.js是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
- NPM是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
- yarn是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
- webpack是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
- webpack-配置修改
- 脚手架是什么?怎么用?好处是?
- vue是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
- jQuery是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
- layui是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
- 除了变量、嵌套,你还使用过哪些和Less/Sass相关的特性?
- 作为这个系统的使用者.我该怎么使用?
- 封装了什么组件,用到了哪些技术点?
- 六、非-常规问题
一、H5,css
HTML 的语义化你是怎么理解的?
- 可读性。代码结构清晰,便于理解,对人和机器的可读性都更好。
- 可维护和团队协作。清晰语义化的结构,可维护性更高,更有利于团队协作。
- 有利于搜索引擎优化(seo)
实现一个左侧固定,右侧自适应的布局?
- 左侧固定宽度 右侧flex自适应 flex = 1
- 左侧浮动 右侧设置宽度100%, 占据左侧普通流位置
- 左侧固定宽度左浮动, 右侧设margin-left等于左侧宽度
- 左侧固定宽度 固定定位 右侧宽度100%
***说一下对 BFC 的理解?
块级格式化上下文
bfc是指浏览器中创建了一个独立的渲染区域, 该区域内所有元素的布局不会影响到区域外元素的布局,这个渲染区域只对块级元素起作用
创建BFC:
- 元素设置浮动:
float
有值并不为空 - 元素设置绝对定位:
position(absolute、fixed)
overfilow
值为:hidden
、auto
、scroll
display
值为:inline-block
、table-cell
、table-caption
、flex
等
BFC作用:
- 解决
margin
重叠问题:由于BFC是一个独立的区域,内部元素和外部元素互不影响,将两个元素变为BFC,就解决了margin重叠问题 - 创建自适应两栏布局:可以用来创建自适应两栏布局,左边宽高固定,右边宽度自适应。
- 解决高度塌陷问题:在子元素设置浮动后,父元素会发生高度的塌陷,也就是父元素的高度为0解决这个问题,只需要将父元素变成一个BFC。
如果数组里边是字符串类型如何排序?
数组排序 sort(function (a,b) {})
return a - b 从小到大
return b - a 从大到小
移动端适配
- flex布局 自适应布局
- rem 适配方案
rem 是一个相对单位,rem 就是 html 根文字的大小
通过媒体查询可以检测视口宽度 然后根据不同视口宽度, 设置不同的根字号完成适配
- vw/vh 适配方案
vw就是视口的宽度,vw 是个相对单位。
不管在什么屏幕下, 我们把屏幕分为平均的 100等份。
盒子模型
在标准盒子模型中,width 和 height 指的是内容区域的宽度和高度。增加内边距、边框和外边距不会影响内容区域的尺寸,但是会增加元素框的总尺寸。
IE盒子模型中,width 和 height 指的是内容区域+border+padding的宽度和高度。
清除浮动的方法
浮动的盒子脱离标准流,如果父盒子没有设置高度的话,下面的盒子就会撑上来。
1.额外标签法(在最后一个浮动标签后,新加一个标签,给其设置clear:both;)(不推荐)
2.父级添加overflow属性(父元素添加overflow:hidden)(不推荐)
3.使用after伪元素清除浮动(推荐使用)
.clearfix:after{/*伪元素是行内元素 正常浏览器清除浮动方法*/
content: "";
display: block;
height: 0;
clear:both;
visibility: hidden;
}
.clearfix{
*zoom: 1;/*ie6清除浮动的方式 *号只有IE6-IE7执行,其他浏览器不执行*/
}
4…使用before和after双伪元素清除浮动
.clearfix:after,.clearfix:before{
content: "";
display: table;
}
.clearfix:after{
clear: both;
}
.clearfix{
*zoom: 1;
}
常见的布局方法有哪些?他们的优缺点是什么?
页面布局常用的方法有浮动、定位、flex、grid网格布局、栅格系统布局
浮动:
- 优点:兼容性好。
- 缺点:浮动会脱离标准文档流,因此要清除浮动。我们解决好这个问题即可。
绝对定位
- 优点:快捷。
- 缺点:导致子元素也脱离了标准文档流,可实用性差。
flex 布局(CSS3中出现的)
- 优点:解决上面两个方法的不足,flex布局比较完美。移动端基本用 flex布局。
网格布局(grid)
- CSS3中引入的布局,很好用。代码量简化了很多。
利用网格布局实现的一个左右300px中间自适应的布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
html * {
padding: 0;
margin: 0;
}
/* 重要:设置容器为网格布局,宽度为100% */
.layout.grid .left-center-right {
display: grid;
width: 100%;
grid-template-rows: 100px;
grid-template-columns: 300px auto 300px; /* 重要:设置网格为三列,并设置每列的宽度。即可。*/
}
.layout.grid .left {
background: red;
}
.layout.grid .center {
background: green;
}
.layout.grid .right {
background: blue;
}
</style>
</head>
<body>
<section class="layout grid">
<article class="left-center-right">
<div class="left">
我是 left
</div>
<div class="center">
<h1>网格布局解决方案</h1>
我是 center
</div>
<div class="right">
我是 right
</div>
</article>
</section>
</body>
</html>
栅格系统布局
优点:可以适用于多端设备
常见的盒子垂直居中的方法有哪些请举例3种?
利用子绝父相定位的方式来实现
#container{
width:500px;
height:500px;
position:relative;
}
#center{
width:100px;
hight:100px;
position: absolute;
top: 50%;
left: 50%;
margin-top:-50px;
margin-left:-50px;
}
利用Css3的transform,可以轻松的在未知元素的高宽的情况下实现元素的垂直居中。
#container{
position:relative;
}
#center{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
flex
#container{
display:flex;
justify-content:center;
align-items: center;
}
#center{
}
说一下 Less 你用过哪些特性(除了变量嵌套计算再找几条)?
Extend 继承
before
.box-shadow {
box-shadow: 0 0 9px 3px #ccc;
border-radius: 6px;
}
.box1 {
height: 200px;
width: 400px;
margin: 50px auto 0;
&:extend(.box-shadow);
}
.box2 {
height: 100px;
width: 400px;
margin: 30px auto 0;
&:extend(.box-shadow);
}
after
.box-shadow,
.box1,
.box2 {
box-shadow: 0 0 9px 3px #ccc;
border-radius: 6px;
}
.box1 {
height: 200px;
width: 400px;
margin: 50px auto 0;
}
.box2 {
height: 100px;
width: 400px;
margin: 30px auto 0;
}
Mixin 混入
before
// #1 如果这儿带了括号,此 .box-shadow 不会生成
.box-shadow(@radius: 6px) {
box-shadow: 0 0 9px 3px #ccc;
border-radius: @radius;
}
.box1 {
height: 200px;
width: 400px;
margin: 50px auto 0;
.box-shadow();
}
.box2 {
height: 100px;
width: 400px;
margin: 30px auto 0;
// #2 可以传递参数
.box-shadow(50px);
}
// #3 相比较于继承,.box1 和 .box2 公用的样式并没有被整合
after
.box1 {
height: 200px;
width: 400px;
margin: 50px auto 0;
box-shadow: 0 0 9px 3px #ccc;
border-radius: 6px;
}
.box2 {
height: 100px;
width: 400px;
margin: 30px auto 0;
box-shadow: 0 0 9px 3px #ccc;
border-radius: 50px;
}
Func 函数
.box-shadow(@radius: 6px) {
box-shadow: 0 0 9px 3px #ccc;
border-radius: @radius;
}
.box1 {
height: 200px;
width: 400px;
margin: 50px auto 0;
.box-shadow();
// return a color which is 10% *lighter* than @color
background-color: lighten(red, 20%);
}
什么是回流和重绘,如何减少回流和重绘
**回流:**当render tree的一部分或者全部元素因改变了自身的宽高,布局,显示或隐藏,或元素内部的文字结构发生变化,导致需要重新构建页面的时候,回流就产生了。
**重绘:**当一个元素自身的宽高,布局,及显示或隐藏没有改变,而只是改变了元素的外观风格的时候,就产生了重绘。
**结论:**回流必定触发重绘,而重绘不一定触发回流。
放弃传统操作 DOM 的时代,基于 vue/react 开始数据影响视图模式
分离读写操作(现代浏览器的渲染队列的机制)(重要)
元素批量修改(重要)
样式集中改变(不重要)
假如有 10000 个元素需要添加到页面上,你觉得怎么操作性能最好(考察文档碎片)?
你可以把它认为是一个dom节点的容器,当你创造了10个节点,当每个节点都插入到文档当中都会引发一次浏览器的回流,也就是说浏览器要回流10次,十分消耗资源。
而使用碎片化文档,也就是说我把10个节点都先放入到一个容器当中,最后我再把容器直接插入到文档就可以了!浏览器只回流了1次。
DocuemntFragment(碎片化文档)
移动端 300ms 延迟是怎么回事,你是怎么处理的?
双击缩放(double tap to zoom),这也是会有上述 300 毫秒延迟的主要原因。双击缩放,顾名思义,即用手指· 在屏幕上快速点击两次,iOS 自带的 Safari 浏览器会将网页缩放至原始比例。
fastClick:专门为解决移动端浏览器 300 毫秒点击延迟问题所开发的一个轻量级的库。简而言之,FastClick 在检测到 touchend 事件的时候,会通过 DOM 自定义事件立即触发一个模拟 click 事件,并把浏览器在 300 毫秒之后真正触发的click事件阻止掉
做移动端开发时,有没有碰到过什么问题(考察移动端的兼容性,搜索一下,找几条)?
1、安卓浏览器看背景图片,有些设备会模糊。
2、图片加载 若您遇到图片加载很慢的问题,对这种情况,手机开发一般用canvas方法加载:
3、假如手机网站不用兼容IE浏览器,一般我们会使用zeptojs。
4、防止手机中网页放大和缩小。
移动端有哪些常见的问题,都是怎么解决的?
点击事件300MS延迟问题 解决方案:下载fastclick的包
H5页面窗口自动调整到设备宽度,并禁止用户缩放页面
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
忽略Android平台中对邮箱地址的识别
<meta name="format-detection" content="email=no">
当网站添加到主屏幕快速启动方式,可隐藏地址栏,仅针对ios的safari
<!-- ios7.0版本以后,safari上已看不到效果 -->
<meta name="apple-mobile-web-app-capable" content="yes">
移动端 1px 的问题了解吗?
就是我们在 pc 的浏览器上设置的 1 px 的边框,在移动 端的浏览器上看上去会“更粗” 一些。
白屏现象与优化建议:
白屏现象分析
首屏白屏在单页应用(例如vue)中,有时更明显,尤其是在网速较差+应用体积大(主要是指首页就需要加载的js、css文件,例如app.js,chunk-vendors.js)。
有些浏览器渲染机制(比如chrome)要先构建 DOM 树和 CSSOM 树,构建完成后再进行渲染,如果 CSS 部分放在 HTML 尾部,由于 CSS 未加载完成,浏览器迟迟未渲染,从而导致白屏;也可能是把 js 文件放在头部,脚本的加载会阻塞后面 文档内容的解析,从而页面迟迟未渲染出来,出现白屏问题。
单页应用首屏白屏优化建议:
1.按需加载,使用code-split技术实现按需加载。
2.使用骨架屏或loading,优化人机感知交互体验。
3.js阻塞渲染,应尽可能考虑首屏异步加载js脚本,首屏内容太长可考虑分屏分段分开加载。
4.压缩技术,考虑压缩要加载的文件体积。可以考虑使用webpack 打包压缩文件大小的相关技术。
5.使用服务端渲染技术,这对于SEO优化和首屏加载优化都有好处。
6.当项目太大的时候,可以考虑根据内容作分包管理,使用Monorepo 组织管理代码。(解释下,Monorepo 是管理项目代码的一个方式,指在一个项目仓库 (repo) 中管理多个模块/包 (package))
FOUC现象与优化建议:
FOUC现象分析
FOUC(文档样式短暂失效(Flash of Unstyled Content)),主要指的是样式闪烁的问题,由于浏览器渲染机制(比如firefox),在 CSS 加载之前,先呈现了 HTML,就会导致展示出无样式内容,然后样式突然呈现的现象。会出现这个问题的原因主要是 css 加载时间过长,或者 css 被放在了文档底部。
FOUC优化建议:
1.减少使用@import导入样式表。
2.不在文档尾部引入样式。
3.尽量使用link标签在head中引入。(当然link标签是一个只能在head中使用的标签,因此使用link必然是在head中的)
二、js基础
js的数据类型有哪些?
js的数据类型分为基本数据类型(string、number、boolean、null、undefined、symbol)
复杂数据类型 : 数组 对象 函数
基本数据类型的特点:直接存储在栈中的数据
复杂数据类型的特点:存储的是该对象在栈中引用,真实的数据存放在堆内存里
请写至少三种数组去重的方法?(原生js)
//利用filter
function unique(arr) {
return arr.filter(function(item, index, arr) {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item, 0) === index;
});
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//利用ES6 Set去重(ES6中最常用)
function unique (arr) {
return Array.from(new Set(arr))
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", true, 15, false, undefined, null, NaN, "NaN", 0, "a", {}, {}]
//利用for嵌套for,然后splice去重(ES5中最常用)
function unique(arr){
for(var i=0; i<arr.length; i++){
for(var j=i+1; j<arr.length; j++){
if(arr[i]==arr[j]){ //第一个等同于第二个,splice方法删除第二个
arr.splice(j,1);
j--;
}
}
}
return arr;
}
var arr = [1,1,'true','true',true,true,15,15,false,false, undefined,undefined, null,null, NaN, NaN,'NaN', 0, 0, 'a', 'a',{},{}];
console.log(unique(arr))
//[1, "true", 15, false, undefined, NaN, NaN, "NaN", "a", {…}, {…}] //NaN和{}没有去重,两个null直接消失了
数组常用的方法/及返回值
pop shift 返回被删除的元素
splice(如果删除元素则返回被删除元素的数组, 添加元素则返回空数组)
拼接数组 join()返回字符串
翻转数组 reverse() 返回翻转后的新数组
从数组中选定元素返回新数组 slice(第几项开始, 数组截取到的位置)返回截取后的数组
数组去重 new Set( )
数组判断是否包含某值includes( ) 返回布尔值
判断是否为数组Array.isArray(要检测的内容) 返回布尔值
数组map方法(渲染数组)返回映射后的新数组
数组filter方法(筛选数组)返回过滤后的新数组
数组forEach方法(遍历数组)返回undefined
some/every返回布尔值
reduce返回内容取决于最后一个参数是数字/数组/对象
数组findIndex方法(找下标)
多维数组扁平化(数组内置flat( Infinity )方法 —>只适用于扁平二维数组 ----> 加上Infinity 可以将多个数组都扁平化)
数组的 push 和 pop 方法的返回值是什么
push:push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度。
pop:pop() 方法用于删除并返回数组的最后一个元素。
什么是伪数组,伪数组怎么转真数组,Array.from、{…伪数组}(三个点只能处理可迭代数据)?
伪数组 : 和数组很像有长度,下标,没有数组的方法
伪数组 ---->真数组
Array.from()
Array.slice.call(arguments)
变量提升、作用域、作用域链
变量提升
js代码在解析的时候,会将所有的变量函数,提升到代码的最上面。
变量提升,提升的只是变量申明
,并不会吧变量赋值提升上来
console.log(i) // 4
var i = 4
作用域
作用域是一个变量或函数的可访问范围,作用域控制着变量或函数的可见性和生命周期
全局作用域:可以全局访问
- 最外层函数和最外层定义的变量拥有全局作用域
- window上的对象属性方法拥有全局作用域
- 为定义直接复制的变量自动申明拥有全局作用域
- 过多的全局作用域变量会导致变量全局污染,命名冲突
函数作用域:只能在函数中访问使用哦
- 在函数中定义的变量,都只能在内部使用,外部无法访问
- 内层作用域可以访问外层,外层不能访问内存作用域
ES6中的块级作用域
:只在代码块中访问使用使用ES6中新增的
let
、const
什么的变量,具备块级作用域,块级作用域可以在函数中创建(由{}包裹的代码都是块级作用域)let
、const
申明的变量不会变量提升,const
也不能重复申明块级作用域主要用来解决由变量提升导致的变量覆盖问题
var i = 3 function fnc() { console.log(i); var i = 6; } fnc() // undefined
作用域链
变量在指定的作用域中没有找到,会依次向上一层作用域进行查找,直到全局作用域。这个查找的过程被称为作用域链。
什么是原型(prototype)
原型也是一个对象
- 原型: 每个对象在内部初始化的时候,都会初始化一个
prototype
原型属性 ,而对象的_proto_
属性,指向它的原型对象。
什么是原型链?
多个对象之间通过__proto__
链接起来的这种关系
Object 是所有对象的爸爸,所有对象都可以通过 proto 找到它
Function 是所有函数的爸爸,所有函数都可以通过 proto 找到它
函数的 prototype 是⼀个对象
对象的 proto 属性指向原型, proto 将对象和原型连接起来组成了原型链
原型链 :每一个对象都有原型,原型本身又是对象,所以原型又有原型,以此类推形成一个链式结构,称为原型链
对象也是一个对象,也有proto属性,这样一层一层往上找的过程就形成了原型链。
1. 每一个对象都有原型,原型本身又是对象,所以原型又有原型,以此类推形成一个链式结构,称为原型链
2 .原型链作用 : 实现面向对象继承
先访问自己,自己没有找原型,原型没有找原型的原型,直到原型链终点。 如果还找不到,属性则获取undefined,方法则报错xxx is not defined
什么是闭包?手写一个闭包函数? 闭包有哪些优缺点?
- 1.闭包是什么 :
- 闭包 是 访问其他函数内部变量的 函数
- 闭包 = 函数 + 上下文引用
闭包优点:
- 创建全局私有变量,避免变量全局污染
- 可以实现封装、缓存等
- 间接访问函数内部的变量
闭包缺点:
- 创建的变量不能被回收,容易消耗内存,使用不当会导致内存溢出
function fn() {
var num = 10;
function fun() {
console.log(num);
}
return fun;
}
var f = fn();
f();
作用:延长变量作用域、在函数的外部可以访问函数内部的局部变量,容易造成内层泄露,因为闭包中的局部变量永远不会被回收
- 3-注意事项:
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
经典面试题
循环中使⽤闭包解决 var 定义函数的问题
for (var i = 1; i <= 5; i++) {
;(function(j) {
setTimeout(function timer() {
console.log(j)
}, j * 1000)
})(i) }
什么是递归,递归有哪些优点或缺点?
- 1.递归 : 在函数内部调用自己 应用场景: 树形组件
- 2.递归作用 :
- 浅拷贝与深拷贝
- 遍历dom树
递归:如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函
数内部自己调用自己, 这个函数就是递归函数
优点:结构清晰、可读性强
缺点:效率低、调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。->性能
什么是深拷贝、什么是浅拷贝?
-
- 浅拷贝:只拷贝对象的第一层,如果对象内容还有对象的话,拷贝的就是一个引用地址
- 修改拷贝后的数据原数据也会变化(浅拷贝只是复制对象的值类型,通过
Object.assign
或者扩展运算符即可实现)
- 2.深拷贝:递归浅拷贝的过程 拷贝数据, 修改拷贝后的数据原数据不会变化
深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。
JSON.parse(JSON.stringify(object))
解决深拷贝
局限: 会忽略 undefined
会忽略 symbol
不能序列化函数
不能解决循环引⽤的对象
this 的指向有哪些?
- this : 谁
调用
我,我就指向谁 - 普通函数
函数名()
: window - 对象方法
对象名.方法名()
: 对象 - 构造函数
new 函数名()
: new创建的实例对象 - 箭头函数没有,找上一级,还没有,就是window
new关键字做了哪些事情
- 1、首先创建了一个新对象
- 2、设置原型,将对象的原型设置为函数的prototype对象
- 3、让函数的this指向这个对象,执行构造函数的代码(为这个新对象添加属性)
- 4、判断函数的返回值类型,如果是值类型,返回创建的对象。如果是引用类型,就返回这个引用类型的对象
localStorage与sessionStorage区别
- 1.相同点
作用一致 : 用于存储数据
- 都是只能存储字符串类型数据(上限5MB)
- 2.不同点: 存储方式不同
localStorage : 硬盘存储
(永久存储,页面关闭还在,存在硬盘)sessionStorage :内存存储
(临时存储,页面关闭了就消失)
- 3.localStorage与sessionStorage如何存储引用类型数据(数组和对象)
- 转json存储
***call apply bind区别
- 共同点:
- 都可以修改this,第一个参数都是
修改的this
- 都可以修改this,第一个参数都是
- 不同点
- 传参方式不同: call是逐一传参, apply是数组/伪数组传参
- 函数名.call(修改的this,参数1,参数2…)
- 函数名.apply(修改的this,数组/伪数组)
- 执行机制不同:call和apply会立即执行函数,bind不会立即执行
- bind会得到一个修改this后的新函数
- 传参方式不同: call是逐一传参, apply是数组/伪数组传参
***如何判断数据类型
-
- typeof有两种数据类型无法检测: null 、array
-
- 万能检测Object.prototype.toString.call(数据)–适用于IE6及以上
不同类型的优缺点 | typeof | instanceof | constructor | Object.prototype.toString.call() |
---|---|---|---|---|
优点 | 使用简单 | 能检测出引用类型 | 基本能检测所有的类型(除了null和undefined) | 检测出所有的类型 |
缺点 | 只能检测出基本类型(无法正确判断null) | 不能检测出基本类型,且不能跨iframe | constructor易被修改,也不能跨iframe | IE6下,undefined和null均为Object |
实例对象 instanceof 构造函数 |
节流和防抖
节流(throttle):在n秒内只允许执行一次 (例:鼠标不断点击触发,监听滚动事件)
防抖(debounce):在n秒内多次触发但不执行,而是在n秒后执行,如果n秒内触发则重新计算。(例:计时器,搜索)
for–in和for–of的区别
for…of遍历获取的是对象的键值,
for…in获取的是对象的键名;
for…in会遍历对象的整个原型链, 性能非常差不推荐使用,
而for…of只遍历当前对象不会遍历原型链;
对于数组的遍历,for…in会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of只返回数组的下标对应的属性值; 总结:for…in循环主要是为了遍历对象而生,不适用遍历数组; for…of循环可以用来遍历数组、类数组对象、字符串、Set、Map以及Generator对象
for…of是ES6新引入的特性。修复了ES5引入的
for…in的不足
for…of不能循环普通的对象,需要通过和
Object.keys()搭配使用
js的执行机制(事件循环机制 EventLoop)
js是一个单线程、异步、非阻塞I/O模型、 event loop事件循环的执行机制
宏观任务、微观任务
js中的任务分为两种:宏观任务(MacroTask|Task)
、微观任务(MicorTask)
。
- 宏任务:
script全部代码
、setTimeout
、setInterval
、I/O
、UI Rendering
- 微任务:
Promise.then
、Process.nexTick(Node独有)
、Mutation Observer(变动观察器)
宏任务-----微任务—DOM渲染–宏任务
同步任务、异步任务
js有一个主线程和一个执行栈(调用栈),所有的任务都会放到执行栈中等待被主线程执行。
js代码在执行时,所有函数都会压入执行栈中。
- 执行同步代码,这属于宏观任务
- 所有代码执行完毕,执行栈清空,执行异步队列中任务
- 异步队列中,先执行微观任务
- 微观任务执行完毕,再执行宏观任务
js延迟加载的方式(原理:异步操作)
js延迟加载意味着----当页面全部加载完毕,然后在加载js文件,有助于提高页面加载的速度
1.defer属性(只对外部文件有效)
在script标签上,设置defer属性,可以达到异步加载js文件,延迟执行js脚本文件的目的。
1、defer属性只对外部文件有效,对本地js文件没有效果。
2、defer属性是在遇到scirpt标签时,浏览器开始异步下载,当遇到标签时,表名页面加载完毕,开始执行js文件。
3、并且js文件是按顺序执行的。
2、async属性(只对外部文件有效)
在script标签上,设置async属性,可以达到异步加载js文件的目的。
1、async属性只对外部文件有效,对本地js文件没有效果。
2、async属性是遇到scirpt标签开始通知浏览器异步下载,下载完毕之后,就可以立即执行。
3、async设置的js文件不是按照顺序的。
3、动态创建DOM方式
动态创建script标签,当页面的全部内容加载完毕后,在执行创建挂载。
<script>
function loadJS() {
let element = document.createElement("script")
element.src = "download.js"
document.body.appendChild(element)
}
if(window.addEventListener) {
window.addEventListener("load", loadJS, false)
}else if(window.attachEvent) {
window.attachEvent("onload", loadJS)
}else {
window.onload = loadJS
</script>
4、使用setTimeout
在每一个脚本文件最外层设置一个定时器。
5、把js文件放到最后
当外部加载js文件是,应该将js脚本放在最后,当全部的文件都加载完成后,再开始加载执行js脚本。
垃圾回收机制
(1)没有被引用的对象或变量
(2)无法访问到的对象(几个对象引用形成一个环,互相引用)
垃圾回收机制(GC:Garbage Collection):为了以防内存泄漏,垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存。
两种方式:
标记清除、
引用计数: 会引起内存泄漏
常见内存泄漏的原因:
(1)全局变量引起的内存泄露
(2)闭包引起的内存泄露:慎用闭包
(3)dom清空或删除时,事件未清除导致的内存泄漏
(4)循环引用带来的内存泄露
js实现继承(组合继承= call式继承 + 原型继承)
构造函数继承: ( call继承的是属性) call继承不能继承原型的方法
挂载方法不能挂载在实例上, 性能浪费, 方法一般挂载在原型上
原型继承: 继承的是方法
组合继承 = call式继承 + 原型继承
ex6中 extends继承
常见的继承有哪些?
一、原型链继承
利用对象的原型链,将子类Son.prototype
指向父类的构造函数创建出来的实例对象new Person()
优点:
- 子类可以继承父类构造函数–原型上的属性方法
缺点:
- 父类引用类型的实例对象被共享,容易造成修改的混乱。
- 创建子类的时候不能向父类传参
function Person() {
...
};
function Son() {
....
};
// 关键代码
Son.prototype = new Person();
二、借用构造函数继承
利用.call()
或者.apply()
方法,在子类中借用父类的构造函数,初始化父类构造函数。
function Person(name) {
...
};
function Son(name) {
// 关键代码
Person.call(this, name)
...
}
优点:
- 子类在继承父类时,可以向父类构造函数中传参。
- 不会造成子类势力之间引用属性共享。
缺点:
- 只能继承父类构造函数中的属性方法,无法访问原型上的方法。
- 每个子类都会创建一个父类副本
三、组合继承(组合原型链继承和借用构造函数继承)(常用)
重点:结合了两种模式的优点,传参和复用
特点:1、可以继承父类原型上的属性,可以传参,可复用。
2、子类的实例之间不会被共享
缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函数。
四、原型式继承
重点:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的实例或对象。object.create()就是这个原理。
特点:类似于复制一个对象,用函数来包装。
缺点:1、所有实例都会继承原型上的属性。
2、无法实现复用。(新实例属性都是后面添加的)
function create(obj) {
// 创建一个空的的构造函数
function F() {};
// 将空的构造函数原型指向传递进来的对象
F.prototype = obj;
// 返回一个实例对象
return new F();
}
const obj = {
name: 'zs',
...
};
const newObj1 = create(obj);
const newObj2 = create(obj);
五、class类实现继承
通过extends 和super 实现继承
六、寄生式继承
重点:就是给原型式继承外面套了个壳子。
优点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成了创建的新对象。
缺点:没用到原型,无法复用。
function createObj(obj){
// 获取继承的子类对象,也就是上面的create方法实现
const newObj = Object.create(obj);
// 函数增强
newObj.say = function() {
...
}
// 返回对象
return newObj;
}
事件冒泡,事件委托
事件发生的三个阶段:捕获阶段、目标阶段、冒泡阶段
- 事件冒泡:目标元素—>目标元素的父元素—>父元素的父元素—>根
- 如何阻止:
- 普通浏览器:
event.stopPropagation()
- IE浏览器:
event.cancelBubble = true
;
- 普通浏览器:
- 如何阻止:
-
- 什么是事件委托呢?
- 事件委托是给父元素注册事件,委托子元素处理
- 事件委托原理是什么呢?
- 事件冒泡
- 事件委托可以解决什么问题呢?
- (1)动态新增元素
- (2)所有的子元素需要注册同名事件,给父元素注册
- 在开发中,什么时候我们会用到事件委托呢?
- 动态新增
- 什么是事件委托呢?
事件对象的常用属性或方法吗?
- e.preventDefault() : 阻止a和form默认跳转
- e.target : 事件委托里面的触发源
- e.stopPrapagation() : 阻止事件流(冒泡+捕获)
- e.key / e.keyCode : 获取按键
三、ajax/计算机网络相关
ajax、axios、fetch区别
ajax
- 基于原生的XHR开发
- 本身针对MVC编程,不符合现在前端MVVM潮流
axios
- 从浏览器中创建
XMLHttpRequest
- 支持
promise
- 支持请求拦击和响应拦截
- 从node.js创建http请求
- 客服端支持防止
CSRF/XSRF
fetch
- 浏览器原生实现的请求方式,ajax的替代品
- 只对网络请求报错,对400、500都当做成功的请求,需要封装
- fetch默认不会带cookie,需要添加配置项
- fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费
- fetch没有办法原生监测请求的进度,而XHR可以
get和post的区别
传参方式不同
get 请求直接在url中传参
post 在请求体中传参
数据大小不同
get有大小限制
post没有大小限制(文件上传)
传输速度不同
get传输速度快
post传输速度慢
4.安全性不同
get安全性低
post安全性高(登录 注册必须是post请求)
跨域
浏览器的同源策略会导致跨域问题
同源策略
同源指的是:协议、端口号、域名必须一致。
同源策略的目的主要是为了保证用户的信息安全,它只是对 js 脚本的一种限制
,并不是对浏览器的限制,对于一般的 img、或者script 脚本请求都不会有跨域的限制,这是因为这些操作都不会通过响应结果来进行可能出现安全问题的操作
解决跨域问题
CORS:服务器开启跨域资源共享
(一) 当前端配置
withCredentials=true
时, 后端配置Access-Control-Allow-Origin
不能为*
, 必须是相应地址(二) 当配置
withCredentials=true
时, 后端需配置Access-Control-Allow-Credentials
(三) 当前端配置请求头时, 后端需要配置
Access-Control-Allow-Headers
为对应的请求头集合JSONP:利用JavaScript标签不存在跨域限制,只支持GET请求
Nginx:反向代理
本地存储
Cookie(存储token时会用到)
- 存储小,只有4k
- 不同域之间不能共享
- 不安全,容易被拦截
SessionStorage(上限5MB)
- 存储在内存中,体积相对较大
- 页面关闭,数据会删除
- 相对Cookie安全
- 存储引用数据类型需要转json存储
LocalStorage
- 体积大,可以存储更多内容
- 生命周期长,需要手动删除
- 存储在硬盘,不会像cookie一样被请求携带
- 存储引用数据类型需要转json存储
地址栏输入地址做了哪些事情
- 解析url的有效性和和合法性
- 执行缓存策略, 策略内缓存中有则从缓存取并显示
- 缓存中没有则发送请求协议, DNS解析域名, 换取映射的ip地址
- 浏览器和服务器进行TCP连接, 进行三次握手
- 服务器 301 重定向(从 http://example.com 重定向到 http://www.example.com)
- 浏览器跟踪重定向地址,请求另一个带 www 的网址
- 服务器接受处理请求包, 寻求资源数据, 并将资源数据返回给浏览器(可能什么都没有, 也可能出错)
- 服务器返回一个HTTP响应(报头中把 Content-type 设置为 ‘text/html’)
- 浏览器接收返回的资源数据, 尝试解析数据
- 浏览器解析返回的资源数据过程: 默认按照排序先后进行解析,加载 head 中的资源等等,例如 css 资源
加载 script 脚本资源;script 脚本的执行只在默认的情况下是同步和阻塞的。 script 标签可以有 defer 和 async 属性(延迟和异步),这可以改变脚本的执行方式(在支持他们的浏览器)
继续解析后面的标签,文档内容
- 浏览器渲染页面,同时执行可能的异步请求
- 响应结束(可能正常显示,也可能异常)
https和http的区别
HTTP
是明文传输,不安全。HTTPS
基于SSL
进行加密传输,比较安全。HTTPS
需要CA
证书,HTTP
不需要。HTTP
端口为80
,HTTPS
端口为443
- HTTP 的连接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全
http状态码分别代表什么意思?
1XX: 请求正在处理
2XX:正常状态码
- 200 :请求处理成功
- 201 : 请求成功并且服务器创建了新资源
- 202 :服务器已经接收请求,但尚未处理
3XXX:重定向状态
- 301 :请求重定向
- 302: 临时重定向
- 303: 临时重定向,使用get请求新的url
- 304:浏览器缓存相关
4XX:错误状态码
- 400: 服务器无法理解请求格式,需要修改请求内容后再次发起请求
- 401: 未身份验证
- 403: 禁止访问
- 404: 服务器上无法找到请求资源、
- 413: 文件超过最大限制
- 422:
5XX:服务器错误
- 500: 服务端错误
- 503: 服务器暂时无法处理请求
TCP、UDP协议
TCP
和UDP
都是在传输层定义的两种传输协议。基于UDP
协议传输不能保证数据准确无误的送达,但UDP
不仅可以支持一对一的传输方式,还可以支持一对一、一对多等形式。也不需要像TCP
一样建立连接,所以传输速度快。TCP
的目的是提供可靠的数据,并且需要在传输前建立连接(三次握手)。只支持一对一进行传输。区别:
TCP
协议可靠,UDP
协议不可靠TCP
面向连接,UDP
采用无连接TCP
可以保证数据顺序,UDP
不能TCP
一对一传输,UDP
可以一对多、多对一等形式
请求头中的contentType有什么用处
Content-Type 的作用是让服务器端对 post 请求中请求体 entity body 中的数据进行解码,获取到对应的数据格式。
安全问题 :CSRF 和 XSS攻击?
CSRF
(Cross-site request forgery
):跨站请求伪造。
方法一、Token 验证:(用的最多)
- 服务器发送给客户端一个
token
; - 客户端提交的表单中带着这个
token
。 - 如果这个
token
不合法,那么服务器拒绝这个请求。
方法二:隐藏令牌:
把 token
隐藏在 http
的 head
头中。
方法二和方法一有点像,本质上没有太大区别,只是使用方式上有区别。
方法三、Referer 验证:
Referer
指的是页面请求来源。意思是,只接受本站的请求,服务器才做响应;如果不是,就拦截
XSS(Cross Site Scripting)``:跨域脚本攻击。
1. 编码:
对用户输入的数据进行HTML Entity
编码。
如上图所示,把字符转换成 转义字符。
Encode的作用是将
$var`等一些字符进行转化,使得浏览器在最终输出结果上是一样的。
比如说这段代码:
<script>alert(1)</script>
若不进行任何处理,则浏览器会执行alert的js操作,实现XSS注入。进行编码处理之后,L在浏览器中的显示结果就是
<script>alert(1)</script>
,实现了将``$var作为纯文本进行输出,且不引起J
avaScript`的执行。
2、过滤:
- 移除用户输入的和事件相关的属性。如
onerror
可以自动触发攻击,还有onclick
等。(总而言是,过滤掉一些不安全的内容) - 移除用户输入的
Style
节点、Script
节点、Iframe
节点。(尤其是Script
节点,它可是支持跨域的呀,一定要移除)。
3、校正
- 避免直接对
HTML Entity
进行解码。 - 使用
DOM Parse
转换,校正不配对的DOM
标签。
备注:我们应该去了解一下DOM Parse
这个概念,它的作用是把文本解析成DOM
结构。
比较常用的做法是,通过第一步的编码转成文本,然后第三步转成DOM
对象,然后经过第二步的过滤。
CSRF 和 XSS 的区别
区别一:
CSRF
:需要用户先登录网站A
,获取cookie
XSS
:不需要登录。
区别二:(原理的区别)
CSRF
:是利用网站A
本身的漏洞,去请求网站A
的api
。XSS
:是向网站A
注入JS
代码,然后执行JS
里的代码,篡改网站A
的内容。
cookie和session 的区别
- 1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
- 2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗
- 考虑到安全应当使用session。
- 3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
- 考虑到减轻服务器性能方面,应当使用COOKIE。
- 4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
- 5、所以个人建议:
- 将登陆信息等重要信息存放为SESSION
- 其他信息如果需要保留,可以放在COOKIE中
token是什么
token也可以称做令牌,一般由
uid+time+sign(签名)+[固定参数]
组成uid: 用户唯一身份标识 time: 当前时间的时间戳 sign: 签名, 使用 hash/encrypt 压缩成定长的十六进制字符串,以防止第三方恶意拼接 固定参数(可选): 将一些常用的固定参数加入到 token 中是为了避免重复查库
token在客户端一般存放于localStorage,cookie,或sessionStorage中。在服务器一般存于数据库中
token 的认证流程
用户登录,成功后服务器返回Token给客户端。 客户端收到数据后保存在客户端 客户端再次访问服务器,将token放入headers中 或者每次的请求 参数中 服务器端采用filter过滤器校验。校验成功则返回请求数据,校验失败则返回错误码
token可以抵抗csrf,cookie+session不行
session时有状态的,一般存于服务器内存或硬盘中,当服务器采用分布式或集群时,session就会面对负载均衡问题。负载均衡多服务器的情况,不好确认当前用户是否登录,因为多服务器不共享session
客户端登陆传递信息给服务端,服务端收到后把用户信息加密(token)传给客户端,客户端将token存放于localStroage等容器中。客户端每次访问都传递token,服务端解密token,就知道这个用户是谁了。通过cpu加解密,服务端就不需要存储session占用存储空间,就很好的解决负载均衡多服务器的问题了。这个方法叫做JWT(Json Web Token)
post和get的区别
传递的参数不同,POST
传递的参数在request body
中,GET
传递的参数在url
后拼接
POST
相对GET
请求安全
GET
请求长度有限制,POST
没有
GET
请求会被浏览器主动缓存,POST
不会,要手动设置
GET
请求一般用于查询,POST
一般用于提交某种信息进行某些修改操作
cdn 为什么能给前端实现加速
内容分布网路
在现有的internet中增加一层新的cache缓存层, 将网站发布的内容发布到最接近用户的网络"边缘节点", 使用户可以就近取得所需的内容, 提高用户访问网站的响应速度.
从技术上全面解决由于网络带宽小, 用户访问量大, 网点分布不均等原因, 提高用户访问网站的响应速度
四、webpack
五大核心概念
- entry(入口)
- output(输出)
- loader加载器—借助loader解析其他资源
- plugins插件–扩展webpack功能
- mode(模式)
开发模式
生产模式
module.exports = {
entry:'',// 相对路径
output: {
// 所有文件的输出路径
path:path.resolve(__dirname,'dist'), // 文件输出路径(绝对路径)
filename:'', // 入口文件打包输出文件名
clean: true, // 在打包前,将path整个目录清空, 在进行打包
},
module:{
rules: [ // loader的配置 ]
},
plugins: { },
model: 'development'
}
处理css资源
- css-loader: 负责将css文件编译成webpack能识别的模块
- style-loader: 会动态创建一个style标签, 里面放置webpack中css模块内容
- 此时样式就会以style标签形式在页面上生效
处理图片资源
将小于8kb文件转base64,而不是打包成一个文件type:"asset"
test: 处理类型
type:“asset”
parser: {
dataurlComdition: { maxSize: 4 * 1024 (10kb) }
}
修改输出文件目录
generator: {
// 输出 图片名称 --hash:10代表hash只取10位
filename:‘static/images/[hash:10][ext][query]’
}
babel
js编译器----降级
presets: [] // 预设
预设就是一组babel插件, 扩展babel功能
@babel/preset-env智能预设…
五、ES6
对ES6对理解
- 解构赋值
- 扩展运算符
- 模版字符串
- 箭头函数
async/await
Class
- 引入
Moldule
语法 import 和export class
类- 模块化
var、let、const区别
var
声明变量可以重复声明,而let
不可以var
是不受限于块级作用域,而let
受限var
存在变量提升,let
和const
不存在变量提升const
声明常量const和let
由于暂时性死区的原因,不能在声明前使用–声明之后必须赋值,否则会报错- 函数提升优于变量提升, 函数提升会把整个函数挪到作用域顶部, 变量提升只会把声明挪到作用域顶部
var
在全局作⽤域下声明变量会导致变量挂载在window
上,其他两者不会
symbol
ES6 引入新的原始数据类型Symbol,表示独一无二的值
回调地狱
什么是回调地狱(函数作为参数层层嵌套)
如何解决回调地狱
- 保持你的代码简短(给函数取有意义的名字,见名知意,而非匿名函数,写成一大坨)
- 模块化(函数封装,打包,每个功能独立,可以单独的定义一个js文件Vue,react中通过import导入就是一种体现)
- 处理每一个错误
- 创建模块时的一些经验法则
- Promise承诺/生成器/ES6等
Promise
Promise
是异步编程的一种解决方案,将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
它有三种状态
pending
初始状态fulfilled
操作成功rejected
操作失败。
Promise
状态改变只有两种可能
- 从
pending
------>fulfilled
- 从
pending
------>rejected
Promise
构造函数接收一个参数和一个带有resolve
和reject
参数的回调函数。
resolve
的作用是将Promise
状态从pending
变为fulfilled
,在异步操作成功时调用,并将异步结果返回,作为参数传递出去reject
的作用是将Promise
状态从pending
变为rejected
,在异步操作失败后,将异步操作错误的结果,作为参数传递出去promise.then()
对应resolve
成功的处理promise.catch()
对应reject
失败的处理
Promise
静态方法
promise.call()
将多个Promise
实例,包装成一个新的Promise
实例,返回的实例就是普通的Promise
。有一个失败,代表该Primise
失败。当所有的子Promise
完成,返回值是全部值的数组promise.race()
类似promise.all()
,区别在于有任意一个完成就算完成(例如:将异步和定时器放在一起,设置请求超时)Promise.resolve()
Promise.reject()
async await 是什么?它有哪些作用?
async await 是es7里面的新语法、它的作用就是 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执行完成。它可以很好的替代promise 中的then
async
函数返回一个 Promise 对象,可以使用then
方法添加回调函数。当函数执行的时候,一旦遇到await
就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
箭头函数和普通函数的区别
箭头函数是匿名函数,不能作为构造函数,不能使用new
没有prototype
不能new的原因-----没有constructor方法(如果一个类没有constructor方法, 直接new依然报错)
所以修this指向的call bind apply都不生效
箭头函数不绑定arguments
参数, 使用argument会报未声明的错误
箭头函数没有自己的this
,将所在的上下文的this
作为自己的this
值, 否则指向window
箭头函数的解析规则相比普通函数, 受操作符的优先级影响, 解析顺序相对靠前
forEach和map的区别
forEach
返回值是undefined
,不可以链式调用map()
返回一个新的数组,不改变原数组。forEach
改变原数组。
Set、Map的区别
Set(数组去重)
- 创建:
new Set([1, 1, 2, 3, 3, 4, 2])
add(value)
:添加某个值,返回Set结构本身。delete(value)
:删除某个值,返回一个布尔值,表示删除是否成功。has(value)
:返回一个布尔值,表示该值是否为Set的成员。clear()
:清除所有成员,没有返回值。
Map(数据储存)
set(key, val):
向Map
中添加新元素get(key):
通过键值查找特定的数值并返回has(key):
判断Map
对象中是否有Key
所对应的值,有返回true
,否则返回false
delete(key):
通过键值从Map
中移除对应的数据clear():
将这个Map
中的所有元素删除
区别
Map
是一种键值对的集合,和对象不同的是,键可以是任意值Map
可以遍历,可以和各种数据格式转换Set
是类似数组的一种的数据结构,但在Set中没有重复的值
ES6中的class类
es6中的class可以把它看成是es5中构造函数的语法糖,它简化了构造函数的写法, 类的共有属性放到 constructor 里面
- 通过class 关键字创建类, 类名我们还是习惯性定义首字母大写
- 类里面有个constructor 函数,可以接受传递过来的参数,同时返回实例对象
- constructor 函数 只要 new 生成实例时,就会自动调用这个函数, 如果我们不写这个函数,类也会自动生成这个函数
- 多个函数方法之间不需要添加逗号分隔
- 生成实例 new 不能省略
- 语法规范, 创建类 类名后面不要加小括号,生成实例 类名后面加小括号, 构造函数不需要加function
- 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的
- 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则)
- 如果子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super 调用 父类的构造函数,super 必须在子类this之前调用
- 时刻注意this的指向问题,类里面的共有的属性和方法一定要加this使用.
- constructor中的this指向的是new出来的实例对象
- 自定义的方法,一般也指向的new出来的实例对象
- 绑定事件之后this指向的就是触发事件的事件源
- 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象
知道lodash吗?它有哪些常见的API ?
Lodash是一个一致性、模块化、高性能的 JavaScript 实用工具库。
_.cloneDeep 深度拷贝
_.reject 根据条件去除某个元素。
_.drop(array, [n=1] ) 作用:将 array
中的前 n
个元素去掉,然后返回剩余的部分.
模块化
CommonJS
CommonJS 最早是 Node 在使⽤,⽬前也仍然⼴泛使⽤,⽐如在 Webpack
中你就能⻅到它,当然⽬前在 Node 中的模块管理已经和 CommonJS 有⼀些
区别了
CommonJS和ES Module区别
- commjs支持动态导入
require(${path}/xx.js)
- commonjs是同步导入, 用于服务端
- es6模块化导入是异步的, 用于浏览器,需要下载文件, 如果也采用同步会对渲染有很大的影响
- CommonJS 在导出时都是值拷⻉,就算导出的值变了,导⼊的值也不会改变,所以如果想 更新值,必须重新导⼊⼀次。但是 ES Module 采⽤实时绑定的⽅式,导⼊导出的值都指 向同⼀个内存地址,所以导⼊值会跟随导出值变化
- ES Module 会编译成 require/exports 来执⾏的
什么是mvvm,mvc模型
MVC: MVC即model-view-controller(模型-视图-控制器)是项目的一种分层架构思想,它把复杂的业务逻辑,抽离为职能单一的小模块,每个模块看似相互独立,其实又各自有相互依赖关系。它的好处是:保证了模块的智能单一性,方便程序的开发、维护、耦合度低。
mvvm: MVVM:MVVM即 Model-View-ViewModel,(模型-视图-控制器)它是一种双向数据绑定的模式,用viewModel来建立起model数据层和view视图层的连接,数据改变会影响视图,视图改变会影响数据
mvvm数据双向绑定,而mvc各部分数据流通是单向的;
组件中name选项的作用
项目使用keep-alive
,可搭配组件name进行缓存过滤
DOM做递归组价时需要调用自身name
调试工具里显示组件名称是由vue中组件name决定的从
vue2中响应式的原理
主要做了这么几件事:数据劫持、收集依赖、派发更新
- 数据劫持:new Vue的时候遍历data对象,用Object.defineProperty给所有属性加上了getter和setter
- 依赖的收集:render的过程,会触发数据的getter ,在getter的时候把当前的watcher对象收集起来
- 派发更新:setter的时候,遍历这个数据的依赖对象(watcher对象),进行更新
vue双向绑定的原理
vue.js采用数据劫持, 通过object.defineProperty()来劫持各个属性的setter, getter,在数据发生变动时触发响应的监听回调 。
利用了 Object.defineProperty() 这个方法重新定义了对象获取属性值(get)和设置属性值(set)的操作来实现的。
我们在vue里面使用v-model实现双向数据绑定,原理是检测用户输入,把输入的值获取到 ,然后同步更新给页面的元素。
Object.defineProperty()数据劫持缺陷
该方法只能监听数据的修改, 监听不到数据的新增和删除, vue2中会对数组的新增和删除方法push, pop, shift, unshift, splice, sort,reserve
通过重写的形式, 在拦截里面进行手动收集触发依赖更新.
在vue2中, 需要数据里添加或删除时, 使用this.$set(对象名, '属性', '值')/this.$delete(对象名, '属性')
进行操作
Vue.set(对象名, '属性', '值')/Vue.delete(对象名, '属性')
在vue3中, 改用proxy
对对象进行代理, 返回一个代理对象, 只需要操作新对象就可以
new Vue的流程
合并配置
调用一些初始化函数
触发声明周期钩子函数
调用$mount开启下一个阶段
vue生命周期
vue 实例从创建到销毁的过程就是生命周期。
也就是从开始创建、初始化数据、编译模板、挂在 dom -> 渲染、更新 -> 渲染、准备销毁、销毁在等一系列过程
vue的声明周期常见的主要分为4大阶段8大钩子函数
另外三个生命周期函数不常用
keep-alive 主要用于保留组件状态或避免重新渲染。
activated只有在keep-alive 组件激活时调用。
deactivated只有在keep-alive 组件停用时调用。
errorCapured 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false
以阻止该错误继续向上传播。
一、创建前 / 后
在beforeCreate生命周期函数执行的时候,data和method 还没有初始化
在created 生命周期函数执行的时候,data和method已经初始化完成
二、渲染前/后
在beforeMount 生命周期函数执行的时候,已经编译好了模版字符串、但还没有真正渲染到页面中去
在mounted 生命周期函数执行的时候,已经渲染完,可以看到页面
三、数据更新前/后
在beforeUpdate生命周期函数执行的时候,已经可以拿到最新的数据,但还没渲染到视图中去。
在updated生命周期函数执行的时候,已经把更新后的数据渲染到视图中去了。
四、销毁前/后
在beforeDestroy 生命周期函数执行的时候,实例进入准备销毁的阶段、此时data 、methods 、指令等还是可用状态
在destroyed生命周期函数执行的时候,实例已经完成销毁、此时data 、methods 、指令等都不可用
v-if 和v-show有什么区别?
v-if
是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建,操作的实际上是dom元素的创建或销毁。
v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换 它操作的是display:none/block属性。
一般来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show
较好;如果在运行时条件很少改变,则使用 v-if
较好。
组件是什么? 好处
组件是可复用的 Vue 实例, 封装标签, 样式和JS代码
好处: 各自独立, 便于复用
全局入口在main.js,
语法 import 组件对象 from ‘vue文件路径’
Vue.component(“组件名”, 组件对象)
组件-scoped作用
作用: 解决多个组件样式名相同, 冲突问题
原理: 会自动给标签添加data-v-hash值属性, 所有选择都带属性选择
v-for 循环为什么一定要绑定key key的作用:?
key的作用主要是为了高效的更新虚拟DOM,其原理是vue在patch过程中通过key可以精准判断两个节点是否是同一个,从而避免频繁更新不同元素,减少DOM操作量,提高性能。
页面上的标签都对应具体的虚拟dom对象(虚拟dom就是js对象), 循环中 ,如果没有唯一key , 页面上删除一条标签, 由于并不知道删除的是那一条! 所以要把全部虚拟dom重新渲染, 如果知道key为x标签被删除掉, 只需要把渲染的dom为x的标签去掉即可!
组件中的data为什么要定义成一个函数而不是一个对象?
每个组件都是 Vue 的实例。组件共享 data 属性,当 data 的值是同一个引用类型的值时,改变其中一个会影响其他. data为一个函数, 每个组件都会有自己的私有数据空间, 不会干扰其他组件的运行.
父子组件的生命周期钩子:
父beforeCreate => 父created => 父beforeMount => 子beforeCreate => 子created =>子beforeMount => 子Mounted=>父Mounted
子组件先挂载 然后到父组件,更新也类似 父beforeUpdate =>子beforeUpdate => 子updated => 父updated
Vuex状态管理模式
状态管理
使用场景:大型项目, 涉及父子组件,非父子组件传值会出现混乱复杂的情况, 如果使用vuex不止能把数据放在全局,组件无论在哪都能反问获取到,并且线路清晰,有利于维护。
- state
:
Vuex`的基本数据,用于存储变量 getter
:从state
派生出来的数据,当相遇state
的计算属性,在这里可以对state
数据进行过滤、筛选等操作mutation
:操作state中的数据, 必须是同步执行action
:一般用于异步操作,发请求—可以包含同步任务module
:模块化Vuex,每个模块都有自己的state
、mutation
、actoion
、getter
mutation和action的区别
mutation
更专注于修改state
,必须是同步执行。action
提交的是mutation
,而不是直接更新数据,可以是异步的。action
可以整合多个mutation
Vuex和localstory的区别
Vuex
存储在内存中,页面关闭刷新就会消失。而localstorage
存储在本地,读取内存比读取硬盘速度要快Vuex
应用于组件之间的传值,localstorage
主要用于不同页面之间的传递Vuex
是响应式的,localstorage
需要刷新
vuex持久化操作
下包 yarn add vuex-persistedstate
配置: src/store/modules/index.js
import { createStore, createLogger } from ‘vuex’
import createPersistedstate from ‘vuex-persistedstate’
plugins: [
createPersistedstate({
key: ‘erabbit-client-pc-store’, // 往本地存的键名
paths: [‘user’, ‘cart’] // 哪个模块数据需要持久化到本地
}),
createLogger()
]
计算属性和watcher的使用区别:
1.computed用来监控自己定义的变量,该变量不在data里面声明,直接在computed里面定义,然后就可以在页面上进行双向数据绑定展示出结果或者用作其他处理;(定义变量的位置在computed)
2.watch主要用于监控vue实例的变化,它监控的变量当然必须在data里面声明才可以,它可以监控一个变量,也可以是一个对象,一般用于监控路由、input输入框的值特殊处理等等,它比较适合的场景是一个数据影响多个数据,它不具有缓存性
3.watch:监测的是属性值, 只要属性值发生变化,其都会触发执行回调函数来执行一系列操作
(监听的是属性值,属性值改变其会触发执行的回调函数来进行一系列操作)
4.computed:监测的是依赖值,依赖值不变的情况下其会直接读取缓存进行复用,变化的情况下才会重新计算。(具有缓存的特性,依赖值不变的情况复用,具有缓存的特性)
5.计算属性不能执行异步任务,计算属性必须同步执行
vue2中v-model是一个语法糖,那具体是怎么实现的?
也就是v-modle实际为语法糖。v-model=”something”则表示将value值绑定在something上,当值发生改变时触发绑定的oninput事件。oninput事件绑定的函数是将触发oninput事件的目标(该input)的value值赋值给something这个变量。所以:
something即是实时的value值,每一次value值得更改都会触发something改变
如果有别的控件绑定something那么input的value值改变改控件的值显示,因此实现了双向绑定
<input v-bind:value="something" v-on:input="something = $event.target.value">
简单来说,通过v-bind将表单的vuale值绑定在变量身上,当变量发生改变时候触发表单的input事件 input的函数会将触发oninput事件的目标(该input)的value值赋值给something
这个变量
v-model的原理: 向标签内的value属性赋值 给标签绑定input事件, 并把收到的值, 赋予给vue变量
路由守卫
- 全局前置钩子:
beforeEach
、beforeResolve
、afterEach
- 路由独享守卫:
beforeEnter
- 组件内钩子:
beforeRouterEnter
、beforeRouterUpdate
、beforeRouterLeave
hash和history的区别
hash
hash模式是vue开发中的默认模式,地址栏URL携带#
,#
后为路由。
原理是通过onhashchange()
事件监听路由hash
的变化,这个好处就是当hash
值发生变化,不需要向后端发起请求,window
就可以监听事件的改变,并按照规则加载项对应的代码。除此之外,hash
值的变化对应的URL
都会被浏览器记录下来,这样就能实现浏览器历史页面的前进后退。
history
vue还提供history
模式,在history
模式下URL中没有#
,相比hash模式更加好看。但是需要后台配置支持。
history
的原理是利用HTML5中hostory
提供的pushState
、replaceState
这两个API,这两个API记录了浏览器历史栈,并且当在修改URL
时不会触发页面刷新和后台请求。
动态路由
定义方式
- params传参
- 路由配置:
/index/:id
- 路由跳转:
this.$router.push({name: 'index', params: {id: "zs"}});
- 路由参数获取:
this.params.id
- 最后形成的路由:
/index/zs
- 路由配置:
- query传参
- 路由配置:
/index
正常的路由配置 - 路由跳转:
this.$rouetr.push({path: 'index', query:{id: "zs"}});
- 路由参数获取:
this.query.id
- 最后形成的路由:
/index?id=zs
- 路由配置:
router和router 和router和route
$router
是指整个路由对象,可以使用this.$router.push({name: ;index'})
进行页面跳转$route
时指当前页面的路由对象,可以使用this.$route.parmas.id
来获取当前路由对象传递进来的参数
怎么理解 vue 中的虚拟 DOM
虚拟DOM,就是用一个JS
对象来描述一个DOM
节点。Vue
是数据驱动视图的,数据发生变化视图就要随之更新,在更新视图的时候难免要操作DOM
,而操作真实DOM
又是非常耗费性能的,这是因为浏览器的标准就把 DOM
设计的非常复杂,所以一个真正的 DOM
元素是非常庞大的。VNode
类中包含了描述一个真实DOM
节点所需要的一系列属性,tag
表示节点的标签名,text
表示节点中包含的文本,children
表示该节点包含的子节点等。
虚拟DOM:就是用js对象来模拟页面上的DOM元素和嵌套关系,从而实现页面元素的高效更新。
模版编译原理
模版编译主要过程:template ---> ast ---> render
,分别对应三个方法
parse
函数解析template
optimize
函数优化静态内容generate
函数创建render
函数字符串
调用parse
方法,将template
转化为AST
(抽象语法树),AST
定义了三种类型,一种html
标签,一种文本,一种插值表达式,并且通过 children
这个字段层层嵌套形成了树状的结构。
optimize
方法对AST
树进行静态内容优化,分析出哪些是静态节点,给其打一个标记,为后续更新渲染可以直接跳过静态节点做优化。
generate
将AST
抽象语法树编译成 render
字符串,最后通过new Function(render)
生成可执行的render
函数
diff算法逻辑
diff
算法发生在视图更新阶段,也就是当数据发生变化的时候,diff
会对新旧虚拟DOM进行对比,只渲染有变化的部分。
当数据发生变化的时候,依赖对应的watcher
会通知更新,生成一个新的vnode
,新的vnode
会去和旧的vnode
进行对比更新。
整个更新的过程就是调用path函数,主要做了三件事:
- 创建节点:新的
vnode
中有而旧的vnode
中的节点,在旧vnode
中进行创建 - 删除节点:新的
vnode
中没有而旧的vnode
中有,在旧的vnode
中删除 - 更新节点:新的
vnode
和旧的vnode
中都有,以新的vnode
位主,更新旧的vnode
vue实例是挂载到那个标签上的?
vue实例最后会挂载在body标签里面,所以我们在vue中是获取不了body 标签的,如果要使用body标签的话需要用原生的方式获取document.body
keep-alive原理
keep-alive
是Vue.js的一个内置组件。它能够将不活动的组件实例保存在内存中,而不是直接将其销毁,它是一个抽象组件,不会被渲染到真实DOM中,也不会出现在父组件链中。
通过include、exclude
来匹配和排除缓存,max
定义缓存的上限。
keep-alive
内部其实是一个函数式组件,没有template
标签。在render
中通过获取组件的name
和include、exclude
进行匹配。匹配不成功,则不需要进行缓存,直接返回该组件的Vnode。
匹配成功就进行缓存,获取组件的key
在this.cache
中进行查找,如果存在就直接将缓存的组件实例覆盖到当前的Vnode上,然后将当前组件的key
从keys
中进行删除,然后在push(key)
添加到尾部,这样做是为了改变key
当前的位置,也就实现了max
功能。
不存在的话,就需要对组件进行缓存。将当前组件push(key)
添加到尾部,然后再判断当前缓存的max是否超出指定个数,如果超出直接将第一个组件销毁(缓存淘汰策略LRU)。
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
active-class 是哪个组件的属性
active-class是vue-router模块的router-link组件中的属性,用来做选中样式的切换;
route和router的区别
route是路由信息对象包括path, params, hash, query,fullpath…
router是路由实例, 对象包括了路由的跳转方法, 钩子函数等等
vue-router怎么配置路由
在vue中配置路由分为5个步骤,分别是:
- 引入vue-router.js
- 配置路由path和组件, 和生成路由对象
- 把路由对象配置到new Vue中router选项下
- 页面使用 承载路由
- 设置路由导航(声明式导航方式/编程式跳转)
平时都是用那些工具进行打包的?babel是什么?
WebPack 是一个模块打包工具,你可以使用WebPack管理你的模块依赖,并编绎输出模块们所需的静态文件。它能够很好地管理、打包Web开发中所用到的HTML、Javascript、CSS以及各种静态文件(图片、字体等),让开发过程更加高效。对于不同类型的资源,webpack有对应的模块加载器。webpack模块打包器会分析模块间的依赖关系,最后 生成了优化且合并后的静态资源
babel可以帮助我们转换一些当前浏览器不支持的语法,它会把这些语法转换为低版本的语法以便浏览器识别。
图片懒加载是怎么实现的?
就是我们先设置图片的data-set属性(当然也可以是其他任意的,只要不会发送http请求就行了,作用就是为了存取值)值为其图片路径,由于不是src,所以不会发送http请求。 然后我们计算出页面scrollTop的高度和浏览器的高度之和, 如果图片距离页面顶端的坐标Y(相对于整个页面,而不是浏览器窗口)小于前两者之和,就说明图片就要显示出来了(合适的时机,当然也可以是其他情况),这时候我们再将 data-set 属性替换为 src 属性即可。
vue中computed 和watch 的区别是什么?
computed计算属性就是为了简化template里面模版字符串的计算复杂度、防止模版太过冗余。它具有缓存特性
computed用来监控自己定义的变量,该变量不在data里面声明,直接在computed里面定义,然后就可以在页面上进行双向数据绑定展示出结果或者用作其他处理;
watch主要用于监控vue实例的变化,它监控的变量当然必须在data里面声明才可以,它可以监控一个变量,也可以是一个对象,一般用于监控路由、input输入框的值特殊处理等等,它比较适合的场景是一个数据影响多个数据,它不具有缓存性
- watch:监测的是属性值, 只要属性值发生变化,其都会触发执行回调函数来执行一系列操作。
- computed:监测的是依赖值,依赖值不变的情况下其会直接读取缓存进行复用,变化的情况下才会重新计算。
除此之外,有点很重要的区别是:计算属性不能执行异步任务,计算属性必须同步执行。也就是说计算属性不能向服务器请求或者执行异步任务。如果遇到异步任务,就交给侦听属性。watch也可以检测computed属性。
nextTick的原理
vue中的nextTick
是浏览器eventLoop
是应用。nextTick
是将回调函数放到一个异步队列中,保证在异步更新DOM的watcher
后面,从而获取到更新后的DOM。
Vue在更新DOM时是异步执行的。只要侦听到数据变化,Vue
将开启1个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个watcher
被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM
操作是非常重要的。nextTick
方法会在队列中加入一个回调函数,确保该函数在前面的dom操作完成后才调用;
vue中的组件通讯方式
父传子
- props属性传值 —props只读
- $children
- $refs
子传父
e m i t ( 子组件通过 V u e 实例方法 emit (子组件通过Vue实例方法 emit(子组件通过Vue实例方法emit进行触发并且可以携带参数,父组件监听使用@(v-on)进行监听)
$parent
兄弟组件
provied
inject
eventBus(第三方new Vue定义为eventBus,created中订阅方法eventBus. o n ,另一个兄弟组件中的 m e t h o d s 中写函数,在函数中发布 e v e n t B u s 订阅的方法 e v e n t B u s . on,另一个兄弟组件中的methods中写函数,在函数中发布eventBus订阅的方法eventBus. on,另一个兄弟组件中的methods中写函数,在函数中发布eventBus订阅的方法eventBus.emit("自定义事件名”) ,在组件的template中绑定事件(比如click))
Vuex
v-model和.sync的对比
两者本质都是一样,并没有任何区别: “监听一个触发事件”=“(val) => value = val”。
1.只不过v-model默认对应的是input或者textarea等组件的input事件,如果在子组件替换这个input事件,其本质和.sync修饰符一模一样。比较单一,不能有多个。
2.一个组件可以多个属性用.sync修饰符,可以同时"双向绑定多个“prop”,而并不像v-model那样,一个组件只能有一个。
vue路由钩子beforeEach的参数
to: Route
: 即将要进入的目标 路由对象from: Route
: 当前导航正要离开的路由next: Function
: 一定要调用该方法来 resolve 这个钩子。执行效果依赖next
方法的调用参数。
vue2中如何自定义指令
Vue.directive( id, [definition] )
方式注册全局指令,第一个参数为自定义指令名称(指令名称不需要加 v-
前缀,默认是自动加上前缀的,使用指令的时候一定要加上前缀),第二个参数可以是对象数据,也可以是一个指令函数。
Vue实例中添加 directives
对象数据注册局部自定义指令。
// 注册一个全局自定义指令 v-focus
Vue.directive(‘focus’, {
// 当被绑定的元素插入到 DOM 中时……
inserted: function (el) {
// 聚焦元素
el.focus()
}
}
一个指令定义对象可以提供如下几个钩子函数 (均为可选):
- bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
- inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。
- update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 。
- componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。
- unbind:只调用一次,指令与元素解绑时调用。
钩子函数的几个参数吧。
- el: 指令所绑定的元素,可以用来直接操作 DOM,就是放置指令的那个元素。
- binding: 一个对象,里面包含了几个属性,这里不多展开说明,官方文档上都有很详细的描述。
- vnode:Vue 编译生成的虚拟节点。
- oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
CDN
是内容分发网络-----我们用它来提高访问速度。
公司购买的云服务—腾讯云
把一些静态资源:css, .js,图片,视频放在第三方的CDN服务器上,可以加速访问速度。
好处:
- 减少应用打包出来的包体积
- 加快静态资源的访问
- 通过在现有的Internet中增加一层新的CACHE(缓存)层,将网站的内容发布到最接近用户的网络”边缘“的节点,使用户可以就近取得所需的内容,提高用户访问网站的响应速度。
- 从技术上全面解决由于网络带宽小、用户访问量大、网点分布不均等原因,提高用户访问网站的响应速度。
简单的说,CDN的工作原理就是将您源站的资源缓存到位于全球各地的CDN节点上,用户请求资源时,就近返回节点上缓存的资源,而不需要每个用户的请求都回您的源站获取,避免网络拥塞、缓解源站压力,保证用户访问资源的速度和体验
说一下什么是动态组件和异步组件?
1 动态组件就是 component组件 ,组件身上可以绑定一个is属性, 用来表示某一个组件。
通过使用保留的元素,动态地绑定到它的 is 特性,我们让多个组件可以使用同一个挂载点,并动态切换。根 据 v-bind:is=“组件名” 中的组件名去自动匹配组件,如果匹配不到则不显示。
2 异步组件
在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模 块。为了简化,Vue 允许你以一个工厂函数(工厂函数是专门创建对象的函数)的方式定义你的组件,这个工厂 函数会异步解析你的组件定义。Vue 只有在这个组件需要被渲染的时候才会触发该工厂函数,且会把结果缓 存起来供未来重渲染。
创建 store 实例的时候,除了 state、mutations、actions、getters、mudules,plugins 选项是干什么的,你用过吗(讲过 vuex-persistedstate 这个插件可以做 vuex 中的数据持久化,百度一下)?
vue2中vue.use是怎么用的
安装插件,如果插件是一个对象,则必须要提供一个install方法。 如果插件是个函数,它会被作为install方法。 调用install方法时, 会将Vue作为参数传入; install方法被同一个插件调用多次时, 插件也只会被安装一次
vue-cli 2.0和3.0 有什么区别?
3.0 把配置webpack的文件隐藏了,如果需要配置它需要创建一个vue.config.js文件,3.0 是2018.10月出来的
vue2和vue3的区别:
1、生命周期的改变(setup代替了之前的beforeCreate和created,其他生命周期名字有些变化,功能都是没有变化的) proxy代替defineProperty
2、性能方面的改变(Diff算法的提升)
3、新增的compositionAPI(组合式API)
4、v-model的变化
vue3的响应式原理
通过proxy(代理) : 拦截对象中任意属性的变化, 包括:属性值的读写, 属性的添加, 属性的删除等
通过reflect(反射) 对被代理对象的属性进行操作
Object.defineProperty和proxy的区别
proxy的优势
- proxy可以直接监听对象而不是属性
- 可以监听数组的变化
- 有多达13种拦截方法apply, ownKeys, deleteProperty等Object.defineProperty不具备的
- 返回的是一个新对象, 我们可以操作对象而达到目的, 而Object.defineProperty只能遍历对象属性直接修改
Object.defineProperty优势
兼容性好
vue中组件和插件有什么区别
编写形式
每一个.vue文件都可以看成是一个组件
vue
插件的实现应该暴露一个 install
方法。这个方法的第一个参数是 Vue
构造器,第二个参数是一个可选的选项对象
注册形式
组件:全局注册通过vue.component(第一个参数为组件的名称,第二个参数为传入的配置项)
局部注册通过components: {组件名}
插件: vue.use(第一个参数为插件的名字,第二个参数是可选择的配置项)
使用场景
组件 (Component)
是用来构成你的 App
的业务模块,它的目标是 App.vue
插件 (Plugin)
是用来增强你的技术栈的功能模块,它的目标是 Vue
本身
Vue 相关的性能优化
v-if和v-for 不要一起使用,v-for 循环加key 路由懒加载 v-if和v-show区分使用场景,computed和方法优先使用前者
v-for / v-if 为什么不建议放一行?如何处理?Vue3 呢?
v-for的优先级更高, 每一次都需要遍历整个数组,造成不必要的计算,影响性能,
则在外层嵌套template
(页面渲染不生成dom
节点),在这一层进行v-if判断,然后在内部进行v-for循环
vue3中 v-if的优先级更高 尽量避免在同一个元素上面同时使用 v-if 和 v-for ,建议使用计算属性替代。
说一下对 Vue3 的了解?
性能更高了。
- 响应式的原理变成了 Proxy;
- VNode Diff 算法进行了优化;
体积更小了。
- 所有的 API 都是按需导入,能配合 Webpack 支持 Tree Shaking;
- 删除了不常用的一些特性 / API,filter、Event Bus…
对 TS 支持更好了。
- 本身就是用 TS 写的。
Composition API(组合 API)。
- 对于开发大型项目更利于代码的组织和复用。
新增加了一些特性(Fragment、Suspense、Teleport…)。
你项目中是怎么组织接口请求的,axios 封装,你都封装了哪些东西?
怎么配置 vue-cli 这个工具呢,你都配置过哪些东西(vue.config.js)?
了解 Webpack 吗,说一下常见的配置?
node.js是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
是
一个基于 Chrome V8 引擎的
JavaScript- 基于 Express/Koa 框架(http://www.expressjs.com.cn/),可以快速构建 Web 应用
- 基于 Electron 框架(https://electronjs.org/),可以构建跨平台的桌面应用
- 基于 restify 框架(http://restify.com/),可以快速构建 API 接口项目
- 读写和操作数据库、创建实用的命令行工具辅助前端开发
可以直接去node.js 官网下载 node –v 查看版本号
在任意终端 直接输入 node 命令并回车
NPM是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
npm(node package manage)node 包 管理器。管理node包的工具。
npm这个工具,在安装 node 的时候,就已经安装到你的计算机中了。
命令行中执行:
npm -v
,如果看到版本号,说明安装成功了。npm的作用是:管理node模块的工具。
- 下载并安装第三方的模块
- 卸载第三方模块
- 发布模块
- 删除已发布的模块
正常的下载安装 npm install 模块名
简写install为i npm i 模块名
一次性安装多个模块 npm i 模块名 模块名 模块名
卸载模块 npm uninstall 模块名 npm un 模块名 npm un
yarn是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
yarn 是包管理器, 快速、可靠、安全的依赖管理工具。(类似npm作用)
初始化 yarn init
添加依赖 yarn add …
移除包 yarn remove …
全局安装 yarn global add [package]
npm i -g yarn下载
下载地址: https://yarn.bootcss.com/docs/install/#windows-stable
webpack是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
webpack 是静态模块打包器 还能翻译和压缩代码 减小代码包体积, 让浏览器更快速打开网页
用法:
- 创建Day01_webpack基础使用文件夹
- yarn init
- yarn add webpack@5.31.2 webpack-cli@4.6.0
- 在package.json中, 配置scripts(自定义命令)
scripts: { “build”: “webpack” }
好处:
- 专注于处理模块化的项目,能做到开箱即用,一步到位
- 通过plugin扩展,完整好用又不失灵活
- 通过loaders扩展, 可以让webpack把所有类型的文件都解析打包
- 区庞大活跃,经常引入紧跟时代发展的新特性,能为大多数场景找到已有的开源扩展
webpack-配置修改
项目根目录 - 新建webpack.config.js文件 (默认配置文件名)
const path = require(“path”)
module.exports = {
entry: “./src/main.js”, // enter: 默认入口
output: {
path: path.join(__dirname, “dist”), // 出口"文件夹"名
filename: “bundle.js” // 出口"文件"名
}
}脚手架是什么?怎么用?好处是?
脚手架是为了保证各施工过程顺利进行而搭设的工作平台
怎么用 : 1 全局安装@vue/cli模块包 yarn global add @vue/cli
2 查看`Vue`命令版本 vue -V 出现版本号就安装成功, 否则失败
好处: 开箱即用 0配置webpack babel支持 css, less支持 开发服务器支持
脚手架如何 创建项目-启动服务
1 创建项目 vue create vuecli-demo
2 cd vuecil-demo yarn serve 启动本地热更新开发服务器
vue是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
vue 是 渐进式javacript框架
怎么用: 1 全局安装@vue/cli模块包
2vue create 项目名
好处: 开发更快速,更高效
企业开发都在使用
前端工程师必备技能,高薪脚手架好处:
- 开箱即用
- 0配置webpack
- babel支持
- css, less支持
- 开发服务器支持
jQuery是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
jQuery 是一个 JavaScript 函数库。
网络上有大量开源的 JS 代码库
layui是什么? 怎么用? 好处是什么? 解决了什么问题? 原理是? 场景是? 有没有什么替代方案?
layui 是一套开源的 Web UI 解决方案
好处: 极易上手,拿来即用
除了变量、嵌套,你还使用过哪些和Less/Sass相关的特性?
混入、计算、数、判断、循环…
作为这个系统的使用者.我该怎么使用?
画面感
封装了什么组件,用到了哪些技术点?
- 每一个.vue文件就可以说是一个大的组件,一般称为业务组件
- 第三方的UI组件 .element vant
- 每一个路由页面的面包屑组件,每一个路由页面的公众标题,编辑和新增功能的组件封装 上传组件…
- 技术点 传值和校验, 插槽和作用域插槽,自定义事件
- 作用域插槽 : 可以在父组件拿到子组件的数据,在父组件加工处理之后再交给子组件使用
六、非-常规问题
为什么 0.1 + 0.2 != 0.3
因为 JS 采⽤ IEEE 754 双精度版本( 64 位),并且只要采⽤ IEEE 754 的语⾔
都有该问题
IEEE 754 双精度版本(64位)将 64 位分为了三段
第⼀位⽤来表示符号
接下去的 11 位⽤来表示指数
其他的位数⽤来表示有效位,也就是⽤⼆进制表示 0.1 中的 10011(0011)
那么这些循环的数字被裁剪了,就会出现精度丢失的问题,也就造成了 0.1
不再是 0.1 了,⽽是变成了 0.100000000000000002
解决: parseFloat((0.1 + 0.2).toFixed(10)) === 0.3 // true