面试章

379 阅读32分钟

js

0、DOM与BOM

1、面向对象

  • 封装:也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
  1. 通过构造函数添加
  2. 通过原型prototype
  3. 在类的外部通过.语法添加
  • 继承:通过继承创建的新类称为“子类”或“派生类”。继承的过程,就是从一般到特殊的过程。
  1. 类式继承:所谓的类式继承就是使用的原型的方式,将方法添加在父类的原型上,然后子类的原型是父类的一个实例化对象,但各个是实例化子类相互影响
  2. 构造函数继承:造函数继承的核心思想就是SuperClass.call(this,id),直接改变this的指向,使通过this创建的属性和方法在子类中复制一份,因为是单独复制的,所以各个实例化的子类互不影响。但是会造成内存浪费的问题
  3. 组合式继承:所以组合式继承就是汲取两者的优点,即避免了内存浪费,又使得每个实例化的子类互不影响
  4. 寄生组合继承:我们先给父类的原型创建一个副本,然后修改子类constructor属性,最后在设置子类的原型就可以了
  • 多态:对象的多功能,多方法,一个方法多种表现形式。

null和undefined区别

Undefined类型只有一个值,即undefined。当声明的变量还未被初始化时,变量的默认值为undefined。用法:

  • 变量被声明了,但没有赋值时,就等于undefined。
  • 调用函数时,应该提供的参数没有提供,该参数等于undefined。
  • 对象没有赋值的属性,该属性的值为undefined。
  • 函数没有返回值时,默认返回undefined。

Null类型也只有一个值,即null。null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象。用法

  • 作为函数的参数,表示该函数的参数不是对象。
  • 作为对象原型链的终点。

JS作用域的理解

JS中的作用域分为两种:全局作用域和函数作用域。函数作用域中定义的变量,只能在函数中调用,外界无法访问。没有块级作用域导致了if或for这样的逻辑语句中定义的变量可以被外界访问,因此ES6中新增了let和const命令来进行块级作用域的声明。

2、内存泄漏

是指当一块内存不再被应用程序使用的时候,由于某种原因,这块内存没有返还给操作系统或者内存池的现象。内存泄漏可能会导致应用程序卡顿或者崩溃。

解决办法:

  1. 使用严格模式,避免不经意间的全局变量泄露
  2. 关注 DOM 生命周期,在销毁阶段记得解绑相关事件
  3. 避免过度使用闭包

3、深拷贝与浅拷贝

当我们把一个引入类型给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的

实现方式

  1. JSON.parse(JSON.stringify()) //乞丐
  2. 函数库lodash的_.cloneDeep方法
  3. jQuery.extend()方法
  4. 手写递归方法 递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝

4、闭包与冒泡理解

简单来说闭包就是在函数里面声明函数,本质上说就是在函数内部和函数外部搭建起一座桥梁,使得子函数可以访问父函数中所有的局部变量,但是反之不可以,这只是闭包的作用之一,另一个作用,则是保护变量不受外界污染,使其一直存在内存中,在工作中我们还是少使用闭包的好,因为闭包太消耗内存,不到万不得已的时候尽量不使用。

5、原型链

所有的JS对象都有一个prototype属性,指向它的原型对象。当试图访问一个对象的属性时,如果没有在该对象上找到,它还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。

6、this

7、es6

1.不一样的变量声明:const和let

  • let表示声明变量,而const表示声明常量,两者都为块级作用域;const 声明的变量都会被认为是常量,意思就是它的值被设置完成后就不能再修改了
  • 如果const的是一个对象,对象所包含的值是可以被修改的。抽象一点儿说,就是对象所指向的地址没有变就行
  • let 关键词声明的变量不具备变量提升(hoisting)特性
  • let 和 const 声明只在最靠近的一个块中(花括号内)有效
  • 当使用常量 const 声明时,请使用大写变量,如:CAPITAL_CASING
  • const 在声明时必须被赋值

2.模板字符串

  • 基本的字符串格式化。将表达式嵌入字符串中进行拼接。用${}来界定;
  • ES6反引号(``)

3.箭头函数

  • 不需要 function 关键字来创建函数
  • 省略 return 关键字
  • 继承当前上下文的 this 关键字

4.对象和数组解构

5.for...of 和 for...in

for...of 用于遍历一个迭代器,如数组 for...in 用来遍历对象中的属性

6.class类

ES6 中支持 class 语法,不过,ES6的class不是新的对象继承模型,它只是原型链的语法糖表现形式。

7.Promise

Promise 是异步编程的一种解决方案: 从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。 promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态) ;状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

promise是用来解决两个问题的:
  • 回调地狱,代码难以维护, 常常第一个的函数的输出是第二个函数的输入这种现象
  • promise可以支持多个并发的请求,获取并发请求中的数据
  • 这个promise可以解决异步的问题,本身不能说promise是异步的

引用

8、浏览器事件循环机制

事件循环机制从整体上告诉了我们 JavaScript 代码的执行顺序 Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

先执行 Script 脚本,然后清空微任务队列,然后开始下一轮事件循环,继续先执行宏任务,再清空微任务队列,如此往复。

macro-task大概包括(宏任务):

  • script(整体代码)
  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI render

micro-task大概包括(微任务):

  • process.nextTick
  • Promise
  • Async/Await(实际就是promise)
  • MutationObserver(html5新特性) 可以总结下浏览器的事件循环流程为:
  • 一开始整段脚本作为第一个宏任务执行
  • 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
  • 当前宏任务执行完出队,检查微任务队列,如果有则依次执行,直到微任务队列为空
  • 执行队首新的宏任务,回到第二步,依此循环,直到宏任务和微任务队列都为空

防抖与节流

防抖(debounce)

所谓防抖,就是指触发事件后 n 秒后才执行函数,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。

防抖函数分为非立即执行版和立即执行版。

节流(throttle)

所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。  节流会稀释函数的执行频率。

对于节流,一般有两种方式可以实现,分别是时间戳版和定时器版。

9、数组去重

  • 第一种方法遍历循环 最常用的办法,使用数组的indexOf()方法。
  • 第二种方法存放Hash对象 ,将数组所有的元素转变成对象的键名,利用对象键名的不可重复的特性来去重
  • 第三种方法排序比较 利用数组原生的sort()方法,将数组先进行排序,排序后比较相邻两个元素的值。
  • 第四种方法利用Set类型 开发环境支持ES6,这个方法是最简洁的。

10、js垃圾回收

11、call、apply、bind

三个函数的作用都是将函数绑定到上下文中,用来改变函数中this的指向;三者的不同点在于语法的不同 applycall的区别是call方法接受的是若干个参数列表,而apply接收的是一个包含多个参数的数组。 而bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列

13、Promise.all和Promise.race

  • Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值
  • Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

14、基本类型的判断

  • Typeof 优点:能够快速区分基本数据类型 缺点:不能将Object、Array和Null区分,都返回object
  • instanceof 优点:能够区分Array、Object和Function,适合用于判断自定义的类实例对象 缺点:Number,Boolean,String基本数据类型不能判断
  • Object.prototype.toString 优点:精准判断数据类型 缺点:写法繁琐不容易记,推荐进行封装后使用

15、数据类型

基本类型

  • Sting
  • Number
  • Boolean
  • null
  • undefined
  • Symbol 引入类型
  • Object
  • Date
  • function
  • RegExp
  • Array

16、数组操作

改变原数组

  • splice() 添加/删除数组元素
  • sort() 数组排序
  • pop() 删除一个数组中的最后的一个元素
  • shift() 删除数组的第一个元素
  • push() 向数组的末尾添加元素
  • unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度
  • reverse() 颠倒数组中元素的顺序
  • ES6: copyWithin() 指定位置的成员复制到其他位置
  • ES6: fill() 填充数组 使用给定值,填充一个数组

不改变原数组的方法

  • slice() 浅拷贝数组的元素
  • join() 数组转字符串
  • toLocaleString() 数组转字符串
  • toString() 数组转字符串 不推荐
  • cancat() 数组合并
  • indexOf() 查找数组是否存在某个元素,返回下标
  • lastIndexOf() 查找指定元素在数组中的最后一个位置
  • ES7 includes() 查找数组是否包含某个元素 返回布尔

遍历方法

for

  • forEach
  • every 检测数组所有元素是否都符合判断条件
  • some 数组中的是否有满足判断条件的元素
  • filter 过滤原始数组,返回新数组
  • map 对数组中的每个元素进行处理,返回新的数组
  • reduce 为数组提供累加器,合并为一个值
  • reduceRight 从右至左累加
  • ES6:find()& findIndex() 根据条件找到数组成员
  • ES6 keys()&values()&entries() 遍历键名、遍历键值、遍历键名+键值 # 【干货】js 数组详细操作方法及解析合集

16、箭头函数和普通函数区别

this指向问题:箭头函数的this指向的是父级作用域的,而普通函数是指向函数本身的 箭头函数不能用作构造器,和 new一起用会抛出错误 箭头函数没有原型属性不能作为构造函数

v8的垃圾回收

V8是目前主流的 JS 引擎,内存上有上限,采用新生代和老生代进行垃圾回收。新生代采用复制算法 + 标记管理的方式进行对象回收,用空间换时间。老生代是用的是标记清除、标记整理、增量标记算法进行对象回收,分批进行标记最后回收,提高了用户的体验

vue

MVC 和 MVVM 区别

MVC

MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范

  • Model(模型):是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据
  • View(视图):是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的
  • Controller(控制器):是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据
MVVM

MVVM 新增了 VM 类

  • ViewModel 层:做了两件事达到了数据的双向绑定 一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM 事件监听。

MVVM 与 MVC 最大的区别就是:它实现了 View 和 Model 的自动同步,也就是当 Model 的属性改变时,我们不用再自己手动操作 Dom 元素,来改变 View 的显示,而是改变属性后该属性对应 View 层显示会自动改变(对应Vue数据驱动的思想)

严格的 MVVM 要求 View 不能和 Model 直接通信,而 Vue 提供了$refs 这个属性,让 Model 可以直接操作 View,违反了这一规定,所以说 Vue 没有完全遵循 MVVM。

1、vue双向绑定

vue采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty劫持data属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

2、虚拟dom(vdom)

由于在浏览器中操作 DOM 是很昂贵的。频繁的操作 DOM,会产生一定的性能问题。这就是虚拟 Dom 的产生原因。Vue2 的 Virtual DOM 借鉴了开源库 snabbdom 的实现。Virtual DOM 本质就是用一个原生的 JS 对象去描述一个 DOM 节点,是对真实 DOM 的一层抽象。

3、diff算法

diff算法介绍

Diff算法是一种对比算法。对比两者是旧虚拟DOM和新虚拟DOM,对比出是哪个虚拟节点更改了,找出这个虚拟节点,并只更新这个虚拟节点所对应的真实节点,而不用更新其他数据没发生改变的节点,实现精准地更新真实DOM,进而提高效率

diff算法原理

Diff同层对比

新旧虚拟DOM对比的时候,Diff算法比较只会在同层级进行, 不会跨层级比较。 所以Diff算法是:深度优先算法

4、讲一下 vue 的生命周期和每个声明周期做的事情

  • beforeCreate(创建前) vue实例的挂载元素$el和数据对象 data都是undefined, 还未初始化
  • created(创建后) 完成了 data数据初始化, el还未初始化
  • beforeMount(载入前) vue实例的$el和data都初始化了, 相关的render函数首次被调用
  • mounted(载入后) 此过程中进行ajax交互
  • beforeUpdate(更新前)
  • updated(更新后)
  • beforeDestroy(销毁前)
  • destroyed(销毁后)

vue 常用指令

  • v-text:更新元素的 textContent。如果要更新部分的 textContent,需要使用 {{ Mustache }} 插值。
  • v-html:更新元素的 innerHTML注意:内容按普通 HTML 插入 - 不会作为 Vue 模板进行编译。如果试图使用 v-html 组合模板,可以重新考虑是否通过使用组件来替代
  • v-show:根据表达式之真假值,切换元素的 display CSS property。
  • v-if与v-else
  • v-for 循环 配合:key使用
  • v-on 绑定事件监听器 缩写@
  • v-bind 动态地绑 缩写:
  • v-model 在表单控件或者组件上创建双向绑定。细节请看下面的教程链接。
  • v-slot 插槽
  • v-once 只渲染一次
  • v-cloak 这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时,这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。

5、v-show 和 v-if 的区别

  1. v-if 通过dom节点存在与否来控制元素的显隐藏 v-show是通过dom元素的display样式来 显示或者隐藏
  2. v-if更高的切换消耗、v-show更高的初始渲染消耗
  3. 编译过程v-if切换有一个局部编译/卸载的过程,切换过程中合适低销毁和重建内部事件监听和子组件 。 v-show只是简单的机遇css切换

6、v-for 的 key 的作用

vue中列表循环需加:key="唯一标识" 唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM

v-for与v-if不一起是使用

原因:v-for比v-if优先,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候。

7、data 为什么用函数的方式写

组件中的data写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的data。如果单纯的写成对象形式,就使得所有组件实例共用了一份data,造成了数据污染。

怎么重置data

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象

this.$data获取当前状态下的data

this.$options.data()获取该组件初始状态下的data。

bject.assign(this.data,this.data, this.options.data())

vue常用的全局api

  • vue.nextTick 在修改数据之后立即使用这个方法,获取更新后的 DOM。
  • vue.set 触发视图更新
  • vue.directive 注册或获取自定义指令
  • vue.filter 注册或获取过滤器
  • vue.component 注册或获取组件
  • vue.use 安装 Vue.js 插件。如果插件是一个对象,必须提供 install 方法。如果插件是一个函数,它会被作install 方法。install 方法调用时,会将 Vue 作为参数传入。该方法需要在调用 new Vue() 之前被调用。当 install 方法被同一个插件多次调用,插件将只会被安装一次。
  • vue.mixin 混入

keep-alive

keep-alive 是 Vue 内置的一个组件,可以实现组件缓存,当组件切换时不会对当前组件进行卸载。

  • 常用的两个属性 include/exclude,允许组件有条件的进行缓存。
  • 两个生命周期 activated/deactivated,用来得知当前组件是否处于活跃状态。
  • keep-alive 的中还运用了 LRU(最近最少使用) 算法,选择最近最久未使用的组件予以淘汰。

8、computed 和 watch 的区别

  • computed 计算属性,如果一个数据需要经过复杂计算就用computed
  • watch 数据监听, 如果一个数据需要被监听并且对数据做一些操作就用 watch

两者区别

computed

  • computed 监控的数据在 data 中没有声明
  • computed 不支持异步,当 computed 中有异步操作时,无法监听数据的变化
  • computed 具有缓存,页面重新渲染,值不变时,会直接返回之前的计算结果,不会重新计算
  • 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,一般使用 computed
  • computed 计算属性值是函数时,默认使用get方法。如果属性值是属性值时,属性有一个get和set方法,当数据发生变化时会调用set方法。 watch
  • 监测的数据必须在 data 中声明或 props 中数据
  • 支持异步操作
  • 没有缓存,页面重新渲染时,值不改变时也会执行
  • 当一个属性值发生变化时,就需要执行相应的操作
  • 监听数据发生变化时,会触发其他操作,函数有两个参数
  1. immediate :组件加载立即触发回调函数
  2. deep:深度监听,主要针对复杂数据,如监听对象时,添加深度监听,任意的属性值改变都会触发。 注意:对象添加深度监听之后,输出的新旧值是一样的。
  3. computed 页面重新渲染时,不会重复计算,而 watch 会重新计算,所以 computed 性能更高些。

子父组件传值的方式

父传子:通过属性传递

  1. 在父组件内通过属性传值,在引入子组件的标签上,自定义属性然后绑定上你所需要传输的值
  2. 在子组件内通过props属性进行接收
  3. 所接收的值不能直接修改

子传父:通过自定义事件进行传递

  1. 在父组件内引入子组件的标签上,自定义一个事件,如:@changVal=“changeVal
  2. 在子组件内,通过emit进行值传递,如this.emit进行值传递,如this.emit( “changeVal”, it, “other” ),第一个参数固定为定义的事件名,后面接着写所需要带上的参数, 可以一次往后排,也可以传入一个对象

Vuex

什么是vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理插件。它采用集中式存储管理应用的所有组件的状态,而更改状态的唯一方法是提交mutation,例this.$store.commit('SET_VIDEO_PAUSE', video_pauseSET_VIDEO_PAUSE为mutations属性中定义的方法 。

vuex有几种属性

  • state:vuex的基本数据,用来存储变量
  • getters:从基本数据(state)派生的数据,相当于store的计算属性;getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
  • mutations:提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数,提交载荷作为第二个参数
  • actions:和mutation的功能大致相同,不同之处在于:\
  • ① Action 提交的是 mutation,而不是直接变更状态;\
  • ② Action 可以包含任意异步操作。
  • modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,管理更为方便。

vuex工作原理

Vuex的双向绑定通过调用 new Vue实现,然后通过applyMixin方法,调用了Vue.mixin在所有组件的 beforeCreate生命周期注入了设置 this.$store这样一个对象。,再通过劫持state.get将数据放入组件中

11、$nextTick

在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新之后的DOM

12、 vue-router

路由模式

hash模式

即地址栏URL中的#符号,它的特点在于:hash 虽然出现URL中,但不会被包含在HTTP请求中,对后端完全没有影响,不需要后台进行配置,因此改变hash不会重新加载页面。

history模式

利用了HTML5 History Interface 中新增的pushState() 和replaceState() 方法(需要特定浏览器支持)。history模式改变了路由地址,因为需要后台配置地址

active-class

active-class是vue-router模块的router-link组件中的属性,用来做选中样式的切换

vue-router中push和replace的区别

  1. push会向history栈添加一个记录,点击后退会返回到上一个页面。
  2. replace不会向history里面添加新的记录,点击返回,会跳转到上上一个页面。上一个记录是不存在的

vue-router 传参

1、params传参

  • 只能用name,不能用path。
  • 地址栏不显示参数名称id,但是有参数的值。
  • 是刷新页面会消失、可以考虑用vuex+浏览器缓存的方式

2、query传参

  • name和path都能用。用path的时候,提供的path值必须是相对于根路径的相对路径,而不是相对于父路由的相对路径,否则无法成功访问。
  • 地址栏显示参数格式为?id=0&code=1

router与route的区别

$router:是 router 实例

通过 this.$router 访问路由器,相当于获取了整个路由文件,在$router.option.routes中,或查看到当前项目的整个路由结构 具有实例方法

$route:当前激活的路由信息对象

当前激活的路由信息对象。这个属性是只读的,里面的属性是 immutable (不可变) 的,不过可以 watch (监测变化) 它。通过 this.$route 访问的是当前路由,获取和当前路由有关的信息

14、axios

添加请求拦截器 添加响应拦截器

// 添加请求拦截器
axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
}, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
  });

axios.all,axios.spread

cancelToken

平时我们在开发项目过程中,可能会遇到这种场景,比如当一个请求出现问题之后,就取消所有的请求,原生提供了abort()这个方法。而axios也提供了取消请求的api ---- axios之cancelToken

取消重复请求

  • 首先我们要收集请求中的接口并判断哪些请求是重复请求,我们才能取消它,那么如何判断呢?很简单,只要是请求地址、请求方式、请求参数一样,那么我们就能认为是一样的。而我们要存储的队列里面的数据结构很明显应该是以键值对的形式来存储,这里面我们选择 Map 对象来操作。

  • 取消重复请求并出删除队列

  • 添加拦截器

vue3

Object.definePropety和Proxy区别

  • Object.defineProperty

    • 只能监听对象(Object),不能监听数组的变化,无法触发push, pop, shift, unshift,splice, sort, reverse。
    • 必须遍历对象的每个属性
    • 只能劫持当前对象属性,如果想深度劫持,必须深层遍历嵌套的对象
  • Proxy

    • Proxy可以直接监听对象而非属性
    • Proxy直接可以劫持整个对象,并返回一个新对象。
    • Proxy可以直接监听数组的变化
    • Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具备的。 、

vue添加全局的方法、组件

Vue规定引入的插件必须是对象或者函数。

  • 如果引入的插件是个对象:必须提供install方法,例如: element-ui组件库 , 需要使用Vue.use(),该方法默认会调用install方法; 
  •  如果引入的插件是个函数,不是按照vue规则设计(准确地说不是专门为VUE服务)里面没有install方法,例如:axios函数库。 那么就通过添加到vue原型链上的方式使用,它会被直接当作install函数执行。 vue添加自定义指令
  • Vue自定义指令和组件一样存在着全局注册和局部注册两种方式。先来看看注册全局指令的方式,通过 Vue.directive( id, [definition] ) 方式注册全局指令
  • 第一个参数为自定义指令名称(指令名称不需要加 v- 前缀,默认是自动加上前缀的,使用指令的时候一定要加上前缀
  • 第二个参数可以是对象数据,也可以是一个指令函数

钩子函数 一个指令定义对象可以提供如下几个钩子函数 (均为可选):

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置

  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)。

  • update:所在组件的 VNode 更新时调用,但是可能发生在其子 VNode 更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新 。

  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用。

  • unbind:只调用一次,指令与元素解绑时调用。

钩子函数的几个参数

  • el:指令所绑定的元素,可以用来直接操作 DOM。

  • binding:一个对象,包含以下 property:

    • name:指令名,不包括 v- 前缀。
    • value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
    • expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"
    • arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"
    • modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • vnode:Vue 编译生成的虚拟节点。移步 VNode API 来了解更多详情。

  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用

vue为什么需要路由懒加载

当刚运行项目的时候,发现刚进入页面,就将所有的js文件和css文件加载了进来,这一进程十分的消耗时间。 如果打开哪个页面就对应的加载响应页面的js文件和css文件,那么页面加载速度会大大提升。

Vue.set

了解 Vue 响应式原理的同学都知道在两种情况下修改数据 Vue 是不会触发视图更新的

1.在实例创建之后添加新的属性到实例上(给响应式对象新增属性)

2.直接更改数组下标来修改数组的值

Vue.set 或者说是$set 原理如下

因为响应式数据 我们给对象和数组本身都增加了__ob__属性,代表的是 Observer 实例。当给对象新增不存在的属性 首先会把新的属性进行响应式跟踪 然后会触发对象__ob__的 dep 收集到的 watcher 去更新,当修改数组索引时我们调用数组本身的 splice 方法去更新数组

keep-alive 使用场景和原理

keep-alive 是 Vue 内置的一个组件,可以实现组件缓存,当组件切换时不会对当前组件进行卸载。

  • 常用的两个属性 include/exclude,允许组件有条件的进行缓存。
  • 两个生命周期 activated/deactivated,用来得知当前组件是否处于活跃状态。
  • keep-alive 的中还运用了 LRU(最近最少使用) 算法,选择最近最久未使用的组件予以淘汰。

vue性能优化

  • 对象层级不要过深,否则性能就会差

  • 不需要响应式的数据不要放到 data 中(可以用 Object.freeze() 冻结数据)

  • v-if 和 v-show 区分使用场景

  • computed 和 watch 区分使用场景

  • v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if

  • 大数据列表和表格性能优化-虚拟列表/虚拟表格

  • 防止内部泄漏,组件销毁后把全局变量和事件销毁

  • 图片懒加载

  • 路由懒加载

  • 第三方插件的按需引入

  • 适当采用 keep-alive 缓存组件

  • 防抖、节流运用

  • 服务端渲染 SSR or 预渲染

css

1、css样式中元素隐藏的三种方式以及区别

  1. display:none 不占据空间、更改该属性时会引起DOM树结构的变化、页面重新布局---回流
  2. visibility:hidden 元素隐藏。仍然占据空间。更改该属性时不会引起DOM树结构变化、不会引起页面重新布局、只会按照css代码重新渲染-重绘
  3. opacity:0 元素隐藏、占空间、更改该属性时DOM树结构没有变化、不会引起页面重新布局、只可重新渲染-重绘

2、BFC

BFC(Block Formatting Context)格式化上下文,是Web页面中盒模型布局的CSS渲染模式,指一个独立的渲染区域或者说是一个隔离的独立容器。

BFC应用

  • 防止margin重叠
  • 清除内部浮动
  • 自适应两(多)栏布局
  • 防止字体环绕

触发BFC条件

  • 根元素
  • float的值不为none
  • overflow的值不为visible
  • display的值为inline-block、table-cell、table-caption
  • position的值为absolute、fixed

BFC的特性

  • 内部的Box会在垂直方向上一个接一个的放置。
  • 垂直方向上的距离由margin决定
  • bfc的区域不会与float的元素区域重叠。
  • 计算bfc的高度时,浮动元素也参与计算
  • bfc就是页面上的一个独立容器,容器里面的子元素不会影响外面元素。

3、CSS选择器及其优先级

  • !important
  • 内联样式style=""
  • ID选择器#id
  • 类选择器/属性选择器/伪类选择器.class.active[href=""]
  • 元素选择器/关系选择器/伪元素选择器html+div>span::after
  • 通配符选择器*

4、伪元素和伪元素的使用场景

css引入伪类和伪元素概念是为了格式化文档树以外的信息。也就是说,伪类和伪元素都是用来修饰不在文档树中的部分。

伪类

伪类存在的意义是为了通过选择器找到那些不存在DOM树中的信息以及不能被常规CSS选择器获取到的信息。

  1. 获取不存在与DOM树中的信息。比如a标签的:link、visited等,这些信息不存在与DOM树结构中,只能通过CSS选择器来获取;
  2. 获取不能被常规CSS选择器获取的信息。比如:要获取第一个子元素,我们无法用常规的CSS选择器获取,但可以通过 :first-child 来获取到。

伪元素

伪元素用于创建一些不在文档树中的元素,并为其添加样式。比如说,我们可以通过:before来在一个元素前增加一些文本,并为这些文本添加样式。虽然用户可以看到这些文本,但是这些文本实际上不在文档树中。常见的伪元素有:::before::after::first-line::first-letter::selection::placeholder

因此,伪类与伪元素的区别在于:有没有创建一个文档树之外的元素。

5、min-width、max-width、width的包含(优先级关系)关系

6、flex布局

采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。

父级容器属性

flex-direction属性

主轴为水平方向,起点在左端。row-reverse:主轴为水平方向,起点在右端。column:主轴为垂直方向,起点在上沿。column-reverse:主轴为垂直方向,起点在下沿。

flex-wrap属性

默认情况下,项目都排在一条线(又称"轴线")上。flex-wrap属性定义,如果一条轴线排不下,如何换行。

justify-content属性

定义了项目在主轴上的对齐方式。

align-items属性

align-items属性定义项目在交叉轴上如何对齐。

align-content属性

align-content属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。

子级容器属性

order属性

order属性定义子元素或者子容器的排列顺序。数值越小,排列越靠前,默认为0。

flex-grow属性

flex-grow属性定义子元素或者子容器的放大比例,默认为0,即如果存在剩余空间,也不放大

flex-shrink属性

flex-shrink属性定义了项目的缩小比例,默认为1,即如果空间不足,该项目将缩小

flex-basis属性

flex-basis属性定义了在分配多余空间之前,项目占据的主轴空间(main size)。浏览器根据这个属性,计算主轴是否有多余空间。它的默认值为auto,即项目的本来大小。

flex属性

flex属性是flex-growflex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选。

align-self属性

align-self属性允许单个项目有与其他项目不一样的对齐方式,可覆盖align-items属性。默认值为auto,表示继承父元素的align-items属性,如果没有父元素,则等同于stretch

css预处理

Sass是一种动态样式语言,Sass语法属于缩排语法,比css比多出好些功能(如变量、嵌套、运算,混入(Mixin)、继承、颜色处理,函数等),更容易阅读。

7、css的书写规范

样式属性顺序

  1. 定位:position z-index left right top bottom clip等。
  2. 自身属性:width height min-height max-height min-width max-width等。
  3. 文字样式:color font-size letter-spacing, color text-align等。
  4. 背景:background-image border等。
  5. 文本属性: text-align vertical-align text-wrap text-transform text-indent text-decoration  letter-spacing word-spacing white-space text-overflow等。
  6. css3中属性:contentbox-shadowanimationborder-radiustransform

rem 移动端布局原理

是指相对于根元素的字体大小的单位。简单的说它就是一个相对单位,实例

响应式布局的准则

  1. 内容区块可伸缩:内容区块的在一定程度上能够自动调整,以确保填满整个页面
  2. 内容区块可自由排布:当页面尺寸变动较大时,能够减少/增加排布的列数
  3. 边距适应:到页面尺寸发生更大变化时,区块的边距也应该变化
  4. 图片适应:对于常见的宽度调整,图片在隐去两侧部分时,依旧保持美观可用
  5. 内容能够自动隐藏/部分显示:如在电脑上显示的的大段描述文本,在手机上就只能少量显示或全部隐藏
  6. 导航和菜单能自动折叠:展开还是收起,应该根据页面尺寸来判断
  7. 字体比例缩放时,字体也保持缩放

浏览器

1、浏览器渲染机制

  • 构建DOM树(parse):渲染引擎解析HTML文档,首先将标签转换成DOM树中的DOM node
  • 构建渲染树(construct):解析对应的CSS样式文件信息
  • 布局渲染树(reflow/layout):从根节点递归调用,计算每一个元素的大小、位置等,给出每个节点所应该在屏幕上出现的精确坐标
  • 绘制渲染树(paint/repaint):遍历渲染树,使用UI后端层来绘制每个节点

2、重绘和重排的区别

重绘(repaint或redraw) :当盒子的位置、大小以及其他属性,例如颜色、字体大小等都确定下来之后,浏览器便把这些原色都按照各自的特性绘制一遍,将内容呈现在页面上。重绘是指一个元素外观的改变所触发的浏览器行为,浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。

重绘发生在元素的可见的外观被改变,但并没有影响到布局的时候。比如,仅修改DOM元素的字体颜色(只有Repaint,因为不需要调整布局)

重排(重构/回流/reflow) :当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建, 这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。

触发重排的条件:任何页面布局和几何属性的改变都会触发重排:

  • 页面渲染初始化(无法避免)
  • 添加或删除可见的DOM元素
  • 元素位置的改变,或者使用动画
  • 元素尺寸的改变——大小,外边距,边框
  • 浏览器窗口尺寸的变化
  • 填充内容的改变,比如文本的改变或图片大小改变而引起的计算值宽度和高度的改变

重排必定会引发重绘,但重绘不一定会引发重排。

3、网络攻击手段,XSS 和 CSRF 的原理,与防御手段

XSS概念

XSS,跨站脚本攻击,攻击者利用网站没有对用户提交数据进行转义处理或者过滤不足的缺点,进而添加一些代码,嵌入到web页面中去,从而盗取用户资料、利用用户身份进行某种动作或者对访问者进行病毒侵害的一种攻击方式

XSS防御

不信任任何客户端提交的数据,只要是客户端提交的数据就应该先进行相应的过滤处理然后方可进行下一步的操作

CSRF概念

跨站请求伪造,攻击方伪装用户身份发送请求从而窃取信息或者破坏系统

CSRF防御

  1. 重要数据交互采用POST进行接收,当然是用POST也不是万能的,伪造一个form表单即可破解
  2. 使用验证码,只要是涉及到数据交互就先进行验证码验证,这个方法可以完全解决CSRF。但是出于用户体验考虑,网站不能给所有的操作都加上验证码。因此验证码只能作为一种辅助手段,不能作为主要解决方案。
  3. 验证HTTP Referer字段,该字段记录了此次HTTP请求的来源地址,最常见的应用是图片防盗链。PHP中可以采用Apache URL重写规则进行防御
  4. 为每个表单添加token令牌并验证

4、浏览器地址栏输入地址后发生了什么

  1. 浏览器将url地址补充完整:没有书写协议,添加上协议
  2. 浏览器对url地址进行url编码:如果url地址中出现非ASCII字符,则浏览器会对其进行编码
  3. 浏览器构造一个没有消息体的GET请求,发送至服务器,等待服务器的响应,此时浏览器标签页往往会出现一个等待的图标
  4. 服务器接收到请求,将一个HTML页面代码组装到消息体中,响应给浏览器
  5. 浏览器拿到服务器的响应后,丢弃掉当前页面,开始渲染消息体的html代码。浏览器之所以知道这是一个html代码,是因为服务器的响应头指定了消息类型为text/html
  6. 浏览器在渲染页面的过程中,发现有其他的嵌入资源,如CSS、JS、图片等
  7. 浏览器使用不阻塞渲染的方式,重新向服务器发送对该资源的请求,拿到响应结果后根据Content-Type做相应处理
  8. 当所有的资源都已下载并处理后,浏览器触发window.onload事件

5、跨域

  1. CORS
  2. jsonp
  3. 服务器代理

6、GET和POST区别

请求方式GETPOST
参数位置参数拼接到url的后面参数在请求体中
参数大小受限于浏览器url大小,一般不超过32K1G
服务器数据接收接收1次根据数据大小,可分多次接收
适用场景从服务器端获取数据向服务器提交数据
安全性参数携带在url中,安全性低相对于GET请求,安全性更高

7、## HTTP与HTTPS的区别

  1. HTTP的URL由http://起始且默认使用端口80,而HTTPS的URL由https://起始且默认使用端口443
  2. HTTP是超文本传输协议,信息是明文传输,HTTPS则是具有安全性的 SSL 加密传输协议
  3. HTTP的连接很简单,是无状态的,HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 http 协议安全

8、三次握手

先讲下握手的过程:

1、第一次握手:客户端给服务器发送一个 SYN 报文。

2、第二次握手:服务器收到 SYN 报文之后,会应答一个 SYN+ACK 报文。

3、第三次握手:客户端收到 SYN+ACK 报文之后,会回应一个 ACK 报文。

4、服务器收到 ACK 报文之后,三次握手建立完成。

作用是为了确认双方的接收与发送能力是否正常。

第一次握手:客户端发送网络包,服务端收到了。这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。

第二次握手:服务端发包,客户端收到了。这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。

第三次握手:客户端发包,服务端收到了。这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。 # 关于三次握手与四次挥手面试官想考我们什么?--- 不看后悔系列

9、浏览器缓存与区别

  • cookie用来保存登录信息,大小限制为4KB左右
  • localStorage是Html5新增的,用于本地数据存储,保存的数据没有过期时间,一般浏览器大小限制在5MB 怎么设置过期时间?
  • sessionStorage接口方法和localStorage类似,但保存的数据的只会在当前会话中保存下来,页面关闭后会被清空。
  • IndexDB 非关系型数据库,50m

10、http缓存

缓存分为强缓存和协商缓存。强缓存不过服务器,协商缓存需要过服务器,协商缓存返回的状态码是304。两类缓存机制可以同时存在,强缓存的优先级高于协商缓存。当执行强缓存时,如若缓存命中,则直接使用缓存数据库中的数据,不再进行缓存协商。

  1. 强缓存
  • Expires
  • cache-control
  1. 协商缓存
  • Last-Modified 和 If-Modified-Since
  • Etag 和 If-None-Match

微信小程序

微信小程序的登录

微信小程序支付

wx.requestPayment

属性类型默认值必填说明
timeStampstring时间戳,从 1970 年 1 月 1 日 00:00:00 至今的秒数,即当前的时间
nonceStrstring随机字符串,长度为32个字符以下
packagestring统一下单接口返回的 prepay_id 参数值,提交格式如:prepay_id=***
signTypestringMD5签名算法,应与后台下单时的值一致
paySignstring签名,具体见微信支付文

弹起微信支付

微信小程序两列瀑布流动态加载

由于涉及动态加载,所以纯css方法column-count等不太适用,需要js

两列瀑布流,每一项的宽度固定,高度不确定,每一项由两部分组成:高度不确定的图片和高度确定的内容区

:计算出每一项的图片的高度+固定内容高度得出项的总高,瀑布流的两列为两个list,哪个list的高度更低,就把当前项push进去,统计两个list的高度。

计算图片高度,使用img的bindload事件(如果能够从后端获取到图片的宽高可跳过此步)

嵌套h5怎么跳小程序详情

微信小程序骨架屏

其他

1、git常见命令

2、webpack相关

3、vite

4、前端新能优化

  • 使用CDN引入组件
  • gzip压缩
  • 文件与图片压缩
  • 合并请求
  • 雪碧图 logo icon不常换的图片与图标组合在一起
  • 图片懒加载
  • 缓存资源
  • 减少DOM操作
  • Vue的keep-alive缓存
  • 路由懒加载
  • 节流防抖
  • 浏览器缓存

5、首屏优化

1. 使用webpack-bundle-analyzer分析前端项目包大小, 找出问题源头.

2. 去掉vuecli打包生成的map文件

3.CDN引入需要的资源

vue.config.js中配置不打包相关资源. 这样可以大幅降低项目打包文件大小

4. 开启gzip打包

同时需要配置nginx才可支持gzip

5. 配置路由懒加载

这样能让打包出来的代码分割, 而不会统统堆一个文件里. 这样的好处是首屏渲染在用户眼里更快了

6. 服务端渲染(SSR)/预渲染(Prerendering)

由于服务端渲染对代码侵入性比较高,因此我使用了预渲染方案.

6、如何保证代码的质量

制定项目编码规范

  1. 开发编辑器及 lint 工具配置
  2. 添加 .editorconfig 文件
  3. 配置 Git Hook 强制执行编码风格检测与修正

7、公共组件

参考 谢小飞

本文仅供自我学习,完整请访问谢小飞的这篇文章