92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线

首頁

JavaScript的padStart()和padEnd()格式化字符串使用技巧

seo達人

用例

讓我們從介紹幾種不同的填充用例開始。


標簽和值

假設你在同一行上有標簽和值,例如 name:zhangsan 和 Phone Number:(555)-555-1234。如果把他們放在一起看起來會有點奇怪,會是這樣:


Name: zhangsan

Phone Number: (555)-555-1234

你可能想要這個。


Name:           zhangsan

Phone Number:   (555)555-1234

或這個...


       Name: zhangsan

Phone Number: (555)555-1234

金額

在中國,顯示價格時通常顯示兩位數的角、分。所以代替這個...


¥10.1

你會想要這個。


¥10.01

日期

對于日期,日期和月份都需要2位數字。所以代替這個...


2020-5-4

你會想要這個。


2020-05-04

時間

與上面的日期類似,對于計時器,你需要2位數字表示秒,3位數字表示毫秒。所以代替這個...


1:1

你會想要這個。


01:001

padstart()

讓我們從 padStart() 以及標簽和值示例開始。假設我們希望標簽彼此正確對齊,以使值在同一位置開始。


       Name: zhangsan

Phone Number: (555)555-1234

由于 Phone Number 是兩個標簽中較長的一個,因此我們要在 Name 標簽的開頭加上空格。為了將來的需要,我們不要把它專門填充到電話號碼的長度,我們把它填充到長一點,比如說20個字符。這樣一來,如果你在未來使用較長的標簽,這一招仍然有效。


在填充之前,這是用于顯示此信息的入門代碼。


const label1 = "Name";

const label2 = "Phone Number";

const name = "zhangsan"

const phoneNumber = "(555)-555-1234";


console.log(label1 + ": " + name);

console.log(label2 + ": " + phoneNumber);


//Name: zhangsan

//Phone Number: (555)-555-1234

現在,讓我們填充第一個標簽。要調用 padStart(),你需要傳遞兩個參數:一個用于填充字符串的目標長度,另一個用于你希望填充的字符。在這種情況下,我們希望長度為20,而填充字符為空格。


const label1 = "Name";

const label2 = "Phone Number";

const name = "zhangsan"

const phoneNumber = "(555)-555-1234";


console.log(label1.padStart(20, " ") + ": " + name);

console.log(label2 + ": " + phoneNumber);


//               Name: zhangsan

////Phone Number: (555)-555-1234

現在填充第二行。


const label1 = "Name";

const label2 = "Phone Number";

const name = "zhangsan"

const phoneNumber = "(555)-555-1234";


console.log(label1.padStart(20, " ") + ": " + name);

console.log(label2.padStart(20, " ") + ": " + phoneNumber);


//               Name: zhangsan

////     Phone Number: (555)-555-1234

padEnd()

對于相同的標簽和值示例,讓我們更改填充標簽的方式。讓我們將標簽向左對齊,以便在末尾添加填充。


初始代碼


const label1 = "Name";

const label2 = "Phone Number";

const name = "zhangsan"

const phoneNumber = "(555)-555-1234";


console.log(label1 + ": " + name);

console.log(label2 + ": " + phoneNumber);


//Name: zhangsan

//Phone Number: (555)-555-1234

現在,讓我們填充第一個標簽,與我們之前所做的類似,但有兩個小區別。現在,我們使用 padEnd() 而不是padStart(),并且需要在填充之前將冒號與標簽連接起來,這樣我們就能確保冒號在正確的位置。


const label1 = "Name";

const label2 = "Phone Number";

const name = "zhangsan"

const phoneNumber = "(555)-555-1234";


console.log((label1 + ': ').padEnd(20, ' ') + name);

console.log(label2 + ": " + phoneNumber);


//Name:               zhangsan

//Phone Number: (555)-555-1234

現在兩行都已填充。


const label1 = "Name";

const label2 = "Phone Number";

const name = "zhangsan"

const phoneNumber = "(555)-555-1234";


console.log((label1 + ': ').padEnd(20, ' ') + name);

console.log((label2 + ': ').padEnd(20, ' ') + phoneNumber);


//Name:               zhangsan

//Phone Number:       (555)-555-1234

數字(價格、日期、計時器等)呢?

padding函數是專門針對字符串而不是數字的,所以,我們需要先將數字轉換為字符串。


價格

讓我們看一下顯示價格的初始代碼。


const rmb = 10;

const cents = 1;

console.log("¥" + rmb + "." + cents); //¥10.1

要填充分,我們需要先將其轉換為字符串,然后調用 padStart() 函數,指定長度為1且填充字符為'0';


const rmb = 10;

const cents = 1;

console.log("¥" + rmb + "." + cents.toString().padStart(2,0)); //¥10.01

日期

這是顯示日期的初始代碼。


const month = 2;

const year = 2020;


console.log(year + "-" + month); //2020-2

現在,讓我們填充月份以確保它是兩位數。


const month = 2;

const year = 2020;


console.log(year + "-" + month.toString().padStart(2,"0")); // 2020-02

計時器

最后是我們的計時器,我們要格式化兩個不同的數字,即秒和毫秒。盡管有相同的原則。這是初始代碼。


const seconds = 1;

const ms = 1;


console.log(seconds + ":" + ms); //1:1

現在要填充,我將在單獨的行上進行填充,以便于閱讀。


const seconds = 1;

const formattedSeconds = seconds.toString().padStart(2,0);

const ms = 1;

const formattedMs = ms.toString().padStart(3,0);


console.log(formattedSeconds + ":" + formattedMs); // 01:001

最后

雖然編寫自己的padding函數并不難,但既然已經內置在JavaScript中,為什么還要自己去做呢?有很多有趣的函數已經內置了。在你自己構建一些東西之前,可能值得先快速搜索一下。

JavaScript版數據結構與算法——基礎篇(一)

前端達人

數組

數組——最簡單的內存數據結構

數組存儲一系列同一種數據類型的值。( Javascript 中不存在這種限制)

對數據的隨機訪問,數組是更好的選擇,否則幾乎可以完全用 「鏈表」 來代替

在很多編程語言中,數組的長度是固定的,當數組被填滿時,再要加入新元素就很困難。Javascript 中數組不存在這個問題。

但是 Javascript 中的數組被實現成了對象,與其他語言相比,效率低下。

數組的一些核心方法

方法 描述
push 方法將一個或多個元素添加到數組的末尾,并返回該數組的新長度。(改變原數組)
pop 方法從數組中刪除最后一個元素,并返回該元素的值。(改變原數組)
shift 方法從數組中刪除第一個元素,并返回該元素的值,如果數組為空則返回 undefined 。(改變原數組)
unshift 將一個或多個元素添加到數組的開頭,并返回該數組的新長度(改變原數組)
concat 連接兩個或多個數組,并返回結果(返回一個新數組,不影響原有的數組。)
every 對數組中的每個元素運行給定函數,如果該函數對每個元素都返回 true,則返回 true。若為一個空數組,,始終返回 true。 (不會改變原數組,[].every(callback)始終返回 true)
some 對數組中的每個元素運行給定函數,如果任一元素返回 true,則返回 true。若為一個空數組,,始終返回 false。(不會改變原數組,)
forEach 對數組中的每個元素運行給定函數。這個方法沒有返回值,沒有辦法中止或者跳出 forEach() 循環,除了拋出一個異常(foreach不直接改變原數組,但原數組可能會被 callback 函數該改變。)
map 對數組中的每個元素運行給定函數,返回每次函數調用的結果組成的數組(map不直接改變原數組,但原數組可能會被 callback 函數該改變。)
sort 按照Unicode位點對數組排序,支持傳入指定排序方法的函數作為參數(改變原數組)
reverse 方法將數組中元素的位置顛倒,并返回該數組(改變原數組)
join 將所有的數組元素連接成一個字符串
indexOf 返回第一個與給定參數相等的數組元素的索引,沒有找到則返回 -1
lastIndexOf 返回在數組中搜索到的與給定參數相等的元素的索引里最大的值,沒有找到則返回 -1
slice 傳入索引值,將數組里對應索引范圍內的元素(淺復制原數組中的元素)作為新數組返回(原始數組不會被改變)
splice 刪除或替換現有元素或者原地添加新的元素來修改數組,并以數組形式返回被修改的內容(改變原數組)
toString 將數組作為字符串返回
valueOf 和 toString 類似,將數組作為字符串返回

是一種遵循后進先出(LIFO)原則的有序集合,新添加或待刪除的元素都保存在棧的同一端,稱作棧頂,另一端就叫棧底。在棧里,新元素都靠近棧頂,舊元素都接近棧底。

通俗來講,就是你向一個桶里放書本或者盤子,你要想取出最下面的書或者盤子,你必須要先把上面的都先取出來。

棧也被用在編程語言的編譯器和內存中保存變量、方法調用等,也被用于瀏覽器歷史記錄 (瀏覽器的返回按鈕)。

代碼實現

// 封裝棧
    function Stack() {
        // 棧的屬性
        this.items = []

        // 棧的操作
        // 1.將元素壓入棧
        Stack.prototype.push = function (element) {
            this.items.push(element)
        }
        // 2.從棧中取出元素
        Stack.prototype.pop = function () {
            return this.items.pop()
        }
        // 3.查看棧頂元素
        Stack.prototype.peek = function () {
            return this.items[this.items.length - 1]
        }
        // 4.判斷是否為空
        Stack.prototype.isEmpty = function () {
            return this.items.length === 0
        }
        // 5.獲取棧中元素的個數
        Stack.prototype.size = function () {
            return this.items.length
        }
        // 6.toString()方法
        Stack.prototype.toString = function () {
            let str = ''
            for (let i = 0; i< this.items.length; i++) {
                str += this.items[i] + ' '
            }
            return str
        }

    }

    // 棧的使用
    let s = new Stack()

隊列

隊列是遵循先進先出(FIFO,也稱為先來先服務)原則的一組有序的項。隊列在尾部添加新
元素,并從頂部移除元素。添加的元素必須排在隊列的末尾。

生活中常見的就是排隊

代碼實現

function Queue() {
        this.items = []
        // 1.將元素加入隊列
        Queue.prototype.enqueue = function (element) {
            this.items.push(element)
        }
        // 2.從隊列前端刪除元素
        Queue.prototype.dequeue = function () {
            return this.items.shift()
        }
        // 3.查看隊列前端元素
        Queue.prototype.front = function () {
            return this.items[0]
        }
        // 4.判斷是否為空
        Queue.prototype.isEmpty = function () {
            return this.items.length === 0
        }
        // 5.獲取隊列中元素的個數
        Queue.prototype.size = function () {
            return this.items.length
        }
        // 6.toString()方法
        Queue.prototype.toString = function () {
            let str = ''
            for (let i = 0; i< this.items.length; i++) {
                str += this.items[i] + ' '
            }
            return str
        }
    }
    
    // 隊列使用
    let Q = new Queue()

優先級隊列:

代碼實現


function PriorityQueue() {
        function QueueElement(element, priority) {
            this.element = element
            this.priority = priority
        }
        this.items = []

        PriorityQueue.prototype.enqueue = function (element, priority) {
            let queueElement = new QueueElement(element,priority)

            // 判斷隊列是否為空
            if (this.isEmpty()) {
                this.items.push(queueElement)
            } else {
                let added = false // 如果在隊列已有的元素中找到滿足條件的,則設為true,否則為false,直接插入隊列尾部
                for (let i = 0; i< this.items.length; i++) {
                    // 假設priority值越小,優先級越高,排序越靠前
                    if (queueElement.priority < this.items[i].priority) {
                        this.items.splice(i, 0, queueElement)
                        added = true
                        break
                    }
                }
                if (!added) {
                    this.items.push(queueElement)
                }
            }

        }
        
    }
    

鏈表

鏈表——存儲有序的元素集合,但在內存中不是連續放置的。


鏈表(單向鏈表)中的元素由存放元素本身「data」 的節點和一個指向下一個「next」 元素的指針組成。牢記這個特點

相比數組,鏈表添加或者移除元素不需要移動其他元素,但是需要使用指針。訪問元素每次都需要從表頭開始查找。

代碼實現:
單向鏈表


function LinkedList() {
        function Node(data) {
            this.data = data
            this.next = null

        }
        this.head = null // 表頭
        this.length = 0
        // 插入鏈表
        LinkedList.prototype.append = function (data) {
            // 判斷是否是添加的第一個節點
            let newNode = new Node(data)
            if (this.length == 0) {
                this.head = newNode
            } else {
                let current = this.head
                while (current.next) { 
                // 如果next存在,
                // 則當前節點不是鏈表最后一個
                // 所以繼續向后查找
                    current = current.next
                }
                // 如果next不存在
                 // 則當前節點是鏈表最后一個
                // 所以讓next指向新節點即可
                current.next = newNode
            }
            this.length++
        }
        // toString方法
        LinkedList.prototype.toString = function () {
            let current = this.head
            let listString = ''
            while (current) {
                listString += current.data + ' '
                current = current.next
            }
            return listString
        }
         // insert 方法
        LinkedList.prototype.insert = function (position, data) {
            if (position < 0 || position > this.length) return false
            let newNode = new Node(data)
            if (position == 0) {
                newNode.next = this.head
                this.head = newNode
            } else {
                let index = 0
                let current = this.head
                let prev = null
                while (index++ < position) {
                    prev = current
                    current = current.next
                }
                newNode.next = current
                prev.next = newNode
            }
            this.length++
            return true
        }
        // get方法
        LinkedList.prototype.get = function (position) {
            if (position < 0 || position >= this.length) return null
            let index = 0
            let current = this.head
            while (index++ < position){
                current = current.next
            }
            return current.data
        }
        LinkedList.prototype.indexOf = function (data) {
            let index = 0
            let current = this.head
            while (current) {
                if (current.data == data) {
                    return index
                } else {
                    current = current.next
                    index++
                }
            }

            return  -1
        }
        LinkedList.prototype.update = function (position, data) {
            if (position < 0 || position >= this.length) return false
            let index = 0
            let current = this.head
            while (index++ < position) {
                current = current.next
            }
            current.data = data
            return  true
        }
        LinkedList.prototype.removeAt = function (position) {
            if (position < 0 || position >= this.length) return null
            if (position == 0) {
                this.head = this.head.next
            } else {
                let index = 0
                let current = this.head
                let prev = null
                while (index++ < position) {
                    prev = current
                    current = current.next
                }
                prev.next = current.next
            }
            this.length--
            return  true


        }
        LinkedList.prototype.remove = function (data) {
            let postions = this.indexOf(data)

            return this.removeAt(postions)
        }
        
    }

    let list = new LinkedList()
雙向鏈表:包含表頭、表尾 和 存儲數據的 節點,其中節點包含三部分:一個鏈向下一個元素的next, 另一個鏈向前一個元素的prev 和存儲數據的 data牢記這個特點

function doublyLinkedList() {
        this.head = null // 表頭:始終指向第一個節點,默認為 null
        this.tail = null // 表尾:始終指向最后一個節點,默認為 null
        this.length = 0 // 鏈表長度

        function Node(data) {
            this.data = data
            this.prev = null
            this.next = null
        }

        doublyLinkedList.prototype.append = function (data) {
            let newNode = new Node(data)

            if (this.length === 0) {
            // 當插入的節點為鏈表的第一個節點時
            // 表頭和表尾都指向這個節點
                this.head = newNode
                this.tail = newNode
            } else {
            // 當鏈表中已經有節點存在時
            // 注意tail指向的始終是最后一個節點
            // 注意head指向的始終是第一個節點
            // 因為是雙向鏈表,可以從頭部插入新節點,也可以從尾部插入
            // 這里以從尾部插入為例,將新節點插入到鏈表最后
            // 首先將新節點的 prev 指向上一個節點,即之前tail指向的位置
                newNode.prev = this.tail
            // 然后前一個節點的next(及之前tail指向的節點)指向新的節點
            // 此時新的節點變成了鏈表的最后一個節點
                this.tail.next = newNode
            // 因為 tail 始終指向的是最后一個節點,所以最后修改tail的指向
                this.tail = newNode
            }
            this.length++
        }
        doublyLinkedList.prototype.toString = function () {
            return this.backwardString()
        }
        doublyLinkedList.prototype.forwardString = function () {
            let current = this.tail
            let str = ''

            while (current) {
                str += current.data + ''
                current = current.prev
            }

            return str
        }
        doublyLinkedList.prototype.backwardString = function () {
            let current = this.head
            let str = ''

            while (current) {
                str += current.data + ''
                current = current.next
            }

            return str
        }

        doublyLinkedList.prototype.insert = function (position, data) {
            if (position < 0 || position > this.length) return false
            let newNode = new Node(data)
            if (this.length === 0) {
                this.head = newNode
                this.tail = newNode
            } else {
                if (position == 0) {
                    this.head.prev = newNode
                    newNode.next = this.head
                    this.head = newNode
                } else if (position == this.length) {
                    newNode.prev = this.tail
                    this.tail.next = newNode
                    this.tail = newNode
                } else {
                    let current = this.head
                    let index = 0
                    while( index++ < position){
                        current = current.next
                    }
                    newNode.next = current
                    newNode.prev = current.prev
                    current.prev.next = newNode
                    current.prev = newNode

                }

            }

            this.length++
            return true
        }
        doublyLinkedList.prototype.get = function (position) {
            if (position < 0 || position >= this.length) return null
            let current = this.head
            let index = 0
            while (index++) {
                current = current.next
            }

            return current.data
        }
        doublyLinkedList.prototype.indexOf = function (data) {
            let current = this.head
            let index = 0
            while (current) {
                if (current.data === data) {
                    return index
                }
                current = current.next
                index++
            }
            return  -1
        }
        doublyLinkedList.prototype.update = function (position, newData) {
            if (position < 0 || position >= this.length) return false
            let current = this.head
            let index = 0
            while(index++ < position){
                current = current.next
            }
            current.data = newData
            return true
        }
        doublyLinkedList.prototype.removeAt = function (position) {
            if (position < 0 || position >= this.length) return null
            let current = this.head
            if (this.length === 1) {
                this.head = null
                this.tail = null
            } else {
                if (position === 0) { // 刪除第一個節點
                    this.head.next.prev = null
                    this.head = this.head.next
                } else if (position === this.length - 1) { // 刪除最后一個節點
                    this.tail.prev.next = null
                    this.tail = this.tail.prev
                } else {
                    let index = 0
                    while (index++ < position) {
                        current = current.next
                    }
                    current.prev.next = current.next
                    current.next.prev = current.prev
                }
            }
            this.length--
            return current.data
        }
        doublyLinkedList.prototype.remove = function (data) {
            let index = this.indexOf(data)
            return this.removeAt(index)
        }
    }


感謝你的閱讀~
————————————————
版權聲明:本文為CSDN博主「重慶崽兒Brand」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/brand2014/java/article/details/106134844



認識 ESLint 和 Prettier

seo達人

ESLint

先說是什么:ESLint 是一個檢查代碼質量與風格的工具,配置一套規則,他就能檢查出你代碼中不符合規則的地方,部分問題支持自動修復。


使用這么一套規則有什么用呢?如果單人開發的話倒是沒什么了,但是一個團隊若是存在兩種風格,那格式化之后處理代碼沖突就真的要命了,統一的代碼風格真的很重要!


(其實以前自己做一個項目的時候,公司電腦和家庭電腦的代碼風格配置不一樣,在家加班的時候也經常順手格式化了,這么循環了幾次不同的風格,導致 diff 極其混亂

JavaScript中的Event Loop(事件循環)機制

seo達人

事件循環

JavaScript是單線程,非阻塞的

瀏覽器的事件循環


執行棧和事件隊列

宏任務和微任務

node環境下的事件循環


和瀏覽器環境有何不同

事件循環模型

宏任務和微任務

經典題目分析

1. JavaScript是單線程,非阻塞的

單線程:


JavaScript的主要用途是與用戶互動,以及操作DOM。如果它是多線程的會有很多復雜的問題要處理,比如有兩個線程同時操作DOM,一個線程刪除了當前的DOM節點,一個線程是要操作當前的DOM階段,最后以哪個線程的操作為準?為了避免這種,所以JS是單線程的。即使H5提出了web worker標準,它有很多限制,受主線程控制,是主線程的子線程。


非阻塞:通過 event loop 實現。


2. 瀏覽器的事件循環

執行棧和事件隊列

為了更好地理解Event Loop,請看下圖(轉引自Philip Roberts的演講 《Help, I'm stuck in an event-loop》)

Help, I'm stuck in an event-loop


執行棧: 同步代碼的執行,按照順序添加到執行棧中


function a() {

   b();

   console.log('a');

}

function b() {

   console.log('b')

}

a();

我們可以通過使用 Loupe(Loupe是一種可視化工具,可以幫助您了解JavaScript的調用堆棧/事件循環/回調隊列如何相互影響)工具來了解上面代碼的執行情況。


調用情況


執行函數 a()先入棧

a()中先執行函數 b() 函數b() 入棧

執行函數b(), console.log('b') 入棧

輸出 b, console.log('b')出棧

函數b() 執行完成,出棧

console.log('a') 入棧,執行,輸出 a, 出棧

函數a 執行完成,出棧。

事件隊列: 異步代碼的執行,遇到異步事件不會等待它返回結果,而是將這個事件掛起,繼續執行執行棧中的其他任務。當異步事件返回結果,將它放到事件隊列中,被放入事件隊列不會立刻執行起回調,而是等待當前執行棧中所有任務都執行完畢,主線程空閑狀態,主線程會去查找事件隊列中是否有任務,如果有,則取出排在第一位的事件,并把這個事件對應的回調放到執行棧中,然后執行其中的同步代碼。


我們再上面代碼的基礎上添加異步事件,


function a() {

   b();

   console.log('a');

}

function b() {

   console.log('b')

   setTimeout(function() {

       console.log('c');

   }, 2000)

}

a();

此時的執行過程如下

img


我們同時再加上點擊事件看一下運行的過程


$.on('button', 'click', function onClick() {

   setTimeout(function timer() {

       console.log('You clicked the button!');    

   }, 2000);

});


console.log("Hi!");


setTimeout(function timeout() {

   console.log("Click the button!");

}, 5000);


console.log("Welcome to loupe.");

img


簡單用下面的圖進行一下總結


執行棧和事件隊列


宏任務和微任務

為什么要引入微任務,只有一種類型的任務不行么?


頁面渲染事件,各種IO的完成事件等隨時被添加到任務隊列中,一直會保持先進先出的原則執行,我們不能準確地控制這些事件被添加到任務隊列中的位置。但是這個時候突然有高優先級的任務需要盡快執行,那么一種類型的任務就不合適了,所以引入了微任務隊列。


不同的異步任務被分為:宏任務和微任務

宏任務:


script(整體代碼)

setTimeout()

setInterval()

postMessage

I/O

UI交互事件

微任務:


new Promise().then(回調)

MutationObserver(html5 新特性)

運行機制

異步任務的返回結果會被放到一個任務隊列中,根據異步事件的類型,這個事件實際上會被放到對應的宏任務和微任務隊列中去。


在當前執行棧為空時,主線程會查看微任務隊列是否有事件存在


存在,依次執行隊列中的事件對應的回調,直到微任務隊列為空,然后去宏任務隊列中取出最前面的事件,把當前的回調加到當前指向棧。

如果不存在,那么再去宏任務隊列中取出一個事件并把對應的回到加入當前執行棧;

當前執行棧執行完畢后時會立刻處理所有微任務隊列中的事件,然后再去宏任務隊列中取出一個事件。同一次事件循環中,微任務永遠在宏任務之前執行。


在事件循環中,每進行一次循環操作稱為 tick,每一次 tick 的任務處理模型是比較復雜的,但關鍵步驟如下:


執行一個宏任務(棧中沒有就從事件隊列中獲取)

執行過程中如果遇到微任務,就將它添加到微任務的任務隊列中

宏任務執行完畢后,立即執行當前微任務隊列中的所有微任務(依次執行)

當前宏任務執行完畢,開始檢查渲染,然后GUI線程接管渲染

渲染完畢后,JS線程繼續接管,開始下一個宏任務(從事件隊列中獲?。?

簡單總結一下執行的順序:

執行宏任務,然后執行該宏任務產生的微任務,若微任務在執行過程中產生了新的微任務,則繼續執行微任務,微任務執行完畢后,再回到宏任務中進行下一輪循環。


宏任務和微任務


深入理解js事件循環機制(瀏覽器篇) 這邊文章中有個特別形象的動畫,大家可以看著理解一下。


console.log('start')


setTimeout(function() {

 console.log('setTimeout')

}, 0)


Promise.resolve().then(function() {

 console.log('promise1')

}).then(function() {

 console.log('promise2')

})


console.log('end')

瀏覽器事件循環


全局代碼壓入執行棧執行,輸出 start

setTimeout壓入 macrotask隊列,promise.then 回調放入 microtask隊列,最后執行 console.log('end'),輸出 end

調用棧中的代碼執行完成(全局代碼屬于宏任務),接下來開始執行微任務隊列中的代碼,執行promise回調,輸出 promise1, promise回調函數默認返回 undefined, promise狀態變成 fulfilled ,觸發接下來的 then回調,繼續壓入 microtask隊列,此時產生了新的微任務,會接著把當前的微任務隊列執行完,此時執行第二個 promise.then回調,輸出 promise2

此時,microtask隊列 已清空,接下來會會執行 UI渲染工作(如果有的話),然后開始下一輪 event loop, 執行 setTimeout的回調,輸出 setTimeout

最后的執行結果如下


start

end

promise1

promise2

setTimeout

node環境下的事件循環

和瀏覽器環境有何不同

表現出的狀態與瀏覽器大致相同。不同的是 node 中有一套自己的模型。node 中事件循環的實現依賴 libuv 引擎。Node的事件循環存在幾個階段。


如果是node10及其之前版本,microtask會在事件循環的各個階段之間執行,也就是一個階段執行完畢,就會去執行 microtask隊列中的任務。


node版本更新到11之后,Event Loop運行原理發生了變化,一旦執行一個階段里的一個宏任務(setTimeout,setInterval和setImmediate)就立刻執行微任務隊列,跟瀏覽器趨于一致。下面例子中的代碼是按照的去進行分析的。


事件循環模型

┌───────────────────────┐

┌─>│        timers         │

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

│  │     I/O callbacks     │

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

│  │     idle, prepare     │

│  └──────────┬────────────┘      ┌───────────────┐

│  ┌──────────┴────────────┐      │   incoming:   │

│  │         poll          │<──connections───     │

│  └──────────┬────────────┘      │   data, etc.  │

│  ┌──────────┴────────────┐      └───────────────┘

│  │        check          │

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

└──┤    close callbacks    │

  └───────────────────────┘

事件循環各階段詳解

node中事件循環的順序


外部輸入數據 --> 輪詢階段(poll) --> 檢查階段(check) --> 關閉事件回調階段(close callback) --> 定時器檢查階段(timer) --> I/O 事件回調階段(I/O callbacks) --> 閑置階段(idle, prepare) --> 輪詢階段...


這些階段大致的功能如下:


定時器檢測階段(timers): 這個階段執行定時器隊列中的回調如 setTimeout() 和 setInterval()。

I/O事件回調階段(I/O callbacks): 這個階段執行幾乎所有的回調。但是不包括close事件,定時器和setImmediate()的回調。

閑置階段(idle, prepare): 這個階段僅在內部使用,可以不必理會

輪詢階段(poll): 等待新的I/O事件,node在一些特殊情況下會阻塞在這里。

檢查階段(check): setImmediate()的回調會在這個階段執行。

關閉事件回調階段(close callbacks): 例如socket.on('close', ...)這種close事件的回調

poll:

這個階段是輪詢時間,用于等待還未返回的 I/O 事件,比如服務器的回應、用戶移動鼠標等等。

這個階段的時間會比較長。如果沒有其他異步任務要處理(比如到期的定時器),會一直停留在這個階段,等待 I/O 請求返回結果。

check:

該階段執行setImmediate()的回調函數。


close:

該階段執行關閉請求的回調函數,比如socket.on('close', ...)。


timer階段:

這個是定時器階段,處理setTimeout()和setInterval()的回調函數。進入這個階段后,主線程會檢查一下當前時間,是否滿足定時器的條件。如果滿足就執行回調函數,否則就離開這個階段。


I/O callback階段:

除了以下的回調函數,其他都在這個階段執行:


setTimeout()和setInterval()的回調函數

setImmediate()的回調函數

用于關閉請求的回調函數,比如socket.on('close', ...)

宏任務和微任務

宏任務:


setImmediate

setTimeout

setInterval

script(整體代碼)

I/O 操作等。

微任務:


process.nextTick

new Promise().then(回調)

Promise.nextTick, setTimeout, setImmediate的使用場景和區別

Promise.nextTick

process.nextTick 是一個獨立于 eventLoop 的任務隊列。

在每一個 eventLoop 階段完成后會去檢查 nextTick 隊列,如果里面有任務,會讓這部分任務優先于微任務執行。

是所有異步任務中最快執行的。


setTimeout:

setTimeout()方法是定義一個回調,并且希望這個回調在我們所指定的時間間隔后第一時間去執行。


setImmediate:

setImmediate()方法從意義上將是立刻執行的意思,但是實際上它卻是在一個固定的階段才會執行回調,即poll階段之后。


經典題目分析

一. 下面代碼輸出什么

async function async1() {

   console.log('async1 start');

   await async2();

   console.log('async1 end');

}

async function async2() {

   console.log('async2');

}

console.log('script start');

setTimeout(function() {

   console.log('setTimeout');

}, 0)

async1();

new Promise(function(resolve) {

   console.log('promise1');

   resolve();

}).then(function() {

   console.log('promise2');

});

console.log('script end');

先執行宏任務(當前代碼塊也算是宏任務),然后執行當前宏任務產生的微任務,然后接著執行宏任務


從上往下執行代碼,先執行同步代碼,輸出 script start

遇到setTimeout,現把 setTimeout 的代碼放到宏任務隊列中

執行 async1(),輸出 async1 start, 然后執行 async2(), 輸出 async2,把 async2() 后面的代碼 console.log('async1 end')放到微任務隊列中

接著往下執行,輸出 promise1,把 .then()放到微任務隊列中;注意Promise本身是同步的立即執行函數,.then是異步執行函數

接著往下執行, 輸出 script end。同步代碼(同時也是宏任務)執行完成,接下來開始執行剛才放到微任務中的代碼

依次執行微任務中的代碼,依次輸出 async1 end、 promise2, 微任務中的代碼執行完成后,開始執行宏任務中的代碼,輸出 setTimeout

最后的執行結果如下


script start

async1 start

async2

promise1

script end

async1 end

promise2

setTimeout

二. 下面代碼輸出什么

console.log('start');

setTimeout(() => {

   console.log('children2');

   Promise.resolve().then(() => {

       console.log('children3');

   })

}, 0);


new Promise(function(resolve, reject) {

   console.log('children4');

   setTimeout(function() {

       console.log('children5');

       resolve('children6')

   }, 0)

}).then((res) => {

   console.log('children7');

   setTimeout(() => {

       console.log(res);

   }, 0)

})

這道題跟上面題目不同之處在于,執行代碼會產生很多個宏任務,每個宏任務中又會產生微任務


從上往下執行代碼,先執行同步代碼,輸出 start

遇到setTimeout,先把 setTimeout 的代碼放到宏任務隊列①中

接著往下執行,輸出 children4, 遇到setTimeout,先把 setTimeout 的代碼放到宏任務隊列②中,此時.then并不會被放到微任務隊列中,因為 resolve是放到 setTimeout中執行的

代碼執行完成之后,會查找微任務隊列中的事件,發現并沒有,于是開始執行宏任務①,即第一個 setTimeout, 輸出 children2,此時,會把 Promise.resolve().then放到微任務隊列中。

宏任務①中的代碼執行完成后,會查找微任務隊列,于是輸出 children3;然后開始執行宏任務②,即第二個 setTimeout,輸出 children5,此時將.then放到微任務隊列中。

宏任務②中的代碼執行完成后,會查找微任務隊列,于是輸出 children7,遇到 setTimeout,放到宏任務隊列中。此時微任務執行完成,開始執行宏任務,輸出 children6;

最后的執行結果如下


start

children4

children2

children3

children5

children7

children6

三. 下面代碼輸出什么

const p = function() {

   return new Promise((resolve, reject) => {

       const p1 = new Promise((resolve, reject) => {

           setTimeout(() => {

               resolve(1)

           }, 0)

           resolve(2)

       })

       p1.then((res) => {

           console.log(res);

       })

       console.log(3);

       resolve(4);

   })

}



p().then((res) => {

   console.log(res);

})

console.log('end');

執行代碼,Promise本身是同步的立即執行函數,.then是異步執行函數。遇到setTimeout,先把其放入宏任務隊列中,遇到p1.then會先放到微任務隊列中,接著往下執行,輸出 3

遇到 p().then 會先放到微任務隊列中,接著往下執行,輸出 end

同步代碼塊執行完成后,開始執行微任務隊列中的任務,首先執行 p1.then,輸出 2, 接著執行p().then, 輸出 4

微任務執行完成后,開始執行宏任務,setTimeout, resolve(1),但是此時 p1.then已經執行完成,此時 1不會輸出。

最后的執行結果如下


3

end

2

4

你可以將上述代碼中的 resolve(2)注釋掉, 此時 1才會輸出,輸出結果為 3 end 4 1。


const p = function() {

   return new Promise((resolve, reject) => {

       const p1 = new Promise((resolve, reject) => {

           setTimeout(() => {

               resolve(1)

           }, 0)

       })

       p1.then((res) => {

           console.log(res);

       })

       console.log(3);

       resolve(4);

   })

}



p().then((res) => {

   console.log(res);

})

console.log('end');

3

end

4

1

最后強烈推薦幾個非常好的講解 event loop 的視頻:


What the heck is the event loop anyway? | Philip Roberts | JSConf EU

Jake Archibald: In The Loop - JSConf.Asia

小程序入門到精通:了解小程序開發4個重要文件

前端達人

點擊查看原圖


1. 小程序沒有DOM對象,一切基于組件化

2. 小程序的四個重要的文件

  • *.js —> view邏輯 —> javascript
  • *.wxml —> view結構 ----> html
  • *.wxss —> view樣式 -----> css
  • *. json ----> view 數據 -----> json文件

注意:為了方便開發者減少配置項,描述頁面的四個文件必須具有相同的路徑與文件名。

2.1 WXML

WXML(WeiXin Markup Language)是框架設計的一套標簽語言,結合基礎組件、事件系統,可以構建出頁面的結構。WXML 充當的就是類似 HTML 的角色
要完整了解 WXML 語法,請參考WXML 語法參考。

2.2 WXSS

WXSS (WeiXin Style Sheets)是一套樣式語言,用于描述 WXML 的組件樣式。

WXSS 用來決定 WXML 的組件應該怎么顯示。

為了適應廣大的前端開發者,WXSS 具有 CSS 大部分特性。同時為了更適合開發微信小程序,WXSS 對 CSS 進行了擴充以及修改。

與 CSS 相比,WXSS 擴展的特性有:



尺寸單位

樣式導入

2.3 json

JSON 是一種數據格式,并不是編程語言,在小程序中,JSON扮演的靜態配置的角色。



全局配置

小程序根目錄下的 app.json 文件用來對微信小程序進行全局配置,決定頁面文件的路徑、窗口表現、設置網絡超時時間、設置多 tab 等。



頁面配置

每一個小程序頁面也可以使用同名 .json 文件來對本頁面的窗口表現進行配置,頁面中配置項會覆蓋 app.json 的 window 中相同的配置項。



工具配置 project.config.json

通常大家在使用一個工具的時候,都會針對各自喜好做一些個性化配置,例如界面顏色、編譯配置等等,當你換了另外一臺電腦重新安裝工具的時候,你還要重新配置。

考慮到這點,小程序開發者工具在每個項目的根目錄都會生成一個 project.config.json,你在工具上做的任何配置都會寫入到這個文件,當你重新安裝工具或者換電腦工作時,你只要載入同一個項目的代碼包,開發者工具就自動

注意:

JSON文件都是被包裹在一個大括號中 {},通過key-value的方式來表達數據。JSON的Key必須包裹在一個雙引號中,在實踐中,編寫 JSON 的時候,忘了給 Key 值加雙引號或者是把雙引號寫成單引號是常見錯誤。

JSON的值只能是以下幾種數據格式,其他任何格式都會觸發報錯,例如 JavaScript 中的 undefined。



數字,包含浮點數和整數

字符串,需要包裹在雙引號中

Bool值,true 或者 false

數組,需要包裹在方括號中 []

對象,需要包裹在大括號中 {}

Null

還需要注意的是 JSON 文件中無法使用注釋,試圖添加注釋將會引發報錯。


2.4 js

一個服務僅僅只有界面展示是不夠的,還需要和用戶做交互:響應用戶的點擊、獲取用戶的位置等等。在小程序里邊,我們就通過編寫 JS 腳本文件來處理用戶的操作。


注冊頁面

對于小程序中的每個頁面,都需要在頁面對應的 js 文件中進行注冊,指定頁面的初始數據、生命周期回調、事件處理函數等



使用 Page 構造器注冊頁面

簡單的頁面可以使用 Page() 進行構造。



使用 Component 構造器構造頁面

Page 構造器適用于簡單的頁面。但對于復雜的頁面, Page 構造器可能并不好用。

此時,可以使用 Component 構造器來構造頁面。 Component 構造器的主要區別是:方法需要放在 methods: { } 里面。

————————————————

版權聲明:本文為CSDN博主「前端嵐楓」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。

原文鏈接:https://blog.csdn.net/yilanyoumeng3/java/article/details/106292742





2020年,令人驚嘆的Echarts!

前端達人

點擊查看原圖


1.可視化面板介紹

應對現在數據可視化的趨勢,越來越多企業需要在很多場景(營銷數據,生產數據,用戶數據)下使用,可視化圖表來展示體現數據,讓數據更加直觀,數據特點更加突出。

01-技術要點

  1. div + css 布局
  2. flex 布局
  3. Less
  4. 原生js + jquery 使用
  5. rem適配
  6. echarts基礎

02-案例適配方案

  1. 設計稿是1920px
  2. flexible.js 把屏幕分為 24 等份
  3. cssrem 插件的基準值是 80px
    插件-配置按鈕—配置擴展設置–Root Font Size 里面 設置。
    但是別忘記重啟vscode軟件保證生效


03-頁面主體布局

  1. header布局
  2. mainbox布局
  3. 公共面板模塊 panel
  4. 柱形圖 bar
因為我們今天的主題是echarts部分所以前面的這些,我就為大家寫好框架,里面的布局相信以大家的能力都是分分鐘解決的事情。


2.Echarts(重點)

echarts介紹

常見的數據可視化庫:

D3.js 目前 Web 端評價最高的 Javascript 可視化工具庫(入手難)
ECharts.js 百度出品的一個開源 Javascript 數據可視化庫
Highcharts.js 國外的前端數據可視化庫,非商用免費,被許多國外大公司所使用
AntV 螞蟻金服全新一代數據可視化解決方案 等等
Highcharts 和 Echarts 就像是 Office 和 WPS 的關系

ECharts,一個使用 JavaScript 實現的開源可視化庫,可以流暢的運行在 PC 和移動設備上,兼容當前絕大部分瀏覽器(IE8/9/10/11,Chrome,Firefox,Safari等),底層依賴矢量圖形庫 ZRender,提供直觀,交互豐富,可高度個性化定制的數據可視化圖表。

官網地址:https://www.echartsjs.com/zh/index.html

echarts體驗
下載echarts https://github.com/apache/incubator-echarts/tree/4.5.0

使用步驟(5大步驟):
1.引入echarts 插件文件到html頁面中
2.準備一個具備大小的DOM容器

<div id="main" style="width: 600px;height:400px;"></div>

3.初始化echarts實例對象

var myChart = echarts.init(document.getElementById('main'));

4.指定配置項和數據(option)

var option = {
    xAxis: {
        type: 'category',
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
    },
    yAxis: {
        type: 'value'
    },
    series: [{
        data: [820, 932, 901, 934, 1290, 1330, 1320],
        type: 'line'
    }]
};

5.將配置項設置給echarts實例對象


myChart.setOption(option);  


echarts基礎配置

這是要求同學們知道以下配置每個模塊的主要作用干什么的就可以了

series

系列列表。每個系列通過 type 決定自己的圖表類型

大白話:圖標數據,指定什么類型的圖標,可以多個圖表重疊。

xAxis:直角坐標系 grid 中的 x 軸

boundaryGap: 坐標軸兩邊留白策略 true,這時候刻度只是作為分隔線,標簽和數據點都會在兩個刻度之間的帶(band)中間。

yAxis:直角坐標系 grid 中的 y 軸

grid:直角坐標系內繪圖網格。

title:標題組件

tooltip:提示框組件

legend:圖例組件

color:調色盤顏色列表

數據堆疊,同個類目軸上系列配置相同的stack值后 后一個系列的值會在前一個系列的值上相加。



option = {

    // color設置我們線條的顏色 注意后面是個數組

    color: ['pink', 'red', 'green', 'skyblue'],

    // 設置圖表的標題

    title: {

        text: '折線圖堆疊123'

    },

    // 圖表的提示框組件 

    tooltip: {

        // 觸發方式

        trigger: 'axis'

    },

    // 圖例組件

    legend: {

       // series里面有了 name值則 legend里面的data可以刪掉

    },

    // 網格配置  grid可以控制線形圖 柱狀圖 圖表大小

    grid: {

        left: '3%',

        right: '4%',

        bottom: '3%',

        // 是否顯示刻度標簽 如果是true 就顯示 否則反之

        containLabel: true

    },

    // 工具箱組件  可以另存為圖片等功能

    toolbox: {

        feature: {

            saveAsImage: {}

        }

    },

    // 設置x軸的相關配置

    xAxis: {

        type: 'category',

        // 是否讓我們的線條和坐標軸有縫隙

        boundaryGap: false,

        data: ['星期一', '周二', '周三', '周四', '周五', '周六', '周日']

    },

     // 設置y軸的相關配置

    yAxis: {

        type: 'value'

    },

    // 系列圖表配置 它決定著顯示那種類型的圖表

    series: [

        {

            name: '郵件營銷',

            type: 'line',

           

            data: [120, 132, 101, 134, 90, 230, 210]

        },

        {

            name: '聯盟廣告',

            type: 'line',



            data: [220, 182, 191, 234, 290, 330, 310]

        },

        {

            name: '視頻廣告',

            type: 'line',

          

            data: [150, 232, 201, 154, 190, 330, 410]

        },

        {

            name: '直接訪問',

            type: 'line',

          

            data: [320, 332, 301, 334, 390, 330, 320]

        }

    ]

};



3.Echarts快速使用

1.官網實例

點擊查看原圖



官網默認為我們提供了大量的案例,我們需要使用那一種只需要直接點擊打開后復制他們的相關配置,然后按照我上面說的5大步驟進行使用即可。

2.社區Gallery

點擊查看原圖



官方自帶的圖例,可能在很多時候并不能滿足我們的需要,在社區這里可以找到一些基于echart的高度定制好的圖表,相當于基于jquery開發的插件,這里是基于echarts開發的第三方的圖表。

本案例中使用了地圖模擬飛行的案例就是從社區中進行引用的,
參考社區的例子:https://gallery.echartsjs.com/editor.html?c=x0-ExSkZDM (模擬飛機航線)
實現步驟:

第一需要下載china.js提供中國地圖的js文件
第二個因為里面代碼比較多,我們新建一個新的js文件 myMap.js 引入
使用社區提供的配置即可。
代碼已經上傳至我的碼云如有需要的小伙伴可自行下載:
https://gitee.com/jiuyueqi/echarts

ps:最后呢,如果大家看完樓主的文章覺得對echarts的學習和了解有所幫助,麻煩大家路過點個贊點個關注唄!樓主后續還會繼續更新有關前端方面的面試題資料或者其他方面的知識。
————————————————
版權聲明:本文為CSDN博主「程序猿玖月柒」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_45257157/java/article/details/106300587

關于JavaScript獲取時間函數及實現倒計時

前端達人

JavaScript數組對象的迭代方法詳解

上一篇博客講到了數組的方法,當然里邊比較復雜的就是數組的迭代方法,因為涉及到了回調函數,所以這篇博客我們來詳細講解一下js數組迭代方法的使用。


1.forEach(funcrion(value,index,arr){}):對數組的每一項運行給定函數,這個方法不進行返回,所以一般用于讓數組循環執行某方法。

  var arr=[1,2,3,4,5,6];

    arr.forEach(function(val,index,arr){

        console.log(val,index,arr);

    })

    // 其中:

    // value:每一個數組項的值 必填項

    // index:每一個數組項對應的索引

    // arr:當前的數組


注意:forEach()方法不返回值,所以回調函數中使用return會打印出來undefined。

2.map(funcrion(value,index,arr){}):對數組的每一項運行給定函數,它將返回執行函數后的結果組成的新數組。

 var aNum2 = [1.2, 1.8, 2.0, 4.3];

    console.log(aNum2.map(Math.floor()));// [1,1,2,4]

    

    var arr=[1,2,3];

    console.log(arr.map(function(val,index){

        return val*3

    }));// 3 6 9

    // 其中:

    // value:每一個數組項的值 必填項

    // index:每一個數組項對應的索引

    // arr:當前的數組

注意:map()方法有返回值,返回值為新的數組,所以可以直接再回調函數中return。

3.every(funcrion(value,index,arr){}):對數組的每一項都運行給定函數,進項判斷,若對于每項執行函數都返回了true,則其結果為true。

 var arr=[10,20,30];

    console.log(arr.every(function(val){

        return val>20;

    }));// false

    

    console.log(arr.every(function(val){

        return val>0;

    }));// true

    

    // 其中:

    // value:每一個數組項的值 必填項

    // index:每一個數組項對應的索引

    // arr:當前的數組



注意:every()方法所有的數組項都符合判斷時返回true,否則返回false。

4.some(funcrion(value,index,arr){}):對數組的每一項都運行給定函數,進行判斷,若存在一項符合條件的數組項,則其結果為true。

    var arr=[10,20,30];

    console.log(arr.some(function(val){

        return val>20;

    }));// true

    

    console.log(arr.some(function(val){

        return val>0;

    }));// true

    

    console.log(arr.some(function(val){

        return val<0;

    }));// false

    

    arr.some(function(val){

        console.log(val<0);

    });//fasle false false

    // 其中:

    // value:每一個數組項的值 必填項

    // index:每一個數組項對應的索引

    // arr:當前的數組


注意:some()方法如果回調函數執行完會根據結果返回true或false,但是回調函數中打印判斷是,只會作為判斷條件的返回值,則會打印多遍。

5.fliter(funcrion(value,index,arr){}):對數組的每一項都運行給定函數,進行過濾,將符合條件的數組項添加到新的數組中,并返回新的數組。

   var aNum=[1,2,3,4];
    console.log(aNum.filter(function (num) {
        return num > 1;
    }));//[2,3,4,]
    aNum.filter(function (num) {
        console.log(num > 1);//true true true
    })

注意:filter()方法對數組項進行過濾,然后將符合條件的數組項添加到一個新的數組并返回,但是如果直接打印這個判斷條件,相當于打印的判斷條件的結果,只會返回true或者false。

6.ES6中新增的迭代方法

1.find():返回第一個符合傳入測試(函數)條件的數組元素。


  var aNum=[10,20,30,40];

    console.log(aNum.find(function (num) {

        return num > 19;

    }));//1

    console.log(aNum.find(function (num) {

        return num < 0;

    }));//undefined



2.findIndex():返回符合傳入測試(函數)條件的數組元素索引。


console.log(aNum.findIndex(function (num) { return num > 19; }));//3


3.includes():判斷一個數組是否包含一個指定的值。

總結:

forEach()與map()是一對,用于數組遍歷執行指定函數,前者不返回數組,后者返回 處理過的新數組。
every()與some()是一對,分別適用于檢測數組是否全部滿足某條件或者存在滿足的數組項,返回true或false。
filter()則是相當于過濾器的存在,過濾掉數組中不符合條件的數據,將符合條件的數組項添加到新數組,并返回。
————————————————
版權聲明:本文為CSDN博主「Mr_Han119」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_39155611/java/article/details/106294417


了不起的 tsconfig.json 指南

seo達人

在 TypeScript 開發中,tsconfig.json 是個不可或缺的配置文件,它是我們在 TS 項目中最常見的配置文件,那么你真的了解這個文件嗎?它里面都有哪些優秀配置?如何配置一個合理的 tsconfig.json 文件?本文將全面帶大家一起詳細了解 tsconfig.json 的各項配置。



本文將從以下幾個方面全面介紹 tsconfig.json 文件:

了不起的 tsconfig.json 指南.png




水平有限,歡迎各位大佬指點~~


一、tsconfig.json 簡介


1. 什么是 tsconfig.json

TypeScript 使用 tsconfig.json 文件作為其配置文件,當一個目錄中存在 tsconfig.json 文件,則認為該目錄為 TypeScript 項目的根目錄。

通常 tsconfig.json 文件主要包含兩部分內容:指定待編譯文件和定義編譯選項。



從《TypeScript編譯器的配置文件的JSON模式》可知,目前 tsconfig.json 文件有以下幾個頂層屬性:


compileOnSave

compilerOptions

exclude

extends

files

include

references

typeAcquisition


文章后面會詳細介紹一些常用屬性配置。



2. 為什么使用 tsconfig.json

通常我們可以使用 tsc 命令來編譯少量 TypeScript 文件:


/*

 參數介紹:

 --outFile // 編譯后生成的文件名稱

 --target  // 指定ECMAScript目標版本

 --module  // 指定生成哪個模塊系統代碼

 index.ts  // 源文件

*/

$ tsc --outFile leo.js --target es3 --module amd index.ts

但如果實際開發的項目,很少是只有單個文件,當我們需要編譯整個項目時,就可以使用 tsconfig.json 文件,將需要使用到的配置都寫進 tsconfig.json 文件,這樣就不用每次編譯都手動輸入配置,另外也方便團隊協作開發。



二、使用 tsconfig.json

目前使用 tsconfig.json 有2種操作:


1. 初始化 tsconfig.json

在初始化操作,也有 2 種方式:


手動在項目根目錄(或其他)創建 tsconfig.json 文件并填寫配置;

通過 tsc --init 初始化 tsconfig.json 文件。


2. 指定需要編譯的目錄

在不指定輸入文件的情況下執行 tsc 命令,默認從當前目錄開始編譯,編譯所有 .ts 文件,并且從當前目錄開始查找 tsconfig.json 文件,并逐級向上級目錄搜索。


$ tsc

另外也可以為 tsc 命令指定參數 --project 或 -p 指定需要編譯的目錄,該目錄需要包含一個 tsconfig.json 文件,如:


/*

 文件目錄:

 ├─src/

 │  ├─index.ts

 │  └─tsconfig.json

 ├─package.json

*/

$ tsc --project src

注意,tsc 的命令行選項具有優先級,會覆蓋 tsconfig.json 中的同名選項。



更多 tsc 編譯選項,可查看《編譯選項》章節。



三、使用示例

這個章節,我們將通過本地一個小項目 learnTsconfig 來學著實現一個簡單配置。

當前開發環境:windows / node 10.15.1 / TypeScript3.9



1. 初始化 learnTsconfig 項目

執行下面命令:


$ mkdir learnTsconfig

$ cd .\learnTsconfig\

$ mkdir src

$ new-item index.ts

并且我們為 index.ts 文件寫一些簡單代碼:


// 返回當前版本號

function getVersion(version:string = "1.0.0"): string{

   return version;

}


console.log(getVersion("1.0.1"))

我們將獲得這么一個目錄結構:


 └─src/

    └─index.ts


2. 初始化 tsconfig.json 文件

在 learnTsconfig 根目錄執行:


$ tsc --init


3. 修改 tsconfig.json 文件

我們設置幾個常見配置項:


{

 "compilerOptions": {

   "target": "ES5",             // 目標語言的版本

   "module": "commonjs",        // 指定生成代碼的模板標準

   "noImplicitAny": true,       // 不允許隱式的 any 類型

   "removeComments": true,      // 刪除注釋

   "preserveConstEnums": true,  // 保留 const 和 enum 聲明

   "sourceMap": true            // 生成目標文件的sourceMap文件

 },

 "files": [   // 指定待編譯文件

   "./src/index.ts"  

 ]

}

其中需要注意一點:

files 配置項值是一個數組,用來指定了待編譯文件,即入口文件。

當入口文件依賴其他文件時,不需要將被依賴文件也指定到 files 中,因為編譯器會自動將所有的依賴文件歸納為編譯對象,即 index.ts 依賴 user.ts 時,不需要在 files 中指定 user.ts , user.ts 會自動納入待編譯文件。



4. 執行編譯

配置完成后,我們可以在命令行執行 tsc 命令,執行編譯完成后,我們可以得到一個 index.js 文件和一個 index.js.map 文件,證明我們編譯成功,其中 index.js 文件內容如下:


function getVersion(version) {

   if (version === void 0) { version = "1.0.0"; }

   return version;

}

console.log(getVersion("1.0.1"));

//# sourceMappingURL=index.js.map

可以看出,tsconfig.json 中的 removeComments 配置生效了,將我們添加的注釋代碼移除了。



到這一步,就完成了這個簡單的示例,接下來會基于這個示例代碼,講解《七、常見配置示例》。



四、tsconfig.json 文件結構介紹


1. 按頂層屬性分類

在 tsconfig.json 文件中按照頂層屬性,分為以下幾類:

tsconfig.json 文件結構(頂層屬性).png


了不起的 tsconfig.json 指南.png



2. 按功能分類

tsconfig.json 文件結構(功能).png




五、tsconfig.json 配置介紹


1. compileOnSave

compileOnSave 屬性作用是設置保存文件的時候自動編譯,但需要編譯器支持。


{

   // ...

 "compileOnSave": false,

}


2. compilerOptions

compilerOptions 屬性作用是配置編譯選項。

若 compilerOptions 屬性被忽略,則編譯器會使用默認值,可以查看《官方完整的編譯選項列表》。

編譯選項配置非常繁雜,有很多配置,這里只列出常用的配置。


{

 // ...

 "compilerOptions": {

   "incremental": true, // TS編譯器在第一次編譯之后會生成一個存儲編譯信息的文件,第二次編譯會在第一次的基礎上進行增量編譯,可以提高編譯的速度

   "tsBuildInfoFile": "./buildFile", // 增量編譯文件的存儲位置

   "diagnostics": true, // 打印診斷信息

   "target": "ES5", // 目標語言的版本

   "module": "CommonJS", // 生成代碼的模板標準

   "outFile": "./app.js", // 將多個相互依賴的文件生成一個文件,可以用在AMD模塊中,即開啟時應設置"module": "AMD",

   "lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的庫,即聲明文件,es5 默認引用dom、es5、scripthost,如需要使用es的高級版本特性,通常都需要配置,如es8的數組新特性需要引入"ES2019.Array",

   "allowJS": true, // 允許編譯器編譯JS,JSX文件

   "checkJs": true, // 允許在JS文件中報錯,通常與allowJS一起使用

   "outDir": "./dist", // 指定輸出目錄

   "rootDir": "./", // 指定輸出文件目錄(用于輸出),用于控制輸出目錄結構

   "declaration": true, // 生成聲明文件,開啟后會自動生成聲明文件

   "declarationDir": "./file", // 指定生成聲明文件存放目錄

   "emitDeclarationOnly": true, // 只生成聲明文件,而不會生成js文件

   "sourceMap": true, // 生成目標文件的sourceMap文件

   "inlineSourceMap": true, // 生成目標文件的inline SourceMap,inline SourceMap會包含在生成的js文件中

   "declarationMap": true, // 為聲明文件生成sourceMap

   "typeRoots": [], // 聲明文件目錄,默認時node_modules/@types

   "types": [], // 加載的聲明文件包

   "removeComments":true, // 刪除注釋

   "noEmit": true, // 不輸出文件,即編譯后不會生成任何js文件

   "noEmitOnError": true, // 發送錯誤時不輸出任何文件

   "noEmitHelpers": true, // 不生成helper函數,減小體積,需要額外安裝,常配合importHelpers一起使用

   "importHelpers": true, // 通過tslib引入helper函數,文件必須是模塊

   "downlevelIteration": true, // 降級遍歷器實現,如果目標源是es3/5,那么遍歷器會有降級的實現

   "strict": true, // 開啟所有嚴格的類型檢查

   "alwaysStrict": true, // 在代碼中注入'use strict'

   "noImplicitAny": true, // 不允許隱式的any類型

   "strictNullChecks": true, // 不允許把null、undefined賦值給其他類型的變量

   "strictFunctionTypes": true, // 不允許函數參數雙向協變

   "strictPropertyInitialization": true, // 類的實例屬性必須初始化

   "strictBindCallApply": true, // 嚴格的bind/call/apply檢查

   "noImplicitThis": true, // 不允許this有隱式的any類型

   "noUnusedLocals": true, // 檢查只聲明、未使用的局部變量(只提示不報錯)

   "noUnusedParameters": true, // 檢查未使用的函數參數(只提示不報錯)

   "noFallthroughCasesInSwitch": true, // 防止switch語句貫穿(即如果沒有break語句后面不會執行)

   "noImplicitReturns": true, //每個分支都會有返回值

   "esModuleInterop": true, // 允許export=導出,由import from 導入

   "allowUmdGlobalAccess": true, // 允許在模塊中全局變量的方式訪問umd模塊

   "moduleResolution": "node", // 模塊解析策略,ts默認用node的解析策略,即相對的方式導入

   "baseUrl": "./", // 解析非相對模塊的基地址,默認是當前目錄

   "paths": { // 路徑映射,相對于baseUrl

     // 如使用jq時不想使用默認版本,而需要手動指定版本,可進行如下配置

     "jquery": ["node_modules/jquery/dist/jquery.min.js"]

   },

   "rootDirs": ["src","out"], // 將多個目錄放在一個虛擬目錄下,用于運行時,即編譯后引入文件的位置可能發生變化,這也設置可以虛擬src和out在同一個目錄下,不用再去改變路徑也不會報錯

   "listEmittedFiles": true, // 打印輸出文件

   "listFiles": true// 打印編譯的文件(包括引用的聲明文件)

 }

}


3. exclude

exclude 屬性作用是指定編譯器需要排除的文件或文件夾。

默認排除 node_modules 文件夾下文件。


{

   // ...

 "exclude": [

   "src/lib" // 排除src目錄下的lib文件夾下的文件不會編譯

 ]

}

和 include 屬性一樣,支持 glob 通配符:


* 匹配0或多個字符(不包括目錄分隔符)

? 匹配一個任意字符(不包括目錄分隔符)

**/ 遞歸匹配任意子目錄


4. extends

extends 屬性作用是引入其他配置文件,繼承配置。

默認包含當前目錄和子目錄下所有 TypeScript 文件。


{

   // ...

 // 把基礎配置抽離成tsconfig.base.json文件,然后引入

   "extends": "./tsconfig.base.json"

}


5. files

files 屬性作用是指定需要編譯的單個文件列表。

默認包含當前目錄和子目錄下所有 TypeScript 文件。


{

   // ...

 "files": [

   // 指定編譯文件是src目錄下的leo.ts文件

   "scr/leo.ts"

 ]

}


6. include

include 屬性作用是指定編譯需要編譯的文件或目錄。


{

   // ...

 "include": [

   // "scr" // 會編譯src目錄下的所有文件,包括子目錄

   // "scr/*" // 只會編譯scr一級目錄下的文件

   "scr/*/*" // 只會編譯scr二級目錄下的文件

 ]

}


7. references

references 屬性作用是指定工程引用依賴。

在項目開發中,有時候我們為了方便將前端項目和后端node項目放在同一個目錄下開發,兩個項目依賴同一個配置文件和通用文件,但我們希望前后端項目進行靈活的分別打包,那么我們可以進行如下配置:


{

   // ...

 "references": [ // 指定依賴的工程

    {"path": "./common"}

 ]

}


8. typeAcquisition

typeAcquisition 屬性作用是設置自動引入庫類型定義文件(.d.ts)相關。

包含 3 個子屬性:


enable  : 布爾類型,是否開啟自動引入庫類型定義文件(.d.ts),默認為 false;

include  : 數組類型,允許自動引入的庫名,如:["jquery", "lodash"];

exculde  : 數組類型,排除的庫名。

{

   // ...

 "typeAcquisition": {

   "enable": false,

   "exclude": ["jquery"],

   "include": ["jest"]

 }

}


六、常見配置示例

本部分內容中,我們找了幾個實際開發中比較常見的配置,當然,還有很多配置需要自己摸索喲~~



1. 移除代碼中注釋

tsconfig.json:


{

 "compilerOptions": {

   "removeComments": true,

 }

}

編譯前:


// 返回當前版本號

function getVersion(version:string = "1.0.0"): string{

   return version;

}

console.log(getVersion("1.0.1"))

編譯結果:


function getVersion(version) {

   if (version === void 0) { version = "1.0.0"; }

   return version;

}

console.log(getVersion("1.0.1"));


2. 開啟null、undefined檢測

tsconfig.json:


{

   "compilerOptions": {

       "strictNullChecks": true

   },

}

修改 index.ts 文件內容:


const leo;

leo = new Pingan('leo','hello');


這時候編輯器也會提示錯誤信息,執行 tsc 后,控制臺報錯:


src/index.ts:9:11 - error TS2304: Cannot find name 'Pingan'.


9 leo = new Pingan('leo','hello');


Found 1 error.


3. 配置復用

通過 extends 屬性實現配置復用,即一個配置文件可以繼承另一個文件的配置屬性。

比如,建立一個基礎的配置文件 configs/base.json :


{

 "compilerOptions": {

   "noImplicitAny": true,

   "strictNullChecks": true

 }

}

在tsconfig.json 就可以引用這個文件的配置了:


{

 "extends": "./configs/base",

 "files": [

   "main.ts",

   "supplemental.ts"

 ]

}


4. 生成枚舉的映射代碼

在默認情況下,使用 const 修飾符后,枚舉不會生成映射代碼。

如下,我們可以看出:使用 const 修飾符后,編譯器不會生成任何 RequestMethod 枚舉的任何映射代碼,在其他地方使用時,內聯每個成員的值,節省很大開銷。


const enum RequestMethod {

 Get,

 Post,

 Put,

 Delete

}


let methods = [

 RequestMethod.Get,

 RequestMethod.Post

]

編譯結果:


"use strict";

let methods = [

   0 /* Get */,

   1 /* Post */

];

當然,我們希望生成映射代碼時,也可以設置 tsconfig.json 中的配置,設置 preserveConstEnums 編譯器選項為 true :


{

 "compilerOptions": {

   "target": "es5",

   "preserveConstEnums": true

 }

}


最后編譯結果變成:


"use strict";

var RequestMethod;

(function (RequestMethod) {

   RequestMethod[RequestMethod["Get"] = 0] = "Get";

   RequestMethod[RequestMethod["Post"] = 1] = "Post";

   RequestMethod[RequestMethod["Put"] = 2] = "Put";

   RequestMethod[RequestMethod["Delete"] = 3] = "Delete";

})(RequestMethod || (RequestMethod = {}));

let methods = [

   0 /* Get */,

   1 /* Post */

];


5. 關閉 this 類型注解提示

通過下面代碼編譯后會報錯:


const button = document.querySelector("button");

button?.addEventListener("click", handleClick);

function handleClick(this) {

console.log("Clicked!");

this.removeEventListener("click", handleClick);

}


報錯內容:


src/index.ts:10:22 - error TS7006: Parameter 'this' implicitly has an 'any' type.

10 function handleClick(this) {

Found 1 error.


這是因為 this 隱式具有 any 類型,如果沒有指定類型注解,編譯器會提示“"this" 隱式具有類型 "any",因為它沒有類型注釋?!?。



解決方法有2種:


指定 this 類型,如本代碼中為 HTMLElement 類型:

HTMLElement 接口表示所有的 HTML 元素。一些HTML元素直接實現了 HTMLElement 接口,其它的間接實現HTMLElement接口。

關于 HTMLElement 可查看詳細。


使用 --noImplicitThis 配置項:


在 TS2.0 還增加一個新的編譯選項: --noImplicitThis,表示當 this 表達式值為 any 類型時生成一個錯誤信息。我們設置為 true 后就能正常編譯。


{

 "compilerOptions": {

   "noImplicitThis": true

 }

}


七、Webpack/React 中使用示例


1. 配置編譯 ES6 代碼,JSX 文件

創建測試項目 webpack-demo,結構如下:


webpack-demo/

 |- package.json

 |- tsconfig.json

 |- webpack.config.js

 |- /dist

   |- bundle.js

   |- index.html

 |- /src

   |- index.js

   |- index.ts

 |- /node_modules

安裝 TypeScript 和 ts-loader:


$ npm install --save-dev typescript ts-loader

配置 tsconfig.json,支持 JSX,并將 TypeScript 編譯為 ES5:


{

 "compilerOptions": {

   "outDir": "./dist/",

   "noImplicitAny": true,

+   "module": "es6",

+   "target": "es5",

+   "jsx": "react",

   "allowJs": true

 }

}

還需要配置 webpack.config.js,使其能夠處理 TypeScript 代碼,這里主要在 rules 中添加 ts-loader :


const path = require('path');


module.exports = {

 entry: './src/index.ts',

 module: {

   rules: [

     {

       test: /\.tsx?$/,

       use: 'ts-loader',

       exclude: /node_modules/

     }

   ]

 },

 resolve: {

   extensions: [ '.tsx', '.ts', '.js' ]

 },

 output: {

   filename: 'bundle.js',

   path: path.resolve(__dirname, 'dist')

 }

};


2. 配置 source map

想要啟用 source map,我們必須配置 TypeScript,以將內聯的 source map 輸出到編譯后的 JavaScript 文件中。

只需要在 tsconfig.json 中配置 sourceMap 屬性:


 {

   "compilerOptions": {

     "outDir": "./dist/",

+     "sourceMap": true,

     "noImplicitAny": true,

     "module": "commonjs",

     "target": "es5",

     "jsx": "react",

     "allowJs": true

   }

 }

然后配置 webpack.config.js 文件,讓 webpack 提取 source map,并內聯到最終的 bundle 中:


 const path = require('path');


 module.exports = {

   entry: './src/index.ts',

+   devtool: 'inline-source-map',

   module: {

     rules: [

       {

         test: /\.tsx?$/,

         use: 'ts-loader',

         exclude: /node_modules/

       }

     ]

   },

   resolve: {

     extensions: [ '.tsx', '.ts', '.js' ]

   },

   output: {

     filename: 'bundle.js',

     path: path.resolve(__dirname, 'dist')

   }

 };


八、總結

本文較全面介紹了 tsconfig.json 文件的知識,從“什么是 tsconfig.js 文件”開始,一步步帶領大家全面認識 tsconfig.json 文件。

文中通過一個簡單 learnTsconfig 項目,讓大家知道項目中如何使用 tsconfig.json 文件。在后續文章中,我們將這么多的配置項進行分類學習。最后通過幾個常見配置示例,解決我們開發中遇到的幾個常見問題。

vue.js路由與vuex數據模型設計

seo達人

路由設計

本則路由考慮驗證進入登錄頁面,完成登錄操作進入首頁。


import Vue from "vue";

import Router from "vue-router";

Vue.use(Router);


import store from "@/store/store";


// (延遲加載)

const Login = () => import("@/views/login");

const Home = () => import("@/views/home");


const HomeRoute = {

 path: "/",

 name: "首頁",

 component: Home

};


export { HomeRoute };


const router = new Router({

 base: process.env.BASE_URL,

 routes: [

   {

     path: "/login",

     name: "登錄",

     component: Login

   },

   HomeRoute

 ]

});


router.beforeEach((to, from, next) => {

 let loginName = store.state.user.loginName;

 if (to.path === "/" && loginName == "") {

   next("/login");

 } else {

   next();

 }

});


export default router;

數據模型

const state = {

 loginName: ""

};

const mutations = {

 SET_LOGINNAME(state, loginName) {

   state.loginName = loginName;

 }

};

const actions = {

 login({ commit }, userInfo) {

   return new Promise((res, ret) => {

     commit("SET_LOGINNAME", userInfo);

     res();

   });

 },

 logout({ commit }) {

   return new Promise((res, ret) => {

     commit("SET_LOGINNAME", "");

     res();

   });

 }

};

export default {

 namespaced: true,

 state,

 mutations,

 actions

};

import Vue from "vue";

import Vuex from "vuex";

Vue.use(Vuex);


import user from "./modules/user";


const store = new Vuex.Store({

 modules: {

   user

 }

});


export default store;

組件

<div class="modify">

 <input

   type="text"

   @keydown.enter.prevent="handleKeydown"

   v-model="currentVal"

   placeholder="使用enter鍵切換頻道"

 />

 <button @click="reset" style="margin-left:5px;outline:none;cursor:pointer;">復位</button>

</div>

import { mapState, mapMutations, mapActions } from "vuex";

export default {

 name: "login",

 data() {

   return {

     currentVal: "",

     list: ["咨詢服務", "音悅臺", "體育臺", "財經頻道", "時尚資訊"],

     index: 0

   };

 },

 computed: {

   ...mapState({

     loginName: state => state.user.loginName

   })

 },

 methods: {

   ...mapActions({

     login: "user/login"

   }),

   handleToHome() {

     let userInfo = "user";

     this.login(userInfo);

     this.$router.push({

       path: "/"

     });

   },

RN和React路由詳解及對比

seo達人

前言

在平時H5或者RN開發時,我們業務場景中大部分都不是單頁面的需求,那這時我們就能使用路由在進行多頁面的切換。下面會對比一下react路由和RN路由的本質區別和使用方法。


路由(routing)是指分組從源到目的地時,決定端到端路徑的網絡范圍的進程

React路由

簡介

使用React構建的單頁面應用,要想實現頁面間的跳轉,首先想到的就是使用路由。在React中,常用的有兩個包可以實現這個需求,那就是react-router和react-router-dom。本文主要針對react-router-dom進行說明


在根組件上配置路由,引用react-router-dom結構{ HashRouter as Router, Route ,Link ,Redirect ,Switch },HashRouter組件是路由的根組件,定義屬性和方法傳遞給子組件。Router組件進行路由,指定每個路由跳轉到相應的組件。Link組件指定跳轉鏈接。Redirect組件路由重定向,不管什么情況下,都會跳轉當前指定的路徑,和switch組件聯合起來一起調用,當路徑匹配到路由,不在往下匹配


兩類路由

HashRouter:利用監聽hash變化(有一個事件hashchange)實現路由切換,它是路由容器,

渲染子組件,并向下層子組件傳遞(Context上下文傳遞)loaction,history等路由信息


BrowserHistory:利用H5Api實現路由切換,是路由容器,渲染子組件,

并向子組件傳遞loaction,history等路由信息

路由配置

image-20200601110809995


路由實現原理

HashRouter只是一個容器,本身并沒有DOM結構

它渲染的就是它的子組件,并向下層傳遞location

組件掛載完成之后根據hash改變pathname的值,如果沒有hash值就默認展示根組件

需要跳轉路由頁面時我們使用link或者push去賦值hash的pathname 如this.props.history.push({ pathname: preview, param: { pic, index } });

當hash值發生變化的時候會通過hashchange捕獲變化,并給pathname重新賦值

拿到上下文中傳過來的location,然后取出pathname。再對它的子組件進行遍歷,如果子組件的path屬性和當前上下文中傳過來的pathname屬性相匹配就進行渲染,若不匹配就返回null。

總結

React路由是實質就是,根據遍歷識別路由的pathname,來切換router路由容器中component組件的加載渲染。每次更改pathname就都是組件的重新渲染流程,頁面也都會呈現出刷新的效果。


RN路由

簡介

RN把導航和路由都集中到了react-navigation庫里面

組件使用堆棧式的頁面導航來實現各個頁面跳轉

構造函數:StackNavigator(RouteConfigs, StackNavigatorConfig)

RouteConfigs:頁面路由配置

StackNavigatorConfig:路由參數配置

路由配置

image-20200601111333107


參數詳解

navigationOptions:配置StackNavigator的一些屬性。


   title:標題,如果設置了這個導航欄和標簽欄的title就會變成一樣的,不推薦使用

   header:可以設置一些導航的屬性,如果隱藏頂部導航欄只要將這個屬性設置為null

   headerTitle:設置導航欄標題,推薦

   headerBackTitle:設置跳轉頁面左側返回箭頭后面的文字,默認是上一個頁面的標題。可以自定義,也可以設置為null

   headerTruncatedBackTitle:設置當上個頁面標題不符合返回箭頭后的文字時,默認改成"返回"

   headerRight:設置導航條右側??梢允前粹o或者其他視圖控件

   headerLeft:設置導航條左側??梢允前粹o或者其他視圖控件

   headerStyle:設置導航條的樣式。背景色,寬高等

   headerTitleStyle:設置導航欄文字樣式

   headerBackTitleStyle:設置導航欄‘返回’文字樣式

   headerTintColor:設置導航欄顏色

   headerPressColorAndroid:安卓獨有的設置顏色紋理,需要安卓版本大于5.0

   gesturesEnabled:是否支持滑動返回手勢,iOS默認支持,安卓默認關閉



screen:對應界面名稱,需要填入import之后的頁面


mode:定義跳轉風格


  card:使用iOS和安卓默認的風格


  modal:iOS獨有的使屏幕從底部畫出。類似iOS的present效果


headerMode:返回上級頁面時動畫效果


  float:iOS默認的效果


  screen:滑動過程中,整個頁面都會返回


  none:無動畫


cardStyle:自定義設置跳轉效果


  transitionConfig: 自定義設置滑動返回的配置


  onTransitionStart:當轉換動畫即將開始時被調用的功能


  onTransitionEnd:當轉換動畫完成,將被調用的功能


path:路由中設置的路徑的覆蓋映射配置


initialRouteName:設置默認的頁面組件,必須是上面已注冊的頁面組件


initialRouteParams:初始路由參數

路由首頁

react:


image-20200601111638902


在react中初始化時沒有指定hash值,route會匹配路由表里面的根組件”/”


RN:


image-20200601111722749


RN 需要在StackNavigatorConfig里面指定首頁


RN路由使用

image-20200601112012191


在入口路由列表注冊完成之后 在導航器中的每一個頁面,都有 navigation 屬性 通過提供的navigate方法來提供跳轉


navigation

在導航器中每一個頁面都有navigation屬性,該屬性有以下幾個屬性/方法

navigate 跳轉到其他頁面 常用參數如下

routeName 導航器中配置的路由名稱

params 傳遞到下一個頁面的參數

state:state 里面包含有傳遞過來的參數 params 、 key 、路由名稱 routeName

setParams 更改當前頁面路由參數(后面詳細介紹)

goBack: 回退可穿參數

navigate



setParams




日歷

鏈接

個人資料

藍藍設計的小編 http://m.skdbbs.com

存檔

92国产精品视频_亚洲a级在线观看_国产精品电影观看_国产精品免费观看在线_精品伊人久久97_亚洲人成在线观_尤物九九久久国产精品的特点_成人激情在线播放_成人黄色大片在线免费观看_亚洲成人精品久久久_久久免费视频在线观看_久久精品国产一区_国产一区二区三区18_亚洲欧美中文字幕在线一区_日韩美女中文字幕_日韩视频免费在线
在线观看日韩av| 国产免费a∨片在线观看不卡| 欧美成人中文字幕| 亚洲国产欧美在线人成| 日韩欧美123| 一区二区理论电影在线观看| 巨乳诱惑日韩免费av| 日韩欧美一区二区在线视频| 亚洲精品高清视频| 精品国产乱码久久久久久虫虫漫画| 黄页网站大全在线免费观看| 一区二区三区四区在线免费观看| 国产精品久久久久久久久久小说| 亚洲综合久久av| 欧美体内she精视频在线观看| 日本丶国产丶欧美色综合| 国产成人精品在线视频| 2022国产精品视频| 97精品欧美一区二区三区| 乱馆动漫1~6集在线观看| 黑人巨大精品欧美一区免费视频| 97久久人人超碰| av天在线观看| 精品国内自产拍在线观看视频| 欧美激情一区二区三区在线| 欧美日韩福利视频| 色综合天天综合| 色噜噜亚洲精品中文字幕| 日韩欧美电影一区| 亚洲成人第一| 国产精品一色哟哟哟| 美女视频一区在线观看| 日本在线不卡视频| 成人嫩草影院| 久久免费国产视频| 国产欧美一二三区| 国产日韩一区二区三免费高清| 丝袜国产日韩另类美女| 日本久久精品电影| 国产日韩欧美在线观看| 欧美精品中文字幕一区| 日韩一区和二区| 日韩在线激情| 激情欧美丁香| 九九九九久久久久| 国产精品一二三区在线| 精品久久久久久久久久岛国gif| 亚洲久久在线| 国产98色在线|日韩| 欧美精品视频www在线观看| 久久久久久久91| 久久成人综合| 久久91导航| 国产精品91免费在线| 亚洲精品女av网站| 成人午夜高潮视频| 日韩高清不卡一区二区| www在线观看播放免费视频日本| 欧美—级在线免费片| 午夜电影网亚洲视频| 欧美制服丝袜第一页| 国精产品一区一区三区mba桃花| 91国偷自产一区二区三区观看| 久久精品99国产精品酒店日本| 亚洲a级在线播放观看| 无遮挡在线观看| 我不卡伦不卡影院| 成人免费高清视频在线观看| 成人网在线播放| 网站一区二区| 久久国产婷婷国产香蕉| 成人黄色影片在线| 中文字幕va一区二区三区| 鲁片一区二区三区| 日本高清无吗v一区| 66精品视频在线观看| 韩日av一区二区| 男人资源在线播放| 亚洲成人动漫精品| 红杏视频成人| 55夜色66夜色国产精品视频| 超碰国产精品一区二页| 国产精品福利一区二区三区| 黄色欧美网站| 亚洲在线观看视频网站| 亚洲一区二区av电影| 亚洲人成电影网| 亚洲精品高清国产一线久久| 免费成人结看片| 蜜桃麻豆www久久国产精品| 私拍精品福利视频在线一区| 欧洲一区二区在线| 中文字幕成在线观看| 成人无号精品一区二区三区| 亚洲激情另类| 厕沟全景美女厕沟精品| 国内小视频在线看| 91禁外国网站| 久久99精品一区二区三区三区| 在线观看国产精品淫| 91精品国产一区| 欧美在线视频观看免费网站| 小说区图片区色综合区| 欧美三级韩国三级日本一级| 欧美激情久久久久久久久久久| 欧美精品久久一区| 全球av集中精品导航福利| 亚洲va韩国va欧美va| 亚洲毛茸茸少妇高潮呻吟| 成人av免费在线播放| 日韩精品久久一区二区三区| jizz亚洲女人高潮大叫| 第九色区aⅴ天堂久久香| 日韩美女免费线视频| 精品丝袜一区二区三区| h视频在线播放| 老司机免费在线视频| 欧美13videosex性极品| 久久精品一偷一偷国产| 日韩欧美国产wwwww| 欧美三级自拍| 中文字幕一区二区5566日韩| 四虎精品一区二区免费| 巨大荫蒂视频欧美另类大| 欧美第一精品| 久久午夜精品一区二区| 国产欧美日韩精品在线观看| 亚洲成人a**站| 日韩激情av在线播放| 91探花福利精品国产自产在线| 亚洲欧美综合一区| 日韩一级在线| 国产成人精品a视频一区www| 亚洲午夜精品久久久久久久久| 国产精品色婷婷视频| 亚洲一区二区三区成人在线视频精品| 欧美激情一区二区三区| 国产精品视频久久久| 欧美一级淫片aaaaaaa视频| 国产成人在线免费观看| 国产精品蜜臀在线观看| 日韩avvvv在线播放| 亚洲欧美一区二区三区四区| 久久99国产精品久久99果冻传媒| 尤物视频在线看| 亚洲欧美激情一区二区| 一本一道久久a久久精品逆3p| 日韩在线观看一区二区三区| 欧美三级电影在线播放| 中文字幕亚洲欧美日韩在线不卡| 麻豆成人久久精品二区三区红| 欧美性xxxxxx少妇| 日本强好片久久久久久aaa| 国产欧美日韩不卡免费| 在线亚洲一区二区| 99久久久精品免费观看国产蜜| 亚洲午夜一级| 国产美女精品视频免费观看| 成人看片毛片免费播放器| 欧洲亚洲视频| 91精品国产黑色紧身裤美女| 欧美精品日日鲁夜夜添| 中文字幕国产一区二区| 中文字幕在线看视频国产欧美在线看完整|