css
1. rem
rem是相对于根元素大小的一个单位, 他可以用来解决移动端适配的问题,应用媒体查询或者js动态检测设备的宽度,不同宽度下设置对应的根元素字体大小。根元素字体大小变化了,对应以rem为单位的元素也会跟着变化。在开发中也可以使用vw为单位来做适配。
2. 盒子居中
- display: flex; 然后通过justify-content: center, align-items: center
- 定位,父盒子相对定位,子盒子绝对定位, 给子盒子设置top: 50%, left: 50%, margin-top:-自身宽度的一半, margin-left: -自身宽度的一半 (或者直接transform: translate(-50%, -50%))
- 知道宽高就算下用margin挤过去
3. em ; rem 区别
- em是相对自身元素字体大小的一个单位 (em一般设置行高 文本缩进)
- rem是相对于根元素(html)字体大小的一个单位 (rem一般用来解决移动端适配)
4. BFC是什么
- BFC是块格式化上下文, 是一种特性(文档中的一块独立渲染区域), 拥有自己的渲染规则,可以决定子元素将如何排列, 以及和其它元素的相互关系和作用
- 有BFC特性的元素如: html, 浮动元素(元素的float不是none), 绝对或固定定位的元素, 行内块元素, 表格单元(元素的display: table-cell), overfow不等于visible
- BFC可以解决: 1. 清除浮动 2. 排除外部浮动带来的影响(文字环绕) 3. 阻止外边距重叠 4.解决margin塌陷
一. js基础
1. js的数据类型
值类型: 字符串(string) 、数字(number) 、布尔(boolean)、 空(null)、 未定义(undefined) 、symbol
引用数据类型: 对象(object) 数组(array) 函数(function)
2. 如何判断数据类型
typeof 可以检测number string boolean undefined symbol object function; 返回的结果是该类型的字符串(全小写形式)表示; 但是对于null 和 数组返回值都是object
instanceof 检测右边构造函数的原型对象在不在左边实例对象的原型链中,返回一个boolean值; 例如 [1, 2, 3] instanceof Object => true (只能检测引用类型, 对于基础数据类型检测不出来)
constructor 判断方法跟instanceof相似, 但是consturctor可以处理基础数据类型, 不仅仅是对象 注意: null 和 undefined 没有 constructor 判断方式: '1'.constructor === String // true new Number(1).constructor === Number // true ; 原理是根据原型对象的constructor属性指向构造函数来判断的.
Object.prototype.toString.call() 万能检测数据类型 返回[Object 数据类型] 原理: 原型链的终点是Object数以所有数据都可以用它的toString方法, 但是有些数据的原型对象上也有toString方法;为防止覆盖所以直接用Object.prototype.toString调用然后.call改变this的指向
还有像Array.isarray()
还可以自己写个正则判断一下
3. 事件委托
- 事件委托,就是把给子元素绑定的事件。统一委托(绑定)到祖先元素身上
- 原理: 事件冒泡
- 能提高性能, 对后续新增的元素同样具有事件绑定效果
- 可以用自定义属性按需触发
- 但是事件委托在现在前端框架的时代已经过时了,很少使用,在之前jquery的时代还有应用场景
4. 函数传参简单数据类型和复杂数据类型的qubie
-
简单数据类型,在函数内部修改它的值不会影响外部变量本身
-
复杂数据类型,在函数内部修改它的属性(参数内容)会影响外部数据, 但是直接修改这个数据的地址不会影响外面的数据(例如对它重新赋值)
二、js高级
3. js中的继承
原型链继承 借用构造函数继承 组合继承 原型式继承 寄生式继承 寄生组合式继承
4. 原型链
构造函数在创建的时候,就会有一个原型对象,new出来一个实例对象后这个实例对象有一个__proto__指向构造函数的原型对象,但是这个原型对象它也是一个对象也有自己的__proto__指向自己的原型,这样一层层向上找的过程形成了一个链式结构. 这个链式结构就叫做原型链.
5. 闭包
闭包就是内层函数访问了外层函数的变量,就叫做闭包; 闭包的用处就是可以延长作用域链;让函数内部的变量可以被外面访问到; 可以让变量一直保存在内存中不被回收(这就会造成内存泄漏);
6. 如何判断this的指向
以普通函数调用 this指向window
以方法的形式调用this指向调用方法的那个对象
以构造函数的形式调用 this指向创建出来的实例对象
箭头函数没有this, 它的this取决与外部环境
call apply bind 可以改变this的指向 this指向第一个参数; call apply bind的区别, 执行机制: call apply会立即执行; bind不会立即执行 而是返回一个修改this之后的新函数; 传参方式不一样, call bind是单个传参 用逗号隔开, apply是数组或伪数组传参
call,apply,bind在开发中我用得比较少,但是我见过他们的代码,例如:object.prototype.toString.call()检测数据类型;Math.max.apply去求数组最大值
7. 数组常用api
shift unshift push pop includes splice join find sort
遍历数组: every some findexof indexof filter map forEach reduce
8. reduce方法怎么用
reduce一般用于数组求和, 它可以接收两个参数, 第一个是一个回调函数, 第二个是初始值.这个方法会为数组的每项元素执行一次回调; 回调函数可以接收4个参数 上次回调的返回值 第二个代表数组的每项元素(当前要执行的那项元素), 数组下标, 数组; reduce最后的返回值是回调函数最后一次的返回值
9. 回调地狱
函数作为参数层次嵌套
10. Promise
promise是es6新增的一个新特性,它是一个函数,一般我们当构造函数使用; 就是需要new来得到一个promise的实例对象. 这个实例对象有三个状态,pending(进行中), fulfilled(已成功), rejected(已失败); 通常通过.then抛出成功.catch抛出失败。promise主要解决了回调地狱的问题。但是写起来代码还是不够优雅,通常开发中我用async await来配合promise去简化代码和解决回调地狱的问题
async await是es7 出来的新特性。async是用来声明一个promise的函数, await是用来等待一个promise函数的处理并抛出它的处理结果
11. new背后做了哪些事
创建一个空对象
this指向这个对象
给这个对象赋值
返回这个对象
12. for in 和 for of的区别
for in 可以遍历数组和对象 for of 只能遍历数组
for in 遍历出来的是key 和 下标, for..of遍历出来的是value
for in 还可以遍历出原型链中的属性
for of 如果想遍历对象,可以结合Object.keys()搭配使用
13. 递归
递归就是函数内部自己调用自己就是递归, 应用场景树形组件
14. eventloop事件循环:
js事件分为两种: 宏任务和微任务
宏任务包括: script, setTimeout, setInterval
微任务: Promise.then, process.nextTick(node中)
事件循环: js执行机制每次会挑选一个宏任务执行,每个宏任务对应的都会有自己的微任务队列,在执行这个宏任务过程中同步代码会先执行。异步代码就会放到对应的任务队列;像宏任务加载对应的程序中,满足条件就会进入到消息队列中;微任务会进入到微任务的队列中,待同步任务执行完毕后,事件循环会去到当前宏任务的微任务队列中找到所有的微任务一次性全部执行完毕,这就算是完成了一个事件循环;然后事件循环会去到消息队列中找是否有满足条件的宏任务继续执行上面的动作。
15. 防抖和节流
防抖和节流 都是用来优化性能的一种表现
防抖就是持续触发它不执行,而是不触发的一段时间之后才执行最后一次
节流就是持续触发一段时间内只执行一次
防抖的在开发中用的比较多的就是input框的一个输入事件
节流的在开发中我做的最多的就是鼠标滚动到最低端然后向后台请求数据
16. 箭头函数和普通函数的区别
- 箭头函数没有this , 没有this就不能当构造器,就没有自己的prototype;也不能调用call,apply,bind去修改this,可以调用这些方法,但是第一个参数会被忽略
- 箭头函数可以简写
- 箭头函数内部没有aguments,可以使用"..."拿到所有实参的集合数组
- 箭头函数不能用作Generator函数, 不能使用yeild关键字(function*)
Gennerator函数就是function* 内部可以使用yield关键字
17. 图片懒加载的原理
图片懒加载时常见的性能优化手段:
核心思路:
1. 开始时,不设置src属性
2. 当图片可见时,设置src,加载图片
如何判断图片在不在可见区域内,是个难点,以前我们要通过一系列的计算去判断图片是否可见,现在浏览器有一个api,IntersectionObserver,可以去判断元素是否可见.
开发中我会封装一个自定义指令,因为我是用的vue的技术栈,所以我会结合vueuse中的useIntersectionObserver方法实现懒加载
三、HTTP系列
1. http和https的区别
http是超文本传输协议, 在互联网上所有的文件都要遵守这个HTTP协议,就是用来实现客户端与服务端相互请求
Https是http的安全版本,也叫超文本安全传输,是基于http超文本传输的基础上在http与tcp之间加了一个特殊的ssl加密
从http和https的描述来看,https比http更加安全,https是需要申请购买证书,他们占用的端口号也不同,https是443的端口号,http是80的端口
2. 常见http状态码
-
1XX 信息响应 收到并理解请求, 请求处理将继续
-
2xx : 请求ok
200 : 请求成功 204 : 一般出现在post请求,表示数据继续传输
-
3xx : 重定向(服务器主动修改浏览器网址)
302 : 服务端重定向
-
4xx : 前端出问题
400 : 常见于参数错误 401: 当前用户需要验证(一般token过期) 403 : 没有权限(没有登录),禁止访问 404 : url错误 405: 请求方法不被允许
-
5xx : 服务器问题
500 : 服务器内部错误
3. 从输入url到网页展示的过程
1. dns域名解析
2. tcp三次握手 (建立稳定有效的传输机制)
3. 浏览器向服务器发送http请求;
4. 服务器处理请求,并返回http响应报文。
5. 浏览器拿到响应报文后解析,渲染页面
6. 断开连接: tcp四次挥手
4. tcp三次握手
-
第一次由浏览器发起,告诉服务器,我要发送请求了,你准备接收 (第一次就证明浏览器发送请求ok)
-
第二次由服务端发起,服务器接收到浏览器请求,然后告诉浏览器,你的请求我收到了,你发吧; (这就能证明服务端接收和响应功能ok)
-
第三次由浏览器发起,告知服务器,好的,我开始发了,你准备接收;(这个就能证明浏览器接收功能ok)
tcp三次握手的意义就是建立安全可靠的传输机制
5. tcp四次挥手
1. 第一次挥手由浏览器发起, 送给服务端, 我请求报文发完了,你准备关闭吧
2. 第二次挥手由服务端发起, 告诉浏览器, 请求报文我接收完了,我准备关闭了,你也准备吧
3. 第三次挥手: 由服务器发起, 告诉浏览器, 我响应报文发完了,你准备关闭吧
4. 第四次挥手由浏览器发起: 告诉服务器,我响应报文接收完了,我准备关闭了, 你也准备关闭吧
6. get请求和post请求的区别
1. 传参方式不一样
get请求传参直接是url后面拼接
post请求是请求体传参
2. 传参速度不同
get比post快
3. 数据大小不同
get传参有大小限制, 主要是浏览器对url长度的限制,不同浏览器不一样
post因为是请求体传参没有打下限制
4. 安全性不同
因为get请求是直接在URL后面拼接传参,所以参数都是暴漏在外的.就没有post请求安全
7. http协议请求消息Request的三个内容
起始行 消息头 和 消息体
8. 请求头中contentTyp有什么用处
让服务端对post请求中请求体的数据进行解码, 获取到对应的数据格式
9. cookie 和 sessionStorage , localStorage
cookie:
- cookie是网站为了标识用户身份而储存在用户本地终端上的数据,通常是经过加密的
- cookie始终会在同源的http请求头中携带(不管需要还是不需要), 在浏览器和服务器间来回传递
- cookie会有大小限制, 一般是4k
- cookie在设置的过期时间之前一直有效(这个一般是后端设置),不管浏览器是否关闭
sessionStorage:
临时存储,存在内存中, 浏览器关闭就没有了,大小5M
localStorage :
永久存储,存在硬盘中,不手动清除会一直存在,大小5M
localStorage, sessionStorage 不会自动把数据发给服务端仅在本地保存
三、Vue
1. 单个组件的生命周期钩子
beforeCreate => created => beforeMount => Mounted => beforeUpdate => Updated => beforeDestory => Destoryed
2. 页面打开父子组件的生命周期钩子顺序
父beforeCreate => 父Created => 父beforeMount => 子beforeCreate => 子Created => 子beforeMount => 子Mounted = 父 Mounted
3. 更新时父子组件的生命周期钩子顺序
父beforeUpdate => 子beforeUpdate => 子Updated => 父Updated
4. 哈希路由和history路由的区别
- 它们都是用来实现前端路由的; (前端路由就是页面地址和组件之间对应的关系); hash带#号,history不带#号
- 原理上:
- hash是通过监听hashChange事件做的处理
- history模式是利用了H5新增的History相关API实现的,例如popstate事件,pushState replaceState等方法
- 刷新时对后端的表现:
- hash值不会被带到http请求中,所以hash值对服务端安全无用,
- history模式在刷新时,history地址对于服务器来说是一个新的请求,所以需要后端做一些支持,否者会404
5. 哈希路由和history路由原理:
hash模式: 在浏览器中符号"#", #以及#后面的字符称之为hash,用window.localtion.hash读取. 特点: hash虽然在url中,但不被包括在Http请求中; 用来指导浏览器动作,对服务端安全无用, hash改变不会重新加载页面
history模式: history采用HTML5的新特性; 且提供了两个新方法: pushState(), replaceState()可以对浏览器历史记录栈进行修改,以及popState事件监听到状态改变
6. 组件之间的通信方式
Vue2 组件通信方式:
- props
- $emit / v-on
- .sync
- v-model
- ref
- parent
- listeners
- provide / inject
- EventBus
- Vuex
- $root
- slot
7. Vuex如何使用
vuex是一个全局状态管理的js库,它实现全局页面和组件数据的共享,解决了非关系的组件之间的数据传递问题。
vuex中常见的有5个配置项, state mutation action getters modules
state: 是存放数据的。state中的数据是有响应式的,数据变化了,页面中使用到这些数据的元素也会跟着改变
mutation: 一般写同步代码,可以用来更改state中的数据项 用commit调用; mutation中如果写了异步代码虽然代码正常能跑, 但是会不好维护,然后eslint会报错
action: 可以发异步请求, 一般这里发请求之后用context.commit调mutation中的方法更改state中的数据;action中的方法用dispatch调用
getters: 就是vuex的计算属性
modules: 可以将vuex模块化
像全局的状态管理器我们在vue3中可以用pinia来代替vuex
使用流程: 先下包, 然后建store文件夹, 文件夹中一般有index.js文件, 在文件引入vue, 引入vuex, 然后将vuex注册为vue的插件,然后new vuex.Store实例对象,在向外暴漏这个对象, 在main.js总引入这个对象,并将这个对象注入到new vue的实例对象中
8. paina
vue3中 ,跟vuex功能一样, 也是实现全局页面和组件的数据共享
paina 没有mutation 所有的异步同步代码都可以在action中
使用流程: 也是先小包, 然后再main.ts中从pinia引入createpinia, createpinia创建一个pinia对象 ,将这个对象注册为 app的插件
import { createApp } from 'vue'
import App from './App.vue'
import { createPinia } from 'pinia'
const app = createApp(APP)
const pinia = createPinia()
app.use(pinia)
然后也是创建一个Store文件夹, 文件夹中可以有很多ts文件, 在ts文件中引入defineStore, 创建defineStore对象,并向外暴漏这个对象, 对象中可以有state, action 和 getters属性
在要用的地方引入, 然后以函数的形式调用,返回这个defineStore里面的对象;
// 以上图为例,
import home from '文件路径'
const home = home()
9. vue 路由钩子beforeEach的参数
- to : Route: 要去哪里, 即将要进入的路由对象
- from: Route: 从哪里来, 当前导航要离开的路由
- next: Function: (中断或者跳转)
10. vuex的getter的作用
getter就是Store的计算属性
11 mutation和action的使用区别
- mutation 一般处理 同步的代码 mutation中的方法有两个参数,第一个是state对象, 第二个是一个形参,commit调用这个方法时可以传入一个实参给这个形参. mutation中的方法可以直接修改state中的数据
- action可以处理异步的请求, action中的方法也是两个参数, 一个是与store实例具有相同方法和属性的context对象. 另一个是一个形参,dispath调用方法时可传入一个实参给这个形参. action不能直接修改state中的数据, action要修改state中的数据可以通过context.commit调用mutation中的方法来修改state中的数据
12. key的作用
key的作用是为了更高效的对比虚拟dom中两个节点是不是相同的节点, 是用来提高diff算法的性能表现. 更具体一点, vue在 patch过程中判断两个节点是否是相同节点,key值是一个必要条件.
key一般在开发中是做v-for循环的时候使用
key的作用是为了高效的更新虚拟DOM, 在vue的源码中sameVNode对比新旧虚拟dom时,key值是最先拿来比较的,如果key值不一样,那么两个两个节点肯定不是同一个.
在使用v-for循环时, 尽量避免直接使用数组小标作为key值, 因为他们在做删除操作时 可能会导致渲染异常;
key值最好是唯一的值, 可以将一项与另一项区分开
13. vue2的指令有哪些
1、v-model多用于表单元素实现双向数据绑定(同angular中的ng-model)
2、v-for格式:v-for="字段名in(of)数组json"循环数组或json(同angular 中的ng-repeat),需要注意从Vue2开始取消了$index
3、v-show显示内容(同angular中的ng-show)
4、v-hide隐藏内容(同angular中的ng-hide)
5、v-if显示与隐藏(
dom元素的删除添加同angular中的ng-if默认值为false)
6、v-else-if必须和v-if连用
7、v-else必须和v-if连用不能单独使用否则报错模板编译错误第87页共165页
8、v-bind动态绑定作用:及时对页面的数据进行更改
9、v-on:click给标签绑定函数,可以缩写为@,例如绑定一个点击函数函数 必须写在methods里面
10、v-text解析文本
11、v-html解析html标签
12、v-bind:class三种绑定方法1、对象型'{red:isred}'2、三元型 'isred?"red":"blue"'
13、v-once进入页面时只渲染一次不在进行渲染
14、v-cloak防止闪烁
15、v-pre把标签内部的元素原位输出
14. vue2 和 vue3 的区别
- 生命周期的改变(setup代替了之前的beforeCreate 和 cteated, 其它生命周期名字有些变化, 但是功能都是没有变化的), proxy 代替了definePropety
- 性能方面的改变(diff算法的提升)
- 新增的compositionAPI(组合式API)
- v-model的变化
vue3中 当在自定义组件中使用
v-model
时,组件接收一个属性modelValue
的值,然后通过触发update:modelValue
事件来更新该值:
15. vue2 中$nextTick
等待页面dom更新循环结束后执行延迟回调. 在修改数据之后立即使用这个方法,获取更新之后的DOM
16. vue2中的响应式原理
主要是做了这么几件事: 数据劫持、依赖收集、派发更新
- 数据劫持: new Vue的时候遍历data对象, 用Object.definePropery给所有属性加上getter和setter
- 依赖收集: render的过程中,会触发数据的getter, 在getter的时候把当前的依赖(watcher)对象收集起来
- 派发更新: setter的时候,遍历数据的依赖对象(watcher对象), 进行更新
V2 的问题
-
深度监听是需要一次性递归,不管数据有没有用到,都给给他们加上get和set,浪费性能
-
无法监听对象新增和删除的对象(新增和删除的属性不具有响应式)(通过this.delete)
-
考虑性能方面的问题vue2不支持通过索引对数组元素的修改,而是通过封装了7个数组的api
v3中就对这些问题进行了优化,就不存在上面的问题 1.原理上不在是通过object.definePropery,而是通过proxy直接代理整个对象,proxy这个方法是通过惰性的方法给属性加上set和get的,就是只有调用的时候加,没调用就不会加,所以后续添加的属性也可以具备响应式; proxy也支持对删除操作的拦截; 因为是惰性添加响应式的 所以不存在性能方面的问题,proxy支持通过索引去修改数组
17. Vue中的事件修饰符
- prevent: 阻止默认事件
- stop: 阻止事件冒泡
- once: 事件只触发一次
- capture: 使用事件的捕获模式
- self: 只有event.target是当前操作的元素是才触发事件
- passive: 事件的默认行为立即执行,无需等待事件回调执行完毕
18. diff算法
1. 同级比较
4个指针: 头与头, 尾与尾, 尾与头, 头与尾
没有匹配到就在节点里遍历
2. 深度优先
19. 虚拟DOM
虚拟dom本质就是一个js对象. 为什么要引入虚拟dom
-
虚拟dom可以提高性能
浏览器的真实dom属性特别多,很大,虚拟dom相对于真实dom来说,属性会少很多.然后在更新的时候,虚拟dom会有一个新旧的虚拟dom对比,对比后会最小化的局部更新真实dom。
-
虚拟dom还可以实现跨平台的能力
虚拟dom抽象了原本的渲染过程,从而实现跨平台的能力,不在局限于是浏览器的dom,而可以是安卓 IOS的原生组件,也可以是小程序。
20. v-if 和 v-for 为什么不能一起使用
v-if 和 v-for 的优先级在v2 和 v3 中 优先级不一样, 一般不建议一起使用, 如果真的有这方面的需求 其实是有其它的一些替代方案的. 比如要先循环再做if判断 我们可以在计算属性中算出来要做循环的项 再对计算属性做for循环
比如要先做判断再做循环,可以用temeplete对要循环的项包裹起来做一个v-if判断 再里面v-for循环做渲染
21. watch, computed, methods区别
- computed 计算属性是根据已有的数据进行计算,得到一个新值. 这个值具有缓存性, 只要依赖项不变,就不会重新调计算,而是从缓存中取值, 但是computed不能执行异步的操作.
- methods 中的方法也可以根据已有属性计算出一个值,但是没有缓存,调用一次就会执行一次,性能相对于computed要低一些; 开发中一般将事件的回调函数写在methods中
- watch侦听属性,一般用来侦听vue实例中的变量的变化,根据变化做一些副作用相关的操作,例如发请求, watch在开发中经常用来侦听路由的变化
22. 子组件到达生命周期的某个阶段,父组件做一些相关的操作
1.通过自定义事件, 在子组件对应的生命周期钩子中通过emit触发父组件的自定义事件 2.v3中@vnode-生命周期钩子="回调函数",v2中是@hook:生命周期钩子="回调函数"
23. 路由守卫
路由守卫可以分为三大类, 共7个路由守卫
-
全局路由守卫
beforeEach 前置守卫
affterEach 后置守卫
beforeResolve 解析守卫
-
组件的守卫
beforeRouterEnter 进入组件之前, 组件没有创建在created之前
beforeRouterUpdated 路由更新但是内容不会改变
beforeRouterLeave 离开组件之前, 此时进入组件, 但是没离开
-
路由独享守卫
beforeEnter 读取路由的信息
开发中用得比较多的就是beforeEach 前置守卫, 在这里做过一些权限的控制,和token的判断