source

Vue.js를 사용한 사용자 지정 테마

factcode 2022. 8. 12. 23:15
반응형

Vue.js를 사용한 사용자 지정 테마

다양한 테마를 가진 VueJS 앱을 가지고 있습니다(최소 20개 정도).각 테마 스타일시트는 색상이나 글꼴 크기뿐만 아니라 일부 요소의 위치와 레이아웃도 변경합니다.

이러한 테마를 동적으로 전환할 수 있도록 하고 싶다.따라서 실행 시 사용자는 Options 메뉴를 열고 드롭다운에서 선택할 수 있습니다.

VueJ에서 사용자가 동적으로 선택할 수 있는 테마를 많이 가질 수 있는 가장 깨끗한 방법은 무엇입니까?


다음과 같은 몇 가지 방법을 생각해 보았습니다.

  • <link> ★★★★★★★★★★★★★★★★★」<style>태그는 동작할 수 있지만, 특별히 「깨끗하다」라고는 생각하지 않습니다.AJAX에서 로딩하면, 자주 FUC를 볼 수 있습니다.
  • 단순히 계산된 속성을 통해 Vue 클래스 바인딩을 변경합니다., .if-else모든 컴포넌트에서 지원되는 모든 테마에 대해 체인을 설정합니다.나중에 새로운 테마를 추가할 때마다 모든 컴포넌트를 업데이트해야 하기 때문에 이 솔루션은 특별히 마음에 들지 않습니다.

리액트에 아마 플러그인 같은 게 있는 것 같은데<ThemeProvider> 여기서 테마를 추가하는 것은 랩핑하는 것만큼이나 간단합니다.<ThemeProvider theme={themeProp}><MyComponent></ThemeProvider>이 테마의 모든 스타일은 해당 컴포넌트와 모든 하위 컴포넌트에 적용됩니다.

VueJ에도 유사한 기능이 있습니까?아니면 구현 방법이 있습니까?

이거 재밌었다고 인정할게요.이 솔루션은 Vue에 의존하지 않지만 Vue에서 쉽게 사용할 수 있습니다.자, 갑니다!

.<link>FOUC를 초래하지 않는 스타일시트.

클래스(기술적으로는 컨스트럭터 함수이지만, 무슨 말인지 아시겠지만)를 만들었습니다.ThemeHelper뭇매를 맞다

  • myThemeHelper.add(themeName, href) 됩니다.hrefURL)이 있는 (stylesheet.disabled = true이름을 붙입니다(단순히 추적하기 위해서).됩니다.PromiseCSSTyleSheet로 해결됩니다.onload출됩니니다다
  • myThemeHelper.theme = "<theme name>"합니다.이전 테마는 실행 중지되고 지정된 테마는 실행 중지됩니다. 되어 있기 때문에 됩니다..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>

나도 즐거웠던 만큼 너도 도움이 됐으면 좋겠어!

오늘 이 문제를 해결할 수 있는 가장 간단한 방법을 찾았습니다.또한 SCSS에서도 동작합니다(테마마다 개별 CSS가 필요 없습니다.이것은 테마가 1개의 라이브러리를 기반으로 하고 있고 변경사항만 정의하려는 경우에 중요합니다).

  1. 각 테마에 대해 .scss/.css 파일을 만듭니다.
  2. 예를 들어 src/bootstrap-temes/dark.scs 폴더 내에서 이러한 파일을 사용할 수 있도록 합니다.
  3. App.vue의 조건을 지정하여 .scss를 Import합니다.created:,예를들면
if (Vue.$cookies.get('darkmode') === 'true') {
     import('../bootstrap-themes/dark.scss');
     this.nightmode = true;
} else {
     import('../bootstrap-themes/light.scss');
     this.nightmode = false;
}

사용자가 페이지를 열었을 때 쿠키를 읽고 마지막으로 나갔을 때 야간 모드가 활성화되어 있는지 확인하고 올바른 scs를 로드합니다.

스위치를 사용하여 테마를 변경하면 이 메서드가 호출됩니다.이 메서드는 쿠키를 저장하고 페이지를 새로고침합니다.이 메서드는 쿠키를 읽고 올바른 scs를 로드합니다.

setTheme(nightmode) {
     this.$cookies.set("darkmode", nightmode, "7d")
     this.$router.go()
}

매우 심플하고 효과적인 접근법 중 하나는 몸의 css 클래스를 동적으로 변경하는 것입니다.

이건 어때?

https://www.mynotepaper.com/create-multiple-themes-in-vuejs

그리고 이거는

https://vuedose.dev/sys/theming-using-custom-properties-in-vuejs-components/

그것이 당신의 프로젝트에 대한 기본적인 아이디어를 줄 것이라고 생각합니다.

우선 Continuous Load의 고무적인 코드 조각에 감사드립니다.나만의 주제 선곡기를 만드는 데 많은 도움이 되었어요.피드백을 드리고 원래 코드에 대한 변경 사항을 공유하고자 합니다. 특히 함수 프리로드에서요.주제. 가장 큰 변경은 link.disabled 값을 변경할 때마다 최소한 Firefox에서는 다시 실행되기 때문에 초기 로드 후에 onload() 이벤트청취자를 삭제하는 것입니다.도움이 되었으면 좋겠다:)

const ThemeHelper = function() {
  const preloadTheme = href => {
    let link = document.createElement('link');
    link.rel = 'stylesheet';
    link.disabled = false;
    link.href = href;

    return new Promise((resolve, reject) => {
      link.onload = function() {
        // Remove the onload() event listener after initial load, because some
        // browsers (like Firefox) could call onload() later again when changing
        // the link.disabled value.
        link.onload = null;
        link.disabled = true;
        resolve(link);
      };
      link.onerror = event => {
        link.onerror = null;
        reject(event);
      };
      document.head.appendChild(link);
    });
  };

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

  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>

언급URL : https://stackoverflow.com/questions/46730904/user-switchable-custom-themes-with-vue-js

반응형