vue2、3面试题

SPA(single-page application)优缺点

原文SPA的优缺点
优点
1、良好的交互体验(不用重新加载页面)
2、前后端分离,效率更高
3、减轻服务器压力(只需返回数据)
4、共用一套后端代码,只需改样式适配pc、移动
缺点
1、不利于SEO
2、初次加载耗时

SPA首屏优化

原文面试官:SPA(单页应用)首屏加载速度慢怎么解决?
1、动态加载路由(减小入口文件体积)component: () => import(‘./components/xxx.vue’)
2、静态资源本地缓存(http缓存)
3、UI框架按需加载
4、图片资源压缩(在线icon、精灵图)
5、开启GZip
6、SSR(服务端渲染)Nuxt.js

对vue的理解

1、MVVM架构,渐进式架构
2、组件化(低耦合,维护性高)
3、指令系统(v-if、v-model、v-for等)

vue2数据双向绑定原理

原文vue双向绑定原理及实现
1、vue双向绑定是通过数据劫持(Object.defineProperty)结合发布订阅模式的当时来实现的,也就是数据和视图同步。
2、Observer:核心就是Object.defineProperty()方法;
用Object.defineProperty()重写对象属性的get和set方法。来监听model数据的改变,通知Watcher
3、Compile:实现指令解析,订阅view数据变化,绑定好更新函数
3、每个组件实例都对应一个watcher实例,Watcher一方面接收Observer通过dep传递过来的数据变化,一方面通知Compile进行view update

vue3数据双向绑定原理

原文面试官:Vue3.0里为什么要用 Proxy API 替代 defineProperty API ?
1、Object.defineProperty() 是通过遍历对象的属性进行数据劫持的,导致新增或删除对象属性时无法监听到对象的改变,只能用其他方法进行辅助( $ set、$ delete、重写数组方法)
2、ES6中的Proxy(12种拦截方法)可以直接劫持整个对象,并返回一个新对象,proxy可以监听数组的变化

vue3的性能提升

原文面试官:Vue3.0 性能提升主要是通过哪几方面体现的?
1、静态提升(diff算法优化)
vue3中对不参与更新的元素会做静态提升(给静态元素标记-1),只被创建一次,在渲染时复用;
2、响应式系统(proxy)
3、源码体积较小(移除某些API、tree-shaking机制打包时没用到的ref、reactive等不打包)
4、SSR优化(大量静态内容时会用createStaticVNode方法生成静态node,不要用创建对象再根据对象渲染)

vue2、vue3的区别

原文vue3.0 到底提升了什么?(值得收藏)
1、vue3完全支持typescript,提高项目的可维护性
2、Fragment:模板更简单。
vue3用到了一个虚拟的父级,
vue2只能有一个父级节点
3、 vue2用 new Vue(); vue3用createApp();
多个实例时,vue2中的vue实例共享一个Vue构造函数,无法做到完全隔离
createApp方法返回一个提供上下文的应用实例,应用实例挂载的整个组件共享同一个上下文

vue3的静态标记和静态提升(diff优化)

原文Vue3.0学习 - 第一节,diff算法、静态标记
静态标记:在vue3的render方法里会对动态数据做标记,更新虚拟DOM时只对静态标记对象进行比较

<div>{{msg}}</div>
_createVNode("p", null, _toDisplayString(_ctx.kkl), 1 /* TEXT */),

静态提升:在render方法外面将静态内容进行提取,后面可以直接复用。

vue2中template标签只能有一个节点

vue实例会将template标签下的节点作为入口(多个节点时无法确认入口)

vue2中 $set用法

this.$set(obj, key, value)

$nextTick()

等dom更新后再执行回调(v-if显示元素,等dom更新后再执行获取元素)

路由两种传参方式params、query

params: 和name匹配(刷新无数据)

this.$router.push({
   name: '详情',  //匹配route里面配置的name
   params: {
     detailFrom: 1 //传递的参数名和值
   }
 });
this.$route.params.detailFrom;

query: 和path匹配(对象要转json再传递)

 this.$router.push({
    path: '/order/chargedtail',
    query: {
      //将对象转换为json传递
      detailFrom: JSON.stringify(row)
    }
  });
JSON.parse(this.$route.query.detailFrom);

动态组件Component

<component :is="currentView"></component>
this.currentView =  {template: `<div>我是存档页</div>`}

keep-alive

组件用keep-alive时,组件存在activated()和deactivated两个钩子函数
keep-alive会将组件保留在内存中,以便下次使用
路由中配置

{
   path:'ratings',
   name: 'ratings',
   component: Ratings,
   meta: {
 	keepAlive: true  // 需要缓存
  }
 }

页面中标签配置


<keep-alive>
 <component></component>
</keep-alive>

slot(插槽)

原文vue slot及slot-scope传值的理解

父组件:
<template>
    <div class="father">
        <h3>这里是父组件</h3>
        <child>
        	具名插槽
            <div v-slot:header='scope' >
              <span>{{scope.userName}}</span>
            </div>
            匿名插槽
             <div v-slot='scope' >
              <span>{{scope.userName}}</span>
            </div>
        </child>
    </div>
</template>
子组件
<template>
     // 具名插槽
     <slot name="header"></slot>
     // 匿名插槽
      <slot></slot>
<template>

vue3的作用域插槽写法

<template #default="scope">
  <span :title="scope.userName" > {{scope.userName}} </span>
</template>

computed和watch

computed: 计算结果并返回,只有当被计算的值发生改变时会触发;(日期变化,格式化返回)
每一个计算属性都会被缓存起来,当计算属性依赖的属性变更是重新计算当前技术属性的值。
watch:监听某个值的变化并做对应操作。(监听boolean显隐元素)
immediate: true //初始化执行一遍
deep:ture //监听对象

Diff算法

原文简单粗暴的React Diff算法将传统O(n^3)Diff算法的时间复杂度降为O(n)
老版算法0(n^3):
更新前的树,与更新后的树 遍历进行比较 O(n^2)
再进行树的编辑(插入、替换、删除) O(n^1)
vue:
认为不同类型的元素,不同层级的元素,大概率内容不会重复,就直接删除,添加新节点 O(n)

vue自定义过滤器

用法

<div>{{date | formatDate}}</div>
<div :id = "id | formatId"></div>

自定义写法:

//组件内部过滤器
//与methods同级
filters: {
  formatDate: function(value) {
    let date = '2022-'+value+'-02'
    return date;
  }
}
//全局过滤器
//在main.ts中写
Vue.filter("upperCase", function(value) {
  if (!value) return "";
  value = value.toString();
  return value.toUpperCase(); // 小写转为大写
});

vue自定义指令

局部注册

//与data同级
directives: {
	focus:{
		inserted: function(el) {
			el.focus();
		}
	}
}

全局注册

Vue.directive('focus',{
		inserted: function(el){
			el.focus();
		}
	}
)

vue2生命周期钩子函数

beforeCreate:不能用this调用data中的数据
created:调接口
beforeMount
mounted
beforeUpdate
updated
( activated deactivated //keep-alive缓存的组件)
beforeDestroy
destroyed
errorCaptured:报错处理(2.5.0新增)

vue3生命周期钩子函数

setup() : 开始创建组件之前,在 beforeCreate 和 created 之前执行,创建的是 data 和 method
onBeforeMount() : 组件挂载到节点上之前执行的函数;
onMounted() : 组件挂载完成后执行的函数;
onBeforeUpdate(): 组件更新之前执行的函数;
onUpdated(): 组件更新完成之后执行的函数;
onBeforeUnmount(): 组件卸载之前执行的函数;
onUnmounted(): 组件卸载完成后执行的函数;
onActivated(): 被包含在 中的组件,会多出两个生命周期钩子函数,被激活时执行;
onDeactivated(): 比如从 A 组件,切换到 B 组件,A 组件消失时执行;
onErrorCaptured(): 当捕获一个来自子孙组件的异常时激活钩子函数。

父子组件钩子函数执行顺序

子组件在父组件挂载(mounted)前加载完成
father:beforecreated、created、beforemount、
son:beforecreated、created、beforemount、mounted
father: mounted、xxxx

data需要return

防止数据污染
一个组件的data选项必须是一个函数,因此每个实例可以维护一份被返回对象的独立拷贝。

vuex(全局状态管理)

原文Vuex的全面用法总结
1.state – 存放状态 store.state.xxx
2.getters – state的计算属性 store.getters.xxx
3.mutations – 更改状态的逻辑,同步操作 store.commit(’ funName ‘, param)
4.actions – 提交mutation,异步操作 store.dispath(’ funName ',param)
5.mudules – 将store模块化 modules: { a : moduleA, b : moduleB }
可以直接this.$store.state.xxx = xxx
但是不建议,

vuex直接修改state,异步直接用mutation修改

vuex是单向数据流,只能通过mutation进行修改。
如果直接修改不利于对state里的数据状态的跟踪(vue调试工具),
如果是异步的修改,在异步执行完之前别的地方修改,程序就会存在问题

vuex数据状态持久化

原文Vuex数据状态持久化
1、手动存储
state从storage里拿,mutations修改时改变storage里的值
2、利用vuex-persistedstate(原理也是利用storage存储)

vue的两种路由模式( hash 、history)

原文vue-router的两种模式及区别
hash:地址栏 URL 中携带 # 符号,但不包括在请求地址中;
history:利用了HTML5 History Interface中新增的pushState()和replaceState()方法;
history.go(-1);//后退一次
history.back(); //后退一次
history.forward(); //前进

路由导航守卫

1、全局守卫
前置守卫 router.beforeEach((to,from,next)=>{})
后置守卫 router.afterEach((to,from)=>{})
在main.ts中设置全局守卫,判断是否登录(next() 或 next(‘/login’))
2、组件内守卫(vm是应为data数据未加载完成)

beforeRouteEnter:(to,from,next)=>{
    next(vm=>{
        alert("hello" + vm.name);
    })
}
beforeRouteLeave:(to,from,next)=>{}

3、路由独享守卫
beforeEnter:(to,from,next)=>{},用法与全局守卫一致。只是,将其写在路由(router)文件里,只在这个路由下起作用。

vue2兄弟传值

创建busEvent.js

import Vue from 'vue'
let VueEvent = new Vue()
export default VueEvent

发送数据

 import bus from '../assets/busEvevt'
 bus.$emit('infoMsg',this.info)

接收数据

 bus.$on('infoMsg',(msg) => {
      this.mssage = msg
    })

vue3兄弟传值

npm install mitt,
main.js挂载到全局
app.config.globalProperties.$mitt = new mitt();
发送数据

proxy.$mitt.emit('mittFn', money.value)

接收数据

proxy.$mitt.on('mittFn', (res) => {
		console.log(res);
	})

vue3

代码层面主要是Composition -api
setup是组合式(Composition)编程的入口,setup接受两个值:props 和 context
props(父组件向子组件传值)
context 上下文(context.emit(‘xxx’);//调用父方法)

父组件 helloworld

<template>
<button @click="btnClick">调用子组件方法</button>
  <div class="hello">
    <helloSon @myFun="myFun" ref="helloSonRef" :toSon="fatherInfo"></helloSon>
  </div>
</template>

<script>
import { reactive } from '@vue/reactivity';
import helloSon from "./helloSon.vue"
export default {
  name: "HelloWorld",
  components: {helloSon},
  setup(props,context) {
    const fatherInfo = reactive({name:'son',age:'20'})
    const myFun = (val)=> {
      console.log(val);
    }
    //注册子ref
    let helloSonRef = ref<any>();
    //调用子方法
    const btnClick = () => {
    	helloSonRef.value.sonFun();
    }
    return {fatherInfo, myFun };
  },
  mounted() {},
};
</script>

子组件 helloSon

<template>
  <div>
    <button @click="getFather">son</button>
  </div>
</template>
<script>
export default {
  name: 'Vue3DemoHelloson',
  // props: ['name'],
  props: {
    toSon: {
      type: String,
      default: '12'
    }
  },
  setup(props,context) {
    console.log(props.toSon);
    //调用父方法
    const getFather = ()=> {
      context.emit("myFun",'hello')
    }
    const sonFun = () => {
    	console.log('我是子');
    }
    return { getFather ,sonFun };
  }
};
</script>

1、子组件重复使用时的Ref

//html
<child-component 
	:ref="(refItem) => childRef[index] = refItem"
	 v-for="(item,index) in 12">
</child-component>
//ts
const childRef = ref<any>([]);
return {childRef}

VUE3 watch 和watchEffect

watch
watch() 默认是懒侦听的,即仅在侦听源发生变化时才执行回调函数。
可监听:
一个函数,返回一个值
一个 ref
一个响应式对象
…或是由以上类型的值组成的数组

const state = reactive({ count: 0 })
watch(
  () => state.count,
  (count, prevCount) => {
    /* ... */
  }
)

watchEffect
立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行。

const count = ref(0)
watchEffect(() => console.log(count.value))
// -> 输出 0
count.value++
// -> 输出 1

副作用清除:watchEffect的参数是一个函数,这个函数的参数也是一个函数,可用来清除副作用

watchEffect(async (onCleanup) => {
  const { response, cancel } = doAsyncWork(id.value)
  // `cancel` 会在 `id` 更改时调用
  // 取消之前未完成的请求
  onCleanup(cancel)
  data.value = await response
})

watchEffect返回一个停止监听函数,用于停止监听

const stop = watchEffect(() => {})

// 当不再需要此侦听器时:
stop()

区别
options中的flush配置:sync(dom更新前)post(dom更新后)
watch()
    懒执行副作用;
    更加明确是应该由哪个状态触发侦听器重新执行;
    可以访问所侦听状态的前一个值和当前值。
watchEffect()
    它会自动追踪函数内部使用的响应式数据
    由于watchEffect的自动追踪机制,它通常用于处理副作用,例如执行异步操作或者更新UI。

    watch(
      () => state.count,
      (newVal, oldVal) => {
        console.log(`count 变化,新值为 ${newVal},旧值为 ${oldVal}`);
      }
    );
 
    // 使用 watchEffect 响应式地追踪 count
    watchEffect(() => {
      state.double = state.count * 2;
    });
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值