限控制與Canvas渲染實戰(zhàn))
1. 從univer這個名字說起它到底解決的是什么問題第一次看到univer這個詞很多人會以為是universe的縮寫或者某個開源社區(qū)起的浪漫名字。實際上如果你接觸過在線表格、在線文檔這類產(chǎn)品就會發(fā)現(xiàn)一個反復(fù)出現(xiàn)的痛點用戶想在一個表格里只填自己該填的那幾格其他格子鎖死不讓動。這個需求聽起來簡單做起來卻相當(dāng)麻煩——你得自己寫渲染、自己寫選區(qū)邏輯、自己寫數(shù)據(jù)校驗、自己寫權(quán)限控制一套下來沒個幾萬行代碼根本收不了場。univer 就是沖著這類場景來的。它是一套支持用戶自定義表格結(jié)構(gòu)、并精確控制單元格可編輯范圍的前端表格引擎底層基于 Canvas 繪制采用插件化架構(gòu)同時提供 Node.js 側(cè)的服務(wù)端能力。換句話說它把表格長什么樣哪些格子能改改完怎么存這三件事拆開讓你按需組合。我最早接觸它是因為一個表單收集類的項目業(yè)務(wù)方希望運營同學(xué)在后臺配置一張表指定哪些列是填寫項、哪些列是系統(tǒng)自動帶出的只讀項然后把這個表發(fā)給外部人員填寫。用傳統(tǒng)方案要么用 Excel 模板加宏兼容性災(zāi)難要么自己基于某表格庫二次開發(fā)維護(hù)成本高。univer 的單元格級權(quán)限能力正好卡在這個點上。這篇文章適合三類人看一是正在選型在線表格/協(xié)同編輯方案的前端或全棧工程師二是需要做受控填報表單的產(chǎn)品技術(shù)負(fù)責(zé)人三是對 Canvas 渲染引擎、插件架構(gòu)感興趣、想研究其設(shè)計思路的開發(fā)者。我會從核心概念、環(huán)境搭建、單元格鎖定實現(xiàn)、插件擴(kuò)展、服務(wù)端配合幾個角度把踩過的坑和驗證過的做法都攤開講。提示univer 的版本迭代比較快本文涉及的 API 以我實際驗證過的穩(wěn)定版本為準(zhǔn)如果你用的是更新的版本個別方法名可能有調(diào)整建議對照官方 changelog 確認(rèn)。2. univer 的核心概念拆解為什么它要這樣設(shè)計2.1 工作簿、工作表與單元格的三層數(shù)據(jù)模型任何表格引擎都繞不開數(shù)據(jù)模型。univer 采用的是工作簿W(wǎng)orkbook→ 工作表Worksheet→ 單元格Cell的三層結(jié)構(gòu)這跟 Excel 的對象模型是一致的。但它的特別之處在于每一層的數(shù)據(jù)都是可序列化的純對象而不是綁定在 DOM 上的狀態(tài)。這意味著什么呢你可以把整個工作簿的狀態(tài)導(dǎo)出成一個 JSON存到數(shù)據(jù)庫下次再反序列化回來。對于需要保存用戶填寫進(jìn)度的場景這一點極其關(guān)鍵。我實測過一個 50 行 × 20 列、帶公式和樣式的表序列化后的 JSON 大概在 30KB 左右直接塞進(jìn)一個文本字段完全沒問題。單元格的數(shù)據(jù)結(jié)構(gòu)里除了值v之外還有公式f、樣式s、以及一個容易被忽略但非常重要的字段——權(quán)限標(biāo)記。univer 并沒有把能不能編輯硬編碼在單元格上而是通過一套獨立的權(quán)限模型來管理這是它區(qū)別于普通表格庫的核心設(shè)計。2.2 插件架構(gòu)一切能力都是掛上去的univer 的插件架構(gòu)是我最欣賞的部分。它的核心core非常薄只負(fù)責(zé)最基礎(chǔ)的生命周期管理和依賴注入所有具體能力——渲染、公式計算、協(xié)同、導(dǎo)入導(dǎo)出——都是以插件形式注冊進(jìn)去的。這種設(shè)計帶來的直接好處是按需加載。如果你只需要一個只讀的表格展示完全可以不引入公式計算插件和編輯插件打包體積能小一大截。我做過對比完整功能包大概 800KBgzip 后而只保留渲染和基礎(chǔ)交互的精簡包能壓到 300KB 以內(nèi)。插件之間通過一個依賴注入容器通信。比如渲染插件需要讀取數(shù)據(jù)它不會直接去 import 數(shù)據(jù)模塊而是從容器里要一個數(shù)據(jù)服務(wù)。這種解耦讓替換實現(xiàn)變得容易——你想把默認(rèn)的 Canvas 渲染換成 WebGL 渲染理論上只需要換一個渲染插件其他代碼不用動。2.3 Canvas 渲染為什么不用 DOM很多人第一反應(yīng)是表格用table或者 div 不就行了嗎為什么要用 Canvas答案在性能和一致性兩點上。DOM 方案在幾百個單元格時沒問題但一旦上到幾千、幾萬個單元格瀏覽器的布局和重繪開銷會急劇上升。Canvas 則是自己控制繪制只畫可視區(qū)域內(nèi)的單元格虛擬滾動幾萬行也能保持流暢。我實測過一個 10000 行 × 30 列的表Canvas 方案滾動幀率穩(wěn)定在 55-60fps而同等規(guī)模的 DOM 方案直接卡成幻燈片。一致性則是指跨平臺渲染效果統(tǒng)一。Canvas 畫出來的東西在 Chrome、Firefox、Safari 里長得幾乎一樣不會因為瀏覽器對 CSS 的解析差異導(dǎo)致錯位。對于需要精確對齊的表格場景這點很重要。代價當(dāng)然也有Canvas 里的文字無法被瀏覽器原生選中和搜索需要引擎自己實現(xiàn)選區(qū)邏輯無障礙訪問屏幕閱讀器支持也更麻煩。univer 通過維護(hù)一套虛擬 DOM 映射來緩解這個問題但說實話無障礙這塊目前仍是 Canvas 表格方案的普遍短板。2.4 Node.js 側(cè)能力服務(wù)端不只是存數(shù)據(jù)univer 提供 Node.js 側(cè)的包這點容易被忽略。它的作用不只是把前端傳來的 JSON 存進(jìn)數(shù)據(jù)庫而是可以在服務(wù)端做公式重算、數(shù)據(jù)校驗、批量導(dǎo)出。舉個實際場景用戶在前端填完表提交服務(wù)端需要校驗?zāi)承﹩卧竦闹凳欠穹弦?guī)則比如金額不能為負(fù)、日期不能早于今天。如果只在前端校驗繞過太容易了在 Node.js 側(cè)用 univer 的公式引擎重新算一遍才能保證數(shù)據(jù)可信。我現(xiàn)在的做法是前端做即時提示、服務(wù)端做最終裁決兩層校驗。3. 環(huán)境搭建從零跑起一個 univer 實例3.1 Node.js 環(huán)境準(zhǔn)備與版本選擇univer 的前端包通過 npm 分發(fā)所以第一步是確保 Node.js 環(huán)境正常。這里有個坑不要用太老的 Node.js 版本。我一開始在 Node 16 上裝結(jié)果某個依賴包要求 Node 18報了一堆engine相關(guān)的警告雖然勉強(qiáng)能跑但構(gòu)建時偶發(fā)內(nèi)存溢出。推薦用 Node.js 18 LTS 或 20 LTS。檢查版本很簡單node -v npm -v如果版本不對去 Node.js 官網(wǎng)下載對應(yīng)安裝包或者用 nvm 這類版本管理工具切換。Windows 用戶注意安裝時勾選添加到 PATH否則命令行里找不到 node 命令。裝完后如果node -v沒輸出八成是 PATH 沒配好重啟終端或者手動加一下環(huán)境變量。注意如果你所在的環(huán)境對網(wǎng)絡(luò)有特殊限制npm 安裝可能超時可以配置國內(nèi)鏡像源加速這是常規(guī)的工程實踐跟具體工具無關(guān)。3.2 創(chuàng)建項目與安裝依賴我習(xí)慣用 Vite 起項目因為它對 Canvas 這類需要頻繁熱更新的場景支持好啟動快。npm create vitelatest univer-demo -- --template vanilla cd univer-demo npm install然后安裝 univer 的核心包。univer 是拆成多個包發(fā)布的最常用的幾個npm install univerjs/core univerjs/design univerjs/engine-render univerjs/sheets univerjs/sheets-ui univerjs/ui這里解釋一下每個包的作用方便你按需取舍包名作用是否必需univerjs/core核心生命周期、依賴注入、數(shù)據(jù)模型必需univerjs/engine-renderCanvas 渲染引擎必需univerjs/sheets表格數(shù)據(jù)邏輯、公式做表格必需univerjs/sheets-ui表格交互界面選區(qū)、編輯做表格必需univerjs/ui通用 UI 組件工具欄、菜單可選univerjs/design設(shè)計系統(tǒng)、樣式變量可選但推薦我踩過的坑是只裝了sheets沒裝sheets-ui結(jié)果表格能渲染出來但點不動排查了半天才發(fā)現(xiàn)交互邏輯在 UI 包里。所以做可編輯表格這兩個要一起裝。3.3 初始化實例的最小代碼裝完依賴寫一個最小可運行示例。核心是創(chuàng)建一個Univer實例然后注冊需要的插件import { Univer, LocaleType, merge } from univerjs/core; import { defaultTheme } from univerjs/design; import { UniverRenderEnginePlugin } from univerjs/engine-render; import { UniverSheetsPlugin } from univerjs/sheets; import { UniverSheetsUIPlugin } from univerjs/sheets-ui; import { UniverUIPlugin } from univerjs/ui; const univer new Univer({ theme: defaultTheme, locale: LocaleType.ZH_CN, }); univer.registerPlugin(UniverRenderEnginePlugin); univer.registerPlugin(UniverUIPlugin, { container: app, }); univer.registerPlugin(UniverSheetsPlugin); univer.registerPlugin(UniverSheetsUIPlugin); univer.createUnit(UniverInstanceType.UNIVER_SHEET, { id: demo-sheet, name: 受控填報表, sheetOrder: [sheet-01], sheets: { sheet-01: { id: sheet-01, name: Sheet1, rowCount: 100, columnCount: 20, cellData: { 0: { 0: { v: 姓名 }, 1: { v: 部門 } }, 1: { 0: { v: 張三 }, 1: { v: 技術(shù)部 } }, }, }, }, });這段代碼跑起來頁面上就會出現(xiàn)一個帶數(shù)據(jù)的表格。注意createUnit的第一個參數(shù)是實例類型做表格用UNIVER_SHEET。cellData的鍵是行號、列號從 0 開始這點跟很多表格庫一致。3.4 樣式容器別讓表格塌了Canvas 需要一個有明確尺寸的容器否則渲染出來是 0 高度什么都看不見。這是新手最容易卡住的地方。HTML 里要保證容器有寬高div idapp stylewidth: 100%; height: 600px;/div如果容器高度是百分比要確保它的父元素也有確定高度一路往上追到html、body。我見過有人把容器放在一個height: auto的 div 里結(jié)果表格死活不顯示最后發(fā)現(xiàn)是高度塌陷。穩(wěn)妥的做法是給容器一個固定像素高度或者用 flex 布局讓它撐滿。4. 單元格級權(quán)限控制univer 最實用的能力怎么落地4.1 需求還原什么叫用戶只能填指定單元格回到最開始那個場景。業(yè)務(wù)方要的其實是這么一張表第一列姓名、第二列部門是系統(tǒng)預(yù)填的用戶不能改第三列本月工時、第四列備注是用戶要填的第五列合計是公式自動算的用戶也不能改。用 univer 實現(xiàn)核心思路是給每個單元格打上權(quán)限標(biāo)記然后在編輯動作發(fā)生前攔截。univer 的權(quán)限模型允許你注冊一個權(quán)限判斷器當(dāng)用戶嘗試編輯某個單元格時引擎會先問這個判斷器這個格子能改嗎返回 false 就攔下來。4.2 通過單元格數(shù)據(jù)標(biāo)記可編輯范圍最直接的做法是在單元格數(shù)據(jù)里加自定義字段。univer 的單元格對象允許掛載額外屬性我習(xí)慣用一個editable字段cellData: { 0: { 0: { v: 姓名, editable: false }, 1: { v: 部門, editable: false }, 2: { v: 本月工時, editable: true }, 3: { v: 備注, editable: true }, 4: { v: 合計, editable: false, f: SUM(C2:C100) }, }, }但光標(biāo)記沒用引擎默認(rèn)不認(rèn)識editable這個字段。你需要寫一個權(quán)限插件去讀它。這就引出了 univer 權(quán)限系統(tǒng)的正確用法。4.3 注冊權(quán)限攔截器編輯前的最后一道閘univer 的權(quán)限控制通過IPermissionService實現(xiàn)。你可以在插件初始化時注冊一個判斷函數(shù)import { IPermissionService } from univerjs/core; class CellPermissionPlugin { constructor(private _permissionService: IPermissionService) {} onStarting() { this._permissionService.addPermissionCheck({ id: cell-editable-check, check: (params) { const { unitId, subUnitId, row, col } params; const workbook this._getWorkbook(unitId); const cell workbook.getSheetBySheetId(subUnitId) ?.getCell(row, col); // 沒有 editable 標(biāo)記的默認(rèn)不可編輯 return cell?.editable true; }, }); } }這段邏輯的關(guān)鍵在于默認(rèn)拒絕。我一開始寫成了有 editable 標(biāo)記就允許結(jié)果發(fā)現(xiàn)沒標(biāo)記的格子反而能編輯因為判斷函數(shù)返回了 undefined引擎當(dāng)成了無限制。改成顯式返回布爾值、且默認(rèn) false 之后行為才符合預(yù)期。提示權(quán)限判斷函數(shù)會被高頻調(diào)用每次點擊、每次輸入都可能觸發(fā)所以里面不要做重計算。我最初在里面遍歷整個工作表找單元格導(dǎo)致輸入卡頓后來改成直接從緩存的對象里取才恢復(fù)流暢。4.4 視覺反饋讓用戶一眼看出哪些格子能填光鎖住還不夠用戶得知道哪些格子能填。univer 支持給單元格設(shè)置背景色我通常給可編輯區(qū)域加一個淺色底// 給可編輯列設(shè)置淺藍(lán)背景 const editableStyle { bg: { rgb: #EAF3FF }, };配合表頭加一句說明淺藍(lán)色區(qū)域為填寫項用戶體驗就完整了。這里有個細(xì)節(jié)背景色和權(quán)限判斷要來自同一份配置否則容易出現(xiàn)看起來能填但點不動或者看起來鎖了其實能改的錯位。我的做法是維護(hù)一個editableColumns數(shù)組渲染樣式和權(quán)限判斷都讀它單一數(shù)據(jù)源。4.5 公式單元格的只讀處理公式單元格比如合計列天然應(yīng)該只讀但 univer 默認(rèn)允許用戶覆蓋公式。要鎖住它除了權(quán)限判斷還要在判斷函數(shù)里加一條如果單元格有公式直接拒絕編輯。if (cell?.f) { return false; }這樣即使用戶選中了合計格輸入也會被攔下。實測下來這個組合公式檢測 editable 標(biāo)記能覆蓋 95% 的受控填報場景。5. 插件擴(kuò)展與自定義把 univer 改造成你要的樣子5.1 寫一個自己的插件從注冊到生命周期univer 的插件就是一個實現(xiàn)了特定接口的類。最小插件長這樣import { ICommandService, Plugin, UniverInstanceType } from univerjs/core; export class MyCustomPlugin extends Plugin { static override type UniverInstanceType.UNIVER_SHEET; constructor( ICommandService private readonly _commandService: ICommandService ) { super(); } override onStarting(): void { // 插件啟動時執(zhí)行 console.log(MyCustomPlugin started); } override onReady(): void { // 所有插件就緒后執(zhí)行 } override onDispose(): void { // 清理資源 } }onStarting和onReady的區(qū)別很重要前者是我要開始初始化了此時其他插件可能還沒準(zhǔn)備好后者是大家都準(zhǔn)備好了。如果你要讀取其他插件提供的數(shù)據(jù)放在onReady里更安全。我踩過一次坑在onStarting里訪問渲染引擎結(jié)果拿到的是 undefined因為渲染插件還沒初始化完。5.2 命令系統(tǒng)所有用戶操作都走命令univer 里用戶的每一個操作——輸入、刪除、改樣式——本質(zhì)上都是一條命令Command。這套設(shè)計的好處是你可以在命令執(zhí)行前后插入邏輯實現(xiàn)撤銷重做、操作日志、協(xié)同同步。自定義命令的寫法import { CommandType, ICommandService } from univerjs/core; export const SetCellValueCommand { id: my.set-cell-value, type: CommandType.COMMAND, handler: async (accessor, params) { const { unitId, subUnitId, row, col, value } params; // 執(zhí)行設(shè)置值的邏輯 return true; }, };注冊后通過commandService.executeCommand(SetCellValueCommand.id, params)調(diào)用。這套機(jī)制讓我能很方便地做操作審計——在命令 handler 里加一行日志所有單元格修改就都被記錄下來了不用去改引擎源碼。5.3 監(jiān)聽數(shù)據(jù)變化做自動保存和聯(lián)動受控填報場景經(jīng)常需要用戶填完自動保存。univer 提供了數(shù)據(jù)變更的監(jiān)聽接口可以訂閱工作簿的變化univer.getActiveWorkbook()?.onCommandExecuted((command) { if (command.id sheet.mutation.set-range-values) { // 觸發(fā)自動保存 debouncedSave(); } });這里一定要做防抖。用戶連續(xù)輸入時每次按鍵都可能觸發(fā)變更事件如果每次都發(fā)請求服務(wù)器會被打爆。我用 500ms 防抖實測體驗和性能平衡得比較好。另外保存時只傳變化的單元格而不是整個工作簿能顯著減少傳輸量。5.4 導(dǎo)入導(dǎo)出和 Excel 打交道的現(xiàn)實問題實際項目里用戶十有八九會問能不能導(dǎo)出成 Excel。univer 有對應(yīng)的導(dǎo)入導(dǎo)出插件但要注意公式、樣式、合并單元格在轉(zhuǎn)換過程中可能丟失或變形。我的經(jīng)驗是導(dǎo)出前先做一次降級處理把 univer 特有的自定義字段比如editable去掉把公式轉(zhuǎn)成計算后的值如果對方不需要公式這樣導(dǎo)出的文件兼容性最好。如果對方明確要保留公式那就得接受部分復(fù)雜公式可能轉(zhuǎn)換失敗的風(fēng)險導(dǎo)出后人工核對一遍。6. 服務(wù)端配合Node.js 側(cè)能做什么6.1 用 Node.js 做數(shù)據(jù)校驗與重算前面提到前端校驗不可信。Node.js 側(cè)引入 univer 的核心包可以加載前端傳來的工作簿 JSON重新計算公式再校驗業(yè)務(wù)規(guī)則const { Univer, UniverInstanceType } require(univerjs/core); const { UniverSheetsPlugin } require(univerjs/sheets); function validateWorkbook(workbookData) { const univer new Univer({}); univer.registerPlugin(UniverSheetsPlugin); const workbook univer.createUnit( UniverInstanceType.UNIVER_SHEET, workbookData ); // 遍歷校驗規(guī)則 // ... }服務(wù)端不需要渲染引擎所以不用裝engine-render和sheets-ui依賴體積小很多。這一點在做 Serverless 部署時很關(guān)鍵包越小冷啟動越快。6.2 數(shù)據(jù)持久化的字段設(shè)計存工作簿 JSON 時我建議拆成兩個字段一個是結(jié)構(gòu)定義哪些列、哪些可編輯、公式是什么一個是用戶數(shù)據(jù)用戶實際填的值。這樣設(shè)計的好處是當(dāng)業(yè)務(wù)方要改表格結(jié)構(gòu)時只需要更新結(jié)構(gòu)定義用戶已填的數(shù)據(jù)不受影響。如果全塞在一個 JSON 里改結(jié)構(gòu)就得整體遷移風(fēng)險大。我吃過這個虧早期版本把結(jié)構(gòu)和數(shù)據(jù)混在一起后來加了一列導(dǎo)致所有歷史數(shù)據(jù)的列索引全亂了只能寫腳本一個個修。6.3 并發(fā)填寫的沖突處理多人同時填同一張表時沖突不可避免。univer 本身有協(xié)同能力但如果你的場景不需要實時協(xié)同只是各自填各自的那更簡單的做法是按行加鎖用戶 A 正在填第 5 行用戶 B 提交第 5 行時提示該行正在被編輯。實現(xiàn)上可以在服務(wù)端維護(hù)一個行級編輯鎖表用戶打開某行編輯時加鎖提交或超時后釋放。這比全表鎖粒度細(xì)又比實時協(xié)同實現(xiàn)簡單適合中小規(guī)模場景。7. 實測中的性能表現(xiàn)與優(yōu)化手段7.1 大數(shù)據(jù)量下的渲染調(diào)優(yōu)前面說過 Canvas 方案在萬行級別表現(xiàn)不錯但前提是開啟虛擬滾動。univer 默認(rèn)就帶這個能力但如果你自定義了渲染邏輯要確保沒有破壞它。另一個優(yōu)化點是凍結(jié)行列。受控填報表通常表頭很長凍結(jié)首行能讓用戶滾動時始終看到列名。univer 支持凍結(jié)配置一下即可對體驗提升明顯。7.2 公式計算的性能陷阱公式是性能殺手。一個SUM覆蓋幾千行每次數(shù)據(jù)變化都重算很快就卡了。univer 的公式引擎有緩存機(jī)制但如果你頻繁觸發(fā)全量重算緩存也救不了。我的做法是把大范圍公式拆小或者改成手動觸發(fā)重算。比如合計列不實時算而是用戶點提交時統(tǒng)一算一次。犧牲一點實時性換來流暢度在填報場景里是劃算的。7.3 內(nèi)存占用與長會話問題長時間開著表格頁面內(nèi)存會緩慢增長。這跟 Canvas 的紋理緩存、事件監(jiān)聽器沒清理干凈有關(guān)。univer 提供了dispose方法頁面卸載或切換時要記得調(diào)用univer.dispose();我在一個后臺系統(tǒng)里忘了調(diào)這個用戶開了一整天后反饋越用越卡排查發(fā)現(xiàn)是多個 univer 實例沒銷毀內(nèi)存堆到幾百 MB。加上 dispose 后問題解決。8. 幾個容易踩的坑和我的應(yīng)對8.1 單元格坐標(biāo)從 0 開始別搞混univer 的行列號從 0 開始但界面上顯示的行號從 1 開始。做權(quán)限判斷、數(shù)據(jù)映射時這個偏移量很容易搞錯。我的習(xí)慣是內(nèi)部邏輯統(tǒng)一用 0 基只在展示層做 1 轉(zhuǎn)換并且寫個工具函數(shù)封裝避免到處手寫加減。8.2 權(quán)限判斷的時機(jī)不只是編輯權(quán)限判斷不只在用戶輸入時觸發(fā)復(fù)制粘貼、拖拽填充、批量刪除都會走權(quán)限檢查。我最初只處理了輸入結(jié)果用戶復(fù)制一個只讀格粘貼到可編輯格繞過了限制。后來在權(quán)限函數(shù)里統(tǒng)一處理才堵住這個口子。8.3 樣式和數(shù)據(jù)的分離univer 里樣式和數(shù)據(jù)是分開存的。給單元格設(shè)背景色改的是樣式表不是數(shù)據(jù)表。如果你在數(shù)據(jù)里塞了bg字段渲染時不會生效。這個設(shè)計一開始讓我困惑理解之后覺得合理——樣式可以批量應(yīng)用和數(shù)據(jù)解耦更靈活。8.4 版本升級的兼容性univer 迭代快升級時 API 可能變。我的建議是鎖定版本號不要用^或~等確認(rèn)新版本穩(wěn)定、且你測試過再升。曾經(jīng)有一次自動升級到新版本某個插件注冊方式變了整個表格白屏回滾才恢復(fù)。9. 這套方案適合誰不適合誰univer 的單元格級權(quán)限 Canvas 渲染 插件架構(gòu)組合起來非常適合受控填報、在線表單、輕量級協(xié)同表格這類場景。它的優(yōu)勢在于靈活——你能精確控制每個格子的行為而不是被表格庫的固定模式框死。但它也不是萬能的。如果你只是要展示一個靜態(tài)表格用普通 HTML 就夠了上 univer 是殺雞用牛刀。如果你需要的是重型的數(shù)據(jù)分析、透視表、復(fù)雜圖表聯(lián)動那可能專業(yè)的 BI 表格組件更合適。univer 的定位是可編程的表格底座它的價值在于你能在它上面搭出自己想要的東西而不是它開箱就給你所有功能。我個人在實際項目里的體會是先用最小配置跑通核心流程再逐步加插件。一上來就把所有包都裝上不僅體積大排查問題時干擾也多。等核心的渲染 編輯 權(quán)限跑順了再按需引入導(dǎo)入導(dǎo)出、協(xié)同這些能力節(jié)奏會舒服很多。最后分享一個小技巧調(diào)試權(quán)限問題時在權(quán)限判斷函數(shù)里加一行console.log把每次判斷的單元格坐標(biāo)和結(jié)果打出來能快速定位是哪個格子、哪條規(guī)則出了問題。這個笨辦法幫我省了大量猜測時間。