作者:羊先生
轉發鏈接:
https://segmentfault.com/a/1190000022512358
前言
vue.config.js
完整的架構配置
<code>const
path =require
('path'
);const
UglifyJsPlugin =require
('uglifyjs-webpack-plugin'
)const
CompressionWebpackPlugin =require
('compression-webpack-plugin'
);const
{ HashedModuleIdsPlugin } =require
('webpack'
);function
resolve
(dir
) {return
path.join(__dirname, dir) }const
isProduction = process.env.NODE_ENV ==='production'
;const
externals = {'vue'
:'Vue'
,'vue-router'
:'VueRouter'
,'vuex'
:'Vuex'
,'axios'
:'axios'
,"element-ui"
:"ELEMENT"
}const
cdn = {dev
: {css
: ['https://unpkg.com/element-ui/lib/theme-chalk/index.css'
],js
: [] },build
: {css
: ['https://unpkg.com/element-ui/lib/theme-chalk/index.css'
],js
: ['https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js'
,'https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.min.js'
,'https://cdn.jsdelivr.net/npm/[email protected]/dist/vuex.min.js'
,'https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js'
,'https://unpkg.com/element-ui/lib/index.js'
] } }module
.exports = {lintOnSave
:false
,productionSourceMap
:false
,publicPath
:'./'
,outputDir
: process.env.outputDir,chainWebpack
:config
=> { config.resolve.alias .set('@'
, resolve('src'
)) config.module .rule('images'
) .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/
) .use('image-webpack-loader'
) .loader('image-webpack-loader'
) .options({bypassOnDebug
:true
}) config.optimization.delete('splitChunks'
) 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 }) config .plugin('webpack-bundle-analyzer'
) .use(require
('webpack-bundle-analyzer'
).BundleAnalyzerPlugin) },configureWebpack
:config
=> {const
plugins = [];if
(isProduction) { plugins.push(new
UglifyJsPlugin({uglifyOptions
: {output
: {comments
:false
, },warnings
:false
,compress
: {drop_console
:true
,drop_debugger
:false
,pure_funcs
: ['console.log'
] } } }) ) plugins.push(new
CompressionWebpackPlugin({algorithm
:'gzip'
,test
:/\.(js|css)$/
,threshold
:10000
,deleteOriginalAssets
:false
,minRatio
:0.8
}) ) plugins.push(new
HashedModuleIdsPlugin() ) config.optimization = {runtimeChunk
:'single'
,splitChunks
: {chunks
:'all'
,maxInitialRequests
:Infinity
,minSize
:1000
*60
,cacheGroups
: {vendor
: {test
:/[\\/]node_modules[\\/]/
, name(module
) {const
packageName =module
.context.match(/[\\/]node_modules[\\/](.*?)([\\/]|$)/
)[1
]return
`npm.
${packageName.replace(
'@'
,''
)}` } } } } }; config.performance = {hints
:'warning'
,maxEntrypointSize
:1000
*500
,maxAssetSize
:1000
*1000
,assetFilter
:function
(assetFilename
) {return
assetFilename.endsWith('.js'
); } } config.externals = externals; }return
{ plugins } },pluginOptions
: {'style-resources-loader'
: {preProcessor
:'less'
,patterns
: [resolve('./src/style/theme.less'
)] } },devServer
: {open
:false
,host
:'0.0.0.0'
,port
:6060
,https
:false
,hotOnly
:false
,proxy
: {'^/sso'
: {target
: process.env.VUE_APP_SSO,ws
:true
,secure
:false
,changeOrigin
:true
} } } }/<code>
html模板配置cdn
<code> ><
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
="favicon.ico"
><
title
><
%=
htmlWebpackPlugin.options.title
%>title
><
%
for
(var
i
in
htmlWebpackPlugin.options.cdn
&&htmlWebpackPlugin.options.cdn.css
) { %><
link
href
=""
rel
="preload"
as
="style"
/><
link
href
=""
rel
="stylesheet"
/><
%
} %>head
><
body
><
noscript
><
strong
> We're sorry but<
%=
htmlWebpackPlugin.options.title
%> doesn't work properly without JavaScript enabled. Please enable it to continue.strong
>noscript
><
div
id
="app"
>div
><
%
for
(var
i
in
htmlWebpackPlugin.options.cdn
&&htmlWebpackPlugin.options.cdn.js
) { %><
script
src
=""
>script
><
%
} %>body
>html
>/<code>
開啟Gzip壓縮,包含文件js、css
<code>new
CompressionWebpackPlugin({
algorithm:
'gzip'
,
test:
/\.(js|css)$/,
//
匹配文件名
threshold:
10000
,
//
對超過10k的數據壓縮
deleteOriginalAssets:
false
,
//
不刪除源文件
minRatio:
0.8
//
壓縮比
})
/<code>
去掉註釋、去掉console.log
安裝cnpm i uglifyjs-webpack-plugin -D
<code>const
UglifyJsPlugin
=
require('uglifyjs-webpack-plugin')
new
UglifyJsPlugin({
uglifyOptions:
{
output:
{
comments:
false
,
//
去掉註釋
},
warnings:
false
,
compress:
{
drop_console:
true
,
drop_debugger:
false
,
pure_funcs:
['console.log']
//移除console
}
}
})
/<code>
壓縮圖片
<code>chainWebpack:config
=> { config.module .rule('images'
) .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/
) .use('image-webpack-loader'
) .loader('image-webpack-loader'
) .options({bypassOnDebug
:true
}) }/<code>
本地代理
<code>devServer:
{
open:
false
,
//
自動啟動瀏覽器
host:
'0.0.0.0'
,
//
localhost
port:
6060
,
//
端口號
https:
false
,
hotOnly:
false
,
//
熱更新
proxy:
{
'^/sso'
:
{
target:
process.env.VUE_APP_SSO,
//
重寫路徑
ws:
true
,
//開啟WebSocket
secure:
false
,
//
如果是https接口,需要配置這個參數
changeOrigin:
true
}
}
}
/<code>
設置vscode 識別別名
在vscode中插件安裝欄搜索 Path Intellisense 插件,打開settings.json文件添加 以下代碼 "@": "${workspaceRoot}/src",按以下添加
<code>{"workbench.iconTheme"
:"material-icon-theme"
,"editor.fontSize"
:16
,"editor.detectIndentation"
:false
,"guides.enabled"
:false
,"workbench.colorTheme"
:"Monokai"
,"path-intellisense.mappings"
: {"@"
:"${workspaceRoot}/src"
} }/<code>
在項目package.json所在同級目錄下創建文件jsconfig.json
<code>{"compilerOptions"
: {"target"
:"ES6"
,"module"
:"commonjs"
,"allowSyntheticDefaultImports"
:true
,"baseUrl"
:"./"
,"paths"
: {"@/*"
: ["src/*"
] } },"exclude"
: ["node_modules"
] }/<code>
如果還沒請客官移步在vscode中使用別名@按住ctrl也能跳轉對應路徑
配置環境變量開發模式、測試模式、生產模式
在根目錄新建
.env.development
<code>NODE_ENV
='development'
VUE_APP_SSO
='http://http://localhost:9080'
/<code>
.env.test
<code>NODE_ENV
='production'
VUE_APP_MODE
='test'
VUE_APP_SSO
='http://http://localhost:9080'
outputDir
= test/<code>
.env.production
<code>NODE_ENV
='production'
VUE_APP_SSO
='http://http://localhost:9080'
/<code>
package.json
<code>"scripts"
: {"build"
:"vue-cli-service build"
,"lint"
:"vue-cli-service lint"
,"dev"
:"vue-cli-service serve"
,"test"
:"vue-cli-service build --mode test"
,"publish"
:"vue-cli-service build && vue-cli-service build --mode test"
}/<code>
請求路由動態添加
router/index.js文件
<code>import
Vuefrom
'vue'
;import
VueRouterfrom
'vue-router'
Vue.use(VueRouter)import
defaultRouterfrom
'./defaultRouter'
import
dynamicRouterfrom
'./dynamicRouter'
;import
storefrom
'@/store'
;const
router =new
VueRouter({routes
: defaultRouter,mode
:'hash'
, scrollBehavior(to,from
, savedPosition) {if
(savedPosition && to.meta.keepAlive) {return
savedPosition; }return
new
Promise
((
resolve, reject
) => { setTimeout(()
=> { resolve({x
:0
,y
:0
}) },200
) }) } })const
selfaddRoutes =function
(params
) { router.matcher =new
VueRouter().matcher; router.addRoutes(params); } router.beforeEach((
to,
from
, next) => {const
{ hasRoute } = store.state;if
(hasRoute) { next() }else
{ dynamicRouter(to,from
, next, selfaddRoutes) } })export
default
router;/<code>
dynamicRouter.js
<code>import
httpfrom
'@/http/request'
;import
defaultRouterfrom
'./defaultRouter'
import
storefrom
'@/store'
const
menusMap =function
(menu
) {return
menu.map(v
=> {const
{ path, name, component } = vconst
item = { path, name,component
:()
=>import
(`@/
${component}
`) }return
item; }) }const
addPostRouter =function
(to, from, next, selfaddRoutes
) { http.windPost('/mock/menu'
) .then(menu
=> { defaultRouter[0
].children.push(...menusMap(menu)); selfaddRoutes(defaultRouter); store.commit('hasRoute'
,true
); next({ ...to,replace
:true
}) }) }export
default
addPostRouter;/<code>
defaultRouter.js 默認路由
<code>const
main =r
=>require
.ensure([],()
=> r(require
('@/layout/main.vue'
)),'main'
)const
index =r
=>require
.ensure([],()
=> r(require
('@/view/index/index.vue'
)),'index'
)const
about =r
=>require
.ensure([],()
=> r(require
('@/view/about/about.vue'
)),'about'
)const
detail =r
=>require
.ensure([],()
=> r(require
('@/view/detail/detail.vue'
)),'detail'
)const
error =r
=>require
.ensure([],()
=> r(require
('@/view/404/404.vue'
)),'error'
);const
defaultRouter = [ { path:"/"
, component: main, redirect: { name:"index"
}, children:[ { path:'/index'
, component: index, name:'index'
, meta: { title:'index'
} }, { path:'/about'
, component: about, name:'about'
, meta: { title:'about'
} }, { path:'/detail'
, component: detail, name:'detail'
, meta: { title:'detail'
} } ] }, { path:'/404'
, component: error, name:'404'
, meta: { title:'404'
} } ]export
default
defaultRouter;/<code>
axios配置
<code>import
axiosfrom
"axios"
;import
mergefrom
'lodash/merge'
import
qsfrom
'qs'
const
http = axios.create({timeout
:1000
*30
,withCredentials
:true
, }); http.interceptors.request.use(function
(config
) {return
config; },function
(error
) {return
Promise
.reject(error); }); http.interceptors.response.use(response
=> {if
(response.data && (response.data.code ===401
)) { }return
response }, error => {return
Promise
.reject(error) }) http.adornUrl =(
url
) => {return
url; } http.adornParams =(
params = {}, openDefultParams =
true
) => {var
defaults = {t
:new
Date
().getTime() }return
openDefultParams ? merge(defaults, params) : params } http.adornData =(
data = {}, openDefultdata =
true
, contentType ='json'
) => {var
defaults = {t
:new
Date
().getTime() } data = openDefultdata ? merge(defaults, data) : datareturn
contentType ==='json'
?JSON
.stringify(data) : qs.stringify(data) } http.windPost =function
(url, params
) {return
new
Promise
((
resolve, reject
) => { http.post(http.adornUrl(url), qs.stringify(params)) .then(res
=> { resolve(res.data) }) .catch(error
=> { reject(error) }) }) } http.windJsonPost =function
(url, params
) {return
new
Promise
((
resolve, reject
) => { http.post(http.adornUrl(url), http.adornParams(params)) .then(res
=> { resolve(res.data) }) .catch(error
=> { reject(error) }) }) } http.windGet =function
(url, params
) {return
new
Promise
((
resolve, reject
) => { http.get(http.adornUrl(url), {params
: params }) .then(res
=> { resolve(res.data) }) .catch(error
=> { reject(error) }) }) } http.upLoadPhoto =function
(url, params, callback
) {let
config = {}if
(callback !==null
) { config = {onUploadProgress
:function
(progressEvent
) { callback(progressEvent) } } }return
new
Promise
((
resolve, reject
) => { http.post(http.adornUrl(url), http.adornParams(params), config) .then(res
=> { resolve(res.data) }) .catch(error
=> { reject(error) }) }) }export
default
http;/<code>
添加mock數據
<code>const
Mock =require
('mockjs'
)const
Random = Mock.Randomconst
produceNewsData =function
() {let
newsList = []for
(let
i =0
; i3
; i++) {let
newNewsObject = {}if
(i ===0
){ newNewsObject.path ='/add/article'
; newNewsObject.name ='add-article'
; newNewsObject.component ='modules/add/article/article'
; }if
(i ===1
){ newNewsObject.path ='/detail/article'
; newNewsObject.name ='detail-article'
; newNewsObject.component ='modules/detail/article/article'
}if
(i ===2
){ newNewsObject.path ='/edit/article'
; newNewsObject.name ='edit-article'
; newNewsObject.component ='modules/edit/article/article'
} newsList.push(newNewsObject) }return
newsList; } Mock.mock('/mock/menu'
, produceNewsData)/<code>
配置全局less
<code>pluginOptions
: {'style-resources-loader'
: {preProcessor
:'less'
,patterns
: [resolve('./src/style/theme.less'
)] } }/<code>
只打包改變的文件
安裝cnpm i webpack -D
<code>const
{ HashedModuleIdsPlugin } =require
('webpack'
); configureWebpack:config
=> {const
plugins = []; plugins.push(new
HashedModuleIdsPlugin() ) }/<code>
開啟分析打包日誌
安裝cnpm i webpack-bundle-analyzer -D
<code>chainWebpack:config
=> { config .plugin('webpack-bundle-analyzer'
) .use(require
('webpack-bundle-analyzer'
).BundleAnalyzerPlugin) }/<code>
完整代碼
點擊獲取完整代碼
github:https://github.com/hangjob/vue-admin
推薦Vue和React學習資料文章:
《聊聊昨晚尤雨溪現場針對Vue3.0 Beta版本新特性知識點彙總》
《【新消息】Vue 3.0 Beta 版本發佈,你還學的動麼?》
《Vue + Koa從零打造一個H5頁面可視化編輯器——Quark-h5》
《深入淺出Vue3 跟著尤雨溪學 TypeScript 之 Ref 【實踐】》
《手把手教你深入淺出vue-cli3升級vue-cli4的方法》
《Vue 3.0 Beta 和React 開發者分別槓上了 》
《手把手教你用vue drag chart 實現一個可以拖動 / 縮放的圖表組件》
《Vue3 嚐鮮》
《手把手讓你成為更好的Vue.js開發人員的12個技巧和竅門【實踐】》
《2020 年,Vue 受歡迎程度是否會超過 React?》
《手把手教你Vue解析pdf(base64)轉圖片【實踐】》
《手把手教你Vue之父子組件間通信實踐講解【props、$ref 、$emit】》
《深入淺出Vue3 的響應式和以前的區別到底在哪裡?【實踐】》
《乾貨滿滿!如何優雅簡潔地實現時鐘翻牌器(支持JS/Vue/React)》
《基於Vue/VueRouter/Vuex/Axios登錄路由和接口級攔截原理與實現》
《手把手教你D3.js 實現數據可視化極速上手到Vue應用》
《吃透 Vue 項目開發實踐|16個方面深入前端工程化開發技巧【上】》
《吃透 Vue 項目開發實踐|16個方面深入前端工程化開發技巧【中】》
《吃透 Vue 項目開發實踐|16個方面深入前端工程化開發技巧【下】》
《Vue 3.0 Beta 和React 開發者分別槓上了》
《手把手深入Redux react-redux中間件設計及原理(上)【實踐】》
《手把手深入Redux react-redux中間件設計及原理(下)【實踐】》
《為了學好 React Hooks, 我解析了 Vue Composition API》
《【React 高級進階】探索 store 設計、從零實現 react-redux》
《深入淺出掌握React 與 React Native這兩個框架》
《你需要的 React + TypeScript 50 條規範和經驗 》
《手把手教你深入淺出實現Vue3 & React Hooks新UI Modal彈窗》
《全平臺(Vue/React/微信小程序)任意角度旋圖片裁剪組件》
作者:羊先生
轉發鏈接:
https://segmentfault.com/a/1190000022512358