vue-cli3 от построения до оптимизации

Vue.js

предисловие

адрес гитхаба:GitHub.com/Ли остается Мэри…(полная ветка: оптимизация ветки)

демонстрационный адрес:vue-project-demo.eloco.cn

Установите и инициализируйте схему

Установить

узел >= 8.9 Рекомендуется: 8.11.0+

Установить:npm install -g @vue/cli

экзамен:vue --version

Если у вас установлена ​​более старая версия, вам необходимо сначалаnpm uninstall vue-cli -gУдалите старую версию.

Инициализировать схему

  • Создайте:vue create project-name

image

Примечание. Имена проектов не могут быть написаны в верблюжьем регистре.

  • Выберите предустановку (здесь я выбираю дополнительные функции):

image

  • Выберите те, которые нужно установить (Babel, Router, Vuex, Pre-processors, Linter/Formatter):

image

  • Использовать ли режим маршрутизации истории (Да):

image

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

    image

  • Выберите конфигурацию eslint (ESLint + стандартная конфигурация):

image

  • Выберите, когда выполнять проверку eslint (Lint при сохранении):

    image

  • Выберите, как настроить функции, выбранные выше (в специальных файлах конфигурации):

image

  • Сохранять ли предыдущие настройки в качестве предустановленного шаблона (y):

image

Если вы выберете y, это позволит вам ввести имя для прямого использования в следующий раз, в противном случае начнется прямая инициализация проекта.

  • Наконец, взгляните на сгенерированный каталог базовой схемы:
    image

Элегантно используйте svg в своем проекте

  • первый в/src/componentsСоздайтеSvgIcon.vue:
    image

Ссылаться на:Будущее должно быть жарким: введение в технологию SVG Sprite - Чжан Синьсюй

  • существуетsrc/Создать подiconsпапку и создать под нейsvgПапка используется для хранения файлов svg, созданияindex.jsВ качестве входного файла:

image

Скрипт index.js:

import Vue from 'vue'
import SvgIcon from '@/components/SvgIcon.vue' // svg组件

// 全局注册
Vue.component('svg-icon', SvgIcon)

const requireAll = requireContext => requireContext.keys().map(requireContext)
const req = require.context('./svg', false, /\.svg$/)
requireAll(req)
  • использоватьsvg-sprite-loaderиспользуется в проектеsvgОбрабатывать:

npm install svg-sprite-loader --save-dev;

изменить значение по умолчаниюwebpackКонфигурация, созданная в корневом каталоге проектаvue.config.js, код показан ниже;

const path = require('path')

function resolve(dir) {
  return path.join(__dirname, './', dir)
}

module.exports = {
  chainWebpack: config => {
    // svg loader
    const svgRule = config.module.rule('svg') // 找到svg-loader
    svgRule.uses.clear() // 清除已有的loader, 如果不这样做会添加在此loader之后
    svgRule.exclude.add(/node_modules/) // 正则匹配排除node_modules目录
    svgRule // 添加svg新的loader处理
      .test(/\.svg$/)
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })

    // 修改images loader 添加svg处理
    const imagesRule = config.module.rule('images')
    imagesRule.exclude.add(resolve('src/icons'))
    config.module
      .rule('images')
      .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
  }
}
  • Наконец, вmain.jsвведен вimport '@/icons'Ты сможешь;
// 使用示例
<svg-icon icon-class="add" />

PS: Что касается svg, я лично рекомендую использовать библиотеку иконок с открытым исходным кодом Ali.iconFont

axios инкапсулирует API, модульный vuex

аксиомы
  • Установить в проектaxios:npm install axios;
  • существуетsrcСоздано в каталогеutils/и создатьrequest.jsиспользуется для инкапсуляцииaxios, код выше:
import axios from 'axios'

// 创建axios 实例
const service = axios.create({
  baseURL: process.env.BASE_API, // api的base_url
  timeout: 10000 // 请求超时时间
})

// request 拦截器
service.interceptors.request.use(
  config => {
    // 这里可以自定义一些config 配置

    return config
  },
  error => {
    //  这里处理一些请求出错的情况

    console.log(error)
    Promise.reject(error)
  }
)

// response 拦截器
service.interceptors.response.use(
  response => {
    const res = response.data
    // 这里处理一些response 正常放回时的逻辑

    return res
  },
  error => {
    // 这里处理一些response 出错时的逻辑

    return Promise.reject(error)
  }
)

export default service
  • Поскольку вы хотите использоватьaxios, необходимо настроить переменные среды и запрашиваемый адрес, который можно легко изменить здесь.poackage.json:
"scripts": {
    "dev": "vue-cli-service serve --project-mode dev",
    "test": "vue-cli-service serve --project-mode test",
    "pro": "vue-cli-service serve --project-mode pro",
    "pre": "vue-cli-service serve --project-mode pre",
    "build:dev": "vue-cli-service build --project-mode dev",
    "build:test": "vue-cli-service build --project-mode test",
    "build:pro": "vue-cli-service build --project-mode pro",
    "build:pre": "vue-cli-service build --project-mode pre",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },

Также измените vue.config.js:

const path = require('path')

function resolve(dir) {
  return path.join(__dirname, './', dir)
}

module.exports = {
  chainWebpack: config => {
    // 这里是对环境的配置,不同环境对应不同的BASE_API,以便axios的请求地址不同
    config.plugin('define').tap(args => {
      const argv = process.argv
      const mode = argv[argv.indexOf('--project-mode') + 1]
      args[0]['process.env'].MODE = `"${mode}"`
      args[0]['process.env'].BASE_API = '"http://47.94.138.75:8000"'
      return args
    })

    // svg loader
    const svgRule = config.module.rule('svg') // 找到svg-loader
    svgRule.uses.clear() // 清除已有的loader, 如果不这样做会添加在此loader之后
    svgRule.exclude.add(/node_modules/) // 正则匹配排除node_modules目录
    svgRule // 添加svg新的loader处理
      .test(/\.svg$/)
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })

    // 修改images loader 添加svg处理
    const imagesRule = config.module.rule('images')
    imagesRule.exclude.add(resolve('src/icons'))
    config.module
      .rule('images')
      .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
  }
}
  • как пользоваться? я скорее порекомендуюsrc/Создать подapiКаталог, используемый для унифицированного управления всеми запросами, например:

image

Преимущество этого в том, что это удобно для управления и пост-обслуживания, а также может соответствовать внутренним микросервисам и создавать несколько файлов для хранения разных модулей.api. Остальное зависит от того, какой API вы используете, вы можете импортировать его самостоятельно.

Расширение: настройки cors на стороне сервера

Включает в себя междоменные, вот использованиеcors,Многие знакомые часто спрашивают о принципе реализации cors в интервью.В этом интернете много теорий,которые в основном говорят так:

image

На самом деле это понимание очень абстрактно, как сервер проверяет?

Здесь вы можете понять это по-популярному, как бэк-энд получает фронт-эндrequestПо запросу будетrequestперехватчик вродеaxios responseПерехватчик такой же. Ниже сphp lumenВозьмите фреймворк в качестве примера, чтобы понять этот процесс глубже:

<?php

namespace App\Http\Middleware;

use App\Http\Utils\Code;
use Closure;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;

class CorsMiddleware
{
    private $headers;

    /**
     * 全局 : 解决跨域
     * @param $request
     * @param \Closure $next
     * @return mixed
     * @throws \HttpException
     */
    public function handle($request, Closure $next)
    {
        //请求参数
        Log::info('http request:'.json_encode(["request_all" => $request->all()]));

        $allowOrigin = [
            'http://47.94.138.75',
            'http://localhost',
        ];
        $Origin = $request->header("Origin");

        $this->headers = [
            'Access-Control-Allow-Headers'     => 'Origin,x-token,Content-Type',
            'Access-Control-Allow-Methods'     => 'GET, POST, PUT, DELETE, OPTIONS',
            'Access-Control-Allow-Credentials' => 'true',//允许客户端发送cookie
            'Access-Control-Allow-Origin'      => $Origin,
            //'Access-Control-Max-Age'           => 120, //该字段可选,间隔2分钟验证一次是否允许跨域。
        ];
        //获取请求方式
        if ($request->isMethod('options')) {
            if (in_array($Origin, $allowOrigin)) {
                return $this->setCorsHeaders(new Response(json_encode(['code' => Code::SUCCESS, "data" => 'success', "msg" => ""]), Code::SUCCESS));
            } else {
                return new Response(json_encode('fail', 405));
            }
        }
        $response = $next($request);
        //返回参数
        Log::info('http response:'.json_encode($response));
        return $this->setCorsHeaders($response);

    }

    /**
     * @param $response
     * @return mixed
     */
    public function setCorsHeaders($response)
    {
        foreach ($this->headers as $key => $val) {
            $response->header($key, $val);
        }
        return $response;
    }
}

vuex статьи

При создании проекта выберитеvuex, то значение по умолчанию будет вsrcЕсть каталогstore.jsкак файл репозитория. Но в более практических сценариях, если введениеvuex, то вам не избежать подмодулей, давайте сначала посмотрим на код файла по умолчанию:

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {

  },
  mutations: {

  },
  actions: {

  }
})

Так что теперь преобразуйте его, например, сначала разделитеapp,userДва модуля, может быть так:

import Vue from 'vue'
import Vuex from 'vuex'
import app from './store/modules/app'
import user from './store/modules/user'
import getters from './store/getters'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    app,
    user
  },
  getters
})

export default store

существуетsrc/Создать подstore/содержание:

image

app moduleМожет использоваться для хранения состояния приложения, например глобальногоloadingили контролировать глобальный размер сторонних компонентов, таких какelement uiглобальные компоненты вsize;

user moduleМожет использоваться для хранения информации о текущем пользователе;

Конечно, магазин отлично подходит для локального хранилища, здесь мы используемjs-cookie.

Глобальная загрузка, рациональное использование vue router guard

глобальная загрузка

вышеизложенное закончилосьaxios、vuex, теперь поговорим о настройке глобального перед объединениемloadingЭффект.

Обычно код пишут перед каждым запросом, как правило, необходимо установитьloading, закончить после успехаloadingэффект, который заставляет нас писать много повторяющегося кода, если вы не хотите этого делать, вы можете комбинироватьaxiosиvuexунифицированный.

  • Во-первых, говоряvuexкогда я былsrc/создалstore, Сейчасstore/modules/app.jsнапиши этоLoadingкод эффекта;
const app = {
  state: {
    requestLoading: 0
  },
  mutations: {
    SET_LOADING: (state, status) => {
      // error 的时候直接重置
      if (status === 0) {
        state.requestLoading = 0
        return
      }
      state.requestLoading = status ? ++state.requestLoading : --state.requestLoading
    }
  },
  actions: {
    SetLoading ({ commit }, status) {
      commit('SET_LOADING', status)
    }
  }
}

export default app
  • Измените его сноваutils/request.js
import axios from 'axios'
import store from '@/store'

// 创建axios 实例
const service = axios.create({
  baseURL: process.env.BASE_API, // api的base_url
  timeout: 10000 // 请求超时时间
})

// request 拦截器
service.interceptors.request.use(
  config => {
    // 这里可以自定义一些config 配置

    // loading + 1
    store.dispatch('SetLoading', true)

    return config
  },
  error => {
    //  这里处理一些请求出错的情况

    // loading 清 0 
    setTimeout(function () {
      store.dispatch('SetLoading', 0)
    }, 300)

    console.log(error)
    Promise.reject(error)
  }
)

// response 拦截器
service.interceptors.response.use(
  response => {
    const res = response.data
    // 这里处理一些response 正常放回时的逻辑

    // loading - 1
    store.dispatch('SetLoading', false)

    return res
  },
  error => {
    // 这里处理一些response 出错时的逻辑

    // loading - 1
    store.dispatch('SetLoading', false)

    return Promise.reject(error)
  }
)

export default service
  • Во-вторых, вsrc/components/Создать подRequestLoading.vueКомпоненты:
<template>
  <transition name="fade-transform" mode="out-in">
    <div class="request-loading-component" v-if="requestLoading">
      <svg-icon icon-class="loading" />
    </div>
  </transition>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  name: 'RequestLoading',
  computed: {
    ...mapGetters([
      'requestLoading'
    ])
  }
}
</script>

<style lang='scss' scoped>
.request-loading-component {
  position: fixed;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  //background-color: rgba(48, 65, 86, 0.2);
  background-color: transparent;
  font-size: 150px;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  z-index: 999999;
}
</style>

Наконец, вapp.vueможно импортировать.

Приложение: Для облегчения демонстрации инициализация включает в себя проектaxios,vuex,vue-router, в проекте используетсяjs-cookie,element-uiПодождите, после этого шага он будет преобразованapp.vue;

защита маршрутизатора vue

vue-router предоставляет очень удобные хуки, которые позволяют нам выполнять некоторые операции при выполнении переходов маршрутизации, например, общую проверку разрешений.

  • Во-первых, вам нужноsrc/utils/Создать подauth.js, используется для хранения токенов;
import Cookies from 'js-cookie'

const TokenKey = 'project-token'

export function getToken () {
  return Cookies.get(TokenKey)
}

export function setToken (token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken () {
  return Cookies.remove(TokenKey)
}

существуетsrc/utils/Создать подpermission.js:

import router from '@/router'
import store from '@/store'
import {
  getToken
} from './auth'
import NProgress from 'nprogress' // 进度条
import 'nprogress/nprogress.css' // 进度条样式
import {
  Message
} from 'element-ui'

const whiteList = ['/login'] // 不重定向白名单
router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
    if (to.path === '/login') {
      next({
        path: '/'
      })
      NProgress.done()
    } else { // 实时拉取用户的信息
      store.dispatch('GetUserInfo').then(res => {
        next()
      }).catch(err => {
        store.dispatch('FedLogOut').then(() => {
          Message.error('拉取用户信息失败,请重新登录!' + err)
          next({
            path: '/'
          })
        })
      })
    }
  } else {
    if (whiteList.includes(to.path)) {
      next()
    } else {
      next('/login')
      NProgress.done()
    }
  }
})

router.afterEach(() => {
  NProgress.done() // 结束Progress
})
Nginx try_files и 404

nginxКонфигурация выглядит следующим образом:

location / {
        root   /www/vue-project-demo/;
        try_files $uri $uri/ /index.html index.htm;
}

try_files: можно понять, что nginx не обрабатывает ваши запросы на эти url-адреса; Затем, если сервер не справляется с этим, внешний интерфейс должен выполнить некоторые операции 404 самостоятельно, например следующие:

// router.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'

Vue.use(Router)

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    { path: '/404', component: () => import('@/views/404') },
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/about',
      name: 'about',
      // route level code-splitting
      // this generates a separate chunk (about.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
    },
    { path: '*', redirect: '/404' }
  ]
})

Затем напишите представление 404, и все в порядке.

Часто используемые утилиты

До сих пор,utils/В каталоге должно бытьauth.js 、permission.js、request.js;

  • Ну, с некоторыми общими методами, вы можете поставитьutils/common.jsв, унифицированныйinstallприбытьvueэкземпляр, и поVue.use()использовать;

  • Для некоторых глобальных фильтров еще можно поставитьutils/filters.jsв использованииVue.fileter()зарегистрироваться в глобале;

  • Для некоторых глобальных методов, которые давно не используются, их можно поместить вutils/index.js, где использовать гдеimport

mixin уменьшает избыточный код проекта

Просто посмотрите на код, он рухнет....

Используйте cdn, чтобы уменьшить размер упаковки файлов

На этом этапе посмотрите, что я использовал в своем проекте:

image.png
Это основные, а как насчет выполнения команды упаковки?
image.png

Возможно, вы еще ничего не чувствуете в это время, максимальное количество одиночных файлов не превышено800kbШерстяная ткань...

я прохожу проектjenkinsРазверните на сервере и посмотрите на доступ:

image.png

можно увидеть,chunk-vendorsЗагрузка заняла почти 12 секунд, это только при условии, что у фреймворка нет контента, конечно, вы можете сказать, что не можете использовать его в своем проекте.vuexбесполезныйjs-cookie, но при итеративном сопровождении проекта меньше, чем сейчас, точно не будет.

Итак, можно ли попробовать некоторые файлы в рабочей среде?cdnШерстяная ткань?

Для удобства сравнения оставьте исходный код здесь без изменений (masterветвь), а затем вырезать ветвь, чтобы изменить оптимизацию (optimizeфилиал), приведенный выше код:

// vue.config.js  修改
const path = require('path')

function resolve(dir) {
  return path.join(__dirname, './', dir)
}

// cdn预加载使用
const externals = {
  'vue': 'Vue',
  'vue-router': 'VueRouter',
  'vuex': 'Vuex',
  'axios': 'axios',
  'element-ui': 'ELEMENT',
  'js-cookie': 'Cookies',
 'nprogress': 'NProgress'
}

const cdn = {
  // 开发环境
  dev: {
    css: [
      'https://unpkg.com/element-ui/lib/theme-chalk/index.css',
      'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
    ],
    js: []
  },
  // 生产环境
  build: {
    css: [
      'https://unpkg.com/element-ui/lib/theme-chalk/index.css',
      'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
    ],
    js: [
      'https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.min.js',
      'https://cdn.jsdelivr.net/npm/vue-router@3.0.1/dist/vue-router.min.js',
      'https://cdn.jsdelivr.net/npm/vuex@3.0.1/dist/vuex.min.js',
      'https://cdn.jsdelivr.net/npm/axios@0.18.0/dist/axios.min.js',
      'https://unpkg.com/element-ui/lib/index.js',
      'https://cdn.bootcss.com/js-cookie/2.2.0/js.cookie.min.js',
      'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.js'
    ]
  }
}

module.exports = {
  chainWebpack: config => {
    // 这里是对环境的配置,不同环境对应不同的BASE_API,以便axios的请求地址不同
    config.plugin('define').tap(args => {
      const argv = process.argv
      const mode = argv[argv.indexOf('--project-mode') + 1]
      args[0]['process.env'].MODE = `"${mode}"`
      args[0]['process.env'].BASE_API = '"http://47.94.138.75:8000"'
      return args
    })

    /**
     * 添加CDN参数到htmlWebpackPlugin配置中, 详见public/index.html 修改
     */
    config.plugin('html').tap(args => {
      if (process.env.NODE_ENV === 'production') {
        args[0].cdn = cdn.build
      }
      if (process.env.NODE_ENV === 'development') {
        args[0].cdn = cdn.dev
      }
      return args
    })

    // svg loader
    const svgRule = config.module.rule('svg') // 找到svg-loader
    svgRule.uses.clear() // 清除已有的loader, 如果不这样做会添加在此loader之后
    svgRule.exclude.add(/node_modules/) // 正则匹配排除node_modules目录
    svgRule // 添加svg新的loader处理
      .test(/\.svg$/)
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })

    // 修改images loader 添加svg处理
    const imagesRule = config.module.rule('images')
    imagesRule.exclude.add(resolve('src/icons'))
    config.module
      .rule('images')
      .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
  },

  // 修改webpack config, 使其不打包externals下的资源
  configureWebpack: config => {
    const myConfig = {}
    if (process.env.NODE_ENV === 'production') {
      // 1. 生产环境npm包转CDN
      myConfig.externals = externals
    }
    if (process.env.NODE_ENV === 'development') {
      /**
       * 关闭host check,方便使用ngrok之类的内网转发工具
       */
      myConfig.devServer = {
        disableHostCheck: true
      }
    }
    //   open: true,
    //   hot: true
    //   // https: true,
    //   // proxy: {
    //   //   '/proxy': {
    //   //     target: 'http://47.94.138.75',
    //   //     // changeOrigin: true,
    //   //     pathRewrite: {
    //   //       '^/proxy': ''
    //   //     }
    //   //   }
    //   // },
    // }
    return myConfig
  }
}
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="zh-CN">

<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">

  <!-- 使用CDN加速的CSS文件,配置在vue.config.js下 -->
  <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.css) { %>
  <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="preload" as="style">
  <link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="stylesheet">
  <% } %>

  <!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
  <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
  <link href="<%= htmlWebpackPlugin.options.cdn.js[i] %>" rel="preload" as="script">
  <% } %>

  <title>vue-project-demo</title>
</head>

<body>
  <noscript>
    <strong>We're sorry but vue-project-demo doesn't work properly without JavaScript enabled. Please enable it to
      continue.</strong>
  </noscript>
  <div id="app"></div>
  <!-- 使用CDN加速的JS文件,配置在vue.config.js下 -->
  <% for (var i in htmlWebpackPlugin.options.cdn&&htmlWebpackPlugin.options.cdn.js) { %>
  <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
  <% } %>
  <!-- built files will be auto injected -->
</body>

</html>

окончательное удалениеmain.jsвведен вimport 'element-ui/lib/theme-chalk/index.css'

Хорошо, теперь выполнитеbuild:

image.png

Видно, что по сравнению с793.20KB,61.94kпочти слишком маленький13раз! ! !

Разверните эту ветку на сервере, особо нечего сказать, просто сравните:

image.png

Ускоряйтесь с помощью Gzip

const path = require('path')
const CompressionWebpackPlugin = require('compression-webpack-plugin')

function resolve(dir) {
  return path.join(__dirname, './', dir)
}

// cdn预加载使用
const externals = {
  'vue': 'Vue',
  'vue-router': 'VueRouter',
  'vuex': 'Vuex',
  'axios': 'axios',
  'element-ui': 'ELEMENT',
  'js-cookie': 'Cookies',
  'nprogress': 'NProgress'
}

const cdn = {
  // 开发环境
  dev: {
    css: [
      'https://unpkg.com/element-ui/lib/theme-chalk/index.css',
      'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
    ],
    js: []
  },
  // 生产环境
  build: {
    css: [
      'https://unpkg.com/element-ui/lib/theme-chalk/index.css',
      'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
    ],
    js: [
      'https://cdn.bootcss.com/vue/2.5.21/vue.min.js',
      'https://cdn.bootcss.com/vue-router/3.0.2/vue-router.min.js',
      'https://cdn.bootcss.com/vuex/3.0.1/vuex.min.js',
      'https://cdn.bootcss.com/axios/0.18.0/axios.min.js',
      'https://unpkg.com/element-ui/lib/index.js',
      'https://cdn.bootcss.com/js-cookie/2.2.0/js.cookie.min.js',
      'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.js'
    ]
  }
}

// 是否使用gzip
const productionGzip = true
// 需要gzip压缩的文件后缀
const productionGzipExtensions = ['js', 'css']

module.exports = {
  chainWebpack: config => {
    // 这里是对环境的配置,不同环境对应不同的BASE_API,以便axios的请求地址不同
    config.plugin('define').tap(args => {
      const argv = process.argv
      const mode = argv[argv.indexOf('--project-mode') + 1]
      args[0]['process.env'].MODE = `"${mode}"`
      args[0]['process.env'].BASE_API = '"http://47.94.138.75:8000"'
      return args
    })

    /**
     * 添加CDN参数到htmlWebpackPlugin配置中, 详见public/index.html 修改
     */
    config.plugin('html').tap(args => {
      if (process.env.NODE_ENV === 'production') {
        args[0].cdn = cdn.build
      }
      if (process.env.NODE_ENV === 'development') {
        args[0].cdn = cdn.dev
      }
      return args
    })

    // svg loader
    const svgRule = config.module.rule('svg') // 找到svg-loader
    svgRule.uses.clear() // 清除已有的loader, 如果不这样做会添加在此loader之后
    svgRule.exclude.add(/node_modules/) // 正则匹配排除node_modules目录
    svgRule // 添加svg新的loader处理
      .test(/\.svg$/)
      .use('svg-sprite-loader')
      .loader('svg-sprite-loader')
      .options({
        symbolId: 'icon-[name]'
      })

    // 修改images loader 添加svg处理
    const imagesRule = config.module.rule('images')
    imagesRule.exclude.add(resolve('src/icons'))
    config.module
      .rule('images')
      .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
  },

  // 修改webpack config, 使其不打包externals下的资源
  configureWebpack: config => {
    const myConfig = {}
    if (process.env.NODE_ENV === 'production') {
      // 1. 生产环境npm包转CDN
      myConfig.externals = externals

      myConfig.plugins = []
      // 2. 构建时开启gzip,降低服务器压缩对CPU资源的占用,服务器也要相应开启gzip
      productionGzip && myConfig.plugins.push(
        new CompressionWebpackPlugin({
          test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
          threshold: 8192,
          minRatio: 0.8
        })
      )
    }
    if (process.env.NODE_ENV === 'development') {
      /**
       * 关闭host check,方便使用ngrok之类的内网转发工具
       */
      myConfig.devServer = {
        disableHostCheck: true
      }
    }
    //   open: true,
    //   hot: true
    //   // https: true,
    //   // proxy: {
    //   //   '/proxy': {
    //   //     target: 'http://47.94.138.75',
    //   //     // changeOrigin: true,
    //   //     pathRewrite: {
    //   //       '^/proxy': ''
    //   //     }
    //   //   }
    //   // },
    // }
    return myConfig
  }
}
  • бежать сноваbuild, мы найдемdist/под всеми.jsи.cssбудет еще один.js.gz、.css.gzФайл, это сжатый файл, который нам нужен, вы можете видеть, что самый большой только18.05KB, подумайте, будет ли это более захватывающим...

    image.png

  • Конечно, этой штуке тоже нужна серверная поддержка, то есть настройкаnginx:

gzip on;
gzip_static on;
gzip_min_length 1024;
gzip_buffers 4 16k;
gzip_comp_level 2;
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/vnd.ms-fontobject font/ttf font/opentype font/x-woff image/svg+xml;
gzip_vary off;
gzip_disable "MSIE [1-6]\.";
  • Перезагрузка после настройкиnginx:
    image.png

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

image.png

существуетresponse headersбудет одинContent-Encoding:gzip

image.png

---------------------------- Продолжение следует------------------- ------------