【项目】基于Vue2+Router+Vant 前端面经项目
环境配置
Vue脚手架的创建
- 在终端中打开输入
vue create 项目包名 -m npm
注意⚠️:项目名称不再允许包含大写字母。
- 选择第三项
3.选择要安装的模块
从上到下的功能模块:
- Babel - ES:降级处理
- Router-Vue:路由插件
- CSS预处理器
- ESLint代码格式检查工具
- 选择下载Vue2版本
- 选择模式
- 选择路由模式(输入n)
- 选择Css预处理器(选择Less)
- ESLint版本(选择Standard——标准版)
- 选择配置
从上到下依次是:
- Lint on save 代码保存的时候,检查代码格式
- 配置文件保存位置选择保存到单个文件中
- 如果选择
package.json
表示几种保存在package.json
中- 是否将所有选择保存为预设(输入n)
- 等待安装完成,出现如下字样即表示创建成功。
- 运行项目:
cd
到指定文件夹,执行npm run serve
启动项目。
安装ESLint插件
ESLint 是一个用于检测和规范 JavaScript 代码风格及语法错误的工具。它可以帮助开发者保持代码的一致性和可读性,提高代码质量。例如,它能检查是否遗漏了分号,变量名是否符合规范等。
作用:检查代码格式,保证每个成员的代码风格应该一致。
安装方式:直接在vscode扩展商店中搜索ESLint
,点击下载即可。
当通过 @vue/cli 脚手架工具安装项目后,默认已经将 eslint 相关的包安装并配置好了。
我们将使用vue 的 eslint 插件规定的默认规则进行代码检查。如果需要查看规则,则可以查看 https://eslint.nodejs.cn/docs/latest/rules/%E3%80%82
ESLint报错提示:
配置ESLint
-
关于ESLint的配置,需要放到配置文件
.eslintrc.js
中。 -
打开
.eslintrc.js
文件,找到里面的rules
节点,这个rules
节点,用于自定义 ESLint规则 -
配置的每条语句结束,必须加分号。
例如:在 rules
节点中加入如下规则
"rules": {"semi": ["error", "always"]
}
注意⚠️:修改了配置文件,需要重启项目。
安装自动化格式插件
自动化格式插件有很多种,比如 ESlint、Vue插件,都带这种格式化功能。
这边的话就跟我的配置来安装包,保证了配置统一性。
安装步骤:
- 在扩展商店查找以下👇插件
- 打开 vscode,按
Ctrl + ,
快捷键,打开设置界面。 - 点击右上角的
打开设置(json)
- 将下面的代码复制进去(复制到最后一个大括号之前)
// 代码缩进 2 个空格
"editor.tabSize": 2,
// 保存(包括自动保存),自动格式化
"editor.formatOnSave": true,
// 按Ctrl + S保存时,自动修复
"editor.codeActionsOnSave": {"source.fixAll.eslint": true
},
// 编辑器窗口失去焦点时,自动保存代码【可选】
"files.autoSave": "onFocusChange",
// 配置vue默认的格式化程序【必须的配置】
"[vue]": {"editor.defaultFormatter": "numso.prettier-standard-vscode"
},
// 设置JS文件的默认格式化程序【必须的配置】
"[javascript]": {"editor.defaultFormatter": "numso.prettier-standard-vscode"
},
"[jsonc]": {"editor.defaultFormatter": "numso.prettier-standard-vscode"
},
注意⚠️:如果出现 黄色的下划线,说明有重复的配置。去掉前面的(你自己的)。
使用方法:
右键–>找到使用…格式化文档–>选择Prettier-Standard
Vant组件库
因为本次项目是一个移动端项目,所以移动端上使用的组件库目前比较不错的一个流行库就是Vant啦🥳。
官方网站:Vant 2 - 轻量、可靠的移动端组件库 (vant-ui.github.io)
安装步骤:
打开终端输入以下👇命令
npm i vant@latest-v2
安装如果有报错用👇
npm i vant@latest-v2 --legacy-peer-deps
检查是否安装完成:
引入组件
- 按需引入:只引入项目中实际用到的组件,能减小项目体积。通常通过配置或特定的模块加载方式实现。
- 全局引入:将组件库整体加载到项目中,在任何地方都能直接使用。比如在入口文件中一次性引入。
- 手动导入:把组件库当作一个模块,在需要的模块中单独引入相关组件。
⚠️ 注意三种方式不能混用。
⚠️ 我会讲按需引入
和全局引入
这两种方式的引入步骤,但是本项目使用的是全局引入的方式来制作。所以按需引入方式教学大家看看就好🙏。
全局引入
步骤:直接在main.js中粘贴以下👇代码:
import Vant from 'vant'
import 'vant/lib/index.css'
Vue.use(Vant)
按需引入
步骤:
-
安装一个插件(插件配置后,就可以实现自动导入)
npm i babel-plugin-import -D --legacy-peer-deps
-
在
babel.config.js
中配置module.exports = {presets: ['@vue/cli-plugin-babel/preset'],plugins: [['import', {libraryName: 'vant',libraryDirectory: 'es',style: true}, 'vant']] }
-
按需引入main.js
import { Button, Icon } from 'vant'Vue.use(Button) Vue.use(Icon)
-
在App.vue中测试
<van-button type="primary">主要按钮</van-button> <van-button type="info">信息按钮</van-button> <van-button type="default">默认按钮</van-button> <van-button type="warning">警告按钮</van-button> <van-button type="danger">危险按钮</van-button>
再次强调⚠️
完整导入和按需导入Vant,二者只能用一个;
- 如果你用完整导入,请将按需导入的配置都去掉,然后重启项目。
实现vw适配
使用Vant组件的适配方案就非常的简单😆。
官方说明:https://vant-contrib.gitee.io/vant/v2/#/zh-CN/advanced-usage
步骤:
-
安装命令
npm i postcss-px-to-viewport@1.1.1 -D 安装报错就用👇 npm i postcss-px-to-viewport@1.1.1 -D --legacy-peer-deps
-
在项目的根目录,新建一个配置文件
pocstcss.config.js
// postcss.config.js module.exports = {plugins: {'postcss-px-to-viewport': {// 设计稿如果是2倍图,宽是750,则 750/2 = 375,下面就写375// 设计稿如果是3倍图,宽是 1080,则 1080/3 = 360,下面就写360viewportWidth: 375}} }
路由配置
总体路由结构图:
我们根据上面的结构图分析一下,这个项目呢——是由4个页面组成的。
一共有4个一级目录:App.vue
、Login.vue
、Register.vue
、Detall.vue
和1个二级目录:Home.vue
。所以我们先根据结构创建好项目目录先。
怎么在src
文件夹下的views
新建以下👇文件:
-
Home.vue:用来做主页
<template><div id="app">主页</div> </template><script>export default {} </script><style lang="less" scoped></style>
-
Login.vue:用来做登录页
<template><div id="app">登录页</div> </template><script>export default {} </script><style lang="less" scoped></style>
-
Register.vue:用来做注册页
<template><div id="app">登录页</div> </template><script>export default {} </script><style lang="less" scoped></style>
-
Detall.vue:用来做详情页
<template><div id="app">登录页</div> </template><script>export default {} </script><style lang="less" scoped></style>
创建完上面四个文件之后我们还有在views
文件夹下再创建一个Layout
包,继续创建我们的二级目录文件:
-
Article.vue:文章列表
<template><div id="app">文章列表</div> </template><script>export default {} </script><style lang="less" scoped></style>
-
Collect.vue:收藏界面
<template><div id="app">收藏界面</div> </template><script>export default {} </script><style lang="less" scoped></style>
-
Like.vue:喜欢界面
<template><div id="app">喜欢界面</div> </template><script>export default {} </script><style lang="less" scoped></style>
-
User.vue:用户界面
<template><div id="app">用户界面</div> </template><script>export default {} </script><style lang="less" scoped></style>
配置路由
前置知识:路由
路由模版:
const routes = [{path:'/', //url地址component: () => import('文件路径'), //指定文件} ]
二级路由模版:
const routes = [{path:'/', //url地址component: () => import('文件路径'), //指定文件redirect: '/二级默认展示url地址', // 重定向,指向二级路由默认展示的页面children: [{path:'/', //url地址component: () => import('文件路径'), //指定文件}]} ]
直接在router文件夹下的index.js
文件下书写我们的路由代码。
import Vue from 'vue'
import VueRouter from 'vue-router'Vue.use(VueRouter)const routes = [// 主页{path: '/',component: () => import('@/views/Home.vue'),redirect: '/article', // 重定向,指向二级路由默认展示的页面children: [// 列表页{ path: 'article', component: () => import('@/views/Layout/Article') },// 藏页{ path: 'collect', component: () => import('@/views/Layout/Collect') },// 喜欢页{ path: 'like', component: () => import('@/views/Layout/Like') },// 用户页{ path: 'user', component: () => import('@/views/Layout/User') }]},// 登录页{ path: '/login', component: () => import('@/views/Login.vue') },// 详情页{ path: '/detail', component: () => import('@/views/Detail.vue') },// 注册页{ path: '/register', component: () => import('@/views/Register.vue') }
]const router = new VueRouter({routes
})export default router
之后在App.vue中和Home.vue中,分别加入 <router-view></router-view>
。
<template><div class="layout-page"><router-view></router-view></div>
</template><script>
export default {name: 'layout-page'
}
</script><style lang="less" scoped>
</style>
最后我们进而可以通过url栏手动修改值来测试一下页面跳转。
登录页面搭建
静态布局
效果展示:
登录页面的搭建主要用到Vant组件中的导航栏组件
。
所以我们只需要保留title属性即可:
<!-- 登录login页面 -->
<template><div class="login-page"><van-nav-bar title="面经项目登录" /></div>
</template><script>
export default {name: 'login-page',methods: {}
}
</script><style lang="less" scoped></style>
然后我们就要写一个表单来实现账号密码输入框,同样的在Vant组件中找到表单
组件。
我先来对一些代码字段解析一下让大家更好的理解:
<van-fieldv-model="username"name="username"label="账号"placeholder="请输入账号":rules="[{ required: true, message: '请填写用户名' }]"/>
v-model="username"
: 将输入字段的值绑定到 Vue 实例的username
数据属性上。name="username"
: 设置输入字段的name
属性,通常用于表单提交时标识字段。label="账号"
: 设置输入字段的标签文本为“账号”。placeholder="请输入账号"
: 设置输入字段的占位符文本为“请输入账号”。:rules="[{ required: true, message: '请填写用户名' }]"
: 设置输入字段的验证规则,这里要求输入字段必须填写,否则会显示错误消息“请填写用户名”。
然后就是修改提交
按钮的样式,我这里也是先对代码进行解释:
<van-button round block type="info" native-type="submit">提交</van-button>
round
: 使按钮具有圆角。block
: 使按钮宽度充满其父容器。type="info"
: 设置按钮的类型为info
,通常表示按钮具有提示或信息性质。native-type="submit"
: 将按钮的type
属性设置为submit
,使其在表单中可以提交表单数据。
我们还需要修改一下圆角按钮的样式,首先将提交
改为登录
,然后去掉小圆角,将按钮颜色改为橙色。
如何修改颜色呢?Vant 的 <van-button>
组件支持 color
属性,你可以直接在组件上设置颜色。例如,如果你想将按钮颜色改为橙色。
<div style="margin: 16px"><van-button block type="info" native-type="submit" color="#FF5500">登录</van-button>
下面还有一个超链接用于跳转到注册页面,可以使用a标签
或者是直接使用<router-link>
标签。我这里就用link标签方便一点。
<router-link to="/register">还没有账号,去注册吧!</router-link>
测试一下是否能跳转:
然后再给link添加一个class写样式:
<style lang="less" scoped>
.link {float: right;font-size: 14px;color: #ff5500;margin-right: 10px;
}
</style>
最后就完成了我们想要的效果啦🥳!
但是我们还可以把代码复制给Ai做一下界面美化。下面我就贴上我完成的代码:
<!-- 登录login页面 -->
<template><div class="login-page"><!-- 标题栏 --><van-nav-bar title="面经项目登录" /><!-- 表单 --><div class="font-div"><van-form @submit="onSubmit"><van-fieldv-model="username"name="username"label="账号"placeholder="请输入账号":rules="[{ required: true, message: '请填写用户名' }]"/><van-fieldv-model="password"type="password"name="password"label="密码"placeholder="请输入密码":rules="[{ required: true, message: '请填写密码' }]"/><div style="margin: 16px"><van-button block type="info" native-type="submit" color="#FF5500">登录</van-button></div></van-form><!-- 超链接-注册按钮 --><router-link class="link" to="/register">还没有账号,去注册吧!</router-link></div></div>
</template><script>
export default {name: 'login-page',data () {return {username: '',password: ''}},methods: {onSubmit (values) {console.log('submit', values)}}
}
</script><style lang="less" scoped>
.font-div {position: absolute;top: 26%;left: 50%;transform: translate(-50%, -50%);width: 300px;padding: 20px;border: 1px solid #ddd;border-radius: 8px;// box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);// background-color: #fff;
}.van-nav-bar {// background-color: #ff5500;margin-top: 10px;/deep/ .van-nav-bar__title {color: #ff5500;font-weight: 700;font-size: 25px;}
}.van-button {border-radius: 4px;
}.link {display: block;text-align: right;font-size: 14px;color: #ff5500;margin-top: 10px;
}
</style>
表单验证
Vant组件也有告诉我们,通过 rules
定义表单校验规则
使用 Field 的 rules
属性可以定义校验规则,可选属性如下:
键名 | 说明 | 类型 |
---|---|---|
required | 是否为必选字段,当值为空字符串、空数组、undefined 、null 时,校验不通过 | boolean |
message v2.5.3 | 错误提示文案 | string | (value, rule) => string |
validator v2.5.3 | 通过函数进行校验 | (value, rule) => boolean | Promise |
pattern v2.5.3 | 通过正则表达式进行校验 | RegExp |
trigger v2.5.2 | 本项规则的触发时机,可选值为 onChange 、onBlur | string |
formatter v2.5.3 | 格式化函数,将表单项的值转换后进行校验 | (value, rule) => any |
我们这里主要用到required
`message \
pattern `。通过正则表达式校验可以让我们对输入的信息进行数字、字母、长度的要求。
v-model="password"type="password"name="password"label="密码"placeholder="请输入密码":rules="[{ required: true, message: '请输入密码' },{pattern: /^(?=.*[a-zA-Z]).{2,6}$/,message: '密码必须为2-6位且包含至少一个字母'}]"/>
在这个正则表达式中:
^(?=.*[a-zA-Z])
是一个正向先行断言,确保字符串中至少包含一个字母(不区分大小写)。.{2,6}$
确保整个字符串的长度在2到6个字符之间。
如果规则比较多,可以在data里面写一个变量,
<van-fieldv-model="username"name="username"label="账号"placeholder="请输入账号":rules="userRules"
/>export default {name: 'login-page',data () {return {username: '',password: '',userRules: [{ required: true, message: '请输入账号' },{ pattern: /^\d{2,6}$/, message: '账号必须为2-6位数字' }]}},
}
其中 pattern
键名对应的正则表达式 /^\d{2,6}$/
用于验证输入是否全为数字且长度为2-6位。如果输入不符合这个正则表达式,将会显示 message
键名对应的错误信息。
注册页面布局
注册页面和登录页面大多相似,我们直接复制登录页面的代码到注册页面中,然后稍微做一些修改就好了。
- 修改标题
- 修改注册按钮
- 修改超链接,别忘了还有文本也要修改哦🤗
- 修改组件名称
- 修改主题颜色,然他和注册页面有所区分
整体效果:
发送请求
首先我们要注册一个账号,所以就要对后台数据发送请求,使用axios来完成这个注册操作。
首先安装一个axios包
npm i axios
如果报错了就用以下👇安装
npm install axios --legacy-peer-deps
axios前置知识
补一下前置知识有关axios的内容!
import axios from 'axios'export default{name:'',date(){},method:{async onSubmit(values){//定义一个变量接受结果const {data:res} = await axios({method:'',url:'',data:value})console.log(res)}}
}
因为黑马那边的数据接口关闭了,所以我们没办法调用他们接口里面的数据。既然别人不给我们,那么我们就自己造!因为大家都是前端朋友,使用json-server来搭建接口会更加友好一点,不会涉及后端知识,也不用mysql数据库。
搭建数据大家可以看我这一篇文章:
然后我们这里的话也是用的是json-server,我这里给大家放上json数据供大家搭建
{"users": [{"id": 1,"name": "李雷","UID": "01a1"},{"id": 2,"name": "韩梅梅","UID": "02b2"},{"id": 3,"name": "张伟","UID": "03c3"},{"id": 4,"name": "王芳","UID": "04d4"},{"id": 5,"name": "赵敏","UID": "05e5"},{"id": 6,"name": "马云","UID": "06f6"},{"id": 7,"name": "马东","UID": "07a7"}],"register": [{"id": 1,"username": "01","password": "a1"},{"id": 2,"username": "02","password": "a2"},{"id": 3,"username": "03","password": "a3"},{"id": 4,"username": "04","password": "a4"},{"id": 5,"username": "05","password": "a5"},{"id": 6,"username": "06","password": "a6"},{"id": 7,"username": "07","password": "a7"}]
}
封装axios
为了让整体项目更加的规整,我们需要将axios单独封装到一个文件当中,方便后面管理。
我们在src目录下新建一个utils
文件夹,里面再新建一个文件一般叫做request.js
。
// 封装axios请求
// 1.导入axios
import axios from 'axios'
// 2.配置
const requerst = axios.create({// 配置请求根路径baseURL: 'http://localhost:5000'// 超时时间// timeout:5000
})
// request配置拦截器
// 3.导出
export default requerst
导入request.js文件
import request from '@/utils/request'
配置axios
async onSubmit (values) {const { data: res } = await request({method: 'POST',url: '/register',data: values})console.log(res)
抽离注册的API方法
在src目录下新建一个Api
文件夹,里面再新建一个文件一般叫做user.js
。用来存放与用户相关的请求。
// 和用户相关的请求
import request from '@/utils/request'export function registerAPI (data) {return request({method: 'POST',url: '',data: data})
}
在注册组件中导入api方法
// <!-- 导入封装的axios -->
// import request from '@/utils/request'
// 导入API方法
import { registerAPI } from '@/Api/user'
记得把上次导入axios语句注释掉⚠️
修改调用方法
const { data: res } = await registerAPI()
// 之前的都注释掉了// request({// method: 'POST',// url: 'register',// data: values// })
我们可以发现调用的registerAPI
依旧是原来的request
。
最后再给registerAPI()方法传入values参数:
const { data: res } = await registerAPI(values)
还有user.js里面的url写上我们的路径:
// 和用户相关的请求
import request from '@/utils/request'export function registerAPI (data) {return request({method: 'POST',url: 'register', //user.js里面的url写上我们的路径data: data})
}
之所以要这样写,就是为了我们这个方法能够复用起来,以后要使用这个直接调用这个方法就可以了。
注册、登录页面跳转
下面我们就要写一个注册跳转功能,让我们注册完之后跳转到登录界面 。使用axios的try
和catch
方法来实现。
async onSubmit (values) {try {const { data: res } = await registerAPI(values)console.log(res) // 其实这个res这个变量没有用到过,可以删掉也可以的this.$toast('注册成功')this.username = this.password = '' // 重置表单this.$router.push('/login') // 跳转到登录页面} catch (err) {}}
同样的我们也在登录页面设置一些,来实现登录成功跳转到首页当中。这一次我们只需要在login.vue文件中进行设置就可以了,具体代码和注册跳转是同样滴。
methods: {// 点击登录的时候执行async onSubmit (values) {// values={username:'',password:''}try {await registerAPI(values) //这里我就把没有用到的data删掉了this.$toast('登录成功')this.username = this.password = ''this.$router.push('/article') //这里url要变成主页的地址} catch (err) {}console.log('submit', values)}}
⚠️前面别忘记导入封装好的registerAPI
方法。
主页搭建
面经列表数据
封装面经方法
在Api包中新建一个article.js
。
// 存放和面经内容相关的请求
import request from '@/utils/request'export function articleAPI (params) {return request({url: '',params: params})
}
这里因为params先待定。
然后我们在article.vue组件中的脚本区域获取数据。
import { articleAPI } from '@/Api/article'
export default {name: 'article-page',data () {return {curent: 1, // 页码、获取第一页的数据sorter: 'weight_desc'}},methods: {async article () {const { data: res } = await articleAPI({current: this.current,sorter: this.sorter})console.log(res)}},created () {this.article() // 创建生命周期,使其一开始就加载页面}
}
{"code": 10000,"message": "请求成功","data": {"current": 1,"pageTotal": 10,"pageSize": 20,"total": 1000,"rows": [{"id": "40821","stem": "广东紫云平台数据服务有限公司前端面经,未通过","content": "<p><b>面试公司:</b>广东紫云平台数据服务有限公司<br></p><p><b>面试岗位"createdAt": "2022-01-20 00-00-00","creator": "中二苏北陌","avatar": "http://teachoss.itheima.net/heimaQuestionMiniapp/%E5%AE%98%E6%96%B9%E9%BB%98%E8%AE%A4%E5%A4%B4%E5%83%8F%402x.png","likeCount": 1,"views": 32}]}
}
axios前置知识——get请求
省略
method
属性(默认GET):javascriptexport function articleAPI(data) {return request({url: '', // 这里应填写实际的API URLparams: data // 使用params来传递URL查询参数}); }
明确指定
method
为'GET'
:javascriptexport function articleAPI(data) {return request({method: 'GET',url: '', // 同样需要填写实际的API URLparams: data}); }
在这两种方式中,
params
属性用于传递URL查询参数,这是GET请求中常见的做法。例如,如果你需要根据用户ID获取文章详情,你可以这样调用articleAPI
函数:javascript复制 articleAPI({ userId: 123 });
面经页面搭建
页面搭建就是用Vant组件库里面的Cell单元格,通过循环遍历的方式,来渲染出列表。
<van-cell title="单元格" value="内容" />
但是单元格现实的内容太少了,我们还需要用到插槽
来搭建更多的内容。
<van-cell value="内容" is-link><!-- 使用 title 插槽来自定义标题 --><template #title><span class="custom-title">单元格</span><van-tag type="danger">标签</van-tag></template>
</van-cell><van-cell title="单元格" icon="shop-o"><!-- 使用 right-icon 插槽来自定义右侧图标 --><template #right-icon><van-icon name="search" class="search-icon" /></template>
</van-cell><style>.custom-title {margin-right: 4px;vertical-align: middle;}.search-icon {font-size: 16px;line-height: inherit;}
</style>
给插槽传值
在Vue 2中,给插槽(slot)传值通常使用
slot-scope
属性。slot-scope
属性允许你将数据传递给具名插槽或作用域插槽。全写形式:<template v-slot:title> 简写形式:<template #title>
静态结构我去直接提供给大家啦,大家可以试一试调一调。
<template><div class="article-page"><nav class="my-nav van-hairline--bottom"><a href="javascript:;">推荐</a><a href="javascript:;">最新</a><div class="logo"><img src="@/assets/logo.png" alt=""></div></nav><van-cell class="article-item" ><template #title><div class="head"><img src="@/assets/logo.png" alt="" /><div class="con"><p class="title van-ellipsis">宇宙头条校招前端面经</p><p class="other">不风流怎样倜傥 | 2022-01-20 00-00-00</p></div></div></template><template #label><div class="body van-multi-ellipsis--l2">笔者读大三, 前端小白一枚, 正在准备春招, 人生第一次面试, 投了头条前端, 总共经历了四轮技术面试和一轮hr面, 不多说, 直接上题 一面</div><div class="foot">点赞 46 | 浏览 332</div></template></van-cell></div>
</template>
样式:
<style lang="less" scoped>
.article-page {margin-bottom: 50px;margin-top: 44px;.my-nav {height: 44px;position: fixed;left: 0;top: 0;width: 100%;z-index: 999;background: #fff;display: flex;align-items: center;> a {color: #999;font-size: 14px;line-height: 44px;margin-left: 20px;position: relative;transition: all 0.3s;&::after {content: '';position: absolute;left: 50%;transform: translateX(-50%);bottom: 0;width: 0;height: 2px;background: #222;transition: all 0.3s;}&.active {color: #222;&::after {width: 14px;}}}.logo {flex: 1;display: flex;justify-content: flex-end;> img {width: 64px;height: 28px;display: block;margin-right: 10px;}}}
}
.article-item {.head {display: flex;img {width: 40px;height: 40px;border-radius: 50%;overflow: hidden;}.con {flex: 1;overflow: hidden;padding-left: 10px;p {margin: 0;line-height: 1.5;&.title {width: 280px;}&.other {font-size: 10px;color: #999;}}}}.body {font-size: 14px;color: #666;line-height: 1.6;margin-top: 10px;}.foot {font-size: 12px;color: #999;margin-top: 10px;}
}
</style>
将单个单元格封装成组件
因为不止在主页中会用到这个单元格列表,其二级目录下的收藏页,或者喜欢页也都会有这个列表的展示,所以我们不妨直接将他封装成一个组件,后面需要用到的时候直接调用即可ヾ(◍°∇°◍)ノ゙。
在src下面新建一个components
文件夹用来存放组件,然后新建一个.vue
文件,就叫做Articleitem.vue
吧。
然后把上面写的单元格列表和样式全部丢进去(≧∇≦)ノ。
<!-- 封装单元格组件 -->
<template><div><van-cell class="article-item"><template #title><div class="head"><img src="@/assets/logo.png" alt="" /><div class="con"><p class="title van-ellipsis">宇宙头条校招前端面经</p><p class="other">不风流怎样倜傥 | 2022-01-20 00-00-00</p></div></div></template><template #label><div class="body van-multi-ellipsis--l2">笔者读大三, 前端小白一枚, 正在准备春招, 人生第一次面试, 投了头条前端,总共经历了四轮技术面试和一轮hr面, 不多说, 直接上题 一面</div><div class="foot">点赞 46 | 浏览 332</div></template></van-cell></div>
</template><script>
export default {}
</script><style lang="less" scoped>
.article-item {.head {display: flex;img {width: 40px;height: 40px;border-radius: 50%;overflow: hidden;}.con {flex: 1;overflow: hidden;padding-left: 10px;p {margin: 0;line-height: 1.5;&.title {width: 280px;}&.other {font-size: 10px;color: #999;}}}}.body {font-size: 14px;color: #666;line-height: 1.6;margin-top: 10px;}.foot {font-size: 12px;color: #999;margin-top: 10px;}
}
</style>
注册全局组件
在main.js
中导入:
import ArticleItem from '@/components/ArticleItem.vue'
注册:
Vue.component('ArticleItem', ArticleItem)
回到Article.vue文件使用组件
<ArticleItem></ArticleItem>
[eslint]Component name "Articleitem" should always be multi-word vue/multi-word-component-names
循环遍历列表数据
在data中创建list用来存储面经列表项目
data () {return {curent: 1, // 页码、获取第一页的数据sorter: 'weight_desc'list:[] //存储要展示的面经列表项目}},
v-for循环遍历
<ArticleItem v-for="item in list" :key="item.id" :item="item" //父传子></ArticleItem>
子组件接受传值
props: {item: {type: Object,//默认值如果是数组或对象,则而要写成函数,函数中返回数组或对象default: () =>({})}}
使用差值表达式渲染数据
推荐和最新栏目
类名切换
接口文档中是规定了排序字段通过这个来区分最新
还是推荐
。
sorter属性通过发请求数据到之前定义好的onLoad
方法中:
用到的是切换类名,通过绑定类名的值,在模版字符串里面进行判断接口中的数据是哪一种,吐过是推荐就传weight,如果是最新页面就不要传值。
然后还有添加一个点击事件,作用是为了通过点击事件实现两个a标签切换。
点击方法:
数据切换
类名变换实现了,那么我们的样式就会改变。
继续调用上面的Onload
方法,来获取到数据。
最不好理解的问题,如果在推荐页面滚到比较低的地方,然后切换到最新页面,这个时候滚动条依旧还在先前推荐页面滚动的位置,而不是重新再最顶部。为了避免重复发送请求,就要加上这句代码:
this.loading=true//避免自动发送请求
实现详情页
数据获取
我们希望在列表中点击标题的时候,能够进入到详细页面中观看内容。
localhost:8080/#/detail
这个时候就要用到路由传参
前置知识——路由传参
传参方式
地址栏
- 查询参数:
/xx/xx?id=2
- path:
/xx/xx
- 组件中获取参数值:
$route.query.id
动态路由
- 动态路由:
/xx/xx/2
- path:
/xx/xx/:id
- 组件中获取参数值:
$route.params.id
-
设置传参方式,在封装好的articleitem.vue组件中添加一个点击事件
@click="$router.push(/detail?id=${item.id})
-
路由规则不需要改动
-
组件中获取参数值
内容渲染
在article.js
文件中封装axios组件
导入封装好的axios请求
组件中处理请求数据
收藏点赞功能
在文章中,我们会有两个小按钮来实现收藏和点赞两个功能。
主要是通过的在接口里面的这两个数据:
在组件中使用:
-
绑定数据
-
添加点击事件
-
编写方法
-
处理细节
当我们点赞的时候,这里也要加上“1” -
提示功能就用咱们得
轻提示
就好啦。this.$toast.success('点赞成功')
-
修改服务器数据,因为我们做的都是页面上的数据,还没有涉及到服务器请求。一刷新就回到原来的数据了。
实现主题定制
整体网站风格,其实都是橙色的,可以通过变量覆盖的方式,制定主题色
https://vant-contrib.gitee.io/vant/v2/#/zh-CN/theme
注意 ⚠️下面的操作会涉及到修改配置文件,所以修改完配置文件之后都要重启一下项目。
引入样式源文件
main.js
引入less:
import Vant from 'vant'
import 'vant/lib/index.css'
import 'vant/lib/index.less'
Vue.use(Vant)
修改样式变量
vue.config.js
覆盖变量,直接全部复制我们的覆盖掉原来代码就可以了。
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({transpileDependencies: true,css: {loaderOptions: {less: {// 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。lessOptions: {modifyVars: {// 直接覆盖变量blue: '#ff5500',// 'text-color': '#111',// 'border-color': '#eee'// 或者可以通过 less 文件覆盖(文件路径为绝对路径)// hack: 'true; @import "your-less-file-path.less";'}}}}}
})
修改配置文件后,需要重启服务。
实现收藏和喜欢页面
收藏和喜欢的页面结构都是一样的,就像登录
和注册
一样。所以我们只需要写一个页面,后面再稍微做一下修改就完成了这两个页面。
写一个标题栏:
1.收藏页标题去登录页复制
<template><div class="collect-page"><van-nav-bar fixed title="我的收藏" /><van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"><article-item v-for="(item, i) in list" :key="i" :item="item" /></van-list></div>
</template>
封装API请求方法:
export const collectAndLikeListAPI = (params) => {return request.get('/h5/interview/opt/list', {params: params})
}
script内容:
去Article.vue复制<van-list></van-list>
,然后去Article.vue复制data和methods
,去掉sorter变量:去掉changeSorter方法
,最后在api/articlejs中,新封装一个获取收藏列表的方法
<script>
import { collectAndLikeListAPI } from '@/api/article'
export default {name: 'collect-page',data () {return {list: [],loading: false,finished: false,page: 1}},methods: {async onLoad () {// 异步更新数据const { data: res } = await collectAndLikeListAPI({page: this.page,optType: 2})this.list.push(...res.data.rows)this.loading = falseif (this.page === res.data.pageTotal || !res.data.rows.length) {this.finished = true} else {this.page++}}}
}
然后我们在首页收藏一些页面,就有最终的效果啦!
喜欢页面也是一样的,直接复制上面的代码,修改请求参数optType: 1
即可。
前置知识——vuex的使用
Vuex是vue项目中实现大范围数据共享的技术方案。
作用:能够方便、高效的实现组件之间的数据共享。
安装vuex
npm i vuex@3.6.2
配置vuex
- 封装一个store模块,里面新建一个index.js
- 在man.js导入store
基本使用:
state用于存储数据。
state:{name:'tom',age:20 }
组件中直接使用数据,组件中可通过
$store.state.属性名
getters是vuex中的计算属性,和组件中的计算属性意义一样,但是不支持set修改。
getters:{abc(state){return s state.age * state.age} }
为了方便取state中的数据:插件作者会给每个计算属性方法,传递一个state参数。
使用和state用法一致
$store.getters.属性名
导航守卫
前置知识——导航守卫
加入导航守卫,目的是如果在没有登录的情况下,是不允许访问到其他的页面。
- to.path — 要访问的地址
- from.path — 你从哪里来的
- next() — 放行
- next(‘/url’) — 不放行,并且跳转到url中。
在router.index.js路由文件中使用以下👇代码:
router.beforeEach((to,from,next)=>{if (to.path !=='/login'&& store.state.user.token === ''){next('/login')return}next()
})
打包项目
在项目终端中运行npm run build
打包的时候可能会有一点点久,大家稍等一会。
当再次出现终端路径,就表示打包完成了。
打包完毕之后就会生成一个dist
文件夹,文件夹里面的内容如下:
然后我们发现点击index.html
,项目是展示不出来的。因为我们的一些数据都在接口服务器上,需要在服务器上打来才行。如果要本地打开也不是没有办法,我们在vue.config.js
文件中添加下面一句话:
//表示打包的结果,允许以文件的形式打开
publicPath:'./'
我们还会发现在生成的文件当中会出现非常的.map
文件,这个作用就是映射原始代码对应打包后的代码位置,一般可以不要的。在vue.config.js
文件中添加下面一句话:
//去掉mao文件
productionSourceMap:false
然后还可以添加一个方法——生成打包报告,来看看是那些文件占用的体积比较大。
对于一些外部组件库,可以使用CDN
(引入网络中的文件)的方式引入组件。
然后再重新npm run build
打包文件。
相关文章:

【项目】基于Vue2+Router+Vant 前端面经项目
环境配置 Vue脚手架的创建 在终端中打开输入 vue create 项目包名 -m npm注意⚠️:项目名称不再允许包含大写字母。 选择第三项 3.选择要安装的模块 从上到下的功能模块: Babel - ES:降级处理Router-Vue:路由插件CSS预处理器E…...

【论文阅读】YOLOv10: Real-Time End-to-End Object Detection
题目:YOLOv10: Real-Time End-to-End Object Detection 作者:Ao Wang Hui Chen∗ Lihao Liu Kai Chen Zijia Lin Jungong Han Guiguang Ding∗ 清华大学的 motivation: 作者觉得YOLO系列的NMS和某些结构非常的耗时,提出NMS-free和一些列高效…...
计算资源消耗
计算资源消耗 计算资源的消耗分成: 模型参数本身的存储。模型参数的梯度以及梯度momentum的存储。token的传播过程 例如以llama3-7b为例: 模型参数存储: 模型参数量 * fp32 例如llama3-70b为例,7 * 10^9 * 4 模型参数的梯度以…...
企业微信推送消息的Java实现教程
在这篇教程中,我们将介绍如何使用Java实现企业微信的消息推送功能,特别是在完成任务后,将结果信息通过企业微信推送给指定的用户。我们将基于您提供的代码进行说明。 1. 环境准备 1.1 依赖库 在开始编写代码之前,确保您的项目中…...

强化学习之Actor-Critic算法(基于值函数和策略的结合)——以CartPole环境为例
0.简介 DQN算法作为基于值函数的方法代表,基于值函数的方法只学习一个价值函数。REINFORCE算法作为基于策略的方法代表,基于策略的方法只学习一个策略函数。Actor-Critic算法则结合了两种学习方法,其本质是基于策略的方法,因为其目…...

Linux学习记录(五)-------三类读写函数
文章目录 三种读写函数1.行缓存2.无缓存3.全缓存4.fgets和fputs5.gets和puts 三种读写函数 1.行缓存 遇到新行(\n),或者写满缓存时,即调用系统函数 读:fgets,gets,printf,fprintf,sprintf写:fputs,puts,scanf 2.无缓…...
2024年8月13日(lvs NAT脚本 RS脚本 ds脚本)
lvs-nat模式的优点配置简单,缺点是请求和响应都必须经过ds,容易称为性能瓶颈 希望有这样的模式,请求的时候使用input链进行负载均衡,响应的时候就不要经过ds,直接由rs响应给客户端 在nat模式的时候,请求vip,接收vip的响应 构想 请求vip,接受rip响应,这是不允许lvs-dr模式 NAT脚…...

css实现水滴效果图
效果图: <template><div style"width: 100%;height:500px;padding:20px;"><div class"water"></div></div> </template> <script> export default {data() {return {};},watch: {},created() {},me…...

接口测试面试题目,你都会了吗?
面试题 什么是接口测试? 接口自动化测试的流程是什么? GET请求和POST请求区别是什么? 接口测试的常用工具有哪些? HTTP接口的请求参数类型有哪些? 如何从上一个接口获取相关的响应数据传递到下一个接口࿱…...

jmeter-beanshell学习16-自定义函数
之前写了一个从文件获取指定数据,用的时候发现不太好用,写了一大段,只能取出一个数,再想取另一个数,再粘一大段。太不好看了,就想到了函数。查了一下确实可以写。 public int test(a,b){return ab; } ctes…...

LogicFlow工作流在React和Vue3中的使用
LogicFlow 是一款流程图编辑框架,提供了一系列流程图交互、编辑所必需的功能和简单灵活的节点自定义、插件等拓展机制,方便我们快速在业务系统内满足类流程图的需求。 核心能力 可视化模型:通过 LogicFlow 提供的直观可视化界面,…...
Python循环语句:不到长城心不死
Python中的循环语句是编程中非常重要的结构,它们允许你重复执行一段代码多次,直到满足某个条件为止。Python提供了两种主要的循环类型:for循环和while循环。 文章目录 1. for 循环2. while 循环循环控制语句range() 函数结合循环语句和 rang…...

Unity教程(九)角色攻击的改进
Unity开发2D类银河恶魔城游戏学习笔记 Unity教程(零)Unity和VS的使用相关内容 Unity教程(一)开始学习状态机 Unity教程(二)角色移动的实现 Unity教程(三)角色跳跃的实现 Unity教程&…...

宠物空气净化器真的能除毛吗?有哪些选购技巧和品牌推荐修改版
夏日炎炎,有猫超甜。作为一名资深铲屎官,家里养有猫让我倍感幸福,夏天里有空调、有西瓜、有猫,这几个搭配在一起真的是超级爽。但在这么高温的夏天,家里养有宠物还是有不少烦恼的。比如家里的浮毛一直飘,似…...

Qt自定义注释
前言 是谁在Qt中编写代码,函数注释,类注释时,注释符号一个一个的敲? comment注释brief简洁的 Detailed详细的 第一步: 打开Qt 工具->选项->文本编辑器->片段 第二步: 点击添加 然后点击OK…...

【模电笔记】——信号的运算和处理电路(含电压比较器)
tips:本章节的笔记已经打包到word文档里啦,建议大家下载文章顶部资源(有时看不到是在审核中,等等就能下载了。手机端下载后里面的插图可能会乱,建议电脑下载,兼容性更好且易于观看),…...
Java之 equals()与==
目录 运算符用途:用于比较两个引用是否指向同一个对象。比较内容:比较的是内存地址(引用)。适用范围:适用于基本数据类型和对象引用 equals() 方法用途:用于比较两个对象的内容是否相同。比较内容…...

Ubuntu20.04 运行深蓝路径规划hw1
前言 环境: ubuntu 20.04 ; ROS版本: noetic; 问题 1、出现PCL报错:#error PCL requires C14 or above catkin_make 编译时,出现如下错误 解决: 在grid_path_searcher文件夹下面的CMakeLis…...

企业如何组建安全稳定的跨国通信网络
当企业在海外设有分公司时,如何建立一个安全且稳定的跨国通信网络是一个关键问题。为了确保跨国通信的安全和稳定性,可以考虑以下几种方案。 首先,可以在分公司之间搭建虚拟专用网络。虚拟专用网络通过对传输数据进行加密,保护通信…...

WordPress原创插件:Download-block-plugin下载按钮图标美化
WordPress原创插件:Download-block-plugin下载按钮图标美化 https://download.csdn.net/download/huayula/89632743...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...

最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
数据链路层的主要功能是什么
数据链路层(OSI模型第2层)的核心功能是在相邻网络节点(如交换机、主机)间提供可靠的数据帧传输服务,主要职责包括: 🔑 核心功能详解: 帧封装与解封装 封装: 将网络层下发…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...