vue面试题总结

Vuex和redux有什么区别?他们的共同思想是什么?

VuexRedux 都是用于管理状态的状态管理库,它们在不同的前端框架中有着相似的思想,但也有一些关键的区别。

区别:

  1. 框架依赖:
    • Vuex 是为 Vue.js 框架设计的状态管理库,直接集成到 Vue.js 应用中。
    • Redux 是一个独立于框架的 JavaScript 库,可以与多个框架一起使用,最为常见的是与 React 配合使用。
  2. 概念和结构:
    • Vuex 的核心概念包括 state(状态)、getters(获取器)、mutations(变化)和 actions(动作)。
    • Redux 的核心概念包括 store(存储)、reducers(减速器)和 actions(动作)。
  3. 异步处理:
    • Vuex 在处理异步操作时,推荐使用 actions。Actions 可以包含异步操作,并通过 mutations 触发状态的变化。
    • Redux 则使用中间件(如 redux-thunk)来处理异步操作。Actions 可以返回函数,而不仅仅是对象,以支持异步逻辑。

共同思想:

  1. 单一数据源:
    • VuexRedux 都遵循单一数据源的思想,整个应用的状态被存储在一个地方,便于追踪和调试。
  2. 不可变性:
    • 两者都鼓励使用不可变数据,即状态不直接修改,而是通过创建新的状态来实现。
  3. 纯函数:
    • Reducers(Vuex 中的 mutations)应该是纯函数,接受先前的状态和一个动作,返回新的状态。这确保了可预测性和可测试性。

Vuex 是专为 Vue.js 打造的,直接嵌入到 Vue 应用中,而 Redux 更像是独立的超级英雄,可以在不同框架间徜徉自如。它们的核心思想都是单一数据源、不可变性和纯函数。

Vue项目常见优化点

嘿,在 Vue 项目里,要想让应用变得更快更顺畅,有几个小技巧可以用。首先是用异步组件或者懒加载来加载东西,这样可以让页面更快地展示。还有就是图片要小巧精致,选对格式,也可以懒加载或者用个占位符。哦,还有,大的代码可以切成小块,慢慢加载,也可以把一些静态资源放到 CDN 上,让速度更快哦。如果有些东西会频繁显示隐藏,v-if 和 v-show 可以轻松搞定。最后,如果首屏加载很慢,可以用 缓存组件或者服务端渲染(SSR),这样用户就能快速看到内容啦。希望这些小技巧能帮到你!

Vue如何在用户没登陆的时候重定向登录界面?

现在 我们需要实现这样 一个功能,登录拦截,其实就是 路由拦截,首先在定义路由的时候就需要多添加一个自定义字段requireAuth,用于判断该路由的访问是否需要登录。如果用户已经登录,则顺利进入路由, 否则就进入登录页面。在路由管理页面添加meta字段

{
  path:'/manage',
  name:'manage',
  component:manage,
  meta:{requireAuth:true}
}

在入口文件中,添加路由守卫

  1. 先判断该路由是否需要登录权限
  2. 判断本地是否存在token,如果存在token就next(),不存在token重定向到登录页

谈谈你对Vue3.0有什么了解?

  • 性能比vue2.x快1.2~2倍
    • diff算法更快
    • 静态提升
    • 事件侦听缓存
  • 支持tree-shaking,按需编译,体积比vue2.x更小
    • 在vue3.0中创建vue项目 除了vue-cli,webpack外还有 一种创建方法是Vite Vite是作者开发的一款有意取代webpack的工具,其实现原理是利用ES6的import会发送请求去加载文件的特性,拦截这些请求,做一些预编译,省去webpack冗长的打包时间
  • 支持组合API
  • 更好的支持TS
  • 更先进的组件

vue3.0的组合API跟之前vue2.0在完成业务逻辑上的区别

在vue2.0中: 主要是往data 和method里面添加内容,一个业务逻辑需要什么data和method就往里面添加,而组合API就是 有一个自己的方法,里面有自己专注的data 和method。

再说一下组合API的本质是什么

首先composition API(组合API) 和 Option API(vue2.0中的data和method)可以共用 composition API(组合API)本质就是把内容添加到Option API中进行使用

ref和reactive的简单理解

1.ref和reactive都是vue3的监听数据的方法,本质是proxy 2.ref 基本类型复杂类型都可以监听(我们一般用ref监听基本类型),reactive只能监听对象(arr,json) 3.ref底层还是reactive

Vue的组件通信

1、props和$emit

父组件向子组件传递数据是通过prop传递的,子组件传递数据给父组件是通过$emit触发事件

2、 a t t r s 和 attrs和 attrslisteners

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和 parentchildren

7、boradcast和dispatch

8、vuex处理组件之间的数据交互 如果业务逻辑复杂,很多组件之间需要同时处理一些公共的数据,这个时候才有上面这一些方法可能不利于项目的维护,vuex的做法就是将这一些公共的数据抽离出来,然后其他组件就可以对这个公共数据进行读写操作,这样达到了解耦的目的。

简单说一下 微信小程序 与 Vue 的区别

1、生命周期:

小程序的钩子函数要简单得多 。 vue的钩子函数在跳转新页面时,钩子函数都会触发,但是小程序的钩子函数,页面不同的跳转方式,触发的钩子并不一样。

在页面加载请求数据时,两者钩子的使用有些类似,vue一般会在created或者mounted中请求数据,而在小程序,会在onLoad或者onShow中请求数据。

2、数据绑定:

vue动态绑定一个变量的值为元素的某个属性的时候,会在变量前面加上冒号:

<img :src="imgSrc"/>

小程序 绑定某个变量的值为元素属性时,会用两个大括号括起来,如果不加括号,为被认为是字符串

<image src="{{imgSrc}}"></image>

3、列表循环

4、显示与隐藏元素

vue中,使用v-ifv-show控制元素的显示和隐藏

小程序中,使用wx-ifhidden控制元素的显示和隐藏

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、生命周期:

小程序的钩子函数要简单得多 。 vue的钩子函数在跳转新页面时,钩子函数都会触发,但是小程序的钩子函数,页面不同的跳转方式,触发的钩子并不一样。

在页面加载请求数据时,两者钩子的使用有些类似,vue一般会在created或者mounted中请求数据,而在小程序,会在onLoad或者onShow中请求数据。

2、数据绑定:

vue动态绑定一个变量的值为元素的某个属性的时候,会在变量前面加上冒号:

<img :src="imgSrc"/>

小程序 绑定某个变量的值为元素属性时,会用两个大括号括起来,如果不加括号,为被认为是字符串

<image src="{{imgSrc}}"></image>

3、列表循环

4、显示与隐藏元素

vue中,使用v-ifv-show控制元素的显示和隐藏

小程序中,使用wx-ifhidden控制元素的显示和隐藏

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中,接收传递的值

什么是v-model?它的原理是什么?

1、v-model本质上是一个语法糖,可以看成是value + input 方法的语法糖。可以通过model的prop属性和event事件来进行自定义。

2、v-model是vue的双向绑定的指令,能将页面上控件输入的值同步更新到相关绑定的data属性, 也会在更新data绑定属性时候,更新页面上输入控件的值。

简述Vue每个生命周期具体适合哪些场景

生命周期 发生了什么
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 专属 , 组件被销毁时调用

v-if 和 v-show的区别

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是不支持 语法

什么是 Vue.nextTick()?

1、$nextTick 是在下次DOM更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM,意思是 等你dom加载完毕以后再去调用nextTick()里面的数据内容

Vuex刷新页面数据会丢失吗?咋解决的?

1、问题描述:页面刷新的时候vuex里的数据会重新初始化,导致数据丢失。因为vuex里的数据是保存在运行内存中的,当页面刷新时,页面会重新加载vue实例,vuex里面的数据就会被重新赋值。

2、解决思路:

办法一:将vuex中的数据直接保存到浏览器缓存中(sessionStorage、localStorage、cookie) 办法二:在页面刷新的时候再次请求远程数据,使之动态更新vuex数据 办法三:在父页面向后台请求远程数据,并且在页面刷新前将vuex的数据先保存至sessionStorage(以防请求数据量过大页面加载时拿不到返回的数据)

3、 解决过程:

3.1、选择合适的浏览器存储

3.2、解决方案

  1. 由于state里的数据是响应式,所以sessionStorage存储也要跟随变化,而且只能通过mutations来改变state中的值。 首先在用户登录成功之后,然后把用户信息,菜单信息通过actions分发保存至vuex中。然后在菜单页面计算vuex中state的菜单数据,将数据解析组装成前端组件所需的格式,然后渲染组件,生成菜单树。如果页面没有刷新,则一切正常。
  2. 监听浏览器刷新前事件,在浏览器刷新之前就把vuex里的数据保存至sessionStorage中,刷新成功后如果异步请求的数据还没返回则直接获取sessionStorage里的数据,否则获取vuex里的数据。

说一下Vue中路由跳转和传值的方式

1、路由跳转

  1. router-link组件 默认会被渲染成一个 标签,进行跳转,在组件中可以通过绑定to属性来指定要跳转的链接;tag属性指本来的标签

    <router-link :to="/foo"  tag="h2">Foo</router-link>
    
  2. $router.push()方法

2、路由传参

  1. 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
      }
    })
    
  2. 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.js 中的动态组件和异步组件是两个不同的概念,它们都可以帮助你在应用程序中更灵活地管理和加载组件。

动态组件(Dynamic Components)

动态组件允许你动态地在同一个挂载点上切换不同的组件。你可以使用 Vue 的 元素来实现动态组件。这个元素的 is 特性可以绑定一个组件名,根据绑定的数据动态地渲染不同的组件。

示例:


上面的例子中,currentComponent 是一个在数据中定义的变量,根据它的值,不同的组件会在相同的挂载点上动态地渲染出来。

异步组件(Async Components)

异步组件允许你延迟加载组件,在需要时再进行加载,这对于优化大型应用程序的性能很有帮助。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 文件。异步组件在首次渲染时不会被加载,而是在它们被实际需要的时候才会被下载并渲染。

这两种组件的使用场景不同,动态组件更多用于在同一个挂载点上动态切换组件,而异步组件则更多用于按需加载以优化应用程序的性能。

说一下Vue-router守卫有哪些。如何实现路由懒加载?

  • 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
    }
    }]
})

Vue插槽是什么?怎么使用?

slot用于封装组件中,写在子组件 接收父组件动态传递子组件内容片断

slot插槽的使用方法其实就是类似于一个子组件或者标签的引用的过程,在父组件里面定义一个slot,给她起个name,然后组件引入到子组件,子组件里面有个元素的属性slot值等于name,然后父组件里面没有值的时候就可以显示子组件里面的信息了(切记:插槽slot要写在父组件里面!!!)

vue slot用法1:

slot主要是让组件的可扩展性更强

1.匿名slot使用

//定义组件

//定义组件

//使用方法


    

如果不在slot里加入任何标签,slot什么都不会显示。

Vue中 router-link 和传统 a 链接的区别?

组件支持用户在具有路由功能的应用中 (点击) 导航。 通过 to 属性指定目标地址,默认渲染成带有正确链接的 标签,可以通过配置 tag 属性生成别的标签.。 通过router-link进行跳转不会跳转到新的页面,也不会重新渲染,它会选择路由所指的组件进行渲染,避免了重复渲染的“无用功”。 总结:对比,router-link组件避免了不必要的重渲染,它只更新变化的部分从而减少DOM性能消耗

说一下 VueRouter 的 hash 模式和 history 模式区别

hash history
url显示 有#,很Low 无#,好看
回车刷新 可以加载到hash值对应页面 一般就是404掉了
支持版本 支持低版本浏览器和IE浏览器 HTML5新推出的API

Vue中子组件是否可以修改props,如果想修改的话如何修改

在 Vue 中,子组件不应该直接修改从父组件传递下来的 props。props 被视为单向数据流,意味着它们应该由父组件传递给子组件,并在子组件中作为不可变的数据来使用。

如果子组件需要修改 props 传递下来的数据,这通常是不推荐的,因为这会导致状态不一致和难以追踪的问题。Vue 严格遵循单向数据流的原则,子组件不应该直接修改 props。

如果子组件需要根据 props 的值进行一些操作,有几种替代方法:

  1. 使用 Prop 数据计算新数据: 在子组件中,可以使用 props 的值来计算新的数据并将其作为子组件内部的数据进行操作,而不是直接修改 props。
  2. 触发事件向上传递信息: 子组件可以通过触发事件的方式告知父组件需要修改的内容,父组件捕获这个事件并相应地修改数据,然后再将修改后的数据通过 props 传递给子组件。
  3. 使用 Vuex 或其他状态管理工具: 如果你的应用有复杂的状态管理需求,可以考虑使用 Vuex 或其他状态管理工具,在全局状态中管理需要共享的数据,从而让多个组件共享数据并对其进行修改。

修改 props 可能会引起不可预测的行为,因此最好避免这样做,而是通过其他方式管理组件之间的数据传递和通信。

Vue 中 template 的编译过程

vue template模板编译的过程经过parse()生成ast(抽象语法树),optimize对静态节点优化,generate()生成render字符串 之后调用new Watcher()函数,用来监听数据的变化,render 函数就是数据监听的回调所调用的,其结果便是重新生成 vnode。 当这个 render 函数字符串在第一次 mount、或者绑定的数据更新的时候,都会被调用,生成 Vnode。 如果是数据的更新,那么 Vnode 会与数据改变之前的 Vnode 做 diff,对内容做改动之后,就会更新到 我们真正的 DOM

Vue 中 v-on 可以绑定多个方法吗?

<p v-on="{click:dbClick,mousemove:MouseClick}"></p>

Vue计算属性和 Watch 的区别

methods(方法):

只要进行调用就会执行,不管依赖的值有没有改变。无缓存。

computed(计算属性):

监听其所有依赖的变化,如果有变化会执行,没有变化不执行。有缓存,不用每次重新算。不支持异步。

详解:在vue的 模板内({{}})是可以写一些简单的js表达式的 ,很便利。但是如果在页面中使用大量或是复杂的表达式去处理数据,对页面的维护会有很大的影响。这个时候就需要用到computed 计算属性来处理复杂的逻辑运算。

1.优点:

在数据未发生变化时,优先读取缓存。computed 计算属性只有在相关的数据发生变化时才会改变要计算的属性,当相关数据没有变化是,它会读取缓存。而不必想 motheds方法 和 watch 方法是否每次都去执行函数。

2.setter 和 getter方法:(注意在vue中书写时用set 和 get)

setter 方法在设置值是触发。

getter 方法在获取值时触发。

watch(侦听属性):

观察某一个变量,发生变化会执行。支持异步。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性。

一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。

小结:

1.主动调用的方法写在methods里,依据某些变量的更新进行某种操作用computed或者watch。

2.computed和watch:如果要异步,只能用watch。如果是计算某个值推荐用computed,比如购物车全选单选功能,购物车计算总价小计功能。

vue的常用修饰符

Vue 提供了一些修饰符,用于在事件处理、指令和表单输入时改变行为或增强功能。以下是一些常用的 Vue 修饰符:

事件修饰符(Event Modifiers):

  1. .stop:阻止事件继续传播。
  2. .prevent:阻止默认事件的触发。
  3. .capture:事件将以捕获模式触发。
  4. .self:只有事件是由当前元素本身触发时才触发事件处理函数。
  5. .once:事件将只会触发一次。
  6. .passive:告诉浏览器你不想阻止事件的默认行为。

按键修饰符(Key Modifiers):

  1. .enter:只在 Enter 键按下时触发事件。
  2. .tab:只在 Tab 键按下时触发事件。
  3. .delete / .backspace:只在删除键按下时触发事件。
  4. .esc:只在 Esc 键按下时触发事件。
  5. .space:只在 Space 键按下时触发事件。
  6. .up / .down / .left / .right:只在相应的箭头键按下时触发事件。

表单修饰符(Form Modifiers):

  1. .lazy:将输入事件改为 change 事件。
  2. .trim:自动过滤用户输入的首尾空白字符。

修饰符的使用方法:













修饰符可以帮助你更好地控制事件、输入和指令的行为,使代码更加清晰、简洁并且更符合预期。

Vue中key的作用?不加会怎么样?

vue中列表循环需加:key=“唯一标识” 唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM

Vue 的 computed 的原理

Vue 的 computed 属性是用于在 Vue 实例中计算派生数据的一种方式。它的原理涉及到 Vue 的响应式系统。

响应式系统

当 Vue 实例被创建时,Vue 会遍历其数据对象的所有属性,并使用 Object.defineProperty(或者在不支持 Object.defineProperty 的环境中采用类似方法)将这些属性转换为 getter/setter。这样一来,当属性被访问或修改时,Vue 将能够捕获到这些操作,并通知相关的界面进行响应更新。

Computed 的原理

computed 属性允许你声明式地定义一个计算属性。当依赖的响应式数据发生变化时,计算属性会自动重新计算其值,但仅当相关依赖发生变化时才会触发。

  1. 依赖追踪:Vue 在计算属性的 getter 函数中捕获所依赖的响应式数据。当这些数据发生变化时,Vue 知道需要重新计算计算属性的值。
  2. 缓存:计算属性是基于它们的响应式依赖进行缓存的。只有在相关依赖发生改变时,计算属性才会重新计算值。这意味着在多次访问计算属性时,只有在依赖值变化时才会触发实际的计算。
// 示例
new Vue({
  data: {
    message: 'Hello'
  },
  computed: {
    reversedMessage: function () {
      // 计算属性依赖于 this.message
      return this.message.split('').reverse().join('');
    }
  }
});

在这个示例中,reversedMessage 计算属性依赖于 message 数据属性。只要 message 发生变化,reversedMessage 会自动更新为 message 的反转字符串。

这种方式使得开发者能够以声明式的方式编写复杂的数据操作,而不必手动追踪依赖或手动触发更新。Vue 的响应式系统会自动处理这些计算属性的依赖追踪和更新。

虚拟 dom 为什么会提高性能?

虚拟DOM其实就是一个JavaScript对象。通过这个JavaScript对象来描述真实DOM,真实DOM的操作,一般都会对某块元素的整体重新渲染,采用虚拟DOM的话,当数据变化的时候,只需要局部刷新变化的位置就好了 ,

虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能

具体实现步骤如下

  • JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
  • 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
  • 把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新

介绍下vue父子组件生命周期的执行顺序

Vue.js 中父子组件的生命周期钩子函数执行顺序如下:

父组件生命周期钩子执行顺序:

  1. beforeCreate:父组件实例被创建,但是数据观测和事件/生命周期事件的配置之前执行。
  2. created:在实例创建完成后被立即调用。可以访问数据、方法、计算属性等。但是此时还未挂载 DOM。
  3. beforeMount:在挂载开始之前被调用,相关的 render 函数首次被调用。
  4. mounted:在实例被挂载到 DOM 后调用。此时,组件已经渲染到页面上。

子组件生命周期钩子执行顺序:

  1. beforeCreate:子组件实例被创建,但是数据观测和事件/生命周期事件的配置之前执行。
  2. created:与父组件相同,在实例创建完成后被立即调用。
  3. beforeMount:与父组件相同,在挂载开始之前被调用,相关的 render 函数首次被调用。
  4. mounted:与父组件相同,在实例被挂载到 DOM 后调用。

父子组件的更新顺序:

  1. beforeUpdate:当数据更新时被调用,发生在虚拟 DOM 重新渲染和打补丁之前。
  2. updated:组件更新完成后被调用。

销毁顺序:

  1. beforeDestroy:在实例销毁之前调用。此时实例仍然完全可用。
  2. destroyed:在实例被销毁后调用。此时,所有的事件监听器和子实例都已被移除。

值得注意的是,父组件的生命周期钩子函数在其子组件之前执行。而在父子组件各自的生命周期中,创建(created)、挂载(mounted)、更新(updated)和销毁(destroyed)钩子函数的执行顺序是相同的。

介绍下vue单页面和多页面区别

单页应用 页面跳转---->js渲染 优点:页面切换快 缺点:首屏加载稍慢,seo差

多页应用 页面跳转---->返回html 优点:首屏时间快,seo效果好 缺点:页面切换慢

nextTick知道吗、实现的原理是什么?是宏任务还是微任务?

微任务

原理:

nextTick方法主要是使用了宏任务和微任务,定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空队列。

作用: nextTick用于下次Dom更新循环结束之后执行延迟回调,在修改数据之后使用nextTick用于下次Dom更新循环结束之后执行延迟回调,在修改数据之后使用nextTick用于下次Dom更新循环结束之后执行延迟回调,在修改数据之后使用nextTick,则可以在回调中获取更新后的DOM。

你做过哪些Vue的性能优化?

  • 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 的冗余代码

    Vue组件中的Data为什么是函数,根组件却是对象呢?

    综上可知,如果data是一个函数的话,这样每复用一次组件,就会返回一份新的data,类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果。

    所以说vue组件的data必须是函数。这都是因为js的特性带来的,跟vue本身设计无关。

Vue中的单项数据流

单向数据流指只能从一个方向来修改状态。

数据从父级组件传递给子组件,只能单向绑定。

子组件内部不能直接修改从父级传递过来的数据。

Vnode的缺点

VNode(虚拟节点)是 Vue 中用于描述 DOM 结构的对象。它是 Vue 在更新 DOM 时使用的一种抽象表示,但它也有一些缺点:

1. 内存消耗

VNode 的创建和管理需要占用一定的内存。在大型应用中,如果频繁地创建和销毁大量的 VNode 对象,可能会导致内存消耗较大。

2. 性能开销

尽管 VNode 的使用使得 Vue 能够更高效地进行虚拟 DOM 的比对和更新,但在极端情况下,频繁的虚拟 DOM 比对也可能导致一定的性能开销。特别是当页面中有大量动态节点、频繁的更新操作或者复杂的计算时,虚拟 DOM 的比对成本可能会增加。

3. 学习曲线

对于新手开发者来说,理解和使用 VNode 可能会增加学习曲线。这个抽象概念需要一定的时间和实践来熟悉和掌握。

4. 不利于某些特定场景

在某些场景下,例如性能要求非常高的小型应用或者静态内容展示页,使用虚拟 DOM 的成本可能会超过其带来的好处,这时直接操作原生 DOM 可能更为高效。

尽管 VNode 有其缺点,但在大多数情况下,它作为 Vue 的核心特性,为开发者提供了更好的开发体验和维护大型应用程序的能力。Vue 在内部会尽可能地优化和管理 VNode,以平衡性能和开发者的使用便利性。

jQuery写的页面,切换到vue的页面,有卡顿吗?

在从使用 jQuery 编写的页面切换到使用 Vue 编写的页面时,可能会出现一些不同。这些差异可能导致一些卡顿或性能方面的问题,但通常情况下,这些问题是可以解决或优化的。

可能的卡顿因素:

  1. 渲染方式不同:Vue 使用虚拟 DOM 进行更新,而 jQuery 直接操作实际 DOM。这意味着在切换到 Vue 页面时,Vue 首先需要进行渲染和建立虚拟 DOM,这个过程可能会产生一些初始的性能开销。
  2. 事件处理方式:Vue 采用了自己的事件处理机制,可能与之前基于 jQuery 的事件处理方式有所不同。如果之前的页面大量使用 jQuery 绑定事件,可能需要进行重新适配。
  3. 数据绑定和状态管理:Vue 使用数据驱动视图,采用响应式的数据绑定。如果之前的页面在更新数据时是通过手动更新 DOM,切换到 Vue 后可能需要重构以适应 Vue 的数据流程。

可能的解决方案:

  1. 渐进式迁移:不要一次性将整个页面都迁移到 Vue,而是采用渐进式迁移的方式,逐步替换和重构 jQuery 代码。
  2. 性能优化:Vue 提供了一些性能优化策略,比如合理使用 v-ifv-for,避免不必要的重新渲染,合理使用组件懒加载等。
  3. 组件化和模块化:利用 Vue 的组件化和模块化特性,将页面拆分成独立的组件,提高代码的可维护性和可测试性。
  4. 事件和数据迁移:逐步迁移事件绑定和数据处理,重新设计 Vue 组件以适应 Vue 的生命周期和数据管理。
  5. 性能监控和优化:使用工具(如 Vue Devtools、Chrome 开发者工具等)进行性能监控和分析,发现性能瓶颈并进行优化。

综上所述,从 jQuery 切换到 Vue 可能会遇到一些初始的性能问题,但通过逐步迁移、优化和重构代码,可以解决或减轻这些问题,最终得到更好的性能和开发体验。

用过beforeEach吗?

每次通过vue-router进行页面跳转,都会触发beforeEach这个钩子函数,这个回调函数共有三个参数,to,from,next这三个参数,to表示我要跳转的目标路由对应的参数,from表示来自那个路由,就是操作路由跳转之前的,即将离开的路由对应的参数,next是一个回调函数,一定要调用next方法来resolve这个钩子函数;

怎么修改Vuex中的状态?Vuex中有哪些方法

  • 通过this.$store.state.属性 的方法来访问状态
  • 通过this.$store.commit(‘mutation中的方法’) 来修改状态

diff复杂度原理及具体过程画图

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

Vue 使用虚拟 DOM(Virtual DOM)来提高性能,它是一种在内存中对 DOM 结构进行抽象表示的技术。

虚拟 DOM 的工作原理:

  1. 初始渲染
    • 当 Vue 组件的状态发生变化时,Vue 会首先构建一个虚拟 DOM 树,与实际的 DOM 树结构相对应。
    • 这个虚拟 DOM 树包含了所有需要渲染的元素及其对应的属性。
  2. 对比更新
    • 当状态发生变化时,Vue 不会立即操作实际 DOM,而是先通过新的状态生成一个新的虚拟 DOM 树。
    • 然后,Vue 会将新生成的虚拟 DOM 树与之前的虚拟 DOM 树进行对比,找出两者之间的差异(Diff 算法)。
  3. 最小化更新
    • Vue 将找到的差异(变更)进行最小化,只更新实际改变了的部分。
    • 这些变更会被应用到实际的 DOM 中,从而更新页面的显示。

虚拟 DOM 的优势:

  1. 性能提升:通过在内存中进行操作,减少了直接操作实际 DOM 所带来的性能消耗。
  2. 批量更新:Vue 能够批量处理对虚拟 DOM 的修改,然后一次性将变更应用到实际的 DOM 中,避免了多次更新实际 DOM 的开销。
  3. 跨平台:虚拟 DOM 的抽象性使得它可以在不同的平台上使用,不仅限于浏览器环境。

值得注意的是,虽然虚拟 DOM 可以提高性能,但在某些情况下,直接操作实际 DOM 可能更为高效,尤其是对于一些简单的页面或对性能要求极高的场景。Vue 在内部会尽可能地优化虚拟 DOM 的使用,以达到更好的性能表现。

为什么要设置key值,可以用index吗?为什么不能?

vue中列表循环需加:key=“唯一标识” 唯一标识可以是item里面id index等,因为vue组件高度复用增加Key可以标识组件的唯一性,为了更好地区别各个组件 key的作用主要是为了高效的更新虚拟DOM

axios怎么封装的

// 使用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 等),中间件通常按照定义的顺序依次执行,处理请求并在处理过程中执行相应的操作。以下是一般中间件生效的步骤:

1. 注册中间件

  • 开发者需要明确注册中间件并定义中间件的功能和操作。这些中间件按照顺序被添加到中间件链中。

2. 请求到达中间件链

  • 当请求到达系统时,它会被传递给中间件链的起始点。

3. 依次执行中间件

  • 中间件按照注册的顺序依次执行。
  • 每个中间件可以执行预定的操作,比如记录日志、修改请求或响应、验证等。

4. 控制权传递

  • 中间件可以决定是否将请求继续传递给下一个中间件,或者提前结束请求处理流程。

5. 响应返回

  • 当请求经过所有中间件处理完毕后,系统生成最终的响应并返回给客户端。

中间件机制的优势在于它提供了一种灵活的方式来对请求进行预处理、处理和后处理,并且可以在系统中简单地插入新的功能或操作,而不需要改变现有的代码结构。这种模式使得系统更易于扩展、维护和管理。

Vuex如何实现跨组价的数据监听

Vuex 是 Vue.js 官方提供的状态管理库,它可以在 Vue 应用程序中实现全局的状态管理。在 Vuex 中,可以通过 state(状态)来存储应用程序中的数据,而通过 mutations(变更)和 actions(动作)来修改和操作这些数据。

要实现跨组件的数据监听,Vuex 提供了一种集中式存储管理的方式,通过以下几个核心概念来实现:

1. State(状态)

在 Vuex 中,数据存储在状态树(state tree)中。任何组件都可以访问和读取状态中的数据,因此当状态发生变化时,所有依赖这些数据的组件都可以实时感知到这种变化。

2. Mutations(变更)

Mutations 是用来修改 Vuex 中状态的方法,每个 Mutation 都有一个字符串类型的事件类型(type)和一个回调函数,这个函数接收 state 作为第一个参数,允许对状态进行同步修改。

3. Actions(动作)

Actions 类似于 Mutations,但是它可以处理异步操作。Actions 提交 Mutations,而不是直接修改状态。它可以包含任何异步操作,并在完成后提交 Mutations。

4. Getters(获取器)

Getters 允许你在 Vuex 中对 state 进行计算操作,并且将计算后的结果缓存起来,类似于 Vue 中的计算属性。

通过这些核心概念,任何一个组件都可以通过提交 Mutations 或者调用 Actions 来改变 Vuex 中的状态。当状态发生变化时,所有依赖这些状态的组件都会得到更新,因为它们都是从同一个状态树中获取数据。

这种机制使得 Vuex 能够实现跨组件的数据监听,而且可以更好地管理应用程序的状态。任何一个组件都可以触发状态的变更,而所有依赖这些状态的组件都会自动更新。

defineProperty在数据劫持后是如何通知数据的更新和视图的更新的

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)。

Vue $forceUpdate的原理

1、作用:

迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。

2、内部原理:

Vue.prototype.$forceUpdate = function () {
    const vm: Component = this
    if (vm._watcher) {
        vm._watcher.update()
    }
}

实例需要重新渲染是在依赖发生变化的时候会通知watcher,然后通知watcher来调用update方法,就是这么简单。

Vue proxy的原理

主要通过Proxy对对象进行绑定监听处理,通过new Map对对象的属性操作进行处理,将要执行的函数匹配到存到对应的prop上面,通过每次的访问触发get方法,进行存方法的操作,通过修改触发set的方法,此时执行回调监听的函数,这样达到修改数据和视图的

Vue 响应式原理

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 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

Vue 生命周期通常使用哪些

常用的生命周期有,beforeCreate,created,beforeMount,mounted,beforeUpdate,updated,beforeDestroy,destroyed

如何封装一个通用组件

通用组件的封装就是对可复用组件的解耦和样式复用,为了解耦一般数据都是通过父组件传递过来,在子组件中进行数据处理,对于一些较为复杂的数据可能还需要做数据验证,为了避免高耦合,逻辑最好放在父组件中,通过自定义事件将数据回传,子组件只是一个承载体,这样既降低耦合,保证子组件中数据和逻辑不会混乱。如果同一组件需要适应不同需求时,我们需要配合slot来使用,可以通过具名插槽灵活地解决了不同场景同一组件不同配置的问题。

Vue路由实现的底层原理

在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的重新渲染。

Vue组件如何引入使用

1. 定义组件并抛出
2. import引入,并在component里面定义
3. 使用组件(注意首字母大写)

Vue路由传参,刷新后还有吗

通过params传参会出现参数丢失的情况,可以通过query的传参方式或者在路由匹配规则加入占位符即可以解决参数丢失的情况。

Vue和React区别

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的缺点

如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的,并且state中的值会伴随着浏览器的刷新而初始化,无缓存。

如果你是leader,做管理系统项目 Vue和React 怎么选择?

评估项目成员的水平,如果成员js基础较好、编码能力较强则选择React,否则Vue。
 评估系统的大小,如果想构建生态系统,则选择React,如果要求而快,简单和“能用就行",则选择Vue。
 评估系统运行环境,如果你想要一个同时适用于Web端和原生APP的框架,请选择React(RN)。

Object.defineProperty有什么缺点

1:无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;
2:只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。

请你说一下 Vue 中 create 和 mount 的区别

create为组件初始化阶段,在此阶段主要完成数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,此时还未生成真实的DOM,也就无法获取和操作DOM元素。而mount主要完成从虚拟DOM到真实DOM的转换挂载,此时html已经渲染出来了,所以可以直接操作dom节点。

Vue和React中diff算法区别

vue和react的diff算法,都是忽略跨级比较,只做同级比较。vue diff时调动patch函数,参数是vnode和oldVnode,分别代表新旧节点。

1.vue对比节点。当节点元素相同,但是classname不同,认为是不同类型的元素,删除重建,而react认为是同类型节点,只是修改节点属性。

2.vue的列表对比,采用的是两端到中间比对的方式,而react采用的是从左到右依次对比的方式。当一个集合只是把最后一个节点移到了第一个,react会把前面的节点依次移动,而vue只会把最后一个节点移到第一个。总体上,vue的方式比较高效。

Vue中如何实现子组件内的css样式名在项目中绝对唯一性

在style标签上加上scoped属性

Vue 路由守卫

vue-router 提供的导航守卫主要用来对路由的跳转进行监控,控制它的跳转或取消,路由守卫有全局的, 单个路由独享的, 或者组件级的。导航钩子有3个参数:

1、to:即将要进入的目标路由对象;
2、from:当前导航即将要离开的路由对象;
3、next :调用该方法后,才能进入下一个钩子函数(afterEach)。

用过插槽吗?用的是具名插槽还是匿名插槽

用过,都使用过。插槽相当于预留了一个位置,可以将我们书写在组件内的内容放入,写一个插槽就会将组件内的内容替换一次,两次则替换两次。为了自定义插槽的位置我们可以给插槽取名,它会根据插槽名来插入内容,一一对应。

Vuex是什么?怎么使用?描述使用它实现登录功能的流程?

Vuex是Vue.js的官方状态管理库,用于集中式管理Vue应用中的各种组件共享的状态。它通过一个全局的状态树来管理应用中的所有状态,并提供了一些方法用于状态的修改、监听和响应。

使用Vuex,你可以创建一个全局的 store 对象,其中包含了你希望保存的所有状态。这个 store 中的状态可以被任意组件访问和修改,但是状态的修改需要通过特定的方法(mutations)来实现,以确保状态的变更是可追踪和可控的。

实现登录功能的流程可以概括如下:

  1. 设置状态: 在 Vuex 的 store 中设置一个状态来表示用户登录状态,比如 isLoggedin,初始值为 false。

  2. 定义 mutations: 创建 mutations 方法来修改 isLoggedin 状态,包括登录和登出两种情况。比如:

    const store = new Vuex.Store({
      state: {
        isLoggedin: false
      },
      mutations: {
        login(state) {
          state.isLoggedin = true;
        },
        logout(state) {
          state.isLoggedin = false;
        }
      }
    });
    
  3. 调用 mutations: 在登录页面或者组件中,当用户完成登录时,触发对应的 mutations 方法来改变登录状态。

    methods: {
      login() {
        // 在这里进行用户验证,如果验证通过则调用登录 mutations
        this.$store.commit('login');
      }
    }
    
  4. 获取状态: 在需要使用登录状态的地方,可以通过 Vuex 的 getter 方法来获取 isLoggedin 状态。

    computed: {
      isLoggedIn() {
        return this.$store.state.isLoggedin;
      }
    }
    

这样,通过以上流程,在应用中你可以方便地使用 Vuex 来管理登录状态。当用户登录时,调用登录 mutations 来修改状态,当用户登出时,调用登出 mutations 来改变状态。并且在整个应用中都可以随时获取和监控这个登录状态。

Vue如何实现单页面应用

通常的url 地址由以下内容构成:协议名 域名 端口号 路径 参数 哈希值,当哈希值改变,页面不会发生跳转,单页面应用就是利用了这一点,给window注册onhashchange事件,当哈希值改变时通过location.hash就能获得相应的哈希值,然后就能跳到相应的页面。

Vue中 keep-alive 的作用

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。一旦使用keepalive包裹组件,此时mouted,created等钩子函数只会在第一次进入组件时调用,当再次切换回来时将不会调用。此时如果我们还想在每次切换时做一些事情,就需要用到另外的周期函数,actived和deactived,这两个钩子函数只有被keepalive包裹后才会调用。

Vue 等单页面应用的优缺点

// 优点
1单页应用的内容的改变不需要重新加载整个页面,web应用更具响应性和更令人着迷。

2、单页应用没有页面之间的切换,就不会出现“白屏现象”,也不会出现假死并有“闪烁”现象

3、单页应用相对服务器压力小,服务器只用出数据就可以,不用管展示逻辑和页面合成,吞吐能力会提高几倍。

4、良好的前后端分离。后端不再负责模板渲染、输出页面工作,后端API通用化,即同一套后端程序代码,不用修改就可以用于Web界面、手机、平板等多种客户端。

// 缺点
1、首次加载耗时比较多。

2、SEO问题,不利于百度,360等搜索引擎收录。

3、容易造成Css命名冲突。

4、前进、后退、地址栏、书签等,都需要程序进行管理,页面的复杂度很高,需要一定的技能水平和开发成本高。

Vue 中怎么自定义过滤器

通过filter来定义过滤器,过滤器分为全局和局部过滤器,过滤器的主体为一个普通的函数,来对数据进行处理,可以传递参数。当有局部和全局两个名称相同的过滤器时候,会以就近原则进行调用,即:局部过滤器优先于全局过滤器被调用。

Vue 中怎么自定义指令

通过directive来自定义指令,自定义指令分为全局指令和局部指令,自定义指令也有几个的钩子函数,常用的有bind和update,当 bind 和 update 时触发相同行为,而不关心其它的钩子时可以简写。一个表达式可以使用多个过滤器。过滤器之间需要用管道符“|”隔开。其执行顺序从左往右。

对比 jQuery,Vue 有什么不同

jQuery 专注视图层,通过直接操作 DOM 去实现页面的一些逻辑渲染;Vue 专注于数据层,通过数据的双向绑定,最终表现在 DOM 层面,减少了 DOM 操作。Vue 使用了组件化思想,使得项目子集职责清晰,提高了开发效率,方便重复利用,便于协同开发

$route 和 $router 的区别

$route用来获取路由的信息的,它是路由信息的一个对象,里面包含路由的一些基本信息,包括name、meta、path、hash、query、params、fullPath、matched、redirectedFrom等。而$router主要是用来操作路由的,它是VueRouter的实例,包含了一些路由的跳转方法,钩子函数等

vue-router 路由实现原理

Vue Router 是 Vue.js 官方的路由管理器,它基于 Vue.js 实现了单页面应用(SPA)的路由功能。其实现原理主要基于 Vue.js 提供的组件化开发思想和观察者模式,下面是它的一些核心实现原理:

  1. 路由映射: Vue Router 通过配置路由表(Route Table)将 URL 映射到对应的组件上。这些映射关系以及路由参数等信息被存储在路由器实例中,用于匹配 URL。
  2. 组件化思想: Vue Router 利用 Vue.js 的组件化思想,将每个路由视图都看作是一个组件。这些路由视图(组件)可以被声明式地嵌套在页面中,当 URL 发生变化时,Vue Router 会根据路由配置来渲染对应的组件。
  3. 动态路由匹配: Vue Router 支持动态路由匹配,可以通过参数化的 URL 来实现动态路由。例如,/user/:id 这样的路由可以匹配 /user/123/user/456 等不同的 URL,并且根据不同的 ID 参数渲染对应的组件。
  4. 路由守卫: Vue Router 提供了导航守卫机制,允许在路由变化前后触发特定的逻辑。这些守卫包括全局前置守卫、路由独享的守卫、组件内的守卫等,可以控制导航行为,进行权限验证、页面跳转等操作。
  5. URL 操作: Vue Router 使用 HTML5 History API(或 hash 模式)来实现路由导航。它可以监听浏览器 URL 的变化,并且在 URL 变化时更新应用视图。
  6. 路由状态管理: Vue Router 也支持路由状态的管理,例如通过路由参数传递信息、路由元信息等,以及提供了动态路由匹配的解决方案。

总体来说,Vue Router 的路由实现原理是基于 Vue.js 的组件化和响应式特性,在浏览器端利用 History API(或 hash 模式)来监听 URL 的变化,匹配路由并渲染对应的组件,同时提供了一系列的导航守卫来控制导航行为和实现额外逻辑。

v-model和vuex有冲突吗?

严格来说,v-model和vuex并不直接冲突。v-model是Vue提供的一种双向数据绑定指令,用于将视图和模型的数据进行双向绑定。vuex是Vue提供的状态管理库,用于在应用程序中集中管理状态。

但是,在某些情况下,v-model和vuex可能会引起一些问题。例如,如果将v-model直接绑定到vuex中的状态,则可能会导致视图直接修改了vuex中的状态,而绕过了vuex的mutation。这可能会破坏状态的可预测性和一致性。

为了避免这种冲突,可以采取以下措施:

  • 在组件中使用计算属性代替v-model,并且在计算属性中获取和修改vuex中的状态。
  • 使用vuex提供的**store.commit()**方法来修改vuex中的状态。

为什么Vuex的mutation不能做异步操作

Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,否则无法被devtools所监测。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。

Vuex可以直接修改state的值吗?

可以直接修改,但是极其不推荐,state的修改必须在mutation来修改,否则无法被devtool所监测,无法监测数据的来源,无法保存状态快照,也就无法实现时间漫游/回滚之类的操作。

什么情况下使用 Vuex

如果应用够简单,最好不要使用 Vuex,一个简单的 store 模式即可,需要构建一个中大型单页应用时,使用Vuex能更好地在组件外部管理状态

Composition API 的出现带来哪些新的开发体验,为啥需要这个?

1:在Compostion API 中时根据逻辑相关组织代码的,提高可读性和可维护性,类似于react的hook写法。
2:更好的重用逻辑代码,在Options API中通过MIxins重用逻辑代码,容易发生命名冲突且关系不清。
3:解决在生命周期函数经常包含不相关的逻辑,但又不得不把相关逻辑分离到了几个不同方法中的问题,如在mounted中设置定时器,但需要在destroyed中来清除定时器,将同一功能的代码拆分到不同的位置,造成后期代码维护的困难。

你知道Vue响应式数据原理吗?Proxy 与 Object.defineProperty 优劣对比?

// 响应式原理

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(垫片)来弥补

说一下 r o o t , root, rootparent,$refs

$root,和$parent都能访问父组件的属性和方法,区别在于如果存在多级子组件,通过parent 访问得到的是它最近一级的父组件,通过root 访问得到的是根父组件。通过在子组件标签定义 ref 属性,在父组件中可以使用$refs 访问子组件实例。

对MVC,MVP,MVVM的理解

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即可同时渲染多个组件。

vue-router 3.1.0 新增的v-slot属性怎么用?

router-link 通过一个作用域插槽暴露底层的定制能力。这是一个更高阶的 API,主要面向库作者,但也可以为开发者提供便利,多数情况用在一个类似 NavLink 这样的自定义组件里。

在使用 v-slot API 时,需要向 router-link 传入一个单独的子元素。否则 router-link 将会把子元素包裹在一个 span 元素内。

如何再Vue的单文件组件里的样式定义全局CSS?

在style标签上不加上scoped的属性,默认为全局css样式

你可能感兴趣的:(vue.js,前端,javascript)