10

条件编译,是指 用同一套代码和同样的编译构建过程,根据设置的条件,选择性地编译指定的代码,从而输出不同程序的过程。一般用在C++、Java、C#这样的编译执行语言。对于前端javascript、typescript、vue、css、scss、html等代码,我们也可以使用基于webpackjs-conditional-compile-loader插件实现类似的条件编译功能。

应用场景举例

我们经常会遇到类似这样的需求:

  • 代码需要根据运行环境,运行不同的代码。比如,测试环境可以在页面上浮层显示调试信息,生产环境则不提示;同时又不希望输出的代码中存在判断环境的if-else代码使程序包体积增大。
  • 项目交付给多个客户使用,而某些客户会有一些定制模块。这些定制模块只给特定用户使用,不希望也一起打包在不相干客户的程序包中,但也不希望给定制客户单独维护一个特殊项目而增加维护成本。然后使用参数构建不同程序:如npm run build --ali构建ali用的程序包,npm run build --tencent构建tencent用的程序包。
  • 前端为了调试,引用了很多mock数据。但是生产环境打包时也包含了这些mock数据和mock插件,增大了代码包体积。希望生产环境程序包完全不含这些(即使是异步按需加载也不要)。

使用条件编译的方法,可以优雅地解决这样的问题,发布的程序包中不会有多余的代码存在,同时代码维护也方便,。

image

插件原理

js-conditional-compile-loader是一个webpack的loader插件,它支持js等各种代码文件(只要是文本文件都可以用,如javascript的js文件、typescript的ts文件、vue文件、css、scss、less文件等等),只需在webpack的rules针对文件类型把它作为最先加载的loader即可。可参考:此配置

它会在webpack处理各种代码文件之前,将代码内容根据设置的条件进行修改,去掉当前条件下不需要的代码,保留需要的代码,从而实现条件编译的功能。

使用步骤

可参考这里的中文文档

1. 安装

npm i -D js-conditional-compile-loader

2. 配置webpack

在rules中为js、vue、ts、scss等文件添加loader,作为第一步处理js文件,并配置编译条件。css配置略复杂,可参考:此配置

const conditionalCompiler = {
    loader: 'js-conditional-compile-loader',
    options: {
        isDebug: process.env.NODE_ENV === 'development', // optional, this expression is default
        envTest: process.env.ENV_CONFIG === 'test', // any prop name you want, used for /* IFTRUE_evnTest ...js code... FITRUE_evnTest */
        myFlag: process.env.npm_config_myflag, // enabled by `npm run build --myflag`
    }
}

module.exports = {
    // others...
    module: {
        rules: [
            {
                test: /\.vue$/,
                use: ['vue-loader', conditionalCompiler],
            },
            {
                test: /\.js$/,
                include: [resolve('src'), resolve('test')],
                use: [
                    //step-2
                    'babel-loader?cacheDirectory',
                    //step-1
                    conditionalCompiler,
                ],
            },
            // others...
        ]
    }
}

3. 项目代码中使用

插件支持IFDEBUG和IFTRUE两个条件编译指令。用法是:在js代码的任意地方以/*IFDEBUG/*IFTRUE_xxx开头,以FIDEBUG*/FITRUE_xxx*/结尾,中间是被包裹的js代码。xxx是在webpack中指定的条件属性名,如上面的myFlag。

举个例子,

源码:

我们用这样的源代码:

/* IFTRUE_forAlibaba */
var aliCode = require('./ali/alibaba-business.js')
aliCode.doSomething()
/* FITRUE_forAlibaba */

$state.go('win', {dir: menu.winId /*IFDEBUG , reload: true FIDEBUG*/})

条件1输出

当webpack中插件的options配置为{isDebug: true, forAlibaba: true}时,构建后输出的内容:

var aliCode = require('./ali/alibaba-business.js')
aliCode.doSomething()

$state.go('win', {dir: menu.winId, reload: true })

条件2输出

当webpack中插件的options配置为{isDebug: false, forAlibaba: false}时,构建后输出的内容为:

$state.go('win', {dir: menu.winId})

如此便实现了条件编译。结合命令参数关联到环境变量,可以用命令参数指定不同的编译条件。例如本文中的配置条件:myFlag: process.env.npm_config_ali,执行命令时带上--ali即可激活为truenpm run build --ali
实际项目中可以按照你的需要尽情发挥。

vue、css示例

<temeplate>
    <div>
        /* IFTRUE_myFlag
        <h2>This is a test! For HTML</h2>
        <pre>
            {{$attrs.info || ''}}
        </pre>
        FITRUE_myFlag */
    </div>
</temeplate>

<script>
    var vueComponent = {
        data: {
            /* IFTRUE_myFlag
            falgData: 'Flag Data',
            FITRUE_myFlag */
        },
    };
</script>

/* IFTRUE_myFlag*/
<style scoped>
    .any-where-test {
        color: red;
    }
</style>
/* FITRUE_myFlag*/


<style id="a" scoped>
    /* IFTRUE_myFlag*/
    .test-for-css {
        color: red;
    }
    /*FITRUE_myFlag */
</style>

示例项目

这里是个完整项目的例子,可供参考。


okfine
512 声望29 粉丝

A front-end enthusiast!