preface
Learning is like rowing upstream; not to advance is to drop back.
Starting with encouragement, today is a full day, ha ha ha ~
Without further ado, let’s get to the point.
I have used Vue for a long time, and the recent project involves a lot of components. Fortunately, I take more time to sort them out, so that I can review and use them next time. Vue 2.x version is used in this article, and Vue 3 version will be updated later when the time is ripe.
A journey of a thousand miles begins with a single step
Component address: Github.
Component functions
The specific functions are as follows:
- Open a new page, cache the target page;
- Close the opened page and clear the corresponding cache.
- Right menu realization: reset current, close current, close other, close left, close right, all closed;
- Page switching animation;
To prepare
Initialization project:
4.5.6 vue create vue-compCopy the code
Install the necessary dependencies:
/ / [email protected]; [email protected]; [email protected]; NPM install element-ui node-sass sass-loader --save-dev // [email protected]; [email protected]; npm install vuex vue-router --saveCopy the code
Simple layout:
Prepare several pages and configure routing, popular background layout, this piece of code will not be posted, if you need to refer to github sample code (this style file will almost not be posted, important is the idea and logic ~).
Train of thought
Use VUex and Keep-Alive to implement background project page caching (similar on mobile).
The keep-alive include attribute controls which pages need to be cached.
Create two variables in VUEX:
-
CachedViews: A collection of pages that need to be cached (it is important to note that the value stored in the array must correspond to the component’s name attribute for caching to take effect).
Data format: [‘Comp1’, ‘Comp2’…]
-
VisitedViews: A collection of all pages currently visited (the value saved is information about the current route, that is, vm.$route).
Data format: [{path: ‘/ comp1, name:’ comp1 ‘…}, {path: ‘/ comp2, name:’ comp2,…},…
Implementation idea:
- When the page is accessed, the current routing information is saved
visitedViews
And determine whether the page needs to be cached (add when setting up routesmeta
Object, settitle
If yes, cache is required. Otherwise, cache is required. Specific judgment conditions can be determined based on service scenarios. If necessary, the current route is routedname
depositcachedViews
. - When the page is empty, remove
cachedViews
andvisitedViews
The corresponding term in.
Basic implementation
Creating a storage variable
Create cache.js in vuex:
const state = {
visitedViews: [].cachedViews: []}const mutations = {}
const actions = {}
export default {
namespaced: true,
state,
mutations,
actions
}
Copy the code
Set the keep alive
Modify the page rendering component (I created the appmain. vue component for page rendering) to support caching.
Two calculated properties are set in the component:
-
CachedViews: currently cached entries.
-
Key: identifies the route. The value is set to fullPath of the current route.
Router-view attribute key (router-view key)
-
Without setting key:
When switching between components (for example, /comp/a => /comp/b, /comp? param=a => /comp? Created and Mounted are not executed. The beforeRouteUpdate method is used to obtain data.
The order in which the hook functions are executed: beforeRouteUpdate.
-
Set key to $route.path:
-
/comp/a => /comp/b: dynamic route. The value of $route.path is different, so components will not be reused.
BeforeRouteUpdate => Created => Mounted
-
/comp? param=a => /comp? Param =b: $route.path = $route.
The order in which the hook functions are executed: beforeRouteUpdate.
-
-
Set key to $route.fullPath:
/comp/a => /comp/b, /comp? param=a => /comp? Param =b: The corresponding value of $route.fullPath is different, so the component will not reuse it.
BeforeRouteUpdate => Created => Mounted
<template>
<section class="app-main">
<div class="app-main-content">
<keep-alive :include="cachedViews">
<router-view :key="key"/>
</keep-alive>
</div>
</section>
</template>
<script>
import { mapState } from 'vuex'
export default {
name: 'AppMain'.computed: {
...mapState('cache'['cachedViews']),
key() {
return this.$route.fullPath
}
},
}
</script>
Copy the code
Set up thetabs
Function: As a quick access point to opened pages.
Create a new component, TabsView, that looks like the “Tabs” TAB in Element-UI. I wrote my own style here because I thought it would be easier to control the components and make it easier to extend the business in the future.
<template>
<div class="tabs-view-container">
<div class="tabs-view-wrapper">
<router-link
v-for="tab in visitedViews"
:key="tab.name"
:class="`tabs-view-item ${isActive(tab) ? 'active' : ''}`"
ref="tab"
:to="tab.fullPath"
tag="span"
>
{{tab.title}}
<i class="el-icon-close" />
</router-link>
</div>
</div>
</template>
<script>
export default {
name: 'TabsView'.data() {
return {
visitedViews: [{title: 'overview'.name: 'Dashboard'}, {title: 'test'}}},methods: {
isActive(route) {
return route.name === this.$route.name
},
}
}
</script>
Copy the code
Take a look at this component first.
A variable is written to loop router-link, and the intermediate content is the title set in the routing information. The function isActive determines which item is currently active and sets the corresponding class name (the function is used, not the evaluated property, because arguments are being passed in).
The system display is as follows:
Adding cached pages
In front of the preparatory work is basically done, the following into the core stage ~
Since the variables visitedViews and cachedViews are managed in VUEX, the operation of the data depends on the corresponding mutation and action.
Add two functions to the Mutations object:
ADD_VISITED_VIEW
: Adds a page that has been visited.ADD_CACHED_VIEW
: Adds pages to be cached.
const mutations = {
/ / add
ADD_VISITED_VIEW(state, view) {
if (view.meta && view.meta.hidden) return
if (state.visitedViews.some(v= > v.name === view.name)) return
state.visitedViews.push(Object.assign({}, view, {
title: view.meta.title || 'no-name'}})),// If noCache is set, no caching is required (you can customize the condition).
ADD_CACHED_VIEW(state, view) {
if (view.meta && view.meta.hidden) return
if (state.cachedViews.includes(view.name)) return
if(! view.meta.noCache) { state.cachedViews.push(view.name) } } }Copy the code
Add three functions to the Actions object:
addView
Distribute:addVisitedView
andaddCachedView
.addVisitedView
Submitted:ADD_VISITED_VIEW
.addCachedView
Submitted:ADD_CACHED_VIEW
.
const actions = {
/ / add
addView({ dispatch }, view) {
dispatch('addVisitedView', view)
dispatch('addCachedView', view)
},
addVisitedView({ commit }, view) {
commit('ADD_VISITED_VIEW', view)
},
addCachedView({ commit }, view) {
commit('ADD_CACHED_VIEW', view)
}
}
Copy the code
Alter TabsView script:
A brief analysis:
The main change is to change the value of visitedViews, which was written out above, to a value added dynamically through the addTabs function. This function is executed during component initialization and route switching, with the selectedTab variable storing the route information for the currently displayed page (used later).
<script>
import { mapState, mapActions } from 'vuex'
export default {
name: 'TabsView'.data() {
return {
selectedTab: {}, // Record the current TAB}},computed: {
...mapState('cache'['visitedViews']),},watch: {
$route() {
this.addTabs()
}
},
created() {
this.addTabs()
},
methods: {
...mapActions('cache'['addView']),
isActive(route) {
return route.name === this.$route.name
},
// Add cached pages
addTabs() {
if (this.$route.name) {
this.addView(this.$route)
this.selectedTab = this.$route
}
return false
},
}
}
</script>
Copy the code
Test this: Now visit each page and the tabs TAB will add pages that have not been added before. If you refresh the page, the tabs bar will be cleared and the current page will be used as the initial page. (If you want to refresh the previously opened items, you can store data synchronization in the sessionStorage and read the values in the sessionStorage during initialization.) This function is not covered here, the logic is very simple).
With the addition, the following is the removal of ~
Delete cached pages
The method of deleting cached pages is basically the same as in the previous section, but it should be noted that if the item to be deleted is the currently displayed page, the page needs to be redirected.
Add two functions to the Mutations object:
DEL_VISITED_VIEW
: Deletes a page that has been accessed.DEL_CACHED_VIEW
: Deletes a record from the cache.
const mutations = {
// ...
// Delete single [I, v] => [0, {name: 'XXX '}]
DEL_VISITED_VIEW(state, view) {
for (const [i, v] of state.visitedViews.entries()) {
if (v.name === view.name) {
state.visitedViews.splice(i, 1)
break}}},DEL_CACHED_VIEW(state, view) {
const index = state.cachedViews.indexOf(view.name)
index > -1 && state.cachedViews.splice(index, 1)}}Copy the code
Add three functions to the Actions object:
delView
Distribute:delVisitedView
anddelCachedView
.delVisitedView
Submitted:DEL_VISITED_VIEW
.delCachedView
Submitted:DEL_CACHED_VIEW
.
const actions = {
// ...
/ / delete
delView({ dispatch }, view) {
dispatch('delVisitedView', view)
dispatch('delCachedView', view)
},
delVisitedView({ commit }, view) {
commit('DEL_VISITED_VIEW', view)
},
delCachedView({ commit }, view) {
commit('DEL_CACHED_VIEW', view)
},
}
Copy the code
Alter TabsView script section:
Analysis:
I thought about deleting cached pages in the following ways (after executing the delete action) :
-
The visitedViews array has values.
There are two cases:
- The item that triggers deletion is not the page currently displayed. You can delete it normally.
- If the item that triggers deletion is the currently displayed page, you need to switch the currently active page (the last item in this article) after deletion.
-
The visitedViews array has no value.
There are two cases:
- If the item triggering deletion is not an overview page, jump to the overview page.
- The item that triggered the deletion is the overview page, and the overview page is reloaded.
<template> <! -... --><i class="el-icon-close" @click.prevent.stop="closeSelectedTab(tab)" /><! -... --><template/>
<script>
import { mapState, mapActions } from 'vuex'
export default {
name: 'TabsView'.// ...
methods: {
...mapActions('cache'['addView'.'delView']),
// ...
closeSelectedTab(tab) {
/ / remove the TAB
this.delView(tab)
// Update the view
this.toUpdateView(tab)
},
// Update the view
toUpdateView(tab) {
const latestView = this.visitedViews.slice(-1) [0]
if (latestView) { // There are still visited pages
// The name of the deleted item is the same as the name of the current route
if (tab.name === this.$route.name) this.$router.push(latestView.fullPath)
} else {
if (tab.name === 'Dashboard') { The overview page needs to be "reloaded" when the last item to be deleted is the overview page
// TODO temporary solution
this.$router.push({
path: '/common'.query: {
test: Math.random() * 1000}})}else this.$router.push('/common')}}}}</script>
Copy the code
Only the revised parts are posted here (not all of them are posted in order to highlight the revised parts on the one hand, and to avoid too long, so you can make do with it).
There is a feature reserved here, where the overview page is reloaded. Because there is no “reset current” function, so I used a temporary solution: page jump way, to achieve the “reload” function.
Following the above analysis, of course, we also need to test whether page switching can be cached.
Write a random input field to test and execute the following two “use cases” :
- After entering the content, jump to another page and return to the page, the content is still there;
- Close the page and open it again, content empty;
At this point, the basic add and remove functions have been implemented ~
Now let’s extend the functionality.
Function extension
Add a TAB right-click menu that provides the following functions: Reset Current, Close Current, Close Other, Close left, Close right, and close all.
Right-click menu
Before implementing these extensions, the TabsView component should first implement the right menu, including menu: style and content, show/hide, location, click events functions.
Style and content
-
MenuVisible: Control right menu display/hide;
-
MenuPos: Control menu location;
-
MenuConfig: Controls whether the current TAB has the function of closing the left and right sides.
-
MenuMinWidth: the minimum width of the TAB DOM (used to calculate the position of the menu);
<template>
<div class="tabs-view-container">
<! -... -->
<ul v-show="menuVisible" :style="menuPos" class="contextmenu">
<li @click="refreshSelectedTab">Reset the current</li>
<li @click="closeSelectedTab(selectedTab)">Close the current</li>
<li @click="closeOthersTabs">Close the other</li>
<li :class="{'not-allowed': menuConfig.left}" @click="closeDirTabs('left')">Shut down the left side of the</li>
<li :class="{'not-allowed': menuConfig.right}" @click="closeDirTabs('right')">Shut down the right side of the</li>
<li @click="closeAllTabs">All closed</li>
</ul>
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
name: 'TabsView'.data() {
return {
selectedTab: {}, // Record the current TAB
menuVisible: false.menuPos: {},
menuConfig: {
left: true.right: true,},menuMinWidth: 100,}},// ...
methods: {
// ...
// Reset the current page
refreshSelectedTab() {},
// Close the current cached page
closeSelectedTab(tab) {},
// Close other cached pages
closeOthersTabs() {},
// Delete the left/right cached page
closeDirTabs() {},
// Close all cached pages
closeAllTabs(){}}}</script>
Copy the code
Show and hide
Implementation steps:
-
Add a right click event to the router-link tag:
@contextmenu.prevent.native="openMenu($event, tab)"
-
Implement open and close methods:
-
When TAB is clicked, openMenu triggers, opens the menu and determines the location of the menu.
-
Set watch to monitor menuVisible variable. When the menu is displayed, bind the event of closing the menu to body, and trigger the button to close the menu. Removes the listening event when the menu is hidden.
-
<template>
<div class="tabs-view-container">
<div class="tabs-view-wrapper">
<router-link
v-for="tab in visitedViews"
:key="tab.name"
:class="`tabs-view-item ${isActive(tab) ? 'active' : ''}`"
ref="tab"
:to="tab"
tag="span"
@contextmenu.prevent.native="openMenu($event, tab)"
>
{{tab.title}}
<i class="el-icon-close" @click.prevent.stop="closeSelectedTab(tab)" />
</router-link>
</div>
<! -... -->
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
export default {
// ...
watch: {
// ...
menuVisible(boolean) {
if (boolean) document.body.addEventListener('click'.this.closeMenu)
else document.body.removeEventListener('click'.this.closeMenu)
}
},
methods: {
// ...
// Open the cache TAB right-click menu
openMenu(e, tag) {
this.selectedTab = tab
},
// Close the cache TAB right-click menu
closeMenu() {
this.menuVisible = false}}},</script>
Copy the code
The implementation of openMenu function:
openMenu(e, tab) {
this.menuConfig = {
left: this.visitedViews[0].name === tab.name,
right: this.visitedViews[this.visitedViews.length - 1].name === tab.name
}
const offsetWidth = this.$el.offsetWidth
// Right-click menu left maximum value
const maxLeft = offsetWidth - this.menuMinWidth
const left = e.clientX - 200
const top = e.offsetY
this.menuVisible = true
this.menuPos = {
left: `${left > maxLeft ? maxLeft : left}px`.top: `${top}px`
}
this.selectedTab = tab
}
Copy the code
This function does three things:
-
Determine menu functions (whether to close left/right)
When the name of the item that triggers the right button event is equal to the name of the first item of visitedViews, it means that the trigger item is the first item, and there is no need to “close the left side” function (there is no page to close on the left side);
If the name of the item that triggers the right click event is equal to the name of the last item on the visitedViews, it indicates that the triggered item is the last item, and the function of “close the right side” is not needed (there is no page to close on the right side).
-
Control menu position
The important thing to keep in mind when controlling position here is that the calculation is influenced by style (the menu is positioned relative to who and how the calculation is done). I’m positioning it relative to div.tabs-view-container.
OffsetWidth: div. Tabs -view-container width.
MaxLeft: Sets the maximum value of left in the menu.
E. clientx: horizontal coordinate to the left of the mouse pointer from the client when the event occurs.
MenuMinWidth: The minimum DOM width of the menu (default: 100).
It should be easy to understand the calculation logic for menu positions
-
Open the menu
Set the variable menuVisible to true.
To test this, the menu now opens normally.
Now start to improve the menu function ~
Reset the current
The realization idea is: the current page -> enter the transfer page -> call route replace method, so as to achieve page refresh.
Create a Transfer page: transfer. vue:
<script>
export default {
created() {
const { params, query } = this.$route
const { path } = params
this.$router.replace({
path: '/' + path,
query
})
},
render() {
return (<div></div>)
}
}
</script>
Copy the code
Adding a route:
{
path: '/transfer/:path(.*)'.name: 'transfer'.component: Transfer
}
Copy the code
Modify the TabsView component:
- Add the reset page function
reloadPage
. - Modify the
toUpdateView
, replacing the previous temporary jump scheme with a callreloadPage
. - Modify the
refreshSelectedTab
: Deletes the cache entry and then invokesreloadPage
.
<script> methods: { ... mapActions('tagsView'['addView'.'delView'.'delCachedView']),
// ...
// Reload the target page
reloadPage(tab) {
this.$router.replace({
path: `/transfer${tab.fullPath}`.query: {
...tab.params,
}
})
},
// Update the view
toUpdateView(tab) {
const latestView = this.visitedViews.slice(-1) [0]
if (latestView) { // There are still visited pages
// If the name of the TAB to be deleted is the same as the name of the current route, switch the activated TAB to the last TAB. Otherwise, no additional operations are required.
if (tab.name === this.$route.name) this.$router.push(latestView.fullPath)
} else {
// If the last item to be deleted is the overview page, the overview page needs to be "reloaded" instead of jumping directly
if (tab.name === 'Dashboard') this.reloadPage(tab) // Reload
else this.$router.push('/common')}},// Reset the current page
refreshSelectedTab() {
let tab = this.selectedTab
this.delCachedView(tab)
this.reloadPage(tab)
},
// ...
}
</script>
Copy the code
Close the current
Call the base implementation directly -> Delete cached Page method (closeSelectedTab).
Close the other
Analyze two scenarios:
- Trigger item is current page: close other pages directly.
- If the trigger item is not the current page, you need to switch to the page corresponding to the trigger item after closing other pages.
Add two functions to the Mutations object:
DEL_OTHERS_VISITED_VIEWS
: Filters the routing information of trigger items.DEL_OTHERS_CACHED_VIEWS
: Filters trigger itemsname
.
const mutations = {
// ...
// Delete others
DEL_OTHERS_VISITED_VIEWS(state, view) {
state.visitedViews = state.visitedViews.filter(v= > v.name === view.name)
},
DEL_OTHERS_CACHED_VIEWS(state, view) {
state.cachedViews = state.cachedViews.filter(name= > name === view.name)
},
}
Copy the code
Add three functions to the Actions object:
delOthersViews
Distribute:delOthersVisitedViews
anddelOthersCachedViews
.delOthersVisitedViews
Submitted:DEL_OTHERS_VISITED_VIEWS
.delOthersCachedViews
Submitted:DEL_OTHERS_CACHED_VIEWS
.
const actions = {
// ...
// Delete others
delOthersViews({ dispatch }, view) {
dispatch('delOthersVisitedViews', view)
dispatch('delOthersCachedViews', view)
},
delOthersVisitedViews({ commit }, view) {
commit('DEL_OTHERS_VISITED_VIEWS', view)
},
delOthersCachedViews({ commit }, view) {
commit('DEL_OTHERS_CACHED_VIEWS', view)
},
}
Copy the code
Modify the closeOthersTags method of TabsView:
<script> methods: { ... mapActions('cache'['addView'.'delView'.'delCachedView'.'delOthersViews']),
// ...
// Close other cached pages
closeOthersTabs() {
let tab = this.selectedTab
this.delOthersViews(tab)
if (this.$route.name ! == tab.name)this.$router.push(tab.fullPath)
},
}
</script>
Copy the code
According to the above two cases of testing, no problem let’s continue (each step of testing is very important, to ensure that every small function is no problem, there are problems can be located in time) ~
Close all
The “Turn Off All” function is as simple as clearing visitedViews and cachedViews, and then reloading the overview page.
Add two functions to the Mutations object:
DEL_ALL_VISITED_VIEWS
: Clears visited pages.DEL_ALL_CACHED_VIEWS
: Clears cached pagesname
.
const mutations = {
// ...
// Delete all
DEL_ALL_VISITED_VIEWS(state) {
state.visitedViews = []
},
DEL_ALL_CACHED_VIEWS(state) {
state.cachedViews = []
},
}
Copy the code
Add three functions to the Actions object:
delAllViews
Distribute:delAllVisitedViews
anddelAllCachedViews
.delAllVisitedViews
Submitted:DEL_ALL_VISITED_VIEWS
.delAllCachedViews
Submitted:DEL_ALL_CACHED_VIEWS
.
const actions = {
// ...
// Delete all
delAllViews({ dispatch }, view) {
dispatch('delAllVisitedViews', view)
dispatch('delAllCachedViews', view)
},
delAllVisitedViews({ commit }) {
commit('DEL_ALL_VISITED_VIEWS')},delAllCachedViews({ commit }) {
commit('DEL_ALL_CACHED_VIEWS')}},Copy the code
Modify closeAllTabs in TabsView:
<script> methods: { ... mapActions('tagsView'['addView'.'delView'.'delCachedView'.'delOthersViews'.'delAllViews']),
// ...
// Close all cached pages
closeAllTags() {
this.delAllViews()
this.reloadPage({ fullPath: '/common' })
},
}
</script>
Copy the code
Continue testing ~
Everything is fine ~
Close left/close right
Analysis:
To close left/right, the key is to get the index of the current item, and then work with the array.
- Close left: through the array
slice
Method, intercept fromindex
To the element at the end of the array, and then reassign. - Close right side: Through array
splice
Method to delete fromindex
Element to the end of the array.
Add two functions to the Mutations object:
DEL_DIR_VISITED_VIEWS
: Deletes the accessed page on the left or right of the trigger item.DEL_DIR_CACHED_VIEWS
: Deletes the cached page to the left or right of the trigger itemname
.
const mutations = {
// ...
// Delete left/right side
DEL_DIR_VISITED_VIEWS(state, { view, dir }) {
let visitedViews = [...state.visitedViews]
for (const [i, v] of visitedViews.entries()) {
if (v.name === view.name) {
if (dir === 'right') {
let len = visitedViews.length - (i + 1) // There are len terms on the right-hand side
visitedViews.splice(i + 1, len)
} else visitedViews = visitedViews.slice(i)
break
}
}
state.visitedViews = visitedViews
},
DEL_DIR_CACHED_VIEWS(state, { view, dir }) {
const index = state.cachedViews.indexOf(view.name)
let cachedViews = [...state.cachedViews]
if (index > -1) {
if (dir === 'right') {
let len = cachedViews.length - (index + 1) // There are len terms on the right-hand side
cachedViews.splice(index + 1, len)
} else cachedViews = cachedViews.slice(index)
}
state.cachedViews = cachedViews
},
}
Copy the code
Add three functions to the Actions object:
delDirViews
Distribute:delDirVisitedViews
anddelDirCachedViews
.delDirVisitedViews
Submitted:DEL_DIR_VISITED_VIEWS
.delDirCachedViews
Submitted:DEL_DIR_CACHED_VIEWS
.
const actions = {
// ...
// Delete left/right side
delDirViews({ dispatch }, { view, dir }) {
dispatch('delDirVisitedViews', { view, dir })
dispatch('delDirCachedViews', { view, dir })
},
delDirVisitedViews({ commit }, { view, dir }) {
commit('DEL_DIR_VISITED_VIEWS', { view, dir })
},
delDirCachedViews({ commit }, { view, dir }) {
commit('DEL_DIR_CACHED_VIEWS', { view, dir })
},
}
Copy the code
Modify the closeDirTabs method of TabsView:
-
Check whether there are “left and right sides” that can be deleted. If there are “left and right sides” that can be deleted, execute the deletion function; otherwise, return directly.
-
Check whether the page corresponding to the current route is included in the deletion item (perform a loop to match the name of the current route). If yes, jump to the page corresponding to the last item.
<script> methods: { ... mapActions('tagsView'['addView'.'delView'.'delCachedView'.'delOthersViews'.'delAllViews'.'delDirViews']),
// ...
// Delete the left/right cached page
closeDirTabs(dir) {
if (this.menuConfig[dir]) return
let tab = this.selectedTab
this.delDirViews({
view: tab,
dir: dir
})
let needUpdateVisited = true
for (const [, v] of this.visitedViews.entries()) {
if (this.$route.name === v.name) {
needUpdateVisited = false
break}}if (needUpdateVisited) this.$router.push(this.visitedViews.slice(-1) [0].fullPath)
},
}
</script>
Copy the code
The test ~
“Small” is done
At this point, the entire cache component’s functionality is complete, but there are two areas that need to be optimized:
- Page switch animation: the current page switch is too stiff, need to add transition animation.
- TAB bar scrolling: Line breaks can occur when many pages are open, so we need to add a scrollable “box” to the outer layer to prevent style confusion.
Page transition animation
Remember the original AppMain component, which we modified slightly:
Add the transition tag and specify name as fade and mode as out-of-in (the current element transitions first and the new element transitions after completion).
<template>
<section class="app-main">
<div class="app-main-content">
<transition name="fade" mode="out-in">
<keep-alive :include="cachedViews">
<router-view :key="key"/>
</keep-alive>
</transition>
</div>
</section>
</template>
Copy the code
Create a new style file: transition.scss and add a transition style:
/* global transition css */
/* fade */
.fade-leave-active..fade-enter-active {
transition: all .3s;
}
.fade-enter {
opacity: 0;
transform: translateX(-30px);
}
.fade-leave-to {
opacity: 0;
transform: translateX(30px);
}
Copy the code
Add transition.scss to the entry file main.js:
import '@/style/transition'
Copy the code
Implement the scroll
Why have a separate menu for scrolling?
I came across the el-Scrollbar component (provided by Element-UI) today, and although it is not documented, it can be used directly, so I tested it here (mainly the style of the scrollbar)
Modify TabsView:
<template>
<div class="tabs-view-container">
<el-scrollbar class="tabs-view-scroll">
<div class="tabs-view-wrapper">
<router-link
v-for="tab in visitedViews"
:key="tab.name"
:class="`tabs-view-item ${isActive(tab) ? 'active' : ''}`"
ref="tab"
:to="tab"
tag="span"
@contextmenu.prevent.native="openMenu($event, tab)"
>
{{tab.title}}
<i class="el-icon-close" @click.prevent.stop="closeSelectedTab(tab)" />
</router-link>
</div>
</el-scrollbar>
<! -... -->
</div>
</template>
Copy the code
Effect:
Awesome 💯 (thanks to the Element team)!
The big one is done!
conclusion
In this paper, the technology stack mainly involves VUE, VUex, VUE-Router, Keep-alive, transition, element-UI. In the implementation process of components, it is also a review of some of their basic knowledge and usage. Of course, sometimes there are more complex business scenarios, but I think most routes can be configured with different parameters and processed accordingly (EMMM… I haven’t come across any special cases yet, so please discuss with me if you have.
Also, talk a little bit about why I wanted to write this component.
Recently, I was working on a refactoring project. I saw some components encapsulated by my former colleague, and gained something. Therefore, I rewrote some components based on my own ideas and planned to record the process of thinking. The inadequacy also please advise ~
Finally, I wish you a happy weekend, by the way to 👍 (praise) it ~