本篇博客由慕课网视频[从基础到实战手把手带你掌握新版Webpack4.0](https://coding.imooc.com/class/316.html)阅读整理而来,观看视频请支持正版。
本篇博客 Webpack 版本是4.0+,请确保你安装了Node.js最新版本。
webpack的核心定义是一个模块打包工具。javascript
https://webpack.js.org/concepts/css
GUIDES: 解决某一个方向问题答案,如代码分割,TypeScripthtml
CONCEPTS: 一些核心的概念vue
CONFIGURATION: 某个配置项java
API: 写一些loader,plugin插件node
LOADERS: 查看loader的做用,配置。(若是找不到到插件的githup上找)react
PLUGINS: 插件的做用,配置。(若是找不到到插件的githup上找)jquery
首先要安装node.js以及npm, 并保证是最新版本。(最新版本会加快打包构建速度)webpack
全局安装ios
npm install webpack webpack-cli -g
项目安装
npm install webpack webpack-cli -D
带有版本的安装
npm install webpack@4.16.5 webpack-cli -D
npm uninstall webpack webpack-cli -g
全局安装查看指令:
webpack -v
项目内安装查看指令:
npx webpack -v
npm info webpack
在根目录建立webpack.config.js
const path = require('path');//引入node核心模块 module.exports = { //mode: 'production',//默认模式,会压缩代码,不写便是默认,不写会有提示 mode: 'development',//开发模式, 不会压缩代码 entry: { main: './src/index.js' },//从哪个文件开始打包 output: {//输出到哪里 filename: 'bundle.js',//输出的文件名称 path: path.resolve(__dirname,'bundle') //输出到哪个文件夹下, path后面跟绝对路径 //__dirname指的是webpack.config.js文件当前所在的路径 } }
把默认的webpack.config.js
, 修改成webpackconfig.js
npx webpack --config webpackconfig.js
npx会在目录下的node_modules下面找webpack,没有就安装一个。npm 则先去全局,而后再去当前目录下的node_modules找webpack,没有就不找了
webpack index.js
npx webpack index.js
npm scripts
)npm run bundle
package.json文件
{ "name": "webpack-demo", "version": "1.0.0", "description": "", "private": true, "scripts": { "bundle":"webpack" }, "author": "LiuJunFeng", "license": "ISC", "devDependencies": { "webpack": "^4.41.5", "webpack-cli": "^3.3.10" } }
loader是一个打包方案,它知道对于某一个特定的文件,webpack该如何进行打包。自己webpack是不知道对于一些文件(jpg,txt,excel)该如何处理的,可是loader知道。 因此webpack去求助loader就能够啦。
打包图片资源能够选用两个loader
, 一个是file-loader
,一个是url-loader
。
npm i file-loader -D
npm i url-loader -D
url-loader更加友好, 它能够经过图片大小来判断是使用base64格式图片仍是直接打包成一个图片资源文件。
webpack.config.js
module: { rules: [{ test: /\.(jpg|png|gif)$/, use: { // loader: 'file-loader',// 遇到jpg格式不知道怎么打包就去求助file-loader插件 loader: 'url-loader',//图片转化为base64, 不是单独生成一个文件。 options: { // placeholder 占位符 name: '[name]_[hash].[ext]',//name 打包文件名字 name/原有文件名字 hash/本次打包哈希值 ext/原有名字后缀 outputPath: 'images/',//把图片文件打包到images目录下 limit: 204800//若是文件超过204800字节,就会像file-loader打包到dist目录下生成一个文件, //若是文件小于204800字节,那就回变成base64字符串, 放到js内 } } }] }
File-loader底层处理逻辑,先将文件转移到打包目录下,再将dist中的文件路径返回给index.js。
任何的静态文件均可以使用file-loader
插件, 只要你但愿把静态文件移动到打包目录下而且获取到此文件地址。
打包css
文件须要使用两个loader
,style-loader
和css-loader
。
npm i style-loader css-loader -D
css-loader
帮咱们分析出几个css文件的引入关系, 最终将这些css文件合并成一段css。
style-loader
再获得css-loader
生成的内容后, 会把这段代码挂载到html的head部分。
在打包css文件时, 必定要css-loader
和style-loader
配合使用。
npm install sass-loader node-sass --save-dev
npm i -D postcss-loader
npm i autoprefixer -D
loader打包的执行顺序是从下到上(从右到左)来执行, 以下:sass文件会先执行sass-loader
, 处理完后再执行style-loader
挂载到html的head部分。
webpack.config.js
在module对象内的rule数组内添加如下代码:
css样式文件配置
{ test: /\.scss$/, use:[ 'style-loader', { loader: 'css-loader', options: { importLoaders: 2,//在scss又引入另一个scss时,有可能直接走css-loader,不走sass-loader和postcss-loader,加上此配置项可让它继续走下面两个loader //modules: true//开启css模块化打包 解决全局样式冲突 } }, 'sass-loader',//sass文件编译 'postcss-loader'//加厂商前缀 ] }]
字体文件配置
{ test: /\.(eot|ttf|woff|woff2|svg)$/, use: { loader: 'file-loader', options:{ outputPath: 'fonts/', } } }
若是须要加厂商前缀, 须要在根目录在建立一个文件, 取名为postcss.config.js
, 如下为具体配置:
module.exports = { plugins: [ require('autoprefixer') ] }
默认打包支持的浏览器,不须要厂商前缀,能够把浏览器条件放宽:
能够在根目录package.json
文件中添加浏览器条件:
"browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ]
plugin 能够在webpack运行到某个时刻的时候, 帮你作一些事情。 很像vue中的生命周期函数。
npm i -D html-webpack-plugin
html-webpack-plugin会在打包结束的时刻, 自动生成一个html文件, 并把打包生成的js自动注入到这个html文件中。
首先须要引入该插件, 而后再module
对象内写入plugins
属性名, 属性是一个数组,数组内实例化该插件。
const HtmlWebpackPlugin = require('html-webpack-plugin'); plugins:[new HtmlWebpackPlugin()],
npm i clean-webpack-plugin -D
clean-webpack-plugin
插件会在打包流程执行前清空dist目录
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
plugins:[new HtmlWebpackPlugin({ template: 'src/index.html' }),new CleanWebpackPlugin()],
entry: { main: './src/index.js', sub:'./src/index.js' },//从哪个文件开始打包
若是一个文件打包两次, 能够用以上方式配置, 一个文件会成为main.js, 一个会成为sub.js。
output: {//输出到哪里 publicPath:'http://cdn.com.cn',//文件使用cdn域名 filename: '[name].js',//输出的文件名称 name对应的是entry里的key值 chunkFilename:'[name].chunk.js',//间接引入的文件会加上.chunk path: path.resolve(__dirname,'dist') //输出到哪个文件夹下, path后面跟绝对路径 //__dirname指的是webpack.config.js文件当前所在的路径 }
可使用这个配置查看源代码那里有错误, 而不是打包后的文件错误。
好比你的js文件里面写的有问题,好比dist目录下的main.js文件第96行出错。SourceMap他是一个映射关系, 他知道dist目录下的main.js文件96行实际上对应的是src目录下index.js文件中的第一行。他就知道是index.js第一行出错了。
module.exports = { //mode: 'production',//默认模式,会压缩代码,不写便是默认,不写会有提示 mode: 'development',//开发模式, 不会压缩代码 devtool:'source-map', entry: { main: './src/index.js' },//从哪个文件开始打包
devtool:'inline-source-map'
source-map会在dist目录下生成一个main.js.map, 而使用 inline-source-map会直接经过data-url的方式直接写在main.js内的底部。
devtool:'cheap-inline-source-map'
当咱们遇到代码量很大的时候, 若是咱们的代码出了错误, 加上cheap它就会只告诉咱们是哪一行出了错误, 而不会告诉咱们第几列。 打包会更加节省性能。
devtool:'cheap-module-inline-source-map'
source-map只会告诉咱们业务相关的代码是否有错, 而不会告诉咱们引入第三方模块代码是否有错误, 好比 loader内的错误。若是你想让它也管第三方模块的错误能够加上module。
devtool:'eval',
使用eval
配置执行效率最快, 性能最好的打包方式。它使用了eval
的语法执行源代码。可是若是比较复杂的代码状况下, 它提示出来的内容可能不太准确。
若是你在开发环境中, 使用source-map建议使用devtool:'cheap-module-inline-eval-source-map'
, 它提示出来的错误是比较全的, 同时它的打包速度也是比较快的。
若是在线上环境中, 不必使用source-map做为映射, 直接删除此配置便可。
固然若是你也须要看错误提示, 可使用devtool:'cheap-module-source-map'。
它的提示会更好一些。
修改源码自动就会进行打包。提高开发效率。
总共有如下两种方式:
监听文件改动实时进行打包
package.json
"scripts": { "watch": "webpack --watch" },
自动打包并刷新浏览器,还能够模拟服务器上的特性。 vue以及react脚手架使用的都是此配置。推荐使用, 这个也是业界最常用的方案。
npm i webpack-dev-server -D
webpack.config.js
devServer: { contentBase: './dist',//服务器起在哪个文件夹下 open: true,//自动打开浏览器 port: 8080,//使用哪一个端口号 proxy: { './api':'http://locallhost:3000' }//若是访问api这个地址,也就是locallhost:8000/api, 它会帮你转发到http://locallhost:3000这个地址上 },
package.json
"scripts": { "watch": "webpack --watch", "start": "webpack-dev-server" },
HMR是Hot Module Replacement的缩写。
它能够只更新你改动的文件, 不会直接刷新浏览器。
优势:
webpack.config.js
const webpack = require('webpack');
devServer: { contentBase: './dist',//服务器起在哪个文件夹下 open: true,//自动打开浏览器 port: 8080,//使用哪一个端口号 // proxy: { // './api':'http://locallhost:3000' // }//若是访问api这个地址,也就是locallhost:8000/api, 它会帮你转发到http://locallhost:3000这个地址上 hot:true,//开启热更新 hotOnly:true//即使热更新没有生效,也不刷新浏览器 },
plugins: [ new HtmlWebpackPlugin({ template: "src/index.html" }), new CleanWebpackPlugin(), new webpack.HotModuleReplacementPlugin() ],
index.js内
若是开启热更新, number.js文件只要发生变化就会从新执行一下
npm i -D babel-loader @babel/core npm i @babel/preset-env -D npm i --save @babel/polyfill
babel-loader插件只是做为babel与webpack沟通的桥梁, 若是想要翻译ES6语法, 须要安装@babel/preset-env插件。
babel/polyfill 用来补充babel/preset-env的, 有的语法babel/preset-env不能翻译(如Promise), 这时候可使用babel/polyfill。
在业务代码js的顶部引入babel/polyfill 。若是使用了useBuiltIns:"usage"
,也能够不引入此插件。
import "@babel/polyfill";
webpack.config.js
{ test: /\.js$/,//js文件由ES6转成ES5 exclude: /node_modules/,//无论这个文件夹下的js文件 loader: "babel-loader", options: { presets: [["@babel/preset-env",{ targets: { edge: "17", firefox: "60", chrome: "67", safari: "11.1" },//浏览器版本,如chrome版本大于67将不会翻译成ES5 useBuiltIns:"usage"//js里用哪一个翻译那个,不用的语法特性不会翻译, 减小文件体积 }]] } }
若是开发组件库或者第三方模块的时候, 不要使用@babel/polyfill插件。由于它在注入promise或者map方法的时候, 它会经过全局变量的方式注入, 会污染到全局环境。
咱们能够换一种方式:
首先把业务代码中引入的@babel/polyfill注释掉, 而后按照 @babel/plugin-transform-runtime和@babel/runtime插件。
npm i -D @babel/plugin-transform-runtime npm i --save @babel/runtime npm i --save @babel/runtime-corejs2
webpack.config.js
{ test: /\.js$/,//js文件由ES6转成ES5 exclude: /node_modules/,//无论这个文件夹下的js文件 loader: "babel-loader", options: { plugins: [["@babel/plugin-transform-runtime",{ "corejs":2, "helpers":true, "regenerator":true, "useESModules":false }]] } }
若是plugins内corejs配置了2, 那么就要安装@babel/runtime-corejs2这个插件了。
固然, 在咱们配置过多的babel配置时, 也能够在根目录建立一个.babelrc文件。用来放置相关配置, 以下:
webpack.config.js
{ test: /\.js$/,//js文件由ES6转成ES5 exclude: /node_modules/,//无论这个文件夹下的js文件 //include: path.resolve(__dirname,'../src')//只对src目录的js文件打包 loader: "babel-loader" }
babelrc
{ "plugins": [["@babel/plugin-transform-runtime",{ "corejs":2, "helpers":true, "regenerator":true, "useESModules":false }]] }
npm i react react-dom --save npm i --save-dev @babel/preset-react
babelrl
{ "presets": [ [ "@babel/preset-env", { "targets": { "edge": "17", "firefox": "60", "chrome": "67", "safari": "11.1" }, "useBuiltIns": "usage" } ], "@babel/preset-react" ] }
这个执行顺序是按照从下到上,从右到左来执行的。顺序必定不要写反了。它是先执行react转成js, 而后执行babel转成ES5的。
Tree Shaking支持ES6的Module引入方式,它只支持静态引入的方式, 动态引入的方式它不支持。
Tree Shaking中文翻译是摇树的意思, 大意就是把无效的代码摇晃掉, 只留下有用的代码。
好比你引入了一个模块中的方法, 它就只打包你引入的方法, 不须要的方法不会进行打包了。
在开发环境中, 是没有Tree Shaking功能的, 若是须要请添加如下配置:
webpack.config.js
在线上环境中能够不配置这个
optimization:{ usedExports:true }, output: { //输出到哪里 filename: "[name].js", //输出的文件名称 name对应的是entry里的key值 path: path.resolve(__dirname, "dist") //输出到哪个文件夹下, path后面跟绝对路径 //__dirname指的是webpack.config.js文件当前所在的路径 }
package.json文件内
"name": "webpack-demo", "sideEffects": ["@babel/polly-fill"], "version": "1.0.0", "description": "", "private": true,
配置"sideEffects": ["@babel/polly-fill"]
,后, Tree Shaking不会对这个插件有任何做用了。
固然没有引用@babel/polly-fill
也能够设置为false
。
若是你的业务js代码引入了js,以下:
你也须要在"sideEffects"进行配置, 通常咱们会对css文件进行如下配置:
"sideEffects": [ "*.css" ]
只要遇到任何的css文件,那么也不要去使用Tree Shaking。
在开发环境能够方便咱们开发, 有热模块更新DevServer等配置能够更加方便咱们的调试代码。
而线上环境会压缩代码, 对source-map
精简(没有报错信息或者只显示行错误)。
首先咱们须要把webpack.config.js修改成webpack.dev.js, 表示开发环境。
const path = require("path"); //引入node核心模块 const HtmlWebpackPlugin = require("html-webpack-plugin"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const webpack = require("webpack"); module.exports = { //mode: 'production',//默认模式,会压缩代码,不写便是默认,不写会有提示 mode: "development", //开发模式, 不会压缩代码 devtool: "cheap-module-eval-source-map", entry: { main: "./src/index.js" }, //从哪个文件开始打包 devServer: { contentBase: "./dist", //服务器起在哪个文件夹下 open: true, //自动打开浏览器 port: 8080, //使用哪一个端口号 // proxy: { // './api':'http://locallhost:3000' // }//若是访问api这个地址,也就是locallhost:8000/api, 它会帮你转发到http://locallhost:3000这个地址上 hot: true, //开启热更新 hotOnly: true //即使热更新没有生效,也不刷新浏览器 }, module: { rules: [ { test: /\.(jpg|png|gif)$/, use: { // loader: 'file-loader',// 遇到jpg格式不知道怎么打包就去求助file-loader插件 loader: "url-loader", //图片转化为base64, 不是单独生成一个文件。 options: { // placeholder 占位符 name: "[name]_[hash].[ext]", //name 打包文件名字 name/原有文件名字 hash/本次打包哈希值 ext/原有名字后缀 outputPath: "images/", //把图片文件打包到images目录下 limit: 204800 //若是文件超过204800字节,就会像file-loader打包到dist目录下生成一个文件, //若是文件小于204800字节,那就回变成base64字符串, 放到js内 } } }, { test: /\.(eot|ttf|woff|woff2|svg)$/, use: { loader: "file-loader", options: { outputPath: "fonts/" } } }, { test: /\.scss$/, use: [ "style-loader", { loader: "css-loader", options: { importLoaders: 2 //在scss又引入另一个scss时,有可能直接走css-loader,不走sass-loader和postcss-loader,加上此配置项可让它继续走下面两个loader //modules: true//开启css模块化打包 解决全局样式冲突 } }, "sass-loader", //sass文件编译 "postcss-loader" //加厂商前缀 ] }, { test: /\.css$/, use: [ "style-loader", "css-loader", "postcss-loader" //加厂商前缀 ] }, { test: /\.js$/,//js文件由ES6转成ES5 exclude: /node_modules/,//无论这个文件夹下的js文件 loader: "babel-loader" } ] }, plugins: [ new HtmlWebpackPlugin({ template: "src/index.html" }), new CleanWebpackPlugin(), new webpack.HotModuleReplacementPlugin() ], optimization:{ usedExports:true }, output: { //输出到哪里 filename: "[name].js", //输出的文件名称 name对应的是entry里的key值 path: path.resolve(__dirname, "dist") //输出到哪个文件夹下, path后面跟绝对路径 //__dirname指的是webpack.config.js文件当前所在的路径 } };
package.json
"scripts": { "dev": "webpack-dev-server --config wepack.dev.js" },
在开发环境内也能够把hotOnly: true去掉, 使浏览器能自动刷新。
而后建立webpack.prod.js文件, 表示线上环境。
const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const webpack = require("webpack"); module.exports = { mode: "production", devtool: "cheap-module-source-map", entry: { main: "./src/index.js" }, module: { rules: [ { test: /\.(jpg|png|gif)$/, use: { loader: "url-loader", options: { name: "[name]_[hash].[ext]", outputPath: "images/", limit: 204800 } } }, { test: /\.(eot|ttf|woff|woff2|svg)$/, use: { loader: "file-loader", options: { outputPath: "fonts/" } } }, { test: /\.scss$/, use: [ "style-loader", { loader: "css-loader", options: { importLoaders: 2 } }, "sass-loader", "postcss-loader" ] }, { test: /\.css$/, use: ["style-loader", "css-loader", "postcss-loader"] }, { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" } ] }, plugins: [ new HtmlWebpackPlugin({ template: "src/index.html" }), new CleanWebpackPlugin() ], output: { filename: "[name].js", path: path.resolve(__dirname, "dist") } };
package.json
"scripts": { "dev": "webpack-dev-server --config wepack.dev.js", "build":"webpack --config webpack.prod.js" },
打包完成后把dist文件夹丢给后端使用便可。
咱们能够发如今webpack.dev.js和webpack.prod.js中有不少相同代码, 这时候咱们能够把相同的代码抽离出来放到webpack.common.js中。
咱们须要引入一个插件合并common文件与prod或者dev文件
npm i webpack-merge -D
建立webpack.common.js, 相同代码放入此文件
const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); module.exports = { entry: { main: "./src/index.js" }, module: { rules: [ { test: /\.(jpg|png|gif)$/, use: { loader: "url-loader", options: { name: "[name]_[hash].[ext]", outputPath: "images/", limit: 204800 } } }, { test: /\.(eot|ttf|woff|woff2|svg)$/, use: { loader: "file-loader", options: { outputPath: "fonts/" } } }, { test: /\.scss$/, use: [ "style-loader", { loader: "css-loader", options: { importLoaders: 2 } }, "sass-loader", "postcss-loader" ] }, { test: /\.css$/, use: ["style-loader", "css-loader", "postcss-loader"] }, { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" } ] }, plugins: [ new HtmlWebpackPlugin({ template: "src/index.html" }), new CleanWebpackPlugin() ], output: { filename: "[name].js", path: path.resolve(__dirname, "dist") } };
webpack.dev.js
const webpack = require("webpack"); const merge = require("webpack-merge"); const commonConfig = require("./webpack.common.js"); const devConfig = (module.exports = { mode: "development", devtool: "cheap-module-eval-source-map", devServer: { contentBase: "./dist", open: true, port: 8080, hot: true, hotOnly: true }, plugins: [new webpack.HotModuleReplacementPlugin()], optimization: { usedExports: true } }); module.exports = merge(commonConfig, devConfig);
webpack.prod.js
const merge = require("webpack-merge"); const commonConfig = require("./webpack.common.js"); const prodConfig = { mode: "production", devtool: "cheap-module-source-map" }; module.exports = merge(commonConfig, prodConfig);
固然, 若是咱们这几个配置文件放在了根目录的build文件夹内, 你须要在package.json内对指令进行更改,把目录须要更改到build目录下
package.json
"scripts": { "dev": "webpack-dev-server --config ./build/webpack.dev.js", "build": "webpack --config ./build/webpack.prod.js" },
固然, 输出的目录也须要修改一下
webpack.common.js
output: { filename: "[name].js", path: path.resolve(__dirname, "../dist") }
Code Splitting指的是代码分割。若是咱们不使用代码分割,打包出来的文件会很大, 加载时间会很长。还有一种状况, 咱们引入一个lodash库, 这部分代码不变, 仅仅是业务代码改变了, 若是咱们再次打包就会所有又加载一遍, 影响了加载的速度。 咱们但愿的是lodash库不须要再次加载。
咱们先安装一个插件lodash
npm i lodash --save
src目录建立一个lodash.js文件
import _ from 'lodash'; window._ = _;
webpack.common.js
entry: { lodash: "./src/lodash.js", main: "./src/index.js" },
以上为同步引入方式,可按照一下代码进行配置:
webpack.common.js
optimization: { splitChunks: { chunks: 'all' } },
这段代码能帮你作代码分割。
异步引入指的是如下状况:
npm i babel-plugin-dynamic-import-webpack
.babelrc
{ "presets": [ [ "@babel/preset-env", { "targets": { "edge": "17", "firefox": "60", "chrome": "67", "safari": "11.1" }, "useBuiltIns": "usage" } ], "@babel/preset-react" ], "plugins": ["dynamic-import-webpack"] }
代码分割,和webpack无关
webpack中实现代码分割,两种方式
须要在package.json去掉babel-plugin-dynamic-import-webpack插件,由于它是一个第三方的插件,不支持魔法注释, 咱们须要一个官方的插件来进行魔法注释。
npm i -D @babel/plugin-syntax-dynamic-import
.babelrc
plugins修改成babel/plugin-syntax-dynamic-import
"plugins": ["@babel/plugin-syntax-dynamic-import"]
webpack.common.js
若是optimization不写任何内容, 只是一个空对象, 会按照如下默认配置打包:
optimization: { splitChunks: { chunks: 'aysnc', minSize: 30000, maxSize: 0, minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, automaticNameDelimiter: '~', name:true, cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }, default: false } } }
通常按照这个选项配置便可:
webpack.common.js
optimization: { splitChunks: { chunks: 'all', } }
默认配置是async
只对异步引入进行代码分割
只对同步引入进行代码分割
所有都会进行代码分割,同时必需要配置defaultVendors
defaultVendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }
执行过程是首先看是否须要代码分割, 也就是chunks配置,若是须要分割会走到cacheGroups内看如何分割, 从defaultVendors看看是否在node_modules里, 那它就符合这个配置的要求, 因而他就会把须要打包的模块(如lodash)打包在一个Vendors组里面去。
这个文件是在vendors组内, 入口文件是main.js
vendors: { test: /[\\/]node_modules[\\/]/, priority: -10, filename: 'vendors.js' },
加上filename可使文件名改成vendors.js
minSize: 30000
引入的文件大于30000字节才进行分割, 通常配置30000。
若是不是node_modules内的文件, 须要配置一个默认选项:
cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10, filename: 'vendors.js' }, default: { priority: -20, reuseExistingChunk: true, filename: 'common.js' } }
这样打包后会在文件前加default~的前缀, 以下:
基本上没用, 通常不会配置这个选项。
这个配置是进行二次拆分的选项。 好比一个lodash库, 你配置50000, 也就是50kb, 这个lodash库是1m, 那么它会拆分红20个文件, 可是通常状况下lodash库是拆分不了的。
当一个模块引入了多少次才会进行代码分割。
同时加载的模块数量。通常不会配置。默认便可。
好比咱们这个选项配置为5, 加入我引入了10个库, 分割了10个文件,那你一打开网页的时候, 同时要加载10个文件。那就违反了maxAsyncRequests配置为5的要求, 同时只能加载5个请求, 那么webpack在打包前5个库的时候会为你生成5个js文件, 超过5个它就不会作代码分割了。
通常不会配置。默认便可。
整个网站首页加载的时候, 或者说入口文件加载的时候, 入口文件可能会引入其它的js文件。入口文件引入的库若是是配置为3, 那么最多作3次代码分割。 超过3个就不会再作代码分割了。
组和文件名作链接时的链接符
为true
会让cacheGroups
起的名字有效。也就是filename
。
cacheGroups
也就是缓存组, 与上面的选项息息相关。会把符合条件的代码缓存到一个组内。
priority
指的是优先级。 哪一个大优先哪一个。
reuseExistingChunk: true
假如我有一个a模块, 又有一个b模块。a模块内又使用了b模块。在打包a代码的时候, 因为a模块使用了
b模块,因此b模块代码也会被打包进去。可是若是配置这个选项,它会去看, 以前b模块代码已经被引入过,那么它会去复用以前打包的模块。
webpack打包过程当中生成的每一个文件都是一个chunk
代码分割配置
minChunks: 2 至少两个打包文件引入这个模块 才单独分割打包
如下代码能够实现懒加载, 在点击页面后再加载代码
async function getComponent() { const { default: _ } = await import(/* webpackChunkName:"lodash" */ 'lodash'); const element = document.createElement('div'); element.innerHTML = _.join(['Dell', 'Lee'], '-'); return element; } document.addEventListener('click', () =>{ getComponent().then(element => { document.body.appendChild(element); }); })
优势: 页面加载速度更快。
懒加载并非webpack的功能, 它是ESModule的一个概念, 只不过webpack可以识别对它进行代码分割。
https://github.com/webpack/analyse
package.json
"scripts": { "dev-build": "webpack --profile --json > stats.json --config ./build/webpack.dev.js", "dev": "webpack-dev-server --config ./build/webpack.dev.js", "build": "webpack --config ./build/webpack.prod.js" },
整个打包过程的描述放在stats.json
文件内。
打开http://webpack.github.io/analyse/,把文件上传便可获得如下的分析图。
固然也可使用Webpack Bundle Analyzer这个插件。
webpack 指望首次加载速度最优化,不是利用缓存在下次加载时提升访问速度 应该提升代码使用率
show coverage 代码使用率
交互代码能够放到单独的异步模块里 提升加载速度及页面利用率
以下:
可是异步加载交互代码时:例如当点击的时候才再加载异步代码,虽然提升了页面初始化速度,可是对用用户点击
的体验很差,速度太慢;
为了解决懒加载带来的问题:使用prefretch preload
prefetch:会等主流程都加载完成,等待空闲再加载;(最优)
import(/* webpackPrefetch: true */ 'LoginModal');
preload:是和主线程一块儿加载
想要使用css代码分割咱们必需要修改一下 Tree Shaking 配置
package.json
"sideEffects": [ "*.css", "*.scss" ],
首先, 咱们须要安装一个插件
npm install --save-dev mini-css-extract-plugin
引入css文件
import './style.css';
webpack.prod.js配置
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { plugins: [new MiniCssExtractPlugin({ filename: "[name].css", chunkFilename: "[name].chunk.css" })], module: { rules: [ { test: /\.css$/i, use: [MiniCssExtractPlugin.loader, 'css-loader'], }, ], }, };
压缩css代码:
npm i optimize-css-assets-webpack-plugin -D
webpack.prod.js
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); optimization: { minimizer: [new OptimizeCSSAssetsPlugin({})] }, plugins: [ new MiniCssExtractPlugin({ filename: "[name].css", chunkFilename: "[name].chunk.css" }) ]
多个入口文件引入的css文件打包到一块儿, 须要借助splitChunks,额外增长一个style组,只要发现你的打包文件是css文件, 统一打包到一个叫styles.css的文件内,enforce
为true
忽略默认的一些参数(如minsize之类)。只要你是一个css文件我就作代码的拆分,把代码分割到style.css文件内。
webpack.prod.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { optimization: { splitChunks: { cacheGroups: { styles: { name: 'styles', test: /\.css$/, chunks: 'all', enforce: true, }, }, }, }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', }), ], module: { rules: [ { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'], }, ], }, };
当咱们想要每一个入口文件打包到不一样的css文件内的时候,仍是利用cacheGroups, 以下: 若是入口文件是foo文件就走fooStyles的逻辑,若是是bar文件就走barStyles的逻辑。
const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); function recursiveIssuer(m) { if (m.issuer) { return recursiveIssuer(m.issuer); } else if (m.name) { return m.name; } else { return false; } } module.exports = { entry: { foo: path.resolve(__dirname, 'src/foo'), bar: path.resolve(__dirname, 'src/bar'), }, optimization: { splitChunks: { cacheGroups: { fooStyles: { name: 'foo', test: (m, c, entry = 'foo') => m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry, chunks: 'all', enforce: true, }, barStyles: { name: 'bar', test: (m, c, entry = 'bar') => m.constructor.name === 'CssModule' && recursiveIssuer(m) === entry, chunks: 'all', enforce: true, }, }, }, }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', }), ], module: { rules: [ { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'], }, ], }, };
webpack.common.js
关闭性能上的警告
performance: false,
在咱们使用webpack的时候,线上代码修改的时候,由于代码的名字没有改变致使浏览器在加载
网页的时候,取的是缓存中的代码,致使没有及时的获取最新的代码,这时候就要清除浏览器的缓存, 咱们能够利用输出文件配置contenthash, 这样只有在修改代码了才会改变hash值, 就能够作到修改了代码线上浏览器缓存也会更新。
webpack.prod.js
output: { filename: '[name].[contenthash].js', chunkFilename: '[name].[contenthash].js' }
老版本若是发现即便没修改代码,打包文件的hash值也不同,请按下图配置:
shimming做用:解决webpack打包的兼容性问题.
好比你引入一个jquery.ui的库, 可是没有引入jquery, 使用了$写代码。 可是在你的业务是有引入jquery的, 这样在业务代码若是运行jqueryui初始化会报错的。 因此咱们应该使用shimming。
webpack.common.js
const webpack = require("webpack");
webpack.common.js
plugins: [ new HtmlWebpackPlugin({ template: "src/index.html" }), new CleanWebpackPlugin(), new webpack.ProvidePlugin({ $: 'jquery', _join:['lodash','join'], //_: 'lodash' }) ],
以上代码的意思就是若是个人一个模块中使用了$
, 那我就会在模块里自动帮你引入jquery这个模块。
使用_join就是lodash下的join方法
每一个模块的this都是指向自身模块, 不会指向window。 若是想要指向window, 能够引入这个插件:
npm i imports-loader --save-dev
webpack.common.js
{ test: /\.js$/, exclude: /node_modules/, use: [{ loader: "babel-loader", },{ loader: "imports-loader?this=>window" }] }
咱们换一种方式来启动不一样环境下的打包方式, 经过一个变量:
package.json
{ "name": "webpack-demo", "sideEffects": [ "*.css", "*.scss" ], "version": "1.0.0", "description": "", "private": true, "scripts": { "dev-build": "webpack --profile --json > stats.json --config ./build/webpack.common.js", "dev": "webpack-dev-server --config ./build/webpack.common.js", "build": "webpack --env.production --config ./build/webpack.common.js" }, "author": "LiuJunFeng", "license": "ISC", "devDependencies": { "@babel/core": "^7.8.4", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-transform-runtime": "^7.8.3", "@babel/preset-env": "^7.8.4", "@babel/preset-react": "^7.8.3", "autoprefixer": "^9.7.4", "babel-loader": "^8.0.6", "clean-webpack-plugin": "^3.0.0", "css-loader": "^3.4.2", "file-loader": "^5.0.2", "html-webpack-plugin": "^3.2.0", "imports-loader": "^0.8.0", "mini-css-extract-plugin": "^0.9.0", "node-sass": "^4.13.1", "optimize-css-assets-webpack-plugin": "^5.0.3", "postcss-loader": "^3.0.0", "sass-loader": "^8.0.2", "style-loader": "^1.1.3", "url-loader": "^3.0.0", "webpack": "^4.41.5", "webpack-cli": "^3.3.10", "webpack-dev-server": "^3.10.3", "webpack-merge": "^4.2.2" }, "dependencies": { "@babel/polyfill": "^7.8.3", "@babel/runtime": "^7.8.4", "@babel/runtime-corejs2": "^7.8.4", "jquery": "^3.4.1", "lodash": "^4.17.15", "react": "^16.12.0", "react-dom": "^16.12.0" }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ] }
webpack.common.js
const path = require("path"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const webpack = require("webpack"); const merge = require("webpack-merge"); const devConfig = require("./webpack.dev.js"); const prodConfig = require("./webpack.prod.js") const commonConfig = { entry: { main: "./src/index.js" }, module: { rules: [ { test: /\.(jpg|png|gif)$/, use: { loader: "url-loader", options: { name: "[name]_[hash].[ext]", outputPath: "images/", limit: 204800 } } }, { test: /\.(eot|ttf|woff|woff2|svg)$/, use: { loader: "file-loader", options: { outputPath: "fonts/" } } }, { test: /\.scss$/, use: [ "style-loader", { loader: "css-loader", options: { importLoaders: 2 } }, "sass-loader", "postcss-loader" ] }, { test: /\.js$/, exclude: /node_modules/, use: [{ loader: "babel-loader", },{ loader: "imports-loader?this=>window" }] } ] }, plugins: [ new HtmlWebpackPlugin({ template: "src/index.html" }), new CleanWebpackPlugin(), new webpack.ProvidePlugin({ $: 'jquery' }) ], optimization: { usedExports: true, splitChunks: { chunks: 'all', cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10, name: 'vendors' } } } }, performance: false, output: { path: path.resolve(__dirname, "../dist") } }; module.exports = (env) => { if(env && env.production) { return merge(commonConfig, prodConfig) } else { return merge(commonConfig, devConfig) } }
webpack.dev.js
const webpack = require("webpack"); const devConfig = (module.exports = { mode: "development", devtool: "cheap-module-eval-source-map", devServer: { contentBase: "./dist", open: true, port: 8080, hot: true, hotOnly: true }, module: { rules: [ { test: /\.scss$/, use: [ "style-loader", { loader: "css-loader", options: { importLoaders: 2 } }, "sass-loader", "postcss-loader" ] }, { test: /\.css$/, use: ["style-loader", "css-loader", "postcss-loader"] } ] }, plugins: [new webpack.HotModuleReplacementPlugin()], output: { filename: "[name].js", chunkFilename:'[name].js', } }); module.exports = devConfig;
webpack.prod.js
const webpack = require("webpack"); const devConfig = (module.exports = { mode: "development", devtool: "cheap-module-eval-source-map", devServer: { contentBase: "./dist", open: true, port: 8080, hot: true, hotOnly: true }, module: { rules: [ { test: /\.scss$/, use: [ "style-loader", { loader: "css-loader", options: { importLoaders: 2 } }, "sass-loader", "postcss-loader" ] }, { test: /\.css$/, use: ["style-loader", "css-loader", "postcss-loader"] } ] }, plugins: [new webpack.HotModuleReplacementPlugin()], output: { filename: "[name].js", chunkFilename:'[name].js', } }); module.exports = devConfig;
咱们也能够在package.json里这样写:
那么对应的webpack.common.js是这样的
还能够这么写:
package.json
webpack.common.js
建立一个新的文件夹Library, 并初始化项目:
npm init -y
安装webpack
npm i webpack webpack-cli --save
建立webpack.config.js配置如下代码:
const path = require('path'); module.exports = { mode: 'production', entry: './src/index.js', output: { path: path.resolve(__dirname,'dist'), filename: 'library.js', libraryTarget: 'umd',//不管什么方式引入组件均可以正确引入到 } }
package.json
{ "name": "Library", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "build": "webpack" }, "keywords": [], "author": "LIU", "license": "MIT", "dependencies": { "webpack": "^4.41.5", "webpack-cli": "^3.3.10" } }
若是你想这样经过src引入js, 而且想经过library获取它下面的方法或属性,
添加library: 'library'
便可
webpack.config.js
const path = require('path'); module.exports = { mode: 'production', entry: './src/index.js', output: { path: path.resolve(__dirname,'dist'), filename: 'library.js', library: 'library', libraryTarget: 'umd',//不管什么方式引入组件均可以正确引入到 //libraryTarget: 'this', //libraryTarget: 'window', //libraryTarget: 'global', } }
这样配置好如下几种方式均可以正确引入了:
webpack.config.js
const path = require('path'); module.exports = { mode: 'production', entry: './src/index.js', externals: ["lodash"],//打包过程当中若是遇到lodash库你就忽略这个库,不要把它打包到你的代码中去,防止使用时用户重复引入库(例如:lodash) // externals: { // lodash: { // //root: '_',//全局script标签引入,必须在页面注入一个名字叫_的变量 // //commonjs: 'lodash'//若是个人lodash在commonjs这个环境中使用,而且引入名字必须是lodash // } // }, output: { path: path.resolve(__dirname,'dist'), filename: 'library.js', library: 'library', libraryTarget: 'umd',//不管什么方式引入组件均可以正确引入到 } }
package.json
{ "name": "Library", "version": "1.0.0", "description": "", "main": "./dist/library.js", "scripts": { "build": "webpack" }, "keywords": [], "author": "LIU", "license": "MIT", "dependencies": { "lodash": "^4.17.15", "webpack": "^4.41.5", "webpack-cli": "^3.3.10" } }
发布组件库流程:
npm adduser
,输入用户名,密码npm publish
发布到npmnpm i http-server --save-dev
package.json
scripts命令
"start": "http-server dist",
PWA:是一种强缓存技术,访问过的页面就算服务器断开,也能经过缓存浏览以前访问的页面
只在上线环境配置就能够了, 开发环境不用考虑服务器挂掉不挂掉的问题。
npm i workbox-webpack-plugin --save-dev
webpack.prod.js
const WorkboxPlugin = require('workbox-webpack-plugin');
plugins: [ new MiniCssExtractPlugin({ filename: "[name].css", chunkFilename: "[name].chunk.css" }), new WorkboxPlugin.GenerateSW({ clientsClaim: true, skipWaiting: true }) ],
业务逻辑文件增长如下代码:
if ('serviceWorker' in navigator){ window.addEventListener('load', ()=>{ navigator.serviceWorker.register('/service-worker.js') .then(registration => { console.log('service-worker registed'); }).catch(error => { console.log('service-worker register error'); }) }) }
安装插件:
npm i ts-loader typescript --save
webpack-config.js
module: { rule: [{ test: /\.tsx?$/, use: 'ts-loader' }] },
建立tsconfig.json文件
{ "compilerOptions": { "outDir": "./dist",//输出目录 "module": "es6",//只容许es6 Module的方式引入模块 "target":"es5",//编译为Es5这样的代码 "allowJs":true//容许TS里引入js这样的模块 } }
若是想引入lodash库,而且想让它的非法错误提示出来, 首先安装一个这样的模块:
npm i @types/lodash --save-dev
以下图, 若是不向join
方法传入参数会有报错提示
若是还想安装jquery库, 那你也须要安装对应的类型文件
npm i @types/jquery --save-dev
这个网站能够查询都有哪些类型文件能够试用:
https://microsoft.github.io/TypeSearch/
若是咱们如今项目内发送请求, 咱们通常会安装一个axios库
npm i axios --save
在项目中咱们通常会在开发环境有一个请求api以供测试, 线上环境有一个请求api。 这时候咱们通常须要使用相对路径写接口地址, 可是使用相对路径接口地址带上的就是localhost了, 这时候咱们须要作一个代理:
webpack.config.js
devServer: { contentBase: './dist', open: true, port: 8080, hot: true, hotOnly: true, proxy: { '/react/api': { target: 'https://www.dell-lee.com', secure: false, pathRewrite: { 'header.json': 'demo.json' }, changeOrigin: true, headers: { host: 'www.dell-lee.com', } } } },
secure: false: https的接口须要设置这个
pathRewrite: 至关于想要去获取header.json, 配置这个获取的是demo.json。 通常用于后端接口还没写好的时候使用一个demo数据, 等写好了再使用写好的接口,只须要把这个选项注释掉, 不用去业务代码中再修改了。
changeOrigin: true 始终配置就行, 主要为了有的网站使用了origin限制。
headers: 设置请求头, 可设置host, cookie
首先咱们须要安装一个路由插件:
npm i react-router-dom --save
在咱们使用单页应用时, 若是咱们要访问list页面, 那么服务器会觉得咱们访问的是一个叫list的页面。可是咱们的文件里并无一个list.html, 那它就会提示咱们页面不存在。
想要达到咱们预期的效果, 须要配置devServer
webpack.config.js
devServer: { contentBase: './dist', open: true, port: 8080, hot: true, hotOnly: true, historyApiFallback:true,
固然你也能够单独设置每一个地址的访问,如abc.html
转发到index.html
historyApiFallback: { rewrites: [{ from: /abc.html/, to: '/index.html' }] },
historyApiFallback只在开发环境中有效,线上环境须要和后端配合
安装插件
npm i eslint --save-dev
npx eslint --init
配置好文件之后能够用此命令查看项目文件是否符合语法:
npx eslint src
安装解析器
npm i babel-eslint --save-dev
.eslintrc.js
其实咱们也能够不借助webpack, 直接使用编辑器自带的插件, 如vscode的Eslint插件, 这样使用会更加方便!
若是咱们的团队有些规范并不想要符合airbnb它的规范, 咱们能够这么配置:
首先复制出规范的名称, 以下图:
而后再.eslintrc.js文件的rules里进行配置:
假设咱们团队有一个同窗使用的不是vscode, 他没有这样的语法提示, 就会跟咱们写的代码不同。 这时候咱们就须要借助webpack了:
首先咱们须要安装一个这样的插件:
npm i eslint-loader --save-dev
而后再webpack.config.js配置
eslint-loader 必定要写在后面, 只有语法正确再进行转义或者其它。由于loader是先执行后边再执行前边的。
webpack.config.js
{ test: /\.js$/, exclude: /node_modules/, use: ['babel-loader', { loader: 'eslint-loader', options: { fix: true, }, force: 'pre', }, ], }
fix: true 自动帮你修复比较浅显的问题
force: 'pre' 强制先执行eslint-loader
不用配置webpack, 直接使用git的钩子, 再提交代码时验证语法。
能够干掉的配置:
extensions 建议配逻辑文件, css,图片类不要配置, 浪费性能。
咱们引入了一个lodash库, 咱们知道这个库的文件它是不会变的, 可是每次打包都会打包它, 咱们可让它只在第一次打包, 下次就不打包了。
首先建立一个 webpack.dll.js
const path = require('path'); const webpack = require('webpack'); module.exports = { mode: 'production', entry: { vendors: ['lodash'], react: ['react', 'react-dom'] }, output: { filename: '[name].dll.js', path: path.resolve(__dirname, '../dll'), library: '[name]' }, plugins: [ new webpack.DllPlugin({ name: '[name]', path: path.resolve(__dirname, '../dll/[name].manifest.json'), }) ] }
运行命令打包组件库:
而后安装一个插件:
npm i add-asset-html-webpack-plugin --save
在webpack.common.js引入
const fs = require('fs');//引入核心模块 const AddAssetHtmlPlugin = require("add-asset-html-webpack-plugin")
const plugins = [ new HtmlWebpackPlugin({ template: 'src/index.html' }), new CleanWebpackPlugin(['dist'], { root: path.resolve(__dirname, '../') }) ]; const files = fs.readdirSync(path.resolve(__dirname, '../dll')); files.forEach(file => { if(/.*\.dll.js/.test(file)) { plugins.push(new AddAssetHtmlWebpackPlugin({ filepath: path.resolve(__dirname, '../dll', file) })) } if(/.*\.manifest.json/.test(file)) { plugins.push(new webpack.DllReferencePlugin({ manifest: path.resolve(__dirname, '../dll', file) })) } }) module.exports = { plugins }
不要引入无用组件库, 多使用Tree Shaking, 使用SplitChunks代码拆分。
不要太详细,配置合适的便可
开发环境配置模式为development
mode: "development",
配置entry:
想要添加多页面首先在src目录增长对应的js文件,而后在entry增长入口文件
webpack.common.js
const path = require("path"); const fs = require("fs"); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const HtmlWebpackPlugin = require("html-webpack-plugin"); const AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin"); const webpack = require("webpack"); const makePlugins = configs => { const plugins = [new CleanWebpackPlugin()]; Object.keys(configs.entry).forEach(item => { plugins.push( new HtmlWebpackPlugin({ template: "src/index.html", filename: `${item}.html`, chunks: ["runtime", "vendors", item] }) ); }); const files = fs.readdirSync(path.resolve(__dirname, "../dll")); files.forEach(file => { if (/.*\.dll.js/.test(file)) { plugins.push( new AddAssetHtmlWebpackPlugin({ filepath: path.resolve(__dirname, "../dll", file) }) ); } if (/.*\.manifest.json/.test(file)) { plugins.push( new webpack.DllReferencePlugin({ manifest: path.resolve(__dirname, "../dll", file) }) ); } }); return plugins; }; const configs = { entry: { main: "./src/index.js", list: "./src/list.js" }, resolve: { extensions: [".js", ".jsx"] }, module: { rules: [ { test: /\.jsx?$/, include: path.resolve(__dirname, "../src"), use: [ { loader: "babel-loader" } ] }, { test: /\.(jpg|png|gif)$/, use: { loader: "url-loader", options: { name: "[name]_[hash].[ext]", outputPath: "images/", limit: 10240 } } }, { test: /\.(eot|ttf|svg)$/, use: { loader: "file-loader" } } ] }, optimization: { runtimeChunk: { name: "runtime" }, usedExports: true, splitChunks: { chunks: "all", cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10, name: "vendors" } } } }, performance: false, output: { path: path.resolve(__dirname, "../dist") } }; configs.plugins = makePlugins(configs); module.exports = configs;
首先建立一个文件夹 make-loader, 对项目初始化, 而后安装webpack
npm init -y npm i webpack webpack-cli --save-dev
建立文件夹 loader, loader文件夹内建立文件replaceLoader.js
咱们要实现的目标是若是发现咱们的业务逻辑文件内有dell
这个字符, 咱们要把它修改为dellLee
//replaceLoader.js //source 引入文件的内容 //this.query携带传过来的参数,name是loader配置的name参数 //loaderUtils能够分析参数, 好比参数是个对象的时候能够用到 //this.callback代替return 传递除result,还能够传递sourcemap等内容 //异步loader 使用this.async() const loaderUtils = require('loader-utils'); module.exports = function (source) { const options = loaderUtils.getOptions(this); const callback = this.async(); setTimeout(() => { const result = source.replace('dell',options.name); callback(null,result) }, 1000); }
//replaceLoader.js module.exports = function (source) { return source.replace('dell','world'); }
webpack.config.js
const path = require('path'); module.exports = { mode: "development", entry: { main: './src/index.js' }, //当你引入loader的时候,会先在node_modules里找,若是没有回去loader里去找 resolveLoader: { modules: ['node_modules','./loaders'] }, module: { rules: [{ test: /\.js/, use: [{ loader: 'replaceLoader', },{ loader: 'replaceLoaderAsync', options: { name: 'lee' } }] }] }, output: { path: path.resolve(__dirname,'dist'), filename: '[name].js' } }
若是你的loader配置参数有些诡异, 如是一个对象, 这时候咱们可使用一个插件作分析:
npm i loader-utils --save-dev
当咱们在源代码中引入一个新的js文件,或者一个其余格式的文件的时候, 咱们可使用loader处理这个引入的文件。
在咱们作打包的时候, 在某一个具体时刻上。 好比说, 当我打包结束后,我要自动生成一个html文件, 这时候咱们就可使用一个html-webpack-plugin的插件。它会在打包结束后生成html文件。
Plugin能够在咱们打包过程的某个时刻想作一些事情。
首先咱们初始化一个项目, 安装webpack和webpack-cli。
若是咱们想要生成带版权的文件, 能够这么作:
建立文件夹plugins, plugins文件夹内建立copyright-webpack-plugin.js
//options是plugin配置传过来的参数 //compiler是webpack的实例,存储了咱们webpack相关各类各样的配置文件,打包过程,等等一系列的内容。 //钩子, 指某个时刻会自动执行的函数。 如vue生命周期 //emit 当你把打包资源放到目标文件夹的时刻。它是一个异步的钩子。 能够在后面写一个tabAsync. 他有两个参数 第一个是插件名字,第二个是剪头函数 //compile 同步的钩子, 后面跟tap, 箭头函数只传compilation //compilation 和compiler不同, 只存放此次打包的相关内容 //compilation.assets 打包生成的内容 class CopyrightWebpackPlugin { // constructor(options) { // } apply(compiler) { compiler.hooks.compile.tap('CopyrightWebpackPlugin',(compilation)=>{ console.log('compiler'); }) compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin',(compilation,cb) => { compilation.assets['copyright.txt'] = { source: function() { return 'copyright by dell lee' }, size: function() { return 21; } }; cb(); }) } } module.exports = CopyrightWebpackPlugin
在webpack.config.js配置:
const path = require('path'); const CopyRightWebpackPlugin = require('./plugins/copyright-webpack-plugin') module.exports = { mode: 'development', entry: { main: './src/index.js' }, plugins: [ new CopyRightWebpackPlugin() ], output: { path: path.resolve(__dirname,'dist'), filename:'[name].js' } }
--inspect 开启node调试工具
--inspect-brk webpack执行命令的第一行打断点
输入 npm run debug后打开浏览器, 点击控制台左上角node小图标
这时候咱们就能够看到插件的详细信息了
可在watch增长compilation的监控
Vue 内的Webpack设计理念是让咱们用的更爽, 即便是webpack小白用户咱们也可以轻松使用。
若是咱们想配置webpack, 须要在项目的根目录建立一个vue.config.js
这里的配置和webpack并不同, 它对webpack的配置进行了大量的封装, 若是咱们须要配置, 可参考脚手架的配置参考:https://cli.vuejs.org/zh/config/
若是咱们想实现原生的webpack, 在脚手架参考文档使用configureWebpack 便可。