帶你用 Vue 全家桶和 Node.js 完成一個聚合應用

平時會常常瀏覽一些網站充電,可是總是須要切換網站也很麻煩,因此就有了作這個小項目的想法。經過爬蟲抓取一些網站,而後整合在一個應用中。雖然是個簡單應用,可是五臟六腑俱全,適合 Vue 的新手學習。javascript

項目地址css

項目技術棧

  • Vue 全家桶
  • 語言:ES6
  • UI:這裏使用了Element-ui,畢竟小項目,不花時間在 UI 上
  • 後臺:express + superagent + cheerio

後端

使用 express 作接口,在請求接口時,同時使用 superagent 請求對應的地址抓取數據前端

app.get('/api/zaoduke', function (req, res, next) {
  // 請求接口附帶的參數
  let page = req.query.page
  superagent.get(`https://toutiao.io/subjects/11907?f=new&page=${page}`)
    .end(function (err, sres) {
      ...
    });
});複製代碼

而後經過 cheerio 解析返回的網頁源代碼 (相似 jqury) 的寫法,得到本身須要的數據,而後經過 express 回傳vue

var $ = cheerio.load(sres.text)
      var items = []
      var data = {
        items: items,
        hasMore: true
      }
      if ($('.post').length < 30) {
        data.hasMore = false
      }
      $('.post').each(function (idx, element) {
        var $element = $(element)
        // cheerio 沒有 innterText 的方法,因此經過 nodeType 去取只屬於這個元素的文本
        var $author = $element.find('.meta').contents().filter(function () {
          return this.nodeType === 3;
        });
        // 這裏取數據的方式和jqury是同樣的
        items.push({
          title: $element.find('.title>a').text().trim(),
          href: 'https://toutiao.io' + $element.find('.title>a').attr('href'),
          author: $author.text().trim()
        })
      })

      res.send(data)複製代碼

前端

路由相關

在 Vue 中使用插件必須調用 Vue.use(xxx)java

路由懶加載,固然小項目不作異步也徹底沒問題。當項目大了,可使用這個功能分割不一樣路由組件,這個能夠作到訪問才加載路由。node

const Lists = type => () => import('../page/Lists.js').then(m => m.default(type))複製代碼

由於主體內容樣式是同樣的,因此我經過使用不一樣 type 的方式來複用代碼。git

let router = new Router({ 
  // 想使用 scrollBehavior 必須用這個 mode
  mode: 'history',
  // 切換路由時內容滑動到底部
  scrollBehavior: () => ({
    y: 0
  }),
  routes: [{
    path: '/',
    redirect: '/zaoduke'
  },
  // 如下就是經過不一樣類型的 type 去複用代碼
  {
    path: '/raywenderlich/:page(\\d+)?',
    component: Lists('raywenderlich')
  },
  {
    path: '/csstricks/:page(\\d+)?',
    component: Lists('csstricks')
  }
  ...
  ]
})複製代碼

項目使用 Vuex 去管理數據,可是當手動刷新瀏覽器的時候,由於 Vuex 是全局變量,因此變量被銷燬,不能獲取正確的數據了。有種方式是將 state 存到 localStorage 中,在這裏我使用了還有種方法。Vue route 有個全局鉤子 beforeEach 他會在進入路由前調用。github

router.beforeEach((to, from, next) => {
  // 去獲取路由當前的頁碼,用於請求數據
  store.state.page = to.params.page || 1
  // 獲取 type
  store.state.type = to.path.match(/[a-zA-Z0-9]+/)[0]
  // 這裏必須調用 next,不然進不了路由
  next()
})複製代碼

接下來導出 router 便可,到這裏路由部分結束。vuex

export default router複製代碼

Vuex

使用 Vuex 仍是要注意下場景,畢竟使用這個插件仍是很繁瑣的。在這個項目中其實不使用 Vuex 也是能夠的,直接使用瀏覽器內置的方式去管理數據便可。由於代碼中 Vuex 用的不多,就一筆略過了express

PS: 在使用 Vuex 和 Vue route 要注意必須在 Vue 的實例中傳入。

new Vue({
  el: '#app',
  router,
  store,
  render: h => h(App)
})複製代碼

Vue

先看一下複用組件的代碼

// 內容組件
import List from './ray.vue'

export default function createListView(type) {
  return {
    // 經過 type 渲染,這個 type 須要在 List 中的 props 定義
    render(h) {
      return h(List, {
        props: {
          type
        }
      })
    }
  }
}複製代碼

List 組件中包含了三個子組件,分別爲頂部進度條,分頁組件,單個 li 組件。具體的 HTML 和 CSS 代碼你們能夠本身看一下,沒有任何難度。

在頁面中請求數據的代碼我選擇放在 mounted 中,由於只有在這個鉤子及之後才能夠訪問到 $refs 實例

async getData(page = 1) {
      // 已經在加載了,就返回
      if (this.isLoad) {
        return
      }
      this.isLoad = true
      // 調用進度條的 start 方法
      this.$refs.progress.start()
      // 這種異步請求方法寫起來簡單
      let data = await getRaywenderlichData(this.type, page)
      // 給數據賦值
      this.list = data.items
      // 判斷下一頁還有沒有數據,沒有的話禁用下一頁按鈕
      if (!data.hasMore) {
        this.noMore = true
      } else {
        this.noMore = false
      }
      this.isLoad = false
      this.$refs.progress.finish()
    }複製代碼

子組件給父組件通訊

// 當點擊上一頁或下一頁時調用
routerPush() {
      // 改變路由
      this.$router.push(`/${this.type}/${this.pageIndex}`)
      // 發送消息
      this.$emit('changePage', this.pageIndex)
    }
// 而後在父組件使用
<pageBreak class="pageBreak" 
      :type="type" 
      :isLoad="isLoad"
      :noMore="noMore" 
      @changePage="changePage">複製代碼

後記

你們有興趣的能夠在這個 issus 中回覆以爲不錯的網站,我會添加進項目。

若是在項目中發現了有什麼不解或者發現了 bug,歡迎提交 PR 或者 issue,歡迎大神們多多指點小弟🙏🙏🙏

項目地址,若是喜歡這個項目,歡迎 Star!

相關文章
相關標籤/搜索