前端工程化
前端工程化
是使用软件工程的方法
来单独
解决前端
的开发流程中模块化、组件化、规范化、自动化
的问题,其主要目的为了提高效率和降低成本。
实现技术栈
前端工程化实现的技术栈有很多,在这里采用ES6+nodejs+npm+Vite+VUE3+router+pinia+axios+Element-plus组合实现
ECMAScript6 VUE3中大量使用ES6语法
Nodejs 前端项目运行环境
npm 依赖下载工具
vite 前端项目构建工具
VUE3 优秀的渐进式前端框架
router 通过路由实现页面切换
pinia 通过状态管理实现组件数据传递
axios ajax异步请求封装技术实现前后端数据交互
Element-plus 可以提供丰富的快速构建网页的组件仓库
ECMAScript6
ECMAScript6,简称ES6,是JavaScript 语言的一次重大更新。ES6带来了大量的新特性,包括箭头函数、模板字符串、let和const关键字、解构、默认参数值、模块系统等等,大大提升了JavaScript的开发体验。
ES6对JavaScript的改进在以下几个方面:
更加简洁:ES6引入了一些新的语法,如箭头函数、类和模板字符串等,使代码更加简洁易懂。
更强大的功能:ES6引入了一些新的API、解构语法和迭代器等功能,从而使得JavaScript更加强大。
更好的适用性:ES6引入的模块化功能为JavaScript代码的组织和管理提供了更好的方式,不仅提高了程序的可维护性,还让JavaScript更方便地应用于大型的应用程序。
变量和模板字符串
ES6 新增了let
和const
,用来声明变量,使用的细节上也存在诸多差异
let和var的差别
1、let不能重复声明
2、let有块级作用域,非函数的花括号遇见let会有块级作用域,也就是只能在花括号里面访问。
3、let不会预解析进行变量提升
4、let定义的全局变量不会作为window的属性
const和var的差异
1、新增const和let类似,只是const定义的变量不能修改
2、并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。
模板字符串(template string)是增强版的字符串,用反引号(`)标识
1、字符串中可以出现换行符
2、可以使用 ${xxx} 形式输出变量和拼接变量
解构表达式
ES6 的解构赋值是一种方便的语法,可以快速将数组或对象中的值拆分并赋值给变量。解构赋值的语法使用花括号 {}
表示对象,方括号 []
表示数组。通过解构赋值,函数更方便进行参数接受等!
数组解构赋值
let [a, b, c] = [1 , 2 , 3 ]; console .log (a); console .log (b); console .log (c);
该语句将数组 [1, 2, 3] 中的第一个值赋值给 a 变量,第二个值赋值给 b 变量,第三个值赋值给 c 变量。 可以使用默认值为变量提供备选值,在数组中缺失对应位置的值时使用该默认值。例如:
let [a, b, c, d = 4 ] = [1 , 2 , 3 ];console .log (d);
对象解构赋值
let {a, b} = {a : 1 , b : 2 }; console .log (a); console .log (b);
该语句将对象 {a: 1, b: 2} 中的 a 属性值赋值给 a 变量,b 属性值赋值给 b 变量。 可以为标识符分配不同的变量名称,使用 : 操作符指定新的变量名。例如:
let {a : x, b : y} = {a : 1 , b : 2 };console .log (x); console .log (y);
函数参数解构赋值
function add ([x, y] ) { return x + y; } add ([1 , 2 ]);
箭头函数 示例 <script > let fn1 = function ( ){} let fn2 = ( )=>{} let fn3 = x =>{} let fn4 = x => console .log (x) let fun5 = x => x + 1 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 ; setInterval (() => { this .count ++; console .log (this .count ); }, 1000 ); } let counter = new Counter (); </script >
rest和spread
rest参数,在形参上使用和JAVA中的可变参数几乎一样
<script > let fun1 = function (a,b,c,d=10 ){console .log (a,b,c,d)} let fun2 = (a,b,c,d=10 ) =>{console .log (a,b,c,d)} fun1 (1 ,2 ,3 ) fun2 (1 ,2 ,3 ,4 ) let fun3 = function (...args ){console .log (args)} let fun4 = (...args ) =>{console .log (args)} fun3 (1 ,2 ,3 ) fun4 (1 ,2 ,3 ,4 ) </script >
spread参数,在实参上使用rest
<script > let arr =[1 ,2 ,3 ] let fun1 =(a,b,c ) =>{ console .log (a,b,c) } fun1 (...arr) let arr2=[4 ,5 ,6 ] let arr3=[...arr,...arr2] console .log (arr3) let p1={name :"张三" } let p2={age :10 } let p3={gender :"boy" } let person ={...p1,...p2,...p3} console .log (person) </script >
对象创建和拷贝 对象创建的语法糖
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 ("面条" )
模块化处理
模块化是一种组织和管理前端代码的方式,将代码拆分成小的模块单元,使得代码更易于维护、扩展和复用。它包括了定义、导出、导入以及管理模块的方法和规范。前端模块化的主要优势如下:
提高代码可维护性:通过将代码拆分为小的模块单元,使得代码结构更为清晰,可读性更高,便于开发者阅读和维护。
提高代码可复用性:通过将重复使用的代码变成可复用的模块,减少代码重复率,降低开发成本。
提高代码可扩展性:通过模块化来实现代码的松耦合,便于更改和替换模块,从而方便地扩展功能。
目前,前端模块化有多种规范和实现,包括 CommonJS、AMD 和 ES6 模块化。ES6 模块化是 JavaScript 语言的模块标准,使用 import 和 export 关键字来实现模块的导入和导出。现在,大部分浏览器都已经原生支持 ES6 模块化,因此它成为了最为广泛使用的前端模块化标准
ES6模块化的几种暴露和导入方式
分别导出
统一导出
默认导出
ES6中无论以何种方式导出,导出的都是一个对象,导出的内容都可以理解为是向这个对象中添加属性或者方法
分别导出
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.` ); } }
import * as m1 from './module.js' console .log (m1.PI )let result =m1.sum (10 ,20 )console .log (result)let person =new m1.Person ('张三' ,10 )person.sayHello ()
index.html作为程序启动的入口 导入 app.js
<script src ="./app.js" type ="module" />
统一导出
const PI = 3.14 function sum (a, b ) { return a + b; } class Person { constructor (name, age ) { this .name = name; this .age = age; } sayHello ( ) { console .log (`Hello, my name is ${this .name} , I'm ${this .age} years old.` ); } } export { PI , sum, Person }
import {PI ,Person ,sum,PI as pi,Person as People ,sum as add} from './module.js' console .log (PI )console .log (pi)let result1 =sum (10 ,20 )console .log (result1)let result2 =add (10 ,20 )console .log (result2)let person1 =new Person ('张三' ,10 )person1.sayHello () let person2 =new People ('李四' ,11 )person2.sayHello ()
默认导出
export const PI = 3.14 function sum (a, b ) { return a + b; } class Person { constructor (name, age ) { this .name = name; this .age = age; } sayHello ( ) { console .log (`Hello, my name is ${this .name} , I'm ${this .age} years old.` ); } } export default sumexport { Person }
import * as m1 from './module.js' import {default as add} from './module.js' import add2 from './module.js' let result =m1.default (10 ,20 )console .log (result)let result2 =add (10 ,20 )console .log (result2)let result3 =add2 (10 ,20 )console .log (result3)import {PI ,Person } from './module.js' let person =new Person ('张三' ,10 )person.sayHello () console .log (PI )
前端工程化环境搭建 nodejs
Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,可以使 JavaScript 运行在服务器端。使用 Node.js,可以方便地开发服务器端应用程序,如 Web 应用、API、后端服务,还可以通过 Node.js 构建命令行工具等。相比于传统的服务器端语言(如 PHP、Java、Python 等),Node.js 具有以下特点:
单线程,但是采用了事件驱动、异步 I/O 模型,可以处理高并发请求。
轻量级,使用 C++ 编写的 V8 引擎让 Node.js 的运行速度很快。
模块化,Node.js 内置了大量模块,同时也可以通过第三方模块扩展功能。
跨平台,可以在 Windows、Linux、Mac 等多种平台下运行。
npm
NPM全称Node Package Manager,是Node.js包管理工具,是全球最大的模块生态系统,里面所有的模块都是开源免费的;也是Node.js的包管理工具,相当于后端的Maven 。
npm配置
配置依赖下载使用阿里镜像
npm 安装依赖包时默认使用的是官方源,由于国内网络环境的原因,有时会出现下载速度过慢的情况。为了解决这个问题,可以配置使用阿里镜像来加速 npm 的下载速度,具体操作如下:
打开命令行终端,执行以下命令,配置使用阿里镜像:
npm config set registry https://registry.npmmirror.com
确认配置已生效,可以使用以下命令查看当前 registry 的配置:如果输出结果为 https://registry.npmmirror.com
,说明配置已成功生效。
npm config set registry https :
配置全局依赖下载后存储位置
npm常用命令
1.项目初始化
npm init
进入一个vscode创建好的项目中, 执行 npm init 命令后,npm 会引导您在命令行界面上回答一些问题,例如项目名称、版本号、作者、许可证等信息,并最终生成一个package.json 文件。package.json信息会包含项目基本信息!类似maven的pom.xml
npm init -y
执行,-y yes的意思,所有信息使用当前文件夹的默认值
2.安装依赖 (查看所有依赖地址 )
npm install 包名 或者 npm install 包名@版本号
npm install -g 包名
安装全局依赖包(安装到默认全局目录)则可以在任何项目中使用它,而无需在每个项目中独立安装该包。
npm install
3.升级依赖
4.卸载依赖
5.查看依赖
6.运行命令
npm run 命令是在执行 npm 脚本时使用的命令。npm 脚本是一组在 package.json 文件中定义的可执行命令。npm 脚本可用于启动应用程序,运行测试,生成文档等,还可以自定义命令以及配置需要运行的脚本。
在 package.json 文件中,scripts 字段是一个对象,其中包含一组键值对,键是要运行的脚本的名称,值是要执行的命令。例如,以下是一个简单的 package.json 文件:
scripts 对象包含 start、test 和 build 三个脚本。当您运行 npm run start 时,将运行 node index.js,并启动应用程序。同样,运行 npm run test 时,将运行 Jest 测试套件,而 npm run build 将运行 webpack 命令以生成最终的构建输出。
总之,npm run 命令提供了一种在 package.json 文件中定义和管理一组指令的方法,可以在项目中快速且灵活地运行各种操作。
Vue3 Vue的两个核心功能:
声明式渲染 :Vue 基于标准 HTML 拓展了一套模板语法,使得我们可以声明式地描述最终输出的 HTML 和 JavaScript 状态之间的关系。
响应性 :Vue 会自动跟踪 JavaScript 状态并在其发生变化时响应式地更新DOM
Vue文档
通过Vite实现工程化
Vite 旨在利用生态系统中的新进展解决上述问题:浏览器开始原生支持 ES 模块,且越来越多 JavaScript 工具使用编译型语言编写。https://cn.vitejs.dev/guide/why.html
前端工程化的作用包括但不限于:
快速创建项目:使用脚手架可以快速搭建项目基本框架,避免从零开始搭建项目的重复劳动和繁琐操作,从而节省时间和精力。
统一的工程化规范:前端脚手架可以预设项目目录结构、代码规范、git提交规范等统一的工程化规范,让不同开发者在同一个项目上编写出风格一致的代码,提高协作效率和质量。
代码模板和组件库:前端脚手架可以包含一些常用的代码模板和组件库,使开发者在实现常见功能时不再重复造轮子,避免因为轮子质量不高带来的麻烦,能够更加专注于项目的业务逻辑。
自动化构建和部署:前端脚手架可以自动进行代码打包、压缩、合并、编译等常见的构建工作,可以通过集成自动化部署脚本,自动将代码部署到测试、生产环境等。
Vite创建Vue3工程化项目
1 使用命令行创建工程
第一次使用vite时会提示下载vite,输入y回车即可,下次使用vite就不会出现了
2 安装项目所需依赖
cd ./vue3-demo1 npm install
3 启动项目
{ "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" } }
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 元素中。
2.vite的运行界面
在安装了 Vite 的项目中,可以在 npm scripts 中使用 vite
可执行文件。下面是通过脚手架创建的 Vite 项目中默认的 npm scripts:(package.json)
{ "scripts" : { "dev" : "vite" , "build" : "vite build" , "preview" : "vite preview" } }
export default defineConfig ({ plugins : [vue ()], server :{ port :3000 } })
Vite+Vue3项目组件(SFC入门)
什么是VUE的组件?
一个页面作为整体,是由多个部分组成的,每个部分在这里就可以理解为一个组件
每个.vue文件就可以理解为一个组件,多个.vue文件可以构成一个整体页面
组件化给我们带来的另一个好处就是组件的复用和维护非常的方便
什么是.vue文件?
传统的页面有.html文件.css文件和.js文件三个文件组成(多文件组件)
vue将这文件合并成一个.vue文件(Single-File Component,简称 SFC,单文件组件)
.vue文件对js/css/html统一封装,这是VUE中的概念 该文件由三个部分组成<script> <template> <style>
template标签 代表组件的html部分代码 代替传统的.html文件
script标签 代表组件的js代码 代替传统的.js文件
style标签 代表组件的css样式代码 代替传统的.css文件
工程化vue项目如何组织这些组件?
index.html是项目的入口,其中 <div id ='app'></div>
是用于挂载所有组建的元素
index.html中的script标签引入了一个main.js文件,具体的挂载过程在main.js中执行
main.js是vue工程中非常重要的文件,他决定这项目使用哪些依赖,导入的第一个组件
App.vue是vue中的核心组件,所有的其他组件都要通过该组件进行导入,该组件通过路由可以控制页面的切换
Vite+Vue3响应式和setup函数
使用vite创建一个vue+JavaScript项目
npm create vite npm install npm run dev
vue3响应式数据入门
<script type ="module" > import {ref} from 'vue' export default { setup ( ){ let counter = ref (1 ) function increase ( ){ counter.value ++ } function decrease ( ){ counter.value -- } return { counter, increase, decrease } } } </script > <template > <div > <button @click ="decrease()" > -</button > {{ counter }} <button @click ="increase()" > +</button > </div > </template > <style scoped > button { border : 1px solid red; } </style >
setup函数和语法糖
<script type="module" setup> /* <script type="module" setup> 通过setup关键字 可以省略 export default {setup(){ return{}}}这些冗余的语法结构 */ import {ref} from 'vue' // 定义响应式数据 let counter = ref(1) // 定义函数 function increase(){ counter.value++ } function decrease(){ counter.value-- } </script> <template> <div> <button @click="decrease()">-</button> {{ counter }} <button @click="increase()">+</button> </div> </template> <style scoped> button{ border: 1px solid red; } </style>
Vue3视图渲染技术
Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作。
插值表达式和文本渲染
插值表达式:最基本的数据绑定形式是文本插值,它使用的是“Mustache”语法 ,即双大括号{{}}
插值表达式是将数据渲染到元素的指定位置的手段之一
插值表达式不绝对依赖标签,其位置相对自由
插值表达式中支持javascript的运算表达式
插值表达式中也支持函数的调用
<script setup type ="module" > let msg ="hello vue3" let getMsg = ( )=>{ return 'hello vue3 message' } let age = 19 let bee = '蜜 蜂' const carts = [{name :'可乐' ,price :3 ,number :10 },{name :'薯片' ,price :6 ,number :8 }]; function compute ( ){ let count = 0 ; for (let index in carts){ count += carts[index].price *carts[index].number ; } return count; } </script > <template > <div > <h1 > {{ msg }}</h1 > msg的值为: {{ msg }} <br > getMsg返回的值为:{{ getMsg() }} <br > 是否成年: {{ age>=18?'true':'false' }} <br > 反转: {{ bee.split(' ').reverse().join('-') }} <br > 购物车总金额: {{ compute() }} <br /> 购物车总金额: {{carts[0].price*carts[0].number + carts[1].price*carts[1].number}} <br > </div > </template > <style scoped > </style >
为了渲染双标中的文本,我们也可以选择使用v-text
和v-html
命令
v-***这种写法的方式使用的是vue的命令
v-***的命令必须依赖元素,并且要写在元素的开始标签中
v-***指令支持ES6中的字符串模板
插值表达式中支持javascript的运算表达式
插值表达式中也支持函数的调用
v-text可以将数据渲染成双标签中间的文本,但是不识别html元素结构的文本
v-html可以将数据渲染成双标签中间的文本,识别html元素结构的文本
<script setup type ="module" > let msg ='hello vue3' let getMsg = ( )=>{ return msg } let age = 19 let bee = '蜜 蜂' let redMsg ='<font color=\'red\'>msg</font>' let greenMsg =`<font color=\'green\'>${msg} </font>` </script > <template > <div > <span v-text ='msg' > </span > <br > <span v-text ='redMsg' > </span > <br > <span v-text ='getMsg()' > </span > <br > <span v-text ='age>18?"成年":"未成年"' > </span > <br > <span v-text ='bee.split(" ").reverse().join("-")' > </span > <br > <span v-html ='msg' > </span > <br > <span v-html ='redMsg' > </span > <br > <span v-html ='greenMsg' > </span > <br > <span v-html ="`<font color='green'>${msg}</font>`" > </span > <br > </div > </template > <style scoped > </style >
Attribute属性渲染
想要渲染一个元素的attribute,应该使用v-bind
指令
由于插值表达式不能直接放在标签的属性中,所有要渲染元素的属性就应该使用v-bind
v-bind可以用于渲染任何元素的属性,语法为 v-bind:属性名='数据名'
, 可以简写为 :属性名='数据名'
事件的绑定
我们可以使用 v-on
来监听 DOM 事件,并在事件触发时执行对应的 Vue的JavaScript代码。
用法:v-on:click="handler"
或简写为 @click="handler"
vue中的事件名=原生事件名去掉on
前缀 如:onClick --> click
handler的值可以是方法事件处理器,也可以是内联事件处理器
绑定事件时,可以通过一些绑定的修饰符,常见的事件修饰符如下
.once:只触发一次事件。[重点]
.prevent:阻止默认事件。[重点]
.stop:阻止事件冒泡。
.capture:使用事件捕获模式而不是冒泡模式。
.self:只在事件发送者自身触发时才触发事件。
<script setup type ="module" > import {ref} from 'vue' let count=ref (0 ) let addCount = ( )=>{ count.value ++ } let incrCount = (event )=>{ count.value ++ event.preventDefault (); } </script > <template > <div > <h1 > count的值是:{{ count }}</h1 > <button v-on:click ="addCount()" > addCount</button > <br > <button @click ="count++" > incrCount</button > <br > <button @click.once ="count++" > addOnce</button > <br > <a target ="_blank" @click.prevent ="count++" > prevent</a > <br > <a target ="_blank" @click ="incrCount($event)" > prevent</a > <br > </div > </template > <style scoped > </style >
响应式基础
此处的响应式是指: 数据模型发生变化时,自动更新DOM树内容,页面上显示的内容会进行同步变化,vue3的数据模型不是自动响应式的,需要我们做一些特殊的处理
响应式实现关键字ref
ref
可以将一个基本类型的数据(如字符串,数字等)转换为一个响应式对象。 ref
只能包裹单一元素
<script type ="module" setup > import {ref} from 'vue' let counter = ref (0 ); function show ( ){ alert (counter.value ); } let decr = ( ) =>{ counter.value --; } let incr = ( ) =>{ counter.value ++; } </script > <template > <div > <button @click ="counter--" > -</button > <button @click ="decr()" > -</button > {{ counter }} <button @click ="counter++" > +</button > <button @click ="incr()" > +</button > <hr > <button @click ="show()" > 显示counter值</button > </div > </template > <style scoped > </style >
在上面的例子中,我们使用 ref
包裹了一个数字,在代码中给这个数字加 1 后,视图也会跟着动态更新。需要注意的是,由于使用了 ref
,因此需要在访问该对象时使用 .value
来获取其实际值。
响应式实现关键字reactive
我们可以使用 reactive() 函数创建一个响应式对象或数组:
<script type ="module" setup > import {ref,reactive} from 'vue' let data = reactive ({ counter :0 }) function show ( ){ alert (data.counter ); } let decr = ( ) =>{ data.counter --; } let incr = ( ) =>{ data.counter ++; } </script > <template > <div > <button @click ="data.counter--" > -</button > <button @click ="decr()" > -</button > {{ data.counter }} <button @click ="data.counter++" > +</button > <button @click ="incr()" > +</button > <hr > <button @click ="show()" > 显示counter值</button > </div > </template > <style scoped > </style >
对比ref和reactive:
条件渲染
v-if
条件渲染
v-if='表达式'
只会在指令的表达式返回真值时才被渲染
也可以使用 v-else
为 v-if
添加一个“else 区块”。
一个 v-else
元素必须跟在一个 v-if
元素后面,否则它将不会被识别。
v-show
条件渲染扩展:
另一个可以用来按条件显示一个元素的指令是 v-show
。其用法基本一样:
不同之处在于 v-show
会在 DOM 渲染中保留该元素;v-show
仅切换了该元素上名为 display
的 CSS 属性。
v-show
不支持在 <template>
元素上使用,也不能和 v-else
搭配使用。
v-if
vs v-show
v-if
是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
v-if
也是惰性 的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。
相比之下,v-show
简单许多,元素无论初始条件如何,始终会被渲染,只有 CSS display
属性会被切换。
总的来说,v-if
有更高的切换开销,而 v-show
有更高的初始渲染开销。因此,如果需要频繁切换,则使用 v-show
较好;如果在运行时绑定条件很少改变,则 v-if
会更合适。
列表渲染
我们可以使用 v-for
指令基于一个数组来渲染一个列表。
双向绑定
单项绑定和双向绑定
单向绑定: 响应式数据的变化会更新dom树,但是dom树上用户的操作造成的数据改变不会同步更新到响应式数据
双向绑定: 响应式数据的变化会更新dom树,但是dom树上用户的操作造成的数据改变会同步更新到响应式数据
用户通过表单标签才能够输入数据,所以双向绑定都是应用到表单标签上的,其他标签不行
v-model专门用于双向绑定表单标签的value属性,语法为 v-model:value=''
,可以简写为 v-model=''
v-model还可以用于各种不同类型的输入,<textarea>
、<select>
元素
<script type ="module" setup > import { reactive,ref} from 'vue' let hbs = ref ([]); let user = reactive ({username :null ,password :null ,introduce :null ,pro :null }) function login ( ){ alert (hbs.value ); alert (JSON .stringify (user)); } function clearx ( ){ user.username ='' user.password ='' user.introduce ='' user.pro ='' hbs.value .splice (0 ,hbs.value .length );; } </script > <template > <div > 账号: <input type ="text" placeholder ="请输入账号!" v-model ="user.username" > <br > 密码: <input type ="text" placeholder ="请输入账号!" v-model ="user.password" > <br > 爱好: 吃 <input type ="checkbox" name ="hbs" v-model ="hbs" value ="吃" > 喝 <input type ="checkbox" name ="hbs" v-model ="hbs" value ="喝" > 玩 <input type ="checkbox" name ="hbs" v-model ="hbs" value ="玩" > 乐 <input type ="checkbox" name ="hbs" v-model ="hbs" value ="乐" > <br > 简介:<textarea v-model ="user.introduce" > </textarea > <br > 籍贯: <select v-model ="user.pro" > <option value ="1" > 黑</option > <option value ="2" > 吉</option > <option value ="3" > 辽</option > <option value ="4" > 京</option > <option value ="5" > 津</option > <option value ="6" > 冀</option > </select > <br > <button @click ="login()" > 登录</button > <button @click ="clearx()" > 重置</button > <hr > 显示爱好:{{ hbs }} <hr > 显示用户信息:{{ user }} </div > </template > <style scoped > </style >
数据监听器
计算属性允许我们声明性地计算衍生值。然而在有些情况下,我们需要在状态变化时执行一些“副作用”:例如更改 DOM,或是根据异步操作的结果去修改另一处的状态。我们可以使用 watch 函数 在每次响应式状态发生变化时触发回调函数:
watch主要用于以下场景:
当数据发生变化时需要执行相应的操作
监听数据变化,当满足一定条件时触发相应操作
在异步操作前或操作后需要执行相应的操作
监控响应式数据(watchEffect):
<script type ="module" setup > import { ref,reactive,watch, watchEffect} from 'vue' let firstname=ref ('' ) let lastname=reactive ({name :'' }) let fullname=ref ('' ) watchEffect (()=> { console .log (firstname.value ) console .log (lastname.name ) fullname.value =`${firstname.value} ${lastname.name} ` }) </script > <template > <div > 全名:{{fullname}} <br > 姓氏:<input type ="text" v-model ="firstname" > <br > 名字:<input type ="text" v-model ="lastname.name" > <br > </div > </template > <style scoped > </style >
Vue生命周期
每个 Vue 组件实例在创建时都需要经历一系列的初始化步骤,比如设置好数据侦听,编译模板,挂载实例到 DOM,以及在数据改变时更新 DOM。在此过程中,它也会运行被称为生命周期钩子的函数
,让开发者有机会在特定阶段运行自己的代码
常见钩子函数
onMounted() 注册一个回调函数,在组件挂载完成后执行。
onUpdated() 注册一个回调函数,在组件因为响应式状态变更而更新其 DOM 树之后调用。
onUnmounted() 注册一个回调函数,在组件实例被卸载之后调用。
onBeforeMount() 注册一个钩子,在组件被挂载之前被调用。
onBeforeUpdate() 注册一个钩子,在组件即将因为响应式状态变更而更新其 DOM 树之前调用。
onBeforeUnmount() 注册一个钩子,在组件实例被卸载之前调用。
Vue组件
组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对每个部分进行单独的思考。组件就是实现应用中局部功能代码和资源的集合!在实际应用中,组件常常被组织成层层嵌套的树状结构,这和我们嵌套 HTML 元素的方式类似,Vue 实现了自己的组件模型,使我们可以在每个组件内封装自定义内容与逻辑。
组件化:对js/css/html统一封装,这是VUE中的概念
模块化:对js的统一封装,这是ES6中的概念
组件化中,对js部分代码的处理使用ES6中的模块化
组件之间传递数据 父传子
Vue3 中父组件向子组件传值可以通过 props 进行,具体操作如下:
首先,在父组件中定义需要传递给子组件的值,接着,在父组件的模板中引入子组件,同时在引入子组件的标签中添加 props 属性并为其设置需要传递的值。
在 Vue3 中,父组件通过 props 传递给子组件的值是响应式的。也就是说,如果在父组件中的传递的值发生了改变,子组件中的值也会相应地更新。
<script setup > import Son from './components/Son.vue' import {ref,reactive,toRefs} from 'vue' let message = ref ('parent data!' ) let title = ref (42 ) function changeMessage ( ){ message.value = '修改数据!' title.value ++ } </script > <template > <div > <h2 > {{ message }}</h2 > <hr > <Son :message ="message" :title ="title" > </Son > <hr > <button @click ="changeMessage" > 点击更新</button > </div > </template > <style scoped > </style >
<script setup type ="module" > import {ref,isRef,defineProps} from 'vue' defineProps ({ message :String , title :Number }) </script > <template > <div > <div > {{ message }}</div > <div > {{ title }}</div > </div > </template > <style > </style >
子传父
<script setup > import Son from './components/Son.vue' import {ref} from 'vue' let pdata = ref ('' ) const padd = (data ) => { console .log ('2222' ); pdata.value =data; } const psub = (data ) => { console .log ('11111' ); pdata.value = data; } </script > <template > <div > <Son @add ="padd" @sub ="psub" > </Son > <hr > {{ pdata }} </div > </template >
<script setup > import {ref,defineEmits} from 'vue' let emites = defineEmits (['add' ,'sub' ]); let data = ref (1 ); function sendMsgToParent ( ){ emites ('add' ,'add data!' +data.value ) emites ('sub' ,'sub data!' +data.value ) data.value ++; } </script > <template > <div > <button @click ="sendMsgToParent" > 发送消息给父组件</button > </div > </template >
兄弟传参
Navigator.vue: 发送数据到App.vue
<script setup type ="module" > import {defineEmits} from 'vue' const emits = defineEmits (['sendMenu' ]); function send (data ){ emits ('sendMenu' ,data); } </script > <template > <div > <ul > <li @click ="send('学员管理')" > 学员管理</li > <li @click ="send('图书管理')" > 图书管理</li > <li @click ="send('请假管理')" > 请假管理</li > <li @click ="send('考试管理')" > 考试管理</li > <li @click ="send('讲师管理')" > 讲师管理</li > </ul > </div > </template >
App.vue: 发送数据到Content.vue
<script setup > import Header from './components/Header.vue' import Navigator from './components/Navigator.vue' import Content from './components/Content.vue' import {ref} from "vue" var navigator_menu = ref ('ceshi' ); const receiver = (data ) =>{ navigator_menu.value = data; } </script > <template > <div > <hr > {{ navigator_menu }} <hr > <Header class ="header" > </Header > <Navigator @sendMenu ="receiver" class ="navigator" > </Navigator > <Content class ="content" :message ="navigator_menu" > </Content > </div > </template >
<script setup type ="module" > defineProps ({ message :String }) </script > <template > <div > 展示的主要内容! <hr > {{ message }} </div > </template >
router
1 什么是路由?
定义:路由就是根据不同的 URL 地址展示不同的内容或页面。
通俗理解:路由就像是一个地图,我们要去不同的地方,需要通过不同的路线进行导航。
2 路由的作用
单页应用程序(SPA)中,路由可以实现不同视图之间的无刷新切换,提升用户体验;
路由还可以实现页面的认证和权限控制,保护用户的隐私和安全;
路由还可以利用浏览器的前进与后退,帮助用户更好地回到之前访问过的页面。
router重定向
重定向的作用:将一个路由重定向到另一个路由上
修改示例:访问/list和/showAll都定向到List.vue
router.js
import {createRouter,createWebHashHistory} from 'vue-router' 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;
<script setup > </script > <template > <div > <h1 > App页面</h1 > <hr /> <router-link to ="/" > home页</router-link > <br > <router-link to ="/list" > list页</router-link > <br > <router-link to ="/showAll" > showAll页</router-link > <br > <router-link to ="/add" > add页</router-link > <br > <router-link to ="/update" > update页</router-link > <br > <hr /> <hr > 默认展示位置:<router-view > </router-view > <hr > Home视图展示:<router-view name ="homeView" > </router-view > <hr > List视图展示:<router-view name ="listView" > </router-view > <hr > Add视图展示:<router-view name ="addView" > </router-view > <hr > Update视图展示:<router-view name ="updateView" > </router-view > </div > </template > <style scoped > </style >
编程式路由(useRouter)
普通路由
<router-link to="/list">list页</router-link>
这种路由,to中的内容目前是固定的,点击后只能切换/list对象组件(声明式路由)
编程式路由
通过useRouter,动态决定向那个组件切换的路由
在 Vue 3 和 Vue Router 4 中,可以使用 useRouter
来实现动态路由(编程式路由)
这里的 useRouter
方法返回的是一个 router 对象,你可以用它来做如导航到新页面、返回上一页面等操作
案例需求: 通过普通按钮配合事件绑定实现路由页面跳转,不直接使用router-link标签
<script setup type ="module" > import {useRouter} from 'vue-router' import {ref} from 'vue' let router = useRouter () let routePath =ref ('' ) let showList = ( )=>{ router.push ({path :'/list' }) } </script > <template > <div > <h1 > App页面</h1 > <hr /> <router-link to ="/" > home页</router-link > <br > <router-link to ="/list" > list页</router-link > <br > <router-link to ="/showAll" > showAll页</router-link > <br > <router-link to ="/add" > add页</router-link > <br > <router-link to ="/update" > update页</router-link > <br > <button @click ="showList()" > showList</button > <br > <hr /> <hr > 默认展示位置:<router-view > </router-view > <hr > Home视图展示:<router-view name ="homeView" > </router-view > <hr > List视图展示:<router-view name ="listView" > </router-view > <hr > Add视图展示:<router-view name ="addView" > </router-view > <hr > Update视图展示:<router-view name ="updateView" > </router-view > </div > </template > <style scoped > </style >
路由传参(useRoute)
路径参数
在路径中使用一个动态字段来实现,我们称之为 路径参数
例如:查看数据详情 /showDetail/1
,1
就是要查看详情的id,可以动态添值
键值对参数
案例需求:切换到ShowDetail.vue组件时,向该组件通过路由传递参数
<script setup type ="module" > import {useRouter} from 'vue-router' let router = useRouter () let showDetail = (id,language )=>{ router.push ({name :"showDetail" ,params :{id :id,language :language}}) } let showDetail2 = (id,language )=>{ router.push ({path :"/showDetail2" ,query :{id :id,language :language}}) } </script > <template > <div > <h1 > App页面</h1 > <hr /> <router-link to ="/showDetail/1/JAVA" > showDetail路径传参显示JAVA</router-link > <button @click ="showDetail(1,'JAVA')" > showDetail动态路由路径传参显示JAVA</button > <hr /> <router-link v-bind:to ="{path:'/showDetail2',query:{id:1,language:'Java'}}" > showDetail2键值对传参显示JAVA</router-link > <button @click ="showDetail2(1,'JAVA')" > showDetail2动态路由键值对传参显示JAVA</button > <hr > showDetail视图展示:<router-view name ="showDetailView" > </router-view > <hr > showDetail2视图展示:<router-view name ="showDetailView2" > </router-view > </div > </template > <style scoped > </style >
import {createRouter,createWebHashHistory} from 'vue-router' import ShowDetail from '../components/ShowDetail.vue' import ShowDetail2 from '../components/ShowDetail2.vue' const router = createRouter ({ history : createWebHashHistory (), routes :[ { path :'/showDetail/:id/:language' , name :'showDetail' , components :{ showDetailView :ShowDetail } }, { path :'/showDetail2' , components :{ showDetailView2 :ShowDetail2 } }, ] }) export default router;
ShowDetail.vue 通过useRoute获取路径参数
<script setup type ="module" > import {useRoute} from 'vue-router' import { onUpdated,ref } from 'vue' ; let route =useRoute () let languageId = ref (0 ) let languageName = ref ('' ) onUpdated (()=> { languageId.value =route.params .id languageName.value =route.params .language console .log (languageId.value ) console .log (languageName.value ) }) </script > <template > <div > <h1 > ShowDetail页面</h1 > <h3 > 编号{{route.params.id}}:{{route.params.language}}是世界上最好的语言</h3 > <h3 > 编号{{languageId}}:{{languageName}}是世界上最好的语言</h3 > </div > </template > <style scoped > </style >
ShowDetail2.vue通过useRoute获取键值对参数
<script setup type ="module" > import {useRoute} from 'vue-router' import { onUpdated,ref } from 'vue' ; let route =useRoute () let languageId = ref (0 ) let languageName = ref ('' ) onUpdated (()=> { console .log (route.query ) console .log (languageId.value ) console .log (languageName.value ) languageId.value =route.query .id languageName.value =route.query .language }) </script > <template > <div > <h1 > ShowDetail2页面</h1 > <h3 > 编号{{route.query.id}}:{{route.query.language}}是世界上最好的语言</h3 > <h3 > 编号{{languageId}}:{{languageName}}是世界上最好的语言</h3 > </div > </template > <style scoped > </style >
路由守卫
在 Vue3 中,路由守卫是用于在路由切换期间进行一些特定任务的回调函数。路由守卫可以用于许多任务,例如验证用户是否已登录、在路由切换前提供确认提示、请求数据等。Vue3 为路由守卫提供了全面的支持,并提供了以下几种类型的路由守卫:
全局前置守卫 :在路由切换前被调用,可以用于验证用户是否已登录、中断导航、请求数据等。
全局后置守卫 :在路由切换之后被调用,可以用于处理数据、操作 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} ` ); });
登录案例,登录以后才可以进入home,否则必须进入login
<script setup > import {ref} from 'vue' import {useRouter} from 'vue-router' let username =ref ('' ) let password =ref ('' ) let router = useRouter (); let login = ( ) =>{ console .log (username.value ,password.value ) if (username.value == 'root' & password.value == '123456' ){ router.push ({path :'/home' ,query :{'username' :username.value }}) localStorage .setItem ('username' ,username.value ) }else { alert ('登录失败,账号或者密码错误!' ); } } </script > <template > <div > 账号: <input type ="text" v-model ="username" placeholder ="请输入账号!" > <br > 密码: <input type ="password" v-model ="password" placeholder ="请输入密码!" > <br > <button @click ="login()" > 登录</button > </div > </template > <style scoped > </style >
<script setup > import {ref} from 'vue' import {useRoute,useRouter} from 'vue-router' let route =useRoute () let router = useRouter () let username =window .localStorage .getItem ('username' ); let logout = ( )=>{ window .localStorage .removeItem ('username' ) router.push ("/login" ) } </script > <template > <div > <h1 > Home页面</h1 > <h3 > 欢迎{{username}}登录</h3 > <button @click ="logout" > 退出登录</button > </div > </template > <style scoped > </style >
<script setup type ="module" > </script > <template > <div > <router-view > </router-view > </div > </template > <style scoped > </style >
import {createRouter,createWebHashHistory} from 'vue-router' 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 )=> { console .log (`从哪里来:${from .path} ,到哪里去:${to.path} ` ) if (to.path == '/login' ){ next () }else { 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;
数据交互axios Promise 普通函数和回调函数
普通函数: 正常调用的函数,一般函数执行完毕后才会继续执行下一行代码
<script > let fun1 = ( ) =>{ console .log ("fun1 invoked" ) } fun1 () console .log ("other code processon" ) </script >
回调函数: 一些特殊的函数,表示未来才会执行的一些功能,后续代码不会等待该函数执行完毕就开始执行了
<script > setTimeout (function ( ){ console .log ("setTimeout invoked" ) },2000 ) console .log ("other code processon" ) </script >
Promise简介
前端中的异步编程技术,类似Java中的多线程+线程结果回调
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6将其写进了语言标准,统一了用法,原生提供了Promise
对象。
所谓Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的API,各种异步操作都可以用同样的方法进行处理。
Promise
对象有以下两个特点:
Promise对象代表一个异步操作,有三种状态:Pending
(进行中)、Resolved
(已完成,又称 Fulfilled)和Rejected
(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise
这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从Pending
变为Resolved
和从Pending
变为Rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。
Promise基本用法
ES6规定,Promise对象是一个构造函数,用来生成Promise实例。
<script > let promise =new Promise (function (resolve,reject ){ console .log ("promise do some code ... ..." ) reject ("promise fail" ) }) console .log ('other code1111 invoked' ) promise.then ( function (value ){console .log (`promise中执行了resolve:${value} ` )}, function (error ){console .log (`promise中执行了reject:${error} ` )} ) console .log ('other code2222 invoked' ) </script >
catch()
Promise.prototype.catch
方法是.then(null, rejection)
的别名,用于指定发生错误时的回调函数,用法类似于try-catch
<script > let promise =new Promise (function (resolve,reject ){ console .log ("promise do some code ... ..." ) throw new Error ("error message" ) }) console .log ('other code1111 invoked' ) promise.then ( function (resolveValue ){console .log (`promise中执行了resolve:${resolveValue} ` )} ).catch ( function (error ){console .log (error)} ) console .log ('other code2222 invoked' ) </script >
async和await的使用
async和await是ES6中用于处理异步操作的新特性。通常,异步操作会涉及到Promise对象,而async/await则是在Promise基础上提供了更加直观和易于使用的语法。
async 用于标识函数的
async标识函数后,async函数的返回值会变成一个promise对象
如果函数内部返回的数据是一个非promise对象,async函数的结果会返回一个成功状态 promise对象
如果函数内部返回的是一个promise对象,则async函数返回的状态与结果由该对象决定
如果函数内部抛出的是一个异常,则async函数返回的是一个失败的promise对象
<script > async function fun1 ( ){ let promise = Promise .reject ("heihei" ) return promise } let promise =fun1 () promise.then ( function (value ){ console .log ("success:" +value) } ).catch ( function (value ){ console .log ("fail:" +value) } ) </script >
await
await右侧的表达式一般为一个promise对象,但是也可以是一个其他值
如果表达式是promise对象,await返回的是promise成功的值
await会等右边的promise对象执行结束,然后再获取结果,后续代码也会等待await的执行
如果表达式是其他值,则直接返回该值
await必须在async函数中,但是async函数中可以没有await
如果await右边的promise失败了,就会抛出异常,需要通过 try … catch捕获处理
<script > async function fun1 ( ){ return 10 } async function fun2 ( ){ try { let res = await fun1 () }catch (e){ console .log ("catch got:" +e) } console .log ("await got:" +res) } fun2 () </script >
Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中,算是VUE提供发送ajax请求的方式。 它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)。在服务端它使用原生 node.js http
模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。它有如下特性:
Axios入门
1 案例需求:请求后台获取随机网站数据
https://api.uomg.com/api/rand.qinghua?format=json
{ "code" : 1 , "content" : "xxxxxxxx" }
2 安装axios
3 设计页面(App.Vue)
<script setup type ="module" > import axios from 'axios' import { onMounted,reactive } from 'vue' ; let jsonData =reactive ({code :1 ,content :'xxxxxx' }) let getLoveMessage =( )=>{ axios ({ method :"post" , url :"https://api.uomg.com/api/rand.qinghua?format=json" , data :{ username :"123456" } }).then ( function (response ){ console .log (response) Object .assign (jsonData,response.data ) }).catch (function (error ){ console .log (error) }) } onMounted (()=> { getLoveMessage () }) </script > <template > <div > <h1 > {{jsonData.content}}</h1 > <button @click ="getLoveMessage" > 更新数据</button > </div > </template > <style scoped > </style >
异步响应的数据结构
响应的数据是经过包装返回的,一个请求的响应包含以下信息。
{ data: { } , status: 200 , statusText: 'OK', headers: { } , config: { } , request: { } }
then (function (response ) { console .log (response.data ); console .log (response.status ); console .log (response.statusText ); console .log (response.headers ); console .log (response.config ); });
4 通过async和await处理异步请求
<script setup type ="module" > import axios from 'axios' import { onMounted,reactive } from 'vue' ; let jsonData =reactive ({code :1 ,content :'xxxxxxxx' }) let getLoveWords = async ( )=>{ return await axios ({ method :"post" , url :"https://api.uomg.com/api/rand.qinghua?format=json" , data :{ username :"123456" } }) } let getLoveMessage =( )=>{ let {data} = await getLoveWords () Object .assign (message,data) } onMounted (()=> { getLoveMessage () }) </script > <template > <div > <h1 > {{jsonData.content}}</h1 > <button @click ="getLoveMessage" > 更新数据</button > </div > </template > <style scoped > </style >
axios在发送异步请求时的可选配置:
{ url: '/user', method: 'get', baseURL: 'https: 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 }
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参数
<script setup type ="module" > import axios from 'axios' import { onMounted,ref,reactive,toRaw } from 'vue' ; let jsonData =reactive ({code :1 ,content :'xxxxxxx' }) let getLoveWords = async ( )=>{ try { return await axios.get ( 'https://api.uomg.com/api/rand.qinghua' , { params :{ format :'json' , username :'zhangsan' , password :'123456' }, headers :{ 'Accept' : 'application/json, text/plain, text/html,*/*' } } ) }catch (e){ return await e } } let getLoveMessage =( )=>{ let {data} = await getLoveWords () Object .assign (message,data) } onMounted (()=> { getLoveMessage () }) </script >
测试post参数
<script setup type ="module" > import axios from 'axios' import { onMounted,ref,reactive,toRaw } from 'vue' ; let jsonData =reactive ({code :1 ,content :'xxxxxxxx' }) let getLoveWords = async ( )=>{ try { return await axios.post ( 'https://api.uomg.com/api/rand.qinghua' , { username :'zhangsan' , password :'123456' }, { params :{ format :'json' , }, headers :{ 'Accept' : 'application/json, text/plain, text/html,*/*' , 'X-Requested-With' : 'XMLHttpRequest' } } ) }catch (e){ return await e } } let getLoveMessage =( )=>{ let {data} = await getLoveWords () Object .assign (message,data) } onMounted (()=> { getLoveMessage () }) </script >
Axios 拦截器
如果想在axios发送请求之前,或者是数据响应回来在执行then方法之前做一些额外的工作,可以通过拦截器完成
axios.interceptors .request .use ( function (config ) { return config; }, function (error ) { return Promise .reject (error); } ); axios.interceptors .response .use ( function (response ) { return response; }, function (error ) { return Promise .reject (error); } );
import axios from 'axios' 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
<script setup type ="module" > import axios from './axios.js' import { onMounted,ref,reactive,toRaw } from 'vue' ; let jsonData =reactive ({code :1 ,content :'我努力不是为了你而是因为你' }) let getLoveWords = async ( )=>{ try { return await axios.post ( 'api/rand.qinghua' , { username :'zhangsan' , password :'123456' }, { params :{ format :'json' , } } ) }catch (e){ return await e } } let getLoveMessage =( )=>{ getLoveWords ().then ( (response ) => { console .log ("after getloveWords" ) console .log (response) Object .assign (jsonData,response.data ) } ) } onMounted (()=> { getLoveMessage () }) </script >
状态管理Pinia Pinia介绍
如何实现多个组件之间的数据传递?
方式1 组件传参
方式2 路由传参
方式3 通过pinia状态管理定义共享数据
当我们有多个组件共享一个共同的状态(数据源)
时,多个视图可能都依赖于同一份状态。来自不同视图的交互也可能需要更改同一份状态。虽然我们的手动状态管理解决方案(props,组件间通信,模块化)在简单的场景中已经足够了,但是在大规模的生产应用中还有很多其他事项需要考虑:
更强的团队协作约定
与 Vue DevTools 集成,包括时间轴、组件内部审查和时间旅行调试
模块热更新 (HMR)
服务端渲染支持
Pinia 就是一个实现了上述需求的状态管理库,由 Vue 核心团队维护,对 Vue 2 和 Vue 3 都可用。https://pinia.vuejs.org/zh/introduction.html
Pinia基本用法
安装pinia
定义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中
import { createApp } from 'vue' import App from './App.vue' import router from './routers/router.js' import { createPinia } from 'pinia' let pinia= createPinia ()let app =createApp (App )app.use (router) app.use (pinia) app.mount ('#app' )
Operate.vue 中操作Pinia数据
<script setup type ="module" > import { ref} from 'vue' ; import { definedPerson} from '../store/store' ; let person= definedPerson () let hobby = ref ('' ) </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 >
List.vue中展示Pinia数据
<script setup type ="module" > import { definedPerson} from '../store/store' ; let person= definedPerson () </script > <template > <div > <h1 > List页面,展示Pinia中的数据</h1 > 读取姓名:{{person.username}} <br > 读取年龄:{{person.age}} <br > 通过get年龄:{{person.getAge}} <br > 爱好数量:{{person.getHobbiesCount}} <br > 所有的爱好: <ul > <li v-for ='(hobby,index) in person.hobbies' :key ="index" v-text ="hobby" > </li > </ul > </div > </template > <style scoped > </style >
定义组件路由router.js
import {createRouter,createWebHashHistory} from 'vue-router' import List from '../components/List.vue' import Operate from '../components/Operate.vue' const router = createRouter ({ history : createWebHashHistory (), routes :[ { path :'/opearte' , component :Operate }, { path :'/list' , component :List }, ] }) export default router;
App.vue中通过路由切换组件
<script setup type ="module" > </script > <template > <div > <hr > <router-link to ="/opearte" > 显示操作页</router-link > <br > <router-link to ="/list" > 显示展示页</router-link > <br > <hr > <router-view > </router-view > </div > </template > <style scoped > </style >
Pinia其他细节
State在大多数情况下,都是store的核心。人们通常会先定义能代表他们 APP 的 state。在 Pinia 中,state 被定义为一个返回初始状态的函数。
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 } } } )
<script setup type ="module" > import { ref} from 'vue' ; import { definedPerson} from '../store/store' ; let person= definedPerson () let hobby = ref ('' ) let addHobby = ( )=> { console .log (hobby.value ) person.hobbies .push (hobby.value ) } 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) }) </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 > <input type ="text" @change ="addHobby" v-model ="hobby" > <br > <button @click ="person.doubleAge()" > 年龄加倍</button > <br > <button @click ="person.$reset()" > 恢复默认值</button > <br > <button @click ="person.$patch({username:'奥特曼',age:100,hobbies:['晒太阳','打怪兽']})" > 变身奥特曼</button > <br > person:{{person}} </div > </template > <style scoped > </style >
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 支持按需加载,且不依赖于任何第三方 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 浏览器。
Element-plus常用组件
Element-plus示例
1 准备vite项目
npm create vite // 注意选择 vue+TypeScript npm install npm install pinia npm install axios
2 安装element-plus
3 完整引入element-plus
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' )
<script setup > import { ref } from 'vue' const value = ref (true ) </script > <template > <div > <el-button > 按钮</el-button > <br > <el-switch v-model ="value" size ="large" active-text ="Open" inactive-text ="Close" /> <br /> <el-switch v-model ="value" active-text ="Open" inactive-text ="Close" /> <br /> <el-switch v-model ="value" size ="small" active-text ="Open" inactive-text ="Close" /> </div > </template > <style scoped > </style >