//數據地址:https://www.ele.me/restapi/shopping/restaurants?extras%5B%5D=activities&geohash=ws0e5f7pu8xe&latitude=23.041037&limit=24&longitude=113.372243&offset=0&terminal=web
let imgCount = 0,imgIndex = 0,dataCount = 0,dataIndex = 0
// 獲取的圖片總數 正在處理的圖片下標 獲取商家的總數 正在處理的商家數據下標
const start = () => { //開始調用數據接口使用UTF8編碼獲取數據
https.get(url, (res, req) => {
res.setEncoding('utf8')
let data = ""
res.on("data", (chunk) => {
data += chunk
}).on("end", () => {
gatData(JSON.parse(data)) //JSON格式化一下,再去對數據作處理,過濾一些本身想要的數據
})
})
}
複製代碼
Eleme的地區商家列表數據過濾與處理 🌰
const gatData = (data) => {
let arr = [],imgarr = []
data.forEach((item, index) => {
let obj = {
sid:item.id, //商家ID
name:item.name, //商家名字
address: item.address,
latitude: item.latitude, //商家經度
longitude: item.longitude, //商家緯度
icon: getImg(item.image_path).src //商家圖標
//此處省略一大堆過濾數據
}
if(imgarr.indexOf(item.image_path) === -1){
imgarr.push(item.image_path)
}
arr.push(obj)
})
imgCount = imgarr.length - 1
dataCount = arr.length - 1
console.log("共獲取圖片:" + imgarr.length + "張")
console.log("共獲取數據:" + arr.length + "條")
}
複製代碼
Eleme商家列表的圖片獲取 🌰
//圖片獲取
const saveImg = (_src,list) => {
let src = getImgType(_src).src, //獲取圖片的地址
name = getImgType(_src).type //根據本身需求肯定保存圖片的名字
https.get(src, (res) => {
let imgData = ''
res.setEncoding("binary") //注意請求返回的編碼
res.on('data', (chunk) => {
imgData += chunk
})
res.on('end', () => {
fs.writeFile("./保存圖片的路徑/" + _src + "." + name, imgData, 'binary', (error) => {
if(error) {
console.log('下載失敗')
}
imgIndex++
console.log('下載成功!',src)
if(imgIndex === imgCount){
return
}
download(list)
})
})
})
}
複製代碼
Eleme商家列表的數據庫的寫入-> 使用MySQL的 🌰
const saveDB = (list,i) => {
if(dataIndex === dataCount + 1){
return console.log("所有插入完成!!!!")
}
//此處對數據處理 ----- 必須對數據的格式作些處理
let SQL = "INSERT INTO `ele_seller` (`sel_id`, `sel_name`,....) VALUES (NULL, '" + list[i].name + "', "....");"
db.query(SQL, function(error, rows) {
if(error) {
return console.log(error)
}
dataIndex++
saveDB(list,dataIndex)
console.log("插入數據庫成功!!!__")
})
}
//這是在人家沒有對返回的數據進行加密、還有沒有對請求域攔截的狀況下可行
複製代碼
下面是關於某商城直接抓頁面的數據 🌰
//獲取數據的來源:https://www.yohobuy.com/list/ci56-gd1.html?page=1
//先是獲取左側商品的分類,再經過分類獲取具體的所屬商品
// 上衣(所有)下裝(所有)鞋子(所有)包類(所有)居家生活(所有)服配(所有)
const start = () => {
let _url = "https://www.yohobuy.com/list/ci" + typeArr[typeIndex] + "-gd1.html?page=" + page
superagent.get(_url).end((err, res) => {
if(err) {
return console.log("err", err)
}
gatData(res.text,typeArr[typeIndex] + "--" + page)
})
}
const gatData = (html) => {
let $ = cheerio.load(html),sknArr = []
$(".good-info").map((index, item) => {
let obj = {
id:$(item).attr("data-skn"),
img:imgForm($(item).find("img").eq(0).attr("data-original"))
//此處省略一大堆數據
}
sknArr.push(obj)
})
console.log(sknArr)
} //使用Http直接請求頁面會返回403,經過superagent模塊去請求頁面,再經過cheerio抓取對應數據,這種方式獲取的數據不全
複製代碼
express的數據加密 -> AES加密 🌰
//加密的模塊不少,推薦使用crypto、crypto-js
//這裏的加密解密是把加密向量一同發送的,每次加密的加密向量都是一段固定長度的隨機字符串,因此每次請求同一個URI,返回的數據同樣,可是返回的加密數據是不同的
//加密 data爲須要加密的數據
const encode = data => {
let key = cryptojs.enc.Latin1.parse(加密的KEY),
ivs = crypt(), //這只是固定長度的隨機字符串
iv = cryptojs.enc.Latin1.parse(ivs) //加密向量
let backdata = cryptojs.AES.encrypt(JSON.stringify(data), key, {
iv: iv,
mode: cryptojs.mode.CBC,
padding: cryptojs.pad.ZeroPadding
}).toString()
let json = {
data:backdata,
iv:ivs
}
return json
}
//解密
const decode = data => {
let key = cryptojs.enc.Latin1.parse(加密的KEY),
iv = cryptojs.enc.Latin1.parse(config.data.result.iv) //加密向量
let decrypted = cryptojs.AES.decrypt(config.data.result.data, key, {
iv: iv,
padding: cryptojs.pad.ZeroPadding
})
let data = config.data
data.result.data = JSON.parse(decrypted.toString(cryptojs.enc.Utf8))
return data
}
複製代碼
路由的鑑權(可使用session,寫文件,[token](https://mp.weixin.qq.com/s/82kGtrI1QK7gkswtd-QsAQ)等等) 🌰
token通常格式爲:字符串A.字符串B.字符串C,字符串A爲Header(頭部),字符串B爲Payload(負載),字符串C爲Signature(簽名)html
SESSION,可使用redis或者寫文件的方式保存用戶的登陸方式、逾期時間等等前端
* 這裏以Token爲例子,JWT(Json Web Token)爲服務器無狀態token,一旦確認用戶身份,就頒發token,知道token過時以前,都是有權請求資源的
* Token的加密方法不少,默認採用HMAC SHA256算法加密
const jwt = require("jsonwebtoken")
//頒發Token
const getToken = data => { //data必須爲JSON格式的對象,保存用戶非敏感的元信息
jwt.sign(data, "加密的KEY", {
expiresIn: 60 * 60 * 24, //token的過時時間
algorithm: "HS256", //token的加密方式
subject: "webside" //token主題
}) //token帶的參數
}
//驗證token
jwt.verify(token, "加密的KEY", (err, decoded) => {
if(err) {
return false
}
return decoded //decoded爲token前兩個json的數據
})
複製代碼
express路由的搭建與路由攔截鑑權 🌰
//設置全部路由公用的配置
app.all("*", function(req, res, next) {
//CROS 設置跨域、請求的方式、容許攜帶的請求頭等等
if(req.path !== "/" && !req.path.includes(".")) {
res.header("Access-Control-Allow-Origin", req.headers["origin"] || "*");
res.header("Access-Control-Allow-Credentials", true);
res.header("Access-Control-Allow-Headers", 'Content-Type,Content-Length, Authorization,\'Origin\',Accept,X-Requested-With');
res.header("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE,OPTIONS");
res.header("Content-Type", "application/json;charset=utf-8");
}
next() //路由一級一級往下傳遞,必須定義next纔會執行傳遞下個路由
})
app.use("/api/eleme/catalog", require("./eleme/getCatalog"), (req, res, next) => {
console.log("eleme => 獲取商家經營分類")
})
app.use("/api/eleme/map", require("./eleme/getAddress"), (req, res, next) => {
console.log("eleme => 獲取城市地區列表")
})
//--------------
//上面路由匹配的是從/api/eleme/子路由進去
//下面路由匹配爲/api/eleme => 定義相同 /api/eleme/子路由的權限鑑定
//--------------
app.use("/api/eleme", (req, res, next) => {
let token = req.body.token || req.query.token || req.headers['x-access-token']
if(token) {
//權限的鑑定
next() //成功才匹配/api/eleme/下的子路由
} else {
//獲取token失敗,token的獲取與存放在什麼位置自定義就好
}
})
//上面一個/api/eleme/路由開啓鑑定,下面的路由必須經過上面路由的鑑權成功才能訪問
app.use("/api/eleme/search", require("./eleme/getSearch"), (req, res, next) => {
console.log("eleme => 商家搜索")
})
複製代碼
Vue生命週期 🌰
生命鉤子函數 | Data | DOM | 描述 | 適用環境 |
---|---|---|---|---|
beforeCreate | 未初始化 | 未初始化 | loading加載效果 | |
created | 初始化 | 未初始化 | 屬性綁定DOM,不存在,$el不存在 | 結束loading |
beforeMount | 初始化 | 初始化 | $el爲元素的虛擬節點 | |
mounted | 不保證組建在document中,$el被DOM替換 | ajax請求 | ||
beforeUpdate | 組件更新以前 | |||
updated | 組件更新以後 | |||
activated | 使用keep-alive,組件被激活時使用 | |||
deactivated | 使用keep-alive,組件被移除時使用 | |||
beforeDestory | 組件被銷燬前調用 | 判斷組件是否應被刪除 | ||
destroyed | 組件被銷燬後調用 | 組件移除 |
Vue路由的搭建 🌰
//使用路由懶加載
const 頁面A = resolve => require(['./../view/頁面A.vue'], resolve)
const 頁面B = resolve => require(['./../view/頁面B.vue'], resolve)
//路有鉤子 將公用的路由鉤子業務扔到公用的鉤子函數,也能夠給組件單獨設置
beforeRouteEnter (to, from, next) {
// 在渲染該組件的對應路由被 confirm 前調用
// 不!能!直接獲取組件實例 `this`,在next中能夠用參數獲取間接獲取實例
// 由於當鉤子執行前,組件實例還沒被建立
}
beforeRouteUpdate (to, from, next) {
// 在當前路由改變,可是該組件被複用時調用
// 舉例來講,對於一個帶有動態參數的路徑 /foo/:id,在 /foo/1 和 /foo/2 之間跳轉的時候,
// 因爲會渲染一樣的 Foo 組件,所以組件實例會被複用。而這個鉤子就會在這個狀況下被調用。能夠訪問組件實例 `this`
}
beforeRouteLeave (to, from, next) {
// 導航離開該組件的對應路由時調用 能夠訪問組件實例 `this`
}
const router = new Router({
mode: 'history',
routes: [{
name: 'index',
path: '/',
component: ElemeView,
meta:{ //對應路由的元信息 => 可動態設置路由下的title、description
title:"首頁",
description:"這是對首頁的描述"
}
redirect: '/home',
children: [{
name: 'home',
path: 'home',
component: ElemeHome
}, {
name: 'order',
path: 'order',
component: ElemeOrder,
redirect: '/order/home'
}]
},{
name: 'orderInfo',
path: '/order/:id',
component: OrderInfo,
meta:{
title:"訂單詳情頁",
description:"這是對訂單詳情頁面的描述"
}
}, {
path: '*',
redirect: '/home'
}]
})
export default router
//當動態對meta設置的時候
router.beforeEach((to, from, next) => {
if (to.meta.title) { //當有title或者其餘的元信息時,能夠手動動態設置(並不知道會不會對SEO有沒有效)
document.title = to.meta.title
}
next() //傳遞下去
})
複製代碼
vuex 單向數據流的模塊化編寫 🌰
const Store = new Vuex.Store({
getters: {
router: state => state.common.router,
cartList: state => state.cart.cartList,
cartCount: state => state.cart.cartCount,
cartPay: state => state.cart.cartPay,
sellerInfo: state => state.common.sellerInfo,
sellerList: state => state.common.sellerList,
orderList: state => state.order.orderList
},
mutations: {
getState(state){
console.log(state) //state爲全局state
}
},
modules: {
common: commonStore,
order: orderStore,
cart: shopcartStore
}
})
//組件中調用某模塊的mutations時
this.$store.commit("order/finishOrder", e.target.dataset.id)
this.$store.commit("模塊名字/該模塊下的方法名字", 傳過去的數據)
//組件中使用異步actions
this.$store.commit("模塊名字/actions名字")
//直接調用全局的mutations或actions時,不須要加模塊名即可以調用
複製代碼
ajax庫 -> 順便在ajax攔截器中將後臺的數據解密 🌰
//以axios爲例子,其實都差很少的 加密解密方法上面提到,跟服務器的方法同樣
axios.defaults.timeout = 10000 //請求超時時間
axios.defaults.retry = 4 //請求失敗後,繼續請求,請求次數爲5次
axios.interceptors.request.use((config) => { //發送請求的攔截
config.headers["Content-Type"] = "application/x-www-form-urlencoded"
//在這裏加密
return config
})
axios.interceptors.response.use((res) => { //響應請求的攔截
if(res.status === 654) {
window.alert('請求超時!')
}
if(res.data.code < 200 || res.data.result > 300) {
window.alert('數據返回有誤')
return Promise.reject(res)
}
//在這裏解密 -> 直接將解密的數據返回,其他的無關字段能夠不用管
}, (error) => { //失敗的後續操做
let config = error.config
if(!config || !config.retry) return Promise.reject(error)
config.__retryCount = config.__retryCount || 0
if(config.__retryCount >= config.retry) {
return Promise.reject(error)
}
config.__retryCount += 1
let backoff = new Promise(function(resolve) {
setTimeout(function() {
resolve()
}, config.retryDelay || 1)
})
return backoff.then(function() {
return axios(config)
})
})
export default axios
複製代碼
一些比較好看的vue組件 🌰