實用webpack插件之DefinePlugin

clipboard.png

經過閱讀這篇文章,能夠學習到如何使用DefinePlugin插件使得前端項目更加工程化,說清晰點就是如何使用這個插件,在編譯階段根據NODE_ENV自動切換配置文件,提高前端開發效率。前端

  • DefinePlugin的正確用法
  • 如何使用DefinePlugin添加配置文件,構建期間自動檢測環境變化,也就是如何根據NODE_ENV引入配置文件?

DefinePlugin的正確用法

DefinePlugin中的每一個鍵,是一個標識符或者經過.做爲多個標識符。vue

  • 若是value是一個字符串,它將會被當作code片斷
  • 若是value不是字符串,它將會被stringify(包括函數)
  • 若是value是一個對象,則全部key的定義方式相同。
  • 若是key有typeof前綴,它只是對typeof 調用定義的。

這些值將內聯到代碼中,壓縮減小冗餘。node

new webpack.DefinePlugin({
    PRODUCTION: JSON.stringify(true),
    VERSION: JSON.stringify('5fa3b9'),
    BROWSER_SUPPORTS_HTML5: true,
    TWO: '1+1',
    'typeof window': JSON.stringify('object'),
    'process.env': {
         NODE_ENV: JSON.stringify(process.env.NODE_ENV)
     }
});
console.log('Running App version' + VERSION);

plugin不是直接的文本值替換,它的值在字符串內部必須包括實際引用。典型的狀況是用雙引號或者JSON.stringify()進行引用,'"production"',JSON.stringify('production')。webpack

重點:在vue-cli建立的項目中,凡是src下的文件,均可以訪問到VERSION這個變量,例如main.js,App.vue等等git

咱們如今看一下上面的幾種類型的key值,在代碼中的輸出。github

console.log(PRODUCTION, VERSION, BROWSER_SUPPORTS_HTML5, TWO, typeof window, process.env);
PRODUCTION: true,
VERSION: "5fa3b9",
BROWSER_SUPPORTS_HTML5: true,
TWO: 2,
typeof window: "object",
process.env: {NODE_ENV: "development"},

在代碼中,咱們通常會有如下幾種用途:web

  • 根據process.env.NODE_ENV區分環境
  • 引入配置文件
  • 根據NODE_ENV引入配置文件(這個很重要,後面會講到)
Feature Flag

能夠控制新特性和實驗特性的開關。vue-cli

new webpack.DefinePlugin({
    'NICE_FEATURE': JSON.stringify(true),
    'EXPERIMENTAL': JSON.stringify(false),
})
process.env.NODE_ENV的正確配置方式是什麼?
process: {
    env: {
        NODE_ENV: JSON.stringify('production')
    }
}

評價:很是很差,會overwrite整個process對象,僅僅保留新的NODE_ENV,破壞進程。
原始的process對象包含以下內容 ,包含了當前進程的不少信息。api

process {
  title: 'node',
  version: 'v8.11.2',
  moduleLoadList: 
   [ 'Binding contextify',],
  versions: 
   { http_parser: '2.8.0'},
  arch: 'x64',
  platform: 'darwin',
  release: 
   { name: 'node' },
  argv: [ '/usr/local/bin/node' ],
  execArgv: [],
  env: 
   { TERM: 'xterm-256color'},
  pid: 14027,
  features: 
   { debug: false},
  ppid: 14020,
  execPath: '/usr/local/bin/node',
  debugPort: 9229,
  _startProfilerIdleNotifier: [Function: _startProfilerIdleNotifier],
  _stopProfilerIdleNotifier: [Function: _stopProfilerIdleNotifier],
  _getActiveRequests: [Function: _getActiveRequests],
  _getActiveHandles: [Function: _getActiveHandles],
  reallyExit: [Function: reallyExit],
  abort: [Function: abort],
  chdir: [Function: chdir],
  cwd: [Function: cwd],
  umask: [Function: umask],
  getuid: [Function: getuid],
  geteuid: [Function: geteuid],
  setuid: [Function: setuid],
  seteuid: [Function: seteuid],
  setgid: [Function: setgid],
  setegid: [Function: setegid],
  getgid: [Function: getgid],
  getegid: [Function: getegid],
  getgroups: [Function: getgroups],
  setgroups: [Function: setgroups],
  initgroups: [Function: initgroups],
  _kill: [Function: _kill],
  _debugProcess: [Function: _debugProcess],
  _debugPause: [Function: _debugPause],
  _debugEnd: [Function: _debugEnd],
  hrtime: [Function: hrtime],
  cpuUsage: [Function: cpuUsage],
  dlopen: [Function: dlopen],
  uptime: [Function: uptime],
  memoryUsage: [Function: memoryUsage],
  binding: [Function: binding],
  _linkedBinding: [Function: _linkedBinding],
  _events: 
   { newListener: [Function],
     removeListener: [Function],
     warning: [Function],
     SIGWINCH: [ [Function], [Function] ] },
  _rawDebug: [Function],
  _eventsCount: 4,
  domain: [Getter/Setter],
  _maxListeners: undefined,
  _fatalException: [Function],
  _exiting: false,
  assert: [Function],
  config: {},
  emitWarning: [Function],
  nextTick: [Function: nextTick],
  _tickCallback: [Function: _tickDomainCallback],
  _tickDomainCallback: [Function: _tickDomainCallback],
  stdout: [Getter],
  stderr: [Getter],
  stdin: [Getter],
  openStdin: [Function],
  exit: [Function],
  kill: [Function],
  _immediateCallback: [Function: processImmediate],
  argv0: 'node' }
'process.env': {
    NODE_ENV: JSON.stringify('production')
}

評價:很差,會overwrite整個process.env對象,破壞進程環境,致使破壞兼容性。
原始的process.env對象包含以下內容 ,包含了當前進程的不少信息。bash

{ TERM: 'xterm-256color',
  SHELL: '/bin/bash',
  TMPDIR: '/var/folders/lw/rl5nyyrn4lb0rrpspv4szc3c0000gn/T/',
  Apple_PubSub_Socket_Render: '/private/tmp/com.apple.launchd.dEPuHtiDsx/Render',
  USER: 'frank',
  SSH_AUTH_SOCK: '/private/tmp/com.apple.launchd.MRVOOE7lpI/Listeners',
  __CF_USER_TEXT_ENCODING: '0x1F5:0x19:0x34',
  PATH: '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/Wireshark.app/Contents/MacOS',
  PWD: '/Users/frank/Desktop/corporation/weidian-crm',
  XPC_FLAGS: '0x0',
  XPC_SERVICE_NAME: '0',
  SHLVL: '1',
  HOME: '/Users/frank',
  LOGNAME: 'frank',
  LC_CTYPE: 'zh_CN.UTF-8',
  _: '/usr/local/bin/node' }
'process.env.NODE_ENV': JSON.stringify('production')

評價:好。由於僅僅對NODE_ENV值進行修改,不會破壞完整進程,也不會破壞兼容性。

如何使用DefinePlugin添加配置文件,構建期間自動檢測環境變化,也就是如何根據NODE_ENV引入配置文件?

情景:開發階段的接口地址每每與生產階段的接口地址是不一致的。例如開發時是development.foo.com,而生產時是production.foo.com,若是須要打包發佈,那麼須要手動去替換域名或者是一個分支維護一個專門的配置文件,這兩種方式是很是笨重的。

  • 手動替換

文件效率低下,每次在development和production見切換都須要進行配置文件的更新,容易出錯

  • 配置文件

相對手動替換高級一些,可是不能一次性查看development和production的所有配置信息,須要在分支間切換,效率低下,且不適用於多種環境的配置

  • webpack.DefinePlugin()

全局配置文件,自動檢測環境變化,效率高效。

webpack的DefinePlugin正是爲咱們解決這樣一個問題,它維護一個全局的配置文件,在編譯期間會自動檢測process.env.NODE_ENV,根據當前的環境變量去替換咱們的接口域名。

下面我將以一個實例來介紹如何正確使用webpack.DefinePlugin。

/config/api.js

const NODE_ENV = process.env.NODE_ENV;
const config = {
     production: {
        FOO_API: 'production.foo.api.com',
        BAR_API: 'production.bar.api.com',
        BAZ_API: 'production.baz.api.com',
     },
     development: {
        FOO_API: 'development.foo.api.com',
        BAR_API: 'development.bar.api.com',
        BAZ_API: 'development.baz.api.com',
     },
     test: {
        FOO_API: 'test.foo.api.com',
        BAR_API: 'test.bar.api.com',
        BAZ_API: 'test.baz.api.com',
     }
}
module.exports = config[NODE_ENV];

webpack.dev.conf.js/webpack.prod.conf.js/webpack.test.conf.js

const apiConfig = require('./config/api');
const webpackConfig = {
    plugins: [
        new webpack.DefinePlugin({
            API_CONFIG: JSON.stringify(apiConfig);
        })
    ]
}
...

custom.component.vue

<template>
...
</template>
<script>
// 這裏也能夠訪問到API_CONFIG
export default {
    // 這裏不管是data函數,methods對象,computed對象,watch對象,均可以訪問到API_CONFIG;
   data() {
       return {
           fooApi: API_CONFIG.FOO_API,
           user:{
               id: '',
               name: '',
           },
           hash: '',
        } 
    },
    computed: {
        userAvator() {
            return `${API_CONFIG.BAR_API}?id=${user.id}&name=${user.name}`
        }
    },
    methods: {
        uploadImage() {
            api.uploadImage({user: `${API_CONFIG.BAZ}\${hash}`})
                 .then(()=>{})
                 .catch(()=>{})
        }
    }
}
</script>

上述僅僅適用於vue-cli2.0時代,vue-cli3.0引入了webpack-chain,配置方式大大不一樣,下文將給出示例。

如何在vue.config.js中,使用使用DefinePlugin添加配置文件,構建期間自動檢測環境變化,也就是如何根據NODE_ENV引入配置文件?

vue.config.js

const apiConfig = require('./config/api');

module.exports = {
    chainWebpack: config => {
        config
            .plugin('define')
            .tap(args => { 
                args[0].API_CONFIG = JSON.stringify(apiConfig)
                return args
            })
    }
}

須要注意的是,在vue-cli3.0中,咱們不能直接SET NODE_ENV=production或者EXPORT NODE_ENV=production。
由於vue-cli-servive有3種模式,serve默認爲development,build爲production,若想修改vue-cli-service包中的NODE_ENV,須要經過vue-cli-service serve --mode production進行切換。
就像下面這樣:

{
  "scripts": {
    "dev": "vue-cli-service serve", // mode默認爲development 
    "production": "vue-cli-service serve --mode production", 
  },
}

注意:咱們只能在development, production或者test 3個模式下進行切換,不能引入相似preproduction之類的自定義node環境,可是實際上這3個環境已經足以知足大多數的開發狀況。

爲何vue-cli 3.0中的DefinePlugin能夠用config.plugin('define')修改入參?

源碼文件base.js中,有下面的代碼:

webpackConfig
      .plugin('define')
        .use(require('webpack/lib/DefinePlugin'), [
          resolveClientEnv(options)
        ])

這一點很關鍵!咱們在vue.config.js中拿到的config.plugin('define'),實際上時vue-service內部建立的webpack.DefinePlugin實例的引用 !明確了這一點,咱們在之後加強webpack默認插件配置時,須要先到vue-service的源碼中尋找一番,看看有沒有對應plugin的引用,如有,必須根據vue-service定義的名字直接引用,不然會修改失敗。

相關文章
相關標籤/搜索