A comprehensive guide to coding, performance optimization, and ecosystem improvements in Vue 3 and Vue 2

Summary: Vue.js is a popular JavaScript framework, and its latest version, Vue 3, differs from its previous version, Vue 2, in many ways. This blog will focus on the coding methods in Vue 3 that are inconsistent with Vue 2. We'll explore the new features, syntax changes, and optimizations introduced in Vue 3. By comparing these changes in detail, we will understand how to better migrate and use Vue 3.

1. Introduction

In Vue 3, the Composition API was introduced as a supplement and extension to the Options API. The Composition API is designed to provide a more flexible and composable way of organizing code. The inconsistencies between the Composition API and Options API are detailed below.

1. Composition API

Composition API is a new feature in Vue 3. It provides a set of functional APIs to organize and reuse code logic. Compared with Options API, Composition API is more flexible and maintainable, and is especially suitable for processing complex business logic and shared logic between components.

Key features of the Composition API include:

  • Use functions to organize logic: Encapsulate relevant logic according to functions and reuse them through functions.
  • Precise combination of logic: The logic of different functions can be combined as needed without following a fixed order of declaration.
  • Better code reusability: logic can be extracted into custom functions and referenced and called in components.
  • Better type inference and editor support: Thanks to functional programming, the editor can better infer types and provide code completion.

Here is an example using the Composition API:

import {
    
     reactive, computed } from 'vue';

export default {
    
    
  setup() {
    
    
    const state = reactive({
    
    
      count: 0,
    });

    const increment = () => {
    
    
      state.count++;
    };

    const doubledCount = computed(() => {
    
    
      return state.count * 2;
    });

    return {
    
    
      state,
      increment,
      doubledCount,
    };
  },
};

2. Options API

Options API is a commonly used way to write components in Vue 2. It defines various properties, methods and life cycle hooks of the component by declaring an options object. Compared with Composition API, Options API is more suitable for writing simple components or for beginners.

Key features of the Options API include:

  • Based on options object: Use an object containing various options to define various aspects of the component.
  • Declarative life cycle hooks: Handle component life cycle events through predefined life cycle hook functions.
  • Data and methods are defined in the same object: the data and methods of the component are defined in the properties of the options object.
  • Context access inside the object: The context of the component can be accessed inside the component through the this keyword.

Here is an example using the Options API:

export default {
    
    
  data() {
    
    
    return {
    
    
      count: 0,
    };
  },
  methods: {
    
    
    increment() {
    
    
      this.count++;
    },
  },
  computed: {
    
    
    doubledCount() {
    
    
      return this.count * 2;
    },
  },
};

Summary:
Composition API was introduced in Vue 3 as a supplement to Options API. The Composition API provides a more flexible and composable way of writing code, suitable for handling complex business logic and shared logic between components. And Options API

It is more suitable for writing simple components or for beginners. In migration projects or new projects, you can choose the appropriate API to write Vue components according to your needs.

2. Template syntax

In Vue 3, there are also some inconsistencies with Vue 2 in terms of template syntax. The following will introduce the v-model directive, attribute binding in the template, and changes in dynamic components respectively.

1. v-model directive:

In Vue 2, the v-model directive is used to implement two-way binding between form elements and components. In Vue 3, the behavior of the v-model directive has changed. The v-model directive in Vue 3 is mainly used for two-way binding between props and emit of components.

When using the v-model directive, you need to define a modelValue attribute in the component's props and use update:modelValue in the emit event to update the attribute. The specific usage is as follows:

<template>
  <custom-input v-model="value"></custom-input>
</template>

<script>
import CustomInput from './CustomInput.vue';

export default {
  components: {
    CustomInput,
  },
  data() {
    return {
      value: '',
    };
  },
};
</script>

In the CustomInput component, you need to receive the modelValue through props, and use $emit('update:modelValue', newValue) in the input event of the input box to update the value in the parent component.

2. Attribute binding in the template:

In Vue 2, property binding can be achieved using the v-bind directive or the abbreviated colon syntax. In Vue 3, the colon syntax is written slightly differently, and you need to use the v-bind directive to explicitly bind properties.

Property binding example for Vue 2:

<template>
  <div :class="{ active: isActive }"></div>
  <input :value="message">
</template>

Property binding example for Vue 3:

<template>
  <div v-bind:class="{ active: isActive }"></div>
  <input v-bind:value="message">
</template>

It should be noted that in Vue 3, you can also use the abbreviated form of attribute binding, abbreviating v-bind as a prefix symbol (such as: @, :, #, etc.), and choose the appropriate prefix symbol according to the specific purpose. .

3. Dynamic components:

In Vue 2, you can use elements and is attributes to achieve dynamic component switching. In Vue 3, the way of writing dynamic components has undergone some changes.

Dynamic component example in Vue 2:

<template>
  <component :is="currentComponent"></component>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

export default {
  data() {
    return {
      currentComponent: 'ComponentA',
    };
  },
  components: {
    ComponentA,
    ComponentB,
  },
};
</script>

Dynamic component example in Vue 3:

<template>
  <component :is="currentComponent"></component>
</template>

<script>
import ComponentA from './ComponentA.vue';
import ComponentB from './ComponentB.vue';

export default {
  data() {
    return {
      currentComponent: ComponentA,
    };
  },
  components: {
    ComponentA

,
    ComponentB,
  },
};
</script>

In Vue 3, you can directly use the component object as the value of currentComponent without using the component's name string.

Summary:
In Vue 3, the usage of the v-model directive has changed, and it is mainly used for two-way binding between props and emit of components. Property binding in templates requires explicit property binding using the v-bind directive, and you can choose to use different prefix symbols to simplify the syntax. The writing method of dynamic components remains consistent in Vue 3. You only need to assign the component object directly to the is attribute. These changes make the template syntax more flexible and easier to use in Vue 3.

3. Responsive system

In Vue 3, the reactive system also has some inconsistencies with Vue 2. The following will introduce Ref and Reactive, Computed and Watch, and changes in static properties respectively.

1. Ref and Reactive:

In Vue 2, use the Vue.observable() method to convert a normal object into a reactive object. In Vue 3, two new reactive APIs, Ref and Reactive, were introduced.

  • Ref: Ref is a new type in Vue 3 that wraps normal data and makes it reactive. Ordinary data can be wrapped into Ref objects through the ref() function. When using Ref objects, you need to access and modify the value through the .value property.
import {
    
     ref } from 'vue';

const count = ref(0);

console.log(count.value); // 访问值

count.value = 1; // 修改值
  • Reactive: Reactive is a new function in Vue 3 that receives a normal object and returns a reactive proxy object. When using Reactive functions, there is no need to manually call Vue.observable() to convert objects like in Vue 2.
import {
    
     reactive } from 'vue';

const state = reactive({
    
    
  count: 0,
});

console.log(state.count); // 访问值

state.count = 1; // 修改值

2. Computed and Watch:

In Vue 3, the use of Computed and Watch is basically the same as in Vue 2, but there are some subtle changes.

  • Computed: In Vue 3, you can use the computed() function to create computed properties. The computed() function receives a function as a parameter and returns a Ref object. It should be noted that in Vue 3, you no longer need to use get and set to define calculated properties, but directly return the calculated results.
import {
    
     computed, ref } from 'vue';

const count = ref(0);

const doubledCount = computed(() => count.value * 2);

console.log(doubledCount.value); // 访问计算属性的值
  • Watch: In Vue 3, you can use the watch() function to create a listener to execute a callback function when the reactive data changes. The watch() function receives two parameters: the data to be monitored and the callback function. It should be noted that in Vue 3, the watch() function no longer supports string expressions.
import {
    
     watch, ref } from 'vue';

const count = ref(0);

watch(count, (newValue, oldValue) => {
    
    
  console.log(`count changed from ${
      
      oldValue} to ${
      
      newValue}`);
});

3. Static attributes:

In Vue 2, a component's static properties can be accessed through the $options property of the Vue instance. In Vue 3, static properties were removed and cannot be accessed directly through component instances. This is because Vue 3 uses a new internal implementation based on Proxy, and static properties are no longer part of the instance.

Summary:
In Vue 3, Ref and Reactive provide a more flexible and intuitive way to create responsive data. Computed and Watch are basically used in the same way.

But there are some changes in grammatical details. It should be noted that the static properties of components have been removed in Vue 3 and cannot be accessed directly through instances. These changes make the responsive system more intuitive and easier to use.

4. Virtual DOM

In Vue 3, the virtual DOM (Virtual DOM) also has some inconsistencies with Vue 2. The following will introduce three changes related to the virtual DOM: Fragment, Teleport and Suspense.

1. Fragment:

In Vue 2, it is invalid to use multiple root elements in a template, they must be wrapped in a parent element. In Vue 3, you can use Fragment to solve this problem. Fragment is a special placeholder used to wrap multiple elements in a template, but does not generate additional DOM elements in the rendering result.

Example using Fragment:

<template>
  <Fragment>
    <div>Element 1</div>
    <div>Element 2</div>
  </Fragment>
</template>

<></>Fragments can be represented using a shorthand syntax :

<template>
  <>
    <div>Element 1</div>
    <div>Element 2</div>
  </>
</template>

2. Teleport:

Teleport is a new feature in Vue 3, which is used to render the content of components anywhere in the DOM. Teleport provides a concise way to handle the need to render content outside the component, such as rendering the component in a modal, dialog, or popup menu.

Example using Teleport:

<template>
  <Teleport to="body">
    <div class="modal">
      Modal Content
    </div>
  </Teleport>
</template>

In the above example, tothe attribute specifies the target location to render the Teleport to, here body, but it could be any other legal DOM selector.

3. Suspense:

Suspense is another feature introduced in Vue 3 that is used to display a placeholder during the loading of an asynchronous component until the async component is loaded and ready to be rendered. Suspense provides a better user experience and more granular control.

Example using Suspense:

<template>
  <Suspense>
    <template #default>
      <div>Loading...</div>
    </template>
    <template #fallback>
      <div>Error! Failed to load component.</div>
    </template>
    <AsyncComponent />
  </Suspense>
</template>

<script>
import { defineAsyncComponent } from 'vue';

const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'));

export default {
  components: {
    AsyncComponent,
  },
};
</script>

In the above example, #defaultthe content in the slot will be displayed before the asynchronous component is loaded, and #fallbackthe content in the slot will be displayed when the asynchronous component fails to load.

Summary:
In Vue 3, Fragment provides a way to solve the problem of multiple root elements, Teleport provides more flexible component rendering position control, and Suspense provides a better asynchronous component loading experience. These

The changes enrich the functionality of Virtual DOM, providing more development options and a better user experience.

5. Performance optimization

In Vue 3, there are also some new features and improvements in performance optimization. The following will introduce static promotion and compilation optimization, two aspects related to performance optimization.

1. Static Hoisting:

In Vue 2, static content in templates is recreated on every re-render, which can cause some performance penalties. In Vue 3, through static promotion technology, the compiler can detect and optimize static content and promote it to static constants, reducing the cost of re-creation.

Static lifting can bring the following advantages:

  • It reduces the number of virtual DOM creation and comparisons and improves rendering performance.
  • By reducing unnecessary DOM operations, the cost of page reflow and redrawing is reduced.

2. Compilation optimization:

Vue 3 has improved the compiler and introduced some optimization measures to improve compilation efficiency and performance.

  • Patch flag: The compiler will add a Patch flag to each VNode node based on the difference between the static content and dynamic content of the template to perform more precise operations during the update process and reduce unnecessary comparison and rendering operations.

  • Caching event handling functions: In Vue 3, the compiler caches event handling functions to avoid creating new function instances every time it is re-rendered, improving the performance of event processing.

  • Static node promotion: The compiler will detect static nodes in the template and promote them to constants to avoid creating new VNodes every time they are re-rendered, reducing rendering overhead.

  • Tree shaking: Vue 3’s compiler supports removing unused components and instructions from templates, thereby reducing packaging volume and improving application loading performance.

Summary:
In Vue 3, application performance can be improved through measures such as static improvement and compilation optimization. Static boosting reduces the overhead of recreating static content, while compilation optimization improves performance by improving the compiler and optimizing the rendering process. These optimization measures have significantly improved the performance of Vue 3, providing a better user experience and higher development efficiency.

6. TypeScript support

In Vue 3, support for TypeScript has been significantly improved. The following will introduce type inference and type declaration, two aspects related to TypeScript support.

1. Type inference:

Vue 3 is able to automatically infer the type of variables in many cases, providing better type safety and development experience. Here are some examples:

  • Props inference: In a component, you can definePropsdefine the props of the component through the function, and perform type inference based on the default value of the props and the passed-in value.
import {
    
     defineComponent, defineProps } from 'vue';

const MyComponent = defineComponent({
    
    
  props: {
    
    
    message: String,
    count: {
    
    
      type: Number,
      default: 0,
    },
  },
  setup(props) {
    
    
    // 在使用 props 时,可以获得类型推断
    console.log(props.message.toUpperCase());
    console.log(props.count.toFixed(2));

    // ...
  },
});
  • Ref inference: When using refthe function to create a Ref object, Vue 3 can automatically infer the type of the value wrapped in the Ref object.
import {
    
     ref } from 'vue';

const count = ref(0); // 推断为 Ref<number>

console.log(count.value.toFixed(2)); // value 推断为 number
  • Computed inference: When using computedthe function to create a calculated property, Vue 3 can automatically infer the type of the calculated property based on the return value of the calculated function.
import {
    
     computed, ref } from 'vue';

const count = ref(0);

const doubledCount = computed(() => count.value * 2); // 推断为 ComputedRef<number>

console.log(doubledCount.value.toFixed(2)); // value 推断为 number

2. Type declaration:

Vue 3 provides better support for more accurate type inference and type checking when using Vue components in TypeScript.

  • defineComponent function: defineComponentComponents can be defined using the function, which accepts a component configuration object and returns a component options object containing the correct type declaration.
import {
    
     defineComponent } from 'vue';

export default defineComponent({
    
    
  name: 'MyComponent',
  props: {
    
    
    message: String,
    count: {
    
    
      type: Number,
      default: 0,
    },
  },
  setup(props) {
    
    
    console.log(props.message.toUpperCase());
    console.log(props.count.toFixed(2));

    // ...
  },
});
  • Options API type declarations: For components that use the Options API, Vue 3 provides correct type declarations to ensure you get correct type hints and type checking when writing component options.
import {
    
     Options, VueConstructor } from 'vue';

const MyComponent: Options = {
    
    
  name: 'MyComponent',
  props: {
    
    
    message: String,
    count: {
    
    
      type: Number,
      default: 0,
    },
  },
  setup(props) {
    
    
    console.log(props.message.toUpperCase());
    console.log(props.count.toFixed(2));

    // ...
  },
};

export default MyComponent as VueConstructor;

Summary:
TypeScript support improved in Vue 3

, including type inference and type declaration. Type inference enables developers to obtain better type safety and development experience, while type declarations ensure accurate type hints and type checking when using Vue components. These improvements make developing Vue applications with TypeScript more comfortable and efficient.

7. Slots and components

In Vue 3, there are also some changes in slots and components. The new slot syntax and component instance access will be introduced below.

1. New slot syntax:

Vue 3 introduces a new slot syntax that is more flexible and intuitive. The new slot syntax uses <slot>elements to define slots and enables more functionality through named slots and scoped slots.

Named slot example:

<template>
  <div>
    <slot name="header"></slot>
    <div>Content</div>
    <slot name="footer"></slot>
  </div>
</template>

When using components, you can use <template v-slot:slotName>or abbreviation <template #slotName>to specifically define the slot content.

<template>
  <MyComponent>
    <template #header>
      <h1>Header Content</h1>
    </template>
    <p>Main Content</p>
    <template #footer>
      <footer>Footer Content</footer>
    </template>
  </MyComponent>
</template>

Scope slot example:

<template>
  <div>
    <slot name="header" :title="title"></slot>
    <div>Content</div>
    <slot name="footer" :year="year"></slot>
  </div>
</template>

slotPropsWhen using scoped slots, the data passed to the slot can be accessed through properties within the slot content .

<template>
  <MyComponent>
    <template #header="slotProps">
      <h1>{
   
   { slotProps.title }}</h1>
    </template>
    <p>Main Content</p>
    <template #footer="slotProps">
      <footer>{
   
   { slotProps.year }}</footer>
    </template>
  </MyComponent>
</template>

2. Component instance access:

In Vue 2, $refscomponent instances can be accessed through , but in Vue 3, access to component instances has changed. You can use reffunctions to create a reactive reference and bind it to a component.

<template>
  <div>
    <MyComponent ref="myComponentRef"></MyComponent>
    <button @click="logComponentData">Log Component Data</button>
  </div>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const myComponentRef = ref(null);

    const logComponentData = () => {
      console.log(myComponentRef.value.data);
    };

    return {
      myComponentRef,
      logComponentData,
    };
  },
};
</script>

In the above example, refa reference is created via a function myComponentRefand bound to MyComponentthe component. You can then myComponentRef.valueaccess the component instance via and get its data.

Summary:
In Vue 3, slot syntax has changed

ization, introducing a new slot syntax, making the use of slots more flexible and intuitive. At the same time, access to component instances has also changed. refReactive references can be created through functions and bound to components to access component instances. These changes make the use of slots and components more convenient and powerful.

8. Transitions and animations

There are also some improvements in transitions and animations in Vue 3. The two aspects of Transition component and animation class name will be introduced below.

1. Transition component:

In Vue 3, transition effects can be achieved through the Transition component. The Transition component provides the ability to apply transition effects when elements are inserted or removed.

When using the Transition component, you can control the transition effect through the following properties:

  • name: Specify the name of the transition effect. You can define specific transition effects by setting the CSS class name.

  • appear: Specifies whether to apply transition effects on initial rendering.

  • enter-class// enter-active-class: enter-to-classSpecify the transition class name when inserting elements.

  • leave-class// leave-active-class: leave-to-classSpecify the transition class name when removing elements.

Here is an example of using the Transition component to implement a gradient transition:

<template>
  <Transition name="fade">
    <div v-if="show" class="box"></div>
  </Transition>
  <button @click="toggle">Toggle</button>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {
    const show = ref(true);

    const toggle = () => {
      show.value = !show.value;
    };

    return {
      show,
      toggle,
    };
  },
};
</script>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}

.fade-enter,
.fade-leave-to {
  opacity: 0;
}
</style>

In the above example, name="fade"the Transition component is applied to the wrapped element through settings and the CSS class name of the transition effect is defined. During the initial rendering, the fade-enterand fade-enter-activeclass names will be applied to achieve the gradient appearance effect. When the element is removed, the class name will be applied to achieve the disappearance effect of the gradient fade-leave.fade-leave-active

2. Animation class name:

In Vue 3, you can use animation class names to animate elements. Similar to Vue 2, animation effects can be triggered by adding a class name to an element.

Commonly used animation class names in Vue 3 include:

  • enter: The starting state of the inserted element.

  • enter-active: The transition state of the inserted element.

  • enter-to: The end state of the inserted element.

  • leave: Remove the starting state of the element.

  • leave-active: Remove the transition state of an element.

  • leave-to: Remove the end state of the element.

Specific animation effects can be achieved by defining the CSS styles of these class names.

For example, implement a simple fade animation:

<template>
  <div :class="animationClass" class="box"></div>
  <button @click="toggle">Toggle</button>
</template>

<script>
import { ref } from 'vue';

export default {
  setup() {


    const show = ref(true);

    const toggle = () => {
      show.value = !show.value;
    };

    const animationClass = ref('');

    watch(show, () => {
      animationClass.value = show.value ? 'fade-enter-active' : 'fade-leave-active';
    });

    return {
      show,
      toggle,
      animationClass,
    };
  },
};
</script>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}

.fade-enter,
.fade-leave-to {
  opacity: 0;
}
</style>

In the above example, animationClassthe animation effect is achieved through dynamic binding. When showthe value of changes, the value of will be showset according to the value of animationClass, thereby triggering the corresponding animation effect.

Summary:
In Vue 3, transition and animation aspects can be achieved through the Transition component and animation class name. The Transition component provides the ability to control the insertion and removal of transition effects on elements, and the animation class name can trigger animation effects by adding the class name. These improvements make transitions and animations more flexible and easier to use.

9. Global API changes

In Vue 3, the global API has undergone some changes, including name changes and global API removal. Changes in these two aspects will be introduced below.

1. Global API name change:

In Vue 3, the names of some global APIs have changed to better reflect their functionality and purpose.

  • Vue: In Vue 3, Vueconstructors are no longer exported as the default entry point. You can use createAppfunctions to create application instances.
// Vue 2
new Vue({
    
    
  // ...
});

// Vue 3
import {
    
     createApp } from 'vue';

createApp({
    
    
  // ...
}).mount('#app');
  • filter: In Vue 2, Vue.filterfilters could be registered via global methods, but in Vue 3, filters have been removed. You can use local filters or custom functions to achieve the same functionality.

  • directive: In Vue 2, Vue.directivedirectives could be registered through global methods, but in Vue 3, directives have been removed. You can use custom directives or use directives directly in components to replace global directives.

  • mixin: In Vue 2, Vue.mixinmixin options could be passed through global methods, but in Vue 3, mixin options have been removed. The same functionality can be achieved using the Composition API.

  • component: In Vue 2, Vue.componentglobal components can be registered through global methods, but in Vue 3, the way components are registered has changed. Components can be registered using local registration or through application instance componentmethods.

2. Removed global API:

In addition to the name changes, Vue 3 also removes some global APIs because their functionality can be implemented through other means or their use is no longer recommended.

  • v-once: In Vue 2, you can use v-oncedirectives to mark elements and components to be rendered only once. In Vue 3, v-oncedirectives have been removed and the same effect can be achieved with reactive data and conditional rendering.

  • inline-template: In Vue 2, you can use inline-templateattributes to write a component's template inside the component's tag. In Vue 3, inline-templatethe feature has been removed and the same functionality can be achieved through single-file components or string templates.

  • syncModifiers: In Vue 2, you can use syncmodifiers to achieve two-way binding between parent and child components. In Vue 3, syncthe modifier has been removed and v-modeltwo-way binding can be achieved by using explicit binding to the event.

Summary:
In Vue 3, the global API has undergone some changes, including name changes and moves

Except for the global API. These changes are intended to improve the consistency and ease of use of the API and push developers to use better alternatives to achieve the same functionality. Developers need to be aware of these changes and update their code accordingly.

10. Building Tools and Ecosystems

In terms of build tools and ecosystem, Vue 3 introduces some improvements, including the adaptation of Vue CLI and third-party libraries. Changes in these two aspects will be introduced below.

1. Vue CLI given Vue CLI 2:

Vue CLI is the official scaffolding tool of Vue.js, used to quickly build and manage Vue projects. Vue CLI 2 is the version of the Vue 2 era, while Vue CLI is a new version for Vue 3.

Vue CLI 3 has the following improvements compared to Vue CLI 2:

  • It uses a modern plug-in mechanism to make expansion and customization more flexible and easier.

  • A graphical interface is provided through the Vue CLI UI to facilitate project configuration and management.

  • Introduced Vue CLI Service, which is an extensible underlying build tool that integrates commonly used build and development tools.

  • Supports new features of Vue 3, such as Composition API and new life cycle.

Therefore, for Vue 3 projects, it is recommended to use Vue CLI for project initialization and management.

2. Adaptation of third-party libraries:

With the release of Vue 3, many third-party libraries are also adapting and upgrading to support Vue 3's new features and syntax. Some commonly used Vue ecosystem libraries have released compatible versions of Vue 3, or provided Vue 3 adaptation guides in their official documentation.

When using a third-party library, it is recommended to consult its official documentation to learn whether it supports Vue 3 and how to adapt. Usually, third-party libraries adapted to Vue 3 will provide specific installation and usage guidelines, and there may be some syntax and API changes that need to be noted.

In addition, Vue 3 also introduces an @vue/composition-apiofficial plug-in called, which can be used to use the Composition API in Vue 2 projects. This plugin makes it easier to gradually migrate to Vue 3’s Composition API in Vue 2 projects.

Summary:
In terms of building tools and ecosystem, Vue 3 brings updates to the Vue CLI and adaptation of third-party libraries. It is recommended to use Vue CLI 3 to create and manage Vue 3 projects, and check the official documentation of third-party libraries to learn about their support and adaptation to Vue 3. These improvements enable developers to better take advantage of Vue 3’s new features and ecosystem, improving development efficiency and code quality.

Guess you like

Origin blog.csdn.net/qq_43326668/article/details/130860380