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 国产午夜精品一区二区不卡 ,99久久成人国产精品免费,日本一级特黄特色大片免费观看

          整合營(yíng)銷服務(wù)商

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

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

          JavaScript高級(jí) 第二集

          JavaScript高級(jí) 第二集

          1-面向?qū)ο?三大特性

          封裝

          把一些比較散的, 單一的值, 有結(jié)構(gòu)的組裝成為一個(gè)整體

          把一些值隱藏在內(nèi)部, 不暴漏給外界

          案例

          · 表述兩個(gè)學(xué)生的姓名,年齡,學(xué)號(hào) --- 學(xué)習(xí)

          ? 直接定義變量形式

          ? 弊端

          ? 過于分散

          ? 所有變量暴漏外界, 不安全

          ? 易被修改或產(chǎn)生沖突

          ? 表述含義的變量名無法統(tǒng)一

          ? 使用對(duì)象封裝優(yōu)化

          繼承

          獲取已經(jīng)存在的對(duì)象已有屬性和方法的一種方式

          多態(tài)

          多態(tài)表現(xiàn)為

          · 同一操作,作用于不同的對(duì)象,會(huì)產(chǎn)生不同的解釋和行為。

          · 例如: toString()

          ? 不同的對(duì)象, 調(diào)用相同的方法, 產(chǎn)生不同的結(jié)果

          多用于強(qiáng)類型語言中,JavaScript具備與生俱來的多態(tài)特性。

          · // 弱類型: 不同類型之間運(yùn)算, 會(huì)存在隱式轉(zhuǎn)換

          · // 強(qiáng)類型: 不同類型之間運(yùn)算, 需要顯示的轉(zhuǎn)換為同一個(gè)類型進(jìn)行運(yùn)算

          02-面向?qū)ο?構(gòu)造函數(shù)使用-注意事項(xiàng)1

          01-構(gòu)造函數(shù)設(shè)置屬性和方法

          實(shí)例屬性/實(shí)例方法

          · 都是綁定在使用構(gòu)造函數(shù)創(chuàng)建出來的對(duì)象p上; 最終使用的時(shí)候也是使用對(duì)象p來進(jìn)行訪問;

          · 案例

          ? function Person(name, age, doFunc) {

          ? this.name=name;

          ? this.age=age;

          ? this.doFunc=doFunc;

          ? }

          靜態(tài)屬性/靜態(tài)方法

          · 綁定在函數(shù)身上的屬性和方法

          · 注意:

          ? 函數(shù)本質(zhì)也是一個(gè)對(duì)象, 既然是個(gè)對(duì)象, 那么就可以動(dòng)態(tài)的添加屬性和方法

          ? 只要函數(shù)存在, 那么綁定在它身上的屬性和方法, 也會(huì)一直存在

          · 應(yīng)用場(chǎng)景

          ? 記錄總共創(chuàng)建了多少個(gè)人對(duì)象

          ? 方案1

          ? 全局變量

          ? 方案2

          ? 實(shí)例屬性

          ? ?

          ? 方案3

          ? 靜態(tài)屬性/靜態(tài)方法

          概念補(bǔ)充

          · 實(shí)例化

          ? 通過構(gòu)造函數(shù), 構(gòu)造出對(duì)象這個(gè)過程

          · 實(shí)例

          ? 被構(gòu)造出來的對(duì)象

          02-關(guān)于創(chuàng)建出來的對(duì)象類型獲取

          01 獲得內(nèi)置對(duì)象的類型

          · {}

          · [1,2,3]

          02 獲取根據(jù)自己聲明的構(gòu)造函數(shù)創(chuàng)建的對(duì)象

          · p.constructor.name

          · constructor

          ? 對(duì)象的構(gòu)造器

          ? 類似于, 一個(gè)產(chǎn)品上, 關(guān)于廠家的標(biāo)識(shí)

          03-關(guān)于創(chuàng)建出來的對(duì)象類型驗(yàn)證

          instanceof

          03-面向?qū)ο?構(gòu)造函數(shù)使用-注意事項(xiàng)2

          構(gòu)造函數(shù)的調(diào)用

          標(biāo)準(zhǔn)調(diào)用

          · var p=new Person();

          · var p=new Person

          ? 針對(duì)無參數(shù)的情況

          錯(cuò)誤調(diào)用

          · var p=Person();

          ? this會(huì)變

          實(shí)例方法的調(diào)用

          p.run();

          var tmp=p.run;

          · tmp();

          · ?

          總結(jié):

          針對(duì)于函數(shù)內(nèi)部的this

          如果這個(gè)函數(shù)當(dāng)做一般函數(shù)來調(diào)用

          · this則代表這個(gè)函數(shù)的調(diào)用者

          如果這個(gè)函數(shù), 被當(dāng)做構(gòu)造函數(shù)來使用

          · this, 代表構(gòu)造出來的對(duì)象

          04-面向?qū)ο?構(gòu)造函數(shù)-優(yōu)化-方案1

          問題

          每個(gè)實(shí)例的方法一般都是相同的;

          方法本質(zhì)也是一個(gè)對(duì)象

          但是, 之前的方案

          · 針對(duì)于每個(gè)對(duì)象, 都會(huì)產(chǎn)生一個(gè)新的方法對(duì)象

          · 造成資源的浪費(fèi)

          優(yōu)化方案

          方案1:

          · 抽取函數(shù)對(duì)象到全局

          · 構(gòu)造函數(shù)內(nèi)部直接賦值

          05-面向?qū)ο?構(gòu)造函數(shù)-優(yōu)化-方案2

          方案1的問題

          ① 全局變量增多,造成污染

          ② 代碼結(jié)構(gòu)混亂,不易維護(hù)

          ③ 函數(shù)可以不通過對(duì)象直接調(diào)用,封裝性不好

          優(yōu)化方案-原型對(duì)象方法擴(kuò)展

          什么是原型對(duì)象?

          · 1. 每當(dāng)我們聲明了一個(gè)函數(shù)(本質(zhì)對(duì)象); 那么這個(gè)函數(shù)上就會(huì)被附加很多屬性來描述這個(gè)函數(shù)

          · 2. 其中有個(gè)屬性 叫 prototype , 是一個(gè)引用類型, 指向著的對(duì)象, 就被成為原型對(duì)象

          · 3. 原型對(duì)象中, 有一個(gè)屬性 叫 constructor , 指向著關(guān)聯(lián)的函數(shù)

          原型對(duì)象有什么作用?

          · 每次我們通過一個(gè)構(gòu)造函數(shù)創(chuàng)建一個(gè)對(duì)象的時(shí)候, 被創(chuàng)建的對(duì)象, 就會(huì)自動(dòng)添加一個(gè)屬性 __proto__ 來指向構(gòu)造函數(shù)的原型對(duì)象

          · 這個(gè)指向有什么用?

          ? 當(dāng)我們調(diào)用一個(gè)對(duì)象的屬性,或者方法時(shí), 會(huì)先到對(duì)象內(nèi)部查找, 如果查找到了, 就直接調(diào)用

          ? 如果查找不到, 則,根據(jù)這條線, 到原型對(duì)象上面去查找

          ? 原型對(duì)象中如果存在該屬性或方法,那么就直接使用,如果不存在指定的屬性則返回undefined,如果不存在指定的方法則報(bào)錯(cuò)

          · 原型對(duì)象, 可以說是各個(gè)對(duì)象公共的區(qū)域

          · 舉例

          ? 數(shù)組

          具體步驟

          · 代碼

          ? Person.prototype.run=function () {

          ? console.log(this.name, '跑吧, 熊孩子');

          ? }

          · 01-給原型對(duì)象增加一個(gè)方法

          ? 所有的對(duì)象, 都可以根據(jù)關(guān)聯(lián)的線查找到這個(gè)方法

          · 02- 內(nèi)部的this, 是調(diào)用者

          注意:

          如果原型對(duì)象和構(gòu)造對(duì)象的屬性和方法,沖突,會(huì)有什么效果?

          · 就近原則

          通常在創(chuàng)建對(duì)象"之前"設(shè)置構(gòu)造函數(shù)的原型對(duì)象(提供共享的屬性|方法)

          訪問原型對(duì)象的正確方法是 構(gòu)造函數(shù).prototype 而不是 對(duì)象.prototype

          設(shè)置原型對(duì)象的屬性和方法

          · 通過原型對(duì)象來修改

          · 不要通過對(duì)象來修改

          ? ?

          再次概念區(qū)分

          實(shí)例屬性/方法

          · 構(gòu)造函數(shù)內(nèi)部, 綁定的屬性和方法

          靜態(tài)屬性/方法

          · 構(gòu)造函數(shù)自身, 綁定的屬性和方法

          原型屬性/方法

          · 原型對(duì)象, 綁定的屬性和方法

          概念聲明

          函數(shù)的原型對(duì)象

          對(duì)象的原型對(duì)象

          06-面向?qū)ο?對(duì)原型對(duì)象擴(kuò)展屬性和方法

          方式1:

          直接借助對(duì)象的動(dòng)態(tài)性, 添加屬性和方法

          問題:

          · 如果大批量的添加, 冗余代碼比較多, 看起來比較亂

          方式2:

          替換原型對(duì)象

          · 原型對(duì)象本身就是一個(gè)對(duì)象

          · 它與函數(shù), 之間, 是通過函數(shù)的prototype屬性進(jìn)行關(guān)聯(lián)

          · 所以可以直接創(chuàng)建一個(gè)對(duì)象,當(dāng)做是原型對(duì)象, 然后修改函數(shù)的prototype指針指向

          注意:

          · 注意替換原型對(duì)象,和創(chuàng)建對(duì)象的先后順序

          ? 替換在前

          ? 創(chuàng)建在后

          · 替換總結(jié)

          ? ① 當(dāng)替換構(gòu)造函數(shù)的原型對(duì)象的時(shí)候,已經(jīng)使用構(gòu)造函數(shù)創(chuàng)建出來的對(duì)象指向的原型對(duì)象不會(huì)發(fā)生改變

          ? ② 如果是替換了構(gòu)造函數(shù)的原型對(duì)象,那么構(gòu)造函數(shù)的新的原型對(duì)象和舊的原型對(duì)象之間沒有任何關(guān)系

          07-面向?qū)ο?原型對(duì)象屬性-方法的讀取和設(shè)置

          設(shè)置原型對(duì)象屬性/方法

          對(duì)象.屬性=xxx

          · 如果該屬性在對(duì)象中已經(jīng)存在,則修改該屬性的值

          · 如果該屬性在對(duì)象中尚未存在,則新增該屬性

          · 錯(cuò)

          原型對(duì)象.屬性=xxx

          · 一定要獲取到原型對(duì)象來設(shè)置

          · 函數(shù)名.prototype

          特例

          · 如果原型對(duì)象的屬性, 是一個(gè)引用類型的

          · 那么通過對(duì)象也可以修改(操作對(duì)象內(nèi)部屬性)

          ? 影響所有對(duì)象

          訪問原型對(duì)象屬性/方法

          構(gòu)造函數(shù)創(chuàng)建出來的對(duì)象在訪問屬性的時(shí)候,會(huì)先在實(shí)例內(nèi)查找,如果沒有找到則進(jìn)一步到對(duì)應(yīng)的原型對(duì)象中查找

          08-面向?qū)ο?訪問函數(shù)原型對(duì)象的方式

          1. 通過 函數(shù)名.prototype

          2. 通過對(duì)象中的__proto__屬性訪問

          注意點(diǎn)說明

          __proto__是一個(gè)非標(biāo)準(zhǔn)屬性

          即ECMAScript中并不包含該屬性,這只是某些瀏覽器為了方便開發(fā)人員開發(fā)和調(diào)試而提供的一個(gè)屬性,不具備通用性

          建議:在調(diào)試的時(shí)候可以使用該屬性,但不能出現(xiàn)在正式的代碼中

          09-面向?qū)ο?hasOwnProperty和in屬性操作

          in 判斷一個(gè)對(duì)象, 是否擁有某個(gè)屬性(如果對(duì)象身上沒有, 會(huì)到原型對(duì)象里面查找)

          hasOwnProperty: 只到對(duì)象自身查找

          思考

          怎樣判斷一個(gè)屬性僅僅是原型對(duì)象屬性

          10-面向?qū)ο?isPrototypeOf和instanceOf

          isPrototypeOf: 判斷一個(gè)對(duì)象, 是否是某個(gè)實(shí)例的原型對(duì)象

          Person.prototype.isPrototypeOf(p)

          instanceOf : 判斷一個(gè)對(duì)象, 是否是某個(gè)構(gòu)造函數(shù)的原型鏈上

          11-面向?qū)ο?原型完善-constructor

          用于獲取一個(gè)對(duì)象的真實(shí)類型

          提問

          畫出構(gòu)造函數(shù), 實(shí)例, 原型對(duì)象之間的關(guān)系

          怎樣給原型對(duì)象擴(kuò)展屬性和方法?

          1. 直接借助對(duì)象的動(dòng)態(tài)特性

          · 拿到原型對(duì)象

          函數(shù)的宿主可以有哪些?根據(jù)不同的宿主可以稱為什么方法?

          篇文章是為ReactJs小白準(zhǔn)備的,希望他們快速抓住ReactJs的要點(diǎn)并能在實(shí)踐中隨機(jī)應(yīng)變。

          兩句話版本

          • ReactJs把視圖更新簡(jiǎn)化為一個(gè)render函數(shù)
          • render函數(shù)接收兩個(gè)參數(shù),分別是配置項(xiàng)和狀態(tài)

          長(zhǎng)版本

          ReactJs是一個(gè)專注于View的Web前端框架。Web前端的View就是瀏覽器中的Dom元素,改變View的唯一途徑就是修改瀏覽器中的Dom元素,因此ReactJs的核心任務(wù)就是如何修改Dom元素,作為一個(gè)成功的框架,ReactJs使修改Dom元素變得高效而又簡(jiǎn)單。

          ReactJs把修改Dom的操作簡(jiǎn)化成一個(gè)函數(shù)renderInto(parentDom, props, states)=>htmlString,這個(gè)函數(shù)的意圖就是根據(jù)props,states計(jì)算出視圖對(duì)應(yīng)的html字符串并添加為parentDom的子節(jié)點(diǎn)。props和states就是普通的javascript對(duì)象,這個(gè)函數(shù)的核心邏輯就是計(jì)算html元素的機(jī)構(gòu)及元素屬性然后拼接成字符串返回。作為框架,ReactJs用JSX形式的DSL解決了拼接html的任務(wù)并接管了更新到parentDom的職責(zé)。看一個(gè)例子,理解這個(gè)函數(shù)并理解ReactJs怎么使用這個(gè)函數(shù)你就可以一個(gè)人開始ReactJs之旅了。

          var props={name: 'myname'};  
          var states={amount: 1000};  
          
          
          function render(props, states) {  
            var title=’Hello, ' + props.name;  
            var description='You have ' + states.amount + ' score now.';  
          
          
            return (  
              <div className="score-board">  
                 <h1>{title}</h1>  
                 <p>{description}</p>  
              </div>  
            );  
          }
          

          函數(shù)第一行根據(jù)props計(jì)算title,第二行根據(jù)states計(jì)算description,最后以JSX形式返回拼接好的html字符串。

          如果你用過AngularJs,EmberJs等類似的前端框架,你可能會(huì)覺得沒什么了不起,不就是把模板和邏輯放到一起嗎?是的,沒錯(cuò),但這不僅僅是組織形式上的改變,而是編程隱喻的轉(zhuǎn)變—從復(fù)雜的MVC或MVVM模式到簡(jiǎn)單的render函數(shù)。還有一點(diǎn)不同是JSX最終編譯成調(diào)用react-dom的javascript語句,而不是直接生成字符串。

          render函數(shù)還只是ReactJs這座冰山的一角,”React”會(huì)在render函數(shù)的輸入變化時(shí)再次調(diào)用這個(gè)函數(shù)。再看一個(gè)例子。

          var props={name: 'myname'};  
          var states={amount: 1000};  
          
          
          function handleClickAdd() {  
            states={amount: states.amount + 1};  
          }  
          
          
          function render(props, states) {  
            var title=’Hello, ' + props.name;  
            var description='You have ' + states.amount + ' score now.';  
          
          
            return (  
              <div className="score-board">  
                 <h1>{title}</h1>  
                 <p>{description}</p>  
                 <button onClick={handleClickAdd}>+1</button>  
              </div>  
            );  
          }
          

          這個(gè)例子增加了一個(gè)”+1”按鈕,當(dāng)用戶點(diǎn)擊按鈕時(shí)會(huì)修改states,ReactJs在states變化時(shí)的”React”就是再次調(diào)用render函數(shù),然后用新輸出更新瀏覽器的dom。

          可能你會(huì)問,props和states不就是Model嗎?是的,可以理解成Model,但此Model非彼Model,props和states都是為View服務(wù)的而非和View平起平坐。

          可能你還會(huì)問,為啥不把props和states合并成一個(gè)對(duì)象?要回答這個(gè)問題,就涉及到復(fù)雜視圖的場(chǎng)景。想想看,當(dāng)視圖內(nèi)的元素不斷增加時(shí),代碼上如何處理,還要在一個(gè)render函數(shù)里折騰嗎?肯定不會(huì)。我猜你已經(jīng)想到了,要把大問題拆小。ReactJs給出的解決方法就是把大視圖拆成若干個(gè)小視圖,每個(gè)視圖都有自己的render函數(shù),在JSX中可以直接使用視圖標(biāo)簽。看一個(gè)例子。

          var Score=React.createClass({  
            initialState: function() {  
              return {amount: 1000};  
            },  
          
          
            function handleClickAdd() {  
              this.setState({amount: this.states.amount + 1});  
            }  
          
          
            render: function() {  
              var title=’Hello, ' + this.props.name;  
              var description='You have ' + this.states.amount + ' score now.';  
          
          
              return (  
                <div className="score-board">  
                   <h1>{title}</h1>  
                   <p>{description}</p>  
                   <button onClick={handleClickAdd}>+1</button>  
                </div>  
              );  
            }  
          });  
          
          
          var ScoreList=React.createClass({  
            render() {  
              return (  
                <ul className="score-list">  
                  <li><Score name="Tom" /></li>  
                  <li><Score name="Jerry" /></li>  
                </ul>  
              );  
            }  
          });  
          
          
          ReactDOM.render(  
            <ScoreList />,  
            document.getElementById('content')  
          );
          

          這個(gè)例子中有兩類View,分別是Score和ScoreList。ScoreList的render函數(shù)中使用Score標(biāo)簽并給出配置項(xiàng)name的值。詳細(xì)看一下Score,ReactJs提供createClass方法定義視圖,在render函數(shù)中通過this.props訪問外部傳入的配置項(xiàng),通過this.states訪問視圖內(nèi)部的狀態(tài)。從意圖上看,props外部傳入視圖的配置項(xiàng),擁有者是父視圖,視圖內(nèi)部只能讀取配置項(xiàng),states的擁有者是視圖自身。

          區(qū)分props和states的結(jié)果就是,子視圖沒辦法直接改變父視圖,視圖改變一定是自觸發(fā)改變的視圖開始向子視圖傳播。對(duì)上面的例子,當(dāng)Tom的Score改變時(shí),ScoreList其他部分一定不會(huì)改變,所以視圖更新從Tom的Score視圖開始就可以,這就保證了能更高效地計(jì)算視圖變化,再加上VirtualDom的使用,使ReactJs的效率大大超過其他框架。

          當(dāng)子視圖需要改變父視圖時(shí),也一定是從父視圖開始向下更新。假如上面的例子中ScoreList還有平均分的視圖,當(dāng)Tom的分?jǐn)?shù)改變時(shí),需要更新ScoreList中的平均分。這就需要Score視圖在處理”+1”輸入時(shí)把變化通知到ScoreList,做法時(shí)給Score增加配置項(xiàng),ScoreList中定義更新平均分的函數(shù)并把函數(shù)作為配置項(xiàng)傳給Score。當(dāng)ScoreList更新時(shí),因?yàn)镴erry的props和states都沒變化,所以Jerry的Score視圖不需要更新。

          這就是ReactJs的全部秘密了(不過Web前端本身是一個(gè)復(fù)雜系統(tǒng),你還需要了解更多其他內(nèi)容)。

          介:該教程兼容pc+移動(dòng)端,兼容原理:同時(shí)封裝了pc框架antd、和移動(dòng)端框架antdMobile,根據(jù)不同客戶端類型渲染不同的組件,同一個(gè)組件需要根據(jù)客戶端類型同時(shí)封裝pc和移動(dòng)端,如果覺得開發(fā)麻煩,可忽略兼容部分教程,根據(jù)需要分別構(gòu)建pc、和移動(dòng)端

          1. antd官網(wǎng):https://ant.design/components/overview-cn/
          2. antd-mobile官網(wǎng):https://mobile.ant.design/zh
          3. next.js: https://www.nextjs.cn/
          4. react:https://react.zcopy.site/
          5. redux:https://react-redux.js.org/api/hooks#custom-context

          一、介紹

          Next.js,這是一個(gè) React 的同構(gòu)應(yīng)用開發(fā)框架。

          • 直觀的、 基于頁面 的路由系統(tǒng)(并支持 動(dòng)態(tài)路由)
          • 預(yù)渲染。支持在頁面級(jí)的 靜態(tài)生成 (SSG) 和 服務(wù)器端渲染 (SSR)
          • 自動(dòng)代碼拆分,提升頁面加載速度
          • 具有經(jīng)過優(yōu)化的預(yù)取功能的 客戶端路由
          • 內(nèi)置 CSS 和 Sass 的支持,并支持任何 CSS-in-JS 庫(kù)
          • 開發(fā)環(huán)境支持 快速刷新
          • 利用 Serverless Functions 及 API 路由 構(gòu)建 API 功能
          • 完全可擴(kuò)展

          二、構(gòu)建項(xiàng)目

          yarn create next-app “文件名” --typescript
          yarn dev

          三、調(diào)整項(xiàng)目

          1. 文件目錄

          1. _app.tsx
          import type { AppProps } from "next/app";
          
          export default function App({ Component, pageProps }: AppProps) {
            return <Component {...pageProps} />
          }
          
          1. index.tsx
          import {NextPage} from "next";
          
          const Home: NextPage=(props)=> {  
              return <div>dsada</div>
          };
          export default Home

          三、靜態(tài)資源assets

          1. 創(chuàng)建assets>css、assets>font、assets>img
          2. 安裝依賴
          yarn add sass
          1. 集成字體圖標(biāo),下載阿里icon庫(kù),解壓后把壓縮包里的文件復(fù)制到assets>font
          <i class="iconfont icon-usename"></i>
          1. css文件下分別創(chuàng)建globals.scss、iframe.scss、normalize.scss、variable.scss
          //globals.scss 全局樣式文件
          
          
          body{
            font-size: $font_size!important;
          }
          //iframe.scss 公共樣式導(dǎo)入
          
          @import "./globals";
          @import "./normalize";
          @import "../font/iconfont.css";
          //normalize.scss 同一瀏覽器樣式,下載后放入該文件中
          http://nicolasgallagher.com/about-normalize-css/
          https://github.com/necolas/normalize.css
          //variable.scss 全局變量文件
          
          $primary-color: red;
          /**
          * 字體大小
          */
          $font_size: 14px;//基礎(chǔ)字體大小
          $sm_font_size: 12px;//小號(hào)字體
          $bg_font_size: 16px;//大號(hào)字體
          $xl_font_size: 20px;//超大號(hào)字體
          
          /**
          * icon 大小
          */
          $icon_size: $font_size;//默認(rèn)字體
          $bg_icon_size: $bg_font_size;//大號(hào)字體
          $sm_icon_size: $sm_font_size;//小號(hào)字體
          $xl_icon_size: $xl_font_size;//超大號(hào)字體
          
          /**
          * button 顏色、大小
          */
          $btn_primary: #1677ff;
          $btn_danger: #ff4d4f;
          
          /**
          * h1-h5標(biāo)簽字體大小
          */
          $h1_font_size: 38px;//h1字體大小
          $h2_font_size: 30px;//h2字體大小
          $h3_font_size: 24px;//h3字體大小
          $h4_font_size: $xl_font_size;//h4字體大小
          $h5_font_size: $bg_font_size;//h5字體大小
          1. 配置引入路徑tsconfig.json
          "paths": {
              ...
              "@css/": [
                  "./src/assets/css/"
              ],
              "@img/": [
                  "./src/assets/img/"
              ],
              ...
          }
          1. 引入全局樣式,修改_app.tsx
          import type { AppProps } from "next/app";
          import "@css/iframe.scss";
          
          export default function App({ Component, pageProps }: AppProps) {
            return <Component {...pageProps} />
          }
          
          1. 引入全局sass變量,next.config.js
          const path=require("path");
          /** @type {import('next').NextConfig} */
          const nextConfig={
            ...
            sassOptions:{
              includePaths: [path.join(__dirname, "./src/assets/css")],
              prependData: "@import 'variable.scss';"
            },
            ...
          }
          
          module.exports=nextConfig
          
          1. 配置antd-mobile主題,https://mobile.ant.design/zh/guide/theming,新建 css>antdMobileTheme.scss
          :root:root {
            --adm-color-primary: #ff4d4f;
          }
          1. 配置antd主題
          • 新建pages>antTheme.module.scss
          
          /* antd 主題配置
           * 詳細(xì)配置可參考 https://ant.design/docs/react/customize-theme-cn*/
          
          :export {
            colorPrimary: $primary-color;
            fontSize: $font_size;
            fontSizeHeading1: $h1_font_size;
            fontSizeHeading2:$h2_font_size;
            fontSizeHeading3:$h3_font_size;
            fontSizeHeading4:$h4_font_size;
            fontSizeHeading5:$h5_font_size;
            fontSizeLG:$bg_font_size;
            fontSizeSM:$sm_font_size;
            fontSizeXL:$xl_font_size;
            fontSizeIcon:$sm_icon_size;
          }


          • 修改_app.tsx
          import type { AppProps } from "next/app";
          import {ConfigProvider} from "antd";
          import them from "./antTheme.module.scss";
          import "@css/iframe.scss";
          
          export default function App({ Component, pageProps }: AppProps) {
          
            return  <ConfigProvider theme={{token: them}}>
              <Component {...pageProps}/>
            </ConfigProvider>
          }
          
          1. 集成postcss
          • 安裝依賴postcss-px-to-viewport-8-plugin
          yarn add postcss-px-to-viewport-8-plugin --dev
          • 根目錄新建postcss.config.js
          //postcss.config.js
          
          module.exports={
              plugins: {
                  "postcss-px-to-viewport-8-plugin": {
                      viewportWidth: 375, // 視窗的寬度,對(duì)應(yīng)的是我們?cè)O(shè)計(jì)稿的寬度
                      viewportHeight: 912, // 視窗的高度,對(duì)應(yīng)的是我們?cè)O(shè)計(jì)稿的高度,可以不設(shè)置
                      unitPrecision: 3, // 指定`px`轉(zhuǎn)換為視窗單位值的小數(shù)位數(shù)(很多時(shí)候無法整除)
                      viewportUnit: 'vw', // 指定需要轉(zhuǎn)換成的視窗單位,建議使用vw
                      propList: ['*'],
                      selectorBlackList: [/^.pc/],
                      minPixelValue: 1, // 小于或等于`1px`不轉(zhuǎn)換為視窗單位,你也可以設(shè)置為你想要的值
                      mediaQuery: false, // 允許在媒體查詢中轉(zhuǎn)換`px`,
                      exclude: [/pc.module/,/antTheme.module.scss/,/braft-editor/], //設(shè)置忽略文件,用正則做目錄名匹配
                  }
              },
          };
          

          參數(shù)

          說明

          類型

          默認(rèn)值

          unitToConvert

          需要轉(zhuǎn)換的單位,默認(rèn)為 px

          string

          px

          viewportWidth

          設(shè)計(jì)稿的視口寬度,如傳入函數(shù),函數(shù)的參數(shù)為當(dāng)前處理的文件路徑,函數(shù)返回

          undefind

          跳過轉(zhuǎn)換

          number | Function

          320

          unitPrecision

          單位轉(zhuǎn)換后保留的精度

          number

          5

          propList

          能轉(zhuǎn)化為 vw 的屬性列表

          string[]

          ['*']

          viewportUnit

          希望使用的視口單位

          string

          vw

          fontViewportUnit

          字體使用的視口單位

          string

          vw

          selectorBlackList

          需要忽略的 CSS 選擇器,不會(huì)轉(zhuǎn)為視口單位,使用原有的 px 等單位

          string[]

          []

          minPixelValue

          設(shè)置最小的轉(zhuǎn)換數(shù)值,如果為 1 的話,只有大于 1 的值會(huì)被轉(zhuǎn)換

          number

          1

          mediaQuery

          媒體查詢里的單位是否需要轉(zhuǎn)換單位

          boolean

          false

          replace

          是否直接更換屬性值,而不添加備用屬性

          boolean

          true

          landscape

          是否添加根據(jù)

          landscapeWidth

          生成的媒體查詢條件

          @media (orientation: landscape)

          boolean

          false

          landscapeUnit

          橫屏?xí)r使用的單位

          string

          vw

          landscapeWidth

          橫屏?xí)r使用的視口寬度,,如傳入函數(shù),函數(shù)的參數(shù)為當(dāng)前處理的文件路徑,函數(shù)返回

          undefind

          跳過轉(zhuǎn)換

          number

          568

          exclude

          忽略某些文件夾下的文件或特定文件,例如 node_modules 下的文件,如果值是一個(gè)正則表達(dá)式,那么匹配這個(gè)正則的文件會(huì)被忽略,如果傳入的值是一個(gè)數(shù)組,那么數(shù)組里的值必須為正則

          Regexp

          undefined

          include

          需要轉(zhuǎn)換的文件,例如只轉(zhuǎn)換 'src/mobile' 下的文件 (

          include: /\/src\/mobile\//

          ),如果值是一個(gè)正則表達(dá)式,將包含匹配的文件,否則將排除該文件, 如果傳入的值是一個(gè)數(shù)組,那么數(shù)組里的值必須為正則

          Regexp

          undefined

          四、集成redux

          1. 安裝依賴
          yarn add redux react-redux redux-saga
          yarn add @types/react-redux @types/redux-saga next-redux-wrapper redux-devtools-extension --dev
          1. 創(chuàng)建redux>reducers、redux>sagas文件夾
          2. 配置引入路徑tsconfig.json
          "paths": {
              ...
              "@reducers/*": [
                "./src/redux/store/reducers/*"
              ],
              "@sagas/*": [
                "./src/redux/store/sagas/*"
              ],
              "@store/*": [
                "./src/redux/store/*"
              ],
              ...
          }
          1. 創(chuàng)建第一個(gè)store,redux>reducers>mobileStore.tsx
          /**
           * @description 該store,判斷是否是移動(dòng)端
           * */
          
          
          /**
           * @description 定義相關(guān)接口或者枚舉
           * */
          export enum MobileStoreActionEnum {
              INIT="mobileStoreInit",
              CHANGE="mobileStoreChange"
          }
          
          export type MobileStoreStateType=boolean;
          
          interface MobileStoreActionInterface{
              type: MobileStoreActionEnum,
              payload:MobileStoreStateType
          }
          
          /**
           * @description store邏輯
           * */
          const mobileInitState:MobileStoreStateType=false;
          const mobileStore=(state:MobileStoreStateType=mobileInitState, action: MobileStoreActionInterface):MobileStoreStateType=> {
              switch (action.type) {
                  case MobileStoreActionEnum.INIT:
                      return state
                  case MobileStoreActionEnum.CHANGE:
                      return action.payload
                  default:
                      return state
              }
          }
          export default mobileStore;
          
          1. 創(chuàng)建第一個(gè)sagaStore,redux>reducers>demoStore.tsx,異步store
          /**
           * @description 定義相關(guān)接口或者枚舉
           * */
          
          export enum DemoStoreActionEnum{
              WATCH='watchDemoStore',
              CHANGE='demoStoreChange'
          }
          
          interface DemoStoreStateInterface {
              num:number
          }
          
          export interface DemoStoreActionInterface {
              type: DemoStoreActionEnum
              payload: DemoStoreStateInterface
          }
          
          /**
           * @description store邏輯
           * */
          const initState:DemoStoreStateInterface={
              num: 100
          }
          
          const demoStore=(state:DemoStoreStateInterface=initState, action: DemoStoreActionInterface):DemoStoreStateInterface=> {
              switch (action.type) {
                  case DemoStoreActionEnum.CHANGE:
                      return action.payload
                  default:
                      return state
              }
          };
          export default demoStore;
          
          1. 依次創(chuàng)建redux>sagas>demo.tsx、redux>sagas>mainSaga.tsx
          • saga的應(yīng)用場(chǎng)景是復(fù)雜異步,如長(zhǎng)時(shí)事務(wù)LLT(long live.transcation)等業(yè)務(wù)場(chǎng)景。
          • 方便測(cè)試,可以使用takeEvery打印logger。
          • 提供takeLatest/takeEvery/throttle方法,可以便利的實(shí)現(xiàn)對(duì)事件的僅關(guān)注最近事件、關(guān)注每一次、事件限頻
          • 提供cancel/delay方法,可以便利的取消、延遲異步請(qǐng)求
          • 提供race(effects),[…effects]方法來支持競(jìng)態(tài)和并行場(chǎng)景
          • 提供channel機(jī)制支持外部事
          import { call, put, takeEvery, takeLatest,take,all,race,throttle,delay,fork,cacel,cancelled} from 'redux-saga/effects';
          takeEvery:被調(diào)用的任務(wù)無法控制何時(shí)被調(diào)用, 它們將在每次 action 被匹配時(shí)一遍又一遍地被調(diào)用。并且它們也無法控制何時(shí)停止監(jiān)聽。
          take:與takeEver相反,與 action 只會(huì)監(jiān)聽一次,使用一次就銷毀
          takeLatest:每次 action 被匹配,當(dāng)有action正在匹配,會(huì)放棄正在匹配的action,執(zhí)行最新的
          call: saga通過 Generator函數(shù)實(shí)現(xiàn),在yield函數(shù)后執(zhí)行effect,其中call是用于執(zhí)行某些異步操作的。
          put: 和上面的call一樣,中間件提供put 來把a(bǔ)ction丟到中間件中去dispatch,好處同樣是便于測(cè)試
          all: 同步執(zhí)行多個(gè)任務(wù)使需要用到 yield all([call(fetch, '/users'),call(fetch, '/repos')])
          race: 和promise中的race一個(gè)概念,執(zhí)行多個(gè)任務(wù),受到響應(yīng)后則繼續(xù)執(zhí)行 yield race({posts: call(fetchApi, '/posts'),timeout: call(delay, 1000)})
          fork:fork和take不同,take會(huì)和call一樣阻塞代碼的執(zhí)行,知道結(jié)果返回,fork則不會(huì),它會(huì)將任務(wù)啟動(dòng)并且不阻塞代碼的執(zhí)行,fork會(huì)返回一個(gè)task,可以用cacel(task)來取消任務(wù)
          cacel:來取消任務(wù)
          cancelled:如果當(dāng)前任務(wù),被cacel取消,則返回true
          throttle:節(jié)流
          //redux>sagas>demo.tsx
          import {call, put, takeEvery} from "@redux-saga/core/effects";
          import {DemoStoreActionEnum, DemoStoreActionInterface} from "@reducers/demoStore";
          
          
          // 延時(shí)器
          const delay=(ms:number)=> new Promise(resolve=> setTimeout(resolve, ms));
          
          function* asyncDemoSaga(action:DemoStoreActionInterface):Generator {
              yield call(delay,2000);
              yield put({ type: DemoStoreActionEnum.CHANGE,payload:action.payload})
          }
          
          function* watchDemoSaga():Generator {
              yield takeEvery(DemoStoreActionEnum.WATCH, asyncDemoSaga)
          }
          
          
          export default watchDemoSaga;
          
          //redux>sagas>mainSaga.tsx
          
          import {all} from "redux-saga/effects"
          import watchDemoSaga from "@sagas/demo";
          
          // saga中間件 主saga,用于區(qū)別是否需要saga來處理異步操作,如果沒有異步,則放行
          function* mainSaga() {
              yield all([
                  // 監(jiān)聽 saga 中有 userWatchSaga 操作,所以會(huì)攔截這個(gè) action
                  watchDemoSaga(),
              ])
          }
          
          // 主saga要對(duì)外暴露出去
          export default mainSaga;
          1. 修改_app.tsx
          import type { AppProps } from "next/app";
          import {ConfigProvider} from "antd";
          import them from "./antTheme.module.scss";
          import "@css/iframe.scss";
          import {useEffect} from "react";
          import {useDispatch} from "react-redux";
          import { MobileStoreActionEnum} from "@reducers/mobileStore";
          import wrapper from "@/redux";
          import {Dispatch} from "redux";
          
          const App=({ Component, pageProps }: AppProps)=> {
            const dispatch:Dispatch=useDispatch();
            useEffect(():void=> {
              //判斷是哪個(gè)客戶端(pc,mobile),主要用來兼容樣式
              if (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i)) {
                dispatch({
                  type:  MobileStoreActionEnum.CHANGE,
                  payload: true
                });
                //增加全局class,用于設(shè)置全局樣式
                document.getElementsByTagName('html')[0].className='mobile';
              }else{
                //增加全局class,用于設(shè)置全局樣式
                document.getElementsByTagName('html')[0].className='pc';
              }
            },[])
            return  <ConfigProvider theme={{token: them}}>
              <Component {...pageProps}/>
            </ConfigProvider>
          }
          
          export default wrapper.withRedux(App)
          
          1. 創(chuàng)建redux>index.tsx
          import { createWrapper, MakeStore } from "next-redux-wrapper";
          import { applyMiddleware, createStore, Store} from "redux";
          import createSagaMiddleware, {SagaMiddleware} from "redux-saga";
          import { composeWithDevTools } from "redux-devtools-extension/developmentOnly";
          
          import rootReducer from "@store/index";
          import mainSaga from "@sagas/mainSaga"; //異步初始化store
          
          const makeStore: MakeStore<Store>=()=> {
              const sagaMiddleware:SagaMiddleware=createSagaMiddleware()
              const store:Store=createStore(rootReducer, composeWithDevTools(applyMiddleware(sagaMiddleware)))
              sagaMiddleware.run(mainSaga)
              return store
          }
          
          export default createWrapper<Store>(makeStore)
        1. 封裝pc+移動(dòng)端兼容性button組件,創(chuàng)建兼容性u(píng)i框架,需要把a(bǔ)ntd、antd-mobile二次封裝,并合并一些共同的參數(shù),修改成共同的樣式;創(chuàng)建非ui框架的組件,只要注意像素單位的兼容就行,如:mobile.module.scss、pc.module.scss,postcss已限制pc.module 樣式文件的轉(zhuǎn)換
          • 修改.eslintrc.json
          {
            "extends": "next/core-web-vitals",
            "rules": {
              "react/display-name": "off"
            }
          }
          
          • 創(chuàng)建components>antd>button
          • 創(chuàng)建pc端button組件button>pc.tsx、button>pc.module.scss
          //button>pc.tsx
          
          /**
           * @description pc端Button組件
           * */
          
          /**********第三方插件、組件引用**********/
          import React from "react";
          import {Button as PcButton, ButtonProps} from "antd";
          import {SizeType} from "antd/es/config-provider/SizeContext";
          import {ButtonType} from "antd/es/button";
          /**********當(dāng)前目錄文件*********/
          import styles from "./pc.module.scss";
          
          export interface PcButtonInterface {
              type?: ButtonType,
              size?: SizeType,
              onClick?: ButtonProps['onClick'],
              children?: React.ReactNode
          }
          
          const Button=React.memo((props:PcButtonInterface)=>{
              return <PcButton className={styles.button}
                               type={props.type}
                               size={props.size}
                               onClick={props.onClick}>
                  { props.children }
              </PcButton >
          });
          
          export default Button;
          • 創(chuàng)建移動(dòng)端button組件button>mobile.tsx、button>mobile.module.scss
          //button>mobile.tsx
          
          /**
           * @description 移動(dòng)端Button組件
           * */
          
          /**********第三方插件、組件引用**********/
          import React from "react";
          import {Button as MobileButton, ButtonProps} from "antd-mobile";
          /**********當(dāng)前目錄文件*********/
          import styles from "./mobile.module.scss";
          
          export interface MobileButtonInterface {
              type?: ButtonProps['color'],
              size?: ButtonProps['size'],
              onClick?:ButtonProps['onClick'],
              children?: React.ReactNode;
          }
          
          const Button=React.memo((props:MobileButtonInterface)=>{
              return  <MobileButton className={styles.button}
                                    color={props.type}
                                    size={props.size}
                                    onClick={props.onClick}>
                  { props.children }
              </MobileButton>
          });
          
          export default Button;
          • 創(chuàng)建兼容pc+移動(dòng)組件button>index.tsx、button>index.scss
          //button>index.tsx
          
          /**
           * @description 同時(shí)兼容pc、移動(dòng)的Button組件
           * */
          
          import React, {useState} from "react";
          import PcButton, {PcButtonInterface} from "./pc";
          import MobileButton, {MobileButtonInterface} from "./mobile";
          import {useSelector, useStore} from "react-redux";
          import {Store} from "redux";
          
          interface ClientButtonInterface {
              type?: PcButtonInterface['type'] & MobileButtonInterface['type'],
              size?: PcButtonInterface['size'] & MobileButtonInterface['size'],
              onClick?: PcButtonInterface['onClick'] & MobileButtonInterface['onClick'],
              children?: React.ReactNode
          }
          
          const Button=React.memo((props: ClientButtonInterface)=> {
              const store:Store=useStore();
              const storeState=store.getState() as any;
              const [mobile,setMobile]=useState(storeState.mobileStore)
              useSelector((state:any):void=> {
                  if(mobile!=state?.mobileStore){
                      setMobile(state?.mobileStore);
                  }
              });
          
              return <>
                  {mobile ? <MobileButton {...props}/> : <PcButton {...props}/>}
              </>
          });
          
          export default Button;
          
          //button>index.scss
          
          .button{
            font-size: 14px;
            height: 32px;
            padding: 4px 15px;
            border-radius: 6px;
          }
          • 修改button>mobile.module.scss、button>pc.module.scss
          @import "./index";
          • 使用
          import Button from "@/components/antd/button";
          
          <Button type="primary">antd</Button>
          1. 持續(xù)儲(chǔ)存
          • 安裝依賴,https://www.npmjs.com/package/redux-persist
          yarn add redux-persist
          • 修改redux>index.tsx
          import { createWrapper, MakeStore } from "next-redux-wrapper";
          import { applyMiddleware, createStore, Store} from "redux";
          import createSagaMiddleware, {SagaMiddleware} from "redux-saga";
          import { composeWithDevTools } from "redux-devtools-extension/developmentOnly";
          import {persistStore, persistReducer} from "redux-persist";
          import storage from "redux-persist/lib/storage";
          
          import rootReducer from "@store/index";
          import mainSaga from "@sagas/mainSaga"; //異步初始化store
          
          //持久化儲(chǔ)存配置
          const persistConfig={
              key: 'root', //在localStorge中生成key為root的值
              storage,
              blacklist:['demoSaga'] //設(shè)置某個(gè)reducer數(shù)據(jù)不持久化
          }
          const makeStore: MakeStore<Store>=()=> {
              const sagaMiddleware:SagaMiddleware=createSagaMiddleware();
              const rootPersistReducer=persistReducer(persistConfig, rootReducer)
              const store:Store=createStore(rootPersistReducer, composeWithDevTools(applyMiddleware(sagaMiddleware)))
              sagaMiddleware.run(mainSaga);
              persistStore(store);
              return store
          }
          
          export default createWrapper<Store>(makeStore)

          五、頁面配置

          1. 設(shè)置頁面標(biāo)題:_app.tsx
          import '@/assets/css/globals.scss';
          import type { AppProps } from 'next/app';
          import Head from 'next/head';
          import { ConfigProvider } from 'antd';
          import them from '@/pages/app.module.scss';
          
          export default function App({ Component, pageProps }: AppProps) {
            return <>
              <Head>
                <title>頁面標(biāo)題</title>
              </Head>
              <ConfigProvider theme={{token: them}}>
                <Component {...pageProps}/>
              </ConfigProvider>
            </>
          }
          1. 設(shè)置頁面框架代碼:_document.tsx,只會(huì)在初始化預(yù)渲染,設(shè)置的內(nèi)容只會(huì)在build后生效
          import {Html, Head, Main, NextScript} from 'next/document'
          
          export default function Document() {
              return (
                  <Html lang="en">
                      <Head>
                          <link rel="icon" href="/favicon.ico"></link>
                          <meta name="description" content="頁面框架"></meta>
                      </Head>
                      <body>
                      <Main/>
                      <NextScript/>
                      </body>
                  </Html>
              )
          }
          
          1. 自定義404頁面,pages下新建404.tsx頁面
          export default function Custom_404(){
              return <>404頁面</>
          }

          六、圖片引用

          1. 方法一:原生img,使用' '可能會(huì)導(dǎo)致LCP變慢和帶寬增加,build時(shí)會(huì)有此警告
          import idCard from '@img/idCard.png';
          <img src={idCard.src}/>
          1. 方法二:使用 next/image;簡(jiǎn)單的圖片引用建議用原生img
          //建議用div包括起來,不單獨(dú)使用,單獨(dú)使用會(huì)自動(dòng)生成很多自帶的樣式;Image會(huì)自適應(yīng)div大小
          import idCard from '@img/idCard.png';
          import Image from 'next/image';
          <div><Image src={idCard} alt=""/></div>
          1. next/image 自帶優(yōu)化的api適用于SSR,SSG中無法使用 ,可以改動(dòng) next.config.js 配置解決
          const nextConfig={
            reactStrictMode: true,
            swcMinify: true,
            images:{
                unoptimized:true
            }
          }
          
          
          module.exports=nextConfig
          1. 安裝sharp包(高性能圖片處理器),Next.js將自動(dòng)使用它的圖像優(yōu)化
          yarn add sharp

          七、動(dòng)態(tài)路由

          1. 創(chuàng)建pages>demo文件夾
          2. 創(chuàng)建demo>index.tsx、demo>index.scss、demo>mobile.module.scss、demo>pc.module.scss
          //demo>index.tsx
          
          import Image from "next/image";
          import idCard from "@img/idCard.png";
          import useStyle from "@hook/styleHook";
          import mobileStyle from "./mobile.module.scss";
          import pcStyle from "./pc.module.scss";
          
          const Demo=()=> {
              const styles=useStyle(pcStyle,mobileStyle);
              return <div className={styles.P_demo}>
                  <Image src={idCard} alt=""/>
              </div>;
          };
          
          
          export default Demo
          
          //demo>index.scss
          
          .P_demo{
            img{
              width: 100px;
              height: 100px;
            }
          }
          //demo>mobile.module.scss、demo>pc.module.scss
          @import "./index";
          1. 修改page>index.tsx
          import {NextRouter, useRouter} from "next/router";
          
          const router:NextRouter=useRouter();
          <Button onClick={()=> router.push('/demo')}>goDemo</Button>
          1. 自定義路由,改動(dòng) next.config.js 配置
          const nextConfig={
            ...
            //自定義路由,通常不需要自定義路由,適用于SS
            exportPathMap: async ()=>{
              return {
                '/':{
                  page:'/index'
                }
              }
            },
            ...
          }
          
          
          module.exports=nextConfig
          1. 動(dòng)態(tài)路由
          • 修改demo>index.tsx為demo>[id].tsx
          import Image from "next/image";
          import idCard from "@img/idCard.png";
          import useStyle from "@hook/styleHook";
          import mobileStyle from "./mobile.module.scss";
          import pcStyle from "./pc.module.scss";
          import {NextRouter, useRouter} from "next/router";
          
          const Demo=()=> {
              const styles=useStyle(pcStyle,mobileStyle);
              const router:NextRouter=useRouter();
              console.log(router.query)
              return <div className={styles.P_demo}>
                  <Image src={idCard} alt=""/>
              </div>;
          };
          
          
          export default Demo
          
          • 修改pages>index.tsx
          <Button onClick={()=> router.push('/demo/1')}>goDemo</Button>
          1. 路由嵌套
          • [id].tsx:paths 里面參數(shù)只有一個(gè)id ,/demo/1
          • [...routers].tsx :paths 里面可以包含多個(gè)路徑,以數(shù)組形式發(fā)揮參數(shù),/demo/1/3
          • [id]/[comment].tsx:paths 里面可以包含多個(gè)路徑,以json形式返回,/demo/1/3

          八、接口api

          NEXT.js存在CSR/SSR/SSG 三種請(qǐng)求方式,最多存在兩種:1、CSR+SSR;2、CSR+SSG

          CSR請(qǐng)求:常規(guī)前端項(xiàng)目中請(qǐng)求方式,由客戶端瀏覽器端發(fā)送請(qǐng)求,拿到數(shù)據(jù)后再渲染道頁面

          SSR請(qǐng)求:由服務(wù)端發(fā)起請(qǐng)求(NEXT.js中的node.js),拿到數(shù)據(jù)后,組裝HTML,再把HTML返回到客戶端瀏覽器

          SSG請(qǐng)求:與SSR請(qǐng)求類似,由服務(wù)端發(fā)起請(qǐng)求(NEXT.js中的node.js),拿到數(shù)據(jù)后,組裝HTML,然后靜態(tài)化輸出。由于是完全靜態(tài)化輸出,當(dāng)數(shù)據(jù)變化時(shí),必須重新靜態(tài)化才能更新頁面

          1. 安裝axios
          yarn add axios
          1. 封裝axios
          /**
           * @description axios公共請(qǐng)求封裝
           * */
          import axios, {AxiosResponse, InternalAxiosRequestConfig} from "axios";
          
          /**
           * @description 定義相關(guān)接口或者枚舉
           * */
          
          //請(qǐng)求枚舉
          export enum MethodEnum {
              Get='GET',
              Post='POST'
          }
          
          //返回結(jié)果
          export interface ResponseResultInterface<Body> {
              Header:{},
              Body: Body
          }
          
          //請(qǐng)求參數(shù)
          export interface RequestInterface<params> {
              url:string,
              params?:params,
              method?:MethodEnum
          }
          
          /**
           * 封裝axios
           * */
          
          // 添加請(qǐng)求攔截器
          axios.interceptors.request.use( (config:InternalAxiosRequestConfig)=>{
              return config;
          }, (error)=>{
              return Promise.reject(error);
          });
          
          // 添加響應(yīng)攔截器
          axios.interceptors.response.use( (response:AxiosResponse)=> {
              return response;
          }, (error)=> {
              return Promise.reject(error);
          });
          
          /**
           * @method useAxiosRequest axios請(qǐng)求封裝
           * @param requestPar { RequestInterface } 請(qǐng)求url
           * @return Promise
           * */
          const baseRequest=async <params,responseData>(requestPar:RequestInterface<params>):Promise<responseData>=> {
              const requestResult:AxiosResponse=await axios({
                  method: requestPar.method || MethodEnum.Post,
                  url: requestPar.url,
                  data: requestPar.params
              });
              return requestResult.data as responseData;
          };
          
          export default baseRequest;
          
          1. 配置引入路徑,修改 tsconfig.json
          "paths": {
               ... 
               "@common/*": [
                    "./src/common/*"
               ],
               "@api/*": [  
                   "./src/pages/api/*"
               ],
               ...
          }
          1. 創(chuàng)建api服務(wù),pages>api>demoApi.tsx
          // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
          import type { NextApiRequest, NextApiResponse } from "next";
          import {ResponseResultInterface} from "@common/baseRequest";
          
          export interface DemoInterface {
              id: number,
              name?: string
          }
          
          type ApiDemoType=ResponseResultInterface<DemoInterface>
          
          export default function demoApi(
              req: NextApiRequest,
              res: NextApiResponse<ApiDemoType>
          ):void {
              let data:ApiDemoType={
                  Header:{},
                  Body:{
                      id:-1
                  }
              };
              if(req.method=="GET"){
                  const id:number=Number(req.query.id);
                  data.Body.id=id;
                  switch (id) {
                      case 1:
                          data.Body.name="我是API服務(wù)1"
                          break;
                  }
                  res.status(200).json(data)
              }else{
                  res.status(200).json(data)
              }
          }
          1. 修改pages>demo>[id].tsx
          import Image from "next/image";
          import {NextRouter, useRouter} from "next/router";
          import {GetServerSideProps} from "next";
          import {ParsedUrlQuery} from "querystring";
          import idCard from "@img/idCard.png";
          import useStyle from "@hook/styleHook";
          import baseRequest, {MethodEnum, RequestInterface} from "@common/baseRequest";
          import {DemoInterface} from "@api/demoApi";
          import mobileStyle from "./mobile.module.scss";
          import pcStyle from "./pc.module.scss";
          
          const Demo=(props: DemoInterface)=> {
              const styles=useStyle(pcStyle,mobileStyle);
              const router:NextRouter=useRouter();
              console.log(router.query)
              console.log(props);
              return <div className={styles.P_demo}>
                  <Image src={idCard} alt=""/>
              </div>;
          };
          
          /**
           * getServerSideProps 適用于SSR,不適用于SSG
           * getStaticProps SSR 和 SSG 均支持,但僅在網(wǎng)站build時(shí)候發(fā)起API請(qǐng)求
           * getServerSideProps 和 getStaticProps請(qǐng)求都是在服務(wù)端進(jìn)行不涉及跨域
           * */
          
          export const getServerSideProps: GetServerSideProps=async (paths)=> {
              const query:ParsedUrlQuery=paths.query;
              const requestOption:RequestInterface<undefined>={
                  url:`http://localhost:3000/api/demoApi?id=${query.id}`,
                  method:MethodEnum.Get
              }
              const requestResult=await baseRequest<DemoInterface>({
                  url: requestOption.url,
                  method:requestOption.method
              });
              return {
                  props: requestResult.Body
              }
          }
          
          /**
           * SSG 靜態(tài)生成
           * getStaticPaths build 時(shí)會(huì)生成多個(gè)頁面
           * 只是用于getStaticProps
           * */
          // export const getStaticPaths: GetStaticPaths<DemoParams>=async ()=> {
          //     // const arr: string[]=['1', '2'];
          //     // const paths=arr.map((id)=> {
          //     //     return {
          //     //         params: { id },
          //     //     }
          //     // })
          //     // return {
          //     //     //這里的路由參數(shù)提供給getStaticProps使用
          //     //     paths,
          //     //     //不在以上參數(shù)路由將返回404
          //     //     fallback: false
          //     // }
          //     const id1:DemoParams={id: '1'};
          //     const id2:DemoParams={id: '2'};
          //     const staticPathOption={
          //         //這里的路由參數(shù)提供給getStaticProps使用
          //         path: [{
          //             params: id1
          //         }, {
          //             params: id2
          //         }],
          //         //不在以上參數(shù)路由將返回404dc
          //         // fallback: false
          //     }
          //     return staticPathOption
          // }
          
          export default Demo
          

          九、生成靜態(tài)網(wǎng)站(SSG)

          1. 設(shè)置SSG的export命令,修改package.json
          "scripts": {
            "dev": "next dev",
            "build": "next build && next export",
            "start": "next start",
            "lint": "next lint"
          },
          1. 然后執(zhí)行yarn build,該命令回顯執(zhí)行next build 再執(zhí)行 next export,輸出目錄為根目錄下的out目錄

          1. 設(shè)置靜態(tài)資源的bassePath,如果發(fā)布在服務(wù)器二級(jí)目錄需要設(shè)置,更改next.config.js 配置;/app為二級(jí)目錄
          const nextConfig={
            reactStrictMode: true,
            swcMinify: true,
            basePath: process.env.NODE_ENV=="development"? '':'/app'
            images:{
                unoptimized:true
            }
          }
          
          
          module.exports=nextConfig
          1. 設(shè)置輸出目錄export輸出目錄為app
          "scripts": {
            "dev": "next dev",
            "build": "next build && next export -o app",
            "start": "next start",
            "lint": "next lint"
          },

          十、以SSR模式運(yùn)行項(xiàng)目

          yarn build
          yarn start -p 4000 //默認(rèn)端口3000

          十一、動(dòng)態(tài)生成項(xiàng)目目錄

          1. 安裝依賴
          npm install cross-env -g
          1. package.json
           "scripts": {
              "dev": "next dev",
              "build": "next build && next export",
              "start": "next start",
              "lint": "next lint",
              "customBuild": "cross-env BASE_PSTH=%npm_config_base% next build && next export -0 %npm_config_base%",
              "customBuild": "cross-env BASE_PSTH=$npm_config_base next build && next export -0 $npm_config_base%"//mac
            },


          1. 運(yùn)行npm run customBuild --base=/demo --out=demo

          十二、多環(huán)境開發(fā)配置

          1. 安裝依賴
          yarn add cross-env --dev
          1. 根目錄下創(chuàng)建.env.production(生產(chǎn)環(huán)境)、.env.development(開發(fā)環(huán)境)、.env.test(測(cè)試環(huán)境)、.env.local(默認(rèn)環(huán)境,始終覆蓋默認(rèn)設(shè)置)、.env(所有環(huán)境)
          //.env.test
          
          TEST=test //只有服務(wù)端可以獲取到
          NEXT_PUBLIC_HOST=http://127.0.0.1:3000/ //變量暴漏給瀏覽器端,加NEXT_PUBLIC_
          1. 指定環(huán)境運(yùn)行,修改package.json
            "scripts": {
          
              "dev:test": "cross-env NODE_ENV=test next dev",
            
            },
            
            頁面打印:
            console.log(process.env.TEST);
            console.log(process.env.NEXT_PUBLIC_HOST);


          十二、項(xiàng)目部署

          1. 安裝nginx:https://nginx.org/en/download.html,下載合適的版本,解壓到任意位置nginx 配置
          • 啟動(dòng)cd到nginx目錄運(yùn)行 nginx.exe
          • 啟動(dòng)cd到nginx目錄運(yùn)行start nginx,訪問http://localhost:80 正常訪問nginx運(yùn)行成功
          • 啟動(dòng)cd到nginx目錄運(yùn)行重新加載配置
          • 啟動(dòng)cd到nginx目錄運(yùn)行nginx -s stop 停止
          • 啟動(dòng)cd到nginx目錄運(yùn)行nginx -s quit 正常關(guān)閉
          • 啟動(dòng)cd到nginx目錄運(yùn)行nginx -s reopen 重新打開
          • 配置nginx.conf
          
           server {
                listen       9001;
                server_name  localhost;
                # server_name  btyhub.site, www.btyhub.site;
                # ssl兩個(gè)文件,放在 nginx的conf目錄中
                # ssl_certificate      btyhub.site_bundle.pem;
                # ssl_certificate_key  btyhub.site.key;
          
                # ssl_session_cache    shared:SSL:1m;
                # ssl_session_timeout  5m;
          
                # ssl_ciphers  HIGH:!aNULL:!MD5;
                # ssl_prefer_server_ciphers  on;
                # 代理到Next的服務(wù),默認(rèn)3000端口,也可以在start的時(shí)候指定
                location / {
                    proxy_pass    http://127.0.0.1:3000/;
                }
          
            }
          1. 安裝 pm2,node進(jìn)程管理工具:npm install -g pm2
          2. 把打包后得.next,next.config.js 上傳服務(wù)器
          3. package.json,運(yùn)行yarn install
          {
            "name": "react_common",
            "version": "0.1.0",
            "private": true,
            "scripts": {
              "start": "next start"
            },
            "dependencies": {
             //項(xiàng)目下package.json 中dependencies
            },
            "devDependencies": {
               //項(xiàng)目下package.json 中devDependencies
              "child_process": "^1.0.2"
          }
          
          1. 安裝child_process,運(yùn)行yarn add child_process --dev,創(chuàng)建start.js
          let exec=require("child_process").exec;
          //yarn start -p 9003 指定端口運(yùn)行項(xiàng)目
          exec("yarn start", {windowsHide: true});
          1. 運(yùn)行pm2 start start.js --name projectName,pm2:node進(jìn)程管理工具

          十三、其他

          1. next 命令
          yarn dev --help 某個(gè)命令幫助信息
          next lint 設(shè)置 Next.js 的內(nèi)置 ESLint 配置
          next lint --no-cache 清除 ESLint 緩存
          1. next內(nèi)置組件
          • import Head from 'next/head'
          • import Image from 'next/image'
          • import { Inter } from 'next/font/google'
          • import { Html, Head, Main, NextScript } from 'next/document'
          • import type { AppProps } from 'next/app'
          • import type { NextApiRequest, NextApiResponse } from 'next'
          • import { useRouter } from 'next/router'
          • import Link from 'next/link'
          • import Script from 'next/script'
          • import { useAmp } from 'next/amp'
          • import type { NextRequest } from 'next/server'
          • import { Inter } from 'next/font/google'
          • import getConfig from 'next/config'
          1. next.config.js

          主站蜘蛛池模板: 一区二区三区午夜| 无码一区二区三区在线观看| 精品欧洲av无码一区二区| 国产精品视频一区国模私拍| 成人中文字幕一区二区三区| 国产精品无码一区二区三区免费 | 亚洲国产欧美日韩精品一区二区三区| 精品一区二区三区波多野结衣 | 91国在线啪精品一区| 激情内射日本一区二区三区| 精品国产一区二区三区香蕉 | 久99精品视频在线观看婷亚洲片国产一区一级在线 | 另类免费视频一区二区在线观看| 夜夜添无码试看一区二区三区| 日本免费一区二区在线观看| 无码精品久久一区二区三区| 成人区精品一区二区不卡亚洲| 丰满人妻一区二区三区免费视频 | 久久高清一区二区三区| www.亚洲一区| 日本人真淫视频一区二区三区| 蜜桃AV抽搐高潮一区二区| 老熟女五十路乱子交尾中出一区| 无码人妻精品一区二区蜜桃AV| 日韩精品久久一区二区三区| 亚洲av成人一区二区三区 | 久久精品国产AV一区二区三区| 亚洲国产精品一区二区第一页 | 国产成人精品一区在线| 一区二区三区在线看| 日韩精品无码人妻一区二区三区 | 亚洲国产综合无码一区| 国产亚洲一区二区在线观看| 看电影来5566一区.二区| 无码国产精品一区二区免费 | 久久亚洲色一区二区三区| 色噜噜AV亚洲色一区二区| 福利一区二区三区视频在线观看| 日本免费一区二区三区 | 无码精品蜜桃一区二区三区WW| 一区二区三区国模大胆|