ビルド最適化からdocker配備までのVue-cli3プロジェクト

プロジェクトの住所 vue-cli3-project Welcome star

元のアドレス www.ccode.live/lentoo/list …

1. Vueプロジェクトを作成する

私はほとんどの人がすでにプロジェクトを作成する方法を知っていると思います。このセクションを飛ばして次のセクションを見てください。

1.1 @ vue / cliをインストールする

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

次の手順を開始する前にインストールが完了するのを待ちます。

1.2初期化プロジェクト

vue create vue-cli3-project

可以选择默认预设,默认预设包含了babel,eslint

より多くの機能を選択手動で機能を選択

入力してプラグインすることを選択

ここで選択します(Babel、Router、Vuex、Cssプリプロセッサ、Linter / Formatterフォーマットチェック、単体テストフレームワーク)

history モードのルートを使用するかどうか(はい)

ここで ESLint + Standard config を選択します。個人的にはこのコード仕様をお勧めします

保存を選択する

vscodeエディタを使用している場合は、自動コードフォーマット用にeslintプラグインを設定できます。

7. 选择测试框架 (Mocha + Chai)

8. 选择将这些配置文件写入到什么地方 (In dedicated config files)

次回にvueプロジェクトを作成することを選択した場合は、設定しなくてもこのプリセットファイルを直接使用できます。

依存関係が完了するのを待っています

2.グローバルコンポーネントの自動登録

components ディレクトリに global ディレクトリを作成します。このディレクトリには、グローバルに登録する必要があるコンポーネントがいくつか含まれています。

main.vue が導入されている限り、 index.js 関数はコンポーネントオブジェクトをエクスポートします。

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

最後に、この index.js をエントリファイル main.js にインポートします。

自動ルート導入

Vue プロジェクトでルーティングを使う新しいページを追加するには、ルーティング設定でそのページの情報を設定する必要があります。

より多くのページがある場合、どうすればルーティングをより簡潔にすることができますか?

3.1分割ルート

さまざまなサービスモジュールに基づく分割ルーティング

各サブモジュールのルーティング構成配列をエクスポートする

ルート index.js にあるすべてのサブモジュールをインポートします。

3.2ルーティングとインポートのためのサブモジュールの自動スキャン

私たちのビジネスがどんどん大きくなっていくと、新しいビジネスモジュールを追加するたびに、ルーティングの下にサブルーティングモジュールを追加し、それを index.js にインポートする必要があります。

それでは、どのようにこの操作を単純化しますか?

上記の自動スキャングローバルコンポーネント登録を通じて、サブモジュールのルーティングとインポートの自動スキャンも実装できます。

4.ノードを通してコンポーネントを生成する

フロントエンド開発者として、 node のようなものを使用できない場合、それは無駄ではありませんか?

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

それでは、 node を通してこれらのことを手助けできますか?作成したコンポーネントの名前を node に伝えるだけです。他のことで node にやらせる

4.1ノードを通してコンポーネントを生成する

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 です。

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()
    })
  }
}
"new:view": "node ./scripts/generateView"

npm を使用する場合は、 npm run new:view です。

yarn を使用する場合、それは yarn new:view です。

5. axiosパッケージ

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

5.1さまざまな環境を設定する

ルートディレクトリに3つの新しい環境変数ファイルを作成します。

分别输入不同的地址,
比如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 フォルダを作成し、 axios を設定するための index.js 設定を作成します。情報

// 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 を介してニュースリストを取得する関数をカプセル化します。

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 が提供する Vxin が大きな役割を果たしています。

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.1 gzip圧縮

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 ファイルが生成されたことがわかります。サーバーでnginxを使用している場合は、 GZIP を有効にするようにnginxも設定する必要があります。 nginx GZIP を有効にする方法は次のとおりです。

7.2サードパーティライブラリリファレンスcdn

vue vue-router vuex axios 、および element-ui の場合頻繁に変更されないこれらのライブラリを待って、 webpack にそれらをパッケージ化せず、 cdn で紹介します。これにより、コードのサイズを縮小し、サーバーの帯域幅を削減できます。これらのファイルはクライアントにキャッシュされ、クライアントはより速くロードされます。

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
    }
  }
}
<!--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 ドメイン名を持っている必要があります。持っていない場合は、 7つのNiuyun公式ウェブサイトに登録を申請する

現時点では、これらのバッチ操作および繰り返し操作は node で実行する必要があります。 node を介してリソースファイルをまとめてアップロードします。

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

ノードを渡す方法については、7つのNiuyun Webサイトのドキュメントセンターをご覧ください。 ファイルをアップロードして、興味のある人が自分で調べることができます。

npm install qiniu glob mime --save-dev
// /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

publicPath cdn のドメイン名を指すように vue.config.js の設定情報を変更します。

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 server にリソースファイルをアップロードするようにpackage.json設定を変更します。

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

运行查看效果

npm run build

然后到你的cdn控制台的内容管理看看文件是否已经上传成功

8.港湾労働者の配備

これが centOS7 環境ですが、別のシステムを使用している場合は、他のシステムのインストール方法を参照できます。

8.1 dockerをインストールする

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 docker + jenkins自動デプロイ

コードがgithubに送信された後に自動展開環境を実装するには、 docker + jenkins を使用します。

docker + jenkins + node.js自動導入環境を最初から構築

6.エクステンション

もっと良い練習方法があれば、コメント欄にようこそ。 !

プロジェクトの住所 vue-cli3-project Welcome star

元のアドレス www.ccode.live/lentoo/list …

ようこそ注目

毎日最新の技術情報を共有するための公開番号「コード開発」へようこそ。

image

元のリンク