How should vue do permission management? What to do if you control to button-level permissions
1. What is
Permissions are permissions to access specific resources. The so-called permission control is to ensure that users can only access the assigned resources.
The front-end permission is ultimately the right to initiate the request. The initiation of the request may be triggered in the following two forms
- page load trigger
- Button click on page triggers
In general, all requests are initiated from front-end routes or views
Therefore, we can start from these two aspects to control the source of triggering permissions. The ultimate goal to be achieved is:
- In terms of routing, after logging in, users can only see the navigation menu they have access to, and can only access the routing addresses they have access to, otherwise they will jump to the 4xx prompt page
- In terms of view, users can only see the content they have permission to browse and the controls they have permission to operate
- Finally, add request control as the last line of defense. The routing may be misconfigured, and the button may forget to add permissions. At this time, request control can be used to make a bottom line, and unauthorized requests will be intercepted at the front end.
2. How to do it
Front-end permission control can be divided into four aspects:
- Interface permissions
- button permissions
- menu permissions
- Routing permissions
Interface permissions
At present, the interface permission is generally verified in the form of jwt. If it does not pass, it generally returns 401 and jumps to the login page to log in again.
After logging in, get the token, save the token, intercept it through the axios request interceptor, and carry the token in the header of each request
copyaxios.interceptors.request.use(config => { config.headers['token'] = cookie.get('token') return config }) axios.interceptors.response.use(res=>{},{response}=>{ if (response.data.code === 40099 || response.data.code === 40098) { //token expired or wrong router.push('/login') } })
Routing permission control
Option One
The initialization is to mount all the routes, and mark the corresponding permission information on the routes, and verify before each route jump.
copyconst routerMap = [ { path: '/permission', component: Layout, redirect: '/permission/index', alwaysShow: true, // will always show the root menu meta: { title: 'permission', icon: 'lock', roles: ['admin', 'editor'] // you can set roles in root nav }, children: [{ path: 'page', component: () => import('@/views/permission/page'), name: 'pagePermission', meta: { title: 'pagePermission', roles: ['admin'] // or you can only set roles in sub nav } }, { path: 'directive', component: () => import('@/views/permission/directive'), name: 'directivePermission', meta: { title: 'directivePermission' // if do not set roles, means: this page does not require permission } }] }]
There are four disadvantages to this approach:
- Load all routes. If there are many routes, and the user does not have permission to access all routes, it will affect the performance.
- In the global routing guard, permission judgment is required for each routing jump.
- The menu information is written on the front end. To change the display text or permission information, you need to recompile
- The menu is coupled with the route. When defining the route, the menu display title, icon and other information are added, and the route is not necessarily displayed as a menu, but more fields are added for identification.
Option II
When initializing, mount routes that do not require permission control, such as login pages, 404 and other error pages. If the user forcibly accesses through the URL, it will go directly to 404, which is equivalent to controlling from the source
After logging in, obtain the user's permission information, then filter the routes that have permission to access, and call addRoutes in the global routing guard to add routes
copyimport router from './router' import store from './store' import { Message } from 'element-ui' import NProgress from 'nprogress' // progress bar import 'nprogress/nprogress.css'// progress bar style import { getToken } from '@/utils/auth' // getToken from cookie NProgress.configure({ showSpinner: false })// NProgress Configuration // permission judge function function hasPermission(roles, permissionRoles) { if (roles.indexOf('admin') >= 0) return true // admin permission passed directly if (!permissionRoles) return true return roles.some(role => permissionRoles.indexOf(role) >= 0) } const whiteList = ['/login', '/authredirect']// no redirect whitelist router.beforeEach((to, from, next) => { NProgress.start() // start progress bar if (getToken()) { // determine if there has token /* has token*/ if (to.path === '/login') { next({ path: '/' }) NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it } else { if (store.getters.roles.length === 0) { // Determine whether the current user has finished pulling user_info information store.dispatch('GetUserInfo').then(res => { // pull user_info const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop'] store.dispatch('GenerateRoutes', { roles }).then(() => { // Generate an accessible routing table based on roles permissions router.addRoutes(store.getters.addRouters) // Dynamically add accessible routing table next({ ...to, replace: true }) // hack method make sure addRoutes is done, set the replace: true so the navigation will not leave a history record }) }).catch((err) => { store.dispatch('FedLogOut').then(() => { Message.error(err || 'Verification failed, please login again') next({ path: '/' }) }) }) } else { // If there is no need to dynamically change permissions, you can directly next() delete the permission judgment below ↓ if (hasPermission(store.getters.roles, to.meta.roles)) { next()// } else { next({ path: '/401', replace: true, query: { noGoBack: true }}) } // Can be deleted ↑ } } } else { /* has no token*/ if (whiteList.indexOf(to.path) !== -1) { // In the login-free whitelist, enter directly next() } else { next('/login') // Otherwise all redirect to login page NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it } } }) router.afterEach(() => { NProgress.done() // finish progress bar })
For on-demand mounting, the routing needs to know the routing permissions of the user, that is, when the user logs in, it needs to know which routing permissions the current user has.
This method also has the following disadvantages:
- In the global routing guard, each routing jump must be judged
- The menu information is written on the front end. To change the display text or permission information, you need to recompile
- The menu is coupled with the route. When defining the route, the menu display title, icon and other information are added, and the route is not necessarily displayed as a menu, but more fields are added for identification.
menu permissions
Menu permissions can be understood as decoupling pages from reasons
Option One
The menu is separated from the route, and the menu is returned by the backend
Front-end defines routing information
copy{ name: "login", path: "/login", component: () => import("@/pages/Login.vue") }
The name field is not empty. It is necessary to associate this field with the menu returned by the back-end. The menu information returned by the back-end must have a field corresponding to the name, and the uniqueness check must be performed.
Judgment in the global routing guard
copyfunction hasPermission(router, accessMenu) { if (whiteList.indexOf(router.path) !== -1) { return true; } let menu = Util.getMenuByName(router.name, accessMenu); if (menu.name) { return true; } return false; } Router.beforeEach(async (to, from, next) => { if (getToken()) { let userInfo = store.state.user.userInfo; if (!userInfo.name) { try { await store.dispatch("GetUserInfo") await store.dispatch('updateAccessMenu') if (to.path === '/login') { next({ name: 'home_index' }) } else { //Util.toDefaultPage([...routers], to.name, router, next); next({ ...to, replace: true })//The menu permission update is completed, re-enter the current route } } catch (e) { if (whiteList.indexOf(to.path) !== -1) { // In the login-free whitelist, enter directly next() } else { next('/login') } } } else { if (to.path === '/login') { next({ name: 'home_index' }) } else { if (hasPermission(to, store.getters.accessMenu)) { Util.toDefaultPage(store.getters.accessMenu,to, routes, next); } else { next({ path: '/403',replace:true }) } } } } else { if (whiteList.indexOf(to.path) !== -1) { // In the login-free whitelist, enter directly next() } else { next('/login') } } let menu = Util.getMenuByName(to.name, store.getters.accessMenu); Util.title(menu.title); }); Router.afterEach((to) => { window.scrollTo(0, 0); });
Every time the route jumps, the authority must be judged. The judgment here is also very simple, because the name of the menu corresponds to the name of the route, and the menu returned by the backend is already filtered by the authority.
If the corresponding menu cannot be found according to the route name, it means that the user has permission to access
If there are many routes, you can mount only the routes that do not require permission control when the application is initialized. After obtaining the menu returned by the backend, according to the corresponding relationship between the menu and the route, filter out the accessible routes, and dynamically mount them through addRoutes
Disadvantages of this way:
- The menu needs to be in one-to-one correspondence with the routing. New functions have been added to the front end, and new menus need to be added through the menu management function. If the menu is configured incorrectly, the application will not be able to be used normally.
- In the global routing guard, each routing jump must be judged
Option II
Both the menu and the route are returned by the backend
Front-end unified definition of routing components
copyconst Home = () => import("../pages/Home.vue"); const UserInfo = () => import("../pages/UserInfo.vue"); export default { home: Home, userInfo: UserInfo };
The backend routing component returns the following format
copy[ { name: "home", path: "/", component: "home" }, { name: "home", path: "/userinfo", component: "userInfo" } ]
Before dynamically mounting the back-end return route through addRoutes, you need to process the data and replace the component field with a real component
If there are nested routes, when designing the back-end function, you should pay attention to adding the corresponding fields, and the front-end will also need to deal with the data when you get it.
There are also disadvantages to this approach:
- In the global routing guard, each routing jump must be judged
- The front and rear ends have higher requirements for cooperation
button permissions
Option One
Button permissions can also be judged with v-if
But if there are too many pages, each page must obtain the user permission role and the meta.btnPermissions in the routing table, and then make a judgment
No examples will be given in this way.
Option II
Judging button permissions through custom instructions
First configure routing
copy{ path: '/permission', component: Layout, name: 'permission test', meta: { btnPermissions: ['admin', 'supper', 'normal'] }, //Permissions required for the page children: [{ path: 'supper', component: _import('system/supper'), name: 'Permission test page', meta: { btnPermissions: ['admin', 'supper'] } //Permissions required for the page }, { path: 'normal', component: _import('system/normal'), name: 'Permission test page', meta: { btnPermissions: ['admin'] } //Permissions required for the page }] }
Custom authorization authentication instructions
copyimport Vue from 'vue' /**Permission Directive**/ const has = Vue.directive('has', { bind: function (el, binding, vnode) { // Get page button permissions let btnPermissionsArr = []; if(binding.value){ // If the command is passed by value, get the command parameters, and compare the command parameters with the button permissions of the current login person. btnPermissionsArr = Array.of(binding.value); }else{ // Otherwise, the parameters in the route are obtained, and the btnPermissionsArr of the route is compared with the button permissions of the current login person. btnPermissionsArr = vnode.context.$route.meta.btnPermissions; } if (!Vue.prototype.$_has(btnPermissionsArr)) { el.parentNode.removeChild(el); } } }); // Permission check method Vue.prototype.$_has = function (value) { let isExist = false; // Get user button permissions let btnPermissionsStr = sessionStorage.getItem("btnPermissions"); if (btnPermissionsStr == undefined || btnPermissionsStr == null) { return false; } if (value.indexOf(btnPermissionsStr) > -1) { isExist = true; } return isExist; }; export {has}
Only the v-has directive needs to be referenced in the button used
copy<el-button @click='editClick' type="primary" v-has>edit</el-button>
summary
Regarding how to choose which appropriate scheme for permissions, you can consider whether the routing and menu are separated according to the scheme items of your own project.
Permissions need to be combined with the front and back ends, the front end should be controlled as much as possible, and more need to be judged by the background
What are asynchronous components? What are the usage scenarios?
analyze
Because of the existence of asynchronous routing, we use asynchronous components less often, so it is still necessary to distinguish between the two.
experience
In large applications, we need to split the application into smaller chunks and load components when they are needed
copyimport { defineAsyncComponent } from 'vue' // defineAsyncComponent defines an asynchronous component, returning a wrapper component. The wrapper component decides what to render based on the state of the loader const AsyncComp = defineAsyncComponent(() => { // Load function returns Promise return new Promise((resolve, reject) => { // ...can load components from the server resolve(/* loaded component */) }) }) // Dynamic import of ES modules with the help of packaging tools const AsyncComp = defineAsyncComponent(() => import('./components/MyComponent.vue') )
Answer example
- In large applications, we need to split the application into smaller chunks and load components when they are needed.
- Not only can we lazily load components when routes are switched, but we can also continue to use asynchronous components in page components to achieve finer segmentation granularity.
- The easiest way to use asynchronous components is to directly specify a loader function for defineAsyncComponent, which can be quickly implemented in combination with the dynamic import function import of ES modules. We can even specify loadingComponent and errorComponent options to give the user a nice loading feedback. In addition, Vue3 can also use asynchronous components in combination with Suspense components.
- Asynchronous components are easily confused with lazy routing, but they are not actually the same thing. Asynchronous components cannot be used to define lazy loading routes, the vue framework handles it, and the routing component is loaded by vue-router. But it is possible to use async components in lazy loaded routing components
Front-end vue interview questions answered in detail
Why Vue components can only have one root element
There is no problem in vue3
copyVue.createApp({ components: { comp: { template: ` <div>root1</div> <div>root2</div> ` } } }).mount('#app')
- Components in vue2 can indeed only have one root, but components in vue3 can already have multiple root nodes.
- The reason for this is because vdom is a single-root tree structure, and the patch method starts traversing from the root node when traversing, and it requires only one root node. The component is also converted to a vdom
- The reason why multiple root nodes can be written in vue3 is because the concept of Fragment is introduced, which is an abstract node. If it is found that the component has multiple roots, a Fragment node is created, and multiple root nodes are used as its children. When patch ing in the future, if it is found to be a Fragment node, it will directly traverse the children to create or update
Vue routing hook function
The home page can control navigation jumps, beforeEach, afterEach, etc., which are generally used for page title modification. Some redirect functions that require a login to adjust the page.
- beforeEach mainly has 3 parameters to, from, next.
- to: The target route object that the route is about to enter.
- from: route the route that the current navigation is about to leave.
- next: function must call the method resolve hook. The execution effect depends on the call parameters of the next method. Can control the jump of web pages
Why Vue uses asynchronous rendering
Vue is a component-level update. If asynchronous update is not used, the current component will be re-rendered every time the data is updated. Therefore, for performance, Vue will update the view asynchronously after the current round of data update. Core idea nextTick

Source code related
dep.notify() notifies the watcher to update, subs[i].update calls watcher's update in turn, queueWatcher de-reloads the watcher into the queue, nextTick ( flushSchedulerQueue ) refreshes the watcher queue in the next tick (asynchronously)
copyupdate () { /* istanbul ignore else */ if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { queueWatcher(this); // When the data changes, the watcher will be put into a queue and updated in batches } } export function queueWatcher (watcher: Watcher) { const id = watcher.id // will filter the same watcher if (has[id] == null) { has[id] = true if (!flushing) { queue.push(watcher) } else { let i = queue.length - 1 while (i > index && queue[i].id > watcher.id) { i-- } queue.splice(i + 1, 0, watcher) } // queue the flush if (!waiting) { waiting = true if (process.env.NODE_ENV !== 'production' && !config.async) { flushSchedulerQueue() return } nextTick(flushSchedulerQueue) // Call the nextTick method to update in batches } } }
How to cache the current component? How to update after cache
The cache component uses the keep-alive component, which is a very common and useful optimization method. The keep-alive in vue3 has a relatively large update, and there are many points that can be said.
ideas
- keep-alive for caching, its role and usage
- Usage details such as cache assignment/exclusion, combining router s and transition s
- After the component is cached, the update can use activated or beforeRouteEnter
- Principles
Answer example
- In development, the cache component uses the keep-alive component. Keep-alive is a built-in component of vue. When keep-alive wraps the dynamic component component, it will cache the inactive component instances instead of destroying them, so that the state is kept in the component switching process. In memory, preventing repeated rendering of the DOM
copy<keep-alive> <component :is="view"></component> </keep-alive>
- Combining the attributes include and exclude can explicitly specify which components to cache or exclude specific components from being cached. In vue3, the combination of vue-router has changed greatly. Before, keep-alive wrapped router-view, and now it is necessary to wrap keep-alive with router-view in turn.
copy<router-view v-slot="{ Component }"> <keep-alive> <component :is="Component"></component> </keep-alive> </router-view>
- If you want to get data after caching, there are two solutions:
- beforeRouteEnter: In projects with vue-router, beforeRouteEnter is executed every time the route is entered
copybeforeRouteEnter(to, from, next){ next(vm=>{ console.log(vm) // Every time you enter the route execution vm.getData() // retrieve data }) },
- actived: actived hooks are executed when the components in the keep-alive cache are activated
copyactivated(){ this.getData() // retrieve data },
- keep-alive is a general component. It defines a map internally to cache the created component instance. The rendering function it returns will look for the vnode of the component corresponding to the embedded component component. If the component exists in the map, it will return directly. it. Since the is property of the component is a reactive data, as long as it changes, the render function of keep-alive will be re-executed
What is the principle of two-way binding?
We all know that Vue is a framework for two-way data binding, and two-way binding consists of three important parts
- Data layer (Model): application data and business logic
- View layer (View): the display effect of the application, various UI components
- Business logic layer (ViewModel): The core of framework encapsulation, which is responsible for associating data with views
The above layered architecture scheme can be called in a professional term: the core function of the control layer in MVVM is "two-way data binding". Naturally, we only need to understand what it is, and we can further understand the principle of data binding
Understanding ViewModel s
Its main responsibilities are:
- Update view after data changes
- Update data after view changes
Of course, it also has two main parts
- Listener (Observer): monitor the properties of all data
- Compiler: Scan and parse the instructions of each element node, replace data according to the instruction template, and bind the corresponding update function
What is the vue-router routing hook function and what is the execution order
The execution process of the routing hook, the types of hook functions are: global guard, routing guard, component guard
The complete navigation analysis process:
- Navigation is triggered.
- Call the beforeRouteLeave guard on the deactivated component.
- Call the global beforeEach guard.
- Call the beforeRouteUpdate guard (2.2+) in the reused component.
- Call beforeEnter in the routing configuration.
- Parse asynchronous routing components.
- Call beforeRouteEnter on the activated component.
- Call the global beforeResolve guard (2.5+).
- Navigation is confirmed.
- Call the global afterEach hook.
- Trigger a DOM update.
- Call the callback function passed to next in the beforeRouteEnter guard, and the created component instance will be passed in as a parameter of the callback function.
The benefits of writing name attributes in components
The specific name of the component can be identified to facilitate debugging and find the corresponding properties
copy// Source code location src/core/global-api/extend.js // enable recursive self-lookup if (name) { Sub.options.components[name] = Sub // Document yourself recurse yourself in components -> jsx }
nextTick usage scenarios and principles
The callbacks in nextTick are deferred callbacks that execute after the end of the next DOM update loop. Use this method immediately after modifying the data to get the updated DOM. The main idea is to call the asynchronous method in a micro-task-first way to execute the method of nextTick packaging
The relevant code is as follows
copylet callbacks = []; let pending = false; function flushCallbacks() { pending = false; //revert the flag to false // Execute callbacks in sequence for (let i = 0; i < callbacks.length; i++) { callbacks[i](); } } let timerFunc; //Defining async methods with graceful degradation if (typeof Promise !== "undefined") { // if promise s are supported const p = Promise.resolve(); timerFunc = () => { p.then(flushCallbacks); }; } else if (typeof MutationObserver !== "undefined") { // MutationObserver mainly monitors dom changes and is also an asynchronous method let counter = 1; const observer = new MutationObserver(flushCallbacks); const textNode = document.createTextNode(String(counter)); observer.observe(textNode, { characterData: true, }); timerFunc = () => { counter = (counter + 1) % 2; textNode.data = String(counter); }; } else if (typeof setImmediate !== "undefined") { // If none of the above is supported, judge setImmediate timerFunc = () => { setImmediate(flushCallbacks); }; } else { // The final downgrade uses setTimeout timerFunc = () => { setTimeout(flushCallbacks, 0); }; } export function nextTick(cb) { // In addition to the rendering watcher, the nextTick manually called by the user is collected into an array callbacks.push(cb); if (!pending) { // If nextTick is called multiple times, only one asynchronous operation will be performed, and the flag will be changed to false after the asynchronous queue is emptied. pending = true; timerFunc(); } }
What design patterns are used in vue
1. Factory Pattern - Create an instance by passing in parameters
The virtual DOM returns the Vnode of the base label and the Vnode of the component according to the different parameters
2. Singleton pattern - the entire program has one and only one instance
The plugin registration method install of vuex and vue-router judges that if there is an instance in the system, it will return directly
3. Publish-subscribe mode (vue event mechanism)
4. Observer Pattern (Reactive Data Principle)
5. Decoration mode: (Usage of @ decorator)
6. Strategy pattern Strategy pattern means that an object has a certain behavior, but in different scenarios, the behavior has different implementation schemes - such as the merging strategy of options
...other modes are welcome to add
How to extend a component in Vue
This question is a practical question. It examines everyone's proficiency in using vue's commonly used APIs. When answering the question, not only should these solutions be listed, but it is best to tell their similarities and differences.
Question answering ideas:
- Listed by logical expansion and content expansion
- Logic extensions are: mixins, extends, composition api
- Content extensions have slots;
- Name their usage, scene differences, and problems, respectively.
- As an extension, we can also talk about the changes brought by the newly introduced composition api in vue3
Sample answer:
- Common component extension methods are: mixins, slots, extends, etc.
- mixins are a very flexible way to distribute reusable functionality in Vue components. Mixin objects can contain arbitrary component options. When a component uses a mixin, all options of the mixin will be mixed into the options of the component itself
copy// Reuse code: it is a configuration object, the options are the same as in the component const mymixin = { methods: { dosomething(){} } } // Global mixins: pass in the mixin object Vue.mixin(mymixin) // Local mixin: Set the array item to the mixins option, which only affects the current component const Comp = { mixins: [mymixin] }
- Slots are mainly used for content distribution in vue components, and can also be used for component expansion
Child component
copy<div> <slot>This content will be replaced by the content passed by the parent component</slot> </div>
Parent component Parent
copy<div> <Child>Content from parent component</Child> </div>
You can use named slots if you want to distribute exactly to different locations, and scoped slots if you want to use data from subcomponents
- There is also a less commonly used option extends in the component options, which can also be used to extend the component.
copy// extension object const myextends = { methods: { dosomething(){} } } // Component extension: set the array item to the extends option, which only affects the current component // Unlike mixins, it can only extend a single object // In addition, if there is a conflict with the mixin, this option has a higher priority and takes precedence. const Comp = { extends: myextends }
- The mixed data and methods cannot clearly determine the source and may cause naming conflicts with the variables in the current component. The composition api introduced in vue3 can solve these problems very well. Using the independent responsive module can easily write independent logic and provide Responsive data is then combined in setup options to enhance code readability and maintainability. E.g
copy// Multiplexing logic 1 function useXX() {} // Multiplexing logic 2 function useYY() {} // logical combination const Comp = { setup() { const {xx} = useXX() const {yy} = useYY() return {xx, yy} } }
What is a recursive component? Give an example to illustrate?
analyze
We use less recursive components, but they are used in components such as Tree and Menu.
experience
A component refers to itself by the component name, in this case a recursive component
copy<template> <li> <div> {{ model.name }}</div> <ul v-show="isOpen" v-if="isFolder"> <!-- Note here: the component renders itself recursively --> <TreeItem class="item" v-for="model in model.children" :model="model"> </TreeItem> </ul> </li> <script> export default { name: 'TreeItem', // ... } </script>
Answer example
- If a component refers to itself by component name, this is a recursive component.
- In actual development of components like Tree and Menu, their nodes often contain child nodes, and the structure of child nodes and parent nodes are often the same. The data of this type of component is often a tree structure, which is a typical scenario for using recursive components.
- When using recursive components, since we have not and cannot import itself inside the component, we set the component name property to find the component definition. If SFC is used, it can be inferred from the SFC file name. There is usually a recursive end condition inside the component, such as a judgment like model.children.
- Looking at the generated rendering function, we can see that a Boolean value will be passed to resolveComponent when the recursive component is searched, so that the actual component obtained is the current component itself
principle
In the compilation result of the recursive component, an identifier is passed when the component is obtained _resolveComponent("Comp", true)
copyconst _component_Comp = _resolveComponent("Comp", true)
is passing maybeSelfReference
copyexport function resolveComponent( name: string, maybeSelfReference?: boolean ): ConcreteComponent | string { return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name }
The final return in resolveAsset is the component itself:
copyif (!res && maybeSelfReference) { // fallback to implicit self-reference return Component }
Talk about the idea of Vue and React componentization
- 1. When we develop each page, there will be many repeated functions, such as xxxx in element. Such purely non-page UI has become our commonly used UI components, and the initial front-end components only refer to UI components
- 2. As business logic becomes more and more, we want our components to handle a lot of things. This is what we often call componentization. This component is not a UI component, but a business component that includes specific business.
- 3. This development idea is divide and conquer. The effect of reducing development difficulty and maintenance cost to the greatest extent. And multiple people can collaborate, each person writes different components, and finally forms a page like building blocks
Vue.extend function and principle
Official explanation: Vue.extend uses the base Vue constructor to create a "subclass". The parameter is an object containing options for the component.
In fact, it is a subclass constructor. It is the core api implementation idea of Vue components. The idea of using prototypal inheritance is to return the subclass of Vue and use mergeOptions to merge the options of the incoming component and the options of the parent class.
The relevant code is as follows
copyexport default function initExtend(Vue) { let cid = 0; //Unique ID of the component // Create a subclass to inherit the Vue parent class to facilitate property extension Vue.extend = function (extendOptions) { // Create the constructor of the subclass and call the initialization method const Sub = function VueComponent(options) { this._init(options); //Call the Vue initialization method }; Sub.cid = cid++; Sub.prototype = Object.create(this.prototype); // Subclass prototype points to parent class Sub.prototype.constructor = Sub; //constructor points to itself Sub.options = mergeOptions(this.options, extendOptions); //Merge your own options with the parent's options return Sub; }; }
What are the ways to communicate between Vue components?
Communication between Vue components is one of the common knowledge points in interviews. This question is a bit similar to an open question. Of course, the more methods you answer, the more points you will get, indicating that you are more proficient in Vue. Communication between Vue components only refers to the following three types of communication: parent-child component communication, intergenerational component communication, and sibling component communication. Below we introduce each communication method and explain which type of inter-component communication this method is applicable to.
(1) props / $emit is suitable for parent-child component communication. This method is the basis of Vue components. I believe most students have heard of it, so I will not introduce it here.
(2) ref and $parent / $children apply to parent-child component communication
- ref: If used on a normal DOM element, the reference points to the DOM element; if used on a child component, the reference points to the component instance
- $parent / $children: access parent/child instances
(3)EventBus ($emit / $on) is suitable for parent-child, intergenerational, and sibling components. This method uses an empty Vue instance as the central event bus (event center) to trigger events and listen for events, thereby enabling Communication between any component, including parent-child, intergenerational, and sibling components.
(4) $attrs/$listeners are suitable for intergenerational component communication
- $attrs: Contains attribute bindings (except class and style) that are not recognized (and acquired) by prop s in the parent scope. When a component does not declare any prop s, all parent-scoped bindings (except class and style ) are included here, and can be passed to inner components via v-bind="$attrs". Usually used with the inheritAttrs option.
- $listeners: Contains the v-on event listeners in the parent scope (without the .native decorator). It can be passed to internal components via v-on="$listeners"
(5) provide / inject is suitable for intergenerational component communication. The ancestor component provides the variable through the provider, and then injects the variable through the inject in the descendant component. The provide/inject API mainly solves the communication problem between cross-level components, but its usage scenarios are mainly that subcomponents obtain the state of the parent component, and a relationship between active provision and dependency injection is established between the cross-level components. (6) Vuex is suitable for parent-child, intergenerational, and sibling component communication Vuex is a state management mode specially developed for Vue.js applications. The core of every Vuex application is the store. A "store" is basically a container that holds most of the state in your application.
- Vuex's state storage is reactive. When a Vue component reads state from the store, if the state in the store changes, the corresponding component will be efficiently updated accordingly.
- The only way to change state in the store is to explicitly commit mutations. This allows us to easily track each state change.
watch principle
watch essentially creates a watcher for each listening property setter, and calls the incoming callback function when the monitored property is updated. Common configuration options are deep and immediate, the corresponding principles are as follows
- deep: deeply monitor the object, create a watcher for each property of the object, so as to ensure that the incoming callback function is triggered when each property of the object is updated. The main reason is that the object is a reference type, and the update of a single property does not trigger the object setter, so the introduction of deep can solve the problem of listening to the object well. At the same time, a judgment mechanism will also be introduced to ensure that the callback function is only triggered once when multiple attributes are updated to avoid performance waste.
- immediate: The callback function is called directly at initialization, the same effect can be achieved by manually calling the callback function in the created phase
The difference between vue and react
=> Same point:
copy1. Data-driven pages, providing responsive view components 2. have virtual DOM,Componentized development, through props Parameters are used to transfer data between components between parent and child, all implemented webComponents specification 3. One-way data flow, all support server rendering SSR 4. have support native Methods, react Have React native, vue Have wexx
=> Difference:
copy1.Data binding: Vue Two-way data binding is implemented, react Data flow is one-way 2.Data rendering: large-scale data rendering, react faster 3.scenes to be used: React Cooperate Redux The architecture is suitable for large-scale multi-person collaborative complex projects, Vue Suitable for small projects 4.Development style: react Recommended practice jsx + inline style Bundle html and css are written in js span vue is to adopt webpack + vue-loader single file component format, html, js, css the same file
How to go from real DOM to virtual DOM
Involving the principle of template compilation in Vue, the main process:
- Convert templates into ast trees, ast uses objects to describe real JS syntax (converts real DOM into virtual DOM)
- optimization tree
- Generate code from the ast tree
Use VNode to describe a DOM structure
A virtual node is an object that describes a real DOM element. First convert the template (real DOM) into ast, the ast tree generates a render function through codegen, and the _c method in the render function converts it into a virtual dom