Vue3 跟 Vue差别
vue2 和 vue3 的区别有以下8点:1、双向数据绑定原理不同;2、是否支持碎片;3、API类型不同;4、定义数据变量和方法不同;5、生命周期钩子函数不同;6、父子传参不同;7、指令与插槽不同;8、main.js文件不同。
vue2:vue2的双向数据绑定是利用ES5 的一个 APIObject.definePropert() 对数据进行劫持,结合发布订阅模式的方式来实现的。
vue3:vue3中使用了ES6 的 Proxy API对数据代理。相比vue2.x,使用proxy的优势如下:
· defineProperty只能监听某个属性,不能对全对象监听
· 可以省去for in,闭包等内容来提升效率(直接绑定整个对象即可)
· 可以监听数组,不用再去单独的对数组做特异性操作vue3.x可以检测到数组内部数据的变化。
vue2:vue2不支持碎片。
vue3:vue3支持碎片( Fragments ) ,就是说可以拥有多个根节点。
vue2:vue2使用选项类型****api,选项型api在代码里分割了不同的属性:data,computed,methods等。
vue3:vue3使用合成型****api,新的合成型api能让我们使用方法来分割,相比于旧的api使用属性来分组,这样代码会更加简便和整洁。
vue2:vue2是把数据放入data中,在vue2中定义数据变量是data(){} ,创建的方法要在methods:{} 中。
vue3:,vue3就需要使用一个新的setup()方法,此方法在组件初始化构造的时候触发。使用以下三个步骤来建立反应性数据:
· 从vue引入reactive;
· 使用reactive() 方法来声明数据为响应性数据;
· 使用setup()方法来返回我们的响应性数据,从而template可以获取这些响应性数据。
vue2:vue2 中的生命周期:
· beforeCreate 组件创建之前
· created 组件创建之后
· beforeMount 组价挂载到页面之前执行
· mounted 组件挂载到页面之后执行
· beforeUpdate 组件更新之前
· updated 组件更新之后
vue3:vue3 中的生命周期:
· setup 开始创建组件
· onBeforeMount 组价挂载到页面之前执行
· onMounted 组件挂载到页面之后执行
· onBeforeUpdate 组件更新之前
· onUpdated 组件更新之后
而且vue3.x 生命周期在调用前需要先进行引入。除了这些钩子函数外,vue3.x还增加了onRenderTracked 和onRenderTriggered函数。
vue2:父传子,用props,子传父用事件 Emitting Events。在vue2中,会调用****this$emit然后传入事件名和对象。
vue3:父传子,用props,子传父用事件 Emitting Events。在vue3中的setup()中的第二个参数content对象中就有emit,那么我们只要在setup()接收第二个参数中使用分解对象法取出****emit就可以在setup方法中随意使用了。
vue2:vue2中使用slot可以直接使用****slot;v-for与v-if在vue2中优先级高的是v-for 指令,而且不建议一起使用。
vue3:vue3中必须使用v-slot 的形式;vue3中v-for与v-if,只会把当前v-if当做v-for中的一个判断语句,不会相互冲突;vue3中移除keyCode作为v-on的修饰符,当然也不支持config.keyCodes;vue3中移除****v-on.native 修饰符;vue3中移除过滤器****filter。
vue2:vue2中我们可以使用pototype( 原型 ) 的形式去进行操作,引入的是构造函数。
vue3:vue3中需要使用结构的形式进行操作,引入的是工厂函数;vue3中app组件中可以没有根标签。
组件的通讯方式
props:用于父=》子组件通信
自定义事件:@on,@emit 可以实现子给父通信即vm.$emit( event, arg )
全局事件总线 eventBus:$bus 全能
pubsub-js:vue当中几乎不用(因为vue中有全局事件总线和这个第三方提供的库功能重复) 但全能
插槽****
vuex
Vuex 核心
State :保存所有组件的共享状态
Getters :类似状态值的计算属性
Mutations :修改 State 中状态值的唯一方法,里面包含状态变化监听器和记录器
Actions :用于异步处理 State 中状态值,异步函数结束后调用Mutations
Modules :当一个 State 对象比较庞大时,可以将 State 分割成多个Modules 模块。
watch如何监听一个对象,里面可以使用箭头函数吗,为什么
不能
watch属性:watch是vue实列的一个属性,它是一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用
方法一:直接写一个监听处理函数,每次监听到属性值(如cityName)变化,执行此函数
方法二:监听数据后面加字符串形式方法名,方法名在 methods 中定义
方法三:使用 immediate(立即)属性和 handler (处理者)方法,deep 深度监测。如果只需要监听对象中的一个属性值,使用字符串形式监听对象属性名
使用介绍:状态管理模式,采用集中式存储管理应用的所有组件的状态,vuex中数据变化时,引用改数据的组件会自动更新
全局唯一得状态管理仓(state),同步操作(mutations),异步操作(actions),缓存依赖(getters),模块(modules)
在组件中通过ajax请求获得数据,把数据存储在vuex中,就要先通过dispatch来通知actions来工作,actions里面的commit再通知mutations进行工作,最后mutations把数据放在state中,用来读取的状态集中放在store中,改变状态得方式是提交mutations,这个是同步的,异步逻辑应该封装在action中
修改elmentui样式
在style里面(不设置scoped)添加新的样式
deep深度选择器
在外层添加一层div,设置自定义类名,再修改里边的样式, 格式.自定义类名 .需要修改的样式 {}
git 的一些基本指令
git clone/pull 获取远端库项目。
git add . 把内容提交到缓存区。
git commit 提交到本地版本库+注释。
git push推送到远端库。
git status 查看文件状态
git branch新建一个分支。
git checkout 切换分支。
将分支与主枝master合并
git checkout master #(首先切换回主枝)
git merge AAA #(将分支AAA与主枝合并)
vue 中 computed 和 watch 的区别
computed
-
支持缓存,只有依赖数据发生改变,才会重新进行计算
-
不支持异步,当computed内有异步操作时无效,无法监听数据的变化
3.computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
5.如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法
Watch:
- 不支持缓存,数据变,直接会触发相应的操作;
2.watch支持异步;
3.监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
-
当一个属性发生变化时,需要执行对应的操作;一对多;
-
监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,
immediate:组件加载立即触发回调函数执行,
deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。
vue3 ref和reactive的区别
ref:定义任意类型数据返回值包含一个.value属性的响应式对象,基本类型通过object.defineProperty实现响应式,支持所有类型(字符串,数字,对象),可以重新分配给一个对象,是使用.value属性访问和更新的
const user = ref({name:1}) console.log(user.value.name)
reective:仅接受对象或数组,实现深度响应式,直接访问属性即可触发更新,支持对象或者数组,基本类型会报错 用于创建数据的核心API,ref可以处理基本类型和对象,需要通过object.assign或逐属性修改,不可以重新分配对象
const user = reactive({name:1}) console.log(user.name)
reactive清空
reactive定义的对象:let obj = reactive( { name:'123' } ) 循环清空原有属性,而不是替换整个对象,
Object.keys(obj).forEach(key => delete obj[key]) 二。Object.assign({},filters)
reactive定义的数组:let arr = reactive( [] )直接让arr.length = 0 就可以
vue3 watch和watchEffect的区别 监听
watch:需显示指定监听源并支持新旧对比,仅在监听数据发生确实改变时才会触发回调,提供变化前后的值,通过提供一个getter函数来指定依赖
单个:const num = ref(1)
watch(num,(newVal,oldVal)=>{console.log(oldVal,oldVal)})
多个:const num1 = ref(0) const num2 = ref(0)
watch([num1,num2],(newNum1,newNum2)=>{ console.log(newNum2, newNum3);})
watch(()=>num1,(num1)=>{},{deep:true})深度监听
watch(()=>num1,(num1)=>{},{immediate:true}) 即时回调侦听器,是否立即调用一次
watchEffect:自动搜集依赖并立即执行,更方便,初始化时会触发一次,追踪能访问到得所有响应式,只跟踪回调中被使用的属性,而不是递归跟踪所有得属性,对于需要侦听一个对象多个属性得情况watchEffect比深度侦听效率更高
跟踪对象多个属性
const nums = reactive({num1:1,num2:2,num3:3})
watchEffect(()=>{console.log(nums.num1,nums.num2,nums.num3)})
在 Vue 3 中,watch 和 watchEffect 都是用于观察和响应 Vue 实例上的数据变化的功能,但它们在使用方式、响应性追踪和行为上有一些关键区别。
-
使用方式:
watch需要显式地指定要观察的数据源(可以是一个 getter 函数、一个 ref、一个 reactive 对象或它们的计算属性),以及当数据源变化时要执行的回调函数。watchEffect则自动追踪响应式依赖,并在任何依赖项变化时执行一个函数。你不需要显式地告诉它要观察什么;它会自动收集依赖。
-
响应性追踪:
- 在
watch中,你通过提供一个 getter 函数来指定依赖项。Vue 会追踪这个函数内部访问的所有响应式属性,并在它们变化时触发回调。这种追踪是精确的,只包括你明确指定的属性。 watchEffect的追踪是自动的。它运行一个函数,并“记住”这个函数访问了哪些响应式属性。当这些属性中的任何一个变化时,watchEffect都会重新运行该函数。这种方式的追踪可能更加宽泛,因为它包括了函数执行过程中访问的所有响应式数据。
- 在
-
执行时机:
watch在初始化时不会立即执行回调,除非明确设置了immediate: true选项。它只在依赖的数据变化时执行。watchEffect在初始化时会立即执行一次,以收集依赖,并在任何依赖项变化时再次执行。
-
清理:
- 当你不再需要观察者时,可以使用
watch返回的停止函数来手动停止观察。这对于避免内存泄漏和不必要的计算很有用。 watchEffect同样返回一个停止函数,允许你在需要时停止观察。
- 当你不再需要观察者时,可以使用
-
用途:
watch更适合用于特定的、已知的数据源的观察,特别是当你需要精确控制何时以及如何响应数据变化时。watchEffect更适合用于快速原型设计或当你不想显式指定所有依赖项时。它的自动追踪特性可以简化代码,但也可能导致意外的重新执行,特别是当函数内部有复杂的逻辑或副作用时。
总的来说,watch 和 watchEffect 在 Vue 3 中提供了灵活且强大的数据观察机制。选择哪一个取决于你的具体需求和偏好。
nextTick的原理和作用
作用:让回调函数在DOM更新完成后再执行回调,通过监听异步队列的完成状态,确保回调拿到得最新的DOM,依赖浏览器的时间循环机制
主要通过微任务优先实现,可以确保在更新完后执行回调函数,从而避免因为DOM更新导致得一些问题
在数据更新之后,获取最新得DOM值,计算位置大小,确保操作最新得DOM状态,在生命周期钩子中,执行一步操作
inject和provide
跨组件通信,解决了props需要逐层传递的繁琐问题,响应式数据传递
provide(提供):父组件提供
import {provide} from 'vue
provide('appTheme','dark')
inject(注入):子组件注入数据
import {inject} from 'vue'
const theme = inject('appTheme','light') // 默认值light
console.log('theme') //dark
vue2组件传参
父传子:props,声明周期顺序:父beforeMount-子beforeCreate..子mounted-父mounted,在父组件通过使用子组件标签,并添加属性来传递props使用v-bind或者:来绑定父组件的数据
子传父:$emit,使用自定义事件,在父组件中监听
父组件调用子组件的方法(on):父 click(){this.refs.child.emit('childMethod','123')} 子:mounted(){this.on('childMethod',(res)=>{console.log(res,'123')})}
爷孙组件得参数传递:provide和inject
兄弟组件传参:eventBus,相当于所有组件共用一个事件中心,用来管理事件, 任意组件只要导入 bus (bus.js) 就可以随意,发送 与监听数据
使用:再要发送数据的组件中导入bus.js,使用bus.$emit('msgChange','123')
接收数据:再有接收数据的数组中导入bus.js,并使用bus.on('msgChange',(event)=>{this.mag = event}) //mag=123
组件销毁时需要解绑监听beforeDestory(){bus.$off('msgChange')}
通过children可以拿到父子组件的实例。从而调用实例的方法
代码复用和处理
组件化开发:将ui分隔成小的独立的组件,提高可维护性
使用插槽slots
混入(Mixins)
自定义指令
js
reduce方法
求和,数据转换等复杂操作,数组扁平
对数组中得每个元素执行一次提供的回调函数,函数为升序执行,并将其结果返回汇总为单个值,回调函数(必须)
accumulator:累积值数组的第一个元素
currentValue:当前处理的元素
index(可选):当前元素索引
array(可选):原数组引用
常和concat一起用返回新数组
promise和async await的区别 都是非阻塞的
promise是是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大,简单地说,Promise好比容器,里面存放着一些未来才会执行完毕(异步)的事件的结果,而这些结果一旦生成是无法改变的
async await也是异步编程的一种解决方案,他拥有内置执行器,不需要额外的调用直接会自动执行并输出结果,它返回的是一个Promise对象,await只能用在async函数里,等待后面的函数执行完成,在执行下面得语句
普通函数和箭头函数的区别?
箭头函数是匿名函数,不能作为构造函数,不能使用new
箭头函数不绑定arguments,取而代之用rest参数...解决
箭头函数不绑定this,会捕获其所在的上下文的this值,作为自己的this值
箭头函数没有原型属性
箭头函数通过 call() 或 apply() 方法调用一个函数时,只传入了一个参数,对 this 并没有影响。
箭头函数不能当做Generator函数,不能使用yield关键字
遍历数组的方法:
1.forEach
2.Map它提供一个回调函数,参数依次为处于当前循环的元素、该元素下标、数组本身,三者均可选。默认返回一个数组,这个新数组的每一个元素都是原数组元素执行了回调函数之后的返回值。使用break不能中断循环,不改变原数组
3.filter,默认返回一个数组,原数组得元素执行了回调函数之后返回值若为true,则将这个元素放入返回的数组中,不改变原数组
4.some,every返回值全部为true时,every方法会返回true,只要一个为false,every方法就会返回false。当有一个为true时,some方法返回true。当全部为false时,every会返回false,不改变原数组
5.reduce方法有两个参数,第一个参数是一个回调函数(必须),第二个参数是初始值(可选),回调函数有四个参数,为本轮循环得累计值,当前循环得元素(必须),该元素的下标(可选),数组本身(可选)
reduce方法,会让数组的每一个元素执行一次回调函数,并将上一次循环时回调函数得返回值作为下一次循环的初始值,最后将这个结果返回,使用场景:求和,字符串处理,复杂数据结构处理,数据转换,多维数据处理
数组展平:使用reduce(),你可以将多维数组转换为一维数组。 var matrixElements = rows.reduce( function (prev, current) { return prev.concat(current); });
如果没有初始值则reduce会将数组得第一个元素作为循环开始的初始值。第二个元素开始执行回调函数,不改变原数组
6.for of遍历的只是数组内的元素,他可以响应break,continue,return
7.for inindex索引为字符串数字,不能直接进行几何运算 2.遍历顺序有可能不是按照实际数组得内部顺序 3.使用for in会遍历数组的可枚举性,for in更适合遍历对象,不要使用for in遍历数组,遍历对象的自身属性通过hasOwnPropety方法
join()将数组元素结合为一个字符串
pop()从数组中删除最后一个元素:返回删除的值
push()在数组结尾处新添加一个新的元素,返回新数组的长度
shift()方法会删除收个数组元素,把所有其他元素"位移"到更低的索引,返回删除的值
unshift()在开头添加新元素,并“反向位移”,返回新长度
splice()将数组添加新元素,参1从什么位置开始,参2删除多少个元素,参3要插入的元素,返回被删除得元素组成的新数组
concat()方法通过合并链接现有数组来创建一个新数组:方法不会改变现有数组,总是返回一个新数组
slice()方法用数组的某个片段切出新数组,参1开始位置,参2前结束
array.isArray()判断一个对象是不是数组,返回的是布尔值
sort()排序 a-b从小到大,b-a从大到小
reverse()翻转数组
indexOf,lastIndexOf(),返回要查找得首次出现的位置,在没有找到的情况下返回-1
array.from()用于类似数组的对象和可遍历对象转为真正的数组
array.of将一阻值转变为数组
字符串方法
1.charAt()查找返回指定位置的字符
2.charcodeAt()查找返回指定位置字符的unicode码
3.concat()连接字符串
4.slice()字符串截取,参1开始,到参2前结束
5.split()将字符串转为数组
6.substring()字符串截取 参1开始,到参2前结束
7.substr()字符串截取,参1开始,到参2为个数
8.indexOf()遍历字符串,查找包含第一个参数的字符串,如包含返回第一位的下标,停止查找,如果没有返回-1,第二个参数表示从此下标开始查找
9.replace()替换字符串,只能匹配一次,将参1替换为参2
10.match()查找字符串
11.includes()检测是否包含指定字符串
BFC
块级格式上下文,形成了一个独立的渲染区域,触发了BFC规则的元素,像是一个独立的容器,容器里面的子元素不会再布局上影响外面的元素,如被隔离
触发的条件:1.浮动元素。float属性值不为one
2.绝对定位元素,position属性值为absolute,fixed脱离文档流
3.overflow得属性值为hidden,auto,scroll
4.display的值为tabel-cell,inline-block
优点:清楚元素之间的影响。清除内部浮动元素对父级元素的影响。创建自适应布局
let const var的区别
var声明变量存在变量提升,let和const不存在变量提升
let,const都是块级局部变量,就是只在当前代码块起作用,let不允许在相同作用域内,重复声明同一个变量
const声明时候必须赋值,声明后不能再修改,定义常量
$nexttick
当数据更新了在dom中渲染后,自动执行该函数。 vue实现响应式并不是数据发生变化后dom立即变化,而是按照一定策略来进行dom更新。 $nextTick()是在下一次dom更新循环结束之后在执行延迟回调,在修改数据之后使用这个方法更新dom
hash和 history
地址栏展示不同,hash有#
history在服务端有配置,不配置一刷新会出现404,hash不用配置
hash监听hashChange事件,history监听popstate事件
hash不会刷新页面,history会刷新页面
说一下cookie,sessionStorage和localStorage的区别
cookie:关闭浏览器就失效了,通过浏览器记录信息确认身份,最大4KB,请求性能受到影响
sessionStorage:自己设置存储的时间,过了时间才失效,本地存储一个会话数据,不是持久化得本地存储
localStorage: 持久化的本地存储,除非主动删除,否则永久不会过期
什么是闭包(closure),为什么要用它
概念:函数A里面有一个函数B,函数B可以访问函数A中的变量,那么函数B就是闭包,闭包就是能够读取其他函数内部变量的函数,在本质上,闭包是将函数内部与函数外部连接起来的桥梁
优点1.延长变量的生命周期=》一个不会被销毁的函数执行空间(始终占用内存) 2.可以在函数外部通过闭包函数访问函数内部的变量=》一个不会被销毁的函数执行空间
缺点1.内存有限2,浪费空间
应用场景定时器,回调,函数防抖
dom节点操作
appendChild()添加节点 removeChild()删除节点 insertBefore插入节点 replaceChild替换节点
跨域
浏览器出于安全考虑,有同源策略,协议域名或者端口有一个不同就是跨域,ajax就会请求失败
解决跨域机制:1.只限于get请求,利用src不受同源策略的影响==利用script标签把所有内容当做js代码来执行==就绕开了浏览器同源策略的影响使用jsonp 2.使用vue的proxy反向代理 3.使用nginx设置反向代理 4.后端设置请求头:header('Access-Control-Allow-Origin:*');//允许所有来源访问
header('Access-Control-Allow-Method:POST,GET');//允许访问的方式
深拷贝浅拷贝
假设B复制了A,如果B也变化了,说明是浅拷贝,b没变就是深拷贝 浅拷贝仅复制第一层属性且共享嵌套对象的内存地址,深拷贝递归创建完全独立的内存
深拷贝:
JSON.parse(JSON.string)
$.extend(true,[ ],array); // true为深拷贝,false为浅拷贝
lodash插件cloneDeep
浅拷贝:
object.assign
...
slice.concat实现对数组的深拷贝(当数组里面得值是基本数据类型属于深拷贝,string,number,boolean)
(数组中的值为引用类型时属于浅拷贝如object,array)
数据类型
js基本数据类型:string,number,null,boolean,undefined,es6中的symbol 复杂数据类型:object 检查数据类型typeOf,instanceOf,object.prototype.toString.call
数组去重
new set() filter+indexOf
Es6新特性
1.声明变量let和const
2.解构赋值
3.字符串新增了模版字符串includes(),repeat(),trimstart()
4.数组新增了find(),flat().map()
5.对象新增了object.assign(),for of(),
6.新增了箭头函数
7.新增了数据类型symbol
8.新增了set和三点运算符
9.新增了class类
Vue3 简介
使用Proxy代替defineProperty实现响应式。
重写虚拟DOM的实现和Tree-Shaking。