10-讲讲vue-element-admin开源项目
项目配置
vue.config.js
在根目录下创建vue.config.js
,做一些相应的配置
//为什么是module.exports,因为此文件的执行环境是node环境module.exports={
publicPath:'/best-practice',//部署应用包时的基本URL
devServer:{
port:port
},
configureWebpack:{
// 想index.html注入标题
name:'vue最佳实践'
}
}
// index.html——使用webpack中配置的name
<title><%=webpackConfig.name %></title>
优雅使用icon——svg
icon发展史
- 最初的时候,大部分图标都是用
img
来实现的,如果一个项目中涉及到很多图标的时候,那一个页面中的请求资源中img
占了大部分; - 后面慢慢出现了雪碧图,将很多图标整合到一个图片上,css利用background-position 定位显示不同的 icon 图标,比起最开始
img
的方式,雪碧图已经有了很大的优化,但是依然有一个非常大的痛点,那就是每新增一个图标,就得去更新原先的雪碧图,这样做风险是很大的,不小心影响了原先图标的位置时,那页面中某些图标就加载不出来了,这种方式是极难维护的。 Font Awesome 出现,Font Awesome官网的体验不是太好,图标很小,找起来相当费力。
iconfont出现,阿里的开源库,找起来相当轻松,而且图标数量相当可人。
iconfont使用方式
先总体讲一下,在iconfont官网上将需要的图标打包下载下来后,如下:
将上面的这些包放到项目中,如果仅仅用unicode和font-class的方式来展示图标的话,那么仅仅需要引入iconfont.css
即可,如果需要增加图标,新下载了图标包下来后,只需要将iconfont.css
文件更新一下即可。
如果使用svg的方式展示图标的话,这时候起作用的是iconfont.js
文件,如果图标有更新的话,将iconfont.js
文件替换掉就行。
unicode
Unicode 是字体在网页端最原始的应用方式,特点是:
- 兼容性最好,支持 IE6+,及所有现代浏览器。
- 支持按字体的方式去动态调整图标大小,颜色等等。
- 但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。
注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式
Unicode 使用步骤如下:
第一步:引入
iconfont.css
文件// iconfont.css
@font-face {font-family: "iconfont";
src: url('iconfont.eot?t=1593657248234'); /* IE9 */
src: url('iconfont.eot?t=1593657248234#iefix') format('embedded-opentype'), /* IE6-IE8 */
//更新的时候最重要的是更新这一部分
url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('iconfont.woff?t=1593657248234') format('woff'),
url('iconfont.ttf?t=1593657248234') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
url('iconfont.svg?t=1593657248234#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-yundong-:before {
content: "e611";
}
.icon-yundong:before {
content: "e6ea";
}
.icon-zhoubao:before {
content: "e68c";
}
// 新增的图标后面直接加入即可
.icon-meilishuo:before {
content: "ea15";
}
.icon-meilishuo1:before {
content: "e78e";
}
第二步:挑选相应图标并获取字体编码,应用于页面
<span class="iconfont">3</span>
font-class
font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。
与 Unicode 使用方式相比,具有如下特点:
- 兼容性良好,支持 IE8+,及所有现代浏览器。
- 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
- 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
- 不过因为本质上还是使用的字体,所以多色图标还是不支持的。
font-class 使用步骤如下:
第一步:引入
iconfont.css
文件<link rel="stylesheet" href="./iconfont.css">
注意:修改
iconfont.css
文件的方式与unicode
的方式是一样的第二步:挑选相应图标并获取类名,应用于页面:
<span class="iconfont icon-xxx"></span>
symbol
这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:
- 支持多色图标了,不再受单色限制。
- 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
- 兼容性较差,支持 IE9+,及现代浏览器。
- 浏览器渲染 SVG 的性能一般,还不如 png。
使用步骤如下:
第一步:引入项目下面生成的 symbol 代码:
<script src="./iconfont.js"></script>
第二步:加入通用 CSS 代码(引入一次就行):
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
第三步:挑选相应图标并获取类名,应用于页面:
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-xxx"></use>
</svg>
vue项目中使用svg
使用svg好处
- 支持多色图标了,不再受单色限制。
- 支持像字体那样通过font-size,color来调整样式。
- 支持 ie9+
- 可利用CSS实现动画。
- 减少HTTP请求。
- 矢量,缩放不失真
- 可以很精细的控制SVG图标的每一部分
使用svg-icon的好处是我再也不用发送woff|eot|ttf| 这些很多个字体库请求了,所有的svg都可以内联在html内。
svg是真正的矢量图,放大缩小都不会失真。
安装loader
mpm i svg-sprite-loader -D
svg-sprite-loader
将加载的 svg
图片拼接成雪碧图,放到页面中,其它地方通过 复用。
本来只添加svg-sprite-loader
就行了,但是svg
也是图片的一种,所以file-loader
也会对其进行处理,所以就会冲突,解决的办法就是,在项目中新建一个文件icons
,使用file-loader
编译svg
的时候不编译icons
里面的图标
添加配置——vue.config.js
vue inspect --rules
:可查看webpack中有哪些相关的规则
['vue',
'images',
'svg',
'media',
'fonts',
'pug',
'css',
'postcss',
'scss',
'sass',
'less',
'stylus',
'js',
'eslint',
'icons'
]
vue inspect --rule svg
:单独查看某一项在webpack
中的配置
// 对svg处理的默认配置:使用file-loader{
test: /.(svg)(?.*)?$/,
use: [
{
loader: 'file-loader',
options: {
name: 'static/img/[name].[hash:8].[ext]'
}
}
]
}
添加链式操作
vue-cli
默认情况下使用file-loader
对svg进行处理,会将svg放到/img
目录下,现在需要更改webpack配置,使用svg-sprite-loader处理
svg`。
直接将,默认配置中的
test: /.(png|jpe?g|gif|svg)(?.*)?$/,
中的svg
删掉,不过这样处理是有风险的,因为项目中可能有的svg
确实需要当图片资源使用,而且第三方库中也有可能使用到svg
在webpack中重新做配置,只处理需要处理的那一部分
svg
module.exports={publicPath:'/best-practice',//部署应用包时的基本URL
devServer:{
port:port
},
configureWebpack:{
// 想index.html注入标题
name:'vue最佳实践'
},
chainWebpack(config){
// set svg-sprite-loader
// 1.svg rule中要排除icons目录
config.module
.rule('svg')
.exclude.add(resolve('src/icons'))
.end()
// 2加一个规则icons
config.module
.rule('icons')
.test(/.svg$/)
.include.add(resolve('src/icons'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end()
}
}
自动导入svg图标
// icons/index.jsconst req = require.context('./svg', false, /.svg$/)
req.keys().map(req);
// main.js
import'./icons'
使用:
<svg class="icon" aria-hidden="true"><use xlink:href="#icon-xxx"></use>
</svg>
其实到这一步,项目中已经可以随意使用svg
图标了,为了能统一处理svg
图标的样式以及代码的可阅读性,咱们一起去创建一个svg
的公用组件去~
创建svg组件
- svg组件
<template><svg :class="svgClass" aria-hidden="true">
<use :href="https://juejin.im/post/5efbeedbf265da23094e00bb/iconName" />
</svg>
</template>
<script>
// doc: https://panjiachen.github.io/vue-element-admin-site/feature/component/svg-icon.html#usage
import { isExternal } from '@/utils/validate'
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
isExternal () {
return isExternal(this.iconClass)
},
iconName () {
return `#icon-${this.iconClass}`
},
svgClass () {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
},
styleExternalIcon () {
return {
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover!important;
display: inline-block;
}
</style>
- 全局注册
import Vue from'vue'import SvgIcon from'@/components/SvgIcon'
// 全局注册svg组件
Vue.component('svg-icon', SvgIcon)
- 使用
<svg-icon icon-class="keeping"></svg-icon>
权限控制及动态路由
路由定义
路由分为两种:constantRoutes
和 asyncRoutes
constantRoutes
是常规路由,不需要守卫,用户可直接访问
exportconst constantRoutes = [{
path: '/login',
component: () =>import('@/views/login/index'),
hidden: true
},
{
path: '/404',
component: () =>import('@/views/error-page/404'),
hidden: true
},
{
path: '/401',
component: () =>import('@/views/error-page/401'),
hidden: true
}
]
asyncRoutes
:权限页面,用户需要登录并且拥有访问的权限角色才能访问
exportconst asyncRoutes = [{
path: "/about",
component: Layout,
redirect: "/about/index",
children: [
{
path: "index",
component: () =>
import(/* webpackChunkName: "home" */"@/views/About.vue"),
name: "about",
meta: {
title: "About",
icon: "qq",
roles: ['admin', 'editor']
},
}
]
}
]
exportdefaultnew Router({
mode: "history",
base: process.env.BASE_URL,
routes: constRoutes
})
路由守卫
// 白名单const whiteList = ['/login', '/auth-redirect'] // no redirect whitelist
if (whiteList.indexOf(to.path) !== -1) {
// 白名单, go directly
next()
} else {
api.shadow().then(async () => {
const hasRoles = store.getters.roles && store.getters.roles.length > 0
if (hasRoles) {
next()
} else {
try {
// 获取权限
const roles = await store.dispatch('user/getInfo')
resetRouter()
// 挂载路由
let accessRoutes = await store.dispatch('permission/generateRoutes', roles)
accessRoutes = accessRoutes.map(item => ({ ...item, component: Layout }))
router.addRoutes(accessRoutes)
next({
...to,
replace: true
}) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
} catch (error) {
next('login')
}
}
}).catch(() => {
next('login')
})
}
添加动态路由
根据用户角色过滤出克访问的路由并动态添加到router。
创建permission模块,store/modules/permission.js
import { asyncRoutes, constantRoutes } from'@/router'/**
* Use meta.role to determine if the current user has permission
* @param roles
* @param route
*/
functionhasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role))
} else {
returntrue
}
}
/**
* Filter asynchronous routing tables by recursion
* @param routes asyncRoutes
* @param roles
*/
exportfunctionfilterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
res.push(tmp)
}
})
return res
}
const state = {
routes: [],
addRoutes: []
}
const mutations = {
SET_ROUTES: (state, routes) => {
state.addRoutes = routes
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, roles) {
returnnewPromise(resolve => {
let accessedRoutes
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes)
})
}
}
exportdefault {
namespaced: true,
state,
mutations,
actions
}
按钮权限
--待更新--
导航菜单生成
--待更新--
服务封装
axios拦截器
--待更新--
数据mock
本地mock
easy-mock
解决跨域
--待更新--
参考
手摸手,带你优雅的使用 icon
以上是 10-讲讲vue-element-admin开源项目 的全部内容, 来源链接: utcz.com/a/29174.html