沐鳴註冊_簡單的useState實現

以下是一段非常簡單的 react 代碼:

const App = () => {
  const [n, setN] = useState(0);
  return (
    <div>
      {n}
      <button onClick={() => setN(x => x + 1)}>+1</button>
    </div>
  );
}

react.render(<App />, rootElement)

這樣的用法和以往的 setState 是有明顯的不同的,他看起來更像 redux——我們初始化一個 state,然後 dispatch 一個 action,再由 reducer 改變 state 后返回新的 state。

Redux 思想實現 useState

既然我們覺得它像,那我們就來自己實現一個吧。

不熟悉 Redux 思想的同學請自行閱讀文檔

const useState = (initialValue) => {
  let state = initialValue;
  const dispatch = (newState) => {
    state = newState;
    render(<App />, document.getElementById('root'));
  };
  return [state, dispatch];
};

然後我們用這個自定義的 useState 代替 React 的 useState——就會發現我們失敗了,setN 無論如何都不會有任何反應。

這是因為我們每次重新 render 的時候都重新執行了函數,於是我們總是會重新賦值

為什麼不會重新賦值?

對於這段 React 代碼來說,當我們第一次運行時,React 會進行首次渲染,即 render(<App />, …)。

在此過程中,會先調用 App(),之後便會得到虛擬 DOM,再創建真實的 Div。

當我們觸發點擊事件時,會調用 setN,再次 render()。之後調用 App(),然後得到新的虛擬 DOM,進行 diff 算法,根據 diff 算法的結果去更新新的 Div。

而不管是第一次渲染,還是第二次調用,都會調用 useState()。

但是我們寫的是 useState(0) 啊,兩次調用明明是一樣的代碼,為何 n 的值不同?怎麼解決這個問題呢?

很簡單,閉包嘛。

const createUseState = () => {
  let state;
  const useState = (initialValue) => {
    if (!state) {
      state = initialValue;
    }
    const dispatch = (newState) => {
      state = newState;
      render(<App />, document.getElementById('root'));
    };
    return [state, dispatch];
  };
};

這樣就解決了重新賦值的問題。

多次調用

但是我們需要多次調用 useState 呀,不可能只用一次的。

於是我們將 state 改為一個數組:const state = [];。

const createUseState = () => {
  const state = [];
  let index = 0;
  return (initialValue) => {
    state[index] = state[index] || initialValue;
    const currentIndex = index;
    const dispatch = (newState) => {
      state[currentIndex] = newState;
      // 重點
      index = 0;
      ReactDOM.render(
        <React.StrictMode>
          <App />
        </React.StrictMode>,
        rootElement
      );
    };
    return [state[index++], dispatch];
  };
};

我們創建了一個 index 變量來控制索引。它需要我們保證每次重新渲染 App 傳入數組的元素是一樣的——這就是為什麼我們不可以將 useState 寫在 if 判斷中。

在上述代碼中有一處重點,在於我們需要在每次 set 之後將索引歸零 index = 0。

因為每次 render 結束后,React 都會重新執行該函數。

作者:幾乎一米八的徐某某

出處:Aero Blog (https://www.cnblogs.com/xhyccc/)


站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9761

沐鳴娛樂_使用 Typescript 開發 Nodejs 命令行工具

本文記錄了搭建基於 TypeScript 的 Nodejs 命令行開發環境的全過程。

為何使用TypeScript

首先,對於編寫類庫或者工具而言,使用 TypeScript 的最大好處就是其提供了類型機制,可以避免我們犯一些低級錯誤。

其次,配合編輯器(如 VS Code),TypeScript 能提供強大的代碼提示功能,我們不需要記憶很多API的具體使用,在編寫代碼時編輯器會自動進行提示。比如引入了 http 之後,輸入 http. 就會提示可以使用的各個方法和屬性,並給出詳細的說明。

同是微軟旗下,VS Code 具有非常強大便利的功能,強烈推薦使用 VS Code 進行 TypeScript 和 Nodejs 開發。

最後,使用 TypeScript 是大勢所趨,很多大公司都在推 TypeScript,使用 TypeScript 開發,可以讓我們對 TS 使用更加熟練。

初始化工程

建立命令行工具,需要先創建一個 npm 包。下文將使用 npm 工具來完成包的初始化和依賴的安裝。

首先創建一個文件夾,然後運行初始化命令:

mkdir ts-node-demo && cd ts-node-demo
npm init

控制台會出現一系列提示, 按照需求輸入即可,然後一路回車,完成之後輸入 yes 。

package name: (typescript-cli) 
version: (1.0.0) 
description: a cli in typescript
entry point: (index.js) 
test command: 
git repository: 
keywords: CLI,TypeScript
author: YourName
license: (ISC) MIT

初始化之後本地文件夾會出現一個 package.json 文件。我們的 npm 包就已經初始化完成了。為了避免誤發布,我們在 package.json 中做一個更改:

- private: false,
+ private: true,

初始化 Git

在當前目錄下運行:

git init

然後在當前目錄創建 .gitignore 文件,指定忽略 node_modules 文件夾:

node_modules/
lib/

引入 Node 類型

既然是開發 Nodejs 程序,為了獲得合適的類型校驗和代碼提示,我們需要引入 Nodejs 的類型文件:

npm i -D @types/node

引入 typescript

npm i typescript

然後需要初始化 tsconfig 文件。

./node_modules/.bin/tsc --init

上述命令會在當前文件夾下面創建一個 tsconfig 文件,用來指導 TypeScript 進行編譯。 在裏面有非常多的配置項,並且有非常詳細的解釋,我們做兩個更改來適配我們的項目:

+ "sourceMap": true,
+ "outDir": "lib",

上述配置指定生成 sourceMap 文件,並將 TypeScript 的編譯結果輸出到 ./lib 文件夾.

然後在與 compilerOptions 平級的地方增加選項:

"compilerOptions": {
    ...
},
+ "include": [
+    "src/**/*"
+ ]

這表示我們只會編譯 src 目錄下的 .ts 文件。

編寫代碼

在當前目錄下創建 src 文件夾,並創建 index.ts:

mkdir src && echo "console.log('Your cli is running.');" > src/index.ts

然後運行:

./node_modules/.bin/tsc 

可以發現在文件夾下出現了 lib/ 目錄,裏面就是 index.ts 編譯之後的 js 文件。

創建運行腳本

每次編譯都需要引用 node_modules 裏面的 tsc 命令,有些繁瑣,有三種方法可以解決:

全局安裝 typescript 包:

npm i typescript -g

就可以直接使用 tsc 命令了。

使用 npx 執行 npx 是 npm 提供的命令,其會自動下載對應的包並執行.

npx tsc

創建 npm 腳本 在 package.json 中的 script 中增加一行腳本:

"script": {
+    "build": "tsc"
}

這裏我們採用第3種方法,寫入腳本后可以執行:

npm run build

也會成功進行編譯。

註冊命令

開發 Nodejs 命令行工具,就是提供一個可以直接調用的命令,而不是使用下面這種方式執行文件:

node lib/index.js

我們想要的效果是執行一個命令就能調用我們的 js 文件。

首先在當前文件夾創建文件 bin/node-cli-demo :

mkdir bin && touch bin/node-cli-demo.js

然後在文件中寫入以下內容:

#!/usr/bin/env node
require('../lib/index.js');

npm 包註冊的命令需要在 package.json 中進行聲明,增加如下內容:

{
    "name": "typescript-cli",
    "version": "0.0.1",
+   "bin": {
+       "node-cli-demo": "./bin/node-cli-demo.js"
+   },
}

這表示當執行 node-cli-demo 這個命令時就去執行我們的 ./bin/node-cli-demo.js 文件。

最後在當前目錄調用 npm link ,這條命令會把我們本地註冊的命令放到 Nodejs 安裝目錄的 bin 文件夾下。在安裝 Nodejs 時系統將該文件夾添加到命令查找的路徑中。所以我們就可以直接使用我們剛剛註冊的命令:

node-cli-demo
// Your cli is running.

自動監聽文件變動

我們希望每次更改了 .ts 文件之後,不必手動執行 npm run build 就能看到最新的效果,可以使用 typescript 的 –watch 選項,在 package.json 中的 script 中增加 start 命令:

{
    "script": {
+       "start": "tsc --watch"
    }
}

在當前目錄下運行命令:

npm start

然後對 src/index.ts 文件做一些更改,另開一個控制台窗口,運行 node-cli-demo,會發現打印的內容已經更新了。 這樣我們在開發時就只需要關注代碼編寫,而不用考慮編譯的問題了。

接下來我們就可以在 src 文件裏面寫我們的具體代碼了!

注: 本文的 demo 代碼可以在 github 上查看。為了避免創建很多倉庫,我將其放到了一個倉庫的子目錄裏面。

總結

使用 TypeScript 開發 Nodejs 命令行的流程如下:

安裝 typescript 並進行配置;

在 package.json 中聲明命令並使用 npm link 將其鏈接到全局命令中;

使用 tsc –watch 自動監聽文件變動並重新編譯;

運行註冊過的命令,查看效果。

配置ESLint

使用 ESLint 校驗我們的代碼,可以避免一些低級錯誤。而 TypeScript 現在推薦採用 ESLint 來檢查代碼。我們可以為我們的工程配置 ESLint。

安裝依賴

首先安裝依賴:

npm i -D eslint @typescript-eslint/parser  @typescript-eslint/eslint-plugin

@typescript-eslint/parser 是用來解析 TypeScript 代碼的,類似於 @babel/parser;

@typescript-eslint/eslint-plugin 是 ESLint 插件,用來配置具體的檢查規則。

設置配置

在根目錄下創建 .eslintrc, 寫入以下內容:

{
    "root": true,
    "parser": "@typescript-eslint/parser",
    "plugins": [
        "@typescript-eslint"
    ],
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/eslint-recommended",
        "plugin:@typescript-eslint/recommended"
    ],
    "rules": {
        "no-console": "error"
    },
    // set eslint env
    "env": {
        "node": true
    }
}

root: true 表示當前目錄就是尋找 ESLint 的最終配置,這樣 ESLint 就不會再往上層尋找配置;

parse 指定了使用 @typescript-eslint/parser 來解析我們的 TypeScript 代碼;

plugins 指定使用的 ESLint 插件;

extends 指定了額外使用的規則配置。插件指定了一系列自定義的規則配置,只有在 extends 中指定才會生效。

rules 中可以擴展我們自己的規則。

env 中可以指定我們代碼運行的環境,這樣就可以自動判斷某些代碼是不是有錯誤。比如上述配置了 node: true ,我們在使用 require 的時候就不會報錯了。

運行校驗命令

在 package.json 的 script 中寫入:

{
    "script": {
        "lint": "eslint ./src --ext .ts"
    }
}

上述命令指定了對 ./src 目錄下擴展名為 .ts 的文件進行校驗。

然後運行 npm run lint,會發現控制台出現報錯,我們的 ESLint 已經生效了。

配置 VSCode 的 ESLint 插件,編輯器在開發時就會自動提示有問題的代碼,幫助我們編寫符合規範的代碼。

忽略某些文件

我們可以指定某些目錄下的文件不進行校驗,在當前目錄下創建 .eslintignore ,類似 .gitignore,然後在裏面寫入需要忽略的目錄或文件:

node_modules

至此,ESLint 也配置完成了!

以上就是搭建 Nodejs 命令行的 TypeScript 開發環境的全部內容了,希望能幫到大家~

來自:https://zhuqingguang.github.io/2020/11/07/nodejs-cli-with-typescript/

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9831

沐鳴總代理_一個好的web前端開發者,是怎麼學習的?

IT 行業的變化快是眾人皆知的,需要持續去學習新的知識內容。但是,往往我們工作之後,經常發現學習的東西很少了,學習效率非常低,感覺自己到了一個瓶頸期,久而久之,就演變成『一年工作經驗,重複去用十年』的怪圈。

不管你是已經工作了,還是正在學習中的初學者,如果你想在web前端行業中有更高更遠的發展,請用五分鐘閱讀下面的文章。

一、低效率的學習壞習慣

1.被動反覆閱讀

通常一個新手在學習web前端開發的時候,往往會選擇買書去學習,首先這樣的學習效率是非常差的,比如在學習html,css的時候,是完全不用看書的。書上大多數都是理論知識,你並不知道哪裡是重點,所以每個人地方你都會去看,但是事實往往是大多數東西你都不理解。這種學習方式是非常不可取的,你沒有那麼多時間去這麼干。

2 、看書看視頻中代碼示例認為自己就理解了,從不動手編程

這是新手在學習web前端的時候常見的問題,也是大忌,很多人都說,我課程聽懂了,但是自己不會動手寫,這首先就是學習方法的錯誤,這是新手學編程的大忌,不去動手寫,不去跟編譯器和開發環境做鬥爭,你永遠不知道軟件開發過程中的糟糕事情。

 3 、拖延

既然你選擇了學習web前端,就應該知道,我們這行需要不斷去學習新的東西,拖延會讓你成為一個真正的“碼農”。這也是影響N多人不去學習的理由。解決的辦法只有一個,馬上去做!一旦你開始去做了,你就會發現一切沒有那麼難。你的計劃再完美,你選的書籍再經典,你挑的視頻水平再高,如果你不馬上去看,去學,去動手實踐,那永遠也只是停留在空想的階段。成功學習的典範就是成功戰勝拖延症的典範。

4.喜歡自己閉門造車

學習專業知識,不是都靠自己頑強的意志,更多的是需要跟別人交流,重要的就是跟比你強的人交流,加一些氛圍比較好的交流學習群,或者別人的一句話就能讓你茅塞頓開,學技術切記不能閉門造車,學習的大忌。

5.遇到問題搞不清楚,只能百度,然後自己一團糟

遇到問題的時候,不假思索「百度」,但是很多時候我們是浪費了大量的時間,也搞不清楚自己的問題在哪裡。當然了,我這裏特指“初學者”而不是已經工作中的人。那些已經在做web前端工作的人當然很多問題都要自己解決,但是對於一個web前端新手來說,能找老師盡量找老師,很多問題我們新手不必浪費太多自己的學習時間,因為那樣的效率太慢了。

 二:一些學習的好習慣

1 、與其反覆閱讀,不如經常回顧

大家記住,對於web前端技術性的書籍,絕對不是讓你一頁一頁去看的,像是完成做一樣。有一句話說:溫故而知新,可以為師矣。學過的知識點,你時常去複習一下,你每天都見到你自然就記住了,而不會像很多人說“學了就忘”你不總去回顧,那能不忘記嗎?不如有意識地總結回顧看過的書,學過的知識。只需要每晚趟在床上的時候,回想一下今天都學到了什麼?今天自己有進步一點點嗎?

2 、多做練習,多寫代碼,從錯誤中學習

 一個優秀的web前端開發者,沒有什麼聰明人,他們都是一行一行代碼積累出來的,對於一個初學者來說,如果你想要找到一份不錯的工作,你只能是大量的練習,形成一個好的學習習慣。在初學階段哪怕對着書本敲也沒有什麼問題。認真完成書中留的習題,在自己沒有盡最大努力的情況下面不要去看答案。不要怕犯錯,每一次犯錯都是自己進步的機會。

3 、多總結問題的解決方案,多寫可復用的代碼,拒絕複製粘貼

每天把學習中遇到的問題最後的解決方案總結一下,想想為什麼出現了這個錯誤,加深自己的印象,是什麼導致了這個錯誤,犯過一次的錯誤就盡量不要犯第二次,導致錯誤的根本原因是什麼。是自己的邏輯混亂,粗心大意,還是程序太複雜?

4 、對於自己想要學習的內容,制訂一下計劃,有節奏地學習

一個學習習慣好的人,做什麼事情都會有一個明確的計劃,對於一個web前端初學者來說,一份好的學習計劃是你開始的前提,因為學習一個東西最怕三天打漁,如果能夠持續地學習一個東西,我可以把它學習地很好。這時候,你就應該結合我自身的情況,選擇一段最佳的學習時間,在這段學習時間里我可以不被打擾,保持高度專註。比如每天早上6.30起床看一個小時書。

5、注意勞逸結合

對於電腦工作者來說,多參加體育鍛煉,多去戶外走走,運動能夠增強人的記憶力,並且有時候還能產生靈感。如果身體不好,你的學習效率會非常低,人的精神狀態一旦非常好的時候,做什麼都會如魚得水。想要成為大神,身體好是前提條件。有人30歲成為大神,我資質不好,我35歲成為大神總可以吧。切莫在30歲的時候就把身體弄跨了,然後35歲轉行了,永遠失去了成長為大神的機會。

6.向別人解釋你的知識,多與人討論

一個好的web前端開發者,一定是一個很會思考的,有能力就多寫博客,多分享自己的所學所思,只要你能寫出東西,不怕你寫的太低級,這些對於學習者自身也是非常有益的。通過用別人能夠理解的語言來解釋你學到的東西,本身就要求你對該知識充分理解。另外,很多人經常感嘆「跟你討論一下,我馬上變得有思路了」,這其實就是交流的作用。

前端學習方法很重要,選擇適合自己的學習方法,學好前端最重要的培養持續的興趣,其次就是不斷實踐,從實踐中逐漸練習前端代碼等,從而對前端有很好的理解與吸收。如大家對前端還有不了解的問題,可以持續關注我,每天會發一些關於前端相關知識,供大家學習與參考!

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9894

沐鳴代理_如何開發一款前端工具

基本上,使用任何成熟的語言都可以開發 cli 工具,作為一個前端小白,還是 JavaScript 比較順手,因此我們選用nodejs作為開發語言,開發一款node-cli工具。

類似於腳手架工具,Node工具會自動去詢問你一些預設的問題,然後將你回答的結果結合一些模板文件,給你生成一個項目結構。

那接下來我們以一個小型的腳手架工具為例,通過Nodejs完成一個Node工具,再來去深入體會一下Node工具的工作過程。

那我們知道腳手架工具實際上就是一個node-cli應用,那創建腳手架就是創建一個node-cli應用,那這裏我們具體來操作一下,我們首先進入到命令行,通過mkdir去創建一個工具目錄

mkdir samlpe-cli
cd sample-cli

在這個目錄下面我們通過yarn init 方式去初始化一個package.json文件

yarn init

有了這個文件之後通過編輯器打開這個目錄,緊接着我們需要在package.json中添加一個bin字段,用於去指定一下我們cli應用的入口文件, 我們這裏叫cli.js

{
  "name": "sample-cli",
  "bin": "cli.js",
  ...
}

再然後我們添加這個cli.js文件,跟以往我們在Node中書寫的文件有所不同,cli的入口文件必須要有一個特定的文件頭, 也就是在這個文件頂部寫上這樣一句話 #! /usr/bin/env node 我們在這個文件中console.log一句話。

#! /usr/bin/env node

console.log('cli working')

如果說你的操作系統是linux或者mac系統你還還需要去修改這個文件的讀寫權限,把他修改成755,這樣才可以作為一個cli的入口文件。

我們回到命令行,我們通過yarn link 將這個模塊映射到全局

yarn link

這時候我們就可以在命令行使用sample這樣一個命令, 通過執行這個命令我們的console.log成功打印出來,表示代碼執行了。也就意味着我們這個cli基礎就已經ok了。

sample-cli

接下來我們實現一下腳手架的具體業務,也就是我們腳手架的工作過程。

首先我們需要通過命令行交互的的方式去詢問用戶的一些信息,然後緊接着呢根據用戶反饋回來的結果我們去生成文件,

  1. 通過命令行交互的方式詢問用戶信息

  2. 根據用戶反饋回來的結果生成文件

在Node當中去發起命令行交互詢問我們使用inquirer這樣一個模塊,那我們需要通過npm安裝一下這個模塊,我這裏使用yarn,安裝在依賴文件當中。

yarn add inquirer --dev

那有了這個模塊過後就可以在代碼中去載入, inquirer這個模塊提供一個叫做prompt的方法用於發起一個命令行的詢問。

他可以接收一個數組參數,數組中每一個成員就是一個問題,可以通過type指定問題輸入方式,然後name指定返回值的鍵,message去指定屏幕上給用戶的一個提示,在promise的then裏面拿到這個問題接收到用戶的答案。

我們這裏不着急往下寫,我們先通過console.log去打印一下。

const inquirer = require('inquirer');

inquirer.prompt([
    {
        type: 'input',
        name: 'name',
        message: 'Project name'
    }
]).then(answer => {
    console.log(answer);
})

回到控制台,我們命令行執行sample-cli, 此時就會提示我們需要輸入項目的名稱。

sample-cli

這樣就可以看到問題和返回的結果。這也就證明inquirer確實可以幫我們發起命令行交互詢問。

那有了inquirer之後接下來我們要考慮的就是動態的去生成我們的項目文件。

我們一般會根據模板去生成,所以我們在項目的跟目錄下新建一個templates目錄,在這個目錄下我們去新建一些模板。

由於我們這裡是討論腳手架的工作過程,所以我們也不去關心模板裏面有什麼,我們就隨便寫點什麼。我們可以通過 <%%>去替換詢問過程中得到的答案。

index.html

<head>
    <title><%= name %></title>
</head>

我們還可以添加一些其他的模板文件,比如style.css

style.css

body {
    margin: 0;
    background-color: red;
}

回到cli.js文件, 這時候我們可以在得到問題答案的位置,根據用戶回答的問題去生成文件。不過在生成前我們一般會先將模板路徑和目標目錄確定下來。

模板的目錄應該是項目當前目錄的templates,我們可以通過path獲取。

const path = require('path');

// 工具當前目錄
const tmplDir = path.join(__dirname, 'templates');

輸出的目標目錄一般是我們命令行在哪個目錄去執行就應該是哪個路徑,也就是cwd目錄

const path = require('path');

// 工具當前目錄
const tmplDir = path.join(__dirname, 'templates');
// 命令行所在目錄
const destDir = process.cwd();

明確這兩個目錄,我們就可以通過fs模塊去讀取一下模板目錄下一共有哪些文件。把這些文件全部輸入到我們的目標目錄,我們通過fs的readDir方法,這個方法會自動掃描目錄下的所有文件

fs.readdir(tmplDir, (err, files) => {
    if (err) {
        throw err;
    }
    files.forEach(file => {
        console.log(file); // 得到每個文件的相對路徑
    })
})

我們可以通過模板引擎去渲染路徑對應的文件,先去安裝一款模板引擎,這裏我們使用ejs

yarn add ejs --dev

安裝過後,回到代碼中引入這個模板引擎, 通過模板引擎提供的renderFile去渲染這個路徑對應的文件。

第一個參數是文件的絕對路徑,第二個參數是模板引擎在工作的時候的數據上下文,第三個參數是回調函數,也就是我們在渲染成功過後的回調函數,當然如果你在渲染過程中出現了意外那你可以通過throw err的方式把這個錯誤拋出去。

我們可以先把result通過打印的方式打印出來看一下。

const fs = require('fs');
const path = require('path');
const inquirer = require('inquirer');
const ejs = require('ejs');

// 工具當前目錄
const tmplDir = path.join(__dirname, 'templates');
// 命令行所在目錄
const destDir = process.cwd();

inquirer.prompt([
    {
        type: 'input',
        name: 'name',
        message: 'Project name'
    }
]).then(answer => {
    fs.readdir(tmplDir, (err, files) => {
        if (err) {
            throw err;
        }
        files.forEach(file => {
            ejs.renderFile(path.join(tmplDir, file), answer, (err, result) => {
                if (err) {
                    throw err;
                }
                console.log(result);
            })
        })
    })
})

編輯完成之後我們運行一下腳手架工具。

sample-cli

此時打印出來的這個結果其實是已經經過模板引擎工作過後的結果,我們只需要將這個結果通過文件寫入的方式寫入到目標目錄就可以了,那目標目錄應該是通過path.join把我們destDir以及我們的file做一個拼接。內容就是我們這裏的result。

files.forEach(file => {
    ejs.renderFile(path.join(tmplDir, file), answer, (err, result) => {
        if (err) {
            throw err;
        }
        fs.writeFileSync(path.join(destDir, file), result);
    })
})

完成過後我們找到一個新的目錄,使用一下這個腳手架

sample-cli

我們輸入項目名稱過後,就會發現他會自動把我們模板裏面的文件自動生成到對應的目錄裏面,至此我們就已經完成了一個非常簡單,非常小型的一個腳手架應用。

那我們也回顧了一下腳手架的工作過程,其實腳手架的工作原理並不複雜,但是他的意義卻是很大的,因為他確實在創建項目環節大大提高了我們的效率。

我們可以將自己的工具發布至npm上,提供給更多的人使用。

至於發布npm也非常的簡單,首先我們需要註冊npm賬號,有兩種方式可以註冊,一種是登錄npm官網https://www.npmjs.com/, 另一種是使用命令npm adduser。

npm adduser

會提示你輸入用戶名,密碼,以及郵箱。

註冊好后登錄npm賬號。

npm login

依次輸入第二步中第一種方法註冊的用戶名、密碼和郵箱。

登錄成功后執行npm發布命令。

npm publish

注意:如果報錯:’You do not have permission to publish “samlpe-cli”. Are you logged in as the correct user?’

表示包samlpe-cli名字已經在包管理器已經存在被別人用了,需要更該包名稱,我們可以前往package.json中的name中換一個名字。

{
  "name": "sample-cli1",
  "version": "1.0.0",
  "bin": "cli.js",
  ...
}

再次執行publish命令。出現 +sample-cli1@1.0.0即表示發布成功。

如果發布時報錯:no_perms Private mode enable, only admin can publish this module:

表示當前不是原始鏡像,要切換回原始的npm鏡像

npm config set registry https://registry.npmjs.org/

至此你的node工具就可以提供給其他人使用了。

如果需要更新你的工具,只要繼續執行npm publish就可以更新發布了,不過需要注意,每次發布都需要修改版本號version的值,同一個版本不允許發布兩次。

{
  "name": "sample-cli1",
  "version": "1.0.1",
  "bin": "cli.js",
  ...
}

如果想要撤銷本次發布可以執行

npm unpublish

不過需要注意,只有在發包的24小時內才允許撤銷發布的包,超過24小時,就無法撤回了。

作者:隱冬

鏈接:https://juejin.cn/post/6899446879487180808

來源:掘金

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9893

沐鳴登陸地址_Fetch還是Axios,哪個更適合HTTP請求?

前端開發最重要的部分之一是通過發出HTTP請求與後端進行通信,我們有幾種方法可以異步地在JavaScript中進行API調用。

幾年前,大多數應用程序都使用Ajax發送HTTP請求,Ajax代表異步JavaScript和XML。但是現在,開發人員通常會決定在 .fetch() API和Axios之間進行選擇。

在本文中,我想比較這兩種方法,並簡要介紹一下基本知識和語法。除此之外,我還將比較在兩種情況下以及在錯誤處理中將數據轉換為jsON格式的過程。我還將討論HTTP攔截和下載進度。

開始吧!

Fetch概述和語法

在構建Javascript項目時,我們可以使用window對象,並且它帶有許多可以在項目中使用的出色方法。這些功能之一是Fetch API,它提供了一種簡單的全局 .fetch() 方法,這是一種從API異步獲取數據的邏輯解決方案。

讓我們看一下 .fetch() 方法的語法。

fetch(url)
  .then((res) => 
    // handle response
  )
  .catch((error) => {
    // handle error
  })

在上面的示例中,您可以看到簡單的獲取GET請求的語法。在 .fetch() 方法中,我們有一個強制性參數url,它返回一個Promise,可以使用Response對象來解決。

.fetch() 方法的第二個參數是選項,它是可選的。如果我們不傳遞 options,請求總是GET,它從給定的URL下載內容。

在選項參數裏面,我們可以傳遞方法或頭信息,所以如果我們想使用POST方法或其他方法,我們必須使用這個可選的數組。

正如我之前提到的,Promise會返回Response對象,正因為如此,我們需要使用另一個方法來獲取響應的主體。有幾種不同的方法可以使用,取決於我們需要的格式:

response.json()
response.text()
response.formData()
response.blob()
response.arrayBuffer()

讓我們看一下帶有可選參數的代碼示例。

fetch(url, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify(data)
});
  .then((response) => response.json())
  .catch((error) => console.log(error))

在上面的代碼示例中,你可以看到簡單的POST請求,包括 method、header 和 body params。然後我使用 json() 方法將響應轉換為JSON格式。

現在,讓我們仔細看看axios

Axios概述和語法

Axios是一個Javascript庫,用於從Node.js或XMLHttpRequests或瀏覽器發出HTTP請求。作為一個現代的庫,它是基於Promise API的。

axios 有一些優勢,比如對XSRF的保護或取消請求。

為了能夠使用 axios 庫,我們必須將其安裝並導入到我們的項目中。可以使用CDN,npm或bower安裝 axios。現在,讓我們來看一個簡單的GET方法的語法。

axios.get(url)
  .then(response => console.log(response));
  .catch((error) => console.log(error));

在上面的代碼中,你可以看到我使用 .get() 方法創建一個簡單的GET請求。如果你想在函數中使用POST方法,那麼只需使用 .post() 方法代替,並將請求數據作為參數傳遞即可。

當我們創建配置對象時,我們可以定義一堆屬性,最常見的是:

baseUrl
params
headers
auth
responseType

作為響應,axios 返回一個promise,該promise將與響應對象或錯誤對象一起解析。在響應對象中,具有以下值:

data,這是實際的響應主體


status,調用的HTTP狀態,例如200或404


statusText,以文本消息形式返回的HTTP狀態,例如 ok


headers,服務器發回標頭


config,請求配置


request,XMLHttpRequest對象

現在,讓我們看一下帶有數據的POST方法的代碼示例。

axios.post({
  '/url', 
  { name: 'John', age: 22},
  { options }
})

在上面的代碼中,你可以看到 post 方法,我們把config對象作為param,其中有URL、數據和附加選項。

我們還可以將config對象定義為變量,然後像下面的示例一樣將其傳遞給 axios。

const config = {
  url: 'http://api.com',
  method: 'POST',
  header: {
    'Content-Type': 'application/json'
  },
  data: {
    name: 'John',
    age: 22
  }
}
axios(config);

在這裏,你可以看到所有的參數,包括URL、數據或方法,都在config對象中,所以在一個地方定義所有的東西可能更容易。

JSON

如前所述,當我們在使用 .fetch() 方法的時候,需要對響應數據使用某種方法,當我們在發送帶有請求的body時,需要對數據進行字符串化。

在 axios 中,它是自動完成的,所以我們只需在請求中傳遞數據或從響應中獲取數據。它是自動字符串化的,所以不需要其他操作。

讓我們看看如何從 fetch() 和 axios 獲取數據。

// fetch
fetch('url')
  .then((response) => response.json())
  .then((data) => console.log(data))
  .catch((error) => console.log(error))
// axios
axios.get('url')
  .then((response) => console.log(response))
  .catch((error) => console.log(error))

在上面的例子中,你可以看到,使用 axios 我們沒有額外的一行代碼,在 .fetch()的例子中,我們必須將數據轉換為JSON格式。在一個較大的項目中,如果你創建了大量的調用,那麼使用 axios 來避免重複代碼會更舒服。

錯誤處理

在這一點上,我們還需要給 axios 點贊,因為處理錯誤是非常容易的。如果出現像404這樣的錯誤響應,promise就會被拒絕並返回一個錯誤,所以我們需要捕獲一個錯誤,我們可以檢查它是什麼類型的錯誤,就是這樣。讓我們看看代碼示例。

axios.get('url')
  .then((response) => console.log(response))
  .catch((error) => {
    if (error.response) {
      // When response status code is out of 2xx range 
      console.log(error.response.data)
      console.log(error.response.status)
      console.log(error.response.headers)
    } else if (error.request) {
      // When no response was recieved after request was made
      console.log(error.request)
    } else {
      // Error
      console.log(error.message)
    }
  })

在上面的代碼中,當響應良好時,我返回了數據,但是如果請求以任何方式失敗,我就能夠檢查 .catch() 部分中的錯誤類型並返回正確的消息。

對於 .fetch() 方法,就比較複雜了。每次我們從 .fetch() 方法中得到響應時,我們需要檢查狀態是否成功,因為即使不是,我們也會得到響應。在 .fetch() 的情況下,只有當請求沒有完成時,promise才會被解決。讓我們看一下代碼示例。

fetch('url')
  .then((response) => {
    if (!response.ok) {
      throw Error(response.statusText);
    }
    return response.json()
  })
  .then((data) => console.log(data))
  .catch((error) => console.log(error))

在這段代碼中,我已經在承諾對象中檢查了代碼的狀態,如果響應有狀態 ok,那麼我就可以處理並使用 .json() 方法,但如果沒有,我必須在 .then() 裏面返回錯誤。

為了方便和正確的錯誤處理,對於你的項目來說,axios 絕對會是一個更好的解決方案,但如果你正在構建一個只有一兩個請求的小項目,使用 .fetch() 是可以的,但你需要記住正確處理錯誤。

下載進度

當我們需要下載大量的數據時,一種跟蹤進度的方法會很有用,特別是當用戶的網絡速度很慢時。早期,為了實現進度指標,開發者使用了 XMLHttpRequest.onprogress 回調。在 .fetch() 和 axios 中,有不同的方法來實現。

為了在 .fetch() 中跟蹤下載進度,我們可以使用其中一個 response.body 屬性,一個 ReadableStream 對象。它逐塊提供主體數據,並允許我們計算時間消耗了多少數據。

在axios中,實現一個進度指示器也是可能的,而且更容易,因為存在一個現成的模塊,可以安裝和實現,它叫做Axios Progress Bar。

如果你有大量的大數據要下載,你想跟蹤進度指標的進度,你可以用 axios 來管理,更容易更快,但 .fetch() 也提供了這種可能性,只是它需要更多的代碼來開發同樣的結果。

HTTP攔截

當我們需要檢查或改變我們從應用程序到服務器的HTTP請求時,或者以其他方式,例如,為了驗證,HTTP攔截可能是重要的。

在 axios 的情況下,HTTP攔截是這個庫的關鍵功能之一,這就是為什麼我們不需要創建額外的代碼來使用它。讓我們看一下代碼示例,看看我們能做到多麼容易。

// 請求攔截
axios.interceptors.request.use((config) => {
  console.log('Request sent');
})
// 響應攔截
axios.interceptors.response.use((response) => {
  // do an operation on response
  return response
})
axios.get('url')
  .then((response) => console.log(response))
  .catch((error) => console.log(error))

在代碼中,您可以看到請求攔截和響應攔截。在第一種情況下,我創建了一個 console.log,告知發送請求的情況,在響應攔截中,我們可以對響應做任何操作,然後返回。

.fetch() 默認不提供HTTP攔截功能,我們可以覆蓋 .fetch() 方法,定義發送請求過程中需要發生的事情,當然,這需要更多的代碼,可能比使用 axios 功能更複雜。

總結

在這篇文章中,我比較了用於創建HTTP請求的兩種方法,從簡單的概述開始,通過語法和一些重要的功能,如下載進度或錯誤處理。

通過比較可以看出,對於有大量HTTP請求,需要良好的錯誤處理或HTTP攔截的應用,Axios是一個更好的解決方案。在小型項目的情況下,只需要幾個簡單的API調用,Fetch也是一個不錯的解決方案。

在選擇項目的最佳解決方案時,還要注意一個因素,這是非常重要的。大多數瀏覽器和Node.js環境都支持Axios,而現代瀏覽器僅支持Fetch,並且某些版本可能會與舊版本一起發布。

通過這些知識的了解,希望大家能夠選擇出最適合自己的方案,也希望大家覺得這個比較有幫助。

來源:https://medium.com
作者:Harsh Patel

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9885

沐鳴下載_二進制數與位運算符

位運算符是基於二級制數進行操作的,即表示数字的 32 個數位,它由0和1組成…

二進制數表示整數18

ECMAScript整數有兩種類型,即有符號整數(允許用正數和負數)和無符號整數(只允許用正數)。在ECMAScript中,所有整数字面量默認都是有符號整數。

有符號整數使用31位(第0到第30位)表示整數的數值,用第32位(第31位)表示整數的符號,0表示正數,1表示負數。數值範圍從-2147483648到2147483647。

前31位中的每一位都表示2的冪,從第1 位(位 0)開始,表示20,第2位(位1)表示21。沒用到的位用0填充,即忽略不計。例如,下圖展示的是數18的表示法。

18的二進製版本只用了前5位,它們是這個数字的有效位。把数字轉換成二進制字符串,就能看到有效位:

var Num = 18;
alert(Num.toString(2));	//輸出 "10010"

這段代碼只輸出”10010”,而不是18的32位表示。其他的數位並不重要,因為僅使用前5位即可確定這個十進制數值。如下圖所示:

二級制數表示負數-18

負數也存儲為二進制代碼,不過採用的形式是二進制補碼。計算数字二進制補碼的步驟有三步:

1.確定該数字的非負版本的二進製表示(例如,要計算-18的二進制補碼,首先要確定18的二進製表示) 2.求得二進制反碼,即要把0替換為1,把1替換為0 3.在二進制反碼上加1

按照如上步驟計算:

1)獲取18的二級製表示

0000 0000 0000 0000 0000 0000 0001 0010

2)求得二進制反碼

1111 1111 1111 1111 1111 1111 1110 1101

3)在二進制反碼上加1

1111 1111 1111 1111 1111 1111 1110 1101
                                      1
---------------------------------------
1111 1111 1111 1111 1111 1111 1110 1110

因此,-18的二進製表示即1111 1111 1111 1111 1111 1111 1110 1110。記住,在處理有符號整數時,開發者不能訪問31位。

特別需要注意的是,把負整數轉換成二進制字符串后,ECMAScript並不以二進制補碼的形式显示,而是用数字絕對值的標準二進制代碼前面加負號的形式輸出。例如:

var iNum = -18;
alert(iNum.toString(2));	//輸出 "-10010"

這段代碼輸出的是”-10010”,而非二進制補碼,這是為避免訪問位31。所有整数字面量都默認存儲為有符號整數。只有ECMAScript的位運算符才能創建無符號整數。

二級制數表示小數

現代計算機中,一般都以IEEE 754標準存儲浮點數,這個標準的在內存中存儲的形式實際上是一種科學計數法 ,用符號,指數和尾數來表示,底數為2,也就是把浮點數表示為尾數乘以2的指數次方再添加上符號的形式。

  符號位 階碼 尾數 總長度
float 1 8 23 32
double 1 11 52 64

浮點數在內存中的是使用科學計數法來表示的,我們先將浮點數用二進制的形式表示出來。

// 整數部分
// 例如12可以表示為
// 2³*1 + 2²*1 + 2¹*0 + 2º*0  = 12
// 8*1 + 4*1 + 2*0 + 1*0  = 12
// 二級製表示為1100

//小數部分
// 例如0.18的小數部分可以表示為
// ½*0 + ¼*0 +  ...  = 0.18
// 0.5*0 + 0.25*0 + 0.125*1 + 0.0625*0 + 0.03125*1 + ...  = 0.18
// 也就是說小數可以表示為2的附一、負二、負三次方...這樣的數相加
// 二級製表示為0.001011100001010001111010111000010100011110101110000101

12.18表示為double

1)獲取二進製表示

12.18使用二級製表示為1100.001011100001010001111010111000010100011110101110000101。

2)獲取科學計數法的表示

double類型尾數為52位,使用科學計數法表示,不足52位補0,超出部分刪除,最終結果為1.1000010111000010100011110101110000101000111101011100*2³。

3)計算階碼

double類型階碼共11位,可以表示-1024~1023,因為指數可以為負數,為了方便表示,先加上1023變為非負數,上面的3表示為3+1023=1026,二進製為10000000010。

4)最終結果

符號位,0為正,1為負。所以最終結果是

0 10000000010 1000010111000010100011110101110000101000111101011100

12.18表示為float

1)獲取二進製表示

12.18使用二級製表示為1100.001011100001010001111010111000010100011110101110000101。

2)獲取科學計數法的表示

float類型尾數為23位,使用科學計數法表示,不足52位補0,超出部分刪除,最終結果為1.10000101110000101000111*2³。

3)計算階碼

float類型階碼共8位,表示的範圍是-128~127,因為指數可以為負數,為了方便表示,先加上127變為非負數,上面的3表示為3+127=130,二進製為10000010。

4)最終結果

符號位,0為正,1為負。所以最終結果是

0 10000010 10000101110000101000111

二級制與8進制、16進制之間的轉換

十進制數由0~9共10個数字字符組成,在十進制數的每一位上滿十進一,在十進制每一位中表示最大数字為9.

二進制數由0和1兩個数字字符組成,在二進制中“逢二進一”,在二進制每一位中表示最大数字為1.

八進制是由0~7共8個数字字符組成,在八進制中“逢八進一”,在八進制中每一位中表示最大数字為7.

十六進制是由0~9、A、B、C、D、E、F共16個字符組成,在十六進制中“逢十六進一”,在十六進制中最大的數是F。

// 如十進制的123使用二進制、八進制、十六進製表示
// 二進制
// 123 = 64*1 + 32*1 + 16*1 + 8*1 + 4*0 + 2*1 + 1*1
// 1111011
(123).toString(2)

// 八進制
// 123 = 8*8*1 + 8*7 + 1*3
// 173
(123).toString(8)

// 十六進制
// 123 = 16*7 + 1*11
// 7b
(123).toString(16)

位運算操作

程序中的所有數在計算機內存中都是以二進制的形式儲存的。位運算就是直接對整數在內存中的二進制位進行操作。

1、位運算 NOT(~)

位運算NOT由否定號(~)表示,位運算NOT是三步的處理過程:1)把運算數轉換成32位数字2)把二進制數轉換成它的二進制反碼3)把二進制數轉換成浮點數。它實質上是對数字求負,然後減1。

var iNum1 = 25;		//25 等於 00000000000000000000000000011001
var iNum2 = ~iNum1; //轉換為 11111111111111111111111111100110
alert(iNum2);		//輸出 "-26"

2、位運算 AND(&)

兩個都為1才為1,否則為0。

第一個数字中的數位 第二個数字中的數位 結果
1 1 1
1 0 0
0 1 0
0 0 0
**3、位運算 OR( )**

兩个中有一個為1結果就為1,否則為0。

第一個数字中的數位 第二個数字中的數位 結果
1 1 1
1 0 1
0 1 1
0 0 0

4、位運算 XOR(^)

兩个中有且僅有一個為1結果就為1,否則為0。

第一個数字中的數位 第二個数字中的數位 結果
1 1 0
1 0 1
0 1 1
0 0 0

位運算常見使用場景

1、判斷一個數是奇數還是偶數

利用and運算 & 判斷一個數是奇數還是偶數,這是因為二進制的最末位為0表示該數為偶數,最末位為1表示該數為奇數。因此只需要把數和1進行and運算,根據結果就可以判斷。

var val = 15;
if(val&1 === 0){
    console.log('值為偶數')
}
if(val&1 === 1){
    console.log('值為奇數')
}

來自:https://www.kelede.win/posts/二進制數與位運算符/

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9840

沐鳴平台_img標籤到底是行內元素還是塊級元素

面試官問你<img>是什麼元素時你怎麼回答

寫這篇文章源自我之前的一次面試,題目便是問img標籤屬於塊級元素還是行內元素,當時想都沒想就說了是行內(inline)元素,面試官追問為什麼能夠設置 <img /> 標籤的寬和高,當時腦子突然一懵,發現這是自己技術上的一個空白,所以有了這篇文章。

閱讀本文您將收穫

<img />標籤的基本使用

MDN關於元素的定義

特殊的可替換元素

<img /> 標籤的基本使用

瀏覽器支持

所有主流瀏覽器都支持 <img> 標籤

標籤定義及使用說明

<img> 標籤定義 html 頁面中的圖像

<img> 標籤有兩個必需的屬性:src 和 alt

強烈推薦在開發中每個圖像中都使用 alt 屬性。這樣即使圖像無法显示,用戶還是可以看到關於丟失了什麼東西的一些信息。而且對於殘疾人來說,alt 屬性通常是他們了解圖像內容的唯一方式

<img />究竟是什麼元素

<img /> 是行內元素還是塊級元素?

<img /> 標籤沒有獨佔一行,所以是行內元素,這沒啥問題

既然是行內元素為什麼能夠設置寬高呢?

這個問題就要引申出下面部分了,<img /> 標籤屬於替換元素,具有內置的寬高屬性,所以可以設置,具體解釋看下面。

元素的定義

從元素本身的特點來講,可以分為不可替換元素和替換元素

元素相關的MDN解釋

不可替換元素

(X)html 的大多數元素是不可替換元素,即其內容直接表現給用戶端(例如瀏覽器)

如:<h1>我是標題</h1>

可替換元素

瀏覽器根據元素的標籤和屬性,來決定元素的具體显示內容

例如瀏覽器會根據 <img> 標籤的src屬性的值來讀取圖片信息並显示出來,而如果查看(X)HTML代碼,則看不到圖片的實際內容;又例如根據 <input> 標籤的type屬性來決定是显示輸入框,還是單選按鈕等

(X)HTML中的 <img>、<input>、<textarea>、<select>、<object> 都是替換元素。這些元素往往沒有實際的內容,即是一個空元素

如:<img src=”tigger.jpg”/>、<input type=”submit” name=”Submit” value=”提交”/>

可替換元素的性質同設置了display:inline-block的元素一致


特殊的可替換元素

<img>屬於可替換元素

<img>同時具有行內元素,行內塊,和塊級元素的特性

替換元素一般有內在尺寸,所以具有 width 和 height,可以設定

例如你不指定 <img> 的 width 和 height 時,就按其內在尺寸显示,也就是圖片被保存的時候的寬度和高度

對於表單元素,瀏覽器也有默認的樣式,包括寬度和高度

<img>、<input>屬於行內替換元素。height/width/padding/margin均可用。效果等於塊元素

來自:https://www.cnblogs.com/programmerzhang/archive/2020/11/27/14047688.html

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9868

沐鳴平台首頁_TypeScript的索引類型與映射類型,以及常用工具泛型的實現

相信現在很多小夥伴都在使用 TypeScript(以下簡稱 TS),在 TS 中除了一些常用的基本類型外,還有一些稍微高級一點的類型,這些就是我本次文章要講的內容:索引類型與映射類型,希望小夥伴們看過這篇文章后能對 TS 有更深一步的理解。

索引類型

下面我通過一個官方的例子來說明下什麼是索引類型:

function pluck(o, names) {
  return names.map((n) => o[n])
}

這是個簡單的函數,names 是一個數組,裏面是 key 值,我們可以從“o”裏面取出這些 key 值,理想情況下 names 裏面的 key 應該都是“o”裡面包含的,否則最終的結果裏面就會有 undefined,這個函數返回的結果也應該是“o”中都包含的 value 值,那麼我們如何才能做到這些類型約束呢,如果只用一些基礎類型,很難達到滿意的效果,下面使用索引類型改寫下:

function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
  return names.map((n) => o[n])
}

interface Person {
  name: string
  age: number
}
let person: Person = {
  name: 'Jarid',
  age: 35
}
let strings: string[] = pluck(person, ['name']) // ok, string[]

改寫后這個函數是一個泛型函數,泛型為 T 和 K,其中 K 有點特殊,K extends keyof T,是什麼意思呢,其中 keyof 就是索引類型查詢操作符,我們從字面意思理解,它就是 T 的 key,就是 T 上已知的公共屬性名的聯合,對於上面的代碼,keyof Person就是’name’|’age’,那麼K extends keyof T就是K extends ‘name’|’age’,這樣我們就獲取到了 Person 上所有 key 組成的一個聯合類型,然後參數o: T, names: K[],就很好理解了,names 就是 K 組成的一個數組。返回值中T[K][]我們需要拆開來看 T[K]和[],就是 T[K]組成的一個數組,那麼 T[K]是什麼類型呢,它就是索引訪問操作符,類似於 js 中對象的取值操作,不過這裏取的是類型,因為 K 是’name’|’age’,所以 T[K]就是string|number,這些就是索引類型,其實也不難理解,下面再說下映射類型,它和索引類型結合起來可以做很多事情。

映射類型

映射類型也很容易理解,我們先看一個簡單的例子

type Keys = 'option1' | 'option2'
type Flags = { [K in Keys]: boolean }

這個就是一個簡單的映射類型,其中的in可以理解為是我們平時用的for…in,就是去遍歷 Keys,然後把 boolean 賦給每一個 key,上面的 Flags 得到的結果就是

type Flags = {
  option1: boolean
  option2: boolean
}

很簡單吧,那麼這個東西有什麼用處呢,請看下面的例子:

// Person
type Person {
    name: string
    age: number
}

我們想把這個 Person 裏面的屬性都變成只讀的,像這樣:

// Readonly Person
type Person {
    readonly name: string
    readonly age: number
}

如果我們有很多這樣的類型,那麼改起來會很麻煩,因為每次都要把這個類型重新寫一遍。其實我們可以使用剛才的索引類型和映射類型來寫一個泛型:

type Readonly<T> = {
  readonly [P in keyof T]: T[P]
}

[P in keyof T]就是遍歷 T 中的 key,T[P]就是當前的 key 的類型,其實[P in keyof T]: T[P]就是把 T 遍歷了一遍,但是我們在屬性前面加了個 readonly,這樣我們調用這個泛型的時候,它就會把傳入的類型的 key 遍歷一遍,遍歷的同時在前面加個 readonly,最終給我們返回一個新的類型。我們在調用的時候只需要這麼用:

type Readonly<T> = {
  readonly [P in keyof T]: T[P]
}
type Person {
    name: string
    age: number
}
type ReadonlyPerson = Readonly<Person>

索引類型和映射類型除了能實現 Readonly,還能實現很多有意思的東西,我們平時在使用 TS 的時候,TS 已經內置了一些常用的輔助泛型,剛才的 Readonly 就是其一,另外還有很多,我從 TS 的類型定義文件里找了一些,這些泛型從簡單到複雜的都有,但基本上都是用上面提到的兩個類型實現的,下面我們一起來分析一下。

TS 常用的輔助泛型及其實現方式

首先來看第一個

/**
 * Make all properties in T optional
 */
type Partial<T> = {
  [P in keyof T]?: T[P]
}

相信這個泛型很多人都用過,就是把類型都變成可選的,和剛才的 Readonly 是類似的實現方式,只是這個是在後面加了個問號,這樣一來屬性就變成可選的了。
與之相對的還有一個 Required

/**
 * Make all properties in T required
 */
type Required<T> = {
  [P in keyof T]-?: T[P]
}

注意這個稍有點不同,它是-?,其實就是減去問號,這樣就可以把問號去掉,從而變成必選的屬性。再來看下一個

/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
  [P in K]: T[P]
}

如果你理解了最開始的那個 pluck 函數,這個就很好理解了,我們傳入 T 和 K,其中 K 是 T 的 keys 組成的聯合類型,再看返回值[P in K]: T[P],就是把 K 遍歷了一遍,同時賦值上原類型,那麼綜合來看 Pick 就是幫我們提取出某些類型的,比如通過Pick<Person, ‘name’>我們就可以得到{name: string},再來看下一個

/**
 * Exclude from T those types that are assignable to U
 */
type Exclude<T, U> = T extends U ? never : T

這個泛型傳入一個 T 和 U,然後它判斷了 T 是否屬於 U,屬於的話返回 never 否則返回原類型 T,注意 never 在最終的類型中是不會存在的,所以它可以幫助我們消除某些屬性,其實這個 Exclude 就是消除了T extends U的類型,比如我們使用Exclude<‘a’|’b’,’b’|’c’>,最終會得到’a’,與之相反的有:

/**
 * Extract from T those types that are assignable to U
 */
type Extract<T, U> = T extends U ? T : never

這個正好相反,是從 T 中取出 U 中擁有的類型。

有了 Exclude,我們就可以和 Pick 結合來實現另外一個:

/**
 * construct a type with the properties of T except for those in type K.
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>

這個泛型是先使用了Exclude<keyof T, K>,去除了 keyof T 中的 K,然後又使用 Pick 取出了這些類型,這樣我們就可以從 T 中去除 K 裡面包含的 keys 了,達到和 Pick 相反的效果。

我們再來看另一個稍微複雜一點的

type NonNullObject<O> = Pick<
  O,
  {
    [K in keyof O]: O[K] extends null | undefined ? never : K
  }[keyof O]
>

這個不是 TS 內置的類型,但也是一個很有用的類型,我們來一點一點分析。首先這個泛型使用了 Pick,我們知道 Pick 就是取出一些屬性,我們先看傳給 Pick 的第二個參數

{
  [K in keyof O]: O[K] extends null | undefined ? never : K
}[keyof O]

它遍歷了 O 的 keys,然後進行了一個判斷,如果是extends null | undefined則返回 never,否則返回 K,K 就是 O 中的 key 值,注意這裏和之前的一些泛型有些不一樣,之前的都是O[K],而這裏的屬性的值還是 K,最終我們得到的是類似K:K這樣的東西,比如{name: string, age: null}這個,經過上面的轉化會變成{name:’name’, age:never},可能有些小夥伴還不清楚為什麼要這樣轉換,我們接着往下分析,經過這個轉換之後,又進行了一個操作[keyof O],對於 Person,keyof O 就是’name’|’age’,那麼這裏就就是{name:’name’, age:never}[‘name’|’age’],這樣就很清晰了,其實就是一個取值操作,這樣我們就可以得到’name’|never,還記得 never 的特性嗎,它可以幫我們消除一些類型,那麼最終的就是’name’,這也是為什麼我們寫成類似 K:K 這樣,就是要把 null|undefined 對應的 key 轉換成 never,然後再通過 keyof 把他們全都取出來,別忘了最外面還有一個 Pick,這樣我們就從原始類型中去除了 null|undefined。

另外還有一個比較有用的是 ReturnType

/**
 * Obtain the return type of a function type
 */
type ReturnType<T extends (...args: any) => any> = T extends (
  ...args: any
) => infer R
  ? R
  : any

它可以幫我們取到函數返回值的類型,這個 ReturnType 接收的一個參數是函數,然後進行了一個判斷T extends (…args: any) => infer R,就是判斷是否是函數,這裡有個東西是 infer,通過這個操作符我們可以獲取 R 的引用,就是函數的返回值,最終再把 R 返回出去,就獲得了函數 T 的返回值。

其實除了我分析的這些泛型,TS 還內置了其他的很多泛型,比如還有獲取函數的參數的,獲取構造函數類型的,總的來說各種泛型基本上都可以用索引類型和映射類型實現,希望大家看過這篇文章后能多多使用這兩種類型,在自己的項目里也能開發一些常用的輔助泛型,來提升工作效率。

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9892

沐鳴代理:_unaipp異步加載數據實現頁面同步

前言

之前學vue的時候看到過異步加載數據的寫法,並且在vue裏面用的大都是ES6的語法。對於async和await並不太了解,網上說在uniapp中請求數據時總是數據和頁面不同步,用了他倆可以將數據搞同步。那今天就搞搞async和await

開搞

1.在uniapp中封裝一個請求方法,文件名request.js

export default function request(url, data = {}, method = 'GET') { //data和method是初始化值
    return new Promise((resolve, reject) => {
        uni.request({
            url: url,
            data: data,
            method: method,
            success: (res) => {
                console.log(res)
                resolve(res.data);
            },
            fail: (err) => {
                reject(err)
            }
        });
    })
}

2.再開另一個js文件,叫做axios.js,用於填寫request.js中的參數並獲取返回數據,這裏面可以寫好多個export,隨意點嘛

import request from "./request.js";
const url = "http://localhost:9988";

//頁面初始化時獲取前1-20條數據
export const getInitPaged = () => request(url + '/find/pages/1/20')

3.在xxx.vue中的method中寫一個方法,用於將axios.js中返回的值接收並處理

//1.先將文件導入
import { getInitPaged } from '../../request/axios.js';
//2.在method中創建一個方法,用async修飾,裏面的方法用await修飾
async getInitPages() {
    const arr = await getInitPaged();
    this.version = arr.object;//將數據給data裏面的變量
},
//初始化的時候可以使用
created() {
    this.getInitPages()
},

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9890

沐鳴登錄_前端高效開發必備的 js 庫梳理

之前有很多人問學好前端需要學習哪些 js 庫, 主流框架應該學 vue 還是 react ? 針對這些問題, 筆者來說說自己的看法和學習總結.

首先我覺得在學習任何知識之前必須要有一個明確的學習目標, 知道自己為什麼要學它, 而不是看網上說的一股腦的給你灌輸各種知識, 讓你學習各種庫, 從而不斷的製造大家的焦慮感.

前端由於入行門檻低, 更新換代很快, 每年都會有大量新的框架和庫出現, 也有大量庫被淘汰(比如 JQuery, 但是學習它的設計思想很有必要). 所以我們大可不必擔心, 保持自己的學習步伐, 按需學習即可. 比如說你對移動端比較感興趣, 工作中也剛好涉及到一些技術的應用,那麼我可以專門研究移動端相關的技術和框架, 又或者你對企業後台/中台產品感興趣, 比較喜歡開發PC端項目, 那麼我們可以專門研究這種類型的js庫或者框架, 接下來筆者也是按照不同前端業務的需求, 來整理一份能快速應用到工作中的js庫, 以提高大家的開發效率.

js常用工具類

lodash 一個一致性、模塊化、高性能的 JavaScript 實用工具庫。

ramda 一個很重要的庫,提供了許多有用的方法,每個 JavaScript 程序員都應該掌握這個工具

day.js 一個輕量的處理時間和日期的 JavaScript 庫,和 Moment.js 的 API 設計保持完全一樣, 體積只有2kb

big.js 一個小型,快速的JavaScript庫,用於任意精度的十進制算術運算

qs 一個 url參數轉化 (parse和stringify)的輕量級js庫

dom庫

JQuery 封裝了各種dom/事件操作, 設計思想值得研究借鑒

zepto jquery的輕量級版本, 適合移動端操作

fastclick 一個簡單易用的庫,它消除了移動端瀏覽器上的物理點擊和觸發一個 click 事件之間的 300ms 的延遲。目的就是在不干擾你目前的邏輯的同時,讓你的應用感覺不到延遲,反應更加靈敏。

文件處理

file-saver 一個在客戶端保存文件的解決方案,非常適合在客戶端上生成文件的Web應用程序

js-xlsx 一個強大的解析和編寫excel文件的庫

網絡請求

Axios 一個基於 Promise 的 HTTP 庫,可用在 Node.js 和瀏覽器上發起 HTTP 請求,支持所有現代瀏覽器,甚至包括 IE8+

Superagent 基於Ajax的優化, 可以與 Node.js HTTP 客戶端搭配使用

fly.js 一個基於promise的http請求庫, 可以用在node.js, Weex, 微信小程序, 瀏覽器, react Native中

動畫庫

Anime.js 一個JavaScript動畫庫,可以處理css屬性,單個css轉換,SVG或任何DOM屬性以及JavaScript對象

Velocity 一個高效的 Javascript 動畫引擎,與jQuery的 $.animate() 有相同的API, 同時還支持彩色動畫、轉換、循環、畫架、SVG支持和滾動等效果

Vivus 一個零依賴的JavaScript動畫庫,可以讓我們用SVG製作動畫,使其具有被繪製的外觀

GreenSock JS 一個JavaScript動畫庫,用於創建高性能、零依賴、跨瀏覽器動畫,已在超過400萬個網站上使用, 並且可以在React、vue、Angular項目中使用

Scroll Reveal 零依賴,為 web 和移動瀏覽器提供了簡單的滾動動畫,以動畫的方式显示滾動中的內容

Kute.js 一個強大高性能且可擴展的原生JavaScript動畫引擎,具有跨瀏覽器動畫的基本功能

Typed.js 一個輕鬆實現打字效果的js插件

fullPage.js 一個可輕易創建全屏滾動網站的js滾動動畫庫, 兼容性無可替代

iscroll 移動端使用的一款輕量級滾動插件

鼠標/鍵盤相關

KeyboardJS 一個在瀏覽器中使用的庫(與node.js兼容).它使開發人員可以輕鬆設置鍵綁定和使用組合鍵來設置複雜的綁定.

SortableJS 功能強大的JavaScript 拖拽庫

圖形/圖像處理庫

html2canvas 一個強大的使用js開發的瀏覽器網頁截圖工具

dom-to-image 一個可以將任意DOM節點轉換為用JavaScript編寫的矢量(SVG)或光柵(PNG或JPEG)圖像的庫

pica 一個在瀏覽器中調整圖像大小,而不會出現像素失真,處理速度非常快的圖片處理庫

Lena.js 一個輕量級的可以給你圖像加各種濾鏡的js庫

Compressor.js 一個使用本地canvas.toBlob API進行圖像有損壓縮的js庫

Fabric.js 一個易於使用的基於html5 canvas元素的圖片編輯器

merge-images 一個將多張圖片合併成一張圖的js插件

cropperjs 一款強大的圖片裁切庫, 支持靈活的圖片裁切方式

Grade 一個基於圖像中的前2種主要顏色生成互補漸變背景的庫

作者:徐小夕

鏈接:https://juejin.cn/post/6898962197335490573

來源:掘金

站長推薦

1.雲服務推薦: 國內主流雲服務商,各類雲產品的最新活動,優惠券領取。地址:阿里雲騰訊雲華為雲

2.廣告聯盟: 整理了目前主流的廣告聯盟平台,如果你有流量,可以作為參考選擇適合你的平台點擊進入

鏈接: http://www.fly63.com/article/detial/9889