<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用 github 快速搭建屬于自己的圖床

          共 8994字,需瀏覽 18分鐘

           ·

          2021-01-28 21:49


          作者:_Wendao?
          https://juejin.cn/post/6906791889777721352

          起因

          說起來,圖床應(yīng)用這東西,在github上有很多,但是大多都是基于一些云廠商免費的靜態(tài)存儲服務(wù)來實現(xiàn)的,比如七牛云的靜態(tài)存儲,考慮到這些云廠商的賺錢欲望,所以我并不放心將他們作為圖床的服務(wù)提供商。

          也有支持github的,比如picgo,不過涉及到personal token,我也不是很放心將自己的token寫入到一個開源項目的桌面應(yīng)用里。而且picgo導(dǎo)出的github圖片鏈接是以 githubusercontent.com 為host的鏈接,眾所周知,該域名在中國很多地區(qū)都被DNS污染了,只有通過改host或是科學(xué)上網(wǎng)進行訪問,所以結(jié)論是,picgo基于github導(dǎo)出的圖片鏈接,在國內(nèi)等于沒用。

          那有沒有一種方式,既能讓圖片鏈接不被DNS污染或是被墻掉,又不會涉及到開發(fā)者personal token,影響賬戶安全呢?

          于是,就有了picpic。picpic是我在做一個另一個大型的開源項目的過程中抽空實現(xiàn)的,初始版本只用了兩天就寫出來了,但是我本人自認為是一個合格和還不錯的product maker,并不愿意產(chǎn)出一個使用繁瑣,功能殘缺的半成品給別人使用——關(guān)鍵是自己用的也不爽。

          我做產(chǎn)品,核心觀點就是,做出來的東西自己愿不愿意用,用起來有沒有感受到“美”,是不是能夠沉靜在產(chǎn)品中去感受它,這很重要,正是因為我從沒將自己定位成一個前端,或是node開發(fā),而是product maker,終極理想就是artist,就是做藝術(shù),內(nèi)心始終有一個想法:你不是在寫代碼,你是在畫一幅畫,你享受這個過程,如果能夠讓別人享受到“結(jié)果”,那是再好不過了。

          所以就有了它:

          DEMO地址:https://matrixage.github.io/picpic_example/

          項目地址:https://github.com/MatrixAges/picpic

          picpic

          基于離線版本,脫離了webpack的vue.js構(gòu)建的單頁面應(yīng)用,原理就是通過node把圖片數(shù)據(jù)預(yù)編譯并寫入到window對象中,然后通過chunk進行分片,提供翻頁功能,至于文件夾模式,則是通過node把assets文件夾下的文件結(jié)構(gòu)預(yù)編譯成樹形數(shù)據(jù),寫入到window對象,然后給頁面中的js進行調(diào)用。

          服務(wù)基于github pages,自動化構(gòu)建使用的是github actions,通過自動化構(gòu)建部署,部署靜態(tài)文件到gh-pages分支,然后訪問 username.github.io/${repo}/${img_path} 即可訪問不被槍的靜態(tài)圖片。

          幾經(jīng)打磨,最后我把它做成了cli,你只需要npm i @matrixage/picpic,即可使用。

          下面講講,我是如何通過node和vue構(gòu)建這樣一個單頁面應(yīng)用的。

          沒有webpack的web應(yīng)用

          使用github actions也有一段時間了,在經(jīng)歷過很多次構(gòu)建之后,我觀察到了一個現(xiàn)象:那就是80%的時間都是webpack花掉的,關(guān)鍵是一些很簡單的項目,因為webpack,還是會有一個比較長的安裝npm包的時間,那這對于一個圖床應(yīng)用來說,是致命的。

          所以我決定擺脫webpack,使用離線版本的vue.min.js來構(gòu)建應(yīng)用,將部署時間控制在30s以內(nèi),做到提交圖片,即刻可用。



          <script?src='./libs/js/vue.min.js'>script>
          <script?src='./libs/js/lodash.chunk.js'>script>
          <script?src='./libs/js/lodash.throttle.js'>script>
          <script?src='./libs/js/clipboard.js'>script>
          <script?src='./index.js'>script>

          使用XHR和CustomEvent進行組件化開發(fā)

          在html頂部引入include.js,改文件的作用是在文檔加載完成之后將include標簽中的地址通過同步的XHR,請求到組件的html內(nèi)容,然后寫入到頁面中。

          //?include.js

          getFileContent:?function?(url){
          ????var?o?=?new?XMLHttpRequest()
          ?
          ?o.open('get',?url,?false)
          ?o.send(null)
          ?
          ?return?o.responseText
          }

          接著通過自定義事件發(fā)出通知:

          //?include.js

          var?evt?=?new?CustomEvent('included',?{
          ?bubbles:?true,
          ?cancelable:?false
          })

          window.onload?=?function?(){
          ????new?Include().replaceIncludeElements()
          ????
          ????document.dispatchEvent(evt);
          }

          在其他腳本中接收通知:

          //?index.js

          document.addEventListener('included',?function?(){...})

          通過node預(yù)編譯組件

          僅僅是使用include是不夠的,組件的js和css代碼同樣要分離出來,這樣才有意義,于是node出場,其實你理解的webpack,不過時穿上紳士馬甲的node編譯腳本,本質(zhì)上還是預(yù)編譯。

          所以不用webpack,我們直溯本源,手寫預(yù)編譯代碼。在picpic項目根目錄新建一個build文件夾,其中的文件就是預(yù)編譯要用的代碼。

          //?build/index.js

          const?fs?=?require('fs-extra')
          const?globby?=?require('globby')
          const?inject?=?require('./inject')
          const?paths?=?require('./utils/paths')

          const?main?=?async?()?=>?{
          ?if?(!fs.existsSync(paths.dist))?{
          ??fs.mkdirSync(paths.dist)
          ?}?else?{
          ??fs.removeSync(paths.dist)
          ??fs.mkdirSync(paths.dist)
          ??????}
          ??????
          ?fs.writeFileSync(`${paths.dist}/index.html`,?await?inject())
          ?fs.copySync(paths.assets,?paths.dist)
          ?fs.copySync(paths.getPath('../../src'),?paths.dist)
          ?fs.removeSync(`${paths.dist}/source.html`)

          ?const?less?=?await?globby(`${paths.dist}/**/*.less`)

          ??????less.map(item?=>?fs.removeSync(item))
          ??????
          ?console.log('----------?picpic?build?success!?----------?\n')
          }

          try?{
          ?main()
          }?catch?(error)?{
          ?console.log('----------?picpic?build?error!?----------?\n')
          ?console.error(error)
          }

          這里的inject就是注入組件和數(shù)據(jù)之后的html,接下來展示一下如何進行組件注入。

          //?build/inject/index.js

          const?fs?=?require('fs-extra')
          const?injectData?=?require('./injectData')
          const?injectStyles?=?require('./injectStyles')
          const?injectTemplates?=?require('./injectTemplates')
          const?injectJs?=?require('./injectJs')
          const?paths?=?require('../utils/paths')

          function?Inject?(){
          ?this.html?=?''

          ?this.getSource?=?()?=>?{
          ??this.html?=?fs.readFileSync(paths.getPath('../../src/source.html')).toString()

          ??return?new?Promise(resolve?=>?resolve(this.html))
          ?}

          ?this.injectData?=?async?()?=>?{
          ??this.html?=?await?injectData(this.html)

          ??return?new?Promise(resolve?=>?resolve(this.html))
          ?}

          ?this.injectStyles?=?async?()?=>?{
          ??this.html?=?await?injectStyles(this.html)

          ??return?new?Promise(resolve?=>?resolve(this.html))
          ?}

          ?this.injectTemplates?=?async?()?=>?{
          ??this.html?=?await?injectTemplates(this.html)

          ??return?new?Promise(resolve?=>?resolve(this.html))
          ?}
          }

          const?inject?=?async?()?=>?{
          ?return?await?new?Inject()
          ??.getSource()
          ??.then(res?=>?injectData(res))
          ??.then(res?=>?injectStyles(res))
          ??.then(res?=>?injectTemplates(res))
          ??.then(res?=>?injectJs(res))
          }

          module.exports?=?inject

          通過返回this的方法進行鏈式調(diào)用,比一層一層用方法包裹優(yōu)雅很多,有沒有感受到代碼之美,嘻嘻。

          injectStyles injectTemplates injectJs這三種方法異曲同工,原理特簡單,就是字符串替換,不過這里要注意空格,少一個都匹配不到。

          //?build/inject/injectStyles.js

          const?globby?=?require('globby')
          const?paths?=?require('../utils/paths')

          module.exports?=?async?str?=>?{
          ?const?paths_source?=?await?globby([?`${paths.getPath('../../src/components/**/*.css')}`?])
          ?const?paths_target?=?[]

          ?paths_source.map(item?=>
          ??paths_target.push(item.replace('src',?'.').split('/').slice(-4).join('/'))
          ??????)

          ?const?items?=?paths_target.map(item?=>?'@import?'?+?"'"?+?item?+?"'"?+?';'?+?'\n')

          ?return?str.replace(
          ??`
          ??????
          `
          ,
          ??`
          ??????
          `

          ?)
          }


          在頁面中,三種占位符分別用于注入組件相關(guān)的文件:




          <style>style>


          <template-slot>template-slot>


          <script?id="component_scripts">script>

          注入之后的結(jié)果為:




          <style>
          @import?'./components/Detail/index.css';
          @import?'./components/Empty/index.css';
          @import?'./components/FolderSelect/index.css';
          @import?'./components/Header/index.css';
          @import?'./components/ImgItems/index.css';
          @import?'./components/Msg/index.css';
          @import?'./components/Pagination/index.css';
          style
          >


          <include?src="./components/Detail/index.html">include>
          <include?src="./components/Empty/index.html">include>
          <include?src="./components/FolderSelect/index.html">include>
          <include?src="./components/Header/index.html">include>
          <include?src="./components/ImgItems/index.html">include>
          <include?src="./components/Msg/index.html">include>
          <include?src="./components/Pagination/index.html">include>


          <script?src="./components/Detail/index.js">script>
          <script?src="./components/Empty/index.js">script>
          <script?src="./components/FolderSelect/index.js">script>
          <script?src="./components/Header/index.js">script>
          <script?src="./components/ImgItems/index.js">script>
          <script?src="./components/Msg/index.js">script>
          <script?src="./components/Pagination/index.js">script>

          不要詬病組件文件夾大寫,我是react的擁躉,如果不是因為web-component強制使用-分割符小寫,所有的組件我都希望大寫,因為辨識度比前者高很多。

          通過node預(yù)編譯目錄數(shù)據(jù)

          主要是通過dree到處樹形數(shù)據(jù),通過imageinfo獲取圖片長寬,然后再進行數(shù)據(jù)裁剪,把需要的數(shù)據(jù)進行組裝后導(dǎo)出。代碼多且雜,這里僅結(jié)果,有興趣的可以去github看代碼。

          {
          ????"name":"assets",
          ????"type":"directory",
          ????"size":"1.14MB",
          ????"children":[
          ????????{
          ????????????"name":"projects",
          ????????????"type":"directory",
          ????????????"size":"1.14MB",
          ????????????"children":[
          ????????????????{
          ????????????????????"name":"picpic",
          ????????????????????"type":"directory",
          ????????????????????"size":"1.14MB",
          ????????????????????"children":[
          ????????????????????????{
          ????????????????????????????"name":"choose_gh_pages.jpg",
          ????????????????????????????"type":"file",
          ????????????????????????????"extension":"jpg",
          ????????????????????????????"size":"61.1KB",
          ????????????????????????????"dimension":"2020x940",
          ????????????????????????????"path":"projects/picpic/choose_gh_pages.jpg"
          ????????????????????????},
          ????????????????????????{
          ????????????????????????????"name":"folder_hover_status.jpg",
          ????????????????????????????"type":"file",
          ????????????????????????????"extension":"jpg",
          ????????????????????????????"size":"116.74KB",
          ????????????????????????????"dimension":"956x1896",
          ????????????????????????????"path":"projects/picpic/folder_hover_status.jpg"
          ????????????????????????}
          ????????????????????]
          ????????????????}
          ????????????]
          ????????}
          ????]
          }

          然后寫入到html中:

          //?build/inject/injectData.js

          const?{?getFileTree?}?=?require('../utils')

          module.exports?=?async?str?=>?{
          ?const?tree?=?await?getFileTree()

          ?return?str.replace(
          ??`
          ??????
          ????????????PicPic
          ??????
          `
          ,
          ??`
          ??????
          ????????????PicPic
          ????????????
          ??????
          `

          ?)
          }

          做成命令行工具

          僅僅做成上面那樣使用起來,還需要別人clone你的倉庫,后續(xù)升級麻煩,而且編譯源文件什么的都暴露出來了,看起來臟的不行,所以不僅要產(chǎn)品本身美,使用方式也需要簡單優(yōu)雅。

          package.json 中添加如下字段,發(fā)布包之后,當別人在 npm i @matrixage/picpic 時會生成命令行工具文件:

          "bin":?{
          ????"picpic":?"./bin/index.js"
          }

          編寫命令行工具代碼:

          //?bin/index.js

          #!/usr/bin/env?node

          const?fs?=?require('fs-extra')
          const?path?=?require('path')
          const?child_process?=?require('child_process')
          const?pkg?=?require(`${process.cwd()}/package.json`)

          const?main?=?()?=>?{
          ?const?args?=?process.argv[2]
          ?const?root?=?process.cwd()
          ?const?getPath?=?p?=>?path.join(__dirname,?p)

          ?switch?(args)?{
          ??case?'init':
          ???pkg['scripts']['build']?=?'picpic?build'

          ???fs.writeFileSync('./package.json',?JSON.stringify(pkg,?null,?2).concat('\n'))
          ???if?(!fs.existsSync(`${root}/assets`))?fs.mkdirSync(`${root}/assets`)
          ???if?(!fs.existsSync(`${root}/.github`))?fs.mkdirSync(`${root}/.github`)
          ???if?(!fs.existsSync(`${root}/.gitignore`))?fs.writeFileSync(`${root}/.gitignore`,`/dist?\n/node_modules?\n.DS_Store`)
          ???fs.copySync(getPath('../.github'),?`${root}/.github`)

          ???console.log('----------?picpic?init?success!?----------?\n')
          ???break
          ??case?'build':
          ???child_process.execSync(`node?${getPath('../build/index.js')}`)
          ???break
          ??default:
          ???break
          ?}
          }

          try?{
          ?main()

          ?process.exit(0)
          }?catch?(e)?{
          ?console.error(e)

          ?process.exit(1)
          }

          當用戶 npm i @matrixage/picpic 之后,在 package.jsonscripts 字段中加入 "init": "picpic init" ,然后執(zhí)行npm run init,項目根目錄會生成 .github assets 文件夾以及 .gitignore 文件。

          這個時候用戶只需要把圖片移動到assets文件夾中,支持在assets中新建任意不超過12層的文件夾。然后提交到github,github action將自動進行構(gòu)建,然后把構(gòu)建出的dist文件夾推送到倉庫的gh-pages上,如果沒有開啟gh-pages請自行開啟。

          至此,全部構(gòu)建流程講解完畢。這個過程,寫預(yù)編譯代碼其實是最簡單,麻煩的是:

          • 如何構(gòu)建美的應(yīng)用?
          • 如何讓用戶簡單且優(yōu)雅地使用?

          回首我做過的所有項目,花在邏輯上的時間其實是最少的,寫邏輯是跟機器對話,機器嘛,就那幾句話,記住就行了。而畫界面,做交互,是在跟人,首先就是跟自己進行對話,了解自己內(nèi)心深處的想法,然后就是跟用戶進行對話,其實你把用戶當成千千萬萬個我,那你就能感受到,你的idea,該如何生長,你的畫,該是何模樣。

          總之,以人為本。

          DEMO地址:https://matrixage.github.io/picpic_example/

          項目地址:https://github.com/MatrixAges/picpic

          注意,在github的readme文件中使用username.github.io/repo/~這樣的鏈接,github會將之自動轉(zhuǎn)化為camo.githubusercontent.com該host下的圖片鏈接,該鏈接被DNS污染了,如要預(yù)覽,請在host中加入如下DNS解析:

          199.232.96.133?raw.githubusercontent.com
          199.232.96.133?camo.githubusercontent.com

          如果你發(fā)現(xiàn)訪問github很慢,那是因為本地服務(wù)商在進行DNS網(wǎng)絡(luò)過濾,加入如下host跳過服務(wù)商網(wǎng)絡(luò)過濾:

          140.82.112.3?github.com

          如果你的倉庫的主分支是master而不是main,請自行修改構(gòu)建腳本依賴分支為master,在.github/workflows/ci.yml中。


          最后


          • 歡迎加我微信(winty230),拉你進技術(shù)群,長期交流學(xué)習(xí)...

          • 歡迎關(guān)注「前端Q」,認真學(xué)前端,做個專業(yè)的技術(shù)人...

          點個在看支持我吧
          瀏覽 36
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩操逼黄片 | 围产精品久久久久久 | 在线无码高清播放 | 一级做a爰久久 | 亚洲黄色在线网站 |