美文网首页让前端飞Web前端之路Vue
前端工程化开发中“国际化和主题切换”方案小记

前端工程化开发中“国际化和主题切换”方案小记

作者: BeautifulHao | 来源:发表于2019-09-14 10:54 被阅读0次

    1、前言

    ​这些年来,前端伴随着Vue和React等等一些框架的出现,开发方式发生了日新月异的变化。笔者从业10多年,组件方面使用过早期的微软Asp.Net的服务器渲染组件、JAVA体系的JSP模板控件,用过基于Jquery的众多框架(EasyUI、JqueryUI)组件,ExtJS,Dojo等等。虽然这些前端组件(或者叫做框架)层出不穷,但其基本思想是一致的,都是对浏览器端的Dom进行操作或者操作的优化。诚然这么说,似乎对新框架特点的概括的太笼统,尤其在前端工程化开发大规模推广的情况下,现在的新框架和之前的以jquery为主的前端开发模式已经不是一个级别的了。一个信息系统软件开发团队人员的组成,尤其能看出这点,前端工程师越来越多,而且对前端的依赖越来越大。一个技术或者一个系统复杂以后,往往以分而治之来解决面临的问题,“前后端”分离的开发方式,也是在近几年慢慢提出和被很多开发团队使用。而且SPA(单页)系统的出现,似乎对理解前端开发又造成了很大的困扰。

    ​说了这么多变化,那么现在复杂的信息系统,前端之于业务模块的组成形态到底有没有变化呢。或者说,面对现在流行的微服务框架,前端的应用场景又是如何呢。

    ​近期正好一个大的生产系统面临着前端大改造,系统大概包括一个平台(页面容器)、若干个业务模块。各个业务模块需要以菜单的形式注册到平台,且在平台内部嵌套打开(tab形式,作为一个整体)。

    集成.png

    ​考察了很多开源开发框架,往往一个前端工程,即包含基础模块,又包含业务模块,而且一个系统就一个SPA,所谓大单页应用。但是,真实的研发团队,往往有很多独立的微服务开发小组组成,每个团队有自己的技术体系。如果按照一个大单页的形式组织开发,似乎跨团队的集成打包交叉很多,而且分块更新的流程似乎也很复杂。那么自然最合适的方式是每个业务模块以一个SPA应用的形式开发,由各个团队负责(平台亦是如此),最终各个模块统一以一定形式集成到平台内。所以很多开源开发框架往往不太适合复杂信息系统的研发方式。在现行架构下,无法做到一个复杂系统是一个SPA这种业态。

    ​那么更好的方式,其实还是类似之前复杂系统的处理方式,因为要集成各个业务模块的内嵌,平台前端容器其实还是个iframe容器,唯一区别的是,业务模块划分后,每个模块可以以独立SPA的形式存在,我想这也是微服务改造的初衷吧。

    ​确定了这种模块组成形式,业务模块和容器模块的集成,就成了影响系统统一性的关键所在。在这方面,有两块内容,在前端开发框架设计时需要着重考虑。这也是今天行文的重点,一块是统一的国际化处理;另一块的统一的主题切换。

    ​废话说了这么多,切入重点。本文以React+Antd+Umijs这一套前端方案为基础,分享演示一个工程化前端开发过程中主题切换和国际化处理的方案。先上示例切换效果图:

    theme-change.gif

    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文件即可。


    多主题.png
    多主题2.png

    加载指定主题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/

    相关文章

      网友评论

        本文标题:前端工程化开发中“国际化和主题切换”方案小记

        本文链接:https://www.haomeiwen.com/subject/mmjtyctx.html