Vue-next (Vue 3) has been out for a while. It is currently in the release phase, which means there will be no major changes in the API. It’s good to see that Vue has settled down and is ready to participate in our projects.
I have to say that Vue 2 itself is amazing. But with its new features, Vue 3 is capable of taking our projects to new heights. I guess the most attractive feature in Vue 3 will be the component APIs. Evan You herself mentioned that the component APIs are inspired by Hooks in React. Although the hook and the API component are very similar, based on the code base we can see that they are completely different. We won’t discuss which is better or promising because I really think both of these frameworks are great.
Overall, it’s really happy to see Vue able to do what React does too. Let’s take a close look at the new features.
1. Vite
This is another Evan You artwork that aims to replace Webpack during Vue development (Currently only works for Vue). It is designed to work as quickly as its French name does.
Get started with Vite
The official repo gives us a simple way to create Vue 3 apps through Vite .
Npm
1 2 3 4 5 | $ npm init vite-app <project-name> $ cd <project-name> $ npm install $ npm run dev |
Yarn
1 2 3 4 5 | $ yarn create vite-app <project-name> $ cd <project-name> $ yarn $ yarn dev |
Start Dev Server
Everything just happened in a blink of an eye
1 2 3 4 5 6 7 8 9 10 | ❯ yarn dev yarn run v1.22.4 $ vite vite v1.0.0-rc.4 Dev server running at: > Local: http://localhost:3000/ > Network: http://192.168.3.2:3000/ > Network: http://10.80.67.216:3000/ |
Open http: // localhost: 3000 / and we can see the wellcome screen of Vue
vue-next-features
I have created a small application to demo new features of Vue 3 (link above). If you look at ** package.json **, the simplicity of the dependencies of vue-next-features will make you fall in love with Vite in no time. (I mean, who wouldn’t want to start with a simpler package.json?)
There is another Vue 3 “Hello World” repo ( vue-next-webpack-preview ) included with Webpack . It is also a good playground for you to learn about new features in Vue 3.
vue-next-features
1 2 3 4 5 6 7 8 9 10 11 | { ..., "dependencies": { "vite": "^1.0.0-rc.4", "vue": "^3.0.0-rc.5" }, "devDependencies": { "@vue/compiler-sfc": "^3.0.0-rc.5" } } |
vue-next-webpack-preview
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | { ..., "dependencies": { "vue": "^3.0.0-beta.2" }, "devDependencies": { "@vue/compiler-sfc": "^3.0.0-beta.2", "css-loader": "^3.4.2", "file-loader": "^6.0.0", "mini-css-extract-plugin": "^0.9.0", "url-loader": "^4.0.0", "vue-loader": "^16.0.0-alpha.3", "webpack": "^4.42.1", "webpack-cli": "^3.3.11",¥ "webpack-dev-server": "^3.10.3" } } |
2. Composition API
As Vue.js’ biggest change, the component API will become your next most frequently used and most popular feature. Just like React hooks, with the Vue component API will give you more customization.
Here is a list of the APIs that make up Vue 3. (There are actually more …)
- Reactivity:
computed
reactive
ref
readonly
watch
watchEffect
unref
toRefs
isRef
isProxy
isReactive
isReadonly
customRef
markRaw
shallowReactive
shallowReadonly
shallowRef
toRaw
- Lifecycle Hooks:
onBeforeMount
onBeforeUnmount
onBeforeUpdate
onMounted
onupdated
onErrorCapture
onRenderTracked
onRenderTriggered
onUnmount
onActivated
onDeactivated
Please visit the official Vue doc page to learn more about these APIs https://v3.vuejs.org/api/composition-api.html
3. Component Style
In Vue2
Use templates to define component content. In Vue 3, this old usage is still available. If you like this style, you can keep using it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <template> <button @click="count++">count: {{ count }}</button> </template> <script> const multiplier = 2 export default { data () { return { count: 0 } }, computed: { result () { return this.count * multiplier } }, mounted () { console.log(this.count) }, watch: { count (val, oldVal) { console.log(val, oldVal) } } } </script> |
In Vue3
To use the component API, you need to add setup to the export default. The code below is completely equivalent to the code above:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <template> <button @click="count++">count: {{ count }}</button> </template> <script> import { computed, reactive, toRefs, onMounted, watch } from 'vue' export default { setup () { const multiplier = 2 const state = reactive({ count: 0 }) const result = computed(() => { return state.count * multiplier }) onMounted(() => { console.log(state.count) }) watch(state.count, (val, oldVal) => { console.log(val, oldVal) }) return { ...toRefs(state) } } } </script> |
Continue with the new API
Here are four reasons why you should use the component API instead of the default Vue2 config template:
- Increased readability of code
- Avoid duplicate or redundant logics
- To group similar logic segments
- To reuse logic
Compared to Vue 2’s configuration type, logic is precisely broken down into smaller chunks so that you can group similar logic together easily. In this way, it also reduces the chance of getting confused from mismatched logic. This will increase productivity.
4. Advanced Reactivity API
Personally, I think this is no different from other reactivity APIs. But it really does offer customization capabilities like custom hooks and shallow layer modifications. It’s now part of the basic reactivity API according to the official Vue 3 documentation.
In the Vue component API documentation, the following APIs are listed as enhanced reactivity APIs.
- customRef: custom Hook
- markRaw: cannot be a
reactive
- NongReactive: The first class of the
reactive
object - shallowReadonly: The first class of the object
readonly
- shallowRef: The value of the object is not
reactive
- toRaw: restores
reactive
to normal object
Here is the official demo of customRef
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | import { customRef } from 'vue' const useDebouncedRef = (value, delay = 200) => { let timeout return customRef((track, trigger) => { return { get() { track() return value }, set(newValue) { clearTimeout(timeout) timeout = setTimeout(() => { value = newValue trigger() }, delay) }, } }) } export default { setup () { return { text: useDebouncedRef('some text') } } } |
5.v-enter-from / v-leave-from
In Vue 2, the <Transition>
component helps to handle the animation / transition of the brushonent. But the v-enter-active v-enter v-enter-to property is quite ambiguous to me. Sometimes I get confused which will happen first.
Now in Vue 3, those transition attribute names are more consistent and intuitive.
- v-enter => v-enter-from
- v-leave => v-leave-from
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <template> <transition name="fade"> <div v-show="show">fade transition</div> </transition> </template> <script> import { reactive, toRefs } from 'vue' export default { setup () { const state = reactive({ show: true }) setTimeout(() => { state.show = false }, 1000) setTimeout(() => { state.show = true }, 2000) return { ...toRefs(state) } } } </script> <style> .fade-enter-from, .fade-leave-to { opacity: 0; } .fade-enter-to, .fade-leave-from { opacity: 1; } .fade-enter-active, .fade-leave-active { transition: opacity 2000ms; } </style> |
The order will be as follows:
- v-enter-from (v-enter)
- v-enter-active
- v-enter-to
- v-leave-from (v-leave)
- v-leave-active
- v-leave-to
I firmly believe that the name above will be much easier than before
6. Allow multiple Root Elements
Vue2 will throw an error when using the multi root element. All elements must be nested within a single element
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!-- Error --> <template> <div>pitayan</div> <div>blog</div> </template> <!-- One Root Element only --> <template> <div> <div>pitayan</div> <div>blog</div> </div> </template> |
Vue3 has removed this annoying bug. I think this helps a lot when you really don’t want to nest your element in a parent container. Sometimes all you need is just put this element in the right place. It works similarly to React Fragments , helping you to reduce the problems associated with “nesting”:
1 2 3 4 5 6 7 | <!-- Vue 3 Multiple Root Element --> <!-- Okay --> <template> <div>pitayan</div> <div>blog</div> </template> |
7. Filters are no longer accepted (Removed)
I think many people think that filters can be a great feature of Vue.js. It really works well in Vue. (Example: format data / calculation, etc.).
So let’s see how 3 explains why the filter was removed:
Although the filter seems very convenient, it requires a custom syntax that breaks the rule of assuming syntax inside curly braces is “just JavaScript”.
I believe developing without a filter will not be affected, although it may take some time to switch to Vue 3. In my projects, the appearance of a filter is a rather rare case because I can replace that functionality with a method or computed easily. Because in my method / computed
, the method / computed
will be easier to read and understand than a filter .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <template> <!-- Deprecated (removed) & Error --> <span>{{ count | double }}</span> <!-- If you have to use fiter, make it a function --> <span>{{ double(count) }}</span> </template> <script> import { ref } from 'vue' export default { // Not working filters: { double (val) { return val * 2 } }, setup () { const count = ref(1) return { count, double: val => val * 2 } } } </script> |
8. New Async Component: Suspense
This is probably the only new Vue 3 feature that could be changed even after its official release. Inspiration is also from React Suspense . Therefore, the usage will be the same in my opinion.
Do you remember how to render the aynchronous data before when using Vue2? I think v-if
/ v-else
is the answer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <template> <div> <div v-for="i in items" :key="i">{{ i }}</div> <div v-else>loading...<div> </div> </template> <script> export default { data () { return { items: null } }, mounted () { this.items = await new Promise(resolve => { setTimeout(() => { return resolve(['one', 'two']) }, 3000) }) } } </script> |
With the Suspense
component, you will be able to do just that without having to manually adjust the conditions. By setting the default
and fallback
, the suspense
component will adjust the async event automatically
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | <template> <suspense> <template #default> <div v-for="i in items" :key="i">{{ i }}</div> </template> <template #fallback> Loading... </template> </suspense> </template> <script> export default { async setup () { const items = await new Promise(resolve => { setTimeout(() => { return resolve(['one', 'two']) }, 3000) }) return { items } } } </script> |
9. Show it somewhere else: Teleport
It’s another cool one based on React Portals . It provides the ability to insert components into a Note DOM.
What we do in Vue 2 to insert a custom component in <body>
(Of course there is a 3rd party Vue PortalVue plugin that provides such functionality):
1 2 3 4 5 6 7 8 9 10 | import Vue from 'vue' const Ctor = Vue.extends({ template: `<div>hello world</div>` }) const vm = new Ctor({ ... }).$mount() document.body.appendChild(vm.$el) |
To use this feature in Vue3, wrap the desired component with <Teleport>
and define the destination in props to
1 2 3 4 5 6 | <template> <Teleport to="body"> <div>Pitayan</div> </Teleport> </template> |
10. Allows multiple v-models
v-model
is used for two-dimensional data constraints in form elements or even custom components. In Vue 2, a custom component can only have one v-model
in a tag.
1 2 3 4 | <template> <my-input-form v-model="input" /> </template> |
Vue 3 has removed the limit and allows you to have multiple v-model
so you can specify separate constraints for more input elements.
1 2 3 4 5 6 7 | <template> <my-input-form v-model:first="inputFirst" v-model:second="inputSecond" /> </template> |
11. Global APIs
Vue3 provides us a number of APIs to help us control our components and instances better.
createApp
In Vue2, Vue
can be used as a constructor to return an Object instance. In Vue3, you can use createApp
instead. The use is completely the same:
1 2 3 4 5 6 7 8 9 10 11 | // Vue 2 import Vue from 'vue' import App from '@/src/App' new Vue({ el: '#app', components: { App } }) |
1 2 3 4 5 6 | // Vue 3 import { createApp } from 'vue' import App from '@/src/App' const app = createApp(App) |
What about other global methods like extend
component
mixin
and directive
?
The answer is quite similar, but you must use the instance method:
1 2 3 4 5 6 | // Global methods app.extend() app.component() app.mixin() app.directive() |
nextTick
I think nextTick
is a frequently used API because a lot of logic is really asynchronous and they need to be arranged for the next DOM update cycle.
In Vue 2, nextTick
is an instance method.
1 2 3 4 5 6 7 8 9 | export default { ..., mounted () { this.$nextTick(() => { console.log('pitayan') }) } } |
Vue3 allows you to use nextTick
as a standalone function:
1 2 3 | // nextTick function type export declare function nextTick(fn?: () => void): Promise<void>; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // An official doc Example import { nextTick } from 'vue' export default { setup () { const message = ref('Hello, Pitayan!') const changeMessage = async newMessage => { message.value = newMessage await nextTick() console.log('Now DOM is updated') } } } |
Some other helper functions
These new APIs will be extremely useful when you need to add controls for much more abstract situations. Personally, I think they can be used frequently in third-party libraries.
- h : returns virtual note
- createRenderer : custom render can be used for cross-environment
- defineComponent : Enter the Object you passed
- defineAsyncComponent : Load the async component as needed
- to resolveComponent : resolves a component in the current instance scope
- ResolutionDirective : Gets
directive
from the current instance scope - withDirectives : Apply
directive
to aVNode
The article was translated by me by author Yanze Dai. Link the original article here https://dev.to/daiyanze/vue-3-new-features-summary-2cie