Chapter:11.前端页面开发
11.12-15.书籍详情页面的静态实现和动态数据绑定(有重要知识)
展示层代码
太长了,为了避免影响阅读体验放后面了,主要就是 html 结构,css 样式,引入 book.js 文件,在添加对应的vue.js 模板语法来展示数据
在index的各组件对应的书籍列表项里实现跳转到书籍详情页的链接
var id=location.href.split('?id=').pop();
中间层代码
在static/script/pages
下新建book.js
文件
var id=location.href.split('?id=').pop();//用js正则取地址栏id,这是最简单的方法,1当然在别的地方不一定适用,比如说传了多个参数
//var id=18218;
$.get('/ajax/book?id='+id,function(d){
//debugger;
new Vue({
el:'#app',
data:d,//将从接口返回的数据赋值给变量data
methods:{
})
},'json');
路由层代码
入口文件app.js
里添加
//书籍详情页数据路由接口(跟其它几个稍微不同,这个需要传书籍的id)
app.use(controller.get('/ajax/book',function*(){
this.set('Cache-Control','no-cache');
var params = querystring.parse(this.req._parsedUrl.query);//读取地址栏地址的http参数并转为object格式
var id = params.id;//将参数赋值给本地变量
if(!id){
id="";
}
this.body =service.get_book_data(id);//这里函数不是是异步返回的,不用yield返回
}));
服务层代码
webAppService.js
添加相应代码
//书籍详情页(需要传参数书籍id)
exports.get_book_data = function(id){//Node.js的语法
if(!id){//如果传过来的id是空的,设为默认书籍
id="18218";
}
if(fs.existsSync('./mock/book/' + id + '.json')){
return fs.readFileSync('./mock/book/' + id + '.json', 'utf-8');
}else{
return fs.readFileSync('./mock/book/18218.json', 'utf-8');
}
}
页面跳转链接完善
在主页各个模块的图书信息里包上一层链接,实现跳转到该书记详情页
<a href="/book?id={{item.fiction_id}}">
前后端数据交互原理
图书信息的交互和数据流动方向:
1.访问主页
1.1输入主页地址
1.2服务器检查是否是否有对应路由接口 /
,有则进行下一步。路由在启动文件 app.js
设置,需要先引入对应 KOA
模块,路由可以先对传入的地址进行处理,比如说参数解析传参等,之后打开相应html文件,而这个html文件也需要启动文件中相应代码才能渲染出样式和传输数据等
1.3接着调用对应HTML文件 index.html
(加上什么尾缀以匹配对应的网页静态文件和渲染样式也是需要在启动文件app.js
里设置的),因为这是服务端渲染,所以需要等js文件获取了数据填充到网页之后才会返回html
文件,所以先进行下面操作
1.4 index.html
里引入了 index.js
,这个是展示层和路由接口的中间处理文件,这个文件访问对应路由接口 /ajax/index/
(名字可以随便起,不过这样是规范的取法)
1.5 路由接口 /ajax/index/
调用对应处理函数,这些处理函数在服务层文件 webAppService.js
中定义,该函数会访问本地数据(因为用的是mock数据,如果是http接口则是通过对应接口调用数据),并返回给路由接口 /ajax/index
,路由接口再将数据返回给调用接口的中间处理文件 index.js
, index.js
再将数据返回给引入该文件的页面 index.html
,通过模板引擎渲染出来再返回客户端(这里 vue.js 虽然不算模板引擎,但是有其功能)
2.主页index.html
得到图书信息后,利用模板引擎读取了对应的图书数据,包括书籍id, 每本书籍都包围了一个<li>
内都包围一个<a>
链接,将id添加到跳转链接中 <a href="/book?id={{item.fiction_id}}">
,点击即能访问对应书籍详情页
3.访问书籍详情页
1.1 在 index.html
中点击链接后,同样是检查对应路由(注意传入的参数不算是路由的名字一部分),( app.js
的路由代码可以读取链接中的参数并处理) 。之后调用打开对应的html文件book.html
1.2book.html
里引入了界面和路由接口之间的中间层文件 book.js
,book.js
是对应的访问路由接口获取数据并处理返回的代码。book.js
读取当前请求访问的地址(此时对应书籍详情页还未打开)(当然有其它获取参数的方法,这种方法最简单,但是如果传入的参数是空的或者有多个参数会出问题)·,获知了书籍id
,
var id=location.href.split('?id=').pop();
并调用了启动文件 app.js
里定义的/ajax/book/
路由接口,将参数id
传了进去
1.3 /ajax/book/
路由接口调用在 服务层 webAppService.js
中定义的函数,也把参数id
传了进去,该函数利用这个 id
参数检查本地(服务器/数据库/或线上接口)是否有该文件,并返回相应的数据给路由接口,路由接口再返回给中间层函数,中间层函数再返回给页面,然后页面利用模板引擎将数据渲染出来再返回到客户端
前端页面
book.html
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<meta name="format-detection" content="telephone=no"><!--不智能识别电话号-->
<link rel="stylesheet" type="text/css" href="/static/css/reset.css"><!--基础通用样式-->
<title>书籍详情</title>
<style type="text/css">
.main-card {
border-bottom: 10px solid #f5f5f5;
}
.main-card.-folder { padding-bottom: 1px;}
.main-card.-folder .cnt {
margin-bottom:12px;
}
.main-card>.cnt,.main-card .list li {
padding-left: 14px;
padding-right: 14px;
}
.u-title {
margin-bottom: 8px;
padding-top: 15px;
padding-left: 14px;
padding-right: 14px;
font-size: 15px;
color:#8d8d8d;
}
.u-book {
position: relative;
overflow: hidden;
}
.u-book.-detail{
padding: 40px;
display: table;
width: 100%;
box-sizing:border-box;
}
.u-book.-detail .book-cover{width: 100px;height: 134px;}
.u-book.-detail .author {color:#4b99a7;}
.u-book.-detail .author>span {display: inline-block;margin-right:5px;}
.u-book.-detail .title {margin-bottom:13px;font-size:16px;}
.u-book.-detail .info {
padding: 0 0 0 14px;
display: table-cell;/*不同于float、flex的一种布局方法*/
vertical-align: middle;
}
.u-book.-vertical {width: 86px;}/*列向排列的书籍列表*/
/*.u-book的子元素中.-vertical类的样式(-开头的类相当于表示打一个标记的意思,标记这里的书数列向排列的)*/
.u-book.-vertical .title {
font-size: 13px;
line-height: 1.4em;
max-height: 2.8em;
overflow: hidden;
color: #8d8d8d;
margin-bottom: 0px;
text-align: left;
}
.u-book.-vertical .book-cover{
float: none;
height: 113px;
width: 86px;
}
.u-book.-vertical .info {
margin-left: 0;
padding-top: 8px;
}
.book-cover{
position: relative;
background:#eeece9;
box-shadow: 0px 6px 5px -3px #aaa;
border: 1px solid #f0f0f0;
border-bottom: none;
overflow: hidden;
}
.book-cover img {
width: 100%;
height: 100%;
}
.u-booktag{
margin-left: 3px;
border: 1px solid #00a0e9;
border-radius: 4px;
font-size: 12px;
line-height: 16px;
display: inline-block;
padding: 0 2px;
}
.u-booktag.-serial{/*连载中/已完结 标签的样式*/
color:#63bd6e;
border-color:#63bd6e;
}
.book-dash .wrap {/*开始阅读按钮外层*/
padding: 0 14px;
margin:0 0 27px;
}
.book-dash-text:after{/*"开始阅读"文字,这种静态的文字都建议用CSS实现*/
content:'\5f00\59cb\9605\8bfb';
}
.btn-group {width: 100%;font-size: 0;white-space: nowrap;}
.bth-group li {display: inline-block;width: 49%;}
.bth-group li:only-child{width: 100%;}
.bth-group li:first-child{margin-right: 2%;}
.u-btn2{
display: block;
height: 2.8em;
line-height: 2.8em;
text-align: center;
border-radius: 4px;
font-size: 14px;
-webkit-box-sizing:border-box;
background:#f35d02;
border: 1px solid #e35109;
color:#fff;
padding: 0 10px;
}
.u-folder>.folder-cnt{
position: relative;
line-height: 1.6;/*让字看起来不都挤在一起*/
padding:0 14px;
margin-bottom:10px;
font-size: 14px;
color:#585858;
}
.u-folder>.folder-tail{
text-align: center;
font-size: 14px;
border-top: 1px solid #f0f0f0;
color:#8d8d8d;
}
.u-folder>.folder-top{
font-size: 16px;
font-weight: normal;
color:#8d8d8d;
padding: 14px 14px 8px;
}
.m-tag { line-height: 1;overflow: hidden;}
/*这样设的原因是为了让相邻的元素的颜色不一样,为了美观*/
.m-tag.-color .u-tag:nth-child(3n+1){/*n从0开始*/
background-color: #fbebe8;
}
.m-tag.-color .u-tag:nth-child(3n+2){
background-color: #f0f0f0;
}
.m-tag.-color .u-tag:nth-child(3n+3){
background-color: lightblue;
}
.m-tag .u-tag {
margin: 0 10px 5px 0;
display: inline-block;
width: auto;
line-height: 1.8em;
padding: 0 20px;
color:#766d5d;
border-radius: 4px;
background:#909da8;
font-size: 14px;
text-align: center;
border: 1px solid #d3d3d3;
}
.book-table { font-size: 0;}
.book-table li>*{
display: inline-block;
}
.book-table li{
width: 33.3%;
display: inline-block;
vertical-align: top;
line-height: 1;
margin-bottom: 8px;
}
.book-table li:nth-child(3n+1) {
text-align: left;
}
.book-table li:nth-child(3n+2) {
text-align: center;
}
.book-table li:nth-child(3n+3) {
text-align: right;
}
</style>
</head>
<body>
<div id="app" style="width:734px;overflow:hidden"><!--只显示734px宽的部分-->
<%include include/common-header.html %>
<div class="container-scroll" style="width:734px;"><!--设置可滚动区域的容器-->
<div class="book-page">
<section class="main-card" style="border-bottom:none"><!--书籍信息(左边封面右边文字描述)-->
<div class="u-book -detail">
<div class="book-cover"><!--书籍封面-->
<img alt="{{item.title}}" v-bind:src="item.cover"/>
</div>
<div class="info"><!--书籍信息-->
<h3 class="title">{{item.title}}</h3>
<p class="author">
<span class="author">{{item.authors}}</span>
</p>
<div class="u-grade">
<span class="read">{{item.score_count}}个评价</span>
</div>
<p class="price">价格:{{item.price}}书币/千字</p>
<p class="count">字数:{{item.word_count}}字
<span class="u-booktag -serial">连载中</span><!--这种class名的起名方式是老师的公司的约定,
-serial想表示的是还可能有其它内容比如"已完结",横杠开头相当于是标记一下这里的意思-->
</p>
</div>
</div>
</section>
<section class="main-card">
<div class="book-dash"><!--"开始阅读"按钮-->
<div class="wrap">
<ul class="btn-group">
<li class="u-btn2" v-on:click="readBook()">
<span class="book-dash-text"></span>
</li>
</ul>
</div>
</div>
<div class="u-folder">
<div class="folder-cnt"><!--书籍摘录简介-->
{{ item.content }}
</div>
<div class="folder-tail"><!--显示最新章节-->
<div>最新:{{ item.lastest }}</div>
</div>
</div>
</section>
<section class="main-card">
<div class="u-folder"><!--类别标签-->
<div class="folder-top">
<h3>类别标签</h3>
</div>
<div class="folder-cnt">
<ul class="m-tag -color"><!--因为标签的颜色是有多种的,所以这里加个color标签-->
<li class="u-tag" v-for="tag in item.tags">
{{ tag }}
</li>
</ul>
</div>
</div>
</section>
<section class="main-card"><!--作者其它作品-->
<div class="u-title">
<h1>作者的其他作品</h1>
</div>
<div class="cnt">
<ul class="book-table">
<li v-for="book in author_books">
<div class="u-book -vertical">
<div class="book-cover">
<img title="{{ book.title }}" v-bind:src="book.cover"/>
</div>
<div class="info">
<h3 class="title">{{ book.title }}</h3>
</div>
</div>
</li>
</ul>
</div>
</section>
<section class="main-card">
<div class="u-title"><!--"喜欢本书的人也喜欢"推荐-->
<h1>喜欢本书的人也喜欢</h1>
</div>
<div class="cnt">
<ul class="book-table">
<li v-for="book in related">
<div class="u-book -vertical">
<div class="book-cover">
<img title="{{ book.title }}" v-bind:src="book.cover">
</div>
<div class="info">
<h3 class="title">{{ book.title }}</h3>
</div>
</div>
</li>
</ul>
</div>
</section>
<section class="main-card -folder"><!--版权信息-->
<div class="u-title">
<h1>图书信息</h1>
</div>
<div class="cnt">
<ul class="text">
<li>版权:{{ item.rights }}</li>
</ul>
</div>
</section>
</div>
</div>
</div>
</body>
<script src="/static/script/vue.js"></script>
<script src="/static/script/zepto.js"></script>
<script src="/static/script/pages/book.js"></script>
<!--可以通过访问页面,在控制台上输入相应名称如vue,zepto等查看是否成功引入文件-->
</html>
common-header.html
<style type="text/css">
.top{
position:relative;/*因为里面有元素要设为绝对定位,所以外层要设为相对定位?*/
height:44px;
border-bottom:1px solid #ddd;
font:15px/45px a;
color:rgba(0,0,0,0.7);
background-color: #efeff0;
}
.top__back{
float:left;
width:42px;
height:44px;
}
.top__back:before{/*在.top_back的元素前面插入一个空元素(有大小有背景图)*/
content:'';
display:block;
margin:15px 0 0 16px;
width:10px;
height:16px;
background:url(/static/img/back.png) no-repeat center;
background-size:10px 16px;
}
.header-home{
float:right;
width:44px;
height:44px;
background: url(/static/img/home.png) no-repeat;
background-size:16px;
}
</style>
<div class="top">
<a class="top__back" href="/"></a>
<span class="top_title"></span>
<a class="header-home" href="/"></a>
</div>
网友评论