Vuex
Vue
官方推荐的全局状态管理器
在脚手架项目的实现过程中,避免不了需要将一些数据存储在全局,方便在任何组件中直接获取并使用的需求。这种需求就非常契合vuex
的功能,vuex
主要用于将一些数据实现全局存储,需要的时候直接访问vuex
即可获取这些数据。
官方文档:https://vuex.vuejs.org/zh/guide/
。
Vuex
和核心js
文件: src/store/index.js
,提供了vuex
内置的5
个对象:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// 此处用于存储全局共享的状态数据
state: {
user: null, // user用于存储当前登录用户
},
// getters用于提供一些方法,方便的获取state中数据的临时计算结果
// 类似vue中的计算属性:computed
getters: {
},
// mutations用于提供一些方法,修改state中的状态数据
mutations: {
},
actions: {
},
modules: {
}
})
state
: 此处用于存储全局共享的状态数据,可以直接将数据定义在此处,全局可用:
state: {
user: {name: 'zs'}
}
在vue
组件中使用:通过this.$store
访问vuex
的仓库:
let name = this.$store.state.user.name
<div>{{$store.state.user.name}}</div>
mapState
辅助函数
<div>{{user.nickname}}</div>
<div>{{token}}</div>
import {mapState} from 'vuex'
export default {
computed:{
...mapState(['user','token'])
}
}
mutations
: 用于提供一些方法,修改state
中的状态数据:
mutations:{
// 这个方法完全被vuex所管理,由vuex来调用,参数由vuex来传递
// vuex将会传过来两个参数:
// state: 当前vuex仓库的state对象
// newName: 形参,由用户指定,vuex将会把用户的参数也带过来赋值给newName
updateName(state, newUser){
state.user = newUser
}
}
mutations
中的方法定义完成后,用户可以通过以下代码来调用这个方法,从而修改state
中的数据:
// commit方法用于通知vuex:“vuex,请你执行updateName方法,顺便给你传个参数”
// this.$store.commit('mutations方法名称', 自定义参数信息)
this.$store.commit('updateName', {id:1, user:'温柔的亮', email:'xxx'})
mapMutations
辅助函数
import {mapMutations} from 'vuex'
export default {
methods: {
...mapMutations(['updateUserInfo', 'saveToken'])
}
mounted(){
this.updateUserInfo(user) 代替
this.$store.commit('updateUserInfo', user)
}
}
actions
: actions
: 用于提供一些方法,异步做了一些耗时任务后,修改state
中的状态数据 , (不能直接修改state,而是需要调用mutations中的方法,来修改state)。
actions: {
// 这个方法完全被vuex所管理,由vuex来调用,参数由vuex来传递
// vuex将会传过来两个参数:
// store: 当前vuex仓库Store对象
// payload: 形参,由用户指定,vuex将会把用户的参数也带过来赋值给payload
login(store, payload){
// 执行异步任务,发送请求,得到结果后
// 将异步任务的结果交给mutations来进行后续处理
// store.commit('updateUserInfo', 用户对象)
}
},
如何调用actions
的方法呢?
// this.$store.dispatch('actions中的方法名称', 用户自定义的参数)
this.$store.dispatch('login', {username:'zs', password:'123456'})
mapActions
辅助函数
import {mapActions} from 'vuex'
export default {
methods: {
...mapActions(['login'])
login(user){
this.$store.dispatch('login', user)
}
}
mounted(){
this.login(user) 代替
this.$store.dispatch('login', user)
}
}
只有登录后,才可以看到除登录之外的页面。
实现思路:在项目的VueRouter
对象中添加前置守卫,前置守卫将会在跳转路由页面之前先执行,我们就可以在此处判断,用户访问当前地址时,vuex
里是否有当前用户,如果有可以通过,如果没有,直接拦截后,强制跳转到登陆页面。
只有登录后,才可以发送登录请求之外的http
的请求
如果想要实现该功能,则需要服务端进行请求验证,验证当前请求中是否包含登录用户的身份(服务端需要完成用户身份的鉴权),如果该用户拥有操作数据的权限,则执行相应业务;如果该用户没有处理该请求的权限,则直接打回去,返回一个错误消息:您没有操作该模块的权限。
基于Token
机制重构所有的请求
- 当发送登录请求,得到响应后,不仅可以获取到当前登录用户,还可以获取到一个服务端返回回来的
Token
加密字符串。这个字符串客户端看不懂,由服务端生成,在该Token
字符串中,包含了很多与用户相关的信息:userId
nickname
email
phone
过期时间
等信息。 - 客户端获取该
token
字符串后,需要将token
存起来,当做是当前用户的身份标识符,以后发送任何的请求时,都要携带该token
字符串一起发送请求。 - 这样服务端就可以接收该请求,解析出
token
后进行解密,解密出用户信息,从而了解该请求是哪一个用户发出的,完成用户鉴权后实现后续业务。
TypeScript
Typescript
是Javascript
的一个超集。 Typescript
在原有js
的基础之上又添加了编译期的类型检查的功能,意味着在TS
的环境下进行开发时,会对数据类型进行较为严格的验证,防止程序员写出可能出错的代码。规范编程习惯,适合大型项目开发使用。
Vue3.x
TypeScript
代码的编写及运行方式
typescript
代码写在后缀名为.ts
的文件中,这种文件可以被typescript
编译器编译解析,最终生成一套功能相同的js
代码,输出到一个.js
文件中。typescript
语言的类型语法的验证都是在编译期间来处理的。生成出来的js
与普通的js
并无差别。
如果在编译的过程中,ts
语法有类型验证错误,则会中断编译,报错。
全局安装Typescript
编译器
npm install -g typescript # 安装ts编译器
npm install -g ts-node # 安装ts-node后,可以使用code runner执行ts文件
npm install @types/node
安装成功之后,就可以使用tsc
命令,对ts
文件进行编译:
tsc helloworld.ts
编译过程做类型检查,通过后,将生成helloworld.js
文件,项目中使用的依然是js
文件。
vscode
中有个插件可以使用:
code Runner
Typescript
的数据类型
https://www.tslang.cn/docs/handbook/basic-types.html
// 01_basictype.ts 基本数据类型
// 描述一个人
let lName:string = '亮亮';
let lAge:number = 28;
let lMarried:boolean = true;
// let lHobby: string[] = ['摊煎饼', '玩单杠', '擦玻璃']
let lHobby: Array<string> = ['摊煎饼', '玩单杠', '擦玻璃']
// 亮亮的玩具 Tuple类型
// 描述一个3阶 边长10公分 的 塑料魔方
let mofang:[number,number,string] = [3, 10, '塑料']
// 枚举类型
// 枚举类型的作用就是用一个友好的名称来引用一些变量值
let c1 = '#ff0000' // 红色
let c2 = '#00ff00' // 绿色
let c3 = '#0000ff' // 蓝色
let css = `background: ${c1}, color: ${c2}`
// 使用枚举类型,定义这些颜色值,每一个颜色指定一个友好的名称
enum Color {Red='#f00', Green='#0f0', Blue='#00f'}
// 上述枚举类型Color定义完毕后,意味着在Color中有3个可以选择
// 的枚举值,使用时,通过Color.的方式直接引用,有提示
css = `background:${Color.Red}; color:${Color.Blue}`
console.log(css)
enum BMDURL { //通过枚举 给不好记的地址,起一个好认的名字
ADD_ACTOR = '/actor/add',
UPDATE_ACTOR = '/actor/update',
ADD_MOVIE = '/movie/add',
ADD_CINEMA = '/cinema/add'
}
//经常会使用枚举类型,定义大量的常量值,通过友好的名称方便访问
let url1 = 'http://localhost:8080' + BMDURL.ADD_ACTOR
let url2 = 'http://localhost:8080' + BMDURL.UPDATE_ACTOR
let url3 = 'http://localhost:8080' + BMDURL.ADD_MOVIE
let url4 = 'http://localhost:8080' + BMDURL.ADD_CINEMA
// .....
enum Sex {FAMALE, MALE} // 女性=0 男性=1
enum Category {Hot, Wait, Classic} // 热映0 待映1 经典2
console.log(Category.Hot)
console.log(Category.Wait)
console.log(Category.Classic)
// Any类型
//有时,会想要为那些在编程阶段还不清楚类型的变量指定一个类型。
// 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。
// 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 any类型来标记这些变量:
//假设res就是axios返回的结果
let res:any = {code:200, msg:'ok', data:[]}
console.log(res)
res = {code:401, msg:'token已经失效'}
console.log(res)
// 类型断言
// 定义一个变量v1,但是不太清楚具体存储的数据类型,所以:any
let v1:any = '/home/user/login'
// 调用v1.split()方法时,不会有提示,因为vscode不知道v1是什么
let r1 = v1.split('/')
// 在此处,如果认为确定v1就是字符串类型,那么可以做:类型断言
// <string>: 类型断言
// 告诉ts编译器:放心,我断定v1就是字符串,当字符串用即可
let v2:string = <string>v1
console.log(v2.split('/')) // split方法就有提示了
Typescript
中的函数
和JavaScript一样,TypeScript
函数可以创建有名字的函数和匿名函数。 你可以随意选择适合应用程序的方式,不论是定义一系列API
函数还是只使用一次的函数。
函数的类型
在Typescript
中函数的声明语法中可以指定函数返回值的类型:
function add(x: number, y: number): number {
return x + y;
}
let myAdd = function(x: number, y: number): number { return x + y; };
如上述指定函数参数的类型与返回值的类型后,调用函数时就必须遵守函数的参数的类型定义,必须传递两个number
,得到一个number
。
// 02_func.ts
// 声明一个函数,得到一个n以内的小数(随机数)
function getNum(n :number) :number{
return Math.random() * n
}
let r = getNum(10)
console.log(r.toFixed(2))
// 为变量指定类型:完整函数类型的定义
// myadd是一个函数,该函数必须接受两个number参数,返回number
let myadd: (x:number, y:number)=>number =
function(x:number, y:number):number{ return x+y }
console.log(myadd(10, 10))
// console.log(myadd(10, '1')) // 错误参数对不上
// 调用ts的函数时,参数列表必须与ts函数的参数列表的定义一一对应
function buildName(fname:string, lname?:string):string{
return `${fname} - ${lname}`
}
console.log(buildName('成','小亮'))
console.log(buildName('纪','小新'))
// 如果有需求,可能传一个,也可能传两个参 【?: 可选参数】
console.log(buildName('纪')) //
// 定义一个函数,传递pagesize与page变量,返回分页查询结果
function queryList(pagesize:number = 20, page:number = 1){
return `返回第${page}页的数据。(每页${pagesize}条)`
}
console.log(queryList(10))
console.log(queryList(10, 3))
// 什么都不传,应该返回默认page=1 pagesize=20
console.log(queryList())
Typescript
中的自定义类型
// Person.ts
// 声明一个自定义类型,用于描述一个人
// 自定义类型中需要定义一些属性字段用于更精确的描述一个人
class Person {
name: string; // 属性 【类的成员】
age?: number; // 属性
married?: boolean; // 属性
// 构造器【类的成员】new时执行 用于构造一个具体的对象
constructor(name:string,age?:number,married?:boolean){
this.name = name
this.age = age
this.married = married
}
// 【类的成员】 方法 向某人打招呼
greet(other?: Person){
if(other){
console.log(
`${this.name}正在向${other.name}打招呼`)
}else{
console.log(`${this.name}正在打招呼`)
}
}
}
//new通过自定义类型中声明的信息,创造一个人
let p1:Person = new Person('亮亮', 29, true);
//new通过自定义类型中声明的信息,创造一个人
let p2:Person = new Person('泡泡');
//new通过自定义类型中声明的信息,创造一个人
let p3:Person = new Person('小新', undefined, false);
console.log(p1.name)
console.log(p2.name)
console.log(p3.name)
// 调用对象的方法
p2.greet(p1)
p2.greet(p3)
p2.greet() // 没有传递参数
enum Color {BLACK, BLUE}
enum Rom {G8, G16}
enum Store {G128, G256}
// Phone.ts 用于描述一台手机
class Phone {
brand: string; // 品牌
name: string; // 名称
price: number; // 价格
color: Color; // 配色
rom: Rom; // 内存
storage: Store; // 存储
constructor(brand:string, name:string, price:number,
color: Color, rom:Rom, storage:Store){
this.brand = brand;
this.name = name;
this.price = price;
this.color = color;
this.rom = rom;
this.storage = storage;
}
}
// 造一部手机
let p1:Phone = new Phone(
'Oppo', 'A36', 899.0, Color.BLACK, Rom.G16, Store.G128)
console.log(p1)
// 再造一部手机: Apple iPhone14pro Max 黑色 16G 256G 9.9元
let p2:Phone = new Phone(
'Apple', 'iPhone14pro Max', 9.9, Color.BLACK, Rom.G16, Store.G256)
console.log(p2)
enum Color {BLACK, BLUE, RICE, WHITE, GRAY}
// Shoes.ts 实体类的定义
class Shoes {
title: string;
price: number;
brand: string;
color: Color;
size: number;
constructor(title:string, price:number, brand:string,
color:Color, size:number){
this.title = title;
this.price = price;
this.brand = brand;
this.color = color;
this.size = size;
}
}
作业:做一个扑克牌比大小的小游戏。
提示:
-
需要设计一些实体类:
class Card { suit花色 黑红梅方 rank点数 3,4,5,6,7,8,9,10,J,Q,K,A,2,小,大 compare(other:Card):number{ 返回-1 比other小 返回1 比other大 } } class Player { name名字 cards: Card[] mopai(card){} }