Webpack是一個如今Javascript應用程序的模塊化打包器,在Webpack中JS/CSS/圖片等資源都被視爲JS模塊,簡化了編程。當Webpack構建時,會遞歸造成一個模塊依賴關係圖,而後將全部的模塊打包爲一個或多個bundle。
本文內容javascript
要系統地學習Webpack,須要先了解Webpack的四個核心概念:css
webpack使用Node.js運行,所以全部的Node.js模塊均可以使用,好比文件系統、路徑等模塊。html
對Node.js基礎不太瞭解的讀者,能夠參考個人Node.js系列java
配置文件webpack.config.js
的通常格式爲:node
const path = require('path'); // 導入Node.js的path模塊 module.exports = { mode: 'development', // 工做模式 entry: './src/index', // 入口點 output: { // 輸出配置 path: path.resolve(__dirname, 'dist'), // 輸出文件的目錄 filename: 'scripts/[name].[hash:8].js', // 輸出JS模塊的配置 chunkFilename:'scripts/[name].[chunkhash:8].js', // 公共JS配置 publicPath:'/' // 資源路徑前綴,通常會使用CDN地址,這樣圖片和CSS就會使用CDN的絕對URL }, module:{ rules: [ { test:/\.(png|gif|jpg)$/, // 圖片文件 use:[ { loader:'file-loader', // 使用file-loader加載 options:{ // file-loader使用的加載選項 name:'images/[name].[hash:8].[ext]' // 圖片文件打包後的輸出路徑配置 } } ] } ] }, plugins:[ // 插件配置 new CleanWebpackPlugin() ] };
Webpack本身只管JS模塊的輸出,也就是output.filename是JS的配置,CSS、圖片這些是經過loader來處理輸出的
入口指明瞭Webpack從哪一個模塊開始進行構建,Webpack會分析入口模塊依賴到的模塊(直接或間接),最終輸出到一個被稱爲bundle的文件中。webpack
使用 entry來配置項目入口。
單一入口git
最終只會生成1個js文件github
module.exports = { entry: './src/index', };
多個入口web
最終會根據入口數量生成對應的js文件npm
module.exports = { entry:{ home:'./src/home/index', // 首頁JS about:'./src/about/index' // 關於頁JS } };
多個入口通常會在多頁面應用中使用,好比傳統的新聞網站。
輸出指明瞭Webpack將bundle輸出到哪一個目錄,以及這些bundle如何命名等,默認的目錄爲./dist
。
module.exports = { output:{ path:path.resolve(__dirname, 'dist'), // 輸出路徑 filename:'scripts/[name].[hash:8].js', // 輸出JS模塊的文件名規範 chunkFilename:'scripts/[name].[chunkhash:8].js', // 公共JS的配置 publicPath:'/', // 資源路徑前綴,通常會使用CDN地址,這樣圖片和CSS就會使用CDN的絕對URL } };
path
path是打包後bundle的輸出目錄,必須使用絕對路徑。全部類型的模塊(js/css/圖片等)都會輸出到該目錄中,固然,咱們能夠經過配置輸出模塊的名稱規則來輸出到path下的子目錄。好比上例中最終輸出的JS目錄以下:
|----dist |---- scripts |---- home.aaaaaaaa.js
filename
入口模塊輸出的命名規則,在Webpack中,只有js是親兒子,能夠直接被Webpack處理,其餘類型的文件(css/images等)須要經過loader來進行轉換。
filename的經常使用的命名以下:
[name].[hash].js
chunkFilename
非入口模塊輸出的命名規則,通常是代碼中引入其餘依賴,同時使用了optimization.splitChunks配置會抽取該類型的chunk
hash
Webpack中常見的hash有hash
,contenthash
,chunkhash
,很容易弄混淆,這裏說明一下。
publicPath
資源的路徑前綴,打包以後的資源默認狀況下都是相對路徑,當更改了部署路徑或者須要使用CDN地址時,該選項比較經常使用。
好比咱們把本地編譯過程當中產生的全部資源都放到一個CDN路徑中,能夠這麼定義:
publicPath: 'https://static.ddhigh.com/blog/'
那麼最終編譯的js,css,image等路徑都是絕對連接。
loader用來在import時預處理文件,通常用來將非JS模塊轉換爲JS能支持的模塊,好比咱們直接import一個css文件會提示錯誤,此時就須要loader作轉換了。
好比咱們使用loader來加載css文件。
module.exports = { module:{ rules:[ { test: /\.(css)$/, use: ['css-loader'] } ] } };
Webpack中有3種使用loader的方式:
module.rules用來配置loader。test用來對加載的文件名(包括目錄)進行正則匹配,只有當匹配時纔會應用對應loader。
多個loader配置時從右向左進行應用
配置式Webpack的loader也有好幾種形式,有些是爲了兼容而添加的,主要使用的方式有如下3種。
module.exports = { module:{ rules:[ { test: /\.less$/, loader:'css-loader!less-loader', // 多個loader中用感嘆號分隔 }, { test:/\.css/, use:['css-loader'],//數組形式 }, { test:/\.(png|gif|jpg)$/, use:[ // loader傳遞參數時建議該方法 { loader: 'file-loader', options:{ // file-loader本身的參數,跟webpack無關 name: 'images/[name].[hash:8].js' } } ] } ] } };
每一個loader的options參數不必定相同,這個須要查看對應loader的官方文檔。
loader通常用來作模塊轉換,而插件能夠執行更多的任務,包括打包優化、壓縮、文件拷貝等等。插件的功能很是強大,能夠進行各類各樣的任務。
下面是打包以前清空dist目錄的插件配置示例。
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { plugins: [ new CleanWebpackPlugin(), ] };
插件也能夠傳入選項,通常在實例化時進行傳入。
new MiniCssPlugin({ filename: 'styles/[name].[contenthash:8].css', chunkFilename: 'styles/[name].[contenthash:8].css' })
Webpack4中提取公共代碼只須要配置optimization.splitChunks便可。
optimization: { splitChunks: { cacheGroups: { vendor: { // 名爲vendor的chunk name: "vendor", test: /[\\/]node_modules[\\/]/, chunks: 'all', priority: 10 }, styles: { // 名爲styles的chunk name: 'styles', test: /\.css$/, chunks: 'all' } } } },
上面的例子中將node_modules中的js打包爲vendor,以css結尾的打包爲styles
加載css文件
{ test:/\.css$/ loader:['css-loader'] }
加載less文件,通常須要配合css-loader
{ test:/\.less$/, loader:['css-loader','less-loader'] }
將文件拷貝到輸出文件夾,並返回相對路徑。通常經常使用在加載圖片
{ test:/\.(png|gif|jpg)/, use:[ { loader:'file-loader', options:{ name:'images/[name].[hash:8].[ext]' } } ] }
轉換ES2015+代碼到ES5
{ test:/\.js$/, exclude: /(node_modules|bower_components)/, // 排除指定的模塊 use:[ { loader:'babel-loader', options:{ presets:['@babel/preset-env'] } } ] }
轉換Typescript到Javascript
{ test:/\.ts/, loader:'ts-loader' }
簡化HTML的建立,該插件會自動將當前打包的資源(如JS、CSS)自動引用到HTML文件
const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { plugins:[ new HtmlWebpackPlugin() ] };
打包以前清理dist目錄
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { plugins:[ new CleanWebpackPlugin() ] };
提取、壓縮CSS,須要同時配置loader和plugin
const MiniCssPlugin = require('mini-css-extract-plugin'); module.exports = { module:{ rules:[ { test: /\.less$/, use: [MiniCssPlugin.loader, 'css-loader', 'less-loader'] }, { test: /\.css$/, use: [MiniCssPlugin.loader, 'css-loader'] }, ] }, plugins:[ new MiniCssPlugin({ filename: 'styles/[name].[contenthash:8].css', chunkFilename: 'styles/[name].[contenthash:8].css' }), ] };
下面使用Webpack來配置一個傳統多頁面網站開發的示例。
├── package.json ├── src │ ├── about 關於頁 │ │ ├── index.html │ │ ├── index.js │ │ └── style.less │ ├── common │ │ └── style.less │ └── home 首頁 │ ├── images │ │ └── logo.png │ ├── index.html │ ├── index.js │ └── style.less ├── webpack.config.js
"clean-webpack-plugin": "^3.0.0", "css-loader": "^3.2.1", "exports-loader": "^0.7.0", "extract-text-webpack-plugin": "^4.0.0-beta.0", "file-loader": "^5.0.2", "html-webpack-plugin": "^3.2.0", "html-withimg-loader": "^0.1.16", "less": "^3.10.3", "less-loader": "^5.0.0", "mini-css-extract-plugin": "^0.8.0", "normalize.css": "^8.0.1", "script-loader": "^0.7.2", "style-loader": "^1.0.1", "url-loader": "^3.0.0", "webpack": "^4.41.2", "webpack-cli": "^3.3.10", "webpack-dev-server": "^3.9.0", "zepto": "^1.2.0"
因爲是傳統多頁網站,每一個頁面都須要單獨打包一份JS,所以每一個頁面須要一個入口。
entry: { // 入口配置,每一個頁面一個入口JS home: './src/home/index', // 首頁 about: './src/about/index' // 關於頁 }
本例咱們不進行CDN部署,所以輸出點配置比較簡單。
output: { // 輸出配置 path: path.resolve(__dirname, 'dist'), // 輸出資源目錄 filename: 'scripts/[name].[hash:8].js', // 入口點JS命名規則 chunkFilename: 'scripts/[name]:[chunkhash:8].js', // 公共模塊命名規則 publicPath: '/' // 資源路徑前綴 }
本地開發時不須要每次都編譯完Webpack再訪問,經過webpack-dev-server,咱們能夠邊開發變查看效果,文件會實時編譯。
devServer: { contentBase: './dist', // 開發服務器配置 hot: true // 熱加載 },
本例中沒有使用ES6進行編程,可是引用了一個非CommonJS的js模塊Zepto
,傳統用法中在HTML頁面引入Zepto就會在window下掛載全局對象Zepto。可是在Webpack開發中不建議使用全局變量,不然模塊化的優點將受到影響。
經過使用exports-loader和script-loader,咱們能夠將Zepto包裝爲CommonJS模塊進入導入。
module: { rules: [ { test: require.resolve('zepto'), loader: 'exports-loader?window.Zepto!script-loader' // 將window.Zepto包裝爲CommonJS模塊 }, { test: /\.less$/, use: [MiniCssPlugin.loader, 'css-loader', 'less-loader'] }, { test: /\.css$/, use: [MiniCssPlugin.loader, 'css-loader'] }, { test: /\.(png|jpg|gif)$/, use: [ { loader: 'file-loader', options: { name: 'images/[name].[hash:8].[ext]' } } ] }, { test: /\.(htm|html)$/i, loader: 'html-withimg-loader' } ] },
主要進行公共模塊的打包配置。
optimization: { splitChunks: { cacheGroups: { vendor: { name: "vendor", test: /[\\/]node_modules[\\/]/, chunks: 'all', priority: 10, // 優先級 }, styles: { name: 'styles', test: /\.css$/, chunks: 'all' } } } },
plugins: [ new CleanWebpackPlugin(), // 清理髮布目錄 new HtmlWebpackPlugin({ chunks: ['home', 'vendor', 'styles'], // 聲明本頁面使用到的模塊,有主頁,公共JS以及公共CSS filename: 'index.html', // 輸出路徑,這裏直接輸出到dist的根目錄,也就是dist/index.html template: './src/home/index.html', // HTML模板文件路徑 minify: { removeComments: true, // 移除註釋 collapseWhitespace: true // 合併空格 } }), new HtmlWebpackPlugin({ chunks: ['about', 'vendor', 'styles'], filename: 'about/index.html', // 輸出到dist/about/index.html template: './src/about/index.html', minify: { removeComments: true, collapseWhitespace: true } }), new MiniCssPlugin({ filename: 'styles/[name].[contenthash:8].css', chunkFilename: 'styles/[name].[contenthash:8].css' }), new webpack.NamedModulesPlugin(), // 熱加載使用 new webpack.HotModuleReplacementPlugin() // 熱加載使用 ]
部分示例代碼以下:
// src/about/index.js const $ = require('zepto'); require('normalize.css'); require('../common/style.less'); require('./style.less'); $('#about').on('click', function () { alert('點擊了about按鈕'); });
和傳統的JS有點不太同樣,多了一些css的require,前面說過,webpack把全部資源當作JS模塊,所以這是推薦的作法。
<!--首頁--> <!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> <ul> <li><a href="/">首頁</a> </li> <li><a href="/about">關於</a></li> </ul> <div class="logo"></div> <button id="home">首頁按鈕</button> </body> </html>
頁面中再也不須要編寫JS。
注意:html中使用<img />標籤導入圖片的編譯,目前尚未好的解決辦法,能夠經過css background的形式進行處理
開發模式下直接啓用webpack-dev-server便可,會自動加載工做目錄下的webpack.config.js
// package.json "scripts": { "build": "webpack", "dev": "webpack-dev-server" }
npm run dev
生產模式下使用webpack編譯,編譯完成後輸出最終文件。
npm run build
├── about │ └── index.html ├── images │ └── logo.b15c113a.png ├── index.html ├── scripts │ ├── about.3fb4aa0f.js │ ├── home.3fb4aa0f.js │ └── vendor:ed5b7d31.js └── styles ├── about.71eb65e9.css ├── home.cd2738e6.css └── vendor.9df34e21.css
項目已經託管到github,有須要的讀者能夠自取。
https://github.com/xialeistudio/webpack-multipage-example