zoukankan      html  css  js  c++  java
  • vue-element-admin学习笔记--F5刷新页面导致主题失效问题

    今天在vue-element-admin学习笔记--Setting保存到cookie笔记里有网友留言关于切换主题后,在刷新页面主题样式就失效的问题。之前没太注意,只记得是联网切换(只替换颜色)和自定义主题切换两种。这里说的是只替换颜色的形式。
    按照vue-element-admin的文档中所述:

    element-ui 2.0 版本之后所有的样式都是基于 SCSS 编写的,所有的颜色都是基于几个基础颜色变量来设置的,所以就不难实现动态换肤了,只要找到那几个颜色变量修改它就可以了。 首先我们需要拿到通过 package.json 拿到 element-ui 的版本号,根据该版本号去请求相应的样式。拿到样式之后将样色,通过正则匹配和替换,将颜色变量替换成你需要的,之后动态添加 style 标签来覆盖原有的 css 样式。
    于是仔细看了下ThemePicker组件的换肤方法,于是考虑临时的实现思路如下:

    • 主题颜色从布局layoout的index开始加载(定义一个方法然后mount阶段执行),这样刷新页面后就可以加载主题颜色
    • 从cookie中取主题没有的话就取默认的
    • 在布局layout的index中,根据ThemePicker中的换肤的代码,在线获取换肤的主题颜色并替换,可以参考源码中的getThemeCluster()方法和getCSSString()
    • 这样每次刷新页面就可以获取保存的皮肤了

    根据这样的思路,于是修改自己demo中的srclayoutsindex.vue文件,这里的demo使用的是vue-element-admin-i18n版本的源码为基准,部分内容有改动,所以目录结构是一样的。代码如下:

    <template>
      <div :class="classObj"
           class="app-wrapper">
        <div v-if="device === 'mobile' && sidebar.opened"
             class="drawer-bg"
             @click="handleClickOutside" />
        <sidebar class="sidebar-container" />
        <div :class="{ hasTagsView: needTagsView }"
             class="main-container">
          <div :class="{ 'fixed-header': fixedHeader }">
            <navbar />
            <tags-view v-if="needTagsView" />
          </div>
          <app-main />
          <right-panel v-if="true">
            <settings />
          </right-panel>
        </div>
      </div>
    </template>
    
    <script>
    import { mapState } from "vuex";
    import RightPanel from "@/components/RightPanel";
    import Settings from "./components/Settings";
    
    import AppMain from "./components/AppMain";
    import Navbar from "./components/NavBar";
    import Sidebar from "./components/Sidebar";
    import TagsView from "./components/TagsView";
    // import ResizeMixin from "./mixin/ResizeHandler";
    import Cookies from "js-cookie";
    
    //加载皮肤用
    const version = require("element-ui/package.json").version; // element-ui version from node_modules
    const ORIGINAL_THEME = "#409EFF"; // default color
    
    export default {
      name: "Layout",
      components: { AppMain, Navbar, Sidebar, TagsView, RightPanel, Settings },
      // mixins: [ResizeMixin],
      computed: {
        ...mapState({
          sidebar: state => state.app.sidebar,
          device: state => state.app.device,
          showSettings: state => state.settings.showSettings,
          needTagsView: state => state.settings.tagsView,
          fixedHeader: state => state.settings.fixedHeader
        }),
        classObj () {
          return {
            hideSidebar: !this.sidebar.opened,
            openSidebar: this.sidebar.opened,
            withoutAnimation: this.sidebar.withoutAnimation,
            mobile: this.device === "mobile"
          };
        }
      },
      mounted () {
        //取cooike中的换肤
        this.handleUserTheme();
      },
      methods: {
        handleClickOutside () {
          this.$store.dispatch("app/closeSideBar", { withoutAnimation: false });
        },
        //换皮肤 防止F5后皮肤丢失
        async handleUserTheme () {
          let val = Cookies.get("theme");
          const oldVal = this.chalk ? this.theme : ORIGINAL_THEME;
          if (typeof val !== "string") return;
          const themeCluster = this.getThemeCluster(val.replace("#", ""));
          const originalCluster = this.getThemeCluster(oldVal.replace("#", ""));
          console.log(themeCluster, originalCluster);
          const getHandler = (variable, id) => {
            return () => {
              const originalCluster = this.getThemeCluster(
                ORIGINAL_THEME.replace("#", "")
              );
              const newStyle = this.updateStyle(
                this[variable],
                originalCluster,
                themeCluster
              );
    
              let styleTag = document.getElementById(id);
              if (!styleTag) {
                styleTag = document.createElement("style");
                styleTag.setAttribute("id", id);
                document.head.appendChild(styleTag);
              }
              styleTag.innerText = newStyle;
            };
          };
    
          if (!this.chalk) {
            const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`;
            await this.getCSSString(url, "chalk");
          }
    
          const chalkHandler = getHandler("chalk", "chalk-style");
    
          chalkHandler();
    
          const styles = [].slice
            .call(document.querySelectorAll("style"))
            .filter(style => {
              const text = style.innerText;
              return (
                new RegExp(oldVal, "i").test(text) && !/Chalk Variables/.test(text)
              );
            });
          styles.forEach(style => {
            const { innerText } = style;
            if (typeof innerText !== "string") return;
            style.innerText = this.updateStyle(
              innerText,
              originalCluster,
              themeCluster
            );
          });
    
        },
    
        getThemeCluster (theme) {
          const tintColor = (color, tint) => {
            let red = parseInt(color.slice(0, 2), 16);
            let green = parseInt(color.slice(2, 4), 16);
            let blue = parseInt(color.slice(4, 6), 16);
    
            if (tint === 0) {
              // when primary color is in its rgb space
              return [red, green, blue].join(",");
            } else {
              red += Math.round(tint * (255 - red));
              green += Math.round(tint * (255 - green));
              blue += Math.round(tint * (255 - blue));
    
              red = red.toString(16);
              green = green.toString(16);
              blue = blue.toString(16);
    
              return `#${red}${green}${blue}`;
            }
          };
    
          const shadeColor = (color, shade) => {
            let red = parseInt(color.slice(0, 2), 16);
            let green = parseInt(color.slice(2, 4), 16);
            let blue = parseInt(color.slice(4, 6), 16);
    
            red = Math.round((1 - shade) * red);
            green = Math.round((1 - shade) * green);
            blue = Math.round((1 - shade) * blue);
    
            red = red.toString(16);
            green = green.toString(16);
            blue = blue.toString(16);
    
            return `#${red}${green}${blue}`;
          };
    
          const clusters = [theme];
          for (let i = 0; i <= 9; i++) {
            clusters.push(tintColor(theme, Number((i / 10).toFixed(2))));
          }
          clusters.push(shadeColor(theme, 0.1));
          return clusters;
        },
    
        updateStyle (style, oldCluster, newCluster) {
          let newStyle = style;
          oldCluster.forEach((color, index) => {
            newStyle = newStyle.replace(new RegExp(color, "ig"), newCluster[index]);
          });
          return newStyle;
        },
    
        getCSSString (url, variable) {
          return new Promise(resolve => {
            const xhr = new XMLHttpRequest();
            xhr.onreadystatechange = () => {
              if (xhr.readyState === 4 && xhr.status === 200) {
                this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, "");
                resolve();
              }
            };
            xhr.open("GET", url);
            xhr.send();
          });
        },
      }
    };
    </script>
    
    <style lang="scss" scoped>
    @import "~@/styles/mixin.scss";
    @import "~@/styles/variables.scss";
    
    .app-wrapper {
      @include clearfix;
      position: relative;
      height: 100%;
       100%;
    
      &.mobile.openSidebar {
        position: fixed;
        top: 0;
      }
    }
    
    .drawer-bg {
      background: #000;
      opacity: 0.3;
       100%;
      top: 0;
      height: 100%;
      position: absolute;
      z-index: 999;
    }
    
    .fixed-header {
      position: fixed;
      top: 0;
      right: 0;
      z-index: 9;
       calc(100% - #{$sideBarWidth});
      transition: width 0.28s;
    }
    
    .hideSidebar .fixed-header {
       calc(100% - 54px);
    }
    
    .mobile .fixed-header {
       100%;
    }
    </style>
    
    

    代码可以对比vue-element-admin的源码来看,这里将ThemePicker中更换主题颜色的方法复制过来并修改,可以临时满足刷新页面能够保存主体颜色的功能。但有以下几个问题,可以进行优化:

    • 在加载布局前先获取主题然后缓存起来,这样就不必每次刷新都请求网络了
    • 把样式相关的缓存都从cookie改到localstorage里存储
    • 参考ThemePicker中的async theme(val)方法,感觉可以改成定义成单独的组件,混入的方式加载到页面里。或者有其他更好的方法,因为我对于vue还处于简单的使用阶段,挺多功能不太熟练。
    • 因为现在是要在线获取主题然后在替换,所以页面会闪一下原来的主题。如果改成提前加载或者提前从localstorage里取,然后在替换就可以解决这个问题了
      暂时只想到这些,欢迎补充讨论。
  • 相关阅读:
    Construct Binary Tree from Preorder and Inorder Traversal
    Construct Binary Tree from Inorder and Postorder Traversal
    Maximum Depth of Binary Tree
    Sharepoint 2013 创建TimeJob 自动发送邮件
    IE8 不能够在Sharepoint平台上在线打开Office文档解决方案
    TFS安装与管理
    局域网通过IP查看对方计算机名,通过计算机名查看对方IP以及查看在线所有电脑IP
    JS 隐藏Sharepoint中List Item View页面的某一个字段
    SharePoint Calculated Column Formulas & Functions
    JS 两个一组数组转二维数组
  • 原文地址:https://www.cnblogs.com/GYoungBean/p/13727405.html
Copyright © 2011-2022 走看看