前端工程化(VUE3)
文章目录
- 前端工程化(VUE3)
- 一、概述
- 二、ECMA6Script
- 2.1 es6的变量和模板字符串
- 2.2 es6的解构表达式
- 2.3 es6的箭头函数
- 2.4 rest和spread
- 2.5 es6的对象创建和拷贝
- 2.6 es6的模块化处理
- 三、前端工程化环境搭建
- 3.1 Node.js
- 3.2 npm
- 3.2.1 npm的配置
- 3.2.2 npm 常用命令
- 四、Vue3简介和快速体验
- 五、Vue3通过Vite实现工程化
- 5.1 Vite 简介
- 5.2 Vite创建Vue3工程化项目
- 5.3 Vite+Vue3项目的目录结构
- 5.4 Vite+Vue3项目组件(SFC入门)
- 5.5 Vite+Vue3关于样式的导入方式
- 5.6 Vite+Vue3响应式和setup函数
- 5.7 关于JS和TS选择的问题
- 六、Vue3视图渲染技术
- 6.1 模板语法
- 6.2 条件渲染
- 6.3 列表渲染
- 6.4 属性计算
- 6.5 数据监听器
- 6.6 生命周期
- 6.7 组件之前传递数据
- 七、Vue3路由机制router
- 7.1 路由入门配置
- 7.2 编程式路由(useRouter)
- 7.3 路由传参(useRoute)
- 7.4 路由守卫
- 八、promise
- 8.1 基本用法
- 8.2 catch()
- 8.3 async和await的使用
- 九、数据交互axios
- 9.1 基本用法
- 9.2 Axios get和post方法
- 9.3 Axios 拦截器
- 十、跨域问题
- 十一、Pinia
- 11.1 基本用法
- 11.2 Pinia其他细节
- 十二、Element Plus
一、概述
前端工程化
是使用 软件工程的方法
来 单独
解决 前端
的开发流程中 模块化、组件化、规范化、自动化
的问题,其主要目的为了提高效率和降低成本。
前端工程化实现的技术栈有很多,我们采用 ES6+nodejs+npm+Vite+VUE3+router+pinia+axios+Element-plus
组合来实现
- ECMAScript6 VUE3中大量使用ES6语法
- Nodejs 前端项目运行环境
- npm 依赖下载工具
- vite 前端项目构建工具
- VUE3 优秀的渐进式前端框架
- router 通过路由实现页面切换
- pinia 通过状态管理实现组件数据传递
- axios ajax异步请求封装技术实现前后端数据交互
- Element-plus 可以提供丰富的快速构建网页的组件仓库
二、ECMA6Script
ECMAScript 6,简称ES6,是JavaScript语言的一次重大更新。它于2015年发布,是原来的ECMAScript标准的第六个版本。ES6带来了大量的新特性,包括箭头函数、模板字符串、let和const关键字、解构、默认参数值、模块系统等等
2.1 es6的变量和模板字符串
ES6 新增了 let
和 const
,用来声明变量,使用的细节上也存在诸多差异
- let 和var的差别:let 与 java中变量的定义类似
- let不能重复声明
- let有块级作用域,非函数的花括号遇见let会有块级作用域,也就是只能在花括号里面访问
- let不会预解析进行变量提升
- let定义的全局变量不会作为window的属性
- let在es6中推荐优先使用
- const和var的差异:const 与 java 中被 final 变量类似
模板字符串(template string)是增强版的字符串,用反引号(`)标识,会原样记录字符串,可以使用 ${xxx} 形式输出变量和拼接变量
const times = '3次';
let str = `${times}
够不够?`;
console.log(str);
2.2 es6的解构表达式
let [a, b, c] = [1, 2, 3]; // 数组解构赋值
let {a, b} = {a: 1, b: 2}; // 对象解构赋值function add([x, y]) {return x + y;
}
add([1, 2]); // 函数参数解构赋值
2.3 es6的箭头函数
- 函数声明
let fn1 = function(){}
let fn2 = ()=>{} //箭头函数,此处不需要书写function关键字
let fn3 = x =>{} //单参数可以省略(),多参数无参数不可以!
let fn4 = x => console.log(x) //只有一行方法体可以省略{};
let fun5 = x => x + 1 //当函数体只有一句返回值时,可以省略花括号和 return 语句
- 使用特点:箭头函数this关键字
在 JavaScript 中,this 关键字通常用来引用函数所在的对象,或者在函数本身作为构造函数时,来引用新对象的实例。但是在箭头函数中,this 的含义与常规函数定义中的含义不同,是由箭头函数定义时的上下文来决定的,而不是由函数调用时的上下文来决定的。箭头函数没有自己的this,this指向的是外层上下文环境的this
let person ={name:"张三",showName:function (){console.log(this) // 这里的this是personconsole.log(this.name)},viewName: () =>{console.log(this) // 这里的this是windowconsole.log(this.name)}
}
person.showName()
person.viewName()//this应用
function Counter() {this.count = 0;window.setInterval(() => {// 这里的 this 是上一层作用域中的 this,即 Counter实例化对象;如果用func的方式则this指windowthis.count++;console.log(this.count);}, 1000);
}
let counter = new Counter();
2.4 rest和spread
rest参数,在形参上使用和JAVA中的可变参数几乎一样
let fun = (...args) =>{console.log(args)}
spread参数,在函数调用实参上使用rest;还可以快速合并数组或者对象
// 1. 函数实参
let a = [1,2,3];
fun(...a); // 如果传 a,则a整体会被当成一个数组作为参数传给方法fun,用 ...a 则 a 中每个元素都会单独作为一个参数传入fun函数中执行
// 2. 合并数组
let a = [1,2,3];
let b = [4,5,6];
let c = [...a,...b]; // [1,2,3,4,5,6]
// 3.合并对象
let a = { x: 'm', y: 'h'};
let b = { y: 'n'};
let c = {...a, ...b}; // {x: 'm', y: 'n'}
2.5 es6的对象创建和拷贝
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("面条")
深拷贝
let arr =['java','c','python'];
let person ={name:'张三',language:arr
};
// 深拷贝,通过JSON和字符串的转换形成一个新的对象
let person2 = JSON.parse(JSON.stringify(person));
2.6 es6的模块化处理
目前,前端模块化有多种规范和实现,包括 CommonJS、AMD 和 ES6 模块化。ES6 模块化是 JavaScript 语言的模块标准,使用 import 和 export 关键字来实现模块的导入和导出。现在,大部分浏览器都已经原生支持 ES6 模块化,因此它成为了最为广泛使用的前端模块化标准.
- ES6模块化的几种暴露和导入方式
- 分别导出:对每个需要导出的变量或方法前加 export,如
export PI
- 统一导出:模式适用 export 并将需要导出变量或方法放入 export 大括号内,如
export { A, B ,C }
- 默认导出:
export default PI
一个文件中只能写一个,代表默认的导出对象
- 分别导出:对每个需要导出的变量或方法前加 export,如
ES6中无论以何种方式导出,导出的都是一个对象,导出的内容都可以理解为是向这个对象中添加属性或者方法
- 导入:
import * as m1 from './module.js'
import {PI ,Person ,sum,PI as pi,Person as People,sum as add} from './module.js'
import {default as add} from './module.js' // 用的少
import add2 from './module.js' // 等效于 import {default as add2} from './module.js'
三、前端工程化环境搭建
3.1 Node.js
Node.js 相对 js 而言相当于Java的虚拟机。Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,可以使 JavaScript 运行在服务器端。使用 Node.js,可以方便地开发服务器端应用程序,如 Web 应用、API、后端服务,还可以通过 Node.js 构建命令行工具等。Node.js 底层用 C++ 编写,运行速度快。Node.js是单线程,但是采用了事件驱动、异步 I/O 模型,可以处理高并发请求。
在 Node.js 中,我们可以使用 JavaScript 来编写服务器端程序,这也使得前端开发人员可以利用自己已经熟悉的技能来开发服务器端程序,同时也让 JavaScript 成为一种全栈语言
下载与安装
- 打开官网https://nodejs.org/en下载对应操作系统的 LTS 版本。
- 双击安装包进行安装,安装过程中遵循默认选项即可(或者参照https://www.runoob.com/nodejs/nodejs-install-setup.html )。安装完成后,可以在命令行终端输入
node -v
和npm -v
查看 Node.js 和 npm 的版本号。
3.2 npm
npm 相当于 java maven 的依赖管理功能。安装了上述 node.js 会自动安装 npm。
3.2.1 npm的配置
npm 安装依赖包时默认使用的是官方源,由于国内网络环境的原因,有时会出现下载速度过慢的情况。为了解决这个问题,可以配置使用阿里镜像来加速 npm 的下载速度,具体操作如下:
npm config set registry https://registry.npmmirror.com
原:https://registry.npmjs.org/
将替换为 https://registry.npmmirror.com
npm config get registry
恢复官方源可执行下面语句
npm config set registry https://registry.npmjs.org/
配置全局依赖下载后存储位置:默认位置 <用户目录>\AppData\Roaming\npm
npm config set prefix "D:\softwareData\npmRepository"
查看依赖存储位置:
npm config get prefix
npm 升级命令
npm install -g npm@10.2.3
3.2.2 npm 常用命令
- 项目初始化
npm init
:进入一个vscode创建好的项目中, 执行 npm init 命令后,npm 会引导您在命令行界面上回答一些问题,例如项目名称、版本号、作者、许可证等信息,并最终生成一个package.json 文件。package.json信息会包含项目基本信息,类似maven的pom.xmlnpm init -y
:-y yes的意思,所有信息使用当前文件夹的默认值,不用挨个填写
- 安装依赖 (查看所有依赖地址 https://www.npmjs.com )
npm install
包名 或者npm install 包名@版本号
:安装包或者指定版本的依赖包(安装到当前项目中)npm install -g 包名
:安装全局依赖包(安装到D:\softwareData\npmRepository
)则可以在任何项目中使用它,而无需在每个项目中独立安装该包npm install
:安装package.json中的所有记录的依赖
- 升级依赖
npm update 包名
:将依赖升级到最新版本
- 卸载依赖
npm uninstall 包名
- 查看依赖
npm ls
:查看项目依赖npm list -g
:查看全局依赖
- 运行命令
npm run
:执行 npm 脚本时使用的命令。npm 脚本是一组在 package.json 文件中定义的可执行命令。npm 脚本可用于启动应用程序,运行测试,生成文档等,还可以自定义命令以及配置需要运行的脚本。- 在 package.json 文件中,scripts 字段是一个对象,其中包含一组键值对,键是要运行的脚本的名称,值是要执行的命令。
- scripts 对象包含 start、test 和 build 三个脚本。当您运行 npm run start 时,将运行 node index.js,并启动应用程序。同样,运行 npm run test 时,将运行 Jest 测试套件,而 npm run build 将运行 webpack 命令以生成最终的构建输出
四、Vue3简介和快速体验
Vue (发音为 /vjuː/,类似 view) 是一款用于构建用户界面的 JavaScript 框架。它基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。无论是简单还是复杂的界面,Vue 都可以胜任。官网为: https://cn.vuejs.org/
Vue的两个核心功能:
- 声明式渲染:Vue 基于标准 HTML 拓展了一套模板语法,使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系
- 响应性:Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新 DOM
Vue3快速体验(非工程化方式)
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><!-- 这里也可以用浏览器打开连接,然后将获得的文本单独保存进入一个vue.js的文件,导入vue.js文件即可 --><script src="https://unpkg.com/vue@3/dist/vue.global.js"></script><div id="app"><!-- 给style属性绑定colorStyle数据 --><!-- {{插值表达式 直接将数据放在该位置}} --><h1 v-bind:style="colorStyle">{{headline}}</h1><!-- v-text设置双标签中的文本 --><p v-text="article"></p><!-- 给type属性绑定inputType数据 --><input v-bind:type ="inputType" value="helloVue3"> <br><!-- 给按钮单击事件绑定函数 --><button @click="sayHello()">hello</button></div><script>//组合apiconst app = Vue.createApp({// 在setup内部自由声明数据和方法即可!最终返回!setup(){//定义数据//在VUE中实现DOM的思路是: 通过修改修数据而影响页面元素// vue3中,数据默认不是响应式的,需要加ref或者reactive处理,后面会详细讲解let inputType ='text'let headline ='hello vue3'let article ='vue is awesome' let colorStyle ={'color':'red'} // 定义函数let sayHello =()=>{alert("hello Vue")}//在setup函数中,return返回的数据和函数可以在html使用return {inputType,headline,article,colorStyle,sayHello}}});//挂载到视图app.mount("#app");</script></body>
</html>
五、Vue3通过Vite实现工程化
5.1 Vite 简介
Vite作用相当于 Java 中 maven 的项目管理功能。
Vite 旨在利用生态系统中的新进展解决前端项目管理问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写。https://cn.vitejs.dev/guide/why.html前端工程化的作用包括但不限于以下几个方面:
- 快速创建项目:使用脚手架可以快速搭建项目基本框架,避免从零开始搭建项目的重复劳动和繁琐操作,从而节省时间和精力。
- 统一的工程化规范:前端脚手架可以预设项目目录结构、代码规范、git提交规范等统一的工程化规范,让不同开发者在同一个项目上编写出风格一致的代码,提高协作效率和质量。
- 代码模板和组件库:前端脚手架可以包含一些常用的代码模板和组件库,使开发者在实现常见功能时不再重复造轮子,避免因为轮子质量不高带来的麻烦,能够更加专注于项目的业务逻辑。
- 自动化构建和部署:前端脚手架可以自动进行代码打包、压缩、合并、编译等常见的构建工作,可以通过集成自动化部署脚本,自动将代码部署到测试、生产环境等。
5.2 Vite创建Vue3工程化项目
项目创建:项目目录下执行命令:执行后会出现选项选择框架。选择生成后,进入项目目录,执行 npm install
npm create vite@latest
cd .\vite-project\
npm install
看 package.json
{"name": "vite-project","private": true,"version": "0.0.0","type": "module","scripts": {"dev": "vite", // 启动开发服务器,别名:`vite dev`,`vite serve`"build": "vite build", // 为生产环境构建产物"preview": "vite preview" // 本地预览生产构建产物},"dependencies": {"vue": "^3.3.8"},"devDependencies": {"@vitejs/plugin-vue": "^4.5.0","vite": "^5.0.0"}
}
scripts
中即可运行选项,这里执行命令启动本地开发环境
npm run dev
5.3 Vite+Vue3项目的目录结构
public/ 目录
:用于存放一些公共资源,如 HTML 文件、图像、字体等,这些资源会被直接复制到构建出的目标目录中。src/ 目录
:存放项目的源代码,包括 JavaScript、CSS、Vue 组件、图像和字体等资源。在开发过程中,这些文件会被 Vite 实时编译和处理,并在浏览器中进行实时预览和调试。以下是src内部划分建议:assets/
目录:用于存放一些项目中用到的静态资源,如图片、字体、样式文件等。components/
目录:用于存放组件相关的文件。组件是代码复用的一种方式,用于抽象出一个可复用的 UI 部件,方便在不同的场景中进行重复使用。layouts/
目录:用于存放布局组件的文件。布局组件通常负责整个应用程序的整体布局,如头部、底部、导航菜单等。pages/
目录:用于存放页面级别的组件文件,通常是路由对应的组件文件。在这个目录下,可以创建对应的文件夹,用于存储不同的页面组件。plugins/
目录:用于存放 Vite 插件相关的文件,可以按需加载不同的插件来实现不同的功能,如自动化测试、代码压缩等。router/
目录:用于存放 Vue.js 的路由配置文件,负责管理视图和 URL 之间的映射关系,方便实现页面之间的跳转和数据传递。store/
目录:用于存放 Vuex 状态管理相关的文件,负责管理应用程序中的数据和状态,方便统一管理和共享数据,提高开发效率。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 元素中。
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.4 Vite+Vue3项目组件(SFC入门)
什么是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中的核心组件,所有的其他组件都要通过该组件进行导入,该组件通过路由可以控制页面的切换
5.5 Vite+Vue3关于样式的导入方式
-
全局引入main.js
import './style/reset.css' //书写引入的资源的相对路径即可!
-
vue文件script代码引入
import './style/reset.css'
-
Vue文件style代码引入
@import './style/reset.css'
5.6 Vite+Vue3响应式和setup函数
使用前需要先导入:import { ref, reactive } from 'vue'
-
VUE3中变量要经过
ref
或者reactive
处理后才是响应式的,VUE2中默认都是响应式的,注意:该项都是单向绑定ref
:修饰基本类型的数据。处理的响应式数据在js编码修改的时候需要通过.value
操作。在绑定到html上时不需要.value
reactive
:适合修饰对象,直接使用即可,无需通过.value
toRef
:基于reactive响应式对象上的一个属性,创建一个对应的 ref 响应式数据toRefs
:将一个响应式对象多个属性转换为一个多个ref数据,如let {counter,name} = toRefs(data)
-
双向绑定:一般用于表单,也有人称为收集表单信息的命令 。
v-model:value='数据'
,一般省略value简写v-model='数据'
-
通过setup关键字可以省略
export default {setup(){ return{}}}
这些冗余的语法结构
<script type="module" setup>
5.7 关于JS和TS选择的问题
TS是JS的一个超集,使用TS之后,JS的语法更加的像JAVA,实际开发中用的确实更多,但TS不是非学不可,不用TS仍然可以正常开发工程化的前端项目,如有必要可后续精进
六、Vue3视图渲染技术
Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作
6.1 模板语法
文本渲染:
v-**
这种写法的方式使用的是vue的命令,这种命令必须依赖元素,并且要写在元素的开始标签中
- 插值表达式:最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 ,即双大括号
{{}}
v-text
:将数据以字符串显示,插值表达式与该标签效果一致v-html
:将数据以 html 格式显示
属性渲染:
v-bind
:可以用于渲染任何元素的属性,语法为 v-bind:属性名='数据名'
, 可以简写为 :属性名='数据名'
事件的绑定:
v-on
:v-on:click="handler"
或简写为 @click="handler"
vue中的事件名=原生事件名去掉 on
前缀 如:onClick --> click
。
绑定事件时,可以通过一些绑定的修饰符,常见的事件修饰符如下:
.once
:只触发一次事件。[重点].prevent
:阻止默认事件。用原生 js 可以增加控制逻辑,通过方法传入$event
对象,调用event.preventDefault
实现[重点].stop
:阻止事件冒泡.capture
:使用事件捕获模式而不是冒泡模式.self
:只在事件发送者自身触发时才触发事件
6.2 条件渲染
v-if='flag'
及v-else
v-show
:不同之处在于v-show
会在 DOM 渲染中保留该元素,仅切换display
的 CSS 属性,且v-show
不支持在template
元素上使用v-if
vsv-show
:v-if
有更高的切换开销,而v-show
有更高的初始渲染开销。因此,如果需要频繁切换,则使用v-show
较好;如果在运行时绑定条件很少改变,则v-if
会更合适
6.3 列表渲染
v-for
:指令的值需要使用item in items
形式的特殊语法,也支持使用可选的第二个参数表示当前项的位置索引(item, index) in items
6.4 属性计算
// 一个计算属性 ref
const publishedBooksMessage = computed(() => {console.log("publishedBooksMessage")return author.books.length > 0 ? 'Yes' : 'No'
})
computed()
方法期望接收一个 getter 函数,返回值为一个计算属性 ref,Vue 的计算属性会自动追踪响应式依赖。它会检测到publishedBooksMessage
依赖于author.books
,所以当author.books
改变时,任何依赖于publishedBooksMessage
的绑定都会同时更新。- 计算属性值会基于其响应式依赖被缓存,仅会在其响应式依赖更新时才重新计算
6.5 数据监听器
watch:只追踪明确侦听的数据源
- 监听一个 ref 响应式数据:
watch(firstname, (newValue,oldValue) => {})
- 监听 reactive 响应式数据的指定属性:
watch(()=>lastname.name,(newValue,oldValue)=>{})
- 监听reactive响应式数据的所有属性(深度监视,一般不推荐):
watch(()=>lastname,(newValue,oldValue)=>{},{deep:true,immediate:false}
deep:true
:深度监视immediate:true
:深度监视在进入页面时立即执行一次,此时的 newValue 和 oldValue 一样,都是 lastname
watchEffect:自动追踪回调函数中所有能访问到的响应式属性
//监听所有响应式数据
watchEffect(()=>{//直接在内部使用监听属性即可,不用外部声明也不需要,即时回调设置,默认初始化就加载一次console.log(firstname.value)console.log(lastname.name)fullname.value=`${firstname.value}${lastname.name}`
})
6.6 生命周期
常见钩子函数
- onMounted() 注册一个回调函数,在组件挂载完成后执行。
- onUpdated() 注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。
- onUnmounted() 注册一个回调函数,在组件实例被卸载之后调用。
- onBeforeMount() 注册一个钩子,在组件被挂载之前被调用。
- onBeforeUpdate() 注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
- onBeforeUnmount() 注册一个钩子,在组件实例被卸载之前调用。
6.7 组件之前传递数据
父传子:在父组件中定义属性,使用 :变量名
的方式传递给子组件。子组件中使用 defineProps
函数接收父组件的传值
子传父:在子组件中使用 defineEmits
函数定义事件,再使用 emites('事件名', 数据)
执行数据传递;父组件中使用 @事件名=方法
的方式接收事件带来的数据
七、Vue3路由机制router
路由就是根据不同的 URL 地址展示不同的内容或页面
npm install vue-router@4 --save //vue-router,省略后面内容会安装最新版本的。安装全局的vue-router 4版本,
7.1 路由入门配置
<router-view>
:配置路由视图,可定义 name 属性指定视图名称
路由配置:src/router/router.js
// 导入路由创建的相关方法
import {createRouter,createWebHashHistory} from 'vue-router'
// 导入vue组件
import Home from '../components/Home.vue'
import List from '../components/List.vue'
// 创建路由对象,声明路由规则
const router = createRouter({//createWebHashHistory() 是 Vue.js 基于 hash 模式创建路由的工厂函数。在使用这种模式下,路由信息保存在 URL 的 hash 中,//使用 createWebHashHistory() 方法,可以创建一个路由历史记录对象,用于管理应用程序的路由。在 Vue.js 应用中,//通常使用该方法来创建路由的历史记录对象。//就是路由中缓存历史记录的对象,vue-router提供history: createWebHashHistory(),routes:[{path:'/',/* component指定组件在默认的路由视图位置展示components:Homecomponents指定组件在name为某个值的路由视图位置展示components:{default:Home,// 默认路由视图位置homeView:Home// name为homeView的路由视图位置} */components:{default:Home,homeView:Home} },{path:'/list',components:{listView : List} },{path:'/showAll',// 重定向:将一个路由重定向到另一个路由上redirect :'/list'}]})// 对外暴露路由对象
export default router;
引入 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")
总结3步骤:
- 配置路由视图
router-view
- 添加路由配置
router.js
- 引入路由
main.js
7.2 编程式路由(useRouter)
普通路由:<router-link to="/list">list页</router-link>
这种路由,to中的内容目前是固定的,点击后只能切换/list对象组件(声明式路由)
编程式路由:通过 useRouter
,动态决定向那个组件切换的路由,useRouter 返回的即 router.js 中 export 的对象
<script setup type="module">import {useRouter} from 'vue-router'//创建动态路由对象let router = useRouter()let showList= ()=>{// 编程式路由// 直接push一个路径//router.push('/list')// push一个带有path属性的对象router.push({path:'/list'})}
</script>
7.3 路由传参(useRoute)
路径参数:如 /showDetail/:id
- 路径参数首先要在
router.js
配置的路由上加上/:参数名
表示路由参数 - 发送时可用路径直接发送或者使用:useRouter 返回的对象调用
router.push({name:"showDetail",params:{id:id,language:language}})
- 接收时使用
useRoute
返回的对象的params
属性获取
键值对参数:如 /showDetail?hid=1
,则将上述 params
替换成 query
属性即可
7.4 路由守卫
- 全局前置守卫:在路由切换前被调用,可以用于验证用户是否已登录、中断导航、请求数据等
- 全局后置守卫:在路由切换之后被调用,可以用于处理数据、操作 DOM 、记录日志等
- 守卫代码的位置: 在
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}`);
});
八、promise
前端中的异步编程技术,类似Java中的多线程+线程结果回调
- Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了
Promise
对象 - 所谓
Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理
Promise
对象有以下两个特点。
- Promise对象代表一个异步操作,有三种状态:
Pending
(进行中)、Resolved
(已完成,又称 Fulfilled)和Rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。 - 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从
Pending
变为Resolved
和从Pending
变为Rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。
8.1 基本用法
<script>/* 1.实例化promise对象,并且执行(类似Java创建线程对象,并且start)参数: resolve,reject随意命名,但是一般这么叫参数: resolve,reject分别处理成功和失败的两个函数: 成功resolve(结果) 失败reject(结果)参数: 在function中调用这里两个方法,那么promise会处于两个不同的状态状态: promise有三个状态pending 正在运行resolved 内部调用了resolve方法rejected 内部调用了reject方法参数: 在第二步回调函数中就可以获取对应的结果 */let promise =new Promise(function(resolve,reject){console.log("promise do some code ... ...")//resolve("promise success")reject("promise fail")})console.log('other code1111 invoked')//2.获取回调函数结果 then在这里会等待promise中的运行结果,但是不会阻塞代码继续运行promise.then(function(value){console.log(`promise中执行了resolve:${value}`)},function(error){console.log(`promise中执行了reject:${error}`)})// 3 其他代码执行 console.log('other code2222 invoked')</script>
8.2 catch()
Promise.prototype.catch
方法是 .then(null, rejection)
的别名,用于指定发生错误时的回调函数。
<script>let promise =new Promise(function(resolve,reject){console.log("promise do some code ... ...")// 故意响应一个异常对象throw new Error("error message")})console.log('other code1111 invoked')/* then中的reject()的对应方法可以在产生异常时执行,接收到的就是异常中的提示信息then中可以只留一个resolve()的对应方法,reject()方法可以用后续的catch替换then中的reject对应的回调函数被后续的catch替换后,catch中接收的数据是一个异常对象*/promise.then(function(resolveValue){console.log(`promise中执行了resolve:${resolveValue}`)}//,//function(rejectValue){console.log(`promise中执行了reject:${rejectValue}`)}).catch(function(error){console.log(error)} )console.log('other code2222 invoked')
</script>
8.3 async和await的使用
async/await 是在Promise基础上提供了更加直观和易于使用的语法。
async 用于标识函数
- async标识函数后,async函数的返回值会变成一个promise对象
- 如果函数内部返回的数据是一个非promise对象,async函数的结果会返回一个成功状态 promise对象
- 如果函数内部返回的是一个promise对象,则async函数返回的状态与结果由该对象决定
- 如果函数内部抛出的是一个异常,则async函数返回的是一个失败的promise对象
await :获取一个 promise 对象的成功值,失败则会抛异常
- await右侧的表达式一般为一个promise对象,但是也可以是一个其他值
- 如果表达式是promise对象,await返回的是promise成功的值
- await会等右边的promise对象执行结束,然后再获取结果,后续代码也会等待await的执行
- 如果表达式是其他值,则直接返回该值
- await必须在async函数中
- 如果await右边的promise失败了,就会抛出异常,需要通过 try … catch捕获处理
九、数据交互axios
Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http
模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。
原生javascript方式进行ajax(了解):
<script>function loadXMLDoc(){var xmlhttp;if (window.XMLHttpRequest){// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码xmlhttp=new XMLHttpRequest();}else{// IE6, IE5 浏览器执行代码xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}// 设置回调函数处理响应结果xmlhttp.onreadystatechange=function(){if (xmlhttp.readyState==4 && xmlhttp.status==200){document.getElementById("myDiv").innerHTML=xmlhttp.responseText;}}// 设置请求方式和请求的资源路径xmlhttp.open("GET","/try/ajax/ajax_info.txt",true);// 发送请求xmlhttp.send();}
</script>
9.1 基本用法
使用 axios 前需先安装
npm install axios
使用:
<script setup type="module">import axios from 'axios'let getLoveMessage =()=>{axios({method:"post", // 请求方式url:"https://api.uomg.com/api/rand.qinghua?format=json", // 请求的urldata:{ // 当请求方式为post时,data下的数据以JSON串放入请求体,否则以key=value形式放url后username:"123456"}}).then( function (response){//响应成功时要执行的函数console.log(response)Object.assign(jsonData,response.data)}).catch(function (error){// 响应失败时要执行的函数console.log(error)})}
</script>
response 响应数据:
{data: {}, // `data` 由服务器提供的响应status: 200, // `status` 来自服务器响应的 HTTP 状态码statusText: 'OK', // `statusText` 来自服务器响应的 HTTP 状态信息// `headers` 是服务器响应头// 所有的 header 名称都是小写,而且可以使用方括号语法访问// 例如: `response.headers['content-type']`headers: {},config: {}, // `config` 是 `axios` 请求的配置信息// `request` 是生成此响应的请求// 在node.js中它是最后一个ClientRequest实例 (in redirects),// 在浏览器中则是 XMLHttpRequest 实例request: {}
}
axios在发送异步请求时的可选配置:
{// `url` 是用于请求的服务器 URLurl: '/user',// `method` 是创建请求时使用的方法method: 'get', // 默认值// `baseURL` 将自动加在 `url` 前面,除非 `url` 是一个绝对 URL。// 它可以通过设置一个 `baseURL` 便于为 axios 实例的方法传递相对 URLbaseURL: '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, Bufferdata: {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 Authauth: {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 requestsresponseEncoding: '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/cancellationcancelToken: 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.2 Axios get和post方法
axios.get(url[, config])axios.get(url,{上面指定配置key:配置值,上面指定配置key:配置值
})axios.post(url[, data[, config]])axios.post(url,{key:value},{上面指定配置key:配置值,上面指定配置key:配置值
})
9.3 Axios 拦截器
定义 src/utils/request.js
提取拦截器和配置语法
// 创建instance实例
const instance = axios.create({baseURL:'http://localhost:8080/',timeout:10000
})// 添加请求拦截
instance.interceptors.request.use(// 请求前处理函数config=>{//处理指定的请求头return config},// 设置请求错误处理函数error=>{return Promise.reject(error)}
)
// 添加响应拦截器
instance.interceptors.response.use(// 设置响应正确时的处理函数,2xx 范围内的状态码都会触发该函数response=>{return response},// 设置响应异常时的处理函数,超出 2xx 范围的状态码都会触发该函数error=>{return Promise.reject(error)}
)
// 默认导出
export default instance
十、跨域问题
同源策略(Sameoriginpolicy)是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号
解决跨域的两个方式,1 前端代理模式(高并发下效果不好,一般不用)2 后端跨域过滤器
后端跨域过滤器:CrosFilter过滤器
package com.pro.filter;import com.pro.common.Result;
import com.pro.util.WebUtil;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;import java.io.IOException;@WebFilter("/*")
public class CrosFilter implements Filter {@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest request = (HttpServletRequest) servletRequest;System.out.println(request.getMethod());HttpServletResponse response = (HttpServletResponse) servletResponse;response.setHeader("Access-Control-Allow-Origin", "*");response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, HEAD");response.setHeader("Access-Control-Max-Age", "3600");response.setHeader("Access-Control-Allow-Headers", "access-control-allow-origin, authority, content-type, version-info, X-Requested-With");// 如果是跨域预检请求,则直接在此响应200业务码if(request.getMethod().equalsIgnoreCase("OPTIONS")){WebUtil.writeJson(response, Result.ok(null));}else{// 非预检请求,放行即可filterChain.doFilter(servletRequest, servletResponse);}}
}
未来使用框架,用一个 @CrossOrigin
就可以解决跨域问题了
十一、Pinia
Pinia 是 Vue 的专属状态管理库,它允许你跨组件或页面共享状态。由 Vue 核心团队维护,对 Vue 2 和 Vue 3 都可用
11.1 基本用法
先安装
npm install pinia
定义 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}}}
)
在 main.js
配置 pinia
组件到vue中
let app =createApp(App)
app.use(router)
// app中使用pinia功能
app.use(pinia)
app.mount('#app')
pinia
使用:
<script setup type="module">import { ref} from 'vue';import { definedPerson} from '../store/store';// 读取存储的数据let person= definedPerson()</script><template><div><h1>operate视图,用户操作Pinia中的数据</h1>请输入姓名:<input type="text" v-model="person.username"> <br>请输入年龄:<input type="text" v-model="person.age"> <br>请增加爱好:<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><!-- 事件中调用person的doubleAge()方法 --><button @click="person.doubleAge()">年龄加倍</button> <br><!-- 事件中调用pinia提供的$reset()方法恢复数据的默认值 --><button @click="person.$reset()">恢复默认值</button> <br><!-- 事件中调用$patch方法一次性修改多个属性值 --><button @click="person.$patch({username:'奥特曼',age:100,hobbies:['晒太阳','打怪兽']})">变身奥特曼</button> <br>显示pinia中的person数据:{{person}}</div>
</template>
<style scoped>
</style>
11.2 Pinia其他细节
- 状态监听及初始状态
State (状态) 在大多数情况下,state 都是你的 store 的核心。人们通常会先定义能代表他们 APP 的 state。在 Pinia 中,state 被定义为一个返回初始状态的函数。
// 读取存储的数据let person= definedPerson()// 监听状态person.$subscribe((mutation,state)=>{console.log('---subscribe---')/* mutation.storeIdperson.$id一样mutation.payload传递给 cartStore.$patch() 的补丁对象。state 数据状态,其实是一个代理*/console.log(mutation)console.log(mutation.type)console.log(mutation.payload)console.log(mutation.storeId)console.log(person.$id)// 数据 其实是一个代理对象console.log(state)})
- Getter 完全等同于 store 的 state 的计算属性。可以通过
defineStore()
中的getters
属性来定义它们。推荐使用箭头函数,并且它将接收state
作为第一个参数
export const useStore = defineStore('main', {state: () => ({count: 0,}),getters: {doubleCount: (state) => state.count * 2,},
})
- 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
Element Plus 是一套基于 Vue 3 的开源 UI 组件库,是由饿了么前端团队开发的升级版本 Element UI。Element Plus 提供了丰富的 UI 组件、易于使用的 API 接口和灵活的主题定制功能,可以帮助开发者快速构建高质量的 Web 应用程序。
-
Element Plus 可以在支持 ES2018 和 ResizeObserver 的浏览器上运行。 如果您确实需要支持旧版本的浏览器,请自行添加 Babel 和相应的 Polyfill
-
官网https://element-plus.gitee.io/zh-CN/
-
由于 Vue 3 不再支持 IE11,Element Plus 也不再支持 IE 浏览器。
使用步骤:
- 安装:
npm install 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')
- 复制官网组件代码直接使用即可
https://element-plus.gitee.io/zh-CN/component/button.html