├── .babelrc ├── .browserslistrc ├── .editorconfig ├── .env ├── .env.local ├── .env.production ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── babel.config.js ├── dist ├── favicon.ico ├── index.html └── static │ ├── css │ ├── app.dfcc6672.css │ └── home.ebfbb232.css │ └── js │ ├── app.6676dd50.js │ ├── home.19c33026.js │ ├── vendor.8c3e1f68.js │ └── vendor.8c3e1f68.js.gz ├── package.json ├── postcss.config.js ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── api │ ├── articles.js │ └── fetch.js ├── assets │ └── css │ │ └── common.less ├── lib │ └── utils.js ├── main.js ├── router │ └── index.js ├── store │ ├── index.js │ └── modules │ │ └── articles.js └── views │ └── index.vue ├── vue.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [] 3 | } 4 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 8 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js, jsx, ts, tsx, vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | NODE_ENV = 'development' 2 | PUBLIC_PATH = '/' 3 | VUE_APP_BASE_API='https://www.boblog.com/api/v1' 4 | -------------------------------------------------------------------------------- /.env.local: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb/vue-cli3-template/fd466e24a86e11e05543748fd1f243427755013a/.env.local -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | NODE_ENV = 'production' 2 | PUBLIC_PATH = '/' 3 | VUE_APP_BASE_API='https://api.com/api/v1' 4 | 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /config/ 3 | /cmas/ 4 | /node_modules/ 5 | /src/utils/ 6 | /tests/unit/ 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | 'plugin:vue/essential', 8 | '@vue/airbnb', 9 | ], 10 | parserOptions: { 11 | parser: 'babel-eslint', 12 | }, 13 | rules: { 14 | 'no-console': 'off', 15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.idea 13 | *.pid.lock 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # Bower dependency directory (https://bower.io/) 28 | bower_components 29 | 30 | # node-waf configuration 31 | .lock-wscript 32 | 33 | # Compiled binary addons (https://nodejs.org/api/addons.html) 34 | build/Release 35 | 36 | # Dependency directories 37 | node_modules/ 38 | jspm_packages/ 39 | 40 | # TypeScript v1 declaration files 41 | typings/ 42 | 43 | # Optional npm cache directory 44 | .npm 45 | 46 | # Optional eslint cache 47 | .eslintcache 48 | 49 | # Optional REPL history 50 | .node_repl_history 51 | 52 | # Output of 'npm pack' 53 | *.tgz 54 | 55 | # Yarn Integrity file 56 | .yarn-integrity 57 | 58 | # dotenv environment variables file 59 | # .env 60 | 61 | # next.js build output 62 | .next 63 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 梁凤波 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 项目介绍 2 | 基于 Vue-cli3 搭建的前端开发脚手架项目模板,主要包括有以下内容:Webpack4.x 性能调优配置,Vue.js 全家桶,移动端 vw 适配,单元测试等功能,仅供参考,欢迎大家围观指教! 3 | 4 | ## 项目特点 5 | [![license](https://img.shields.io/badge/vue-2.6.10-brightgreen.svg)](https://github.com/vuejs/vue) 6 | [![license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/liangfengbo/vue-cli3-template/blob/master/LICENSE) 7 | 8 | ## 特征 9 | - [x] Babel 10 | - [x] VueRouter 11 | - [x] Vuex 12 | - [x] CSS 预编译工具:Less 13 | - [x] HTTP 库:Axios 14 | - [x] 代码规范:eslint airbnb 规范 15 | - [x] 业务代码和第三方库区分打包:DllPlugin 16 | - [x] 删除冗余代码:UglifyJsPlugin 17 | - [x] 开启 Gizp 压缩:compression-webpack-plugin 18 | - [x] 配置引入公共样式文件 19 | - [x] 使用 alias 简化路径 20 | - [x] vw 移动端适配 21 | - [x] 文件结构可视化:webpack-bundle-analyzer 22 | 23 | ## 安装及快速开始 24 | ``` 25 | # 克隆项目 26 | $ git clone https://github.com/liangfengbo/vue-cli3-template 27 | 28 | # 进入目录 29 | $ cd vue-cli3-template 30 | 31 | # 安装依赖包 32 | $ yarn install 33 | 34 | # 启动项目 35 | $ yarn serve 36 | 37 | # eslint 检测 38 | $ yarn lint 39 | 40 | # eslint 修复 41 | $ yarnr lint --fix 42 | 43 | # 项目构建打包 44 | $ yarn run build 45 | 46 | # 项目构建打包分析 47 | $ yarn run build --report 48 | 49 | # 现代模式打包 50 | $ yarn build --modren 51 | ``` 52 | 53 | ## FAQ 54 | 1. 没有yarn环境,npm 可以吗? 55 | > 答:可以的,建议使用 yarn,yarn 比 npm 速度快,主要是安装版本统一。 56 | 2. vue.config.js 里面的一些配置可以不需要吗?或者我新增一些配置可以吗? 57 | > 答:可以的,你可以根据你的实际需要进行修改或增删配置的,比如你不需要 开启 Gizp 压缩,你在 vue.config.js 里面删除 开启gzip 内容即可。且 env 文件, 代理服务器的接口或转发路径,这些肯定需要改为你实际开发中的接口路径的。 58 | 3. ... 更多问题请到 [Issues](https://github.com/liangfengbo/vue-cli3-template/issues)查阅,或者有问题请到 [Issues 提问](https://github.com/liangfengbo/vue-cli3-template/issues/new),我会及时回复的,如果对你有帮助,请你点个 star 鼓励一下,谢谢!共勉! 59 | ### MIT 60 | [@梁凤波](https://github.com/liangfengbo/vue-cli3-template/blob/master/LICENSE) 61 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset', 4 | ] 5 | }; 6 | -------------------------------------------------------------------------------- /dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lfb/vue-cli3-template/fd466e24a86e11e05543748fd1f243427755013a/dist/favicon.ico -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | webapp
-------------------------------------------------------------------------------- /dist/static/css/app.dfcc6672.css: -------------------------------------------------------------------------------- 1 | #app{font-family:Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,微软雅黑,Arial,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-align:center;color:#2c3e50} -------------------------------------------------------------------------------- /dist/static/css/home.ebfbb232.css: -------------------------------------------------------------------------------- 1 | .home[data-v-84b29466]{margin:4.267vw;font-size:4vw;font-weight:700} -------------------------------------------------------------------------------- /dist/static/js/app.6676dd50.js: -------------------------------------------------------------------------------- 1 | !function(e){function t(t){for(var n,o,u=t[0],c=t[1],s=t[2],f=0,p=[];fdocument.createEvent("Event").timeStamp&&(un=function(){return fn.now()})}function ln(){var t,e;for(cn=un(),an=!0,en.sort((function(t,e){return t.id-e.id})),sn=0;snt.id;)n--;en.splice(n+1,0,t)}else en.push(t);on||(on=!0,te(ln))}}(this)},dn.prototype.run=function(){if(this.active){var t=this.get();if(t!==this.value||s(t)||this.deep){var e=this.value;if(this.value=t,this.user)try{this.cb.call(this.vm,t,e)}catch(t){Bt(t,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,t,e)}}},dn.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},dn.prototype.depend=function(){for(var t=this.deps.length;t--;)this.deps[t].depend()},dn.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||m(this.vm._watchers,this);for(var t=this.deps.length;t--;)this.deps[t].removeSub(this);this.active=!1}};var hn={enumerable:!0,configurable:!0,get:$,set:$};function vn(t,e,n){hn.get=function(){return this[e][n]},hn.set=function(t){this[e][n]=t},Object.defineProperty(t,n,hn)}function yn(t){t._watchers=[];var e=t.$options;e.props&&function(t,e){var n=t.$options.propsData||{},r=t._props={},o=t.$options._propKeys=[];function i(i){o.push(i);var a=Dt(i,e,n,t);At(r,i,a),i in t||vn(t,"_props",i)}for(var a in t.$parent&&Ot(!1),e)i(a);Ot(!0)}(t,e.props),e.methods&&function(t,e){for(var n in t.$options.props,e)t[n]="function"!=typeof e[n]?$:A(e[n],t)}(t,e.methods),e.data?function(t){var e=t.$options.data;u(e=t._data="function"==typeof e?function(t,e){pt();try{return t.call(e,e)}catch(t){return Bt(t,e,"data()"),{}}finally{dt()}}(e,t):e||{})||(e={});for(var n,r=Object.keys(e),o=t.$options.props,i=(t.$options.methods,r.length);i--;){var a=r[i];o&&b(o,a)||36!==(n=(a+"").charCodeAt(0))&&95!==n&&vn(t,"_data",a)}Ct(e,!0)}(t):Ct(t._data={},!0),e.computed&&function(t,e){var n=t._computedWatchers=Object.create(null),r=rt();for(var o in e){var i=e[o],a="function"==typeof i?i:i.get;r||(n[o]=new dn(t,a||$,$,mn)),o in t||gn(t,o,i)}}(t,e.computed),e.watch&&e.watch!==tt&&function(t,e){for(var n in e){var r=e[n];if(Array.isArray(r))for(var o=0;oparseInt(this.max)&&Nn(a,s[0],s,this._vnode)),e.data.keepAlive=!0}return e||t&&t[0]}}};Dn=Tn,Fn={get:function(){return F}},Object.defineProperty(Dn,"config",Fn),Dn.util={warn:ct,extend:j,mergeOptions:It,defineReactive:At},Dn.set=St,Dn.delete=jt,Dn.nextTick=te,Dn.observable=function(t){return Ct(t),t},Dn.options=Object.create(null),D.forEach((function(t){Dn.options[t+"s"]=Object.create(null)})),j((Dn.options._base=Dn).options.components,Bn),Dn.use=function(t){var e=this._installedPlugins||(this._installedPlugins=[]);if(-1=o||t.timeStamp<=0||t.target.ownerDocument!==document)return i.apply(this,arguments)}}Ar.addEventListener(t,e,et?{capture:n,passive:r}:n)}function Lr(t,e,n,r){(r||Ar).removeEventListener(t,e._wrapper||e,n)}function Rr(t,e){if(!r(t.data.on)||!r(e.data.on)){var n=e.data.on||{},i=t.data.on||{};Ar=e.elm,function(t){if(o(t[jr])){var e=X?"change":"input";t[e]=[].concat(t[jr],t[e]||[]),delete t[jr]}o(t[Er])&&(t.change=[].concat(t[Er],t.change||[]),delete t[Er])}(n),ie(n,i,Pr,Lr,$r,e.context),Ar=void 0}}var Ir,Nr={create:Rr,update:Rr};function Dr(t,e){if(!r(t.data.domProps)||!r(e.data.domProps)){var n,i,a,s,c=e.elm,u=t.data.domProps||{},f=e.data.domProps||{};for(n in o(f.__ob__)&&(f=e.data.domProps=j({},f)),u)n in f||(c[n]="");for(n in f){if(i=f[n],"textContent"===n||"innerHTML"===n){if(e.children&&(e.children.length=0),i===u[n])continue;1===c.childNodes.length&&c.removeChild(c.childNodes[0])}if("value"===n&&"PROGRESS"!==c.tagName){var l=r(c._value=i)?"":String(i);s=l,(a=c).composing||"OPTION"!==a.tagName&&!function(t,e){var n=!0;try{n=document.activeElement!==t}catch(t){}return n&&t.value!==e}(a,s)&&!function(t,e){var n=t.value,r=t._vModifiers;if(o(r)){if(r.number)return h(n)!==h(e);if(r.trim)return n.trim()!==e.trim()}return n!==e}(a,s)||(c.value=l)}else if("innerHTML"===n&&ir(c.tagName)&&r(c.innerHTML)){(Ir=Ir||document.createElement("div")).innerHTML=""+i+"";for(var p=Ir.firstChild;c.firstChild;)c.removeChild(c.firstChild);for(;p.firstChild;)c.appendChild(p.firstChild)}else if(i!==u[n])try{c[n]=i}catch(t){}}}}var Mr={create:Dr,update:Dr},Fr=_((function(t){var e={},n=/:(.+)/;return t.split(/;(?![^(]*\))/g).forEach((function(t){if(t){var r=t.split(n);1=a&&s()};setTimeout((function(){u]*>)/g,v=/\$([$&'`]|\d\d?)/g;r("replace",2,(function(t,e,n){return[function(n,r){var o=c(this),i=null==n?void 0:n[t];return void 0!==i?i.call(n,o,r):e.call(String(o),n,r)},function(t,i){var c=n(e,t,this,i);if(c.done)return c.value;var d=o(t),h=String(this),v="function"==typeof i;v||(i=String(i));var y=d.global;if(y){var m=d.unicode;d.lastIndex=0}for(var g=[];;){var b=f(d,h);if(null===b)break;if(g.push(b),!y)break;""===String(b[0])&&(d.lastIndex=u(h,a(d.lastIndex),m))}for(var _,w="",x=0,O=0;Odocument.F=Object"),t.close(),p=t.F;n--;)delete p[l][a[n]];return p()};t.exports=Object.create||function(t,e){var n;return null!==t?(r[l]=o(t),n=new r,r[l]=null,n[f]=t):n=p(),void 0===e?n:i(n,e)},s[f]=!0},"7dd0":function(t,e,n){"use strict";function r(){return this}var o=n("23e7"),i=n("9ed3"),a=n("e163"),s=n("d2bb"),c=n("d44e"),u=n("9112"),f=n("6eeb"),l=n("b622"),p=n("c430"),d=n("3f8c"),h=n("ae93"),v=h.IteratorPrototype,y=h.BUGGY_SAFARI_ITERATORS,m=l("iterator"),g="values",b="entries";t.exports=function(t,e,n,l,h,_,w){function x(t){if(t===h&&$)return $;if(!y&&t in j)return j[t];switch(t){case"keys":case g:case b:return function(){return new n(this,t)}}return function(){return new n(this)}}i(n,e,l);var O,k,C,A=e+" Iterator",S=!1,j=t.prototype,E=j[m]||j["@@iterator"]||h&&j[h],$=!y&&E||x(h),T="Array"==e&&j.entries||E;if(T&&(O=a(T.call(new t)),v!==Object.prototype&&O.next&&(p||a(O)===v||(s?s(O,v):"function"!=typeof O[m]&&u(O,m,r)),c(O,A,!0,!0),p&&(d[A]=r))),h==g&&E&&E.name!==g&&(S=!0,$=function(){return E.call(this)}),p&&!w||j[m]===$||u(j,m,$),d[e]=$,h)if(k={values:x(g),keys:_?$:x("keys"),entries:x(b)},w)for(C in k)!y&&!S&&C in j||f(j,C,k[C]);else o({target:e,proto:!0,forced:y||S},k);return k}},"7f9a":function(t,e,n){var r=n("da84"),o=n("9e81"),i=r.WeakMap;t.exports="function"==typeof i&&/native code/.test(o.call(i))},"825a":function(t,e,n){var r=n("861d");t.exports=function(t){if(!r(t))throw TypeError(String(t)+" is not an object");return t}},"83ab":function(t,e,n){var r=n("d039");t.exports=!r((function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a}))},"861d":function(t,e){t.exports=function(t){return"object"==typeof t?null!==t:"function"==typeof t}},"8aa5":function(t,e,n){"use strict";var r=n("6547").charAt;t.exports=function(t,e,n){return e+(n?r(t,e).length:1)}},"8c4f":function(t,e,n){"use strict";function r(t){return-1=t.length?n():t[o]?e(t[o],(function(){r(o+1)})):r(o+1)};r(0)}function bt(t){return function(e,n,o){var i=!1,a=0,s=null;_t(t,(function(t,e,n,c){if("function"==typeof t&&void 0===t.cid){i=!0,a++;var u,f=Ot((function(e){var r;((r=e).__esModule||xt&&"Module"===r[Symbol.toStringTag])&&(e=e.default),t.resolved="function"==typeof e?e:H.extend(e),n.components[c]=e,--a<=0&&o()})),l=Ot((function(t){var e="Failed to resolve async component "+c+": "+t;s||(s=r(t)?t:new Error(e),o(s))}));try{u=t(f,l)}catch(e){l(e)}if(u)if("function"==typeof u.then)u.then(f,l);else{var p=u.component;p&&"function"==typeof p.then&&p.then(f,l)}}})),i||o()}}function _t(t,e){return wt(t.map((function(t){return Object.keys(t.components).map((function(n){return e(t.components[n],t.instances[n],t,n)}))})))}function wt(t){return Array.prototype.concat.apply([],t)}var xt="function"==typeof Symbol&&"symbol"==typeof Symbol.toStringTag;function Ot(t){var e=!1;return function(){for(var n=[],r=arguments.length;r--;)n[r]=arguments[r];if(!e)return e=!0,t.apply(this,n)}}var kt=function(t){function e(e){t.call(this),this.name=this._name="NavigationDuplicated",this.message='Navigating to current location ("'+e.fullPath+'") is not allowed',Object.defineProperty(this,"stack",{value:(new t).stack,writable:!0,configurable:!0})}return t&&(e.__proto__=t),(e.prototype=Object.create(t&&t.prototype)).constructor=e}(Error);function Ct(t,e){this.router=t,this.base=function(t){if(!t)if(W){var e=document.querySelector("base");t=(t=e&&e.getAttribute("href")||"/").replace(/^https?:\/\/[^\/]+/,"")}else t="/";return"/"!==t.charAt(0)&&(t="/"+t),t.replace(/\/$/,"")}(e),this.current=m,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[]}function At(t,e,n,r){var o=_t(t,(function(t,r,o,i){var a=function(t,e){return"function"!=typeof t&&(t=H.extend(t)),t.options[e]}(t,e);if(a)return Array.isArray(a)?a.map((function(t){return n(t,r,o,i)})):n(a,r,o,i)}));return wt(r?o.reverse():o)}function St(t,e){if(e)return function(){return t.apply(e,arguments)}}kt._name="NavigationDuplicated",Ct.prototype.listen=function(t){this.cb=t},Ct.prototype.onReady=function(t,e){this.ready?t():(this.readyCbs.push(t),e&&this.readyErrorCbs.push(e))},Ct.prototype.onError=function(t){this.errorCbs.push(t)},Ct.prototype.transitionTo=function(t,e,n){var r=this,o=this.router.match(t,this.current);this.confirmTransition(o,(function(){r.updateRoute(o),e&&e(o),r.ensureURL(),r.ready||(r.ready=!0,r.readyCbs.forEach((function(t){t(o)})))}),(function(t){n&&n(t),t&&!r.ready&&(r.ready=!0,r.readyErrorCbs.forEach((function(e){e(t)})))}))},Ct.prototype.confirmTransition=function(t,e,n){function i(t){!o(kt,t)&&r(t)&&a.errorCbs.length&&a.errorCbs.forEach((function(e){e(t)})),n&&n(t)}var a=this,s=this.current;if(b(t,s)&&t.matched.length===s.matched.length)return this.ensureURL(),i(new kt(t));var c=function(t,e){var n,r=Math.max(t.length,e.length);for(n=0;n=this.stack.length)){var r=this.stack[n];this.confirmTransition(r,(function(){e.index=n,e.updateRoute(r)}),(function(t){o(kt,t)&&(e.index=n)}))}},e.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:"/"},e.prototype.ensureURL=function(){},e}(Ct),Mt={currentRoute:{configurable:!0}};function Ft(t,e){return t.push(e),function(){var n=t.indexOf(e);-10&&void 0!==arguments[0]?arguments[0]:{};this.options=Object.assign(this.options,t)}},{key:"set",value:function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,r=JSON.stringify({value:e,expire:null!==n?(new Date).getTime()+n:null});this.storage.setItem(this.options.namespace+t,r)}},{key:"get",value:function(e){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,r=this.storage.getItem(this.options.namespace+e);if(null!==r)try{var o=JSON.parse(r);if(null===o.expire)return o.value;if(o.expire>=(new Date).getTime())return o.value;this.remove(e)}catch(t){return n}return n}},{key:"key",value:function(t){return this.storage.key(t)}},{key:"remove",value:function(t){return this.storage.removeItem(this.options.namespace+t)}},{key:"clear",value:function(){if(0!==this.length){for(var t=[],e=0;e1&&void 0!==arguments[1]?arguments[1]:{},n=Object.assign({},e,{storage:e.storage||"local",name:e.name||"ls"});if(n.storage&&-1===["memory","local","session"].indexOf(n.storage))throw new Error('Vue-ls: Storage "'.concat(n.storage,'" is not supported'));var r=null;switch(n.storage){case"local":r="localStorage"in f?f.localStorage:null;break;case"session":r="sessionStorage"in f?f.sessionStorage:null;break;case"memory":r=a;break}r||(r=a,console.error('Vue-ls: Storage "'.concat(n.storage,'" is not supported your system, use memory storage')));var o=new u(r);o.setOptions(Object.assign(o.options,{namespace:""},n||{})),t[n.name]=o,Object.defineProperty(t.prototype,"$".concat(n.name),{get:function(){return o}})}};return f.VueStorage=l}()}).call(this,n("c8ba"))},c345:function(t,e,n){"use strict";var r=n("c532"),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];t.exports=function(t){var e,n,i,a={};return t&&r.forEach(t.split("\n"),(function(t){if(i=t.indexOf(":"),e=r.trim(t.substr(0,i)).toLowerCase(),n=r.trim(t.substr(i+1)),e){if(a[e]&&0<=o.indexOf(e))return;a[e]="set-cookie"===e?(a[e]?a[e]:[]).concat([n]):a[e]?a[e]+", "+n:n}})),a}},c401:function(t,e,n){"use strict";var r=n("c532");t.exports=function(t,e,n){return r.forEach(n,(function(n){t=n(t,e)})),t}},c430:function(t,e){t.exports=!1},c532:function(t,e,n){"use strict";var r=n("1d2b"),o=n("c7ce"),i=Object.prototype.toString;function a(t){return"[object Array]"===i.call(t)}function s(t){return null!==t&&"object"==typeof t}function c(t){return"[object Function]"===i.call(t)}function u(t,e){if(null!=t)if("object"!=typeof t&&(t=[t]),a(t))for(var n=0,r=t.length;nc;)r(s,n=e[c++])&&(~i(u,n)||u.push(n));return u}},cc12:function(t,e,n){var r=n("da84"),o=n("861d"),i=r.document,a=o(i)&&o(i.createElement);t.exports=function(t){return a?i.createElement(t):{}}},cca6:function(t,e,n){var r=n("23e7"),o=n("60da");r({target:"Object",stat:!0,forced:Object.assign!==o},{assign:o})},cdf9:function(t,e,n){var r=n("825a"),o=n("861d"),i=n("f069");t.exports=function(t,e){if(r(t),o(e)&&e.constructor===t)return e;var n=i.f(t);return(0,n.resolve)(e),n.promise}},ce4e:function(t,e,n){var r=n("da84"),o=n("9112");t.exports=function(e,n){try{o(r,e,n)}catch(t){r[e]=n}return n}},cee4:function(t,e,n){"use strict";var r=n("c532"),o=n("1d2b"),i=n("0a06"),a=n("2444");function s(t){var e=new i(t),n=o(i.prototype.request,e);return r.extend(n,i.prototype,e),r.extend(n,e),n}var c=s(a);c.Axios=i,c.create=function(t){return s(r.merge(a,t))},c.Cancel=n("7a77"),c.CancelToken=n("8df4"),c.isCancel=n("2e67"),c.all=function(t){return Promise.all(t)},c.spread=n("0df6"),t.exports=c,t.exports.default=c},d012:function(t,e){t.exports={}},d039:function(t,e){t.exports=function(t){try{return!!t()}catch(t){return!0}}},d066:function(t,e,n){function r(t){return"function"==typeof t?t:void 0}var o=n("428f"),i=n("da84");t.exports=function(t,e){return arguments.length<2?r(o[t])||r(i[t]):o[t]&&o[t][e]||i[t]&&i[t][e]}},d1e7:function(t,e,n){"use strict";var r={}.propertyIsEnumerable,o=Object.getOwnPropertyDescriptor,i=o&&!r.call({1:2},1);e.f=i?function(t){var e=o(this,t);return!!e&&e.enumerable}:r},d233:function(t,e,n){"use strict";function r(t,e){for(var n=e&&e.plainObjects?Object.create(null):{},r=0;r>6]+a[128|63&s]:s<55296||57344<=s?o+=a[224|s>>12]+a[128|s>>6&63]+a[128|63&s]:(i+=1,s=65536+((1023&s)<<10|1023&r.charCodeAt(i)),o+=a[240|s>>18]+a[128|s>>12&63]+a[128|s>>6&63]+a[128|63&s])}return o},isBuffer:function(t){return!(!t||"object"!=typeof t)&&!!(t.constructor&&t.constructor.isBuffer&&t.constructor.isBuffer(t))},isRegExp:function(t){return"[object RegExp]"===Object.prototype.toString.call(t)},merge:function t(e,n,a){if(!n)return e;if("object"!=typeof n){if(i(e))e.push(n);else{if(!e||"object"!=typeof e)return[e,n];(a&&(a.plainObjects||a.allowPrototypes)||!o.call(Object.prototype,n))&&(e[n]=!0)}return e}if(!e||"object"!=typeof e)return[e].concat(n);var s=e;return i(e)&&!i(n)&&(s=r(e,a)),i(e)&&i(n)?(n.forEach((function(n,r){if(o.call(e,r)){var i=e[r];i&&"object"==typeof i&&n&&"object"==typeof n?e[r]=t(i,n,a):e.push(n)}else e[r]=n})),e):Object.keys(n).reduce((function(e,r){var i=n[r];return o.call(e,r)?e[r]=t(e[r],i,a):e[r]=i,e}),s)}}},d2bb:function(t,e,n){var r=n("825a"),o=n("3bbe");t.exports=Object.setPrototypeOf||("__proto__"in{}?function(){var t,e=!1,n={};try{(t=Object.getOwnPropertyDescriptor(Object.prototype,"__proto__").set).call(n,[]),e=n instanceof Array}catch(n){}return function(n,i){return r(n),o(i),e?t.call(n,i):n.__proto__=i,n}}():void 0)},d3b7:function(t,e,n){var r=n("6eeb"),o=n("b041"),i=Object.prototype;o!==i.toString&&r(i,"toString",o,{unsafe:!0})},d44e:function(t,e,n){var r=n("9bf2").f,o=n("5135"),i=n("b622")("toStringTag");t.exports=function(t,e,n){t&&!o(t=n?t:t.prototype,i)&&r(t,i,{configurable:!0,value:e})}},d784:function(t,e,n){"use strict";var r=n("9112"),o=n("6eeb"),i=n("d039"),a=n("b622"),s=n("9263"),c=a("species"),u=!i((function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")})),f=!i((function(){var t=/(?:)/,e=t.exec;t.exec=function(){return e.apply(this,arguments)};var n="ab".split(t);return 2!==n.length||"a"!==n[0]||"b"!==n[1]}));t.exports=function(t,e,n,l){var p=a(t),d=!i((function(){var e={};return e[p]=function(){return 7},7!=""[t](e)})),h=d&&!i((function(){var e=!1,n=/a/;return"split"===t&&((n={constructor:{}}).constructor[c]=function(){return n},n.flags="",n[p]=/./[p]),n.exec=function(){return e=!0,null},n[p](""),!e}));if(!d||!h||"replace"===t&&!u||"split"===t&&!f){var v=/./[p],y=n(p,""[t],(function(t,e,n,r,o){return e.exec===s?d&&!o?{done:!0,value:v.call(e,n,r)}:{done:!0,value:t.call(n,e,r)}:{done:!1}})),m=y[0],g=y[1];o(String.prototype,t,m),o(RegExp.prototype,p,2==e?function(t,e){return g.call(t,this,e)}:function(t){return g.call(t,this)}),l&&r(RegExp.prototype[p],"sham",!0)}}},d925:function(t,e,n){"use strict";t.exports=function(t){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(t)}},da84:function(t,e,n){(function(e){function n(t){return t&&t.Math==Math&&t}t.exports=n("object"==typeof globalThis&&globalThis)||n("object"==typeof window&&window)||n("object"==typeof self&&self)||n("object"==typeof e&&e)||Function("return this")()}).call(this,n("c8ba"))},ddb0:function(t,e,n){var r=n("da84"),o=n("fdbc"),i=n("e260"),a=n("9112"),s=n("b622"),c=s("iterator"),u=s("toStringTag"),f=i.values;for(var l in o){var p=r[l],d=p&&p.prototype;if(d){if(d[c]!==f)try{a(d,c,f)}catch(t){d[c]=f}if(d[u]||a(d,u,l),o[l])for(var h in i)if(d[h]!==i[h])try{a(d,h,i[h])}catch(t){d[h]=i[h]}}}},df75:function(t,e,n){var r=n("ca84"),o=n("7839");t.exports=Object.keys||function(t){return r(t,o)}},df7c:function(t,e,n){(function(t){function n(t,e){for(var n=0,r=t.length-1;0<=r;r--){var o=t[r];"."===o?t.splice(r,1):".."===o?(t.splice(r,1),n++):n&&(t.splice(r,1),n--)}if(e)for(;n--;)t.unshift("..");return t}function r(t,e){if(t.filter)return t.filter(e);for(var n=[],r=0;r=e.length?{value:t.target=void 0,done:!0}:"keys"==n?{value:r,done:!1}:"values"==n?{value:e[r],done:!1}:{value:[r,e[r]],done:!1}}),"values"),i.Arguments=i.Array,o("keys"),o("values"),o("entries")},e2cc:function(t,e,n){var r=n("6eeb");t.exports=function(t,e,n){for(var o in e)r(t,o,e[o],n);return t}},e667:function(t,e){t.exports=function(t){try{return{error:!1,value:t()}}catch(t){return{error:!0,value:t}}}},e683:function(t,e,n){"use strict";t.exports=function(t,e){return e?t.replace(/\/+$/,"")+"/"+e.replace(/^\/+/,""):t}},e6cf:function(t,e,n){"use strict";function r(t){var e;return!(!w(t)||"function"!=typeof(e=t.then))&&e}function o(t,e,n){if(!e.notified){e.notified=!0;var o=e.reactions;E((function(){for(var i=e.value,a=1==e.state,s=0;o.length>s;){var c,u,f,l=o[s++],p=a?l.ok:l.fail,d=l.resolve,h=l.reject,v=l.domain;try{p?(a||(2===e.rejection&&rt(t,e),e.rejection=1),!0===p?c=i:(v&&v.enter(),c=p(i),v&&(v.exit(),f=!0)),c===l.promise?h(q("Promise-chain cycle")):(u=r(c))?u.call(c,d,h):d(c)):h(i)}catch(i){v&&!f&&v.exit(),h(i)}}e.reactions=[],e.notified=!1,n&&!e.rejection&&et(t,e)}))}}function i(t,e,n){var r,o;J?((r=z.createEvent("Event")).promise=e,r.reason=n,r.initEvent(t,!1,!0),h.dispatchEvent(r)):r={promise:e,reason:n},(o=h["on"+t])?o(r):t===Y&&T("Unhandled promise rejection",n)}function a(t,e,n,r){return function(o){t(e,n,o,r)}}function s(t,e,n,r){e.done||(e.done=!0,r&&(e=r),e.value=n,e.state=2,o(t,e,!0))}var c,u,f,l,p=n("23e7"),d=n("c430"),h=n("da84"),v=n("d066"),y=n("fea9"),m=n("6eeb"),g=n("e2cc"),b=n("d44e"),_=n("2626"),w=n("861d"),x=n("1c0b"),O=n("19aa"),k=n("c6b6"),C=n("2266"),A=n("1c7e"),S=n("4840"),j=n("2cf4").set,E=n("b575"),$=n("cdf9"),T=n("44de"),P=n("f069"),L=n("e667"),R=n("69f3"),I=n("94ca"),N=n("b622"),D=n("60ae"),M=N("species"),F="Promise",U=R.get,B=R.set,V=R.getterFor(F),H=y,q=h.TypeError,z=h.document,G=h.process,K=v("fetch"),W=P.f,X=W,Q="process"==k(G),J=!!(z&&z.createEvent&&h.dispatchEvent),Y="unhandledrejection",Z=I(F,(function(){if(66===D)return!0;if(!Q&&"function"!=typeof PromiseRejectionEvent)return!0;if(d&&!H.prototype.finally)return!0;if(51<=D&&/native code/.test(H))return!1;function t(t){t((function(){}),(function(){}))}var e=H.resolve(1);return(e.constructor={})[M]=t,!(e.then((function(){}))instanceof t)})),tt=Z||!A((function(t){H.all(t).catch((function(){}))})),et=function(t,e){j.call(h,(function(){var n,r=e.value;if(nt(e)&&(n=L((function(){Q?G.emit("unhandledRejection",r,t):i(Y,t,r)})),e.rejection=Q||nt(e)?2:1,n.error))throw n.value}))},nt=function(t){return 1!==t.rejection&&!t.parent},rt=function(t,e){j.call(h,(function(){Q?G.emit("rejectionHandled",t):i("rejectionhandled",t,e.value)}))},ot=function(t,e,n,i){if(!e.done){e.done=!0,i&&(e=i);try{if(t===n)throw q("Promise can't be resolved itself");var c=r(n);c?E((function(){var r={done:!1};try{c.call(n,a(ot,t,r,e),a(s,t,r,e))}catch(n){s(t,r,n,e)}})):(e.value=n,e.state=1,o(t,e,!1))}catch(n){s(t,{done:!1},n,e)}}};Z&&(H=function(t){O(this,H,F),x(t),c.call(this);var e=U(this);try{t(a(ot,this,e),a(s,this,e))}catch(t){s(this,e,t)}},(c=function(){B(this,{type:F,done:!1,notified:!1,parent:!1,reactions:[],rejection:!1,state:0,value:void 0})}).prototype=g(H.prototype,{then:function(t,e){var n=V(this),r=W(S(this,H));return r.ok="function"!=typeof t||t,r.fail="function"==typeof e&&e,r.domain=Q?G.domain:void 0,n.parent=!0,n.reactions.push(r),0!=n.state&&o(this,n,!1),r.promise},catch:function(t){return this.then(void 0,t)}}),u=function(){var t=new c,e=U(t);this.promise=t,this.resolve=a(ot,t,e),this.reject=a(s,t,e)},P.f=W=function(t){return t===H||t===f?new u:X(t)},d||"function"!=typeof y||(l=y.prototype.then,m(y.prototype,"then",(function(t,e){var n=this;return new H((function(t,e){l.call(n,t,e)})).then(t,e)}),{unsafe:!0}),"function"==typeof K&&p({global:!0,enumerable:!0,forced:!0},{fetch:function(t){return $(H,K.apply(h,arguments))}}))),p({global:!0,wrap:!0,forced:Z},{Promise:H}),b(H,F,!1,!0),_(F),f=v(F),p({target:F,stat:!0,forced:Z},{reject:function(t){var e=W(this);return e.reject.call(void 0,t),e.promise}}),p({target:F,stat:!0,forced:d||Z},{resolve:function(t){return $(d&&this===f?H:this,t)}}),p({target:F,stat:!0,forced:tt},{all:function(t){var e=this,n=W(e),r=n.resolve,o=n.reject,i=L((function(){var n=x(e.resolve),i=[],a=0,s=1;C(t,(function(t){var c=a++,u=!1;i.push(void 0),s++,n.call(e,t).then((function(t){u||(u=!0,i[c]=t,--s||r(i))}),o)})),--s||r(i)}));return i.error&&o(i.value),n.promise},race:function(t){var e=this,n=W(e),r=n.reject,o=L((function(){var o=x(e.resolve);C(t,(function(t){o.call(e,t).then(n.resolve,r)}))}));return o.error&&r(o.value),n.promise}})},e893:function(t,e,n){var r=n("5135"),o=n("56ef"),i=n("06cf"),a=n("9bf2");t.exports=function(t,e){for(var n=o(e),s=a.f,c=i.f,u=0;u 2 | 3 | 4 | 5 | 6 | 7 | 8 | webapp 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 14 | 24 | -------------------------------------------------------------------------------- /src/api/articles.js: -------------------------------------------------------------------------------- 1 | import fetch from './fetch'; 2 | 3 | export default { 4 | // 获取文章列表 5 | list(params) { 6 | return fetch.get('/article', params); 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /src/api/fetch.js: -------------------------------------------------------------------------------- 1 | import qs from 'qs'; 2 | import Vue from 'vue'; 3 | import utils from '../lib/utils'; 4 | 5 | utils.ajax.defaults.headers.common = { 6 | 'X-Requested-With': 'XMLHttpRequest', 7 | }; 8 | 9 | utils.ajax.interceptors.request.use((cnf) => { 10 | const config = cnf; 11 | // 获取token 12 | config.headers.common.Authorization = `Bearer ${Vue.ls.get('token')}`; 13 | return config; 14 | }, (error) => Promise.reject(error)); 15 | 16 | utils.ajax.interceptors.response.use((response) => { 17 | // 如果后端有新的token则重新缓存 18 | const newToken = response.headers['new-token']; 19 | if (newToken) { 20 | Vue.ls.set('token', newToken); 21 | } 22 | 23 | return response; 24 | }, (error) => { 25 | const res = error.response; 26 | let extraErrors = []; 27 | 28 | if (res && res.config && res.config.extraErrors) { 29 | extraErrors = res.config.extraErrors; 30 | } else if (res && res.config && res.config.params && res.config.params.extraErrors) { 31 | extraErrors = res.config.params.extraErrors; 32 | } 33 | 34 | const { code } = res ? res.data : {}; 35 | 36 | // 错误代码如果不在'需要单独处理的错误代码数组'内的话 37 | if (extraErrors.indexOf(code) === -1) { 38 | switch (code) { 39 | case 401: 40 | Vue.ls.set('token', null); 41 | break; 42 | 43 | case 404: 44 | console.log('查询信息不存在'); 45 | break; 46 | 47 | case 413: 48 | console.log(res.data.message); 49 | break; 50 | 51 | case 418: 52 | console.log(res.data.message); 53 | break; 54 | 55 | case 500: 56 | console.log(res.data.message || '服务器开了一会小差~', 'error'); 57 | break; 58 | 59 | default: 60 | console.log(res.data.message); 61 | } 62 | } 63 | 64 | return Promise.reject(error); 65 | }); 66 | 67 | export default { 68 | post(url, params = {}) { 69 | const { isLoading = true, extraErrors = [] } = params; 70 | 71 | return utils.ajax({ 72 | method: 'post', 73 | url, 74 | data: qs.stringify(params), 75 | timeout: 30000, 76 | isLoading, 77 | extraErrors, 78 | headers: { 79 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 80 | }, 81 | }); 82 | }, 83 | 84 | get(url, params = {}) { 85 | const { isLoading = true, extraErrors = [] } = params; 86 | return utils.ajax({ 87 | method: 'get', 88 | url, 89 | params, 90 | paramsSerializer: (query) => qs.stringify(query), 91 | isLoading, 92 | extraErrors, 93 | }); 94 | }, 95 | 96 | delete(url, params = {}) { 97 | const { isLoading = true, extraErrors = [] } = params; 98 | return utils.ajax({ 99 | method: 'delete', 100 | url, 101 | params, 102 | isLoading, 103 | extraErrors, 104 | }); 105 | }, 106 | put(url, params = {}) { 107 | const { isLoading = true, extraErrors = [] } = params; 108 | return utils.ajax({ 109 | method: 'put', 110 | url, 111 | data: qs.stringify(params), 112 | isLoading, 113 | extraErrors, 114 | timeout: 30000, 115 | headers: { 116 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 117 | }, 118 | }); 119 | }, 120 | }; 121 | -------------------------------------------------------------------------------- /src/assets/css/common.less: -------------------------------------------------------------------------------- 1 | /* 公共样式 */ 2 | /* 默认颜色 */ 3 | @defaultColor: #2d8cf0; 4 | -------------------------------------------------------------------------------- /src/lib/utils.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const util = {}; 4 | 5 | const ajaxUrl = process.env.VUE_APP_BASE_API; 6 | 7 | util.ajax = axios.create({ 8 | baseURL: ajaxUrl, 9 | timeout: 30000, 10 | }); 11 | 12 | util.api = ajaxUrl; 13 | util.oauthUrl = ajaxUrl; 14 | 15 | export default util; 16 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Storage from 'vue-ls'; 3 | import App from './App.vue'; 4 | import router from './router'; 5 | import store from './store'; 6 | 7 | Vue.config.productionTip = false; 8 | 9 | Vue.use(Storage, { 10 | namespace: 'project_name_storage__', 11 | name: 'ls', 12 | storage: 'local', 13 | }); 14 | 15 | new Vue({ 16 | router, 17 | store, 18 | render: (h) => h(App), 19 | }).$mount('#app'); 20 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import VueRouter from 'vue-router'; 3 | 4 | Vue.use(VueRouter); 5 | 6 | const routes = [ 7 | { 8 | path: '/', 9 | name: 'index', 10 | component: () => import(/* webpackChunkName: "home" */ '../views/index.vue'), 11 | }, 12 | ]; 13 | 14 | const router = new VueRouter({ 15 | mode: 'history', 16 | // 如果路由存在二级目录,需要添加 base 属性,否则默认为 "/" 17 | // 默认路由模式是 hash 模式,会携带 # 标记,与真实 url 不符,可以改为 history 模式 18 | base: process.env.BASE_URL, 19 | // 页面组件没有进行按需加载,可以使用 require.ensure() 来进行优化 20 | routes, 21 | }); 22 | 23 | export default router; 24 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | 4 | Vue.use(Vuex); 5 | 6 | const store = new Vuex.Store({ 7 | mutations: {}, 8 | actions: {}, 9 | }); 10 | 11 | /** 12 | * 查询模块 13 | */ 14 | const modulesContext = require.context('@/store/modules', true, /\.js$/); 15 | 16 | /** 17 | * 创建模块函数 18 | * @param fileArr 19 | */ 20 | function createStoreModules(fileArr) { 21 | fileArr.keys().forEach((modules) => { 22 | store.registerModule(modules.replace(/(^\.\/)|(\.js$)/g, ''), fileArr(modules).default); 23 | }); 24 | } 25 | 26 | /** 27 | * 创建模块 28 | */ 29 | createStoreModules(modulesContext); 30 | 31 | export default store; 32 | -------------------------------------------------------------------------------- /src/store/modules/articles.js: -------------------------------------------------------------------------------- 1 | import articles from '@api/articles'; 2 | 3 | const state = { 4 | list: [], 5 | }; 6 | 7 | const mutations = { 8 | SET_LIST(states, data) { 9 | const s = states; 10 | s.list = data; 11 | }, 12 | }; 13 | 14 | const actions = { 15 | /** 16 | * 获取文章列表 17 | */ 18 | async getArticleList({ commit }, params) { 19 | const r = await articles.list(params); 20 | commit('SET_LIST', r); 21 | return r; 22 | }, 23 | }; 24 | 25 | export default { 26 | namespaced: true, 27 | state, 28 | mutations, 29 | actions, 30 | }; 31 | -------------------------------------------------------------------------------- /src/views/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 22 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const merge = require('webpack-merge'); 3 | const CompressionWebpackPlugin = require('compression-webpack-plugin'); 4 | const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); 5 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); 6 | 7 | const isProduction = process.env.NODE_ENV === 'production'; 8 | 9 | const resolve = (dir) => path.join(__dirname, dir); 10 | 11 | module.exports = { 12 | publicPath: process.env.PUBLIC_PATH, 13 | // 构建好的文件打包输出到 output 文件夹下 14 | outputDir: 'dist', 15 | assetsDir: 'static', 16 | // 可以对本地服务器进行相应配置 17 | devServer: { 18 | // string | Object 代理设置 19 | proxy: { 20 | '/api': { 21 | target: 'http://mall.test/api', 22 | pathRewrite: { '^/api': '' }, 23 | }, 24 | }, 25 | }, 26 | // 链式修改 27 | chainWebpack: (config) => { 28 | // 使用 alias 简化路径 29 | config.resolve.alias 30 | .set('@', resolve('src')) 31 | .set('@lib', resolve('src/lib')) 32 | .set('@components', resolve('src/components')) 33 | .set('@img', resolve('src/assets/images')) 34 | .set('@api', resolve('src/api')); 35 | // 图片下的 url-loader 值,将其 limit 限制改为 5M 36 | config.module 37 | .rule('images') 38 | .use('url-loader') 39 | .tap((options) => merge(options, { 40 | limit: 5 * 1024, 41 | })); 42 | config.module 43 | .rule('compile') 44 | .test(/\.js$/) 45 | .include 46 | .add(resolve('src')) 47 | .end() 48 | .use('babel') 49 | .loader('babel-loader?cacheDirectory=true') 50 | .options({ 51 | presets: [ 52 | '@vue/cli-plugin-babel/preset', 53 | ], 54 | // 可以再这里按需引入组件库,如 vant 组件 55 | // plugins: [ 56 | // ['import', { 57 | // libraryName: 'vant', 58 | // libraryDirectory: 'es', 59 | // style: true 60 | // }, 'vant'] 61 | // ] 62 | }); 63 | config.optimization.splitChunks({ 64 | chunks: 'all', 65 | cacheGroups: { 66 | // 第三方模块 67 | vendor: { 68 | name: 'vendor', // chunk 名称 69 | priority: 1, // 权限更高,有限抽离 70 | test: /node_modules/, 71 | minSize: 0, // 大小限制 72 | minChunks: 1, // 最少复用过几次 73 | }, 74 | // 公共模块 75 | common: { 76 | name: 'common', 77 | priority: 0, 78 | minSize: 0, // 大小限制 79 | minChunks: 2, // 最少复用过几次 80 | }, 81 | }, 82 | }); 83 | }, 84 | // 和 chainWebpack 一样,但configureWebpack 更倾向于整体替换和修改 85 | configureWebpack: (config) => { 86 | if (isProduction) { 87 | // 上线压缩去除console等信息 88 | config.plugins.push( 89 | new UglifyJsPlugin({ 90 | uglifyOptions: { 91 | compress: { 92 | // 删除所有的console语句 93 | drop_console: true, 94 | // 把使用多次的静态值自动定义为变量 95 | reduce_vars: true, 96 | drop_debugger: false, 97 | // 移除console 98 | pure_funcs: ['console.log'], 99 | }, 100 | output: { 101 | // 使输出的代码尽可能紧凑 102 | beautify: false, 103 | }, 104 | }, 105 | sourceMap: false, 106 | // 允许并发 107 | parallel: true, 108 | // 开启缓存 109 | cache: true, 110 | }), 111 | ); 112 | // 开启gzip,降低服务器压缩对CPU资源的占用,服务器也要相应开启gzip 113 | config.plugins.push( 114 | new CompressionWebpackPlugin({ 115 | // 目标文件名称。[path] 被替换为原始文件的路径和 [query] 查询 116 | filename: '[path].gz[query]', 117 | // 使用 gzip 压缩 118 | algorithm: 'gzip', 119 | // 处理与此正则相匹配的所有文件 120 | test: /\.(js|css|json|txt|html|ico|svg|tiff)(\?.*)?$/i, 121 | // 只处理大于此大小的文件 122 | threshold: 10240, 123 | // 最小压缩比达到 0.8 时才会被压缩 124 | minRatio: 0.8, 125 | }), 126 | ); 127 | if (process.env.npm_config_report) { 128 | // 打包后模块大小分析 129 | config.plugins.push( 130 | new BundleAnalyzerPlugin(), 131 | ); 132 | } 133 | } 134 | }, 135 | // 生产环境下为了快速定位错误信息 136 | productionSourceMap: false, 137 | // 引入公共样式文件 138 | pluginOptions: { 139 | 'style-resources-loader': { 140 | preProcessor: 'less', 141 | patterns: [ 142 | resolve('src/assets/css/common.less'), 143 | ], 144 | }, 145 | }, 146 | }; 147 | --------------------------------------------------------------------------------