我會將全部的讀者歸納爲初學者,即便你可能有基礎,學習本節以前我但願你具備必定的JavaScript和node基礎javascript
... ...
表明省略掉部分代碼,和上面的代碼相同Web瀏覽器使用HTML,CSS和JavaScript。隨着項目的發展,跟蹤和配置全部這些文件變得很是複雜,解決這個問題就須要一個新的工具css
相似webpack的工具還有Grunt和Gulp,webpack是模塊管理工具,把你的項目按照你的想法進行劃分模塊打包,舉個最簡單的例子,這個頁面須要加載一個 a.js
和b.js
,可是你只想加載一個js文件,就可使用webpack將兩個文件進行合併,固然webpack的功能不止於此,代碼轉化、項目優化、代碼分割、代碼預編譯、自動構建、自動刷新...html
再好比你想你的代碼兼容其餘老的瀏覽器,你的css代碼兼容不一樣的瀏覽器內核,或者你想自動精簡掉你寫了可是沒有用到的代碼,這些均可以使用webpack實現vue
若是你是vue或者react等框架的使用者,確定使用過 vue-cli 或 react-create-app 這類腳手架工具,那麼實現這個效果,就要學習webpackjava
注意本文都是webpack4的內容node
建立一個 webpackdemo文件夾,使用命令npm init -y
快速初始化一個項目react
安裝 webpack可使用全局安裝webpack
npm install webpack -g
可是我更推薦你在每一個項目裏面單獨引入,這樣能夠控制版本,若是你使用 webpack 4+ 版本,你還須要安裝 CLI。git
npm install -D webpack@<version> npm install -D webpack-cli
本文默認使用項目引入的方式,咱們在根目錄下新建 src/index.js,webpack在不進行任何配置的狀況下,會默認尋找這個文件es6
而後命令行執行node_modules\.bin\webpack
,若是你是全局安裝的能夠直接使用webpack
命令
注意此時命令行爆黃色警告,這是沒有指定當前模式的緣由,而且能夠發現,目錄下多了一個 dist/main.js
文件,這即是默認的輸出文件
爲了體驗項目的打包,咱們新建一個src/clg.js
文件
export default function clg(msg) { console.log(msg); }
咱們在index.js
裏面導入並使用
import clg from './clg'; clg('webpack init');
而後根目錄咱們新建一個 index.html
文件,引入打包後的文件
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="./dist/main.js"></script> </head> <body> </body> </html>
而後修改一下打包命令,指定當前爲開發模式,再次運行.\node_modules\.bin\webpack --mode development
此次打包沒有爆警告,而且咱們打開index.html
控制檯查看結果
在前面,咱們都是使用webpack-cli
爲咱們提供的默認配置,若是咱們想使用webpack更強大的功能仍是須要自定義配置文件的,在根目錄新建webpack.config.js,執行webpack命令的時候會自動找到它
const path = require('path'); module.exports = { // 環境 mode: 'development', // 目標文件 entry: [path.resolve(__dirname, './src/index.js')], // 自定義輸出文件 output: { path: path.resolve(__dirname, './dist'), //路徑 filename: 'main.js' //文件名稱 }, // 插件 plugins: [ ], // 給導入的文件制定規則 module: { } }
爲了方便調試,咱們在 package.json
中添加命令,此時執行命令npm run dev
或npm run build
就很是方便了
注意 scripts命令裏面可省略
.\node_modules\.bin\
使用npx webpack
也是這個效果
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev":"webpack --mode development", "build":"webpack --mode production" },
在上面是一個目標文件index.js
和一個輸出文件main.js
,若是咱們想要對多個目標文件進行打包,且輸出多個文件該怎麼辦呢?咱們在根目錄新建一個 other.js
首先咱們將entry修改成多個目標文件,並設置一個鍵
名,而後修改輸出文件的名稱爲變量[name]
const path = require('path'); module.exports = { // 環境 mode: 'development', // 目標文件 entry: { index: path.resolve(__dirname, './src/index.js'), other: path.resolve(__dirname, './src/other.js') }, // [path.resolve(__dirname, './src/index.js'), path.resolve(__dirname, './src/other.js')], // 自定義輸出文件 output: { path: path.resolve(__dirname, './dist'), //路徑 filename: '[name].bundle.js' //文件名稱 }, }
此時咱們執行 npm run build
可發現dist目錄的多個js文件
在上面,咱們本身建立了一個 index.html
文件來測試咱們打包的文件是否正常,其實webpack爲咱們提供了更爲自動的方式,在這裏咱們將使用第一個webpcak插件html-webpack-plugin
,首先須要安裝它
npm i html-webpack-plugin -D
而後咱們在 webpack.config.js
中配置使用這個插件
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { // 目標文件 entry: { index: path.resolve(__dirname, './src/index.js'), other: path.resolve(__dirname, './src/other.js'), }, // 自定義輸出文件 output: { path: path.resolve(__dirname, './dist'), //路徑 filename: 'main.js' //文件名稱 }, // 插件 plugins: [ new HtmlWebpackPlugin({ title: "Webpack init", }), ], }
此時,咱們刪除 index.html
文件,而後再次執行npm run build
能夠發現,webpack自動爲咱們建立了一個indedx.html
文件,並引入了打包後的js文件
在上面,咱們都是使用的一個 index.html
單頁面,實際開發中漸進式的單頁面程序也比較多,可是仍是會有多頁面的場景
簡單的修改一下webpack.config.js
,屢次實例化插件就能夠了,filename
爲輸出文件名,chunks
爲這個頁面須要使用的js文件,固然若是你不是使用的自動生成頁面,可使用template
屬性指定你的頁面位置
module.exports = { ... ... // 插件 plugins: [ new HtmlWebpackPlugin({ filename:"index.html", title: "Webpack init", chunks:['index'] }), new HtmlWebpackPlugin({ filename:"other.html", title: "Webpack init", chunks:['other'] }), new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin(), new CleanWebpackPlugin(), ], ... ... }
此時咱們使用npm run dev
,此時能夠發現dist
目錄輸出了兩個頁面並引入不一樣的js文件
打包後的js文件都混淆到了一個或者多個文件中,丟失了本來的文件格式,若是在運行過程當中出現bug,很難定位本來的錯誤位置 source map 就能夠解決這個問題
爲了更容易地追蹤錯誤和警告,JavaScript 提供了 source map 功能,將編譯後的代碼映射回原始源代碼。若是一個錯誤來自於
b.js
,source map 就會明確的告訴你。
開啓 source map 很是簡單,只須要在配置文件webpack.config.js
中增長
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const webpack = require('webpack'); module.exports = { ... ... devtool: "cheap-module-eval-source-map", ... ... }
爲了驗證是否生效,咱們在other.js
中增長
console.log('other'); console.error("error")
而後使用npm run dev
,接着在控制檯查看錯誤並點擊,便能跳轉到出錯的位置
devtool有多個模式,不一樣的性能和品質,開發環境中咱們但願性能更好,生產環境咱們但願質量更好詳細配置
開發環境可使用cheap-module-eval-source-map、eval 、eval-source-map
生產環境可使用inline-source-map、inline-cheap-module-source-map、cheap-source-map
在上面的內容中,每次修改內容後都須要手動執行構建命令,webpack爲咱們提供了更爲自動的方法
咱們只需簡單的修改一下命令npm run dev --watch
,一樣的咱們爲了方便,能夠直接將命令寫入 package.json
中
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack --mode development", "build": "webpack --mode production", "watch":"webpack --mode production --watch" },
此時,咱們執行npm run watch
命令後,只要修改文件中的內容,webpack便可自動構建
可是在實際開發中,使用webpack-dev-server(簡稱wds)更爲方便,它爲咱們提供了一個簡單的服務器,而且瀏覽器可以實時加載,也就是說,當你修改文件保存後,瀏覽器可自動加載最新的內容,而且這一切都是發生在內存中,構建速度更快
安裝
npm i webpack-dev-server -D
一樣的咱們在 package.json
中添加一個命令
"scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "webpack --mode development", "build": "webpack --mode production", "watch": "webpack --mode production --watch", "server": "webpack-dev-server --mode development" },
此時,咱們只需執行npm run server
,webpack即可自動建立一個服務器,並將項目運行在其中,當咱們修改文件中的任意內容的時候,頁面便會自動刷新
若是使用過 vscode的插件live server的同窗,不難發現,這就是相似的功能
咱們還能夠在 webpack.config.js
中進一步的對 wds進行配置
module.exports = { ... ... devServer: { /** * 日誌模式 friendly-errors-webpack-plugin 插件能夠優化輸出 * errors-only 只在發生錯誤時觸發 * minimal 只在發生錯誤或者有新的編譯時輸出 * none 沒有輸出 * normal 標準輸出 * verbose 所有輸出 */ stats: "errors-only", //默認地址 localhost host: process.env.HOST, //默認端口 8080 port: process.env.PORT, //是否直接打開瀏覽器 open: true, }, ... ... }
此時咱們再次運行 npm run server
,webpack便能按照咱們的配置來構建了
模塊熱替換(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。它容許在運行時更新各類模塊,而無需進行徹底刷新。
在上面的內容中,咱們修改文件的部份內容,webpack都須要將項目從新構建並通知瀏覽器從新渲染,這個過程十分浪費資源,使用 HMR就能夠實現,修改哪裏,從新加載哪裏的這個效果
NamedModulesPlugin插件是在熱加載時直接返回更新文件名
使用 HMR咱們只須要簡單的配置便可
webpack.config.js
... ... const webpack = require('webpack'); module.exports = { ... ... // 插件 plugins: [ ... ... new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin() ], devServer: { ... ... //是否開啓熱更替 hot: true }, }
爲了驗證是不是局部更替,我麼修改一下文件內容
index.js
import clg from './clg'; console.log('webpack init'); // module.hot Webpack經過全局變量公開HMR接口 if (module.hot) { module.hot.accept('./clg.js', function () { clg('檢測到clg模塊修改'); }) }
此時咱們使用 npm run server
將項目運行起來,簡單的修改 index.js文件中內容,發現控制檯只打印了
咱們再次修改clg.js
中內容,打個空格再保存便可,此時驗證了咱們想要的效果
開發環境(development)和生產環境(production)的構建目標差別很大,官方建議爲每一個環境編寫彼此獨立的 webpack 配置。
咱們將新建兩個配置文件webpack.dev.js(開發環境)
和webpack.prod.js(生產環境)
可是它們具備不少相同的配置,因此咱們再新建一個webpack.common.js(通用配置)
文件
咱們使用webpack-merge
插件來將不一樣的環境配置文件和通用配置文件進行合併,而且使用clean-webpack-plugin
插件來每次重置咱們的構建文件夾
npm i webpack-merge -D npm i clean-webpack-plugin -D
webpack.common.js
const path = require('path'); const webpack = require('webpack'); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: [path.resolve(__dirname, './src/index.js')], plugins: [ new CleanWebpackPlugin(), new webpack.NamedModulesPlugin(), new HtmlWebpackPlugin({ title: 'Webpack init' }) ], output: { filename: 'main.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ ] } };
webpack.dev.js
咱們再開發環境配置文件中配置 server的相關信息,而且打開source-map
const merge = require('webpack-merge'); const common = require('./webpack.common.js'); module.exports = merge(common, { mode:"development", devtool: 'eval', devServer: { stats: "errors-only", //默認地址 localhost host: process.env.HOST, //默認端口 8080 port: process.env.PORT, //是否直接打開瀏覽器 open: true, //是否開啓熱更替 hot: true, }, module: { rules: [ //打包css文件的規則 { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] } });
webpack.prod.js
咱們在生產環境配置文件中省略掉其餘配置文件
const merge = require('webpack-merge'); const common = require('./webpack.common.js'); module.exports = merge(common, { mode: "production", });
而後咱們在 package.json增長一些命令
"envdev":"webpack-dev-server --config webpack.dev.js", "envbuild":"webpack --config webpack.prod.js"
此時,咱們的項目構建就更加清晰了
webpack不只僅是打包 js文件這麼簡單,此處咱們簡單的介紹幾個經常使用的資源打包方式,更詳細的內容能夠參考官方文檔
使用過腳手架的同窗應該都記得,項目裏面的css文件能夠經過js直接引入的方式使用
import 'xxx.css';
來簡單實踐一下,首先安裝插件
npm i style-loader css-loader -D
爲了展現咱們的打包效果,咱們新建一個 js/divdoc.js
文件用來在頁面中渲染出一個字符串(此時咱們已將clg.js也轉移到 ./js
文件夾)
export default function divdoc() { //建立一個dom let element = document.createElement('div'); element.innerHTML = "webpack init"; element.classList.add('init'); //將dom渲染到頁面上 document.body.appendChild(element); }
在index.js中導入並使用
import clg from './js/clg'; import divdoc from './js/divdoc'; console.log('webpack init now'); divdoc(); // module.hot Webpack經過全局變量公開HMR接口 if (module.hot) { module.hot.accept('./js/clg.js', function () { clg('檢測到clg模塊修改'); }) }
此時運行 npm run server
查看效果
接下來咱們新建 src/css/app.css
文件
咱們前面渲染的dom節點是包含一個 class名爲 init的
.init{ color: red; }
編寫規則webpack.dev.js
module.exports = { ... ... // 給導入的文件制定規則 module: { rules: [ //打包css文件的規則 { test: /\.css$/, use: ['style-loader', 'css-loader'] }, ] } }
在index.js中導入,並查看效果
import './css/app.css';
順即可以打包一下less,首先安裝插件npm i -D less-loader
,而後寫一下規則
module: { rules: [ //打包css文件的規則 { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, ] }
咱們新建一個 src/css/app.less
文件
.init{ color: red; }
咱們在 index.js
中註釋掉本來導入的 app.css
,而後導入less文件
import './css/app.less';
從新構建項目,查看項目,效果依然生效,同理sass、stylus
也是這個用法,這裏再也不贅述
在前面,咱們打包css,最終都是將css代碼添加到頁面的 style標籤中,若是咱們想將全部的css都打包到專門的文件裏面可使用mini-css-extract-plugin插件
npm i mini-css-extract-plugin -D
而後修改一下配置webpack.prod.js
... ... const MiniCssExtractPlugin = require("mini-css-extract-plugin"); module.exports = { ... ... // 插件 plugins: [ new MiniCssExtractPlugin({ filename: "styles/[name].css", }) ], // 給導入的文件制定規則 module: { rules: [ //打包css文件的規則 // { // test: /\.css$/, // use: ['style-loader', 'css-loader'] // }, { test: /\.css$/i, use: [ MiniCssExtractPlugin.loader, 'css-loader' ], } ] } ... ... }
此時咱們執行npm run build
能夠發現dist
目錄下面建立了一個index.css
文件,由於咱們是在 index.js
中導入css文件的,[name]
的值是js的文件名,而不是css的名
固然若是你想指定輸入和導出的css的名字也是能夠的,使用這種方式,你就不須要在js中再次引入css文件了
entry: { index: path.resolve(__dirname, './src/index.js'), other: path.resolve(__dirname, './src/other.js'), app: path.resolve(__dirname, './src/css/app.css') },
webpack在處理樣式方面還有不少很強大的插件,好比purgecss能夠精簡掉頁面中沒有使用的css樣式、Autoprefixer能夠自動給你添加不一樣瀏覽器兼容的css插件
在網頁中,圖片通常都是加載的網路路徑,可是在開發中咱們都是使用的本地圖片,那麼爲了保證上線後和本地的資源位置保持一致,咱們可使用webpack來進行一下打包,最後統一上傳oss存儲
首先須要安裝url-loader
npm i url-loader -D
而後咱們在 webpack.common.js
中進行配置
const path = require('path'); const webpack = require('webpack'); const { CleanWebpackPlugin } = require("clean-webpack-plugin"); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: [path.join(__dirname, './src/index.js')], plugins: [ new CleanWebpackPlugin(), new webpack.NamedModulesPlugin(), new HtmlWebpackPlugin({ title: 'Webpack init' }) ], output: { filename: 'main.js', path: path.resolve(__dirname, 'dist'), //靜態文件打包的網絡路徑 publicPath:'https://www.lookroot.cn/assets/' }, module: { rules: [ { test: /\.(png|svg|jpg|gif)$/, use: { loader: 'url-loader', options: { //超過這個大小,圖片就打包爲圖片,不超過就打包爲base64格式的代碼 limit: 1000, //打包文件名 name: "img/[hash].[ext]", }, } }, ] } };
爲了驗證,首先咱們放一張圖片logo.jpg
到src/asserts/img
目錄下,咱們簡單的修改一下app.css
.init { color: red; } body { background-image: url("../assets/img/logo.jpg"); background-repeat: no-repeat; }
一樣的咱們也能夠在js代碼裏使用圖片,修改一下divdoc.js
import logo from "../assets/img/logo.jpg"; export default function divdoc() { ... ... // 插入一張圖片 let img = document.createElement('img'); img.src = logo; element.appendChild(img); ... ... }
而後使用npm run envdev
運行起來,效果是正常的
而後咱們使用npm run envbuild
執行編譯,而後咱們打開dist/img/main.css
body { background-image: url(https://www.lookroot.cn/assets/img/494654d849ba012e2aab0505d7c82dc0.jpg); background-repeat: no-repeat; }
咱們能夠發現,webpack就給咱們自動加上了網絡路徑,對於圖片的處理,還有能夠優化圖片的[image-webpack-loader(https://github.com/tcoopman/image-webpack-loader)、能夠自動生成雪碧圖的postcss-sprites
除了上面我說的這些資源外,webpack還支持很是多的資源格式,只要理解這個思想,使用也不難
eslint是實際開發中很是經常使用的代碼檢查工具,咱們在webpack中使用它來進行代碼檢查
首先安裝eslint、loader、錯誤格式化插件
npm i eslint eslint-loader eslint-friendly-formatter -D
而後咱們在根目錄新建一個.eslintrc.json
,固然你也可使用命令npx eslint --init
來初始化配置文件
rules表明規則,這裏我設置一個禁止使用 alert
代碼來測試是否能夠完成代碼檢查,更多規則請看文檔
{ "env": { "browser": true, "es6": true, "node": true }, "extends": "eslint:recommended", "parserOptions": { "ecmaVersion": 11, "sourceType": "module" }, "rules": { "no-alert": 2 } }
而後在webpack.dev.js
中增長配置
module: { rules: [ ... ... { test: /\.js$/, loader: 'eslint-loader', enforce: 'pre', include: [path.resolve(__dirname, 'src')], options: { formatter: require('eslint-friendly-formatter') } } ] }
而後咱們在 index.js
文件中增長一句alert("webpack init")
,而後使用命令npm run envdev
發現報錯,eslint成功捕捉到了錯誤
一樣的你還可使用 StyleLint工具來檢查你的css代碼
在實際開發中若是使用了 es6+的代碼,有些瀏覽器是不支持的,爲了兼容,全部須要將代碼進一步轉化,可使用babel進行轉化
babel的使用稍微比較繁瑣,本文只介紹在webpack的使用方法,更多細緻的東西請自行查閱
安裝本體和loader babel-loader @babel/core
, @babel/preset-env
是轉換插件的預設組合,@babel/plugin-transform-runtime
用來解決一些瀏覽器不支持的方法和對象問題
npm i @babel/runtime -S npm i babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime -D
而後咱們在根目錄新建一個配置文件.babelrc
,使用@babel/preset-env
提供的插件集合能完成大部分的工做了,targets
表示咱們的代碼要運行到哪些平臺上面,更爲詳細的請點擊
{ "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", "corejs": 3, "targets": { "browsers": [ "ie >= 8", "chrome >= 62" ] } } ] ] }
而後修改一下webpack.dev.js
module: { rules: [ ... ... { test: /\.js$/, use: [{ loader: 'babel-loader', }] } ] }
爲了驗證代碼是否轉換成功,咱們在index.js
中添加代碼
const say=(msg)=>{ console.log(msg); }
而後使用命令npm run envdev
,並打開source map 查看源文件,能夠發現箭頭函數已經被轉換了
本節的內容就是這些,下一次將會有個簡單的實戰,對於webpack還有不少要學習的地方,好比打包優化、插件編寫等等學完基礎之後,這些就須要你本身去探索