Webpack核心概念解析

原文連接:banggan.github.io/2019/05/09/…css

Webpack核心概念解析

終於忙完了論文,能夠愉快的開始學習了,重拾起重學前端、webpack以及Vue的源碼解讀做爲入職前的複習吧。整個webpack系列將分紅五個大的部分進行,以webpack4.0爲文檔進行解讀,從簡單的概念解讀到最後的實現。 整個知識點涉及範圍: html

image

loader

使用loader來預處理文件,把不一樣的靜態資源(模塊的結尾不是js的模塊)打包成js文件前端

loader打包靜態資源

打包圖片

  • 安裝使用file-loader實現:npm install file-loader -D
  • 在webpack.config.js中添加loader的配置
module.exports = {
    //打包項目的入口文件
    entry: './src/index.js',
    module:{
        rules:[{
            test:/\.(jpg|png|gif)$/,//打包以jpg、png、gif結尾的全部圖片文件
            use:{
                loader:'file-loader',
                options:{//placeholder 佔位符 
                    name:'[name]_[hash].[ext]',//保持原圖片的名字+hash值和後綴,主要單引號
                    outputPath:'image/'//打包圖片的位置
                }
            }
        }]
    }
}
複製代碼

打包圖片成base64格式

url-loader基本能實現file-loader的打包功能,適用於小圖片的打包vue

  • 好處:圖片打包成js文件,不用加載圖片的地址,頁面快速顯示
  • 壞處:圖片過大致使js文件過大

因此,當圖片的大小小於limit值時會把圖片打包成base64格式,大於limit值則按照file-loader打包成圖片文件node

  • 安裝使用url-loader實現:npm install url-loader -D
  • 在webpack.config.js中添加loader的配置
module.exports = {
    module:{
        rules:[{//打包以jpg、png、gif結尾的全部圖片文件
            test:/\.(jpg|png|gif)$/,
            use:{
                loader:'url-loader',
                options:{//placeholder 佔位符 
                name:'[name]_[hash].[ext]',//保持原圖片的名字+hash值和後綴,主要單引號
                outputPath:'image/',//打包圖片的位置
                limit:2048
            }
        }]
    }
}
複製代碼

打包樣式css文件

須要使用css-loader、style-loaderwebpack

  • css-loader:分析幾個css文件的關係,合併css文件
  • style-loader:將css-loader合併的css內容掛載在頁面的head部分

實現方式:git

  • 安裝loader實現:npm install css-loader style-loader -D
  • 在webpack.config.js中添加loader的配置
module.exports = {
    module: {
        rules: [{//打包css文件
            test:/\.css$/,
            use:['style-loader','css-loader']
        }]
    }
}
複製代碼

打包樣式scss文件

須要使用sass-loader、node-sasses6

  • 安裝loader實現:npm install sass-loader node-sass -D
  • 在webpack.config.js中添加loader的配置
module.exports = {
    module: {
        rules: [{
            test: /\.scss$/,
            use:['style-loader','css-loader','sass-loader'] 
        }]
    }
};

複製代碼

在配置中,有三個loader,執行順序是從下到上,從右到左。在打包scss文件時,首先執行sass-loader:對sass翻譯成css文件,在掛載到css-loader,最後style-loader.github

爲樣式添加不一樣瀏覽器的前綴

爲了兼容不一樣的瀏覽器,在寫樣式的時候須要加上適用不一樣瀏覽器的前綴,如-o、-webkit、-moz等web

-安裝loader實現:npm install postcss-loader autoprefixer -D -在根目錄建立postcss.config.js

moudle.exports ={
	plugins:[
        require('autoprefixer')
	]
}
複製代碼
  • 在webpack.config.js中添加loader的配置
module.exports = {
    module: {
        rules: [{
            test:/\.scss$/,
            use:[
            'style-loader',
            'css-loader',
            'sass-loader',
            'postcss-loader']
        }]
    }
}
複製代碼

css-loader添加不一樣的配置

css模塊化打包
  • 場景:在文件引入的scss不只影響當前的文件,還影響當前文件引入的其餘js文件,形成樣式衝突
  • 實現:css只在當前模塊類有效,在配置中添加modules:true開啓css的模塊化打包,在引入的時候注意區分
scss文件的嵌套引用
  • 場景:scss文件經過import引入其餘scss文件,致使打包的時候引入的scss文件打包錯誤

  • 實現:importLoader:2

  • 在webpack.config.js中添加loader的配置

module:{
        rules:[{//打包scss文件
            test:/\.scss$/,
            use:[
            'style-loader',
            {
                loader:'css-loader',
                options:{
                    importLoaders:2,//index.scss中經過import引入其餘的scss文件,引入的scss文件在打包的時候也將依次通過全部的loader
                    modules:true 
                }
            },
            'sass-loader',
            'postcss-loader']
        }]
    }
複製代碼

打包字體圖標文件

阿里巴巴矢量圖標庫中,把須要的字體圖標下載到本地,解壓。將iconfont.eot iconfont.svg iconfont.ttf iconfont.woff 四種圖片文件放入到項目中,在src中新建一個放字體圖標的文件夾font。將iconfont.css文件拷貝到項目中,修改對應字體的引用路徑。

  • 須要安裝 file-loader:npm i file-loader -D
  • 在webpack.config.js中添加loader的配置
module.exports = {
    ...
    module: {
        rules: [{
            test: /\.(eot|ttf|svg|woff)$/,
            use:{
                loader:'file-loader'
            }
        },
            ]
        }]
    }
}
複製代碼

打包數據文件

如遇到json、scv、xml文件須要打包時,使用csv-loader 和 xml-loader實現。

  • 安裝:npm install csv-loader xml-loader -D
  • 在webpack.config.js中添加loader的配置
module.exports = {
    module: {
      rules: [{
         test: /\.(csv|tsv)$/,
         use: [
           'csv-loader'
         ]
       },
       {
         test: /\.xml$/,
         use: [
           'xml-loader'
         ]
       }]
    }
  }
複製代碼

plugins

loaders能夠將各個類型的靜態資源打包成webpack能處理的模塊,而plugins有更強大的功能。它能夠從打包優化和壓縮,一直到從新定義環境中的變量。

plugin能夠在webpack運行到某一個時刻,自動完成一些事情。

自動生成html文件,並引入打包生成的js文件到生成的html文件中

  • 安裝使用HtmlWebpackPlugin實現:npm install html-webpack-plugin -D
  • 在webpack.config.js中添加loader的配置

在src中建立一個html的模板,在HtmlWebpackPlugin的配置中引入該模板,打包後生成和模板相似的html文件並引入打包的js文件。

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  entry: 'index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
    plugins: [new HtmlWebpackPlugin({
        template: 'src/index.html' 
    })]
}
複製代碼

自動清除上一次打包的dist文件

先刪除上一次打包的dist文件,再執行打包

  • 安裝使用CleanWebpackPlugin 實現:npm install clean-webpack-plugin -D
  • 在webpack.config.js中添加loader的配置
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const path = require('path');

module.exports = {
  entry: 'index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
    plugins: [
        new HtmlWebpackPlugin({
        template: 'src/index.html' 
    }),
        new CleanWebpackPlugin(['dist']), // 在打包以前,能夠刪除dist文件夾下的全部內容
    ]
}
複製代碼

Entry與Output的基礎配置

  • 需求:打包多個入口文件,對應的在出口的配置中注意命名filename避免出口文件名字衝突----使用佔位符)來確保每一個文件具備惟一的名稱
  • 需求:打包後的文件,做爲後端的接口文件,靜態資源須要上傳到cdn,在output中進行配置,提早加入cdn的地址。在編譯時,若是不知道publicPath的地址,能夠留空,在入口起點文件運行時動態設置。__webpack_public_path__ = myRuntimePublicPath
module.exports = {
	mode: 'development',
	entry: {
		main: './src/index.js',
		sub: './src/index.js'
	},
	plugins: [new HtmlWebpackPlugin({
		template: 'src/index.html'
	}), new CleanWebpackPlugin(['dist'])],
	output: {
		publicPath: 'http://cdn.com.cn', //加入cdn地址
		filename: '[name].js',
		path: path.resolve(__dirname, 'dist')
	}
}
複製代碼

SourceMap的配置

SourceMap是一個映射關係,打包文件和源文件的映射關係,用於開發者的調試。

在devtool中進行設置:devtool: 'source-map'打包速度會下降,在dist裏面會有map映射文件

經常使用設置devtool說明:

  • none:在開發者模式下,默認開啓sourcemap,將其關閉
  • inline前綴:不單獨生成map文件,把對應的map文件以base64的形式直接打包到js文件。
  • cheap前綴:sourcemap和打包後的js同行顯示,並無映射到列忽略源自 loader 的 source。 map,而且僅顯示轉譯後的代碼,因此打包速度相對來講較快。代碼出錯提示不用精確顯示第幾行的第幾個字符出錯,只顯示第幾行出錯,會提升一些性能,
  • moudle前綴:不只映射業務代碼,還會包括loader、第三方模塊的錯誤。
  • eval前綴:打包速度最快。

development環境推薦使用: devtool: 'cheap-module-eval-source-map'

production環境推薦使用: devtool: 'cheap-module-source-map'

使用WebpackDevServer提高開發效率

場景:每次在src裏編寫完代碼都須要手動從新運行 npm run bundle,如何自動解決?

-安裝:npm install webpack-dev-server –D

  • devServer參數說明
  1. contentBase :配置開發服務運行時的文件根目錄
  2. open :自動打開瀏覽器
  3. host:開發服務器監聽的主機地址
  4. compress :開發服務器是否啓動gzip等壓縮
  5. port:開發服務器監聽的端口
  • 在 webpack.config.js 中,加 devServer
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',
	devServer: {
		contentBase: './dist',
		open: true,
		port: 8080
		proxy:{//配置跨域,訪問的域名會被代理到本地的3000端口
      	'/api': 'http://localhost:3000'
    	}
	}
}
複製代碼
  • 在 package.json 中配置
{
  "name": "banggan",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "bundle": "webpack",
    "watch": "webpack --watch",
    "start": "webpack-dev-server",
  },
}
複製代碼

如何實現本身寫一個相似webpackdevserver的工具

  • 在package.json 中配置 建立一個新的指令,npm run server運行本身寫的相似webpackdevserver的工具
{
  "name": "banggan",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "bundle": "webpack",
    "watch": "webpack --watch",
    "start": "webpack-dev-server",
	"server" : "node server.js" 
  },
}
複製代碼
  • 安裝express:npm install express webpack-dev-middleware -D
  • 在根目錄建立一個server.js
  • 在node中直接使用webpack:webpack(config)
const express = require('express'); //引入express
const webpack = require('webpack');//引入webpack
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./webpack.config.js');//引入配置文件

const complier = webpack(config); //編譯一次嗎,打包一次代碼
const app = express();//建立express實例

app.use(webpackDevMiddleware(complier, {}));

app.listen(3000, () => {//監聽3000端口
	console.log('server is running');
});
複製代碼

Hot Module Replacement熱模塊更新

  • 場景:在程序運行中。替換、添加、替換某個模塊,不須要從新加載整個頁面,實現交互時更新。

  • 在 webpack.config.js 中,添加配置

module.exports = {
	mode: 'development',
	devtool: 'cheap-module-eval-source-map',
	devServer: {
		contentBase: './dist',
		open: true,
		port: 8080,
		hot: true,//開啓熱更新功能
		hotOnly: true//若是html功能沒有實現,也不讓瀏覽器刷新
	},
	plugins: [
		new HtmlWebpackPlugin({
			template: 'src/index.html'
		}), 
		new CleanWebpackPlugin(['dist']),
		new webpack.HotModuleReplacementPlugin()//使用熱模塊插件
	],
}
複製代碼
  • 在main.js文件中,使用 HotModuleReplacementPlugin 啓用模塊的熱替換功能。接口暴露在moudle.hot屬性下面
//若是模塊啓用了HMR,就能夠用 module.hot.accept(),監聽模塊的更新。
if (module.hot) {
  module.hot.accept('./library.js', function() {
    // 使用更新過的 library 模塊執行某些操做...
  })
}

複製代碼
//拒絕給定依賴模塊的更新,使用 'decline' 方法強制更新失敗。
module.hot.decline(
  dependencies // 能夠是一個字符串或字符串數組
)
複製代碼

注意:引入css文件時,用框架Vue,React 時,不須要寫 module.hot.accept(),由於在使用css-loader,vue-loader,babel-preset時,均配置好了HMR,不須要本身從新寫 若是文件沒有內置HMR,須要本身手動寫監聽模塊更新代碼

結合Bable處理ES6語法

  • 場景:代碼中含有ES6/ES7的代碼,爲了讓低版本的瀏覽器兼容代碼,須要使用Bable進行轉換
  • 安裝:Bable官網
//preset-env語法轉換
npm install babel-loader @babel/core @babel/preset-env -D
//兼容低版本瀏覽器的語法,函數的補充
npm install --save @babel/polyfill

複製代碼
  • 在 webpack.config.js 中,添加配置
module: {
  rules: [
    {
        test: /\.js$/,
     	exclude: /node_modules/, //排除在外:在node_modules中的js,babel-loader不生效
     	loader: "babel-loader" ,
        options:{
            "presets": [["@babel/preset-env",{
                targets: {
                    edge: "17",
                    firefox: "60",
                    chrome: "67",
                    safari: "11.1",
                  },//運行在大於**版本的瀏覽器上,,已經支持es6的高瀏覽器不須要轉換爲es5
                useBuiltIns:'usage' //按需添加polyfill,把業務代碼中的新語法新函數都轉成低版本瀏覽器兼容的
            }]]
        }
    }
  ]
}

複製代碼
  • 在src目錄下的index.js中頂部位置導入import "@babel/polyfill";

注意若是不是打包業務代碼,而是寫的類庫、或者z組件庫的時候不能使用@babel/polyfill實現,由於會致使聲明的變量變成全局變量,污染全局環境。使用plugin-transform-runtime實現

  • 安裝插件
npm install --save-dev @babel/plugin-transform-runtime
npm install --save @babel/runtime
npm install --save @babel/runtime-corejs2

複製代碼
  • 在 webpack.config.js 中,添加配置
module: {
  rules: [
    {
        test: /\.js$/,
     	exclude: /node_modules/,
     	loader: "babel-loader" ,
        options:{
            "plugins": [["@babel/plugin-transform-runtime",{
                "corejs": 2,
                "helpers": true,
                "regenerator": true,
                "useESModules": false
            }]]
        }
    }
  ]
}

複製代碼

因爲babel配置的內容較多,官網推薦在根目錄下建立屬於babel的配置文件.babelrc文件

因爲babel須要配置的內容很是多,咱們須要在項目根目錄下建立一個 .babelrc 文件。 就不須要在 webpack.config.js 中寫 babel 的配置了。 在 .babelrc 中:

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

總結

  • 在webpack.config.js中
module.exports = {
	mode: 'development', //開發環境進行打包,打包的代碼不會壓縮
	devtool: 'cheap-module-eval-source-map',//不帶列信息,只對業務代碼進行sourcemap的生成 
	entry: {//配置入口文件
		main: './src/index.js'
	},
	devServer: {//配置webpack,開發環境的調試
		contentBase: './dist',//啓動服務器的目錄
		open: true,//打開新的端口號8080
		port: 8080,
		hot: true,//打開熱替換功能
		hotOnly: true
	},
	module: {//對不一樣的文件進行打包規則
		rules: [{ 
			test: /\.js$/, //對js文件的babel-loader打包,其配置在.babelrc文件中
			exclude: /node_modules/, //排除在外:在node_modules中的js,babel-loader不生效
			loader: 'babel-loader',
		}, {
			test: /\.(jpg|png|gif)$/,//對圖片進行打包
			use: {
				loader: 'url-loader',
				options: {
					name: '[name]_[hash].[ext]',
					outputPath: 'images/',
					limit: 10240//小於10240以base64的形式進行打包
				}
			} 
		}, {
			test: /\.(eot|ttf|svg)$/,//字體文件的打包
			use: {
				loader: 'file-loader'
			} 
		}, {
			test: /\.scss$/,//scss文件的打包,先用postcss-loader,在用sass-loader進行解析,最後css-loader進行掛載
			use: [
				'style-loader', 
				{
					loader: 'css-loader',
					options: {
						importLoaders: 2
					}
				},
				'sass-loader',
				'postcss-loader'
			]
		}, {
			test: /\.css$/,//css文件的打包,沒有sass-loader的解析
			use: [
				'style-loader',
				'css-loader',
				'postcss-loader'
			]
		}]
	},
	plugins: [
		new HtmlWebpackPlugin({
			template: 'src/index.html'
		}), 
		new CleanWebpackPlugin(['dist']),//自動清空上一次打包
		new webpack.HotModuleReplacementPlugin()//熱替換插件
	],
	output: {//出口文件
		filename: '[name].js',
		path: path.resolve(__dirname, 'dist')
	}
}
複製代碼
相關文章
相關標籤/搜索