目录
项目优化
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字符
4)GET相对不安全
5)GET请求传递给服务器的数据大小是有限制的,不同的浏览器限制是不一样的
post:
1)主要用于把资源扔给服务器
2)把参数放到请求体中传递
3)传递的数据类型是无限制
4)POST相对安
6)POST请求,理论上传递给服务器的数据大小是不限制
事件循环
同步叫阻塞模式,异步叫非阻塞模式
异步的实现原理: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声明的变量是会进⾏作⽤域提升
let、const没有进⾏作⽤域提升,但是会在解析阶段被创建出来
let,const具有暂时性死区
块级作⽤域
var不存在块级作⽤域
let和const存在块级作⽤域
重复声明
var允许重复声明变量
let和const在同⼀作⽤域不允许重复声明变量
修改声明的变量
let,var 可以修改声明的变量
const它表示保存的数据⼀旦被赋值,就不能被修改,但是如果赋值的是引⽤类型,那么可以通
过引⽤找到对应的对象,修改对象的内容