Vuex
和 Redux
都是用于管理状态的状态管理库,它们在不同的前端框架中有着相似的思想,但也有一些关键的区别。
区别:
Vuex
是为 Vue.js 框架设计的状态管理库,直接集成到 Vue.js 应用中。Redux
是一个独立于框架的 JavaScript 库,可以与多个框架一起使用,最为常见的是与 React 配合使用。Vuex
的核心概念包括 state(状态)、getters(获取器)、mutations(变化)和 actions(动作)。Redux
的核心概念包括 store(存储)、reducers(减速器)和 actions(动作)。Vuex
在处理异步操作时,推荐使用 actions。Actions 可以包含异步操作,并通过 mutations 触发状态的变化。Redux
则使用中间件(如 redux-thunk
)来处理异步操作。Actions 可以返回函数,而不仅仅是对象,以支持异步逻辑。共同思想:
Vuex
和 Redux
都遵循单一数据源的思想,整个应用的状态被存储在一个地方,便于追踪和调试。Vuex
中的 mutations)应该是纯函数,接受先前的状态和一个动作,返回新的状态。这确保了可预测性和可测试性。Vuex
是专为 Vue.js 打造的,直接嵌入到 Vue 应用中,而 Redux
更像是独立的超级英雄,可以在不同框架间徜徉自如。它们的核心思想都是单一数据源、不可变性和纯函数。
嘿,在 Vue 项目里,要想让应用变得更快更顺畅,有几个小技巧可以用。首先是用异步组件或者懒加载来加载东西,这样可以让页面更快地展示。还有就是图片要小巧精致,选对格式,也可以懒加载或者用个占位符。哦,还有,大的代码可以切成小块,慢慢加载,也可以把一些静态资源放到 CDN 上,让速度更快哦。如果有些东西会频繁显示隐藏,v-if 和 v-show 可以轻松搞定。最后,如果首屏加载很慢,可以用
现在 我们需要实现这样 一个功能,登录拦截,其实就是 路由拦截,首先在定义路由的时候就需要多添加一个自定义字段requireAuth,用于判断该路由的访问是否需要登录。如果用户已经登录,则顺利进入路由, 否则就进入登录页面。在路由管理页面添加meta字段
{
path:'/manage',
name:'manage',
component:manage,
meta:{requireAuth:true}
}
在入口文件中,添加路由守卫
在vue2.0中: 主要是往data 和method里面添加内容,一个业务逻辑需要什么data和method就往里面添加,而组合API就是 有一个自己的方法,里面有自己专注的data 和method。
首先composition API(组合API) 和 Option API(vue2.0中的data和method)可以共用 composition API(组合API)本质就是把内容添加到Option API中进行使用
1.ref和reactive都是vue3的监听数据的方法,本质是proxy 2.ref 基本类型复杂类型都可以监听(我们一般用ref监听基本类型),reactive只能监听对象(arr,json) 3.ref底层还是reactive
1、props和$emit
父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件
2、 a t t r s 和 attrs和 attrs和listeners
3、中央事件总线 bus
上面两种方式处理的都是父子组件之间的数据传递,而如果两个组件不是父子关系呢?这种情况下可以使用中央事件总线的方式。新建一个Vue事件bus对象,然后通过bus. e m i t 触发事件, b u s . emit触发事件,bus. emit触发事件,bus.on监听触发的事件。
4、provide和inject
父组件中通过provider来提供变量,然后在子组件中通过inject来注入变量。不论子组件有多深,只要调用了inject那么就可以注入provider中的数据。而不是局限于只能从当前父组件的prop属性来获取数据,只要在父组件的生命周期内,子组件都可以调用。
5、v-model
父组件通过v-model传递值给子组件时,会自动传递一个value的prop属性,在子组件中通过this.$emit(‘input’,val)自动修改v-model绑定的值
6、 p a r e n t 和 parent和 parent和children
7、boradcast和dispatch
8、vuex处理组件之间的数据交互 如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。
1、生命周期:
小程序
的钩子函数要简单得多 。 vue
的钩子函数在跳转新页面时,钩子函数都会触发,但是小程序
的钩子函数,页面不同的跳转方式,触发的钩子并不一样。
在页面加载请求数据时,两者钩子的使用有些类似,vue
一般会在created
或者mounted
中请求数据,而在小程序
,会在onLoad
或者onShow
中请求数据。
2、数据绑定:
vue动态绑定一个变量的值为元素的某个属性的时候,会在变量前面加上冒号:
<img :src="imgSrc"/>
小程序 绑定某个变量的值为元素属性时,会用两个大括号括起来,如果不加括号,为被认为是字符串
<image src="{{imgSrc}}"></image>
3、列表循环
4、显示与隐藏元素
vue
中,使用v-if
和v-show
控制元素的显示和隐藏
小程序
中,使用wx-if
和hidden
控制元素的显示和隐藏
5、事件处理
vue
:使用v-on:event
绑定事件,或者使用@event
绑定事件
小程序
中,全用bindtap(bind+event)
,或者catchtap(catch+event)
绑定事件
6、数据的双向绑定
在vue
中,只需要再表单
元素上加上v-model
,然后再绑定data
中对应的一个值,当表单元素内容发生变化时,data
中对应的值也会相应改变 。
当表单内容发生变化时,会触发表单元素上绑定的方法,然后在该方法中,通过this.setData({key:value})
来将表单上的值赋值给data
中的对应值 。
7、绑定事件传参
在vue
中,绑定事件传参挺简单,只需要在触发事件的方法中,把需要传递的数据作为形参传入就可以了
在小程序
中,不能直接在绑定事件的方法中传入参数,需要将参数作为属性值,绑定到元素上的data-
属性上,然后在方法中,通过e.currentTarget.dataset.*
的方式获取
8、父子组件通信
父组件向子组件传递数据,只需要在子组件通过v-bind
传入一个值,在子组件中,通过props
接收,即可完成数据的传递
父组件向子组件通信和vue
类似,但是小程序
没有通过v-bind
,而是直接将值赋值给一个变量 在子组件properties
中,接收传递的值
小程序
的钩子函数要简单得多 。 vue
的钩子函数在跳转新页面时,钩子函数都会触发,但是小程序
的钩子函数,页面不同的跳转方式,触发的钩子并不一样。
在页面加载请求数据时,两者钩子的使用有些类似,vue
一般会在created
或者mounted
中请求数据,而在小程序
,会在onLoad
或者onShow
中请求数据。
2、数据绑定:
vue动态绑定一个变量的值为元素的某个属性的时候,会在变量前面加上冒号:
<img :src="imgSrc"/>
小程序 绑定某个变量的值为元素属性时,会用两个大括号括起来,如果不加括号,为被认为是字符串
<image src="{{imgSrc}}"></image>
3、列表循环
4、显示与隐藏元素
vue
中,使用v-if
和v-show
控制元素的显示和隐藏
小程序
中,使用wx-if
和hidden
控制元素的显示和隐藏
5、事件处理
vue
:使用v-on:event
绑定事件,或者使用@event
绑定事件
小程序
中,全用bindtap(bind+event)
,或者catchtap(catch+event)
绑定事件
6、数据的双向绑定
在vue
中,只需要再表单
元素上加上v-model
,然后再绑定data
中对应的一个值,当表单元素内容发生变化时,data
中对应的值也会相应改变 。
当表单内容发生变化时,会触发表单元素上绑定的方法,然后在该方法中,通过this.setData({key:value})
来将表单上的值赋值给data
中的对应值 。
7、绑定事件传参
在vue
中,绑定事件传参挺简单,只需要在触发事件的方法中,把需要传递的数据作为形参传入就可以了
在小程序
中,不能直接在绑定事件的方法中传入参数,需要将参数作为属性值,绑定到元素上的data-
属性上,然后在方法中,通过e.currentTarget.dataset.*
的方式获取
8、父子组件通信
父组件向子组件传递数据,只需要在子组件通过v-bind
传入一个值,在子组件中,通过props
接收,即可完成数据的传递
父组件向子组件通信和vue
类似,但是小程序
没有通过v-bind
,而是直接将值赋值给一个变量 在子组件properties
中,接收传递的值
1、v-model本质上是一个语法糖,可以看成是value + input 方法的语法糖。可以通过model的prop属性和event事件来进行自定义。
2、v-model是vue的双向绑定的指令,能将页面上控件输入的值同步更新到相关绑定的data属性, 也会在更新data绑定属性时候,更新页面上输入控件的值。
生命周期 | 发生了什么 |
---|---|
beforeCreate | 初始化界面前 : 在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问,都还没有完成初始化。 |
created | 初始化界面后 : 在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数,也就是不会更新视图。实例的data数据和methods方法都已经被初始化完毕了,可以正常访问 |
beforeMount | 挂载前 :完成模板编译,虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。数据还没有更新到页面上去。当编译完成之后,只是在内存中已经有了编译好的页面,但并未渲染。 |
mounted | 挂载完成 : 将编译好的模板挂载到页面 (虚拟DOM挂载) ,可以在这进行异步请求以及DOM节点的访问,在vue用$ref操作 |
beforeUpdate | 更新数据前 : 组件数据更新之前调用,数据都是新的,页面上数据都是旧的。将要根据最新的data数据,重新解析所有指令,从而重新渲染浏览器页面。 |
updated | 组件更新后 : render重新渲染 , 此时数据和界面都是新的 ,要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新 |
beforeDestroy | 组件卸载前 : 实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器 |
destroyed | 组件卸载后 : 组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。 |
activited | keep-alive 专属 , 组件被激活时调用 |
deactivated | keep-alive 专属 , 组件被销毁时调用 |
1、手段:v-if是动态的向DOM树添加或者删除DOM元素;v-show是通过设置DOM元素的display样式属性控制显示和隐藏。
2、编译过程:v-if切换有一个局部编译/卸载的过程, 切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换。
3、编译条件:v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译(编译被缓存?编译被缓存后,然后再切换的时候进行局部卸载); v-show是在任何条件下(首次条件是否为真)都被编译,然后被缓存,而且DOM元素保留
4、性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗 使用场景:v-if适合运营条件不大可能改变;v-show适合频繁切换
6、相同点: v-show 都可以动态控制着dom元素的显示隐藏 不同点: v-if 的显示隐藏是将DOM元素整个添加或删除,v-show 的显示隐藏是为DOM元素添
7、加css的样式display,设置none或者是block,DOM元素是还存在的
8、在渲染多个元素的时候,可以把一个 元素作为包装元素,并使用v-if 进行条件判断,最终的渲染不会包含这个元素,v-show是不支持 语法
1、$nextTick 是在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM,意思是 等你dom加载完毕以后再去调用nextTick()里面的数据内容
1、问题描述:页面刷新的时候vuex里的数据会重新初始化,导致数据丢失。因为vuex里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,vuex里面的数据就会被重新赋值。
2、解决思路:
办法一:将vuex中的数据直接保存到浏览器缓存中(sessionStorage、localStorage、cookie) 办法二:在页面刷新的时候再次请求远程数据,使之动态更新vuex数据 办法三:在父页面向后台请求远程数据,并且在页面刷新前将vuex的数据先保存至sessionStorage(以防请求数据量过大页面加载时拿不到返回的数据)
3、 解决过程:
3.1、选择合适的浏览器存储
3.2、解决方案
1、路由跳转
router-link组件 默认会被渲染成一个 标签,进行跳转,在组件中可以通过绑定to
属性来指定要跳转的链接;tag
属性指本来的标签
<router-link :to="/foo" tag="h2">Foo</router-link>
$router.push()方法
2、路由传参
query
// 方法一
<template>
<router-link
:to="{
path: 'blogDetail',
query: { id: item.id, views: item.views }
}"
tag="h2"
>
</router-link>
</template>
// 方法二
this.$router.push({
path: 'blogDetail',
query: {
id: item.id,
views: item.views
}
})
params
<template>
<router-link
:to="{
name: 'blogDetail',
params: { id: item.id, views: item.views }
}"
tag="h2"
>
</router-link>
</template>
this.$router.push({
name: 'blogDetail',
params: {
id: item.id,
views: item.views
}
})
Vue.js 中的动态组件和异步组件是两个不同的概念,它们都可以帮助你在应用程序中更灵活地管理和加载组件。
动态组件允许你动态地在同一个挂载点上切换不同的组件。你可以使用 Vue 的
元素来实现动态组件。这个元素的 is
特性可以绑定一个组件名,根据绑定的数据动态地渲染不同的组件。
示例:
上面的例子中,currentComponent
是一个在数据中定义的变量,根据它的值,不同的组件会在相同的挂载点上动态地渲染出来。
异步组件允许你延迟加载组件,在需要时再进行加载,这对于优化大型应用程序的性能很有帮助。Vue 允许你使用工厂函数或 import()
语法定义异步组件。
Vue.component('async-component', function (resolve, reject) {
// 通过 setTimeout 模拟异步加载
setTimeout(function () {
resolve({
template: '这是一个异步加载的组件'
});
}, 1000);
});
import()
:Vue.component('async-component', () => import('./AsyncComponent.vue'));
在上述例子中,import()
函数会在需要时动态加载 AsyncComponent.vue
文件。异步组件在首次渲染时不会被加载,而是在它们被实际需要的时候才会被下载并渲染。
这两种组件的使用场景不同,动态组件更多用于在同一个挂载点上动态切换组件,而异步组件则更多用于按需加载以优化应用程序的性能。
router.beforeEach
全局前置守卫 进入路由之前router.beforeResolve
全局解析守卫(2.5.0+) 在beforeRouteEnter
调用之后调用router.afterEach
全局后置钩子 进入路由之后1、 Vue异步加载技术
1:vue-router配置路由,使用vue的异步组件技术,可以实现懒加载,此时一个组件会生成一个js文件。
2:component: resolve => require(['放入需要加载的路由地址'], resolve)
2、 ES6推荐方式imprort ()----推荐使用
import Vue from 'vue';
import Router from 'vue-router';
// 官网可知:下面没有指定webpackChunkName,每个组件打包成一个js文件。
const Foo = () => import('../components/Foo')
const Aoo = () => import('../components/Aoo')
// 下面2行代码,指定了相同的webpackChunkName,会合并打包成一个js文件。
// const Foo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/Foo')
// const Aoo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/Aoo')
export default new Router({
routes: [
{
path: '/Foo',
name: 'Foo',
component: Foo
},
{
path: '/Aoo',
name: 'Aoo',
component: Aoo
}
]
})
3、 webpack提供的require.ensure()实现懒加载
1:vue-router配置路由,使用webpack的require.ensure技术,也可以实现按需加载。
2:这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。
3:require.ensure可实现按需加载资源,包括js,css等。他会给里面require的文件单独打包,不会和主文件打包在一起。
4:第一个参数是数组,表明第二个参数里需要依赖的模块,这些会提前加载。
5:第二个是回调函数,在这个回调函数里面require的文件会被单独打包成一个chunk,不会和主文件打包在一起,这样就生成了两个chunk,第一次加载时只加载主文件。
6:第三个参数是错误回调。
7:第四个参数是单独打包的chunk的文件名
import Vue from 'vue';
import Router from 'vue-router';
const HelloWorld=resolve=>{
require.ensure(['@/components/HelloWorld'],()=>{
resolve(require('@/components/HelloWorld'))
})
}
Vue.use('Router')
export default new Router({
routes:[{
{path:'./',
name:'HelloWorld',
component:HelloWorld
}
}]
})
slot用于封装组件中,写在子组件 接收父组件动态传递子组件内容片断
slot插槽的使用方法其实就是类似于一个子组件或者标签的引用的过程,在父组件里面定义一个slot,给她起个name,然后组件引入到子组件,子组件里面有个元素的属性slot值等于name,然后父组件里面没有值的时候就可以显示子组件里面的信息了(切记:插槽slot要写在父组件里面!!!)
vue slot用法1:
slot主要是让组件的可扩展性更强
1.匿名slot使用
//定义组件
//定义组件
//使用方法
如果不在slot里加入任何标签,slot什么都不会显示。
组件支持用户在具有路由功能的应用中 (点击) 导航。 通过 to 属性指定目标地址,默认渲染成带有正确链接的 标签,可以通过配置 tag 属性生成别的标签.。 通过router-link进行跳转不会跳转到新的页面,也不会重新渲染,它会选择路由所指的组件进行渲染,避免了重复渲染的“无用功”。 总结:对比,router-link组件避免了不必要的重渲染,它只更新变化的部分从而减少DOM性能消耗
hash | history | |
---|---|---|
url显示 | 有#,很Low | 无#,好看 |
回车刷新 | 可以加载到hash值对应页面 | 一般就是404掉了 |
支持版本 | 支持低版本浏览器和IE浏览器 | HTML5新推出的API |
在 Vue 中,子组件不应该直接修改从父组件传递下来的 props。props 被视为单向数据流,意味着它们应该由父组件传递给子组件,并在子组件中作为不可变的数据来使用。
如果子组件需要修改 props 传递下来的数据,这通常是不推荐的,因为这会导致状态不一致和难以追踪的问题。Vue 严格遵循单向数据流的原则,子组件不应该直接修改 props。
如果子组件需要根据 props 的值进行一些操作,有几种替代方法:
修改 props 可能会引起不可预测的行为,因此最好避免这样做,而是通过其他方式管理组件之间的数据传递和通信。
vue template模板编译的过程经过parse()生成ast(抽象语法树),optimize对静态节点优化,generate()生成render字符串 之后调用new Watcher()函数,用来监听数据的变化,render 函数就是数据监听的回调所调用的,其结果便是重新生成 vnode。 当这个 render 函数字符串在第一次 mount、或者绑定的数据更新的时候,都会被调用,生成 Vnode。 如果是数据的更新,那么 Vnode 会与数据改变之前的 Vnode 做 diff,对内容做改动之后,就会更新到 我们真正的 DOM
<p v-on="{click:dbClick,mousemove:MouseClick}"></p>
只要进行调用就会执行,不管依赖的值有没有改变。无缓存。
监听其所有依赖的变化,如果有变化会执行,没有变化不执行。有缓存,不用每次重新算。不支持异步。
详解:在vue的 模板内({{}}
)是可以写一些简单的js表达式的 ,很便利。但是如果在页面中使用大量或是复杂的表达式去处理数据,对页面的维护会有很大的影响。这个时候就需要用到computed 计算属性来处理复杂的逻辑运算。
1.优点:
在数据未发生变化时,优先读取缓存。computed 计算属性只有在相关的数据发生变化时才会改变要计算的属性,当相关数据没有变化是,它会读取缓存。而不必想 motheds方法 和 watch 方法是否每次都去执行函数。
2.setter 和 getter方法:(注意在vue中书写时用set 和 get)
setter 方法在设置值是触发。
getter 方法在获取值时触发。
观察某一个变量,发生变化会执行。支持异步。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性。
一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。
小结:
1.主动调用的方法写在methods里,依据某些变量的更新进行某种操作用computed或者watch。
2.computed和watch:如果要异步,只能用watch。如果是计算某个值推荐用computed,比如购物车全选单选功能,购物车计算总价小计功能。
Vue 提供了一些修饰符,用于在事件处理、指令和表单输入时改变行为或增强功能。以下是一些常用的 Vue 修饰符:
.stop
:阻止事件继续传播。.prevent
:阻止默认事件的触发。.capture
:事件将以捕获模式触发。.self
:只有事件是由当前元素本身触发时才触发事件处理函数。.once
:事件将只会触发一次。.passive
:告诉浏览器你不想阻止事件的默认行为。.enter
:只在 Enter 键按下时触发事件。.tab
:只在 Tab 键按下时触发事件。.delete
/ .backspace
:只在删除键按下时触发事件。.esc
:只在 Esc 键按下时触发事件。.space
:只在 Space 键按下时触发事件。.up
/ .down
/ .left
/ .right
:只在相应的箭头键按下时触发事件。.lazy
:将输入事件改为 change 事件。.trim
:自动过滤用户输入的首尾空白字符。
修饰符可以帮助你更好地控制事件、输入和指令的行为,使代码更加清晰、简洁并且更符合预期。
vue中列表循环需加:key=“唯一标识” 唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM
Vue 的 computed
属性是用于在 Vue 实例中计算派生数据的一种方式。它的原理涉及到 Vue 的响应式系统。
当 Vue 实例被创建时,Vue 会遍历其数据对象的所有属性,并使用 Object.defineProperty
(或者在不支持 Object.defineProperty
的环境中采用类似方法)将这些属性转换为 getter/setter。这样一来,当属性被访问或修改时,Vue 将能够捕获到这些操作,并通知相关的界面进行响应更新。
computed
属性允许你声明式地定义一个计算属性。当依赖的响应式数据发生变化时,计算属性会自动重新计算其值,但仅当相关依赖发生变化时才会触发。
// 示例
new Vue({
data: {
message: 'Hello'
},
computed: {
reversedMessage: function () {
// 计算属性依赖于 this.message
return this.message.split('').reverse().join('');
}
}
});
在这个示例中,reversedMessage
计算属性依赖于 message
数据属性。只要 message
发生变化,reversedMessage
会自动更新为 message
的反转字符串。
这种方式使得开发者能够以声明式的方式编写复杂的数据操作,而不必手动追踪依赖或手动触发更新。Vue 的响应式系统会自动处理这些计算属性的依赖追踪和更新。
虚拟DOM其实就是一个JavaScript对象。通过这个JavaScript对象来描述真实DOM,真实DOM的操作,一般都会对某块元素的整体重新渲染,采用虚拟DOM的话,当数据变化的时候,只需要局部刷新变化的位置就好了 ,
虚拟dom
相当于在js
和真实dom
中间加了一个缓存,利用dom diff
算法避免了没有必要的dom
操作,从而提高性能
具体实现步骤如下
JavaScript
对象结构表示 DOM
树的结构;然后用这个树构建一个真正的 DOM
树,插到文档当中DOM
树上,视图就更新Vue.js 中父子组件的生命周期钩子函数执行顺序如下:
值得注意的是,父组件的生命周期钩子函数在其子组件之前执行。而在父子组件各自的生命周期中,创建(created)、挂载(mounted)、更新(updated)和销毁(destroyed)钩子函数的执行顺序是相同的。
单页应用 页面跳转---->js渲染 优点:页面切换快 缺点:首屏加载稍慢,seo差
多页应用 页面跳转---->返回html 优点:首屏时间快,seo效果好 缺点:页面切换慢
微任务
原理:
nextTick方法主要是使用了宏任务和微任务,定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空队列。
作用: nextTick用于下次Dom更新循环结束之后执行延迟回调,在修改数据之后使用nextTick用于下次Dom更新循环结束之后执行延迟回调,在修改数据之后使用nextTick用于下次Dom更新循环结束之后执行延迟回调,在修改数据之后使用nextTick,则可以在回调中获取更新后的DOM。
1、首屏加载优化
2、路由懒加载
{
path: '/',
name: 'home',
component: () => import('./views/home/index.vue'),
meta: { isShowHead: true }
}
3、开启服务器 Gzip
开启 Gzip 就是一种压缩技术,需要前端提供压缩包,然后在服务器开启压缩,文件在服务器压缩后传给浏览器,浏览器解压后进行再进行解析。首先安装 webpack 提供的compression-webpack-plugin
进行压缩,然后在 vue.config.js:
const CompressionWebpackPlugin = require('compression-webpack-plugin')
const productionGzipExtensions = ['js', 'css']......plugins: [
new CompressionWebpackPlugin(
{
algorithm: 'gzip',
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 10240,
minRatio: 0.8
}
)]....
4、启动 CDN 加速
我们继续采用 cdn 的方式来引入一些第三方资源,就可以缓解我们服务器的压力,原理是将我们的压力分给其他服务器点。
5、代码层面优化
computed 和 watch 区分使用场景
computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算;
watch:类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
v-if 和 v-show 区分使用场景 v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show 则适用于需要非常频繁切换条件的场景。这里要说的优化点在于减少页面中 dom 总数,我比较倾向于使用 v-if,因为减少了 dom 数量。
v-for 遍历必须为 item 添加 key,且避免同时使用 v-if v-for 遍历必须为 item 添加 key,循环调用子组件时添加 key,key 可以唯一标识一个循环个体,可以使用例如 item.id 作为 key 避免同时使用 v-if,v-for 比 v-if 优先级高,如果每一次都需要遍历整个数组,将会影响速度。
6、Webpack 对图片进行压缩
7、避免内存泄漏
8、减少 ES6 转为 ES5 的冗余代码
综上可知,如果data是一个函数的话,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。
所以说vue组件的data必须是函数。这都是因为js的特性带来的,跟vue本身设计无关。
单向数据流指只能从一个方向来修改状态。
数据从父级组件传递给子组件,只能单向绑定。
子组件内部不能直接修改从父级传递过来的数据。
VNode
(虚拟节点)是 Vue 中用于描述 DOM 结构的对象。它是 Vue 在更新 DOM 时使用的一种抽象表示,但它也有一些缺点:
VNode
的创建和管理需要占用一定的内存。在大型应用中,如果频繁地创建和销毁大量的 VNode
对象,可能会导致内存消耗较大。
尽管 VNode
的使用使得 Vue 能够更高效地进行虚拟 DOM 的比对和更新,但在极端情况下,频繁的虚拟 DOM 比对也可能导致一定的性能开销。特别是当页面中有大量动态节点、频繁的更新操作或者复杂的计算时,虚拟 DOM 的比对成本可能会增加。
对于新手开发者来说,理解和使用 VNode
可能会增加学习曲线。这个抽象概念需要一定的时间和实践来熟悉和掌握。
在某些场景下,例如性能要求非常高的小型应用或者静态内容展示页,使用虚拟 DOM 的成本可能会超过其带来的好处,这时直接操作原生 DOM 可能更为高效。
尽管 VNode
有其缺点,但在大多数情况下,它作为 Vue 的核心特性,为开发者提供了更好的开发体验和维护大型应用程序的能力。Vue 在内部会尽可能地优化和管理 VNode
,以平衡性能和开发者的使用便利性。
在从使用 jQuery 编写的页面切换到使用 Vue 编写的页面时,可能会出现一些不同。这些差异可能导致一些卡顿或性能方面的问题,但通常情况下,这些问题是可以解决或优化的。
v-if
和 v-for
,避免不必要的重新渲染,合理使用组件懒加载等。综上所述,从 jQuery 切换到 Vue 可能会遇到一些初始的性能问题,但通过逐步迁移、优化和重构代码,可以解决或减轻这些问题,最终得到更好的性能和开发体验。
每次通过vue-router进行页面跳转,都会触发beforeEach这个钩子函数,这个回调函数共有三个参数,to,from,next这三个参数,to表示我要跳转的目标路由对应的参数,from表示来自那个路由,就是操作路由跳转之前的,即将离开的路由对应的参数,next是一个回调函数,一定要调用next方法来resolve这个钩子函数;
diff算法是一种通过同层的树节点进行比较的高效算法,避免了对树进行逐层搜索遍历,所以时间复杂度只有 O(n)。
diff算法有两个比较显著的特点:
1、比较只会在同层级进行, 不会跨层级比较。
2、在diff比较的过程中,循环从两边向中间收拢。
diff流程: 首先定义 oldStartIdx、newStartIdx、oldEndIdx 以及 newEndIdx 分别是新老两个 VNode 的两边的索引。
接下来是一个 while 循环,在这过程中,oldStartIdx、newStartIdx、oldEndIdx 以及 newEndIdx 会逐渐向中间靠拢。while 循环的退出条件是直到老节点或者新节点的开始位置大于结束位置。
while 循环中会遇到四种情况:
情形一:当新老 VNode 节点的 start 是同一节点时,直接 patchVnode 即可,同时新老 VNode 节点的开始索引都加 1。
情形二:当新老 VNode 节点的 end 是同一节点时,直接 patchVnode 即可,同时新老 VNode 节点的结束索引都减 1。
情形三:当老 VNode 节点的 start 和新 VNode 节点的 end 是同一节点时,这说明这次数据更新后 oldStartVnode 已经跑到了 oldEndVnode 后面去了。这时候在 patchVnode 后,还需要将当前真实 dom 节点移动到 oldEndVnode 的后面,同时老 VNode 节点开始索引加 1,新 VNode 节点的结束索引减 1。
情形四:当老 VNode 节点的 end 和新 VNode 节点的 start 是同一节点时,这说明这次数据更新后 oldEndVnode 跑到了 oldStartVnode 的前面去了。这时候在 patchVnode 后,还需要将当前真实 dom 节点移动到 oldStartVnode 的前面,同时老 VNode 节点结束索引减 1,新 VNode 节点的开始索引加 1。
while 循环的退出条件是直到老节点或者新节点的开始位置大于结束位置。
情形一:如果在循环中,oldStartIdx大于oldEndIdx了,那就表示oldChildren比newChildren先循环完毕,那么newChildren里面剩余的节点都是需要新增的节点,把[newStartIdx, newEndIdx]之间的所有节点都插入到DOM中
情形二:如果在循环中,newStartIdx大于newEndIdx了,那就表示newChildren比oldChildren先循环完毕,那么oldChildren里面剩余的节点都是需要删除的节点,把[oldStartIdx, oldEndIdx]之间的所有节点都删除
Vue 使用虚拟 DOM(Virtual DOM)来提高性能,它是一种在内存中对 DOM 结构进行抽象表示的技术。
值得注意的是,虽然虚拟 DOM 可以提高性能,但在某些情况下,直接操作实际 DOM 可能更为高效,尤其是对于一些简单的页面或对性能要求极高的场景。Vue 在内部会尽可能地优化虚拟 DOM 的使用,以达到更好的性能表现。
vue中列表循环需加:key=“唯一标识” 唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM
// 使用axios用于对数据的请求
import axios from 'axios'
// 创建axios实例
const instance = axios.create({
baseURL: baseURL + version,
timeout: 5000
})
// 创建请求的拦截器
instance.interceptors.request.use(config => {
config.headers['Authorization'] = localStorage.getItem('token')
return config
}, error => {
return Promise.reject(error)
})
// 创建响应的拦截器
instance.interceptors.response.use(response => {
let res = null
// 对相应的数据进行过滤
if (response.status === 200) {
if (response.data && response.data.err === 0) {
res = response.data.data
} else if (response.data.err === -1) {
return alert('token无效')
}
} else {
return alert('请求失败')
}
return res
}, error => {
return Promise.reject(error)
})
export default instance
中间件机制通常用于在软件系统中的请求处理过程中插入额外的逻辑,它是一种将请求处理流程分解为一系列独立处理单元的方式。在不同的软件框架和系统中,中间件的生效方式可能会有所不同。
在很多 Web 框架中(比如 Express.js、Koa、Django 等),中间件通常按照定义的顺序依次执行,处理请求并在处理过程中执行相应的操作。以下是一般中间件生效的步骤:
中间件机制的优势在于它提供了一种灵活的方式来对请求进行预处理、处理和后处理,并且可以在系统中简单地插入新的功能或操作,而不需要改变现有的代码结构。这种模式使得系统更易于扩展、维护和管理。
Vuex 是 Vue.js 官方提供的状态管理库,它可以在 Vue 应用程序中实现全局的状态管理。在 Vuex 中,可以通过 state(状态)来存储应用程序中的数据,而通过 mutations(变更)和 actions(动作)来修改和操作这些数据。
要实现跨组件的数据监听,Vuex 提供了一种集中式存储管理的方式,通过以下几个核心概念来实现:
在 Vuex 中,数据存储在状态树(state tree)中。任何组件都可以访问和读取状态中的数据,因此当状态发生变化时,所有依赖这些数据的组件都可以实时感知到这种变化。
Mutations 是用来修改 Vuex 中状态的方法,每个 Mutation 都有一个字符串类型的事件类型(type)和一个回调函数,这个函数接收 state 作为第一个参数,允许对状态进行同步修改。
Actions 类似于 Mutations,但是它可以处理异步操作。Actions 提交 Mutations,而不是直接修改状态。它可以包含任何异步操作,并在完成后提交 Mutations。
Getters 允许你在 Vuex 中对 state 进行计算操作,并且将计算后的结果缓存起来,类似于 Vue 中的计算属性。
通过这些核心概念,任何一个组件都可以通过提交 Mutations 或者调用 Actions 来改变 Vuex 中的状态。当状态发生变化时,所有依赖这些状态的组件都会得到更新,因为它们都是从同一个状态树中获取数据。
这种机制使得 Vuex 能够实现跨组件的数据监听,而且可以更好地管理应用程序的状态。任何一个组件都可以触发状态的变更,而所有依赖这些状态的组件都会自动更新。
vue的双向绑定是由数据劫持结合发布者-订阅者模式实现的,那么什么是数据劫持?vue是如何进行数据劫持的?说白了就是通过Object.defineProperty()来劫持对象属性的setter和getter操作,在数据变动时做你想要做的事情
我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有属性。如果属性发生变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,对每个节点元素进行扫描和解析,将相关指令(如v-model,v-on)对应初始化成一个订阅者Watcher,并替换模板数据或者绑定相应的函数,此时当订阅者Watcher接收到相应属性的变化,就会执行对应的更新函数,从而更新视图。因此接下去我们执行以下3个步骤,实现数据的双向绑定:
1.实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者。
2.实现一个订阅者Watcher,每一个Watcher都绑定一个更新函数,watcher可以收到属性的变化通知并执行相应的函数,从而更新视图。
3.实现一个解析器Compile,可以扫描和解析每个节点的相关指令(v-model,v-on等指令),如果节点存在v-model,v-on等指令,则解析器Compile初始化这类节点的模板数据,使之可以显示在视图上,然后初始化相应的订阅者(Watcher)。
1、作用:
迫使 Vue
实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
2、内部原理:
Vue.prototype.$forceUpdate = function () {
const vm: Component = this
if (vm._watcher) {
vm._watcher.update()
}
}
实例需要重新渲染是在依赖发生变化的时候会通知watcher,然后通知watcher来调用update方法,就是这么简单。
主要通过Proxy对对象进行绑定监听处理,通过new Map对对象的属性操作进行处理,将要执行的函数匹配到存到对应的prop上面,通过每次的访问触发get方法,进行存方法的操作,通过修改触发set的方法,此时执行回调监听的函数,这样达到修改数据和视图的
1.观察者observer:首先通过观察者对data中的属性使用object.defineproperty劫持数据的getter和setter,通知订阅者,触发他的update方法,对视图进行更新
2.Compile:用来解析模板指令,并替换模板数据,初始化视图,初始化相应的订阅器
3.订阅者Watcher:订阅者接到通知后,调用update方法更新对应的视图
4.订阅器Dep:订阅者可能有多个,因此需要订阅器Dep来专门接收这些订阅者,并统一管理
但在vue3中抛弃了object.defineproperty方法,因为
1.Object.defineproperty无法监测对象属性的添加和删除、数组索引和长度的变更,因此vue重写了数组的push/pop/shift/unshift/splice/sort/reverse方法
2.Object.defineProperty只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历,这样很消耗性能
vue3中实现数据双向绑定的原理是数据代理,使用proxy实现。Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
常用的生命周期有,beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed
通用组件的封装就是对可复用组件的解耦和样式复用,为了解耦一般数据都是通过父组件传递过来,在子组件中进行数据处理,对于一些较为复杂的数据可能还需要做数据验证,为了避免高耦合,逻辑最好放在父组件中,通过自定义事件将数据回传,子组件只是一个承载体,这样既降低耦合,保证子组件中数据和逻辑不会混乱。如果同一组件需要适应不同需求时,我们需要配合slot来使用,可以通过具名插槽灵活地解决了不同场景同一组件不同配置的问题。
在Vue中利用数据劫持defineProperty在原型prototype上初始化了一些getter,分别是router代表当前Router的实例 、 router代表当前Router的实例、router代表当前Router的实例、route 代表当前Router的信息。在install中也全局注册了router-view,router-link,其中的Vue.util.defineReactive, 这是Vue里面观察者劫持数据的方法,劫持_route,当_route触发setter方法的时候,则会通知到依赖的组件。
接下来在init中,会挂载判断是路由的模式,是history或者是hash,点击行为按钮,调用hashchange或者popstate的同时更_route,_route的更新会触发route-view的重新渲染。
1. 定义组件并抛出
2. import引入,并在component里面定义
3. 使用组件(注意首字母大写)
通过params传参会出现参数丢失的情况,可以通过query的传参方式或者在路由匹配规则加入占位符即可以解决参数丢失的情况。
1:Vue 使用的是 web 开发者更熟悉的模板与特性,Vue的API跟传统web开发者熟悉的模板契合度更高,比如Vue的单文件组件是以模板+JavaScript+CSS的组合模式呈现,它跟web现有的HTML、JavaScript、CSS能够更好地配合。React 的特色在于函数式编程的理念和丰富的技术选型,Vue更加注重web开发者的习惯。
2:Vue跟React的最大区别在于数据的reactivity,就是反应式系统上。Vue提供反应式的数据,当数据改动时,界面就会自动更新,而React里面需要调用方法SetState。我把两者分别称为Push-based和Pull-based
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的,并且state中的值会伴随着浏览器的刷新而初始化,无缓存。
评估项目成员的水平,如果成员js基础较好、编码能力较强则选择React,否则Vue。
评估系统的大小,如果想构建生态系统,则选择React,如果要求而快,简单和“能用就行",则选择Vue。
评估系统运行环境,如果你想要一个同时适用于Web端和原生APP的框架,请选择React(RN)。
1:无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
2:只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。
create为组件初始化阶段,在此阶段主要完成数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,此时还未生成真实的DOM,也就无法获取和操作DOM元素。而mount主要完成从虚拟DOM到真实DOM的转换挂载,此时html已经渲染出来了,所以可以直接操作dom节点。
vue和react的diff算法,都是忽略跨级比较,只做同级比较。vue diff时调动patch函数,参数是vnode和oldVnode,分别代表新旧节点。
1.vue对比节点。当节点元素相同,但是classname不同,认为是不同类型的元素,删除重建,而react认为是同类型节点,只是修改节点属性。
2.vue的列表对比,采用的是两端到中间比对的方式,而react采用的是从左到右依次对比的方式。当一个集合只是把最后一个节点移到了第一个,react会把前面的节点依次移动,而vue只会把最后一个节点移到第一个。总体上,vue的方式比较高效。
在style标签上加上scoped属性
vue-router 提供的导航守卫主要用来对路由的跳转进行监控,控制它的跳转或取消,路由守卫有全局的, 单个路由独享的, 或者组件级的。导航钩子有3个参数:
1、to:即将要进入的目标路由对象;
2、from:当前导航即将要离开的路由对象;
3、next :调用该方法后,才能进入下一个钩子函数(afterEach)。
用过,都使用过。插槽相当于预留了一个位置,可以将我们书写在组件内的内容放入,写一个插槽就会将组件内的内容替换一次,两次则替换两次。为了自定义插槽的位置我们可以给插槽取名,它会根据插槽名来插入内容,一一对应。
Vuex是Vue.js的官方状态管理库,用于集中式管理Vue应用中的各种组件共享的状态。它通过一个全局的状态树来管理应用中的所有状态,并提供了一些方法用于状态的修改、监听和响应。
使用Vuex,你可以创建一个全局的 store 对象,其中包含了你希望保存的所有状态。这个 store 中的状态可以被任意组件访问和修改,但是状态的修改需要通过特定的方法(mutations)来实现,以确保状态的变更是可追踪和可控的。
实现登录功能的流程可以概括如下:
设置状态: 在 Vuex 的 store 中设置一个状态来表示用户登录状态,比如 isLoggedin,初始值为 false。
定义 mutations: 创建 mutations 方法来修改 isLoggedin 状态,包括登录和登出两种情况。比如:
const store = new Vuex.Store({
state: {
isLoggedin: false
},
mutations: {
login(state) {
state.isLoggedin = true;
},
logout(state) {
state.isLoggedin = false;
}
}
});
调用 mutations: 在登录页面或者组件中,当用户完成登录时,触发对应的 mutations 方法来改变登录状态。
methods: {
login() {
// 在这里进行用户验证,如果验证通过则调用登录 mutations
this.$store.commit('login');
}
}
获取状态: 在需要使用登录状态的地方,可以通过 Vuex 的 getter 方法来获取 isLoggedin 状态。
computed: {
isLoggedIn() {
return this.$store.state.isLoggedin;
}
}
这样,通过以上流程,在应用中你可以方便地使用 Vuex 来管理登录状态。当用户登录时,调用登录 mutations 来修改状态,当用户登出时,调用登出 mutations 来改变状态。并且在整个应用中都可以随时获取和监控这个登录状态。
通常的url 地址由以下内容构成:协议名 域名 端口号 路径 参数 哈希值,当哈希值改变,页面不会发生跳转,单页面应用就是利用了这一点,给window注册onhashchange事件,当哈希值改变时通过location.hash就能获得相应的哈希值,然后就能跳到相应的页面。
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。一旦使用keepalive包裹组件,此时mouted,created等钩子函数只会在第一次进入组件时调用,当再次切换回来时将不会调用。此时如果我们还想在每次切换时做一些事情,就需要用到另外的周期函数,actived和deactived,这两个钩子函数只有被keepalive包裹后才会调用。
// 优点
1单页应用的内容的改变不需要重新加载整个页面,web应用更具响应性和更令人着迷。
2、单页应用没有页面之间的切换,就不会出现“白屏现象”,也不会出现假死并有“闪烁”现象
3、单页应用相对服务器压力小,服务器只用出数据就可以,不用管展示逻辑和页面合成,吞吐能力会提高几倍。
4、良好的前后端分离。后端不再负责模板渲染、输出页面工作,后端API通用化,即同一套后端程序代码,不用修改就可以用于Web界面、手机、平板等多种客户端。
// 缺点
1、首次加载耗时比较多。
2、SEO问题,不利于百度,360等搜索引擎收录。
3、容易造成Css命名冲突。
4、前进、后退、地址栏、书签等,都需要程序进行管理,页面的复杂度很高,需要一定的技能水平和开发成本高。
通过filter来定义过滤器,过滤器分为全局和局部过滤器,过滤器的主体为一个普通的函数,来对数据进行处理,可以传递参数。当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用。
通过directive来自定义指令,自定义指令分为全局指令和局部指令,自定义指令也有几个的钩子函数,常用的有bind和update,当 bind 和 update 时触发相同行为,而不关心其它的钩子时可以简写。一个表达式可以使用多个过滤器。过滤器之间需要用管道符“|”隔开。其执行顺序从左往右。
jQuery 专注视图层,通过直接操作 DOM 去实现页面的一些逻辑渲染;Vue 专注于数据层,通过数据的双向绑定,最终表现在 DOM 层面,减少了 DOM 操作。Vue 使用了组件化思想,使得项目子集职责清晰,提高了开发效率,方便重复利用,便于协同开发
$route用来获取路由的信息的,它是路由信息的一个对象,里面包含路由的一些基本信息,包括name、meta、path、hash、query、params、fullPath、matched、redirectedFrom等。而$router主要是用来操作路由的,它是VueRouter的实例,包含了一些路由的跳转方法,钩子函数等
Vue Router 是 Vue.js 官方的路由管理器,它基于 Vue.js 实现了单页面应用(SPA)的路由功能。其实现原理主要基于 Vue.js 提供的组件化开发思想和观察者模式,下面是它的一些核心实现原理:
/user/:id
这样的路由可以匹配 /user/123
、/user/456
等不同的 URL,并且根据不同的 ID 参数渲染对应的组件。总体来说,Vue Router 的路由实现原理是基于 Vue.js 的组件化和响应式特性,在浏览器端利用 History API(或 hash 模式)来监听 URL 的变化,匹配路由并渲染对应的组件,同时提供了一系列的导航守卫来控制导航行为和实现额外逻辑。
严格来说,v-model和vuex并不直接冲突。v-model是Vue提供的一种双向数据绑定指令,用于将视图和模型的数据进行双向绑定。vuex是Vue提供的状态管理库,用于在应用程序中集中管理状态。
但是,在某些情况下,v-model和vuex可能会引起一些问题。例如,如果将v-model直接绑定到vuex中的状态,则可能会导致视图直接修改了vuex中的状态,而绕过了vuex的mutation。这可能会破坏状态的可预测性和一致性。
为了避免这种冲突,可以采取以下措施:
Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,否则无法被devtools所监测。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。
可以直接修改,但是极其不推荐,state的修改必须在mutation来修改,否则无法被devtool所监测,无法监测数据的来源,无法保存状态快照,也就无法实现时间漫游/回滚之类的操作。
如果应用够简单,最好不要使用 Vuex,一个简单的 store 模式即可,需要构建一个中大型单页应用时,使用Vuex能更好地在组件外部管理状态
1:在Compostion API 中时根据逻辑相关组织代码的,提高可读性和可维护性,类似于react的hook写法。
2:更好的重用逻辑代码,在Options API中通过MIxins重用逻辑代码,容易发生命名冲突且关系不清。
3:解决在生命周期函数经常包含不相关的逻辑,但又不得不把相关逻辑分离到了几个不同方法中的问题,如在mounted中设置定时器,但需要在destroyed中来清除定时器,将同一功能的代码拆分到不同的位置,造成后期代码维护的困难。
// 响应式原理
vue的响应式实现主要是利用了Object.defineProperty的方法里面的setter 与getter方法的观察者模式来实现。在组件初始化时会给每一个data属性注册getter和setter,然后再new 一个自己的Watcher对象,此时watcher会立即调用组件的render函数去生成虚拟DOM。在调用render的时候,就会需要用到data的属性值,此时会触发getter函数,将当前的Watcher函数注册进sub里。当data属性发生改变之后,就会遍历sub里所有的watcher对象,通知它们去重新渲染组件。
// proxy的优势如下:
Proxy 可以直接监听对象而非属性,可以直接监听数组的变化;
Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;
Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;
// Object.defineProperty 的优势如下:
兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill(垫片)来弥补
$root,和$parent都能访问父组件的属性和方法,区别在于如果存在多级子组件,通过parent 访问得到的是它最近一级的父组件,通过root 访问得到的是根父组件。通过在子组件标签定义 ref 属性,在父组件中可以使用$refs 访问子组件实例。
mvc 和 mvvm 其实区别并不大。都是一种设计思想。主要就是 mvc 中 Controller 演变成 mvvm 中的 viewModel。mvvm 主要解决了 mvc 中大量的 DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。和当 Model 频繁发生变化,开发者需要主动更新到 View 。
MVVM 是 Model-View-ViewModel 的缩写。mvvm 是一种设计思想。
1:Model 层代表数据模型,也可以在 Model 中定义数据修改和操作的业务逻辑;View 代表 UI 组件,它负责将数据模型转化成 UI 展现出来,ViewModel 是一个同步 View 和 Model 的对象。
2:在 MVVM 架构下,View 和 Model 之间并没有直接的联系,而是通过 ViewModel 进行交互,Model 和 ViewModel 之间的交互是双向的, 因此 View 数据的变化会同步到 Model 中,而 Model 数据的变化也会立即反应到 View 上。
3:ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而 View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作 DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
可以通过watch方法来对$route进行监听,或者通过导航守卫的钩子函数beforeRouteUpdate来监听它的变化。
只需将多个路径的component字段的值设置为同一个组件即可。
可以通过命名视图(router-view),它容许同一界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 router-view 没有设置名字,那么默认为 default。通过设置components即可同时渲染多个组件。
新增的v-slot属性怎么用?router-link 通过一个作用域插槽暴露底层的定制能力。这是一个更高阶的 API,主要面向库作者,但也可以为开发者提供便利,多数情况用在一个类似 NavLink 这样的自定义组件里。
在使用 v-slot API 时,需要向 router-link 传入一个单独的子元素。否则 router-link 将会把子元素包裹在一个 span 元素内。
在style标签上不加上scoped的属性,默认为全局css样式