使用vue完成知乎日报web版

在线地址

demo地址

github地址

运行截图




运行动图

运行过程:首页–>轮播–>侧滑栏–>详情页–>分享–>评论页–>专栏列表–>详情页–>个人页–>更改为夜间模式–>运行–>主题日报

如需观看请在wifi下打开此地址

制作起因

前段时间在github上看到了知乎日报的API和一个用vue制作的知乎日报,觉得自己也应该试着做一个用来练习练习vue。制作历时两周,基本上是仿照安卓版知乎日报的ui做的,功能上除了一些需要登录后完成的功能如评论点赞等,基本上都实现了。可能在不同手机浏览器里会出现不兼容的现象。在chrome中可以比较好的运行。

下面就来介绍一下整个过程中,和遇到了哪些比较问题,是如何解决或者是变通的。

过程

项目搭建

使用的构建工具是webpack,但是我其实对构建项目上并没有很多经验,主要是应用了vue-cli这个脚手架工具,在vue-cli生成的项目的基础上做了一些小的修改,比如:因为是移动端的页面,用了flexible,所以加了postcss来处理px和rem之间的转换。

项目目录基本维持了vue-cli的目录结构,只是多加了一个view目录来存放每个路由的页面,把跟路由直接写进了入口的html文件内。

分析接口

其中很主要的一个工作就是分析接口中返回数据的格式,因为有些接口可能会是同一个但是返回的数据接口可能是不一样的。

比如:推荐者列表这个接口,有主编和没有主编的返回的数据接口是不一样的。

比如:日报详情页面,首页日报和主题日报可能就是不一样的,有些主题日报并没有返回body这个字段,所以你需要判断如果没有body这个字段,就需要用一个iframe标签去加载share_url这个字段的地址去加载内容,否则页面显示就会是空白的。

这样的情况有很多,接口的功能相同,但是返回的字段结构确是不同的,这时候就需要去分析每个接口,在布局方面注意想到不同情况下使展示方面不出现问题。

流程问题

另一个需要去想的就是使用流程方面的问题。

比如,从列表页进入详情页返回时应该要回到当前列表的位置。如下:

1
2
3
4
5
6
7
8
9
// 判断从哪里来,如果是详情页就不重新请求,并返回上次列表位置
if (transition.from.name === 'detail') {
_this.$nextTick(function () {
window.document.body.scrollTop = window.sessionStorage.scrollTop
})
} else {
_this.getTheme()
window.document.body.scrollTop = 0
}

进到列表页的可以从详情页返回到列表页,可以从侧边栏选择不同主题进到列表页,所以就需要判断,到底是从那个路由进来的,在不同路由时触发不同的动作。从详情页到列表页就应该回到上次的位置,从侧边栏进入就应该从新请求数据,并返回最顶部。

再比如,有时下个页面会需要一些上个页面的数据,你通过哪种方式携带过去?是通过url,还是通过sessionStorage。

布局

主要用了flexible和flex的布局方案,flexible是因为是移动端页面,根据屏幕大小可以让盒子的尺寸适应屏幕,flex可能不是很兼容pc端一些低级浏览器,但是如果在移动端,基本都支持flex,用flex也可以免去浮动带来的一些特殊情况(比如改变webview的字体大小带来的浮动布局变乱),和绝对定位应用不灵活的缺点。


问题

知乎的图片防盗链

知乎的图片可能会通过请求头的referer参数判断,如果不是指定的域名会返回403,如果精通后台的同学,可以去访问这些图片来缓存这些图片,我是搜索到了一个相对简单一些的办法,点击链接,主要用到的是Images.weserv.nl这个网站,可以缓存图片,而且可以修改图片的尺寸大小。

具体是我自定义了一个过滤器,在需要用到地方,把知乎的url替换成这个图片代理网站的url,这样图片就可以显示了。

1
2
3
exports.replaceUrl = (srcUrl) => {
return srcUrl.replace(/http\w{0,1}:\/\/p/g, 'https://images.weserv.nl/?url=p')
}

知乎接口跨域问题

接口跨域后浏览器是会禁止的,我试了一下jsonp,但是知乎的接口好像不支持jsonp。所以我们只能是在后台转发这个请求。我也是临时现学了一下nginx,用nginx起了一个服务,所有通过api.yatessss.com/news-at/xxxxxx的请求都会替换成http://news-at.zhihu.com/xxxxxx。下面是nginx配置:

1
2
3
4
5
6
7
8
9
10
11
server {
listen 8888;
server_name api.yatessss.com;
location /news-at/{
proxy_pass http://news-at.zhihu.com/;
add_header Content-Type "text/plain;charset=utf-8";
add_header 'Access-Control-Allow-Origin' 'http://zhihudaily-vue.yatessss.com';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET';
}
}

还有一种方法是听同学说的,用fetch api不会出现跨域的问题,但是我自己没有试过,有兴趣的同学不妨去了解一下试一下。

另另外一种方法,论坛上v友说的,可以用vue里config/index.js 里面的 proxyTable可以代理转发api,我没有试过,但是我觉得这个应该是最简单的方法了,我要去了解一下。

组件复用问题

虽说vue是可以组件化得,我在这里也用到了,但是组件化是一个需要考虑每个应用场景的,同一个组件在不同的view中,展示的会有不一样的地方,所以每种情况显示哪些内容都是需要考虑的。

比如:列表页的每个话题栏li,话题栏是一个组件,但是话题栏有些是没有图片的当没有图片时,你的话题栏li的高度可以要自适应,如果多图图片右下角要给出提示。而进入到专栏列表时话题栏左下部需要有时间,而在平时是没有的这个也需要去注意。

获取不到scrollTop的值

这个其实是css的问题,以前从来没有碰到过这样的问题。如果在给body设置了overflow这个属性是scroll后就无法获取到scrollTop的值了,所以如果碰到上拉加载更多的时候要尤其注意一下这个问题。

因为我会有一个蒙层,在蒙层出现的时候蒙层下方的列表就不能滑动了,在这里我是给body的style加了一个overflow:hidden这样一个属性。在蒙层消失后在改变为overflow:auto所以才会出现我上述的这个问题。

解决办法是使用添加移除class的方式来添加这个属性,或者是使用overflow:initial这个属性。

一些过滤器方面的积累

平时我们在完成工作的过程中,总会遇到后台返回的字符串可能会不太合心意,比如后台python返回给我们的时间戳是10位的,而js生成的是13位的。这就需要我们来处理一下了,另外时间戳的格式化也需要处理;一般后台传回来的金额都是以分为单位的,而一般前端展示是以元为单位的,所以也需要单位转换的方法。

在我们平时的工作当中逐渐积累在遇到相同的问题时就会更加的得心应手了。

动态修改header的title

app中列表页的header上的文字可以随着列表上拉,到相应的日期显示对应的文字,这个目前有一个解决的办法,是判断列表距离顶部的位置,当距离小于小于零的时候就去把他改变成对应的日期,主要用到的是getBoundingClientRect这个api,他主要是可以获取选择元素位于当前视口的位置。具体可以看这个资料:用Javascript获取页面元素的位置。但是在iphone中会有在滚动时dom无法及时更新这个问题,搜索了一下暂时没找到解决办法。

iphone在滚动时dom无法及时更新

这里有两个答案:

JavaScript DOM changes in touchmove delayed until scroll ends on mobile Safari

移动端onscroll事件在部分浏览器内不能实时触发


不足

虽然完成了大多数功能,是其实可以优化的地方有很多,最大的优化点,没有使用vuex来管理数据状态,现在所有的都是耦合在项目里的,都是通过变量在组件当中互相传递状态的。因为我还没有学习vuex所以在这里就没有用到,在后面学习了vuex后,我会重新优化一下这个部分。

没有加入手势功能,比如在侧边栏出现的时候,向左滑动来收起侧边栏比点击要自然很多,vue也专门有一个手势功能的库,所以这个部分也是可以优化的。