vue3 - 图灵

目录

      • vue3简介
      • 整体上认识vue3项目
        • 创建Vue3工程
          • 使用官方脚手架创建Vue工程[推荐]
        • 主要⼯程结构
      • 数据双向绑定
        • vue2语法的双向绑定
          • 简单表单双向绑定
          • 复杂表单双向绑定
        • CompositionAPI替代OptionsAPI
          • CompositionAPI简单不带双向绑定写法
          • CompositionAPI简单带双向绑定写法
          • setup简写⽅式
          • 脚本单独写到ts⽂件中
        • Vue3中的数据双向绑定
          • ref定义基础类型响应式数据
          • reactive定义对象型响应式数据
          • ref也可以定义对象型响应式数据(不推荐)
          • 通过toRef或toRefs函数将转出来的数据具备响应式能力
          • 标签的ref属性/ref绑定标签
            • 通过ref将当前DOM元素绑定给响应式变量
            • 父组件拿到自定义组件里的值
            • 子组件接收父组件传来的数据
      • VUE3⽣命周期
      • Vue-Router组件路由机制
        • 基础使⽤
        • 路由⼯作模式
        • replace
        • 嵌套路由
        • 路由传参
          • query传参
          • params传参
      • Pinia集中式状态存储
        • 理解状态
        • 创建store
        • 使⽤store操作数据
        • storeToRefs声明响应式数据
        • store的混合式写法
      • 快速上⼿Element-Plus

vue3快速上手指南
你只要会基础的HTML,JS,CSS,那么就可以上手Vue了。如果你会Java,那么上手Vue非常轻松。如果你会Vue2,那么上手Vue3会更加舒服。

vue3简介

官网地址:https://vuejs.org/。中文官网 https://cn.vuejs.org/

Vue是什么?易学易用,性能出色,适用场景丰富的 Web 前端框架。

Vue2已经于2023年12月31日停止维护。建议升级到Vue.js3.0版本。打包更小,内存更少,渲染更快。好消息是,vue3向下兼容vue2的语法

Vue3于2020年9月18日发布,代号:One Piece 海贼王。久经磨砺

Vue3新特性:组合式API(重点),更好的支持TypeScript(熟悉),状态存储框架Pinia(重点),新组件(了解)。。。。。详见官网

整体上认识vue3项目

创建Vue3工程

前置:安装NodeJS。NodeJS版本最好在18.0以上。下载地址:https://nodejs.org/en
ps:lts:长期支持的版本

vite简介:类似于maven可以打包

1、所有功能组件都可以后续手动添加。
关于TypeScript,在Vue中的TypeScript可以认为是在JS的基础上,增加面向对象的能力。可以定义接口、类、抽象类等。

2、 npm install过程中会去node仓库下载很多依赖库,放到项目本地node-modules目录。建议将npm源设定为淘宝提供的国内镜像,可以下载快一点。

npm config get registry https://registry.npmmirror.com
使用官方脚手架创建Vue工程[推荐]

找一个空的文件夹

D:\WorkspaceOfIdea\vue3>npm create vue@latest
Need to install the following packages:
create-vue@3.10.3
Ok to proceed? (y) y

Vue.js - The Progressive JavaScript Framework

√ 请输入项目名称: ... vue-project
√ 是否使用 TypeScript 语法? ... 否 / 是√
√ 是否启用 JSX 支持? ... 否√ / 是
√ 是否引入 Vue Router 进行单页面应用开发? ... 否√ / 是
√ 是否引入 Pinia 用于状态管理? ... 否√ / 是
√ 是否引入 Vitest 用于单元测试? ... 否√ / 是
√ 是否要引入一款端到端(End to End)测试工具? » 不需要
√ 是否引入 ESLint 用于代码质量检测? ... 否 / 是√
√ 是否引入 Prettier 用于代码格式化? ... 否√ / 是
√ 是否引入 Vue DevTools 7 扩展用于调试? (试验阶段) ... 否√ / 是

正在初始化项目 D:\WorkspaceOfIdea\vue3\vue-project...

项目初始化完成,可执行以下命令:

  cd vue-project
  npm install
  npm run dev


D:\WorkspaceOfIdea\vue3>cd vue-project

D:\WorkspaceOfIdea\vue3\vue-project>npm install

D:\WorkspaceOfIdea\vue3\vue-project>npm run dev

> vue-project@0.0.0 dev
> vite


  VITE v5.2.11  ready in 702 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help
 

浏览器访问:http://localhost:5173/

主要⼯程结构

ps:官⽅建议开发IDE: vscode。提供了辅助开发插件 vue-official。 在这之前有个插件叫volar,现在已经停⽤

主要代码结构如下图
在这里插入图片描述
在这里插入图片描述

典型的Vue项⽬,都是在index.html这⼀个单⻚⾯⾥形成各种交互,这也就是所谓的SPA(Single Page 
Application)

Vue3的核⼼是通过createApp函数创建⼀个应⽤实例,在这个实例中构建各种应⽤。(main.ts中)

每个vue⽂件就是⼀个⻚⾯上的组件,组件可以嵌套使⽤。

vue中的组件分为<template>⻚⾯模板,<script>脚本和<style>样式三个部分。Vue2中要求<template>下必
须有⼀个唯⼀的根元素,Vue3中则没有了这个限制。

数据双向绑定

双向绑定是Vue最为核⼼的功能。简单理解就是<template>中的⻚⾯数据和<script>中的脚本数据进⾏绑定,其
中任何⼀个数据发⽣了变化,另⼀个数据也随之发⽣变化。
vue2语法的双向绑定
简单表单双向绑定

App.vue

<template>
  <div>
    姓名:<input v-model="userName" /> {{ userName }} <br />
    薪⽔:<input type="number" v-model="salary" />  {{ salary }} <br />
    <button @click="addSalary">提交</button>
  </div>
</template>
<script lang="ts">
export default {
    //数据
    data() {
      return {
        userName: "王⼀",
        salary: 15000
      }
    },
    //⽅法
    methods: {
      addSalary() {
        this.salary += 1000
      }
    }
  }
</script>
<style scoped>
</style>
复杂表单双向绑定

数据双向绑定可以说是整个Vue的核⼼。例如,我们可以⽤数据双向绑定实现⼀些更为复杂的表单。
App.vue

<template>
  <div>
    姓名:<input v-model="userName" /> {{ userName }} <br />
    薪⽔:<input type="number" v-model="salary" /> {{ salary }} <br />
    <button @click="addSalary">提交</button> <button @click="changeShowUserInfo">查看个⼈信息</button>

    <hr />
    <div class="userInfo" v-show="showUserInfo">
      <h2>个⼈信息</h2>
      <p>年龄:<input type="number" v-model="userInfo.age" /></p>
      <p>性别:<input type="radio" value="1" v-model="userInfo.sex"><input type="radio" value="2" v-model="userInfo.sex"></p>
      <p>岗位:<select v-model="userInfo.department">
          <option value="dev">开发</option>
          <option value="test">测试</option>
          <option value="maintain">运维</option>
        </select></p>
      <p>技术: <span v-for="skill in userInfo.skills" :key="skill">{{ skill }}</span></p>
      <p>新技术: <input v-model="newSkill" /> <button @click="learnNewSkill">学习新技术</button></p>
      <p>个⼈信息汇总:{{ userInfo }}</p>
    </div>
  </div>

</template>

<script lang="ts">
export default {
  data() {
    return {
      userName: 'roy',
      salary: 15000,
      userInfo: {
        age: 0,
        sex: 1,
        skills: ['java', 'vue', 'python'],
        department: ''
      },
      newSkill: '',
      showUserInfo: false
    }
  },
  methods: {
    addSalary() {
      this.salary += 1000
    },
    learnNewSkill() {
      if (this.newSkill)
        this.userInfo.skills.push(this.newSkill)
    },
    changeShowUserInfo() {
      this.showUserInfo = !this.showUserInfo
    }
  }
}
</script>

<style scoped>
.userInfo {
  background-color: bisque;
  width: 80%;
}

.userInfo span {
  background-color: yellow;
  margin-left: 10px;
  border: 1px;
  border-radius: 5px;
}
</style>

在这里插入图片描述

CompositionAPI替代OptionsAPI

Vue2中常⽤的这种编写⽅式称为OptionsAPI,配置式。其实现⽅式是⽤⼀个统⼀的配置对象来实现全部代码逻辑。在这个对象中,通过data、methods、computed等配置选项来控制逻辑。

OptionsAPI是Vue2时的标准API编写⽅式。Vue3向下兼容了Vue2的API。因此,Vue2的⽼项⽬,在Vue3中基本可以⽆缝迁移。 实际上,OptionsAPI是在CompositionAPI的基础上实现的。关于Vue的基础概念和知识,在这两种API之间是通⽤的 。另外,官⽅建议,如果采⽤Vue构建完整的SPA应⽤,那么更建议使⽤CompositionAPI。

但是,OptionsAPI所有逻辑都混在⼀起,不便于维护和复⽤。 Vue3另外通过了⼀种更⽅便的API,Composition API,混合式API。

上⾯同样的示例,⽤Composition API的写法如下:

CompositionAPI简单不带双向绑定写法
<template>
  <div>
    姓名:<input v-model="userName" /> {{ userName }} <br />
    薪⽔:<input type="number" v-model="salary" /> {{ salary }} <br />
    <button @click="addSalary">提交</button>
  </div>
</template>

<script lang="ts">
  export default {
    setup() {
      //现在声明的变量还不具备双向绑定
      let userName = "王⼀"
      let salary = 15000
      function addSalary() {
        salary += 1000
        console.log("salary = " + salary)
      }
      //模板要⽤哪些,就返回哪些
      return { userName, salary, addSalary }
    }
  }
</script>

<style scoped>
</style>
1、setup是Vue3中的⼀个⽣命周期函数,他会在组件加载时执⾏。后⾯会细讲⽣命周期。
2、setup可以返回对象或者函数。如果是⼀个对象,则对象中的属性、⽅法等,可以在模板中直接使⽤(常
⽤)。如果返回⼀个函数,则通过函数的返回值直接渲染⻚⾯,不经过模板。例如 setup(){return ()=>"直
接渲染"}
3、setup是⼀个普通的函数,不能使⽤this。 OptionsAPI中可以通过this访问脚本本身的数据 同时 setup中
不处理this,意味着setup编写可以更灵活,不需要依赖当前⻚⾯上下⽂
4、此时声明的userName, salary等变量不具备双向绑定。Vue3对双向绑定做了重新设计,后⾯会详细分
享。
CompositionAPI简单带双向绑定写法
<template>
  <div>
    姓名:<input v-model="userName" /> {{ userName }} <br />
    薪⽔:<input type="number" v-model="salary" /> {{ salary }} <br />
    <button @click="addSalary">提交</button>
  </div>
</template>

<script lang="ts">
import { ref } from 'vue';
  export default {
    setup() {
      //现在声明的变量具备了双向绑定
      let userName = ref("王⼀")
      let salary = ref(15000)
      function addSalary() {
        salary.value += 1000
        console.log("salary = " + salary)
      }
      //模板要⽤哪些,就返回哪些
      return { userName, salary, addSalary }
    }
  }
</script>

<style scoped>
</style>
setup简写⽅式
5、setup有⼀种简写的⽅式<script setup lang="ts">。这样就不需要写函数了,标签内部直接写函数体。在
标签内部声明的对象,函数等,都会直接return出去。 项⽬中常⽤
<template>
  <div>
    姓名:<input v-model="userName" /> {{ userName }} <br />
    薪⽔:<input type="number" v-model="salary" /> {{ salary }} <br />
    <button @click="addSalary">提交</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
    let userName = ref("王⼀")
    let salary = ref(15000)
    function addSalary() {
      salary.value += 1000
      console.log("salary = " + salary)
    }
</script>

<style scoped>
</style>

在CompositionAPI中,由于setup是⼀个不同的函数,不需要处理this。这也意味着setup函数编写可以更加灵活,不需要依赖当前⻚⾯上下⽂。例如:将示例中的脚本单独写到⼀个ts⽂件中。

脚本单独写到ts⽂件中

component文件夹下新建MySalary.ts

import { onMounted, ref } from "vue"
export default function () {
    //之前声明的变量还不具备双向绑定。现在添加ref函数才具备了响应式
    const userName = ref("王⼀")
    const salary = ref(15000)
    function addSalary() {
        salary.value += 1000
        console.log("salary = " + salary.value)
    }
    onMounted(() => {
        console.log("加载了外部脚本")
    });
    return { userName, salary, addSalary }
}

然后,在App.vue中就可以直接引⽤脚本

<template>
  <div>
    姓名:<input v-model="userName" /> {{ userName }} <br />
    薪⽔:<input type="number" v-model="salary" /> {{ salary }} <br />
    <button @click="addSalary">提交</button>
  </div>
</template>

<script setup lang="ts">
import MySalary from './components/MySalary';
let {userName,salary,addSalary} = MySalary()
</script>

<style scoped>
</style>

如果App.vue的逻辑越来越复杂,通过这种⽅式,就更易于将相关的属性和⽅法整理到⼀起,从⽽实现⼀个特定的业务功能。

1、ref函数让变量具备了双向绑定功能。后⾯详细分析。
2、复杂⻚⾯可以⽤这种⽅式。⼀般情况下,显然是将MySalary的模板和脚本封装到⼀起,这就是⾃定义组
件了。
Vue3中的数据双向绑定

ref包裹后可以在模板中直接使用,在js中要加上.value,因为ref包裹后是一个RefImpl对象
ref先后用不同值包裹两次后,value值改变,但没有和控件建立绑定关系,原来控件上的旧值也失去了响应式能力;改了别的ref后vue会重新扫描所有的ref,控件会和之前的ref重新绑定

ref定义基础类型响应式数据
语法: let userName=ref(初始值)。
返回值:⼀个RefImpl的实例对象,值被包裹在对象的value属性中。
注意点:
	脚本中要⽤ref对象的value属性访问值,例如userName.value。但是模板中可以直接⽤。
	ref对象本身不是响应式的,value属性是响应式的。例如js中修改值,要通过userName.value="xxx",
	⽽不能userName="xxx"。
	vue-official插件中可以选择⾃动添加value属性。(需要⼿动勾选)
<template>
  <div>
    姓名:<input v-model="userName" /> {{ userName }} <br />
    薪⽔:<input type="number" v-model="salary" /> {{ salary }} <br />
    <button @click="addSalary">提交</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
    let userName = ref("王⼀")	//基础类型⽤ref声明响应式
    let salary = ref(15000)
    function addSalary() {
      salary.value += 1000		 //脚本中操作数据要加.value
      console.log("salary = " + salary)
    }
</script>

<style scoped>
</style>
reactive定义对象型响应式数据

语法: let salaryInfo = reactive({userName:“王⼀”,salary:15000})
返回值:reactive包裹后变成了⼀个Proxy实例对象,具有双向绑定能⼒。

<template>
  <div>
    姓名:<input v-model="salaryInfo.userName" /> {{ salaryInfo.userName }} <br />
    薪⽔:<input type="number" v-model="salaryInfo.salary" /> {{ salaryInfo.salary }} <br />
    <button @click="addSalary">提交</button>
  </div>
</template>

<script setup lang="ts">
import { reactive } from 'vue';
    let salaryInfo = reactive({userName:"roy",salary:10000})
    function addSalary() {
      salaryInfo.salary += 1000
      console.log("salary = " + salaryInfo.salary)
    }
</script>

<style scoped>
</style>
ref也可以定义对象型响应式数据(不推荐)

ref包裹完对象类型数据后封装成reactive包裹的对象,对象里拆出来的属性不会有双向绑定能力

<template>
  <div>
    姓名:<input v-model="salaryInfo.userName" /> {{ salaryInfo.userName }} <br />
    薪⽔:<input type="number" v-model="salaryInfo.salary" /> {{ salaryInfo.salary }} <br />
    <button @click="addSalary">提交</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
    let salaryInfo = ref({userName:"roy",salary:10000})
    function addSalary() {
      salaryInfo.value.salary += 1000
      console.log("salary = " + salaryInfo.value.salary)
    }
</script>

<style scoped>
</style>
通过toRef或toRefs函数将转出来的数据具备响应式能力

对象型响应数据,如果将其中的各个属性拆解出来,是不具备响应式的。如果需要响应式属性,可以使⽤toRefs或者toRef函数进⾏转换

将对象的属性换成ref包裹的属性

<template>
  <div>
    姓名:<input v-model="userName" /> {{ userName }} <br />
    薪⽔:<input type="number" v-model="salary" /> {{ salary }} <br />
    <button @click="addSalary">提交</button>
  </div>
</template>

<script setup lang="ts">
import { reactive, toRef } from 'vue';
    let salaryInfo = reactive({userName:"roy",salary:10000})
    // toRef将对象的某个属性转为⼀个响应式数据
    let userName = toRef(salaryInfo,'userName')
    let salary = toRef(salaryInfo,'salary')
    function addSalary() {
      salaryInfo.salary += 1000
      console.log(salaryInfo)
    }
</script>

<style scoped>
</style>
<template>
  <div>
    姓名:<input v-model="userName" /> {{ userName }} <br />
    薪⽔:<input type="number" v-model="salary" /> {{ salary }} <br />
    <button @click="addSalary">提交</button>
  </div>
</template>

<script setup lang="ts">
import { reactive, toRef, toRefs } from 'vue';
    let salaryInfo = reactive({userName:"roy",salary:10000})
    // toRefs将对象的所有属性⼀起转换成响应式数据
    let {userName,salary} = toRefs(salaryInfo)
    function addSalary() {
      salaryInfo.salary += 1000
      console.log(salaryInfo)
    }
</script>

<style scoped>
</style>
标签的ref属性/ref绑定标签

ref不仅可以通过v-model的形式形成双向绑定,也可以在元素上加上ref
在定义模板时,可以通过ref属性将当前DOM元素绑定给响应式变量。

通过ref将当前DOM元素绑定给响应式变量
<template>
  <div>
    姓名:<input ref="name" abc="aaaaa" /> <br />
    <button @click="showRes">提交</button>
  </div>
</template>

<script setup lang="ts">
import { reactive, toRef, toRefs, ref } from 'vue';
  let name = ref()
  function showRes(){
    console.log(name) //RefImpl ref对象
    console.log(name.value) //<input> dom元素
    console.log(name.value.value) //输⼊框的值
    console.log(name.value.getAttribute("abc")) //⾃定义属性的值
  }
</script>

<style scoped>
</style>
父组件拿到自定义组件里的值

如果只是针对普通元素,还体现不出Ref的作⽤。如果配合⾃定义组件,则更能体现Ref属性的作⽤。例如,针对薪⽔信息,可以⾃⼰写⼀个简单组件,把多个输⼊框整合到⼀起。

components下新建MySalaryInfo.vue

<!-- ⾃定义的薪⽔信息输⼊组件 -->
<template>
    姓名:<input v-model="userName"><br />
    薪⽔:<input type="number" v-model="salary">
</template>

<script lang="ts">
//组件名默认是⽂件名。如果不希望⽤⽂件名,也可以⾃定义
export default {
    name: "SalaryInfo"
}
</script>

<script setup lang="ts">
import { ref } from 'vue';
    //响应式数据默认值
    let userName = ref("unknown")
    let salary = ref(1000)
    //子组件对外暴露属性。只有暴露出去,组件外部才能访问
    defineExpose({ userName, salary })
</script>

<style></style>

App.vue

<template>
  <MySalaryInfo ref="salaryInfo" /><button @click="showRes">查看薪⽔信息</button>		<!--2-->
</template>

<script setup lang="ts">
import { reactive, toRef, toRefs, ref } from 'vue';
//引⼊⼦组件
import MySalaryInfo from '@/components/MySalaryInfo.vue';		//1
//获取绑定对象
let salaryInfo = ref()										 //3
function showRes() {
  console.log(salaryInfo) //RefImpl ref对象					 //4
  console.log(salaryInfo.value) //Proxy ⼦组件的响应式数据
  console.log(salaryInfo.value.userName) //⼦组件的输⼊框的值
  console.log(salaryInfo.value.salary)	 //⼦组件的输⼊框的值
}
</script>

<style scoped></style>
子组件接收父组件传来的数据

MySalaryInfo.vue
接收值

<!-- ⾃定义的薪⽔信息输⼊组件 -->
<template>
    姓名:<input v-model="salaryInfo.userName"><br />
    薪⽔:<input type="number" v-model="salaryInfo.salary"><br />
    {{ salaryInfo }}
</template>

<script lang="ts">
//组件名默认是⽂件名。如果不希望⽤⽂件名,也可以⾃定义
export default {
    name: "SalaryInfo"
}
</script>

<script setup lang="ts">
defineProps([               //可以接收外面传进来的值
    "salaryInfo"
])
</script>

<style>
</style>

App.vue
父组件修改值,然后往子组件传值

<template>
  <div>
    <MySalaryInfo :salary-info="salaryInfo"></MySalaryInfo>
    <button @click="showRes">修改薪⽔</button>
  </div>
</template>

<script setup lang="ts">
import { reactive, toRef, toRefs, ref } from 'vue';
//引⼊⼦组件
import MySalaryInfo from '@/components/MySalaryInfo.vue';
//获取绑定对象
let salaryInfo = reactive({ userName: 'roy', salary: 10000 })
function showRes() {
  salaryInfo.salary += 1000
}
</script>

<style scoped></style>

在这里插入图片描述
TypeScript版:
但是上面的salaryInfo传到子组件时子组件并不知道它是什么类型,也不知道它有什么属性,可以引入ts对salaryInfo类型做限制
src下新建types文件夹和salaryInfo.ts

export interface SalaryInfo{
    userName:string,
    salary:number
}

子组件引入和定义泛型
MySalaryInfo.vue

<!-- ⾃定义的薪⽔信息输⼊组件 -->
<template>
    姓名:<input v-model="salaryInfo.userName"><br />
    薪⽔:<input type="number" v-model="salaryInfo.salary"><br />
    {{ salaryInfo }}
</template>

<script lang="ts">
//组件名默认是⽂件名。如果不希望⽤⽂件名,也可以⾃定义
export default {
    name: "SalaryInfo"
}
</script>

<script setup lang="ts">
import type { SalaryInfo } from '@/types/salaryInfo';

defineProps<{salaryInfo:"SalaryInfo"}>(              //接收外面传进来的值
)
</script>

<style>
</style>

VUE3⽣命周期

每个 Vue 组件实例在创建时都需要经历⼀系列的初始化步骤,⽐如设置好数据侦听,编译模板,挂载实例DOM,以及在数据改变时更新 DOM。在此过程中,它也会运⾏被称为⽣命周期钩⼦的函数,让开发者有机会在特定阶段运⾏⾃⼰的代码。

⽣命周期有四个阶段:创建,挂载,更新,销毁。每个阶段有⼀前⼀后两个函数

OptionsAPI的⽣命周期函数:

创建阶段: beforeCreate 、 created
挂载阶段: beforeMount 、 mounted
更新阶段: beforeUpdate 、 updated
销毁阶段: beforeDestroy 、 destroyed

CompositionAPI的⽣命周期函数:

创建阶段: setup
挂载阶段: onBeforeMount 、 onMounted
更新阶段: onBeforeUpdate 、 onUpdated
卸载阶段: onBeforeUnmount 、 onUnmounted

示例

<template>
    <div>
        薪⽔:<input type="number" v-model="salary" /> <br />
        <button @click="addsum">薪⽔+1000</button>
    </div>
</template>

<!-- vue3写法 -->
<script lang="ts" setup>
import {
    ref,
    onBeforeMount,
    onMounted,
    onBeforeUpdate,
    onUpdated,
    onBeforeUnmount,
    onUnmounted
} from 'vue'
// 数据
let salary = ref(0)
// ⽅法
function addsum() {
    salary.value += 1000
}
console.log('setup')
// ⽣命周期钩⼦
onBeforeMount(() => {
    console.log('挂载之前')
})
onMounted(() => {
    console.log('挂载完毕')
})
onBeforeUpdate(() => {
    console.log('更新之前')
})
onUpdated(() => {
    console.log('更新完毕')
})
onBeforeUnmount(() => {
    console.log('卸载之前')
})
onUnmounted(() => {
    console.log('卸载完毕')
})
</script>

Vue-Router组件路由机制

Vue项⽬虽然只有index.html⼀个⻚⾯,但是可以通过多路由机制实现多⻚⾯跳转的效果。访问不同链接,展示不同的⻚⾯内容,形成多⻚⾯的效果。
Vue官⽅提供了Vue-Router组件实现路由管理,官⽹地址:https://router.vuejs.org/zh/ 。该组件可以在创建Vue项⽬时选择引⼊。如果创建时没有安装,也可以⼿动安装。

npm install vue-router@4

vue3要求使⽤router组件最新版本。⽬前最新版本是4

基础使⽤

页面准备
HomePage.vue,AboutPage.vue,NewsPage.vue

<template>
        首页
</template>

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

<style>
</style>

main.ts

import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'

import { createRouter,createWebHistory } from "vue-router"
import HomePage from './pages/HomePage.vue'
import AboutPage from './pages/AboutPage.vue'
import NewsPage from './pages/NewsPage.vue'

// 1.配置路由规则
const routes = [
    { path: '/',redirect: '/home'}, //默认跳转到⾸⻚
    { path: '/home', component: HomePage }, 
    { path: '/about', component: AboutPage }, //命名路由
    { path: '/news', component: NewsPage, name:'news' },
]

// 2.创建路由器
const router = createRouter({
    history: createWebHistory(),//路由器⼯作模式
    routes,
})

// 3.加载路由器
// createApp(App).mount('#app')
const app = createApp(App)
//加载路由器
app.use(router)
app.mount('#app')

App.vue

<template>
  <div id="app">
    <h1>Hello App!</h1>
    <p>
      <!-- 路由链接-->
      <router-link to="/home">⾸⻚</router-link> <!-- 直接字符串跳转 -->
      <router-link :to="{ path: '/about' }">关于</router-link> <!-- 对象跳转 -->
      <router-link :to="{ name: 'news' }">新闻</router-link> <!-- 具名跳转 -->
    </p>
    <div class="content">
      <router-view />   <!-- 路由出⼝,路由匹配到的组件将渲染在这⾥ -->
    </div>
  </div>
</template>

<!-- vue3写法 -->
<script lang="ts" setup>
</script>

<style>
a {
  margin: 10px;
}
.content {
  background: yellowgreen;
  widows: 10%;
  height: 400px;
  border: 1cap;
  border-radius: 10px;
}
</style>
路由⼯作模式

在router配置中的history项为路由⼯作模式。Vue提供了两种⼯作模式:

history模式
访问路径:URL不带#,斜杠链接,接近传统⽹站。缺点:容易产⽣404错误。

const router = createRouter({
 history:createWebHistory(), //history模式
 /******/
})

hash模式
访问路径:URL带有#。缺点:对SEO不太友好。⽐较适合内部系统。

const router = createRouter({
 history:createWebHashHistory(), //hash模式
 /******/
})
replace

route-link标签可以添加replace属性。有两种可选配置: push和replace
push 追加浏览器历史记录(默认值)。追加历史记录后,可以使⽤浏览器的返回按钮,跳回历史⻚
replace 替换浏览器历史记录。替换历史记录后,浏览器的返回按钮不可⽤。
在这里插入图片描述

嵌套路由

页面准备
NewsDetail1.vue,NewsDetail2.vue

<!-- NewsDetail1.vue -->
<template>
    <p>新闻ID: 1</p>
    <p>新闻标题: 1 </p>
    <p>新闻内容: 1 </p>
</template>
<script lang="ts" setup>
</script>
<style></style>

main.ts

import NewsDetail1 from './pages/NewsDetail1.vue'
import NewsDetail2 from './pages/NewsDetail2.vue'

const routes = [
    { path: '/', redirect: '/home' }, //默认跳转到⾸⻚
    { path: '/home', component: HomePage },
    { path: '/about', component: AboutPage }, //命名路由
    {
        path: '/news',
        component: NewsPage,
        name: 'news',
        children: [ //⼦路由
            {
                path: "1",
                component: NewsDetail1,
                name: "xinwen1"
            },
            {
                path: "2",
                component: NewsDetail2,
                name: "xinwen2"
            }
        ]
    },
]

NewsPage.vue

<template>
    <div class="news">
        <!-- 导航区 -->
        <ul>
            <li>
                <RouterLink to="/news/1">新闻1</RouterLink>
            </li>
            <li>
                <RouterLink to="/news/2">新闻2</RouterLink>
            </li>
        </ul>
        <!-- 展示区 -->
        <div class="news-content">
            <RouterView></RouterView>
        </div>
    </div>
</template>

这样就实现了新闻⻚内的嵌套路由。点击新闻标题,会跳到对应的新闻详情⻚。

路由传参

上⾯的示例显然太呆板,现实的场景当然是希望查出⼀个完整的新闻列表,然后每个新闻⻚都是展示新闻列表中的内容,⽽不是每个组件内固定的内容。这也就需要进⾏路由传参,也就是NewsDetail中的内容是从新闻列表中传递进来的。

Vue3中提供了两种传参⽅式,query传参和param传参。

query传参

NewsPage.vue传参

<!-- 字符串传参 -->
<router-link to="/news/1?id=1&title=新闻1&content=asdfasdf">新闻1</RouterLink>
             
<!-- 对象传参 -->
<RouterLink
    :to="{
        path:'/news/1',
        query:{
            id:'1',
            title:'新闻1',
            content:'asdfasdf'
        }
    }">
 	新闻1
</RouterLink>

NewsDetail.vue接收参数

<!-- NewsDetail1.vue -->
<template>
    <p>新闻ID: {{ query.id }}</p>
    <p>新闻标题:{{ query.title }}</p>
    <p>新闻内容:{{ query.content }} </p>
</template>
<script lang="ts" setup>
import { useRoute } from 'vue-router'
import { toRef } from 'vue'
let route = useRoute()
// 获取和打印query参数
console.log(route.query)
// 双向绑定数据
let query = toRef(route, 'query')
</script>
<style></style>

在这里插入图片描述

params传参

params传参⽅式表示所有参数都拼接到URL上。
⾸先需要在route配置中预设占位符

main.ts

{ 
    path: '/news', component: NewsPage, name:'news',
    children:[ //⼦路由
     {
        path: "1",
        component: NewsDetail1
     },
     {
        name:'xinwen2',						// 对应params传参方式2
        path: "2/:id/:title/:content",		// Param传参,URL预设占位符,?表示参数可有可没有
        component: NewsDetail2
     }
   ]
},

然后,传参时,在RouteLink中直接传到预设的URL,或者⽤name属性指定⽬标。
NewsPage.vue

<!-- params传参方式1 -->
<RouterLink to="/news/2/2/新闻2/qowuieoiurr">param路径传参</RouterLink>
<!-- params传参方式2 -->
<RouterLink
    :to="{
        name:'xinwen2',				<!-- main.ts里也要加name属性 -->
        params:{
            id:2,
            title :'新闻2',
            content :'qowiueoiqu'
        }
    }">
     param对象传参
</RouterLink>

接下来NewsDetail2.vue中通过路由的params属性接收参数

<!-- NewsDetail2.vue -->
<template>
    <p>新闻ID: {{ params.id }}</p>
    <p>新闻标题:{{ params.title }}</p>
    <p>新闻内容:{{ params.content }} </p>
</template>
<script lang="ts" setup>
import { useRoute } from 'vue-router'
import { toRef } from 'vue'
let route = useRoute()
// 接收并打印query参数
console.log(route.params)
// 双向绑定数据
let params = toRef(route, 'params')
</script>
<style></style>

Pinia集中式状态存储

理解状态

在任意Vue⻚⾯之间共享的存储数据。简单理解:在当前Vue项⽬中使⽤的MySQL数据库。例如登录信息,只要完成了登录,所有Vue⻚⾯都能读取到当前登录⽤户。

Vue2中提供的集中状态存储框架是Vuex,Vue3中新提供了Pinia。如果你使⽤的还是Vue2,那么主要下,Vuex和Pinia不能⼀起使⽤。

创建store

Pinia可以在创建应⽤时选择引⼊。如果创建时没有引⼊,那就需要⼿动引⼊⼀下。

npm install pinia

Pinia的使⽤⽅式和Route组件基本相似。需要在启动的ts⽂件中使⽤use函数引⼊。
main.ts

import {createPinia} from 'pinia'
//加载pinia
const pinia = createPinia()
app.use(pinia)

接下来使⽤pinia需要创建Store。⼀个Store可以理解为MySQL中的⼀个库,保存⼀部分数据。Pinia的Store中有三个概念: state,getter , action。这三个概念也可以类⽐于熟悉的MVC。state相当于是数据;getter相当于是服务,⽤来获取并返回数据;action相当于Controller,组织业务逻辑。

创建定义store的⽂件 store/user.ts

import { defineStore } from 'pinia'

export const userStore = defineStore('userStore', {
    //action封装修改state的业务动作
    actions: {
        changeUsername(value: string) {
            if (value && value.length < 10) {
                this.username += value
            }
        }
    },
    //getters读取state的计算值
    getters: {
        getUsername(): string {
            return this.username.toUpperCase()
        }
    },
    //state定义要保存的数据结构
    state() {
        return {
            //给定默认值
            username: '--'
        }
    }
})
使⽤store操作数据

App.vue中修改stroe的数据

<template>
  <div id="app">
    <h1>Hello App!</h1>
  </div>
</template>

<script lang="ts" setup>
//获取store
import { userStore } from '@/store/User';
const user = userStore()
//修改store中的值
//1、直接修改某⼀个state
user.username = 'roy'
//2、批量修改完整的state
user.$patch({
  username: 'roy2'
})
//3、通过action进⾏修改 推荐⽅式
user.changeUsername('roy')
console.log(user.username)
console.log(user.getUsername)
</script>

<style></style>

pinia的使⽤⼏乎没有⻔槛,相⽐vuex要简单很多,所以官⽅对Pinia的定义是符合直觉的状态管理库。因此,在使⽤pinia时,更应该是注意使⽤规范。

storeToRefs声明响应式数据
<script lang="ts" setup>
//获取store
import { userStore } from '@/store/User';
import { storeToRefs } from "pinia";
import { toRefs } from "vue";

const user = userStore()

//storeToRefs转换后只有username和getUsername
let userInfo = storeToRefs(user)
console.log(userInfo)
//toRefs转换后包含了很多隐藏⽅法和属性,⽐如$patch
let userInfo2 = toRefs(user)
console.log(userInfo2)

</script>
store的混合式写法

store也有⼀种混合式的写法,将各种组件混合到⼀起。

import { defineStore } from 'pinia'
import { reactive } from 'vue'
export const userStore = defineStore('userStore',()=>{
    //相当于是state
    const userInfo = reactive({username:"---"})
    //相当于action
    function changeUsername(value:string){
        if(value && value.length<10){
        	userInfo.username = value
         }
    }
    //相当于getters
    function getUsername():string{
    	return userInfo.username.toUpperCase()
    }
    //不⽤区分什么类型,返回出去的就可以⽤
    return {userInfo,changeUsername,getUsername}
})

在App.vue中,也可以像使⽤普通对象⼀样,使⽤store中的⽅法和对象。

<template>
    <div id="app">
    <!-- 注意对象拆包过程 -->
    <h1>Hello {{ res.userInfo.value.username }}</h1>
</div>
</template>
<!-- vue3写法 -->
<script lang="ts" setup >
//获取store
import { userStore } from "@/store/user2";
import { storeToRefs } from "pinia";
const user = userStore()
//修改store中的值
//通过action进⾏修改 推荐⽅式
user.changeUsername('roy')
// 获取store中的数据
console.log(user.userInfo)
// 通过getter获取state数据 推荐⽅式
console.log(user.getUsername())
//混合式store转成Ref后,只有数据的ref
let res = storeToRefs(user)
console.log(res)
</script>
<style>
</style>

这种⽅式相当于在做MVC开发时,将Controller\Service\Dao这些组件写到⼀起。
复杂项⽬当中,不太建议这样⽤。但是如果别⼈这么⽤了,你要能看懂。

快速上⼿Element-Plus

ElementUI是饿了么开源的⼀套基于Vue2的经典UI库。针对Vue3,升级成为了ElementPlus。熟悉ElementPlus库,不但可以节省⼤量前端项⽬的开发时间,同时也是深⼊了解Vue3复杂组件开发的很好途径。

ElementPlus官⽹地址:https://element-plus.org/zh-CN/ 。 ⽬前还在迭代更新过程当中。

1、安装ElementPlus

npm install element-plus --save

2、引⼊ElementPlus
main.ts

import { createApp } from 'vue'
import App from './App.vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')

3、使⽤ElementPlus组件 参⻅官⽅⽂档。

<template>
    <div class="mb-4">
        <el-button>Default</el-button>
        <el-button type="primary">Primary</el-button>
        <el-button type="success">Success</el-button>
        <el-button type="info">Info</el-button>
        <el-button type="warning">Warning</el-button>
        <el-button type="danger">Danger</el-button>
    </div>
</template>
<!-- vue3写法 -->
<script lang="ts" setup >
import { ElButton } from 'element-plus';
</script>
<style>
</style>

或者,你也可以直接使⽤element-plus提供的Demo:https://github.com/element-plus/element-plus-vite-starter 。 ⾥⾯有更多现成的案例。

ElementUI针对Vue2还推出过⼀个vue-admin模版,⾥⾯案例更丰富,集成度也更⾼。很多企业内部项⽬都可以直接拿来⽤。有兴趣可以了解⼀下。⽽针对Vue3,只推出了⼀个将ElementUI从Vue2升级到Vue3的迁移⼯具,尚未提供Vue3的版本。

类似的UI框架还有很多,给⼤家例举⼏个常⽤的
Ant Design Vue(https://www.antdv.com/docs/vue/getting-started-cn) 经典⽼框架
Native UI(https://www.naiveui.com/zh-CN/light) 仅⽀持Vue3的⼀个新的UI库
Tdesign(https://tdesign.tencent.com/) 腾讯开源的前端UI框架 包含桌⾯与移动端
NutUI(https://nutui.jd.com/#/) 京东开源的前端UI框架
uvuewui(https://www.uviewui.com/) 适合移动端uni-app开发

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/609333.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

链表的中间结点(C语言)———链表经典算法题

题目描述. - 力扣&#xff08;LeetCode&#xff09;&#xff1a; ​ 答案展示: /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* middleNode(struct ListNode* head) {struct ListNode* fast,…

商业写字楼如何选择停车场管理系统?停车场管理系统建设有哪些注意事项

在现代商业环境中&#xff0c;写字楼停车场的高效管理对于维护企业形象、提高员工满意度以及增强客户体验至关重要。写字楼停车场管理的特点主要包括高流量、高周转率、多样化的车辆类型、高安全性要求以及对客户体验的重视&#xff0c;那么商业写字楼停车场应该从哪些方面提升…

【计网】TCP中的滑动窗口

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;日常聊聊 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 正文 工作原理如下&#xff1a; 结语 我的其他博客 正文 TCP&#xff08;传输控制协议&#xff09;中的滑动窗口是一种用于流量控制和拥…

[Kubernetes] sealos部署 K8s 集群

文章目录 1.sealos 介绍2.操作系统基础配置3.安装部署 K8s4.验证 K8s 集群5.部署测试资源 1.sealos 介绍 Sealos 是一个基于 Kubernetes 内核的云操作系统发行版。它采用云原生方式&#xff0c;摒弃传统的云计算架构&#xff0c;转向以 Kubernetes 为云内核的新架构。这使得企…

30万奖励!2023-2024年度成都市工业企业“小升规”申报对象奖补、材料程序

一、申报对象及奖励标准 &#xff08;一&#xff09;2023年度申报对象及奖励标准。2022年3月1日至2022年12月31日首次进入成都市规模以上工业名录库的企业。2022年营业收入在2000万元&#xff08;含&#xff09;至5000万元的&#xff0c;给予10万元奖励&#xff1b;2022年营业…

4. 分布式链路追踪客户端工具包Starter设计

前言 本文将从零搭建分布式链路追踪客户端工具包的Starter&#xff0c;并将在后续文章中逐步丰富支持的场景。这里首先将搭建一个最基础的Starter&#xff0c;能提供的功能和1. 看完这篇文章我奶奶都懂Opentracing了一文中的示例demo类似。 相关版本依赖如下。 opentracing-…

大模型LLM之SFT微调总结

一. SFT微调是什么 在大模型的加持下现有的语义理解系统的效果有一个质的飞跃&#xff1b;相对于之前的有监督的Pre-Train模型&#xff1b;大模型在某些特定的任务中碾压式的超过传统nlp效果&#xff1b;由于常见的大模型参数量巨大&#xff1b;在实际工作中很难直接对大模型训…

视频剪辑批量转码技巧:如何将MP4视频快速转换为MP3音频的方法

在视频剪辑和音频处理的领域中&#xff0c;经常需要将视频文件转换为音频文件&#xff0c;特别是将MP4视频转换为MP3音频。这样的转换不仅可以减少文件大小&#xff0c;方便传输和存储&#xff0c;还可以在不损失音频质量的情况下&#xff0c;方便在各种设备上播放。下面&#…

AI写的论文AI疑似度太高怎么办?教你一招降低aigc痕迹

随着 AI 技术迅猛发展&#xff0c;各种AI辅助论文写作的工具层出不穷&#xff01; 为了防止有人利用AI工具进行论文代写&#xff0c;在最新的学位法中已经明确规定“已经获得学位者&#xff0c;在获得该学位过程中如有人工智能代写等学术不端行为&#xff0c;经学位评定委员会…

【随笔】Git 高级篇 -- 不带 source 参数的命令 git fetch git push(三十九)

&#x1f48c; 所属专栏&#xff1a;【Git】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x1f496; 欢迎大…

电脑硬盘故障,这5种情况要了解!

在数字化时代&#xff0c;电脑硬盘作为存储数据的重要设备&#xff0c;其稳定性和安全性直接关系到用户的数据安全和工作效率。然而&#xff0c;硬盘故障却是一个无法完全避免的问题。为什么会出现电脑硬盘故障&#xff1f;出现该问题时应该如何解决&#xff1f;一文带你弄懂答…

【个人博客搭建】(18)使用Quartz.NET 定时备份数据库

Quartz.NET在系统主要承担的一些关键功能&#xff1a; 任务调度&#xff1a;Quartz.NET 允许开发人员创建、调度和管理定时任务&#xff0c;支持简单触发器和Cron表达式等多样化的触发策略。灵活性&#xff1a;Quartz.NET 提供了灵活的任务安排机制&#xff0c;不仅支持基于时间…

数据挖掘(一)数据类型与统计

前言 打算新开一个笔记系列&#xff0c;基于国防科技大学 丁兆云老师的《数据挖掘》 数据挖掘 1、数据类型与统计 数据统计 最大值&#xff0c;最小值&#xff0c;平均值&#xff0c;中位数&#xff0c;位数&#xff0c;方差等统计指标 df.describe() #当调用df.describe(…

电池储能系统的电荷状态预测 | 利用数据驱动机器学习预测锂离子电池储能系统的电荷状态附代码

概述 准确估计电荷状态(SOC)对于保证锂离子电池储能系统的安全性和稳定性至关重要。然而,由于锂离子电池内多个复杂过程的耦合动力学,以及缺乏监测电池内部性能变化的措施,这项任务非常具有挑战性。近年来,随着图形处理器(GPU)计算能力的不断发展,深度学习作为 SOC 估计方…

# 从浅入深 学习 SpringCloud 微服务架构(十四)微服务链路追踪

从浅入深 学习 SpringCloud 微服务架构&#xff08;十四&#xff09;微服务链路追踪 一、微服务的链路追踪概述 1、微服务架构下的问题 在大型系统的微服务化构建中&#xff0c;一个系统会被拆分成许多模块。这些模块负责不同的功能&#xff0c;组合成系统&#xff0c;最终可…

java中的并发编程

1、上下文切换 即使是单核处理器也支持多线程执行代码&#xff0c;CPU通过给每个线程分配CPU时间片来实现 这个机制。这个时间片特别短&#xff0c;一般是几十毫秒&#xff0c;所以会让我们觉得好多任务同时进行。 CPU通过时间片分配算法来循环执行任务&#xff0c;当前任务执…

autolabor(ROS开发笔记)__1

视频链接&#xff1a;ROS机器人 chapter 1 ROS概述与环境搭建 学习步骤&#xff1a; 1.了解该模块的相关概念 是什么&#xff0c;为什么学&#xff0c;前世今生&#xff0c;发展前景 2.安装官方软件包 具备基本的开发环境&#xff0c;简陋notepad 3.搭建集成开发环境(IDE,Int…

打造抖音萌娃账户,一条广告轻松过万,副业兼职最佳选择(实例教程 素材内容)

我特别喜欢简单易操作的新项目&#xff0c;因为过于复杂和门槛高的项目对新手来说可能是毁灭性的&#xff0c;他们往往难以入门&#xff0c;而且付出努力也得不到反馈。 下 载 地 址 &#xff1a; laoa1.cn/1971.html 小宝宝小萌娃账户就相对简单&#xff0c;它类似于电视剧…

文字图形化:UI设计师的必备能力,带你看看为什么要这么做。

在UI设计中&#xff0c;文字尽可能要进行图形化设计的原因有以下几点&#xff1a; 提高识别性&#xff1a; 图形化设计可以通过视觉效果和形状来吸引用户的注意力&#xff0c;从而提高文字的可识别性。这有助于用户更快地理解并记住信息&#xff0c;同时也可以增强品牌的认知…

Python实现一个简单的计算器

简单版本 使用 Python 的 Tkinter 模块来实现一个简单的图形化计算器。以下是一个基本的示例代码 示例效果 代码源码 import tkinter as tkdef button_click(number):current entry.get()entry.delete(0, tk.END)entry.insert(0, current str(number))def button_clear():e…
最新文章