提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
运用vue创建简单黑马头条的项目
vue create demo-toutiao
//prettier在setting.json的配置
"prettier.configPath": "D:\\VsCode Workplace\\配置文件\\.prettierrc.js",
"eslint.alwaysShowStatus": true,
"prettier.trailingComma": "none",
"prettier.semi": false, //每行文字个数超出此限制将会被迫换行
"prettier.printWidth": 300, //使用单引号替换双引号
"prettier.singleQuote": true,
"prettier.arrowParens": "avoid", //甚至在.vue文件中,HTML代码的格试化插件
"vetur.format.defaultFormatter.html": "js-beautify-html",
"vetur.ignoreProjectWarning": true,
"vetur.format.defaultFormatterOptions": {
"prettier": {
"trailingComma": "none",
"semi": false,
"singleQuote": true,
"arrowParens": "avoid",
"printWidth": 300
},
"js-beautify-html": {
"wrap attributes": false
}
},
-ESLint
//ESLint插件在setting.json的配置
"editor.codeActionsOnSave": {
"source.fixAll": true
},
home.vue
组件和user.vue
组件,项目的结构如下:main.js
中引入Vantimport Vue from 'vue'
import App from './App.vue'
import Vant from 'vant'
import 'vant/lib/index.css' //引入全部的vant组件,一般不推荐
Vue.config.productionTip = false
Vue.use(Vant) //使用Vant
new Vue({
render: h => h(App)
}).$mount('#app')
user.vue
组件中,声明如下的模板结构: <template>
<div class="user-container">
<div class="user-card">
<van-cell>
<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>0span>
<span>动态span>
div>
<div class="user-data-item">
<span>0span>
<span>关注span>
div>
<div class="user-data-item">
<span>0span>
<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>
home.vue
组件中,声明如下的模板结构<template>
<div><van-nav-bar title="黑马头条" fixed />div>
template>
<script>
export default {
name: 'home'
}
script>
<style lang="less" scoped>
.home-container {
padding: 46px 0 50px 0;//防止标题覆盖新闻内容
.van-nav-bar {
background-color: #007bff;//改变Navbar的默认样式,权重比自定义的主题大
/deep/.van-nav-bar__title { ///deep/深度选择器,能够作用得“更深”,影响子组件
color: #fff;
}
}
}
style>
App.vue
组件中,声明如下的模板结构<template>
<div>
<router-view>router-view>
<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>
<script>
export default {
name: 'App'
}
script>
<style lang="less" scoped>style>
module.exports = {
root: true,
env: {
node: true
},
extends: ['plugin:vue/essential', '@vue/standard'],
parserOptions: {
parser: '@babel/eslint-parser'
},
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
'space-before-function-paren': ['error', 'always'],
'vue/multi-word-component-names': ['error', { ignores: ['home', 'user'] }]
}
}
npm i [email protected]
index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入需要的组件
import Home from '@/views/Home/home.vue'
import User from '@/views/User/user.vue'
// 把 VueRouter 安装为 Vue 的插件
Vue.use(VueRouter)
// 路由规则的数组
const routes = [
// 定义首页的路由规则
{ path: '/', component: Home },
// 定义我的路由规则
{ path: '/user', component: User }
]
// 创建路由实例对象
const router = new VueRouter({
routes
})
export default router
main.js
引入routerimport Vue from 'vue'
import App from './App.vue'
import Vant from 'vant'
import router from './router'
import 'vant/lib/index.css' // 引入全部的vant组件,一般不推荐
Vue.config.productionTip = false
Vue.use(Vant) // 使用Vant
new Vue({
router,
render: h => h(App)
}).$mount('#app')
request.js
安装axios
在终端输入npm install axios
在src文件夹下新建utils文件夹,再在其下新建request.js
文件
import axios from 'axios'
// 调用 axios.create() 函数,创建一个 axios 的实例对象,用 request 来接收
const request = axios.create({
// 指定请求的根路径
baseURL: 'https://applet-base-api-t.itheima.net/'
})
export default request
articleAPI.js
articleAPI.js
文件// 文章相关的 API 接口,都封装到这个模块中
import request from '@/utils/request.js'
// 向外按需导出一个 API 函数
export const getArticleListAPI = function (_page, _limit) {
return request.get('/articles', {
// 请求参数
params: {
_page,
_limit
}
})
}
home.vue
<script>
// 按需导入 API 接口
import { getArticleListAPI } from '@/api/articleAPI.js'
export default {
name: 'home',
data() {
return {
// 页码值
page: 1,
// 每页显示多少条数据
limit: 10,
// 文章的数组
artlist: []
}
},
created() {
this.initArticleList()
},
methods: {
// 封装获取文章列表数据的方法
async initArticleList() {
// 发起 GET 请求,获取文章的列表数据
const { data: res } = await getArticleListAPI(this.page, this.limit)
console.log(res)
}
}
}
</script>
home.vue
的页面上 <template>
<div>
<van-cell>
<template #title>
<div class="title-box">
<span>{{ title }}span>
<img :src="cover.images[0]" alt="" class="thumb" v-if="cover.type === 1" />
div>
<div class="thumb-box" v-if="cover.type === 3">
<img :src="item" alt="" class="thumb" v-for="(item, i) in cover.images" :key="i" />
div>
template>
<template #label>
<div class="label-box">
<span>作者 {{ author }} {{ cmtCount }} 评论 发布日期 {{ time }}span>
<van-icon name="cross" />
div>
template>
van-cell>
div>
template>
<script>
export default {
name: 'ArticleInfo',
// 自定义属性
props: {
// 文章的标题
title: {
type: String,
default: ''
},
// 作者名字
author: {
type: String,
default: ''
},
// 评论数
cmtCount: {
// 通过数组形式,为当前属性定义多个可能的类型
type: [Number, String],
default: 0
},
// 发布日期
time: {
type: String,
default: ''
},
// 封面的信息对象
cover: {
type: Object,
// 通过 default 函数,返回 cover 属性的默认值
default: function() {
// 这个 return 的对象就是 cover 属性的默认值
return { type: 0 }
}
}
}
}
script>
<style lang="less" scoped>
.label-box {
display: flex;
justify-content: space-between;
align-items: center;
}
.thumb {
// 矩形黄金比例:0.618
width: 113px;
height: 70px;
background-color: #f8f8f8;
object-fit: cover;
}
.title-box {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
.thumb-box {
display: flex;
justify-content: space-between;
}
style>
home.vue
上使用ArticleInfo
组件,并向ArticleInfo.vue
传输数据<template>
<div class="home-container">
<van-nav-bar title="黑马头条" fixed />
<ArticleInfo v-for="item in artlist" :key="item.id" :title="item.title" :author="item.aut_name" :cmt-count="item.comm_count" :time="item.pubdate" :cover="item.cover">ArticleInfo>
div>
template>
<script>
// 按需导入 API 接口
import { getArticleListAPI } from '@/api/articleAPI.js'
// 导入需要的组件
import ArticleInfo from '@/components/Article/ArticleInfo.vue'
export default {
name: 'home',
components: { ArticleInfo },
data() {
return {
// 页码值
page: 1,
// 每页显示多少条数据
limit: 10,
// 文章的数组
artlist: []
}
},
created() {
this.initArticleList()
},
methods: {
// 封装获取文章列表数据的方法
async initArticleList() {
// 发起 GET 请求,获取文章的列表数据
const { data: res } = await getArticleListAPI(this.page, this.limit)
console.log(res)
this.artlist = res
}
}
}
script>
<style lang="less" scoped>
.home-container {
padding: 46px 0 50px 0;
.van-nav-bar {
background-color: #007bff;
/deep/.van-nav-bar__title {
color: #fff;
}
}
}
style>
<template>
<div class="home-container">
<van-nav-bar title="黑马头条" fixed />
<van-pull-refresh v-model="isLoading" :disabled="finished" @refresh="onRefresh">
<van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
<ArticleInfo v-for="item in artlist" :key="item.id" :title="item.title" :author="item.aut_name" :cmt-count="item.comm_count" :time="item.pubdate" :cover="item.cover">ArticleInfo>
van-list>
van-pull-refresh>
div>
template>
<script>
// 按需导入 API 接口
import { getArticleListAPI } from '@/api/articleAPI.js'
// 导入需要的组件
import ArticleInfo from '@/components/Article/ArticleInfo.vue'
export default {
name: 'home',
components: { ArticleInfo },
data() {
return {
// 页码值
page: 1,
// 每页显示多少条数据
limit: 10,
// 文章的数组
artlist: [],
// 是否正在加载下一页数据,如果 loading 为 true,则不会反复触发 load 事件
// 每当下一页数据请求回来之后,千万要记得,把 loading 从 true 改为 false
loading: true,
// 所有数据是否加载完毕了,如果没有更多数据了,一定要把 finished 改成 true
finished: false,
// 是否正在下拉刷新
isLoading: false
}
},
created() {
this.initArticleList()
},
methods: {
// 封装获取文章列表数据的方法
async initArticleList(isRefresh) {
// 发起 GET 请求,获取文章的列表数据
const { data: res } = await getArticleListAPI(this.page, this.limit)
if (isRefresh) {
// 证明是下拉刷新;新数据在前,旧数据在后
// this.artlist = [新数据在后, 旧数据在前]
this.artlist = [...res, ...this.artlist]
this.isLoading = false
} else {
// 证明是上拉加载更多;旧数据在前,新数据在后
// this.artlist = [旧数据在前, 新数据在后]
this.artlist = [...this.artlist, ...res]
this.loading = false
}
if (res.length === 0) {
// 证明没有下一页数据了,直接把 finished 改为 true,表示数据加载完了!
this.finished = true
}
},
// 只要 onLoad 被调用,就应该请求下一页数据
onLoad() {
console.log('触发了 load 事件!')
// 1. 让页码值 +1
this.page++
// 2. 重新请求接口获取数据
this.initArticleList()
},
// 下拉刷新的处理函数
onRefresh() {
console.log('触发了下拉刷新!')
// 1. 让页码值 +1
this.page++
// 2. 重新请求接口获取数据
this.initArticleList(true)
}
}
}
script>
<style lang="less" scoped>
.home-container {
padding: 46px 0 50px 0;
.van-nav-bar {
background-color: #007bff;
/deep/.van-nav-bar__title {
color: #fff;
}
}
}
style>
修改默认样式
demo-toutiao