一、目标:
目的:自己做记录,记录制作过程以及遇到的问题。内容来自b站的up主:黑马程序员(up主名字)的黑马程序员Vue全套视频教程,从vue2.0到vue3.0一套全覆盖,前端学习....(视频名称部分)。
效果展示:
二、开始制作
(一)、初始化操作:
(1)创建项目
1. 打开一个文件夹,在所处文件夹中打开终端(将鼠标放在文件夹名字那一栏的空白处(如图所示),点击一下,输入cmd回车,再按回车打开终端)
2. 在终端中输入:vue create toutiao,开始进行项目的创建,选择如下选项(多选选项:点击空格添加“*”号是选择,回车进入下一步,通过下上键进行选项切换)
History路由需要和后端配合使用,兼容性较差。最后一个的意思是存不存为一个预设,存不存都无所谓。等它加载成(如图)这样就是创建好了。然后关闭终端,用vscode打开我们创建的文件。在vscode中打开终端。
3. 图上这两个文件夹的区别:它们两个都是放组件的,如果某个组件是通过路由来进行动态切换的,这种组件放在views里面;如果不是通过路由来切换的,是一个可复用的组件,放在components里面
因为它会自动生成代码,打开app.vue,ctrl+A全选删除。Views文件夹中自动生成了两个组件,不需要,直接右键删除,components下面的helloworld也不需要,删除。将router文件夹里面的index.js里routes的数组清空,然后把导入的home组件也删除
更改以后的index.js:
import Vue from 'vue'
import VueRouter from 'vue-router'
// 把VueRoter安装为Vue的插件
Vue.use(VueRouter)
// 路由规则的数组
const routes = []
// 创建路由实例对象
const router = new VueRouter({
routes
})
export default router
打开终端,npm run serve,运行,按住ctrl点击local对应的网址,打开网页,F12进入,以移动设备展示
4. 安装和配置vant组件:Vant 2 - 轻量、可靠的移动端组件库 (gitee.io) (如果直接在浏览器输入的,要记得选择适用于vue2的那个)。它提供了很多组件,很适合移动端的开发
4.1 进行安装
选择命令进行安装。点击加号新建一个终端,在新的终端里面复制命令(鼠标右键即可在终端中复制)。如果出现了以下情况的报错:
输入 npm i vant@latest-v2 -S --legacy-peer-deps (legacy的意思:遗产/(软件或硬件)已过时但因使用范围广而难以替代的;而npm install xxxx --legacy-peer-deps命令用于绕过peerDependency里依赖的自动安装;它告诉npm忽略项目中引入的各个依赖模块之间依赖相同但版本不同的问题,以npm v3-v6的方式去继续执行安装操作。所以其实该命令并没有真的解决冲突,而是忽略了冲突,以“过时”(v3-v6)的方式进行下载操作。)
4.2 进行配置 安装了之后继续看文档,看如何进行配置。
因为组件很多,但不是每一个我们都需要,如果我们都打包体积就会很大,出于性能上考虑,方法一更好。但由于方法一比较麻烦复杂,所以在开发阶段,由于不需要考虑体积的问题,我们通常采用方法三。在发布阶段,可以直接把vant抽出去(将vant优化掉)。
注意:在同一个文件中,不要重复导入,复制没有的粘贴即可(import必须放在头部)
更改之后的main.js:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
// 导入并安装Vant组件库
import Vant from 'vant';
import 'vant/lib/index.css';
Vue.use(Vant);
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
5. 实现底部Tabbar的切换:
要实现这两个组件的切换,首先我们需要这两个组件。所以(1)创建两个组件的页面,那这两个组件我们应该放在哪个目录(components或views)下呢?放在views下,因为这两个页面都是通过路由来加载的。所以在views文件夹下新建User文件夹(在文件夹里面创建User.vue)和Home文件夹(在文件夹里面创建Home.vue),注意等级关系,不要放错位置将两个的基础部分都写好。
(2)组件页面写好了,来到vant寻找合适的组件,在“导航组件”里面有个“Tabbar标签栏”。思考:假设我们要使用它,应该将它放在何处展示?放在根组件的底部,点击即切换,底下我们可以认为是两个路由链接,它的上面就是一个路由占位符,下面就是vant的组件。打开app根组件。
v-model:双向数据绑定指令。此处我们不需要使用v-model="active",保存查看效果
删掉保留两个,切换第二个的图标(如何换图标?点开vant文档,点击基础组件,点开icon图标,找到自己喜欢的,将图标名字替换成自己挑的那个)
(选择的图标)
(更改后的)
此时点击它们两个还不会切换,所以我们要想办法把底下两个改造成路由链接进行跳转。(3)来到vant文档的Tabbar标签栏,来到“路由模式”
它是给容器加route属性,里面每一个可点的项目加“to”属性,指向我们要跳转的hash地址。
首页就是“/”。打开页面,点击两个图标,看见头部的文件地址会随之切换。
注意:此时底部不会高亮,之后加上路由规则就可以高亮了,试过了配置了之后就可以高亮了。
将占位符写上:
<template>
<div>
<!-- 路由占位符 -->
<router-view></router-view>
<!-- Tabbar区域 -->
<van-tabbar route>
<van-tabbar-item icon="home-o" to="/">首页</van-tabbar-item>
<van-tabbar-item icon="user-o" to="/user">我的</van-tabbar-item>
</van-tabbar>
</div>
</template>
6. 来到index.js。进行如下配置:
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入需要的组件
import Home from '@/views/Home/Home.vue'
import User from '@/views/Users/User.vue'
// 把VueRoter安装为Vue的插件
Vue.use(VueRouter)
// 路由规则的数组
const routes = [
// 定义首页的路由规则
{ path:'/',component:Home },
// 定义我的路由规则
{ path:'/user',component:User }
]
// 创建路由实例对象
const router = new VueRouter({
routes
})
export default router
7. 制作“我的”页面
这个页面只设计,不做功能。在User.vue组件中,声明如下内容:
<template>
<div class="user-container">
<!-- 用户基本信息面板 -->
<div class="user-card">
<!-- 用户头像、姓名 -->
<van-cell>
<!-- 使用 title 插槽来自定义标题 -->
<template #icon>
<img src="../../assets/logo.png" alt="" class="avatar" />
</template>
<template #title>
<span class="username">用户名</span>
</template>
<template #label>
<van-tag color="#fff" text-color="#007bff">申请认证</van-tag>
</template>
</van-cell>
<!-- 动态、关注、粉丝 -->
<div class="user-data">
<div class="user-data-item">
<span>0</span>
<span>动态</span>
</div>
<div class="user-data-item">
<span>0</span>
<span>关注</span>
</div>
<div class="user-data-item">
<span>0</span>
<span>粉丝</span>
</div>
</div>
</div>
<!-- 操作面板 -->
<van-cell-group class="action-card">
<van-cell icon="edit" title="编辑资料" is-link />
<van-cell icon="chat-o" title="小思同学" is-link />
<van-cell icon="warning-o" title="退出登录" is-link />
</van-cell-group>
</div>
</template>
<script>
export default {
name: 'User'
}
</script>
<style lang="less" scoped>
.user-container {
.user-card {
background-color: #007bff;
color: white;
padding-top: 20px;
.van-cell {
background: #007bff;
color: white;
&::after {
display: none;
}
.avatar {
width: 60px;
height: 60px;
background-color: #fff;
border-radius: 50%;
margin-right: 10px;
}
.username {
font-size: 14px;
font-weight: bold;
}
}
}
.user-data {
display: flex;
justify-content: space-evenly;
align-items: center;
font-size: 14px;
padding: 30px 0;
.user-data-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 33.33%;
}
}
}
</style>
(二)实现首页
1. 开始实现首页。(1)首先实现头部,①来到vant文档,找到导航组件的Navbar导航栏,复制粘贴,保留自己需要的部分。放在home组件下。留下标题后,我们要注意,此时如果内容过多,在滚动时标题也会随之消失,如何让标题一直留在头部?
点开vant文档,继续往下翻,来到“api”,看到如下:
更改为如下:(因为是布尔值,所以需要加“:”进行属性绑定)
(简写形式)
因为在使用vant组件的时候,如果某个组件的某个属性默认值是false,想要改成true,只需要直接写属性名称即可。
输入一点内容,如下图:
我们可以看到,第一行的内容被遮住了,为什么呢?因为标题固定定位了,脱离了标准文档流,所以覆盖掉了。同样,当内容过长时,底部的内容也会被遮盖住,因为底部导航栏也是固定定位。如何解决?给home组件的盒子加个样式,打开我们编写的页面测量出上面标题栏高度为46,下面导航栏是50,使用样式如图(此处的46与50是通过检查盒子的大小得到的)
②完善样式:点开我们写的页面,检查代码,点击头部区域,找到背景颜色所在的位置,复制样式的名称:.van-nav-bar,放在home组件中。找到题目文字,改他的颜色,发现直接改不好使,在前面加/deep/,就可以了。为什么呢?因为加上deep之后她就不只是类名[ ]选择器了,而是变成后代选择器
(加上deep之后变成这样)
(改标题)
注意:这种改法有一个缺点,就是只改了当前页面的样式,假设我们还有一个页面也用到了这个头部组件,在那个页面他的颜色并不会改变。(vant有定制主题,可以使用定制主题进行统一更改)
(1)文章列表功能
(cover是文章的封面)
1. 用axios调接口,拿数据。在终端装包,npm i axios -S. 装完包之后,就要开始配置,先在main.js里面导入axios
1.1 在src文件夹下创建文件夹utils(代表工具,存放工具模块),在里面封装一个request.js(因为我们要调接口,所以先找到我们的小axios,因此先创建一个request模块,这个模块就是一个工具模块)。
request.js代码:
import axios from 'axios'
const requset = axios.create({
// 指定请求的根路径
baseURL: 'https://applet-base-api-t.itheima.net'
})
export default requset
1.2 在home组件里面一进来就要发请求,拿数据。所以在home组件里面封装一个methods
Home.vue(script部分)代码:
<script>
import request from '@/utils/request';
export default {
name:'Home',
data(){
return {
page:1,
limit:10
}
},
created(){
this.initArticleList()
},
methods:{
async initArticleList(){
const { data:res } =await request.get('/articles',{
params:{
_page:this.page,
_limit:this.limit
}
})
console.log(res);
}
}
}
</script>
图为控制台(拿到的数据)展示内容:
1.3 封装articleAPI模块。假如我们希望一点开“我的”便获取到数据,应该怎么办呢?先点开user.vue,在里面封装方法,步骤跟上面一样。但是这样写完了代码会重复,我们希望能够复用,应该怎么做呢?为什么加await,因为调用的返回值是params,将来我们如果能拿到一个params的实例对象,就可以了。放哪去呢?
所以怎么把代码抽走呢?在src目录下,新建接口api文件夹,创建获取文件数据的api(articleAPI.js)
(在App.vue里)(得到的结果)
传什么值,就请求谁的数据。所以在其他组件导入使用它时,需要传参
articleAPI.js:
将Home.vue里面改成如下:
这样,如果我们要调接口获取用户的信息,新建api专门用来获取用户的。首先要导入request,向外导出一个方法,需要传参就用params,不需要传参直接get,将结果return出去。
1.4 渲染列表的数据,封装ArticleInfo组件。ArticleInfo组件放在components文件夹下,在components文件夹下创建一个Article文件夹,在Article文件夹下存放ArticleInfo组件
(ArticleInfo.vue里面)
如果某一个数据要渲染到页面上使用,要把数据放在data里面。此处,res是数据
此时Home.vue的代码:
<template>
<!-- <h1>Home组件</h1> -->
<div class="home-container">
<van-nav-bar title="黑马头条" fixed/>
<p>{
{ artlist.length }}</p>
<ArticleInfo v-for="item in artlist" :key="item.id"></ArticleInfo>
</div>
</template>
<script>
import { getArticleListAPI } from '@/api/articleAPI.js';
import ArticleInfo from '@/components/Article/ArticleInfo.vue'
export default {
name:'Home',
data(){
return {
page:1,
limit:10,
// 文章的数组
artlist:[]
}
},
created(){
this.initArticleList()
},
methods:{
async initArticleList(){
const { data:res } =await getArticleListAPI(this.page,this.limit)
// console.log(res);
this.artlist=res
}
},
components:{
ArticleInfo
}
}
</script>
<style lang="less" scoped>
.home-container {
padding: 46px 0 50px 0;
.van-nav-bar {
background-color: #007bf0;
}
/deep/ .van-nav-bar__title {
color: #fff;
}
}
</style>
打开页面显示显示的效果:
1.4.1 在 ‘ArticleInfo.vue’组件中,声明如下的模板结构:
<template>
<div>
<van-cell>
<!-- 标题区域的插槽 -->
<template #title>
<div class="title-box">
<!-- 标题 -->
<span>文章的标题噢</span>
<!-- 单张图片 -->
<img src="https://www.escook.cn/vuebase/pics/1.png" alt="" class="thumb">
</div>
<!-- 三张图片 -->
<div class="thumb-box">
<img src="https://www.escook.cn/vuebase/pics/2.png" alt="" class="thumb">
<img src="https://www.escook.cn/vuebase/pics/2.png" alt="" class="thumb">
<img src="https://www.escook.cn/vuebase/pics/2.png" alt="" class="thumb">
</div>
</template>
<!-- label 区域的插槽 -->
<template #label>
<div class="label-box">
<span>作者 0评论 发布日期</span>
<!-- 关闭按钮 -->
<van-icon name="cross" />
</div>
</template>
</van-cell>
</div>
</template>
<script>
export default {
name:'ArticleInfo',
}
</script>
<style lang="less" scoped>
.label-box {
display: flex;
justify-content: space-between;
align-items: center;
font-size: medium;
}
.thumb {
// 矩形黄金比例:0.618
width: 150x;
height: 90px;
background-color: #f8f8f8;
object-fit: cover;
}
.title-box {
display: flex;
justify-content: space-between;
align-items: flex-start;
font-size: large;
}
.thumb-box {
display: flex;
justify-content: space-between;
}
</style>
将home组件里面刚刚p标签注释掉,页面展示效果如下:
此时内容都是写死的,所以需要我们自定义属性
(自定义属性)(运用差值表达式)
(效果展示图)
此时会没有内容先显示,所以应该在使用文章组件的时候传一个title进去(:title【属性名叫title】,值等于item项.标题,查看上面的表)。
(home.vue)(效果展示)
然后就一个一个的传过去
为ArticleInfo组件封装cover属性。
因为有一张图片的和三张图片的,所以需要v-if的判断,由于他们两个不是平级,所以不能用v-else
此时图片地址还是写死的,用cover.image
一张图片的索引为0,三张的采用循环,key后面绑定的值无意义(直接拿索引当做key)
(自定义属性里面)
(home组件里面)
(2)上拉加载更多功能
- 1.了解list组件。打开vant文档,找到list列表。我们在这里是希望ArticleInfo上拉加载更多
- loading=true,代表当前正在请求下一页数据,防止用户多次请求(比如反复上拉加载),只有为false时才会触发loading事件。因为loading为true的时候,不会反复触发load事件
- 先将它添加进代码(home组件中)
(写在方法methods里)
(效果图)
但是一进来就会触发一次,这并不是我们想要的效果。因为一进来我们会触发created生命周期,在created中调用了函数拿到了第一页的数据。将loading值改为true就不会触发了,但是这样到底部本来应该触发也没有触发,所以应该在第一页数据请求回来以后改成false
让它请求新数据
同时,当前是把最新的数据覆盖掉上一页的数据,这样不对,应该在覆盖之前先进行新旧数据的拼接。
使用push不行,用…;如果把两个数组合成一个大数组,用…
当res等于空数组,证明没有下一页了,此时将finish改为true,同时解决了刚才一过快就会卡死的现象
当前Home.vue的(script部分)代码:
<script>
import { getArticleListAPI } from '@/api/articleAPI.js';
import ArticleInfo from '@/components/Article/ArticleInfo.vue'
export default {
name:'Home',
data(){
return {
page:1,
limit:10,
// 文章的数组
artlist:[],
loading:true,
finished:false
}
},
created(){
this.initArticleList()
},
methods:{
async initArticleList(){
const { data:res } =await getArticleListAPI(this.page,this.limit)
// console.log(res);
this.artlist=[...this.artlist,...res]
this.loading=false
if(res.length===0){
this.finished=true
}
},
onLoad(){
console.log('触发了load事件');
this.page++
this.initArticleList()
}
},
components:{
ArticleInfo
}
}
</script>
(3)下拉刷新功能
1. 同样在vant文档的list组件中,将其包裹在外面,
(写在method里面)
此时它会一直显示“加载中”这样。只要我们下拉刷新一松手就应该发起请求去拿数据,当数据拿回来以后,我们应该把refreshing重置成false
注意:此时是全部都是尾部拼接,这样就只能实现上拉加载更多,所以我们应该进行判断。加个布尔值isRefresh。什么时候会传一个true进来,设置下拉的时候会传一个true;如果什么都不传,就是undefined。给下拉刷新传一个true进来,上拉加载没有传true,默认为undefined
当前Home.vue的(script部分)代码:
<script>
import { getArticleListAPI } from '@/api/articleAPI.js';
import ArticleInfo from '@/components/Article/ArticleInfo.vue'
export default {
name:'Home',
data(){
return {
page:1,
limit:10,
// 文章的数组
artlist:[],
loading:true,
finished:false,
refreshing:false
}
},
created(){
this.initArticleList()
},
methods:{
async initArticleList(isRefresh){
const { data:res } =await getArticleListAPI(this.page,this.limit)
if(isRefresh){
this.artlist=[...res,...this.artlist]
this.refreshing=false
}else{
this.artlist=[...this.artlist,...res]
this.loading=false
}
if(res.length===0){
this.finished=true
}
},
onLoad(){
console.log('触发了load事件');
this.page++
this.initArticleList()
},
onRefresh(){
console.log('触发了下拉刷新事件');
this.page++
this.initArticleList(true)
}
},
components:{
ArticleInfo
}
}
</script>
这样就能实现了下拉刷新了。同时,当它全部刷新出来没有数据之后,我们应该把下拉刷新禁用掉。来到vant文档,找到pullrefresh,查看是否有禁用:
完成啦!!