1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 8923字,需瀏覽 18分鐘

         ·

        2021-02-10 03:05

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

        起因

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

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

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

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

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

        所以就有了它:

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

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

        picpic

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

        服務(wù)基于github pages,自動(dòng)化構(gòu)建使用的是github actions,通過自動(dòng)化構(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)建這樣一個(gè)單頁面應(yīng)用的。

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

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

        所以我決定擺脫webpack,使用離線版本的vue.min.js來構(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)行組件化開發(fā)

        在html頂部引入include.js,改文件的作用是在文檔加載完成之后將include標(biāo)簽中的地址通過同步的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出場,其實(shí)你理解的webpack,不過時(shí)穿上紳士馬甲的node編譯腳本,本質(zhì)上還是預(yù)編譯。

        所以不用webpack,我們直溯本源,手寫預(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,接下來展示一下如何進(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

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

        injectStyles injectTemplates injectJs這三種方法異曲同工,原理特簡單,就是字符串替換,不過這里要注意空格,少一個(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(
        ??`
        ??????
        `
        ,
        ??`
        ??????
        `

        ?)
        }


        在頁面中,三種占位符分別用于注入組件相關(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的擁躉,如果不是因?yàn)閣eb-component強(qiáng)制使用-分割符小寫,所有的組件我都希望大寫,因?yàn)楸孀R度比前者高很多。

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

        主要是通過dree到處樹形數(shù)據(jù),通過imageinfo獲取圖片長寬,然后再進(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"
        ????????????????????????}
        ????????????????????]
        ????????????????}
        ????????????]
        ????????}
        ????]
        }

        然后寫入到html中:

        //?build/inject/injectData.js

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

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

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

        ?)
        }

        做成命令行工具

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

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

        "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)
        }

        當(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中新建任意不超過12層的文件夾。然后提交到github,github action將自動(dòng)進(jìn)行構(gòu)建,然后把構(gòu)建出的dist文件夾推送到倉庫的gh-pages上,如果沒有開啟gh-pages請自行開啟。

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

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

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

        總之,以人為本。

        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ù)覽,請?jiān)趆ost中加入如下DNS解析:

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

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

        140.82.112.3?github.com

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

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

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            一级a毛片高清视频 | 亚洲AV第二区国产精品 | 国产 日韩 欧美 综合 | 99久久免费观看视频 | 天天躁夜夜躁狠狠躁AV | 国产乱xxxxx987国语对白 | 最新中文字幕MV第三季歌词完整版 | 骚逼少妇| 亚洲免费三级 | 精品欧美一区二区三区成人片 |