<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          自定義 ESLint 規(guī)則,讓代碼持續(xù)美麗

          共 8225字,需瀏覽 17分鐘

           ·

          2020-07-26 19:14

          背景

          一段真實(shí)的代碼發(fā)展歷史

          很久很久以前,有一個(gè)需求,然后產(chǎn)出了一段代碼,代碼優(yōu)雅而簡(jiǎn)潔

          export?const?getConfig?=?(param1,?param2)?=>?{
          ??return?...
          };

          不久又來了個(gè)需求,加個(gè)參數(shù)擴(kuò)展,so easy!

          export?const?getConfig?=?(param1,?param2,?param3)?=>?{
          ??return?...
          };

          經(jīng)過多次產(chǎn)品需求迭代后,現(xiàn)在的代碼b36ba93007169caa2fceaef34eda7ef1.webp

          export?const?getConfig?=?(param1,?param2,?param3,?param4,?param5,?param6,?param7……)?=>?{
          ??return?...
          };

          在產(chǎn)品迭代過程中,上面的 case 一個(gè)函數(shù)的參數(shù)從 2 個(gè)發(fā)展到了 7 個(gè),優(yōu)雅的代碼逐漸變?yōu)椴豢删S護(hù)。這是什么問題?這歸咎于日益增長(zhǎng)的需求,快速響應(yīng)和代碼質(zhì)量之間的矛盾。

          那如何避免呢?

          • 制定代碼規(guī)范

          • 靠開發(fā)同學(xué)的自我修養(yǎng)

          • 進(jìn)行 Code Review

          • 工具提示

          • 發(fā)版控制,不允許發(fā)版

          制定代碼規(guī)范肯定是需要的,那如何約束代碼呢?規(guī)范文檔宣講,再憑借開發(fā)同學(xué)的自我修養(yǎng)?答案是:無(wú)法保證。

          Code Review ?但難免也有落網(wǎng)之魚。發(fā)版控制?能有效解決但是開發(fā)體驗(yàn)不好。

          如果我們?cè)陂_發(fā)者寫代碼的時(shí)候就及時(shí)給到提示和建議,那開發(fā)體驗(yàn)就很棒了,而 ESLint 的自定義規(guī)則就可以實(shí)現(xiàn)在開發(fā)過程中給開發(fā)同學(xué)友好的提示。

          ESLint 原理

          ESLint 是一個(gè)代碼檢查工具,通過靜態(tài)的分析,尋找有問題的模式或者代碼。默認(rèn)使用 Espree (https://github.com/eslint/espree) 解析器將代碼解析為 AST 抽象語(yǔ)法樹,然后再對(duì)代碼進(jìn)行檢查。

          看下最簡(jiǎn)單的一段代碼使用 espree 解析器轉(zhuǎn)換成的抽象語(yǔ)法樹結(jié)構(gòu),此處可以使用 astexplorer (https://astexplorer.net/) 快速方便查看解析成 AST 的結(jié)構(gòu):

          代碼片段:

          var?a?=?1;

          轉(zhuǎn)換出的結(jié)果:

          {
          ??"type":?"Program",
          ??"start":?0,
          ??"end":?10,
          ??"range":?[
          ????0,
          ????10
          ??],
          ??"body":?[
          ????{
          ??????"type":?"VariableDeclaration",
          ??????"start":?0,
          ??????"end":?10,
          ??????"range":?[
          ????????0,
          ????????10
          ??????],
          ??????"declarations":?[
          ????????{
          ??????????"type":?"VariableDeclarator",
          ??????????"start":?4,
          ??????????"end":?9,
          ??????????"range":?[
          ????????????4,
          ????????????9
          ??????????],
          ??????????"id":?{
          ????????????"type":?"Identifier",
          ????????????"start":?4,
          ????????????"end":?5,
          ????????????"range":?[
          ??????????????4,
          ??????????????5
          ????????????],
          ????????????"name":?"a"
          ??????????},
          ??????????"init":?{
          ????????????"type":?"Literal",
          ????????????"start":?8,
          ????????????"end":?9,
          ????????????"range":?[
          ??????????????8,
          ??????????????9
          ????????????],
          ????????????"value":?1,
          ????????????"raw":?"1"
          ??????????}
          ????????}
          ??????],
          ??????"kind":?"var"
          ????}
          ??],
          ??"sourceType":?"module"
          }

          代碼轉(zhuǎn)換為 AST 后,可以很方便的對(duì)代碼的每個(gè)節(jié)點(diǎn)對(duì)代碼進(jìn)行檢查。

          自定義 ESLint 規(guī)則開發(fā)

          怎么自定義

          語(yǔ)法樹分析

          對(duì)目標(biāo)代碼進(jìn)行語(yǔ)法樹解析,可使用 astexplorer (https://astexplorer.net/)

          add35582b6b5f990c99269776d76cab5.webp

          編寫規(guī)則

          下面是一個(gè)規(guī)則簡(jiǎn)單的結(jié)構(gòu)(官方 API 文檔說明:https://eslint.org/docs/developer-guide/working-with-rules#rule-basics)

          module.exports?=?{
          ??meta:?{
          ????docs:?{
          ??????description:?"最多參數(shù)允許參數(shù)",
          ????},
          ??},
          ??create:?function?(context)?{
          ????return?{
          ??????FunctionDeclaration:?(node)?=>?{
          ????????if?(node.params.length?>?3)?{
          ??????????context.report({
          ????????????node,
          ????????????message:?"參數(shù)最多不能超過3個(gè)",
          ??????????});
          ????????}
          ??????},
          ????};
          ??},
          };
          • meta(對(duì)象)包含規(guī)則的元數(shù)據(jù)
          • create ( function ) 返回一個(gè)對(duì)象,其中包含了 ESLint 在遍歷 JavaScript 代碼的抽象語(yǔ)法樹 AST ( ESTree 定義的 AST ) 時(shí),用來訪問節(jié)點(diǎn)的方法

          • context.report ( ) ?用來發(fā)布警告或錯(cuò)誤,并能提供自動(dòng)修復(fù)功能(取決于你所使用的配置)

          最簡(jiǎn)單的示例(只使用 node 和 message 參數(shù)):

          context.report({
          ????node,
          ????message:?"參數(shù)最多不能超過3個(gè)",
          });
          使用上面的這個(gè)規(guī)則,結(jié)合編輯器就有了對(duì)整個(gè) node 節(jié)點(diǎn)的提示,如果需要更精確的錯(cuò)誤或警告提示,我們可以使用 loc 參數(shù),API 文檔說明 (https://eslint.org/docs/developer-guide/working-with-rules#context-report)。14a96893265ef668914a03ca6c88d626.webpimage

          如何使用自定義規(guī)則

          使用自定義的 ESLint 規(guī)則,你需要自定義一個(gè) ESLint 的插件,然后將規(guī)則寫到自定義的 ESLint 插件中,然后在業(yè)務(wù)代碼中添加 ESLint 配置,引入 ESLint 插件。

          ESLint 插件

          創(chuàng)建

          創(chuàng)建一個(gè) ESLint plugin,并創(chuàng)建 一個(gè) ESLint rule

          基于 Yeoman generator (https://yeoman.io/authoring/) ,可以快速創(chuàng)建 ESLint plugin 項(xiàng)目。

          npm?i?-g?yo
          npm?i?-g?generator-eslint
          //?創(chuàng)建一個(gè)plugin
          yo?eslint:plugin
          //?創(chuàng)建一個(gè)規(guī)則
          yo?eslint:rule

          創(chuàng)建好的項(xiàng)目目錄結(jié)構(gòu):

          • rules 文件夾存放的是各個(gè)規(guī)則文件

          • tests 文件夾存放單元測(cè)試文件

          • package.json 是你的 ESLint 插件 npm 包的說明文件,其中的 name 屬性就是你的 ESLint ?插件的名稱,命名規(guī)則:帶前綴 eslint-plugin-
          80bfc9c5e67a2c19e0cc767893464f81.webp

          示例代碼:

          lib/rules/max-params.js

          module.exports?=?{
          ??meta:?{
          ????docs:?{
          ??????description:?"最多參數(shù)",
          ????},
          ??},
          ??create:?function?(context)?{
          ????/**
          ?????*?獲取函數(shù)的參數(shù)的開始、結(jié)束位置
          ?????*?@param?{node}?node?AST?Node?
          ?????*/

          ????function?getFunctionParamsLoc(node)?{
          ??????const?paramsLength?=?node.params.length;
          ??????return?{
          ????????start:?node.params[0].loc.start,
          ????????end:?node.params[paramsLength?-?1].loc.end,
          ??????};
          ????}
          ????return?{
          ??????FunctionDeclaration:?(node)?=>?{
          ????????if?(node.params.length?>?3)?{
          ??????????context.report({
          ????????????loc:?getFunctionParamsLoc(node),
          ????????????node,
          ????????????message:?"參數(shù)最多不能超過3個(gè)",
          ??????????});
          ????????}
          ??????},
          ????};
          ??},
          };

          補(bǔ)充測(cè)試用例

          /tests/lib/rules/max-params.js

          var?ruleTester?=?new?RuleTester();
          ruleTester.run("max-params",?rule,?{
          ??valid:?["function?test(d,?e,?f)?{}"],
          ??invalid:?[
          ????{
          ????????code:?"function?test(a,?b,?c,?d)?{}",
          ????????errors:?[{
          ????????????message:?"參數(shù)最多不能超過3個(gè)",
          ????????}]
          ????},
          ??],
          });

          ESLint 插件安裝

          在需要的業(yè)務(wù)代碼中安裝你的 ESLint 插件。(eslint-plugin-my-eslist-plugin 是你的 ESLint 插件 npm 包的包名)

          npm?install?eslint-plugin-my-eslist-plugin?

          如果你的 npm 包還未發(fā)布,需要進(jìn)行本地調(diào)試:

          可使用 npm link 本地調(diào)試,npm link 的使用 (https://www.baidu.com/s?ie=UTF-8&wd=npm%20link)。

          配置

          添加你的 plugin 包名(eslint-plugin- 前綴可忽略) 到 .eslintrc 配置文件的 plugins 字段。

          .eslintrc 配置文件示例:

          {
          ????"plugins":?[
          ????????"zoo"?//?你的?ESLint?plugin?的名字
          ????]
          }

          rules 中再將 plugin 中的規(guī)則導(dǎo)入。?? ESlint更新后,需要重啟 vsCode,才能生效。( vsCode ?重啟快捷方式:CTRL +SHITF + P,輸入 Reload Window

          此處涉及 ESLint 的規(guī)則設(shè)置(參考說明:https://eslint.org/docs/user-guide/configuring#configuring-rules)

          {
          ????"rules":?{
          ????????"zoo/rule-name":?2
          ????}
          }

          效果

          14a96893265ef668914a03ca6c88d626.webpimage

          實(shí)際應(yīng)用案例

          函數(shù)、方法的入?yún)€(gè)數(shù)控制,其實(shí)已經(jīng)在 ESLint 的規(guī)則中了。在業(yè)務(wù)場(chǎng)景中,我們需要對(duì)我們的業(yè)務(wù)規(guī)則編寫自定義的 ESLint 規(guī)則。

          一個(gè)簡(jiǎn)單的業(yè)務(wù)場(chǎng)景:業(yè)務(wù)中通常會(huì)出現(xiàn)跳轉(zhuǎn)到很多不同的業(yè)務(wù)域名的操作,不同的環(huán)境有不同的域名,我們需要從配置中取出域名使用,而不是采取硬編碼域名的方案。

          由此我們產(chǎn)生出了一個(gè)規(guī)則:禁止硬編碼業(yè)務(wù)域名。

          規(guī)則為:

          module.exports?=?{
          ??meta:?{
          ????type:?"suggestion",
          ????docs:?{
          ??????description:?"不允許硬編碼業(yè)務(wù)域名",
          ????},
          ????fixable:?"code",
          ??},

          ??create:?function?(context)?{
          ????const?sourceCode?=?context.getSourceCode();

          ????function?checkDomain(node)?{
          ??????//?匹配硬編碼的業(yè)務(wù)域名的正則
          ??????const?Reg?=?/^(http:\/\/|https:\/\/|\/\/)(.*.){0,1}zcygov(.com|cn)(.*)/;
          ??????const?content?=
          ????????(node.type?===?"Literal"?&&?node.value)?||
          ????????(node.type?===?"TemplateLiteral"?&&?node.quasis[0].value.cooked);

          ??????const?domainNode?=
          ????????(node.type?===?"Literal"?&&?node)?||
          ????????(node.type?===?"TemplateLiteral"?&&?node.quasis[0]);

          ??????if?(Reg.test(content))?{
          ????????context.report({
          ??????????node,
          ??????????//?錯(cuò)誤/警告提示信息
          ??????????message:?"不允許硬編碼業(yè)務(wù)域名",
          ??????????//?修復(fù)
          ??????????fix(fixer)?{
          ????????????
          ????????????const?fixes?=?[];
          ????????????
          ????????????let?domainKey?=?content.match(Reg)[2];
          ????????????domainKey?=?domainKey
          ????????????????domainKey.substr(0,?domainKey.length?-?1)
          ??????????????:?"";

          ????????????if?(node.type?===?"Literal")?{
          ??????????????fixes.push(
          ????????????????fixer.replaceTextRange(
          ??????????????????[domainNode.start?+?1,?domainNode.end?-?1],
          ??????????????????content.replace(Reg,?`$4`)
          ????????????????)
          ??????????????);
          ????????????}

          ????????????if?(node.type?===?"TemplateLiteral")?{
          ??????????????fixes.push(
          ????????????????fixer.replaceTextRange(
          ??????????????????[domainNode.start,?domainNode.end],
          ??????????????????content.replace(Reg,?`$4`)
          ????????????????)
          ??????????????);
          ????????????}
          ?????????????
          ????????????if?(
          ??????????????node.type?===?"Literal"?&&
          ??????????????node.parent.type?===?"JSXAttribute"
          ????????????)?{
          ??????????????fixes.push(fixer.insertTextBefore(node,?"{"));
          ??????????????fixes.push(fixer.insertTextAfter(node,?"}"));
          ????????????}

          ????????????fixes.push(
          ??????????????fixer.insertTextBefore(
          ????????????????node,
          ????????????????`window.getDomain('${domainKey}')?+?`
          ??????????????)
          ????????????);

          ????????????return?fixes;
          ??????????},
          ????????});
          ??????}
          ????}
          ????return?{
          ??????//?文本
          ??????Literal:?checkDomain,
          ??????//?模板字符串
          ??????TemplateLiteral:?checkDomain,
          ????};
          ??},
          };

          補(bǔ)充測(cè)試用例

          /tests/lib/rules/no-zcy-domain.js

          var?rule?=?require("../../../lib/rules/no-zcy-domain"),
          ????RuleTester?=?require("eslint").RuleTester;

          var?ruleTester?=?new?RuleTester();
          ruleTester.run("no-zcy-domain",?rule,?{
          ??valid:?[
          ????"bar",
          ????"baz",
          ????`
          ??var?s?=?{
          ????x:?"zcygov"
          ??};
          ??`
          ,
          ??],
          ??invalid:?[
          ????{
          ??????code:?`
          ??????????????var?s?=?"http://zcygov.cn"
          ????????????`
          ,
          ??????errors:?[
          ????????{
          ??????????message:?"不允許硬編碼業(yè)務(wù)域名",
          ????????},
          ??????],
          ????},
          ????{
          ??????code:?`
          ????????????var?s?=?{
          ??????????????x:?"http://bidding.zcygov.cn"
          ????????????};
          ????????????`
          ,
          ??????errors:?[
          ????????{
          ??????????message:?"不允許硬編碼業(yè)務(wù)域名",
          ????????},
          ??????],
          ????},
          ??],
          });
          結(jié)合 vsCode 保存自動(dòng)修復(fù) ESLint 錯(cuò)誤的功能,效果如下:82f16300f202a308b5785d210e9d61a6.webp

          更多的應(yīng)用場(chǎng)景

          除了上面說的硬編碼的場(chǎng)景,還可以將沉淀出的最佳實(shí)踐和業(yè)務(wù)規(guī)范通過自定義 ESLint 的方式來提示開發(fā)者,這對(duì)于多人協(xié)助、代碼維護(hù)、代碼風(fēng)格的一致性都會(huì)有很大的幫助。

          更多的應(yīng)用場(chǎng)景有:

          • Input 必須要有 maxlength 屬性,防止請(qǐng)求的后端接口數(shù)據(jù)庫(kù)異常

          • 代碼中不能出現(xiàn)加減乘除等計(jì)算,如果需要計(jì)算應(yīng)該引入工具函數(shù),來控制由于前端浮點(diǎn)數(shù)計(jì)算引起的 Bug

          • 規(guī)范限制,單位元的兩邊的括號(hào)要用英文括號(hào),不能用中文括號(hào),來達(dá)到交互展示統(tǒng)一的效果

          • 代碼中不能使用 OSS 地址的靜態(tài)資源路徑,應(yīng)該使用 CDN 地址的資源路徑

          • ...

          參考文獻(xiàn)

          • https://developer.mozilla.org/zh-CN/docs/Mozilla/Projects/SpiderMonkey/Parser_API

          • https://eslint.org/docs/developer-guide/working-with-rules



          ?? 看完三件事

          如果你覺得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:


          1. 點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

          2. 關(guān)注我的官網(wǎng)?https://muyiy.cn,讓我們成為長(zhǎng)期關(guān)系

          3. 關(guān)注公眾號(hào)「高級(jí)前端進(jìn)階」,公眾號(hào)后臺(tái)回復(fù)「面試題」 送你高級(jí)前端面試題,回復(fù)「加群」加入面試互助交流群


          》》面試官都在用的題庫(kù),快來看看《《

          瀏覽 39
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产午夜精品一区二区三区视频 | 人人操天天干 | 操逼操逼操逼开房操小姐逼 | www插插插无码视频 | 女人毛片|