vue-cli3 專案從搭建最佳化到docker部署
專案地址 vue-cli3-project 歡迎 star
原文地址
https://www。
ccode。live/lentoo/list/
9?from=art
1。 建立一個vue專案
相信大部分人都已經知道怎麼建立專案的,可以跳過這一節,看下一節。
1。1 安裝@vue/cli
# 全域性安裝 vue-cli腳手架
npm install -g @vue/cli
等待安裝完成後開始下一步
1。2 初始化專案
vue create vue-cli3-project
選擇一個預設
可以選擇預設預設,預設預設包含了
babel
,
eslint
我們選擇更多功能
Manually select features
回車後來到選擇外掛
外掛選擇
這邊選擇了(Babel、Router、Vuex、Css預處理器、Linter / Formatter 格式檢查、Unit測試框架)
路由模式選擇
是否使用
history
模式的路由 (Yes)
選擇一個css預處理器 (Sass/SCSS)
選擇一個eslint配置
這邊選擇
ESLint + Standard config
,個人比較喜歡這個程式碼規範
選擇什麼時候進行
eslint
校驗
選擇(Lint on save)儲存是檢查
如果你正在使用的vscode編輯器的話,可以配置eslint外掛進行程式碼自動格式化
7。 選擇測試框架 (Mocha + Chai)
8。 選擇將這些配置檔案寫入到什麼地方 (In dedicated config files)
是否儲存這份預設配置?(y)
選是的話,下次建立一個vue專案,可以直接使用這個預設檔案,而無需再進行配置。
等待依賴完成
2。 全域性元件自動註冊
在
components
目錄下建立一個
global
目錄,裡面放置一些需要全域性註冊的元件。
index。js
作用只要是引入
main。vue
,匯出元件物件
在
components
中建立一個
index。js
,用來掃描全域性物件並自動註冊。
// components/index。js
import
Vue
from
‘vue’
// 自動載入 global 目錄下的 。js 結尾的檔案
const
componentsContext
=
require
。
context
(
‘。/global’
,
true
,
/\。js$/
)
componentsContext
。
keys
()。
forEach
(
component
=>
{
const
componentConfig
=
componentsContext
(
component
)
/**
* 相容 import export 和 require module。export 兩種規範
*/
const
ctrl
=
componentConfig
。
default
||
componentConfig
Vue
。
component
(
ctrl
。
name
,
ctrl
)
})
最後在入口檔案
main。js
中匯入這個
index。js
中就可以了
3。路由自動引入
在
Vue
專案中使用路由,相信想熟的人已經很熟悉怎麼使用了,要新增一個頁面的話,需要到路由配置中配置該頁面的資訊。
如果頁面越來越多的話,那麼如何讓我們的路由更簡潔呢?
3。1 拆分路由
根據不同的業務模組進行拆分路由
在每個子模組中匯出一個路由配置陣列
在根
index。js
中匯入所有子模組
3。2 自動掃描子模組路由並匯入
當我們的業務越來越龐大,每次新增業務模組的時候,我們都要在路由下面新增一個子路由模組,然後在
index。js
中匯入。
那麼如何簡化這種操作呢?
透過上面的自動掃描全域性元件註冊,我們也可以實現自動掃描子模組路由並匯入
4。 透過node來生成元件
作為前端開發者,放著
node
這麼好用的東西如果不能運用起來,豈不是很浪費?
雖然我們透過上面已經實現了元件的自動註冊,不過每次新建元件的時候,都要建立一個目錄,然後新增一個
。vue
檔案,然後寫
template
、
script
、
style
這些東西,然後新建一個
index。js
、匯出vue元件、雖然有外掛能實現自動補全,但還是很麻煩有木有。
那麼我們能不能透過
node
來幫助我們幹這些事情呢?只要告訴
node
幫我生成的元件名稱就行了。其它的事情讓
node
來幹
4。1 透過node來生成元件
安裝一下
chalk
,這個外掛能讓我們的控制檯輸出語句有各種顏色區分
npm install chalk ——save-dev
在根目錄中建立一個
scripts
資料夾,
新增一個
generateComponent。js
檔案,放置生成元件的程式碼、
新增一個
template。js
檔案,放置元件模板的程式碼 * template。js
// template。js
module
。
exports
=
{
vueTemplate
:
compoenntName
=>
{
return
`
${
compoenntName
}
”>
${
compoenntName
}
元件
export default {
name: ‘
${
compoenntName
}
’
}
。
${
compoenntName
}
{
}
`
},
entryTemplate
:
`import Main from ‘。/main。vue’
export default Main`
}
generateComponent。js`
// generateComponent。js`
const
chalk
=
require
(
‘chalk’
)
const
path
=
require
(
‘path’
)
const
fs
=
require
(
‘fs’
)
const
resolve
=
(。。。
file
)
=>
path
。
resolve
(
__dirname
,
。。。
file
)
const
log
=
message
=>
console
。
log
(
chalk
。
green
(
`
${
message
}
`
))
const
successLog
=
message
=>
console
。
log
(
chalk
。
blue
(
`
${
message
}
`
))
const
errorLog
=
error
=>
console
。
log
(
chalk
。
red
(
`
${
error
}
`
))
const
{
vueTemplate
,
entryTemplate
}
=
require
(
‘。/template’
)
const
generateFile
=
(
path
,
data
)
=>
{
if
(
fs
。
existsSync
(
path
))
{
errorLog
(
`
${
path
}
檔案已存在`
)
return
}
return
new
Promise
((
resolve
,
reject
)
=>
{
fs
。
writeFile
(
path
,
data
,
‘utf8’
,
err
=>
{
if
(
err
)
{
errorLog
(
err
。
message
)
reject
(
err
)
}
else
{
resolve
(
true
)
}
})
})
}
log
(
‘請輸入要生成的元件名稱、如需生成全域性元件,請加 global/ 字首’
)
let
componentName
=
‘’
process
。
stdin
。
on
(
‘data’
,
async
chunk
=>
{
const
inputName
=
String
(
chunk
)。
trim
()。
toString
()
/**
* 元件目錄路徑
*/
const
componentDirectory
=
resolve
(
‘。。/src/components’
,
inputName
)
/**
* vue元件路徑
*/
const
componentVueName
=
resolve
(
componentDirectory
,
‘main。vue’
)
/**
* 入口檔案路徑
*/
const
entryComponentName
=
resolve
(
componentDirectory
,
‘index。js’
)
const
hasComponentDirectory
=
fs
。
existsSync
(
componentDirectory
)
if
(
hasComponentDirectory
)
{
errorLog
(
`
${
inputName
}
元件目錄已存在,請重新輸入`
)
return
}
else
{
log
(
`正在生成 component 目錄
${
componentDirectory
}
`
)
await
dotExistDirectoryCreate
(
componentDirectory
)
// fs。mkdirSync(componentDirectory);
}
try
{
if
(
inputName
。
includes
(
‘/’
))
{
const
inputArr
=
inputName
。
split
(
‘/’
)
componentName
=
inputArr
[
inputArr
。
length
-
1
]
}
else
{
componentName
=
inputName
}
log
(
`正在生成 vue 檔案
${
componentVueName
}
`
)
await
generateFile
(
componentVueName
,
vueTemplate
(
componentName
))
log
(
`正在生成 entry 檔案
${
entryComponentName
}
`
)
await
generateFile
(
entryComponentName
,
entryTemplate
)
successLog
(
‘生成成功’
)
}
catch
(
e
)
{
errorLog
(
e
。
message
)
}
process
。
stdin
。
emit
(
‘end’
)
})
process
。
stdin
。
on
(
‘end’
,
()
=>
{
log
(
‘exit’
)
process
。
exit
()
})
function
dotExistDirectoryCreate
(
directory
)
{
return
new
Promise
((
resolve
)
=>
{
mkdirs
(
directory
,
function
()
{
resolve
(
true
)
})
})
}
// 遞迴建立目錄
function
mkdirs
(
directory
,
callback
)
{
var
exists
=
fs
。
existsSync
(
directory
)
if
(
exists
)
{
callback
()
}
else
{
mkdirs
(
path
。
dirname
(
directory
),
function
()
{
fs
。
mkdirSync
(
directory
)
callback
()
})
}
}
配置package。json
“new:comp”: “node 。/scripts/generateComponent”
執行
如果使用
npm
的話 就是
npm run new:comp
如果使用
yarn
的話 就是
yarn new:comp
4。2 透過node來生成頁面元件
透過上面的邏輯程式碼我們可以透過
node
來生成元件了,那麼也可以舉一反三來生成頁面元件。只需稍微修改一下生成元件程式碼的邏輯。 在
scripts
目錄下新建一個
generateView。js
檔案
// generateView。js
const
chalk
=
require
(
‘chalk’
)
const
path
=
require
(
‘path’
)
const
fs
=
require
(
‘fs’
)
const
resolve
=
(。。。
file
)
=>
path
。
resolve
(
__dirname
,
。。。
file
)
const
log
=
message
=>
console
。
log
(
chalk
。
green
(
`
${
message
}
`
))
const
successLog
=
message
=>
console
。
log
(
chalk
。
blue
(
`
${
message
}
`
))
const
errorLog
=
error
=>
console
。
log
(
chalk
。
red
(
`
${
error
}
`
))
const
{
vueTemplate
}
=
require
(
‘。/template’
)
const
generateFile
=
(
path
,
data
)
=>
{
if
(
fs
。
existsSync
(
path
))
{
errorLog
(
`
${
path
}
檔案已存在`
)
return
}
return
new
Promise
((
resolve
,
reject
)
=>
{
fs
。
writeFile
(
path
,
data
,
‘utf8’
,
err
=>
{
if
(
err
)
{
errorLog
(
err
。
message
)
reject
(
err
)
}
else
{
resolve
(
true
)
}
})
})
}
log
(
‘請輸入要生成的頁面元件名稱、會生成在 views/目錄下’
)
let
componentName
=
‘’
process
。
stdin
。
on
(
‘data’
,
async
chunk
=>
{
const
inputName
=
String
(
chunk
)。
trim
()。
toString
()
/**
* Vue頁面元件路徑
*/
let
componentVueName
=
resolve
(
‘。。/src/views’
,
inputName
)
// 如果不是以 。vue 結尾的話,自動加上
if
(
!
componentVueName
。
endsWith
(
‘。vue’
))
{
componentVueName
+=
‘。vue’
}
/**
* vue元件目錄路徑
*/
const
componentDirectory
=
path
。
dirname
(
componentVueName
)
const
hasComponentExists
=
fs
。
existsSync
(
componentVueName
)
if
(
hasComponentExists
)
{
errorLog
(
`
${
inputName
}
頁面元件已存在,請重新輸入`
)
return
}
else
{
log
(
`正在生成 component 目錄
${
componentDirectory
}
`
)
await
dotExistDirectoryCreate
(
componentDirectory
)
}
try
{
if
(
inputName
。
includes
(
‘/’
))
{
const
inputArr
=
inputName
。
split
(
‘/’
)
componentName
=
inputArr
[
inputArr
。
length
-
1
]
}
else
{
componentName
=
inputName
}
log
(
`正在生成 vue 檔案
${
componentVueName
}
`
)
await
generateFile
(
componentVueName
,
vueTemplate
(
componentName
))
successLog
(
‘生成成功’
)
}
catch
(
e
)
{
errorLog
(
e
。
message
)
}
process
。
stdin
。
emit
(
‘end’
)
})
process
。
stdin
。
on
(
‘end’
,
()
=>
{
log
(
‘exit’
)
process
。
exit
()
})
function
dotExistDirectoryCreate
(
directory
)
{
return
new
Promise
((
resolve
)
=>
{
mkdirs
(
directory
,
function
()
{
resolve
(
true
)
})
})
}
// 遞迴建立目錄
function
mkdirs
(
directory
,
callback
)
{
var
exists
=
fs
。
existsSync
(
directory
)
if
(
exists
)
{
callback
()
}
else
{
mkdirs
(
path
。
dirname
(
directory
),
function
()
{
fs
。
mkdirSync
(
directory
)
callback
()
})
}
}
配置package。json 新增一個
scripts
指令碼
“new:view”: “node 。/scripts/generateView”
執行
如果使用
npm
的話 就是
npm run new:view
如果使用
yarn
的話 就是
yarn new:view
5。 axios封裝
安裝 axios
npm install axios ——save
// or
yarn add axios
5。1 配置不同的環境
在根目錄新建三個環境變數檔案
分別輸入不同的地址, 比如
dev
就寫
dev
的api地址、
test
就寫
test
的api地址
# // 。env
NODE_ENV = “development”
BASE_URL = “https://easy-mock。com/mock/5c4c50b9888ef15de01bec2c/api”
接著在根目錄中新建一個
vue。config。js
// vue。config。js
module。exports = {
chainWebpack: config => {
// 這裡是對環境的配置,不同環境對應不同的BASE_URL,以便axios的請求地址不同
config。plugin(‘define’)。tap(args => {
args[0][‘process。env’]。BASE_URL = JSON。stringify(process。env。BASE_URL)
return args
})
}
}
然後在
src
目錄下新建一個
api
資料夾,建立一個
index。js
用來配置
axios
的配置資訊
// src/api/index。js
import
axios
from
‘axios’
import
router
from
‘。。/router’
import
{
Message
}
from
‘element-ui’
const
service
=
axios
。
create
({
// 設定超時時間
timeout
:
60000
,
baseURL
:
process
。
env
。
BASE_URL
})
// post請求的時候,我們需要加上一個請求頭,所以可以在這裡進行一個預設的設定
// 即設定post的請求頭為application/x-www-form-urlencoded;charset=UTF-8
service
。
defaults
。
headers
。
post
[
‘Content-Type’
]
=
‘application/x-www-form-urlencoded;charset=UTF-8’
‘
export
default
service
5。2 請求響應封裝
import axios from ’axios‘
import router from ’。。/router‘
import { Message } from ’element-ui‘
const service = axios。create({
// 設定超時時間
timeout: 60000,
baseURL: process。env。BASE_URL
})
/**
* 請求前攔截
* 用於處理需要在請求前的操作
*/
service。interceptors。request。use(config => {
const token = localStorage。getItem(’token‘)
if (token) {
config。headers[’Authorization‘] = token
}
return config
}, (error) => {
return Promise。reject(error)
})
/**
* 請求響應攔截
* 用於處理需要在請求返回後的操作
*/
service。interceptors。response。use(response => {
const responseCode = response。status
// 如果返回的狀態碼為200,說明介面請求成功,可以正常拿到資料
// 否則的話丟擲錯誤
if (responseCode === 200) {
return Promise。resolve(response)
} else {
return Promise。reject(response)
}
}, error => {
// 伺服器返回不是 2 開頭的情況,會進入這個回撥
// 可以根據後端返回的狀態碼進行不同的操作
const responseCode = error。response。status
switch (responseCode) {
// 401:未登入
case 401:
// 跳轉登入頁
router。replace({
path: ’/login‘,
query: {
redirect: router。currentRoute。fullPath
}
})
break
// 403: token過期
case 403:
// 彈出錯誤資訊
Message({
type: ’error‘,
message: ’登入資訊過期,請重新登入‘
})
// 清除token
localStorage。removeItem(’token‘)
// 跳轉登入頁面,並將要瀏覽的頁面fullPath傳過去,登入成功後跳轉需要訪問的頁面
setTimeout(() => {
router。replace({
path: ’/login‘,
query: {
redirect: router。currentRoute。fullPath
}
})
}, 1000)
break
// 404請求不存在
case 404:
Message({
message: ’網路請求不存在‘,
type: ’error‘
})
break
// 其他錯誤,直接丟擲錯誤提示
default:
Message({
message: error。response。data。message,
type: ’error‘
})
}
return Promise。reject(error)
})
export default service
Message
方法是
element-ui
提供的一個訊息提示元件、大家可以根據自己的訊息提示元件進行替換
5。3 斷網處理
在響應攔截中新增處理邏輯
service
。
interceptors
。
response
。
use
(
response
=>
{
const
responseCode
=
response
。
status
// 如果返回的狀態碼為200,說明介面請求成功,可以正常拿到資料
// 否則的話丟擲錯誤
if
(
responseCode
===
200
)
{
return
Promise
。
resolve
(
response
。
data
)
}
else
{
return
Promise
。
reject
(
response
)
}
},
error
=>
{
// 斷網 或者 請求超時 狀態
if
(
!
error
。
response
)
{
// 請求超時狀態
if
(
error
。
message
。
includes
(
’timeout‘
))
{
console
。
log
(
’超時了‘
)
Message
。
error
(
’請求超時,請檢查網路是否連線正常‘
)
}
else
{
// 可以展示斷網元件
console
。
log
(
’斷網了‘
)
Message
。
error
(
’請求失敗,請檢查網路是否已連線‘
)
}
return
}
// 省略其它程式碼 ······
return
Promise
。
reject
(
error
)
})
5。4 封裝圖片上傳
// src/api/index。js
export
const
uploadFile
=
formData
=>
{
const
res
=
service
。
request
({
method
:
’post‘
,
url
:
’/upload‘
,
data
:
formData
,
headers
:
{
’Content-Type‘
:
’multipart/form-data‘
}
})
return
res
}
呼叫
async
uploadFile
(
e
)
{
const
file
=
document
。
getElementById
(
’file‘
)。
files
[
0
]
const
formdata
=
new
FormData
()
formdata
。
append
(
’file‘
,
file
)
await
uploadFile
(
formdata
)
}
5。5 請求 顯示 Loading 效果
let
loading
=
null
service
。
interceptors
。
request
。
use
(
config
=>
{
// 在請求先展示載入框
loading
=
Loading
。
service
({
text
:
’正在載入中……‘
})
// 省略其它程式碼 ······
return
config
},
(
error
)
=>
{
return
Promise
。
reject
(
error
)
})
service
。
interceptors
。
response
。
use
(
response
=>
{
// 請求響應後關閉載入框
if
(
loading
)
{
loading
。
close
()
}
// 省略其它程式碼 ······
},
error
=>
{
// 請求響應後關閉載入框
if
(
loading
)
{
loading
。
close
()
}
// 省略其它程式碼 ······
return
Promise
。
reject
(
error
)
})
6。 巧用 Mixins
6。1 封裝 store 公用方法
假設有這樣一個場景,我們透過
vuex
封裝了獲取新聞列表的
function
import
Vue
from
’vue‘
import
Vuex
from
’vuex‘
import
{
getNewsList
}
from
’。。/api/news‘
Vue
。
use
(
Vuex
)
const
types
=
{
NEWS_LIST
:
’NEWS_LIST‘
}
export
default
new
Vuex
。
Store
({
state
:
{
[
types
。
NEWS_LIST
]
:
[]
},
mutations
:
{
[
types
。
NEWS_LIST
]
:
(
state
,
res
)
=>
{
state
[
types
。
NEWS_LIST
]
=
res
}
},
actions
:
{
[
types
。
NEWS_LIST
]
:
async
({
commit
},
params
)
=>
{
const
res
=
await
getNewsList
(
params
)
return
commit
(
types
。
NEWS_LIST
,
res
)
}
},
getters
:
{
getNewsResponse
(
state
)
{
return
state
[
types
。
NEWS_LIST
]
}
}
})
然後在新聞列表頁,我們透過
mapAction
、
mapGetters
來呼叫
Action
和
getters
我們需要寫上這些程式碼
import { mapActions, mapGetters } from ’vuex‘
computed: {
。。。mapGetters([’getNewsResponse‘])
},
methods: {
。。。mapActions([’NEWS_LIST‘])
}
在假設,在另一個頁面又需要重新呼叫獲取新聞列表的介面,我們又要在寫一遍上面的程式碼對吧?
複製貼上就是幹有木有?
如果介面突然加了一個引數,那豈不是每個要用到這個介面的程式碼都得加這個引數。
複製貼上一時爽,需求一改你就爽
既然是重複的程式碼,我們肯定要複用,這時候
Vue
提供的
Mixin
就起了大作用了 * 封裝 news-mixin。js 在
src
下建立一個
mixins
目錄,用來管理所有的mixins 新建一個
news-mixin。js
import
{
mapActions
,
mapGetters
}
from
’vuex‘
export
default
{
computed
:
{
。。。
mapGetters
([
’getNewsResponse‘
])
},
methods
:
{
。。。
mapActions
([
’NEWS_LIST‘
])
}
}
然後在需要用到的元件中引入這個
mixin
,就能直接呼叫這個方法了。不管多少個頁面,只要引入這個
mixin
,直接就能使用。
需求一改的話,也只需要修改這個
mixin
檔案
// news/index。vue
import
Vue
from
’vue‘
import
newsMixin
from
’@/mixins/news-mixin‘
export
default
{
name
:
’news‘
,
mixins
:
[
newsMixin
],
data
()
{
return
{}
},
async
created
()
{
await
this
。
NEWS_LIST
()
console
。
log
(
this
。
getNewsResponse
)
}
}
6。2 擴充套件
除了封裝
vuex
的公用方法,其實還有很多的東西也能做封裝。例如:
分頁物件
,
表格資料
,
公用方法
、等等就不一一舉例了。可以看github
在多個地方經常使用,就可以考慮封裝成
mixin
,不過請寫好註釋哦。不然就會有人在背後罵你了!!你懂的~~
7。 最佳化
7。1 gzip壓縮
安裝
compression-webpack-plugin
外掛
npm install compression-webpack-plugin ——save-dev
// or
yarn add compression-webpack-plugin ——dev
在 vue。config。js 中新增配置
// vue。config。js
const CompressionPlugin = require(’compression-webpack-plugin‘)
module。exports = {
chainWebpack: config => {
// 這裡是對環境的配置,不同環境對應不同的BASE_URL,以便axios的請求地址不同
config。plugin(’define‘)。tap(args => {
args[0][’process。env‘]。BASE_URL = JSON。stringify(process。env。BASE_URL)
return args
})
if (process。env。NODE_ENV === ’production‘) {
// #region 啟用GZip壓縮
config
。plugin(’compression‘)
。use(CompressionPlugin, {
asset: ’[path]。gz[query]‘,
algorithm: ’gzip‘,
test: new RegExp(’\\。(‘ + [’js‘, ’css‘]。join(’|‘) + ’)$‘),
threshold: 10240,
minRatio: 0。8,
cache: true
})
。tap(args => { })
// #endregion
}
}
}
npm run build
後能看到生成
。gz
檔案就OK了。如果你的伺服器使用nginx的話,nginx也需要配置開啟
GZIP
、下面會講到如何在
nginx
中開啟
GZIP
7。2 第三方庫引用cdn
對於
vue
、
vue-router
、
vuex
、
axios
和
element-ui
等等這些不經常改動的庫、我們讓
webpack
不對他們進行打包,透過
cdn
引入,可以減少程式碼的大小、也可以減少伺服器的頻寬,更能把這些檔案快取到客戶端,客戶端載入的會更快。 * 配置
vue。config。js
const CompressionPlugin = require(’compression-webpack-plugin‘)
module。exports = {
chainWebpack: config => {
// 省略其它程式碼 ······
// #region 忽略生成環境打包的檔案
var externals = {
vue: ’Vue‘,
axios: ’axios‘,
’element-ui‘: ’ELEMENT‘,
’vue-router‘: ’VueRouter‘,
vuex: ’Vuex‘
}
config。externals(externals)
const cdn = {
css: [
// element-ui css
’//unpkg。com/element-ui/lib/theme-chalk/index。css‘
],
js: [
// vue
’//cdn。staticfile。org/vue/2。5。22/vue。min。js‘,
// vue-router
’//cdn。staticfile。org/vue-router/3。0。2/vue-router。min。js‘,
// vuex
’//cdn。staticfile。org/vuex/3。1。0/vuex。min。js‘,
// axios
’//cdn。staticfile。org/axios/0。19。0-beta。1/axios。min。js‘,
// element-ui js
’//unpkg。com/element-ui/lib/index。js‘
]
}
config。plugin(’html‘)
。tap(args => {
args[0]。cdn = cdn
return args
})
// #endregion
}
}
}
修改
index。html
<!——public/index。html——>
<!DOCTYPE html>
<
html
lang
=
“en”
>
<
head
>
<
meta
charset
=
“utf-8”
>
<
meta
http-equiv
=
“X-UA-Compatible”
content
=
“IE=edge”
>
<
meta
name
=
“viewport”
content
=
“width=device-width,initial-scale=1。0”
>
<
link
rel
=
“icon”
href
=
“<%= BASE_URL %>favicon。ico”
>
<
% if (process。env。NODE_ENV === ’production‘) { %>
<
% for(var css of htmlWebpackPlugin。options。cdn。css) { %>
<
link
href
=
“<%=css%>”
rel
=
“preload”
as
=
“style”
>
<
link
rel
=
“stylesheet”
href
=
“<%=css%>”
as
=
“style”
>
<
% } %>
<
% for(var js of htmlWebpackPlugin。options。cdn。js) { %>
<
link
href
=
“<%=js%>”
rel
=
“preload”
as
=
“script”
>
<
script
src
=
“<%=js%>”
>
script
>
<
% } %>
<
% } %>
<
title
>
vue-cli3-project
title
>
head
>
<
body
>
<
noscript
>
<
strong
>
We’re sorry but vue-cli3-project doesn‘t work properly without JavaScript enabled。 Please enable it to continue。
strong
>
noscript
>
<
div
id
=
“app”
>
div
>
<!—— built files will be auto injected ——>
body
>
html
>
7。3 全站cdn
我們已經把第三方庫使用
cdn
替代了,那麼我們
build
後生成的
js
,
css
之類的檔案能否也用
cdn
呢?
申請自己的cdn域名
要想把自己的資源上傳到
cdn
上,前提是得有自己的
cdn
域名,如果沒有的話,可以到七牛雲官網上註冊申請一個 1。 註冊七牛雲賬號 2。 到七牛雲物件儲存模組中新建儲存空間 3。 輸入儲存空間資訊
4。 確定建立 5。 建立成功後會跳轉到這個儲存空間的控制檯頁面
6。 其中有個域名就是你的測試域名 7。 我們可以在內容管理那上傳我們的
js
、
css
之類的檔案、不過我們的檔案那麼多,一個一個上傳明顯不合理。要你你也不幹。
這時候,這些批次又重複的操作應該由我們的
node
出馬,讓我們來透過
node
來批次上傳我們的資原始檔
將生成的js、css資源上傳到七牛cdn
在七牛雲官網的文件中心有介紹如何透過
node
上傳檔案、感興趣的人可以自己去研究一下。
檢視
AccessKey
和
SecretKey
,在你的個人面板 -> 秘鑰管理 ,這兩個秘鑰待會會用到
安裝需要的外掛
npm install qiniu glob mime ——save-dev
在
scripts
目錄下建立一個
upcdn。js
檔案
// /scripts/upcdn。js
const
qiniu
=
require
(
’qiniu‘
)
const
glob
=
require
(
’glob‘
)
const
mime
=
require
(
’mime‘
)
const
path
=
require
(
’path‘
)
const
isWindow
=
/^win/
。
test
(
process
。
platform
)
let
pre
=
path
。
resolve
(
__dirname
,
’。。/dist/‘
)
+
(
isWindow
?
’\\‘
:
’‘
)
const
files
=
glob
。
sync
(
`
${
path
。
join
(
__dirname
,
’。。/dist/**/*。?(js|css|map|png|jpg|svg|woff|woff2|ttf|eot)‘
)
}
`
)
pre
=
pre
。
replace
(
/\\/g
,
’/‘
)
const
options
=
{
scope
:
’source‘
// 空間物件名稱
}
var
config
=
{
qiniu
:
{
accessKey
:
’‘
,
// 個人中心 秘鑰管理裡的 AccessKey
secretKey
:
’‘
,
// 個人中心 秘鑰管理裡的 SecretKey
bucket
:
options
。
scope
,
domain
:
’http://ply4cszel。bkt。clouddn。com‘
}
}
var
accessKey
=
config
。
qiniu
。
accessKey
var
secretKey
=
config
。
qiniu
。
secretKey
var
mac
=
new
qiniu
。
auth
。
digest
。
Mac
(
accessKey
,
secretKey
)
var
putPolicy
=
new
qiniu
。
rs
。
PutPolicy
(
options
)
var
uploadToken
=
putPolicy
。
uploadToken
(
mac
)
var
cf
=
new
qiniu
。
conf
。
Config
({
zone
:
qiniu
。
zone
。
Zone_z2
})
var
formUploader
=
new
qiniu
。
form_up
。
FormUploader
(
cf
)
async
function
uploadFileCDN
(
files
)
{
files
。
map
(
async
file
=>
{
const
key
=
getFileKey
(
pre
,
file
)
try
{
await
uploadFIle
(
key
,
file
)
console
。
log
(
`上傳成功 key:
${
key
}
`
)
}
catch
(
err
)
{
console
。
log
(
’error‘
,
err
)
}
})
}
async
function
uploadFIle
(
key
,
localFile
)
{
const
extname
=
path
。
extname
(
localFile
)
const
mimeName
=
mime
。
getType
(
extname
)
const
putExtra
=
new
qiniu
。
form_up
。
PutExtra
({
mimeType
:
mimeName
})
return
new
Promise
((
resolve
,
reject
)
=>
{
formUploader
。
putFile
(
uploadToken
,
key
,
localFile
,
putExtra
,
function
(
respErr
,
respBody
,
respInfo
)
{
if
(
respErr
)
{
reject
(
respErr
)
}
resolve
({
respBody
,
respInfo
})
})
})
}
function
getFileKey
(
pre
,
file
)
{
if
(
file
。
indexOf
(
pre
)
>
-
1
)
{
const
key
=
file
。
split
(
pre
)[
1
]
return
key
。
startsWith
(
’/‘
)
?
key
。
substring
(
1
)
:
key
}
return
file
}
(
async
()
=>
{
console
。
time
(
’上傳檔案到cdn‘
)
await
uploadFileCDN
(
files
)
console
。
timeEnd
(
’上傳檔案到cdn‘
)
})()
修改 publicPath
修改
vue。config。js
的配置資訊,讓其
publicPath
指向我們
cdn
的域名
const IS_PROD = process。env。NODE_ENV === ’production‘
const cdnDomian = ’http://ply4cszel。bkt。clouddn。com‘
module。exports = {
publicPath: IS_PROD ? cdnDomian : ’/‘,
// 省略其它程式碼 ·······
}
修改package。json配置
修改package。json配置,使我們
build
完成後自動上傳資原始檔到
cdn伺服器
“build”: “vue-cli-service build ——mode prod && node 。/scripts/upcdn。js”,
執行檢視效果
npm run build
然後到你的
cdn
控制檯的內容管理看看檔案是否已經上傳成功
8。 docker部署
這邊使用的是
centOS7
環境,不過使用的是不同的系統,可以參考一下其它系統的安裝方法
8。1 安裝docker
更新軟體庫
yum update -y
安裝docker
yum install docker
啟動docker服務
service docker start
安裝docker-compose
// 安裝epel源
yum install -y epel-release
// 安裝docker-compose
yum install docker-compose
8。2 編寫docker-compose。yaml
version: ’2。1‘
services:
nginx:
restart: always
image: nginx
volumes:
#~ /var/local/nginx/nginx。conf為本機目錄, /etc/nginx為容器目錄
- /var/local/nginx/nginx。conf:/etc/nginx/nginx。conf
#~ /var/local/app/dist 為本機 build 後的dist目錄, /usr/src/app為容器目錄,
- /var/local/app/dist:/usr/src/app
ports:
- 80:80
privileged: true
8。3 編寫 nginx。conf 配置
#user nobody;
worker_processes 2;
#工作模式及連線數上線
events {
worker_connections 1024; #單個工作程序 處理程序的最大併發數
}
http {
include mime。types;
default_type application/octet-stream;
#sendfile 指令指定 nginx 是否呼叫 sendfile 函式(zero copy 方式)來輸出檔案,對於普通應用,
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
# 開啟GZIP
gzip on;
# # 監聽 80 埠,轉發請求到 3000 埠
server {
#監聽埠
listen 80;
#編碼格式
charset utf-8;
# 前端靜態檔案資源
location / {
root /usr/src/app;
index index。html index。htm;
try_files $uri $uri/ @rewrites;
}
# 配置如果匹配不到資源,將url指向 index。html, 在 vue-router 的 history 模式下使用,就不會顯示404
location @rewrites {
rewrite ^(。*)$ /index。html last;
}
error_page 500 502 503 504 /50x。html;
location = /50x。html {
root html;
}
}
}
8。4 執行 docker-compose
docker-compose -d up
8。5 docker + jenkins 自動化部署
使用
docker
+
jenkins
能實現程式碼提交到github後自動部署環境、這個要講起來內容太多,有興趣的可以看我這一篇文章
從零搭建docker+jenkins+node。js自動化部署環境
6。 擴充套件
使用pm2自動化部署node專案
透過vue-cli3構建一個SSR應用程式
如果大家還有什麼更好的實踐方式,歡迎評論區指教!!
專案地址 vue-cli3-project 歡迎 star
原文地址
https://www。
ccode。live/lentoo/list/
9?from=art
歡迎關注
歡迎關注公眾號“
碼上開發
”,每天分享最新技術資訊