0


若依框架前端Vue项目分析实战

说明

前面讲解了vue2的单点知识,下面用若依提供的vue项目,实战分析一下,拿到一个vue项目,如何进行分析并进行二次开发。

一、public/index.html与main.js和App.vue三者的关系

vue项目是单界面应用,所有的界面将在public下的index.html里呈现。
main.js是程序的入口,在这里,定义了vue实例对象。如下代码:

newVue({el:'#app',
  router,//路由插件
  store,//vuex插件,所有的组件中都可以使用store中的action,mutation和state数据。通过$strore调用或者mapXXX函数映射render:h=>h(App)})

其中,el配置项是配置vm实例要填充的public/index.html里的元素对象。render属性配置的是用App.vue组件进行填充。在vm实例对象挂载完成之前,即mouted函数执行之前,页面显示的是public/index.html中本身的元素,在挂载完成后,public/index.html中会显示App.vue中的元素。
在public/index.html中,原有的是加载中插件,如下:

<body><divid="app"><divid="loader-wrapper"><divid="loader"></div><divclass="loader-section section-left"></div><divclass="loader-section section-right"></div><divclass="load_title">正在加载系统资源,请耐心等待</div></div></div>

所以,刷新界面时,先出现加载中界面,如下:
在这里插入图片描述
等vm实例挂载完成后,显示App.vue组件内容。

下面分析App.vue中组件的内容。
看template内容:

<template><divid="app"><!-- 路由占位,router-link在哪   --><router-view/></div></template>

这里只有router-view,说明这里要进行路由的切换展示。所以,接下来该分析路由的配置,看App.vue跳转到了哪个组件。

二、路由分析

路由的配置在router/index.js下,进行分析:

{path:'',component: Layout,redirect:'index',children:[{path:'index',component:()=>import('@/views/index'),name:'Index',meta:{title:'首页',icon:'dashboard',affix:true}}]}

这段路由配置就是App.vue挂载完成后的展示组件。首先,项目访问路径为’'(空)时,加载的组件是Layout组件,展示Layout组件内容。通过redirect属性重定向到index路径。index是子路由,在Layout中展示index.vue界面。这就是为何每次刷新,默认打开首页tab页的原因。
这里,补充个知识点:
不管是router-link的to属性,还是直接在浏览器输入路径,如果输入的是子路由的路径,那么界面会展示子路由的组件,也会展示父路由的组件。不会单独展示子路由对应的组件。
如:to=/home/SeatDistribute,则对应的view会显示App.vue和Home.vue和/home/SeatDistribute组件。
to=/home,view会显示App.vue组件和Home.vue组件。
根据路径,找router-view的位置:
路径为一层的,如:/home。则对应App.vue中的router-view。
路径为多层的,如/home/SeatDistribute,则找其父组件的router-view,即Home.vue组件中的router-view。

所以,上面重定向到index.vue组件后,也是显示Layout组件的。即在Layout组件内部,再显示index.vue组件。router定义子路由时,默认children子路由就是被父路由包裹的。

三、Layout组件分析

通过上面分析,可知App.vue展示的是Layout组件,那么下面就分析Layout组件。通过分析,Layout组件是布局组件,即后台管理系统的整体布局,是通过这个组件完成的。里面包含了很多子组件嵌套。下面先画一下其组件划分结构:
在这里插入图片描述
可见,将整体布局划分成上图的几个组件。这也体现出了vue组件化编程的思想。在每个组件内部,又对各个部分进行了组件的拆分,例如在navbar组件中,
在这里插入图片描述
每个元素,都划分成了一个组件。这里主要体会组件化编程思想。具体组件的实现方式,这里不做深入研究。已经提供好的功能,我们直接拿来用即可。

四、Vuex应用分析

在main.js中定义vue的时候,可以看到使用了Vuex技术,如下:

newVue({el:'#app',
  router,//路由插件
  store,//vuex插件,所有的组件中都可以使用store中的action,mutation和state数据。通过$strore调用或者mapXXX函数映射render:h=>h(App),beforeMount(){debugger;}})

下面就分析一下Vuex技术在项目中是如何应用的。
首先,看stroe/index.js文件:

Vue.use(Vuex)const store =newVuex.Store({modules:{
    app,
    user,
    tagsView,
    permission,
    settings
  },
  getters
})exportdefault store

可以看到,使用了模块化命名,分成了权限模块,设置模块,用户模块等几个模块。因为Vuex最终操作的都是state对象,所以,重点看一下这几个模块的state对象都存入了什么:
权限模块permission.js:

state:{routes:[],addRoutes:[],defaultRoutes:[],topbarRouters:[],sidebarRouters:[]}

可见,权限模块state存入了路由数据。路由可以共享到每个组件去使用。

用户模块user.js:

state:{token:getToken(),name:'',avatar:'',roles:[],permissions:[]}

用户模块state定义了token,用户名,用户角色,用户权限等数据,用于共享。

设置模块settings.js:

const state ={title:'',theme: storageSetting.theme ||'#409EFF',sideTheme: storageSetting.sideTheme || sideTheme,showSettings: showSettings,topNav: storageSetting.topNav ===undefined? topNav : storageSetting.topNav,tagsView: storageSetting.tagsView ===undefined? tagsView : storageSetting.tagsView,fixedHeader: storageSetting.fixedHeader ===undefined? fixedHeader : storageSetting.fixedHeader,sidebarLogo: storageSetting.sidebarLogo ===undefined? sidebarLogo : storageSetting.sidebarLogo,dynamicTitle: storageSetting.dynamicTitle ===undefined? dynamicTitle : storageSetting.dynamicTitle
}

可见,settings.js模块定义了皮肤颜色,主题风格,边距等等数据用于共享。

各模块的Actions和Mutations对象定义了各种函数来操作这些state数据,这里就 不一一列举了。

通过上面存入Vuex中的数据可以知道,在项目中,把全局的一些信息,存入Vuex中。这些信息可以是前端自己设定的,也可以是后台获取的数据。这样,在项目的任何一个组件中,都可以通过$store属性或者mapXXX函数获取到这些共享数据。
Vuex有点儿类似于原生js中的全局变量的感觉。

五、关于动态路由和菜单权限的实现

前面讲路由时,路由都是在router/index.js里预置好的。但是在真实项目中,菜单肯定是通过权限,在后台获取的。分析一下若依框架,是如何实现权限分配和动态路由的。
首先找到菜单的组件,上面已经分析过,是Layout组件中的sidebar组件。点进去,看sidebar组件的实现:

<sidebar-item
                    v-for="(route, index) in sidebarRouters":key="route.path  + index":item="route":base-path="route.path"/>

可见,其遍历了sidebarRouters,而sidebarRouters通过Vuex的分析可知,其配置在了permissions模块的state中。所以,在permissions.js的Actions对象和Mutations对象中,肯定有操作这个数据的函数,找相应的代码。
代码比较分散,下面看其实现思路:
首先,在Actions函数中,利用axios发送请求到后台,获取用户菜单权限,该接口的返回值如下:

{"msg":"操作成功",
    "code":200,
    "data":[{"name":"System",
        "path":"/system",
        "hidden": false,
        "redirect":"noRedirect",
        "component":"Layout",
        "alwaysShow": true,
        "meta":{"title":"系统管理",
            "icon":"system",
            "noCache": false,
            "link": null
        },
        "children":[{"name":"User",
            "path":"user",
            "hidden": false,
            "component":"system/user/index",
            "meta":{"title":"用户管理",
                "icon":"user",
                "noCache": false,
                "link": null
            }}, {"name":"Role",
            "path":"role",
            "hidden": false,
            "component":"system/role/index",
            "meta":{"title":"角色管理",
                "icon":"peoples",
                "noCache": false,
                "link": null
            }}, {"name":"Menu",
            "path":"menu",
            "hidden": false,
            "component":"system/menu/index",
            "meta":{"title":"菜单管理",
                "icon":"tree-table",
                "noCache": false,
                "link": null
            }}, {"name":"Dept",
            "path":"dept",
            "hidden": false,
            "component":"system/dept/index",
            "meta":{"title":"部门管理",
                "icon":"tree",
                "noCache": false,
                "link": null
            }}, {"name":"Post",
            "path":"post",
            "hidden": false,
            "component":"system/post/index",
            "meta":{"title":"岗位管理",
                "icon":"post",
                "noCache": false,
                "link": null
            }}, {"name":"Dict",
            "path":"dict",
            "hidden": false,
            "component":"system/dict/index",
            "meta":{"title":"字典管理",
                "icon":"dict",
                "noCache": false,
                "link": null
            }}, {"name":"Config",
            "path":"config",
            "hidden": false,
            "component":"system/config/index",
            "meta":{"title":"参数设置",
                "icon":"edit",
                "noCache": false,
                "link": null
            }}, {"name":"Notice",
            "path":"notice",
            "hidden": false,
            "component":"system/notice/index",
            "meta":{"title":"通知公告",
                "icon":"message",
                "noCache": false,
                "link": null
            }}, {"name":"Log",
            "path":"log",
            "hidden": false,
            "redirect":"noRedirect",
            "component":"ParentView",
            "alwaysShow": true,
            "meta":{"title":"日志管理",
                "icon":"log",
                "noCache": false,
                "link": null
            },
            "children":[{"name":"Operlog",
                "path":"operlog",
                "hidden": false,
                "component":"monitor/operlog/index",
                "meta":{"title":"操作日志",
                    "icon":"form",
                    "noCache": false,
                    "link": null
                }}, {"name":"Logininfor",
                "path":"logininfor",
                "hidden": false,
                "component":"monitor/logininfor/index",
                "meta":{"title":"登录日志",
                    "icon":"logininfor",
                    "noCache": false,
                    "link": null
                }}]}]}, {"name":"Monitor",
        "path":"/monitor",
        "hidden": false,
        "redirect":"noRedirect",
        "component":"Layout",
        "alwaysShow": true,
        "meta":{"title":"系统监控",
            "icon":"monitor",
            "noCache": false,
            "link": null
        },
        "children":[{"name":"Online",
            "path":"online",
            "hidden": false,
            "component":"monitor/online/index",
            "meta":{"title":"在线用户",
                "icon":"online",
                "noCache": false,
                "link": null
            }}, {"name":"Job",
            "path":"job",
            "hidden": false,
            "component":"monitor/job/index",
            "meta":{"title":"定时任务",
                "icon":"job",
                "noCache": false,
                "link": null
            }}, {"name":"Druid",
            "path":"druid",
            "hidden": false,
            "component":"monitor/druid/index",
            "meta":{"title":"数据监控",
                "icon":"druid",
                "noCache": false,
                "link": null
            }}, {"name":"Server",
            "path":"server",
            "hidden": false,
            "component":"monitor/server/index",
            "meta":{"title":"服务监控",
                "icon":"server",
                "noCache": false,
                "link": null
            }}, {"name":"Cache",
            "path":"cache",
            "hidden": false,
            "component":"monitor/cache/index",
            "meta":{"title":"缓存监控",
                "icon":"redis",
                "noCache": false,
                "link": null
            }}]}, {"name":"Tool",
        "path":"/tool",
        "hidden": false,
        "redirect":"noRedirect",
        "component":"Layout",
        "alwaysShow": true,
        "meta":{"title":"系统工具",
            "icon":"tool",
            "noCache": false,
            "link": null
        },
        "children":[{"name":"Build",
            "path":"build",
            "hidden": false,
            "component":"tool/build/index",
            "meta":{"title":"表单构建",
                "icon":"build",
                "noCache": false,
                "link": null
            }}, {"name":"Gen",
            "path":"gen",
            "hidden": false,
            "component":"tool/gen/index",
            "meta":{"title":"代码生成",
                "icon":"code",
                "noCache": false,
                "link": null
            }}, {"name":"Swagger",
            "path":"swagger",
            "hidden": false,
            "component":"tool/swagger/index",
            "meta":{"title":"系统接口",
                "icon":"swagger",
                "noCache": false,
                "link": null
            }}]}, {"name":"Http://ruoyi.vip",
        "path":"http://ruoyi.vip",
        "hidden": false,
        "component":"Layout",
        "meta":{"title":"若依官网",
            "icon":"guide",
            "noCache": false,
            "link":"http://ruoyi.vip"}}]}

分析其返回值,可以看到,就是按照router的格式,将菜单数据进行了返回。其中,子菜单就以children属性,定义在了父菜单下面。菜单名称,定义在了meta属性里面。
这样,就根据用户权限获取了菜单路由信息。下面,看这些信息是如何进行使用的。
还回到Sidebar组件的这段代码:

<sidebar-item
                    v-for="(route, index) in sidebarRouters":key="route.path  + index":item="route":base-path="route.path"/>

可以看到,涉及到另一个自定义组件sidebar-item,遍历菜单,把获取到的路由信息传了过去,最终实现了菜单的展示和点击出现tab页面。具体实现不再详细解释。

六、自定义指令的应用

若依框架自定义了几个指令,下面看如何进行自定义指令和这几个指令的作用:
在directive包下,进行了自定义指令。
重点看permissions相关的自定义指令。一个权限指令,一个角色指令,其实现思路就是根据Vuex中user的角色和权限数据,判断是否有某个角色或者某个权限,从而判断是否有权限操作某个按钮。即菜单中按钮权限的判断,是通过自定义指令完成的。

七、关于element-ui

在一个组件中,有很多的标签,当是el-开头的时候,就是element-ui的标签,可以根据官网查找对应属性的意思等。不是以el开头的标签,可能是项目自定义的组件,也可能是第三方组件。具体的点进去一看便知。

八、history模式与项目部署

前面讲到,history模式下,刷新浏览器,会报404,这里,再说清楚一点。
在开发环境中,即使使用history模式,刷新界面,不会报404,但是项目打包部署到web服务器上后,再访问前端项目,再刷新界面,会报404,这是为何呢?
个人理解,在开发环境中,vue-cli自行解决了history刷新报404的问题,直接通过路由,定位到了相应的组件。但是项目部署到web服务器上后,刷新界面,浏览器中的url直接就发送到web服务器上了,服务器默认并不会处理路径,而是直接根据路径找资源,找不到url的资源,所以就报404了。
项目部署后,如何解决history模式的404问题呢?根据不同的web服务器,进行简单的设置,就可以解决了。
具体可参考:不同的历史模式
下面记录在nginx下部署vue项目的过程。
首先,在package.json中, 运行build命令,对项目进行打包。
打包完成后,在项目根目录下,会生成dist文件,dist文件里,打包好的文件,有个index.html文件,整个vue项目,就在这个单界面里展示。

配置nginx服务器:
首先,配置nginx.config配置文件,重点如下:

server {
        listen       80;#nginx端口,也是前端项目访问的端口
        server_name  localhost;#nginx的ip,也是前端工程访问ip#charset koi8-r;#access_log  logs/host.access.log  main;

        location / {
            root   "F:/java_structure/vue/RuoYi-Vue-master/ruoyi-ui/dist";#vue项目打包dist后的路径
            index  index.html index.htm;#这里配置index就行,不用管
            try_files $uri$uri/ /index.html;#解决history刷新报404问题,配置此项后刷新就不再报404了。}#vue项目想后台发送请求的前缀。在开发环境中,配置proxyTable可以进行代理转发。在nginx上,在此进行代理转发配置
        location ^~/prod-api { 
           rewrite ^/prod-api/(.*)$ /$1break;#此配置表明发送到后台的请求不带prod-api,将其自动去掉
           proxy_pass http://localhost:8080;#后台服务器路径。此处需要注意,这里的后台服务器,是为vue项目提供数据支撑的服务器。而上面配置的listen和server_name,是nginx的port和ip,也是前端项目的port和ip。解决history模式404的问题,也是解决的nginx服务器返回的404的问题,并不是解决后台项目返回的404的问题,这点一定要清楚。}

        error_page   500502503504  /50x.html;
        location = /50x.html {
            root   html;}}

八、统计图,报表使用

echarts可以和vue很好的配合使用,此外,再提供一个很好的统计报表可视化插件库:Viser

标签: vue.js 前端

本文转载自: https://blog.csdn.net/qq1309664161/article/details/124041085
版权归原作者 敲代码的小小酥 所有, 如有侵权,请联系我们删除。

“若依框架前端Vue项目分析实战”的评论:

还没有评论