vue3 script setup + ts 实践

本文探讨了Vue3中script setup与TypeScript的结合使用,重点在于顶层属性的直接使用、响应式数据的声明及优化、生命周期钩子的处理。通过示例展示了如何在模板中直接使用顶层绑定,并利用setup进行响应式数据的管理,同时讲解了watch的用法和生命周期函数的调用方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ts + setup

本文是对vue3.x 语法的实践,不会单独的介绍某个具体的 API 语法,详细语法请参考 vue官网;此次文章中使用的是 script setup + ts 的用法,个人比较喜欢的一种语法,感觉是 vue3 关于 ts 的终极实践。

顶层属性

任何在 script setup 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容) 都能在模板中直接使用; 但仅仅是使用,由于不是响应式数据,没有双向绑定,所以不会在页面上同步更新。

<template>
  <div>
    <h1>{{ hello }}</h1>
    <p>
      {{ count }}
      <button @click="handleClick">add</button>  
    </p>
  </div>
</template>

<script lang="ts" setup>
// 如果只是单类型,则 vue 会自动推到,不用加类型
let hello: string | undefined = "hello, vue";

// vue 根据后面的值,自动推导。
let count = 0;

function handleClick(): void {
  console.log(count);
  count++;
}
</script>

响应式数据

  • ref: 用来给 基本数据类型 绑定响应式数据,访问时需要通过 .value 的形式, tamplate 不需要 .value,会被解析。
  • reactive: 用来给 复杂数据类型 绑定响应式数据,直接访问即可。
<template>
  <div>
    <h1>{{ title }}</h1>
    <p>
      当前等级:{{ count }}
      <button @click="handleAdd">升级</button>  
    </p>

    <p>宠物:{{ userInfo.pet }}</p>
  </div>
</template>

<script lang="ts" setup>
import { ref, reactive } from "vue";

// 设置基本数据类型时,使用 ref
const title = ref<string>("响应式数据");

// 也可以根据设置的默认数据推导当前类型
let count = ref(0);

// 用户信息 --- reactive 用来绑定复杂数据类型
interface IUserInfo {
  name: string,
  pet: string | undefined,
  level: number
}
const userInfo = reactive<IUserInfo>({
  name: "zs",
  level: 0,
  pet: undefined,
})

// 升级
function handleAdd(): void {
  count.value++;

  // 复杂数据类型,不需要通过 .value 的形式获取数据
  userInfo.level = count.value;
  userInfo.pet = count.value >= 5 ? '马' : undefined; // 达到 5 级 解锁宠物
}
</script>

使用 watch 优化上面的例子

  1. vue 中结构 watch;
  2. watch 是一个函数,可以一次写多个,也可以一次性监听多个例子;
<template>
  <div>
    <h1>{{ title }}</h1>
    <p>
      当前等级:{{ count }}
      <button @click="handleAdd">升级</button>  
    </p>

    <p>宠物:{{ userInfo.pet }}</p>
  </div>
</template>

<script lang="ts" setup>
import { ref, reactive, watch } from "vue";

// 设置基本数据类型时,使用 ref
const title = ref<string>("响应式数据");

// 也可以根据设置的默认数据推导当前类型
let count = ref(0);

// 用户信息 --- reactive 用来绑定复杂数据类型
interface IUserInfo {
  name: string,
  pet: string | undefined,
  level: number
}
const userInfo = reactive<IUserInfo>({
  name: "zs",
  level: 0,
  pet: undefined,
})

// 使用 watch 监听 count 的变化
watch(count, (nl, od) => {
  if (nl >= 5) {
  userInfo.level = count.value;
  userInfo.pet = count.value >= 5 ? '马' : undefined; // 达到 5 级 解锁宠物
  }
})

// 升级
function handleAdd(): void {
  count.value++;
}
</script>

使用 computed

<template>
  <div>
    <h1>{{ title }}</h1>
    <p>
      当前等级:{{ count }}
      <button @click="handleAdd">升级</button>  
    </p>

    <p>宠物:{{ userInfo.pet }}</p>

    <p>{{ showAddictionPrevention }}</p>
  </div>
</template>

<script lang="ts" setup>
import { ref, reactive, watch, computed } from "vue";

// 设置基本数据类型时,使用 ref
const title = ref<string>("响应式数据");

// 也可以根据设置的默认数据推导当前类型
let count = ref(0);

// 用户信息 --- reactive 用来绑定复杂数据类型
interface IUserInfo {
  name: string,
  pet: string | undefined,
  level: number
}
const userInfo = reactive<IUserInfo>({
  name: "zs",
  level: 0,
  pet: undefined,
})

// 是否显示防沉迷
const showAddictionPrevention = computed(() => {
  if (count.value === 0) return "Welcome";
  if (count.value >= 5) return "登录时间过长";
  return '';
})

// watch: 监听一个属性;
watch(count, (nl, od) => {
  if (nl >= 5) {
    userInfo.level = count.value;
    userInfo.pet = count.value >= 5 ? '马' : undefined; // 达到 5 级 解锁宠物
  }
})

// 升级
function handleAdd(): void {
  count.value++;
}
</script>

使用生命周期

生命周期方法vue 结构后,它是一个函数,传入 callback 后即可执行。

<template>
  <div>
    <h1>{{ title }}</h1>
  </div>
</template>

<script lang="ts" setup>
import { ref, onMounted } from "vue";

// 设置基本数据类型时,使用 ref
const title = ref<string>("Props And Emits");

onMounted(() => {
  console.log("加载完成...");
});
</script>

props and emit

这是基于 ts 的写法,所以按照泛型的形式传入,如果不是 ts ,可以按照 vue 的语法进行实现。

<!-- Father.vue -->
<template>
  <div>
    <h1>{{ title }}</h1>
    <Child :count="count" @change="handleChange"/>
  </div>
</template>

<script lang="ts" setup>
import { ref, } from "vue";
// 子组件不需要注册,可以直接使用
import Child from "./Child.vue";

// 设置基本数据类型时,使用 ref
const title = ref<string>("Props And Emits");
const count = ref<number>(1);

function handleChange(val: number):void {
  count.value = val;
}
</script>

<!-- Child.vue -->
<template>
  <div>
    <h2>Child</h2>
    <p>{{ props.count }}</p>
    <button @click="handleAdd">add</button>
  </div>
</template>


<script lang="ts" setup>
import { defineProps, defineEmits } from "vue";

// 使用 
interface IProps {
  count: number
}
const props = defineProps<IProps>();

const emit = defineEmits<{
  (event: 'change', total: number): void
}>()


function handleAdd(): void {
  emit("change", props.count + 1);
}
</script>
### 项目布局实现 以下是基于 Vite、Vue3、Pinia、Vue-Router@4、TypeScript 和 `setup` 语法构建项目的具体方法: #### 1. 初始化项目并安装依赖 通过 Vite 创建一个新的 Vue3 项目,并指定 TypeScript 支持: ```bash npm create vite@latest my-project --template vue-ts cd my-project ``` 随后,安装必要的依赖项,包括 Vue Router v4 和 Pinia: ```bash npm install vue-router@4 pinia element-plus axios ``` #### 2. 配置 Element Plus 的按需加载 为了优化打包体积,推荐使用插件来实现 Element Plus 的按需导入。可以安装 `unplugin-vue-components` 和 `unplugin-auto-import` 插件: ```bash npm install unplugin-vue-components unplugin-auto-import -D ``` 在 `vite.config.ts` 中配置这些插件: ```typescript import { defineConfig } from &#39;vite&#39; import vue from &#39;@vitejs/plugin-vue&#39; import AutoImport from &#39;unplugin-auto-import/vite&#39; import Components from &#39;unplugin-vue-components/vite&#39; import { ElementPlusResolver } from &#39;unplugin-vue-components/resolvers&#39; export default defineConfig({ plugins: [ vue(), AutoImport({ resolvers: [ElementPlusResolver()], }), Components({ resolvers: [ElementPlusResolver()], }), ], }) ``` 这一步实现了 Element Plus 组件和 API 的自动导入[^1]。 #### 3. 设置路由管理器 创建 `src/router/index.ts` 文件以定义路由逻辑: ```typescript import { createRouter, createWebHistory } from &#39;vue-router&#39;; import routes from &#39;./routes&#39;; const router = createRouter({ history: createWebHistory(), routes, }); // 路由守卫:验证用户身份 router.beforeEach((to, from, next) => { const token = localStorage.getItem(&#39;token&#39;); const isAuthenticated = !!token; if (to.name === &#39;login&#39;) { localStorage.clear(); } if (to.name !== &#39;login&#39; && !isAuthenticated) { next({ name: &#39;login&#39; }); } else { next(); } }); export default router; ``` 同时,在同一目录下创建 `routes.ts` 定义具体的路由映射表: ```typescript import { RouteRecordRaw } from &#39;vue-router&#39;; const routes: Array<RouteRecordRaw> = [ { path: &#39;/&#39;, component: () => import(&#39;@/views/Home.vue&#39;), name: &#39;home&#39; }, { path: &#39;/login&#39;, component: () => import(&#39;@/views/Login.vue&#39;), name: &#39;login&#39; }, ]; export default routes; ``` #### 4. 整合状态管理工具 Pinia 初始化 Pinia 并将其挂载至应用实例中。编辑 `src/main.ts` 文件如下: ```typescript import { createApp } from &#39;vue&#39;; import App from &#39;./App.vue&#39;; import router from &#39;./router&#39;; import pinia from &#39;./stores&#39;; // 引入 Pinia 实例 import &#39;element-plus/dist/index.css&#39;; const app = createApp(App); app.use(pinia); app.use(router); app.mount(&#39;#app&#39;); ``` 接着,在 `src/stores/` 下创建存储模块文件(如 `userStore.ts`),用于管理全局状态: ```typescript import { defineStore } from &#39;pinia&#39;; export const useUserStore = defineStore(&#39;user&#39;, { state: () => ({ username: &#39;&#39;, isLoggedIn: false, }), actions: { login(username: string) { this.username = username; this.isLoggedIn = true; }, logout() { this.username = &#39;&#39;; this.isLoggedIn = false; }, }, }); ``` #### 5. 构建基础布局结构 修改 `src/App.vue` 来支持动态视图渲染以及顶部导航栏等功能: ```html <template> <div id="app"> <!-- 导航 --> <nav-bar /> <!-- 动态内容区域 --> <router-view></router-view> <!-- 底部版权信息 --> <footer-component /> </div> </template> <script setup lang="ts"> import NavBar from &#39;@/components/NavBar.vue&#39;; import FooterComponent from &#39;@/components/Footer.vue&#39;; </script> ``` #### 6. 在组件中使用路由与状态管理功能 假设有一个按钮点击事件触发跳转或者更新状态的操作,可以在任何组件内部这样写: ```typescript <script setup lang="ts"> import { useRouter, useRoute } from &#39;vue-router&#39;; import { useUserStore } from &#39;@/stores/userStore&#39;; const userStore = useUserStore(); const router = useRouter(); function handleLoginClick() { userStore.login(&#39;John Doe&#39;); // 更新 Pinia 状态 router.push(&#39;/dashboard&#39;); // 执行页面跳转 } </script> ``` 以上即完成了整个基于 Vite、Vue3、Pinia、Vue-Router@4、TypeScriptSetup 语法的项目布局设计[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值