11无监听程序_腾讯开心鼠英语 小程序实践与总结
騰訊開心鼠英語 團隊中有很多小程序的項目,且后續還會很多小程序的開發和迭代規劃,因此我們團隊是小程序的重度使用者。在小程序的開發中,團隊積累了一些技術和經驗,也遇到了一些困難和挑戰,還踩了很多坑,因此有必要將我們團隊的小程序實踐進行總結和分享。
騰訊開心鼠英語一、工程化探索
微信小程序的開發規范里,有一些工程方面的要求,例如可以通過項目的配置文件來設置根目錄,每個頁面或組件需要wxml、js、json、wxss 4個文件組成等,于此同時微信的開發者工具可以幫忙初始化項目,并設置好目錄結構。除此之外對于項目工程方面的支持就比較薄弱了。為了提升團隊的開發效率和質量,我們還需要在已有的基礎之上進行一些列的優化。我們希望小程序的開發腳手架至少具備以下的能力:
因為現階段的小程序開發的工程需求主要集中在文件編譯和資源整理上,小程序開發者工具會幫我們處理文件打包,因此我們考慮使用gulp去搭建工程腳手架。
1. 目錄結構設計
微信小程序的代碼主要由4個放置在同一目錄下的文件構成:
微信開發者工具會對以上的文件進行監聽,當其中任意一個文件發生改變時,開發者工具就會刷新預覽。
如果使用ts進行開發,那在同一目錄下,還將多出ts文件;如果參考這種方式引入css的預編譯語言,那還會再多出一個待編譯的樣式文件。這樣一個頁面或組件的目錄下,就至少會存在6個文件,顯得非常臃腫,不僅降低了文件查找的效率,還有可能帶來其他的誤操作。因此這樣的目錄結構就不足以支撐我們后續開發的工程化要求。
我們希望將源文件和編譯文件分離,只保留基本的4種類型文件,當源文件發生改變時,就觸發編譯,將其編譯成對應的開發者工具可以監聽的文件類型。因此設計了以下新的目錄結構:
目錄結構src 目錄下存放項目的源文件,使用gulp監聽文件的變化,并觸發對應的編譯任務,將源文件編譯為目標文件,或者拷貝不需要編譯的文件到目標目錄(dist文件夾),然后在project.config.json里指定小程序的根目錄為dist文件夾,這樣開發者工具就會去監聽dist目錄里的文件變化并更新預覽了。通過這樣的設計,整個目錄結構更加清晰,開發者只需要關注src目錄即可。
當然在實踐的過程中,這樣的目錄結構也存在一定的問題:
以上的問題還需要我們團隊在后續的開發中去解決和優化。
2. css預處理語言支持
微信小程序的樣式代碼主要是編寫在wxss文件中,其語法和css是一樣的,只有少量的css規則不適用。如果只是編寫css樣式,那只寫wxss是完全沒問題的。但是現在市面上還是有很多流行的css預處理語言可以幫助更好地開發css樣式,提供了例如mixins、function、變量等功能。因此我們希望編寫css預處理語言,并將其編譯成wxss文件。
我們團隊使用的是postcss。源文件編寫的是css文件,通過設置gulp task的方式,將css編譯成wxss。并通過postcss插件的方式,集成更多的其他功能。
在實踐過程中,我們發現當項目規模開始變大時,在有些機器上,修改一次樣式文件后觸發更新的速度很慢,這個時候我們就引入cache去加速css的編譯。第一次編譯時會將所有的css編譯,而后只會去編譯修改過的文件。代碼如下:
/** * 將 css 編譯成 wxss */const cssCompile = () => src([`${mpDir}/**/*.css`, `!${mpDir}/**/_*.css`]) .pipe(cache('css-compile')) .pipe( // 防止編譯中斷 postcss().on('error', () => { this.emit('end'); }) ) // 去掉編譯出來的 :root{} .pipe(replace(/:root\s\{[^}]*\}?\s*/, '')) .pipe( rename((path) => { path.extname = '.wxss'; }) ) .pipe(dest((file) => file.base));3. typescript支持
原先的微信小程序對ts文件的支持,是通過預置編譯腳本,使用 tsc 去編譯ts文件的。我們的項目引入了gulp之后,對ts的支持就是通過設置gulp task,使用gulp-typescript 這個插件去編譯ts文件,同時還需要使用gulp-sourcemaps這個插件去寫入sourcemap。
在實踐過程中,我們發現有的時候ts的文件編譯比較慢,這個時候可以使用gulp-typescript提供的增量編譯的功能。開啟增量編譯之后,第一次編譯時的速度是一樣的,而之后的編譯速度就會提升約50%。代碼如下:
const tsProject = ts.createProject("tsconfig.json");// 編譯 tsconst tsCompile = () => gulp .src(tsPath) .pipe(sourcemaps.init()) .pipe( /* 增量編譯 */ tsProject() ) .js.pipe(sourcemaps.write()) .pipe(gulp.dest(dist));4. 更好的npm包支持
微信小程序是支持使用npm包的,但是這個支持是有一些前提條件的。例如當引入某個包時:
import { abcRequest } from '@tencent/abcmouse-sdk-mp-tools';小程序會去根目錄下的miniprogram_npm這個文件夾下查找有沒有@tencent/abcemouse-sdk-mp-tools這個包,如果沒有則會提示找不到對應的包。而miniprogram_npm又是根據package.json里的dependencies字段里聲明的依賴構建而成的。因此微信小程序要使用npm包的前提總結如下:
因此要想使用npm包,整個過程是比較波折的。好在小程序官方有提供對應的ci構建可以幫助我們。
但在實踐過程中發現,調用ci.packNpmManually這個接口構建出來的miniprogram_npm目錄,不僅包含了dependencies里的依賴,還包含了其他的依賴,而miniprogram_npm這個目錄里的代碼在上傳小程序代碼時也是會上傳的,引入其他多余的依賴會增大小程序的包體積,在小程序嚴格的代碼大小要求下,這是不可取的。因此還需要對構建的包進行篩選。其大致流程如下:
流程使用gulp監聽package.json文件,當安裝新的npm包,并指定 --save 時,package.json文件會發生變化,并觸發對應的gulp task。在gulp task里去遍歷package.json的denpendcies字段,并從頂層目錄的node_modules里拷貝對應的npm包放入dist目錄的node_modules中。最后再通過ci.packNpmManually方法去構建,這個時候構建出來的miniprogram_npm目錄里就只有必須的npm包了。
通過這種方式,我們需要使用新的npm包時,就只需要npm install并在代碼中import就可以了,其他的處理過程對開發者來說都是無感知的。相應的gulp task代碼如下:
/** * 在小程序根目錄下生成package.json文件用于構建miniprogram_npm * @param {Array} deps denpendencies對象 */const generateSubPkg = (deps) => writeJsonFile(subPkgPath, { dependencies: deps }) .then(() => deps);/** * 獲取必要的npm包目錄路徑 * @param {Array} deps 依賴數組 */const getDepsModule = (deps) => Object.keys(deps).map((key) => `node_modules/${key}`);/** * 構建miniprogram_npm * @param {Array} modules npm包路徑數組 */const packNpmManually = (modules) => { const packPath = `${mpDir}/miniprogram_npm`; const subNpmPath = `${mpDir}/node_modules`; fsx.emptyDirSync(packPath); fsx.emptyDirSync(subNpmPath); modules.forEach((modulePath) => { fsx.copySync(modulePath, `${mpDir}/${modulePath}`); }); return ci.packNpmManually({ packageJsonPath: path.resolve(process.cwd(), subPkgPath), miniprogramNpmDistDir: path.resolve(process.cwd(), mpDir), });};/** * 構建miniprogram_npm gulp plugin */const packPkgManually = () => through.obj(function (chunk, enc, cb) { const filepath = path.resolve(process.cwd(), 'package.json'); if (!fsx.pathExists(filepath)) { cb(null, chunk); } const pkgData = fsx.readJSONSync(filepath); const dependencies = pkgData.dependencies || {}; // denpendencies 沒有發生變動則不需要構建 if (isEqual(cached, dependencies) || packing) { cb(null, chunk); } const spinner = ora('開始構建npm包...').start(); packing = true; generateSubPkg(dependencies) .then(getDepsModule) .then(packNpmManually) .then((result) => { cached = dependencies; spinner.succeed('構建成功,構建結果:'); packing = false; console.log(result); cb(null, chunk); }) .catch((err) => { spinner.fail('構建失敗'); packing = false; console.error(err); }); });const pkgPack = () => src(pkgPath).pipe(packPkgManually());const pkgWatch = () => { watch(pkgPath, pkgPack);};5. 代碼校驗
我們團隊還接入了imweb團隊的eslint和stylelint規則去做代碼校驗,這里就不具體暫開講了,感興趣的可以參考:
6. 后續展望
其實在小程序的工程化這一塊還有很多工作可以做,例如:
...
我們團隊會繼續在小程序工程化方面進行探索和嘗試,如果有新的成果我們也會及時分享。
二、性能優化
因為微信小程序是運行在微信app里的,所以其運行環境是比較苛刻的,因此要想使小程序流暢地運行,提供良好的用戶體驗,對其進行性能優化就至關重要。
對于小程序優化來說,一些傳統的前端優化方案也適用于小程序。而其雙線程的設計模式,又和傳統前端的單線程有所不同,因此也有一些新的優化點,下面主要從幾個大的方面歸納總結:
1. 加快網絡請求
1.1 減小代碼包大小
小程序在冷啟動時,會首先下載對應的代碼包,然后解壓執行代碼,所以減小包大小可以加快代碼下載和解壓的速度。一些方法可以減小代碼包的大小:
- 代碼復用,盡量將可以復用的代碼提取封裝做出npm包或通用函數;
- 剔除無用的樣式,可以使用類似 purifycss的庫將無用的css樣式剔除以減小樣式代碼的大小;
- 剔除無用的函數,可以嘗試引入tree-shaking剔除無用的代碼;
- 靜態資源走cdn,盡量不要將靜態資源打包到代碼中;
- 分包,可以將一些和首頁渲染無關的代碼分發到子包中從而加快主包的下載和執行。
1.2 預請求
- 數據預拉取:小程序提供了一個接口,可以讓用戶在進入到小程序之前就去請求接口,當然這個方法有一定的限制,需要深入調研和謹慎使用;
- 分包預下載:可以配置進入某個頁面時下載可能會用到的分包,可以有效避免進入某些頁面”白屏“時間過長。
1.3 緩存
沒有什么請求比不請求更快的了,合理利用緩存可以有效減少網絡請求的數量,加快整體的加載速度。對于一些實時性不高的數據,我們可以利用微信提供的緩存能力,將一些數據存儲在本地,從而避免一些網絡請求。
1.4 圖片優化
- webp格式圖片;
- 圖片剪裁和降質;
- 圖片懶加載和雪碧圖;
- 漸進式加載大圖資源:在不得不使用大圖資源的場景下,可以適當使用“體驗換速度”的措施來提升渲染性能。小程序會把已加載的靜態資源緩存在本地,因此,對于大圖資源,我們可以先呈現高度壓縮的模糊圖片,同時利用一個隱藏的節點來加載原圖,待原圖加載完成后再轉移到真實節點上渲染。整個流程,從視覺上會感知到圖片從模糊到高清的過程,但與對首屏渲染的提升效果相比,這點體驗落差是可以接受的。
2. 加快頁面渲染
其實小程序的頁面渲染的優化思路和傳統前端的優化思路是一致的,主要思想是:關鍵渲染路徑渲染;
關鍵渲染路徑(Critical Rendering Path)是指完成屏幕渲染的過程中必須發生的事件。
我們可以分析頁面上哪些部分是主要模塊,哪些部分是次要模塊,例如一些提示性的組件,我們可以稍后渲染。
于此同時,還可以在主要模塊中還可以優先渲染主屏模塊,不在主屏內的模塊可以延遲加載或者滾動加載。
3. 提升渲染性能
提升渲染性能可以有效減少交互時的卡頓,讓用戶在交互的時候體驗”如絲般流暢“。小程序采用的雙線程模型,即渲染和邏輯分散在兩個不同的線程中。所以在小程序的環境下,加快用戶響應主要從以下3點出發:
可以采用的辦法有:
...
4. 內存優化
因為小程序畢竟是一個程序(微信)中的程序,可提供給其運行的環境資源是十分有限的,這對于小程序的設計和開發來說就比較苛刻了,因此要在實現的時候小心翼翼。可以從以下幾個方面注意內存的優化:
5. 其他優化
還可以采用一些其他的方式來對小程序進行優化:
最后總結輸出一下小程序性能優化相關的腦圖如下:
腦圖三、自動化探索
1. 自動化測試
小程序官方提供了自動化地工具去模擬小程序的操作,搭配常用的測試框架,可以很容易地實現小程序的前端測試。小程序的模擬器提供了4個級別的操作api:
我們可以利用這幾個api對象去模擬小程序的行為,例如模擬元素點擊、頁面數據修改、頁面跳轉等操作。具體api可以參考:小程序自動化工具 [ 3 ]。
在實踐接入的過程中遇到了一些坑。要想使用小程序模擬器是有一些前提條件的,首先是要開啟開發者工具安全設置中的 CLI/HTTP 調用功能。并通過以下代碼開啟調用:
automator.launch({ cliPath: 'path/to/cli', // 工具 cli 位置 projectPath: 'path/to/project', // 項目文件地址})其中 cliPath 是我們的開發者工具的文件路徑,如果沒有更改過默認安裝的位置則可以忽略。projectPath是項目目錄的地址,這里的項目目錄指的是project.config.json里指定的目錄,而且這個路徑必須是絕對路徑才可以調用成功。
其次小程序的模擬器是有一些使用限制的,它不能調用和操作微信系統的原生組件的,例如授權、位置、支付等功能。當我們希望利用腳本去測試整個頁面的流程時,當涉及到授權和支付等操作時,往往就跑不下去,因此小程序的模擬器更適合去做一些關鍵操作的測試,例如測試某些操作之后,頁面的樣式、行為是否符合預期。
2. CI接入
小程序的代碼打包、上傳等功能可以交由CI來操作。我們團隊主要使用藍盾CI,配合藍盾上的小程序插件,可以很輕松地接入,其流程比較簡單,主要是拉取代碼、安裝依賴和構建上傳。藍盾流水線如下:
藍盾流水線小程序代碼上傳需要用到秘鑰,我們可以將其秘鑰托管在藍盾,即方便又安全。
References
[1]?eslint-config:?https://github.com/imweb/eslint-config-imweb[2]?stylelint-config:?https://github.com/imweb/stylelint-config-imweb[3]?小程序自動化工具:?https://developers.weixin.qq.com/miniprogram/dev/devtools/auto/
總結
以上是生活随笔為你收集整理的11无监听程序_腾讯开心鼠英语 小程序实践与总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 窗口位置按钮取消_梦幻西游:五开玩家都是
- 下一篇: navicat保存查询语句_还在用 Na