0%

前言

最近把日期選擇器開發到一個段落了
差不多要發布了吧…

等等! 我的開發紀錄才寫了兩篇欸
後面還有很多內容沒寫
而且其實唯一寫的那兩篇規劃的東西大部分都改了…

好啦我之後補!!!
現在誰也不能阻止我發布套件!!!

NPM 是什麼?

NPM, Node Package Manager
是 NodeJS 專屬的 套件管理工具

就像 Python 有 pip,Ruby 有 gem 一樣
裡面放著來自世界各地工程師提供的套件
讓你可以不用從頭開始打造,就能使用許多功能

npm command line tool 會在你安裝 NodeJS 時一起被安裝

今天這篇便是要來介紹如何在 npm 上發布自己的套件
那我當然就以 react native 為例囉!

發布步驟

1. 建立專案資料夾

1
2
mkdir react-native-neat-date-picker
cd react-native-neat-date-picker

2. 連結 github

到 github 上建立一個新的 repository 叫 react-native-neat-date-picker
並將它連結到你的專案資料夾

1
2
3
4
5
6
7
echo "# test" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/roto93/react-native-neat-date-picker.git
git push -u origin main

3. 建立專案

執行 npm init
接下來他會問你一系列問題
以下是我的輸入值

1
2
3
4
5
6
7
8
9
package name: (react-native-date-picker) react-native-neat-date-picker
version: (1.0.0)
description: An easy-to-use date picker for React Native
entry point: (index.js)
test command:
git repository: (https://github.com/roto93/react-native-date-picker.git)
keywords: react-native, date, datePicker, date-picker, date picker, react native
author: roto93
license: (ISC) MIT
  • package name: 套件的名稱。
  • version: 版本,通常從 1.0.0 開始。
    之後每次你 publish 套件到 npm 時都必須更新版本,如果沒更新 npm 不會讓你發布。
  • description: 套件描述。可以在這裡描述套件的主要功能。
  • entry point: 套件的程式進入點。
    一般來說不會更動,維持index.js就好。
    雖然自訂進入點也是可以,但會不利於其他人 contribute
  • test command: 執行測試的指令,我目前沒用到所以沒深入研究。
  • git repository: 原始碼來源。
    之所以會在 npm init 之前就先建立 github repo 就是為了讓這裡能自動設定。
    我個人是認為 github 要打從一開始就設定好比較不會有問題
  • keywords: 關鍵字。
    意思是其他人可以藉由查詢什麼關鍵字來找到你的套件,越多越相關越好。
  • author: 作者。
    通常就放自己的 github 帳號,或名字
  • license: 認證。
    open source 的認證百百種,通常最簡單的就選 ISC 或 MIT。
    他們的意思都差不多是:「嘿 自己拿去用啦 你可以自由修改 但不要哪天反過來說是我抄你的ㄛ」

4. 建立套件進入點

剛剛有提到 index.js 是套件的進入點,意思是你必須讓你的套件要提供的功能從 index.js 中 export 出去。
你可以選擇直接把套件主程式寫在 index.js 裡
或者是你也可以寫在其他檔案中,import 進 index.js 裡,再 export
我的作法是後者,因為一開始開發時我就直接把主元件寫在 components 資料夾裡了,
如果現在又改變結構的話還要處理一些 import 的路徑問題
但我懶

1
2
3
// index.js
import DatePicker from './src/components/NeatDatePicker'
export default DatePicker

5. 設定 Babel

Babel 簡單來說是一個語法轉換器。
以網頁為例,就是讓瀏覽器可以使用 JS 的新語法

很幸運地,有現成的 preset 可以使用

1
npm install metro-react-native-babel-preset --save-dev

接著新增一個 .babelrc 檔案
裡面放:

1
2
3
{
"presets": ["module:metro-react-native-babel-preset"]
}

這部分就 ok 了

6. 建立 .gitignore 和 .npmignore

這兩份檔案會影響到當你上傳套件或 push 到 github 時,要忽略那些檔案
這邊提供簡單的 template
可以再視情況做增減

.gitignore

1
2
3
4
5
6
7
8
9
10
11
# Logs
*.log
npm-debug.log

# Runtime data
tmp
build
dist

# Dependency directory
node_modules

.npmignore

1
2
3
4
5
6
7
8
9
10
11
12
# Logs
*.log
npm-debug.log

# Dependency directory
node_modules

# Runtime data
tmp

# Examples (If applicable to your project)
examples

7. 本地測試

在發布之前,比較好的做法是先進行本地安裝測試看看
如果先發布,後來卻發現有嚴重問題的話就會修改得很匆忙
因為很可能已經有人下載來用了
然後他們就會覺得這套件怎麼這麼爛XD
與其如此不如在發布前先確保沒有什麼大問題

雖然這麼說
但我其實這步驟沒有成功
但我還是把流程放上來辣

首先在你即將發布套件的專案中執行
npm link
這行命令會告訴 npm
這個資料夾的專案是一個可安裝的套件喔

接著你進到本地的任何一個 app 專案中
執行 npm link react-native-neat-date-picker
便會進行本地安裝

如果以上步驟遇到問題
也可以選擇以路徑安裝
如: npm install {PATH_TO_PACKAGE}

這個做法的先天缺陷是一旦你對套件本身有做任何修改
你必須重新安裝一次,才能更新

不過很可惜上述兩個方法我都沒成功,
link 沒報錯,install 也沒 error
但 import 時就告訴我找不到 module
試了網路上很多方法都不行,所以我乾脆先跳過
直接發布試試看
結果可以 run ! 到底是怎樣 !!!

8. 發布

終於進到發布的環節了
首先我們登入 npm (你要先去註冊)

1
npm login

接著

1
npm publish

完成! 現在任何人都可以上去下載你提供的套件了
國家…不
我是說全世界都會感謝你的:)))

結語

上傳我的第一個 npm 套件實在是很興奮
可是也隱約有股壓力,因為不知道大家會不會覺得東西很爛
而且因為我發布的時候連 README 都很不完整,
也就是說前期下載的人可能完全不知道怎麼用
所以發布後到寫文的現在經過的這兩天
我便盡力把 README 補充到至少知道用法的地步
最後
希望世界上有人會喜歡我的套件
哈哈哈哈哈

什麼是 reaimated ?

reanimated 完整名稱是 react-native-reanimated
是一個專門用來做 react native 動畫的套件
在我寫這篇文的現在,最新穩定版本是 v2.2.0

前言

在過去 v1 時期,要學習還蠻不容易的
想當初我就是先接觸 v1,那時候我對 rn 和 js 的基礎都還不熟
看網路上的教學時一下看到用 Animated.Value,一下有人用 useRef.current
語法有點不一樣我就不行了,更不用說當時大多數人都還在用 class component
所以學得蠻挫折的,我到現在還不知道要怎麼用 v1 實作動畫

2020 年中推出的 v2 給了我一絲希望
他提供幾個方便的 hook,簡單幾行就可以實現動畫
太開心了
多虧了它我才能在菜鳥時期學會做一些不太複雜的動畫
(不過基礎還是應該要先會啦XD)

v2 版本也被很多 youtuber 推薦
其最大的優點在於能將動畫統一放到 UI thread 上執行
甚至提供一些 method 讓我們能操作 JS/UI thread
於是動畫就能變得非常流暢
我真的覺得一個 app 好不好用,順暢度真的佔了很大一部份

安裝

今天會想整理這篇是因為當初在安裝時耗了很多工夫
除了一直來來回回安裝
也很怕不小心把專案毀了回不去

這是因為我沒注意到不同 expo-cli 版本可以使用的 reanimated 版本是不一樣的
安裝前必須先確認好才行
不過現在已經沒有這個問題了

  1. npm 安裝
1
npm install react-native-reanimated
  1. 打開babel.config.js
1
2
3
4
5
6
7
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: ['react-native-reanimated/plugin'], // <-- 加上這行
};
};
  1. 重新啟動專案並清除 cache

再次啟動時不要執行npm start,而是

1
expo start -c

這步非常重要,當初不知道這個步驟的我浪費非常多時間
偏偏 doc 裡面也沒講 QQ

至此,只要安裝版本正確、有加 plugin、有清除 cache
就可以順跑ㄌ…我是說使用 reanimated 了

初探 worklet

worklet 是 v2 新發布的功能,介紹功能之前先讓大家看看它的用法

1
2
3
4
5
6
7
8
9
10

const doSomething = (input)=>{
// do something
}

const doSomethingWorklet = (input)=>{
'worklet'
// do something
}

跟一般的 function 幾乎沒差異
就只是在閉包裡頭第一行多寫一句'worklet'而已
當一個 fuction 被定義成 worklet,它就「能」被交由 UI thread 執行
至於什麼是 UI thread 嘛…

就定義而言,它是從原本的 javascript thread 中分離出來的一部份
你可以把 UI thread 看成是 JS 因忙不過來而請來的助理
顧名思義,UI thread 主要是用來處理畫面渲染的工作的

而 reanimated 引入 worklet 這個新用法正是為了讓動畫從 app 的運算中獨立出來
不要讓動畫被其他事情影響卡住,能夠順順跑

另外有發現我前面強調「能」由 UI thread 執行嗎?
這是因為如果真的要在 UI thread 執行,必須用runOnUI這個函式才行
如下:

1
2
3
<Button onPress={()=>{
runOnUI(doSomethingWorklet)(input) // doSomethingWorklet 將會在 UI thread 執行
}}>

那如果情況反過來,我想在 worklet 中執行 JS thread 怎麼辦?
方法很類似,只要用runOnJS就行了

1
2
3
4
const doSomethingWorklet = () => {
'worklet'
runOnJS(doSomething)(input) // doSomething 將會在 JS thread 執行
}

值得一提的是這個runOnJS會蠻常碰到的
例如我在做匯率 APP 的時候有些按鈕同時兼顧動畫跟 setState
state 的變動就必須用runOnJS來執行了

結語

今天的超入門介紹就到這邊
雖然還沒講到能實作動畫的階段
但我覺得快速確實地安裝好套件
並搞清楚workletrunOnUIrunOnJS的用法原理還是很重要的

下一篇介紹 reanimated 的文就會來說說useSharedValueuseAnimatedStyle怎麼使用!

在做 SideProject 時很常遇到一個狀況
TextInput 在 Focus 狀態時,是沒辦法立刻觸發其他按鈕的
你必須先點擊螢幕一次 (TextInput和鍵盤以外的地方),將鍵盤收起
才能觸發別的按鈕
其實不只是按鈕,其他需要 recieve touch 的元件也有這個問題

至於為什麼強調是在做 sideProject 的時候呢?
因為這在一般情況不太算是問題
直到我以使用者的角度來操作app時才發現
當我預期點擊按鈕有用時,結果居然只是給我收起鍵盤而已?
真的很煩躁啊!我的耐心都到哪去了
這時我才了解使用者經驗的精隨
任何操作都要有預期的效果
只要有任何一次的點擊失效
負評的心情就來了

回到問題本身,會有這個限制我想是因為輸入文字跟顯示鍵盤這兩件事是密切相關的
該輸入文字的時候就該顯示鍵盤
不打算輸入文字的時候就關閉鍵盤
在流程操作上比較直觀
但就像我說的,這不符合使用者的直覺
那該怎麼控制呢?

ScrollView 來幫個忙


ScrollView 有一個屬性是我們需要的
叫做 keyboardShouldPersistTaps
一句話解釋:鍵盤打開的時候要不要接收觸控訊號?

可以設定三種值:
'never': 當子元件的 TextInput focus 時,點擊文字框和鍵盤以外的地方會關閉鍵盤,當這件事發生時,子元件不會接收到 tap
'always': 鍵盤不會自動收起,ScrollView 不會接收到 tap (但可以滑動),子元件可以接收到 tap
'handled': 如果 tap 被子元件或父元件 catch,鍵盤不會自動收起;但如果是被 ScrollView catch,鍵盤還是會收起來。

所以策略是這樣的
我們可以將此屬性設為'handled',先防止鍵盤自動關閉
這時按鈕就能互動了
接著再手動控制鍵盤關閉的時機就好
控制鍵盤的方式是用 react native 的KeyboardAPI

1
2
3
4
5
6
7
8

import {ScrollView, Keyboard} from 'react native'

<ScrollView keyboardShouldPersistTaps="handled">
<TextInput/>
<TouchableOpacity/>
</ScrollView>

正當我以為沒問題的時候來了個小插曲ˋˊ

檢查上層了嗎?


在某一次我完成這些設定後卻發現沒效果!? 按鈕還是不能按
經過一番研究後終於知道了
設定沒效果代表在此元件之上一定還有另一層沒設定的 ScrollView
所以只要找到該標籤,加上keyboardShouldPersistTaps的屬性就可以了
記得要一路檢查到 root 哦