返回博客首页
← 所有文章

使用主题切换器动态切换主题!

2021年10月22日 — 作者:Igor Randjelovic

通常情况下,您希望用户能够根据自己的偏好配置应用程序的外观。theme-switcher是一个轻量级且高效的插件,它允许您轻松地在不同的 (s)css 文件集之间切换,并在切换到不同的主题时处理卸载每个主题的所有样式。

npm i --save @nativescript/theme-switcher
import { Dialogs } from "@nativescript/core";
import { initThemes, switchTheme } from '@nativescript/theme-switcher';

// in main.ts
initThemes({
  default: () => import('theme-loader!./themes/default.scss'),
  red: () => import('theme-loader!./themes/red.scss'),
  green: () => import('theme-loader!./themes/green.scss'),
}, {
  /* optionally pass in options here */
});

// in the button tap handler
async function onTap() {
  const res = await Dialogs.action('Select theme', 'Cancel', [
    'default', 'red', 'green'
  ])

  if(res) {
    switchTheme(res);
  }
}

工作原理 - 深入源代码

插件本身非常简单,它可以分解成几个不同的部分

  1. index.ts 中的插件代码本身
  2. theme-loader.js 中的 webpack 加载器
  3. nativescript.webpack.js 中的 (nativescript) webpack 配置
  4. shims.d.ts 中支持导入 css/scss 文件的 TypeScript shim

插件代码

插件包含一个 ThemeSwitcher 类,一个导出的默认实例以及可用的方法的直接导出作为快捷方式。

切换主题时,先前选择的主题的 css 规则将使用核心中的 removeTaggedAdditionalCSS 方法清理。新的 css 规则将使用 addTaggedAdditionalCSS 添加。最后,我们通过在根视图和所有打开的模态视图上调用 _onCssStateChange() 来刷新样式。

持久化是使用核心中的 ApplicationSettings 完成的,保存最后一个主题的键是可配置的。

关于 @nativescript/webpack@5+ 中 css/scss 加载方式的注意事项

@nativescript/webpack@5+ 中,css 和 scss 使用核心中的 addAdditionalTaggedCSSremoveAdditionalTaggedCSS 方法加载和应用,并在您自动 import './something.css' 时调用。

切换主题时,我们需要导入 css,但我们实际上不希望样式自动应用,因为在切换到其他主题时没有简单的方法删除它们。

为了简化使用,@nativescript/theme-switcher 附带了一个微小的 webpack 加载器,它将所有对 addAdditionalTaggedCSSremoveAdditionalTaggedCSS 的调用替换为空操作函数调用 - 禁用导入的 css/scss 的自动应用。

加载器旨在通过在其前面添加导入路径来直接在标记中使用

import "theme-loader!./path/to/some.css"
import "theme-loader!./path/to/some.scss"

以上将正常处理 some.s?css 文件,将其通过所有配置的加载器运行,最后将通过我们的 theme-loader 运行以禁用加载代码。

修改 webpack 配置的新方法

插件假设您正在使用 @nativescript/webpack@5+ 并利用它提供的一些新功能。即,此插件有一个 nativescript.webpack.js 文件,它为 theme-loader 注册了一个新的加载器别名,从而简化了与现有 NativeScript 项目的集成。

const { resolve } = require('path');

/**
 * @param {typeof import("@nativescript/webpack")} webpack
 */
module.exports = (webpack) => {
	webpack.chainWebpack((config) => {
		// prettier-ignore
		config.resolveLoader.alias
            .set('theme-loader', resolve(__dirname, 'theme-loader'))
	});
};

这种方法为插件作者打开了无限的可能性。ThemeSwitcher 只是可能性的冰山一角。

在文档中阅读有关插件 API 的更多信息

让 TypeScript 高兴

TypeScript 不知道如何处理 .css.scss 文件,并且在遇到以下代码时会报错

import("theme-loader!./themes.default.css");

以上对于 TypeScript 来说确实毫无意义,但由于 NativeScript 应用程序是使用 webpack 构建的 - 我们配置了规则来处理 css 和 scss 文件。

解决此问题的快速方法是在 import 语句上方添加 // @ts-ignore,然后就完事了。

ThemeSwitcher 附带一个 shims.d.ts 文件,它让 typescript 知道以上是一个有效的模块/导入。

shim 本身包含以下内容,并且没有声明任何细节,仅用于让 TypeScript 知道这些模块。

declare module "theme-loader!*" {}

要让 TypeScript 知道 shims,请将其包含在 references.d.ts 文件中

/// <reference path="./node_modules/@nativescript/theme-switcher/shims.d.ts" />

就是这样,如果您遇到问题,请告诉我们。