1.项目案例-头条新闻
1.1.数据接口
聚合数据:https://www.juhe.cn/
申请 “新闻头条” 数据接口(不可直接跨域访问)
注意:按照聚合数据网站的规定,进行注册以及实名认证,然后申请 “新闻头条” 数据接口。
1.2.工程目录结构
1.3.package.json文件
{
"name": "topnews",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build"
},
"dependencies": {
"axios": "^0.26.1",
"core-js": "^3.8.3",
"vue": "^3.2.13",
"vue-router": "^4.0.3"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-router": "~5.0.0",
"@vue/cli-service": "~5.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}
1.4.vue.config.js而文件
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 8080,
proxy: {
'/juheNews': {
target: 'http://v.juhe.cn/toutiao/index', //需要跨域的url
ws: true, //代理webSocket
changeOrigin: true, //允许跨域
pathRewrite: {
'^/juheNews': '' //重写路径
}
}
}
}
})
1.5.main.js文件
main.js文件没有改变
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
1.6.路由配置文件
import { createRouter, createWebHashHistory } from 'vue-router'
import TopNews from '../views/TopNews.vue'
import TypeNews from '../views/TypeNews.vue'
const routes = [{
path: '/',
name: 'Home',
component: TopNews
}, {
path: '/topNews',
name: 'TopNews',
component: TopNews
}, {
path: '/typeNews',
name: 'TypeNews',
component: TypeNews
}]
const router = createRouter({
history: createWebHashHistory(),
routes
})
export default router
1.7.Vue文件
1.7.1.App.vue文件
<template>
<div id="app">
<header>新闻头条</header>
<nav>
<ul>
<li
:class="{ navinit: isActive == 'topNews' }"
@click="changeNav('topNews')"
>
头条新闻
</li>
<li
:class="{ navinit: isActive == 'typeNews' }"
@click="changeNav('typeNews')"
>
分类新闻
</li>
</ul>
</nav>
<router-view />
</div>
</template>
<script>
import { reactive, toRefs } from "vue";
import { useRouter } from "vue-router";
export default {
setup() {
//初始化路由组件
const router = useRouter();
const state = reactive({
isActive: "topNews",
});
const path = location.href.substring(location.href.lastIndexOf("/") + 1);
state.isActive = path == "" ? "topNews" : path;
function changeNav(param) {
state.isActive = param;
if (param == "topNews") {
router.push("/topNews");
} else if (param == "typeNews") {
router.push("/typeNews");
}
}
return {
...toRefs(state),
changeNav,
};
},
};
</script>
<style>
/******************** css reset ********************/
html,
body,
div,
header,
nav,
h1,
h2,
h3,
h4,
h5,
h6,
ul,
li {
margin: 0;
padding: 0;
font-family: "微软雅黑";
}
ul {
list-style: none;
}
a {
text-decoration: none;
}
header {
width: 100%;
height: 48px;
background-color: #e03d3e;
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
color: #fff;
/*设置字间距*/
letter-spacing: 4px;
}
nav {
width: 100%;
height: 56px;
display: flex;
justify-content: center;
align-items: center;
}
nav ul {
width: 160px;
height: 26px;
display: flex;
justify-content: space-between;
}
nav ul li {
width: 70px;
height: 26px;
display: flex;
justify-content: center;
align-items: center;
}
.navinit {
color: #e03d3e;
border-bottom: solid 2px #e03d3e;
}
</style>
1.7.2.NewsList.vue共通组件
<template>
<div>
<ul>
<li
v-for="item in newsArr"
@click="toNews(item.url)"
:key="item.uniquekey"
>
<div class="img-box">
<img :src="item.thumbnail_pic_s" />
</div>
<div class="text-box">
<h3>{{ item.title }}</h3>
<p>{{ item.author_name }} {{ item.date }}</p>
</div>
</li>
</ul>
</div>
</template>
<script>
import { reactive, toRefs, computed } from "vue";
export default {
props: {
data: Array,
},
setup(props) {
const state = reactive({
//将父组件传过来的数据中的title进行在加工处理
newsArr: computed(() => {
let arr = props.data;
for (let i = 0; i < arr.length; i++) {
if (arr[i].title.length > 24) {
arr[i].title = arr[i].title.substr(0, 24) + "...";
}
}
return arr;
}),
});
function toNews(url) {
location.href = url;
}
return {
...toRefs(state),
toNews,
};
},
};
</script>
<style scoped>
ul {
width: 100%;
}
ul li {
box-sizing: border-box;
padding: 6px;
width: 100%;
height: 93px;
display: flex;
border-bottom: dashed 1px #aaa;
user-select: none;
cursor: pointer;
}
ul li .img-box {
flex: 0 0 100px;
height: 80px;
}
ul li .img-box img {
width: 100px;
height: 80px;
}
ul li .text-box {
flex: 1;
box-sizing: border-box;
padding-left: 10px;
}
ul li .text-box h3 {
font-size: 16px;
font-weight: 300;
}
ul li .text-box p {
font-size: 14px;
text-align: right;
}
</style>
1.7.3.TopNews.vue组件
<template>
<div>
<img src="../assets/logo.png" />
<NewsList :data="newsList"></NewsList>
</div>
</template>
<script>
import { reactive, toRefs } from "vue";
import NewsList from "../components/NewsList";
import axios from "axios";
export default {
setup() {
const state = reactive({
newsList: [],
});
const init = () => {
axios
.get("/juheNews/toutiao/index", {
params: {
type: "top",
key: "655962c80fdaccf03709b567da3bc795",
},
})
.then((response) => {
state.newsList = response.data.result.data;
})
.catch((error) => {
console.log(error);
});
};
init();
return {
...toRefs(state),
};
},
components: {
NewsList,
},
};
</script>
<style scoped>
img {
width: 100%;
height: 100px;
display: block;
}
</style>
1.7.4.TypeNews组件
<template>
<div>
<div class="news-img">
<a :href="news.url">
<img :src="news.thumbnail_pic_s" />
</a>
</div>
<div class="type-news">
<ul>
<li
v-for="item in typeList"
:key="item.id"
:class="{ typeinit: isAlive == item.id }"
@click="change(item.id)"
>
{{ item.name }}
</li>
</ul>
</div>
<NewsList :data="newsList"></NewsList>
</div>
</template>
<script>
import { reactive, toRefs } from "vue";
import NewsList from "../components/NewsList";
import axios from "axios";
export default {
setup() {
const state = reactive({
typeList: [
{ id: "guonei", name: "国内" },
{ id: "guoji", name: "国际" },
{ id: "yule", name: "娱乐" },
{ id: "tiyu", name: "体育" },
{ id: "junshi", name: "军事" },
{ id: "keji", name: "科技" },
{ id: "caijing", name: "财经" },
{ id: "youxi", name: "游戏" },
{ id: "qiche", name: "汽车" },
{ id: "jiankang", name: "健康" },
],
isAlive: "guonei",
newsList: [],
news: {},
});
getNews("guonei");
function getNews(type) {
axios
.get("/juheNews/toutiao/index", {
params: {
type: type,
key: "655962c80fdaccf03709b567da3bc795",
},
})
.then((response) => {
state.newsList = response.data.result.data;
state.news =
state.newsList[Math.floor(Math.random() * state.newsList.length)];
})
.catch((error) => {
console.log(error);
});
}
function change(id) {
state.isAlive = id;
getNews(id);
}
return {
...toRefs(state),
change,
};
},
components: {
NewsList,
},
};
</script>
<style scoped>
.news-img img {
width: 100%;
height: 200px;
display: block;
}
.type-news {
width: 100%;
margin-top: 8px;
}
.type-news ul {
width: 100%;
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.type-news ul li {
box-sizing: border-box;
width: 48px;
height: 22px;
border: solid 1px #e03d3e;
border-radius: 11px;
margin: 5px 10px;
font-size: 14px;
color: #e03d3e;
display: flex;
justify-content: center;
align-items: center;
}
.typeinit {
background-color: #e03d3e;
color: #fff !important; /*!important:将优先级提升最高*/
}
</style>
1.8.Vue文件(setup语法糖方式)
下面使用setup语法糖的形式,重写这四个组件。
1.8.1.App.vue文件
<template>
<div id="app">
<header>新闻头条</header>
<nav>
<ul>
<li
:class="{ navinit: isActive == 'topNews' }"
@click="changeNav('topNews')"
>
头条新闻
</li>
<li
:class="{ navinit: isActive == 'typeNews' }"
@click="changeNav('typeNews')"
>
分类新闻
</li>
</ul>
</nav>
<router-view />
</div>
</template>
<script setup>
import { ref } from "vue";
import { useRouter } from "vue-router";
const router = useRouter();
const isActive = ref("topNews");
const path = location.href.substring(location.href.lastIndexOf("/") + 1);
isActive.value = path == "" ? "topNews" : path;
const changeNav = (param) => {
isActive.value = param;
if (param == "topNews") {
router.push("/topNews");
} else if (param == "typeNews") {
router.push("/typeNews");
}
};
</script>
<style>
/******************** css reset ********************/
html,
body,
div,
header,
nav,
h1,
h2,
h3,
h4,
h5,
h6,
ul,
li {
margin: 0;
padding: 0;
font-family: "微软雅黑";
}
ul {
list-style: none;
}
a {
text-decoration: none;
}
header {
width: 100%;
height: 48px;
background-color: #e03d3e;
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
color: #fff;
/*设置字间距*/
letter-spacing: 4px;
}
nav {
width: 100%;
height: 56px;
display: flex;
justify-content: center;
align-items: center;
}
nav ul {
width: 160px;
height: 26px;
display: flex;
justify-content: space-between;
}
nav ul li {
width: 70px;
height: 26px;
display: flex;
justify-content: center;
align-items: center;
}
.navinit {
color: #e03d3e;
border-bottom: solid 2px #e03d3e;
}
</style>
1.8.2.NewsList.vue共通组件
<template>
<div>
<ul>
<li
v-for="item in newsArr"
@click="toNews(item.url)"
:key="item.uniquekey"
>
<div class="img-box">
<img :src="item.thumbnail_pic_s" />
</div>
<div class="text-box">
<h3>{{ item.title }}</h3>
<p>{{ item.author_name }} {{ item.date }}</p>
</div>
</li>
</ul>
</div>
</template>
<script setup>
import { reactive, computed } from "vue";
const myProps = defineProps({
data: Array,
});
const newsArr = reactive(
computed(() => {
let arr = myProps.data;
for (let i = 0; i < arr.length; i++) {
if (arr[i].title.length > 24) {
arr[i].title = arr[i].title.substr(0, 24) + "...";
}
}
return arr;
})
);
const toNews = (url) => {
location.href = url;
}
</script>
<style scoped>
ul {
width: 100%;
}
ul li {
box-sizing: border-box;
padding: 6px;
width: 100%;
height: 93px;
display: flex;
border-bottom: dashed 1px #aaa;
user-select: none;
cursor: pointer;
}
ul li .img-box {
flex: 0 0 100px;
height: 80px;
}
ul li .img-box img {
width: 100px;
height: 80px;
}
ul li .text-box {
flex: 1;
box-sizing: border-box;
padding-left: 10px;
}
ul li .text-box h3 {
font-size: 16px;
font-weight: 300;
}
ul li .text-box p {
font-size: 14px;
text-align: right;
}
</style>
1.8.3.TopNews.vue组件
<template>
<div>
<img src="../assets/logo.png" />
<NewsList :data="newsList"></NewsList>
</div>
</template>
<script setup>
import { ref } from "vue";
import NewsList from "../components/NewsList";
import axios from "axios";
const newsList = ref([]);
axios
.get("/juheNews?type=top&key=655962c80fdaccf03709b567da3bc795")
.then((response) => {
newsList.value = response.data.result.data;
})
.catch((error) => {
console.log(error);
});
</script>
<style scoped>
img {
width: 100%;
height: 100px;
display: block;
}
</style>
1.8.4.TypeNews组件
<template>
<div>
<div class="news-img">
<a :href="news.url">
<img :src="news.thumbnail_pic_s" />
</a>
</div>
<div class="type-news">
<ul>
<li
v-for="item in typeList"
:key="item.id"
:class="{ typeinit: isAlive == item.id }"
@click="change(item.id)"
>
{{ item.name }}
</li>
</ul>
</div>
<NewsList :data="newsList"></NewsList>
</div>
</template>
<script setup>
import { reactive, ref } from "vue";
import NewsList from "../components/NewsList";
import axios from "axios";
const typeList = [
{ id: "guonei", name: "国内" },
{ id: "guoji", name: "国际" },
{ id: "yule", name: "娱乐" },
{ id: "tiyu", name: "体育" },
{ id: "junshi", name: "军事" },
{ id: "keji", name: "科技" },
{ id: "caijing", name: "财经" },
{ id: "youxi", name: "游戏" },
{ id: "qiche", name: "汽车" },
{ id: "jiankang", name: "健康" },
];
const isAlive = ref("guonei");
const newsList = ref([]);
const news = ref({});
const getNews = (type) => {
axios
.get("/juheNews", {
params: {
type: type,
key: "655962c80fdaccf03709b567da3bc795",
},
})
.then((response) => {
newsList.value = response.data.result.data;
news.value = newsList.value[Math.floor(Math.random() * newsList.value.length)];
})
.catch((error) => {
console.log(error);
});
}
getNews("guonei");
const change = (id) => {
isAlive.value = id;
getNews(id);
}
</script>
<style scoped>
.news-img img {
width: 100%;
height: 200px;
display: block;
}
.type-news {
width: 100%;
margin-top: 8px;
}
.type-news ul {
width: 100%;
display: flex;
justify-content: center;
flex-wrap: wrap;
}
.type-news ul li {
box-sizing: border-box;
width: 48px;
height: 22px;
border: solid 1px #e03d3e;
border-radius: 11px;
margin: 5px 10px;
font-size: 14px;
color: #e03d3e;
display: flex;
justify-content: center;
align-items: center;
}
.typeinit {
background-color: #e03d3e;
color: #fff !important; /*!important:将优先级提升最高*/
}
</style>