vue3
主要内容
核心:ref、reactive、computed、watch、生命周期
常用:hooks、自定义ref、路由、pinia、miit
面试:组件通信、响应式相关api
----> 笔记:ts快速梳理;vue3快速上手.pdf
笔记及大纲 如下:
目录
3.1 OptionsAPI 与 CompositionAPI
reactive------>(只能定义)对象类型的响应式数据
情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性
1. 基础(p01-)
vue3介绍
2020.9.18 vue3发布
本文按照,2023.10发布的公开版本 3.3.4 来讲解
特点:
- 性能提升:打包体积减少、初次渲染更快、更新更快、内存减少
- 源码升级:用 Proxy 代替defineProperty实现响应式;重写虚拟DOM的实现和Tree-Shaking
- vue3 支持TypeScript
- 新特性:
- Composition API(组合式API):setup、ref 与 reactive、computed 与 watch
- 内置组件:Fragment、Teleport、Suspense...
- 其他改变:新的生命周期钩子、data 选项应始终被声明为一个函数;移除 keyCode 支持作为 v-on 的修饰符
2. 创造vue3工程
方法一:vue-cli 创建;方法二:vite 创建(推荐)
vite是新一代前端构建工具,优势:
- 轻量快速的热重载(HMR),极速服务启动。
- vite的构建速度,比 webpack快
- 真正的按需编译(见下图),不再等待整个应用编译完成。
- 对TypeScript、JSX、CSS等支持开箱即用
webpack构建 与 vite构建 对比图如下:
webpack:从entry入口进去,分析route路由,分析模块module,处理Bundle,工程启动Server ready。(分析路由和模块耗时)
vite:server ready 项目启动,入口entry进去,看哪个路由和模块 则立刻处理,不看 则不处理。----> 构建快。
方法二:vite 构建 的具体操作(参考 vue3官方文档——快速上手)
①创建命令:
npm create vue@latest
②配置项目:名称、ts支持、jsx支持、router路由环境、pinia状态管理、vitest单元测试、end-to-end 端到端测试、eslint 语法检查、prettier 代码格式化
注:创建前,确保有后端 node环境,去官方下载(如果没有node 则没有npm)
如何判断:命令行,输入node,输出为版本号 则有;输出为不是内部命令 则无,去官网下载node安装,安装后重启电脑。
软件推荐:vscode
目录:
- .vscode/extensions.json 配置插件的文件夹(打开时,会推荐安装 配置的扩展文件,更便捷。可删)
- public/favicon.ico 页签图标
- src 工作成果(.js、.css、.vue的文件)
- env.d.ts (飘红是因为没有安装依赖,执行 npm i 安装所有依赖 即可解决。ts 不认识 txt 文件等,env.d.ts 把所有可能用的文件都做了声明)
- index.html 入口文件(只是把这个文件呈现到页面中)(前端启动项目,查看 package.json包,运行 npm run dev)
- package.json 和 package-lock.json 包管理文件/依赖声明文件
- README.md 简单的对工程进行分介绍
- tsconfig.json 和 tsconfig.app.json 和 tsconfig.node.json ts的配置文件
- vite.config.ts 整个工程的配置文件(用途:安装插件、配置代理)
- src/main.ts
编写 App 组件
注:功能,添加这两插件(若已在 .vscode中 配置好插件,打开vscode后 将提示安装)
步骤:(创建前端应用,把app传进去,并挂载到 index.html 中的 id="app" 中)
1. index.html 引入 main.ts文件
2. main.ts 中,创建前端应用,并把 app 传进去,将其挂载到 index.html 中的 id="app" 上
3. 确定 index.html 中,id="app"(摆了花盆) + 引入 main.ts文件
src中
- main.ts
- App.vue 组件(根)
- components 组件(树枝)
删掉 src,手写 src 的步骤:
1. 先写main.ts
2. 写 App.vue(三个东西必须有)
问题:删掉src,ts 配置文件报错,找不到 src文件
解决:重新打开vscode应用;如果未能解决,可以等代码写完,自然就好了。
3. vue3的核心语法
3.1 OptionsAPI 与 CompositionAPI
3.2 setup语法糖
vue2中
- 不可以有多个根标签
- 通过 data(){..} 存放数据,methods: {} 方法
setup中
- setup函数中的 this 是 undefined
- vue2中的选项式的语法,可以与vue3的setup语法共存
- 旧写法可以读取setup的数据,setup语法不能读取旧写法(data、methods)的数据
- vue2的setup函数中,数据和方法要 return{} 交出去
- 执行周期:先 setup,再 beforeCreate 建立。
- 直接写在 setup中的数据,不是响应式的。
- setup 返回值,可以直接指定渲染内容(return ()=>'哈哈')
两个 script(一个配置名,一个配置组合式api),要为同一种语言,如 lang="ts"
<script setup>语法糖
配置名 可以通过插件完成
①终端中:npm i vite-plugin-vue-setup-extend -D
② vite.config.ts 中,添加
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
export default defineConfig({
plugins: [
VueSetupExtend(),
]
})
③修改完重新运行开发服务器(ctrl+c 终止,npm run dev 重新运行)
④这时候,可以在 <script setup name="Person1233"> 直接写 name
3.3 响应式数据 ref、reactive
vue2中,data中的即是响应式数据(里头做了,数据代理 和 数据劫持)
vue3中,两种方式可以做响应式数据:ref 和 reactive
ref ------>基本类型的响应式数据
想让哪个数据为响应式,则拿 ref('xxx') 包一下
特点:ref 是函数;传递一个初始化数据;是会发生变动的数据。
返回值:是 RefImpl的实例对象,其 不带_ 是给我们用的。
使用时:模板中使用时不需要.value(它自动添加了);js中使用时要添加 .value
reactive------>(只能定义)对象类型的响应式数据
用 reactive({ 对象 }) 包裹
reactive 返回值是Proxy代理对象,(返回结果是响应式的),其 Target为 数据内容。
reactive是深层次的,reactive包裹对象,如a.b.c
注:
- 使用 v-fo r中,key是每个节点的唯一标识,:key="xx.id" 中的冒号: 是将其用 js 表达式 来解析,其完整的指令是 v-bind:key="xx.id"
- 选中,直接按圆括号,直接包起来了。
ref---------->对象类型的响应式数据
ref 也可以定义对象类型的数据,ref 定义对象 底层还是通过 reactive 实现
数组对象,要先 .value,再 [ ]
总结:ref 对比 reactive(如何使用)
ref,必须使用.value(可以用 插件Volar,自动添加.value)
reactive的局限性,重新分配对象,则失去响应式。
解决:(法一:可以使用 Object.assign(obj1,obj2,obj3) 分配,将obj1后面添加obj2,在添加obj3;法二:可以用ref,但要加 .value,因为.value必然是响应式的)
使用原则:
- 基本类型--------------------------> 必用 ref
- 对象类型,层级不深-----------> ref、reactive都可
- 对象类型,且层级较深--------> reactive
3.4 toRefs 与 toRef 解构赋值
原先,解构传来的,不是响应式的数据。要添加 let { 数据1,数据2 } = toRefs(对象)
toRefs、toRef,把一个响应式对象的东西,解构拿出来,且同时具备响应式能力。
解构出来的返回值是 ObjectRefImpl (ref 定义的响应式数据)
3.5 computed 计算属性
应用场景:实现输出双向绑定-------》要求首字符大写(大量使用模板插值,模板不简单了/模板太复杂了,在模板里计算,未做到结构和交互分离)------------》使用计算属性。
模板太复杂了,这时可以使用计算属性 computed。
计算属性的特点:
- ①computed 依赖的数据只要发生变化,它就会重新计算。
- ②计算属性有缓存,方法没有缓存。
- ③计算属性的 返回值是 ComputedRefml(ref 的响应式数据)
- ④计算属性,是只读的。可读可写要加 get、set
① computed 依赖的数据发生变化,计算属性会重新计算
④计算属性中,写 get、set 函数,才可以可读可写
3.6 watch 监视(重要)
作用:监视数据的变化(vue2和vue3的作用一样)
watch 的特点:
- watch中 不需要添加 .value.(可以一用Volar插件,自动带.value)
- watch 调用有返回值,是一个函数。(停止监视)
watch的参数:
- 第一个参数是:被监视的数据
- 第二个参数是:监视的回调
- 第三个参数是: 配置对象(deep、immediate等等)
四种变化:
- ① ref 定义的数据
★情况一:监视【 ref 】定义的 【基本类型】
watch(对象,回调函数)
情况二: 监视 【ref 】定义的【对象类型】
监视 ref 定义的 【对象类型】数据,监视的是对象的地址值。
若想监视对象内部属性的变化,需要开启深度监视。(watch的第三个参数,配置deep:true)
watch第三个参数:配置对象(如 深度监视 deep、立即执行 immediate 等等)
①(原先)只监视整个对象发生变动
② 加上 { deep: true } ,可以监视对象内部的属性变化
③ 新值和旧值
- 如果只是改对象的属性,新旧值是同一个;
- 如果把整个对象换了,新旧值是不同的值。
- 如果只写一个,就是 新值,一般不管旧值
情况三:监视【reactive】定义的【对象类型】
【reactive】 定义的【对象类型】数据, 且默认开启了深度监视(深度监视不可关闭的,隐式的开启了深度监听。)
------》对 对象内部的属性 也进行了监视。
reactive 定义的 对象,不可整体对对象修改,要用 Object.assign(对象,{...}) ,替换内容(属性名相同,值覆盖了)( Object.assign(对象,{...}) 未创建新对象)。
★情况四:监视【ref】或【reactive】定义的【对象类型】数据中的某个属性
- 若该属性 是 基本类型(不是对象类型),要写成函数形式(可以用箭头函数)
- 若监视的对象的某个属性依然是 【对象类型】,可以直接写(属性),也可写成函数,建议写成函数!
★ 结论: 监听的要是对象里的属性,最好写函数式,注意点:若是对象监听的是地址值,要关注对象内部,需要手动开启深度监听。
★ 监听对象的某属性的最佳实践:【监听源 函数式】+【深度监听】
- 监听源,写 函数式(函数式,监听的是对象的地址值)----> 监听整个对象
- 加 深度监听-------------------------------------------------------------> 监听对象内部的属性
具体的:
1. 若该属性 是 基本类型(不是对象类型),要写成函数形式(可以用箭头函数)
2. 若监视的对象的某个属性依然是 【对象类型】,可以直接写(属性),也可写成函数,建议写成函数!
① 监听源,直接写 属性
存在问题:
- 修改前两个,可以成功监听;
- 但是修改整个对象时,控制台没有输出,未能成功监听。(即使加上深度监听,也无济于事)
因为当监视整个对象时,监视的对象被重新赋值新的对象,被覆盖了,即 监视的对象被删了,此时,监视整个对象已不存在,监视失败。
深度监视
注: 一般,什么时候开启深度监视?
- 本来是 监视地址值;
- 当想要让其 监视内部细节(属性)时,开启深度监视。
② 监听源,写成函数式
- 监听源写成函数式(监听的是 对象的地址值)-----------> 整个对象可以监听
- 内部属性不被监听
监听源
无效监视源:非 getter函数、ref、reactive、以上组成的数组类型
(getter函数:能返回一个值的函数)
★监听的源 必须是: getter函数(返回一个值的)、ref、reactive、以上值 组成的数组类型。
情况五: 监视上述多个数据
- 监视源,写数组,可监视数组中多个数据。
- 例子,监视 某对象中的属性: ①属性为【基本类型】,要用 【函数式】;②属性为【对象类型】,可以用【直接写】,也可以用函数式。--->即,情况四
- 监视的是整个数组,新值、旧值也是整个数组
如,监视对象中的某个属性:
因为,① name和c1都是普通字符串,即 基本类型 ---------->所以,监视源要用 函数式
② 监视的属性是【对象类型】----> 可以 【直接写】
tips:watch特别重要,响应式数据、计算属性写得好,写功能基本没太大问题。有问题的话,有时候可能是时机不对,即 生命周期不对。
3.7 watchEffect
watch 与 watchEffect 的区别:
- watch 需要明确指出要监听的数据
- watchEffect 无需明确指出要监视的对象,它会自动分析(监听的对象 / 谁要监听)
3.8 标签的 ref 属性
作用:用于注册模板引用,即 给节点打标识。
两种情况:
- ① 用在【普通 DOM 标签】(即【 html标签】)上-----> 获取的是 DOM 节点
- ② 用在【组件标签】上 ------> 获取的是 【组件实例对象】,其数据是隐藏的-------> 要显示数据,将其暴露出去(做法:子组件中引入 de)
① ref 加在【html标签】上
标签的 ref 属性:创建一个xxx,用于存储 ref 标记的内容
② ref 加在【组件标签】上(即 【.vue文件】)
------> 返回值的数据隐藏,它返回的是一个实例对象
------> 解决:显示数据,在子组件中 引入 deExpose 并将要显示的数据 暴露出来(做法:引入 defineExpose,最后返回 defineExpose({ 数据1, 数据2, 数据2...}))
具体内容:
大部分都加在 html标签上,而不加在组件标签上
因为加在组件标签上,只显示组件的实例对象,不显示具体的数据内容,子组件内部的数据被隐藏了(这是种保护措施)
解决: 引入 defineExpose,最后返回 defineExpose({ 数据1, 数据2, 数据2...})
局部样式
css中有 scoped,则是局部样式。
vue2写法如何改vue3:
在 script 标签中,
- 加 setup
- 加 name="" (采用了插件组件别名简写)
在 script 标签内,
- 去掉整个 export default,以及内部的组件名和注册组件
注:为何不需要另外注册组件?因为 import 引入组件 时,会自动 return ,return后 就可以直接用了。
TS中的 接口、泛型、自定义类型
问题:js中,出现类型错误、书写错误,程序未报错。
解决:用ts进行类型规范。
- 接口,定义对象的一个规范
- 泛型,定义对象的多个规范
- 自定义类型,给对象定义多个泛型规范
创建类型规范文件夹
src/types 各种类型文件夹
- index.ts 写该文件名,名字好,后期引入无需再写 types/index.ts,可以直接写 types
ts中的接口
定义一个接口,限制数据。(-------> 限制一个对象)
例子,
要求:Person对象中的属性名 id、name、age不能写错,以及其对应的数据类型不能写错
作用:定义一个接口,限制 Person 人的对象的具体属性。
定义接口:interface 接口名 {},并暴露 export 出去------> export interface 接口名 {}
不能打印一个规范,只能打印一个具体的值。
引入时,需要加上 type
js 与 ts 接口语法的区别
- js 接口语法:interface 接口名 = {...}
- ts 接口语法: interface 接口名 {}
(注: js有等号;ts没有等号(即,直接写))
★ ts 中,暴露的三种方式:默认暴露、分别暴露、统一暴露
- 分别暴露:export xxx,即 在接口前加上 export,也就是 export interface 接口名 {...}
- 统一暴露: export { xxx }
ts中的泛型
限制多个对象,可使用泛型,使其符合规范。
泛型:不知道将使用哪种类型,需要在使用时才知道是哪种类型
做法:
- ① 定义接口
- ② 定义规范
- 用接口规范时,等号右边是对象,只能约束一个对象;
- 使用泛型规范时,等号右边是数组,这时候类型是Array数组,规范是PersonInter,写法为 let 变量名:类型<接口规范>,可用于规范多个对象
ts中的自定义类型
问题:要用多个泛型去规范------> 解决:使用 自定义类型
自定义类型的写法:export type 名称 = 类型<泛型>
vue3 继续
3.9 props的使用
Props:将父组件中的数据,传给子组件。
具体代码步骤:
import { defineProps } from 'vue'
defineProps([ ' 父组件的属性名 ' ]) --------> 即 defineProps([' 父组件传过来的数据 '])
template中,使用模板插值法 { { 父组件的属性名}} 显示数据---------->即使用 { { 数据 }}
步骤:
① 子组件中,引入 defineProps
② 使用defineProps( ) 里头传数组,数组里头为字符串,字符串为父组件的属性名
③在模板中,使用该数据(父组件的该数据的属性名),即 可使用有父组件传过来的数据
注:就算只有一个数据也一定要用数组
使用 Props 接收数据的几种情况(子组件中):
- 直接接收 list,并输出 ---------- defineProps([' list ']) -----------> 会报错
- 接受list,同时将props保存起来----- let x = defineProps(['list']) console.log(x.list)
- 接受 list +限制类型------- import { type Persons } from '@types' defineProps< { list: Persons } >() ---- 用泛型,传一个对象,要求里头的 list 必须是Persons 类型。(其中,list为父传子的数据,Persons为 ts 规范)
- 接受 list+限制类型+限制必要性+指定默认值 ------- withDefault( defineProps<list:Persons>(),{ 数据: () => [{默认值....}] })--------->必须要 函数返回
★ 接收 list+限制类型+限制必要性+指定默认值(代码讲解):
注: defineXxxx可以直接使用(不用引入):
defineXxxx属于宏函数,不用引入,即可直接使用。----------> 也就是说,不需要 import { defineExpose,defineProps } from 'vue',就可以直接使用
注:ts 类型限制
① 通过 ts 规范,进行类型限制
② reactive 直接传泛型,reactive< ts规范 >,使用泛型进行类型限制
注:定义数据可选---> 使用"?",即 在 ts 规范中,使用 数据?:类型 定义
3.10 生命周期
生命周期:组件的一生。
生命周期、生命周期函数、生命周期钩子,在特定的时刻,调用特定的函数
vue2和vue3的生命周期不同
4个阶段:创建、挂载、更新、卸载。 8个生命钩子(但钩子不止八个,后面会学)
vue2和vue3的区别:
- vue3 的创建阶段是 setup ,不是beforeCreate、created
- vue3 前面多了一个on
- vue3 最后一个阶段为【卸载】,【onBeforeUnmount】
vue2的生命周期:
- 创建前后:beforeCreate、created
- 挂载前后:beforeMount、mounted
- 更新前后:beforeUpdate、Updated
- 销毁前后:beforeDestroy、Destroyed
vue3的生命周期:
- 创建:setup
- 挂载:onBeforeMount、onMounted()
- 更新前后:onBeforeUpdate、onUpdated
- 卸载前后:onBeforeUnmount、onUnmounted
挂载前:onBeforeMount(()=>{ }),vue3在挂载前,调用onBeforeMount里的那个指定的函数
其他知识点:
创建 vue2 的脚手架:vue create vue2_test
如何启动项目:
包管理文件 --> script 选项 --> (不清楚的话,可以挨个试,必定在其中)---> 如这里,应在终端执行 npm run serve
3.11 自定义hooks
hooks:让一个功能的数据和方法贴在一起 ----> 即,封装/模块化开发,封装成一个文件 useXxx.js/useXxx.ts
(vue3 中的 hooks 把模块化开发发挥到极致,类似于 vue2 中的 mixin 混合)
hooks的创建步骤:
子组件中,
① 新建文件,命名规则:use+功能.js/ts
② 封装,数据和方法,用函数包裹起来:export default function() { }
③ 向外部提供东西:return { xxx } 或 return [ ] ,(给外部提供数据和方法)
父组件中,
④ 引入 hooks,并在模板中直接调用
hooks 本质上是一个个可以调用的函数,hooks 使得 数据和方法都是集中管理的
引入hooks,即 引入 useXxx.js 并将其解构成一个个可以直接使用的函数
hooks中,可写数据 和方法
(如上所述)
hooks中,可以写钩子,还可以写计算属性(也是数据)
钩子,如 onMounted(()=>{ })
axios相关知识点
安装 axios:npm i axios
发请求: .then() 或 async...await
.then() 代码如下:
axios.get('接口地址').then(
response => { },
error => { }
)
其中,response 为成功的回调,error 为失败的回调
async...await 代码见下方的异常处理代码截图中红色部分
处理异常:try catch 或 axios的拦截器
try...catch :可以进行错误的处理 ------------------------> 每次处理一个接口请求--->代码如下
axios的拦截器:可以统一进行错误的请求与处理-----> 可以每次处理多个请求
注:本课程的接口,【无跨域问题】已解决跨域问题,接口可以直接用;【比较稳定】。
4. 路由
路由 route:是一组 key-value 的对应关系。多个路由需要经过路由器的管理
路由器 router:路由器监测路径变化,路由器进行路由匹配;路径变化时,路由器在第一时间捕获,并寻找规则,找到规则后,把旧的组件进行卸载,把新组件进行挂载。
SPA(single page web application)单页应用 -------> 只有一个 html 页面
切换效果:
创建路由的步骤:
① 导航区、展示区(静态模板)
② 配置路由
③ 配置路由模式
④ 将路由暴露出去
⑤ 在main.ts中,引入路由,并挂载?
⑥ 使用
注意点
to的两种写法
路由器的工作模式
命名路由
嵌套路由
路由传参
props 配置
replace 属性
编程式导航
重定向