前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【转载】vue 3.x 如何有惊无险地快速入门 —— 一文扫遍 vue2 与 3 的差异点

【转载】vue 3.x 如何有惊无险地快速入门 —— 一文扫遍 vue2 与 3 的差异点

作者头像
FungLeo
发布2020-05-27 10:28:08
1.9K0
发布2020-05-27 10:28:08
举报

【转载】vue 3.x 如何有惊无险地快速入门 —— 一文扫遍 vue2 与 3 的差异点

前言

本文所分享的是关于 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 代替。

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 来完成。开发者可以把更多的时间放在业务的开发上。

代码语言:javascript
复制
<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 插槽的内容 。

代码语言:javascript
复制
<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)

代码语言:javascript
复制
<!-- 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>

我觉得这是好事,合二为一,不会让人有一点点的困惑。

简写

代码语言:javascript
复制
<TestComponent>
  <template #one="{ name }">Hello {{ name }}</template>
</TestComponent>

指令动态参数

适用版本:Version: 2.x,Version: 3.x

代码语言:javascript
复制
<!-- 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

代码语言:javascript
复制
<!-- 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

代码语言:javascript
复制
<!-- 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 的内部实现也有了小调整

元素

代码语言:javascript
复制
<input v-model="xxx">

<!-- would be shorthand for: -->

<input
  :model-value="xxx"
  @update:model-value="newValue => { xxx = newValue }"
>

组件

代码语言:javascript
复制
<MyComponent v-model:aaa="xxx"/>

<!-- would be shorthand for: -->

<MyComponent
  :aaa="xxx"
  @update:aaa="newValue => { xxx = newValue }"
/>

不过好像组 alpha 版本的还不支持 v-model:aaa="xxx"

函数组件

适用版本: vue 3.x

代码语言:javascript
复制
<!-- 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 方法创建

代码语言:javascript
复制
import { defineAsyncComponent } from 'vue'
const AsyncComp = defineAsyncComponent(() => import('./Foo.vue'))

全局 api

适用版本: vue 3.x

在 vue 2.x 中

代码语言:javascript
复制
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 中

代码语言:javascript
复制
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.原来的方式保留

代码语言:javascript
复制
<input v-model="foo">

2.可绑定多个 v-model

代码语言:javascript
复制
<InviteeForm
  v-model:name="inviteeName"
  v-model:email="inviteeEmail"
/>

其实上面这种方式就相当于之前的 .sync 。

3.额外处理

代码语言:javascript
复制
<Comp
   v-model:foo.trim="text"
   v-model:bar.number="number" />

我们可以给这个属性添加额外的处理

指令的钩子函数

适用版本:Version 3.x

在 vue 3.x 中 指令的钩子函数仿照了组件中的钩子函数命名规则

vue 2.x 时

代码语言:javascript
复制
const MyDirective = {
  bind(el, binding, vnode, prevVnode) {},
  inserted() {},
  update() {},
  componentUpdated() {},
  unbind() {}
}

vue 3.0 中

代码语言:javascript
复制
const MyDirective = {
  beforeMount(el, binding, vnode, prevVnode) {},
  mounted() {},
  beforeUpdate() {},
  updated() {},
  beforeUnmount() {}, // new
  unmounted() {}
}

transition

适用版本:Version 3.x

<transition> 作为组件的根元素时,外部切换不会触发过渡效果

vue 2.x

代码语言:javascript
复制
<!-- modal component -->
<template>
  <transition>
    <div class="modal"><slot/></div>
  </transition>
</template>

<!-- usage -->
<modal v-if="showModal">hello</modal>

vue 3.x

代码语言:javascript
复制
<!-- 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-fromv-leave重命名成 v-enter-from

代码语言:javascript
复制
.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 回传的状态自定义,不管是样式还是类。

代码语言:javascript
复制
<router-link to="/" custom v-slot="{ href, navigate, isActive }">
  <li :class="{ 'active': isActive }">
    <a :href="href" @click="navigate">
      <Icon>home</Icon><span class="xs-hidden">Home</span>
    </a>
  </li>
</router-link>

也就是说,新版本的 Router 就更加的纯粹,只提供给我们一些参数,让我们自己利用这些参数来实现不同的场景。

meta 合并
代码语言:javascript
复制
{
  path: '/parent',
  meta: { requiresAuth: true, isChild: false },
  children: [
    { path: 'child', meta: { isChild: true }}
  ]
}

当访问 /parent/child 时,子路由中的 meta 如下:

代码语言:javascript
复制
{ requiresAuth: true, isChild: true }

合并策略与 Object.assign 类似

路由匹配所有
代码语言:javascript
复制
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 匹配所有路由的写法已经改变了,不是旧版本的 * ,在新的版本里写法参考上面的示例代码

获取当前路由信息
代码语言:javascript
复制
import router from '../router'
export default {
  setup () {
    const currentRoute = router.currentRoute.value
    console.log(currentRoute)
  }
}

引入的 router 为我们通过 createRouter() 方法创建的对象

代码语言:javascript
复制
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
  history: createWebHashHistory(),
  routes
})

routes 路由为我们定义的路由数组,跟旧版本的一样。

样式 scoped

适用版本:Version: 2.x, 3.x

旧版本写法

代码语言:javascript
复制
/* 深度选择器 */
/*方式一:*/
>>> .foo{ }
/*方式二:*/
/deep/ .foo{ }
/*方式三*/
::v-deep .foo{ }

新版本写法

代码语言:javascript
复制
/* 深度选择器 */
::v-deep(.foo) {}

除了上面的深度选择器外,还有下面的两个,写法也差不多。

代码语言:javascript
复制
/* 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 处理完后还是什么属性值。

异步组件

代码语言:javascript
复制
import { defineAsyncComponent } from "vue"
    
// simple usage
const AsyncFoo = defineAsyncComponent(() => import("./Foo.vue"))

写法上与之前有些不一样。

动态路由

适用版本 Router 4

添加了几个方法

  1. router.addRoute(route: RouteRecord) 动态添加路由
  2. router.removeRoute(name: string | symbol),动态删除路由
  3. router.hasRoute(name: string | symbol): boolean ,判断路由是否存在
  4. router.getRoutes(): RouteRecord[] 获取路由列表
代码语言:javascript
复制
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

代码语言:javascript
复制
const Foo = defineComponent({
  emits: {
    submit: (payload: { email: string; password: string }) => {
      // perform runtime validation
    }
  },
    
  methods: {
    onSubmit() {
      this.$emit('submit', {
        email: 'foo@bar.com',
        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
  • on, off 和 $once

阅读完本文我相信你大概对 vue 3 有了一个基本的认识。虽然本文会不让你瞬间成为 vue 3.x 的驾驭者,但怎么说也让你含蓄地体验了一把 vue 3.x 的新特性。特别是 composition API 即使本文没有详细写出来,但通过补充的链接,你也能阅读到它的所有。我觉得 composition API 真的很棒。

vue 计划

github.com/vuejs/vue/p…

转载说明

这篇文章写得太好了,只要阅读本文,即可快速的了解 vue2.0vue3.0 的大多数差异。你既可以把本文当成一个上手资料,也可以把本文当成一个字典查询。因为写得实在太好,因此我在取得了作者同意的情况下,转载了此文。

在这里插入图片描述
在这里插入图片描述

作者: QUIKER

文章首发地址: https://juejin.im/post/5ec537486fb9a047bb6a4204#heading-12

作者个人网站: http://yunkus.com/

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-05-24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 【转载】vue 3.x 如何有惊无险地快速入门 —— 一文扫遍 vue2 与 3 的差异点
    • 前言
      • 新增
        • composition-api
        • teleport 组件
        • Suspense
      • 变更
        • 插槽 slot 语法
        • 指令动态参数
        • Tree-shaking
        • .sync 大变样
        • 函数组件
        • 全局 api
        • v-model
        • 指令的钩子函数
        • transition
        • transition-class
        • Router
        • 样式 scoped
        • 属性值修正
        • 异步组件
        • 动态路由
        • emits-option
        • 组件根元素数量
      • vue 3.x 中费弃
        • vue 计划
      • 转载说明
        • 这篇文章写得太好了,只要阅读本文,即可快速的了解 vue2.0 与 vue3.0 的大多数差异。你既可以把本文当成一个上手资料,也可以把本文当成一个字典查询。因为写得实在太好,因此我在取得了作者同意的情况下,转载了此文。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档