面试题vue

1.你对vue的理解

Vue是一套构建用户界面的渐进式框架,也可以理解为是一个视图模板引擎,强调的是状态到界面的映射。

2vue的响应式数据

vue中data中定义的数据会随着我们通过方法改变该数据的同时,页面上相关此数据的也会相应的刷新,实现响应式数据

3你所理解的MVVM

模型(Model): 数据保存—存放着各种数据,有的是固定写死的,大多数是从后端返回的数据

视图 (View):用户界面,也就是DOM

视图模型(View-Model):连接View和Model的桥梁,当数据变化时,ViewModel够监听到数据的变化(通过Data Bindings),自动更新视图,而当用户操作视图,ViewModel也能监听到视图的变化(通过DOM Listeners),然后通知数据做改动,这就实现了数据的双向绑定。

MVVM和 MVC 区别?
都是一种设计思想
MVC 后台用的多,MVC是Model-View-Controller的简写,即模型-视图-控制器。
MVC的目的就是将M和V 的代码分离
MVC是单向通信,也就是View和Model,必须通过Controller来承上启下
MVVM实现了View和Model的自动同步,当Model的属性改变时,不用再自己手动操作DOM元素,提高了页面渲染性能

4你对vue指令的理解

vue指令就是为了实现某种特定功能比如按条件渲染、绑定属性等,而封装的一些指令,这样使用者就能够快速实现一些功能。

v-if:根据其后表达式的 bool 值进行判断是否渲染该元素
v-else:紧跟着v-if搭配使用

v-show:其后表达式的 bool 值为 false 时,对渲染的出标签添加display:none;的样式

v-for:v-for的用法: v-for="(item, index) in arr" ,类似于数组的用法,一般用:key来搭配v-for,key的值必须保证不同。key的作用:帮助vue区分不同的元素,提升vue的渲染性能。
v-bind:(缩写为:):用于绑定某些属性,响应地更新 html 特性
v-on:(缩写为@):用于监听指定元素的 DOM 事件
v-once: v-once这个指令不需要任何表达式,它的作用就是定义它的元素或组件只会渲染一次,首次渲染后,不再随着数据的改变而重新渲染。也就是说使用v-once,那么该块都将被视为静态内容。

5怎么封装vue自定义指令

Vue 自定义指令有全局注册和局部注册两种方式。先来看看注册全局指令的方式,通过 Vue.directive( id, [definition] ) 方式注册全局指令。然后在入口文件中进行 Vue.use() 调用。
新建一个文件,命名为directive.js,定义全局自定义指令插件
import Vue from 'vue'
封装代码:名字可以随意命名,我们根据语义化命名为'focus'
在main.js中引入,并注册全局
指令定义函数的钩子函数(可选):
 
bind: 只调用一次,指令第一次绑定到元素时调用,可以定义一个在绑定时执行一次的初始化动作。
inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值。
componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
unbind: 只调用一次, 指令与元素解绑时调用。

6vue2和vue3区别,发生那些变化

vue2.x数据存放在data,方法在methods,通过this去调用。但是在vue3.x这些都没有了,所有的代码全部都在setup里面实现,你页面需要哪些方法,就要在当前页引入即可。
7computed的计算属性和watch监听之间的区别

作为计算属性 ,computed声明的计算属性不需要以()的方式进行获取;
computed中的函数必须用return返回;
计算属性具有缓存性,只在相关依赖的值发生变化时,才会重新求值,否则只会返回之前的缓存值,不会重新计算;
适用单一值依赖多值进行变化的场景;

watch不需要调用,会自动执行;
watch中的函数接收两个参数,newVal和oldVal,且并不一定需要return;
watch在监听值发生变化的时候就会执行回调函数,还有deep(监听对象属性的变化)和immediate(立即执行监听函数)选项;
适用多值依赖单一值进行变化的场景;

8 谈谈响应式数据

“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码。例如,视图渲染中使用了数据,数据改变后,视图也会自动更新。

9VNode是什么
本质上来说就是一个普通的JavaScript对象,是从VNode类实例化的对象。我们用这个JavaScript对象来描述一个真实DOM元素的话,那么该DOM元素上的所有属性在VNode这个对象上都存在对应得属性。

10VNode 是如何渲染的
创建一个组件,将vnode当作参数传入,利用createElement api 渲染,或者说在render里面渲染。可以创局部变量,当然也可以抽离出一个render-dom组件

11vue生命周期执行顺序
beforeCreate    在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。
created    实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。
beforeMount    在挂载开始之前被调用:相关的 render 函数首次被调用。
mounted    el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。
beforeUpdate    数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。
updated    由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。
activated    keep-alive 组件激活时调用。
deactivated    keep-alive 组件停用时调用。
beforeDestroy    实例销毁之前调用。在这一步,实例仍然完全可用。
destroyed    Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。
12 vuex核心属性
原文链接:https://blog.csdn.net/weixin_35955795/article/details/57412181

const store = new Vuex.Store({
    state: {
        count:0
    }
})
const app = new Vue({
    //..
    store,
    computed: {
        count: function(){
            return this.$store.state.count
        }
    },
    //..
})

每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。


state => 基本数据
getters => 从基本数据派生的数据
mutations => 提交更改数据的方法,同步!
actions => 像一个装饰器,包裹mutations,使之可以异步。
modules => 模块化Vuex
13vuex里面机制是怎么运行的
Vuex提供数据(state)来驱动视图(vue components),通过dispath派发actions,在其中可以做一些异步的操作,然后通过commit来提交mutations,最后mutations来更改state。
14 vuex action ,mutation和state运转方式

15  keep和alive组件实现原理

commit 用于调用mutation,当前模块和其他模块;
dispatch 用于调用action,当前模块和其他模块;
getters 用于获取当前模块getter;
state 用于获取当前模块state;
rootState 用于获取其它模块state;
rootGetters 用于获取其他模块getter;

16 如何在vue项目中应用我们的权限

//接口文档
axios.interceptors.request.use(config => {
    config.headers['token'] = cookie.get('token')
    return config
})
axios.interceptors.response.use(res=>{},{response}=>{
    if (response.data.code === 40099 || response.data.code === 40098) { //token过期或者错误
        router.push('/login')
    }
}) 


方案一

初始化即挂载全部路由,并且在路由上标记相应的权限信息,每次路由跳转前做校验

const routerMap = [
  {
    path: '/permission',
    component: Layout,
    redirect: '/permission/index',
    alwaysShow: true, // will always show the root menu
    meta: {
      title: 'permission',
      icon: 'lock',
      roles: ['admin', 'editor'] // you can set roles in root nav
    },
    children: [{
      path: 'page',
      component: () => import('@/views/permission/page'),
      name: 'pagePermission',
      meta: {
        title: 'pagePermission',
        roles: ['admin'] // or you can only set roles in sub nav
      }
    }, {
      path: 'directive',
      component: () => import('@/views/permission/directive'),
      name: 'directivePermission',
      meta: {
        title: 'directivePermission'
        // if do not set roles, means: this page does not require permission
      }
    }]
  }]

这种方式存在以下四种缺点:

  • 加载所有的路由,如果路由很多,而用户并不是所有的路由都有权限访问,对性能会有影响。

  • 全局路由守卫里,每次路由跳转都要做权限判断。

  • 菜单信息写死在前端,要改个显示文字或权限信息,需要重新编译

  • 菜单跟路由耦合在一起,定义路由的时候还有添加菜单显示标题,图标之类的信息,而且路由不一定作为菜单显示,还要多加字段进行标识

方案二

初始化的时候先挂载不需要权限控制的路由,比如登录页,404等错误页。如果用户通过URL进行强制访问,则会直接进入404,相当于从源头上做了控制

登录后,获取用户的权限信息,然后筛选有权限访问的路由,在全局路由守卫里进行调用addRoutes添加路由

 View Code

按需挂载,路由就需要知道用户的路由权限,也就是在用户登录进来的时候就要知道当前用户拥有哪些路由权限

这种方式也存在了以下的缺点:

  • 全局路由守卫里,每次路由跳转都要做判断
  • 菜单信息写死在前端,要改个显示文字或权限信息,需要重新编译
  • 菜单跟路由耦合在一起,定义路由的时候还有添加菜单显示标题,图标之类的信息,而且路由不一定作为菜单显示,还要多加字段进行标识

 

菜单权限
菜单权限可以理解成将页面与理由进行解耦

方案
菜单与路由分离,菜单由后端返回

前端定义路由信息

{
    name: "login",
    path: "/login",
    component: () => import("@/pages/Login.vue")
}

name字段都不为空,需要根据此字段与后端返回菜单做关联,后端返回的菜单信息中必须要有name对应的字段,并且做唯一性校验

前端统一定义路由组件

const Home = () => import("../pages/Home.vue");
const UserInfo = () => import("../pages/UserInfo.vue");
export default {
    home: Home,
    userInfo: UserInfo
};

后端路由组件返回以下格式

[
    {
        name: "home",
        path: "/",
        component: "home"
    },
    {
        name: "home",
        path: "/userinfo",
        component: "userInfo"
    }
]

按钮权限

方案一

按钮权限也可以用v-if判断

但是如果页面过多,每个页面页面都要获取用户权限role和路由表里的meta.btnPermissions,然后再做判断

方案二

通过自定义指令进行按钮权限的判断

首先配置路由

{
    path: '/permission',
    component: Layout,
    name: '权限测试',
    meta: {
        btnPermissions: ['admin', 'supper', 'normal']
    },
    //页面需要的权限
    children: [{
        path: 'supper',
        component: _import('system/supper'),
        name: '权限测试页',
        meta: {
            btnPermissions: ['admin', 'supper']
        } //页面需要的权限
    },
    {
        path: 'normal',
        component: _import('system/normal'),
        name: '权限测试页',
        meta: {
            btnPermissions: ['admin']
        } //页面需要的权限
    }]
}

自定义权限鉴定指令

import Vue from 'vue'
/**权限指令**/
const has = Vue.directive('has', {
    bind: function (el, binding, vnode) {
        // 获取页面按钮权限
        let btnPermissionsArr = [];
        if(binding.value){
            // 如果指令传值,获取指令参数,根据指令参数和当前登录人按钮权限做比较。
            btnPermissionsArr = Array.of(binding.value);
        }else{
            // 否则获取路由中的参数,根据路由的btnPermissionsArr和当前登录人按钮权限做比较。
            btnPermissionsArr = vnode.context.$route.meta.btnPermissions;
        }
        if (!Vue.prototype.$_has(btnPermissionsArr)) {
            el.parentNode.removeChild(el);
        }
    }
});
// 权限检查方法
Vue.prototype.$_has = function (value) {
    let isExist = false;
    // 获取用户按钮权限
    let btnPermissionsStr = sessionStorage.getItem("btnPermissions");
    if (btnPermissionsStr == undefined || btnPermissionsStr == null) {
        return false;
    }
    if (value.indexOf(btnPermissionsStr) > -1) {
        isExist = true;
    }
    return isExist;
};
export {has}

在使用的按钮中只需要引用v-has指令

<el-button @click='editClick' type="primary" v-has>编辑</el-button> 

17 vue如何做导航守卫

import Vue from 'vue';
import Router from 'vue-router';
import LoginPage from '@/pages/login';
import HomePage from '@/pages/home';
import GoodsListPage from '@/pages/good-list';
import GoodsDetailPage from '@/pages/good-detail';
import CartPage from '@/pages/cart';
import ProfilePage from '@/pages/profile';
Vue.use(Router)
const router = new Router({
 routes: [
  {
   path: '/', // 默认进入路由
   redirect: '/home'  //重定向
  },
  {
   path: '/login',
   name: 'login',
   component: LoginPage
  },
  {
   path: '/home',
   name: 'home',
   component: HomePage
  },
  {
   path: '/good-list',
   name: 'good-list',
   component: GoodsListPage
  },
  {
   path: '/good-detail',
   name: 'good-detail',
   component: GoodsDetailPage
  },
  {
   path: '/cart',
   name: 'cart',
   component: CartPage
  },
  {
   path: '/profile',
   name: 'profile',
   component: ProfilePage
  },
  {
   path: '**',  // 错误路由
   redirect: '/home'  //重定向
  },
 ]
});
 
// 全局路由守卫
router.beforeEach((to, from, next) => {
 console.log('navigation-guards');
 // to: Route: 即将要进入的目标 路由对象
 // from: Route: 当前导航正要离开的路由
 // next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
 
 const nextRoute = ['home', 'good-list', 'good-detail', 'cart', 'profile'];
 let isLogin = global.isLogin; // 是否登录
 // 未登录状态;当路由到nextRoute指定页时,跳转至login
 if (nextRoute.indexOf(to.name) >= 0) { 
  if (!isLogin) {
   console.log('what fuck');
   router.push({ name: 'login' })
  }
 }
 // 已登录状态;当路由到login时,跳转至home 
 if (to.name === 'login') {
  if (isLogin) {
   router.push({ name: 'home' });
  }
 }
 next();
});
 
export default router;

判断登录状态的“路由集合”,当跳转至集合中的路由时,如果“未登录状态”,则跳转到登录页面LoginPage;
2.当直接进入登录页面LoginPage时,如果“已登录状态”,则跳转到首页HomePage;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值