编辑推荐: |
本文主要介绍了前端工程化(VUE3)相关知识。希望对您的学习有所帮助。
本文来自于CSDN,由火龙果软件Linda编辑、推荐。 |
|
一、概述
前端工程化 是使用 软件工程的方法 来 单独 解决 前端 的开发流程中 模块化、组件化、规范化、自动化
的问题,其主要目的为了提高效率和降低成本。
前端工程化实现的技术栈有很多,我们采用 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 = ()=>{}
let fn3 = x =>{}
let fn4 = x => console.log(x)
let fun5 = x => x + 1
|
使用特点:箭头函数this关键字
在 JavaScript 中,this 关键字通常用来引用函数所在的对象,或者在函数本身作为构造函数时,来引用新对象的实例。但是在箭头函数中,this
的含义与常规函数定义中的含义不同,是由箭头函数定义时的上下文来决定的,而不是由函数调用时的上下文来决定的。箭头函数没有自己的this,this指向的是外层上下文环境的this
let person ={
name:"张三",
showName:function (){
console.log(this)
console.log(this.name)
},
viewName: () =>{
console.log(this)
console.log(this.name)
}
}
person.showName()
person.viewName()
function Counter() {
this.count = 0;
window.setInterval(() => {
this.count++;
console.log(this.count);
}, 1000);
}
let counter = new Counter();
|
2.4 rest和spread
rest参数,在形参上使用和JAVA中的可变参数几乎一样
let fun = (...args) =>{console.log(args)}
|
spread参数,在函数调用实参上使用rest;还可以快速合并数组或者对象
let a = [1,2,3];
fun(...a);
let a = [1,2,3];
let b = [4,5,6];
let c = [...a,...b];
let a = { x: 'm', y: 'h'};
let b = { y: 'n'};
let c = {...a, ...b};
|
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
};
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 一个文件中只能写一个,代表默认的导出对象
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'
|
三、前端工程化环境搭建
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 set registry https://registry.npmjs.org/
|
配置全局依赖下载后存储位置:默认位置 <用户目录>\AppData\Roaming\npm
npm config set prefix "D:\softwareData\npmRepository"
|
查看依赖存储位置:
npm 升级命令
npm install -g npm@10.2.3
|
3.2.2 npm 常用命令
项目初始化
npm init :进入一个vscode创建好的项目中, 执行 npm init 命令后,npm 会引导您在命令行界面上回答一些问题,例如项目名称、版本号、作者、许可证等信息,并最终生成一个package.json
文件。package.json信息会包含项目基本信息,类似maven的pom.xml
npm 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>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">
<h1 v-bind:style="colorStyle">{{headline}}</h1>
<p v-text="article"></p>
<input v-bind:type ="inputType" value="helloVue3"> <br>
<button @click="sayHello()">hello</button>
</div>
<script>
const app = Vue.createApp({
setup(){
let inputType ='text'
let headline ='hello vue3'
let article ='vue is awesome'
let colorStyle ={'color':'red'}
let sayHello =()=>{
alert("hello Vue")
}
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",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.3.8"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.5.0",
"vite": "^5.0.0"
}
}
|
scripts 中即可运行选项,这里执行命令启动本地开发环境
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",
"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 vs v-show :v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要频繁切换,则使用
v-show 较好;如果在运行时绑定条件很少改变,则 v-if 会更合适
6.3 列表渲染
v-for :指令的值需要使用 item in items 形式的特殊语法,也支持使用可选的第二个参数表示当前项的位置索引
(item, index) in items
6.4 属性计算
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'
import Home from '../components/Home.vue'
import List from '../components/List.vue'
const router = createRouter({
history: createWebHashHistory(),
routes:[
{
path:'/',
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'
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) => {
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 前需先安装
使用:
<script setup type="module">
import axios from 'axios'
let getLoveMessage =()=>{
axios({
method:"post", // 请求方式
url:"https://api.uomg.com/api/rand.qinghua?format=json", // 请求的url
data:{ // 当请求方式为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: {},
status: 200,
statusText: 'OK',
headers: {},
config: {},
request: {}
}
|
axios在发送异步请求时的可选配置:
{
url: '/user',
method: 'get',
baseURL: 'https://some-domain.com/api/',
transformRequest: [function (data, headers) {
return data;
}],
transformResponse: [function (data) {
return data;
}],
headers: {'X-Requested-With': 'XMLHttpRequest'},
params: {
ID: 12345
},
paramsSerializer: function (params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
data: {
firstName: 'Fred'
},
data: 'Country=Brasil&City=Belo Horizonte',
timeout: 1000,
withCredentials: false,
adapter: function (config) {
},
auth: {
username: 'janedoe',
password: 's00pers3cret'
},
responseType: 'json',
responseEncoding: 'utf8',
xsrfCookieName: 'XSRF-TOKEN',
xsrfHeaderName: 'X-XSRF-TOKEN',
onUploadProgress: function (progressEvent) {
},
onDownloadProgress: function (progressEvent) {
},
maxContentLength: 2000,
maxBodyLength: 2000,
validateStatus: function (status) {
return status >= 200 && status < 300;
},
maxRedirects: 5,
socketPath: null,
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
proxy: {
protocol: 'https',
host: '127.0.0.1',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},
cancelToken: new CancelToken(function (cancel) {
}),
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 提取拦截器和配置语法
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(
response=>{
return response
},
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 {
@Override
public 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");
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 基本用法
先安装
定义 pinia store 对象 src/store/store.js
[推荐这么命名不是强制]
import {defineStore } from 'pinia'
export const definedPerson = defineStore(
{
id: 'personPinia',
state:()=>{
return {
username:'张三',
age:0,
hobbies:['唱歌','跳舞']
}
},
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.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>
<button @click="person.doubleAge()">年龄加倍</button> <br>
<button @click="person.$reset()">恢复默认值</button> <br>
<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---')
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 作为第一个参数
let person= definedPerson()
person.$subscribe((mutation,state)=>{
console.log('---subscribe---')
console.log(mutation)
console.log(mutation.type)
console.log(mutation.payload)
console.log(mutation.storeId)
console.log(person.$id)
console.log(state)
})
|
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'
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')
|
复制官网组件代码直接使用即可
|