JAVAWeb--前端工程化

一、前端工程化开篇

1.1 什么是前端工程化

前端工程化是使用软件工程的方法单独解决前端的开发流程中模块化、组件化、规范化、自动化的问题,其主要目的为了提高效率和降低成本。

1.2 前端工程化实现技术栈

前端工程化实现的技术栈有很多,我们采用ES6+nodejs+npm+Vite+VUE3+router+pinia+axios+Element-plus组合来实现

  • ECMAScript6 VUE3中大量使用ES6语法

  • Nodejs 前端项目运行环境

  • npm 依赖下载工具

  • vite 前端项目构建工具

  • VUE3 优秀的渐进式前端框架

  • router 通过路由实现页面切换

  • pinia 通过状态管理实现组件数据传递

  • axios ajax异步请求封装技术实现前后端数据交互

  • Element-plus 可以提供丰富的快速构建网页的组件仓库

二、ECMA6Script

2.1. es6的介绍

ECMAScript 6,简称ES6,是JavaScript语言的一次重大更新。它于2015年发布,是原来的ECMAScript标准的第六个版本。ES6带来了大量的新特性,包括箭头函数、模板字符串、let和const关键字、解构、默认参数值、模块系统等等,大大提升了JavaScript的开发体验。由于VUE3中大量使用了ES6的语法,所以ES6成为了学习VUE3的门槛之一 ES6对JavaScript的改进在以下几个方面:

  1. 更加简洁:ES6引入了一些新的语法,如箭头函数、类和模板字符串等,使代码更加简洁易懂。

  2. 更强大的功能:ES6引入了一些新的API、解构语法和迭代器等功能,从而使得JavaScript更加强大。

  3. 更好的适用性:ES6引入的模块化功能为JavaScript代码的组织和管理提供了更好的方式,不仅提高了程序的可维护性,还让JavaScript更方便地应用于大型的应用程序。

总的来说,ES6在提高JavaScript的核心语言特性和功能方面取得了很大的进展。由于ES6已经成为了JavaScript的标准,它的大多数新特性都已被现在浏览器所支持,因此现在可以放心地使用ES6来开发前端应用程序。

历史版本:

标准版本 发布时间 新特性
ES1 1997年 第一版 ECMAScript
ES2 1998年 引入setter和getter函数,增加了try/catch,switch语句允许字符串
ES3 1999年 引入了正则表达式和更好的字符串处理
ES4 取消 取消,部分特性被ES3.1和ES5继承
ES5 2009年 Object.defineProperty,JSON,严格模式,数组新增方法等
ES5.1 2011年 对ES5做了一些勘误和例行修订
ES6 2015年 箭头函数、模板字符串、解构、let和const关键字、类、模块系统等
ES2016 2016年 数组.includes,指数操作符(**),Array.prototype.fill等
ES2017 2017年 异步函数async/await,Object.values/Object.entries,字符串填充
ES2018 2018年 正则表达式命名捕获组,几个有用的对象方法,异步迭代器等
ES2019 2019年 Array.prototype.{flat,flatMap},Object.fromEntries等
ES2020 2020年 BigInt、动态导入、可选链操作符、空位合并操作符
ES2021 2021年 String.prototype.replaceAll,逻辑赋值运算符,Promise.any等
... ...

2.2 es6的变量和模板字符串

ES6 新增了letconst,用来声明变量,使用的细节上也存在诸多差异

  • let 和var的差别

    1、let 不能重复声明

    2、let有块级作用域,非函数的花括号遇见let会有块级作用域,也就是只能在花括号里面访问。

    3、let不会预解析进行变量提升

    4、let 定义的全局变量不会作为window的属性

    5、let在es6中推荐优先使用

  • const和var的差异

    1、新增const和let类似,只是const定义的变量不能修改

    2、并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。

模板字符串(template string)是增强版的字符串,用反引号(`)标识

1、字符串中可以出现换行符

2、可以使用 ${xxx} 形式输出变量和拼接变量

2.3 es6的解构表达式

ES6 的解构赋值是一种方便的语法,可以快速将数组或对象中的值拆分并赋值给变量。解构赋值的语法使用花括号 {} 表示对象,方括号 [] 表示数组。通过解构赋值,函数更方便进行参数接受等!

数组解构赋值

  • 可以通过数组解构将数组中的值赋值给变量,语法为:

let [a, b, c] = [1, 2, 3]; //新增变量名任意合法即可,本质是按照顺序进行初始化变量的值
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
  • 该语句将数组 [1, 2, 3] 中的第一个值赋值给 a 变量,第二个值赋值给 b 变量,第三个值赋值给 c 变量。 可以使用默认值为变量提供备选值,在数组中缺失对应位置的值时使用该默认值。例如:

let [a, b, c, d = 4] = [1, 2, 3];
console.log(d); // 4

对象解构赋值

  • 可以通过对象解构将对象中的值赋值给变量,语法为:

let {a, b} = {a: 1, b: 2};
//新增变量名必须和属性名相同,本质是初始化变量的值为对象中同名属性的值
//等价于 let a = 对象.a  let b = 对象.b
  
console.log(a); // 1
console.log(b); // 2
  • 该语句将对象 {a: 1, b: 2} 中的 a 属性值赋值给 a 变量,b 属性值赋值给 b 变量。 可以为标识符分配不同的变量名称,使用 : 操作符指定新的变量名。例如:

let {a: x, b: y} = {a: 1, b: 2};
console.log(x); // 1
console.log(y); // 2

函数参数解构赋值

  • 解构赋值也可以用于函数参数。例如:

function add([x, y]) {
  return x + y;
}
add([1, 2]); // 3
  • 该函数接受一个数组作为参数,将其中的第一个值赋给 x,第二个值赋给 y,然后返回它们的和。

  • ES6 解构赋值让变量的初始化更加简单和便捷。通过解构赋值,我们可以访问到对象中的属性,并将其赋值给对应的变量,从而提高代码的可读性和可维护性。

2.4 es6的箭头函数

ES6 允许使用“箭头” 义函数。语法类似Java中的Lambda表达式

2.4.1 声明和特点
2.4.2 实践和应用场景



    
    Document
    


    
   
2.4.3 rest和spread

rest参数,在形参上使用 和JAVA中的可变参数几乎一样

spread参数,在实参上使用rest

2.5 es6的对象创建和拷贝

2.5.1 对象创建的语法糖

ES6中新增了对象创建的语法糖,支持了class extends constructor等关键字,让ES6的语法和面向对象的语法更加接近

class Person{
      // 属性
      #n;
      age;
      get name(){
          return this.n;
      }
      set name(n){
          this.n =n;
      }
      // 实例方法
      eat(food){
          console.log(this.age+"岁的"+this.n+"用筷子吃"+food)
      }
      // 静态方法
      static sum(a,b){
          return a+b;
      }
      // 构造器
      constructor(name,age){
          this.n=name;
          this.age = age;
​
      }
  }
  let person =new Person("张三",10);
  // 访问对象属性
  // 调用对象方法
  console.log(person.name)
  console.log(person.n)
  person.name="小明"
  console.log(person.age)
  person.eat("火锅")
  console.log(Person.sum(1,2))
​
  class Student extends  Person{
      grade ;
      score ;
      study(){
​
      }
      constructor(name,age ) {
          super(name,age);
      }
  }
​
  let stu =new Student("学生小李",18);
  stu.eat("面条")
2.5.2 对象的深拷贝和浅拷贝

对象的拷贝,快速获得一个和已有对象相同的对象的方式

  • 浅拷贝

  • 深拷贝

2.6 es6的模块化处理

2.6.1模块化介绍

模块化是一种组织和管理前端代码的方式,将代码拆分成小的模块单元,使得代码更易于维护、扩展和复用。它包括了定义、导出、导入以及管理模块的方法和规范。前端模块化的主要优势如下:

  1. 提高代码可维护性:通过将代码拆分为小的模块单元,使得代码结构更为清晰,可读性更高,便于开发者阅读和维护。

  2. 提高代码可复用性:通过将重复使用的代码变成可复用的模块,减少代码重复率,降低开发成本。

  3. 提高代码可扩展性:通过模块化来实现代码的松耦合,便于更改和替换模块,从而方便地扩展功能。

目前,前端模块化有多种规范和实现,包括 CommonJS、AMD 和 ES6 模块化。ES6 模块化是 JavaScript 语言的模块标准,使用 import 和 export 关键字来实现模块的导入和导出。现在,大部分浏览器都已经原生支持 ES6 模块化,因此它成为了最为广泛使用的前端模块化标准. `

  • ES6模块化的几种暴露和导入方式

    1. 分别导出

    2. 统一导出

    3. 默认导出

  • ES6中无论以何种方式导出,导出的都是一个对象,导出的内容都可以理解为是向这个对象中添加属性或者方法

2.6.2 分别导出

JAVAWeb--前端工程化_第1张图片

  • module.js 向外分别暴露成员

//1.分别暴露
// 模块想对外导出,添加export关键字即可!
// 导出一个变量
export const PI = 3.14
// 导出一个函数
export function sum(a, b) {
  return a + b;
}
// 导出一个类
export class Person {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  sayHello() {
    console.log(`Hello, my name is ${this.name}, I'm ${this.age} years old.`);
  }
}
  • app.js 导入module.js中的成员

/* 
    *代表module.js中的所有成员
    m1代表所有成员所属的对象
*/
import * as m1 from './module.js'
// 使用暴露的属性
console.log(m1.PI)
// 调用暴露的方法
let result =m1.sum(10,20)
console.log(result)
// 使用暴露的Person类
let person =new m1.Person('张三',10)
person.sayHello()
  • index.html作为程序启动的入口 导入 app.js



        
                                 

{{headline}}

                   

                   
                           
​            

五、Vue3通过Vite实现工程化

5.1 Vite的介绍

在浏览器支持 ES 模块之前,JavaScript 并没有提供原生机制让开发者以模块化的方式进行开发。这也正是我们对 “打包” 这个概念熟悉的原因:使用工具抓取、处理并将我们的源码模块串联成可以在浏览器中运行的文件。时过境迁,我们见证了诸如 webpack、Rollup 和 Parcel 等工具的变迁,它们极大地改善了前端开发者的开发体验

  • 当我们开始构建越来越大型的应用时,需要处理的 JavaScript 代码量也呈指数级增长。

  • 包含数千个模块的大型项目相当普遍。基于 JavaScript 开发的工具就会开始遇到性能瓶颈:通常需要很长时间(甚至是几分钟!)才能启动开发服务器,即使使用模块热替换(HMR),文件修改后的效果也需要几秒钟才能在浏览器中反映出来。如此循环往复,迟钝的反馈会极大地影响开发者的开发效率和幸福感。

Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写。为什么选 Vite | Vite 官方中文文档前端工程化的作用包括但不限于以下几个方面:

  1. 快速创建项目:使用脚手架可以快速搭建项目基本框架,避免从零开始搭建项目的重复劳动和繁琐操作,从而节省时间和精力。

  2. 统一的工程化规范:前端脚手架可以预设项目目录结构、代码规范、git提交规范等统一的工程化规范,让不同开发者在同一个项目上编写出风格一致的代码,提高协作效率和质量。

  3. 代码模板和组件库:前端脚手架可以包含一些常用的代码模板和组件库,使开发者在实现常见功能时不再重复造轮子,避免因为轮子质量不高带来的麻烦,能够更加专注于项目的业务逻辑。

  4. 自动化构建和部署:前端脚手架可以自动进行代码打包、压缩、合并、编译等常见的构建工作,可以通过集成自动化部署脚本,自动将代码部署到测试、生产环境等。

5.2 Vite创建Vue3工程化项目

5.2.1 Vite+Vue3项目的创建、启动、停止

1 使用命令行创建工程

  • 在磁盘的合适位置上,创建一个空目录用于存储多个前端项目

  • 用vscode打开该目录

  • 在vocode中打开命令行运行如下命令

npm create vite@latest
  • 第一次使用vite时会提示下载vite,输入y回车即可,下次使用vite就不会出现了

  • 注意: 选择vue+JavaScript选项即可

2 安装项目所需依赖

  • cd进入刚刚创建的项目目录

  • npm install命令安装基础依赖

cd ./vue3-demo1
npm install

3 启动项目

  • 查看项目下的package.json

{
  "name": "vue3-demo1",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "bootstrap": "^5.2.3",
    "sass": "^1.62.1",
    "vue": "^3.2.47"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.1.0",
    "vite": "^4.3.2"
  }
}
npm run dev

5 停止项目

  • 命令行上 ctrl+c

5.2.2 Vite+Vue3项目的目录结构

1.下面是 Vite 项目结构和入口的详细说明:

  • public/ 目录:用于存放一些公共资源,如 HTML 文件、图像、字体等,这些资源会被直接复制到构建出的目标目录中。

  • src/ 目录:存放项目的源代码,包括 JavaScript、CSS、Vue 组件、图像和字体等资源。在开发过程中,这些文件会被 Vite 实时编译和处理,并在浏览器中进行实时预览和调试。以下是src内部划分建议:

    1. assets/ 目录:用于存放一些项目中用到的静态资源,如图片、字体、样式文件等。

    2. components/ 目录:用于存放组件相关的文件。组件是代码复用的一种方式,用于抽象出一个可复用的 UI 部件,方便在不同的场景中进行重复使用。

    3. layouts/ 目录:用于存放布局组件的文件。布局组件通常负责整个应用程序的整体布局,如头部、底部、导航菜单等。

    4. pages/ 目录:用于存放页面级别的组件文件,通常是路由对应的组件文件。在这个目录下,可以创建对应的文件夹,用于存储不同的页面组件。

    5. plugins/ 目录:用于存放 Vite 插件相关的文件,可以按需加载不同的插件来实现不同的功能,如自动化测试、代码压缩等。

    6. router/ 目录:用于存放 Vue.js 的路由配置文件,负责管理视图和 URL 之间的映射关系,方便实现页面之间的跳转和数据传递。

    7. store/ 目录:用于存放 Vuex 状态管理相关的文件,负责管理应用程序中的数据和状态,方便统一管理和共享数据,提高开发效率。

    8. utils/ 目录:用于存放一些通用的工具函数,如日期处理函数、字符串操作函数等。

  • vite.config.js 文件:Vite 的配置文件,可以通过该文件配置项目的参数、插件、打包优化等。该文件可以使用 CommonJS 或 ES6 模块的语法进行配置。

  • package.json 文件:标准的 Node.js 项目配置文件,包含了项目的基本信息和依赖关系。其中可以通过 scripts 字段定义几个命令,如 dev、build、serve 等,用于启动开发、构建和启动本地服务器等操作。

  • Vite 项目的入口为 src/main.js 文件,这是 Vue.js 应用程序的启动文件,也是整个前端应用程序的入口文件。在该文件中,通常会引入 Vue.js 及其相关插件和组件,同时会创建 Vue 实例,挂载到 HTML 页面上指定的 DOM 元素中。

2.vite的运行界面

  • 在安装了 Vite 的项目中,可以在 npm scripts 中使用 vite 可执行文件,或者直接使用 npx vite 运行它。下面是通过脚手架创建的 Vite 项目中默认的 npm scripts:(package.json)

{
  "scripts": {
    "dev": "vite", // 启动开发服务器,别名:`vite dev`,`vite serve`
    "build": "vite build", // 为生产环境构建产物
    "preview": "vite preview" // 本地预览生产构建产物
  }
}
  • 运行设置端口号:(vite.config.js)

//修改vite项目配置文件 vite.config.js
export default defineConfig({
  plugins: [vue()],
  server:{
    port:3000
  }
})
5.2.3 Vite+Vue3项目组件(SFC入门)

什么是VUE的组件?

  • 一个页面作为整体,是由多个部分组成的,每个部分在这里就可以理解为一个组件

  • 每个.vue文件就可以理解为一个组件,多个.vue文件可以构成一个整体页面

  • 组件化给我们带来的另一个好处就是组件的复用和维护非常的方便

什么是.vue文件?

  • 传统的页面有.html文件.css文件和.js文件三个文件组成(多文件组件)

  • vue将这文件合并成一个.vue文件(Single-File Component,简称 SFC,单文件组件)

  • .vue文件对js/css/html统一封装,这是VUE中的概念 该文件由三个部分组成

    2 vue3响应式数据入门

    
    
    
    

    3 vue3 setup函数和语法糖

    • 位置:src/App.vue

    
    
    5.2.5 Vite+Vue3关于样式的导入方式
    1. 全局引入main.js

      import './style/reset.css' //书写引入的资源的相对路径即可!
    2. vue文件script代码引入

      import './style/reset.css'
    3. Vue文件style代码引入

      @import './style/reset.css'
    5.2.6 关于JS和TS选择的问题

    TS是JS的一个超集,使用TS之后,JS的语法更加的像JAVA,实际开发中用的确实更多,那么这里为什么我们没有立即使用TS进行开发,原因如下

    1 为了降低难度,提高前端工程化的效率

    2 对于学JAVA的我们来说,学习TS非常容易,但是还是需要一些时间

    3 TS不是非学不可,不用TS仍然可以正常开发工程化的前端项目

    4 尚硅谷已经发布了TS的专项课程,请大家在B站上自行搜索 "尚硅谷 TS"

    5 建议大家先学完完整的前端工程化内容,然后再根据需求单独学习TS即可

    六、Vue3视图渲染技术

    6.1 模版语法

    Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作。

    6.1.1 插值表达式和文本渲染

    插值表达式:最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 ,即双大括号{{}}

    • 插值表达式是将数据渲染到元素的指定位置的手段之一

    • 插值表达式不绝对依赖标签,其位置相对自由

    • 插值表达式中支持javascript的运算表达式

    • 插值表达式中也支持函数的调用

    
    
    
    
    

    为了渲染双标中的文本,我们也可以选择使用v-textv-html命令

    • v-*** 这种写法的方式使用的是vue的命令

    • v-***的命令必须依赖元素,并且要写在元素的开始标签中

    • v-***指令支持ES6中的字符串模板

    • 插值表达式中支持javascript的运算表达式

    • 插值表达式中也支持函数的调用

    • v-text可以将数据渲染成双标签中间的文本,但是不识别html元素结构的文本

    • v-html可以将数据渲染成双标签中间的文本,识别html元素结构的文本

    
    
    
    
    
    6.1.2 Attribute属性渲染

    想要渲染一个元素的 attribute,应该使用 v-bind指令

    • 由于插值表达式不能直接放在标签的属性中,所有要渲染元素的属性就应该使用v-bind

    • v-bind可以用于渲染任何元素的属性,语法为 v-bind:属性名='数据名', 可以简写为 :属性名='数据名'

    6.1.3 事件的绑定

    我们可以使用 v-on 来监听 DOM 事件,并在事件触发时执行对应的 Vue的JavaScript代码。

    • 用法:v-on:click="handler" 或简写为 @click="handler"

    • vue中的事件名=原生事件名去掉on 前缀 如:onClick --> click

    • handler的值可以是方法事件处理器,也可以是内联事件处理器

    • 绑定事件时,可以通过一些绑定的修饰符,常见的事件修饰符如下

      • .once:只触发一次事件。[重点]

      • .prevent:阻止默认事件。[重点]

      • .stop:阻止事件冒泡。

      • .capture:使用事件捕获模式而不是冒泡模式。

      • .self:只在事件发送者自身触发时才触发事件。

    6.2 响应式基础

    此处的响应式是指 : 数据模型发生变化时,自动更新DOM树内容,页面上显示的内容会进行同步变化,vue3的数据模型不是自动响应式的,需要我们做一些特殊的处理

    6.2.1 响应式需求案例

    需求:实现 + - 按钮,实现数字加一减一

    
    
     
    
    
    
    6.2.2 响应式实现关键字ref

    ref 可以将一个基本类型的数据(如字符串,数字等)转换为一个响应式对象。 ref 只能包裹单一元素

    • 在上面的例子中,我们使用 ref 包裹了一个数字,在代码中给这个数字加 1 后,视图也会跟着动态更新。需要注意的是,由于使用了 ref,因此需要在访问该对象时使用 .value 来获取其实际值。

    6.2.3 响应式实现关键字reactive

    我们可以使用 reactive() 函数创建一个响应式对象或数组:

    对比ref和reactive:

    • 使用 ref 适用于以下开发场景:

      • 包装基本类型数据:ref 主要用于包装基本类型数据(如字符串、数字等),即只有一个值的数据,如果你想监听这个值的变化,用 ref 最为方便。在组件中使用时也很常见。

      • 访问方式简单:ref 对象在访问时与普通的基本类型值没有太大区别,只需要通过 .value 访问其实际值即可。

    • 使用 reactive 适用于以下开发场景:

      • 包装复杂对象:reactive 可以将一个普通对象转化为响应式对象,这样在数据变化时会自动更新界面,特别适用于处理复杂对象或者数据结构。

      • 需要递归监听的属性:使用 reactive 可以递归追踪所有响应式对象内部的变化,从而保证界面的自动更新。

    • 综上所述,ref 适用与简单情形下的数据双向绑定,对于只有一个字符等基本类型数据或自定义组件等情况,建议可以使用 ref;而对于对象、函数等较为复杂的数据结构,以及需要递归监听的属性变化,建议使用 reactive。当然,在实际项目中根据需求灵活选择也是十分必要的。

    6.2.4 扩展响应式关键字toRefs 和 toRef

    toRef基于reactive响应式对象上的一个属性,创建一个对应的 ref响应式数据。这样创建的 ref 与其源属性保持同步:改变源属性的值将更新 ref 的值,反之亦然。toRefs将一个响应式对象多个属性转换为一个多个ref数据,这个普通对象的每个属性都是指向源对象相应属性的 ref。每个单独的 ref 都是使用 toRef() 创建的。

    案例:响应显示reactive对象属性

    
    
     
    
    
    
    

    6.3 条件和列表渲染

    6.3.1 条件渲染

    v-if 条件渲染

    • v-if='表达式'只会在指令的表达式返回真值时才被渲染

    • 也可以使用 v-elsev-if 添加一个“else 区块”。

    • 一个 v-else 元素必须跟在一个 v-if 元素后面,否则它将不会被识别。

    v-show条件渲染扩展:

    • 另一个可以用来按条件显示一个元素的指令是 v-show。其用法基本一样:

    • 不同之处在于 v-show 会在 DOM 渲染中保留该元素;v-show 仅切换了该元素上名为 display 的 CSS 属性。

    • v-show 不支持在

      6.5 属性计算

      模板中的表达式虽然方便,但也只能用来做简单的操作。如果在模板中写太多逻辑,会让模板变得臃肿,难以维护。比如说,我们有这样一个包含嵌套数组的对象:

      • 这里的模板看起来有些复杂。我们必须认真看好一会儿才能明白它的计算依赖于 author.books。更重要的是,如果在模板中需要不止一次这样的计算,我们可不想将这样的代码在模板里重复好多遍。

      因此我们推荐使用计算属性来描述依赖响应式状态的复杂逻辑。这是重构后的示例:

      • 我们在这里定义了一个计算属性 publishedBooksMessagecomputed() 方法期望接收一个 getter 函数,返回值为一个计算属性 ref。和其他一般的 ref 类似,你可以通过 publishedBooksMessage.value 访问计算结果。计算属性 ref 也会在模板中自动解包,因此在模板表达式中引用时无需添加 .value

      • Vue 的计算属性会自动追踪响应式依赖。它会检测到 publishedBooksMessage 依赖于 author.books,所以当 author.books 改变时,任何依赖于 publishedBooksMessage 的绑定都会同时更新。

      计算属性缓存 vs 方法

      • 若我们将同样的函数定义为一个方法而不是计算属性,两种方式在结果上确实是完全相同的,然而,不同之处在于计算属性值会基于其响应式依赖被缓存。一个计算属性仅会在其响应式依赖更新时才重新计算。这意味着只要 author.books 不改变,无论多少次访问 publishedBooksMessage 都会立即返回先前的计算结果!

      6.6 数据监听器

      计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。我们可以使用 watch 函数在每次响应式状态发生变化时触发回调函数:

      • watch主要用于以下场景:

        • 当数据发生变化时需要执行相应的操作

        • 监听数据变化,当满足一定条件时触发相应操作

        • 在异步操作前或操作后需要执行相应的操作

      监控响应式数据(watch):

      
      
       
      
      

      监控响应式数据(watchEffect):

      • watchEffect默认监听所有的响应式数据

      
      
       
      
      

      watch vs. watchEffect

      • watchwatchEffect 都能响应式地执行有副作用的回调。它们之间的主要区别是追踪响应式依赖的方式:

        • watch 只追踪明确侦听的数据源。它不会追踪任何在回调中访问到的东西。另外,仅在数据源确实改变时才会触发回调。watch 会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。

        • watchEffect,则会在副作用发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式属性。这更方便,而且代码往往更简洁,但有时其响应性依赖关系会不那么明确。

      6.7. Vue生命周期

      6.7.1 生命周期简介

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

      • 周期图解:

      • 常见钩子函数

        • onMounted() 注册一个回调函数,在组件挂载完成后执行。

        • onUpdated() 注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。

        • onUnmounted() 注册一个回调函数,在组件实例被卸载之后调用。

        • onBeforeMount() 注册一个钩子,在组件被挂载之前被调用。

        • onBeforeUpdate() 注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。

        • onBeforeUnmount() 注册一个钩子,在组件实例被卸载之前调用。

      6.7.2 生命周期案例

      6.8 Vue组件

      6.8.1 组件基础

      组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。组件就是实现应用中局部功能代码和资源的集合!在实际应用中,组件常常被组织成层层嵌套的树状结构:

      • 这和我们嵌套 HTML 元素的方式类似,Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。

      传统方式编写应用:

      组件方式编写应用:

      • 组件化:对js/css/html统一封装,这是VUE中的概念

      • 模块化:对js的统一封装,这是ES6中的概念

      • 组件化中,对js部分代码的处理使用ES6中的模块化

      6.8.2 组件化入门案例

      案例需求: 创建一个页面,包含头部和菜单以及内容显示区域,每个区域使用独立组建!

      1 准备vue项目

      npm create vite
      cd vite项目
      npm install

      2 安装相关依赖

      npm install sass
      npm install bootstrap

      3 创建子组件 在src/components文件下 vscode需要安装Vetur插件,这样vue文件有快捷提示

      • Header.vue

      
      
      
      
      
      • Navigator.vue

      • Content.vue

      • App.vue 入口组件App引入组件

      
      
      
      
      

      4 启动测试

      npm run dev
      6.8.3 组件之间传递数据
      6.8.3.1 父传子

      Vue3 中父组件向子组件传值可以通过 props 进行,具体操作如下:

      1. 首先,在父组件中定义需要传递给子组件的值,接着,在父组件的模板中引入子组件,同时在引入子组件的标签中添加 props 属性并为其设置需要传递的值。

      2. 在 Vue3 中,父组件通过 props 传递给子组件的值是响应式的。也就是说,如果在父组件中的传递的值发生了改变,子组件中的值也会相应地更新。

      • 父组件代码:App.vue

      
      
      
      
      
      • 子组件代码:Son.vue

      
      
      
      
      
      6.8.3.2 子传父
      • 父组件: App.vue

      
      ​
      ​
      ​
      
      • 子组件:Son.vue

      
      
      6.8.3.3 兄弟传参
      • Navigator.vue: 发送数据到App.vue

      
      
      
      
      
      • App.vue: 发送数据到Content.vue

      
      
      
      
      
      • Content.vue

      
      
      
      
      

      七、Vue3路由机制router

      7.1 路由简介

      1 什么是路由?

      • 定义:路由就是根据不同的 URL 地址展示不同的内容或页面。

      • 通俗理解:路由就像是一个地图,我们要去不同的地方,需要通过不同的路线进行导航。

      2 路由的作用

      • 单页应用程序(SPA)中,路由可以实现不同视图之间的无刷新切换,提升用户体验;

      • 路由还可以实现页面的认证和权限控制,保护用户的隐私和安全;

      • 路由还可以利用浏览器的前进与后退,帮助用户更好地回到之前访问过的页面。

      7.2 路由入门案例

      1 案例需求分析

      2 创建项目和导入路由依赖

      npm create vite //创建项目cd 项目文件夹 //进入项目文件夹
      npm install //安装项目需求依赖
      npm install vue-router@4 --save //安装全局的vue-router 4版本

      3 准备页面和组件

      • components/Home.vue

      
      
      
      
      
      
      • components/List.vue

      
      
      
      
      
      • components/Add.vue

      
      
      
      
      
      • components/Update.vue

      
      
      
      
      
      • App.vue

      
      
      
      
      
      

      4 准备路由配置

      • src/routers/router.js

      // 导入路由创建的相关方法
      import {createRouter,createWebHashHistory} from 'vue-router'
      
      // 导入vue组件
      import Home from '../components/Home.vue'
      import List from '../components/List.vue'
      import Add from '../components/Add.vue'
      import Update from '../components/Update.vue'
      
      // 创建路由对象,声明路由规则
      const router = createRouter({
          //createWebHashHistory() 是 Vue.js 基于 hash 模式创建路由的工厂函数。在使用这种模式下,路由信息保存在 URL 的 hash 中,
          //使用 createWebHashHistory() 方法,可以创建一个路由历史记录对象,用于管理应用程序的路由。在 Vue.js 应用中,
          //通常使用该方法来创建路由的历史记录对象。
          //就是路由中缓存历史记录的对象,vue-router提供
          history: createWebHashHistory(),
          routes:[
              {
                  path:'/',
                  /* 
                      component指定组件在默认的路由视图位置展示
                      components:Home
                      components指定组件在name为某个值的路由视图位置展示
                      components:{
                          default:Home,// 默认路由视图位置
                          homeView:Home// name为homeView的路由视图位置
                      }   
                  */
                  components:{
                      default:Home,
                      homeView:Home
                  }      
              },
              {
                  path:'/list',
                  components:{
                      listView : List
                  } 
              },
              {
                  path:'/add',
                  components:{
                      addView:Add
                  } 
              },
              {
                  path:'/update',
                  components:{
                      updateView:Update
                  }  
              },
          ]
      
      })
      
      // 对外暴露路由对象
      export default router;

      5 main.js引入router配置

      • 修改文件:main.js (入口文件)

      import { createApp } from 'vue'
      import './style.css'
      import App from './App.vue'
      //导入router模块
      import router from './routers/router.js'
      let app = createApp(App)
      //绑定路由对象
      app.use(router)
      //挂在试图
      app.mount("#app")

      6 启动测试

      npm run dev

      7.3 路由重定向

      重定向的作用:将一个路由重定向到另一个路由上

      • 修改案例:访问/list和/showAll都定向到List.vue

      • router.js

      // 导入路由创建的相关方法
      import {createRouter,createWebHashHistory} from 'vue-router'
      
      // 导入vue组件
      import Home from '../components/Home.vue'
      import List from '../components/List.vue'
      import Add from '../components/Add.vue'
      import Update from '../components/Update.vue'
      
      // 创建路由对象,声明路由规则
      const router = createRouter({
          history: createWebHashHistory(),
          routes:[
              {
                  path:'/',
                  components:{
                      default:Home,
                      homeView:Home
                  }      
              },
              {
                  path:'/list',
                  components:{
                      listView : List
                  } 
              },
              {
                  path:'/showAll',
                  // 重定向
                  redirect :'/list'
              },
              {
                  path:'/add',
                  components:{
                      addView:Add
                  } 
              },
              {
                  path:'/update',
                  components:{
                      updateView:Update
                  }  
              },
          ]
      
      })
      
      // 对外暴露路由对象
      export default router;
      • App.vue

      
      
      
      
      

      7.4 编程式路由(useRouter)

      普通路由

      • list页这种路由,to中的内容目前是固定的,点击后只能切换/list对象组件(声明式路由)

      编程式路由

      • 通过useRouter,动态决定向那个组件切换的路由

      • 在 Vue 3 和 Vue Router 4 中,你可以使用 useRouter 来实现动态路由(编程式路由)

      • 这里的 useRouter 方法返回的是一个 router 对象,你可以用它来做如导航到新页面、返回上一页面等操作。

      案例需求: 通过普通按钮配合事件绑定实现路由页面跳转,不直接使用router-link标签

      • App.vue

      
      
      
      
      

      7.5 路由传参(useRoute)

      路径参数

      • 在路径中使用一个动态字段来实现,我们称之为 路径参数

        • 例如: 查看数据详情 /showDetail/1 ,1就是要查看详情的id,可以动态添值!

      键值对参数

      • 类似与get请求通过url传参,数据是键值对形式的

        • 例如: 查看数据详情/showDetail?hid=1,hid=1就是要传递的键值对参数

        • 在 Vue 3 和 Vue Router 4 中,你可以使用 useRoute 这个函数从 Vue 的组合式 API 中获取路由对象。

        • useRoute 方法返回的是当前的 route 对象,你可以用它来获取关于当前路由的信息,如当前的路径、查询参数等。

      案例需求 : 切换到ShowDetail.vue组件时,向该组件通过路由传递参数

      • 修改App.vue文件

      
      
      
      
      
      
      • 修改router.js增加路径参数占位符

      // 导入路由创建的相关方法
      import {createRouter,createWebHashHistory} from 'vue-router'
      
      // 导入vue组件
      
      import ShowDetail from '../components/ShowDetail.vue'
      import ShowDetail2 from '../components/ShowDetail2.vue'
      
      // 创建路由对象,声明路由规则
      const router = createRouter({
          history: createWebHashHistory(),
          routes:[
              
              {
                  /* 此处:id  :language作为路径的占位符 */
                  path:'/showDetail/:id/:language',
                  /* 动态路由传参时,根据该名字找到该路由 */
                  name:'showDetail',
                  components:{
                      showDetailView:ShowDetail
                  }  
              },
              {
                  path:'/showDetail2',
                  components:{
                      showDetailView2:ShowDetail2
                  }  
              },
          ]
      
      })
      
      // 对外暴露路由对象
      export default router;
      • ShowDetail.vue 通过useRoute获取路径参数

      
      
      
      
      
      
      • ShowDetail2.vue通过useRoute获取键值对参数

      
      
      
      
      
      

      7.6 路由守卫

      在 Vue 3 中,路由守卫是用于在路由切换期间进行一些特定任务的回调函数。路由守卫可以用于许多任务,例如验证用户是否已登录、在路由切换前提供确认提示、请求数据等。Vue 3 为路由守卫提供了全面的支持,并提供了以下几种类型的路由守卫:

      1. 全局前置守卫:在路由切换前被调用,可以用于验证用户是否已登录、中断导航、请求数据等。

      2. 全局后置守卫:在路由切换之后被调用,可以用于处理数据、操作 DOM 、记录日志等。

      3. 守卫代码的位置: 在router.js中

      //全局前置路由守卫
      router.beforeEach( (to,from,next) => {
          //to 是目标地包装对象  .path属性可以获取地址
          //from 是来源地包装对象 .path属性可以获取地址
          //next是方法,不调用默认拦截! next() 放行,直接到达目标组件
          //next('/地址')可以转发到其他地址,到达目标组件前会再次经过前置路由守卫
          console.log(to.path,from.path,next)
      
          //需要判断,注意避免无限重定向
          if(to.path == '/index'){
              next()
          }else{
              next('/index')
          }
          
      } )
      
      //全局后置路由守卫
      router.afterEach((to, from) => {
          console.log(`Navigate from ${from.path} to ${to.path}`);
      });
      

      登录案例,登录以后才可以进入home,否则必须进入login

      • 定义Login.vue

      • 定义Home.vue

      • App.vue

      • 定义routers.js

      // 导入路由创建的相关方法
      import {createRouter,createWebHashHistory} from 'vue-router'
      
      // 导入vue组件
      
      import Home from '../components/Home.vue'
      import Login from '../components/login.vue'
      // 创建路由对象,声明路由规则
      const router = createRouter({
          history: createWebHashHistory(),
          routes:[
              {
                  path:'/home',
                  component:Home
              },
              {
                  path:'/',
                  redirect:"/home"
              },
              {
                  path:'/login',
                  component:Login
              },
          ]
      
      })
      
      // 设置路由的全局前置守卫
      router.beforeEach((to,from,next)=>{
          /* 
          to 要去那
          from 从哪里来
          next 放行路由时需要调用的方法,不调用则不放行
          */
          console.log(`从哪里来:${from.path},到哪里去:${to.path}`)
      
          if(to.path == '/login'){
              //放行路由  注意放行不要形成循环  
              next()
          }else{
              //let username =window.sessionStorage.getItem('username'); 
              let username =window.localStorage.getItem('username'); 
              if(null != username){
                  next()
              }else{
                  next('/login')
              }
      
          }
      })
      // 设置路由的全局后置守卫
      router.afterEach((to,from)=>{
          console.log(`从哪里来:${from.path},到哪里去:${to.path}`)
      })
      
      // 对外暴露路由对象
      export default router;
      • 启动测试

      npm run dev
      

      九、Vue3数据交互axios

      9.0 预讲知识-promise

      9.0.1 普通函数和回调函数

      普通函数: 正常调用的函数,一般函数执行完毕后才会继续执行下一行代码

      回调函数: 一些特殊的函数,表示未来才会执行的一些功能,后续代码不会等待该函数执行完毕就开始执行了

      9.0.2 Promise 简介

      前端中的异步编程技术,类似Java中的多线程+线程结果回调!

      • Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。

      • 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

      Promise对象有以下两个特点。

      (1)Promise对象代表一个异步操作,有三种状态:`Pending`(进行中)、`Resolved`(已完成,又称 Fulfilled)和`Rejected`(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是`Promise`这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
      
      (2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从`Pending`变为`Resolved`和从`Pending`变为`Rejected`。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。
      9.0.3 Promise 基本用法

      ES6规定,Promise对象是一个构造函数,用来生成Promise实例。

          
      9.0.4 Promise catch()

      Promise.prototype.catch方法是.then(null, rejection)的别名,用于指定发生错误时的回调函数。

      9.0.5 async和await的使用

      async和await是ES6中用于处理异步操作的新特性。通常,异步操作会涉及到Promise对象,而async/await则是在Promise基础上提供了更加直观和易于使用的语法。

      async 用于标识函数的

      1. async标识函数后,async函数的返回值会变成一个promise对象

      2. 如果函数内部返回的数据是一个非promise对象,async函数的结果会返回一个成功状态 promise对象

      3. 如果函数内部返回的是一个promise对象,则async函数返回的状态与结果由该对象决定

      4. 如果函数内部抛出的是一个异常,则async函数返回的是一个失败的promise对象

      await

      1. await右侧的表达式一般为一个promise对象,但是也可以是一个其他值

      2. 如果表达式是promise对象,await返回的是promise成功的值

      3. await会等右边的promise对象执行结束,然后再获取结果,后续代码也会等待await的执行

      4. 如果表达式是其他值,则直接返回该值

      5. await必须在async函数中,但是async函数中可以没有await

      6. 如果await右边的promise失败了,就会抛出异常,需要通过 try ... catch捕获处理

      9.1 Axios介绍

      ajax

      • AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。

      • AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

      • AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。

      • AJAX 不需要任何浏览器插件,但需要用户允许 JavaScript 在浏览器上执行。

      • XMLHttpRequest 只是实现 Ajax 的一种方式。

      ajax工作原理:

      原生javascript方式进行ajax(了解):

         

      什么是axios 官网介绍:起步 | Axios Docs

      • Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http 模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。它有如下特性

        • 从浏览器创建 XMLHttpRequests

        • 从 node.js 创建 http 请求

        • 支持 Promise API

        • 拦截请求和响应

        • 转换请求和响应数据

        • 取消请求

        • 自动转换JSON数据

        • 客户端支持防御XSRF

      9.2 Axios 入门案例

      1 案例需求:请求后台获取随机土味情话

      • 请求的url

      https://api.uomg.com/api/rand.qinghua?format=json    或者使用  http://forum.atguigu.cn/api/rand.qinghua?format=json
      • 请求的方式

      GET/POST
      • 数据返回的格式

      {"code":1,"content":"我努力不是为了你而是因为你。"}

      2 准备项目

      npm create vite
      npm install 
      /*npm install vue-router@4 --save
      npm install pinia */

      3 安装axios

      npm install axios

      4 设计页面(App.Vue)

      5 启动测试

      npm run dev

      异步响应的数据结构

      • 响应的数据是经过包装返回的!一个请求的响应包含以下信息。

      {
        // `data` 由服务器提供的响应
        data: {},
        // `status` 来自服务器响应的 HTTP 状态码
        status: 200,
        // `statusText` 来自服务器响应的 HTTP 状态信息
        statusText: 'OK',
        // `headers` 是服务器响应头
        // 所有的 header 名称都是小写,而且可以使用方括号语法访问
        // 例如: `response.headers['content-type']`
        headers: {},
        // `config` 是 `axios` 请求的配置信息
        config: {},
        // `request` 是生成此响应的请求
        // 在node.js中它是最后一个ClientRequest实例 (in redirects),
        // 在浏览器中则是 XMLHttpRequest 实例
        request: {}
      }
      • then取值

      then(function (response) {
          console.log(response.data);
          console.log(response.status);
          console.log(response.statusText);
          console.log(response.headers);
          console.log(response.config);
      });

      6 通过async和await处理异步请求

      
      
      
      
      
      

      axios在发送异步请求时的可选配置:

      {
        // `url` 是用于请求的服务器 URL
        url: '/user',
        // `method` 是创建请求时使用的方法
        method: 'get', // 默认值
        // `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。
        // 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URL
        baseURL: 'https://some-domain.com/api/',
        // `transformRequest` 允许在向服务器发送前,修改请求数据
        // 它只能用于 'PUT', 'POST' 和 'PATCH' 这几个请求方法
        // 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream
        // 你可以修改请求头。
        transformRequest: [function (data, headers) {
          // 对发送的 data 进行任意转换处理
          return data;
        }],
        // `transformResponse` 在传递给 then/catch 前,允许修改响应数据
        transformResponse: [function (data) {
          // 对接收的 data 进行任意转换处理
          return data;
        }],
        // 自定义请求头
        headers: {'X-Requested-With': 'XMLHttpRequest'},
        // `params` 是与请求一起发送的 URL 参数
        // 必须是一个简单对象或 URLSearchParams 对象
        params: {
          ID: 12345
        },
        // `paramsSerializer`是可选方法,主要用于序列化`params`
        // (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
        paramsSerializer: function (params) {
          return Qs.stringify(params, {arrayFormat: 'brackets'})
        },
        // `data` 是作为请求体被发送的数据
        // 仅适用 'PUT', 'POST', 'DELETE 和 'PATCH' 请求方法
        // 在没有设置 `transformRequest` 时,则必须是以下类型之一:
        // - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
        // - 浏览器专属: FormData, File, Blob
        // - Node 专属: Stream, Buffer
        data: {
          firstName: 'Fred'
        },
        // 发送请求体数据的可选语法
        // 请求方式 post
        // 只有 value 会被发送,key 则不会
        data: 'Country=Brasil&City=Belo Horizonte',
        // `timeout` 指定请求超时的毫秒数。
        // 如果请求时间超过 `timeout` 的值,则请求会被中断
        timeout: 1000, // 默认值是 `0` (永不超时)
        // `withCredentials` 表示跨域请求时是否需要使用凭证
        withCredentials: false, // default
        // `adapter` 允许自定义处理请求,这使测试更加容易。
        // 返回一个 promise 并提供一个有效的响应 (参见 lib/adapters/README.md)。
        adapter: function (config) {
          /* ... */
        },
        // `auth` HTTP Basic Auth
        auth: {
          username: 'janedoe',
          password: 's00pers3cret'
        },
        // `responseType` 表示浏览器将要响应的数据类型
        // 选项包括: 'arraybuffer', 'document', 'json', 'text', 'stream'
        // 浏览器专属:'blob'
        responseType: 'json', // 默认值
        // `responseEncoding` 表示用于解码响应的编码 (Node.js 专属)
        // 注意:忽略 `responseType` 的值为 'stream',或者是客户端请求
        // Note: Ignored for `responseType` of 'stream' or client-side requests
        responseEncoding: 'utf8', // 默认值
        // `xsrfCookieName` 是 xsrf token 的值,被用作 cookie 的名称
        xsrfCookieName: 'XSRF-TOKEN', // 默认值
        // `xsrfHeaderName` 是带有 xsrf token 值的http 请求头名称
        xsrfHeaderName: 'X-XSRF-TOKEN', // 默认值
        // `onUploadProgress` 允许为上传处理进度事件
        // 浏览器专属
        onUploadProgress: function (progressEvent) {
          // 处理原生进度事件
        },
        // `onDownloadProgress` 允许为下载处理进度事件
        // 浏览器专属
        onDownloadProgress: function (progressEvent) {
          // 处理原生进度事件
        },
        // `maxContentLength` 定义了node.js中允许的HTTP响应内容的最大字节数
        maxContentLength: 2000,
        // `maxBodyLength`(仅Node)定义允许的http请求内容的最大字节数
        maxBodyLength: 2000,
        // `validateStatus` 定义了对于给定的 HTTP状态码是 resolve 还是 reject promise。
        // 如果 `validateStatus` 返回 `true` (或者设置为 `null` 或 `undefined`),
        // 则promise 将会 resolved,否则是 rejected。
        validateStatus: function (status) {
          return status >= 200 && status < 300; // 默认值
        },
        // `maxRedirects` 定义了在node.js中要遵循的最大重定向数。
        // 如果设置为0,则不会进行重定向
        maxRedirects: 5, // 默认值
        // `socketPath` 定义了在node.js中使用的UNIX套接字。
        // e.g. '/var/run/docker.sock' 发送请求到 docker 守护进程。
        // 只能指定 `socketPath` 或 `proxy` 。
        // 若都指定,这使用 `socketPath` 。
        socketPath: null, // default
        // `httpAgent` and `httpsAgent` define a custom agent to be used when performing http
        // and https requests, respectively, in node.js. This allows options to be added like
        // `keepAlive` that are not enabled by default.
        httpAgent: new http.Agent({ keepAlive: true }),
        httpsAgent: new https.Agent({ keepAlive: true }),
        // `proxy` 定义了代理服务器的主机名,端口和协议。
        // 您可以使用常规的`http_proxy` 和 `https_proxy` 环境变量。
        // 使用 `false` 可以禁用代理功能,同时环境变量也会被忽略。
        // `auth`表示应使用HTTP Basic auth连接到代理,并且提供凭据。
        // 这将设置一个 `Proxy-Authorization` 请求头,它会覆盖 `headers` 中已存在的自定义 `Proxy-Authorization` 请求头。
        // 如果代理服务器使用 HTTPS,则必须设置 protocol 为`https`
        proxy: {
          protocol: 'https',
          host: '127.0.0.1',
          port: 9000,
          auth: {
            username: 'mikeymike',
            password: 'rapunz3l'
          }
        },
        // see https://axios-http.com/zh/docs/cancellation
        cancelToken: new CancelToken(function (cancel) {
        }),
        // `decompress` indicates whether or not the response body should be decompressed 
        // automatically. If set to `true` will also remove the 'content-encoding' header 
        // from the responses objects of all decompressed responses
        // - Node only (XHR cannot turn off decompression)
        decompress: true // 默认值
      }

      9.3 Axios get和post方法

      配置添加语法

      axios.get(url[, config])
      
      axios.get(url,{
         上面指定配置key:配置值,
         上面指定配置key:配置值
      })
      
      axios.post(url[, data[, config]])
      
      axios.post(url,{key:value //此位置数据,没有空对象即可{}},{
         上面指定配置key:配置值,
         上面指定配置key:配置值
      })

      测试get参数

      
      
      
      
      
      

      测试post参数

      
      
      
      
      
      

      9.4 Axios 拦截器

      如果想在axios发送请求之前,或者是数据响应回来在执行then方法之前做一些额外的工作,可以通过拦截器完成

      // 添加请求拦截器 请求发送之前
      axios.interceptors.request.use(
        function (config) {
          // 在发送请求之前做些什么
          return config;
        }, 
        function (error) {
          // 对请求错误做些什么
          return Promise.reject(error);
        }
      );
      ​
      // 添加响应拦截器 数据响应回来
      axios.interceptors.response.use(
        function (response) {
          // 2xx 范围内的状态码都会触发该函数。
          // 对响应数据做点什么
          return response;
        }, 
        function (error) {
          // 超出 2xx 范围的状态码都会触发该函数。
          // 对响应错误做点什么
          return Promise.reject(error);
        }
      );
      • 定义src/axios.js提取拦截器和配置语法

      import axios from 'axios'
      ​
      ​
      //  创建instance实例
      const instance = axios.create({
          baseURL:'https://api.uomg.com',
          timeout:10000
      })
      ​
      //  添加请求拦截
      instance.interceptors.request.use(
          // 设置请求头配置信息
          config=>{
              //处理指定的请求头
              console.log("before request")
              config.headers.Accept = 'application/json, text/plain, text/html,*/*'
              return config
          },
          // 设置请求错误处理函数
          error=>{
              console.log("request error")
              return Promise.reject(error)
          }
      )
      // 添加响应拦截器
      instance.interceptors.response.use(
          // 设置响应正确时的处理函数
          response=>{
              console.log("after success response")
              console.log(response)
              return response
          },
          // 设置响应异常时的处理函数
          error=>{
              console.log("after fail response")
              console.log(error)
              return Promise.reject(error)
          }
      )
      // 默认导出
      export default instance
      • App.vue

      
      

      十一、Vue3状态管理Pinia

      11.1 Pinia介绍

      如何实现多个组件之间的数据传递?

      • 方式1 组件传参

      • 方式2 路由传参

      • 方式3 通过pinia状态管理定义共享数据

      当我们有多个组件共享一个共同的状态(数据源)时,多个视图可能都依赖于同一份状态。来自不同视图的交互也可能需要更改同一份状态。虽然我们的手动状态管理解决方案(props,组件间通信,模块化)在简单的场景中已经足够了,但是在大规模的生产应用中还有很多其他事项需要考虑:

      • 更强的团队协作约定

      • 与 Vue DevTools 集成,包括时间轴、组件内部审查和时间旅行调试

      • 模块热更新 (HMR)

      • 服务端渲染支持

      Pinia 就是一个实现了上述需求的状态管理库,由 Vue 核心团队维护,对 Vue 2 和 Vue 3 都可用。简介 | Pinia

      11.2 Pinia基本用法

      1 准备vite项目

      npm create vite
      npm install 
      npm install vue-router@4 --save

      2 安装pinia

      npm install pinia

      3 定义pinia store对象 src/store/store.js [推荐这么命名不是强制]

      import {defineStore } from 'pinia'
      
      //定义数据并且对外暴露
      // store就是定义共享状态的包装对象
      // 内部包含四个属性: id 唯一标识 state 完整类型推理,推荐使用箭头函数 存放的数据 getters 类似属性计算,存储放对数据
      // 操作的方法  actions 存储数据的复杂业务逻辑方法
      // 理解: store类似Java中的实体类, id就是类名, state 就是装数据值的属性  getters就是get方法,actions就是对数据操作的其他方法
      export const definedPerson = defineStore(
          {
              id: 'personPinia', //必须唯一
              state:()=>{ // state中用于定义数据
                  return {
                      username:'张三',
                      age:0,
                      hobbies:['唱歌','跳舞']
                  }
              },
              getters:{// 用于定义一些通过数据计算而得到结果的一些方法 一般在此处不做对数据的修改操作
                       // getters中的方法可以当做属性值方式使用
                  getHobbiesCount(){
                      return this.hobbies.length
                  },
                  getAge(){
                      return this.age
                  }
              },
              actions:{ // 用于定义一些对数据修改的方法
                  doubleAge(){
                      this.age=this.age*2
                  }
              }
          }
      )

      4 在main.js配置pinia组件到vue中

      import { createApp } from 'vue'
      import App from './App.vue'
      import router from './routers/router.js'
      // 导pinia
      import { createPinia } from 'pinia'
      // 创建pinia对象
      let pinia= createPinia()
      
      let app =createApp(App)
      app.use(router)
      // app中使用pinia功能
      app.use(pinia) 
      app.mount('#app')

      5 Operate.vue 中操作Pinia数据

      
      
      
      

      6 List.vue中展示Pinia数据

      
      
      
      
      
      

      7 定义组件路由router.js

      // 导入路由创建的相关方法
      import {createRouter,createWebHashHistory} from 'vue-router'
      
      // 导入vue组件
      import List  from '../components/List.vue'
      import Operate  from '../components/Operate.vue'
      // 创建路由对象,声明路由规则
      const router = createRouter({
          history: createWebHashHistory(),
          routes:[
              {
                  path:'/opearte',
                  component:Operate
              },
              
              {
                  path:'/list',
                  component:List
              },
          ]
      
      })
      
      // 对外暴露路由对象
      export default router;

      8 App.vue中通过路由切换组件

      9 启动测试

      npm run dev

      11.3 Pinia其他细节

      State (状态) 在大多数情况下,state 都是你的 store 的核心。人们通常会先定义能代表他们 APP 的 state。在 Pinia 中,state 被定义为一个返回初始状态的函数。

      • store.js

      import {defineStore} from 'pinia'
      ​
      export const definedPerson = defineStore('personPinia',
          {
              state:()=>{
                  return {
                      username:'',
                      age:0,
                      hobbies:['唱歌','跳舞']
                  }
              },
              getters:{
                  getHobbiesCount(){
                      return this.hobbies.length
                  },
                  getAge(){
                      return this.age
                  }
              },
              actions:{
                  doubleAge(){
                      this.age=this.age*2
                  }
              }
          }
      )
      • Operate.vue

      
      

      2 Getter 完全等同于 store 的 state 的计算值。可以通过 defineStore() 中的 getters 属性来定义它们。推荐使用箭头函数,并且它将接收 state 作为第一个参数:

      export const useStore = defineStore('main', {
        state: () => ({
          count: 0,
        }),
        getters: {
          doubleCount: (state) => state.count * 2,
        },
      })

      3 Action 相当于组件中的 method。它们可以通过 defineStore() 中的 actions 属性来定义,并且它们也是定义业务逻辑的完美选择。类似 getter,action 也可通过 this 访问整个 store 实例,并支持完整的类型标注(以及自动补全)。不同的是,action 可以是异步的,你可以在它们里面 await 调用任何 API,以及其他 action!

      export const useCounterStore = defineStore('main', {
        state: () => ({
          count: 0,
        }),
        actions: {
          increment() {
            this.count++
          },
          randomizeCounter() {
            this.count = Math.round(100 * Math.random())
          },
        },
      })
      

      十三、Element-plus组件库

      13.1 Element-plus介绍

      Element Plus 是一套基于 Vue 3 的开源 UI 组件库,是由饿了么前端团队开发的升级版本 Element UI。Element Plus 提供了丰富的 UI 组件、易于使用的 API 接口和灵活的主题定制功能,可以帮助开发者快速构建高质量的 Web 应用程序。

      • Element Plus 支持按需加载,且不依赖于任何第三方 CSS 库,它可以轻松地集成到任何 Vue.js 项目中。Element Plus 的文档十分清晰,提供了各种组件的使用方法和示例代码,方便开发者快速上手。

      • Element Plus 目前已经推出了大量的常用 UI 组件,如按钮、表单、表格、对话框、选项卡等,此外还提供了一些高级组件,如日期选择器、时间选择器、级联选择器、滑块、颜色选择器等。这些组件具有一致的设计和可靠的代码质量,可以为开发者提供稳定的使用体验。

      • 与 Element UI 相比,Element Plus 采用了现代化的技术架构和更加先进的设计理念,同时具备更好的性能和更好的兼容性。Element Plus 的更新迭代也更加频繁,可以为开发者提供更好的使用体验和更多的功能特性。

      • Element Plus 可以在支持 ES2018 和 ResizeObserver 的浏览器上运行。 如果您确实需要支持旧版本的浏览器,请自行添加 Babel 和相应的 Polyfill

      • 官网https://element-plus.gitee.io/zh-CN/

      • 由于 Vue 3 不再支持 IE11,Element Plus 也不再支持 IE 浏览器。

      13.2 Element-plus入门案例

      1 准备vite项目

      npm create vite  // 注意选择 vue+typeScript
      npm install 
      npm install vue-router@4 --save
      npm install pinia
      npm install axios

      2 安装element-plus

      npm install element-plus

      3 完整引入element-plus

      • main.js

      import { createApp } from 'vue'
      //导入element-plus相关内容
      import ElementPlus from 'element-plus'
      import 'element-plus/dist/index.css'
      ​
      import App from './App.vue'
      ​
      const app = createApp(App)
      ​
      app.use(ElementPlus)
      app.mount('#app')

      4 入门案例

      • App.vue

      5 启动测试

      npm run dev

      13.3 Element-plus常用组件

      https://element-plus.gitee.io/zh-CN/component/button.html

你可能感兴趣的:(JAVAWeb--前端工程化)