1、说一下vue2和vue3双向数据绑定的区别?
vue2.x的双向数据绑定是通过数据劫持结合发布者订阅者模式的方式来实现的,通过Es5的api object.defineProperty来劫持各个属性的setter,getter,在数据变化时发布消息给订阅者,触发相应的监听回调来渲染视图。object.defineProperty的缺点:
1、无法发现对象新增和被删除的属性,当你给一个对象添加一个新的属性,这个新增的属性没有添加到 vue 的数据更新侦查机制里
2、当你利用索引直接设置一个数组(new Array(4))或者修改数组的长度时,Vue 不能检测到数组的变动
所以我们需要使用Vue.$set()来设置新增的数据
在vue3中我们定义响应式数据可以有ref和reactive两种方法,ref一般定义简单数据类型数据,reactive一般定义响应式数据。ref定义的数据响应式原理还是object.defineProperty,reactive定义对象的原理是利用es6新增的API proxy。如果用ref定义对象 也会默认的转换为proxy。proxy已经规避掉了object.defineProperty的缺点。
2、vue3比vue2的优势
- 性能比vue2更快
原因:
-
- diff方法优化
vue2中的虚拟dom是全量的对比(每个节点不论写死的还是动态的都会比较)
vue3新增了静态标记(patchflag)与上次虚拟节点对比时,只对比带有patch flag的节点(动态 数据所在的节点);可通过flag信息得知当前节点要对比的具体内容
-
- 静态提升
vue2无论元素是否参与更新,每次都会重新创建然后再渲染
vue3对于不参与更新的元素,会做静态提升,只会被创建一次,在渲染时直接复用即可
-
- 时间侦听器缓存
默认情况下onClick会被视为动态绑定,所以每次都会追踪它的变化
但是因为是同一个函数,所以不用追踪变化,直接缓存起来复用即可
- 按需编译,体积比vue2.x更小
- 组合API
- 更好的Ts支持
- 模板可以有多个根元素
3、如何看待composition API 和options API
- 在逻辑组织和逻辑复用方面,Composition API是优于Options API
- 因为Composition API几乎是函数,会有更好的类型推断。
- Composition API对 tree-shaking 友好,代码也更容易压缩
- Composition API中见不到this的使用,减少了this指向不明的情况
- 如果是小型组件,可以继续使用Options API,也是十分友好的
4、watch和watchEffect的区别是什么
1、watch是显式监听,来设置监听哪个属性,watchEffect是隐监听,会根据齐中的属性,自动的监听
2、watch默认是值发生变化执行 watchEffect页面初始化就会执行
5、watchEffect与computed的区别
- 但是computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
- computed若是值没有被使用时不会调用,但是watchEffect始终会调用一次
6、setup中如何获取组件的实例
- setup和其他Composition API中都没有this
- 在Options API中仍然可以使⽤this
- Composition API中可以使⽤getCurrentInstance⽅法获取
7、vue3为什么要⽤proxy实现双向绑定
object.defineProperty的缺点:
1.因为es5的object.defineProperty⽆法监听对象属性的删除和添加
2.不能监听数组的变化,除了push/pop/shift/unshift/splice/spObject.definert/reverse,其他都不⾏
3.Object.defineProperty只能遍历对象属性直接修改(需要深拷⻉进⾏修改)
proxy的优点:
1.直接监听对象⽽⾮属性
2.直接监听数组的变化
3.拦截的⽅式有很多种(有13种,set,get,has) 4.Proxy返回⼀个新对象,可以操作新对象达到⽬的
proxy的缺点:
proxy有兼容性问题,不能⽤polyfill来兼容(polyfill主要抚平不同浏览器之间对js实现的差异)
1、vue2与vue3的区别
- vue2是选项api vue3是组合api
- 生命周期函数不一样 vue3多了setup 少了beforeCreate和created
- vue3是函数调用
- v2和v3双向数据绑定的原理是不一样的
在Vue2中,数据响应式主要借助Object.defineProperty()来实现,存在的缺陷是新增属性,删除属性以及直接通过下标修改数组,页面不会自动更新的问题,可以通过this.$set来解决了数据发生变化视图不更新的问题。在Vue3中,数据响应式主要借助proxy和Reffect配合实现,可以做到实现对象属性的增删改查,页面能够实时响应
- 删除了过滤器
2、vue3项目
vite vue-cli
3、定义响应式数据 ref reactive
可以通过ref函数和reactive函数
<script setup>
//导入一般情况下放在第一行
import {ref} from "vue"
//定义一个变量(这种方式定义的变量不是响应式的)
let username="xiaosan"
//怎么定义一个响应式的变量的 ref reactive
let stuname=ref("lisi")
let age=ref(12)
let bool=ref(true)
let arr=ref([1,2,3])
let obj=ref({username:'笑死'})
console.log(stuname)
//定义一个方法
const changename=()=>{
stuname.value="zhangxiaosan"
arr.value.push(5)
}
</script>
reactive和ref的区别:
- 在定义上说 ref可以定义所有类型的数据,reactive只是定义引用数据类型的数据
- 在原理上 ref定义的数据还是依据object.defineProperty()来实现的 通过ref定义的对象也是在内部转换为proxy。而reactive的原理是通过proxy来定义的数据,
- 在使用上 ref定义的数据 重新赋值需要.value来实现 reactive是不用的
4、 toRef函数和toRefs函数
toRef()函数是从响应对象中解构出一个变量,并且值是关联的
用法:let 变量名=toRef(对象,"属性")
当修改变量的值的时候 属性也会发生改变
const obj=reactive({
username:"小三",
age:20
})
let username=toRef(obj,"username")
//定义一个方法
const changename=()=>{
username.value="xiaosi"
}
toRefs() 函数是将响应对象多个属性解构出来,值也是关联的
<script setup>
//导入一般情况下放在第一行
import {ref,reactive,toRefs} from "vue"
const obj=reactive({
username:"小三",
age:20
})
let {username}=obj
console.log(username)
const newobj=toRefs(obj)
console.log(newobj)
//定义一个方法
const changename=()=>{
obj.username="xiaosi"
}
</script>
5、使用vue3实现一个留言板
6、vue3的计算属性定义
语法:const 计算属性名=computed(()=>{
return
})
//女生的数量
const femailcount=computed(()=>{
//filter 过滤 返回符合条件的数据 返回的是一个新数组
let arr=stulist.value.filter(item=>item.sex=="女")
return arr.length
})
//年龄大于22的学生的数量
const count=computed((item)=>{
let arr=stulist.value.filter(item=>item.age>22)
return arr.length
})
7、watch
语法:
watch(变量(可以一个变量,也可以多可,用,隔开),()=>{
//自己的代码
},{
深度监听 和立即执行的参数
})
//监听的ref定义的响应数据
watch(stulist,(val)=>{
console.log(val.length)
},
{
deep:true,//深度监听
immediate: true//立即执行 页面初始化的时候执行 不用等着改变
})
//也可以单独的监听对象的属性 需要写成箭头函数的样子
watch(()=>stuobj.stuname,(val)=>{
console.log(val)
})
//监听多个值
watch([stuobj,stulist],(val)=>{
console.log(val)
})
8、watchEffect
语法:
watchEffect(()=>{
//回调
})
watch与watchEffect的区别:
1、watch是显式监听,watchEffect是隐监听
2、watch默认是值发生变化执行 watchEffect页面初始化就会执行
watchEffect与computed:
- 但是computed注重的计算出来的值(回调函数的返回值),所以必须要写返回值。
- 而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值。
- computed若是值没有被使用时不会调用,但是watchEffect始终会调用一次
9、生命周期函数
回顾vue2.x生命周期钩子函数:
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
认识vue3.0生命周期钩子函数
- setup 创建实例前
- onBeforeMount 挂载DOM前
- onMounted 挂载DOM后
(操作dom元素 添加事件监听 请求接口数据 最早可以操作dom的狗子函数)
- onBeforeUpdate 更新组件前
- onUpdated 更新组件后
- onBeforeUnmount 卸载销毁前
- onUnmounted 卸载销毁后
10、在vue3中实现对金钱保留两位小数
在vue2中我们可以通过过滤器来实现,在vue3中没有过滤器了。我们可以通过定义hook函数来实现或者通过计算属性来实现
//写一个函数实现对于任意的数据保留两位小数
/*
功能:实现对数据的格式化 保留位数
@author 小四
@params num 要操作的数据源 count 保留的位数
@return 返回值是处理好的数据
*/
function setNum(num,count=0){
//首先保证传递的num是个数字
if(typeof num !="number"){
return false;
}
//数字
return num.toFixed(count)
}
export {setNum}
<template>
<div>
{{setNum(num1,2)}}
</div>
</template>
<script setup>
import {ref} from "vue"
//引入功能函数
import {setNum} from "@/hook/utils.js"
const num1=ref(123.321)
</script>
11、vue3的组件通信
1、父传子--->props:
- 在父组件中通过v-bind自定属性来传递数据
- 在子组件中通过defineProps来接受数据
2、子传父---->emit:
- 在子组件中导入 defineEmits
- 定义子组件元素上的方法
- 通过defineEmits定义事件
- 在子组件的方法中触发刚才定义的事件 同时传递参数
- 在父组件的子组件标签上挂载在子组件中定义的事件
- 再写父组件中执行的方法
3、refs来实现通信
- 在子组件中通过defineExpose来暴露让别人访问的数据
<template>
<div>
第二个子组件
</div>
</template>
<script setup>
import {ref, defineExpose} from "vue"
const classname=ref("2206B")
const test=()=>{
alert(123)
}
//语法糖中无法$parent 和 $ref是关闭 需要收到向外暴露
defineExpose({
classname,
test
})
</script>
<style lang="scss" scoped>
</style>
- 在父组件中通过refs.ctx.***来获取到子组件的属性和方法
<template>
<div>
<son-two ref="twoson"></son-two>
<button @click="getSon">获取子组件的数据</button>
</div>
</template>
<script setup>
import { getCurrentInstance } from '@vue/runtime-core';
//导入组件
import SonTwo from "@/components/SonTwo"
//通过refs获取到子组件的属性和方法 在语法糖中是没有this对象的 所以需要借助于函数
const currentInstance = getCurrentInstance()
const getSon=()=>{
console.log(currentInstance.ctx.$refs.twoson.classname)
}
</script>
4、依赖注入 provide/inject
在组件嵌套特别深的时候,我们可以使用依赖注入将组件的值 注入到孙组件中
这两个都是函数 用的时候必须先导入
1、发送 provide(“ming”,值)
<template>
<div>
<p>名字:{{username}}</p>
<son-two ref="twoson"></son-two>
<button @click="change">改变</button>
</div>
</template>
<script setup>
import {ref,provide} from "vue"
import SonTwo from "@/components/SonTwo"
const username=ref("hello")
provide("name",username)
const change=()=>{
username.value="hello world"
}
</script>
<style lang="scss" scoped>
</style>
2、在孙子组件中 inject(“ming”)
<template>
<div>
第二个子组件---{{name}}
</div>
</template>
<script setup>
import {ref,inject} from "vue"
const name=inject("name")
</script>
<style lang="scss" scoped>
</style>
5、v-model的语法糖
将数据传递给子组件,同时又需要在子组件中修改变量的时候,我们可以使用v-model的语法糖。这个语法糖其实就是父传子 和 改变数据 子传父的一个简写
父组件
<template>
<div>
<!-- 完成写法 -->
<!-- <son-two :sonname="username" @update:sonname="changename"></son-two> -->
<!-- 简写 -->
<son-two v-model:sonname="username"></son-two>
</div>
</template>
<script setup>
import {ref} from "vue"
import SonTwo from "@/components/SonTwo"
const username=ref("hello")
const changename=(name)=>{
username.value=name
}
</script>
<style lang="scss" scoped>
</style>
子组件
<template>
<div>
第二个子组件---{{sonname}}
<button @click="change">改变</button>
</div>
</template>
<script setup>
import {ref,defineProps,defineEmits} from "vue"
defineProps({
sonname:String
})
//定义一个事件 这个使用的是v-model的语法糖 需要在定义事件的时候用:指定要修改的属性
const emit=defineEmits(['update:sonname'])
const change=()=>{
emit("update:sonname","helloworld")
}
</script>
<style lang="scss" scoped>
</style>
6、vuex/pinia/本地存储
- vuex的概念
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
- vuex的运行原理
在组件中通过dispatch调用actions中的方法,在actions中通过commit调用mutations中的方法,在mutations中通过dispatch来修改state中的数据,只要state中的数据发生改变就会立刻响应到视图上。
- vuex的五大核心
-
- state 存储数据
- mutations
- actions
- getters
- modules
1、定义一个模块
export default {
namespaced:true,//默认的情况下只有state是局部模块的空间,mutations getters actions都会合到主分支上,所以我要开启命名空间 来做局部作用域
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
2、挂载一个模块
modules: {
user
}
3、使用模块
-
- state $store.state.模块名.属性名
- mutations store.commit("模块名/方法名",参数)
- getters store.state.模块名.getters
- actions store.dispatch("模块名/fangfaming",参数)
- vuex的持久化
可以使用vuex数据的持久化
- 下载插件cnpm install vuex-persistedstate --save
- 导入插件 import persistedState from 'vuex-persistedstate'
- 挂载插件默认的使用localStorageplugins: [persistedState()]还可以配置成sessionStorageplugins: [persistedState({storage: window.sessionStorage})]
12、闭包
- 什么是闭包
闭包就是指有权访问另一个函数作用域中的变量的函数
- 闭包的作用
- 闭包的第一个用途是使我们在函数外部能够访问到函数内部的变量。通过使用闭包,可以通过在外部调用闭包函数,从而在外部访问到函数内部的变量,可以使用这种方法来创建私有变量。
- 闭包的另一个用途是使已经运行结束的函数上下文中的变量对象继续留在内存中,因为闭包函数保留了这个变量对象的引用,所以这个变量对象不会被回收。
- 闭包在项目中使用以及带来的问题
在实际的项目中,会基于闭包把自己编写的模块内容包裹起来,这样编写就可以保护自己的代码是私有的,防止和全局变量或者是其他的代码冲突,这一点是利用保护机制。
但是不建议过多的使用闭包,因为使用不被释放的上下文,是占用栈内存空间的,过多的使用会导致导致内存泄漏。
解决闭包带来的内存泄漏问题的方法是:使用完闭包函数后手动释放。
- 闭包场景
-
- return 回一个函数
- 函数作为参数
- IIFE(自执行函数)
- 循环赋值
- 使用回调函数就是在使用闭包
- 节流防抖
- 函数柯里化
后期还会总结