vue-cli3 從搭建到優(yōu)化的詳細(xì)步驟
前言
github地址: https://github.com/LeeStaySmall/vue-project-demo (完整分支:optimize分支)
demo地址: vue-project-demo.eloco.cn
安裝與初始化架構(gòu)
安裝
node >= 8.9 推薦:8.11.0 +
安裝: npm install -g @vue/cli
檢查: vue --version
如果已安裝舊版本,需要先 npm uninstall vue-cli -g 卸載掉舊版本。
初始化架構(gòu)
創(chuàng)建: vue create project-name

注:項(xiàng)目名稱不能駝峰命名。
選擇一個(gè)預(yù)設(shè)(這里我選擇更多功能):

選擇需要安裝的(Babel、Router、Vuex、Pre-processors、Linter / Formatter):

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

選擇css 預(yù)處理器(Sass/SCSS):

選擇eslint 配置(ESLint + Standard config):

選擇什么時(shí)候執(zhí)行eslint校驗(yàn)(Lint on save):

選擇以什么樣的形式配置以上所選的功能(In dedicated config files):

是否將之前的設(shè)置保存為一個(gè)預(yù)設(shè)模板(y):

如果選擇 y 會(huì)讓輸入名稱,以便下次直接使用,否則直接開始初始化項(xiàng)目。
最后,看一下生成的基本架構(gòu)目錄:

在項(xiàng)目中優(yōu)雅的使用svg 首先在 /src/components 創(chuàng)建 SvgIcon.vue :

在 src/ 下創(chuàng)建 icons 文件夾,以及在其下創(chuàng)建 svg 文件夾用于存放svg文件,創(chuàng)建 index.js 作為入口文件:

編寫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 對項(xiàng)目中使用的 svg 進(jìn)行處理:
npm install svg-sprite-loader --save-dev ;
修改默認(rèn)的 webpack 配置, 在項(xiàng)目根目錄創(chuàng)建 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, 如果不這樣做會(huì)添加在此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 ,個(gè)人比較建議使用阿里開源的圖標(biāo)庫iconFont
axios封裝api、模塊化vuex
axios篇
項(xiàng)目中安裝 axios : npm install axios ;
在 src 目錄下創(chuàng)建 utils/ , 并創(chuàng)建 request.js 用來封裝 axios ,上代碼:
import axios from 'axios'
// 創(chuàng)建axios 實(shí)例
const service = axios.create({
baseURL: process.env.BASE_API, // api的base_url
timeout: 10000 // 請求超時(shí)時(shí)間
})
// request 攔截器
service.interceptors.request.use(
config => {
// 這里可以自定義一些config 配置
return config
},
error => {
// 這里處理一些請求出錯(cuò)的情況
console.log(error)
Promise.reject(error)
}
)
// response 攔截器
service.interceptors.response.use(
response => {
const res = response.data
// 這里處理一些response 正常放回時(shí)的邏輯
return res
},
error => {
// 這里處理一些response 出錯(cuò)時(shí)的邏輯
return Promise.reject(error)
}
)
export default service
既然要使用 axios ,必不可少的需要配置環(huán)境變量以及需要請求的地址,這里可以簡單的修改 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"
},
同時(shí)修改vue.config.js:
const path = require('path')
function resolve(dir) {
return path.join(__dirname, './', dir)
}
module.exports = {
chainWebpack: config => {
// 這里是對環(huán)境的配置,不同環(huán)境對應(yīng)不同的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, 如果不這樣做會(huì)添加在此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/ 下創(chuàng)建 api 目錄,用來統(tǒng)一管理所有的請求,比如下面這樣: ‘'

這樣的好處是方便管理、后期維護(hù),還可以和后端的微服務(wù)對應(yīng),建立多文件存放不同模塊的 api 。剩下的就是你使用到哪個(gè)api時(shí),自己引入便可。
拓展:服務(wù)端的cors設(shè)置
牽涉到跨域,這里采用 cors ,很多朋友在面試中經(jīng)常會(huì)被問到cors的實(shí)現(xiàn)原理,這個(gè)網(wǎng)上有很多理論大多是這樣講的:

其實(shí),這樣理解很抽象,服務(wù)器端到底是怎么做驗(yàn)證的?
這里大家可以通俗的理解為后端在接收前端的 request 請求的時(shí)候,會(huì)有一個(gè) request 攔截器,像 axios response 攔截器一樣。下面以 php lumen 框架為例,來深入理解一下這個(gè)流程:
<?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)
{
//請求參數(shù)
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',//允許客戶端發(fā)送cookie
'Access-Control-Allow-Origin' => $Origin,
//'Access-Control-Max-Age' => 120, //該字段可選,間隔2分鐘驗(yàn)證一次是否允許跨域。
];
//獲取請求方式
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);
//返回參數(shù)
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 篇
如果創(chuàng)建項(xiàng)目的時(shí)候,選擇了 vuex ,那么默認(rèn)會(huì)在 src 目錄下有一個(gè) store.js 作為倉庫文件。但在更多實(shí)際場景中,如果引入 vuex ,那么肯定避免不了分模塊,先來看一下默認(rèn)文件代碼:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
},
mutations: {
},
actions: {
}
})
那么現(xiàn)在改造一下,比如先劃分出 app 、 user 兩個(gè)模塊,可以這樣:
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/ 下創(chuàng)建 store/ 目錄:

app module 可以用來存儲(chǔ)應(yīng)用的狀態(tài),比如接下來要講到的全局 loading ,或者控制第三方組件的全局大小,比如 element ui 中的全局組件 size ;
user module 可以用來存儲(chǔ)當(dāng)前用戶的信息;
當(dāng)然,store 配合本地存儲(chǔ)比較完美,這里采用 js-cookie 。
全局loading、合理利用vue router守衛(wèi)
全局loading
上面說完了 axios、vuex ,現(xiàn)在結(jié)合之前說一下設(shè)置全局 loading 效果。
平常寫代碼每個(gè)請求之前一般都需要設(shè)置 loading ,成功之后結(jié)束 loading 效果,這就迫使我們不得不寫大量重復(fù)代碼,如果不想這樣做,可以結(jié)合 axios 和 vuex 統(tǒng)一做了。
首先,在說 vuex 的時(shí)候,我在 src/ 下創(chuàng)建了一個(gè) store ,現(xiàn)在就在 store/modules/app.js 寫這個(gè) Loading 效果的代碼;
const app = {
state: {
requestLoading: 0
},
mutations: {
SET_LOADING: (state, status) => {
// error 的時(shí)候直接重置
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'
// 創(chuàng)建axios 實(shí)例
const service = axios.create({
baseURL: process.env.BASE_API, // api的base_url
timeout: 10000 // 請求超時(shí)時(shí)間
})
// request 攔截器
service.interceptors.request.use(
config => {
// 這里可以自定義一些config 配置
// loading + 1
store.dispatch('SetLoading', true)
return config
},
error => {
// 這里處理一些請求出錯(cuò)的情況
// 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 正常放回時(shí)的邏輯
// loading - 1
store.dispatch('SetLoading', false)
return res
},
error => {
// 這里處理一些response 出錯(cuò)時(shí)的邏輯
// loading - 1
store.dispatch('SetLoading', false)
return Promise.reject(error)
}
)
export default service
其次,在 src/components/ 下創(chuàng)建 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 中引入即可。
附: 為了方便演示,項(xiàng)目里出了初始化包括 axios 、 vuex 、 vue-router , 項(xiàng)目使用了 js-cookie 、 element-ui 等,此步驟之后,會(huì)改造一下 app.vue ;
vue router守衛(wèi)
vue-router 提供了非常方便的鉤子,可以讓我們在做路由跳轉(zhuǎn)的時(shí)候做一些操作,比如常見的權(quán)限驗(yàn)證。
首先,需要在 src/utils/ 下創(chuàng)建 auth.js ,用于存儲(chǔ)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/ 下創(chuàng)建 permission.js :
import router from '@/router'
import store from '@/store'
import {
getToken
} from './auth'
import NProgress from 'nprogress' // 進(jìn)度條
import 'nprogress/nprogress.css' // 進(jìn)度條樣式
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 { // 實(shí)時(shí)拉取用戶的信息
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() // 結(jié)束Progress
})
Nginx try_files 以及 404
nginx 配置如下:
location / {
root /www/vue-project-demo/;
try_files $uri $uri/ /index.html index.htm;
}
try_files : 可以理解為nginx 不處理你的這些url地址請求; 那么服務(wù)器如果不處理了,前端要自己做一些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' }
]
})
然后寫一個(gè)404 的view 就ok 。
常用的utils
到現(xiàn)在為止, utils/ 目錄下應(yīng)該有 auth.js 、permission.js、request.js ;
- 那么對與一些常用的方法,你可以放到
utils/common.js里,統(tǒng)一install到vue實(shí)例上,并通過Vue.use()使用; - 對于一些全局的過濾器,你仍可以放到
utils/filters.js里,使用Vue.fileter()注冊到全局; - 對于一些全局方法,又不是很長用到的,可以放到
utils/index.js,哪里使用哪里import
mixin減少項(xiàng)目冗余代碼
直接看代碼吧,要寫奔潰了....
使用cdn減少文件打包的體積
到此時(shí),看我項(xiàng)目里都用了什么:

主要就是這些,那么執(zhí)行一下打包命令呢?

可能這時(shí)候你還覺得沒什么, 單文件最多的還沒超過 800kb 呢...
我把項(xiàng)目通過 jenkins 部署到服務(wù)器上,看一下訪問:

可以看到, chunk-vendors 加載了將近12秒,這還是只有框架沒有內(nèi)容的前提下,當(dāng)然你可能說你項(xiàng)目中用不到 vuex 、用不到 js-cookie ,但是隨著項(xiàng)目的迭代維護(hù),最后肯定不比現(xiàn)在小。
那么,有些文件在生產(chǎn)環(huán)境是不是可以嘗試使用 cdn 呢?
為了方便對比,這里保持原代碼不動(dòng)( master 分支),再切出來一個(gè)分支改動(dòng)優(yōu)化( optimize 分支), 上代碼:
// vue.config.js 修改
const path = require('path')
function resolve(dir) {
return path.join(__dirname, './', dir)
}
// cdn預(yù)加載使用
const externals = {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios',
'element-ui': 'ELEMENT',
'js-cookie': 'Cookies',
'nprogress': 'NProgress'
}
const cdn = {
// 開發(fā)環(huán)境
dev: {
css: [
'https://unpkg.com/element-ui/lib/theme-chalk/index.css',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
],
js: []
},
// 生產(chǎn)環(huán)境
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 => {
// 這里是對環(huán)境的配置,不同環(huán)境對應(yīng)不同的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參數(shù)到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, 如果不這樣做會(huì)添加在此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. 生產(chǎn)環(huán)境npm包轉(zhuǎn)CDN
myConfig.externals = externals
}
if (process.env.NODE_ENV === 'development') {
/**
* 關(guān)閉host check,方便使用ngrok之類的內(nèi)網(wǎng)轉(zhuǎn)發(fā)工具
*/
myConfig.devServer = {
disableHostCheck: true
}
}
// open: true,
// hot: true
// // https: true,
// // proxy: {
// // '/proxy': {
// // target: 'http://47.94.138.75',
// // // changeOrigin: true,
// // pathRewrite: {
// // '^/proxy': ''
// // }
// // }
// // },
// }
return myConfig
}
}
最后去除 main.js 中引入的 import 'element-ui/lib/theme-chalk/index.css'
OK ,現(xiàn)在執(zhí)行一下 build :

可以看到,相對于 793.20KB , 61.94k 小了將近 13 倍?。?!
把這個(gè)分支部署到服務(wù)器,話不多說,對比一下就好:

使用Gzip 加速
引入 compression-webpack-plugin : npm i -D compression-webpack-plugin https://www.webpackjs.com/plugins/compression-webpack-plugin/
修改 vue.config.js ,老規(guī)矩,上最全的代碼:
const path = require('path')
const CompressionWebpackPlugin = require('compression-webpack-plugin')
function resolve(dir) {
return path.join(__dirname, './', dir)
}
// cdn預(yù)加載使用
const externals = {
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios',
'element-ui': 'ELEMENT',
'js-cookie': 'Cookies',
'nprogress': 'NProgress'
}
const cdn = {
// 開發(fā)環(huán)境
dev: {
css: [
'https://unpkg.com/element-ui/lib/theme-chalk/index.css',
'https://cdn.bootcss.com/nprogress/0.2.0/nprogress.min.css'
],
js: []
},
// 生產(chǎn)環(huán)境
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 => {
// 這里是對環(huán)境的配置,不同環(huán)境對應(yīng)不同的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參數(shù)到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, 如果不這樣做會(huì)添加在此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. 生產(chǎn)環(huán)境npm包轉(zhuǎn)CDN
myConfig.externals = externals
myConfig.plugins = []
// 2. 構(gòu)建時(shí)開啟gzip,降低服務(wù)器壓縮對CPU資源的占用,服務(wù)器也要相應(yīng)開啟gzip
productionGzip && myConfig.plugins.push(
new CompressionWebpackPlugin({
test: new RegExp('\\.(' + productionGzipExtensions.join('|') + ')$'),
threshold: 8192,
minRatio: 0.8
})
)
}
if (process.env.NODE_ENV === 'development') {
/**
* 關(guān)閉host check,方便使用ngrok之類的內(nèi)網(wǎng)轉(zhuǎn)發(fā)工具
*/
myConfig.devServer = {
disableHostCheck: true
}
}
// open: true,
// hot: true
// // https: true,
// // proxy: {
// // '/proxy': {
// // target: 'http://47.94.138.75',
// // // changeOrigin: true,
// // pathRewrite: {
// // '^/proxy': ''
// // }
// // }
// // },
// }
return myConfig
}
}
再次運(yùn)行 build ,我們會(huì)發(fā)現(xiàn) dist/ 下所有的 .js 和 .css 都會(huì)多出一個(gè) .js.gz、.css.gz 的文件,這就是我們需要的壓縮文件,可以看到最大的只有 18.05KB ,想想是不是比較激動(dòng)...

當(dāng)然,這玩意還需要服務(wù)端支持,也就是配置 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 :

配置成功的話,可以看到加載的是比較小的 Gzip :

在 response headers 里會(huì)有一個(gè) Content-Encoding:gzip

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 深入理解Vue-cli搭建項(xiàng)目后的目錄結(jié)構(gòu)探秘
- vue-cli3搭建項(xiàng)目的詳細(xì)步驟
- vue-cli2.0轉(zhuǎn)3.0之項(xiàng)目搭建的詳細(xì)步驟
- 用Vue-cli搭建的項(xiàng)目中引入css報(bào)錯(cuò)的原因分析
- 使用Vue-cli 3.0搭建Vue項(xiàng)目的方法
- vue-cli webpack模板項(xiàng)目搭建及打包時(shí)路徑問題的解決方法
- 詳解Vue使用 vue-cli 搭建項(xiàng)目
- 詳解搭建一個(gè)vue-cli的移動(dòng)端H5開發(fā)模板
- vue-cli5搭建vue項(xiàng)目的實(shí)現(xiàn)步驟
相關(guān)文章
element-ui修改el-form-item樣式的詳細(xì)示例
在使用element-ui組件庫的時(shí)候經(jīng)常需要修改原有樣式,下面這篇文章主要給大家介紹了關(guān)于element-ui修改el-form-item樣式的詳細(xì)示例,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11
在 Vue-CLI 中引入 simple-mock實(shí)現(xiàn)簡易的 API Mock 接口數(shù)據(jù)模擬
本文以 Vue-CLI 為例介紹引入 simple-mock 實(shí)現(xiàn)前端開發(fā)數(shù)據(jù)模擬的步驟。感興趣的朋友跟隨小編一起看看吧2018-11-11
Vue中el-menu-item實(shí)現(xiàn)路由跳轉(zhuǎn)的完整步驟
這篇文章主要給大家介紹了關(guān)于Vue中el-menu-item實(shí)現(xiàn)路由跳轉(zhuǎn)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-09-09
vue vue-Router默認(rèn)hash模式修改為history需要做的修改詳解
今天小編就為大家分享一篇vue vue-Router默認(rèn)hash模式修改為history需要做的修改詳解,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09
解決vue中使用history.replaceState?更改url?vue?router?無法感知的問題
這篇文章主要介紹了vue中使用history.replaceState?更改url?vue?router?無法感知的問題,本文給大家分享修復(fù)這個(gè)問題的方法,需要的朋友可以參考下2022-09-09

