前端Vue面试总结1

1. 网站优化
1)代码压缩/第三方工具进行代码压缩
2)控制图片大小,超出的图片用canvas进行处理
3)CDN加速,把公用的的js包或CSS包放在公共服务器或npm仓库进行异步加载减少页面阻塞
4)图片懒加载,尽量用CSS替换图片,图片存放在服务器中转换成https链接的形式,减小包容量
5)大数据分页处理,减少DOM节点读取的卡顿
6)利用缓存存储必要信息,减少接口的频繁调用,减轻服务器压力
7)防爆操作,如支付/表单提交等,用户不得频繁调用接口,只有接口处理返回之后才能重新调用,惰 性函数/一次性函数/异步函数增强业务逻辑的同时,也可以合理延长请求时间,减少接口请求频率
8)组件仓库化(npm仓库进行管理),减少项目体积

【补充】
【1】按需加载第三方库(UI 组件库或工具库等);
【2】移除调试信息,例如 log;
【3】合理使用 SourceMap;
【4】通过 externals 忽略第三方包再配合 CDN 的方式进行加载;
【5】路由/组件/图片/数据懒加载、图片压缩/雪碧图、压缩合并 CSS、提取公共 JS 等;
【6】合理的使用缓存(包含前端缓存或 HTTP 缓存);
【7】webpack实现代码压缩/代码分区合并/图片压缩/console.log打印清除/注释清除/debugger清除。

2、网站安全
1)使用第三方的图片验证码实现防字典爆破
2)md5/sha1实现数据加密,譬如支付前的token加密,确保信息安全;
3)前端生成验证码,短信验证码,登陆密码校验等实现真人校验
4)输入信息校验/过滤,防SQL语句注入

3、浏览器兼容
1)兼容IE9及以上,360浏览器,火狐浏览器和谷歌浏览器

2)JS通过渐进增强和平稳退化的方式,
比如,IE的JS的api不兼容,冒泡模式不一样,怪异盒模型,IE8以下浏览器的浮动样式问题

3)CSS兼容问题
不同浏览器的默认样式存在差异,可以使用 Normalize.css 抹平这些差异。当然,你也可以定制属于自己业务的 reset.css

    

4、前端跨域问题
1)JQuery的jsonP跨越【需要配置jsonp接口】,get请求可通过 /

2、组件更加细分,组件与组件之间异步加载,数据加载更快
3、尽可能少使用大数据的二维数组,如果要使用,也得一个组件只能有一个v-for

5)PC端微信小程序的wx.getLocation无效【因为PC端是没GPS定位的】
1、通过wx.getSystemSync()获取platform信息,除了andiron,ios之外的系统都是PC端。给定经纬度的默认值
2、通过授权登录获取用户信息,获取手机号判断用户上次用手机定位的信息

6)前端版本控制清除用户本地缓存
通过前端版本控制清除用户本地缓存使之重新登陆,刷新用户本地缓存信息,并推送相应的版本更新提示,或者通过对localStorage过期时间设置实现,推荐文章:面试官: 如何让localStorage支持过期时间设置?
前端Vue面试总结1_第2张图片
大概思想为:再存入对应的键值时,同时通过加连接符绑定一个过期时间。当下次进入页面使用时,与当前时间比较,比较后做对应的操作

7)iphoneX的安全区域问题
1、CSS的constant(),env()方法实现安全区域兼容
2、通过wx.getSystemSync()获取statusHeight和windowHeight来判别安全高度

8)react中声明的纯函数组件的ref为null
纯函数组件的状态更改只能够通过hooks,手动更改或渲染

9)react的纯函数组件视图与数据分离,视图更改数据未更改,数据更改视图未更改

10)数组对象里的属性更改,视图不渲染
React 并不会检查数组里的元素是否有改变,它只会检查数组本身的引用是否有改变。通过扩展运算符更改数组的引用

8、js事件循环(event loop)
推荐视频

js事件循环包括宏任务和微任务、js单线程执行过程、浏览器异步执行机制

下述文字讲解的是浏览器异步执行原理和事件驱动的基础上开拓的js的事件循环机制

1)浏览器js异步执行原理
浏览器是多线程的,js是单线程的(指的是执行js的代码是单线程的,是浏览器提供的js引擎线程);浏览器还有定时器线程和http请求线程等等。

比如说主线程中会发送一个ajax请求,就会把这个任务交给浏览器的http请求线程去请求,js主线程不受影响继续向下执行,当请求成功之后,会将请求中的回调函数在返给js主线程。
换句话说,浏览器才是真正执行请求的角色,而js是处理请求的回调函数的角色

浏览器包括浏览器进程、渲染进程、GPU进程、网络进程等
我们前端主要关注的是渲染进程【包括js引擎线程、http请求线程,定时器线程,条件出发、GUI线程等】

浏览器异步执行的原理是通过事件驱动(事件触发、任务选择、任务执行)来触发的

事件循环就是在事件驱动模式中来管理和执行事件的一套流程

事件驱动和状态/数据驱动来改变渲染,浏览器中典型的事件处理也是典型的事件驱动,在事件驱动中,当有事件触发后,被触发的事件会按顺序暂时存在一个队列中,等待js的同步任务执行完成之后就从这个队列中取出要处理的事件并处理,什么时候取出任务,优先处理那些任务,这都是由我们的事件循环来控制

浏览器中的事件循环、必须了解执行栈和任务队列
js在执行一段代码时,会将同步代码按顺序排列在某个地方,这就是执行栈。依次执行里面的函数,当遇到异步任务时就交给其他线程来处理。等待当前同步代码执行完成之后,它会从一个队列中去取出完成中的异步任务的回调加入执行栈继续执行。遇到异步任务然后又交给其他线程。通过这样一个循环来执行完整个代码

js按顺序执行执行栈里面的方法,每次执行一个方法时,都会为这个方法生成一个独有的执行环境,也就是我们平时说的上下文(context) 等待这个方法执行完成之后,会销销毁掉这个环境,并从执行栈中弹出此方法,然后又继续调用下一个方法

由此可见,在事件驱动的模式下,至少包含了一个执行循环来检测任务队列是否有新的任务,通过不断循环取出异步回调来执行,这个过程就是我们所说的事件循环(事件周期)

宏任务和微任务
任务队列根据任务不同可以分为宏任务队列和微任务队列。执行栈在同步代码执行完成之后优先会去检查微任务队列是否会有任务需要执行,如果没有,再去宏任务队列检查。如此往复

微任务一般会在当前循环优先执行,而宏任务会等到下一次循环开始的时候执行。

微任务要比宏任务先执行,并且微任务只有一个队列,而宏任务有多个【因为宏任务就包括定时器任务,请求任务,鼠标点击,键盘按下等等事件】

宏任务包括 setTimeOut() \ setInterval() \ setImmediate()
微任务包括 Promise.then() \ Promise.catch \

宏任务特征:有明确的异步任务需要执行和回调,需要其他异步线程支持
微任务特征,没有明确的异步任务需要执行,只有回调,不需要其他异步线程支持

如果我们将定时器设置为0ms,谷歌浏览器会默认为4ms,Node.js会默认为1ms

定时器误差
因为js单线程先执行同步代码,再取出执行异步回调。当执行setTimeOut时,浏览器会启用新的线程去计时,计时结束后重新出发定时器事件,将回调存入宏任务队列中,等待JS主线程空了的时候再取出执行。如果同步代码还在执行,这个时候的宏任务只能挂起,这就会导致计时不准确的问题。同步代码越长/微任务耗时长,计时也就越不准确

微任务队列执行完成之后,也就是一次事件循环结束之后,浏览器会执行视图渲染。当然这里也有浏览器自己的一个优化,它可能合并多次事件循环的结果做一次视图重绘。因此视图更新是在事件循环之后,所以并不是每一次操作dom都会立马刷新视图,视图重绘之前会先执行requestAnimationFrame回调

9、js判断空对象的5种方法
知识点:
1)Object.getOwnPropertyNames ,获取对象的全部属性名存放在一个数组中
2)Object.keys(obj) 获取给定对象的所有可枚举属性的字符串数组
3)hasOwnProperty检测属性是否存在对象实例中(可枚举属性),如果存在返回true,不存在返回false

例如:

  let arrObj = { name: "字符串", age: 18 };
  // 第一种方法
  console.log(JSON.stringify(arrObj) === "{}"); //false

  // 第二种方法 for...in
  let fun = (arr) => {
    for (let i in arr) {
      return false;
    }
    return true;
  };
  console.log(fun(arrObj)); // false,表示不是空对象

  // 第三种方法
  let _arr = Object.getOwnPropertyNames(arrObj);
  console.log(_arr); // ["name" , "age"]
  console.log(_arr.length != 0); // true,表示不为空对象

  // 第四种方法
  let _arr1 = Object.keys(arrObj);
  console.log(_arr.length != 0); // true,表示不为空对象

  // 第五种方法
  let fun1 = (arr) => {
    for (let i in arr) {
      if (arr.hasOwnProperty(i)) {
        return false;
      }
    }
    return true;
  };
  console.log(fun1(arrObj)); // false,表示不为空对象

10、forEach方法跳出循环
知识点:
1)forEach是函数,不是if、for之类的语句。它的参数是一个匿名函数,是一个callback,实际每次forEach操作的是一个函数,所以不能使用for语句相关的continue,break
2)forEach方法机制,是对数组的每个有效元素执行一次callback函数
3)forEach跳出循环通过try…catch…方式

      let arr = [1, 2, 3, 4];

      // 第一种示例,未跳出循环
      arr.forEach((n) => {
        // forEach方法内是传递的一个匿名函数
        if (n === 2) {
          // break;
          // continue;  这两个适用于for语句,不适用于forEach函数的
          return;
        }
        console.log(n); // 控制台分别会打印1,3,4
      });

      // 第二种示例,跳出循环
      let k = null;

      try {
        arr.forEach((n) => {
          // forEach方法内是传递的一个匿名函数
          if (n === 2) {
            k = n;
            throw new Error();
          }
        });
      } catch (e) {}
      console.log(k); // 2

11、什么是中间件
中间件是一个起连接作用的东西

只要能够获取数据的,都可以算是中间件。比如axios

12、JSONP跨域
1)什么是跨域?
当请求url时,它的协议、域名、端口,任意一个与你当前页面的url不同即为跨域

2)什么是JSONP?
JSONP其实是一个跨域解决方案

3)JSONP跨域原理
1、img\script等标签的src属性,它不受同源策略的限制
2、JSONP请求数据时,服务器返回的是一段可执行的javascript代码

4)JSONP用途:解决请求其他服务器时的跨域问题

5)JSONP局限:只能用get方式,需要服务器做一些处理、配合

6)分为三个部分:
1、安装nodeJS Express框架:npm i express -D
2、创建静态目录:app.use(‘/public’ , express.static(‘public’))
3、中间件js文件,响应get请求

示例:


13、Promise链式调用,和axios处理高并发
应用场景:下一个操作依赖于上一个操作的状态和结果
前提条件:.then方法中必须返回一个promise对象
运行过程:1).then在链式调用时,会等其前一个then中回调函数执行完毕;2)并且成功返回状态的promise,才会执行下一个then的回调函数

情景如下,我们需要请求三个接口,这三个接口都是要等上一次的请求完成之后,才能请求下一个的请求

let url1 = 'https://123.com/api/api1'
let url2 = 'https://123.com/api/api2'
let url3 = 'https://123.com/api/api3'

// 链式调用,回调地狱模式【不推荐】,可维护性差
axios.get('url1').then(res1=>{
    console.log(res1)
    axios.get('url1').then(res2 => {
        console.log(res2)
        axios.get('url1').then(res3 => {
            console.log(res3)
        })
    })
})

// 因为 axios.get('url1') 就会返回一个Promise对象
// 所以要这样处理,可维护性强
axios.get('url1').then(res1 => {
    console.log(res1)
    return axios.get('url2')
}).then(res2=>{
    console.log(res2)
    return axios.get('url3')
}).then(res3 => {
    console.log(res3)
})

Promise.all() 异步任务同步请求
任务一:【推荐用这种】

    getProps () {
      const that = this
      const cellStyle = this.$refs.formTable0.submit() // 这是一个Promise对象
      const rowStyle = this.$refs.formTable1.submit()
      // 异步任务同步执行
      Promise.all([cellStyle, rowStyle]).then(res => {
        console.log('res', res)
      }).catch(res => {
        console.log('任务阻塞', res)
        that.$emit('update:submit', res)
      })
    }

任务二: await会阻塞下面的await,Promise.all中的catch捕获reject传来的错误
    async getProps () {
      const that = this
      const cellStyle = await this.$refs.formTable0.submit()
      const rowStyle = await this.$refs.formTable1.submit()
      // 异步任务同步执行
      Promise.all([cellStyle, rowStyle]).then(res => {
        console.log('res', res)
      }).catch(res => {
        console.log('任务阻塞', res)
        that.$emit('update:submit', res)
      })
    }
  }

【补充】Promise.all和Promise.race的区别和使用
详细Promise介绍:Promise 的各种方法的详细使用
1)Promise.all 类似于Promise链式请求
比如当一个页面需要在很多个模块的数据都返回回来时才正常显示,否则loading
比如当数组里的P1,P2都执行完成时,页面才显示。
值得注意的是,返回的数组结果顺序不会改变,即使P2的返回要比P1的返回快,顺序依然是P1,P2

    // 获取表格下拉框数据
    async getSelectList () {
      const that = this
      const P1 = await orderumber({ type: 'order' })
      const P2 = await getCustomerList()
      const P3 = await listBaseOtherOffer()
      // 异步任务同步执行,为了让所有的数据都请求完毕,再打开新增弹框,否则不打开
      // 优点保证数据加载完毕,缺点是加载时长过长
      Promise.all([P1,P2,P3]).then(res => {
        that.outerVisible = true
      }).catch(res => {
        console.log('下拉框数据获取失败')
      })
    },

2)Promise.race
race是赛跑的意思,也就是说Promise.race([p1, p2, p3])里面的结果哪个获取的快,就返回哪个结果,不管结果本身是成功还是失败

  // 获取表格下拉框数据
    async getSelectList () {
      const that = this
      const P1 = await orderumber({ type: 'order' })
      const P2 = await getCustomerList()
      const P3 = await listBaseOtherOffer()
      // 异步任务同步执行,为了让所有的数据都请求完毕,再打开新增弹框,否则不打开
      // 优点保证数据加载完毕,缺点是加载时长过长
      Promise.race([P1,P2,P3]).then(res => {
        that.outerVisible = true
      }).catch(res => {
        console.log('下拉框数据获取失败')
      })
    },

一般用于和定时器绑定,比如将一个请求和一个三秒的定时器包装成Promise实例,加入到race队列中,请求三秒中还没有回应时,给用户一些提示或一些相应的操作。

3)Promise.resolve()
将现有对象转换未Promise对象

4)Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

5)实际开发中,经常遇到一种情况:不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误

Promise.try就是模拟try代码块,就像promise.catch模拟的是catch代码块。

【若想详细了解Promsie的底层原理】推荐视频:bilibili视频

14、Vue的render函数
render函数的用途: 创建html模板

知识点:
1)render函数的参数是createElement函数,createElement返回值是一个虚拟dom,即VNode,也就是我们需要渲染的节点
2)createElement有三个参数
第一个参数,必需,{ String | Object | Function } 是要渲染的html标签
第二个参数,可选:{ Object } html标签的各种属性
第三个参数,可选,{ String | Array } 当前html标签的子元素

3)渲染过程
1、独立构建,包括模板编译器,HTML字符串=> render函数=> VNode=>真实的DOM节点
2、运行时构建,不包含模板编译器,render函数=>VNode=>真实的DOM节点




也可以将创建的模板存放在一个变量里面



也可以将创建的子元素存放在一个data属性里面



15、Vue的动态组件
作用:通过component的is属性,来切换不同的组件
知识点:
使用将允许您按名称访问全局和本地组件,可以动态绑定我们的组件,根据数据的不同更换不同的组件。在这种情况下,它更像一个容器

:is 属性 是is-bind的缩写
component标签中的is属性决定了当前采用的是哪个组件





【典型案例】动态组件的方式实现不同标签的转换
应用场景为: 侧边菜单栏 实现跳转页面内或跳转外链
示例代码:




v-bind 可以返回一系列属性v-bind="linkProps(to)"

16、Vue的keep-alive
会缓存当前不活动组件的状态
作用:避免多次重复渲染降低性能
常用知识点:1)include – 字符串或正则表达式,只有名称匹配的组件会被缓存
2)exclude-- 字符串或正则表达式,任何名称匹配的组件都不会背后缓存
3)max – 数字。最多可以缓存多少组件实例
4)结合router。缓存部分页面 。 $router.meta.keepAlive选项

1)第一种方式
通过设置include/exclude/max在标签上设置属性,实现组件活性保持

2)第二种方式
结合router的方式
前端Vue面试总结1_第3张图片
前端Vue面试总结1_第4张图片
17、vue.config.js配置解决跨域问题
浏览器的同源策略:就是两个页面具有相同的协议/主机/端口号

请求同一个接口时,出现Access-Control-Allow-Origin等,就说明跨域了

vue中解决跨域,配置vue.config.js文件,如果没有就自行新建一个

原理:
1)将域名发送给本地的服务器(localhost:8080)
2)再由本地的服务器去请求真正的服务器
3)因为请求是从服务器端发出的,所以不存在跨域问题

注意:修改vue.config.js 文件,需要重启服务

      module.exports = {
        devServer: {
          // 跨域
          proxy: {
            "/api": {
              // 目标路径
              target: "https://www.123.com/index",
              // 是否跨域
              changeOrigin: true,
              // 重写路径
              pathRewrite: {
                "^/api": "",
              },
            },
          },
        },
        chainWebpack: (config) => {
          // 发行或运行时启用了压缩时会生效
          config.optimization.minimizer("terser").tap((args) => {
            const compress = args[0].terserOptions.compress;
            // 非 App 平台移除 console 代码(包含所有 console 方法,如 log,debug,info...)
            compress.drop_console = true;
            compress.pure_funcs = [
              "__f__", // App 平台 vue 移除日志代码
              //'console.dug' // 可移除指定的 console 方法
            ];
            return args;
          });
        },
      };

17、Vue.extend()创建动态组件
动态创建组件:只在事件触发的时候,才产生某组件。平时它并不存在
Vue.extend() extend创建的是一个组件的构造器,而不是一个具体的组件实例

v-if 只在为true的时候被渲染,根据表达式的值在DOM中生成或移除HTML元素

v-show 更具表达式的值来,显示或者隐藏HTML元素

使用场景:
1)动态渲染组件
2)类似于window.alert() 提示组件

1)先创建一个组件模板 toast.vue ,也是以组件的方式创建,动态组件模板是没有data对象的
前端Vue面试总结1_第5张图片
2)在extend…js文件中
前端Vue面试总结1_第6张图片
前端Vue面试总结1_第7张图片
【补充】
效果为:
前端Vue面试总结1_第8张图片
toast.vue文件







toast-extend.js文件

/*
 * @Author: Null
 * @Date: 2022-04-21 10:58:21
 * @Description:  动态通知组件----toast-extend.js
 */

import Vue from 'vue'
import Toast from './index.vue'

// 生成构造函数构造器
const ToastConstructor = Vue.extend(Toast)

export function showToast (text, time = 2000) {
  const dom = new ToastConstructor({
    el: document.createElement('div'),
    data () {
      return {
        text,
        isShow: true
      }
    }
  })
  document.body.appendChild(dom.$el)
  setTimeout(() => {
    dom.isShow = false
  }, time)
}

页面中使用


  测试点击


import { showToast } from '@/components/Toast/toast-extend'

testClick () {
  console.log('显示文本')
  showToast('显示文本')
},

【补充】Vue.extend() 内使用computed属性失效 , 这个只接受静态的vue模板,如下图
前端Vue面试总结1_第9张图片
18、javascript的闭包机制
闭包:父函数中,返回子函数

this , this的指向是,由它所在函数调用上下文【执行环境】决定的,而不是,由它所在函数定义的上下文决定的

var 和 let的区别
var 创建的变量会挂载在window对象上,而let 、 const创建的变量不会挂载在window对象上

【补充】const声明引用类型数据,不改变引用地址就不会报错
前端Vue面试总结1_第10张图片
当我们在单独创建好的js文件中,声明的 const 变量 = 变量值,必须是先声明,再引用。也就是 const 变量 = 变量值 ,声明先在前,引用在后

打印结果为 : 我是老尚
前端Vue面试总结1_第11张图片
如果我将var 更改为 let ,那么打印值为undefined,因为let 不会将变量挂载到window对象上

19、backdrop-filter(实现毛玻璃效果)
CSS效果为:
前端Vue面试总结1_第12张图片
知识点:
前端Vue面试总结1_第13张图片
样式为:
前端Vue面试总结1_第14张图片
20、如何使用匿名自执行函数创建独立命名空间
匿名自执行函数,IIFE 。它被称之为,立即执行函数
格式:

      (function (i) {
        console.log("匿名自执行函数IIFE");
      })(i);

优点:1)避免变量名的重复;2)独立的作用域;3)提高一些性能,减少对作用域链的查找;4)单独的js模块

()圆括号在js中的作用:1)调用函数表达式;2)对表达式求值

函数有两种创建的方式:1)通过function声明函数:function fun(){} ; 2)函数表达式:var fun = function(){}
示例:

      (function (win) {
        win.myFun = function () {
          console.log("添加属性");
        };
        win.abc = "123";
        console.log("封装好的实例", win); // 打印出 {abc:123,myFun:f() }
      })((window.fun = window.fun || {})); // 传入的值相当于独立的命名空间,是在window对象下挂载fun命名空间,
     //  IIFE表达式的属性和方法不直接挂载到window对象下;

21、v-if和v-for为什么不能同时使用?
原因:1)v-for优先级更高;2)会造成性能浪费
解决方案:1)把v-if写在外层dom中;2)把v-if写在外层template中;3)把v-if写在v-for内
【注意只要保证不同级就可以,因为v-for和v-if都是虚拟dom,都要经过vue的diff算法的,生成真实节点】

22、Promise对象的resolve与pending状态的区别?
知识点:
1)async 函数,返回一个Promise对象,如果在函数中return 一个直接量,async会把这个直接量通过Promise.resolve() 封装成Promise对象
2)Resolved,完成状态
3)Pending,进行状态,表示延迟(异步)操作正在进行

23、await 和 .then方法的区别
示例代码1:

    

前端Vue面试总结1_第15张图片
在IE浏览器中: fulfilled状态
前端Vue面试总结1_第16张图片

在谷歌浏览器中: resolved状态
前端Vue面试总结1_第17张图片
与await相比


24、hash模式和history模式
这个hash值就是我们访问链接#号之后的东西,

示例: https://www.123.com/hash.html#xx
我们可以通过window.location.hash能够访问到‘#xx’ 这个hash值,这个hash值不会带到服务器去

两个比较重要的参数,一个是newURL【当前路由】和oldURL【上一个路由】
前端Vue面试总结1_第18张图片
我们可以根据hash值去显示不同的div,从而实现路由跳转
前端Vue面试总结1_第19张图片
示例为:


    
    
    
    Document
    



    
    
首页首页首页首页首页首页首页
详情详情详情详情详情详情详情详情详情详情详情详情详情

history模式的实现
主要是通过history.pushState() 添加切换路由 ,window.onpopstate()监听浏览器路由的变化
前端Vue面试总结1_第20张图片
前端Vue面试总结1_第21张图片
25、防抖和节流
防抖是通过setTimeOut 和 clearTimeOut实现的
使用场景:一些搜索下拉框等高频率请求的场景,就需要使用这个,只请求超出输入时间1.5s的请求

  

节流是节事件流,节约连续输入,每隔3s执行一次请求


【补充】防抖节流函数:

/**
* @desc 函数防抖---"立即执行版本" 和 "非立即执行版本" 的组合版本
* @param func 需要执行的函数
* @param wait 延迟执行时间(毫秒)
* @param immediate---true 表立即执行,false 表非立即执行(默认)
**/
debounce(func, wait, immediate) {
   let timer;
   return function () {
       let that = this;
       let args = arguments;
       if (timer) clearTimeout(timer);
       // 立即执行
       if (immediate) {
           var callNow = !timer;
           timer = setTimeout(() => {
               timer = null;
           }, wait)
           if (callNow) func.apply(that, args)
       } else {
           // 非立即执行
           timer = setTimeout(function () {
               func.apply(that, args)
           }, wait);
       }
   }
},

/**
* @desc 函数节流
* @param func 需要执行的函数
* @param delay 规定时间内不再触发(毫秒)
**/
throttle(func, delay) {
   let oldDate = Date.now()
   return function () {
       let that = this
       let args = arguments
       let newDate = Date.now()
       if (newDate - oldDate > delay) {
           func.apply(that, args)
           oldDate = Date.now()
       }
   }
},

使用方式为:
前端Vue面试总结1_第22张图片

26、Vue中按钮级别的权限控制
1)通过vuex中注册相应的按钮状态,显示或隐藏;在src/store文件夹下的index.js文件夹中

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {
    buttonPermission: {
      add: true,
      edit: false,
      delete: false
    }
  }
})

export default store

2)在src/direcitive文件夹下的has.js文件夹中通过注册指令的方式

export default {
  inserted(el, binddings, vnode) {
    console.log("dom元素插入了")
    console.log(vnode)
    // 获取绑定指令上的值
    let btnPermissionValue = binddings.value
    // 获取vuex中的state的相应状态
    let bool = vnode.context.$store.state.buttonPermission[btnPermissionValue]
    !bool && el.parentNode.removeChild(el) // 如果vuex中的状态为false,则移除此节点
  }
}

3)在相应的页面中使用此指令




效果为:
前端Vue面试总结1_第23张图片
27、filters的使用
数据过滤
示例:
在这里插入图片描述




28、Vue异步组件的使用
就是需要加载该组件的时候才加载
正常来说,我们直接通过import 组件名 from ‘组件相对路径’ ,这样会把组件全部加载出来。

效果为:
前端Vue面试总结1_第24张图片
前端Vue面试总结1_第25张图片
/* webpackChunkName:“list” */ 是给默认的1.js 重命名为 list.js 【未成功】

通过Vue ui可以实现,以前的具体问题处在那里,不清楚

component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')

示例代码:




创建异步组件工程函数




怎么样才能看见loading的加载效果,将网络更改为slow 3G,就能够看见loading…加载组件了
前端Vue面试总结1_第26张图片
29、Vue路由懒加载
为什么采用路由懒加载,是因为减小首次加载的包下载,提高页面加载速度

import Vue from 'vue'
import VueRouter from 'vue-router'
import Hellowworld from '../components/HelloWorld.vue'
import index from '../pages/index.vue'
// import text from '../pages/text.vue'

Vue.use(VueRouter)

const routes = [{
  path: '/',
  redirect: '/index'
},
{
  path: '/index',
  name: 'index',
  component: index
}, {
  path: '/Hellowworld',
  name: 'Hellowworld',
  component: Hellowworld
}, {
  path: '/text',
  name: 'text',
  // 这就是路由懒加载的方式
  component: () => {
    import(/* webpackChunkName:"text" */ '../pages/text.vue')
  }
}
]

const router = new VueRouter({
  routes
})

export default router

30、什么是Vue Mixins(混入)?
作用:复用我们的代码
适用于场景:我现在有两个组件,组件内的方法和data属性都是可以公用的,那么就可以采用混入mixins的方式
示例:
componentA.vue





componentB.vue




在src/mixins/show.js文件中写入

export const showMixins = {
  data() {
    return {
      isShow: false
    }
  },
  methods: {
    changShow() {
      this.isShow = !this.isShow
    }
  }
}

我们可以将componentA和componentB简写为:








【补充】混入其实跟把页面中的公共部分提取出来,再通过模板字符串分别复制到对应页面中

31、Vue3中计算属性computed跟watch的区别
computed:
支持缓存,只有依赖数据发生改变,才会重新进行计算;不支持异步,当computed内有异步操作时无效,无法监听数据的变化;
如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed

watch:

  1. 不支持缓存,数据变,直接会触发相应的操作;
    2.watch支持异步;
    3.监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
  2. 当一个属性发生变化时,需要执行对应的操作;一对多;
  3. 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,
      immediate:组件加载立即触发回调函数执行,
      deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。

32、组件之间的通信
1)父子之间的通信
props+ $emit ;使用回调函数的方式【跟react的useState的改变方法函数相似】; 组件实例上的 $parent + $children ; provide+inject , 给组件绑定ref,获取组件实例,操作组件属性和方法

provide+inject 示例 【向下传递的props好说,单向传递,孙向父上传递得借助中央事件总线或Vuex】
1)父组件
前端Vue面试总结1_第27张图片
2)子组件 【父到孙之间的数据传递,是通过v-bind=‘$attrs’ , $listeners.changeName 可以监听父组件的事件】
前端Vue面试总结1_第28张图片
3)孙子组件
前端Vue面试总结1_第29张图片
33、深拷贝函数
一项一项重赋值给另一个变量


34、数组去重
1)利用es6的set去重

    function unique(arr) {
        return Array.from(new Set(arr))
    }

2)利用for嵌套for,然后splice去重(ES5中最常用)

    function unique(arr) {
        for (var i = 0; i < arr.length; i++) {
            for (var j = i + 1; j < arr.length; j++) {
                if (arr[i] == arr[j]) { //第一个等同于第二个,splice方法删除第二个
                    arr.splice(j, 1);
                    j--;
                }
            }
        }
        return arr;
    }

3)利用indexOf去重

    function unique(arr) {
        if (!Array.isArray(arr)) {
            console.log('type error!')
            return
        }
        var array = [];
        for (var i = 0; i < arr.length; i++) {
            if (array.indexOf(arr[i]) === -1) {
                array.push(arr[i])
            }
        }
        return array;
    }

4)利用reduce+includes

    function unique(arr) {
        return arr.reduce((prev, cur) => prev.includes(cur) ? prev : [...prev, cur], []);
    }

5)[…new Set(arr)]

[...new Set(arr)]
//代码就是这么少----(其实,严格来说并不算是一种,相对于第一种方法来说只是简化了代码)

35、Vue的生命周期
创建前/创建后,挂载前/挂载后,更新前/更新后,销毁前/销毁后

36、动态路由中的参数传递
1)我们首先会创建相应路由
前端Vue面试总结1_第30张图片
2)我们会根据访问的/user/:id的参数路由不同,显示对应的id值
前端Vue面试总结1_第31张图片
上面这个data属性里的 id:this.$route.params.id 只会渲染一次,所以我们动态更换路由场景,不会刷新id
1)通过watch监听

    watch: {
        // to代表是新的路由 , from代表是老的路由
        $route(to, from) {
            this.id = to.params.id
        }
    },

2)这个时候我们就必须借助一个钩子函数 beforeRouteUpdate

  beforeRouteUpdate(to , from){
    this.id = to.params.id
  },

37、使用addRoute动态添加路由
1)创建两类路由,一类是普通路由,一类是带权限的路由

前端Vue面试总结1_第32张图片
前端Vue面试总结1_第33张图片
2)就拿登录来说,我们可以在Vuex中保存一个登录状态
前端Vue面试总结1_第34张图片
3)我们可以在相应的页面中,通过this.$store.commit('login') 来改变store中的state状态 ,也可以通过this.$router.addRoutes(asyncRoutes) 动态添加路由
前端Vue面试总结1_第35张图片
38、递归中的闭包
什么是递归?
递归是函数是直接或间接地调用自己

闭包是什么?
父函数里包含子函数,子函数被return 到父函数之外,这个子函数就是闭包

下面这样的写法,每次return的,都是一个全新的函数

    ...
    return {
        fun:function(){
            // 函数体
        }
    }

看下述示例:


请看题:


打印结果为:
前端Vue面试总结1_第36张图片
39、forEach()和map()方法的区别
共同点:1)都循环数组中的每一项;2)每次循环的匿名函数都有三个参数(当前项item , 索引值index, 原始数组arr); 3)匿名函数中的this指向window;4)只能循环数组

区别:1)forEach()没有返回值;2)map()有返回值,返回新数组,原数组不变

用途:forEach() 不改变数据,只能用一些数据做一些事情
map() 你需要返回一个新数组

40、vuex实现状态持久化
主要是利用locaStorage缓存实现,在刷新页面的前一刻存至本地缓存中,页面刷新完毕,更新vuex,并清空本地缓存

41、AJAX是个啥?如何使用AJAX获得数据?
知识点:
1)AJAX是在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容
2)核心为:XMLHttpRequest对象, 用于在后台与服务器之间交换数据
3)open( method , url , async ) , 规定请求的类型,URL , 以及是否异步处理请求
4)send() , 将请求发送到服务器
5)readyState属性 , 保存XMLHttpRequest的状态,从0到4的发生变化;0:请求未初始化;1:服务器已建立连接;2:请求已接受;3:请求处理中;4:请求已完成,且响应已就绪
6)onreadystatechange事件,每当readyState属性发生改变时,就会调用该函数;
7)status: 响应状态 , 200:‘OK’ , 400:‘未找到页面’


未能够访问到nodeJs内的端口数据

因为跨域的问题,访问不到【未解决】不到,请看上文12、jsonp跨域
在这里插入图片描述
我们用nodeJs写一个GET请求,下为app.js文件。通过node app.js启动该接口

// nodeJs自带的http请求模块
const http = require('http') 
// 自带的querystring模块,作用是将url问号后的参数以对象的形式输出
const querystring = require('querystring') 

// req 为请求头,请求头自带method/url ,不带有query属性的
// res 为响应数据
const server = http.createServer((req,res)=>{
    const method = req.method
    console.log('nethod' , method)
    const url = req.url
    console.log('url' , url)
    // 我们在req 请求头对象下新增加属性query
    req.query = querystring.parse(url.split('?')[1])
    console.log('req.query' , req.query)
    // 这个发送数据地方一定要转为JSON字符串
    res.end(JSON.stringify(req.query))
})

server.listen(5000,()=>{
    console.log('server run at port 5000!')
})

成功在本地端口号5000启动
在这里插入图片描述
访问路径: http://localhost:5000/?id=1
前端Vue面试总结1_第37张图片
接口创建成功

42、数据结构与算法—栈
数据结构和算法在规划存取和计算效率上很重要。

栈是一种线性的、后进先出(LIFO)的数据结构,线性表示与数组一样,只有一维呈线性排列,后进先出指的是最后添加到栈中的元素先出去
前端Vue面试总结1_第38张图片
前端Vue面试总结1_第39张图片
例如
1)JavaScript函数调用栈就是通过栈来实现的
2)计算带小括号的数学表达式

操作栈的方法有:
push() 将元素加至栈顶,栈大小+1; pop()将元素从栈顶抛出,栈大小-1
isEmpty() 判断栈是否为空 ; size() 获取栈的大小 ; peek()查看栈顶元素

我们可以通过JavaScript中的数组实现栈操作

      class Stack {
        stack = [];
        // 入栈
        push(item) {
          this.stack.push(item);
        }
        // 出栈
        pop() {
          return this.isEmpty() ? "栈为空" : this.stack.pop();
        }
        // 判断栈是否为空
        isEmpty() {
          return this.size() === 0;
        }
        // 返回栈的大小
        size() {
          return this.stack.length;
        }
        // 返回栈顶元素,并不会从栈顶弹出
        peek() {
          return this.stack[this.stack.length - 1];
        }
      }
      let statck = new Stack();
      stack.push(1);
      stack.push(5);
      stack.push(8);
      stack.peek(); // 栈顶为8
      stack.size(); // 长度为3

      stack.pop();
      stack.size(); // 栈为2
      stack.pop();
      stack.pop();
      stack.pop(); // 输出栈为空

你可能感兴趣的:(Vue知识点,前端知识,前端,vue.js,面试)