vue3 入门
本章内容
- vue3简介
- vue3项目构建工具vite
- vue3组合式API
- vue3响应式原理–Proxy+Object.defineProperty()
- computed计算属性
- watch侦听器
- 函数的使用
- 新增内置组件
一、vue3
1.1 简介
- 2020年9月18日发布,3.0版本 代号 ‘one piece’。
- 3.0版本代表了超过2年的开发工作,包括30多个RFC、2600多个提交、来自99个贡献者的628个请求,以及核心回购之外的大量开发和文档工作。
- gitHub地址:https://github.com/vuejs/core/releases/tag/v3.0.0
1.2 优势
- 与Vue 2相比,Vue 3在打包大小(体积最多轻41%)、初始渲染(最多快55%)、更新(最多快133%)和内存使用(最多少54%)方面都有显著的性能改进。
- 使用Proxy代替Object.defineProperty()实现响应式
- 重写虚拟DOM的实现 diff 算法
- 更好的支持TypeScript
- TreeShaking(打包时把无关代码全部干掉,体积更小)
1.3 新的特性
- Vue 3 中一些需要关注的新功能包括:
- setUp配置
- ref和reactive
- watch和watchEffect
- provide与inject
- Teleport 传送门
- 片段
- 触发组件选项
- 新的生命周期钩子
- 来自
@vue/runtime-core
的createRenderer
API ,用于创建自定义渲染器 - Suspense 实验性
- 。。。。。。
二、Vue3项目构建
2.1 vue-cli构建项目(现阶段开发必用)
#vue3对vue-cli的版本要求必须在4.5.0以上 执行命令:vue -V查看vue-cli版本 如果版本低于4.5.0 可以重新执行命令 npm install -g @vue/cli安装最新版 ## 创建项目 vue create 项目名 ## 启动项目 cd 项目目录 npm run serve
2.2 vite构建项目(vue官方出版的)
2.2.1 vite简介
官网地址:https://vitejs.cn/
Vite(法语意为 “快速的”,发音 /vit/
,发音同 “veet”)是一种新型前端构建工具,能够显著提升前端开发体验。它主要由两部分组成:
- 一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。
- 一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
Vite 意在提供开箱即用的配置,同时它的 插件 API 和 JavaScript API 带来了高度的可扩展性,并有完整的类型支持。
grant gulp webpack
2.2.2 传统构建与vite构建对比图
基于打包器启动时,重建整个包的效率很低。原因显而易见:因为这样更新速度会随着应用体积增长而直线下降。
一些打包器的开发服务器将构建内容存入内存,这样它们只需要在文件更改时使模块图的一部分失活[1],但它也仍需要整个重新构建并重载页面。这样代价很高,并且重新加载页面会消除应用的当前状态,所以打包器支持了动态模块热重载(HMR):允许一个模块 “热替换” 它自己,而不会影响页面其余部分。这大大改进了开发体验 —— 然而,在实践中我们发现,即使采用了 HMR 模式,其热更新速度也会随着应用规模的增长而显著下降。
在 Vite 中,HMR 是在原生 ESM 上执行的。当编辑一个文件时,Vite 只需要精确地使已编辑的模块与其最近的 HMR 边界之间的链失活[1](大多数时候只是模块本身),使得无论应用大小如何,HMR 始终能保持快速更新。
2.2.3 构建vite项目
## 创建项目 npm init vite-app 项目名 ## 进入项目 cd 项目名 ## 安装依赖包 vite项目需要自己手动安装依赖包 npm install ## 运行项目 npm run dev
2.2.4 vite构建vue3项目的结构分析
- 目录结构基本没有什么变化,用法一样
- main.js分析
//此处的引入import 不在是 Vue的构造函数了 引入的是一个名为 createApp的工厂函数。 createApp(App).mount("#app")这句代码可以拆分成两部分代码 //创建实例对象 //注意:此处创建的app跟之前的vm实例对象不一样了,可以看成是一个轻量级的vm实例对象。 let app = createApp(App) //挂载模板 app.mount('#app')
- vue2实例对象与vue3实例对象的对比
vue2的实例对象:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZdA6uB2G-1655452084593)(assets/image-20220308104303764.png)]
vue3的实例对象:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QfO6UVqi-1655452084594)(assets/image-20220308104022830.png)]
**注意:**在vue3构建的项目中无法再使用vue2创建vue实例的Vue
构造方法了。必须使用vue3的写法,引入createApp()
工厂方法 - 普通组件中的变化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R1GwiOcF-1655452084596)(assets/image-20220308104731384.png)]
**注意:**定义组件时 DOM结构可以不用根标签包括了。
2.3 vue3配套的开发者工具
打开chrome应用商店 下载beta版 就是vue3的开发者工具版本,之前安装的应该也可以使用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kB9O8dNs-1655452084596)(assets/image-20220308112040949.png)]
三、vue3常用的组合式(Composition)API
3.1 什么是组合式API
通过创建 Vue 组件,我们可以将界面中重复的部分连同其功能一起提取为可重用的代码段。仅此一项就可以使我们的应用在可维护性和灵活性方面走得相当远。然而,我们的经验已经证明,光靠这一点可能并不够,尤其是当你的应用变得非常大的时候——想想几百个组件。处理这样的大型应用时,共享和重用代码变得尤为重要。
使用 (data
、computed
、methods
、watch
) 组件选项来组织逻辑通常都很有效。然而,当我们的组件开始变得更大时,逻辑关注点的列表也会增长。尤其对于那些一开始没有编写这些组件的人来说,这会导致组件难以阅读和理解。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3EoQG5rU-1655452084598)(assets/options-api.png)]
这是一个大型组件的示例,其中逻辑关注点按颜色进行分组。
这种碎片化使得理解和维护复杂组件变得困难。选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块。
如果能够将同一个逻辑关注点相关代码收集在一起会更好。而这正是组合式 API 使我们能够做到的。
3.2 组合式API基础使用----setup
既然我们知道了为什么,我们就可以知道怎么做。为了开始使用组合式 API,我们首先需要一个可以实际使用它的地方。在 Vue 组件中,我们将此位置称为 setup
。
为什么要使用setup()函数: https://zhuanlan.zhihu.com/p/68477600
3.2.1 setup
组件选项
**注意:**在 setup
中你应该避免使用 this
,因为它不会找到组件实例。setup
的调用发生在 data
property、computed
property 或 methods
被解析之前,所以它们无法在 setup
中被获取。
setup
选项是一个接收 props
和 context
的函数,我们将在之后进行讨论。此外,我们将 setup
返回的所有内容都暴露给组件的其余部分 (计算属性、方法、生命周期钩子等等) 以及组件的模板。
**使用方式一:**在vue3中 之前定义的data,methods,computed,watch,directives还都仍然可以使用 用法跟vue2是完全一样的,但是过滤器不能使用了
<template> <h1>{{ msg }}</h1> <button @click="count++">count is: {{ count }}</button> <p>{{msg1}}</p> <p v-color='"blue"'>计算属性{{aaa}}</p> <p>过滤器{{count | bbb}}</p> <button type="button" @click="changeMsg">改变msg</button> </template> <script> import {ref} from 'vue' export default { name: 'HelloWorld', props: { msg: String }, data(){ return { count:0, msg1:'', } }, methods:{ changeMsg(){ this.msg1 = this.msg } }, computed:{ aaa(){ return this.count+1 } }, watch:{ count(newVal,oldVal){ console.log(newVal,oldVal) } }, filters:{ bbb(val){ return val+5 } }, directives:{ color(el,binding){ el.style.color = binding.value } } } </script>
使用方式二:setup基本用法
在实例中不在配置data,methods,computed…等独立配置项 而是在其中添加一个setup(){}
项, 所有的配置内容都写在其中,最终把页面中需要用到的数据通过return{} 暴漏出去。
<template> <h1>这里是组件{{age}}</h1> <button type="button" @click="age+=1">点我</button> </template> <script> import {ref} from 'vue' export default { components:{}, //定义setup函数 setup(){ let name = '张三' let age = ref(58) return{ name,age } } } </script>
注意:
- setup是vue3中的一个配置项
- 组件中需要用到的所有的数据, 函数,等都要配置在setup函数中
- setup函数中需要有return返回值 返回的是一个对象 其中包含的数据,可以在页面中直接使用;
- 在vue3中仍然可以使用vue2的data,methods等配置项,并且 这些配置项中也可以访问到setup中配置的信息.
但是setup中无法访问到vue2配置中的信息。 重名变量,setup优先. - vue2和vue3的语法推荐大家不要混合使用, 用哪一个就全部都用那一个!
3.3 ref函数的使用
3.3.1 ref函数的使用----基本数据类型
**发现问题:**在以上案例中,如果我们想点击按钮修改age的值,会发现 页面中是无法更新渲染age数据的 为什么?
带 ref
的响应式变量
在 Vue 3.0 中,我们可以通过一个新的 ref
函数使任何响应式变量在任何地方起作用,如下所示:
//一定要先导入 import { ref } from 'vue' const counter = ref(0)
ref
接收参数并将其包裹在一个带有 value
property 的对象中返回,然后可以使用该 property 访问或更改响应式变量的值:
import { ref } from 'vue' const counter = ref(0) console.log(counter) // { value: 0 } console.log(counter.value) // 0 counter.value++ console.log(counter.value) // 1
将值封装在一个对象中,看似没有必要,但为了保持 JavaScript 中不同数据类型的行为统一,这是必须的。这是因为在 JavaScript 中,Number
或 String
等基本类型是通过值而非引用传递的:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fyLxXbYc-1655452084599)(assets/pass-by-reference-vs-pass-by-value-animation.gif)]
在任何值周围都有一个封装对象,这样我们就可以在整个应用中安全地传递它,而不必担心在某个地方失去它的响应性。
换句话说,ref
为我们的值创建了一个响应式引用。在整个组合式 API 中会经常使用引用的概念。
使用:let age = ref(58)
此句代码 58 被ref包括后 并不是说age=58 此时的age是一个对象RefImpl{}
其中包含一个属性value, 我们使用age.value
才可以真正的取到数值58 ;
此处的控制台输出为:
小结:
- 页面模板中展示数据直接使用向外返回的属性名就可以了 不需要加.value
- 在setup中定义的基本数据类型如果想实现响应式,必须用ref()包括 , 并且如果在setup中想对数据做修改 , 必须通过
属性名.value
才可以完成 ;
3.3.2 ref函数的使用----对象类型
在setup中添加如下代码 son对象: 点击按钮 改变 son中的数据
<template> <h2>儿子的名字:{{son.name}}</h2> <h2>儿子的年龄:{{son.age}}</h2> <button type="button" @click="changeSonAge">点我改变儿子的年龄+1</button> </template> <script> //需要先导入ref函数 import {ref} from 'vue' export default { components:{}, setup(){ let son = ref({ name:'张三', age:18 }) function changeSonAge(){ //报错 son.value.age.value += 1 //有效代码 son.value.age += 1 console.log(son) } //返回页面中需要使用的数据 return{ son,changeSonAge } } } </script>
此时虽然同样是用 ref包括的数据 但是操作时 只需要取出 son.value
操作属性就可以了, 不需要使用son.value.age.value
这样会报错 ;
原因: 此处我们打印 son
对象 发现 被ref()包括的son对象是如下形式:
此时虽然整体的son还是 RefImpl
形式 , 但是其中的value
属性却变成了 proxy
形式 是 ES6的新特性 : 代理
小结:
- ref包括对象类型的数据 生成也是
RefImpl
形式 - 其中的value属性 是
proxy
形式
注意:
- vue3中ref对基本类型的响应式原理 仍然是
Object.defineProperty()
getter和setter - vue3中ref 对引用对象类型的响应式原理是 ES6中的
proxy
3.4 reactive 函数的使用
简介:
- 使用reactive来定义一个对象类型的响应式数据, (基本类型使用ref函数)
- 基于ES6的proxy代理来实现 响应式原理
使用:
<template> <h2>儿子的名字:{{son.name}}</h2> <h2>儿子的年龄:{{son.age}}</h2> <button type="button" @click="changeSonAge">点我改变儿子的年龄+1</button> </template> <script> import {ref,reactive} from 'vue' export default { components:{}, setup(){ let son = reactive({ name:'张三', age:18 }) function changeSonAge(){ son.age += 1 console.log(son) } return{ name,age,son,changeSonAge } } } </script>
此处把son对象使用reactive
函数包括起来 此时再想操作son中的age属性就不需要添加value了 可以直接使用son.age
操作, 因为此时返回的直接就是 proxy
代理对象 把son对象换成数组 一样可以操作 , 并且可以直接通过下标来对数组进行修改(vue2中不行) ;
注意: ref函数包括对象类型实现响应式就是因为 ref在底层调用了reactive
函数 ;
小结:
- vue3中基本类型都使用ref函数包括
- 对象(引用类性)都是用 reactive函数包括
- 使用之前记得先引入函数。
3.5 setup函数中的两个参数和自定义事件的使用
setup完整语法格式:setup(props,context){}
- props 参数 表示接收父组件传递来的数据
- context是当前组件的上下文 我们在控制台打印context可以得到如下图所示内容[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wa3GIxwK-1655452084600)(assets/image-20220308220827367.png)]
- attrs: 当子组件中没有声明props属性时, 父向子传递的数据会保存在attrs中
- emit: 自定义事件会保存在此处
子组件中:
<button @click="bbb">子组件中触发自定义事件</button> export default{ //此处添加emits属性 把自定义的事件类型填入 emits:['hello'], setup(props,context){ function bbb(){ console.log('hello自定义事件触发了') //通过context调用emit方法来完成自定义事件 context.emit('hello','你好哈哈哈') } return{ bbb } } }
- 父组件中:
<Son @hello='aaa'></Son> setup(){ //自定义事件触发aaa函数 val就是子组件传递来的数据 function aaa(val){ console.log('接收到子组件传递来的数据:',val) } return{ aaa } }
- **slots:**插槽内容会保存在此处(了解)
其中包含的只是 虚拟DOM
3.6 provide与inject / 祖孙(后代)传递数据
**简介:**通常,当我们需要从父组件向子组件传递数据时,我们使用 props。想象一下这样的结构:有一些深度嵌套的组件,而深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将 prop 沿着组件链逐级传递下去,可能会很麻烦。
对于这种情况,我们可以使用一对 provide
和 inject
。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个 provide
选项来提供数据,子组件有一个 inject
选项来开始使用这些数据。
**主要作用:**父组件向后代组件传递数据。
在祖组件
使用privade
定义要传递的数据,在后代组件
中通过inject
来接收参数。
//祖组件中 定义privade传递数据 provide('fage',fage.value) //后代组件 接收privade数据的方式二 let fage = inject('fage')
vue3技术简易入门剖析(二)https://developer.aliyun.com/article/1432713