【前端Vue】社交信息头条项目完整笔记第2篇:二、登录注册,准备【附代码文档】

社交媒体-信息头条项目完整开发笔记完整教程(附代码资料)主要内容讲述:一、项目初始化使用 Vue CLI 创建项目,加入 Git 版本管理,调整初始目录结构,导入图标素材,引入 Vant 组件库,移动端 REM 适配,关于 , 配置文件,封装请求模块。十、用户关注 & 粉丝。十一、我的收藏/历史。十二、编辑用户资料创建组件并配置路由,页面布局,展示用户信息,修改昵称,修改性别,修改生日,修改头像。二、登录注册准备,实现基本登录功能,登录状态提示,表单验证,验证码处理,处理用户 Token,优化封装本地存储操作模块,关于 Token 过期问题。三、个人中心TabBar 处理,页面布局,处理已登录和未登录的页面展示,用户退出,展示登录用户信息,优化设置 Token。四、首页—文章列表页面布局,频道列表,文章列表,文章列表项。五、首页—频道编辑处理页面弹出层,创建频道编辑组件,页面布局,展示我的频道,展示推荐频道列表,添加频道,编辑频道,频道数据持久化,正确的获取首页频道列表数据。六、文章搜索创建组件并配置路由,页面布局,处理页面显示状态,搜索联想建议,搜索结果,搜索历史记录。七、文章详情创建组件并配置路由,页面布局,关于后端返回数据中的大数字问题,展示文章详情,处理内容加载状态,关于文章正文的样式,图片点击预览,关注用户,文章收藏,文章点赞。八、文章评论展示文章评论列表,评论点赞,发布文章评论,评论回复。九、用户页面创建组件并配置路由,页面布局,展示用户信息,用户关注,展示用户文章列表。

全套笔记资料代码移步: 前往gitee仓库查看

感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~


全套教程部分目录:


部分文件图片:

二、登录注册

目标

  • 能实现登录页面的布局
  • 能实现基本登录功能
  • 能掌握 Vant 中 Toast 提示组件的使用
  • 能理解 API 请求模块的封装
  • 能理解发送验证码的实现思路
  • 能理解 Vant Form 实现表单验证的使用
image-20200228161041266

准备

创建组件并配置路由

1、创建 src/views/login/index.vue 并写入以下内容

<template><div class="login-container">登录页面</div>
</template><script>
export default {name: 'LoginPage',components: {},props: {},data () {return {}},computed: {},watch: {},created () {},mounted () {},methods: {}
}
</script><style scoped lang="less"></style>

2、然后在 src/router/index.js 中配置登录页的路由表

{path: '/login',name: 'login',component: () => import('@/views/login')
}

最后,访问 /login 查看是否能访问到登录页面。

image-20200229115109026

布局结构

这里主要使用到三个 Vant 组件:

  • [NavBar 导航栏](
  • [Form 表单](
    • [Field 输入框](
    • [Button 按钮](

一个经验:使用组件库中的现有组件快速布局,再慢慢调整细节,效率更高(刚开始可能会感觉有点麻烦,越用越熟,慢慢的就有了自己的思想)。

布局样式

写样式的原则:将公共样式写到全局(src/styles/index.less),将局部样式写到组件内部。

1、src/styles/index.less

body {background-color: #f5f7f9;
}.page-nav-bar {background-color: #3296fa;.van-nav-bar__title {color: #fff;}
}

2、src/views/login/index.vue

<template><div class="login-container"><!-- 导航栏 --><van-nav-bar class="page-nav-bar" title="登录" /><!-- /导航栏 --><!-- 登录表单 --><van-form @submit="onSubmit"><van-fieldname="用户名"placeholder="请输入手机号"><i slot="left-icon" class="toutiao toutiao-shouji"></i></van-field><van-fieldtype="password"name="验证码"placeholder="请输入验证码"><i slot="left-icon" class="toutiao toutiao-yanzhengma"></i><template #button><van-button class="send-sms-btn" round size="small" type="default">发送验证码</van-button></template></van-field><div class="login-btn-wrap"><van-button class="login-btn" block type="info" native-type="submit">登录</van-button></div></van-form><!-- /登录表单 --></div>
</template><script>
export default {name: 'LoginIndex',components: {},props: {},data () {return {}},computed: {},watch: {},created () {},mounted () {},methods: {onSubmit (values) {console.log('submit', values)}}
}
</script><style scoped lang="less">
.login-container {.toutiao {font-size: 37px;}.send-sms-btn {width: 152px;height: 46px;line-height: 46px;background-color: #ededed;font-size: 22px;color: #666;}.login-btn-wrap {padding: 53px 33px;.login-btn {background-color: #6db4fb;border: none;}}
}
</style>

实现基本登录功能

思路:

  • 注册点击登录的事件
  • 获取表单数据(根据接口要求使用 v-model 绑定)
  • 表单验证
  • 发请求提交
  • 根据请求结果做下一步处理

一、根据接口要求绑定获取表单数据

1、在登录页面组件的实例选项 data 中添加 user 数据字段

...
data () {return {user: {mobile: '',code: ''}}
}

2、在表单中使用 v-model 绑定对应数据

<!-- van-cell-group 仅仅是提供了一个上下外边框,能看到包裹的区域 -->
<van-cell-group><van-fieldv-model="user.mobile"requiredclearablelabel="手机号"placeholder="请输入手机号"/><van-fieldv-model="user.code"type="number"label="验证码"placeholder="请输入验证码"required/>
</van-cell-group>

最后测试。

一个小技巧:使用 VueDevtools 调试工具查看是否绑定成功。

二、请求登录

1、创建 src/api/user.js 封装请求方法

/*** 用户相关的请求模块*/
import request from "@/utils/request"/*** 用户登录*/
export const login = data => {return request({method: 'POST',url: '/app/v1_0/authorizations',data})
}

2、给登录按钮注册点击事件

async onLogin () {try {const res = await login(this.user)console.log('登录成功', res)} catch (err) {if (err.response.status === 400) {console.log('登录失败', err)}}
}

最后测试。

登录状态提示

Vant 中内置了[Toast 轻提示](

// 简单文字提示
Toast("提示内容");// loading 转圈圈提示
Toast.loading({duration: 0, // 持续展示 toastmessage: "加载中...",forbidClick: true // 是否禁止背景点击
});// 成功提示
Ttoast.success("成功文案");// 失败提示
Toast.fail("失败文案");

提示:在组件中可以直接通过 this.$toast 调用。

另外需要注意的是:Toast 默认采用单例模式,即同一时间只会存在一个 Toast,如果需要在同一时间弹出多个 Toast,可以参考下面的示例

Toast.allowMultiple();const toast1 = Toast('第一个 Toast');
const toast2 = Toast.success('第二个 Toast');toast1.clear();
toast2.clear();

下面是为我们的登录功能增加 toast 交互提示。

async onLogin () {// 开始转圈圈this.$toast.loading({duration: 0, // 持续时间,0表示持续展示不停止forbidClick: true, // 是否禁止背景点击message: '登录中...' // 提示消息})try {const res = await request({method: 'POST',url: '/app/v1_0/authorizations',data: this.user})console.log('登录成功', res)// 提示 success 或者 fail 的时候,会先把其它的 toast 先清除this.$toast.success('登录成功')} catch (err) {console.log('登录失败', err)this.$toast.fail('登录失败,手机号或验证码错误')}
}

假如请求非常快的话就看不到 loading 效果了,这里可以手动将调试工具中的网络设置为慢速网络。

测试结束,再把网络设置恢复为 Online 正常网络。

表单验证

参考文档:[Form 表单验证](

<template><div class="login-container"><!-- 导航栏 --><van-nav-bar class="page-nav-bar" title="登录" /><!-- /导航栏 --><!-- 登录表单 --><!--表单验证:1、给 van-field 组件配置 rules 验证规则参考文档:2、当表单提交的时候会自动触发表单验证如果验证通过,会触发 submit 事件如果验证失败,不会触发 submit--><van-form @submit="onSubmit"><van-fieldv-model="user.mobile"name="手机号"placeholder="请输入手机号":rules="userFormRules.mobile"type="number"maxlength="11"><i slot="left-icon" class="toutiao toutiao-shouji"></i></van-field><van-fieldv-model="user.code"name="验证码"placeholder="请输入验证码":rules="userFormRules.code"type="number"maxlength="6"><i slot="left-icon" class="toutiao toutiao-yanzhengma"></i><template #button><van-button class="send-sms-btn" round size="small" type="default">发送验证码</van-button></template></van-field><div class="login-btn-wrap"><van-button class="login-btn" block type="info" native-type="submit">登录</van-button></div></van-form><!-- /登录表单 --></div>
</template><script>
import { login } from '@/api/user'export default {name: 'LoginIndex',components: {},props: {},data () {return {user: {mobile: '', // 手机号code: '' // 验证码},userFormRules: {mobile: [{required: true,message: '手机号不能为空'}, {pattern: /^1[3|5|7|8]\d{9}$/,message: '手机号格式错误'}],code: [{required: true,message: '验证码不能为空'}, {pattern: /^\d{6}$/,message: '验证码格式错误'}]}}},computed: {},watch: {},created () {},mounted () {},methods: {async onSubmit () {// 1. 获取表单数据const user = this.user// TODO: 2. 表单验证// 3. 提交表单请求登录this.$toast.loading({message: '登录中...',forbidClick: true, // 禁用背景点击duration: 0 // 持续时间,默认 2000,0 表示持续展示不关闭})try {const res = await login(user)console.log('登录成功', res)this.$toast.success('登录成功')} catch (err) {if (err.response.status === 400) {this.$toast.fail('手机号或验证码错误')} else {this.$toast.fail('登录失败,请稍后重试')}}// 4. 根据请求响应结果处理后续操作}}
}
</script><style scoped lang="less">
.login-container {.toutiao {font-size: 37px;}.send-sms-btn {width: 152px;height: 46px;line-height: 46px;background-color: #ededed;font-size: 22px;color: #666;}.login-btn-wrap {padding: 53px 33px;.login-btn {background-color: #6db4fb;border: none;}}
}
</style>

验证码处理

验证手机号

async onSendSms () {console.log('onSendSms')// 1. 校验手机号try {await this.$refs.loginForm.validate('mobile')} catch (err) {return console.log('验证失败', err)}// 2. 验证通过,显示倒计时// 3. 请求发送验证码
}

使用倒计时组件

1、在 data 中添加数据用来控制倒计时的显示和隐藏

data () {return {...isCountDownShow: false}
}

2、使用倒计时组件

<van-fieldv-model="user.code"placeholder="请输入验证码"
><i class="icon icon-mima" slot="left-icon"></i><van-count-downv-if="isCountDownShow"slot="button":time="1000 * 5"format="ss s"@finish="isCountDownShow = false"/><van-buttonv-elseslot="button"size="small"type="primary"round@click="onSendSmsCode">发送验证码</van-button>
</van-field>

发送验证码

1、在 api/user.js 中添加封装数据接口

export const getSmsCode = mobile => {return request({method: 'GET',url: `/app/v1_0/sms/codes/${mobile}`})
}

2、给发送验证码按钮注册点击事件

3、发送处理

async onSendSms () {// 1. 校验手机号try {await this.$refs.loginForm.validate('mobile')} catch (err) {return console.log('验证失败', err)}// 2. 验证通过,显示倒计时this.isCountDownShow = true// 3. 请求发送验证码try {await sendSms(this.user.mobile)this.$toast('发送成功')} catch (err) {// 发送失败,关闭倒计时this.isCountDownShow = falseif (err.response.status === 429) {this.$toast('发送太频繁了,请稍后重试')} else {this.$toast('发送失败,请稍后重试')}}
}

处理用户 Token

image-20200329121650635

Token 是用户登录成功之后服务端返回的一个身份令牌,在项目中的多个业务中需要使用到:

  • 访问需要授权的 API 接口
  • 校验页面的访问权限

但是我们只有在第一次用户登录成功之后才能拿到 Token。

所以为了能在其它模块中获取到 Token 数据,我们需要把它存储到一个公共的位置,方便随时取用。

往哪儿存?

  • 本地存储
    • 获取麻烦
    • 数据不是响应式
  • Vuex 容器(推荐)
    • 获取方便
    • 响应式的

使用容器存储 Token 的思路:

image-20200109192157006

  • 登录成功,将 Token 存储到 Vuex 容器中
    • 获取方便
    • 响应式
  • 为了持久化,还需要把 Token 放到本地存储
    • 持久化

下面是具体实现。

1、在 src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'Vue.use(Vuex)export default new Vuex.Store({state: {// 用户的登录状态信息user: JSON.parse(window.localStorage.getItem('TOUTIAO_USER'))// user: null},mutations: {setUser (state, user) {state.user = userwindow.localStorage.setItem('TOUTIAO_USER', JSON.stringify(user))}},actions: {},modules: {}
})

2、登录成功以后将后端返回的 token 相关数据存储到容器中

async onLogin () {// const loginToast = this.$toast.loading({this.$toast.loading({duration: 0, // 持续时间,0表示持续展示不停止forbidClick: true, // 是否禁止背景点击message: '登录中...' // 提示消息})try {const res = await login(this.user)// res.data.data => { token: 'xxx', refresh_token: 'xxx' }
+    this.$store.commit('setUser', res.data.data)// 提示 success 或者 fail 的时候,会先把其它的 toast 先清除this.$toast.success('登录成功')} catch (err) {console.log('登录失败', err)this.$toast.fail('登录失败,手机号或验证码错误')}// 停止 loading,它会把当前页面中所有的 toast 都给清除// loginToast.clear()
}

优化封装本地存储操作模块

创建 src/utils/storage.js 模块。

export const getItem = name => {const data = window.localStorage.getItem(name)try {return JSON.parse(data)} catch (err) {return data}
}export const setItem = (name, value) => {if (typeof value === 'object') {value = JSON.stringify(value)}window.localStorage.setItem(name, value)
}export const removeItem = name => {window.localStorage.removeItem(name)
}

关于 Token 过期问题

登录成功之后后端会返回两个 Token:

  • token:访问令牌,有效期2小时
  • refresh_token:刷新令牌,有效期14天,用于访问令牌过期之后重新获取新的访问令牌

我们的项目接口中设定的 Token 有效期是 2 小时,超过有效期服务端会返回 401 表示 Token 无效或过期了。

为什么过期时间这么短?

  • 为了安全,例如 Token 被别人盗用

过期了怎么办?

  • 让用户重新登录,用户体验太差了
  • 使用 refresh_token 解决 token 过期

如何使用 refresh_token 解决 token 过期?

到课程的后面我们开发的业务功能丰富起来之后,再给大家讲解 Token 过期处理。

大家需要注意的是在学习测试的时候如果收到 401 响应码,请重新登录再测试

img

概述:服务器生成token的过程中,会有两个时间,一个是token失效时间,一个是token刷新时间,刷新时间肯定比失效时间长,当用户的 token 过期时,你可以拿着过期的token去换取新的token,来保持用户的登陆状态,当然你这个过期token的过期时间必须在刷新时间之内,如果超出了刷新时间,那么返回的依旧是 401。

处理流程:

  1. 在axios的拦截器中加入token刷新逻辑
  2. 当用户token过期时,去向服务器请求新的 token
  3. 把旧的token替换为新的token
  4. 然后继续用户当前的请求

在请求的响应拦截器中统一处理 token 过期:

/*** 封装 axios 请求模块*/
import axios from "axios";
import jsonBig from "json-bigint";
import store from "@/store";
import router from "@/router";// axios.create 方法:复制一个 axios
const request = axios.create({baseURL: " // 基础路径
});/*** 配置处理后端返回数据中超出 js 安全整数范围问题*/
request.defaults.transformResponse = [function(data) {try {return jsonBig.parse(data);} catch (err) {return {};}}
];// 请求拦截器
request.interceptors.request.use(function(config) {const user = store.state.user;if (user) {config.headers.Authorization = `Bearer ${user.token}`;}// Do something before request is sentreturn config;},function(error) {// Do something with request errorreturn Promise.reject(error);}
);// 响应拦截器
request.interceptors.response.use(// 响应成功进入第1个函数// 该函数的参数是响应对象function(response) {// Any status code that lie within the range of 2xx cause this function to trigger// Do something with response datareturn response;},// 响应失败进入第2个函数,该函数的参数是错误对象async function(error) {// Any status codes that falls outside the range of 2xx cause this function to trigger// Do something with response error// 如果响应码是 401 ,则请求获取新的 token// 响应拦截器中的 error 就是那个响应的错误对象console.dir(error);if (error.response && error.response.status === 401) {// 校验是否有 refresh_tokenconst user = store.state.user;if (!user || !user.refresh_token) {router.push("/login");// 代码不要往后执行了return;}// 如果有refresh_token,则请求获取新的 tokentry {const res = await axios({method: "PUT",url: "headers: {Authorization: `Bearer ${user.refresh_token}`}});// 如果获取成功,则把新的 token 更新到容器中console.log("刷新 token  成功", res);store.commit("setUser", {token: res.data.data.token, // 最新获取的可用 tokenrefresh_token: user.refresh_token // 还是原来的 refresh_token});// 把之前失败的用户请求继续发出去// config 是一个对象,其中包含本次失败请求相关的那些配置信息,例如 url、method 都有// return 把 request 的请求结果继续返回给发请求的具体位置return request(error.config);} catch (err) {// 如果获取失败,直接跳转 登录页console.log("请求刷线 token 失败", err);router.push("/login");}}return Promise.reject(error);}
);export default request;

未完待续, 同学们请等待下一期

全套笔记资料代码移步: 前往gitee仓库查看

感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/763645.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线程池相关详解

1.线程池的核心参数 线程池核心参数主要参考ThreadPoolExecutor这个类的7个参数的构造函数&#xff1a; corePoolSize核心线程数目 maximumPoolSize最大线程数目&#xff08;核心线程救急线程的最大数目&#xff09; keepAliveTime生存时间:救急线程的生存时间&#xff0c;生…

【Linux中vim系列】如何在vim中检索字符串

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

1.2 编译型语言和解释型语言的区别

编译型语言和解释型语言的区别 通过高级语言编写的源码&#xff0c;我们能够轻松理解&#xff0c;但对于计算机来说&#xff0c;它只认识二进制指令&#xff0c;源码就是天书&#xff0c;根本无法识别。源码要想执行&#xff0c;必须先转换成二进制指令。 所谓二进制指令&…

elment-ui el-tabs组件 每次点击后 created方法都会执行2次

先看错误的 日志打印: 错误的代码如下: 正确的日志打印: 正确的代码如下: 前言: 在element-ui的tabs组件中,我们发现每次切换页面,所有的子组件都会重新渲染一次。当子页面需要发送数据请求并且子页面过多时,这样会过多的占用网络资源。这里我们可以使用 v-if 来进行…

Oh My Bug || PHPmyAdmin导入csv文件时,502报错

解决&#xff1a; 在宝塔面板文件配置中加入一下代码 location / { proxy_pass http://localhost:888; } location /backend-api { rewrite ^/backend-api(.*)$ $1 break; proxy_pass http://你的ip地址; }

判断出栈顺序是否满足入栈顺序

在学习数据结构的过程中,使用代码实现算法有利于加深理解 下面思路过程以及代码 0.先给出各个变量名字以及作用 1.函数 //match是具体的匹配函数&#xff1b;input是输入的顺序&#xff1b;output是输出的顺序 void match(string& input, string& output); 2.函数内部…

基于python+vue发艺美发店管理系统flask-django-php-nodejs

目 录 摘 要 I Abstract II 1 绪 论 1 1.1 研究背景 1 1.2 研究意义 2 1.3 主要内容 2 2系统相关技术概述 4 2.1开发工具 4 2.2 python语言简介 4 2.4 django框架介绍 5 2.5 MySQL数据库技术简介 6 3 发艺美发店管理系统的设计 7 3.1系统可行性分析 7 3.1.1技术可行性 8 3.1.2…

出现nginx error 问题

报错&#xff1a; Something has triggered an error on your website. This is the default error page for nginx that is distributed with Fedora. It is located /usr/share/nginx/html/50x.html You should customize this error page for your own site or edit the er…

【史上最全面arduino esp32教程】SPI层次结构SPI协议与SPI控制器结构

文章目录 前言一、SPI 程序层次1.1 硬件原理图1.2 硬件框图1.3 软件层次 二、SPI协议2.1 硬件连线2.2 如何访问SPI设备2.3 SPI 框图 总结 前言 欢迎阅读本篇文章&#xff0c;将为您介绍Arduino ESP32上的SPI通信协议。SPI&#xff08;Serial Peripheral Interface&#xff09;…

鸿蒙开发案例:【图像加载缓存库ImageKnife】

专门为OpenHarmony打造的一款图像加载缓存库&#xff0c;致力于更高效、更轻便、更简单。 简介 OpenHarmony的自研版本&#xff1a; 支持内存缓存&#xff0c;使用LRUCache算法&#xff0c;对图片数据进行内存缓存。支持磁盘缓存&#xff0c;对于下载图片会保存一份至磁盘当…

新材料正在加速金属3D打印的应用步伐

在金属3D打印领域&#xff0c;材料性能是影响工件综合表现的关键因素&#xff0c;如强度、硬度、耐腐蚀性、抛光性能以及导热性能等&#xff0c;都与材料息息相关&#xff0c;好的材料是推动金属3D打印向更多领域应用的基础。 在这一背景下&#xff0c;上海毅速新材料推出的多款…

递增四元组

解法&#xff1a; 首先都可以想到dp[i]&#xff1a;第i个元素结尾的递增四元组有dp[i]个 然后发现有一组数据&#xff1a;2,3,6,1,5,8。会出现6结尾和5结尾的递增三元组&#xff0c;也就是未来的决策受过去影响&#xff0c;专业的说就是有后效性。需要强化约束条件&#xff0…

vue3+threejs新手从零开发卡牌游戏(三):尝试在场景中绘制一张卡牌

首先我们思考下&#xff0c;一张最简单的卡牌有哪些东西构成&#xff1a;卡牌样式和卡牌数据。一张卡牌有正面和背面&#xff0c;有名称、属性、种族、攻击力等数据&#xff0c;我们先不考虑数据&#xff0c;先尝试在场景中绘制一张卡牌出来。 一、寻找卡牌素材 为了简单我直…

变老特效怎么弄?分享3个软件体验!

变老特效怎么弄&#xff1f;分享3个软件体验&#xff01; 当我们想要预览自己老去的模样&#xff0c;或者给照片增添一丝岁月的韵味时&#xff0c;变老特效成为了一个热门选择。那么&#xff0c;这种神奇的效果是如何实现的呢&#xff1f;又有哪些软件可以让我们轻松体验呢&am…

就业班 第二阶段 2401--3.18 day1 初识mysql

初识&#xff1a; 1、关系型数据库mysql、mariadb、sqlite 二维关系模型 2、非关系型数据库 redis、memcached sql 四个部分 DDL 数据库定义语言 创建数据库&#xff0c;创建用户&#xff0c;创建表 DML 数据库操作语言 增删改 DQL 数据库查询语言 查 DCL 数据库控制语言 授权 …

忘记密码找回流程请求拦截器-前端

目录 设置找回密码请求拦截器 1.相关参数 2.约定 代码实现 1. 实现思路 2. 实现代码 校园统一身份认证系统&#xff1a; 基于网络安全&#xff0c;找回密码、重新设置密码的流程和正常登录流程中密钥等请求头不一致。 设置找回密码请求拦截器 1.相关参数 clientId 应…

多线程实现

1.多线程&#xff1a;并发实现 主线程和子线程并行实现。 一个进程中有多个线程&#xff0c;可以同时进行多个任务。进程是系统分配的&#xff0c;线程的执行是由调度器决定的。 注意&#xff1a;线程开启不一定执行&#xff0c;由Cpu调度执行。 线程创建的三种方式&#xff…

HarmonyOS系统开发ArkTS常用组件文本输入及参数

TextInput文本输入组件&#xff0c;用于接收用户输入的文本内容。 1、TextInput组件的参数 TextInput(value?:{placeholder?: string|Resource , text?: string|Resource}) placeholder属性用于设置无输入时的提示文本text用于设置输入框当前的文本内容 Entry Component st…

20240316-2-协同过滤(collaborative filtering)

协同过滤(collaborative filtering) 直观解释 协同过滤是推荐算法中最常用的算法之一&#xff0c;它根据user与item的交互&#xff0c;发现item之间的相关性&#xff0c;或者发现user之间的相关性&#xff0c;进行推荐。比如你有位朋友看电影的爱好跟你类似&#xff0c;然后最…

【Review+预测】测试架构演进的曲折之路

文章目录 前言 一、“原始”阶段 二、“小打小闹”阶段 三、“小米加步枪”阶段 四、“摩托化部队”阶段 五、“骑兵连”阶段 六、“海军陆战队”阶段 七、“社区型组织”阶段 前言 近期公司的测试团队需要重新组织安排&#xff0c;本着谦虚谨慎的态度&#xff0c;我从…