MVC MVVM
都是为了解决数据和展示的耦合问题,提高代码可读性可维护性。
MVC
- M(Model:模型层+业务逻辑层【获取存放数据】)
- V(View视图层【展示界面】
- C(Controller控制层【处理页面交互】)
View中既要负责展示,也要负责部分逻辑的处理,导致Controller层比较单薄,而View层内容比较庞大。
MVVM
- ViewModel 实现一套数据响应式机制:只要Model数据发生改变,视图就会自动精准最小化更新。(无需开发者去手动操作更新DOM)
- ViewModel 通过事件监听响应View中用户的行为,去更新数据。
- MVVM在降低Model层和View层耦合的同时,还减少他们的维护关系,开发者只专注于业务逻辑的开发。
Vue3 vs Vue2
- 原理上Vue3使用Proxy,Vue2使用defineProperty。代理整个对象代替了递归遍历数据上的所有属性。使用Proxy添加删除属性|改变数组也能响应式。
- Vue3源码抛弃了Vue2的类式编程,使用了函数式编程,可以按需引入,配合tree-shaking能让打包体积更小。
- diff算法里面,Vue3做了静态节点标记,在做diff算法里面不参与比较。
- Vue3增加组合式api,没有this; 同react由类式组件升级到函数式组件一样。组合式api更利于代码的拆分和复用。
- Vue3淡化了生命周期,没有created,setup等同于created;同react由类式组件升级到函数式组件一样。
- Vue3中mixin混入对象被抛弃,推荐使用组合式函数。mixin混入存在来源不明确,有属性名冲突问题。
- Vue3生态上更推荐的搭配是 Pinia + Vite;传统Vue2项目使用的 Webpack + Vuex。
- Vue3中v-if优先级高于v-for。
- Vue3单文件组件里面可以允许多个并列的标签。
- Vue3更友好的ts支持。
React类组件 vs 函数组件
- 设计思想不同:类组件的设计思想是面向对象编程,有继承|有内部状态的管理|有生命周期;函数式组件使用函数理念,在相同的输入下,输出结果必然相同。
- 相对于类组件,函数组件更纯粹,结构更简单。函数组件更利于将展示层和业务层分离,更利于代码的组织和复用。
- 函数式组件去this化,淡化了生命周期,类组件里面的this难以管理,比较模糊,且随着应用的复杂性,更多的业务逻辑实现是在非生命周期中实现。
- 组件是否重新渲染的控制不一样,类组件使用的是 shouldComponentUpdate函数去阻断渲染,而函数组件使用的是React.memo策略实现。
- 组合优于继承,函数组件 > 类组件。
React vs Vue
- 单文件组件的写法不一样,Vue里面更多的写法是 template script style;而React推荐的写法是 jsx。
- 默认情况下,React父组件更新了,子组件也会跟着去更新,有性能浪费的情况;React里面的写法更灵活,要求原生js能力强,更多的优化需要开发者去维护。
- React搭建一个应用时,其他状态存储、路由等需要自己去组装。
- React + hooks Vue3 + compositionApi 函数式编程。
- 单选数据流。
- 虚拟DOM + Diff算法。
- Vue里面有keep-alive,mixin ;style含有scope。
- Vue含有v-if v-show v-for指令。
diff算法
- 虚拟DOM是描述真是DOM的js对象,里面含有标签名tagName|属性对象props|子节点数组|文本节点等
- diff算法是指改变了响应的数据后,生成了新的虚拟DOM对象,同旧的虚拟DOM对象做比较的过程。新旧虚拟DOM直接的差异运用diff算法,找出差异,最小化的更新视图。
- www.processon.com/diagraming/…
Webpack vs Vite
- Webpack 拥有强大的插件系统;但是开发环境构建速度慢,配置较为复杂。Webpack在启动的时候把所有的文件都build一遍。不管模块是否会执行,都要编译和打包。
- Vite 在启动时候不需要打包,不需要编译,按需动态编译。
Vuex vs Pinia
- 使用方式不一样
- 配置项不一样
// Vuex in main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store'
new Vue({
store,
render: h => h(App)
}).$mount('#app')
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state:{},
getters:{},
mutations:{},
actions:{},
modules:{}
})
// Vuex in xxx.vue
<p>{{ $store.state.xxx }}</p>
// Pinia in main.js
import { createApp } from 'vue';
import App from './App.vue';
import { createPinia } from 'pinia';
const app = createApp(App);
const pinia = createPinia();
app.use(pinia).mount('#app');
import { defineStore } from 'pinia';
// Pinia Store
export const useFundFilterStore = defineStore('fundFilters', {
state: () => ({
primaryId: '',
}),
getters: {},
actions: {
setPrimaryId(primaryId = '') {
this.primaryId = primaryId;
},
},
});
// Pinia in xxx.vue
import { useFundFilterStore } from '/store/useFundFilterStore.js';
const store = useFundFilterStore();
const { setPrimaryId } = store;
const { primaryId } = storeToRefs(store);
性能优化
借助浏览器的性能分析工具 + 打包体积分析工具
- 浏览器的性能分析工具分析前端资源 + 后端接口的响应时间
- 打包体积分析工具分析前端每个资源的体积
编码方面的优化
- 首屏数据尽量并行,小数据量的接口可以让后端在同一个接口返回。
- 若后端接口响应时间慢,可以让后端优化响应时间(后端加redis,表索引)
- 分页加载数据,异步加载【跟首屏渲染没有关系的可以延后加载】
- React项目 优化空间大【React.memo()】;Vue项目【v-if v-show | key | keep-alive | 非响应类的数据不要使用响应式Api】
- 骨架屏
减小打包体积
- Webpack打包的一定要调试到production模式
- 插件升级【换成支持tree-shaking的按需引入的插件,另外项目中尽量减少插件库的使用,格式化日期的使用dayjs替代moment插件】
- 异步加载的模块可以使用import()动态引入,譬如路由懒加载
网络方面的优化
- http升级
- 强缓存 + 协商缓存
- cdn
组件封装的思路
- 共性的逻辑由封装的组件实现,共同的属性由组件自身维护。
- 个性化的值由使用方作为props传入,同时组件方必要时做好参数props类型校验、可选值校验、默认值填充
- 个性化的显示区域可以由默认/具名插槽实现
- 封装的组件特征:props传入 + 自定义事件的传入 + 自身维护的数据|功能方法 + 暴露给使用方的数据|方法 + 有多个插槽区域 + 遵从数据单向流
- 介绍组件的使用文档。
示例:FilterTablePagination
// 共性的:布局是固定 + 筛选器的表单类型是可有限枚举的 + 查询|重置的动作是固定的 + 表格组件是固定的 + 分页的组件的逻辑是固定的
// 个性化差异:筛选器的配置项(属性传递)+ 表格的列数组(属性传递)+ 查询数据的api(属性传递) + 有的可能需要导出按钮等(插槽)
// 可能需要对外暴露的方法:筛选器对象 + 查询的方法
<FormTable
ref="formTableRef"
:formColumns="formColumnsClone"
:tableColumns="tableColumnsClone"
:apiRequest="clueNewestPPLListRequest"
:queryParamsHandler="queryParamsHandler"
>
<template #tableTools>
<el-button type="primary" @click="onExport" icon="Download">导出</el-button>
</template>
<template #tableOperate>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200" :fixed="'right'">
<template #default="{ row }">
<el-button link type="primary" @click="onShowPPLHistoryDialog(row)">更新</el-button>
</template>
</el-table-column>
</template>
</FormTable>