本篇博客由慕課網視頻[從基礎到實戰手把手帶你掌握新版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 便可。