为什么要使用webpack
- 模块化、将复杂的程序细化为小的文件
- 对于TS,vue,react,以及一些高级ES语法,webpack能够将其转换为浏览器能识别的代码
- 对于scss,less等CSS预处理器也能转化
- 模块热更新
- 配置代理解决跨域
- 就算是vue,react等脚手架中也是封装了webpack来进行使用
配置
1 2 3 4 5 6 7
| "scripts":{ "build":"webpack --config prod.config.js" }
npx webpack-cli init
|
入口和上下文
1 2 3 4 5 6
| const path = require('path'); module.exports = { context:path.resolve(__dirname,'app'), entry:'文件路径' entry:['./a.js','./b.js'] }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| module.exports = { entry:{ app:'./src/app.js' adminApp:'./src/adminApp.js', } }
module.exports = { entry: { pageOne: './src/pageOne/index.js', pageTwo: './src/pageTwo/index.js', pageThree: './src/pageThree/index.js', }, };
|
模式
提供mode配置选项,告知webpack使用相应模式的内置优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| module.exports = { mode:'development'|'production'|'none' }
var config = { entry: './app.js', };
module.exports = (env, argv) => { if (argv.mode === 'development') { config.devtool = 'source-map'; }
if (argv.mode === 'production') { }
return config; };
|
输出
可以存在多个entry起点,只能指定一个output配置
1 2 3 4 5 6
| module.exports = { output:{ filename:'build.js', path:path.resolve(__dirname,'dist') } }
|
resolve
resolve.alias
创建import或require的别名,来确保模块引入变得更简单
1 2 3 4 5 6 7 8 9 10 11
| const path = require('path');
module.exports = { resolve: { alias: { Utilities: path.resolve(__dirname, 'src/utilities/'), Templates: path.resolve(__dirname, 'src/templates/'), }, }, };
|
resolve.extensions
1 2 3 4 5 6 7
| module.exports = { resolve: { extensions: ['.js', '.json', '.wasm'], }, };
|
loader
用于对模块的源代码进行转换,可以处理less,scss等
内联方式(不推荐使用)
1
| import 'css-loader! ../css/login.css'
|
配置方式(推荐使用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| module:{ rules:[ { test:/\.css$/, use:[ {loader:'css-loader'} ] }, { test:/\.css$/, loader:'css-loader' } { test:/\.css$/, use:['css-loader'] use:['style-loader','css-loader'] use:['style-loader','css-loader','less-loader'] } ] }
|
常用loader
- babel-loader:使用Babel加载ES高级代码将其转化为ES5
- ts-loader:像加载JS一样加载TS
- style-loader:将模块导出的内容作为样式添加到DOM中
- css-loader:加载CSS文件并解析import的CSS文件,最终返回CSS代码
- less-loader:加载并编译LESS文件
- sass-loader:加载并编译SASS/SCSS文件
- postcss-loader:使用postCSS加载并转换css文件
- stylus-loader:加载并编译Stylus文件
- vue-loader:加载并编译Vue组件
postcss-loader
安装
1 2
| npm i postcss-loader -D npm i postcss-cli -D //命令行操作
|
使用postcss在css-loader之前进行工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| { test:/\.css$/, use:[ 'style-loader', 'css-loader', { loader:'postcss-loader', options:{ postcssOptions:{ plugins:[ require('autoprefixer') ] } } } ] }
|
在真正开发过程中,也可以直接使用集成的postcss-preset-env插件
1
| npm i postcss-preset-env -D
|
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { test:/\.css$/, use:[ 'style-loader', 'css-loader', { loader:'postcss-loader', options:{ postcssOptions:{ plugins:[ <!-- require('autoprefixer'), require('postcss-preset-env'), 'postcss-preset-env' ] } } } ] }
|
对于less文件的配置,如果也需要使用postcss-preset-env,又要进行相同的配置
为了防止代码的冗余,可以将这些配置信息写到另一个postcss.config.js文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| module.exports = { plugins:[ require('postcss-preset-env') ] }
{ test:/\.less$/, use:[ 'style-loader', 'css-loader', 'postcss-loader', 'less-loader' ] }
|
需要注意的问题
1 2 3 4 5 6 7 8
| 当在一个css文件中import另一个文件时 01.login.css @import test.css 02.login.css可以被匹配,当它被匹配之后就是postcss-loader进行工作 03.基于当前代码,post-loader拿到了login.css当中的代码之后分析不需要额外的处理 04.于是代码转给了css-loader 05.此时css-loader可以处理@import media url... 这时候拿到了test.css文件,但它并不能进行一些兼容性处理也不能回头找postcss-loader 06.最终将处理好的css代码交给style-loader进行展示
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| module:{ rules:[ { test:/\.css$/, use:[ 'style-loader', { loader:'css-loader', options:{ importLoaders:1 } }, 'postcss-loader' ] } ] }
|
babel
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| npm install -D babel-loader @babel/core @babel/preset-env webpack
module: { rules: [ { test: /\.m?js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env',{target:'chrome 91'}], plugins: ['@babel/plugin-proposal-object-rest-spread'] } } } ] }
module.exports = { preset:['@babel/preset-env'] }
use:['babel-loader']
|
polyfill
babel:可以将ES6转换为ES5代码,而不转换新的API,比如可以转换箭头函数,但是不能转换Array.of
作用:用于实现浏览器并不支持的原生API代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
module.exports = { presets:[ [ '@babel/preset-env', { useBuiltIns:'usage', corejs:3 } ] ] }
|
打包图片/字体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| { test:/\.(png|svg|gif|jpe?g)$/, use:[ { loader:'file-loader', options:{ name:'img/[name].[hash:6].[ext]', } } ] }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| npm i url-loader -D { test:/\.(png|svg|gif|jpe?g)$/, use:[ { loader:'url-loader', options:{ name:'img/[name].[hash:6].[ext]', limit:25 * 1024 } } ] }
|
上面两种打包方式的关系
01.url-loader 把图片以base64 uri的方式加到文件中,减少请求次数
02.file-lodaer 将资源拷贝至指定的目录,分开请求
03.url-loader 内部其实也可以调用 file-loader
04.limit 决定调用file-loader 还是 url-loader
1 2 3 4 5 6 7 8 9 10
| asset处理图片 使用asset module type 可以简化处理图片 这个模块在webpack5中内置,不需要额外安装 在webpack5中也放弃了url-loader,raw-loader,file-loader 新增了四种资源模块(asset modules)替代了这些loader的功能
01.asset/resource——>file-loader(输出路径) 02.asset/inline——>url-loader(输出为url(data:)) 03.asset/source——>raw-loader(导出为源码source code) 04.asset(parser)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| { test:/\.(png|svg|gif|jpe?g)$/, type:'asset/resource }
如果想配置图片输出的目录以及名字 可以在更改output相应的配置,但是这样在打包字体的时候也会到配置目录 output:{ filename:'main.js', path:path.resolve(__dirname,'dist'), assetModuleFilename:"img/[name].[hash:4][ext]" },
所以也可以单独配置 { test:/\.(png|svg|gif|jpe?g)$/, type:'asset/resource, generator:{ filename:"img/[name].hash[4][ext]" } }
{ test:/\.(png|svg|gif|jpe?g)$/, type:'asset/inline } 原理和url-loader差不多
//如果想要按照图片大小分别处理 { test:/\.(png|svg|gif|jpe?g)$/, type:'asset', generator:{ filename:'img/[name].[hash:4][ext]' }, parser:{ dataUrlCondition:{ maxSize:30*1024 } } }
//处理字体 { test:/\.(ttf|woff2?)$/, //根据自己的字体来确认正则 type:'asset/resource, generator:{ filename:'font/[name]/[hash:3][ext]' } }
|
plugin
插件的目的在于解决loader无法实现的其他事情
copy-webpack-plugin
作用:将单个文件或整个目录复制到构建目录
拷贝一些固定不变的文件内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| npm i copy-webpack-plugin --save -dev const CopyPlugin = require('copy-webpack-plugin')
module.exports = { plugins:[ new CopyPlugin({ patterns:[ { from:'public' globOptions:{ ignore:['**/index.html'] } } ] }) ] }
|
html-webpack-plugin
1 2 3 4 5 6 7 8
| npm i html-webpack-plugin --save-dev
const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { plugins:[ new HtmlWebpackPlugin() ] }
|
会在output目录下自动生成一个index.html
webpack-dev-server
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| devServer:{ hot: true, hotOnly: true, port: 4000, open: false, compress: true historyApiFallback: true proxy: { '/api': { target: 'http://localhost:3000', pathRewrite: { '^/api': '' }, changeOrigin: true, }, }
|
控制是否生成,以及如何生成source-map
1
| [inline-|hidden-|eval-][nosources-]|[cheap-[module-]]source-map
|
其中vue的错误处理方式是source-map
react的错误处理方式是cheap-module-source-map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| eval-source-map map信息以base64的方式放入man.js中
inline-source-map 也是以base64塞到打包后的文件中 就会少一次请求
cheap-source-map 报错的时候只提供行信息,不提供列信息,减少消耗
cheap-module-source-map 报错的时候显示源代码,而不是打包兼容后的代码
hidden-source-map 无法定位到源文件
nosources-source-map 知道报错,无法定位到错误
|
target
往往采用browserslist配置来规定需要兼容的环境
可以在package.json文件中添加broswerslist属性
1 2 3 4 5
| "broserslist":[ ">1%", "last 2 version", "not dead" ]
|
也可以新建.browserlistrc文件
1 2 3
| >1% last 2 version not dead
|