“今日头条”首页的核心特点是:

- 信息流聚合来自不同频道(如推荐、视频、社会、娱乐等)。
- 瀑布流/卡片式布局以图文卡片的形式,非规则地、紧凑地排列,最大化利用屏幕空间。
- 频道切换:顶部有清晰的频道导航栏,点击可切换不同分类的内容。
- “推荐”频道个性化:基于用户行为推荐内容(这个在织梦中需要借助插件或二次开发实现,我们先做静态展示)。
- 多媒体支持:大量包含图片、视频的文章。
- 内容吸引眼球:大图、醒目标题、简洁摘要。
我们将从以下几个方面进行修改:
第一步:准备工作
- 织梦环境:确保你已经安装好了织梦CMS,并熟悉后台的基本操作。
- 模板文件:准备好一套基础的织梦模板,我们将修改
default目录下的模板文件。 - 频道分类:在织梦后台,提前创建好你需要的所有内容频道(如:推荐、视频、社会、财经、科技、娱乐、体育等),这些分类将对应头条的频道栏。
- 内容填充:为每个分类添加一些测试文章,以便预览效果,建议多上传一些带缩略图的文章。
第二步:修改首页核心文件 (index.php)
今日头条的首页内容是动态加载的,但为了简化,我们先做一个静态的首页,所有内容都通过首页调用,织梦的首页默认是 index.php,它会调用 templets/default/index.htm 模板。
我们不需要直接修改 index.php 文件本身,但需要理解它的工作原理:它执行一系列数据库查询,并将结果变量(如 arclist)传递给模板文件。
第三步:制作频道导航栏
频道栏是头条首页的标志性元素,我们将在 index.htm 中使用织梦的 channel 标签来制作。

打开 templets/default/index.htm,在 <head> 标签后或 <body> 的最顶部添加导航栏代码。
代码示例 (index.htm):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">我的今日头条 - 仿制版</title>
<!-- 引入CSS文件,稍后我们会创建 -->
<link rel="stylesheet" href="{dede:global.cfg_templets_skin/}/css/style.css">
</head>
<body>
<!-- 顶部导航栏 -->
<div class="header">
<div class="container">
<div class="logo">
<a href="/">
<img src="{dede:global.cfg_templets_skin/}/images/logo.png" alt="我的头条">
</a>
</div>
<div class="nav">
<!-- 使用dede:channelartlist或dede:channel循环输出所有顶级栏目 -->
<!-- 这里我们使用一个更灵活的方法,调用所有非外部链接的顶级栏目 -->
{dede:channel type='top' row='10'}
<li><a href="[field:typelink/]">[field:typename/]</a></li>
{/dede:channel}
<!-- 可以手动添加一个“推荐”频道,它通常指向首页 -->
<li class="active"><a href="/">推荐</a></li>
</div>
<div class="search">
<form action="/plus/search.php" method="get">
<input type="text" name="q" placeholder="搜索热点">
<button type="submit">搜索</button>
</form>
</div>
</div>
</div>
<!-- 主要内容区域 -->
<div class="main-container">
<!-- 这里将放置内容列表 -->
</div>
</body>
</html>
说明:
{dede:channel type='top' row='10'}: 这个标签会调用系统中所有顶级栏目(频道),最多显示10个。class="active": 我们给“推荐”频道添加了active类,用于在CSS中高亮显示当前选中的频道。
第四步:创建信息流内容列表
这是最核心的一步,头条首页的内容区域是一个巨大的信息流,我们将使用织梦强大的 {dede:arclist} 标签来调用文章。

修改 index.htm 中的 <div class="main-container"> 部分:
<div class="main-container">
<!--
使用arclist标签调用所有频道的最新文章。
头条首页是聚合所有频道内容的,所以我们不指定typeid,或者指定一个包含所有频道ID的typeid。
-->
{dede:arclist row='50' titlelen='40' infolen='120' imgwidth='300' imgheight='200' orderby='pubdate'}
<div class="article-card">
<!-- 如果有缩略图,显示左侧大图 -->
[field:picname runphp='yes']
@me = (@me == '') ? '' : '<div class="article-img"><a href="[field:arcurl/]"><img src="[field:picname/]" alt="[field:title/]"></a></div>';
[/field:picname]
<div class="article-content">
<h2><a href="[field:arcurl/]">[field:title/]</a></h2>
<p class="article-summary">[field:info/]...</p>
<div class="article-meta">
<span class="source">[field:typename/]</span>
<span class="time">[field:pubdate function="MyDate('m-d H:i',@me)"/]</span>
<span class="views">[field:click/] 阅读</span>
</div>
</div>
</div>
{/dede:arclist}
</div>
{dede:arclist} 标签关键参数解释:
row='50': 显示50篇文章,你可以根据需要调整,len='40'`: 标题长度,40个汉字。infolen='120': 内容摘要长度,120个汉字。imgwidth='300'&imgheight='200': 自动缩略图的宽度和高度。注意: 这需要你在后台“系统”->“附件设置”中开启“远程/本地图片支持”,并确保文章上传了缩略图。orderby='pubdate': 按发布时间倒序排列,这是最新的方式。[field:picname runphp='yes']...[/field:picname]: 这是一个高级用法,如果文章没有缩略图(@me == ''),则不显示图片区域;如果有,则生成图片HTML,这实现了“有图/无图”两种卡片的统一。
第五步:编写CSS样式 (style.css)
让我们创建 style.css 文件,来实现头条那种紧凑、现代的卡片式布局。
在 templets/default/ 目录下新建一个 css 文件夹,并在其中创建 style.css 文件。
style.css 文件内容:
/* 全局重置和基础样式 */
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; background-color: #f4f5f6; color: #333; line-height: 1.6; }
.container { max-width: 1200px; margin: 0 auto; padding: 0 15px; }
a { text-decoration: none; color: #333; }
a:hover { color: #1a73e8; }
/* 顶部导航栏 */
.header { background-color: #fff; border-bottom: 1px solid #f0f0f0; position: sticky; top: 0; z-index: 1000; }
.header .container { display: flex; align-items: center; height: 60px; }
.logo img { height: 40px; }
.nav { flex: 1; margin-left: 30px; }
.nav ul { list-style: none; display: flex; }
.nav li { margin-right: 30px; }
.nav a { font-size: 16px; color: #333; font-weight: 500; }
.nav a.active { color: #1a73e8; } /* 高亮当前频道 */
.search { margin-left: auto; }
.search input { padding: 8px 12px; border: 1px solid #ddd; border-radius: 20px; width: 200px; outline: none; }
.search button { padding: 8px 15px; background-color: #1a73e8; color: white; border: none; border-radius: 20px; cursor: pointer; margin-left: -25px; }
区域 - 瀑布流布局 */
.main-container { padding: 20px 0; }
.article-card {
background-color: #fff;
border-radius: 8px;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
margin-bottom: 15px;
display: flex;
overflow: hidden;
transition: transform 0.2s;
}
.article-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
布局 */
.article-img { flex: 0 0 300px; } /* 图片区域固定宽度 */
.article-img img { width: 100%; height: 200px; object-fit: cover; display: block; }
.article-content { flex: 1; padding: 15px; }
.article-content h2 { font-size: 18px; font-weight: bold; margin-bottom: 10px; line-height: 1.4; }
.article-content h2 a { color: #333; }
.article-summary { font-size: 14px; color: #666; line-height: 1.5; margin-bottom: 10px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.article-meta { font-size: 12px; color: #999; }
.article-meta span { margin-right: 15px; }
CSS布局关键点:
- Flexbox布局: 我们对
.article-card使用了display: flex,这是实现“一张图+一段文字”这种并列布局最简单高效的方式。 - 固定宽度: 图片区域
.article-img设置了flex: 0 0 300px;,意思是“不要伸缩,固定占用300px宽度”。 - 内容区域自适应: 内容区域
.article-contentflex: 1,会自动占据剩余的所有空间。 - 无图处理: 在第四步的PHP代码中,我们已经处理了无图的情况,如果无图,
.article-img整个div都不会被渲染,.article-content会自动撑满整行,实现图文混排。
第六步:实现频道切换功能(进阶)
目前点击导航栏的链接会跳转到对应的分类页面,要实现头条那种“不刷新页面,动态切换内容”的效果,需要用到 Ajax。
思路:
- 修改导航栏的链接,使其指向一个自定义的PHP处理文件,而不是直接跳转到分类页。
- 这个PHP文件接收一个分类ID(
typeid),然后只调用该分类下的文章列表。 - 使用JavaScript监听导航栏的点击事件,阻止默认跳转,然后发起Ajax请求。
- 将返回的文章列表HTML,替换掉首页内容区域的HTML。
具体步骤:
-
创建Ajax处理文件: 在
plus/目录下创建ajax_list.php文件。plus/ajax_list.php<?php require_once(dirname(__FILE__)."/../include/common.inc.php"); // 获取前端传来的分类ID $typeid = isset($_GET['typeid']) ? intval($_GET['typeid']) : 0; // 使用arclist标签调用指定分类的文章 // 注意:这里直接输出了HTML,所以不需要加载完整的模板 $artlist = ''; $dsql->SetQuery("SELECT id, title, litpic, description, pubdate, click FROM `#@__archives` WHERE typeid IN ($typeid) ORDER BY pubdate DESC LIMIT 0, 20"); $dsql->Execute('list'); while($row = $dsql->GetArray('list')) { $arcurl = GetOneArchive($row['id']); $arcurl = $arcurl['arcurl']; // 拼接HTML,这里可以复用你首页的卡片HTML结构 $artlist .= <<<EOF <div class="article-card"> <div class="article-img"><a href="$arcurl"><img src="{$row['litpic']}" alt="{$row['title']}"></a></div> <div class="article-content"> <h2><a href="$arcurl">{$row['title']}</a></h2> <p class="article-summary">{$row['description']}...</p> <div class="article-meta"> <span class="source">频道名</span> <span class="time">{$row['pubdate']}</span> <span class="views">{$row['click']} 阅读</span> </div> </div> </div> EOF; } // 直接输出HTML echo $artlist; exit; ?>注意:这个
ajax_list.php是一个简化版,实际项目中应该更严谨地处理SQL注入,并使用{dede:arclist}标签来保持代码一致性,但直接写SQL在某些情况下效率更高。 -
修改导航栏链接并添加JavaScript: 修改
index.htm中的导航栏部分,并添加JS代码。修改后的
index.htm部分:<!-- 在 <head> 中引入 jQuery --> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script> <!-- 导航栏部分,给链接添加data-typeid属性 --> <div class="nav"> {dede:channel type='top' row='10'} <li><a href="[field:typelink/]" data-typeid="[field:id/]">[field:typename/]</a></li> {/dede:channel} <li class="active"><a href="/" data-typeid="0">推荐</a></li> <!-- 推荐频道typeid为0,表示所有 --> </div> <!-- 主要内容区域,给一个id方便JS操作 --> <div class="main-container" id="article-list"> <!-- 原来的arclist标签保留,作为初始加载的内容 --> {dede:arclist row='50' titlelen='40' infolen='120' imgwidth='300' imgheight='200' orderby='pubdate'} <div class="article-card"> [field:picname runphp='yes']@me = (@me == '') ? '' : '<div class="article-img"><a href="[field:arcurl/]"><img src="[field:picname/]" alt="[field:title/]"></a></div>';[/field:picname] <div class="article-content"> <h2><a href="[field:arcurl/]">[field:title/]</a></h2> <p class="article-summary">[field:info/]...</p> <div class="article-meta"> <span class="source">[field:typename/]</span> <span class="time">[field:pubdate function="MyDate('m-d H:i',@me)"/]</span> <span class="views">[field:click/] 阅读</span> </div> </div> </div> {/dede:arclist} </div> <!-- 在body底部添加JS代码 --> <script> $(document).ready(function(){ // 点击导航栏链接 $('.nav a').on('click', function(e){ e.preventDefault(); // 阻止默认跳转行为 // 更新导航栏高亮状态 $('.nav a').removeClass('active'); $(this).addClass('active'); // 获取typeid var typeid = $(this).data('typeid'); // 显示加载提示(可选) $('#article-list').html('<div style="text-align:center;padding:20px;">加载中...</div>'); // 发起Ajax请求 $.ajax({ url: '/plus/ajax_list.php', type: 'GET', data: { typeid: typeid }, dataType: 'html', success: function(response){ // 请求成功,将返回的HTML插入到内容区域 $('#article-list').html(response); }, error: function(){ $('#article-list').html('<div style="text-align:center;padding:20px;color:red;">加载失败,请重试。</div>'); } }); }); }); </script>
总结与后续优化
通过以上步骤,你已经成功地将一个普通的织梦首页,改造为一个具有“今日头条”风格的、支持频道切换的动态信息流网站。
后续可以优化的方向:
- 无限滚动/“加载更多”:在页面底部添加一个“加载更多”按钮,或者当用户滚动到页面底部时,自动加载下一页内容,这同样需要用到Ajax,原理和频道切换类似,只是请求的参数是
page而不是typeid。 - “推荐”频道算法:真正的头条推荐是基于用户画像的,这需要记录用户的点击、浏览、停留时间等行为,并建立一套复杂的推荐算法,在织梦中实现这个非常困难,通常需要借助第三方推荐服务(如友盟、百度统计)或进行深度二次开发。
- 响应式设计:完善CSS,让网站在手机、平板等不同尺寸的屏幕上都有良好的显示效果。
- 性能优化:对于大型网站,首页调用50篇文章可能会很慢,可以考虑使用织梦的“自定义模型”或“图集模型”来优化特定类型内容的调用,或者使用缓存技术。
- 特殊处理:为视频类型的文章设计特殊的卡片样式,比如在缩略图上播放按钮,并自动提取视频时长等信息。
