0%

Passing Data Deeply with Context

  • 當裡面的子狀態,會想要往父層去找尋時,就可以使用Context
  • 學習文件:React Docs
  • 資料會和其他元件要共用
    • prop drilling
      1. 最基本使用
    • 範例:Section.js \ Heading.js
    • children 帶入元件的內容

  1. 將 app.js 改寫
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
import Heading from './Heading.js';
import Section from './Section.js';

export default function Page() {
return (
<Section>
<Heading level={1}>Title</Heading>
<Section>
<Heading level={2}>Heading</Heading>
<Heading level={2}>Heading</Heading>
<Heading level={2}>Heading</Heading>
<Section>
<Heading level={3}>Sub-heading</Heading>
<Heading level={3}>Sub-heading</Heading>
<Heading level={3}>Sub-heading</Heading>
<Section>
<Heading level={4}>Sub-sub-heading</Heading>
<Heading level={4}>Sub-sub-heading</Heading>
<Heading level={4}>Sub-sub-heading</Heading>
</Section>
</Section>
</Section>
</Section>
);
}

以上方式,看到 在 Heading 元件傳入參數
改寫為,在 section 元件就來控制裡面的 Heading 是什麼 level

1
2
3
4
5
6
7
8
9
<Section level={3}>  

<Heading>About</Heading>

<Heading>Photos</Heading>

<Heading>Videos</Heading>

</Section>
  1. 實作
    1. Create a context.
    2. Use that context,在子層import { useContext } from 'react';
    3. 提供 context 的值

Create a context.

1
2
3
4
import { createContext } from 'react';

export const LevelContext = createContext(1);

Use that context

  • 是 Heading 要往外層找資料,所以要在這裡使用 useContext
  • 要往外層的地方
  • 注意 level
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { useContext } from 'react';
import { LevelContext } from './LevelContext.js';

export default function Heading({ children }) {
const level = useContext(LevelContext);
switch (level) {
case 1:
return <h1>{children}</h1>;
case 2:
return <h2>{children}</h2>;
case 3:
return <h3>{children}</h3>;
case 4:
return <h4>{children}</h4>;
case 5:
return <h5>{children}</h5>;
case 6:
return <h6>{children}</h6>;
default:
throw Error('Unknown level: ' + level);
}
}

Provide the context

  • 建立 context 的 provider
  • 提供此資料的提供者、元件
  • 這個 provider 是放在最外層,所以在此範例應該放在 section 去改寫
原本的 section.js
1
2
3
4
5
6
7
8
export default function Section({ children }) {
return (
<section className="section">
{children}
</section>
);
}

改寫section.js
  • level 從這邊拿
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { LevelContext } from './LevelContext.js';  


export default function Section({ level, children }) {

return (

<section className="section">

<LevelContext.Provider value={level}>

{children}

</LevelContext.Provider>

</section>

);

}

首先要先了解 linked lists 和陣列是不一樣的!!
linked lists是一種常見的資料結構,其使用node(節點)來記錄、表示、儲存資料(data),並利用每個node中的pointer指向下一個node

題目:

You are given the heads of two sorted linked lists list1 and list2.

Merge the two lists in a one sorted list. The list should be made by splicing together the nodes of the first two lists.

Return the head of the merged linked list.

注意類似題目要設立一個 dummy 節點

  • 如何訪問list 中某個節點的 值,要從第一個節點開始,透過 next 依序訪問下一個節點重複操作直到抵達目標節點並讀取 value
  • 訪問第一個節點為 list.val ; 訪問第二個節點 list.next.val ; 訪問第三個節點list.next.next.val
  • 要將兩個 list 合併的方法,每次比較兩個 而最小的數挑出,放入新的 list
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
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} list1
* @param {ListNode} list2
* @return {ListNode}
*/

var mergeTwoLists = function (l1, l2) {
//current
const dummy = new ListNode(-Infinity)
//point dummy node
let prev = dummy;
while (l1 && l2) {
if (l1.val <= l2.val) {
prev.next = l1;
//移動 prev list
prev = l1
//鏈錶指針 l1 就往後移動
l1 = l1.next
} else {
prev.next = l2;
prev = l2
l2 = l2.next
}
}
//如果 l1 是 null ,就是 l2
if (!l1) prev.next = l2;
if (!l2) prev.next = l1;
return dummy.next
};

mergeTwoLists([1, 100], [3, 4, 6, 8])

對於刷題總時敬而遠之,記得剛開始學習 JavaScript 時,有挑幾題來練習,看到題目後完全不知道該如何著手,甚至有些題目還要反覆看幾次才比較理解。 畢竟也踏入前端工程師近一年的時間,使用 JavaScript 也有兩年了,該是好好鑽研練習 Leetcode。

題目:
Given a string s containing just the characters ‘(‘, ‘)’, ‘{‘, ‘}’, ‘[‘ and ‘]’, determine if the input string is valid.

An input string is valid if:

Open brackets must be closed by the same type of brackets.
Open brackets must be closed in the correct order.
Every close bracket has a corresponding open bracket of the same type.

以字串帶入,並要確認傳入的字串中的大括號, 中括號等是否有兩倆配對

Example 1:

1
2
Input: s = "()"
Output: true
1
2
Input: s = "()[]{}"
Output: true
1
2
Input: s = "(]"
Output: false
  1. 製作一個物件,為括號的配對
  2. 存放括號的空物件
  3. 將字串進行迭代判斷
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
i 會loop 此字串
"([]){}"

第一圈:遇到 "("
stack = [ ")"]

第二圈:遇到 "["
stack = [ ")","]"]

第三圈:遇到 "]" , 此時,stack = [ ")","]"]
進入第二個判斷:stack[stack.length - 1] === ch
陣列的最後一個是否與 i=2 ,ch = "]" 互相配對,若配對成功則從陣列中刪除

第四圈:遇到 ")" , 此時,stack = [ ")"]
進入第二個判斷:stack[stack.length - 1] === ch
陣列的最後一個是否與 i=3 ,ch = ")" 互相配對,若配對成功則從陣列中刪除

第五圈:遇到 "{" , 此時,stack = []
stack = [ "}"]

第六圈:遇到 "}" , 此時,stack = [ "}"]
進入第二個判斷:stack[stack.length - 1] === ch
陣列的最後一個是否與 i=5 ,ch = "}" 互相配對,若配對成功則從陣列中刪除

最後跳出陣列確認 stack.length === 0 => 為 0 表示皆配對成功


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var isValid = function (s) {
const hashMap = { "(": ")", "{": "}", "[": "]" };
const stack = [];
for (let ch of s) {
if (hashMap[ch]) {
// ch is an opening bracket
stack.push(hashMap[ch]);
} else if (stack.length > 0 && stack[stack.length - 1] === ch) {
//當空陣列裡面有值且陣列的最後一個值和 key 是一樣的就從陣列中刪除
// ch is a closing bracket and top of stack matches
stack.pop();
} else {
// ch is a closing bracket and top of the stack doesn't match
return false;
}
}
return stack.length === 0;
};

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

之前曾快速的學習了React 資料的建立與傳遞方式,並寫了篇文章,最近因為六角讀書會活動也再次更深入一些的學習 React ,並將一些 React Hook 反覆使用練習。
因為目前工作主要還是以 Vue 進行開發,所以有時候會提一下兩者框架使用的相同或不同之處。

component 元件化

在 Vue 的撰寫中,也是會嘗試將頁面中的不同區塊做拆分。與 Vue 一樣,元件的的命名會以 大寫 為開頭。
透過減少重複為用意來建立元件化。


codepen-Component

多個資料,傳遞給單一元件

  • 建立兩個元件 component1 , component2
  • 建立共通元件 Board ,此元件可以各別放入兩個元件要傳入的資料(props)

codepen-共用元件

共同資料,傳遞給不同元件

codepen-共用資料

條件 render

  • 這部分在官方文件就有清楚介紹,可以依據使用條件來顯示元件資料
  • 使用條件判斷時,若不複雜,可以使用三元運算方式
    • isLoggedIn ? <UserGreeting /> : <GuestGreeting />
  • conditional-rendering

元件資料傳遞

子傳父

  • 範例將以 input 為範例
  • 此方式的資料傳遞,有點類似於 Vue 中的 v-model 資料雙向綁定
  • 使用 v-model 實現父子元件傳遞資料
  • 在 React 可以將 useState 中設訂的值綁在元件上作為傳遞
  • 在下方的圖示
  • 在父層有 value , setValue

-> 其中 setValue 方法提供給子元件,同時就會讓子元件也產生改變 value 的能力(也就是可以使用父元件的方法)

  • 要實現子傳父,就在父層掛一個 hook

經常使用 vue 或 React 等前端框架來進行專案開發,但沒有機會好好來認識下他背後的打包工具:webpack
趁著這次六角釋出的基礎 webpack 教學,順道來記錄一下。

前端壓縮打包工具

  • 從官網的圖,很明顯地就可以看到將左側複雜的檔案進行壓縮,並做項目管理
  • 在 Vue , React 的背後都有使用 webpack

    使用 webpack

  1. 安裝
    建立 webpack 需要使用到 npm 初始化
    npm init -y
    安裝之後,會產生 package.jason 檔案

  2. 安裝 webpack 以及 webpack-cli ,並存在 開發環境
    npm install webpack webpack-cli --save-dev

    • 會產生 package-lock.json => 紀錄版本詳細資訊
    • node_modules 資料夾

進入點(entry)、輸出點(output)

  • /src/index.js
  • /dist/main.js
  • 指令:"build": "webpack" => 設定 webpack 將 src 中的 index.js 檔案輸出,放到 dist 資料夾
    • 使用該指令 npm run build
      • build 之後產生 dist/main.js

entry, output 名稱修改

* 建立 `webpack.config.js` 檔案
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const path = require('path');

module.exports = {
//預設路徑位置
entry: './src/index.js',
output: {
//輸出位置名稱(filename),檔案位置
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
};

//path.resolve(__dirname, 'dist')
// dirname當前資料夾路徑
//path.resolve 將相對路徑改為絕對路徑

讓每次產生的 bundle 名稱不一樣

  • 搭配 plugin 來使用 HtmlWebpackPlugin

NPM script , 自訂指令

  • 用 node 執行一段 JS ,位置為同層下的 hello.js
  • npm run hello
  • 網站上線\部署或開發,可能都需要下不同指令來進行管理

mode 切換

  • 不同模式下,會因程式內容而輸出不同內容
  • production 上線版模式, 輸出的 bundle 檔案會較為精簡
    • 會壓縮優化,將程式碼壓成一行
    • 不容易除錯與理解

CSS loader

  • 幫助 webpack 載入 css
  • npm install --save-dev css-loader
  • CSS loader
  • webpack.config.js
  • 別忘了 在載入點加入 ·import css from './all.css'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const path = require('path');

module.exports = {
//預設路徑位置
entry: './src/index.js',
output: {
//輸出位置名稱(filename),檔案位置
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.css$/i, //只要副檔名為 .css 就會使用以下
use: ["style-loader", "css-loader"],
},
],
},
};

載入 webpack 測試伺服器

1
2
3
4
5
6
7
8
9
devServer: {
static: {
directory: path.join(__dirname, 'dist'),
//contentBase: path.join(__dirname, 'dist'),
},
compress: true,
port: 9000,
open: true //可以即時更新
},
  • 最後,指令調整,如此就可以開始進行編譯

  • webpack 也可以載入 node_module 下的東西

  • 用 dist 下的檔案來部署

    webpack.config.js 檔案

  • 注意

  • mode: 設定development 或是 production

  • entry 進入點在哪

  • output 輸出去哪

  • module -> rule 下 會放 scss-loader , babel-loader 等規則

  • plugins: webpack 主要是讀檔案,但透過 plugins ,如建立新的 html檔案, MiniCssExtractPlugin:將 css 從 js 抽離出來

  • devtool: ‘source-map’ => 協助檔案 debug

關於 Webpack,它是什麼?能夠做什麼?為什麼?怎麼做?— freeCodeCamp 的筆記
Webpack 是什麼?模組打包工具的用途及基本 Webpack 教學

props, state 的應用

  • props 傳入參數的部分
  • state 讓組建本身有狀態

製作 計數器

  • props
  • 製作,讓計數器的數字變化,可以同步變化到 button 上的數字
    • 在父層 App.vue 做一個內容的傳送
    • 在 子元件,要自訂一個變數,作為接收父層從來的資料
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import "./styles.css";

type BtnProps = {
btnNum: number;
};

const Btn: React.FC<BtnProps> = ({ btnNum }) => {
return (
<button>
add 1<span>目前總數:{btnNum}</span>
</button>
);
};

export default function App() {
const num = 0;
return (
<div className="App">
<h1>Counter: {num}</h1>

<Btn btnNum={num} />
</div>
);
}


進階,希望透過點擊 button 同步更改數值

  • 讓 render (react 渲染機制),可以在變數被變動的時候順便觸發渲染機制

    • 改變變數 -> 觸發渲染

      使用 state 達成上述目的

    對組建本身引入 state 機制

  • 用 state 來做變數宣告,只要 state 變數有做變動都會觸發渲染機制

    • 當變數有涉及於頁面上的更新,就會需要使用 state
  • Hook 本身多是 use 開頭

  • useState() 是一個 function

用陣列解構來取的 return 的值

    1. 當前的值
    1. 再回傳一個 function : 用來設定/變動 第一個數值
      const [num2, setNum2] = useState(999)

example-1

實現 父層數值變化,子組件隨之變化

  • 應該將主要的更新動作都放置父組件
  • 將放在父組件的事件,透過 props 方式傳給子組件,並將該變數放到按鈕的點擊事件上

example-2

剛學習 vue 的時候,對 slot 完全一竅不通,不了解要如何挖空多元件,來放入資料,更是不知道他能使用的時機是什麼時候,種種因素,讓我從官方文件找些範例釐清,也找了些文章閱讀加深記憶,順手也紀錄在部落格上~

使用時機:

  • 使用同一個 元件 , 但只有內文不同。
  • 有時候資料是靜態,不常變動,甚至需要大量重複。這情況不必要使用 props。
  1. 共用樣式
    官網的範例可以看到,讓按鈕的文字可以彈性的變化。

  2. named slot(具名插槽)

  • 建立一個元件,其中含有多個slot區塊
    範例連結
  1. Scoped slots(作用域插槽)
  • 把子元件的資料取出來,給父層使用
  • 不同於前面 slot 的使用,是由父層輸入資料呈現於頁面
    範例

資料來源:
vuejs
不只懂 Vue 語法:什麼是 slot?請示範 slot 的用法?
用範例理解 Vue.js #18:Slot

近期購買了布魯斯的 TypeScript 的課程,除了學到型別之外,還接觸到 WebSocket 的使用,所以就來簡單紀錄下 WebSocket 是什麼,在專案內可以實現什麼功能。

WebSocket是一種網路傳輸協定,讓前端與後端溝通除非有一方斷開連接,否則會一直保持聯繫

情境說明

  1. 使用者上下線狀態:可以延遲幾秒鐘,可以使用 setInterval 做出輪循
  • 設定每幾秒發出請求,更新狀態
  • 使用 HTTP 協議的請求:GET\POST 等方式,做出Request\Response
  1. 聊天室:會希望聊天訊息是即時更新,若採用上面的方式,為了使訊息更新及時會從每幾秒改為每幾毫秒,這樣的方式會造成短時間內不斷發出請求,造成消耗後端資源
  • 指建立一次連結,可以與後端持續保持聯係

聊天室的實際運用

  • 將在 node.js 建立 websocket 的服務,讓前端連線到node.js的服務
  • socket.io => 包裝 websocket 協議的 node.js 的工具包
    • 當使用 node.js 做開發,就可以透過 socket.io 提供的功能實現

安裝 socket.io

  • socket.io-clinet : 在前端的部分要使用 clinet 安裝包
    npm i -D socket.io socket.io-clinet

後端

  • 將 socket.io 引入 node.js 的 server 端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import devServer from "@/server/dev";
import prodServer from "@/server/prod";
import express from "express";
import { Server } from "socket.io"
import http from "http" //也可以引入http模組

const port = 3000;
const app = express();
//透過 http 將 server 建立起來
const server = http.createServer(app)
const io = new Server(server)
const userService = new UserService

//監聽
server.listen(port, () => {
console.log(`The application is running on port ${port}.`);
});

1
2
3
4
5
6
//建立起連接時,發出訊息
//當有用戶連接到connection 就會有回呼函式
io.on('connection',(socket)=>{
//發出訊息
socket.emit('join', 'Welcom')
})

如何在前端使用,與後端建立連線?

  • 到前端的入口檔案
1
2
3
4
5
6
7
8
9
10
//將 socket.io 的包引入前端程式這邊
import { io } from "socket.io-client";

//1. 建立連接到 node server
const clientIo = io();


clientIo.on('join',(msg)=>{
console.log('msg',msg)
})

toRef

  • 將…變成 ref
    從 reactive 物件抽出包裝成一個 ref 的值,與原本物件 是連接的,所以修改時,原本的值也會跟著變動。
  • 只能處理一個屬性,須告知要輸入的屬性\物件
  • 注意: 更改原始物件內的值,所響應會跟著變動

在 prop 的應用

  • 來自的 props 是 物件資料,可以使用toRef
    1
    2
    3
    4
    5
    6
    7
    8
    9
    props: {
    apiUser: {
    required: true,
    type: Object
    }
    },
    setup(props) {
    const userCopy = toRef(props, 'apiUser')
    }
  • 範例二
1
2
3
4
5
6
7
8
9
10
<template>

<h2>姓名: {{person.name}}</h2>
<h2>年齡: {{person.age}}</h2>
<h2>職業描述: {{person.job.front.dec}}</h2>
<button @click='person.name += ~'>修改姓名</button>
<button @click='person.age ++ '>增加年齡</button>

</template>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
import {reactive} from 'vue';

export default {
name: 'Demo',
setup(){
const person = reactive({
name: 'Eva',
age: 25,
job:{
front:{
dec:'前端工程師'
}
}
})

return{
person
}
}
}
</script>
  • 要將以上的模板寫的更容易
1
2
3
4
5
6
7
8
9
10
<template>

<h2>姓名: {{name}}</h2>
<h2>年齡: {{age}}</h2>
<h2>職業描述: {{dec}}</h2>
<button @click='name += ~'>修改姓名</button>
<button @click='age ++ '>增加年齡</button>

</template>

1
2
3
4
5
6
//就是返回普通的物件內容
return{
name: person.name,
age: person.age,
dec: person.job.front.dec
}
  • 但此方式要注意,字串模板會無法響應更新的問題

  • 使用 toRef 來達到響應

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
<script>
import {reactive,toRef} from 'vue';

export default {
name: 'Demo',
setup(){
const person = reactive({
name: 'Eva',
age: 25,
job:{
front:{
dec:'前端工程師'
}
}
})

//單一轉換
const name2 = toRefs(person,'name')

return{
name: toRef(person,'name'),
age: toRef(person,'age'),
dec: toRef(person.job.front,'dec')
}
}
}
</script>

toRefs

toRefs用來把響應式物件轉換成普通物件,把物件中的每一個屬性,包裹成ref物件
* toRefs就是toRef的升級版,只是toRefs是把響應式物件進行轉換

延續上方,toRefs

  • 可以批量處理,一個物件裡的屬性
    • 只會將第一層展開,所以以下在 job 的內層
1
2
3
4
5
6
7
8
9
10
<template>

<h2>姓名: {{name}}</h2>
<h2>年齡: {{age}}</h2>
<h2>職業描述: {{job.front.dec}}</h2>
<button @click='name += ~'>修改姓名</button>
<button @click='age ++ '>增加年齡</button>

</template>

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
<script>
import {reactive,toRefs} from 'vue';

export default {
name: 'Demo',
setup(){
const person = reactive({
name: 'Eva',
age: 25,
job:{
front:{
dec:'前端工程師'
}
}
})

//可以批量處理,所以只需要傳入個物件,
const new = toRefs(person)
console.log(new)

//在物件中再放toRefs(person) ,會變成物件包物件,所以要展開
return{
...toRefs(person)
// name: toRef(person,'name'),
// age: toRef(person,'age'),
// dec: toRef(person.job.front,'dec')
}
}
}
</script>