vue - 基本使用

转载改编自:https://www.bilibili.com/video/BV1Ap4y1W7MG/


文章目录

    • 二、Composition API(组合式API)
      • 1、setup
      • 2、API - ref
      • 3、API - reactive (对象)
      • 4、API - toRefs
    • 三、Provide与Inject(提供/注入)
      • 1、Vue2写法
      • 2、Vue3写法
      • 3、响应性
    • 四、Teleport(传送门)
    • 五、Suspense(等待)
    • 六、Fragment(碎片)
    • 七、TreeShaking(消除未使用代码)
    • 八、Performance(性能)
      • 1、diff方法优化
      • 2、静态提升
      • 3、cacheHandlers 事件侦听器缓存
      • 4、ssr渲染
    • 九、Setup的生命周期
    • 十、TypeScript支持


二、Composition API(组合式API)

相当于 React Hooks

我们先使用以前vue2的方式实现一个累加:

<template><h2>{{count}}</h2><button @click="btnClick">累加</button>
</template><script>
export default {data(){return {count: 0}},methods: {btnClick(){this.count++;}}
}
</script>

这套代码可以实现一个累加的效果,但如果以后我们想把这个组件中的 count 字段与 btnClick 单独拎出来管理,那就比较麻烦了,因为 countbtnClick 不在同一个方法内,很难抽离。



1、setup

setup有以下特性:

  1. setup函数是处于 生命周期函数 beforeCreate 和 Created 两个钩子函数之间的函数 也就说在 setup函数中是无法 使用 data 和 methods 中的数据和方法的
  2. setup函数是 Composition API(组合API)的入口
  3. 在setup函数中定义的变量和方法最后都是需要 return 出去的 不然无法再模板中使用
  4. 由于我们不能在 setup函数中使用 data 和 methods,所以Vue 为了避免我们错误的使用,直接将 setup函数中的this修改成了 undefined
  5. setup函数只能是同步的不能是异步的

2、API - ref

我们先来体验一下vue3怎么处理这个累加效果:

<template><h2>{{count}}</h2><button @click="btnClick">累加</button>
</template><script>
import {ref} from 'vue'
export default {data(){return {count: 0}},setup(){const count = ref(1);	// 此时我们使用ref指定count的默认值为1,因此上面data中的count会失效let btnClick = () => {count.value++;		// 修改ref中的值要用xxx.value}return {count, btnClick}}
}

此时如果我想单独管理这个累加效果,我就可以这么操作:

<template><h2>{{count}}</h2><button @click="btnClick">累加</button>
</template><script>
import {ref} from 'vue'
export default {data(){return {// count: 0		// 一旦把setup中的代码抽离,return中对应的值要去掉,否则ref无效}},setup(){// 函数调用后就会返回一个对象,因此我们直接returnreturn clickCountFn()// 如果后期还想同时返回其他数据,可以将clickCountFn()的返回结果展开// return {...clickCountFn(), 其他数据}}
}// 封装一个函数,这样这块功能我们就能单独管理了
function clickCountFn(){const count = ref(1);let btnClick = () => {count.value++;}return {count, btnClick}
}
</script>

3、API - reactive (对象)

再来了解另一个API :

reactive函数和ref作用非常接近,但是它的参数是一个对象,我们可以在对象中定义其方法,而通过这个形式,就不需要再对其进行进行 .value 调用了。

  • reactive 往往会搭配 toRefs 一起使用
  • 使用 reactive 生成的对象与 ref 生成的值都是响应式的。
<template><h2>{{count}}</h2><button @click="btnClick">累加</button><p>姓名:{{obj.username}}</p><button @click="btnClick1">修改姓名</button>
</template><script>
import {ref, reactive} from 'vue'
export default {setup(){// 使用reactivelet obj = reactive({username: "Jack"})let btnClick1 = () => {obj.username = "Mary"}return {...clickCountFn(), obj, btnClick1}}
}function clickCountFn(){const count = ref(1);let btnClick = () => {count.value++;}return {count, btnClick}
}
</script>

这里可以看到我们在 html 中调用数据时,使用的是 obj.username ,那我们是否可以直接写 username 呢?答案是可以的,但这里需要注意:

由于reactive返回的对象本质上已经是一个Proxy对象,所以通过…扩展符号展开的属性,是无法进行响应式的

也就是说,如果这么写:

return {...clickCountFn(), ...obj, btnClick1}

那么是无法实现的。


4、API - toRefs

正确的写法应该是:

<template><h2>{{count}}</h2><button @click="btnClick">累加</button><!-- 无需obj.username,直接username即可 --><p>姓名:{{username}}</p><button @click="btnClick1">修改姓名</button>
</ template><script>
// 新增toRefs方法
import {ref, reactive, toRefs} from 'vue'
export default {setup(){let obj = reactive({username: "Jack"})let btnClick1 = () => {obj.username = "Mary"}// 通过toRefs方法let refObj = toRefs(obj);// 通过...refObj将数据扩展return {...clickCountFn(), ...refObj, btnClick1}}
}// 其他代码...
</script>

三、Provide与Inject(提供/注入)


1、Vue2写法

以往我们的父传子是通过props传的:

<!-- Father.vue父组件 -->
<template><Child :num="num" />
</template><script>
import Child from './Child.vue'
export default {data(){return {num: 123}},components: {Child}
}
</script><!-- Child.vue子组件 -->
<template><h2>父组件传过来的值:{{num}}</h2>
</template><script>
export default {props: ['num']
}
</script>

这个时候限制死了数据必须来自父组件,我们其实还有 ProvideInject

<!-- Father.vue父组件 -->
<template><Child />
</template><script>
import Child from './Child.vue'
export default {components: {Child},provide: {num: 456}
}
</script><!-- Child.vue子组件 -->
<template><h2>父组件传过来的值:{{num}}</h2>
</template><script>
export default {inject: ['num']
}
</script>

Provide/Inject 相比于 props 的好处在于:

如果组件嵌套较多,那么 props 需要一级一级往下传递,后期很难维护。Provide+Inject 相当于是跨级组件传值,比如孙子组件也想用上面这个 num 的值,就不用一级一级往下传,直接在孙子组件使用即可:

<!-- Sun.vue孙子组件 -->
<template><h4>孙子组件:{{num}}</h4>
</template><script>
export default {// 将Sun组件在Child组件中引入,即可实现跨级组件传值inject: ['num']
}
</script>

2、Vue3写法

vue3中的 provide/inject。两者都只能在当前活动实例的 setup() 期间调用。

格式为:

// provide
import {provide} from 'vue' // 显式导入
export default {setup() {// 此处name必须是String类型,value则不限制provide(name, value)}
}// inject
import {inject} from 'vue' // 显式导入
export default {setup(){// name即为传过来的字段,第二个参数可选,可填写默认值const val = inject(name, defaultValue);return {val}}
}

我们修改以上案例的代码:

<!-- Father.vue父组件 -->
<template><Child />
</template><script>
import {provide} from 'vue' // 显式导入
import Child from './Child.vue'
export default {components: {Child},setup(){provide('num', 789)}
}
</script><!-- Sun.vue孙子组件 -->
<template><h4>孙子组件:{{mynum}}</h4>
</template><script>
import {inject} from 'vue' // 显式导入
export default {setup(){const mynum = inject('num');return {mynum}}
}
</script>

3、响应性

所谓的 Provide/Inject 响应性,其实就是把传递的值结合上文提及的 refreactive 一起使用:

<!-- Father.vue父组件 -->
<template><Child /><button @click="changeNumFn">修改num</button>
</template><script>
import {provide, ref} from 'vue' // 显式导入
import Child from './Child.vue'
export default {components: {Child},setup(){// 使用ref来定义num的值const num = ref(123);// 声明一个函数,专门用于修改numlet changeNumFn = () => {num.value = 456;}provide('num', num)// 返回这个函数return {changeNumFn}}
}
</script>

此时,当你点击按钮时,孙子组件接收到的 num 就会被修改了。


四、Teleport(传送门)

在vue2中,想要将子节点渲染到存在于父组件以外的 DOM 节点时,需要通过第三方库 portal-vue 去实现。而vue3中,Teleport 是一种能够将我们的模板移动到 DOMVue app 之外的其他位置的技术。

官方文档:《teleport》

举个最简单的例子:

我们在 index.html#app 同级的地方新增一个 #test 元素:

<div id="app"></div>
<div id="test"></div>

由于vue的 main.js 中规定了打包出来的代码都放入 #app 中:

createApp(App).mount('#app')

因此,你现在没有办法将代码放入 #test 中。此时,我们可以使用传送门:

App.vue 中:

<template><Home />
</template><script>
import Home from './components/Home.vue'
export default {name: 'App',components: {Home}
}
</script>

Home.vue 中:

<template><p>这段话是渲染在#app中的</p><teleport to="#test"><p>这段话是渲染在#test中的--1</p></teleport><teleport to="#test"><p>这段话是渲染在#test中的--2</p></teleport>
</template>

此时,你打开浏览器控制台,就可以看到第2、3个p标签已经被渲染到 #test 中。

备注:

1、标签身上都to属性,填写的是css选择器。

2、多个传送门书写时,会按照自上而下的顺序传送至另一个DOM元素。


五、Suspense(等待)

Suspense组件用于在等待某个异步组件解析时显示后备内容。

那我们什么时候需要使用异步组件呢?多了去了,比如:

  • 在页面加载之前显示加载动画
  • 显示占位符内容
  • 处理延迟加载的图像

那么,让我们看看 Suspense 怎么使用,我们先来提一个需求:

在等待组件获取数据并解析时显示“玩命加载中…”之类的内容

OK,我们来写一个 Article.vue 组件:

<template><p>{{ content }}</p>
</template><script>
import { ref } from "vue";
export default{async setup() {let content = ref('内容')content.value = await new Promise((resolve,reject)=>{setTimeout(() => {// 3秒后修改contentresolve("你好世界");}, 3000);})return {content}},
}
</script>

用个 Home.vue 组件来调用它:

<template<Suspense><template #default><Article></Article></template><template #fallback><p>玩命加载中...</p></template></Suspense>
</template><script>
import Article from './Article.vue'
export default {components:{Article}
}
</script>

可以看出,<Suspense> 中,包含了两个template标签,#default 定义了我们要写入的内容, #fallback 定义了我们要预显示的内容。

⚠️ 注意:

作为template中的内容(即插槽),必须要有根元素。


六、Fragment(碎片)

vue2中,如果你创建一个Vue组件,那么它只能有一个根节点。这意味着不能创建这样的组件:

<template><div>你好</div><div>世界</div>
</template>

原因是代表任何Vue组件的Vue实例需要绑定到一个单一的DOM元素中。唯一可以创建一个具有多个DOM节点的组件的方法就是创建一个没有底层Vue实例的功能组件。

这情况同样存在于react,但react可以使用空标签 <></> 来包裹,或者是使用一个名为Fragment的虚拟元素:

class Columns extends React.Component {render() {return (<React.Fragment><td>你好</td><td>世界</td></React.Fragment>)}
}

尽管Fragment看起来像一个普通的DOM元素,但它是虚拟的,根本不会在DOM树中呈现。目前你可以在Vue 2中使用vue-fragments库来使用Fragments,而在Vue 3中,你直接使用就行了,无需引入任何库。


七、TreeShaking(消除未使用代码)

TreeShaking 是一个术语,指的是在打包构建过程中移除没有被引用到的代码,这些代码可以成为 dead code。这个概念最早在基于 ES6 的打包工具 Rollup 中提出,后来被引入到 webpack 中。TreeShaking 比较依赖于 ES6 模块系统的静态结构特性,比如 importexport

文档参考:https://vue3js.cn/docs/zh/guide/migration/global-api-treeshaking.html#_2-x-%E8%AF%AD%E6%B3%95

举个例子:

vue2中我们常使用 Vue.nextTick(()=>{}) 来预操作DOM,但有时候我们不用这个 nextTick ,比如改用别的方式来代替(如setTimeout),那么项目打包时,vue 全局的 nextTick 就成为一个多余的代码,从而使你的项目打包体积变大。

在vue3中,官方团队重构了所有全局 API 的组织方式,让所有的 API 都支持了 TreeShaking。所以vue3中如果还想使用全局的 nextTick ,就需要引入:

import { nextTick } from 'vue';nextTick(() => {// 和 DOM 有关的一些操作
});

如果你在 Vue 3 中不引入而直接调用 Vue.nextTick() ,就会得到一个报错:undefined is not a function

官方也给出了Vue 2.x 中的受此更改影响的全局 API:

  • Vue.nextTick
  • Vue.observable (用 Vue.reactive 替换)
  • Vue.version
  • Vue.compile (仅全构建)
  • Vue.set (仅兼容构建)
  • Vue.delete (仅兼容构建)

八、Performance(性能)

vue3.0相对于vue2.0来说性能快1.2到1.5倍,主要原因如下:


1、diff方法优化

  • Vue2 中的虚拟dom是进行全量的对比
  • Vue3 新增了静态标记(PatchFlag),只比对带有 PF 的节点,并且通过 Flag 的信息得知 当前节点要比对的具体内容。

2、静态提升

  • Vue2中无论元素是否参与更新, 每次都会重新创建, 然后再渲染
  • Vue3中对于不参与更新的元素, 会做静态提升, 只会被创建一次, 在渲染时直接复用即可

3、cacheHandlers 事件侦听器缓存

  • 默认情况下onClick会被视为动态绑定, 所以每次都会去追踪它的变化
  • 但是因为是同一个函数,所以没有追踪变化, 直接缓存起来复用即可

4、ssr渲染

  • 当有大量静态的内容时候,这些内容会被当做纯字符串推进一个buffer里面, 即使存在动态的绑定,会通过模板插值嵌入进去。这样会比通过虚拟dmo来渲染的快上很多很多。
  • 当静态内容大到一定量级时候,会用_createStaticVNode方法在客户端去生成一个static node, 这些静态node,会被直接innerHtml,就不需要创建对象,然后根据对象渲染。

九、Setup的生命周期

组合式API需要在setup中使用,setup中含有的生命钩子与vue的大体一致:

具体参考:《setup生命周期钩子》


十、TypeScript支持

vue3新增了对TS语法的支持。


2024-01-16

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/6655.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

秋招算法刷题8

20240422 2.两数相加 时间复杂度O&#xff08;max(m,n))&#xff0c;空间复杂度O&#xff08;1&#xff09; public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode headnull,tailnull;int carry0;while(l1!null||l2!null){int n1l1!null?l1.val:0;int n2l2!…

什么是责任链模式?有哪些应用?

一、定义、目的 责任链模式的目的是避免请求发送者与多个接收者之间的耦合关系&#xff0c;将这些接收者组成一条链&#xff0c;并沿着这条链传递请求&#xff0c;直到有一个接收者处理它为止。 在责任链模式中&#xff0c;通常将处理请求的对象称为处理器或者链的节点&#…

OpenWRT部署Zerotier虚拟局域网实现内网穿透

前言 细心的小伙伴肯定已经发现了&#xff1a;电脑上部署了Zerotier&#xff0c;如果路由器也部署了OpenWRT&#xff0c;那是否能远程访问呢&#xff1f; 答案是肯定的。 OpenWRT部署Zerotier有啥好处&#xff1f; 那好处必须多&#xff0c;其中的一个便是在外远程控制家里…

pytorch 线性代数基本操作

pytorch是非常好的人工智能训练和推理框架&#xff0c;我们来通过李沐老师的《动手学深度学习》来简单学习一下。 首先&#xff0c;安装pytorch&#xff0c;请参考pytorch官网&#xff1a;PyTorch 也可以到kaggle、colab、启智社区等使用配置好的pytorch环境&#xff0c;更简单…

图像压缩问题

图像压缩问题的bilibil讲解 1.问题引入 首先&#xff0c;图像是由像素组合成的&#xff0c;每个像素都有灰度值&#xff0c;灰度值是体现像素的颜色的。灰度值从0~255&#xff0c;灰度值占用的位数就是像素占用的位数。我们要存储一个图像就要存储它的所有像素。现在的问题是我…

0502_C高级H1

练习题&#xff1a; 5月2日作业&#xff1a; 第一题&#xff1a; 段错误 str指向申请堆空间内存的首地址&#xff0c;直接拷贝hello world 会导致段错误 第二题&#xff1a; 段错误 临时变量返回地址属于典型的段错误 第三题&#xff1a; hello 第四题&#xff1a; 段错误 free…

ssm(教学评价系统)

开发语言&#xff1a;Java JDK版本&#xff1a;JDK1.8&#xff08;或11&#xff09;服务器&#xff1a;tomcat 数据库&#xff1a;mysql 5.6/5.7&#xff08;或8.0&#xff09;数据库工具&#xff1a;Navicat 开发软件&#xff1a;idea 依赖管理包&#xff1a;Maven 代码数据库…

车载电子电器架构 —— 关于bus off汇总

车载电子电器架构 —— 关于bus off汇总 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明…

【uniapp】H5+、APP模拟浏览器环境内部打开网页

前言 今天将智能体嵌入到我的项目中&#xff0c;当作app应用时&#xff0c;发现我使用的webview组件&#xff0c;无论H5怎么登录都是未登录&#xff0c;而APP却可以&#xff0c;于是进行了测试&#xff0c;发现以下几种情况&#xff1a; 方法<a>标签webviewAPP✅✅网页…

Acwing2024蓝桥杯回溯

AcWing 167. 木棒 dfs剪枝: #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int N70; int n,a[N],flag[N],sum,len; //a:每个木棍长度,flag:状态数组 //len:木棒的长度,sum:所有木棍的总长度 //cnt:当前正在拼第几根…

[C++核心编程-01]----C++内存四区详解

前言 结构体&#xff08;Struct&#xff09;是C中一种用户定义的复合数据类型&#xff0c;用于存储不同类型的数据项。结构体可以包含不同类型的数据成员&#xff0c;这些数据成员可以是基本类型&#xff08;如int、float、char等&#xff09;&#xff0c;也可以是数组…

Xcode 对应的 macOS、SDK 版本

最低要求和支持的 SDK 本表截取于 2024-05-04&#xff0c;更多更新可见&#xff1a;https://developer.apple.com/cn/support/xcode/ Xcode 版本要求的最低 OS 版本SDK架构部署目标模拟器SwiftXcode 15.3macOS Sonoma 14iOS 17.4 macOS 14.4 tvOS 17.4 watchOS 10.4 DriverKi…

spring高级篇(八)

本篇对Spring MVC 的执行流程做一个简单总结 MVC执行流程总结 当浏览器发送一个请求&#xff0c;例如http://localhost:8080/hello&#xff0c;请求到达服务器后&#xff0c;一般会进行如下操作&#xff1a; 1、首先会经过DispatcherServlet&#xff0c;默认映射路径为 /&…

GRU模块:nn.GRU层的输出state与output

在 GRU&#xff08;Gated Recurrent Unit&#xff09;中&#xff0c;output 和 state 都是由 GRU 层的循环计算产生的&#xff0c;它们之间有直接的关系。state 实际上是 output 中最后一个时间步的隐藏状态。 GRU 的基本公式 GRU 的核心计算包括更新门&#xff08;update gat…

用于密集预测任务的通道知识蒸馏——关键字:蒸馏

摘要 https://arxiv.org/pdf/2011.13256 知识蒸馏(KD)已被证明是训练紧凑密集预测模型的简单有效工具。通过从大型教师网络转移而来的额外监督来训练轻量级学生网络。大多数先前的针对密集预测任务的KD变体都在空间域中对学生网络和教师网络的激活图进行对齐,通常是通过在每…

【Linux】进程间通信 - 管道

文章目录 1. 进程间通信介绍1.1 进程间通信目的1.2 进程间通信发展1.3 进程间通信分类 2. 管道2.1 什么是管道2.2 匿名管道2.3 用 fork 来共享管道原理2.4 站在文件描述符角度 - 深入理解管道2.5 站在内核角度 - 管道本质2.6 管道读写规则2.7 管道特点 3. 命名管道3.1 匿名管道…

linux代码实操——信号的使用

信号的基本概念 信号是系统响应某个条件而产生的事件&#xff0c;进程接收到信号会执行相应的操作。 与信号有关的系统调用在“signal.h”头文件中有声明 常见信号的值&#xff0c;及对应的功能说明&#xff1a; 修改信号的响应方式 – signal() 我们来做个小实验: 在键盘上…

推荐书单|提升境界、思维能力

1、《别做正常的傻瓜》 豆瓣评分&#xff1a;8.1 通过揭示人们在日常生活中常见的非理性行为&#xff0c;引导读者认识并克服这些行为&#xff0c;从而做出更明智的决策。 2、《活法》 豆瓣评分&#xff1a;8.1 稻盛和夫分享其人生哲学和经营哲学的著作&#xff0c;强调了正确…

软设之段页式存储

页式存储&#xff1a;将程序与内存划分为同样大小的块&#xff0c;以页为单位将程序调入内存。 比如说每页是1M&#xff0c;不管是内存还是程序&#xff0c;每页都是1M 高级程序语言使用逻辑地址&#xff0c;比如C语言用的指针。 运行状态时&#xff0c;内存中使用物理地址。…

【C++】模板初阶:泛型编程的起点

&#x1f49e;&#x1f49e; 前言 hello hello~ &#xff0c;这里是大耳朵土土垚~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f4a5;个人主页&#x…