面试题种类
概念类、区别类
概念类: 请简单说说事件委托原理
(1)是什么 (2)应用 (3)原理
区别类: localStorage与sessionStorage区别
面试时,注意礼貌,看着面试官,自信一点。
01-前端基础-webpai
1.1-事件委托面试点
-
1.事件委托:给父元素注册事件,委托给子元素处理,
-
2.事件委托原理:==事件冒泡==
-
3.事件委托应用场景 :
给动态新增元素注册委托事件 -
使用事件委托的好处: 性能高。对后续新增的元素同样有事件绑定效果。
-
如何按需触发:使用自定义属性。
-
4.事件委托注意点
- this : 指向父元素
- e.target : 指向触发事件的子元素
- vue也支持事件委托,不过没有必要。现在流行前端框架,基本不怎么使用了。
- jQuery时代有用,现在的话就用的少了。
利用事件冒泡的原理,把需要绑定到子元素身上的事件,绑定到祖先元素的身上去,其好处:性能高,对后续新增的元素同样有绑定效果,在jQuery时代用的比较多,现在流行前端框架,基本上就用的少了。
1.2-cookie -localStorage与sessionStorage区别
-
1.相同点
作用一致 : 用于存储数据- localStorage和sessionStorage存储大小在5M左右
- cookie 数据根据不同浏览器限制,大小一般不能超过 4k
-
2.不同点: 存储方式不同
localStorage : 硬盘存储(永久存储,页面关闭还在,存在硬盘)sessionStorage :内存存储(临时存储,页面关闭了就消失)- cookie 设置的cookie过期时间之前一直有效,与浏览器是否关闭无关
-
3.localStorage与sessionStorage如何存储引用类型数据(数组和对象)
- 转json存储
4 实际应用场景:登录时存储token
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button class="btn1">存对象</button>
<button class="btn2">取对象</button>
<script>
/*
json -> js : let jsObj = JSON.parse( json格式 )
js -> json : let jsonStr = JSON.stringify( js对象 )
*/
let obj = {
name:'ikun',
age:30,
sex:'男'
}
document.querySelector('.btn1').onclick = function(){
//存 : (1)js -> json (2)存json
// //(1)js->json
// let jsonStr = JSON.stringify( obj )
// //(2)存json
// localStorage.setItem('user',jsonStr)
//合写一行
localStorage.setItem('user',JSON.stringify( obj ))
}
document.querySelector('.btn2').onclick = function(){
//取: (1)取json (2)json->js
// //(1)取json
// let jsonStr = localStorage.getItem('user')
// //(2)json->js
// let jsObj = JSON.parse( jsonStr )
//
//合写一行
let jsObj = JSON.parse( localStorage.getItem('user') )
console.log( jsObj )
}
</script>
</body>
</html>
02-前端基础-js高级
1.1-new关键字做了哪些事情
-
1.创建空对象
-
2.this指向这个对象
-
3.对象赋值(执行构造函数)
-
4.返回实例对象
-
new 构造函数中如果说手动return (1)return 值类型 :无效,还是返回的new创建的这个对象 (2)return 引用类型 :有效,会覆盖new创建的对象
-
应用: new`用来创建实例对象 比如vue的实例创建。
1.1.1构造函数,原型对象,实例对象 三者的关系
- 1.prototype :属于构造函数,指向原型对象
- 2.—proto— :属于实例对象,指向原型对象
- 3.constructor: 属于原型对象,指向构造函数
1.2-原型链相关
当访问对象的某一个属性时,会先从自身的属性上去找,当没有找到时就会去他的隐式原型去寻找,如果还没有找到会再去它原型的原型上去寻找,依此类推,形成一个链式结构,我们称为原型链。
-
1.原型链作用 : 面向对象
继承 -
2.对象访问原型链规则: 就近原则
- 先访问自己,自己没有找原型,原型没有找原型的原型,直到原型链终点。 如果还找不到,属性则获取undefined,方法则报错xxx is not defined
实际应用场景:将一个方法或属性挂载在原型上,其他对象也可以访问。 在对axios再次封装时将axios挂载在vue的实例上。
1.3-如何判断this指向
- this : 谁
调用我,我就指向谁 - 普通函数
函数名(): window - 对象方法
对象名.方法名(): 对象 - 构造函数
new 函数名(): new创建的实例对象 - 箭头函数的this看外层是否有函数,有的话外层函数的this指向就是内部箭头函数的指向。,没有就是window
1.4-call apply bind区别
-
共同点:
- 都可以修改this,第一个参数都是
修改的this - 都可以利用后续参数传参。
- 都可以修改this,第一个参数都是
-
不同点
-
传参方式不同: call是逐一传参, apply是数组/伪数组传参
- 函数名.call(修改的this,参数1,参数2....)
- 函数名.apply(修改的this,数组/伪数组)
- call和bind可以传递任意的参数。apply只能传递两个元素。
-
执行机制不同:call和apply会立即执行函数,bind不会立即执行
- bind会得到一个修改this指向后的新函数
应用场景:call 判断数据类型。apply: 找出数组中的最大最小值,(Math.max.apply(null,arr))
我在上班的时候看见他们的机会并不多,我只是看到过这些代码,比如:object.prototypt.tosting.call(), Math.max.apply(null,arr) 求最大值。
-
1.5-如何判断数据类型
-
- typeof有两种数据类型无法检测: null 、array
- instanof 检测原型。(判断两个对象是否属于实例关系。)不能判断基本数据类型。
-
- Object.prototype.toString.call(数据)
1.6-闭包
-
1.闭包是什么 :
- 闭包 是 使用其他函数内部变量的 函数 (闭包 = 函数 + 其他函数内部变量)
- 闭包 = 函数 + 上下文引用
-
2.闭包作用 : 解决全局变量污染
-
缺点: 容易造成内层泄露,因为闭包中 的局部变量永远不会被回收
-
应用场景:闭包随处可见。
-
手写闭包
-
function fn() {
var num = 10;
function fun() {
console.log(num);
}
return fun;
}
var f = fn();
f();
1.7-递归及应用场景
-
1.递归 : 在函数内部调用自己 (问题相同, 规模不同) 尾递归
-
优点:结构清晰、可读性强
缺点:效率低、调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容
量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。->性能
-
2.递归应用 :
- 浅拷贝与深拷贝
- 遍历dom树
- 数据扁平化处理
1.8-浅拷贝与深拷贝
-
1.浅拷贝:拷贝地址, 修改拷贝后的数据原数据也会变化
-
指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝
如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址
在
JavaScript中,存在浅拷贝的现象有:Object.assignArray.prototype.slice(),Array.prototype.concat()- 使用拓展运算符实现的复制
-
2.深拷贝:拷贝数据, 修改拷贝后的数据原数据不会变化
-
深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性
- 推荐 json : let obj = JSON.parse( JSON.stringify( 对象 ) )
- 递归
- 深拷贝的方法有哪些?
1.推荐 json : let obj = JSON.parse( JSON.stringify( 对象 ) )
2.Object.assign()拷贝
当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。
3.通过jQuery的extend方法实现深拷贝
4.lodash.cloneDeep()实现深拷贝
1.9-数组常用迭代方法
1 连接 arr.join()
2 reverse() 将数组中各元素颠倒顺序
3 pop() 删除数组中最后一个元素,返回删除的那个值,并将长度减 1。
4 shift() 删除数组中第一个元素,返回删除的那个值,并将长度减 1。
5 unshift() 往数组前面添加一个或多个数组元素,长度要改变
6 push() 往数组结尾添加一个或多个数组元素,长度要改变
7 splice( ) 插入、删除或替换数组的元素
8 concat( ) 连接数组
9 sort( ) 对数组元素进行排序
10 slice( ) 返回数组的一部分
11 forEach 遍历所有元素
12 every 判断所有元素是否都符合条件
13 map 指“映射”,对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组。
14 filter 过滤符合条件的元素
15 some():判断数组中是否存在满足条件的项,只要有一项满足条件,就会返回true。
16 findIndex方法,返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
17 Array.Includes 用于判断数组中是否有此元素
18 Array.reduce()接受两个参数:一个是对数组每个元素执行的回调方法,一个是初始值。
19 Array.from方法用于将伪数组转为真正的数组
20 数组去重indexOf、Set、include
1.10-for in与for of区别
(1)功能不同
for - in 遍历下标和元素
for-of 遍历元素
(2)原型不同
for-in 可以遍历原型中的属性
for-of不可以遍历原型中的属性
(3)数据类型不同
for-in 可以遍历 数组+对象
for-of 遍历对象时需要和object.key()配合使用。
总结应用: 如果想要下标和元素就用 for-in 只想要元素就用for-of
1.10-防抖和节流
1防抖:单位时间内,频繁触发事件,只会触发最后一次。
2节流: 单位时间内,频繁触发事件,只会触发一次。
3 作用:相用点: 减少触发的频率,提高代码的性能
3.1.函数防抖流程 :(立刻执行,非立刻执行两种。)
3.1 声明全局变量存储定时器ID 3.2 每一次触发事件, 先清除上一次定时器。 然后将事件处理代码放入本次定时器中 3.3开启定时器
3.2.函数节流流程 : 3.1 声明全局变量存储上一次触发交互时间 3.2 每一次触发事件, 获取当前时间 - 上一次时间。判断是否超过节流间隔 3.3 如果 超过节流时间,则执行事件处理代码。 并且存储本次触发时间。
实际应用场景:防抖:输入框输入事件 input事件。
节流:滚动条事件scroll事件 鼠标移动mousemove事件
1.11浏览器中的事件循环
事件循环(Event Loop)是js实现异步的一种方法,也是js的执行机制。
- 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数。
- 当指定的事情完成时,Event Table会将这个函数移入消息队列
- 主线程内的任务执行完毕为空,会去消息队列读取对应的函数,进入主线程执行。
- 上述过程会不断重复,也就是常说的Event Loop(事件循环)。
web worker
1.12 async await 是什么?它有哪些作用?
async await 是es7里面的新语法、它的作用就是 async 用于申明一个 function 是异步的,而 await 用
于等待一个异步方法执行完成。它可以很好的替代promise 中的then ,async/await 最大的优势就是我们可以用同步的思路来写异步的业务逻辑
async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇
到 await 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
缺点:
代码不容易理解。
await 表达式会阻塞运行,甚至可以直接阻塞循环,不恰当使用会影响代码的性能
应用场景: 在发送异步请求,拿数据时会用。
-
概念
-
Promise Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,简单地说,Promise好比容器,里面存放着一些未来才会执行完毕(异步)的事件的结果,而这些结果一旦生成是无法改变的
-
async await async await也是异步编程的一种解决方案,他遵循的是Generator 函数的语法糖,他拥有内置执行器,不需要额外的调用直接会自动执行并输出结果,它返回的是一个Promise对象
两者的区别- Promise的出现解决了传统callback函数导致的“回调地狱”问题,但它的语法导致了它向纵向发展行成了一个回调链,遇到复杂的业务场景,这样的语法显然也是不美观的。而async await代码看起来会简洁些,使得异步代码看起来像同步代码,await的本质是可以提供等同于”同步效果“的等待异步返回能力的语法糖,只有这一句代码执行完,才会执行下一句。
- async await与Promise一样,是非阻塞的。
- async await是基于Promise实现的,可以说是改良版的Promise,它不能用于普通的回调函数。
- Promise 中不能自定义使用 try/catch 进行错误捕获,但是在 Async/await 中可以像处理同步代码处理错误
1.13、常见的继承有哪些?
一、原型链继承
特点:1、实例可继承的属性有:实例的构造函数的属性,父类构造函数属性,父类原型的属性。(新
实例不会继承父类实例的属性!
缺点:1、新实例无法向父类构造函数传参。
2、继承单一。
3、所有新实例都会共享父类实例的属性。(原型上的属性是共享的,一个实例修改了原型属
性,另一个实例的原 型属性也会被修改!)
二、借用构造函数继承
function fn() {
var num = 10;
function fun() {
console.log(num);
}
return fun;
}
var f = fn();
f();
1
2
3
4
5
6
7
8
9重点:用.call()和.apply()将父类构造函数引入子类函数(在子类函数中做了父类函数的自执行(复
制))
特点:1、只继承了父类构造函数的属性,没有继承父类原型的属性。
2、解决了原型链继承缺点1、2、3。
3、可以继承多个构造函数属性(call多个)。
4、在子实例中可向父实例传参。
缺点:1、只能继承父类构造函数的属性。
2、无法实现构造函数的复用。(每次用每次都要重新调用)
3、每个新实例都有父类构造函数的副本,臃肿。
三、组合继承(组合原型链继承和借用构造函数继承)(常用)
重点:结合了两种模式的优点,传参和复用
特点:1、可以继承父类原型上的属性,可以传参,可复用。
2、每个新实例引入的构造函数属性是私有的。
缺点:调用了两次父类构造函数(耗内存),子类的构造函数会代替原型上的那个父类构造函
数。
四、原型式继承
重点:用一个函数包装一个对象,然后返回这个函数的调用,这个函数就变成了个可以随意增添属性的
实例或对象。object.create()就是这个原理。
特点:类似于复制一个对象,用函数来包装。
缺点:1、所有实例都会继承原型上的属性。
2、无法实现复用。(新实例属性都是后面添加的)
五、* class*类实现继承
通过extends 和super 实现继承
六、寄生式继承
重点:就是给原型式继承外面套了个壳子。
优点:没有创建自定义类型,因为只是套了个壳子返回对象(这个),这个函数顺理成章就成
了创建的新对象。
缺点:没用到原型,无法复用。
1.14 平时都是用什么实现跨域的?
说起跨域,我们不得不提的是同源 什么是同源呢? 同源= 协议 +域名 +端口 , 同源策略是指,若页面的源和页面运行过程中加载的源不一致时,出于安全考虑,浏览器会对跨域的资源访问进行一些限制** 当发送aiax请求时,只要其中任何一个不相同,我们都称之为跨域
解决:
1 jsonp: 利用 script 标签没有跨域限制的漏洞,设置src
2 CORS:跨域资源共享 **CORS 需要浏览器和后端同时支持。
浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域
3 nginx反向代理
实现原理类似于Node中间件代理,需要你搭建一个中转nginx服务器,用于转发请求。
使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。
实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
4 .proxy跨域配置
利用node中间件proxy配置跨域(此处只介绍vite中的代理配置)
node proxy跨域原理图
vite.config.js中
proxy: {
1.如发送请求/api/xxx,此时请求拦/api
'/api': {
2.路径转发,此时请求地址变为http://localhost:5001/api/xxxx
target: 'http://localhost:5001',
changeOrigin: true,//请求改变源,此时nginx可以获取到真实的请求ip
3.路径重写,此时请求变成了http://localhost:5001/xxxx,
path.replace(/^/api/, ‘’)-->将请求地址中的api去除了
rewrite: (path) => path.replace(/^/api/, ‘’)
}
}
1.15* 、谈谈你平时都用了哪些方法进行性能优化?*
减少http请求次数、打包压缩上线代码、使用懒加载、使用雪碧图、动态渲染组件、CDN加载包。尽量减少Dom元素的重绘和回流
1.16 js的执行机制是怎么样的?
js是一个单线程、异步、非阻塞I/O模型、 event loop事件循环的执行机制
所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
异步 任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,
某个异步任务可以执行了,该任务才会进入主线程执行。
1.17 、数据类型的判断有哪些方法?他们的优缺点及区别是什么?
1.18 promise* 是什么?它有哪些作用?*
Promise 是异步编程的一种解决方案.(改变异步代码结果处理的顺序)是es6新增的函数,一般作为一个构造函数去使用,需要new一下,来创建一个promise实例,它有三种状态分别是:pending(进行中),fufilled(已成功),rejected(以失败) 成功会触发then,失败会触发 cath; 还有一个finally是永远都会被触发。
我们会用它去解决回调地狱的问题,也就是异步深层嵌套的问题,,但使用promise并不能简化代码,所以在工作中我一般都会配合 async ,await 来使用。
promise.race() 接收多个promise实例,可以的到最先处理完毕的结果。
promise.all() 接收多个实例参数,都成功会触发then,有一个失败就会触发catch
promise.any() 接收多个实例参数,可以的到最先处理成功的结果,都失败才会触发catch.
1.19 一个页面从输入 URL 到页面加载显示完成,这个过程中都发生 了什么?
1 DNS域名解析:将域名解析为IP地址 2 TCP三次握手:可靠的传输协议 2.1 浏览器->服务器 :在吗?能听到我说话吗?(浏览器发送 ok) 2.2 服务器->浏览器 :我在,听到了 你能听到我说话吗?(服务器接收ok 发送 ok) 2.1 浏览器->服务器 :我也听到你说话了,咱们开始吧(浏览器发送 ok)
3 发送http请求,处理,响应 ajax发送请求 服务器处理 服务器响应html格式文件
4 浏览器解析渲染HTML文件 (1)解析HTML 得到dom树 (2)解析CSS 得到样式树 (3)渲染树 = dom树+样式树 (4)渲染引擎 开始绘制渲染树,并呈现页面 */
1.20 重绘和回流
回流必将引起重绘,重绘不一定会引起回流。
当Render Tree中部分或全部元素的尺寸、结构、或某些属性发生改变时,浏览器重新渲染部分或全部文档的过程称为回流。
什么情况下会导致回流?
- 页面首次渲染
- 浏览器窗口大小发生改变
- 元素尺寸或位置发生改变
- 元素内容变化(文字数量或图片大小等等)
- 元素字体大小变化
- 添加或者删除可见的DOM元素
- 激活CSS伪类(例如::hover)
- 查询某些属性或调用某些方法
一些常用且会导致回流的属性和方法:
- clientWidth、clientHeight、clientTop、clientLeft
- offsetWidth、offsetHeight、offsetTop、offsetLeft
- scrollWidth、scrollHeight、scrollTop、scrollLeft
- scrollIntoView()、scrollIntoViewIfNeeded()
- getComputedStyle()
- getBoundingClientRect()
- scrollTo()
重绘* *(Repaint)**:当页面中元素样式的改变并不影响它在文档流中的位置时(例如:color、background-color、visibility等),浏览器会将新样式赋予给元素并重新绘制它,这个过程称为重绘。
回流比重绘的代价要更高。
1.21 BFC的理解
块级格式化上下文,是一种特性,他拥有自己的一套渲染规则,他决定了其子元素将如何布局,以及和其他元素的相互作用关系。
作用:
1,可以包含内部浮动的元素,
2,可以排除外部浮动带来的影响,
3,阻止外边距重叠,(处于同一BFC中,中间没有具有高度的元素隔开的化,外边距会有重叠。)
4,解决margin塌陷问题
触发BFC特性:
1.根元素(html)
2 浮动元素(元素的flot不是none)
3绝对定位的元素(元素的position为absolute或fixed)
4,行内块元素(元素的display为inline-block)
5 overflow不等于visible
6 display值为flow-root的元素 (没有副作用,常用)
1.22 em和rem的区别
em相对于元素自身字体大小的一个元素,而rem相对于根元素(rem)字体大小的一个单位。
1.23 rem适配的原理
利用媒体查询或 js动态的检测/获取设备的宽度,不同宽度下设置相应的根元素字体大小,当设备宽度变化,根元素字体大小跟着变,那么所有使用rem做适配的元素自然也跟着变。
适配:不同设备宽度不同,显示的内容大小不同。
1.24 如何实现一个盒子水平/垂直居中
1.利用flex布局
display:flex
justify-content:center, 主轴居中
align-items:center 侧轴居中
2 父元素设置相对定位,子元素设置绝对定位
3:在有时不知道盒子自身宽高的情况下使用transform:translate(-50%,-50%)进行位移。
1.24,函数传参,传递简单数据类型和复杂数据类型有怎样的差异。
1.25箭头函数和普通函数的差异
箭头函数是es6新增的特性,用来简化普通函数的写法,规避普通函数this指向的痛点。
1.没有this (不能作为构造函数使用,不能通过 call apply bind修改this指向,不能用作generator函数)
2可以简写,
3内部没有arguments 可以使用'...'拿到所有实参的集合数组
1.26如何并发请求
promise.all()
1.27图片懒加载
常见的性能优化的手段,他的核心的思路。图片不在可视区时不设置src 在进入可视区时再去设置src ,其主要的难度在于,检测元素是否在可视区内,在以前时需要通过一系列的计算来判断,现在的话,浏览器提供了一个api: intersectonobserver来监听元素是否可见,在vue技术栈中开发中在vueuse中有一个useintersectonobserver 这个api更好的来帮助我们判断元素是否可见。
- 先将img标签的src链接设为同一张图片(默认图片),当js监听到该图片进入可视窗口时,再将实际地址应用。
- 判断元素是否可见浏览器有一个api: intersectonobserver来监听元素是否可见,在开发中在vueuse中有一个useintersectonobserver 这个api更好的来帮助我们判断元素是否可见。
2021年我的前端面试准备 - 掘金 (juejin.cn)
1.28.后端给十万条数据怎么处理?
-
采用懒加载+分页(前端维护懒加载的数据分发和分页)
懒加载和分页方式一般用于做长列表优化, 类似于表格的分页功能, 具体思路就是用户每次只加载能看见的数据, 当滚动到底部时再去加载下一页的数据.
-
使用虚拟滚动技术(目前react的antd4.0已支持虚拟滚动的select长列表)
虚拟滚动技术也可以用来优化长列表, 其核心思路就是每次只渲染可视区域的列表数,当滚动后动态的追加元素并通过顶部padding来撑起整个滚动内容
1.29.文件上传,断点上传?
大文件上传
- 将大文件转换成二进制流的格式
- 利用流可以切割的属性,将二进制流切割成多份
- 组装和分割块同等数量的请求块,并行或串行的形式发出请求
- 待我们监听到所有请求都成功发出去以后,再给服务端发出一个合并的信号
断点续传
- 为每一个文件切割块添加不同的标识
- 当上传成功的之后,记录上传成功的标识
- 当我们暂停或者发送失败后,可以重新发送没有上传成功的切割文件
1.30.es6新增的那些特性?
- 新增let和const
1,相同点,都是用来声明变量的
2不同点 :
2.1var没有块级作用域,但有变量提示效果 let const 有块级作用域,没有变量提示的效果 2.2 const声明后必须赋值,且赋值是一个常量 3.3 let const不允许重复声明,var可以重复声明
2:新增了箭头函数(本身是没有this的)。简化定义函数的写法,
3.新增了promise解决回调地狱的问题。
4.新增了模块化 import export 实现导入导出。
5新增了解构赋值。展开运算符。
-
6 Map 和 Set:
- Map对象用于保存键值对,任何值JavaScript支持的值都可以作为一个键或者一个值。这听起来和对象差不多啊?其实它们还是有区别的:
a) object的键只能是字符串或ES6的symbol值,而Map可以是任何值。
b) Map对象有一个size属性,存储了键值对的个数,而object对象没有类似属性。
Map构造函数接收一个二维数组来创建一个Map对象。数组元素的第0位表示Map对象的key,第1位表示Map对象的value。
Map对象使用set方法来新增数据,set方法接收两个参数,第一个表示key,第二个表示value。使用get方法获取数据,参数是对象的key。
Map对象使用delete方法来删除数据,接收一个参数,表示需要被删除的key。
Map对象使用has方法检测是否已经具有某个属性,返回boolean值。
Set对象和Map对象类似,但它是用来存储一组唯一值的,而不是键值对。类似数组,但它的每个元素都是唯一的。
03-服务器课程-ajax
get与post的区别
*(1)传参方式不同
get: 直接在url后面拼接
post: 在请求体中发送
(2)传参速度不同
get: 传输速度快
post: 传输速度慢
(3)数据大小不同
get: 有大小的限制,不同浏览器,大小限制不同
post: 没有大小的限制
一般文件的上传都是post
(4)安全性不同
get:get的安全性低。
post: post的安全性高。
登录注册一般都是post
04-服务器课程-nodejs
常见的git操作
git branch 查看本地所有分支
git status 查看当前状态
git commit 提交
git branch -a 查看所有的分支
git branch -r 查看远程所有分支
git commit -am "init" 提交并且加注释
git remote add origin git@192.168.1.119:ndshow
git push origin master 将文件给推到服务器上
git remote show origin 显示远程库origin里的资源
git push origin master:develop
git push origin master:hb-dev 将本地库与服务器上的库进行关联
git checkout --track origin/dev 切换到远程dev分支
git branch -D master develop 删除本地库develop
git checkout -b dev 建立一个新的本地分支dev
git merge origin/dev 将分支dev与当前分支进行合并
git checkout dev 切换到本地dev分支
git remote show 查看远程库
git add . git rm 文件名(包括路径) 从git中删除指定文件
git clone git://github.com/schacon/grit.git 从服务器上将代码给拉下来
git config --list 看所有用户
git ls-files 看已经被提交的
git rm [file name] 删除一个文件
git commit -a 提交当前repos的所有的改变
git add [file name] 添加一个文件到git index
git commit -v 当你用-v参数的时候可以看commit的差异
git commit -m "This is the message describing the commit" 添加commit信息 git commit -a -a是代表add,把所有的change加到git index里然后再commit git commit -a -v 一般提交命令 git log 看你commit的日志 git diff 查看尚未暂存的更新 git rm a.a 移除文件(从暂存区和工作区中删除) git rm --cached a.a 移除文件(只从暂存区中删除) git commit -m "remove" 移除文件(从Git中删除) git rm -f a.a 强行移除修改后文件(从暂存区和工作区中删除) git diff --cached 或 $ git diff --staged 查看尚未提交的更新 git stash push 将文件给push到一个临时空间中 git stash pop 将文件从临时空间pop下来
05-框架: Vue
1.1-v-if和v-show异同点
v-show和v-if的区别? 分别说明其使用场景?
共同点: v-if和v-show都是控制元素是否可见的。
不同点:原理,性能,和应用场景上有所不同。
原理:
v-show是采用的display:none (样式上对元素进行显示隐藏)
v-if采用的是惰性加载 (他会控制元素是否被创建或销毁。)
性能: v-if比v-show更加消耗性能。
实际应用场景:当需要频繁切换显示隐藏使用v-show (tab栏的切换)
在运行时条件很少改变,则使用 v-if 较好 (在做权限的控制中,控制页面级别按钮是否可见)
为什么避免v-for和v-if在一起使用
效率低:有效率更高的做法,用计算属性过滤一下。
vue2和3中的表现不同,现在的话不会这样写。一定要写的话,会
通过计算属性,先计算出需要循环的元素。
还有一种就是把v-if先放在templte中进行包裹,再进行v-for循环展示。.
1.2-vue生命周期勾子有哪些
总: Vue 实例从创建到销毁的过程,就是生命周期。初始化=>挂载=>更新=>销毁
1,vue2的生命周期:四个阶段八个钩子函数
初始化=>挂载=>更新=>销毁
beforeCreate === created ===beforeMount ===mounted===beforeUpdate===updated===beforeDestroy===destroyed
其他钩子:activated === 在 keep-alive 组件激活时调用。该钩子在服务器端渲染期间不被调用。
deactivated === 在 keep-alive 组件停用时调用。该钩子在服务器端渲染期间不被调用。
- 常用的有三个: created(发送ajax请求),mounted(获取DOM元素,用echer画图时,我们需要操作Dom,需要用到。),beforeDestroy(清空定时器)
- vue3中的钩子函数和vue2中的有所不同。(setup代替了beforeCreate 和created ,其他钩子在名称前都加了on)
可能衍生的问题vue在第一次加载时会执行哪些钩子
在页面第一次加载时会执行: beforeCreate =>created=>beforeMount=>mounted 这几个钩子函数。
父子组件的生命周期钩子函数执行顺序:
Vue 实例从创建到销毁的过程,就是生命周期。初始化=>挂载=>更新=>销毁
1**)beforeCreate (vue实例刚刚创建) vue 实例刚刚在内存中创建,此时 data 和 methods 这些都没初始化好。 比beforeCreate 更早的的是setup
2* )created*(访问到data中的数据 发送ajax请求) vue 实例在内存中已经创建好了,data 和 methods 也能够获取到了,但是模板还没编译。
可以访问到data中的数据 发送异步请求
3* )beforeMount*()
在挂载开始之前被调用:创建 虚拟DOM 相关的 render 函数首次被调用。 完成了模板的编译,但是还没挂载到页面上。
4* )mounted*(挂载到真实Dom上)(用echer画图时,我们需要操作Dom,需要用到。用echer画图时,我们需要操作Dom,需要用到。)
把虚拟DOM 挂载到真实的DOM上, 在Vue中可以获取DOM元素对象 模板编译完成,挂载完成,页面也可以显示。
5* )beforeUpdate*
数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器。该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
6* )updated*
由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
7* )activated*
keep-alive 组件激活时调用。该钩子在服务器端渲染期间不被调用。
8* )deactivated*
keep-alive 组件停用时调用。该钩子在服务器端渲染期间不被调用。
9* )beforeDestroy* (清空定时器。)
实例销毁之前调用。在这一步,实例仍然完全可用。该钩子在服务器端渲染期间不被调用。
10* )destroyed*
Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。
11* )errorCaptured(2.5.0+ 新增)*
当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传播。
常用的有三个: created,mounted,beforeDestroy
Vue2--------------vue3
beforeCreate -> setup()
created -> setup()
beforeMount -> onBeforeMount
mounted -> onMounted
beforeUpdate -> onBeforeUpdate
updated -> onUpdated
beforeDestroy -> onBeforeUnmount
destroyed -> onUnmounted
activated -> onActivated
deactivated -> onDeactivated
errorCaptured -> onErrorCaptured
1.3-vue在第一次加载时会执行哪些钩子
在页面第一次加载时会执行: beforeCreate =>created=>beforeMount=>mounted 这几个钩子函数。
1.4 created和mouted的区别
1.首先,created() 和 mounted() 都可以访问原型上的 data 和 props
2 在created()中非常适合调用 API,发送异步请求。而mounted()` 非常适合在 DOM 元素完全加载后执行任何操作。
实际应用: 在created()钩子函数中 发送异步请求, 而在mounted()中去获取更新后的DOM
在created中对chart.js做图时,进行一些初始化配置时,去操作html的dom节点,找不到相关的元素。这时候使用mounted()
1.5 :v-model双向数据绑定的原理
在vue2中
1其本质是语法糖。v-model即可以作用于表单元素,又可作用于自定义组件,无论是哪一种情况(vue2, vue3),它都是一个语法糖,最终会生成一个属性和一个事件。
原理: 在input框里面通过v-bind动态绑定一个value,然后在input框里面通过@input方法去动态获取input输入的值,然后重新给变量赋值就可以了。
在vue3中:
modelValue属性和onUpdate:modelValue事件。
- 给子组件传递一个属性:modelValue,
2。监听子组件上的事件update:modelValue,在回调函数中,将从子组件回传的值保存
实际应用场景:收集用户输入的数据。以及父子组件传值中都有用到。
1.6:vue响应式的原理:
1.vue主要是通过:数据劫持 + 依赖收集(发布者-订阅者模式)的方式来实现的。
2.Vue2.x是借助Object.defineProperty()实现的,而Vue3.x是借助Proxy实现的,
3 vue2. 原理:3.1通过Object.defineProperty为对象添加属性,可以设置对象属性的getter和setter函 数。(遍历生成对应的setter,getter函数)
3.2 在获取属性时都会执行getter函数,在这个函数中我们会把调用此属性的依赖收集到一个集合中 ;
3.3 在修改属性时,会触发这里定义的setter函数,在setter函数中会去通知集合中的依赖更新,做到数据变更驱动视图变更。
4 vue3.中的响应式与2.x的核心思想一致,只不过数据的劫持使用Proxy(一次性的添加getter和setter函数)而不是Object.defineProperty,Proxy相比Object.defineProperty在处理数组和新增属性的响应式处理上更加方便。
1.7 组件中的data为什么要定义成一个函数而不是一个对象?
每个组件都是 Vue 的实例。组件共享 data 属性,当 data 的值是同一个引用类型的值时,改变其中一 个会影响其他 ,。而如果data是一个函数是,他得到的是一个返回后的新函数,改变其中一个时他不会影响到其他。
1.8 vue中computed 和watch的区别是什么?
computed(计算属性) :
computed也是一个特殊的watch
1 支持缓存,只有依赖数据发生改变,才会重新进行计算。
2 不支持异步,当computed内有异步操作时无效,无法[监听]数据的变化
4 在使用上:如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed 5 如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。
watch( 侦听属性)
1 不支持缓存,数据变,直接会触发相应的操作; 2 watch支持异步; 3 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值; 4 当一个属性发生变化时,需要执行对应的操作;一对多;
5 watch监听对象时需要配合deep:true属性使用
6 immediate:true 页面首次加载的时候做一次监听
注意
不要在 computed 或 watch 中,去修改所依赖的数据的值,尤其是 computed;如果这样做,可能导致一个无线循环的触发。
应用场景: 实际得到的数据不能直接放到页面中展示,而是需要进行一些操作处理,就使用computed。
- watch 适用场景:一个数据影响多个数据;当需要在数据变化时执行异步或开销较大的操作时 (计数器)(监听路由的变化)
- computed适用场景:一个数据受多个数据影响;(购物总价的计算)
实际得到的数据不能直接放到页面中展示,而是需要进行一些操作处理,就使用computed。
当需要在数据变化时执行异步或开销较大的操作时,就使用watch。。
1.9 什么是Pinia以及它的作用?
pinia是什么?
pinia是用于vue的状态管理库,类似于vuex,是vue的另一种状态管理工具
与vuex相比
与 Vuex 相比,Pinia 提供了一个更简单的 API,具有更少的规范,提供了 Composition-API 风格的 API,最重要的是,在与 TypeScript 一起使用时具有可靠的类型推断支持。
- mutations 不再存在。
- 无需创建自定义复杂包装器来支持 TypeScript,所有内容都是类型化的,并且 API 的设计方式尽可能利用 TS 类型推断。
- 不再需要注入、导入函数、调用函数、享受自动完成功能!
- 无需动态添加 Store,默认情况下它们都是动态的
- 不再有 modules 的嵌套结构
安装pinia(仅限于vue3):
npm install pinia`或者 `yarn add pinia
使用:
- 在src目录下新建一个stroe文件夹,在文件夹中新建一个index.ts
- 通过import将下载好的pinia引入到index.ts中并导出
import { createPinia, defineStore } from 'pinia'
export const Pinia = createPinia()
export default defineStore('admin', {
//这里的state与vue2中用来存放初始化变量的data的写法相似,需要return
state: () => {
return {
count: 100,
}
},
getters: {
count: state => state.count
},
//Actions 相当于组件中的 methods。 它们可以使用 defineStore() 中的 actions 属性定义,并且它们非常适合定义业务逻辑:
actions: {
}
},
})
在main.js中进行注册:
import { createApp } from 'vue'
import App from './App.vue'
import { Pinia } from './store/index'
const app = createApp(App)
app.use(Pinia)
app.mount('#app')
了解更多pinia中文官网链接
1.10 hash模式和history模式的区别?
相同点:这两种模式都可以进行路由的跳转。
不同之处有三点。
一是原理不同。
hash模式的实现原理是通过监听hashChange事件来实现的。
history模式是通过调用 history.pushState方法(或者replaceState) 并且 监听popstate事件来实现的。
history.pushState会追加历史记录,并更换地址栏地址信息,但是页面不会刷新,需要手动调用地址变化之后的处理函数,并在处理函数内部决定跳转逻辑;监听popstate事件是为了响应浏览器的前进后退功能。
二是表现不同。hash模式会在地址栏中有#号,而history模式没有;同时由于history模式的实现原理用到H5的新特性,所以它对浏览器的兼容性有要求(IE >= 10)。
三是history模式开发的SPA项目,需要服务器端做额外的配置,否则会出现刷新白屏(链接分享失效)。原因是页面刷新时,浏览器会向服务器真的发出对这个地址的请求,而这个文件资源又不存在,所以就报404。处理方式就由后端做一个保底映射:所有的请求全部拦截到index.html上。
1.11 Vue中的$nextTick有什么作用?
立即能够获取更新后的dom元素,本质是一种优化策略。
vue在更新Dom时是异步执行的,当数据发生变化时,vue会开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新,而$nextTick就应运而生了,我们可以使用它,来立即获取更新后的DOM元素。
原理:
- 把回调函数放入callbacks(异步操作队列)等待执行
- 将执行函数放到微任务或者宏任务中
- 事件循环到了微任务或者宏任务,执行函数依次执行callbacks中的回调
应用:
- created中获取DOM的操作需要使用它
1.12 什么是虚拟DOM?
(1)vue在执行时会将模板(template)通过 vue-loader编译为渲染函数(render)(只会做一次)
(2)渲染函数会根据当前的数据data生成虚拟DOM树
(3)虚拟Dom树会和上次的虚拟Dom树通过diff算法做对比
(4)局部更新真实Dom,在上一轮真实Dom的基础上打补丁(patch)
虚拟Dom就是一个(树状)js对象。有跨平台的能力,体积小,性能提升的优点。
虚拟dom(virtual dom ===> vdom)
虚拟DOM是由js实现的避免DOM树频繁更新,通过js的对象模拟DOM中的节点,然后通过特定的render方法将它渲染成真实的节点,数据更新时,渲染得到新的 Virtual DOM,与上一次得到的 Virtual DOM 进行 diff,得到所有需要在 DOM 上进行的变更,然后在 patch 过程中应用到 DOM 上实现UI的同步更新。
diff算法:
diff算法特点:(diff算法就是进行一个虚拟节点的·对比,返回一个patch对象,存储两个节点不同的地方,最后用patch的记录去局部更新Dom)调用patch函数比较新旧节点,一边比较一边给真实dom打补丁。
同级比较(四个指针,头对头,尾对尾,交叉对比,没有找到就是新增,找到就是复用) ,
深度优先(递归)
1.13 vue3新特性
- 数据响应式原理重新实现 (ES6 proxy 替代了 ES5 的 Object.defineProperty) 解决了: 例如数组的更新检测等bug, 大大优化了响应式监听的性能 (原来检测对象属性的变化, 需要一个个对属性递归监听) proxy 可以直接对整个对象劫持
- 虚拟DOM - 新算法 (更快 更小)
- 提供了composition api, 可以更好的逻辑复用
- 模板可以有多个根元素
- 源码用 typescript 重写, 有更好的类型推导 (类型检测更为严格, 更稳定)
1.14 setup 函数
setup 函数的特点:
composition api的使用, 需要配置一个setup 函数
- setup 函数是一个新的组件选项, 作为组件中 compositionAPI 的起点
- 从生命周期角度来看, setup 会在 beforeCreate 钩子函数之前执行
- setup 中不能使用 this, this 指向 undefined
- 在模版中需要使用的数据和函数,需要在
setup返回。
1.15 组件传值方式:
1,常用父子传值 props $emit / v-on slot插槽 2,不常用父子传值 $parent / $children ref / $refs 3,语法糖传值 v-model .sync 4,跨级通信 eventBus Vuex provide / inject $attrs / $listeners 5,访问根组件 $root
1.16说一下SPA单页面有什么优缺点?
优点:
1.体验好,不刷新,减少 请求 数据ajax异步获取 页面流程;
2.前后端分离
3.减轻服务端压力
4.共用一套后端程序代码,适配多端
缺点:
1.首屏加载过慢;
2.SEO 不利于搜索引擎抓取
1.17 路由传参
1、通过params方式传参
通过$route.push的path携带参数方式 (路由配置中指定参数)
这种方式参数是以/id跟在url后,刷新页面后参数不会丢失。
通过$route.push的params传参 (路由配置中未指定参数)
这种方式的传参,必须使用name进行跳转,未在路由配置:id,url后不会显示id,刷新页面后 参数会丢失。
2、通过query方式传参
这种方式的参数以?id跟在url后,类似get传参,并且,query必须使用path进行传参。刷新页面后参数不会丢失。
1.18 key的作用
1更加高效的更新虚拟DOM。
2 key是每一个虚拟Dom的唯一id,也是diff算法的一种优化策略,可以根据key更准确,更快的找到对应的虚拟Dom的节点。
讲加了key和不加key的区别。
应用场景:v-for循环。
1.19 vue2的缺点:
1.深度监听需要一次性递归。
2无法监听对象新增|删除的属性。
3无法监听数组的元素操作。
4 在vue3中使用了proxy代替了Object.defineProperty解决了这些问题。
1.20你会在vue的哪个生命周期钩子里面发请求,为什么
首先来说,发请求的时机肯定是越早越好的,但是还有一点,有时候请求前需要依赖data里面的数据,或调用methods里面的方法,而data和methods都是需要实例创建完毕后(created)才具有的,所以一般在created里面发请求。
不过有时候在这个请求需要依赖DOM相关操作的话,我会选择在mounted中进行。
1.20路由守卫的理解
1、全局前置守卫—— router.beforeEach
包含三个参数:
(1)to(必填) :即将要进入的目标————通俗来说就是去哪儿
(2)form(必填) :当前导航正要离开的路由————通俗来说就是来自哪
(3)next(可选) : 钩子函数,里面定义参数,确认下一步路由要做什么 next('/')或者 next({ path: '/' }):跳转到一个不同的地址。
一般应用在用户未能验证身份时重定向到 /login
2、全局解析守卫————router.beforeResolve
这和 router.beforeEach 类似,因为它在 每次导航时都会触发,但是确保在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用
3、全局后置钩子————router.afterEach
和守卫不同的是,后置钩子不会接受 next函数也不会改变导航本身:
router.afterEach((to, from) => { // 在全局内置守卫中使用功能包(特效),可在后置钩子中进行终止
})
4、路由独享的守卫————beforeEnter
你可以直接在路由配置上定义,但是它只在进入路由时触发,不会在 params、query 或 hash 改变时触发。
const routes = [
{
path: '/users/:id',
component: UserDetails,
beforeEnter: (to, from) => {
// reject the navigation
return false
},
},
]
复制代码
5、组件内的守卫
beforeRouteEnter---进入前beforeRouteUpdate--- 路由变化时beforeRouteLeave--- 离开后
完整的导航解析流程#
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave守卫。 - 调用全局的
beforeEach守卫。 - 在重用的组件里调用
beforeRouteUpdate守卫(2.2+)。 - 在路由配置里调用
beforeEnter。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter。 - 调用全局的
beforeResolve守卫(2.5+)。 - 导航被确认。
- 调用全局的
afterEach钩子。 - 触发 DOM 更新。
- 调用
beforeRouteEnter守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入
1.21.单页面数据优化,
单页面:
SPA首屏优化方式
-
减小入口文件积
常用的手段是路由懒加载,把不同路由对应的组件分割成不同的代码块,待路由被请求的时候会单独打包 路由,使得入口文件变小,加载速度大大增加。
-
静态资源本地缓存
前端合理利用
localStorage -
UI框架按需加载
不直接引用整个
UI库,按需引入 -
图片资源的压缩
对于所有的图片资源,我们可以进行适当的压缩
对页面上使用到的
icon,可以使用在线字体图标,或者雪碧图,将众多小图标合并到同一张图上,用以 减轻http请求压力。 -
组件重复打包
-
开启GZip压缩
减少首屏渲染时间的方法有很多,总的来讲可以分成两大部分 :资源加载优化 和 页面渲染优化
1.22.有没有使用哈希路由
hash模式
1.url路径会出现#字符。
- hash值不包括在http请求中,他是交由前端路由处理的.所以改变hash值时,它不会刷新页面,也不会发送请求。 3. hash值的改变会触发hashchange事件。
1.23.爆400 怎么弄
HTTP请求状态码400,说明这个请求是无效的,并没有进入后台服务器(控制器)里。
原因一:请求参数个数不对。,
原因二:请求参数的类型不对
原因三:Get请求的url长度超过浏览器或web服务器限制。
解决办法: 对照参数名称 ,个数,类型 等保证一致。
1.24.vuex 是怎么理解的
Vuex 是一个专为 Vue.js 应用程序开发的状态管理工具。它采用集中式存储管理应用的所有组件的状态,而更改状态的唯一方法是提交mutation
当项目遇到以下两种场景时
- 多个组件依赖于同一状态时。
- 来自不同组件的行为需要变更同一状态。
5个核心属性
state、getters、mutations、actions、modules 。
Vuex中状态是对象时,使用时要注意点:
因为对象是引用类型,复制后改变属性还是会影响原始数据,这样会改变state里面的状态,是不允许,所以先用深度克隆复制对象,再修改。
Vuex中action和mutation有什么区别?
有用过Vuex模块吗,为什么要使用,怎么使用。
有,因为使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。所以将 store 分割成模块(module)。每个模块拥有自己的 state、mutations、actions、getters,甚至是嵌套子模块,从上至下进行同样方式的分割。
在module文件新建moduleA.js和moduleB.js文件。在文件中写入
const state={ //... } const getters={ //... } const mutations={ //... } const actions={ //... } export default{ state, getters, mutations, actions }
然后再index.js引入模块
import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); import moduleA from './module/moduleA' import moduleB from './module/moduleB' const store = new Vuex.Store({ modules:{ moduleA, moduleB } }) export default store
1.25.vuex 持久化怎么解决
1.手动利用HTML5的本地存储
方法
- vuex的state在localStorage或sessionStorage或其它存储方式中取值
- 在mutations,定义的方法里对vuex的状态操作的同时对存储也做对应的操作。
2.利用vuex-persistedstate插件
Vuex持久化插件-解决刷新数据消失的问题 - 掘金 (juejin.cn)
1.26插槽你是怎么理解的
插槽,其实就相当于占位符。它在组件中给你的HTML模板占了一个位置,让你来传入一些东西。插槽又分为 匿名插槽、具名插槽、作用域插槽。
1.27.插槽分几种
匿名插槽 我们也可以叫它单个插槽或者默认插槽。和具名插槽相对,它是不需要设置 name 属性的,它隐藏的name属性为default ,也可以不写就是默认的意思啦
具名插槽 顾名思义就是slot 是带有name的 ,定义: 或者使用简单缩写的定义 #header 使用:要用一个 template标签包裹
作用域插槽 就是用来传递数据的插槽 。如果希望在父组件中的插槽中使用到子组件中对应插槽的数据源,可以使用作用域插槽(子组件 slot 中用到的数组中可以传给父组件来使用)
1.28.自定义指令 有没有用到过 具体说一说你定义过的
Vue自定义指令和组件一样存在着全局注册和局部注册两种方式。先来看看注册全局指令的方式,通过 Vue.directive( id, [definition] ) 方式注册全局指令,第一个参数为自定义指令名称(指令名称不需要加 v- 前缀,默认是自动加上前缀的,使用指令的时候一定要加上前缀),第二个参数可以是对象数据,也可以是一个指令函数。
定义过自定义指令有v-lazy其就是一个图片懒加载的功能,在开始时不设置src属性.当图片可见时(IntersectionObserver判断元素可见)去设置src加载图片。
20常用的修饰符有哪些
-
1.29. 常用的修饰符有哪些
vue中修饰符分为以下五种:
- 表单修饰符
- 事件修饰符
- 鼠标按键修饰符
- 键值修饰符
- v-bind修饰符
表单修饰符
在我们填写表单的时候用得最多的是input标签,指令用得最多的是v-model
关于表单的修饰符有如下:
- lazy:在我们填完信息,光标离开标签的时候,才会将值赋予给value,也就是在change事件之后再进行信息同步
- trim:自动过滤用户输入的首空格字符,而中间的空格不会过滤
- number:自动将用户的输入值转为数值类型,但如果这个值无法被parseFloat解析,则会返回原来的值
事件修饰符
事件修饰符是对事件捕获以及目标进行了处理,有如下修饰符:
- stop:阻止了事件冒泡,相当于调用了event.stopPropagation方法
- prevent:阻止了事件的默认行为,相当于调用了event.preventDefault方法
- self:只当在 event.target 是当前元素自身时触发处理函数
- once:绑定了事件以后只能触发一次,第二次就不会触发
- capture:使事件触发从包含这个元素的顶层开始往下触发
- passive:在移动端,当我们在监听元素滚动事件的时候,会一直触发onscroll事件会让我们的网页变卡,因此我们使用这个修饰符的时候,相当于给onscroll事件整了一个.lazy修饰符
- native:让组件变成像html内置标签那样监听根元素的原生事件,否则组件上使用 v-on 只会监听自定义事件
鼠标按钮修饰符
鼠标按钮修饰符针对的就是左键、右键、中键点击,有如下:
- left 左键点击:
<button @click.left="shout(1)">ok</button> - right 右键点击:
<button @click.right="shout(1)">ok</button> - middle 中键点击:
<button @click.middle="shout(1)">ok</button>
键盘修饰符
键盘修饰符是用来修饰键盘事件(onkeyup,onkeydown)的,有如下:
keyCode存在很多,但vue为我们提供了别名,分为以下两种:
普通键(enter、tab、delete、space、esc、up...)
系统修饰键(ctrl、alt、meta、shift...)
<input type="text" @keyup.keyCode="shout()"> //只有按键为keyCode的时候才能触发还可以通过以下方式自定义一些全局的键盘码别名
Vue.config.keyCodes.f2 = 113v-bind修饰符
v-bind修饰符主要是为属性进行操作,用来分别有如下:
- async:能对props进行一个双向绑定
//父组件 <comp :myMessage.sync="bar"></comp> //子组件 this.$emit('update:myMessage',params);使用async需要注意以下两点:
- 使用sync的时候,子组件传递的事件名格式必须为update:value,其中value必须与子组件中props中声明的名称完全一致
- 注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用
- 将 v-bind.sync 用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”,是无法正常工作的
- prop:设置自定义标签属性,避免暴露数据,防止污染HTML结构
<input id="uid" title="title1" value="1" :index.prop="index">- camel:将命名变为驼峰命名法,如将view-Box属性名转换为 viewBox
-
-
应用场景
根据每一个修饰符的功能,我们可以得到以下修饰符的应用场景:
.stop:阻止事件冒泡
.native:绑定原生事件
.once:事件只执行一次
.self :将事件绑定在自身身上,相当于阻止事件冒泡
.prevent:阻止默认事件
.caption:用于事件捕获
.once:只触发一次
.keyCode:监听特定键盘按下
.right:右键
1.30.Webpack怎么理解 具体解决了 什么问题?
Webpack 最初的目标是实现前端项目的模块化,旨在更高效地管理和维护项目中的每一个资源,是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)
模块化: 最早的时候,我们会通过文件划分的形式实现模块化,也就是将每个功能及其相关状态数据各自单独放到不同的JS 文件中
约定每个文件是一个独立的模块,然后再将这些js文件引入到页面,一个script标签对应一个模块,然后调用模块化的成员
减小代码包体积, 让浏览器更快速打开网页
1.31.三次握手 ,四次挥手
TCP三次握手:可靠的传输协议 2.1 浏览器->服务器 :在吗?能听到我说话吗?(浏览器发送 ok) 2.2 服务器->浏览器 :我在,听到了 你能听到我说话吗?(服务器接收ok 发送 ok) 2.1 浏览器->服务器 :我也听到你说话了,咱们开始吧(浏览器发送 ok)
TCP 四次挥手。
1.TCP客户端发送一个FIN,用来关闭客户到服务器的数据传送。 (浏览器发起通知,请求已经发送完毕,你准备关闭吧)
2.服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。 (服务器发起通知,请求已经已经接收完毕,你准备关闭吧)
3.服务器关闭客户端的连接,发送一个FIN给客户端。 (服务器发起通知,响应报文已经发送完毕,你准备关闭吧)
4.客户端发回ACK报文确认,并将确认序号设置为收到序号加1。 (浏览器发起通知,我准备关闭了,你也准备关闭吧)
1.32.小程序 登录和 支付流程 。
登录
支付流程
1.33.mvvm的理解
MVC
-
前端的MVC 与后端类似,具备着 View、Controller 和 Model。
- Model:负责保存应用数据,与后端数据进行同步。
- Controller:负责业务逻辑,根据用户行为对 Model 数据进行修改。
- View:负责视图展示,将 Model 中的数据可视化出来。
三者形成了一个如图所示的模型:
MVVM
ViewModel 通过实现一套数据响应式机制自动响应Model中数据变化; 同时 Viewmodel 会实现一套更新策略自动将数据变化转换为视图更新; 通过事件监听响应View中用户交互修改 Model 中数据。 这样在 ViewModel 中就减少了大量DOM操作代码。 MVVM 在保持 View 和 Model 松耦合的同时,还减少了维护它们关系的代码,使用户专注于业务逻辑,兼顾开发效率和可维护性。
1.34.路由的传参有几种方式
1、通过params方式传参
通过$route.push的path携带参数方式 (路由配置中指定参数)
这种方式参数是以/id跟在url后,刷新页面后参数不会丢失。
通过$route.push的params传参 (路由配置中未指定参数)
这种方式的传参,必须使用name进行跳转,未在路由配置:id,url后不会显示id,刷新页面后 参数会丢失。
2、通过query方式传参
这种方式的参数以?id跟在url后,类似get传参,并且,query必须使用path进行传参。刷新页面后参数不会丢失。
1.35.组件的缓存用过哪些
keep-alive
06 项目中常见的问题
1* 、后台管理系统中的权限管理是怎么实现的?*
登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token,
拿到token之后(我会将这个token存贮到cookie中,保证刷新页面后能记住用户登录状态),前端会
根据token再去拉取一个 user_info 的接口来获取用户的详细信息(如用户权限,用户名等等信息)。
权限验证:通过token获取用户对应的 权限,动态根据用户的 权限算出其对应有权限的路由,通过
router.addRoutes 动态挂载这些路由
\