0.鏈接
狀態(tài)碼精選 https://www.cnblogs.com/yaya-003/p/12653602.html
ts精選 https://blog.csdn.net/xgangzai/article/details/119012373?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-1.pc_relevant_paycolumn_v3&spm=1001.2101.3001.4242.2&utm_relevant_index=4
1.響應(yīng)式布局有幾種方法
響應(yīng)式布局方法一:媒體查詢
使用@media媒體查詢可以針對(duì)不同的媒體類型定義不同的樣式,特別是響應(yīng)式頁面,可以針對(duì)不同屏幕的大小,編寫多套樣式,從而達(dá)到自適應(yīng)的效果。舉例來說:
通過媒體查詢,可以通過給不同分辨率的設(shè)備編寫不同的樣式來實(shí)現(xiàn)響應(yīng)式的布局,比如我們?yōu)椴煌直媛实钠聊?,設(shè)置不同的背景圖片。比如給小屏幕手機(jī)設(shè)置@2x圖,為大屏幕手機(jī)設(shè)置@3x圖,通過媒體查詢就能很方便的實(shí)現(xiàn)。
但是媒體查詢的缺點(diǎn)也很明顯,如果在瀏覽器大小改變時(shí),需要改變的樣式太多,那么多套樣式代碼會(huì)很繁瑣。
響應(yīng)式布局方法二:百分比%
比如當(dāng)瀏覽器的寬度或者高度發(fā)生變化時(shí),通過百分比單位,通過百分比單位可以使得瀏覽器中的組件的寬和高隨著瀏覽器的變化而變化,從而實(shí)現(xiàn)響應(yīng)式的效果。
height,width屬性的百分比依托于父標(biāo)簽的寬高。但是,padding、border、margin等屬性的情況又不一樣
子元素的padding如果設(shè)置百分比,不論是垂直方向或者是水平方向,都相對(duì)于直接父親元素的width,而與父元素的height無關(guān)。
子元素的margin如果設(shè)置成百分比,不論是垂直方向還是水平方向,都相對(duì)于直接父元素的width
border-radius不一樣,如果設(shè)置border-radius為百分比,則是相對(duì)于自身的寬度
缺點(diǎn)
計(jì)算困難,如果我們要定義一個(gè)元素的寬度和高度,按照設(shè)計(jì)稿,必須換算成百分比單位。
各個(gè)屬性中如果使用百分比,相對(duì)父元素的屬性并不是唯一的。比如width和height相對(duì)于父元素的width和height,而margin、padding不管垂直還是水平方向都相對(duì)比父元素的寬度、border-radius則是相對(duì)于元素自身等等,造成我們使用百分比單位容易使布局問題變得復(fù)雜。
所以,不建議用%來做響應(yīng)式布局。
響應(yīng)式布局方法三:vw/vh
css3中引入了一個(gè)新的單位vw/vh,與視圖窗口有關(guān),vw表示相對(duì)于視圖窗口的寬度,vh表示相對(duì)于視圖窗口高度。 任意層級(jí)元素,在使用vw單位的情況下,1vw都等于視圖寬度的百分之一。
與百分比布局很相似,但更好用。
響應(yīng)式布局方法四:rem
rem單位是相對(duì)于字體大小的html元素,也稱為根元素。 默認(rèn)情況下,html元素的font-size為16px。所以此時(shí)1rem = 16px。
優(yōu)化版
//動(dòng)態(tài)為根元素設(shè)置字體大小
functioninit () {
?// 獲取屏幕寬度
?varwidth = document.documentElement.clientWidth
?// 設(shè)置根元素字體大小。此時(shí)為寬的10等分
?document.documentElement.style.fontSize = width / 10 + 'px'
}
//首次加載應(yīng)用,設(shè)置一次
init()
// 監(jiān)聽手機(jī)旋轉(zhuǎn)的事件的時(shí)機(jī),重新設(shè)置
window.addEventListener('orientationchange', init)
// 監(jiān)聽手機(jī)窗口變化,重新設(shè)置
window.addEventListener('resize', init)
理解:上面代碼實(shí)現(xiàn)了,無論設(shè)備可視窗口如何變化,始終設(shè)置rem為width的1/10.即實(shí)現(xiàn)了百分比布局。就沒有第一版的媒體查詢那樣僵硬。
以上代碼需在dom之前寫入(可放在head里面第一個(gè)script標(biāo)簽)
響應(yīng)式布局方法五:flex彈性布局
彈性布局是一種十分方便的,只需要依賴于CSS樣式的實(shí)現(xiàn)響應(yīng)式布局的方式,也是最多用到的一種實(shí)現(xiàn)響應(yīng)式的方法。
尤其是現(xiàn)在類似于某寶、某東一類的電商web站或者手機(jī)app的頁面,利用彈性布局是都可以很輕松的實(shí)現(xiàn)的。
彈性布局在父、子元素上都有相對(duì)應(yīng)的屬性來規(guī)范子元素在父元素中的“彈力”。
在父元素上,我們經(jīng)常會(huì)用到的有關(guān)彈性布局的屬性主要有 flex-direction主軸方向 , flex-wrap項(xiàng)目都排在一條線(又稱”軸線”)上。flex-wrap屬性定義,如果一條軸線排不下,如何換行 , justify-content主軸上的對(duì)齊方式 , align-items交叉軸上如何對(duì)齊 , align-content 多根軸線的對(duì)齊方式,這幾個(gè)屬性分別從 主軸的方向、是否換行、項(xiàng)目在主軸上的對(duì)齊方式、項(xiàng)目在交叉軸上的對(duì)齊方式、項(xiàng)目在多根軸線上的對(duì)齊方式來規(guī)范了項(xiàng)目在父元素中的彈性。
在子元素上,我們經(jīng)常會(huì)用到的有關(guān)彈性布局的屬性主要有 order , flex-grow , flex-shrink ,flex-basis , align-self ,這幾個(gè)屬性分別從 項(xiàng)目的排序、項(xiàng)目放大比例、項(xiàng)目縮小比例、項(xiàng)目占據(jù)主軸空間、單個(gè)項(xiàng)目在交叉軸上的對(duì)齊方式來規(guī)范了項(xiàng)目自身的彈性。
2.如何讓一個(gè)元素水平/垂直居中
(1)flex布局。
? ? ? .father {
? ? ? ? width: 200px;
? ? ? ? height: 200px;
? ? ? ? border: 1px solid red;
? ? ? ? display: flex;
? ? ? ? justify-content: center;
? ? ? ? align-items: center;
? ? ? }
(2)grid布局。
? ? ? .father {
? ? ? ? width: 200px;
? ? ? ? height: 200px;
? ? ? ? border: 1px solid red;
? ? ? ? display: grid;
? ? ? ? place-items: center;
? ? ? }
(3)定位。
? ? ? .father {
? ? ? ? width: 200px;
? ? ? ? height: 200px;
? ? ? ? border: 1px solid red;
? ? ? ? position: relative;
? ? ? }
? ? ? .son {
? ? ? ? position: absolute;
? ? ? ? top: 50%;
? ? ? ? left: 50%;
? ? ? ? transform: translate(-50%, -50%);
? ? ? }
(4)對(duì)于文本。
? ? ? .father {
? ? ? ? width: 200px;
? ? ? ? height: 200px;
? ? ? ? border: 1px solid red;
? ? ? ? text-align: center;
? ? ? ? line-height: 200px;
(5) table布局。
? ? ? .father {
? ? ? ? width: 200px;
? ? ? ? height: 200px;
? ? ? ? border: 1px solid red;
? ? ? ? display: table;
? ? ? }
? ? ? .son {
? ? ? ? display: table-cell;
? ? ? ? vertical-align: middle;
? ? ? ? text-align: center;
? ? ? }
3.CSS盒子模型
CSS盒子模型就是在CSS技術(shù)所使用的一種思維模型。CSS假定所有的HTML文檔元素都生成一個(gè)描述該元素在HTML文檔布局中所占空間的矩形元素框,可以形象地將其看作是一個(gè)盒子。通過定義一系列與盒子相關(guān)的屬性,可極大地豐富和促進(jìn)各個(gè)盒子乃至整個(gè)HTML文檔的表現(xiàn)效果和布局結(jié)構(gòu)。
CSS盒子模型由內(nèi)容區(qū)、填充、邊框和空白邊四部分組成。內(nèi)容區(qū)是盒子模型的中心,呈現(xiàn)盒子的主要信息內(nèi)容;填充是內(nèi)容區(qū)和邊框之間的空間;邊框是環(huán)繞內(nèi)容區(qū)和填充的邊界;空白邊位于盒子的最外圍,是添加在邊框外周圍的空間。
1、豐富的樣式定義:CSS提供了豐富的文檔樣式外觀,以及設(shè)置文本和背景屬性的能力;允許為任何元素創(chuàng)建邊框,以及元素邊框與其他元素間的距離,以及元素邊框與元素內(nèi)容間的距離;允許隨意改變文本的大小寫方式、修飾方式以及其他頁面效果。
2、易于使用和修改:CSS可以將樣式定義在HTML元素的style屬性中,也可以將其定義在HTML文檔的header部分,也可以將樣式聲明在一個(gè)專門的CSS文件中,以供HTML頁面引用??傊?,CSS樣式表可以將所有的樣式聲明統(tǒng)一存放,進(jìn)行統(tǒng)一管理。
4.div 和? p
div元素是塊級(jí)的(簡單地說,它等同于其前后有斷行),用于組合一大塊的代碼。
p(段落)元素是塊級(jí)的,前后斷行,而且還要再隔一行.相當(dāng)于斷兩行。
5.解決本地開發(fā)接口請(qǐng)求跨域問題3種方案
1.webpack-dev-server配置跨域方案
如果你項(xiàng)目是用webpack作為前端自動(dòng)化構(gòu)建工具的話,那么可以引用webpack-dev-server來進(jìn)行配置跨域方案。webpack-dev-server是一個(gè)小型的nodejs服務(wù)器,是基于express框架的,用于實(shí)時(shí)監(jiān)聽和打包編譯靜態(tài)資源。其中里面有一個(gè)屬性是proxy,是專門來配置代理請(qǐng)求接口的。
舉個(gè)例子:
比如我現(xiàn)在通過webpack構(gòu)建了一個(gè)本地開發(fā)環(huán)境,端口號(hào)是9000,現(xiàn)在我要在本地去請(qǐng)求(GET)http://jsonplaceholder.typicode.com/users地址獲取數(shù)據(jù),如果前端沒有設(shè)置代理的話,請(qǐng)求會(huì)因?yàn)榭缬蛘?qǐng)求失敗。這時(shí)候通過如下配置,就可以正常請(qǐng)求了。
devServer: {
contentBase: __dirname + "/",
port: 9000,
proxy: {
"/users": {//需要代理的路徑target: "http://jsonplaceholder.typicode.com",//需要代理的域名changeOrigin:true//必須配置為true,才能正確代理}
}
}
通過如上配置,然后在js里面這樣請(qǐng)求就可以成功拿到數(shù)據(jù)了:
//使用fetch獲取ajax請(qǐng)求fetch('/users')//填寫路徑即可.then(function(response) {returnresponse.json()
}).then(function(json) {
console.log('parsed json', json)
}).catch(function(ex) {
console.log('parsing failed', ex)
})
這是通過webpack-dev-server配置的代理方案,那如果沒有webpack-dev-server服務(wù),要如何配置代理方案呢?比如在gulp和fis環(huán)境中,要怎么設(shè)置nodejs的代理服務(wù)。這時(shí)候我們就得來追尋下webpack-dev-server代理的實(shí)現(xiàn)機(jī)制了。
2.讓新版Chrome支持本地跨域請(qǐng)求調(diào)試
提示無法進(jìn)行跨域請(qǐng)求。這時(shí)候如果解決呢?其實(shí)可以通過Chrome的啟動(dòng)參數(shù),來解決這個(gè)問題,讓我們的本地聯(lián)調(diào)測(cè)試更加方便。具體方法是:
1、創(chuàng)建一個(gè)Chrome的啟動(dòng)快捷方式;
2、右鍵點(diǎn)擊快捷方式屬性,然后在目標(biāo)路徑后面,添加以下參數(shù):
–disable-web-security –user-data-dir=”e:\chromedev“
注意在最新版本的Chrome中,–user-data-dir參數(shù)也是必須要添加的,下劃線部分可以隨便指定到其他路徑,這里保存是的Chrome的用戶數(shù)據(jù)的。
3.nginx反向代理解決跨域設(shè)置
1、打開本地Host文件,?C:\Windows\System32\drivers\etc\ hosts文件
配置本地域名:
127.0.0.1 ? ? ? qyh.xxx.com ? //目的是瀏覽器訪問使用域名 ,如果不懂就安裝配置就可以
2、打開安裝nginx配置文件 進(jìn)行配置
server {
listen 80 default backlog=2048;
listen 443 ssl;
server_name qyh.citic.com;#這里的域名要跟HOST配置一致
#ssl on;
ssl_certificate d:/nginx-1.11.10/conf/server.crt;#HTTPS需要證書路徑
ssl_certificate_key d:/nginx-1.11.10/conf/server.key;#HTTPS需要證書路徑
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!AESGCM;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:100m;
ssl_session_timeout 100m;
location /{
root D:/xampp/htdocs;#默認(rèn)請(qǐng)求路徑
}
autoindex on;
index index.html index.htm index.shtml;
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
location /apis {
rewrite ^.+apis/?(.*)$ /$1 break;
include uwsgi_params;
proxy_pass https://qyhtest.citic.com/;#代理地址 --服務(wù)器接口域名
}
}
注意: server_name 要與HOST域名一致。
proxy_pass 是代理接口域名
listen監(jiān)聽443 及80端口處。
6. var, let, const
1. 塊級(jí)作用域?let, const
??????? ES5 中作用域有:全局作用域、函數(shù)作用域。沒有塊作用域的概念。
?????????ES6?中新增了塊級(jí)作用域。塊作用域由 { } 包括,if語句和 for語句里面的{ }也屬于塊作用域。
2. var、let、const的區(qū)別
var定義的變量,沒有塊的概念,可以跨塊訪問, 不能跨函數(shù)訪問??梢宰兞刻嵘?。
let定義的變量,只能在塊作用域里訪問,不能跨塊訪問,也不能跨函數(shù)訪問。不可以變量提升。
const用來定義常量,使用時(shí)必須初始化(即必須賦值),只能在塊作用域里訪問,而且不能修改。
7. json,字符集 編碼, dom和bom, 原型和原型鏈
json通用數(shù)據(jù)結(jié)構(gòu),用來數(shù)據(jù)交換,可以表示數(shù)組,對(duì)象,string,number,一般ajax,request請(qǐng)求對(duì)象化回來的就是json
unicode字符集utf-8編碼
dom是文檔對(duì)象模型 document.getElementById(),bom瀏覽器對(duì)象模型核心是window
????dom方法返回的和arguments這些類數(shù)組可以通過 array,prototype.slice.call或者array.form
使用構(gòu)造函數(shù)來新建一個(gè)對(duì)象的,每一個(gè)對(duì)象都有一個(gè)prototype屬性,屬性值是一個(gè)對(duì)象,當(dāng)訪問一個(gè)對(duì)象時(shí),如果這個(gè)對(duì)象內(nèi)部不存在這個(gè)屬性,那么它會(huì)去原型對(duì)象里找這個(gè)屬性,這個(gè)原型對(duì)象又會(huì)有自己的原型,于是這么一直找下去,就是原型鏈的概念。
在JavaScript 中,每個(gè)對(duì)象都有一個(gè)指向它的原型(prototype)對(duì)象的內(nèi)部鏈接。這個(gè)原型對(duì)象又有自己的原型,直到某個(gè)對(duì)象的原型為 null 為止(也就是不再有原型指向),組成這條鏈的最后一環(huán)。這種一級(jí)一級(jí)的鏈結(jié)構(gòu)就稱為原型鏈(prototype chain)。
8.異步編程
1.回調(diào)函數(shù)
使用回調(diào)函數(shù)是最常見的一種形式
//jQuery ajax$.get('test.php',res=>{//數(shù)據(jù)處理});//node異步讀取文件letfs=require('fs');? ? fs.read('file/path',(error,data)=>{//數(shù)據(jù)處理});
回調(diào)函數(shù)就是定義函數(shù)的時(shí)候,將另外一個(gè)函數(shù)(回調(diào)函數(shù))作為參數(shù)傳入,在異步操作執(zhí)行完成后就會(huì)執(zhí)行該回調(diào)函數(shù),從而可以保證在回調(diào)函數(shù)中的操作可以在異步執(zhí)行完成之后執(zhí)行?;卣{(diào)函數(shù)的缺點(diǎn)是當(dāng)需要執(zhí)行多個(gè)異步操作時(shí),需要將多個(gè)回調(diào)函數(shù)嵌套在一起,組成代碼結(jié)構(gòu)上的混亂,可讀性較差,被稱為“回調(diào)地獄”。
func1(data1,()=>{? ? func2(data2,()=>{? ? ? ? func3(data3,()=>{? ? ? ? ? ? //業(yè)務(wù)邏輯? ? ? ? });});});
2.Promise
Promise以一種鏈?zhǔn)秸{(diào)用的方法來組織異步代碼,可以將原來以回調(diào)函數(shù)形式調(diào)用的異步操作,改寫為promise鏈?zhǔn)秸{(diào)用
//jQuery ajax promise$('test.html').then(res=>{//業(yè)務(wù)邏輯}).then(res=>{//業(yè)務(wù)邏輯});
我們還可以利用ES6的Promise構(gòu)造函數(shù),自定義Promise
letresultA=newPromise((resolve,reject)=>{letflag=Math.random()<0.5;setTimeout(()=>{if(flag){resolve({result:true,msg:'ok'});? ? ? ? ? ? }else{reject({result:false,msg:'error'});? ? ? ? ? ? }? ? },0);? ? });? ? ? resultA.then(res=>{console.log(res.msg);returnPromise.resolve('I am from this first promise');? ? ? ? }).catch(err=>{console.log(err.msg);? ? ? ? }).then(res=>{console.log(res);? ? ? ? });
在最新版本的node(node 8.0+)環(huán)境里,還可以利用util.promisify方法將回調(diào)函數(shù)形式的函數(shù)直接轉(zhuǎn)換為Promise形式。
letutil =require('util');letfs =require('fs');letreadFilePromise = util.promisify(fs.readFile);readFilePromise('demo.js','utf-8').then((data)=>{console.log(data); });
3.Async/Await
node7.6以上的版本引入了一個(gè)es7的新特性 Async/Await,專門用來進(jìn)行異步控制,下面先看一簡單的個(gè)例子
letdelay =newPromise((resolve,reject)=>{setTimeout(()=>{console.log('test');resolve('msg from promise');? });});letbaz=async()=>{letres =awaitdelay;console.log(res);//'msg from promise'};baz();
首先我們需要使用async關(guān)鍵字定義一個(gè)包含異步代碼執(zhí)行的函數(shù),在promise形式你的異步執(zhí)行函數(shù)前面使用await關(guān)鍵字,就可以將異步寫成同步的操作形式。
相比Generators生成器,由于Async/Await使原生的異步控制方案,所以比較推薦使用的。
4.Generators
node的著名開發(fā)者TJ利用ES6的新特性生成器開發(fā)了一個(gè)異步控制控制工具co,Generators生成器,借助co可以將異步代碼的寫法寫成類似同步代碼的形式。
letutil =require('util');letfs =require('fs');letco=require('co');letreadFilePromise = util.promisify(fs.readFile);co(function*(){letbaz =yieldreadFilePromise('baz.js','utf-8');console.log(baz);letfoo =yieldreadFilePromise('foo.js','utf-8');console.log(foo);});
可以看出Generator的優(yōu)點(diǎn)顯而易見,它可以使異步代碼寫的非常清晰,可讀性很高,缺點(diǎn)使依賴第三方庫才能使用該特性。
9. Promise 解決了什么問題
回調(diào)地獄
10.JS中 并發(fā)(concurrency)和并行(parallelism)區(qū)別
1.并發(fā):宏觀概念,有兩個(gè)任務(wù)A和B,在一段時(shí)間內(nèi),通過在A和B兩個(gè)任務(wù)間切換,來完成兩個(gè)任務(wù),這種情況叫并發(fā)
2.并行:微觀概念,假設(shè)CPU有兩個(gè)核心,那么我們就可以同時(shí)完成A,B兩個(gè)任務(wù)。即:同時(shí)完成多個(gè)任務(wù)的情況可以稱為并行。
11. mvvm,mvc區(qū)別
model,view都有
Controller 層會(huì)接收用戶所有的操作,并根據(jù)寫好的代碼進(jìn)行相應(yīng)的操作——觸發(fā) Model 層,或者觸發(fā) View 層,抑或是兩者都觸發(fā)。
缺點(diǎn)就是 Controller 層和 View 層之間是一一對(duì)應(yīng)的,斷絕了 View 層復(fù)用的可能,因而產(chǎn)生了很多冗余代碼,代碼集中在Controller壓力很大,Zepto,juqery,angular
mvvm雙向綁定,react,vue
12.react生命周期

Constructor
1: 用于初始化操作,一般很少使用
2:唯一一個(gè)直接修改state的地方,其他地方通過調(diào)用this.setState()方法。
getDerivedStateFromProps
1:當(dāng)state需要從props初始化時(shí),使用
2:盡量不使用,維護(hù)倆者狀態(tài)需要消耗額外資源,增加復(fù)雜度
3:每次render都會(huì)調(diào)用
4:典型場(chǎng)景表單獲取默認(rèn)值
componentDidMount
1:UI渲染完成后調(diào)用
2:只執(zhí)行一次
3:典型場(chǎng)景:獲取外部資源
componentWillUnmount
1:組件被移除時(shí)調(diào)用
2:典型場(chǎng)景:資源釋放
getSnapshotBeforeUpdate
1:在render之前調(diào)用,state已更新
2:典型場(chǎng)景:獲取render之前的dom狀態(tài)
componentDidUpdate
1:每次UI更新被調(diào)用
2:典型場(chǎng)景:頁面通過props重新獲取數(shù)據(jù)
shouldComponentUpdate
1:覺得Vistual Dom是否重繪
2:一般可以由PuerComponent自動(dòng)實(shí)現(xiàn)
3:典型場(chǎng)景:性能優(yōu)化
13.immer 簡介
immer 的作者同時(shí)也是 mobx 的作者。mobx 又像是把 Vue 的一套東西融合進(jìn)了 React,已經(jīng)在社區(qū)取得了不錯(cuò)的反響。immer 則是他在 immutable 方面所做的另一個(gè)實(shí)踐。比lodash的深拷貝好體積只有五分之一。
與 immutable-js 最大的不同,immer 是使用原生數(shù)據(jù)結(jié)構(gòu)的 API 而不是像 immutable-js 那樣轉(zhuǎn)化為內(nèi)置對(duì)象之后使用內(nèi)置的 API,舉個(gè)簡單例子:
const produce= require('immer')
? const state= {
? ? done: false,
? ? val: 'string',
}
// 所有具有副作用的操作,都可以放入 produce 函數(shù)的第二個(gè)參數(shù)內(nèi)進(jìn)行
// 最終返回的結(jié)果并不影響原來的數(shù)據(jù)
? const newState= produce(state, (draft) => {
? ? draft.done= true
? })
? console.log(state.done)? ? // false
? console.log(newState.done) // true
通過上面的例子我們能發(fā)現(xiàn),所有具有副作用的邏輯都可以放進(jìn) produce 的第二個(gè)參數(shù)的函數(shù)內(nèi)部進(jìn)行處理。在這個(gè)函數(shù)內(nèi)部對(duì)原來的數(shù)據(jù)進(jìn)行任何操作,都不會(huì)對(duì)原對(duì)象產(chǎn)生任何影響。
這里我們可以在函數(shù)中進(jìn)行任何操作,例如 push splice 等非 immutable 的 API,最終結(jié)果與原來的數(shù)據(jù)互不影響。
Immer 最大的好處就在這里,我們的學(xué)習(xí)沒有太多成本,因?yàn)樗?API 很少,無非就是把我們之前的操作放置到 produce 函數(shù)的第二參數(shù)函數(shù)中去執(zhí)行。
immer 原理解析
Immer 源碼中,使用了一個(gè) ES6 的新特性 Proxy 對(duì)象。Proxy 對(duì)象允許攔截某些操作并實(shí)現(xiàn)自定義行為,但大多數(shù) JS 同學(xué)在日常業(yè)務(wù)中可能并不經(jīng)常使用這種元編程模式,所以這里簡單且快速的介紹一下它的使用。
Proxy
Proxy 對(duì)象接受兩個(gè)參數(shù),第一個(gè)參數(shù)是需要操作的對(duì)象,第二個(gè)參數(shù)是設(shè)置對(duì)應(yīng)攔截的屬性,這里的屬性同樣也支持 get,set 等等,也就是劫持了對(duì)應(yīng)元素的讀和寫,能夠在其中進(jìn)行一些操作,最終返回一個(gè) Proxy 對(duì)象實(shí)例。
constproxy=newProxy({},{get(target,key){// 這里的 target 就是 Proxy 的第一個(gè)參數(shù)對(duì)象console.log('proxy get key',key)},set(target,key,value){console.log('value',value)}})// 所有讀取操作都被轉(zhuǎn)發(fā)到了 get 方法內(nèi)部proxy.info// 'proxy get key info'// 所有設(shè)置操作都被轉(zhuǎn)發(fā)到了 set 方法內(nèi)部proxy.info=1// 'value 1'
上面這個(gè)例子中傳入的第一個(gè)參數(shù)是一個(gè)空對(duì)象,當(dāng)然我們可以用其他已有內(nèi)容的對(duì)象代替它,也就是函數(shù)參數(shù)中的 target。
immer 中的proxy
immer 的做法就是維護(hù)一份 state 在內(nèi)部,劫持所有操作,內(nèi)部來判斷是否有變化從而最終決定如何返回。下面這個(gè)例子就是一個(gè)構(gòu)造函數(shù),如果將它的實(shí)例傳入 Proxy 對(duì)象作為第一個(gè)參數(shù),就能夠后面的處理對(duì)象中使用其中的方法:
classStore{constructor(state){this.modified=falsethis.source=statethis.copy=null}get(key){if(!this.modified)returnthis.source[key]returnthis.copy[key]}set(key,value){if(!this.modified)this.modifing()returnthis.copy[key]=value}modifing(){if(this.modified)returnthis.modified=true// 這里使用原生的 API 實(shí)現(xiàn)一層 immutable,// 數(shù)組使用 slice 則會(huì)創(chuàng)建一個(gè)新數(shù)組。對(duì)象則使用解構(gòu)this.copy=Array.isArray(this.source)?this.source.slice():{...this.source}}}
上面這個(gè) Store 構(gòu)造函數(shù)相比源代碼省略了很多判斷的部分。實(shí)例上面有 modified,source,copy 三個(gè)屬性,有 get,set,modifing 三個(gè)方法。modified 作為內(nèi)置的 flag,判斷如何進(jìn)行設(shè)置和返回。
里面最關(guān)鍵的就應(yīng)該是 modifing 這個(gè)函數(shù),如果觸發(fā)了 setter 并且之前沒有改動(dòng)過的話,就會(huì)手動(dòng)將 modified 這個(gè) flag 設(shè)置為 true,并且手動(dòng)通過原生的 API 實(shí)現(xiàn)一層 immutable。
對(duì)于 Proxy 的第二個(gè)參數(shù),在簡版的實(shí)現(xiàn)中,我們只是簡單做一層轉(zhuǎn)發(fā),任何對(duì)元素的讀取和寫入都轉(zhuǎn)發(fā)到 store 實(shí)例內(nèi)部方法去處理。
constPROXY_FLAG='@@SYMBOL_PROXY_FLAG'consthandler={get(target,key){// 如果遇到了這個(gè) flag 我們直接返回我們操作的 targetif(key===PROXY_FLAG)returntargetreturntarget.get(key)},set(target,key,value){returntarget.set(key,value)},}
這里在 getter 里面加一個(gè) flag 的目的就在于將來從 proxy 對(duì)象中獲取 store 實(shí)例更加方便。
最終我們能夠完成這個(gè) produce 函數(shù),創(chuàng)建 store 實(shí)例后創(chuàng)建 proxy 實(shí)例。然后將創(chuàng)建的 proxy 實(shí)例傳入第二個(gè)函數(shù)中去。這樣無論在內(nèi)部做怎樣有副作用的事情,最終都會(huì)在 store 實(shí)例內(nèi)部將它解決。最終得到了修改之后的 proxy 對(duì)象,而 proxy 對(duì)象內(nèi)部已經(jīng)維護(hù)了兩份 state ,通過判斷 modified 的值來確定究竟返回哪一份。
functionproduce(state,producer){conststore=newStore(state)constproxy=newProxy(store,handler)// 執(zhí)行我們傳入的 producer 函數(shù),我們實(shí)際操作的都是 proxy 實(shí)例,所有有副作用的操作都會(huì)在 proxy 內(nèi)部進(jìn)行判斷,是否最終要對(duì) store 進(jìn)行改動(dòng)。producer(proxy)// 處理完成之后,通過 flag 拿到 store 實(shí)例constnewState=proxy[PROXY_FLAG]if(newState.modified)returnnewState.copyreturnnewState.source}
這樣,一個(gè)分割成 Store 構(gòu)造函數(shù),handler 處理對(duì)象和 produce 處理 state 這三個(gè)模塊的最簡版就完成了,將它們組合起來就是一個(gè)最最最 tiny 版的 immer ,里面去除了很多不必要的校驗(yàn)和冗余的變量。但真正的 immer 內(nèi)部也有其他的功能,例如上面提到的深層嵌套對(duì)象的結(jié)構(gòu)化共享等等。
當(dāng)然,Proxy 作為一個(gè)新的 API,并不是所有環(huán)境都支持,Proxy 也無法 polyfill,所以 immer 在不支持 Proxy 的環(huán)境中,使用 Object.defineProperty 來進(jìn)行一個(gè)兼容。
性能
我們用一個(gè)簡單測(cè)試來測(cè)試一下 immer 在實(shí)際中的性能。這個(gè)測(cè)試使用一個(gè)具有 100k 狀態(tài)的狀態(tài)樹,我們記錄了一下操作 10k 個(gè)數(shù)據(jù)的時(shí)間來進(jìn)行對(duì)比。
freeze 表示狀態(tài)樹在生成之后就被凍結(jié)不可繼續(xù)操作。對(duì)于普通 JS 對(duì)象,我們可以使用 Object.freeze 來凍結(jié)我們生成的狀態(tài)樹對(duì)象,當(dāng)然像 immer / immutable-js 內(nèi)部自己有凍結(jié)的方法和邏輯。
14.react相關(guān)
1.setState異步還是同步
通常異步,setTimeout是同步的
componentDidUpdate(nextProps, nextState) {
? if (nextState.activateLightColorForRed) {
? ? // when the state is updated (turned red),
// a timeout is triggered to switch it back off
? ? this.turnOffRedTimeout= setTimeout(() => {
? ? ? this.setState(() => ({activateLightColorForRed: false})
? ? }, 500);
}
}
componentWillUnmount() {
? // we set the timeout to this.turnOffRedTimeout so that we can
// clean it up when the component is unmounted.
// otherwise you could get your app trying to modify the state on an
// unmounted component, which will throw an error
? clearTimeout(this.turnOffRedTimeout);
}
2.react hook性能優(yōu)化使用memo、useCallback、useMemo,以及其它鉤子作用
????????https://www.cnblogs.com/seemoon/p/12792987.html
import React, { memo, useCallback, useMemo, useState } from 'react';
// 子組件
const ChildComp = (props) => {
? console.log('ChildComp...');
? return (<div>ChildComp...</div>);
};
const MemoChildComp = memo(ChildComp);
// 父組件
const Parent = () => {
? const [count, setCount] = useState(0);
? const [name] = useState('jack');
? const [age] = useState(11);
? const info = useMemo(() => ({ name, age }), [name, age]);
? const changeName = useCallback(() => {
? ? console.log('輸出名稱...');
? }, []);
? return (
? ? <div className="App">
? ? ? <div>hello world {count}</div>
? ? ? <div onClick={() => { setCount(count => count + 1); }}>點(diǎn)擊增加</div>
? ? ? <MemoChildComp info={info} changeName={changeName}/>
? ? </div>
? );
};
export default Parent;
useContext: 獲取 context 對(duì)象
useReducer: 類似于 Redux 思想的實(shí)現(xiàn)但其并不足以替代 Redux可以理解成一個(gè)組件內(nèi)部的 redux:
并不是持久化存儲(chǔ)會(huì)隨著組件被銷毀而銷毀
屬于組件內(nèi)部各個(gè)組件是相互隔離的單純用它并無法共享數(shù)據(jù)
配合useContext`的全局性可以完成一個(gè)輕量級(jí)的 Redux(easy-peasy)
useCallback: 緩存回調(diào)函數(shù)避免傳入的回調(diào)每次都是新的函數(shù)實(shí)例而導(dǎo)致依賴組件重新渲染具有性能優(yōu)化的效果
useMemo: 用于緩存?zhèn)魅氲?props避免依賴的組件每次都重新渲染
useRef: 獲取組件的真實(shí)節(jié)點(diǎn)
useLayoutEffect:DOM更新同步鉤子。用法與useEffect類似只是區(qū)別于執(zhí)行時(shí)間點(diǎn)的不同
useEffect:屬于異步執(zhí)行并不會(huì)等待 DOM 真正渲染后執(zhí)行而useLayoutEffect則會(huì)真正渲染后才觸發(fā)
可以獲取更新后的 state
3.diff算法?
把樹形結(jié)構(gòu)按照層級(jí)分解只比較同級(jí)元素。
時(shí)間復(fù)雜度O(n)
給列表結(jié)構(gòu)的每個(gè)單元添加唯一的key屬性方便比較。
React?只會(huì)匹配相同?class?的?component這里面的class指的是組件的名字
合并操作調(diào)用?component?的?setState?方法的時(shí)候,?React?將其標(biāo)記為 -?dirty.到每一個(gè)事件循環(huán)結(jié)束,?React?檢查所有標(biāo)記?dirty的?component重新繪制.
選擇性子樹渲染。開發(fā)人員可以重寫shouldComponentUpdate提高diff的性能
2. React Diff算法 => O(n) => 簡單粗暴,所有的節(jié)點(diǎn)按層級(jí)比較,只會(huì)遍歷一次
# 按葉子節(jié)點(diǎn)位置比較
[0,0]? ? ? ? ? ? ? :? pA => lA? ? ? #相同,不理會(huì)
[0.0,0.0]? ? ? ? :? pD => lB? ? ? #不同,刪除pD,添加lB
[0.1,0.1]? ? ? ? :? pB => lD? ? ? #不同,刪除pB,添加lD
[0.1.0,0.1.0]? :? pC => Null? #last樹沒有該節(jié)點(diǎn),直接刪除pC
[0.1.2,0.1.2]? :? Null => lC? ? #prev樹沒有該節(jié)點(diǎn),添加lC到該位置
4.react性能優(yōu)化方案
重寫shouldComponentUpdate來避免不必要的dom操作
使用?production?版本的react.js
使用key來幫助React識(shí)別列表中所有子組件的最小變化
5. React與this指向
class App extends React.Component{
? constructor(){
? ? this.name
? }
? handleClick(event){
? ? console.log(this); // 'this' 值為 undefined
? }
? render(){
? ? return (
? ? ? <button onClick={this.handleClick}>
? ? ? ? Click Me
? ? ? </button>
? ? );
? }
}
ReactDOM.render(
? <React.StrictMode>
? ? <App />
? </React.StrictMode>,
? document.getElementById('root')
);
模擬Render渲染函數(shù)生成的虛擬dom產(chǎn)物
renderDom() {
? ? var btn = document.createElement("button");
? ? var t = document.createTextNode("Click Me");
? ? btn.appendChild(t);
? ? function createCallback(fn) {
? constcontext=undefined;
return()=>fn.apply(context);//this沒有指向?qū)嵗脑?/p>
//由于react執(zhí)行回調(diào)的時(shí)候使用apply方法,指定的上下文是context對(duì)象而不是對(duì)象實(shí)例。
? ? }
? ? btn.addEventListener("click", createCallback(this.handleClick));
? ? document.getElementById("root").appendChild(btn);
? ? return
? }

解法1:構(gòu)造函數(shù)上bind
在構(gòu)造函數(shù)上將所有方法講this綁定到app實(shí)例上
constructor(props){super(props);// 綁定實(shí)例this.handleClick=this.handleClick.bind(this)}
解法2:render函數(shù)上bind
第二種思路和第一種類似只不過是在render函數(shù)上bind。
render(){return(<buttononClick={this.handleClick.bind(this)}>Click Me</button>);}
解法3: handle函數(shù)使用箭頭函數(shù)
另外一種思路就是利用箭頭函數(shù)和詞法作用域。
handleClick=(event)=>{// debuggerconsole.log("click",this);// 'this' 值為 undefined}
方法4: 綁定事件函數(shù)使用箭頭函數(shù)
最后一種和上面類似還是利用箭頭函數(shù)只是位置改為JSX中
render(){return(<buttononClick={()=>this.handleClick}>Click Me</button>);}
5.REACT 版本更新
react16.3
為舊的生命周期添加新的getDerivedStateFromProps()生命周期和UNSAFE_別名。(@bvaughn在#12028中)
添加新的getSnapshotBeforeUpdate()生命周期。(@bvaughn在#12404中)
react16.8.0(2019 年 2 月 6 日)
添加Hooks——一種無需編寫類即可使用狀態(tài)和其他 React 特性的方法。(@acdlite等人在#13968中)
改進(jìn)useReducerHook 延遲初始化 API。(@acdlite在#14723中)
6.react ??!0
如果不使用?!!b?進(jìn)行強(qiáng)轉(zhuǎn)數(shù)據(jù)類型會(huì)在頁面里面輸出?0。
render(){constb=0;return<div>{!!b&&<div>這是一段文本</div>}</div>}
15.圖片懶加載
https://blog.csdn.net/w1418899532/article/details/90515969
16.HTTP狀態(tài)碼共分為5種類型:
1**信息,服務(wù)器收到請(qǐng)求,需要請(qǐng)求者繼續(xù)執(zhí)行操作
2**成功,操作被成功接收并處理
3**重定向,需要進(jìn)一步的操作以完成請(qǐng)求
4**客戶端錯(cuò)誤,請(qǐng)求包含語法錯(cuò)誤或無法完成請(qǐng)求
5**服務(wù)器錯(cuò)誤,服務(wù)器在處理請(qǐng)求的過程中發(fā)生了錯(cuò)誤
.最常見的狀態(tài)碼:
100Continue繼續(xù)。客戶端應(yīng)繼續(xù)其請(qǐng)求
200 - 請(qǐng)求成功
203Non-Authoritative Information非授權(quán)信息。請(qǐng)求成功。但返回的meta信息不在原始的服務(wù)器,而是一個(gè)副本
301 - 資源(網(wǎng)頁等)被永久轉(zhuǎn)移到其它URL
302Found臨時(shí)移動(dòng)。與301類似。但資源只是臨時(shí)被移動(dòng)??蛻舳藨?yīng)繼續(xù)使用原有URL
400Bad Request客戶端請(qǐng)求的語法錯(cuò)誤,服務(wù)器無法理解
401Unauthorized請(qǐng)求要求用戶的身份認(rèn)證
404 - 請(qǐng)求的資源(網(wǎng)頁等)不存在
405Method Not Allowed客戶端請(qǐng)求中的方法被禁止 postget寫錯(cuò)了可能是
500 - 內(nèi)部服務(wù)器錯(cuò)誤
502Bad Gateway作為網(wǎng)關(guān)或者代理工作的服務(wù)器嘗試執(zhí)行請(qǐng)求時(shí),從遠(yuǎn)程服務(wù)器接收到了一個(gè)無效的響應(yīng)