深入淺出的webpack構建工具--webpack4+vue搭建環境 (十三)
從上面一系列的webpack配置的學習,我們現在來使用webpack來搭建vue的開發環境。首先我們來設想下我們的項目的目錄結構如下:
### 目錄結構如下: demo1 # 工程名 | |--- dist # 打包后生成的目錄文件 | |--- node_modules # 所有的依賴包 | |--- app | | |---index | | | |-- views # 存放所有vue頁面文件 | | | |-- components # 存放vue公用的組件 | | | |-- app.js # vue入口配置文件 | |--- views | | |-- index.html # html文件 | |--- webpack.config.js # webpack配置文件 | |--- .gitignore | |--- README.md | |--- package.json | |--- .babelrc # babel轉碼文件
因此需要依賴package.json文件配置如下:
{ "name": "vue項目架構", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "webpack-dev-server --progress --colors --devtool cheap-module-eval-source-map --hot --inline", "build": "webpack --progress --colors --devtool cheap-module-source-map", "build:dll": "webpack --config webpack.dll.config.js" }, "author": "tugenhua0707@qq.com", "sideEffects": false, "license": "ISC", "devDependencies": { "add-asset-html-webpack-plugin": "^2.1.3", "babel-core": "^6.26.3", "babel-loader": "^7.1.5", "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.7.0", "babel-preset-stage-2": "^6.24.1", "clean-webpack-plugin": "^0.1.19", "css-loader": "^1.0.0", "cssnano": "^4.0.5", "extract-text-webpack-plugin": "^4.0.0-beta.0", "file-loader": "^1.1.11", "happypack": "^5.0.0", "html-webpack-plugin": "^3.2.0", "lodash-es": "^4.17.11", "mini-css-extract-plugin": "^0.4.2", "path": "^0.12.7", "postcss-cssnext": "^3.1.0", "postcss-loader": "^3.0.0", "postcss-pxtorem": "^4.0.1", "postcss-sprites": "^4.2.1", "style-loader": "^0.21.0", "stylus": "^0.54.5", "stylus-loader": "^3.0.2", "uglifyjs-webpack-plugin": "^1.2.7", "url-loader": "^1.0.1", "vue-loader": "^15.4.2", "vue-style-loader": "^4.1.2", "vue-template-compiler": "^2.5.17", "webpack": "^4.16.1", "webpack-cli": "^3.0.8", "webpack-deep-scope-plugin": "^1.6.0", "webpack-dev-server": "^3.1.4", "webpack-parallel-uglify-plugin": "^1.1.0" }, "dependencies": { } }
接着項目 views/index.html 代碼初始化如下:
<!DOCTYPE html> <html> <head> <title>webpack4+vue項目架構</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0"> </head> <body> <div id="app"> </div> </body> </html>
.babelrc 轉碼文件代碼如下:
{ "plugins": [ [ "transform-runtime", { "polyfill": false } ] ], "presets": [ [ "env", { "modules": false // 關閉Babel的模塊轉換功能,保留ES6模塊化語法 } ], "stage-2" ] }
我們現在在 app/index/views 下新建一個test.vue 代碼如下:
<style lang="stylus">
</style>
<template>
<div class='app-container'>
<div>
<p v-if="datas.length > 0" v-for="(item, index) in datas">{{item}}</p>
</div>
</div>
</template>
<script type="text/javascript">
export default {
data() {
return {
datas: [1, 2, 3, 4]
}
}
}
</script>
如上代碼文件是vue文件,因此我們需要安裝vue-loader等插件,安裝命令如下:
npm i -D vue-loader css-loader vue-template-compiler vue-style-loader
vue框架運行需要的庫,命令如下:
npm i --save vue
上面依賴的作用如下:
vue-loader: 解析和轉換.vue文件,提取出其中的邏輯代碼script,樣式代碼style及html模板template,再分別將他們交給對應的Loader去處理。
css-loader: 加載由vue-loader提取出的css代碼。
vue-template-compiler: 將vue-loader 提取出的HTML模板編譯成對應的可執行javascript代碼。
然后我們編寫下 app/index/app.js 的入口文件簡單的代碼如下:
import Vue from 'vue'; import Test from './views/test'; new Vue({ el: '#app', render: h => h(Test) });
接着 webpack.config.js 代碼配置如下:
const path = require('path');
// 引入 mini-css-extract-plugin 插件
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
// 清除dist目錄下的文件
const ClearWebpackPlugin = require('clean-webpack-plugin');
const webpack = require('webpack');
// 引入打包html文件
const HtmlWebpackPlugin = require('html-webpack-plugin');
// 引入HappyPack插件
const HappyPack = require('happypack');
// 引入 ParallelUglifyPlugin 插件
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
// 引入 webpack-deep-scope-plugin 優化
const WebpackDeepScopeAnalysisPlugin = require('webpack-deep-scope-plugin').default;
module.exports = {
// 入口文件
entry: {
main: './app/index/app.js'
},
output: {
filename: process.env.NODE_ENV === 'production' ? '[name].[contenthash].js' : 'bundle.js',
// 將輸出的文件都放在dist目錄下
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
// 使用正則去匹配
test: /\.styl$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {}
},
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: [
require('postcss-cssnext')(),
require('cssnano')(),
require('postcss-pxtorem')({
rootValue: 16,
unitPrecision: 5,
propWhiteList: []
}),
require('postcss-sprites')()
]
}
},
{
loader: 'stylus-loader',
options: {}
}
]
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'happypack/loader?id=css-pack'
]
},
{
test: /\.(png|jpg)$/,
use: ['happypack/loader?id=image']
},
{
test: /\.js$/,
// 將對.js文件的處理轉交給id為babel的HappyPack的實列
use: ['happypack/loader?id=babel'],
// loader: 'babel-loader',
exclude: path.resolve(__dirname, 'node_modules') // 排除文件
},
{
test: /\.vue$/,
use: ['happypack/loader?id=vue-loader'],
exclude: path.resolve(__dirname, 'node_modules') // 排除文件
}
]
},
resolve: {
extensions: ['*', '.js', '.json', '.vue']
},
devtool: 'cheap-module-eval-source-map',
devServer: {
port: 8081,
host: '0.0.0.0',
headers: {
'X-foo': '112233'
},
inline: true,
overlay: true,
stats: 'errors-only'
},
mode: 'development', // 開發環境下
// mode: 'production',
plugins: [
new HtmlWebpackPlugin({
template: './views/index.html' // 模版文件
}),
new ClearWebpackPlugin(['dist']),
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css'
}),
/**** 使用HappyPack實例化 *****/
new HappyPack({
// 用唯一的標識符id來代表當前的HappyPack 處理一類特定的文件
id: 'babel',
// 如何處理.js文件,用法和Loader配置是一樣的
loaders: ['babel-loader']
}),
new HappyPack({
// 用唯一的標識符id來代表當前的HappyPack 處理一類特定的文件
id: 'vue-loader',
// 如何處理.js文件,用法和Loader配置是一樣的
loaders: ['vue-loader']
}),
new HappyPack({
id: 'image',
loaders: [{
loader: require.resolve('url-loader'),
options: {
limit: 10000,
name: '[name].[ext]'
}
}]
}),
// 處理styl文件
new HappyPack({
id: 'css-pack',
loaders: ['css-loader']
}),
// 使用 ParallelUglifyPlugin 並行壓縮輸出JS代碼
new ParallelUglifyPlugin({
// 傳遞給 UglifyJS的參數如下:
uglifyJS: {
output: {
/*
是否輸出可讀性較強的代碼,即會保留空格和制表符,默認為輸出,為了達到更好的壓縮效果,
可以設置為false
*/
beautify: false,
/*
是否保留代碼中的注釋,默認為保留,為了達到更好的壓縮效果,可以設置為false
*/
comments: false
},
compress: {
/*
是否在UglifyJS刪除沒有用到的代碼時輸出警告信息,默認為輸出,可以設置為false關閉這些作用
不大的警告
*/
warnings: false,
/*
是否刪除代碼中所有的console語句,默認為不刪除,開啟后,會刪除所有的console語句
*/
drop_console: true,
/*
是否內嵌雖然已經定義了,但是只用到一次的變量,比如將 var x = 1; y = x, 轉換成 y = 5, 默認為不
轉換,為了達到更好的壓縮效果,可以設置為false
*/
collapse_vars: true,
/*
是否提取出現了多次但是沒有定義成變量去引用的靜態值,比如將 x = 'xxx'; y = 'xxx' 轉換成
var a = 'xxxx'; x = a; y = a; 默認為不轉換,為了達到更好的壓縮效果,可以設置為false
*/
reduce_vars: true
}
}
}),
new WebpackDeepScopeAnalysisPlugin()
]
};
然后我們運行 npm run dev 后,打包報錯了:如下:

vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config.
通過百度搜索后,網上都說需要引入 VueLoaderPlugin 的webpack組件,webpack如下引入方式:
const VueLoaderPlugin = require('vue-loader/lib/plugin');
module.exports = {
plugins: [
new VueLoaderPlugin()
]
}
引入后,再運行還會報錯,如下信息:
Error: [VueLoaderPlugin Error] No matching use for vue-loader is found. Make sure the rule matching .vue files include vue-loader in its use.
我這邊是使用的是"vue-loader": "^15.4.2", 我剛開始以為是 vue-loader 版本的問題了,然后當我修改版本到14版本后,還是會報錯,然后在github上搜索這個答案,發現老外也提了這樣的問題,說是不是版本的問題,最后尤雨溪回答,這和vue-loader版本沒有關系,最后我搜索到 vue-loader 15.1, 它不支持happypack這個插件優化,可以看github(https://github.com/vuejs/vue-loader/issues/1339)上的提示,有了這個提示,我直接把webpack中和vue相關的 happypack的優化去掉,然后打包既然就可以了。
因此webpack中的vue配置就變成如下:
module.exports = { module: { rules: [ { test: /\.vue$/, use: ['vue-loader'] } ] } }
一切准備就緒后,我們現在運行 npm run dev 后,會運行如下所示:

然后我們就可以在瀏覽器下 運行 http://0.0.0.0:8081/ 就可以看到vue頁面了,當我們繼續修改 test.vue的時候, 保存后更新。
理想是美好的,現實是殘酷的,但是當我一切認為可以的時候,然后我們把對應的<style lang="stylus">樣式加上,如下代碼:
<style lang="stylus" > .app-container width 200px </style> <template> <div class='app-container'> <div> <p v-if="datas.length > 0" v-for="(item, index) in datas">{{item}}</p> </div> </div> </template> <script type="text/javascript"> export default { data() { return { datas: [5, 2, 3, 4] } } } </script>
繼續打包,就報這樣的錯誤了;如下:
Module parse failed: Unexpected token (2:0) You may need an appropriate loader to handle this file type. | > .app-container | width 200px
最后github搜索,發現 webpack4 不兼容vue-loader 15.x.x版本,github點擊查看(https://github.com/airyland/vux/issues/3060)或者可以看這個(https://segmentfault.com/a/1190000014586699)
然后我把vue-loader 改成 "vue-loader": "^14.2.2",然后繼續打包, 就沒有問題了。
如下運行結果:

二:webpack4上如何提取css文件到單獨的文件
webpack4以上貌似不能使用mini-css-extract-plugin提取css文件,比如我在代碼里面這樣寫:
new MiniCssExtractPlugin({ filename: process.env.NODE_ENV === 'production' ? 'css/[name].[contenthash:8].css' : '[name].css', chunkFilename: process.env.NODE_ENV === 'production' ? 'css/[id].[contenthash:8].css' : '[id].css' }),
但是貌似提取不了,看到說webpack4還是可以用extract-text-webpack-plugin 只不過安裝的時候插件名加個@next,於是就改用
extract-text-webpack-plugin來提取這個插件就可以打包在一個css文件內.
因此我們現在安裝命令如下:
npm i extract-text-webpack-plugin@next -D
然后在webpack.config.js 這樣配置代碼即可:
// webpack.config.js var ExtractTextPlugin = require("extract-text-webpack-plugin") module.exports = { // other options... module: { rules: [ { test: /\.vue$/, loader: 'vue-loader', options: { extractCSS: true } } ] }, plugins: [ new ExtractTextPlugin("style.css") ] }
運行結構如下所示;可以看到css文件被提取出來了。

具體可以看官方文檔(https://vue-loader-v14.vuejs.org/zh-cn/configurations/extract-css.html)
下面是github上的源碼:
基於webpack4+vue 單頁面(https://github.com/tugenhua0707/webpack-all-demo/tree/master/webpack%2Bvue)
