เบเปเปเบเบทเบเบฑเบเบชเบฐเบเบฒเบเบฑเบเบเบฐเบเบฐเบเบณ "เบฅเบนเบเบเปเบฒ-เปเบเบตเบเปเบงเบต" เบเบปเปเบงเปเบ, เปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเปเบเบเบตเปเบกเบตเบเบฒเบเปเบเปเบเบเบฑเปเบเบเบธเปเบกเบเบญเบเปเบกเปเบเบกเบตเบฅเบฑเบเบชเบฐเบเบฐ:
- เบเปเปเบเปเบฒเปเบเบฑเบเบเปเบญเบเปเบเบฑเบเบฎเบฑเบเบชเบฒเบเบฒเบเบเปเปเบกเบนเบเบเบตเปเบกเบตเบเบฒเบเปเบเบปเปเบฒเบชเบนเปเบฅเบฐเบเบปเบเบเบญเบเบเบนเปเปเบเปเปเบฅเบฐเบฅเบฐเบซเบฑเบเบเปเบฒเบ. เบเปเปเบกเบนเบเบเบฒเบเปเบเบปเปเบฒเปเบเบดเบเปเบกเปเบเบเบทเบเปเบเบฑเบเบฎเบฑเบเบชเบฒเปเบงเปเบชเบฐเปเบเบฒเบฐเปเบเบเบเบนเปเปเบเปเปเบญเบ, เปเบฅเบฐเบเบฒเบเบขเบทเบเบขเบฑเบเบเบญเบเบเบงเบฒเบกเบเบทเบเบเปเบญเบเบเบญเบเบเบงเบเบกเบฑเบเปเบเบตเบเบเบทเปเบเปเบเบฅเบฐเบเบฑเบเปเบเปเบเบเบญเบ.
- เบเปเปเบเปเบฒเปเบเบฑเบเบเปเบญเบเปเบเปเปเบเบทเปเบญเบเปเบกเปเบเปเบฒเบ. เปเบซเบเบเบปเบเบเบญเบเบเปเบฒเบฎเปเบญเบเบชเบฐเบซเบกเบฑเบเบชเบฒเบกเบฒเบเบเบทเบเบเบฐเบเบดเบเบฑเบเบขเบนเปเปเบเปเบเบทเบญเบเปเบฒเบ blockchain, เบเปเบญเบเบเบตเปเบกเบฑเบเปเบเบฑเบเปเบเปเบเปเบเบตเปเบเบฐเปเบเบฑเบเบฎเบฑเบเบชเบฒเบเปเบฒเบเบงเบเบเปเปเบกเบนเบเบเบตเปเบเปเบญเบเบเบฒเบ.
เบกเบต 2 เบเบฒเบเปเบเบฑเบเบฎเบฑเบเบชเบฒเบเบตเปเบเปเบญเบเบเปเบฒเบเบเบญเบเปเบเบชเปเบฒเบฅเบฑเบเบฅเบฐเบซเบฑเบเบเบนเปเปเบเป - เบฎเบฒเบเปเบง wallets เปเบฅเบฐเบชเปเบงเบเบเบฐเบซเบเบฒเบเบเบญเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบ. เบฎเบฒเบเปเบง wallets เบชเปเบงเบเบซเบผเบฒเบเปเบกเปเบเบเบญเบเปเบเบเบตเปเบชเบธเบ, เปเบเปเบเบฒเบเบเบตเปเบเบฐเปเบเปเปเบฅเบฐเบขเบนเปเปเบเบเบฒเบเบเปเปเปเบชเบเบเปเบฒ, เปเบเปเบเบฒเบเบเบฐเบซเบเบฒเบเบเบญเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบเปเบกเปเบเบเบฒเบเบเบฐเบชเบปเบกเบเบฐเบชเบฒเบเบเบตเปเบชเบปเบกเบเบนเบเปเบเบเบเบญเบเบเบงเบฒเบกเบเบญเบเปเบเปเบฅเบฐเบเบงเบฒเบกเบเปเบฒเบเปเบเบเบฒเบเบเปเบฒเปเบเป, เปเบฅเบฐเบเบฑเบเบชเบฒเบกเบฒเบเบเปเปเปเบชเบเบเปเบฒเบชเปเบฒเบฅเบฑเบเบเบนเปเปเบเปเบชเบธเบเบเปเบฒเบ.
เปเบเบเบเบดเบเบฒเบฅเบฐเบเบฒเบเบฑเบเบซเบกเบปเบเบเบตเป, เบเบงเบเปเบฎเบปเบฒเบเปเบญเบเบเบฒเบเบเบตเปเบเบฐเปเบฎเบฑเบเปเบซเปเบเบฒเบเบเบฐเบซเบเบฒเบเบเบตเปเบเบญเบเปเบเบเบตเปเบชเบธเบเบเบตเปเปเบฎเบฑเบเปเบซเปเบกเบฑเบเบเปเบฒเบเบเบถเปเบเปเบเบเบฒเบเบเบฑเบเบเบฐเบเบฒเบเปเบฒเบฎเปเบญเบเบชเบฐเบซเบกเบฑเบเบเบฒเบเปเบเปเบเบเบฑเปเบเบเบธเปเบกเบเบญเบเปเบเบเบเบฒเบเบชเบฐเบซเบเบญเบ API เบเปเบฒเบเบเบฒเบเบชเปเบฒเบฅเบฑเบเบเบฒเบเปเบฎเบฑเบเบงเบฝเบเบเบฑเบเบเบธเบฅเบฐเบเปเบฒเปเบฅเบฐเบฅเบฒเบเปเบเบฑเบ.
เบเบงเบเปเบฎเบปเบฒเบเบฐเบเบญเบเบเปเบฒเบเบเปเบฝเบงเบเบฑเบเบเบฐเบชเบปเบเบเบฒเบเบเบตเปเบเปเบฒเบเบฅเบธเปเบกเบเบตเป.
เบเบปเบเบเบงเบฒเบกเบเบฐเบเบฐเบเบญเบเบเปเบงเบเบเปเบฒเปเบเบฐเบเปเบฒเบเบฑเปเบเบเบญเบเปเบเบเบเบฑเปเบเบเบญเบเบเปเบฝเบงเบเบฑเบเบงเบดเบเบตเบเบฒเบเบเบฝเบเบชเปเบงเบเบเบฐเบซเบเบฒเบเบเบญเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบ, เบกเบตเบเบปเบงเบขเปเบฒเบเบฅเบฐเบซเบฑเบเปเบฅเบฐ screenshots. เบเปเบฒเบเบชเบฒเบกเบฒเบเบเบญเบเบซเบฒเบฅเบฐเบซเบฑเบเบเบฑเบเบซเบกเบปเบเปเบ
เบเบฐเบซเบงเบฑเบเบซเบเปเปเบเบญเบเบชเปเบงเบเบเบฐเบซเบเบฒเบเบเบญเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบ
เบชเปเบงเบเบเบฐเบซเบเบฒเบเบเบญเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบเบกเบตเบกเบฒเปเบเบฑเบเปเบงเบฅเบฒเบเบปเบเปเบฅเปเบง. เบเบงเบเบกเบฑเบเบเบฒเบเบปเบเบขเบนเปเปเบ Internet Explorer เปเบเบเบต 1999, เปเบ Firefox เปเบเบเบต 2004. เบขเปเบฒเบเปเบเบเปเบเบฒเบก, เบชเปเบฒเบฅเบฑเบเปเบงเบฅเบฒเบเบปเบเบเบฒเบเบเปเปเบกเบตเบกเบฒเบเบเบฐเบเบฒเบเบเบฝเบงเบชเปเบฒเบฅเบฑเบเบเบฒเบเบเบฐเบซเบเบฒเบ.
เบเบงเบเปเบฎเบปเบฒเบชเบฒเบกเบฒเบเปเบงเบปเปเบฒเปเบเปเบงเปเบฒเบกเบฑเบเบเบฒเบเบปเบเบเปเบญเบกเบเบฑเบเบชเปเบงเบเบเบฐเบซเบเบฒเบเปเบ Google Chrome เบฎเบธเปเบเบเบตเบชเบตเป. เปเบเปเบเบญเบ, เบเปเปเบกเบตเบเปเปเบกเบนเบเบชเบฐเปเบเบฒเบฐเบซเบผเบฑเบเบเบฒเบเบเบฑเปเบ, เปเบเปเบกเบฑเบเปเบกเปเบ Chrome API เบเบตเปเบเบฒเบเปเบเบฑเบเบเบทเปเบเบเบฒเบเบเบญเบเบกเบฑเบ: เปเบเบเปเบเปเปเบญเบปเบฒเบเบฐเบเบฐเบเบฐเบซเบผเบฒเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบเบชเปเบงเบเปเบซเบเปเปเบฅเบฐเบกเบตเบฎเปเบฒเบเปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเบ, Chrome เบเบปเบงเบเบดเบเปเบฅเปเบงเปเบเปเบเปเบฒเบเบปเบเบกเบฒเบเบเบฐเบเบฒเบเบชเปเบฒเบฅเบฑเบเบชเปเบงเบเบเบฐเบซเบเบฒเบเบเบญเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบ.
Mozilla เบกเบตเบกเบฒเบเบเบฐเบเบฒเบเบเบญเบเบเบปเบเปเบญเบ, เปเบเปเปเบซเบฑเบเบเบงเบฒเบกเบเบดเบเบปเบกเบเบญเบเบชเปเบงเบเบเบฐเบซเบเบฒเบ Chrome, เบเปเบฅเบดเบชเบฑเบเปเบเปเบเบฑเบเบชเบดเบเปเบเบชเปเบฒเบ API เบเบตเปเปเบเบปเปเบฒเบเบฑเบเปเบเป. เปเบเบเบต 2015, เปเบเบเบฒเบเบฅเบดเปเบฅเบตเปเบกเบเบญเบ Mozilla, เบเบธเปเบกเบเบดเปเบชเบเปเบเปเบเบทเบเบชเปเบฒเบเบเบฑเปเบเบเบทเปเบเบเบฒเบเปเบ World Wide Web Consortium (W3C) เปเบเบทเปเบญเปเบฎเบฑเบเบงเบฝเบเบเปเบฝเบงเบเบฑเบเบเปเปเบเปเบฒเบเบปเบเบเบฒเบเบเบฐเบซเบเบฒเบเบเปเบฒเบกเบเบปเบงเบเปเบญเบเปเบงเบฑเบ.
เบชเปเบงเบเบเบฐเบซเบเบฒเบ API เบเบตเปเบกเบตเบขเบนเปเบชเปเบฒเบฅเบฑเบ Chrome เปเบเปเบเบทเบเบเบฐเบเบดเบเบฑเบเปเบเบฑเบเบเบทเปเบเบเบฒเบ. เบงเบฝเบเบเบฒเบเบเบฑเปเบเบเปเบฒเบงเปเบเปเบเบทเบเบเบฐเบเบดเบเบฑเบเปเบเบเบเบฒเบเบชเบฐเบซเบเบฑเบเบชเบฐเบซเบเบนเบเบเบญเบ Microsoft (Google เบเบฐเบเบดเปเบชเบเบเบตเปเบเบฐเปเบเบปเปเบฒเบฎเปเบงเบกเปเบเบเบฒเบเบเบฑเบเบเบฐเบเบฒเบกเบฒเบเบเบฐเบเบฒเบ), เปเบฅเบฐเบเบฑเปเบเบเบฑเปเบ, เบชเบฐเบเบฑเบเบฎเปเบฒเบเปเบเปเบเบฒเบเบปเบเบเบถเปเบ.
เบขเปเบฒเบเปเบเบฑเบเบเบฒเบเบเบฒเบ, เบเปเปเบกเบนเบเบชเบฐเปเบเบฒเบฐเปเบกเปเบเบชเบฐเบซเบเบฑเบเบชเบฐเบซเบเบนเบเปเบเบ Edge, Firefox เปเบฅเบฐ Opera (เบชเบฑเบเปเบเบเบงเปเบฒ Chrome เบเปเปเบขเบนเปเปเบเบเบฑเบเบเบตเบฅเบฒเบเบเบทเปเบเบตเป). เปเบเปเปเบเบเบงเบฒเบกเปเบเบฑเบเบเบดเบ, เบกเบฒเบเบเบฐเบเบฒเบเปเบกเปเบเปเบซเบกเบฒเบฐเบชเบปเบกเบเบฑเบ Chrome เบชเปเบงเบเปเบซเบเป, เปเบเบฒเบฐเบงเปเบฒเบกเบฑเบเบเบทเบเบเบฝเบเปเบเบเบญเบตเบเปเบชเปเบเบฒเบเบเบฐเบซเบเบฒเบเบเบญเบเบกเบฑเบ. เบเปเบฒเบเบชเบฒเบกเบฒเบเบญเปเบฒเบเปเบเบตเปเบกเปเบเบตเบกเบเปเบฝเบงเบเบฑเบ WebExtensions API
เปเบเบเบเบฐเบเบญเบเบเบฒเบเบเบฐเบซเบเบฒเบ
เปเบเบฅเปเบเบฝเบงเบเบตเปเบเปเบญเบเบเบฒเบเบชเปเบฒเบฅเบฑเบเบเบฒเบเบเบฐเบซเบเบฒเบเปเบกเปเบ manifest (manifest.json). เบกเบฑเบเบเบฑเบเปเบเบฑเบ "เบเบธเบเปเบเบปเปเบฒ" เปเบเบเบฒเบเบเบฐเบซเบเบฒเบ.
เบเบฒเบเบชเบฐเปเบเบ
เบญเบตเบเบเบฒเบกเบเปเปเบกเบนเบเบชเบฐเปเบเบฒเบฐ, เปเบเบฅเป manifest เปเบกเปเบเปเบเบฅเป JSON เบเบตเปเบเบทเบเบเปเบญเบ. เบฅเบฒเบโเบฅเบฐโเบญเบฝเบโเปเบเบฑเบกโเบเบตเปโเบเบญเบโเบเบฐโเปเบ manifest เบเบตเปโเบกเบตโเบเปเปโเบกเบนเบโเบเปเบฝเบงโเบเบฑเบโเบเบฒเบโเบเบตเปโเบเบฐโเปเบโเบเบตเปโเบเบทเบโเบฎเบญเบโเบฎเบฑเบโเปเบโเบเบตเปโเบเบปเบงโเบเปเบญเบโเปเบงเบฑเบโเบชเบฒโเบกเบฒเบโเปเบเบดเปเบโเปเบเปโ
เบเบฐเปเบเบเบตเปเบเปเปเบขเบนเปเปเบเบชเบฐเปเบเบฒเบฐ "เบญเบฒเบเบเบฐ" เบเบทเบเบฅเบฐเปเบฅเบตเบ (เบเบฑเบ Chrome เปเบฅเบฐ Firefox เบฅเบฒเบเบเบฒเบเบเบงเบฒเบกเบเบดเบเบเบฒเบ, เปเบเปเบชเปเบงเบเบเบฐเบซเบเบฒเบเบเบฑเบเบชเบทเบเบเปเปเปเบฎเบฑเบเบงเบฝเบ).
เปเบฅเบฐเบเปเบฒเบเบฐเปเบเบปเปเบฒเบขเบฒเบเบเบฐเบเบถเบเบเบนเบเปเบญเบปเบฒเบเบฒเบเบเบธเบ.
- เบเบงเบฒเบกเปเบเบฑเบเบกเบฒ - เบงเบฑเบเบเบธเบเบตเปเบเบฐเบเบญเบเบกเบตเบเปเบญเบเบเปเปเบกเบนเบเบเปเปเปเบเบเบตเป:
- scripts โ array เบเบญเบ scripts เบเบตเปเบเบฐเบเบทเบเบเบฐเบเบดเบเบฑเบเปเบเบชเบฐเบเบฒเบเบเบทเปเบเบเบฒเบ (เบเบงเบเปเบฎเบปเบฒเบเบฐเปเบงเบปเปเบฒเบเปเบฝเบงเบเบฑเบเปเบฅเบทเปเบญเบเบเบตเปเบเปเปเบกเบฒเปเบฅเบฑเบเบเปเบญเบ);
- Page - เปเบเบเบเบตเปเบเบฐเปเบเบฑเบ scripts เบเบตเปเบเบฐเบเบทเบเบเบฐเบเบดเบเบฑเบเปเบเบซเบเปเบฒเปเบเบปเปเบฒ, เบเปเบฒเบเบชเบฒเบกเบฒเบเบเปเบฒเบเบปเบ html เบเบฑเบเปเบเบทเปเบญเบซเบฒ. เปเบเบเปเบฅเบฐเบเบตเบเบตเป, เบเปเบญเบเบชเบฐเบเบฃเบดเบเบเบฐเบเบทเบเบฅเบฐเปเบฅเบตเบ, เปเบฅเบฐ scripts เบเบฐเบเปเบญเบเบเบทเบเปเบชเปเปเบเบปเปเบฒเปเบเปเบเบซเบเปเบฒเปเบเบทเปเบญเบซเบฒ;
- เบขเบนเป โ เบเบธเบเบเบนเป, เบเปเบฒเบเปเปเบฅเบฐเบเบธ, เบเบฃเบฒเบงเปเบเบตเบเบฐ โเบเปเบฒโ เบเบฐเบเบงเบเบเบฒเบเบเบทเปเบเบซเบผเบฑเบเปเบกเบทเปเบญเบกเบฑเบเบเบดเบเบฒเบฅเบฐเบเบฒเบงเปเบฒเบกเบฑเบเบเปเปเปเบเปเปเบฎเบฑเบเบซเบเบฑเบ, เปเบฅเบฐเปเบเบตเบเบกเบฑเบเบเบทเบเปเปเปเบเปเบฒเบเบณเปเบเบฑเบ. เบเปเบฒเบเปเปเบเบฑเปเบเบเบฑเปเบ, เบซเบเปเบฒเปเบงเบฑเบเบเบฐเบเบทเบเบเบปเบเปเบฅเบตเบเปเบกเบทเปเบญเบเบปเบงเบเปเบญเบเปเบงเบฑเบเบเบทเบเบเบดเบ. เบเปเปเบฎเบญเบเบฎเบฑเบเปเบ Firefox.
- content_scripts โ array เบเบญเบเบงเบฑเบเบเบธเบเบตเปเบญเบฐเบเบธเบเบฒเบเปเบซเปเบเปเบฒเบเบชเบฒเบกเบฒเบเปเบซเบฅเบ scripts เบเบตเปเปเบเบเบเปเบฒเบเบเบฑเบเปเบเบซเบฒเบซเบเปเบฒเปเบงเบฑเบเบเปเบฒเบเป. เปเบเปเบฅเบฐเบงเบฑเบเบเบธเบกเบตเบเปเบญเบเบเปเปเบกเบนเบเบชเบณเบเบฑเบเบเปเปเปเบเบเบตเป:
- เบเบปเบเบเบฑเบ -
url เบฎเบนเบเปเบเบ , เปเบเบดเปเบเบเปเบฒเบเบปเบเบงเปเบฒเบชเบฐเบเบดเบเปเบเบทเปเบญเบซเบฒเปเบเบเบชเบฐเปเบเบฒเบฐเบเบฐเบเบทเบเบฅเบงเบกเปเบเบปเปเบฒเบซเบผเบทเบเปเป. - js โ เบเบฑเบโเบเบตโเบฅเบฒเบโเบเบทเปโเบเบญเบ scripts เบเบตเปโเบเบฐโเปเบเปโเบฎเบฑเบโเบเบฒเบโเปเบซเบผเบโเปเบเบปเปเบฒโเปเบโเปเบโเบเบฒเบโเปเบเปเบโเบเบฑเบโเบเบตเปโ;
- exclude_match - เบเปเปเบฅเบงเบกเบเบฒเบเบเบฒเบเบชเบฐเบซเบเบฒเบก
match
URL เบเบตเปเบเบปเบเบเบฑเบเบเปเบญเบเบเปเปเบกเบนเบเบเบตเป.
- เบเบปเบเบเบฑเบ -
- page_action - เบเบปเบงเบเบดเบเปเบฅเปเบงเปเบกเปเบเบงเบฑเบเบเบธเบเบตเปเบฎเบฑเบเบเบดเบเบเบญเบเบชเปเบฒเบฅเบฑเบเปเบญเบเบญเบเบเบตเปเบชเบฐเปเบเบเบขเบนเปเบเปเบฒเบเปเบเบเบเบตเปเบขเบนเปเปเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบเปเบฅเบฐเบเบฐเบเบดเบชเปเบฒเบเบฑเบเบเบฑเบเบกเบฑเบ. เบกเบฑเบเบเบฑเบเบญเบฐเบเบธเบเบฒเบเปเบซเปเบเปเบฒเบเบชเบฒเบกเบฒเบเบชเบฐเปเบเบเบเปเบญเบเบขเปเบฝเบกเบเปเบญเบเบญเบฑเบ, เปเบเบดเปเบเบเบทเบเบเปเบฒเบเบปเบเปเบเบเปเบเป HTML, CSS เปเบฅเบฐ JS เบเบญเบเบเปเบฒเบเปเบญเบ.
- default_popup โ เปเบชเบฑเปเบเบเบฒเบเปเบเบซเบฒเปเบเบฅเป HTML เบเบตเปเบกเบตเบเบฒเบเปเบเปเบเบญเบเบเปเบญเบเบญเบฑเบ, เบญเบฒเบเบเบฐเบกเบต CSS เปเบฅเบฐ JS.
- เบเบฒเบเบญเบฐเบเบธเบเบฒเบ โ array เบชเปเบฒเบฅเบฑเบเบเบฒเบเบเบธเปเบกเบเบญเบเบชเบดเบเบเบดเบเบฒเบเบเบฐเบซเบเบฒเบ. เบกเบต 3 เบเบฐเปเบเบเบเบญเบเบชเบดเบเบเบด, เปเบเบดเปเบเปเบเปเบญเบฐเบเบดเบเบฒเบเบขเปเบฒเบเบฅเบฐเบญเบฝเบ
เบเบตเปเบเบตเป - web_accessible_resources โ เบเบฑเบเบเบฐเบเบฒเบเบญเบเบชเปเบงเบเบเบฐเบซเบเบฒเบเบเบตเปเบซเบเปเบฒเปเบงเบฑเบเบชเบฒเบกเบฒเบเบฎเปเบญเบเบเปเปเบเป, เบเบปเบงเบขเปเบฒเบ, เบฎเบนเบเบเบฒเบ, JS, CSS, HTML.
- externally_connectable โ เปเบโเบเบตเปโเบเบตเปโเบเปเบฒเบโเบชเบฒโเบกเบฒเบโเบฅเบฐโเบเบธโเบขเปเบฒเบโเบเบฑเบโเปเบเบ IDs เบเบญเบโเบเบฒเบโเบเบฐโเบซเบเบฒเบโเบญเบทเปเบเปโเปเบฅเบฐโเปเบโเปเบกเบโเบเบญเบโเบซเบเปเบฒโเปเบงเบฑเบโเบเบตเปโเบเปเบฒเบโเบชเบฒโเบกเบฒเบโเปเบเบทเปเบญเบกโเบเปเปโ. เปเบเปเบกเบเบชเบฒเบกเบฒเบเปเบเบฑเบเบฅเบฐเบเบฑเบเบเบตเบชเบญเบเบซเบผเบทเบชเบนเบเบเบงเปเบฒ. เปเบเปเบเปเปเปเบเปเปเบ Firefox.
เบเปเบฅเบดเบเบปเบเบเบฒเบเบเบฐเบเบดเบเบฑเบ
เบชเปเบงเบเบเบฐเบซเบเบฒเบเบกเบตเบชเบฒเบกเบชเบฐเบเบฒเบเบเบฒเบเบเบฐเบเบดเบเบฑเบเบฅเบฐเบซเบฑเบ, เบเบฑเปเบเปเบกเปเบ, เปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเบเบเบฐเบเบญเบเบเปเบงเบเบชเบฒเบกเบชเปเบงเบเบเบตเปเบกเบตเบฅเบฐเบเบฑเบเบเบฒเบเปเบเบปเปเบฒเปเบเบดเบ API เบเบญเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบ.
เบเปเบฅเบดเบเบปเบเบเบฒเบเบเบฐเบซเบเบฒเบ
เบชเปเบงเบเปเบซเบเปเบเบญเบ API เปเบกเปเบเบกเบตเบขเบนเปเบเบตเปเบเบตเป. เปเบเบชเบฐเบเบฒเบเบเบฒเบเบเบตเปเบเบงเบเปเบเบปเบฒ "เบเปเบฒเบฅเบปเบเบเบตเบงเบดเบ":
- เปเปเบฒเบเบทเปเบเบซเบผเบฑเบ - เบชเปเบงเบ "backend" เบเบญเบเบชเปเบงเบเบเบฐเบซเบเบฒเบ. เปเบเบฅเปเบเบทเบเบฅเบฐเบเบธเปเบงเปเปเบ manifest เปเบเบเปเบเปเบเบธเปเบก "เบเบทเปเบเบซเบผเบฑเบ".
- เปเปเบฒเบเบฑเบญเบเบญเบฑเบ โ เปเปเบฒเบเบฑเบญเบเบญเบฑเบเบเบตเปเบเบฒเบเบปเบเบเบถเปเบเปเบกเบทเปเบญเบเปเบฒเบเบเบฅเบดเบเปเบชเปเปเบญเบเบญเบเบชเปเบงเบเบเบฐเบซเบเบฒเบ. เปเบ manifesto เปเบเป
browser_action
->default_popup
. - เปเปเบฒเบเบณเบเบปเบเปเบญเบ - เบซเบเปเบฒเบชเปเบงเบเบเบฐเบซเบเบฒเบ, "เบเปเบฒเบฅเบปเบเบเบตเบงเบดเบ" เปเบเปเบเบเปเบเบเบเปเบฒเบเบซเบฒเบเบเบญเบเบกเบธเบกเปเบเบดเปเบ
chrome-extension://<id_ัะฐััะธัะตะฝะธั>/customPage.html
.
เบเปเบฅเบดเบเบปเบเบเบตเปเบกเบตเบขเบนเปเปเบเบฑเบเปเบญเบเบฐเบฅเบฒเบเบเบฒเบเปเปเบฒเบเปเบฒเบ เปเบฅเบฐเปเบเบเบเบญเบเบเบฃเบฒเบงเปเบเบต. เปเปเบฒเบเบทเปเบเบซเบผเบฑเบ เบกเบตเบขเบนเปเปเบเบชเปเบฒเปเบเบปเบฒเบเบฝเบงเปเบฅเบฐเปเบฎเบฑเบเบงเบฝเบเบชเบฐเปเบซเบกเบต (เบเปเปเบเบปเบเปเบงเบฑเปเบเปเบกเปเบเบซเบเปเบฒเปเบซเบเบเบฒเบ, เปเบกเบทเปเบญเบชเบฐเบเบดเบเบเบทเปเบเบซเบฅเบฑเบเบเบทเบเปเบเบตเบเบเบปเบงเปเบเบเปเบซเบเบเบฒเบเปเบฅเบฐ "เบเบฒเบ" เบซเบผเบฑเบเบเบฒเบเบเบฒเบเบเบฐเบเบดเบเบฑเบเบเบญเบเบกเบฑเบ). เปเปเบฒเบเบฑเบญเบเบญเบฑเบ เบกเบตเบขเบนเปเปเบเปเบงเบฅเบฒเบเบตเปเบเปเบญเบเบขเปเบฝเบกเบเปเบญเบเบญเบฑเบเปเบเบตเบ, เปเบฅเบฐ เปเปเบฒเบเบณเบเบปเบเปเบญเบ โ เปเบเบเบฐเบเบฐเบเบตเปเปเบเบเบเบตเปเบกเบตเบกเบฑเบเปเบเบตเบเบขเบนเป. เบเปเปเบกเบตเบเบฒเบเปเบเบปเปเบฒเปเบเบดเบเปเบเบเบญเบทเปเบ เปเบฅเบฐเปเบเบทเปเบญเบซเบฒเบเบญเบเบเบงเบเบกเบฑเบเบเบฒเบเบเปเบฅเบดเบเบปเบเบเบตเป.
เบชเบฐเบเบฃเบดเบเปเบเบทเปเบญเบซเบฒ
เปเบเบฅเป script เปเบเบทเปเบญเบซเบฒเบเบทเบเปเบเบตเบเบเบปเบงเบเปเบญเบกเบเบฑเบเปเบเปเบฅเบฐเปเบเบเบเบญเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบ. เบกเบฑเบเบกเบตเบเบฒเบเปเบเบปเปเบฒเปเบเบดเบเบชเปเบงเบเบซเบเบถเปเบเบเบญเบ API เบเบญเบเบเบฒเบเบเบฐเบซเบเบฒเบเปเบฅเบฐเบเบปเปเบเปเบกเป DOM เบเบญเบเบซเบเปเบฒเปเบงเบฑเบ. เบกเบฑเบเปเบเบฑเบ scripts เปเบเบทเปเบญเปเบเบเบตเปเบฎเบฑเบเบเบดเบเบเบญเบเบชเปเบฒเบฅเบฑเบเบเบฒเบเบเบปเบงเบเบฑเบเบเบฑเบเบซเบเปเบฒ. เบชเปเบงเบเบเบฐเบซเบเบฒเบเบเบตเป manipulate เบเบปเปเบเปเบกเป DOM เปเบฎเบฑเบเบชเบดเปเบเบเบตเปเปเบเบชเบฐเบเบดเบเปเบเบทเปเบญเบซเบฒ - เบเบปเบงเบขเปเบฒเบ, เบเบปเบงเบชเบฐเบเบฑเบเปเบเบชเบฐเบเบฒเบซเบผเบทเบเบฑเบเปเบ. เบเบญเบเบเบฒเบเบเบตเป, script เปเบเบทเปเบญเบซเบฒเบชเบฒเบกเบฒเบเบชเบทเปเบชเบฒเบเบเบฑเบเบซเบเปเบฒเปเบเบเบเปเบฒเบเบกเบฒเบเบเบฐเบเบฒเบ postMessage
.
เบเปเบฅเบดเบเบปเบเบเบญเบเบซเบเปเบฒเปเบงเบฑเบ
เบเบตเปเปเบกเปเบเบซเบเปเบฒเปเบงเบฑเบเบเบปเบงเบเบดเบเบเบญเบเบกเบฑเบเปเบญเบ. เบกเบฑเบเบเปเปเบกเบตเบซเบเบฑเบเบเปเบฝเบงเบเปเบญเบเบเบฑเบเบเบฒเบเบเบฐเบซเบเบฒเบเปเบฅเบฐเบเปเปเบกเบตเบเบฒเบเปเบเบปเปเบฒเปเบเบดเบเบเบตเปเบเบฑเปเบ, เบเบปเบเปเบงเบฑเปเบเปเบเบเปเบฅเบฐเบเบตเบเบตเปเปเบเปเบกเบเบเบญเบเบซเบเปเบฒเบเบตเปเบเปเปเปเบเปเบฅเบฐเบเบธเบขเปเบฒเบเบเบฑเบเปเบเบเปเบ manifest (เปเบเบตเปเบกเปเบเบตเบกเบขเบนเปเบเปเบฒเบเบฅเบธเปเบกเบเบตเป).
เบเบฒเบเปเบฅเบเบเปเบฝเบเบเปเปเบเบงเบฒเบก
เบเบฒเบเบชเปเบงเบเบเปเบฒเบเปเบเบญเบเปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเบเบเปเบญเบเปเบฅเบเบเปเบฝเบเบเปเปเบเบงเบฒเบกเปเบเบดเปเบเบเบฑเบเปเบฅเบฐเบเบฑเบ. เบกเบต API เบชเปเบฒเบฅเบฑเบเบเบฒเบเบเบตเป runtime.sendMessage
เปเบเบทเปเบญเบชเบปเปเบเบเปเปเบเบงเบฒเบก background
ะธ tabs.sendMessage
เปเบเบทเปเบญเบชเบปเปเบเบเปเปเบเบงเบฒเบกเปเบเบซเบฒเปเปเบฒเปเบเปเบถเปเบ (เบชเบฐเบเบฃเบดเบเปเบเบทเปเบญเบซเบฒ, เบเบฑเบญเบเบญเบฑเบ เบซเบผเบทเปเปเบฒเปเบงเบฑเบเบเปเบฒเบกเบต externally_connectable
). เบเปเบฒเบเบฅเบธเปเบกเบเบตเปเปเบกเปเบเบเบปเบงเบขเปเบฒเบเปเบกเบทเปเบญเปเบเบปเปเบฒเปเบเบดเบ Chrome API.
// ะกะพะพะฑัะตะฝะธะตะผ ะผะพะถะตั ะฑััั ะปัะฑะพะน JSON ัะตัะธะฐะปะธะทัะตะผัะน ะพะฑัะตะบั
const msg = {a: 'foo', b: 'bar'};
// extensionId ะผะพะถะฝะพ ะฝะต ัะบะฐะทัะฒะฐัั, ะตัะปะธ ะผั ั
ะพัะธะผ ะฟะพัะปะฐัั ัะพะพะฑัะตะฝะธะต 'ัะฒะพะตะผั' ัะฐััะธัะตะฝะธั (ะธะท ui ะธะปะธ ะบะพะฝัะตะฝั ัะบัะธะฟัะฐ)
chrome.runtime.sendMessage(extensionId, msg);
// ะขะฐะบ ะฒัะณะปัะดะธั ะพะฑัะฐะฑะพััะธะบ
chrome.runtime.onMessage.addListener((msg) => console.log(msg))
// ะะพะถะฝะพ ัะปะฐัั ัะพะพะฑัะตะฝะธั ะฒะบะปะฐะดะบะฐะผ ะทะฝะฐั ะธั
id
chrome.tabs.sendMessage(tabId, msg)
// ะะพะปััะธัั ะบ ะฒะบะปะฐะดะบะฐะผ ะธ ะธั
id ะผะพะถะฝะพ, ะฝะฐะฟัะธะผะตั, ะฒะพั ัะฐะบ
chrome.tabs.query(
{currentWindow: true, active : true},
function(tabArray){
tabArray.forEach(tab => console.log(tab.id))
}
)
เบชเปเบฒเบฅเบฑเบเบเบฒเบเบชเบทเปเบชเบฒเบเบขเปเบฒเบเปเบเบฑเบกเบเบตเป, เบเปเบฒเบเบชเบฒเบกเบฒเบเบชเปเบฒเบเบเบฒเบเปเบเบทเปเบญเบกเบเปเปเปเบเบเบเปเบฒเบ runtime.connect
. เปเบโเบเบฒเบโเบเบญเบโเบชเบฐโเบซเบเบญเบโเบเบงเบโเปเบฎเบปเบฒโเบเบฐโเปเบเปโเบฎเบฑเบโ runtime.Port
, เบเบตเป, เปเบเบเบฐเบเบฐเบเบตเปเบกเบฑเบเปเบเบตเบ, เบเปเบฒเบเบชเบฒเบกเบฒเบเบชเบปเปเบเบเปเปเบเบงเบฒเบกเปเบเป. เปเบเบเปเบฒเบเบฅเบนเบเบเปเบฒ, เบชเปเบฒเบฅเบฑเบเบเบปเบงเบขเปเบฒเบ, contentscript
, เบกเบฑเบเปเบเบดเปเบเบเบทเบงเปเบฒเบเบตเป:
// ะะฟััั ะถะต extensionId ะผะพะถะฝะพ ะฝะต ัะบะฐะทัะฒะฐัั ะฟัะธ ะบะพะผะผัะฝะธะบะฐัะธะธ ะฒะฝัััะธ ะพะดะฝะพะณะพ ัะฐััะธัะตะฝะธั. ะะพะดะบะปััะตะฝะธะต ะผะพะถะฝะพ ะธะผะตะฝะพะฒะฐัั
const port = chrome.runtime.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
if (msg.question === "Who's there?")
port.postMessage({answer: "Madame"});
else if (msg.question === "Madame who?")
port.postMessage({answer: "Madame... Bovary"});
เปเบเบตเบเปเบงเบต เบซเบผเบทเบเบทเปเบเบซเบผเบฑเบ:
// ะะฑัะฐะฑะพััะธะบ ะดะปั ะฟะพะดะบะปััะตะฝะธั 'ัะฒะพะธั
' ะฒะบะปะฐะดะพะบ. ะะพะฝัะตะฝั ัะบัะธะฟัะพะฒ, popup ะธะปะธ ัััะฐะฝะธั ัะฐััะธัะตะฝะธั
chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name === "knockknock");
port.onMessage.addListener(function(msg) {
if (msg.joke === "Knock knock")
port.postMessage({question: "Who's there?"});
else if (msg.answer === "Madame")
port.postMessage({question: "Madame who?"});
else if (msg.answer === "Madame... Bovary")
port.postMessage({question: "I don't get it."});
});
});
// ะะฑัะฐะฑะพััะธะบ ะดะปั ะฟะพะดะบะปััะตะฝะธั ะฒะฝะตัะฝะธั
ะฒะบะปะฐะดะพะบ. ะััะณะธั
ัะฐััะธัะตะฝะธะน ะธะปะธ ะฒะตะฑ ัััะฐะฝะธั, ะบะพัะพััะผ ัะฐะทัะตัะตะฝ ะดะพัััะฟ ะฒ ะผะฐะฝะธัะตััะต
chrome.runtime.onConnectExternal.addListener(function(port) {
...
});
เบเบญเบเบเบฒเบเบเบตเปเบเบฑเบเบกเบตเปเบซเบเบเบฒเบ onDisconnect
เปเบฅเบฐเบงเบดเบเบตเบเบฒเบ disconnect
.
เปเบเบเบงเบฒเบเบเปเบฒเบฎเปเบญเบเบชเบฐเบซเบกเบฑเบ
เปเบซเปเบเบงเบเปเบฎเบปเบฒเบชเปเบฒเบเบชเปเบงเบเบเบฐเบซเบเบฒเบเบเบญเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบเบเบตเปเปเบเบฑเบเบฎเบฑเบเบชเบฒเบฅเบฐเบซเบฑเบเบชเปเบงเบเบเบปเบง, เบชเบฐเบซเบเบญเบเบเบฒเบเปเบเบปเปเบฒเปเบเบดเบเบเปเปเบกเบนเบเบชเบฒเบเบฒเบฅเบฐเบเบฐ (เบเบตเปเบขเบนเป, เบฅเบฐเบซเบฑเบเบชเบฒเบเบฒเบฅเบฐเบเบฐเบเบดเบเบเปเปเบชเบทเปเบชเบฒเบเบเบฑเบเบซเบเปเบฒเปเบฅเบฐเบญเบฐเบเบธเบเบฒเบเปเบซเปเปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเบเบเบฒเบเบชเปเบงเบเบเบตเบชเบฒเบกเบฎเปเบญเบเบเปเบฅเบฒเบเปเบเบฑเบเบชเปเบฒเบฅเบฑเบเบเบฒเบเปเบฎเบฑเบเบเบธเบฅเบฐเบเปเบฒ.
เบเบฒเบเบเบฑเบเบเบฐเบเบฒเปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเบ
เบเปเบฒเบฎเปเบญเบเบชเบฐเบซเบกเบฑเบเบเบญเบเบเบงเบเปเบฎเบปเบฒเบเปเบญเบเบเบปเบงเบเบฑเบเบเบฑเบเบเบนเปเปเบเปเปเบฅเบฐเปเบซเปเบซเบเปเบฒเปเบงเบฑเบเบเบตเปเบกเบต API เปเบเบทเปเบญเปเบเบซเบฒเบงเบดเบเบตเบเบฒเบ (เบเบปเบงเบขเปเบฒเบเปเบเบฑเปเบเบเบฒเบเปเบเบฑเบเบเบฒเบเปเบฎเบฑเบเบเบธเบฅเบฐเบเปเบฒ). เปเบฎเบฑเบเบเปเบงเบเบเบฝเบเบญเบฑเบเบเบฝเบง contentscript
เบเบฐเบเปเปเปเบฎเบฑเบเบงเบฝเบ, เปเบเบฒเบฐเบงเปเบฒเบกเบฑเบเบเบฝเบเปเบเปเปเบเบปเปเบฒเปเบเบดเบ DOM, เปเบเปเบเปเปเปเบกเปเบ JS เบเบญเบเบซเบเปเบฒ. เปเบเบทเปเบญเบกเบเปเปเบเปเบฒเบ runtime.connect
เบเบงเบเปเบฎเบปเบฒเบเปเปเบชเบฒเบกเบฒเบ, เปเบเบฒเบฐเบงเปเบฒ API เปเบกเปเบเบเปเบฒเปเบเบฑเบเปเบเบเบธเบเปเบเปเบกเบ, เปเบฅเบฐเบเบฝเบเปเบเปเบซเบเบถเปเบเบชเบฐเปเบเบฒเบฐเบเบตเปเบชเบฒเบกเบฒเบเบเบทเบเบฅเบฐเบเบธเปเบงเปเปเบ manifest. เบเบฑเปเบเบเบฑเปเบ, เปเบเบเบงเบฒเบเบเบฐเบกเบตเบฅเบฑเบเบชเบฐเบเบฐเบเบฑเปเบเบเบตเป:
เบเบฐโเบกเบตโเบเบปเบงโเบญเบฑเบโเบชเบญเบโเบญเบทเปเบ - inpage
, เบเบตเปเบเบงเบเปเบฎเบปเบฒเบเบฐเบชเบตเบเปเบเบปเปเบฒเปเบเปเบเบซเบเปเบฒ. เบกเบฑเบเบเบฐเบเปเบฒเปเบเบตเบเบเบฒเบเปเบเบชเบฐเบเบฒเบเบเบฒเบเบเบญเบเบกเบฑเบเปเบฅเบฐเบชเบฐเบซเบเบญเบ API เบชเปเบฒเบฅเบฑเบเบเบฒเบเปเบฎเบฑเบเบงเบฝเบเบเบฑเบเบชเปเบงเบเบเบฐเบซเบเบฒเบ.
ะะฐัะฐะปะพ
เบฅเบฐเบซเบฑเบเบชเปเบงเบเบเบฐเบซเบเบฒเบเบเบญเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบเบเบฑเบเปเบปเบเปเบกเปเบเบกเบตเบขเบนเป
เปเบซเปเปเบฅเบตเปเบกเบเบปเปเบเบเปเบงเบ manifesto:
{
// ะะผั ะธ ะพะฟะธัะฐะฝะธะต, ะฒะตััะธั. ะัะต ััะพ ะฑัะดะตั ะฒะธะดะฝะพ ะฒ ะฑัะฐัะทะตัะต ะฒ chrome://extensions/?id=<id ัะฐััะธัะตะฝะธั>
"name": "Signer",
"description": "Extension demo",
"version": "0.0.1",
"manifest_version": 2,
// ะกะบัะธะฟัั, ะบะพัะพััะต ะฑัะดัั ะธัะฟะพะปะฝัััั ะฒ background, ะธั
ะผะพะถะตั ะฑััั ะฝะตัะบะพะปัะบะพ
"background": {
"scripts": ["background.js"]
},
// ะะฐะบะพะน html ะธัะฟะพะปัะทะพะฒะฐัั ะดะปั popup
"browser_action": {
"default_title": "My Extension",
"default_popup": "popup.html"
},
// ะะพะฝัะตะฝั ัะบัะธะฟัั.
// ะฃ ะฝะฐั ะพะดะธะฝ ะพะฑัะตะบั: ะดะปั ะฒัะตั
url ะฝะฐัะธะฝะฐััะธั
ัั ั http ะธะปะธ https ะผั ะทะฐะฟััะบะฐะตะผ
// contenscript context ัะพ ัะบัะธะฟัะพะผ contentscript.js. ะะฐะฟััะบะฐัั ััะฐะทั ะฟะพ ะฟะพะปััะตะฝะธะธ ะดะพะบัะผะตะฝัะฐ ะดะปั ะฒัะตั
ััะตะนะผะพะฒ
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"contentscript.js"
],
"run_at": "document_start",
"all_frames": true
}
],
// ะ ะฐะทัะตัะตะฝ ะดะพัััะฟ ะบ localStorage ะธ idle api
"permissions": [
"storage",
// "unlimitedStorage",
//"clipboardWrite",
"idle"
//"activeTab",
//"webRequest",
//"notifications",
//"tabs"
],
// ะะดะตัั ัะบะฐะทัะฒะฐัััั ัะตััััั, ะบ ะบะพัะพััะผ ะฑัะดะตั ะธะผะตัั ะดะพัััะฟ ะฒะตะฑ ัััะฐะฝะธัะฐ. ะขะพะตััั ะธั
ะผะพะถะฝะพ ะฑัะดะตั ะทะฐะฟัะฐัะธะฒะฐัั fetche'ะผ ะธะปะธ ะฟัะพััะพ xhr
"web_accessible_resources": ["inpage.js"]
}
เบชเปเบฒเบ background.js เบซเบงเปเบฒเบเปเบเบปเปเบฒ, popup.js, inpage.js เปเบฅเบฐ contentscript.js. เบเบงเบเปเบฎเบปเบฒเปเบเบตเปเบก popup.html - เปเบฅเบฐเปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเบเบเบญเบเบเบงเบเปเบฎเบปเบฒเบชเบฒเบกเบฒเบเบเบทเบเปเบซเบฅเบเปเบเบปเปเบฒเปเบเปเบ Google Chrome เปเบฅเปเบงเปเบฅเบฐเปเบซเปเปเบเปเปเบเบงเปเบฒเบกเบฑเบเปเบฎเบฑเบเบงเบฝเบ.
เปเบเบทเปเบญเบเบงเบเบชเบญเบเบเบตเป, เบเปเบฒเบเบชเบฒเบกเบฒเบเปเบญเบปเบฒเบฅเบฐเบซเบฑเบ
เปเบเบเบฑเบเบเบธเบเบฑเบเบชเปเบงเบเบเบฐเบซเบเบฒเบเบเบญเบเบเบงเบเปเบฎเบปเบฒเบเบทเบเบเบดเบเบเบฑเปเบเปเบฅเบฐเปเบฎเบฑเบเบงเบฝเบ. เบเปเบฒเบโเบชเบฒโเบกเบฒเบโเปเบเปโเปเบเบทเปเบญเบโเบกเบทโเบเบฒเบโเบเบฑเบโเบเบฐโเบเบฒโเบชเปเบฒโเบฅเบฑเบโเบชเบฐโเบเบฒเบโเบเบฒเบโเบเบตเปโเปเบเบโเบเปเบฒเบโเบเบฑเบโเบเบฑเปเบโเบเปเปโเปเบโเบเบตเปโ:
เบเปเบญเบเบญเบฑเบ ->
เบเบฒเบเปเบเบปเปเบฒเปเบเบดเบ console script เปเบเบทเปเบญเบซเบฒเปเบกเปเบเบเปเบฒเปเบเบตเบเปเบเบเบเปเบฒเบ console เบเบญเบเบซเบเปเบฒเบเบญเบเบกเบฑเบเปเบญเบเบเบตเปเบกเบฑเบเบเบทเบเปเบเบตเบเบเบปเบง.
เบเบฒเบเปเบฅเบเบเปเบฝเบเบเปเปเบเบงเบฒเบก
เบเบฑเปเบเบเบฑเปเบ, เบเบงเบเปเบฎเบปเบฒเบเปเบฒเปเบเบฑเบเบเปเบญเบเบชเปเบฒเบเบชเบญเบเบเปเบญเบเบเบฒเบเบเบฒเบเบชเบทเปเบชเบฒเบ: inpage <-> background เปเบฅเบฐ popup <-> background. เบเปเบฒเบเบชเบฒเบกเบฒเบ, เปเบเปเบเบญเบ, เบเบฝเบเปเบเปเบชเบปเปเบเบเปเปเบเบงเบฒเบกเปเบเบซเบฒเบเบญเบเปเบฅเบฐเบเบฐเบเบดเบเปเบเปเบเบเบญเบเบเบญเบเบเปเบฒเบเปเบญเบ, เปเบเปเบเปเบญเบเบกเบฑเบเบงเบดเบเบตเบเบฒเบเบเบตเปเบเปเบญเบเปเบซเบฑเบเปเบเปเบเบเบเบฒเบ metamask open source.
เบเบตเปเปเบกเปเบเบชเปเบงเบเบเบฐเบซเบเบฒเบเบเบญเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบเบชเปเบฒเบฅเบฑเบเบเบฒเบเปเบฎเบฑเบเบงเบฝเบเบเบฑเบเปเบเบทเบญเบเปเบฒเบ Ethereum. เปเบเบกเบฑเบ, เบเบฒเบเบชเปเบงเบเบเปเบฒเบเปเบเบญเบเปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเบเบเบดเบเบเปเปเบชเบทเปเบชเบฒเบเบเปเบฒเบ RPC เปเบเบเปเบเปเบซเปเบญเบเบชเบฐเบซเบกเบธเบ dnode. เบกเบฑเบเบญเบฐเบเบธเบเบฒเบเปเบซเปเบเปเบฒเบเบเบฑเบเบเบฒเบเปเบฅเบเบเปเบฝเบเบเปเบญเบเบเปเบฒเบเปเบงเปเบฅเบฐเบชเบฐเบเบงเบเบเปเบฒเบเปเบฒเบเบชเบฐเบซเบเบญเบเบกเบฑเบเบเปเบงเบเบเปเปเบฒ nodejs เปเบเบฑเบเบเบฒเบเบเบปเบเบชเบปเปเบ (เบซเบกเบฒเบเบเบงเบฒเบกเบงเปเบฒเบงเบฑเบเบเบธเบเบตเปเบเบฐเบเบดเบเบฑเบเบเบฒเบเปเบเปเบเบญเบเบเบฝเบงเบเบฑเบ):
import Dnode from "dnode/browser";
// ะ ััะพะผ ะฟัะธะผะตัะต ััะปะพะฒะธะผัั ััะพ ะบะปะธะตะฝั ัะดะฐะปะตะฝะฝะพ ะฒัะทัะฒะฐะตั ััะฝะบัะธะธ ะฝะฐ ัะตัะฒะตัะต, ั
ะพัั ะฝะธัะตะณะพ ะฝะฐะผ ะฝะต ะผะตัะฐะตั ัะดะตะปะฐัั ััะพ ะดะฒัะฝะฐะฟัะฐะฒะปะตะฝะฝัะผ
// Cะตัะฒะตั
// API, ะบะพัะพัะพะต ะผั ั
ะพัะธะผ ะฟัะตะดะพััะฐะฒะธัั
const dnode = Dnode({
hello: (cb) => cb(null, "world")
})
// ะขัะฐะฝัะฟะพัั, ะฟะพะฒะตัั
ะบะพัะพัะพะณะพ ะฑัะดะตั ัะฐะฑะพัะฐัั dnode. ะัะฑะพะน nodejs ัััะธะผ. ะ ะฑัะฐัะทะตัะต ะตััั ะฑะธะฑะธะปะธะพัะตะบะฐ 'readable-stream'
connectionStream.pipe(dnode).pipe(connectionStream)
// ะะปะธะตะฝั
const dnodeClient = Dnode() // ะัะทะพะฒ ะฑะตะท ะฐะณััะผะตะฝัะฐ ะทะฝะฐัะธั ััะพ ะผั ะฝะต ะฟัะตะดะพััะฐะฒะปัะตะผ API ะฝะฐ ะดััะณะพะน ััะพัะพะฝะต
// ะัะฒะตะดะตั ะฒ ะบะพะฝัะพะปั world
dnodeClient.once('remote', remote => {
remote.hello(((err, value) => console.log(value)))
})
เปเบเบเบฑเบเบเบธเบเบฑเบเบเบงเบเปเบฎเบปเบฒเบเบฐเบชเปเบฒเบเบซเปเบญเบเบฎเบฝเบเบเปเบฒเบฎเปเบญเบเบชเบฐเบซเบกเบฑเบ. เบกเบฑเบเบเบฐเบชเปเบฒเบเบงเบฑเบเบเบธ API เบชเปเบฒเบฅเบฑเบเบเปเบญเบเบญเบฑเบเปเบฅเบฐเบซเบเปเบฒเปเบงเบฑเบ, เปเบฅเบฐเบชเปเบฒเบ dnode เบชเปเบฒเบฅเบฑเบเบเบงเบเปเบเบปเบฒ:
import Dnode from 'dnode/browser';
export class SignerApp {
// ะะพะทะฒัะฐัะฐะตั ะพะฑัะตะบั API ะดะปั ui
popupApi(){
return {
hello: cb => cb(null, 'world')
}
}
// ะะพะทะฒัะฐัะฐะตั ะพะฑัะตั API ะดะปั ัััะฐะฝะธัั
pageApi(){
return {
hello: cb => cb(null, 'world')
}
}
// ะะพะดะบะปััะฐะตั popup ui
connectPopup(connectionStream){
const api = this.popupApi();
const dnode = Dnode(api);
connectionStream.pipe(dnode).pipe(connectionStream);
dnode.on('remote', (remote) => {
console.log(remote)
})
}
// ะะพะดะบะปััะฐะตั ัััะฐะฝะธัั
connectPage(connectionStream, origin){
const api = this.popupApi();
const dnode = Dnode(api);
connectionStream.pipe(dnode).pipe(connectionStream);
dnode.on('remote', (remote) => {
console.log(origin);
console.log(remote)
})
}
}
เบเบตเปเบเบตเปเปเบฅเบฐเบเปเบฒเบเบฅเบธเปเบกเบเบตเป, เปเบเบเบเบตเปเบเบฐเปเบเบฑเบเบงเบฑเบเบเบธ Chrome เบเบปเปเบงเปเบฅเบ, เบเบงเบเปเบฎเบปเบฒเปเบเป extensionApi, เปเบเบดเปเบเปเบเบปเปเบฒเปเบเบดเบ Chrome เปเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบเบเบญเบ Google เปเบฅเบฐเบเบปเบงเบเปเบญเบเปเบงเบฑเบเบเบญเบเบเบปเบเบญเบทเปเบ. เบเบตเปเปเบกเปเบเปเบฎเบฑเบเบชเปเบฒเบฅเบฑเบเบเบงเบฒเบกเปเบเบปเปเบฒเบเบฑเบเปเบเปเบเบญเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบเบเปเบฒเบก, เปเบเปเบชเปเบฒเบฅเบฑเบเบเบธเบเบเบฐเบชเบปเบเบเบญเบเบเบปเบเบเบงเบฒเบกเบเบตเป, เบเบฝเบเปเบเปเบชเบฒเบกเบฒเบเบเปเบฒเปเบเป 'chrome.runtime.connect'.
เบกเบฒเบชเปเบฒเบเบเบปเบงเบขเปเบฒเบเปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเบเปเบเบชเบฐเบเบฃเบดเบเบเบทเปเบเบซเบผเบฑเบ:
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {SignerApp} from "./SignerApp";
const app = new SignerApp();
// onConnect ััะฐะฑะฐััะฒะฐะตั ะฟัะธ ะฟะพะดะบะปััะตะฝะธะธ 'ะฟัะพัะตััะพะฒ' (contentscript, popup, ะธะปะธ ัััะฐะฝะธัะฐ ัะฐััะธัะตะฝะธั)
extensionApi.runtime.onConnect.addListener(connectRemote);
function connectRemote(remotePort) {
const processName = remotePort.name;
const portStream = new PortStream(remotePort);
// ะัะธ ัััะฐะฝะพะฒะบะต ัะพะตะดะธะฝะตะฝะธั ะผะพะถะฝะพ ัะบะฐะทัะฒะฐัั ะธะผั, ะฟะพ ััะพะผั ะธะผะตะฝะธ ะผั ะธ ะพะฟะฟัะตะดะตะปัะตะผ ะบัะพ ะบ ะฝะฐะผ ะฟะพะดะปััะธะปัั, ะบะพะฝัะตะฝััะบัะธะฟั ะธะปะธ ui
if (processName === 'contentscript'){
const origin = remotePort.sender.url
app.connectPage(portStream, origin)
}else{
app.connectPopup(portStream)
}
}
เปเบเบทเปเบญเบเบเบฒเบ dnode เปเบฎเบฑเบเบงเบฝเบเบเบฑเบเบเบฐเปเบช, เปเบฅเบฐเบเบงเบเปเบฎเบปเบฒเปเบเปเบฎเบฑเบเบเบญเบ, เบซเปเบญเบเบฎเบฝเบเบญเบฐเปเบเบเปเบเบตเปเบกเปเบเบเปเบฒเปเบเบฑเบ. เบกเบฑเบเบเบทเบเบชเปเบฒเบเบเบถเปเบเปเบเบเปเบเปเบซเปเบญเบเบชเบฐเบซเบกเบธเบเบเบตเปเบชเบฒเบกเบฒเบเบญเปเบฒเบเปเบเป, เปเบเบดเปเบเบเบฐเบเบดเบเบฑเบเบเบฒเบเบเปเบฒเบเบเบญเบ nodejs เปเบเบเบปเบงเบเปเบญเบเปเบงเบฑเบ:
import {Duplex} from 'readable-stream';
export class PortStream extends Duplex{
constructor(port){
super({objectMode: true});
this._port = port;
port.onMessage.addListener(this._onMessage.bind(this));
port.onDisconnect.addListener(this._onDisconnect.bind(this))
}
_onMessage(msg) {
if (Buffer.isBuffer(msg)) {
delete msg._isBuffer;
const data = new Buffer(msg);
this.push(data)
} else {
this.push(msg)
}
}
_onDisconnect() {
this.destroy()
}
_read(){}
_write(msg, encoding, cb) {
try {
if (Buffer.isBuffer(msg)) {
const data = msg.toJSON();
data._isBuffer = true;
this._port.postMessage(data)
} else {
this._port.postMessage(msg)
}
} catch (err) {
return cb(new Error('PortStream - disconnected'))
}
cb()
}
}
เบเบญเบเบเบตเปเปเบซเปเบเบงเบเปเบฎเบปเบฒเบชเปเบฒเบเบเบฒเบเปเบเบทเปเบญเบกเบเปเปเปเบ UI:
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import Dnode from 'dnode/browser';
const DEV_MODE = process.env.NODE_ENV !== 'production';
setupUi().catch(console.error);
async function setupUi(){
// ะขะฐะบะถะต, ะบะฐะบ ะธ ะฒ ะบะปะฐััะต ะฟัะธะปะพะถะตะฝะธั ัะพะทะดะฐะตะผ ะฟะพัั, ะพะฑะพัะฐัะธะฒะฐะตะผ ะฒ stream, ะดะตะปะฐะตะผ dnode
const backgroundPort = extensionApi.runtime.connect({name: 'popup'});
const connectionStream = new PortStream(backgroundPort);
const dnode = Dnode();
connectionStream.pipe(dnode).pipe(connectionStream);
const background = await new Promise(resolve => {
dnode.once('remote', api => {
resolve(api)
})
});
// ะะตะปะฐะตะผ ะพะฑัะตะบั API ะดะพัััะฟะฝัะผ ะธะท ะบะพะฝัะพะปะธ
if (DEV_MODE){
global.background = background;
}
}
เบซเบผเบฑเบเบเบฒเบเบเบฑเปเบ, เบเบงเบเปเบฎเบปเบฒเบชเปเบฒเบเบเบฒเบเปเบเบทเปเบญเบกเบเปเปเปเบ script เปเบเบทเปเบญเปเบ:
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import PostMessageStream from 'post-message-stream';
setupConnection();
injectScript();
function setupConnection(){
const backgroundPort = extensionApi.runtime.connect({name: 'contentscript'});
const backgroundStream = new PortStream(backgroundPort);
const pageStream = new PostMessageStream({
name: 'content',
target: 'page',
});
pageStream.pipe(backgroundStream).pipe(pageStream);
}
function injectScript(){
try {
// inject in-page script
let script = document.createElement('script');
script.src = extensionApi.extension.getURL('inpage.js');
const container = document.head || document.documentElement;
container.insertBefore(script, container.children[0]);
script.onload = () => script.remove();
} catch (e) {
console.error('Injection failed.', e);
}
}
เปเบเบทเปเบญเบเบเบฒเบเบเบงเบเปเบฎเบปเบฒเบเปเบญเบเบเบฒเบ API เบเปเปเปเบเปเบขเบนเปเปเบเบชเบฐเบเบดเบเปเบเบทเปเบญเบซเบฒ, เปเบเปเปเบเบเบเบปเบเบขเบนเปเปเบเบซเบเปเบฒ, เบเบงเบเปเบฎเบปเบฒเปเบฎเบฑเบเบชเบญเบเบขเปเบฒเบ:
- เบเบงเบเปเบฎเบปเบฒเบชเปเบฒเบเบชเบญเบเบชเบฒเบเบเปเปเบฒ. เบซเบเบถเปเบ - เบเปเปเบซเบเปเบฒ, เบขเบนเปเปเบเบดเบเบชเบธเบเบเบญเบ postMessage. เบชเปเบฒเบฅเบฑเบเบเบตเปเบเบงเบเปเบฎเบปเบฒเปเบเปเบเบตเป
เบเบธเบเบเบตเป เบเบฒเบเบเบนเปเบชเปเบฒเบ metamask. เบเบฐเปเบชเบเบตเบชเบญเบเปเบกเปเบเปเบเบทเปเบญเบเบทเปเบเบซเบฅเบฑเบเบเบญเบเบเบญเบเบเบตเปเปเบเปเบฎเบฑเบเบเบฒเบruntime.connect
. เปเบซเปเบเบทเปเบเบงเบเบกเบฑเบ. เบเบญเบเบเบตเปเปเปเบฒเปเบงเบฑเบเบเบฐเบกเบตเบเบฐเปเบชเปเบเบซเบฒเบเบทเปเบเบซเบผเบฑเบ. - เปเบชเปเบชเบฐเบเบฃเบดเบเปเบชเป DOM. เบเบฒเบงเปเบซเบฅเบเบชเบฐเบเบฃเบดเบ (เบเบฒเบเปเบเบปเปเบฒเปเบเบดเบเบกเบฑเบเบเบทเบเบญเบฐเบเบธเบเบฒเบเปเบซเปเบขเบนเปเปเบ manifest) เปเบฅเบฐเบชเปเบฒเบเปเบเบฑเบ
script
เบเบฑเบโเปเบเบทเปเบญโเปเบโเบเบญเบโเบเบปเบโเบขเบนเปโเปเบโ:
import PostMessageStream from 'post-message-stream';
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
setupConnection();
injectScript();
function setupConnection(){
// ะกััะธะผ ะบ ะฑะตะบะณัะฐัะฝะดั
const backgroundPort = extensionApi.runtime.connect({name: 'contentscript'});
const backgroundStream = new PortStream(backgroundPort);
// ะกััะธะผ ะบ ัััะฐะฝะธัะต
const pageStream = new PostMessageStream({
name: 'content',
target: 'page',
});
pageStream.pipe(backgroundStream).pipe(pageStream);
}
function injectScript(){
try {
// inject in-page script
let script = document.createElement('script');
script.src = extensionApi.extension.getURL('inpage.js');
const container = document.head || document.documentElement;
container.insertBefore(script, container.children[0]);
script.onload = () => script.remove();
} catch (e) {
console.error('Injection failed.', e);
}
}
เบเบญเบเบเบตเปเบเบงเบเปเบฎเบปเบฒเบชเปเบฒเบเบงเบฑเบเบเบธ api เปเบ inpage เปเบฅเบฐเบเบฑเปเบเบกเบฑเบเปเบเบฑเบเบเบปเปเบงเปเบฅเบ:
import PostMessageStream from 'post-message-stream';
import Dnode from 'dnode/browser';
setupInpageApi().catch(console.error);
async function setupInpageApi() {
// ะกััะธะผ ะบ ะบะพะฝัะตะฝััะบัะธะฟัั
const connectionStream = new PostMessageStream({
name: 'page',
target: 'content',
});
const dnode = Dnode();
connectionStream.pipe(dnode).pipe(connectionStream);
// ะะพะปััะฐะตะผ ะพะฑัะตะบั API
const pageApi = await new Promise(resolve => {
dnode.once('remote', api => {
resolve(api)
})
});
// ะะพัััะฟ ัะตัะตะท window
global.SignerApp = pageApi;
}
เบเบงเบเปเบฎเบปเบฒเบเปเบญเบกเปเบฅเปเบง
API เบซเบงเปเบฒเบเปเบเบปเปเบฒ เปเบฅเบฐเบเบปเปเบเบเบณเปเบเบตเบ. เปเบเบเปเบฒเบเบซเบเปเบฒ, เบเบงเบเปเบฎเบปเบฒเบชเบฒเบกเบฒเบเปเบเบซเบฒเบซเบเปเบฒเบเบตเปเบชเบฐเบเบฒเบเบเบตเปเบเบฑเปเบเบเบตเป:
เบเบฒเบเปเบฎเบฑเบเบงเบฝเบเบเบฑเบเบเบฑเบเบเบฑเบ callback เปเบ JS เบเบตเปเบเบฑเบเบชเบฐเปเบซเบกเปเบกเปเบเบฅเบฑเบเบชเบฐเบเบฐเบเบตเปเบเปเปเบเบต, เบชเบฐเบเบฑเปเบเปเบซเปเบเบงเบเปเบฎเบปเบฒเบเบฝเบเบเบปเบงเบเปเบงเบเบเปเบญเบเปเปเบเบทเปเบญเบชเปเบฒเบ dnode เบเบตเปเบเปเบงเบเปเบซเปเบเปเบฒเบเบชเบฒเบกเบฒเบเบชเบปเปเบเบเปเบฒเบเบงเบฑเบเบเบธ API เปเบเบซเบฒ utils.
เบเบญเบเบเบตเปเบงเบฑเบเบเบธ API เบเบฐเบกเบตเบฅเบฑเบเบชเบฐเบเบฐเบเบฑเปเบเบเบตเป:
export class SignerApp {
popupApi() {
return {
hello: async () => "world"
}
}
...
}
เปเบญเบปเบฒเบงเบฑเบเบเบธเบเบฒเบเปเบฅเบเบฐเปเบเปเบเบเบเบตเป:
import {cbToPromise, transformMethods} from "../../src/utils/setupDnode";
const pageApi = await new Promise(resolve => {
dnode.once('remote', remoteApi => {
// ะก ะฟะพะผะพััั ััะธะปะธั ะผะตะฝัะตะผ ะฒัะต callback ะฝะฐ promise
resolve(transformMethods(cbToPromise, remoteApi))
})
});
เปเบฅเบฐเบเบฑเบเบเบฑเบเบเบฒเบเปเบเบชเบปเปเบเบเบทเบเบเปเบฒเบชเบฑเบเบเบฒ:
เบฅเบธเปเบเบเบตเปเบกเบตเบเบฑเบเบเบฑเบเบเปเปเบเบดเปเบเปเบเบ
เปเบเบเบฅเบงเบกเปเบฅเปเบง, เบงเบดเบเบตเบเบฒเบ RPC เปเบฅเบฐ stream เปเบเบดเปเบเบเบทเบงเปเบฒเบกเบตเบเบงเบฒเบกเบเบทเบเบซเบเบธเปเบเบซเบผเบฒเบ: เบเบงเบเปเบฎเบปเบฒเบชเบฒเบกเบฒเบเบเปเบฒเปเบเปเบเบฒเบ multiplexing เปเบญเบเปเปเบฒเปเบฅเบฐเบชเปเบฒเบ APIs เบเบตเปเปเบเบเบเปเบฒเบเบเบฑเบเบซเบผเบฒเบเบชเปเบฒเบฅเบฑเบเบงเบฝเบเบเบฒเบเบเบตเปเปเบเบเบเปเบฒเบเบเบฑเบ. เปเบเบซเบผเบฑเบเบเบฒเบ, dnode เบชเบฒเบกเบฒเบเบเปเบฒเปเบเปเปเบเปเบเบธเบเบเปเบญเบ, เบชเบดเปเบเบเบตเปเบชเปเบฒเบเบฑเบเปเบกเปเบเบเบฒเบเบซเปเปเบเบฒเบเบเบปเบเบชเบปเปเบเปเบเบฎเบนเบเปเบเบเบเบญเบเบเปเปเบฒ nodejs.
เบเบฒเบเปเบฅเบทเบญเบเบญเบทเปเบเปเบกเปเบเบฎเบนเบเปเบเบ JSON, เปเบเบดเปเบเบเบฐเบเบดเบเบฑเบเปเบเปเบเบเบญเบ JSON RPC 2. เปเบเบงเปเบเบเปเปเบเบฒเบก, เบกเบฑเบเปเบฎเบฑเบเบงเบฝเบเบเบฑเบเบเบฒเบเบเบปเบเบชเบปเปเบเบชเบฐเปเบเบฒเบฐ (TCP เปเบฅเบฐ HTTP(S)), เปเบเบดเปเบเบเปเปเบชเบฒเบกเบฒเบเปเบเปเปเบเปเปเบเบเปเบฅเบฐเบเบตเบเบญเบเบเบงเบเปเบฎเบปเบฒ.
เบชเบฐเบเบฒเบเบฐเบเบฒเบเปเบ เปเบฅเบฐ localStorage
เบเบงเบเปเบฎเบปเบฒเบเบฐเบเปเบญเบเบเบฒเบเปเบเบฑเบเบฎเบฑเบเบชเบฒเบชเบฐเบเบฒเบเบฐเบเบฒเบเปเบเบเบญเบเบเปเบฒเบฎเปเบญเบเบชเบฐเบซเบกเบฑเบ - เบขเปเบฒเบเบซเบเปเบญเบเบฅเบฐเบซเบฑเบเปเบเบฑเบเบเบทเป. เบเบงเบเปเบฎเบปเบฒเบชเบฒเบกเบฒเบเปเบเบตเปเบกเบชเบฐเบเบฒเบเบฐเปเบซเปเบเบฑเบเปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเบ เปเบฅเบฐเบงเบดเบเบตเบเบฒเบเบเปเบฝเบเบกเบฑเบเบขเบนเปเปเบ popup API เปเบเปเบเปเบฒเบเป:
import {setupDnode} from "./utils/setupDnode";
export class SignerApp {
constructor(){
this.store = {
keys: [],
};
}
addKey(key){
this.store.keys.push(key)
}
removeKey(index){
this.store.keys.splice(index,1)
}
popupApi(){
return {
addKey: async (key) => this.addKey(key),
removeKey: async (index) => this.removeKey(index)
}
}
...
}
เปเบเบเบทเปเบเบซเบฅเบฑเบ, เบเบงเบเปเบฎเบปเบฒเบเบฐเบซเปเปเบเบธเบเบขเปเบฒเบเปเบเบเบฑเบเบเบฑเบเปเบฅเบฐเบเบฝเบเบงเบฑเบเบเบธเปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเบเปเบชเปเบเปเบญเบเบขเปเบฝเบกเปเบเบทเปเบญเปเบซเปเบเบงเบเปเบฎเบปเบฒเบชเบฒเบกเบฒเบเปเบฎเบฑเบเบงเบฝเบเบเบฑเบเบกเบฑเบเบเบฒเบ console:
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {SignerApp} from "./SignerApp";
const DEV_MODE = process.env.NODE_ENV !== 'production';
setupApp();
function setupApp() {
const app = new SignerApp();
if (DEV_MODE) {
global.app = app;
}
extensionApi.runtime.onConnect.addListener(connectRemote);
function connectRemote(remotePort) {
const processName = remotePort.name;
const portStream = new PortStream(remotePort);
if (processName === 'contentscript') {
const origin = remotePort.sender.url;
app.connectPage(portStream, origin)
} else {
app.connectPopup(portStream)
}
}
}
เบกเบฒเปเบเบตเปเบกเบเบฐเปเบเบเบฒเบเบญเบฑเบเบเบฒเบ UI console เปเบฅเบฐเปเบเบดเปเบเบงเปเบฒเปเบเบตเบเบซเบเบฑเบเบเบถเปเบเบเบฑเบเบชเบฐเบเบฒเบเบฐ:
เบฅเบฑเบเบเปเบญเบเปเบเปเบฎเบฑเบเบเบฒเบเปเบฎเบฑเบเปเบซเปเบเบปเบเบเบตเปเปเบเบทเปเบญเบเปเปเปเบซเปเบเบฐเปเบเบชเบนเบเปเบชเบเปเบเปเบเปเบงเบฅเบฒเบเบตเป restart.
เบเบงเบเปเบฎเบปเบฒเบเบฐเปเบเบฑเบเบฎเบฑเบเบชเบฒเบกเบฑเบเปเบงเปเปเบ localStorage, เบเบฝเบเบเบฑเบเบกเบฑเบเบเบฑเบเบเบธเบเปเบเบฒเบเบเปเบฝเบเปเบเบ. เบซเบผเบฑเบเบเบฒเบเบเบฑเปเบ, เบเบฒเบเปเบเบปเปเบฒเปเบเบดเบเบกเบฑเบเบเบฑเบเบเบฐเบกเบตเบเบงเบฒเบกเบเปเบฒเปเบเบฑเบเบชเปเบฒเบฅเบฑเบ UI, เปเบฅเบฐเบเปเบญเบเบเปเปเบเปเบญเบเบเบฒเบเบเบญเบเบเบฒเบเบเปเบฝเบเปเบเบ. เบญเบตเบเบเบฒเบกเบเบฒเบเบเบตเป, เบกเบฑเบเบเบฐเบชเบฐเบเบงเบเปเบเบเบฒเบเบชเปเบฒเบเบเบฒเบเปเบเบฑเบเบฎเบฑเบเบชเบฒเบเบตเปเบชเบฒเบกเบฒเบเบชเบฑเบเปเบเบเปเบเปเปเบฅเบฐเบเบญเบเบเบฒเบเบเปเบฝเบเปเบเบเบเบญเบเบกเบฑเบ.
เบเบงเบเปเบฎเบปเบฒเบเบฐเปเบเปเบซเปเบญเบเบชเบฐเบซเบกเบธเบ mobx (
เปเบซเปเปเบเบตเปเบกเบเบฒเบเปเบฅเบตเปเบกเบเบปเปเบเบเบญเบเบชเบฐเบเบฒเบเบฐเปเบเบทเปเบญเบเบเบปเปเบเปเบฅเบฐเปเบฎเบฑเบเปเบซเปเบฎเปเบฒเบเบชเบฒเบกเบฒเบเบชเบฑเบเปเบเบเปเบเป:
import {observable, action} from 'mobx';
import {setupDnode} from "./utils/setupDnode";
export class SignerApp {
constructor(initState = {}) {
// ะะฝะตัะฝะต store ัะฐะบ ะธ ะพััะฐะฝะตััั ัะตะผ ะถะต ะพะฑัะตะบัะพะผ, ัะพะปัะบะพ ัะตะฟะตัั ะฒัะต ะตะณะพ ะฟะพะปั ััะฐะปะธ proxy, ะบะพัะพััะต ะพััะปะตะถะธะฒะฐัั ะดะพัััะฟ ะบ ะฝะธะผ
this.store = observable.object({
keys: initState.keys || [],
});
}
// ะะตัะพะดั, ะบะพัะพััะต ะผะตะฝััั observable ะฟัะธะฝััะพ ะพะฑะพัะฐัะธะฒะฐัั ะดะตะบะพัะฐัะพัะพะผ
@action
addKey(key) {
this.store.keys.push(key)
}
@action
removeKey(index) {
this.store.keys.splice(index, 1)
}
...
}
"เบเบฒเบเปเบเปเบเปเบฒเบกเปเบฒเบ," mobx เปเบเปเบเปเบฝเบเบเบทเปเบเบเบตเปเบฎเปเบฒเบเบเบฑเบเบซเบกเบปเบเบเปเบงเบเบเบปเบงเปเบเบเปเบฅเบฐเบเบฑเบเบเบงเบฒเบเบเบฒเบเปเบเบเบฑเบเบซเบกเบปเบเปเบเบซเบฒเบเบงเบเปเบเบปเบฒ. เบกเบฑเบเบเบฐเปเบเบฑเบเปเบเปเบเปเบเบตเปเบเบฐเบเบญเบเบเปเปเบเบงเบฒเบกเปเบซเบผเบปเปเบฒเบเบตเป.
เบเปเบฒเบเบฅเบธเปเบกเบเบตเปเบเปเบญเบเบกเบฑเบเบเบฐเปเบเปเบเปเบฒเบงเปเบฒ "เปเบกเบทเปเบญเบเปเบฝเบเปเบเบ", เปเบเบดเบเปเบกเปเบเบงเปเบฒเบกเบฑเบเบเปเปเบเบทเบเบเปเบญเบเบเบฑเบเบซเบกเบปเบ. Mobx เบเบดเบเบเบฒเบกเบเบฒเบเปเบเบปเปเบฒเปเบเบดเบเบเปเบญเบเบเปเปเบกเบนเบ. Getters เปเบฅเบฐ setters เบเบญเบ proxy objects เบเบตเปเบซเปเบญเบเบชเบฐเบซเบกเบธเบเบชเปเบฒเบเปเบกเปเบเบเบทเบเบเปเบฒเปเบเป.
เบเบฑเบเบเบปเบเปเบเปเบเบเบฐเบเบดเบเบฑเบเบฎเบฑเบเปเบเปเบชเบญเบเบเบธเบเบเบฐเบชเบปเบ:
- เปเบโเบฎเบนเบโเปเบเบโเบเบตเปโเปเบเบฑเปเบโเบเบฑเบโเบเบตเปโเบกเบตโเบเบธเบ enforceActions, mobx เบซเปเบฒเบกโเบเบฒเบโเบเปเบฝเบโเปเบเบโเบฅเบฑเบโเปเบเบโเบเบปเบโ. เบกเบฑเบเบเบทเบงเปเบฒเปเบเบฑเบเบเบฒเบเบเบฐเบเบดเบเบฑเบเบเบตเปเบเบตเบเบตเปเบเบฐเปเบฎเบฑเบเบงเบฝเบเบเบฒเบเปเบเปเปเบเบทเปเบญเบเปเบเบเบตเปเปเบเบฑเปเบเบเบฑเบ.
- เปเบเบดเบเปเบกเปเบเบงเปเบฒเบซเบเปเบฒเบเบตเปเบเปเบฝเบเปเบเบเบฅเบฑเบเบซเบผเบฒเบเบเบฑเปเบ - เบเบปเบงเบขเปเบฒเบ, เบเบงเบเปเบฎเบปเบฒเบเปเบฝเบเบซเบผเบฒเบเบเปเบญเบเบเปเปเบกเบนเบเปเบเบฅเบฐเบซเบฑเบเบซเบผเบฒเบเบชเบฒเบ - เบเบนเปเบชเบฑเบเปเบเบเบเบฒเบเปเบเปเบฎเบฑเบเบเบฒเบเปเบเปเบเปเบเบทเบญเบเบเบฝเบเปเบเปเปเบกเบทเปเบญเบกเบฑเบเบชเปเบฒเปเบฅเบฑเบ. เบเบตเปเปเบกเปเบเบชเบดเปเบเบชเปเบฒเบเบฑเบเปเบเบเบชเบฐเปเบเบฒเบฐเบชเปเบฒเบฅเบฑเบ frontend, เบเปเบญเบเบเบตเปเบเบฒเบเบเบฑเบเบเบธเบเบฅเบฑเบเบเบตเปเบเปเปเบเปเบฒเปเบเบฑเบเบเปเบฒเปเบเบชเบนเปเบเบฒเบเบชเบฐเปเบเบเบญเบปเบเบเบฐเบเบญเบเบเบตเปเบเปเปเบเปเบฒเปเบเบฑเบ. เปเบเบเปเบฅเบฐเบเบตเบเบญเบเบเบงเบเปเบฎเบปเบฒ, เบเบฑเบเบชเบญเบเบญเบฑเบเบเปเบฒเบญเบดเบเบซเบผเบทเบเบตเบชเบญเบเปเบกเปเบเบเปเปเบเปเบฝเบงเบเปเบญเบเปเบเบเบชเบฐเปเบเบฒเบฐ, เปเบเปเบเบงเบเปเบฎเบปเบฒเบเบฐเบเบฐเบเบดเบเบฑเบเบเบฒเบกเบเบฒเบเบเบฐเบเบดเบเบฑเบเบเบตเปเบเบตเบเบตเปเบชเบธเบ. เบกเบฑเบเปเบเบฑเบเบเบฐเปเบเบเบตเบเบตเปเบเบฐเบเบดเบเปเบเบทเปเบญเบเบเบปเบเปเบเปเบเบเบฑเบเบซเบเปเบฒเบเบตเปเบเบฑเบเบซเบกเบปเบเบเบตเปเบเปเบฝเบเบชเบฐเบเบฒเบเบฐเบเบญเบเบเบปเปเบเบเบฒเบเบตเปเบชเบฑเบเปเบเบเปเบซเบฑเบ.
เปเบเบเบทเปเบเบซเบฅเบฑเบเบเบงเบเปเบฎเบปเบฒเบเบฐเปเบเบตเปเบกเบเบฒเบเปเบฅเบตเปเบกเบเบปเปเบเปเบฅเบฐเบเบฑเบเบเบถเบเบชเบฐเบเบฒเบเบฐเปเบ localStorage:
import {reaction, toJS} from 'mobx';
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {SignerApp} from "./SignerApp";
// ะัะฟะพะผะพะณะฐัะตะปัะฝัะต ะผะตัะพะดั. ะะฐะฟะธััะฒะฐัั/ัะธัะฐัั ะพะฑัะตะบั ะฒ/ะธะท localStorage ะฒะธะดะต JSON ัััะพะบะธ ะฟะพ ะบะปััั 'store'
import {loadState, saveState} from "./utils/localStorage";
const DEV_MODE = process.env.NODE_ENV !== 'production';
setupApp();
function setupApp() {
const initState = loadState();
const app = new SignerApp(initState);
if (DEV_MODE) {
global.app = app;
}
// Setup state persistence
// ะ ะตะทัะปััะฐั reaction ะฟัะธัะฒะฐะธะฒะฐะตััั ะฟะตัะตะผะตะฝะฝะพะน, ััะพะฑั ะฟะพะดะฟะธัะบั ะผะพะถะฝะพ ะฑัะปะพ ะพัะผะตะฝะธัั. ะะฐะผ ััะพ ะฝะต ะฝัะถะฝะพ, ะพััะฐะฒะปะตะฝะพ ะดะปั ะฟัะธะผะตัะฐ
const localStorageReaction = reaction(
() => toJS(app.store), // ะคัะฝะบัะธั-ัะตะปะตะบัะพั ะดะฐะฝะฝัั
saveState // ะคัะฝะบัะธั, ะบะพัะพัะฐั ะฑัะดะตั ะฒัะทะฒะฐะฝะฐ ะฟัะธ ะธะทะผะตะฝะตะฝะธะธ ะดะฐะฝะฝัั
, ะบะพัะพััะต ะฒะพะทะฒัะฐัะฐะตั ัะตะปะตะบัะพั
);
extensionApi.runtime.onConnect.addListener(connectRemote);
function connectRemote(remotePort) {
const processName = remotePort.name;
const portStream = new PortStream(remotePort);
if (processName === 'contentscript') {
const origin = remotePort.sender.url
app.connectPage(portStream, origin)
} else {
app.connectPopup(portStream)
}
}
}
เบเบฑเบเบเบฑเบเบเบดเบเบดเบฃเบดเบเบฒเปเบกเปเบเบซเบเปเบฒเบชเบปเบเปเบเบขเบนเปเบเบตเปเบเบตเป. เบกเบฑเบโเบกเบตโเบชเบญเบโเบเบฒเบโเปเบเปโเบเบฝเบโ:
- เบเบปเบงเปเบฅเบทเบญเบเบเปเปเบกเบนเบ.
- เบเบปเบงเบเบฑเบเบเบฒเบเบเบตเปเบเบฐเบเบทเบเปเบญเบตเปเบเบเปเบงเบเบเปเปเบกเบนเบเบเบตเปเบเบธเบเบเบฑเปเบเบเบตเปเบกเบฑเบเบเปเบฝเบเปเบเบ.
เบเปเปเปเบซเบกเบทเบญเบเบเบฑเบ redux, เบเปเบญเบเบเบตเปเบเบงเบเปเบฎเบปเบฒเปเบเปเบฎเบฑเบเบฅเบฑเบเปเบเบฑเบเบเบฒเบเปเบเปเบเบฝเบเบขเปเบฒเบเบเบฑเบเปเบเบ, mobx เบเบทเปเบเปเบฒเบงเปเบฒเบเบงเบเปเบฎเบปเบฒเปเบเบปเปเบฒเปเบเบดเบเบเบฒเบเปเบเบเบปเบงเปเบฅเบทเบญเบ, เปเบฅเบฐเบเบฝเบเปเบเปเปเบเบซเบฒเบเบปเบงเบเบฑเบเบเบฒเบเปเบกเบทเปเบญเบเบงเบเปเบเบปเบฒเบเปเบฝเบเปเบเบ.
เบกเบฑเบเปเบเบฑเบเบชเบดเปเบเบชเปเบฒเบเบฑเบเบเบตเปเบเบฐเปเบเบปเปเบฒเปเบเบขเปเบฒเบเปเบเปเบเบดเบเบงเปเบฒ mobx เบเบฑเบเบชเบดเบเปเบเปเบเบงเปเบเบเบตเปเบเบงเบเปเบฎเบปเบฒเบเบดเบเบเบฒเบก. เบเปเบฒเบเปเบญเบเบเบฝเบเบเบปเบงเปเบฅเบทเบญเบเปเบเบฅเบฐเบซเบฑเบเปเบเบเบเบตเป() => app.store
, เบซเบผเบฑเบเบเบฒเบเบเบฑเปเบ, เบเบฐเบเบดเบเบดเบฅเบดเบเบฒเบเบฐเบเปเปเบเบทเบเปเบญเบตเปเบเบงเปเบฒ, เบเบฑเบเบเบฑเปเบเปเบเปเบเบฒเบเปเบเบฑเบเบฎเบฑเบเบชเบฒเบเบปเบงเบกเบฑเบเปเบญเบเบเปเปเบชเบฒเบกเบฒเบเบชเบฑเบเปเบเบเปเบซเบฑเบเปเบเป, เบเบฝเบเปเบเปเบเบปเปเบเบเบฒเบเบญเบเบกเบฑเบเปเบเบปเปเบฒเบเบฑเปเบ.
เบเปเบฒเบเปเบญเบเบเบฝเบเปเบเบเบเบตเป () => app.store.keys
, เบซเบผเบฑเบเบเบฒเบเบเบฑเปเบ, เบญเบตเบเปเบเบทเปเบญเบซเบเบถเปเบเบเปเปเบกเบตเบซเบเบฑเบเปเบเบตเบเบเบถเปเบ, เบเบฑเบเบเบฑเปเบเปเบเปเปเบกเบทเปเบญเปเบเบตเปเบก / เบเบญเบเบญเบปเบเบเบฐเบเบญเบ array, เบเบฒเบเบญเปเบฒเบเบญเบตเบเปเบเบดเบเบกเบฑเบเบเบฐเบเปเปเบเปเบฝเบเปเบเบ.
Mobx เปเบฎเบฑเบเปเปเบฒเบเบตเปเปเบเบฑเบเบเบปเบงเปเบฅเบทเบญเบเปเบเบฑเบเบเบฑเปเบเบเบณเบญเบดเบ เปเบฅเบฐเบเบฝเบเปเบเปเบเบดเบเบเบฒเบกเบชเบดเปเบเบเบตเปเบชเบฑเบเปเบเบเปเบเปเบเบตเปเบเบงเบเปเบฎเบปเบฒเปเบเปเปเบเบปเปเบฒเปเบเบดเบ. เบเบตเปเปเบกเปเบเปเบฎเบฑเบเบเปเบฒเบเบเบปเบงเบฎเบฑเบเบเบปเบงเปเบเบ. เบเบฑเปเบเบเบฑเปเบ, เบเบฑเบเบเบฑเบเบเบตเปเบชเปเบฒเบเบเบถเปเบเปเบกเปเบเบเบทเบเบเปเบฒเปเบเปเบขเบนเปเบเบตเปเบเบตเป toJS
. เบกเบฑเบเบชเบปเปเบเบเบทเบเบงเบฑเบเบเบธเปเปเปเบเบตเปเบกเบตเบเบปเบงเปเบเบเบเบฑเบเปเบปเบเปเบเบเบเบตเปเบเปเบงเบเบเปเบญเบเบเปเปเบกเบนเบเบเบปเปเบเบชเบฐเบเบฑเบ. เปเบโเบฅเบฐโเบซเบงเปเบฒเบโเบเบฒเบโเบเบฐโเบเบดโเบเบฑเบโ, เบกเบฑเบโเบญเปเบฒเบโเบเบฒเบโเบชเบฐโเบซเบเบฒเบกโเบเบฑเบโเบซเบกเบปเบโเบเบญเบโเบงเบฑเบโเบเบธ - เปเบเบฒเบฐโเบชเบฐโเบเบฑเปเบ getters เปเบกเปเบ triggeredโ.
เปเบเบเบฑเบญเบเบญเบฑเบเบเบญเบเปเบเบเบเบงเบเปเบฎเบปเบฒเบเบฐเปเบเบตเปเบกเบฅเบฐเบซเบฑเบเบซเบผเบฒเบเบญเบฑเบเบญเบตเบเบเบฑเปเบ. เปเบงเบฅเบฒเบเบตเปเบเบงเบเปเบเบปเบฒเบเบฑเบเบชเบดเปเบเบชเบธเบเบฅเบปเบเปเบ localStorage:
เปเบกเบทเปเบญเปเปเบฒเบเบทเปเบเบซเบผเบฑเบเบเบทเบเปเบซเบผเบเบเบทเบเปเปเป, เบเปเปเบกเบนเบเบเบฑเบเบเบปเบเบขเบนเป.
เบฅเบฐเบซเบฑเบเบเปเบฒเบฎเปเบญเบเบชเบฐเบซเบกเบฑเบเบเบฑเบเบซเบกเบปเบเปเบเบดเบเบเบธเบเบเบตเปเบชเบฒเบกเบฒเบเปเบเบดเปเบเปเบเป
เบเบฒเบเปเบเบฑเบเบฎเบฑเบเบชเบฒเบเบฐเปเบเบชเปเบงเบเบเบปเบงเบเบตเปเบเบญเบเปเบ
เบเบฒเบเปเบเบฑเบเบฎเบฑเบเบชเบฒเบเบฐเปเบเบชเปเบงเบเบเบปเบงเปเบเบเปเปเบเบงเบฒเบกเบเบตเปเบเบฑเบเปเบเบเปเบกเปเบเบเปเปเบเบญเบเปเบ: เบกเบตเปเบญเบเบฒเบเบชเบฐเปเบซเบกเบตเบเบตเปเปเบเบปเปเบฒเบเบฐเบเบทเบเปเบฎเบฑเบ, เปเบเบปเปเบฒเปเบเบดเบเบเบญเบกเบเบดเบงเปเบเบตเบเบญเบเบเปเบฒเบ, เปเบฅเบฐเบญเบทเปเบเป. เบเบฑเปเบเบเบฑเปเบ, เปเบ localStorage เบเบงเบเปเบฎเบปเบฒเบเบฐเปเบเบฑเบเบฎเบฑเบเบชเบฒเบเบฐเปเบเปเบเบฎเบนเบเปเบเบเบเบตเปเปเบเบปเปเบฒเบฅเบฐเบซเบฑเบเบฅเบฑเบ.
เปเบเบทเปเบญเบเบงเบฒเบกเบเบญเบเปเบเบซเบผเบฒเบเบเบงเปเบฒเปเบเบปเปเบฒ, เบเบงเบเปเบฎเบปเบฒเบเบฐเปเบเบตเปเบกเบชเบฐเบเบฒเบเบฐเบเบตเปเบเบทเบเบฅเบฑเบญเบเปเบซเปเบเบฑเบเปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเบ, เปเบเบดเปเบเปเบเบเบฑเปเบเบเบฐเบเปเปเบกเบตเบเบฒเบเปเบเบปเปเบฒเปเบเบดเบเบเบฐเปเบเปเบฅเบตเบ. เบเบงเบเปเบฎเบปเบฒเบเบฐเปเบญเบเบชเปเบงเบเบเบฐเบซเบเบฒเบเปเบเปเบซเปเบชเบฐเบเบฒเบเบฐเบเบตเปเบเบทเบเบฅเบฑเบญเบเปเบเบเบญเบฑเบเบเบฐเปเบเบกเบฑเบเปเบเบทเปเบญเบเบเบฒเบเปเบปเบเปเบงเบฅเบฒ.
Mobx เบเปเบงเบเปเบซเปเบเปเบฒเบเบชเบฒเบกเบฒเบเปเบเบฑเบเบเปเปเบกเบนเบเปเบเปเบเบฝเบเปเบเปเบเบธเบเบเปเบฒเปเบชเบธเบเบเบตเป, เปเบฅเบฐเบชเปเบงเบเบเบตเปเปเบซเบผเบทเบญเบเบฐเบเบทเบเบเบดเบเปเบฅเปเปเบเบเบญเบฑเบเบเบฐเปเบเบกเบฑเบเปเบเบเบญเบตเบเปเบชเปเบกเบฑเบ. เปเบซเบผเบปเปเบฒเบเบตเปเปเบกเปเบเบญเบฑเบเบเบตเปเปเบญเบตเปเบเบงเปเบฒเบเบธเบเบชเบปเบกเบเบฑเบเบเปเบฒเบเบงเบ. เบเบงเบเปเบเบปเบฒเบชเบฒเบกเบฒเบเบเบทเบเบเบฝเบเบเบฝเบเบเบฑเบ views เปเบเบเบฒเบเบเปเปเบกเบนเบ:
import {observable, action} from 'mobx';
import {setupDnode} from "./utils/setupDnode";
// ะฃัะธะปะธัั ะดะปั ะฑะตะทะพะฟะฐัะฝะพะณะพ ัะธััะพะฒะฐะฝะธั ัััะพะบ. ะัะฟะพะปัะทััั crypto-js
import {encrypt, decrypt} from "./utils/cryptoUtils";
export class SignerApp {
constructor(initState = {}) {
this.store = observable.object({
// ะฅัะฐะฝะธะผ ะฟะฐัะพะปั ะธ ะทะฐัะธััะพะฒะฐะฝะฝัะต ะบะปััะธ. ะัะปะธ ะฟะฐัะพะปั null - ะฟัะธะปะพะถะตะฝะธะต locked
password: null,
vault: initState.vault,
// ะะตััะตัั ะดะปั ะฒััะธัะปะธะผัั
ะฟะพะปะตะน. ะะพะถะฝะพ ะฟัะพะฒะตััะธ ะฐะฝะฐะปะพะณะธั ั view ะฒ ะฑะด.
get locked(){
return this.password == null
},
get keys(){
return this.locked ?
undefined :
SignerApp._decryptVault(this.vault, this.password)
},
get initialized(){
return this.vault !== undefined
}
})
}
// ะะฝะธัะธะฐะปะธะทะฐัะธั ะฟัััะพะณะพ ั
ัะฐะฝะธะปะธัะฐ ะฝะพะฒัะผ ะฟะฐัะพะปะตะผ
@action
initVault(password){
this.store.vault = SignerApp._encryptVault([], password)
}
@action
lock() {
this.store.password = null
}
@action
unlock(password) {
this._checkPassword(password);
this.store.password = password
}
@action
addKey(key) {
this._checkLocked();
this.store.vault = SignerApp._encryptVault(this.store.keys.concat(key), this.store.password)
}
@action
removeKey(index) {
this._checkLocked();
this.store.vault = SignerApp._encryptVault([
...this.store.keys.slice(0, index),
...this.store.keys.slice(index + 1)
],
this.store.password
)
}
... // ะบะพะด ะฟะพะดะบะปััะตะฝะธั ะธ api
// private
_checkPassword(password) {
SignerApp._decryptVault(this.store.vault, password);
}
_checkLocked() {
if (this.store.locked){
throw new Error('App is locked')
}
}
// ะะตัะพะดั ะดะปั ัะธััะพะฒะบะธ/ะดะตัะธััะพะฒะบะธ ั
ัะฐะฝะธะปะธัะฐ
static _encryptVault(obj, pass){
const jsonString = JSON.stringify(obj)
return encrypt(jsonString, pass)
}
static _decryptVault(str, pass){
if (str === undefined){
throw new Error('Vault not initialized')
}
try {
const jsonString = decrypt(str, pass)
return JSON.parse(jsonString)
}catch (e) {
throw new Error('Wrong password')
}
}
}
เบเบญเบเบเบตเปเบเบงเบเปเบฎเบปเบฒเปเบเบฑเบเบฎเบฑเบเบชเบฒเบฅเบฐเบซเบฑเบเปเบเบปเปเบฒเบฅเบฐเบซเบฑเบ เปเบฅเบฐเบฅเบฐเบซเบฑเบเบเปเบฒเบเปเบเบปเปเบฒเบเบฑเปเบ. เบเบธเบเบชเบดเปเบเบเบธเบเบขเปเบฒเบเบญเบทเปเบเปเบกเปเบเบเบดเบเปเบฅเป. เบเบงเบโเปเบฎเบปเบฒโเปเบฎเบฑเบโเบเบฒเบโเปเบญเบโเบเบฑเบโเบฅเบฑเบโเบเบตเปโเบเบทเบโเบฅเบฑเบญเบโเปเบเบโเบเบฒเบโเปเบญเบปเบฒโเบฅเบฐโเบซเบฑเบโเบเปเบฒเบโเบเบฒเบโเบฅเบฑเบโ. API เบชเบฒเบเบฒเบฅเบฐเบเบฐเปเบเบเบฑเบเบเบธเบเบฑเบเบกเบตเบงเบดเบเบตเบเบฒเบเปเบฅเบตเปเบกเบเบปเปเบเบเบฒเบเปเบเบฑเบเบฎเบฑเบเบชเบฒ.
เบเบฝเบเบชเปเบฒเบฅเบฑเบเบเบฒเบเปเบเบปเปเบฒเบฅเบฐเบซเบฑเบ
import CryptoJS from 'crypto-js'
// ะัะฟะพะปัะทัะตััั ะดะปั ะพัะปะพะถะฝะตะฝะธั ะฟะพะดะฑะพัะฐ ะฟะฐัะพะปั ะฟะตัะตะฑะพัะพะผ. ะะฐ ะบะฐะถะดัะน ะฒะฐัะธะฐะฝั ะฟะฐัะพะปั ะทะปะพัะผััะปะตะฝะฝะธะบั ะฟัะธะดะตััั ัะดะตะปะฐัั 5000 ั
ะตัะตะน
function strengthenPassword(pass, rounds = 5000) {
while (rounds-- > 0){
pass = CryptoJS.SHA256(pass).toString()
}
return pass
}
export function encrypt(str, pass){
const strongPass = strengthenPassword(pass);
return CryptoJS.AES.encrypt(str, strongPass).toString()
}
export function decrypt(str, pass){
const strongPass = strengthenPassword(pass)
const decrypted = CryptoJS.AES.decrypt(str, strongPass);
return decrypted.toString(CryptoJS.enc.Utf8)
}
เบเบปเบงเบเปเบญเบเปเบงเบฑเบเบกเบต API idle เปเบเบเบเปเบฒเบเบเบตเปเบเปเบฒเบเบชเบฒเบกเบฒเบเบเบญเบเปเบซเบเบเบฒเบ - เบเบฒเบเบเปเบฝเบเปเบเบเบเบญเบเบฅเบฑเบ. เบฅเบฑเบ, เบเบฒเบกเบเบงเบฒเบกเปเบซเบกเบฒเบฐเบชเบปเบก, เบญเบฒเบเบเบฐเปเบเบฑเบ idle
, active
ะธ locked
. เบชเปเบฒเบฅเบฑเบ idle เบเปเบฒเบเบชเบฒเบกเบฒเบเบเปเบฒเบเบปเบเปเบงเบฅเบฒเบซเบกเบปเบเปเบงเบฅเบฒ, เปเบฅเบฐเบเบทเบเบฅเบฑเบญเบเปเบกเปเบเบเบทเบเบเปเบฒเบเบปเบเปเบกเบทเปเบญ OS เบเบปเบงเบเบญเบเบกเบฑเบเปเบญเบเบเบทเบเบเบฅเบฑเบญเบ. เบเบงเบโเปเบฎเบปเบฒโเบเบฑเบโเบเบฐโเบเปเบฝเบโเบเบปเบงโเปเบฅเบทเบญเบโเบชเปเบฒโเบฅเบฑเบโเบเบฒเบโเบเบฑเบโเบเบถเบโเปเบเบฑเบ localStorageโ:
import {reaction, toJS} from 'mobx';
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {SignerApp} from "./SignerApp";
import {loadState, saveState} from "./utils/localStorage";
const DEV_MODE = process.env.NODE_ENV !== 'production';
const IDLE_INTERVAL = 30;
setupApp();
function setupApp() {
const initState = loadState();
const app = new SignerApp(initState);
if (DEV_MODE) {
global.app = app;
}
// ะขะตะฟะตัั ะผั ัะฒะฝะพ ัะทัะฒะฐะตะผ ะฟะพะปะต, ะบะพัะพัะพะผั ะฑัะดะตั ะฟัะพะธัั
ะพะดะธัั ะดะพัััะฟ, reaction ะพััะฐะฑะพัะฐะตั ะฝะพัะผะฐะปัะฝะพ
reaction(
() => ({
vault: app.store.vault
}),
saveState
);
// ะขะฐะนะผะฐัั ะฑะตะทะดะตะนััะฒะธั, ะบะพะณะดะฐ ััะฐะฑะพัะฐะตั ัะพะฑััะธะต
extensionApi.idle.setDetectionInterval(IDLE_INTERVAL);
// ะัะปะธ ะฟะพะปัะทะพะฒะฐัะตะปั ะทะฐะปะพัะธะป ัะบัะฐะฝ ะธะปะธ ะฑะตะทะดะตะนััะฒะพะฒะฐะป ะฒ ัะตัะตะฝะธะต ัะบะฐะทะฐะฝะฝะพะณะพ ะธะฝัะตัะฒะฐะปะฐ ะปะพัะธะผ ะฟัะธะปะพะถะตะฝะธะต
extensionApi.idle.onStateChanged.addListener(state => {
if (['locked', 'idle'].indexOf(state) > -1) {
app.lock()
}
});
// Connect to other contexts
extensionApi.runtime.onConnect.addListener(connectRemote);
function connectRemote(remotePort) {
const processName = remotePort.name;
const portStream = new PortStream(remotePort);
if (processName === 'contentscript') {
const origin = remotePort.sender.url
app.connectPage(portStream, origin)
} else {
app.connectPopup(portStream)
}
}
}
เบฅเบฐเบซเบฑเบเบเปเบญเบเบเบฑเปเบเบเบญเบเบเบตเปเปเบกเปเบ
เบเบฒเบเปเบฎเบฑเบเบเบธเบฅเบฐ เบเบณ
เบเบฑเปเบเบเบฑเปเบ, เบเบงเบเปเบฎเบปเบฒเบกเบฒเบฎเบญเบเบชเบดเปเบเบเบตเปเบชเปเบฒเบเบฑเบเบเบตเปเบชเบธเบ: เบเบฒเบเบชเปเบฒเบเปเบฅเบฐเปเบเบฑเบเบเบฒเบเปเบฎเบฑเบเบเบธเบฅเบฐเบเปเบฒเปเบ blockchain. เบเบงเบเปเบฎเบปเบฒเบเบฐเปเบเป WAVES blockchain เปเบฅเบฐเบซเปเบญเบเบชเบฐเบซเบกเบธเบ
เบเปเบฒเบญเบดเบ, เปเบซเปเปเบฎเบปเบฒเบเบทเปเบกเปเบชเปเบฅเบฑเบ array เบเบญเบเบเปเปเบเบงเบฒเบกเบเบตเปเบเปเบฒเปเบเบฑเบเบเปเบญเบเปเบเปเปเบเบฑเบ, เบซเบผเบฑเบเบเบฒเบเบเบฑเปเบเปเบเบตเปเบกเบงเบดเบเบตเบเบฒเบเบชเปเบฒเบฅเบฑเบเบเบฒเบเปเบเบตเปเบกเบเปเปเบเบงเบฒเบกเปเบซเบกเป, เบขเบทเบเบขเบฑเบเบฅเบฒเบเปเบเบฑเบ, เปเบฅเบฐเบเบฒเบเบเบฐเบเบดเปเบชเบ:
import {action, observable, reaction} from 'mobx';
import uuid from 'uuid/v4';
import {signTx} from '@waves/waves-transactions'
import {setupDnode} from "./utils/setupDnode";
import {decrypt, encrypt} from "./utils/cryptoUtils";
export class SignerApp {
...
@action
newMessage(data, origin) {
// ะะปั ะบะฐะถะดะพะณะพ ัะพะพะฑัะตะฝะธั ัะพะทะดะฐะตะผ ะผะตัะฐะดะฐะฝะฝัะต ั id, ััะฐัััะพะผ, ะฒััะตะผะตะฝะตะผ ัะพะทะดะฐะฝะธั ะธ ัะด.
const message = observable.object({
id: uuid(), // ะะดะตะฝัะธัะธะบะฐัะพั, ะธัะฟะพะปัะทััั uuid
origin, // Origin ะฑัะดะตะผ ะฒะฟะพัะปะตะดััะฒะธะธ ะฟะพะบะฐะทัะฒะฐัั ะฒ ะธะฝัะตััะตะนัะต
data, //
status: 'new', // ะกัะฐัััะพะฒ ะฑัะดะตั ัะตัััะต: new, signed, rejected ะธ failed
timestamp: Date.now()
});
console.log(`new message: ${JSON.stringify(message, null, 2)}`);
this.store.messages.push(message);
// ะะพะทะฒัะฐัะฐะตะผ ะฟัะพะผะธั ะฒะฝัััะธ ะบะพัะพัะพะณะพ mobx ะผะพะฝะธัะพัะธั ะธะทะผะตะฝะตะฝะธั ัะพะพะฑัะตะฝะธั. ะะฐะบ ัะพะปัะบะพ ััะฐััั ะฟะพะผะตะฝัะตััั ะผั ะทะฐัะตะทะพะปะฒะธะผ ะตะณะพ
return new Promise((resolve, reject) => {
reaction(
() => message.status, //ะัะดะตะผ ะพะฑัะตัะฒะธัั ััะฐััั ัะพะพะฑัะตะฝั
(status, reaction) => { // ะฒัะพัะพะน ะฐัะณัะผะตะฝั ััะพ ัััะปะบะฐ ะฝะฐ ัะฐะผ reaction, ััะพะฑั ะตะณะพ ะผะพะถะฝะพ ะฑัะปะพ ัะฝะธััะพะถัั ะฒะฝัััะธ ะฒัะทะพะฒะฐ
switch (status) {
case 'signed':
resolve(message.data);
break;
case 'rejected':
reject(new Error('User rejected message'));
break;
case 'failed':
reject(new Error(message.err.message));
break;
default:
return
}
reaction.dispose()
}
)
})
}
@action
approve(id, keyIndex = 0) {
const message = this.store.messages.find(msg => msg.id === id);
if (message == null) throw new Error(`No msg with id:${id}`);
try {
message.data = signTx(message.data, this.store.keys[keyIndex]);
message.status = 'signed'
} catch (e) {
message.err = {
stack: e.stack,
message: e.message
};
message.status = 'failed'
throw e
}
}
@action
reject(id) {
const message = this.store.messages.find(msg => msg.id === id);
if (message == null) throw new Error(`No msg with id:${id}`);
message.status = 'rejected'
}
...
}
เปเบกเบทเปเบญเบเบงเบเปเบฎเบปเบฒเปเบเปเบฎเบฑเบเบเปเปเบเบงเบฒเบกเปเบซเบกเป, เบเบงเบเปเบฎเบปเบฒเปเบเบตเปเบก metadata เบเบฑเบเบกเบฑเบ, เปเบฎเบฑเบ observable
เปเบฅเบฐเปเบเบตเปเบกเปเบชเป store.messages
.
เบเปเบฒเปเบเบปเปเบฒเบเปเป observable
เบเปเบงเบเบเบปเบเปเบญเบ, เบซเบผเบฑเบเบเบฒเบเบเบฑเปเบ mobx เบเบฐเปเบฎเบฑเบเบกเบฑเบเปเบญเบเปเบกเบทเปเบญเปเบเบตเปเบกเบเปเปเบเบงเบฒเบกเปเบชเป array. เบขเปเบฒเบเปเบเบเปเปเบเบฒเบก, เบกเบฑเบเบเบฐเบชเปเบฒเบเบงเบฑเบเบเบธเปเบซเบกเปเบเบตเปเบเบงเบเปเบฎเบปเบฒเบเบฐเบเปเปเบกเบตเปเบญเบเบฐเบชเบฒเบเบญเปเบฒเบเบญเบตเบ, เปเบเปเบเบงเบเปเบฎเบปเบฒเบเบฐเบเปเบญเบเบเบฒเบเบกเบฑเบเบชเปเบฒเบฅเบฑเบเบเบฑเปเบเบเบญเบเบเปเปเปเบ.
เบเปเปเปเบ, เบเบงเบเปเบฎเบปเบฒเบเบฑเบเบเบทเบเบเปเบฒเบชเบฑเบเบเบฒเบเบตเปเปเบเปเปเบเปเบกเบทเปเบญเบชเบฐเบเบฒเบเบฐเบเบฒเบเบเปเปเบเบงเบฒเบกเบเปเบฝเบเปเบเบ. เบชเบฐเบเบฒเบเบฐเปเบเปเบเบทเบเบเบดเบเบเบฒเบกเปเบเบเบเบฐเบเบดเบเบดเบฅเบดเบเบฒ, เปเบเบดเปเบเบเบฐ "เบเปเบฒเบเบปเบงเบกเบฑเบเปเบญเบ" เปเบกเบทเปเบญเบชเบฐเบเบฒเบเบฐเบเบฒเบเบเปเบฝเบเปเบเบ.
เบฅเบฐเบซเบฑเบเบงเบดเบเบตเบเบฒเบ approve
ะธ reject
เบเปเบฒเบโเบเบฒเบโเบซเบผเบฒเบโ: เบเบงเบโเปเบฎเบปเบฒโเบเบฝเบโเปเบเปโเบเปเบฝเบโเบชเบฐโเบเบฒโเบเบฐโเบเบฒเบโเบเบญเบโเบเปเปโเบเบงเบฒเบกโ, เบซเบผเบฑเบโเบเบฒเบโเบเบฒเบโเปเบเบฑเบโเบกเบฑเบโเบเปเบฒโเบซเบฒเบโเบงเปเบฒโเบเปเบฒโเปเบเบฑเบโ.
เบเบงเบเปเบฎเบปเบฒเปเบญเบปเบฒเบเบฒเบเบญเบฐเบเบธเบกเบฑเบ เปเบฅเบฐเบเบฐเบเบดเปเบชเบเปเบ UI API, newMessage เปเบ API เบเบญเบเปเปเบฒ:
export class SignerApp {
...
popupApi() {
return {
addKey: async (key) => this.addKey(key),
removeKey: async (index) => this.removeKey(index),
lock: async () => this.lock(),
unlock: async (password) => this.unlock(password),
initVault: async (password) => this.initVault(password),
approve: async (id, keyIndex) => this.approve(id, keyIndex),
reject: async (id) => this.reject(id)
}
}
pageApi(origin) {
return {
signTransaction: async (txParams) => this.newMessage(txParams, origin)
}
}
...
}
เบเบญเบเบเบตเปเปเบซเปเบฅเบญเบเปเบเบฑเบเบชเบฑเบเบเบฒเบเบฑเบเบชเปเบงเบเบเบฐเบซเบเบฒเบ:
เปเบเบเบเบปเปเบงเปเบ, เบเบธเบเบชเบดเปเบเบเบธเบเบขเปเบฒเบเปเบกเปเบเบเบฝเบกเบเปเบญเบก, เบเบฑเบเบซเบกเบปเบเบเบตเปเบเบฑเบเบเบปเบเบขเบนเป
UI
เบเบฒเบเปเบเปเบเบญเบเบเปเบญเบเบเบฒเบเบเบฒเบเปเบเบปเปเบฒเปเบเบดเบเบชเบฐเบเบฒเบเบฐเบเบญเบเปเบญเบฑเบเบเบฅเบดเปเบเบเบฑเบ. เปเบเบเปเบฒเบ UI เบเบงเบเปเบฎเบปเบฒเบเบฐเปเบฎเบฑเบ observable
state เปเบฅเบฐเปเบเบตเปเบกเบเบฑเบเบเบฑเบเปเบซเปเบเบฑเบ API เบเบตเปเบเบฐเบเปเบฝเบเบชเบฐเบเบฒเบเบฐเบเบตเป. เปเบซเปเบเบทเปเบก observable
เปเบเบซเบฒเบงเบฑเบเบเบธ API เบเบตเปเปเบเปเบฎเบฑเบเบเบฒเบเบเบทเปเบเบซเบผเบฑเบ:
import {observable} from 'mobx'
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {cbToPromise, setupDnode, transformMethods} from "./utils/setupDnode";
import {initApp} from "./ui/index";
const DEV_MODE = process.env.NODE_ENV !== 'production';
setupUi().catch(console.error);
async function setupUi() {
// ะะพะดะบะปััะฐะตะผัั ะบ ะฟะพััั, ัะพะทะดะฐะตะผ ะธะท ะฝะตะณะพ ัััะธะผ
const backgroundPort = extensionApi.runtime.connect({name: 'popup'});
const connectionStream = new PortStream(backgroundPort);
// ะกะพะทะดะฐะตะผ ะฟัััะพะน observable ะดะปั ัะพััะพัะฝะธั background'a
let backgroundState = observable.object({});
const api = {
//ะัะดะฐะตะผ ะฑะตะบะณัะฐัะฝะดั ััะฝะบัะธั, ะบะพัะพัะฐั ะฑัะดะตั ะพะฑะฝะพะฒะปััั observable
updateState: async state => {
Object.assign(backgroundState, state)
}
};
// ะะตะปะฐะตะผ RPC ะพะฑัะตะบั
const dnode = setupDnode(connectionStream, api);
const background = await new Promise(resolve => {
dnode.once('remote', remoteApi => {
resolve(transformMethods(cbToPromise, remoteApi))
})
});
// ะะพะฑะฐะฒะปัะตะผ ะฒ background observable ัะพ ััะตะนัะพะผ
background.state = backgroundState;
if (DEV_MODE) {
global.background = background;
}
// ะะฐะฟััะบ ะธะฝัะตััะตะนัะฐ
await initApp(background)
}
เปเบโเบเบญเบโเบเปเบฒเบโเบเบงเบโเปเบฎเบปเบฒโเปเบฅเบตเปเบกโเบเบปเปเบโเบเบฒเบโเปเบฎเบฑเบโเปเบซเปโเบเบฒเบโเปเบเปโเบเบญเบโเบเบญเบโเบเปเบฒโเบฎเปเบญเบโเบชเบฐโเบซเบกเบฑเบโ. เบเบตเปโเปเบกเปเบโเบเปเบฒโเบฎเปเบญเบโเบชเบฐโเบซเบกเบฑเบ reactโ. เบเบธเบโเบเบฐโเบชเบปเบโเบเบทเปเบโเบเบฒเบโเปเบกเปเบโเบเบฝเบโเปเบเปโเบเปเบฒเบโเบเบฒเบโเบเปเบฒโเปเบเป propsโ. เบกเบฑเบเบเบฐเบเบทเบเบเปเบญเบ, เปเบเปเบเบญเบ, เปเบเบทเปเบญเปเบฎเบฑเบเปเบซเปเบเบฒเบเบเปเบฅเบดเบเบฒเบเปเบเบเบเปเบฒเบเบซเบฒเบเบชเปเบฒเบฅเบฑเบเบงเบดเบเบตเบเบฒเบเปเบฅเบฐเบฎเปเบฒเบเบชเปเบฒเบฅเบฑเบเบฅเบฑเบ, เปเบเปเบชเปเบฒเบฅเบฑเบเบเบธเบเบเบฐเบชเบปเบเบเบญเบเบเบปเบเบเบงเบฒเบกเบเบตเป, เบเบตเปเปเบกเปเบเบเบฝเบเบเป:
import {render} from 'react-dom'
import App from './App'
import React from "react";
// ะะฝะธัะธะฐะปะธะทะธััะตะผ ะฟัะธะปะพะถะตะฝะธะต ั background ะพะฑัะตะบัะพะผ ะฒ ะบะฐัะตัั ะฒะต props
export async function initApp(background){
render(
<App background={background}/>,
document.getElementById('app-content')
);
}
เบเปเบงเบ mobx เบกเบฑเบเบเปเบฒเบเบซเบผเบฒเบเบเบตเปเบเบฐเปเบฅเบตเปเบกเบเบฒเบเบชเบฐเปเบเบเบเบปเบเปเบกเบทเปเบญเบเปเปเบกเบนเบเบกเบตเบเบฒเบเบเปเบฝเบเปเบเบ. เบเบงเบเปเบฎเบปเบฒเบเบฝเบเปเบเปเปเบเบงเบเปเบเบทเปเบญเบเบญเบญเบเปเบเบเบเบฑเบเบชเบฑเบเปเบเบเบเบฒเบเบเบฒเบเบเบธเบ
import React, {Component, Fragment} from 'react'
import {observer} from "mobx-react";
import Init from './components/Initialize'
import Keys from './components/Keys'
import Sign from './components/Sign'
import Unlock from './components/Unlock'
@observer // ะฃ ะะพะผะฟะพะฝะตัะฐ ั ััะธะผ ะดะตะบะพัะฐัะพะผ ะฑัะดะตั ะฐะฒัะพะผะฐัะธัะตัะบะธ ะฒัะทะฒะฐะฝ ะผะตัะพะด render, ะตัะปะธ ะฑัะดัั ะธะทะผะตะฝะตะฝั observable ะฝะฐ ะบะพัะพััะต ะพะฝ ัััะปะฐะตััั
export default class App extends Component {
// ะัะฐะฒะธะปัะฝะพ ะบะพะฝะตัะฝะพ ะฒัะฝะตััะธ ะปะพะณะธะบั ัะตะฝะดะตัะฐ ัััะฐะฝะธั ะฒ ัะพััะธะฝะณ ะธ ะฝะต ะธัะฟะพะปัะทะพะฒะฐัั ะฒะปะพะถะตะฝะฝัะต ัะตัะฝะฐัะฝัะต ะพะฟะตัะฐัะพัั,
// ะธ ะฟัะธะฒัะทัะฒะฐัั observable ะธ ะผะตัะพะดั background ะฝะตะฟะพััะตะดััะฒะตะฝะฝะพ ะบ ัะตะผ ะบะพะผะฟะพะฝะตะฝัะฐะผ, ะบะพัะพััะต ะธั
ะธัะฟะพะปัะทััั
render() {
const {keys, messages, initialized, locked} = this.props.background.state;
const {lock, unlock, addKey, removeKey, initVault, deleteVault, approve, reject} = this.props.background;
return <Fragment>
{!initialized
?
<Init onInit={initVault}/>
:
locked
?
<Unlock onUnlock={unlock}/>
:
messages.length > 0
?
<Sign keys={keys} message={messages[messages.length - 1]} onApprove={approve} onReject={reject}/>
:
<Keys keys={keys} onAdd={addKey} onRemove={removeKey}/>
}
<div>
{!locked && <button onClick={() => lock()}>Lock App</button>}
{initialized && <button onClick={() => deleteVault()}>Delete all keys and init</button>}
</div>
</Fragment>
}
}
เบญเบปเบเบเบฐเบเบญเบเบเบตเปเบเบฑเบเปเบซเบผเบทเบญเบชเบฒเบกเบฒเบเปเบเบดเปเบเปเบเปเปเบเบฅเบฐเบซเบฑเบ
เปเบเบเบฑเบเบเบธเบเบฑเบเบขเบนเปเปเบเบซเปเบญเบเบฎเบฝเบเบเปเบฒเบฎเปเบญเบเบชเบฐเบซเบกเบฑเบ, เบเปเบฒเบเบเปเบฒเปเบเบฑเบเบเปเบญเบเปเบเปเปเบฅเบทเบญเบเบฅเบฑเบเบชเปเบฒเบฅเบฑเบ UI เปเบฅเบฐเปเบเปเบ UI เปเบกเบทเปเบญเบกเบฑเบเบเปเบฝเบเปเบเบ. เปเบเบทเปเบญเปเบฎเบฑเบเบชเบดเปเบเบเบตเป, เปเบซเปเปเบเบตเปเบกเบงเบดเบเบตเบเบฒเบ getState
ะธ reaction
เปเบ remote.updateState
:
import {action, observable, reaction} from 'mobx';
import uuid from 'uuid/v4';
import {signTx} from '@waves/waves-transactions'
import {setupDnode} from "./utils/setupDnode";
import {decrypt, encrypt} from "./utils/cryptoUtils";
export class SignerApp {
...
// public
getState() {
return {
keys: this.store.keys,
messages: this.store.newMessages,
initialized: this.store.initialized,
locked: this.store.locked
}
}
...
//
connectPopup(connectionStream) {
const api = this.popupApi();
const dnode = setupDnode(connectionStream, api);
dnode.once('remote', (remote) => {
// ะกะพะทะดะฐะตะผ reaction ะฝะฐ ะธะทะผะตะฝะตะฝะธั ััะตะนัะฐ, ะบะพัะพััะน ัะดะตะปะฐะตั ะฒัะทะพะฒะตั ัะดะฐะปะตะฝะฝั ะฟัะพัะตะดััั ะธ ะพะฑะฝะพะฒะธั ััะตะนั ะฒ ui ะฟัะพัะตััะต
const updateStateReaction = reaction(
() => this.getState(),
(state) => remote.updateState(state),
// ะขัะตััะธะผ ะฐัะณัะผะตะฝัะพะผ ะผะพะถะฝะพ ะฟะตัะตะดะฐะฒะฐัั ะฟะฐัะฐะผะตััั. fireImmediatly ะทะฝะฐัะธั ััะพ reaction ะฒัะฟะพะปะฝะธัััั ะฟะตัะฒัะน ัะฐะท ััะฐะทั.
// ะญัะพ ะฝะตะพะฑั
ะพะดะธะผะพ, ััะพะฑั ะฟะพะปััะธัั ะฝะฐัะฐะปัะฝะพะต ัะพััะพัะฝะธะต. Delay ะฟะพะทะฒะพะปัะตั ัััะฐะฝะพะฒะธัั debounce
{fireImmediately: true, delay: 500}
);
// ะฃะดะฐะปะธะผ ะฟะพะดะฟะธัะบั ะฟัะธ ะพัะบะปััะตะฝะธะธ ะบะปะธะตะฝัะฐ
dnode.once('end', () => updateStateReaction.dispose())
})
}
...
}
เปเบกเบทเปเบญเปเบเปเบฎเบฑเบเบงเบฑเบเบเบธ remote
เบชเปเบฒเบ reaction
เปเบเบทเปเบญเบเปเบฝเบเบชเบฐเบเบฒเบเบฐเบเบตเปเปเบญเบตเปเบเบเบฑเบเบเบฑเบเบขเบนเปเบเปเบฒเบ UI.
เบเบฒเบเบชเปเบฒเบเบฑเบเบชเบธเบเบเปเบฒเบเปเบกเปเบเบเบฒเบเปเบเบตเปเบกเบเบฒเบเบชเบฐเปเบเบเบเปเปเบเบงเบฒเบกเปเบซเบกเปเปเบเปเบญเบเบญเบเบชเปเบงเบเบเบฐเบซเบเบฒเบ:
function setupApp() {
...
// Reaction ะฝะฐ ะฒัััะฐะฒะปะตะฝะธะต ัะตะบััะฐ ะฑะตะดะถะฐ.
reaction(
() => app.store.newMessages.length > 0 ? app.store.newMessages.length.toString() : '',
text => extensionApi.browserAction.setBadgeText({text}),
{fireImmediately: true}
);
...
}
เบเบฑเปเบเบเบฑเปเบ, เบเปเบฒเบฎเปเบญเบเบชเบฐเบซเบกเบฑเบเปเบกเปเบเบเบฝเบกเบเปเบญเบก. เบซเบเปเบฒเปเบงเบฑเบเบญเบฒเบเบเบฐเบฎเปเบญเบเบเปเบฅเบฒเบเปเบเบฑเบเบชเปเบฒเบฅเบฑเบเบเบฒเบเปเบฎเบฑเบเบเบธเบฅเบฐเบเปเบฒ:
เบฅเบฐเบซเบฑเบเปเบกเปเบเบกเบตเบขเบนเปเบเปเบญเบเบเบตเป
เบชเบฐเบซเบฅเบธเบ
เบเปเบฒเบเปเบฒเบเปเบเปเบญเปเบฒเบเบเบปเบเบเบงเบฒเบกเบเบปเบเปเบเบดเบเบเบตเปเบชเบธเบ, เปเบเปเบเบฑเบเบกเบตเบเปเบฒเบเบฒเบก, เบเปเบฒเบเบชเบฒเบกเบฒเบเบเบฒเบกเบเบงเบเปเบเบปเบฒเปเบเปเบเบตเป
เปเบฅเบฐเบเปเบฒเบเปเบฒเบเบชเบปเบเปเบเปเบเบดเปเบเบฅเบฐเบซเบฑเบเบชเปเบฒเบฅเบฑเบเบเบฒเบเบเบฐเบซเบเบฒเบเบเบปเบงเบเบดเบ, เบเปเบฒเบเบชเบฒเบกเบฒเบเบเบญเบเบซเบฒเบเบตเป
เบฅเบฐเบซเบฑเบ, repository เปเบฅเบฐเบฅเบฒเบเบฅเบฐเบญเบฝเบเบงเบฝเบเปเบฎเบฑเบเบเบฒเบเบเปเบฒเบเบฒเบ
เปเบซเบผเปเบเบเปเปเบกเบนเบ: www.habr.com