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', //需要跨域的urlws: true, //代理webSocketchangeOrigin: 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>
