webpack打包詳解(頁面性能優(yōu)化)
先附上webpack的中文文檔地址:https://www.webpackjs.com/
先讓我們來了解一下什么是webpack
webpack 是一個現(xiàn)代 JavaScript 應(yīng)用程序的靜態(tài)模塊打包器(module bundler)。當(dāng) webpack 處理應(yīng)用程序時,它會遞歸地構(gòu)建一個依賴關(guān)系圖(dependency graph),其中包含應(yīng)用程序需要的每個模塊,然后將所有這些模塊打包成一個或多個 bundle,在webpack眼里一切都可以打包,打包成js文件
一.下載webpack環(huán)境
npm i webpack@4 webpack-cli@3 -D
安裝webpack4 配置 cli3
接下來做一個小測試
在src下創(chuàng)建app.js
src/app.js
import name from './name.js'
console.log(name)
src/name.js
export default 'test'
命令行輸入打包指令
webpack --entry ./src/app.js --output dist/bundle.js
//--entry后為入口文件地址 --output后為出口文件地址
// 可以進(jìn)入dist文件夾中查看是否打包成功
二.正式配置
第一步運行完畢,成功打包,說明了webpack的環(huán)境已經(jīng)安裝成功,接下來就可以開始正式的打包配置了
2.1 入口及出口的配置
src/app.js
console.log(config)
//app.js 入口文件中打印一個字符
根目錄下創(chuàng)建config/webpack.config.dev.js
const path = require('path')
module.exports = {
mode: 'development',
//入口,從入口開始逐個模塊打包
entry: {
app: path.resolve(__dirname, '../src/app.js')
},
//出口,編譯完成輸出文件夾
output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].js' //[name] 拿到entry中的key值
}
}
配置啟動命令
"scripts": {
"dev": "",
"build": "webpack --config ./config/webpack.config.dev.js"
}
2.2. 載入其他功能插件
npm i html-webpack-plugin -D/ yarn add html-webpack-plugin -D解析html
npm i copy-webpack-plugin -D/ yarn add copy-webpack-plugin -D打包小圖標(biāo)
npm i clean-webpack-plugin -D/ yarn add clean-webpack-plugin -D
刪除dist中的文件
解析html:導(dǎo)入html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...,
//插件
plugins: [
new HtmlWebpackPlugin({
//導(dǎo)入文件名
filename: 'index.html',
//模板:導(dǎo)入的html路徑
template: path.resolve(__dirname, '../public/index.html')
})
]
}
打包小圖標(biāo):導(dǎo)入copy-webpack-plugin
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
...,
plugins: [
...,
new CopyWebpackPlugin({
patterns: [
{
from: resolve('public/favicon.ico'),
to: resolve('dist/')
}
]
})
]
}
刪除dist圖片:npm i clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
...,
plugins: [
new CleanWebpackPlugin (),
...
]
}
2.3. 樣式處理
打包css也是css代碼壓縮,簡單來說就是無效代碼刪除和css語義合并css的樣式文件可分為css、stylus、scss,打包不同類型的文件,也需要下載對應(yīng)的模塊
css:npm i css-loader -D / yarn add css-loader -D
stylus:npm i stylus stylus-loader -D / yarn add stylus stylus-loader -D
scss:npm i node-sass sass-loader -D / yarn add ndoe-sass sass-loader -D
style-loader:npm i style-loader -D / yarn add style-loader -D
css抽離:npm i mini-css-extract-plugin -D / yarn add mini-css-extract-plugin -D
2.3.1. css:css類型文件的打包
module.exports = {
...,
//模塊
module: {
rules: [
...,
{
//正則驗證,.css為后綴的使用css-loader
test: /\.css$/,
//載入程序:
loader: 'css-loader'
}
]
},
...
}
若樣式導(dǎo)入進(jìn)js中,將其解析出來需要使用style-loader 模塊
npm i style-loader -D / yarn add style-loader -D
module.exports = {
...,
module: {
rules: [
...,
{
test: /\.css$/,
// loader: 'css-loader'
loaders: ['style-loader', 'css-loader'] // 后面的模塊為前面的服務(wù)
}
]
},
...
}
2.3.2. stylus:stylus類型的打包
module.exports = {
...,
module: {
rules: [
...,
{
test: /\.(css|styl)$/,
// loader: 'css-loader'
loaders: ['style-loader', 'css-loader', 'stylus-loader'] // 后面的為前面的服務(wù)
}
]
},
...
}
2.3.3. scss:scss類型的打包
module.exports = {
...,
module: {
rules: [
...,
{
test: /\.scss$/,
loaders: ['style-loader', 'css-loader', 'sass-loader'] // 后面的為前面的服務(wù)
}
]
},
...
}
2.3.4. css的抽離:css代碼被css-loader轉(zhuǎn)換后,交給的是style-loader進(jìn)行處理。
style-loader使用的方式是用一段js代碼,將樣式加入到style元素中。而實際的開發(fā)中,我們往往希望依賴的樣式最終形成一個css文件
此時,就需要用到一個庫:mini-css-extract-plugin,該庫提供了1個plugin和1個loader
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
...,
module: {
rules: [
...,
{
test: /\.(css|styl)$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'stylus-loader']
},
{
test: /\.scss$/,
use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
}
]
},
plugins: [
...,
new MiniCssExtractPlugin({
filename: 'style/[name].css'
})
],
...
}
2.3.5. 自動補全css:
對于css屬性,尤其對css3的新屬性而言,瀏覽器私有屬性前綴,是非常滴重要,然鵝,如果要知道每個屬性的瀏覽器私有前綴,那是非常的麻煩,“如何補齊css前綴”,且看**autoprefixer
//配置文件中
const loaderUse = (fileLoader) => {
return [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../'
}
},
'css-loader',
'postcss-loader', // ******
fileLoader
]
}
//posts.config.js
module.exports = {
plugins: [
require('autoprefixer')({
overrideBrowserslist: ['last 100 versions']
})
]
}
//如果不設(shè)置配置文件
const loaderUse = (fileLoader) => {
return [
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../'
}
},
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
[
'autoprefixer',
{
overrideBrowserslist: ['last 100 versions']
}
],
[
'postcss-preset-env',
{
// Options
}
]
]
}
}
},
fileLoader
]
}
2.4. 處理圖片
方式一:src/assets圖片
module.exports = {
...,
module: {
rules: [
...,
{
test: /\.(jpg|png|jpeg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 2048 // 如果設(shè)置的值夠大,顯示base64(內(nèi)存中),如果設(shè)置的值小,顯示圖片地址
}
}
]
}
]
},
...
}
如果需要設(shè)置圖片的輸出目錄
module.exports = {
...,
module: {
rules: [
...,
{
test: /\.(jpg|png|jpeg|gif)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 2048, // 如果設(shè)置的值夠大,顯示base64,如果設(shè)置的值小,顯示圖片地址
outputPath: '../dist/images' // 不要寫絕對路徑 類似 path.resolve()?
}
}
]
}
]
},
...
}
方式二:放入public 拷貝至 dist
不受webpack管轄,直接拷貝即可,使用copy-webpack-plugin插件
module.exports = {
...,
plugins: [
...,
new CopyWebpackPlugin({
patterns: [
{
context: resolve('public/'), // 一定要添加上下文對象,否則直接復(fù)制public目錄至dist
from: '**/*',
to: resolve('dist/'),
globOptions: {
ignore: ['index.html']
}
}
]
}),
...
],
...
}
2.5. 處理高級js
npm i @babel/core @babel/preset-env babel-loader -D / yarn add @babel/core @babel/preset-env babel-loader -D
@babel/preset-env是一系列插件的集合,包含了我們在babel6中常用的es2015,es2016, es2017等最新的語法轉(zhuǎn)化插件,允許我們使用最新的js語法,比如 let,const,箭頭函數(shù)等等,但不包括stage-x階段的插件。
module.exports = {
...,
module: {
rules: [
{
test: /\.js$/,
// loader: 'babel-loader '
// bower_components 是以前的一種包管理器 bootstrap
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
...
]
},
...
}
但是如果給類添加屬性之后,就會發(fā)生錯誤,此時需要添加新的插件
npm i @babel/plugin-proposal-class-properties -D / yarn add @babel/plugin-proposal-class-properties -D
module.exports = {
...,
module: {
rules: [
{
test: /\.js$/,
// loader: 'babel-loader '
// bower_components 是以前的一種包管理器 bootstrap
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-proposal-class-properties']
}
}
},
...
]
},
...
}
如果代碼包含 async await,需要添加新的插件
npm i @babel/plugin-transform-runtime @babel/runtime -D / yarn add @babel/plugin-transform-runtime @babel/runtime -D
module.exports = {
...,
module: {
rules: [
{
test: /\.js$/,
// loader: 'babel-loader '
// bower_components 是以前的一種包管理器 bootstrap
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-transform-runtime'
]
}
}
},
...
]
},
...
}
如果代碼中有裝飾器,同樣需要添加新的插件
npm i @babel/plugin-proposal-decorators -D / yarn add @babel/plugin-proposal-decorators -D
module.exports = {
...,
module: {
rules: [
{
test: /\.js$/,
// loader: 'babel-loader '
// bower_components 是以前的一種包管理器 bootstrap
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
plugins: [
'@babel/plugin-transform-runtime', // 處理async await
[
'@babel/plugin-proposal-decorators',
{
legacy: true
}
],
[
'@babel/plugin-proposal-class-properties', // 處理類的屬性
{
loose: true
}
]
]
}
}
},
...
]
},
...
}
如果js中含有瀏覽器解析不了的語句,可以使用墊片
npm i @babel/polyfill -D / yarn add @babel/polyfill -D
方式一:入口文件頂級添加如下語句
import "@babel/polyfill";
方式二:配置文件入口處
module.exports = {
entry: {
app: ["@babel/polyfill", path.resolve(__dirname, '../src/app.js')]
}
}
方式三:添加@babel/preset-env時添加配置選項
module.exports = {
...,
module: {
rules: [
{
test: /\.js$/,
// loader: 'babel-loader '
// bower_components 是以前的一種包管理器 bootstrap
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage'
}
]
],
plugins: [
'@babel/plugin-transform-runtime', // 處理async await
[
'@babel/plugin-proposal-decorators',
{
legacy: true
}
],
[
'@babel/plugin-proposal-class-properties', // 處理類的屬性
{
loose: true
}
]
]
}
}
},
...
]
},
...
}
此時運行查看會有提示信息安裝core-js模塊,需要安裝配置
npm i core-js@3 -D / yarn add core-js@3 -D
module.exports = {
...,
module: {
rules: [
{
test: /\.js$/,
// loader: 'babel-loader '
// bower_components 是以前的一種包管理器 bootstrap
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: [
[
'@babel/preset-env',
{
useBuiltIns: 'usage',
corejs:3
}
]
],
plugins: [
'@babel/plugin-transform-runtime', // 處理async await
[
'@babel/plugin-proposal-decorators',
{
legacy: true
}
],
[
'@babel/plugin-proposal-class-properties', // 處理類的屬性
{
loose: true
}
]
]
}
}
},
...
]
},
...
}
為什么要使用墊片
Babel默認(rèn)只轉(zhuǎn)換新的JavaScript句法(syntax),而不轉(zhuǎn)換新的API,比如 Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局對象,以及一些定義在全局對象上的方法(比如Object.assign)都不會轉(zhuǎn)碼。舉個例子,ES6在Array對象上新增了Array.from方法。Babel就不會轉(zhuǎn)碼這個方法。如果想讓這個方法運行,必須使用babel-polyfill,為當(dāng)前環(huán)境提供一個墊片。