写在前面
上一篇完成了后台管理系统的基础搭建和一些具体功能,现在就直接开始完成页面功能的具体实现和项目优化。
页面基础功能实现
1.登录页
分为 用户名+密码登录 和 手机号+验证码登录两种,验证码mock
随机六位数返回,登录成功会返回一个token
,请求接口时携带token
,图片是从千图网上扣的(白嫖一波)。
2.layout布局
layout 布局按照之前设想的那样,左边导航菜单,上面面包屑+用户信息,再往下就是标签页。
导航栏 看了vue-element-admin
的代码,发现还可以导航栏递归生成,顿时眼前一亮,然后照着写了一遍,发现有很多地方可能用不到,比如外链,再比如 alwaysShow
等,就简化了下,逻辑更加简单。
标签页 模块主要的难点是(自己手写的,没用tab组件):
1.标签页数量超出可显示区域后显示 左右移动按钮,点击移动按钮移动标签页;
2.点击新页面的时候,当前位置挪动到最后面,显示当前页面的标签页
然后再添加上下布局,得更改下样式和布局方式即可。
3.权限验证
由于之前做过的系统都是后端传路由过来,所以这是第一次尝试使用前端自己控制路由,不过还好vue-element-admin 里面权限控制已经做得非常完美了;画了个流程图,看看就好...
4.echarts图
这里添加了各种的图大部分常用的各种形态,echarts图,柱状图、折线图、饼图、关系图、地图、词云图等。如地图:分布图、散点图、航线图、热力图等,均可实现 点击下钻
5.系统管理
这个按照之前设想的那样,做一个用户管理和一个角色管理,系统管理只有管理员权限才能看到,可以修改用户所看的菜单,以一个树形结构的方式展示, 但是mock不是很好模拟这些数据,所以就只做了一个假的。
6.其他
之后还添加一些我觉得比较有意思的组件,如:头像上传裁剪 、webSocket、上传Excel等有兴趣可以看看。
优化
虽然页面已经写完了,但是发现还是存在很多问题,如首页加载时间太长,打包文件太大等问题,然后就开始进一轮优化。
1. 关闭生产环境 sourceMap
关闭方法:vue.config.js
设置 productionSourceMap
为 false 即可
productionSourceMap: false,
2. 关闭预加载和预编译
vue会预加载后面的路由导致首屏加载时间较长,所以这里去掉。
关闭方法:vue.config.js
设置 chainWebpack
:
config.plugins.delete('prefetch');
config.plugins.delete('preload');
3. g-zip压缩
使用 compression-webpack-plugin
插件 进行g-zip压缩,需要nginx
配合
const CompressionWebpackPlugin = require('compression-webpack-plugin');
config.plugin('CompressionWebpackPlugin').use(CompressionWebpackPlugin, [{
filename: '[path].gz[query]',
algorithm: 'gzip',
test: /\.js$|\.css/, //匹配文件名
threshold: 10240, //对超过10k的数据压缩
minRatio: 0.8
} ]);
4. 清除生产环境console 和debugger
vu-cli4里面默认使用的是 terser,我们只需修改这个的属性即可。
config.optimization.minimizer('terser').tap(args => {
args[0].terserOptions.compress.warnings = false;
args[0].terserOptions.compress.drop_console = true
args[0].terserOptions.compress.drop_debugger = true;
args[0].terserOptions.compress.pure_funcs = ['console.*'];
return args;
});
5. splitChunks 优化
const isProd = process.env.NODE_ENV === 'production';
config.when(isProd, config => {
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial'
},
commons: {
name: 'chunk-commons',
test: /[\\/]src[\\/]js[\\/]/,
minChunks: 2, // minimum common number
priority: 5,
reuseExistingChunk: true
}
}
});
6.异步加载cdn
之前使用高德Amap获取行政区边界的时候,都是直接在public
里面的index.html
直接引入,但是这样会很不好,登录页的时候就会加载AMap,有时甚至需要2-3s,所以这里我们把它抽出来动态创建 script
加载。
这里我封装了下,直接使用即可。
const remoteLoad = url => {
return new Promise((resolve, reject) => {
const existingScript = document.getElementById(url);
//如果script不存在
if (!existingScript) {
const script = document.createElement('script');
script.id = url;
script.src = url;
script.async = true;
document.body.appendChild(script);
script.onload = function() {
setTimeout(() => {
this.onerror = this.onload = null;
resolve();
}, 500);
};
script.onerror = function() {
this.onerror = this.onload = null;
reject('加载失败' + url);
};
}
else {
setTimeout(() => {
resolve();
}, 500);
}
})
}
export default remoteLoad;
使用方法:
async init() {
try {
await remoteLoad(TinymceCDN);
if (window.tinymce) {
this.initTinymce();
} else {
this.$message.error('加载资源失败');
}
} catch (error) {
console.log(error);
this.$message.error(error);
} },
7. 封装获取geoJson的方法
以前写的时候没有对geoJson进行封装,每个页面如果需要获取地图geoJson都必须得重新获取,页面看起来很多重复代码,所以这次递归生成geoJson。
//先异步加载cdn再 调用方法
export function getGeoJson(adcode, childAdcode = '') {
return new Promise((resolve, reject) => {
if (window.AMap && window.AMapUI) {
insideFun(adcode, childAdcode);
} else {
remoteLoad(AMapCDN).then(() => {
if (window.AMap) {
remoteLoad(AMapUiCDN).then(() => {
if (window.AMapUI) {
insideFun(adcode, childAdcode);
} else {
console.error('AMapUI获取失败');
} });
} else {
console.error('AMap获取失败');
}
});
}
function insideFun(adcode, childAdcode) {
AMapUI.loadUI(['geo/DistrictExplorer'], DistrictExplorer => {
var districtExplorer = new DistrictExplorer();
districtExplorer.loadAreaNode(adcode, function(error, areaNode) {
if (error) {
console.error(error);
reject(error);
return;
}
let Json = areaNode.getSubFeatures();
if (Json.length === 0) {
let parent = areaNode._data.geoData.parent.properties.acroutes;
insideFun(parent[parent.length - 1], adcode);
return;
}
if (childAdcode) {
Json = Json.filter(item => {
return item.properties.adcode == childAdcode;
});
}
let mapJson = {
features: Json
};
resolve(mapJson);
});
});
}
});
}
8.CDN抽离
由于打包后的文件很大,所以在生产环境使用CDN,开发环境使用本地依赖,不过这样会导致线上的bug很难排查,慎用。
vue.config.jsconst
cdn = { css: [],
js: [VueCDN, AxiosCDN, VueRouterCDN, VuexCDN],
externals: {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
axios: 'axios'
}};
config.plugin('html').tap(args => {
args[0].cdn = cdn;
return args;
});
index.html
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) {
%> <link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" /> <%
} %>
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) {
%> <script type="text/javascript" src="<%= htmlWebpackPlugin.options.cdn.js[i] %>">
</script> <% } %>
最后
这样项目初始化基本上完成了,项目权限管理算是比较不错,其他基本功能也已经实现,还有些其他的功能感兴趣可以看看,本来之前还准备写几套自定义主题和国际化的,最后由于时间不够就放弃掉了。
项目地址:github.com/biubiubiu01…
其他文章
-
八.使用vue+antd搭建后台管理系统(实现篇)
-
七.使用vue+antd搭建后台管理系统(需求分析和搭建篇)
-
六.记一次Vue3.0尝鲜
-
五.记一次用webpack搭建vue项目
-
四.记一次用ts+vuecli4重构项目
-
二. Echarts+Amap实现点击下钻功能
-
一. vue keep-alive踩坑,删除keep-alive缓存
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!