vue3基础

348 阅读2分钟

先贴个官网,vue官网个人觉得比react易读点😂 vue3

相对vue2,vue3的一些不兼容改动:

被移除的 API
  • keyCode 作为 v-on 修饰符的支持
比如<input v-on:keyup.enter="submit" />
改成了在 v-on 上使用按键修饰符
<input v-on:keyup.page-down="nextPage">
  • $on$off$once 实例方法,主要是eventBus的使用
  • 过滤器 (filter)用计算属性computed/方法 method代替
  • $children property 已被移除,这里需要使用$refs
  • $destroy 实例方法。用户不应再手动管理单个 Vue 组件的生命周期。
  • 全局函数 set 和 delete 以及实例方法 $set 和 $delete。因为vue3用的绑定原理是Proxy
非兼容的变更 模板指令
  • 组件上 v-model 用法已更改,以替换 v-bind.sync
  • <template v-for> 和非 v-for 节点上的 key 用法已更改
  • 在同一元素上使用的 v-if 和 v-for 优先级已更改,vue3for优先,vue2if优先
  • v-bind="object" 现在排序敏感,如下,vue2的id是red,vue3的排名是谁在后面就是谁
<div id="red" v-bind="{ id: 'blue' }"></div>
  • v-on:event.native 修饰符已移除
  • v-for 中的 ref 不再注册 ref 数组
组件
  • 只能使用普通函数创建函数式组件
  • functional attribute 在单文件组件 (SFC) 的 <template> 和 functional 组件选项中被废弃
  • 异步组件现在需要使用 defineAsyncComponent 方法来创建
  • 组件事件现在需要在 emits 选项中声明
渲染函数
  • 渲染函数 API 更改.以前是形参h现在直接引入import { h } from 'vue'
  • $scopedSlots property 已移除,所有插槽都通过 $slots 作为函数暴露
  • $listeners 被移除或整合到 $attrs
  • $attrs 现在包含 class 和 style attribute
自定义元素
  • 自定义元素检测现在在模板编译时执行
  • 特殊的 is attribute 的使用被严格限制在被保留的 <component> 标签中

重点组合式API

在vue中创建名字为project项目通常有3种办法(cli4/5vite还有vue ui图形化工具):

// vue cli创建,通常3的话不会用vli2,而是用4或者5
vue create project

// vite创建
npm init vite@latest project --template vue
yarn create vite project --template vue
pnpm create vite project -- --template vue

// vue-cli基于webpack构建,并且有一些默认的预设配置,在服务启动之前会把所有代码打包成bundle,
// 然后启动服务,提供给浏览器使用,所以vue-cli4在开发一些大型项目的时候,越到后面服务启动的越慢。

// vite为了解决服务器动慢的问题,使用的ESbuild预编译依赖,比JS编写的打包器构建依赖要快很多倍。
// 为了避免每次启动项目的时候要重建整个包,Vite把预构建的回来缓存到了node_modules/.vite。

组合式 API官网说法:将同一个逻辑关注点相关代码收集在一起,在setup中使用。


<template>
  <!-- Teleport提供了一种允许我们控制在DOM中哪个父节点下渲染HTML,比如新手用户指导弹窗 -->
  <teleport to="具体的元素上(dom,#id,.class)">
    <view> 弹窗内容 </view>
  </teleport>
  <h1 ref="domref" class="props-color">{{ propsdata }}</h1>
  <button class="count-color" @click="count++">count is: {{ count }}</button>
</template>

<script>
import { ref, reactive, onMounted, toRefs, computed, watch } from 'vue'
export default {
    // // 父组件注册
    //  provide: {
    //         demo: 'haha'
    //         }   
    //     // 或者main.js全局引入
    // app.provide('guide', 'Vue3 guide') // 挂载前注入
    // // 子组件拿到
    //  inject: {
    //     book: {
    //         from: 'guide'
    //     },
    //     demo: 'demo'
    // },
  props: {
      propsdata: {
        type: String,
        required: true,
        validator: value => {
          return [
            'syncing',
            'synced',
            'version-conflict',
            'error'
          ].includes(value)
        }
      }
  },

  setup(props, context) {
   // setup是在创建之前执行
    // setup中除了没有beforeCreate和created生命周期,其他的生命周期都有
    // 放在Vue2 created的数据交互请求一般会放在setup,这里执行完如果外层有beforeCreate,created就会去执行他们两个,再回来这里的onBeforeMount/onMounted然后再去执行外面的onBeforeMount/onMounted
    // setup第一个参数是props,是响应式的,不能使用es6解构,否则消除prop的响应式
    // setup中第二个参数是一个普通JS对象,暴露了其他可能在setup中有用的值,它是一个普通的JS对象,不是响应式的。可以使用es6解构
    // 如果要解构prop,可以在setup函数中使用toRefs函数来完成操作
    let { propsdata } = toRefs(props)
    // ref()函数用来根据给定的值创建一个响应式的数据对象
    // ref()函数调用的返回值是一个对象,这个对象上只包含一个value属性
    const count = ref(1)
    
    // 获取一个dom元素
    const domref = ref(null)
    
    const init = () => {
      console.log('hello world init')
    }

    // 我们可以在setup里注册生命周期钩子函数,组合式API上的生命周期钩子函数不变,但是有前缀on,onMounted
    onMounted(init)

    // 独立的computed计算属性
    // console.log(count.value)
    const com = computed(() =>count.value * 2 )

    // watch不用加on就可以使用,可以接收三个参数
    watch(count, (newVal, oldVal) => {
      console.log(count.value, newVal, oldVal)
    })
    // watch监听比较一个数组 或对象的值,这些值是响应式的,要求它有一个由值构成的副本,这里需要注意一下
    const arr = reactive([1, 2, 3, 4])
    watch(
      () => [...arr],
      (newValue, oldValue) => {
        console.log(newValue, oldValue)
      }
    )
    
    // 如果要为JS对象创建响应式状态,可以用reactive
    const obj = reactive({ title: 'Vue3 Guide' })
    // reactive用法与ref用法相似,也是将数据变成响应式数据,不同的是ref用于基本数据类型
    // reactive用于复杂数据类型,reactive的转换是'深度转换',它会影响传递对象的所有嵌套property
   // watch监听深度嵌套对象时,仍然需要使用deep选项设置为true
    // deep监听的时候两个都是新值,没有旧值了
    watch(
      () => obj,
      (newValue, oldValue) => {
        console.log(newValue, oldValue)
      },
      {deep: true}
    )
    
    // 可以跟vue2中的data啊,method啊一起用
    data() {
        return {
            color: 'red'
        }
    }
    return {
      count,
      obj,
      domref,
      ...
    }

  }
}
</script>

// 单文件组件组合式API语法糖 script setup,无需return
// 这里面的代码会在每次组件实例被创建的时候执行
// 任何在这声明的的顶层绑定(变量、函数,以及import引入的内容)都能在模板中直接使用
// <script setup>可以和<script>一起用
<script setup>
import { ref } from 'vue'
defineProps({
    data: String// 这里接受参数
})
let theme = { color: 'green' }
// ref值在模板中使用会自动解构
const count = ref(0)

// defineExpose,父组件通过模板ref方式获取到当前组件的实例
defineExpose({
    count
})
</script>
<style scoped>
/* 单文件组件状态驱动的 CSS 变量 (<style> 中的 v-bind) */
.props-color {
  color: v-bind(color);
}
.count-color {
  color: v-bind("theme.color");
}
</style>

vuex状态管理器从vuex3升级成了vuex4,不过推荐pinia

路由的修改:

创建从new Router()变成了createRouter 三个路由模式:

'history'createWebHistory()
'hash'createWebHashHistory()
'abstract'createMemoryHistory()

去掉*比如在router.push('/not/found/url')的时候 router.onReady()函数已被替换为router.isReady()不带任何参数并返回 Promise 的函数 transition现在keep-alive必须通过API在内部使用:RouterView v-slot

<router-view v-slot="{ Component }">
  <transition>
    <keep-alive>
      <component :is="Component" />
    </keep-alive>
  </transition>
</router-view>

append to修改了

replace
<router-link to="child-route" append>to relative child</router-link>
with
<router-link :to="append($route.path, 'child-route')">
  to relative child
</router-link>

mixin 中的导航守卫被忽略

router.match, 和router.resolve已合并在一起router.resolve router.getMatchedComponents被删除,可以从以下位置检索匹配的组件router.currentRoute.value.matched

所有导航,包括第一个导航,现在都是异步的

parent删除,可以通过下面访问到

const parent = this.$route.matched[this.$route.matched.length - 2]

params如果定义了参数没有传参就会报错

// given the following route:
const routes = [{ path: '/users/:id', name: 'user', component: UserDetails }]

// Missing the `id` param will fail
router.push({ name: 'user' })
router.resolve({ name: 'user' })

具体路由变更可以参考官网