1. 程式人生 > >前端工程化開發中“國際化和主題切換”方案小記

前端工程化開發中“國際化和主題切換”方案小記

1、前言

​這些年來,前端伴隨著Vue和React等等一些框架的出現,開發方式發生了日新月異的變化。筆者從業10多年,元件方面使用過早期的微軟Asp.Net的伺服器渲染元件、JAVA體系的JSP模板控制元件,用過基於Jquery的眾多框架(EasyUI、JqueryUI)元件,ExtJS,Dojo等等。雖然這些前端元件(或者叫做框架)層出不窮,但其基本思想是一致的,都是對瀏覽器端的Dom進行操作或者操作的優化。誠然這麼說,似乎對新框架特點的概括的太籠統,尤其在前端工程化開發大規模推廣的情況下,現在的新框架和之前的以jquery為主的前端開發模式已經不是一個級別的了。一個資訊系統軟體開發團隊人員的組成,尤其能看出這點,前端工程師越來越多,而且對前端的依賴越來越大。一個技術或者一個系統複雜以後,往往以分而治之

來解決面臨的問題,“前後端”分離的開發方式,也是在近幾年慢慢提出和被很多開發團隊使用。而且SPA(單頁)系統的出現,似乎對理解前端開發又造成了很大的困擾。

​說了這麼多變化,那麼現在複雜的資訊系統,前端之於業務模組的組成形態到底有沒有變化呢。或者說,面對現在流行的微服務框架,前端的應用場景又是如何呢。

​近期正好一個大的生產系統面臨著前端大改造,系統大概包括一個平臺(頁面容器)、若干個業務模組。各個業務模組需要以選單的形式註冊到平臺,且在平臺內部巢狀開啟(tab形式,作為一個整體)。

​考察了很多開源開發框架,往往一個前端工程,即包含基礎模組,又包含業務模組,而且一個系統就一個SPA,所謂大單頁應用。但是,真實的研發團隊,往往有很多獨立的微服務開發小組組成,每個團隊有自己的技術體系。如果按照一個大單頁的形式組織開發,似乎跨團隊的整合打包交叉很多,而且分塊更新的流程似乎也很複雜。那麼自然最合適的方式是每個業務模組以一個SPA應用的形式開發,由各個團隊負責(平臺亦是如此),最終各個模組統一以一定形式整合到平臺內。所以很多開源開發框架往往不太適合複雜資訊系統的研發方式。在現行架構下,無法做到一個複雜系統是一個SPA這種業態。

​那麼更好的方式,其實還是類似之前複雜系統的處理方式,因為要整合各個業務模組的內嵌,平臺前端容器其實還是個iframe容器,唯一區別的是,業務模組劃分後,每個模組可以以獨立SPA的形式存在,我想這也是微服務改造的初衷吧。

​確定了這種模組組成形式,業務模組和容器模組的整合,就成了影響系統統一性的關鍵所在。在這方面,有兩塊內容,在前端開發框架設計時需要著重考慮。這也是今天行文的重點,一塊是統一的國際化處理;另一塊的統一的主題切換。

​廢話說了這麼多,切入重點。本文以React+Antd+Umijs這一套前端方案為基礎,分享演示一個工程化前端開發過程中主題切換和國際化處理的方案。先上示例切換效果圖:

2、原始碼及使用方式

​目前前端基於React和Antd的專案越來越多,而且開源腳手架Umijs的使用似乎也成為中小型專案的必選。先把今天要分享的方案程式碼標記和說明下:

gitee:https://gitee.com/BeautifulHao/theme-demo-app

//本地演示
git clone https://gitee.com/BeautifulHao/theme-demo-app.git
cd theme-demo-app
yarn install
yarn start

3、國際化方案

​國際化是一個全域性性的設定,所以作為單頁應用,最理想的設定地方最好是最外層的包裝元件,而結合umijs,最合適的就是layout元件,或者說是route的最外層巢狀元件設定全域性國際化。react的國際化有兩個庫,一個是react-intl,另一個是react-intl-universal,本文介紹的是react-intl-universal。

ant design 元件國際化

結合官方的文件:https://ant.design/docs/react/i18n-cn,列舉專案原始碼說明src/layouts/index.js


import React, { PureComponent } from 'react'
//antd國際化包裝元件
import { ConfigProvider } from 'antd';
import intl from 'react-intl-universal';
//antd國際化資源
import zhCN from 'antd/es/locale-provider/zh_CN';
import enUS from 'antd/es/locale-provider/en_US';
//本地國際化資源
import enTranslationsData from '../locale/en-US';
import zhTranslationsData from '../locale/zh-CN';
//國際化鍵常量和基於cookie全域性設定方法
import { Locale_en_US, Locale_zh_CN, getCookieLocale } from '../utils/i18n';

//包含常規一些國際化字串
require('intl/locale-data/jsonp/en.js');
require('intl/locale-data/jsonp/zh.js');

const AntdTranslations = {
  [Locale_zh_CN]: zhCN,
  [Locale_en_US]: enUS,
}

class BasicLayout extends PureComponent {

  state = {initDone: false}

  componentWillMount () {
    //通過平臺容器集設定的cookie獲取當前全域性環境的國際化設定
    const currentLocale = getCookieLocale();
    //初始化react-intl-universal 元件國際化設定
    intl.init({
      currentLocale,
      locales: {
        [Locale_zh_CN]: { ...zhTranslationsData },
        [Locale_en_US]: { ...enTranslationsData },
      }
    }).then(() => {
      this.setState({initDone: true});
    });
  }

  render () {
    const currentLocale = getCookieLocale();
    return (
      //按照antd官網設定國際化包裝器ConfigProvider
      this.state.initDone &&
          <ConfigProvider locale={AntdTranslations[currentLocale]}>{this.props.children}</ConfigProvider>
    )
  }
}

本地國際化資源格式(../locale/en-US):

const enUS ={
  "test.helloworld": "hello world",
  "test.show":"hello,I'm from english context!",
  "test.buttom":"test buttom",
  "test.themeAndLocale":'Please Select The Theme & Locale:',
  "test.pageShow":'page show:'
}

export default enUS

業務元件國際化使用

//引入元件
import intl from 'react-intl-universal';
//呼叫方法獲取
intl.get('test.themeAndLocale')

4、主題切換方案

可行方案介紹

  • css換膚:生成多套主題css,在頁面載入時根據主題選擇載入指定css

  • less換膚:採用動態Less機制,通過js動態編譯less變數修改css(可參考:less換膚)

css主題切換介紹

從效果來說,動態Less換膚優於css換膚(頁面不重新整理),但對於單個業務模組而言,雖然作為SPA應用,但是模組內多個功能地址都將作為選單獨立開啟,每次載入一個頁面都將動態編譯一次less,效能將有很大的消耗。所以在行文開頭說明的複雜系統的場景下less換膚不是一個最優的選擇。那麼css換膚呢,綜合考量了下,的確還是能夠達到預期的效果的,尤其是antd一個主色就能搞定一個主題的場景(antd),而且主題切換往往就是antd面板切換。所以要實現主題切換,就得從antd下手。

生成多份主題檔案

Antd 的樣式 CSS 檔案可以通過 link 標籤引入,而該專案又要求提供多套Antd 的主題樣式。首先想到的 能否直接去官網能否直接定製並下載 CSS 檔案。然而答案是 否定 的。但是作為業界優秀的開源專案,Antd 提供了自定義定製主題樣式的方法,在官網 定製主題 就有詳細的說明,然而 Antd 卻只提供了 LESS 樣式定製的功能,並沒有提供現成的生成 CSS 樣式檔案定製並下載的功能。

那麼自然可行的辦法就是:

  • 獲取官方標準的 LESS 主檔案
  //一般就在當前專案下
  node_modules/antd/dist/antd.less
  • 隨後自定義一份 LESS 檔案,引入該主檔案後,根據需求指定的樣式變數進行覆蓋
  @import "../node_modules/antd/dist/antd.less";   
// Import Ant Design styles by less entry
  
  @primary-color: #1890ff;
  • 利用 lessc 工具最終編譯出所需要的 CSS 檔案
lessc -js theme-demo-app-default.less ../public/theme/theme-demo-app-default.css

根據上面的思路,我們只需要多做幾份不同@primary-color顏色的css檔案即可。

載入指定主題css

從單頁應用入口檔案index.html下手,在頁面載入之初去載入指定css,結合umijs的html模板檔案,我們可以按照如下方式處理:document.ejs

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Theme-Demo Application</title>
  <script>
    window.publicPathString = "<%= context.publicPath %>"
    document.title = "<%= context.config.plugins[0][1].title %>"
  </script>
  <script src="<%= context.publicPath %>theme/append-theme.js"></script>
</head>
<body>
  <div id="root"></div>
</body>
</html>
// append-theme.js
//根據cookie動態新增sup-ui的樣式檔案
var themeHandle = {
  getCookie: function(name) {
    name = name + "=";
    var decodedCookie = decodeURIComponent(document.cookie);
    var ca = decodedCookie.split(';');
    for(var i = 0; i <ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0) === ' ') {
            c = c.substring(1);
         }
         if (c.indexOf(name) === 0) {
            return c.substring(name.length, c.length);
         }
     }
    return undefined;
  },
  appendTheme: function() {
    var publicPath = window.publicPathString;
    var theme = this.getCookie('theme');
    theme = theme ? theme : 'default'
    var link = document.createElement('link');
    link.type = 'text/css';
    link.id = 'theme-' + theme;
    link.rel = 'stylesheet';
    link.href = publicPath + 'theme/theme-demo-app-' + theme + '.css';
    document.getElementsByTagName('head')[0].appendChild(link);
  },
};

themeHandle.appendTheme();

主要的過程就是從cookie中獲取指定主題設定,然後載入不同的antd主題css.

參考:

https://www.jianshu.com/p/35e0581629d2
https://boycgit.github.io/customize-antd-css/