自從工做以後,就已經好久沒有寫過博客了。時間被分割得比較碎,積累了一段時間的學習成果,才寫下了這篇博客。css
以前有寫過 Webpack4 的文章,可是都比較偏入門,惟一的一篇實戰篇 —— 基於Webpack搭建React開發環境,也是比較簡單的,沒有涉及到 CSS 抽取,第三方庫打包等功能,這篇文章相對而言比較深刻。但因爲做者水平有限,不免存在謬誤之處,歡迎你們指正。html
還有沒入門的童鞋能夠參考我以前的文章:vue
在命令行中敲入以下命令:node
mkdir Webpack-Vue && cd Webpack-Vue && npm init -y
複製代碼
而後你就能夠在你的當前路徑下看到一個叫 Webpack-Vue
的文件夾,裏面有一個包含默認信息的 package.json
文件,打開並修改這個文件的一些內容。react
而後咱們在項目文件夾中建立如下幾個文件夾:webpack
Linux 下能夠輸入一下命令進行快速建立:git
mkdir src src/components dist build -p
複製代碼
其中,dist 用於存放 Webpack 打包後的項目文件、src 用於存放你的源代碼文件、build 用於存放 Webpack 打包相關的配置文件。github
在 src 下,建立入口文件 index.js
。web
Linux 下建立的命令:vue-router
touch ./src/index.js
複製代碼
在根目錄下建立 index.html
文件,內容以下:
<!DOCTYPE html>
<html>
<head>
<title>Webpack Vue Demo</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="app"></div>
</body>
</html>
複製代碼
這將用於做爲咱們應用的模板,打包的 js 文件會在 Webpack 插件的處理下插入到這個文件中。
其餘配置性文件根據你本身的喜愛來添加了,好比 .gitignore
文件等。
要使用 Webpack
,第一步固然是先安裝。使用如下命令進行安裝:
npm i webpack webpack-cli -D
複製代碼
而後你就能夠看到你的項目文件夾中多了一個 node_modules
文件夾,而後 package.json
文件中多了一個 devDependencies
屬性。裏面包含了安裝的依賴名稱和依賴版本,如今暫時還只有 webpack
和 webpack-cli
。
這一節咱們將着手配置一個具備最基本打包功能的項目,從 src/index.js
開始對項目進行打包。
爲了項目結構更加科學合理,咱們把全部的 Webpack 配置相關的文件都存放在了 build
目錄中。
進入 build
文件夾,而後建立如下幾個文件:
在 Linux 中,能夠敲入以下命令快速建立:
cd build/ && touch webpack.base.conf.js webpack.dev.conf.js webpack.prod.conf.js build.js
複製代碼
其中,webpack.base.conf.js
是最基礎的打包配置,是開發環境和生產環境都要用到的配置。webpack.dev.conf.js
就是在開發環境要使用的配置。webpack.prod.conf.js
就是在生產環境要使用的配置了。build.js
是經過 Node 接口進行打包的腳本。
接下來咱們在對應的文件中寫入最基本的配置信息。
先寫最基本的配置信息:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
bundle: path.resolve(__dirname, '../src/index.js')
},
output: {
path: path.resolve(__dirname, '../dist'),
filename: '[name].[hash].js'
},
module: {
rules: [
]
},
plugins: [
new HtmlWebpackPlugin({
template: path.resolve(__dirname, '../index.html')
})
]
};
複製代碼
一樣寫入最基本的配置信息:
const merge = require('webpack-merge');
const path = require('path');
const baseConfig = require('./webpack.base.conf');
module.exports = merge(baseConfig, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
contentBase: path.resolve(__dirname, '../dist'),
open: true
}
});
複製代碼
下面的配置中,CleanWebpackPlugin 是 1.x.x 版本的寫法,在升級到 2.x.x 版本之後,構造函數的寫法發生了變化,詳情請參考:clean-webpack-plugin,感謝掘友 @厚厚厚厚厚 的提醒!
繼續寫入最基礎的配置:
const merge = require('webpack-merge');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const path = require('path');
const baseConfig = require('./webpack.base.conf');
module.exports = merge(baseConfig, {
mode: 'production',
devtool: 'source-map',
module: {
rules: []
},
plugins: [
new CleanWebpackPlugin(['dist/'], {
root: path.resolve(__dirname, '../'),
verbose: true,
dry: false
})
]
});
複製代碼
注意到咱們上面引用了兩個新的依賴,須要先進行安裝才能使用:
cnpm i webpack-merge clean-webpack-plugin webpack-dev-server html-webpack-plugin -D
複製代碼
這個腳本用於構建生產環境,開發環境基於 webpack-dev-server
搭建,不寫腳本。
接下來,寫入咱們的打包腳本,經過 Node 調用 Webpack 進行打包。
const webpack = require('webpack');
const config = require('./webpack.prod.conf');
webpack(config, (err, stats) => {
if (err || stats.hasErrors()) {
// 在這裏處理錯誤
console.error(err);
return;
}
// 處理完成
console.log(stats.toString({
chunks: false, // 使構建過程更靜默無輸出
colors: true // 在控制檯展現顏色
}));
});
複製代碼
這樣作的好處是能夠利用 Node 作一些其餘的事情,另外當 Webpack 配置文件不在項目文件夾根部時方便調用。
配置 npm scripts 可以使咱們更方便的使用打包命令。
在 package.json
文件的 scripts
屬性中,寫入以下兩條:
"build": "node build/build.js",
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js"
複製代碼
基本的配置寫完了,咱們測試一下打包效果,在 src/index.js
中寫入以下代碼:
console.log('index.js!');
複製代碼
而後在命令行中輸入:
npm run dev
複製代碼
在自動打開的網頁中,我打開控制檯,咱們能夠看到輸出了一句「index.js」,符合預期。
而後輸入構建命令進行構建:
npm run build
複製代碼
截圖以下:
這就表示打包成功了,可是咱們還只完成了最基本的打包功能,Vue 還不能使用,接下來咱們將這個項目變得更增強大。
爲了方便開發,咱們須要引入一些 Loader,以簡化咱們的寫法以及使咱們的代碼兼容更多的環境。
這一部分能夠根據 Webpack 的文檔來寫,由於都是一些基本的東西,配置起來也不難。
爲了使咱們的 JavaScript 代碼兼容更多環境,咱們須要使用 babel-loader。
配置方法:
首先安裝 babel-loader
、babel-preset-env
和 babel-core
。須要注意的是,若是你的 babel-loader
是 7.x 版本的話,你的 babel-core
必須是 6.x 版本;若是你的 babel-loader
是 8.x 版本的話,你的 babel-core
必須是 7.x 版本。若是不這樣的話,Webpack 會報錯。
安裝命令以下:
npm i babel-loader@7 babel-core babel-preset-env -D
複製代碼
而後在 webpack.base.conf.js
的 module.rules
中新增以下對象:
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
}
複製代碼
咱們還須要添加一個配置文件(.babelrc)在根目錄下:
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}]
]
}
複製代碼
這就是 babel-preset-env 的做用,幫助咱們配置 babel。咱們只須要告訴它咱們要兼容的狀況(目標運行環境),它就會自動把代碼轉換爲兼容對應環境的代碼。
以上代碼表示咱們要求代碼兼容最新兩個版本的瀏覽器,不用兼容 IE 8,另外市場份額超過 1% 的瀏覽器也必須支持。
只須要告訴 babel-preset-env 你想要兼容的環境,它就會自動轉換,是否是很爽?不再用配置那麼多了。
接下來咱們試一試,把 src/index.js
中的代碼改寫爲:
const x = 'index.js';
const y = (x) => {
console.log(x);
}
y(x);
複製代碼
而後使用 npm run build
進行打包,打包以後的代碼中:
var x = 'index.js';
var y = function y(x) {
console.log(x);
};
y(x);
複製代碼
說明咱們的代碼已經被成功地轉換了。
爲了作一個對比,未配置 babel
時,轉換結果以下:
const x = 'index.js';
const y = (x) => {
console.log(x);
}
y(x);
複製代碼
這個用於將字體文件、圖片文件進行模塊化。
首先安裝 file-loader
:
npm i file-loader -D
複製代碼
而後在 webpack.base.conf.js
中添加以下配置到 module.rules
:
{
test: /\.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
use: [
'file-loader'
]
}
複製代碼
固然能夠簡化配置信息,把兩個 test 正則合併到一處。
接下來咱們配置 vue-loader
。
爲了使用 Vue 單文件組件,咱們須要對 .vue
文件進行處理,使用 vue-loader
。
首先安裝 vue-loader
、css-loader
、vue-style-loader
和 vue-template-compiler
,後者也是必不可少的,少了會報錯。
命令:
npm i vue-loader css-loader vue-style-loader vue-template-compiler -D
複製代碼
而後咱們配置 webpack.base.conf.js
,寫入如下代碼到該文件的 module.rules
屬性當中:
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader']
}
複製代碼
只有這一處配置是不行的,根據 vue-loader 官網的說明,咱們還須要配置一個插件,而後還須要配置 resolve.alias
別名,否則 Webpack 無法找到 Vue 模塊。
配置插件,首先在文件頭部引入:
const VueLoaderPlugin = require('vue-loader/lib/plugin');
複製代碼
而後在 plugins
數組中添加這個插件對象:
new VueLoaderPlugin(),
複製代碼
隨後咱們還要配置別名,將 resolve.alias
配置爲以下對象:
{
'vue$': 'vue/dist/vue.esm.js',
'@': path.resolve(__dirname, '../src'),
}
複製代碼
這可使得 Webpack 很方便的找到 Vue,咱們在 JavaScript 文件中引入依賴的時候,也能夠方便地使用 @
來代替 src
,省去了寫文件路徑的麻煩。
咱們順便添加一個 resolve.extensions
屬性,方便咱們引入依賴或者文件的時候能夠省略後綴:
extensions: ['*', '.js', '.json', '.vue'],
複製代碼
extensions 屬性是一個數組。這樣配置以後,咱們在 JavaScript 文件中 import JavaScript 文件、json 文件和 Vue 單文件組件均可以省略後綴。
以上幾步都很重要,最好不要省略。
接下來咱們驗證一下 Vue 單文件組件是否可用。
安裝 Vue:
npm i vue --save
複製代碼
而後修改 index.js
文件內容以下:
import Vue from 'vue';
import App from './App';
new Vue({
el: '#app',
template: '<App/>',
components: { App }
});
複製代碼
而後在同級目錄下建立一個 App.vue
文件,內容以下:
<template>
<h1>Hello World!</h1>
</template>
<script>
export default {
name: 'App'
}
</script>
<style>
html, body {
padding: 0;
margin: 0;
box-sizing: border-box;
font-size: 16px;
}
</style>
複製代碼
運行命令 npm run dev
就能夠看到一個大大的一級標題 —— Hello World 啦!
到這裏,咱們的項目已經可使用 Vue 單文件組件進行開發了,可是尚未完,咱們還有一些任務要作。
這裏咱們使用 postcss 的 autoprefixer 插件爲咱們的 css 代碼自動添加前綴以適應不一樣的瀏覽器。
首先安裝依賴:
npm i postcss-loader autoprefixer -D
複製代碼
而後修改 module.rules
中的 css 配置項,修改以後以下:
{
test: /\.css$/,
use: ['vue-style-loader', 'css-loader', 'postcss-loader']
}
複製代碼
而後在咱們項目的根目錄下新增配置文件 postcss.config.js
,內容以下:
module.exports = {
plugins: [
require('autoprefixer')
]
}
複製代碼
表明咱們將要使用 autoprefixer 插件。
以後咱們就能夠愉快地寫代碼了,能夠本身驗證一下是否自動添加了前綴,這裏再也不贅述。
Webpack 4 開啓熱更新相對容易,具體步驟以下:
修改 webpack.dev.conf.js
,在 devServer
屬性中設置 hot
的值爲 true
,這就表明開啓了熱更新。可是隻這樣作還不夠,須要咱們添加一個插件,繼續修改 webpack.dev.conf.js
。
設置其 plugins 屬性以下:
const webpack = require('webpack');
// 在文件頭部引入 webpack 依賴
[
new webpack.HotModuleReplacementPlugin()
]
複製代碼
這就開啓了 css 熱更新(由於 vue-style-loader 封裝了 style-loader,熱更新開箱即用),可是 JavaScript 熱更新還不能用,每次修改代碼咱們都會刷新瀏覽器,因此咱們須要繼續配置。
爲了使得 JavaScript 模塊也能進行 HMR,咱們須要在咱們的 入口文件(index.js) 的底部添加以下代碼:
if (module.hot) {
module.hot.accept();
}
複製代碼
接下來就能夠進行 HMR 了。
每次咱們對項目進行打包時,咱們都會把引用的第三方依賴給打包一遍,好比 Vue、Vue-Router、React 等等。可是這些庫的代碼基本都是不會變更的,咱們不必每次打包都構建一次,因此咱們最好將這些第三方庫提取出來單獨打包,這樣有利於減小打包時間。
官方插件是 DllPlugin,可是這個插件配置比較繁瑣。網上有人推薦一個比較好用的插件 —— autodll-webpack-plugin
,確實很好用。
下面是它的配置方法:
首先安裝:
npm i autodll-webpack-plugin -D
複製代碼
而後在 webpack.base.conf.js
中引入:
const AutoDllPlugin = require('autodll-webpack-plugin');
複製代碼
而後在 plugins 屬性中添加這個插件:
new AutoDllPlugin({
inject: true, // will inject the DLL bundle to index.html
debug: true,
filename: '[name]_[hash].js',
path: './dll',
entry: {
vendor: ['vue', 'vue-router', 'vuex']
}
})
複製代碼
inject
爲 true,插件會自動把打包出來的第三方庫文件插入到 HTML。filename
是打包後文件的名稱。path
是打包後的路徑。entry
是入口,vendor
是你指定的名稱,數組內容就是要打包的第三方庫的名稱,不要寫全路徑,Webpack 會自動去 node_modules
中找到的。
每次打包,這個插件都會檢查註冊在 entry 中的第三方庫是否發生了變化,若是沒有變化,插件就會使用緩存中的打包文件,減小了打包的時間,這時 Hash 也不會變化。
使用 splitChucksPlugin 插件,這是 Webpack 自帶的,不用安裝第三方依賴。
使用方法:
在 webpack.base.conf.js
的 plugins 屬性中添加以下插件對象;
new webpack.optimize.SplitChunksPlugin()
複製代碼
這表明你將使用默認的提取配置來提取你的公共代碼,若是你不想使用默認配置,請給插件構造函數傳入配置對象.
具體怎麼配置,請參考冷星大神的博客 —— webpack4——SplitChunksPlugin使用指南,裏面關於配置項的做用介紹得很清楚很詳細。
我我的比較喜歡 stylus,由於寫起來比較無拘無束,相似 Python,沒那麼多條條框框,並且用起來也不是很複雜。
引入方法:
首先下載 stylus 和 stylus-loader 依賴:
npm i stylus stylus-loader -D
複製代碼
而後在配置項 module.rules
中添加一個處理 stylus 文件的配置對象。
配置信息以下:
{
test: /\.styl(us)$/,
use: ['vue-style-loader', 'css-loader', 'stylus-loader', 'postcss-loader']
}
複製代碼
接下來只要你在 Vue 單文件組件的 style 標籤加上 lang='stylus'
,你就可使用 stylus 來寫 CSS 了。
這個功能的配置方法在 Vue Loader 官網交代得很清楚了。
使用的是 mini-css-extract-plugin
插件,首先安裝:
npm i mini-css-extract-plugin -D
複製代碼
而後在配置文件頭部引入:
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
複製代碼
而後當你要抽取 CSS 的時候(好比生產環境打包),你就把原來配置文件中的全部 vue-style-loader
替換爲 MiniCssExtractPlugin.loader,其餘的什麼 css-loader
、stylus-loader
等等都不要動。
最後,修改 plugins 選項,插入以下插件:
new MiniCssExtractPlugin({
filename: "[name].css",
chunkFilename: "[id].css"
})
複製代碼
打包以後,你會發現全部的 CSS 代碼都被抽取到了一個單獨的 CSS 文件當中。
示例代碼放在個人 GitHub 倉庫,須要的同窗能夠自取。
若有錯誤,敬請指出!