webpack 4常用
初始化
npm init
// Webpack 4.0以后需要单独安装
npm install webpack webpack-cli --save-dev
基础的config
entry: './src/index.js', mode: 'development', output: { filename: 'main.js', path: path.resolve(__dirname, 'dist') }
字段说明
- entry : 入口文件。一般是一个string,多个可以使用数组或者json
entry:"a.js"; entry:["a.js","b.js"]; entry:{ index:["a.js"], index2:["b.js"] }
- output : 出口文件 json
output : { fileName:"a.js", path:path.resolve(__dirname,"dist"); }
- mode 提供 mode 配置选项
mode : production;
mode : development;
- loader 用于对模块的源代码进行转换
module:{ rules:[ { test:/.css$/, use:[ { loader:"style-loader" }, { loader: 'css-loader', options: { modules: true } } ] } ] }
- plugin 插件目的在于解决 loader 无法实现的其他事。
plugin : [ new webpack.optimize.UglifyJsPlugin(), new HtmlWebpackPlugin({template: './src/index.html'}) ]
webpack常用
module.noParse
防止 webpack 解析那些符合匹配条件的文件,忽略的文件夹中不应该含有 import、require、define的调用,或任何其他导入机制,忽略的 library 可以提高构建效率。
开启sourceMap
css在cssloader的option设置{sourceMap:true},js在 devtool: 'inline-source-map'
module.exports = { entry:"./index.js", output:{ filename:"[name].js" }, module:{ rules:[ { test:/.(sc|c|sa)ss$/, use:[ { loader:"css-loader", options:{ sourceMap: true } } ] } ] }, devtool:"source-map" }
devtool可选值
1. source-map
2. inline-source-map
3. inline-cheap-source-map
4. inline-cheap-module-source-map
5. eval
热更新(webpack.HotModuleReplacementPlugin)
const webpack = require('webpack'); module.exports = { devServer: { contentBase: path.join(__dirname, 'dist'), //本地服务器所加载的页面所在的目录 clinetLogLevel: 'warning', // 可能值有 none, error, warning 或者 info (默认值) hot:true,//启动热更新替换特性,需要配合 webpack.HotModuleReplacementPlugin 插件 host:'0.0.0.0', // 启动服务器的 host port:7000, // 端口号 compress:true, // 为所有服务启用gzip压缩 overlay: true, // 在浏览器中显示全屏覆盖 stats: "errors-only" ,// 只显示包中的错误 open:true, // 启用“打开”后,dev服务器将打开浏览器。 proxy: { // 设置代理 "/api": { target: "http://localhost:3000", pathRewrite: {"^/api" : ""} } } }, plugins::[ new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin() ] } // package.json { ..... script:{ "dev":"webpack-dev-server --config webpack.dev.js" } ..... }
别名替换(resolve.alias)
给定对象的键后的末尾添加 $,以表示精准匹配
module.exports = { resolve:{ alias:{ '@src':path.resolve(__dirname, 'src/') } } }
resolve 可配置参数:
aliasFields
enforceExtension如果设置为true则不许可引入无扩展名的文件 例如 false可以使用require("./index") true 则必须require("./index.js");
extensions 自动解析确定扩展名 默认值为 [".js",".json"],在导入语句没带文件后缀时,Webpack 会自动带上后缀后去尝试访问文件是否存在。
css单独打包(mini-css-extract-plugin)
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module: { rules: [ { test: /.(sc|c|sa)ss$/, use: [ MiniCssExtractPlugin.loader, { loader:"css-loader", options:{ sourceMap: true } }, ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', // 最终输出的文件名 chunkFilename: '[id].css' }) ]
压缩js和css(uglifyjs-webpack-plugin、optimize-css-assets-webpack-plugin)
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');//压缩js const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');//压缩css module.exports = { // ... 省略 plugins: [ // ... 省略 new OptimizeCssAssetsPlugin({}), new UglifyJsPlugin({ //当 JS 没有发生变化则不压缩 cache: true, //是否启用并行压缩; parallel: true, sourceMap: true }) ], }
引入动态文件、处理html(hash)(HtmlWebpackPlugin)
const HtmlWebpackPlugin = require('html-webpack-plugin'); plugins: [ new HtmlWebpackPlugin({ title: "title!", // 生成的文件标题 filename: "main.html", // 最终生成的文件名 minify: { // 压缩选项 collapseWhitespace: true, // 移除空格 removeComments: true, // 移除注释 removeAttributeQuotes: true, // 移除双引号 } }) ],
清理目录(clean-webpack-plugin)
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); plugins: [ new CleanWebpackPlugin() ],
图片处理和优化(file-loader(处理)、image-webpack-loader(优化))
module: { { test: /.(png|svg|jpg|jpeg|gif)$/, include: [path.resolve(__dirname, 'src/')], use: ["file-loader",{ loader: "image-webpack-loader", options: { mozjpeg: { progressive: true, quality: 65 }, optipng: { enabled: false }, pngquant: { quality: '65-90', speed: 4 }, gifsicle: { interlaced: false }, webp: { quality: 75 } } }, ] }] },
图片 base64 处理(url-loader 不能和image-webpack-loader一起使用)
module: { { test: /.(png|svg|jpg|jpeg|gif)$/, include: [path.resolve(__dirname, 'src/')], use: [ { loader: 'url-loader', // 根据图片大小,把图片转换成 base64 options: { limit: 10000 }, }, ] }] },
字体处理(file loader)
module: { { test: /.(woff|woff2|eot|ttf|otf)$/, include: [path.resolve(__dirname, 'src/')], use: [ 'file-loader' ] } },
配置合并和提取公共配置(webpack-merge)
const merge = require('webpack-merge'); const common = require('./webpack.common.js'); let devConfig = { // configs... } module.exports = merge(common, devConfig)
设置代理
devServer: { // ... 省略其他 proxy: { "/api": { // 以 '/api' 开头的请求,会跳转到下面的 target 配置 target: "http://192.168.30.33:8080", pathRewrite: { "^api": "/mock/api" } } }
配置外部拓展(externals)
当我们使用 CDN 引入 jquery 时,我们并不想把它也打包到项目中,我们就可以配置 externals 外部拓展的选项,来将这些不需要打包的模块从输出的 bundle 中排除:<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
配置 externals:module.exports = { // ... 省略其他 externals: { jquery: 'jQuery' }, }
打包分析报表及优化总结(webpack-bundle-analyzer)
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; module.exports = { plugins: [ new BundleAnalyzerPlugin() ] }
分割代码(SplitChunksPlugin)(针对复用的体积大于30k)
{ optimization.splitchunks参数:{ minSize 分离后的最小块文件大小,单位为字节 minChunks 分离前,该块被引入的次数(也就是某个js文件通过import或require引入的次数) maxInitialRequests 一个入口文件可以并行加载的最大文件数量 maxAsyncRequests 内层文件(第二层)按需加载时最大的并行加载数量 name 用以控制分离后代码块的命名,当存在匹配的缓存组(后面会说到)时,命名使用缓存组中的name值,若不存在则为 [来源]~[入口的key值].js 的格式 automaticNameDelimiter 修改上面的 “~” , 若改为: “-” 则分离后的js默认命名规则为 [来源]-[入口的key值].js test 用于规定缓存组匹配的文件位置,test: /node_modules/ 即为匹配相应文件夹下的模块 cacheGroups 名字叫做缓存组,其实就是存放分离代码块的规则的对象,叫做cacheGroup的原因是webpack会将规则放置在cache流中,为对应的块文件匹配对应的流,从而生成分离后的块。 chunks 匹配的块的类型:initial(初始块),async(按需加载的异步块),all(所有块) } } optimization: { runtimeChunk: { "name": "manifest" }, splitChunks: { chunks: 'all', cacheGroups: { common: { minChunks: 2, name: 'commons', chunks: 'async', priority: 10, reuseExistingChunk: true, enforce: true } } } }
打包一个可以被import的js
output: { library: "myDemo",//如果在 HTML 页面中使用script标签引入打包结果文件,那么变量myDemo对应的值将会是入口文件(entry file)的返回值。 libraryTarget: "commonjs2" }
libraryTarget:
-
"var" - 以直接变量输出(默认library方式) var Library = xxx (default)
-
"this" - 通过设置this的属性输出 this["Library"] = xxx
-
"commonjs" - 通过设置exports的属性输出 exports["Library"] = xxx
-
"commonjs2" - 通过设置module.exports的属性输出 module.exports = xxx
-
"amd" - 以amd方式输出
-
"umd" - 结合commonjs2/amd/root
全局引入包(ProvidePlugin)
plugins: [ //提供全局的变量,在模块中使用无需用require,import引入 new webpack.ProvidePlugin({ $: "jquery", jQuery: 'jquery' }), ]
全局常量(DefinePlugin)
plugins: [ new webpack.DefinePlugin({ 'url':'www.baidu.com' }), ]
copy文件(copy-webpack-plugin)
npm install --save-dev copy-webpack-plugin const CopyWebpackPlugin = require('copy-webpack-plugin'); new CopyWebpackPlugin([ { from:__dirname+'/public/lib', to:__dirname+'/build/lib' } ])
优化模块查找路径 resolve.modules
Webpack的resolve.modules配置模块库(即 nodemodules)所在的位置,在 js 里出现 import 'vue' 这样不是相对、也不是绝对路径的写法时,会去 nodemodules 目录下找。但是默认的配置,会采用向上递归搜索的方式去寻找,但通常项目目录里只有一个 nodemodules,且是在项目根目录,为了减少搜索范围,可以直接写明 nodemodules 的全路径
const path = require('path'); function resolve(dir) { // 转换为绝对路径 return path.join(__dirname, dir); } resolve: { modules: [ // 优化模块查找路径 path.resolve('src'), path.resolve('node_modules') // 指定node_modules所在位置 当你import 第三方模块时 直接从这个路径下搜索寻找 ] }
配置好src目录所在位置后,由于util目录是在src里面 所以可以用下面方式引入util中的工具函数// main.js import dep1 from 'util/dep1'; import add from 'util/add';
多线程打包(较小项目不建议,反而会慢)
const HappyPack = require('happypack'); const os = require('os'); // node 提供的系统操作模块 // 根据我的系统的内核数量 指定线程池个数 也可以其他数量 const happyThreadPool = HappyPack.ThreadPool({size: os.cpus().lenght}) module: { rules: [ { test: /.js$/, use: 'happypack/loader?id=babel', exclude: /node_modules/, include: path.resolve(__dirname, 'src') } ] }, plugins: [ new HappyPack({ // 基础参数设置 id: 'babel', // 上面loader?后面指定的id loaders: ['babel-loader?cacheDirectory'], // 实际匹配处理的loader threadPool: happyThreadPool, verbose: true }), new Happypack({其他loader配置}) ]