$refs的优点以及弊端
$refs的弊端是,状态管理比较混乱,不利于维护
异步组件和路由懒加载区别
异步组件:异步组件是一种技术,它允许在页面需要时才从服务器加载相应的组件。这种方式适用于大型应用,可以将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块,从而提高页面渲染速度
路由懒加载:路由懒加载结合了异步组件技术和webpack代码分割功能,以达到优化项目的目的。它允许只加载当前点击的模块,而不是整个网页。这种方式可以有效地分担首页所承担的加载压力,减少首页加载用时,提高用户体验
总结来说,异步组件和路由懒加载都是为了提高页面加载速度和用户体验,但它们的技术实现和应用场景有所不同。异步组件更侧重于代码分割和模块加载,而路由懒加载则侧重于页面加载的优化和用户体验的提升
组件上使用v-model
父组件
<template>
<div id="app">
<HelloWorld v-model="showFlag" v-if="showFlag"></HelloWorld>
<button @click="showFlag=true">打开组件</button>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld'
export default {
name: 'App',
data(){
return {
showFlag: false
}
},
components: {
HelloWorld
}
}
</script>
简单说明一下,这里我们引入了我们的子组件HelloWorld,
通过showFlag来控制组件的显示隐藏,
当然,组件上还用v-model绑定了showFlag,那么我们就来看看子组件是如何搞的吧。
子组件
<template>
<div class="hello">
<h1>这是组件里面的内容</h1>
<button @click="close">关闭组件</button>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
model: {
prop: 'checked',
event: 'change'
},
props: {
checked: {
type: Boolean
}
},
methods: {
close(){
this.$emit('change', false)
}
}
}
对于子组件来说,允许自定义使用v-model时定制prop和event,
v-model中的prop就是把value用作prop,input用作event,
但是为了避免冲突,我们使用model的选项可以回避这些冲突,当然,你也得使用props声明checked这个prop
计算属性和侦听器的区别
1:功能不同,计算属性用于解决模板语法冗余问题。侦听器侦听data中某一个数据变化
2:计算属性有缓存机制,侦听器没有缓存机制
3:计算属性不支持异步操作,侦听器支持异步操作
4:计算属性可以给vue新增属性,侦听器必须是data中已有属性
5:计算属性只要使用了就会立即执行一次,侦听器默认只有当数据第一次执行才会执行
6:侦听器可以通过设置immerdiate为true,也可以像计算属性一样立即执行一次
浏览器存储技术:localStorage、sessionStorage和cookie的区别
localStorage:永久存储
sessionStorage:临时存储
cookie:cookie 中的数据会随着 HTTP 请求一起发送到服务vue2和vue3x区别
双向数据绑定的原理不同,Vue2使用的Object.definProperty()进行数据劫持,而Vue3采用了ES6的Proxy API 这使得对数据监控更加高校和全面
API类型不同。Vue 2 采用选项式API(OPtions API) 而Vue3引入了组合式API(compostion API) 使得代码的组织和逻辑服用更加灵活和可读。
支持碎片不同,vue3支持碎片,允许渲染多个根节点,简化了组件结构的编写。
生命周期钩子函数不同,虽然两者再生命周期钩子的数量和命名上有所差异但在Vue3中,使用组合式API时,需要显式引入生命周期钩子。
父子组件传参方式不同,Vue3提供了更灵活的父子组件传参方式,包括props和事件(emit)的使用
异步组件(Suspense) 支持不同。Vue3提供了Suspense组件,用于在异步组件加载时提供兜底内容,提升用户体验。
指令与插槽的使用不同,vue3在指令和插槽的使用上也有所变化,列如v-if和v-for的优先级调整。
main.js文件配置不同,在项目配置上,vue3相较于vue2的main.js文件中的设位置也有所不同
es6的新特性
1.块级作用域:
ES6引入了let和const关键字,可以在块级作用域中声明变量。使用let声明的变量只在当前作用域内有效,避免了变量污染和重复定义的问题
let x = 1;const y = 2;
2.箭头函数
箭头函数就可以更简介地定义的函数,并且它的this值绑定在定义时的环境中,而不是执行时的环境
const sum = (a, b) => a + b;
3.模板字符串
模板字符串可以方便的拼接字符串和变量,避免了繁琐的字符串拼接和转义。
const name = 'John';console.log(`My name is ${name}`);
4.解构赋值
解构赋值可以方便的提取对象中数组中的值,并赋值给变量,使得代码更加简洁易懂
const obj = { x: 1, y: 2 };const { x, y } = obj;console.log(x, y);
5. Rest参数
Rest参数可以将函数作为数组来处理,避免了需要使用arguments对象的情况
const sum = (...args) => args.reduce((a, b) => a + b, 0);console.log(sum(1, 2, 3));
6.Spread操作符
Spread操作符可以将数组或对象展开独立的元素,方便的进行数组合并,对象合并等操作
const arr1 = [1, 2, 3];const arr2 = [4, 5, 6];const arr3 = [...arr1, ...arr2];console.log(arr3);
7.Class类
Class类可以更方便地定义对象和继承,使得面向对象编程更加规范和易懂。
class Person { constructor(name) { this.name = name; } sayHello() { console.log(`Hello, my name is ${this.name}`); }}const john = new Person('John');john.sayHello();
8.Promise异步编程
Promise可以更好的处理异步操作,避免了回调地狱的问题
const fetchData = () => { return new Promise((resolve, reject) => { // 异步操作 if (success) { resolve(data); } else { reject(error); } });}; fetchData().then(data => console.log(data)).catch(error => console.log(error));
9.Promise.all方法
Promise.all方法可以同时执行多个Promise.all方法可以同时执行多个Promise对象,并在所有Promise对象执行完毕
.all()方法接收一个Promise数组作为参数,当所有Promise都fulfilled时,返回的新的Promise才会fulfilled,并带有所有Promise的结果组成的数组。只要其中一个Promise rejected,则返回的Promise立即rejected
返回的Promise全部成功走
Promise.all([fetchData1(), fetchData2()]) .then(results => console.log(results)) .catch(error => console.log(error));
9.promise.race()
.race()方法同样接收Promise数组,当其中任意一个Promise fulfilled或rejected时,返回的新Promise就会采用相同的状态,并返回那个率先改变状态的Promise的结果或错误原因。
任意一个Promise成功就返回那个成功Promise,无论成功几个都走第一个
Promise.race(promisesArray).then(value => { // 只要有一个Promise fulfilled就执行 }).catch(error => { // 第一个rejected的Promise的错误原因 });
9.promise.allSettled()
.allSettled()方法接收一个Promise数组,当所有Promise都settled(即完成,无论是fulfilled还是rejected)时,返回的新的Promise会fulfilled,并带有所有Promise的结果对象组成的数组,每个结果对象包含status("fulfilled"或"rejected")和value(如果是fulfilled时的结果值)或reason(如果是rejected时的错误原因)属性。
promise.allSettled() 无论成功与失败都会返回,反正的值最后是一个数组
Promise.allSettled(promisesArray).then(results => {
// 所有Promise都settled(无论是fulfilled还是rejected)时执行
});
9.promise.any()
.any()方法也是接收一个Promise数组,只要其中任何一个Promise fulfilled,返回的Promise就会以该Promise的结果fulfilled;但如果所有Promise都rejected,则返回的Promise将以AggregateError rejected(AggregateError是一种内置错误类型,它表示一组错误的集合,通常用于一次性报告多个错误的情况)。
Promise.any(promisesArray).then(value => {
// 只要有一个Promise fulfilled就执行
}).catch(error => {
// 如果所有Promise都rejected,则返回AggregateError
});
promise 链式引用时根据多个promise循环,根据上一个promise对象进行请求
10.模块化
ES6引入了摸块化的概念,可以更好的组织和管理代码,避免了全局变量的污染。
// 导出export const sum = (a, b) => a + b; // 导入import { sum } from './math';console.log(sum(1, 2));
11.Set 和Map
Set 和Map可以更方便的处理集合和键值对,使得数据结构更加丰富和易用。
const set = new Set([1, 2, 3]); set.add(4) ;console.log(set.has(4)); const map = new Map([['x', 1], ['y', 2], ['z', 3]]); console.log(map.get('y'));
12.for...of循环
for...of循环可以更方便地遍历数组,字符串,Map,Set等对象,使得代码更加简洁易懂
const arr = [1, 2, 3];for (const num of arr) { console.log(num); }
13.Object.assign方法,Object.assign方法,可以将多个对象合并成一个对象
const obj1 = { x: 1 };const obj2 = { y: 2 };const obj3 = Object.assign({}, obj1, obj2);console.log(obj3);
14.includes方法
includes方法可以判断数组或字符串是否包含某个元素
const arr = [1, 2, 3];console.log(arr.includes(2));const str = 'Hello, world!';console.log(str.includes('world'));
15.扩展的对象字面量;
扩展的对象字面量;扩展的对象字面量可以更方便地定义对象
const x = 1, y = 2;const obj = { x, y };console.log(obj);
16.其他新特性
Es6还引入了默认参数,Symbol类型,生成器函数等其他新特性。
17.Symbol、BigInt
Symbol 指的是独一无二的值。每个通过 Symbol() 生成的值都是唯一的。
symbol 是一种基本数据类型(primitive data type) 。Symbol()函数会返回symbol类型的值,该类型具有静态属性和静态方法。它的静态属性会暴露几个内建的成员对象;它的静态方法会暴露全局的symbol注册,且类似于内建对象类,但作为构造函数来说它并不完整,因为它不支持语法:“new Symbol()”。
每个从Symbol()返回的symbol值都是唯一的。一个symbol值能作为对象属性的标识符;这是该数据类型仅有的目的。
那么,如何使用 Symbol 创建两个可以相等的变量呢?
let var_symbol = Symbol.for('symbol');
let other_symbol = Symbol.for('symbol');
console.log(var_symbol === other_symbol)
// true
Symbol.for(key) 方法会根据给定的键 key(字符串),来从运行时的 symbol 注册表中找到对应的 symbol,如果找到了,则返回它,否则,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中。
————————————————————————————————————————
和 Symbol() 不同的是,用 Symbol.for() 方法创建的的 symbol 会被放入一个全局 symbol 注册表中。Symbol.for() 并不是每次都会创建一个新的 symbol,它会首先检查给定的 key 是否已经在注册表中了。假如是,则会直接返回上次存储的那个。否则,它会再新建一个。
BigInt 是一种数字类型的数据,它可以表示任意精度格式的整数。而在其他编程语言中,可以存在不同的数字类型,例如:整数、浮点数、双精度数或大斐波数。
JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。二是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回Infinity。
// 超过 53 个二进制位的数值,无法保持精度
Math.pow(2, 53) === Math.pow(2, 53) + 1 // true
// 超过 2 的 1024 次方的数值,无法表示
Math.pow(2, 1024) // Infinity
vue常用的修饰符
应用场景
根据每一个修饰符的功能,我们可以得到以下修饰符的应用场景
.stup:阻止事件冒泡
.native 绑定原生事件
. self 将事件绑定自身身上,相当于组织事件冒泡
.prevent 组织默认事件
.capture 用于事件捕捉
.once 只触发一次
.keyCode 监听特定键盘按下
.right 右键
vue实现首屏加载的优化
图片懒加载
解决方法: 图片懒加载原理都差不多,这里使用一个叫VueLazyComponent
组件,方便与后面的组件分包懒加载一起使用。其内部实现就是一个带插槽的公共组件,进入视野后再加载资源,有兴趣的可以自行去看源码。传送门
使用方式很简单,这样等图片进入视野后才会加载图片资源
js资源加载
async:
异步加载js,js加载完后立即执行,多个js执行顺序不固定。
defer:
异步加载js,解析 document后,按js文件顺序以此执行。
文件分包懒加载
路由懒加载
解决方法: 路由懒加载:打包时会将每个路由页面拆分成单独的 js 资源,同时跳转到对应页面才会加载对应路由的 js 资源。
vuex实现持久化
安装插件
2 3 |
|
使用方法
import Vuex from "vuex";
// 引入插件
import createPersistedState from "vuex-persistedstate";
Vue.use(Vuex);
const state = {};
const mutations = {};
const actions = {};
const store = new Vuex.Store({
state,
mutations,
actions,
/* vuex数据持久化配置 */
plugins: [
createPersistedState({
// 存储方式:localStorage、sessionStorage、cookies
storage: window.sessionStorage,
// 存储的 key 的key值
key: "store",
render(state) {
// 要存储的数据:本项目采用es6扩展运算符的方式存储了state中所有的数据
return { ...state };
}
})
]
});
export default store;
vuex中module数据的持久化存储
/* module.js */
export const dataStore = {
state: {
data: []
}
}
/* store.js */
import { dataStore } from './module'
const dataState = createPersistedState({
paths: ['data']
});
export new Vuex.Store({
modules: {
dataStore
},
plugins: [dataState]
});
注意事项
- storage为存储方式,可选值为localStorage、sessionStorage和cookies;
- localStorage和sessionStorage两种存储方式可以采用上述代码中的写法,若想采用cookies坐位数据存储方式,则需要另外一种写法;
- render接收一个函数,返回值为一个对象;返回的对象中的键值对既是要持久化存储的数据;
- 若想持久化存储部分数据,请在return的对象中采用key:value键值对的方式进行数据存储,render函数中的参数既为state对象
js闭包
1.js闭包就是可以读取其他函数内部的变量
function outerFunction() {
let outerVariable = '我在outer函数里!';
function innerFunction() {
console.log(outerVariable);
}
return innerFunction;
}
const innerFunc = outerFunction();
innerFunc(); // 输出: 我在outer函数里!
2.闭包的用途
1.封装私有变量
闭包可以用于封装私有变量,以防止其被外部访问和修改。封装私有变量可以一定程度上防止全局变量污染,使用闭包封装私有变量可以将这些变量限制在函数内部或模块内部,从而减少了全局变量的数量,降低了全局变量被误用或意外修改的风险。
在下面这个例子中,调用函数,输出的结果都是1,但是显然我们的代码效果是想让count每次加一的。
function add() {
let count = 0;
count++;
console.log(count);
}
add() //输出1
add() //输出1
add() //输出1
一种显而易见的方法是将count提到函数体外,作为全局变量。这么做当然是可以解决问题,但是在实际开发中,一个项目由多人共同开发,你不清楚别人定义的变量名称是什么,这么做有点冒险,有什么其他的办法可以解决这个问题呢?
function add(){
let count = 0
function a(){
count++
console.log(count);
}
return a
}
var res = add()
res() //1
res() //2
res() //3
答案是用闭包。在上面的代码示例中,add函数返回了一个闭包a,其中包含了count变量。由于count只在add函数内部定义,因此外部无法直接访问它。但是,由于a函数引用了count变量,因此count变量的值可以在闭包内部被修改和访问。这种方式可以用于封装一些私有的数据和逻辑。
2.做缓存
函数一旦被执行完毕,其内存就会被销毁,而闭包的存在,就可以保有内部环境的作用域。
function foo(){
var myName ='张三'
let test1 = 1
const test2 = 2
var innerBar={
getName: function(){
console.log(test1);
return myName
},
setName:function(newName){
myName = newName
}
}
return innerBar
}
var bar = foo()
console.log(bar.getName()); //输出:1 张三
bar.setName('李四')
console.log(bar.getName()); //输出:1 李四
3.闭包的缺点
闭包会造成内存泄露的问题
闭包也存在着一个潜在的问题,由于闭包会引用外部函数的变量,但是这些变量在外部函数执行完毕后没有被释放,那么这些变量会一直存在于内存中,总的内存大小不变,但是可用内存空间变小了。 一旦形成闭包,只有在页面关闭后,闭包占用的内存才会被回收,这就造成了所谓的内存泄漏。
解决闭包的内存泄漏的问题
1.及时释放闭包:手动调用闭包函数,并将其返回值赋值为null,这样可以让闭包中的变量及时被垃圾回收器回收。
2.使用立即执行函数:在创建闭包时,将需要保留的变量传递给一个立即执行函数,并将这些变量作为参数传递给闭包函数,这样可以保留所需的变量,而不会导致其他变量的内存泄漏
什么是原型,什么是原型链
1.显示原型
每一个类都有一个(构造函数)都有一个显示原型的prototype(本质就是一个原型)
02 隐式原型
每一个实例都有一个隐式原型__proto__
03 显式原型与隐式原型的关系
类显示原型的prototype等于其创建的隐式原型_proto_
var arr = [];
arr.__proto__ === Array.prototype
查找对象实例的方法和属性时,先在自身找,找不到则沿着__proto__向上查找,我们把__proto__形成的链条关系称原型链
(实现了js继承)
04 原型链
查找对象实例的方法和属性时,先在自身找,找不到则沿着__proto__向上查找,我们把__proto__形成的链条关系称原型链
(实现了js继承)
let arr1={}
let arr2={}
arr2.prototype=arr1
5. 什么是类,什么是实例
01 类:是创建对象实例的模板 本质上讲类是个函数 (例如Array object String )
001 构造函数 :用new来执行的函数
002 class xxx{}
02 实例:就是由类创建的对象 本质上就是对象
例如 [1,2,3] {name : "mumu " } , “abc”
数据可视化性能优化
数据预加载与懒加载
数据懒加载:在应用启动时,预先加载关键数据,这样可以用户查看大屏时减少等待时间。
懒加载:对于关键数据或大型数据文件,可以咋子用户滚动到相应位置或触发特定事件或触发特定事件再加载,减少初始加载时间
数据分页与虚拟滚动
- 数据分页:将数据分成多个小批次,只加载当前页面需要的数据,用户翻页时再加载下一批数据。
- 虚拟滚动:当处理大量数据时,不一次性加载所有数据,而是根据用户滚动位置动态加载和卸载数据项,这样可以显著减少内存占用和提高响应速度。
使用高效的图表库
选择性能优良的图表库,如ECharts、D3.js等,这些库通常经过优化,能够高效地渲染大量数据。