2023 Front-end Interview Series--Vue Chapter

Summary of common Vue interview questions

MVVM model?

MVVM, the Model-View-ViewModelabbreviation of MVVM, is essentially MVCan upgraded version of the model. Among them Model, represents the data model, Viewrepresents the page you see, ViewModeland is the bridge between and. The data will be bound to Viewthe layer and automatically render the data into the page. When the view changes, the layer will be notified to update the data. Previously the view was updated via actions, now it is .ModelViewModelViewModelDOM数据驱动视图

Vue life cycle

The life cycle of Vue can be divided into 8 stages: before and after creation, before and after mounting, before and after updating, before and after destruction, as well as the life cycle of some special scenarios. Vue 3 also adds three new scenes for debugging and server-side rendering.

Lifecycle hooks in Vue 2 Lifecycle options for Vue 3 options API Life cycle hooks in Vue 3 composition API describe
beforeCreate beforeCreate setup() Before creation, the data at this time dataand methodsare not initialized yet.
created created setup() After creation, datathere is a value in it, it has not been mounted yet, and some Ajaxrequests can be made.
beforeMount beforeMount onBeforeMount Before mounting, the virtual will be found DOMand compiled intoRender
mounted mounted onMounted After mounting, DOMit is created and can be used to get access to data and DOMelements
beforeUpdate beforeUpdate onBeforeUpdate Before update, can be used to obtain various statuses before update
updated updated onUpdated After updating, all statuses are up to date
beforeDestroy beforeUnmount onBeforeUnmount Can be used to cancel some timers or subscriptions before destruction
destroyed unmounted onUnmounted After destruction, it can be used for the cancellation of some timers or subscriptions
activated activated onActivated keep-aliveWhen a cached component is activated
deactivated deactivated onDeactivated keep-aliveWhen a cached component is deactivated
errorCaptured errorCaptured onErrorCaptured Called when catching an error from a descendant component
renderTracked onRenderTracked Debug hook, called when reactive dependencies are collected
renderTriggered onRenderTriggered Debug hook, called when reactive dependency is triggered
serverPrefetch onServerPrefetch Called before the component instance is rendered on the server

** Regarding the life cycle in Vue 3, it is recommended to read the official documentation <img src="https://link.juejin.cn/?target=https%3A%2F%2Fcn.vuejs.org%2Fapi%2Fcomposition-api-lifecycle .html “https://cn.vuejs.org/api/composition-api-lifecycle.html”)[Optional API: Lifecycle Options – Official Documentation](https://link.juejin.cn/?target= https%3A%2F%2Fcn.vuejs.org%2Fapi%2Foptions-lifecycle.html “https://cn.vuejs.org/api/options-lifecycle.html”” style=”margin: auto” />

Life cycle of parent-child components: * 加载渲染阶段: parent beforeCreate -> parent created -> parent beforeMount -> child beforeCreate -> child created -> child beforeMount -> child mounted -> parent mounted

  • 更新阶段: Father beforeUpdate -> Child beforeUpdate -> Child updated -> Father updated
  • 销毁阶段: father beforeDestroy -> child beforeDestroy -> child destroyed -> father destroyed

Vue.$nextTick

Execute the delayed callback after the next DOM update cycle. Use this method immediately after modifying the data to get the updated DOM.

nextTickIt is a global API provided by Vue. Due to Vue's asynchronous update strategy, our data modifications will not be directly reflected in the DOM. At this time, if we want to obtain the updated DOM status immediately, we need to use this method.

Vue executes asynchronously when updating the DOM. When data changes, Vue will open an asynchronous update queue and buffer all data changes that occur in the same event loop. If the same watcheris triggered multiple times, it will only be pushed into the queue once. This deduplication during buffering is very important to avoid unnecessary calculations and DOM operations. nextTickThe method will add a callback function to the queue to ensure that the function is called only after the previous DOM operation is completed.

scenes to be used:

1. If you want to get the updated DOMstructure immediately after modifying the data, you can use Vue.nextTick()
2. Operate in createdthe life cycleDOM

What happens during the Vue instance mounting process?

The mounting process refers to app.mount()the process, which is an initialization process that does two things as a whole: 初始化and 建立更新机制.

Initialization will create component instances, initialize component states, and create various responsive data.

The step of establishing the update mechanism will immediately execute the component's update function, which will execute the component rendering function for the first time and convert to ; at the same time, executing the rendering function for the first time patchwill create a dependency between its internal responsive data and the component update function, which This will cause the corresponding update function to be executed when the data changes in the future.vnodedom

Vue template compilation principle

There is a unique compiler module in Vue called compiler. Its main function is to templatecompile user-written functions into executable renderfunctions in js. In Vue, the compiler will first templateparse, this step is called parse, after the end, a JS object is obtained, which is called 抽象语法树AST; then there is ASTa conversion process for deep processing, this step is called , and finally the JS code transformobtained previously is generated AST, that is, renderfunction.

Vue’s responsiveness principles

1. The data responsiveness in Vue 2 will be processed differently according to the data type. If it is an object, by Object.defineProperty(obj,key,descriptor)intercepting object attribute access, it will sense and react when the data is accessed or changed; if it is an array, it will extend its 7 change methods (push, pop, shift, unshift, splice, sort, reverse), so that these methods can perform additional update notifications to respond. Disadvantages: * Recursive traversal during initialization will cause performance loss; * The notification update process needs to maintain a large number of depinstances and watcherinstances, which takes up a lot of additional memory; * Adding or deleting object attributes cannot be intercepted, and it needs to pass Vue.setand deletesuch API to take effect; * These ES6newly generated data structures are not supported Map. 2. The mechanism proxy used in Vue 3 requires responsive data. It can support objects and arrays at the same time. Dynamic attribute additions and deletions can be intercepted. All new data structures are supported. Object nested attributes are recursive at runtime and are proxied only when used. There is no need to maintain a particularly large number of dependencies, and the performance has been greatly improved. Big progress.Set
ES6Proxy

Virtual DOM

1. Concept: Virtual DOM, as the name suggests, is a virtual DOM object. It is a JS object in itself, but it describes a view structure through different attributes. 2. Benefits of virtual DOM: (1) Performance improvement. There are limitations to directly operating DOM. There are many attributes on a real element. If you directly operate it, many additional attribute contents will be operated at the same time. This is not the case. necessary. If these operations are transferred to JS objects, it will be much simpler. In addition, operating the DOM is relatively expensive, and frequent DOM operations can easily cause page redrawing and reflow. If intermediate processing is performed through abstract VNode, the number of direct DOM operations can be effectively reduced, thereby reducing page redrawing and reflow. (2) It is convenient for cross-platform implementation. The same VNode node can be rendered into corresponding content on different platforms. For example: when rendered in the browser, it is a DOM element node, and when rendered in Native (iOS, Android), it becomes the corresponding control. Vue 3 allows developers to implement custom renderers based on VNode to facilitate rendering for different platforms. 3. Structure: There is no unified standard, generally including three items tag: , props, and children. tag:required. It's a label, or it can be a component, or a function. props: Optional. It's the properties and methods on this label. children: Optional. It is the content or child nodes of this tag. If it is a text node, it is a string; if it has child nodes, it is an array. In other words, if it is judged childrento be a string, it means it must be a text node, and this node must have no child elements. ### diff algorithm

1. Concept: diffThe algorithm is a comparison algorithm. By comparing the old virtual DOM and the new virtual DOM, we can find out which virtual node has changed. Find this virtual node and only update the real node corresponding to this virtual node. Instead of updating other nodes that have not changed, the real DOM can be accurately updated, thereby improving efficiency. 2. Comparison method: diffThe overall strategy of the algorithm is: 深度优先,同层比较. Comparisons will only be performed at the same level, and will not be compared across levels; during the comparison process, the loop will shrink from both sides to the middle.

  • tagFirst, determine whether the two nodes are the same. If they are different, delete the node and create a new node to replace it.
  • tagWhen they are the same, the attributes are replaced first, and then the sub-elements are compared, which are divided into the following situations: * When the old and new nodes have sub-elements, the double pointer method is used for comparison. Compare the old and new head and tail pointers, move the loop closer to the middle, call to patchVnoderepeat patchthe process according to the situation, call to createElemcreate a new node, find a keyconsistent VNodenode from the hash table, and then operate according to the situation. * If the new node has child elements and the old node has no child elements, just convert the virtual node of the child element into a real node and insert it. * If the new node has no child elements and the old node has child elements, the child elements will be cleared and set to the text content of the new node. * When both the old and new nodes have no child elements, that is, they are both text nodes, the text content will be compared directly, and if they are different, they will be updated.

What is the role of key in Vue?

keyThe main function is 为了更加高效的更新虚拟 DOM.

When Vue determines whether two nodes are the same, it mainly determines keythe sum of the two 元素类型tag. Therefore, if it is not set key, its value is undefined, and it may always be considered that these are two identical nodes, and only update operations can be performed, which will cause a large number of DOM update operations.

Why is the data in the component a function?

In new Vue(), it can be a function or an object, because there is only one root instance and no data pollution will occur.

In a component, data must be a function in order to prevent multiple component instance objects from sharing the same data and causing data pollution; in the form of a function, when initData is used as a factory function, a new data object will be returned.

How to communicate between components in Vue?

1. Parent-child component communication: The parent passes data to the child props, and the child transmits data to the parent by $emittriggering events; communication can also be done through the parent chain/child chain ( $parent/ $children); refcomponent instances can also be accessed; provide/ inject; $attrs/ $listeners. 2. Brother component communication: global event bus EventBus, Vuex. 3. Cross-level component communication: global event bus EventBus, Vuex, provide/ inject. ### What is the difference between v-show and v-if?

1. Different control methods. v-showBy adding css attributes to the element display: none, the element still exists; while v-ifcontrolling the display or hiding of the element is by adding or deleting the entire element.
2. The compilation process is different. v-ifThere is a partial compilation/uninstallation process for switching. During the switching process, the internal event listeners and subcomponents are properly destroyed and rebuilt; it is just a v-showsimple CSS-based switch.
3. Compilation conditions are different. v-ifIt is true conditional rendering. It will ensure that the event listeners and subcomponents within the conditional block are properly destroyed and rebuilt during the switching process. When the rendering condition is false, no operation will be performed until it is true.
4. The trigger life cycle is different. v-showWhen changing from false to true, the life cycle of the component will not be triggered; when v-ifchanging from false to true, the component's hooks will be triggered, and when beforeCreatechanging from true to false, the component's hooks will be triggered . 5. Performance consumption is different. There is a higher switching cost; there is a higher initial rendering cost.createdbeforeMountmountedbeforeDestorydestoryed
v-ifv-show

Usage scenario: If you need to switch very frequently, it v-showis better to use it, such as: accordion menu, tab page, etc.; if the conditions rarely change during runtime, it is v-ifbetter to use it, such as: after the user logs in, display according to different permissions Different content.

What is the difference between computed and watch?

  • computedCalculated properties rely on other properties to calculate values. Any change in any internal dependency will re-execute the function. Calculated properties have a cache. When the calculated properties are reused multiple times, the return value will be obtained from the cache. The calculated properties must have keywords return.
  • watchDetect changes in certain data to trigger the function. When the data is an object type, you need to use the deep listening deepattribute when the attribute value in the object changes, or you can use the immediate listening attribute when the page is first loaded immdiate.

Usage scenarios: Computed properties are generally used in template rendering. A certain value depends on other response objects or even calculated properties; listening properties are suitable for observing changes in a certain value to complete a complex business logic.

Why are v-if and v-for not recommended to be used together?

In Vue 2, v-forthe priority ratio v-ifis higher , which means that v-ifit will be run separately in each v-forloop. If the array to be traversed is large and the actual data to be displayed is very small, it will cause a huge waste of performance.

In Vue 3, it is exactly the opposite, v-ifwith a higher priority v-for, so v-ifwhen executed, the variable it calls does not exist yet, which will cause an exception.

There are usually two situations that lead to this:

  • To filter items in a list, for example: v-for = "user in users" v-if = "user.active". In this case, you can define a calculated property and let it return the filtered list.
  • To avoid rendering a list that should be hidden, v-for = "user in users" v-if = "showUsersFlag"e.g. In this case, you can v-ifmove it to the container element or wrap it with a layer outside template.

set method in Vue 2?

setIt is a global API in Vue 2 . Responsive data can be added manually to solve the problem of data change views not being updated. When you directly set the value of an item in the array or directly set the value of a property of the object in the project, you will find that the page is not updated. This is because Object.defineProperty()of the limitation that data changes cannot be monitored, which can be this.$set(数组或对象,数组下标或对象的属性名,更新后的值)solved.

What is keep-alive?

  • Function: Implement component caching, maintain component status, and avoid performance problems caused by repeated rendering.
  • Working principle: Vue.js internally abstracts DOM nodes into VNode nodes, and keep-alivethe cache of components is also based on VNode nodes. It caches the components that meet the conditions in the cache object, and when re-rendering, the VNode node is taken out of the cache object and rendered.
  • The following attributes can be set: ① include: String or regular expression. Only components with matching names will be cached. exclude: String or regular expression, any component with a matching name will not be cached. max: Number, the maximum number of component instances that can be cached. Matching first checks the component's nameoptions. If namethe option is not available, it matches its local registration name (the key value of the parent component's components option). Anonymous components cannot be matched.

Components with cache set up keep-alivewill have two more life cycle hooks: activated, deactivated. When entering the component for the first time: beforeCreate --> created --> beforeMount --> mounted --> activated --> beforeUpdate --> updated --> deactivated When entering the component again: activated --> beforeUpdate --> updated -- > deactivated

mixin

mixin(Mixin), which provides a very flexible way to distribute reusable functionality in Vue components.

Usage scenarios: Some identical or similar codes are often used in different components, and the functions of these codes are relatively independent. The same or similar code can be extracted through mixins.

shortcoming:

1. The source of variables is unclear.
2. Multiple mixins may cause naming conflicts (solution: Vue 3’s combination API).
3. There is a many-to-many relationship between mixins and components, making the project more complex.

slot

slotSlots are generally used inside components. When encapsulating a component, if you are not sure what form of element is displayed at the position inside the component, you can occupy slotthis position. The element at this position needs to be passed in the form of content from the parent component. slotDivided into:

  • 默认插槽: Subcomponents use <slot>tags to determine the rendering position. Structures can be placed in tags DOMas backup content. When the parent component is in use, content can be directly written in the tag of the subcomponent. This part of the content will be inserted into the tag <slot>position of the subcomponent. . If the parent component does not pass content to the slot when it is used, the backup content will be displayed on the page.
  • 具名插槽: The subcomponent uses nameattributes to represent the name of the slot. If there is no specified nameslot, there will be an implicit name default. When used in the parent component, v-slotthe directive specifies which slot the element needs to be placed in based on the default slot, and the value is the slot attribute value v-slotof the child component . nameUsing v-slotinstructions to specify which slot an element should be placed in must match <template>the element, and an <template>element can only correspond to one reserved slot, that is, multiple <template>elements cannot use v-slotinstructions to specify the same slot. v-slotThe abbreviation of is #, for example, v-slot:headerit can be abbreviated as #header.
  • 作用域插槽: The subcomponent <slot>binds propsdata on the label to pass the subcomponent data to the parent component for use. The method for the parent component to obtain the slot-bound props data: 1. scope="received variable name": <template scope="接收的变量名">2. slot-scope="received variable name": <template slot-scope="接收的变量名">3.v-slot: slot name="received variable name":<template v-slot:插槽名="接收的变量名">

What are the modifiers in Vue?

In Vue, modifiers handle many details of DOM events, so that we no longer need to spend a lot of time dealing with these troublesome things, and can have more energy to focus on the logical processing of the program. Modifiers in Vue are divided into the following types:

1. Form modifier lazyAfter filling in the information, the value will be assigned to value only when the cursor leaves the label, that is, changethe information will be synchronized after the event. numberAutomatically convert user input value to numeric type, but if the value cannot be parseFloatparsed, the original value will be returned. trimAutomatically filter the first and last spaces entered by the user, but the spaces in the middle will not be filtered.
2. The event modifier stopprevents the event from bubbling, which is equivalent to calling event.stopPropagationa method. preventPreventing the default behavior of the event is equivalent to calling event.preventDefaultthe method. selfThe handler function is only event.targettriggered when is the current element itself. onceAfter binding the event, it can only be triggered once, and it will not be triggered the second time. captureUse the event capture mode, that is, events triggered by the element itself are processed here first, and then handed over to internal elements for processing. passiveTell the browser that you don't want to block the default behavior of the event. Let the component listen to the native events of the root element nativelike the built-in tag, otherwise the component will only listen to custom events. 3. Mouse button modifier left click. right click. Middle click. 4. Key value modifiers Keyboard modifiers are used to modify keyboard events ( , ), as follows: There are many, but they provide us with aliases, which are divided into the following two types: * Ordinary keys (enter, tab, delete, space , esc, up...) * System modifier keys (ctrl, alt, meta, shift...)htmlv-onleftrightmiddleonkeyuponkeydownkeyCodevue

What is your understanding of SPA?

1. Concept: SPA(Single-page application), that is, single-page application, which is a model of a network application or website that interacts with users by dynamically rewriting the current page. This method avoids interrupting the user experience when switching between pages. In SPA, all necessary code (HTML, JavaScript, and CSS) is retrieved with the load of a single page, or the appropriate resources are dynamically loaded and added to the page as needed (usually in response to user actions). The page does not reload at any point in time, nor does control transfer to other pages. For example, just like a cup, it contains milk in the morning, coffee at noon, and tea in the afternoon. It always has the content and the cup remains unchanged. 2. The difference SPAbetween MPA: MPA(Muti-page application), that is, multi-page applications. In MPA, each page is a main page and is independent. Whenever a page is accessed, the Html, CSS, and JS files need to be reloaded, and public files are loaded on demand according to needs. || SPA | MPA || — | — | — || Composition | One main page and multiple page fragments | Multiple main pages || url mode | hash mode | history mode || SEO search engine optimization | Difficult to achieve, but possible Use SSR method to improve | Easy to implement | | Data transfer | Easy | Passed through url, cookie, localStorage, etc. | | Page switching | Fast speed, good user experience | Switching loading resources, slow speed, poor user experience | | Maintenance cost | Relative Easy | Relatively complex | 3. SPAAdvantages and Disadvantages: Advantages: * Has the immediacy of desktop applications, portability and accessibility of websites * Good and fast user experience, content changes do not require reloading the entire page * Good before and after End-to-end separation, clearer division of labor Disadvantages: * Not conducive to search engine crawling * The first rendering speed is relatively slow ### Two-way binding?

1. Concept: Two-way binding in Vue is an instruction v-modelthat can bind a responsive data to the view, and changes in the view can change the value. It is syntactic sugar, which is equivalent to and v-modelby default . Its use can reduce a lot of tedious event processing code and improve development efficiency. 2. Usage: Usually used on form items , and can also be used on custom components to represent the input and output control of a certain value. 3. Principle: It is an instruction. Two-way binding is actually completed by the Vue compiler. By outputting the component rendering function containing the template, it is actually the binding of properties and event monitoring. The corresponding variables will be made in the event callback function. Update operation.:value@inputv-model
v-model
v-modelv-modelvalueinput

Can child components directly change the data of parent components?

1. Everything propfollows the single binding principle, propschanges due to the update of the parent component, and naturally flows the new state downward to the child component without passing it in the reverse direction. This prevents child components from accidentally modifying the parent component's state, otherwise the application's data flow would easily become confusing and difficult to understand. In addition, every time the parent component is updated, all the child components propswill be updated to the latest values, which means that you should not modify one of the child components prop. If you do, Vue will throw a warning on the console.
2. In the actual development process, there are usually two scenarios that require modification prop: * propis used to pass in an initial value, and the subcomponent wants to use it as a local data attribute later. In this case, it is best to define a new local data attribute and propsget the initial value from . prop* Further conversion is required on the passed in value. propIt's better to define a computed property based on this value.
3. In practice, if you really want to change the properties of the parent component, emitan event should be used to let the parent component change. When an object or array propsis passed in as a component, although the child component cannot change propsthe binding, it can still change the value inside the object or array. This is because JS objects and arrays are passed by reference, and for Vue, although it is possible to prohibit such changes, it will cause a huge performance loss, and the gain outweighs the gain.

Common routing modes and principles in Vue Router?

1. Hash mode: location.hashThe value of * is what follows # in the URL. Its characteristic is that although the hash appears in the URL, it will not be included in the HTTP request and has no impact on the backend at all, so changing the hash will not reload the page.

  • You can add listening events for hash changes window.addEventListener("hashchange", funcRef, false). Each change hash (window.location.hash)will add a record to the browser's access history. Using the above characteristics of hash, you can implement the function of front-end routing to update the view without re-requesting the page . Features: Good compatibility but not beautiful. 2. History mode: Use the new pushState()and replaceState()methods in HTML5 History Interface. These two methods are applied to the browser's history stack. Based on the currently existing back, forward, and go, they provide the function of modifying the history record. These two methods have something in common: when they are called to modify the browser history stack, although the current URL has changed, the browser will not refresh the page. This means that the front-end routing for single-page applications "updates the view but does not re-request the page." "Provides basic features: although it is beautiful, 404 will appear when refreshing and requires back-end configuration.

Dynamic routing?

Many times, we need to map routes for a given matching pattern to the same component. In this case, we need to define dynamic routing. For example, we have a User component, which must be used to render all users with different IDs. Then, we can use it in the routing path of vue-router 动态路径参数(dynamic segment)to achieve this effect: {path: '/user/:id', compenent: User}, which :idis the dynamic path parameter.

Understanding Vuex?

1. Concept: Vuex is a state management library dedicated to Vue. It centrally manages the state of the application in a global manner and uses corresponding rules to ensure that the state changes in a predictable way. 2. Problems solved: The main problem Vuex solves is state sharing between multiple components. Although state sharing can also be achieved using various communication methods, it is often necessary to maintain state consistency among multiple components. This model is prone to problems and complicates program logic. Vuex extracts the shared state of components and manages it in a global singleton mode, so that any component can obtain and modify the state in a consistent way. Responsive data can also ensure simple one-way flow, making the code more efficient. Structured and easy to maintain. 3. When to use: Vuex is not necessary. It can manage state, but it also brings more concepts and frameworks. If we do not plan to develop a large single-page application or there is not a large amount of global state to maintain in the application, there is no need to use Vuex. A simple store mode is enough. Otherwise, Vuex would be the natural choice.
4. Usage: Vuex puts the global state into statethe object, which itself is a state tree. The component uses storeinstances to stateaccess these states; then uses supporting mutationmethods to modify these states, and can only mutationmodify the state and call it in the component. commitMethod submission mutation; if there are asynchronous operations or complex logic combinations in the application, they need to be written action. After execution, if there are state modifications, they still need to be submitted mutation, and the components are dispatchdispatched action. Finally, modularization is used to modulesorganize the split sub-modules through options. When accessing the state, you need to pay attention to adding the name of the sub-module. If the sub-module has settings, additional namespace prefixes are required when namespacesubmitting mutationand dispatching .action

How to solve the problem of Vuex state loss after page refresh?

Vuex only saves the state in memory, and it will be lost after refreshing. If you want to persist, you need to save it.

localStorageIt's very suitable. mutationIt can be saved at the same time when submitting localStorage, and storethe value can be taken out as statethe initial value.

You can also use third-party plug-ins. It is recommended to use vuex-persistplug-ins. It is a plug-in for Vuex persistent storage. It does not require you to access manually storage, but directly saves the state to cookieor localStorage.

Do you understand Vue SSR?

SSRThat 服务端渲染(Server Side Render)is, the work of Vue rendering tags into HTML on the client is done on the server, and then the HTML is returned directly to the client.

  • Advantages: Better SEO, and the first screen loads faster.
  • Disadvantages: Development conditions will be limited. Server-side rendering only supports two hooks, beforeCreate and created. When we need some external extension libraries, special processing is required. Server-side rendering applications also need to be in the Node.js running environment. The server will have greater load requirements.

What performance optimization methods do you know about Vue?

  • Routes are loaded lazily. Effectively split the application size and load it asynchronously when accessed.
  • keep-aliveCache the page. Avoid duplicate creation of component instances and retain cached component state.
  • v-forTraversal avoids simultaneous use v-if. In fact, it is already a wrong usage in Vue 3.
  • Long list performance optimization, virtual list can be used.
  • v-once. Data usage that no longer changes v-once.
  • The event is destroyed. After the component is destroyed, global variables and timers are destroyed.
  • Pictures are loaded lazily.
  • Third-party plug-ins are introduced on demand.
  • Subcomponent splitting. Heavier state components are suitable for splitting.
  • Server-side rendering.

at last

We have prepared a front-end information package for everyone. Contains 54, 2.57G front-end related e-books, "Front-end Interview Guide (with answers and analysis)", video tutorials on difficult and key knowledge (full set).



Friends in need can click on the card below to receive it and share it for free

Guess you like

Origin blog.csdn.net/web2022050901/article/details/129447556