我承认我在这件事上玩得很开心.此解决方案在Vue上不支持depend,但在Vue上可以轻松使用by.开始!
我的目标是创建一个"特别干净"的动态插入<link>
个样式表,这不应该导致FOUC个.
我创建了一个名为ThemeHelper
的类(从技术上讲,它是一个构造函数,但你知道我的意思),其工作原理如下:
myThemeHelper.add(themeName, href)
将从href
(一个URL)和stylesheet.disabled = true
预加载一个样式表,并给它一个名称(只是为了跟踪它).当调用样式表的onload
时,返回一个解析为CSSStyleSheet的Promise
.
myThemeHelper.theme = "<theme name>"
(setter) Select 要应用的主题.上一个主题被禁用,而给定的主题被启用.切换发生得很快,因为.add
已经预加载了样式表.
myThemeHelper.theme
(getter)返回当前主题名.
这门课本身有33行.我制作了一个片段,在一些 bootstrap 样本主题之间切换,因为这些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>
我希望这对你有用,对我来说也很有趣!