0%

之所以會撰寫這篇文,是因為面試過程被考倒的觀念,才發現自己過去從沒注意到這部分啊,那就來寫篇文章來認識它們之中的傳遞方式。

首先,先從型別的認識開始

在Javascript分兩大類,一種是基本型別(primitive type),另一種是物件(Object)。

  • Primitive type (以純值的形式存在)
    Boolean
    Null
    Undefined
    Number
    BigInt
    String
    Symbol(於 ECMAScript 6 新定義)
  • Object
    物件型別指的是可能由零或多種不同型別 (包括純值與物件) 所組合成,例如object,array, function, map

知道型別後,可以簡易的分類:

  • primitive type會是 pass by value,
  • object 是 pass by reference。

接下來來觀察,它們之間不同

pass by value

範例1:

1
2
3
4
5
6
7
let x=10;
let y=x;

x=20;
console.log(x); //20
console.log(y); //10

  • 注意:x和y是兩個獨立變數 (先記著這點)
    • 值會存入該變數
      var y = x; 看起來會像是y的內容要複製x,但可以的話要理解為,變數 y 是去建立了一個新的值,然後將變數 x 的內容複製了一份過來。
  • 因為兩的變數,各自獨立,所以當變數 x 的內容後來經過更新變成 20 之後,變數 y 的內容依舊保持原來的 10 而不受影響。

範例2

1
2
3
4
5
6
7
8
9
10
11
var num=3;
console.log("num start:",num);

function passByValue(func_num){
func_num=5;
}

passByValue(num);

console.log("num end:", num);

結果:

1
2
3
num start:3
num end: 3

  • 先是宣告新變數
  • 隨後建立passByValue() 函式
  • 呼叫passByValue(num) 複製變數num的值,3傳入passByValue(func_num)
    • 一開始 值會是3
    • 遇到func_num=5; => 將值改為 5
  • 但因為出去了函式範圍(scope),最終的值 num end:3

pass by refrence

範例

1
2
3
4
5
6
7
8
let x={value:10};
let y=x;

x.value=20;
console.log(x); //{value:20}
console.log(y); //{value:20}
console.log( x === y ); //true

拆解說明一下

  • 當宣告一個物件
  • JavaScript 會在記憶體的某處建立起一個物件 (圖右側),然後再將這個 x變數指向新生成的物件

  • 接著,當我們宣告了第二個變數 y ,並且透過 = 將y 指向 x 的位置。
  • 接著當我們更新了 x.value 的內容後, y.value 的內容也被更新了。

範例2

1
2
3
4
5
6
7
8
9
10
11
12
var obj1={item:"unchanged"};

console.log("obj1 start:",obj1);

function passByReference(ref){
ref.item= "changed";
}

passByReference(obj1);

console.log("obj end", obj1);

結果

1
2
3
obj1 start:{item: "unchanged"}
obj1 end:{item:"changed"}

  • 當執行passByReference(obj1) 。想像他是個地址(0x0016),進入函式中將地址複製,傳入。
    • 此時他的value 是個地址(0x0016)
  • 進入函式,遇到ref.item
    • de-reference ,進入原本的記憶體位置,找到item,並改變他的值

在不一般情況下,基本型別是 pass by value,而物件型別是 pass by reference的方式,但總有例外的時候。

pass by sharing

1
2
3
4
5
6
7
8
9
10
11
12
var obj1={item:"unchanged"};

console.log("obj1 start:",obj1);

function passBySharing(ref){
ref={ item: "changed"};
}

passBySharing(obj1);

console.log("obj end", obj1);

1
2
3
obj1 start:{item: "unchanged"}
obj1 end:{item:"unchanged"}

  • 傳入之前start 沒有改變
  • 呼叫函式,並進入passBySharing(),還是複製地址,傳入
  • 遇到ref={ item: "changed"};,會直接覆蓋地址(有點類似pass by value)
    • 這並不是直接更改物件特性

最後,來說說 JavaScript 屬於?

看了多篇文章,實在也是有點混亂,該認為三種形式都有呢?還是就是Pass by sharing、Pass by reference呢? 那例外情形又該如何解釋?

所以這邊的結尾,直接引用Kuro、Huli老師文章的資訊,供給大家參考。

從Kuro Hsu 的文章
提及認為 JavaScript 應該更屬於 Pass by sharing 的形式。

  • JavaScript 不屬於單純的傳值或傳址。

參考 ECMA-262-3 in detail. Chapter 8. Evaluation strategy

Regardless of usage concept of reference in this case, this strategy should not be confused with the “call by reference” discussed above. The value of the argument is not a direct alias, but the copy of the address.
由於在 JavaScript 的物件類型是可變的 (mutable),當物件更新時,會影響到所有引用這個物件的變數與其副本,修改時會變動到原本的參考,但當賦與新值時,會產生新的實體參考。

另外在 Huli 的文章中:

依據細分程度的不同,下面幾句話都是正確的:
JavaScript 裡面只有 pass by value
JavaScript 的 primitive type 是 pass by value,object 是 pass by sharing


最終來個綜合練習:

相信在最後的這個練習,可以更清楚,pass by value,pass by reference,pass by sharing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function changeStuff(num,obj,obj2){
num=num*10;
obj.item="changed";
obj={item:"changed"};
}

var num=10;
var obj={item: "unchanged"};
var obj2= {item: "unchanged"};

changeStuff(num, obj, obj2);
console.log(num);
console.log(obj.item);
console.log(obj.item);
1
console=> 10 "changed" "unchanged"

參考資料:
JS基本觀念:call by value 還是reference 又或是 sharing?
重新認識 JavaScript: Day 05 JavaScript 是「傳值」或「傳址」?
Tech Talk: Pass By Sharing with Javascript
深入探討 JavaScript 中的參數傳遞:call by value 還是 reference?

延續 Wilson Ren課程來認識常見的排列方法!

排列的演算法介紹

  • 在JS中,就有內建 array.sort()
    • 同樣在其他程式語言,都會有內建的 sorting function
    • 但還是需要知道他們是如何運作
  • 總共有 6 種 sorting
  • 此篇文章會以前面兩種為主

Bubble Sort

  • 冒泡排序
  • 會比較相鄰的元素,如果順序不對會互換element
  • 是相當簡單的演算法,在現實世界很少拿來使用,最常用在教學
  • 而像在python、java 他們內建的排序演算法,都不是用冒泡排序,多是用 quicksort,merge sort(比較複雜、但有效率)

    範例說明

  • 比較array的最後兩個數字 => 發現順序不對就對調
  • 對調之後,再往下兩個元素
    • 發現順序正確,不需更動
  • 不更動之後,再往下兩個

    以此類推…
  • 小結:將找到的最小值,推到最左邊

要如何做 Bubble Sort

  • 先從一個陣列中2個數值比較開始思考

    1
    2
    3
    for j from A.length-1 to 1;
    if A[j] < A[j-i] ;
    swap A[j] and A[j-i]
  • 虛擬碼

  • i => sorted elements

  • j => adjacent elements(j是相對i而來)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    function bubbleSort(arr){
    for(let i=0;i<arr.length-2;i++){
    for(let j=arr.length-1;j>=i+1;j--){
    if (arr[j]<arr[j-1]){
    //swap arr[j] and arr[j-1]
    let temp=arr[j];
    arr[j] = arr[j-1];
    arr[j-1] = temp;
    }
    }
    }
    console.log(arr);
    }

    bubbleSort([4,1,5,2,7]);
  • 加入隨機的數字,組成新的陣列

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    function bubbleSort(arr) {
    let step = 0;
    for (let i = 0; i <= arr.length - 2; i++) {
    for (let j = arr.length - 1; j >= i + 1; j--) {
    if (arr[j] < arr[j - 1]) {
    // swap arr[j] and arr[j - 1]
    let temp = arr[j];
    arr[j] = arr[j - 1];
    arr[j - 1] = temp;
    step++;
    }
    }
    }
    console.log("It takes " + step + " steps to complete.");
    console.log(arr);
    }

    let test = [];

    for (let i = 0; i < 100; i++) {
    test.push(Math.floor(Math.random() * 100));
    }

    bubbleSort(test);

    Big O of Bubble Sort

  • 最糟情況下: 由大到小,要排成 由小到大 => 他交換的次數會是 (n-1)+(n-2)+(n-3)+…+(n-n)次

  • 最好的情況: 本身的arr就接近 小到大

    • 優化語法
    • 發現沒有任何elements被交換,就可以停止迴圈
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      function bubbleSort(arr){
      for(let i=0;i<arr.length-2;i++){
      let swapping=false;
      for(let j=arr.length-1;j>=i+1;j--){
      if (arr[j]<arr[j-1]){
      //swap arr[j] and arr[j-1]
      let temp=arr[j];
      arr[j] = arr[j-1];
      arr[j-1] = temp;
      swapping=true;
      }
      }
      }
      if (swapping==false){
      break;
      }
      }
  • 平均情況還是用到 nested for loop

    • O(n^2)

Insertion Sort

  • 效率比 bubble sort 好一些
  • 理論上, 都是 O(n^2)
  • 不斷做插入的動作

    範例說明

  • 先認為這條arr長度是1
  • 從陣列最前面開始,要將1插入4這個arr上

  • 數字2,要和左邊的數字作比較

  • 數字3去比較

虛擬碼

1
2
3
index 0,1,2,3,4
value 1,2,3,4,0

  • 檢查這條arr的 index=1
  • 設定key=A[j],並將key插入 sorted sequence
    • j 的前一格為 i
  • 確認 i 有沒有大於key => 也就是對於key而言,要不斷地和它左邊的值比較
    • 如果左邊的值比key大,就要讓他們互換
1
2
3
4
5
6
7
index 0,1,2,3,4
value 1,2,3,4 (i),0 (j key)

while i>0 && A[i] > key
A[i+1] = A[i]
i -= 1

  • 語法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let unsorted = [14, -4, 17, 6, 22, 1, -5];

insertionSort(unsorted);

function insertionSort(arr) {
for (let j = 1; j <= arr.length - 1; j++) {
let key = arr[j];
i = j - 1;
while (i >= 0 && arr[i] > key) {
arr[i + 1] = arr[i];
i -= 1;
}
arr[i + 1] = key;
}

console.log(arr);
return arr;
}

Big O

常常看到 YouTube 演算法造成頻道經營的難度、或是facebook演算法而使得行銷曝光度的改變,但始終對演算法這個名詞沒有認識。
藉由 Wilson Ren課程

什麼是演算法?

用以解決問題而可以逐步執行的步驟或程序。

來看看現實生活中的演算法

  • Google Map 如何找到最短路徑
  • YouTube 推薦給你,認為你有興趣的影片
  • FB\IG 的加好友、追蹤推薦

演算法比較

有兩個演算法都可以完成目標任務,那我們會如何取決誰比較好?

  • 哪個演算法執行速度快?
  • 所佔用電腦的記憶體資源少?

    時間?

    首先,在計時演算法所耗時的部分:
  • 幫演算法做計時,是不實際的事情
    • 同一台電腦在執行同一任務,所執行的時間會不同
    • 不同電腦、CPU處理速度不一樣

      應該考慮,複雜度 Complexity

  • 複雜度分為兩種:時間複雜度、空間複雜度 (在本文多是討論時間複雜度)
  • 要如何計算時間複雜度?
    • 加、減、乘、除、comparison ,這些每一個都可以被算作一個 operation
    • Complexity: 在所寫的演算法中,總共用到多少 operations(運算子)
    • 使用 function 來顯示 Complexity 和 input size 的關係。

Big O Notation

  1. 是一個工具,用來描述當你的值不斷擴大時,f(n)值會去哪裡
  2. 為最壞情況的打算。他會展示一個演算法複雜度的趨勢

計算 Big O 的值

  1. Constant doesn’t matter : 常數它並不重要
    • f(n)=3n :3為常數、n為變數
  2. Small Terms don’t matter
    • fn= 3n^2 + 6n + 4 => 只需保留到fn= 3n^2
  3. Logarithm Base doesn’t matter

    範例:

  4. f(n)=3n
    答案:O(n)
  5. f(n)=13n^3 + 6n +7
    答案:O(n^3)
  6. f(n)=4log₂n
    答案:O(logn)
  7. f(n)=5
    答案:O(1)

演算法常見 Big O 的值

由好至差

  1. O(1)
  2. O(logn)
  3. O(n)
  4. O(nlogn)
  5. O(n^2)
  6. O(n^3)
  • 很多sorting值會是 O(nlogn)
  • 盡量讓演算法可以達到3、4的值

圖示:

emit 實作練習

  • 透過點擊add按鈕,觸發外層元件數值的改變
    • 先定義外層接收資料方式
    • 定義內層的 $emit 觸發方法
    • 使用 v-on 的方式觸發外層方法(口訣:前內、後外)

外層元件

  • 定義接收方法:當內層傳給你的時候,要做什麼事
    • addNumber()使num 增加1
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      const app = Vue.createApp({
      data() {
      return {
      num: 0,
      text: ''
      };
      },
      methods: {
      addNumber() {
      console.log('addnumber');
      this.num++;
      },

      }
      });

內層元件

  • 在方法內建立函式,來觸發資料傳遞
  • 並將click綁到內部元件的按鈕上。使點擊時候,會觸發$emit
    • emit 名稱emit-num
      1
      2
      3
      4
      5
      6
      7
      8
      9
      app.component('button-counter', {
      methods: {
      click() {
      console.log('inner,click');
      this.$emit('emit-num');
      }
      },
      template: `<button type="button" @click="click">add</button>`
      });

建立內外層元件的溝通橋樑

  • 使用 v-on 的方式觸發外層方法(口訣:前內、後外)
    • 在 div內放入子元件button-counter
    • :emit-num="addNumber"
1
2
3
4
<div id="app">
{{ num }}
<button-counter v-on:emit-num="addNumber"></button-counter>
</div>

練習二,資料接收

外層元件

  • 接收方法為 getData 接收 text
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    const app = Vue.createApp({
    data() {
    return {
    text: ''
    };
    },
    methods: {

    getData(value) {
    console.log('getData', text);
    this.text = value;
    }
    }
    });

內層元件

  • 建立觸發傳遞的方式
    • 將內層元件text: '內部資料' 傳遞到外層
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      app.component('button-text', {
      data() {
      return {
      text: '內部資料',
      }
      },
      methods: {
      emitText() {
      this.emit('emit-text', this.text)
      }
      },
      template: `<button type="button" @click="emitText">emit data</button>`
      });

      建立內外橋樑

      1
      2
      3
      <h3>傳遞資料狀態</h3>
      內部傳來的文字:{{ text }}<br>
      <button-text @emit-text="getData"></button-text>

emit 命名

與 props 命名一樣需要注意:

  • 在內層若以駝峰命名 emitText
    • 也可以一開始內層就是用- 來命名即可,如emit-text
      1
      2
      3
      emitText() {
      this.emit('emitText', this.text)
      }
  • 到了要綁定的 template 上,emit-text
    1
    <button-text @emit-text="getData"></button-text>

因為Vue每個元件都是各自獨立,所以我們無法在各自元件去調整資料,而直接修改另一個元件的資料。
所以,要使元件之間可以相互溝通,就需要使用資料傳遞方式。
在傳遞方式:

  • 外層傳遞內層 : props
  • 內層向外傳遞 : emit
    當外層元件,要將資料往內層元件丟時,此過程就是使用props。

傳遞資料的方式:

靜態資料傳入

  • 預期將外層傳入一個url的變數,傳進給內層使用
    • photo為內部元件,在template 我希望能夠取得外層傳入的圖片(urlimg)
    • 故在 props 以此命名,並將它綁到 template
      1
      2
      3
      4
      app.component('photo', {
      props: ['url'],
      template: `<img :src="url" class="img-thumbnail" alt>`
      });
  • 將上方的元件資料,加到外層元件:像是加入html屬性一樣
    1
    2
    3
    <photo
    url="https://images.unsplash.com/photo-1605784401368-5af1d9d6c4dc?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80">
    </photo>

動態資源

  • 使用 v-bind,綁定內部元件的props ,使資料可以跟外層元件連動
  • 記得使用口訣: 前內、後外 =>前面就是props內的名稱,後面就是外層元件的名稱
    1
    2
    3
    <h3>動態資源</h3>
    <p>技巧:前內、後外</p>
    <photo v-bind:urlimg="imgUrl"></photo>

資料傳遞為 單向數據流

  • 外部所定義的資料,當往內層傳遞,是單向性
  • 不能試圖使用v-model或各種方式,來改變props傳入的內容
    • 以下範例:在子元件 photo2 放入 input綁定 v-model
    • 當嘗試在輸入框修改網址,會出現錯誤
1
2
//外層元件綁定 url
<photo2 :url="imgUrl"></photo2>
1
2
3
4
5
6
7
8
9
10
11
12
13
const app = Vue.createApp({
data() {
return {
imgUrl: 'https://images.unsplash.com/photo-1605784401368-5af1d9d6c4dc?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=600&q=80',
};
},
});

app.component('photo2', {
props: ['url'],
template: `<img :src="url" class="img-thumbnail" alt><br>
<input type="text" v-model="url"> {{ url }}`
})

在內部元件,要為props來命名

在命名上有些要注意的地方

  • 首字母大寫: PostData、SetItems
  • 駝峰命名法: postData、setItems
    要注意:在 HTML 中使用時必須使用 kebab-case (短橫線分隔)且應該為小寫。
1
2
3
4
5
6
7
8
9
10
11
<div id="app">

<photo3 :super-url="imgUrl" ></photo3>
</div>

<script>
Vue.component("photo3", {
props: ["superUrl"],
template: `<img :src="superUrl" class="img-thumbnail alt >"`
});
</script>

定義 Props 型別驗證

  • 使用型別驗證,會改用大括號,裡面放入props名稱,並用物件形式加入設定的內容
    • props:{ propC: { type: String, required: true, } }
  • 其中,可以針對該props 來設定:
    • type型別 : 可以是 String、Number、Boolean、Array、Object、Date、Function、Symbol
    • default :如果該 prop 沒有接收到傳入的值,就會使用 default 的值作為預設值。
    • required : 是否為必填項,如果設為 true 則表示必須要有值傳入,若沒有,就會出現錯誤提示。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      app.component('props-validation', {
      props: {
      // 單一型別檢查,可接受的型別 String, Number, Object, Boolean, Function(在 Vue 中可使用 Function 驗證型別)
      // null, undefined 會直接通過驗證
      propA: Function,

      // 多個型別檢查
      propB: [String, Number],

      // 必要值
      propC: {
      type: String,
      required: true,
      },

      // 預設值
      propD: {
      type: Number,
      default: 300
      },

      // 自訂函式
      propE: {
      type: Object,
      default() {
      return {
      money: 300
      }
      }
      },

      // 自訂驗證
      propF: {
      validator(value) {
      return value > 1000
      }
      },
      },
      六角學院
      [DAY12]跟 Vue.js 認識的30天 - Vue 模組資料傳遞(props)

修飾符有分為:
1.按件修飾符
2.滑鼠修飾符
3.事件修飾符

codepen

按鍵修飾符

1-1. 別名修飾

  • 在特定按鍵,按下去的時候觸發
    • .enter, .tab, .delete, .esc, .space, .up, .down, .left, .righ
  • 使用.enter只有再按下 enter 鍵才會觸發
1
2
3
4
5
<div id="app">
<h6 class="mt-3">別名修飾</h6>
<input type="text" class="form-control" v-model="text" @keyup.enter="trigger('enter')">

</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
Vue.createApp({

data(){
return {

}
},
methods: {
trigger: function(name) {
console.log(name, '此事件被觸發了')
},
}
}).mount('#app')

1-2. 相應按鍵時才觸發的監聽器

  • 僅在按下相應按鍵時才觸發鼠標或鍵盤事件的監聽器
  • .ctrl, .alt, .shift, .meta
  • 此範例為 @keyup.shift.enter
    1
    2
    <h6 class="mt-3">相應按鍵時才觸發的監聽器</h6>
    <input type="text" class="form-control" v-model="text" @keyup.shift.enter="trigger('shift + Enter')">
  1. 滑鼠修飾符
    .left 只當點擊鼠標左鍵時觸發。
    .right 只當點擊鼠標右鍵時觸發。
    .middle 只當點擊鼠標中鍵時觸發。
  • 按下滑鼠右鍵
    <span class="box" @click.right="trigger('right button')">
1
2
3
4
5
6
<h4>滑鼠修飾符</h4>
<h6 class="mt-3">滑鼠修飾符</h6>
<div class="p-3 bg-primary">
<span class="box" @click.right="trigger('right button')">
</span>
</div>

  1. 事件修飾符
  • 不會限定,是使用滑鼠還是鍵盤,是針對事件本身來進行修飾
    .stop - 調用 event.stopPropagation()
    .prevent - 調用 event.preventDefault()
    .capture - 添加事件偵聽器時使用 capture 模式。
    .self - 只當事件是從偵聽器綁定的元素本身觸發時才觸發回調。
    .once - 只觸發一次回調。
  • 常用的是 <a>外部連結:移除預設事件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <h4>事件修飾符</h4>
    <ul>
    <li>.stop - 調用 event.stopPropagation()。</li>
    <li><strong>.prevent - 調用 event.preventDefault()。</strong></li>
    <li>.capture - 添加事件偵聽器時使用 capture 模式。</li>
    <li>.self - 只當事件是從偵聽器綁定的元素本身觸發時才觸發回調。</li>
    <li>.once - 只觸發一次回調。</li>
    </ul>
    <a href="https://www.google.com/" @click.prevent="trigger('prevent')">加入 Prevent</a>

資料來源:
六角學院 - Vue 3.js

前言

之前在安裝套件時,就有先安裝過 Node.js 但從沒有特別去注意,它是應用在哪一塊、可以做什麼?就找了之前 udemy 全端課程,特意拉此部分來認識一下。

Node.js,讓我們可以將js檔案拿出瀏覽器,並直接和電腦硬件互動

  • js: 進行網頁中的實作、函式、行為的建立。動畫的執行、下拉選單
  • Node.js: 允許我們用JS 直接與電腦的硬體互動。例如建立桌面應用程式
  • 此外,還能在別人的電腦或server用Node.js來用行JS。例如:使用者登入 google 並輸入詢問,而你的瀏覽器會發出請求給google servers,而在這些servers 可以真的執行JS程式碼來執行發出的請求

如何使用Node

  1. 首先,建立一個資料夾 intro-to-node,在資料夾內建立檔案index.js
  2. 在該資料夾位置,運行node index.js

  • 印出檔案內的console

node REPL

  • Read Evaluation Print Loop
  • 要進入:就是在終端機下node
  • 和google 的console有點類似
  • 取得些提示 con+ tab 會列出相關的可能性

    離開

  • .exit
  • control+c 兩次
  • clear 可以清除終端機的資料

    native node module

  • 當安裝node,他就會包含許多建立好的模組

    使用node.js 進入電腦本地端的資料

  • nodejs.org/api 可以查到所有native node module,以及使用方式說明的文件
  • 當要使用 modual

  • 警告 jshint esversion:6

實際應用練習:複製檔案

  • 在原本的資料夾內建立新的檔案

  • 在index.jd 輸入該語法
  • 輸入後要運行 node index.js
    • 運行之後會看到 file2 建立完成
  • 若是已經建立好的檔案
  • 在檔案內輸入資訊I am file2
  • 執行 copyFileSync

NPM package manager

如何使用真實npm

  • 在要進行安裝的資料夾內,npm install superheroes

  • 參考文件的說明
    • 在js檔案中輸入
    • 運行 node index.js

什麼是 Node.js?Node.js 完整介紹懶人包!

v-for 呈現多筆陣列

  • v-for= item in peopleArr , item 為 自定義名稱,後面(peopleArr)填入要迴圈的資料
    • item是表示 peopleArr 陣列中每個單一物件
  • key :為帶入陣列的 key 值
1
2
3
4
5
6
7
8
9
10
11
<div id="app">
<h3>v-for 與 key</h3>
<p> 人員介紹</p>
<ul>
<li v-for="(item, key) in peopleArr">
{{ key }} - {{ item.name}} / {{ item.age }} 歲
</li>
</ul>

</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Vue.createApp({

data(){
return {
peopleArr: [
{
name: 'Ken',
age: 30,
vegan: false
},
{
name: 'Wong',
page: 35,
vegan: false
},
{
name: 'Merry',
age: 60,
vegan: false
},
{
name: 'Tom',
age: 30,
vegan: true
},
],
}
},

}).mount('#app')

codepen

物件迴圈

  • 每個item 就是每個物件的資料
  • key值為物件的屬性名稱
1
2
3
4
5
6
7
8
9
10
11
12
<div id="app">
<h3>v-for 與 key</h3>
<p> 人員介紹</p>
<ul>
<li v-for="(item, key) in peopleObj">
{{ key }} - {{ item.name}} / {{ item.age }} 歲

</li>
</ul>

</div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Vue.createApp({

data(){
return {
peopleObj:{
Salse: {
name: 'Ken',
age: 30,
vegan: false
},
Manager: {
name: 'Wong',
page: 35,
vegan: false
},
HumanResource: {
name: 'Merry',
age: 60,
vegan: false
},
Crew: {
name: 'Tom',
age: 30,
vegan: true
}
},
}
},

}).mount('#app')

v-for 與 key

v-for 可以使用 index 當作 key 嗎?

以六角課程為範例說明

1
2
3
4
5
6
7
8
<h3>v-for 與 key </h3>
<p>菜單</p>
<ul>
<li v-for="(item, key) in products">
{{ key }} - {{ item.name}} / {{ item.price }} 元
<input type="text">
</li>
</ul>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Vue.createApp({

data(){
return {
products: [
{
name: '蛋餅',
price: 30,
vegan: false
},
{
name: '飯糰',
price: 35,
vegan: false
},
{
name: '小籠包',
price: 60,
vegan: false
},
{
name: '蘿蔔糕',
price: 30,
vegan: true
},
],
},
method:{
reverseArray: function () {
this.products.reverse();
},
}
}
}).mount('#app')

  • 反轉之後,並沒有跟著

  • 綁定key之後

    1
    <li v-for="(item, key) in products" v-bind:key='item.name'>

v-for 可以使用 index 當作 key 嗎?

索引是依照位置來判定,所以當資料的位置修改,索引值也會跟著變動,導致後續的渲染錯誤。
如果key是會變動的話,可能會導致資料的渲染錯誤

如果後端提供的資料,沒有唯一索引要如何處理?

1.與後端溝通,為何前端要用到key的值
2.拿到資料後,自己產生key,並放入該物件資料中

1
2
3
使用v-for時,一定要用key 
使用v-for時,不要拿索引當作key
請使用唯一的值當作key的值

使用v-model來進行資料的「雙向」綁定, v-model 會根據不同的表單類別來更新元素的內容。
主要應用在表單類型進行綁定,常見的表單元素像是 <input><textarea> 以及 <select> 等。

v-model 可以與 input 、textarea 綁定

1
2
3
4
<h3>input</h3>
<input type="text" class="form-control" v-model="name">
{{ name }}

1
2
3
4
5
6
7
8
9
Vue.createApp({

data(){
return {
name: "小明"
}
},

}).mount('#app')

1
2
3
<h3>textarea</h3>
<textarea cols="30" rows="3" class="form-control" v-model="text"></textarea>
{{ text }}
1
2
3
4
5
6
7
8
9
Vue.createApp({

data(){
return {
text: "一段文字敘述"
}
},

}).mount('#app')

checkbox 單選框

  1. checkbox與p段落連動
  • 在input加入v-model="checkAnswer"
  • P段洛,放入三元運算:用來判斷當checkAnswer為true,顯示’吃飽了’;反之,’還沒’
    • 透過選單的勾選,來顯示 checkAnswer 是 true \ false
1
2
3
4
5
6
7
 <h3>checkbox 單選框</h3>
<p>小明,你是吃飽沒?</p>
<p>{{ checkAnswer ? '吃飽了' : '還沒'}}</p>
<div class="form-check">
<input type="checkbox" class="form-check-input" id="check1" v-model="checkAnswer">
<label class="form-check-label" for="check1">小明回覆</label>
</div>
1
2
3
4
5
6
7
8
9
Vue.createApp({

data(){
return {
checkAnswer: false,
}
},

}).mount('#app')

  1. checkbox 單選延伸
  • 回傳單一的值,相對使用三元運算比較直觀
  • checkAnswer2 是空字串,在 input 綁定後,設定false-value、true-value
  • 將文字綁入:true-value="吃飽了" false-value="還沒",就可以放入資料欄位上
1
2
3
4
5
6
7
8
<h3>checkbox 單選延伸</h3>
<p>小明,你是吃飽沒?</p>
<p>{{ checkAnswer2 }}</p>
<div class="form-check">
<input type="checkbox" v-model="checkAnswer2" true-value="吃飽了" false-value="還沒" class="form-check-input"
id="check2">
<label class="form-check-label" for="check2">小明回覆</label>
</div>
1
2
3
4
5
6
7
8
9
Vue.createApp({

data(){
return {
checkAnswer2: '',
}
},

}).mount('#app')

checkbox 複選框

  • 資料格式為陣列
  • 覆選框的input裡面要有value
  • 當綁定v-model,於畫面點選該項目時,會將 input 中的 value,放入的陣列中
    • 最後將資料渲染於畫面
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      <h3>checkbox 複選框</h3>
      <p>你還要吃什麼?</p>
      <p>{{ checkAnswer3.join('') }}</p>
      <div class="form-check">
      <input type="checkbox" class="form-check-input" id="check3" value="蛋餅" v-model="checkAnswer3">
      <label class="form-check-label" for="check3">蛋餅</label> </div>
      <div class="form-check">
      <input type="checkbox" class="form-check-input" id="check4" value="蘿蔔糕" v-model="checkAnswer3">
      <label class="form-check-label" for="check4">蘿蔔糕</label>
      </div>
      <div class="form-check">
      <input type="checkbox" class="form-check-input" id="check5" value="豆漿" v-model="checkAnswer3">
      <label class="form-check-label" for="check5">豆漿</label>
      </div>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      Vue.createApp({

      data(){
      return {
      checkAnswer3: [],
      }
      },

      }).mount('#app')

v-model 修飾符

修飾符為畫面上,v-model的資料和實際data中的資料在綁定之間,額外處理的小方法。

  1. 延遲 Lazy
  • 輸入文字後,要點擊外面或是按下enter,才會出現
  • 綁定到html的change事件:當完成輸入框的事件之後,才會綁定到資料集
    1
    2
    3
    4
    <h3>修飾符</h3>
    <h4 class="mt-3">延遲 Lazy</h4>
    {{ lazyMsg }}
    <input type="text" class="form-control" v-model.lazy="lazyMsg">

純數值 Number

需要用戶輸入數值,可先將type改為number,並加入修飾符

  • <input type="number">:輸入框無法輸入文字,只能輸入數字,但型別依然是string
1
2
3
<h4 class="mt-3">純數值 Number</h4>
{{ numberMsg }}{{ typeof numberMsg }}
<input type="number" class="form-control" v-model="numberMsg">

  • 要確保輸入的內容為純數字型別:要加修飾符
1
2
3
<h4 class="mt-3">純數值 Number</h4> 
{{ numberMsg }}{{ typeof numberMsg }}
<input type="number" class="form-control" v-model.number="numberMsg">

trim

將資料內容的前後空白鍵,刪除

  • 應用於要輸入e-mail帳號,避免用戶不小心在前後加入空白,而造成資料錯誤
    1
    2
    3
    <h4 class="mt-3">修剪 Trim</h4>
    這是一段{{ trimMsg }}緊黏的文字
    <input type="text" class="form-control" v-model.trim="trimMsg">

參考資料:
重新認識 Vue.js | Kuro Hsu

動態屬性綁定(注意大小寫)

  • 宣告變數dynamic,將 dynamic 綁定於點擊事件上 :click="dynamic = dynamic === 'disabled' ? 'readonly':'disabled'"
    • 如果變數結果是 disable 的話就會切換成 readonly,反之,就會切換為 disabled
  • 接著可以將值動態地加入 HTML 屬性上 :[dynamic]
    • 綁定後,就會發現輸入框隨著切換,有不同效果
      1
      2
      3
      4
      5
      <h3>動態屬性綁定(注意大小寫)</h3>
      <button type="button"
      v-on:click="dynamic = dynamic === 'disabled' ? 'readonly':'disabled'">切換為 {{ dynamic }}</button>
      <br>
      <input type="text" :[dynamic] :value="name">
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      //JS

      Vue.createApp({

      data(){
      return {
      dynamic: 'disabled',
      breakfastShop: {
      name: '奇蹟早餐',
      imgUrl: 'https://images.unsplash.com/photo-1600182610361-4b4d664e07b9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=200&q=80',
      resizeImg: 'https://images.unsplash.com/photo-1600182610361-4b4d664e07b9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&q=80'
      },
      }
      }
      }).mount('#app')

readony:可以選擇輸入框,但無法輸入值
disabled:無法點選輸入框

1
2
3
4
再次補充說明:
1. dynamic = 【這邊是最前面的變數,用來存放後面回傳回來的值】
2. dynamic === 'disabled' ? 【這邊就是中間判斷式的部分,判斷 dynamic 變數是否為 'disabled'】
3. 'readonly':'disabled' 【這邊就是最後面的值,會依照前方的判斷式來決定回傳 'readonly' 還是 'disabled',如果判斷式結果為 True 則回傳 'readonly' ,反之回傳 'disabled'】

HTML 樣式綁定

樣式綁定是時常使用到的效果,能透過此方式,使顏色改變、增加一點簡易動畫的效果。

範例解說

  • 替 box 增加旋轉效果
  • 綁定class可以用陣列方式、或物件方式來添加,以下範例為物件方式
    • 物件key值對應 className,物件的值是對應 true\false(判斷式)
    • 若className 有 - , 注意要 “bg-danger”
  • 事件的綁定 change() 透過函式來切換 true\falsethis[key] = !this[key];
  • 切換是否旋轉、切換背景色
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <style>
    .box {
    background-color: var(--bs-light);
    border: 1px solid var(--bs-gray);
    width: 80px;
    height: 80px;
    }
    .box {
    transition: all .5s;
    }
    .box.rotate {
    transform: rotate(45deg)
    }
    </style>

    <h2>切換 Class</h2>
    <h3>物件寫法</h3>
    <!-- 物件key值對應 className,物件的值是對應 true\false -->
    <div class="box" :class="{ rotate: isTransform ,'bg-danger':boxColor}"></div>
    <hr>
    <button class="btn btn-outline-primary" v-on:click="change('isTransform')">選轉物件</button>
    <button class="btn btn-outline-primary ms-1" v-on:click="change('boxColor')">切換色彩</button>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    const App = {
    data() {
    return {
    isTransform: true,
    boxColor: false,


    };
    },
    methods: {
    change: function (key) {
    this[key] = !this[key];
    },
    addClass(arr) {
    this.arrayClass.push(...arr);
    }
    },
    };

    Vue.createApp(App).mount('#app');

整合為一個物件

將上個範例的兩個class樣式,整合成一個物件

1
2
3
4
<hr class="mt-4">
<h3>物件寫法 2</h5>
<div class="box" :class='classObj'></div>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const App = {
data() {
return {
isTransform: true,
boxColor: false,

classObj:{
rotata: true,
"bg-danger": true,
}

};
},
methods: {
change: function (key) {
this[key] = !this[key];
},
addClass(arr) {
this.arrayClass.push(...arr);
}
},
};

Vue.createApp(App).mount('#app');

綁定樣式,陣列寫法

  • 針對單一元素,加入多個class
    • 此方式就不用 true\false 的判斷動作,只要綁定在陣列之中的樣式,就會套入效果
    • 像是範例,就是使按鈕背景色彩為紅色(‘btn-danger’)、無法點擊(‘disabled’)
      1
      2
      3
      4
      <h4>陣列寫法</h4>
      <button class="btn" :class="['disabled','btn-danger']">請操作本元件</button>
      <button type="button" class="btn btn-outline-primary" v-on:click="addClass(['btn-primary', 'active'])">為陣列加入
      Class</button>
  • 另一種,製作陣列,並將陣列放入
    • 將arrayClass,綁定在標籤的class上
    • 在點擊按鈕,綁定點擊事件,addClass(arr),在該函式傳入參數,將陣列內容加入arrayClass,並套入綁定的標籤上
    • 相對應的方法:
1
2
3
4
5
<h4>陣列寫法</h4>
<button class="btn" :class="arrayClass" >請操作本元件</button>
<button type="button"
class="btn btn-outline-primary"
v-on:click="addClass(['btn-primary', 'active'])">為陣列加入 Class</button>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const App = {
data() {
return {
// Array 操作
arrayClass: [''],
};
},
methods: {

addClass(arr) {
this.arrayClass.push(...arr);
}
},
};

Vue.createApp(App).mount('#app');

  • btn-primary:背景色為綠色

補充:混合寫法

  • 將bootstrap的class放入,且加入arrayClass
    • :class="[arrayClass,'text-danger'],也就是除了放入 arrayClass 這個變數之外,我們還可以放入新的class給予不同樣式
      1
      2
      3
      4
      <h4>陣列寫法</h4>
      <button class="btn" :class="[arrayClass,'text-danger']">請操作本元件</button>
      <button type="button" class="btn btn-outline-primary" v-on:click="addClass(['btn-primary', 'active'])">為陣列加入
      Class</button>

行內樣式style

要綁定style的時候

  • key會帶入style的屬性(注意要以駝峰式),如background-color,要改為backgroundColor
  • 值則是帶入style相對應的值
1
2
3
4
5
<h2>行內樣式</h2>
<h4>綁定行內樣式</h4>
<div class="box" :style="{backgroundColor:'red'}"></div>
<div class="box"></div>
<div class="box"></div>

加入準備好的資料格式

  • 將設定好的物件,裡面有包含數個樣式,直接綁定至style

  • 背景色彩,紅色,邊框5px

同時多個樣式

  • 以陣列裡面包多個物件
    1
    <div class="box" :style="[styleObject,styleObject2]"></div>