web 安全 XSS CSP CSRF 點擊劫持 OAuth cdnjavascript
/* * xss 攻擊預防 * 目前地址欄的xss : http://xxx.html?name=<script>alert(1)</script> * 輸入框,圖片,富文本 * chrome 會本身轉義 * * */ // 轉義 < > " ' 空格不轉義 function escapeHtml (str) { if(!str) return ''; // 地址欄的xss已經先被chrome 轉義了,這代碼無效了 str = str.replace(/&/g, '&') str = str.replace(/</g, '<') str = str.replace(/>/g, '>') str = str.replace(/"/g, '&quto;') str = str.replace(/'/g, ''') // html實體 return str } // js 轉義 // 地址欄的xss已經先被chrome 轉義了,這代碼無效了 // let url = JSON.stringify(xx), /* * 富文本xss 過濾 * 黑名單過濾 * 白名單容許 * 插件 npm i xss * * * */ // 富文本過濾 function filter (str) { if(!str) return ''; // 黑名單過濾太多太麻煩 str = str.replace(/<\s*\/?script\s*>/g,''); // 過濾 <script> str = str.replace(/javascript:[^'"]*/g,''); // 過濾 javascript:alert(1) str = str.replace(/onerror\s*=\s*['"]?[^'"]*['"]?/g,''); // 過濾 onerror return str; } // cheerio 解析html, 語法相似 jq function filter2 (html) { if(!html) return ''; var cheerio = require('cheerio'); // 引入 cheerio var $ = cheerio.load(html) // 白名單 var whiteList = { 'img':['src'], 'font':['color','size'], 'a':['href'] } $('*').each(function (index, elem) { if(!whiteList[elem.name]){ // 清空白名單不存在的標籤 $(elem).remove(); return; } for(var attr in elem.attribs){ if(whiteList[elem.name].indexOf(attr)===-1){ $(elem).attr(attr,null) // 清空白名單不存在屬性 } } }) } // npm i xss 使用第三方庫解決白名單 /* * csp 內容安全策略,用於指定哪些內容可執行 * 預防xss 攻擊 * * <meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';"> * 詳見 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CSP * */ // nodejs 防xss // ctx.set('X-XSS-Protection',1); // 0是關閉 1是打開防護 功能不全面 // // ctx.set(`Content-Security-Policy`,`default-src 'self'`) //csp內容安全策略 /* * CSRF: 第三方網站不用訪問你的網站,就拿到你的信息,並調用你的接口亂來 * * 打開某網站A,本身的帳號信息保存在cookie中被獲取,致使本身銀行卡的錢丟了 * 1.設置cookie只能本網站訪問 * cookie.set(sameSite = Strict) * * 2.加圖形驗證碼/TOKEN等,必須通過本網站,防CSRF * nodejs npm i ccap 生成圖形驗證碼 * * 3.加 token ,必須通過本網站,防CSRF, token的用戶體驗比圖形驗證碼好 * * 4. referer 不是很好,有些訪問沒有referer * 驗證referer值 禁止來自第三方網站的請求 * // nodejs * var referer = ctx.reques.headers.referer; * if(!/^https?:\/\/localhost/.test(referer)){ * console.log('error') * } * */ // nodejs: npm i crypto 加密 /* * 點擊劫持:原理:本身的網站被第三方用Iframe內嵌, * 本身點擊跳出來的網站後,發現本身的錢沒了。 * 垃圾郵件點擊劫持 * 本網站禁止內嵌,防止點擊劫持 * nodejs ctx.set('X-Frame-Options','DENY'); 禁止頁面內嵌 兼容ie8 * */ // 若是有iframe嵌入的話,二者值不一致,一刷新假頁面,頁面會跳轉到真正的頁面 // 若是被禁止js 則如下代碼無效了 if(top.location !==window.location){ top.location = window.location } // <iframe sandbox = '' > iframe 的sandbox 能夠禁用 js 等一堆功能 // http協議 竊聽,篡cuan改 // 生成ca證書,放入項目中,引用ca證書,使用https來訪問,proxy代理抓包則看不到任何請求數據 /* * 密碼 md5等加密 mysql 注入 文件上傳漏洞(後端文件內容檢查、用nginx讀取文件再返回、控制讀寫權限等,nodejs問題不大) * */ // 防止盜QQ,盜身份證進信息等 OAuth 引入 使用QQ登錄某網站,使用了用戶受權 // cdn 加速 防dns攻擊 https加密 token 時間戳, 簽名
es6html
let {sin, cos} = Math // 取出對應的參數 console.log(sin(30)) /** * 參數默認值 * @param name */ function f1 (name = 'kang') { console.log(name) } f1() /** * 擴展運算符 * @param a * @param param */ function f2 (a, ...param) { console.log(param) // [2,3,4,5,6,7] } f2(1, 2, 3, 4, 5, 6, 7) // 合併數組 let arr1 = [1, 2, 3] let arr2 = [4, 5, 6] let arr3 = [...arr1, ...arr2] console.log(arr3) /* * 箭頭函數不能new 不能使用 arguments 可用 rest代替, 箭頭函數中的this取決於函數的定義 * */ function Person () { } Person.name=123 // 靜態屬性 經過類名直接訪問
分頁 固定內容高 offsetHeight +滾動top scrollTop >= 實際高 scrollHeightvue
offsetWidth實際獲取的是盒模型(width+border + padding)
offsetHeight內容的高(內容高+padding+border)
<template> <div> <div class="list" @scroll.passive="scrollData" ref="scrollOut"> <p class="a" v-for="(val,key) in list" :key="key">{{val.id}}/{{val.name}}</p> </div> </div> </template> <script> export default { data () { return { list: [ {id: 1, name: 'aa'}, {id: 1, name: 'aa2'}, {id: 1, name: 'aa3'}, {id: 1, name: 'aa4'}, {id: 1, name: 'aa5'}, {id: 1, name: 'aa6'}, {id: 1, name: 'aa7'}, {id: 1, name: 'aa8'}, {id: 1, name: 'aa9'}, {id: 1, name: 'aa10'}, {id: 1, name: 'aa11'}, ], canLoad: true } }, created () { document.title = '首頁' }, methods: { /* * offsetWidth實際獲取的是盒模型(width+border + padding) offsetHeight內容的高(內容高+padding+border) 固定內容高 offsetHeight +滾動top scrollTop >= 實際高 scrollHeight * */ scrollData () { let outH = this.$refs.scrollOut.scrollHeight, currentH = this.$refs.scrollOut.offsetHeight, offH = this.$refs.scrollOut.scrollTop console.log('curr',currentH,offH,'out',outH) if (currentH + offH >= outH && this.canLoad) { this.fetchUser() } }, fetchUser () { console.log('push') this.list.push({ id: 2, name: 222 }) this.canLoad=false } } } </script> <style lang="less" scoped> .list { overflow-y: scroll; max-height: 200px; } .a { line-height: 30px; font-size: 16px; } </style>
基本數據類型:棧內存java
函數柯里化node
在一個函數中,首先填充幾個參數,而後再返回一個新的函數的技術,稱爲函數的柯里化。一般可用於在不侵入函數的前提下,爲函數 預置通用參數,供屢次重複調用。mysql
const add = function add(x) { return function (y) { return x + y } } const myadd = add(2) console.log(myadd(10)) // 屢次調用 console.log(myadd(20)) // 屢次調用
數組去重: [...new Set([1,2,2,3,4,4,5])] // [1,2,3,4,5]nginx
重繪:元素改顏色等樣式不影響佈局,重繪元素,損耗不大es6
迴流:元素尺寸、結構變了,瀏覽器會從新渲染頁面,稱迴流。 迴流必重繪。 web
** 避免頻繁操做樣式,可彙總後統一 一次修改,把 DOM 離線後修改,好比:先把 DOM 給 display:none
(有一次 Reflow),而後你修改 100 次,而後再把它顯示出來vue-router
頁面初次渲染
瀏覽器窗口大小改變
元素尺寸、位置、內容發生改變
元素字體大小變化
添加或者刪除可見的 dom 元素
激活 CSS 僞類(例如::hover)
查詢某些屬性或調用某些方法
clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、scrollWidth、
getComputedStyle()
getBoundingClientRect()
scrollTo()
內存泄露: 定時器未清除、閉包變量
XSS: 地址欄加代碼,轉義
CSRF: 第三方網站不用訪問你的網站,就拿到你的信息, 加圖片驗證碼、加 token等
點擊劫持:網站 被iframe, 禁止iframe js 判斷 top.location!== window.location 二者要等纔對
節流:scroll事件,resize窗口大小調整事件, 設置一個時間,時間到了,執行scroll 回調,這個時間內觸發的scroll事件,被忽略
防抖:scroll事件,用戶操做頻繁,只認中止操做最後一次scroll事件,仍是須要設置一個等待時間
apply call bind
let round = { pi: 3.14, area: function (r) { return this.pi * r * r } } console.log(round.area(2)) // pi改成3.14159 // 傳個object 來改變this的指向 console.log(round.area.apply({pi: 3.14159}, [10])) // apply(obj,[]) console.log(round.area.call({pi: 3.14159}, '10')) // call(obj,'str') let newRound = round.area.bind({pi: 3.14159}, '10') console.log(newRound()) // bind 要再調用一下
Object.freeze(obj) 凍結對象,不能修改屬性值,不報錯,修改無效
Object.seal(obj) 凍結對象屬性 ,不能刪除屬性,不報錯,刪除無效
MVVM: M 數據 V 視圖 VM 監聽數據改變動新視圖,因此只須要關注數據邏輯,不須要操做DOM
VUE響應式原理:
vue採用數據劫持結合發佈者-訂閱者模式的方式,經過Object.definedProperty()劫持各個屬性的setter,getter,在數據變更時,發佈消息給訂閱者,觸發相應的監聽回調
Observer.js 利用Object.definedProperty() 監聽屬性的getter, setter, 實現一個消息訂閱器,通知訂閱者Watcher,
Compile.js 解析模板指令,替換成數據,渲染視圖,先把dom節點轉換成文檔碎片fragment,提升性能
watcher.js 屬性變更,接收到消息通知時,調用自身的update()方法,觸發Compile中綁定的回調,更新視圖
給 data 添加新屬性 watch: {
obj: { handler (newValue, oldValue) { console.log('obj changed') }, deep: true } }this.$set(this.obj, 'b', 'obj.b')
數組的delete
let arr = [1,2,3,4]
delete arr[1] // 下標不變 0,2 3
vue的 this.$delete // 下標從新變成 0,1,2
vue-router 默認 hash模式,單頁應用,當url改變時,頁面不會從新加載
還有history模式,利用h5的history.pushState來跳轉,須要後臺配置一個靜態頁面,當url匹配不到時時進行跳轉
地址變動的監聽, window.addEventListener('hashchange',()=>{})
vue-router做爲一個插件,使用Vue.use()來註冊;new Vue()時會執行vue-router注入的beforeCreate()鉤子函數,執行 hashchange,會匹配到地址route, 賦值給vm._route, 被Vue的數據劫持攔截到,觸發視圖更新
vuex
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
// 只用 state 和 mutations 不用常量,不用getters(過濾state數據) 和 actions(異步提交 mutation)
export default new Vuex.Store({
state: {
money: 10
},
mutations: {
money (state, val) {
state.money = val
}
}
})
getState(){
console.log(this.$store.state['money']) // get
},
setState(){
this.$store.commit('money',30) // set
}
main.js 全局 setVuex getVuex
Vue.prototype.setVuex = function (key, val) {
return this.$store.commit(key, val)
}
Vue.prototype.getVuex=function (val) {
return this.$store.state[val]
}
fetch
es6新出,只有網絡故障時或請求被阻止時,纔會reject() 後端返回404 500,要本身處理 promise 以前 是 xmlHttpRequest
scoreCalcAPI.js
let develop = 'xx' // 開發 let release = 'xx' // 測試 /* * 將用戶在各應用的行爲,發送給會管家,由會管家計算用戶的分享,分銷積分. * obj: 接口參數對象 * obj={ AppId: "xx", // 應用Id UserId: "xx", // 用戶Id MarkId: "xx", // 觸發表主鍵Id,如添加了一條分享邀約,此處填分享邀約的Id MarkDesc: "xx", // 觸發內容,如添加了一條分享邀約,此處填分享邀約標題,如果評論,則填寫評論的內容. ChannelCode: "xx", // 渠道編號 RuleType: xx, // 數值 } * */ function scoreCalcAPI (obj) { let URL, href = window.location.href if (href.indexOf('http://p')>0) { URL = production } else { URL = release } let xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState === 4 && xmlhttp.status === 200) { // 請求成功 console.log(xmlhttp.responseText); } } xmlhttp.open("post", URL+ 'api/commonapi/pushpoint', true); xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlhttp.send("pointdata=" + JSON.stringify(obj)); }
Synthesis.js canvas圖片合成
export class SynthesisShare { constructor(w, h) { //初始化canvas let canvas = document.createElement("canvas") canvas.width = w canvas.height = h this.canvas = canvas this.ctx = canvas.getContext("2d") } /** * 合成圖片元素 * @param elements 一個包含了SynthesisElement類的數組 * @returns {string} 合成後圖片數據 */ synthesisImage(elements) { this.clear() if (Array.isArray(elements)) { for (let i = 0; i < elements.length; i++) { let e = elements[i] this.drawElement(e) } } else { this.drawElement(elements) } return this.canvas.toDataURL('image/png') } drawElement(e) { this.ctx.drawImage(e.image, e.x, e.y, e.w, e.h) } /** * 方便屢次複用canvas,清理畫面 */ clear() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); } } /** * 合成元素類 */ export class SynthesisElement { constructor() { this.url = '' this.x = 0 this.y = 0 this.w = 0 this.h = 0 this.image = null } /** * 初始化合成元素 * @param url 圖片地址 * @param x x座標 * @param y y座標 * @param w 寬 * @param h 高 * @returns {Promise<void>} */ async init(url, x, y, w, h) { this.url = url this.x = x this.y = y this.w = w this.h = h this.image = await this.loadImage(url) } /** * 加載元素圖片 * @param url * @returns {Promise<any>} */ loadImage(url) { // console.log(url); return new Promise((resolve, reject) => { let img = new Image(); img.setAttribute('crossOrigin', '*'); if (!url) { reject('err') } img.src = url img.onload = () => { resolve(img) } }) } }