欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

vue-cli3 项目从搭建优化到docker部署

程序员文章站 2022-07-09 15:02:19
项目地址 "vue cli3 project" 欢迎 star 原文地址 "https://www.ccode.live/lentoo/list/9?from=art" 1. 创建一个vue项目 相信大部分人都已经知道怎么创建项目的,可以跳过这一节,看下一节。 1.1 安装@vue/cli 等待安装 ......

项目地址 欢迎 star

原文地址

1. 创建一个vue项目

相信大部分人都已经知道怎么创建项目的,可以跳过这一节,看下一节。

1.1 安装@vue/cli

# 全局安装 vue-cli脚手架
npm install -g @vue/cli

等待安装完成后开始下一步

1.2 初始化项目

vue create vue-cli3-project
  1. 选择一个预设

vue-cli3 项目从搭建优化到docker部署
可以选择默认预设,默认预设包含了babel,eslint

我们选择更多功能 manually select features

回车后来到选择插件

  1. 插件选择

这边选择了(babel、router、vuex、css预处理器、linter / formatter 格式检查、unit测试框架)

vue-cli3 项目从搭建优化到docker部署

  1. 路由模式选择

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

vue-cli3 项目从搭建优化到docker部署

  1. 选择一个css预处理器 (sass/scss)

vue-cli3 项目从搭建优化到docker部署

  1. 选择一个eslint配置

这边选择 eslint + standard config,个人比较喜欢这个代码规范
vue-cli3 项目从搭建优化到docker部署

  1. 选择什么时候进行 eslint 校验

选择(lint on save)保存是检查

如果你正在使用的vscode编辑器的话,可以配置eslint插件进行代码自动格式化
vue-cli3 项目从搭建优化到docker部署

  1. 选择测试框架 (mocha + chai)
    vue-cli3 项目从搭建优化到docker部署
  2. 选择将这些配置文件写入到什么地方 (in dedicated config files)

vue-cli3 项目从搭建优化到docker部署

  1. 是否保存这份预设配置?(y)

选是的话,下次创建一个vue项目,可以直接使用这个预设文件,而无需再进行配置。
vue-cli3 项目从搭建优化到docker部署

等待依赖完成

vue-cli3 项目从搭建优化到docker部署

2. 全局组件自动注册

components目录下创建一个global目录,里面放置一些需要全局注册的组件。

index.js作用只要是引入main.vue,导出组件对象

vue-cli3 项目从搭建优化到docker部署
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 拆分路由

根据不同的业务模块进行拆分路由

vue-cli3 项目从搭建优化到docker部署

在每个子模块中导出一个路由配置数组

vue-cli3 项目从搭建优化到docker部署

在根 index.js中导入所有子模块

vue-cli3 项目从搭建优化到docker部署

3.2 自动扫描子模块路由并导入

当我们的业务越来越庞大,每次新增业务模块的时候,我们都要在路由下面新增一个子路由模块,然后在index.js中导入。

那么如何简化这种操作呢?

通过上面的自动扫描全局组件注册,我们也可以实现自动扫描子模块路由并导入

vue-cli3 项目从搭建优化到docker部署

4. 通过node来生成组件

作为前端开发者,放着 node这么好用的东西如果不能运用起来,岂不是很浪费?

vue-cli3 项目从搭建优化到docker部署
虽然我们通过上面已经实现了组件的自动注册,不过每次新建组件的时候,都要创建一个目录,然后新增一个.vue文件,然后写templatescriptstyle这些东西,然后新建一个index.js、导出vue组件、虽然有插件能实现自动补全,但还是很麻烦有木有。

那么我们能不能通过node来帮助我们干这些事情呢?只要告诉node帮我生成的组件名称就行了。其它的事情让node来干

vue-cli3 项目从搭建优化到docker部署

4.1 通过node来生成组件

  • 安装一下chalk,这个插件能让我们的控制台输出语句有各种颜色区分
npm install chalk --save-dev

在根目录中创建一个 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)
    })
  })
}

// 递归创建目录
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

vue-cli3 项目从搭建优化到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 结尾的话,自动加上
  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
vue-cli3 项目从搭建优化到docker部署

5. axios封装

  • 安装 axios
npm install axios --save
// or
yarn add axios

5.1 配置不同的环境

在根目录新建三个环境变量文件
vue-cli3 项目从搭建优化到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 => {
    // 这里是对环境的配置,不同环境对应不同的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

vue-cli3 项目从搭建优化到docker部署

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]
    }
  }
})

然后在新闻列表页,我们通过 mapactionmapgetters来调用actiongetters
我们需要写上这些代码

import { mapactions, mapgetters } from 'vuex'

computed: {
    ...mapgetters(['getnewsresponse'])
},
methods: {
    ...mapactions(['news_list'])
}

在假设,在另一个页面又需要重新调用获取新闻列表的接口,我们又要在写一遍上面的代码对吧?

复制粘贴就是干有木有?

如果接口突然加了一个参数,那岂不是每个要用到这个接口的代码都得加这个参数。

复制粘贴一时爽,需求一改你就爽

vue-cli3 项目从搭建优化到docker部署

既然是重复的代码,我们肯定要复用,这时候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 的公用方法,其实还有很多的东西也能做封装。例如:分页对象,表格数据,公用方法、等等就不一一举例了。可以看

在多个地方经常使用,就可以考虑封装成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

vue-cli3 项目从搭建优化到docker部署

7.2 第三方库引用cdn

对于 vuevue-routervuexaxioselement-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呢?

vue-cli3 项目从搭建优化到docker部署

申请自己的cdn域名

要想把自己的资源上传到cdn上,前提是得有自己的cdn域名,如果没有的话,可以到上注册申请一个

  1. 注册七牛云账号
  2. 到七牛云对象存储模块中新建存储空间
  3. 输入存储空间信息
    vue-cli3 项目从搭建优化到docker部署
  4. 确定创建
  5. 创建成功后会跳转到这个存储空间的控制台页面
    vue-cli3 项目从搭建优化到docker部署
  6. 其中有个域名就是你的测试域名
  7. 我们可以在内容管理那上传我们的jscss之类的文件、不过我们的文件那么多,一个一个上传明显不合理。要你你也不干。

这时候,这些批量又重复的操作应该由我们的node出马,让我们来通过 node来批量上传我们的资源文件

将生成的js、css资源上传到七牛cdn

在七牛云官网的有介绍如何通过node上传文件、感兴趣的人可以自己去研究一下。

  1. 查看accesskeysecretkey,在你的个人面板 -> 秘钥管理 ,这两个秘钥待会会用到

vue-cli3 项目从搭建优化到docker部署

  1. 安装需要的插件
npm install qiniu glob mime --save-dev
  1. 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

vue-cli3 项目从搭建优化到docker部署
然后到你的cdn控制台的内容管理看看文件是否已经上传成功

vue-cli3 项目从搭建优化到docker部署

vue-cli3 项目从搭建优化到docker部署

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

vue-cli3 项目从搭建优化到docker部署

5.5 docker + jenkins 自动化部署

使用docker + jenkins 能实现代码提交到github后自动部署环境、这个要讲起来内容太多,有兴趣的可以看我这一篇文章

6. 扩展

如果大家还有什么更好的实践方式,欢迎评论区指教!!

vue-cli3 项目从搭建优化到docker部署

项目地址 欢迎 star

原文地址

欢迎关注

欢迎关注公众号“码上开发”,每天分享最新技术资讯

vue-cli3 项目从搭建优化到docker部署