用4种框架分别实现简易新闻系统

导读:以一个简易新闻系统的开发实例,分别用4种框架实现。实现零基础入门Node.js、微信小程序、Vue.js、React等前端框架技术。

图1 网页版新闻系统列表页

图2 网页版新闻系统详情页

图3 微信小程序端新闻列表页 图4 微信小程序新闻详情页

1. Node.js+Express

思路:在 Node.js 的路由处理函数中渲染模板,并将数据发送给模板。在模板文件中绑定数据。

· routes/index.js文件的代码

var express = require('express');

var router = express.Router();

const conn = require('../db');

router.get('/', function(req, res, next) { //处理首页的路由,发送两个栏目框的数据给模板

var query = 'select * from news order by id desc limit 6';

conn.query(query, function(err, rows, fields){

var title = rows;

var query2 = "select * from news where BigClassName='学生工作' order by id desc limit 6";

conn.query(query2, function(err, rows, fields){

var title2 = rows;

res.render('lm', { title: title,title2:title2, formatD:formatD });

});

});

});

router.get('/show', function(req, res, next) { //处理内容页的路由,发送一条记录的数据给模板

var articleID = req.query.id; //获取首页传来的文章ID

var qry = 'SELECT * FROM news WHERE ID=' + conn.escape(articleID);

conn.query(qry, function(err, rows, fields) {

var qry2 = 'UPDATE news SET hits=hits+1 WHERE ID=' + conn.escape(articleID);

var article = rows0;

console.log(article);

conn.query(qry2, function(err, rows, fields) {

article.infotime = formatD(article.infotime);

res.render('show', { article: article });

});

});

});

module.exports = router;

·views/lm.ejs文件的代码

<div class="news">

<div class="title">

<h2>新闻动态</h2>

<span class="more"><a href="more.htm">更多&gt;&gt;</a></span>

</div>

<ul class="list_one">

<% for(let i=0; i<title.length;i++) { %>

<li><a href="show?id=<%= titlei.ID %>"><%= titlei.title %></a>

<b><%= formatD(titlei.infotime) %></b></li>

<% } %>

</ul>

</div>

<div class="news">

<div class="title">

<h2><%= title20.BigClassName %></h2>

<span class="more"><a href="more.htm">更多&gt;&gt;</a></span>

</div>

<ul class="list_one">

<% for(let i=0; i<title2.length;i++) { %>

<li><a href="show?id=<%= title2i.ID %>"><%= ellipsee(title2i.title,18) %></a>

<b><%= formatD(title2i.infotime) %></b></li>

<% } %>

</ul>

</div>

·views/show.ejs文件的代码

<body style="width:700px; margin:20px auto;">

<h1><%= article.title %></h1>

<div style="border: 1px dashed #666; height:1.8em; text-align:center">

发布者:<%= article.user %> 发布时间:<%= article.infotime %> 点击次数 <%= article.hits %>

</div>

<div style="font-size: 16px; color:#999"><%- article.content %> </div>

</body>

2. 微信小程序实现简易新闻系统

微信小程序、Vue.js和React访问Node.js后端输出的数据,都可以实现前后端分离的架构。在前后端分离架构下,后端的Node.js程序完全一样,其思路是输出json数据给前端。

1. 后端程序

将routes/index.js文件的代码修改如下:

router.get('/', function(req, res, next) {

var query = 'select * from news order by id desc limit 6';

conn.query(query, function(err, rows, fields) {

var title = rows;

var query2 = "select * from news where BigClassName='学生工作' order by id desc limit 6";

conn.query(query2, function(err, rows, fields) {

var title2 = rows;

var titall = ...title, ...title2;

res.send(titall);

});

});

});

router.get('/show', function(req, res, next) {

let id = req.query.id;

var query = 'select * from news where id=' + id;

conn.query(query, function(err, rows, fields) {

var title = rows0;

console.log(rows);

res.send(title);

});

});

2. 微信小程序端的代码

index/index.js文件

Page({

onLoad: function (options) {

this.getList()

},

getList: function () {

var _self = this

wx.request({

url: 'http://localhost:3000', //请求的Node.js端网址

methods: 'GET',

success: function (res) {

console.log(res.data); //用于测试

_self.setData({

title: res.data.splice(0,6), //将返回的数组前半赋值给title

title2: res.data.splice(0,6) //将返回的数组后半赋值给title2

});

}

})

},

seeDetail:function(e){ //跳转到新闻详情页

var Id = e.currentTarget.id;

wx.navigateTo({

url: '../show/show?Id=' + Id,

//url: '../show/show/' + Id,

})

}

})

index/index.wxml文件

<view class="news">

<view class="title">

<view>新闻动态</view>

<text class="more"><a href="more.htm">更多&gt;&gt;</a></text>

</view>

<view class="list_one">

<view id="{{ titlei.ID }}" wx:for="{{title}}" wx:for-index="i" wx:key="id" bindtap='seeDetail'>

<a href="show?id={{ titlei.ID }}">{{ titlei.title }}</a> <b>{{ titlei.infotime }}</b></view>

</view>

</view>

<view class="news">

<view class="title">

<view>{{ title20.BigClassName }}</view>

<text class="more"><a href="list?type={{ title20.BigClassName }}">

更多&gt;&gt;</a></text>

</view>

<view class="list_one">

<view id="{{ title2i.ID }}" wx:for="{{title2}}" wx:for-index="i" wx:key="id" bindtap='seeDetail'>

<a href="show?id={{ title2i.ID }}">{{ title2i.title }}</a><b>{{ title2i.infotime }}</b></view>

</view>

</view>

show/show.js文件

var Id

const { stripHtml } = require('../func/func.js');

Page({

data: { },

onLoad: function(options) {

Id = options.Id

console.log(options)

this.loadDetail()

},

loadDetail: function() {

if (Id != "") {

var _self = this;

wx.request({

url: 'http://localhost:3000/show?id=' + Id, //发送Id给后端程序

method: 'GET',

success: function(res) {

var title = res.data;

title.content=stripHtml(title.content);

_self.setData({title: title });

}

})

}

},

})

show/show.wxml文件

<view class="header">{{title.title}}</view>

<view class="content">{{title.content}}</view>

3. Vue前端实现新闻系统

本例采用Vue构建模式,将HTML代码写在<template>中,CSS代码写在<style>中,JavaScript代码写在export default {}中。

1. 新闻列表页的实现:App.vue

<script>

import axios from 'axios';

export default {

created() { //页面载入时执行

this.getbooks();

},

data() {

return {

title: \[\],

title2: \[\]

}

},

methods: {

getbooks() {

var self = this;

axios.get('http://localhost:3000', {

params: { }

}).then(function(response) {

self.title = response.data.splice(0, 6);

self.title2 = response.data.splice(0, 6);

//console.log(response.status);

//console.log(self.title);

}).catch(function(error) {

console.log('请求失败:' + error.status + ',' + error.statusText);

});

},

}

}

</script>

<template>

<div class="news">

<div class="title">

<h2>新闻动态</h2>

<span class="more"><a href="more.htm">更多&gt;&gt;</a></span>

</div>

<ul class="list_one">

<li v-for="(item,index) in title" :key="index">

<a :href="'show?id='+item.ID">{{ item.title }}</a><b>{{ item.infotime }}</b>

</li>

</ul>

</div>

<div class="news">

<div class="title">

<h2>新闻动态</h2>

<span class="more"><a href="more.htm">更多&gt;&gt;</a></span>

</div>

<ul class="list_one">

<li v-for="(item,index) in title2" :key="index">

<a :href="'show?id='+item.ID">{{ item.title }}</a><b>{{ item.infotime }}</b>

</li>

</ul>

</div>

</template>

<style>

.news {

width: 480px;

margin-left: 20px;

}

.title {

height: 40px;

line-height: 40px;

/*设置标题栏高度并使内容垂直居中*/

border-bottom: 2px solid #025483;

padding-bottom: 2px;

display: flex;

justify-content: space-between;

}

.title h2 {

height: 40px;

/*标题左浮动*/

font-size: 22px;

color: #000;

border-bottom: 4px solid #900;

padding-right: 40px;

margin: 0;

/*去掉h2标记的默认上下边界*/

}

.title .more {

display: block;

height: 22px;

padding-right: 10px;

}

/*"更多"右边保留一点间距*/

.more a {

font-size: 14px;

text-decoration: none;

color: #666;

}

.list_one {

min-height: 245px;

/*设置内容区域的最小高度*/

margin-top: 10px;

/*设置第一条新闻上面的间隙*/

}

.list_one li {

line-height: 36px;

height: 36px;

/*垂直居中*/

color: #999;

font-size: 18px;

padding-left: 6px;

text-overflow: ellipsis;

/*文本溢出则裁切*/

white-space: nowrap;

/*强制不换行*/

overflow: hidden;

/*溢出内容隐藏*/

display: flex;

justify-content: space-between;

}

.list_one li::before {

display: inline-block;

content: "";

width: 0;

height: 0;

border: 6px solid transparent;

border-left-color: #c13432;

margin-top: 14px;

}

.list_one li a {

flex-grow: 1;

text-overflow: ellipsis;

/*文本溢出则裁切*/

white-space: nowrap;

/*强制不换行*/

overflow: hidden;

/*溢出内容隐藏*/

}

.list_one li b {

font-weight: normal;

width: 80px;

text-align: right;

}

</style>

提示:要修改Vue项目的端口号,修改项目目录下以下2处即可:在package.json中,把"dev": "vite --port 5173",

在vite.config.js中,添加:

export default defineConfig({

plugins: vue(),

server: {

port: 5173 // 将端口改为5173

}

})

2. 添加路由实现新闻列表页和新闻详情页。

(1 )src/App.vue

<template>

<div id="app">

<router-view></router-view>

</div>

</template>

(2 )router/index.js

import { createRouter, createWebHistory } from 'vue-router'

const routes = [

{

path: '/',

name: 'NewsList',

component: () => import('../views/NewsList.vue')

},

{

path: '/news/:id',

name: 'NewsDetail',

component: () => import('../views/NewsDetail.vue')

}

]

const router = createRouter({

history: createWebHistory(),

routes

})

export default router

(3) views/NewsList.vue

<template>

<div class="news">

<div class="title">

<h2>新闻动态</h2>

<span class="more"><a href="more.htm">更多&gt;&gt;</a></span>

</div>

<ul class="list_one">

<li v-for="(item,index) in title" :key="index">

<router-link :to="'/news/' + item.ID">{{ item.title }}</router-link><b>{{ item.infotime }}</b>

</li>

</ul>

</div>

<div class="news">

<div class="title">

<h2>{{title20.title}}</h2>

<span class="more"><a href="more.htm">更多&gt;&gt;</a></span>

</div>

<ul class="list_one">

<li v-for="(item,index) in title2" :key="index">

<router-link :to="'/news/' + item.ID">{{ item.title }}</router-link> <b>{{ item.infotime }}</b>

</li>

</ul>

</div>

</template>

其他代码与上一节中App.vue的代码完全相同。特别注意:虽然**<router-link>**标签中传递ID是采用的路由参数形式,但Vue会截获该路由,委托给axios传递参数给后端程序,而axios传递id参数时会采用url参数的形式,因此后端程序必须使用req.query.id方式获取参数。

特别注意:

(4)views/NewsList.vue

<script>

import axios from 'axios';

export default {

created() {

this.getNewsDetail();

},

data() {

return {

news: null,

isLoading: true

}

},

methods: {

getNewsDetail() {

this.isLoading = true;

var self = this;

axios.get('http://localhost:3000/news', {

params: { id: this.$route.params.id }

}).then(function(response) {

self.news = response.data;

self.isLoading = false;

}).catch(function(error) {

console.log('请求失败:' + error.status + ',' + error.statusText);

self.isLoading = false;

});

}

}

}

</script>

<template>

<div class="detail-page">

<router-link to="/" class="back-link">← 返回新闻列表</router-link>

<div v-if="isLoading" class="loading">

加载中...

</div>

<div v-else-if="news" class="detail-content">

<h1 class="detail-title">{{ news.title }}</h1>

<div class="detail-meta">

<span class="detail-time">{{ news.infotime }}</span>

</div>

<div class="detail-body" v-html="news.content"></div>

</div>

<div v-else class="not-found">

新闻不存在

</div>

</div>

</template>

<style scoped>

.detail-page {

width: 80%;

margin: 20px auto;

padding: 20px;

background: #fff;

box-shadow: 0 2px 10px rgba(0,0,0,0.1);

}

.back-link {

display: inline-block;

padding: 8px 16px;

background: #025483;

color: #fff;

border-radius: 4px;

font-size: 14px;

margin-bottom: 20px;

text-decoration: none;

}

.back-link:hover {

background: #013a5c;

}

.loading {

text-align: center;

padding: 50px;

color: #999;

}

.detail-content {

line-height: 1.8;

}

.detail-title {

font-size: 28px;

color: #000;

margin-bottom: 20px;

padding-bottom: 10px;

border-bottom: 1px solid #eee;

}

.detail-meta {

color: #999;

font-size: 14px;

margin-bottom: 20px;

}

.detail-body {

font-size: 16px;

color: #333;

}

.detail-body img {

max-width: 100%;

height: auto;

margin: 10px 0;

}

.detail-body p {

margin-bottom: 1em;

}

.not-found {

text-align: center;

padding: 50px;

color: #999;

font-size: 18px;

}

</style>

4. React实现新闻系统前端

本例中列表页对应NewsList()组件,新闻详情页对应NewsDetail组件。在根组件App中定义到这两个组件的路由。

src/App.js代码

import './style.css';

import React, { useState, useEffect } from 'react';

import { Routes, Route, Link } from 'react-router-dom';

import axios from 'axios';

import NewsDetail from './components/NewsDetail';

function NewsList() {

const data, setData = useState(\[\]);

const data2, setData2 = useState(\[\]);

useEffect(() => {

const fetchData = async () => {

const result = await axios('http://localhost:3000');

console.log(result.data);

setData(result.data.splice(0,6));

setData2(result.data.splice(0,6));

};

fetchData();

}, \[\]);

return (

<>

<div className="news">

<div className="title">

<h2>新闻动态</h2>

<span className="more"><a href="more.htm">更多&gt;&gt;</a></span>

</div>

<ul className="list_one">

{data.map(item => (

<li key={item.ID}>

<Link to={`/show/${item.ID}`}>{item.title}</Link>

<b>{item.infotime}</b>

</li>

))}

</ul>

</div>

<div className="news">

<div className="title">

<h2>新闻动态</h2>

<span className="more"><a href="more.htm">更多&gt;&gt;</a></span>

</div>

<ul className="list_one">

{data2.map(item => (

<li key={item.ID}>

<Link to={`/show/${item.ID}`}>{item.title}</Link>

<b>{item.infotime}</b>

</li>

))}

</ul>

</div>

</>

);

}

function App() {

return (

<div className="app">

<Routes>

<Route path="/" element={<NewsList />} />

<Route path="/show/:id" element={<NewsDetail />} />

</Routes>

</div>

);

}

export default App;

src\components\NewsDetail.js

import { useState, useEffect } from 'react';

import { useParams, Link } from 'react-router-dom';

import axios from 'axios';

function NewsDetail() {

const { id } = useParams();

const news, setNews = useState(null);

const loading, setLoading = useState(true);

const error, setError = useState(null);

useEffect(() => {

const fetchNews = async () => {

try {

const result = await axios(`http://localhost:3008/show?id=${id}\`);

setNews(result.data);

setLoading(false);

} catch (err) {

setError(err.message);

setLoading(false);

}

};

fetchNews();

}, id);

if (!news) {

return <div className="news-detail">未找到该新闻</div>;

}

return (

<div className="news-detail">

<Link to="/" className="back-link">← 返回列表</Link>

<h1 className="news-title">{news.title}</h1>

<div className="news-meta">

<span className="news-time">{news.infotime}</span>

</div>

<div className="news-content" dangerouslySetInnerHTML={{ __html: news.content }}></div>

</div>

);

}

export default NewsDetail;