目录
1、HTML、XHTML、XML 有什么区别?⭐
2、 XML和JSON的区别?
3、是否了解W3C的规范?⭐
4、什么是语义化标签?⭐⭐
5、常用的块级元素和行内元素有哪一些? ⭐
6、行内元素和块级元素的区别?⭐
7、css盒子模型有几种类型?它们区别是什么 ⭐
8、标签上title与alt属性有什么区别?
9、 H5新特性有哪些?⭐⭐
10、css3的新特性有哪些?⭐⭐
11、css的引用有哪些,link和@import的区别?
12、href和src的区别?⭐
13、CSS常用尺寸单位有哪些?应用场景?
14、移动端适配方案有哪些?⭐
15、什么是浮动?
16、清除浮动有哪些方法? ⭐
17、css选择器有哪些?⭐
18、CSS 样式覆盖规则?⭐
19、CSS 样式的优先级?⭐⭐
20、display: none 和 visibily: hidden 区别?
21、相对定位,绝对定位,固定定位的区别?
22、说几个未知宽高元素水平垂直居中方法?
二、JS 篇
1、JS数据类型有哪些?区别?⭐⭐⭐
2、JS中检测数据类型的有哪些?⭐⭐
3、JS中的栈和堆是什么?优缺点?⭐⭐⭐
4、深克隆和浅克隆?⭐⭐⭐
5、JS垃圾回收机制?⭐
6、JS哪些操作会造成内存泄露?⭐⭐
7、闭包?⭐⭐
8、什么是原型链?⭐⭐⭐
9、JS继承的方法有哪些?优缺点?⭐
10、new操作符具体都干了什么?⭐⭐
11、JS的几种具体异常类型(报错)
12、什么是事件冒泡?什么是事件委托?
13、事件对象?
14、undefined 和 null 区别?⭐
15、说一说伪数组和数组的区别?
16、对于数组去重都有哪些方法?⭐⭐
17、 对join、push、split、splic、slice的理解?
18、说一下this指向?⭐⭐
19、 js中call、apply、bind有什么区别?⭐⭐
20、箭头函数和普通函数有什么区别?⭐⭐
21、JQ对象和DOM元素之间如何转换?
22、JS模块化有哪些?
23、如何操作DOM元素?
24、防抖与节流的区别,并分别用代码表示
24、数组迭代的方法有哪些?
25、for循环和forEach有什么区别?
26、使用JQ和vue的区别?
三、ES6 篇
1、ES6新增的内容有哪些?⭐⭐⭐
2、ES6中,Set和Map的区别?⭐⭐⭐
3、map和forEach的区别?
4、es6 中的箭头函数?⭐⭐⭐
5、什么是扩展运算符,用于什么场景?⭐⭐
6、JS变量提升?⭐⭐
7、怎么实现Module模块化?
8、同步和异步的区别?同步函数和异步函数的区别?⭐
9、JS的执行顺序?同步任务和异步任务?⭐⭐⭐
10、 promise和async await区别?⭐⭐⭐
四、TS 篇
五、VUE 篇
1、写出vue的常用指令
2、v-if 和 v-show 的区别
3、v-if 和 v-for 那个优先级更高
4、v-if 和 v-for 可以一起使用吗?
5、常用的事件修饰符有哪一些(v-on 指令)
6、什么是虚拟 DOM
7、虚拟 DOM 是怎么更新数据的
8、 虚拟 DOM 是怎么生成的
9、Diff 算法总结
10、真实 DOM 的优缺点
11、虚拟 DOM 的优缺点
12、v-for 为什么加 key?不加 key 会怎么样?
13、v-for 写在 template 上,不能加 key 怎么办?
14、vue2 生命周期?⭐⭐⭐
15、vue3 生命周期
16、小程序生命周期
17、vue 里边如何让 css 样式只在当前组件中生效?
18、vue 的 watch 和 computed 有什么区别?他们的应用场景?
19、$nextTick 是什么?有什么作用?什么时候用?
20、哪个阶段无法获取 DOM 节点?怎么处理?
21、vue 中使用 refs 获取组件的 dom 元素报 undefined 如何解决?
22、多种数据修改后页面不刷新?怎么解决?⭐⭐⭐
23、Vue.extend 和 Vue.component 的区别?
24、vue 是什么?说一下对 vue 的理解?⭐⭐⭐⭐
25、什么是渐进式?⭐⭐⭐
26、vue 是如何实现双向数据绑定? (原理与缺陷)⭐⭐⭐
27、什么是订阅发布模式?⭐
28、vue2 和 vue3 有什么区别?⭐⭐⭐
29、什么是 mvvm? mvc 是什么?区别?原理?⭐⭐⭐
30、react 和 vue 有哪些不同,说说你对这两个框架的看法
31、说一下 vue 的服务端渲染 SSR?⭐⭐
32、什么是 “状态管理”?⭐
33、vuex 实现原理?⭐
34、vuex是什么?怎么用?优点和缺点?什么时候用?⭐⭐⭐
35、vuex 刷新页面后数据丢失如何解决⭐⭐⭐
36、什么是路由懒加载?为什么要路由懒加载?如何实现?
37、怎么定义 vue-router 的动态路由?怎么获取传过来的值?
38、vue 路由钩子函数 (路由守卫)
39、小程序路由跳转有哪些方式?
40、vue 里面 $route 和 $router 的区别?
41、组件开发有什么好处?为什么要封装组件? ⭐
42、什么情况下需要封装组件?
43、组件封装流程?怎么封装组件的? ⭐
44、vue 的组件通信有哪些方法 ⭐
45、$eventBus 如何实现跨页面传数据?
46、为什么组件的 data 必须是一个函数?
47、keep-alive 的作用是什么?⭐
六、uni-app 篇
1、uniapp 打包过程(安卓 Android)⭐
2、uniapp 打包过程(苹果 ISO)⭐
3、uniapp 小程序打包过程 ⭐
4、uniapp 的基本配置?
5、uniapp 上传文件时用到的 api 是什么? 格式是什么?
6、uniapp 获取地理位置的API 是什么?
7、rpx、px、em、rem、%、vh、vw 的区别是什么?⭐
8、uniapp 如何监听页面滚动?
9、如何让图片宽度不变,高度自动变化,保持原图宽高比不变?
10、uni-app 的优缺点?
11、分别写出 jQuery、vue、小程序、uni-app 中的本地存储
12、JQ、VUE、uni-app、小程序的页面传参方式? ⭐
13、vue、微信小程序、uni-app 绑定变量属性区别?
14、uni-app 的生命周期?
15、小程序组件传参有哪些方式
七、HTTP 请求篇
1、浏览器输入url后都经历了什么?⭐⭐⭐
2、HashRouter 和 HistoryRouter 的区别?⭐⭐⭐
3、cookie、sessionStorage、localStorage的区别?⭐⭐⭐
4、如何实现可过期的 localstorage 数据?
5、HTTP 中请求行、请求头、请求体有什么作用?
6、Token 能放在 cookie 中吗?
7、Token 认证流程?
8、什么是同源策略?为什么要有同源策略?⭐⭐
9、XSS攻击是什么?
10、CSRF攻击是什么?
11、什么是跨域?为什么有跨域问题?⭐⭐⭐
12、跨域的解决方案有哪几种?⭐⭐⭐
13、什么是浏览器内核?有什么用?有哪一些?
14、什么是浏览器兼容问题?
15、优雅降级和渐进增强(浏览器兼容问题)
16、http 和 https 有何区别?
17、常见的HTTP状态码?
18、说一说前端性能优化手段?⭐⭐⭐
19、网站性能优化的好处?怎么优化?
20、axios的拦截器原理及应用?
21、创建 ajax 过程?
22、说一下 fetch 请求方式?
23、说一下浏览器如何渲染页面的?
24、说一下有什么方法可以保持前后端实时通信?
八、git 篇
1、git 是什么?⭐
2、git 常用的命令?⭐
3、git 和 svn 的区别?⭐⭐
4、Git项目如何配置,如何上传至GitHub?
5、你们公司git分支是怎么管理的?⭐⭐
6、git 工作流⭐
6、git版本冲突是什么?⭐
7、如何解决git版本冲突?⭐
九、功能的实现
1、vuex 实现购物车功能
十、手撕代码
十一、机试
十二、其他
1、自我介绍
2、说一下你是怎么样优化代码的
3、你是如何优化项目的
4、开发中都使用了哪些工具
5、开发中遇到过什么bug
6、你对加班的看法
7、为什么离开上一家公司
8、你是如何学习的
1、HTML、XHTML、XML 有什么区别?⭐
XML: XML 是可扩展标记语言 ,主要是用来存储和传输数据,而非显示数据,可以用来标记数据,定义数据类型,允许用户对自己的标记语言进行定义。
HTML: HTML 是超文本标记语言 ,主要是用来描述网页的一种标记语言,通过标记标签来描述网页。
XHTML: XHTML 是可扩展超文本标记语言 ,XHTML 基于 XML 和 HTML 而来,也是用来描述网页的标记语言,是更严格的 HTML 版本。例如:XHTML 元素必须被正确地嵌套,标签名必须用小写字母, 文档必须拥有根元素,对于图片需添加 alt 属性等。XHTML 和 HTML 4.01 几乎是相同的,XHTML 是 W3C 标准。
XML和HTML区别: XML 相比 HTML 语法要求严格。HTML 是预定义的,XML 标签是免费的、自定义的、可扩展的。HTML 的设计目的是显示数据并集中于数据外观,XML 的设计目的是描述数据、存放数据并集中于数据的内容。XML 是一种跨平台的,数据处理和传输的工具。总的来说,XML用来传输数据,而 HTML 和 XHTML 用来描述网页,XHTML 比 HTML 更为严格。
2、 XML和JSON的区别?
JSON: JSON 是一种轻量级的数据交换格式,它基于 JavaScript 的一个子集,简单的说 JSON 就是一串字符串用于不同平台的数据交换。
XML: XML 是可扩展标记语言,是标准通用标记语言 (SGML) 的子集,XML 主要是用来存储和传输数据,而非显示数据,可以用来标记数据、定义数据类型,允许用户对自己的标记语言进行定义。
JSON和XML区别: JSON 数据的体积小,传递速度更快,与 JavaScript 数据交互更加方便,更容易解析处理。XML 对数据的描述性比较好。JSON 支持数组,XML 不支持数组。JSON 不支持命名空间,XML 支持命名空间。JSON 容易阅读,XML 难以阅读和解释。JSON 不使用结束标记,XML 有开始和结束标签。JSON 的安全性较低,不支持注释,仅支持 UTF-8 编码。XML 比 JSON 更安全,支持注释,支持各种编码。
3、是否了解W3C的规范?⭐
w3c 标准指 的是万维网联盟标准,万维网联盟标准指的不是一个标准,而是一系列标准的集合。web 可以简单分为结构、表现、行为三部分,三部分独立开来使其模块化,w3c 是对 web 做出规范,使代码更加严谨,做出来的网页更加容易使用和维护。
结构标准 主要包括 XHTML 和 XML ,比如像标签闭合、标签元素和属性名字小写、标签不乱嵌套、属性必须有属性值、属性值必须用引号括起来、特殊符号用编码表示、定义语言编码等。标签规范可以提高搜索引擎对页面的抓取效率,对 SEO (搜索引擎优化) 很有帮助,越规范的网站搜索排名越靠前。
表现标准 主要包括 CSS,行为标准 主要包括对象模型(像 W3C DOM,ECMAScript),比如说尽量使用外链的 css 和 js 脚本,提高页面的渲染效率,尽量少使用行内样式,类名要做到见名知意。遵循 w3c 标准可以让我们的页面,我们的程序能够支持所有浏览器,能够满足尽可能多的用户。
W3C 标准的体现 ,也就是说是开发者在开发过程中怎么去准守 W3C 标准,其实这里面很多规范是为了 XHTML 的,jQurry 不符合 W3C 标准。
4、什么是语义化标签?⭐⭐
语义化标签就是标签语义化,让标签有自己的含义,使浏览器和搜索引擎能直观的认识标签的用途和内容。虽然可以采用 DIV + CSS 的方式布局页面,但 DIV 标签本身没有特殊含义,文档结构不够清晰,不利于浏览器对页面的读取,在分离 CSS 样式后,体验不友好。使用语义化标签可以使代码结构清晰,可读性高,便于团队开放和维护。在页面没有加载 CSS 的情况下也能呈现良好的结构,易于阅读。有利于SEO(搜索引擎优化)。
5、常用的块级元素和行内元素有哪一些? ⭐
6、行内元素和块级元素的区别?⭐
行内元素: 默认不换行,设置宽高无效 (默认宽度是本身内容宽度),不能包含块级元素,只能包含文本或者其它行内元素,设置 margin,padding 上下无效。
块级元素: 默认换行,独占一行,可设置宽高 (宽度是父容器的100%),块级元素可以嵌套任意元素,块级文字不能放入其他块级元素。
行内块元素: 综合块级元素与行内元素的特性,可设宽高(默认是内容宽高),也可以设置内外边距。
转换: display:block,display:inline,display:inline-block。
7、css盒子模型有几种类型?它们区别是什么 ⭐
根据盒子大小的计算方式不同,css 盒子模型分成了两种类型,分别是 W3C 标准盒子模型和怪异盒子模型也叫 IE 盒子模型。
标准盒子模型内容的宽度等于设置的宽度,盒子的宽度 = 内容的宽度 + padding*2 + margin*2 + border*2。
IE盒子模型内容宽度 = 设置的宽度 - padding*2 - margin*2 - border*2,盒子宽度等于设置的宽度。
默认情况下都是标准盒子模型,设置 IE 盒子模型:box-sizing:border-box,设置标准模型:box-sizing:content-box
8、标签上title与alt属性有什么区别?
alt 是给搜索引擎识别,在图像无法显示时的替代文本。
title 是元素的注释信息,主要是给用户解读。
当鼠标放到文字或是图片上时有 title 文字显示。在 IE 浏览器中 alt 起到了 title 的作用,变成文字提示。
9、 H5新特性有哪些?⭐⭐
语义化标签
定义文档的头部区域
定义导航链接
内容标签
定义文档某个区域
侧边栏标签
定义了文档的尾部区域
规定独立的流内容(图像、图表、照片、代码等)
定义 元素的标题
增强表单功能 (类型和属性)
音频视频: 、
绘图 (画布):
SVG 绘图
地理位置
拖拽 APL
WebStorage (本地存储:LocalStorage / SessionStorage )
10、css3的新特性有哪些?⭐⭐
选择器:层级选择器,属性选择器,状态伪类选择器,结构伪类选择器,伪元素选择器
文本效果:文本阴影 ,文本自动换行,文本溢出,(单词拆分,文本拆分)
边框:圆角边框 border-radius,边框阴影 box-shadow,边框图片 border-image
背景:渐变背景,多重背景 (设定背景图像的尺寸,指定背景图像的位置区域,背景的绘制)
透明度:opacity ( 取值0-1,通常用于做元素的遮罩效果)
高斯模糊:filter
渐变:background: linear-gradient (线性渐变,径向渐变 ,文字渐变)
过渡:transition
2D转换 / 3D转换: transform
动画:Animation (@keyframes 动画帧)
媒体查询:@media
多列布局 (兼容性不好,还不够成熟)
弹性布局 (flex)
网格布局
11、css的引用有哪些,link和@import的区别?
css 的引用有哪些:
内联方式(直接在 html 标签中的 style 样式)
嵌入方式(在 < style > 标签下书写 css 代码)
链接方式(使用 link 引入外部的 css 文件)
link 和 @import 的区别:
link 和 import 写法不同,link 通过 标签的 href 属性引入,import 通过 @import url() 引入。
link 是 XHTML 标签,还可以定义其他事务,@import 属于 CSS 范畴,只能加载 CSS。
link 无兼容问题,@import 兼容 IE5 以上。
link 支持使用 Javascript 控制 DOM 去改变样式,@import 不支持改变样式。
link 是连接整个 css 文件,@import 可以模块化引入 css 文件。
link 引用 CSS 时,在页面加载时同时加载 css,而 @import 会把 css 文件放在页面的最底部,导致 css 最后才加载完毕,等到页面完全加载才加载 css,导致页面留白时间长,影响用户体验。
12、href和src的区别?⭐
请求资源不同: href 超文本引用,用来建立当前元素和文档之间的连接,常用的是link、a 标签。src 会将指向的资源下载并引用到当前文档中,常用的标签有 script,img,iframe 标签。
作用结果不同: href 用于在当前文档和引用资源之间确立联系,src 用于替换当前内容。
浏览器解析方式不同: herf 引用的资源时,浏览器会将其识别为 CSS 文档,并行下载资源并且不会停止对当前文档的处理。当浏览器解析到 src 时,会暂停其他资源的下载和处理,直接将该资源下载,编译,执行完毕。
13、CSS常用尺寸单位有哪些?应用场景?
px: 像素,相对长度单位,它的大小取决于屏幕的分辨率,是一个固定值,不能够自适应。
em: 相对长度的单位,相对于当前对象内文本的字体尺寸,未设置则默认是浏览器默认字体尺寸。
rem: CSS3 中新增的一个相对长度单位,相对于根元素 的 font-size 字体大小,根元素字体大小未设置,使用浏览器默认字体大小。
vw: 相对于视口的宽度。视口被均分为100单位的 vw。
vh: 相对视口高度,视口被均分为100单位的 vh。
vmin: 相对于视口宽度或高度中较小的那个。其中最小的那个被均分为100单位的 vmin。
vmax: 相对于视口宽度或高度中较大的那个。其中最大的那个被均分为100单位的 vmax。
cm: 厘米,绝对长度单位。
mm: 毫米,绝对长度单位。
in: 英寸,绝对长度单位。
%: 百法比
应用场景:
在移动端网页开发中,页面要做成响应式的,可使用 rem 配合媒体查询实现。原理: 通过媒体查询,能够在屏幕尺寸发生改变时,重置 html 根元素的字体大小,页面中的元素都是使用rem 为单位设置的尺寸,因此只要改变根元素字体大小,页面中的其他元素的尺寸就自动跟着修改。
利用 vw 和 rem 实现响应式。原理: 由于 vw 被更多浏览器兼容之后,在做移动端响应式页面时,通常使用 vw 配合 rem。原理是使用 vw 设置根元素 html 字体的大小,当窗口大小发生改变,vw 代表的尺寸随着修改,无需加入媒体查询,页面中的其他元素仍使用 rem 为单位,就可实现响应式。
14、移动端适配方案有哪些?⭐
vw、rem、em、rpx、%
15、什么是浮动?
设置浮动的图片,可以实现文字环绕图片。设置了浮动的块级元素可以排列在同一行。设置了浮动的行内元素可以设置宽高。浮动造成的影响:使盒子脱离文档流,如果父级盒子没有设置高度,需要被子盒子撑开,那么这时候父级盒子的高度就塌陷了,同时也会造成后面的盒子布局受到影响。
16、清除浮动有哪些方法? ⭐
清除浮动,主要是为了解决父级元素因为子级浮动引起的内部高度为 0 的问题
父级div定义height (只适合高度固定的布局)
结尾处加空div标签然后添加一个clear:both样式 (浮动多的话要加很多个div)
父级 div 定义 overflow:hidden 超出盒子部分会被隐藏 (不推荐)
父级div定义伪类:(推荐) clearfix:after
{
content:"";
display:block;
visibility:hidden;
height:0;
line-height:0;
clear:both;
}
17、css选择器有哪些?⭐
基本选择器:
标签选择器 ( div { } )
id 选择器 ( id=”a”, #a { } )
类选择器 class (class=”b”, .b { } )
通配符选择器 ( * { } )
CSS3新增选择器:
层级选择器:
后代选择器
ul li { } : 选择子类元素,包括间接子类
子代选择器
ul>li { } :选择子类元素,只包括直接子类
相邻兄弟选择器
div+p { } : 选择紧跟在 div 元素后面的 p 元素
通用兄弟选择器
div~p { } : 选择 div 元素后面的所有 p 元素
共享选择器
div1,div2 { } :选择所有元素
状态伪类选择器
结构伪类选择器
表单伪类选择器
伪元素选择器
属性选择器
18、CSS 样式覆盖规则?⭐
规则一:由于继承而发生样式冲突时,最近祖先获胜。
规则二:继承的样式和直接指定的样式冲突时,直接指定的样式获胜。
规则三:直接指定的样式发生冲突时,样式权值高者获胜。
规则四:样式权值相同时,后者获胜。
规则五:!important 的样式属性不被覆盖。
19、CSS 样式的优先级?⭐⭐
引入方式: 内联样式的优先级高于嵌入和外链,嵌入和外链的选择器相同就看他们在页面的插入顺序,后面插入的会覆盖前面的。
选择器优先级: id 选择器高于 ( 类选择器/伪类选择器/属性选择器 ) 高于 ( 标签选择器/后代选择器/伪元素选择器 ) 高于 ( 子选择器/相邻选择器 ) 高于通配符 *。
继承样式: 继承样式是所有样式中优先级比较低的,浏览器默认样式优先级最低。
!important: !important 最高权重,无论引入方式是什么,选择器是什么,它的优先级都是最高的。所以 !important 使用要谨慎,一定要优先考虑使用样式优先级的规则来解决问题而不是 !important 。只有在需要覆盖全站或外部 CSS 的特定页面中使用 !important。永远不要在插件中使用 !important ,永远不要在全站范围的 CSS 代码中使用 !important.
!important >> 内联样式 >> id 选择器 >> 类选择器/伪类选择器/属性选择器 >> 标签选择器/后代选择器/伪元素选择器 >> 子选择器/相邻选择器 >> 通配符 * >> 继承样式 >> 浏览器默认样式
20、display: none 和 visibily: hidden 区别?
display:none: 隐藏对应的元素,整个元素消失不占空间。
visibily:hidden: 隐藏对应的元素,元素还会占用空间。
disapaly 还可以转换元素类型,可以转换成块级元素、行内元素,行内块元素、弹性布局、网格布局等。visibility 只能做隐藏。
21、相对定位,绝对定位,固定定位的区别?
position:relative: 相对定位,相对于自己进行定位。
position:absolute: 绝对定位,相对于有相对定位的父级元素进行定位,没有就找 body。
position:fixed: 固定定位,相对于浏览器定位。
22、说几个未知宽高元素水平垂直居中方法?
绝对定位: 通过 left,top 和 transform 属性实现水平垂直居中,其中 translate 属性取值为负数时表示向左和向下移动。这种方式兼容性好,被广泛使用的一种方式。
弹性布局: 设置父级为弹性盒子:display:flex ,设置水平和垂直居中:justify-content:center、align-items:center。 这种方式代码简洁,但是兼容性 ie11 以上支持。
网格布局: 设置父级为网格元素:display: table-cell, 设置水平和垂直居中:justify-content:center、align-items:center。 这种方式代码简洁,但是兼容性 ie10 以上支持
表格布局 : 设置父级为表格元素:display: table-cell ,内部元素水平和垂直居中:text-align: center、vertical-align: middle ,设置子元素为行内块:display: inline-block。 兼容性好。
二、JS 篇
1、JS数据类型有哪些?区别?⭐⭐⭐
JS 的数据类型分为两类,分别是基本数据类型和引用数据类型。它们主要区别是在内存中的存储方式不同。
基本数据类型: number 数字、string 字符串、boolean 布尔值、null 空值、undefined 未定义、symbol 唯一值、BigInt 最大值。基本数据类型有固定的大小和值,存放在栈中,可以直接访问,而引用数据类型不确定大小,但是其引用地址是固定的,因此,它的地址存在栈中,指向存储在堆中的对象。
引用数据类型: Object (包括普通对象,数组,正则,日期,Math 数学函数等)。引用数据类型是存放在堆中的对象,在栈中保存的是对象在堆中的引用地址(引用变量),通过引用地址可以快速查找到保存在堆中的对象。
Symbol 是 Es6 新出的一种数据类型,这种数据类型的特点就是没有重复的数据,可以做 object 的 key。
BigInt 也是 ES6 新出的一种数据类型,BigInt 可以表示任意大的整数,能够解决解精度缺失的问题 (超过 Number 类型支持范围的数值都会失去精度)。使用方法: (1) 整数末尾直接加n:647326483767797n。(2) 调用 BigInt() 构造函数:BigInt("647326483767797")。
2、JS中检测数据类型的有哪些?⭐⭐
typeof: 常用于判断基本数据类型,除了 null 检测为 object。对于引用数据类型除了 function 返回 function,其余全部返回 object。
instanceof: 主要用于检测引用数据类型,不适合用来检测基本数据类型。如果检测的类型在当前实例的原型链上,则返回 true,说明这个实例属于这个类型,否则返回 false。例如: A instanceof B, 判断 B 在不在 A 的原型链上,如果在就返回 true,如果找到原型链的尽头 null 都没找到,就返回 false。(由于原型链的指向可以随意改动,导致检测不准确)
constructor: 获取实例的构造函数判断和某个类型是否相同,如果相同就说明该数据是符合那个数据类型的。使用方法是:"实例.constructor"。constructor 可以检测出除了 undefined 和 null 以外的其他类型,因为 undefined 和 null 没有原生构造函数。(不可靠,容易被修改)
object.prototype.toString.call( ): 适用于所有类型的判断检测,检测方法是: Object.prototype.toString.call(数据) ,返回的是该数据类型的字符串。
3、JS中的栈和堆是什么?优缺点?⭐⭐⭐
JS 的变量都储存到内存中,内存中开辟了两个区域储存变量,分别是栈区域和堆区域。 栈与堆实际上是操作系统对进程占用的内存空间的两种管理方式。
栈:
栈是一种先进后出的数据解构,由操作系统自动分配内存空间,自动释放,占固定的大小空间。
栈存储的是基本数据类型的值以及引用数据类型的引用地址。
栈中存储的数据的生命周期随着当前环境的执行完成而结束。
堆:
堆由操作系统动态分配内存空间,大小不定也不会自动释放,一般由程序员分配释放也可由垃圾回收机制回收。
栈存储的是对象和复杂数据结构,存储的是对象的实际数据,而不是对象的引用。
引用数据类型只有在引用的它的变量不在时,被垃圾回收机制回收。
栈和堆的优缺点:
栈相对于堆存取速度更快,且栈内存中数据是可以共享的,但内存空间有限。
堆存取效率相对较低,但内存空间大。
栈内存可以及时得到回收,相对来说更容易管理内存空间,但存储在栈中的数据大小和生存期必须是确定的,缺乏灵活性。
堆的内存是操作系统动态分配的,方便存储和开辟内存空间。有垃圾回收机制,生存周期比较灵活。
4、深克隆和浅克隆?⭐⭐⭐
浅克隆:
克隆对象的第一层属性。
如果是基本数据类型,直接将存储在栈中的值赋值给对应的变量,原始值改变不会影响。
如果是引用数据类型,则克隆的是对象的引用地址,改变引用地址,新对象也会跟着改变,想要改变这种继承的现象就要使用深度克隆。
在 JS 中可以通过 Object.assign( ) 或者扩展运算符 ... 合并对象实现浅克隆。
深克隆:
克隆对象各个层级的属性。
深克隆是指创建一个与原对象完全相同的新对象 (数据源不同,数据地址已变化)。
可以通过递归的方式实现深克隆,也可以通过简单粗暴的方式实现 JSON.parse (JSON.stringify(obj))。但需要注意: 时间对象会变成字符串的形式。RegExp、Error 对象序列化的结果将只得到空对象。函数、undefined 序列化的结果会把函数或 undefined 丢失。NaN、Infinity 序列化的结果会变成 null。如果对象中存在循环引用的情况也无法正确实现深拷贝。JSON.stringify() 只能序列化对象的可枚举的自有属性。
5、JS垃圾回收机制?⭐
内存泄漏: JS 代码运行时,需要分配内存空间存储变量和值,当这些变量不再作用时,需要释放内存,如果没有及时释放,就会引起内存泄漏,堆积起来会影响性能甚至造成系统崩溃。垃圾回收机制就是为了防止内存泄漏,及时释放不再使用的内存,提高程序性能。
垃圾回收机制: 垃圾回收机制是一种自动管理内存的机制,它会自动监测和回收不再使用的对象,从而释放内存空间。实现的原理主要有标记清除、引用计数、复制算法。
标记清除算法: 垃圾回收器会定期扫描内存中的对象,标记那些可达对象和不可达对象。可达对象指的是正在被使用的对象。不可达对象指的是不再被引用的对象。垃圾回收器会将不可达对象标记为垃圾对象,并将他们从内存中清除。该算法的优点是可以处理循环引用的情况,缺点是由于垃圾回收器的工作需要消耗一定的系统资源,因此如果程序中存在大量的内存占用或者存在频繁创建和销毁对象的操作,执行垃圾回收操作的时间会比较长,对性能造成一定的影响。
引用计数算法: 垃圾回收器会记录每个对象被引用的次数,当对象被引用的次数为0时,该对象就会被清除。该算法的优点是实现较为简单,但无法处理循环引用的情况,即两个对象相互引用,但是它们的引用次数都不为 0,导致内存无法被释放,可能会导致内存泄漏。
复制算法: 将内存空间划分为两个相等的区域,每次只使用一个区域,这个区域满时,将其中存活的对象复制到另外一个区域,再将原区域的对象全部清除。优点是可以避免由于产生大量内存碎片而引发的内存分配失败问题。
存在问题?怎么优化?
虽然浏览器可以自动的进行垃圾回收,但是当代码比较复杂时,垃圾回收对性能的消耗比较大,所以应该尽量减少垃圾回收。需要根据具体应用的需求和环境进行优化。
对数组进行优化。清空数组时,赋值为[ ],同时将数组的长度设置为0。
对象复用,尽量减少对象的创建和销毁次数。
不再使用的对象就设置为 null。
函数优化,函数功能单一化。
尽早释放资源。
使用闭包,在闭包中的变量不会被垃圾回收机制回收。
6、JS哪些操作会造成内存泄露?⭐⭐
意外的全局变量。由于使用未声明的变量而意外的创建了一个全局变量,而使用这个变量一直留在内存中无法被回收。
没有清理的DOM元素引用。获取一个DOM元素的引用,元素被删除了,由于保留了元素的引用,所以也无法被回收。
被遗忘的定时器或者回调函数。
闭包。
7、闭包? ⭐⭐
什么是闭包?
因为作用域链的存在,函数的内部可以直接读取全局变量,而函数内部无法读取另一个函数内部的局部变量,如果想读取函数内部的局部变量,可以通过闭包来实现。闭包就是在一个函数内部创建另外一个函数,让你可以在一个内层函数中访问到外层函数的局部变量。简单来说,闭包就是可以读取其他函数内部局部变量的函数,本质上,闭包是将函数内部和函数外部连接起来的桥梁。
为什么要使用闭包?
局部变量无法共享和长久的保存,而全局变量可能造成变量污染。
闭包可以读取函数内部的局部变量,且不会被垃圾回收机制回收,可以长期保存。
闭包的作用?
在函数外部可以访问函数内部的局部变量。
可以使函数内部的变量在函数执行结束之后不被销毁 ,长久保存在内存中,不会被垃圾回收机制回收。
使用闭包,可以封装自己的函数代码,实现模块化。
保护:避免命名冲突。
保存:解决循环绑定引发的索引问题。
闭包的缺点?
由于垃圾回收器不会将闭包中变量销毁,于是就造成了内存泄露,内存泄露积累多了就容易导致内存溢出。如何解决:在销毁函数之前,将不使用的局部变量全部删除。
闭包的应用?
能够模仿块级作用域。
设计模式中的单例模式。
for 循环中的保留 i 的操作。
防抖和节流。
函数柯里化。
在构造函数中定义特权方法。
Vue 中数据响应式 Observer 中使用闭包。
8、什么是原型链?⭐⭐⭐
每个函数身上都有一个 prototype 的原型对象,并且有一个__proto__的指针指向下一级原型对象,如果一个对象的属性或方法在自身中找不到,那么就会去 prototype 原型对象中查找,如果还找不到继续向上查找直到 null,当_proto_指针指向 null 时形成一个链条,这个链条叫做原型链。
在原型链中,对象可以继承原型对象的属性和方法。如果想在构造函数中添加属性和方法,可以将它们添加到构造函数的 prototype 属性中,这样通过该构造函数创建的对象都可以访问到这些属性和方法。
原型链的特点是:对象可以沿着原型链向上查找属性和方法,实现了属性和方法的共享和继承。
9、JS继承的方法有哪些?优缺点?⭐
JS继承的方法有以下几种:原型链继承、构造函数继承、组合继承、原型式继承和寄生式继承,寄生组合式继承,ES6 Class实现继承。继承的目的是:重复利用另外一个对象的属性和方法。
原型链继承: 将父类的实例作为子类的原型,从而实现对父类属性和方法的继承。优点: 写法方便简洁,容易理解。缺点: 不能传递参数和共享所有继承的属性和方法,当一个发生改变另外一个随之改变。
构造函数继承: 在子类的构造函数中调用父类的构造函数,使用 apply() 或 call() 方法将父对象的构造函数绑定在子对象上,从而实现对父类实例属性的继承。优点: 解决了原型链继承不能传参的问题和父类的原型共享的问题。缺点: 方法都在构造函数中定义,因此无法实现函数复用。
组合继承: 将原型链继承和构造函数继承结合起来,既可以实现对父类原型属性和方法的继承,又可以实现对父类实例属性的继承。优点: 解决了原型链继承和构造函数继承造成的影响。缺点: 无论在什么情况下,都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。
原型式继承: 通过创建一个临时构造函数来实现对父类的属性和方法的继承。优点: 不需要单独创建构造函数。缺点: 属性中包含的引用值始终会在相关对象间共享。
寄生式继承: 在原型式继承的基础上,通过在临时构造函数中添加方法和属性,从而实现对父类的继承。优点: 写法简单,不需要单独创建构造函数。缺点: 通过寄生式继承给对象添加函数会导致函数难以重用。
寄生组合式继承: 通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。优点: 高效率只调用一次父构造函数,并且因此避免了在子原型上面创建不必要,多余的属性。与此同时,原型链还能保持不变。缺点: 代码复杂。
ES6 Class实现继承: ES5 的继承,实质是先创造子类的实例对象 this,然后再将父类的方法添加到 this 上面 (Parent.apply(this))。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法加到 this 上面 (所以必须先调用super方法),然后再用子类的构造函数修改 this。需要注意的是,class 关键字只是原型的语法糖,JS继承仍然是基于原型实现的。 优点: 语法简单易懂,操作更方便。缺点: 并不是所有的浏览器都支持 class 关键字 lass Person。
10、new操作符具体都干了什么?⭐⭐
创建一个新对象 obj。
将该对象与构造函数通过原型链连接起来(设置该对象的构造函数)。
将构造函数中的 this 绑定到该对象上。
根据构造函数返回类型作判断,如果是值类型则返回新对象 obj,如果返回对象,则返回构造函数里的对象。
// 手写 new 操作符
function mockNew(constructor, ...args) {
// 1.创建一个新对象 obj
const obj = {};
// 2.把构造函数当参数传入,新对象指向构造函数原型对象
obj.__proto__ = constructor.prototype;
// 3.通过 apply 将构建函数的 this 指向新对象
let result = constructor.apply(obj, args);
// 4.根据返回值判断
return result instanceof Object ? result : obj;
}
11、JS的几种具体异常类型(报错)
SyntaxError:语法错误
ReferenceError:引用错误
RangeError:范围错误
ypeError:类型错误
URLError:与 url 相关参数不正确
EvalError:全局函数 eval 执行错误
12、什么是事件冒泡?什么是事件委托?
事件冒泡: 在一个对象上触发某类事件,这个事件会向这个对象的的父级传播,从里到外,直至它被处理或者到达了对象层次的最顶层,即 document 对象。这个过程就是事件冒泡。
事件委托: 事件委托就是利用事件冒泡,指定一个事件处理程序,就可以管理某一类型的所有事件。原理: 在元素的父级元素添加事件,点击元素时,因为事件冒泡的作用实现事件委托。简单来说,事件委托就是将子元素的事件通过冒泡的形式交由父元素来执行。优点: 使用事件委托可以减少代码执行优化资源。
13、事件对象?
event: 事件对象。
currentTarget:绑 定的事件。
target: 触发的事件。
eventPhase: 返回当前触发的阶段(捕获阶段1,事件派发阶段2,冒泡阶段3)
type: 返回当前event对象的事件名。
14、undefined 和 null 区别?⭐
undefind 是全局对象的一个属性,当一个变量没有被赋值或者一个函数没有返回值或者某个对象不存在某个属性却去访问或者函数定义了形参但没有传递实参,这时候都是 undefined ,undefined 通过 typeof 判断类型是 undefined。
null 代表空值,代表一个空对象指针,代表对象的值未设置,相当于一个对象没有设置指针地址就是 null。null 通过 typeof 判断类型是 object。
undefined 表示一个变量初始状态值,而 null 则表示一个变量被人为的设置为空对象,而不是原始状态。当需要释放一个对象时,直接赋值为 null 即可,对象被赋值了null 以后,对象对应的堆内存中的值就是游离状态了,GC 会择机回收该值并释放内存。因此,需要释放某个对象,就将变量设置为 null,即表示该对象已经被清空,目前无效状态。
null 是 javascript 的关键字,和其它语言一样都是代表空值 , undefined 却是 javascript 才有的。是为了区分空指针对象和未初始化的变量,它是一个预定义的全局变量。
15、说一说伪数组和数组的区别?
伪数组它的类型不是 Array,而是 Object,而数组类型是 Array。
伪数组可以使用的 length 属性查看长度,也可以使用 index 获取某个元素,但是不能使用数组的其他方法,也不能改变长度,遍历使用 for in 方法。
伪数组转换成真数组方法:(1)Array.prototype.slice.call(伪数组) 、(2)[].slice.call(伪数组) (3)Array.from(伪数组),转换后的数组长度由 length 属性决定,索引不连续时转换结果是连续的,会自动补位。
16、对于数组去重都有哪些方法?⭐⭐
双重 for 循环: 这是一个最笨的方法。
对象属性 key: 利用对象属性名 key 不可以重复这一特点,如果对象中不存在,就 push 进空数组。
for 循环 + indexOf: 主要是利用 indexOf 的特性,查找元素返回下标,找不到就返回-1。-1就 push 进空数组。
for 循环 + sort 排序: 利用数组的 sort 排序方法去重,如果第 i 项 和 i-1 项不一致,就 push 进空数组。
filter + indexOf: 利用 filter 过滤配合 indexOf 查找元素,判断返回元素下标和过滤数组的 index 是否相等。
Set: Es6 中新增了数据类型 Set,Set 的最大一个特点就是数据不重复,可以用作数组去重。new Set 方法,返回是一个类数组,需要结合扩展运算符...,转成真实数组。
set + Array.from: Set 去重结合 Array.from 转成真实数组。Array.from(new Set(原数组))
for 循环 + includes: includes 用来判断一个数组是否包含一个指定的值,是就返回 true,否则返回 false。判断空数组是否包含原数组的某个元素,不包含就 push 进空数组。
reduce + includes: 利用 reduce 遍历结合 includes去重。
17、 对join、push、split、splic、slice的理解?
join: 把数组变成字符串。
split: 把字符串变成数组。
push: 往数组最后一位添加成员。
splice: 增加、修改、删除数组的成员。
slice: 截取数组,然后返回一个新数组。
18、说一下this指向?⭐⭐
19、 js中call、apply、bind有什么区别?⭐⭐
原生 JS 提供了 call、apply、bind 三种方式来修改 this 指向。
call 和 bind 是选项式参数,apply 是数组式参数。
call、apply 会立即执行,bind 返回一个新函数,不会立即执行。
call、apply 临时改变 this 指向一次,bind 永久改变 this 指向。
应用场景:call 用于对象的继承 、伪数组转换成真数组。apply 用于找出数组中的最大值和最小值以及数组合并。bind 用于 vue 或者 react 框架中改变函数的 this 指向。
20、箭头函数和普通函数有什么区别?⭐⭐
普通函数的 this 指针指向调用者,可以修改。
箭头函数没有自己的 this,它的 this 是继承而来,默认指向在定义它时所处的对象 (父级作用域),不能修改。
21、JQ对象和DOM元素之间如何转换?
DOM转JQ对象:$(DOM对象) 或 $("div")。
JQ对象转DOM:可以通过[index] 或者 .get(index)方法,例如 $("div")[0] 或者 $("div").get(1)。
22、JS模块化有哪些?
commonjs、es6、amd、cmd
23、如何操作DOM元素?
原生操作DOM元素:
直接给相应的元素加 id ,然后再 document.getElementById("id") 获取。
vue操作DOM元素:
获取/操作根元素 DOM:$root。
获取/操作父元素 DOM:$parent。
获取/操作子元素 DOM: $refs $children。
使用 ref,给相应的元素加 ref=“name” 然后再 this.$refs.name 获取到该元素。
24、防抖与节流的区别,并分别用代码表示
24、数组迭代的方法有哪些?
for。
for in:可以遍历对象, 首个行参是 key。
for of:只能遍历数组,首个行参是 value:。
forEach:for的增强版,特殊简化版,不支持在循环中添加删除操作。
while :先判断后执行。
do while:先执行后判断。
some:所有返回值都为真则返回真,否则返回假。
every:反之。
25、for循环和forEach有什么区别?
for 循环的 return 是终止循环 forEach 是返回参数。
for 循环实际上是可以使用 break 和 continue 去终止循环的,但是 forEach 不行。
forEach 不支持在循环中添加删除操作。
for 多数时候都可以使用,一般我们需要知道循环次数。而 forEach 更适合于集合对象的遍历和操作。
26、使用JQ和vue的区别?
JQ 是函数库,本质上还是操控 JS,只不过更简便了。vue 是 mvvm 框架,核心是双向数据绑定,
一般情况下不需要操控 DOM 元素,而是操控数据为主。
三、ES6 篇
1、 ES6新增的内容有哪些?⭐⭐⭐
let、const、结构赋值、class 类、箭头函数、map、set、promise、async await(es7)、扩展运算符...、Module 模块化。
2、 ES6中,Set和Map的区别 ?⭐⭐⭐
set: ES6 提供新的数据结构 set,它类似于数组,但是成员的值都是唯一的,没有重复的值。可以用来做数组去重,但是 set 是伪数组,可以通过 Array.from() 或者扩展运算符...转换成数组。Set 结构没有键名,只有键值 (或者说键名和键值是同一个值)。可以使用 keys()、values()、entries()、forEach() 方法遍历成员,使用 for..of... 循环遍历 Set。如果想直接在遍历操作中改变原来的 Set 结构,可以利用原 Set 结构映射出一个新的结构,然后赋值给原来的 Set 结构,或者使用 Array.from 方法。
实例操作方法: size、add、delete、has、clear。
size:返回 set 实例的成员总数。
add(value):添加某个值,返回 set 结构本身。
delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
has(value):返回一个布尔值,表示该值是否为 set 的成员。
clear():清除所有成员,没有返回值。
Map: "键值对" 的数据结构,可以实现数组重组。JavaScript 对象 object 本质上是键值对的集合,缺陷是只能使用字符串作为键,而 map 结构优化了这个缺陷,它提供了 "值-值" 对的形式,让键名不再局限于字符串,是一种更完善的 Hash 结构实现。简单来说:Map 类似于对象,也是键值对的集合,但是 "键" 的范围不限于字符串, 各种类型的值(包括对象)都可以当作键。遍历方法:keys( )、values( )、entries( )、forEach( )、for..of...。注意:由于一个 key 只能对应一个 value,所以多次对一个 key 放入 value,后面的值会把前面的值冲掉。
实例操作方法:size、set、get、has、delete、clear。
size:返回 Map 实例的成员总数。
set:添加新的 key-value。
get:传入一个 key 返回一个 val。
delete:传入一个 key,删除这个 key-value。
has:查看是否有这个 key。
clear:清除所有成员,没有返回值。
Set 和 Map 的区别:
Map 类似于对象也是键值对的集合,但 '键' 的范围不限于字符串可以是各种数据类型。Set 类似于数组,Set 对象是值的集合。
Map 存储 key-value 键值对,是一组映射关系。Set 只存储 key,没有 value,value 就是 key。
Set 的值是唯一的、不重复的,Map 的 key 是唯一的。
Map 可以通过 get 方法获取值,而 Set 不能因为它只有值。
Set 的值是唯一的可以做数组去重,Map 由于没有格式限制,可以做数据存储。
初始化需要值不一样,Map 需要的是一个二维数组,而 Set 需要的是一维 Array 数组。
都能通过迭代器进行 for...of 遍历。
3、map和forEach的区别?
map:map 有返回值,可以开辟新空间,return 出来一个 length 和原数组一致的数组,即便数组元素是 undefined 或者是 null,map 能新增删除元素。
forEach 默认无返回值,返回结果为 undefined,可以通过在函数体内部使用索引修改数组元素,forEach 不能新增删除元素。
4、es6 中的箭头函数?⭐⭐⭐
箭头函数相当于匿名函数,简化了函数定义。
箭头函数有两种写法,当函数体是单条语句的时候可以省略 {} 和 return,另一种是包含多条语句,不可以省略 {} 和 return。
箭头函数最大的特点就是没有自己的 this,它的 this 是从外部获取,就是继承外部的执行上下文中的 this。
由于没有 this 关键字所以箭头函数也不能作为构造函数。
箭头函数也没有原型和 super。
不能使用 yield 关键字,因此箭头函数不能用作 Generator 函数。
适用场景:简单的函数表达式,内部没有 this 引用,没有递归、事件绑定、解绑定,适用于 map、filter 等方法中。
5、什么是扩展运算符,用于什么场景?⭐⭐
扩展运算符(…) 是一个展开语法,用于取出对象或数组中的所有可遍历属性或元素,然后将它们展开到当前的位置,展开的内容可以放在任何它可以存在的地方(数组,函数,对象)。
在对象中:扩展运算符可以用于创建新的对象,将多个对象合并成一个对象,或者复制一个对象。
在数组中:在函数调用时将一个数组参数转为用逗号分隔的参数序列。数组拷贝[...arr]。合并数组。扩展运算符与解构赋值结合,用于生成数组。将伪数组转为真正的数组。数组去重 [...new Set(arr)]。做为函数传递参数时不确定形参个数的时候使用。
扩展运算符可以提高代码的可读性和简洁性,减少重复代码的编写。
6、JS变量提升?⭐⭐
变量提升是指 JS 的变量和函数声明会在代码编译期,提升到代码的最前面。 变量提升成立的前提是使用 Var 关键字进行声明的变量,并且变量提升的时候只有声明被提升,赋值并不会被提升,同时函数的声明提升会比变量的提升优先。
7、怎么实现Module模块化?
模块的功能主要由 export(导出) 和 import(导入) 组成。
每一个模块都有自己单独的作用域,模块之间的相互调用关系是通过 export 来规定模块对外暴露的接口,通过 import 来引用其它模块提供的接口。
同时还为模块创造了命名空间,防止函数的命名冲突。
8、同步和异步的区别?同步函数和异步函数的区别?⭐
同步就是同时执行,异步就是分开来执行。
同步函数就是把同步变成异步执行,异步函数就是把异步变成同步执行。
场景:同步和异步的问题通常是指 ajax 的回调问题。如果是同步调用,在发起请求的时候就会暂停,等到服务器响应后继续运行。如果是异步调用,发起请求后不会暂停,立即执行后面的代码,服务器响应后会自动触发回调函数进行处理。异步的性能佳,同步则用于需要立即获取结果并实时处理的情况。
9、JS 的执行顺序?实现异步的方法 ?⭐⭐⭐
js 是单线程语言,同一个时间只能做一件事,这样很容易造成阻塞,所以把任务分成了同步任务和异步任务两种。当主线程遇到异步任务,比如说计时器或者 ajax 请求回调问题,就把异步任务放进 "任务队列" 里,执行完同步任务后,再循环的去检查任务队列里面有哪些异步任务要执行。这是一个异步执行,分开来执行的,如果想要变成同步执行,比如说等到计时器执行完后再执行或者等请求服务器响应后再继续运行,可以使用回调函数,或者使用异步函数 promise 或 async await,把异步变成同步执行。所有异步任务都是在同步任务执行结束之后,从任务队列中依次取出执行。
JS实现异步的方法:
回调函数: 回调函数是异步操作最基本的方法,优点是简单,容易理解和实现,缺点是不利于代码的阅读和维护,有回调地狱问题 (多个回调函数嵌套的情况)。
promise: 为了解决回调地狱和代码不规范问题,es6 出了个 promise ,promise 是一个异步函数,是异步编程的一种解决和优化的方案,在异步操作成功时通过状态调用 resolve 或 reject 并把结果作为参数传递出去,通过 then 接收到对应的数据,做出相应的处理。相比回调函数 promise 不仅能捕捉错误,还能解决回调地狱问题,通过多个 promise 调用 then 方法来把地狱回调转化为链式编程。缺点是错误需要通过回调函数捕获,只能异步的处理错误,Promise 链式调用相当于一个新的回调地狱。
async/await: async/await 是基于 Promise 实现的,async/await 使异步代码看起来像同步代码。优点是使用方法清晰明了,更加优雅,可读性强,可以以通过 try-catch 捕捉错误,同步的处理错误,且 async/await 完美解决了回调地狱的问题。缺点是 await 将异步代码改造成了同步代码,如果多个异步代码没有依赖性却使用了 await 会导致性能上的降低,代码没有依赖性的话,完全可以使用 Promise 去实现。
10、 promise和async await区别?⭐⭐⭐
什么是 promise ? 为什么要有 promise ?
promise 是 es6 中的一个内置对象,实际是一个构造函数,是一个异步函数,是异步编程的一种解决方案,把异步执行变成同步执行。比传统的解决方案 “回调函数” 更加合理强大,解决了 “回调函数” 中的回调地狱,代码不规范,可读性低,不便维护等问题。(通过多个 promise 调用 then 方法来把地狱回调转化为链式编程)
promise 有什么作用?
通常用来解决异步调用问题。解决多层回调嵌套的方案。提高代码可读性,更便于维护。
promise 写法?如何实现的?
可以使用 promise 构造函数 new 一个实例,promise 构造函数接收一个函数作为参数,这个函数有两个参数, 分别是成功回调 resolve 和失败回调 reject。在异步操作成功时调用 resolve,把 promise的状态从 pending 转变到 resolved,并将异步操作的结果作为参数传递出去。在异步操作失败时调用 reject,把 Promise 的状态从从 pending 变为 rejected,并将异步操作报出的错误,作为参数传递出去。Promise 实例生成后,当 promise 状态一改变,不管是成功还是失败,就都会来到 promise 对象的 .then 方法,根据其状态,选择特定的响应函数执行。then 方法返回的是一个新的 Promise 实例,因此可以采用链式写法,即 then 方法后面再调用另一个 then 方法,把回调地狱转为链式编程。如果 Promise 的状态变为 reject 时,会被 catch 捕捉到,所以就把成功的处理逻辑写在 .then()里,把失败的处理逻辑写在 .cache() 里。
什么是 async await ?
async await 是 ES7 新特性,也是一个异步函数,把异步执行变同步执行,它是基于 promise 实现的,是一个语法糖。
async await 写法?
async await 基于 promise 实现的,async 写在方法前面,用于声明一个 function 是异步的,async 和 await 是配对使用的,await 只能写在 async 的内部,await 后面跟着的是一个 promise 对象,async 执行期间一旦遇到 await,会等后面的 Promise(异步)执行完毕再执行后面的代码,如果是 reject 状态,可以使用 try-catch 捕捉
promise 和 async await 区别:
相同点: promise 和 async await 都是优化异步编程的解决方案。
不同点:
promise 是 ES6 的一个内置对象,是应用层的解决方案,通过 catch 来捕捉,只能异步的处理错误。async/await 是 ES7 的新特性,是基于 promise 的,可以说是 promise 的补充,是语言层的解决方案,可以让用户像编写同步代码一样编写异步代码,可以通过 try-catch 捕捉错误,同步的处理错误。
promise 更多应用在函数封装中,async/await 用在函数的使用中。
链式调用相当于一个新的回调Promise 地狱, 也不能统一处理异常。async/await 完美解决了回调地狱的问题。
async/await 相对于 promise 来讲,写法更加优雅。async/await 用同步的写法使得可读性更强,同时方便 try-catch 捕获异常。
四、TS 篇
五、VUE 篇
1、写出vue的常用指令
指令的本质:语法糖。在编译阶段 render 函数里,会把指令编译成 JavaScript 代码
v-for:遍历 data 中的数据,实现列表的渲染(数组或对象)
v-if:通 过 true 和 false 控制元素是否需要被渲染
v-else:搭配 v-if 使用,不需要表达式,当 vi-if 为 false 时才被渲染出来
v-show:通过 true 和 false 控制元素是否显示或隐藏
v-model:实现表单控件和数据的双向绑定( 、、
v-bind:动态绑定元素属性
v-on:简写@,事件绑定
v-text:渲染字符串,会覆盖原先的字符串
v-html:渲染 Html,{{}} 和 v-text 都是输出文本,v-html 输出 Html (XSS攻击)
v-once:只渲染元素和组件一次,当数据发生改变时,不会再变化(用于优化更新性能)
v-slot:定义一个具名插槽或作用域插槽。可缩写为 #
2、v-if 和 v-show 的区别
相同的是: 都是条件渲染指令,通过 true 和 false 控制元素的显示和隐藏。
不同的是:
v-if 当条件为 true 时, 把元素创建并渲染到 Html,为 false 时把元素删除,不会渲染到 Html 上。是一个创建和删除的过程,删除时并不会占用空间位置。
v-shou 无论是 true 还是 false 元素都被渲染出来,当条件为 false 时通过 display: none 控制元素隐藏。是一个显示和隐藏的过程,隐藏时还是会占用空间位置。
应用场景:
v-show:适合使用在切换频繁的元素上 (显示/隐藏)
v-if:适合使用在切换不频繁,且元素内容很多,渲染一次性能消耗很大的元素上
3、v-if 和 v-for 那个优先级更高
同时使用 v-if 和 v-for 是不推荐的,因为这样二者的优先级不明显
在 vue2 中 v-for 比 v-if 有更高的优先级。这意味着 v-if 将分别重复运行于每个 v-for 循环中。
在 vue3 中 v-if 比 v-for 有更高的优先级。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名。
4、v-if 和 v-for 可以一起使用吗?
可以一起使用,但不建议。在 vue2 v-for 的优先级比 v-if 的高,v-if 判断运行于每一个 v-for 循环中,一起使用会造成性能的浪费。解决办法: 可以把 v-if 放在 v-for 的外层或者把需要使用 v-for 遍历的属性先从计算属性中过滤一次。vue3 更新了 v-if 和 v-for 的优先级,使 v-if 的优先级高于 v-for。
5、常用的事件修饰符有哪一些(v-on 指令)
prevent:阻止事件的默认行为
stop:阻止冒泡
self:只监听触发该元素的事件,其他不管(currentTarget 和 target为同一个元素时候触发)
capture:阻止捕获
once:事件只触发一次
left:左键事件
right:右键事件
middle:中间滚轮事件
6、什么是虚拟 DOM
虚拟 DOM 是一种在内存中构建和操作的 JavaScript 对象树,用来描述真实 DOM 的结构信息,是为了解决浏览器性能问题引入的概念。虚拟 DOM 是相对于浏览器渲染出来的真实 DOM 而言的。
在传统的前端开发中,当数据发生改变时,我们需要直接操作真实的 DOM 来更新页面,但是这种直接操作会带来性能上的问题。因为改变 DOM 会触发浏览器的重绘和重排,频繁操作消耗了大量的计算资源。而且直接操作 DOM 几乎都要遍历整颗 DOM 树,相比之下查找 js 对象属性变化要比查询 DOM 树开销小。
虚拟 DOM 的出现解决了这个问题。当数据发生改变的时候,我们首先在内存中创建一个虚拟 DOM 树,然后与之前保存的旧的虚拟 DOM 树进行对比,然后通过 diff 算法找出差异,并只更新差异部分对应的真实 DOM。这样就避免了直接操作真实 DOM,减少了性能的开销。
虚拟 DOM 的好处是通过 diff 算法只更新需要更新的部分,减少了对真实 DOM 的操作次数,避免频繁的重构和重排,减少了性能的开销,提高了渲染效率。支持跨平台,虚拟 DOM 本质上是一个 JS 对象,而 DOM 与平台强相关。相比之下虚拟 DOM 可以在不同的平台上使用,例如浏览器、移动端和桌面应用等。简化逻辑,虚拟 DOM 提供了一个抽象层,让开发者可以更简洁的操作 DOM,提高了代码的可读性和可维护性,简化了代码逻辑。
虚拟 DOM 结构:sel 标签,date 标签属性,text 标签的文本,children 嵌套的子标签。
7、虚拟 DOM 是怎么更新数据的
虚拟 DOM 可以很好的跟踪当前 DOM 的状态,他会根据当前数据生成一个描述当前 DOM 的虚拟 DOM,然后数据改变的时候又会生成一个新的虚拟 DOM,然后通过 diff 算法计算出前后两个虚拟DOM 之间的差异,得出一个最优的更新方法。
8、 虚拟 DOM 是怎么生成的
首先代码运行会走生命周期,当生命周期走到 created 到 beforeMount 之间的时候,会编译 template 模板成 render 函数。然后在 beforeMount 和 mounted 之间执行 render 函数。当 render 函数运行时 h 函数会被调用,而 h 函数最主要的就是执行了 vnode 函数,vnode 函数主要作用就是将 h 函数传进来的参数转换成 js 对象,即生成虚拟 DOM。之后当数据发生改变时会重新编译生成一个新虚拟 DOM (vdom),然后通过 diff 算法计算出前后两个虚拟 DOM 之间的差异,得出一个最优的更新方法。从而减少了不必要的 DOM 操作,提高了页面的渲染速度和性能。
9、Diff 算法总结
调用 patch 函数比较两个虚拟 DOM 的根节点是否是相同节点。如果不同直接替换。
如果相同,则调用 patchVnode 函数比较两个节点的子级,既属性、文本和子节点。此时,要么新增,要么删除,要么替换。只有都存在子节点时,会执行 updateChildren 函数进一步比较他们的子节点。
子节点在 updateChildren 函数中进行比较更新,首先会分别添加两个指针指向子节点的第一个节点和最后一个节点,然后会进行 头头比较,头尾比较,尾头比较,尾尾比较,如果匹配上,那么会将旧节点对应的真实 DOM 移到新节点的位置上,指针向中间移动,同时匹配的两个节点会继续调用 patchVnode 函数进行进一步的对比。当指向第一个节点的指针大于指向最后一个节点指针的时候表示匹配结束,此时,多余的元素删除,新增的元素新增。如果上面这几种情况都没有出现,key 就起到了关键性作用,存在 key 时,可以直接通过 key 去找到节点的原来的位置。如果没有找到,就新增节点。找到了就移动节点位置,执行 patchVnode 函数进行进一步的对比,指针向后移。这样查找效率非常高。而如果没有 key 呢,那么压根就不会去原来的节点中查找了。而是直接新增这个节点。这就导致这个节点下的所有子节点都会被重新新增。会出现明显的性能耗。所以,合理的使用 key,也是一种性能上的优化。
总的来说,diff 的过程,就是一个执行 patch 函数,patchVnode 函数,updateChildren 函数……这样的一个循环递归的过程 (patch 只执行一次)
10、真实 DOM 的优缺点
优点:
易用
缺点:
效率低:解析速度慢,内存占用高。
性能差:频繁操作真实 DOM,容易导致重绘和回流。
11、虚拟 DOM 的优缺点
优点:
虚拟 DOM 通过 diff 算法只更新需要更新的部分,减少了对真实 DOM 的操作次数,避免频繁的重构和重排,减少了性能的开销,提高了渲染效率。(重绘和回流)
支持跨平台,虚拟 DOM 本质上是一个 JS 对象,而 DOM 与平台强相关。相比之下虚拟 DOM 可以在不同的平台上使用,例如浏览器、移动端和桌面应用等。(服务器渲染)
简化逻辑,虚拟 DOM 提供了一个抽象层,让开发者可以更简洁的操作 DOM,提高了代码的可读性和可维护性,简化了代码逻辑。
提升用户体验
缺点:
额外的内存消耗和性能开销,虚拟 DOM 需要在内存中维护一个额外的数据结构,并且在每次更新时都要进行比较和计算差异,可能会增加一定的内存消耗和性能的开销。
首次渲染时慢一些,由于虚拟 DOM 需要在内存中构建一颗 DOM 树,然后再将其转为真实的 DOM 树,所以首次渲染的耗时可能比直接操作 DOM 要长一些。
不适合所有场景,对于静态内容或较少变化的页面,使用虚拟 DOM 可能会带来不必要的开销。
提高学习成本。无法进行极致优化。
12、v-for 为什么加 key?不加 key 会怎么样?
什么是就地复用策略?
就地复用策略是 Vue 在 DOM 更新时采用的一种优化方式。在更新现有 DOM 树时,Vue 会尽可能地复用已存在的 DOM 元素,而不是删除并重新插入。这样可以减少不必要的 DOM 操作,提高性能。
Vue 在执行 DOM 更新时会进行四个步骤:
创建一个新的虚拟 DOM 树
对比新旧虚拟 DOM 树,找出差异
根据差异对现有的 DOM 树进行更新
触发 DOM 更新后的钩子函数
在第二步中,Vue 会对比新旧虚拟 DOM 树的节点,找出它们之间的差异。对于相同的节点,在更新时 Vue 会尽可能地复用已存在的 DOM 元素。这样做可以避免不必要的 DOM 操作,提高性能。就地复用策略只适用于同一层级的元素之间进行比较。如果两个元素的父元素不同,Vue 会直接删除旧元素并在新父元素中创建新元素。
key 是什么?
唯一标识。
为什么要在 v-for 上加 key?
v-for 中加上 key 是为了提高列表的渲染性能和提供更好的更新策略。
不加 key 会怎么样?key 有什么用?
性能问题:如果没有 key,vue 在更新列表时会使用一种“就地复用”策略,既复用已经存在的 DOM 元素,而不是删除重新插入。这可能会导致错误的元素被复用,导致渲染错误或者不符合预期的结果。加上 key 可以确保正确地复用和更新元素,提高渲染性能。
顺序问题:如果顺序发生改变,没有设置 key,Vue 将无法识别新旧节点的对应关系,从而导致重新渲染整个列表,而不仅仅是更新需要修改的部分。加上 key 可以帮助 vue 更好的识别新旧节点对应的关系,只更新变化的部分。
组件状态问题:如果列表中的组件包含状态,没有设置 key 会导致状态丢失或混乱。加上 key 可以确保组件在重用时保持自己的状态。
13、v-for 写在 template 上,不能加 key 怎么办?
方法一:在 template 里面套上两层 div,v-for 和 key 写在里面的 div 上面。
方法二:在下一层需要循环遍历的真实 DOM 上加一个 Key 就可以了。
14、vue2 生命周期?⭐⭐⭐
什么是钩子函数?什么是生命周期?
vue 生命周期是指 vue 对象从创建到销毁的过程。
每个生命周期都有对应的函数,我们把这些函数称为钩子函数。生命周期有11个阶段,即有11个构造函数。
vue2 的生命周期有哪些?
beforeCreate 创建前: 在实例初始化之后,数据观测(data observer) 之前被调用。 (只有一些默认的生命周期钩子和默认事件,没有 data,没有 el)
created 创建后: 在实例创建完成后同步调用。 可访问 data、computed、watch、methods 上的方法和数据,未挂载到 DOM,不能访问到 el 属性,常用于简单的 ajax 请求。如果要进行 dom 操作,那就要用 $nextTick 函数。
beforeMount 挂载前 :在挂载开始之前被调用。 在 beforeMount 之前已经将 template 模板编译成 render 函数。在 beforeMount 的时候 render 函数首次被调用。(还没有挂载到界面,有data有 el)
mounted 挂载后: 实例被挂载后调用,Vue 实例已经初始化完成了。 实例挂载到 DOM 上,此时可以通过 DOM API 获取到 DOM 节点,可进行数据请求。(el 被新创建的 vm.$el 替换》?)
beforeUpdate 更新前: 在数据发生改变后,DOM 被更新之前被调用。 适合在更新之前访问现有的 DOM,比如手动移除已添加的事件监听器 (此时页面旧数据,data 新数据,还没有同步)
updated 更新后 :在数据更改导致的虚拟 DOM 重新渲染和更新完毕之后调用。 组件 DOM 已经更新 ( 此时页面和 data 的数据已经同步,都是最新数据 )
beforeDestroy 销毁前: 实例销毁之前调用。 this 仍能获取到实例,常用于销毁定时器、解绑全局事件、销毁插件对象等操作。
destroyed 销毁后: 实例销毁之后调用。 该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。
activated 激活 : 被 keep-alive 缓存的组件激活时调用。
deactivated 取消激活: 被 keep-alive 缓存的组件失活时调用。
errorCaptured 错误捕捉: 在捕获错误时被调用。 收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上传,防止组件无限的渲染循环。
注意:mounted 和 updated 不会保证所有的子组件都被挂载或重新渲染,如果希望等到整个视图都渲染完毕再执行某些操作,可以在 mounted 或 updated 内部使用 $nextTick 函数。
父子组件生命周期执行顺序?
挂载:父created -> 子created -> 子mounted> 父mounted
更新:父beforeUpdate -> 子beforeUpdated -> 子updated -> 父亲updated
销毁:父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
vue 生命周期的作用是什么?
vue 的生命周期中有多个事件钩子,通过不同阶段对应不同的钩子函数来实现组件数据管理和 DOM渲染两大功能。
第一次页面加载会触发哪几个钩子?
beforeCreate,created,beforeMount,mounted
如果加入了 keep-alive 多两个生命周期:
activated,deactiated
如果加入了 keep-alive ,第一次进入组件会执行那些生命周期?
beforeCreate,created,beforeMount,mounted,activated
如果加入了 keep-alive,第 n 次进入组件会执行那些生命周期?
只执行 activated
创建后和渲染后有什么区别?
created 是实例创建完成后调用,只能访问 data、computed、watch、methods 上的方法和数据,未挂载 dom,还不能获取 dom 节点,如果要进行 dom 操作,那就要用 $nextTick 函数。常用于简单的 ajax 请求。不能访问到 el 属性。mounted 是实例被挂载后调用,vue 实例已经初始化完成了,已经可以获取 dom节点和操作 dom 节点了。可以访问 el 属性。
渲染后和更新后有什么区别?
updated 与 mounted 不同的是,在每一次的 DOM 结构更新 vue 都会调用一次 updated() 钩子函数!而 mounted 仅仅只执行一次而已。
简述每个周期具体适合哪些场景?
beforecreate : 可以在这加个 loading 事件,在加载实例时触发。
created : 初始化完成时的事件写在这里,如在这结束 loading 事件,异步请求也适宜在这里调用。
mounted : 挂载元素,获取到 DOM 节点。
updated : 如果对数据统一处理,在这里写上相应函数。
beforeDestroy : 可以做一个确认停止事件的确认框。
nextTick : 更新数据后立即操作 dom。
15、vue3 生命周期
配置项形式:
vue3 可以继续使用 vue2 中的生命周期钩子,但流程设计上有了一些区别,一些钩子名称发生了变化,也新增了两个钩子。除此之外 Vue3 还可以通过组合式 API 形式去使用生命组件钩子。
流程设计上的区别: Vue3 先将 el 挂载了之后再进行的 beforeCreate,而 Vue2 是 created 之后进行判断是否挂载 el,如果没,则流程终止,beforeCreate 和 created 俩个钩子冗余使用,因此 Vue3 进行了优化的。
钩子名称上的区别: beforeDestroy 改名为 beforeUnmount,destroyed 改名为 unmounted。
新增的钩子函数: renderTracked:跟踪虚拟 DOM 重新渲染时调用。renderTriggered:当虚拟 DOM 重新渲染被触发时调用。
组合式 API 形式:
Vue2生命周期
Vue3生命周期
beforeCreate
setup
created
created
beforeMount
onBeforeMount
mounted
onMounted
beforeUpdate
onBeforeUpdate
updated
onUpdated
beforeDestroy
onBeforeUnmount
destroyed
onUnmounted
activated
onActivated
deactivated
onDeactivated
errorCaptured
onErrorCaptured
16、小程序生命周期
全局生命周期:
onLaunch 第一次全局执行
onShow 第一次执行,或者关闭后重新打开后执行
onHide 关闭小程序,但是没完全关闭,切换到后台
onError 小程序报错
页面生命周期:
onLoad 当前页面第一次加载的时候
onShow 第一次执行当前页面,或者关闭后重新打开后执行
onReady 当渲染页(视图层-wxml)加载完毕执行
onHide 关闭当前页面,但是没完全关闭,切换到后台
onUnload 销毁当前页
onPullDownRefresh 下拉刷新
onReachBottom 页面触底时执行,一般用于做上拉加载
onShareAppMessage 分享页面执行
onPageScroll 当前页面滚动执行
onResize 放大缩小页面的时候执行
onTabItemTap 点击底部导航栏触发
组件生命周期:
created 创建组件时执行
attached 被插入到父组件节点时执行
ready 渲染完后执行
moved 移动组件节点
detached 从父组件节点移除时执行
error 组件方法抛出错误时执行
17、vue 里边如何让 css 样式只在当前组件中生效?
在组件中的 style 前面加上 scoped 就可以了
18、vue 的 watch 和 computed 有什么区别?他们的应用场景?
computed 是计算属性,可以修改数据,通过 get 获取数据 set 修改数据,而且它是基于它的依赖缓存,只有相关依赖发生改变时才会重新取值,否则就拿上一次已经计算好存在栈里的值。
watch 是监听属性,可以监听数据 (data 值) 的改变,支持异步,每当监听的数据变化时都会执行回调进行后续操作。有三个属性:handler 事件句柄,immediate 默认监听,deep 深度监听。
应用场景:
computed 应用场景: 在需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算。
watch 应用场景: 需要在数据变化时执行异步或开销较大的操作时,应该使用 watch。使用 watch 选项允许我们执行异步操作,限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
19、$nextTick 是什么?有什么作用?什么时候用?
$nextTick 是什么?
事件轮询,把所有的触发事件都放在一个事件栈里 (事件队列),再根据实际情况先后执行。
$nextTick 有什么作用?
在 DOM 下一次更新完成后延迟回调。
Vue 在更新 DOM 时是异步执行的,在修改数据后视图不会立刻更新,而是等同一事件循环中的所有数据修改完成之后,再统一进行视图更新。所以修改完数据,立即在方法中获取 DOM,获取的仍是未修改的 DOM。为了在修改数据后等待 Vue 更新 DOM,可以在数据变化之后立即使用 $nextTick ,这样回调函数将在 DOM 更新完成后被调用,可以获取更新后的 DOM,解决了异步渲染获取不到更新后 DOM 的问题。
$nextTick 的原理?
$nextTick 本质是返回一个 Promise 对象。
$nextTick 的应用场景?
在钩子函数 created 里面想要获取操作 Dom,把操作 DOM 的方法放在 $nextTick 中。
mounted 和 updated 不会保证所有的子组件都被挂载或重新渲染,如果希望等到整个视图都渲染完毕再执行某些操作,可以在 mounted 或 updated 内部使用 $nextTick 函数。
20、哪个阶段无法获取 DOM 节点?怎么处理?
可能是因为在 beforeCreated,Created,beforeMount 里获取,可能是获取的节点的祖先节点用 v-if 控制了。将获取节点的操作放在 $nextTick 中,在 DOM 下一次更新完成后延迟回调。
21、vue 中使用 refs 获取组件的 dom 元素报 undefined 如何解决?
22、多种数据修改后页面不刷新?怎么解决?⭐⭐⭐
为什么修改数据后页面不更新?
由于 vue2 双向数据绑定的核心原理是数据劫持,通过 Object.defineProperty 来劫持对象的 geter 和 seter 属性,当数据发生改变发出通知。而 Object.defineProperty 必须要递归才能劫持深层数据,导致性能严重受影响所以默认不提供递归,即数组对象更新不会触发渲染更新。
情况1:vue 无法检测实例被创建时不存在于 data 中的变量
原因: 由于 Vue 会在初始化实例时对 data 中的数据执行 getter/setter 转化,所以变量必在 data 对象上存在才能让 Vue 将它转换为响应式的。
解决方法: 把变量放到 data 里面。
情况2:vue 无法检测到 data 中对象的动态添加和删除
原因:
解决方法: 通过 this.$set 动态添加,通过 Object.assign({},{}) 合并对象并返回新对象。通过 this.$delete 动态移除。
情况3:数组的时候,不能添加和删除,不能通过索引直接修改或者赋值,也不能修改数组的长度
原因:
解决方法: 通过 this.$set 动态添加,通过 Object.assign({},{}) 合并数组并返回新数组。通过 push、pop、shift、unshift、splice、sort、reverse 等直接改变原数组的方法会触发更新。
情况4:异步获取接口数据,DOM 数据不发现变化
原因: Vue 在更新 DOM 时是异步执行的。在修改数据后视图不会立刻更新,而是等同一事件循环中的所有数据修改完成之后,再统一进行视图更新。所以修改完数据,立即在方法中获取 DOM,获取的仍是未修改的 DOM。
解决方法: 可以在数据变化之后立即使用 $nextTick,这样回调函数将在 DOM 更新完成后被调用,可以获取更新后的 DOM,$nextTick 解决了异步渲染获取不到更新后 DOM 的问题。
情况5:循环嵌套层级太深,视图不更新
原因:
解决方法: 可以让页面强制刷新,this.$forceUpdate (不建议用)
拓展:路由参数变化时,页面不更新(数据不更新)
原因: 路由视图组件引用了相同组件时,当路由参会变化时,会导致该组件无法更新,也就是我们常说中的页面无法更新的问题。路由切换时页面只会渲染第一次路由匹配到的参数。
解决方法: 通过 watch 监听 $route 的变化。给 绑定 key 属性,这样 Vue 就会认为这是不同的。弊端:跳转到某些路由下没有这个问题,key 属性是多余的
23、Vue.extend 和 Vue.component 的区别?
区别:
vue.extend 的写法步骤更加繁琐一些,要创建构造器,还要 new 一个实例,还要将实例通过 $mount 挂载到特定的元素上。Vue.component 注册组件会自动使用给定的 id 设置组件的名称,然后想在哪里用就在哪里写组件名就可以了。
vue.extend 只能通过自身初始化结构,使用范围可以挂载在某元素下,可以被 Vue 实例的components 引用,可以被 Vue.component 组件引用。vue.component 可通过自身初始化组件结构,可通过引入 vue.extend 初始化组件结构,可通过第三方模板 temple.html 初始化组件结构,使用范围是任何已被 vue 初始化过的元素内。
vue.component 是用来注册全局组件的方法,可以在任何地方使用。而 Vue.extend 是用来创建一个 vue 子类的方法 (一个可复用的组件模板),可以在局部组件中使用。
Vue.component 注册的组件可以直接在模板中使用。而 Vue.extend 创建的子类需要先进行实例化,然后才能在模板中使用。
extend 的基础用法:
使用基础 Vue 构造器函数,创建一个子类,参数是一个包含组件选项的对象。然后 new 一个实例并通过 $mount 挂载到特定的元素上,就可以把这个实例 append 到页面 body 里。
extend 的应用场景:
Vue.extend 属于 Vue 的全局 API,在实际业务开发中我们很少使用,因为相比常用的 Vue.component 写法,使用 extend 步骤要更加繁琐一些。但是在一些独立组件开发场景中,Vue.extend + $mount 这对组合是我们需要去关注的。比如: 如 alert 弹窗组件,你肯定希望 alert 可以动态插入到 body 中,而不需要在 dom 文档流中放一个莫名其妙的 alert,大部分时间他还是隐藏的。
24、vue 是什么?说一下对 vue 的理解?⭐⭐⭐⭐
Vue 是一款渐进式 JavaScript 框架,用于构建用户界面。它采用了 MVVM 的设计模式,通过数据驱动和组件化的开发方式,使得前端开发变得更加简单、高效。
vue 核心概念(优点):
双向数据绑定: Vue 框架采用的是 mvvm 模式,相比 MVC 模式,不再局限于数据单向绑定,而是能够实现数据双向绑定、将数据和视图进行实时的同步更新。实现的原理: vue2 是通过 (Object.defineProperty 数据劫持)结合(订阅发布模式)实现双向数据绑定的。vue3 是通过 new Proxy 数据代理实现的。实现了当数据发生改变时,视图会自动更新;当用户操作视图时,数据也会相应地进行更新。
组件化开发: Vue 鼓励使用组件化开发,将页面拆分成多个独立的组件,每个组件负责特定的功能。组件可以复用、嵌套和组合,提高代码的可维护性和复用性。
虚拟 DOM: Vue 采用了虚拟 DOM 技术,通过在内存中构建虚拟 DOM,并与真实 DOM 进行比较,只更新需要更新的部分,减少真实 DOM 操作,提高了渲染性能。
渐进式: Vue 是一款渐进式 JavaScript 框架,所谓渐进式就是逐步实现新特性的意思,如实现模块化开发、路由、状态管理等新特性,根据业务需求逐步选择需要的 vue 功能模块。
生态丰富: Vue 拥有一个活跃的社区,有大量的第三方插件和工具可以使用。同时,Vue 也提供了官方的路由、状态管理和构建工具等配套库,方便开发者进行项目开发。
简单来说,Vue 是一款渐进式 JavaScript 框架,综合了 Angular(模块化)和 React(虚拟 DOM)的优点。采用了 MVVM 的设计模式,实现了双向数据绑定。通过数据驱动和组件化的开发方式,使得前端开发变得更加简单、高效。
25、什么是渐进式?⭐⭐⭐
技术上的渐进式选型,也就是说你不用一上来就要选 vue 的全家桶,可以根据自己的业务逐步的选择你需要的 vue 功能模块。
业务模块的渐进式的调用,这就涉及 vue 中的一个重要的概念-组件化,组件在不需要的时候不加载,当业务需要时再加载。
26、vue 是如何实现双向数据绑定? (原理与缺陷)⭐⭐⭐
什么是数据双向绑定?
Vue 框架采用的是 mvvm 模式,实现了双向数据绑定,当数据发生改变时,立即触发视图的更新。当用户操作视图时,数据也会相应地进行更新。
vue2 是如何实现双向数据绑定的?
原理:
vue2 是通过 Object.defineProperty 数据劫持 结合 订阅发布模式 实现双向数据绑定的。通过 Object.defineProperty 来劫持各个属性的 getter 和 setter,当数据对象的属性被读取或修改时,会触发相应的 getter 和 setter 函数。当数据发生变化时,触发 setter 函数会通知相应的订阅者对象 Dep,然后 Dep 触发相应的监听回调进行更新视图。也就是说数据和视图同步。
缺点:
Object.defineProperty 数据劫持,只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历,如果对象里面还有对象,需要递归。所以性能差。
Object.defineproperty 只能监听到已有数据是否被修改,不能监听到对象的新增删除属性,需要调用相应的方法更新,如 this.$set 和 this.$delete。由于 Object.defineProperty 劫持的是对象的属性,所以新增属性时,需要重新遍历对象,对其新增属性再使用 Object.defineProperty 进行劫持。
Object.defineproperty 也不能监听到数组的添加删除,而且无法监控到数组下标的变化,导致不能通过索引直接修改或者赋值,也不能修改数组的长度。依然可以 this.$set 和 this.$delete 去更新数据,当然也可以使用数组重写的方法实现了数组的响应,比如使用 push 给数组添加一个数据,不需要调用 this.$set 页面也能够更新响应。( 数组方法: push 添加、pop 删除、shift 删除、unshift 添加、splice 插入、sort 排序、reverse 反转)
vue3 是如何实现双向数据绑定的?
原理:
Vue3 是通过 Proxy 数据代理来实现的数据双向绑定,Proxy 是 ES6 中新增的一个特性,可以劫持整个对象,并返回一个新对象。实现过程是当一个普通的 javaScript 对象传入 vue 实例的时候,vue 会使用 Proxy 对象包装该对象,并在外界访问和修改对象属性时进行拦截。当属性发生改变的时候,vue 就能捕获到变化并更新相应的视图。ES6 提供 Proxy 构造函数,用来生成 Proxy 实例,接收两个参数 target 和 handler。target 是用 Proxy 包装的被代理对象,可以是任何类型的对象,包括原生数组,函数,甚至是另外一个代理。handler 是一个对象,其声明了代理 target 的一些操作,当任何一个对象属性发生变化时,就会触发 handler 中的 set 方法,从而更新对应的DOM节点。
优点:
可以劫持整个对象,而非对象属性,并返回一个新的对象。
代理对象可以劫持对象属性的访问、新增、删除等操作,不需要初始化时遍历所有属性。
如果有多层属性嵌套,只有在访问到某个属性的时候才会递归处理下一级属性。
可以监听到数组变化,例如索引。
不仅可以代理对象,还可以代理数组,还可以代理动态增加的属性
缺点: 存在兼容性问题,IE 无法兼容,vue2 兼容到了 IE8,如果不考虑兼容 IE 浏览器,可以考虑使用 vue3。
27、什么是订阅发布模式?⭐
订阅发布模式是一种软件设计模式,其原理是基于一个中心的事件调度器 (或者称为消息队列) 组件可以订阅感兴趣的事件,当事件发生时,调度器会通知所有订阅了该事件的组件进行相应的处理。
组成:
发布者:负责发布事件。当某个事件发生时,发布者会将事件发布到调度器中。
订阅者:订阅感兴趣的事件。订阅者通过向调度器注册自己对某个事件的关注,以便在事件发生时接收通知。
调度器:负责接收发布者发布的事件,并将事件分发给所有订阅了该事件的订阅者。
原理:
当发布者发布一个事件时,调度器会遍历所有订阅了该事件的订阅者,并将事件传递给它们。订阅者收到事件后,可以执行相应的处理逻辑。这种方式实现了组件之间的解耦,使得发布者和订阅者可以独立地进行开发和演化。
订阅发布模式的优点是灵活性和可扩展性。通过在调度器中注册和管理订阅关系,我们可以方便地添加新的发布者和订阅者,而不会影响现有的组件。同时,由于订阅者只关注自己感兴趣的事件,可以有效地减少不必要的通信和处理开销。
总结来说,订阅发布模式通过调度器实现了组件之间的松耦合通信。发布者将事件发布到调度器,而订阅者通过订阅感兴趣的事件来接收通知并执行相应的处理逻辑。这种模式提供了一种灵活、可扩展的组件间通信方式。
28、vue2 和 vue3 有什么区别?⭐⭐⭐
1、生命周期发生了改变
2、Vue2 只支持一个根节点,Vue3 支持多个根节点
3、Vue2 使用的是选项式 api,Vue3 使用的是函数式 api
vue2 使用 optionsApi(选项式 api)
vue3 使用 CompositionApi(函数式\组合式)代码复用性更强
4、核心原理不同
vue2 是通过 【Object.defineProperty 数据劫持】结合【订阅发布模式】实现双向数据绑定的。
vue3 是通过 Proxy 数据代理来实现的数据双向绑定的。
5、优缺点
vue2 的 【Object.defineProperty 数据劫持】只能劫持对象的某个属性,不能对整个对象进行监听,劫持多个属性需要遍历,如果对象里面还有对象需要递归,性能差。而且只能监听已有对象属性的修改,不能监听到对象的新增删除属性,需要调用相应的方法更新,如 this.$set 和 this.$delete。也不能监听到数组的添加删除,而且无法监控到数组下标的变化,导致不能通过索引直接修改或者赋值,也不能修改数组的长度。 可以通过 this.$set 和 this.$delete 去更新数据,当然也可以使用数组重写的方法实现了数组的响应。
vue3 的 Proxy 数据代理,代理对象可以劫持对象属性的访问、新增、删除等操作,不需要初始化时遍历所有属性,可以劫持整个对象,而非对象属性。可以监听到数组变化,例如索引。不仅可以代理对象,还可以代理数组,还可以代理动态增加的属性。缺点是存在兼容性问题,IE 无法兼容,如果不考虑兼容 IE 浏览器,可以考虑使用 vue3。
6、父子传参不同,setup() 函数特性
vue2 是把数据放入 data 中,vue3 就需要使用一个新的 setup() 方法,此方法在组件初始化构造得时候触发。使用一下三个步骤来简=建立反应性数据: 1. 从 vue 引入 reactive。2. 使用 reactive() 方法来声明数据为响应性数据。3. 使用 setup() 方法来返回我们得响应性数据,从而 template 可以获取这些响应性数据。
29、什么是 mvvm? mvc 是什么?区别?原理?⭐⭐⭐
MVC(Model-View-Controller)
MVC 是比较直观的架构模式,用户操作->View(负责接收用户的输入操作)->Controller(业务逻辑处理)->Model(数据持久化)->View(将结果反馈给View)。MVC 使用非常广泛,比如 JavaEE 中的 SSH 框。
MVVM(Model-View-ViewModel) (vue \ react \ angular)
m ( module ) 模型,存放数据的地方
v ( view ) 视图,template 本身,渲染数据的地方
vm ( view-module ) 视图模型,转换机制 ( vue 本身 ),把 m 层 ( 数据层 ) 上的数据,自动渲染到 v 层 ( 视图层 )
vm 是如何实现的?通过双向数据绑定的原理,vue2 的底层核心是数据劫持 ( 订阅发布模式 ),在 v ( 视图层 ) 和 m ( 数据层 ) 之间是没有联系的,通过 vm 进行交互,而且 m 和 vm 之间的交互也是双向的,因此视图层的数据改变的时候同时会修改数据源,而数据源数据的变化也会立即反应 view。
Vue 框架采用的模式是 mvvm 模式,响应式布局,与 MVC 模式相比,不再局限于数据单向绑定,而是能够实现数据双向绑定、同步刷新。m 是 module 模型,存放数据的地方。v 是 view 视图,渲染数据的地方。vm 是 view-module 视图模型,是一个转换机制,把 module 层 上的数据,自动渲染到 view 层。vm 是通过双向数据绑定实现的,vue2 的核心是数据劫持 ( 订阅发布模式 ),vue3 的核心是 new Proxy() 数据代理。实现了视图层的数据改变的时候同时会修改数据源,而数据源数据的变化也会立即反应视图层。
30、react 和 vue 有哪些不同,说说你对这两个框架的看法
相同点:
都支持服务器端渲染。
都有 Virtual DOM,组件化开发,通过 props 参数进行父子组件数据的传递,都实现webComponent 规范。
数据驱动视图。
都有支持 native 的方案,React 的 React native,Vue 的 weex。
不同点:
React 严格上只针对 MVC 的 view 层,Vue 则是 MVVM 模式。
virtual DOM 不一样,vue 会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。而对于 React 而言,每当应用的状态被改变时,全部组件都会重新渲染,所以 react 中会需要shouldComponentUpdate 这个生命周期函数方法来进行控制。
组件写法不一样, React 推荐的做法是 JSX + inline style,也就是把 HTML 和 CSS 全都写进JavaScript 了,即 'all in js'。 Vue 推荐的做法是 webpack+vue-loader 的单文件组件格式,即html,css,js 写在同一个文件。
数据绑定:vue 实现了数据的双向绑定,react数据流动是单向的。
state 对象在 react 应用中不可变的,需要使用 setState 方法更新状态。在 vue 中,state 对象不是必须的,数据由 data 属性在 vue 对象中管理。
31、说一下 vue 的服务端渲染 SSR?⭐⭐
什么是 服务端渲染 SSR?
服务端渲染 SSR 顾名思义页面上的内容是通过服务端渲染生成的,浏览器直接显示服务端返回的html 就可以了。和它对应的是 CSR,CSR 是客户端在请求时,服务端不做任何处理,直接将前端资源打包后生成的 html 返回给客户端,此时的 html 中无任何网页内容, 需要客户端去加载执行 js 代码才能渲染生成页面内容,同时完成事件绑定,然后客户端再去通过 ajax 请求后端的 API 获取数据更新视图。
服务端渲染的优势?
减少网络传输,响应快,用户体验好,首屏渲染快,对搜索引擎友好,搜索引擎爬虫可以看到完整的程序源码,有利于SEO。
服务端渲染的缺点?
网络传输数据量大,占用部分服务器运算资源,不容易维护,前端修改部分 html/css 后端也要改用户体验差。
开发条件所限。浏览器特定的代码,只能在某些生命周期钩子函数(lifecycle hook)中使用。一些外部扩展库(external library)可能需要特殊处理,才能在服务器渲染应用程序中运行。
涉及构建设置和部署的更多要求。与可以部署在任何静态文件服务器上的完全静态单页面应用程序(SPA)不同,服务器渲染应用程序,需要处于 Node.js server 运行环境。
更多的服务器端负载。在 Node.js 中渲染完整的应用程序,显然会比仅仅提供静态文件的 server 更加大量占用 CPU 资源(CPU-intensive - CPU 密集),因此如果你预料在高流量环境 (high traffic) 下使用,请准备相应的服务器负载,并明智地采用缓存策略。
32、什么是 “状态管理”?⭐
把组件的共享状态抽取出来,以一个全局单例模式管理。在这种模式下,我们的组件树构成了一个巨大的视图,不管在树的哪个位置,任何组件都能获取状态或者触发行为!这就是状态管理模式。简单来说就是把组件的共享状态抽出来,不管任何组件在任何地方都能直接获取这个状态或者触发里面的行为。
33、vuex 实现原理?⭐
把多个组件需要共享的变量全部储存在一个对象里面,然后将这个对象放在顶层的 Vue 实例中,让其他组件可以使用,并且让这个对象里面的属性做到响应式。
34、vuex是什么?怎么用?优点和缺点?什么时候用?⭐⭐⭐
vuex 是什么?
Vuex 是 vue 框架中状态管理库,是集中管理项目公共数据的。
vuex 怎么用?
vuex 主要有五大核心属性,分别是 state 数据状态、getter 计算属性、mutation 同步提交、action 异步提交、module 模块化。
state 属性用来存储公共管理的数据,可以通过 this.$store.state 调用数据。
getters 属性可以认为是定义 store 的计算属性(过滤数据),就像计算属性一样,getter 的返回值会根据它的依 赖缓存起来,只有当它的依赖值发生改变才会重新计算。通过 this.$store.getters调用属性(获取过滤后的数据)。
mutation 用来存放更改 state 数据的同步方法,不可以写异步方法,理论上是修改 state 的唯一途径。通过 this.$store.commit(”function”) 来调用 mutation 中的同步方法。
action 用来存放异步方法,类似于 mutation,不同在于 action 不能直接更改 state 里面的状态,通过 context.commit 提交 mutation,触发 mutation 里面的逻辑方法去修改 state 里面的状态,action 可以包含任意的异步操作。可以通过 this.$store.dispatch('function') 去调用 action 里面的异步方法。
moudle 属性是将 store 分割成模块。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块。
vuex 的优点?
能够在 vuex 中,集中管理共享的数据,易于开发和后期维护。
js 原生的数据对象写法,比起 localStorage 不需要做转换, 使用方便。
减少 http 请求,提高浏览器性能。
多层嵌套的组件、兄弟组件间的状态会更好管理维护,高效的实现组件间的数据共享,提高开发效率。
vuex 的状态管理是响应式的,如果 state 里面的状态发生变化,相应的组件也会渲染更新,能够实时保持数据与页面的同步。
vuex 的缺点?
刷新浏览器,vuex 中的 state 会重新变为初始状态。
什么时候适合使用 Vuex,什么时候不适合使用 Vuex?
项目中,如果不打算开发大型单页应用,使用 vuex 可能是繁琐冗余的。如果项目比较小比较简单,使用 prop 和 $emit 完成父子间的传参,或者订阅发布模式 $eventBus 实现复杂的组件间的通信也是可以满足需求的。vuex 具体应用在哪取决于项目的规模以及具体的业务场景,可能是为了解决多层嵌套组件之间的通信问题,或是为了更好地管理应用中错综复杂的状态关系,而不能为了用 vuex 而在项目中使用 vuex。
vuex 的应用场景?
登录状态(token)
购物车
权限码
复杂的组件通信(比如兄弟组件的通信,多层嵌套的组件的传值等等)
vuex 可配合 sessionStorage 做用户基本信息的持久化存储。
35、vuex 刷新页面后数据丢失如何解决⭐⭐⭐
项目用 vuex 做全局状态管理,页面刷新后,仓库内的 state 被初始化,所以存的数据也会丢失。
在刷新前把 state 的数据 (通过 localStorage 或者 sessionStorage) 本地存储起来,刷新之后再把存储起来的数据放回 store 的根状态(store.state)。
36、什么是路由懒加载?为什么要路由懒加载?如何实现?
什么是路由懒加载?
路由懒加载也叫延迟加载,既在需要的时候加载,随用随载。
为什么要路由懒加载?(官方解析)
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。
如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。
继续解释为什么要路由懒加载:
像 vue 这种单页面应用,如果没有应用懒加载,运用 webpack 打包后的文件将会异常的大。造成进入首页时,需要加载的内容过多,时间过长,会出现长时间的白屏,即使做了 loading 也是不利于用户体验。而运用懒加载则可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时。也就是说:进入页面不用也不需要一次性加载过多资源造成加载时间过长!
路由懒加载如何实现?三种实现方式?
Vue 异步组件 component: resolve=>(require(['../views/About'],resolve))
ES6 标准语法 import()---------推荐使用!!!!! component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
webpack 的 require,ensure()
37、怎么定义 vue-router 的动态路由?怎么获取传过来的值?
通过标签 router-link 跳转传参,query 和 param
通过事件 this.$router.push({})跳转传参
// query
跳转到b页面
// param
跳转到b页面
// $router
this.$router.push({path:"/home",query:{id:1}})
// 区别:
1. query : query 通过 path 引用路由
通过 url 传参,刷新还在
接收参数: this.$route.query.id
query 类似于 get 传参,在浏览器地址栏显示
2. param : params 通过 name 引用路由,name 要在路由内配置
刷新消失,需要设置动态路由加冒号 :绑定动态参数,path:"/routeB/:id"
接收参数: this.$route.params.id
params 类似于 post 传参,不在地址栏显示
38、vue 路由钩子函数 (路由守卫)
全局前置守卫: 每一个路由进去之前进行拦截 beforeEach
全局解析守卫: 同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
全局后置守卫: 每一个路由进去之后进行拦截 afterEach
路由独享守卫: 写在路由,控制具体的某一个路由
组件守卫: 写在组件
39、小程序路由跳转有哪些方式?
navigateTo: 页面跳转(非tab页)
switchTab: tab页的跳转
redirectTo: 页面重定向
navigateBack: 返回上一页
40、vue 里面 $route 和 $router 的区别?
$route 是当前路由对象
$router 是一个全局部的路由对象
41、组件开发有什么好处?为什么要封装组件? ⭐
组件可以提升整个项目的开发效率,避免重复的工作量。
提高复用性。
能够把页面抽象成多个相对独立的模块,使代码逻辑清晰,方便项目的后期维护。
便于协同开发,不用把大量精力放在内部的实现上,实现模块化开发。
提高开发效率、提高复用性、使代码逻辑更加清晰,方便项目的后期维护、便于协同开发
42、什么情况下需要封装组件?
解决避免重复的工作量问题: 在项目中多次出现相同特征的模块,该组件未来还有可能出现在其他的地方,且开发这样一个功能模块成本比较高,或者是该组件后续会发生部分特征的改变,且会影响类似特征的模块。
使代码逻辑更加清晰: 如果每一个模块都写到同一个页面,这样模块的交互功能和处理逻辑很多,造成页面代码量很大,各个模块中也会有相类似的方法,后续代码的可读性不高,功能的维护复杂度提高。
不用把大量的精力放在内部实现上: 提高团队的协作问题,在一个团队里,每个人擅长部分都不一样的,针对这样的情况,我们可以将其单独进行封装,使使用组件者不用把大量精力放在内部的实现上。
43、组件封装流程?怎么封装组件的? ⭐
使用 Vue.extend 方法创建一个组件,然后使用 Vue.component 方法注册组件。但是我们一般用脚手架开发项目,每个 .vue 单文件就是一个组件。在另一组件 import 导入,并在 components 中注册。首先建立好自定义模板,准备好组件的数据的输入,子组件定好 props 里面的数据和类型,接收父组件传过来的数据,然后通过 $emit( )暴露输出子组件的数据返回给父组件。
传统流程:
创建 Xxx.vue 文件,对组件进行封装
通过 import 的方式将组件导入 import Xxx from '...'
对组件进行注册 componments:{ Xxx }
使用组件:
views 放页面级的组件、commen 放公共组件、feature 放功能组件
44、vue 的组件通信有哪些方法 ⭐
父子传参:
边界访问 (父子通信):
访问父组件:$parent
访问子组件:$children
访问根实例:$root
访问子组件实例或子元素:$refs
兄弟通信:
通过父组件进行兄弟组件之间通讯
订阅发布模式:$eventBus ($emit / $on / $off)
状态管理模式:vuex
跨级通信:
依赖注入:provide / inject (父传子)
$attrs / $listeners (inheritAttrs)
状态管理模式:vuex
45、$eventBus 如何实现跨页面传数据?
在 beforeDestoried(销毁之前) 执行提交事件。
46、为什么组件的 data 必须是一个函数?
可以反复的调用
可以反复创建新对象
避免组件间的数据冲突
47、keep-alive 的作用是什么?⭐
是 vue 内置的一个组件 (是一个抽象组件)
作用:
它自身不会渲染一个 DOM 元素,也不会出现在父组件链中,使用 keep-alive 包裹动态组件时,会缓存不活动的组件实例(组件进行切换的时候,默认会进行销毁,消耗资源)
优点:
保留组件状态,避免重新渲染,缓存组件能加快速度,降低消耗资源。
用法:
当组件在 内被切换的时候,activated (激活) 和 deactivated (取消激活) 这两个生命周期钩子函数将会被对应执行。页面第一次进入的时候,钩子触发的顺序是 created->mounted->activated。页面退出的时候会触发 deactivated,当再次前进或者后退的时候只触发 activated。当 keep-alive 组件激活时,触发 activated。keep-alive 组件停用时调用 deactivated。
六、uni-app 篇
1、uniapp 打包过程(安卓 Android)⭐
如果是使用私有证书,打包前得生成安卓的 .keystore 证书文件。
打开 manifest.json 配置文件进行配置: 基础配置 (AppID、应用名称、应用描述、应用版本号、vue 版本号)、App 图标配置 (不配置默认是 uni-app 图标)、App 启动界面配置 (不配置启动时是 uni-app 默认图)、APP 模块配置、APP 权限配置等。
配置好后,点击 HbuilderX 的发行,选择原生APP-云打包,勾选 Android 选项,填写 Android 包名,勾选私有证书 (也可以选择使用 DCloud 公有证书),填写证书别名 alias,证书私钥密码 (自己设置的),上传证书文件 (生成 keystore 文件的地址),取消广告联盟中的所有广告 (个人选择),点击打包。
打包成功后 HbuilderX 控制台会自动返回 APP 下载地址,然后可以对该链接在电脑中进行下载,也可以将该链接复制到手机浏览器,在手机中对该软件进行下载。
详细步骤: uni-app Android 打包详细步骤
2、uniapp 打包过程(苹果 ISO)⭐
ISO 系统打包需要有苹果开发者账号,新建一个 APPid,设置 Bundle ID,申请 IOS 发布证书并保存证书私钥密码,申请 IOS 发布描述文件。
打开 hbuilderx 点击发行-原生 APP 云打包,勾选 ISO 选项,填写 Bundle ID 和证书私钥密码,上传描述文件和发布证书,点击打包。
等待控制后台返回下载链接,点击链接进行下载 IPA 文件。
在 iTunes connect 上创建 APP,上传 IPA 到 APP store,设置 app 信息提交到商店进行审核
详细步骤: uni-app ISO 打包详细步骤
3、uniapp 小程序打包过程 ⭐
进入微信公众平台注册微信小程序获取 AppID
打开 manifest.json 配置文件,点击微信小程序配置,填写 AppID 和相关配置
点击 HbuilderX 的发行,选择小程序微信,填写小程序名称和 AppID 点击发布
HbuilderX 自动调开微信开发者工具,点击右上角上传按钮,填写版本号和备注,点击上传
登录微信公众平台,选择版本管理,点击提交审核
详细步骤: uniapp 小程序打包详细步骤
4、uniapp 的基本配置?
page.json:uni-app 全局配置,配置页面的文件路径、窗口样式、原生的导航栏、底部 tabbar
App.vue:页面入口文件(主组件)
main.js:项目入口文件
pages:页面管理部分
manifest.json:配置文件,配置应用名称,appid,logo,版本等打包信息
unpackage:打包目录
static:静态资源目录
components:uni-app 组件目录 (符合 vue 规范)
wxcomponents:存放小程序组件目录
uni.scss:全局样式
uni_modles:存放插件
5、uniapp 上传文件时用到的 api 是什么? 格式是什么?
api: uni.uploadFile。
格式: 上传的地址 url,上传类型 fileType,图片路径 filePath,文件对应的key name,成功的回调
uni.uploadFile({
url: '要上传的地址',
fileType:'image',
filePath:'图片路径',
name:'文件对应的key',
success: function(res){
console.log(res)
}
})
6、uniapp 获取地理位置的API 是什么?
uni.getLocation
7、rpx、px、em、rem、%、vh、vw 的区别是什么?⭐
rpx:相当于把屏幕宽度分为750份,1份就是1rpx。
px:绝对单位,页面按精确像素展示。
em:相对单位,相对于它的父节点字体进行计算。
rem:相对单位,相对根节点html的字体大小来计算。
%:一般来说就是相对于父元素。
vh:视窗高度,1vh等于视窗高度的1%。
vw:视窗宽度,1vw等于视窗宽度的1%。
8、uniapp 如何监听页面滚动?
使用 onPageScroll 监听
9、如何让图片宽度不变,高度自动变化,保持原图宽高比不变?
给 image 标签添加 mode=‘widthFix’
10、uni-app 的优缺点?
优点:
一套代码可以生成多端。
学习成本低,语法是vue的,组件是小程序的。
拓展能力强。
使用HBuilderX开发,支持vue语法。
突破了系统对H5条用原生能力的限制。
缺点:
问世时间短,很多地方不完善。
社区不大。
官方对问题的反馈不及时。
在Android平台上比微信小程序和iOS差。
文件命名受限。
11、分别写出 jQuery、vue、小程序、uni-app 中的本地存储
jQuery:
存:cookie(’key’,’value’)
取:cookei(’key’)
vue:
存:localstorage.setItem(‘key’,‘value’)
取:localstorage.getItem(‘key’)
微信小程序:
存:wx.setStorage/wx.setStorageSync
取:wx.getStorage/wx.getStorageSync
uni-app:
存:uni.setStorage({key:“属性名”,data:“值”})
取:uni.getStorage({key:“属性名”,success(e){e.data//这就是你想要取的token}})
12、JQ、VUE、uni-app、小程序的页面传参方式? ⭐
JQ传参:通过url拼接参数进行传参。
VUE传参:
可以通过标签 router-link 跳转传参,通过 path+ 路径,query+ 参数。
可以通过事件里的 this.$router.push 跳转传参。
uni-app,小程序传参 :通过跳转路径后面拼接参数来进行跳转传参。
13、vue、微信小程序、uni-app 绑定变量属性区别?
vue 和 uni-app 动态绑定一个变量的值为元素的某个属性的时候,会在属性前面加上冒号 ":"。小程序绑定某个变量的值为元素属性时,会用两个大括号 {{}} 括起来,如果不加括号,为被认为是字符串。
14、uni-app 的生命周期?
uniapp 生命周期是以小程序生命周期为基础实现的,分为应用生命周期、页面生命周期、组件生命周期。 其中组件生命周期就是 vue 生命周期。
应用生命周期:
onLaunch: 当 uniapp 初始化完成时调用 (只触发一次)。
onShow: 当 uniapp 启动或从后台进入前台时发生调用 (监听用户进入小程序)。
onHide: 当 uniapp 从前台进入后台时发生调用 (监听用户离开小程序)。
onError: 当 uniapp 报错时被触发。
onUniNViewMessage: 对 nvue 页面的数据进行监听。
onUnhandledRejection: 对未处理的 Promise 拒绝事件进行监听。
onPageNotFound: 页面不存在监听函数。
onThemeChange: 监听系统主题的变化。
页面生命周期:
onLoad
: 监听页面加载。
onShow
: 监听页面显示 (每次页面显示都触发)。
onReady
: 监听页面初次渲染完成。
onHide
: 监听页面隐藏。
onUnload
: 监听页面卸载。
onResize
: 监听窗口尺寸的变化。
onPullDownRefresh
: 监听用户下拉动作。
onReachBottom
: 监听页面上拉触底事件。
onTabItemTab
: 点击 TabBar 时触发。
onShareAppMessage
: 点击右上角分析时触发。
onShareTimeline
: 点击右上角转发到朋友圈时触发。
onAddToFavorites
: 点击右上角收藏时触发。
onPageScroll
: 监听页面滚动。
onNavigationBarButtonTap
: 监听标题栏按钮点击事件。
onNavigationBarSearchInputChanged
: 监听标题栏搜索输入框输入内容变化事件。
onNavigationBarSearchInputClicked
: 监听标题栏搜索输入框点击事件。
onBackPress
: 监听用户点击右上角收藏。
组件生命周期:
uniapp 的组件生命周期和 Vue 标准组件生命周期相同。在当前版本的 uniapp中,你既可以选择使用 vue2 进行开发,也可以使用 vue3 进行开发,可以参考 vue2 和 vue3 的生命周期。
15、小程序组件传参有哪些方式
全局数据、Storage存储、eventBus。
七、HTTP 请求篇
1、浏览器输入url后都经历了什么?⭐⭐⭐
查看缓存(浏览器缓存\系统缓存\路由器缓存),如果缓存中有则直接显示页面内容。
域名解析(DNS解析)获取相应的 IP 地址。
浏览器向服务器发起 TCP 连接,建立 TCP 三次握手。
浏览器向服务器发起 HTTP 请求,请求数据包。
服务器处理请求,返回响应数据至浏览器。
关闭 TCP 连接(四次挥手)。
浏览器解析 HTML 代码并请求资源(js、css、图片等)。
浏览器进行页面布局渲染。
2、HashRouter 和 HistoryRouter 的区别?⭐⭐⭐
实现原理:
hash: hash是通过监听hashChange事件来实现前端路由。hash通过window.onhashchange 方法获取新 URL 中 hash 值,如果 hash 值有变化就会自动调用 hashChange 的监听事件,在这个回调事件中,相应的执行无刷新页面跳转。
history: history 是利用浏览历史记录栈的 API 来实现前端路由。history 通过 pushState 和 replaceState 方法更新浏览器 URL 地址而不重新发起请求(替换URL页面跳转且不刷新页面),并且将地址记录到历史记录中。通过 onpopstate 监听浏览器的前进和后退。注意:URL发生了变化,但是不会立即向后端服务器发送请求,但是如果点击刷新,就会重新向后端服务器发送请求
区别:
hash 模式的 url 中有 # 号,不太优雅。history 模式的 url 中没有 # 号,更美观。
hash 模式不需要在服务器层面上处理,但不利于 SEO。history 模式依赖 H5 API 和后台配置,没有后台配置的话,页面刷新时会出现 404,需要和后端人员配合配置一下 url 重定向,如果 URL 匹配不到任何静态资源,则重定向到首页路由。由于依赖 H5 所以 history 有浏览器兼容问题。
hash 模式 url 发生变化时,变化的是 url 的#号后面的 hash 值,hash 虽然包含在 url 中,但是没有被包含在 http 请求中,对后端没影响,所以不会向后端发送请求,所以 hash 改变也不会重新加载页面。history 模式的 URL 改变会向服务器发出请求,同时也会在浏览器历史记录中添加一条记录,可能会重新加载页面。
相同的 url,history 会触发添加到浏览器历史记录栈中,hash 不会触发。
因为 vue 是一个单页面应用,所以默认只有 hash 模式,没有 history 模式,需要手动设置。使用 history 模式,需要适当的服务器配置,将页面请求全部重定向到 index.html。
3、cookie、sessionStorage、localStorage的区别?⭐⭐⭐
共同点: 都是存储在浏览器本地的、相对不安全。
不同点:
写入方式: cookie 是由服务端写入的,需要服务器支持。而 localStorage 和 sessionStorage 都是由前端写入的。
生命周期: 如果没有设置过期时间,cookie 默认关闭浏览器立即消失。而 localStorage 默认是永久储存,除非手动清除。sessionStorage 是页面关闭的时候就会自动清除。
存储大小: cookie 的存储大小大概4kb,存储条数为 20 条,超过20条后面的数据会替代前面的数据。localStorage 和 sessionStorage 存储大小大概5M,存储条数不限制。
兼容性: cookie 可以兼容低版本浏览器。localStroage 和 sessionStorage 是 H5 新特性,无法兼容 ie8 及以下的浏览器。
多Tab: LocalStorage 可以在多个 Tab(窗口) 打开,SessionStorage 限制必须在同一个页面,多 tab(窗口) 的时候不可以共享数据。
应用场景: 前端向后端发起请求时会自动携带 cookie 里面的数据,但是 SessionStorage 和 localStorage 不会,所以他们的应用场景也不同。Cookie 一般用于存储登录验证信息 SessionID 或者 token。localStorage 常用于存储不易变动的数据,减轻服务器的压力。SessionStorage 可以用来检测用户是否刷新进入页面,如音乐播放器恢复播放进度条的功能。
4、如何实现可过期的 localstorage 数据?
localStorage 只能用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去删除。所以要实现可过期的 localStorage 缓存的中重点就是:如何清理过期的缓存。 目前有两种方法,一种是惰性删除,另一种是定时删除。
惰性删除: 指某个键值过期后,该键值不会被马上删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。实现方法是,存储的数据类型是个对象,该对象有两个key,一个是要存储的 value 值,另一个是当前时间。获取数据的时候,拿到存储的时间和当前时间做对比,如果超过过期时间就清除 Cookie。
定时删除: 每隔一段时间执行一次删除操作,并通过限制删除操作执行的次数和频率,来减少删除操作对 CPU 的长期占用。另一方面定时删除也有效的减少了因惰性删除带来的对localStorage 空间的浪费。实现过程,获取所有设置过期时间的 key 判断是否过期,过期就存储到数组中,遍历数组,每隔1S(固定时间)删除5个(固定个数),直到把数组中的 key从 localstorage 中全部删除。
5、HTTP 中请求行、请求头、请求体有什么作用?
请求行: 状态码、请求地址、请求类型。
请求头: token、发送的数据格式。
请求体: 发送的数据。
6、Token 能放在 cookie 中吗?
token 一般是用来判断用户是否登录的,它内部包含的信息有 uid(用户唯一的身份标识)、time(当前时间戳)、sign(签名)。token 可以存放在 Cookie 中,token 是否过期,应该由后端来判断,不该前端来判断,所以 token 存储在 cookie 中只要不设置 cookie 的过期时间就可以了,如果 token 失效,就让后端在接口中返回固定的状态表示 token 失效,需要重新登录,再重新登录的时候,重新设置 cookie 中的 token 就行。
7、Token 认证流程?
客户端使用用户名跟密码请求登录。
服务端收到请求,去验证用户名与密码。
验证成功后,服务端签发一个 token ,并把它发送给客户端。
客户端接收 token 以后会把它存储起来,比如放在 cookie 里或者 localStorage 里。
客户端每次发送请求时都需要带着服务端签发的 token (把 token 放到 HTTP 的 Header 里)
服务端收到请求后,需要验证请求里带有的 token ,如验证成功则返回对应的数据。
8、什么是同源策略?为什么要有同源策略?⭐⭐
同源策略: 域名、协议、端口号都相同才能互相访问。
目的: 防止黑客攻击,xss、csrf 攻击 ( 导致了前后端无法访问 ,也就是跨域问题)
9、XSS攻击是什么?
XSS是跨站脚本攻击、向目标网站插入恶意代码、大量用户访问网站时运行恶意脚本获取信息
原理: 通过向 Web 页面里面插入 script 代码,当用户浏览这个页面时,就会运行被插入的script 代码,达到攻击者的目的。XSS 的危害一般是泄露用户的登录信息 cookie,攻击者可以通过 cookie 绕过登录步骤直接进入站点。
XSS类型: XSS 的分类分为反射型和存储型。反射型就是临时通过 url 访问网站,网站服务端将恶意代码从 url 中取出,拼接在 HTML 中返回给浏览器,用户就会执行恶意代码。存储型就是将恶意代码以留言的形式保存在服务器数据库,任何访问网站的人都会受到攻击。
10、CSRF攻击是什么?
CSRF跨站点请求伪造,攻击者盗用了用户的身份,以用户的身份发送恶意请求,但是对服务器来说这个请求是合理的,这样就完成了攻击者的目标。
原理: 用户打开浏览器,访问目标网站A,输入用户名和密码请求登录,用户信息在通过认证后,网站A产生一个 cookie 信息返回给浏览器,这个时候用户以可正常发送请求到网站A。用户在没有退出网站A之前在同一个浏览器打开了另一个新网站B,新网站B收到用户请求之后返回一些攻击代码,并发出一个请求要求访问返回cookie的网站A,浏览器收到这些攻击性代码之后根据新网站B的请求在用户不知道的情况下以用户的权限操作了cookie 并向网站A服务器发起了合法的请求。
11、什么是跨域?为什么有跨域问题?⭐⭐⭐
跨域是由于浏览器的同源策略,当一个页面中某个请求的地址的 “协议”,“域名“,”端口“ 三者之间任意一个与当前页面的地址不同,在前后端分离的情况下导致无法直接通讯和访问,这种就是跨域。
出现跨域的话主要是因为浏览器的同源策略导致的,浏览器同源策略主要是为了防止黑客攻击,像 xss、csrf 攻击,但也导致了域名、协议、端口其中一个不同的话前后端无法访问,也就是跨域问题。其实跨域就是不允许跨域请求资源,是浏览器的安全限制。
12、跨域的解决方案有哪几种?⭐⭐⭐
cors 跨域资源共享: 目前最常用的一种解决办法,通过设置后端允许跨域实现,类似后端给前端开了个后门一样,允许所有请求通过。通过 setHeader 设置 'Access-Control-Allow-Origin' 为通配符,允许所有请求通过。res.setHeader('Access-Control-Allow-Origin', '*');
nodejs 中间件代理跨域: 原理是让请求发给代理服务器,静态页面和代理服务器是同源的,然后代理服务器向后端服务器发请求,服务器和服务器之间不存在同源限制。具体实现:在vue.config.js 文件下配置,首先开启本地服务器 devServer,更改端口号 port,设置代理 proxy,设置代理目标(baseURL),是否允许跨域,设置反向代理路径,在接口地址前添加反向代理路径。
jsonp: 只能发送 get 请求。 原理是 script 标签可以跨域请求资源,将回调函数作为参数通过 ”?“ 拼接在 url 中,后端收到请求,调用该回调函数,并将数据作为参数返回去,前端通过回调函数获取响应数据。注意设置响应头返回文档类型,应该设置成 javascript。
13、什么是浏览器内核?有什么用?有哪一些?
什么是浏览器内核?
浏览器最核心部分, Rendering Engine 渲染引擎,一般叫浏览器内核。负责对网页语法的解释并渲染网页,渲染引擎决定了浏览器如何显示网页的内容以及页面的格式信息。
有什么用?
浏览器内核对于浏览器而言,是基础,是依托,决定了网页的呈现的内容、格式以及效果。
有哪一些?
Trident内核:IE内核
Gecko内核: Firefox内核(火狐浏览器)
WebKit内核:Safari
Blink内核: Chrome(基于webkit,Google与Opera Software共同开发)
presto内核: Opera(已改用Google Chrome的Blink内核)
14、什么是浏览器兼容问题?
因为不同浏览器的浏览器内核不一样,所以对同一段代码有不同的解析,造成页面显示效果不统一的情况。在大多数情况下,我们的需求是,无论用户用什么浏览器来查看我们的网站或者登陆我们的系统,都应该是统一的显示效果。
15、优雅降级和渐进增强(浏览器兼容问题)
渐进增强: 一开始就针对低版本浏览器进行构建页面,完成基本的功能,然后再针对高级浏览器进行效果、交互、追加功能达到更好的体验。
优雅降级: 一开始就构建站点的完整功能,然后针对浏览器测试和修复。比如一开始使用 CSS3 的特性构建了一个应用,然后逐步针对各大浏览器进行 hack 使其可以在低版本浏览器上正常浏览。
其实渐进增强和优雅降级并非什么新概念,只是旧的概念换了一个新的说法。在传统软件开发中,经常会提到向上兼容和向下兼容的概念。渐进增强相当于向上兼容,而优雅降级相当于向下兼容。
16、http 和 https 有何区别?
http: HTTP 协议运行在 TCP 之上。所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。
https: HTTP 运行在 SSL/TLS 之上,SSL/TLS 运行在 TCP 之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。此外客户端可以验证服务器端的身份,如果配置了客户端验证,服务器方也可以验证客户端的身份。
17、常见的HTTP状态码?
2开头(请求成功)
200 (成功) 201 (已创建) 202 (已接受) 203 (非授权信息)
204 (无内容) 205 (重置内容) 206 (部分内容)
3开头 (请求被重定向)
300 (多种选择) 301 (永久移动) 302 (临时移动) 303 (查看其他位置)
304 (未修改) 305 (使用代理) 307 (临时重定向)
4开头 (请求错误)
400(错误请求) 401(未授权) 403(禁止) 404(未找到) 405(方法禁用)
406(不接受) 407(需要代理授权) 408(请求超时) 409(冲突) 410(已删除)
411(需要有效长度) 412(未满足前提条件) 413 (请求实体过大)
414(请求的 URI 过长) 415 (不支持的媒体类型) 416 (请求范围不符合要求)
417(未满足期望值)
5开头(服务器错误)
500 (服务器内部错误) 501 (尚未实施) 502 (错误网关)
503 (服务不可用) 504 (网关超时) 505 (HTTP 版本不受支持)
18、说一说前端性能优化手段?⭐⭐⭐
前端性能优化分为两类,一类是文件加载更快,另一类是文件渲染更快。
加载更快的方法:
让传输的数据包更小:图片压缩和文件压缩。
减少网络请求的次数:雪碧图/精灵图、节流防抖。
减少渲染的次数:缓存(HTTP缓存、本地缓存、Vue 的 keep-alive 缓存等)。
渲染更快的方法:
提前渲染:ssr 服务器端渲染。
避免渲染阻塞:CSS 放在 HTML 的 head 中,JS 放在 HTML 的 body 底部。
避免无用渲染:懒加载。
减少渲染次数:对 dom 查询进行缓存、将 dom 操作合并、使用减少重排的标签。
19、网站性能优化的好处?怎么优化?
好处:
用户角度: 优化能够让页面加载得更快、对用户的操作响应得更及时,能够给用户提供更为友好的体验。
服务商角度: 优化能够减少页面请求数、或者减小请求所占带宽,能够节省可观的资源。
怎么优化?
页面级别的优化:HTTP请求数、脚本的无阻塞加载、内联脚本的位置优化等
代码级别的优化:js 中的 DOM 操作优化、CSS选择符优化、图片优化以及 HTML结构优化等。
页面级优化:
JavaScript 压缩和模块打包
按需加载资源
在使用 DOM 操作库时用上 array-ids
缓存
启用 HTTP/2
应用性能分析
使用负载均衡方案
为了更快的启动时间考虑一下同构
使用索引加速数据库查询
使用更快的转译方案
避免或最小化 JavaScript 和 CSS 的使用而阻塞渲染
用于未来的一个建议:使用 service workers + 流
图片编码优化
20、axios的拦截器原理及应用?
应用场景: axios 为开发者提供了这样一个 API:拦截器。拦截器分为请求拦截器和响应拦截器。请求拦截器:在接口请求之前做的处理,比如为每个请求带上相应的参数,像 token、时间戳等。 返回拦截器:在接口返回之后做的处理,像对返回的状态进行判断,例如判断 token 是否过期。
21、创建 ajax 过程?
创建XHR对象:new XMLHttpRequest()。
设置请求参数:request.open(Method, 服务器接口地址)。
发送请求:request.send(),如果是 get 请求不需要参数,post 请求需要参数request.send(data)。
监听请求成功后的状态变化:根据状态码进行相应的处理。 XHR.onreadystatechange = function(){
if(XHR.readyState == 4 && XHR.status == 200){
console.log(XHR.responseText);
}
}
22、说一下 fetch 请求方式?
fetch 是一种 HTTP 数据请求的方式,是 XMLHttpRequest 的一种替代方案。Fetch 函数就是原生js,没有使用 XMLHttpRequest 对象。fetch() 方法返回一个 Promise 解析 Response 来自 Request显 示状态(成功与否)的方法。
23、说一下浏览器如何渲染页面的?
浏览器拿到 HTML,先将 HTML 转换成 dom 树,再将 CSS 样式转换成 stylesheet,根据 dom 树和 stylesheet 创建布局树,对布局树进行分层,为每个图层生成绘制列表,再将图层分成图块,紧接着光栅化将图块转换成位图,最后合成绘制生成页面。
24、说一下有什么方法可以保持前后端实时通信?
轮询: 是客户端和服务器之间会一直进行连接,每隔一段时间就询问一次。其缺点明显:连接数会很多,一个接受,一个发送。而且每次发送请求都会有 Http 的 Header,会很耗流量,也会消耗CPU 的利用率。优点 就是实现简单,无需做过多的更改。缺点 是轮询的间隔过长,会导致用户不能及时接收到更新的数据。轮询的间隔过短,会导致查询请求过多,增加服务器端的负担。
长轮询: 对轮询的改进版,客户端发送 HTTP 给服务器之后,如果没有新消息,就一直等待。有新消息,才会返回给客户端。在某种程度上减小了网络带宽和CPU利用率等问题。
iframe流方式: 在页面中插入一个隐藏的 iframe,利用其 src 属性在服务器和客户端之间创建一条长连接,服务器向 iframe 传输数据,来实时更新页面。优点 是消息能够实时到达,浏览器兼容好。缺点 是服务器维护一个长连接会增加开销,IE、chrome、Firefox 会显示加载没有完成,图标会不停旋转。
WebSocket: 类似 Socket 的 TCP 长连接的通讯模式,一旦 WebSocket 连接建立后,后续数据都以帧序列的形式传输。在客户端断开 WebSocket 连接或 Server 端断掉连接前,不需要客户端和服务端重新发起连接请求。优点 是在海量并发和客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实时性优势明显。缺点 是浏览器支持程度不一致,不支持断开重连。
SSE: 建立在浏览器与服务器之间的通信渠道,然后服务器向浏览器推送信息。SSE 是单向通道,只能服务器向浏览器发送,因为 streaming 本质上就是下载。 优点 是 SSE 使用 HTTP 协议,现有的服务器软件都支持。SSE 属于轻量级,使用简单,SSE 默认支持断线重连。
轮询适用于小型应用,实时性不高。长轮询适用于一些早期的对及时性有一些要求的应用像web IM 聊天。iframe 适用于客服通信等。 WebSocket 适用于微信、网络互动游戏等。SSE适用于金融股票数据、看板等。
八、git 篇
1、git 是什么?⭐
Git是一个免费的、开源的分布式版本控制系统,git 支持分布式部署,可以有效、高速的处理从很小到非常大的项目版本管理。
分布式相比于集中式的最大区别在于开发者可以提交到本地,每个开发者通过克隆(git clone),在本地机器上拷贝一个完整的 Git 仓库。
我们可以在本地建一个版本库,每当我们需要修改时,就可以把之前的版本提交并标明此版的特点。这样文件夹里就只有一个编程文档了。当你需要哪个版本时,只要在版本库中恢复一下就可以了。
2、git 常用的命令?⭐
git clone 地址 :克隆
git status :检查文件所处的状态
git add . :提交到暂存区
git commit -m "描述" :提交到本地仓库
git push :推送到远程仓库
git pull :拉取远程仓库
git reset --hard 哈希值(前6位): 版本回退
git log :打印日志
git branch 分支名 :创建分支
git branch -a :查看所有分支
git checkout 分支名 (检出) :切换分支
git checkout -b :创建分支的同时切换到新分支
git branch :删除分支
git remote -v :查看当前路径
rm -r 文件名 :删除文件夹
3、git 和 svn 的区别?⭐⭐
Git是分布式版本控制工具,SVN是集中式版本控制工具。
Git没有一个全局的版本号,而SVN有。
Git和SVN的分支不同。
Git把内容按元数据方式存储,而SVN是按文件。
Git内容的完整性要优于SVN。
Git无需联网就可使用(无需下载服务端),而SVN必须要联网(须下载服务端)。因为Git的版本区就在自己电脑上,而 svn 在远程服务器上。
4、Git项目如何配置,如何上传至GitHub?
注册登录 github
创建 github 仓库
安装 git 客户端
绑定用户信息
设置 ssh key
创建本地项目以及仓库
关联 github 仓库
推送项目到 github 仓库
5、你们公司git分支是怎么管理的?⭐⭐
git 在开发中用来进行项目托管,项目中一般有三个分支,主分支master 只有项目负责人有操作权限,平时开发功能是在 develop 开发分支下创建的子分支也就是 feature 功能分支下开发的,功能开发完测试没问题了,切换到 develop 分支并拉取最新的 develop 分支,然后合并自己的任务分支并推送 develop 分支,再由项目负责人合并到 master 分支上。
6、git 工作流⭐
master:主分支(上线发布的稳定版)
dev(develop):开发分支
feature:功能分支(从 develop 拉取出来做新功能的分支)
fix:普通补丁分支(从 develop 拉取出来做修复bug的分支)
hotfix:维护分支(从 master 拉取出来的用于做紧急修复的分支,修复完后应马上合并回master 和 develop 分支)
release:预发布分支(从 develop 拉取出来的用于做测试的分支,检测完后合并到 master 分支发布,release 上做的 bug 修改要合并回 develop 分支)
6、git版本冲突是什么?⭐
所谓冲突指的就是,两个开发者对同一个文件同一个位置做出了不同的内容修改,然后在进行分支合并时或者是从远程仓库拉取代码到本地库时,就会产出冲突报错;两个不同的版本,导致 git 不知道要接受那个。
7、如何解决git版本冲突?⭐
寻找冲突: 首先要找到出现冲突的位置,通过 git merge 分支名 如果有冲突的话会提示那些文件有冲突。
修改冲突: 找到冲突文件对比差异,然后手动修改为正确的。如果使用命令的话,使用 cat 冲突文件 查看不同分支代码的差异,然后 vim 进入冲突文件进行删除修改正确代码。
提交修改后的冲突文件: 分别执行命令 git add 添加到暂存区、git commit -m 提交到本地库、最后 git push 推送到远程库就可以了。
九、功能的实现
1、vuex 实现购物车功能
原理: 比如购物车商品数量,在商品列表页面添加商品会更改这个值,在详情页增减商品数量会更改这个值,进入购物车删除商品也会更改这个值,通过提交 mutation 来更改这个值就很简单明了。当然也可以每次都调用获取购物车数量的接口,也可以实现,但每一次的 HTTP 请求都是对浏览器性能的消耗。
具体实现:
给加入购物车一个点击事件,并把当前点击商品的所有内容,即 item 作为参数,用来传参。
通过点击事件获取到商品参数,通过 vuex 的 this.$store.dispatch(’触发的函数’,‘参数’) 把商品参数传给 vuex 的 action 函数。
在 vuex 的 state 中初始化购物车
编写 action 里面的异步方法,获取传过来的商品参数 item,通过 context.commit 提交 mutation
编写 mutation 里面的逻辑,获取到传过来的商品参数后做一个判断去重,因为同一个商品,第一次点击是添加,第二次点击是商品数量的增加。遍历在 state 中定义保存到本地的数组,判断 state 中保存到本地的 id 和传过来的 id 是否一样,如果一样,当前的商品的数量加加。如果 id 不一样则是第一次点击添加商品,通过 push 将商品参数 item 和 复选框状态以及商品数量添加到本地数组,计算总价,遍历保存到本地的商品,如果选中的状态就计算总价,保存到本地。
从本地拿数据并渲染页面,从 vuex 中取值,获取到加购的商品参数和单价和总价,通过 computed 保存到本地,然后渲染购物车页面
点击单件商品上的复选框并同步底部的全选复选框,点击底部全选复选框并同步上面商品的复选框,并加加减减计算价钱,不管是选择单件商品还是全选还是减商品、加商品、删除商品都是通过 this.$store.dispatch 去调用 vuex 中的 action 里面的异步方法,通过 context.commit 触发 mutation 里面的相应逻辑。
点击商品前面的复选框:触发 mutation 里面的相应同步方法,首先先改变复选框的状态,false 改为 true ,true 改为 false。通过 every( ) 方法检测购物车商品数组里面的元素的复选框是否都选中(true),是的话返回 true 否则返回 false , 把值赋给全选按钮,同步全选按钮。然后遍历购物车商品数据,判断商品复选框是否选中,选中就计算商品总价,然后保存到本地。
点击底部全选按钮:触发 mutation 里面的相应同步方法,更改全选复选框状态,遍历购物车数据,把全选复选框状态赋值给每个商品的复选框状,让他们同步,遍历购物车所有数据,判断如果商品的复选框为 true,则计算总价,然后保存到本地。
减商品:触发 mutation 里面的相应同步方法,判断当前商品的件数是否大于1,大于1的话商品件数减一,然后遍历商品数据重新计算总价。保存商品数据到本地。
加商品:触发 mutation 里面的相应同步方法,商品件数加一,遍历商品数据,重新计算商品总价,然后保存商品数据到本地。
删除数据:触发 mutation 里面的相应同步方法,通过 splice 删除当前数据,遍历商品数据,重新计算商品总价,然后保存商品数据到本地
十、手撕代码
常见前端手写代码总汇(持续更新中...)-CSDN博客
十一、机试
十二、其他
1、自我介绍
2、说一下你是怎么样优化代码的
3、你是如何优化项目的
4、开发中都使用了哪些工具
5、开发中遇到过什么bug
6、你对加班的看法
7、为什么离开上一家公司
8、你是如何学习的