经过阅读这篇文章,能够学习到如何使用DefinePlugin插件使得前端项目更加工程化,说清晰点就是如何使用这个插件,在编译阶段根据NODE_ENV自动切换配置文件,提高前端开发效率。前端
DefinePlugin中的每一个键,是一个标识符或者经过.
做为多个标识符。vue
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
能够控制新特性和实验特性的开关。vue-cli
new webpack.DefinePlugin({ 'NICE_FEATURE': JSON.stringify(true), 'EXPERIMENTAL': JSON.stringify(false), })
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值进行修改,不会破坏完整进程,也不会破坏兼容性。
情景:开发阶段的接口地址每每与生产阶段的接口地址是不一致的。例如开发时是development.foo.com,而生产时是production.foo.com,若是须要打包发布,那么须要手动去替换域名或者是一个分支维护一个专门的配置文件,这两种方式是很是笨重的。
文件效率低下,每次在development和production见切换都须要进行配置文件的更新,容易出错
相对手动替换高级一些,可是不能一次性查看development和production的所有配置信息,须要在分支间切换,效率低下,且不适用于多种环境的配置
全局配置文件,自动检测环境变化,效率高效。
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
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个环境已经足以知足大多数的开发状况。
在源码文件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定义的名字直接引用,不然会修改失败。