gpt4 book ai didi

vue.js - 使用 Vue.js 的用户可切换自定义主题

转载 作者:搜寻专家 更新时间:2023-10-30 22:11:18 25 4
gpt4 key购买 nike

我有一个 VueJS 应用程序,它将带有许多不同的主题(至少 20 个左右)。每个主题样式表不仅会更改颜色和字体大小等内容,还会更改某些元素的位置和布局。

我希望用户能够在这些主题之间动态切换。因此,在运行时,用户将能够打开选项菜单并从下拉列表中进行选择。

在 VueJS 中拥有许多动态用户可选主题的最干净的方法是什么?


我想到了几个方法,比如:

  • 动态插入 <link><style>标签。虽然这可能有效,但我并不真的认为它特别“干净”,如果我从 AJAX 加载,那么我经常会看到 FOUC .
  • 通过计算属性简单地更改 Vue 类绑定(bind)。就像有一个 if-else每个组件中每个支持的主题的链。我不是特别喜欢这个解决方案,因为以后每次添加新主题时,我制作的每个组件都需要更新。

在 React 中,我认为有一个插件或具有 <ThemeProvider> 的东西组件,其中添加主题就像包装它一样简单,即 <ThemeProvider theme={themeProp}><MyComponent></ThemeProvider> ,并且该主题中的所有样式都将应用于该组件和所有子组件。

VueJS有没有类似的东西,或者有没有办法实现?

最佳答案

我承认我从中获得了一些乐趣。此解决方案不依赖 Vue,但 Vue 可以轻松使用它。我们开始吧!

我的目标是创建一个“特别干净”的动态插入 <link>不应导致 FOUC 的样式表.

我创建了一个名为 ThemeHelper 的类(从技术上讲,它是一个构造函数,但你知道我的意思) ,它的工作原理是这样的:

  • myThemeHelper.add(themeName, href)将从 href 预加载样式表(一个 URL)与 stylesheet.disabled = true , 并为其命名(只是为了跟踪它)。这将返回 Promise解析为 CSSStyleSheet当样式表的 onload被称为。
  • myThemeHelper.theme = "<theme name>" (setter) 选择要应用的主题。先前的主题被禁用,而给定的主题被启用。切换发生得很快,因为样式表已经被 .add 预加载了。 .
  • myThemeHelper.theme (getter) 返回当前主题名称。

类本身是 33 行。我制作了一个在一些 Bootswatch 主题之间切换的片段,因为这些 CSS 文件非常大 (100Kb+)。

const ThemeHelper = function() {

const preloadTheme = (href) => {
let link = document.createElement('link');
link.rel = "stylesheet";
link.href = href;
document.head.appendChild(link);

return new Promise((resolve, reject) => {
link.onload = e => {
const sheet = e.target.sheet;
sheet.disabled = true;
resolve(sheet);
};
link.onerror = reject;
});
};

const selectTheme = (themes, name) => {
if (name && !themes[name]) {
throw new Error(`"${name}" has not been defined as a theme.`);
}
Object.keys(themes).forEach(n => themes[n].disabled = (n !== name));
}

let themes = {};

return {
add(name, href) { return preloadTheme(href).then(s => themes[name] = s) },
set theme(name) { selectTheme(themes, name) },
get theme() { return Object.keys(themes).find(n => !themes[n].disabled) }
};
};

const themes = {
flatly: "https://bootswatch.com/4/flatly/bootstrap.min.css",
materia: "https://bootswatch.com/4/materia/bootstrap.min.css",
solar: "https://bootswatch.com/4/solar/bootstrap.min.css"
};

const themeHelper = new ThemeHelper();

let added = Object.keys(themes).map(n => themeHelper.add(n, themes[n]));

Promise.all(added).then(sheets => {
console.log(`${sheets.length} themes loaded`);
themeHelper.theme = "materia";
});
<h3>Click a button to select a theme</h3>

<button
class="btn btn-primary"
onclick="themeHelper.theme='materia'">Paper theme
</button>

<button
class="btn btn-primary"
onclick="themeHelper.theme='flatly'">Flatly theme
</button>

<button
class="btn btn-primary"
onclick="themeHelper.theme='solar'">Solar theme
</button>

不难看出我完全喜欢 ES6(也许我过度使用了一点 const :)

就 Vue 而言,您可以制作一个包装 <select> 的组件:

const ThemeHelper = function() {

const preloadTheme = (href) => {
let link = document.createElement('link');
link.rel = "stylesheet";
link.href = href;
document.head.appendChild(link);

return new Promise((resolve, reject) => {
link.onload = e => {
const sheet = e.target.sheet;
sheet.disabled = true;
resolve(sheet);
};
link.onerror = reject;
});
};

const selectTheme = (themes, name) => {
if (name && !themes[name]) {
throw new Error(`"${name}" has not been defined as a theme.`);
}
Object.keys(themes).forEach(n => themes[n].disabled = (n !== name));
}

let themes = {};

return {
add(name, href) { return preloadTheme(href).then(s => themes[name] = s) },
set theme(name) { selectTheme(themes, name) },
get theme() { return Object.keys(themes).find(n => !themes[n].disabled) }
};
};

let app = new Vue({
el: '#app',
data() {
return {
themes: {
flatly: "https://bootswatch.com/4/flatly/bootstrap.min.css",
materia: "https://bootswatch.com/4/materia/bootstrap.min.css",
solar: "https://bootswatch.com/4/solar/bootstrap.min.css"
},
themeHelper: new ThemeHelper(),
loading: true,
}
},
created() {
// add/load themes
let added = Object.keys(this.themes).map(name => {
return this.themeHelper.add(name, this.themes[name]);
});

Promise.all(added).then(sheets => {
console.log(`${sheets.length} themes loaded`);
this.loading = false;
this.themeHelper.theme = "flatly";
});
}
});
<script src="https://unpkg.com/vue@2.5.2/dist/vue.js"></script>

<div id="app">
<p v-if="loading">loading...</p>

<select v-model="themeHelper.theme">
<option v-for="(href, name) of themes" v-bind:value="name">
{{ name }}
</option>
</select>
<span>Selected: {{ themeHelper.theme }}</span>
</div>

<hr>

<h3>Select a theme above</h3>
<button class="btn btn-primary">A Button</button>

我希望这对你有用,因为它对我来说很有趣!

关于vue.js - 使用 Vue.js 的用户可切换自定义主题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46730904/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com