Vue3 + Vite + TypeScript + Pinia + Yarn

  • yarn
    • 常用命令
  • vite 构建工具
    • vite 创建项目
  • Vue 3
    • vue 3 组件库
    • vue 文档
    • 组合式 API
      • 1. setup 函数
      • 2. reactive 函数
      • 3. ref 函数
      • 4. reactive 和 ref 的选择
      • 5. setup 语法糖
      • 6. computed 函数
      • 7. watch 函数
        • 7.1 使用 `watch` 监听一个响应式数据
        • 7.2 使用 `watch` 监听多个响应式数据
        • 7.3 使用 `watch` 监听响应式对象数据中的一个属性(简单)
        • 7.4 使用 `watch` 监听响应式对象数据中的一个属性(复杂),配置深度监听
      • 8. 声明周期函数
      • 9. ref 获取 DOM 元素
      • 10. ref操作组件 - defineExpose
      • 11. 父传子 - defineProps 函数
      • 12. 子传父 - defineEmits 函数
      • 13. 跨级组件通讯provide与inject函数
      • 14. 保持响应式 - toRefs 函数
  • TypeScript
    • TypeScript 文档
    • TypeScript 编译
    • 创建 vue-ts 项目
    • TypeScript 核心
      • 1. 类型注解
      • 2. 原始类型
      • 3. 数组类型
      • 4. 联合类型
      • 5. 类型别名
      • 6. 函数类型
      • 7. void 类型
      • 8. 可选参数
      • 9. 对象类型
      • 10. 接口 interface
      • 11. 类型推断
      • 12. 字面量类型
      • 13. any 类型
      • 14. 类型断言
      • 15. 泛型
        • 15.1 泛型别名
        • 15.2 泛型接口
        • 15.3 泛型函数
    • TypeScript 与 组合式API
      • 1. defineProps的TS写法
      • 2. defineEmits的TS写法
      • 3. ref的TS写法
      • 4. reactive的TS写法
      • 5. computed和TS
      • 6. 事件处理与TS
      • 7. Template Ref与TS
      • 8. 非空断言
      • 9. TypeScript类型声明文件
        • 9.1 内置类型声明文件
        • 9.2 第三方库类型声明文件
        • 9.3 自定义类型声明文件 【重要】
          • 9.3.1 共享类型(重要)
          • 9.3.2 给JS文件提供类型
  • Pinia
    • 知识点
      • 1. 使用步骤:
        • 1.1 安装
        • 1.2 导入
        • 1.4 创建仓库&使用仓库
        • 1.5 进行状态管理
        • 1.6 总结
      • 2. storeToRefs的使用

yarn

常用命令

# 查看yarn 版本
yarn -v
# 查看yarn配置
yarn config list
# 查看当前yarn源
yarn config get registry# 修改yarn源(初始)
yarn config set registry https://registry.yarnpkg.com# 修改yarn源(此处为淘宝的源)
yarn config set registry https://registry.npm.taobao.org# yarn安装依赖
yarn add 包名          # 局部安装
yarn global add 包名   # 全局安装# yarn 卸载依赖
yarn remove 包名         # 局部卸载
yarn global remove 包名  # 全局卸载(如果安装时安到了全局,那么卸载就要对应卸载全局的)# yarn 查看全局安装过的包
yarn global list
npm install -g yarn  # 安装yarn
yarn --version       # 安装成功后,查看版本号
md yarn   # 创建文件夹 yarn
cd yarn   # 进入yarn文件夹 # 初始化项目
yarn init # 同npm init,执行输入信息后,会生成package.json文件# yarn的配置项:
yarn config list # 显示所有配置项
yarn config get <key> # 显示某配置项
yarn config delete <key> # 删除某配置项
yarn config set <key> <value> [-g|--global] # 设置配置项# 安装包:
yarn install         # 安装package.json里所有包,并将包及它的所有依赖项保存进yarn.lock
yarn install --flat  # 安装一个包的单一版本
yarn install --force         # 强制重新下载所有包
yarn install --production    # 只安装dependencies里的包
yarn install --no-lockfile   # 不读取或生成yarn.lock
yarn install --pure-lockfile # 不生成yarn.lock
# 添加包(会更新package.json和yarn.lock):yarn add [package] #  在当前的项目中添加一个依赖包,会自动更新到package.json和yarn.lock文件中
yarn add [package]@[version] #  安装指定版本,这里指的是主要版本,如果需要精确到小版本,使用-E参数
yarn add [package]@[tag] #  安装某个tag(比如beta,next或者latest)# 不指定依赖类型默认安装到dependencies里,你也可以指定依赖类型:
yarn add --dev/-D # 加到 devDependencies
yarn add --peer/-P # 加到 peerDependencies
yarn add --optional/-O # 加到 optionalDependencies# 默认安装包的主要版本里的最新版本,下面两个命令可以指定版本:
yarn add --exact/-E # 安装包的精确版本。例如yarn add foo@1.2.3会接受1.9.1版,但是yarn add foo@1.2.3 --exact只会接受1.2.3版
yarn add --tilde/-T # 安装包的次要版本里的最新版。例如yarn add foo@1.2.3 --tilde会接受1.2.9,但不接受1.3.0yarn publish # 发布包
yarn remove <packageName>  # 移除一个包,会自动更新package.json和yarn.lock
yarn upgrade # 更新一个依赖: 用于更新包到基于规范范围的最新版本
yarn run   # 运行脚本: 用来执行在 package.json 中 scripts 属性下定义的脚本
yarn info <packageName> # 可以用来查看某个模块的最新版本信息yarn cache # 缓存
yarn cache list # 列出已缓存的每个包
yarn cache dir # 返回 全局缓存位置
yarn cache clean # 清除缓存

vite 构建工具

对比 webpack:

  • 需要查找依赖,打包所有的模块,然后才能提供服务,更新速度会随着代码体积增加越来越慢

vite 的原理:

  • 使用原生 ESModule 通过 script 标签动态导入,访问页面的时候加载到对应模块编译并响应

注明:项目打包的时候最终还是需要打包成静态资源的,打包工具 Rollup

问题:

  • 基于 webpack 构建项目,基于 vite 构建项目,谁更快体验更好?vite
  • 基于 webpackvue-cli 可以创建 vue 项目吗?可以,慢一点而已

vite 创建项目

  1. 运行创建项目命令:
# 使用npm
npm create vite@latest
# 使用yarn
yarn create vite
# 使用pnpm
pnpm create vite
  1. 输入项目名称,默认是 vite-project

  1. 选择前端框架

  1. 选择项目类型

  1. 创建完毕

  1. 进入项目目录,安装依赖,启动项目即可。

Vue 3

  1. 需要切换插件

    vue3 组件代码和 vue2 有些不一样,使用的语法提示和高亮插件也不一样。

    • vetur 插件需要禁用,安装 volar插件。
  2. 总结 vue3 写法不同

    1. 组件一个根节点非必需
    2. 创建应用挂载到根容器
    3. 入口页面,ESM 加载资源
  • 平常组件

    <template><div>节点1</div><div>节点2</div>
    </template>
    
  • main.js

    import { createApp } from 'vue'
    import App from './App.vue'
    // 根据App组件创建一个应用实例
    const app = createApp(App)
    // app应用挂载(管理)index.html的 #app 容器
    app.mount('#app')
    
  • index.html

    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
    

vue 3 组件库

库名称 简介
ant-design-vue PC 端组件库:Ant Design 的 Vue 实现,开发和服务于企业级后台产品
arco-design-vue PC 端组件库:字节跳动出品的企业级设计系统
element-plus PC 端组件库:基于 Vue 3,面向设计师和开发者的组件库
Naive UI PC 端组件库:一个 Vue 3 组件库,比较完整,主题可调,使用 TypeScript,快,有点意思
vant 移动端组件库:一个轻量、可靠的移动端组件库,于 2017 年开源
VueUse 基于 composition 组合式 api 的常用函数集合

vue 文档

中文文档:

  1. 相关文档

    1. Vue3 中文文档(新) https://cn.vuejs.org/
    2. Vue2 中文文档(旧) https://v2.cn.vuejs.org/
    3. Vue3 设计理念 https://vue3js.cn/vue-composition/
  2. 了解框架优点特点
    1. 首次渲染更快
    2. diff 算法更快
    3. 内存占用更少
    4. 打包体积更小
    5. 更好的 Typescript 支持
    6. Composition API 组合 API

组合式 API

1. setup 函数

setup函数是组合式API的入口函数

  • setup 函数作为组合式API的起点
  • 它在 beforeCreate 之前执行
  • 函数中 this 不是组件实例,是 undefined
  • 如果数据或者函数在模板中使用,需要在 setup 返回
<template><div class="container"><h1 @click="say()">{{msg}}</h1></div>
</template><script>
export default {setup () {console.log('setup执行了')console.log(this)// 定义数据和函数const msg = 'hi vue3'const say = () => {console.log(msg)}// 返回给模板使用return { msg , say}},beforeCreate() {console.log('beforeCreate执行了')console.log(this)}
}
</script>

2. reactive 函数

reactive 函数通常定义:复杂类型的响应式数据 且 知道明确字段

  • 不可以转换简单数据

使用步骤:

  • vue 中导出 reactive 函数
  • setup 函数中,使用 reactive 函数,传入一个普通对象,返回一个响应式数据对象
  • 最后 setup 函数返回一个对象,包含该响应式对象即可,模板中可使用
<template><div><p>姓名:{{state.name}}</p><p>年龄:{{state.age}} <button @click="state.age++">一年又一年</button></p></div>
</template><script>
// 1. 导入函数
import { reactive } from "vue";
export default {setup() {// 2. 创建响应式数据对象const state = reactive({ name: 'tom', age: 18 })// 3. 返回数据return { state }}
};
</script>

3. ref 函数

通常使用它定义响应式数据,不限类型

  • ref 可以把简单数据或者复杂数据转换成响应式数据,注意在JS使用时加上 .value,不过模板可省略。

使用步骤:

  • vue 中导出 ref 函数
  • setup 函数中,使用 ref 函数,传入普通数据(简单or复杂),返回一个响应式数据
  • 最后 setup 函数返回一个对象,包含该响应式数据即可
  • 注意:使用 ref 创建的数据,js 中需要 .valuetemplate 中可省略

代码示例:

<template><div><p>计数器:{{ count }}<button @click="count++">累加1</button><!-- template中使用可省略.value --><button @click="increment">累加10</button></p></div>
</template><script>
// 1. 导入函数
import { ref } from "vue";
export default {setup() {// 2. 创建响应式数据对象const count = ref(0);const increment = () => {// js中使用需要.valuecount.value += 10;};// 3. 返回数据return { count, increment };},
};
</script>

4. reactive 和 ref 的选择

在定义响应式数据的函数选择上,遵循:尽量使用 ref 函数支持所有场景,确定字段的对象使用 reactive 可以省去.value

  • reactive 可以转换对象成为响应式数据对象,但是不支持简单数据类型。
  • ref 可以转换简单数据类型为响应式数据对象,也支持复杂数据类型,但是操作的时候需要 .value

推荐用法:

  • 如果能确定数据是对象且字段名称也确定,可使用 reactive 转成响应式数据,其他一概使用 ref

参考代码:

// 1. 明确表单对象有两个字段
const form = reactive({username: '',password: ''
})// 2. 后台返回的数据对象
const data = ref(null)
const res = await axios.get('/user/100')
data.value = res.data

5. setup 语法糖

script setup 中的顶层变量都可以在模板使用,数据,函数,组件。

发现

  • 使用 setup 有几件事必须做:默认导出配置选项,setup函数声明,返回模板需要数据与函数。
<script>
export default {setup() {const say = () => console.log('hi')return { say }}
}
</script>

解法

  • 使用 setup 语法糖
<script setup>const say = () => console.log('hi')
</script>

示例代码

<script setup>// 显示隐藏const show = ref(true)const toggle = () => {show.value = !show.value}// 计数器const count = ref(0)const increment = () => {count.value ++}
</script><template><button @click="toggle">显示隐藏图片</button><img v-show="show" alt="Vue logo" src="./assets/logo.png" /><hr />计数器:{{ count }} <button @click="increment">累加</button>
</template>

6. computed 函数

使用 computed 定义计算属性,场景:当需要依赖一个数据得到新的数据使用计算属性

使用步骤:

  • vue 中导出 computed 函数
  • setup 函数中,使用 computed 函数,传入一个函数,函数返回计算好的数据
  • 最后 setup 函数返回一个对象,包含该计算属性数据即可,然后模板内使用

代码示例:

<script setup>import { ref, computed } from "vue";const scoreList = ref([80, 100, 90, 70, 60]);// 计算属性const betterList = computed(() => scoreList.value.filter((item) => item >= 90));// 改变数据,计算属性改变setTimeout(() => {scoreList.value.push(92, 66);}, 3000);</script><template><div><p>分数:{{ scoreList }}</p><p>优秀:{{ betterList }}</p></div>
</template>

7. watch 函数

  • watch(需要监听的数据,数据改变执行函数,配置对象) 来进行数据的侦听
  • 数据:单个数据,多个数据,函数返回对象属性,属性复杂需要开启深度监听
  • 配置对象:deep 深度监听 immediate 默认执行
  • watch('x', () => {}, {})

7.1 使用 watch 监听一个响应式数据

<script setup>import { ref, watch } from "vue";const count = ref(0);// 1. 监听一个响应式数据// watch(数据, 改变后回调函数)watch(count, () => {console.log("count改变了");});// 2s改变数据setTimeout(() => {count.value++;}, 2000);
</script><template><p>计数器:{{ count }}</p>
</template>

7.2 使用 watch 监听多个响应式数据

<script setup>import { reactive, ref, watch } from "vue";const count = ref(0);const user = reactive({name: "tom",info: {gender: "男",age: 18,},});// 2. 监听多个响应式数据// watch([数据1, 数据2, ...], 改变后回调函数)watch([count, user], () => {console.log("数据改变了");});// 2s改变数据setTimeout(() => {count.value++;}, 2000);// 4s改变数据setTimeout(() => {user.info.age++;}, 4000);
</script><template><p>计数器:{{ count }}</p><p>姓名:{{ user.name }} 性别:{{ user.info.gender }} 年龄:{{ user.info.age }}</p>
</template>

7.3 使用 watch 监听响应式对象数据中的一个属性(简单)

<script setup>import { reactive, watch } from "vue";const user = reactive({name: "tom",info: {gender: "男",age: 18,},});// 3. 监听响应式对象数据的一个数据,简单类型// watch(()=>数据, 改变后回调函数)watch(()=>user.name, () => {console.log("数据改变了");});// 2s改变数据setTimeout(() => {user.name = 'jack';}, 2000);// 4s改变数据setTimeout(() => {user.info.age = 60;}, 4000);
</script><template><p>姓名:{{ user.name }} 性别:{{ user.info.gender }} 年龄:{{ user.info.age }}</p>
</template>

7.4 使用 watch 监听响应式对象数据中的一个属性(复杂),配置深度监听

<script setup>import { reactive, watch } from "vue";const user = reactive({name: "tom",info: {gender: "男",age: 18,},});// 4. 监听响应式对象数据的一个数据,复杂类型// watch(()=>数据, 改变后回调函数, {deep: true})watch(() => user.info,() => {console.log("数据改变了");},{// 开启深度监听deep: true,});// 2s改变数据setTimeout(() => {user.info.age = 60;}, 2000);
</script><template><p>姓名:{{ user.name }} 性别:{{ user.info.gender }} 年龄:{{ user.info.age }}</p>
</template>

使用 watch 监听,配置默认执行

{// 开启深度监听deep: true,// 默认执行一次immediate: true
}

8. 声明周期函数

常用的 onMounted 组件渲染完毕:发请求,操作dom,初始化图表…

  • 生命周期钩子函数可以调用多次

具体内容:

  • Vue3和vue2的生命周期对比
选项式API下的生命周期函数使用 组合式API下的生命周期函数使用
beforeCreate 不需要(直接写到setup函数中)
created 不需要(直接写到setup函数中)
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeDestroyed onBeforeUnmount
destroyed onUnmounted
activated onActivated
deactivated onDeactivated

代码示例:

<script setup>import { onMounted } from "vue";// 生命周期函数:组件渲染完毕onMounted(()=>{console.log('onMounted触发了')})onMounted(()=>{console.log('onMounted也触发了')})
</script><template><div>生命周期函数</div>
</template>

9. ref 获取 DOM 元素

元素上使用 ref属性关联响应式数据,获取DOM元素

  • 默认值是null,需要在渲染完毕后访问DOM属性

步骤:

  1. 创建 ref => const hRef = ref(null)
  2. 模板中建立关联 => <h1 ref="hRef">我是标题</h1>
  3. 使用 => hRef.value

代码:

<script setup>
import { ref } from 'vue'const hRef = ref(null)
const clickFn = () => {hRef.value.innerText = '我不是标题'
}
</script><template><div><h1 ref="hRef">我是标题</h1><button @click="clickFn">操作DOM</button></div>
</template>

10. ref操作组件 - defineExpose

  • 配合 defineExpose 暴露数据和方法,ref获取的组件实例才可以使用

步骤:

  • 使用 <script setup> 的组件是默认关闭的,组件实例使用不到顶层的数据和函数。
  • 需要配合 defineExpose 暴露给组件实例使用,暴露的响应式数据会自动解除响应式。

代码示例:

<script setup>
import { ref } from 'vue'const count = ref(0)
const validate = () => {console.log('表单校验方法')
}// 暴露属性给外部组件使用
defineExpose({count, validate})
</script><template><h3>我是Form组件</h3>
</template>

ref操作组件

<script setup>
import { ref } from 'vue'
import Form from './components/Form.vue'// 1. 提供一个ref
const formRef = ref(null)
// 2. 使用组件组件和方法
const fn = () => {console.log(formRef.value.count)formRef.value.validate()
}
</script><template><Form ref="formRef"></Form>
</template>

11. 父传子 - defineProps 函数

  • 如果使用 defineProps 接收数据,这个数据只能在模板中渲染
  • 如果想要在 script 中也操作 props 属性,应该接收返回值

步骤:

  1. 父组件提供数据
  2. 父组件将数据传递给子组件
  3. 子组件通过 defineProps 进行接收
  4. 子组件渲染父组件传递的数据

代码:

ParentCom.vue

<script setup>
import { ref } from 'vue'
import ChildCom from './components/ChildCom.vue'const money = ref(100)
const car = ref('玛莎拉蒂')
</script><template><div><h1>我是父组件</h1><div>金钱:{{ money }}</div><div>车辆:{{ car }}</div><hr /><ChildCom :money="money" :car="car"></ChildCom></div>
</template>

ChildCom.vue

<script setup>
import { computed } from 'vue'// defineProps: 接收父组件传递的数据
const props = defineProps({money: Number,car: String,
})
// 使用props
console.log(props.money)
</script><template><div><h3>我是子组件</h3><div>{{ money }} --- {{ car }}</div></div>
</template>

12. 子传父 - defineEmits 函数

  • defineEmits 获取 emit 函数,且组件需要触发的事件需要显性声明出来

步骤:

  1. 子组件通过 defineEmits获取 emit 函数(因为没有this)
  2. 子组件通过 emit 触发事件,并且传递数据
  3. 父组件提供方法
  4. 父组件通过自定义事件的方式给子组件注册事件

代码:

ChildCom.vue

<script setup>
defineProps({money: Number,car: String,
})// 得到emit函数,显性声明事件名称
const emit = defineEmits(['changeMoney'])
const change = () => {emit('changeMoney', 10)
}
</script>

PrarentCom.vue

<script setup>
import { ref } from 'vue'
import ChildCom from './components/ChildCom.vue'const money = ref(100)
const car = ref('玛莎拉蒂')
const changeMoney = (num) => {money.value = money.value - num
}
</script>
<ChildCom :money="money" :car="car" @changeMoney="changeMoney"></ChildCom>

13. 跨级组件通讯provide与inject函数

  • provide 和 inject 是解决跨级组件通讯的方案

    • provide 提供后代组件需要依赖的数据或函数
    • inject 注入(获取)provide提供的数据或函数
  • 官方术语:依赖注入
    • App是后代组件依赖的数据和函数的提供者,Child是注入(获取)了App提供的依赖

落地代码:

  • 祖先组件:App.vue
<script setup>
import { provide, ref } from 'vue';
import ParentCom from './ParentCom.vue';// 1. app组件数据传递给child
const count = ref(0);
provide('count', count);// 2. app组件函数传递给child,调用的时候可以回传数据
const updateCount = (num) => {count.value += num;
};
provide('updateCount', updateCount);
</script><template><divclass="app-page"style="border: 10px solid #ccc; padding: 50px; width: 600px">app 组件 {{ count }} updateCount<ParentCom /></div>
</template>
  • 父级组件:ParentCom.vue
<script setup>
import ChildCom from './ChildCom.vue';
</script><template><div class="parent-page" style="padding: 50px">parent 组件<hr /><ChildCom /></div>
</template>
  • 子级组件:ChildCom.vue
<script setup>
const count = inject('count');
const updateCount = inject('updateCount');
</script><template><div class="child-page" style="padding: 50px; border: 10px solid #ccc">child 组件 {{ count }} <button @click="updateCount(100)">修改count</button></div>
</template>

14. 保持响应式 - toRefs 函数

当去解构和展开响应式数据对象使用 toRefs 保持响应式

  • 作用:把对象中的每一个属性做一次包装成为响应式数据
  • 响应式数据展开的时候使用,解构响应式数据的时候使用

落地代码:

  • 基础案例
<script setup>
import { reactive } from "vue";
const user = reactive({ name: "tom", age: 18 });
</script><template><div><p>姓名:{{ user.name }}</p><p>年龄:{{ user.age }} <button @click="user.age++">一年又一年</button></p></div>
</template>
  • 使用响应式数据,踩坑
<script setup>
import { reactive } from "vue";
const { name, age } = reactive({ name: "tom", age: 18 });
</script><template><div><p>姓名:{{ name }}</p><!-- 响应式丢失 --><p>年龄:{{ age }} <button @click="age++">一年又一年</button></p></div>
</template>
  • 使用 toRefs 处理响应式数据,爬坑
import { reactive, toRefs } from "vue";
const user = reactive({ name: "tom", age: 18 });
const { name, age } = toRefs(user)

TypeScript

TypeScript 是一种带有 类型语法 的 JavaScript 语言,在任何使用 JavaScript 的开发场景中都可以使用。

  • TS 需要编译才能在浏览器运行。

TypeScript 文档

官方网站:https://www.typescriptlang.org/

中文官网: https://www.tslang.cn/

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:

  • 新建 hello.ts 文件
  • 当前目录打开命令行窗口,执行 tsc hello.ts 命令,同级目录生成 hello.js 文件
  • 执行 node hello.js 验证一下

创建 vue-ts 项目

# npm 6.x
npm create vite@latest my-vue-ts-app --template vue-ts# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-ts-app -- --template vue-ts# yarn
yarn create vite my-vue-ts-app --template vue-ts# pnpm
pnpm create vite my-vue-ts-app --template vue-ts

TypeScript 核心

1. 类型注解

变量后面约定类型的语法,就是类型注解

  • : number 就是类型注解,它为变量提供类型约束。
  • 约定了什么类型,就只能给该变量赋值什么类型的值,否则报错。

示例代码:

// 约定变量 age 的类型为 number 类型
let age: number = 18;
age = 19;

2. 原始类型

  • JS 已有类型

    • 简单类型,number string boolean null undefined
    • 复杂类型,对象 数组 函数
  • TS 新增类型
    • 联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any、泛型 等

原始类型:

  • 使用简单,完全按照 JS 的类型来书写即可
let age: number = 18;
let myName: string = '黑马程序员';
let isLoading: boolean = false;
let nullValue: null = null;
let undefinedValue: undefined = undefined;

3. 数组类型

推荐使用:

  • number[] 写法
  • 写法 1
let numbers: number[] = [1, 3, 5];
  • 写法 2
let strings: Array<string> = ['a', 'b', 'c'];

4. 联合类型

  • 类型与类型之间使用 | 连接,代表类型可以是它们当中的其中一种,这种类型叫:联合类型

代码示例:

数组中有 numberstring 类型

let arr: (number | string)[] = [1, 'a', 3, 'b'];

定时器添加类型:

let timer: number | null = null;
timer = setInterval(() => {}, 1000);

5. 类型别名

  • type 类型别名 = 具体类型 基本语法
  • 定义类型别名,遵循大驼峰命名规范,类似于变量
  • 使用类型别名,与类型注解的写法一样即可

示例代码:

// let arr: ( number | string )[] = [ 1, 'a', 4]
// 类型别名: type 类型别名 = 具体类型
type CustomArr = (number | string)[];
let arr: CustomArr = [1, 'a', 4];

使用场景:

  • 当同一类型(复杂)被多次使用时,可以通过类型别名,简化 该类型的使用
type CustomArr = (number | string)[];
let arr: CustomArr = [1, 'a', 4];
let arr2: CustomArr = [2, 'b', 8];

6. 函数类型

  • 给函数指定类型,其实是给 参数返回值 指定类型。
  • 两种写法:
    • 在函数基础上 分别指定 参数和返回值类型
    • 使用类型别名 同时指定 参数和返回值类型
  • 通过类似箭头函数形式的语法来为函数添加类型,只适用于 函数表达式

示例代码 1:分别指定

// 函数声明
function add(num1: number, num2: number): number {return num1 + num2;
}// 箭头函数
const add = (num1: number, num2: number): number => {return num1 + num2;
};

示例代码 2:同时指定

type AddFn = (num1: number, num2: number) => number;const add: AddFn = (num1, num2) => {return num1 + num2;
};

7. void 类型

如果函数没有返回值,定义函数类型时返回值类型为 void

  • 如果函数没有返回值,且没有定义函数返回值类型的时候,默认是 void
  • JS 中如果没有返回值,默认返回的是 undefined,但是 voidundefinedTypeScript 中并不是一回事,如果指定返回值类型是 undefined 那返回值必须是 undefined

代码示例:

const say = (): void => {console.log('hi');
};
const say = () => {console.log('hi');
};

**注意事项:**在 JS 中如果没有返回值,默认返回的是 undefined,但是 voidundefinedTypeScript 中并不是一回事,如果指定返回值类型是 undefined 那返回值必须是 undefined

const add = (): undefined => {return undefined;
};

8. 可选参数

使用 ? 将参数标记为可选

如果函数的参数,可以传也可以不传,这种情况就可以使用 可选参数 语法,参数后加 ? 即可

  • 必选参数必须在可选参数的前面

    • (start?: number, end: number) 错误的写法

代码示例:

const fn = (n?: number) => {// ..
};
fn();
fn(10);
const mySlice = (start?: number, end?: number) => {console.log('起始Index:', start, '结束Index:', end);
};
mySlice();
mySlice(1);
mySlice(1, 2);

9. 对象类型

基本使用:

  • TS 的对象类型,其实就是描述对象中的 属性 方法 的类型,因为对象是由属性和方法组成的。
// 空对象
let person: {} = {};// 有属性的对象
let person: { name: string } = {name: '同学',
};// 有属性和方法,一行书写多个属性 ; 分隔
let person: { name: string; sayHi(): void } = {name: 'jack',sayHi() {},
};// 换行写可以省略 ; 符号
let person: {name: string;sayHi(): void;
} = {name: 'jack',sayHi() {},
};

扩展用法:

  • 函数使用箭头函数类型
let person: {name: stringsayHi: () => void
} = {name: 'jack',sayHi() {},
};
  • 对象属性可选
// 例如: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) => {};
  • 使用声明描述对象结构?{}
  • 属性怎么写类型?属性名: 类型
  • 方法怎么写类型? 方法名(): 返回值类型
  • 对象的方法使用箭头函数类型怎么写?{sayHi:()=>void}
  • 对象的可选参数怎么设置?{name?: string}
  • 对象类型会使用 {} 如何提供可阅读性?类型别名

10. 接口 interface

接口声明是命名对象类型的另一种方式

定义:interface A {} 继承:interface A extends B {}

继承后 接口A 拥有 接口B 的所有属性和函数的类型声明

继承:

  • 相同的属性或展示可以抽离出来,然后使用 extends 实现继承复用
interface Point2D {x: number;y: number;
}
// 继承 Point2D
interface Point3D extends Point2D {z: number;
}
// 继承后 Point3D 的结构:{ x: number; y: number; z: number }

type 交叉类型:

使用 & 可以合并连接的对象类型,也叫:交叉类型

  • 实现 Point2D{z: number} 类型合并得到 Ponit3D 类型
// 使用 type 来定义 Point2D 和 Point3D
type Point2D = {x: number;y: number;
};// 使用 交叉类型 来实现接口继承的功能:
// 使用 交叉类型 后,Point3D === { x: number; y: number; z: number }
type Point3D = Point2D & {z: number;
};let o: Point3D = {x: 1,y: 2,z: 3,
};

11. 类型推断

在 TS 中存在类型推断机制,在没有指定类型的情况下,TS 也会给变量提供类型。

  • 能省略类型注解的地方就省略,充分利用TS推断 的能力,提高开发效率
  • 不知道类型怎么写,可以把鼠标放至变量上,可以通过 Vscode 提示看到类型

发生类型推断的几个场景场景:

  • 声明变量并初始化时
// 变量 age 的类型被自动推断为:number
let age = 18;
  • 决定函数返回值时
// 函数返回值的类型被自动推断为:number
const add = (num1: number, num2: number) => {return num1 + num2;
};

12. 字面量类型

使用 js字面量 作为变量类型,这种类型就是字面量类型

js 字面量如:18 'jack' ['a'] {age: 10}

代码示例:

// : 'jack' 是字面量类型
let name: 'jack' = 'jack';
// : 18 是字面量类型
let age: 18 = 18;// 报错:不能将类型“19”分配给类型“18”
age = 19;let str1 = 'Hello TS'; // 类型 string
const str2 = 'Hello TS'; // 类型 Hello TS
// 原因:str2 是 const 声明的,值只能是 Hello TS,所以类型只能是 Hello TS

类型应用:

例如:性别只能是 男 和 女,不会出现其他值。

// let gender = '男'
// gender = '女'
// ------------------------
type Gender = '男' | '女'
let gender: Gender = '男'
gender = '女'// 使用自定义类型:
type Direction = 'up' | 'down' | 'left' | 'right'
function changeDirection(direction: Direction) {console.log(direction)
}
// 调用函数时,会有类型提示:
changeDirection('up')

13. any 类型

any 类型的作用是逃避 TS 的类型检查

  • any 的使用越多,程序可能出现的漏洞越多,因此不推荐使用 any 类型,尽量避免使用。
  • 显式any情况:当变量的类型指定为 any 的时候,不会有任何错误,也不会有代码提示,TS会忽略类型检查
let obj: any = { age: 18 }
obj.bar = 100
obj()
const n: number = obj
// 以上的代码虽然没有报错提示,但是将来是可能出现错误的
  • 隐式any的情况:声明变量不给类型或初始值,函数参数不给类型或初始值
// 声明变量不给类型或初始值
let a;
// 函数参数不给类型或初始值
const fn = (n) => {}

14. 类型断言

你会比 TS 更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型

使用 as 关键字实现类型断言

// aLink 的类型 HTMLElement,该类型只包含所有标签公共的属性或方法
// 这个类型太宽泛,没包含 a 元素特有的属性或方法,如 href
const aLink = document.getElementById('link')
  • 但是我们明确知道获取的是一个 A 元素,可以通过 类型断言 给它指定一个更具体的类型。
const aLink = document.getElementById('link') as HTMLAnchorElement
  • 解释:

    1. 使用 as 关键字实现类型断言
    2. 关键字 as 后面的类型是一个更加具体的类型(HTMLAnchorElement 是 HTMLElement 的子类型)
    3. 通过类型断言,aLink 的类型变得更加具体,这样就可以访问 a 标签特有的属性或方法了

例如:

const img = document.getElementById('img') as HTMLImageElement
// 如果不知道标签的类型:document.querySelector('div') 鼠标摸上去就可以看见

15. 泛型

在TypeScript中,泛型是一种创建可复用代码组件的工具。这种组件不只能被一种类型使用,而是能被多种类型复用。类似于参数的作用,泛型是一种用以增强类型(types)、接口(interfaces)、函数类型等能力的非常可靠的手段。

15.1 泛型别名

  • 泛型:定义类型别名后加上<类型参数> 就是泛型语法, 使用的时候传入具体的类型即可
  • <T> 是一个变量,可以随意命名,建议遵循大驼峰即可。
  • 和类型别名配合,在类型别名后加上泛型语法,然后类型别名内就可以使用这个类型参数
  • 泛型可以提高类型的复用性灵活性

代码示例:

// 对后台返回的数据进行类型定义
type User = {name: string;age: number;
}type Goods = {id: number;goodsName: string;
}type Data<T> = {msg: string;code: number;data: T
}// 使用类型
type UserData = Data<User>
type GoodsData = Data<Goods>

15.2 泛型接口

在接口名称的后面添加 <类型变量>,那么,这个接口就变成了泛型接口,接口中所有成员都可以使用类型变量

代码示例:

// 对象,获取单个ID函数,获取所有ID函数,ID的类型肯定是一致的,但是可能是数字可能是字符串
interface IdFn<T> {id: () => T;ids: () => T[];
}const idObj: IdFn<number> = {id() { return 1 },ids() { return [1, 2] },
};

内置的泛型接口:

  • 可以通过 Ctrl + 鼠标左键(Mac:Command + 鼠标左键) 去查看内置的泛型接口
const arr = [1, 2, 3];
// TS有自动类型推断,其实可以看做:const arr: Array<number> = [1, 2, 3]
arr.push(4);
arr.forEach((item) => console.log(item));

15.3 泛型函数

  • 泛型函数语法?

    • 函数名称后加上 <T>T是类型参数,是个类型变量,命名建议遵循大驼峰即可。
  • T什么时候确定?
    • 当你调用函数的时候,传入具体的类型,T 或捕获到这个类型,函数任何位置均可使用。
  • 泛型函数好处?
    • 让函数可以支持不同类型(复用),且保证类型是安全的。
  • 调用函数,什么时候可以省略泛型?
    • 传入的数据可以推断出你想要的类型,就可以省略。

代码示例:

// 函数的参数是什么类型,返回值就是什么类型
function getId<T>(id: T): T {return id
}let id1 = getId<number>(1)
let id2 = getId('2')
// TS会进行类型推断,参数的类型作为泛型的类型 getId<string>('2')
// 我需要的类型 { name: string, age?: number } 但是推断出来是 { name: string}
let id2 = getId({name:'jack'})

TypeScript 与 组合式API

typescript 配合 Vue3 composition-api 使用

https://staging-cn.vuejs.org/guide/typescript/composition-api.html

  • script 加上 lang="ts" 才能写ts代码

    <script setup lang="ts"></script>

1. defineProps的TS写法

1 defineProps 的基本使用:

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

2 defineProps 通过泛型参数来定义 props 的类型通常更直接:

const props = defineProps<{money: numbercar?: string
}>()

3 如果需要给 props 设置默认值,需要使用 withDefaults 函数:

const props = withDefaults(defineProps<{money: number;car?: string;
}>(),{car: '宝马车'
})

4 上面写法太笨拙,可以使用 响应式语法糖 解构 + defineProps 就行:

const { money, car = "宝马车" } = defineProps<{money: numbercar?: string
}>();

注意:目前需要 显式地选择开启 ,因为它还是一个实验性特性。

// vite.config.ts
export default defineConfig({plugins: [vue({reactivityTransform: true,}),],
});

响应式语法糖 即将移除,建议使用 withDefaults 过度

2. defineEmits的TS写法

1. defineEmits 的基本用法:

const emit = defineEmits(['changeMoney', 'changeCar'])

2. defineEmits 通过泛型参数来定义,可以实现更细粒度的校验:

const emit = defineEmits<{(e: 'changeMoney', money: number): void(e: 'changeCar', car: string): void
}>()

了解:扩展TS语法 调用签名

3. ref的TS写法

ref() 会隐式的依据数据推导类型

1. 如果是简单类型,推荐使用类型推导:

// const money = ref<number>(10)const money = ref(10)

2. 如果是复杂类型,推荐指定泛型:

type Todo = {id: numbername: stringdone: boolean
}
const list = ref<Todo[]>([])setTimeout(() => {list.value = [{ id: 1, name: '吃饭', done: false },{ id: 2, name: '睡觉', done: true }]
}, 1000)

复杂数据一般是后台返回数据,默认值是空,无法进行类型推导。

4. reactive的TS写法

reactive() 也会隐式的依据数据推导类型

  • 官方:不推荐使用 reactive() 的泛型参数,因为底层和 ref() 实现不一样。

1. 默认值属性是固定的,推荐使用类型推导:

// 推导得到的类型:{ title: string }
const book = reactive({ title: 'Vue3' })

2. 根据默认值推导不出我们需要的类型,推荐使用接口或者类型别名给变量指定类型:

// 我们想要的类型:{ title: string, year?: number }
type Book = {title: stringyear?: number
}
const book: Book = reactive({ title: 'Vue3' })
book.year = 2022

5. computed和TS

1. computed() 会从其计算函数的返回值上推导出类型:

import { ref, computed } from 'vue'const count = ref(100);
const doubleCount = computed(() => count.value * 2);

2. 可以通过泛型参数显式指定类型:

const doubleMoney = computed<string>(() => (count.value * 2).toFixed(2));

6. 事件处理与TS

1. 不加类型,event默认是any,类型不安全:

<script setup lang="ts">
// 提示:参数“event”隐式具有“any”类型。
const handleChange = (event) => {console.log(event.target.value)
}
</script><template><input type="text" @change="handleChange" />
</template>

2. 处理类型:

// `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)
}

7. Template Ref与TS

模板 ref 需要通过一个显式指定的泛型参数,建议默认值 null

  • 注意为了严格的类型安全,有必要在访问 el.value 时使用可选链或类型守卫。
  • 这是因为直到组件被挂载前,这个 ref 的值都是初始的 null,并且在由于 v-if 的行为将引用的元素卸载时也可以被设置为 null
<script setup lang="ts">
import { ref, onMounted } from 'vue'const el = ref<HTMLInputElement| null>(null)onMounted(() => {el.value?.focus()
})
</script><template><input ref="el" />
</template>

8. 非空断言

处理类型可能是 null 或 undefined 的值,下面的属性或函数的访问赋值:

1. 可选链

<script setup lang="ts">
import { onMounted, ref } from 'vue';const input = ref< HTMLInputElement | null >(null)onMounted(()=>{// 只能访问console.log(input.value?.value);
})</script><template><div>App组件</div><input type="text" ref="input" value="abc">
</template>

2. 逻辑判断

  if (input.value) {console.log(input.value.value)input.value.value = '123'}

3. 非空断言

  // 一定要确定不为空!!!console.log(input.value!.value)input.value!.value = '123'

9. TypeScript类型声明文件

  • 在第三方库中的JS代码都有对应的 TS类型声明文件

什么是类型什么文件?

  • 在 TypeScript 中以 .d.ts 为后缀的文件,我们称之为 TypeScript 类型声明文件。它的主要作用是描述 JavaScript 模块内所有导出成员的类型信息。

TS 中有两种文件类型:.ts 文件 .d.ts 文件作用是啥?

  • .ts 文件:

    1. 既包含类型信息又可执行代码
    2. 可以被编译为 .js 文件,然后,执行代码
    3. 用途:编写程序代码的地方
  • .d.ts 文件:
    1. 只包含类型信息的类型声明文件
    2. 不会生成 .js 文件,仅用于提供类型信息,在.d.ts文件中不允许出现可执行的代码,只用于提供类型
    3. 用途:为 JS 提供类型信息

9.1 内置类型声明文件

TypeScript 给 JS 运行时可用的所有标准化内置 API 都提供了声明文件,这个声明文件就是 内置类型声明文件

  • 可以通过 Ctrl + 鼠标左键(Mac:Command + 鼠标左键)来查看内置类型声明文件内容
  • 查看 forEach 的类型声明,在 VSCode 中会自动跳转到 lib.es5.d.ts 类型声明文件中
  • 像 window、document 等 BOM、DOM API 也都有相应的类型声明文件 lib.dom.d.ts

9.2 第三方库类型声明文件

首先,常用的第三方库都有相应的类型声明文件,只是使用的方式不同而已。

情况1:库本身自带类型声明文件

  • 比如:axios,安装后可查看 node_modules/axios 可发现对应的类型声明文件。
  • 导入 axios 后就会加载对应的类型文件,提供该库的类型声明。

情况2:由 DefinitelyTyped 提供

  • 比如:jquery,安装后导入,提示:需要安装 @types/jquery 类型声明包
  • DefinitelyTyped 是一个 github 仓库,用来提供高质量 TypeScript 类型声明
  • 当安装 @types/* 类型声明包后,TS 也会自动加载该类声明包,以提供该库的类型声明

https://www.typescriptlang.org/dt/search 可以搜索是否有对应的 @types/*

9.3 自定义类型声明文件 【重要】

9.3.1 共享类型(重要)
  • 如果多个 .ts 文件中都用到同一个类型,此时可以创建 .d.ts 文件提供该类型,实现类型共享。
  • 操作步骤:
    1. 创建 index.d.ts 类型声明文件。
    2. 创建需要共享的类型,并使用 export 导出(TS 中的类型也可以使用 import/export 实现模块化功能)。
    3. 在需要使用共享类型的 .ts 文件中,通过 import 导入即可(.d.ts 后缀导入时,直接省略)。

src/types/data.d.ts

export type Person = {id: number;name: string;age: number;
};

App.vue

<script lang="ts" setup>
import { Person } from './types/data'
const p: Person = {id: 100,name: 'jack',age: 19
}
</script>
9.3.2 给JS文件提供类型
  • 在导入 .js 文件时,TS 会自动加载与 .js 同名的 .d.ts 文件,以提供类型声明。

  • declare 关键字:

    • 用于类型声明,为其他地方(比如,.js 文件)已存在的变量声明类型,而不是创建一个新的变量。
    1. 对于 type interface 等这些明确就是 TS 类型的(只能在 TS 中使用的),可以省略 declare 关键字。
    2. 其他 JS 变量,应该使用 declare 关键字,明确指定此处用于类型声明。

add/index.js

const add = (a, b) => {return a + b;
};const point = (p) => {console.log('坐标:', p.x, p.y);
};export { add, point }

add/index.d.ts

declare const add: (a: number, b: number) => number;type Position = {x: number;y: number;
};declare const point: (p: Position) => void;export { add , point};

main.ts

import { add , point} from './add';add(3, 10)point({x: 100, y: 200})

Pinia

  • Pinia 是一个状态管理工具,它和 Vuex 一样为 Vue 应用程序提供共享状态管理能力。
  • 语法和 Vue3 一样,它实现状态管理有两种语法:选项式API组合式API,我们学习组合式API语法。
  • 它也支持 Vue2 也支持 devtools,当然它也是类型安全的,支持 TypeScript
  • 可以创建多个全局仓库,不用像 Vuex 一个仓库嵌套模块,结构复杂。
  • 管理数据简单,提供数据和修改数据的逻辑即可,不像Vuex需要记忆太多的API。

知识点

1. 使用步骤:

1.1 安装

yarn add pinia
# or
npm i pinia

1.2 导入

导入实例化,当做插件使用,和其他插件使用套路相同

import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'const pinia = createPinia()
const app = createApp(App)app.use(pinia)
app.mount('#app')

1.4 创建仓库&使用仓库

import { defineStore } from "pinia"
import { computed, ref } from "vue"export const useCounterStore = defineStore("counter", () => {return {}
})
<script setup lang="ts">
import { useCounterStore } from "./store/counter"
// store中有状态和函数
const store = useCounterStore()
</script>

1.5 进行状态管理

// state
const count = ref(100)
// getters
const doubleCount = computed(() => count.value * 2)
// mutations
const update = () => count.value++
// actions
const asyncUpdate = () => {setTimeout(() => {count.value++}, 1000)
}
return { count, doubleCount, update, asyncUpdate }
<template>APP {{ store.count }} {{ store.doubleCount }}<button @click="store.update()">count++</button><button @click="store.asyncUpdate()">async update</button>
</template>

1.6 总结

  • 通过 const useXxxStore = defineStore('id',函数) 创建仓库得到使用仓库的函数
Vuex Pinia
state refreactive创建的响应式数据
getters computed 创建的计算属性
mutations 和 actions 普通函数,同步异步均可
  • 使用Pinia与在组件中维护数据大体相同,这就是 Pinia 的状态管理基本使用

2. storeToRefs的使用

问题:

  • 当我们想解构 store 提供的数据时候,发现数据是没有响应式的。

回忆:

  • 在学习 vue 组合式API创建的响应式数据的时候,使用 toRefs 保持结构后数据的响应式

方案:

  • 使用 storeToRefs 解决解构仓库状态丢失响应式的问题

代码:

import { storeToRefs } from 'pinia'const store = useCounterStore()
const { count, doubleCount } = storeToRefs(store)

小结:

  • 当你想从 store 中解构对应的状态使用,需要使用 storeToRefs

vue3 + vite + ts + pinia + yarn相关推荐

  1. vue3:vue3+vite+ts+pinia

    一.背景 记录一套技术方案. 二.项目基础 2.1.创建项目 yarn create vite 输入名字后,这里出现了几个选项,不清楚都是干啥的,下来研究 选择后完成 2.2.vite.config. ...

  2. uniapp+Vue3+Vite+ts+pinia

    创建项目:npx degit dcloudio/uni-preset-vue#vite-ts my-vue3-project 使用pinia-plugin-persistedstate可以实现持久化存 ...

  3. 【Vue3+Vite+TS项目集成ESlint +Prettier实现代码规范检查和代码格式化】

    目录 前言 创建项目 安装初始化ESlint 安装ESlint: 初始化ESlint: 安装配置Prettier 安装prettier: 配置prettier: 配置VScode保存时自动格式化代码 ...

  4. 【记录】VUE3 + VITE + TS 配置跨域

    [记录]VUE3 + VITE + TS 配置跨域 在vite.config.ts进行如下设置 在vite.config.ts进行如下设置 server: {host: true,// 设置端口号po ...

  5. 如何在vue3+vite+ts中使用require

    vue3+vite+ts中不能使用require 之前使用vue2,去动态设置图片src属性时,采用require,但是vue3+vite+ts中使用require,项目能够运行,但浏览器中报错req ...

  6. vue3 vite ts引入vue文件报错 ts(2307)

    vue3 vite ts 生成的项目模板,在ts文件中引入vue文件报错 ts(2307),只是ts报错,并不影响项目运行. 官方文档有说明:http://vue.dragonlm.com/guide ...

  7. vue3 + vite + ts 集成mars3d

    vue3 + vite + ts 集成mars3d 文章目录 vue3 + vite + ts 集成mars3d 前言 一.创建一个vue3 + vite + ts项目 二.引入mars3d相关依赖 ...

  8. 一个基于vue3+vite+ts的完整项目

    VUE VBEN ADMIN2.0 介绍 vue-vben-admin-2.0 是一个全新的开源系统,基于ant-design-vue2.x,typescript4,vue3,vite实现的 vue3 ...

  9. Vue3+Vite+TS+Eslint搭建生产项目

    目录 一.初始化一个vite项目: 二.配置prettier,使用prettier进行语法规范 三.将ESLint的 错误显示在浏览器界面 一.初始化一个vite项目: yarn create vit ...

最新文章

  1. ming 贪心 NOIP模拟
  2. 事故通报绝不能一报了事22344
  3. easyui树拖拽排序java_项目中集成Easyui-Tree,可拖拽更新节点
  4. python 打开targz文件_Python下使用pandas打开excel文件并进行处理
  5. 计算机or笔记本,笔记本or台式机?大学生第一个烦恼被它解决了
  6. Linux 网络服务之FTP 文件传输
  7. Linux创建进程必须fork么,Linux - fork() 创建进程
  8. 【转】关于EASYSIZE宏(动态调整控件位置、大小的宏)
  9. MapWinGis入门
  10. qt tabwidget 设置tab 位置_qml创建TabWidget的案例
  11. 思科路由器配置命令大全
  12. WinZip for Mac注册版
  13. 使用Python对比两个excel表格中的重复数据
  14. OneDrive教育版注册和登录
  15. 根据时间进行视频的裁剪
  16. 马尔可夫链的定义、举例和应用
  17. 转Ruby on Rails的核心特性是什么
  18. CS品牌SD NAND(贴片式T卡)和儿童摇摇车搭配资料
  19. 记录2015年年初跳槽的经历!
  20. 2022年Q3白酒销量排行榜

热门文章

  1. Vue v-show
  2. android编译boost,使用android ndk编译boost动态库
  3. 《来吧,一起创客》新书上市,精彩抢先看!
  4. HBase为什么适合海量数据场景
  5. 用Python深度学习来快速实现图片的风格迁移
  6. java 线程池扩容_java线程池自动扩容
  7. SteamVR插件使用
  8. Android Studio学习——布局
  9. python hello world程序代码_第一个Python程序——hello world
  10. localStorage(本地储存)