整合營銷服務(wù)商

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

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

          前端常見的適配方式的方法

          前端常見的適配方式的方法

          為一個(gè)真正的前端工作者適配是一個(gè)老生常談的問題,那么今天給大家總結(jié)一下在工作當(dāng)中常用做適配的方式。

          一、固定布局(pc端)(靜態(tài)布局)

          以像素作為頁面的基本單位,不管設(shè)備和瀏覽器寬度,只設(shè)計(jì)一套尺寸

          二、根據(jù)不同根據(jù)不同的分辨率,加載不同的CSS樣式文件(可切換的固定布局)自適應(yīng)布局

          <script>
          // 分辨率大于等于1680,大部分為1920的情況下,調(diào)用此css
          if(window.screen.width >=1680){
          document.write('<link rel="stylesheet" href="css/index_1920.css">');
          }
          // 分辨率再在1600-1680的情況下,調(diào)用此css
          else if(window.screen.width >=1600){
          document.write('<link rel="stylesheet" href="css/index_1600.css">');
          }
          // 分辨率小于1600的情況下,調(diào)用此css
          else{
          document.write('<link rel="stylesheet" href="css/index.css">');
          }
          </script>
          

          注意:注意這里的js一定要寫在標(biāo)簽里面,這樣在加載頁面內(nèi)容之前,可以提前把css樣式表加載出來

          媒體查詢

          媒體查詢可以讓我們根據(jù)設(shè)備顯示器的特性(如視口寬度、屏幕比例、設(shè)備方向:橫向或縱向)為其設(shè)定CSS樣式,媒體查詢由媒體類型和一個(gè)或多個(gè)檢測媒體特性的條件表達(dá)式組成。



          link元素中的CSS媒體查詢

          <link rel="stylesheet" media="(max-width: 800px)" href="example.css" />
          

          css3

          必須設(shè)置<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

          viewport:視口

          width=device-width:就將布局視口設(shè)置成了理想的視口。

          initial-scale:[0,10] 初始縮放比例,1表示不縮放

          maximum-scale:[0,10] 最小縮放比例

          maximum-scale: [0,10] 最大縮放比例

          user-scalable: yes/no 是否允許手動縮放頁面,默認(rèn)值為yes

          語法:

          @media mediatype and|not|only (media feature) {

          CSS-Code;

          }

          第一種方法是css2媒體查詢用法,最大的弊端是他會增加頁面http的請求次數(shù),增加了頁面負(fù)擔(dān),

          我們一般用第二種方法CSS3把樣式都寫在一個(gè)文件里面才是最佳的方法。

          但是媒體查詢的缺點(diǎn)也很明顯,如果在瀏覽器大小改變時(shí),需要改變的樣式太多,那么多套樣式代碼會很繁瑣。

          三、rem布局(彈性布局)移動端

          rem(font size of the root element)是指相對于根元素 (html)的字體大小的單位。

          1、媒體查詢結(jié)合rem布局

          媒體查詢動態(tài)修改根元素的大小,使得rem 一直在跟著變化,響應(yīng)式就成功了。

          為什么使用媒體查詢結(jié)合rem布局

          一個(gè)物體在一個(gè)超大空間中顯得很小,但是在小的空間存放不下,為使得頁面布局不管在什么設(shè)備上都是正常,協(xié)調(diào)的情況,就會采用媒體查詢 + rem,來根據(jù)不同的設(shè)備去相應(yīng)的改變元素的大小。

          媒體查詢結(jié)合rem布局的缺點(diǎn)

          計(jì)算起來太繁瑣

          2、flexble.js和rem布局:

          用法:首先在頁面導(dǎo)入flexible.js導(dǎo)入 去掉mete標(biāo)簽(禁止縮放)的設(shè)置

          當(dāng)設(shè)計(jì)圖為750px;從設(shè)計(jì)圖量出header為88px;

          css設(shè)計(jì)為header的height 88/100=0.88rem;

          3、rem+VW布局

          必須設(shè)置<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

          為了方便計(jì)算可把html中font-size設(shè)置成100px;但是100px固定大小不會自動適配。所以把100px轉(zhuǎn)換成vw

          100px=?vw等于他的視口寬度

          假如:設(shè)備:640px dpr:2 320px

          因?yàn)?100vw=320px

          所以 1vw=3.2px;

          所以 100px=31.25vw

          假如 設(shè)備750px dpr為2 375px

          因?yàn)?100vw=375px;

          所以 1vw=3.75px;

          所以 100px=26.67vw

          當(dāng)設(shè)備640px :31.25vw

          當(dāng)設(shè)備750px :26.67vw

          注意文字要單獨(dú)設(shè)置單位為px,否則文字為100px太大,要用媒體查詢的方式改變文字大小。

          優(yōu)點(diǎn):理想狀態(tài)是所有屏幕的高寬比和最初的設(shè)計(jì)高寬比一樣,或者相差不多,完美適應(yīng)。

          缺點(diǎn):這種rem+js只不過是寬度自適應(yīng),高度沒有做到自適應(yīng),一些對高度,或者元素間距要求比較高的設(shè)計(jì),則這種布局沒有太大的意義。如果只是寬度自適應(yīng),更推薦響應(yīng)式設(shè)計(jì)。

          四、百分比布局 (流式布局)

          以百分比作為頁面的基本單位,可以適應(yīng)一定范圍內(nèi)所有的尺寸的設(shè)備屏幕及瀏覽器寬度,并能完美利用有效空間展現(xiàn)最佳效果

          五、混合式布局

          同彈性布局類似,可以適應(yīng)一定范圍內(nèi)所有尺寸的設(shè)備屏幕及瀏覽器寬度,并能完美利用有效空間展現(xiàn)最佳效果;只是混合像素、和百分比兩種單位作為頁面單位

          六、響應(yīng)式布局

          1、布局特點(diǎn):每個(gè)屏幕分辨率下面會有一個(gè)布局樣式,即元素位置和大小都會變。

          2、設(shè)計(jì)方法:媒體查詢+流式布局。通常使用 @media 媒體查詢 和網(wǎng)格系統(tǒng) (Grid System) 配合相對布局單位進(jìn)行布局,實(shí)際上就是綜合響應(yīng)式、流動等上述技術(shù)通過 CSS 給單一網(wǎng)頁不同設(shè)備返回不同樣式的技術(shù)統(tǒng)稱。

          優(yōu)點(diǎn):適應(yīng)pc和移動端,如果足夠耐心,效果完美

          缺點(diǎn):(1)媒體查詢是有限的,也就是可以枚舉出來的,只能適應(yīng)主流的寬高。(2)要匹配足夠多的屏幕大小,工作量不小,設(shè)計(jì)也需要多個(gè)版本。

          響應(yīng)式頁面在頭部會加上這一段代碼:

          <meta name="applicable-device" content="pc,mobile">
          <meta http-equiv="Cache-Control" content="no-transform ">
          

          以上就是給大家介紹的關(guān)于前端常見適配方式的方法,想了解更多web前端行業(yè)動態(tài)和學(xué)習(xí)資料,可以關(guān)注“武漢千鋒”微信公眾號!

          這節(jié)開始,我們看看解析器如何對邏輯控制語句,例如if else while do while goto等語句進(jìn)行相應(yīng)的語法解析。

          IF Else語句的語法解析

          我們先看看if else的語法定義:

          if_statement->IF LP test RP statement
          if_else_statment->if_statement
          if_else_statement->if_else_statement ELSE statement
          statement->if_else_statement
          test->expr
          decl->var_decl EQUAL initializer
          initializer->expr

          如果c編譯器遇到了下面的語句

          if(i<0){
            i=1;
          }else if(i==0){
          	i=2;
          }else{
          	i=3;
          }

          最開始的if(i<0),則對應(yīng)表達(dá)式

          if_statement->if LP test RP statement

          括號中間的 i < 0, 對應(yīng)于語法中的TEST, 如果if 后面跟著else 關(guān)鍵字的話,像上面的例子, 那么代碼:
          if (i < 0)

          i=1;

          else

          這部分對應(yīng)語法表達(dá)式:

          IF_ELSE_STATEMENT ->IF_ELSE_STATEMENT ELSE STATEMENT

          中的 IF_ELSE_STATEMENT ELSE 這部分, 剩下的部分:

          if (i==0)

          i=2;

          else

          i=3;

          則對應(yīng) STATEMENT , 其實(shí)這部分先是對應(yīng):

          F_ELSE_STATEMENT ->IF_ELSE_STATEMENT ELSE STATEMENT

          然后再由:
          STATEMENT -> IF_ELSE_STATEMENT
          回歸到STATEMENT.

          下面我們看一個(gè)具體的分析實(shí)例:

          void f() {
           int a=0;
           int i=0;
           if (i < 1)
             a=1;
           else if (i < 2)
             a=2;
           else 
             a=3;
          }

          對應(yīng)語句:
          int a=0;
          int i=0;
          解析過程前面章節(jié)已經(jīng)詳細(xì)描述,這里不再分析,我們看看if 部分語句的分析:

          1: 讀入關(guān)鍵字if, 返回標(biāo)簽IF

          2: 讀入左括號,返回標(biāo)簽LP

          3: 讀入變量i, 返回對應(yīng)標(biāo)簽NAME,根據(jù)表達(dá)式:

          UNARY -> NAME

          BINARY -> UNARY

          進(jìn)行遞歸

          4:讀入符號 <, 得到標(biāo)簽 RELOP

          5: 讀入數(shù)字1, 返回標(biāo)簽NUMBER

          6: 根據(jù)下面表達(dá)式進(jìn)行連續(xù)遞歸:

          UNARY -> .NUMBER

          BINARY -> .UNARY

          BINARY -> .BINARY RELOP BINARY

          NO_COMMA_EXPR -> .BINARY

          EXPR -> .NO_COMMA_EXPR

          TEST -> .EXPR

          也就是說括號內(nèi)的表達(dá)式 i < 0, 被推導(dǎo)為 TEST -> EXPR

          7: 讀入右括號LP, 如果變量i,返回標(biāo)簽NAME, 接著進(jìn)行下面的表達(dá)式遞歸:

          UNARY -> .NAME

          BINARY -> .UNARY

          NO_COMMA_EXPR -> .BINARY

          8: 讀入等號,得到標(biāo)簽 EQUAL, 繼續(xù)讀入數(shù)值1,然后根據(jù)下面表達(dá)式進(jìn)行遞歸:

          UNARY -> .NUMBER

          BINARY -> .UNARY

          NO_COMMA_EXPR -> .BINARY

          9: 此時(shí)滿足表達(dá)式:

          NO_COMMA_EXPR -> .NO_COMMA_EXPR EQUAL NO_COMMA_EXPR

          因此根據(jù)該表達(dá)式再次遞歸。

          然后再根據(jù)表達(dá)式:

          EXPR -> .NO_COMMA_EXPR

          進(jìn)行遞歸。

          10:讀入分號,得到標(biāo)簽SEMI, 此時(shí)EXPR SEMI 正好構(gòu)成表達(dá)式:

          STATEMENT -> .EXPR SEMI

          的右半部分,因此可以進(jìn)行對應(yīng)reduce操作。

          11: 此時(shí)解析堆棧上的所有符號就能滿足表達(dá)式:

          IF_STATEMENT -> .IF LP TEST RP STATEMENT

          因此,當(dāng)前解析的內(nèi)容都對應(yīng)非終結(jié)符IF_STATEMENT. 繼續(xù)根據(jù)表達(dá)式:

          IF_ELSE_STATEMENT -> .IF_STATEMENT

          再遞歸一次。

          12: 接下來的else 后面部分,其解析過程跟前面解析if相關(guān)語句的流程是一樣的。先把關(guān)鍵字else讀入,得到標(biāo)簽ELSE. 后面又是if else 的重復(fù),所以解析過程跟前面步驟是一樣的。

          13: 讀入最后一個(gè)else, 然后讀入變量名i, 根據(jù)以下表達(dá)式進(jìn)行遞歸:

          UNARY -> .NAME

          BINARY -> .UNARY

          NO_COMMA_EXPR -> .BINARY

          14: 讀入等號得到標(biāo)簽EQUAL, 讀入數(shù)值3,根據(jù)下面表達(dá)式進(jìn)行遞歸:

          UNARY -> .NUMBER

          BINARY -> .UNARY

          NO_COMMA_EXPR -> .BINARY

          15: 此時(shí)表達(dá)式:

          NO_COMMA_EXPR -> .NO_COMMA_EXPR EQUAL NO_COMMA_EXPR

          的右邊可以滿足,于是就可以進(jìn)行對應(yīng)的遞歸操作。

          然后再次依賴表達(dá)式:

          EXPR -> .NO_COMMA_EXPR

          再進(jìn)行一次遞歸。

          16:讀入接下來的分號,得到標(biāo)簽SEMI,然后又可以根據(jù)下面表達(dá)式進(jìn)行遞歸:

          STATEMENT -> .EXPR SEMI

          17:得到STATEMENT后,IF_ELSE_STATEMENT 的語法表達(dá)式右邊部分又得到滿足:

          IF_ELSE_STATEMENT -> .IF_ELSE_STATEMENT ELSE STATEMENT

          這樣的話,整個(gè)if … else if … else …. 這段代碼就全被上面的語法表達(dá)式給吸收了。

          18: 最后再跟進(jìn)表達(dá)式:

          STATEMENT -> .IF_ELSE_STATEMENT

          進(jìn)行遞歸,這也說明,if else 相關(guān)語法是對應(yīng)于STATEMENT這個(gè)非終結(jié)符的。

          后續(xù)的解析流程跟前面分析的一樣,最后解析器會進(jìn)入到接收狀態(tài),這表明我們給的的語法能夠正確的解析if else if 這種條件轉(zhuǎn)移控制代碼。

          我們開發(fā)系統(tǒng)的時(shí)候,可能會接到這樣的需求:不要讓用戶復(fù)制頁面上的文字或者圖片,不要讓用戶調(diào)試我們的頁面,更甚至也不要讓用戶進(jìn)行打印操作等等。

          聽起來是不是讓人很頭大,這咋實(shí)現(xiàn)???這有必要嗎?這能禁住么?

          如果你沒做過這些,或者沒接到過這樣的需求,那你也應(yīng)該看到過某個(gè)網(wǎng)站做了一些這樣的措施。

          既然要做,我們就得想方案,先來看看禁止復(fù)制都有哪些方法。

          禁止復(fù)制

          假設(shè)我們有這樣一段代碼:

          <div style="padding-left: 56px;">
            <textarea rows="5" cols="33"></textarea>
          </div>
          <pre>
            ??驼勫?,煙濤微茫信難求,
          
            越人語天姥,云霞明滅或可睹。
          
            天姥連天向天橫,勢拔五岳掩赤城。
          
            天臺四萬八千丈,對此欲倒東南傾。
          
            我欲因之夢吳越,一夜飛度鏡湖月。
          
            湖月照我影,送我至剡溪。
            </pre>

          接下來就通過這個(gè)例子來論述我們的方案:

          x效果

          1. 通過user-select:none

          這是一個(gè)css屬性,標(biāo)識了元素及其子元素的文本不可被選中,因此設(shè)定之后,文本將不能夠被選中,因此也就不能復(fù)制:

          <pre style="user-select: none;">
            ??驼勫?,煙濤微茫信難求,
          
            越人語天姥,云霞明滅或可睹。
          
            天姥連天向天橫,勢拔五岳掩赤城。
          
            天臺四萬八千丈,對此欲倒東南傾。
          
            我欲因之夢吳越,一夜飛度鏡湖月。
          
            湖月照我影,送我至剡溪。
            </pre>

          我們在這段文本上,加上這個(gè)樣式。

          效果

          可以看到,文字壓根就不能選擇,從鼠標(biāo)形狀也能看出來。

          1. 通過攔截copy操作

          由于用在進(jìn)行復(fù)制操作的時(shí)候,會觸發(fā)copy事件,我們可以通過監(jiān)聽它來做一些處理,使得復(fù)制的行為發(fā)生改變:

          <div>
            <div style="padding-left: 56px;">
              <textarea rows="5" cols="33"></textarea>
            </div>
            <pre id="content">
              ??驼勫?,煙濤微茫信難求,
          
              越人語天姥,云霞明滅或可睹。
          
              天姥連天向天橫,勢拔五岳掩赤城。
          
              天臺四萬八千丈,對此欲倒東南傾。
          
              我欲因之夢吳越,一夜飛度鏡湖月。
          
              湖月照我影,送我至剡溪。
            </pre>
          </div>
          <script>
            let c=document.getElementById('content')
            c.removeEventListener("copy", copyFilter)
            c.addEventListener("copy", copyFilter)
            function copyFilter(e) {
              let cp=e.clipboardData || window.clipboardData
              if(!cp) {
                return
              }
              let text=window.getSelection().toString()
              if(text) {
                e.preventDefault()
                cp.setData("text/plain", "你復(fù)制了一段魔法")
              }
            }
          </script>

          先獲取到我們要禁止復(fù)制的元素,然后給它添加一個(gè)copy的事件監(jiān)聽,在添加監(jiān)聽之前,要先移除一下,這樣是為了避免局部刷新的時(shí)候重復(fù)添加,然后我們通過copyFilter函數(shù)來對這次操作進(jìn)行處理。

          先獲取剪貼板對象,如果當(dāng)前事件對象里不存在,那就從window里面取,然后我們通過getSelection再拿到選取的內(nèi)容,因?yàn)槲覀儗糍N板對象進(jìn)行修改,所以要阻止默認(rèn)行為,然后把剪貼板的內(nèi)容重新賦值,可以是示例中那樣的一段文字,也可以設(shè)置為空,甚至是任意其他內(nèi)容,然后我們就可以看到產(chǎn)生的效果了:

          效果

          雖然能復(fù)制文本,但是由于我們攔截了復(fù)制操作,更改了它的行為,因此再粘貼的時(shí)候就變成了我們更改的樣子,也做到了禁止復(fù)制的功能。

          這種方式對于使用快捷鍵或者右鍵的方式都是有效的。

          1. 通過攔截cut操作

          這種情況主要是在可編輯區(qū)域,比如文本框、文本域、設(shè)置為contenteditable的元素等,用戶可以對文字進(jìn)行剪切操作,雖然上面禁止了復(fù)制,但是剪切是另一個(gè)操作,不攔截的話還是相當(dāng)于能復(fù)制出來。

          copy和cut只是觸發(fā)的事件不同而已,但是它們都是執(zhí)行相同的邏輯處理:

          <div>
            <div style="padding-left: 56px;">
              <textarea rows="5" cols="33"></textarea>
            </div>
            <pre id="content" contenteditable>
              ??驼勫?,煙濤微茫信難求,
          
              越人語天姥,云霞明滅或可睹。
          
              天姥連天向天橫,勢拔五岳掩赤城。
          
              天臺四萬八千丈,對此欲倒東南傾。
          
              我欲因之夢吳越,一夜飛度鏡湖月。
          
              湖月照我影,送我至剡溪。
            </pre>
          </div>
          <script>
            let c=document.getElementById('content')
            c.removeEventListener("cut", copyFilter)
            c.addEventListener("cut", copyFilter)
            function copyFilter(e) {
              let cp=e.clipboardData || window.clipboardData
              if(!cp) {
                return
              }
              let text=window.getSelection().toString()
              if(text) {
                e.preventDefault()
                cp.setData("text/plain", "你復(fù)制了一段魔法")
              }
            }
          </script>

          這里我為了方便,給元素添加了contenteditable屬性,讓它變成可編輯的,copyFilter函數(shù)沒有變化,我們只是添加了一個(gè)剪切事件的監(jiān)聽,然后它們的處理函數(shù)都是copyFilter??聪滦Ч?/p>

          效果

          可以看到,首先我們對文字進(jìn)行剪切,沒有出現(xiàn)預(yù)期的效果,這時(shí)因?yàn)槲覀冊诖a里面對剪切進(jìn)行了攔截,并阻止了它的默認(rèn)行為,然后我們在粘貼的時(shí)候,文字也改變成我們設(shè)置的了。

          1. 通過媒體查詢控制打印

          雖然我們可以通過上面的幾種方法禁止在頁面上復(fù)制,但是用戶也可能開啟打印預(yù)覽模式,在這種情況下,也是可以進(jìn)行復(fù)制的,我們要想對打印頁面進(jìn)行一些控制,那么就要用到媒體查詢,先看下打印的樣子:

          效果

          雖然我們做了限制,但是在打印頁面沒有生效,現(xiàn)在我們針對這個(gè)場景更改一下代碼:

          @media print {
            html {
              display: none;
            }
          }

          通過添加上面這個(gè)樣式規(guī)則,我們能夠使頁面在打印的時(shí)候,內(nèi)容隱藏起來,這樣就無法進(jìn)行復(fù)制了:

          效果

          能夠看到,點(diǎn)擊打印的時(shí)候,預(yù)覽頁面一片空白,,這樣就禁止了在打印頁面進(jìn)行復(fù)制的操作。當(dāng)然了,你其實(shí)也可以設(shè)置其他的樣式屬性來做些控制,但要記住寫在打印的媒體查詢里面,只有這樣才會在打印頁面生效。

          1. 通過偽元素覆蓋內(nèi)容

          還有一種方式就是,通過設(shè)定一個(gè)偽元素,讓它全面覆蓋文本內(nèi)容,這樣鼠標(biāo)就不能選到實(shí)際的文本,改造一下代碼:

          .content {
            position: relative;
          }
          .content::before {
            content: '';
            position: absolute;
            left: 0px;
            top: 0px;
            width: 100%;
            height: 100%;
          }
          <pre id="content" class="content">
            ??驼勫?,煙濤微茫信難求,
          
            越人語天姥,云霞明滅或可睹。
          
            天姥連天向天橫,勢拔五岳掩赤城。
          
            天臺四萬八千丈,對此欲倒東南傾。
          
            我欲因之夢吳越,一夜飛度鏡湖月。
          
            湖月照我影,送我至剡溪。
          </pre>

          現(xiàn)在就不能在元素上面選中文字了,不過用戶也可能還有一些操作,比如在內(nèi)容區(qū)域外面ctrl+a全選,或者在外面拖動鼠標(biāo)來全選,如果是這種情形,那么我們可以通過監(jiān)聽鍵盤和鼠標(biāo)事件來禁止全選等操作。

          1. 通過監(jiān)聽鼠標(biāo)和鍵盤事件

          由于用戶有很多種操作的方式,鍵盤全選、鼠標(biāo)全選、鍵盤右鍵、鼠標(biāo)右鍵等等,我們?nèi)绻F舉的話,情況太多了,因此我們只監(jiān)聽鼠標(biāo)按下和抬起事件,以及鍵盤的按下事件:

          document.removeEventListener("mousedown", haveSelect)
          document.addEventListener("mousedown", haveSelect)
          document.removeEventListener("mouseup", haveSelect)
          document.addEventListener("mouseup", haveSelect)
          document.removeEventListener("keydown", haveSelect)
          document.addEventListener("keydown", haveSelect)
          function haveSelect() {
            window.getSelection().removeAllRanges()
          }

          主要就是在removeAllRanges方法上面,能夠在觸發(fā)上面事件的時(shí)候,將所選區(qū)域清空,也就是不管你選沒選,咋選的,反正就是你只要進(jìn)行了操作,那我就那可能選擇的區(qū)域給你清空,這樣你就啥也干不了了。

          效果

          好,這樣就可以啦,無論怎么選,即使出現(xiàn)了選區(qū),但是只要你再按了鼠標(biāo)或者鍵盤,那么選區(qū)就會直接消失,就能達(dá)到不能復(fù)制的效果,因?yàn)槟惆l(fā)現(xiàn)啥都做不了。

          這里額外說明一下,對于事件的監(jiān)聽,一定要用addEventListener來實(shí)現(xiàn),因?yàn)樗鼤⒍鄠€(gè)綁定的事件都添加上去,當(dāng)觸發(fā)的時(shí)候就會按照綁定的順序進(jìn)行執(zhí)行,如果是用賦值的方式,那么后面的會覆蓋前面的,而且賦值的方式很容易被篡改,可以很輕松的讓你的綁定函數(shù)不能執(zhí)行從而失效,而使用addEventListener就不會被人為覆蓋,只能通過綁定的函數(shù)句柄來手動移除,也就是說要移除的時(shí)候,必須使用跟綁定時(shí)使用同一個(gè)函數(shù)才行。

          通過CSS的方式禁止復(fù)制,可以很容易的被用戶解除,只能是設(shè)置的稍微復(fù)雜一點(diǎn),增加難度。而通過JS的方式禁止復(fù)制,也可以通過禁用頁面JavaScript代碼來解除,因此我們可以將內(nèi)容通過js來渲染,這樣如果頁面禁用了js,那么內(nèi)容也不會渲染。

          禁止調(diào)試

          對于禁止調(diào)試,主要是指用戶打開控制臺,控制臺也就是開發(fā)者工具,我為了方便稱之為控制臺,想要對頁面進(jìn)行調(diào)試時(shí),我們做一些處理,阻止這種行為,最大可能的攔截通過控制臺對系統(tǒng)的調(diào)試。
          主要的方法有幾種,由于打開控制臺是瀏覽器提供的調(diào)試功能,因此我們沒法攔截打開操作,即使通過事件監(jiān)聽不允許快捷鍵這樣做,但是也可以通過其他方式進(jìn)行打開,因此我們的主要關(guān)注點(diǎn)就在于打開控制臺之后,我們能做哪些事情來限制用戶行為。

          1. 禁用快捷鍵

          打開控制臺的快捷鍵主要有F12和ctrl+shift+i,我們先把這倆給禁用了:

          document.removeEventListener("keydown", disableDevShortcut)
          document.addEventListener("keydown", disableDevShortcut)
          function disableDevShortcut(e) {
            console.log(e)
            if(e.keyCode===123) {
              e.preventDefault()
            }else if(e.keyCode===73 && e.ctrlKey && e.shiftKey) {
              e.preventDefault()
            }
          }

          這樣在使用這兩個(gè)快捷鍵的時(shí)候,頁面沒有任何反應(yīng),控制臺也不會喚起,因?yàn)槲覀冏柚沽怂鼈兊哪J(rèn)行為。

          1. 禁用右鍵的檢查功能

          除了通過快捷鍵,還可以使用右鍵的方式,并點(diǎn)擊檢查也會調(diào)出控制臺。

          效果

          這種情景,我們可以通過禁止在頁面上使用右鍵的方式,來阻止打開控制臺:

          document.removeEventListener("contextmenu", cancelContextmenu)
          document.addEventListener("contextmenu", cancelContextmenu)
          function cancelContextmenu(e) {
            e.preventDefault()
          }

          現(xiàn)在就不能通過右鍵打開控制臺了,但是相應(yīng)的整個(gè)右鍵功能也都不能使用了。

          1. 設(shè)置無限斷點(diǎn)

          如果用戶最終打開了控制臺,比如通過在瀏覽器的更多功能中來打開的話,那么我就需要采取其他的措施,其中之一就是給代碼設(shè)置無限斷點(diǎn),因?yàn)閿帱c(diǎn)只在控制臺打開的時(shí)候才會發(fā)生作用,從而不必?fù)?dān)心非調(diào)試模式下的程序正常運(yùn)行。

          無限斷點(diǎn)的主要思路就是利用定時(shí)器等手段,頻繁的觸發(fā)斷點(diǎn)效果,使得不能輕松的調(diào)試程序,先看下代碼:

          ;(()=> {
            function breakDebugger() {
              if(new checkDebugger().check) {
                breakDebugger()
              }
            }
            function checkDebugger() {
              const now=new Date();
              eval('(function () {debugger;false;})()')
              const dur=Date.now() - now
              if(dur < 5) {
                return {check: false}
              }else {
                return {check: true}
              }
            }
            setInterval(()=> {
              eval('(function () {debugger;true;})()')
              breakDebugger()
            }, 500)
          })()

          我們利用一個(gè)立即執(zhí)行的自執(zhí)行函數(shù),來使我們的代碼被封裝在一個(gè)固定塊內(nèi),不與其他部分有任何影響。

          這里主要做了兩步:

          第一步設(shè)置一個(gè)重復(fù)執(zhí)行的定時(shí)器,其中包括了一個(gè)斷點(diǎn)和一個(gè)函數(shù)調(diào)用。

          第二步通過函數(shù)來遞歸調(diào)用斷點(diǎn),主要使用了實(shí)例化對象的方式和時(shí)間差的判斷。

          這樣做的主要作用就是在設(shè)置無限斷點(diǎn)的同時(shí),也能夠讓每次的斷點(diǎn)都是被重新生成的,看下效果,一目了然:

          效果

          發(fā)現(xiàn)沒有,我們通過這種方式,只要打開了控制臺,那么就會進(jìn)入到無限斷點(diǎn)的循環(huán)中,使得不能做任何其他事情,而且每個(gè)斷點(diǎn)的生成都會開辟一個(gè)新的虛擬運(yùn)行環(huán)境,這種情況下,只有關(guān)閉控制臺,才能結(jié)束斷點(diǎn)。

          即使使用右鍵選擇Never parse here,也毫無作用,雖然可以通過Deactive breakpoints按鈕來徹底禁用斷點(diǎn),就是下面這個(gè)按鈕:

          按鈕

          但是,如果這樣做的話,那么用戶也就同時(shí)失去了調(diào)試其他代碼的能力。

          況且,我們接下來還會介紹其他的控制手段,可以配合著使用。

          1. 監(jiān)測控制臺開啟

          我們?nèi)绻苡幸环N手段,可以知道用戶開啟了控制臺,換句話說只要控制臺被打開,就通知我們或者被我們監(jiān)測到,那么我們就可以執(zhí)行一些控制手段,這種效果肯定是很理想的,遺憾的是還沒有這種api暴露給我們?nèi)プ屛覀兡軌蜻@樣做。

          不過我們可以通過其他的方式,利用既有的一些能力來實(shí)現(xiàn)這一點(diǎn),這里我還是使用循環(huán)定時(shí)器,來不斷的去嗅探用戶是否開啟了控制臺,直接看代碼:

          ;(()=> {
            setInterval(function() {
              let foo=document.createElement('a')
              let a1=+new Date()
              console.table(foo)
              let a2=+new Date()
              if(a2 - a1 > 1) {
                location.href='about:blank'
              }
              console.clear()
            }, 500)
          })()

          同樣,通過一個(gè)自執(zhí)行函數(shù),我們開啟了一個(gè)循環(huán)定時(shí)器,然后在回調(diào)方法里面,我們就去實(shí)現(xiàn)上面的目標(biāo),也是分為了兩步:

          第一步創(chuàng)建一個(gè)a元素,然后通過表格的形式將它打印出來,并記錄下消耗的時(shí)間。

          第二步判斷耗時(shí)的長短來控制是否跳轉(zhuǎn)到空白頁,然后清空控制臺。

          這種方式主要是利用了console.table的特性,它會將元素以表格的形式輸出到控制臺,大概就像下面的樣子:

          效果

          由于太多了,我就沒有全部截下來,如果沒有打開控制臺的話,使用console.table輸出我們創(chuàng)建的a標(biāo)簽是很快的,有多快呢,就是js執(zhí)行一條語句的速度,所以打印a1和a2的時(shí)間間隔非常短,幾乎為0,因?yàn)樗麄兛斓讲畈欢嗍峭瑫r(shí)執(zhí)行的,給大家打印看一下:

          效果

          我們先不打開控制臺,等輸出完畢再打開,很清楚的發(fā)現(xiàn),我們沒打開控制臺的時(shí)候,輸出的a就是它標(biāo)簽,而且時(shí)間間隔是0毫秒。

          現(xiàn)在我們打開控制臺的時(shí)候刷新一下頁面,看看控制臺的輸出:

          效果

          這次就變成了以table的形式輸出a元素,而且它的耗時(shí)明顯增多,不再是0毫秒,而是耗費(fèi)了10毫秒,雖然打開控制臺的時(shí)候多次刷新頁面,每次輸出的毫秒數(shù)是不同的,但是跟關(guān)閉控制臺的時(shí)候輸出的耗時(shí)差距非常明顯,因此我們就可以在這個(gè)上面做文章。

          我在上面的代碼中假定了,只要是大于1毫秒的耗時(shí),那就表示用戶打開了控制臺,然后我們就把頁面給跳轉(zhuǎn)到空白頁,當(dāng)然了你也可以做一些任何你想做的操作,比如彈出一個(gè)提示,或者把body內(nèi)容置空等等等等。

          回到我們上面的代碼,看一下它實(shí)際發(fā)生的作用和帶給我們的效果:

          效果

          哈哈,古德古德,平時(shí)瀏覽一切正常,只要剛一打開控制臺,瞬間頁面就被跳轉(zhuǎn)走了,什么都干不了。這樣我們就通過這種方式,達(dá)到了限制打開控制臺的目的,也就是在當(dāng)前頁沒法調(diào)試,一打開就跳轉(zhuǎn)。

          這種辦法由于是繞路實(shí)現(xiàn)的,那么你可能會有疑問,它穩(wěn)定嗎?會不會誤判,我可以對它絕對放心嗎?

          理論上來說,通過輸出的執(zhí)行時(shí)間是不太能精確掌握的,但是我們可以再做一些其他的措施來逼近真相:

          ;(()=> {
            setInterval(function() {
              let foo=document.createElement('a')
              let a1=+new Date()
              console.table(foo)
              let a2=+new Date()
              if(a2 - a1 > 1) {
                let time=0
                for(let i=0; i < 10; i++) {
                  let a1=+new Date()
                  console.table(foo)
                  let a2=+new Date()
                  time +=a2 - a1
                }
                if(time > 20) {
                  location.href='about:blank'
                }
              }
              console.clear()
            }, 500)
          })()

          我又改造了一下判斷的邏輯,當(dāng)發(fā)現(xiàn)輸出耗時(shí)為2毫秒甚至更多的時(shí)候,我立馬再進(jìn)行一次真?zhèn)闻袛啵簿褪钦f,萬一由于其他的影響,導(dǎo)致我第7行的代碼誤判了,那么我再同步執(zhí)行一個(gè)循環(huán),連續(xù)輸出10次,把他們的耗時(shí)總和計(jì)算出來,然后判斷是否大于20毫秒,如果還是耗時(shí)過高的話,那么就可以非常肯定的知道用戶是打開了控制臺,這個(gè)時(shí)候就可以放心的做一些處理了。

          最后

          其實(shí)限制用戶行為的方法有很多很多,上面列出了一些主要的,多種方法還是要結(jié)合著使用。你也可以自由發(fā)揮,多使用一些其他的手段,也會增加用戶復(fù)制或者調(diào)試的難度,比如防止用戶重寫console的方法,或者清除所有定時(shí)器等。

          甚至也可以將你的內(nèi)容繪制到canvas上面來防止復(fù)制,多加一些js的處理工作,防止禁用js的時(shí)候,我們的代碼不生效,只有在js可用的時(shí)候再去渲染內(nèi)容等。也可以在綜合考慮的情況下加上代碼混淆、代碼加密等措施。

          話說回來,大家都是同路人,何必相互為難,哈哈哈,不過提這個(gè)需求的人也著實(shí)會為難我們,既然提了那就盡力去做,能做到什么程度,只能說是盡量做到極致。

          希望上面的內(nèi)容能夠幫助到你,也希望能夠?qū)δ阌兴鶈l(fā)。

          謝謝


          主站蜘蛛池模板: 一区二区精品在线观看| 中文字幕乱码一区二区免费 | 久久无码一区二区三区少妇 | 国产精品成人一区二区三区| 不卡一区二区在线| 亚洲日本乱码一区二区在线二产线 | 蜜臀Av午夜一区二区三区| 色一情一乱一伦一区二区三欧美| 一区二区三区精品高清视频免费在线播放 | 人妻天天爽夜夜爽一区二区| 国产伦精品一区二区三区免费迷 | 国产一区在线视频| 亚洲日韩一区二区三区| 亚洲狠狠久久综合一区77777 | 日本道免费精品一区二区| 精品久久综合一区二区| 中文字幕一区二区三匹| 国产日韩一区二区三区在线观看| 久久精品一区二区三区中文字幕| 久久久99精品一区二区| 美女免费视频一区二区| 国产一区二区三区日韩精品 | 国精产品一区一区三区有限在线| 国产在线精品一区二区夜色| 天天视频一区二区三区| 好吊妞视频一区二区| 国模视频一区二区| 国产第一区二区三区在线观看| 亚洲熟女www一区二区三区| 日韩一区二区三区四区不卡| 日本一区二区三区日本免费| 国模私拍一区二区三区| 久久精品无码一区二区无码 | 少妇无码一区二区三区| 琪琪see色原网一区二区| 国产福利一区二区三区视频在线| 成人精品视频一区二区三区| 无码aⅴ精品一区二区三区| 精品无码一区二区三区爱欲| 精品国产毛片一区二区无码| 中文字幕AV一区中文字幕天堂 |