本質上,webpack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler)。當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個 bundle。css
webpack 提供 mode 配置選項,配置 webpack 相應模式的內置優化。html
// webpack.production.config.js module.exports = { + mode: 'production', }
入口文件,相似於其餘語言的起始文件。好比:c 語言的 main 函數所在的文件。vue
入口起點(entry point)指示 webpack 應該使用哪一個模塊,來做爲構建其內部依賴圖的開始。進入入口起點後,webpack 會找出有哪些模塊和庫是入口起點(直接和間接)依賴的。node
能夠在 webpack 的配置文件中配置入口,配置節點爲: entry
,固然能夠配置一個入口,也能夠配置多個。react
output 屬性告訴 webpack 在哪裏輸出它所建立的 bundles,以及如何命名這些文件。jquery
const path = require('path'); module.exports = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js' } };
loader 讓 webpack 可以去處理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 能夠將全部類型的文件轉換爲 webpack 可以處理的有效模塊,而後你就能夠利用 webpack 的打包能力,對它們進行處理。webpack
loader 被用於轉換某些類型的模塊,而插件則能夠用於執行範圍更廣的任務。插件的範圍包括,從打包優化和壓縮,一直到從新定義環境中的變量。插件接口功能極其強大,能夠用來處理各類各樣的任務。css3
請確保安裝了 Node.js
的最新版本。並且已經在您的項目根目錄下已經初始化好了最基本的package.json
文件git
$ npm install --save-dev webpack # 若是你使用 webpack 4+ 版本,你還須要安裝 CLI。 npm install --save-dev webpack-cli
安裝完成後,能夠添加npm
的script
腳本github
// package.json "scripts": { "start": "webpack --config webpack.config.js" }
將使 webpack 在全局環境下可用:
npm install --global webpack
注意:不推薦全局安裝 webpack。這會將你項目中的 webpack 鎖定到指定版本,而且在使用不一樣的 webpack 版本的項目中,可能會致使構建失敗。
首先咱們建立一個目錄,初始化 npm,而後 在本地安裝 webpack,接着安裝 webpack-cli(此工具用於在命令行中運行 webpack):
mkdir webpack-demo && cd webpack-demo npm init -y npm install webpack webpack-cli --save-dev
項目結構
webpack-demo
+ |- package.json + |- /dist + |- index.html + |- /src + |- index.js
npm install --save lodash
編寫:src/index.js 文件
import _ from 'lodash'; function createDomElement() { var dom = document.createElement('div'); dom.innerHTML = _.join(['aicoder', '.com', ' wow'], ''); return dom; } document.body.appendChild(createDomElement());
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>起步</title> </head> <body> <script src="./main.js"></script> </body> </html>
根目錄下添加 webpack.config.js
文件。
webpack-demo
|- package.json
+ |- webpack.config.js |- /dist |- index.html |- /src |- index.js
webpack.config.js 內容以下:
const path = require('path'); module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, './dist') } };
直接執行構建任務:
npx webpack
打開: dist/index.html 能夠查看到頁面的結果。
webpack 最出色的功能之一就是,除了 JavaScript,還能夠經過 loader 引入任何其餘類型的文件
style-loader
和 css-loader
npm install --save-dev style-loader css-loader
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] } };
css-loader
: 輔助解析 js 中的 import './main.css'
style-loader
: 把 js 中引入的 css 內容 注入到 html 標籤中,並添加 style 標籤.依賴 css-loader
你能夠在依賴於此樣式的 js 文件中 導入樣式文件,好比:import './style.css'。如今,當該 js 模塊運行時,含有 CSS 字符串的
<style>
標籤,將被插入到 html 文件的<head>
中。
在 src 目錄中添加 style.css
文件
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- bundle.js
|- index.html
|- /src
+ |- style.css |- index.js |- /node_modules
src/style.css
.hello { color: red; }
修改 js 文件
import _ from 'lodash';
+ import './style.css'; function createDomElement() { let dom = document.createElement('div'); dom.innerHTML = _.join(['aicoder', '.com', ' wow'], ''); + dom.className = 'hello'; return dom; } document.body.appendChild(createDomElement());
最後從新打開 dist 目錄下的 index.html 看一下文字是否變成了紅色的了。
模塊(module): 這些選項決定了如何處理項目中的不一樣類型的模塊。
webpack 模塊能夠支持以下:
(url(...))
或 HTML 文件(<img src=...>)
中的圖片連接(image url)
值的類型: RegExp | [RegExp] | function
防止 webpack 解析那些任何與給定正則表達式相匹配的文件。忽略的文件中不該該含有 import, require, define 的調用,或任何其餘導入機制。忽略大型的 library 能夠提升構建性能。
module.exports = { mode: 'devleopment', entry: './src/index.js', ... module: { noParse: /jquery|lodash/, // 從 webpack 3.0.0 開始,可使用函數,以下所示 // noParse: function(content) { // return /jquery|lodash/.test(content); // } } ... };
建立模塊時,匹配請求的規則數組。這些規則可以修改模塊的建立方式。這些規則可以對模塊(module)應用 loader,或者修改解析器(parser)。
module.exports = { ... module: { noParse: /jquery|lodash/, rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] } ... };
module.exports = { ... module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] } ... };
其餘的條件好比:
{ include: Condition }
:匹配特定條件。通常是提供一個字符串或者字符串數組,但這不是強制的。{ exclude: Condition }
:排除特定條件。通常是提供一個字符串或字符串數組,但這不是強制的。{ and: [Condition] }
:必須匹配數組中的全部條件{ or: [Condition] }
:匹配數組中任何一個條件{ not: [Condition] }
:必須排除這個條件module.exports = { ... module: { rules: [ { test: /\.css$/, include: [ path.resolve(__dirname, "app/styles"), path.resolve(__dirname, "vendor/styles") ], use: ['style-loader', 'css-loader'] } ] } ... };
應用於模塊指定使用一個 loader。
Loaders can be chained by passing multiple loaders, which will be applied from right to left (last to first configured).
加載器能夠鏈式傳遞,從右向左進行應用到模塊上。
use: [ 'style-loader', { loader: 'css-loader' }, { loader: 'less-loader', options: { noIeCompat: true } } ];
傳遞字符串(如:use: [ "style-loader" ])是 loader 屬性的簡寫方式(如:use: [ { loader: "style-loader "} ])。
加載 Sass 須要sass-loader
。
安裝
npm install sass-loader node-sass webpack --save-dev
使用:
// webpack.config.js module.exports = { ... module: { rules: [{ test: /\.scss$/, use: [{ loader: "style-loader" }, { loader: "css-loader" }, { loader: "sass-loader" }] }] } };
爲 sass 文件注入內容:
若是你要將 Sass 代碼放在實際的入口文件(entry file)以前,能夠設置 data 選項。此時 sass-loader 不會覆蓋 data 選項,只會將它拼接在入口文件的內容以前。
{ loader: "sass-loader", options: { data: "$env: " + process.env.NODE_ENV + ";" } }
注意:因爲代碼注入, 會破壞整個入口文件的 source map。 一般一個簡單的解決方案是,多個 Sass 文件入口。
css-loader
和sass-loader
均可以經過該 options 設置啓用 sourcemap。
// webpack.config.js module.exports = { ... module: { rules: [{ test: /\.scss$/, use: [{ loader: "style-loader" }, { loader: "css-loader", options: { sourceMap: true } }, { loader: "sass-loader", options: { sourceMap: true } }] }] } };
PostCSS是一個 CSS 的預處理工具,能夠幫助咱們:給 CSS3 的屬性添加前綴,樣式格式校驗(stylelint),提早使用 css 的新特性好比:表格佈局,更重要的是能夠實現 CSS 的模塊化,防止 CSS 樣式衝突。
咱們經常使用的就是使用 PostCSS 進行添加前綴,以此爲例:
安裝
npm i -D postcss-loader npm install autoprefixer --save-dev # 如下能夠不用安裝 # cssnext可讓你寫CSS4的語言,並能配合autoprefixer進行瀏覽器兼容的不全,並且還支持嵌套語法 $ npm install postcss-cssnext --save-dev # 相似scss的語法,實際上若是隻是想用嵌套的話有cssnext就夠了 $ npm install precss --save-dev # 在@import css文件的時候讓webpack監聽並編譯 $ npm install postcss-import --save-dev
const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ 'style-loader', { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前綴 ] } }, { loader: 'sass-loader', options: { sourceMap: true } } ] } ] } };
首先如下的 css 的處理咱們都把 mode 設置爲 production
。
webpack4 開始使用: mini-css-extract-plugin
插件, 1-3 的版本能夠用: extract-text-webpack-plugin
抽取了樣式,就不能再用
style-loader
注入到 html 中了。
npm install --save-dev mini-css-extract-plugin
const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const devMode = process.env.NODE_ENV !== 'production'; // 判斷當前環境是開發環境仍是 部署環境,主要是 mode屬性的設置值。 module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader' ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: devMode ? '[name].css' : '[name].[hash].css', // 設置最終輸出的文件名 chunkFilename: devMode ? '[id].css' : '[id].[hash].css' }) ] };
再次運行打包:
在 dist 目錄中已經把 css 抽取到單獨的一個 css 文件中了。修改 html,引入此 css 就能看到結果了。
webpack5 貌似會內置 css 的壓縮,webpack4 能夠本身設置一個插件便可。
壓縮 css 插件:optimize-css-assets-webpack-plugin
安裝
npm i -D optimize-css-assets-webpack-plugin
const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const autoprefixer = require('autoprefixer'); module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'main.[hash].js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader' }, { loader: 'postcss-loader', options: { ident: 'postcss', plugins: loader => [autoprefixer({ browsers: ['> 0.15% in CN'] })] } }, { loader: 'sass-loader' } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name][hash].css', chunkFilename: '[id][hash].css' }) ], optimization: { minimizer: [new OptimizeCSSAssetsPlugin({})] } };
壓縮須要一個插件: uglifyjs-webpack-plugin
, 此插件須要一個前提就是:mode: 'production'
.
安裝
npm i -D uglifyjs-webpack-plugin
const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const autoprefixer = require('autoprefixer'); module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'main.[hash].js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader' }, { loader: 'postcss-loader', options: { ident: 'postcss', plugins: loader => [autoprefixer({ browsers: ['> 0.15% in CN'] })] } }, { loader: 'sass-loader' } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name][hash].css', chunkFilename: '[id][hash].css' }) ], optimization: { minimizer: [ new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps }), new OptimizeCSSAssetsPlugin({}) ] } };
HtmlWebpackPlugin
插件,能夠把打包後的 CSS 或者 JS 文件引用直接注入到 HTML 模板中,這樣就不用每次手動修改文件引用了。
安裝
npm install --save-dev html-webpack-plugin
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const autoprefixer = require('autoprefixer'); module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'main.[hash].js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader' }, { loader: 'postcss-loader', options: { ident: 'postcss', plugins: loader => [autoprefixer({ browsers: ['> 0.15% in CN'] })] } }, { loader: 'sass-loader' } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name][hash].css', chunkFilename: '[id][hash].css' }), new HtmlWebpackPlugin({ title: 'AICODER 全棧線下實習', // 默認值:Webpack App filename: 'main.html', // 默認值: 'index.html' template: path.resolve(__dirname, 'src/index.html'), minify: { collapseWhitespace: true, removeComments: true, removeAttributeQuotes: true // 移除屬性的引號 } }) ], optimization: { minimizer: [ new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps }), new OptimizeCSSAssetsPlugin({}) ] } };
每次構建,咱們的 /dist 文件夾都會保存生成的文件,而後就會很是雜亂。
一般,在每次構建前清理 /dist 文件夾,是比較推薦的作法
clean-webpack-plugin
是一個比較普及的管理插件,讓咱們安裝和配置下。
npm install clean-webpack-plugin --save-dev
webpack.config.js
const path = require('path'); .... + const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' }, plugins: [ + new CleanWebpackPlugin(['dist']) ... ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } ... };
如今執行 npm run build,再檢查 /dist 文件夾。若是一切順利,你如今應該不會再看到舊的文件,只有構建後生成的文件!
在 css 文件或者 sass 文件中添加以下代碼
$red: #900; $size: 20px; .box { height: 30px*2; font-size: $size; transform: translate3d( 0, 0, 0 ); + background: url('../static/1.jpeg') }
運行打包發現以下錯誤:
ERROR in ./src/static/1.jpeg 1:0 Module parse failed: Unexpected character '�' (1:0) You may need an appropriate loader to handle this file type.
解決方案:file-loader
處理文件的導入
npm install --save-dev file-loader
webpack.config.js
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, + { + test: /\.(png|svg|jpg|gif)$/, + use: [ + 'file-loader' + ] + } ] } };
此時運行打包,發現 dist 目錄多了一個圖片文件,另外報錯再也不出現。
那更進一步,圖片如何進行優化呢?
image-webpack-loader
能夠幫助咱們對圖片進行壓縮和優化。
npm install image-webpack-loader --save-dev
使用:webpack.config.js
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|svg|jpg|gif|jpeg|ico)$/, 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 + } + } + }, ] } ] } };
此時在運行 webpack,發現會 生成的圖片的大小會被壓縮不少。
url-loader
功能相似於 file-loader,能夠把 url 地址對應的文件,打包成 base64 的 DataURL,提升訪問的效率。
如何使用:
npm install --save-dev url-loader
webpack.config.js
module.exports = { module: { rules: [ { test: /\.(png|svg|jpg|gif|jpeg|ico|woff|woff2|eot|ttf|otf)$/, use: [ { loader: 'url-loader', // 根據圖片大小,把圖片優化成base64 options: { limit: 10000 } }, { 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 } } } ] } ] } };
因爲 css 中可能引用到自定義的字體,處理也是跟圖片一致。
const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|svg|jpg|gif)$/, use: [ 'file-loader' ] }, + { + test: /\.(woff|woff2|eot|ttf|otf)$/, + use: [ + 'file-loader' + ] + } ] } };
開發環境(development)和生產環境(production)配置文件有不少不一樣點,可是也有一部分是相同的配置內容,若是在兩個配置文件中都添加相同的配置節點, 就很是不爽。
webpack-merge
的工具能夠實現兩個配置文件進合併,這樣咱們就能夠把 開發環境和生產環境的公共配置抽取到一個公共的配置文件中。
安裝:
npm install --save-dev webpack-merge
例如:
project
webpack-demo
|- package.json
- |- webpack.config.js + |- webpack.common.js + |- webpack.dev.js + |- webpack.prod.js |- /dist |- /src |- index.js |- math.js |- /node_modules
webpack.common.js
+ const path = require('path'); + const CleanWebpackPlugin = require('clean-webpack-plugin'); + const HtmlWebpackPlugin = require('html-webpack-plugin'); + + module.exports = { + entry: { + app: './src/index.js' + }, + plugins: [ + new CleanWebpackPlugin(['dist']), + new HtmlWebpackPlugin({ + title: 'Production' + }) + ], + output: { + filename: '[name].bundle.js', + path: path.resolve(__dirname, 'dist') + } + };
webpack.dev.js
+ const merge = require('webpack-merge'); + const common = require('./webpack.common.js'); + + module.exports = merge(common, { + devtool: 'inline-source-map', + devServer: { + contentBase: './dist' + } + });
webpack.prod.js
+ const merge = require('webpack-merge'); + const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); + const common = require('./webpack.common.js'); + + module.exports = merge(common, { + plugins: [ + new UglifyJSPlugin() + ] + });
當 webpack 打包源代碼時,可能會很難追蹤到錯誤和警告在源代碼中的原始位置。例如,若是將三個源文件(a.js, b.js 和 c.js)打包到一個 bundle(bundle.js)中,而其中一個源文件包含一個錯誤,那麼堆棧跟蹤就會簡單地指向到 bundle.js。
使用 inline-source-map
選項,這有助於解釋說明 js 原始出錯的位置。(不要用於生產環境):
webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' }, + devtool: 'inline-source-map', plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'Development' }) ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };
每次修改完畢後,都手動編譯異常痛苦。最簡單解決的辦法就是啓動watch
。
npx webpack --watch
固然能夠添加到 npm 的 script 中
package.json
{ "name": "development", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "watch": "npx webpack --watch", "build": "npx webpack" }, "devDependencies": { "clean-webpack-plugin": "^0.1.16", "css-loader": "^0.28.4", "csv-loader": "^2.1.1", "file-loader": "^0.11.2", "html-webpack-plugin": "^2.29.0", "style-loader": "^0.18.2", "webpack": "^3.0.0", "xml-loader": "^1.2.1" } }
可是有個 bug,就是每次咱們修改 js 或者 css 文件後,要看到修改後的 html 的變化,須要我本身從新刷新頁面。
如何能不刷新頁面,自動更新變化呢?
webpack-dev-server 爲你提供了一個簡單的 web 服務器,而且可以實時從新加載(live reloading)。
安裝
npm install --save-dev webpack-dev-server
webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' }, devtool: 'inline-source-map', + devServer: { + contentBase: './dist' + }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'Development' }) ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };
啓動此 webserver:
webpack-dev-server --open
devServer: { clientLogLevel: 'warning', // 可能的值有 none, error, warning 或者 info(默認值) hot: true, // 啓用 webpack 的模塊熱替換特性, 這個須要配合: webpack.HotModuleReplacementPlugin插件 contentBase: path.join(__dirname, "dist"), // 告訴服務器從哪裏提供內容, 默認狀況下,將使用當前工做目錄做爲提供內容的目錄 compress: true, // 一切服務都啓用gzip 壓縮 host: '0.0.0.0', // 指定使用一個 host。默認是 localhost。若是你但願服務器外部可訪問 0.0.0.0 port: 8080, // 端口 open: true, // 是否打開瀏覽器 overlay: { // 出現錯誤或者警告的時候,是否覆蓋頁面線上錯誤消息。 warnings: true, errors: true }, publicPath: '/', // 此路徑下的打包文件可在瀏覽器中訪問。 proxy: { // 設置代理 "/api": { // 訪問api開頭的請求,會跳轉到 下面的target配置 target: "http://192.168.0.102:8080", pathRewrite: {"^/api" : "/mockjsdata/5/api"} } }, quiet: true, // necessary for FriendlyErrorsPlugin. 啓用 quiet 後,除了初始啓動信息以外的任何內容都不會被打印到控制檯。這也意味着來自 webpack 的錯誤或警告在控制檯不可見。 watchOptions: { // 監視文件相關的控制選項 poll: true, // webpack 使用文件系統(file system)獲取文件改動的通知。在某些狀況下,不會正常工做。例如,當使用 Network File System (NFS) 時。Vagrant 也有不少問題。在這些狀況下,請使用輪詢. poll: true。固然 poll也能夠設置成毫秒數,好比: poll: 1000 ignored: /node_modules/, // 忽略監控的文件夾,正則 aggregateTimeout: 300 // 默認值,當第一個文件更改,會在從新構建前增長延遲 } }
如何啓用熱更新呢?
webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); + const webpack = require('webpack'); module.exports = { entry: { app: './src/index.js' }, devtool: 'inline-source-map', devServer: { contentBase: './dist', + hot: true }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'Hot Module Replacement' }), + new webpack.NamedModulesPlugin(), // 更容易查看(patch)的依賴 + new webpack.HotModuleReplacementPlugin() // 替換插件 ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };
雖然現代的瀏覽器已經兼容了96%以上的ES6的語法了,可是爲了兼容老式的瀏覽器(IE八、9)咱們須要把最新的ES6的語法轉成ES5的。那麼babel
的loader就出場了。
安裝
npm i -D babel-loader babel-core babel-preset-env
用法
在webpack的配置文件中,添加js的處理模塊。
module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, // 加快編譯速度,不包含node_modules文件夾內容 use: { loader: 'babel-loader' } } ] }
而後,在項目根目錄下,添加babel的配置文件 .babelrc
.
.babelrc
文件以下:
{ "presets": ["env"] }
最後,在入口js文件中,添加ES6的❤新語法:
class Temp { show() { console.log('this.Age :', this.Age); } get Age() { return this._age; } set Age(val) { this._age = val + 1; } } let t = new Temp(); t.Age = 19; t.show();
最後打包:
npx webpack
最終打包後的js代碼:
var a = 1, b = 3, c = 9; console.log('a :', a); console.log('b :', b); console.log('c :', c); var Temp = function () { function Temp() { _classCallCheck(this, Temp); } _createClass(Temp, [{ key: 'show', value: function show() { console.log('this.Age :', this.Age); } }, { key: 'Age', get: function get() { return this._age; }, set: function set(val) { this._age = val + 1; } }]); return Temp; }(); var t = new Temp(); t.Age = 19; t.show();
babel-loader能夠配置以下幾個options:
cacheDirectory
:默認值爲 false。當有設置時,指定的目錄將用來緩存 loader 的執行結果。以後的 webpack 構建,將會嘗試讀取緩存,來避免在每次執行時,可能產生的、高性能消耗的 Babel 從新編譯過程(recompilation process)。若是設置了一個空值 (loader: 'babel-loader?cacheDirectory') 或者 true (loader: babel-loader?cacheDirectory=true),loader 將使用默認的緩存目錄 node_modules/.cache/babel-loader,若是在任何根目錄下都沒有找到 node_modules 目錄,將會降級回退到操做系統默認的臨時文件目錄。
cacheIdentifier
:默認是一個由 babel-core 版本號,babel-loader 版本號,.babelrc 文件內容(存在的狀況下),環境變量 BABEL_ENV 的值(沒有時降級到 NODE_ENV)組成的字符串。能夠設置爲一個自定義的值,在 identifier 改變後,強制緩存失效。
forceEnv
:默認將解析 BABEL_ENV 而後是 NODE_ENV。容許你在 loader 級別上覆蓋 BABEL_ENV/NODE_ENV。對有不一樣 babel 配置的,客戶端和服務端同構應用很是有用。
注意:sourceMap 選項是被忽略的。當 webpack 配置了 sourceMap 時(經過 devtool 配置選項),將會自動生成 sourceMap。
babel 在每一個文件都插入了輔助代碼,使代碼體積過大.babel 對一些公共方法使用了很是小的輔助代碼,好比 _extend。 默認狀況下會被添加到每個須要它的文件中。你能夠引入 babel runtime
做爲一個獨立模塊,來避免重複引入。
安裝:
npm install babel-plugin-transform-runtime --save-dev npm install babel-runtime --save
配置:
webpack.config.js
rules: [ // 'transform-runtime' 插件告訴 babel 要引用 runtime 來代替注入。 { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', } } ]
修改.babelrc
{ "presets": ["env"], "plugins": [ ["transform-runtime", { "helpers": true, "polyfill": true, "regenerator": true, "moduleName": "babel-runtime" }] ] }
此時,webpack打包的時候,會自動優化重複引入公共方法的問題。
安裝
npm install eslint --save-dev npm install eslint-loader --save-dev # 如下是用到的額外的須要安裝的eslint的解釋器、校驗規則等 npm i -D babel-eslint standard
使用
// webpack.config.js module.exports = { // ... module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: "eslint-loader", options: { // eslint options (if necessary) fix: true } }, ], }, // ... }
eslint配置能夠直接放到webpack的配置文件中,也能夠直接放到項目根目錄的 .eslintrc
中文檔。
// .eslintrc.js // https://eslint.org/docs/user-guide/configuring module.exports = { root: true, parserOptions: { parser: 'babel-eslint' }, env: { browser: true }, extends: [ // https://github.com/standard/standard/blob/master/docs/RULES-en.md 'standard' ], globals: { NODE_ENV: false }, rules: { // allow async-await 'generator-star-spacing': 'off', // allow debugger during development 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', // 添加,分號必須 semi: ['error', 'always'], 'no-unexpected-multiline': 'off', 'space-before-function-paren': ['error', 'never'], // 'quotes': ["error", "double", { "avoidEscape": true }] quotes: [ 'error', 'single', { avoidEscape: true } ] } };
此時eslint的配置就結束了。
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CleanWebpackPlugin = require('clean-webpack-plugin'); const autoprefixer = require('autoprefixer'); const webpack = require('webpack'); module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, './dist') }, devtool: 'inline-source-map', devServer: { clientLogLevel: 'warning', // 可能的值有 none, error, warning 或者 info(默認值) hot: true, // 啓用 webpack 的模塊熱替換特性, 這個須要配合: webpack.HotModuleReplacementPlugin插件 contentBase: path.join(__dirname, "dist"), // 告訴服務器從哪裏提供內容, 默認狀況下,將使用當前工做目錄做爲提供內容的目錄 compress: true, // 一切服務都啓用gzip 壓縮 host: '0.0.0.0', // 指定使用一個 host。默認是 localhost。若是你但願服務器外部可訪問 0.0.0.0 port: 8085, // 端口 open: true, // 是否打開瀏覽器 overlay: { // 出現錯誤或者警告的時候,是否覆蓋頁面線上錯誤消息。 warnings: true, errors: true }, publicPath: '/', // 此路徑下的打包文件可在瀏覽器中訪問。 proxy: { // 設置代理 "/api": { // 訪問api開頭的請求,會跳轉到 下面的target配置 target: "http://192.168.0.102:8080", pathRewrite: { "^/api": "/mockjsdata/5/api" } } }, quiet: true, // necessary for FriendlyErrorsPlugin. 啓用 quiet 後,除了初始啓動信息以外的任何內容都不會被打印到控制檯。這也意味着來自 webpack 的錯誤或警告在控制檯不可見。 watchOptions: { // 監視文件相關的控制選項 poll: true, // webpack 使用文件系統(file system)獲取文件改動的通知。在某些狀況下,不會正常工做。例如,當使用 Network File System (NFS) 時。Vagrant 也有不少問題。在這些狀況下,請使用輪詢. poll: true。固然 poll也能夠設置成毫秒數,好比: poll: 1000 ignored: /node_modules/, // 忽略監控的文件夾,正則 aggregateTimeout: 300 // 默認值,當第一個文件更改,會在從新構建前增長延遲 } }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, // 加快編譯速度,不包含node_modules文件夾內容 use: [{ loader: 'babel-loader' },{ loader: 'eslint-loader', options: { fix: true } }] }, { test: /\.(sa|sc|c)ss$/, use: [ 'style-loader', { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: (loader) => [autoprefixer({browsers: ['> 0.15% in CN']})] } }, { loader: 'sass-loader', options: { sourceMap: true } } ] }, { test: /\.(woff|woff2|eot|ttf|otf)$/, use: [ { loader: 'url-loader', options: { limit: 10000 } } ] }, { test: /\.(png|svg|jpg|gif|jpeg|ico)$/, use: [ { loader: 'url-loader', options: { limit: 10000 } }, { 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 } } } ] } ] }, plugins: [ new MiniCssExtractPlugin({filename: '[name].css', chunkFilename: '[id].css'}), new CleanWebpackPlugin(['dist']), new webpack.NamedModulesPlugin(), // 更容易查看(patch)的依賴 new webpack.HotModuleReplacementPlugin(), // 替換插件 new HtmlWebpackPlugin({ title: 'AICODER 全棧線下實習', // 默認值:Webpack App filename: 'index.html', // 默認值: 'index.html' minify: { collapseWhitespace: true, removeComments: true, removeAttributeQuotes: true, // 移除屬性的引號 }, template: path.resolve(__dirname, 'src/index.html') }) ], optimization: {} };
用於生產環境的配置
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); const CleanWebpackPlugin = require('clean-webpack-plugin'); const autoprefixer = require('autoprefixer'); module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'main.[hash].js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, // 加快編譯速度,不包含node_modules文件夾內容 use: [{ loader: 'babel-loader' },{ loader: 'eslint-loader', options: { fix: true } }] }, { test: /\.(sa|sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader' }, { loader: 'postcss-loader', options: { ident: 'postcss', plugins: (loader) => [autoprefixer({browsers: ['> 0.15% in CN']})] } }, { loader: 'sass-loader' } ] }, { test: /\.(woff|woff2|eot|ttf|otf)$/, use: [ { loader: 'url-loader', options: { limit: 10000 } } ] }, { test: /\.(png|svg|jpg|gif|jpeg|ico)$/, 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 } } } ] } ] }, plugins: [ new MiniCssExtractPlugin({filename: '[name][hash].css', chunkFilename: '[id][hash].css'}), new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'AICODER 全棧線下實習', // 默認值:Webpack App filename: 'index.html', // 默認值: 'index.html' template: path.resolve(__dirname, 'src/index.html'), minify: { collapseWhitespace: true, removeComments: true, removeAttributeQuotes: true, // 移除屬性的引號 } }) ], optimization: { minimizer: [ new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps }), new OptimizeCSSAssetsPlugin({}) ] } };
配置模塊如何解析。好比: import _ from 'lodash'
,實際上是加載解析了lodash.js文件。此配置就是設置加載和解析的方式。
resolve.alias
建立 import 或 require 的別名,來確保模塊引入變得更簡單。例如,一些位於 src/ 文件夾下的經常使用模塊:
// webpack.config.js module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'main.[hash].js', path: path.resolve(__dirname, './dist') }, + resolve: { + alias: { + vue$: path.resolve(__dirname, 'src/lib/vue/dist/vue.esm.js'), + '@': path.resolve(__dirname, 'src/') + } + } ... } // index.js // 在咱們的index.js文件中,就能夠直接import import vue from 'vue'; // 等價於 import vue from 'src/lib/vue/dist/vue.esm.js';
resolve.extensions
的應用自動解析肯定的擴展。
// webpack.config.js module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'main.[hash].js', path: path.resolve(__dirname, './dist') }, resolve: { alias: { vue$: path.resolve(__dirname, 'src/lib/vue/dist/vue.esm.js'), '@': path.resolve(__dirname, 'src/') }, + extensions: [".js", ".vue",".json"] // 默認值: [".js",".json"] } ... }
給定對象的鍵後的末尾添加 $,以表示精準匹配
externals 配置選項提供了「從輸出的 bundle 中排除依賴」的方法。 文檔
例如,從 CDN 引入 jQuery,而不是把它打包:
index.html
<script src="https://code.jquery.com/jquery-3.1.0.js" integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk=" crossorigin="anonymous"> </script>
webpack.config.js
// webpack.config.js
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'main.[hash].js',
path: path.resolve(__dirname, './dist')
},
alias: {
extensions: [".js", ".vue",".json"] // 默認值: [".js",".json"]
vue$: path.resolve(__dirname, 'src/lib/vue/dist/vue.esm.js'),
'@': path.resolve(__dirname, 'src/')
},
+ externals: {
+ jquery: 'jQuery'
+ },
...
}
這樣就剝離了那些不須要改動的依賴模塊,換句話,下面展現的代碼還能夠正常運行:
import $ from 'jquery'; $('.my-element').animate(...);
具備外部依賴(external dependency)的 bundle 能夠在各類模塊上下文(module context)中使用,例如 CommonJS, AMD, 全局變量和 ES2015 模塊。外部 library 多是如下任何一種形式:
不一樣的配置方式:
externals : {
react: 'react'
}
// 或者
externals : {
lodash : {
commonjs: "lodash",
amd: "lodash",
root: "_" // 指向全局變量
}
}
// 或者
externals : {
subtract : {
root: ["math", "subtract"] // 至關於: window.math.substract
}
}
webpack 可以爲多種環境或 target 構建編譯。想要理解什麼是 target 的詳細信息,請閱讀 target 概念頁面。
target
: 告知 webpack 爲目標(target)指定一個環境。
能夠支持如下字符串值:
選項 | 描述 |
---|---|
async-node | 編譯爲類 Node.js 環境可用(使用 fs 和 vm 異步加載分塊) |
electron-main | 編譯爲 Electron 主進程。 |
electron-renderer | 編譯爲 Electron 渲染進程,使用 JsonpTemplatePlugin, FunctionModulePlugin 來爲瀏覽器環境提供目標,使用 NodeTargetPlugin 和 ExternalsPlugin 爲 CommonJS 和 Electron 內置模塊提供目標。 |
node | 編譯爲類 Node.js 環境可用(使用 Node.js require 加載 chunk) |
node-webkit | 編譯爲 Webkit 可用,而且使用 jsonp 去加載分塊。支持 Node.js 內置模塊和 nw.gui 導入(實驗性質) |
web | 編譯爲類瀏覽器環境裏可用(默認) |
webworker | 編譯成一個 WebWorker |
例如,當 target 設置爲 "electron",webpack 引入多個 electron 特定的變量.
webpack.config.js
// webpack.config.js
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'main.[hash].js',
path: path.resolve(__dirname, './dist')
},
alias: {
extensions: [".js", ".vue",".json"] // 默認值: [".js",".json"]
vue$: path.resolve(__dirname, 'src/lib/vue/dist/vue.esm.js'),
'@': path.resolve(__dirname, 'src/')
},
externals: {
jquery: 'jQuery'
},
+ target: 'node'
...
}
webpack
可使用 loader 來預處理文件。這容許你打包除 JavaScript 以外的任何靜態資源。你可使用 Node.js 來很簡單地編寫本身的 loader。
raw-loader
加載文件原始內容(utf-8)val-loader
將代碼做爲模塊執行,並將 exports 轉爲 JS 代碼url-loader
像 file loader 同樣工做,但若是文件小於限制,能夠返回 data URLfile-loader
將文件發送到輸出文件夾,並返回(相對)URLscript-loader
在全局上下文中執行一次 JavaScript 文件(如在 script 標籤),不須要解析babel-loader
加載 ES2015+ 代碼,而後使用 Babel 轉譯爲 ES5buble-loader
使用 Bublé 加載 ES2015+ 代碼,而且將代碼轉譯爲 ES5traceur-loader
加載 ES2015+ 代碼,而後使用 Traceur 轉譯爲 ES5ts-loader
或 awesome-typescript-loader
像 JavaScript 同樣加載 TypeScript 2.0+coffee-loader
像 JavaScript 同樣加載 CoffeeScripthtml-loader
導出 HTML 爲字符串,須要引用靜態資源pug-loader
加載 Pug 模板並返回一個函數jade-loader
加載 Jade 模板並返回一個函數markdown-loader
將 Markdown 轉譯爲 HTMLreact-markdown-loader
使用 markdown-parse parser(解析器) 將 Markdown 編譯爲 React 組件posthtml-loader
使用 PostHTML 加載並轉換 HTML 文件handlebars-loader
將 Handlebars 轉移爲 HTMLmarkup-inline-loader
將內聯的 SVG/MathML 文件轉換爲 HTML。在應用於圖標字體,或將 CSS 動畫應用於 SVG 時很是有用。style-loader
將模塊的導出做爲樣式添加到 DOM 中css-loader
解析 CSS 文件後,使用 import 加載,而且返回 CSS 代碼less-loader
加載和轉譯 LESS 文件sass-loader
加載和轉譯 SASS/SCSS 文件postcss-loader
使用 PostCSS 加載和轉譯 CSS/SSS 文件stylus-loader
加載和轉譯 Stylus 文件mocha-loader
使用 mocha 測試(瀏覽器/NodeJS)eslint-loader
PreLoader,使用 ESLint 清理代碼jshint-loader
PreLoader,使用 JSHint 清理代碼jscs-loader
PreLoader,使用 JSCS 檢查代碼樣式coverjs-loader
PreLoader,使用 CoverJS 肯定測試覆蓋率vue-loader
加載和轉譯 Vue 組件polymer-loader
使用選擇預處理器(preprocessor)處理,而且 require()
相似一等模塊(first-class)的 Web 組件angular2-template-loader
加載和轉譯 Angular 組件webpack-bundle-analyzer
插件能夠幫助咱們分析打包後的圖形化的報表。
僅僅在開發環境使用。
安裝
npm install --save-dev webpack-bundle-analyzer
+ const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; module.exports = { plugins: [ + new BundleAnalyzerPlugin() ] }
自動生成一個網頁報表,以下所示:
webpack仍是有不少其餘須要學習的內容。 請參考官網,或者研究一下vue-cli
的生成的webpack的相關配置,也很值得學習。
另外其餘腳手架生成的相關配置均可以研究一下好比:create-react-app
、yo
等