零零碎碎

小知识

放到store中的数据是一种内存缓存,刷新会消失,要一直存在要进行本地缓存
_是一种程序员之间默认的协定,属于实例自己的私有数据

前后端分离项目认识

  • 前后端分离:前后端的代码没有任何交集
  • 前后端分离,人也分离,有很多时间要和后端沟通,有小细节后端很可能没传过来,这时候就需要找后端
  • 先把结构搭出来,在和后端给的数据做对比,如果返回数据很乱,用一个映射的工具函数把很乱的数据处理成自己想要的格式,再把数据放进界面展示>
  • 在项目里配置某个版本的node,这个项目跑的是它,不被全局安装的影响

后端数据处理

后端数据不要直接拿过来展示,要有分层的思想,中间用一层做转换,这样哪怕后端数据变了也好处理,还能返回自己期望的格式的数据

后端返回的json格式数据,放在ts中转换网站json to ts

key值最好用常量定义,难免可能拼错

  • key值一般用常量,比如name,token,防止写错
    export const LOGIN_TOKEN = 'login/token'
  • 刷新页面重新动态加载路由这里,代码一点点写完了自己检查也没问题,但功能实现不了,拜托AI老师帮我检查代码,发现use,user拼写错误,说明用常量声明key真的很重要
  • 瞧瞧我这个截图,错拼的useInfo又错写成了userinfo,这种问题真的很难避免,以后一定要多多多定义常量,仔细再仔细
    拼写错误啊啊啊
  • style中子组件的根元素可以被父组件直接选中,不需要加deep;子组件背包包裹的其他父组件不能选中

.data和函数中命名建议:

接着上面刷新页面重新动态加载路由功能这里,我改着改着userMenus的数据又出问题了,对我来说最喜欢的解决办法是都在控制台答应出来然后一个个检查改,这里问AI反而没解决,我觉得是因为他不知道后端给我传的数据是什么样子的
而且我不该这样起名,仓库里有userMenu,函数中还用userMenu接受.data后的数据,后续代码多了发现自己会乱掉,直接改命名为userMenuResData

1
2
3
4
5
6
7
//检查数据
const roleId = userInfo.data.role.id
const userMenusResult = await getRoleMenus(roleId)
console.log('userMenusResult', userMenusResult)
const userMenus = userMenusResult.data
console.log('userMenus( userMenusResult.data)', userMenus)
console.log('userMenus.data( userMenusResult.data.data)', userMenus.data)

.data问题
成功解决(详细的注释在代码里score/login.ts):

1
2
3
4
5
6
7
8
const roleId = userInfo.data.role.id
const userMenusResult = await getRoleMenus(roleId)
const userMenusData = userMenusResult.data.data
console.log('userMenusData', userMenusData)//这个是期望保存的数据
this.userMenus = userMenusData

localCache.setCache('userInfo', this.userInfo)
localCache.setCache('userMenus', this.userMenus)

下图.data问题和刷新重新加载路由解决:
.data问题和刷新重新加载路由解决

浏览器本地缓存+封装缓存工具

放到store中的数据是一种内存缓存,刷新会消失,要一直存在要进行本地缓存
在浏览器保存tokenlocalStorage.setItem('token',this.token)
但是保存对象不可以localStorage.setItem('userInfo',{name:"nie"})//{Object,object}` 正确的要这样localStorage.setItem(‘userInfo’,JSON.stringify({name:”nie”})) 同理取出对象JSON.parse(localStorage.getItem(‘userInfo’))``
如果每次缓存对象都这样很麻烦,封装缓存工具: utils/cache.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//1. 定义 CacheType 枚举:区分两种缓存类型(local/session)
//2. 创建 NieCache 类:封装通用的缓存操作方法(set/get/delete/clear)
//3. 根据传入的 CacheType 决定使用 localStorage 还是 sessionStorage
//4. 提供 setCache / getCache / deleteCache / clearCache 方法
//5. 创建两个实例:localCache 和 sessionCache
//6. 导出这两个实例,供其他模块使用
enum CacheType {
Local,
Session
}

class NieCache {
storage: Storage

constructor(type: CacheType) {
this.storage = type === CacheType.Local ? localStorage : sessionStorage
}

setCache(key: string, value: any) {
if (value) {
this.storage.setItem(key, JSON.stringify(value))
}
}

getCache(key: string) {
const value = this.storage.getItem(key)
if (value) {
return JSON.parse(value)
}
}

removeCache(key: string) {
this.storage.removeItem(key)
}

clear() {
this.storage.clear()
}
}

const localCache = new NieCache(CacheType.Local)
const sessionCache = new NieCache(CacheType.Session)

export { localCache, sessionCache }

后续再缓存直接localCache.setCache('token',this.token)')
localCache.setCache('userInfo',{name:"nie"})

store为什么看不见呢(细节)

在开发main页面时,明明之前保存了但是开始看不到store,为什么呢
原因是刚开始开发这个页面,还没写ts,而pinia中有很多个store,在你没有使用的时候它不会出现,需要在mian页面使用(导入拿到)一下它,这样就能看到了

1
2
3
4
import useLoginStore from '@/store/login/login'
const loginStore = useLoginStore()
//获取菜单,同时使用loginStore了,现在页面的pinia里有菜单的数据
const userMenus = loginStore.userMenus

node卸载重装后hexo无法使用

node删除用nvm-desktop重装后,我以前项目里的node相关东西都不见了,博客hexo跑不起,需要项目里重新安装环境

在终端npm install -g hexo-cli正常情况下就可以了,但是由于我环境变量配错了,绕了个大圈,具体如下;

安装了hexo,hexo -v找不到怎么办呢
下面的这些已经解决了,根本原因是我的node缓存系统变量配置错了,改正之后就好了
在这里我要由衷地感谢通义,他根据终端显示告诉我原因是环境变量的配置问题,帮我少走弯路
hexo -v显示不认识,我问AI,再遇到类似问题可以用下面的思路

先检查npm把东西安装到了哪里npm list -g --depth=0
找到hexo.cmd文件所在文件夹,添加到系统变量里,如C:\Program Files\NVM Desktop\node_global\node_modules\hexo-cli\bin
还是不行,使用带扩展名的完整命令来运行 hexo.cmd -v
原因:但是 Windows 系统在解析 .cmd 文件时有一些限制,尤其是在某些 shell(比如 PowerShell)中,不会自动识别 .cmd 扩展名的命令,除非你在调用时加上 .cmd,或者将 .cmd 加入默认可执行扩展名列表中

element-ui使用有感

  • 学习阶段用组件库,应该先把属性都删掉,用哪个加哪个,加深理解
    一定要理清楚嵌套关系,每部分是什么

  • 不同的ui组件之间不要随意嵌套,要用插槽

  • 组件重写属性失败怎么办?
    使用element-UI时,如果组件的属性值不合心意,想在代码中覆盖,要在组件所在的类里面重写属性+ !import
    在父类中重写会被子类覆盖,就算加了!import也不生效

  • ui库里的小图标
    按需导入注册很麻烦,不如使用官方文档推荐的全局导入(小图标全局导入也不会占很多空间)

如在一个组件中使用icon小图标,通过插槽放进去

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<el-tab-pane>
账号密码
<template #label>
<span class="icon">
<div class="label">
<el-icon><Watermelon /></el-icon>
<span calss="text">账号登录</span>
</div>
</span>
</template>
</el-tab-pane>
</template>
  • v-model
    在绑定v-model时,多去控制台看看数据有没有绑定成功,如图
    登录组件

400(Bad Request)

这部分我卡了六个小时,最后发现,原因是coderwhy拼成了codewhy,可笑的是我在接口软件和终端调试请求时,用户名写的都是coderwhy,回到登录页面,每次一写的都是codewhy

记一下处理400(Bad Request)的思路,希望以后用不到

  1. 传的数据和后端要接收的不一样
    在发送post请求前添加日志,获取到的数据
    1
    2
    const requestBody = JSON.stringify(account)
    console.log('实际发送的请求体:', requestBody) // 添加日志
  2. 数据的格式不对
    明确指定JSON格式'Content-Type': 'application/json'
  3. 用接口软件,或者CMD给接口传递数据,看接口有没有问题
  4. 检测控制台,请求头
    (纪念一下)
    不要再拼写错了

typescript

在做vue3+ts项目时,编译器总是一不小心就爆红,都是一些细节的小问题,但目前对ts应用不是很熟悉,还是有些头疼

  1. 方法形参爆红“参数隐式具有any类型”
    比如在给store定义actions时,写了一个方法,注意参数
    1
    2
    3
    4
    5
    acitons:{
    vhangeCounterAction(newCounter){
    his.counter=newCounter
    }
    }
    此时(newCounter)爆红,提示“参数隐式具有any类型”
    解决:
    (1)应该这样做:(newCounter:any)
    (2)可以修改配置,在tsconfig.json中,添加"noImplicitAny": false,默认是true,可以改为false,这样,在定义方法时,就不需要显示的声明参数类型了(不推荐)

起名规范

起名经常纠结,把常用命名记在这里,需要的时候来看

规范

注意:避免的常见错误,使用复数形式,和过于简写的名称

组件命名应该使用连字符,全小写,比如base-table,而组件文件名可能使用PascalCase

变量命名
API响应数据,如userList,表单数据userForm。
布尔值变量用has、is开头,比如isLoading

方法命名动词开头,比如handleSubmit,fetchUserList

ts类型或接口命名使用PascalCase+语义化名词,比如UserItem,TableColumn

Pinia Store的命名使用use+名词+Store,比如useUserStore

路由

1
2
3
4
5
{
name: 'userDetail',
path: '/user-detail/:id',
component: () => import('@/views/UserDetail.vue')
}

常用词

panel控制板 acount账户
isRemPwd是否保存密码

例子

  1. 组件文件
    1
    2
    3
    4
    5
    6
     # 主登录表单组件
    AuthLoginForm.vue # PascalCase风格
    auth-login-form.vue # kebab-case风格
    # 子组件
    AuthPasswordInput.vue # 密码输入专用组件
    AuthCaptcha.vue # 验证码组件
  2. 数据相关
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 登录表单数据
    interface LoginFormData {
    username: string
    password: string
    rememberMe: boolean
    captcha: string
    }

    // 响应数据
    interface LoginResponse {
    token: string
    expiresIn: number
    userInfo: UserProfile
    }
  3. 变量命名
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 表单数据
    const loginForm = reactive<LoginFormData>({
    username: '',
    password: '',
    rememberMe: false,
    captcha: ''
    })

    // 状态管理
    const isLoading = ref(false) // 加载状态
    const isCaptchaRequired = ref(false) // 是否需要验证码
    const loginErrorMsg = ref('') // 错误提示
  4. 方法/函数
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 提交处理
    const handleLoginSubmit = async () => {
    try {
    isLoading.value = true
    await submitLogin(loginForm)
    } catch (error) {
    handleLoginError(error)
    } finally {
    isLoading.value = false
    }
    }

    // 验证码
    const refreshCaptcha = () => { /*...*/ }
  5. 密码相关
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 密码强度提示
    const passwordStrength = computed(() => {
    return calculateStrength(loginForm.password)
    })

    // 显示/隐藏密码
    const isPasswordVisible = ref(false)
    const togglePasswordVisibility = () => {
    isPasswordVisible.value = !isPasswordVisible.value
    }
  6. 注册流程
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 注册表单
    interface RegisterFormData extends LoginFormData {
    email: string
    confirmPassword: string
    }

    // 注册API
    const submitRegistration = async (form: RegisterFormData) => {
    // ...
    }

    // 注册协议勾选
    const isAgreementAccepted = ref(false)
    //是否记住密码(是否保持)
    const isKeepPwd = ref(false)
    const isRemPws = ref(false)
  7. 验证规则
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 集中式验证规则
    const validationRules = {
    username: [
    { required: true, message: '请输入用户名', trigger: 'blur' },
    { min: 4, max: 16, message: '长度在4到16个字符', trigger: 'blur' }
    ],
    password: [
    { validator: checkPasswordComplexity, trigger: 'blur' }
    ]
    }

    // 自定义验证函数
    const checkPasswordComplexity = (rule: any, value: string, callback: any) => {
    if (!/(?=.*[A-Z])/.test(value)) {
    return callback(new Error('必须包含大写字母'))
    }
    // ...
    }
  8. 错误处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 错误码映射
    const LOGIN_ERROR_CODES = {
    1001: '用户名不存在',
    1002: '密码错误',
    1003: '验证码已过期'
    }

    // 统一错误处理器
    const handleLoginError = (error: ApiError) => {
    loginErrorMsg.value = LOGIN_ERROR_CODES[error.code] || '未知错误'
    }
  9. CSS类名规范
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // BEM风格示例
    .auth-page {
    &__header {
    // 页面头部样式
    }

    &__form-container {
    // 表单容器
    }

    &__submit-btn {
    // 提交按钮
    &--loading {
    // 加载状态样式
    }
    }
    }