主頁 > 企業開發 > 我的 React 最佳實踐

我的 React 最佳實踐

2022-11-03 07:41:34 企業開發

There are a thousand Hamlets in a thousand people's eyes. ----- 威廉·莎士比亞

免責宣告:以下充滿個人觀點,辯證學習

React 目前開發以函陣列件為主,輔以 hooks 實作大部分的頁面邏輯,目前數堆疊的 react 版本是 16.13.1,該版本是支持 hooks 的,故以下實踐是 hooks 相關的最佳實踐,

前置理解

首先,應當明確 React 所推崇的函式式編程以及 f(data) = UI 是什么?

函式式編程

函式式編程是什么?這里的函式并非 JavaScript 中的函式,或任何語言中的函式,此處的函式是數學概念中的函式,讓我們來回憶一下數學中的函式是什么,

file

在數學概念中,函式即一種特殊的映射,如何理解映射

以一元二次方程為例, f(x) 是一種映射關系,給定一個的 x ,則存在 y 與之對應,

我們將一元二次方程理解為一個黑盒,該黑盒存在一個輸入,一個輸出,在輸入端我們給入一個值 x = 2 則輸出端必然會給出一個 y = 4

f(data)=UI

在了解了數學中函式的概念后,將其概念套用到 React 中,我們就可以明白 f(data)=UI 到底指什么意思?

結論:個人理解,將當前組件內部的所有邏輯視為一個黑盒,該黑盒有且僅有一個輸入,有且僅有一個輸出,輸入端為 props,輸出端則是當前組件的 UI ,不同的輸入會決定不同的輸出,就像把 x = 1x = 2 給到所得到的結果是不一樣的一樣,而將這樣的組件組合起來就是每一個頁面,即 React 應用,

業務開發

目前絕大部分的后臺管理系統,不僅僅是數堆疊,包括所有 ERP 系統,圖書管理系統等等,其主體頁面大致可以包括一下三類:

  • 篩選條件(Filter)+ 表格(Table)
  • 表單(Form)相關的新增,編輯功能
  • 概覽頁面(Overview),包括一些圖表和表格

第一類

file

以上是這次在資產中負責開發的檔案治理規則頁面,屬于是第一類的典型頁面,那這一類的頁面應該如何開發才是最佳實踐?

首先,我們將這一類頁面抽象成如下結構
file

如此一來,我們不難實作如下的 dom 結構

<div className="container">
  <div className="header">
    <div className="filter">篩選區</div>
    <div className="buttonGroup">按鈕區</div>
  </div>
  <div className="content">表格區</div>
</div>

接下來,我們需要補充各個區域的內容,

篩選區通常會有一些篩選項,這里我們有兩個選擇,如果比較復雜的篩選項,如存在 5 個以上或存在聯動的互動,則選擇通過 Form 來實作,而上圖中這種比較簡單的則可以直接實作,
這里我們不難觀察到我們需要 3 個 value 值來實作這 3 個輸入框或下拉框的受控模式,這里我們通過宣告一個 filter 的變數,將這 3 個值做一個整合,

function (){
  const [filter, setFilter] = useState({
    a: undefined,
    b: undefined,
    c: undefined
  });
  
  return <><>
}

提問:為什么要 3 個值都放入到一個變數里?

答:1. 減少冗長的定義, 2. 為后續鋪墊,

宣告完 3 個值后,我們把組件填入

function (){
  const [filter, setFilter] = useState({
    a: undefined,
    b: undefined,
    c: undefined
  });

  return (
    <div className="container">
      <div className="header">
        <div className="filter">
          <Input value=https://www.cnblogs.com/dtux/p/{filter.a} onChange={(e) => setFilter(f => ({...f, a: e.target.value})} />
           setFilter(f => ({...f, b: e.target.value})} />
           setFilter(f => ({...f, c: e.target.value})} />
        

接著,我們實作表格區,有經驗的同學可以知道,一個 Table 需要 dataSource 資料,columnspaginationtotal,有時候還需要 selectedRowsorterfilter

我們先考慮 dataSource 和 columns,首先,我們先考慮資料獲取,實作 getDataSourceList

interface ITableProps {}

function (){
  const [filter, setFilter] = useState({
    a: undefined,
    b: undefined,
    c: undefined
  });
  const [loading, setLoading] = useState(false);
  const [dataSource, setDataSource] = useState<ITableProps>([]);

  const getDataSourceList = () => {
    setLoading(true);
    Promise.then(() => {
      /// xxxx
    }).finally(() => {
      setLoading(false);
    })
  };

  return (
    <div className="container">
      <div className="header">
        <div className="filter">
          <Input value=https://www.cnblogs.com/dtux/p/{filter.a} onChange={(e) => setFilter(f => ({...f, a: e.target.value})} />
           setFilter(f => ({...f, b: e.target.value})} />
           setFilter(f => ({...f, c: e.target.value})} />
        

可以看到,我們新增了 loading 變數,用來優化互動的程序,新增了 ITableProps 型別,用來宣告表格資料的型別,此時應當和服務端的同學溝通,從介面檔案中獲取到相關的資料結構,并補全這一塊的型別

假設我們此時已經完成了型別的補全,那接下來我們需要完善 columns

const columns: ColumnType<ITableProps>[] = [];

這里存在部分分歧,有部分人認為需要加上 useMemo
為什么不加 useMemo?

  1. 因為 useMemo 主要是為了快取計算量比較大的值,而此處并沒有計算量,只是一個變數的宣告而已
  2. 如果重復宣告 useMemo 的話,是否會導致 Table 的重復渲染,我認為過早的性能優化不如不優化,如果真的存在這樣子的問題再進行優化也不急,
  3. 如果這里加上 useMemo 在某些情況下會有比較多的 depths 需要加到依賴項里,個人感覺這不優雅

在完善 columns 后,我們繼續剛才提到的 Paginationtotal 欄位,
這里我們需要宣告兩個變數

const [pagination, setPagination] = useState({current: 1, pageSize: 20});
const [total, setTotal] = useState(0);

思考:這里的 total 和 pagination 是否可以合并成一個變數?

答:可以,但是我不愿意,因為我們后面會有存在如下代碼,會導致無限回圈

useEffect(() => { 
  // getDataSourceList 中存在改變 total 的值,會導致無限回圈
  // 如果要解決這個問題,則需要在 depths 中分別寫 current 和 pageSize
  // 我不愿意
  getDataSourceList();
},[pagination]);

然后接下來我們要實作請求功能,不難總結出我們需要在以下幾種情況下做請求:

  • 頁面初始化
  • Pagination 改變進行請求
  • 篩選條件改變進行請求
  • Table 的 filter 或 sorter 發生改變進行請求
  • 互動修改資料后進行請求(如洗掉,新增等)

除了最后這個暫時不做考慮,第三第四項的請求我們可以再細分為如圖所示
file

那么,就可以和前兩項進行合并,總結如下:

  • 頁面初始化
  • Pagination 改變

將上述思路轉化為代碼可得

function (){
  const [filter, setFilter] = useState<Record<string, any>>({
    a: undefined,
    b: undefined,
    c: undefined,
    d: undefined,
    sorter: undefined
  });
  const [pagination, setPagination] = useState({ current: 1, pageSize: 20 });

  const getDataSourceList = () => {};

  useEffect(() => {
    setPagination(p => ({ ...p, current: 1 }));
  }, [filter]);

  useEffect(() => {
    getDataSourceList();
  }, [pagination]);
}

到這里,我們已經實作了絕大部分主要功能,接下來我們簡單實作一個 selectedRows即可,

function (){
  const [selectedRows, setSelectedRows] = useState([]);

  return (
    <Table
      rowSelection={{
        selectedRowKeys,
        onChange: (selected) => setSelectedRows(selected)
      }}
    />
  )
}

問:為什么這里的 rowSelection 不提出來,放到 useMemo 里去?

至此,一個滿足業務的一類業務相關的框架代碼已經撰寫完成,
到這里之后,一類業務頁面還有剩下什么東西需要開發?

第二類

二類頁面的特點通常是新增和編輯復用同一個頁面,需要 Form 表單,

除此之外,通常二類頁面有通過 Drawer 或者 Modal 或者跳轉路由的方式,但這不影響代碼的書寫,
不論是 Drawer 還是 Modal 還是路由的方式,我們都需要將表單內容抽離出一個新的組件,

首先,我們看一個比較普遍且較為簡單的新增或編輯頁面
file

可以觀察到,這個表單通過 Steps 組件分割成了 3 個步驟

這里我們需要明確一個思想,即上面強調的 ,那么這里不論是新增還是編輯,其差異只存在于 data 中是否存在 id 值,
這里有兩種做法,第一種做法是 3 個步驟只用一個 Form,第二種做法是 3 個步驟 3 個 Form,我個人比較喜歡第一種做法,理由如下:

  • 因為 data 是一份的,如果用第二種做法,需要將一份 data 拆分成三份
  • 倘若出現在第一步中填寫某個值會影響第二步中的下拉項,如果是第二種做法,還需要將 Form 的值傳給第二步的組件,第一步的做法可以直接通過 form.getFieldValue('xxx')
  • 無它,就是圖個簡潔

比較復雜的表單通常都有聯動情況,比如資料同步任務的表單,或者這里的資料源和表選擇的互動,
這一類互動在 antd@3 中通常實作起來會比較的繁瑣,在 antd@4中善用 dependenciesonValuesChanged可以很好地解決這一類問題,

{[TRINO, KINGBASEES8, SAPHANA1X].includes(dataSourceType) && (
  <FormItem
    name="schemaName"
    label="選擇Schema"
    initialValue=https://www.cnblogs.com/dtux/p/{schemaName}
    rules={[
      {
        required: true,
        message:'請選擇Schema',
      },
    ]}
    >
    <Select
      showSearch
      style={{
        width: '100%',
      }}
      onSelect={this.onSchemaChange}
      onPopupScroll={this.handleSchemaScroll}
      onSearch={this.handleSchemaSearch}
      >
      {this.renderSchemaListOption(currentSchema)}
    </Select>
  </FormItem>
)}

如上代碼所示, schema 的欄位只有在所選擇的資料源是 xxx 這幾種情況下才會展示,如果我們按照上述代碼的寫法的話,需要在 state 中新增 dataSourceType欄位
那如果用 dependencies的話,可以改成如下寫法:

<FormItem noStyle dependencies={['sourceId']}>
  {({ getFieldValue }) => (
  isXXXXX(options.find(o => o.sourceId === getFieldValue('sourceId'))?.type) && (
    <FormItem
      name="schemaName"
      label="選擇Schema"
      initialValue=https://www.cnblogs.com/dtux/p/{schemaName}
      rules={[
        {
          required: true,
          message:'請選擇Schema',
        },
      ]}
      >
      <Select
        showSearch
        style={{
          width: '100%',
        }}
        onSelect={this.onSchemaChange}
        onPopupScroll={this.handleSchemaScroll}
        onSearch={this.handleSchemaSearch}
        >
        {this.renderSchemaListOption(currentSchema)}
      </Select>
    </FormItem>
  )
)}
</FormItem>

更進一步,可以把 find 抽象一個 getTypeBySourceId函式出來,即優化了可維護性,又減少了變數宣告,

除此之外,還有下拉選單的聯動,如 A 的選擇會引起 B 的下拉選單獲取,B 的下拉選單可能又會引起 C 的下拉選單改變,如此鏈路下去,會導致宣告的 handleChange 函式又多又長

function(){
  const handleAChanged = () => {};
  
  const handleBChanged = () => {};
  
  const handleCChanged = () => {};
  
  return (
    <Form>
      <FormItem name ="a">
        <Select onChange={handleAChanged} />
      </FormItem>
      <FormItem name ="b">
        <Select onChange={handleBChanged} />
      </FormItem>
      <FormItem name ="c">
        <Select onChange={handleCChanged} />
      </FormItem>
    </Form>
  )	
}

那我們可以借助 antd@4onValuesChanged函式,來把所有相關組件的 onChange 做合并,即如下:

function(){
   const handleFormFieldChanged = (changed: Partial<IFormFieldProps>) => {
    if('a' in changed){
      // do something about a
      getOptionsForB();
      form.resetFields(['b', 'c']);
    }

    if('b' in changed){
      // do something about b
      getOptionsForC();
      form.resetFields(['c']);
    }

    if('c' in changed){
      // do something about c
      getOptionsForD();
    }
  }
  
  return (
    <Form onValuesChanged={handleFormFieldChanged}>
      <FormItem name ="a">
        <Select />
      </FormItem>
      <FormItem name ="b">
        <Select />
      </FormItem>
      <FormItem name ="c">
        <Select />
      </FormItem>
    </Form>
  )	
}

同時,在這個函式里我們也可以順便把 reset 的操作做了

到這里,我們大致完成了 Form 表單的架構思路,接下來,我們需要處理新增和編輯的區分,
通常來說,新增是不需要賦初始值的,而編輯是需要賦初始值的,
這里需要注意的點在于,我們所理解的初始值并不是 Form 組件中 initialValue 的含義,(至少我認為不是)
我認為 Form 組件的生命周期應當分為如下部分:

  • 初始化階段(該階段 Form 表單用 initialValue把表單 UI 渲染出來)
  • 賦值階段(該階段用戶通過 set 操作把初始值賦給表單的 state)
  • 互動階段
  • 提交階段

如上階段中說明所示,我認為初始值的操作應當通過 set 操作完成,那代碼實作起來應當如下所示:

function(){
  useEffect(() => {
    if(router.record.id){
      setLoading(true);
      api.getxxx({recordId: record.id}).then(res => {
        if(res.success){
          form.setFieldsValue({
            a: res.data.a,
            b: res.data.b
          })
        }
      }).finally(() => {
        setLoading(false);
      });
    }
  }, []);
}

把編輯的賦值操作全都放到 useEffect 中執行,統一了書寫的地方,有利于后期的維護,

總結后,可以得出整個 Form 表單頁面的概覽大致如下圖所示
file

相信各位同學到這里之后,面對一個表單的原型圖,心里已經有一個大致的偽代碼的實作了,

第三類

通常概覽頁面的要素是,統計資料、時間選擇、圖表,這一類頁面由于通常是作為用戶第一個打開的頁面,所以需要額外注意的是 loading 狀態的展示,

需要注意的是這里的 loading 會存在以下幾種情況:

  • 整個頁面的 loading,這種情況通常是全域的一個時間選擇器,然后同時獲取統計資料和圖表,需要借助 Promise.allPromise.allSettled 實作,
  • 磁區域的 loading,如圖表區有圖表區的 loading,表格區有表格的 loading,統計資料區有統計資料區的 loading,這種時候需要把 loading 更加細分,為每一個區域增加 loading 變數,確保各個請求都有 loading 狀態展示

其他沒什么好說的,主要是 CSS 的要求會更高,大致的偽代碼會如下:

function(){
  const [options, setOptions] = useState({...defaultOptions});
  const [loading, setLoading] = useState(false);
  const [timeRange, setTimeRange] = useState([moment(), moment()]);
  const [statistic, setStatistic] = useState({
    a: 0,
    b: 0,
  });

  const getStatistic = () => {
    return new Promise((resolve) => {
      setStatistic({a: 1000, b: 30});
      resolve();
    });
  };

  const getCharts = () => {
    return new Promise((resolve) => {
      options.xxx = xxxx;
      setOptions({...options});
    });
  };

  useEffect(() => {
    setLoading(true);
    Promise.all([getStatistic(), getCharts()]).finally(() => {
      setLoading(false);
    });
  }, [timeRange]);


  return (
    <>
      <DateRange value=https://www.cnblogs.com/dtux/p/{timeRange} onChange={(val) => setTimeRange(val)} />
      
      
    
  )
}

代碼開發

通常來說,我個人的習慣是先實作需求,再進行代碼優化和分割,模塊的提取等,所以以下優化都是基于所有業務邏輯已經完成的情況下,

hooks

通常,在完成業務后,一個組件內部會包含大量的 hooks 相關的東西,通常優化手段如下:

  • 減少 useState的使用,將不影響渲染的資料放到 useRef 里去,甚至說常量可以放到函式外部,同時,如果是同一個種類的可以進行合并
  • 減少 useMemo 的時候,普通的賦值或宣告或簡單的計算完全不需要引入 useMemo,可以在復雜的計算時加上 useMemo
  • 避免 useCallback 的使用,目前想到的 useCallback 的場景,只有addEventListener的時候,其余情況下大部分都用不到,
  • useEffect 可以進行寫多個的,所以有些時候不同的邏輯可以放到不同的 useEffect 里去
  • useRef 可以大量持有,useRefcover 的場景遠大于 ref
  • useContext在簡單場景下完全可以替代 redux,但是有性能問題,所以復雜場景下,建議是配合use-context-selector使用,或者選擇其他狀態管理工具,如:recoil
  • useLayoutEffectuseEffect在絕大部分應用情況下沒有差異,只需要直接使用 useEffect 即可,目前考慮前者的唯一不可替代性僅存在于「閃爍」場景,
  • useReducer 一般來說用不到,其使用場景應該是當存在多個資料,而某一個引數的改變會引起其他資料同時改變,從而引起頁面重渲染,
const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}
  • useImperativeHandler在通常情況下避免使用,使用場景應該只有兩種,第一種是要做 ref 的轉發,比如封裝了一個組件,需要把組件內部的某一個 input 的 ref 轉發給父組件,第二種是封裝了一個組件,該組件內部實作邏輯是非 JSX 的,存在實體,則需要通過該 hooks 把相關實體轉發給父組件,
  • useDebugValue不熟
  • useDeferredValue不熟
  • useTransition不熟
  • useId不熟
  • useSyncExternalStore不熟
  • useInsertionEffect不熟

Ant Design 相關

眾所周知,React 庫搭配 Ant Design 食用是后臺管理系統的高效的原因之一,
Ant Design 相關實踐如下:

  • 巧用 Space 組件,個人感覺有點類似于簡單場景的 Grid 組件
  • 復雜表單經常使用 Steps 做步驟分割,個人更加推薦 Steps+Form 而不是 Steps.item+Form
  • AutoComplete 個人感覺有很多問題,譬如資料回填高亮等問題,個人感覺不如直接 Input
  • Form組件比較復雜,且配合其他組件的用法比較多,如 Form+Steps Form+Tabs Form+Modal 等,需要注意 inactiveDestroy + preserve={false},另外需要注意 requiredMarkrequired 的區別,validator 和其他 rules 的區別,setFieldsValuesetFields 的區別
  • Input等輸入控制元件需要注意 placeholder 的值,Select 需要額外主要 allowSearch
  • Select個人習慣直接通過 options 賦值,而不是 children(原因:相比 jsx 會有更加好的渲染性能)
  • select可以通過 dropdownRender自定義下拉內容
  • Tree 組件面向的場景比較復雜的情況下,個人覺得 antd 的 tree 組件不好用,偏向于自己手寫
  • 所有的 popup 相關組件都可以設定 getPopupContainer,該屬性用來修改組件渲染位置,如果遇到彈出層沒有隨滾動條滾動可以設定,但是設定完可能需要考慮 overflow:hidden 的問題
  • Table組件的 rowKey 屬性很重要,必加,
  • 需要注意 Table + Modal 的寫法,不要把 Modal 寫到操作列里面去
  • ModalDrawer組件不推薦用 visible && <Modal />的寫法,會導致影片丟失,建議在寫 Modal 或者 Drawer 的時候,把「空狀態」考慮進去,同時可以配合 ModaldestroyOnClose屬性可以讓每次 Modal 打開內容都是新內容,(PS:Form 除外)
  • Spin 可以多,但不能少

其他

  • 愛惜你的 div,不要動不動就搞個 div,過多的層級結構會影響加載速度的,量變引起質變
  • 盡量避免 ref 的使用,在通常情況下如果考慮用 ref 解決問題的話,那可能代表了你的思路不對或代碼設計不對,
  • 有時候,可以用 IIFE,做到不脫離當前的背景關系,又實作了相關邏輯的提取,比函式中定義函式更好一些,

總結

以上的相關實踐,是本人在榷訓月累中總結和摸索出來的,
如有雷同,說明你和我有一樣的感受,

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

標籤:Html/Css

上一篇:DNS原理,攻擊以及防御

下一篇:怎么樣子盒子能撐起父盒子?浮動,BFC,邊距重疊

標籤雲
其他(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