7-vue-2

vue3.2 自定义全局指令、局部指令

// 在src目录下新建一个directive文件,在此文件夹下新建一个index.js文件夹,接着输入如下内容
const directives =  (app) => {
   
  //这里是给元素取得名字,虽然是focus,但是实际引用的时候必须以v开头
  app.directive('focus',{
   
    //这里的el就是获取的元素
    mounted(el) {
   
      el.focus() 
     }
  })
}

//默认导出 directives
export default directives
// 在全局注册directive
import {
    createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import directives from './directives'

const app = createApp(App)
directives(app)

app.use(store).use(router).mount('#app')

<template>
  <div class="container">
    <div class="content">
      <input type="text"  v-focus>
      内容
    div>
  div>
template>

<script setup>
import {
      reactive, ref } from 'vue'
// const vMove:Directive = () =>{
     

// }
script>

vue3.2 setup语法糖模式下,自定义指令变得及其简单

<input type="text" v-model="value" v-focus>

<script setup>
//直接写,但是必须是v开头
const vFocus = {
     
  mounted(el) {
     
    // 获取input,并调用其focus()方法
    el.focus()
  }
}
script>


<template>
  <div class="container">
    <div class="content" v-move="{ background: value }">
      内容
      <input type="text" v-model="value" v-focus @keyup="see">
    div>
  div>
template>

<script setup>
import {
      reactive, ref } from 'vue'
const value = ref('')

const vFocus = {
     
  mounted(el) {
     
    // 获取input,并调用其focus()方法
    el.focus()
  }
}

let timer = null

const vMove = (el, binding) => {
     
  if (timer !== null) {
     
    clearTimeout(timer)
  }
  timer = setTimeout(() => {
     
    el.style.background = binding.value.background
    console.log(el);
  }, 1000);
}

script>

<style lang="scss" scoped>
.container {
     
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;

  .content {
     
    border-top: 5px solid black;
    width: 200px;
    height: 200px;
    cursor: pointer;
    border-left: 1px solid #ccc;
    border-right: 1px solid #ccc;
    border-bottom: 1px solid #ccc;
  }
}
style>

----------@----------

Vue3的设计目标是什么?做了哪些优化

1、设计目标

不以解决实际业务痛点的更新都是耍流氓,下面我们来列举一下Vue3之前我们或许会面临的问题

  • 随着功能的增长,复杂组件的代码变得越来越难以维护
  • 缺少一种比较「干净」的在多个组件之间提取和复用逻辑的机制
  • 类型推断不够友好
  • bundle的时间太久了

Vue3 经过长达两三年时间的筹备,做了哪些事情?

我们从结果反推

  • 更小
  • 更快
  • TypeScript支持
  • API设计一致性
  • 提高自身可维护性
  • 开放更多底层功能

一句话概述,就是更小更快更友好了

更小

  • Vue3移除一些不常用的 API
  • 引入tree-shaking,可以将无用模块“剪辑”,仅打包需要的,使打包的整体体积变小了

更快

主要体现在编译方面:

  • diff算法优化
  • 静态提升
  • 事件监听缓存
  • SSR优化

更友好

vue3在兼顾vue2options API的同时还推出了composition API,大大增加了代码的逻辑组织和代码复用能力

这里代码简单演示下:

存在一个获取鼠标位置的函数

import {
    toRefs, reactive } from 'vue';
function useMouse(){
   
    const state = reactive({
   x:0,y:0});
    const update = e=>{
   
        state.x = e.pageX;
        state.y = e.pageY;
    }
    onMounted(()=>{
   
        window.addEventListener('mousemove',update);
    })
    onUnmounted(()=>{
   
        window.removeEventListener('mousemove',update);
    })

    return toRefs(state);
}

我们只需要调用这个函数,即可获取xy的坐标,完全不用关注实现过程

试想一下,如果很多类似的第三方库,我们只需要调用即可,不必关注实现过程,开发效率大大提高

同时,VUE3是基于typescipt编写的,可以享受到自动的类型定义提示

2、优化方案

vue3从很多层面都做了优化,可以分成三个方面:

  • 源码
  • 性能
  • 语法 API

源码

源码可以从两个层面展开:

  • 源码管理
  • TypeScript

源码管理

vue3整个源码是通过 monorepo的方式维护的,根据功能将不同的模块拆分到packages目录下面不同的子目录中

7-vue-2_第1张图片

这样使得模块拆分更细化,职责划分更明确,模块之间的依赖关系也更加明确,开发人员也更容易阅读、理解和更改所有模块源码,提高代码的可维护性

另外一些 package(比如 reactivity 响应式库)是可以独立于 Vue 使用的,这样用户如果只想使用 Vue3的响应式能力,可以单独依赖这个响应式库而不用去依赖整个 Vue

TypeScript

Vue3是基于typeScript编写的,提供了更好的类型检查,能支持复杂的类型推导

性能

vue3是从什么哪些方面对性能进行进一步优化呢?

  • 体积优化
  • 编译优化
  • 数据劫持优化

这里讲述数据劫持:

vue2中,数据劫持是通过Object.defineProperty,这个 API 有一些缺陷,并不能检测对象属性的添加和删除

Object.defineProperty(data, 'a',{
   
  get(){
   
    // track
  },
  set(){
   
    // trigger
  }
})

尽管Vue为了解决这个问题提供了 setdelete实例方法,但是对于用户来说,还是增加了一定的心智负担

同时在面对嵌套层级比较深的情况下,就存在性能问题

default {
   
  data: {
   
    a: {
   
      b: {
   
          c: {
   
          d: 1
        }
      }
    }
  }
}

相比之下,vue3是通过proxy监听整个对象,那么对于删除还是监听当然也能监听到

同时Proxy 并不能监听到内部深层次的对象变化,而 Vue3 的处理方式是在getter 中去递归响应式,这样的好处是真正访问到的内部对象才会变成响应式,而不是无脑递归

语法 API

这里当然说的就是composition API,其两大显著的优化:

  • 优化逻辑组织
  • 优化逻辑复用

逻辑组织

一张图,我们可以很直观地感受到 Composition API在逻辑组织方面的优势

7-vue-2_第2张图片

相同功能的代码编写在一块,而不像options API那样,各个功能的代码混成一块

逻辑复用

vue2中,我们是通过mixin实现功能混合,如果多个mixin混合,会存在两个非常明显的问题:命名冲突和数据来源不清晰

而通过composition这种形式,可以将一些复用的代码抽离出来作为一个函数,只要的使用的地方直接进行调用即可

同样是上文的获取鼠标位置的例子

import {
    toRefs, reactive, onUnmounted, onMounted } from 'vue';
function useMouse(){
   
    const state = reactive({
   x:0,y:0});
    const update = e=>{
   
        state.x = e.pageX;
        state.y = e.pageY;
    }
    onMounted(()=>{
   
        window.addEventListener('mousemove',update);
    })
    onUnmounted(()=>{
   
        window.removeEventListener('mousemove',update);
    })

    return toRefs(state);
}

组件使用

import useMousePosition from './mouse'
export default {
   
    setup() {
   
        const {
    x, y } = useMousePosition()
        return {
    x, y }
    }
}

可以看到,整个数据来源清晰了,即使去编写更多的hook函数,也不会出现命名冲突的问题

----------@----------

Vue3有了解过吗?能说说跟vue2的区别吗?

1. 哪些变化

7-vue-2_第3张图片

从上图中,我们可以概览Vue3的新特性,如下:

  • 速度更快
  • 体积减少
  • 更易维护
  • 更接近原生
  • 更易使用

1.1 速度更快

vue3相比vue2

  • 重写了虚拟Dom实现
  • 编译模板的优化
  • 更高效的组件初始化
  • undate性能提高1.3~2倍
  • SSR速度提高了2~3倍

7-vue-2_第4张图片

1.2 体积更小

通过webpacktree-shaking功能,可以将无用模块“剪辑”,仅打包需要的

能够tree-shaking,有两大好处:

  • 对开发人员,能够对vue实现更多其他的功能,而不必担忧整体体积过大
  • 对使用者,打包出来的包体积变小了

vue可以开发出更多其他的功能,而不必担忧vue打包出来的整体体积过多

7-vue-2_第5张图片

1.3 更易维护

compositon Api

  • 可与现有的Options API一起使用
  • 灵活的逻辑组合与复用
  • Vue3模块可以和其他框架搭配使用

7-vue-2_第6张图片

更好的Typescript支持

VUE3是基于typescipt编写的,可以享受到自动的类型定义提示

7-vue-2_第7张图片

1.4 编译器重写

7-vue-2_第8张图片

1.5 更接近原生

可以自定义渲染 API

7-vue-2_第9张图片

1.6 更易使用

响应式 Api 暴露出来

7-vue-2_第10张图片

轻松识别组件重新渲染原因

7-vue-2_第11张图片

2. Vue3新增特性

Vue 3 中需要关注的一些新功能包括:

  • framents
  • Teleport
  • composition Api
  • createRenderer

2.1 framents

Vue3.x 中,组件现在支持有多个根节点

<!-- Layout.vue -->
<template>
  <header>...</header>
  <main v-bind="$attrs">...</main>
  <footer>...</footer>
</template>

2.2 Teleport

Teleport 是一种能够将我们的模板移动到 DOMVue app 之外的其他位置的技术,就有点像哆啦A梦的“任意门”

vue2中,像 modals,toast 等这样的元素,如果我们嵌套在 Vue 的某个组件内部,那么处理嵌套组件的定位、z-index 和样式就会变得很困难

通过Teleport,我们可以在组件的逻辑位置写模板代码,然后在 Vue 应用范围之外渲染它

<button @click="showToast" class="btn">打开 toastbutton>

<teleport to="#teleport-target">
    <div v-if="visible" class="toast-wrap">
        <div class="toast-msg">我是一个 Toast 文案div>
    div>
teleport>

2.3 createRenderer

通过createRenderer,我们能够构建自定义渲染器,我们能够将 vue 的开发模型扩展到其他平台

我们可以将其生成在canvas画布上

7-vue-2_第12张图片

关于createRenderer,我们了解下基本使用,就不展开讲述了

import {
    createRenderer } from '@vue/runtime-core'

const {
    render, createApp } = createRenderer({
   
  patchProp,
  insert,
  remove,
  createElement,
  // ...
})

export {
    render, createApp }

export * from '@vue/runtime-core'

2.4 composition Api

composition Api,也就是组合式api,通过这种形式,我们能够更加容易维护我们的代码,将相同功能的变量进行一个集中式的管理

7-vue-2_第13张图片

关于compositon api的使用,这里以下图展开

7-vue-2_第14张图片

简单使用:

export default {
   
    setup() {
   
        const count = ref(0)
        const double = computed(() => count.value * 2)
        function increment() {
   
            count.value++
        }
        onMounted(() => console.log('component mounted!'))
        return {
   
            count,
            double,
            increment
        }
    }
}

3. 非兼容变更

3.1 Global API

  • 全局 Vue API 已更改为使用应用程序实例
  • 全局和内部 API 已经被重构为可 tree-shakable

3.2 模板指令

  • 组件上 v-model 用法已更改