給開源項目作貢獻,一方面可以增長本身看源碼的積累,另外一方面也是對自身代碼能力的檢驗。由於開源項目自己已是完整的項目,對於開源項目咱們能貢獻的大概分三種(docs文檔、bug、feature新特性),其中難易度爲 docs < bug < feature,但我提倡入手的時候能夠從修復bug開始,這樣更利於熟悉項目的代碼。html
如下以我最近給vue-i18n
的一次pr爲例,展現下從issue到pr的總體流程。vue
github.com/kazupon/vue…,對於一個反映bug的issue,咱們首先須要確認下是否可以復現,該issue上面已經提供了復現連接;demo中設置了formatFallbackMessages: true
,在組件<i18n>
使用不在messages的I accept {tos}.
時,將其錯誤解析爲I accept [object Object].
node
<i18n path="I accept {tos}." tag="div">
<template #tos>
<a href="about:blank">{{ $t('Terms of Service') }}</a>
</template>
</i18n>
複製代碼
整個issue中提到了formatFallbackMessages
這個配置,因爲本人沒有使用過,因此須要理解這個配置項,打開官網文檔發現該配置中文部分是缺失的(目前已經補充)。fallback-interpolation文檔git
注意message中的key是帶有佔位變量的github
const messages = {
ru: {
'Hello {name}': 'Здравствуйте {name}'
}
}
const i18n = new VueI18n({
locale: 'ru',
fallbackLocale: 'en',
formatFallbackMessages: true,
messages
})
複製代碼
當模板template以下時:npm
<p>{{ $t('Hello {name}', { name: 'John' }}) }}</p>
<p>{{ $t('The weather today is {condition}!', { condition: 'sunny' }) }}</p>
複製代碼
將會輸出:element-ui
<p>Здравствуйте John</p>
<p>The weather today is sunny!</p>
複製代碼
該feature的做者原意是想以en
的翻譯文案做爲message,同時將en
的文案做爲其餘語言的key,這樣在代碼層面就能夠很容易的理解多語言的內容。理解了配置項的含義後,咱們的修復bug之路就能夠邁進下一步了。設計模式
首先咱們查看項目的貢獻文檔(大多數項目都會有貢獻說明文檔),雖然說文檔上讓開發者在我的項目的v8.x
分支編寫,但我一般都會在v8.x
切出fix分支,這樣更利於以後對該項目其餘issue的貢獻。app
我看代碼的流程一般是從調用方式開始看的,可是這是在組件<i18n>
中使用,因此在源碼中難以查找其調用方式,因此此次咱們以配置項formatFallbackMessages
爲入口查看。dom
經過全局搜索查到,src/index.js的_warnDefault
方法中有其配置的判斷(且僅有這裏有調用):
if (this._formatFallbackMessages) {
const parsedArgs = parseArgs(...values)
return this._render(key, 'string', parsedArgs.params, key)
} else {
return key
}
複製代碼
_warnDefault
是當獲取不到相關key的時候進行調用,而在咱們知道這個配置項的含義就是找不到key的使用使用翻譯值當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
}
複製代碼
這段函數,message會通過formatter的interpolate方法,interpolate方法,以$t
的調用來簡單說明,會根據第二個參數的數據類型進行判斷並組合,例如$t('hello {1}', {1: 'me'})
會被編譯爲hello me
、$t('hello {1}', ['me'])
也會被編譯爲hello me
,固然還有vnode的形式調用(v-html使用);通過編譯後會根據interpolateMode
值進行不一樣的組合,這裏咱們看到_warnDefault
中調用的_render
是傳遞寫死的string
,經過查看其餘_render
的調用發現interpolateMode
還能是raw
。
到這一步,咱們懷疑多是interpolateMode
寫死string
的可能,爲此咱們能夠寫一個test函數,這段測試函數能夠在原有的單元測試中添加,在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,設置formatFallbackMessages : true
,i8n的path設置爲帶有參數的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
中的interpolateMode
爲raw
,輸出符合預想,確實是這個參數的緣由,而後咱們就能夠進行修復工做了。
經過調用的源頭髮現,interpolateMode
參數一直有傳遞進去,因此咱們只要修改沿途調用的interpolateMode
爲傳遞的值,最後傳遞進_render
就能夠了。
if (this._formatFallbackMessages) {
const parsedArgs = parseArgs(...values)
return this._render(key, interpolateMode, parsedArgs.params, key)
} else {
return key
}
複製代碼
補充item後,命令行跑npm run test
,根據輸出test:unit測試是跑通的,可是test:e2e報錯,提示我安裝jdk,當時我想着代碼沒問題就能夠提交pr了。
這裏有個小技巧,在給element-ui
貢獻代碼時我就發現,若是你在commit信息中添加相關的issue編號,也就是https://github.com/kazupon/vue-i18n/issues/779
中最後的數字,那麼在該issue中就會關聯到你提交的commit(儘管這時你還沒提交pr)。
提交pr後等待機器自動跑通test(如今開源項目通常都會有這一步驟),發現仍是跑不通test:e2e,這時負責pr的老哥就過來指導我了:
慚愧慚愧,其實開源項目說明我是這時候才認真看的,根據要求修改後,我就嘗試在本地跑通test:e2e命令,安裝jdk後仍是不能跑通,這令我很困惑畢竟單元測試也跑通了。
爲此我嘗試了倆種方式確認: 1.回退到我修改以前的版本,跑test:e2e命令,發現仍是跑不通,那說明要不就我本地環境不同,或者自己就跑不通。 2.在項目的pr頁面查看我以前合併的pr,發現也是跑不通的,這時我就肯定項目自己跑不通。
這樣子我就嘗試本身修復e2e測試,但無果;次日我發現主分支由做者本人更新了,拉下來後發現是能跑通e2e測試,因此使用git pull vue-i18n v8.x --rebase
命令後(vue-i18n是我本身設置的遠程地址別名,--rebase能讓個人commit延後到主分支以後),再次提交pr就能夠了。
同一天,項目做者合併了個人pr,這段修復流程應該就畫上了句號。但並不,由於以前咱們說過該配置沒有中文文檔,另外也有issue反映沒有中文文檔很差理解,因此我又提了一個pr用於docs文檔補充(github.com/kazupon/vue… )。
其實給開源項目作貢獻,可以讓我在下班後學習新編碼結構和對設計模式的理解,也能讓我暫時脫離對業務的編寫情緒中(固然了,工做中有時候也會作優化相關的有意思的工做),因此我有空仍是會上去貢獻過的項目中看看issue,可否做出pr貢獻,這便是對開源項目的理解熟悉,也是對自己能力的提高。