2020-02-25 談一次開源項目貢獻

給開源項目做貢獻,一方面能夠增加自己看源碼的積累升酣,另一方面也是對自身代碼能力的檢驗舷暮。因為開源項目本身已經(jīng)是完整的項目,對于開源項目我們能貢獻的大概分三種(docs文檔噩茄、bug脚牍、feature新特性),其中難易度為 docs < bug < feature巢墅,但我提倡入手的時候可以從修復(fù)bug開始,這樣更利于熟悉項目的代碼券膀。

以下以我最近給vue-i18n的一次pr為例君纫,展示下從issuepr的整體流程。

查看issue

https://github.com/kazupon/vue-i18n/issues/779芹彬,對于一個反映bug的issue蓄髓,我們首先需要確認下是否能夠復(fù)現(xiàn),該issue上面已經(jīng)提供了復(fù)現(xiàn)鏈接舒帮;demo中設(shè)置了formatFallbackMessages: true会喝,在組件<i18n>使用不在messagesI accept {tos}.時陡叠,將其錯誤解析為I accept [object Object].

<i18n path="I accept {tos}." tag="div">
  <template #tos>
    <a href="about:blank">{{ $t('Terms of Service') }}</a>
  </template>
</i18n>

整個issue中提到了formatFallbackMessages這個配置,由于本人沒有使用過肢执,所以需要理解這個配置項枉阵,打開官網(wǎng)文檔發(fā)現(xiàn)該配置中文部分是缺失的(目前已經(jīng)補充)。fallback-interpolation文檔

注意message中的key是帶有占位變量的

const messages = {
  ru: {
    'Hello {name}': 'Здравствуйте {name}'
  }
}

const i18n = new VueI18n({
  locale: 'ru',
  fallbackLocale: 'en',
  formatFallbackMessages: true,
  messages
})

當(dāng)模板template如下時:

<p>{{ $t('Hello {name}', { name: 'John' }}) }}</p>
<p>{{ $t('The weather today is {condition}!', { condition: 'sunny' }) }}</p>

將會輸出:

<p>Здравствуйте John</p>
<p>The weather today is sunny!</p>

該feature的作者原意是想以en的翻譯文案作為message预茄,同時將en的文案作為其他語言的key兴溜,這樣在代碼層面就可以很容易的理解多語言的內(nèi)容。理解了配置項的含義后耻陕,我們的修復(fù)bug之路就可以邁進下一步了拙徽。

查看源碼

首先我們查看項目的貢獻文檔(大多數(shù)項目都會有貢獻說明文檔),雖說文檔上讓開發(fā)者在個人項目的v8.x分支編寫诗宣,但我通常都會在v8.x切出fix分支膘怕,這樣更利于之后對該項目其他issue的貢獻。

我看代碼的流程通常是從調(diào)用方式開始看的召庞,但是這是在組件<i18n>中使用岛心,所以在源碼中難以查找其調(diào)用方式,所以這次我們以配置項formatFallbackMessages為入口查看裁眯。

通過全局搜索查到鹉梨,src/index.js_warnDefault方法中有其配置的判斷(且僅有這里有調(diào)用):

if (this._formatFallbackMessages) {
  const parsedArgs = parseArgs(...values)
  return this._render(key, 'string', parsedArgs.params, key)
} else {
  return key
}

_warnDefault是當(dāng)獲取不到相關(guān)key的時候進行調(diào)用,而在我們知道這個配置項的含義就是找不到key的使用使用翻譯值當(dāng)key穿稳,所以我們接著往下看this._render方法:

_render (message: string, interpolateMode: string, values: any, path: string): any {
  let ret = this._formatter.interpolate(message, values, path)

  // If the custom formatter refuses to work - apply the default one
  if (!ret) {
    ret = defaultFormatter.interpolate(message, values, path)
  }

  // if interpolateMode is **not** 'string' ('row'),
  // return the compiled data (e.g. ['foo', VNode, 'bar']) with formatter
  return interpolateMode === 'string' ? ret.join('') : ret
}

這段函數(shù)存皂,message會經(jīng)過formatter的interpolate方法,interpolate方法逢艘,以$t的調(diào)用來簡單說明旦袋,會根據(jù)第二個參數(shù)的數(shù)據(jù)類型進行判斷并組合,例如$t('hello {1}', {1: 'me'})會被編譯為hello me它改、$t('hello {1}', ['me'])也會被編譯為hello me疤孕,當(dāng)然還有vnode的形式調(diào)用(v-html使用);經(jīng)過編譯后會根據(jù)interpolateMode值進行不同的組合央拖,這里我們看到_warnDefault中調(diào)用的_render是傳遞寫死的string祭阀,通過查看其他_render的調(diào)用發(fā)現(xiàn)interpolateMode還能是raw

測試

到這一步鲜戒,我們懷疑可能是interpolateMode寫死string的可能专控,為此我們可以寫一個test函數(shù),這段測試函數(shù)可以在原有的單元測試中添加遏餐,在test/interpolation.test.js中我們找到了如何對<i18n>組件測試的方法:

describe('included translation locale message', () => {
  it('should be interpolated', done => {
    const el = document.createElement('div')
    const vm = new Vue({
      i18n,
      render (h) {
        return h('i18n', { props: { path: 'term' } }, [
          h('template', { slot: '0' }, [
            h('a', { domProps: { href: '/term', textContent: this.$t('tos') } })
          ])
        ])
      }
    }).$mount(el)
    nextTick(() => {
      assert.strictEqual(
        vm.$el.innerHTML,
        'I accept xxx <a href=\"/term\">Term of service</a>.'
      )
    }).then(done)
  })
})

我們就依葫蘆畫瓢伦腐,增加一個describe,設(shè)置formatFallbackMessages : true失都,i8n的path設(shè)置為帶有參數(shù)的string

describe('formatFallbackMessages', () => {
  let i18n
  beforeEach(() => {
    i18n = new VueI18n({
      locale: 'en',
      messages,
      formatFallbackMessages: true
    })
  })

  it('should be interpolated', done => {
    const el = document.createElement('div')
    const vm = new Vue({
      i18n,
      render (h) {
        return h('i18n', { props: { path: 'I am {0}' } }, [
          h('template', { slot: '0' }, [
            h('a', { domProps: { href: '/term', textContent: this.$t('tos') } })
          ])
        ])
      }
    }).$mount(el)

    nextTick(() => {
      assert.strictEqual(
        vm.$el.innerHTML,
        'I am <a href=\"/term\">Term of service</a>'
      )
    }).then(done)
  })
})

之后我們通過修改_warnDefault中的interpolateModeraw柏蘑,輸出符合預(yù)想幸冻,確實是這個參數(shù)的原因,然后我們就可以進行修復(fù)工作了咳焚。

修復(fù)

通過調(diào)用的源頭發(fā)現(xiàn)洽损,interpolateMode參數(shù)一直有傳遞進去,所以我們只要修改沿途調(diào)用的interpolateMode為傳遞的值黔攒,最后傳遞進_render就可以了趁啸。

if (this._formatFallbackMessages) {
  const parsedArgs = parseArgs(...values)
  return this._render(key, interpolateMode, parsedArgs.params, key)
} else {
  return key
}

進行補充測試item和全量測試

補充item后,命令行跑npm run test督惰,根據(jù)輸出test:unit測試是跑通的不傅,但是test:e2e報錯,提示我安裝jdk赏胚,當(dāng)時我想著代碼沒問題就可以提交pr了访娶。

提交pr

這里有個小技巧,在給element-ui貢獻代碼時我就發(fā)現(xiàn)觉阅,如果你在commit信息中添加相關(guān)的issue編號崖疤,也就是https://github.com/kazupon/vue-i18n/issues/779中最后的數(shù)字,那么在該issue中就會關(guān)聯(lián)到你提交的commit(盡管這時你還沒提交pr)典勇。

image.png

提交pr后等待機器自動跑通test(現(xiàn)在開源項目一般都會有這一步驟)劫哼,發(fā)現(xiàn)還是跑不通test:e2e,這時負責(zé)pr的老哥就過來指導(dǎo)我了:


image.png

慚愧慚愧割笙,其實開源項目說明我是這時候才認真看的权烧,根據(jù)要求修改后,我就嘗試在本地跑通test:e2e命令伤溉,安裝jdk后還是不能跑通般码,這令我很困惑畢竟單元測試也跑通了。

為此我嘗試了倆種方式確認:
1.回退到我修改之前的版本乱顾,跑test:e2e命令板祝,發(fā)現(xiàn)還是跑不通,那說明要不就我本地環(huán)境不一樣走净,或者本身就跑不通券时。
2.在項目的pr頁面查看我之前合并的pr,發(fā)現(xiàn)也是跑不通的伏伯,這時我就確定項目本身跑不通革为。

這樣子我就嘗試自己修復(fù)e2e測試,但無果舵鳞;第二天我發(fā)現(xiàn)主分支由作者本人更新了,拉下來后發(fā)現(xiàn)是能跑通e2e測試琢蛤,所以使用git pull vue-i18n v8.x --rebase命令后(vue-i18n是我自己設(shè)置的遠程地址別名蜓堕,--rebase能讓我的commit延后到主分支之后)抛虏,再次提交pr就可以了。

后話

同一天套才,項目作者合并了我的pr迂猴,這段修復(fù)流程應(yīng)該就畫上了句號。但并不背伴,因為之前我們說過該配置沒有中文文檔沸毁,另外也有issue反映沒有中文文檔不好理解,所以我又提了一個pr用于docs文檔補充(https://github.com/kazupon/vue-i18n/pull/785
)傻寂。

其實給開源項目做貢獻息尺,能夠讓我在下班后學(xué)習(xí)新編碼結(jié)構(gòu)和對設(shè)計模式的理解,也能讓我暫時脫離對業(yè)務(wù)的編寫情緒中(當(dāng)然了疾掰,工作中有時候也會做優(yōu)化相關(guān)的有意思的工作)搂誉,所以我有空還是會上去貢獻過的項目中看看issue,能否作出pr貢獻静檬,這即是對開源項目的理解熟悉炭懊,也是對本身能力的提升。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拂檩,一起剝皮案震驚了整個濱河市侮腹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稻励,老刑警劉巖父阻,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異钉迷,居然都是意外死亡至非,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門糠聪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荒椭,“玉大人,你說我怎么就攤上這事舰蟆∪せ荩” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵身害,是天一觀的道長味悄。 經(jīng)常有香客問我,道長塌鸯,這世上最難降的妖魔是什么侍瑟? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上涨颜,老公的妹妹穿的比我還像新娘费韭。我一直安慰自己,他們只是感情好庭瑰,可當(dāng)我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布星持。 她就那樣靜靜地躺著,像睡著了一般弹灭。 火紅的嫁衣襯著肌膚如雪督暂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天穷吮,我揣著相機與錄音逻翁,去河邊找鬼。 笑死酒来,一個胖子當(dāng)著我的面吹牛卢未,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播堰汉,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼辽社,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了翘鸭?” 一聲冷哼從身側(cè)響起滴铅,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎就乓,沒想到半個月后汉匙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡生蚁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年噩翠,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邦投。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡伤锚,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出志衣,到底是詐尸還是另有隱情屯援,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布念脯,位于F島的核電站狞洋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏绿店。R本人自食惡果不足惜茫负,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦乱凿、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至堡距,卻和暖如春甲锡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背羽戒。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工缤沦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人易稠。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓缸废,卻偏偏與公主長得像,于是被迫代替她去往敵國和親驶社。 傳聞我的和親對象是個殘疾皇子企量,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容