By reading this article, you can learn how to use DefinePlugin to make front-end projects more engineering. Specifically, how to use the DefinePlugin to automatically switch configuration files according to NODE_ENV at compile time to improve front-end development efficiency.

  • The correct use of DefinePlugin
  • How do I add configuration files using DefinePlugin to automatically detect environment changes during build, i.e. how do I introduce configuration files based on NODE_ENV?

The correct use of DefinePlugin

Each key in DefinePlugin is an identifier or pass. As multiple identifiers.

  • If value is a string, it will be treated as a code fragment
  • If value is not a string, it will be stringify(including the function)
  • If value is an object, all keys are defined the same way.
  • If the key has atypeofPrefix, which is defined only for typeof calls.

These values are inlined into the code, compressed to reduce redundancy.

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)
     }
});
Copy the code
console.log('Running App version' + VERSION);
Copy the code

Plugin is not a direct text value replacement; its value must contain the actual reference inside the string. The typical case is to use double quotes or json.stringify (), ‘”production”, json.stringify (‘production’).

Important: In vue-cli created projects, all files under SRC can access the VERSION variable, such as main.js, app.vue, etc

Let’s now look at the output of the above types of keys in the code.

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

In code, we generally have the following uses:

  • Distinguish the environment according to process.env.node_env
  • Importing a Configuration File
  • Importing configuration files based on NODE_ENV (this is important and will be covered later)
Feature Flag

A switch that controls new and experimental features.

new webpack.DefinePlugin({
    'NICE_FEATURE': JSON.stringify(true),
    'EXPERIMENTAL': JSON.stringify(false})),Copy the code
What is the correct way to configure process.env.node_env?
process: {
    env: {
        NODE_ENV: JSON.stringify('production')}}Copy the code

Evaluation: very bad, overwrite the entire process object, leaving only the new NODE_ENV, breaking the process. The original Process object contains the following, containing a lot of information about the current process.

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' }
Copy the code
'process.env': {
    NODE_ENV: JSON.stringify('production')}Copy the code

Evaluation: No, will overwrite the entire process.env object, damage the process environment, resulting in broken compatibility. The original process.env object contains the following, containing a lot of information about the current process.

{ 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' }
Copy the code
'process.env.NODE_ENV': JSON.stringify('production')
Copy the code

Evaluation: Good. Because simply changing the NODE_ENV value does not break the entire process, nor does it break compatibility.

How do I add configuration files using DefinePlugin to automatically detect environment changes during build, i.e. how do I introduce configuration files based on NODE_ENV?

Scenario: Interface addresses in development are often inconsistent with those in production. For example, development is development.foo.com and production is production.foo.com. If you need to package and distribute, you need to manually replace the domain name or maintain a special configuration file in one branch, which is very cumbersome.

  • Replacing files manually is inefficient. Configuration files need to be updated every time you switch between Development and Production, which is prone to errors
  • Configuration files are more advanced than manual replacement, but it is not possible to view all configuration information of development and production at one time. Switching between branches is inefficient, and it is not suitable for configuration in multiple environments
  • Webpack.defineplugin () global configuration file, automatic detection of environment changes, efficient.

Webpack’s DefinePlugin solves this problem for us by maintaining a global configuration file that automatically detects process.env.node_env at compile time and replaces our interface domain name with the current environment variable.

Here’s an example of how to use webpack.definePlugin correctly.

/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];
Copy the code

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); }})]...Copy the code

custom.component.vue

<template> ... API_CONFIG export default {// Whether it is a data function, a methods object, a computed object, a watch object, Access to 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>Copy the code

The above is only applicable to vuE-CLI2.0 era. Vue-cli3.0 introduces Webpack-chain, and the configuration methods are greatly different. Examples are given below.

How to add config files in vue.config.js using DefinePlugin to automatically detect environment changes during build, i.e. how to introduce config files based on 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
            })
    }
}
Copy the code

Note that in VUE-CLI3.0, we cannot directly SET NODE_ENV=production or EXPORT NODE_ENV=production. If you want to modify the NODE_ENV in vue-cli-service package, run the following command: You need to run vue-cli-service serve –mode production to switch. Like this:

{
  "scripts": {
    "dev": "vue-cli-service serve".// mode defaults to development
    "production": "vue-cli-service serve --mode production",}}Copy the code

Note: We can only switch between development, production, or Test modes and can’t introduce a custom Node environment like Preproduction, but in fact these three environments are sufficient for most development situations.

Why can DefinePlugin in VUE-CLI 3.0 be modified with config.plugin(‘define’)?

In the source file base.js, there is the following code:

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

Copy the code

This is crucial! The config.plugin(‘define’) we got in vue.config.js is actually a reference to the webpack.defineplugin instance created inside vue-service! After this, when we enhance the default webpack plug-in configuration in the future, we need to search the source code of vue-service to see if there is a reference to the corresponding plugin. If there is, we must directly reference the name of vue-service definition, otherwise the modification will fail.

I am looking forward to communicating with you and making progress together. Welcome to join the technical discussion group I created which is closely related to front-end development:

  • SegmentFault technosphere :ES new specification syntax sugar
  • SegmentFault column: Be a good front-end engineer while you’re still young
  • Zhihu column: Be an excellent front-end engineer while you are still young
  • Github blog: Personal blog 233 while You’re Still Young
  • Front-end development QQ group: 660634678
  • excellent_developers

Strive to be an excellent front-end engineer!