1、什么是MVVM ?
是一种设计模式, Model(数据层) View(视图层) ViewModel(视图数据控制层)
数据变化 => ViewModel监听到 => 更新视图 视图变化 => ViewModel监听到 => 更新数据
双向数据绑定
-
双向数据绑定的原理 数据变化的监听? Vue2 Object.defineProperty Vue3 proxy
-
视图变化的监听? 给元素注册事件监听 @change @input ...
问: Object.defineProperty 和 proxy的差异?
Object.defineProperty:
(1) Object.defineProperty 是对于对象属性的劫持, 需要一个个递归劫持, 效率低
(2) Object.defineProperty 对于数组的更新检测, 比较麻烦
prxoy: proxy对于整个对象的劫持, 对象内部的任何属性的修改, 都会先经过外层proxy, 无需递归, 效率高 proxy也可以直接监听数组的变化
-
Vue的响应式系统 => 观察者模式 监听到, 更新谁? 如何运作的? Vue采用的是观察者模式, 以Vue2为例, 通过 Object.defineProperty 完成对于数据的劫持 通过观察者模式, 完成对于节点的数据更新
观察者模式 :
是一种 一对多的 设计模式, 一个对象变化了, 其他所有依赖的, 跟着变 一上来 vue 解析渲染, 会进行依赖收集, 会将渲染watcher, 计算属性watcher, 侦听器watcher, 都会收集到对应 dep 中 (依赖收集完成)
当 Object.defineProperty 监听到数据变化, 就会根据依赖关系, 派发更新, 每个watcher侦听器 都是一个对象, 都有着callback回调, 执行回调 (调用函数, 派发更新)
2、Vue生命周期
- 开始构建
- 初始化数据
- 编译模板
- 挂载dom
- 渲染 / 更新
- 卸载
8大钩子 Vue2 Vue3
beforeCreate setup
created setup
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroy onBeforeUnmount
destroyed onUnmounted
3、Vue组件通信
-
父传子 子传父 (1) 通过 props 父传子, 给组件添加属性传值 - - 组件内部props接收
(2) 子传父, 利用 $emit 和 @ 注册事件, 实现数据传递** - 子组件: this.$emit('事件名', 参数) - 父组件: @事件名="处理函数" -
事件总线 (Vue3被移除)
通过EventBus进行信息的发布和订阅更新 (1) 创建事件总线 const eventBus = new Vue() export default eventBus (2) 在A组件中, 监听, 订阅变化 eventBus.emit('事件名', 参数)
-
provide inject (Vue3被增强)
用于父组件向子孙后代组件共享传递数据 Vue2, 其实也有 provide 和 inject, 但是不太好用 Vue3, 大大的增强了 provide 和 inject, 广泛应用
Vue3: (1) provide('共享的键名', '值') provide('共享的方法名', function() { ... }) (2) let result = inject('共享的键名' , 默认值) let fn = inject('共享的方法名') // 子孙后代, 调用祖辈的方法, 传参更新数据
-
parent $refs
children[0] 第一个组件 refs: 用于配合ref, 获取具体哪一个组件
-
listeners
组件二次封装用到 => 需要进行 批量 的跨层级数据传递 (使用起来, 就像父传子, 子传父一样) v-bind="listeners" 批量监听事件, 批量向上触发
4、Vuex 5个核心概念:
(1) state: 存放数据
(2) mutations: 存放操作数据的方法
(3) actions: 存放一些异步操作, 注意: actions 不可以直接操作state, 需要提交mutation
(4) getters: 计算属性
(5) modules: 分模块
同步情况: 页面中 commit('模块名/某mutation', '参数')
异步情况: 页面中 dispatch('模块名/某action', '参数')
action中 commit('模块名/某mutation', '参数')
1.pinia (Vue3) 官方推荐的状态库 将来Vuex5的语法, 就会和pinia的语法类似 核心概念: (1) state: 存数据 data
```
state () {
return {
count: 100,
money: 1000
}
}
```
**(2) actions: 存放方法, 既可以同步, 也可以异步, 且可以直接操作状态 methods**
```
actions: {
addCount () {
this.count++
},
addCountAsync () {
setTimeout(() => {
this.count++
}, 1000)
},
async getMoney () {
const res = await xxApi()
this.money = res.data.money
}
}
```
(3) getters: 计算属性
--------------------------------------------------------------
完整 pinia仓 库写法: store/count.js 一个文件, 就是一个仓库
```
export const useCounterStore = defineStore('counter', {
state() {
return { count: 0 }
},
actions: {
add() {
this.count++
},
},
})
```
页面中, 无论同步还是异步, 直接导入对应的仓库, 直接调用方法即可
1. **导入仓库**
import { useCounterStore } from '@/store/count.js'
const counter = useCounterStore()
2. **直接访问使用仓库的数据, 获取调用仓库的方法**
<div>
{{ counter.count }}
<button @click="counter.add">按钮</button>
</div>
5、Vue中key的作用? / React中key的作用?
作用: 给虚拟dom添加标识, 优化复用对比策略, 优化渲染性能
-
Vue/React的更新机制: 差异化更新, 对比新旧虚拟dom, 找出新旧dom不同的部分, 进行更新视图
-
为什么对比虚拟dom? 不对比真实dom?
真实dom的结构, 太复杂, 有着很多无用的属性, 无需对比, 如果对比了浪费性能
虚拟dom, 是一个js对象, 通过js对象, 描述真实的dom, 只记录关键属性, 对比起来性能更高
-
就算是进行了虚拟dom的优化, 但是dom树, 是树形结构! 虚拟dom树, 也是树形结构, 树形结构的对比? => diff算法
diff算法策略:
(1) tree diff 同层比较, 同层对比, 如果发现根级别的元素类型不同, 直接整棵树销毁重建
(2) component diff 同层级别元素类型相同, 按照策略, 对比差异, 记录差异
(3) element diff 子节点列表, 同层兄弟节点, 默认按照下标进行对比, 如果加上key, 就相当于给虚拟dom加上了标识
对比策略: 就是对相同key的元素, 进行比较
子节点列表, 顺序下标, 会发生变化时, 推荐都加上key, 便于正确复用! (列表 v-for 往列表前面加, 中间加)
6、Vue 跳转路由时的传参方式 (Vue2 / Vue3)
3种 => query传参, params传参, 动态路由传参
-
query传参: 会在地址栏显示, 刷新不会丢失
router.push('/login?username=zs&age=18') router.push({ path: '/login', query: { username: 'zs' age: 18 } }) 获取: route.query.username -
params传参: 内存中传递, 不显示地址栏, 刷新会丢失, 必须配合命名路由使用 如果要处理刷新会丢失的问题, 需要配合 localStorage
{ path: '/login', name: 'login', component: () => import('@/views/login/index.vue') }
router.push({
name: 'login',
params: {
car: '小车车'
}
})
获取: route.params.car
```
3. 动态路由传参: 地址栏传递, 显示在地址栏, 刷新不会丢失, 必须配动态路
{ path: '/user/:id', component: () => import('@/views/user/index.vue') }
router.push({
path: '/user/92377447',
})
获取: route.params.id
7、Vue权限控制
RBAC: role based access control 基于角色的权限控制 给用户分配角色, 给角色分配权限
核心点:
-
按钮权限控制 / 表单 / 表格 登录成功时, 获取用户的权限相关信息, 角色roles: [{ xx }, { xx }] 按钮规则btnRules: [{ xx }, { xx }] 根据返回的权限信息, 调用封装好的函数, 判断按钮, 是否需要禁用启用 / 显示隐藏
-
菜单权限控制 (1) 登录成功时, 获取用户的权限相关信息, menus: ['xx', 'xx', 'xx']
[ { "moduleGroupId": 1001, "moduleGroupName": "部署管理", "requestMapping": "deploy-manage", }, { "moduleGroupId": 1100, "moduleGroupName": "系统管理", "requestMapping": "sys-manage", "moduleList": [ { "moduleId": 1101, "moduleName": "系统日志", "requestMapping": "system-log", "moduleGroupId": 1100, }, { "moduleId": 1102, "moduleName": "系统告警", "requestMapping": "sys-alert", "moduleGroupId": 1100, }, ], } ](2) 将路由拆分成, 动态路由和静态路由, 动态路由需要权限才能访问的路由, 静态路由默认即可访问的路由 (3) 用户一登录成功, 会动态的新增一些动态路由 addRoutes, 此时不同用户的路由已经不一样了 (4) 但是为了菜单的响应式展示, 动态使用 vuex 维护路由数组
-
api接口url权限控制: 后端控制多一点
8、首屏渲染优化
1)网站性能优化实战——从12.67s到1.06s的故事 (首屏优化) 量化数据, 更有说服力 juejin.cn/post/684490…
-
移动spa商城优化记(一)---首屏优化篇 (首屏优化) juejin.cn/post/684490…
-
前端黑科技:美团网页首帧优化实践 (白屏问题优化) juejin.cn/post/684490…