perf: Improve the global loading display
							parent
							
								
									e650a0b863
								
							
						
					
					
						commit
						77d40dc763
					
				| 
						 | 
				
			
			@ -17,7 +17,38 @@ async function initApplication() {
 | 
			
		|||
    overrides: overridesPreferences,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  import('./bootstrap').then((m) => m.bootstrap(namespace));
 | 
			
		||||
  // 启动应用并挂载
 | 
			
		||||
  // vue应用主要逻辑及视图
 | 
			
		||||
  const { bootstrap } = await import('./bootstrap');
 | 
			
		||||
  await bootstrap(namespace);
 | 
			
		||||
 | 
			
		||||
  // 移除并销毁loading
 | 
			
		||||
  destoryAppLoading();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 移除并销毁loading
 | 
			
		||||
 * 放在这里是而不是放在 index.html 的app标签内,主要是因为这样比较不会生硬,渲染过快可能会有闪烁
 | 
			
		||||
 * 通过先添加css动画隐藏,在动画结束后在移除loading节点来改善体验
 | 
			
		||||
 */
 | 
			
		||||
function destoryAppLoading() {
 | 
			
		||||
  // 全局搜索文件 loading.html, 找到对应的节点
 | 
			
		||||
  const loadingElement = document.querySelector('#__app-loading__');
 | 
			
		||||
  if (loadingElement) {
 | 
			
		||||
    loadingElement.classList.add('hidden');
 | 
			
		||||
    const injectLoadingElements = document.querySelectorAll(
 | 
			
		||||
      '[data-app-loading^="inject"]',
 | 
			
		||||
    );
 | 
			
		||||
    // 过渡动画结束后移除loading节点
 | 
			
		||||
    loadingElement.addEventListener(
 | 
			
		||||
      'transitionend',
 | 
			
		||||
      () => {
 | 
			
		||||
        loadingElement.remove();
 | 
			
		||||
        injectLoadingElements.forEach((el) => el?.remove());
 | 
			
		||||
      },
 | 
			
		||||
      { once: true },
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
initApplication();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
import { defineConfig } from '@vben/vite-config';
 | 
			
		||||
 | 
			
		||||
export default defineConfig({
 | 
			
		||||
  appcation: {
 | 
			
		||||
  application: {
 | 
			
		||||
    compress: false,
 | 
			
		||||
    compressTypes: ['brotli', 'gzip'],
 | 
			
		||||
    importmap: false,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,6 +13,7 @@ export { toPosixPath } from './path';
 | 
			
		|||
export { prettierFormat } from './prettier';
 | 
			
		||||
export type { Package } from '@manypkg/get-packages';
 | 
			
		||||
export { consola } from 'consola';
 | 
			
		||||
export { nanoid } from 'nanoid';
 | 
			
		||||
export { readPackageJSON } from 'pkg-types';
 | 
			
		||||
export { rimraf } from 'rimraf';
 | 
			
		||||
export { $, chalk as colors, fs, spinner } from 'zx';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,11 +7,11 @@ import { defineConfig, loadEnv, mergeConfig } from 'vite';
 | 
			
		|||
import { getApplicationConditionPlugins } from '../plugins';
 | 
			
		||||
import { getCommonConfig } from './common';
 | 
			
		||||
 | 
			
		||||
import type { DefineAppcationOptions } from '../typing';
 | 
			
		||||
import type { DefineApplicationOptions } from '../typing';
 | 
			
		||||
 | 
			
		||||
function defineApplicationConfig(options: DefineAppcationOptions = {}) {
 | 
			
		||||
function defineApplicationConfig(options: DefineApplicationOptions = {}) {
 | 
			
		||||
  return defineConfig(async ({ command, mode }) => {
 | 
			
		||||
    const { appcation = {}, vite = {} } = options;
 | 
			
		||||
    const { application = {}, vite = {} } = options;
 | 
			
		||||
    const root = process.cwd();
 | 
			
		||||
    const isBuild = command === 'build';
 | 
			
		||||
    const env = loadEnv(mode, root);
 | 
			
		||||
| 
						 | 
				
			
			@ -29,11 +29,10 @@ function defineApplicationConfig(options: DefineAppcationOptions = {}) {
 | 
			
		|||
      mock: true,
 | 
			
		||||
      mode,
 | 
			
		||||
      turboConsole: false,
 | 
			
		||||
      ...appcation,
 | 
			
		||||
      ...application,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const applicationConfig: UserConfig = {
 | 
			
		||||
      // },
 | 
			
		||||
      build: {
 | 
			
		||||
        rollupOptions: {
 | 
			
		||||
          output: {
 | 
			
		||||
| 
						 | 
				
			
			@ -44,7 +43,6 @@ function defineApplicationConfig(options: DefineAppcationOptions = {}) {
 | 
			
		|||
        },
 | 
			
		||||
        target: 'es2015',
 | 
			
		||||
      },
 | 
			
		||||
      //     },
 | 
			
		||||
      esbuild: {
 | 
			
		||||
        drop: isBuild
 | 
			
		||||
          ? [
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,6 @@
 | 
			
		|||
import { existsSync } from 'node:fs';
 | 
			
		||||
import { join } from 'node:path';
 | 
			
		||||
 | 
			
		||||
import { fs } from '@vben/node-utils';
 | 
			
		||||
 | 
			
		||||
import { defineApplicationConfig } from './application';
 | 
			
		||||
import { defineLibraryConfig } from './library';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -18,13 +17,19 @@ function defineConfig(options: DefineConfig = {}) {
 | 
			
		|||
  // 根据包是否存在 index.html,自动判断类型
 | 
			
		||||
  if (type === 'auto') {
 | 
			
		||||
    const htmlPath = join(process.cwd(), 'index.html');
 | 
			
		||||
    projectType = fs.existsSync(htmlPath) ? 'appcation' : 'library';
 | 
			
		||||
    projectType = existsSync(htmlPath) ? 'application' : 'library';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (projectType === 'appcation') {
 | 
			
		||||
    return defineApplicationConfig(defineOptions);
 | 
			
		||||
  } else if (projectType === 'library') {
 | 
			
		||||
    return defineLibraryConfig(defineOptions);
 | 
			
		||||
  switch (projectType) {
 | 
			
		||||
    case 'application': {
 | 
			
		||||
      return defineApplicationConfig(defineOptions);
 | 
			
		||||
    }
 | 
			
		||||
    case 'library': {
 | 
			
		||||
      return defineLibraryConfig(defineOptions);
 | 
			
		||||
    }
 | 
			
		||||
    default: {
 | 
			
		||||
      throw new Error(`Unsupported project type: ${projectType}`);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,7 +33,7 @@ function defineLibraryConfig(options: DefineLibraryOptions = {}) {
 | 
			
		|||
      build: {
 | 
			
		||||
        lib: {
 | 
			
		||||
          entry: 'src/index.ts',
 | 
			
		||||
          fileName: () => 'index.mjs',
 | 
			
		||||
          fileName: 'index.mjs',
 | 
			
		||||
          formats: ['es'],
 | 
			
		||||
        },
 | 
			
		||||
        rollupOptions: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,7 +35,7 @@ async function viteExtraAppConfigPlugin({
 | 
			
		|||
 | 
			
		||||
  return {
 | 
			
		||||
    async configResolved(config) {
 | 
			
		||||
      publicPath = config.base;
 | 
			
		||||
      publicPath = ensureTrailingSlash(config.base);
 | 
			
		||||
      source = await getConfigSource();
 | 
			
		||||
    },
 | 
			
		||||
    async generateBundle() {
 | 
			
		||||
| 
						 | 
				
			
			@ -59,21 +59,13 @@ async function viteExtraAppConfigPlugin({
 | 
			
		|||
    },
 | 
			
		||||
    name: 'vite:extra-app-config',
 | 
			
		||||
    async transformIndexHtml(html) {
 | 
			
		||||
      publicPath = publicPath.endsWith('/') ? publicPath : `${publicPath}/`;
 | 
			
		||||
      const hash = `v=${version}-${generatorContentHash(source, 8)}`;
 | 
			
		||||
 | 
			
		||||
      const appConfigSrc = `${publicPath}${GLOBAL_CONFIG_FILE_NAME}?${hash}`;
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        html,
 | 
			
		||||
        tags: [
 | 
			
		||||
          {
 | 
			
		||||
            attrs: {
 | 
			
		||||
              src: appConfigSrc,
 | 
			
		||||
            },
 | 
			
		||||
            tag: 'script',
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        tags: [{ attrs: { src: appConfigSrc }, tag: 'script' }],
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
  };
 | 
			
		||||
| 
						 | 
				
			
			@ -94,4 +86,8 @@ async function getConfigSource() {
 | 
			
		|||
  return source;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function ensureTrailingSlash(path: string) {
 | 
			
		||||
  return path.endsWith('/') ? path : `${path}/`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { viteExtraAppConfigPlugin };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,7 +20,7 @@ import { viteImportMapPlugin } from './importmap';
 | 
			
		|||
import { viteInjectAppLoadingPlugin } from './inject-app-loading';
 | 
			
		||||
 | 
			
		||||
import type {
 | 
			
		||||
  AppcationPluginOptions,
 | 
			
		||||
  ApplicationPluginOptions,
 | 
			
		||||
  CommonPluginOptions,
 | 
			
		||||
  ConditionPlugin,
 | 
			
		||||
  LibraryPluginOptions,
 | 
			
		||||
| 
						 | 
				
			
			@ -82,7 +82,7 @@ async function getCommonConditionPlugins(
 | 
			
		|||
 * 根据条件获取应用类型的vite插件
 | 
			
		||||
 */
 | 
			
		||||
async function getApplicationConditionPlugins(
 | 
			
		||||
  options: AppcationPluginOptions,
 | 
			
		||||
  options: ApplicationPluginOptions,
 | 
			
		||||
): Promise<PluginOption[]> {
 | 
			
		||||
  // 单独取,否则commonOptions拿不到
 | 
			
		||||
  const isBuild = options.isBuild;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,14 +14,14 @@ async function viteInjectAppLoadingPlugin(
 | 
			
		|||
): Promise<PluginOption | undefined> {
 | 
			
		||||
  const loadingHtml = await getLoadingRawByHtmlTemplate();
 | 
			
		||||
  const envRaw = isBuild ? 'prod' : 'dev';
 | 
			
		||||
  const cacheName = `'__${env.VITE_APP_NAMESPACE}-${envRaw}-theme__'`;
 | 
			
		||||
  const cacheName = `'${env.VITE_APP_NAMESPACE}-${envRaw}-preferences-theme'`;
 | 
			
		||||
 | 
			
		||||
  // 获取缓存的主题
 | 
			
		||||
  // 保证黑暗主题下,刷新页面时,loading也是黑暗主题
 | 
			
		||||
  const injectScript = `
 | 
			
		||||
  <script>
 | 
			
		||||
  <script data-app-loading="inject-js">
 | 
			
		||||
  var theme = localStorage.getItem(${cacheName});
 | 
			
		||||
  document.documentElement.classList.toggle('dark', theme === 'dark');
 | 
			
		||||
  document.documentElement.classList.toggle('dark', /dark/.test(theme));
 | 
			
		||||
</script>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -34,11 +34,8 @@ async function viteInjectAppLoadingPlugin(
 | 
			
		|||
    name: 'vite:inject-app-loading',
 | 
			
		||||
    transformIndexHtml: {
 | 
			
		||||
      handler(html) {
 | 
			
		||||
        const re = /<div\s*id\s*=\s*"app"\s*>(\s*)<\/div>/;
 | 
			
		||||
        html = html.replace(
 | 
			
		||||
          re,
 | 
			
		||||
          `<div id="app">${injectScript}${loadingHtml}</div>`,
 | 
			
		||||
        );
 | 
			
		||||
        const re = /<body\s*>/;
 | 
			
		||||
        html = html.replace(re, `<body>${injectScript}${loadingHtml}`);
 | 
			
		||||
        return html;
 | 
			
		||||
      },
 | 
			
		||||
      order: 'pre',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
<style>
 | 
			
		||||
<style data-app-loading="inject-css">
 | 
			
		||||
  html {
 | 
			
		||||
    /* same as ant-design-vue/dist/reset.css setting, avoid the title line-height changed */
 | 
			
		||||
    line-height: 1.15;
 | 
			
		||||
| 
						 | 
				
			
			@ -13,6 +13,10 @@
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  .loading {
 | 
			
		||||
    position: fixed;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    z-index: 9999;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +26,12 @@
 | 
			
		|||
    background-color: #f4f7f9;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .loading.hidden {
 | 
			
		||||
    visibility: hidden;
 | 
			
		||||
    opacity: 0;
 | 
			
		||||
    transition: all 1s ease-out;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .loading .dots {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +106,7 @@
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
<div class="loading">
 | 
			
		||||
<div class="loading" id="__app-loading__">
 | 
			
		||||
  <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span>
 | 
			
		||||
  <div class="title"><%= VITE_GLOB_APP_TITLE %></div>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
<style>
 | 
			
		||||
<style data-app-loading="inject-css">
 | 
			
		||||
  html {
 | 
			
		||||
    /* same as ant-design-vue/dist/reset.css setting, avoid the title line-height changed */
 | 
			
		||||
    line-height: 1.15;
 | 
			
		||||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
    position: fixed;
 | 
			
		||||
    top: 0;
 | 
			
		||||
    left: 0;
 | 
			
		||||
    z-index: 9999;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +16,14 @@
 | 
			
		|||
    width: 100%;
 | 
			
		||||
    height: 100%;
 | 
			
		||||
    background-color: #f4f7f9;
 | 
			
		||||
 | 
			
		||||
    /* transition: all 0.8s ease-out; */
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .loading.hidden {
 | 
			
		||||
    visibility: hidden;
 | 
			
		||||
    opacity: 0;
 | 
			
		||||
    transition: all 1s ease-out;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .dark .loading {
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +105,7 @@
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
<div class="loading">
 | 
			
		||||
<div class="loading" id="__app-loading__">
 | 
			
		||||
  <div class="loader"></div>
 | 
			
		||||
  <div class="title"><%= VITE_GLOB_APP_TITLE %></div>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ import type { PluginOptions } from 'vite-plugin-dts';
 | 
			
		|||
 | 
			
		||||
import viteTurboConsolePlugin from 'unplugin-turbo-console/vite';
 | 
			
		||||
 | 
			
		||||
export interface IImportMap {
 | 
			
		||||
interface IImportMap {
 | 
			
		||||
  imports?: Record<string, string>;
 | 
			
		||||
  scopes?: {
 | 
			
		||||
    [scope: string]: Record<string, string>;
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +40,7 @@ interface CommonPluginOptions {
 | 
			
		|||
  /** 是否开启devtools */
 | 
			
		||||
  devtools?: boolean;
 | 
			
		||||
  /** 环境变量 */
 | 
			
		||||
  env: Record<string, any>;
 | 
			
		||||
  env?: Record<string, any>;
 | 
			
		||||
  /** 是否构建模式 */
 | 
			
		||||
  isBuild?: boolean;
 | 
			
		||||
  /** 构建模式 */
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ interface CommonPluginOptions {
 | 
			
		|||
  visualizer?: PluginVisualizerOptions | boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface AppcationPluginOptions extends CommonPluginOptions {
 | 
			
		||||
interface ApplicationPluginOptions extends CommonPluginOptions {
 | 
			
		||||
  /** 开启 gzip 压缩 */
 | 
			
		||||
  compress?: boolean;
 | 
			
		||||
  /** 压缩类型 */
 | 
			
		||||
| 
						 | 
				
			
			@ -80,12 +80,12 @@ interface LibraryPluginOptions extends CommonPluginOptions {
 | 
			
		|||
  injectLibCss?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface AppcationOptions extends AppcationPluginOptions {}
 | 
			
		||||
interface ApplicationOptions extends ApplicationPluginOptions {}
 | 
			
		||||
 | 
			
		||||
interface LibraryOptions extends LibraryPluginOptions {}
 | 
			
		||||
 | 
			
		||||
interface DefineAppcationOptions {
 | 
			
		||||
  appcation?: AppcationOptions;
 | 
			
		||||
interface DefineApplicationOptions {
 | 
			
		||||
  application?: ApplicationOptions;
 | 
			
		||||
  vite?: UserConfig;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -95,17 +95,18 @@ interface DefineLibraryOptions {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
type DefineConfig = {
 | 
			
		||||
  type?: 'appcation' | 'auto' | 'library';
 | 
			
		||||
} & DefineAppcationOptions &
 | 
			
		||||
  type?: 'application' | 'auto' | 'library';
 | 
			
		||||
} & DefineApplicationOptions &
 | 
			
		||||
  DefineLibraryOptions;
 | 
			
		||||
 | 
			
		||||
export type {
 | 
			
		||||
  AppcationPluginOptions,
 | 
			
		||||
  ApplicationPluginOptions,
 | 
			
		||||
  CommonPluginOptions,
 | 
			
		||||
  ConditionPlugin,
 | 
			
		||||
  DefineAppcationOptions,
 | 
			
		||||
  DefineApplicationOptions,
 | 
			
		||||
  DefineConfig,
 | 
			
		||||
  DefineLibraryOptions,
 | 
			
		||||
  IImportMap,
 | 
			
		||||
  ImportmapPluginOptions,
 | 
			
		||||
  LibraryPluginOptions,
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,6 +21,8 @@ import { defaultPreferences } from './config';
 | 
			
		|||
import type { Preferences } from './types';
 | 
			
		||||
 | 
			
		||||
const STORAGE_KEY = 'preferences';
 | 
			
		||||
const STORAGE_KEY_LOCALE = `${STORAGE_KEY}-locale`;
 | 
			
		||||
const STORAGE_KEY_THEME = `${STORAGE_KEY}-theme`;
 | 
			
		||||
 | 
			
		||||
interface initialOptions {
 | 
			
		||||
  namespace: string;
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +38,7 @@ function isDarkTheme(theme: string) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
class PreferenceManager {
 | 
			
		||||
  private cache: StorageManager<Preferences> | null = null;
 | 
			
		||||
  private cache: StorageManager | null = null;
 | 
			
		||||
  private flattenedState: Flatten<Preferences>;
 | 
			
		||||
  private initialPreferences: Preferences = defaultPreferences;
 | 
			
		||||
  private isInitialized: boolean = false;
 | 
			
		||||
| 
						 | 
				
			
			@ -60,6 +62,8 @@ class PreferenceManager {
 | 
			
		|||
   */
 | 
			
		||||
  private _savePreferences(preference: Preferences) {
 | 
			
		||||
    this.cache?.setItem(STORAGE_KEY, preference);
 | 
			
		||||
    this.cache?.setItem(STORAGE_KEY_LOCALE, preference.app.locale);
 | 
			
		||||
    this.cache?.setItem(STORAGE_KEY_THEME, preference.app.themeMode);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			@ -89,7 +93,7 @@ class PreferenceManager {
 | 
			
		|||
   *  从缓存中加载偏好设置。如果缓存中没有找到对应的偏好设置,则返回默认偏好设置。
 | 
			
		||||
   */
 | 
			
		||||
  private loadCachedPreferences() {
 | 
			
		||||
    return this.cache?.getItem(STORAGE_KEY);
 | 
			
		||||
    return this.cache?.getItem<Preferences>(STORAGE_KEY);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			@ -231,8 +235,8 @@ class PreferenceManager {
 | 
			
		|||
 | 
			
		||||
  /**
 | 
			
		||||
   * 覆盖偏好设置
 | 
			
		||||
   * @param overrides - 要覆盖的偏好设置
 | 
			
		||||
   * @param namespace - 命名空间
 | 
			
		||||
   * overrides  要覆盖的偏好设置
 | 
			
		||||
   * namespace  命名空间
 | 
			
		||||
   */
 | 
			
		||||
  public async initPreferences({ namespace, overrides }: initialOptions) {
 | 
			
		||||
    // 是否初始化过
 | 
			
		||||
| 
						 | 
				
			
			@ -273,6 +277,8 @@ class PreferenceManager {
 | 
			
		|||
    this.savePreferences(this.state);
 | 
			
		||||
    // 从存储中移除偏好设置项
 | 
			
		||||
    this.cache?.removeItem(STORAGE_KEY);
 | 
			
		||||
    this.cache?.removeItem(STORAGE_KEY_THEME);
 | 
			
		||||
    this.cache?.removeItem(STORAGE_KEY_LOCALE);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ interface StorageItem<T> {
 | 
			
		|||
  value: T;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class StorageManager<T> {
 | 
			
		||||
class StorageManager {
 | 
			
		||||
  private prefix: string;
 | 
			
		||||
  private storage: Storage;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -67,7 +67,7 @@ class StorageManager<T> {
 | 
			
		|||
   * @param defaultValue 当项不存在或已过期时返回的默认值
 | 
			
		||||
   * @returns 值,如果项已过期或解析错误则返回默认值
 | 
			
		||||
   */
 | 
			
		||||
  getItem(key: string, defaultValue: T | null = null): T | null {
 | 
			
		||||
  getItem<T>(key: string, defaultValue: T | null = null): T | null {
 | 
			
		||||
    const fullKey = this.getFullKey(key);
 | 
			
		||||
    const itemStr = this.storage.getItem(fullKey);
 | 
			
		||||
    if (!itemStr) {
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +103,7 @@ class StorageManager<T> {
 | 
			
		|||
   * @param value 值
 | 
			
		||||
   * @param ttl 存活时间(毫秒)
 | 
			
		||||
   */
 | 
			
		||||
  setItem(key: string, value: T, ttl?: number): void {
 | 
			
		||||
  setItem<T>(key: string, value: T, ttl?: number): void {
 | 
			
		||||
    const fullKey = this.getFullKey(key);
 | 
			
		||||
    const expiry = ttl ? Date.now() + ttl : undefined;
 | 
			
		||||
    const item: StorageItem<T> = { expiry, value };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue