前言
本文所分享的是關于 vue 3.x 在用法上的改變,而不是在代碼實作上的不同,
雖然 vue2 到 vue3 的實作大改,但在用法上變化基本不大,比較明顯的一個變化就是添加了 setup(){} 函式了,幾乎所有的配置變成了以函式的方式進行定義,即使是這樣,但小改動還是很多的,本文主要分享的是 vue 2.x 與 vue 3.x 之間一些常見用法的差異,雖然記錄的不多,但也不算少,本文資料來源:github.com/vuejs/rfcs/…
當然這里默認你已經熟練掌握了 vue 2.x 的使用,下面我們就來看看,
新增
composition-api
1.邏輯復用和代碼組織
這是 vue 3.0 的一個核心變更了,除了改了我們定義狀態的書寫方式外,也為我們提供體驗更棒的邏輯復用和代碼組織,新的方式可以讓你把同一個業務邏輯的代碼(狀態,計算屬性,方法等)都放到一塊,這聽起來可能有點不明不白,但如果你寫過比較復雜的組件,你就會發現,這個好,舊版本的 created、beforeCreated 鉤子函式已費棄,在 vue 3.0 中用 setup 代替,
另外注意:光理論是不夠的,在此贈送2020最新企業級 Vue3.0/Js/ES6/TS/React/node等實戰視頻教程,想學的可進裙 519293536 免費獲取,小白勿進哦!
2.更好的型別推斷
更好的支持 TypeScript,
可以看這篇文章:github.com/vuejs/rfcs/…
或者閱讀這篇(中文):vue-composition-api-rfc.netlify.app/zh/
完整的 API:vue-composition-api-rfc.netlify.app/zh/api.html
teleport 組件
teleport 組件它只是單純的把定義在其內部的內容轉移到目標元素中,在元素結構上不會產生多余的元素,當然也不會影響到組件樹,它相當于透明的存在,為什么要有這個組件?為了有更好的代碼組織體驗,比如:有時,組件模板的一部分在邏輯上屬于此組件,但從技術角度來看(如:樣式化需求),最好將模板的這一部分移動到 DOM 中的其他位置,
比如:一些 UI 組件庫的 模態窗、對話框、通知,下拉選單等需要通過 z-index 來控制層級關系,如果都只是在不同的組件或者元素層級中,那么 z-index 的層級順序就難以保證,可能你會說很多 UI 庫不是都已經是這樣的實作了的嗎?至于這個 UI 庫是如何實作的,我猜應該是直接操作 DOM,為什么還要提供這個 teleport 組件呢?可能是因為vue 本身的使命使然:盡量不讓開發者直接操作 DOM,這些事都統一由 VUE 來完成,開發者可以把更多的時間放在業務的開發上,
<teleport to="#modals">
<div>A</div>
</teleport>
<teleport to="#modals">
<div>B</div>
</teleport>
<!-- result-->
<div id="modals">
<div>A</div>
<div>B</div>
</div>
復制代碼
更多細節可看:github.com/vuejs/rfcs/…
Suspense
加載異步組件,在異步組件加載完成成并完全渲染之前 suspense 會先顯示 #fallback 插槽的內容 ,
<Suspense>
<template>
<Suspended-component />
</template>
<template #fallback>
Loading...
</template>
</Suspense>
復制代碼
#fallback 其實是插件 v-solt 的簡寫,而第一個 template 沒有給,則為默認插槽,
變更
插槽 slot 語法
github.com/vuejs/rfcs/…
適用版本:Version: 2.x,Version: 3.x
未來版本的 vue 中可以說合二為一了(slot 和 slot-scope)
<!-- vue 2.x -->
<foo>
<bar slot="one" slot-scope="one">
<div slot-scope="bar">
{{ one }} {{ bar }}
</div>
</bar>
<bar slot="two" slot-scope="two">
<div slot-scope="bar">
{{ two }} {{ bar }}
</div>
</bar>
</foo>
<!-- vue 3.x -->
<foo>
<template v-slot:one="one">
<bar v-slot="bar">
<div>{{ one }} {{ bar }}</div>
</bar>
</template>
<template v-slot:two="two">
<bar v-slot="bar">
<div>{{ two }} {{ bar }}</div>
</bar>
</template>
</foo>
復制代碼
我覺得這是好事,合二為一,不會讓人有一點點的困惑,
簡寫
<TestComponent>
<template #one="{ name }">Hello {{ name }}</template>
</TestComponent>
復制代碼
指令動態引數
適用版本:Version: 2.x,Version: 3.x
<!-- v-bind with dynamic key -->
<div v-bind:[key]="value"></div>
<!-- v-bind shorthand with dynamic key -->
<div :[key]="value"></div>
<!-- v-on with dynamic event -->
<div v-on:[event]="handler"></div>
<!-- v-on shorthand with dynamic event -->
<div @[event]="handler"></div>
<!-- v-slot with dynamic name -->
<foo>
<template v-slot:[name]>
Hello
</template>
</foo>
<!-- v-slot shorthand with dynamic name -->
<!-- pending #3 -->
<foo>
<template #[name]>
Default slot
</template>
</foo>
復制代碼
簡單地說就是指令名,事件名,插槽名,都可以使用變數來定義了,
Tree-shaking
適用版本:Version: 3.x
在 vue 3 中不會把所有的 api 都打包進來,只會 打包你用到的 api
<!-- vue 2.x -->
import Vue from 'vue'
Vue.nextTick(() => {})
const obj = Vue.observable({})
<!-- vue 3.x -->
import Vue, { nextTick, observable } from 'vue'
Vue.nextTick // undefined
nextTick(() => {})
const obj = observable({})
復制代碼
即我們在專案中用什么什么,就只會打包什么,不會像 vue 2.x 那樣全部 api 都打包,
.sync 大變樣
適用版本: vue 3.x
<!-- vue 2.x -->
<MyComponent v-bind:title.sync="title" />
<!-- vue 3.x -->
<MyComponent v-model:title="title" />
復制代碼
也就是說,vue 3.0 又去掉了 .sync ,合并到了 v-model 里,而 v-model 的內部實作也有了小調整
元素
<input v-model="xxx">
<!-- would be shorthand for: -->
<input
:model-value=https://www.cnblogs.com/chengxuyuanaa/p/"xxx"
@update:model-value=https://www.cnblogs.com/chengxuyuanaa/p/"newValue =https://www.cnblogs.com/chengxuyuanaa/p/> { xxx = newValue }"
>
復制代碼
組件
<MyComponent v-model:aaa="xxx"/>
<!-- would be shorthand for: -->
<MyComponent
:aaa="xxx"
@update:aaa="newValue =https://www.cnblogs.com/chengxuyuanaa/p/> { xxx = newValue }"
/>
復制代碼
不過好像組 alpha 版本的還不支持 v-model:aaa="xxx"
函陣列件
適用版本: vue 3.x
<!-- vue 2.x -->
const FunctionalComp = {
functional: true,
render(h) {
return h('div', `Hello! ${props.name}`)
}
}
<!-- vue 3.x -->
import { h } from 'vue'
const FunctionalComp = (props, { slots, attrs, emit }) => {
return h('div', `Hello! ${props.name}`)
}
復制代碼
不再需要 functional:true 選項,<template functional> 不再支付
異步組件也必需通過 api 方法創建
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() => import('./Foo.vue'))
復制代碼
全域 api
適用版本: vue 3.x
在 vue 2.x 中
import Vue from 'vue'
import App from './App.vue'
Vue.config.ignoredElements = [/^app-/]
Vue.use(/* ... */)
Vue.mixin(/* ... */)
Vue.component(/* ... */)
Vue.directive(/* ... */)
Vue.prototype.customProperty = () => {}
new Vue({
render: h => h(App)
}).$mount('#app')
復制代碼
在 vue 3.x 中
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
app.config.isCustomElement = tag => tag.startsWith('app-')
app.use(/* ... */)
app.mixin(/* ... */)
app.component(/* ... */)
app.directive(/* ... */)
app.config.globalProperties.customProperty = () => {}
app.mount(App, '#app')
復制代碼
可以看到,創建實體的方式也改變了,一些全域的 api 方法也不在全域上了,而是放到了實體上,
更多的改變可以看這里:github.com/vuejs/rfcs/…
v-model
適用版本:Version 3.x
1.原來的方式保留
<input v-model="foo">
復制代碼
2.可系結多個 v-model
<InviteeForm
v-model:name="inviteeName"
v-model:email="inviteeEmail"
/>
復制代碼
其實上面這種方式就相當于之前的 .sync ,
3.額外處理
<Comp
v-model:foo.trim="text"
v-model:bar.number="number" />
復制代碼
我們可以給這個屬性添加額外的處理
指令的鉤子函式
適用版本:Version 3.x
在 vue 3.x 中 指令的鉤子函式仿照了組件中的鉤子函式命名規則
vue 2.x 時
const MyDirective = {
bind(el, binding, vnode, prevVnode) {},
inserted() {},
update() {},
componentUpdated() {},
unbind() {}
}
復制代碼
vue 3.0 中
const MyDirective = {
beforeMount(el, binding, vnode, prevVnode) {},
mounted() {},
beforeUpdate() {},
updated() {},
beforeUnmount() {}, // new
unmounted() {}
}
復制代碼
transition
適用版本:Version 3.x
當 <transition> 作為組件的根元素時,外部切換不會觸發過渡效果
vue 2.x
<!-- modal component -->
<template>
<transition>
<div class="modal"><slot/></div>
</transition>
</template>
<!-- usage -->
<modal v-if="showModal">hello</modal>
復制代碼
vue 3.x
<!-- modal component -->
<template>
<transition>
<div v-if="show" class="modal"><slot/></div>
</transition>
</template>
<!-- usage -->
<modal :show="showModal">hello</modal>
復制代碼
也就是說我們只能在 <transition> 內使用切換,
transition-class
重命名兩個過渡類
v-enter 重命名成v-enter-from,v-leave重命名成 v-enter-from,
.v-enter-from, .v-leave-to {
opacity: 0;
}
.v-leave-from, .v-enter-to {
opacity: 1
}
復制代碼
Router
適合版本:Version: Vue (2.x / 3.x) Vue Router (3.x / 4.x)
router-link 變動
router-link 添加 scoped-slot API 和 custom 屬性,并移除了 tag 屬性和 event 屬性,
添加 scoped-slot 有什么用呢?以前只能通過 active-class 來改變元素樣式的,現在有了 scoped-slot 之后,我們就更加靈活了,可以根據 scoped-slot 回傳的狀態自定義,不管是樣式還是類,
<router-link to="/" custom v-slot="{ href, navigate, isActive }">
<li :class="{ 'active': isActive }">
<a :href=https://www.cnblogs.com/chengxuyuanaa/p/"href" @click="navigate">
<Icon>home</Icon><span class="xs-hidden">Home</span>
</a>
</li>
</router-link>
復制代碼
也就是說,新版本的 Router 就更加的純粹,只提供給我們一些引數,讓我們自己利用這些引數來實作不同的場景,
meta 合并
{
path: '/parent',
meta: { requiresAuth: true, isChild: false },
children: [
{ path: 'child', meta: { isChild: true }}
]
}
復制代碼
當訪問 /parent/child 時,子路由中的 meta 如下:
{ requiresAuth: true, isChild: true }
復制代碼
合并策略與 Object.assign 類似
路由匹配所有
const routes = [
{
path: '/',
name: 'Home',
component: () => import(/* webpackChunkName: "Home" */ '../views/Home.vue')
},
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
},
{
path: '/:catchAll(.*)',
name: 'All',
component: () => import(/* webpackChunkName: "All" */ '../views/Home.vue')
}
]
復制代碼
這里有一個需要注意的就是 vue-router 匹配所有路由的寫法已經改變了,不是舊版本的 * ,在新的版本里寫法參考上面的示例代碼
獲取當前路由資訊
import router from '../router'
export default {
setup () {
const currentRoute = router.currentRoute.value
console.log(currentRoute)
}
}
復制代碼
引入的 router 為我們通過 createRouter() 方法創建的物件
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(),
routes
})
復制代碼
routes 路由為我們定義的路由陣列,跟舊版本的一樣,
樣式 scoped
適用版本:Version: 2.x, 3.x
舊版本寫法
/* 深度選擇器 */
/*方式一:*/
>>> .foo{ }
/*方式二:*/
/deep/ .foo{ }
/*方式三*/
::v-deep .foo{ }
復制代碼
新版本寫法
/* 深度選擇器 */
::v-deep(.foo) {}
復制代碼
除了上面的深度選擇器外,還有下面的兩個,寫法也差不多,
/* slot content 起作用 */
::v-slotted(.foo) {}
/* 全域 */
::v-global(.foo) {}
復制代碼
屬性值修正
適用版本:Version: 3.x
vue 本身會對元素的屬性作相應的處理,在舊版本的 vue 中處理如下:
| 運算式 | 正常 | 最終處理成 |
|---|---|---|
:attr="null" |
/ | draggable="false" |
:attr="undefined" |
/ | / |
:attr="true" |
foo="true" |
draggable="true" |
:attr="false" |
/ | draggable="false" |
:attr="0" |
foo="0" |
draggable="true" |
attr="" |
foo="" |
draggable="true" |
attr="foo" |
foo="foo" |
draggable="true" |
attr |
foo="" |
draggable="true" |
新版本處理方式:
| 運算式 | 正常 | 最終處理成 |
|---|---|---|
:attr="null" |
/ | / |
:attr="undefined" |
/ | / |
:attr="true" |
foo="true" |
draggable="true" |
:attr="false" |
foo="false" |
draggable="false" |
:attr="0" |
foo="0" |
draggable="0" |
attr="" |
foo="" |
draggable="" |
attr="foo" |
foo="foo" |
draggable="foo" |
attr |
foo="" |
draggable="" |
在新版本中基本保持了原樣,也就是我們給元素添加什么屬性值,最好 vue 處理完后還是什么屬性值,
異步組件
import { defineAsyncComponent } from "vue"
// simple usage
const AsyncFoo = defineAsyncComponent(() => import("./Foo.vue"))
復制代碼
寫法上與之前有些不一樣,
動態路由
適用版本 Router 4
添加了幾個方法
router.addRoute(route: RouteRecord)動態添加路由router.removeRoute(name: string | symbol),動態洗掉路由router.hasRoute(name: string | symbol): boolean,判斷路由是否存在router.getRoutes(): RouteRecord[]獲取路由串列
router.addRoute({
path: '/new-route',
name: 'NewRoute',
component: NewRoute
})
// add to the children of an existing route
router.addRoute('ParentRoute', {
path: 'new-route',
name: 'NewRoute',
component: NewRoute
})
router.removeRoute('NewRoute')
// normalized version of the records added
const routeRecords = router.getRoutes()
復制代碼
詳情可見:github.com/vuejs/rfcs/…
emits-option
const Foo = defineComponent({
emits: {
submit: (payload: { email: string; password: string }) => {
// perform runtime validation
}
},
methods: {
onSubmit() {
this.$emit('submit', {
email: '[email protected]',
password: 123 // Type error!
})
this.$emit('non-declared-event') // Type error!
}
}
})
復制代碼
現在的 $emit() 方法在用法上沒變,但需要額外多定義 emits 物件,但要注意的是現在 alpha 版本還不支持 TypeScript
組件根元素數量
vue 3 后組件不再限制 template 中根元素的個數(舊的版本之前是只能有一個根元素),
vue 3.x 中費棄
- beforeCreate、created
- filters
- keycode
- inline-template
- data-object
off 和 $once
1.閱讀完本文我相信你大概對 vue 3 有了一個基本的認識,雖然本文會不讓你瞬間成為 vue 3.x 的駕馭者,但怎么說也讓你含蓄地體驗了一把 vue 3.x 的新特性,特別是 composition API 即使本文沒有詳細寫出來,但通過補充的鏈接,你也能閱讀到它的所有,我覺得 composition API 真的很棒,
2.光理論是不夠的,在此贈送2020最新企業級 Vue3.0/Js/ES6/TS/React/node等實戰視頻教程,想學的可進裙 519293536 免費獲取,小白勿進哦!
本文的文字及圖片來源于網路加上自己的想法,僅供學習、交流使用,不具有任何商業用途,著作權歸原作者所有,如有問題請及時聯系我們以作處理
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/73788.html
標籤:JavaScript
