一个现代的javascript应用程序的静态模块打包器。当webpack处理应用程序时,会根据入口文件等引入的地方造成一个依赖关系图,其中包括应用程序须要的每一个模块,而后将这些模块打包成一个或多个bundle。
官方图形象的描述了不一样的格式资源经过webpack最终打包成经常使用的浏览器可识别的资源。javascript
2.1 模块化能简单引入并使用某个被依赖的模块的功能
2.2 处理打包javascript的文件
2.3 能够将ES6转化为浏览器可识别和兼容的语法
2.4 经过loader转化后可打包处理非javascript资源文件
2.5 能提取共用的模块化功能打包后产生独立的一个或多个文件
2.6 搭建开发环境开启本地服务器、监视文件改动,热替换
2.7 合理的长效缓存等...css
用来告诉webpack应该使用哪一个模块,来做为构建依赖图的起点html
module.exports = { // 单个入口文件时,若是未命名则默认打包后名为main.js entry: './src/index.js' // 打包生成 app.js entry:{ app : './src/index.js' } // 多入口路径文件 打包后会生成app.js和main.js entry:{ app: './src/index.js', main: './src/main.js' } }
用于指示webpack打包后文件存放的路径和文件命名,设置静态资源前缀路径等vue
const path = require('path'); module.exports = { entry:{ app:'./src/index.js' }, output:{ path: path.resolve(__dirname,'dist'), filename: 'js/[name].[contenthash:8].js', /* * 能够理解为配置的基础路径 * 打包后以前的源码资源会根据这个配置生成新的访问路径 * 好比'/app.js' => https://cfile.xiaoman.cn/app.js */ publicPath:'https://cfile.xiaoman.cn' } /* 这个module部分会描述** module: { rules: [ { test: /\.vue$/, use: [{ loader:'vue-loader' }] } ] }
示例demo的目录结构和其余文件java
根据上面配置如今全部的资源所有打包在app.js这个文件中,详情以下图node
随着不停的迭代项目会愈来愈大,若是将所有文件打包到同一个文件意味着用户仅浏览一个页面要下载全部页面的资源(针对单页面应用),这种方式大大的下降首屏渲染速度。那么减小首次资源加载和没必要要资源的加载是必不可少的。
则动态按需加载或者共用部分不要重复打包到每一个使用到的文件中,经过将公用部分资源分离彷佛能够解决这个问题。jquery
const path = require('path'); module.exports = { entry:{ app:'./src/index.js' }, output:{ path: path.resolve(__dirname,'dist'), filename: 'js/[name].[contenthash:8].js', chunkFilename: 'js/[name].[contenthash:8].js', publicPath:'https://cfile.xiaoman.cn' }, /* 这个module部分会描述**/ module: { rules: [ { test: /\.vue$/, use: [{ loader:'vue-loader' }] } ] }
根据上面的配置,在其余文件异步加载某个文件,打包状况以下,将异步加载的文件单独造成一个文件。这时候app.js的字节数下降了,而且只有用户须要加载这个资源才会当即被加载
1.非命名式动态加载文件
以上配置便可完成
更改后的index.js文件(将路由改成动态加载)webpack
能够看到打包后单独生成了0.2711cda5.js文件es6
2.命名式动态加载文件
这种比较清晰知道文件的来源,可是每一个文件这样命名较为麻烦web
/* * 在map.json文件中我随便导出了一些json数据,仅供打包演示 * 如下是util.js文件的代码 */ export const getMap = ()=> import( /* webpackChunkName: "async-database-map"*/ './map.json').then(module => { return module.default })
这是目录结构和Add.vue文件的代码
打包以后就多了一个async-database-map.3a0b70bc.js文件
如今在Add.vue中加上样式。开始打包...
发现webpack会报错,大概意思说它不认识css,须要使用loader转化一下从而让wepback能够处理css
那么咱们知道了module主要是把每一个须要转化的类型的模块使用loader进行特有的处理。好比webpack自己不支持处理非javacript类型的文件。则能够经过vue-loader、css-loader转化为webpack能够处理的文件。这样就能够写vue、css文件。固然字体、图片等均可以,须要在moudle中进行另外配置便可。
/* * webpack.config.js 文件,其余配置同上 * 仅展现module配置 */ module.exports = { module: { // 不须要解析的文件名或路径 noParse: \[/iconfont.js/\], // 解析文件的规则 rules:[ { test: /\.vue$/, use: ['vue-loader'] }, { test:'/\.js$/', // 被解析的文件类型 ues: [ /* * 将es6语法能够转话成浏览器可识别的es5语法 * 在根目录下添加 babel.config.js并配置如何转换便可 * 若是用了babel-loader webpack默认会使用 * babel.config.js做为babel-loader的配置选项 * 固然还有其余方式好比.babelrc,详见[babel官网](https://www.babeljs.cn/docs/configuration) */ 'babel-loader', 'thread-loader' ], include: [ /* 需包括的非当前webpack配置所在的根目录下的路径 * 并不是一个大项目的根目录,由于一个项目下可能有多个 * 子项目 */ resolve('common/js') ], // 不包括的需解析转换的目录或文件 exclude: [ /node_modules\/(?!amumu)/ ] }, { test: /\.css$/, use: ['style-loader','css-loader'] } ] } }
配置以后打包成功,而且es6也被babel-loader转成浏览器可识别的es5语法
模块解析,主要用于帮忙找到文件的依赖模块的绝对路径,使其可以被正确解析。
/* * webpack.config.js 文件,其余配置同上 * 仅展现resovle配置 */ module.exports = { resolve:{ /* * 表示js、vue、json后缀的文件再被引入到其余文件时可省略后缀 * 名也能够找到,如不写在引入时不能省略后缀名 */ extensions:['.js', '.vue', '.json'], /* * 别名,import中的@crm在打包时将自动换成配置的别名路径 */ alias: { '@root':resolveRoot('./'), '@crm':resolveRoot('crm/src'), } } }
因为某些包不须要打包到bundle中,而是在运行时再去从外部获取这些扩展的资源。好比经过cdn路径获取jQuery,就无需将jQuery打包到bundle中了。
// 在index.html中 经过cdn去引入jQuery <script src="https://code.jquery.com/jquery-3.1.0.js" crossorigin="anonymous"> </script>
// webpack.config.js module.configs = { exterals: { jquery: jQuery } }
上面的则表示在模块经过import依赖jquery时,jQuery会被当作全局变量去替换被使用到的地方。
固然在咱们现有的系统中,并未经过cdn的形式去直接引入exterals中配置的外部依赖。而是经过gulp将某些配置的基础依赖包打包在vendor.base.js(见gulpfile.js)中,并直接在index.html中引入。
webpack 4新的项目打包优化配置属性,有些属性是用来替代webpack.
optimize.xx下的某些插件。
/* * webpack.config.js 文件,其余配置同上 * 仅展现optimization配置 */ module.exports = { optimization: { /* * 告诉webpack使用TerserPlugin或者在minimizer属性中的插件 * 来最小化 要输出的bundle * 默认只处理 js 文件 */ minimize: false /* 配置经过那个插件和如何最小化bundle */ minimizer:[new TerserPlugin()] /* * 将运行时每一个模块之间的映射关系单独分离成一个文件 * 主要记录了在运行时模块之间的运行和映射关系 * [关于runtime与manifest官方解析](https://www.webpackjs.com/concepts/manifest/) */ runtimeChunk:{ name: 'runtime' }, /* * 分离打包模块 * 将知足test的文件单独分离成一个文件或多个文件,不与入口文件 * app.js 打包在一块儿 */ splitChunks: { cacheGroups: { vendor: { chunks:'initial', name:'vendor', priority:0, test: /\/node\_modules\//, enforce:true, }, styles:{ name: 'styles', test: /\.css$/, chunks: 'all', enforce: true, } } }
这里就清晰的记录了,node_modules模块和styles与app.js分离,自身单独生成文件,又大大的减小了app.js的字节数,加快首屏渲染时间。
若是没有分离,则样式更新或者vue中的其余部分更新都会使得整个文件的缓存失效。分离后css和逻辑js部分彻底独立,更改css不会使js部分失效,同理亦然。这样更好的利用了缓存机制,减小没必要要未更新部分的下载。
用于自定义webpack的构建过程。webpack有内置插件,能够经过webpack.[plugin-name]使用这些插件。固然npm社区有更多的插件。
// webpack.config.js 文件,其余配置同上 module.exports = { plguins: [ /* * 在全局中定义某些变量,使得在项目任意处可访问自定义的变量 */ new webpack.DefinePlugin({ 'process.env':{ PROJECT_NAME:'"crm"', NODE_ENV:'"production"', ENV:'"production"' }, }), ] }
开发时建立本地服务器,可经过配置的网址和端口号进行访问。经过webpack-dev-server生成的bundle运行于项目根目录,这个文件并无存到物理磁盘上,而是在内存中。当咱们更改某些文件内容时会自动编译出最新的结果,可以更快的看到效果,加快开发速率。
安装 webpack-dev-server 依赖, 在package.json中配置start脚本
// webpack.config.js 文件,其余配置同上 module.exports = { devServer:{ host: 'http://localhost:8081', port: 8001, // 开启热更新 hot: true, /* * 将dist目录的文件做为可访问文件。通常想要提供静态文件时才须要 */ contentBase: './dist', /* * 绑定特定的基础目录 * 好比访问某个bundle.js * 其完整路径应是 http://localhost:8081/assets/bundle.js */ publicPath: '/assets', } }
运行后输出以下信息,直接在浏览器便可访问到
1.VueLoaderPlugin
专门用于对vue文件的打包,虽然在module中对vue文件使用了vue-loader, 还须要手动在插件处调用才行。能够本身定义参数,但最新的参数中已经废弃了在vue插件中定义css样式的加载器。
module.exports = { plguins: [ new webpack.DefinePlugin({'同上省略'}), new VueLoaderPlugin(), ] }
2.AssetsPlugin
生成webpack-assets.json文件,记录用到css和js的文件名,用来作缓存破坏。
能够自定义配置生成路径、文件名或只记录入口文件css和js文件名、元信息等
module.exports = { plugins:[ new AssetsPlugin({ /* 是否输入到配置的编译路径处 */ useCompilerPath:true, /* 输出文件中只包含入口文件及其关联块用到的css和js文件名 */ entrypoints:true, /* 为true时只在webpack-dev-server运行时在内存中生成,不写入磁盘 */ keepInMemory:process.env.NODE_ENV!=='production', metadata: { build: { buildTime:format(newDate(), 'yyyy-MM-dd HH:mm:ss'), }, }, }) ] }
3.webpack.DefinePlugin
上面描述了(详见3.4),用来定义全局变量,使得在项目中任意文件可以使用这个变量名
4.webpack.NamedModulesPlugin
给入口文件和已命名的异步打包文件以外的chunk进行命名。好比路由中动态加载的文件。
constroutes = [ { path:'add', component: ()=>import('./Add.vue')}, ]
若是不使用这个插件webpack会自动命名为 [id].[contenthash:8]
(根据上面的chunkFilename的配置会生成hash后缀).
另外根据本身的需求命名文件
module.exports = { plugins: [ new webpack.NamedChunksPlugin(chunk => { if (chunk.name) { returnchunk.name } consthash = require('hash-sum') const joinedHash=hash(Array.from(chunk.modulesIterable, m =>m.id).join('_')) return `chunk-`+joinedHash }) ] }
可与3.2中生成的bunlde进行对比可发现以0.2711cda5.js命名的文件变为
chunk-226123c6.eef091ca.js了
5.HtmlWebpackPlugin
由于项目都有一个入口的html文件,这个文件会负责引入并加载js/img等等静态资源从而显示到浏览器。但咱们打包的js名是由文件名+hash组成。若是在html中引入的js名能自动根据编译结果进行变化就省事很多。这个插件正好解决此问题。
module.exports = { plugins: [ /* 默认会输出到配置的output路径下 */ new HtmlWebpackPlugin({ // 生成的文件名 filename:'index.html', // 以哪一个文件为模板来进入js等打包文件的修改和注入 template:'index.html', }) ] }
打包的bundle截图
生成的index.html自动引入了webpack打包的入口js文件,无需每次打包完成后手动去引入
6.inlineManifestWebpackPlugin
这个插件的做用是把单独打包的关于记录manifest信息的js文件(默认名:runtime.js)的内容之内联方式放在入口html(默认名:index.html)中。由于index.html每次打包都会变更,记录manifest的文件每次也会变更则这个文件的缓存是没用的。若是记录manifest的文件的内容内联到index.html就能够减小一个请求。更好的进行长效缓存。
module.exports = { optimization:{ runtimeChunk:{ name:'runtime', } }, plugins:[ new HtmlWebpackPlugin({ filename:'index.html', template:'index.html', }), /* * 1.首先要把记录manifest信息的文件单独打包 * 2. 搭配HtmlWebpackPlugin一块儿使用,并在后面配置 * 3. 传入的名字与 optimization中配置的分离manifest文件名一致,默认是runtime */ new InlineManifestWebpackPlugin('runtime') ] }
7.CopyWebpackPlugin
顾名思义,拷贝文件。 将某些文件从一个地方拷贝到另外的地方。
module.exports = { plguins: [ new webpack.DefinePlugin({'同上省略'}), new VueLoaderPlugin(), new CopyWebpackPlugin([ { /* 具体根据本身的项目目录 */ from:'../static', to:'./dist/static', ignore: ['.*'], }, ]), ] }
8.MiniCssExtractPlugin
将样式从内嵌style的文件中分离出来并造成单独的文件。好比xx.vue文件中有些样式,就能把这部分样式抽出来。这样作,若是xx.vue中的非样式部分修改,不会使得样式文件的缓存实现。各自独立,不会相互影响,可减小请求http资源的数量。
这里配置遇到一个坑,在mode设置为'development'分离彻底没问题,改成'production',分离以后的css文件就不见了。通过调查该插件有把css分离出来,只是webpack在tree sharking时认为无反作用就将生成的css摇了。
由于最新生成的package.json中包含了 sideEffects: false这个属性,即认为是无反作用的。
生产环境webpack默认sideEffects:true 读取packages.json的标识
来判断模块或包有/无反作用
则webpack在打包检查被分离css文件时并未找到以import和export导入导出的形式,认为无反作用直接摇掉。
webpack关于tree sharking部分定义以下:
9.OptimizeCSSAssetsWebpackPlugin
压缩css,能够跟上比较,字节从63 bytes -> 44 bytes,明显减小。
10.FriendlyErrorsWebpackPlugin
能够识别某些类的网页包错误,并对它们进行清理、聚合和排序,以提供更好的开发人员体验。
在未配置前打包时会输入某些包时的输入信息,大可能是无用的信息,咱们但愿控制台能输入有效且干净的信息。
module.exports = { devServer:{ contentBase:'./dist', hot:true, // 是否启用热更新 /* * 启动以后,只会在控制台打印初始信息,不会打印其余信息。意味关闭了错误和其余提示 * 千万要配置这个属性,不然FriendlyErrorsPlugin不起做用 */ quiet: true, }, plugins:{ /* * 因此须要使用这个插件在编辑成功/报错是进行提示 */ new FriendlyErrorsPlugin({ compilationSuccessInfo: { messages: [`good job compile succ`], }, onErrors:function(){ console.log(`compile faild, check you project`) }, clearConsole:true, }), } }
编译成功时清空了控制台,运行compilationSuccessInfo,执行自定义内容。
编译失败时明确告诉是个文件哪一行编译错误,方便快速定位。
插件的世界多而广,只简单介绍几种项目中用到的比较多的插件。可根据项目的须要在npm社区中寻找合适的插件。
这里描述了一个基本的webpack使用以及webpack基本属性。实际项目远远不止这些配置,也不会如此简单。可能涉及到长效缓存、本地开发优化、首屏加载速度、文件名变动等等一系列的问题。加油吧!
webpack官方文档
手摸手,带你用合理的姿式使用webpack4(下)
带你用合理的姿式使用webpack4(上)
处理打包文件体积过大的问题
webpack4---生产环境css样式丢失问题