目录
11.4.1:Vue3介绍(渐进式JavaScript框架)
第十一部分:前端工程化
11.1:前端工程化开篇
11.1.1:什么是前端工程化
前端工程化是使用软件工程的方法来单独解决前端的开发流程中模块化、组件化、规范化、自动化的问题,其主要目的是为了提高效率和降低成本
11.1.2:前端工程化实现技术栈
前端工程化实现的技术栈有很多,我们采用ES6+nodejs+npm+Vite+Vue3+router+pinia+axios+Element-plus组合来实现
- ECMAScript6:Vue3中大量使用ES6语法
- Node.js:前端项目运行环境
- npm:依赖下载工具
- Vite:前端项目构建工具
- Vue3:优秀的渐进式前端框架
- router:通过路由实现页面切换
- pinia:通过状态管理实现组件数据传递
- axios:ajax异步请求封装技术实现前后端数据交互
- Element-plus:可以提供丰富的快速构建网页的组件仓库
前后端不分离:
前后端分离:
11.2:ECMA6Script
11.2.1:ES6的介绍
ECMAScript 6,简称ES6,是JavaScript语言的一次重大更新。它于2015年发布,是原来的ECMAScript标准的第六个版本。ES6带来了大量的新特性,包括箭头函数、模版字符串、let和const关键字、解构、默认参数值、模块系统等等,大大提升了JavaScript的开发体验。由于VUE3中大量使用了ES6的语法,所以ES6成为了学习VUE3的门槛之一
ECMAScript ,是由网景公司制定的一种脚本语言的标准化规范;最初命名为 Mocha ,后来改名为 LiveScript ,最后重命名为 JavaScript
ECMAScript 2015(ES2015),第 6 版,最早被称作 ECMAScript 6(ES6),添加了新的特性ES6对JavaScript的改进在以下几个方面:
- 更加简洁:ES6引入了一些新的语法,如箭头函数、类和模版字符串等,使代码更加简洁易懂
- 更强大的功能:ES6引入了一些新的API、解构语法和迭代器等功能,从而使得JavaScript更加强大
- 更好的适用性::ES6引入的模块功能为JavaScript代码的组织和管理提供了更好的方式,不仅提高了程序的可维护性,还让JavaScript更方便地应用于大型的应用程序
总的来说,ES6在提高JavaScript的核心语言特性和功能方面取得了很大的进展。由于ES6已经成为了JavaScript的标准,它的大多数新特性都已被现在浏览器所支持,因此现在可以放心地使用ES6来开发前端应用程序
11.2.2:ES6的变量和模版字符串
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>01-let && const</title>
<script>
/*
ES6:新特性,从而大大提高了Javascript的开发体验;VUE3中大量使用了ES6的语法
①箭头函数
②模版字符串
③let和const关键字
④解构
⑤默认参数值
⑥模块系统
*/
// 1、let不能重复声明
// var i = 10
// var i = ""
// let j = 10
// let j = 'zhangsan'
// 2、let有块级作用域,非函数的花括号遇见let会有块级作用域,也就是只能在花括号里面访问
/* {
var i = 10
let j = 10
}
console.log(i) */
// console.log(j)
// 3、let不会预解析进行变量提升
// console.log(a)
// var a = 10
// console.log(b)
// let b = 10
// 4、let定义的全局变量不会作为window的属性
// var a = 10
// let b = 10
// console.log(window.a)
// console.log(window.b)
// 5、let在ES6中推荐优先使用
// 6、const就是不可修改的let 像final修改的变量
// let a = 10
// a = 20
// const b = 10
// b = 20
/*模版字符串和字符串拼接问题
使用""或''来处理字符串是不支持多行操作的
使用``来处理字符串时支持多行操作的
*/
let city = '北京'
let str = `
<ul>
<li></li>
<li>${city}</li>
<li></li>
</ul>`
console.log(str)
</script>
</head>
<body>
</body>
</html>
11.2.3:ES6的解构表达式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>02-解构表达式</title>
<script>
/*
解构表达式是一种在编程中用于将复杂数据结构(如数组或对象)解构为更简单的部分的语
法。
它允许你通过一种简洁的方式来提取数组元素或对象属性,并将它们分配给变量。
解构表达式通常用于JavaScript
*/
let arr = [11,22,33,44]
// 使用解构表达式取出数据中的元素
let[a,b,c,d,e = 10] = arr
console.log(a,b,c,d,e)
let person={
name:"zhangsan",
age:10
}
// 使用解构表达式获取对象的属性值
let{name,age} = person
console.log(name,age)
// 解构表达式应用在方法的参数列表
function showArr([a,b,c]){
console.log(a,b,c)
}
showArr(arr)
</script>
</head>
<body>
</body>
</html>
11.2.4:ES6的箭头函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>箭头函数</title>
<script>
let fun1 = function(){} //普通的函数声明
let fun2 = ()=>{} //箭头函数声明 lambda
let fun3 = (x)=>{return x+1}
let fun4 = x => {return x+1} //参数列表中有且仅有一个参数,()可以省略不写
let fun5 = x => console.log(x) //如果方法体中只有一行代码,{}可以不写
let fun6 = x => x+1 //如果方法体中有且只有一样代码,这行代码是return返回结果的代码,那么{}和return可以省略不写
/*
箭头函数没有自己的this
箭头函数中的this是外层上下文环境中的this
*/
console.log(this)
let Person = {
name:"张三",
showName:function(){
console.log(this.name)
},
viewName:() => {
console.log(this.name)
}
}
Person.showName()
Person.viewName()
</script>
</head>
<body>
</body>
</html>
11.2.5:ES6的rest和spread
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>rest和spread</title>
<script>
// rest 剩余的,解决剩余的参数接收问题
let fun1=(a,b,c,d,... arr) => {
console.log(a,b,c,d)
console.log(arr)
}
fun1(1,2,3,4,5,6,7,8)
//spread 展开 就是rest在实参上的使用
let arr = [1,2,3]
// let info = ...arr //...arr >> 1,2,3;这么写是错误的,必须在调用方法时使用,作为实参使用
let fun2 = (a,b,c)=>{
console.log(a,b,c)
}
fun2(... arr)
// 快速合并数组
let a = [1,2,3]
let b = [4,5,6]
let c = [7,8,9]
let d = [...a,...b,...c]
console.log(d)
// 快速合并对象
let person1 = {name:"张三"}
let person2 = {age:"10"}
let person3 = {gender:"boy"}
let person4 = {...person1,...person2,...person3}
console.log(person4)
</script>
</head>
<body>
</body>
</html>
11.2.6:ES6的对象创建和拷贝
①对象的创建
ES6中新增了对象创建的语法,支持了class、extends、constructor等关键字,让ES6的语法和面向对象的语法更加接近
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width='device-width', initial-scale=1.0">
<title>类和对象</title>
<script>
class Person{
// 属性(加#为私有属性,并给若要使用其属性,需要在使用时也加上#)
name;
age;
// getter setter
get name(){
return this.name;
}
set name(name){
this.name = name;
}
// 实例方法
eat(food){
console.log(`${this.age}岁的${this.name}正在吃${food}`)
}
// 静态方法
static sum(a,b){
return a+b
}
// 构造器
constructor(name,age){
this.name = name
this.age = age
}
}
let person = new Person("小明",8)
console.log(person.name)
console.log(person)
person.eat("红烧肉")
console.log(Person.sum(10,20))
//继承
class Student extends Person{
score
study(){
console.log(`${this.age}岁的${this.name}正在努力的学习,考试获得了${this.score}分`)
}
constructor(name,age,score){
super(name,age)
this.score = score
}
}
let stu = new Student("小贾",10,99)
stu.study()
</script>
</head>
<body>
</body>
</html>
②对象的深拷贝和浅拷贝
对象的拷贝:快速获得一个和已有对象相同的对象的方式
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>拷贝问题</title>
<script>
let arr = [1,2,3]
// // 浅拷贝
// arr[0] = 10
// let arr2 = arr
// console.log(arr2)//arr的改变也会导致arr2的改变
// 深拷贝
let arr2 = [...arr]
arr[0] = 10
console.log(arr2)
</script>
</head>
<body>
</body>
</html>
11.2.7:ES6的模块化处理
①模块化介绍
模块化是一种组织和管理前端代码的方式,将代码拆分成小的模块单元,使得代码更易于维护、扩展和复用。它包括了定义、导出、导入以及管理模块的方法和规范。主要优势如下:
- 提高代码可维护性:通过将代码拆分为小的模块单元,使得代码结构更为清晰,可读性更高,便于开发者阅读和维护
- 提高代码可复用性:通过将重复使用的代码变成可复用的模块,减少代码重复率,降低开发成本
- 提高代码可扩展性:通过模块化来实现代码的松耦合,便于更改和替换模块,从而方便地扩展功能
目前,前端模块化有多种规范和实现,包括CommonJS、AMD和ES6模块化。ES6模块化是JavaScript语言的模块标准,使用import和export关键字来实现模块的导入和导出。现在,大部分浏览器都已经原生支持ES6模块化,因此它成为了最为广泛使用的前端模块化标准
- ES6模块化的几种暴露和导入方式(三种导入方式可以在一个js文件同时使用,但一般不建议这样使用)
- 分别导入
- 统一导入
- 默认导入
- ES6中无论以何种方式导出,导出的都是一个对象,导出的内容都可以理解为是向这个对象中添加属性或方法
②分别导出
module.js:
// 变量
export const PI = 3.14
// 方法
function sum(a,b){
return a+b
}
// 类
class Person{
constructor(name,age){
this.name = name
this.age = age
}
sayHello(){
console.log(`Hello,my name is ${this.name},I am ${this.age}years old!`)
}
}
app.js:
// 导入module.js文件
/*
*代表module.js的所有成员
无论以何种方式导入,导出的内容都会被当成一个对象处理
使用一个对象来接收所有的成员
*/
import * as m1 from './module.js'
console.log(m1.PI)
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>分别导出</title>
<!-- 引入app.js文件 -->
<script src="./app.js" type="module"></script>
</head>
<body>
</body>
</html>
③统一导出
其余文件不变,只改变module.js
// 变量
const PI = 3.14
// 方法
function sum(a,b){
return a+b
}
// 类
class Person{
constructor(name,age){
this.name = name
this.age = age
}
sayHello(){
console.log(`Hello,my name is ${this.name},I am ${this.age}years old!`)
}
}
export{PI,sum,Person}
④默认导出
module.js:
// 变量
const PI = 3.14
// 方法
function sum(a,b){
return a+b
}
// 类
class Person{
constructor(name,age){
this.name = name
this.age = age
}
sayHello(){
console.log(`Hello,my name is ${this.name},I am ${this.age}years old!`)
}
}
// 默认导出在一个js中只能有一个
export default sum
app.js:
// 导入module.js文件
/*
*代表module.js的所有成员
无论以何种方式导入,导出的内容都会被当成一个对象处理
使用一个对象来接收所有的成员
*/
import * as m1 from './module.js'
// import {default as add} from './module.js'
// 简化
import add from './module.js'
console.log(m1.default(10,20))
index.html不变
11.3:前端工程化环境搭建
11.3.1:Node.js的简介和安装
①Node.js简介
Node.js是一个 基于Chrome V8引擎的JavaScript运行时的环境,可以使JavaScript运行在服务器端。使用Node.js可以方便的开发服务器端应用程序,如Web应用API、后端服务,还可以通过Node.js构建命令行工具等。相比于传统的服务器端语言(如PHP、Java、Python等),Node.js具有一下特点:
- 单线程,使用了事件驱动、异步I/O模型,可以处理高并发请求
- 轻量级,使用C++编写的V8引擎让Node.js的运行速度更快
- 模块化,Node.js内置了大量模块,同时也可以通过第三方模块扩展功能
- 跨平台,可以在Windows、Linux、Mac等多种平台下运行
Node.js的核心是其管理事件和异步I/O的能力。Node.js的异步I/O使其能够处理大量并发请求,并且能够避免在等待I/O资源时造成的阻塞。此外,Node.js还拥有高性能网络库和文件系统库,可用于搭建WebSocket服务器、上传文件等。在Node.js中,我们可以使用JavaScript来编写服务器端程序,这也使得前端开发人员可以利用自己已经熟悉的技能来开发服务器端程序,同时也让JavaScript成为一种全栈语言。Node.js受到了广泛的应用,包括了大型企业级应用、云计算、物联网、游戏开发等领域。常用的Node.js框架包括Express、Koa、Egg.js等,它们能够显著提高开发效率和代码质量
②Node.js安装
1、官网地址:Node.js
2、双击安装包进行安装,安装过程遵循默认选项即可。安装完成后,可以在命令行终端输入node -v和npm -v查看Node.js和npm的版本号
11.3.2:npm配置和使用
NPM全称Node Package Manager,是Node.js包管理工具,是全球最大的模块生态系统,里面所有的模块都是开源免费的;也是Node.js的包管理工具,相当于后端的Maven
①安装
安装Node,自动安装NPM包管理工具 !
②配置依赖下载使用阿里镜像
- NPM安装依赖包时默认使用的是官方源,由于国内网络环境的原因,有时会出现下载速度过慢的情况。为了解决这个问题,可以配置使用阿里镜像来加速NPM的下载速度,具体操作如下:
- 打开命令行终端,执行一下命令,配置使用阿里镜像:
③修改全局依赖的安装路径,步骤如下:
④升级npm版本
⑤npm的常用命令
- npm init:初始化
- npm init -y:所有的信息使用当前文件夹的默认值!不用挨个填写
- npm install 包名:安装依赖
- npm uninstall 包名:卸载依赖
- npm ls:查看当前项目依赖
- npm -g ls:查看全局依赖
11.4:Vue3简介和快速体验
11.4.1:Vue3介绍(渐进式JavaScript框架)
Vue是一款用于构建用户界面的JavaScript框架。它基于标准HTML、CSS和JavaScript构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单的还是复杂的界面,Vue都可以胜任
Vue的两个核心功能:
- 声明式渲染:Vue基于标准HTML扩展了一套模版语法,使得我们可以声明式地描述最终输出的HTML和JavaScript状态之间的关系
- 响应性:Vue会自动跟踪JavaScript状态并在其发生变化时响应式地更新DOM
Vue作者:尤雨溪
11.4.2:Vue3快速体验(非工程化方式)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 导入vue依赖 核心的vue3的js文件 -->
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<!-- 第一种方式 -->
<h1 v-text="message" v-bind:style="colorStyle">hello Vue</h1>
<!-- 第二种方式 -->
<h1>{{message}}</h1>
<button @click="fun1()">change</button>
</div>
<script>
const app = Vue.createApp({
setup(props) {
// 定义数据 以变量或对象形式
let message = "hello"
let colorStyle = {"background-color":"red"}
function fun1(){
alert("hello")
}
// 在return中返回变量或方法,才能和HTML元素相关联
return{
message,
colorStyle,
fun1
}
}
})
// 将app对象挂载到指定的元素上,被挂载的元素内部就可以通过vue框架实现数据的渲染了
app.mount("#app")
</script>
</body>
</html>
11.5:Vue3通过Vite实现工程化
11.5.1:Vite介绍(下一代的前端工具链)
在浏览器支持ES模块之前,JavaScript并没有提供原生机制让开发者以模块化的方式进行开发。这也正是我们对“打包”这个概念熟悉的原因:使用工具抓取、处理并将我们的源码模块串联成可以在浏览器中运行的文件。
- 当我们构建越来越大型的应用时,需要处理的JavaScript代码量也呈指数级增长
- 包含数千个模块的大型项目相当普遍。基于JavaScript开发的工具就会开始遇到性能瓶颈:通常需要很长时间(甚至是几分钟)才能启动,开发服务器,即使使用模块热替换(HMR),文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感
现在的VUE项目是基于vite创建的,vite是新一代前端构建工具,vite的优势如下:
①轻量级的热重载(HMR) ,可以实现极速的服务启动。
②对TS、JSX、CSS等支持开箱即用。
③真正的按需编译,不再等待整个应用编译完成。
Vite旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持ES模块,且越来越多的JavaScript工具使用编译型语言编写,前端工程化的项目包括但不限于以下几个方面:
- 快速的创建项目
- 统一的工程化规范
- 代码模版和组件库
- 自动化构建和部署
11.5.2:Vite创建Vue3工程化项目
①Vite+Vue3项目的创建、启动、停止
创建:
依赖下载:
启动:
停止: Ctrl键+C
②Vite+Vue3项目的目录结构
③Vite+Vue3项目组件(SFC入门)
什么是VUE的组件?
- 一个页面作为整体,是由多个部分组成的,每个部分在这里就可以理解为一个组件
- 每个.vue文件就可以理解为一个组件,多个.vue文件可以构成一个整体页面
- 组件化给我们带来的另一个好处就是组件的复用和维护非常的方便
什么是.vue文件?
- 传统的页面由.html文件,.css文件,.js文件 着三个文件组成(多文件组件)
- vue将这三个文件合并成一个.vue文件(Single-File Component,简称SFC,单文件组件【页面相同的部分,单独拿出来,作为一个构建其网页的“组件”,需要的时候引用即可】)
- .vue文件对js/css/html统一封装,这是VUE中的概念,该文件由三个部分组成:<script> <template> <style>
- template标签:代表组件的html部分代码,代替传统的.html文件
- script标签:代表组件的js代码,代替传统的.js文件
- style标签:代表组件的css样式代码,代替传统的.css文件
工程化vue项目如何组织这些组件?
- index.html是项目的入口,其中<div id = 'app'><div>是用于挂载所有组建的元素
- index.html中的script标签引入了一个main.js文件,具体的挂载过程在main.js中执行
- main.js是vue工程中非常重要的文件,它决定这个项目使用哪些依赖,导入的第一个组件
- App.vue是vue中的核心组件,所有的其它组件都要通过该组件进行导入,该组件通过路由可以控制页面的切换
④Vite+Vue3响应式入门和setup函数
<script setup>
import {ref} from 'vue'
// 定义一些要展示到html上的一些数据:变量或对象
/*
响应式数据:在数据变化时,vue框架会将变量最新的值更新到dom树中,页面数据就是实时更新的
非响应式数据:在数据变化时,vue框架不会将变量最新的值更新到dom树中,页面数据不是实时更
新的
vue2中,数据不做特殊处理,默认就是响应式的
vue3中,数据要经过ref/reactive函数的处理才是响应式的
使用ref/reactive函数时,vue框架中给我们提供的方法导入进来即可使用
ref处理的响应式数据,在操作时需要注意:
在script标签中,操作ref的响应式数据需要通过.value的形式操作
在template标签中,操作ref的响应式数据,无需使用.value
*/
let counter = ref(10) //{value:10}
// 让counter自增的方法
function counterIncr(){
counter.value++
}
// 让counter自减的方法
function counterDecr(){
counter.value--
}
// 显示counter值的方法
function showCounter(){
alert(counter.value)
}
</script>
<template>
<div>
<button @click="counterIncr()">+</button>
<span v-text="counter"></span>
<button @click="counterDecr()">-</button><br/>
<button @click="showCounter()">showCounter</button>
</div>
</template>
<style scoped>
</style>
⑤Vite+Vue3关于样式的导入方式
<script setup>
/*
css样式的导入方式:
1、在.vue文件中的style标签中
2、将css样式保存到独立的css文件中,哪个.vue文件需要,就在哪里导入
①script标签中可以导入:import './style/test.css
②style标签中可以导入: @import './style/test.css'
3、如果某个样式要在所有的.vue文件中生效,那么可以在main.js中导入
mian.js中添加:import './style/test.css'
*/
// import './style/test.css'
</script>
<template>
<!-- 语法上,要求template只能有一个一级子标签 -->
<div>
<span class="s1">你好</span>
</div>
</template>
<style scoped>
/* @import './style/test.css' */
</style>
11.6:Vue3视图渲染技术
11.6.1:模版语法
Vue使用一种基于HTML的模版语法,使我们能够声明式地将其组件实例的数据绑定到呈现的DOM上。所有的Vue模版都是语法层面合法的HTML,可以被符合规范的浏览器和HTML解析器解析。在底层机制中,Vue会将模版编译成高度优化的JavaScript代码。结合响应式系统,当应用状态变更时,Vue可以智能的推导出需要重新渲染的组件的最少数量,并应用最少的DOM操作。
①插值表达式和文本渲染
<script setup>
/*
差值表达式
语法:{{数据名字/函数/对象调用API}}
*/
// 定义一些常见数据
let msg = "hello vue3"
let getMsg = ()=>{
return "hello vue3 message"
}
let age = 19
let bee = "蜜 蜂"
let carts = [{name:"可乐",price:3,number:10},{name:"薯片",price:6,number:8}]
// 定义一个获得购物车商品总金额的方法
function compute(){
let count = 0
for(let index in carts){
count += carts[index].price * carts[index].number
}
return count
}
</script>
<template>
<div>
<!-- 将数据绑定到下面的元素上 -->
<h1>{{ msg }}</h1>
<!-- 插值表达式不依赖标签,没有标签可以单独使用 -->
msg的值为{{ msg }}<br>
<!-- 插值表达式中可以调用函数,将函数的返回值渲染到指定的位置 -->
msg的值为{{ getMsg() }}<br>
<!-- 插值表达式支持一些常见的运算符 -->
年龄:{{ age }},是否成年:{{ age > 18 ? '是' : '否' }}<br>
<!-- 差值表达式中支持对象调用一些API -->
{{ bee.split(' ').reverse().join('')}}<br>
<!-- 显示购物车中的总金额 -->
{{ carts[0].price*carts[0].number + carts[1].price*carts[1].number }}
{{ compute() }}<br>
</div>
</template>
<style scoped>
</style>
<script setup>
/*
v-text:不识别HTML结构的文本
v-html:识别文本中的HTML代码的命令
{{}}:插值表达式
v-***:vue的指令
1、命令必须依赖标签,在开始标签中使用
*/
let msg = "hello vue3"
//命令支持字符串模版
let haha = "haha"
let msg2 = `hello ${haha}`
// 命令中支持一些常见的运算符
let age = 19
// 命令中支持常见对象的API调用
let bee = "蜜蜂"
// 命令中支持函数的调用
let getMsg = ()=>{
return "hello"
}
let fontMsg = "<font color='red'>你好</font>"
</script>
<template>
<div>
<h1 v-text="msg"></h1>
<h1 v-text="msg2"></h1>
<h1 v-text="`你好 ${haha}`"></h1>
<h1 v-text="age>=18 ? '成年' : '未成年'"></h1>
<h1 v-text="bee.split('').reverse().join('-')"></h1>
<h1 v-text="getMsg()"></h1>
<h1 v-html="fontMsg"></h1>
</div>
</template>
<style scoped></style>
②Attribute属性渲染
<script setup>
/*
属性渲染命令
v-bind:将数据绑定到元素的属性上
v-bind:属性名="数据名"(冒号可以省略不写)
*/
// let imgUrl = "http://www.atguigu.com/images/index_new/logo.png"
const data= {
logo:"http://www.atguigu.com/images/index_new/logo.png",
name:"尚硅谷",
url:"http://www.atguigu.com"
}
</script>
<template>
<div>
<!-- <img v-bind:src="imgUrl"> -->
<a v-bind:href="data.url">
<img v-bind:src="data.logo" v-bind:title="data.name">
</a>
</div>
</template>
<style scoped>
</style>
③事件的绑定
<script setup>
/*
v-on:事件名称="函数名()"
v-on:事件名可以简写为:@事件名
原生js的事件名是on***:onclick ondbclick onblur
vue中的事件名去掉on: click dbclick blur
*/
function fun1(){
alert("hi")
}
let counter = ref(1)
function fun2(){
counter.value++
}
function fun3(event){
let flag = confirm("确定要访问目标链接吗?")
if(!flag){
// 原生js编码方式组件组件的默认行为
event.preventDefault()
}
}
function fun4(){
alert("超链接被点击了")
}
</script>
<template>
<div>
<!-- 事件的绑定函数 -->
<button v-on:click="fun1()">hello</button>
<button v-on:click="fun2()">+</button>
<!-- 内联事件处理 -->
<button v-on:click="counter++">+</button>
<!-- 事件的修饰符 .once事件只绑定一次 prevent修饰符阻止组件的默认行为 -->
<button v-on:click.once="counter++">+</button>
{{ counter }}
<br>
<a href="http://www.atguigu.com" v-on:click="fun3($event)">尚硅谷</a>
<a href="http://www.atguigu.com" v-on:click.prevent="fun4()">尚硅谷</a>
</div>
</template>
<style scoped>
</style>
11.6.2:响应式基础
此处的响应式是指:数据模型发生变化时,自动更新DOM树内容,页面上显式的内容会进行同步变化,vue3的数据模型不是自动响应式的,需要我们做一些特殊的处理
<script setup>
import {ref,reactive,toRef,toRefs} from 'vue'
/*
让一个普通数据转换为响应式数据:两种方式
1.ref函数 更适合单个变量
在script标签中操作ref相应式数据要通过.value
在template中操作ref响应式数据则无需通过.value
2.reactive函数 更适合对象
无论是在script或template中操作reactive响应式数据都直接使用对象名.属性名的方式即可
*/
let counter = ref(10);
let person= reactive({
name:"",
age:10
})
let p_age = toRef(person,'age')
function incrRefAge(){
p_age.value++
}
let{name,age} = toRefs(person)
function incrRefsAge(){
age.value++
}
function incrAge(){
person.age++
alert(person.age)
}
function incr(){
counter.value++;
// toRef函数 将reactive响应式数据中的某个属性转换为ref响应式数据
// toRefs函数 同时将reactive响应式数据中的多个属性转换为ref响应式数据
}
</script>
<template>
<div>
<button @click="incr()">+</button>
<button @click="counter++">+</button>
{{ counter }}
<hr>
<button @click="incrAge()">+</button>
<button @click="perosn.age++">+</button>
<button @click="incrRefAge()">+</button>
<button @click="incrRefsAge()">+</button>
{{ person.age }}
{{ p_age }}
</div>
</template>
<style scoped>
</style>
11.6.3:条件和列表渲染
①条件渲染
<script setup>
import {ref} from 'vue'
let flag = ref(true)
/*
v-if="表达式/数据":数据为true,则当前元素会渲染进入dom树
v-else:自动和前一个v-if做取反操作
v-show="":数据为true,元素则展示在页面上,否则不展示
区别:
v-if数据为false时,元素则不在DOM树中了
v-show数据为false时,元素仍在DOM树中,通过display的css样式控制元素隐藏
v-if是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁和重建
v-if也是惰性的:如果在初次渲染时条件值为true,则不会做任何事。条件区块只有当条件首次变为true时才被渲染
总的来说,v-if有更高的切换开销,而v-show有更高的初始渲染开销。因此,如果需要频繁切换,则使用v-show较好;
如果在运行时绑定条件很少改变,则v-if会更合适
*/
</script>
<template>
<div>
<h1 id="ha" v-if="flag">vue is awesome!</h1>
<h1 id="hb" v-else>oh no!</h1>
<h1 id="hc" v-show="flag">hahaha!</h1>
<button @click="flag = !flag">toggle</button>
</div>
</template>
<style scoped>
</style>
②列表渲染
<script setup>
import { reactive,ref } from 'vue';
let pro = ref("产品")
let items = reactive([
{
id: "item1",
message: "薯片"
},
{
id: "item2",
message: "可乐"
},
{
id: "item3",
message: "炸鸡"
}
])
</script>
<template>
<div>
<ul>
<li v-for="(item,index) in items" v-bind:key="item.id">
{{ pro }} {{index+1}} {{ item.message }}
</li>
</ul>
</div>
</template>
<style scoped>
</style>
11.6.4:双向绑定
<script setup>
/*
单向绑定 v-bind:响应式数据发生变化时,更新DOM树;用户的操作如果造成页面内容的改变不会影响响应式数据
双向绑定 v-model:页面上的数据由于用户的操作造成了改变,也会同步修改对应的响应式数据
双向绑定一般都用于表单标签
双向绑定也有人称呼为收集表单信息的命令
v-model:value="数据" 双向绑定
v-model:value 一般都省略:value
*/
import {ref,reactive} from 'vue'
let user = reactive({
userName:"",
userPwd:"",
intro:"",
pro:""
})
let hbs=ref([])
// 数据的清空
function clearForm(){
user.userName=""
user.userPwd=""
user.intro=""
user.pro=""
hbs.value.splice(0,hbs.value.length)
}
</script>
<template>
<div>
<input type="text" v-model="user.userName"><br>
<input type="text" v-model="user.userPwd"><br>
爱好:
唱 <input type="checkbox" v-model="hbs" value="sing">
跳 <input type="checkbox" v-model="hbs" value="dance">
rap <input type="checkbox" v-model="hbs" value="rap">
<br>
简介:<textarea v-model="user.intro"></textarea>
<br>
籍贯:
<select v-model="user.pro">
<option value="1">京</option>
<option value="2">津</option>
<option value="3">冀</option>
</select>
<br>
<button @click="clearForm()">清空数据</button>
<br>
{{ user }}
<br>
{{ hbs }}
</div>
</template>
<style scoped>
</style>
11.6.5:属性计算
<script setup>
import {reactive,computed} from 'vue'
/* 计算属性 */
const author = reactive({
name:"张三",
books:["java从入门到精通","算法","Mysql"]
})
// 通过方法返回数据,每使用一次,执行一次
function hasBooks(){
console.log("hasbooks")
return author.books.length > 0 ? "是" : "否"
}
// 通过计算属性获得数据,每次使用时,如果和上次使用时的数据没有变化,则直接使用上一次的结果
let bookMessage = computed(()=>{
console.log("bookMessage")
return author.books.length > 0 ? "是" : "否"
})
</script>
<template>
<div>
<p>作者:{{ author.name }}</p>
是否出版过图书:{{ hasBooks() }}<br>
是否出版过图书:{{ bookMessage }}<br>
</div>
</template>
<style scoped>
</style>
11.6.6:数据监听器
<script setup>
import {ref,reactive,watch,watchEffect} from 'vue'
let fullname = ref("")
let firstname = ref("李")
let lastname = reactive({
name:"小海"
})
// 任何的响应式数据,如果想监听,直接监听即可,无需将要监听的响应式数据作为参数
watchEffect(()=>{
fullname.value = firstname.value + lastname.name
})
// // watch函数监听一个ref的响应式数据
// watch(firstname,(newValue,oldValue)=>{
// console.log(`${oldValue}变为${newValue}`)
// fullname.value = newValue+lastname.name
// })
// // watch函数监听一个reactive响应式数据,专门监听reactive响应式数据中的一个数据
// watch(()=>lastname.name,(newValue,oldValue)=>{
// console.log(`${oldValue}变为${newValue}`)
// fullname.value = firstname.value + newValue
// })
// // watch函数监听一个reactive响应式数据,专门监听reactive响应式数据中的一个数据
// watch(() => lastname.name, (newValue, oldValue) => {
// // newValue和oldValue都一样,都是lastname
// fullname.value = firstname.value + newValue
// },{deep:true,immediate:true})
</script>
<template>
<div>
姓氏:<input type="text" v-model="firstname"><br>
名字:<input type="text" v-model="lastname.name"><br>
全名:{{ fullname }}
</div>
</template>
<style scoped>
</style>
11.6.7:Vue生命周期
每个Vue组件实例在创建时都要经历一系列的初始化步骤,比如设置好数据侦听、编译模板、挂载实例到DOM,以及在数据改变时更新DOM。在此过程中,它也会运行被称为“生命周期钩子的函数”,让开发者有机会在特定阶段运行自己的代码
<script setup>
import {ref,onBeforeMount,onMounted,onBeforeUpdate,onUpdated} from 'vue'
// 挂载之前
onBeforeMount(()=>{
console.log("------------onBeforeMount-----------")
console.log(document.getElementById("s1"))
})
// 挂载完成
onMounted(()=>{
console.log("------------onMounted-----------")
console.log(document.getElementById("s1"))
})
// 更新之前
onBeforeUpdate(()=>{
console.log("------------onBeforeUpdate-----------")
let ele = document.getElementById("s1")
console.log(message.value,ele.innerText)
})
// 更新之后
onUpdated(()=>{
console.log("------------onUpdated-----------")
console.log(message.value, ele.innerText)
})
let message = ref(1)
</script>
<template>
<div>
<span id="s1">{{ message }}</span>
<button @click="message++">changeMessage</button>
</div>
</template>
<style scoped>
</style>
11.6.8:Vue组件
组件允许我们将UI划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。组件就是实现应用中局部功能代码和资源的集合!
在实际应用中,组件常常被组织成层层嵌套的树状结构;
组件之间数据的传递
①Header
<script setup>
</script>
<template>
<div>
欢迎:xxx <a href="#">退出登录</a>
</div>
</template>
<style scoped>
</style>
②Navigator
<script setup>
// 向父组件发送参数
// defineEmits:用于定义向父组件提交数据的事件以及正式的提交数据
import {defineEmits} from 'vue'
// 定义一个向父组件提交数据的事件,事件名称自定义
const emits = defineEmits(["sendMenu"])
// 提交数据的方法
function send(data){
emits("sendMenu",data)
}
</script>
<template>
<div>
<ul>
<li @click="send('学员管理')">学员管理</li>
<li @click="send('图书管理')">图书管理</li>
<li @click="send('请假管理')">请假管理</li>
<li @click="send('考试管理')">考试管理</li>
<li @click="send('班级管理')">班级管理</li>
<li @click="send('教师管理')">教师管理</li>
</ul>
</div>
</template>
<style scoped></style>
③Content
<script setup>
// 接收父组件的参数
import {defineProps} from 'vue'
defineProps({
message:String
})
</script>
<template>
<div>
{{ message }}
</div>
</template>
<style scoped></style>
④Vue
<script setup>
import {ref} from 'vue'
/* 引入多个vue组件 */
import Header from './components/Header.vue'
import Navigator from './components/Navigator.vue'
import Content from './components/Content.vue'
let menu = ref("")
function receiver(data){
menu.value = data
}
</script>
<template>
<div>
<Header class="header"></Header>
<Navigator @sendMenu="receiver" class="navigator"></Navigator>
<Content class="content" :message="menu"></Content>
</div>
</template>
<style scoped>
.header{
height: 80px;
border: 1px solid red;
}
.navigator{
width: 15%;
height: 500px;
border: 1px solid green;
float: left;
}
.content{
width: 83%;
height: 500px;
border: 1px solid blue;
float: right;
}
</style>
11.7:Vue3路由机制router
11.7.1:路由简介
1.什么是路由?
- 定义:路由就是根据不同的URL地址展示不同的内容或页面
- 通俗理解:路由就像是一个地图,我们要去不同的地方,需要通过不同的路线进行导航
2.路由的作用?
- 单页应用程序(SPA)中,路由可以实现不同视图之间的无刷新切换,提升用户体验
- 路由还可以实现页面的认证 和权限控制,保护用户的隐私和安全
- 路由还可以利用浏览器的前进和后退,帮助用户更好的回到之前访问过的页面
11.7.2:编程式路由
<script setup>
import {useRouter} from 'vue-router'
import {ref} from 'vue'
const router = useRouter()
let mypath = ref("")
function goMyPage(){
router.push(mypath.value)
}
</script>
<template>
<div>
<!-- 声明式路由 -->
<router-link to="home">Home</router-link><br>
<router-link to="list">List</router-link><br>
<router-link to="update">Update</router-link><br>
<router-link to="add">Add</router-link><br>
<!-- 编程式路由 -->
<button @click="goMyPage()">Go</button><input type="text" v-model="mypath">
<hr>
<router-view></router-view>
</div>
</template>
<style scoped>
</style>
11.7.3:路由传参(useRoute)
路径参数
- 在路径中使用一个动态字段来实现,我们称之为路径参数
ShowDetail
<script setup>
/* 接收传递过来的路径参数
useRoute函数用来接收参数
route.params表示路径参数
*/
import {useRoute} from 'vue-router'
import {ref,onUpdated} from 'vue'
let languageId = ref(0)
let languageName = ref("")
let route = useRoute()
languageId.value = route.params.id
languageName.value = route.params.language
onUpdated(()=>{
languageId.value = route.params.id
languageName.value = route.params.language
})
</script>
<template>
<div>
<h1>ShowDetail接收路径参数</h1>
<h3>{{ languageId }}{{ languageName }}是世界上最好的语言</h3>
</div>
</template>
<style scoped></style>
ShowDetail2
<script setup>
/* 接收传递过来的路径参数
useRoute函数用来接收参数
route.params表示路径参数
route.query表示键值对参数
*/
import {useRoute} from 'vue-router'
import {ref,onUpdated} from 'vue'
let languageId = ref(0)
let languageName = ref("")
let route = useRoute()
languageId.value = route.query.id
languageName.value = route.query.language
onUpdated(()=>{
languageId.value = route.query.id
languageName.value = route.query.language
})
</script>
<template>
<div>
<h1>ShowDetail2接收键值对参数</h1>
<h3>{{ languageId }}{{ languageName }}是世界上最好的语言</h3>
</div>
</template>
<style scoped></style>
App.vue
<script setup>
import {useRouter} from 'vue-router'
import {ref} from 'vue'
const router = useRouter()
function ShowDetail(id,language){
router.push(`/showDetail/${id}/${language}`)
}
function showDetail2(id,language){
router.push(`/showDetail2?id=${id}&language=${language}`)
}
</script>
<template>
<div>
<router-link to="/showDetail/1/java">声明式路由路径传参</router-link>
<button @click="ShowDetail(2,'PHP')">编程式路由路径传参</button>
<hr>
<router-link to="/showDetail2?id=1&language=java">声明式路由键值对传参</router-link>
<router-link v-bind:to="{path:'showDetail2',query:{id:2,language:'php'}}">声明式路由键值对传参</router-link>
<button @click="showDetail2(3, 'Python')">编程式路由键值对传参</button>
<hr>
<router-view></router-view>
</div>
</template>
<style scoped>
</style>
11.7.4:路由守卫
在Vue 3中,路由守卫是用于在路由切换期间进行一些特殊任务的回调函数。路由守卫可以用于许多任务,例如验证用户是否已登录、在路由切换前提供确认提示、请求数据等。Vue 3为路由守卫提供了全面的支持,并提供了以下几种类型的路由守卫:
- 全局前置守卫:在路由切换前被调用,可以用于验证用户是否已登录、中断导航、请求数据等。
- 全局后置守卫:在路由切换之后被调用,可以用于处理数据、操作DOM、记录日志等。
- 守卫代码的位置:在router.js中
11.8:Vue3数据交互axios
11.8.1:Promise
①普通函数和回调函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>promise</title>
<script>
// // 1、普通函数
// function fun1(){
// console.log("fun1 invoked")
// }
// console.log("code1 invoked")
// // 函数的调用
// fun1()
// console.log("code2 invoked")
// 2、回调函数
/*
回调函数是一种基于事件的自动调用函数
回调函数其它的代码不会等待回调函数执行完毕
回调函数是一种未来会执行的函数,回调函数以外的其它代码会继续执行
*/
function fun1(){
setTimeout(function(){
console.log("fun1 invoked")
},2000)
}
console.log("code1 invoked")
fun1()
console.log("code2 invoked")
</script>
</head>
<body>
</body>
</html>
②Promise简介
前端中的异步编程技术,类似java中的多线程+线程结果回调!
Promise,简单来说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它这里可以获取异步操作的信息。Promise提供统一的API,各种异步操作都可以使用同样的方法进行处理
Promise 是异步编程的一种解决方案,其实是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法
Promise对象有以下两个特点
(1)对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:pending(进行中)、Resolved(已完成,又称Fulfilled)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
③Promise基本用法
ES6规定,Promise对象是一个构造函数,用来生成Promise实例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>promise2</title>
<script>
/*
resolve函数:在回调函数中如果使用resolve方法,promise会由pending转换为resolved
resolve函数:在回调函数中如果使用reject方法,promise会由pending转换为reject
*/
let promise = new Promise(function (resolve,reject){
console.log("function invoked")
// resolve("haha")
// reject("on on")
throw new Error('error message')
})
console.log("other code1")
// then函数:等待promise对象状态发生改变时才会执行的代码
promise.then(
function(value){
// promise转换为resolved状态时会执行的代码
console.log("promise success:" + value)
}
).catch(
function(value){
// 当promise状态是reject时或者promise出现异常时会执行的函数
console.log("promise fail:" + value)
}
)
console.log("other code2")
</script>
</head>
<body>
</body>
</html>
④async和await
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>async && await</title>
<script>
/*
async帮助我们使用简洁的语法获得一个promise对象
let promise = new Promise(function(){})
async function aaa(){}
作用:
①async用户标识的async函数的返回结果就是一个promise对象
②方法如果正常return结果,promise状态就是resolved,return后的结果就是成功状态的返回值
③方法中出现了异常,则返回的promise就是一个失败状态
④async函数返回的结果如果是一个promise,则状态由内部的promise决定
await帮助我们获取promise成功状态的返回值的关键字
作用
①await右边如果是一个普通值,则直接返回该值;如果右边是promise,返回promise
成功状态的结果
let res = await "张三"
let res = await Promise.resolve("张三")
res = "张三"
②await右边如果是一个失败状态的promise,那么await会直接抛异常
③await关键字必须在async修饰的函数中使用,async函数中可以没有await
④在当前代码块内部,await后边的代码会等待await执行完毕继续执行
*/
async function fun1() {
return 10
// throw new Error("something wrong")
// let promise = Promise.resolve("heihei")
// return promise
}
async function fun2(){
// let res = await fun1()
try{
let res = await Promise.reject("something wrong")
}catch(e){
console.log("catch got" + e)
}
console.log("await got:" + res)
}
fun2()
// async function fun1(){
// // return 10
// // throw new Error("something wrong")
// let promise = Promise.resolve("heihei")
// return promise
// }
// let promise = fun1()
// promise.then(
// function(value){
// console.log("success:" + value)
// }
// ).catch(
// function(value){
// console.log("fail:" + value)
// }
// )
</script>
</head>
<body>
</body>
</html>
11.8.2:Axios介绍
ajax
- AJAX=Asynchronous JavaScript and XML(异步的JavaScript和XML)
- AJAX不是新的编程语言,而是一种使用现有标准的新方法
- AJAX最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容
- AJAX不需要任何浏览器插件,但需要客户允许JavaScript在浏览器上执行
- XMLHttpRequest只是实现Ajax的一种方式
11.8.3:Axios案例
1、案例需求:请求后台获取随机土味情话
<script setup>
import axios from 'axios'
import {ref,reactive} from 'vue'
let message = reactive({
"code": 1,
"content": "我爱你就够了!"
})
function getLoveMessage(){
// 使用Axios发送请求获取土味情话
/*
axios({设置请求参数})
请求三要素:
①请求的url
②请求的方式
③请求的参数 keyvalue json ... ...
*/
let promise = axios({
method:"get",
url:"https://api.uomg.com/api/rand.qinghua?format=json",
data:{
// 这里的数据会放入请求体,前提是请求方式得是post
},
params:{
// 以键值对的方式将数据放入url后
format:'json'
}
})
promise.then(
function(response){
console.log(response)
/*
response:响应结果对象
data:服务端响应回来的数据
status:响应状态码 200
statusText:响应状态描述 OK
headers:本次响应的所有响应头
config:本次请求的配置信息
request:本次请求发送时所使用的XMLHttpRequest对象
*/
console.log(response.data)
console.log(response.data.code)
console.log(response.data.content)
// message.content = response.data.content
// message.code = response.data.code
Object.assign(message,response.data)
}
).catch(
function(){
}
)
}
</script>
<template>
<div>
<h1 v-text="message.content"></h1>
<button @click="getLoveMessage()">变</button>
</div>
</template>
<style scoped>
</style>
11.8.4:Axios中get和post方法
<script setup>
import axios from 'axios'
import {ref,reactive} from 'vue'
let message = reactive({
"code": 1,
"content": "你不爱我又如何,我爱你就够了!"
})
function getLoveWords(){
// 发送get请求的方法
// axios.get(url)
// axios.get(url,{请求的其它信息})
// axios.get(url,{params:{键值对参数},header:{设置一些特殊的请求头}})
// axioa.post(url,{要放入请求体中的JSON串},{请求的其它信息})
return axios.get(
"https://api.uomg.com/api/rand.qinghua",
{
params:{
format:"json",
username:"xxxxx",
userpwd:"lisi"
},
headers:{
}
}
)
}
async function getLoveMessage(){
let {data} = await getLoveWords()
Object.assign(message,data)
}
</script>
<template>
<div>
<h1 v-text="message.content"></h1>
<button @click="getLoveMessage()">变</button>
</div>
</template>
<style scoped>
</style>
11.8.5:Axios拦截器
如果想在Axios发送请求之前,或者是数据响应回来在执行then方法之前做一些额外的工作,可以通过拦截器完成
axios.js
import axios from 'axios'
// 使用axios函数创建一个可以发送请求的实例对象
const instance = axios.create({})
// 请求的基础路径
baseURL:"https://api.uomg.com/"
// 设置请求拦截器
instance.interceptors.request.use(
(config)=>{
console.log("请求前拦截器")
// 请求之前,设置请求信息的方法
config.headers.Accept="application/json,text/plain,text/html,*/*"
// 设置完毕之后,必须返回config
return config
},
error=>{
console.log("请求前拦截器异常方法")
// 返回一个失败状态的promise
return Promise.reject("something wrong")
}
)
// 设置响应拦截器
instance.interceptors.response.use(
function(response){
// 响应状态码为200时要执行的方法
// 处理相应的数据
// 最后要返回response
console.log("response success:")
console.log(response)
return response
},
function(error){
console.log("response fail:")
console.log(error)
// 最后一定要响应一个promise
return Promise.reject("something wrong")
}
)
// 使用默认导出暴露instance
export default instance
App.vue
<script setup>
import axios from './axios'
import {ref,reactive} from 'vue'
let message = reactive({
"code": 1,
"content": "你不爱我又如何,我爱你就够了!"
})
function getLoveWords(){
let promise = axios.get("api/rand.qinghua?fromat=json")
return promise
}
async function getLoveMessage(){
let {data} = await getLoveWords()
Object.assign(message,data)
}
</script>
<template>
<div>
<h1 v-text="message.content"></h1>
<button @click="getLoveMessage()">变</button>
</div>
</template>
<style scoped>
</style>
11.9:Vue3状态管理Pinia
11.9.1:Pinia介绍
如何实现多个组件之间的数据传递?
- 方式一:组件传参
- 方式二:路由传参
- 方式三:通过Pinia状态管理定义共享数据
当我们有多个组件共享一个共同的状态(数据源)时,多个视图可能都依赖于同一份状态。来自不同视图的交互也可能需要更改同一份状态。虽然我们的手动状态管理解决方案(props,组件间通信,模块化)在简单的场景中已经足够了,但是在大规模的生产应用中还有很多其他事项需要考虑:
- 更强的团队协作约定
- 与Vue DevTools集成,包括时间轴、组件内部审查和时间旅行调试
- 模块热更新(HMR)
- 服务端渲染支持
Pinia就是一个实现了上述需求的状态管理库,由Vue核心团队维护,对Vue2和Vue3都可用。
11.9.2:Pinia基本用法
①依赖安装
②使用
List.vue
<script setup>
//导入pinia数据
import { definedPerson } from '../store/store.js'
let person = definedPerson()
</script>
<template>
<div>
<h1>用于展示pinia数据</h1>
读取姓名:{{ person.username }}<br>
读取年龄:{{ person.age }}<br>
爱好数量:{{ person.getHobbiesCount }}
</div>
</template>
<style scoped>
</style>
Operate.vue
<script setup>
//导入pinia数据
import { definedPerson } from '../store/store.js'
let person = definedPerson()
</script>
<template>
<div>
<h1>用于修改pinia数据</h1>
名字:<input type="text" v-model="person.username"><br>
年龄:<input type="text" v-model="person.age"><br>
<span>{{ person.getAge }}</span>
爱好:
吃饭<input type="checkbox" value="吃饭" v-model="person.hobbies">
睡觉<input type="checkbox" value="睡觉" v-model="person.hobbies">
打豆豆<input type="checkbox" value="打豆豆" v-model="person.hobbies">
唱歌<input type="checkbox" value="唱歌" v-model="person.hobbies">
跳舞<input type="checkbox" value="跳舞" v-model="person.hobbies">
<br>
<button @click="person.doubleAge()">年龄加倍</button><br>
<!-- 恢复默认值 -->
<button @click="person.$reset()">恢复默认</button><br>
<!-- 一次性修改多个属性值 -->
<button @click="person.$patch({username:'奥特曼',age:20,hobbies:['晒太阳','打怪兽']})">变身奥特曼</button>
<hr>
展示一下person中的数据:{{ person }}
</div>
</template>
<style scoped>
</style>
router.js
import {createRouter,createWebHashHistory} from 'vue-router'
import List from '../components/List.vue'
import Operate from '../components/Operate.vue'
const router = createRouter({
history:createWebHashHistory(),
routes:[
{
path:"/list",
component:List
},
{
path:"/operate",
components:{
default:Operate,
list:List
}
},
{
path:"/",
component:Operate
}
]
})
export default router
store.js
// 定义共享的pinia数据
import {defineStore} from 'pinia'
// 定义一个Person共享
export const definedPerson = defineStore(
{
id:"personPinia", //当前数据的id,必须全局唯一
state:()=>{
return{
username:"zhangsan",
age:0,
hobbies:["唱歌","跳舞"]
}
}, //状态其实就是响应式数据
getters:{//专门定义一个数据或者是使用数据计算结果的一些函数,这里函数不要修改数据
getAge(){
return this.age
},
getHobbiesCount(){
return this.hobbies.length
},
actions:{//专门定义一些修改数据的函数
doubleAge(){
this.age = this.age*2
}
}
}
}
)
main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './routers/router'
// 开启全局的Pinia功能
import {createPinia} from 'pinia'
let pinia = createPinia()
let app = createApp(App)
app.use(router)
app.use(pinia)
app.mount('#app')
App.vue
<script setup>
</script>
<template>
<div>
<router-link to="/operate">operate</router-link><br>
<router-link to="/list">list</router-link><br>
<hr>
<router-view></router-view>
</div>
</template>
<style scoped>
</style>
11.10:Element-plus组件库
11.10.1:介绍
Element-plus是一套基于Vue 3的开源UI组件库,是由饿了吗前端团队开发的升级版本Element UI。Element-plus提供了丰富的UI组件、易于使用的API接口和灵活的主体定制功能,可以帮助开发者快速构建高质量的Web应用程序。