前言:
平時大家開發vue項目的時候,相信大部分人都是使用 vue-cli腳手架生成的項目架構,然後
npm run install
安裝依賴,
npm run serve
啟動項目然後就開始寫業務代碼了。對項目裡的webpack封裝和配置了解的不清楚,容易導緻出問題不知如何解決,或者不會通過webpack去擴充新功能,我們經常會迷惘webpack+vue項目究竟是怎樣搭建起來的呢?文章比較長,需要些耐心,雖然可以直接在GitHub上拉寫好的代碼,但我更希望你能跟着文章一步步進行code。
通過本文你能學習到:
- node項目初始化
- webpack安裝及配置
- ES6代碼轉換成ES5代碼
- scss/sass/less/stylus轉css
- .vue檔案轉換成js檔案
- 使用 jpg、png,font等資源檔案
- 自動添加css各浏覽器産商的字首
- webpack搭建本地服務及代碼熱更新
- vue目錄結構及vue-router使用
- 資源預加載
- 自定義環境變量和常量
- 區分開發環境打包跟生産環境打包
源碼位址
一、初始化項目:
在你想生成檔案的目錄下,打開指令行執行
npm init
然後一路回車就行了,主要是生成一些項目基本資訊。最後會生成一個
package.json
檔案
二、安裝webpack及腳手架
npm install webpack -D
npm install -D webpack-cli
三、ES6+轉碼為ES5及适思考配浏覽器:
配置 ES6/7/8 轉 ES5代碼
安裝相關依賴
npm install babel-loader @babel/core @babel/preset-env -D
根目錄下建立一個src檔案夾,然後再建一個
main.js
檔案,寫2句代碼
// src/main.js
let i = 4.0;
console.log('hello webpack'+ i);
webpack.config.js檔案
在項目根目錄下增加
webpack.config.js
檔案,然後寫入下面這份簡單的配置:
module.exports = {
mode: 'development',// 指定開發者打包模式
entry : './src/main.js',//入口檔案
output : {//輸出檔案
filename : 'index.js',//輸出檔案名
path : __dirname+'/public'//輸出檔案路徑
},
module : {
rules: [
{/*将js或者jsx檔案轉碼成es5*/
test: /\.jsx?$/,// 正則惰性比對字尾名為js或者jsx的檔案
exclude: /node_modules/,//排除這個檔案夾
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
]
},
}
然後在package.json的scripts先添加下面這個指令:
"build": "webpack --config ./webpack.config.js",
最後運作:
npm run build
可以看到根目錄會生成一個
public
檔案夾,而且下面有一個由
src/main.js
打包出來的
index.js
思考:
babel-loader
可以将ES6代碼轉為ES5代碼,進而可以在現有環境執行,是以我們可以用ES6編寫,而不用考慮環境支援的問題;有些浏覽器版本的釋出早于ES6的定稿和釋出,是以如果在程式設計中使用了ES6的新特性,而浏覽器沒有更新版本,或者新版本中沒有對ES6的特性進行相容,那麼浏覽器就會無法識别ES6代碼,例如IE9根本看不懂代碼寫的let和const是什麼東西?隻能選擇報錯,這就是浏覽器對ES6的相容性問題;我們可以通過 babel-polyfill 對一些不支援新文法、相容性差的用戶端提供新文法的實作。
這個思考由你來完成吧,如果你不想加入可以跳過,不影響後面的操作
四、使用 html-webpack-plugin來建立html頁面
1.安裝html-webpack-plugin插件
npm install html-webpack-plugin -D
2.添加入口檔案
在根目錄下增加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>hellow diyVue</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="index.js"></script></body>
</html>
3.修改webpack.config.js配置
const path = require('path');
const HtmlWebpackplugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',// 指定開發者打包模式
entry : './src/main.js',//入口檔案
output : {//輸出檔案
filename : 'index.js',//輸出檔案名
path : __dirname+'/public'//輸出檔案路徑
},
module : {
rules: [
{/*将js或者jsx檔案轉碼成es5*/
test: /\.jsx?$/,// 正則惰性比對字尾名為js或者jsx的檔案
exclude: /node_modules/,//排除這個檔案夾
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
]
},
plugins:[
new HtmlWebpackplugin({
filename: 'index.html', // 打包後的檔案名,預設是index.html
template: path.resolve(__dirname, 'index.html') // 導入被打包的檔案模闆
})
]
}
4.檢視效果
運作
npm run build
,我們可以看到
public
檔案夾下有一個
index.html
生成了,而且還引入了
src/main.js
的壓縮包
index.js
五、安裝配置并使用vue
1.安裝插件及vue
npm install vue-loader vue-template-compiler cache-loader thread-loader -D
npm install vue -S
- vue-loader 用于解析.vue檔案
- vue-template-compiler 用于編譯模闆
- cache-loader 用于緩存loader編譯的結果
- thread-loader 使用 worker 池來運作loader,每個 worker 都是一個 node.js 程序。
2.修改webpack.config.js配置
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackplugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',// 指定開發者打包模式
entry : './src/main.js',//入口檔案
output : {//輸出檔案
filename : 'index.js',//輸出檔案名
path : __dirname+'/public'//輸出檔案路徑
},
module : {
rules: [
{/*将js或者jsx檔案轉碼成es5*/
test: /\.jsx?$/,// 正則惰性比對字尾名為js或者jsx的檔案
exclude: /node_modules/,//排除這個檔案夾
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.vue$/,
use: [
{
loader: 'cache-loader'
},
{
loader: 'thread-loader'
},
{
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
},
}
}
]
},
]
},
plugins:[
new HtmlWebpackplugin({
filename: 'index.html', // 打包後的檔案名,預設是index.html
template: path.resolve(__dirname, 'index.html') // 導入被打包的檔案模闆
}),
new VueLoaderPlugin()
]
}
3.使用vue
在 src 建立一個 App.vue:
<template>
<div class="App">
Hello {{msg}}
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
msg: "diyVue",
};
}
};
</script>
修改src/main.js的代碼:
import Vue from 'vue'
import App from './App.vue'
new Vue({
render: h => h(App)
}).$mount('#app')
4.打包及運作vue
再次運作
npm run build
,然後在浏覽器打開
public/index.html
,可以發現
vue
已經可以運作了
六、安裝本地服務及代碼熱更新
1.安裝
webpack-dev-server
npm install webpack-dev-server -D
2.修改webpack.config.js配置
// ...
devServer: { //node本地伺服器
host: '127.0.0.1',
port: 8010
},
// ...
3.在package.json的scripts中增加一行啟動本地服務指令:
"dev": "webpack-dev-server --env.dev",
4.運作以及檢視效果
- 運作
npm run dev
- 浏覽器打開
我們可以發現本地服務已經成功啟動了,而且當我們修改http://127.0.0.1:8010/
src/app.vue
的代碼後,浏覽器是會自動重新整理的(熱更新)。一個簡單的vue項目我們已經搭建出來了,之後我們可以像堆積木一樣添加自己想要的功能了。
提醒:
生成的檔案是存在我們電腦的記憶體中的,不在我們的硬碟上(不落盤),可以通過檢視public檔案夾知道,我們改動代碼後這檔案夾下的内容是不會變更的。devServer
七、安裝Vue-Router元件
1.安裝
npm install vue-router --save
2.建立相關檔案及編寫代碼
- 新增視圖元件在 src 目錄下新增兩個視圖元件
page1:src/view/page1.vue 和 src/view/page2.vue
<template>
<div class="page1">
<h2>page1</h2>
</div>
</template>
page2:
<template>
<div class="page2">
<h2>page2</h2>
</div>
</template>
- 在
目錄下新增一個src
檔案router/index.js
import Vue from 'vue'
import VueRouter from "vue-router";
import page1 from '../view/page1.vue';
import page2 from '../view/page2.vue';
Vue.use(VueRouter)
export default new VueRouter({
mode: 'hash',
routes: [
{
path: '/page1',
component: page1
},
{
path: '/page2',
component: page2
},
{
path: '*',
redirect: '/page2'
}
]
})
- 修改
檔案main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
router,
render: h => h(App)
}).$mount('#app')
修改
App.vue
元件
<template>
<div>
<div class="App">
Hello {{msg}}
</div>
<div>
<router-link to="/page1">go page1</router-link>
<router-link to="/page2">go page2</router-link>
</div>
<div>
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
msg: "diyVue",
};
}
};
</script>
3.運作及測試效果
- 運作
npm run dev
- 測試效果: 到此為止的檔案目錄是這樣的:
八、一口氣配置基礎元件
通過前面的學習,相信你已經基本掌握了堆積木的操作,為了節省時間接下我們一次性添加多幾個積木
1.安裝基礎元件
npm install sass-loader dart-sass css-loader style-loader file-loader url-loader postcss-loader autoprefixer -D
-
主要是将sass-loader, dart-sass
文法轉為scss/sass
css
-
主要是解析css-loader
檔案css
-
主要是将style-loader
解析到css
頁面的html
上style
-
實作自動添加postcss-loader autoprefixer
css3
字首
2.在webpack.config.js中增加配置
// ...
{
test: /\.(scss|sass)$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'sass-loader',
options: {
implementation: require('dart-sass')
}
},
{
loader: 'postcss-loader',
options: {
plugins: [
require("autoprefixer") /*自動添加字首*/
]
}
}
]
},
{
test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
use: [{
loader: 'url-loader',
options: {
limit: 10000
}
}]
}
// ...
3.運作及測試
在src/App.vue後面加入下面樣式代碼:
// ...
<style scoped>
.App{
color:red;
}
</style>
運作後,我們可以看到樣式代碼生效了
九、自定義環境變量和常量
通過
webpack
提供的
DefinePlugin
插件,可以很友善的定義環境變量
1.我們先建立一個用來以後儲存常量的檔案,在根目錄下添加
config/constant.js
const NODE_ENV = process.env.NODE_ENV; // webpack編譯是擷取node環境的配置資訊
const config = {
production: { // 生産環境(線上環境)
DOMAIN: 'production.com', // 上線域名、位址
FOO_API: 'production.foo.api.com', // api變量
BAR_API: 'production.bar.api.com', // api變量
BAZ_API: 'production.baz.api.com', // api變量
},
development: { // 開發環境
DOMAIN: 'development.com', // 測試域名、位址
FOO_API: 'development.foo.api.com', // api變量
BAR_API: 'development.bar.api.com', // api變量
BAZ_API: 'development.baz.api.com', // api變量
}
}
module.exports = config[NODE_ENV];
2.修改webpack.config.js檔案
const webpack = require('webpack');
const constant = require('./config/constant'); // 引入常量檔案
// ...
plugins:[
// ...
new webpack.DefinePlugin({ // 定義全局變量
CONSTANT: JSON.stringify(constant)
})
],
3.修改package.json
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server",
"build": "cross-env NODE_ENV=production webpack --config ./webpack.config.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
-
用來磨平mac和win中的node環境之間的不同,是以我們需要安裝一下cross-env
npm install cross-env -D
-
和NODE_ENV=development
指定node環境NODE_ENV=production
4.運作及調試
- 由于修改了
是以需要重新運作webpack.config.js
npm run dev
- 在src/main.js後面加上一句測試代碼:
console.log(CONSTANT);
- 配置成功列印出來
十、區分開發環境打包跟生産環境打包
在config下建立兩個檔案
- webpack.dev.js 開發環境使用
- webpack.prod.js 生産環境使用
- webpack.config.js 公用配置
公共配置webpack.config.js
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackplugin = require('html-webpack-plugin');
const webpack = require('webpack');
const constant = require('./config/constant'); // 引入常量檔案
module.exports = {
entry : './src/main.js',//入口檔案
output : {//輸出檔案
filename : 'index.js',//輸出檔案名
path : __dirname+'/public',//輸出檔案路徑
// publicPath: "public", // 虛拟目錄,自動指向path編譯目錄,放在記憶體中,是以在硬碟上是找不到的 預設是:/
},
module : { // 當執行require或import指令時比對下面的加載規則
rules: [
{/*将js或者jsx檔案轉碼成es5*/
test: /\.jsx?$/,// 正則惰性比對字尾名為js或者jsx的檔案
exclude: /node_modules/,//排除這個檔案夾
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{ // vue檔案處理
test: /\.vue$/,
use: [
{
loader: 'cache-loader'
},
{
loader: 'thread-loader'
},
{
loader: 'vue-loader',
options: {
compilerOptions: {
preserveWhitespace: false
},
}
},
]
},
// { // 檔案資源加載 變成base64會跟下面圖檔資源處理沖突是以注釋了
// test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
// use: [{
// loader: 'url-loader',
// options: {
// name: '[name].[ext]'
// }
// }]
// },
{ // 圖檔資源處理
test: /\.(png|jpg|gif|svg)/,
use: [{
loader: "file-loader",
options: {
name: '[name].[ext]',
outputPath: "public/assets/", // 輸出目錄
limit: 8192,
}
}]
}
]
},
plugins:[
new HtmlWebpackplugin({
filename: 'index.html', // 打包後的檔案名,預設是index.html
template: path.resolve(__dirname, 'index.html') // 導入被打包的檔案模闆
}),
new VueLoaderPlugin(),
new webpack.DefinePlugin({ // 定義全局變量
CONSTANT: JSON.stringify(constant)
})
],
}
開發環境
- 不需要壓縮代碼
- 需要本地服務和熱更新
- css不需要提取到css檔案
- sourceMap
const merge = require('webpack-merge')
const webpackConfig = require('../webpack.config')
module.exports = merge(webpackConfig, {
devtool: 'cheap-module-eval-source-map',
mode: 'development',// 指定開發者打包模式
devServer: { //node本地伺服器
host: '127.0.0.1',
port: 8010
},
module : {
rules: [
{
test: /\.(scss|sass)$/,
use: [
{
loader: 'style-loader'
},
{
loader: 'css-loader'
},
{
loader: 'sass-loader',
options: {
implementation: require('dart-sass')
}
},
{
loader: 'postcss-loader',
options: {
plugins: [
require("autoprefixer") /*自動添加字首*/
]
}
}
]
},
]
},
})
生産環境
- 壓縮代碼
- 不需要本地服務和熱更新
- 提取css,壓縮css檔案
- sourceMap
- 建構前清除上一次建構的内容
const path = require('path')
const merge = require('webpack-merge')
const webpack = require('webpack')
const webpackConfig = require('../webpack.config')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssnanoPlugin = require('@intervolga/optimize-cssnano-plugin');
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = merge(webpackConfig, {
mode: 'production',// 指定開發者打包模式壓縮js代碼
devtool: '#source-map',
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
name: 'chunk-vendors',
test: /[\\\/]node_modules[\\\/]/,
priority: -10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
}
}
},
module: {
rules: [
{
test: /\.(scss|sass)$/,
use: [
{
loader: MiniCssExtractPlugin.loader
},
{
loader: 'css-loader',
options: {
importLoaders: 2
}
},
{
loader: 'sass-loader',
options: {
implementation: require('dart-sass')
}
},
{
loader: 'postcss-loader',
options: {
plugins: [
require("autoprefixer") /*自動添加字首*/
]
}
}
]
},
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].css'
}),
new OptimizeCssnanoPlugin({
sourceMap: true,
cssnanoOptions: {
preset: [
'default',
{
mergeLonghand: false,
cssDeclarationSorter: false
}
]
}
}),
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../public'),
to: path.resolve(__dirname, '../dist')
}
]),
new CleanWebpackPlugin(), // 用于删除上次建構的檔案
]
})
安裝所需依賴
npm i @intervolga/optimize-cssnano-plugin mini-css-extract-plugin clean-webpack-plugin webpack-merge copy-webpack-plugin -D
-
用于壓縮@intervolga/optimize-cssnano-plugin
代碼css
-
用于提取mini-css-extract-plugin
到檔案中css
-
用于删除上次建構的檔案clean-webpack-plugin
-
合并webpack-merge
配置webpack
-
使用者拷貝靜态資源copy-webpack-plugin
圖檔資源路徑名使用
修改
src/App.vue
<template>
<div>
<div class="App">
<img :src="imgUrl1">
<div>Hello {{msg}}</div>
</div>
<div>
<router-link to="/page1">go page1</router-link>
<router-link to="/page2">go page2</router-link>
</div>
<div>
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
msg: "diyVue",
imgUrl1: require('./assets/logo.png'),
};
}
};
</script>
<style scoped>
.App{
color:red;
}
</style>
這次的操作優化了圖檔資源的引用,我們執行
npm run dev
後,在浏覽器打開項目,可以看到
優化前這個路徑是base64位的,現在變成了一個普通的路徑。
總結:
vue-cli是一個封裝得很完美的vue腳手架,是以它的适應性很強;但是有些大公司他們的前端項目一般不會直接套用這種腳手架,而是需要結合公司内部的元件一步步搭起一個vue前端項目。
單純的vue架構是非常簡單的,但是結合到node環境和webpack一起用的話,有一些不是太熟悉node、webpack的前端同學就會有些蒙圈。這個案例中,我們主要是搭建好了一個webpack環境,然後将需要的東西一件一件組裝起來,雖然不算太完善,但是學會了這種思路的話,我們處理其他前端項目也不難了。
如果幫助到你了,就點贊,哈哈哈,不可能有贊的!