2020/03/30 周一
#JSON數(shù)據(jù)轉(zhuǎn)Blob后勺远,怎么還原
在axios請(qǐng)求下載文件接口時(shí)翘地,一般設(shè)置responseType: 'blob'馏予,文件返回正常就沒問題吃谣,但后臺(tái)如果處理文件或鑒權(quán)有問題酪碘,接口返回了包含錯(cuò)誤信息的json格式數(shù)據(jù),那樣json數(shù)據(jù)也會(huì)轉(zhuǎn)為Blob對(duì)象吉嫩,而前端有必要將錯(cuò)誤信息展示的其掂,那怎么將Blob數(shù)據(jù)轉(zhuǎn)JSON呢?下面來看看
let fileType = res.headers['content-type']
if (fileType.startsWith('application/json')) {
let reader = new FileReader();
reader.addEventListener("loadend", function() {
let data = JSON.parse(reader.result)
console.log(data);
});
reader.readAsText(res.data, "UTF-8") // 加UTF-8防止中文亂碼
return
}
#2020/03/28 周六
#網(wǎng)頁dark mode(深色模式)適配
微信最近推出了深色模式吴菠,我試了下者填,手機(jī)切換時(shí)頁面效果樣式是實(shí)時(shí)刷新的。于是就想著web怎么能夠監(jiān)聽深色模式做葵,并設(shè)置樣式占哟。查了資料后,在Stack Overflow上找到了答案
通過css里的媒體查詢就能適配深色模式,先來看看怎么用js獲取當(dāng)前是否是深色模式
// 獲取當(dāng)前是否是深色模式
// window.matchMedia('(prefers-color-scheme: dark)').matches
window.matchMedia && console.log('Is dark mode: ', window.matchMedia('(prefers-color-scheme: dark)').matches)
// 用js監(jiān)聽深色模式的切換事件
window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => {
console.log('dark mode change榨乎,已' + (event.matches ? '進(jìn)入': '退出') + 'dark mode')
})
window.matchMedia到底是用來做什么的怎燥?我查了下mdn,發(fā)現(xiàn)了這樣一個(gè)示例
let mql = window.matchMedia('(max-width: 600px)');
document.querySelector(".mq-value").innerText = mql.matches;
從這個(gè)例子看蜜暑,大概就知道怎么用css來支持dark模式了吧铐姚,就是加一個(gè)類似小屏適配的一個(gè)媒體查詢樣式,來看個(gè)例子
/* dark mode support */
@media (prefers-color-scheme: dark) {
body {
background-color: black;
color: #aaa;
}
body .content article, header, aside > div, footer {
border-color: #333;
color: #aaa;
background-color: black;
box-shadow: 0 0 10px #333;
}
}
深色模式下肛捍,一般將背景調(diào)暗隐绵,字體設(shè)置為偏白色即可。zuo11.com 已用上面的方法適配了深色模式拙毫,可以體驗(yàn)下依许。網(wǎng)站是開源的,zuo11.com深色模式支持代碼 - github(opens new window)
參考:
- How do I detect dark mode using JavaScript? - Stack Overflow(opens new window)
- window.matchMedia | MDN(opens new window)
- Supporting Dark Mode in Your Interface | Apple Developer Documentation(opens new window)
#2020/03/27 周五
#canvas繪制模糊的問題
今天發(fā)現(xiàn)同樣的代碼在兩臺(tái)電腦上繪制的一個(gè)清晰缀蹄,一個(gè)模糊峭跳,后來查資料發(fā)現(xiàn)確實(shí)有這個(gè)問題
因?yàn)閏anvas不是矢量圖,高dpi屏幕每平方英寸有更多的像素缺前,也就是兩倍屏蛀醉,瀏覽器會(huì)以兩個(gè)像素點(diǎn)的寬度來渲染一個(gè)像素,所以在Retina屏上會(huì)導(dǎo)致圖片衅码、文字都會(huì)模糊拯刁,怎么解決呢?
獲取設(shè)備像素比:window.devicePixelRatio || 1
如果繪制的實(shí)際區(qū)域大小為 750 * 40肆良,假設(shè)設(shè)備像素比為2筛璧,那么,canvas的width惹恃、height需要設(shè)置為 1500 * 80夭谤,然后用內(nèi)聯(lián)樣式設(shè)置width為750,height為40巫糙,相當(dāng)于canvas繪制2倍的大小朗儒,然后再縮放,這樣就清晰了参淹。
綜上醉锄,在canvas繪制時(shí),各種長度一定要考慮乘以devicePixelRatio浙值,不然可能顯示的不清晰
參考:解決 canvas 在高清屏中繪制模糊的問題(opens new window)
#2020/03/26 周四
#canvas不支持文本換行怎么處理
今天在stackoveflow里面搜索ctx.fill的問題時(shí)恳不,查到了很多關(guān)于canvas ctx.fillText()繪制文本時(shí)不支持換行的問題,找到了一個(gè)比較好的答案
I'm afraid it is a limitation of Canvas' fillText. There is no multi-line support. Whats worse, there's no built-in way to measure line height, only width, making doing it yourself even harder!
一般解決思路是开呐,根據(jù) ctx.measureText('Hello').width 來看需要顯示的文字是否需要換行烟勋,寫一個(gè)for循環(huán)來處理
參考:
- canvas繪制文本內(nèi)容自動(dòng)換行(opens new window)
- javascript - HTML5 canvas ctx.fillText won't do line breaks? - Stack Overflow(opens new window)
#canvas繪制不規(guī)則形狀填充漸變色
在JS高程3中规求,有一章專門將使用canvas繪圖,今天終于用上了卵惦,效果還不錯(cuò)阻肿,來看效果,原生js沮尿,70行不到
<canvas id="drawing1" width="720" height="45" >A draw of something.</canvas>
<script>
drawStatus('drawing1', 2)
function drawStatus(domId, position) {
let str = ['① 狀態(tài)一', '② 狀態(tài)二', '③ 狀態(tài)三', '④ 狀態(tài)四', '⑤ 狀態(tài)五', '⑥ 狀態(tài)六']
let config = {
width: 100,
height: 40,
extendLength: 20,
radius: 4
}
let config2 = { ...config, width: 110 }
let cur = str.length - position
str.reverse().forEach((item, index) => {
let pos = str.length - index - 1
let x = 0 + (str.length - 1) * config.radius
if (pos !== 0) {
x = (pos * 100) + (pos -1) * 10 + (str.length - 1 - pos) * config.radius
}
console.log(pos,x)
let curConfig = pos === 0 ? config : config2
if (pos < (str.length - cur)) {
curConfig.isFocus = true
}
drawUnnormalShape(domId, x , 0, str[index], curConfig)
})
}
function drawUnnormalShape(domId, x, y, text, config) {
let drawing = document.getElementById(domId);
let ctx = drawing.getContext('2d');
let { width, height, extendLength, radius, isFocus } = config
ctx.beginPath(); // 如果都需要重新beginPath 不然丛塌,后面的fill會(huì)覆蓋前面的fill
// 不規(guī)則矩形
ctx.moveTo(x + radius, y) // 從左上角 (x + radius, y) 位置開始
ctx.arcTo(x, y, x, y + radius, radius) // 左上圓角
ctx.lineTo(x, y + height - 2 * radius) // 畫左邊
ctx.arcTo(x, y + height, x + radius, y + height, radius) // 左下圓角
ctx.lineTo(x + width - radius, y + height) // 下邊
// ctx.arcTo(x + width - radius, y + height, x + width - radius, y + height - 1, 1) // 圓角
let extendEndX = x + width + extendLength
let middleHeight = y + height / 2
ctx.arcTo(extendEndX, middleHeight, extendEndX - radius, middleHeight - radius, radius) // 線 + 圓角
ctx.lineTo(x + width - radius, y)
ctx.lineTo(x + radius, y)
var gradient = ctx.createLinearGradient(x, y + height / 2, x + width + extendLength, y + height / 2); // 從(130,130)到(160,160)漸變
gradient.addColorStop(0, isFocus ? '#62ccff' : '#fff'); // 漸變的起點(diǎn)色
gradient.addColorStop(1, isFocus ? '#0486fe' : '#fff'); // 漸變的結(jié)束色
ctx.shadowOffsetX = 6;
ctx.shadowOffsetY = 2;
ctx.shadowBlur = 16; // 模糊像素
ctx.shadowColor = "rgba(58, 86, 111, 0.15)";
ctx.fillStyle = gradient
ctx.fill() // ctx.stroke()
let textArr = text.split(' ')
ctx.font = "15px arial"
ctx.fillStyle = isFocus ? '#fff' : '#ccc'
ctx.fillText(textArr[0], x + width / 2 - 20, y + height / 2 + 5)
ctx.font = "11px arial"
ctx.fillText(textArr[1], x + width / 2, y + height / 2 + 4)
}
</script>
參考之前的筆記:使用canvas繪圖 - JS高程3筆記(opens new window)
完整demo: canvas畫不規(guī)則形狀填充漸變背景 - github(opens new window)
#canvas 多次fill會(huì)覆蓋前面的fill
在使用canvas進(jìn)行繪圖時(shí),封裝了一個(gè)繪制函數(shù)畜疾,每次都會(huì)填充顏色 ctx.fill()赴邻,如果多次執(zhí)行,只會(huì)在最后一次時(shí)啡捶,整體fill一次乍楚?
剛開始以為是后面的fill覆蓋了前面的fill,后來網(wǎng)上查了下届慈,第一次fill后,再次fill需要再次調(diào)用ctx.beginPath()忿偷,不然只會(huì)在最后一次fill金顿。
參考: HTML5的canvas標(biāo)簽為什么會(huì)覆蓋之前畫的東西的顏色 (opens new window)
#2020/03/25 周三
#根據(jù)文件名后綴判斷文件類型不準(zhǔn)確
比如我有個(gè)1.png文件,我修改下后綴名 1.txt鲤桥,那前端如果僅憑文件名的后綴來顯然是不行的揍拆,我們需要根據(jù)文件類型的二進(jìn)制數(shù)據(jù)標(biāo)記來判斷對(duì)應(yīng)的文件類型,這樣才會(huì)更加準(zhǔn)確茶凳,安全性更高
#怎么判斷兩個(gè)文件一模一樣
一般文件的md5可能會(huì)有重復(fù)的嫂拴,怎么減少這種概率呢?校驗(yàn)分三個(gè)部分
- 比較整個(gè)文件的md5
- 選擇文件固定位置的幾個(gè)片段分別計(jì)算md5進(jìn)行比對(duì)
- 比較文件名是否一樣
#md5加密是可逆的嗎贮喧?
理論上md5加密后筒狠,在不知道原始消息的前提下,是無法憑借16個(gè)字節(jié)的消息摘要(Message Digest)箱沦,還原出原始的消息的.
但為什么有些網(wǎng)站可以破解md5加密后的密碼呢辩恼?主要是使用的碰撞檢測。它會(huì)提前算出一些常用弱密碼的md5值谓形,一個(gè)個(gè)比較灶伊。才會(huì)讓人產(chǎn)生md5可逆的錯(cuò)覺。一般除了md5加密外寒跳,我們可以再多進(jìn)行一些處理(加鹽)聘萨,來進(jìn)行干擾,提高破解難度
參考:為什么說 MD5 是不可逆的童太?(opens new window)
#兩個(gè)不同文件md5可能一樣嗎米辐?
什么是md5胸完?md5 是 messge digest [da??d?est] 5 的縮寫,意思是信息摘要算法
linux下儡循,在terminal中執(zhí)行man md5舶吗,可以查看對(duì)應(yīng)的文檔
md5 -- calculate a message-digest fingerprint (checksum) for a file
md5 -- 為一個(gè)文件計(jì)算信息摘要指紋('校驗(yàn)和'或'校驗(yàn)碼')
md5 -s '123456'
# MD5 ("123456") = e10adc3949ba59abbe56e057f20f883e
md5 1.txt
# MD5 (1.txt) = 6f74626e0749e5353cc7e11767418d43
從上面的例子中我們可以看到,將文件或字符串進(jìn)行 md5校驗(yàn) 會(huì)生成一個(gè) 32位 的校驗(yàn)碼择膝。問題來了誓琼,網(wǎng)上看到 md5加密后一般是128位,而這里只有32位為什么呢肴捉?我們要分清16進(jìn)制與2進(jìn)制腹侣,標(biāo)準(zhǔn)說法是,md5加密后的字符為 128bit(16字節(jié))齿穗,而一個(gè)我們看到的32位是16進(jìn)制傲隶,每一位都可以轉(zhuǎn)為4bit,也就是4個(gè)二進(jìn)制位窃页。1 - f 分別對(duì)應(yīng) 0000 - 1111跺株,所以128bit
一個(gè)二進(jìn)制位(bit)只能表示0或1兩種情況,128bit可以表示 Math.pow(2, 128) 2的128次方種情況脖卖,定死了乒省,最多只能表示這么多種情況,不同內(nèi)容的文件實(shí)在是太多了畦木,理論上絕對(duì)是會(huì)超過2的128次方的袖扛。
綜上:兩個(gè)不同文件md5是有可能相同的,因?yàn)閙d5最多只能表示2的128次方種情況十籍,而不同的文件絕對(duì)大于這個(gè)數(shù)
雖然兩個(gè)文件的md5可能一致蛆封,但給定一個(gè)文件的md5值,想偽造另一個(gè)文件的md5值與該值一樣勾栗,相對(duì)還是比較困難的惨篱,因此可用于判斷文件完整性
參考: 有沒有兩個(gè)完全不一樣的文件,但是他們的md5值是一樣的械姻?(opens new window)
#2020/03/22 周日
#koa-multer與@koa/multer邏輯差異
之前有了解過以@開頭的作用域包妒蛇,這次在使用koa-multer這個(gè)模塊時(shí),發(fā)現(xiàn)@koa/multer與koa-multer的邏輯居然不一樣楷拳。源碼有些差異绣夺,下面來具體看看
// 在使用 koa-multer 時(shí)
const multer = require('koa-multer')
router.post('/test', multer().none(), ctx => {
let isFormData = ctx.headers['content-type'].startsWith('multipart/form-data')
// ctx.req node的request對(duì)象, ctx.request koa的request對(duì)象
ctx.body = isFormData ? ctx.req.body : ctx.request.body
})
koa-multer這個(gè)包是從express的multer包上面加了一層封裝,而koa-multer并沒有把fileds字段掛載到ctx.request.body上欢揖,只維持原來express那樣掛載到node的request對(duì)象上陶耍,也就是ctx.req.body,來看看koa-multer的源碼部分
// https://github.com/koa-modules/multer/blob/master/index.js
multer[name] = function () {
const middleware = fn.apply(this, arguments)
return (ctx, next) => {
return new Promise((resolve, reject) => {
middleware(ctx.req, ctx.res, (err) => {
err ? reject(err) : resolve(ctx)
})
}).then(next)
}
}
而@koa/multer則做了處理她混,可以與上面的例子對(duì)比下
// https://github.com/koajs/multer/blob/master/index.js
multer[name] = function() {
const middleware = Reflect.apply(fn, this, arguments);
return (ctx, next) => {
return new Promise((resolve, reject) => {
middleware(ctx.req, ctx.res, err => {
if (err) return reject(err);
if ('request' in ctx) {
if (ctx.req.body) {
ctx.request.body = ctx.req.body;
delete ctx.req.body;
}
if (ctx.req.file) {
ctx.request.file = ctx.req.file;
ctx.file = ctx.req.file;
delete ctx.req.file;
}
if (ctx.req.files) {
ctx.request.files = ctx.req.files;
ctx.files = ctx.req.files;
delete ctx.req.files;
}
}
resolve(ctx);
});
}).then(next);
};
};
我們?cè)賮砜纯词褂聾koa/multer的情況烈钞,就比較方便了泊碑,和其他數(shù)據(jù)一樣掛載到 ctx.request.body
const multer = require('@koa/multer')
router.post('/test', multer().none(), ctx => {
ctx.body = ctx.request.body
})
綜上,如果某個(gè)模塊有兩種包名毯欣,建議先考慮@開頭的作用域包馒过,通常這種功能會(huì)新點(diǎn)。后面迭代維護(hù)應(yīng)該都是以這個(gè)為準(zhǔn)
#2020/03/20 周五
#npm包前面加@是什么意思(vue-cli與@vue/cli)
Vue CLI 的包名稱由 vue-cli 改成了 @vue/cli酗钞。 如果你已經(jīng)全局安裝了舊版本的 vue-cli (1.x 或 2.x)腹忽,你需要先通過 npm uninstall vue-cli -g 或 yarn global remove vue-cli 卸載它。
今天看vue-cli文檔砚作,發(fā)現(xiàn)上面的這段話 vue-cli 改為了 @vue/cli窘奏,這兩個(gè)npm有什么區(qū)別呢?npm包前面加@是什么意思葫录,查了下官網(wǎng)
npm包前面加@着裹,代表scopes相關(guān)的包,可以理解為作用域(范圍)包米同,作用域使我們可以創(chuàng)建與其他用戶或組織創(chuàng)建的包同名骇扇,而不會(huì)發(fā)生沖突。
A scope allows you to create a package with the same name as a package created by another user or Org without conflict.
作用域名稱是介于@和斜線之間的所有內(nèi)容:
The scope name is everything between the @ and the slash:
// “npm” scope:
@npm/package-name
// “npmcorp” scope:
@npmcorp/package-name
npm包一個(gè)詬病就是包名很容易被占用的問題面粮,占用后用其他人就不能用了匠题。而作用域包類似于創(chuàng)建了一個(gè)命名空間,不同的命名空間但金,可以使用相同的包名
作用域的命名不是誰便就能用的,只有兩種可以使用:自己的用戶名郁季、自己創(chuàng)建的組織名
注意:必須先注冊(cè)一個(gè)npm用戶帳戶冷溃,然后才能發(fā)布用戶作用域的npm軟件包。此外梦裂,要發(fā)布組織作用域的軟件包似枕,您必須創(chuàng)建一個(gè)npm用戶帳戶,然后創(chuàng)建一個(gè)npm Org(組織)年柠。
在 vue-cli 中可以用@vue/cli說明使用了vue這個(gè)npm賬號(hào)或者組織發(fā)布了該包凿歼。
參考:
- npm學(xué)習(xí)(十)之如何使用創(chuàng)建、發(fā)布冗恨、使用作用域包(opens new window)
- About scopes - Packages and modules | npm(opens new window)
- Creating and publishing scoped public packages | npm(opens new window)
#2020/03/18 周三
#md怎么加引用注釋答憔,腳注
這里有一個(gè)注腳[^1],這段話的還有其他意思[^2]在里面
[^1]:這里是注腳內(nèi)容
[^2]:這里是其他意思的注腳
注腳放到中間也可以
這里有一個(gè)注腳^1掀抹,這段話的還有其他意思^2在里面
注腳放到中間也可以
#md中鏈接的另一種寫法
我是一段文字虐拓,[baidu][1]、[qq][2]里面有鏈接
[1]: http://baidu.com "baidu"
[2]: http://qq.com "qq"
我是一段文字傲武,baidu (opens new window)蓉驹、qq (opens new window)里面有鏈接
#2020/03/17 周二
#Node.js的核心用處及應(yīng)用場景
打包構(gòu)建城榛、工程化,主要依賴基礎(chǔ)的fs模塊态兴,文件讀寫狠持,如xxx-cli(腳手架)、webpack瞻润、parcel喘垂、hexo,node在打包構(gòu)建敢订、前端工程化這塊基本影響了整個(gè)前端的開發(fā)過程王污,各框架基本都有基于node的cli,快速生成腳手架楚午,使開發(fā)更加高效昭齐、規(guī)范。
寫后臺(tái)接口矾柜,主要依賴基礎(chǔ)的http模塊阱驾,處理請(qǐng)求和響應(yīng)箫老,如 express.js疯淫、koa.js,一般主要用于模擬假數(shù)據(jù)接口, 調(diào)UI烹骨、交互效果以及做一些請(qǐng)求響應(yīng)方面的自測
綜合應(yīng)用:獲取數(shù)據(jù)+渲染頁面(高并發(fā)缆瓣、高性能)喧枷,koa.js對(duì)于開發(fā)商業(yè)化應(yīng)用來說還是比較單薄,egg.js基于koa做了一些增強(qiáng)弓坞,讓node也可以做企業(yè)級(jí)應(yīng)用隧甚。阿里的使用場景就是一個(gè)很好的例子,基礎(chǔ)設(shè)施大部分采用 Java 實(shí)現(xiàn)渡冻,變化較少戚扳,有事務(wù)要求的 Business Services 通常使用 Java。而Node主要用于替代過去php族吻、jsp使用場景, 用在需要快速迭代帽借,需求變化非常快的用戶側(cè)超歌。node已經(jīng)經(jīng)受了阿里雙11的考驗(yàn)砍艾,技術(shù)上是可行的。
題外話:個(gè)人認(rèn)為綜合應(yīng)用這塊巍举,自己玩玩還可以辐董,小團(tuán)隊(duì)或node不是非常強(qiáng)的技術(shù)團(tuán)隊(duì)盡量不要嘗試,阿里能做好這塊是因?yàn)閲鴥?nèi)頂尖的node方面人才基本都在阿里禀综,經(jīng)過多年實(shí)踐踩坑简烘,擁有相對(duì)完善的node基建和生態(tài)苔严。目前市面上前端里node強(qiáng)的比較少,餓了么為了招node服務(wù)端開發(fā)孤澎,還專門寫了個(gè)node相關(guān)的面試教程届氢。可想而知這方面人才有多少覆旭。
node支持高并發(fā)的原因:
- node.js基于異步I/O退子,接收到請(qǐng)求后,直接開一個(gè)I/O線程去執(zhí)行型将,然后就不管了寂祥,立即繼續(xù)執(zhí)行主線程。等I/O線程執(zhí)行完成后七兜,直接執(zhí)行對(duì)應(yīng)的回調(diào)函數(shù)即可丸凭。省去了許多等待請(qǐng)求的時(shí)間
- 事務(wù)驅(qū)動(dòng),主線程通過event loop事件循環(huán)觸發(fā)的方式來運(yùn)行程序腕铸,這一條暫時(shí)還不是很理解惜犀,先寫上~
參考:
- 如何評(píng)價(jià)阿里開源的企業(yè)級(jí) Node.js 框架 EggJS?(opens new window)
- Node.js:淺析高并發(fā)與分布式集群(opens new window)
- 天貓雙11前端分享系列(四):大規(guī)模 Node.js 應(yīng)用(opens new window)
- egg.js(opens new window)
- node-interview | ElemeFE(opens new window)
#webpack與parcel區(qū)別
webpack與parcel都是打包工具狠裹,webpack功能強(qiáng)大虽界,但比較重,配置項(xiàng)比較多涛菠,有點(diǎn)繁瑣莉御。而parcel就是為了解決配置項(xiàng)太多這個(gè)問題的,它默認(rèn)集成了通用的常規(guī)功能俗冻,零配置颈将,如果自定義較多,還是推薦webpack
If you don't want to worry about configuring everything and your needs are common needs, you should go directly with parcel. Parcel provides defaults (for babel-preset-env, post-css, html, etc) that fits most scenarios and works for everybody. You don't have to worry about configuring anything.
From the other hand, if you need a more customization, you should go with webpack. Keep in mind that you will have to setup everything that you need, explicitly set those things.
參考:Webpack vs Parcel - Stack Overflow(opens new window)
#mac使用觸控板拖動(dòng)復(fù)制言疗、移動(dòng)窗口
今天才意識(shí)到,每次要復(fù)制一段文字或移動(dòng)某個(gè)應(yīng)用窗口颂砸,我都是點(diǎn)擊觸控板再拖動(dòng)噪奄。而且Mac Air的觸控板按的聲音比較響,于是找了下是否有手勢可以支持人乓。
發(fā)現(xiàn)可以設(shè)置使用 三指拖移 復(fù)制文字和拖動(dòng)窗口勤篮,三個(gè)手指放上去拖動(dòng)就可以了。
設(shè)置方法:打開系統(tǒng)統(tǒng)偏好設(shè)置 => 點(diǎn) “輔助功能” => 點(diǎn) “鼠標(biāo)與觸控板” => 點(diǎn) “觸控板選項(xiàng)” => 先勾上啟用拖移色罚,然后選擇“三指拖移”碰缔,點(diǎn)擊 “好”
圖文詳情參考: MacBook觸控板選中/復(fù)制(opens new window)
#2020/03/12 周四
#關(guān)于vue和react思考
我個(gè)人理解 vue組件設(shè)計(jì)的對(duì)新手超級(jí)友好,使用門檻很低戳护。雖然我沒用過react金抡,但我可以猜到React應(yīng)該也是犧牲了一定的用戶使用門檻瀑焦,來實(shí)現(xiàn)更加靈活的js控制。而vue犧牲了用js控制的靈活度來降低了用戶使用門檻梗肝。側(cè)重點(diǎn)不一樣榛瓮,但底層實(shí)現(xiàn)基本都差不多,比如虛擬dom巫击、hooks禀晓,只是開放給用戶的調(diào)用方式不一樣。
前端框架再怎么變動(dòng)坝锰,就算弄出一朵花來粹懒,也只是對(duì)dom的修改,最終還是會(huì)回歸到 js高程3里面講到的dom章節(jié)部分內(nèi)容顷级。
#git commit 提交信息有誤怎么修改
如果不小心提交了凫乖,但沒有push,可以使用下面的命令來修改上一次的commit信息
git commit --amend -m 'xxx'
#他人提交了package-lock.json的更新導(dǎo)致拉取時(shí)和本地沖突
一般在npm install 時(shí)會(huì)修改package-lock.json文件愕把,我一般不會(huì)提交這個(gè)更新拣凹,但今天發(fā)現(xiàn)有人提交。我拉取時(shí)恨豁,提示這個(gè)文件沖突嚣镜,導(dǎo)致拉取不下來,我又不想提交更新橘蜜,所以嘗試用下面的命令菊匿,將工作區(qū)該文件的修改丟棄,再拉取
git checkout -- package-lock.json
拉取成功后计福,npm run serve 基本沒什么影響
#2020/03/11 周三
#vue封裝組件方式的思考
在封裝組件時(shí)跌捆,一般我們使用的方法是
把組件單獨(dú)放到一個(gè)xx.vue,然后需要引入時(shí)在components使用懶加載引入再使用
我就在想象颖,每次引入組件都需要三步
- 把組件通過 components 引入
- 在template中寫對(duì)應(yīng)的代碼
- 在data中寫對(duì)應(yīng)的數(shù)據(jù)佩厚,methods里寫綁定的事件
會(huì)不會(huì)太麻煩了,我希望像element的組件那樣说订,通過 this.$message.error(e.message) 這樣直接調(diào)用一個(gè)組件
于是我嘗試使用js的方式來調(diào)用單文件組件(.vue)抄瓦,在之前02/20號(hào)寫過方法,除了直接掛載到body外陶冷,也可以掛載到任何地方钙姊,只要你能拿到對(duì)應(yīng)的dom,可以使用ref屬性埂伦,再通過this.$refs['xx']來獲取其DOM 元素和組件實(shí)例煞额。
// 引入該組件
import ShowInfo from 'showInfo.vue'
// 通過js調(diào)用
clickShow() {
// 創(chuàng)建一個(gè)vue組件
const Component = Vue.extend(ShowInfo)
// 在文檔之外渲染并且隨后掛載,返回對(duì)應(yīng)的Vue實(shí)例(vm)
let showInfoVue = new Component().$mount()
// 將組件實(shí)例的dom,append到當(dāng)前頁面body下
this.$el.appendChild(showInfoVue.$el)
}
其實(shí)你發(fā)現(xiàn)沒膊毁,用js直接調(diào)用vue組件可以是可以胀莹,但也要比正常情況下寫更多代碼,比如
- js調(diào)用vue的方法需要封裝為一個(gè)class
- 以上面的示例為例子媚媒,通過js調(diào)用組件嗜逻,我們需要一個(gè)成功的回調(diào),以及傳參到組件缭召,在獲取到ShowInfo時(shí)栈顷,我們需要知道我們引入的只是一個(gè)'js對(duì)象'。我們可以在對(duì)象的methods里面注入方法嵌巷,用來獲取傳入的值萄凤,或掛載成功后的回調(diào)。這樣相當(dāng)于mixin搪哪,但.vue組件實(shí)現(xiàn)里使用這些注入的事件時(shí)會(huì)不好理解靡努,有種默認(rèn)其妙多出來全局函數(shù)的疑惑
- 以js方法寫的組件,不能兼容默認(rèn)的引用方法晓折,如果要支持那就要寫一些額外的代碼
- 你會(huì)發(fā)現(xiàn)邏輯會(huì)變的不好理解惑朦,不夠簡單,對(duì)新手不友好漓概,如果需要其他人維護(hù)時(shí)漾月,可能不好理解為什么這么做
綜上所述:默認(rèn)的封裝調(diào)用組件的方式就很好,簡單胃珍、明了梁肿,你想在調(diào)用的時(shí)候輕松,那么在封裝組件時(shí)觅彰,就會(huì)增加對(duì)應(yīng)的工作量吩蔑,整體工作量差不多。
我們?cè)賮砜磂lement組件填抬,對(duì)于內(nèi)容比較少的烛芬,比如通知類,element都提供的是js調(diào)用方法飒责,而沒有普通的組件調(diào)用方法赘娄。且一般掛載到body上。為什么dialog組件沒有封裝成js呢读拆?我的理解是dialog里面的內(nèi)容可擴(kuò)展性很強(qiáng),如果改為js調(diào)用鸵闪,可能會(huì)出現(xiàn)把大量代碼寫在js的情況檐晕,或者需要寫VNode的render方法。就顯得不夠優(yōu)雅了。
總結(jié):在封裝組件時(shí)辟灰,什么時(shí)候用封裝為js調(diào)用方式个榕、什么時(shí)候采用普通的封裝呢?我的理解是可以通過下面幾個(gè)方面來進(jìn)行評(píng)估
- 被封裝的組件需求是否穩(wěn)定芥喇,有沒有可能會(huì)在后面經(jīng)常變更或者進(jìn)行漸進(jìn)式增強(qiáng)西采,如果不穩(wěn)定,不建議封裝為js調(diào)用方式继控,對(duì)于可擴(kuò)展性強(qiáng)的械馆,還是建議使用普通組件封裝方式,更利于維護(hù)
- 是否是掛載到body下武通,還是需要放到任意的div內(nèi)? 一般普通組件更好放置霹崎。如果掛載到body下,可以考慮封裝為js調(diào)用方式
- 是否功能相對(duì)單一簡單冶忱,如果組件功能單一簡單建議封裝為js調(diào)用方式
- 該組件是否在頁面里大量被調(diào)用 大量的被調(diào)用尾菇,意味著大量重復(fù)的代碼,可以考慮封裝為js調(diào)用方式囚枪,增加組件實(shí)現(xiàn)時(shí)的復(fù)雜度派诬,來降低調(diào)用時(shí)的復(fù)雜度
總之,不管怎么樣封裝链沼,當(dāng)有人問你為什么這么封裝時(shí)默赂,你要能夠說出你自己的理由。
#2020/03/10 周二
#vue自定義組件v-model屬性實(shí)現(xiàn)dialog組件的二次封裝
當(dāng)某個(gè)組件是element的dialog組件時(shí)忆植,我們需要對(duì)dialog進(jìn)行隱藏顯示放可,當(dāng)子組件里的dialog關(guān)閉時(shí),需要修改父組件傳入的值朝刊,盡管不是表單組件也可以使用v-model來解決耀里,先來看看怎么調(diào)用
<template>
<div>
<user-selection v-model="showUserSelection" @confirm="confirm"/>
<el-button type="primary" @click="showUserSelection = true">打開彈窗</el-button>
<div>
<template>
<script>
export default {
components: {
UserSelection: () => import("../src/components/user-selection/src/main")
},
data() {
return {
showUserSelection: false
}
},
methods: {
confirm(value) {
console.log(value)
}
}
}
</script>
再來看組件實(shí)現(xiàn)
<template>
<div>
<el-dialog title="提示" :visible.sync="dialogVisible">
內(nèi)容
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false">確 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
model: {
prop: 'show', // 設(shè)置對(duì)應(yīng)v-model的屬性字段
event: 'close' // 如果不指定默認(rèn)為input,當(dāng)$emit該事件拾氓,可以自動(dòng)執(zhí)行 修改父組件v-model參數(shù)的值
},
props: ['show'], // 接收v-model的傳值
computed: {
dialogVisible: {
get() {
return this.show
},
set(newVal) {
console.log(newVal)
this.$emit('close', newVal)
}
}
},
data() {
return {}
}
}
</script>
#封裝組件時(shí)預(yù)留vue插件入口便于全局引入
在element組件中冯挎,我們使用el-input等element元素時(shí),不需要在components里引入咙鞍。為什么呢房官?在引入element時(shí),我們有在mian.js里我們使用了Vue.use(elemnt組件)续滋,這樣進(jìn)行了全局注入組件翰守,相當(dāng)于組件做成了一個(gè)vue插件,如果我們自己封裝組件如何能夠在Vue.use后直接可以全局調(diào)用呢疲酌?
于是我特意去看了下element組件源碼蜡峰,這里我們暫時(shí)不要求封裝為npm包了袁,只需要在平常自定義組件的基礎(chǔ)上做一個(gè)增強(qiáng),可以使用Vue.use全局引入湿颅。
element-ui源碼中载绿,以alert組件為例,來看目錄結(jié)構(gòu)
alert
├── src
│ └── main.vue # 組件實(shí)現(xiàn)
└── index.js # install方法油航,供全局引入
import Alert from './src/main';
/* istanbul ignore next */
Alert.install = function(Vue) {
Vue.component(Alert.name, Alert);
};
export default Alert;
綜上:我們?cè)陂_發(fā)組件時(shí)崭庸,可以增加全局引入的接口,層級(jí)也可以仿照element的來谊囚,多研究源碼這樣代碼才會(huì)寫的更健壯怕享。
#css hover后改變其他元素樣式
css中某個(gè)元素hover后,可以對(duì)其他元素設(shè)置樣式秒啦,但注意:只限定于改變他的子元素, 以及其后面的元素
<style>
/* hover后單獨(dú)改變某一個(gè)子元素的樣式 */
.cur-element:hover .child-1 {
color: red;
}
/* 設(shè)置相鄰的后一個(gè)兄弟節(jié)點(diǎn)樣式 */
.cur-element:hover + div {
background: blue;
}
/* 設(shè)置后面的所有對(duì)應(yīng)的兄弟節(jié)點(diǎn)樣式熬粗,不必相鄰,但需要再其后面 */
.cur-element:hover ~ div {
background: red;
}
</style>
<div class="parent">
<div>再前一個(gè)兄弟元素</div>
<div>前一個(gè)兄弟元素</div>
<div class="cur-element">
測試hover
<span class="child-1">child-1</span>
<span class="child-2">child-2</span>
</div>
<div>后一個(gè)兄弟元素</div>
<div>再后一個(gè)兄弟元素</div>
<div>再再后一個(gè)兄弟元素</div>
</div>
參考:
- 之前的css筆記:css + ~ 選擇器(opens new window)
- css:hover狀態(tài)改變另一個(gè)元素樣式的使用(opens new window)
- Change style of all other elements when one element is hovered - stackoverflow(opens new window)
#css vw的使用場景
在輪播圖純css的解決方案中:
- 將圖片區(qū)域?qū)挾仍O(shè)置為 圖片張數(shù) * 100%
- hover切換按鈕時(shí)余境,將圖片區(qū)域向左移動(dòng)一個(gè)100%:transform: translateX(100%)
這里就會(huì)有問題驻呐,向左移動(dòng)100%,寬度是圖片張數(shù)*100%芳来,而不是視窗寬度含末,用 100vw就可以很好的解決了。
參考之前的css筆記:css長度單位 相對(duì)長度 - HTML權(quán)威指南CSS部分(opens new window)
#2020/03/08 周日
#widnows nginx部署https服務(wù)
今天打算用使用 zuo11.com的 二級(jí)域名 api 來寫接口玩玩即舌,打算使用https佣盒。
- 在阿里云將域名免費(fèi)的ssl證書分配給api.zuo11.com
- 在域名解析里,增加 api.zuo11.com 的解析顽聂,解析到服務(wù)器
- 初始化一個(gè)koa項(xiàng)目肥惭,監(jiān)聽某個(gè)端口,比如 9000端口紊搪,寫一些測試的接口
- 部署到服務(wù)器
- nginx 添加對(duì)https的支持:①. 在ssl證書位置下載證書蜜葱,會(huì)有兩個(gè)文件 xxx.pem, xxx.key,在服務(wù)器nignx目錄下的conf目錄新建cert目錄耀石,將兩個(gè)文件拷貝進(jìn)去牵囤,修改conf下nginx.conf的配置
# HTTPS server
server {
listen 443 ssl;
server_name api.zuo11.com;
ssl_certificate cert\3391782_api.zuo11.com.pem;
ssl_certificate_key cert\3391782_api.zuo11.com.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
# 注釋掉默認(rèn)的加密方式
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; #使用此加密套件。
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #使用該協(xié)議進(jìn)行配置滞伟。
ssl_prefer_server_ciphers on;
location / {
# root html;
# index index.html index.htm;
proxy_pass http://127.0.0.1:9000;
}
}
#2020/03/07 周六
#nginx 中文路徑404的問題
在mac本地調(diào)試時(shí)揭鳞,都是ok的,部署到windows服務(wù)器上后時(shí)梆奈,發(fā)現(xiàn)一個(gè)圖片出現(xiàn)了404的問題野崇,最開始以為是緩存的問題,清了緩存后還是404亩钟。這張圖片是中文路徑乓梨,之前全部用的是英文的钥弯,沒發(fā)現(xiàn)這個(gè)問題。試了下英文的圖片鏈接是ok的督禽。百度了下,發(fā)現(xiàn)確實(shí)有這種問題总处。是nginx設(shè)置的編碼與操作系統(tǒng)的編碼不一致的問題狈惫。
# linux查看電腦默認(rèn)編碼
echo $LANG
# zh_CN.UTF-8
# windows下查看默認(rèn)字符編碼
chcp
# 如果顯示 活動(dòng)代碼頁 936 表示GBK編碼 我的服務(wù)器就是這個(gè)編碼,修改了nginx charset utf-8;沒效果
# 65001 表示utf-8
# 設(shè)置字符編碼鹦马,但發(fā)現(xiàn)只在當(dāng)前控制臺(tái)生效胧谈,重新開一個(gè)就沒了。
chcp 65001
# 有個(gè)修改注冊(cè)表的方法:不知道是否可行荸频,但怕影響服務(wù)器的其它服務(wù)菱肖,還是算了,改英文名比較穩(wěn)旭从。稳强。。和悦。
# https://blog.csdn.net/yangzhong0808/article/details/79012628?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
參考:windows下cmd命令行顯示UTF8字符設(shè)置(CHCP命令)(opens new window)
#Invalid character in header content ["Content-Disposition"]
在koa中退疫,如果Content-Disposition里設(shè)置文件名有中文會(huì)提示錯(cuò)誤,需要用類似 encodeURIComponent 轉(zhuǎn)碼的函數(shù)轉(zhuǎn)碼后才行
const fileName = encodeURIComponent('這是一個(gè)文件') // 需要先轉(zhuǎn)碼才行
ctx.set({
'Content-Type': 'application/x-tar',
'Content-Disposition': `attachment; filename="${fileName}.tar"`
})
#2020/03/03 周二
#github Badge 與npm 徽章圖片生成
在寫readme時(shí)鸽素,最開始一般會(huì)貼一些徽章圖片褒繁,比如 build passing,license MIT 等馍忽,這些都是引入的圖片
// 上圖對(duì)應(yīng)的三個(gè)徽章圖片
![version-v0.2.0](https://img.shields.io/badge/version-v0.2.0-yellow.svg)
![build-passing](https://img.shields.io/badge/build-passing-green.svg)
![license-MIT](https://img.shields.io/badge/license-MIT-green.svg)
// 發(fā)現(xiàn)規(guī)律沒棒坏,更改后面的參數(shù),就可以生成不同的圖片
// 比如
https://img.shields.io/badge/JAVA-1.8+-green.svg
npm 徽章圖片遭笋,主要適用于npm包坝冕,顯示npm包的一些信息
// 文檔 https://www.npmjs.com/package/npm-badge
// zuo-blog npm包使用示例,只需要把對(duì)應(yīng)的npm 包名稱修改即可
[![NPM](https://nodei.co/npm/zuo-blog.png)](https://npmjs.org/package/zuo-blog)
#個(gè)人小程序主體可以做什么
目前小程序名稱變得很嚴(yán)格坐梯,只要包含稍微通用點(diǎn)的關(guān)鍵字徽诲,比如 "管理",都會(huì)要求上傳手持身份證照片吵血,且要有對(duì)應(yīng)的商標(biāo)證書谎替。但貌似商標(biāo)申請(qǐng)需要1000,且審批時(shí)間需要1年多... 太難了蹋辅。而且個(gè)人小程序能做的東西越來越少了钱贯。
參考:個(gè)人主體小程序開放的服務(wù)類目 - 微信開放文檔(opens new window)
#前端pc web獲取時(shí)間是不準(zhǔn)確的
我們?cè)?new Date() 時(shí),如果是在pc端侦另,電腦時(shí)間不對(duì)秩命,獲取的時(shí)間也會(huì)不對(duì)尉共。如果需要記錄時(shí)間,應(yīng)該發(fā)送請(qǐng)求給后端弃锐,讓后端計(jì)算時(shí)間袄友。依賴服務(wù)器時(shí)間
修改本地時(shí)間后,用 new Date() 獲取時(shí)間霹菊,如下圖會(huì)是錯(cuò)誤的時(shí)間
#2020/03/02 周一
#瀏覽器tab頁切換時(shí)更改標(biāo)題
當(dāng)用戶點(diǎn)擊了瀏覽器其他tab頁離開頁面剧蚣,或者從其他tab頁進(jìn)入當(dāng)前頁,都會(huì)觸發(fā)visibilitychange事件旋廷,根據(jù)docuemnt.hidden可以判斷是否離開或回來鸠按,我們可以修改標(biāo)題達(dá)到可視化的一個(gè)效果
// 實(shí)現(xiàn)tab間切換時(shí),隱藏頁面title改變功能
// JS高程3 Page Visibility API(頁面可見性API)
// 參考:https://www.yuque.com/guoqzuo/js_es6/nocthb#0cf7a8b7
var title = document.title;
document.addEventListener('visibilitychange', function (event) {
document.title = document.hidden ? '~ 你快回來 ~ ' : title
if (document.hidden) {
// 做一些暫停操作
} else {
// 開始操作
}
}, false)
#網(wǎng)頁中網(wǎng)絡(luò)異橙牡猓或網(wǎng)絡(luò)正常時(shí)動(dòng)態(tài)提示
監(jiān)聽頁面的online和offline事件目尖,顯示或隱藏對(duì)應(yīng)的信息
// 當(dāng)網(wǎng)絡(luò)狀態(tài)發(fā)生改變時(shí)(有網(wǎng) => 無網(wǎng),無網(wǎng) => 有網(wǎng)),提示信息
// JS高程3 離線檢測
// 參考: https://www.yuque.com/guoqzuo/js_es6/sp2k81#244d3090
let errorMsgNode // 用來移除錯(cuò)誤信息節(jié)點(diǎn)
window.ononline = function(event) {
errorMsgNode && document.body.removeChild(errorMsgNode)
message('success', '網(wǎng)絡(luò)已連接', 3000)
}
window.onoffline = function(event) {
message('error', '網(wǎng)絡(luò)已斷開')
}
/**
* 為了顯示網(wǎng)絡(luò)信息扎运,專門寫了個(gè)小tips提示函數(shù)瑟曲,在頂部顯示信息
* @param {}} type 文字顏色 error 為紅色,其他為綠色
* @param {*} msg 顯示信息
* @param {*} sec 如果有傳入時(shí)間豪治,sec秒后關(guān)閉提示
*/
function message(type, msg, sec) {
let color = type === 'error' ? 'red' : 'green'
let cssArr = [
'position:fixed;top:8px;left:50%;z-index:9999999;',
'transform:translateX(-50%);padding:5px 10px;background:#fff;'
]
let htmlStr = `
<div style="${cssArr.join('')}color:${color}">${msg}</di>
`
let node = document.createElement('div')
node.innerHTML = htmlStr
document.body.appendChild(node)
if (Number.isInteger(sec) && sec > 0) {
setTimeout(() => {
document.body.removeChild(node)
}, sec)
} else {
// 錯(cuò)誤信息测蹲,一直提示,需要設(shè)置到變量里鬼吵,等網(wǎng)絡(luò)連接上時(shí)移除
errorMsgNode = node
}
}
#頁面滾動(dòng)比例監(jiān)聽實(shí)現(xiàn)
監(jiān)聽頁面的scroll事件扣甲,整個(gè)滾動(dòng)距離為 document.documentElement.scrollHeight - window.innerHeight,當(dāng)前scrollTop除以整個(gè)滾動(dòng)距離齿椅,就是頁面的百分比琉挖,向body掛載兩個(gè)div來顯示進(jìn)度信息
// 頁面滾動(dòng)比例監(jiān)聽
// posTop 頂部類似阮一峰ES6網(wǎng)頁的滾動(dòng)進(jìn)度條
// pos 右下角滾動(dòng)百分比
// JS高程3 - UI事件 scroll事件
// https://www.yuque.com/guoqzuo/js_es6/elgng1#e38771e5
let htmlStr = `
<div id="posTop" style="position: fixed;top:0;height:2px;background: #25b864;z-index:999999;"></div>
<div id="pos" style="display:none;position:fixed;bottom: 100px;right:20px;padding:10px;background: #25b864;color:white;width:40px;text-align: center;border-radius:5px;"></div>
`
let eleNode = document.createElement('div')
eleNode.innerHTML = htmlStr
document.body.appendChild(eleNode)
window.addEventListener('scroll', function(e) {
let scrollTop = document.documentElement.scrollTop;
let total = document.documentElement.scrollHeight - window.innerHeight;
let persentage = parseInt(scrollTop/total*100);
// console.log(scrollTop);
document.getElementById('pos').style.display = scrollTop === 0 ? 'none' : 'block';
document.getElementById('pos').innerHTML = `${persentage}%`;
document.getElementById('posTop').style.width = `${persentage}%`;
}, false)
#復(fù)制內(nèi)容后,在內(nèi)容中插入作者及當(dāng)前文章信息
監(jiān)聽body里的copy事件涣脚,然后用 document.getSelection()獲取內(nèi)容示辈,追加內(nèi)容后,再使用event.clipboardData.setData像粘貼板里寫入內(nèi)容
// 操作粘貼板
// JS高程3 表單腳本 操作粘貼板
// https://www.yuque.com/guoqzuo/js_es6/ubpn7k#8482e7c5
document.body.oncopy = function(event) {
console.log('copy', event);
// 獲取copy的內(nèi)容
// console.log(document.getSelection().toString());
// 在copy內(nèi)容里加入信息
var msg = `
-----------------------------
標(biāo)題:${document.title}
鏈接:${location.href}
作者:guoqzuo (http://github/zuoxiaobai)
`
event.clipboardData.setData('text/plain', `${document.getSelection().toString()} ${msg}`);
event.preventDefault();
};