webpack4+vue打包簡單入門
前言
最近在研究使用webpack的使用,在查閱了數篇文章後,學習了webpack的基礎打包流程.
本來就可以一刪了之了,但是覺得未免有點可惜,所以就有了這篇文章,供大家參考.
webpack打包的教程具有時效性,有不少作者在一直維護一篇文章.超過一定時間參考價值就會下降,希望各位瞭解這一點.
使用的依賴一覽
"devDependencies": { "clean-webpack-plugin": "^0.1.19", "css-loader": "^1.0.0", "html-webpack-plugin": "^3.2.0", "mini-css-extract-plugin": "^0.4.3", "vue-loader": "^15.4.2", "vue-style-loader": "^4.1.2", "vue-template-compiler": "^2.5.17", "webpack": "^4.19.1", "webpack-cli": "^3.1.0", "webpack-merge": "^4.1.4" }, "dependencies": { "vue": "^2.5.17" }
都是目前最新的穩定版本.
講解到的內容
- webpack的安裝
- webpack簡單配置
- vue的安裝以及使用
- 使用外掛
- 提取css
- 使用多個配置檔案
- 簡單的程式碼分離
構建目錄
我們建立一個新的目錄(learnwebpack).在下面建立src資料夾和一個index.html.
然後我們使用npm init
命令初始化一個package.json
檔案,直接一路回車過去就好了.
目錄結構:
-
learnwebpack
- src
- index.html
- package.json
src儲存我們未經編譯的原始碼.
index.html內容如下:
<!DOCTYPE html> <html> <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>Webpack4 && Vue</title> </head> <body> <div id="root"> </div> <script src="./dist/app.bundle.js"></script> </body> </html>
安裝webpack
在webpack4中不僅僅需要安裝webpack本身,還需要安裝webpack-cli.
npm install webpack webpack-cli --save-dev
使用--save-dev
引數會將這兩個包新增到package.json的開發依賴中.
webpack4的特性
在webpack4中可以實現0配置打包,也就是說不需要任何設定就可以完成簡單的打包操作.
此外最實用的特性之一就是mode
配置選項用於告訴webpack輸出模式是開發還是產品,提供了已經預先定義好的常用配置.
後面的例子中我們會使用到mode
.
webpack的簡單配置
我們在當前目錄下新建一個webpack的配置檔案webpack.config.js.
此時的目錄結構為:
-
learnwebpack
- src
- index.html
- webpack.config.js
- package.json
webpack.config.js的內容:
const {join:pathJoin} = require('path');
module.exports = {
entry:{
app:'./src/index.js' // 入口檔案的位置
},
output:{
filename:'[name].bundle.js', // 輸出檔名字的格式
path:pathJoin(__dirname,'./dist') // 輸出的絕對路徑
},
}
為什麼會有filename上的格式
對於我們當前的應用來說好像沒有任何用途,打包後文件名完全可以和入口一樣.
使用不同的檔名可以解決相同檔名稱導致的瀏覽器快取問題.
對於我們當前這個格式來說,打包後的檔名稱為app.bundle.js
.
此外還有一些額外的模板符號:
- [hash] 模組識別符號(module identifier)的 hash
- [chunkhash] chunk 內容的 hash
- [name] 模組名稱
- [id] 模組識別符號(module identifier)
- [query] 模組的 query,例如,檔名 ? 後面的字串
現在讓我們在src資料夾下新建一個index.js,只寫一行簡單的程式碼用於測試:
console.log('hello world!');
此時的目錄結構為:
-
learnwebpack
-
src
- index.js
- index.html
- webpack.config.js
- package.json
-
為什麼使用index.js
webpack4中零配置的預設入口位置就是當前配置路徑下的./src/index.js
,也就是說如果不指定入口的情況下也是可以打包的.
構建我們的專案
執行:
./node_modules/.bin/webpack-cli --config webpack.config.js
上方的命令有點冗長,我們使用package.json中scripts
欄位來定義一個快捷方式.
package.json:
{
+++
"scripts": {
"build": "webpack --config webpack.config.js"
}
+++
}
+++ 表示package.json
的其他欄位,這部分目前不用關心.
現在執行這條簡化的命令,效果和上方一樣.
npm run build
對於npm版本高於5.2.0的同學可以嘗試使用npm的附帶模組npx
.
npx可以直接執行./node_modules/.bin/
下的內容而不必輸入路徑.
npx webpack --config webpack.config.js
如果操作正確你的目錄下應該會多出一個dist資料夾,內部有一個app.bundle.js檔案.
此時的目錄結構:
-
learnwebpack
-
dist
- app.bundle.js
-
src
- index.js
- index.html
- webpack.config.js
- package.json
-
我們可以執行index.html了,如果一切順利.那麼在控制檯中應該會出現hello world!
.
錯誤
當我們打包的時候webpack4會報錯,原因就是我們沒有指定上文中提到的mode
屬性,webpack預設將他設定為production
.
此時如果你檢視程式碼,就會發現程式碼是經過壓縮的,這是webpack4的預設行為之一.
WARNING in configuration
The 'mode' option has not been set, webpack will fallback to 'production' for this value. Set 'mode' option to 'development' or 'production' to enable defaults for each environment.
You can also set it to 'none' to disable any default behavior. Learn more: https://webpack.js.org/concepts/mode/
安裝Vue
使用npm安裝:
npm -i vue
vue是執行時依賴,webpack需要合適loader將vue檔案解釋為webpack可以理解的格式用於構建,所以我們需要vue-loader
來轉換vue單檔案.
使用npm安裝vue-loader:
npm install vue-loader --save-dev
vue單檔案中分為三個部分,其中template
部分需要專用的外掛進行轉換.
安裝vue-template-compiler
:
npm install vue-template-compiler --save-dev
這個使用vue-loader自動呼叫的外掛,也是官方預設的,不需要任何配置.
如果你不使用他,打包的時候會報錯.
簡單來說他的功能是將template
部分的模板轉為render
函式.
然後我們需要處理css,vue-loader需要css-loader才可以執行.
安裝css-loader:
npm install css-loader --save-dev
css-loader的作用僅僅是將css轉為webpack可以解釋的型別,如果我們需要將樣式使用起來插入到html中,必須使用額外的外掛.
安裝vue-style-loader
:
npm install vue-style-loader --save-dev
vue-style-loader
是由vue官方維護的你也可以使用其他的loader來處理css,他除了提供了常見的插入樣式到html的功能以外還提供了其他的功能,例如熱更新樣式等.
修改配置
webpack.config.js:
const {join:pathJoin} = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin'); // +++
module.exports = {
mode:'production',
entry:{
app:'./src/index.js'
},
output:{
filename:'[name].bundle.js',
path:pathJoin(__dirname,'./dist')
},
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader'] // +++
},
{
test: /\.css$/,
use: ['vue-style-loader','css-loader'] // +++
}
]
},
plugins:[
new VueLoaderPlugin() // +++
]
}
需要注意的是VueLoaderPlugin
在vue-loader
v15的版本中,這個外掛是必須啟用的.
修改目錄結構以及檔案
我們在src
下建立一個pages
資料夾並且在內部建立一個app.vue
檔案.
此時的目錄結構:
-
learnwebpack
-
dist
- app.bundle.js
-
src
-
pages
- app.vue
- index.js
-
- index.html
- webpack.config.js
- package.json
-
app.vue檔案內容:
<style>
.test > p {
background-color: aqua;
}
</style>
<template>
<article class="test">
<p>{{vue}}</p>
</article>
</template>
<script>
export default {
data() {
return {
vue: "vue"
};
}
};
</script>
app.vue是由index.js引用的,回過頭來我們修改index.js:
import Vue from 'vue'
import App from './pages/app.vue';
new Vue({
el:'#root',
render:h=>h(App)
})
激動人心的時候到了,如果一切順利你的vue應用應該打包完成了,執行index.html就可以檢視這個結果了.
使用外掛
回顧前面的一個話題,指定輸出檔名字格式.如果你將我們的內容放到一個伺服器上執行,你會很容易發現一個問題.
就是快取,web上的資源瀏覽器會對其進行快取,但是對開發的時候,或者產品中的程式碼更新十分不友好,新的程式碼不會得到及時的使用.
我們讓資源每次的名字不一樣就可以簡單的解決這個問題,接下來修改我們的webpack.config.js
:
const {join:pathJoin} = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
+++
output:{
filename:'[name].[hash].js',
path:pathJoin(__dirname,'./dist')
},
+++
}
此時再次修改程式碼打包每次的名字都不會一樣.
但是出現了另外一個問題由於資源名字每次都不一致,每次我都需要手動指定index.html中引用的資源,這個時候該html-webpack-plugin
出場了.
這個外掛可以自動生成一個html檔案,並且將資原始檔按照正確順序插入到html中.
安裝html-webpack-plugin
:
npm install html-webpack-plugin --save-dev
webpack.config.js新增配置
const {join:pathJoin} = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:{
app:'./src/index.js'
},
output:{
filename:'[name].[hash]].js',
path:pathJoin(__dirname,'./dist')
},
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader']
},
{
test: /\.css$/,
use: ['vue-style-loader','css-loader']
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'template.html',// 指定模板html檔案
filename:'index.html'// 輸出的html檔名稱
}),
new VueLoaderPlugin()
]
}
然後我們把index.html
重新命名為template.html
,將template.html
中的script
標籤刪除.
指定模板後HtmlWebpackPlugin會保留原來html中的內容,而且會在新生成的html檔案中把script插入到合適的位置.
HtmlWebpackPlugin補充
注意:HtmlWebpackPlugin
必須位於外掛陣列的首位.
執行專案
你會發現在dist目錄下會有index.html檔案,而我們打包後的index.js的輸出檔名也變成了app.XXXXXXXXXXXXXXXX.js
的格式,更加神奇的是在index.html中自動插入了script標籤來引用打包後的js檔案.
使用CleanWebpackPlugin外掛
當你多次打包的時候,你會發現由於使用hash來命名輸出的檔案每次的檔名稱都不一樣,導致檔案越來越多.
使用CleanWebpackPlugin可以每次構建前清空輸出目錄.
安裝:
npm install clean-webpack-plugin --save-dev
webpack.config.js:
const {join:pathJoin} = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require("clean-webpack-plugin");// +++
module.exports = {
entry:{
app:'./src/index.js'
},
output:{
filename:'[name].[hash]].js',
path:pathJoin(__dirname,'./dist')
},
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader']
},
{
test: /\.css$/,
use: ['vue-style-loader','css-loader']
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'template.html',
filename:'index.html'
}),
new VueLoaderPlugin(),
new CleanWebpackPlugin(['dist'])// +++ 執行前刪除dist目錄
]
}
現在無論執行幾次構建dist目錄下都是乾淨的.
提取css
現在我們來將vue單檔案中的css提取出來到一個單獨的css檔案中.
webpack4中完成這個功能的外掛是mini-css-extract-plugin
.
安裝:
npm install mini-css-extract-plugin --save-dev
webpack.config.js:
const {join:pathJoin} = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require("clean-webpack-plugin");
const MiniCssExtractPlugin = require('mini-css-extract-plugin');// +++
module.exports = {
entry:{
app:'./src/index.js'
},
output:{
filename:'[name].[hash]].js',
path:pathJoin(__dirname,'./dist')
},
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader']
},
{
test: /\.css$/,
use: [
{
loader:MiniCssExtractPlugin.loader // +++
},
'css-loader'
]
}
]
},
plugins:[
new HtmlWebpackPlugin({
template:'template.html',
filename:'index.html'
}),
new MiniCssExtractPlugin({
filename:'style.css' // 指定輸出的css檔名.
}),
new VueLoaderPlugin(),
new CleanWebpackPlugin(['dist'])
]
}
執行我們的專案,現在dist目錄下已經有一個style.css的檔案了,而且htmlwebpackplugin自動將style.css插入到了index.html中.
使用多個配置
考慮以下問題:
- 開發的時候我們希望打包速度快,有原始碼提示,有熱過載(本篇文章沒有涉獵)...
- 釋出產品我們希望輸出的內容不會被快取,有程式碼壓縮...
是時候使用多個配置了,一個用於開發一個用於生產.
但是我們還可以做的更好使用webpack-merge
外掛,可以將多個配置合成.
安裝webpack-merge
:
npm install webpack-merge --save-dev
首先我們定義一個包含基本資訊的webpack配置檔案,這個檔案被其他配置檔案依賴.
webpack.common.js:
const {join:pathJoin} = require('path');
const CleanWebpackPlugin = require("clean-webpack-plugin");
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:{
app:'./src/index.js'
},
output:{
filename:'[name].[hash].js',
path:pathJoin(__dirname,'./dist')
},
plugins:[
new HtmlWebpackPlugin({
template:'template.html',
filename:'index.html'
}),
new VueLoaderPlugin(),
new CleanWebpackPlugin(['dist'])
]
}
可以看到這裡定義的都是最基本的資訊.
然後我們定義開發配置並且在內容使用webpack-merge
外掛和webpack.config.js進行合併.
webpack.dev.js:
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development', // 不壓縮程式碼,加快編譯速度
devtool: 'source-map', // 提供原始碼對映檔案除錯使用
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader']
},
{
test: /\.css$/,
use: ['vue-style-loader','css-loader'] // 使用vue-style-loader直接插入到style標籤中
}
]
},
})
定義生產配置.
webpack.prod.js:
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = merge(common,{
mode:'production', // 壓縮程式碼
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader']
},
{
test: /\.css$/,
use: [
{
loader:MiniCssExtractPlugin.loader // 提取css到外部檔案中
},
'css-loader'
]
}
]
},
plugins:[
new MiniCssExtractPlugin({
filename:'style.css'
})
]
})
最後修改一下我們package.json中的快捷方式:
+++
"scripts": {
"dev": "webpack --config webpack.dev.js",
"build": "webpack --config webpack.prod.js"
},
+++
刪除之前的webpack.config.js.
完成後的目錄結構:
-
learnwebpack
- dist
-
src
-
pages
- app.vue
- index.js
-
- package.json
- template.html
- webpack.common.js
- webpack.dev.js
- webpack.prod.js
嘗試執行npm run dev
和npm run build
吧.
簡單的程式碼分離
我們在index.js中引用了Vue,如果你檢視打包後的程式碼你會發現vue本身會被打包到同一個檔案中,如果我們有另外一個檔案也引用了vue同樣的會被打包到該檔案中.
顯然我們的程式碼依賴vue,但是vue不應該存在兩份,如果可以將vue單獨提取出來就好了,這個問題就是要程式碼分離.
在webpack4中刪除了原來的CommonsChunkPlugin外掛,內部整合的optimization.splitChunks選項可以直接進行程式碼分離.
修改webpack.dev.js:
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'source-map',
optimization:{ // +++
splitChunks:{ // +++
chunks:'initial' // +++ initial(初始塊)、async(按需載入塊)、all(全部塊)
}
},
module: {
rules: [
{
test: /\.vue$/,
use: ['vue-loader']
},
{
test: /\.css$/,
use: ['vue-style-loader','css-loader']
}
]
},
})
我們要做的就是將打包後寫入到index.html的檔案從目前的一個檔案拆分成多個,並且寫入到index.html中.
也就是說在入口處分離程式碼.
執行:
npm run dev
你會發現在dist目錄下多出了一個檔案儲存著vue,另外一個檔案中儲存著我們編寫的程式碼.
參考
vue部分:
https://cn.vuejs.org/v2/guide...
https://vue-loader.vuejs.org/zh/
https://www.npmjs.com/package...
webpack部分:
https://webpack.docschina.org...
https://github.com/webpack-co...
https://github.com/jantimon/h...
參考的其他教程:
https://segmentfault.com/a/11...
https://segmentfault.com/a/11...
http://www.cnblogs.com/anani/...
https://blog.csdn.net/bubblin...