VUE3极速上手手册

news/2024/6/15 16:25:47/文章来源:https://blog.csdn.net/roykingw/article/details/137240614

文章目录

  • Vue3简介
  • 一、整体认识Vue3项目
    • 1、创建Vue3工程
    • 2、主要工程结构
  • 二、数据双向绑定
    • 1、vue2语法的双向绑定
    • 2、OptionsAPI和CompositionAPI
    • 3、Vue3中的数据双向绑定
      • 3.1 ref定义基础类型响应式数据
      • 3.2 reactive定义对象型响应式数据
      • 3.3 ref对比reactive
      • 3.4 标签的ref属性
      • 3.5自定义组件的props属性
  • 三、VUE3生命周期
  • 四、Vue-Router组件路由机制
    • 1、基础使用
    • 2、路由工作模式
    • 3、replace属性
    • 4、嵌套路由
    • 5、路由传参
  • 五、Pinia集中式状态存储
    • 1、理解状态
    • 2、创建store
    • 3、使用store操作数据
    • 4、storeToRefs声明响应式数据
    • 5、store的混合式写法
  • 六、快速上手Element-Plus

Vue3快速上手指南

-- 楼兰

​ 前端技术越来越多,后端程序员要不要学前端?

  • 快速融入项目组。只有人会划分前端和后端,业务问题从不分前后端。
  • 进行一些功能验证。做个权限系统,难道全部靠System.out来设计?
  • 关键是:找对了方法,学起来很简单!你只要会基础的HTML,JS,CSS,那么就可以上手Vue了。如果你会Java,那么上手Vue非常轻松。

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项目

1、创建Vue3工程

​ 前置:安装NodeJS。NodeJS版本18.0以上。

​ 使用官方脚手架创建Vue工程[推荐]。

# 使用官方脚手架
npm create vue@latest# 按照脚手架要求选择是否启用相关组件
Vue.js - The Progressive JavaScript Framework
✔ 请输入项目名称: … myVue3
✔ 请输入包名称: … myvue3
✔ 是否使用 TypeScript 语法? … 否 / 是  # 选是
✔ 是否启用 JSX 支持? … 否 / 是
✔ 是否引入 Vue Router 进行单页面应用开发? … 否 / 是
✔ 是否引入 Pinia 用于状态管理? … 否 / 是
✔ 是否引入 Vitest 用于单元测试? … 否 / 是
✔ 是否要引入一款端到端(End to End)测试工具? › 不需要
✔ 是否引入 ESLint 用于代码质量检测? … 否 / 是 # 选是
✔ 是否引入 Prettier 用于代码格式化? … 否 / 是
✔ Add Vue DevTools extension for debugging? (experimental) … 否 / 是# 启动项目
npm install
npm run dev#  VITE v5.1.6  ready in 315 ms
#
#  ➜  Local:   http://localhost:5173/
#  ➜  Network: use --host to expose
#  ➜  press h + enter to show help

1、所有功能组件都可以后续手动添加。

​ 关于TypeScript,在Vue中的TypeScript可以认为是在JS的基础上,增加面向对象的能力。可以定义接口、类、抽象类等。

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

npm config get registry https://registry.npmmirror.com

​ 补充:vue2时提供了另外一个脚手架vue-cli,也可以用来创建vue3项目。但是vue-cli已经处于停止维护状态。

# 安装或者升级脚手架
npm install -g @vue/cli# 查看脚手架版本,确保版本在4.5.0以上
vue --version# 创建应用
vue create vue_test# 创建时选择3.x
#  Choose a version of Vue.js that you want to start the project with (Use arrow keys)
#  > 3.x
#    2.x# 启动
cd vue_test
npm run serve

​ 另外,官方还有其他一些集成vue的方法,自行参考。

2、主要工程结构

官方建议开发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>中的脚本数据进行绑定,其中任何一个数据发生了变化,另一个数据也随之发生变化。

1、vue2语法的双向绑定

<template><div>姓名:<input v-model="userName" /> {{ userName }} <br />薪水:<input type="number" v-model="salary" /> <button @click="addSalary">薪水加1000</button> {{ salary }}</div>
</template>
<script lang="ts">export default{//数据data() {return {userName:"王一",salary:15000}},//方法methods:{addSalary(){this.salary += 1000}}}
</script><style scoped>
</style>

​ 数据双向绑定可以说是整个Vue的核心。例如,我们可以用数据双向绑定实现一些更为复杂的表单。

<template><div>姓名:<input v-model="userName"/> {{ userName }}<br />薪水:<input type="number" v-model="salary"/> {{ salary }}<br /><button v-on:click="addSalary">提交</button> <button @click="changeUserInfo">查看个人信息</button></div><hr /><div class="userInfo" v-if="showUserInfo"><h2>个人信息</h2>年龄:<input type="number" v-model="userInfo.age" /><br />性别:<input type="radio" value="1" v-model="userInfo.sex">男<input type="radio" value="2" v-model="userInfo.sex">女<br/>岗位:<select v-model="userInfo.department"><option value="dev">开发</option><option value="test">测试</option><option value="maintain">运维</option></select><br />技术: <span v-for="skill in userInfo.skills" :key="skill">{{ skill }}</span><br />学习新技术: <input v-model="newSkill" /> <button @click="learnSkill">学习</button><br />个人信息汇总:{{ userInfo }}</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 },learnSkill(){if(this.newSkill)this.userInfo.skills.push(this.newSkill)},changeUserInfo(){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>

​ 这样的表单,如果要用纯JS实现,就会相当困难。但是,用双向绑定就简单很多。

2、OptionsAPI和CompositionAPI

​ 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的写法如下:

<template><div>姓名:<input v-model="userName" /> {{ userName }} <br />薪水:<input type="number" v-model="salary" /> <button @click="addSalary">薪水加1000</button> {{ salary }}</div>
</template>
<script lang="ts">export default{setup(){//现在声明的变量还不具备双向绑定let userName="王一"let salary=15000function addSalary(){salary += 1000console.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对双向绑定做了重新设计,后面会详细分享。

5、setup有一种简写的方式<script setup lang=“ts”>。这样就不需要写函数了,标签内部直接写函数体。在标签内部声明的对象,函数等,都会直接return出去。项目中常用

<script setup lang="ts">
//现在声明的变量还不具备双向绑定
let userName="王一"
let salary=15000function addSalary(){salary += 1000console.log("salary = " + salary)
}
</script>

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

// MySalary.ts
import { onMounted, ref } from "vue"export default function(){//现在声明的变量还不具备双向绑定。添加ref函数才能具备响应式const userName=ref("王一")const salary=ref(15000)function addSalary(){salary.value += 1000console.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" /> <button @click="addSalary">薪水加1000</button> {{ salary }}</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的模板和脚本封装到一起,这就是自定义组件了。

3、Vue3中的数据双向绑定

3.1 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" /> <button @click="changeName">名字后面加1</button> {{ userName }} <br />薪水:<input type="number" v-model="salary" /> <button @click="addSalary">薪水加1000</button> {{ salary }}</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
//基础类型用ref声明响应式
let userName=ref("王一")
let salary=ref(15000)function changeName(){userName.value += "一"//userName不是响应式的,userName.value才是响应式的。重新定义userName就无法双向绑定//userName = ref("王一一")
}function addSalary(){//脚本中操作数据要加.valuesalary.value += 1000//观察salary对象结构console.log("salary = " , salary)
}
</script>

3.2 reactive定义对象型响应式数据

  • 语法: let salaryInfo = reactive({userName:“王一”,salary:15000})
  • 返回值:一个Proxy实例对象,具有双向绑定能力。
<template><div>姓名:<input v-model="salaryInfo.userName" /> <button @click="changeName">名字后面加1</button> {{ salaryInfo.userName }} <br />薪水:<input type="number" v-model="salaryInfo.salary" /> <button @click="addSalary">薪水加1000</button> {{ salaryInfo.salary }}</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
// 对象类型用reactive声明响应式
let salaryInfo = reactive({userName:"王一",salary:15000})function changeName(){salaryInfo.userName+="一"
}function addSalary(){salaryInfo.salary+=1000//观察SalaryInfo对象console.log("salaryInfo",salaryInfo)
}
</script><style scoped>
</style>

3.3 ref对比reactive

这两者都是用来声明响应式数据的。但是也有一些需要注意的地方。

  • ref也可以用来声明对象型响应式数据。其内部也是使用reactive实现。例如下面的写法效果是一样的
<script setup lang="ts">
import { ref } from 'vue';
// 对象类型用reactive声明响应式
let salaryInfo = ref({userName:"王一",salary:15000})function changeName(){salaryInfo.value.userName+="一"
}function addSalary(){salaryInfo.value.salary+=1000//观察SalaryInfo对象console.log("salaryInfo",salaryInfo)
}
</script>

其中salaryInfo.value其实拿到的就是一个Reactive对象。

  • 基础类型响应式数据,只能用ref声明。对象型响应式数据,ref,reactive都可以。通常大对象推荐使用reactive。
  • 对象型响应数据,如果将各个属性拆解出来,是不具备响应式的。如果需要响应式属性,可以使用toRefs或者toRef函数进行转换。例如
<template><div>姓名:<input v-model="name" /> <button @click="changeName">名字后面加1</button> {{ name }} <br />薪水:<input type="number" v-model="money" /> <button @click="addSalary">薪水加1000</button> {{ money }}</div>
</template>
<script setup lang="ts">
import { reactive, toRef, toRefs } from 'vue';
// 对象类型用reactive声明响应式
let salaryInfo = reactive({userName:"王一",salary:15000})
// 拆解出来的属性,是基础数据,不具备响应式
// let name = salaryInfo.userName
// let money = salaryInfo.salary
// toRef将对象的属性转为一个响应式数据
let name = toRef(salaryInfo,'userName')
let money = toRef(salaryInfo,'salary')
// 将对象的所有属性一起转换成响应式数据
// let {userName,salary} = toRefs(salaryInfo)
function changeName(){name.value +="一"console.log("name",name)
}function addSalary(){money.value +=1000//观察SalaryInfo对象console.log("money",money)
}
</script><style scoped>
</style>

3.4 标签的ref属性

​ 在<template>中定义模板时,可以通过ref属性将当前DOM元素绑定给响应式变量。

<template>姓名:<input ref="name" abc="aaaaa"/> <button @click="showRes">分析输入框</button>
</template>
<script setup lang="ts">
import { 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属性的作用。例如,针对薪水信息,可以自己写一个简单组件,把多个输入框整合到一起。

<!-- 自定义的薪水信息输入组件 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中,就可以通过ref属性获取薪水输入框的值。

<!-- App.vue -->
<template><MySalaryInfo ref="salaryInfo"/><button @click="showRes">查看薪水信息</button>
</template>
<script setup lang="ts">//引入子组件
import MySalaryInfo from '@/components/MySalaryInfo.vue';
import { ref } from 'vue';//获取绑定对象
let salaryInfo = ref()function showRes(){console.log(salaryInfo) //RefImpl ref对象console.log(salaryInfo.value) //Proxy 子组件的响应式数据console.log(salaryInfo.value.userName) //输入框的值console.log(salaryInfo.value.salary) 
}
</script><style scoped>
</style>

3.5自定义组件的props属性

​ 上面的示例相当于是子组件将属性暴露给父组件。那如果想要父组件给子组件赋值呢?这就可以用到组件的props属性。

<!-- App.vue -->
<template><MySalaryInfo :salaryInfo="salaryInfo"/> <br /><button @click="setSalary">修改薪水信息</button>
</template>
<script setup lang="ts">
//引入子组件
import MySalaryInfo from '@/components/MySalaryInfo.vue';
import { reactive } from 'vue';let salaryInfo = reactive({userName:"王一",salary:15000})
function setSalary(){salaryInfo.salary+=1000console.log(salaryInfo)
}
</script><style scoped>
</style>

​ 此时,在MySalaryInfo组件内,就可以通过defineProps函数,获取属性。

<!-- MySalaryInfo.vue -->
<template>{{ salaryInfo }} <br /><!-- 父组件传进来的值,不建议直接用,eslint会报红提示 -->姓名:<input v-model="salaryInfo.userName"><br />薪水:<input type="number" v-model="salaryInfo.salary">
</template><script lang="ts">//组件名默认是文件名。如果不希望用文件名,也可以自定义export default {name:"SalaryInfo"}
</script><script setup lang="ts">
import type { SalaryInfo } from '@/types/SalaryInfo';//直接接收,不限定类型
// defineProps(["salaryInfo"])
//接收参数,限定类型
defineProps<{salaryInfo:SalaryInfo}>()
</script><style></style>

三、VUE3生命周期

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

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

OptionsAPI的生命周期函数:

  • 创建阶段:beforeCreatecreated

  • 挂载阶段:beforeMountmounted

  • 更新阶段:beforeUpdateupdated

  • 销毁阶段:beforeDestroydestroyed

CompositionAPI的生命周期函数:

  • 创建阶段:setup

  • 挂载阶段:onBeforeMountonMounted

  • 更新阶段:onBeforeUpdateonUpdated

  • 卸载阶段:onBeforeUnmountonUnmounted

示例

<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

1、基础使用

​ 首先要在ts脚本中配置router组件。

main.ts

import { createApp } from 'vue'
import App from './App.vue'import { createRouter,createWebHistory } from "vue-router";import HomePage from "@/pages/Home.vue"
import AboutPage from "@/pages/About.vue"
import NewsPage from "@/pages/News.vue"
//配置路由规则
const routes = [{ path: '/',redirect: '/home'}, //默认跳转都首页{ path: '/home', component: HomePage }, { path: '/about', component: AboutPage, name:'about' }, //命名路由{ path: '/news', component: NewsPage },]
//创建路由器
const router = createRouter({history: createWebHistory(),//路由器工作模式routes,
})
//项目中,通常将两个配置项放到单独的ts文件中const app = createApp(App)
//加载路由器
app.use(router)app.mount('#app')

​ 然后,在Vue模板中,配置跳转链接(<router-link>标签)以及跳转页面(<router-view>标签)。

App.vue

<template><div id="app"><h1>Hello App!</h1><p><!-- 使用 router-link 组件进行导航 --><!-- 通过传递 `to` 来指定链接 --><!-- `<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签 --><router-link to="/home">首页</router-link> <!-- 直接跳转 --><router-link :to="{ path:'/about'}">关于</router-link> <!-- 路径跳转 --><router-link replace :to="{ name:'news'}">新闻</router-link> <!-- 命名跳转 --></p><div class="content"><!-- 路由出口 --><!-- 路由匹配到的组件将渲染在这里 --><router-view></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>

​ 启动后,点击页面上方的菜单,下方内容页就会显示相对应的内容。同时注意观察上方路径变化。

在这里插入图片描述

2、路由工作模式

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

  • history模式

访问路径:URL不带#,斜杠链接,接近传统网站。缺点:容易产生404错误。

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

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

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

3、replace属性

<route-link>标签可以添加replace属性。有两种可选配置: push和replace

  • push 追加浏览器历史记录(默认值)。追加历史记录后,可以使用浏览器的返回按钮,跳回历史页
  • replace 替换浏览器历史记录。替换历史记录后,浏览器的返回按钮不可用。

4、嵌套路由

​ <route-view>标签嵌入的页面中支持进一步嵌套子菜单。例如,新闻页希望进一步嵌套新闻路由。新闻页有多条新闻,希望在新闻页展示多条新闻的标题。点击标题,可以查看对应新闻的详情。

​ 首先,定义三个新闻对应的详情页。每个详情页包含简单的内容

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

并配置到路由规则中

import News1 from "@/pages/NewsDetail1.vue"
import News2 from "@/pages/NewsDetail2.vue"const routes = [{ path: '/',redirect: '/home'}, //默认跳转都首页{ path: '/home', component: HomePage }, { path: '/about', component: AboutPage, name:'about' }, //命名路由{ path: '/news', component: NewsPage, name:'news',children:[ //子路由{name: "xinwen1",path: "1",component: News1 },{name: "xinwen2",path: "2",component: News2 }] },]

​ 然后,在新闻详情页增加嵌套路由

<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><script setup lang="ts"></script>
<style scoped>
/* 新闻 */
.news {padding: 0 20px;display: flex;justify-content: space-between;height: 100%;
}
.news ul {margin-top: 30px;list-style: none;padding-left: 10px;
}
.news li>a {font-size: 18px;line-height: 40px;text-decoration: none;color: #64967E;text-shadow: 0 0 1px rgb(0, 84, 0);
}
.news-content {width: 90%;height: 90%;border: 1px solid;margin-top: 20px;border-radius: 10px;
}
</style>

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

5、路由传参

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

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

1、query传参

​ News.vue传参

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

​ NewsDetail.vue接收参数

import {useRoute} from 'vue-router'
import {toRefs} from 'vue'
const route = useRoute()
// 打印query参数
console.log(route.query)
//配置双向绑定数据
let {query} = toRefs(route)

2、params传参

​ params传参方式表示所有参数都拼接到URL上。

​ 首先需要在route配置中预设占位符

{ path: '/news', component: NewsPage, name:'news',children:[ //子路由{name: "xinwen2",// Param传参,URL预设占位符,?表示参数可选path: "2/:id/:title/:content",component: News2 }] },

​ 然后,传参时,在RouteLink中直接传到预设的URL,或者用name属性指定目标。

<!-- params传参 -->
<RouterLink to="/news/2/1/新闻2/qowuieoiu">param路径传参</RouterLink>
<!-- params传参 -->
<RouterLink :to="{name:'xinwen2',params:{id:2,title :'新闻2',content :'qowiueoiqu'}}">param对象传参
</RouterLink>

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

import {useRoute} from 'vue-router'
import {toRefs} from 'vue'
const route = useRoute()
// 打印params参数
console.log(route.params)
//配置双向绑定数据
let {params} = toRefs(route)

五、Pinia集中式状态存储

1、理解状态

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

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

2、创建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中最为核心的就是state,而在定义state时,可以利用TypeScript的类型定制功能,对复杂数据结构进行规范。例如

const useStore = defineStore('storeId', {
state: () => {
return {
// 用于初始化空列表
userList: [] as UserInfo[],
// 用于尚未加载的数据
user: null as UserInfo | null,
}
},
})interface UserInfo {
name: string
age: number
}

如果你熟悉Java后端开发,有没有觉得接口很熟悉?那么接下来,类、抽象类这些呢?也可以尝试尝试。

3、使用store操作数据

App.vue中修改stroe的数据

<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')
// 获取store中的数据
console.log(user.username)
// 通过getter获取state数据 推荐方式
console.log(user.getUsername)
</script>

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

4、storeToRefs声明响应式数据

​ 如果需要将store中的数据声明成响应式数据,供Vue的模板使用,可以使用pinia提供的storeToRefs函数。他和Vue提供的toRefs函数的区别在于,stroeToRefs只将store中的数据转换成响应式数据。而toRefs会将store对象中很多隐藏的方法和属性页转换出来。

<template><div id="app"><h1>Hello {{ userInfo.username.value }}</h1>
</div>
</template><!-- vue3写法 -->
<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><style>
</style>

5、store的混合式写法

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

import { defineStore } from 'pinia'
import { reactive } from 'vue'export const userStore = defineStore('userStore',()=>{//相当于是stateconst userInfo = reactive({username:"---"})//相当于actionfunction changeUsername(value:string){if(value && value.length<10){userInfo.username = value}}//相当于gettersfunction 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.gitee.io/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.luyixian.cn/news_show_1033707.aspx

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

相关文章

Android 开发 Spinner setSelection 不起作用

问题 Android 开发 Spinner setSelection 不起作用 详细问题 笔者进行Android项目开发&#xff0c;根据上一个页面用户选择数据&#xff0c;显示当前页面Spinner选项&#xff0c;调用 Spinner setSelection 不起作用。 相关java代码 spinner.setAdapter(adapter); …

uniapp点击按钮连接wifi?

<view><button class"but" bindtap"connectToWifi">点击连接WiFi</button> </view> js: Page({// 页面数据data: {wifiConnected: false},onLoad: function () {this.checkWifiPermission();},// 检查 WiFi 权限checkWif…

不同设备使用同一个Git账号

想要在公司和家里的电脑上用同一个git账号来pull, push代码 1. 查看原设备的用户名和邮箱 第1种方法&#xff0c; 依次输入 git config user.name git config user.email第2种方法&#xff0c; 输入 cat ~/.gitconfig2. 配置新设备的用户名和邮箱 用户名和邮箱与原设备保持…

uniApp——零基础App的快速开发记录

公司需要使用移动端App来接受云平台的数据&#xff0c;记录一下学习过程。因水平有限&#xff0c;在学习的过程中不能完整、准确、全面的发现错误&#xff0c;如有错误&#xff0c;请评论指教&#xff0c;谢谢&#xff01; 需求分析 总体需求目标&#xff1a;开发一款android的…

文件传输升级!如何让你的MacBook与安卓设备快速共享大文件?

在现代工作环境中&#xff0c;跨设备、跨平台的文件传输已成为日常办公不可避免的一部分。尤其是MacBook和安卓手机之间的大文件传输&#xff0c;由于两者系统的差异&#xff0c;可能会遇到一些效率上的挑战。为了优化这一过程&#xff0c;以下是一些提升传输效率的新技巧。 1&…

搜索与图论——Prim算法求最小生成树

在最小生成树问题里&#xff0c;正边和负边都没问题 朴素版prim算法 时间复杂度O(n^2) 生成树&#xff1a;每一次选中的t点&#xff0c;它和集合的距离对应的那条边&#xff0c;就是生成树的一条边 算法流程和dijkstra算法非常相似 #include<iostream> #include<cs…

稀碎从零算法笔记Day33-LeetCode:生命游戏

今天是“耶稣受难人”&#xff0c;笔者给自己放了天假hhh 且慢&#xff0c;还是会写一点的。 根据 百度百科 &#xff0c; 生命游戏 &#xff0c;简称为 生命 &#xff0c;是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 题型&#xff1a;矩阵、原地实现、位运算 链…

Pytorch for training1——read data/image

blog torch.utils.data.Dataset create dataset with class torch.utils.data.Dataset automaticly import torch from torch.utils.data import Datasetclass MyDataset(Dataset):def __init__(self, data):self.data datadef __getitem__(self, index):# 根据索引获取样本…

紫光展锐P7885核心板详细参数介绍_5G安卓智能模块开发方案

紫光展锐P7885核心板采用了先进的6nm EUV制程工艺&#xff0c;集成了高性能的应用处理器和金融级安全解决方案&#xff0c;为用户带来了全新的性能体验。 P7885核心板搭载了先进的6nm制程工艺SoC P7885&#xff0c;其中包含四核A76和四核A55&#xff0c;主频可达2.7Ghz&#xf…

计算机网络面试问题(一)

1.在浏览器中输⼊URL并按下回⻋之后会发⽣什么 2.TCP三次握⼿的过程,为什么三次握手 TCP&#xff08;传输控制协议&#xff09;的三次握⼿是建⽴⽹络连接的过程&#xff0c;确保通信双⽅能够正确地进⾏数据传输。 第⼀次握⼿&#xff08;SYN&#xff09;&#xff1a; 客户端&am…

【THM】SQL Injection(SQL注入)-初级渗透测试

简介 SQL(结构化查询语言)注入,通常称为 SQLi,是对 Web 应用程序数据库服务器的攻击,导致执行恶意查询。当 Web 应用程序使用未经正确验证的用户输入与数据库进行通信时,攻击者有可能窃取、删除或更改私人数据和客户数据,并攻击 Web 应用程序身份验证方法以获取私有数据…

风险与收益

风险与收益 影响资产需求的主要因素财富总量预期收益率资产的流动性影响流动性的主要因素 风险 如何降低风险系统风险和非系统风险机会集合与有效集合资产组合理论 影响资产需求的主要因素 影响资产需求的主要因素包括&#xff1a;财富总量、预期收益率、资产的流动性和风险。…

【JavaEE初阶系列】——多线程案例四——线程池

目录 &#x1f6a9;什么是线程池 &#x1f388;从池子中取效率大于新创建线程效率(why) &#x1f6a9;标准库中的线程池 &#x1f388;为什么调用方法而不是直接创建对象 &#x1f388;工厂类里的方法 &#x1f4dd;newCachedThreadPool() &#x1f4dd;newFixedThread…

使用Leaflet.rotatedMaker进行航班飞行航向模拟的实践

目录 前言 一、Leaflet的不足 1、方向插件 2、方向控制脚本说明 二、实时航向可视化实现 1、创建主体框架 2、飞机展示 3、位置和方位模拟 三、成果及分析 1、成果展示 2、方向绑定解读 总结 前言 众所周知&#xff0c;物体在空间中的运动&#xff08;比如飞行、跑步…

【Linux】ubuntu/centos8安装zsh终端

本文首发于 ❄️慕雪的寒舍 根据这篇知乎文章进行 https://zhuanlan.zhihu.com/p/514636147 1.安装zsh 先安装zsh并设置为默认的终端 # ubuntu sudo apt install zsh # centos sudo yum install zsh util-linux-user # 通用 chsh -s /bin/zsh如果centos下找不到chsh命令&am…

深入解析实时数仓Doris:Rollup上卷表与查询

码到三十五 &#xff1a; 个人主页 心中有诗画&#xff0c;指尖舞代码&#xff0c;目光览世界&#xff0c;步履越千山&#xff0c;人间尽值得 ! 目录 一、基本概念二、Aggregate 和 Unique 模型中的 ROLLUP三、Duplicate 模型中的 ROLLUP四、ROLLUP 调整前缀索引五、ROLLUP使…

改Jenkins版本号

旧服务器迁移到新&#xff0c;打包版本号更新 Jenkins.instance.getItemByFullName("双机热备").updateNextBuildNumber(65)

154 Linux C++ 通讯架构实战9 ,信号功能添加,信号使用sa_sigaction 回调,子进程添加,文件IO详谈,守护进程添加

初始化信号 使用neg_init_signals(); 在nginx.cxx中的位置如下 //(3)一些必须事先准备好的资源&#xff0c;先初始化ngx_log_init(); //日志初始化(创建/打开日志文件)&#xff0c;这个需要配置项&#xff0c;所以必须放配置文件载入的后边&#xff1b;//(4)一些初…

【SpringBoot整合系列】SpirngBoot整合EasyExcel

目录 背景需求发展 EasyExcel官网介绍优势常用注解 SpringBoot整合EaxyExcel1.引入依赖2.实体类定义实体类代码示例注解解释 3.自定义转换器转换器代码示例涉及的枚举类型 4.Excel工具类5.简单导出接口SQL 6.简单导入接口SQL 7.复杂的导出&#xff08;合并行、合并列&#xff0…

C++刷题篇——04找等值元素

一、题目 二、解题思路 1、分割后放进二维数组 2、使用map&#xff0c;key为数值&#xff0c;value为其坐标 3、遍历二维数组元素&#xff0c;再在map中找该元素对应的value值&#xff08;二维数组形式&#xff09;&#xff0c;倘若value.size为1&#xff0c;那直接返回-1&…