双向数据绑定的方式不同
vue2是采用ES5的object.definePropert()对数据进行劫持,结合发布订阅和观察者模式进行的
vue3是采用ES6的Proxy的数据代理来对数据进行代理,修复了v2中对象和数组的属性添加修改的问题
根节点数量不同
vue2中根节点只能有一个
vue3中可以有多个根节点,解决了多个div嵌套的问题
vue3中增加了Composition API ,在script标签上添加setup 或者调用setup()函数
vue2中只有optiosAPI
生命周期钩子少许变化
vue2中的destoryed钩子在vue3中为unmounted
组件传值不太一样
vue3中在子组件中使用emits拦截事件,props拦截属性;
vue2中使用 e m i t ( ) 派发事件,父组件使用 emit()派发事件,父组件使用 emit()派发事件,父组件使用on监听,
定义全局变量的方法不一样
vue2中使用Vue.prototype.$http = axios
vue3中使用app.config.globalProperties.$http = axios
创建vue实例的方式不一样
vue2中3使用new Vue()的方式,来创建实例
vue3中使用createApp()方法来创建实例
插槽的写法不一样
vue2中父组件中使用 slot-scope=“”
vue3中使用v-slot=“” 或者#代替
vue3中新增了teleport穿梭组件,实现组件的穿梭;比如dialog遮罩层可以直接挂到body上
vue3中移除了filter过滤器
官方建议直接使用计算属性或者方法调用来实现过滤
vue3中移除了$childern属性
不可以使用 c h i l d e r n 来获取子组件,而建议使用 r e f 和 t h i s . childern来获取子组件,而建议使用ref和this. childern来获取子组件,而建议使用ref和this.refs来获取子组件上的属性和方法
v-if和v-for优先级
在v2中如果同时使用v-for 和 v-if 那么v-for的优先级是高于 v-if 的
v3中v-if 始终高于 v-for但是还是不建议一起使用
vue3中移除了 .native 修饰符
vue中,vue的生命周期是指,从创建vue对象到销毁vue对象的过程
此处是准备在销毁之前调用的钩子,数据props,data,methods都可以访问,但是DOM已经被移除了
vue2中的响应式是通过object.defineProperty遍历每一个属性(对于深层嵌套的对象则会进行递归处理)并为其添加上getter和setter方法用来来监听数据的读取与改变,也就是我们所说的数据劫持,然后结合观察者模式,也就是==发布者-订阅者模式(Dep-Watcher)==通知页面视图发生改变,从而实现数据的响应式原理,数据劫持
数组的部分操作没有响应式 数组的部分操作没有响应式
Vue3采用了es6的proxy结合reflect对数据拦截进行响应式处理。
组合式 API 是 Vue.js 3.x 中新增的一种编写组件逻辑的方式,它将一个组件划分为多个功能性逻辑块,在不同的函数中进行管理和维护。具体来说,Vue.js 3.x 中的组合式 API 包括以下几个部分:
相比之下,Vue.js 2.x 使用的是 Options API,这种 API 将一个组件的相关配置都集中放在一个对象中。Options API 缺少了对代码逻辑的拆分和重用,导致在模块化开发时难以复用一些组件内部的代码,而且特别是对于大型组件,Options API 在维护和扩展时显得非常麻烦。Vue.js 3.x 的组合式 API 弥补了这些缺点,并使代码结构更加清晰,更容易阅读和维护。
Vue.js 3.x 中自定义指令的声明方式相对于2.x 也有了一些变化。在3.x中,通过调用 app.directive()
函数可以注册全局或局部的自定义指令,并且自定义指令的生命周期函数和参数都进行了更新和优化。
具体来说, 3.x 版本中自定义指令的生命周期函数如下:
beforeMount
- 指令绑定到元素上但未插入父元素前的回调函数。mounted
- 指令插入父元素时的回调函数。beforeUpdate
- 组件更新之前的回调函数。updated
- 组件更新完毕的回调函数。beforeUnmount
- 组件卸载前的回调函数。unmounted
- 组件卸载后的回调函数。此外,还新增了两个新的生命周期函数:
beforeBind
- 指令第一次绑定到元素时的回调函数。updated
- 指令与元素解绑时的回调函数。这里需要注意的是,3.x版本已经没有了 bind
和 unbind
这两个钩子函数,取而代之的是beforeMount
和 beforeUnmount
,它们在处理指令绑定和解绑逻辑上更为直观和简单。同时,在自定义指令的参数传递上,3.x版本中的指令采用了 ES6 的解构方式进行参数传递。
综上所述, Vue.js 3.x 中自定义指令的生命周期函数和参数都得到了优化和更新,并且钩子函数的名称和处理逻辑也更为直观,开发者在使用过程中也需根据具体情况进行调整。
Vue.js 的自定义指令可以通过 binding
对象获取当前绑定到元素的信息,也可以监听特定事件并进行相应操作。它提供了以下几个钩子函数:
另外,还可以使用 modifiers
修饰符来影响指令的行为。例如,在 v-on 指令中,可以使用 .prevent
修饰符阻止事件的默认行为。
我曾经使用自定义指令创建一个可以实现图像懒加载的指令。该指令中,我用到了 window
对象的 scroll 事件和 IntersectionObserver
API。当用户滚动页面时,指令会检测所观察的元素是否进入可视区域,如果没有,则不会将其图像加载。这样可以显著提高网页打开速度,节省带宽。
`` 是 Vue.js 提供的一个用于缓存组件实例的高阶组件。它可以在组件切换时将销毁的组件实例缓存起来,以便下次访问该组件时可以直接使用缓存的组件实例,避免重复渲染和重新执行生命周期函数。
具体来说,当一个被缓存起来的组件被连续访问两次或以上时,第二次及以后的访问都不会再触发该组件的 `created`、`mounted` 等生命周期钩子,而是直接从缓存中读取旧的组件实例并显示出来。同时,Vue.js 还为
注入了 keepAlive
和 include
属性,分别用于控制哪些组件应该被缓存以及如何选择要缓存的组件。
需要注意的是,在 `` 内的动态组件(即通过 v-bind:is
进行多个组件之间的动态切换)切换时,如果组件实例不支持 activated
和 deactivated
钩子函数,则其状态不会被保存,每次切换时仍然会调用其 created
和 destroyed
钩子函数。为了保证这类组件能够正确地被缓存和激活,需要在组件内部自定义 activated
和 deactivated
钩子函数来处理相应状态。
props
选项来声明自己需要哪些数据。注意 props 数据是单向流动的,即仅能从上级组件传下到下级组件,并且不能在子组件中直接修改。$emit
方法触发自定义事件,并通过这个自定义事件来向父组件传递数据。监听事件可以使用 v-on
或者 @
符号绑定方法。$emit/$on
API:可以使用 $emit/$on
API 进行跨级组件通信。比如,当多个孙子组件分别存在一些相同的机制时,无法使用 props
和 events
对每个孙子组件重复操作,可以考虑利用 $emit/$on
在父亲组件中追踪这些孙子组件更新状态。provide
提供一组数据给子孙组件,然后该子孙组件可通过 inject
注入组件实例里获取对应数据。但要注意,在 props 和 provide/inject 两种方式同时使用时,可能会导致不必要的耦合,请谨慎使用。在 hash 模式下,URL 中会带有一个 # 号。例如:http://example.com/#/home
。# 后面的部分被称为 hash
值。hash 值发生变化时,页面不会重新加载,而是通过监听window.onhashchange
事件来对路由变化做出相应。具体实现如下:
div
标签作为容器。history 模式利用了 H5 API 接口,使用 pushState、replaceState 和 popstate 这些 api 来操作浏览器历史记录,并且没有 # 号。在 history 模式下,通过监听window.popstate
事件来处理路由更改。具体实现如下:
div
标签作为容器。Hash 模式和 History 模式在路由实现中有不同的优缺点。Hash 模式兼容性更好,在老版本浏览器上也可以实现,但是 URL 中需要带有 # 号,不太美观。而 History 模式URL 更加干净,但是存在兼容性问题。
我可能更倾向于使用Vue 3,而不是Vue 2,以下是一些原因:
更先进的技术和功能: Vue 3引入了许多新的特性和改进,包括更高效的虚拟DOM算法(Fragment + Text),Composition API、Teleport、Suspense等新的API,以及更好的TypeScript支持。这些新功能可以提升开发效率、代码可读性和可维护性。
更好的性能表现: Vue 3对内部实现进行了优化,提供了更好的性能表现。其响应式系统使用了Proxy代理对象,相较于Vue 2的Object.defineProperty,Proxy具有更高的性能和更丰富的拦截器。另外,Vue 3还引入了静态提升和模块热替换(HMR)等优化,进一步提升了应用程序的性能。
更好的TypeScript支持: Vue 3对TypeScript的支持更加完善。使用Vue 3可以更轻松地结合TypeScript进行开发,享受类型检查、智能感知和自动补全等开发工具的好处,减少潜在的bug和错误。
更灵活的组合式API: Vue 3的Composition API是一种更灵活、可组合和可复用的方式来组织组件逻辑。相较于Vue 2的Options API,Composition API允许将相关的逻辑组织在一起,提高代码的可维护性和可测试性。
虽然Vue 2在许多项目中仍然被广泛使用且稳定可靠,但Vue 3作为最新的版本,带来了许多改进和新特性,可以提供更好的开发体验和性能表现。当然,选择使用Vue 2还是Vue 3取决于具体的项目需求、团队技术栈和迁移成本等因素,需要综合考虑权衡利弊。
Vue 3的createApp
和setup
函数是Vue 3中用于创建应用程序实例和组件的重要部分。下面简单介绍Vue 3中createApp
和setup
函数的底层原理:
createApp的底层原理: createApp
函数用于创建Vue应用程序实例。它的底层原理包括以下几个步骤:
setup的底层原理: setup
函数是Vue 3中组件选项的一部分,用于初始化组件的状态和行为。它的底层原理包括以下几个关键点:
setup
函数,即在组件的beforeCreate
钩子之前执行。setup
函数接收两个参数:props
和context
。props
参数用于接收组件的属性值,可以是响应式的。context
参数是一个上下文对象,提供了访问组件实例、全局属性、插槽等的方法。setup
函数可以返回一个对象,该对象中的属性和方法将会成为组件实例的响应式属性和方法。setup
函数内部,可以使用ref
、reactive
等API创建响应式数据,使用watch
等API处理副作用。总体来说,createApp
和setup
函数是Vue 3中的核心概念,通过它们可以创建和配置应用程序实例,初始化组件状态,并与Vue的响应式系统进行交互。setup
函数的设计使得组件的逻辑更加清晰、可复用,并且提供了更好的类型推断和静态分析。
插件和组件在Vue中有不同的作用和使用方式,下面是它们的区别:
功能和作用:
使用方式:
Vue.use()
方法进行安装,然后才能在整个应用程序中使用。安装插件后,插件中定义的全局功能和方法将可用于整个应用程序,而不需要在每个组件中单独引入和配置。作用域和封装:
综上所述,组件用于封装可复用的UI元素和功能模块,具有自己的作用域和封装性,而插件是一种全局扩展机制,可以向Vue应用程序添加全局功能和库。组件和插件在Vue开发中起着不同的作用,可以根据需求选择合适的方式来进行开发和扩展。
在Vue 3中,使用插件的底层原理主要是通过调用插件对象的install
方法来注册和安装插件。下面是使用插件的底层原理的简要步骤:
定义插件对象: 插件对象是一个JavaScript对象,通常包含一个名为install
的方法。这个方法将在插件被安装时被调用。
调用Vue.use()
方法安装插件: 在Vue应用程序的入口文件或需要使用插件的地方,调用Vue.use()
方法安装插件。该方法会自动调用插件对象的install
方法,并传递Vue应用程序实例作为第一个参数。
插件的install
方法: 插件对象的install
方法会被调用,并接收Vue应用程序实例作为第一个参数。在install
方法内部,可以进行一些全局配置、注册全局组件、指令、混入等。
注册全局功能: 在install
方法中,可以通过app.component()
、app.directive()
等方法来注册全局组件、指令等。这样一来,插件提供的功能就可以在整个应用程序中使用了。
需要注意的是,Vue 3中使用插件的方式略有变化。相较于Vue 2,Vue 3的插件不再支持通过在插件对象上定义install
方法来进行安装。而是直接将插件对象作为参数传递给Vue.use()
方法,Vue 3会自动调用插件对象的install
方法。
使用插件的底层原理简要概括如上所述。通过调用插件对象的install
方法,并将Vue应用程序实例作为参数传递,可以在Vue应用程序中注册全局功能,实现插件的安装和使用。
在Vue 3中,插槽(slots)是一种用于在组件之间传递内容的机制。它允许父组件向子组件传递任意的HTML、组件或其他内容,并在子组件中进行渲染和使用。插槽提供了一种灵活的方式来定制组件的外观和行为。
插槽的理解可以从以下几个方面来阐述:
父组件提供内容: 插槽的使用始于父组件。父组件可以通过组件标签的内部内容来提供需要传递给子组件的内容。这些内容可以是HTML代码、文本、组件等。
子组件接收内容: 子组件通过在其模板中定义插槽来接收父组件传递的内容。插槽使用特殊的语法来定义,通常使用
标签表示。
插槽内容的渲染: 当父组件在使用子组件时,父组件提供的内容会被渲染到子组件的插槽位置上。这意味着子组件可以通过插槽将父组件的内容嵌入到自己的模板中,实现自定义的组合和布局。
默认插槽内容: 子组件可以为插槽提供默认的内容,在没有父组件提供内容时,将使用默认内容进行渲染。默认内容通常在插槽标签内部提供,例如
。
具名插槽: 当父组件需要传递多个插槽时,可以使用具名插槽来标识每个插槽的名称。在子组件中使用
标签时,可以通过name
属性来指定要渲染的具名插槽。
通过使用插槽,可以实现更灵活的组件封装和复用。父组件可以根据自己的需求定制子组件的显示内容,而子组件不需要关心具体内容是什么,只需在适当的位置使用插槽来展示父组件提供的内容。
在Vue 3中,插槽的使用语法和Vue 2有一些不同,主要是通过
标签和v-slot
指令来实现。具体的用法可以参考Vue 3的官方文档中关于插槽的部分。
在Vue 3中,shallowReactive
和shallowRef
是用于创建“浅”响应式对象和ref
对象的API。它们之间的区别主要体现在以下几个方面:
shallowReactive:
shallowReactive
函数接收一个普通的JavaScript对象,并返回一个“浅”响应式代理对象。这意味着只有该对象的第一层属性会被转换为响应式,而嵌套对象的属性不会被转换为响应式。
const shallowReactiveObject = shallowReactive(sourceObject);
shallowReactive
创建的对象是一个响应式代理对象,对于第一层属性的访问和修改会被追踪和触发响应,但对于嵌套对象的属性修改则不会触发响应。
shallowRef:
shallowRef
函数接收一个值,并返回一个“浅”ref
对象,该对象可以被读取和修改。
const shallowRefObject = shallowRef(value);
与常规的ref
对象不同,shallowRef
创建的对象不会将内部的嵌套对象转换为响应式。如果嵌套对象发生变化,不会触发深层的响应,而只有对浅层的属性修改才会触发响应。
区别总结:
转换范围:
shallowReactive
仅将对象的第一层属性转换为响应式,嵌套对象的属性不会被转换为响应式。shallowRef
对于嵌套对象不做响应式转换,只有浅层的属性会被转换为响应式。数据类型:
shallowReactive
适用于将普通的JavaScript对象转换为响应式代理对象。shallowRef
适用于将一个值转换为ref
对象,可以读取和修改。响应触发:
shallowReactive
创建的对象对于第一层属性的修改会触发响应,但对于嵌套对象的属性修改不会触发响应。shallowRef
创建的对象只会对浅层的属性修改触发响应,对于嵌套对象的修改不会触发响应。总体来说,shallowReactive
和shallowRef
都提供了一种“浅”响应式的方式,适用于特定的使用场景,可以减少不必要的响应追踪和性能开销。选择使用哪个函数取决于需要转换为响应式的对象或值的层次结构以及对响应触发的要求。
在Vue 3中,toRef
和toRefs
是用于将响应式数据转换为ref
对象的API,它们之间有一些差异。
toRef:
toRef
函数接收一个响应式对象和一个属性键,返回一个ref
对象,该ref
对象会跟踪原始对象的指定属性的变化。
const refObject = toRef(sourceObject, 'propertyName');
使用toRef
创建的ref
对象是只读的,任何对该对象的修改都不会反映到原始对象上,但可以通过修改原始对象的属性来影响到ref
对象。这意味着,toRef
创建的ref
对象是单向绑定的,只能读取属性值,不能直接修改属性值。
toRefs:
toRefs
函数接收一个响应式对象,将其所有的属性转换为ref
对象,并返回一个包含所有转换后的ref
对象的普通对象。
const refsObject = toRefs(sourceObject);
使用toRefs
创建的对象是一个普通的JavaScript对象,其中的每个属性都是一个ref
对象。与toRef
不同,toRefs
创建的ref
对象是双向绑定的,可以通过修改ref
对象来影响到原始对象的属性值。
这意味着,通过toRefs
创建的对象中的每个属性可以读取和修改,对ref
对象的修改会反映到原始对象上。
主要差异:
返回类型:
toRef
返回一个单独的ref
对象,用于跟踪原始对象中的一个属性。toRefs
返回一个包含多个ref
对象的普通对象,每个ref
对象都对应原始对象的一个属性。单向绑定 vs 双向绑定:
toRef
创建的ref
对象是只读的,只能读取属性值,不能直接修改属性值。toRefs
创建的ref
对象是双向绑定的,可以读取和修改属性值。使用方式:
toRef
适用于需要将单个属性转换为ref
对象的场景,便于单向绑定和数据访问。toRefs
适用于需要将整个响应式对象的所有属性转换为ref
对象的场景,便于双向绑定和对属性的读取和修改。总的来说,toRef
和toRefs
是用于将响应式数据转换为ref
对象的API,主要区别在于返回类型和绑定方式。选择使用哪个函数取决于具体的使用场景和需求。
在Vue 3中,ref
和reactive
都是用于管理响应式状态的新API,它们之间有一些异同点。
异同点:
创建方式:
ref
:通过ref
函数创建一个简单的响应式数据。可以将普通的JavaScript值传递给ref
函数,它会返回一个包装后的响应式引用。reactive
:通过reactive
函数创建一个包含多个属性的响应式对象。可以将一个普通的JavaScript对象传递给reactive
函数,它会返回一个响应式代理对象。使用方式:
ref
:ref
创建的响应式数据需要通过.value
访问和修改其值。例如,myRef.value = 10
来设置值,console.log(myRef.value)
来获取值。reactive
:reactive
创建的响应式对象可以像普通对象一样直接访问和修改其属性。例如,myReactiveObj.prop = 'value'
来设置属性值,console.log(myReactiveObj.prop)
来获取属性值。自动解包:
ref
:ref
创建的响应式数据在模板中使用时会自动解包,无需手动访问.value
。例如,{{ myRef }}
会自动显示myRef.value
的值。reactive
:reactive
创建的响应式对象在模板中使用时需要手动访问属性。例如,{{ myReactiveObj.prop }}
来显示myReactiveObj
的属性prop
的值。不同点:
粒度:
ref
:ref
用于创建单个的响应式数据,可以用于任何类型的值,包括基本类型、对象和函数等。reactive
:reactive
用于创建包含多个属性的响应式对象,适用于需要管理多个相关属性的情况。依赖追踪:
ref
:ref
创建的响应式数据会进行精确的依赖追踪。只有当引用的值发生变化时,相关的响应式更新才会触发。reactive
:reactive
创建的响应式对象会进行递归的依赖追踪。只要对象的任何属性发生变化,相关的响应式更新都会触发。编程风格:
ref
:ref
适用于需要直接操作值的简单场景,更接近于传统的变量操作。reactive
:reactive
适用于需要管理多个属性的复杂场景,更接近于传统的面向对象编程。总的来说,ref
和reactive
都可以用于管理响应式状态,但在使用方式
在Vue 3中,watch
和watchEffect
是用于监视数据变化的两个函数。它们在功能上有一些区别。
watch
函数: watch
函数用于监视特定的数据源,并在其发生变化时执行回调函数。它接收两个参数:要监视的数据源和回调函数。当监视的数据源发生变化时,回调函数将被触发。此外,watch
函数还提供了一些配置选项,例如immediate
、deep
和flush
等,以便更精细地控制监视过程。
watch(data, (newValue, oldValue) => {
// 在数据变化时执行回调函数
});
watchEffect
函数: watchEffect
函数是一个立即执行的监视器,它会自动追踪在其回调函数内部使用的所有响应式数据,并在任何被使用的数据发生变化时重新运行回调函数。它不需要显式指定要监视的数据源,而是基于其内部使用的响应式数据来自动触发。
watchEffect(() => {
// 在回调函数中使用的响应式数据发生变化时重新运行
});
在使用上的主要区别是:
watch
需要显式指定要监视的数据源,并在回调函数中处理数据变化的逻辑,适用于需要对具体数据进行复杂处理或执行异步操作的情况。
watchEffect
不需要显式指定监视的数据源,而是自动追踪其回调函数中使用的所有响应式数据,并在这些数据变化时重新运行回调函数,适用于更简单的场景或需要自动追踪依赖的情况。
总的来说,watch
提供了更精细的控制和更多的配置选项,而watchEffect
则更加自动化和简洁,根据具体的需求选择适合的函数。
Vue 3的Composition API是一种新的组件API,相比Vue 2的Options API,它具有以下优势、好处和特点:
更灵活的组合方式: Composition API允许将组件逻辑按照功能关注点进行组合,而不是按照生命周期钩子分割。这使得代码更具可读性和可维护性,更容易重用和测试。
更好的代码组织: Composition API通过将相关逻辑放在一个独立的函数中,使得组件的代码结构更清晰和模块化。每个功能都可以被封装在一个单独的函数中,便于理解和管理。
更好的类型推导和IDE支持: Composition API利用了TypeScript的类型推导能力,提供了更好的类型检查和IDE支持。在编写组合函数时,IDE可以根据上下文提供更准确的代码提示和自动补全。
更好的响应式能力: Composition API引入了ref
和reactive
等新的响应式API,使得响应式数据的使用更加直观和灵活。通过ref
和reactive
,可以轻松地创建和管理响应式数据,而不需要像Vue 2中那样依赖于data
选项。
更好的逻辑复用: Composition API鼓励将可复用的逻辑提取为自定义的组合函数,并在多个组件之间进行共享。这样可以提高代码的复用性,减少重复的代码,同时还能更方便地对逻辑进行单元测试。
更好的性能优化: Composition API引入了watchEffect
和computed
等新的API,使得性能优化更加直观和灵活。watchEffect
可以自动追踪依赖,并在数据变化时进行响应,而computed
可以创建基于响应式数据的计算属性,以避免不必要的重复计算。
总体而言,Composition API提供了更强大、更灵活和更易用的方式来编写Vue组件。它通过更好的代码组织、类型推导、响应式能力和逻辑复用等方面的优势,使得开发人员可以更高效地构建复杂的应用程序。
Vuex是一个专为Vue.js设计的状态管理库,它借鉴了Flux、Redux等框架的设计思想,提供了一种集中式管理应用状态的方案。Vuex的实现原理是基于Vue.js的响应式系统和发布/订阅模式实现的。
Vuex中包含了五个模块:state、mutations、actions、getters和modules。其中,state是存储应用状态的地方;mutations是修改应用状态的地方;actions是处理异步操作的地方;getters是计算状态的地方;modules是将状态进行模块化管理的地方。
Vuex中提供了两个方法:commit和dispatch。其中,commit用于提交mutation,而mutation是唯一修改状态的方式;dispatch用于触发action,而action则可以进行异步操作,最终通过提交mutation来修改状态。
commit方法的实现原理比较简单,它只是简单地调用mutation的方法来修改状态。
dispatch方法的实现原理则比较复杂,它需要先处理异步操作,然后再通过commit方法提交mutation来修改状态。在处理异步操作时,dispatch方法会根据action的类型分别执行不同的操作,可以是异步请求、定时器等等。当异步操作完成后,它会再次调用commit方法来提交mutation,从而修改状态。
总之,Vuex通过响应式系统和发布/订阅模式,实现了集中式管理应用状态的方案,提供了commit和dispatch方法来实现状态的修改和异步操作,从而简化了应用状态管理的过程。
Vue-router是Vue.js官方提供的路由管理库,可以在Vue.js应用程序中实现客户端路由。
Vue-router的实现原理可以简单概括为以下几个步骤:
定义路由规则:在Vue.js应用程序中定义路由规则,即指定哪些URL路径应该由哪些组件渲染。
创建路由实例:使用VueRouter类创建一个路由实例,将路由规则作为参数传递给该实例。
将路由实例挂载到Vue实例上:在Vue.js应用程序中将路由实例挂载到Vue实例上,使得所有的组件都可以访问路由实例。
监听URL变化:当URL发生变化时,路由实例会根据定义的路由规则匹配相应的组件,并将组件渲染到应用程序的页面上。
实现导航:Vue-router提供了一些API,可以通过编程方式实现导航,例如:push()、replace()、go()、back()等方法。
在实现路由的过程中,Vue-router利用了Vue.js的组件系统,将路由组件定义为Vue组件,实现了路由和组件的无缝衔接。同时,Vue-router还利用了浏览器的History API实现了前端路由的实现。
总的来说,Vue-router的实现原理是通过监听URL的变化,根据定义的路由规则匹配相应的组件,并将组件渲染到应用程序的页面上,实现了前端路由的实现。
设计目标:
不以解决实际业务痛点的更新都是耍流氓,下面我们来列举一下Vue3
之前我们或许会面临的问题
bundle
的时间太久了优化:
封装组件使其可以在不同的项目之间使用的一种常见的方式是通过将组件打包为可复用的npm包。具体步骤如下:
创建一个独立的npm包,可以使用工具如npm init来初始化包的基本信息,或者使用脚手架工具如create-react-library或vue-cli-plugin-library来创建基础的npm包结构。
将要封装的组件放在npm包的src目录下,并编写组件的代码。
使用工具如rollup或webpack将组件代码打包成umd、cjs、esm等不同格式的包,并输出到npm包的dist目录下。同时在package.json文件中定义main、module、browser等字段,指定输出的不同包格式和入口文件。
在组件代码中引用依赖的第三方库,使用peerDependencies字段将第三方库的版本号声明到npm包中,表示组件需要依赖这些第三方库。
发布npm包到npm仓库,可以使用npm publish命令将包发布到公共仓库,或使用私有仓库来管理npm包。
在不同的项目中使用封装的组件时,可以通过npm install安装该组件包,然后在项目中引入组件。对于React项目,可以使用import语句导入组件,并在JSX中使用;对于Vue项目,可以使用Vue.use()方法全局注册组件,或在单个组件中通过import语句导入并使用。需要注意的是,引用组件时需要根据组件包中定义的不同包格式,选择合适的导入方式。
nextTick
中的回调是在下次 DOM
更新循环结束之后执行的延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM
。主要思路就是采用微任务优先的方式调用异步方法去执行 nextTick
包装的方法
1.计算属性是有缓存的,只有当它依赖的数据发生变化的时候才重新渲染,函数只要调用就会去执行
2.计算属性本质上是一个属性,必须要返回一个结果,函数就是一个函数,可以没有返回值
3.计算属性只能获取值,但是不能设置值
1、computed擅长处理的场景: 一个数据受多个数据影响; watch擅长处理的场景: 一个数据影响多个数据.
2、功能上: computed是计算属性,watch是监听一个值的变化,然后执行对应的回调。
3、是否调用缓存:computed支持缓存,只有依赖数据发生改变,才会重新进行计算;而watch不支持缓存,数据变,直接会触发相应的操作。
4、是否调用return: computed中的函数必须要用return返回,watch中的函数不是必须要用retumn.
5、computed不支持异步,当computed内有异步操作时无效,无法监听数据的变化;而watch支持异步。
6、computed默认第一次载的时候就开始监听,watch默认第一次加载不做监听,如果需要第一次加载做监听,添加immediate属性,设置为true (immediate:true)
Pinia和Vuex都是Vue.js状态管理库,但它们在一些方面有所不同。
Pinia的优点:
Pinia的缺点:
Vuex的优点
Vuex的缺点
区别:
pinia一个轻量级的状态管理库,它专注于提供一个简单的API来管理应用程序的状态。相比之下,Vuex是一个更完整的状态管理库,它提供了更多的功能,比如模块化、插件和严格模式等。
pinia它没有mutation,他只有state,getters,action【同步、异步】使用他来修改state数据
pinia他默认也是存入内存中,如果需要使用本地存储,在配置上比vuex麻烦一点
pinia语法上比vuex更容易理解和使用,灵活。
pinia没有modules配置,没一个独立的仓库都是definStore生成出来的
pinia支持初始化工具,vuex不支持
pinia state是一个对象返回一个对象和组件的data是一样的语法
在 Vue 2.x
中,watch
是一个非常重要的 API
。它可以用来观察和响应 Vue
实例对象上的特定数据变化。如果数据变化,那么 watch
就会执行一个回调函数,这个回调函数可以用来更新视图或者执行其他的操作。
API Mounting API
中的 watch
来进行监听。ref
对象或者 reactive
对象作为参数。它们都可以用来观察特定的数据变化,并执行相应的逻辑。另外,它们的用法也是类似的,都可以接受一个回调函数或者一个监听对象。
Vue 3
中的 watch
使用起来更加简单易懂。Vue
2 中的 watch API
需要传入两个参数,一个是要观察的数据,另一个是回调函数。而在 Vue 3
中,只需要传入要观察的数据即可,回调函数可以在watch
函数内部定义。这样的话,代码更加简洁,易于维护和测试。Vue 3
中的 watch
具有更多的功能。Vue 3
中的 watch
可以接收一个配置对象,并支持多个监听器。除了可以监听简单的数据变化之外,Vue 3
中的 watch
还可以监听 props、data、computed
等 Vue
实例对象的属性变化,这使得 watch
功能更加强大、灵活。Vue 3
中的 watch
在性能方面也有所提升。Vue 2
中的 watch
会在每次数据变化时都执行回调函数,这样会导致不必要的计算和渲染,从而影响页面性能。而在 Vue 3
中,watch
使用了 Proxy
对象,可以精确地跟踪修改过的数据,避免不必要的计算和渲染,提高了页面的性能。在vue中我们可以使用keep-alive组件来实现组件缓存,当我们在路由表中定义了是否缓存的标识,就可以实现想要缓存的组件了。
为了实现针对性的缓存,我们需要使用到组件自带的includes属性,则个属性标识的是在这个数组中的组件讲会被缓存,有了这个属性我们再配合vuex和路由守卫,vuex来存储需要缓存的组件的名称,而路由守卫用于判断是具体从哪个页面进来的是否进行缓存等。
Vue 实现权限管理一般需要从前端和后端两个方面考虑,前端主要是 UI 的控制和请求的发送,后端主要是数据的处理和权限校验。
在前端中,Vue 可以通过路由守卫来进行权限控制,比如在 beforeEach 钩子中判断当前用户是否有权限访问该路由,如果没有则跳转到指定的错误页面。另外,也可以使用自定义指令来控制按钮的显示和隐藏,根据用户的权限动态地添加或移除对应的指令。
在后端中,需要对用户的身份进行认证和鉴权,可以通过 session、token 或者 OAuth 等方式实现。一般情况下,用户的权限信息会存储在数据库中,需要在服务器端进行查询和校验。对于按钮级别的权限控制,可以在后端通过权限拦截器对请求进行拦截,根据用户的权限信息来判断是否允许进行操作。
需要注意的是,前端的权限控制只是一种辅助手段,真正的权限控制应该在后端进行,因为前端的代码可以被修改和篡改,容易被绕过。
vue官网上对此ssr的定义:Vue.js 是构建客户端应用程序的框架。默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM。然而,也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序。
服务器渲染的 Vue.js 应用程序也可以被认为是"同构"或"通用",因为应用程序的大部分代码都可以在服务器和客户端上运行。
**通俗解释:**vue的组件是基于vnode的,整个html结构用js的vnode对象树来表达,那么服务端可以通过解析js对象树,在服务端提前生成具有实际表达作用的html字符串,在客户端(浏览器中)每次数据变化的时候通过新旧vnode对象树的对比用diff算法(vue diff算法不了解的可以去搜索一下)去寻找更新最小最优的变化集合,然后再去更新实际的dom。
关键点: 代码可以在客户端运行,也可以在服务端运行,服务端渲染通俗来说就是先在服务端运行,在服务端生成html结构并返回给客户端,接下里继续由客户端代码去完成交互。
解决的问题:
seo:搜索引擎的优先爬取级别是页面的html结构,当我们使用ssr的时候,服务端已经生成了与业务相关联的html,这样的信息对于seo是很友好的。
内容呈现:客户端无需等待所有的js文件加载完成即可看见渲染的业务相关视图(压力来到了服务端这边,这也是需要做权衡的地方,需要区分哪些由服务端渲染,哪些可以交给客户端)。
相关的弊端:
代码兼容:对于开发人员来讲,需要去兼容代码在不同环境的运行,vue ssr所需要的服务端环境是node,有一些客户端的对象,比如dom,windows之类的则无法使用。
服务器负载:相对于前后端分离模式下服务器只需要提供静态资源来说,ssr需要的服务器负载更大,所以在项目中使用ssr模式要慎重,比如一整套图表页面,相对于服务端渲染,可能用户不会在乎初始加载的前几秒,可以交由客户端使用类似于骨架屏,或者懒加载之类的提升用户体验。
对于普通数据类型一般来说可使用defineproperty,但是对于复杂的数据类型比如说,数组,或者对象这类的引用数据类型来说,就要使用proxy,还有就是使用defineproperty来说无法检测数据的改变,如果想要修改数据的话还需要用到原生的$set等方法,修改完后页面的数据才会发生改变,但对于proxy来说不会发生这样的情况
Vuex就是要实现全局的组件实现组件之间的一个共享状态,一个组件更新之后,其他组件也能接受到一个更新的值,先下载vuex,在main,js中全局导入app.use,场景:就是发现有很多组件都用到某个属性或者方法,就可以存入到vuex中,store就是用来存储的,action就相当于执行的方法,这个里面写的是所要执行的函数,在通过dispatch的形式是各个组件都能拿到
采用ES6
的import ... from ...
语法或CommonJS
的require()
方法引入插件
使用全局方法Vue.use( plugin )
使用插件,可以传入一个选项对象Vue.use(MyPlugin, { someOption: true })
created
: 实例已经创建完成之后调用,在这一步,实例已经完成数据观测, 属性和方法的运算, watch/event
事件回调. 然而, 挂载阶段还没有开始, $el属性目前还不可见mounted
: el
被新创建的 vm.$el
替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted
被调用时 vm.$el
也在文档内。activated: keep-alive
组件激活时调用V-if是通过DOM结点的添加或者删除来控制结点显示或者隐藏,
v-show是通过css样式来控制节点的显示或者隐藏,
v-if的切换频率较低的场景,不展示的DOM元素直接被删除,
v-show的切换频率较高的场景,不展示的DOM元素未被移除,仅仅是使用样式隐藏掉。
使用v-if时,元素可能无法获取到,但v-show一定能获取到
l、$router是VueRouter的实例方法,可以认为是全局的路由对象,包含了所有路由的对象和属性
2、$route是一个跳转的路由对象,可以认为是当前组件的路由管理,指当前激活的路由对象,包含当前url解析得到的数据,可以从对象里获取一些数据。如name, path等
MVVM 是 Model-View-ViewModel 的缩写
在MVVM架构下,View 和 Model 之间并没有直接的联系
而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的,
因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,
而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,
因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,
复杂的数据状态维护完全由 MVVM 来统一管理。
双向绑定的原理:
vue 双向数据绑定是通过 数据劫持 结合 发布订阅模式的方式来实现的, 也就是说数据和视图同步,数据发生变化,视图跟着变化,视图变化,数据也随之发生改变,
vue双向数据绑定,其核心是 Object.defineProperty()方法,给Vue中的数据绑定get和set方法,当获取数据的时候,调用get方法,修改data中的数据的时候调用set方法,通过watcher监听器去更新视图,完成数据的双向绑定。
创建阶段:
beforeCreate:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。
用途:通常在这个阶段可以用来进行一些初始的数据操作,比如对 data 中的数据进行修改。
created:实例创建完成后被立即调用。
用途:通常在这个阶段可以用来进行一些数据的异步请求操作,比如请求后端接口获取数据。
挂载阶段:
beforeMount:在挂载开始之前被调用。
用途:通常在这个阶段可以用来进行一些 DOM 操作,比如获取 DOM 元素进行一些操作。
mounted:实例挂载之后被调用。
用途:通常在这个阶段可以用来进行一些 DOM 操作,比如使用 jQuery 对 DOM 元素进行一些操作。
更新阶段:
beforeUpdate:数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。
用途:通常在这个阶段可以用来进行一些数据更新之前的操作,比如获取新的数据并进行一些操作。
updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。
用途:通常在这个阶段可以用来进行一些数据更新之后的操作,比如获取更新后DOM 元素进行一些操作。
销毁阶段:
beforeUnmount:实例销毁之前调用。
用途:通常在这个阶段可以用来进行一些清理操作,比如清除定时器。
unmounted:实例销毁后调用。
用途:通常在这个阶段可以用来进行一些清理操作,比如释放内存。
props传值
props适用于父传子,有两种写法,混合写法和纯vue3写法(语法糖)
attrs传值(父传子)–attrs包含了父作用域里除 class 和 style 除外的非props 属性集合
expose/ref (子传父)–父组件获取子组件的属性或者调用子组件方法
v-model --支持多个数据双向绑定
$emit --在 < template > 中使用的 $emit 方法不能在组件的 < script setup > 部分中使用。组件要触发的事件可以显式地通过 defineEmits() 宏来声明。
provide / inject(跨代传值)
provide: 提供一个值,可以被后代组件注入。
inject: 注入一个由祖先组件或整个应用 (通过 app.provide()) 提供的值。
mitt --Vue3 中没有了 EventBus 跨组件通信,但是现在有了一个替代的方案 mitt.js,原理还是 EventBus
vuex / pinia(跨代传值)
父子通信:在父组件的子组件上使用自定义属性,在子组件中使用props来接受、
子父通信:通过定义自定义事件,在子组件中使用$emit来触发该事件,进行通信、
兄弟通信:使用 e m i t 发送自定义事件,使用 emit发送自定义事件,使用 emit发送自定义事件,使用on来接受自定义的事件、
祖孙通信:使用inject和provide:通过在外部组件中定义procide传递属性,使用inject来接受属性、
全局通信:定义全局的数据使用vuex全局状态管理系统,可以吧store挂载到vue原型上,在vuex的state中定义全局的状态使用this.$store.state.属性访问、