Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 一本色道综合久久爱15,国产成人一区二区三区,99视频在线精品免费

          整合營銷服務(wù)商

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

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

          正則表達(dá)式以及正則在JS表單校驗(yàn)中的應(yīng)用

          則表達(dá)式以及正則在JS表單校驗(yàn)中的應(yīng)用

          1. 正則表達(dá)式的引入

          表單驗(yàn)證兩種常用的方法:

          <input type="submit" onclick="return  validate()"/>
          <form action="/aa" onsubmit="return  validate()"/>

          這里我們使用QQ號碼作為表單驗(yàn)證來測試,我們知道QQ號碼的規(guī)則還是比較復(fù)雜,但是這里我們僅僅使用以下三條規(guī)則來講解:

          1. qq號必須是5-10位

          2. 數(shù)字0不可以作為qq號碼的開頭

          3. QQ號碼必須是純數(shù)字組成

          代碼示例如下所示:

          <input type="submit" onclick="return  validate()"/>
          <form action="/aa" onsubmit="return  validate()"/>

          當(dāng)然除了在form表單中添加onsubmit事件來實(shí)現(xiàn)表單驗(yàn)證,我們還可以使用在submit類型的input標(biāo)簽中添加onclick事件來實(shí)現(xiàn)表單驗(yàn)證,代碼如下所示:

          <form action="server/server.html" method="get" >
              QQ賬號: <input id="qq" type="text" placeholder="請輸入QQ賬號...">
            <span id="msg"></span> <br>
              <input type="submit" value="登錄" onclick="return checkQQ();"> 
          </form>

          相信大家對上述代碼都能夠理解,但是大家應(yīng)該也發(fā)現(xiàn)了使用傳統(tǒng)的if-else來校驗(yàn)qq號碼,代碼非常冗余,而且可讀性很差,何況才短短兩條規(guī)則就寫了這么多的判斷,那真實(shí)的qq號碼肯定規(guī)則更多,如果讓你去驗(yàn)證郵箱,或者網(wǎng)址呢?所以我們非常有必要學(xué)習(xí)一門新的技術(shù)用于我們JS表單校驗(yàn),它就是正則表達(dá)式了。

          2. 正則表達(dá)式概述

          正則表達(dá)式簡介

          為什么需要正則表達(dá)式?

          1.本質(zhì)是用來對復(fù)雜文本進(jìn)行處理。

          2.當(dāng)然也可以用于數(shù)據(jù)校驗(yàn)

          3.文本匹配、搜索、分割、替換等等

          正則表達(dá)式的優(yōu)勢和用途?

          一種強(qiáng)大而靈活的文本處理工具;

          提供了一種緊湊的、動(dòng)態(tài)的方式,能夠以一種完全通用的方式來解決各種字符串處理(例如:驗(yàn)證、查找、替換等)問題;

          大部分語言、數(shù)據(jù)庫都支持正則表達(dá)式。

          正則表達(dá)式定義:

          正如他的名字一樣是描述了一個(gè)規(guī)則,通過這個(gè)規(guī)則可以匹配一類字符串。

          正則表達(dá)式的用處:

          驗(yàn)證給定字符串是否符合指定特征,比如驗(yàn)證是否是合法的郵件地址。

          用來查找字符串,從一個(gè)長的文本中查找符合指定特征的字符串。

          用來替換,比普通的替換更強(qiáng)大,用來替換滿足某個(gè)規(guī)則的字符串

          用來分割,比普通的分割更強(qiáng)大,用來分割滿足某個(gè)規(guī)則的字符串

          3. 工具軟件的使用

          3.1. 工具軟件RegexBuddy的使用

          為了提高開發(fā)效率,一般都先在工具軟件中測試正則表達(dá)式,通過測試后,才在程序中使用。

          3.2 正則表達(dá)式圖形化顯示網(wǎng)站 Regexper

          由于正則表達(dá)式非常靈活,正則表達(dá)式本身的可讀性非常差,所以比較復(fù)雜的正則表達(dá)式閱讀起來相對比較困難,所以我們可以借助一個(gè)圖形化正則表達(dá)式網(wǎng)站來幫助我們分析一個(gè)相對復(fù)雜的正則表達(dá)式。

          正則圖形化顯示網(wǎng)站:
          //regexper.com
          //regexr-cn.com

          圖形化顯示結(jié)果如下所示:

          4. 正則表達(dá)式規(guī)則

          4.1 普通字符

          字母、數(shù)字、漢字、下劃線、以及沒有特殊定義的標(biāo)點(diǎn)符號,都是“普通字符”。表達(dá)式中的普通字符,在匹配一個(gè)字符串的時(shí)候,匹配與之相同的一個(gè)字符。例如字符a, b, c ,1,2,中等等。

          4.2 轉(zhuǎn)義字符

          \n

          代表換行符

          \t

          制表符

          \

          代表\本身

          \^ ,$,\.,\(, \) , \{, \} , \? , \+ , \* , \| ,\[, \]

          匹配這些字符本身

          4.3 字符集合

          [ ]方括號匹配方式,能夠匹配方括號中任意一個(gè)字符

          [ab5@]

          匹配 "a" 或 "b" 或 "5" 或 "@"

          [^abc]

          匹配 "a","b","c" 之外的任意一個(gè)字符

          [f-k]

          匹配 "f"~"k" 之間的任意一個(gè)字母

          [^A-F0-3]

          匹配 "A"~"F","0"~"3" 之外的任意一個(gè)字符

          顯示效果如下所示:


          注意: 正則表達(dá)式中的特殊符號,如果被包含于中括號中,則失去特殊意義,但 \ [ ] : ^ - 除外。

          4.4 預(yù)定義字符

          還有一些字符是正則表達(dá)式語法事先定義好的,有特殊的含義,能夠與 ‘多種字符’ 匹配的表達(dá)式

          \d

          任意一個(gè)數(shù)字,0~9 中的任意一個(gè)

          \w

          任意一個(gè)字母數(shù)字下劃線,也就是 A~Z,a~z,0~9,_ 中任意一個(gè)

          \s

          包括空格、制表符、換行符等空白字符的其中任意一個(gè)

          .

          小數(shù)點(diǎn)可以匹配除了換行符(\n)以外的任意一個(gè)字符

          注意: 如果是\D,\W,\S表示相反的意思。

          4.5 量詞

          {n}

          表達(dá)式重復(fù)n次

          {m,n}

          表達(dá)式至少重復(fù)m次,最多重復(fù)n次

          {m,}

          表達(dá)式至少重復(fù)m次

          ?

          匹配表達(dá)式0次或者1次,相當(dāng)于 {0,1}

          +

          表達(dá)式至少出現(xiàn)1次,相當(dāng)于 {1,}

          *

          表達(dá)式不出現(xiàn)或出現(xiàn)任意次,相當(dāng)于 {0,}

          顯示效果如下所示:

          表達(dá)式至少重復(fù)2次,最多重復(fù)4次

          至少3次:

          匹配表達(dá)式0次或者1次,相當(dāng)于 {0,1}.

          +至少一次

          表達(dá)式不出現(xiàn)或出現(xiàn)任意次,相當(dāng)于 {0,}

          4.6 貪婪模式和非貪婪模式

          匹配次數(shù)中的貪婪模式(匹配字符越多越好)

          “{m,n}”, “{m,}”, “?”, “*”, “+”,具體匹配的次數(shù)隨被匹配的字符串而定。這種重復(fù)匹配不定次數(shù)的表達(dá)式在匹配過程中,總是盡可能多的匹配。

          匹配次數(shù)中的非貪婪模式(匹配字符越少越好)

          在修飾匹配次數(shù)的特殊符號后再加上一個(gè) "?" 號,則可以使匹配次數(shù)不定的表達(dá)式盡可能少的匹配,使可匹配可不匹配的表達(dá)式,盡可能地 "不匹配"。

          4.7 邊界字符

          (本組標(biāo)記匹配的不是字符而是位置,符合某種條件的位置)

          ^

          與字符串開始的地方匹配

          $

          與字符串結(jié)束的地方匹配

          \b

          匹配一個(gè)單詞邊界

          顯示效果如下所示:


          4.8 選擇符和分組

          表達(dá)式

          作用

          |

          左右兩邊表達(dá)式之間 "或" 關(guān)系,匹配左邊或者右邊

          ( )

          (1). 在被修飾匹配次數(shù)的時(shí)候,括號中的表達(dá)式可以作為整體被修飾

          (2). 取匹配結(jié)果的時(shí)候,括號中的表達(dá)式匹配到的內(nèi)容可以被單獨(dú)得到

          (3). 每一對括號會分配一個(gè)編號,使用 () 的捕獲根據(jù)左括號的順序從 1 開始自動(dòng)編號。捕獲元素編號為零的第一個(gè)捕獲是由整個(gè)正則表達(dá)式模式匹配的文本

          選擇符操作顯示效果如下所示:

          分組:

          4.9 反向引用(\nnn)

          1. 每一對()會分配一個(gè)編號,使用 () 的捕獲根據(jù)左括號的順序從 1 開始自動(dòng)編號

          2. 通過反向引用,可以對分組已捕獲的字符串進(jìn)行引用。


          5. 正則表達(dá)式的匹配模式

          5.1 IGNORECASE 忽略大小寫模式

          1. 匹配時(shí)忽略大小寫。

          2. 默認(rèn)情況下,正則表達(dá)式是要區(qū)分大小寫的。

          5.2 SINGLELINE 單行模式

          1. 整個(gè)文本看作一個(gè)字符串,只有一個(gè)開頭,一個(gè)結(jié)尾。

          2.使小數(shù)點(diǎn) "." 可以匹配包含換行符(\n)在內(nèi)的任意字符。

          5.3 MULTILINE 多行模式

          1.每行都是一個(gè)字符串,都有開頭和結(jié)尾。

          2.在指定了 MULTILINE 之后,如果需要僅匹配字符串開始和結(jié)束位置,可以使用 \A 和 \Z

          6.開發(fā)中使用正則表達(dá)式的流程

          1. 分析所要匹配的數(shù)據(jù),寫出測試用的典型數(shù)據(jù)

          2. 在工具軟件中進(jìn)行匹配測試

          3. 在程序中調(diào)用通過測試的正則表達(dá)式


          7. 課堂練習(xí)

          電話號碼驗(yàn)證

          (1)電話號碼由數(shù)字和"-"構(gòu)成

          (2)電話號碼為7到8位(不加區(qū)號)

          (3)如果電話號碼中包含有區(qū)號,那么區(qū)號為三位或四位, 首位是0.

          (4)區(qū)號用"-"和其他部分隔開

          (5)移動(dòng)電話號碼為11位

          (6)11位移動(dòng)電話號碼的第一位和第二位為"13“,”15”,”18”

          0(\d{2,3})-\d{7,8}

          (13)|(15)|(18)\d{9}


          8. Java中使用正則表達(dá)式

          相關(guān)類位于:java.util.regex包下面

          類 Pattern

          正則表達(dá)式的編譯表示形式。

          Pattern p = Pattern.compile(r); //建立正則表達(dá)式,并啟用相應(yīng)模式

          類 Matcher

          通過解釋 Patterncharacter sequence 執(zhí)行匹配操作的引擎

          Matcher m = p.matcher(str); //匹配str字符串

          編程中使用正則表達(dá)式常見情況:

          驗(yàn)證表達(dá)式是否匹配整個(gè)字符串

          驗(yàn)證表達(dá)式是否可以匹配字符串的子字符串

          返回給定字符串中匹配給定正則表達(dá)式所有子字符串

          替換給定字符串中匹配正則表達(dá)式的子字符串

          根據(jù)正則表達(dá)式定義規(guī)則截取字符串


          9. JAVASCRIPT中使用正則表達(dá)式

          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <meta name="viewport" content="width=device-width, initial-scale=1.0">
              <title>Document</title>
          </head>
          <body>
              <!--         QQ賬號的驗(yàn)證: 
                     1.qq號必須是5-10位 
            2.數(shù)字0不可以作為qq號碼的開頭
               -->     <form action="server/server.html" method="get" onsubmit="return checkQQ()">
                  QQ賬號: <input id="qq" type="text" placeholder="請輸入QQ賬號..."> <span id="msg"></span> <br>
                  <input type="submit" value="登錄">
              </form>    
          <script>
                  function checkQQ() {            var qq = document.getElementById("qq").value;
                      var msg = document.getElementById("msg");
                      // 默認(rèn)qq號碼是合格的
                      var flag = true;
                      // 保證5-10位
                      if(qq.length >= 5 && qq.length <= 10) {
                          // 保證不是0開頭
                          if(qq.charAt(0) != 0) {
                              // 保證是數(shù)字
                              for(var i = 0; i < qq.length; i++) {
           var ch = qq.substr(i,1);
                                  if(!(ch >= '0' && ch <= '9')) {
                                      flag = false; 
             msg.innerText = "XXX QQ號碼必須是數(shù)字!!!"                            
          msg.style.color = "red";
                                      break;    
                              }      
                        }      
                    } else {          
                    flag = false;   
                           msg.innerText = "XXX QQ號碼不能以數(shù)字0開頭!!!"
                              msg.style.color = "red";  
                        }      
                } else {       
                   flag = false;   
                       msg.innerText = "XXX QQ號碼的位數(shù)必須在5~10之間!!!"
                          msg.style.color = "red";
                      }       
               return flag;  
                } 
             </script>
          </body>
          </html>

          Flags可選項(xiàng)常見值:

          g (全文查找出現(xiàn)的所有pattern)(沒有g(shù)只會匹配一次)

          i (忽略大小寫)

          <form action="server/server.html" method="get" >
              QQ賬號: <input id="qq" type="text" placeholder="請輸入QQ賬號...">
            <span id="msg"></span> <br>
              <input type="submit" value="登錄" onclick="return checkQQ();"> 
          </form>

          RegExp對象常用方法:

          exec(): 返回的是一個(gè)數(shù)組。該數(shù)組包含了匹配該模式的第一個(gè)子字符串以及該子字符串中匹配相關(guān)分組的字符串。比如:

          var re = new RegExp("(\\d+)([a-z]+)","ig");
          var result = re.exec("33ff55tt77uu88yy");  

          返回的數(shù)組為:[33ff,33,ff]

          test(): 返回一個(gè) Boolean 值,它指出在被查找的字符串中是否存在模式匹配的子字符串

          //test 查找是否 存在的字符串
          var str1 = "abc123abc123";
          //判斷字符串是否都是數(shù)字
          var reg3 = /^\d+$/;
          var flag = reg3.test(str1);
          console.log(flag);

          字符串中常用的使用正則表達(dá)式的方式:

          match():使用正則表達(dá)式模式對字符串執(zhí)行查找,并將符合該模式的所有子字符串包含到數(shù)組中返回。

          var re = new RegExp("(\\d+)([a-z]+)","ig");
          var reg = /(\d+)([a-z]+)/gi;
          var t = "33ff55tt77uu88yy".match(re);

          結(jié)果為數(shù)組:[33ff,55tt,77uu,88yy]

          search(): 返回與正則表達(dá)式查找內(nèi)容匹配的第一個(gè)子字符串的位置

          var reg = /\d+/g; // 數(shù)組正則
          var index = "33ff55tt77uu88yy".search(reg);
          split(regex):按照指定的模式切割字符串,返回一個(gè)數(shù)組。
          var t = "asdfd33ff55tt77uu88yy".split(/\d+/);
          replace(): 替換
          var t = "asdfd3355tt77uu88yy".replace(/\d+/g,"#");
          代碼示例如下所示:
          // 替換
          // 指定為global模式,否則只替換第一個(gè)子字符串
          var reg4= /\d+/g;
          var str2 = str1.replace(reg4,"#");
          console.log(str2);

          10. 針對表單域的驗(yàn)證,封裝一個(gè)通用的函數(shù)

          我們可以編寫一個(gè)簡單的用戶名驗(yàn)證的方法,代碼如下所示:

          <!DOCTYPE html>
          <html lang="en">
          <head>   
           <meta charset="UTF-8">  
            <meta name="viewport" content="width=device-width, initial-scale=1.0">  
            <title>Js表單正則驗(yàn)證</title>
          </head>
          <body> 
             <form action="server/server.html" method="get">
          用戶名:<input type="text" id="nameId" name="name" onblur="checkName();"><span id="msg"></span><br>
          密碼: <input type="password" name="pwd"><br>
                  <input type="submit">  
            </form> 
             <script>
                  /*
                       表單驗(yàn)證:
                      實(shí)現(xiàn)思路: 
                          1.給input標(biāo)簽添加onblur事件
                          2.獲取input標(biāo)簽輸入的內(nèi)容
                          3.判斷是否輸入合法
            4.dom操作修改頁面提示信息    
               */
                   function checkName() {
                      
          var nameInputValue = document.getElementById("nameId").value;
                      var msg = document.getElementById("msg");
                      var reg = /^[\u4e00-\u9fa5]{2,10}$/; 
                      if(reg.test(nameInputValue)) {
                          msg.innerText = "√ 用戶名合法!!!"
                          msg.style.color = "red";
                      } else {
                          msg.innerText = "X 用戶名必須是2-10個(gè)中文字符!!!"
                          msg.style.color = "red"; 
                     }   
                }   
           </script>
          </body>
          </html>

          這里我們通過輸入框失去焦點(diǎn)來對輸入的內(nèi)容進(jìn)行驗(yàn)證,驗(yàn)證的步驟也非常簡單,步驟基本如下所示:

          1. 給表單域添加onblur失去焦點(diǎn)事件

          2. 獲取表單域輸入框的內(nèi)容

          3. 通過正則表達(dá)式判斷輸入是否合法

          4. 根據(jù)是否合法顯示不同的頁面提示信息

          通過觀察我們不難發(fā)現(xiàn),不僅驗(yàn)證用戶名,驗(yàn)證郵箱,密碼,身份證等等基本上步驟都是一樣的,但是有一些例如正則表達(dá)式或者顯示的正確或者錯(cuò)誤信息是變化的,所以我們可以對驗(yàn)證的方法進(jìn)行封裝,將變化的抽取成為形參,從而用于其他的表單校驗(yàn),代碼如下所示:

          現(xiàn)代web開發(fā)中,表單是用戶與網(wǎng)站互動(dòng)的重要方式之一。HTML5為表單提交提供了強(qiáng)大的功能和豐富的輸入類型,讓收集和驗(yàn)證用戶輸入數(shù)據(jù)變得更加容易和安全。本文將詳細(xì)介紹HTML5表單的各個(gè)方面,包括基本結(jié)構(gòu)、輸入類型、驗(yàn)證方法和提交過程。

          基本結(jié)構(gòu)

          HTML表單由<form>標(biāo)簽定義,它可以包含輸入字段、標(biāo)簽、按鈕等元素。一個(gè)基本的表單結(jié)構(gòu)如下所示:

          <form action="/submit_form" method="post">
            <label for="name">姓名:</label>
            <input type="text" id="name" name="name" required>
            
            <label for="email">電子郵箱:</label>
            <input type="email" id="email" name="email" required>
            
            <input type="submit" value="提交">
          </form>
          

          在這個(gè)例子中,表單有兩個(gè)輸入字段:姓名和電子郵箱。每個(gè)輸入字段都有一個(gè)<label>標(biāo)簽,這不僅有助于用戶理解輸入的內(nèi)容,也有助于屏幕閱讀器等輔助技術(shù)。<form>標(biāo)簽的action屬性定義了數(shù)據(jù)提交到服務(wù)器的URL,method屬性定義了提交數(shù)據(jù)的HTTP方法(通常是post或get)。

          輸入類型

          HTML5提供了多種輸入類型,以支持不同的數(shù)據(jù)格式和設(shè)備。

          文本輸入

          <!-- 單行文本 -->
          <input type="text" name="username" placeholder="請輸入用戶名" required>
          
          <!-- 密碼 -->
          <input type="password" name="password" required minlength="8">
          
          <!-- 郵箱 -->
          <input type="email" name="email" required placeholder="example@domain.com">
          
          <!-- 搜索框 -->
          <input type="search" name="search" placeholder="搜索...">
          

          數(shù)值輸入

          <!-- 數(shù)值 -->
          <input type="number" name="age" min="18" max="100" step="1" required>
          
          <!-- 滑動(dòng)條 -->
          <input type="range" name="volume" min="0" max="100" step="1">
          
          <!-- 電話號碼 -->
          <input type="tel" name="phone" pattern="^\+?\d{0,13}" placeholder="+8613800000000">
          

          日期和時(shí)間輸入

          <!-- 日期 -->
          <input type="date" name="birthdate" required>
          
          <!-- 時(shí)間 -->
          <input type="time" name="appointmenttime">
          
          <!-- 日期和時(shí)間 -->
          <input type="datetime-local" name="appointmentdatetime">
          

          選擇輸入

          <!-- 復(fù)選框 -->
          <label><input type="checkbox" name="interest" value="coding"> 編程</label>
          <label><input type="checkbox" name="interest" value="music"> 音樂</label>
          
          <!-- 單選按鈕 -->
          <label><input type="radio" name="gender" value="male" required> 男性</label>
          <label><input type="radio" name="gender" value="female"> 女性</label>
          
          <!-- 下拉選擇 -->
          <select name="country" required>
            <option value="china">中國</option>
            <option value="usa">美國</option>
          </select>
          

          特殊輸入

          <!-- 顏色選擇器 -->
          <input type="color" name="favcolor" value="#ff0000">
          
          <!-- 文件上傳 -->
          <input type="file" name="resume" accept=".pdf,.docx" multiple>
          

          驗(yàn)證方法

          HTML5表單提供了內(nèi)置的驗(yàn)證功能,可以在數(shù)據(jù)提交到服務(wù)器之前進(jìn)行檢查。

          必填字段

          <input type="text" name="username" required>
          

          正則表達(dá)式

          <input type="text" name="zipcode" pattern="\d{5}(-\d{4})?" title="請輸入5位數(shù)的郵政編碼">
          

          數(shù)值范圍

          <input type="number" name="age" min="18" max="99">
          

          長度限制

          <input type="text" name="username" minlength="4" maxlength="8">
          

          表單提交

          當(dāng)用戶填寫完表單并點(diǎn)擊提交按鈕時(shí),瀏覽器會自動(dòng)檢查所有輸入字段的有效性。如果所有字段都滿足要求,表單數(shù)據(jù)將被發(fā)送到服務(wù)器。否則,瀏覽器會顯示錯(cuò)誤信息,并阻止表單提交。

          <input type="submit" value="提交">
          

          可以使用JavaScript來自定義驗(yàn)證或處理提交事件:

          document.querySelector('form').addEventListener('submit', function(event) {
            // 檢查表單數(shù)據(jù)
            if (!this.checkValidity()) {
              event.preventDefault(); // 阻止表單提交
              // 自定義錯(cuò)誤處理
            }
            // 可以在這里添加額外的邏輯,比如發(fā)送數(shù)據(jù)到服務(wù)器的Ajax請求
          });
          

          完整例子

          <!DOCTYPE html>
          <html lang="en">
          <head>
          <meta charset="UTF-8">
          <title>表單提交并顯示JSON</title>
          </head>
          <body>
          
          <!-- 表單定義 -->
          <form id="myForm">
            <label for="name">姓名:</label>
            <input type="text" id="name" name="name">
            <br>
          
            <label for="email">電子郵件:</label>
            <input type="email" id="email" name="email">
            <br>
          
            <input type="button" value="提交" onclick="submitForm()">
          </form>
          
          <script>
          // JavaScript函數(shù),處理表單提交
          function submitForm() {
            // 獲取表單元素
            var form = document.getElementById('myForm');
            
            // 創(chuàng)建一個(gè)FormData對象
            var formData = new FormData(form);
            
            // 創(chuàng)建一個(gè)空對象來存儲表單數(shù)據(jù)
            var formObject = {};
            
            // 將FormData轉(zhuǎn)換為普通對象
            formData.forEach(function(value, key){
              formObject[key] = value;
            });
            
            // 將對象轉(zhuǎn)換為JSON字符串
            var jsonString = JSON.stringify(formObject);
            
            // 彈出包含JSON字符串的對話框
            alert(jsonString);
            
            // 阻止表單的默認(rèn)提交行為
            return false;
          }
          </script>
          
          </body>
          </html>
          

          在這個(gè)例子中:

          1. 我們定義了一個(gè)包含姓名和電子郵件輸入字段的表單,以及一個(gè)按鈕,當(dāng)點(diǎn)擊按鈕時(shí)會調(diào)用submitForm函數(shù)。
          2. 在submitForm函數(shù)中,我們首先獲取表單元素并創(chuàng)建一個(gè)FormData對象。然后,我們遍歷FormData對象,將表單數(shù)據(jù)復(fù)制到一個(gè)普通的JavaScript對象formObject中。
          3. 接著,我們使用JSON.stringify方法將formObject轉(zhuǎn)換成JSON字符串。
          4. 最后,我們使用alert函數(shù)彈出一個(gè)包含JSON字符串的對話框。

          注意,這個(gè)例子中我們使用了type="button"而不是type="submit",因?yàn)槲覀儾幌M韱斡心J(rèn)的提交行為。我們的JavaScript函數(shù)submitForm會處理所有的邏輯,并且通過返回false來阻止默認(rèn)的表單提交。如果你想要使用type="submit",你需要在<form>標(biāo)簽上添加一個(gè)onsubmit="return submitForm()"屬性來代替按鈕上的onclick事件。

          結(jié)論

          HTML5的表單功能為開發(fā)者提供了強(qiáng)大的工具,以便創(chuàng)建功能豐富、用戶友好且安全的網(wǎng)站。通過使用HTML5的輸入類型和驗(yàn)證方法,可以確保用戶輸入的數(shù)據(jù)是有效的,同時(shí)提高用戶體驗(yàn)。隨著技術(shù)的不斷進(jìn)步,HTML5表單和相關(guān)API將繼續(xù)發(fā)展,為前端工程師提供更多的可能性。

          者:郜克帥

          原文:https://dev.to/ajones_codes/a-better-guide-to-forms-in-react-47f0?utm_source=newsletter.reactdigest.net&utm_medium=newsletter&utm_campaign=a-better-guide-to-forms-in-react

          React 生態(tài)擁有豐富的庫、文章、視頻和幾乎你能想到的所有 Web 領(lǐng)域的資料。然而,隨著時(shí)間的推移,這些資料許多都已經(jīng)過時(shí),無法滿足現(xiàn)代最佳實(shí)踐的要求了。

          最近,我在開發(fā)一個(gè) AI 項(xiàng)目,里面有許多復(fù)雜的動(dòng)態(tài)表單。在研究了許多優(yōu)秀的 React 表單指南之后,我意識到,大多數(shù)構(gòu)建表單的資源都已經(jīng)過時(shí)了,而且往往已經(jīng)過時(shí)很多年。

          本文將介紹 React 中構(gòu)建表單的現(xiàn)代最佳實(shí)踐、如何去構(gòu)建動(dòng)態(tài)表單、 RSC(React Server Components)的表單等等。最后,在理解了這些之后,我將解釋我在其他指南中發(fā)現(xiàn)的不足,并根據(jù)我使用 React 的經(jīng)驗(yàn)提出建議。

          受控與非受控

          理解 React 中表單的關(guān)鍵點(diǎn)在于 “受控” 與 “非受控” 的概念,這是 React 中構(gòu)建表單的兩種不同的方法。

          受控表單使用 state 存儲每個(gè) input 的值,然后在每次渲染時(shí)通過 value 屬性設(shè)置對應(yīng) input的值。如果其他函數(shù)更新了這些 state,同樣的,對應(yīng) input 的值也會立刻改變。

          如果你的代碼沒有渲染 Form,但相關(guān)的 state 并不會消失,仍然存在于我們的運(yùn)行時(shí)上下文中。

          受控表單往往給予了我們更大的選擇,例如比較復(fù)雜的、非 HTML 標(biāo)準(zhǔn)的表單校驗(yàn),如檢查密碼強(qiáng)度和對用戶手機(jī)號進(jìn)行格式化。

          它們看起來往往是這個(gè)樣子的:

          import React, { useState } from 'react'
          
          function ControlledForm() {
            const [value, setValue] = useState('');
          
            const handleChange = (event) => {
              setValue(event.target.value);
            };
          
            const handleSubmit = () => {
              sendInputValueToApi(value).then(() => /** 業(yè)務(wù)邏輯... */);
            };
          
            return (
              <>
                <input type="text" value={value} onChange={handleChange} />
                <button onClick={handleSubmit}>send</button>
              </>
            )
          }
          

          注意,用 <form>input 包裹起來并且給 input 一個(gè)命名從語義上來講更加準(zhǔn)確,但是這不是必需的。

          因?yàn)閿?shù)據(jù)已經(jīng)保存在 state 中,所以我們并不需要真正的 onSubmit事件,而且在按鈕點(diǎn)擊時(shí),我們也并不需要直接訪問 input 的值。

          這種方式有一些不足之處:

          1. 你可能不想要每次用戶輸入時(shí)都去重新渲染組件。
          2. 你需要寫許多代碼去管理復(fù)雜的表單,因?yàn)殡S著表單規(guī)模的增長,會導(dǎo)致出現(xiàn)大量的 statesetSate,從而使代碼變的非常臃腫。
          3. 構(gòu)建動(dòng)態(tài)表單將變的非常困難,因?yàn)槟銦o法在條件判斷中使用像 useStatehooks。為了修復(fù)這個(gè)問題,你可能需要:
          1. 整個(gè)表單的值將存儲在一個(gè)巨大的對象中,然而這會導(dǎo)致所有的子組件將在任一其他組件變化時(shí)全部重新渲染,因?yàn)槲覀兏碌姆绞绞?setState({ ...preState, field: newValue })。要解決上述的問題,唯一的辦法就是緩存,但這又會增加大量的代碼。
          1. 在大型表單例如表格和 Excel 中,這會導(dǎo)致性能問題。
          import React, { useState } from "react";
          
          function CumbersomeForm() {
            const [formData, setFormData] = useState({
              firstName: "",
              lastName: "",
              email: "",
              address: "",
              // ... 可能會有更多的值
            });
          
            const handleChange = (e) => {
              const { name, value } = e.target;
              setFormData((prevState) => ({ ...prevState, [name]: value }));
            };
          
            return (
              <>
                <label>First Name:</label>
                <input
                  type="text"
                  name="firstName"
                  value={formData.firstName}
                  onChange={handleChange}
                />
                <label>Last Name:</label>
                <input
                  type="text"
                  name="lastName"
                  value={formData.lastName}
                  onChange={handleChange}
                />
                <label>Email:</label>
                <input
                  type="email"
                  name="email"
                  value={formData.email}
                  onChange={handleChange}
                />
                <label>Address:</label>
                <input
                  type="text"
                  name="address"
                  value={formData.address}
                  onChange={handleChange}
                />
                {/* ... 可能會有更多的字段 */}
              </>
            );
          }
          

          與受控表單不同的是,非受控表單不在 state 中存儲表單的值。相反,非受控表單使用原生 HTML 內(nèi)置的 <form> 的功能和 JavaScript 去管理數(shù)據(jù)。

          舉例來說,瀏覽器會幫我們管理狀態(tài),我們無需在每次 input 改變時(shí)使用 setState 更新 state 并把 state 設(shè)置到 inputvalue 屬性上,我們的組件不再需要或使用這些 state

          當(dāng)組件渲染時(shí),React 會將 onSubmit 監(jiān)聽器添加到表單上。當(dāng)提交按鈕被點(diǎn)擊時(shí),我們的 handleSubmit 函數(shù)會被執(zhí)行。與使用 state相比,它更接近于不使用任何 JavaScript 的普通 HTML 表單的工作方式。

          function UncontrolledForm() {
          
            const handleSubmit = (event) => {
              event.preventDefault();
          
              const formData = new FormData(event.target);
              const inputValue = formData.get('inputName');
          
              sendInputValueToApi(inputValue).then(() => /* 業(yè)務(wù)邏輯... */)  
            };
          
            return (
              <form onSubmit={handleSubmit}>
                <input type="text" name="inputName" />
                <button type="submit">Send</button>
              </form>
            );
          }
          

          使用非受控表單的一個(gè)好處就是會減少大量的冗余代碼:

          // 受控
          const [value, setValue] = useState('')
          
          const handleChange = (event) => {
            setValue(event.target.value);
          }
          ...
          <input type="text" value={value} onChange={handleChange} />
          
          // 非受控
          <input type="text" name="inputName" />
          

          即便只有 1 個(gè) input,區(qū)別也是非常顯著的,當(dāng)有許多 input 時(shí),效果會更加明顯:

          function UncontrolledForm() {
            const handleSubmit = (event) => {
              event.preventDefault();
              const formData = new FormData(event.target);
              const { name, email, address } = Object.fromEntries(formData);
            };
          
            return (
              <form onSubmit={handleSubmit}>
                <label>First Name:</label>
                <input type="text" name="firstName" />
          
                <label>Last Name:</label>
                <input type="text" name="lastName" />
          
                <label>Email:</label>
                <input type="email" name="email" />
          
                <label>Address:</label>
                <input type="text" name="address" />
                {/* ... 可能會有更多的字段 */}
                <button type="submit">Submit</button>
              </form>
            );
          }
          

          非受控表單與受控表單相比,沒有許多冗余的代碼,并且我們不需要手動(dòng)管理許許多多的 state 或一個(gè)巨大的對象。事實(shí)上,這里根本沒有 state。這個(gè)表單可以有成百上千個(gè)子組件但它們不會導(dǎo)致彼此重新渲染。使用這種方式,會讓表單性能變的更好、減少大量的冗余代碼并且使我們代碼的可讀性更強(qiáng)。

          非受控表單的不足之處是你無法直接訪問每個(gè) input 的值。這會使自定義校驗(yàn)變的棘手。例如你需要在用戶輸入手機(jī)號的時(shí)候格式化手機(jī)號。

          注意事項(xiàng)

          不要使用useRef

          許多文章推薦在非受控表單的每個(gè) input 上使用一個(gè) ref 而不是使用 new FormData(),我認(rèn)為原因是 FormData API 很少人知道。然而,它在大約十年前已經(jīng)成為了一個(gè)標(biāo)準(zhǔn)并且已經(jīng)被所有主流瀏覽器支持。

          我強(qiáng)烈建議你不要為表單使用 useRef,因?yàn)樗鼤?useState 一樣引入許多相同的問題和冗余的代碼。

          然而,確實(shí)有一些場景,ref 可以幫助我們。

          1. 聚焦字段時(shí)
          function MyForm() {
            const inputRef = useRef(null);
          
            const focusInput = () => {
              inputRef.current.focus();
            };
          
            return (
              <form>
                <input ref={inputRef} type="text" />
                <button type="button" onClick={focusInput}>
                  Focus Input
                </button>
              </form>
            );
          }
          
          1. 調(diào)用子組件的方法時(shí)
          const ChildComponent = React.forwardRef((props, ref) => (
            <input ref={ref} type="text" />
          ));
          
          function MyForm() {
            const inputRef = useRef(null);
          
            const focusInput = () => {
              inputRef.current.focus();
            };
          
            return (
              <form>
                <ChildComponent ref={inputRef} />
                <button type="button" onClick={focusInput}>
                  Focus Input
                </button>
              </form>
            );
          }
          
          1. 其他的例如保存 useEffect 的前一個(gè)值或測量一個(gè)元素大小時(shí)

          混合受控與非受控

          在許多場景中,你可能需要控制一個(gè)或更多的 input,當(dāng)用戶在輸入手機(jī)號碼時(shí)對其進(jìn)行格式化是一個(gè)非常棒的例子。在這些場景中,即便你正在使用非受控表單,你也可以使用一個(gè)受控的 input。在這種情況下,也不要使用 state 去訪問 input 的值,繼續(xù)使用 new FormData(...),僅僅使用 state 去管理相關(guān)輸入的展示。

          function MixedForm() {
            const [phoneNumber, setPhoneNumber] = useState("");
          
            const handlePhoneNumberChange = (event) => {
              // 格式化手機(jī)號
              const formattedNumber = formatPhoneNumber(event.target.value);
              setPhoneNumber(formattedNumber);
            };
          
            const handleSubmit = (event) => {
              event.preventDefault();
              const formData = new FormData(event.target);
              for (let [key, value] of formData.entries()) {
                console.log(`${key}: ${value}`);
              }
            };
          
            return (
              <form onSubmit={handleSubmit}>
                <label>Name:</label>
                <input type="text" name="name" />
          
                <label>Email:</label>
                <input type="email" name="email" />
          
                <label>Phone Number:</label>
                <input
                  type="tel"
                  name="phoneNumber"
                  value={phoneNumber}
                  onChange={handlePhoneNumberChange}
                />
          
                <label>Address:</label>
                <input type="text" name="address" />
          
                <button type="submit">Submit</button>
              </form>
            );
          }
          
          function formatPhoneNumber(number) {
            return number.replace(/\D/g, "").slice(0, 10);
          }
          

          注意:盡量減少 state,在這個(gè)例子中,你不會既想要一個(gè)保存原始電話號碼的 useState,又想要一個(gè)用于格式化電話號碼的 useState,并且因?yàn)橥剿鼈冞€會帶來多余的重新渲染的效果。

          談到重新渲染優(yōu)化,我們可以將受控 input 抽離出來以此來減少重新渲染對表單其余部分的影響。

          const PhoneInput = () => {
            const [phoneNumber, setPhoneNumber] = useState("");
          
            const handlePhoneNumberChange = (event) => {
              const formattedNumber = formatPhoneNumber(event.target.value);
              setPhoneNumber(formattedNumber);
            };
          
            return (
              <input
                type="tel"
                name="phoneNumber"
                value={phoneNumber}
                onChange={handlePhoneNumberChange}
              />
            );
          };
          
          function MixedForm() {
            const handleSubmit = (event) => {
              event.preventDefault();
              const formData = new FormData(event.target);
              for (let [key, value] of formData.entries()) {
                console.log(`${key}: ${value}`);
              }
            };
          
            return (
              <form onSubmit={handleSubmit}>
                <label>Name:</label>
                <input type="text" name="name" />
          
                <label>Email:</label>
                <input type="email" name="email" />
          
                <label>Phone Number:</label>
                <PhoneInput />
          
                <label>Address:</label>
                <input type="text" name="address" />
          
                <button type="submit">Submit</button>
              </form>
            );
          }
          
          function formatPhoneNumber(number) {
            return number.replace(/\D/g, "").slice(0, 10);
          }
          

          如果你用過受控 input,那么看完上面代碼之后,你可能會想:“沒有傳遞任何 setStateref, 父組件是如何知道子組件的值”。為了理解這個(gè)問題,請記住,當(dāng) React 代碼被渲染成 HTML 時(shí),瀏覽器只會看到 Form 和它里面的 inputs,包括 <PhoneInput /> 渲染的 input

          我們的組件組合方式對我們渲染的 HTML 沒有功能上的影響。因此,那個(gè) input 的值會像其他字段一樣被包含在 FormData 中。這就是組件組合和封裝的力量。我們可以將重新渲染控制在最小影響范圍內(nèi),與此同時(shí),DOM 依然像原生 HTML 一樣呈現(xiàn)。

          等等... 我如何在非受控 input 中做校驗(yàn)?

          考慮到這個(gè)問題的并非只有你一個(gè)!當(dāng)在提交前需要校驗(yàn)時(shí),React 開發(fā)者往往會傾向于去使用受控組件。

          許多開發(fā)者并沒有意識到,你并不需要 React 或自定義的 JavaScript 做這些校驗(yàn)。事實(shí)上,有一些原生的屬性已經(jīng)支持了這些事情。請參閱 MDN 查看更多的細(xì)節(jié):https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation

          在不使用任何 JavaScript 的前提下,你可以設(shè)置 input 必填、設(shè)置長度限制和用正則表達(dá)式設(shè)置格式要求。

          錯(cuò)誤處理

          在相關(guān)的討論中,在我們需要在客戶端展示錯(cuò)誤信息的時(shí)候,開發(fā)者通常會選擇受控表單來解決這個(gè)問題。然而,我會優(yōu)先選擇使用非受控組件并在我的 onSubmit 函數(shù)里面做校驗(yàn)和錯(cuò)誤管理,而不是使用受控組件并在每次 input 改變時(shí)更新對應(yīng)的 state。這種方式可以盡量減少 statesetState 的數(shù)量。

          function UncontrolledForm() {
            const [errors, setErrors] = useState({});
          
            const handleSubmit = (event) => {
              event.preventDefault();
              const formData = new FormData(event.target);
          
              let validationErrors = {};
          
              // 自定義校驗(yàn):確保郵箱的域名是:"example.com"
              const email = formData.get("email");
              if (email && !email.endsWith("@example.com")) {
                validationErrors.email = "Email must be from the domain example.com.";
              }
          
              if (formData.get("phoneNumber").length !== 10) {
                validationErrors.phoneNumber = "Phone number must be 10 digits.";
              }
          
              if (Object.keys(validationErrors).length > 0) {
                setErrors(validationErrors);
              } else {
                console.log(Array.from(formData.entries()));
                 // 清空之前的值
                setErrors({});
              }
            };
          
            return (
              <form onSubmit={handleSubmit}>
                <label>Name:</label>
                <input type="text" name="name" required />
                {errors.name && <div className="error">{errors.name}</div>}
          
                <label>Email (must be @example.com):</label>
                <input type="email" name="email" required />
                {errors.email && <div className="error">{errors.email}</div>}
          
                <label>Phone Number (10 digits):</label>
                <input type="tel" name="phoneNumber" required pattern="\d{10}" />
                {errors.phoneNumber && <div className="error">{errors.phoneNumber}</div>}
          
                <button type="submit">Submit</button>
              </form>
            );
          }
          
          export default UncontrolledForm;
          

          服務(wù)端組件中的 Form

          React Server Components(RSC) 使用服務(wù)端框架去渲染部分組件,通過這種辦法可以減少瀏覽器訪問你網(wǎng)站時(shí)下載的 JavaScript 的數(shù)量。這可以顯著地提升你網(wǎng)站的性能。

          RSC 對我們編寫表單的方式有很大的影響。因?yàn)椋瑢τ谑状武秩緛碚f,如果我們沒有使用 state,它們可以在服務(wù)端被渲染并不附帶任何 JavaScript 文件給瀏覽器。這意味著,非受控表單即使在沒 JavaScript的情況下也可以交互,意味著它們可以更早的工作而不用等待 JavaScript 去下載然后運(yùn)行。這可以讓你的網(wǎng)站體驗(yàn)更加絲滑。

          使用 Next.js,你可以在你的表單中使用 Server Actions,因此你不需要去為了你的表單交互寫一個(gè) API。你需要準(zhǔn)備的只是一個(gè)事件處理函數(shù)。你可以在 Next.js 的文檔中找到關(guān)于這個(gè)主題的更多內(nèi)容或者觀看是 Lee 的視頻。

          如果你要在 RSC 中混合一些受控表單,請確保把它們抽離為單獨(dú)的客戶端組件,就像上面的 <PhoneInput /> 一樣。這可以盡可能的減少需要打包的 JavaScript 文件。

          // page.jsx
          import { PhoneInput } from "./PhoneInput";
          
          export default function Page() {
            async function create(formData: FormData) {
              "use server";
          
              // ... use the FormData
            }
          
            return (
              <form action={create}>
                <label>Name:</label>
                <input type="text" name="name" />
          
                <label>Email:</label>
                <input type="email" name="email" />
          
                <label>Phone Number:</label>
                <PhoneInput />
          
                <label>Address:</label>
                <input type="text" name="address" />
          
                <button type="submit">Submit</button>
              </form>
            );
          }
          
          // PhoneInput.jsx
          "use client";
          
          function formatPhoneNumber(number) {
            return number.replace(/\D/g, "").slice(0, 10);
          }
          
          import { useState } from "react";
          
          export const PhoneInput = () => {
            const handlePhoneNumberChange = (event) => {
              const formattedNumber = formatPhoneNumber(event.target.value);
              setPhoneNumber(formattedNumber);
            };
            const [phoneNumber, setPhoneNumber] = useState("");
          
            return (
              <input
                type="tel"
                name="phoneNumber"
                value={phoneNumber}
                onChange={handlePhoneNumberChange}
              />
            );
          };
          

          Form 庫

          React 生態(tài)中有許多為受控表單設(shè)計(jì)的優(yōu)秀的庫。最近我一直在使用 React Hook Form 來處理這些應(yīng)用,不過我更傾向于使用非受控表單,因?yàn)椴恍枰~外的庫來管理表單狀態(tài)。(一些流行的庫:React Hook FormFormikInformed

          總結(jié)、對比和推薦

          因?yàn)?Google 搜索 react forms 時(shí)排名靠前的文章令人感到困惑、過時(shí)或具有誤導(dǎo)性,因此我寫了本文。

          • 其中一篇文章說:“React 中更通用的方式是受控表單”,我不認(rèn)為受控或非受控誰更通用。實(shí)際上,正如上文所述,這兩種類型都有其用武之地。事實(shí)上,許多舊文章都推薦使用受控表單,同時(shí)理由同樣含糊不清或具有誤導(dǎo)性。
          • 沒有一篇排名靠前的文章使用 FormData。對于非受控表單,至少兩篇文章推薦使用 useRef,這同樣會讓你的代碼變的不靈活且臃腫。
          • 一些排名靠前的文章仍然在使用類組件,沒有提到函數(shù)式組件。

          一些總結(jié)性的看法:

          1. 以我的經(jīng)驗(yàn)來看,許多表單都是受控和不受控混合的。我們之所以有這兩種選擇,是因?yàn)槲覀冇徐`活性,不應(yīng)該教條主義。我們可以使用同時(shí)使用它們,就像上面的 RSC 例子一樣。
          2. 時(shí)至今日,我更偏愛于使用非受控表單,我認(rèn)為這會簡化代碼結(jié)構(gòu)并優(yōu)化性能。
          3. 認(rèn)真的說,在 onSubmit 函數(shù)中使用 new FormData(...)而不要使用 useRef
          4. 封裝和組合受控表單去盡量減少 state 更新對其他組件的影響,并依靠組合后的 DOM 來處理提交事件。

          作者:郜克帥

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

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


          主站蜘蛛池模板: 亚洲精品国产suv一区88| 99久久国产精品免费一区二区| 亚洲一区二区三区在线| 97精品国产福利一区二区三区| 国产成人精品一区二区A片带套| 狠狠色综合一区二区| 精品女同一区二区三区免费站| 日韩电影一区二区三区| 中文字幕aⅴ人妻一区二区| 丝袜人妻一区二区三区网站| 国产成人精品一区二三区在线观看| 日韩精品一区二三区中文| 福利一区二区视频| 亚洲爽爽一区二区三区| 日韩精品无码一区二区三区AV| 不卡一区二区在线| 国产精品高清一区二区三区| 国产成人无码一区二区在线播放 | 一区二区三区在线|欧| 国产成人精品一区二区秒拍 | 亚洲一区二区三区免费视频| 国产精品合集一区二区三区| 日本精品无码一区二区三区久久久 | 午夜在线视频一区二区三区 | 久久久久人妻一区精品| 亚洲AV无码一区二区乱孑伦AS| 性无码免费一区二区三区在线| 亚洲一区免费视频| 日韩精品一区在线| 国产一区二区三区在线影院| 亚洲AV美女一区二区三区 | 国产一区二区三区免费在线观看| 精品视频一区二区观看| 中文字幕在线无码一区二区三区| 亚洲国产精品一区| 四虎在线观看一区二区| 亚洲综合av永久无码精品一区二区| 无码毛片视频一区二区本码| 韩国女主播一区二区| 久久久久人妻一区精品| 国产精品一区在线播放|