Webpack 4.X 從零配置SPA單頁應用

在三大框架潮流的推動下,大大小小的SPA單頁面應用層出不窮,工程化 / 模塊化 / 自動化 漸漸成爲開發的核心思想,但是他們都有一個特點:

源代碼無法在瀏覽器裏直接運行,必需通過編譯纔行

因此也帶來了很多構建工具的興起;諸如具有代表性的 GulpGruntwebpack 等等

今天,我們具體介紹 webpack 4.Xwebpack也從V1過渡到V4,不久之後發佈V5版本( lz學不動了!!!)


一、初始化安裝

建議node版本在 5.0以上

npm init // 可選屬性創建 | npm init -y // 自動創建
npm i webpack -D
npm i webpack-cli -D

安裝完,執行 webpack -v | webpack-cli -v,驗證是否安裝成功
在這裏插入圖片描述

二、搭建項目

這裏我們基礎配置index.html用於測試我們的打包後的效果,webpack.config.js用來配置編譯需求,src項目源碼,package.json項目基本配置

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>webpack 4.X</title>
</head>
<body>
<div id="root"></div>
<script src="./dist/build.js"></script>
</body>
</html>

webpack.config.js

webpack採用 CommonJS 的規範,moudle.exports 導出

const path = require('path') // node提供的path工具,用來做路徑的拼接、轉換等
const fs = require('fs') // node提供的fs文件系統,用來操作文件、文件夾等

module.exports = {
	mode: 'development ',
	entry: '',
	output: {},
	module: {},
	plugins: [],
	devServer: {}
}

src

index.js 入口文件
home.js 測試多入口文件
assets 靜態資源
:::-- img 圖片
:::-- font 字體文件
:::-- media 音視頻文件
:::-- css 樣式文件

package.json

這裏我們使用 npm run test 命令啓動 webpack 編譯

...
 "scripts": {
   "test": "webpack -p --progress --color --config webpack.config.js"
 },

webpack執行命令之後可以添加一些參數,下面是參數列表:

參數名 作用
webpack --config XXX.js 使用另一份配置文件來打包
webpack --watch 監聽變動並自動打包
webpack -p 壓縮混淆腳本,這個非常非常重要!
webpack -d 生成map映射文件,告知哪些模塊被最終打包到哪裏
webpack --progress 顯示進度條
webpack --color 添加顏色

三、基礎配置

webpack編譯 -> 輸出 -> 運行 等等都是由很多配置內容完成

webpack的核心配置:
1. mode:模式----4.X新增,配置當前環境
2. entry:入口----要打包的文件
3. output:出口----配置編譯完成目錄
4. module:模塊----瀏覽器不識別的文件
5. plugins:插件----hook函數輔助開發,提高開發效率
6. devServer:服務器----webpack提供的本地服務器

mode

  • 作用:代表當前的環境:development代表開發模式,production(默認)代表生產模式

  • 區別:mode

entry

  • 作用:將要打包的入口文件

  • 可選類型:String | Array | Object

// String
entry: './src/index.js',
// Array
entry: ['./src/index.js', './src/home.js'], 
// Object
entry: {
	'path/js': './src/index.js',
	home: './src/home.js'
}

output

  • 作用:向硬盤寫入編譯文件配置
  • 屬性:↓↓↓↓↓↓↓
屬性名 作用
filename 向硬盤寫入編譯文件的名稱
path 向硬盤寫入編譯文件的絕對路徑
publicPath 指定資源文件引用的目錄

①:當entryString時,編譯入口文件並輸出

const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/index.js', 
  output: {
    filename: 'build.js',
    path: path.resolve(__dirname, 'dist'),
  }
}

在這裏插入圖片描述
②:當entryArray時,編譯入口文件合併輸出

const path = require('path')

module.exports = {
  mode: 'development',
  entry: ['./src/index.js', './src/home.js'], 
  output: {
    filename: 'build.js',
    path: path.resolve(__dirname, 'dist'),
  }
}

在這裏插入圖片描述
③:當entryObject時,編譯多個入口文件並輸出

  • entry入口指定多個keyvalue時,outputfilename要注意,不可寫死
  • entry入口的 key可以指定爲路徑(多頁面),編譯輸出也是路徑
  • entry入口的 value可以指定數組形式,編譯合併輸出
const path = require('path')

module.exports = {
  mode: 'development',
  entry: {
  	vendor: ['./src/index.js', './src/home.js'], // 合併打包爲輸出爲vender.js
  	'path/index': './src/index.js', // 路徑模式打包
  	home: './src/home.js' // 打包輸出爲home.js
  }, 
  output: {
    filename: '[name].[hash:7].[ext]', // path原名/路徑指定hash輸出
    path: path.resolve(__dirname, 'dist'),
  }
}

在這裏插入圖片描述

module

  • 介紹:webpack中任何一個東西都稱爲模塊(css/img/video/woff…),而且只識別 js
  • 作用:藉助 loader 編譯js爲供瀏覽器識別
①:處理 Css | Less | Sass | Stylus

install

npm i style-loader -D — 把處理完的 css 插入到 style 標籤裏
npm i css-loader -D — 處理 css
npm i postcss-loader -D — 處理不用的瀏覽器廠商前綴
npm i autoprefixer -D — 搭配 postcss-loader 個性化配置

瀏覽器 內核 前綴
Chrome、Safari webkit -webkit-
Firefox gecko -moz-
Opera presto -o-
IE trident -ms-

base

// index.css 
section {
  display: flex;
  align-items: center;
  transition:all 1s;
}

// index.js
import './asset/css/index.css'

document.getElementById('root').innerHTML = `<section>Webpack</section>`

webpack.config.js

const path = require('path')

module.exports = {
  mode: 'development', 
  entry: './src/index.js',
  output: {
    filename: 'build.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
	 {
        test: /\.(css)$/,
        use: [
          'style-loader',
          'css-loader',
          'postcss-loader'
        ]
      },
    ]
}
這裏要注意loader的使用順序:從後向前
1.首先把 .css文件利用 postcss-loader 過濾
2.接着利用 css-loader 處理 css爲瀏覽器所能識別的文件
3.最後利用 style-loader 插入到瀏覽器 style 標籤中
4.如果是 lessstylussass,借住各自的 loader 追加到 postcss-loader 即可

這裏編譯會報錯:No PostCSS Config found in: W:\webpack\src\asset\css缺少PostCSS config配置文件,這裏我們搭配 autoprefixer 進行配置

根目錄新建 postcss.config.js

module.exports = {
  "plugins": {
    "autoprefixer": {}
  }
}

package.json 追加 browserslist

...
  "browserslist": [
    "defaults",
    "not ie < 11",
    "last 2 versions",
    "> 1%",
    "iOS 7",
    "last 3 iOS versions"
  ]
...

:::編譯完成
在這裏插入圖片描述
:::打開 index.html,我們可以看到一切OK!
在這裏插入圖片描述


②:處理 JS | JSX

上面的代碼我們在 Chrome 無壓力,我們在毒瘤 IE11中 運行試試效果
在這裏插入圖片描述
果不其然,報錯了,因爲我們在項目中使用ES6 提供的 模板字符串,一些瀏覽器在ES6發佈後沒有做出相應更新,出現不識別的情況

隨之出現babel-loader可以用來處理ES6語法,將其編譯爲瀏覽器可以執行的js語法

接下來,我們安裝使用:

  • 這裏要注意版本的一致性
  • babel-preset-es2015落伍了,官方推薦使用babel-preset-env

install

這裏的 babel-loader 與 babel/core 版本要對應
npm i babel-loader @babel/core @babel/preset-env -D

webpack.config.js

...
   rules: [
      {
        test: /\.(js|jsx)$/,
        use: {
          loader: 'babel-loader',
          options: {
            include: path.join(__dirname, 'src'), // 具體到 src 更快的搜索速度
            exclude: '/node_modules/', // 排除node_modules,第三方代碼已經處理,不需要二次處理
            presets: '@babel/preset-env' // 將ES6 解析 爲ES5
        }
      },
   ]
...

:::編譯完成
在這裏插入圖片描述
:::然後我們在毒瘤 IE中 運行,IE11IE10IE9 都沒有問題
在這裏插入圖片描述
因爲在IE8中,Object.defineProperty沒有實現,並且不讓別人訪問或修改!!!
在這裏插入圖片描述


③:處理 jpe?g | png | gif | svg …

install

npm i url-loader file-loader -D

base

// index.css
section {
  display: flex;
  align-items: center;
  transition: all 1s;
  width: 100vw;height: 100vh;
  background-image: url(../img/background.jpg) // 引入背景圖
}


// index.js
import './asset/css/index.css'
import icon from './asset/img/icon.png'

// 創建img對象
let img = new Image()
img.width = 200
img.height = 200
img.src = icon

// 創建section對象
let section = document.getElementById('section')
section.appendChild(img)
section.innerHTML += '<img src="./asset/img/bj.jpg">'

// 插入
document.getElementById('root').appendChild(section)

:::這裏配置如果文件大於 10K file-loader原路徑輸出,否則利用url-loader打包爲base64從而減少http請求,但是會變大!

`webpack.config.js

...
    rules: [
      {
        test: /\.(jpe?g|png|gif|svg)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name].[hash:8].[ext]', // 以原圖片名輸出
            outputPath: 'images/', // 輸出路徑
        	publicPath:'./dist/images', // 公共路徑 預防404
            limit: 10240 // 超過10K打包爲圖片,反之打包爲base64
          }
        }
      },
    ]
...

我們看到效果完全一致,icon打包爲base64background.jpg 原路徑輸出
在這裏插入圖片描述:::但是,有一個問題,我們看到頁面無法顯示圖片,這張圖片,我們是直接頁面 img src引入的
在這裏插入圖片描述
這裏因爲html中直接使用img標籤src加載圖片的話,因爲沒有被依賴,圖片將不會被打包

官方文檔,提供loader解決類似問題 html-withimg-loader

npm i html-withimg-loader -D

wepback.config.js 追加插件

{
    test: /\.(htm|html)$/i,
    loader: 'html-withimg-loader'
}

④:處理 mp4 | webm | ogg | mp3 …

webpack.config.js

...
	rules: [
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name].[hash:8].[ext]', // 以原文件名輸出
            outputPath: 'media/', // 輸出目錄
           	publicPath:'./dist/media', // 公共路徑 預防404
            limit: 102400
          }
        }
      },
	]
...

在這裏插入圖片描述


⑤:處理 woff2 | eot | ttf | otf …

webpack.config.js

...
	rules: [
      {
        test: /\.(woff2|eot|ttf|otf)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name].[hash:8].[ext]', // 以原文件名輸出
            outputPath: 'font/',
            publicPath:'./dist/font',
            limit: 10240
          }
        }
      }
	]
...

在這裏插入圖片描述

plugins

  • 介紹:用於擴展webpack的功能,Hook Funtion
  • 作用:提高開發效率,自動化 / 工程化
①:HtmlWebpackPlugin
  • 自動或依據模板生成一個html,動態hash引入 JS CSS
  • 可配置單頁面、多頁面入口

安裝:

npm i html-webpack-plugin -D

使用:

webpack.config.js

const HtmlWebpackPlugin = require('html-webpack-plugin') // 引入

...
 plugins: [
    new HtmlWebpackPlugin({
      title: 'Webpack 4.X', // 文件標題
      filename: 'index.html', // 文件名
      template: path.resolve(__dirname, 'index.html'), // 依賴模板
      inject: true, // js放置位置: true -- body 底部 | head -- head標籤 | false -- 不加載js
      hash: true, // 添加hash
      minify: {
        collapseWhitespace: true, // 移除空格
        removeAttributeQuotes: true, // 移除引號
        removeComments: true // 移除註釋
      }
    })
 ]
...

index.html 模板

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <!--  EJS 語法引入title,配置 minify :removeComments 會移除註釋  -->
  <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
	<div id="root"></div>
</body>
</html>

:::構建成功,測試效果 :::
在這裏插入圖片描述

②:CleanWebpackPlugin
  • 作用:清空上一次編譯結果目錄

安裝:

npm i clean-webpack-plugin -D

使用:

webpack.config.js

const { CleanWebpackPlugin } = require('clean-webpack-plugin') // webpack 4.0 解構賦值,源碼 export { CleanWebpackPlugin }

...
 plugins: [
    new CleanWebpackPlugin(['dist']) // 可配置絕對路徑
    new CleanWebpackPlugin({
   		dry:true, // true -- 僅僅報告要刪除的文件並不刪除  fale -- 全部刪除
   	    cleanOnceBeforeBuildPatterns: [path.resolve(__dirname, 'test')] // 指定絕對路徑
    })
 ]
...
③:MiniCssExtractPlugin
  • 作用:抽取合併css,減小JS文件體積,並自動添加hash

安裝:

npm i mini-css-extract-plugin -D

使用:

webpack.config.js

const MiniCssExtractPlugin = require('mini-css-extract-plugin')

...
 module: {
   rules: [
     {
        test: /\.(css)$/,
        use: [
          MiniCssExtractPlugin.loader, // 當前是production環境,如果是development環境 style-loader
          'css-loader',
          'postcss-loader'
        ]
      },
   ]
 }
 plugins: [
    new MiniCssExtractPlugin({
      filename: 'css/common.css', // 指定路徑,默認hash
    })
 ]
...

:::測試效果 :::
在這裏插入圖片描述

④:OptimizeCSSPlugin
  • 作用:用於合併,壓縮css代碼

安裝:

npm i optimize-css-assets-webpack-plugin -D

使用:

webpack.config.js

const OptimizeCSSPlugin= require('optimize-css-assets-webpack-plugin');

...
 plugins: [
    new OptimizeCSSPlugin()
 ]
...

:::測試效果 :::
在這裏插入圖片描述

⑤:PurifyCssWebpack
  • 作用:用於去除無用css樣式

安裝:

npm i purifycss-webpack purify-css glob -D

使用:

webpack.config.js

const PurifyCssWebpack = require('purifycss-webpack') // 依賴 purify-css
const glob = require("glob") // 搜索資源

...
 plugins: [
    new PurifyCssWebpack({
      //*.html 表示 src 文件夾下的所有 html 文件,還可以清除其它文件 *.js、*.php···
      paths: glob.sync(path.join(__dirname, 'src/*.js'))
    })
 ]
...

:::測試效果 :::

// index.css
section {
  display: flex;
  align-items: center;
  transition: all 1s;
  width: 100vw;height: 100vh;
  background-image: url(../img/background.jpg);
  background-color: #000;
}

.aaa {
  font-size: 12px;
}

.bbb {
  font-size: 12px;
}

在這裏插入圖片描述

⑥:UglifyJsPlugin
  • 作用:壓縮js代碼,去除 debugger console等等

安裝:

npm i uglifyjs-webpack-plugin -D

使用:

webpack.config.js

const UglifyJsPlugin = require('uglifyjs-webpack-plugin');

...
 plugins: [
    new UglifyJsPlugin({
      cache: true, // 開啓緩存
      parallel: true, // 多線程加速構建
      sourceMap: true, // 使用sourceMap捕獲錯誤
      uglifyOptions: {
        compress: {
          drop_console:true, // 放棄對 console 函數的調用
          drop_debugger:true, // 刪除 debugger語句
        }
      },
    })
 ]
...

:::測試效果 :::
在這裏插入圖片描述

⑦:GenerateAssetPlugin
  • 作用:抽離生成額外配置文件

安裝:

npm i generate-asset-webpack-plugin -D

使用:

webpack.config.js

const GenerateAssetPlugin = require('generate-asset-webpack-plugin')
// 配置方法
const createJson = function () {
    let serveConfigJson = {
        baseURI: '172.16.0.94:80'
    }
    return JSON.stringify(serveConfigJson,null,4)
}

...
 plugins: [
    new GenerateAssetPlugin({
        filename: 'serve.config.json', // 輸出到dist根目錄下的serve.config.json
        fn: (compilation, cb) => {
            cb(null, createJson(compilation))
        },
        extraFiles: []
    })
 ]
...

:::測試效果 :::
在這裏插入圖片描述

⑧:SplitChunksPlugin
  • 作用:打包分割代碼

配置:

optimization 屬性 splitChunks

默認屬性:

屬性 作用
chunks all, async, initial 三選一, 插件作用的chunks範圍
minSize 最小尺寸
misChunks 最小chunks
maxAsyncRequests 最大異步請求chunks
maxInitialRequests 最大初始化chunks
name split 的 chunks name
cacheGroups 緩存組,細分打包

我們這裏就不寫默認屬性配置了,直接把node_modules的依賴包打到verder.js,引用2次以上的公共模塊打到common.js

webpack.config.js

...
  optimization: {
    splitChunks: {
      chunks:'initial', // 對入口文件處理
      cacheGroups: {
        vendor:{
          test: /node_modules\//,
          name:'js/vendor',
          priority: 10,
          enforce: true
        },
        common: {
          minChunks:2,
          name: 'js/common',
          priority: 10,
          enforce: true
        }
      },
    },
...

:::測試效果 :::
在這裏插入圖片描述

⑨:BundleAnalyzerPlugin
  • 作用:打包體積分析,方便定位優化

安裝:

npm i webpack-bundle-analyzer -D

使用:

webpack.config.js

const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer')

...
 plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'server', // 服務器啓動
      analyzerHost: '127.0.0.1', // host
      analyzerPort: '5555', // 端口
      reportFilename: 'report.html', // 輸出文件
      defaultSizes: 'parsed', // 報告顯示模塊大小
      openAnalyzer: true, // 瀏覽器自動打開
      generateStatsFile: false, // 不輸出json文件
      statsFilename: 'stats.json', // 輸出json文件名
      statsOptions: null, // 外配置
      logLevel: 'info' // 插件輸出的詳細程度
    }),
 ]
...

:::測試效果
在這裏插入圖片描述

⑩:CompressionPlugin
  • 作用:開啓gizp壓縮,打包體積優化
  • 注意:需要和服務器配合
  • 版本:新版本可能不兼容webpack 4.X 目前用的@1.1.12

安裝:

npm i [email protected] -D

使用:

webpack.config.js

const CompressionWebpackPlugin = require('compression-webpack-plugin')

...
 plugins: [
    new CompressionPlugin({
      test: /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i, // 匹配資源
      filename: '[path].gz[query]', // 輸出名稱
      algorithm: 'gzip', // 壓縮方式
      threshold: 10240, // 處理大於10240字節纔會壓縮
      minRatio: 0.8 // 壓縮率小於纔會被壓縮
    }),
 ]
...

:::測試效果
在這裏插入圖片描述

⑪:CopyWebpackPlugin
  • 作用:在webpack中拷貝文件或者文件夾

安裝:

npm i copy-webpack-plugin -D

使用:

webpack.config.js

const CopyWebpackPlugin = require('copy-webpack-plugin');

...
 plugins: [
    new CopyWebpackPlugin({
     patterns: [
       {
         from: path.resolve(__dirname, './src/static'), // 起始位置
         to: path.resolve(__dirname, './dist/static') // 結束位置
       }
     ]
   })
 ]
...

:::測試效果 :::
在這裏插入圖片描述

⑫:Happypack | DllPlugin
  • Happypack多進程Loader轉換,加速構建
  • DllPlugin抽離第三方模塊,加速構建

總結:都是加速webpack構建,這裏就不在演示了

鏈接:Happypack DllPlugin


devServer

  • 介紹:模塊熱替換功能會在應用程序運行過程中替換、添加或刪除模塊,無需重新加載整個頁面。
    • 保留在完全重新加載頁面時丟失的應用程序狀態
    • 只更新變更內容,以節省寶貴的開發時間
    • 調整樣式更加快速,幾乎相當於在瀏覽器調試器中更改樣式

安裝:

npm install webpack-dev-server -D

使用:

webpack.config.js

const webpack = require('webpack')

...
  plugins: [
	  new webpack.NamedModulesPlugin(), // 顯示熱加載模塊名稱
      new webpack.HotModuleReplacementPlugin() // 熱模塊替換
  ],
  devServer: {
    host: 'localhost', // host地址
    port: '8080', // 端口
    open: true, //自動拉起瀏覽器
    hot: true, //熱加載
    hotOnly: true, // 熱加載不更新
    publicPath: '', // 基礎路徑
    // proxy: {}, // 跨域
    // bypass: {} // 攔截器
  },
...

package.json

...
  "scripts": {
    "dev": "webpack-dev-server --progress --mode development",
    "test": "webpack -p --progress --color --config webpack.config.js"
  },
...

:::測試效果 :::
在這裏插入圖片描述
我們然後修改index.js,發現熱加載不會生效,並且控制檯有警告信息
在這裏插入圖片描述
查閱文檔,我們缺少熱模塊更細,在需要的地方,更新即可

module.hot.accept(

在這裏插入圖片描述


總結:這些只是配置方面,研究事物不能侷限在表面…

待續…


附文章:
Webpack 4.X 配置cdn加載資源
Webpack 各版本 ( v1 - v4 ) 的區別
Webpack 4.X 自定義 loader 和 plugins
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章