摘要:开发环境和生产环境都拥有的配置,但在细节上有所不同,比如说,又比如说中的和参数。更重要的是,实际上开发环境和生产环境的配置文件的绝大部分都是一致的,对于这一致的部分来说,我们坚决要消除冗余,否则后续维护起来不仅麻烦,而且还容易出错。
本文首发于Array_Huang的技术博客——实用至上,非经作者同意,请勿转载。前言
原文地址:https://segmentfault.com/a/1190000006952432
如果您对本系列文章感兴趣,欢迎关注订阅这里:https://segmentfault.com/blog/array_huang
开发环境与生产环境分离的原因如下:
在开发时,不可避免会产生大量debug又或是测试的代码,这些代码不应出现在生产环境中(也即不应提供给用户)。
在把页面部署到服务器时,为了追求极致的技术指标,我们会对代码进行各种各样的优化,比如说混淆、压缩,这些手段往往会彻底破坏代码本身的可读性,不利于我们进行debug等工作。
数据源的差异化,比如说在本地开发时,读取的往往是本地mock出来的数据,而正式上线后读取的自然是API提供的数据了。
如果硬是要在开发环境和生产环境用完全一样的代码,那么必然会付出沉重的代价,这点想必也不用多说了。
下面主要针对两点来介绍如何分离开发环境和生产环境:一是如何以不同的方式进行编译,也即如何分别形成开发环境及生产环境的webpack配置文件;二是在业务代码中如何根据环境的不同而做出不同的处理。
如何分离开发环境和生产环境的webpack配置文件如果同时把一份完整的开发环境配置文件和一份完整的生产环境配置文件列在一起进行比较,那么会出现以下三种情况:
开发环境有的配置,生产环境不一定有,比如说开发时需要生成sourcemap来帮助debug,又或是热更新时使用到的HotModuleReplacementPlugin。
生产环境有的配置,开发环境不一定有,比如说用来混淆压缩js用的UglifyJsPlugin。
开发环境和生产环境都拥有的配置,但在细节上有所不同,比如说output.publicPath,又比如说css-loader中的minimize和autoprefixer参数。
更重要的是,实际上开发环境和生产环境的配置文件的绝大部分都是一致的,对于这一致的部分来说,我们坚决要消除冗余,否则后续维护起来不仅麻烦,而且还容易出错。
怎么做呢?答案很简单:分拆webpack配置文件成N个小module。原先我们是一个完整的配置文件,有好几百行,从头看到尾都头大了,更别说分离不分离的了。下面来看看我分离的结果:
├─webpack.dev.config.js # 开发环境的webpack配置文件(无实质内容,仅为组织整理) ├─webpack.config.js # 生产环境的webpack配置文件(无实质内容,仅为组织整理) ├─webpack-config # 存放分拆后的webpack配置文件 ├─entry.config.js # webpack配置中的各个大项,这一级目录里的文件都是 ├─module.config.js ├─output.config.js ├─plugins.dev.config.js # 俩环境配置中不一致的部分,此文件由开发环境配置文件webpack.dev.config.js来加载 ├─plugins.product.config.js # 俩环境配置中不一致的部分,此文件由生产环境配置文件webpack.config.js来加载 ├─resolve.config.js │ ├─base # 主要是存放一些变量 │ ├─dir-vars.config.js │ ├─page-entries.config.js │ ├─inherit # 存放生产环境和开发环境相同的部分,以供继承 │ ├─plugins.config.js │ └─vendor # 存放webpack兼容第三方库所需的配置文件 ├─eslint.config.js ├─postcss.config.js
文件目录结构看过了,接下来看一下我是如何组织整理最后的配置文件的:
/* 开发环境webpack配置文件webpack.dev.config.js */ module.exports = { entry: require("./webpack-config/entry.config.js"), output: require("./webpack-config/output.config.js"), module: require("./webpack-config/module.config.js"), resolve: require("./webpack-config/resolve.config.js"), plugins: require("./webpack-config/plugins.dev.config.js"), eslint: require("./webpack-config/vendor/eslint.config.js"), postcss: require("./webpack-config/vendor/postcss.config.js"), };
这样,你就可以很轻松地处理开发/生产环境配置文件中相同与不同的部分了。
如何分别调用开发/生产环境的配置文件呢?还记得我在《webpack多页应用架构系列(二):webpack配置常用部分有哪些?》里讲过,我们在控制台调用webpack命令来启动打包时,可以添加上--config参数来指定webpack配置文件的路径吗?我们可以配合上npm scripts来使用,在package.json里定义:
"scripts": { "build": "node build-script.js && webpack --progress --colors", "dev": "node build-script.js && webpack --progress --colors --config ./webpack.dev.config.js", "watch": "webpack --progress --colors --watch --config ./webpack.dev.config.js" },
这样一来,当我们开发的时候就可以使用npm run dev或npm run watch,而到要上线打包的时候就运行npm run build。
业务代码如何判断生产/开发环境在业务代码里要判断生产/开发环境其实很简单,只需一个变量即可:
if (IS_PRODUCTION) { // 做生产环境该做的事情 } else { // 做开发环境该做的事情 }
这么一来,关键就在于这变量IS_PRODUCTION是怎么来的了。
在我还没分离开发和生产环境时,我用的办法是,开发时在业务代码所使用的配置文件中把这变量设为false,而在最后打包上线时就手动改为true。这种方法我用过一段时间,非常繁琐,而且经常上线后发现,我嘞个去怎么ajax读的是我本地的mock服务器。
怎么做呢?我参考了许多文章,先粗略讲讲我没有采用的方法:
用EnvironmentPlugin引入process.env,这样就可以在业务代码中靠process.env.NODE_ENV来判断了。
用ProvidePlugin来控制在不同环境里加载不同的配置文件(业务代码用的)。
那我用的是什么方法呢?我最后选用的是DefinePlugin。
举个官方例子,其大概用法是这样的:
new webpack.DefinePlugin({ PRODUCTION: JSON.stringify(true), VERSION: JSON.stringify("5fa3b9"), BROWSER_SUPPORTS_HTML5: true, TWO: "1+1", "typeof window": JSON.stringify("object") })
DefinePlugin可能会被误认为其作用是在webpack配置文件中为编译后的代码上下文环境设置全局变量,但其实不然。它真正的机制是:DefinePlugin的参数是一个object,那么其中会有一些key-value对。在webpack编译的时候,会把业务代码中没有定义(使用var/const/let来预定义的)而变量名又与key相同的变量(直接读代码的话的确像是全局变量)替换成value。例如上面的官方例子,PRODUCTION就会被替换为true;VERSION就会被替换为"5fa3b9"(注意单引号);BROWSER_SUPPORTS_HTML5也是会被替换为true;TWO会被替换为1+1(相当于是一个数学表达式);typeof window就被替换为"object"了。
再举个例子,比如你在代码里是这么写的:
if (!PRODUCTION) console.log("Debug info") if (PRODUCTION) console.log("Production log")
那么在编译生成的代码里就会是这样了:
if (!true) console.log("Debug info") if (true) console.log("Production log")
而如果你用了UglifyJsPlugin,则会变成这样:
console.log("Production log")
如此一来,只要在俩环境的配置文件里用DefinePlugin分别定义好IS_PRODUCTION的值,我们就可以在业务代码里进行判断了:
/* global IS_PRODUCTION:true */ if (!IS_PRODUCTION) { console.log("如果你看到这个Log,那么这个版本实际上是开发用的版本"); }
需要注意的是,如果你在webpack里整合了ESLint,那么,由于ESLint会检测没有定义的变量(ESLint要求使用全局变量时要用window.xxxxx的写法),因此需要一个global注释声明(/* global IS_PRODUCTION:true */)IS_PRODUCTION是一个全局变量(当然在本例中并不是)来规避warning。
示例代码诸位看本系列文章,搭配我在Github上的脚手架项目食用更佳哦(笑):Array-Huang/webpack-seed(https://github.com/Array-Huang/webpack-seed)。
附系列文章目录(同步更新)webpack多页应用架构系列(一):一步一步解决架构痛点:https://segmentfault.com/a/1190000006843916
webpack多页应用架构系列(二):webpack配置常用部分有哪些?:https://segmentfault.com/a/1190000006863968
webpack多页应用架构系列(三):怎么打包公共代码才能避免重复?:https://segmentfault.com/a/1190000006871991
webpack多页应用架构系列(四):老式jQuery插件还不能丢,怎么兼容?:https://segmentfault.com/a/1190000006887523
webpack多页应用架构系列(五):听说webpack连less/css也能打包?:https://segmentfault.com/a/1190000006897458
webpack多页应用架构系列(六):听说webpack连图片和字体也能打包?:https://segmentfault.com/a/1190000006907701
webpack多页应用架构系列(七):开发环境、生产环境傻傻分不清楚?:https://segmentfault.com/a/1190000006952432
webpack多页应用架构系列(八):教练我要写ES6!webpack怎么整合Babel?:https://segmentfault.com/a/1190000006992218
webpack多页应用架构系列(九):总有刁民想害朕!ESLint为你阻击垃圾代码:https://segmentfault.com/a/1190000007030775
webpack多页应用架构系列(十):如何打造一个自定义的bootstrap:https://segmentfault.com/a/1190000007043716
webpack多页应用架构系列(十一):预打包Dll,实现webpack音速编译:https://segmentfault.com/a/1190000007104372
webpack多页应用架构系列(十二):利用webpack生成HTML普通网页&页面模板:https://segmentfault.com/a/1190000007126268
webpack多页应用架构系列(十三):构建一个简单的模板布局系统:https://segmentfault.com/a/1190000007159115
webpack多页应用架构系列(十四):No复制粘贴!多项目共用基础设施
webpack多页应用架构系列(十五):论前端如何在后端渲染开发模式下夹缝生存
webpack多页应用架构系列(十六):善用浏览器缓存,该去则去,该留则留
本文首发于Array_Huang的技术博客——实用至上,非经作者同意,请勿转载。
原文地址:https://segmentfault.com/a/1190000006952432
如果您对本系列文章感兴趣,欢迎关注订阅这里:https://segmentfault.com/blog/array_huang
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/80419.html
摘要:本文首发于的技术博客实用至上,非经作者同意,请勿转载。原文地址如果您对本系列文章感兴趣,欢迎关注订阅这里这系列文章讲什么本系列文章主要介绍如何用这一当前流行的构建工具来设计一个多页应用的架构。 本文首发于Array_Huang的技术博客——实用至上,非经作者同意,请勿转载。原文地址:https://segmentfault.com/a/1190000006843916如果您对本系列文章...
摘要:在上一篇文章多页应用架构系列二配置常用部分有哪些中,我介绍了如何配置多页应用的入口,然而,如果仅仅如此操作,带来的后果就是,打包生成出来的每一个入口文件都会完整包含所有代码。的初始化常用参数有哪些,给这个包含公共代码的命个名唯一标识。 本文首发于Array_Huang的技术博客——实用至上,非经作者同意,请勿转载。原文地址:https://segmentfault.com/a/1190...
摘要:本文首发于的技术博客实用至上,非经作者同意,请勿转载。只是最近学习生态,用起来转换之余,也不免碰到诸多用上的教程案例,因此便稍作学习。在当前的浏览器市场下,想在生产环境用上,是必不可少的。 本文首发于Array_Huang的技术博客——实用至上,非经作者同意,请勿转载。原文地址:https://segmentfault.com/a/1190000006992218如果您对本系列文章感兴...
摘要:本文首发于的技术博客实用至上,非经作者同意,请勿转载。如果你使用了,或类似的,那么,通过编译前后的代码相差就很大了,这会造成两个问题以为例把你的代码转成什么样你自己是无法控制的,这往往导致无法通过的审查。 本文首发于Array_Huang的技术博客——实用至上,非经作者同意,请勿转载。原文地址:https://segmentfault.com/a/1190000007030775如果您...
摘要:本文首发于的技术博客实用至上,非经作者同意,请勿转载。原文地址如果您对本系列文章感兴趣,欢迎关注订阅这里前言书承上文多页应用架构系列十如何打造一个自定义的。终于,发现了这一大杀器,打包时间过长的问题得到完美解决。 本文首发于Array_Huang的技术博客——实用至上,非经作者同意,请勿转载。原文地址:https://segmentfault.com/a/119000000710437...
阅读 2283·2021-09-30 09:47
阅读 2938·2019-08-30 11:05
阅读 2509·2019-08-29 17:20
阅读 1900·2019-08-29 13:01
阅读 1708·2019-08-26 13:39
阅读 1185·2019-08-26 13:26
阅读 3164·2019-08-23 18:40
阅读 1714·2019-08-23 17:09