杂项

90 阅读6分钟

MVC MVVM

都是为了解决数据和展示的耦合问题,提高代码可读性可维护性。

MVC
  • M(Model:模型层+业务逻辑层【获取存放数据】)
  • V(View视图层【展示界面】
  • C(Controller控制层【处理页面交互】)

View中既要负责展示,也要负责部分逻辑的处理,导致Controller层比较单薄,而View层内容比较庞大。

MVVM
  • ViewModel 实现一套数据响应式机制:只要Model数据发生改变,视图就会自动精准最小化更新。(无需开发者去手动操作更新DOM)
  • ViewModel 通过事件监听响应View中用户的行为,去更新数据。
  • MVVM在降低Model层和View层耦合的同时,还减少他们的维护关系,开发者只专注于业务逻辑的开发。 image.png

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/…

image.png

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>

高并发