demand

  1. Generate navigation content according to the Router table to achieve relevant effects, including:
    • Home directory click background color change
    • Subdirectory click change color
  2. Url input related routes, navigation also achieve 1 effect;

Technology stack

  1. vue
  2. element
  3. vuex
  4. Node (Path module)
  5. Router Global navigation guard
  6. Project architecture: Vue-element-template

implementation

Generate menu based on the router

The Structure of the routerMap is a simple two-dimensional array, which can be up to two dimensions based on requirements, without considering deeper nesting and external links.

For details about routes, see route configuration items and examples.

Register the component Navbar

This is not to say.

Generate El-Tabs using router data

Obtaining Router data

computed: {
    routes() {
      return this.$router.options.routes
    }
}
Copy the code

Render el-tabs to display the unhidden Router

<el-tabs v-model="activeName" @tab-click="handleClick">
  <el-tab-pane
      v-for="route in routes"
      class="slideInUp"
      v-if=! "" route.hidden&&route.children"
      :name="route.path"
      :label= "route.name"
      >
  </el-tab-pane>
</el-tabs>
Copy the code

That’s not so hard, but then there’s the problem.

Problem one: dynamic renderinglabelforicon+text

Label, as an attribute of El-Tab-Pane, receives a string, but I want label with not only text but also my icon on it. After a long time of fruitlessness, I had no choice but to give Element a feature. Finally, I found the idea: Label slot.

<div slot="label">
    <svg-icon class="icons" :icon-class="iconClassName(route)" />
    <span class="nav-title">{{route.name}}</span>
  </div>
Copy the code

Slot in the middle of el-Tab-Pane, and my ICONS appeared! Sen!

Sub – menu

Because el-Tab is used for navbar, submenu generation simply places router.children between El-Tab-Pane

Dynamic rendering of the active class

According to the requirements, the color will change after clicking submenu, just apply the dynamic binding class:

<li
    v-for="(item, key, index) in route.children"
    :key="item.name"
    :id = "index"
    style="cursor: pointer;"
    :class="{'sub-active':activeIndex == item.path}"
    @click="activeIndex = item.path, clickLink(item, $event)">
    {{item.name}}
</li>
Copy the code

According to the uniqueness of path, the activeIndex is changed to the current path when you click, and the activeIndex class is added to the current submenu.

Problem 3: Click Submenu to change the route

The important role of navigation is to control the path changes. The path.resolve application can generate the current clicked path. The clickLink method added in li is:

const path = this.resolvePath(routePath)
this.$router.push(path)
Copy the code

ResolvePath as follows:

resolvePath(routePath) {
  return path.resolve(this.basePath, routePath)
}
Copy the code

This.$router.push can be used to redirect a route, but the current redirect path resolves only subdirectories.

Correction: In El-Tab-Pane add :index=”resolvePath(route.path)” to retrieve the corresponding parent path during initial rendering. Then the secondary route becomes: http://localhost:9528/#/task/index, well, that’s what Aijia wants

The basic functionality is there until I manually enter the path.

Update TAB entries based on the entered path

The active TAB is controlled by the data activeName; The activeIndex item in the submenu is bound to its own path.

Failed Attempt 1:

Add watch, monitor router changes, change activeName and activeIndex, failed;

Failed Attempt TWO:

Mixins was used to generate local hook functions, overwriting activeName and activeIndex.

Cause of failure: The route hook is correct, but it must be globally guarded, and then update the activeName and activeIndex values when the route changes, and then use store to obtain new values for overwriting.

Problem four: manually input path rendering menu

  • Add menu to store
const menu = {
  state: {
    menumain: '/',
    menusub:'overview'
  },
  mutations: {
    updatemain (state,n) {
      state.menumain = n
    },
    updatesub (state,n) {
      state.menusub = n
    }
  }
}
export default menu
Copy the code

Add guard function:

router.beforeEach((to, from, next) => {
  // add menu change data
  let paths = to.path.trim().split('/')
  let activeName =' '
  let activeIndex =' 'activeName = !! paths[2]==true ? '/'+ paths[1] : '/'
  store.commit('updatemain', activeName) activeIndex = !! paths[2]==true ? paths[2] : paths[1]
  store.commit('updatesub', activeIndex) //end menu})Copy the code

Update data in navbar:

  computed: {
    newActiveName() {
      return this.$store.getters.menumain;
    },
    newActiveSubMenu() {
      return this.$store.getters.menusub;
    }
  },
  watch: {
    newActiveName(val){
    	this.activeName = val
    },
    newActiveSubMenu(val) {
      this.activeIndex = val
    }
  }
Copy the code

Path changes are detected by calculating properties and new values are obtained. Watch then detects data changes and assigns the new values to activeName and activeIndex.

Take a look at the results!

Front End Engineer wanted

Busy until midnight finally realized, at first thought it was difficult, do slowly, each time to fix a small point and feel very easy. Or “the sufferers can not, can not be difficult”.

Also, many designs in VUE have unexpected uses, such as slots used this time, dynamically binding classes; I still remember the days when Yong JQ went through li deleting active and then adding active class to an item. Data-driven free DOM manipulation, yeah~

Reference:

Vue named slot Vue-element-admin Navigation guard Element el-tabs

This article is used as a component to write notes, thank you for your advice ~

Unresolved issues

  1. Nested routing: can do recursive processing in the routing hook function
  2. Device check and mobile phone adaptation
  3. Vue related source in-depth

Bug fix

  1. Basepath is generated when you click TAB. If you jump to the path, basepath will be empty. If you click TAB, the path will be 404.

    Fix: The Basepath generation is generated synchronously on the Click item

      clickLink(item, e, route) {
    	let childPath = item.path
        if(! this.isExternalLink(childPath)) { e.preventDefault()let parentPath = this.resolvePath(route.path)
            const finalPath = path.resolve(parentPath, childPath)
            this.$router.push(finalPath)
        }
    }
    Copy the code

Aboutme

Author: Yanni Jia Nickname: Very rabbit Wechat: yanni_it Email: [email protected]