Webpack 相關(guān)

1、 Loader是什么?

1汪拥、我們之前打包的都是js文件,下面試試打包一個(gè)圖片文件篙耗。

首先將一個(gè)圖片文件放進(jìn)src目錄下迫筑,接著添加index.js的模塊引入代碼:

var Header = require('./header.js');
var Sidebar = require('./sidebar.js');
var Content = require('./content.js');
var Logo = require('./logo.png');
 
new Header();
new Sidebar();
new Content();

執(zhí)行npm run bundle后發(fā)現(xiàn)報(bào)錯(cuò),原因是webpack是默認(rèn)知道如何處理js模塊的宗弯,但是不知道圖片這種文件該如何去打包铣焊。所以我們應(yīng)該主動(dòng)告訴他如何去打包圖片文件,這就需要我們自己去配置文件了罕伯。

webpack.config.js:

const path = require('path');
 
module.exports = {
  mode: 'production',  //意思是打包后的文件被壓縮曲伊,我們不配置mode的話默認(rèn)值是被壓縮,但是會警告追他。
  entry: './src/index.js',
  //module的意思是當(dāng)不知道如何去打包的時(shí)候坟募,webpack就會去模塊這個(gè)配置里面去找
  module: {
    //rules是規(guī)則,是一個(gè)數(shù)組
    rules: [{
      //假設(shè)我們打包的模塊是以.png結(jié)尾的文件
      test: /\.png$/,
      //就應(yīng)該這么去打包:用一個(gè)file-loader來打包該文件邑狸。(首先需要安裝file-loader這個(gè)工具)
      use: {
        loader: 'file-loader'
      }
    }]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

安裝file-loader工具: npm install file-loader -D
安裝完成之后執(zhí)行打包命令懈糯,發(fā)現(xiàn)可以正常打包成功。

image.png

可以發(fā)現(xiàn)webpack把該圖片也打包進(jìn)了dist文件夾里面
我們嘗試在index.js文件中console.log(Logo)单雾,然后重新打包赚哗,完成之后在控制發(fā)現(xiàn)打印出了圖片被打包之后的文件名。

image.png

其實(shí)Loader就是一個(gè)打包方案硅堆,它知道對于某一個(gè)特定的文件屿储,webpack應(yīng)該如何進(jìn)行打包,本身webpack是不知道該如何對一些文件進(jìn)行處理的渐逃,但是Loader知道够掠,所以webpack去求助于Loader就可以了。

2茄菊、例子:將打包后的圖片插入到頁面中

將三個(gè)模塊的js文件都刪除疯潭,修改index.js中的代碼:

import Logo from './logo.png';
 
var img = new Image();
img.src = Logo;
 
var root = document.getElementById('root');
root.append(img);

重新打包,完成之后圖片插入到id為root的dom下面了面殖。

本節(jié)主要介紹的就是Loader的作用:webpack不能識別非js結(jié)尾的模塊竖哩,就需要通過Loader讓webpack識別出來。

類似的像我們之前import以vue結(jié)尾的文件脊僚,Webpack也是不能識別的相叁,所以也需要通過loader來幫助它識別。


2、 使用Loader打包靜態(tài)資源(圖片篇)

1钝荡、我們前面對圖片進(jìn)行了打包,可是打包過后的文件名字是一個(gè)很長的字符串舶衬,我們希望打包后的文件名和打包之前一樣埠通。

配置文件webpack.config.js

const path = require('path');
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.png$/,
      use: {
        loader: 'file-loader',
        //使用loader的時(shí)候,可以配置一些參數(shù),這些參數(shù)放在options這個(gè)對象中
        options: {
          name: '[name].[ext]'   //[name]表示打包之前的名字, [ext]表示打包之前的后綴
        }
      }
    }]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

重新打包過后文件目錄:


image.png

[name].[ext]這種配置的語法叫做placeholder,中文叫占位符。

我們可以在test配置中一次添加多個(gè)后綴:


image.png

2姜骡、我們希望打包后的圖片文件被保存在一個(gè)images文件夾里面蝉绷。

配置文件:

const path = require('path');
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'file-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/'   //意思就是將以png或jpg或gif結(jié)尾的打包文件打包過后放在dist文件夾下的images文件夾下面
        }
      }
    }]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

打包完成之后的目錄結(jié)構(gòu):


image.png

file-loader的配置特別多,以后會遇到很多圖片打包的問題租副,如果不知道如何去處理,可以去file-loader的文檔查找。

3渗柿、url-loader

url-loader除了能做file-loader的工作之外,還能做一個(gè)別的事情脖岛。

將配置文件的file-loader改成url-loader:

const path = require('path');
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',   //url-loader可以完成file-loader能完成的任何功能
        options: {
          name: '[name].[ext]',
          outputPath: 'images/'
        }
      }
    }]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

首先嘗試下能否打包朵栖,發(fā)現(xiàn)報(bào)錯(cuò)。原因是我們沒有安裝url-loader柴梆。執(zhí)行安裝命令:npm install url-loader -D

安裝成功之后陨溅,執(zhí)行打包命令,打包成功后的目錄文件如下:


image.png

發(fā)現(xiàn)圖片文件沒有被打包绍在,但是頁面卻將圖片顯示了出來门扇。去控制臺查看發(fā)現(xiàn),圖片的地址不是外部引入的偿渡,而是一個(gè)base64的內(nèi)容:


image.png

原因是url-loader和file-loader不一樣臼寄,會把我們的圖片轉(zhuǎn)換成一個(gè)base64的字符串,然后直接放到bundle.js里面溜宽,而不是單獨(dú)生成一個(gè)圖片文件脯厨。

優(yōu)點(diǎn):圖片打包到j(luò)s里面,實(shí)際上加載好了js坑质,頁面就出來了合武,也就不用額外去請求一個(gè)圖片的地址了。省了一次http請求涡扼。

缺點(diǎn):如果圖片很大稼跳,打包生成的js文件也會很大,那么加載js的時(shí)間就會很長吃沪,所以一開始頁面可能很長時(shí)間什么都顯示不出來汤善。

所以url-loader的最佳使用場景是:如果一個(gè)圖片很小,就幾kb,那么打包成base64的形式是非常好的選擇红淡。沒必要讓幾kb的圖片去發(fā)http請求不狮,很浪費(fèi)時(shí)間。如果圖片很大在旱,那么最好使用file-loader打包到dist目錄下摇零,不要打包到j(luò)s里面。因?yàn)檫@樣可以讓bundle.js快速加載完成桶蝎,頁面可以快速的顯示出來驻仅,不然bundle.js會很大,導(dǎo)致頁面很久才能顯示出來登渣。解決辦法:

添加配置:

const path = require('path');
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048   //意思是如果圖片的大小超過了2048個(gè)字節(jié)的話噪服,那么就會像file-loader一樣,把它打包到dist文件夾下胜茧。小于2048個(gè)字節(jié)即小于2kb的話粘优,會把它打包成一個(gè)base64的字符串。放到bundle.js里面呻顽。
        }
      }
    }]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

3敬飒、 使用Loader打包靜態(tài)資源(樣式篇1)

1、接著之前的圖片芬位,我們需要圖片大小是150x150无拗,這時(shí)我們就需要一個(gè)樣式文件來修飾這張圖片。

index.css:

.logo {
  width: 150px;
  height: 150px;
}

index.js:

import Logo from './logo.png';
import './index.css';
 
var img = new Image();
img.src = Logo;
img.classList.add('logo');
 
var root = document.getElementById('root');
root.append(img);

此時(shí)圖片的樣式是不會改變的昧碉,我們需要告訴webpack去打包這個(gè)css文件英染。打包c(diǎn)ss文件要用到兩個(gè)loader,style-loader和css-loader被饿。執(zhí)行npm install命令安裝這兩個(gè)loader四康。然后配置webpack.config.js。

webpack.config.js:

const path = require('path');
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048   //意思是如果圖片的大小超過了2048個(gè)字節(jié)的話狭握,那么就會像file-loader一樣闪金,把它打包到dist文件夾下。小于2048個(gè)字節(jié)即小于2kb的話论颅,會把它打包成一個(gè)base64的字符串哎垦。放到bundle.js里面。
        }
      }
    }, {
      test: /\.css$/,
      use: ['style-loader', 'css-loader']
    }]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

執(zhí)行打包命令恃疯,成功之后頁面中圖片的大小就是150x150啦漏设。

  • css-loader:
    添加一個(gè)logo.css文件:
.logo {
  width: 150px;
  height: 150px;
}

index.css:

@import './logo.css';

index.js文件不變,此時(shí)執(zhí)行打包命令也能正常地實(shí)現(xiàn)效果今妄。

css-loader作用:css-loader會幫我們分析出幾個(gè)css文件之間的關(guān)系郑口,最終把這幾個(gè)Css文件合并成一段css鸳碧。

  • style-loader
    作用:在得到css-loader生成的css內(nèi)容之后,style-loader會把這些內(nèi)容掛載到頁面的head部分犬性,

2瞻离、有時(shí)候我們用的不是css編寫樣式, 而是使用less或者scss等比較新的技術(shù)來編寫乒裆。

刪除logo.css文件套利,修改index.css文件為index.scss:

body {
  .logo {
    width: 150px;
    height: 150px;
  }
}

index.js中引入index.scss。

此時(shí)缸兔,很顯然是不能打包成功的,此時(shí)還需要借助sass.loader來幫助我們編譯scss文件吹艇。安裝命令:

npm install sass-loader node-sass -D
安裝成功以后修改配置:

const path = require('path');
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.scss$/,
      use: ['style-loader', 'css-loader', 'sass-loader']
    }]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

執(zhí)行打包命令即可成功惰蜜。

注意:在webpack的配置里面,loader是有先后順序的受神,執(zhí)行順序是從下到上抛猖,從右到左。所以當(dāng)我們?nèi)ゴ虬粋€(gè)scss文件的時(shí)候鼻听,首先會去執(zhí)行sass-loader财著,對代碼進(jìn)行一個(gè)翻譯,翻譯成css代碼以后給css-loader執(zhí)行撑碴,都處理好了以后再給style-loader掛載到頁面上撑教。

3、使用css3的時(shí)候醉拓,自動(dòng)幫助我們添加廠商前綴-------postcss-loader伟姐。

首先npm install postcss-loader -D進(jìn)行安裝;

接著安裝一個(gè)插件亿卤,npm install autoprefixer -D愤兵;

在lessons目錄下創(chuàng)建一個(gè)postcss.config.js文件

module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}

配置文件:

const path = require('path');
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        'css-loader',
        'sass-loader',
        'postcss-loader'
      ]
    }]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

index.scss:

body {
  .logo {
    width: 150px;
    height: 150px;
    transform: translate(200px, 200px);
  }
}

最后執(zhí)行npm run bundle打包,可以發(fā)現(xiàn)對于不支持該屬性的瀏覽器會自動(dòng)給我們添加了css3瀏覽器廠商前綴排吴。就是postcss-loader里面的autoprefixer插件幫我們添加的秆乳。


4、 使用Loader打包靜態(tài)資源(樣式篇2)

1钻哩、介紹一下css-loader的配置


image.png

我們?nèi)绻枰獙σ氲腸ss-loader進(jìn)行配置的時(shí)候屹堰,就不要用字符串的形式寫,用一個(gè)對象來包裹街氢。options: {importLoaders: 2}意思是當(dāng)我們在scss文件中也用了@import 引入scss文件的時(shí)候双藕。

image.png

對于index.js里面引入的index.scss文件,他會依次執(zhí)行從下往上的loader過程阳仔。但是打包index.scss的過程中忧陪,index.scss內(nèi)部又額外引入了一個(gè)scss文件扣泊,那么額外引入的scss文件可能就不會走下面的postcss-loader和sass-loader過程,而是直接去走css-loader的過程嘶摊。如果我們也希望額外引入的scss文件也走下面兩個(gè)loader的流程延蟹。那么我們就需要使用importLoaders,并且給它設(shè)置成2叶堆。

2阱飘、Css打包的模塊化

看一個(gè)例子

目錄結(jié)構(gòu):


image.png

createLogo.js:

import Logo from './logo.png';
 
function createLogo() {
  var img = new Image();
  img.src = Logo;
  img.classList.add('logo');
 
  var root = document.getElementById('root');
  root.append(img);
}
 
export default createLogo;

index.js:

import Logo from './logo.png';
import './index.scss';
import createLogo from './createLogo.js';
 
createLogo();
 
var img = new Image();
img.src = Logo;
img.classList.add('logo');
 
var root = document.getElementById('root');
root.append(img);

最后進(jìn)行一次打包,效果圖如下:


image.png

可以發(fā)現(xiàn)這兩個(gè)圖片的樣式一模一樣虱颗。所以發(fā)現(xiàn)雖然我們只在index.js中引入了index.scss文件沥匈,但是createLogo.js里面的函數(shù)創(chuàng)造的圖片樣式也是一樣的。這是因?yàn)槲覀儤邮竭@么引入的話就是全局的忘渔,從而會導(dǎo)致一些問題高帖。從而引入了CSS Module的概念,即CSS 模塊化的概念:表示這個(gè)CSS只在這個(gè)模塊里有效畦粮。

webpack.config.js:

const path = require('path');
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2,
            modules: true    //意思就是開啟css的模塊化打包
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

index.js:

import Logo from './logo.png';
import style from './index.scss';
import createLogo from './createLogo.js';
 
createLogo();
 
var img = new Image();
img.src = Logo;
img.classList.add(style.logo);
 
var root = document.getElementById('root');
root.append(img);

再次打包之后散址,發(fā)現(xiàn)此時(shí)兩個(gè)圖片的樣式就不一樣了。

如果想要另一個(gè)圖片的樣式也一樣宣赔,修改createLogo.js文件:

import Logo from './logo.png';
import style from './index.scss';
 
function createLogo() {
  var img = new Image();
  img.src = Logo;
  img.classList.add(style.logo);
 
  var root = document.getElementById('root');
  root.append(img);
}
 
export default createLogo;

再次打包即可预麸。

使用上述的方法帶來的好處就是能夠保證各個(gè)模塊之間樣式的獨(dú)立,能夠避免很多的問題儒将。

3吏祸、如何使用webpack打包字體文件。

刪除一些文件后的目錄如下:


image.png

下載幾個(gè)iconfont圖標(biāo)下來钩蚊,將里面的.eot犁罩、.ttf、.svg两疚、.woff結(jié)尾的四個(gè)文件復(fù)制到src目錄下新建的font文件夾中床估,然后將iconfont.css的代碼復(fù)制到index.scss中。然后修改iconfont字體的引用路徑诱渤。

目錄結(jié)構(gòu):

image.png

index.scss中的代碼就是iconfont.css的代碼丐巫。

index.js:

import './index.scss';
var root = document.getElementById('root');
 
root.innerHTML = '<div class="iconfont icon-jiantou">abc</div>';

此時(shí)執(zhí)行打包命令,發(fā)現(xiàn)報(bào)錯(cuò)勺美。這是因?yàn)閣ebpack會去打包index.scss文件递胧,但是index.scss文件里又引入了很多類型的字體文件,這個(gè)字體文件webpack不知道該如何去打包赡茸。所以我們應(yīng)該去配置中告訴它該如何去打包缎脾。

配置文件:

const path = require('path');
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {    //打包字體文件配置
      test: /\.(eot|ttf|svg|woff)$/,    
      use: {
        loader: 'file-loader'
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

執(zhí)行打包命令即可。注意別忘了去掉模塊化打包配置占卧,否則沒有效果:

image.png

5遗菠、使用plugins讓打包更快捷

1联喘、我們dist目錄下有一個(gè)index.html文件,假如我們將dist文件夾刪除辙纬,再進(jìn)行打包豁遭,打包之后的dist文件夾里面沒有index.html文件,每次都需要手工添加該文件。這就需要一個(gè)插件幫助我們自動(dòng)生成它。

首先npm install html-webpack-plugin -D安裝該插件片林。

webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');     //引入該插件
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.(eot|ttf|svg|woff)$/,    
      use: {
        loader: 'file-loader'
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [new HtmlWebpackPlugin()],    //配置plugins
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

此時(shí)我們打包生成的dist目錄下就有一個(gè)index.html文件了

HtmlWebpackPlugin 作用: 會在打包結(jié)束后,自動(dòng)生成一個(gè)html文件闪幽,并把打包生成的js自動(dòng)引入到這個(gè)html文件中。

但是目前該文件里面沒有我們需要的id等于root的div標(biāo)簽涡匀。如果我們需要自動(dòng)添加該標(biāo)簽盯腌,可以這么配置。

首先渊跋,在src目錄下腊嗡,新建一個(gè)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>Document</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.(eot|ttf|svg|woff)$/,    
      use: {
        loader: 'file-loader'
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [new HtmlWebpackPlugin({
    template: 'src/index.html'   //意思是當(dāng)打包完成之后會生成一個(gè)html拾酝,此時(shí)生成的html是以src/index.html為模板生成的。
  })],
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

然后執(zhí)行打包命令卡者,此時(shí)dist目錄下的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>Document</title>
</head>
<body>
  <div id="root"></div>
<script type="text/javascript" src="bundle.js"></script></body>
</html>

頁面也有效果了蒿囤。

plugin 作用:可以在webpack運(yùn)行到某個(gè)時(shí)刻的時(shí)候,幫你做一些事情崇决。有點(diǎn)類似vue的生命周期函數(shù)

2材诽、我們修改一下webpack.confitg.js,將output中的filename修改為dist.js恒傻。再次打包脸侥。

打包完成之后發(fā)現(xiàn)雖然index.html中引用的js文件也變成了dist.js,但是dist文件夾中之前打包的bundle.js卻還存在盈厘,有時(shí)候這樣會出現(xiàn)一些問題睁枕。所以我們需要先將bundle.js刪除,再進(jìn)行打包沸手。要實(shí)現(xiàn)這個(gè)功能外遇,我們就需要一個(gè)插件:cleanWebpackPlugin

image.png

首先進(jìn)行安裝:npm install clean-webpack-plugin -D

接著進(jìn)行配置:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');   //引入該插件
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.(eot|ttf|svg|woff)$/,    
      use: {
        loader: 'file-loader'
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()    //表示在打包之前會使用CleanWebpackPlugin這個(gè)插件幫助我們?nèi)h除dist目錄下的所有內(nèi)容
  ],
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
}

dist目錄:


image.png

可以發(fā)現(xiàn)之前的dist.js就不存在了,只有bundle.js了契吉。而且頁面也有效果跳仿。


6、Entry和Output的基礎(chǔ)配置

1捐晶、我們將webpack.config.js文件中的output的filename刪除菲语,再次打包妄辩。可以發(fā)現(xiàn)webpack打包的js文件默認(rèn)名為main.js谨究。

image.png

entry: './src/index.js'這個(gè)寫法和圖片

image.png

的寫法是一樣的恩袱。entry對象中main的意思是'./src/index.js'打包后生成的文件對應(yīng)的名字是main.js。

2胶哲、有個(gè)新需求畔塔,希望把src目錄下的index.js反復(fù)打包兩次,生成兩個(gè)文件鸯屿,第一個(gè)叫main.js澈吨,第二個(gè)叫sub.js。

如果此時(shí)我們output中的filename設(shè)置成bundle.js寄摆,可以發(fā)現(xiàn)會打包錯(cuò)誤谅辣。想要解決這個(gè)問題,我們可以將bundle替換成一個(gè)占位符[name]婶恼,name的意思就是打包entry的key值桑阶,

webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
 
module.exports = {
  mode: 'production',
  entry: {
    main: './src/index.js',
    sub: './src/index.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.(eot|ttf|svg|woff)$/,    
      use: {
        loader: 'file-loader'
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()
  ],
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
}     

執(zhí)行打包命令之后可以看到成功打包了兩個(gè)js文件:

image.png

3、有時(shí)候會把打包完的文件index.html給后端勾邦,作為后端的一個(gè)入口文件蚣录。但是會把js文件上傳到CDN這樣一個(gè)域名下面。此時(shí)html文件的引入地址就需要修改了眷篇。如何實(shí)現(xiàn)打包完之后自動(dòng)在前面添加CDN的域名萎河。

webpack.config.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');

module.exports = {
mode: 'production',
entry: {
main: './src/index.js',
sub: './src/index.js'
},
module: {
rules: [{
test: /.(png|jpg|gif)/, use: { loader: 'url-loader', options: { name: '[name].[ext]', outputPath: 'images/', limit: 2048 } } }, { test: /\.(eot|ttf|svg|woff)/,
use: {
loader: 'file-loader'
}
}, {
test: /.scss$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
'sass-loader',
'postcss-loader'
]
}]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin()
],
output: {
publicPath: 'http://cdn.com.cn', //給index.html文件引入地址之前添加CDN地址
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
}
}
重新打包之后查看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>Document</title>
</head>
<body>
  <div id="root"></div>
<script type="text/javascript" src="http://cdn.com.cn/main.js"></script><script type="text/javascript" src="http://cdn.com.cn/sub.js"></script></body>
</html>

所以,如果我們的項(xiàng)目后臺用index.html蕉饼,而靜態(tài)資源放到cdn上的情況下虐杯,此時(shí)就要用到output的publicPath這個(gè)配置項(xiàng);


7昧港、SourceMap的配置
刪除一些文件后的目錄結(jié)構(gòu):

image.png

index.js:

console.log('hello world');

webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
 
module.exports = {
  mode: 'production',
  devtool: 'none',   //當(dāng)前處于開發(fā)者模式擎椰,SourceMap默認(rèn)是打開的,此配置可以將它關(guān)閉
  entry: {
    main: './src/index.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.(eot|ttf|svg|woff)$/,    
      use: {
        loader: 'file-loader'
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()
  ],
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
}     

此時(shí)我們將sourcemap關(guān)閉了,如果js文件代碼編寫錯(cuò)了创肥,打包之后控制臺報(bào)錯(cuò)达舒,只能看出是打包后的js文件某一行出錯(cuò),而不能知道打包前是哪個(gè)文件出錯(cuò)瓤的。

image.png

解決辦法:

webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
 
module.exports = {
  mode: 'production',
  devtool: 'source-map',   //開啟sourcemap
  entry: {
    main: './src/index.js'
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.(eot|ttf|svg|woff)$/,    
      use: {
        loader: 'file-loader'
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()
  ],
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
}     

此時(shí)打包過后休弃,控制臺報(bào)錯(cuò)會指出具體哪個(gè)js文件的哪一行出錯(cuò)了。而且dist目錄下也多出了一個(gè)main.js.map文件圈膏,里面就是一些映射關(guān)系塔猾。

devtool的各個(gè)配置值的含義:

image.png
image.png
  • source-map: 可以看到它的build是--,表示打包速度比較慢稽坤。
  • inline-source-map: 也會將錯(cuò)誤指示到具體的源文件中丈甸,不過它對比source-map糯俗,沒有map.js文件。其實(shí)用這個(gè)配置的話睦擂,他會把這個(gè)
    map文件通過dataUrl的方式直接寫在main.js里面得湘。
  • cheap前綴:只指出錯(cuò)誤在哪一行即可,不用精確到哪一列顿仇。打包性能會得到一定提升淘正。還有一個(gè)作用:如果寫了它,那么sourmap只針對業(yè)務(wù)代碼臼闻,比如我們的業(yè)務(wù)代碼只在index.js中鸿吆,那么我們的打包只會映射這個(gè)文件和打包生成的main.js之間的關(guān)系。不會管引入的一些第三方模塊loader的錯(cuò)誤如何做映射述呐。如果也想第三方模塊的錯(cuò)誤映射的話惩淳,可以加module
  • eval:是打包速度最快并且性能最好的一種打包方式, 但是如果代碼比較復(fù)雜乓搬,eval提示的錯(cuò)誤可能不會那么全面思犁。

最佳實(shí)踐:如果在開發(fā)環(huán)境(development)中使用sourcemap的話,建議使用cheap-module-eval-source-map這種形式进肯,這種方式提示的錯(cuò)誤比較全激蹲,同時(shí)打包的速度也是比較快的。如果代碼要放到線上了坷澡,此時(shí)就應(yīng)該處于生產(chǎn)環(huán)境(production)托呕,一般上線環(huán)境的代碼沒有必要讓它有一個(gè)sourcemap的映射含蓉,直接放到線上即可频敛。如果我們也想出現(xiàn)錯(cuò)誤以后能夠快速的定位問題,那么此時(shí)可以配置成cheap-module-source-map馅扣,這樣提示效果會更好斟赚。


8. 使用WebpackDevServer 提升開發(fā)效率

1、我們希望修改了src目錄下的源代碼后差油,webpack自動(dòng)地去幫我們打包拗军,而不需要我們用命令行的形式去手動(dòng)打包。

方法一:
修改package.json:

image.png

意思是webpack會幫我們?nèi)ケO(jiān)聽它打包的文件蓄喇,只要打包的文件變化发侵,就會去自動(dòng)打包。我們可以通過修改index.js打印的東西去控制臺查看就可以發(fā)現(xiàn)妆偏。

方法二:

我們第一次執(zhí)行npm run watch的時(shí)候刃鳄,自動(dòng)幫我們實(shí)現(xiàn)打包,同時(shí)自動(dòng)打開瀏覽器钱骂,同時(shí)開可以去模擬服務(wù)器上面的一些特性叔锐。

安裝webpackdevserver挪鹏,命令:npm install webpack-dev-server -D

package.json:


image.png

webpack.config.js:


image.png

執(zhí)行:npm run start 來啟動(dòng)Webpack devserve

可以發(fā)現(xiàn)這個(gè)時(shí)候就自動(dòng)幫我們起了一個(gè)localhost:8080的服務(wù)器了。

image.png

可以在瀏覽器打開它愉烙,同時(shí)修改源文件也會自動(dòng)打包并且自動(dòng)刷新瀏覽器讨盒。

小知識點(diǎn):


image.png

我們?yōu)槭裁匆_一個(gè)服務(wù)器?

寫過vue或react的都知道步责,有時(shí)候前端發(fā)送ajax請求返顺,如果通過文件的形式在瀏覽器直接打開html文件的話,這時(shí)候如果要發(fā)送ajax請求是不可以的蔓肯。因?yàn)橐笏诘膆tml必須在一個(gè)服務(wù)器上通過http協(xié)議的方式打開创南,這就是借助webpack devser開啟一個(gè)服務(wù)器的原因。此時(shí)發(fā)送ajax請求就沒有問題了省核。

我們平時(shí)使用vue或react的腳手架工具它都會幫我們?nèi)ラ_啟一個(gè)服務(wù)器稿辙,實(shí)際上大多數(shù)都是直接使用Webpack devserver幫助我們開啟出來的。

2气忠、之前比較老的腳手架工具里面沒有使用webpackdevser這個(gè)工具邻储,而是自己實(shí)現(xiàn)了一個(gè)類似這個(gè)功能的工具。

package.json添加配置:

  "scripts": {
    "bundle": "webpack",
    "watch": "webpack --watch",
    "start": "webpack-dev-server",
    "server": "node server.js"
  }

意思就是我運(yùn)行npm run middleware的時(shí)候旧噪,我想自己寫一個(gè)服務(wù)器吨娜,這個(gè)服務(wù)器如果監(jiān)聽到src目錄下的內(nèi)容有改變,它會像webpackdevserver一樣自動(dòng)的幫我們?nèi)ブ貑⑦@個(gè)服務(wù)器淘钟,更新網(wǎng)頁上的內(nèi)容宦赠。

  • 安裝express幫助我們快速去創(chuàng)建一個(gè)服務(wù)器, 服務(wù)器要去監(jiān)聽Webpack的變化,然后幫我們重新的去打包米母,所以還需要借助一個(gè)Webpack的開發(fā)中間件webpack-dev-middleware:npm install express webpack-dev-middleware -D
  • webpack.config.js:
image.png

然后在lesson目錄下創(chuàng)建一個(gè)server.js文件:

const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./webpack.config.js');
const complier = webpack(config);   //webpack函數(shù)傳入config勾扭,結(jié)果返回一個(gè)編譯器
 
const app = express();
app.use(webpackDevMiddleware(complier, {
  publicPath: config.output.publicPath   //意思就是只要文件發(fā)生改變了,complier就會重新運(yùn)行铁瞒。重新運(yùn)行生成的文件對應(yīng)的打包輸出內(nèi)容的publicPath就是config.output.publicPath
}))
 
app.listen(3000, () => {
  console.log('server is running');
});

運(yùn)行npm run server命令妙色,發(fā)現(xiàn)成功打包,瀏覽器中也答應(yīng)出內(nèi)容慧耍。我們修改index.js打印的內(nèi)容身辨,刷新瀏覽器后發(fā)現(xiàn)控制臺也跟著改變了,說明又自動(dòng)打包了芍碧。只不過目前沒有webpackdevserver那么智能煌珊。想要實(shí)現(xiàn)那么智能的效果需要耗費(fèi)很多的精力,我們只需要了解可以這么寫就行泌豆。


9. Hot Module Replacement(熱模塊替換HMR)

在使用webpackdevserver的時(shí)候定庵,打包之后我們可以發(fā)現(xiàn)沒有dist這個(gè)目錄。實(shí)際上webpackdevserver還是會對src目錄下的代碼進(jìn)行打包,但是打包生成的文件不會放在dist目錄下洗贰,而是放在電腦內(nèi)存下面找岖。這樣可以有效提升打包的速度,讓我們的開發(fā)更快敛滋。

刪除server.js文件许布,修改一些配置

目錄:


image.png

webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
 
module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',   //開啟sourcemap
  entry: {
    main: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    open: true,    //啟動(dòng)webpackdevser的時(shí)候(即 npm run start)會自動(dòng)幫我們?nèi)ゴ蜷_一個(gè)瀏覽器,然后自動(dòng)訪問服務(wù)器的地址绎晃。
    port: 10000
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.(eot|ttf|svg|woff)$/,    
      use: {
        loader: 'file-loader'
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {                    //增加一個(gè)css loader
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin()
  ],
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
}     

package.json:


image.png

index.js:

import './style.css';
 
var btn = document.createElement('button');
btn.innerHTML = '新增';
document.body.appendChild(btn);
 
btn.onclick = function() {
  var div = document.createElement('div');
  div.innerHTML = 'item';
  document.body.appendChild(div);
}

style.css:

div:nth-of-type(odd) {
  background: yellow;
}

瀏覽器打開這個(gè)項(xiàng)目蜜唾,我們多次點(diǎn)擊新增按鈕,頁面效果:


image.png

此時(shí)我們?nèi)バ薷膕tyle.css文件庶艾,將顏色改為blue袁余。發(fā)現(xiàn)頁面中的div不存在了。我們希望之前渲染的元素不要修改咱揍,只修改樣式颖榜,此時(shí)我們就可以借助HMR來幫助我們實(shí)現(xiàn)該功能。

webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const webpack = require('webpack');    //引入webpack
 
module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  entry: {
    main: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    open: true,
    port: 10000,
    hot: true,    //開啟HMR功能
    hotOnly: true    //即便HRM功能沒有生效煤裙,我也不讓瀏覽器自動(dòng)的刷新
  },
  module: {
    rules: [{
      test: /\.(png|jpg|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.(eot|ttf|svg|woff)$/,    
      use: {
        loader: 'file-loader'
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }), 
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin()    //使用webpack的HMR功能
  ],
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  }
}     

此時(shí)npm run start重新運(yùn)行webpack掩完,發(fā)現(xiàn)再次修改顏色就不會產(chǎn)生上述問題了。

多個(gè)js文件模塊之間不想相互影響時(shí)硼砰,也可以使用HMR,配置都差不多

image.png

當(dāng)我們在代碼里引入其它的模塊的時(shí)候且蓬,如果我們希望這個(gè)模塊的代碼發(fā)生了變化,只去更新這個(gè)模塊這部分的代碼题翰,那就需要使用HMR恶阴。HMR的優(yōu)點(diǎn):方便我們調(diào)試css和js


10. 使用Babel處理ES6的語法(1)

  • 安裝babel-loader插件npm install --save-dev babel-loader @babel/core

  • 添加webpack.config.js配置:


    image.png
  • 安裝babel/preset-env插件幫我們將es6翻譯成es5:npm install @babel/preset-env --save-dev
    這個(gè)時(shí)候我們寫es6語法就會被翻譯成es5的語法了。但是這樣配置語法只是翻譯了一部分豹障,還有一些沒有翻譯的語法在低版本瀏覽器中還是不能識別冯事。我們還需要一個(gè)polyfill插件幫我們將一些變量或者對象在低版本瀏覽器中的補(bǔ)充。

  • 安裝命令:npm install --save @babel/polyfill

  • 在業(yè)務(wù)代碼index.js的最頂部引入:import "@babel/polyfill";
    重新npx webpack進(jìn)行打包沼填,打包之后發(fā)現(xiàn)main.js的文件比之前大了很多桅咆,這就是polyfill要去彌補(bǔ)之前低版本瀏覽器不存在的一些內(nèi)容括授。實(shí)際中我們不希望它把所有缺失的內(nèi)容都保存進(jìn)main.js中坞笙,只需要保存一些我們用了但是低版本瀏覽器缺失的內(nèi)容。增加配置:

image.png

使用npx webpack進(jìn)行打包荚虚,可以發(fā)現(xiàn)main.js小了很多薛夜。

可以單獨(dú)將babel的options寫到.babelrc文件下:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage"
      }
    ]
  ]
}

此時(shí)webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 
 
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    }, {
      test: /\.jpg$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ],
  output: {
    path: path.resolve(__dirname, 'bundle'),
    filename: '[name].js'
  }
}

index.js:

import "@babel/polyfill";
 
const arr = [
  new Promise(() => {}),
  new Promise(() => {})
];
 
arr.forEach((item) => {
  console.log(item);
})

11. 使用Babel處理ES6的語法(2)

1、指定瀏覽器的版本

webpack.config.js:

{
      test: /\.js$/,
      exclude: /node_modules/,
      loader:'babel-loader',
      options: {
        "presets": [['@babel/preset-env', {
          targets: {
            chrome: '67'   //意思是本次項(xiàng)目會運(yùn)行在67版本以上的chrome瀏覽器上版述,這樣打包的文件更小梯澜,因?yàn)?7版本時(shí)有一些用到es6的代碼不需要polyfill補(bǔ)充,因?yàn)榇藭r(shí)該瀏覽器可能已經(jīng)識別了渴析。
          },
          useBuiltIns: 'usage'
        }]]
      }
    }

打包之后發(fā)現(xiàn)main.js更小了晚伙。

注意:如果在編寫一些業(yè)務(wù)代碼的時(shí)候吮龄,需要用到babel闷板,配置方案可以參考在這之前講的那一套配置即可颜武。

2奋单、開發(fā)一個(gè)類庫碧绞、第三方模塊或者一個(gè)組件庫的時(shí)候用babelpolyfill這個(gè)方案實(shí)際上時(shí)會有問題的其兴。因?yàn)樗g一些es6語法時(shí)會通過全局變量的方式來注入砚著,會污染到全局環(huán)境伴嗡。所以要打包一個(gè)ui組件庫等情況下的時(shí)候要換一種配置的方式窄潭。

  • 安裝插件:npm install --save-dev @babel/plugin-transform-runtime
  • 安裝插件:npm install --save @babel/runtime
  • 安裝插件:npm install --save @babel/runtime-corejs2
    通過babel文檔查找配置迅皇,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 = {
  mode: 'production',
  devtool: 'cheap-module-eval-source-map',
  entry: {
    main: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    port: 10000,
    hot: true
  },
  module: {
    rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader:'babel-loader',
      options: {
        // "presets": [['@babel/preset-env', {
        //   targets: {
        //     chrome: '67'   //意思是本次項(xiàng)目會運(yùn)行在67版本以上的chrome瀏覽器上昧辽,這樣打包的文件更小,因?yàn)?7版本時(shí)有一些用到es6的代碼不需要polyfill補(bǔ)充登颓,因?yàn)榇藭r(shí)該瀏覽器可能已經(jīng)識別了搅荞。
        //   },
        //   useBuiltIns: 'usage'
        // }]]
        "plugins": [["@babel/plugin-transform-runtime", {
          "corejs": 2,   
          "helpers": true,
          "regenerator": true,
          "useESModules": false
        }]]
      }
    },
    {
      test: /\.(jpg|png|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 10240
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }),
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ],
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  }
}

index.js:

const arr = [
  new Promise(() => {}),
  new Promise(() => {})
];
 
arr.forEach((item) => {
  console.log(item);
})

配置完畢,打包即可框咙。該方案適合打包一個(gè)ui組件庫等情況取具。

3、當(dāng)babel的配置很多的時(shí)候扁耐,可以在根目錄下創(chuàng)建一個(gè).babelrc文件暇检,將配置代碼放進(jìn)去。

.babelrc:

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

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 = {
  mode: 'production',
  devtool: 'cheap-module-eval-source-map',
  entry: {
    main: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    port: 10000,
    hot: true
  },
  module: {
    rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader:'babel-loader',
    },
    {
      test: /\.(jpg|png|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 10240
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }),
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ],
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  }
}

配置完畢婉称,打包即可块仆。


11. Webpack 實(shí)現(xiàn)對React框架代碼的打包

使用babel/preset-react插件來實(shí)現(xiàn)對react的jsx語法進(jìn)行打包。在babel中查找相應(yīng)的文檔:

  • 要編寫react的代碼王暗,就要安裝react:npm install react react-dom --save
  • 安裝命令:npm install @babel/preset-react -D
    添加配置

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 = {
  mode: 'production',
  devtool: 'cheap-module-eval-source-map',
  entry: {
    main: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    port: 10000,
    hot: true
  },
  module: {
    rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader:'babel-loader',
    },
    {
      test: /\.(jpg|png|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 10240
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }),
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ],
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  }
}

.babelrc:

{
  presets: [
    [
      "@babel/preset-env", {    //再轉(zhuǎn)換一下es6的代碼
        targets: {
          chrome: "67"
        },
        useBuiltIns: "usage"
      }
    
    ],
    "@babel/preset-react"    //先轉(zhuǎn)換一下react的代碼
  ]
}

index.js:

import '@babel/polyfill';   //打包業(yè)務(wù)邏輯代碼
 
import React, { Component } from 'react';
import ReactDom from 'react-dom';
 
class App extends Component {
  render() {
    return <div>hello world</div>
  }
}
 
ReactDom.render(<App />, document.getElementById('root'));

執(zhí)行打包命令后悔据,App這個(gè)類就成功渲染進(jìn)dom中了。


Tree Shaking 概念詳解

index.js:

import '@babel/polyfill';
 
import React, { Component } from 'react';
import ReactDom from 'react-dom';
 
class App extends Component {
  render() {
    return <div>hello world</div>
  }
}
 
ReactDom.render(<App />, document.getElementById('root'));

實(shí)際上當(dāng)前版本的Webpack比較新俗壹,如果在webpack.config.js里面配置了babel-loader的相關(guān)內(nèi)容(其實(shí)我們已經(jīng)把它移到.babelrc這個(gè)文件中了)科汗,如果我們對presets下的useBuiltIns設(shè)置了usage這樣的一個(gè)配置參數(shù)的話。那么我們index.js文件中不去引入babel/polyfill也是可以的绷雏。

2头滔、

目錄:


image.png

index.js:

import {add} from './math.js';
 
add(1, 2);

math.js:

export const add = (a, b) => {
  console.log(a + b);
}
 
export const minus = (a, b) => {
  console.log(a - b);
}

執(zhí)行npx webpack,控制臺打印出3涎显。但是我們?nèi)ain.js里面發(fā)現(xiàn)我們沒有import的minus方法也打包了坤检。這是沒有必要的,因?yàn)槲覀兊臉I(yè)務(wù)代碼只引入了add方法期吓,將minus也打包會導(dǎo)致打包后的main.js過大早歇。最理想的打包方式就是我們引入什么就打包什么。解決辦法:

webpack在2.0版本以后提出了Tree shaking這個(gè)概念:實(shí)際上就是把一個(gè)模塊里沒用的東西都搖晃(shaking表示搖晃)掉。一個(gè)模塊可以理解成一個(gè)樹箭跳,比如說math.js文件是一個(gè)模塊晨另,里面會導(dǎo)出很多的內(nèi)容,這些內(nèi)容可以理解成一個(gè)小的樹形結(jié)構(gòu)谱姓。而我們在index.js中只引入樹的一部分拯刁,只需要把引入的那一部分打包即可。

注意: Tree-shaking只支持ES module模塊引入的方式引入逝段。

配置Webpack.config.js:

mode設(shè)置成 'development'的時(shí)候是沒有Tree shanking這個(gè)功能的垛玻,所以我們需要自己配置:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const webpack = require('webpack');
 
module.exports = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  entry: {
    main: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    port: 10000,
    hot: true
  },
  module: {
    rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader:'babel-loader',
    },
    {
      test: /\.(jpg|png|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 10240
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }),
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ],
  optimization: {    //在開發(fā)環(huán)境中配置Tree shaking
    usedExports: true    //意思就是去打包引入的模塊
  },
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  }
}

package.json:

小知識:


image.png
{
  "name": "webpackDemo",
  "sideEffects": false,
  "version": "1.0.0",
  "description": "",
  "main": "postcss.config.js",
  "dependencies": {
    "@babel/polyfill": "^7.4.4",
    "@babel/runtime-corejs2": "^7.5.4",
    "autoprefixer": "^9.6.1",
    "clean-webpack-plugin": "^3.0.0",
    "core-js": "^3.1.4",
    "css-loader": "^3.0.0",
    "file-loader": "^4.0.0",
    "html-webpack-plugin": "^3.2.0",
    "node-sass": "^4.12.0",
    "postcss-loader": "^3.0.0",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "sass-loader": "^7.1.0",
    "style-loader": "^0.23.1",
    "url-loader": "^2.0.1",
    "webpack": "^4.35.3",
    "webpack-cli": "^3.3.5",
    "webpack-dev-server": "^3.7.2"
  },
  "private": true,
  "devDependencies": {
    "@babel/core": "^7.5.4",
    "@babel/plugin-transform-runtime": "^7.5.0",
    "@babel/preset-env": "^7.5.4",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.6"
  },
  "scripts": {
    "start": "webpack-dev-server"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

"sideEffects": false, 意思是比如index.js中引入了babel/polyfill文件,由于它沒有導(dǎo)出任何東西奶躯,用了Tree-shaking后導(dǎo)致打包會忽略它帚桩。因?yàn)楫?dāng)前index.js中沒有引入像babel/polyfill這樣的包,所以設(shè)置成false嘹黔。表示沒有需要特殊處理的東西账嚎。

重新執(zhí)行打包,發(fā)現(xiàn)還是把minus給打包了儡蔓,這是因?yàn)樵陂_發(fā)環(huán)境中生成的代碼需要做一些調(diào)試郭蕉,如果Tree-shaking把一些代碼刪除掉的話

,在做調(diào)試的時(shí)候喂江,代碼對應(yīng)的sourcemap的行數(shù)就都錯(cuò)了召锈。所以Tree-shaking還是會保留這些代碼。不過從下圖可以看出获询,它已經(jīng)提示我們了導(dǎo)出的只用了add這個(gè)模塊涨岁。

image.png

production環(huán)境下:此時(shí)Tree-shaking就會生效了。配置如下:

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 = {
  mode: 'production',
  devtool: 'cheap-module-source-map',
  entry: {
    main: './src/index.js'
  },
  devServer: {
    contentBase: './dist',
    port: 10000,
    hot: true
  },
  module: {
    rules: [
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader:'babel-loader',
    },
    {
      test: /\.(jpg|png|gif)$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 10240
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: [
        'style-loader',
        'css-loader',
        'postcss-loader'
      ]
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: 'src/index.html'
    }),
    new CleanWebpackPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ],
  //該模式下Tree-shaking的配置自動(dòng)就會寫好了吉嚣,我們不用自己配webpack.config.js
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist')
  }
}

再次執(zhí)行打包后發(fā)現(xiàn)Tree-shaking就生效了:


image.png

二. Develoment 和 Production 模式的區(qū)分打包

1梢薪、當(dāng)我們項(xiàng)目開發(fā)完畢,就需要使用 production模式打包上線尝哆。兩種打包模式的差異:

  • 開發(fā)環(huán)境中sourcemap是非常全的秉撇,這樣的話再開發(fā)環(huán)境下能夠幫我們快速的定位問題。production模式下秋泄,由于已經(jīng)要上線了琐馆,此時(shí)sourcemap就不是那么重要了,此時(shí)的sourcemap會更加簡潔一些或者我們通過生成一個(gè).map文件來進(jìn)行存儲印衔。
  • 開發(fā)環(huán)境下我們的代碼不需要進(jìn)行壓縮啡捶,方便我們查看打包后的代碼。一旦代碼要上線奸焙,此時(shí)我們就希望代碼被壓縮,所以production模式下代碼是被壓縮過的。

2与帆、我們切換兩種模式的時(shí)候了赌,需要經(jīng)常修改webpack.config.js文件,這樣很麻煩玄糟。解決辦法:

  • 首先將 development 模式下的 webpack 配置文件命名為 webpack.dev.js 勿她。
    在根目錄下新建一個(gè) webpack.prod.js 文件,里面放置我們線上環(huán)境下的webpack配置代碼阵翎。

webpack.dev.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 
 
module.exports = {
  mode: 'development',
  entry: './src/index.js',
  devtool: 'cheap-module-eval-source-map',
  devServer: {
    contentBase: './dist',
    port: 10000
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    }, {
      test: /\.jpg$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: ['style-loader', 'css-loader', 'postcss-loader']
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ],
  optimization: {    //在開發(fā)環(huán)境中配置Tree shaking
    usedExports: true    //意思就是去打包引入的模塊
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  }
}

webpack.prod.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 
 
module.exports = {
  mode: 'production',
  entry: './src/index.js',
  devtool: 'cheap-module-source-map',
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    }, {
      test: /\.jpg$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: ['style-loader', 'css-loader', 'postcss-loader']
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ],
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  }
}

package.json:


image.png

dev: 如果要啟動(dòng)devserver進(jìn)行開發(fā)的話逢并,那么就是使用開發(fā)環(huán)境的webpack配置

build: 如果打包線上文件的話,就使用webpack.prod.js這個(gè)配置

此時(shí)運(yùn)行npm run dev郭卫,進(jìn)行的是開發(fā)環(huán)境的webpack打包配置砍聊。運(yùn)行npm run build,執(zhí)行的是生產(chǎn)環(huán)境的打包配置贰军。

3玻蝌、上面的兩種打包方案的文件中存在大量的重復(fù)代碼,優(yōu)化方案:

  • 根目錄下創(chuàng)建webpack.common.js文件词疼。
  • 將兩者共有的代碼都賦值到該文件下俯树,并將之前兩個(gè)文件共有的代碼刪除。
  • 引入一個(gè)第三方模塊: cnpm install webpack-merge -D贰盗,幫助我們將common的代碼和兩個(gè)打包文件代碼做一個(gè)融合许饿。
    webpack.common.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 
 
module.exports = {
  entry: './src/index.js',
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    }, {
      test: /\.jpg$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: ['style-loader', 'css-loader', 'postcss-loader']
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ],
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  }
}

webpack.dev.js:

const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
 
const devConfig = {
  mode: 'development',
  devtool: 'cheap-module-eval-source-map',
  devServer: {
    contentBase: './dist',
    port: 10000
  },
  optimization: {    //在開發(fā)環(huán)境中配置Tree shaking
    usedExports: true    //意思就是去打包引入的模塊
  }
}
 
module.exports = merge(commonConfig, devConfig);   //將自有的和公共的做一個(gè)結(jié)合

webpack.prod.js:

const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');
 
const prodConfig = {
  mode: 'production',
  devtool: 'cheap-module-source-map'
}
 
module.exports = merge(commonConfig, prodConfig);

此時(shí)能夠正常進(jìn)行兩種不同方案的打包。

注意:如果將三個(gè)文件都放在一個(gè)文件夾下面舵盈,此時(shí)package.json文件的命令路徑需要修改米辐,比如在build文件夾下:

"dev": "webpack-dev-server --config ./build/webpack.dev.js"

此時(shí)應(yīng)該修改output配置:


image.png

三. Webpack 和 Code Splitting

Code Splitting:指的是代碼分割。

此時(shí)的目錄結(jié)構(gòu):


image.png

因?yàn)槲覀兿M榭创虬蟮奈募a书释,所以不能用npm run dev來打包翘贮,添加命令:


image.png

1、例子

  • 安裝一個(gè)插件:cnpm install lodash --save 它是一個(gè)功能集合爆惧,提供很多方法狸页,可以高性能地去幫助我們使用字符串拼接的一些函數(shù)等。
    index.js添加代碼:
import _ from 'lodash';
 
console.log(_.join(['a', 'b', 'c']));   //a,b,c

打包后打印出a,b,c扯再。

假如我們index.js的業(yè)務(wù)邏輯很多芍耘,打包的話會把工具庫和業(yè)務(wù)邏輯都打包到main.js文件中。此時(shí)雖然也能正常運(yùn)行熄阻,但是main.js會比較大斋竞,導(dǎo)致加載時(shí)間很長。還有就是假如我們修改了業(yè)務(wù)代碼秃殉,用戶要重新去加載main.js坝初,此時(shí)又要等很長的時(shí)間浸剩,這樣就導(dǎo)致用戶體驗(yàn)很差。

解決辦法:

  • src目錄下新建一個(gè)lodash.js文件鳄袍,添加代碼:
import _ from 'lodash';
 
window._ = _;  //加載了一個(gè)lodash,又將lodash掛載到了全局的下劃線上面绢要,這樣的話我們在其它地方就可以是使用下劃線這個(gè)變量了。

webpack.common.js添加配置:

  entry: {
    lodash: './src/lodash.js',
    main: './src/index.js'
  }

再次打包拗小,運(yùn)行正常重罪。
此時(shí)打包后分成了兩個(gè)js文件,這帶來的好處就是當(dāng)我們修改了業(yè)務(wù)代碼哀九,用戶只需要加載打包后的業(yè)務(wù)js代碼即可剿配,不用加載庫的代碼。

在項(xiàng)目中對代碼公用部分進(jìn)行拆分來提升項(xiàng)目運(yùn)行的速度阅束,也就是Code Splitting呼胚。

2、上面的例子我們是自己做的公用代碼拆分围俘,它不夠智能砸讳。webpack通過它自帶的一些插件可以智能地幫助我們做一些拆分工作。

  • 刪除lodash.js文件界牡,修改index.js文件:
import _ from 'lodash';
console.log(_.join(['a', 'b', 'c']));   //a,b,c
  • 添加weback.common.js配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 
 
module.exports = {
  entry: {
    main: './src/index.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    }, {
      test: /\.jpg$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: ['style-loader', 'css-loader', 'postcss-loader']
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ],
  optimization: {
    splitChunks: {
      chunks: 'all'   //意思就是我要幫你去做代碼分割了
    }
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js'
  }
}

此時(shí)執(zhí)行npm run dev-build打包命令簿寂,發(fā)現(xiàn)dist文件夾下面打包了兩個(gè)js文件,一個(gè)是業(yè)務(wù)邏輯js宿亡,還有一個(gè)是類庫js常遂。

image.png

這種代碼分割配置適合上面這種同步模塊的分割。

3挽荠、webpack中的代碼分割不僅僅適合上面這種同步模塊的分割克胳。異步的代碼也能進(jìn)行分割,而且不需要添加上面的那種配置就會自動(dòng)進(jìn)行圈匆。

index.js:

function getComponent() {
  return import('lodash').then(( { default: _ } ) => {
    var element = document.createElement('div');
    element.innerHTML = _.join(['z', 't'], '-');
    return element;
  })
};
 
getComponent().then( element => {
  document.body.appendChild(element);
});

webpack.config.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 
 
module.exports = {
  entry: {
    main: './src/index.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    }, {
      test: /\.jpg$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: ['style-loader', 'css-loader', 'postcss-loader']
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ],
  // optimization: {
  //   splitChunks: {
  //     chunks: 'all'   //意思就是我要幫你去做代碼分割了
  //   }
  // },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js'
  }
}

執(zhí)行打包命令漠另,此時(shí)webpack對于這種異步的代碼也能智能地進(jìn)行分割。


四. SplitChunksPlugin 配置參數(shù)詳解
webpack的代碼分割底層使用了SplitChunksPlugin這個(gè)插件跃赚。

上面打包之后的dist文件夾:

image.png

1笆搓、 我們希望把 0.js 改一個(gè)名字

修改index.js文件:

function getComponent() {
  //下面的意思就是異步地引入lodash這樣一個(gè)庫,當(dāng)我做代碼分割的時(shí)候纬傲,單獨(dú)給lodash進(jìn)行打包的時(shí)候满败,給它起個(gè)名字叫l(wèi)odash
  return import(/* webpackChunkName: "lodash" */'lodash').then(( { default: _ } ) => {
    var element = document.createElement('div');
    element.innerHTML = _.join(['z', 't'], '-');
    return element;
  })
};
 
getComponent().then( element => {
  document.body.appendChild(element);
});
  • 安裝一個(gè)插件幫我們識別上面那種魔法字符串寫法:cnpm install --save-dev @babel/plugin-syntax-dynamic-import
  • .babelrc添加配置:
{
  "presets":[
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage"
      }
    ],
    "@babel/preset-react"
  ],
  "plugins": ["@babel/plugin-syntax-dynamic-import"] 
}

執(zhí)行 npm run dev-build 命令后,


image.png
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 
 
module.exports = {
  entry: {
    main: './src/index.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    }, {
      test: /\.jpg$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: ['style-loader', 'css-loader', 'postcss-loader']
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',   //意思就是我要幫你去做代碼分割了
      cacheGroups: {
        vendors: false,
        default: false
      }
    }
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js'
  }
}

執(zhí)行打包命令后叹括,dist文件夾下:


image.png

2算墨、SplitChunks配置介紹:

splitChunks: {
    chunks: "async",  //在做代碼分割的時(shí)候,只對異步代碼生效汁雷。
    minSize: 30000,   //發(fā)現(xiàn)引入的塊大于30000個(gè)字節(jié)的話净嘀,就會幫我們做代碼分割
    minChunks: 1,     //當(dāng)一個(gè)Chunk被至少用了多少次的時(shí)候报咳,才對他進(jìn)行代碼分割
    maxAsyncRequests: 5,  //同時(shí)加載的模塊數(shù),最多是5個(gè)
    maxInitialRequests: 3,  //整個(gè)網(wǎng)站首頁進(jìn)行加載的時(shí)候面粮,或者說入口文件進(jìn)行加載的時(shí)候少孝,入口文件可能會引入其它的js文件继低,入口文件如果做代碼分割熬苍,最多只能分割出3個(gè)。此處一般不修改
    automaticNameDelimiter: '~',  //組和文件名之間連接的符號
    name: true,   //打包生成的名字通過cacheGroup設(shè)置有效袁翁。此處一般不變
    cacheGroups: {  //如果都符合下面?zhèn)z個(gè)組的要求柴底,那么誰的priority值大,就用誰的
        vendors: {  //vendors組
            test: /[\\/]node_modules[\\/]/,   //如果是從node_modules引入的
            priority: -10
        },
    default: {      //這個(gè)組里面沒有test粱胜,意思就是所有的模塊都符合要求
            minChunks: 2,
            priority: -20,
            reuseExistingChunk: true  //如果一個(gè)模塊已經(jīng)被打包了柄驻,再打包就會忽略這個(gè)模塊,直接使用之前使用的那么模塊就可以
        }
    }
}

對同步的代碼進(jìn)行分割:

splitChunks: {
    chunks: "all",   //1  同步和異步代碼都會做分割焙压,initial表示對同步代碼做分割
    minSize: 30000,
    minChunks: 1,
    maxAsyncRequests: 5,
    maxInitialRequests: 3,
    automaticNameDelimiter: '~',
    name: true,
    cacheGroups: {
        vendors: {  //2
            test: /[\\/]node_modules[\\/]/,
            priority: -10,
            filename: 'vendors.js'    //打包后的名字不帶main,只有vendors
        },
        default: false   
    }
}

五. Lazy Loading 懶加載鸿脓,Chunk 是什么?

1涯曲、實(shí)現(xiàn)一個(gè)懶加載的行為

index.js:

function getComponent() {
  return import(/* webpackChunkName: "lodash" */'lodash').then(( { default: _ } ) => {
    var element = document.createElement('div');
    element.innerHTML = _.join(['z', 't'], '-');
    return element;
  })
};
 
document.addEventListener('click', () => {
  getComponent().then( element => {
    document.body.appendChild(element);
  });
})

webpack.common.js:

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 
 
module.exports = {
  entry: {
    main: './src/index.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    }, {
      test: /\.jpg$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: ['style-loader', 'css-loader', 'postcss-loader']
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ],
  optimization: {
    splitChunks: {
      chunks: 'all',   //意思就是我要幫你去做代碼分割了
      cacheGroups: {
        vendors: false,
        default: false
      }
    }
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js'
  }
}

執(zhí)行打包命令野哭,此時(shí)控制臺的network只加載了main.js:


image.png

再頁面點(diǎn)擊之后,才加載lodash.js文件:

image.png

上面這種做法的好處是能夠讓頁面的加載速度更快幻件,只加載該頁面用到的js代碼拨黔。
可以使用async函數(shù)來實(shí)現(xiàn)上面的效果,index.js:

async function getComponent() {
  const { default: _ } = await import(/* webpackChunkName: "lodash" */'lodash');
  const element = document.createElement('div');
  element.innerHTML = _.join(['z', 't'], '-');
  return element;
}
 
document.addEventListener('click', () => {
  getComponent().then( element => {
    document.body.appendChild(element);
  });
})

2绰沥、Chunk

Webpack打包過后生成了幾個(gè)js文件篱蝇,每個(gè)js文件我們都把它叫做一個(gè)Chunk。


六. 打包分析徽曲,Preloading, Prefetching
1零截、打包分析:當(dāng)我們使用Webpack進(jìn)行打包之后,我們可以借助打包分析的工具來對我們的打包文件進(jìn)行一定的分析秃臣,然后看打包是否合理涧衙。

打包分析網(wǎng)址
https://github.com/webpack/analyse

上面網(wǎng)址中的教程:

運(yùn)行:npx webpack --profile --json > stats.json 會在根目錄下生成一個(gè)stats.json,該文件就是對打包過程的一個(gè)描述甜刻。
生成該文件以后绍撞,我們就可以借助一些工具來分析里面的內(nèi)容了:進(jìn)入上面的網(wǎng)址,點(diǎn)擊首頁的鏈接得院,就會進(jìn)入一個(gè)頁面傻铣,進(jìn)入之后將stats.json文件上傳上去就會幫我們進(jìn)行打包分析了。
除了這個(gè)分析方法之外祥绞,還有很多別的方法非洲,可以去webpack的文檔中查看鸭限。


七. CSS 文件的代碼分割
index.js

import './style.css';
  • 在src目錄下創(chuàng)建style.css文件,添加代碼:
body {
  background-color: green;
}

執(zhí)行打包后發(fā)現(xiàn)雖然dist目錄下沒有css文件两踏,但是頁面卻又效果败京。這是因?yàn)閣ebpack在做打包的時(shí)候會把Css文件直接打包到j(luò)s里面。

1梦染、希望實(shí)現(xiàn)Css文件單獨(dú)打包進(jìn)dist文件夾下面赡麦。

借助 MiniCssExtractPlugin 這個(gè)插件來進(jìn)來css代碼分割。

  • 安裝:
npm install --save-dev mini-css-extract-plugin
  • webpack.common.js添加配置:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 
module.exports = {
  entry: {
    main: './src/index.js'
  },
  module: {
    rules: [{
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    }, {
      test: /\.jpg$/,
      use: {
        loader: 'url-loader',
        options: {
          name: '[name].[ext]',
          outputPath: 'images/',
          limit: 2048
        }
      }
    }, {
      test: /\.scss$/,
      use: [
        MiniCssExtractPlugin.loader,    //1
        {
          loader: 'css-loader',
          options: {
            importLoaders: 2
          }
        },
        'sass-loader',
        'postcss-loader'
      ]
    }, {
      test: /\.css$/,
      use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader']   //2
    }]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin(),
    new MiniCssExtractPlugin({})
  ],
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  },
  output: {
    path: path.resolve(__dirname, '../dist'),
    filename: '[name].js'
  }
}

此時(shí)執(zhí)行打包命令后發(fā)現(xiàn)dist目錄下成功打包出了css文件帕识。

2泛粹、做Css代碼的壓縮

const TerserJSPlugin = require('terser-webpack-plugin');
// 安裝這兩個(gè)插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
 
module.exports = {
  // 配置壓縮
  optimization: {
    minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].css',
      chunkFilename: '[id].css',
    }),
  ],
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader'],
      },
    ],
  },
};

今天回顧下webpack的相關(guān)知識點(diǎn),發(fā)現(xiàn)了這篇文章寫的相當(dāng)詳細(xì)肮疗,順手摘過來記錄下晶姊,整體看下來的大概1個(gè)小時(shí)左右,備份下
原文鏈接:https://blog.csdn.net/sinat_40697723/article/details/95349783

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末伪货,一起剝皮案震驚了整個(gè)濱河市们衙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碱呼,老刑警劉巖蒙挑,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異巍举,居然都是意外死亡脆荷,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門懊悯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蜓谋,“玉大人,你說我怎么就攤上這事炭分√一溃” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵捧毛,是天一觀的道長观堂。 經(jīng)常有香客問我,道長呀忧,這世上最難降的妖魔是什么师痕? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮而账,結(jié)果婚禮上商玫,老公的妹妹穿的比我還像新娘绎橘。我一直安慰自己沪停,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布竞滓。 她就那樣靜靜地躺著,像睡著了一般吹缔。 火紅的嫁衣襯著肌膚如雪商佑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天厢塘,我揣著相機(jī)與錄音茶没,去河邊找鬼。 笑死俗冻,一個(gè)胖子當(dāng)著我的面吹牛礁叔,可吹牛的內(nèi)容都是我干的牍颈。 我是一名探鬼主播迄薄,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼煮岁!你這毒婦竟也來了讥蔽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤画机,失蹤者是張志新(化名)和其女友劉穎冶伞,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體步氏,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡响禽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了荚醒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芋类。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖界阁,靈堂內(nèi)的尸體忽然破棺而出侯繁,到底是詐尸還是另有隱情,我是刑警寧澤泡躯,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布贮竟,位于F島的核電站,受9級特大地震影響较剃,放射性物質(zhì)發(fā)生泄漏咕别。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一写穴、第九天 我趴在偏房一處隱蔽的房頂上張望惰拱。 院中可真熱鬧,春花似錦确垫、人聲如沸弓颈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翔冀。三九已至导街,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纤子,已是汗流浹背搬瑰。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留控硼,地道東北人泽论。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像卡乾,于是被迫代替她去往敵國和親翼悴。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353