vue3学习笔记

356 阅读5分钟

1.特性

  • 向下兼容:vue3支持大多数vue2特性
  • 性能提升:打包大小、初次渲染、更新、内存使用
  • Composition API
  • Teleport(瞬移组件)、Suspense(解决异步加载组件问题)和全局API的修改和优化
  • 更好TypeScript支持

2.vue-cli搭建Vue3开发环境

Vue-cli

  • Vue.js开发的标准工具。新版的vue-cli还提供了图形界面:通过 vue ui 命令以图形化界面创建和管理项目
  • 省去复杂的项目配置过程,快速生成标准化的Vue开发环境
  • 安装:
npm install -g @vue/cli //推荐
或者
yarn global add @vue/cli
  • 创建vue3项目:vue create vue3-1

  • package.json中的vue-cli-service:能使用vue-cli-service是因为vue-cli自动安装了cli-service这个工具

 "serve": "vue-cli-service serve",
  "build:prd": "vue-cli-service build",

3.一些文件介绍

  • 入口文件main.ts
import {createApp } from "vue"
import App from "./App.vue"
createApp(App).mount("#app")
  • App.vue
<template>
   <HelloWorld msg="welcome sss" />
</template>
<script lang="ts">
   import { defineComponent } from 'vue';
   import HelloWorld from './components/HelloWorld.vue';
   export default defineComponent({
      name:'App',
      components:{
         HelloWorld
      }
   })
</script>

4. setup()和ref()

(1)

  • setup:可以代替vue2datamethods属性
  • return出去的数据和方法,在模板中才可以使用,可以精准得控制暴露的变量和方法

setup(props, context) {}有两个参数,

  • 注意 props 对象是响应式的,watchEffectwatch 会观察和响应 props 的更新。不要解构 props 对象,那样会使其失去响应性
  • context中就提供了this中最常用的三个属性:attrsslot 和emit,分别对应 Vue2.x 中的 $attr属性、slot插槽 和$emit发射事件。并且这几个属性都是自动同步最新的值

(2) ref :要在template中使用的变量,必须用ref包装一下

<script lang="ts">
    import {defineComponent,ref} from "vue";
    export default defineComponent({
     name:"App",
     setup(){
       const arrs=ref(["1","2","3"])
       const selectNum=ref('')
       const selectFn=(index:number)=>{
          selectNum.value=arrs.value[index];
       }
       return {
          arrs,
          selectNum,
          selectFn
       }
     } 
    })
</script>
  • 模板 Refs:当使用组合式 API 时,reactive refs 和 template refs 的概念已经是统一的。为了获得对模板内元素或组件实例的引用,我们可以像往常一样在 setup() 中声明一个 ref 并返回它
<template>
  <div ref="root"></div>
</template>

<script>
  import { ref, onMounted } from 'vue'

  export default {
    setup() {
      const root = ref(null)

      onMounted(() => {
        // 在渲染完成后, 这个 div DOM 会被赋值给 root ref 对象
        console.log(root.value) // <div/>
      })

      return {
        root,
      }
    },
  }
</script>

5. reactive

它也是一个函数(方法),只不过里边接受的参数是一个Object(对象),无论是变量还是方法,都可以作为Object中的一个属性

<script lang="ts">
    interface DataProps{
       arrs:string[];
       selectNum:string;
       selectFn: (index:number)=>void;
    }
    import {ref,reactive} from "vue";
    export default {
     name:"App",
     setup(){
     
     const data:DataProps=reactive({
       arrs:["1","2","3"]
       selectNum:''
       selectFn:(index:number)=>{
          data.selectNum=data.arrs[index];
       }
     })
     return {  //不用再加value属性,返回的时候不用一个个返回,只需返回data
          data
     }
    } 
  }
</script>

接收一个普通对象然后返回该普通对象的响应式代理【等同于 2.x 的 Vue.observable()reactive 类的 api 主要提供了将复杂类型的数据处理成响应式数据的能力

因为是组合函数【对象】,所以必须始终保持对这个所返回对象的引用以保持响应性不能解构该对象或者展开】例如 const { x, y } = useMousePosition()或者return { ...useMousePosition() }

function useMousePosition() {
    const pos = reactive({
        x: 0,
        y: 0,
      })
    return pos
}

ref()reactive()都是生成响应式对象,只是编写上有所不同

6.toRefs()

reactive输出的变量前面要加一个data.,如果用...扩展运算符会解构变成普通变量。不再具有响应式的能力,需要使用toRefs

  • 例一:
<script lang="ts">
    interface DataProps{
       arrs:string[];
       selectNum:string;
       selectFn: (index:number)=>void;
    }
    import { reactive,toRefs } from "vue";
    export default {
     name:"App",
     setup(){
     
     const data:DataProps=reactive({
       arrs:["1","2","3"]
       selectNum:''
       selectFn:(index:number)=>{
          data.selectNum=data.arrs[index];
       }
     })
     
     const refData=toRefs(data)
     
     return {  
          ...refData
      }
    } 
   }
</script>
  • 例二:
 function useMousePosition() {
    const pos = reactive({
        x: 0,
        y: 0,
      })
    return toRefs(pos)
}

// x & y 现在是 ref 形式了!
const { x, y } = useMousePosition()

7.生命周期

  • setup():开始创建组件之前,在beforeCreatecreated之前执行(可以用来代替这两个钩子函数)。创建的是datamethod
  • onBeforeMount():组件挂载在节点上之前执行的函数
  • onMounted():组件挂载完成后执行的函数
  • onBeforeUpdate():组件更新之前执行的函数
  • onUpdated():组件更新完成之后执行的函数
  • onBeforeUnmount():组件卸载之前执行的函数
  • onUnmounted():组件卸载完成后执行的函数
  • onActivated()
  • onDeactivated()
  • onErrorCaptured():当捕获一个来自子孙组件的异常时激活钩子函数

引申:

  • 状态跟踪onRenderTracked:会跟踪页面上所有响应式变量和方法的状态,也就是所有return返回的值都会跟踪。只要页面有update的情况,就会跟踪,然后生成一个event对象,通过event来查找程序问题

  • 状态触发onRenderTriggered:不会跟踪每一个值,而是给出变化值的信息。

    watchEffect 参数选项中的 onTrackonTrigger 类似

export default {
  onRenderTriggered(e) {
    debugger
    // 检查哪个依赖性导致组件重新渲染
  },
}

8.watch

watch(text,(newValue,oldValue)=>{ ....  })//watch单个值

watch([() => state.age, year], ([curAge, newVal], [preAge, oldVal]) => { console.log("新值:", curAge, "老值:", preAge); console.log("新值:", newVal, "老值:", oldVal); });

//watch监听的值需要是响应式对象或者有get\set方法

// 侦听复杂的嵌套对象  如果不使用第三个参数deep:true, 是无法监听到数据变化的。 前面我们提到,默认情况下,watch 是惰性的, 那什么情况下不是惰性的, 可以立即执行回调函数呢?其实使用也很简单, 给第三个参数中设置immediate: true即可
const state = reactive({
  room: { 
   id: 100,
   attrs: { size: "140平方米", type: "三室两厅"},
  },
}); 
watch( () => state.room, (newType, oldType) => { console.log("新值:", newType, "老值:", oldType); }, { deep: true } );

9.slot 具名插槽语法

在 Vue2.x 中具名插槽和作用域插槽分别使用slot和slot-scope来实现, 在 Vue3.0 中将slot和slot-scope进行了合并同意使用。 Vue3.0 中v-slot:

<!-- 父组件中使用 -->
<template v-slot:content="scoped">
  <div v-for="item in scoped.data">{{item}}</div> 
</template>
<!-- 也可以简写成: --> 
<template #content="{data}">
  <div v-for="item in data">{{item}}</div>
</template>

10.在 Vue3.x 中,你可以直接写多个根节点

11.Suspense 组件

Vue3 也新增了类似 React.lazy 功能的 defineAsyncComponent 函数,处理动态引入的组件。defineAsyncComponent可以接受返回promise的工厂函数。当您从服务器检索到组件定义时,应该调用Promise的解析回调

<template>
    <Suspense>
    <template #default>
        <my-component />
    </template>
    <template #fallback>
        Loading ...
    </template>
    </Suspense>
</template>
 
<script lang='ts'>
    import { defineComponent, defineAsyncComponent } from "vue";
    const MyComponent = defineAsyncComponent(() => import('./Component'));
 
export default defineComponent({
    components: {
        MyComponent
    },
    setup() {
        return {}
    }
})
    
</script>

12.Teleport

示例:告诉 Vue “Teleport 这个 HTML 到该‘body’标签”

app.component('modal-button', {
  template: `
    <button @click="modalOpen = true">
        Open full screen modal! (With teleport!)
    </button>

    <teleport to="body">
      <div v-if="modalOpen" class="modal">
        <div>
          I'm a teleported modal! 
          (My parent is "body")
          <button @click="modalOpen = false">
            Close
          </button>
        </div>
      </div>
    </teleport>
  `,
  data() {
    return { 
      modalOpen: false
    }
  }
})

13. vue的全局配置

可以在组件用通过 getCurrentInstance() 来获取全局globalProperties 中配置的信息,getCurrentInstance 方法获取当前组件的实例,然后通过 ctx 属性获得当前上下文,这样我们就能在setup中使用routervuex, 通过这个属性我们就可以操作变量、全局属性、组件属性等等

setup( ) {
    const { ctx } = getCurrentInstance();
    ctx.$http   
}

14.与 React Hooks相比

  • React Hooks 相同级别的逻辑组合功能,但有一些重要的区别。 与 React Hook 不同,setup 函数仅被调用一次,这在性能上比较占优。
  • 不存在忘记记录依赖的问题。React Hook 有臭名昭著的闭包陷阱问题,如果用户忘记传递正确的依赖项数组,useEffectuseMemo` 可能会捕获过时的变量。 Vue 的自动依赖跟踪可以确保侦听器和计算值总是准确无误
  • 不需要顾虑调用顺序,也可以用在条件语句中;
  • 不会在每次渲染时重复执行,以降低垃圾回收的压力
  • 不存在内联处理函数导致子组件永远更新的问题,也不需要useCallback
  • React Hook 里的「依赖」是需要去手动声明的,而且官方提供了一个 eslint 插件,这个插件虽然大部分时候挺有用的,但是有时候也特别烦人,需要你手动加一行丑陋的注释去关闭它。

15.插件

  • vue-swr
<template>
    <div v-if="error">failed to load</div>
    <div v-else-if="loading">loading...</div>
    <div v-else>hello {{fullName}}!</div>
</template>

<script>
import { createComponent, computed } from 'vue'
import useSWR from 'vue-swr'

export default createComponent({
  setup() {
      // useSWR帮你管理好了取数、缓存、甚至标签页聚焦重新请求、甚至Suspense...
      const { data, loading, error } = useSWR('/api/user', fetcher)
      // 轻松的定义计算属性
      const fullName = computed(() => data.firstName + data.lastName)
      return { data, fullName, loading, error }
  }
})
</script>

16.安装升级vue3

npm install -g @vue/cli@next
export  PATH="$PATH:/Users/name/.anpm_modules/bin/"

参考文章:

  1. Vue3 究竟好在哪里?(和 React Hook 的详细对比)
  2. Vue 组合式 API
  3. Vue CLI
  4. Vue3.0 新特性以及使用经验总结