React中级学习(第二天)
JSX 語法的轉化過程 (了解)
演示 : babel中文網試一試 let h1 =
- JSX 僅僅是createElement() 方法的語法糖 (簡化語法)
- JSX 語法 被 @babel/preset-react 插件編譯為 createElement() 方法
- React 元素:是一個對象,用來描述你希望在屏幕上看到的內容
- React 元素 最后 被 ReactDOM.render(<Child/>,document.getElementById('root')) 渲染顯示到頁面
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-9hqEG5rw-1596329786917)(C:/Users/wangyu123/Desktop/新建文件夾/面試題/md-imgs/jsx.png)]
- 演示:
組件更新機制
- setState 的兩個作用
- 修改state
- 重新調用render , 更新組件(UI)
- 過程 : 父組件重新渲染時, 也會重新染當前組件子樹
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-l4SpZboe-1596329786920)(C:/Users/wangyu123/Desktop/新建文件夾/面試題/md-imgs/更新機制.png)]
演示代碼 :
- App > P1+P2> C1+C2
- 這樣效果是出來了,但是它確實有很嚴重的性能問題, 因為子組件都沒有任何變化,如果重新渲染,那么就會重新調用render() 渲染頁面, 有損性能
- 所以需要進行處理 組件性能優化
組件性能優化
優化1: 減輕 state
- 原則 : state 中 只存儲跟組件渲染相關的數據 (比如 : count / 列表數據 等)
- 不用做渲染的數據 不要放在 state 中, (比如 定時器 id )
優化2 : 避免不必要的重新渲染
- 組件更新機制 : 父組件更新會引起子組件也被更新
- 問題 : 子組件沒有任何變化時, 也會重新渲染
- 如何避免不必要的重新渲染呢 ?
- 解決方式 : 使用 鉤子函數` shouldComponentUpdate(nextProps, nextState)
- nextProps : 最新的屬性
- nextState : 最新的狀態
- 場景 : 比較更新前后的 state 或者 props 是否相同, 來決定是否 更新組件
- 作用 : 通過返回值 決定該組件是否需要重新渲染, 返回true ,表示重新渲染, false 表示不重新渲染
- 觸發時機 : 更新階段的鉤子函數, 組件重新渲染 前 執行
- 順序 : shouldComponentUpdate() ==> render() ==> componentDidMount()
- 演示 : 點擊父組件的計算器的數據count
優化3 : 純組件 - PureComponent
- 純組件
- 作用 : 自動實現了 shouldComponentUpdate() 鉤子函數, 不需要再手動對比更新前后的props 或者 state , 來阻止不必要的更新了
- 原理 : PureComponent 內部, 會別對比更新前后的props 以及更新前后的state , 只要有一個不同, 就會讓組件更新, 只有在兩者都相同的情況下, 才會阻止組件更新
PureComponent 內部原理
參考 : API Reference => React => React.PureComponent
-
PureComponent
-
說明 : PureComponent 內部會比較更新前后的 props 和 state 分別進行淺對比
-
對于簡單/值類型來說, 比較兩個值是否相同 (直接賦值即可, 沒有坑)
- 對于引用類型來說, 只比較對象的引用(地址) 是否相同
- 造成的結果 : 對象里 的數據變化,不更新
- 正確做法 : 根據現有狀態生成一個新對象, 然后再更新狀態
- 正確做法說明:
- 在 PureComponent 中 使用引用類型 的狀態時, 應該每次都創建一個新的狀態, 而不是直接修改當前狀態
- 因為PureComponent 是淺對比, 所以,如果直接修改當前對象中的屬性, 會造成: 對象中的值變了, 但是引用地址沒有改變, 而導致組件不會被更新, 這樣的話就出現bug了
- 注意 , 在 React 中, ( 不管是PureComponent 還是 Component ) , 都不要直接修改引用類型的狀態值, 而是要創建一個新的狀態, 修改新的狀態,然后再更新
在 React 組件 中更新應用類型的狀態
- 文檔 : 不可變數據的力量
- 注意 : 對于引用類型的狀態來說, 應該創建新的狀態, 而不要直接修改當前狀態
- 原則 : 狀態不可變!!! 數據不要變, 直接創建新的
- 對象狀態 :
- 數組狀態
虛擬DOM的真正價值
- 虛擬 DOM 的真正價值從來都不是性能。
- 真正的價值:虛擬DOM 能夠讓 React 擺脫瀏覽器的限制(束縛)。也就是,只要能夠運行JS代碼的地方,就能夠運行 React。
- 跨平臺
- JSX => 虛擬DOM => react-dom => DOM 元素=> 瀏覽器
- JSX => 虛擬DOM => React-Native => ios和安卓的元素 => 移動混合開發
- JSX => 虛擬DOM => 工具 => VR
React 組件
- (state, props) => UI
路由基礎
路由介紹
- 路由 : 就是一套映射規則, 是url中 哈希值 與 展示視圖 之間的一種對應關系
- 為什么要學習路由 ?
- 現代的前端應用大多都是 SPA(單頁應用程序),也就是只有一個 HTML 頁面的應用程序。
- 因為它的用戶體驗更好、對服務器的壓力更小,所以更受歡迎。
- 為了有效的使用單個頁面來管理原來多頁面的功能,前端路由 應運而生。
- 使用React路由簡單來說,就是配置 路徑 和 組件(配對)
基本使用
- 安裝 : yarn add react-router-dom
常用組件的使用介紹
- 引入的三個組件
- BrowserRouter 組件 : 使用 Router 組件包裹整個應用 (才能使用路由)
- Link 組件 : 創建一個導航菜單 (路由入口)
- 最終會生成一個a標簽, 通過 to 屬性指定 pathname(history /) 或 hash(哈希模式 #)
- Route 組件 : 用來配置路由規則和要展示的組件 (路由出口)
- path : 配置路由規則
- component : 指定當前路由 規則匹配時要展示的組件
- Route 組件放在哪, 組件內容就展示在哪, 并且每一個路由都是一個單獨的Route組件
路由的執行過程
- 當點擊 Link 的時候,就會修改瀏覽器中的 pathname
- 只要 瀏覽器地址欄中的 pathname 發生改變,React 路由就會監聽到這個改變
- React 路由監聽到 pathname 改變后,就會遍歷所有 Route 組件,分別使用 Route 組件中的 path 路由規則,與當前的 瀏覽器地址欄中的pathname進行匹配
- 只要匹配成功,就會把當前 Route 對應的組件,展示在頁面中
- 注意:匹配時,不是找到第一個匹配的路由就停下來了。而是: 所有的 Route 都會進行匹配,只要匹配就會展示該組件。
- 也就是說:在一個頁面中,可以有多個 Route 同時被匹配
使用 Switch 組件 ,匹配一個
{/* Switch 只會讓 組件顯示出來一個 */} <Switch><Route path="/one" component={One}></Route><Route path="/two" component={Three}></Route><Route path="/two" component={Two}></Route> </Switch>編程式導航
-
改變入口的三種方式 :
-
手動輸入
-
聲明式導航 : (html)
- 編程式導航 : 通過js代碼來實現的跳轉/返回 (js)
-
編程式導航 :
-
可以通過props 拿到 跳轉和返回的方法
-
正常的組件, 打印 props => 默認是 一個空對象 {}
-
凡是參與路由匹配出來的組件 , 路由都會給他們傳入三個屬性 history, location, match
-
history : (主要用來編程式導航)
- push() 跳轉到另外一個頁面 push(path,state)
- goBack() 返回上一個頁面
- replace() 跳轉到另外一個頁面
-
location : (位置路徑的)
-
pathname : 路徑
- state : 通過跳轉傳遞的數據
-
match : 獲取參數
- params : 可以拿到動態路由里的參數 params : {id : 123}
備
HashRouter 傳參的方式和 BrowserRouter 傳參的方式不一樣this.props.history.push({pathname: '/pay',state: {name: 'zs'}})默認路由 - 根路徑 /
- 默認路由地址為:/
- 默認路由在進入頁面的時候,就會自動匹配
匹配模式
問題:當 Link組件的 to 屬性值為 “/login”時,為什么 默認路由/ 也被匹配成功?
- 默認情況下,React 路由是: 模糊匹配模式
- 模糊匹配:只要 pathname 以 path 開頭就會匹配成功
- path 代表Route組件的path屬性
- pathname 代表Link組件的to屬性(也就是url中 location.pathname)
- 精確匹配:只有當 path 和 pathname 完全匹配時才會展示該路由
- 解決辦法 : 給 Route 組件添加 exact 屬性,讓其變為精確匹配模式
重定向
- 需求 : 使用 重定向 '/' => '/one'
- 方式1 : render-props
- 方式2 - children
路由兩種模式的說明
哈希模式
1. 訪問路徑 : http://localhost:8080/#/one http://localhost:8080/#/two2. 服務器接收到的 (服務器是不會 接收 # 后面的內容的)3. 不管訪問的路徑是什么樣的 http://localhost:8080 ==> 服務器返回的默認 的就是 index.html4. 后面的 /one 和 /two 由路由來使用, 根據路由匹配規則找到對應的組件顯示 5. 哈希模式 不管是 開發階段還是發布階段,都是沒有問題的history 模式
1. 訪問路徑 : http://localhost:8080/one http://localhost:8080/two2. 服務器接收到的 http://localhost:8080/one 和 http://localhost:8080/two但是,/one 和 /two 這個路徑是不需要服務器端做任何處理的。 3. http://localhost:8080/getNewshttp://localhost:8080/detail 它們都是 接口地址 , 后面遇到 類似 /one 和 /two 都會以為是接口 是要返回數據的呢? 3. 所以,應該在服務器端添加一個路由配置,直接返回 SPA 的 index.html 頁面就行啦。 4. 類似處理app.get('/getNews', (req,res) => {// 根據 res 返回 對應的數據res.json { ..... }})app.get('/detail', (req,res) => {// 根據 res 返回 對應的數據res.json { ..... }})// 最后 額外再多加一個, 專門用來返回 index.htmlapp.use('*', (req,res) => {res.sendFile('index.html')})總結 :
history模式 : - 開發階段 : webpack腳手架已經處理好了, - 發布階段 : 服務器是公司的服務器, 可能就會報錯 - 我們要做的就是`告訴后臺`,我們使用的 是 history模式,讓他專門處理一下,就可以了 - 如果后臺不給處理,或者處理不好, 我們就使用 `哈希模式`類似 /one 和 /two 都會以為是接口 是要返回數據的呢?
3. 所以,應該在服務器端添加一個路由配置,直接返回 SPA 的 index.html 頁面就行啦。
4. 類似處理
app.get(’/getNews’, (req,res) => {
// 根據 res 返回 對應的數據
res.json { … }
})
app.get(’/detail’, (req,res) => {
// 根據 res 返回 對應的數據
res.json { … }
})
總結
以上是生活随笔為你收集整理的React中级学习(第二天)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: React中级学习(第一天)
- 下一篇: vuex的结构有哪些参数?