这是我参与8月更文挑战的第30天,活动详情查看:8月更文挑战
MVVM 和 MVC
MVC
- M(Model):模型,负责处理数据。
- V(View):视图,负责数据的展示。
- C(Controller):控制器,负责数据和视图之间的交互。
- 总的来说就是:通过controller将Model的数据展示在View上。
MVVM
- M(Model):模型,负责处理数据。
- V(View):视图,负责数据的展示。
VM(View Model):实现了数据的双向绑定。主要通过两件事来实现:- 通过数据绑定将Model转化为View;
- 通过DOM事件监听将View转化为Model
- 实现了View和Model的自动同步。
MVVM 规定 View和Model不能直接通信。
什么是Vue
vue是一个用于构建用户界面的渐进式的javaScript框架。- 其具有灵活、易用、高效的特点。
- 其采用虚拟DOM构建页面,单页面的路由。
- 数据与视图分离、由数据驱动页面的刷新渲染。
- 其借鉴了MVVM模型,但又完全不遵循MVVM,因为this.$refs的使用会破坏这种规则。
- 缺点:单页面不利于seo,不支持IE8以下,首屏加载时间长。
Vue声明周期
2.x版本的生命周期
beforeCreate:在实例初始化之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。created:在实例创建完成后立即被调用。在这一步,实例已完成以下的配置:数据观测 (data observer),property 和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$elproperty 目前尚不可用。beforMount:在挂载开始之前被调用:相关的render函数首次被调用。注意:- 该钩子在服务端渲染期间不被调用
mounted:实例被挂载后调用。注意:- mounted不能所有的子组件也被一起挂载
- 该钩子在服务端渲染期间不被调用
- 如果你希望等到整个视图都渲染完毕,可以在
mounted内部使用 vm.$nextTick
mounted: function () {
this.$nextTick(function () {
// 视图渲染完之后执行的一些操作
})
}
beforUpdate:数据更新时调用,发生在虚拟DOM打补丁之前。此阶段适合 在更新DOM之前访问现有的DOM,比如手动移除已添加的事件监听器。注意:- 该钩子在服务器端渲染期间不被调用,因为只有初次渲染会在服务端进行。
updated:在此阶段DOM已经被更新。注意:- **应该避免在此阶段更改状态,最好使用computed或者watcher替代 **
- 该钩子在服务端渲染期间不会被调用
updated不会保证所有的子组件也都一起被重绘。如果你希望等到整个视图都重绘完毕,可以在updated里使用 vm.$nextTick
updated: function () {
this.$nextTick(function () {
// 视图渲染完之后执行的一些操作
})
}
activated:被 keep-alive 缓存的组件激活时调用。 注意⚠️:- 该钩子在服务端渲染期间不会被调用
deactivated:被 keep-alive 缓存的组件停用时调用。注意⚠️:- 该钩子在服务端渲染期间不会被调用
beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。注意:- 该钩子在服务端渲染期间不会被调用
destroyed:实例销毁后调用。该钩子被调用后,对应 Vue 实例的所有指令都被解绑,所有的事件监听器被移除,所有的子实例也都被销毁。注意:- 该钩子在服务端渲染期间不会被调用
errorCaptured:(v2.5.0+版本新增)当捕获一个来自子孙组件的错误时被调用。此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。此钩子可以返回false以阻止该错误继续向上传播。
3.x版本的生命周期
由于3.x版本使用了setup组合式API(类似于react的hooks),而 setup 是围绕 beforeCreate 和 created 生命周期钩子运行的,所以不需要显式地定义它们。换句话说,在这些钩子中编写的任何代码都应该直接在 setup 函数中编写。所以生命周期如下:
onBeforeMountonMountedonBeforeUpdateonUpdatedonBeforeUnmountonUnmountedonErrorCapturedonRenderTrackedonRenderTriggeredonActivatedonDeactivated
父子组件生命周期执行顺序
- 加载渲染过程:
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted - 所以,为了更好的父子组件之间的数据传递,我们通常将数据请求放在created阶段进行。
- 更新过程:
父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated - 销毁过程:
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
vue实现双向绑定的原理
vue2.x采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()vue3.x通过proxy实现
组件之间的数据传递
父子组件
- 父传子:通过props传递
- 子传父:通过$emit
- 爷孙之间通过 provide, inject
兄弟组件
- 通过eventBus
路由之间传参
- 通过 query、params
v-show 和 v-if
- v-show 只是简单的通过css的display属性进行显示隐藏的切换
- v-if 会真实的销毁和创建元素。
computed 和 watch
- computed: 是计算属性,依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值。(可以理解为自动的)
computed中的属性不能与data中的属性同名,否则会报错- watch 监听,只有当监听到某个属性的变化时,才会执行某些操作。(可以理解为手动的)
data 为什么是一个函数return回的对象?
- 因为组件大多数情况下会被拿来复用,而js中对象是引用类型,如果组件中 data 是一个对象,那么作用域没有隔离,子组件中的 data 属性值会相互影响,如果组件中 data 选项是一个函数,那么每个实例可以维护一份被返回对象的独立的拷贝,组件实例之间的 data 属性值不会互相影响;而 new Vue 的实例,是不会被复用的,因此不存在引用对象的问题。
@hook
- 使用@hook可以监听子组件的生命周期函数,并且做一些你需要的操作
<Child @hook:mounted="dosomething"></Child>
v-model原理
- v-model是一个语法糖,多数情况下用于表单元素上创建双向绑定。实现如下:
- input框的 text 和 textarea 元素使用 value 属性和 input 事件;
- checkbox 和 radio 使用 checked 属性和 change 事件;
- select 使用 value 和 change 事件。
Vue 修饰符
| 修饰符 | 阐述 |
|---|---|
| .lazy | 改变输入框的值时value不会改变,当光标离开输入框时,v-model绑定的值value才会改变 |
| .trim | 将v-model绑定的数据的值的首尾空格过滤掉 |
| .number | 将值转成数字(先输入数字会将前面的数字部分截取出来;先输入字母则修饰符无效) |
| .stop | 阻止事件冒泡 |
| .capture | 事件默认是由里往外冒泡,capture修饰符的作用是反过来,由外向内捕获 |
| .self | 只有点击事件绑定的本身才会触发事件 |
| .once | 事件只执行一次 |
| .prevent | 阻止默认事件 |
| .native | 是加在自定义组件的事件上,保证事件能执行 |
| .lfet | 鼠标左键触发的事件 |
| .right | 鼠标右键触发的事件 |
| .middle | 鼠标中间的键触发的事件 |
| .passive | 相当于给滚动事件增加了.lazy修饰符 |
| .camel | 确保绑定参数被识别为驼峰写法 |
| .camel | 父子组件传值时,如果子组件想改变这个值可以使用此修饰符简写 |
v-for 和 v-if 为什么不建议一起使用?
- v-for的优先级要高于v-if,将其放在同一个元素上时,每次数据渲染都会先循环再去判断条件,造成不必要的渲染,带来性能问题。
vue中使用了哪些设计模式?
- 单例模式:整个应用程序有且仅有一个实例,即我们的入口文件处实例化的vue
- 工厂模式:传入参数即可创建实例。
- 虚拟 DOM 根据参数的不同返回基础标签的 Vnode 和组件 Vnode
- 发布订阅模式:vue的事件机制
- 观察者模式:响应式数据原理
- 等。。。
你做过哪些vue的性能优化?
- 对象层级不要过深,否则性能就会差。
- 不需要响应式的数据不要放到 data 中(可以用 Object.freeze() 冻结数据)
- v-if 和 v-show 区分使用场景
- computed 和 watch 区分使用场景
- v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if
- 大数据列表和表格性能优化-虚拟列表/虚拟表格
- 防止内部泄漏,组件销毁后把全局变量和事件销毁
- 图片懒加载
- 路由懒加载
- 第三方插件的按需引入
- 适当采用 keep-alive 缓存组件
- 防抖、节流运用
- 服务端渲染 SSR or 预渲染
vue为什么要求组件模板只能有一个根元素?
我们在开发中通常都是写的单文件组件,如下:
<template>
<div></div>
</template>
<script>
</script>
<style>
</style>
- 单文件组件其实就是一个vue实例,既然是vue实例,那么就需要有一个入口,如果有多个div就无法指定入口。
- 其次vue最终是要转换成DOM树,渲染到页面中的,而DOM的根节点只有一个。
- template标签是不会真实的渲染到DOM中的。
vue中如何重置data
- this.$data获取当前状态下的data;
- this.$options.data()获取该组件初始状态下的data。
- 使用Object.assgin(this.options.data())
说说你对vue的错误处理的了解
- 分为errorCaptured与errorHandler。
- errorCaptured是组件内部钩子,可捕捉本组件与子孙组件抛出的错误,接收error、vm、info三个参数,return false后可以阻止错误继续向上抛出。
- errorHandler为全局钩子,使用Vue.config.errorHandler配置,接收参数与errorCaptured一致,2.6后可捕捉v-on与promise链的错误,可用于统一错误处理与错误兜底。
Vuex
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
- Vuex 无法持久化、内部核心原理是通过创造一个全局实例 new Vue。(vuex-persist插件可实现持久化)
- 主要包含以下几个核心概念:
State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。