搜公众号
推荐 原创 视频 Java开发 开发工具 Python开发 Kotlin开发 Ruby开发 .NET开发 服务器运维 开放平台 架构师 大数据 云计算 人工智能 开发语言 其它开发 iOS开发 前端开发 JavaScript开发 Android开发 PHP开发 数据库
Lambda在线 > 奇舞周刊 > vue-cli3 从搭建到优化

vue-cli3 从搭建到优化

奇舞周刊 2019-01-21
举报

编者按:本文转载自掘金专栏,由作者 lMadman 授权奇舞周刊转载。

前言

安装与初始化

安装

node >= 8.9 推荐:8.11.0 +

安装:npm install -g @vue/cli

检查:vue --version

如果已安装旧版本,需要先npm uninstall vue-cli -g 卸载掉旧版本。

初始化架构

  • 创建:vue create project-name

注:项目名称不能驼峰命名。

  • 选择一个预设(这里我选择更多功能):

vue-cli3 从搭建到优化

  • 选择需要安装的(Babel、Router、Vuex、Pre-processors、Linter / Formatter):

vue-cli3 从搭建到优化

  • 是否使用history路由模式(Yes):

vue-cli3 从搭建到优化

  • 选择css 预处理器(Sass/SCSS):

vue-cli3 从搭建到优化

  • 选择eslint 配置(ESLint + Standard config):

vue-cli3 从搭建到优化

  • 选择什么时候执行eslint校验(Lint on save):

vue-cli3 从搭建到优化

  • 选择以什么样的形式配置以上所选的功能(In dedicated config files):

vue-cli3 从搭建到优化

  • 是否将之前的设置保存为一个预设模板(y):

vue-cli3 从搭建到优化

如果选择 y 会让输入名称,以便下次直接使用,否则直接开始初始化项目。

  • 最后,看一下生成的基本架构目录:

vue-cli3 从搭建到优化

优化

在项目中优雅的使用svg

  • 首先在/src/components 创建 SvgIcon.vue

vue-cli3 从搭建到优化

参考:未来必热:SVG Sprite技术介绍 - 张鑫旭(https://www.zhangxinxu.com/wordpress/2014/07/introduce-svg-sprite-technology/)

  • src/下创建 icons文件夹,以及在其下创建svg文件夹用于存放svg文件,创建index.js作为入口文件:

vue-cli3 从搭建到优化

编写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 ,个人比较建议使用阿里开源的图标库 iconFont

axios封装api、模块化vuex

axios篇

  • 项目中安装axiosnpm 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

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

    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目录,用来统一管理所有的请求,比如下面这样:

vue-cli3 从搭建到优化

这样的好处是方便管理、后期维护,还可以和后端的微服务对应,建立多文件存放不同模块的api。剩下的就是你使用到哪个api时,自己引入便可。

拓展:服务端的cors设置

牵涉到跨域,这里采用cors,很多朋友在面试中经常会被问到cors的实现原理,这个网上有很多理论大多是这样讲的:

vue-cli3 从搭建到优化

其实,这样理解很抽象,服务器端到底是怎么做验证的?

这里大家可以通俗的理解为后端在接收前端的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: {


  }

})

那么现在改造一下,比如先划分出appuser两个模块,可以这样:

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/目录:

vue-cli3 从搭建到优化

app module 可以用来存储应用的状态,比如接下来要讲到的全局loading,或者控制第三方组件的全局大小,比如element ui中的全局组件size

user module 可以用来存储当前用户的信息;

当然,store 配合本地存储比较完美,这里采用js-cookie

全局loading、合理利用vue router守卫

全局loading

上面说完了axios、vuex,现在结合之前说一下设置全局loading效果。

平常写代码每个请求之前一般都需要设置loading ,成功之后结束loading效果,这就迫使我们不得不写大量重复代码,如果不想这样做,可以结合axiosvuex统一做了。

  • 首先,在说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中引入即可。

附: 为了方便演示,项目里出了初始化包括axiosvuexvue-router, 项目使用了js-cookieelement-ui等,此步骤之后,会改造一下app.vue

vue router守卫

vue-router 提供了非常方便的钩子,可以让我们在做路由跳转的时候做一些操作,比如常见的权限验证。

  • 首先,需要在src/utils/下创建auth.js,用于存储token;

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;

}

// 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 的view 就ok 。

常用的utils

到现在为止,utils/目录下应该有auth.js 、permission.js、request.js

  • 那么对与一些常用的方法,你可以放到utils/common.js 里,统一installvue 实例上,并通过Vue.use()使用;

  • 对于一些全局的过滤器,你仍可以放到utils/filters.js里,使用Vue.fileter()注册到全局;

  • 对于一些全局方法,又不是很长用到的,可以放到utils/index.js,哪里使用哪里import


mixin减少项目冗余代码

直接看代码吧,要写奔溃了....

使用cdn减少文件打包的体积

到此时,看我项目里都用了什么:

vue-cli3 从搭建到优化

主要就是这些,那么执行一下打包命令呢?

vue-cli3 从搭建到优化

可能这时候你还觉得没什么, 单文件最多的还没超过800kb呢...

我把项目通过jenkins部署到服务器上,看一下访问:

vue-cli3 从搭建到优化

可以看到,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 => {

    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

  }

}

html:

<!-- 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'

OK ,现在执行一下build

vue-cli3 从搭建到优化

可以看到,相对于 793.20KB61.94k小了将近13倍!!!

把这个分支部署到服务器,话不多说,对比一下就好:

vue-cli3 从搭建到优化

使用Gzip 加速

  • 引入 compression-webpack-plugin : npm i -D compression-webpack-plugin

  • 修改vue.config.js,老规矩,上最全的代码:


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 => {

    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,想想是不是比较激动...

vue-cli3 从搭建到优化

  • 当然,这玩意还需要服务端支持,也就是配置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

vue-cli3 从搭建到优化

配置成功的话,可以看到加载的是比较小的Gzip

vue-cli3 从搭建到优化

response headers 里会有一个Content-Encoding:gzip

关于奇舞周刊


vue

版权声明:本站内容全部来自于腾讯微信公众号,属第三方自助推荐收录。《vue-cli3 从搭建到优化》的版权归原作者「奇舞周刊」所有,文章言论观点不代表Lambda在线的观点, Lambda在线不承担任何法律责任。如需删除可联系QQ:516101458

文章来源: 阅读原文

相关阅读

举报