Ruoyi framework front-end Vue project analysis actual combat

illustrate

The single-point knowledge of vue2 is explained above. Next, use the vue project provided by Ruoyi to analyze it in practice. When you get a vue project, how to analyze it and carry out secondary development.

1. The relationship between public/index.html and main.js and App.vue

The vue project is a single-interface application, and all interfaces will be presented in index.html under public.
main.js is the entry point of the program, where the Vue instance object is defined. The following code:

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

Among them, the el configuration item is the element object in the public/index.html to be filled by configuring the vm instance. The render attribute is configured to be populated with App.vue components. Before the vm instance object is mounted, that is, before the mounted function is executed, the page displays the elements in public/index.html. After the mounting is completed, the elements in App.vue will be displayed in public/index.html.
In public/index.html, the original one is the loading plugin, as follows:

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

Therefore, when refreshing the interface, the loading interface appears first, as follows:
insert image description here
After the vm instance is mounted, the content of the App.vue component is displayed.

Let's analyze the content of the components in App.vue.
Look at the template content:

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

There is only router-view here, indicating that the switching display of routing is to be performed here. Therefore, the next step is to analyze the routing configuration and see which component App.vue jumps to.

2. Routing analysis

The routing configuration is under router/index.js for analysis:

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

This routing configuration is the display component after App.vue is mounted. First, when the project access path is '' (empty), the loaded component is the Layout component, displaying the content of the Layout component. Redirect to the index path through the redirect attribute. index is a sub-route, which displays the index.vue interface in Layout. This is why every time you refresh, the home tab page is opened by default.
Here, add a knowledge point:
whether it is the to attribute of router-link, or directly enter the path in the browser, if the input is the path of the child route, then the interface will display the components of the child route and the components of the parent route. The components corresponding to sub-routes will not be displayed separately.
For example: to=/home/SeatDistribute, the corresponding view will display App.vue, Home.vue and /home/SeatDistribute components.
to=/home, view will display App.vue component and Home.vue component.
According to the path, find the location of router-view:
the path is one layer, such as: /home. It corresponds to router-view in App.vue.
If the path is multi-layered, such as /home/SeatDistribute, find the router-view of its parent component, that is, the router-view in the Home.vue component.

Therefore, after redirecting to the index.vue component above, the Layout component is also displayed. That is, inside the Layout component, the index.vue component is displayed. When the router defines a child route, the default children route is wrapped by the parent route.

Three, Layout component analysis

Through the above analysis, it can be seen that App.vue displays the Layout component, so the Layout component is analyzed below. Through analysis, the Layout component is a layout component, that is, the overall layout of the background management system is completed through this component. It contains a lot of nested sub-components. Let's first draw its component division structure:
insert image description here
It can be seen that the overall layout is divided into several components in the above figure. This also reflects the idea of ​​vue component programming. Inside each component, each part is split into components. For example, in the navbar component,
insert image description here
each element is divided into a component. Here we mainly understand the idea of ​​component programming. The implementation of specific components will not be studied in depth here. We have already provided good functions, we can use them directly.

4. Vuex application analysis

When defining vue in main.js, you can see that Vuex technology is used, as follows:

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

Let's analyze how Vuex technology is applied in the project.
First, look at the stroe/index.js file:

Vue.use(Vuex)

const store = new Vuex.Store({
    
    
  modules: {
    
    
    app,
    user,
    tagsView,
    permission,
    settings
  },
  getters
})

export default store

It can be seen that modular naming is used, and it is divided into several modules such as permission module, setting module, and user module. Because the final operation of Vuex is the state object, so let's focus on what is stored in the state objects of these modules:
permission module permission.js:

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

It can be seen that the permission module state has stored routing data. Routing can be shared to each component for use.

User module user.js:

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

The user module state defines token, user name, user role, user permission and other data for sharing.

Settings module 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
}

It can be seen that the settings.js module defines skin color, theme style, margin and other data for sharing.

The Actions and Mutations objects of each module define various functions to manipulate these state data, which are not listed here.

From the data stored in Vuex above, we can know that in the project, some global information is stored in Vuex. This information can be set by the front end itself, or it can be data obtained by the background. In this way, in any component of the project, these shared data can be obtained through the $store attribute or the mapXXX function.
Vuex is a bit similar to the feeling of global variables in native js.

5. Realization of dynamic routing and menu permissions

When talking about routing earlier, routing is preset in router/index.js. But in a real project, the menu must be obtained in the background through permissions. Analyze how the Ruoyi framework implements authority distribution and dynamic routing.
First find the menu component, which has been analyzed above, it is the sidebar component in the Layout component. Click in to see the implementation of the sidebar component:

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

It can be seen that it traverses the sidebarRouters, and the sidebarRouters can be seen through the analysis of Vuex that it is configured in the state of the permissions module. Therefore, in the Actions object and Mutations object of permissions.js, there must be a function to manipulate this data, and find the corresponding code.
The code is relatively scattered, let’s look at its implementation ideas below:
First, in the Actions function, use axios to send a request to the background to obtain user menu permissions. The return value of this interface is as follows:

{
    
    
	"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"
		}
	}]
}

Analyzing its return value, we can see that the menu data is returned according to the router format. Among them, the submenu is defined under the parent menu with the children attribute. The menu name is defined in the meta attribute.
In this way, the menu routing information is obtained according to the user authority. Below, see how this information is used.
Also back to this code for the Sidebar component:

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

It can be seen that another custom component sidebar-item is involved, traversing the menu, passing the obtained routing information, and finally realizing the display of the menu and the tab page when clicked. The specific implementation will not be explained in detail.

6. Application of custom instructions

If you have customized several directives according to the framework, let’s see how to customize the directives and the functions of these directives:
Under the directive package, the custom directives are carried out.
Focus on custom commands related to permissions. A permission instruction, a role instruction, the idea of ​​implementation is to judge whether there is a certain role or a certain permission according to the user's role and permission data in Vuex, so as to judge whether there is permission to operate a certain button. That is, the judgment of button permissions in the menu is done through custom commands.

7. About element-ui

In a component, there are many tags. When it starts with el-, it is the tag of element-ui. You can find the meaning of the corresponding attribute according to the official website. A tag that does not start with el may be a project-defined component or a third-party component. Just click on it and you will know.

8. History mode and project deployment

As mentioned earlier, in history mode, if you refresh the browser, 404 will be reported. Here, let’s make it clearer.
In the development environment, even if you use the history mode and refresh the interface, 404 will not be reported, but after the project is packaged and deployed on the web server, and then access the front-end project, and then refresh the interface, 404 will be reported. Why?
Personal understanding, in the development environment, vue-cli solves the problem of history refresh report 404 by itself, and locates the corresponding components directly through routing. But after the project is deployed on the web server, refresh the interface, and the url in the browser is directly sent to the web server. The server does not process the path by default, but directly finds resources according to the path, and cannot find the resource of the url, so Just reported 404.
After the project is deployed, how to solve the 404 problem in the history mode? According to different web servers, simple settings can be solved.
For details, please refer to: Different History Modes
The process of deploying Vue projects under nginx is recorded below.
First, in package.json, run the build command to package the project.
After the packaging is completed, a dist file will be generated in the root directory of the project. In the dist file, there is an index.html file in the packaged file. The entire Vue project is displayed in this single interface.

Configure the nginx server:
First, configure the nginx.config configuration file, the key points are as follows:

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/(.*)$ /$1 break; #此配置表明发送到后台的请求不带prod-api,将其自动去掉
           proxy_pass http://localhost:8080; #后台服务器路径。此处需要注意,这里的后台服务器,是为vue项目提供数据支撑的服务器。而上面配置的listen和server_name,是nginx的port和ip,也是前端项目的port和ip。解决history模式404的问题,也是解决的nginx服务器返回的404的问题,并不是解决后台项目返回的404的问题,这点一定要清楚。
        }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
    
    
            root   html;
        }
    }

8. Use of statistical charts and reports

echarts can be used well with vue. In addition, it provides a good statistical report visualization plug-in library: Viser

Guess you like

Origin blog.csdn.net/qq1309664161/article/details/124041085