整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          前端教程:JavaScript中如何操作元素內(nèi)容?

          作元素樣式有兩種方式,一種是操作style屬性,一種是操作className屬性,下面我們分別進(jìn)行講解。

          1. 操作style屬性

          除了前面講解的元素內(nèi)容和屬性外,對(duì)于元素對(duì)象的樣式,可以直接通過“元素對(duì)象.style.樣式屬性名”的方式操作。樣式屬性名對(duì)應(yīng)CSS樣式名,但需要去掉CSS樣式名里的半字線“-”,并將半字線后面的英文的首字母大寫。例如,設(shè)置字體大小的樣式名font-size,對(duì)應(yīng)的樣式屬性名為fontSize。

          為了便于讀者的學(xué)習(xí)使用,下面我們通過表1列出常用style屬性中CSS樣式名稱的書寫及說明。

          表1 常見的style屬性操作的樣式名

          名稱

          說明

          background

          設(shè)置或返回元素的背景屬性

          backgroundColor

          設(shè)置或返回元素的背景色

          display

          設(shè)置或返回元素的顯示類型

          fontSize

          設(shè)置或返回元素的字體大小

          height

          設(shè)置或返回元素的高度

          left

          設(shè)置或返回定位元素的左部位置

          listStyleType

          設(shè)置或返回列表項(xiàng)標(biāo)記的類型

          overflow

          設(shè)置或返回如何處理呈現(xiàn)在元素框外面的內(nèi)容

          textAlign

          設(shè)置或返回文本的水平對(duì)齊方式

          textDecoration

          設(shè)置或返回文本的修飾

          textIndent

          設(shè)置或返回文本第一行的縮進(jìn)

          transform

          向元素應(yīng)用2D或3D轉(zhuǎn)換

          接下來,通過代碼演示如何對(duì)元素的樣式進(jìn)行添加,具體示例如下。

             <div id="box"></div>
          
             <script>
          
              var ele = document.querySelector('#box'); // 獲取元素對(duì)象
          
              ele.style.width = '100px';
          
              ele.style.height = '100px';
          
              ele.style.transform = 'rotate(7deg)';
          
             </script>

          上述第4~6行代碼用于為獲取的ele元素對(duì)象添加樣式,其效果相當(dāng)于在CSS中添加以下樣式。

          #box {width: 100px; height: 100px; transform: rotate(7deg);}

          2. 操作className屬性

          在開發(fā)中,如果樣式修改較多,可以采取操作類名的方式更改元素樣式,語法為“元素對(duì)象.className”。訪問className屬性的值表示獲取元素的類名,為className屬性賦值表示更改元素類名。如果元素有多個(gè)類名,在className中以空格分隔。

          接下來,通過代碼演示如何使用className更改元素的樣式。

          (1)編寫html結(jié)構(gòu)代碼,具體示例如下。

             <style>
          
              div {
          
               width: 100px;
          
               height: 100px;
          
               background-color: pink;
          
              } 
          
             </style>
          
             <body>
          
              <div class="first">文本</div>
          
            </body>

          上述代碼中,第9行給div元素添加first類,并在style中設(shè)置了first的樣式,瀏覽器預(yù)覽效果如圖1所示。

          圖1 初始效果

          (2)單擊div元素更改元素的樣式,示例代碼如下。

             <script>
          
              var test = document.querySelector('div');
          
              test.onclick = function () {
          
               this.className = 'change';
          
              };
          
             </script>

          上述代碼中,第2行獲取div元素存儲(chǔ)在test對(duì)象中。第3~5行為text對(duì)象添加onclick單擊事件,第4行執(zhí)行事件處理程序使用this.className給test對(duì)象設(shè)置change類名,其中this指的是test對(duì)象。

          (3)在style中添加change類,樣式代碼如下。

             .change {
          
              background-color: purple;
          
              color: #fff;
          
              font-size: 25px;
          
              margin-top: 100px;
          
             }

          (4)單擊div盒子,瀏覽器預(yù)覽效果如圖2所示。

          圖2 單擊后效果

          執(zhí)行上述代碼之后,會(huì)直接把原先的類名first修改為change,如果想要保留原先的類名,可以采取多類名選擇器的方式,修改第(2)步的第4行代碼,示例代碼如下。

          this.className = 'first change';

          修改之后,在控制臺(tái)查看到div元素的類已經(jīng)修改成了<div class="first change">文本</div>,保留了之前的類名。

          在進(jìn)行項(xiàng)目開發(fā)的時(shí)候,可能會(huì)遇到“想找某個(gè)色值”的場(chǎng)景,因?yàn)轭伾狄话闶菙?shù)字類型,沒有語義,不好全局搜索。所以就希望有一個(gè)工具,能夠快速展示工作目錄下的所有顏色,方便取值。

          由于是在編寫代碼階段,所以這個(gè)工具最方便的其實(shí)是編輯器的插件。我使用VSCode插件,所以先來試試開發(fā)一個(gè)VSCode插件。

          在正式開發(fā)之前,先來給這個(gè)插件取個(gè)好名字,color, see, 一想到這兩個(gè)單詞,我腦海里直接蹦出了“給你點(diǎn)顏色看看”的中文式表達(dá)”give you color to see”,看起來Color to See 是個(gè)好名字。

          需求分析

          在開發(fā)之前,先來想想我們的”產(chǎn)品“長什么樣子,要實(shí)現(xiàn)什么功能。

          插件要實(shí)現(xiàn)的目標(biāo)是能夠快速找到某個(gè)色值,所以我們要將顏色可視化,色值是最終我們希望能拿到的,所以需要將項(xiàng)目中所有的顏色收集起來,統(tǒng)一展示(包含可視化、色值)。

          具體功能清單如下:

          • 收集工作區(qū)下的所有顏色
            • 文件更新(刪除、新建、文本更新)了能夠獲取到最新的顏色
          • 可視化所有顏色塊
            • 以網(wǎng)格狀的形式展示顏色色塊
          • 色值展示和獲取
            • 點(diǎn)擊顏色展示對(duì)應(yīng)的色值,支持Hex和RGB格式
            • 點(diǎn)擊色值一鍵復(fù)制

          UI設(shè)計(jì)如下:

          功能實(shí)現(xiàn)

          VSCode WebView

          WebView可以用來創(chuàng)建自定義的視圖,可以看成是VSCode內(nèi)部的 Iframe 容器,可以渲染任何HTML, 通過消息傳遞實(shí)現(xiàn)通信,所以具備非常強(qiáng)大的頁面渲染和交互能力。

          VSCode內(nèi)部提供了三個(gè)API可以創(chuàng)建WebView:

          window.createWebviewPanel

          創(chuàng)建一個(gè)編輯器面板。即時(shí)的,關(guān)閉編輯器就銷毀了,主要用于那些不需要持久化狀態(tài)(在關(guān)閉后不需要恢復(fù))的場(chǎng)景,示例代碼如下:

          const panel = vscode.window.createWebviewPanel(
              'webviewId', // Webview 的標(biāo)識(shí)符
              'My Webview', // 面板標(biāo)題
              vscode.ViewColumn.One, // 面板顯示在哪個(gè)編輯器列中
              { enableScripts: true } // 額外的 Webview 選項(xiàng)
          );
          
          panel.webview.html = "<html><body><h1>Hello, Webview!</h1></body></html>";
          

          window.registerWebviewPanelSerializer

          定義如何序列化和反序列化 WebView 面板的狀態(tài),這樣即使在 VSCode 重啟后,這個(gè) WebView 面板的狀態(tài)也可以被恢復(fù)。所以這種WebView,對(duì)于那些需要保留用戶狀態(tài)或信息的面板來說非常有用。

          class MyWebviewPanelSerializer implements vscode.WebviewPanelSerializer {
              async deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, state: any) {
                  // 重新設(shè)置 Webview 的內(nèi)容等
                  webviewPanel.webview.html = "<html><body><h1>Restored Webview</h1></body></html>";
              }
          }
          
          context.subscriptions.push(vscode.window.registerWebviewPanelSerializer('webviewId', new MyWebviewPanelSerializer()));
          

          window.registerWebviewViewProvider

          創(chuàng)建一個(gè)編輯器面板。持久的視圖,常駐在側(cè)邊欄(Sidebar)和面板(Panel)上

          class MyWebviewProvider implements vscode.WebviewViewProvider {
              resolveWebviewView(webviewView: vscode.WebviewView) {
                  webviewView.webview.html = "<html><body><h1>Hello, Sidebar Webview!</h1></body></html>";
              }
          }
          
          const provider = new MyWebviewProvider();
          context.subscriptions.push(vscode.window.registerWebviewViewProvider('myWebview', provider));
          

          考慮我們插件的使用場(chǎng)景一般是沒有UI稿的后臺(tái)開發(fā),使用次數(shù)不會(huì)很頻繁,所以插件的視圖不需要持久,所以可以選擇用createWebviewPanel創(chuàng)建一個(gè)編輯器面板來承載界面。

          項(xiàng)目初始化

          通過官方文檔給的腳手架命令,開始初始化我們的插件項(xiàng)目。目錄結(jié)構(gòu)如下:

          ├─ .eslintrc.json
          ├─ .gitignore
          ├─ .vscode
          │  ├─ extensions.json
          │  ├─ launch.json
          │  ├─ settings.json
          │  └─ tasks.json
          ├─ .vscode-test.mjs
          ├─ .vscodeignore
          ├─ .yarnrc
          ├─ CHANGELOG.md
          ├─ README.md
          ├─ package.json
          ├─ src
          │  ├─ extension.ts
          │  ├─ test
          │  │  └─ extension.test.ts
          ├─ tsconfig.json
          ├─ vsc-extension-quickstart.md
          ├─ webpack.config.js
          ├─ yarn-error.log
          └─ yarn.lock
          

          package.json文件中,可以看到main配置是dist文件夾下的extension.js文件,這是我們項(xiàng)目的入口文件,是Webpack把src/extentions.ts作為入口文件編譯過來的

          package.jsonscript配置,發(fā)現(xiàn)可以運(yùn)行yarn watch命令進(jìn)行熱更新,也就是實(shí)時(shí)把改動(dòng)的代碼編譯成可執(zhí)行的JS文件。

          運(yùn)行完yarn watch后,通過按“F5”或者“運(yùn)行->啟動(dòng)調(diào)試”,運(yùn)行我們的插件,這時(shí)候VSCode會(huì)打開一個(gè)新的窗口,快捷鍵ctrl + shift + p輸入hello world

          最后,點(diǎn)擊執(zhí)行這個(gè)命令,可以看到右下角彈出了歡迎標(biāo)語。如下圖所示:

          如果修改了代碼,可以F5刷新debug窗口,也可以在debug窗口上使用快捷鍵command + R刷新,和在瀏覽器刷新網(wǎng)頁一樣。

          以上就是 VSCode 插件開發(fā)起步的過程。

          接入WebView

          新增colorToSee命令

          package.json文件的commands屬性上新增extension.colorToSee,這個(gè)命令是使用我們插件的起點(diǎn),所以務(wù)必寫個(gè)好title,為了讓插件國際化,我的title設(shè)置為"ColorToSee: Show colors of the working directory in a webview panel"

           "commands": [
              {
                "command": "extension.colorToSee",
                "title": "ColorToSee: Show colors of the working directory in a webview panel"
              }
            ],
          

          這個(gè)命令的執(zhí)行回調(diào)函數(shù)在extension.ts文件上,重點(diǎn)代碼如下:

           vscode.commands.registerCommand(COMMAND_NAME, () => {
            registerWebviewViewProvider(context);
          })
          

          在這串代碼中,我們把具體的實(shí)現(xiàn)邏輯封裝在registerWebviewViewProvider方法上,接下來我們可以只關(guān)注這個(gè)方法的實(shí)現(xiàn)。

          創(chuàng)建WebView來顯示自定義UI

          使用WebView API createWebviewPanel來創(chuàng)建一個(gè)自定義的HTML頁面,基本的代碼塊如下所示:

          const panel = vscode.window.createWebviewPanel(
            CatCodiconsPanel.viewType,
            "Cat Codicons",
            column || vscode.ViewColumn.One
           );
          
          panel.webview.html = _getHtmlForWebview(panel.webview, extensionUri);
          
          function _getHtmlForWebview(webview: vscode.Webview) {
              // Use a nonce to only allow specific scripts to be run
              const nonce = getNonce();
          
              return `<!DOCTYPE html>
                      <html lang="en">
                      <head>
                          <meta charset="UTF-8">
                          <!-- Use a content security policy to only allow loading images from https or from our extension directory, and only allow scripts that have a specific nonce. -->
                          <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; script-src 'nonce-${nonce}';">
                          <meta name="viewport" content="width=device-width, initial-scale=1.0">
                          <title>Your View</title>
                      </head>
                      <body>
                          <h1>Hello from Your View!</h1>
                      </body>
                      </html>`;
          }
          
          function getNonce() {
              let text = '';
              const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
              for (let i = 0; i < 32; i++) {
                  text += possible.charAt(Math.floor(Math.random() * possible.length));
              }
              return text;
          }
          

          registerWebviewViewProvider方法中,我們將基于上述代碼做些改造,大致的框架如下:

          const registerWebviewViewProvider = (context: vscode.ExtensionContext) => {
            const provider = new ViewProvider(context.extensionUri, config);
          
            const panel = vscode.window.createWebviewPanel(
              ViewProvider.viewType, // Webview 的標(biāo)識(shí)符
              PANEL_TITLE, // 面板標(biāo)題
              vscode.ViewColumn.One, // 面板顯示在哪個(gè)編輯器列中
              {
                enableScripts: true
              } // 額外的 Webview 選項(xiàng)
            );
          
            provider.resolveWebviewView(panel as unknown as vscode.WebviewView);
          
            context.subscriptions.push(panel);
          };
          

          在這段代碼中,我們把頁面信息維護(hù)在ViewProvider這個(gè)類上。在這個(gè)類上,我們要實(shí)現(xiàn)頁面的渲染和更新。

          頁面渲染

          頁面的渲染機(jī)制可以借用React 框架的 render(state) 思想,這個(gè)思想基于函數(shù)式編程的原則,其核心是將UI視為狀態(tài)的函數(shù),UI的每一次更新可以看作是一個(gè)狀態(tài)轉(zhuǎn)換的結(jié)果。

          本插件的頁面構(gòu)成比較簡單,為了方便,可以直接使用原生JS和HTML開發(fā)。頁面的模塊主要分為以下4個(gè)部分:

          • 刷新按鈕
          • 顏色網(wǎng)格
          • 顏色值展示
          • toast彈窗提醒(消息提醒)

          整個(gè)數(shù)據(jù)驅(qū)動(dòng)式的頁面渲染如下所示:

          <body>
            ${generateMainDiv(this.colorInfos)}
          </body>
          

          定義狀態(tài)

          首先,我們定義colorInfos狀態(tài),這個(gè)變量存儲(chǔ)了當(dāng)前工作區(qū)所有顏色信息,包括該顏色的所在文件的位置和色值,TS定義如下所示:

          export type ColorItem = {
            /** 顏色值起始位置 */
            start: number;
            /** 顏色值結(jié)束位置 */
            end: number;
            /** 顏色值 */
            color: string;
            /** 顏色值所在的文件路徑 */
            file: string;
          };
          

          渲染函數(shù)

          ”顏色網(wǎng)格“是我們整個(gè)頁面主要的功能模塊,所以我們重點(diǎn)介紹這個(gè)功能的渲染函數(shù),根據(jù)colorInfos,一個(gè)能根據(jù)這個(gè)狀態(tài)生成HTML的函數(shù)可以簡化成這樣:

          function generateMainDiv(colors) {
            return colors.map(info => `<div style="color: ${info.color}" data-colorItem="${encodeURIComponent(
              JSON.stringify(item)
            )}">${info.color}</div>`).join('');
          }
          

          其中自定義屬性data-colorItem存儲(chǔ)了整個(gè)顏色塊的所有信息。

          頁面掛載

          html元素賦值給WebViewhtml屬性,就可以實(shí)現(xiàn)頁面的掛載。

          this._view.webview.html = this._getHtmlForWebview(this._view.webview);
          
          function _getHtmlForWebview(webview: vscode.Webview) {
              // Use a nonce to only allow specific scripts to be run
              const nonce = getNonce();
          
              return `<!DOCTYPE html>
                      <html lang="en">
                      <head>
                          <meta charset="UTF-8">
                          <!-- Use a content security policy to only allow loading images from https or from our extension directory, and only allow scripts that have a specific nonce. -->
                          <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; script-src 'nonce-${nonce}';">
                          <meta name="viewport" content="width=device-width, initial-scale=1.0">
                          <title>Your View</title>
                      </head>
                      <body>
                          ${generateMainDiv(this.colorInfos)}
                      </body>
                      </html>`;
          }
          

          如何構(gòu)造狀態(tài)colorInfos?

          上面描述的數(shù)據(jù)驅(qū)動(dòng)式框架比較簡單,還沒有涉及到本項(xiàng)目的難點(diǎn)實(shí)現(xiàn),如果構(gòu)造colorInfos數(shù)據(jù),是本項(xiàng)目的難點(diǎn)之一,本節(jié)主要講這塊內(nèi)容。

          顏色格式分析

          在項(xiàng)目開發(fā)中,比較常見的顏色格式分為RGB, Hex, HSL和顏色關(guān)鍵字(Named Colors)這幾種。下面我們來分析這幾種顏色在CSS中的語法。

          從MDN官網(wǎng)發(fā)現(xiàn)rgb()的寫法分為絕對(duì)和相對(duì)格式,我們平時(shí)熟悉的寫法是這樣的:rgb(x, y, z),其中x、y、z是從0到255的整數(shù),屬于絕對(duì)格式的一種。

          為了讓項(xiàng)目能盡快先落地,我們此處只分析“絕對(duì)格式”的場(chǎng)景。絕對(duì)格式的寫法如下:

          rgb(R G B[ / A])
          

          其中R G B的值可以為0-255的整數(shù),或者0%-100%的百分比;A的值為0到1(或者0%-100%),表示顏色的透明度信息。如果有A值時(shí),需要使用 /

          我們經(jīng)常見到的一種寫法是R G B通過逗號(hào)隔開,這種寫法其實(shí)是一種過時(shí)的寫法,但是考慮到大部分人都在用,匹配CSS RGB顏色的時(shí)候也應(yīng)該考慮到。rgba()語法也是一種過時(shí)的寫法,同樣本次也會(huì)考慮這種顏色格式的匹配。

          CSS hsl() 的語法和rgb()是一致的,這里不在贅述,需要請(qǐng)移至MDN官網(wǎng)查看。

          16進(jìn)制Hex的色值寫法分為以下幾種:

          #RGB        // The three-value syntax
          #RGBA       // The four-value syntax
          #RRGGBB     // The six-value syntax
          #RRGGBBAA   // The eight-value syntax
          

          其中R G B的值為0到ff。

          在CSS中,一些常用顏色可以使用預(yù)定義的關(guān)鍵字來表示,例:redbluegreen等,這些就是顏色關(guān)鍵詞。完整的標(biāo)準(zhǔn)關(guān)鍵詞可以在MDN官網(wǎng)找到。

          顏色匹配

          了解了顏色在項(xiàng)目中的多種寫法,下面考慮如何匹配代碼中的顏色。常見的方案分為兩種:

          • 基于文本模式的正則匹配
          • 基于代碼結(jié)構(gòu)的抽象語法樹(AST)分析

          前者實(shí)現(xiàn)比較簡單且通用性大,因此本插件選擇通過正則語法,針對(duì)不同的顏色格式,定制不同的匹配策略。

          編寫一個(gè)基本的正則表達(dá)式來匹配顏色代碼其實(shí)不難,但是如果想提高要求,確保寫出來的正則表達(dá)式既準(zhǔn)確又具有良好的防御性,能夠考慮到各種邊緣情況以及性能優(yōu)化,這并不容易。

          為了實(shí)現(xiàn)這一點(diǎn),此處我借鑒了另外一個(gè)插件Color Highlight的實(shí)現(xiàn)。這個(gè)插件是我目前在使用的比較好用的插件,在開發(fā)自己的插件之前,我從沒想過它是如何實(shí)現(xiàn)的,但是我熟悉它的功能:在編寫代碼的時(shí)候,可以高亮顯示當(dāng)前當(dāng)前編輯器中的顏色格式。要實(shí)現(xiàn)的功能和我的有相似之處,所以了解這個(gè)插件的實(shí)現(xiàn)應(yīng)該對(duì)于完成自己的插件很有幫助。

          事實(shí)確實(shí)是這樣的,Color Highlight插件給出了一系列針對(duì)不同顏色格式的匹配策略,如下圖所示。

          基于它的實(shí)現(xiàn),下面分析了針對(duì)不同顏色格式的正則表達(dá)式解析。

          RGB和HSL

          const colorRegex = /((rgb|hsl)a?(\s*[\d]*.?[\d]+%?\s*(?<commaOrSpace>\s|,)\s*[\d]*.?[\d]+%?\s*\k<commaOrSpace>\s*[\d]*.?[\d]+%?(\s*(\k<commaOrSpace>|/)\s*[\d]*.?[\d]+%?)?\s*))/gi;
          

          整個(gè)正則表達(dá)式是一個(gè)全局不區(qū)分大小寫的匹配(由結(jié)尾的 gi 標(biāo)志指定),用于在文本中查找所有匹配的顏色值。主要分為兩部分:格式名稱(值),其中格式名稱分為三種情況,如下圖所示,分別為rgb,hsl,rgba, hsla。后半部分主要是匹配它們的不同書寫方式,包含數(shù)字值、逗號(hào)或空格分割。具體解析如下:

          1. ((rgb|hsl)a?: 這部分使用捕獲組來匹配 rgbhsl 字符串,后面跟著一個(gè)可選的 a 字符,這樣可以匹配 rgbrgbahslhsla

          2. (:匹配左括號(hào)
          3. **\s*[\d]*.?[\d]+%?\s***: 用來匹配顏色值的第一個(gè)參數(shù),(如紅色值 R、色相值 H ),首先是匹配零個(gè)或多個(gè)空格符,然后是一個(gè)數(shù)字(可能是整數(shù)或小數(shù)),該數(shù)字可能后跟一個(gè)百分號(hào) %

          1. (?<commaOrSpace>\s|,): 定義了一個(gè)命名捕獲組 commaOrSpace,用來匹配空格或逗號(hào)。這是顏色值參數(shù)之間的分隔符。
          2. **\s*[\d]*.?[\d]+%?\s***: 用來匹配顏色值的第二個(gè)參數(shù),(如紅色值 R、色相值 H ),首先是匹配零個(gè)或多個(gè)空格符,然后是一個(gè)數(shù)字(可能是整數(shù)或小數(shù)),該數(shù)字可能后跟一個(gè)百分號(hào) %

          1. \k<commaOrSpace>: 對(duì)前面定義的 commaOrSpace 捕獲組的引用,確保分隔符的一致性。
          2. \s*[\d]*.?[\d]+%?: 再次匹配一個(gè)數(shù)字(整數(shù)或小數(shù)),可能后跟一個(gè)百分號(hào) %。這部分用于匹配顏色值的第三個(gè)參數(shù)。

          1. **\s*(\k<commaOrSpace>|/)\s*[\d]*.?[\d]+%?)?\s*: 這是一個(gè)可選的捕獲組,用于匹配顏色透明度值(alpha 值)。它首先匹配空格,然后是前面定義的分隔符(commaOrSpace)或斜杠 /,后面跟著空格和數(shù)字(整數(shù)或小數(shù)),數(shù)字后面可能有一個(gè)百分號(hào) %**。
          2. ):最后匹配右括號(hào) )

          顏色關(guān)鍵詞

          收集MDN官網(wǎng)給出的顏色關(guān)鍵字,寫一個(gè)簡單的正則,下面是列舉了部分色值的正則匹配:

          (aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|purple|red|silver|teal|white|yellow)
          

          16進(jìn)制Hex

          const colorHex =
            /.?((?:#|\b0x)([a-f0-9]{6}([a-f0-9]{2})?|[a-f0-9]{3}([a-f0-9]{1})?))\b/gi;
          

          這個(gè)正則表達(dá)式用來匹配 CSS 中的十六進(jìn)制顏色值,同時(shí)也匹配十六進(jìn)制顏色值的簡寫形式,并且考慮到可能的透明度值(RGBA格式)。下面逐部分解釋這個(gè)正則表達(dá)式:

          1. .?: 匹配任意單個(gè)字符,但不捕獲它。
          2. ((?:#|\b0x): 這是一個(gè)非捕獲組(由?:開頭),用于匹配 #0x。這里的 \b 是一個(gè)單詞邊界,確保 0x 前面是一個(gè)單詞邊界,避免中間匹配到不正確的字符串。

          1. ([a-f0-9]{6}([a-f0-9]{2})?|[a-f0-9]{3}([a-f0-9]{1})?): 這部分有兩大選項(xiàng),分別通過 | 分隔:
          2. [a-f0-9]{6}([a-f0-9]{2})?: 匹配6個(gè)十六進(jìn)制字符,后面可選地跟隨2個(gè)十六進(jìn)制字符(通常用于表示透明度)。
          3. [a-f0-9]{3}([a-f0-9]{1})?: 匹配3個(gè)十六進(jìn)制字符,后面可選地跟隨1個(gè)十六進(jìn)制字符。這是十六進(jìn)制顏色的簡寫形式
          4. \b: 單詞邊界,確保匹配的字符串在一個(gè)單詞的邊界結(jié)束,防止部分匹配到更長的字符串中。

          顏色獲取

          了解了不同顏色格式的匹配規(guī)則后,顏色的獲取思路其實(shí)很簡單:遍歷每個(gè)文件,獲取每個(gè)文件的文本字符串,通過對(duì)應(yīng)的正則匹配策略去匹配。基本的代碼如下所示:

          策略維護(hù)

          從產(chǎn)品的使用場(chǎng)景看來,本插件所關(guān)注的顏色格式比較簡單:一些基本的rgb, hex, hsl,單詞的顏色可以不考慮,因?yàn)橛貌簧希苡蒙衔叶伎梢灾苯悠ヅ淞耍虼宋覀冎魂P(guān)注沒有語義、且常用的顏色格式。

          最終,針對(duì)本插件,可以總結(jié)出下面三種策略:

          • findColorFunctionsInText: 查找rgb(a), hsl(a)基本顏色
          • findHexRGBA:查找hex顏色
          this.strategies = [findColorFunctionsInText,findHexRGBA];
          

          文件遍歷

          for (const file of files) {
            try {
              const document = await vscode.workspace.openTextDocument(file);
              const instance = await this.findOrCreateInstance(document);
          
              colorsInfos.push(await instance.getColorInfo());
            } catch {
              continue;
            }
          }
          

          顏色獲取getColorInfo

           getColorInfo(document = this.document) {
              const text = this.document.getText();
              const version = this.document.version.toString();
          
              const file = this.document.uri.fsPath; // file path
              const result = await Promise.all(this.strategies.map((fn) => fn(text)));
             return resolveResult(result); // 顏色解析,根據(jù)colorInfos的數(shù)據(jù)格式進(jìn)行數(shù)據(jù)解析
          }
          

          頁面更新

          顏色的更新場(chǎng)景主要有以下幾種情況:

          • 編輯已有文件的顏色
          • 新增有顏色的文件
          • 刪除了有顏色的文件

          針對(duì)這三種情況,在產(chǎn)品設(shè)計(jì)上,可以設(shè)置一個(gè)“刷新按鈕“按鈕,點(diǎn)擊該按鈕,拉取最新的顏色信息。本章節(jié)主要從消息通信和更新機(jī)制兩個(gè)角度討論本插件的實(shí)現(xiàn)。

          消息通信

          在頁面上點(diǎn)擊按鈕,然后執(zhí)行對(duì)應(yīng)的事件,這點(diǎn)的實(shí)現(xiàn)涉及到VSCode插件中ExtensionWebview 的通信,主要是通過postMessageonDidReceiveMessage實(shí)現(xiàn)消息的發(fā)送與接收。

          在Webview發(fā)送消息

          Extension 里通過vscode.postMessage發(fā)送消息:

          // 刷新
          refreshBtn.addEventListener('click', () => {
            // 如果已經(jīng)在Loading, 無需發(fā)送message
            if (refreshBtn.classList.contains('btn--loading')) {
              return;
            }
          
            refreshBtn.classList.add('btn--loading');
            vscode.postMessage({ command: 'refresh' });
          });
          

          在Extension接收消息

          webviewView.webview.onDidReceiveMessage((message) => {
            switch (message.command) {
              case 'refresh':
                const prom = () => this.doUpdateWebView()
          
                prom().finally(() => {
                  webviewView.webview.postMessage({
                    command: 'refreshEnd'
                  });
                });
          
                break;
            }
          });
          

          更新機(jī)制

          Extension接受到需要更新的消息后,需要執(zhí)行對(duì)應(yīng)的更新機(jī)制,這個(gè)實(shí)現(xiàn)主要封裝在doUpdateWebView方法中。更新機(jī)制主要實(shí)現(xiàn)的就是更新colorInfos,并重新執(zhí)行渲染函數(shù)以更新UI。

          為了讓插件的第一個(gè)版本快速上線,文件新增、刪除場(chǎng)景的更新我們可以重新掃描一下所有文件;文件編輯的場(chǎng)景容易定位具體文檔,所以可以按需更新。

          下面給出了本項(xiàng)目的更新實(shí)現(xiàn),其中整個(gè)了頁面初始化的渲染,因?yàn)檫@個(gè)場(chǎng)景的邏輯和文件新增、刪除重復(fù)。

          private async doUpdateWebView() {
            try {
              if (
                this.type === 'init' ||
                this.type === 'add' ||
                this.type === 'delete'
              ) {
                await this.initDataView();
          
                return Promise.resolve();
              }
          
              // 顏色變更:text change
          
              // 收集變更的document,局部更新顏色視圖
              for (let index = 0; index < this.instanceMap.length; index++) {
                const instance = this.instanceMap[index];
          
                // 如果頁面更改了
                if (instance.changed) {
                  const colorDocumentItem = await instance.getColorInfo();
                  this.colorMapArray[index] = colorDocumentItem;
                  // 恢復(fù)
                  instance.changed = false;
                }
              }
          
              // 更新顏色信息
              this.colorInfos = updateColorInfosByMap(this.colorMapArray);
          
              // 更新視圖
              this._view.webview.html = this._getHtmlForWebview(this._view.webview);
          
              return Promise.resolve();
            } catch {
              return Promise.reject();
            }
          }
          

          代碼示意圖如下所示:

          插件配置

          并不是項(xiàng)目中所有文件都有顏色值,所以為了讓插件能夠有效地找到有用的色值,我們需要給插件增加文件類型配置項(xiàng),用來定義哪些文件應(yīng)該在文件掃描過程中包含或排除。

          includeexclude 這兩個(gè)配置項(xiàng)在很多前端開發(fā)相關(guān)的工具和配置文件中非常常見,如 webpack.config文件、tsconfig文件、babel.config文件。為了降低插件使用者的學(xué)習(xí)曲線,本插件也選擇使用 includeexclude 來指定掃描的文件類型。

          下面是本插件默認(rèn)的配置屬性。

          "color-to-see.findFilesRules": {
            "default": {
              "include": [
                "**/*.js",
                "**/*.jsx",
                "**/*.tsx",
                "**/*.css",
                "**/*.less",
                "**/*.sass",
                "**/*.html",
                "**/*.vue"
              ],
              "exclude": [
                "**/node_modules/**",
                "**/dist/**",
                ".git"
              ]
            },
          

          掃描的文件類型主要關(guān)注實(shí)際開發(fā)中包含色值定義的文件類型,比如樣式文件、HTML文件或者JS文件。排除不需要掃描或處理的文件夾,如node_modulesdist.git文件等。includeexclude的值使用 Glob Pattern 語法來指定哪些文件被包括或排除。

          在Extension中,這套配置的使用原理如下:

          config = vscode.workspace.getConfiguration(EXTENSION_NAME);
          
          const findFilesUsingConfig = async (config: Config) => {
            const { include, exclude } = config.findFilesRules;
            const includePattern = `{${include.join(',')}}`;
            const excludePattern = `{${exclude.join(',')}}`;
          
            try {
              const files = await vscode.workspace.findFiles(
                includePattern,
                excludePattern
              );
              return files;
            } catch {
              return [];
            }
          };
          

          成品

          Demo Snapshop

          • 有顏色

          • 無顏色

          插件市場(chǎng)鏈接

          :Color to See - Visual Studio Marketplace

          局限性分析和優(yōu)化點(diǎn)

          盡管插件的功能大致已經(jīng)實(shí)現(xiàn)了,但是本插件還是有一些局限性,以及后續(xù)可以優(yōu)化的地方

          文件掃描效率

          局限性

          插件需要掃描整個(gè)工作區(qū)的所有文件來查找顏色值,盡管增加了文件類型限制少掃描一些文件,但是剩下的文件掃描還是會(huì)消耗大量的計(jì)算資源。

          優(yōu)化

          • 編譯打包:項(xiàng)目代碼打包到一個(gè)文件(如app.js)中以減少顏色掃描的文件數(shù)量的方法
          • 緩存+增量更新:通過緩存機(jī)制保留未更改的文件,而只對(duì)更改的文件進(jìn)行掃描,而不是整個(gè)工作區(qū)。

          用戶體驗(yàn)

          局限性

          顏色展示必須等待文件全部掃描完,長時(shí)間的掃描和等待會(huì)影響用戶體驗(yàn)

          優(yōu)化

          實(shí)現(xiàn)異步掃描機(jī)制,一旦掃描到新的顏色值,就立即在UI中展示,而不是等待所有掃描完成。

          顏色值跳轉(zhuǎn)功能配置化

          局限性

          顏色值跳轉(zhuǎn)功能的實(shí)用性其實(shí)比較低,而且在本插件中,如果一個(gè)顏色在多個(gè)地方出現(xiàn),我只會(huì)記錄第一個(gè)位置。因此,本插件的使用過程中,用戶應(yīng)該更加關(guān)注于顏色值本身,而不是其在代碼中的具體位置。

          除此之外,顏色跳轉(zhuǎn)到具體某個(gè)文件后,當(dāng)前視圖的位置沒有滾動(dòng)到對(duì)應(yīng)的高度。

          優(yōu)化

          增加一個(gè)配置項(xiàng)讓用戶可以自行決定是否啟用顏色值跳轉(zhuǎn)功能。默認(rèn)關(guān)閉。

          總結(jié)

          本文基于一個(gè)場(chǎng)景問題,介紹了VSCode插件的開發(fā)過程,總的來說,這個(gè)項(xiàng)目算是一次很有意思的獨(dú)立開發(fā)體驗(yàn)。在這個(gè)過程中,可以總結(jié)出以下幾個(gè)比較重要的點(diǎn):

          • 善于發(fā)現(xiàn)問題,并嘗試解決它
          • 積累經(jīng)驗(yàn),關(guān)注細(xì)節(jié),說不定何時(shí)能派上用場(chǎng)
          • Always have fun

          作者:蘭燕平

          來源-微信公眾號(hào):Goodme前端團(tuán)隊(duì)

          出處:https://mp.weixin.qq.com/s/WGV70jVODC_IVmh7dM4Sag

          幾天,我手里的一個(gè)項(xiàng)目需要將富文本的所有 html 標(biāo)簽全部刪除,得到純文本后再存儲(chǔ)到數(shù)據(jù)庫中。在一系列得搜索操作之后,我找到了實(shí)現(xiàn)這個(gè)目的的幾種方法,在這里我分享給大家,當(dāng)你遇到同樣的情況興許也能用的上。

          1. 使用 .replace(/<[^>]*>/g, '')

          這個(gè)方法是從文本中去除 html 標(biāo)簽最簡單的方法。它使用字符串的方法 .replace(待替換的字符串,替換后的字符串) 將 HTML 標(biāo)簽替換成空值。 /g 是表示替換字符串所有匹配的值,即字符串中所有符合條件的字符都將被替換。

          這個(gè)方法的缺點(diǎn)是有些 HTML 標(biāo)簽不能被剔除,不過它依然很好用。

          2. 創(chuàng)建臨時(shí)DOM元素并獲取其中的文本

          這種方法是完成該問題的最有效的方法。創(chuàng)建一個(gè)臨時(shí) DOM 并給他賦值,然后我們使用 DOM 對(duì)象方法提取文本。

          3. 使用 html-to-text npm 包

          html-to-text 這個(gè)包的功能很全了,轉(zhuǎn)換也有許多的選項(xiàng)比如:wordwrap, tags, whitespaceCharacters , formatters 等等。

          安裝:

          npm install html-to-text

          使用:

          最后感謝閱讀,如果此文對(duì)您有幫助,請(qǐng)點(diǎn)贊或添加關(guān)注。


          主站蜘蛛池模板: 亚洲av色香蕉一区二区三区蜜桃| 亚洲av区一区二区三| 国产无线乱码一区二三区 | 国产av成人一区二区三区| 久久亚洲日韩精品一区二区三区 | 亚洲av无码天堂一区二区三区 | 亚洲综合一区二区精品导航 | 精品无人区一区二区三区在线| 国产一区二区在线视频| 午夜影视日本亚洲欧洲精品一区| 国产亚洲无线码一区二区| 亚洲中文字幕丝袜制服一区| 国产91精品一区二区麻豆亚洲| 亚洲熟女少妇一区二区| 亚洲AV无码一区东京热久久| 国产激情一区二区三区| 国产乱人伦精品一区二区在线观看| 国产一区二区三区免费视频| 一区二区视频免费观看| 国产日韩AV免费无码一区二区| 国产日韩一区二区三区在线播放| 亚洲综合无码一区二区痴汉| 精品人妻少妇一区二区| 色屁屁一区二区三区视频国产| 99久久综合狠狠综合久久一区| 欧美日韩精品一区二区在线观看| 久久婷婷色一区二区三区| 中文字幕不卡一区| 中文字幕精品亚洲无线码一区应用| 一本久久精品一区二区| 国产91一区二区在线播放不卡 | 国内偷窥一区二区三区视频| 好吊视频一区二区三区| 精品国产精品久久一区免费式| 国产SUV精品一区二区四| 在线精品亚洲一区二区三区| 四虎永久在线精品免费一区二区| 一区国产传媒国产精品| 视频一区二区三区免费观看| 国产精品一区二区香蕉| 大香伊人久久精品一区二区|