vue相关

58 阅读11分钟

直接给一个数组项赋值,Vue 能检测到变化吗?

不能,用索引直接设置一个数组项时 或者 当你修改数组的长度时,Vue 不能检测到数组的变动。
也就是:
1、vm.items[index] = newValue 
2、vm.items.length = newLength
​
为了解决 1:
// Vue.set
Vue.set(vm.items, index, newValue)
​
// vm.$set,Vue.set的一个别名
vm.$set(vm.items, index, newValue)
​
// Array.prototype.splice
vm.items.splice(index, 1, newValue)
​
为了解决 2:
// Array.prototype.splice 只能将数组长度变小,不能变大。
vm.items.splice(newLength)
​
原因:
动态添加的数组项不能被 Object.defineProperty 劫持生成 getter, setter,因此无法产生响应。
​
Vue 能检测到数组变动的方法:
pop, push, shifut, unshift, splice, sort, reverse 做了重写,这些方法可以改变数组。

怎样理解 Vue 的单向数据流?

答: Vue 单向数据流指的是父组件通过 v-bind 指令将属性传递给子组件,数据流向是单向的,只能由父组件-->子组件,如果 想要修改父组件传递的属性时,需要子组件 emit 派发一个自定义事件,由父组件修改。父组件修改完属性后,会发生更新,子组件所有的 prop 都会刷新为最新的值。

说一说 vue 钩子函数

钩子函数:vue 实例创建和销毁过程中自动执行的函数,整个过程称为生命周期。

钩子函数按照组件生命周期的过程分为,挂载阶段 => 更新阶段 => 销毁阶段。

每个阶段对应的钩子函数:

  • 挂载阶段:beforeCreate、created、beforeMount、mounted。
  • 更新阶段:beforeUpdate、updated
  • 销毁阶段:beforeDestroy、destroyed。

如果使用了 keep-alive 组件,又多了:activated、deactivated。属性:include、exclude、max(最大缓存数)

生命周期描述
beforeCreate实例被创建之初,组件的属性生效之前。
created实例创建后,可访问 this、props、data、computed、watch、methods 上的属性和方法,未挂载到 DOM,不能访问 ref、$el,常用于发起网络请求,需要操作 dom 时,可以配合 nextTick 一起使用
beforeMount挂载前,将 template 模板编译成 render 函数并执行,render 函数如果依赖 data 上的数据,执行时则会触发属性的 getter 进行依赖收集。
mounted实例挂载到 DOM 上,可以通过 DOM API 获取到 DOM 节点,可以访问 ref、$el。常用于发起网络请求。
beforeUpdate更新前,数据变化了,但是视图没有更新,所以要更新视图,需要根据变化数据生成一个新的虚拟 DOM,通过 diff 算法对比出新旧两个虚拟 DOM 的差异部分,最后更新视图。
update组件数据更新之后,避免在这个钩子函数中操作数据,可能陷入死循环。
beforeDestory组件销毁前调用,this 仍能访问,常用于销毁定时器、解绑全局事件、销毁 Echarts 等插件对象。
destoryed组件销毁后调用。
activitedkeep-alive 专属,组件激活时调用
deactivatedkeep-alive 专属,组件离开时调用

Vue 父子组件生命周期钩子函数执行顺序?

挂载过程:(3父4子)

  • 父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted

更新过程:

  • 父beforeUpdate -> 子beforeUpdate -> 子updated -> 父update

销毁过程:

  • 父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed

为什么是 3父4子 这个顺序:

vue 解析 template 模板是在 beforeMount 后 mounted 之前进行的,这一步才开始解析子组件。

在哪个生命周期钩子函数内调用异步请求?

可以在 created、beforeMount、mounted 中进行调用,因为这三个钩子函数中,data 已经创建,可以调用实例上的 props、data、methods 上的属性和方法,推荐在 created 中调用: ​

  • 能更快的获取到后端数据,减少页面 loading 时间。对于需要操作 dom 的场景,使用 $nextTick 或在 mounted中调用。
  • ssr 不支持 beforeMounted、mounted 钩子函数,所以放在 created 有利于一致性,减少了可能存在的代码迁移的工作。

父组件监听子组件的生命周期?

$emit:

// Parent.vue
<Child @mounted="doSomething"/>
    
// Child.vue
mounted() {
  this.$emit("mounted");
}

@hook:

//  Parent.vue
<Child @hook:mounted="doSomething" ></Child>
​
doSomething() {
   console.log('父组件监听到 mounted 钩子函数 ...');
},
    
//  Child.vue
mounted(){
   console.log('子组件触发 mounted 钩子函数 ...');
},    
    
以上输出顺序为:
// 子组件触发 mounted 钩子函数 ...
// 父组件监听到 mounted 钩子函数 ...   @hook 方法不仅仅是可以监听 mounted,其它的生命周期事件,例如:created,updated 等都可以监听。

组件中 data 为什么是一个函数?

组件可以被复用,如果 data 是一个对象的话,那么子组件的 data 指向的内存空间都是同一个地址,子组件中的 data 属性值会相互影响。

所以组件中 data 选项是一个函数,返回一个对象,那么每个实例都拥有一个独立对象,组件实例之间的 data 属性值互不影响;

而且函数的功能更加强大,意味着可以在 data 中写 js 代码,可以完成一些复杂数据的初始化工作。虽然 vue 没有推荐这样使用,但是我们知道有这个功能。

而 new Vue({}) 使用对象是因为:每个项目中只有一个 Vue 实例对象,不会存在 data 属性值相互影响的问题。

v-model 的原理?

v-model 是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件。

  • input 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked 属性和 change 事件;
  • select 使用 value 属性和 change 事件;
<input v-model='something'>
相当于
<input v-bind:value="something" v-on:input="something = $event.target.value">

如果在自定义组件中,v-model 默认代表的是 value 属性 和 input 事件。

父组件:
<ModelChild v-model="message"></ModelChild>

子组件:
<div>{{value}}</div>

props:{
    value: String
},
methods: {
  test1(){
     this.$emit('input', '小红')
  },
}

说一说组件通信的方式?

1. props / $emit 适用 父子组件通信

2. ref 与 $parent / $children 适用 父子组件通信

ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例。

$parent / $children:访问父 / 子实例。

3. $attrs / $listeners 适用于 隔代组件通信

$attrs:包含了 props 声明之外的其他属性( class 和 style 除外 )。可以通过 v-bind="$attrs" 传入内部组件。通常配合 inheritAttrs 选项一起使用。

inheritAttrs: true,传入的属性体现在标签上,false:传入的属性不体现在标签上。

$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件。它可以通过 v-on="$listeners" 传入内部组件。

4. provide / inject 适用于 隔代组件通信

祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。主要解决了跨级组件间的通信问题。

5. EventBus ($emit / $on) 适用于 父子、隔代、兄弟组件通信

创建一个空的 Vue 实例作为事件总线,用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件。

6. Vuex 适用于 父子、隔代、兄弟组件通信

你使用过 Vuex 吗?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理器。它的核心就是 store(仓库),内部包含着很多的 state(状态)。

主要包括以下几个模块:

  • State:存储所有的共享数据。定义了数据的数据结构,可以在这里设置数据的默认值。
  • Getter:可以方便的获取 state 的值,类似于计算属性,简化 vuex 的取值操作。
  • Mutation:是唯一更改 state 值的方法,且必须是同步函数。
  • Action:处理异步任务变更数据通过派发(dispatch)Action,在 Action 中 commit Mutation 变更数据。
  • Module:当项目中所要保存的 state 过多时,可以将 store 模块化处理,优化目录结构。

特点:

  • Vuex 的状态存储是响应式的。就是当你的几个组件都使用到了这个Vuex的状态,一旦它改变了,所有关联的组件都会自动更新相对应的数据。

  • 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

  • Vuex 存储的数据会在页面刷新之后重置为初始化状态,并不能让数据持久化。可以使用一些持久化库,或者使用 localStorage 做持久化。

说一说 computed 和 watch 的区别?

计算属性有缓存,watch 没有缓存。

计算属性必须有 返回值,watch 没有返回值

计算属性默认触发一次计算,依赖其他值,只有它依赖的值发生了变化,下一次获取值才会重新计算,不支持异步。

watch 默认初始化不监听,如果需要初始化监听,添加 immediate: true,支持异步。

应用场景:

计算属性:

  • 当一个属性受多个属性影响的时候,比如:需要拼接展示“用户名”、“列表展示”、“购物车商品结算”。
  • 当 v-for 下使用 v-if 时,我们可以使用计算属性来代替 v-if,有利于提高性能,因为避免了无用 dom 的渲染。
  • 如果要使用的属性所在层级很深时,也可以使用,有利于提高代码可读性。

watch:

  • 当需要监听 值变化时,比如:监听父组件传过来的 prop、监听搜索框内容变化。

说一说 v-if 和 v-show 的区别?

答: 都是控制元素隐藏和显示。

v-if 是对 dom 的新增和删除操作,true 新增节点,false 删除节点,如果初始化为 false 则直接不渲染该节点。

v-show 控制的元素无论是 true 还是 false,都被渲染出来了,通过 display:none 控制元素隐藏。首次也渲染节点(在 dom 中显示)。

应用场景:

如果不会频繁的让元素显示和隐藏,那使用 v-if 比较合适,这样可以提高我们的页面渲染速度,因为 v-if 的值为 true 时,这个元素才会被创建。

如果频繁的让元素显示和隐藏,那使用 v-show 比较合适,因为只是改变元素的样式属性,而不用频繁的创建和删除元素。

说一说 vue 中的 keep-alive 组件

是 vue 的内置组件,主要用于缓存组件,避免重复加载一些不需要经常变动的组件。

属性:

include(只有名字匹配的才会被缓存)、exclude(名字匹配的都不会被缓存)、max(最大缓存数)。

钩子函数:

activated 组件进入时调用、deactivated 组件离开时调用。

源码解析:

Vue 组件实例加载顺序:VNode->实例化->真实Node,在实例化的时候会判断该组件是否被 keep-alive 保存过,是的话则直接拿其中的 DOM 进行渲染。

说一说 vue 中 $nextTick 的作用和原理

使用 nextTick() 是为了可以获取更新后的 DOM。

Vue 更新 dom 节点是异步操作,即数据更新之后,视图不会马上更新,所以修改数据后,在方法中获取到的 dom 节点不是更新后的 dom 节点,只有在 nextTick 里面才能获取到更新后的 dom 节点。

触发时机:同一事件循环中的代码执行完毕 -> DOM 更新 -> nextTick callback触发

使用场景:

  1. created 中想要获取 DOM 时;
  2. 获取列表更新后的高度;

Vue 中 key 的作用?

主要是为了高效的更新虚拟 DOM,其原理是 vue 在 patch 过程中通过 key 和 tag 可以精准判断两个节点是否是同一个,从而避免频繁更新相同元素,减少 DOM 操作,提高性能。

没有 key 或者 用 index 作为 key 可能会引发的问题:

  1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作,会产生没有必要的 DOM 更新 ==> 界面效果没问题,但效率低。
  2. 如果结构中还包含输入类的 DOM(输入框有值的情况),会产生错误 DOM 更新 ==> 界面有问题。

开发中如何选择 key?:

  1. 最好使用每条数据的唯一值。
  2. 如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用 index 作为 key 也是没有问题的。

vue2 中 v-for 和 v-if 为什么不能一起使用?

v-if 不能和 v-for 一起使用的原因是 v-for 的优先级比 v-if 高,一起使用会造成性能浪费,主要表现在会渲染出多余的标签。vue2 也会报出警告。

解决方案:

  • 需要 v-for 的数据先从计算属性中过滤一次
  • v-for 标签下添加子标签 手写 v-if。

vue3 更新了 v-if 和 v-for 的优先级,使 v-if 的优先级高于 v-for。

v-html 的原理

会先移除节点下的所有节点,然后设置 innerHTMLv-html 的值。

常见的事件修饰符及其作用

  • .stop:阻止传播。event.stopPropagation() 阻止捕获和冒泡传播,但是不能防止默认行为;例如:a 链接点击后会跳转。
  • .prevent:阻止默认行为。event.preventDefault(),不会阻止捕获和冒泡传播。
  • .capture:设置为事件捕获,事件由外到内。
  • .once:只会触发一次。
  • .native:监听节点的原生事件。
  • .self:只当事件在该元素本身触发时触发,但是不会阻止冒泡!
<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

MVVM的理解

MVVM是一种软件架构模式,MVVM 分为 ModelViewViewModel

  • Model代表数据模型,数据和业务逻辑都在 Model 层中定义;
  • View代表 UI 视图,负责数据的展示;
  • ViewModel 监听 Model 中数据的改变通知 View 视图的更新,并且根据用户操作 View 更新Model 中的数据;

双向数据绑定的原理

采用 "数据劫持" 结合 "发布者-订阅者模式" 的方式实现。

组件实例化时,会创建一个 Observe 实例,会将 data 数据进行递归遍历,并通过 Object.defineProperty 方法,给每个属性添加上 getter 和 setter。在 getter 中收集依赖,在 setter 中通过依赖更新。

针对数组来说:还会进行数组方法的重写,在调用数组方法时,通知更新。

加分:

双向数据绑定的实现流程:

  1. 数据劫持:当 Vue 实例创建时,Observe 会对 data 对象进行遍历,并使用 Object.defineProperty() 将每个属性转换成 getter/setter,实现数据的劫持。
  2. 编译模板:Vue 的编译器会对模板进行编译,将模板替换成对应的 render 函数。
  3. 收集依赖:当 render 函数执行时,会触发数据项的 getter,将当前数据项对应的 Watcher 添加到该数据项对应的 Dep 依赖中 (Dep.append())。
  4. 数据变化:当数据项发生变化时(即 setter 被触发),会触发该数据项对应的 Dep 的 notify 方法,通知所有订阅了该数据项的 Watcher 进行更新。
  5. 更新视图:Watcher 接收到更新通知后,会调用其绑定的回调函数(通常是组件的渲染函数或计算属性的更新函数)来更新用户界面。

资料:

  1. Observe(观察者) Observe 负责将数据对象(通常是 Vue 实例的 data 对象)进行递归遍历,并使用 Object.defineProperty() 将属性转换成 getter/setter,使数据变得“可观察”。当数据发生变化时,setter 会自动通知相关的依赖(即订阅了这个数据的 Watcher)。

  2. Dep(依赖) Dep 是 Vue 中一个非常重要的类,它管理着与该数据项相关的所有 Watcher。当数据项发生变化时,Dep 会通知所有订阅了该数据项的 Watcher 进行更新。每个数据项(即每个通过 Object.defineProperty() 设置的 getter/setter)都会有一个与之对应的 Dep 实例。

  3. Watcher(监听器) Watcher 是 Vue 中用于观察数据变化的组件。当数据项发生变化时,Dep 会通知所有订阅了该数据项的 Watcher,然后 Watcher 会调用其绑定的回调函数(通常是组件的渲染函数或计算属性的更新函数)来更新用户界面。Watcher 可以在 Vue 组件的创建、计算属性或侦听器(watchers)的创建等过程中被创建和添加到对应的 Dep 中。

Vue 是如何收集依赖的?

getter 中通过 dep.depend() 方法对数据依赖进行收集,然后在 settter 中通过 dep.notify() 通知更新。整个 Dep 其实就是一个观察者,把收集的依赖存储起来,在需要的时候进行调用。在收集数据依赖的时候,会为数据创建一个 Watcher ,当数据发生改变通知每个 Watcher,由 Wathcer 进行更新渲染。

使用 Object.defineProperty() 来进行数据劫持有什么缺点?

需要递归遍历每个属性,性能比较差。

对数组来说:不能实现新增项和数组方法的劫持。vue2 中会对数组的方法 push、pop、shift、unshift、splice、sort、reserve 通过重写的形式,在拦截里面进行手动触发依赖更新。

slot 是什么?有什么作用?原理是什么?

slot插槽,一般在封装组件的时候使用,在组件内不知道以那种形式来展示内容时,可以用slot来占据位置,最终展示形式由父组件以内容形式传递过来,主要分为三种:

  • 默认插槽:又名匿名插槽,当slot没有指定name属性值的时候一个默认显示插槽,一个组件内只有有一个匿名插槽。
  • 具名插槽:带有具体名字的插槽,也就是带有name属性的slot,一个组件可以出现多个具名插槽。
  • 作用域插槽:默认插槽、具名插槽的一个变体,可以是匿名插槽,也可以是具名插槽,该插槽的不同点是在子组件渲染作用域插槽时,可以将子组件内部的数据传递给父组件,让父组件根据子组件的传递过来的数据决定如何渲染该插槽。

实现原理:当子组件执行渲染函数时候,遇到 slot 标签,使用 父组件传入的 slot 标签的内容 中的内容进行替换,此时可以为插槽传递数据,则可称为作用域插槽。

对虚拟DOM的理解

虚拟 DOM 就是用 JS 对象来表述 DOM 节点,是对真实 DOM 的一层抽象。

在 Vue 中,会先把代码转换为虚拟 DOM,再最终渲染到页面,在每次数据发生变化前,都会在内存中缓存一份虚拟 DOM,通过 diff 算法来对比新旧虚拟 DOM 的差异,将差异记录到一个对象中按需更新,最后创建真实 DOM,从而提升页面渲染性能。

说说 vue 的 diff 算法?

首先,我们拿到新旧节点的数组,然后初始化四个指针,分别指向 新旧节点 的开始位置 和 结束位置。

然后进行两两对比:

newStart 与 oldStart、newEnd 与 oldEnd、newStart 与 oldEnd、newEnd 与 oldStart 进行对比,

在这个过程中如果匹配到,那么会将 旧节点对应的真实 dom 移到 新节点的位置上,并且指针往中间移动。

如果上述过程都没配有匹配上,则会判断当前新节点的第一个节点是否在旧节点中,若是存在则复用,若是不存在则创建。

当 oldStart > oldEnd 或者 newStart > newEnd 时,跳出 while 循环。

当 oldStart > oldEnd 代表 oldCh 都已匹配完成,那么代表 newStart 和 newEnd 之间的节点为需要新增的节点。

当 newStart > newEnd 代表 newCh 都已匹配完成,那么代表 oldStart 和 oldEnd 之间的节点为需要删除的节点。

在 patchVnode 过程中会调用 updateChildren,所以 vue 的 diff 算法是个深度优先算法。

Vue 的 diff 算法会直接改变旧的虚拟 DOM吗?

Vue 的 diff 算法不会直接改变旧的虚拟 DOM。

diff 算法是比较新旧两个虚拟 DOM 树的差异,并生成一个 patch 对象,这个对象记录了需要更新的差异信息。

Vue 会根据这个 patch 对象来更新真实的 DOM,而不是直接修改旧的虚拟 DOM。

虚拟 DOM 就一定比真实 DOM 更快吗?

虚拟 DOM 不一定比真实 DOM 更快,而是在特定情况下可以提供更好的性能。

在复杂情况下,虚拟 DOM 可以比真实 DOM 操作更快,因为它是在内存中维护一个虚拟的 DOM 树,将真实 DOM 操作转换为对虚拟 DOM 的操作,然后通过 diff 算法找出需要更新的部分,最后只变更这部分到真实 DOM 就可以。在频繁变更下,它可以批量处理这些变化从而减少对真实 DOM 的操作,减少浏览器的回流重绘,提高页面渲染性能。

而在一些简单场景下,直接操作真实 DOM 可能会更快,当更新操作,直接操作真实 DOM 比操作虚拟 DOM 更高效,省去了虚拟 DOM 的计算、对比开销。

router 和 route 的区别

  • $router 是路由实例,包含了路由跳转方法、钩子函数(路由前置导航守卫、路由后置导航守卫)等
  • $route 是当前路由信息,包括pathparamsqueryname等路由信息参数

路由的 hash 和 history 模式的区别

hash 模式 开发中默认的模式,地址栏URL后携带#,后面为路由。 通过hashchange事件监听hash值变化,在页面hash值发生变化后,window就可以监听到事件改变,并按照规则加载相应的代码。hash值变化对应的URL都会被记录下来,这样就能实现浏览器历史页面前进后退。

window.addEventListener('hashchange', event => {
    console.log(event);
})

history 模式 history模式中URL没有#,这样相对hash模式更好看,但是需要后台配置支持。

假设应用地址为hello.com,服务端不加额外的配置。当通过hello.com来访问时,是没有问题的,可以正常加载到html文件,之后通过route-link或者router.api来跳转也不会有问题,因为之后都不会刷新页面请求html,只是通过history.pushState或者history.replaceState`来改变history记录,修改地址栏地址而已;

如果是直接访问子路由hello.com/test时就会有问题,/test是子路由名,但是服务器中并不存在该目录,就无法索引到html文件,此种情况下就会出现404,所以不管是访问什么路径,都应该加载根目录的html文件。一般后端进行nginx配置处理。

history原理是使用HTML5 history提供的pushStatereplaceState两个API,用于浏览器记录历史浏览栈,并且在修改URL时不会触发页面刷新和后台数据请求。

如何设置动态路由

  • params传参

    • 路由配置: /index/:id
    • 路由跳转:this.$router.push({name: 'index', params: {id: "zs"}});
    • 路由参数获取:$route.params.id
    • 最后形成的路由:/index/zs
  • query传参

    • 路由配置:/index正常的路由配置
    • 路由跳转:this.$rouetr.push({path: 'index', query:{id: "zs"}});
    • 路由参数获取:$route.query.id
    • 最后形成的路由:/index?id=zs

区别

  • 获取参数方式不一样,一个通过$route.params,一个通过 $route.query
  • 参数的生命周期不一样,query参数在URL地址栏中显示不容易丢失,params参数不会在地址栏显示,刷新后会消失

路由守卫

  • 全局守卫:beforeEachafterEach
  • 路由独享守卫:beforeEnter
  • 组件内钩子:beforeRouterEnterbeforeRouterLeave

路由前置导航守卫 beforeEach 跳转前判断是否有权限,只有有权限才能进入。

路由后置导航守卫 afterEach 可以用作跳转路由后更改网页名。

路由独享守卫: beforeEnter 某一个路由所单独享用的路由守卫

{
    path: '/',
    name: 'Home',
    component: () => import('../views/Home.vue'),
    meta: { isAuth: true },
    beforeEnter: (to, from, next) => {
        if (to.meta.isAuth) { //判断是否需要授权
            if (localStorage.getItem('school') === 'qinghuadaxue') {
                next()  //放行
            } else {
                alert('抱歉,您无权限查看!')
            }
        } else {
            next()  //放行
        }
    }
},

组件内守卫 beforeRouterEnterbeforeRouterLeave 某一个路由所单独享用的路由守卫

// 通过路由规则,进入该组件时被调用
beforeRouteEnter(to,from,next) {
  if(toString.meta.isAuth){
    if(localStorage.getTime('school')==='qinghuadaxue'){
      next()
    }else{
      alert('学校名不对,无权限查看!')
    }
  } else{
    next()
  }
},
 
//通过路由规则,离开该组件时被调用 
beforeRouteLeave(to,from,next) {
 next()
}

说一说 vue-router 实现懒加载的方法

懒加载的核心思想:按需加载,也叫异步加载。用到再加载。

作用:减少首次加载时的白屏时间,提高用户体验。

方式:
1Vue 异步组件
2ES6 标准语法 import()---------推荐使用

- Vue 异步加载技术:
1:vue-router 配置路由,使用 vue 的异步组件技术,可以实现懒加载,此时一个组件会生成一个js文件。
2component: resolve => require(['@/views/404.vue'], resolve)

- ES6 推荐方式 import():
1:直接将组件引入的方式,importES6 的一个语法标准,如果需要浏览器兼容,需要转化成 es5 的语法。
2:推荐使用这种方式,但是注意 wepack 的版本 > 2.43:vue 官方文档中使用的也是 import 实现路由懒加载。
4:上面声明导入,下面直接使用。
// 下面没有指定 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')

Vue2和Vue3有哪些区别

  • Vue2使用的是optionsAPIVue3使用composition API,更好的组织代码,提高代码可维护性
  • Vue3使用Proxy代理实现了新的响应式系统,比Vue2有着更好的性能和更准确的数据变化追踪能力。
  • Vue3引入了 Teleprot 组件,可以将 DOM 元素移动到 Vue app 之外的位置。用于创建模态框、弹出框等。
  • Vue3全局API名称发生了变化。
  • 生命周期不同
  • 可以多个根节点
  • Vue3TypeScript的支持更加友好
  • v-for 与 v-if 优先级变化。
  • Vue3核心库的依赖更少,减少打包体积
  • 3支持更好的Tree Shanking,可以更加精确的按需要引入模块
<template>
  <div>
    <button @click="showModal = true">Show Modal</button>
    <teleport to="body">
      <modal v-if="showModal" @close="showModal = false">
        <!-- modal content -->
      </modal>
    </teleport>
  </div>
</template>
 
<script>
import { ref } from 'vue';
 
export default {
  components: {
    Modal: {...}
  },
  setup() {
    const showModal = ref(false);
 
    return {
      showModal
    };
  }
};
</script>

实现响应式 vue2 和 Vue3 相比有什么区别?

Vue3 采用了 Proxy 代理的方式,Proxy 是 ES6 引入的一个新特性,它可以对整个对象进行数据劫持。而 Object.defineProperty 只能监听单个属性的读写,无法监听新增、删除等操作。

对数组新增和删除属性时,Object.defineProperty 监听不到,proxy 可以监听到,所以也就不用再重写数组方法了。

Vue的性能优化有哪些

### 编码阶段

- `v-if``v-for`不一起使用
- `v-for`保证`key`的唯一性
- 使用`keep-alive`缓存组件
- 合理使用计算属性
- `v-if``v-show`酌情使用
- 路由懒加载
- 异步组件
- 图片懒加载
- 节流防抖
- 第三方模块按需引入,不要全部引入,减小打包体积。
- 组件卸载前及时解绑全局事件、清除定时器、以及销毁插件实例。

### 打包优化

- 压缩代码
- 使用 CDN 加载第三方模块
- 抽离公共文件

### 用户体验

- html5 语义化标签
- 骨架屏
- 客户端(浏览器)缓存

### SEO 优化

- 预渲染
- 服务端渲染
- 合理使用 `meta` 标签