好久之前,互联网行业有个职位叫作 “软件开发工程师” 在那个时代,你们可能会蒙,放在现今社会,你们会以为这个职位太过笼统,因此在此提一下那个时候的互联网行业状态。javascript
第一个阶段就是开始对着W3C的文档,拿着txt文本文件一个字母字母的敲着代码,那个年代,真的单纯舒服,上来就是一个项目的文件夹,而后就开始img、js、css三个完美的文件夹,再接上一个index.html,就开始到网上各类下载类库jquery、underscore.js,而后手动的引入各类类库,固然过程也伴随着痛苦。php
第二个阶段就是在接触到Vue的时候直接上vue-cli的时候,几行脚本就能够启动本地开发服务和打包线上资源,初次尝试了webpack这个打包工具,好像对其余问题不须要作过多的考虑,直接开始业务开发,其余的事情cli都帮助处理好了。vue init webpack helloWorld, cd helloWorld && cnpm install && npm run dev,真香警告,对工程化只知其一;不知其二,可是好用,方便css
第三个阶段就是给公司项目升级webpack过程当中体验到了更细粒度的控制工程,体验工程化的过程,实际的体验到了工程化为咱们作的事情html
这两类是不一样的东西,一个能够理解为是任务执行器而另一个是模块打包器,任务执行器是能够自动化的执行一些之前你须要手动操做的过程,见下面简单的代码前端
// coffee 源码 console.log 'Hello World' //coffee转 js coffee -c a.coffee (function(){ console.log('Hello World'); }).call(this); //执行编译压缩 uglify -s a.js -o a.min.js (function(){console.log("Hello World")}).call(this);
coffee须要编译成浏览器支持的js,你须要手动的去执行上面的几个命令,若是如今须要去修改源码,在Hello World 后面加上一个!,加完你须要手动的去执行两条命令重复的去编译操做。以 gulp 为例,编写 gulpfile.js来帮助咱们完成重复的工做vue
gulp = require('gulp') coffee = require('gulp-coffee') uglify = require('gulp-uglify') rename = require('gulp-rename') file = './src/js/a.coffee' gulp.task('coffee', function(){ gulp.src(file) .pipe(coffee()) // 编译 .pipe(uglify()) // 压缩 .pipe(rename({ extname: ".min.js" })) .pipe(gulp.dest('./build/js')) gulp.task('watch', function(){ gulp.watch(file, ['coffee']) }) gulp.task('default', ['coffee'])
我只要执行一下 gulp watch 就能够检测到coffee文件的变化,而后为你执行一系列的自动化操做,一样的操做也发生在less, scss, 这些css的预处理器上。在修改到源文件的状况下的编译,压缩这些重复操做都交由它来完成,在我看来Grunt / Gulp 算是一个可以自动化执行一些繁琐重复的操做,提升生产效率,算是一个任务执行器。java
这些操做一样也能够由webpack完成,接下来咱们看一下官网给出的webpack的定义。node
webpack is a module bundler
官方给出的解释是,webpack是一个模块打包器,不是一个任务执行器,它是能够配合Grunt / Gulp 使用的,相关连接webpack集成 。webpack打包器(bundler)帮助你生成准备用于部署的 JavaScript 和样式表,将它们转换为适合浏览器的可用格式。例如,JavaScript的压缩、chunk split和懒加载,以提升性能。因此webpack和Gulp/Grunt之间是有必定功能的重叠,可是处理合适,是能够一块儿配合工做的,不存在所谓的谁替代谁,只是在某些场景下webpack的可以独当一面,完成了Grunt/ Gulp的功能。jquery
rollup以及Parcel的出现,号称零配置,足以让一个配置成本比较高的webpack出现了4.0版本,固然也号称零配置使用,开箱即用,如今来一块儿看看webpack4.x和3.x比较大的区别,先给出一个Release连接webpack Release v4.0.0下面简要的介绍一些大的改动webpack
首先先看下图总体了解一下webpack的一些经常使用配置项
接下来简单的了解一下webpack的一些基本概念
先简短的介绍一下项目,升级的项目为多入口项目,每一个module模块代码一个入口,而后共用不少业务组件
*/build webpack.base.conf.js // dev和prod共用部分 webpack.dev.conf.js // dev模式下特有配置 webapck.prod.conf.js // prod模式下特有配置 */common components // 多个模块共用的组件 assets // 静态资源 */src module1 // 模块1的代码 index.js // 模块1代码的入口 module2 // 模块2的代码 module3 // 模块3的代码 */mock mock-xxx.js // mock api的接口 */config // 配置文件 index.js dev.js prod.js
升级webpack,安装webpack-cli。全局安装npm-check-updates,查看package.json中可升级的依赖的版本。
cnpm install -g npm-check-updates //全局安装 // 在项目根目录下执行ncu ncu // 可升级的依赖,列举部分依赖状况 axios 0.17.1 --> 0.18.0 webpack 3.5.5 --> 4.16.5 webpack-merge 4.1.0 --> 4.1.4 // 升级webpack 安装webpack-cli cnpm install webpack webpack-cli --save-dev // 升级相应的loader和plugin cnpm install url-loader file-loader vue-loader sass-loader css-loader babel-loader html-webpack-plugin --save-dev
公司使用的vue-cli2.0的脚手架生成的项目,简单的列举关于webpack的目录,针对须要可进行自行调整
*/build webpack.base.conf.js // dev和prod共用部分 webpack.dev.conf.js // dev模式下特有配置 webapck.prod.conf.js // prod模式下特有配置 // 经过webpack-merge合并配置输出最后的webpack配置
1:增长mode模式
// webpack.dev.conf.js 开发模式的配置 开启webpack默认的配置优化 mode: 'development' // webpack.prod.conf.js 生产模式的配置 mode: 'production'
2:升级vue-loader,vue-loaderv.15版本和以前的有所区别,vue-loader不在使用自身的配置,而是解析.vue文件以后使用webpack里配置的loader,详细文档见Vue-loader的使用,补充一点:在.vue 文件中style提供的scoped标记,就是经过vue-loader去实如今template中加入了适当的hash,配合样式去作到组件内样式的独立
// webpack.config.js const VueLoaderPlugin = require('vue-loader/lib/plugin') // 插件解析.vue文件把template script style 分别交给webpack配置的loader去处理 module.exports = { // ... plugins: [ new VueLoaderPlugin() ] }
3:部分webpack的插件已经配置停用。若有的话,按照提示删除掉,好比module里的loaders,webpack4再也不支持。而后部分插件转化为经过optimazation 进行配置,主要配置点在于code split以及mainifest文件的提取,一样见下文splitChunks和runtimeChunks的分析
4:开发模式加入devServer。项目是否须要经过手动node搭建本地服务器,取决因而否须要node层面去处理其余东西,在公司项目中启动服务前有两个操做,编译scss文件(皮肤文件),以及mock文件夹(mock接口),因此仍是保留了手动档搭建node层面启动服务,其实彻底能够有webpck-dev-server的before,after配置项完成
// 下面代码块针对devServer的部分配置项作说明,结合相关知识进行分析 devServer: { contentBase: path.join(__dirname, '../static'), // 静态资源提供,不是webpack生成的bundle,生成的bundle在内存中见注释1 host: host || 'localhost', // 主机名,若是你但愿服务器外部可访问,须要设置为0.0.0.0,默认localhost port: process.env.PORT || port, // 端口号 historyApiFallback: true, // handle fallback for HTML5 history API 了解一下这个东西见注释2 proxy: proxyTable, // 后端接口转发见注释3 hot: true, // 热更新见注释4 quiet: true, // webpack打包信息省略 publicPath: assetsPublicPath, // bundle 的位置 outpath: publicPath 相似 clientLogLevel: "none" // HRM WDS 在浏览器控制台的输出 } // 注释1:写过express的就知道有一个指定static中间件用来指定资源目录的 const express = require('express') const app = express() app.use(staticPath, express.static(xxx)) // xxx/js/vendor.js,就能够经过localhost:prot/staticPath/js/vendor.js访问相关静态资源。因此contentBase就是利用express静态中间价提供个一种访问静态资源文件的能力 // 注释2:vue-router的history模式充分利用 history.pushState API 来完成 URL 跳转而无须从新加载页面 example: history.pushState({a:1}, '测试', '/attendance/index') ,而后内部去处理相应的router对象的展现Vue页面和逻辑,因此这就是你顺着程序点路由能够进去,可是刷新的时候,就显示404的缘由,由于该路由在服务器上不是真实存在的,而是在index.html中经过JS去解析模拟的,这就须要咱们生产模式下生产的dist文件全部的请求都转发到index.html。处理方式:在服务器上经过nignx 代理,或者起一个express服务,经过第三方类库 connect-history-api-fallback,固然也能够原生Node去写,此时Node只是一个文件服务器 const http = require('http') const fs = require('fs') const httpPort = 80 http.createServer((req, res) => { fs.readFile('index.htm', 'utf-8', (err, content) => { if (err) { console.log('We cannot open "index.htm" file.') } res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }) res.end(content) }) }).listen(httpPort, () => { console.log('Server listening on: http://localhost:%s', httpPort) }) // 注释3:请求代理,经常被问起跨域请求有哪些方案,jsonp、CORS(后端配合)、window iframe等方式,还有一个就是经过node代码转发请求,服务端请求不存在跨域的概念,webpack中能够node本身使用http-proxy-middleware 去进行代理,本次更新使用的wbepack-dev-server模块同时也依赖了http-proxy-middleware的库,目前Node不做为纯后端,而是做为中间代码,链接纯后端(java php)和前端。 // 注释4:热更新,它容许在运行时更新各类模块,而无需进行彻底刷新,目前的公司项目都是纯刷新的方式,热更新HRM,这个是须要你是用的loader或插件帮助你完成,他们能监听webpck complier期间得钩子,而后给出相应源码更新后须要patch,推送到前端,打补丁,而后实现热更新,而不是刷新整个页面去从新加载页面。好处天然提升开发效率,在修改.vue文件的tempalte和style以及script中不是vue生命周期函数时,是可以保留到当时的vue的各类状态
5:针对生产模式的优化配置,废弃生产模式中使用的优化插件,转为webpack4的optimization.* 配置
下面为升级以前的配置
// 依赖module(require, import 导入的文件)来自node_modules且以.js 结尾的文件将会被打包到名为vendor的bundle中 new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: function (module, count) { // any required modules inside node_modules are extracted to vendor return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) } }), // 在vendor的bundle中把manifest提取出来(mainifest算是webpack实现的在浏览器端进行模拟加载模块和具体加载逻辑的代码块,这个最好拆出来,否则无法作缓存,具体见到长效缓存分析) new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', chunks: ['vendor'] }) // 打包以后就会生成一个vendor.js (里面有着来自node_modules 的第三方类库)和 mainifest.js (具体加载逻辑)
升级以后,完成原有的配置很简单,简单的照着API实现便可,本次升级伴随着两个重点,开发模式的rebuild时间,生成环境的打包文件体积和时间,因此还须要作其余优化
optimization: { splitChunks: { cacheGroups: { // module只要知足下面就会从原来的chunks中抽取出来打包到对应的chunks之中 // 这个vendors是在至少同时有两个initial Chunk中引入的来自node_modules的第三方类库会被打包到chunks-vendors中 vendors: { name: 'chunk-vendors', test: /[\\\/]node_modules[\\\/]/, priority: -10, minChunks: 2, // 至少同时有几个chunk知足才会有可能从这些chunks中提取一些代码到新的chunk中,也就是至少两个共用才会提取,否则就直接打包都所在module的init chunk中 chunks: 'initial' // chunk的概念:代码分割这些操做做用于那些chunk文件,initial是经过提供给webpack入口生成的chunks, async是经过以前提到的import() 路由懒加载造成的chunk, all就是全部的chunk文件 }, // 这个common跟上面的区别在于没有test检测module来源,只是只要有两个chunk共用就是提取出来,很显然就范围来说,下面的大于上面,这时候到底这个来自node_modules的module进入哪一个chunk,取决于priority(优先级),谁高进入哪一个chunk common: { name: 'chunk-common', minChunks: 2, priority: -12, chunks: 'initial', minSize: 0 } } }, runtimeChunk: { name: 'manifest' } } // 好比某些第三方类库只在某个Vue文件中使用,经过动态引入import('vue-qrcode-component').then() 一个类库只在一个地方用,彻底没有必要打包到vendor中(由于vue-qrcode-component 来自node_module) // import 就是新建一个chunk 这个chunk是没有名字的,须要经过/* webpackChunkName: "loadsh"*/ 生成这个loadsh的chunk.name import(/* webpackChunkName: "loadsh"*/ 'loadsh').then(m => { const _ = m.default console.log(_.join(['hello', 'world'])) })
总结:通常通过这几步骤就能完成一个webapck项目的升级,对于本身项目的复杂的地方须要额外的处理,写着写着发现篇幅愈来愈长了,把上文一直出现的splitChunk和runtimeChunk留到下个篇幅着重介绍一下。在公司作升级以前,给的指标不只仅是升级框架,还须要在dev模式开发的rebuild的速度更快(修改一个地方,rebuild的时间12s左右才能看到效果,痛苦,能够经过lessModule来解决),在prod模式下打包的项目文件体积减少已经打包所须要的时间更短(一次测试环境发布须要5,6分钟)。如今vue-cli3.0已经出现了,找时间把vue-cli3.0源码给你们分析一下,简单的包装了一下操做