平時會經常瀏覽一些網站充電,但是老是需要切換網站也很麻煩,所以就有了做這個小專案的想法。通過爬蟲抓取一些網站,然後整合在一個應用中。雖然是個簡單應用,但是五臟六腑俱全,適合 Vue 的新手學習。
專案技術棧
- 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 回傳
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)
。
路由懶載入,當然小專案不做非同步也完全沒問題。當專案大了,可以使用這個功能分割不同路由元件,這個可以做到訪問才載入路由。
const Lists = type => () => import(`../page/Lists.js`).then(m => m.default(type))複製程式碼
因為主體內容樣式是一樣的,所以我通過使用不同 type 的方式來複用程式碼。
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
他會在進入路由前呼叫。
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 即可,到這裡路由部分結束。
export default router複製程式碼
Vuex
使用 Vuex 還是要注意下場景,畢竟使用這個外掛還是很繁瑣的。在這個專案中其實不使用 Vuex 也是可以的,直接使用瀏覽器內建的方式去管理資料即可。因為程式碼中 Vuex 用的很少,就一筆略過了
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!