面试问题记录

文章介绍了Vue项目优化的策略,包括代码优化、异步加载、图片优化等,以及微信和支付宝小程序的支付流程。同时讨论了MVVM和MVC模式,Vue2与Vue3的区别,数据双向绑定的原理,以及虚拟DOM的优缺点。此外,还涵盖了防抖和节流的概念,路由模式,以及闭包和数组方法的相关知识。
摘要由CSDN通过智能技术生成


项目优化

Vue项目优化可以从以下几个方面入手:

代码优化:合理使用计算属性、过滤器、组件等功能,避免不必要的计算和重复代码。

异步加载:使用Vue提供的异步组件(component)和路由懒加载功能,根据用户需求动态加载组件和路由,避免一次性加载过多的资源导致页面加载缓慢。

图片优化:使用图片压缩工具对图片进行压缩,减小图片大小,从而减少页面加载时间。同时,可以使用图片懒加载和图片预加载技术,提高图片的加载效率。

代码分割:使用Webpack等打包工具进行代码分割,将代码分割成多个小块,按需加载,从而减少初始加载时间和提高页面响应速度。

缓存优化:使用浏览器缓存和CDN加速等技术,减少网络请求时间,提高页面加载速度。

服务端渲染:使用服务端渲染技术,将部分页面在服务端渲染,减少客户端渲染时间,提高页面加载速度和SEO优化效果。

大数组优化
冻结响应式数据
虚拟列表
Vue项目优化需要根据具体情况进行优化,不能一概而论。同时,优化也不是一次性完成的,需要不断地进行测试和调整,才能达到最佳的优化效果。

微信支付宝的小程序,支付

// 动作面板 选项
const actions = [
( name:"微信’,pay_type: 1 }
{ name:支付宝’,pay_type: 2 }
( name:"云闪付’,pay_type: 3 }

// 引入微信支付API
import { requestPayment } from 'uni-api-upgrade';
// 引入支付宝支付API
import my from '@system.my';

export default {
  data() {
    return {
      // 订单信息
      orderInfo: {},
    };
  },
  methods: {
    // 微信支付
    async handleWeChatPay() {
      try {
        // 1. 发送订单信息到服务器端,获取预支付订单信息
        const prepayOrder = await this.$api.getWeChatPrepayOrder(this.orderInfo);
       
        // 2. 调用微信支付API
        //prepayOrder  后端返回的信息
        const res = await requestPayment({
          provider: 'wxpay',//支付提供商,取值为wxpay。
          timeStamp: prepayOrder.timeStamp,//时间戳,即预支付订单中的时间戳
          nonceStr: prepayOrder.nonceStr,//随机字符串,即预支付订单中的随机字符串。
          package: prepayOrder.package,//预支付订单ID,即预支付订单中的预支付ID。
          signType: prepayOrder.signType,//签名类型,即预支付订单中的签名类型。
          paySign: prepayOrder.paySign,//签名,即预支付订单中的签名。
        });


        // 3. 支付成功,将支付结果发送到服务器端
        const result = await this.$api.sendPayResult(res);

        // 4. 支付成功,跳转到支付成功页面
        uni.navigateTo({
          url: '/pages/pay-success/pay-success',
        });
      } catch (error) {
        // 支付失败,提示用户支付失败
        uni.showToast({
          title: '支付失败',
          icon: 'none',
        });
      }
    },
    // 支付宝支付
    async handleAliPay() {
      try {
        // 1. 发送订单信息到服务器端,获取预支付订单信息
        const prepayOrder = await 					this.$api.getAliPayPrepayOrder(this.orderInfo);

        // 2. 调用支付宝支付API
        const res = await my.tradePay({
        //即调用支付宝预下单接口后返回的订单信息,需要将订单信息转为字符串后传入。
          orderStr: prepayOrder.orderStr,//预支付订单信息
        });
        // 3. 支付成功,将支付结果发送到服务器端
        const result = await this.$api.sendPayResult(res);
        // 4. 支付成功,跳转到支付成功页面
        my.navigateTo({
          url: '/pages/pay-success/pay-success',
        });
      } catch (error) {
        // 支付失败,提示用户支付失败
        my.showToast({
          content: '支付失败',
        });
      }
    },
  },
};

MVVM、MVC

MVVM


MVVM 分为 Model(数据层)、View(视图层)、ViewModel(虚拟dom层):

- Model代表数据模型,数据和业务逻辑都在Model层中定义;
- View代表UI视图,负责数据的展示;
- ViewModel负责监听Model中数据的改变并且控制视图的更新,处理用户交互操作;
- 
Model(数据层)和View(视图层)并无直接关联,而是通过ViewModel来进行联系的,Model和ViewModel之间有着双向数据绑定的联系。因此当Model中的数据改变时会触发View层的刷新,View中由于用户交互操作而改变的数据也会在Model中同步

这种模式实现了 Model和View的数据自动同步,因此开发者只需要专注于数据的维护操作即可,而不需要自己操作DOM 数据驱动视图。

mvc

mvc
视图(View):用户界面。
控制器(Controller):业务逻辑
模型(Model):数据保存
View (视图层)传送指令到 Controller(控制器)
Controller(控制器) 完成业务逻辑后,要求 Model 改变状态
Model(数据层) 将新的数据发送到 View,用户得到反馈
所有通信都是单向的。

vue2和vue3的区别

数据双向绑定原理

Vue2使用的是Object.defineProperty()进行数据劫持,结合发布订阅的方式实现为对象定义属性的时候注册setter呃呵getter方法,在这两个方法上进行监听,对属性的改变或者获取的时候同时进行页面更新
Vue3使用的是Proxy代理,使用ref或者reactive将数据转化为响应式数据
在 Vue3.0 中通过 Proxy 来替换原本的 Object.defineProperty 来实现数据响应式。
之所以 Vue3.0 要使用 Proxy 替换原本的 API 原因在于 Proxy 无需一层层递归为每个属性添加代
理,一次即可完成以上操作,性能上更好,并且原本的实现有一些数据更新不能监听到,比如数组的
push和pop等,但是 Proxy 可以完美监听到任何方式的数据改变,唯一缺陷就是浏览器的兼容性不好。
vue不能在ie8以下的浏览器使用,因为他们不支持对应的双向绑定的底层实现

性能

性能方面,重写了虚拟DOM,跳过了静态节点,只处理动态节点,update性能提高了1.3~2倍,SSR速度提高了2~3倍。

Vue3更好地支持TS

数据和方法的定义

数据和方法的定义
Vue2:
data() { return {}; }, methods:{ }
Vue3:
数据和方法都定义在setup中,并统一进行return{}
Vue2使用的是选项类型API(Options API),Vue3使用的是合成型API(Composition API)。

生命周期

创建阶段的两个钩子合并到了setup中,其他生命周期前加了一个on,多了一个捕获异常的钩子,只能在开发环境下使用

四个阶段:创建、挂载、更新、销毁
除了更新阶段的两个钩子函数,其他的都只执行一次
这两个也需要了解activated、deactivated,是组件被设置了keep-alive之后,再一次激活组件或者
隐藏组件的时候触发 keep-alive的作用是保证组件不销毁,include和exclude分别表示包含和不包
含,是通过组件的name属性进行控制的
Vue2:beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、
beforeDestory、destoryed
vue3
除了beforecate和created(它们被setup方法本身所取代),我们可以在setup方法中访问的API生命周期
钩子有9个选项:
onBeforeMount – 在挂载开始之前被调用:相关的 render 函数首次被调用。
onMounted – 组件挂载时调用
onBeforeUpdate – 数据更新时调用,发生在虚拟 DOM 打补丁之前。这里适合在更新之前访问现有的
DOM,比如手动移除已添加的事件监听器。
onUpdated – 由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
onBeforeUnmount – 在卸载组件实例之前调用。在这个阶段,实例仍然是完全正常的。
onUnmounted – 卸载组件实例后调用。调用此钩子时,组件实例的所有指令都被解除绑定,所有事件侦
听器都被移除,所有子组件实例被卸载。
onActivated – 被 keep-alive 缓存的组件激活时调用。
onDeactivated – 被 keep-alive 缓存的组件停用时调用。
onErrorCaptured – 当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发
生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回 false 以阻止该错误继续向上
传播。

vue数据的双向绑定有什么 ,底层原理,如何实现的,自己实现过吗

vue数据的双向绑定有什么 v-model

双向绑定:
1)即当数据发⽣变化的时候,视图也就发⽣变化,当视图发⽣变化的时候,数据也会跟着同步变化
2)v-model 是语法糖,它负责监听⽤户在表单元素中的输⼊事件来更新数据

表单元素使⽤v-model的本质:
1)v-bind绑定value属性的值
2)v-on绑定input事件监听到函数,函数会获取最新的值赋值到绑定的属性中
<input type="text" :value="message" @input="message = $event.target.value" />

组件使⽤v-model的本质:
1)将其 value attribute 绑定到⼀个名叫 modelValue 的 prop 上;
2)在其 input 事件被触发时,将新的值通过⾃定义的 update:modelValue 事件抛出(发出)<Counter v-model="appCounter"/>  <!-- 相当于-->
<Counter v-bind:modelValue="appCounter" @update:modelValue="appCounter=$event"/>

v-model的修饰符
三个修饰符:

.trim自动去除文本首尾空格
.number 隐式类型转换变成Number类型
.lazy用于性能, 当表单失焦时再进行双向绑定

diff

虚拟dom

虚拟dom就是一个js表达的dom树结构,它最终会生成真实的dom。它是一个js对象,所
以更新比较的效率很高,在渲染生成真实的dom树的时候会进行局部更新,而不是更新整
个页面。这样子便于提升页面的渲染速度和优化页面性能

diff算法

diff算法是一个比较算法,作用是比较虚拟dom的变化。找到哪一个位置发生了改变,进
行替换操作。它是按照逐层进行比较的,遇到改变的节点,直接替换整棵树

key的作用

key的作用是提高diff算法节点查找的速度。相当于为每一个节点添加一个唯一的主键,能
够快速的判断当前节点是否需要更新

虚拟DOM存在的价值点(优点)在哪里

把DOM更新粒度降到最低,规避人为DOM滥操作,提升性能。配合DIFF算法,可以让页面性能有质的提升。

虚拟DOM存在的价值点(缺点)在哪里

 初始加载耗时:虚拟DOM需要在初始加载时生成一棵虚拟DOM树,这个过程需要消耗一定的时间和内存,因此在初始加载时可能会有一定的性能损耗。

内存占用:虚拟DOM需要在内存中维护一棵虚拟DOM树和一些附加数据结构,这会占用一定的内存空间。

学习成本:虚拟DOM需要开发人员掌握一定的知识和技能,包括虚拟DOM的原理、使用方法和注意事项等,对于新手来说可能需要一定的学习成本。

复杂度增加:虚拟DOM的使用会增加代码的复杂度,因为需要在组件中维护虚拟DOM树、监听状态变化等操作,这会增加代码量和维护难度。

不适用于所有场景:虚拟DOM适用于大部分场景,但是在某些特定的场景中,使用虚拟DOM可能会导致性能问题,例如在大量动态节点的情况下,虚拟DOM的性能可能会受到影响。

需要注意的是,虚拟DOM的缺点相对于其优点来说并不是非常明显和突出,而且大部分缺点可以通过优化和合理使用来避免或减轻。因此,虚拟DOM仍然是当前前端开发中非常流行和重要的技术之一

vue父子通信

 父传子使用属性 

  子传父使用事件派发  
	插槽
	ref
  非相关组件传参使用事件总线或者vuex

  什么是事件总线:我们在vue项目中定义一个空白的vue实例,所有的事件派发和监听都在这个空白的vue实例上进行

  ```$emit```事件派发    发布者

  ```$on```事件监听  订阅

防抖和节流

防抖

// 防抖指令
Vue.directive('debounce', {
  // 指令被插入到元素中时执行
  inserted: function(el, binding) {
    let timer; // 定义定时器
    // 监听input事件
    el.addEventListener('input', () => {
      if (timer) {
        clearTimeout(timer); // 清除定时器
      }
      timer = setTimeout(() => {
        binding.value(); // 执行绑定的函数
      }, 500); // 设置等待时间
    });
  },
});

使用
<input type="text" v-debounce="handleInput" />

节流指令

// 节流指令
Vue.directive('throttle', {
  // 指令被插入到元素中时执行
  inserted: function(el, binding) {
    let timer; // 定义定时器
    let lastTime = 0; // 定义上一次执行的时间戳
    // 监听input事件
    el.addEventListener('input', () => {
      const nowTime = new Date().getTime(); // 获取当前时间戳
      if (nowTime - lastTime > 500) { // 判断是否超过等待时间
        lastTime = nowTime; // 更新上一次执行的时间戳
        binding.value(); // 执行绑定的函数
      }
    });
  },
});

<input type="text" v-throttle="handleInput" />

需要注意的是,使用自定义指令实现防抖和节流时,需要在Vue实例创建之前注册指令,例如在main.js中注册:

import Vue from 'vue';

// 注册防抖指令和节流指令
Vue.directive('debounce', debounceDirective);
Vue.directive('throttle', throttleDirective);

new Vue({
  // ...
});  

路由的两种模式

hash模式:即地址栏 URL 中的 # 符号;

history模式:window.history对象打印出来可以看到里边提供的方法和记录长度。利用了 HTML5 History Interface 中新增的 pushState()replaceState() 方法。(需要特定浏览器支持)。

原理

在使用 hash 路由的时候,就会发现,url 后面跟了"#id",hash 值就是 url 中从"#"号开始到结束的部分。
hash 值变化浏览器不会重新发起请求,但是会触发 window.hashChange 事件,假如我们在 hashChange 事件中获取当前的 hash 值,并根据 hash 值来修改页面内容,则达到了前端路由的目的。

history 就是我们平时看到的正常的连接形式。history 模式基于 window.history 对象的方法
history 模式需要后端配合将所有访问都指向 index.html,否则用户刷新页面,会导致 404 错误

$route 和 $router 的区别

$router是VueRouter的实例,在script标签中想要导航到不同的URL,使用$router.push方法。返回上一个历史history用$router.to(-1)


$route为当前router跳转对象。里面可以获取当前路由的name,path,query,parmas等。

闭包

怎么理解闭包?
闭包=内层函数+外层函数的变量
2.闭包的作用?
延长局部变量的生命周期
封闭数据,实现数据私有,外部也可以访问函数内部的变量
闭包很有用,因为它允许将函数与其所操作的某些数据(环境)关联起来
3闭包可能引起的问题?
内存泄漏
释放闭包:让内部函数对象成为垃圾对象,断开指向它的所有引用应用
应用场景:
- 模块封装,在各模块规范(ES6)出现之前,都是用这样的方式防止变量污染全局
- 数据私有化
- 封装一些工具函数
- 在循环中创建闭包,防止取到意外的值。
- 防抖 、节流 、⽴即执⾏函数 、组合函数等等
删除列表中的的某个商品的例子(带确定框)

数组方法

find 返回数组中第一个满足该条件的值,之后的值不再进行检测,当没有找到满足该条件的值时,返回undefined 用途判断添加内容是否存在数组内

filter()创建一个新的数组,新数组中的元素是通过检查指定数组中符合条件的所有元素。
用于删除指定的数据

every 判断数组中是否所有元素都满足条件 用于判断是否全选 购物车

JavaScript垃圾回收是怎么做的?

所谓垃圾回收, 核心思想就是如何判断内存是否已经不再会被使用了, 如果是, 就视为垃圾, 释放掉
两种常见的浏览器垃圾回收算法: 引用计数 和 标记清除法

引用计数

E采用的引用计数算法, 定义“内存不再使用”的标准很简单,就是看一个对象是否有指向它的引用。
如果没有任何变量指向它了,说明该对象已经不再需要了。

但它却存在一个致命的问题:循环引用。
如果两个对象相互引用,尽管他们已不再使用,垃圾回收器不会进行回收,导致内存泄露。

标记清除算法

从js根部(全局对象), 出发, 能够访问到的对象, 普通变量, 函数等, 都是需要用的, 不会释放
   但是如果从js根部, 无法访问到, 无法触及 (谁都找不到这块空间), 这块空间就是垃圾, 需要被回收

promsie


 是用来解决回调地狱的,他接受一个function作为参数。function中有两个形参,一个成功的回调函数一个失败的回调函数。

  .then在成功的时候触发

  .catch在失败的时候触发

  promise的状态不可逆,三个状态:pending、fulfilled、rejected

  有两个很重要的api:
  .all,表示所有的Promise数组中的方法都成功之后触发
  .race,表示数组中只要有一个完成就结束

深拷贝和浅拷贝

 深拷贝:逐层拷贝对象的每一级

  实现方式:

	JSON.parse(JSON.stringify(obj))

  或者使用插件lodash

  浅拷贝:只拷贝第一级

  实现方式:

  ...扩展运算符

  Object.asign

get,post的区别

get请求和post请求的区别?
  get:
    1)主要用于从服务器获取资源
    2)可以把参数放到url中传递给服务器
    3)传递的数据类型只允许ASCII字符
    4GET相对不安全
    5GET请求传递给服务器的数据大小是有限制的,不同的浏览器限制是不一样的
  post:
    1)主要用于把资源扔给服务器
    2)把参数放到请求体中传递
    3)传递的数据类型是无限制
    4POST相对安
    6POST请求,理论上传递给服务器的数据大小是不限制
   

事件循环

同步叫阻塞模式,异步叫非阻塞模式

  异步的实现原理:js中的事件轮训(eventloop),微任务和宏任务

  在js中有一个异步回调队列,当遇到异步任务的时候会把这个任务直接转到异步队列中。等待所有的同步任务都完成之后在执行异步队列

  异步队列中的任务会分为微任务和宏任务

  每一个宏任务周期中会把当前周期中的所有微任务都执行完成之后,在执行下一个宏任务周期

  宏任务:所有的运行环境提供的异步任务

  微任务:所有的语言自带的异步任务

LocalStorage和SessionStorage和cookie的区别

存储大小
cookie:一般不超过4k 主要用来保存登录信息, 在所有同源窗口中共享
sessionStorage localStorage:5M甚至更多
cookie 判断用户是否登录过网站,以便实现下次自动登录或记住密码;保存事件信息
在浏览器和服务器之间来回传递,如果使用cookie保存过多数据会造成性能问题

sessionStorage:仅在客户端(浏览器)中保存,不参与服务器的通信 敏感账号一次性登录,单页面用的较多
仅在客户端(浏览器)中保存,不参与服务器的通信  
在同一个浏览器窗口是共享的(不同浏览器,即使是统一页面也不共享)

localStorage:用于长期登录,适于长期保存在本地的数据 在所有同源窗口中共享

new对象做了什么

4种
默认绑定:独⽴函数调⽤,函数没有被绑定到某个对象上进⾏调⽤
隐式绑定:通过某个对象发起的函数调⽤,在调⽤对象内部有⼀个对函数的引⽤。
显式绑定:明确this指向的对象,第⼀个参数相同并要求传⼊⼀个对象。
new绑定:
1)创建⼀个全新对象
2)新对象被执⾏prototype链接
3)新对象绑定到函数调⽤的this
4)如果函数没有返回其他对象,表达式会返回这个对象

= =和===

 == (普通相等)
1)在类型不相同的情况下, 会将运算元先转成Number的值, 再进⾏⽐较(隐式转换)
2)null⽐较特殊: null在进⾏⽐较的时候, 应该是会被当成⼀个对象和原⽣类型进⾏⽐较的

=== (严格不等)
1)在类型不同的情况下,直接返回false

const let的区别

作⽤域提升
var声明的变量是会进⾏作⽤域提升
letconst没有进⾏作⽤域提升,但是会在解析阶段被创建出来
let,const具有暂时性死区
块级作⽤域
var不存在块级作⽤域
letconst存在块级作⽤域
重复声明
var允许重复声明变量
letconst在同⼀作⽤域不允许重复声明变量
修改声明的变量
let,var 可以修改声明的变量
const它表示保存的数据⼀旦被赋值,就不能被修改,但是如果赋值的是引⽤类型,那么可以通
过引⽤找到对应的对象,修改对象的内容
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值