http://toutiao.itheima.net/api.html#u5173u6ce8u7528u62370a3ca20id3du5173u6ce8u7528u62373e203ca3e
https://gitee.com/git640640/news
你还可以通过附加的命令行选项直接指定项目名称和你想要使用的模板。例如,要构建一个 Vite + Vue 项目,运行:
# npm 7+, extra double-dash is needed:
npm create vite@latest news -- --template vue
安装结束,命令提示你项目创建成功,按照命令行的提示在终端中分别输入:
# 进入你的项目目录
cd news
# 启动开发服务
npm run dev
Ctrl+鼠标左键单击http://127.0.0.1:5173/进入http访问地址,如果浏览器能打开页面,说明项目创建成功了。
几个好处:
代码备份
多人协作
历史记录
GitHub:https://gitee.com/git640640
如果没有本地仓库
#创建本地仓库
git init
#将文件添加到暂存区
git add .
#提交历史记录
git commit '提交日志'
#添加远程仓库地址
git remote add origin 你的远程仓库地址
#推送提交
git push -u origin master
如果已有本地仓库
#添加远程仓库地址
git remote add origin 你的远程仓库地址
#推送提交
git push -u origin master
如果之后项目代码有了变动需要提交
#将文件添加到暂存区
git add .
#提交历史记录
git commit '提交日志'
#推送提交
git push
默认生成的目录结构不满足我们的开发需求,所以这里需要做一些自定义改动。
这里主要就是下面的两个工作:
删除初始化的默认文件
新增调整我们需要的目录结构
npm install vue-router
npm install less
import { createRouter, createWebHashHistory } from 'vue-router'
// 1、创建路由规则
const routes = [
]
// 2、创建路由实例
const router = createRouter({
history: createWebHashHistory(),
routes
});
export default router;
src/components/HelloWorld.vue
src/components/Aside.vue
src/components/Main.vue
src/api 目录
存储接口封装
src/utils 目录
存储一些工具模块
src/styles 目录
style.less文件,存储全局样式
在 main.js 中加载全局样式 import '~/styles/style.less'
Vant 是有赞商城前端开发团队开发的一个基于 Vue.js 的移动端组件库,它提供了非常丰富的移动端功能组件,简单易用。
官方文档:https://vant-contrib.gitee.io/vant/#/zh-CN
安装
# Vue 3 项目,安装最新版 Vant
npm i vant
下面是使用 Vant 组件的用法示例:
main.js
import { createApp } from 'vue';
// 1. 引入你需要的组件
import { Button } from 'vant';
// 2. 引入组件样式
import 'vant/lib/index.css';
const app = createApp();
// 3. 注册你需要的组件
app.use(Button);
在基于 vite、webpack 或 vue-cli 的项目中使用 Vant 时,可以使用 unplugin-vue-components 插件,它可以自动引入组件,并按需引入组件的样式。
相比于常规用法,这种方式可以按需引入组件的 CSS 样式,从而减少一部分代码体积,但使用起来会变得繁琐一些。如果业务对 CSS 的体积要求不是特别极致,我们推荐使用更简便的常规用法。
# 通过 npm 安装
npm i unplugin-vue-components -D
如果是基于 vite 的项目,在 vite.config.js 文件中配置插件:
import vue from '@vitejs/plugin-vue';
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from 'unplugin-vue-components/resolvers';
export default {
plugins: [
vue(),
Components({
resolvers: [VantResolver()],
}),
],
};
完成以上两步,就可以直接在模板中使用 Vant 组件了,unplugin-vue-components 会解析模板并自动注册对应的组件。
ant 中有个别组件是以函数的形式提供的,包括 Toast,Dialog,Notify 和 ImagePreview 组件。在使用函数组件时,unplugin-vue-components 无法自动引入对应的样式,因此需要手动引入样式。
// Toast
import { showToast } from 'vant';
import 'vant/es/toast/style';
// Dialog
import { showDialog } from 'vant';
import 'vant/es/dialog/style';
// Notify
import { showNotify } from 'vant';
import 'vant/es/notify/style';
// ImagePreview
import { showImagePreview } from 'vant';
import 'vant/es/image-preview/style';
你可以在项目的入口文件或公共模块中引入以上组件的样式,这样在业务代码中使用组件时,便不再需要重复引入样式了。
如果需要使用 rem 单位进行适配,推荐使用以下两个工具:
postcss-pxtorem 是一款 PostCSS 插件,用于将 px 单位转化为 rem 单位
lib-flexible 用于设置 rem 基准值
下面我们分别将这两个工具配置到项目中完成 REM 适配。
npm i -S amfe-flexible
import 'amfe-flexible'
最后测试:在浏览器中切换不同的手机设备尺寸,观察 html 标签 font-size 的变化。
npm install postcss postcss-pxtorem --save-dev
如果设计稿的尺寸不是 375,而是 750 或其他大小,可以将 rootValue 配置调整为:
import postCssPxToRem from 'postcss-pxtorem';
export default defineConfig({
plugins: [vue(),
Components({
resolvers: [VantResolver()],
}),],
css: {
postcss: {
plugins: [
postCssPxToRem({
rootValue({ file }) {
return file.indexOf('vant') !== -1 ? 37.5 : 75;
}, // 1rem的大小
propList: ['*'], // 需要转换的属性,这里选择全部都进行转换
})
]
}
},
})
最后测试:刷新浏览器页面,审查元素的样式查看是否将px转化为rem。
转换之后的,可以看到 px 都被转换为了 rem。
需要注意的是:该插件不能转换行内样式中的px,例如
vite.config.js
import path from "path"
export default defineConfig({
resolve:{
alias:{
"~":path.resolve(__dirname,"src")
}
},
})
和之前项目一样,这里我们还是使用 axios 作为我们项目中的请求库,为了方便使用,我们把它封装为一个请求模块,在需要的时候直接加载即可。
安装
npm i axios
创建 src/utils/request.js
// 请求模块
import axios from 'axios'
const request = axios.create({
baseURL:'http://toutiao.itheima.net/' //基础路径
})
// 请求拦截器
export default request;
我们把每一个请求都封装成每一个独立的功能函数,在需要的时候加载调用,这种做法更便于接口的管理和维护。
通过分析页面,我们可以看到,首页、视频、消息、我的都使用的是同一个底部标签栏,我们没必要在每个页面中都写一个,所有为了通用方便,我们可以使用vue router的嵌套路由来处理。
父路由:一个空页面,包含一个tabbar,中间留子路由出口
子路由:首页、视频、消息、我的
这里主要使用到vant组件:
创建 src/views/layout/index.vue
首页
视频
消息
// 1、创建路由规则
const routes = [
{
path:"/",
component:Layout,
children:[
{
path:"/home",
name:"home",
component:Home,
},
{
path: "/video",
name: "video",
component: Video,
},
{
path: "/news",
name: "news",
component: News,
},
{
path: "/me",
name: "me",
component: Me,
}
]
},
{
path: '/login',
name: 'login',
component: Login
},
]
这里用到的vant组件:
NavBar导航栏:https://vant-contrib.gitee.io/vant/#/zh-CN/nav-bar
Form表单:https://vant-contrib.gitee.io/vant/#/zh-CN/form
Field输入框:https://vant-contrib.gitee.io/vant/#/zh-CN/field
Button按钮:https://vant-contrib.gitee.io/vant/#/zh-CN/button
src/views/login/index.vue
发送验证码
登录
思路:
注册点击登录事件
获取表单数据
表单验证
发请求提交
根据请求结果做下一步处理
import request from '~/utils/request'
// import store from '~/store'
// 登录注册验证
export const login = data => {
return request({
method: 'POST',
url: '/v1_0/authorizations',
data
})
}
Vant 中内置了Toast 轻提示组件,可以实现移动端常见的提示效果。
forbidClick:是否禁止背景点击是否禁止背景点击
给van-field组件配置rules验证规则
当表单提交的时候会自动触发表单验证
如果验证通过,会触发submit事件
如果验证不通过,不会触发submit事件
......
登录
创建 src/api/user.js 封装请求方法
// 发送短信验证码
// 每手机号每分钟1次
export const sendSms = mobile => {
return request({
method: 'GET',
url:`/v1_0/sms/codes/${mobile}`,
})
}
在src/views/login/index.vue中校验手机号
icon-prefix:图标类名前缀,等同于 Icon 组件的 class-prefix 属性
left-icon:左侧图标名称或图片链接,等同于 Icon 组件的 name 属性
validate:验证表单,支持传入一个或多个 name 来验证单个或部分表单项,不传入 name 时,会验证所有表单项
......
发送验证码
......
https://vant-contrib.gitee.io/vant/#/zh-CN/count-down
time:倒计时时长,单位毫秒·
format:时间格式,通过 format 属性设置倒计时文本的内容
finish:倒计时结束时触发
发送验证码
// 发送短信验证码
// 每手机号每分钟1次
export const sendSms = mobile => {
return request({
method: 'GET',
url:`/v1_0/sms/codes/${mobile}`,
})
}
发送验证码
const time = ref(1000 * 10);
async function onSendSms() {
// 1. 校验手机号
try {
await loginForm.value.validate("mobile");
} catch (err) {
return console.log("验证失败", err);
}
// 2. 验证通过,显示倒计时
isCountDownShow.value = true;
// 3. 请求发送验证码
try {
await sendSms(user.mobile);
showToast("发送成功");
} catch (err) {
// 发送失败,关闭倒计时
isCountDownShow.value = false;
if (err.response.status === 429) {
showToast("发送太频繁了,请稍后重试");
} else {
showToast("发送失败,请稍后重试");
}
}
}
Token是用户登录成功之后服务端返回的一个身份令牌,在项目中的多个业务中需要使用到:
访问需要授权的Api接口
校验页面的访问权限
...
但是我们只有在第一次用户登陆成功之后才能拿到Token。
所以为了能在其他模块中获取到Token数据,我们需要把它存储到一个公共的位置,方便随时取用。
往哪儿存?
本地存储
获取麻烦
数据不是响应式的
Vuex容器(推荐)
获取方便
响应式的
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储
管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变
化
1、安装
官网 https://vuex.vuejs.org/zh/
npm install vuex@next --save
2、在 src/store/index.js 中配置vuex文件
import { createStore } from 'vuex'
export default createStore({
state: {
user:JSON.parse(window.localStorage.getItem(TOKEN_KEY))
},
mutations: {
setUser(state, data) {
state.user = data
// 为了防止刷新丢失,我们需要把数据备份到本地存储
window.localStorage.setItem(TOKEN_KEY,JSON.stringify(state.user))
},
},
actions: {
},
modules: {
}
})
3、全局引用vuex文件
//导入文件
import store from './store'
//挂载使用
createApp(App).use(store)
4、登录成功以后将后端返回的 token 相关数据存储到容器中
async function onSubmit() {
showLoadingToast({
message: "登录中...",
forbidClick: true, // 是否禁止背景点击
duration: 0, // 持续展示 toast
});
try {
const res = await login(user);
store.commit("setUser", res.data.data);
console.log(data.data);
showSuccessToast("登陆成功");
} catch (err) {
showFailToast("登陆失败");
}
}
1、创建 src/utils/storage.js 模块。
// 封装本地存储操作模块
// 1.存储数据
export const setItem=(key,value)=>{
// 将数组、对象类型的数据转化为Json格式字符串进行存储
if(typeof value==='object'){
value=JSON.stringify(value)
}
window.localStorage.setItem(key,value)
}
// 2.获取数据
export const getItem=key=>{
const data=window.localStorage.getItem(key)
try {
return JSON.parse(data)
} catch (error) {
return data
}
}
// 3.删除数据
export const removeItem=key=>{
window.localStorage.removeItem(key)
}
2、在 src/store/index.js 中
import { createStore } from 'vuex'
import { getItem, setItem } from '~/utils/storage.js'
const TOKEN_KEY = 'NEWS_USER'
export default createStore({
state: {
user: getItem(TOKEN_KEY),
// user:JSON.parse(window.localStorage.getItem(TOKEN_KEY))
},
mutations: {
setUser(state, data) {
state.user = data
// 为了防止刷新丢失,我们需要把数据备份到本地存储
// window.localStorage.setItem(TOKEN_KEY,JSON.stringify(state.user))
setItem(TOKEN_KEY, state.user)
},
},
actions: {
},
modules: {
}
})
3、在src/views/login/index.vue中
登录成功之后后端会返回两个Token:
token:访问令牌,有效期2小时
refresh_token:刷新令牌,有效期14天,用于访问令牌过期之后重新获取新的令牌
我们的项目接口中设定Token有效期2小时,超过有效期服务端会返回401表示Token无效或过期了。
为什么过期时间这么短?
为了安全,例如Token被别人盗用
过期了怎么办?
让用户重新登录,用户体验太差了
使用refresh_token解决token过期
state:是放置所有公共状态的属性,如果你有一个公共状态数据,你只需要定义在state对象中。state是vuex的基本数据,用来存储变量;
// 引入库
import Vuex from 'vuex'
// 安装 vuex
Vue.use(Vuex)
// 初始化vuex对象
const store = new Vuex.Store({
state: {
// 管理数据
count: 0
}
})
组件中可以使用this.$store获取到vuex中的store对象实例,可通过state属性获取count。
state的数据:{{ $store.state.count }}
getters:从基本数据(state)派生的数据,相当于state的计算属性;
除了state之外,有时候还需要从state中派生出一些状态,这些状态是依赖state的,此时会用到getters,例如对列表进行过滤并计数:
const store = new Vuex.Store({
state: {
list: [1,2,3,4,5,6,7,8,9,10]
},
getters: {
// getters函数的第一个参数是 state
// 必须要有返回值
filterList: state => state.list.filter(item => item > 5)
}
})
mutations:提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个mutation都有一个字符串的事件类型(type)和一个回调函数(handler)。回调函数就是我们实际进行状态更改的地方,并且它会接受state作为第一个参数,提交载荷作为第二个参数。
// ...
mutations: {
addCount(state, data) {
state.count += data
}
你可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload)
// ...
action:和mutation的功能大致相同,不同之处在于:
action提交的是mutation,而不是直接变更状态
action可以包含任意异步操作
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
addCount(state) {
state.count++
}
},
actions: {
addCount(store) {
store.commit('addCount')
}
}
})
action函数接受一个与store实例具有相同方法和属性的context对象,因此你可以调用context.commit提交一个mutation,或者通过context.state和context.getter。
实践中,我们会经常用到ES2015的参数解构来简化代码:
actions: {
addCount({ commit }) {
commit('addCount')
}
}
分发action
store.dispatch('addCount')
5.modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使结构非常清晰,方便管理。