一行一行手敲webpack4配置

qfstudy發表於2019-04-26

程式碼:github

一、webpack4--基本配置

1.初始化配置

mkdir webpack4
cd webpack4
mkdir demo1
cd demo1
npm init -y 或 npm init
複製程式碼

目錄結構

webpack4
├── webpack4/demo1
│   └── webpack4/demo1/package.json
└── webpack4/README.md

複製程式碼
  • 安裝webpack npm install webpack --save-dev

  • 安裝指定版本webapck npm install --save-dev webpack@<version>

  • webpack 4+ 版本,還需要安裝webpack-cli npm install webpack-cli --save-dev

  • npx webpack -v:檢視webpack版本

  • npx webpack-cli -v:檢視webpack-cli版本

推薦本地安裝webpack和webpack-cli 寫這篇部落格的時候webpack最新版本為:4.30.0,也是這篇學習webpack4使用的版本

在demo1目錄下新建src目錄,在src目錄下新建index.js

mkdir src 
cd src
touch index.js
複製程式碼

demo1目錄結構

demo1
├── demo1/package.json
├── demo1/package-lock.json
└── demo1/src
    └── demo1/src/index.js

複製程式碼

在index.js中加寫程式碼,例如:

//index.js

let demo='webpack4'
console.log(demo)
複製程式碼

webpack4可以零配置打包,webpack4會預設對src目錄下的index.js檔案打包。 現在執行npx webapck,可以在demo1目錄下看到dist目錄,dist目錄下有一個main.js檔案,這就是打包後的檔案,開啟查詢可以看到 console.log(demo),說明index.js被打包到main.js中。

2.webpack4的簡單配置

demo1目錄下新建webpack配置檔案webpack.config.js

配置webpack--webpack.config.js
const path = require('path')

module.exports={
  //mode development: 開發環境&emsp;production:生產環境
  mode: 'development', 
  //entry 入口檔案配置  
  entry: {
    index: './src/index.js'
  },
  //打包完成後檔案輸出位置配置
  output: {
    //filename 設定打包後檔案的名字
    //如果不設定filename,則檔案的名字跟入口檔案路徑的屬性名一樣
    filename: 'bundle.js',
    //path 設定打包完成後檔案輸出路徑
    path: path.resolve(__dirname,'dist')
  }
}
複製程式碼

執行npx webpack命令 npx webpack等價於npx webpack --config webpack.config.js

webapck配置檔案命名為webpack.config.js時可以省略--config *.js,直接執行npx webpack即可,否則執行npx webpack --config 配置檔名

看到dist目錄下有bundle.js,說明webpack配置正確。

package.json中配置'script'

"scripts": {
    "build": "webpack"
  }
複製程式碼

新增"build": "webpack",執行npm run build效果等價於執行npx webpack命令。

配置webpack.config.js的modoule物件

loader的用法
file-loader的使用

安裝file-loader npm i file-loader --save-dev

webpack.config.js

const path = require('path')

module.exports={
  //mode development: 開發環境&emsp;production:生產環境
  mode: 'development', 
  //entry 入口檔案配置  
  entry: {
    index: './src/index.js'
  },
  //打包完成後檔案輸出位置配置
  output: {
    //filename 設定打包後檔案的名字
    //如果不設定filename,則檔案的名字跟入口檔案路徑的屬性名一樣
    filename: 'bundle.js',
    //path 設定打包完成後檔案輸出路徑
    path: path.resolve(__dirname,'dist')
  },
  module: {
    rules:[
      {
        test: /\.(png|jpg|gif)$/,
        use: {
          loader: 'file-loader',
          options: {
            name: '[name].[ext]', //對打包後的圖片命名
            outputPath: 'images/' //打包後圖片輸出的位置&emsp;dist\images
          }
        }
      }
    ]
  }
}
複製程式碼

在src目錄下新建images資料夾,存放圖片

修改index.js

//index.js

//import匯入圖片
import image from './images/11.png'

let img=new Image()
img.src=image
document.body.append(img)
複製程式碼

執行npm run build後的目錄結構如下

demo1
├── demo1/dist
│   ├── demo1/dist/bundle.js
│   ├── demo1/dist/images
│   │   └── demo1/dist/images/11.png
│   └── demo1/dist/index.html
├── demo1/package.json
├── demo1/package-lock.json
├── demo1/src
│   ├── demo1/src/images
│   │   └── demo1/src/images/11.png
│   └── demo1/src/index.js
└── demo1/webpack.config.js

複製程式碼

在dist目錄下出現了images目錄和圖片,建立index.html,引入js檔案,在瀏覽器中開啟就可以看到圖片。

url-loader的使用

url-loader安裝 npm i url-loader -D

url-loader的作用跟'file-loader'的作用很類似

webpack.config.js

  module: {
    rules:[
     {
        test: /\.(png|jpg|gif)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name].[ext]', //對打包後的圖片命名
            outputPath: 'images/', //打包後圖片放的位置&emsp;dist\images
            limit: 20480
            //1024 == 1kb  
            //小於20kb時打包成base64編碼的圖片否則單獨打包成圖片
          }
        }
      }
    ]
  }
}
複製程式碼

limit屬性:當圖片大小大於屬性值時打包成圖片輸出到images目錄下,否則打包成base64編碼的圖片注入bundle.js中

因為base64編碼的圖片導致打包檔案變大,所以圖片比較小時打包成base64編碼的圖片,圖片比較大時單獨打包成一張圖片。

cssscss的打包

安裝相應的loader npm i css-loader style-loader -D npm i node-sass sass-loader -D npm i postcss-loader -D npm i autoprefixer -D

postcss-loaderautoprefixer配合使用可以在打包過程中自動新增字首

在demo1根目錄下新建postcss.config.js,配置如下

//postcss.config.js
module.exports={
  plugins: [
    require('autoprefixer')
  ]
}
複製程式碼

在webpack.config.js檔案的`module.rules'陣列中新增配置

module:{
  rules:[
    {
      test: /\.css$/,
       use:[
         'style-loader',
         'css-loader',
         'postcss-loader'  
         //加字首  npm i autoprefixer -D
         //在專案根目錄下配置postcss.config.js檔案
       ]
     },
     {
        test: /\.scss$/,
        use:[
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              //importLoaders
              //用於配置css-loader作用於@import的資源之前有多少個loader先作用於@import的資源
            }
          },
          'postcss-loader',
          'sass-loader'
        ]
      }
  ]
}
複製程式碼

demo1src下新建css資料夾,在css資料夾下新建style.cssindex.scss檔案。

index.scss

body{
  border: 1px solid red;
  width: 300px;
  height: 300px;
  img{
    width: 100px;
    height: 100px;
    border-radius: 10%;
    transform: translate(100px,100px);
  }
}
複製程式碼

style.css

body{
  border-radius: 10%;
}
複製程式碼

index.js

//index.js

import image from './images/11.png'
import './style.css'
import './index.scss'

let img=new Image()
img.src=image
document.body.append(img)
複製程式碼

執行npm run build,在dist目錄下新建index.html,引入js檔案,在瀏覽器中開啟就可以看到效果,說明打包成功。

css模組化

css模組化,避免頁面樣式之間相互影響 在webpack.config.js中的css-loader新增modules: true

//webpack.config.js

module:{
  rules: [
      {
        test: /\.css$/,
        use:[
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          },
          'postcss-loader'  
          //加字首  npm i autoprefixer -D
          //在專案根目錄下配置postcss.config.js檔案
        ]
      },
      {
        test: /\.scss$/,
        use:[
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              //importLoaders
              //用於配置css-loader作用於@import的資源之前有多少個loader先作用於@import的資源
              modules: true //載入css模組化打包,避免樣式檔案之間相互影響
            }
          },
          'postcss-loader',
          'sass-loader'
        ]
      }
  ]
}
複製程式碼

修改index.js .img是類名需要在樣式檔案中提前寫好樣式

//index.js

import image from './images/11.png'
import style from './css/style.css'
// import style from './css/index.scss'

let img=new Image()
img.src=image

//style.img .img是scss檔案中寫好的類名
img.classList.add(style.img)

document.body.append(img)
複製程式碼

index.scss

body{
  border: 1px solid red;
  width: 300px;
  height: 300px;
  img{
    width: 100px;
    height: 100px;
    border-radius: 10%;
    transform: translate(100px,100px);
  }
  .img{
    border: 10px solid royalblue;
  }
}
複製程式碼

style.css

body{
  border-radius: 10%;
}
body .img{
  border: 10px solid yellow;
}
複製程式碼

結果

一行一行手敲webpack4配置

可以看到新增了一個class,類名是一串比較複雜的字串,從而避免這個樣式對別的元素產生影響。


二、進一步配置webpack4,使自己在學習webpack4的時候更方便

這一部分主要是學會使用html-webpack-pluginclean-webpack-plugin外掛,主要是學會配置devServer以及使用webpack的熱模組替換功能。

首先,在webpack4目錄下新建demo2資料夾將demo1目錄下的所有東西複製到demo2

在上一部分我們都是手動在dist目錄下建立index.html引入js檔案檢視打包結果,這樣會很麻煩。我們可以使用html-webpack-plugin來自動生產index.html,並且能夠自動引入打包好的檔案,直接開啟生產的html就可以看到打包結構。

1.html-webpack-plugin的使用

安裝 npm i html-webpack-plugin -D

webpack.config.js中配置plugins配置項

const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')

module.exports={
  //mode development: 開發環境&emsp;production:生產環境
  mode: 'development', 
  //entry 入口檔案配置  
  entry: {
    index: './src/index.js'
  },
  //打包完成後檔案輸出位置配置
  output: {
    //filename 設定打包後檔案的名字
    //如果不設定filename,則檔案的名字跟入口檔案路徑的屬性名一樣
    filename: 'bundle.js',
    //path 設定打包完成後檔案輸出路徑
    path: path.resolve(__dirname,'dist')
  },
  module: { },
  plugins: [
    new htmlWebpackPlugin({
      template: './index.html'
    })
  ]
}
複製程式碼

在demo2目錄下新建index.html作為模板

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>
  <div id="root"></div>
<script type="text/javascript" src="bundle.js"></script></body>
</html>
複製程式碼

執行npm run build,可以看到dist目錄下自動生產index.html,並且還自動引入js檔案

2.clean-webpack-plugin的使用

每次打包生成的dist目錄,如果改一次程式碼,都得要刪除一次dist目錄,這樣很麻煩,可以通過clean-webpack-plugin在每次打包的前自動清空dist目錄。

安裝 npm i clean-webpack-plugin -D

webpack.config.jsplugins中配置如下

const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const cleanWebpackPlugin = require('clean-webpack-plugin')

module.exports={
  //mode development: 開發環境&emsp;production:生產環境
  mode: 'development', 
  //entry 入口檔案配置  
  entry: {
    index: './src/index.js'
  },
  //打包完成後檔案輸出位置配置
  output: {
    //filename 設定打包後檔案的名字
    //如果不設定filename,則檔案的名字跟入口檔案路徑的屬性名一樣
    filename: 'bundle.js',
    //path 設定打包完成後檔案輸出路徑
    path: path.resolve(__dirname,'dist')
  },
  module: { },
  plugins: [
    new htmlWebpackPlugin({
      template: './index.html'
    }),
   new cleanWebpackPlugin()
  ]
}
複製程式碼

執行npm run build,可以自己測試,每次打包前都會把dist目錄下的檔案刪掉。

3.entryoutput多入口配置

module.exports={
  //mode development: 開發環境&emsp;production:生產環境
  mode: 'development', 
  //entry 入口檔案配置  
  entry: {
    index: './src/index.js',
    main: './src/index.js'
  },
  //打包完成後檔案輸出位置配置
  output: {
    //filename 設定打包後檔案的名字
    //如果不設定filename,則檔案的名字跟入口檔案路徑的屬性名一樣
    // 佔位符
    filename: '[name].js',
    //path 設定打包完成後檔案輸出路徑
    path: path.resolve(__dirname,'dist')
  },
}
複製程式碼

當有多入口的時候,需要修改filename的屬性值為'[name].js'

執行npm run build,就會在dist目錄下生產index.jsmain.js

4.配置devtool

devtool決定原始碼與打包後的程式碼之間的對映關係,方便對程式碼進行除錯。

開發環境推薦: cheap-module-eval-source-map 生產環境推薦: cheap-module-source-map

devtool具體內容請查閱:文件:devtool

module.exports={
  devtool: 'cheap-module-eval-source-map',
  //開發環境推薦: cheap-module-eval-source-map
  //生產環境推薦: cheap-module-source-map
}
複製程式碼

5.配置devServer

文件:devServer

安裝webpack-dev-server npm i webpack-dev-server -D

在webpack.config.js中新增以下內容
module.exports={
  devServer: {
    contentBase: './dist',
    // open: true, //自動開啟瀏覽器
    // port: 8080, //預設8080
  }
}
複製程式碼

修改package.jsonscript,新增 "start": "webpack-dev-server"

 "scripts": {
    "start": "webpack-dev-server"
  },
複製程式碼

執行npm run start後開啟瀏覽器就可以看到效果,當我們修改程式碼的時候頁面就會重新重新整理。

有時我們希望頁面只更新我們修改的那一部分就可以了,而並不是重新整理頁面,所以需要啟用webpack的熱模組替換功能。

#### 6.啟用webpack的熱模組替換功能

首先修改index.js
import './css/style.css'

var btn = document.createElement('button')
btn.innerHTML='新增'
document.body.appendChild(btn)

btn.onclick=function(){
  var div=document.createElement('div')
  div.innerHTML='items'
  document.body.appendChild(div)
}
複製程式碼
修改style.css,刪掉index.scss
//style.css
body{
  background: yellow;
}
div:nth-of-type(odd){
  background: chartreuse;
  font-size: 18px;
}
複製程式碼
在webpack.config.js中

引入webpack:const webpack=require('webpack') 新增內容如下:

const webpack=require('webpack')
module.exports={
  plugins: [
    new webpack.HotModuleReplacementPlugin() //啟用HMR
  ],
  devServer: {
    contentBase: './dist',
    // open: true, //自動開啟瀏覽器
    // port: 8080,
    hot: true,&emsp;//啟用webpack的熱模組替換功能
    hotOnly: true&emsp;
    //devServer.hot在沒有頁面重新整理的情況下啟用熱模組替換作為構建失敗時的後備
  }
}
複製程式碼

hot:true啟用HotModuleReplacementPlugin(HMR)

執行npm run start,在瀏覽器開啟以後,修改div的背景顏色,只有改變的地方才發生變化,但是頁面並沒有重新整理。

在demo2的src目錄下新建number.js

number.js
var number=function(){
  var div=document.createElement('div')
  div.setAttribute("id","number")
  div.innerHTML=103
  document.body.appendChild(div)
}

export default number
複製程式碼

修改index.js

import number from './number'
number()
複製程式碼

執行npm run start,在瀏覽器中開啟看結果,然後在number.js中修改內容,但是頁面並沒有顯示修改後的內容

這是因為在引入js檔案的時候,熱模組替換的實現方式有點區別。

js要達到熱模組替換的效果,得要if(module.hot){}這一部分程式碼,否則就算改了程式碼,頁面不重新整理,修改的地方在頁面上頁面變化。

css樣式因為css-loader已經實現if(module.hot){}這一部分,所以不需要單獨實現這一部分。

再次修改index.js

import number from './number'
number()

if(module.hot){
  module.hot.accept('./number.js',function(){
    number()
    document.body.removeChild(document.getElementById('number'))
  })
}
複製程式碼

執行npm run start,在瀏覽器中開啟看結果,然後在number.js中修改內容,發現頁面顯示修改後的內容


三、使用Babel處理js檔案

Babel是一個廣泛使用的轉碼器,可以將ES6程式碼轉為ES5程式碼,從而在現有環境執行。

Babel總共分為三個階段:解析(parse),轉換(transform),生成(generate)。

Babel本身不具有任何轉化功能,它把轉化的功能都分解到一個個plugin裡面。因此當我們不配置任何外掛時,經過Babel輸出的程式碼和輸入是相同的。

Babel外掛的使用

  1. 將外掛的名字增加到配置檔案中:專案根目錄下建立.babelrc配置檔案或是webapck.config.js中配置,一般都是在.babelrc中配置。

  2. 使用 npm install xxx 進行安裝

Babel的配置檔案是.babelrc,存放在專案的根目錄下。使用Babel的第一步,就是配置這個檔案。

該檔案用來設定轉碼規則和外掛,基本格式如下。

{
  "presets": [],
  "plugins": []
}

複製程式碼

Babel簡單介紹

preset

preset(預設)就是一系列外掛的集合 @babel/preset-env包含所有ES6轉譯為ES5的外掛集合

core-js

轉換一些內建類(Promise, Symbols等等)和靜態方法(Array.from等)。

@babel/core

是作為Babel的核心存在,Babel的核心api都在這個模組裡面。

babel-loader

babel-loader在webpack中使用,是webpack和Babel之間的通訊橋樑

@babel/polyfill介紹

@babel/preset-env預設只轉譯js語法,而不轉譯新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全域性物件,以及一些定義在全域性物件上的方法(比如Object.assign)都不會轉譯。這時就必須使用@babel/polyfill(內部整合了core-jsregenerator)。

使用時,在所有程式碼執行之前增加import "@babel/polyfill"

或者是在webpack.config.js入口配置

module.exports = {
  entry: ["@babel/polyfill", "./app/js"],
}
複製程式碼

因此必須把@babel/polyfill作為dependencies而不是devDependencies

@babel/polyfill主要有兩個缺點:

1.使用@babel/polyfill需要做些額外配置,實現打包的時候按需引入,否則會把@babel/polyfill全部注入程式碼中會導致打出來的包非常大。

2.@babel/polyfill會汙染全域性變數。

Babel7的一個重大變化就是npm package 名稱的變化,把所有babel-*重新命名為@babel/*,例如:

  • babel-polyfill重新命名為@babel/polyfill
  • babel-preset-env重新命名為@babel/preset-env

Babel在webpack中的用法

首先實現對ES6語法的轉譯

在webpack4目錄下新建demo3資料夾,將demo2目錄下的所有東西複製到demo3中

安裝babel-loader、 @babel/core、@babel/preset-env

  • npm i babel-loader -D
  • npm i @babel/core -D
  • npm i @babel/preset-env -D

babel-loader@8需要安裝@babel/core7.x版本。

在webpack.config.js配置

module.exports={
  module: {
    rules:[
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use:{
          loader: 'babel-loader',         
          options:{
            presets: [
              ["@babel/preset-env",{
                //targets:表示編譯出的程式碼想要支援的瀏覽器版本
                targets: {
                  chrome: "67"                 
                }
              }]
            ]
          }
        }
      }
    ]
  }
}
複製程式碼

執行npm run buildnpx webpack就可以看到dist目錄下的打包檔案,但是隻是將ES6的語法進行轉譯,並沒有對ES6新API進行轉譯,所以我們需要配置@babel/polyfill解決這個問題。

安裝@babel/polyfill npm i @babel/polyfill --save

index.js中引入@babel/polyfill

index.js

//index.js

import '@babel/polyfill'

let arr=[
  new Promise(()=>{}),
  new Promise(()=>{}),
  2
]

arr.map((item)=>{
  console.log(item)
})
複製程式碼

引入@babel/polyfill前,main.js的大小為29.5KB

一行一行手敲webpack4配置

引入@babel/polyfill後,main.js的大小為1MB

一行一行手敲webpack4配置

注意:以上對比都是在沒有targets這個選項的情況下,因為有些瀏覽器幾乎都支援ES6,在這種情況下,@babel/preset-env將不會對程式碼進行處理。

這是因為把@babel/polyfill對所有API的實現都注入到打包檔案中,但是裡面很多的API我們在程式碼中並沒有用到,所以需要修改配置,按需引入對應的API。

修改webpack.config.js配置

新增"useBuiltIns": "usage"以後,需要安裝core-js@2,並且新增"corejs": 2配置項,這時配置選項比較多,需要在專案根目錄下新建.babelrc檔案,在這個檔案中配置。

.babelrc配置如下:

  • "useBuiltIns"屬性值為"usage"時,會自動引入@babel/polyfill,必須保證已經安裝了@babel/polyfill

  • "useBuiltIns"屬性值為"usage"時,需要新增"corejs": 2配置項,否則報錯,需要安裝core-js

首先刪掉index.js中的import '@babel/polyfill'

安裝core-js npm i --save core-js@2npm i --save core-js@3

{
  "presets": [["@babel/preset-env",{
    "useBuiltIns": "usage", //不需要把polly都打包到程式碼中,根據程式碼按需轉譯
    // core-js@3和core-js@2二選一
    //"corejs": 3,  //npm i --save core-js@3
    "corejs": 2  //npm i --save core-js@2
  }]]
}
複製程式碼

修改webpack.config.js,刪除options物件

module.exports={
  module: {
    rules:[
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  }
}
複製程式碼

執行npm run build,打包後的檔案大小為165KB

一行一行手敲webpack4配置

但是,在開發類庫或是第三方模組時不適合使用@babel/polyfill,所以接下來使用@babel/plugin-transform-runtime來解決這個問題。

@babel/plugin-transform-runtime、@babel/runtime和@babel/runtime-corejs2的用法

@babel/runtime-corejs2:是一個包含Babel modular runtime helpersregenerator-runtime以及core-js的庫。

@babel/runtime:是一個包含Babel modular runtime helpersregenerator-runtime的庫。

在配置項中corejs屬性值為預設為false,如果需要將PromiseAPI進行轉譯,則需要設定屬性值為2時,並且安裝@babel/runtime-corejs2

安裝:

  • npm i @babel/plugin-transform-runtime -D
  • npm i --save @babel/runtime
  • npm i --save @babel/runtime-corejs2

修改.babelrc檔案

{
  "plugins": [
    ["@babel/plugin-transform-runtime",{
      "helpers": true,
      "regenerator": true,
      "useESModules": false,
      "corejs": 2
    }]
  ]
}
複製程式碼

我們把presets配置項去掉了,然後npm run build打包,開啟打包後的main.js檢視,雖然把轉譯了Promise,但是ES6新語法並沒被轉譯,例如:let沒有被轉譯為var

所以還是需要配置presets,因為"@babel/preset-env"包含了對所有ES6語法轉譯為ES5外掛。

再次修改.babelrc檔案

{
&emsp;"presets": ["@babel/preset-env"],
  "plugins": [
    ["@babel/plugin-transform-runtime",{
      "helpers": true,
      "regenerator": true,
      "useESModules": false,
      "corejs": 2
    }]
  ]
}
複製程式碼

新增presets配置項,然後npm run build打包,開啟打包後的main.js檢視,可以看到let和箭頭函式都被轉譯為ES5語法了。 


四、Tree Shaking使用

首先,在webpack4目錄下新建demo4資料夾,將demo3目錄下的所有東西複製到demo4中

Tree Shaking可以用來剔除JavaScript中用不上的死程式碼。它依賴靜態的ES6模組化語法,例如通過importexport匯入匯出。

需要注意的是要讓Tree Shaking正常工作的前提是JavaScript程式碼必須採用ES6模組化語法,因為ES6模組化語法是靜態的,這讓Webpack可以簡單的分析出哪些export的被import過了。

接下來配置WebpackTree Shaking生效

webpack4預設保留ES6模組化語句,並沒有通過Babel將其轉換 修改.babelrc檔案為如下:

//.babelrc

{
   "presets": [["@babel/preset-env",{
      "useBuiltIns": "usage",
      "corejs": 2,
      "modules":false //關閉 Babel 的模組轉換功能,保留原本的 ES6 模組化語法
      //預設是auto,取值還可以是 amd, umd, systemjs, commonjs,auto等
   }]]
}
複製程式碼

修改webapck.config.js,新增

optimization: {
  usedExports: true
}
複製程式碼

module.exports{}

module.exports={
&emsp;mode: 'development',
  optimization: {
  //開發壞境使用tree shaking時加usedExports: true
    usedExports: true&emsp;
  },
}
複製程式碼

還需通過package.json"sideEffects"屬性來告訴webpack哪些模組是可以忽略掉,如果沒有則設定為false,來告知webpack,它可以安全地刪除未用到的export

修改package.json

{
  "name": "your-project",
  "sideEffects": false
}
複製程式碼

在demo4下的src新建math.js

index.js

//tree shaking import export
import {cube} from './math.js'

let component = () => {
  let element = document.createElement('pre')
  element.innerHTML = [
    'Hello webpack!',
    '2 cubed is equal to ' + cube(2)
  ].join('\n\n');
  console.log(cube)
  
  return element;
}
document.body.appendChild(component());
複製程式碼

math.js

export let square= (x) => {
  console.log(x)
  return x * x;
}

export let cube = (x) => {
  console.log(x)
  return x * x * x;
}
複製程式碼

執行npm run build,然後開啟打包後的js檔案:main.js找到下面這段文字

/*!*********************!*\
   !*** ./src/math.js ***!
   \*********************/
 /*! exports provided: square, cube */
 /*! exports used: cube */
 /***/
複製程式碼

從上面這段文字可以看出Tree Shaking生效了,但是在開發環境下,並沒有把沒有用的程式碼刪掉,因為 環境下還需要對程式碼進行除錯。

我們已經找出需要刪除的“未引用程式碼(dead code)”,然而,不僅僅是要找出,還要刪除它們。為此,我們需要將mode配置選項設定為production,將optimization物件刪掉,修改devtool配置選項

webpack.config.js

module.exports = {
  mode: 'production',
  devtool: 'cheap-module-source-map'
}
複製程式碼

執行npm run build,檢視打包結果就可以看到沒有用的程式碼被刪掉了。


五、DevelomentProduction不同環境的配置

在webpack4下新建demo5,將demo4下的所有檔案複製到demo5中

因為在不同的環境下,webpack的配置稍微有點區別,如果我們需要在不同的換將下切換,就得修改webpack配置,這是很麻煩而且還容易改錯,所以我們需要把配置檔案進行拆分。

在專案根目錄下新建build資料夾,然後在build資料夾中新建webpack.dev.jswebpack.prod.jswebpack.base.js三個檔案

webpack.dev.js:是開發環境 webpack.prod.js:是生產環境 webpack.base.js:是開發環境和生產環境都用到的配置

這幾個檔案之間的結合靠'webpack-merge'這個外掛。

安裝 npm i webpack-merge -D

webpack.dev.js

//webpack.dev.js

const webpack=require('webpack')
const merge = require('webpack-merge')
const baseConfig=require('./webpack.base')

const devConfig={
  mode: 'development', 
  devtool: 'cheap-module-eval-source-map',
  plugins: [
    new webpack.HotModuleReplacementPlugin()
  ],
  optimization: {
    usedExports: true
  },
  devServer: {
    contentBase: './dist',
    // open: true, //自動開啟瀏覽器
    // port: 8080,
    hot: true,&emsp;//啟用webpack的熱模組替換功能
    //hotOnly: true&emsp;
    //devServer.hot在沒有頁面重新整理的情況下啟用熱模組替換作為構建失敗時的後備
  }
}

module.exports=merge(baseConfig,devConfig)
複製程式碼

webapck.prod.js

//webapck.prod.js

const merge = require('webpack-merge')
const baseConfig=require('./webpack.base')

const prodConfig={
  mode: 'production', 
  devtool: 'cheap-module-source-map'
}

module.exports=merge(baseConfig,prodConfig)
複製程式碼

但是這兩個檔案還有大量重複的程式碼,新建webpack.base.js

//webpack.base.js

const path = require('path')
const htmlWebpackPlugin = require('html-webpack-plugin')
const cleanWebpackPlugin = require('clean-webpack-plugin')

module.exports={
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname,'dist')
  },
  module: {
    rules:[
      {
        test: /\.(png|jpg|gif)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name].[ext]', 
            outputPath: 'images/', 
            limit: 2048           
          }
        }
      },
      {
        test: /\.css$/,
        use:[
          'style-loader',
          'css-loader',
          'postcss-loader' 
        ]
      },
      {
        test: /\.scss$/,
        use:[
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              importLoaders: 2,
              modules: true 
            }
          },
          'sass-loader',
          'postcss-loader'
        ]
      },
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  },
  plugins: [
    new htmlWebpackPlugin({
      template: './index.html'
    }),
    new cleanWebpackPlugin(),
  ]
}
複製程式碼

修改package.jsonscript:

{
  "scripts": {
    "dev": "webpack-dev-server --config ./build/webpack.dev.js",
    "build": "webpack --config ./build/webpack.prod.js"
  },
}
複製程式碼

開發環境:執行npm run dev,開啟瀏覽器訪問http://localhost:8080/就可以看到結果 生產環境:執行npm run build

相關文章