如何從零開始一個vue+webpack前端工程工作流的搭建,首先我們先從專案的目錄結構入手。一個持續可發展,不斷加入新功能,方便後期維護的目錄結構究竟是長什麼樣子的?接下來閏土大叔帶你們一起手摸手學起來。
初級前端初始化目錄篇
專案伊始,我們肯定是先在terminal終端命令列(以下簡稱terminal)cd進入<project name>根目錄,然後輸入 npm init
初始化一個npm專案,在專案根目錄下面就會出現一個package.json檔案。
然後就可以安裝依賴了,直接在terminal裡輸入 npm i webpack vue vue-loader -D
。當我們把這幾個安裝好以後,terminal這邊會提示我們WARN(警告⚠️):
翻譯過來大意是,vue-loader需要一個css-loader和vue-template-compiler作為它的第三方依賴,所以聽它的話,我們去進行一下安裝:
npm i css-loader vue-template-compiler -D
那下面的警告資訊提示我們缺少一些資訊,這個其實無關痛癢,所以不需要去關心它。
通過以上簡單幾個步驟,我們的專案就初始化好了。然後在根目錄下面建立一個src資料夾,這是我們原始碼放置的目錄。然後我們在src目錄下面新建一個app.vue檔案,裡面就可以寫一些關於專案的業務程式碼:
<template>
<div id="test">{{text}}</div>
</template>
<script>
export default {
data () {
text: '閏土大叔'
}
}
</script>
<style>
#test{
font-size:12px;
color:green;
}
</style>
複製程式碼
當然這個字尾為.vue 檔案是不可以在瀏覽器裡直接執行的,我們需要想辦法讓它執行起來。
現在我們要在專案根目錄下新建一個webpack.config.js檔案,webpack是幫我們前端來打包資源的,前端資源有很多不同的型別,比如說JavaScript,css,html,image,iconfont等這些資源都是需要通過http請求載入的東西。webpack是將一個js檔案載入到瀏覽器端之後,然後去把所有的內容去渲染出來。所以,很多時候,我們可以把js檔案作為專案的入口檔案。
這個時候,我們在src目錄下新建一個index.js作為入口檔案,順便在裡面寫點東西:
import Vue from 'vue'
import App from './app.vue'
const root = document.createElement('div')
document.body.appendChild(root)
new Vue({
render: (h) => h(App)
}).$mount(root)
複製程式碼
index.js準備完畢之後,那麼在webpack.config.js裡面就可以這樣寫:
const path = require('path')
module.exports = {
entry: path.join(__dirname, 'src/index.js'),
output: {
filename: 'bundle.js',
path: path.join(__dirname, 'dist')
}
}
複製程式碼
在上面的程式碼中,__dirname就代表這個檔案所在的目錄地址,path.join()的意思就是和後面的字串路徑拼接起來,形成一個絕對的路徑。
然後通過webpack把所有的檔案打包成一個bundle.js檔案,並且是能在瀏覽器裡面直接執行的程式碼。現在我們可以在package.json 檔案裡的scripts物件裡面新增一個指令碼:
"scripts": {
"build": "webpack --config webpack.config.js"
}
複製程式碼
看到這兒,肯定有童鞋要問了,為什麼要在這裡面呼叫webpack而不是在terminal裡面直接執行呢?
因為只有在這裡呼叫webpack,它才會優先呼叫我們專案裡面安裝的webpack版本,如果我們在命令列裡面輸入webpack,它會調動全域性的webpack,這個時候全域性的webpack可能會跟我們專案中的webpack版本不一致,所以我們還是採取這種方式比較穩妥。
寫完之後,我們就可以在terminal輸入npm run build
跑一下,會尷尬地發現報錯了:
這個錯誤告訴我們,需要為.vue檔案去宣告一個loader。因為webpack原生是隻支援JS檔案型別的,並且只支援ES5的語法,所以我們在使用超出它理解範圍的語法的時候,我們要使用一些幫它去處理的工具。所以我們要在webpack.config.js檔案裡面繼續寫:
module: {
rules: [
{
test: /.vue$/,
loader: 'vue-loader'
}
]
}
複製程式碼
新增完這段之後,我們再去terminal執行下npm run build
,你會發現專案根目錄下多了一個dist資料夾,點開裡面發現webpack為我們自動打包生成了一個bundle.js檔案,感興趣的童鞋可以點開這個js檔案看看:
往下翻到100多行左右的時候,你會發現有很多的程式碼其實是vue原始碼。因為我們專案要依賴vue.js,所以webpack會把vue.js檔案打包進來。
你可以通過快捷鍵 command (Ctrl) + F 查詢關鍵詞$mount看到,紅線圈住的這段程式碼就是我們自己寫的程式碼,其實webpack做的工作就是把這些不同的靜態資源的型別打包成一個js,然後我們在html裡面引用這個js,就可以正常執行。
相信大家做前端都知道,在做一個專案開發的時候,我們希望把一些零碎的js檔案打包到一起,這樣可以減少http請求。同樣的,我們希望使用模組依賴,因為專案中會做很多可複用的程式碼,把它寫到一個模組裡面去,這樣的話當我們再去寫一個新專案的時候,不用再把原來的程式碼重新寫一遍,或者是拷貝一份。
當然這裡面我們暫時沒有提到.babelrc、.eslintrc、editorconfig、postcss.config.js等,這些我們留到後面再講。
中級前端合理細化目錄篇
初始化工作完成之後,接下來我們要細分目錄了。首先我們需要在專案的根目錄下新建一個資料夾叫build,把webpack的檔案單獨放到這個資料夾裡面。因為我們專案中會用到很多不同的相關檔案的配置,接下來先新建一個 webpack.config.base.js 檔案,我們把webpack裡面需要用到的共同的配置放到這個base的檔案裡面。比如開發環境和正式環境,以及後期我們要提到的服務端渲染的環境。我們都依賴於base這個配置。
以下是webpack.config.base.js檔案裡的程式碼:
const path = require('path')
const createVueLoaderOptions = require('./vue-loader.config')
const isDev = process.env.NODE_ENV === 'development'
const config = {
target: 'web',
entry: path.join(__dirname, '../client/index.js'),
output: {
filename: 'bundle.[hash:8].js',
path: path.join(__dirname, '../dist')
},
module: {
rules: [
{
test: /\.(vue|js|jsx)$/,
loader: 'eslint-loader',
exclude: /node_modules/,
enforce: 'pre'
},
{
test: /\.vue$/,
loader: 'vue-loader',
options: createVueLoaderOptions(isDev)
},
{
test: /\.jsx$/,
loader: 'babel-loader'
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(gif|jpg|jpeg|png|svg)$/,
use: [
{
loader: 'url-loader',
options: {
limit: 1024,
name: 'resources/[path][name].[hash:8].[ext]'
}
}
]
}
]
}
}
module.exports = config
複製程式碼
然後我們再新建一個 webpack.config.client.js ,這個client檔案依賴於base檔案,在此基礎上擴充套件一些其他配置。因此我們需要在webpack.config.client.js裡面敲入一行程式碼引入base檔案 :
const baseConfig = require('./webpack.config.base')
基礎工作做完之後,我們該如何去擴充套件配置呢?首先在terminal終端命令列安裝下 npm i webpack-merge -D
我們需要webpack-merge這個工具幫助去擴充套件、合併不同的webpack配置,然後根據宣告好的isDev來判斷應該怎麼合併配置。
以下是webpack.config.client.js檔案裡的程式碼:
const path = require('path')
const HTMLPlugin = require('html-webpack-plugin')
const webpack = require('webpack')
const merge = require('webpack-merge')
const ExtractPlugin = require('extract-text-webpack-plugin')
const baseConfig = require('./webpack.config.base')
const isDev = process.env.NODE_ENV === 'development'
const defaultPlugins = [
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: isDev ? '"development"' : '"production"'
}
}),
new HTMLPlugin()
]
const devServer = {
port: 8000,
host: '0.0.0.0',
overlay: {
errors: true
},
hot: true
}
let config
if (isDev) {
// 開發環境的配置
config = merge(baseConfig, {
devtool: '#cheap-module-eval-source-map',
module: {
rules: [
{
test: /\.styl/,
use: [
'vue-style-loader',
'css-loader',
// {
// loader: 'css-loader',
// options: {
// module: true,
// localIdentName: isDev ? '[path]-[name]-[hash:base64:5]' : '[hash:base64:5]'
// }
// },
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
},
'stylus-loader'
]
}
]
},
devServer,
plugins: defaultPlugins.concat([
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin()
])
})
} else {
// 正式環境的配置
config = merge(baseConfig, {
entry: {
app: path.join(__dirname, '../client/index.js'),
vendor: ['vue']
},
output: {
filename: '[name].[chunkhash:8].js'
},
module: {
rules: [
{
test: /\.styl/,
use: ExtractPlugin.extract({
fallback: 'vue-style-loader',
use: [
'css-loader',
{
loader: 'postcss-loader',
options: {
sourceMap: true
}
},
'stylus-loader'
]
})
}
]
},
plugins: defaultPlugins.concat([
new ExtractPlugin('styles.[contentHash:8].css'),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime'
})
])
})
}
module.exports = config
複製程式碼
最後,這個src資料夾我們要重新命名一下,叫client,因為我們後期還要寫服務端的程式碼,對應的就命名成server,正好對應它的含義。這樣看起來,名稱就變得更加的合理。
當我們萬事大吉的時候,千萬記得要把 webpack.config.base.js 和 webpack.config.client.js 裡面的src路徑改掉,換成client,否則就會報錯。
以上就是我們專案最終形成的目錄結構,client目錄下分別有assets、layout、views這三個資料夾,其中assets目錄下放靜態資源,例如images、styles等;layout目錄下放通用佈局的元件;views目錄下放具體的業務程式碼的元件。
當然,這個目錄其實還可以隨著專案的開發再細分下去,這裡就不展開敘述了。
寫在最後
大家一定要注意,在我們正式開發專案、建立一個專案工程的時候,一定要先把目錄結構理順,條理一定要清楚。每個目錄結構裡面放什麼東西,心裡一定要先有個概念。以後新建的檔案不要亂放,因為專案一旦做大,維護時間比較久的時候,可能兩三個月裡面都有一個檔案你不會去碰它。到時候如果要去找一個東西的時候,你會找不到它,這是非常令人難受的一件事情。
最重要的一點是,目錄結構的混亂,會導致你後續開發專案的效率變得非常的低。
這次關於“一個正式專案的目錄結構是怎麼形成的”的話題就說到這裡,我之後的文章會講些什麼呢?文章預告如下:
- eslint的錯誤修復小技巧
- vue-loader是如何配置的
- 如何回答“對vue生命週期的理解”才能讓面試官滿意?
- 淺談css-module的配置
- ......
- 正式環境打包以及非同步模組打包優化