<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 快速搭建屬于自己的圖床

          共 8923字,需瀏覽 18分鐘

           ·

          2021-02-10 03:05

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

          起因

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

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

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

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

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

          所以就有了它:

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

          項(xiàng)目地址:https://github.com/MatrixAges/picpic

          picpic

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

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

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

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

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

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

          所以我決定擺脫webpack,使用離線版本的vue.min.js來(lái)構(gòu)建應(yīng)用,將部署時(shí)間控制在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進(jìn)行組件化開(kāi)發(fā)

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

          //?include.js

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

          接著通過(guò)自定義事件發(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?(){...})

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

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

          所以不用webpack,我們直溯本源,手寫(xiě)預(yù)編譯代碼。在picpic項(xiàng)目根目錄新建一個(gè)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,接下來(lái)展示一下如何進(jìn)行組件注入。

          //?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

          通過(guò)返回this的方法進(jìn)行鏈?zhǔn)秸{(diào)用,比一層一層用方法包裹優(yōu)雅很多,有沒(méi)有感受到代碼之美,嘻嘻。

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

          //?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(
          ??`
          ??????
          `
          ,
          ??`
          ??????
          `

          ?)
          }


          在頁(yè)面中,三種占位符分別用于注入組件相關(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>

          不要詬病組件文件夾大寫(xiě),我是react的擁躉,如果不是因?yàn)閣eb-component強(qiáng)制使用-分割符小寫(xiě),所有的組件我都希望大寫(xiě),因?yàn)楸孀R(shí)度比前者高很多。

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

          主要是通過(guò)dree到處樹(shù)形數(shù)據(jù),通過(guò)imageinfo獲取圖片長(zhǎng)寬,然后再進(jìn)行數(shù)據(jù)裁剪,把需要的數(shù)據(jù)進(jìn)行組裝后導(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"
          ????????????????????????}
          ????????????????????]
          ????????????????}
          ????????????]
          ????????}
          ????]
          }

          然后寫(xiě)入到html中:

          //?build/inject/injectData.js

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

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

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

          ?)
          }

          做成命令行工具

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

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

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

          編寫(xiě)命令行工具代碼:

          //?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)
          }

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

          這個(gè)時(shí)候用戶只需要把圖片移動(dòng)到assets文件夾中,支持在assets中新建任意不超過(guò)12層的文件夾。然后提交到github,github action將自動(dòng)進(jìn)行構(gòu)建,然后把構(gòu)建出的dist文件夾推送到倉(cāng)庫(kù)的gh-pages上,如果沒(méi)有開(kāi)啟gh-pages請(qǐng)自行開(kāi)啟。

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

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

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

          總之,以人為本。

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

          項(xiàng)目地址:https://github.com/MatrixAges/picpic

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

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

          如果你發(fā)現(xiàn)訪問(wèn)github很慢,那是因?yàn)楸镜胤?wù)商在進(jìn)行DNS網(wǎng)絡(luò)過(guò)濾,加入如下host跳過(guò)服務(wù)商網(wǎng)絡(luò)過(guò)濾:

          140.82.112.3?github.com

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

          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  欧美精品福利 | 欧美精品在线视频 | 国产日韩高清在线观看 | 波多野结衣精品在线 | 性无码免费一区二区三区四区 |