重学vue(2, 3)及其生态+TypeScript 之 vue(中)

简介: 重学vue(2, 3)及其生态+TypeScript 之 vue

watch监听器


如果我们需要监听数据变化,然后做一些逻辑处理,就需要用到watch了。


如何使用? 默认情况下,监听器只能监听本身数据的变化,内部属性的变化是不能被监听的(对于对象来说)


watch: {
        // 侦听顶级 property
        a(val, oldVal) {
          console.log(`new: ${val}, old: ${oldVal}`)
        }
    }


如果想要监听内部数据(数组或者对象)的变化,我们可以将监听写成一个对象,并且传入一个deep: true属性,来让他深度监听,不管内部嵌套多深,都会被监听到。这里需要注意的是,监听函数的新旧值是一模一样的,因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本。如果想要使用旧数据,我们需要自己拷贝副本。


watch: {
        // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
        c: {
          handler(val, oldVal) {
            console.log('c changed')
          },
          deep: true
        },
    }


如果我们想要立即执行监听器,我们就需要传递一个immediate: true属性。


watch: {
        // 该回调将会在侦听开始之后被立即调用
        e: {
          handler(val, oldVal) {
            console.log('e changed')
          },
          immediate: true
        },
    }


我们还可以对一个属性传入多个监听函数。他将被依次调用


watch: {
        // 你可以传入回调数组,它们会被逐一调用
        f: [
          'handle1', // mothods中定义的方法
          function handle2(val, oldVal) {
            console.log('handle2 triggered')
          },
          {
            handler: function handle3(val, oldVal) {
              console.log('handle3 triggered')
            }
            /* ... */
          }
        ]
    }


我们还可以单独监听一个对象中的特定属性值的变化。注意:监听函数拿到的新旧值依旧是一样的。都是改变后的新值。而且是整个对象。而非单独监听的这个属性的值。


watch: {
        // 侦听单个嵌套 property
        'c.d': function (val, oldVal) {
          // do something
        }
    }


如果我们想要监听数组中对象属性值的变化,我们不可以像上面那种监听方法,我们或者通过deep: true来深度监听,或者在子组件中监听传递的数组中的每一项


网络异常,图片无法展示
|


我们还可以调用this.$watch()来监听。并且他会返回一个函数,用于取消监听。


  • 第一个参数是要侦听的源。


  • 第二个参数是侦听的回调函数callback。


  • 第三个参数是额外的其他选项,比如deep、immediate。


const unwatch = this.$watch("info", 
        function (newInfo, oldInfo) {
          console.log(newInfo, oldInfo);
        }, 
        {
          deep: true,
          immediate: true
        }
     )
     // 调用它将取消监听
     unwatch()


mixins 混入


组件和组件之间有时候会存在相同的代码逻辑,我们希望对相同的代码逻辑进行抽取。这个属性对于vue2中代码抽离和复用,非常有效。但是vue3中我们可以使用另外的方式来对代码进行抽离复用


在Vue2和Vue3中都支持的一种方式就是使用Mixin来完成:


  • Mixin提供了一种非常灵活的方式,来分发Vue组件中的可复用功能。


  • 一个Mixin对象可以包含任何组件选项。


  • 当组件使用Mixin对象时,所有Mixin对象的选项将被 混合 进入该组件本身的选项中。 如果Mixin对象中的选项和组件对象中的选项发生了冲突,那么Vue会如何操作呢?


这里分成不同的情况来进行处理;


  • 情况一:如果是data函数的返回值对象 如果data返回值对象的属性发生了冲突,那么会保留组件自身的数据。


  • 情况二:混入生命周期钩子函数


生命周期的钩子函数会被合并到数组中,都会被调用。


  • 情况三:值为对象的选项,例如 methods、components 和 directives,将被合并为同一个对象。


  • 比如都有methods选项,并且都定义了方法,那么它们都会生效;


  • 但是如果对象的key相同,那么会取组件对象的键值对; 如果每个组件都需要用到一段相同的逻辑,那么我们就可以使用全局混入。


  • 全局的Mixin可以使用 应用app的方法 mixin 来完成注册。


  • 一旦注册,那么全局混入的选项将会影响每一个组件。


app.mixin(混入的对象)


混入的代码和该组件中本身的代码执行顺序:全局混入 > 混入 > 自身组件中的代码。


Vue的组件化


我们将一个完整的页面分成很多个组件,每个组件都用于实现页面的一个功能块,而每一个组件又可以进行细分,而组件本身又可以在多个地方进行复用。前面我们的createApp函数传入了一个对象App,这个对象其实本质上就是一个组件,也是我们应用程序的根组件。


vue中的组件其实很简单,官网讲的很详细。


v3.cn.vuejs.org/guide/compo…


但是有很多需要注意的地方。接下来我们就介绍一下:


props约束


  • 当传递的是对象或者数组,我们指定默认值必须是一个工厂函数。并且返回默认值对象和数组。


  • 我们可以通过数组来表示可以是多个类型。


  • 我们还可以通过validator检验函数来自定义约束类型。


  • Prop 的大小写命名,最好使用-链接命名。


网络异常,图片无法展示
|


非props属性处理


当我们传递给一个组件某个属性,但是该属性并没有定义对应的props或者emits时,就称之为 非Prop的Attribute。


  • 当组件有单个根节点时,非Prop的Attribute将自动添加到根节点的Attribute中


  • 如果我们不希望组件的根元素继承attribute,可以在组件中设置 inheritAttrs: false


  • 禁用attribute继承的常见情况是需要将attribute应用于根元素之外的其他元素。


  • 我们可以通过 $attrs来访问所有的 非props的attribute。


  • 多个根节点的attribute


  • 多个根节点的attribute如果没有显示的绑定,那么会报警告,我们必须手动的指定要绑定到哪一个属性上。


网络异常,图片无法展示
|


子组件向父组件传参


我们可以通过emits来对传递的事件参数进行校验,如果出现不符合的,将会出现警告。


网络异常,图片无法展示
|


如果我们徐想要校验参数,直接写数组就行。


emits: ["add", "sub", "addN"]


全局事件总线


主要用在非父子组件传递参数。


Vue3从实例中移除了 on、on、onoff 和 $once 方法,所以我们如果希望继续使用全局事件总线,要通过第三方的库mitt


// eventBus.js
    import mitt from 'mitt';
    const emitter = mitt();
    export default emitter;


注册和监听事件


// 发送事件 
    emitter.emit("字符串事件名", 参数) 
    // 定义事件 
    emitter.on('字符串事件名', 回调函数)
    // 监听所有事件
    emitter.on('*', (事件类型, 对应事件传递的参数) => {})


移除事件


// 移除所有事件
    emitter.all.clear()
    // 移除指定事件
    emitter.off("事件名", 移除事件的引用)


网络异常,图片无法展示
|


vite的简单介绍


下一代前端开发与构建工具。


他是解决上一代构建工具的问题:


  • 在实际开发中,我们编写的代码往往是不能被浏览器直接识别的,比如ES6、TypeScript、Vue文件等等。所以我们必须通过构建工具来对代码进行转换、编译,类似的工具有webpack、rollup、parcel。


  • 随着项目越来越大,需要处理的JavaScript呈指数级增长,模块越来越多。


  • 构建工具需要很长的时间才能开启服务器,HMR也需要几秒钟才能在浏览器反应出来。


  • 开发阶段不需要对代码做过多的适配,并且将浏览器不能识别的文件都转化为esModule文件,提升构建速度,开发效率提升。当项目打包时,在对项目做适配。


网络异常,图片无法展示
|


它主要由两部分组成:


  • 一个开发服务器,它基于原生ES模块提供了丰富的内建功能,HMR的速度非常快速;


  • 一套构建指令,它使用rollup打开我们的代码,并且它是预配置的,可以输出生成环境的优化过的静态资源;


如果我们不借助于其他工具,直接使用ES Module来开发有什么问题呢?


  • 首先,当加载一个库时,加载了这个库的所有依赖模块的js代码,对于浏览器发送请求是巨大的消耗。


  • 其次,我们的代码中如果有TypeScript、less、vue等代码时,浏览器并不能直接识别。


多以上述问题就需要vite来解决。


现在先安装vite


npm install vite –g  #全局安装
    npm install vite –D  #局部安装


Vite对css的支持


  • vite可以直接支持css的处理


  • vite可以直接支持css预处理器,比如less,sass


  • 但是需要安装less,sass编译器


npm install less -D
    npm install sass -D


  • vite直接支持postcss的转换:


  • 只需要安装postcss,并且配置 postcss.config.js 的配置文件即可


npm install postcss postcss-preset-env -D


// postcss.config.js
    module.exports = {
      plugins: [
        require("postcss-preset-env")
      ]
    }


vite对Typescript的支持


  • vite对TypeScript是原生支持的,它会直接使用ESBuild来完成编译:


  • 只需要直接导入即可。


如果我们查看浏览器中的请求,会发现请求的依然是ts的代码:


这是因为vite中的服务器Connect会对我们的请求进行转发,获取ts编译后的代码,给浏览器返回,浏览器可以直接进行解析。 注意:在vite2中,已经不再使用Koa了,而是使用Connect来搭建的服务器


Vite对vue的支持


  • Vue 3 单文件组件插件支持:@vitejs/plugin-vue


  • Vue 3 JSX 插件支持:@vitejs/plugin-vue-jsx


  • Vue 2 插件支持:underfin/vite-plugin-vue2 在vite.config.js中配置插件:


const vue = require('@vitejs/plugin-vue')
    module.exports = {
      plugins: [
        vue()
      ]
    }


上述配置完成后,我们引入.vue文件,并启动项目,会报错。这时候需要我们安装@vue/compiler-sfc插件即可。


网络异常,图片无法展示
|


Vite脚手架工具


执行以下命令即可创建一个完整的vue项目。


npm install @vitejs/create-app -g
    create-app 项目名


插槽


插槽的作用


通过props传递给组件一些数据,让组件来进行展示,但是为了让这个组件具备更强的通用性,我们不能将组件中的内容限制为固定的div、span等等这些元素。我们可以定义插槽,让外部可以自定义展示的内容和元素。


插槽的使用


插槽的使用过程其实是抽取共性、预留不同。我们会将共同的元素、内容依然在组件内进行封装。同时会将不同的元素使用slot标签作为占位,让外部决定到底显示什么样的元素。


具体使用可以访问官网。


v3.cn.vuejs.org/guide/compo…


下面我们来介绍插槽使用的需要注意什么。


注意事项


  • 如果想要给插槽做样式定义,我们需要给slot标签包裹上一个div元素,如果直接在slot标签上写class,那么插槽替换的内容将替代完整的slot插槽。


网络异常,图片无法展示
|


  • 除了默认插槽外,我们传入内容是都需要在template标签上指定插槽的名称。


  • 可以通过 v-slot:[SlotName]方式动态绑定一个名称。


  • 插槽不能访问提供插槽的组件中的属性。


  • 如果我们渲染插槽的时候,需要用到子组件中的数据,我们就可以通过作用域插槽来将数据传递到父组件进行使用。数据放在一个对象中,并且将作为v-slot指令的值。


// 这里的item, index属性将会被放入一个对象中传递给父组件。
     <slot :item="item" :index="index"></slot>


动态组件


我们如果需要根据条件切换组件,我们就可以使用component标签。并指定一个is属性。其中is属性的值:


  • 可以是通过component函数注册的组件。


  • 在一个组件对象的components对象中注册的组件。 所以切换组件,我们就可以改变is属性中的值。


网络异常,图片无法展示
|


并且,我们还可以将组件中的props和事件写入component组件进行传递。当渲染对应的组件时,就会将props和事件传入到对应的组件中。


<component :is="currentTab"
             name="zh"
             :age="20"
             @pageClick="pageClick">
    </component>


组件缓存


默认情况下,我们每次离开一个组件时,该组件都会被销毁,有时候,我们希望保持组件的状态。所以就需要使用keep-alive组件来包裹住需要缓存的组件。只要被包裹后,该组件中的状态就不会消失。


keep-alive有一些属性:(这些一般用于动态组件,路由使用。单个组件包裹一般不需要)


  • include - string | RegExp | Array。只有名称匹配的组件会被缓存。


  • exclude - string | RegExp | Array。任何名称匹配的组件都不会被缓存。


  • max - number | string。最多可以缓存多少组件实例,一旦达到这个数字,那么缓存组件中最近没有被访问的实例会被销毁。


include 和 exclude prop 允许组件有条件地缓存:


  • 二者都可以用逗号分隔字符串、正则表达式或一个数组来表示。


  • 匹配首先检查组件自身的 name 选项。


网络异常,图片无法展示
|


如果我们想要在组件缓存进入和离开之前做一些事情的时候,我们不能调用create, unmounted钩子函数,但是vue给我们内置了用activateddeactivated 这两个生命周期钩子函数。


activated() {
      console.log("about activated");
    },
    deactivated() {
      console.log("about deactivated");
    }


异步组件


如果我们的项目过大了,对于某些组件我们希望通过异步的方式来进行加载(目的是可以对其进行分包处理),那么Vue中给我们提供了一个函数:

defineAsyncComponent


defineAsyncComponent接受两种类型的参数:


  • 类型一:工厂函数,该工厂函数需要返回一个Promise对象。


defineAsyncComponent(() => import("./AsyncCategory.vue"))


上面的import函数返回的就是一个promise对象。是es6的语法。


  • 类型二:接受一个对象类型,对异步函数进行配置。


import { defineAsyncComponent } from 'vue'
    const AsyncComp = defineAsyncComponent({
    // 工厂函数
    loader: () => import('./Foo.vue')
    // 加载异步组件时要使用的组件
    loadingComponent: LoadingComponent,
    // 加载失败时要使用的组件
    errorComponent: ErrorComponent,
    // 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
    delay: 200,
    // 如果提供了 timeout ,并且加载组件的时间超过了设定值,将显示错误组件
    // 默认值:Infinity(即永不超时,单位 ms)
    timeout: 3000,
    // 定义组件是否可挂起 | 默认值:true
    suspensible: false,
    /**
    *
       * @param {*} error 错误信息对象
       * @param {*} retry 一个函数,用于指示当 promise 加载器 reject 时,加载器是否应该重试
       * @param {*} fail  一个函数,指示加载程序结束退出
       * @param {*} attempts 允许的最大重试次数
       */
      onError(error, retry, fail, attempts) {
        if (error.message.match(/fetch/) && attempts <= 3) {
          // 请求发生错误时重试,最多可尝试 3 次
          retry()
        } else {
          // 注意,retry/fail 就像 promise 的 resolve/reject 一样:
          // 必须调用其中一个才能继续错误处理。
          fail()
        }
      }
    })


结合Suspense组件使用


与之结合使用后,他会忽略提供的异步组件中的加载、错误、延迟和超时选项。 suspense组件,具有两个插槽。而且两个插槽只能有一个直接子节点。


  • default默认插槽,用来显示异步组件。


  • fallback插槽,用来显示加载时的组件。


<suspense>
      <template #default>
        // 异步组件
        <async-category></async-category>
      </template>
      <template #fallback>
        <loading></loading>
      </template>
    </suspense>


teleport组件


在组件化开发中,我们封装一个组件A,在另外一个组件B中使用。那么组件A中template的元素,会被挂载到组件B中template的某个位置。最终我们的应用程序会形成一颗DOM树结构。


但是某些情况下,我们希望组件不是挂载在这个组件树上的,可能是移动到Vue app之外的其他位置。比如移动到body元素上,或者我们有其他的div#app之外的元素上。这个时候我们就可以通过teleport来完成。


Teleport是什么呢?


它是一个Vue提供的内置组件,类似于react的Portals。teleport翻译过来是心灵传输、远距离运输的意思。


它有两个属性:


  • to:指定将其中的内容移动到的目标元素,可以使用选择器。


  • disabled:是否禁用 teleport 的功能。但是他仍然是使用方的子组件。仍然可以传递props。同一个目标父组件可以挂载多个teleport传递的组件。按先后顺序插入。


<teleport to="#zh">
      <hello-world :name="helloVal"></hello-world>
    </teleport>
    <teleport to="#zh">
      <p>另外一个组件</p>
    </teleport>
      components: {
        HelloWorld,
      },
      setup() {
        const helloVal = ref('hello')
        return {
          helloVal,
        }
      }


// HelloWorld.vue
  <div>
    <h2>{{name}}</h2>
  </div>
  props: ['name']
}


网络异常,图片无法展示
|


vue生命周期


每个组件都可能会经历从创建、挂载、更新、卸载等一系列的过程。在这个过程中的某一个阶段,用于可能会想要添加一些属于自己的代码逻辑(比如组件创建完后就请求一些服务器数据)。


但是我们如何可以知道目前组件正在哪一个过程呢?


Vue给我们提供了组件的生命周期函数,生命周期钩子的 this 上下文将自动绑定至实例中,因此你可以访问 data、computed 和 methods。


网络异常,图片无法展示
|


相关文章
|
2月前
|
JavaScript 前端开发 安全
【技术革新】Vue.js + TypeScript:如何让前端开发既高效又安心?
【8月更文挑战第30天】在使用Vue.js构建前端应用时,结合TypeScript能显著提升代码质量和开发效率。TypeScript作为JavaScript的超集,通过添加静态类型检查帮助早期发现错误,减少运行时问题。本文通过具体案例展示如何在Vue.js项目中集成TypeScript,并利用其类型系统提升代码质量。首先,使用Vue CLI创建支持TypeScript的新项目,然后构建一个简单的待办事项应用,通过定义接口描述数据结构并在组件中使用类型注解,确保代码符合预期并提供更好的编辑器支持。
72 0
|
2月前
|
JavaScript 前端开发 安全
立等可取的 Vue + Typescript 函数式组件实战
立等可取的 Vue + Typescript 函数式组件实战
|
3月前
|
JavaScript 前端开发
【Vue3+TypeScript】CRM系统项目搭建之 — 关于如何设计出优质的 Vue 业务组件
【Vue3+TypeScript】CRM系统项目搭建之 — 关于如何设计出优质的 Vue 业务组件
42 0
【Vue3+TypeScript】CRM系统项目搭建之 — 关于如何设计出优质的 Vue 业务组件
|
4月前
|
JavaScript 安全 前端开发
Vue 3 中的 TypeScript
【6月更文挑战第15天】
75 6
|
5月前
|
JavaScript 前端开发 开发者
类型检查:结合TypeScript和Vue进行开发
【4月更文挑战第24天】TypeScript是JavaScript超集,提供类型注解等特性,提升代码质量和可维护性。Vue.js是一款高效前端框架,两者结合优化开发体验。本文指导如何配置和使用TypeScript与Vue:安装TypeScript和Vue CLI,创建Vue项目时选择TypeScript支持,配置`tsconfig.json`,编写`.tsx`组件,最后运行和构建项目。这种结合有助于错误检查和提升开发效率。
50 2
|
5月前
|
JavaScript 前端开发 开发者
Vue工具和生态系统: Vue.js和TypeScript可以一起使用吗?
【4月更文挑战第18天】Vue.js与TypeScript兼容,官方文档支持在Vue项目中集成TypeScript。TypeScript作为JavaScript超集,提供静态类型检查和面向对象编程,增强代码准确性和健壮性。使用TypeScript能提前发现潜在错误,提升代码可读性,支持接口和泛型,使数据结构和函数更灵活。然而,不是所有Vue插件都兼容TypeScript,可能需额外配置。推荐尝试在Vue项目中使用TypeScript以提升项目质量。
107 0
|
5月前
|
JavaScript 前端开发
在Vue中使用TypeScript的常见问题有哪些?
在Vue中使用TypeScript的常见问题有哪些?
89 2
|
1月前
|
JavaScript
typeScript进阶(9)_type类型别名
本文介绍了TypeScript中类型别名的概念和用法。类型别名使用`type`关键字定义,可以为现有类型起一个新的名字,使代码更加清晰易懂。文章通过具体示例展示了如何定义类型别名以及如何在函数中使用类型别名。
36 1
typeScript进阶(9)_type类型别名
|
9天前
|
JavaScript 前端开发 安全
深入理解TypeScript:增强JavaScript的类型安全性
【10月更文挑战第8天】深入理解TypeScript:增强JavaScript的类型安全性
18 0
|
9天前
|
JavaScript 前端开发 开发者
深入理解TypeScript:类型系统与实用技巧
【10月更文挑战第8天】深入理解TypeScript:类型系统与实用技巧