Vue3–2天+ ts2天+ pinia1天+ 项目7天
项目笔记:
简介 | 优医问诊H5
黑马网上项目:
https://www.bilibili.com/video/BV1HV4y1a7n4?p=151&vd_source=d824e6f1c7311e50c5b96a40803b1243
了解:vite 工具作用和特点
vite(法语意为 “快速的”,发音 /vit/,发音同 “veet”) 是一种新型前端构建工具,能够显著提升前端开发体验。
注明:项目打包的时候最终还是需要打包成静态资源的,打包工具 Rollup
问题:
yarn create vite my-vue-app --template /vue关于yarn创建vite所遇到的坑,创建vite错误_前端筱攻城狮的博客-CSDN博客
# 使用npm
npm create vite@latest
# 使用yarn
yarn create vite
# 使用pnpm
pnpm create vite
安装依赖使用pnpm install
对 vite 初始化的代码进行分析
vue3 组件代码和 vue2 有些不一样,使用的语法提示和高亮插件也不一样。
import { createApp } from 'vue'
import App from './App.vue'
// 根据App组件创建一个应用实例
const app = createApp(App)
// app应用挂载(管理)index.html的 #app 容器
app.mount('#app')
总结:
setup函数是组合式API的入口函数
总结:
通常使用它定义 对象类型 响应式数据
疑问:以前在 data 函数中返回对象数据就是响应式的,现在 setup 中返回对象数据是响应式的吗?
使用步骤:
通常使用它定义响应式数据,不限类型
使用步骤:
总结:
推荐:以后声明数据,统一用 ref => 统一了编码规范
Proxy第一个参数是对象,第二个值是get方法
知道:在定义响应式数据的时候如何选择reactive和ref
开始分析:
推荐用法:
总结:
简化 setup 固定套路代码 ,让代码更简洁
发现:
解法:
// setup语法糖
// 写法上script标签加上属性setup
// 相当于是简写的语法,此后我们在script标签里的写的内容直接可以在模板中使用
响应式 API:核心 | Vue.js
场景:当需要依赖一个数据得到新的数据使用计算属性
函数或者对象的形式
掌握:使用watch函数监听数据的变化
https://cn.vuejs.org/api/reactivity-core.html#watch
大致内容:
落地代码:
第三种:使用 watch 监听响应式对象数据中的一个属性(简单)
:::info
精确侦听对象的某个属性
一个函数,返回一个值
:::
第四种:使用 watch 监听响应式对象数据中的一个属性(复杂),配置深度监听
默认机制:通过watch监听的ref对象默认是浅层侦听的,直接修改嵌套的对象属性不会触发回调执行,需要开启deep选项
{
// 开启深度监听
deep: true,
// 默认执行一次
immediate: true
}
总结:
:::info
当直接侦听一个响应式reactive对象时,侦听器会自动开启深层模式。
此时只针对响应式reactive
:::
const stop=watch(()=>{})
stop()
当网站访问人数,停止监听
https://cn.vuejs.org/api/reactivity-core.html#watch
https://cn.vuejs.org/api/reactivity-core.html#watcheffect
立即执行,第一个参数回调函数,函数中,只要处理了响应式数据的字段,字段改变了,这个函数就会触发。
注意ref对象要写成count.value
想监听对象中的属性,要写点属性才能监听到
与 watchEffect() 相比,watch() 使我们可以:
watchEffect()书写简单,只要你想监听的东西都写在里面,一变化就会触发回调
缺点:性能不好,但是没有想象的那么夸张。
使用步骤:
具体内容:
8进3 vue3组合式API
使用方式 导入,调用该函数,参数是回调函数,在回调函数中写业务逻辑
Vue2 | Vue3 |
---|---|
created | setup |
mounted | onMounted |
destoryed | onUnmounted |
选项式API下的生命周期函数使用 | 组合式API下的生命周期函数使用 |
---|---|
beforeCreate | 不需要(直接写到setup函数中) |
created | 不需要(直接写到setup函数中) |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
activated | onActivated |
deactivated | onDeactivated |
引入组件 vue2导入注册使用,Vue3导入使用
import 组件名 from ‘xxx路径’
组件名驼峰或者连字符的写法
父组件中引入子组件,导入使用即可
生命周期可以加载执行多次
当点击后show为false,子组件不显示,
子组件的生命周期钩子onUnmounted打印我挂了
//子组件
onUnmounted(() => {
console.log('挂了')
})
元素上使用 ref属性关联响应式数据,获取DOM元素
步骤:
注意:默认值是null,需要在渲染完毕后访问DOM属性。
import { ref, onMounted } from 'vue'
const hRef = ref(null)
onMounted(() => {
setTimeout(function () {
console.log(hRef)
hRef.value.textContent = '帅峰真帅'
}, 2000)
})
组件上使用 ref属性关联响应式数据,获取组件实例,拿到组件上面的方法和属性
步骤:
要访问组件实例的属性和方法,组件必须暴露出该属性和方法
vue3为什么要这么做呢,因为安全,只能暴露了才能改
defineExpose不需要导入,可以直接在组件中用
语法 是个函数,函数里参数是个对象,对象里配置的属性,就是你要暴露的属性和方法
组件中暴露的ref响应式数据,使用的时候自动解套不需要.value
defineExpose------定义暴露
觉得可以拿到所有的数据和方法,并且可以随意更改随意调用 不安全
控制用户到底能拿到哪些数据哪些方法
我想拿到组件中a数据,那组件里就应该把a暴露出去
如果不暴露就拿不到
https://cn.vuejs.org/api/sfc-script-setup.html#defineexpose
src/App.vue
ref操作组件
src/components/HelloWorld.vue
:::info
vue3父传子-defineProps-参数是个对象里面的配置和vue2一样-忘记摸摸或看文档-js中用到props要用变量接收
:::
不需要导入就可以使用
目标:能够实现组件通讯中的父传子组件通讯
步骤:
注意:
App.vue
父子通信
SonCom.vue
Son Comp
父亲给我的零花钱 {{ money }}
:::info
vue3子传父-defineEmits-参数是个数组-emit变量然后触发事件
:::
不需要import导入
目标:能够实现组件通讯中的子传父组件通讯
步骤:
总结:
App.vue
父子通信
src/components/SonComp.vue
Son Comp
父亲给我的零花钱 {{ money }}
:::info
在源码里面github里面,通过1s可以用vscode打开
祖孙间通讯provide和inject函数-provide参数有两个键值对-inject参数key值
:::
为什么组件里面的form表单,有个disabled属性表单里面的属性都禁用了,用了祖孙级别的通信
对自己进行特殊的设置disabled,先看自己的属性,自己的优先级更高
vue2里面有这个属性,vue3中也有
学习语法:
provide(key,value)
inject(key)
GrandSonComp.vue
总结:
:::info
toRefs函数保持响应式-解构reactive对象会失去响应式-用toRefs保持字段是响应式-解构出来字段是ref对象 要用value获取
:::
解构出来的数据没有办法做响应式,我们使用toRefs包装,注意包装成了一个对象,要想在js中获取值,需要.value才醒
toRefs 函数的作用,与使用场景
总结:
toRefs
{{ name }}------ {{ age }}
:::info
综合案例-渲染列表数据功能-crud刷新列表封装在一个方法-发送请求时机
综合案例-删除功能-作用域插槽拿到id发送删除接口
:::
显示 渲染 删除的功能
https://zhoushugang.gitee.io/patient-h5-note/vue/case.html
mock 模拟数据
onMounted函数,里面是回调函数
prop 属性映射数据
删除
注意使用element-插槽
要用#default={row} ,模版中 使用 插值表达式去展示
重启项目,就可以获取新的mock数据
百度网盘:https://pan.baidu.com/s/1ol7In_4pzPAj2kbCAM8ATw?pwd=6nzn#list/path=%2Fsharelink3831943639-635974732214202%2F108%E6%9C%9F&parentPath=%2Fsharelink3831943639-635974732214202
在线笔记
https://zhoushugang.gitee.io/patient-h5-note/
vue3基础
https://gitee.com/qianfengg/vue3-base-108
Vue3基础综合案例
https://gitee.com/qianfengg/vue3-demo-108
vue3+ts基础
https://gitee.com/qianfengg/vue3-ts-108
在线笔记
https://zhoushugang.gitee.io/patient-h5-note/ts/
代码:
代码—基础–demo :https://gitee.com/qianfengg/vue3-base-108
e3+ts基础 https://gitee.com/qianfengg/vue3-ts-108/tree/51eb65ced60552d68ed168246c21bd8bdd9d9cae
TS补充视频课:23-元组_哔哩哔哩_bilibili
TypeScript 是一种带有 类型语法 的 JavaScript 语言,在任何使用 JavaScript 的开发场景中都可以使用。
注意:TS 需要编译才能在浏览器运行。
总结:TS 是 JS 的超集,支持了JS 语法和扩展了类型语法。
知道:TS作用是在编译时进行类型检查提示错误
发现:JS
const num = 18;
num.toLowerCase() 应该字符串才有该写法
// Uncaught TypeError: num.toLowerCase is not a function
原因:
方案:
TS 优势:写代码的时候要放在代码块里面更好演示—编译时
Vue3 + TS 最新的开发技术栈,你还在等什么?
知道:如何使用 tsc 编译 ts 代码 compile
为什么编译:浏览器不认识,全局安装typescript
全局安装:
# npm 安装
npm i -g typescript
# yarn 安装
yarn global add typescript
# 部分mac电脑安装需要sudo权限
# sudo npm i -g typescript
# sudo yarn global add typescript
查看版本
tsc -v
编译 TS:
思考:
无法重新声明块范围变量 如何解决-包个块级作用域
解决给一个块级作用域!!!!
组件card和dialog为什么标题可以传入又可以用插槽
插槽后备内容啥的xxxxx
创建一个基于 ts 的 vue 项目,来学习 ts 语法–练习ts准备的环境-创建项目的时候选ts
# yarn
yarn create vite my-vue-ts-app --template vue-ts
# pnpm
pnpm create vite my-vue-ts-app --template vue-ts
在基于 vite 的项目中可以直接验证 ts 代码结果,因为已经配置好了 ts 环境。
老师是yarn create vite 输入项目名字 回车 选择Vue TS cd进入运行yarn dev
https://juejin.cn/post/7124142007659790372
我的项目代码:E:\108期就业班\14-vue3\代码
vscode 小灯泡
小窗 vim命令
找搜索的页面,关掉的页面 ,快捷键
扩展程序,管理里面有一个vimium cu全键盘操作浏览器
知道:TypeScript 类型注解 【我的理解:注释&解释】
示例代码:
// 约定变量 age 的类型为 number 类型
let age: number = 18;
age = 19;
错误演示:
let age: number = 18;
// 报错:不能将类型“string”分配给类型“number”
age = '19';
小结:
/**
* 类型注解 (注释解释)
* 学ts小技巧,先写js,之后去摸鼠标摸一摸变量
* 语法
* let age: number = 18
* 变量:类型 = 值
* :类型 类型注解
* 作用
* 有类型约束 - 给变量什么类型,后面赋值操作都要是这个类型,否则会报错
* 代码提示会更清晰
*/
{
let age: number = 18;
age = 19
// age = '12'
// age = true
let str: string = 'abc'
str.includes('a')
}
原始类型-基本数据类型添加类型注解-先写js在鼠标摸一摸
知道:ts 有哪些类型,掌握:原始类型使用
TS 常用类型:
原始类型:
/**
* 原始类型
* number string boolean undefined null
* undefined和null 用鼠标摸上去类型注解是any 比较特殊,因为我们一般用于初始值
* js什么类型,类型注解就对应的写
*/
{
let num: number = 1
num
let str: string = '2'
str
let flag: boolean = true
flag
let u: undefined = undefined
u
let n: null = null
n
}
数组类型-类型中括号-Array尖括号里面写类型
掌握:数组类型的两种写法
/**
* 数组类型
* 2种写法
* 第一种: 类型加上[] 比如number[], string[]
* 第二种:Array<类型> 比如Array Array
* 建议使用第一种
*/
{
let arr: number[] = [1, 2, 3]
arr
let arr2: boolean[] = [true, false,]
console.log(arr2);
let arr3: Array<undefined> = [undefined, undefined, undefined]
arr3
}
推荐使用:
思考:
联合类型-语法是一个竖杠-或者的意思
掌握:通过联合类型将多个类型合并为一个类型
需求:数组中有 number 和 string 类型,这个数组的类型如何书写?
/**
* 联合类型
* 语法 类型A | 类型B | ... 是类型A或者是类型B或者是...
* 注意联合类型 是一根竖线
* js或的逻辑---两根竖线
*/
{
let arr: (string | number)[] = [1, '2', 3, '3']
arr
let arr2: (boolean | undefined | null)[] = [true, undefined, null]
arr2
// 字符串或者是数组类型的数组
let arr3: string | number[] = [1, 2, 3]
arr3
/**
* 联合类型综合小练习
* 写延时器 延时器的初始值null
* 赋值setTimeout
*/
let timer: null | number = null
timer = setTimeout(() => {}, 1000)
}
类型别名-用处方便复用-语法 type 类型别名 等号 类型
掌握:使用类型别名语法给类型取别字 利于复用
示例代码:
/**
* 类型别名
* 是什么
* 给类型起别名
* 怎么用
* 关键字type - 记忆方式 let a = 1,
* let 变量名 = 值
* type 类型名字 = 类型(单个类型或者联合等都可以)
* 类型名字规范用大驼峰 所有首字母都要大写
* 之后类型别名的使用 和 类型注解的方式一样
* :类型别名
* 为什么要用
* 为了复用,简化
*/
{
type MyArr = (string | number)[]
let arr: MyArr = [1, '2', 3]
arr
let arr2: MyArr = [2, '3', 4]
arr2
let arr3: MyArr = [3, '4', 5]
arr3
type MyNumber = number
let a: MyNumber = 1
a
}
类型别名:
使用场景:
23-函数类型-基本使用-分别指定参数和返回值都加类型注解-同时指定-类型别名
掌握:给函数指定类型
/**
* 函数类型-基本使用
* 本质上就是 给参数和返回值 加类型注解
* 两种写法
* 分别指定
* 语法(a: 类型A, b: 类型B, ....):返回值类型
* 同时指定
* 用类型别名 函数的语法去处理
* type xxx = (a: 类型A, b: 类型B, ...) => 返回值类型
* 注意 只能用在函数表达式
*
* 函数声明 和 函数表达式
* function xxx () {}
* const xxx = () => {}
*/
{
// 两个数字相加
function add1(a: number, b: number): number {
return a + b
}
add1(1, 2)
// add1('1', 2) error
// 两个字符串拼接
const add2 = (a: string, b: string): string => a + b
add2('1', '2')
// add2('1', 222)
// type 自定义类型别名 = 类型 大驼峰
type MyAddFn = (a: number, b: number) => number
const add3: MyAddFn = (a, b) => a + b
add3(1, 2)
add3(2, 3)
// function add4(a, b) {
// return a + b
// }
}
注意:
通过类似箭头函数形式的语法来为函数添加类型,只适用于 函数表达式
24-函数类型-void类型-默认函数的返回值-返回值是undefined类型必须返回undefined
掌握:void 函数返回值类型
/**
* 函数类型-void类型
* function fn(): void
* 函数里面啥都不返回,ts帮你推断 说返回值就是void 他指的就是没有返回任何东西
* 在ts最新5.1版本,返回值类型配置是undefined 可以不写return undefined
* 其他低版本 返回值undefined必须要写return undefined
* 低版本中返回值void和undefined是有区别的
* 建议:返回值类型是undefined 就返回return undefined
因为不管高低版本都可以
*/
{
function fn (): undefined {
console.log('say hi');
return undefined
}
console.log(fn());
}
注意:
25-函数类型-可选参数-问号语法-参数可以传也可以不传-补充–默认值表示可选
补充:slice 字符串和数组都有的方法—切片
重点细节:参数可以传,可以不传
掌握: 使用 ? 将参数标记为可选
练习,模拟 slice 函数,定义函数参数类型
/**
* 函数类型-可选参数 返回值是void
* 举例说明 slice方法。可以不传参数 也可以传1个 也可以传2个
* 可选参数语法 在参数名后面加一个? 可选(可传可不传)
*/
{
function mySlice (a?: number, b?: number) {
console.log(a, b);
}
mySlice(1, 2)
mySlice(1)
mySlice()
// 注意事项: 可选的必须要在必选的后面 必选的必须在前面
// 下面的函数参数例子是错误示范
// function mySlice2 (a?: number, b: number) {
// console.log(a, b);
// }
}
注意:
写了默认值就代表end是可选参数,TS帮你识别出来了
问号写在冒号前面
26-对象类型-基本使用-单行-一定要加分号或者逗号-多行可以省略-语法 属性 冒号 类型
27-对象类型-扩展用法-箭头函数-属性可选-类型别名写法
掌握:对象类型语法
/**
* 对象类型-基本使用
*/
{
// 空对象 :{}
const obj1: {} = {}
obj1
// 对象里有属性 换行的写法 写分号 写逗号或者不写
const obj2: {
name: string,
age: number
} = {
name: 'zs',
age: 18
}
obj2
// 对象里有属性 不换行写在一行里,需要用分号或者逗号隔开
const obj3: {a: number ;b: boolean} = {
a: 1,
b: true
}
obj3
// type 类型别名 = 类型
type MyObj = {
c: null
d: undefined
sayHi(): void
code(skill: string): string
}
// 对象中方法的语法 方法名(参数1: 参数类型...): 返回值类型
const obj4: MyObj = {
c: null,
d: undefined,
sayHi() {
console.log('1');
},
code (skill: string) {
console.log(skill);
return skill
}
}
obj4
}
小结:
掌握:对象类型中,函数使用箭头函数类型,属性设置可选,使用类型别名。
/**
* 对象类型-扩展用法
* 函数使用箭头函数类型,
* 写法不同,但意思是等价的
* 属性(参数A: 类型A...): 返回值类型
* 属性: (参数A: 类型A...) => 返回值类型
* 属性设置可选,语法 属性名后面加?
* 使用类型别名
*/
{
type MyObj = {
c: number;
d: string;
// a(): void;
a: () => void;
// b(foo: boolean): boolean;
b: (foo: boolean) => boolean;
e?: null;
f?: (a: string, b: string) => string
}
const obj1: MyObj = {
c: 1,
d: '2',
a() {
},
b(foo: boolean) {
return foo
},
// e: null
f(a, b) {
return a + b
}
}
obj1
type Config = {
url: string;
method?: string;
};
// axios参数 对象类型 类型注解
/**
* axios({
* url: '/aaaa',
* method: 'xxx' //不写有默认值get
* })
*/
function myAxios (config: Config) {
console.log(config);
}
myAxios({
url: '',
})
}
// 例如:axios({url,method}) 如果是 get 请求 method 可以省略
const axios = (config: { url: string; method?: string }) => {};
// {} 会降低代码可阅读性,建议对象使用类型别名
// const axios = (config: { url: string; method?: string }) => {};
type Config = {
url: string;
method?: string;
};
const axios = (config: Config) => {};
小结:
练习
创建一个学生对象,该对象中具有以下属性和方法:
/**
* 对象类型-综合练习
*
*/
{
type Student = {
name: string;
gender: string;
score: number;
height?: number
study(): void // 学习没有参数 没有返回 无尽的学习
// 游戏名 string 返回数字 箭头函数类型练习
play?: (game: string) => number
}
const student: Student = {
name: 'zs',
gender: 'M',
score: 100,
study() {
},
play(game) {
console.log(game);
return 1
}
}
// 补充 因为play方法是可选的(可以写可以不写)
// 注意 可选的方法,即使你写了 ts也会给你提示 可能未定义
// 如何解决 3种方案 第1种 可选链 第二种 加判断(类型守卫)第三种非空断言
// student.play?.('王者荣耀')
// 类型守卫
// if (student.play) {
// student.play('游戏A')
// }
student.play && student.play('111')
student.play!('11')
}
如何解决 3种方案 第1种 可选链 第二种 加判断(类型守卫)第三种非空断言!!!!!
26-interface的基本使用-配置对象类型 大驼峰
掌握:使用 interface 声明对象类型
/**
* interface 基本使用
* es6类的语法 class Person {}
* interface语法类比 描述对象 语法没有等号,类型别名语法才有等号
* 接口,不要把所有的属性写在一行 规范一行一个属性
* interface 接口名(大驼峰) {
* 属性名: 类型
* 属性方法名: 类型
* }
* 使用方式,就是和type的类型注解一样
* :接口名
*/
{
// 写个接口Student name age? study方法 play方法?
interface Student {
name: string;
age?: number;
study(): void;
play?: (game: string) => boolean;
}
let student: Student = {
name: 'ls',
study () {
},
}
student
}
小结:
掌握:使用 extends 实现接口继承,达到类型复用
思考:
/**
* interface 继承
* 继承关键字 extends
* interface A extends B(接口)
* A里面有B的属性和方法
*/
{
// interface 接口名 {
// 属性:类型
//}
interface Point2D {
x: number,
y: number
}
const point2d: Point2D = {
x: 1,
y: 2
}
point2d
// Point3D 继承了 Point2D的属性和方法
// Point3D 他有自己的z属性 同时还有了Point2D里面的属性方法
interface Point3D extends Point2D {
z: number
}
const point3d: Point3D = {
x: 1,
y: 2,
z: 3,
}
point3d
}
小结:
掌握:使用 交叉类型 实现 接口的继承效果
语法 类型A & 类型B (一个与的符号)
/**
* type交叉类型
* interface extends 接口的继承 为了复用
* type 复用的语法不是extends 他是用交叉类型 复用 相当于接口的继承
* 语法 类型A & 类型B (一个与的符号)
*/
{
type Point2D = {
x: number,
y: number
}
const point2d: Point2D = {
x: 1,
y: 2
}
point2d
// 类型别名 A & 类型别名 B
// 返回的类型就是A和B的属性都有 - 在这个例子中 xyz三个属性都有了
type Point3D = Point2D & {
z: number
}
const point3d: Point3D = {
x: 1,
y: 2,
z: 3,
}
point3d
}
小结:
29-interface和type的区别-2个相同-3个不同
了解:interface 和 type 的相同点和区别
不同的点:
type Person = {
name: string;
};
// 标识符“Person”重复 Error
type Person = {
age: number;
};
interface Person {
name: string;
}
interface Person {
age: number;
}
// 类型会合并,注意:属性类型和方法类型不能重复定义
const p: Person = {
name: 'jack',
age: 18,
};
小结:
/**
* interface vs type
* interface type
* 支持对象类型(相同) 支持 支持
* 是否可以复用(相同) 可以 可以
* 支持其他类型(不同) 不支持 支持
* 复用的语法(不同) extends &(交叉类型)
* 重复声明(不同) 可以,会合并属性 不可以,会报错
*/
{
// type MyNumberString = number | string
// type MyNumber = number
// 重复声明的问题 有的公司规范喜欢第一个字母写个大写I - 意思就是interface
// 接口重复声明 属性会合并
interface IMyObj {
name: string
}
interface IMyObj {
// name: boolean
age: number
}
const myObjInterface: IMyObj = {
name: 'zs',
age: 18
}
myObjInterface
// 类型别名重复声明 - 重复声明直接报错,以下代码会报错
// type TMyObj = {
// name: string
// }
// type TMyObj = {
// age: number
// }
}
:::info
一、类型推断
类型推断-ts自己会帮你推断出类型-鼠标摸一摸
发生类型推断的几个场景:
知道:TS 的的类型推断机制作用
在 TS 中存在类型推断机制,在没有指定类型的情况下,TS 也会给变量提供类型。
建议:
/**
* 类型推断(你不用写类型,ts自己能帮你推断出来什么类型就是昨天鼠标摸一摸的操作)
* 1. 变量的初始化
* 2. 函数的返回值
*/
{
let age = 18
age
let obj = {a: 1, b: '2'}
obj
function add (a: number, b: number) {
return a + b
}
add(1,2)
}
二、字面量类型
- 字面量类型介绍-写死个字面量类型,数据就不能改
例如:
const str2 = Hello TS!
str2 是 const 声明的,值只能是 Hello TS,所以类型只能是 Hello TS
- 字面量类型应用
类型更加精确-举例性别和方向-配合联合类型
配合联合类型来使用,表示:一组明确的可选的值
优势: 相比于 string 类型,使用字面量类型更加精确、严谨
知道:什么是字面量类型
// : 'jack' 是字面量类型
let name: 'jack' = 'jack';
// : 18 是字面量类型
let age: 18 = 18;
// 报错:不能将类型“19”分配给类型“18”
age = 19;
04-字面量类型应用-类型更加精确-举例性别和方向-配合联合类型 |
知道:字面量类型的应用场景
例如:性别只能是 男 和 女,不会出现其他值。
/**
* 字面量类型应用
* 一般用于联合类型配合字面量类型,能让我们类型更加的精准
*/
{
// 比如 性别 如果用字符串类型 是不是范围太广不太符合逻辑 只有可能是男或者女
// let gender: string = '13241234'
let gender: '男' | '女' = '男'
gender
type MyDir = 'up' | 'down' | 'left' | 'right';
// 方法,方法传入的参数是方向 up down left right
function changeDirection (dir: MyDir) {
console.log(dir);
}
// changeDirection('down')
changeDirection('down')
}
小结:
:::success
三、any类型
任意类型逃避ts检查-不建议用any-用了相当于写js
作用: 逃避 TS 的类型检查
:::
/**
* any类型 - 逃避ts检查(随便你怎么写不会报错相当于写js) any英文的意思任意类型
* anyscript不建议 他相当于你就在写js
* 1. 显式 - 自己写类型注解 :any 在浏览器环境会报错!!
* 2. 隐式
* 初始化没有给值的时候,类型推断就是any类型
* 函数的参数没有给定类型
*/
{
let obj: any = { a: 1, b: 2 }
obj = {
a: 3,
b: '5'
}
obj = 3
obj = true;
obj = null
obj()
}
:::tips
补充:函数参数给初始值-不用加问号-本质上也是可选
:::
:::warning
四、类型断言
初识类型断言-as-比ts更确定一个类型-要确定了才能断言
例子–类型守卫–img元素赋值src练习
:::
有时候你会比 TS 更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型。 比如,
// aLink 的类型 HTMLElement,该类型只包含所有标签公共的属性或方法
// 这个类型太宽泛,没包含 a 元素特有的属性或方法,如 href
const aLink = document.getElementById('link')
const aLink = document.getElementById('link') as HTMLAnchorElement
/**
* 类型断言
* 在我们下面这个例子中,因为我们能确定页面中是有这样一个元素,不可能为null
* 断言必须是你确定有才能去断言 一定是你确定没问题的
*
* 元素的类型确定类型有2种方案
* 第一种 打印去控制台看
* 第二种(推荐) 你创建个你想要的元素,摸一摸然后在把代码删除
*/
{
// Element | null
// ts他并不清楚你是个a标签元素,他就只知道你获取的是个元素
const link = document.querySelector('#link') as HTMLAnchorElement
console.dir(link);
link.href
}
- 软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
- 在TypeScript中,泛型是一种创建可复用代码组件的工具。这种组件不只能被一种类型使用,而是能被多种类型复用。类似于参数的作用,泛型是一种用以增强类型(types)、接口(interfaces)、函数类型等能力的非常可靠的手段。
09-初识-泛型别名-类型传参-别名的后面写尖括号
掌握:泛型别名基本使用,实现类型复用
语法 名字后面加尖括号 传入你想要传入的类型,作为参数
a:A 类型
b:B类型
/**
* 泛型
* 类型传参
* 类比 函数 函数是不是可以传参数
* 类型 也可以传参数
* 泛型别名
* 语法 名字后面加尖括号 传入你想要传入的类型,作为参数
* type xxx<类型参数A = 可以设置默认值, 类型参数B ...> = {
* 字段A: 类型参数A
* 。。。。
* }
*/
{
type MyObj<A = number, B = string> = {
a: A
b: B
}
// type MyObj1 = {
// a: number;
// b: string;
// }
// type MyObj2 = {
// a: boolean;
// b: null;
// }
const obj1: MyObj<{}, Date> = {
a: {},
b: new Date()
}
obj1
const obj2: MyObj<boolean, undefined> = {
a: true,
b: undefined
}
obj2
}
不建议使用交叉类型-复用性没有泛型好
/**
* 泛型别名 练习 模拟发送请求后,拿到后端接口的数据
* 语法 类型别名后加尖括号 传入你想要传入的类型,作为参数
* type xxx<类型参数A = 可以设置默认值, 类型参数B ...> = {
* 字段A: 类型参数A
* 。。。。
* }
*
* {
* code: 200,
* msg: 'xxx',
* data: 数据
* }
*
* 在我们没学泛型前 会用交叉类型做复用,但在这样的例子中不太合适
* type UserData = CodeAndMsg & { data: UserList }
* type ArticelData = CodeAndMsg & { data: ArticleDetail }
* 用交叉类型还是有很多重复的代码
*/
{
// 交叉类型演示 开始
type CodeAndMsg = {
code: number
msg: string;
}
type User = {
name: string
age: number
}
// number[] string[] User[]
type UserList = User[]
type UserData = CodeAndMsg & { data: UserList }
const userData: UserData = {
code: 200,
msg: '获取成功',
data: [
{
name: 'zs',
age: 18
}
]
}
userData
type ArticleDetail = {
id: string
content: string
}
/**
* {
* code: 200,
* msg: '获取详情成功'
* data: {
* id: '',
* content: ''
* }
* }
*/
type ArticelData = CodeAndMsg & { data: ArticleDetail }
const ArticleData: ArticelData = {
code: 200,
msg: '获取成功',
data: {
id: '1',
content: '文章'
}
}
ArticleData
// 交叉类型演示结束
}
小结:
/**
* 泛型别名 练习 模拟发送请求后,拿到后端接口的数据
* 语法 类型别名后加尖括号 传入你想要传入的类型,作为参数
* type xxx<类型参数A = 可以设置默认值, 类型参数B ...> = {
* 字段A: 类型参数A
* 。。。。
* }
*
* {
* code: 200,
* msg: 'xxx',
* data: 数据
* }
*
* 在我们没学泛型前 会用交叉类型做复用,但在这样的例子中不太合适
* type UserData = CodeAndMsg & { data: UserList }
* type ArticelData = CodeAndMsg & { data: ArticleDetail }
* 用交叉类型还是有很多重复的代码,需要用泛型优化
* type UserData = ApiData
* type ArticelData = ApiData
*/
{
// 一般只有一个类型参数,规范上起名会写大写的T 意思就是Type
type ApiData<T> = {
code: number
msg: string;
data: T
}
type User = {
name: string
age: number
}
// number[] string[] User[]
type UserList = User[]
type UserData = ApiData<UserList>
const userData: UserData = {
code: 200,
msg: '获取成功',
data: [
{
name: 'zs',
age: 18
}
]
}
userData
type ArticleDetail = {
id: string
content: string
}
/**
* {
* code: 200,
* msg: '获取详情成功'
* data: {
* id: '',
* content: ''
* }
* }
*/
type ArticelData = ApiData<ArticleDetail>
const ArticleData: ArticelData = {
code: 200,
msg: '获取成功',
data: {
id: '1',
content: '文章'
}
}
ArticleData
}
掌握:泛型接口基本使用,实现类型复用,了解内置泛型接口
// 对象,获取单个ID函数,获取所有ID函数,
//ID的类型肯定是一致的,但是可能是数字可能是字符串
interface IdFn<T> {
id: () => T;
ids: () => T[];
}
const idObj: IdFn<number> = {
id() { return 1 },
ids() { return [1, 2] },
};
内置的泛型接口:
const arr = [1, 2, 3];
// TS有自动类型推断,其实可以看做:const arr: Array = [1, 2, 3]
arr.push(4);
arr.forEach((item) => console.log(item));
掌握:泛型函数基本使用,保证函数内类型复用,且保证类型安全
// 函数的参数是什么类型,返回值就是什么类型
function getId<T>(id: T): T {
return id
}
let id1 = getId<number>(1)
let id2 = getId('2')
// TS会进行类型推断,参数的类型作为泛型的类型 getId('2')
小结
// 我需要的类型 { name: string, age?: number } 但是推断出来是 { name: string}
let id2 = getId({name:'jack'})
总的代码如下:
/**
* 泛型
* 类型传参
* 类比 函数 函数是不是可以传参数
* 类型 也可以传参数
* 泛型别名
* 语法 类型别名后加尖括号 传入你想要传入的类型,作为参数
* type xxx<类型参数A = 可以设置默认值, 类型参数B ...> = {
* 字段A: 类型参数A
* 。。。。
* }
*/
{
type MyObj = {
a: A
b: B
}
// type MyObj1 = {
// a: number;
// b: string;
// }
// type MyObj2 = {
// a: boolean;
// b: null;
// }
const obj1: MyObj<{}, Date> = {
a: {},
b: new Date()
}
obj1
const obj2: MyObj = {
a: true,
b: undefined
}
obj2
}
交叉类型演示后端接口数据
/**
* 泛型别名 练习 模拟发送请求后,拿到后端接口的数据
* 语法 类型别名后加尖括号 传入你想要传入的类型,作为参数
* type xxx<类型参数A = 可以设置默认值, 类型参数B ...> = {
* 字段A: 类型参数A
* 。。。。
* }
*
* {
* code: 200,
* msg: 'xxx',
* data: 数据
* }
*
* 在我们没学泛型前 会用交叉类型做复用,但在这样的例子中不太合适
* type UserData = CodeAndMsg & { data: UserList }
* type ArticelData = CodeAndMsg & { data: ArticleDetail }
* 用交叉类型还是有很多重复的代码
*/
{
// 交叉类型演示 开始
type CodeAndMsg = {
code: number
msg: string;
}
type User = {
name: string
age: number
}
// number[] string[] User[]
type UserList = User[]
type UserData = CodeAndMsg & { data: UserList }
const userData: UserData = {
code: 200,
msg: '获取成功',
data: [
{
name: 'zs',
age: 18
}
]
}
userData
type ArticleDetail = {
id: string
content: string
}
/**
* {
* code: 200,
* msg: '获取详情成功'
* data: {
* id: '',
* content: ''
* }
* }
*/
type ArticelData = CodeAndMsg & { data: ArticleDetail }
const ArticleData: ArticelData = {
code: 200,
msg: '获取成功',
data: {
id: '1',
content: '文章'
}
}
ArticleData
// 交叉类型演示结束
}
泛型别名演示后端数据
/**
* 泛型别名 练习 模拟发送请求后,拿到后端接口的数据
* 语法 类型别名后加尖括号 传入你想要传入的类型,作为参数
* type xxx<类型参数A = 可以设置默认值, 类型参数B ...> = {
* 字段A: 类型参数A
* 。。。。
* }
*
* {
* code: 200,
* msg: 'xxx',
* data: 数据
* }
*
* 在我们没学泛型前 会用交叉类型做复用,但在这样的例子中不太合适
* type UserData = CodeAndMsg & { data: UserList }
* type AraticelData = CodeAndMsg & { data: ArticleDetail }
* 用交叉类型还是有很多重复的代码,需要用泛型优化
* type UserData = ApiData
* type ArticelData = ApiData
*/
{
// 一般只有一个类型参数,规范上起名会写大写的T 意思就是Type
type ApiData = {
code: number
msg: string;
data: T
}
type User = {
name: string
age: number
}
// number[] string[] User[]
type UserList = User[]
type UserData = ApiData
const userData: UserData = {
code: 200,
msg: '获取成功',
data: [
{
name: 'zs',
age: 18
}
]
}
userData
type ArticleDetail = {
id: string
content: string
}
/**
* {
* code: 200,
* msg: '获取详情成功'
* data: {
* id: '',
* content: ''
* }
* }
*/
type ArticelData = ApiData
const ArticleData: ArticelData = {
code: 200,
msg: '获取成功',
data: {
id: '1',
content: '文章'
}
}
ArticleData
}
泛型接口-内置的泛型接口-Array
/**
* 泛型接口 - 了解内置的泛型接口
* 我们之前已经学过了?interface Array
* 面试官问你 什么是泛型 回答:比如我先介绍下内置的泛型接口 interface Array
* 类型传参
*/
{
// number[]
let arr: Array = ["1, 2, 3"]
arr.forEach(item => {
item
})
// push方法是干什么用的 在数组后面添加 返回值是什么 添加后的数组长度
arr.push(1,2,3)
}
手写泛型接口
泛型函数:语法 在执行的括号前面加尖括号 - 箭头函数和普通函数都适用
/**
* 泛型接口 - 自己写泛型接口
* 接口名后面加尖括号
* 语法 interface xxx
*
* 公司量产机器人 分为id是数字型的机器人 id是字符串型的机器人 。。。。
* sayHi返回值 把id返回
*/
{
interface Robot {
id: T
sayHiA(id: T): T
sayHiB?: (id: T) => T
}
const numberRobot: Robot = {
id: 1,
sayHiA(id) {
return id
}
}
numberRobot
const stringRobot: Robot ={
id: '1',
sayHiA(id) {
return id
}
}
stringRobot
}
/**
* 泛型函数
* 语法 在执行的括号前面加尖括号 - 箭头函数和普通函数都适用
*
*/
{
function fn(a: T, b: T) {
console.log(a, b);
}
function fn1(a: number, b: number) {
console.log(a, b);
}
fn1(1, 2)
function fn2(a: string, b: string) {
console.log(a, b);
}
fn2('1', '2')
// 隐式 通过类型推导
// 如果类型推导,推导出来的就是你想要的类型,你尖括号泛型可以省略
fn('1', '2')
// 显式 自己指定类型
fn(true, false)
const foo = (a: A, b: B) => {
console.log(a, b);
}
foo(1, undefined)
}
15-补充-泛型函数-不能随意想当然乱写 泛型T加T会报错
比如一个数组+另外一个数组,一个正则加一个正则,返回的并不是你想要的结果并且没有任何的意义。
/**
* 泛型函数 - 补充 不能想当然的乱写泛型
* 比如 T+T会报错
* 语法 在执行的括号前面加尖括号 - 箭头函数和普通函数都适用
*/
{
// function add(a: T, b: T) {
// console.log(a, b);
// }
function add1(a: number, b: number) {
return a + b
}
add1(1, 2)
function add2(a: string, b: string) {
return a + b
}
add2('a', 'b')
}
code msg 固定 data传一个T---- 动态传入的类型传参
数组,Array<>–专业术语–泛型接口 数组的方法push forEach方法 拿到T类型
泛型接口,在接口名后面加尖括号
泛型别名:别名后面加尖括号—概念 类型传参
代码如下:刷新页面后,展示访问历史记录,记录包含:次数和时间。
/**
* 综合练习
* 刷新页面后,展示访问历史记录,记录包含:次数和时间。
*
* 1. 时间戳 => 时分秒
* date对象上才有对应获取时分秒的方法
* 时 getHours
* 分 getMinutes
* 秒 getSeconds
*
* formtTime() 可以不传参数 不传参数直接获取当前时间 转换时分秒
* formatTime('2023-07-07 01:00:00') 可以传时间格式的字符串 转换时分秒
* formatTime(new Date()) 可以传date类型数据,转换时分秒
*
* 时分秒如果只有一位的 需要前面补0 01:00:00
* 字符串方法 padStart(最大长度, ’补的字符串‘)
* '1'.padStart(2, '0') 往前补, 一共补2位, 用0补
* 2. 定义次数和时间 数据格式
* 是个对象数组
* 对象里有2个属性 次数 - number 时间 - string
* 3. 数据持久化 localStorage
* 定义个变量 key
* getItem 取数据
* setItem 存数据
* 先实现取数据 为什么?
* 1. 添加数据要先获取老的数据(数组)
* 2. 然后在通过push方法添加最新的数据
* 3.存到本地
* 4. 渲染
* 1. 获取本地的数据
* 2. 数据渲染到页面
*/
{
const formatTime = (date?: string | Date) => {
// 可能没有 当前时间
if (!date) date = new Date()
// 字符串 => 时间date对象
if (typeof date === 'string') date = new Date(date)
// 代码走到这里的时候 date必然是个Date类型
// date对象才能调用以下3个方法转成时分秒
const h = date.getHours().toString().padStart(2, '0')
const m = date.getMinutes().toString().padStart(2, '0')
const s = date.getSeconds().toString().padStart(2, '0')
// xx:xx:xx
return `${h}:${m}:${s}`
}
// console.log(formatTime());
// console.log(formatTime('2023-07-07 01:00:00'));
// console.log(formatTime(new Date()));
// 对象数组里的每一项 就是一个Time对象
type Time = {
count: number
time: string
}
// 页面展示的列表数据类型
type List = Array<Time>
const key = 'vue3-ts-108-time-record'
// 取数据 - 数组 即使没有数据也希望返回空数组
function getData () {
const str = localStorage.getItem(key)
// 因为我们确定返回的数据就是个对象Time数组 所以可以用类型断言,返回值就是List数据
return JSON.parse(str || '[]') as List
}
// 存数据
function setData () {
// 1. 获取老的数据 要么是有数据的数组 要么是空数组
const list = getData()
// 2. push新的数据 Time数据 次数 时间 添加好新的数据
list.push({
// 假设是空数组 添加数据 次数1, length原本是0 length加1即可
// 假设里面是有数据的数组 举例 2条数据 新加的数据 次数3 也是length+1
// count的逻辑 就是length + 1
count: list.length + 1,
time: formatTime()
})
// 3. 存到本地
localStorage.setItem(key, JSON.stringify(list))
}
// setData()
function render () {
// 每次一刷新 调用render 要记录新的数据
setData()
const list = getData()
// 获取div#app的元素
const el = document.querySelector('#app') as HTMLDivElement
// console.log(list);
// 元素里渲染数据 - innerHTML
// 对象数组转成字符串
el.innerHTML = list.map(item => `次数:${item.count} 时间: ${item.time}`).join('
')
}
render()
}
https://blog.csdn.net/tanxin2721/article/details/112306417
在 JavaScript 中,基本数据类型(如字符串、数字、布尔值等)是不可变的,也就是说它们的值是不可被修改的。当你给基本数据类型设置属性时,实际上是在创建一个临时包装对象,该对象具有该属性,但是对象在跳出作用域后会立即被销毁,而且不会对原始的基本数据类型产生影响。
让我们来看一个示例:
let num = 10;
num.foo = 'bar';
console.log(num.foo); // undefined
在这个例子中,我们给一个数值类型的变量 num
设置了一个属性 foo
。但是,当我们尝试访问 num.foo
时,返回的是 undefined
。这是因为在设置属性时,JavaScript 会暂时创建一个临时对象,该对象具有该属性,但是在访问时已经被销毁,因此我们无法访问到它。
相反,如果你想给一个对象类型的变量设置属性,那么这个属性将会被正确地添加到该对象,并且在后续的访问中仍然可见。
let obj = {};
obj.foo = 'bar';
console.log(obj.foo); // 'bar'
在这个示例中,我们给一个空对象 obj
设置了一个属性 foo
,并且我们可以成功地访问到这个属性。
总结来说,基本数据类型的属性设置不会产生预期效果,因为这些属性只存在于临时对象中,并且会在对象离开作用域后被销毁。如果你需要给一个变量设置属性,那么变量应该是一个对象类型。
应用就是说和vue结合在一起
typescript 配合 Vue3 composition-api 使用
https://staging-cn.vuejs.org/guide/typescript/composition-api.html
前提:script 加上 lang=“ts” 才能写ts代码
const props = defineProps({
money: {
type: Number,
required: true
},
car: {
type: String,
required: false,
default: '宝马车'
}
})
console.log(props.money) // number
console.log(props.car) // string | undefined
看提示写泛型!!!之前是大写Number vue2中也是大写,因为是JS基本数据类型
泛型是小写
大写Number 包装类构造函数
原理:赋值构造函数转成包装类,获取属性的时候已经销毁了
JS基本数据类型可以点操作,用完就会销毁
const props = defineProps<{
money: number
car?: string
}>()
泛型里面的类型是小写!!!
可选参数用问号,先看文档再去看视频
https://cn.vuejs.org/guide/typescript/composition-api.html#typing-component-props
当使用基于类型的声明时,我们失去了为 props 声明默认值的能力。这可以通过 withDefaults 编译器宏解决:
默认数组类型:返回工厂函数 和vue2中data要用一个函数,为了组件复用,新的对象,新的引用。
两个参数,一个是defineProps 函数第二个是对象–默认值
const props = withDefaults(defineProps<{
money: number;
car?: string;
}>(),{
car: '宝马车'
})
解构一般会丢失响应式
30-defineEmits结合ts写法-配置泛型-函数返回值void-参数事件名-子传父的数据
const emit = defineEmits(['changeMoney', 'changeCar'])
const emit = defineEmits<{
(e: 'changeMoney', money: number): void
(e: 'changeCar', car: string): void
}>()
了解:扩展TS语法 调用签名
ref() 会隐式的 依据数据 推导类型
// const money = ref(10)
const money = ref(10)
https://cn.vuejs.org/guide/typescript/composition-api.html#typing-ref
type Todo = {
id: number
name: string
done: boolean
}
const list = ref<Todo[]>([])
setTimeout(() => {
list.value = [
{ id: 1, name: '吃饭', done: false },
{ id: 2, name: '睡觉', done: true }
]
}, 1000)
复杂数据一般是后台返回数据,默认值是空,无法进行类型推导。
课上代码:
<script setup lang="ts">
import { Ref, onMounted, ref } from 'vue';
// ref + ts
// 简单数据建议使用类型推导 什么都不写 摸上去
const count: Ref<number> = ref(0)
const count=ref<number>() //number|undefined
count
// 复杂数据建议使用泛型配置类型
type User = {
name: string
age: number
}
//泛型可以嵌套泛型
const userList = ref<User[]>([])
onMounted(() => {
setTimeout(() => {
userList.value = [
{
name: 'zs',
age: 18
}
]
}, 2000)
})
</script>
文档搜typescript—组合式API
reactive() 也会隐式的依据数据推导类型
// 推导得到的类型:{ title: string }
const book = reactive({ title: 'Vue3 在线医疗' })
// 我们想要的类型:{ title: string, year?: number }
type Book = {
title: string
year?: number
}
const book: Book = reactive({ title: 'Vue3 在线医疗' })
book.year = 2022
https://cn.vuejs.org/guide/typescript/composition-api.html#typing-reactive
import { ref, computed } from 'vue'
const count = ref(100);
const doubleCount = computed(() => count.value * 2);
const doubleMoney = computed<string>(
() => (count.value * 2).toFixed(2) //string
);
https://cn.vuejs.org/guide/typescript/composition-api.html#typing-computed
18-综合练习-formatTime基本实现-date对象上方法
19-综合练习-formatTime扩展–支持传字符串传date对象-可选参数-类型守卫
20-综合练习-时间前面补0操作-padStart
21-综合练习-定义数据格式类型-分析对象数组-对象里有2个属性
22-综合练习-本地存取时间数据-localStorage
23-综合练习-拿到本地数据渲染页面-数组转成字符串-map-join-br
24-vue结合ts说明-搭架子-script标签上加lang值是ts
25-defineProps结合ts写法-配置泛型-配置对象-属性和类型
26-补充-大写Number-上
27-补充-大写Number-下
28-withDefaults设置父传子的默认值-默认值配置第二个参数,函数工厂函数
29-废弃的响应式语法-默认值用withDefaults–不要解构
30-defineEmits结合ts写法-配置泛型-函数返回值void**-参数事件名-子传父的数据
31-ref结合ts写法-简单建议类型推导**-复杂建议配置泛型
32-作业-自学reactive和computed.itcast
33-day03下午总结
作业
01-reactive结合ts-不推荐用泛型-直接用类型注解
02-computed结合ts-泛型函数-括号前面加尖括号
<script setup lang="ts">
// 提示:参数“event”隐式具有“any”类型。
const handleChange = (event) => {
console.log(event.target.value)
}
</script>
<template>
<input type="text" @change="handleChange" />
</template>
// `event` 隐式地标注为 `any` 类型,如何指定:event 类型?
// 1. @change="handleChange($event)"" 查看$event类型
// 2. 鼠标摸一下事件 @change 查看类型
const handleChange = (event: Event) => {
// `event.target` 是 `EventTarget | null` 类型,如何指定具体类型?
// document.querySelector('input') 查看返回值类型
console.log((event.target as HTMLInputElement).value)
}
总结代码
<script setup lang="ts">
/**
* 获取表单框里的内容 e来获取 e.target.value
* 类型注解如何去配置
* 传参$event 用鼠标摸一摸
*/
const handleInput = (e: Event) => {
// 类型断言
console.log((e.target as HTMLInputElement).value);
}
</script>
<style scoped>
模板 ref 需要通过一个显式指定的泛型参数,建议默认值 null
总结代码
<input type="text" ref="inputRef">
<script setup lang="ts">
import { onMounted, ref } from 'vue';
/**
* 页面中有一个input元素
* input元素自动聚焦功能
*
* 1. 先获取元素(ref获取元素)ts需要配置联合类型,因为初始值是null
挂载以后才能拿到input元素
* null | HTMLInputElement
* 如果我们在这个元素上使用了v-if 且值是false 把这个元素销毁了,
他会自动把值改成null
* 2. 挂载后 调用focus的方法
*/
const inputRef = ref<null | HTMLInputElement>(null)
onMounted(() => {
// 可选链
// inputRef.value?.focus()
// 类型守卫
if (inputRef.value) {
inputRef.value.focus()
}
})
</script>
处理类型可能是 null 或 undefined 的值,下面的属性或函数的访问赋值:
App组件
if (input.value) {
console.log(input.value.value)
input.value.value = '123'
}
// 一定要确定不为空!!!
console.log(input.value!.value)
input.value!.value = '123'
总结代码
<script setup lang="ts">
import { onMounted, ref } from 'vue';
/**
* input框里面的内容赋值
* input框里没有内容 赋值sf input元素.value = 'sf'
* 该逻辑也在元素挂载后再处理
*
* 非空断言的语法,就是当你确定这个东西是有的时候,在这个东西后面加上!
* 举例在这个例子中 inputRef.value.value = 'sf'
* ts觉得是inputRef.value可能是空,但是在这个例子中这个inputRef.value不可能为空
* 所以我们可以使用非空断言,语法就是在ts觉得为空的这个东西后面加!
* inputRef.value!这个意思断言他非空
* inputRef.value!.value = 'sf'
*/
// const show = ref(true)
const inputRef = ref<null | HTMLInputElement>(null)
onMounted(() => {
// 可选链不能用 可选链只能用于获取值,不能用于赋值
// inputRef.value?.value = 'sf'
// if (inputRef.value) {
// inputRef.value.value = 'sf'
// }
inputRef.value!.value = 'sf'
})
</script>
typescript 类型声明文件相关知识
.d.ts 类型声明文件
知道:什么是内置的类型什么文件
发现,在使用数组时,数组所有方法都会有相应的代码提示以及类型信息:
TypeScript 给 JS 运行时可用的所有标准化内置 API 都提供了声明文件,这个声明文件就是 内置类型声明文件
掌握:给第三方库添加对应的类型声明文件
首先,常用的第三方库都有相应的类型声明文件,只是使用的方式不同而已。
情况1:库本身自带类型声明文件
情况2:由 DefinitelyTyped 提供
掌握:使用类型声明文件 提供 需要共享的 TS类型
记得写导入时侯要写type ,为了符合规范!!!!!
imd export
了解:使用类型声明文件 给JS文件 添加类型
在导入 .js 文件时,TS 会自动加载与 .js 同名的 .d.ts 文件,以提供类型声明。
declare 关键字:
export 导出方法
02事件与TS-参数传$event-鼠标摸一摸itcast
03-TemplateRef与TS-获取元素-null和元素类型做联合类型
04-非空断言-感叹号语法-可选链不能赋值-一定要确定的东西才能断言.itcast
05-类型声明文件基本介绍-ts-又可以写类型又可以写执行代码-dts-只能写类型itcast
06-内置类型声明文件-数组和dom等方法有内置的dts文件.itcast
07-第三方库类型声明文件-第三方库自带的类型声明文件-举例axios.itcast
08-第三方库类型声明文件-第三方库没有自带类型声明文件-举例jq-装@types xxxitcast
09-共享类型-重要-自己写一个dts文件复用-写好要导出-使用时导入.itcast
10-给is文件提供类型-了解-declare-最后记得导出方法.itcast
11-黑马头条-综合案例介绍.itcast
12-黑马头条-搭建结构-cv样式和结构.itcast
13-黑马头条-axios配置响应数据类型-上-请求别名函数!!!配置itcast14-黑马头条-axios配置响应数据类型-下-泛型第一个参数是返回数据类型.itcast
第一步:搭建框架—基础结构–别忘记导入样式
第二步:掌握axios配合泛型,设置响应数据类型
小结:
配置泛型参数,第一个参数就是返回值的类型,第一个参数就是返回值的类型,第一个参数就是返回值的类型,第一个参数就是返回值的类型,第一个参数就是返回值的类型
shift+ctrl+alt+v 导出导入
Convert JSON object to typescript interfaces
记录高亮的id activeId===item.id 当前的Id 就激活active类
文章列表也用到了activeId
状态提升–数据声明到父组件–App.vue
https://gitee.com/qianfengg/vue3-ts-108/commit/f14af12cfc0b9e22ff5334a309968b090ff4196c
defineProps《{}》()
监听对象中的一个属性—一个函数返回一个值
https://gitee.com/qianfengg/vue3-ts-108/commit/4a957fffe38ae23c37a5a7de420406fe0808cc31
表示文章的类型,控制台粘贴返回的数据,用插件的快捷键!!导出导入
声明一个响应式数据,赋值,渲染
复杂数据–配置泛型----摸一下返回的结果是Ariticle[] ,c一下
类型声明文件写的详细,点出来的内容就更多
接口文档生成TS类型声明文件-后端接口文档给力的话可以生成代码.itcast
https://zhoushugang.gitee.io/patient-h5-note/pinia/
小结:
掌握:实用Pinia使用,管理计数器的状态
https://pinia.vuejs.org/zh/getting-started.html
注意:getters是computed函数!!!其他同步异步是普通函数
注意定义仓库后要返回属性和方法
const xxx=computed(()=>{})
https://pinia.vuejs.org/zh/core-concepts/
{{ num }}
{{ threeAdd }}
click+1
click+2
具体看文档笔记:
https://zhoushugang.gitee.io/patient-h5-note/pinia/#case
地址:https://snippet-generator.app/
老师的代码
{
"vue3base": {
"prefix": "gqfVue3Base",
"body": [
"",
"",
"",
" ",
" $1
",
" ",
"",
"",
"",
""
],
"description": "vue3base"
},
"vue-ts-scss template": {
"prefix": "gqfVue3TS",
"body": [
"",
"",
"",
" ",
" $1
",
" ",
"",
"",
"",
""
],
"description": "vue-ts-scss template"
}
}
Never类型:那些永不存在的值
项目课程:utility types—泛型编程
extends受到约束
泛型传参
16-黑马头条json2TS插件演示并提取到dts文件处理共享类型 shift+ctrl+alt+v
17-黑马头条-渲染频道数据-染数据前看vue调试工具.itcast
18-黑马头条-切换导航-状态提升-因为子组件都用到高亮的频道id-声明在父组件.itcast
19-黑马头条-列表更新-发送请求-watch-immediate进入发送请求-监听频道id的变化itcast
20-黑马头条-列表更新-渲染列表.itcast
21-接口文档生成TS类型声明文件-后端接口文档给力的话可以生成代码.itcast
22-pinia介绍-是个共享状态的库-方便管理数据共享状态itcast
23-pinia15分钟上手-ref响应式数据-普通函数-computed函数itcast
24-pinia官方文档复盘.itcast export const useXXXStore=defineStore(‘xxx’,回调函数)
25-storeToRefs解决解构丢失响应式函数itcast
面试技巧—是什么,为什么,怎么用
26-头条综合案例环境处理并说明pinia改造需求.itcast
27-pinia改造头条.itcast
28-day04下午总结itcast
29-补充-代码片段itcast
30-补充-unknown类型.itcast
31-补充-never类型itcast