主頁 > 企業開發 > React16原始碼解讀:開篇帶你搞懂幾個面試考點

React16原始碼解讀:開篇帶你搞懂幾個面試考點

2020-10-06 21:21:55 企業開發

引言

如今,主流的前端框架React,Vue和Angular在前端領域已成三足鼎立之勢,基于前端技術堆疊的發展現狀,大大小小的公司或多或少也會使用其中某一項或者多項技術堆疊,那么掌握并熟練使用其中至少一種也成為了前端人員必不可少的技能飯碗,當然,框架的部分實作細節也常成為面試中的考察要點,因此,一方面為了應付面試官的連番追問,另一方面為了提升自己的技能水平,還是有必要對框架的底層實作原理有一定的涉獵,

當然對于主攻哪門技術堆疊沒有嚴格的要求,挑選你自己喜歡的就好,在面試中面試官一般會先問你最熟悉的是哪門技術堆疊,對于你不熟悉的領域,面試官可能也不會做太多的追問,筆者在專案中一直是使用的Vue框架,其上手門檻低,也提供了比較全面和友好的官方檔案可供參考,但是可能因人而異,感覺自己還是比較喜歡React,也說不出什么好壞,可能是自己最早接觸的前端框架吧,不過很遺憾,在之前的作業中一直派不上用場,但即便如此,也阻擋不了自己對底層原理的好奇心,所以最近也是開始研究React的原始碼,并對原始碼的解讀程序做一下記錄,方便加深記憶,如果你的技術堆疊剛好是React,并且也對原始碼感興趣,那么我們可以一起互相探討技術難點,讓整個閱讀原始碼的程序變得更加容易和有趣,原始碼中如果有理解錯誤的地方,還希望能夠指出,

1、準備階段

在facebook的github上,目前React的最新版本為v16.12.0,我們知道在React的v16版本之后引入了新的Fiber架構,這種架構使得任務擁有了暫停恢復機制,將一個大的更新任務拆分為一個一個執行單元,充分利用瀏覽器在每一幀的空閑時間執行任務,無空閑時間則延遲執行,從而避免了任務的長時間運行導致阻塞主執行緒同步任務的執行,為了了解這種Fiber架構,這里選擇了一個比較適中的v16.10.2的版本,沒有選擇最新的版本是因為在最新版本中移除了一些舊的兼容處理方案,雖說這些方案只是為了兼容,但是其思想還是比較先進的,值得我們推敲學習,所以先將其保留下來,這里選擇v16.10.2版本的另外一個原因是React在v16.10.0的版本中涉及到兩個比較重要的優化點:


在上圖中指出,在任務調度(Scheduler)階段有兩個性能的優化點,解釋如下:

  • 將任務佇列的內部資料結構轉換成最小二叉堆的形式以提升佇列的性能(在最小堆中我們能夠以最快的速度找到最小的那個值,因為那個值一定在堆的頂部,有效減少整個資料結構的查找時間),
  • 使用周期更短的postMessage回圈的方式而不是使用requestAnimationFrame這種與幀邊界對齊的方式(這種優化方案指得是在將任務進行延遲后恢復執行的階段,前后兩種方案都是宏任務,但是宏任務也有順序之分,postMessage的優先級比requestAnimationFrame高,這也就意味著延遲任務能夠更快速地恢復并執行),

當然現在不太理解的話沒關系,后續會有單獨的文章來介紹任務調度這一塊內容,遇到上述兩個優化點的時候會進行詳細說明,在開始閱讀原始碼之前,我們可以使用create-react-app來快速搭建一個React專案,后續的示例代碼可以在此專案上進行撰寫:

// 專案搭建完成后React默認為最新版v16.12.0
create-react-app react-learning

// 為了保證版本一致,手動將其修改為v16.10.2
npm install --save [email protected] [email protected]

// 運行專案
npm start

執行以上步驟后,不出意外的話,瀏覽器中會正常顯示出專案的默認界面,得益于在Reactv16.8版本之后推出的React Hooks功能,讓我們在原來的無狀態函陣列件中也能進行狀態管理,以及使用相應的生命周期鉤子,甚至在新版的create-react-app腳手架中,根組件App已經由原來的類組件的寫法升級為了推薦的函式定義組件的方式,但是原來的類組件的寫法并沒有被廢棄掉,事實上我們專案中還是會大量充斥著類組件的寫法,因此為了了解這種類組件的實作原理,我們暫且將App根組件的函式定義的寫法回退到類組件的形式,并對其內容進行簡單修改:

// src -> App.js
import React, {Component} from 'react';

function List({data}) {
    return (
        <ul className="data-list">
            {
                data.map(item => {
                    return <li className="data-item" key={item}>{item}</li>
                })
            }
        </ul>
    );
}

export default class App extends Component {

    constructor(props) {
        super(props);
        this.state = {
            data: [1, 2, 3]
        };
    }

    render() {
        return (
            <div className="container">
                <h1 className="title">React learning</h1>
                <List data=https://www.cnblogs.com/tangshiwei/p/{this.state.data} />
            
); } }

經過以上簡單修改后,然后我們通過呼叫

// src -> index.js
ReactDOM.render(<App />, document.getElementById('root'));

來將組件掛載到DOM容器中,最終得到App組件的DOM結構如下所示:

<div >
    <h1 >React learning</h1>
    <ul >
        <li >1</li>
        <li >2</li>
        <li >3</li>
    </ul>
</div>

因此我們分析React原始碼的入口也將會是從ReactDOM.render方法開始一步一步分析組件渲染的整個流程,但是在此之前,我們有必要先了解幾個重要的前置知識點,這幾個知識點將會更好地幫助我們理解原始碼的函式呼叫堆疊中的引數意義和其他的一些細節,

2、前置知識

首先我們需要明確的是,在上述示例中,App組件的render方法回傳的是一段HTML結構,在普通的函式中這種寫法是不支持的,所以我們一般需要相應的插件來在背后支撐,在React中為了支持這種jsx語法提供了一個Babel預置工具包@babel/preset-react,其中這個preset又包含了兩個比較核心的插件:

// .babelrc
{
    "plugins": [
        ["@babel/plugin-transform-react-jsx", {
            "pragma": "Preact.h", // default pragma is React.createElement
            "pragmaFrag": "Preact.Fragment", // default is React.Fragment
            "throwIfNamespace": false // defaults to true
        }]
    ]
}

這里為了方便起見,我們可以直接使用Babel官方實驗室來查看轉換后的結果,對應上述示例,轉換后的結果如下所示:

// 轉換前
render() {
    return (
        <div className="container">
            <h1 className="title">React learning</h1>
            <List data=https://www.cnblogs.com/tangshiwei/p/{this.state.data} />
        
); } // 轉換后 render() { return React.createElement("div", { className: "content" }, React.createElement("header", null, "React learning"), React.createElement(List, { data: this.state.data })); }

可以看到jsx語法最終被轉換成由React.createElement方法組成的嵌套呼叫鏈,可能你之前已經了解過這個API,或者接觸過一些偽代碼實作,這里我們就基于原始碼,深入原始碼內部來看看其背后為我們做了哪些事情,

2.1 createElement & ReactElement

為了保證原始碼的一致性,也建議你將React版本和筆者保持一致,采用v16.10.2版本,可以通過facebook的github官方渠道進行獲取,下載下來之后我們通過如下路徑來打開我們需要查看的檔案:

// react-16.10.2 -> packages -> react -> src -> React.js 

React.js檔案中,我們直接跳轉到第63行,可以看到React變數作為一個物件字面量,包含了很多我們所熟知的方法,包括在v16.8版本之后推出的React Hooks方法:

const React = {
  Children: {
    map,
    forEach,
    count,
    toArray,
    only,
  },

  createRef,
  Component,
  PureComponent,

  createContext,
  forwardRef,
  lazy,
  memo,

  // 一些有用的React Hooks方法
  useCallback,
  useContext,
  useEffect,
  useImperativeHandle,
  useDebugValue,
  useLayoutEffect,
  useMemo,
  useReducer,
  useRef,
  useState,

  Fragment: REACT_FRAGMENT_TYPE,
  Profiler: REACT_PROFILER_TYPE,
  StrictMode: REACT_STRICT_MODE_TYPE,
  Suspense: REACT_SUSPENSE_TYPE,
  unstable_SuspenseList: REACT_SUSPENSE_LIST_TYPE,

  // 重點先關注這里,生產模式下使用后者
  createElement: __DEV__ ? createElementWithValidation : createElement,
  cloneElement: __DEV__ ? cloneElementWithValidation : cloneElement,
  createFactory: __DEV__ ? createFactoryWithValidation : createFactory,
  isValidElement: isValidElement,

  version: ReactVersion,

  unstable_withSuspenseConfig: withSuspenseConfig,

  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals,

這里我們暫且先關注createElement方法,在生產模式下它來自于與React.js同級別的ReactElement.js檔案,我們打開該檔案,并直接跳轉到第312行,可以看到createElement方法的函式定義(去除了一些__DEV__環境才會執行的代碼):

/**
 * 該方法接收包括但不限于三個引數,與上述示例中的jsx語法經過轉換之后的實參進行對應
 * @param type 表示當前節點的型別,可以是原生的DOM標簽字串,也可以是函式定義組件或者其它型別
 * @param config 表示當前節點的屬性配置資訊
 * @param children 表示當前節點的子節點,可以不傳,也可以傳入原始的字串文本,甚至可以傳入多個子節點
 * @returns 回傳的是一個ReactElement物件
 */
export function createElement(type, config, children) {
  let propName;

  // Reserved names are extracted
  // 用于存放config中的屬性,但是過濾了一些內部受保護的屬性名
  const props = {};

  // 將config中的key和ref屬性使用變數進行單獨保存
  let key = null;
  let ref = null;
  let self = null;
  let source = null;

  // config為null表示節點沒有設定任何相關屬性
  if (config != null) {

    // 有效性判斷,判斷 config.ref !== undefined
    if (hasValidRef(config)) {
      ref = config.ref;
    }

    // 有效性判斷,判斷 config.key !== undefined
    if (hasValidKey(config)) {
      key = '' + config.key;
    }

    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;

    // Remaining properties are added to a new props object
    // 用于將config中的所有屬性在過濾掉內部受保護的屬性名后,將剩余的屬性全部拷貝到props物件中存盤
    // const RESERVED_PROPS = {
    //   key: true,
    //   ref: true,
    //   __self: true,
    //   __source: true,
    // };
    for (propName in config) {
      if (
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)
      ) {
        props[propName] = config[propName];
      }
    }
  }

  // Children can be more than one argument, and those are transferred onto
  // the newly allocated props object.
  // 由于子節點的數量不限,因此從第三個引數開始,判斷剩余引數的長度
  // 具有多個子節點則props.children屬性存盤為一個陣列
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    
    // 單節點的情況下props.children屬性直接存盤對應的節點
    props.children = children;
  } else if (childrenLength > 1) {
    
    // 多節點的情況下則根據子節點數量創建一個陣列
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    props.children = childArray;
  }

  // Resolve default props
  // 此處用于決議靜態屬性defaultProps
  // 針對于類組件或函式定義組件的情況,可以單獨設定靜態屬性defaultProps
  // 如果有設定defaultProps,則遍歷每個屬性并將其賦值到props物件中(前提是該屬性在props物件中對應的值為undefined)
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  
  // 最侄訓傳一個ReactElement物件
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

經過上述分析我們可以得出,在類組件的render方法中最侄訓傳的是由多個ReactElement物件組成的多層嵌套結構,所有的子節點資訊均存放在父節點的props.children屬性中,我們將原始碼定位到ReactElement.js的第111行,可以看到ReactElement函式的完整實作:

/**
 * 為一個工廠函式,每次執行都會創建并回傳一個ReactElement物件
 * @param type 表示節點所對應的型別,與React.createElement方法的第一個引數保持一致
 * @param key 表示節點所對應的唯一標識,一般在串列渲染中我們需要為每個節點設定key屬性
 * @param ref 表示對節點的參考,可以通過React.createRef()或者useRef()來創建參考
 * @param self 該屬性只有在開發環境才存在
 * @param source 該屬性只有在開發環境才存在
 * @param owner 一個內部屬性,指向ReactCurrentOwner.current,表示一個Fiber節點
 * @param props 表示該節點的屬性資訊,在React.createElement中通過config,children引數和defaultProps靜態屬性得到
 * @returns 回傳一個ReactElement物件
 */
const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // This tag allows us to uniquely identify this as a React Element
    // 這里僅僅加了一個$$typeof屬性,用于標識這是一個React Element
    $$typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,

    // Record the component responsible for creating this element.
    _owner: owner,
  };
  
  ...
  
  return element;
};

一個ReactElement物件的結構相對而言還是比較簡單,主要是增加了一個$$typeof屬性用于標識該物件是一個React Element型別,REACT_ELEMENT_TYPE在支持Symbol型別的環境中為symbol型別,否則為number型別的數值,與REACT_ELEMENT_TYPE對應的還有很多其他的型別,均存放在shared/ReactSymbols目錄中,這里我們可以暫且只關心這一種,后面遇到其他型別再來細看,

2.2 Component & PureComponent

了解完ReactElement物件的結構之后,我們再回到之前的示例,通過繼承React.Component我們將App組件修改為了一個類組件,我們不妨先來研究下React.Component的底層實作,React.Component的原始碼存放在packages/react/src/ReactBaseClasses.js檔案中,我們將原始碼定位到第21行,可以看到Component建構式的完整實作:

/**
 * 建構式,用于創建一個類組件的實體
 * @param props 表示所擁有的屬性資訊
 * @param context 表示所處的背景關系資訊
 * @param updater 表示一個updater物件,這個物件非常重要,用于處理后續的更新調度任務
 */
function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  // If a component has string refs, we will assign a different object later.
  // 該屬性用于存盤類組件實體的參考資訊
  // 在React中我們可以有多種方式來創建參考
  // 通過字串的方式,如:<input type="text" ref="inputRef" />
  // 通過回呼函式的方式,如:<input type="text" ref={(input) => this.inputRef = input;} />
  // 通過React.createRef()的方式,如:this.inputRef = React.createRef(null); <input type="text" ref={this.inputRef} />
  // 通過useRef()的方式,如:this.inputRef = useRef(null); <input type="text" ref={this.inputRef} />
  this.refs = emptyObject;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  // 當state發生變化的時候,需要updater物件去處理后續的更新調度任務
  // 這部分涉及到任務調度的內容,在后續分析到任務調度階段的時候再來細看
  this.updater = updater || ReactNoopUpdateQueue;
}

// 在原型上新增了一個isReactComponent屬性用于標識該實體是一個類組件的實體
// 這個地方曾經有面試官考過,問如何區分函式定義組件和類組件
// 函式定義組件是沒有這個屬性的,所以可以通過判斷原型上是否擁有這個屬性來進行區分
Component.prototype.isReactComponent = {};

/**
 * 用于更新狀態
 * @param partialState 表示下次需要更新的狀態
 * @param callback 在組件更新之后需要執行的回呼
 */
Component.prototype.setState = function(partialState, callback) {
  ...
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

/**
 * 用于強制重新渲染
 * @param callback 在組件重新渲染之后需要執行的回呼
 */
Component.prototype.forceUpdate = function(callback) {
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

上述內容中涉及到任務調度的會在后續講解到調度階段的時候再來細講,現在我們知道可以通過原型上的isReactComponent屬性來區分函式定義組件和類組件,事實上,在原始碼中就是通過這個屬性來區分Class ComponentFunction Component的,可以找到以下方法:

// 回傳true則表示類組件,否則表示函式定義組件
function shouldConstruct(Component) {
  return !!(Component.prototype && Component.prototype.isReactComponent);
}

Component建構式對應的,還有一個PureComponent建構式,這個我們應該還是比較熟悉的,通過淺比較判斷組件前后傳遞的屬性是否發生修改來決定是否需要重新渲染組件,在一定程度上避免組件重渲染導致的性能問題,同樣的,在ReactBaseClasses.js檔案中,我們來看看PureComponent的底層實作:

// 通過借用建構式,實作典型的寄生組合式繼承,避免原型污染
function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;

function PureComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  // If a component has string refs, we will assign a different object later.
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

// 將PureComponent的原型指向借用建構式的實體
const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());

// 重新設定建構式的指向
pureComponentPrototype.constructor = PureComponent;

// Avoid an extra prototype jump for these methods.
// 將Component.prototype和PureComponent.prototype進行合并,減少原型鏈查找所浪費的時間(原型鏈越長所耗費的時間越久)
Object.assign(pureComponentPrototype, Component.prototype);

// 這里是與Component的區別之處,PureComponent的原型上擁有一個isPureReactComponent屬性
pureComponentPrototype.isPureReactComponent = true;

通過以上分析,我們就可以初步得出ComponentPureComponent之間的差異,可以通過判斷原型上是否擁有isPureReactComponent屬性來進行區分,當然更細粒度的區分,還需要在閱讀后續的原始碼內容之后才能見分曉,

3、面試考點

看完以上內容,按道理來說以下幾個可能的面試考點應該就不成問題了,或者說至少也不會遇到一個字也回答不了的尷尬局面,試試看吧:

4、總結

本文作為React16原始碼解讀的開篇,先講解了幾個比較基礎的前置知識點,這些知識點有助于我們在后續分析組件的任務調度和渲染程序時能夠更好地去理解原始碼,閱讀原始碼的程序是痛苦的,一個原因是原始碼量巨大,檔案依賴關系復雜容易讓人產生恐懼退縮心理,另一個是閱讀原始碼是個漫長的程序,期間可能會占用你學習其他新技術的時間,讓你無法完全靜下心來,但是其實我們要明白的是,學習原始碼不只是為了應付面試,原始碼中其實有很多我們可以借鑒的設計模式或者使用技巧,如果我們可以學習并應用到我們正在做的專案中,也不失為一件有意義的事情,后續文章就從ReactDOM.render方法開始,一步一步分析組件渲染的整個流程,我們也不需要去搞懂每一行代碼,畢竟每個人的思路不太一樣,但是關鍵步驟我們還是需要去多花時間理解的,

5、交流

如果你覺得這篇文章的內容對你有幫助,能否幫個忙關注一下筆者的公眾號[前端之境],每周都會努力原創一些前端技術干貨,關注公眾號后可以邀你加入前端技術交流群,我們可以一起互相交流,共同進步,

文章已同步更新至Github博客,若覺文章尚可,歡迎前往star!

你的一個點贊,值得讓我付出更多的努力!

逆境中成長,只有不斷地學習,才能成為更好的自己,與君共勉!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/160109.html

標籤:JavaScript

上一篇:JS高級---學習roadmap---5 parts

下一篇:松軟科技web教程:JavaScript HTML DOM 元素

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我們使用的是GraphQL, 雖然GraphQL的使用處于上升趨勢,但是Rest API還是使用的更廣泛一些. 所以還是決定回到傳統的rest api framework上來, Django rest framework的官網上給了一個很好用的QuickStart, 我參考Qu ......

    uj5u.com 2023-04-20 08:17:54 more
  • 記錄-new Date() 我忍你很久了!

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 大家平時在開發的時候有沒被new Date()折磨過?就是它的諸多怪異的設定讓你每每用的時候,都可能不小心踩坑。造成程式意外出錯,卻一下子找不到問題出處,那叫一個煩透了…… 下面,我就列舉它的“四宗罪”及應用思考 可惡的四宗罪 1. Sa ......

    uj5u.com 2023-04-20 08:17:47 more
  • 使用Vue.js實作文字跑馬燈效果

    實作文字跑馬燈效果,首先用到 substring()截取 和 setInterval計時器 clearInterval()清除計時器 效果如下: 實作代碼如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta ......

    uj5u.com 2023-04-20 08:12:31 more
  • JavaScript 運算子

    JavaScript 運算子/運算子 在 JavaScript 中,有一些運算子可以使代碼更簡潔、易讀和高效。以下是一些常見的運算子: 1、可選鏈運算子(optional chaining operator) ?.是可選鏈運算子(optional chaining operator)。?. 可選鏈操 ......

    uj5u.com 2023-04-20 08:02:25 more
  • CSS—相對單位rem

    一、概述 rem是一個相對長度單位,它的單位長度取決于根標簽html的字體尺寸。rem即root em的意思,中文翻譯為根em。瀏覽器的文本尺寸一般默認為16px,即默認情況下: 1rem = 16px rem布局原理:根據CSS媒體查詢功能,更改根標簽的字體尺寸,實作rem單位隨螢屏尺寸的變化,如 ......

    uj5u.com 2023-04-20 08:02:21 more
  • 我的第一個NPM包:panghu-planebattle-esm(胖虎飛機大戰)使用說明

    好家伙,我的包終于開發完啦 歡迎使用胖虎的飛機大戰包!! 為你的主頁添加色彩 這是一個有趣的網頁小游戲包,使用canvas和js開發 使用ES6模塊化開發 效果圖如下: (覺得圖片太sb的可以自己改) 代碼已開源!! Git: https://gitee.com/tang-and-han-dynas ......

    uj5u.com 2023-04-20 08:01:50 more
  • 如何在 vue3 中使用 jsx/tsx?

    我們都知道,通常情況下我們使用 vue 大多都是用的 SFC(Signle File Component)單檔案組件模式,即一個組件就是一個檔案,但其實 Vue 也是支持使用 JSX 來撰寫組件的。這里不討論 SFC 和 JSX 的好壞,這個仁者見仁智者見智。本篇文章旨在帶領大家快速了解和使用 Vu ......

    uj5u.com 2023-04-20 08:01:37 more
  • 【Vue2.x原始碼系列06】計算屬性computed原理

    本章目標:計算屬性是如何實作的?計算屬性快取原理以及洋蔥模型的應用?在初始化Vue實體時,我們會給每個計算屬性都創建一個對應watcher,我們稱之為計算屬性watcher ......

    uj5u.com 2023-04-20 08:01:31 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:01:10 more
  • http1.1與http2.0

    一、http是什么 通俗來講,http就是計算機通過網路進行通信的規則,是一個基于請求與回應,無狀態的,應用層協議。常用于TCP/IP協議傳輸資料。目前任何終端之間任何一種通信方式都必須按Http協議進行,否則無法連接。tcp(三次握手,四次揮手)。 請求與回應:客戶端請求、服務端回應資料。 無狀態 ......

    uj5u.com 2023-04-20 08:00:32 more