溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

發(fā)布時間:2020-09-07 10:58:54 來源:腳本之家 閱讀:242 作者:lentoo 欄目:web開發(fā)

1. 創(chuàng)建一個vue項目

相信大部分人都已經(jīng)知道怎么創(chuàng)建項目的,可以跳過這一節(jié),看下一節(jié)。

1.1 安裝@vue/cli

# 全局安裝 vue-cli腳手架
npm install -g @vue/cli

等待安裝完成后開始下一步

1.2 初始化項目

vue create vue-cli3-project

(1)選擇一個預(yù)設(shè)

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

可以選擇默認(rèn)預(yù)設(shè),默認(rèn)預(yù)設(shè)包含了 babel , eslint

我們選擇更多功能 Manually select features

回車后來到選擇插件

(2)插件選擇

這邊選擇了(Babel、Router、Vuex、Css預(yù)處理器、Linter / Formatter 格式檢查、Unit測試框架)

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

(3)路由模式選擇

是否使用 history 模式的路由 (Yes)

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

(4)選擇一個css預(yù)處理器 (Sass/SCSS)

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

(5)選擇一個eslint配置

這邊選擇 ESLint + Standard config ,個人比較喜歡這個代碼規(guī)范

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

(6)選擇什么時候進(jìn)行 eslint 校驗

選擇(Lint on save)保存是檢查

如果你正在使用的vscode編輯器的話,可以配置eslint插件進(jìn)行代碼自動格式化

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

7. 選擇測試框架 (Mocha + Chai)

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

8. 選擇將這些配置文件寫入到什么地方 (In dedicated config files)

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

9. 是否保存這份預(yù)設(shè)配置?(y)

選是的話,下次創(chuàng)建一個vue項目,可以直接使用這個預(yù)設(shè)文件,而無需再進(jìn)行配置。

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

等待依賴完成

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

2. 全局組件自動注冊

components 目錄下創(chuàng)建一個 global 目錄,里面放置一些需要全局注冊的組件。

index.js 作用只要是引入 main.vue ,導(dǎo)出組件對象

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

components 中創(chuàng)建一個 index.js,用來掃描全局對象并自動注冊。

// components/index.js
import Vue from 'vue'

// 自動加載 global 目錄下的 .js 結(jié)尾的文件
const componentsContext = require.context('./global', true, /\.js$/)

componentsContext.keys().forEach(component => {
 const componentConfig = componentsContext(component)
 /**
 * 兼容 import export 和 require module.export 兩種規(guī)范
 */
 const ctrl = componentConfig.default || componentConfig
 Vue.component(ctrl.name, ctrl)
})

最后在入口文件 main.js 中導(dǎo)入這個 index.js 中就可以了

3.路由自動引入

Vue 項目中使用路由,相信想熟的人已經(jīng)很熟悉怎么使用了,要新增一個頁面的話,需要到路由配置中配置該頁面的信息。

如果頁面越來越多的話,那么如何讓我們的路由更簡潔呢?

3.1 拆分路由

根據(jù)不同的業(yè)務(wù)模塊進(jìn)行拆分路由

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

在每個子模塊中導(dǎo)出一個路由配置數(shù)組

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

在根 index.js 中導(dǎo)入所有子模塊

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

3.2 自動掃描子模塊路由并導(dǎo)入

當(dāng)我們的業(yè)務(wù)越來越龐大,每次新增業(yè)務(wù)模塊的時候,我們都要在路由下面新增一個子路由模塊,然后在 index.js 中導(dǎo)入。

那么如何簡化這種操作呢?

通過上面的自動掃描全局組件注冊,我們也可以實現(xiàn)自動掃描子模塊路由并導(dǎo)入

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

4. 通過node來生成組件

作為前端開發(fā)者,放著 node 這么好用的東西如果不能運用起來,豈不是很浪費?

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

雖然我們通過上面已經(jīng)實現(xiàn)了組件的自動注冊,不過每次新建組件的時候,都要創(chuàng)建一個目錄,然后新增一個 .vue 文件,然后寫 template 、 script 、 style 這些東西,然后新建一個 index.js

、導(dǎo)出vue組件、雖然有插件能實現(xiàn)自動補全,但還是很麻煩有木有。

那么我們能不能通過 node 來幫助我們干這些事情呢?只要告訴 node 幫我生成的組件名稱就行了。其它的事情讓 node 來干

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

4.1 通過node來生成組件

安裝一下 chalk ,這個插件能讓我們的控制臺輸出語句有各種顏色區(qū)分

npm install chalk --save-dev

在根目錄中創(chuàng)建一個 scripts 文件夾,

新增一個 generateComponent.js 文件,放置生成組件的代碼、

新增一個 template.js 文件,放置組件模板的代碼

template.js

// template.js
module.exports = {
 vueTemplate: compoenntName => {
 return `<template>
 <div class="${compoenntName}">
 ${compoenntName}組件
 </div>
</template>
<script>
export default {
 name: '${compoenntName}'
}
</script>
<style lang="scss" scoped>
.${compoenntName} {

}
</style>
`
 },
 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)
 })
 })
}

// 遞歸創(chuàng)建目錄
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"

執(zhí)行

如果使用 npm 的話 就是 npm run new:comp

如果使用 yarn 的話 就是 yarn new:comp

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

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 結(jié)尾的話,自動加上
 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)
 })
 })
}

// 遞歸創(chuàng)建目錄
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"

執(zhí)行

如果使用 npm 的話 就是 npm run new:view

如果使用 yarn 的話 就是 yarn new:view

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

5. axios封裝 安裝 axios

npm install axios --save
// or
yarn add axios

5.1 配置不同的環(huán)境

在根目錄新建三個環(huán)境變量文件

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

分別輸入不同的地址, 比如 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 => {
 // 這里是對環(huán)境的配置,不同環(huán)境對應(yīng)不同的BASE_URL,以便axios的請求地址不同
 config.plugin('define').tap(args => {
 args[0]['process.env'].BASE_URL = JSON.stringify(process.env.BASE_URL)
 return args
 })
 }
}

然后在 src 目錄下新建一個 api 文件夾,創(chuàng)建一個 index.js 用來配置 axios 的配置信息

// src/api/index.js
import axios from 'axios'
import router from '../router'
import { Message } from 'element-ui'
const service = axios.create({
 // 設(shè)置超時時間
 timeout: 60000,
 baseURL: process.env.BASE_URL
})
// post請求的時候,我們需要加上一個請求頭,所以可以在這里進(jìn)行一個默認(rèn)的設(shè)置
// 即設(shè)置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 請求響應(yīng)封裝

import axios from 'axios'
import router from '../router'
import { Message } from 'element-ui'
const service = axios.create({
 // 設(shè)置超時時間
 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)
})
/**
 * 請求響應(yīng)攔截
 * 用于處理需要在請求返回后的操作
 */
service.interceptors.response.use(response => {
 const responseCode = response.status
 // 如果返回的狀態(tài)碼為200,說明接口請求成功,可以正常拿到數(shù)據(jù)
 // 否則的話拋出錯誤
 if (responseCode === 200) {
 return Promise.resolve(response)
 } else {
 return Promise.reject(response)
 }
}, error => {
 // 服務(wù)器返回不是 2 開頭的情況,會進(jìn)入這個回調(diào)
 // 可以根據(jù)后端返回的狀態(tài)碼進(jìn)行不同的操作
 const responseCode = error.response.status
 switch (responseCode) {
 // 401:未登錄
 case 401:
 // 跳轉(zhuǎn)登錄頁
 router.replace({
 path: '/login',
 query: {
 redirect: router.currentRoute.fullPath
 }
 })
 break
 // 403: token過期
 case 403:
 // 彈出錯誤信息
 Message({
 type: 'error',
 message: '登錄信息過期,請重新登錄'
 })
 // 清除token
 localStorage.removeItem('token')
 // 跳轉(zhuǎn)登錄頁面,并將要瀏覽的頁面fullPath傳過去,登錄成功后跳轉(zhuǎn)需要訪問的頁面
 setTimeout(() => {
 router.replace({
 path: '/login',
 query: {
 redirect: router.currentRoute.fullPath
 }
 })
 }, 1000)
 break
 // 404請求不存在
 case 404:
 Message({
 message: '網(wǎng)絡(luò)請求不存在',
 type: 'error'
 })
 break
 // 其他錯誤,直接拋出錯誤提示
 default:
 Message({
 message: error.response.data.message,
 type: 'error'
 })
 }
 return Promise.reject(error)
})

export default service

Message 方法是 element-ui 提供的一個消息提示組件、大家可以根據(jù)自己的消息提示組件進(jìn)行替換

5.3 斷網(wǎng)處理

在響應(yīng)攔截中添加處理邏輯

service.interceptors.response.use(response => {
 const responseCode = response.status
 // 如果返回的狀態(tài)碼為200,說明接口請求成功,可以正常拿到數(shù)據(jù)
 // 否則的話拋出錯誤
 if (responseCode === 200) {
 return Promise.resolve(response.data)
 } else {
 return Promise.reject(response)
 }
}, error => {
 // 斷網(wǎng) 或者 請求超時 狀態(tài)
 if (!error.response) {
 // 請求超時狀態(tài)
 if (error.message.includes('timeout')) {
 console.log('超時了')
 Message.error('請求超時,請檢查網(wǎng)絡(luò)是否連接正常')
 } else {
 // 可以展示斷網(wǎng)組件
 console.log('斷網(wǎng)了')
 Message.error('請求失敗,請檢查網(wǎng)絡(luò)是否已連接')
 }
 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
}

調(diào)用

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 => {
 // 請求響應(yīng)后關(guān)閉加載框
 if (loading) {
 loading.close()
 }
 // 省略其它代碼 ······
}, error => {
 // 請求響應(yīng)后關(guān)閉加載框
 if (loading) {
 loading.close()
 }
 // 省略其它代碼 ······ 
 return Promise.reject(error)
})

6. 巧用 Mixins

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

6.1 封裝 store 公用方法

假設(shè)有這樣一個場景,我們通過 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 來調(diào)用 Actiongetters 我們需要寫上這些代碼

import { mapActions, mapGetters } from 'vuex'

computed: {
 ...mapGetters(['getNewsResponse'])
},
methods: {
 ...mapActions(['NEWS_LIST'])
}

在假設(shè),在另一個頁面又需要重新調(diào)用獲取新聞列表的接口,我們又要在寫一遍上面的代碼對吧?

復(fù)制粘貼就是干有木有?

如果接口突然加了一個參數(shù),那豈不是每個要用到這個接口的代碼都得加這個參數(shù)。

復(fù)制粘貼一時爽,需求一改你就爽

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

既然是重復(fù)的代碼,我們肯定要復(fù)用,這時候 Vue 提供的 Mixin 就起了大作用了

封裝 news-mixin.js 在 src 下創(chuàng)建一個 mixins 目錄,用來管理所有的mixins 新建一個 news-mixin.js

import { mapActions, mapGetters } from 'vuex'
export default {
 computed: {
 ...mapGetters(['getNewsResponse'])
 },
 methods: {
 ...mapActions(['NEWS_LIST'])
 }
}

然后在需要用到的組件中引入這個 mixin ,就能直接調(diào)用這個方法了。不管多少個頁面,只要引入這個 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 擴(kuò)展

除了封裝 vuex 的公用方法,其實還有很多的東西也能做封裝。例如: 分頁對象 , 表格數(shù)據(jù) , 公用方法 、等等就不一一舉例了。可以看github

在多個地方經(jīng)常使用,就可以考慮封裝成 mixin ,不過請寫好注釋哦。不然就會有人在背后罵你了!!你懂的~~

7. 優(yōu)化

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 => {
 // 這里是對環(huán)境的配置,不同環(huán)境對應(yīng)不同的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了。如果你的服務(wù)器使用nginx的話,nginx也需要配置開啟 GZIP 、下面會講到如何在 nginx 中開啟 GZIP

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

7.2 第三方庫引用cdn

對于 vue 、 vue-router 、 vuexaxioselement-ui 等等這些不經(jīng)常改動的庫、我們讓 webpack 不對他們進(jìn)行打包,通過 cdn 引入,可以減少代碼的大小、也可以減少服務(wù)器的帶寬,更能把這些文件緩存到客戶端,客戶端加載的會更快。

配置 vue.config.js

const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
 chainWebpack: config => {
 // 省略其它代碼 ······
 // #region 忽略生成環(huán)境打包的文件

 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

我們已經(jīng)把第三方庫使用 cdn 替代了,那么我們 build 后生成的 js , css 之類的文件能否也用 cdn 呢?

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

申請自己的cdn域名

要想把自己的資源上傳到 cdn 上,前提是得有自己的 cdn 域名,如果沒有的話,可以到七牛云官網(wǎng)上注冊申請一個

  • 注冊七牛云賬號
  • 到七牛云對象存儲模塊中新建存儲空間
  • 輸入存儲空間信息

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

確定創(chuàng)建

創(chuàng)建成功后會跳轉(zhuǎn)到這個存儲空間的控制臺頁面

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

其中有個域名就是你的測試域名

我們可以在內(nèi)容管理那上傳我們的 js 、 css 之類的文件、不過我們的文件那么多,一個一個上傳明顯不合理。要你你也不干。

這時候,這些批量又重復(fù)的操作應(yīng)該由我們的 node 出馬,讓我們來通過 node 來批量上傳我們的資源文件

將生成的js、css資源上傳到七牛cdn

在七牛云官網(wǎng)的文檔中心有介紹如何通過 node 上傳文件、感興趣的人可以自己去研究一下。

查看 AccessKeySecretKey ,在你的個人面板 -> 秘鑰管理 ,這兩個秘鑰待會會用到

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

安裝需要的插件

npm install qiniu glob mime --save-dev

scripts 目錄下創(chuàng)建一個 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服務(wù)器

"build": "vue-cli-service build --mode prod && node ./scripts/upcdn.js",

運行查看效果

npm run build

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

然后到你的 cdn

控制臺的內(nèi)容管理看看文件是否已經(jīng)上傳成功

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

8. docker部署

這邊使用的是 centOS7 環(huán)境,不過使用的是不同的系統(tǒng),可以參考一下其它系統(tǒng)的安裝方法

8.1 安裝docker 更新軟件庫

yum update -y

安裝docker

yum install docker

啟動docker服務(wù)

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為本機(jī)目錄, /etc/nginx為容器目錄
 - /var/local/nginx/nginx.conf:/etc/nginx/nginx.conf
 #~ /var/local/app/dist 為本機(jī) 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;

#工作模式及連接數(shù)上線
events {
 worker_connections 1024; #單個工作進(jìn)程 處理進(jìn)程的最大并發(fā)數(shù)
}

http {
 include mime.types;
 default_type application/octet-stream;
 #sendfile 指令指定 nginx 是否調(diào)用 sendfile 函數(shù)(zero copy 方式)來輸出文件,對于普通應(yīng)用,
 sendfile on;
 #tcp_nopush on;

 #keepalive_timeout 0;
 keepalive_timeout 65;

 # 開啟GZIP
 gzip on;

 # # 監(jiān)聽 80 端口,轉(zhuǎn)發(fā)請求到 3000 端口
 server {
 #監(jiān)聽端口
 listen 80;
 #編碼格式
 charset utf-8;

 # 前端靜態(tài)文件資源
 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 執(zhí)行 docker-compose

docker-compose -d up

vue-cli3 項目從搭建優(yōu)化到docker部署的方法

8.5 docker + jenkins 自動化部署

使用 docker + jenkins 能實現(xiàn)代碼提交到github后自動部署環(huán)境、這個要講起來內(nèi)容太多,有興趣的可以看我這一篇文章

從零搭建docker+jenkins+node.js自動化部署環(huán)境

擴(kuò)展

使用pm2自動化部署node項目
通過vue-cli3構(gòu)建一個SSR應(yīng)用程序

項目地址 vue-cli3-project 歡迎 star

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI