Vue 中使用传统的 JavaScript(非 TypeScript)同样可以基于依赖倒置原则(已实战)

在 Vue 中使用传统的 JavaScript(非 TypeScript)同样可以基于依赖倒置原则(Dependency Inversion Principle, DIP)处理业务逻辑。虽然没有 TypeScript 的接口类型检查,但可以通过 约定式抽象依赖注入 实现解耦。以下是具体实现方法,以 localStorageget/set 操作为例:


1. 核心思路

  • 依赖倒置原则:组件不直接依赖具体的存储实现(如 localStorage),而是依赖一个抽象的存储服务接口。
  • 实现目标:允许后续更换存储方式(如 sessionStorageIndexedDBMock 存储),而无需修改组件代码。

2. 具体实现步骤

(1) 定义抽象存储服务

约定一个存储服务的“接口”(通过对象方法的形式):

// storage/StorageService.js
export class StorageService {
  getItem(key) {
    throw new Error("必须实现 getItem 方法");
  }

  setItem(key, value) {
    throw new Error("必须实现 setItem 方法");
  }
}

(2) 创建具体实现类

基于抽象约定,实现具体的存储服务(如 localStorage):

// storage/LocalStorageService.js
import { StorageService } from './StorageService';

export class LocalStorageService extends StorageService {
  getItem(key) {
    return localStorage.getItem(key);
  }

  setItem(key, value) {
    localStorage.setItem(key, value);
  }
}

(3) 通过依赖注入使用服务

在 Vue 中通过 provide/inject 将存储服务注入组件:

父组件(提供实现)
// App.vue
import { LocalStorageService } from './storage/LocalStorageService';

export default {
  provide() {
    return {
      // 注入具体实现
      storageService: new LocalStorageService()
    };
  }
}
子组件(使用服务)
// UserComponent.vue
export default {
  inject: ['storageService'], // 注入存储服务

  methods: {
    saveUser(user) {
      // 通过抽象方法调用,不直接依赖 localStorage
      this.storageService.setItem('currentUser', JSON.stringify(user));
    },

    loadUser() {
      const userData = this.storageService.getItem('currentUser');
      return userData ? JSON.parse(userData) : null;
    }
  }
}

3. 动态切换存储实现

如果需要更换存储方式(例如切换到 sessionStorage),只需修改注入的实现,无需改动组件代码:

// 创建新的实现类
// storage/SessionStorageService.js
import { StorageService } from './StorageService';

export class SessionStorageService extends StorageService {
  getItem(key) {
    return sessionStorage.getItem(key);
  }

  setItem(key, value) {
    sessionStorage.setItem(key, value);
  }
}

// App.vue
import { SessionStorageService } from './storage/SessionStorageService';

export default {
  provide() {
    return {
      storageService: new SessionStorageService() // 切换实现
    };
  }
}

4. Mock 存储用于测试

在单元测试中,可以注入一个 Mock 存储服务:

// storage/MockStorageService.js
import { StorageService } from './StorageService';

export class MockStorageService extends StorageService {
  constructor() {
    super();
    this.store = {}; // 用内存对象模拟存储
  }

  getItem(key) {
    return this.store[key] || null;
  }

  setItem(key, value) {
    this.store[key] = value;
  }
}

// 测试代码中
const mockStorage = new MockStorageService();
mockStorage.setItem('test', '123');
console.log(mockStorage.getItem('test')); // 输出 "123"

5. 工厂模式统一管理服务

通过工厂函数动态创建存储服务,增强灵活性:

// storage/StorageFactory.js
import { LocalStorageService } from './LocalStorageService';
import { SessionStorageService } from './SessionStorageService';
import { MockStorageService } from './MockStorageService';

export class StorageFactory {
  static createStorage(type = 'local') {
    switch (type) {
      case 'local':
        return new LocalStorageService();
      case 'session':
        return new SessionStorageService();
      case 'mock':
        return new MockStorageService();
      default:
        throw new Error('未知的存储类型');
    }
  }
}

// App.vue
import { StorageFactory } from './storage/StorageFactory';

export default {
  provide() {
    return {
      storageService: StorageFactory.createStorage('local') // 按需创建
    };
  }
}

6. 优势总结

  1. 解耦组件与存储细节:组件只依赖 StorageService 的抽象方法,不关心具体是 localStorage 还是其他实现。
  2. 可扩展性:新增存储方式(如 IndexedDB)只需添加新类并修改注入逻辑。
  3. 便于测试:测试时注入 MockStorageService,无需操作真实 localStorage
  4. 代码一致性:所有存储操作通过统一接口调用,避免直接操作 localStorage 的散落代码。

7. 完整代码示例

文件结构:

src/
├── storage/
│   ├── StorageService.js       # 抽象类
│   ├── LocalStorageService.js  # 具体实现
│   ├── StorageFactory.js       # 工厂类
└── components/
    └── UserComponent.vue       # 使用存储服务的组件

组件中使用:

// UserComponent.vue
export default {
  inject: ['storageService'],

  data() {
    return {
      user: { id: 1, name: "Alice" }
    };
  },

  methods: {
    saveData() {
      this.storageService.setItem('user', JSON.stringify(this.user));
    },

    loadData() {
      const data = this.storageService.getItem('user');
      this.user = data ? JSON.parse(data) : {};
    }
  },

  mounted() {
    this.loadData();
  }
}

8. 总结

通过约定抽象类(StorageService)和依赖注入(provide/inject),即使没有 TypeScript,也可以在 Vue 中实现依赖倒置原则。这种方式让代码更灵活、可维护,同时为未来扩展(如更换存储方式或添加缓存逻辑)提供了清晰路径。

posted @ 2025-04-02 15:32  ring军  阅读(37)  评论(0)    收藏  举报