云计算百科
云计算领域专业知识百科平台

Vue2+Antd项目集成Semi主题化实战

项目背景:

        项目使用的是vue2+antd搭建,是一个成熟完整项目。新需求要求,加入主题化功能。使用字节的semi(部分功能)来完成。

         笔者的理解是用semi的主题化字典表(如:

         –semi-color-primary: #3579ff;

         –semi-color-primary-light-default: #e6f0ff;

          –semi-color-bg-0: #ffffff;           –semi-color-text-0: #1c1f23;

        )

        但是具体的主题换转换功能,需要开发者自己实现。

         开发者的任务是,将semi这些变量表注入到现有的项目中,同时,本项目中还有一些需求是有部分功能不受主题化影响,需做额外配置。

前置准备:

主题化npm包

下载依赖"@semi-bot/semi-theme-xxxxlight": "^1.0.4"—基于 Semi 定制的一套主题 npm 包

这个是自己推上去的css的npm包–即拿了官方的改成自己需要的

内部例:

–semi-color-text-0: rgba(var(–semi-grey-9),1);

主题化hooks文件

 用于设置主题

import Vue from 'vue';
import store from '@/store';

const THEMESTORAGENAMESPACE = Vue.ls.options.namespace;

// 主题常量
export const COMMINDTHEMESTYLE = {
LIGHT: 'light',
DARK: 'dark'
};

const COMMINDFIXEDTHEMEMAP = {
[COMMINDTHEMESTYLE.LIGHT]: 'semi-always-light',
[COMMINDTHEMESTYLE.DARK]: 'semi-always-dark'
};

/**
* @description: 主题相关hooks
* @return {*}
*/
export default function useThemeHooks() {
/**
* @description: 设置主题
* @param {*} theme | COMMINDTHEMESTYLE
* @return {*}
*/
function setTheme(theme) {
document.body.setAttribute('theme-mode', theme);
Vue.ls.set('semi-theme', theme);
store.dispatch('theme/setTheme', theme);
}

/**
* @description: 获取主题
* @return {*}
*/
function getTheme() {
return Vue.ls.get('semi-theme') || COMMINDTHEMESTYLE.LIGHT;
}

/**
* @description: 切换主题
* @return {*}
*/
function toggleTheme() {
const currentTheme = getTheme();
const newTheme = currentTheme === COMMINDTHEMESTYLE.LIGHT ? COMMINDTHEMESTYLE.DARK : COMMINDTHEMESTYLE.LIGHT;
setTheme(newTheme);
}

/**
* @description: 初始化主题
* @return {*}
*/
function initTheme() {
const savedTheme = getTheme();
setTheme(savedTheme);
addLocalStorageListener();
}

function setLightTheme() {
setTheme(COMMINDTHEMESTYLE.LIGHT);
}

function setDarkTheme() {
setTheme(COMMINDTHEMESTYLE.DARK);
}

/**
* @description: 添加监听storage
* @return {*}
*/
function addLocalStorageListener() {
window.addEventListener('storage', themeLocalStorageHandler);
}

/**
* @description: 移除监听storage
* @return {*}
*/
function removeLocalStorageListener() {
window.removeEventListener('storage', themeLocalStorageHandler);
}

/**
* @description: 监听storage 切换主题
* @param {*} e
* @return {*}
*/
function themeLocalStorageHandler(e) {
const isThemeStorage = e.key === THEMESTORAGENAMESPACE + 'semi-theme';
if (!isThemeStorage) return;
const theme = getTheme();
setTheme(theme);
}

return {
setLightTheme,
setDarkTheme,
setTheme,
getTheme,
toggleTheme,
initTheme,
addLocalStorageListener,
removeLocalStorageListener,
COMMINDTHEMESTYLE,
COMMINDFIXEDTHEMEMAP
};
}

固定亮色主题hooks(即不需要主题化)vue自定义指令

用于固定主题,是vue自定义指令的形式

import useThemeHooks from '@/hooks/useThemeHooks';
const { getTheme, COMMINDTHEMESTYLE } = useThemeHooks();

const fixedTheme = {
bind() {
// 预先设置 防止闪屏
document.body.setAttribute('theme-mode', COMMINDTHEMESTYLE.LIGHT);

// 1. 给body 添加mode
setTimeout(() => {
// 防止unbind执行时,dom还没渲染
document.body.setAttribute('theme-mode', COMMINDTHEMESTYLE.LIGHT);
}, 0);
},
unbind() {
const theme = getTheme();
// 恢复原来的mode
document.body.setAttribute('theme-mode', theme);
}
};

export default fixedTheme;

覆盖原antd的组件样式

.ant-modal {
color: var(–semi-color-text-0);
}
.ant-modal-header {
background: var(–semi-color-bg-1);
border-bottom: 1px solid var(–semi-color-border-1);
}
.ant-modal-title {
color: var(–semi-color-text-0);
}

……

整体思路:

精简版:

  先载入semi样式,然后在main.js初始化加载主题化-即

document.body.setAttribute('theme-mode', theme);

  组件主题化使用方式为样式里值写成变量如  color: var(–semi-color-text-0);

  通过事件例如点击按钮,切换主题化。

  特殊场景,可以使用  

[theme-mode='dark'] {
.create-detail-modal {
background-image: none;
}
}

复杂版:

  首先在global.less文件中引入semi的样式文件。

  • 启动时初始化主题:

    • 在 main.js 调用了 initTheme ,在页面刚加载时把主题应用到页面(设置 body 属性、写入 localStorage、写入 Vuex)。 // 初始化主题
      const { initTheme } = useThemeHooks();
      initTheme();
    • 主题初始化逻辑在 useThemeHooks:包含 setTheme、getTheme、initTheme 等函数,以及主题常量 COMMINDTHEMESTYLE 和映射 COMMINDFIXEDTHEMEMAP。
  • 主题持久化与广播:

    • 主题会写入 localStorage(通过 Vue.ls),key 为 'semi-theme'(由 Vue.ls 配置的命名空间控制):见 useThemeHooks 中的 setTheme 实现。
    • 同时把 theme 写进 Vuex(store/modules/theme.js),并通过 getters 暴露。
  • 主题驱动方式:

    • 核心是把主题写到 document.body 的 attribute:document.body.setAttribute('theme-mode', theme)(在 setTheme 中)。组件常用两种方式判断当前主题:
    • 读取 Vuex:this.$store?.state?.theme?.theme(若有响应式切换)。
    • 兜底读取 DOM attribute:document.body.getAttribute('theme-mode')(很多组件的 computed 示例,见例如 src/pages/informationService/aiImage/components/ThumbnailImage.vue、src/pages/informationService/aiVideo/components/ThumbnailVideo.vue、src/pages/informationService/aiSearch/homePage.vue)。

具体使用方法:

首页主题载入(头部拦和侧边菜单栏部分)

<a-layout class="layout-wrapper" :class="themeClass">
</a-layout>

<script>
import useThemeHooks from '@/hooks/useThemeHooks';

const { COMMINDFIXEDTHEMEMAP } = useThemeHooks();

export default {
props: {
…HomeHeader.props
},
computed: {
themeClass() {
if (this.fixedTheme) {
return COMMINDFIXEDTHEMEMAP[this.fixedTheme];
}
return '';
},
isInIframe() {
if (this.$route.query.isInIframe === 'true') {
return true;
}
return window.self !== window.top;
}
}
};
</script>

<style lang="less" scoped>
.semi-always-light {
background-size: cover;
background-position: top center;
background-repeat: no-repeat;
background-color: var(–semi-color-bg-0);
}
.semi-always-dark {
background-color: var(–semi-color-bg-0);
background-image: none;
}
.layout-wrapper {
min-width: fit-content;
}
.layout-content {
min-width: fit-content;
}
</style>

 如何切换主题

<a-button @click="setDarkTheme"/>
const { COMMINDTHEMESTYLE, setLightTheme, setDarkTheme } = useThemeHooks();

固定化主题(固定浅色模式)用法

<Layout v-fixed-theme />

自定义组件添加主题化效果

例如下面,在深色模式下取消背景图。或者开发者希望在暗色模式下,做任何特殊化处理,都可以在这添加。

[theme-mode='dark'] {
.create-detail-modal {
background-image: none;
}
}

[theme-mode='dark'] {
.personal-assets-recent .header .type-select .selected-creation-type {
background: var(–semi-color-fill-3);
}
}

赞(0)
未经允许不得转载:网硕互联帮助中心 » Vue2+Antd项目集成Semi主题化实战
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!