Vue3 與依賴注入
本文寫於 2021 年 2 月 19 日
在 React 中,我們可以通過 context 與 useContext 實現單例、注入……等諸多特性。
詳細請看上一篇文章:如何利用 React Hooks 管理全局狀態.
例如:
const SomeService = createContext(null);
const useRootSomeService = () => {
const [n, setN] = useState(0);
const add = useCallback(() => {
setN((number) => number + 1);
}, []);
return { n, setN };
};
const App: React.FC = () => {
return (
<SomeService.Provider value={useRootSomeService()}>
<Apple />
</SomeService>
)
};
const Apple: React.FC = () => {
const someService = useContext(SomeService)
};
那么 Vue3 可以嗎?
可以!!!只需要利用 Vue3 的 provide 與 inject API 即。
創建一個 Service
import { Ref, ref } from "vue";
class ChatService {
static INJECT_KEY: string;
conversations: Ref<Conversation[]>;
constructor() {
this.conversations = ref([]);
}
}
這樣我們只需要在組件中實例化該 class,就可以擁有響應性的 conversations 屬性了。
但是我們不滿足於如此,我們還想要在任何地方,都可以使用 const chatService = inject(ChatService);
來拿到服務的單例!
provide/inject
vue3 提供給我們的 provide API 可以在任意組件中使用:provide(key, variable);
使用后該組件的自組件的任何位置都可以利用 inject API 拿到剛剛注入的變量:const v = inject(key);
。
因此我們可以這么封裝一下:
import { provide as vueProvide, inject as vueInject } from "vue";
export const createInjectKey = () => {
const randomNumber = Math.round(Math.random() * 10 ** 8).toString();
return randomKey;
};
interface ServiceType<T> {
new (): T;
INJECT_KEY: string;
}
export function provide<T>(Service: ServiceType<T>) {
const key = createInjectKey();
Service.INJECT_KEY = key;
vueProvide(key, new Service());
}
export function inject<T>(Service: ServiceType<T>): T {
const service = vueInject<T>(Service.INJECT_KEY);
if (!service) {
console.error("You have to provide service first!!!");
}
return service!;
}
這樣一來,我們只需要寫一個擁有 static INJECT_KEY 屬性的 class,就可以在組件樹頂端使用 provide(xxxService)
,然后再在任意位置調用 inject(xxxService)
來獲取服務單例了!
(完)