手动搭建vue+node单页面(二)
环境搭建好了,开始写业务和后端接口代码,这一篇讲的内容也比较简单,只适合小白参考;
环境搭建请参考 《手动搭建vue+node单页面(一)》:https://segmentfault.com/a/11...
项目地址:https://github.com/liubingyan...
内容提要:
1.jsonp获取baidu搜索框内容;
2.node调用juejin接口获取前端文章列表;
开发过程中不会讲的太细,有疑问多百度;
一、获取baidu搜索框内容
就是输入的同时下来框展示的内容;
在用node调用百度接口时候发现返回的是gbk格式的内容,node解析遇到困难,所以改用jsonp的方式;
首先在控制台分析接口:
返回值:
很容易看出内容中json数据的‘s’就是我们想要的内容;
接口地址的url内容过长,我们将这个地址复制到地址栏中经过反复测试,最终得到:
对我们有用的参数只有两个:wd(输入框的内容)和cb(返回时调用的方法名),接下来就可以开发了;
这个小功能的开发涉及的:
1.app.vue:将导航和路由写在其中,并做简单布局;
改之前在src目录下创建common文件夹,存放公共样式和方法(base.css等):
比如:
//base.css
//...
.fl{float:left}
.fr{float:right}
//...
//类似这样的预定义样式
app.vue做如下修改:(之后的样式都不再做详细说明)
<template>
<div>
<div class="clearfix wrap">
<!-- 左边 -->
<div class="app_left fl">
<!-- 导航栏 -->
<ul class="nav">
<li><router-link to="/">首页</router-link></li>
<li><router-link to="/forum">魔兽论坛</router-link></li>
</ul>
<!-- 路由页面展示 -->
<router-view></router-view>
</div>
<!-- 右边 -->
<div class="app_aside fr">
</div>
</div>
</div>
</template>
<script>
export default {
}
</script>
<style lang="scss">
/*这里使用@import的方式,如果要在js中用import引入,在webpack配置中module里增加/\.css$/匹配就好了;*/
@import "./common/reset.css";
@import './common/base.css';
.wrap{
min-height: 100%;
background: #eee;
}
.app_left{
width: 75%;
background: white;
min-height:100vh;
box-shadow: -1px 0 0 0 #ccc inset;
}
.app_aside{
width: 25%;
}
</style>
看效果之前先引入路由,不然页面没东西,src/router文件夹下创建index.js,创建router文件夹的原因还是模块化开发的思想,将作用相同的代码放在一起,利于维护和开发;
//router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '../views/home'
Vue.use(Router)
export default new Router({
routes: [{
path: '/',
name: 'home',
component: Home
}]
})
别忘了安装插件
npm i vue-router -save
main.js引入路由配置
//main.js
import Vue from 'vue'
import App from './app'
import router from "./router"//默认加载index文件
new Vue({
el: '#app',
router,//注册到vue实例
render: h => h(App)
})
效果如下:
导航和路由页面放在左边,右边栏留着放小插件;
3.根据导航最起码要有一个首页,一个论坛页,先做首页,在src下创建home.vue;
home页内容有两个,搜索框和juejin拿到的列表,先做搜索框;
//home.vue
<template>
<transition name='fade'>
<div class="wrap clearfix">
<!-- 搜索框组件 -->
<div class="search">
<search></search>
</div>
</div>
</transition>
</template>
<script>
//使用vue组件步骤:import引用->components注册->标签的方式展示
import search from "./views/search"
export default {
name: 'home',
components:{
search
},
}
</script>
<style lang="less" scoped>
</style>
编写搜索框组件,在src/views下创建search.vue
//search.vue
<template>
<div class="searchWrap">
<div class="search clearfix">
<!-- 绑定输入内容,绑定keyup事件调用接口,很简单不是-->
<input type="text" name="" v-model='searchInfo' @keyup='inputKeyUp()'>
<!--由于baidu及各大搜索引擎的搜索功能返回的是html页面并重新渲染,这里就不做搜索功能了;-->
<div class="submit" onselectstart="return false;" @click=''>
搜索
</div>
<!--展示搜索结果-->
<transition name='fade'>
<div class="searchResult" v-show='searchResult.length>0'>
<ul>
<li v-for='(item,i) in searchResult' v-show='i<5' @click='choseSearch(item)'>{{item}}</li>
</ul>
</div>
</transition>
</div>
</div>
</template>
<script>
export default {
name: 'search',
data(){
return {
searchInfo:'',//绑定输入框内容
searchResult:[],//存储返回结果
}
},
created(){
//点击body让搜索结果框小时,无关紧要
this.removeSearchResult();
},
methods:{
inputKeyUp(){
let url='https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+this.searchInfo+'&cb=searchFunction';
//这里是通过jsonp的方式调用接口,要先在window下注册creatScript和searchFunction方法,下面有描述;
window.creatScript(url).then(data=>{
log(data)
this.searchResult=data.s;
});
},
//选择搜索结果更换搜索框内容
choseSearch(item){
this.searchInfo=item;
//vue无法检测数组属性length的改变
this.searchResult.splice(0)
},
//body绑定点击事件,使搜索显示框消失
removeSearchResult(){
document.body.addEventListener('click',ev=>{
if(!(ev&&ev.target.className.indexOf('searchResult')>-1)){
this.searchResult.splice(0)
}
})
}
}
}
</script>
<style lang="less" scoped>
/*样式自己花点时间写一下吧*/
</style>
在src/common下创建base.js添加公共方法(vue有自己的方式将自定义函数属性添加到实例上,自己百度学习吧,是通过组件的方式引入,然后通过vue.的方式调用),这里东西不多,我们就简单粗暴点儿,直接在windows下添加方法,调用也简单;
//src/common/base.js代码比较简单,不多解释了
window.log=console.log;
window.searchFunction = function(val) {
window.searchInfo = val
//将搜索结果保存在searchInfo 中
};
window.creatScript=function(url) {
//选择promise是它的then方法用起来方便
return new Promise((resolve, reject) => {
let script = document.createElement('script');
script.id = 'removeScript';
script.src = url;
document.body.appendChild(script);
script.onload = function() {
resolve(window.searchInfo);
document.body.removeChild(document.getElementById('removeScript'));
}
});
};
在main.js中引入
import './common/base'
再看页面,效果出来了:
二、获取juejin文章列表
到目前为止还是没有写后端代码,接下来通过调用juejin接口来看看一个简单接口怎么写
写之前先整理下思路:要写个展示组件,一个后端接口,在把它们联系起来;
1.写组件,在src/views下创建juejinResources.vue文件
//juejinResources.vue(业务代码不再赘述)
<template>
<div class="juejinResources w60">
<ul>
<li class="" v-for='(item,i) in juejinResources'>
<div class="clearfix littleTop">
<div class="fl red" v-show='!item.original'>热·</div>
<div class="fl pink" v-show='i<10'>专栏·</div>
<div class="fl inherit">{{item.user.jobTitle}}·</div>
<div class="fl inherit" @click='getJuejinResourcesUserInfo(item.user)'>{{item.user.username}}·</div>
<div class="fl inherit">{{item.createdAt|lastTime}}</div>
</div>
<div><a :href="item.originalUrl" class="title" target="_blank">{{item.title}}</a></div>
</li>
</ul>
</div>
</template>
<script>
export default {
data(){
return {
juejinResources:[],
}
},
created(){
this.getJuejinResources();
},
filters:{
lastTime(v){
if(v){
let val=new Date()-new Date(v)
let days = parseInt(val / (1000 * 60 * 60 * 24));
let hours = parseInt((val % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
let minutes = parseInt((val % (1000 * 60 * 60)) / (1000 * 60));
let seconds = ((val % (1000 * 60)) / 1000).toFixed(1);
return days>0?days+'天':hours>0?hours+'小时':minutes>0?minutes+'分钟':seconds+'秒'
}else{
return ''
}
}
},
methods:{
//使用vue-resource插件调用ajax;
getJuejinResources(){
this.$http.get('http://localhost:3000/juejinResources').then(data=>{
//像这样的接口地址应该像base.js一样有一个公共的配置文件统一管理,这里就不麻烦了直接写;
log(data)
this.juejinResources=data.body.d.entrylist;
})
},
getJuejinResourcesUserInfo(item){
window.toNewPage('https://juejin.im/user/'+item.objectId)
//base中添加的用js跳新页面的方法,模拟a标签,很简单
//base.js
//window.toNewPage=function(url){
// let a=document.createElement('a');
// a.href=url;
// a.target='_blank';
// document.body.appendChild(a);
// a.click();
// document.body.removeChild(a);
// }
},
}
}
</script>
<style lang="less" scoped>
/*css还是自己写吧,哈哈*/
</style>
安装vue-resource插件
npm i vue-resource -save
将vue-resource注册到vue中,修改main.js
//main.js
//....
import VueResource from 'vue-resource'
Vue.use(VueResource)
//....
2.写后端接口,在service目录下创建juejinResources.js
//juejinResources.js
var http = require('http');
var log = console.log;
var express = require('express');
var router = express.Router();
//这样的地址获取方式跟baidu的一样,慢慢试;
var url = "http://timeline-merger-ms.juejin.im/v1/get_entry_by_rank?src=web&limit=20&category=5562b415e4b00c57d9b94ac8";
//express自带路由分配
router.get('/', function(req, res) {
http.get(url, function(resquest) {
var html = '';
resquest.setEncoding('utf-8'); //防止中文乱码
//监听data事件,每次取一块数据
resquest.on('data', function(chunk) {
html += chunk;
});
//监听end事件,如果接口返回获取完毕,就执行回调函数
resquest.on('end', function() {
//接口返回的是字符串,中文是unicode码,做了处理才返回给前端
html=JSON.parse(unescape(html.replace(/\\u/g, '%u')))
res.status(200)
res.json(html)
})
})
})
module.exports=router;
3.接口写好以后就把它们联系起来,修改server.js
//server.js
//....在代码最后添加
//node分配路由的方式,多个服务就多写几个分配就行了
//juejinResources.js中用的router.git("/")会自动把"/juejinResources"拼在前面
app.use('/juejinResources',require('./service/juejinResources'))
其实挺简单,来看看效果:
如果接口是https请求,node环境可能会出现这样的报错:
解决办法网上有很多,但不一定有效,我的就不知道怎么解决了,所以都改成了http请求;
到这里:单页面组件-路由-后端接口-服务就都有了,开发模式,生产模式也都具备,爬虫也是用http或则https多次访问,获取方式跟这个其实是一样的,拿到数据想怎么玩都可以,放到自己数据库都没问题。
这个demo项目还不完整,缺少数据库和admin后台管理,结构已经有了,剩下的基本上就是板砖了,不再赘述。
到目前为止的目录结构:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。