Проект Vue-cli3 от оптимизации сборки до развертывания докера

Vue.js
Проект Vue-cli3 от оптимизации сборки до развертывания докера

адрес проектаvue-cli3-projectприветственная звезда

исходный адресWoohoo.C code.live/Len too/list…

1. Создайте проект vue

Я полагаю, что большинство из вас уже знают, как создать проект, вы можете пропустить этот раздел и перейти к следующему.

1.1 Установите @vue/cli

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

Дождитесь завершения установки и начните следующий шаг.

1.2 Инициализация проекта

vue create vue-cli3-project
  1. выбрать предустановку

Можно выбрать предустановки по умолчанию, предустановки по умолчанию включаютbabel,eslint

Мы выбираем больше возможностейManually select features

Введите и выберите плагин

  1. Выбор плагина

Выбрано здесь (Babel, Router, Vuex, препроцессор Css, проверка формата Linter/Formatter, фреймворк модульного тестирования)

  1. Выбор режима маршрутизации

использовать или нетhistoryРежим маршрутизации (Да)

  1. Выберите препроцессор css (Sass/SCSS)

  1. Выберите конфигурацию eslint

выбрать здесьESLint + Standard config, я лично предпочитаю эту спецификацию кода

  1. выбрать, когдаeslintчек об оплате

Выберите (Lint при сохранении), чтобы сохранить флажок

Если вы используете редактор vscode, вы можете настроить плагин eslint для автоматического форматирования кода.

7. Выберите тестовую среду (Mocha + Chai)
8. Выберите, куда записать эти файлы конфигурации (в специальные файлы конфигурации)

  1. Вы хотите сохранить эту конфигурацию по умолчанию? (у)

Если вы выберете «да», в следующий раз, когда вы будете создавать проект 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Разве не было бы расточительством, если бы нельзя было использовать что-то столь полезное?

Хотя мы реализовали автоматическую регистрацию компонентов через вышеизложенное, но каждый раз, когда мы создаем новый компонент, мы должны создать каталог, а затем добавить новый..vueфайл, затем напишитеtemplate,script,styleэти вещи, а затем создать новыйindex.js, Экспорт компонентов vue, хотя есть плагины, которые могут добиться автоматического завершения, но это все еще очень хлопотно.

Так мы можем пройтиnodeчтобы помочь нам сделать это? просто скажиnodeПросто помогите мне сгенерировать имя компонента. другие вещи делаютnodeприходить делать

4.1 Создание компонентов через узел

  • установить его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

4.2 для создания компонентов страницы через узел

Через приведенный выше логический код мы можем пройти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. пакет аксиом

  • установить аксиомы
npm install axios --save
// or
yarn add axios

5.1 Настройка различных сред

Создайте три файла переменных среды в корневом каталоге.

Введите разные адреса соответственно, Напримерdevпросто пишиdevAPI-адрес,testпросто пишиtestAPI-адрес

# // .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 Запрос на отображение эффекта загрузки

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. Используйте миксины

6.1 Инкапсуляция общедоступных методов хранилища

Рассмотрим сценарий, который мы прошли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Каталог для управления всеми миксинами создать новый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Файл в порядке. Если ваш сервер использует 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доменное имя, если оно недоступно, вы можете перейти наОфициальный сайт Qiniu CloudПодать заявку на регистрацию на

  1. Зарегистрируйте учетную запись Qiniu Cloud
  2. Создайте новое пространство для хранения в модуле хранения облачных объектов Qiniu.
  3. Введите информацию о месте для хранения
  4. ОК, чтобы создать
  5. После успешного создания он перейдет на страницу консоли этого хранилища.
  6. Один из доменов является вашим тестовым доменом
  7. Мы можем загрузить нашуjs,cssТакие файлы, а у нас так много файлов, заливать по одному явно неразумно. Не делай этого, если хочешь.

В настоящее время эти пакетные и повторяющиеся операции должны выполняться нашимиnodeВыходи, пройдёмnodeдля массовой загрузки наших файлов ресурсов

Загрузите сгенерированные ресурсы js и css в Qiniu cdn

На официальном сайте QiniuyunЦентр документацииЕсть инструкция как пройтиnodeЗагрузите файл, и те, кто заинтересован, могут изучить его самостоятельно.

  1. ПроверитьAccessKeyа такжеSecretKeyНа вашей личной панели -> Управление ключами, которые будут использоваться два ключа

  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')
})()

Изменить общедоступный путь

Исправлять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. развертывание докера

Здесь используетсяcentOS7среду, но используя другую систему, вы можете обратиться к методу установки других систем

8.1 Установка докера

  • Обновление библиотеки программного обеспечения
yum update -y
  • установить докер
yum install docker
  • запустить докер-сервис
service docker start
  • Установить докер-компоновку
// 安装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 + jenkins

использоватьdocker + jenkinsОн может реализовать среду автоматического развертывания после отправки кода на github.Об этом слишком много говорить.Если вам интересно, вы можете прочитать мою статью.

Создайте среду автоматического развертывания docker+jenkins+node.js с нуля

6. Расширение

Если у вас есть лучшие практические методы, добро пожаловать в область комментариев за советом! !

адрес проектаvue-cli3-projectприветственная звезда

исходный адресWoohoo.C code.live/Len too/list…

Добро пожаловать, чтобы следовать

Добро пожаловать в общедоступный номер»разработка кода», делясь последней технической информацией каждый день

image