本文使用 Webpack 從零開始搭建一個開發環境的腳手架配置,在此作個記錄,也方便之後使用。css
個人上一篇文章簡單介紹了一下 Webpack 的一些核心概念和基本配置,須要瞭解的朋友能夠先參考一下Webpack 的簡單介紹html
從 webpack v4.0.0 開始,能夠不用引入一個配置文件。直接使用 webpack 命令就可進行打包。可是,通常咱們須要進行更靈活的配置功能,因此本文我也建立一個 webpack 的配置文件,對webpack 的一些屬性進行配置。node
本文 Demo 地址react
首先咱們建立一個目錄,初始化 npm,而後 在本地安裝 webpack,接着安裝 webpack-cli(此工具用於在命令行中運行 webpack):webpack
$ mkdir webpack-dev-demo && cd webpack-dev-demo
$ npm init -y
$ npm install webpack webpack-cli --save-dev
複製代碼
projectgit
webpack-dev-demo
|- package.json
|- /public
|- index.html
|- /src
|- index.js
複製代碼
src/index.jsgithub
function component() {
var element = document.createElement('div');
element.innerHTML = 'Hello World';
return element;
}
document.body.appendChild(component());
複製代碼
public/index.jsweb
<!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>Webpack 開發環境配置</title>
</head>
<body>
</body>
</html>
複製代碼
package.jsonnpm
{
"name": "webpack-dev-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.29.0",
"webpack-cli": "^3.2.1"
}
}
複製代碼
在項目根目錄下建立 webpack.config.js 配置文件編程
project
webpack-dev-demo
|- package.json
|- /public
|- index.html
|- /src
|- index.js
+ |- webpack.config.js
複製代碼
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name]-[hash:8].js',
path: path.resolve(__dirname, 'dist')
}
}
複製代碼
$ npm run build
複製代碼
控制檯的打印結果
能夠看到打印日誌,打包成功了,可是此時在瀏覽器打開咱們的 index.html 文件,卻發現界面上什麼都不顯示,這個也好理解,由於 index.html 此時尚未引入任何的 js 文件。因此這個時候就要將打包後的文件引入到 index.html 文件中,可是能夠看到 dist 文件夾下的 js 文件名有不少的 hash 值,並且每次編譯均可能不一樣,怎麼辦呢?這時候就要引入 html-webpack-plugin 插件了
安裝插件:
$ npm install html-webpack-plugin --save-dev
複製代碼
使用插件:
webpack.config.js
...
const HTMLWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...
plugins: [
new HTMLWebpack({
// 用於生成的HTML文檔的標題
title: 'Webpack 開發環境配置',
// webpack 生成模板的路徑
template: './public/index.html'
})
]
}
複製代碼
關於 html-webpack-plugin 插件更多配置請參考:插件文檔。
再次運行 webpack
$ npm run build
複製代碼
能夠看到 dist 文件夾下生成了一個 index.html 文件,在瀏覽器中打開這個 index.html 文件,能夠看到,'Hello World' 已經可以正常顯示了
至此,項目可以正常打包了,可是還不夠,此時能夠看到 dist 文件夾下有兩個 js 文件,可是明明只打了一個包啊。是由於另外一個包使咱們上一次操做打出來的,並無刪除掉。因此,爲了不 dist 文件夾中的文件變得雜亂,咱們還須要引入 clean-webpack-plugin 插件幫助咱們清理 dist 文件夾
安裝插件:
$ npm install clean-webpack-plugin --save-dev
複製代碼
用法:new CleanWebpackPlugin(paths [, {options}])
webpack.config.js
...
const CleanWebpackPlugin = require('clean-webpack-plugin');
module.exports = {
...
plugins: [
...,
// 用法:new CleanWebpackPlugin(paths [, {options}])
new CleanWebpackPlugin(['dist'])
]
}
複製代碼
再次運行 webpack
$ npm run build
複製代碼
此時 dist 文件夾下只有一個 js 和 html 文件了。說明插件配置成功了,關於 clean-webpack-plugin 更多配置請參考:插件文檔。
安裝 webpack-dev-server 包:
$ npm install --save-dev webpack-dev-server
複製代碼
使用:
webpack.config.js
...
const webpack = require('webpack');
module.exports = {
...
devServer: {
// 必須配置的選項,服務啓動的目錄,默認爲跟目錄
contentBase: './dist',
// 使用熱加載時須要設置爲 true
hot: true,
/** * 下面爲可選配置 */
// 指定使用一個 host。默認是 localhost
host: 'localhost',
// 端口號
port: 8000,
// 當使用 HTML5 History API 時,任意的 404 響應均可能須要被替代爲 index.html。經過設置爲 true 進行啓用
historyApiFallback: {
disableDotRule: true
},
// 出現錯誤時是否在瀏覽器上出現遮罩層提示
overlay: true,
/** * 在 dev-server 的兩種不一樣模式之間切換 * 默認狀況下,應用程序啓用內聯模式 inline * 設置爲 false,使用 iframe 模式,它在通知欄下面使用 <iframe> 標籤,包含了關於構建的消息 */
inline: true,
/** * 統計信息,枚舉類型,可供選項: * "errors-only": 只在發生錯誤時輸出 * "minimal": 只在發生錯誤或有新的編譯時輸出 * "none": 沒有輸出 * "normal": 標準輸出 * "verbose": 所有輸出 */
stats: "errors-only",
// 設置接口請求代理,更多 proxy 配置請參考 https://github.com/chimurai/http-proxy-middleware#options
proxy: {
'/api/': {
changeOrigin: true,
// 目標地址
target: 'http://localhost:3000',
// 重寫路徑
pathRewrite: {
'^/api/': '/'
}
}
}
},
plugins: [
...,
// 添加 NamedModulesPlugin,以便更容易查看要修補(patch)的依賴,因爲設置了 mode: 'development',因此這個插件能夠省略
// new webpack.NamedModulesPlugin(),
// 進行模塊熱替換
new webpack.HotModuleReplacementPlugin()
]
}
複製代碼
啓用熱加載功能:(上面已經添加了) 一、在 devServer 配置中添加
hot: true
屬性 二、在 plugins 中添加new webpack.NamedModulesPlugin()
和new webpack.HotModuleReplacementPlugin()
在 package.json 中添加一個執行命令
package.json
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js",
+ "start": "webpack-dev-server"
}
...
複製代碼
啓動 Http 服務
執行命令:
$ npm run start
複製代碼
能夠看到控制檯打印輸出:
打開瀏覽器,輸入:http://localhost:8000/
,能夠看到瀏覽器中能夠正常顯示 Hello World。
webpack 配置中有一個 mode 屬性的配置,三個可選屬性:
production 會將 process.env.NODE_ENV 的值設爲 production。啓用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 UglifyJsPlugin。
development 會將 process.env.NODE_ENV 的值設爲 development。啓用 NamedChunksPlugin 和 NamedModulesPlugin。
none 不選用任何默認優化選項
這裏咱們配置的是開發環境,因此須要將 mode 設置爲 development
webpack.config.js
...
module.exports = {
+ mode: 'development',
...
}
複製代碼
此時項目可以正常運行,因此沒有什麼問題,可是如今咱們修改一下,將 index.js 中的 return element
改爲錯誤的 return ele
。咱們 F12 打開開發工具,能夠看到控制檯的錯誤提示,點進去卻發現跟咱們寫的代碼不一致,難以對錯誤的代碼進行調試,此時 Source Map 就派上用場了。
在 webpack.config.js 中添加 devtool 屬性
webpack.config.js
...
module.exports = {
mode: 'development',
+ devtool: inline-source-map
...
}
複製代碼
devtool 的多個屬性之間的差別
devtool | 構建速度 | 從新構建速度 | 生產環境 | 品質(quality) |
---|---|---|---|---|
(none) | +++ | +++ | yes | 打包後的代碼 |
eval | +++ | +++ | no | 生成後的代碼 |
cheap-eval-source-map | + | ++ | no | 轉換過的代碼(僅限行) |
cheap-module-eval-source-map | o | ++ | no | 原始源代碼(僅限行) |
eval-source-map | -- | + | no | 原始源代碼 |
cheap-source-map | + | o | yes | 轉換過的代碼(僅限行) |
cheap-module-source-map | o | - | yes | 原始源代碼(僅限行) |
inline-cheap-source-map | + | o | no | 轉換過的代碼(僅限行) |
inline-cheap-module-source-map | o | - | no | 原始源代碼(僅限行) |
source-map | -- | -- | yes | 原始源代碼 |
inline-source-map | -- | -- | no | 原始源代碼 |
hidden-source-map | -- | -- | yes | 原始源代碼 |
nosources-source-map | -- | -- | yes | 無源代碼內容 |
再次運行項目:
$ npm run start
複製代碼
能夠看到報錯依舊,可是在開發工具的控制檯上,查看錯誤提示,能夠根據提示清楚的找到咱們寫的代碼所在位置.
測試以後請將錯誤的
return ele
改成正確的return element
此時,開發環境已經配置的差很少了,可是我如今想給 div 加一個樣式,想讓文字編程藍色,居中顯示,那麼此時就須要用的 loader 了,由於 webpack 默認沒法解析 css,因此就須要咱們本身配置了
安裝所需插件:
$ npm install css-loader style-loader --save-dev
複製代碼
css-loader 用來解析 css 文件,而 style-loader 則用來將解析好的 css 內容注入到 JavaScript 裏。因爲 loader 執行順序是從下到上,因此要將 css-loader 寫在下面。
使用:
webpack.config.js
...
module.exports = {
...
plugins: [...],
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
// 還能夠給 loader 添加一些配置
{
loader: 'css-loader',
options: {
// 開啓 sourceMop
sourceMap: true
}
}
]
}
]
}
}
複製代碼
在 src 目錄下新建一個 css 文件
src/index.css:
div {
color: blue;
text-align: center;
}
複製代碼
src/index.js
require('./index.css');
...
複製代碼
從新運行項目:
$ npm run start
複製代碼
能夠看到瀏覽器上此時文字顏色已經變藍,而且居中顯示。
除了 css 以外,其餘文件在 webpack 也都被認爲是一個模塊,也都須要對應的 loader 進行解析。 下面就不一一演示了,先把代碼貼出來看一看。
下載全部須要的插件:
$ npm install file-loader csv-loader xml-loader html-loader markdown-loader --save-dev
複製代碼
webpack.config.js
...
module.exports = {
...
plugins: [...],
module: {
rules: [
...
// 解析圖片資源
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
// 解析 字體
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
// 解析數據資源
{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},
// 解析數據資源
{
test: /\.xml$/,
use: [
'xml-loader'
]
},
// 解析 MakeDown 文件
{
test: /\.md$/,
use: [
"html-loader",
"markdown-loader"
]
}
]
}
}
複製代碼
這些配置基本上能夠知足常規開發中使用到的各類模塊資源,不過在開發過程當中可能還會須要用到 less、scss 等 css 預編譯語言,還須要使用 less-loader,sass-loader 進行配置。更多配置用法這裏也沒法一一詳述,等你們用到的時候再去查閱對應文檔便可。
目前項目能夠正常運行,可是如今 ES六、7 語法已經出來了,可是瀏覽器中還不能徹底識別,因此咱們須要 babel 講 js 文件轉換成瀏覽器能夠識別的 ES5 語法。
安裝 bable-loader 插件
$ npm install babel-core babel-loader --save-dev
複製代碼
babel 還能進行配置,能夠像上面那樣直接在 loader 中進行配置,也能夠在根目錄下建立 .babelrc 文件配置,項目運行會自動今後文件中讀取
使用 babel-loader:
webpack.config.js
...
module.exports = {
...
plugins: [...],
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader?cacheDirectory',
},
]
}
}
複製代碼
默認值爲 false。當有設置時,指定的目錄將用來緩存 loader 的執行結果。以後的 webpack 構建,將會嘗試讀取緩存,來避免在每次執行時,可能產生的、高性能消耗的 Babel 從新編譯過程。若是設置了一個空值 (loader: 'babel-loader?cacheDirectory'
) 或者 true (loader: babel-loader?cacheDirectory=true)
,loader 將使用默認的緩存目錄 node_modules/.cache/babel-loader
,若是在任何根目錄下都沒有找到 node_modules
目錄,將會降級回退到操做系統默認的臨時文件目錄。
使用 cacheDirectory 選項,將 babel-loader 提速至少兩倍。
presets 屬性告訴 Babel 要轉換的源碼使用了哪些新的語法特性,一個 Presets 對一組新語法特性提供支持,多個 Presets 能夠疊加。 Presets 實際上是一組 Plugins 的集合,每個 Plugin 完成一個新語法的轉換工做。Presets 是按照 ECMAScript 草案來組織的,一般能夠分爲如下三大類:
已經被寫入 ECMAScript 標準裏的特性,因爲以前每一年都有新特性被加入到標準裏,因此又可細分爲:
被社區提出來的但還未被寫入 ECMAScript 標準裏特性,這其中又分爲如下四種:
爲了支持一些特定應用場景下的語法,和 ECMAScript 標準沒有關係,例如 babel-preset-react 是爲了支持 React 開發中的 JSX 語法。
plugins 屬性告訴 Babel 要使用哪些插件,插件能夠控制如何轉換代碼。
安裝項目中須要使用的 Presets 插件
$ npm install babel-preset-env babel-preset-stage-0 --save-dev
複製代碼
安裝項目中須要的 babel Plugin
$ npm install babel-plugin-transform-class-properties babel-plugin-transform-runtime babel-runtime --save-dev
複製代碼
babel-plugin-transform-class-properties 能夠在項目中使用新增的 class 屬性用法
babel-plugin-transform-runtime 因爲 babel 轉換文件時會在每一個文件中都寫入輔助代碼,使用此插件能夠直接使用 babel-runtime 中的代碼進行轉換,避免代碼冗餘。因此 babel-plugin-transform-runtime 和 babel-runtime 成對使用
.babelrc
{
"presets": ["env", "stage-0"],
"plugins": [
"transform-runtime",
"transform-class-properties"
]
}
複製代碼
配置完成,此時就能夠在項目中自由的使用 ES6 等新增 js 語法了。
有時候項目提示錯誤,多是編譯錯誤,多是 ESLint 提示錯誤等等,咱們但願錯誤提示可以友好一些,就可使用這個插件
插件安裝:
npm install friendly-errors-webpack-plugin --save-dev
複製代碼
使用:
webpack.config.js
...
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
module.exports = {
...,
plugins: [
...
new FriendlyErrorsWebpackPlugin()
]
}
複製代碼
更多配置請參考插件文檔
開發的時候咱們常常會須要引入本身寫的文件模塊,可能會須要按照路徑一級一級的找,這個時候咱們就能夠配置 resolve,爲一些經常使用的路徑設置別名
配置:
webpack.config.js
...
module.exports = {
...
plugins: [...],
modules: {...},
resolve: {
alias: {
src: path.resolve(__dirname, 'src')
}
}
}
複製代碼
使用: 不管在任何文件裏,引入 src 目錄下的 index.css 文件時,路徑均可以按照下面的這個引入路徑來寫
index.js
- require('./index.css');
+ import 'src/index.css';
複製代碼
從新運行項目,發現項目正常啓動,index.css 中的樣式也正常生效
至此,一個簡單的開發環境的 Webpack 腳手架搭建完成了。
project
webpack-dev-demo
|- package.json
|- /public
|- index.html
|- /src
|- index.js
|- index.css
複製代碼
public/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>Webpack 開發環境配置</title>
</head>
<body>
</body>
</html>
複製代碼
src/index.js
import 'src/index.css';
function component() {
var element = document.createElement('div');
element.innerHTML = 'Hello World';
return element;
}
document.body.appendChild(component());
複製代碼
src/index.css
div {
color: blue;
text-align: center;
}
複製代碼
.babelrc
{
"presets": ["env", "stage-0"],
"plugins": [
"transform-runtime",
"transform-class-properties"
]
}
複製代碼
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: 'development',
devtool: 'inline-source-map',
entry: './src/index.js',
output: {
filename: '[name]-[hash:8].js',
path: path.resolve(__dirname, 'dist')
},
devServer: {
// 必須配置的選項,服務啓動的目錄,默認爲跟目錄
contentBase: './dist',
// 使用熱加載時須要設置爲 true
hot: true,
/** * 下面爲可選配置 */
// 指定使用一個 host。默認是 localhost
host: 'localhost',
// 端口號
port: 8000,
// 當使用 HTML5 History API 時,任意的 404 響應均可能須要被替代爲 index.html。經過設置爲 true 進行啓用
historyApiFallback: {
disableDotRule: true
},
// 出現錯誤時是否在瀏覽器上出現遮罩層提示
overlay: true,
/** * 在 dev-server 的兩種不一樣模式之間切換 * 默認狀況下,應用程序啓用內聯模式 inline * 設置爲 false,使用 iframe 模式,它在通知欄下面使用 <iframe> 標籤,包含了關於構建的消息 */
inline: true,
/** * 統計信息,枚舉類型,可供選項: * "errors-only": 只在發生錯誤時輸出 * "minimal": 只在發生錯誤或有新的編譯時輸出 * "none": 沒有輸出 * "normal": 標準輸出 * "verbose": 所有輸出 */
stats: "errors-only",
// 設置接口請求代理,更多 proxy 配置請參考 https://github.com/chimurai/http-proxy-middleware#options
proxy: {
'/api/': {
changeOrigin: true,
// 目標地址
target: 'http://localhost:3000',
// 重寫路徑
pathRewrite: {
'^/api/': '/'
}
}
}
},
plugins: [
new HTMLWebpackPlugin({
// 用於生成的HTML文檔的標題
title: 'Webpack 開發環境配置',
// webpack 生成模板的路徑
template: './public/index.html'
}),
// 用法:new CleanWebpackPlugin(paths [, {options}])
new CleanWebpackPlugin(['dist']),
// 添加 NamedModulesPlugin,以便更容易查看要修補(patch)的依賴,因爲設置了 mode: 'development',因此這個插件能夠省略
// new webpack.NamedModulesPlugin(),
// 進行模塊熱替換
new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [
{
test: /\.js/,
include: path.resolve(__dirname, 'src'),
loader: 'babel-loader?cacheDirectory'
},
// 解析 css
{
test: /\.css$/,
include: path.resolve(__dirname, 'src'),
use: [
'style-loader',
// 還能夠給 loader 添加一些配置
{
loader: 'css-loader',
options: {
// 開啓 sourceMop
sourceMap: true
}
}
]
},
// 解析圖片資源
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
// 解析 字體
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
},
// 解析數據資源
{
test: /\.(csv|tsv)$/,
use: [
'csv-loader'
]
},
// 解析數據資源
{
test: /\.xml$/,
use: [
'xml-loader'
]
},
// 解析 MakeDown 文件
{
test: /\.md$/,
use: [
"html-loader",
"markdown-loader"
]
}
]
},
resolve: {
alias: {
src: path.resolve(__dirname, 'src')
}
}
}
複製代碼
package.json
{
"name": "webpack-dev-demo",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js",
"start": "webpack-dev-server"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-0": "^6.24.1",
"babel-runtime": "^6.26.0",
"clean-webpack-plugin": "^1.0.1",
"css-loader": "^2.1.0",
"html-webpack-plugin": "^3.2.0",
"style-loader": "^0.23.1",
"webpack": "^4.29.0",
"webpack-cli": "^3.2.1",
"webpack-dev-server": "^3.1.14"
}
}
複製代碼
本文篇幅較長,感謝各位的耐心閱讀。本文主要從 入口、輸出、插件(Plugins)、模塊處理(Module)、loader、解析(resolve)等 6 個配置項着手配置了一個基本的 webpack 開發環境腳手架。本文主要講解的內容爲:
loader 的做用以及如何配置使用
babel 的做用以及配置項
各個插件的功能以及適用場景
解析可以爲開發帶來的效率
本文內容對於已經熟練掌握 Webpack 的朋友來講,可能有些淺薄,可是着重講解了各個配置項的功能以及配置後對項目產生的效果。對於準備入門 webpack 的朋友應該會有必定的幫助。