您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“如何用Vue制作一個(gè)像素繪板”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
技術(shù)棧
[vue + vuex + vue-router] 頁面渲染 + 數(shù)據(jù)共享 + 路由跳轉(zhuǎn)
[axios] 以 Promise 的方式使用 HTTP 請(qǐng)求
[stylus] CSS 預(yù)處理
[element-ui] UI 庫
[Webpack] 打包上面這些東西
[koa 2 & koa-generator] NodeJS 框架和框架腳手架
[mongodb & mongoose] 數(shù)據(jù)庫和操作數(shù)據(jù)庫的庫
[node-canvas] 服務(wù)端數(shù)據(jù)副本記錄
[Socket.io] 實(shí)時(shí)推送
[pm2] Node 服務(wù)部署
[nginx] 部署靜態(tài)資源訪問服務(wù)(HTTPS),代理請(qǐng)求
[letsencrypt] 生成免費(fèi)的 HTTPS 證書
Webpack 之所以也被列出來,是因?yàn)楸卷?xiàng)目作為項(xiàng)目 luwuer.com 的一個(gè)模塊,需要 webpack 來實(shí)現(xiàn)獨(dú)立打包
node-canvas
安裝
node-canvas 是我目前遇到過最難安裝的依賴,以至于我根本不想在 Windows 下安裝他,它的功能依賴很多系統(tǒng)下默認(rèn)不存在的包,在 Github 上也能看到很多 issue 的標(biāo)簽是 installation help。以 CentOS 7 純凈版為例,在安裝它之前你需要安裝以下這些依賴,值得注意的是 npm 文檔上提供的命令沒有 cairo 。
# centos 前置條件 sudo yum install gcc-c++ cairo cairo-devel pango-devel libjpeg-turbo-devel giflib-devel # 安裝本體 yarn add canvas -D
還有一個(gè)不明所以的坑,如果前置條件準(zhǔn)備就緒后,安裝本體仍然一直卡取包這一步(不報(bào)錯(cuò)),此時(shí)需要單獨(dú)更新一下 npm
使用示例
參考文檔很容易就能掌握基本用法,下方例子中先取到像素點(diǎn)數(shù)據(jù)生成 ImageData ,然后通過 putImageData 把歷史數(shù)據(jù)畫到 canvas 。
const { createCanvas, createImageData } = require('canvas') const canvas = createCanvas(canvasWidth, canvasHeight) const ctx = canvas.getContext('2d') // 初始化 const init = callback => { Dot.queryDots().then(data => { let imgData = new createImageData( Uint8ClampedArray.from(data), canvasWidth, canvasHeight ) // 移除 Smooth ctx.mozImageSmoothingEnabled = false ctx.webkitImageSmoothingEnabled = false ctx.msImageSmoothingEnabled = false ctx.imageSmoothingEnabled = false ctx.putImageData(imgData, 0, 0, 0, 0, canvasWidth, canvasHeight) successLog('canvas render complete !') callback() }) }
Socket.io
本項(xiàng)目在設(shè)計(jì)上有兩個(gè)必須用到推送的地方,一是其他用戶的建點(diǎn)信息,二是所有用戶發(fā)送的聊天消息。
client
// socket.io init // transports: [ 'websocket' ] window.socket = io.connect(window.location.origin.replace(/https/, 'wss')) // 接收?qǐng)D片 window.socket.on('dataUrl', data => { this.imageObject.src = data.url this.loadInfo.push('渲染圖像...') this.init() }) // 接收其他用戶建點(diǎn) window.socket.on('newDot', data => { this.saveDot( { x: data.index % this.width, y: Math.floor(data.index / this.width), color: data.color }, false ) }) // 接收所有人的最新推送消息 window.socket.on('newChat', data => { if (this.msgs.length === 50) { this.msgs.shift() } this.msgs.push(data) })
server /bin/www
let http = require('http'); let io = require('socket.io') let server = http.createServer(app.callback()) let ws = io.listen(server) server.listen(port) ws.on('connection', socket => { // 建立連接的 client 加入房間 chatroom ,為了下方可以廣播 socket.join('chatroom') socket.emit('dataUrl', { url: cv.getDataUrl() }) socket.on('saveDot', async data => { // 推送給其他用戶,即廣播 socket.broadcast.to('chatroom').emit('newDot', data) saveDotHandle(data) }) socket.on('newChat', async data => { // 推送給所有用戶 ws.sockets.emit('newChat', data) newChatHandle(data) }) })
letsencrypt
申請(qǐng)證書
# 獲得程序 git clone https://github.com/letsencrypt/letsencrypt cd letsencrypt # 自動(dòng)生成證書(環(huán)境安裝完畢后會(huì)有兩次確認(rèn)),證書目錄 /etc/letsencrypt/live/{輸入的第一個(gè)域名} 我這里是 /etc/letsencrypt/live/www.luwuer.com/ ./letsencrypt-auto certonly --standalone --email html6@foxmail.com -d www.luwuer.com -d luwuer.com
自動(dòng)續(xù)期
# 進(jìn)入定時(shí)任務(wù)編輯 crontab -e # 提交申請(qǐng),我這里設(shè)置每兩月一次,過期時(shí)間為三月 * * * */2 * cd /root/certificate/letsencrypt && ./letsencrypt-auto certonly --renew
nginx
yum install -y nginx
/etc/nginx/config.d/https.conf
server { # 使用 HTTP/2,需要 Nginx1.9.7 以上版本 listen 443 ssl http2 default_server; # 開啟HSTS,并設(shè)置有效期為“6307200秒”(6個(gè)月),包括子域名(根據(jù)情況可刪掉),預(yù)加載到瀏覽器緩存(根據(jù)情況可刪掉) add_header Strict-Transport-Security "max-age=6307200; preload"; # add_header Strict-Transport-Security "max-age=6307200; includeSubdomains; preload"; # 禁止被嵌入框架 add_header X-Frame-Options DENY; # 防止在IE9、Chrome和Safari中的MIME類型混淆攻擊 add_header X-Content-Type-Options nosniff; # ssl 證書 ssl_certificate /etc/letsencrypt/live/www.luwuer.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/www.luwuer.com/privkey.pem; # OCSP Stapling 證書 ssl_trusted_certificate /etc/letsencrypt/live/www.luwuer.com/chain.pem; # OCSP Stapling 開啟,OCSP是用于在線查詢證書吊銷情況的服務(wù),使用OCSP Stapling能將證書有效狀態(tài)的信息緩存到服務(wù)器,提高TLS握手速度 ssl_stapling_verify on; #OCSP Stapling 驗(yàn)證開啟 ssl_stapling on; #用于查詢OCSP服務(wù)器的DNS resolver 8.8.8.8 8.8.4.4 valid=300s; # DH-Key交換密鑰文件位置 ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # 指定協(xié)議 TLS ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 加密套件,這里用了CloudFlare's Internet facing SSL cipher configuration ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; # 由服務(wù)器協(xié)商最佳的加密算法 ssl_prefer_server_ciphers on; server_name ~^(\w+\.)?(luwuer\.com)$; # $1 = 'blog.' || 'img.' || '' || 'www.' ; $2 = 'luwuer.com' set $pre $1; if ($pre = 'www.') { set $pre ''; } set $next $2; root /root/apps/$pre$next; location / { try_files $uri $uri/ /index.html; index index.html; } location ^~ /api/ { proxy_pass http://43.226.147.135:3000/; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # socket代理配置 location /socket.io/ { proxy_pass http://43.226.147.135:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } # location /weibo/ { # proxy_pass https://api.weibo.com/; # } include /etc/nginx/utils/cache.conf; } server { listen 80; server_name www.luwuer.com; rewrite ^(.*)$ https://$server_name$request_uri; }
附錄
數(shù)據(jù)庫存儲(chǔ)結(jié)構(gòu)思考?xì)v程
首先需求是畫板可以作畫實(shí)際大小為 { width: 1024px, height: 512px } ,這就意味著有 1024 * 512 = 524,288 個(gè)像素點(diǎn),或則有 524,288 * 4 = 2,097,152 個(gè)表示顏色的數(shù)字,這些數(shù)據(jù)量在不做壓縮的情況下,最小存儲(chǔ)方式是后者剔除掉 rgba 中的 a ,也就是一個(gè)長度為 524,288 * 3 = 1,572,864 的數(shù)組,如果賦值給變量占用內(nèi)存大概 1.5M (數(shù)據(jù)來源于 Chrome Memory)。為了存儲(chǔ)以上結(jié)構(gòu),我首先分了兩種類型的存儲(chǔ)結(jié)構(gòu):
以點(diǎn)為對(duì)象存儲(chǔ),也就是說會(huì)有 524,288 條數(shù)據(jù)
顏色 rbga 存儲(chǔ),后優(yōu)化為 rgb 存儲(chǔ)
顏色 16 進(jìn)制存儲(chǔ)
整個(gè)畫布數(shù)據(jù)當(dāng)作一條數(shù)據(jù)存儲(chǔ)
雖然看起來結(jié)構(gòu)2有點(diǎn)蠢,但起初我確實(shí)思考過這樣的結(jié)構(gòu),那時(shí)我還不清楚原來取數(shù)據(jù)最耗時(shí)的不是查詢而是 IO 。
后來我分別測試 1.1 和 1.2 這兩種結(jié)構(gòu),然后直接否定了結(jié)構(gòu) 2,因?yàn)樵跍y試中我發(fā)現(xiàn)了 IO 耗時(shí)占總耗時(shí)超過 98% ,而結(jié)構(gòu) 2 無疑不能因?yàn)閱螚l數(shù)據(jù)取得絕對(duì)的性能優(yōu)勢(shì)。
1.1
存儲(chǔ)大小 10M
取出全部數(shù)據(jù) 8000+ms
全表查詢 150ms (findOne 和 find 對(duì)比結(jié)果)
其余耗時(shí) 20ms (findOne 和 find 對(duì)比結(jié)果)
1.2
存儲(chǔ)大小 10M
取出全部數(shù)據(jù) 7500+ms
全表查詢
其余耗時(shí)
結(jié)構(gòu) 2 如果取數(shù)據(jù)不是毫秒級(jí),就是死刑,因?yàn)檫@種結(jié)構(gòu)下單個(gè)像素變動(dòng)就需要存儲(chǔ)整個(gè)圖片數(shù)據(jù)
老實(shí)講這個(gè)測試結(jié)果讓我有些難以接受,問了好幾個(gè)認(rèn)識(shí)的后端為什么性能這么差、有沒有解決辦法,但都沒什么結(jié)果。更可怕的是,測試是在我 i7 CPU 的臺(tái)式電腦上進(jìn)行的,當(dāng)我把測試環(huán)境放到單核服務(wù)器上時(shí),取全表數(shù)據(jù)的耗時(shí)還要乘以 10 。好在只要想一個(gè)問題久了,即使有時(shí)只是想著這個(gè)問題發(fā)呆,也總能迸發(fā)出一些莫名的靈感。我想到了關(guān)鍵之一數(shù)據(jù)可以只在服務(wù)啟動(dòng)時(shí)取出放到內(nèi)存中,像素發(fā)生改變時(shí)數(shù)據(jù)庫和內(nèi)存數(shù)據(jù)副本同步修改,于是得以繼續(xù)開發(fā)下去。最終我選擇了 1.1 的結(jié)構(gòu),選擇原因和下文的“數(shù)據(jù)傳輸”有關(guān)。
const mongoose = require('mongoose') let schema = new mongoose.Schema({ index: { type: Number, index: true }, r: Number, g: Number, b: Number }, { collection: 'dots' })
index 代替 x & y 以及移除 rgba 中的 a 在代碼中再補(bǔ)上,都能顯著降低 collection 的實(shí)際存儲(chǔ)大小
在測試過程中其實(shí)還有個(gè)特別奇怪的問題,就是單核小霸王服務(wù)器上,我如果一次性取出所有數(shù)據(jù)存儲(chǔ)到一個(gè) Array 中,程序會(huì)在中途奔潰,沒有任何報(bào)錯(cuò)信息。起初我以為是 CPU 滿荷載久了導(dǎo)致的奔潰(top 查看硬件使用信息),所以還特意新租了一個(gè)服務(wù)器,想用一個(gè)群里的朋友提醒的“分布式”。再后面一段時(shí)間,我通過分頁取數(shù)據(jù),發(fā)現(xiàn)程序總是在取第二十萬零幾百條(一個(gè)固定數(shù)字)是陡然奔潰,所以為 CPU 證了清白。
Vue具體輕量級(jí)框架、簡單易學(xué)、雙向數(shù)據(jù)綁定、組件化、數(shù)據(jù)和結(jié)構(gòu)的分離、虛擬DOM、運(yùn)行速度快等優(yōu)勢(shì),Vue中頁面使用的是局部刷新,不用每次跳轉(zhuǎn)頁面都要請(qǐng)求所有數(shù)據(jù)和dom,可以大大提升訪問速度和用戶體驗(yàn)。
“如何用Vue制作一個(gè)像素繪板”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。