vue3特性笔记

我们把vue3 和 vue2 的项目进行对比来去说明

main.js

通过 按需导入的 createApp 方法来来构建 vue 实例
通过 vue实例.use 方法来挂载插件(router、vuex)
没有了 Vue 构造方法,无法再挂载原型

App.vue

组件内部结构无变化,依然是
tempalte
script
style
<template> 标签中支持多个根标签

store/index.js

通过 按需导入的 createStore 方法来来构建 store 实例
无需再通过 Vue.use(Vuex) 的形式进行挂载

router/index.js

通过 按需导入的 createRouter 方法来构建 router 实例
通过 按需导入的 createWebHashHistory 方法来创建 hash 模式对象,进行路由模式指定
无需再通过 Vue.use(VueRouter) 的形式进行挂载
routes 路由表的定义无差别

综上所述,在 vue3 的初始化项目中,与 vue2 对比的最大差异其实就是两点:

1、vue3 使用 按需导入的形式 进行初始化操作
2、<template> 标签中支持多个根标签


vue3新语法

setup 与 reactive

setup 函数也是 Composition API 的入口函数,我们的变量、方法都是在该函数里定义的,不再使用vue2中的data而是setup。
reactive 方法是用来创建一个响应式的数据对象,该API也很好地解决了Vue2通过 defineProperty 实现数据响应式的缺陷

//app.vue
<template>
 <div id="app"> 
   <p>{{ state.name }}</p>
 </div>
</template>
<script lang="ts">
import {
 defineComponent, 
 reactive, 
} from 'vue' 

export default defineComponent({
 name: 'App', 
 setup(props, { slots, attrs, emit }) {
   //let name = 'abc'  //return的name 是abc,因为setInterval()是异步的

   //reactive的name就会是aaa1111,每2秒变化一次
   const state = reactive({
     name: 'aaa',
   })
   setInterval(() => {
     state.name += '1' 
   }, 2000)   
   
   //不能...state,因为...是提取值,那显示的就不是reactive对象了
   return {
     state 
   }
 },
})
</script>

上面使用name必须state.name,不方便,另一个解决办法如下

export default {
  setup(props, { attrs, slots, emit }) {
    ...
  }
}

访问组件的 property

执行 setup 时,组件实例尚未被创建。因此,你只能访问以下 property: props attrs slots emit

换句话说,你将无法访问以下组件选项:
data computed methods


生命周期

在Vue3中,这些生命周期部分有所变化,并且调用的方式也有所改变

Vue2 Vue3
beforeCreate setup
created setup
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestory onBeforeUnmount
destoryed onUnmounted

Props的不同处

Ts可指定类型,而不是以往所有对象参数都是object的any类型
另外Props可以提出来

//app.vue
<template>
  <div id="app"> 
    <HelloWorld msg="v3 + TS" :age="age" :config="cfg" />
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue'
import HelloWorld from './components/HelloWorld.vue' 

export default defineComponent({
  name: 'App',
  components: {
    HelloWorld,
  }, 
  data() {
    return {
      age: 24,
      cfg:{
        name:"abc"
      }
    }
  },
  
})
</script> 
//helloword组件
<template>
  <div class="hello">
   name: {{config.name}}
    age:{{ age }}
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue'

interface Config{
  name:string
}

//把props提出来外面
const PropsType = {
  msg: String,
  config:{
      type:Object as PropType<Config>, 
      required:true 
  },
  age: {
    type: Number,
    required: true,
  },
} as const  //多个as const才能让props为readonly只读状态

export default defineComponent({
  name: 'HelloWorld',
  props: PropsType, 
  mounted(){
    this.age
     //有上面的 as PropType<Config>这里就是config接口类型,没有就是Record<string,any>类型
    this.config   //所有定义一个接口类型是有规范意义的
    this.config.name  //如果required不是true,那这里就会报错,因为有可能config不存在,那config.name就是未知数
  }
})
</script>

vue文件转译为js是怎样的,h函数与createVNode

import { createApp, defineComponent, h } from 'vue'
//import App from './App.vue'
import router from './router'
import HelloWorld from './components/HelloWorld.vue'
//由于eslink选择的是TS语法效验,所以出现require是会报错的;手动屏蔽一行效验
const img = require('./assets/logo.png') //eslint-disable-line
/*
创建一个组件
const App = defineComponent({
  render() {
    return 'ABC'    //此时页面就仅显示abc
  },
})
*/

/*
//app.vue默认代码
<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png" />
    <HelloWorld msg="Welcome to Vue.js" />
  </div>
</template>
*/

// h函数(标签,属性,子类)
// 以下就是转为vue文件转为JS的样式
const App = defineComponent({
  render() {
    return (
      h('div', { id: 'app' }),
      [
        h('img', {
          alt: 'Vue logo',
          src: img,
        }),
        h(HelloWorld, {
          msg: 'Welcome to Vue.js"',
        }),
      ]
    )
  },
})
createApp(App).use(router).mount('#app')

h函数只是createVNode的简单封装,所以把h函数替换为createVNode也是一样的

import { createApp, defineComponent, h, createVNode } from 'vue'


ref 与 computed 与 watchEffect

ref 就是通过 reactive 包装了一个对象 ,然后是将值传给该对象中的 value 属性,这也就解释了为什么每次访问时我们都需要加上 .value。
我们可以简单地把 ref(obj) 理解为这个样子 reactive({value: obj})

watch 和 watchEffect 都是用来监视某项数据变化从而执行指定的操作的,但用法上还是有所区别
watchEffect:
1、不需要手动传入依赖
2、每次初始化时会执行一次回调函数来自动获取依赖
3、无法获取到原值,只能得到变化后的值

<template>
  <div id="app"> 
    <p>{{ name }}</p> 
    <p>{{ name2 }}</p> 
    <p>{{ ref2 }}</p>
  </div>
</template>

<script lang="ts">
import {
  defineComponent, 
  ref,
  computed,
  watchEffect,
} from 'vue' 

export default defineComponent({
  name: 'App', 
  setup(props, { slots, attrs, emit }) { 
    //不用reactive,而用ref
    const nameRef = ref('ax:')
    const ref2 = ref("bx:")

    setInterval(() => { 
      nameRef.value += 'a'
    }, 2000)

    setInterval(() => { 
      ref2.value += 'b'
    }, 2000)

    //插一脚,演示下计算属性
    const computedNameRef = computed(() => {
      return nameRef.value + '2'
    })

    //只会监听内部用到的ref,如ref2没用到,所以就不会打印ref2
    watchEffect(() => {
      console.log("watch:",nameRef.value)
    })

    //这样就可以直接name了
    return {
      name: nameRef,
      name2: computedNameRef,
      ref2
    }
  }, 
  created(){
    console.log("created:",this.name)  //这里的this.name仍然是ref,而不是值。因为编译时自动name.value了
  }
})
</script>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容