目录
reactive定义的变量重新赋值会失去响应式,而ref不会
vue的响应式原理
数据变更,视图自动更新
使用Object.defineProperty()方法来监听属性的变化,实现双向数据绑定。Object.defineProperty()方法可以控制对象的属性访问权限,实现响应式原理,当定义一个响应式属性时,Vue将使用Object.defineProperty()方法绑定该属性的getter和setter方法到一个观察者对象。当有任何对属性的更改时,它将触发setter方法,然后执行相应的更新操作。
当对数组进行操作(如push、pop、shift、unshift、splice、sort、reverse等)时,Vue能够捕获到这些变化,并触发视图的更新
对数组进行以下操作时,Vue无法自动触发更新:
1.通过索引直接修改数组元素的值:this.items[index] = newValue
2.修改数组的长度:this.items.length = newLength
Object.definePrototy(obj,'name',{
configuable:false,//不可被删除
enumerable:false,//不可被枚举(遍历)
writable:false,//不可被修改
value:'zhangsan'
})
当我们修改对象的属性值时,Vue能够捕获到这个变化并触发更新。这是因为Vue在对象上使用了Object.defineProperty来重写属性的访问器(getter和setter),从而能够追踪对象属性的变化。
如果要在响应式对象上添加新的属性,使用$set让新添加的属性也具有响应性
vue2.x的响应式
对象类型:通过Object.defineProperty()对属性的读取、修改进行拦截(数据劫持)。
数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
存在问题:
新增属性、删除属性, 界面不会更新。
直接通过下标修改数组, 界面不会自动更新。
Vue3.0的响应式
实现原理:
通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
通过Reflect(反射): 对源对象的属性进行操作。
路由跳转与拦截
拦截:判断token是否存在
token的保存:后端存,后端接口让浏览器把token放cookie里,前端调取token判断使用
vue2和vue3的生命周期
setup取代created,beforecreated
beforeCreate
-> 使用setup()
created
-> 使用setup()
beforeMount
->onBeforeMount
mounted
->onMounted
beforeUpdate
->onBeforeUpdate
updated
->onUpdated
beforeDestroy
->onBeforeUnmount
destroyed
->onUnmounted
watch和computed的区别
监听数据的变化
watch不支持缓存,支持异步,监听函数有两个参数,第一个参数是最新的值,第二个参数是输入之前的值
computed计算属性会缓存数据结果,只有依赖性数据改变才会重新计算,在computed中的,属性都有一个 get 和一个 set 方法,当数据变化时,调用 set 方法,如果 computed 属性值是函数,那么默认会走 get 方法,必须要有一个返回值,用return返回,函数的返回值就是属性的属性值;在 computed 属性对象中定义计算属性的方法,和取data对象里的数据属性一样,以属性访问的形式调用;
vue返回上一级页面
this.$router.go(-1)
uniapp的生命周期
页面生命周期:
1.onLoad:监听页面加载,其参数为上个页面传递的数据,参数类型为object(用于页面传参),只会执行一次,只有当页面刷新会重复执行。
2.onShow:监听页面显示
3..onReady:监听页面初次渲染完成,页面进入动画完成前触发
4.onHide:监听页面隐藏
5.onUnload:监听页面卸载
数组的遍历方法
数组原型方法主要有以下这些
-
join():用指定的分隔符将数组每一项拼接为字符串
-
push() :向数组的末尾添加新元素
-
pop():删除数组的最后一项
-
shift():删除数组的第一项
-
unshift():向数组首位添加新元素
-
slice():按照条件查找出其中的部分元素
-
splice():对数组进行增删改
-
fill(): 方法能使用特定值填充数组中的一个或多个元素
-
filter():“过滤”功能
-
concat():用于连接两个或多个数组
-
indexOf():检测当前值在数组中第一次出现的位置索引
-
lastIndexOf():检测当前值在数组中最后一次出现的位置索引
-
every():判断数组中每一项都是否满足条件
-
some():判断数组中是否存在满足条件的项
-
includes():判断一个数组是否包含一个指定的值
-
sort():对数组的元素进行排序
-
reverse():对数组进行倒序
-
forEach():ES5 及以下循环遍历数组每一项
-
map():ES6 循环遍历数组每一项
-
copyWithin():用于从数组的指定位置拷贝元素到数组的另一个指定位置中
-
find():返回匹配的值
-
findIndex():返回匹配位置的索引
-
toLocaleString()、toString():将数组转换为字符串
-
flat()、flatMap():扁平化数组
-
entries() 、keys() 、values():遍历数组
-
改变原数组的方法:push、pop、shift、unshift、sort、splice、reverse
如何中断promise
通过抛出一个异常throw
通过reject
uniapp组件之间传参
1.uni.$emit,uni.$on,uni.$off
页面 onLoad 里边 uni.$on 注册监听,onUnload 里边 uni.$off 移除,或者一次性的事件,直接使用 uni.$once 监听,onBackPress()监听页面返回
2.uni.navigateTo,onload(上个页面传递的参数)
vue3
setup就是vue2里面的data,method,computed,所有数据方法全写在setup里)
js几种获取对象key的方法和区别
object.keys for...in
getOwnPropertyNames()返回一个对象自身所有的属性,包括可枚举和不可枚举属性组成的数组
ref和reactive的区别
1. 数据类型不同:ref用于包装JavaScript基本类型的数据(如字符串、数字、布尔值等),而reactive可以用于包装JavaScript对象和数组等复杂类型的数据。
2. 使用方式不同:ref需要通过在模板中使用ref指令以及在JavaScript代码中使用ref函数进行创建和使用,而reactive则需要通过调用Vue.js提供的reactive函数进行包装和创建。
3. 访问方式不同:对于通过ref函数创建的响应式数据,我们可以通过.value属性来访问其实际值;而对于通过reactive函数创建的响应式对象,我们可以直接访问其属性或调用其方法。
4. ref本质是reactive({value:"值"})
reactive通过Proxy来实现响应式(数据劫持),并通过Reflect操作源对象内部的数据
vue的设计模式
- 发布-订阅模式。发布者发起事件,所有的订阅者都会执行,例如,Vue的事件机制。
- 观察者模式。观察者与被观察者之间存在一对多的关系,被观察者发生变化时,通知所有观察者进行更新,例如,Vue的响应式数据原理
清除浮动
给父级元素添加了一个:after伪元素
.xpsf ul.p_list::after {
content: "";
display: block;
clear: both;
}
给父元素添加 overflow:hidden
常见伪元素选择器:
::after
::before
常见伪类选择器:
:first-child :hover :focus :last-child :nth-child(n) :nth-last-child(n)
v-for中的key
因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM
使用v-for渲染元素时,使用元素自身的id属性去指定渲染元素的key值有利于单个元素的重新渲染,若采用其他如v-for提供的index, key等值,在改变渲染出来的DOM结构时,会触发所有元素的重新渲染,当数据过大时,可能会造成性能负担。
闭包
闭包通常是通过在一个函数内定义另一个函数来实现的,这样的内部函数可以访问外部函数的局部变量
call、bind、apply三者的区别
call、bind、apply 都是 JavaScript 中用于改变函数执行上下文(即 this 指向)的方法。
call 和 apply 的作用是一样的,都是用来调用函数并且改变函数内部的 this 指向。区别在于传参的方式不同,call 的参数是一个一个传递的,而 apply 的参数是以数组的形式传递的。
bind 方法不会立即执行函数,而是返回一个新的函数,这个新的函数的 this 值被绑定到了指定的对象,调用时也可以传入参数。同时使用 bind 方法可以实现柯里化,即将函数转化为接收部分参数的函数。
vue创建组件
Vue
通过app.component(组件名称, 组件的对象)
的方式创建一个全局组件- 注册成功后,可以在任意其他组件的
template
中使用
vue兄弟组件之间通信
1父组件接收数据,并通过 props 将数据传递给第一个子组件
3父组件再将数据传递给第二个子组件。
方法二:使用Vuex
Vuex 是Vue的官方状态管理库,它可以将组件的共享状态抽离出来,以一个全局单例模式管理。通过 Vuex,兄弟组件可以直接共享状态,实现数据的传递。具体实现步骤如下1.在Vuex的store 中定义共享状态
2兄弟组件分别通过计算属性来获取和修改共享状态。
方法三:使用事件总线
事件总线是一种简单的跨组件通信方式,可以在任何组件中触发和
监听自定义事件。兄弟组件通过事件总线来传递数据。具体实现步
骤如下
1.创建一个事件总线实例。
2兄弟组件分别通过$emit 方法触发自定义事件,并通过$on 方法
监听自定义事件
方法四:使用$refs
$refs是Vue中一个特殊的属性,可以用来访问组件实例或元素兄弟组件通过$refs来获取其他组件的实例,并直接访问其数据。
vue监听多个属性
(1)watch里面写多个监听方法;
(2)将多个数据放在一个对象中,只监听这个对象的变化;
(3)computed和watch连用,watch监听computed的属性:
computed属性会在依赖变化后重新计算,如果我们在某个computed属性中依赖于我们想要监听的属性,当这些属性变化时,此computed属性也会被重新计算,然后只需监听此computed属性即可;利用computed属性依赖变化会导致重新计算的机制可以更加优雅的实现同时监听多个属性变化的效果,而且由于computed是有缓存机制的,性能上也更具优势。
watch和watchEffect的区别
watch需要显式指定要监听的数据和回调函数,而watchEffect会自动追踪依赖的响应式数据,并在数据变化时执行回调函数
在Vue3中,watch默认是惰性执行的,即在组件第一次加载时不会立即执行,只有数据变化时才会执行,而watchEffect在页面首次加载时就会立即执行 ,当前watchEffect 返回的一个函数,调用这个函数时,即可停止监听,watchEffect 也可以监听多个参数,只是不能监听对象,因为他无法监测对象内部的变化,可能是watchEffect 无法实现深度监听吧
vue3 setup语法糖
它位于组件实例的创建阶段之前,用于定义组件的逻辑和状态
所定义的变量都会自动返回,无需return,所有的组件导入即可自动注册
import { defineProps,defineEmits,useAttrs,useSlots,computed,watch,toRefs} from 'vue'
import { ref,reactive,onBeforeMount,onMounted,onBeforeUpdate,onUpdated,onBeforeUnmount,onUnmounted } from ‘vue’
const props = defineProps({
total: {
type: Number,
default: 0
},
page: {
type: Number,
default: 2
}
})
const emit = defineEmits(['update:page'])
const emit = defineEmit(['onHeaderClick'])
emit('onHeaderClick','params')
const attrs = useAttrs()// attrs 主要接收没在 props 里定义,但父组件又传过来的属性, 接受父组件传过来的属性
const slots = useSlots()
//侦听多个数据
watch([count, name], ([newCount, newName],[oldCount,oldName])=>{
console.log(`count或者name变化了,[newCount, newName],[oldCount,oldName])
})
toRefs,useContent
toRefs
是一个函数,用于将响应式对象中的属性转换为普通的响应式引用(ref),因为当我们直接使用 reactive
创建响应式对象时,其属性不会被自动解包成响应式引用。而通过 toRefs
,我们可以确保属性始终是响应式的。vue2中通过this.$refs访问子组件中的dom元素或组件实例,toRef() 函数的作用就是将 obj下的count 提取出来,方便直接使用变量count, 避免obj.count 的写法。
<script setup>
import { ref, toRef } from 'vue'
const obj = reactive({
tableData: [], // 数组对象
count: 10, // 普通变量
})
// 使用 toRefs
let { tableData, count } = toRefs(obj) // 把第一层对象obj 作为参数即可
// 修改数据
// 同样需要调用 .value
tableData.value = ???
count.value = ???
let obj1 = reactive({
count1: 100
})
// 2:toRef
let count1 = toRef(obj1, 'count1') // 将对象obj,和变量名作为参数
// 3:修改,仍然通过属性名.value 的形式修改
function addCount(){
count1.value ++;
}
</script>
在setup里通过 toRefs 把props转换成可响应对象,解构取isDot, max, value的值,最后通过“useContent”返回content,获取上下文
setup(props) {
const { isDot, max, value } = toRefs(props)
const content = useContent(isDot, max, value)
return {
content
}
}
}
watch
函数接收两个参数:要监听的数据或表达式,以及一个回调函数来响应数据变化。
watch
函数接收两个参数:要监听的数据或表达式,以及一个回调函数来响应数据变化。watch
函数内部使用响应式引用来追踪依赖关系,只有在回调函数中使用的响应式数据发生变化时,才会执行回调函数。
reactive定义的变量重新赋值会失去响应式,而ref不会
图片懒加载
先不给页面设置路径,当图片位于可视区域时才加载图片
1.安装懒加载插件,例如vue-lazyload
2.注册插件
Vue.use(VueLazyload, {
//参数配置 可不填
// 懒加载默认加载图片
loading: 'xxx.png',
// 加载失败后加载的图片
error: 'xxx.png',
preLoad: 1.3, // 预加载高度的比例
attempt: 3 // 尝试加载次数
})
3.vue-lazyload提供了一个自定义指令v-lazy
,可以在img标签或者任何需要设置背景图片的标签上使用它。例如:
<!-- 懒加载img标签 -->
<img v-lazy="imgUrl" /><!-- 懒加载背景图片 -->
<div v-lazy:background-image="bgUrl"></div>
v-lazy指令接收一个字符串类型的值,表示图片的地址。如果是背景图片,需要在指令后加上:background-image修饰符(注意冒号)。
当页面滚动时,vue-lazyload会检测元素是否进入可视区域,如果是,则替换元素的src或者style属性,从而实现懒加载。
注意:若图片为循环渲染、分页显示,则必须写key值,不然切换页面后页面视图不刷新
-
使用懒加载(Lazy Load):当图片位于可视区域时才加载。
-
使用懒加载指令或组件:可以简化懒加载的实现。
-
优化图片质量和大小:减少图片的体积和数量。
懒加载组件:使用动态导入(import()
)来分割代码并懒加载组件。
缓存
1. 组件级别的缓存 🧩
Vue提供了<keep-alive>组件,它可以将组件缓存起来,避免重复渲染和销毁。使用<keep-alive>👉包裹需要缓存的组件,可以在组件切换时将其保留在内存中,以便下次使用。
2. 路由级别的缓存 🚀,
keep-alive可以在组件切换时,保存其包裹的组件的状态,使其不被销毁,避免重新渲染。其拥有两个独立的生命周期钩子函数 actived 和 deactived,使用keep-alive包裹的组件在切换时不会被销毁,而是缓存到内存中并执行 deactived 钩子函数,命中缓存渲染后会执行 actived 钩子函数。
Vue Router允许我们对路由进行缓存配置。通过在路由配置中添加meta字段,可以指定需要缓存的路由。这样,在路由切换时,被缓存的组件将保留在内存中,以便下次快速加载和渲染。
3. 数据级别的缓存 💾
Vue中的计算属性和watch监听器可以用于缓存数据结果,减少重复计算的开销。通过将计算结果缓存起来,并在依赖值发生变化时重新计算,可以提高性能和响应速度。
<template>
<keep-alive>
<router-view></router-view>
</keep-alive>
</template>
const routes = [
{
path: '/',
name: 'Home',
component: Home,
meta: { keepAlive: true } // 设置缓存
},
// 其他路由配置
]
data() {
return {
items: [/* 数据数组 */]
}
},
computed: {
cachedItems() {
// 对数据进行缓存
return this.items.filter(/* 过滤操作 */)
}
}
登录验证
在Vue应用中实现登录验证通常涉及以下步骤:
-
前端发送登录请求,通常是通过发送POST请求到后端API。
-
后端验证用户凭据,如果成功,生成一个Token,并将其返回给前端。
-
前端将Token存储在本地,通常存储在
localStorage
或sessionStorage
中。 -
之后的每个请求都会包含这个Token,以便后端验证用户身份。
webpack打包优化
拆包:
代码分离
CDN:一些第三方资源放到CDN服务器上,优化网络加载速度,将 vue、vue-router、vuex、element-ui 和 axios 这五个库,全部改为通过 CDN 链接获取,在 index.html里插入相应链接。
1、减少入口文件大小: 将入口文件拆分为多个较小的模块,使用动态导入(dynamic imports)按需加载,减少初始加载的文件大小。
2、代码分割(Code Splitting): 通过配置Webpack的代码分割功能,将项目代码分割成多个块(chunks),并且在需要的时候按需加载。
3.图片懒加载
盒模型与flex布局的区别
盒模型:每一个HTML元素就相当于一个矩形的“盒子”,这个盒子有边框(border)、内容(content)、内边距(padding)、外边距(margin)构成。
标准盒模型和怪异盒模型:
box-sizing: content-box;width(content) + padding + border + margin
box-sizing: border-box;width(content + padding + border) + margin
width=border+padding+content