angular cli 切换 css_漫谈 Angular 定制主题的四种方式
主題定制是提升用戶體驗最常見的一種,前端框架眾多,主題定制方式卻異曲同工,下面來介紹一下 Angular 中實現主題定制的四種方式。
1. webpack loader
React 版本的 Ant Design 使用 less-loader 加載 globalVars 與 modifyVars 變量,并通過 less 的 render 方法傳遞 callback 到 loader 來實現的項目的主題修改功能。
目前絕大部分的 angular 項目同樣使用 webpack 打包方案。顯然,相同的主題修改方案在 angular 中一樣適用。
webpack 打包 less
- webpack 本身并不具備打包 less 文件的功能,最終實現該部分功能的是 less-loader,該加載器把 less 轉為 CSS,在 webpack 中每個文件或模塊都是有效的 JS 模塊,因此我們還需要 css-loader 將CSS樣式文件轉換為變成 JS 模塊。
- 這時我們已經有了生成的 dist/style.js,在這個模塊中只是將樣式導出為字符串并存放于數組中,我們需要 style-loader 將該數組轉換成 style 標簽。
- 最后我們還需要將 dist/style.js 自動導入 到 html 中,html-webpack-plugin 可以幫我們實現這部分功能。
- 除了以上這些 loader,我們可能還需要 autoprefixer、cssnano 和 postcss-loader 等,有興趣的同學可以自行了解。
modifyVars
上面介紹的 less-loader 可以幫忙我們實現主體定制,通過一下這兩個配置,我們就可以把部分樣式抽出變量,通過不同的變量組合成不同的主題:
custom-webpack
angular-cli 提供了 custom-webpack 的 builder,可以和 angular-cli 合并使用,通過 builder 重寫 webpack 中的 less-loader 的配置,然后利用 modifyVars 實現主題定制。
3.在 angular.json 中使用 @angular-builders/custom-webpack:browser
"architect": {"build": {+ "builder": "@angular-builders/custom-webpack:browser",- "builder": "@angular-builders/build-angular:browser","options": {"customWebpackConfig": {"path": "./extra-webpack.config.js"},"outputPath": "dist/custom-webpack","index": "src/index.html","main": "src/main.ts","polyfills": "src/polyfills.ts","tsConfig": "tsconfig.app.json","assets": ["src/favicon.ico","src/assets"],"styles": ["src/styles.less"]}...} }這樣就可以實現 less 原理的主題定制了,當然 custom-webpack 不僅僅可以做到 less-loader 的重寫,它還可以利用 webpack 實現更多功能,具體研究我們在下一篇文章再來探討;
如果你想進一步了解在 angular cli 中自定義 webpack 打包的方案,可以參考這篇文章
筆者準備好了可以直接使用的源代碼,方便大家查看 點擊查看源碼
純 webpack 打包
如果開發者的項目未使用 Angular CLI,也可以通過同樣的方式實現自己的 webpack 打包器:
筆者準備好了可以直接使用的源代碼,方便大家查看 點擊查看源碼
可能很多開發者并不熟悉 less,開發過程中大多用純 CSS,純 CSS 能否實現主題定制了?答案是肯定的,下面我們來探討一下純 CSS 的主題定制。
2. CSS Variable
CSS3 提供了 Variable, 利用 angular Directive 指令,動態修改 CSS Variable,從而得到主題切換的效果。注意:CSS Variable 支持的瀏覽器可以在 這里 查看
.element{--main-bg-color: brown;} // 聲明局部變量 .element{background-color: var(--main-bg-color);} // 使用局部變量 :root { --global-color: #666; --pane-padding: 5px 42px; } // 聲明全局變量 .demo{ color: var(--global-color); } // 使用全局變量有了以上的的基礎知識,我們很容易想到如何在 angular 中實現基于 css Variable 的主題切換功能,我們只需要一個 Directive 可以根據 @Input 輸入動態切換 style 即可。
1.創建一個指令:ThemeDirective,用來給需要 CSS 變量的標簽添加樣式
import { Directive, ElementRef, Input, OnChanges } from '@angular/core'; @Directive({selector: '[dtTheme]' }) export class ThemeDirective implements OnChanges {@Input('dtTheme') theme: {[prop: string]: string};constructor(private el: ElementRef<HTMLElement>) {}ngOnChanges() {Object.keys(this.theme).forEach(prop => {this.el.nativeElement.style.setProperty(`--${prop}`, this.theme[prop]);});} }2.創建一個組件:app.component.ts
import { Component } from '@angular/core'; @Component({selector : 'app-root',template: `<select (input)="setTheme($event.target.value)" title="theme" class="form-control"><option value="">- select theme -</option><option>green</option><option>pink</option></select><app-trex [dtTheme]="selectedTheme"></app-trex>`,styleUrls : [ './app.component.less' ] }) export class AppComponent {readonly themes = {'green': {'color-main' : '#3D9D46','color-main-darken' : '#338942','color-main-darken2': '#286736','color-main-lighten': '#7BBC4D','color-accent' : '#DC3C2A'},'pink' : {'color-main' : '#E05389','color-main-darken' : '#CA3E86','color-main-darken2': '#C13480','color-main-lighten': '#E77A96','color-accent' : '#208FBC'}};selectedTheme = {};setTheme(val) {this.selectedTheme = this.themes[val];} }3.創建一個trex.component.ts組件
import { Component, OnInit, ViewEncapsulation } from '@angular/core';@Component({selector: 'dt-trex',template: `<div class="class1">aaaa</div><div class="class2">bbb</div><div class="class3">ccc</div><div class="class4">ddd</div>`,styles:`.class1{color:var(--color-main, #ff0000);}.class2{color:var(--color-main-darken);}.class3{color:var(--color-main-darken2);}.class4{color:var(--color-main-lighten);}` }) export class TrexComponent {constructor() { } }CSS 定制主題完成了,筆者準備好了源代碼,方便大家查看,點擊查看源碼
但是這種方式有個缺點,瀏覽器最好支持 CSS3 Variable,如果不支持 CSS3 Variable,那么我還是建議你使用 less 變量。如果你并不想采用 less 的 modifyVars 方式,或者不想重寫 webpack,那么以下這種方式也許適合你。
3. Angular Configuration
Angular 的組件默認工作在 ViewEncapsulation.Emulated 模式下,在這個模式下,應用程序的dom元素都會附加額外的屬性,而 index.html 被添加的 style 會包含這些屬性,從而做到組件樣式的隔離;但是 component 中的樣式,打包后最后會以 JS 形式出現(原理可查看上面 “webpack 打包原理”)。
因此如果想實現主題定制,實際上是需要打多個 angular 的生成包,不過值得高興的是 angular-cli 原生支持同時生成多個 package,我們可以配置 light 和 dark 變量文件,利用 angular-cli 的 builder 打多個主題包,然后利用路由切換不同的主題。
- ViewEncapsulation.Emulated(默認)樣式將被包裝到 style 標簽中,推送到 head 標簽,并唯一標識,以便與組件的模板匹配,樣式將僅用于同一組件中的模板。
- ViewEncapsulation.ShadowDom 全局樣式都不會影響后代組件
- ViewEncapsulation.Native 已棄用
- ViewEncapsulation.None 樣式包裹在 style 標簽中并推送到 head,緊跟在組件內聯和外部樣式之后,屬于全局樣式。
下面簡單介紹一下這種方式的實現流程:
注意:customize_theme 是文件夾名稱,存放于 src/product-configurations/styles/(light|dark)下,利用 angular.json 中的 stylePreprocessorOptions(允許添加額外的基準路徑,這些基準路徑將被檢查予以導入,Import ‘customize_theme’,可以成功導入,再也不用寫很長的../../相對路徑)
@import 'customize_theme';2. 配置 angular.json
注意:升級到 angular8.0 后,configurations 中的 key(如 ligth-theme)不能包含“:”(踩坑),原因這里查看
"configurations": {"light-theme": {"stylePreprocessorOptions": {"includePaths": ["src/styles","src/product-configurations/styles/light"]}},"dark-theme": {"stylePreprocessorOptions": {"includePaths": ["src/styles","src/product-configurations/styles/dark"]}}... }3. 配置 packge.json
{"name": "app","version": "0.0.1","scripts": {"build:light": "ng build --project=app-build --configuration=light-theme","build:dark": "ng build --project=app-build --configuration=dark-theme"}... }這種方式缺點很明顯,需要打包后切換不同語言包,打包時間翻倍,且需要路由來控制語言切換,每次切換語言都要重新加載,性能上比較浪費。既然如此,如何避免這些缺陷了?下面來介紹一種既簡單又性能好的方式。
4. :host-context()
:host-context() 是 webComponents 下的 selector,很多人可能都沒有使用過,但是卻是相對而言最適合的主題切換方式。注意 :host-context() 支持的瀏覽器可以在 這里 查看
:host-context(.theme-light) h2{// 基于當前組件向上查找 .theme-light,有則應用到組件的 h2 中 }下面來介紹一下實現這種主題定制的流程:點擊查看源碼
2. 修改 dark.less 和 light.less 文件
@html-selector: html; @primary-color: blue; @html-selector: html; @primary-color: red;3. 配置全局樣式 styles.less 文件
.themeMixin(@rules) {:host-context(.dark) {@import "theme-dark";@rules();}:host-context(.light) {@import "theme-light";@rules();} }4. 配置 app.component.less 應用
@import "../styles"; .themeMixin({p {color: @primary-color;} });5. 在瀏覽器中給 body 添加 class='dark|light',即可看到效果。
以上方式可以實現 less 的主題動態切換,無需打包和設置路由,但是 :host-context() 和 :host 混用,會有些問題,具體可查看這里。
其他組件中有主題概念,需要用 themeMixin 包起來使用,此外 @html-selector 變量可以實現兩種主題共同存在,如果你需要的話。
對比以上四種方式
- webpack loader:瀏覽器都支持,需多次打包,支持:host混用,流程比較復雜;
- CSS Variable:Chrome 49以上、FireFox 31以上、Safari 9.1以上、IE不支持,1次打包,支持:host混用,流程簡單直接
- Angular Configuration:瀏覽器都支持,需多次打包,支持:host混用,流程簡單直接
- :host-context():Chrome 54以上、opera 41以上,FireFox 、Safari、IE不支持,1次打包 ,不支持:host混用,流程比較復雜
總結
以上是生活随笔為你收集整理的angular cli 切换 css_漫谈 Angular 定制主题的四种方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 应用系统怎么开启审计功能_vivo开启A
- 下一篇: 开源资产管理系统_开源cmdb来啦 通用