项目地址 vue-cli3-project 欢迎 starcss
原文地址 www.ccode.live/lentoo/list…html
相信大部分人都已经知道怎么建立项目的,能够跳过这一节,看下一节。前端
# 全局安装 vue-cli脚手架
npm install -g @vue/cli
复制代码
等待安装完成后开始下一步vue
vue create vue-cli3-project
复制代码
babel
,
eslint
咱们选择更多功能 Manually select features
node
回车后来到选择插件webpack
这边选择了(Babel、Router、Vuex、Css预处理器、Linter / Formatter 格式检查、Unit测试框架)ios
是否使用 history
模式的路由 (Yes)nginx
这边选择 ESLint + Standard config
,我的比较喜欢这个代码规范 git
eslint
校验选择(Lint on save)保存是检查github
若是你正在使用的vscode编辑器的话,能够配置eslint插件进行代码自动格式化
选是的话,下次建立一个vue项目,能够直接使用这个预设文件,而无需再进行配置。
等待依赖完成
在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
中就能够了
在 Vue
项目中使用路由,相信想熟的人已经很熟悉怎么使用了,要新增一个页面的话,须要到路由配置中配置该页面的信息。
若是页面愈来愈多的话,那么如何让咱们的路由更简洁呢?
根据不一样的业务模块进行拆分路由
在每一个子模块中导出一个路由配置数组
在根 index.js
中导入全部子模块
当咱们的业务愈来愈庞大,每次新增业务模块的时候,咱们都要在路由下面新增一个子路由模块,而后在index.js
中导入。
那么如何简化这种操做呢?
经过上面的自动扫描全局组件注册,咱们也能够实现自动扫描子模块路由并导入
做为前端开发者,放着 node
这么好用的东西若是不能运用起来,岂不是很浪费?
.vue
文件,而后写
template
、
script
、
style
这些东西,而后新建一个
index.js
、导出vue组件、虽然有插件能实现自动补全,但仍是很麻烦有木有。
那么咱们能不能经过node
来帮助咱们干这些事情呢?只要告诉node
帮我生成的组件名称就好了。其它的事情让node
来干
chalk
,这个插件能让咱们的控制台输出语句有各类颜色区分npm install chalk --save-dev
复制代码
在根目录中建立一个 scripts
文件夹,
新增一个generateComponent.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`
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()
})
}
}
复制代码
"new:comp": "node ./scripts/generateComponent"
复制代码
若是使用 npm
的话 就是 npm run new:comp
若是使用 yarn
的话 就是 yarn new:comp
经过上面的逻辑代码咱们能够经过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()
})
}
}
复制代码
scripts
脚本"new:view": "node ./scripts/generateView"
复制代码
若是使用 npm
的话 就是 npm run new:view
若是使用 yarn
的话 就是 yarn new:view
npm install axios --save
// or
yarn add axios
复制代码
在根目录新建三个环境变量文件
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
复制代码
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
提供的一个消息提示组件、你们能够根据本身的消息提示组件进行替换
在响应拦截中添加处理逻辑
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)
})
复制代码
// 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)
}
复制代码
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)
})
复制代码
假设有这样一个场景,咱们经过 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
就起了大做用了
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)
}
}
复制代码
除了封装 vuex
的公用方法,其实还有不少的东西也能作封装。例如:分页对象
,表格数据
,公用方法
、等等就不一一举例了。能够看github
在多个地方常用,就能够考虑封装成mixin
,不过请写好注释哦。否则就会有人在背后骂你了!!你懂的~~
compression-webpack-plugin
插件npm install compression-webpack-plugin --save-dev
// or
yarn add compression-webpack-plugin --dev
复制代码
// 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
对于 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>
复制代码
咱们已经把第三方库使用cdn
替代了,那么咱们build
后生成的 js
,css
之类的文件可否也用cdn
呢?
要想把本身的资源上传到cdn
上,前提是得有本身的cdn
域名,若是没有的话,能够到七牛云官网上注册申请一个
js
、css
之类的文件、不过咱们的文件那么多,一个一个上传明显不合理。要你你也不干。这时候,这些批量又重复的操做应该由咱们的node
出马,让咱们来经过 node
来批量上传咱们的资源文件
在七牛云官网的文档中心有介绍如何经过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')
})()
复制代码
修改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配置,使咱们build
完成后自动上传资源文件到cdn服务器
"build": "vue-cli-service build --mode prod && node ./scripts/upcdn.js",
复制代码
npm run build
cdn
控制台的内容管理看看文件是否已经上传成功
这边使用的是 centOS7
环境,不过使用的是不一样的系统,能够参考一下其它系统的安装方法
yum update -y
复制代码
yum install docker
复制代码
service docker start
复制代码
// 安装epel源
yum install -y epel-release
// 安装docker-compose
yum install docker-compose
复制代码
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
复制代码
#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;
}
}
}
复制代码
docker-compose -d up
复制代码
使用docker
+ jenkins
能实现代码提交到github后自动部署环境、这个要讲起来内容太多,有兴趣的能够看我这一篇文章
从零搭建docker+jenkins+node.js自动化部署环境
若是你们还有什么更好的实践方式,欢迎评论区指教!!
项目地址 vue-cli3-project 欢迎 star
欢迎关注公众号“码上开发”,天天分享最新技术资讯