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发展史

  1. 最初的时候,大部分图标都是用img来实现的,如果一个项目中涉及到很多图标的时候,那一个页面中的请求资源中img占了大部分;
  2. 后面慢慢出现了雪碧图,将很多图标整合到一个图片上,css利用background-position 定位显示不同的 icon 图标,比起最开始img的方式,雪碧图已经有了很大的优化,但是依然有一个非常大的痛点,那就是每新增一个图标,就得去更新原先的雪碧图,这样做风险是很大的,不小心影响了原先图标的位置时,那页面中某些图标就加载不出来了,这种方式是极难维护的。
  3. Font Awesome 出现,Font Awesome官网的体验不是太好,图标很小,找起来相当费力。

  4. iconfont出现,阿里的开源库,找起来相当轻松,而且图标数量相当可人。

iconfont使用方式

先总体讲一下,在iconfont官网上将需要的图标打包下载下来后,如下:

将上面的这些包放到项目中,如果仅仅用unicodefont-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`。

  1. 直接将,默认配置中的test: /.(png|jpe?g|gif|svg)(?.*)?$/,中的svg删掉,不过这样处理是有风险的,因为项目中可能有的svg确实需要当图片资源使用,而且第三方库中也有可能使用到svg

  2. 在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.js

const 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>

权限控制及动态路由

路由定义

路由分为两种:constantRoutesasyncRoutes

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

回到顶部