vue2 upgrade vue3

long long ago I made a component library related to Vue low-code tools. Due to the needs of the new business system, I wanted to apply this to the new business system. I started to connect to ing with joy, and suddenly found… the new business system is Vue3…

The old component library uses vue2 and is packaged based on element-ui. In addition to using vue3, the new business system also uses element-plus for the component library. After a wave of forced integration…

Since then, the transformation process of component library upgrade to Vue3 version has begun…

Start the transformation

I record my transformation experience here, and I hope it can provide some reference for partners who need it.

About composite API

The writing method in each component has been changed to combined API. For the introduction and advantages of combined API, you can refer to the official website What is combined API?. After changing to combined API, the subsequent writing method of life cycle, data and props There have been some changes in the writing methods of watch, etc. I won’t go into too much details here. This official website will live forever~~ Overall experience, this writing method is quite comfortable, it is closer to functional programming, and the logic is abstracted and organized. very convenient.

Another thing to note is about the life cycle. In addition to the changes in the introduction method: the destroy life cycle option is renamed to unmounted; the beforeDestroy life cycle option is renamed to beforeUnmount.

About ref()

Previously in vue2, the data for the components we defined was generally defined in data. In the setup function of vue3, we can use the ref API to make responsive data. ref() returns avalue reference (packaging object ). A wrapped object has only one attribute: .value , which points to the value being wrapped inside. It is not difficult to use this piece. The main thing is to understand why packaging objects are used in vue3?

We know that primitive types in js only have values and no references. If a function returns a string, then when we receive the string, we only receive a value. We cannot track its subsequent changes. For this reason We admire Mr. You Yuxi’s design of this area: The significance of packaging objects is to provide a container that allows us to pass any type of value by reference between functions. This is a bit like useRef in React Hooks – but the difference is that Vue’s wrapper object is also a reactive data source. With such a container, we can pass the state back to the component by reference in the composition function that encapsulates the logic. The component is responsible for display (tracking dependencies), and the composition function is responsible for managing state (triggering updates)

This can help us better understand why we use APIs like ref in vue3, but if we want to create a reactive object without packaging, we can use the reactive API (and 2.x Vue.observable() is equivalent).

In additionWhen the wrapper object is exposed to the template rendering context, or is nested in another responsive object, it will be automatically expanded (unwrap) to the internal value, so we are using There is no need to use .value to get the value.

About rendering function

In the vue component library, we will inevitably use functional methods to write some components, that is, the Render function. In Vue2, we use it like this:

render(h) {
  return h('h1', {
    'class': 'm-h'
  }, 'Title')
}

But this is not possible in vue3 and needs to be rewritten as:

import { h } from 'vue';
render() {
  return h('h1', {
    'class': 'm-h'
  }, 'Title')
}

In vue3, h is no longer provided implicitly from the render function, but { h } from vue’ is introduced globally. The h function is used to create vnode. The h function receives three parameters: an HTML tag name, a component, an asynchronous component or a functional component.

Thinking: Why is the h function no longer provided implicitly? My personal understanding is that in this case we can directly return a rendering function from the setup according to our needs instead of having to return it from render. We can use the following method:

import { h } from 'vue'
const myComponent = () => {
  setup() {
    .....
    return (props) => {
      h('div', {}, 'demo')
    }
  }
}
About mixin

When I wrote a vue2 component library before, I would definitely use some mixins. This is what the vue3 official website says:

In Vue 2, mixins are the main tool for abstracting part of component logic into reusable blocks. However, they have several problems: Mixins are prone to collisions: because each mixin’s properties are merged into the same component, you still need to know about every other feature in order to avoid property name conflicts. Reusability is limited: we cannot pass any parameters to a mixin to change its logic, which reduces their flexibility in abstracting logic. To address these issues, we’ve added a new way to organize code by logical concerns: Composable APIs

Did you see that it is a combined API again, so I changed the mixin in the project to a combined API. In fact, it is more violent, that is, the mixin is exported in the form of a function, and the function returns the data and methods in the previous mixin. Wait, then call the function on the component’s setup (don’t forget to pass in the props and context), and post a code snippet to get a feel for it:

//Import data/methods in the original mixin
const {
  mpComponentStatus,
  mpLoadPromises,
  mpAllLoadPromisesCollected,
  mpStartWatchInputChange,
  mpPageLoadTriggered,
  mpDrawerContentChanged,
  componentTree,
  dataModel,
  scriptContext,
  businessConfig
} = templateMixin.templateHandle(props, ctx);

In the mixin, we can use the Combined API to handle the logic of the life cycle and the responsiveness of some data.

In contrast, the function-based API allows us to organize the code of each logical task into a corresponding function. When we find that a component becomes too large, we will split it into multiple smaller components; similarly, if a component’s setup() function becomes very complex, we can It is divided into smaller functions. If it is based on options, such segmentation cannot be achieved, because using mixins will only make things worse. Based on options vs. based on functions is like organizing code based on HTML/CSS/JS vs. organizing code based on single file components.

About emit

In vue2, the events that a component can trigger cannot be defined, but in vue3, the events that a component can trigger can be defined like props, which can be defined as follows:

props: {
   pageTitle: {
     type: String,
     default: ''
   }
 },
 emits: [
   'dialogShow'
 ]
About provide/inject

When we write a vue component library, we will most likely use provide/inject for communication between components. In my component library, I also changed it to a combined API method. Using this method, both can only be used in vue3. Called during setup() of the current active instance, the modified code snippet is posted below:

// parent.vue
import { provide, defineComponent } from 'vue';
export default defineComponent({
  setup(props, ctx) {
    provide('root', ctx);
    provide('datas', {
      a: 1
    })
  }
})
// child.vue
import { inject, defineComponent } from 'vue';
export default defineComponent({
  setup(props, ctx) {
    // The second parameter is the default value (optional)
    const root = inject('root', null);
    const datas = inject('datas', {});
  }
})
About v-model

There is a part in the component library that implements v-model. The previous version using vue2 was implemented like this:

export default {
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  methods() {
    change() {
      this.$emit('input', 'newValue')
    }
  }
}

Then in vue3, the prop and event default names of v-model have been changed:

  • prop: value —-> modelValue
  • event: input —-> update:modelValue
export default {
  props: {
    modelValue: {
      type: String,
      default: ''
    }
  },
  emits: ['update:modelValue'],
  methods() {
    change() {
      this.$emit('update:modelValue', 'newValue') // In vue2 it is this.$emit('input', 'newValue')
    }
  }
}

This change is incompatible, so if the component implements v-model by itself, be sure to pay attention!

About naming variables

In the previous component library, there were variable names starting with $, which can also be used normally, but a warning will be reported. In vue3, it is not recommended to use $ or for variable names. _ begins. I already warned you, what else did you say? Just change it.

About changes in webpack packaging configuration

My vue2 version component library is packaged with my own configured webpack. After upgrading vue3, I also made the following adjustments to the packaging:

  • The introduction of VueLoaderPlugin is changed from require(‘vue-loader/lib/plugin’) to const { VueLoaderPlugin } = require(‘vue-loader/dist/index’)
  • Vue-loader used in 2.x needs to be replaced with vue-loader@next
  • vue-template-compiler used in 2.x needs to be replaced by @vue/compiler-sfc
  • Note that the runtime of vue needs to be set up to depend on the runtime of external vue.

Conclusion

The above are some changes to upgrade the vue2 component library to the vue3 version. The overall principle is to minimize changes. Some of them must be modified, and some are strongly recommended. The overall time consumption is roughly 24h (accumulated time ,for reference only).

In addition, let’s talk about the coding experience. You may not be used to using vue2 when you first start writing, but after basically writing a component, you will feel that the coding method of vue3 is quite nice, especially the combined API provided. It is more comfortable to look at. It is not restricted by the calling order. It can be called conditionally and can be better designed according to your own ideas. It is worth a try~~

Reference documentation

Vue Function-based API RFC

The knowledge points of the article match the official knowledge archives, and you can further learn relevant knowledge. Vue entry skill treevue3 basics (JS)Vue3 life cycle function 39857 people are learning the system