手動搭建vue+node單頁面(二)

手動搭建vue+node單頁面(二)

環境搭建好了,開始寫業務和後端接口代碼,這一篇講的內容也比較簡單,只適合小白參考;

環境搭建請參考 《手動搭建vue+node單頁面(一)》:https://segmentfault.com/a/11...css

項目地址:https://github.com/liubingyan...html

內容提要:
1.jsonp獲取baidu搜索框內容;
2.node調用juejin接口獲取前端文章列表;前端

開發過程當中不會講的太細,有疑問多百度;vue

1、獲取baidu搜索框內容
就是輸入的同時下來框展現的內容;
圖片描述

在用node調用百度接口時候發現返回的是gbk格式的內容,node解析遇到困難,因此改用jsonp的方式;node

首先在控制檯分析接口:
圖片描述webpack

返回值:
圖片描述git

很容易看出內容中json數據的‘s’就是咱們想要的內容;github

接口地址的url內容過長,咱們將這個地址複製到地址欄中通過反覆測試,最終獲得:
圖片描述web

對咱們有用的參數只有兩個:wd(輸入框的內容)和cb(返回時調用的方法名),接下來就能夠開發了;ajax

這個小功能的開發涉及的:
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'

再看頁面,效果出來了:

圖片描述

2、獲取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後臺管理,結構已經有了,剩下的基本上就是板磚了,再也不贅述。

到目前爲止的目錄結構:

圖片描述

相關文章
相關標籤/搜索