跳至主要内容

Python 指定使用 OpenSSL 介紹入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

如果 python -c "import ssl; print(ssl.OPENSSL_VERSION)" 顯示的是 LibreSSL 2.8.3,這表示 Python 並沒有使用 Homebrew 安裝的 OpenSSL,而是使用了系統預設的 LibreSSL。這通常發生在 Python 編譯時,沒有正確地連結到 Homebrew 的 OpenSSL 庫。

為了解決這個問題,請按照以下步驟操作:

步驟 1:重新安裝 Python 並指定使用 OpenSSL

首先,我們需要確保 Python 使用 Homebrew 安裝的 OpenSSL,而不是系統預設的 LibreSSL。

  1. 卸載現有的 Python 版本(如果是從系統預設安裝的)

    使用 pyenv 安裝 Python 會讓你更方便地管理不同版本的 Python,因此我們建議使用 pyenv

  2. 安裝 Homebrew OpenSSL 和 pyenv(如果尚未安裝)

    安裝 Homebrew(如果尚未安裝):

    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

    安裝 pyenv:

    brew install pyenv

    安裝 Homebrew 的 OpenSSL:

    brew install openssl@3
  3. 配置 pyenv 使用 Homebrew OpenSSL

    .zshrc.bash_profile 中添加以下配置來設置編譯環境變數,確保 Python 使用 Homebrew 安裝的 OpenSSL:

    export LDFLAGS="-L/opt/homebrew/opt/openssl@3/lib -L/opt/homebrew/opt/readline/lib -L/opt/homebrew/opt/zlib/lib"
    export CPPFLAGS="-I/opt/homebrew/opt/openssl@3/include -I/opt/homebrew/opt/readline/include -I/opt/homebrew/opt/zlib/include"
    export PKG_CONFIG_PATH="/opt/homebrew/opt/openssl@3/lib/pkgconfig"

    然後運行:

    source ~/.zshrc  # 或者 source ~/.bash_profile

步驟 2:重新安裝 Python 版本

現在,你可以使用 pyenv 來安裝 Python,並確保它與 Homebrew 的 OpenSSL 兼容。

  1. 安裝 Python 版本(例如,Python 3.9.18):

    pyenv install 3.9.18
  2. 設置全域 Python 版本:

    pyenv global 3.9.18
  3. 確認 Python 是否正確安裝並使用 OpenSSL:

    python -c "import ssl; print(ssl.OPENSSL_VERSION)"

如果這時顯示的是 OpenSSL 1.1.1 或者更高版本,則說明你已經成功解決了這個問題,並且 Python 現在是使用 Homebrew 安裝的 OpenSSL。

步驟 3:創建虛擬環境

如果你使用 pyenv 安裝的 Python,並且已經確認它使用了 OpenSSL,那麼可以重新創建虛擬環境:

  1. 創建虛擬環境:

    python -m venv .venv
  2. 激活虛擬環境:

    source .venv/bin/activate
  3. 再次檢查 OpenSSL 版本:

    python -c "import ssl; print(ssl.OPENSSL_VERSION)"

步驟 4:安裝其他依賴

在虛擬環境中,我們可以安裝任何其他的套件,並且確保它們與正確的 OpenSSL 版本兼容。

pip install <package-name>

CSS Flexbox align-items 和 align-content 入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

在 CSS Flexbox 和 Grid 佈局中,align-itemsalign-content 兩者都與「對齊」有關,但適用的情境不同(單行 vs 多行):

1. align-items

  • 作用於單行內容(單行 Flexbox 或 Grid 容器內的項目)。
  • 控制子元素在**交叉軸(cross-axis)**上的對齊方式。

常見值

說明
stretch預設值,子元素會拉伸填滿容器的交叉軸
flex-start對齊交叉軸的起始點
flex-end對齊交叉軸的結束點
center置中對齊
baseline以文字基線(baseline)對齊

範例

.container {
display: flex;
align-items: center; /* 子元素在交叉軸上置中對齊 */
height: 200px;
border: 1px solid black;
}

.item {
width: 50px;
height: 50px;
background-color: lightblue;
}
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
</div>

效果item 會在 container 的交叉軸(垂直方向)置中對齊。


2. align-content

  • 作用於多行內容(當 flex-wrap: wrap 或 Grid 有多行時)。
  • 控制整體行的對齊,而不是單個項目。

常見值

說明
stretch預設值,行會拉伸填滿容器
flex-start行對齊交叉軸的起始點
flex-end行對齊交叉軸的結束點
center行置中對齊
space-between第一行靠起始點,最後一行靠結束點,其他行平均分布
space-around每行之間有相等的間距
space-evenly每行之間及兩側間距相等

範例

.container {
display: flex;
flex-wrap: wrap;
align-content: center; /* 整體行群組在交叉軸上置中 */
height: 400px;
border: 1px solid black;
}

.item {
width: 100px;
height: 100px;
background-color: lightcoral;
margin: 5px;
}
<div class="container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>

效果:當 .container 高度足夠時,多行 .item 會整體置中排列。


總結

屬性影響對象適用於主要功能
align-items子元素單行(未換行)控制子元素在交叉軸上的對齊
align-content整體行多行(flex-wrap: wrap控制多行的整體對齊

如果容器內只有一行align-content 通常不會有影響,這時候應該使用 align-items

需要 align-content 發揮作用時,請確保:

  1. 容器有 flex-wrap: wrap;(多行)
  2. 容器有足夠的高度(不然內容會自動填滿)

Flexbox Froggy 入門教學筆記 | 學習筆記

· 閱讀時間約 8 分鐘
kdchang

Flexbox(Flexible Box Layout)是一種 CSS3 佈局模式,專門用來設計一維的彈性佈局,適用於水平或垂直排列元素,使網頁排版更加靈活且易於維護。

Flexbox Froggy 透過小青蛙過河遊戲化方式去介紹 Flexbox 使用方式:

Level 1

Welcome to Flexbox Froggy, a game where you help Froggy and friends by writing CSS code! Guide this frog to the lilypad on the right by using the justify-content property, which aligns items horizontally and accepts the following values:

flex-start: Items align to the left side of the container. flex-end: Items align to the right side of the container. center: Items align at the center of the container. space-between: Items display with equal spacing between them. space-around: Items display with equal spacing around them. For example, justify-content: flex-end; will move the frog to the right.

#pond {
display: flex;
justify-content: flex-end;
}

Level 2

Use justify-content again to help these frogs get to their lilypads. Remember that this CSS property aligns items horizontally and accepts the following values:

flex-start: Items align to the left side of the container. flex-end: Items align to the right side of the container. center: Items align at the center of the container. space-between: Items display with equal spacing between them. space-around: Items display with equal spacing around them.

#pond {
display: flex;
justify-content: center;
}

Level 3

Help all three frogs find their lilypads just by using justify-content. This time, the lilypads have lots of space all around them.

If you find yourself forgetting the possible values for a property, you can click on the property name to view them. Try clicking on justify-content.

#pond {
display: flex;
justify-content: space-around;
}

Level 4

Now the lilypads on the edges have drifted to the shore, increasing the space between them. Use justify-content. This time, the lilypads have equal spacing between them.

#pond {
display: flex;
justify-content: space-between;
}

Level 5

Now use align-items to help the frogs get to the bottom of the pond. This CSS property aligns items vertically and accepts the following values:

flex-start: Items align to the top of the container. flex-end: Items align to the bottom of the container. center: Items align at the vertical center of the container. baseline: Items display at the baseline of the container. stretch: Items are stretched to fit the container.

#pond {
display: flex;
align-items: flex-end;
}

Level 6

Lead the frog to the center of the pond using a combination of justify-content and align-items.

#pond {
display: flex;
justify-content: center;
align-items: center;
}

Level 7

The frogs need to cross the pond again, this time for some lilypads with plenty of space around them. Use a combination of justify-content and align-items.

#pond {
display: flex;
justify-content: space-around;
align-items: flex-end;
}

Level 8

The frogs need to get in the same order as their lilypads using flex-direction. This CSS property defines the direction items are placed in the container, and accepts the following values:

row: Items are placed the same as the text direction. row-reverse: Items are placed opposite to the text direction. column: Items are placed top to bottom. column-reverse: Items are placed bottom to top.

#pond {
display: flex;
flex-direction: row-reverse;
}

Level 9

Help the frogs find their column of lilypads using flex-direction. This CSS property defines the direction items are placed in the container, and accepts the following values:

row: Items are placed the same as the text direction. row-reverse: Items are placed opposite to the text direction. column: Items are placed top to bottom. column-reverse: Items are placed bottom to top.

#pond {
display: flex;
flex-direction: column;
}

Level 10

Help the frogs get to their own lilypads. Although they seem close, it will take both flex-direction and justify-content to get them there.

Notice that when you set the direction to a reversed row or column, start and end are also reversed.

#pond {
display: flex;
flex-direction: row-reverse;
justify-content: flex-end;
}

Level 11

Help the frogs find their lilypads using flex-direction and justify-content.

Notice that when the flex direction is a column, justify-content changes to the vertical and align-items to the horizontal.

#pond {
display: flex;
flex-direction: column;
justify-content: flex-end;
}

Level 12

Help the frogs find their lilypads using flex-direction and justify-content.

#pond {
display: flex;
flex-direction: column-reverse;
justify-content: space-between;
}

Level 13

Help the frogs find their lilypads using flex-direction, justify-content, and align-items.

#pond {
display: flex;
flex-direction: row-reverse;
justify-content: center;
align-items: flex-end;
}

Level 14

Sometimes reversing the row or column order of a container is not enough. In these cases, we can apply the order property to individual items. By default, items have a value of 0, but we can use this property to also set it to a positive or negative integer value (-2, -1, 0, 1, 2).

Use the order property to reorder the frogs according to their lilypads.

#pond {
display: flex;
}

.yellow {
order: 1;
}

Level 15

Use the order property to send the red frog to his lilypad.

#pond {
display: flex;
}

.red {
order: -1;
}

Level 16

Another property you can apply to individual items is align-self. This property accepts the same values as align-items and its value for the specific item.

#pond {
display: flex;
align-items: flex-start;
}

.yellow {
align-self: flex-end;
}

Level 17

Combine order with align-self to help the frogs to their destinations.

#pond {
display: flex;
align-items: flex-start;
}

.yellow {
order: 1;
align-self: flex-end;
}

Level 18

Oh no! The frogs are all squeezed onto a single row of lilypads. Spread them out using the flex-wrap property, which accepts the following values:

nowrap: Every item is fit to a single line. wrap: Items wrap around to additional lines. wrap-reverse: Items wrap around to additional lines in reverse.

#pond {
display: flex;
flex-wrap: wrap;
}

Level 19

Help this army of frogs form three orderly columns using a combination of flex-direction and flex-wrap.

#pond {
display: flex;
flex-direction: column;
flex-wrap: wrap;
}

Level 20

The two properties flex-direction and flex-wrap are used so often together that the shorthand property flex-flow was created to combine them. This shorthand property accepts the value of the two properties separated by a space.

For example, you can use flex-flow: row wrap to set rows and wrap them.

Try using flex-flow to repeat the previous level.

#pond {
display: flex;
flex-flow: column wrap
}

Level 21

The frogs are spread all over the pond, but the lilypads are bunched at the top. You can use align-content to set how multiple lines are spaced apart from each other. This property takes the following values:

flex-start: Lines are packed at the top of the container. flex-end: Lines are packed at the bottom of the container. center: Lines are packed at the vertical center of the container. space-between: Lines display with equal spacing between them. space-around: Lines display with equal spacing around them. stretch: Lines are stretched to fit the container. This can be confusing, but align-content determines the spacing between lines, while align-items determines how the items as a whole are aligned within the container. When there is only one line, align-content has no effect.

#pond {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}

Level 22

Now the current has bunched the lilypads at the bottom. Use align-content to guide the frogs there.

#pond {
display: flex;
flex-wrap: wrap;
align-content: flex-end;
}

Level 23

The frogs have had a party, but it is time to go home. Use a combination of flex-direction and align-content to get them to their lilypads.

#pond {
display: flex;
flex-wrap: wrap;
flex-direction: column-reverse;
align-content: center;
}

Level 24

Bring the frogs home one last time by using the CSS properties you've learned:

justify-content:Aligns flex items along the main axis.

flex-start (default) flex-end center space-between space-around space-evenly

align-items:Aligns flex items along the main axis.

flex-start (default) flex-end center space-between space-around space-evenly

flex-direction:Aligns flex items along the main axis.

flex-start (default) flex-end center space-between space-around space-evenly

order:Aligns flex items along the main axis.

flex-start (default) flex-end center space-between space-around space-evenly

align-self:Aligns flex items along the main axis.

flex-start (default) flex-end center space-between space-around space-evenly

flex-wrap:Aligns flex items along the main axis.

flex-start (default) flex-end center space-between space-around space-evenly

flex-flow:Aligns flex items along the main axis.

flex-start (default) flex-end center space-between space-around space-evenly

align-content:Aligns flex items along the main axis.

flex-start (default) flex-end center space-between space-around space-evenly

#pond {
display: flex;
flex-wrap: wrap-reverse;
justify-content: center;
align-content: space-between;
flex-direction: column-reverse;
}

參考文件

  1. flexboxfroggy

Grid Garden 入門教學筆記 | 學習筆記

· 閱讀時間約 11 分鐘
kdchang

Grid(Grid Layout)是一種 CSS3 佈局模式,專門用來設計一維的彈性佈局,適用於水平或垂直排列元素,使網頁排版更加靈活且易於維護。

Grid Garden 透過花圃澆花遊戲化方式去介紹 Grid 使用方式

Level 1

Welcome to Grid Garden, where you write CSS code to grow your carrot garden! Water only the areas that have carrots by using the grid-column-start property.

For example, grid-column-start: 3; will water the area starting at the 3rd vertical grid line, which is another way of saying the 3rd vertical border from the left in the grid.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-column-start: 3;
}

Level 2

Uh oh, looks like weeds are growing in the corner of your garden. Use grid-column-start to poison them. Note that the weeds start at the 5th vertical grid line.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#poison {
grid-column-start: 5;
}

Level 3

When grid-column-start is used alone, the grid item by default will span exactly one column. However, you can extend the item across multiple columns by adding the grid-column-end property.

Using grid-column-end, water all of your carrots while avoiding the dirt. We don't want to waste any water! Note that the carrots start at the 1st vertical grid line and end at the 4th.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-column-start: 1;
grid-column-end: 4;
}

Level 4

When pairing grid-column-start and grid-column-end, you might assume that the end value has to be greater than the start value. But this turns out not the case!

Try setting grid-column-end to a value less than 5 to water your carrots.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-column-start: 5;
grid-column-end: 2;
}

Level 5

If you want to count grid lines from the right instead of the left, you can give grid-column-start and grid-column-end negative values. For example, you can set it to -1 to specify the first grid line from the right.

Try setting grid-column-end to a negative value.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-column-start: 1;
grid-column-end: -2;
}

Level 6

Now try setting grid-column-start to a negative value.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#poison {
grid-column-start: 4;
}

Level 7

Instead of defining a grid item based on the start and end positions of the grid lines, you can define it based on your desired column width using the span keyword. Keep in mind that span only works with positive values.

For example, water these carrots with the rule grid-column-end: span 2;.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-column-start: 2;
grid-column-end: 4;
}

Level 8

Try using grid-column-end with the span keyword again to water your carrots.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-column-start: 1;
grid-column-end: 6;
}

Level 9

You can also use the span keyword with grid-column-start to set your item's width relative to the end position.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-column-start: 3;
grid-column-end: 6;
}

Level 10

Typing both grid-column-start and grid-column-end every time can get tiring. Fortunately, grid-column is a shorthand property that can accept both values at once, separated by a slash.

For example, grid-column: 2 / 4; will set the grid item to start on the 2nd vertical grid line and end on the 4th grid line.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-column: 4/6;
}

Level 11

Try using grid-column to water these carrots. The span keyword also works with this shorthand property so give it a try!

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-column: 2/5;
}

Level 12

One of the things that sets CSS grids apart from flexbox is that you can easily position items in two dimensions: columns and rows. grid-row-start works much like grid-column-start except along the vertical axis.

Use grid-row-start to water these carrots.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-row-start: 3;
}

Level 13

Now give the shorthand property grid-row a try.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-row: 3/6;
}

Level 14

Use grid-column and grid-row at the same time to set position in both dimensions.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#poison {
grid-column: 2;
grid-row: 5;
}

Level 15

You can also use grid-column and grid-row together to span larger areas within the grid. Give it a try!

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-column: 2/6;
grid-row: 1/6;
}

Level 16

If typing out both grid-column and grid-row is too much for you, there's yet another shorthand for that. grid-area accepts four values separated by slashes: grid-row-start, grid-column-start, grid-row-end, followed by grid-column-end.

One example of this would be grid-area: 1 / 1 / 3 / 6;.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-area: 1 / 2 / 4 / 6;
}

Level 17

How about multiple items? You can overlap them without any trouble. Use grid-area to define a second area that covers all of the unwatered carrots.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water-1 {
grid-area: 1 / 4 / 6 / 5;
}

#water-2 {
grid-area: 2 / 3 / 5 / 6;
}

Level 18

If grid items aren't explicitly placed with grid-area, grid-column, grid-row, etc., they are automatically placed according to their order in the source code. We can override this using the order property, which is one of the advantages of grid over table-based layout.

By default, all grid items have an order of 0, but this can be set to any positive or negative value, similar to z-index.

Right now, the carrots in the second column are being poisoned and the weeds in the last column are being watered. Change the order value of the poison to fix this right away!

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

.water {
order: 0;
}

#poison {
order: 1;
}

Level 19

Now the water and poison are alternating, even though all of the weeds are at the start of your garden. Set the order of the poisons to remedy this.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

.water {
order: 0;
}

.poison {
order: -1;
}

Level 20

Up to this point, you've had your garden set up as a grid with five columns, each 20% of the full width, and five rows, each 20% of the full height.

This was done with the rules grid-template-columns: 20% 20% 20% 20% 20%; and grid-template-rows: 20% 20% 20% 20% 20%; Each rule has five values which create five columns, each set to 20% of the overall width of the garden.

But you can set the grid up however you like. Give grid-template-columns a new value to water your carrots. You'll want to set the width of the 1st column to be 50%.

#garden {
display: grid;
grid-template-columns: 50% 10% 20% 20%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-column: 1;
grid-row: 1;
}

Level 21

Specifying a bunch of columns with identical widths can get tedious. Luckily there's a repeat function to help with that.

For example, we previously defined five 20% columns with the rule grid-template-columns: 20% 20% 20% 20% 20%;. This can be simplified as grid-template-columns: repeat(5, 20%);

Using grid-template-columns with the repeat function, create eight columns each with 12.5% width. This way you won't overwater your garden.

#garden {
display: grid;
grid-template-columns: repeat(1, 12.5%);
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-column: 1;
grid-row: 1;
}

Level 22

grid-template-columns doesn't just accept values in percentages, but also length units like pixels and ems. You can even mix different units together.

Here, set three columns to 100px, 3em, and 40% respectively.

#garden {
display: grid;
grid-template-columns: 100px 3em 40%;
grid-template-rows: 20% 20% 20% 20% 20%;
}

Level 23

Grid also introduces a new unit, the fractional fr. Each fr unit allocates one share of the available space. For example, if two elements are set to 1fr and 3fr respectively, the space is divided into 4 equal shares; the first element occupies 1/4 and the second element 3/4 of any leftover space.

Here, weeds make up the left 1/6 of your first row and carrots the remaining 5/6. Create two columns with these widths using fr units.

#garden {
display: grid;
grid-template-columns: 1fr 5fr;
grid-template-rows: 20% 20% 20% 20% 20%;
}

Level 24

When columns are set with pixels, percentages, or ems, any other columns set with fr will divvy up the space that's left over.

Here the carrots form a 50 pixel column on the left, and the weeds a 50 pixel column on the right. With grid-template-columns, create these two columns, and use fr to make three more columns that take up the remaining space in between.

#garden {
display: grid;
grid-template-columns: 50px 1fr 1fr 1fr 50px;
grid-template-rows: 20% 20% 20% 20% 20%;
}

#water {
grid-area: 1 / 1 / 6 / 2;
}

#poison {
grid-area: 1 / 5 / 6 / 6;
}

Level 25

Now there is a 75 pixel column of weeds on the left side of your garden. 3/5 of the remaining space is growing carrots, while 2/5 has been overrun with weeds.

Use grid-template-columns with a combination of px and fr units to make the necessary columns.

#garden {
display: grid;
grid-template-columns: 75px 3fr 2fr;
grid-template-rows: 100%;
}

Level 26

grid-template-rows works much the same as grid-template-columns.

Use grid-template-rows to water all but the top 50 pixels of your garden. Note that the water is set to fill only your 5th row, so you'll need to create 5 rows in total.

#garden {
display: grid;
grid-template-columns: 20% 20% 20% 20% 20%;
grid-template-rows: 12.5px 12.5px 12.5px 12.5px;
}

#water {
grid-column: 1 / 6;
grid-row: 5 / 6;
}

Level 27

grid-template is a shorthand property that combines grid-template-rows and grid-template-columns.

For example, grid-template: 50% 50% / 200px; will create a grid with two rows that are 50% each, and one column that is 200 pixels wide.

Try using grid-template to water an area that includes the top 60% and left 200 pixels of your garden.

#garden {
display: grid;
grid-template: 60% / 200px;
}

#water {
grid-column: 1;
grid-row: 1;
}

Level 28

Your garden is looking great. Here you've left a 50 pixel path at the bottom of your garden and filled the rest with carrots.

Unfortunately, the left 20% of your carrots have been overrun with weeds. Use CSS grid one last time to treat your garden.

#garden {
display: grid;
grid-template: 1fr 50px / 1fr 4fr;
}

參考文件

  1. Playing CSS Grid Garden with Answers Explained

WebSocket 入門教學筆記(ESM 模組版) | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

在現代前端與全端開發中,許多應用需求都仰賴即時資料傳輸,例如即時聊天、線上協作、股票報價、IoT 裝置通訊等。而傳統的 HTTP 協定採用「請求-回應」模式,並不適合雙向即時通訊。這時,WebSocket 協定就成為更有效率的替代方案。

WebSocket 是 HTML5 標準的一部分,它允許在用戶端與伺服器之間建立一條持久的雙向連線。這使得伺服器能即時推播資料給客戶端,而非只能等待客戶端請求,適合實作低延遲、高互動的應用場景。

本教學將透過現代的 ESM(ECMAScript Module)語法,介紹如何在 Node.js 環境中建立 WebSocket 應用,並搭配 HTML 客戶端示範雙向通訊流程。


重點摘要

  • WebSocket 是什麼? 一種基於 TCP 的雙向通訊協定,可在瀏覽器與伺服器之間建立長連線。

  • 優點

    • 持久連線,不需每次重複建立與關閉連線
    • 支援伺服器主動推播資料給客戶端
    • 節省頻寬與延遲
    • 適用於高即時性場景
  • WebSocket vs HTTP

    特性HTTPWebSocket
    通訊模式客戶端請求 → 伺服器回應雙向(Client ↔ Server)
    連線型態短連線長連線(持續開啟)
    傳輸效率較低(含 header)高效(精簡封包)
  • 常見應用情境

    • 即時聊天系統
    • 線上遊戲同步
    • IoT 裝置狀態回報
    • 即時儀表板或股市報價
  • 基本 API(前端)

    • new WebSocket(url):建立連線
    • socket.onopen:連線成功
    • socket.send():傳送資料
    • socket.onmessage:接收訊息
    • socket.onclose:連線關閉
    • socket.onerror:錯誤處理

實作範例(Node.js + ESM + 原生 HTML)

一、專案初始化

建立一個新的資料夾並初始化:

mkdir websocket-esm-demo
cd websocket-esm-demo
npm init -y
npm install ws

修改 package.json 以啟用 ESM 模組支援:

{
"name": "websocket-esm-demo",
"version": "1.0.0",
"type": "module",
"dependencies": {
"ws": "^8.0.0"
}
}

二、伺服器端(使用 ESM 模組語法)

建立 server.js

// server.js
import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', (ws) => {
console.log('用戶已連線');

ws.on('message', (message) => {
console.log(`收到用戶訊息:${message}`);
ws.send(`伺服器收到:${message}`);
});

ws.on('close', () => {
console.log('連線已關閉');
});

ws.on('error', (err) => {
console.error('WebSocket 錯誤:', err);
});
});

console.log('WebSocket 伺服器啟動於 ws://localhost:8080');

三、客戶端 HTML(WebSocket 客戶端)

建立 index.html

<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8" />
<title>WebSocket ESM 範例</title>
</head>
<body>
<h1>WebSocket 即時通訊示範</h1>
<input type="text" id="input" placeholder="輸入訊息" />
<button onclick="sendMessage()">送出</button>
<ul id="log"></ul>

<script>
const socket = new WebSocket('ws://localhost:8080');

socket.addEventListener('open', () => {
logMessage('已連線至伺服器');
});

socket.addEventListener('message', (event) => {
logMessage(`來自伺服器:${event.data}`);
});

socket.addEventListener('close', () => {
logMessage('連線已關閉');
});

socket.addEventListener('error', () => {
logMessage('連線發生錯誤');
});

function sendMessage() {
const input = document.getElementById('input');
socket.send(input.value);
logMessage(`你說:${input.value}`);
input.value = '';
}

function logMessage(msg) {
const li = document.createElement('li');
li.textContent = msg;
document.getElementById('log').appendChild(li);
}
</script>
</body>
</html>

四、執行與測試

  1. 啟動伺服器:
node server.js
  1. 打開 index.html(直接用瀏覽器開啟或用 VSCode Live Server 插件)
  2. 在輸入框輸入訊息,點擊「送出」,觀察瀏覽器與後端終端機回應。

補充與進階建議

  • 安全性: 若部署在生產環境,應使用 wss://(WebSocket over TLS)取代 ws://
  • 重連策略: 真實場景下需考慮自動重連機制(如斷線重試)。
  • 認證機制: WebSocket 不支援標準 HTTP 標頭傳遞 JWT,通常可透過 URL query 傳 token,或搭配 Session Cookie。
  • 整合框架: 可結合 Express、Koa、Fastify 使用同一個 HTTP 伺服器提供 HTTP 與 WS。

總結

WebSocket 是實現現代即時網頁應用的重要基礎建設,能提供更快、更輕量的資料通訊方式。透過本文,你應該已能建立一個使用 ESM 寫法的簡單 WebSocket 應用,並了解其基本運作流程。未來你可以進一步探索如 Socket.IO、SignalR 或 WebRTC 等進階解決方案,以支援更多功能與兼容性需求。

HTML & CSS 切版入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

HTML(HyperText Markup Language)是網頁的基礎結構,透過標籤(Tag)定義不同的內容與結構。

1.1 常見標籤

  • <!DOCTYPE html>:宣告 HTML 文件類型。
  • <html>:HTML 文件的根標籤。
  • <head>:包含頁面設定、SEO 資訊、CSS 連結等。
  • <title>:設定網頁標題。
  • <body>:放置頁面可見內容。
  • <h1> ~ <h6>:標題。
  • <p>:段落。
  • <a>:超連結。
  • <img>:圖片。
  • <ul><ol><li>:無序與有序列表。
  • <div>:區塊。
  • <span>:行內元素。
  • <table><tr><td>:表格。

1.2 HTML5 新增語意標籤

  • <header>:頁首。
  • <nav>:導航。
  • <section>:區段。
  • <article>:獨立內容。
  • <aside>:側邊欄。
  • <footer>:頁尾。

2. CSS 基礎概念

CSS(Cascading Style Sheets)用於設計與美化 HTML 元素。

2.1 CSS 引入方式

  • 內嵌樣式(Inline Style):直接寫在 HTML 標籤內,例如:
    <p style="color: red; font-size: 16px;">這是一段文字</p>
  • 內部樣式(Internal Style):寫在 <style> 標籤內,例如:
    <style>
    p { color: blue; font-size: 18px; }
    </style>
  • 外部樣式(External Style):將 CSS 放入 .css 文件中,再用 <link> 連結,例如:
    <link rel="stylesheet" href="styles.css">

2.2 CSS 選擇器

  • 標籤選擇器:影響所有相同標籤,例如:
    p { color: green; }
  • 類別選擇器(Class):適用於多個元素,例如:
    .box { background-color: yellow; }
    <div class="box">內容</div>
  • ID 選擇器:適用於單一元素,例如:
    #header { font-size: 24px; }
    <h1 id="header">標題</h1>
  • 後代選擇器:選擇特定層級內的元素,例如:
    div p { color: blue; }

2.3 盒模型(Box Model)

盒模型包含四個部分:

  1. Content(內容區域)。
  2. Padding(內距,內容與邊框之間的距離)。
  3. Border(邊框)。
  4. Margin(外距,與其他元素的距離)。

範例:

.box {
width: 200px;
height: 100px;
padding: 20px;
border: 2px solid black;
margin: 10px;
}

3. 常見版型切版技巧

3.1 Flexbox 佈局

Flexbox 用於彈性排列子元素。

.container {
display: flex;
justify-content: space-between;
align-items: center;
}

3.2 Grid 佈局

Grid 提供更強大的網格系統。

.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
}

3.3 響應式設計(RWD)

使用 @media 來適應不同螢幕尺寸。

@media (max-width: 768px) {
.container {
flex-direction: column;
}
}

4. 實戰案例:基本網頁切版

HTML

<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>簡單網頁</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header>
<h1>我的網站</h1>
</header>
<nav>
<ul>
<li><a href="#">首頁</a></li>
<li><a href="#">關於</a></li>
<li><a href="#">聯絡</a></li>
</ul>
</nav>
<section class="content">
<p>這是一個簡單的 HTML & CSS 切版示範。</p>
</section>
</body>
</html>

CSS

body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
header {
background: #333;
color: white;
text-align: center;
padding: 10px;
}
nav ul {
list-style: none;
display: flex;
justify-content: center;
padding: 0;
}
nav ul li {
margin: 0 15px;
}
nav a {
text-decoration: none;
color: black;
}
.content {
max-width: 800px;
margin: auto;
padding: 20px;
}

5. 總結

透過學習 HTML 與 CSS,可以建立結構清晰、外觀美觀的網頁。建議多加練習不同的切版方式,如 Flexbox 和 Grid,並運用 RWD 技巧來提升適應性,讓網站在不同裝置上都能有良好的呈現效果。

Lodash 介紹與入門教學筆記 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

前言

在 JavaScript 的開發中,資料處理與函式操作經常需要處理陣列、物件、字串的轉換、搜尋與過濾等需求。儘管 ES6+ 提供了不少內建函式(如 mapfilterreduce 等),但仍有許多情境需要進階或更簡潔的處理方式。

這時候,Lodash 就是一個非常實用的工具庫。它是一個提供大量實用函式的 JavaScript 函式庫,能幫助開發者更方便地進行資料操作、提升開發效率與可讀性。


重點摘要

  • Lodash 是什麼:

    • 一個現代 JavaScript 實用工具庫,專注於資料處理、陣列與物件操作。
    • 模組化設計,可依照需求引入特定函式,減少最終 bundle 大小。
  • 使用方式:

    • 安裝方式:

      npm install lodash
    • 引入方式(使用 ES6 模組):

      import _ from 'lodash';
  • 常用函式分類:

    • 陣列操作:chunkcompactdifferenceuniqflatten
    • 物件操作:getsetmergepickomit
    • 函式處理:debouncethrottleonce
    • 數學與邏輯判斷:isEmptyisEqualclamp
    • 字串操作:camelCasekebabCasestartCase
  • 優點:

    • API 設計一致,學習曲線平緩
    • 可與原生 JS 無縫搭配
    • 處理巢狀資料與深層結構特別方便

實際範例

以下透過幾個實際範例來展示 Lodash 的常見使用情境與語法。

1. 陣列切分:_.chunk

將一個陣列依照固定大小切成多個子陣列。

import _ from 'lodash';

const arr = [1, 2, 3, 4, 5, 6];
const result = _.chunk(arr, 2);
// 輸出:[[1, 2], [3, 4], [5, 6]]

2. 去除 falsy 值:_.compact

移除陣列中的 falsenull0""undefinedNaN

const arr = [0, 1, false, 2, '', 3];
const result = _.compact(arr);
// 輸出:[1, 2, 3]

3. 陣列差集:_.difference

找出第一個陣列中,不存在於其他陣列的元素。

const result = _.difference([1, 2, 3, 4], [2, 3]);
// 輸出:[1, 4]

4. 去除重複值:_.uniq

回傳一個不含重複值的新陣列。

const result = _.uniq([2, 1, 2]);
// 輸出:[2, 1]

5. 平坦化陣列:_.flatten

將多維陣列的一層扁平化。

const result = _.flatten([1, [2, [3, [4]]]]);
// 輸出:[1, 2, [3, [4]]]

若要完全扁平化,可使用 flattenDeep

const result = _.flattenDeep([1, [2, [3, [4]]]]);
// 輸出:[1, 2, 3, 4]

6. 取得物件巢狀值:_.get

避免使用多層 obj && obj.prop 判斷。

const obj = { a: { b: { c: 42 } } };
const result = _.get(obj, 'a.b.c');
// 輸出:42

可設定預設值:

const result = _.get(obj, 'a.b.d', 'not found');
// 輸出:'not found'

7. 防抖動(Debounce):_.debounce

常用於輸入框搜尋防抖,例如搜尋建議:

const search = _.debounce((query) => {
console.log('搜尋中:', query);
}, 300);

search('a');
search('ab');
search('abc'); // 只會觸發這次

8. 函式執行一次:_.once

保證某個函式只執行一次。

const init = _.once(() => {
console.log('只會執行一次的初始化');
});

init();
init();
// 輸出:只會執行一次的初始化

總結

Lodash 是一個穩定、完整、社群活躍的 JavaScript 工具函式庫,適合用於各種日常資料處理場景。尤其當面對資料轉換、巢狀結構處理、函式優化(如 debounce/throttle)等問題時,Lodash 提供了直觀且一致的解法。

若在專案中引入 Lodash 時,建議採用模組化方式僅引入需要的函式,或使用 lodash-es(es module 版本) 搭配 Tree Shaking,以減少最終輸出大小。

Ruby 語言介紹與入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

Ruby 是一種純物件導向、動態型別且語法極為直覺易讀的程式語言,由日本程式設計師「松本行弘(Yukihiro Matsumoto)」於 1995 年開發推出。Ruby 的開發理念是讓程式設計變得「快樂」,它重視人類可讀性多於機器效率,因此語法貼近自然語言、少有繁複標點,受到許多開發者的喜愛。

Ruby 最廣為人知的應用即是 Web 框架 Ruby on Rails,但 Ruby 本身也可應用於腳本撰寫、自動化流程、系統工具與 API 開發。


重點摘要

  • 語言特色:

    • 純物件導向:數字、字串、布林值等皆為物件。
    • 語法簡潔:接近自然語言,易於閱讀與維護。
    • 動態語言:變數無需預先定義型別。
    • 彈性高:支援 metaprogramming、區塊、lambda。
    • 龐大標準函式庫與活躍社群(Gem 套件系統)。
  • 應用領域:

    • Web 開發(搭配 Rails 或 Sinatra)
    • 系統腳本與自動化任務
    • 命令列工具(如 Jekyll、CocoaPods)
    • 測試工具(RSpec、Cucumber)
  • 安裝方式:

    • 建議使用 rbenvRVM 管理版本。
    • 檢查版本:ruby -v
    • 執行程式碼:ruby 檔名.rbirb(互動環境)

語法入門實例

以下是一些 Ruby 基本語法與應用的實際範例,幫助理解語言結構與特性。


1. Hello World

puts "Hello, world!"

使用 puts 印出字串到終端機。無需加分號,換行即表示語句結束。


2. 變數與資料型別

name = "Alice"
age = 30
pi = 3.14
is_admin = true

Ruby 不需宣告型別,會根據賦值自動推斷。


3. 字串操作

greeting = "Hello"
name = "Bob"

puts "#{greeting}, #{name}!" # 字串插值
puts greeting.upcase # => "HELLO"
puts name.reverse # => "boB"

Ruby 字串是物件,具有許多內建方法如 .upcase.reverse


4. 陣列與雜湊(Hash)

arr = [1, 2, 3]
arr << 4 # 加入元素
puts arr[2] # => 3

person = { name: "Tom", age: 25 }
puts person[:name] # => "Tom"

陣列可任意擴充,雜湊(Hash)類似於 JavaScript 的物件或 Python 的字典。


5. 控制流程

score = 85

if score >= 90
puts "優等"
elsif score >= 60
puts "及格"
else
puts "不及格"
end

條件式使用 ifelsifelse,並以 end 結尾。邏輯清楚、語法簡潔。


6. 迴圈與區塊

(1..5).each do |i|
puts "第 #{i} 次"
end

(1..5) 表示範圍,.each 是集合的遍歷方法,do ... end 是 Ruby 的區塊(block)。


7. 方法定義

def greet(name)
"Hello, #{name}"
end

puts greet("Ruby")

方法以 def 開頭,end 結尾,回傳值可省略 return,預設回傳最後一行的值。


8. 類別與物件導向

class Person
attr_accessor :name

def initialize(name)
@name = name
end

def greet
"Hi, I'm #{@name}"
end
end

p = Person.new("Alice")
puts p.greet # => Hi, I'm Alice

Ruby 是純物件導向語言,每個資料皆為物件。類別可包含建構子 initialize 與方法,使用 @變數 表示實例變數。


9. 條件表達式簡寫

puts "你已成年" if age >= 18

Ruby 支援簡化語法,條件可以寫在語句之後,提高可讀性。


10. 匿名函式與 Lambda

double = ->(x) { x * 2 }
puts double.call(5) # => 10

Lambda 是匿名函式,可用 ->lambda 定義,用 .call 呼叫。


Ruby 工具與社群資源

  • Gem 套件系統gem install 套件名,安裝第三方函式庫。

  • irb:互動式 Ruby shell,適合練習語法。

  • RDoc:內建文件系統。

  • 社群網站


總結

Ruby 是一門設計優雅、語法簡潔、表達力強的程式語言,特別適合用於快速開發、腳本處理與 Web 應用。其強大的物件導向特性與彈性語法,讓程式設計不僅實用,更具美感。

如果你是第一次接觸和學習程式語言,Ruby 是一個相當友善的選擇。如果你是經驗豐富的開發者,也能從 Ruby 中找到清晰表達與高層次抽象的樂趣。

Ruby on Rails 介紹與入門教學筆記 | 學習筆記

· 閱讀時間約 4 分鐘
kdchang

前言

Ruby on Rails(簡稱 Rails) 是一套以 Ruby 語言 為基礎所打造的全端開發框架,自 2004 年問世以來,因其「約定大於配置(Convention over Configuration)」與「不重複自己(Don't Repeat Yourself, DRY)」的設計理念,成為 Web 開發世界中極具代表性的框架之一。

Rails 強調快速開發與清晰結構,幫助開發者迅速建構可維護的應用程式,適合用於新創產品、MVP 原型開發,以及後台管理系統等場景。


重點摘要

  • 核心概念:

    • 基於 MVC 架構(Model-View-Controller)
    • 使用 Ruby 作為程式語言,語法簡潔易讀
    • 提供大量自動化工具,減少樣板程式碼
  • 主要特色:

    • Active Record:物件關聯對應(ORM)層,方便操作資料庫
    • Action View:視圖模板系統,支援 HTML + ERB(嵌入式 Ruby)
    • Action Controller:處理使用者請求、導引邏輯
    • 內建資源路由、RESTful 設計、自動產生 CRUD 操作
    • 支援開發環境分離(開發、測試、正式)
  • 優點:

    • 建立專案與開發速度快
    • 有完整且穩定的生態系與套件系統(gem)
    • 測試工具完善,支援 TDD/BDD 開發流程
    • 社群活躍,有大量教學資源
  • 缺點:

    • 在高並發、高效能要求場景可能需額外優化
    • 有一定的學習曲線,尤其對 Ruby 不熟悉的開發者

建立第一個 Rails 專案

1. 安裝 Rails

首先需安裝 Ruby 環境,建議搭配 rbenvRVM 工具管理 Ruby 版本。安裝 Ruby 後,安裝 Rails:

gem install rails

確認版本:

rails -v

2. 建立新專案

rails new blog
cd blog

rails new 指令會建立一個完整的 Rails 專案結構,包含資料夾、設定檔、樣板程式碼等。


3. 建立資料表與模型

Rails 提供 scaffold 產生器,可以一次建立 Model、Controller、View、路由與測試程式:

rails generate scaffold Post title:string body:text

這個指令會建立 posts 資源的 MVC 元件,其中 title 是字串、body 是文字欄位。

接著執行資料庫遷移:

rails db:migrate

這會根據 db/migrate 產生的檔案,建立對應的資料表。


4. 啟動伺服器並測試

rails server

開啟瀏覽器進入:

http://localhost:3000/posts

你會看到一個基本的 CRUD 界面,可以建立、查看、修改與刪除文章。這是 Rails scaffold 的成果。


MVC 架構簡介

Rails 採用 MVC 模式組織應用程式:

  • Model(模型):對應資料表與商業邏輯,使用 ActiveRecord
  • View(視圖):負責呈現 HTML 頁面,支援 ERB、HAML 等模板引擎
  • Controller(控制器):負責接收請求、操作模型、回傳視圖

範例:

# app/models/post.rb
class Post < ApplicationRecord
end

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
@posts = Post.all
end
end

# app/views/posts/index.html.erb
<h1>文章列表</h1>
<% @posts.each do |post| %>
<p><%= post.title %></p>
<% end %>

路由與 RESTful 設計

Rails 使用 config/routes.rb 設定路由。若使用 resources :posts,Rails 會自動產生如下 RESTful 路由:

HTTP 動作路徑控制器動作說明
GET/postsindex查看所有文章
GET/posts/:idshow查看單一文章
GET/posts/newnew顯示新增頁面
POST/postscreate建立文章
GET/posts/:id/editedit顯示編輯頁面
PATCH/posts/:idupdate更新文章
DELETE/posts/:iddestroy刪除文章

常用開發指令與功能

  • 建立模型:rails generate model Comment body:text
  • 建立控制器:rails generate controller Comments
  • 執行測試:rails test
  • 啟動主控台:rails console
  • 進行資料遷移:rails db:migrate

實用工具與套件(Gem)

Rails 依賴大量 gem 套件,以下是常見幾個:

功能套件名稱說明
認證登入Devise最常用的使用者認證套件
上傳檔案CarrierWave支援圖片與檔案上傳
表單處理SimpleForm增強表單設計
管理後台ActiveAdmin快速生成後台管理介面
部署工具Capistrano遠端部署自動化

結語

Ruby on Rails 是一套穩定、完整、且易於上手的全端 Web 框架。它的「約定優於配置」設計理念,讓開發者可以專注在商業邏輯與產品功能,減少重複工作。

雖然使用人數成長趨緩,但在快速開發、MVP、管理後台系統等領域仍非常實用。如果你正在尋找一套能夠快速上線且易於維護的全端框架,Rails 仍是非常值得考慮的選擇(前提是對於 Ruby 不排斥)。

Tree Shaking 介紹與入門教學筆記 | 學習筆記

· 閱讀時間約 5 分鐘
kdchang

前言

在現代前端開發中,專案的程式碼結構越來越複雜,為了提高效能與使用者體驗,減少最終打包後的 JavaScript 檔案大小成為一項重要任務。這時,「Tree Shaking」技術便扮演了關鍵角色。

Tree Shaking 是一種靜態程式碼分析技術,能夠在打包階段分析模組之間的依賴關係,移除未被實際使用的程式碼(dead code)。這不僅能優化網站載入速度,也讓程式碼更精簡、維護性更高。


重點摘要

  • Tree Shaking 是什麼:

    • 靜態分析 JavaScript 模組的依賴關係,排除未使用的導出(export)。
    • 減少 bundle 體積,提升網站效能與載入速度。
  • 依賴條件:

    • 必須使用 ES Moduleimport / export),不適用於 CommonJS(require / module.exports)。
    • 模組必須 無副作用(side effects)
    • 打包工具必須支援 Tree Shaking(如 Webpack、Rollup、Vite)。
  • 支援工具:

    • Webpack:需設定 mode: 'production',可配合 sideEffects 設定。
    • Rollup:原生支援 Tree Shaking。
    • Vite:基於 Rollup,自然具備支援能力。
  • 副作用(Side Effects):

    • 當模組在載入時就執行會對外部環境產生影響的操作(如改寫全域物件、注入 CSS),即視為有副作用。
    • 若模組被標示為有副作用,Tree Shaking 不會移除它,即使未被使用。

實際範例

以下範例展示如何使用 Tree Shaking 與如何正確設定專案來支援這項技術。

範例 1:錯誤導入方式,導致無法 Tree Shaking

// main.js
import _ from 'lodash';

const result = _.uniq([1, 2, 2, 3]);

使用 import _ from 'lodash' 會引入整個 Lodash 函式庫,即使你只使用了 uniq 一個函式,打包結果也包含全部模組。


範例 2:正確方式,搭配 Tree Shaking 使用

npm install lodash-es
// main.js
import { uniq } from 'lodash-es';

const result = uniq([1, 2, 2, 3]);

lodash-es 是 Lodash 的 ES 模組版本,允許 Tree Shaking。打包工具會自動排除未使用的其他函式,例如 cloneDeepmerge 等,顯著降低檔案體積。


範例 3:Webpack 基礎設定

// webpack.config.js
module.exports = {
mode: 'production', // 啟用 Tree Shaking 與壓縮
optimization: {
usedExports: true, // 標記已使用的模組導出(production 模式下自動啟用)
},
};
// package.json
{
"sideEffects": false
}

這樣的設定告訴 Webpack 整個專案皆無副作用,因此可放心進行模組的 Tree Shaking。

若有些檔案需保留副作用(如樣式導入),可以用陣列方式指定:

{
"sideEffects": ["./src/styles.css"]
}

範例 4:Rollup 自動 Tree Shaking

Rollup 原生支援 Tree Shaking,只需使用 ES Module 即可:

// rollup.config.js
import { defineConfig } from 'rollup';
import babel from '@rollup/plugin-babel';

export default defineConfig({
input: 'src/main.js',
output: {
file: 'dist/bundle.js',
format: 'esm',
},
plugins: [babel({ babelHelpers: 'bundled' })],
});

Rollup 會分析哪些函式實際被使用,未使用的會自動排除。


建議實務策略

  • 使用模組化的函式庫版本,如 lodash-esdate-fns(每個功能一個函式)。
  • 避免使用 CommonJS 套件或 require() 語法,否則無法靜態分析。
  • 定期分析 bundle 體積,使用工具如 Webpack Bundle Analyzer
  • 結合動態 import,搭配 lazy loading 拆分 chunk 可進一步優化效能。

補充副作用(side effects)

在 JavaScript 中,副作用(side effects) 指的是 當程式碼執行時,對外部環境造成改變或依賴外部狀態 的行為。副作用是 Tree Shaking 技術的一大考量點,因為具有副作用的程式碼通常無法被安全地移除,即使它沒有被「明確使用」。


一、什麼是副作用?

常見的副作用行為包括:

  • 修改全域變數或物件
  • DOM 操作
  • 寫入檔案 / 本地儲存
  • 發送 API 請求
  • 設定計時器(setTimeout / setInterval
  • console.log、alert 等印出操作
  • 匯入會執行即時副作用的模組(例如樣式、polyfill)

二、舉例說明

✅ 無副作用(pure function)

export function add(a, b) {
return a + b;
}

這個函式純粹根據輸入產出結果,不會影響其他程式碼,可被安全地移除或優化。


❌ 有副作用

console.log('載入模組時執行'); // 印出訊息就是副作用

document.body.style.backgroundColor = 'black'; // 操作 DOM

export function add(a, b) {
return a + b;
}

即使 add() 函式未使用,只要這個模組被 import,就會執行 console.logdocument 操作,因此打包工具不敢移除它(怕破壞行為)。


三、Tree Shaking 為什麼在意副作用?

因為 Tree Shaking 的目標是 移除未使用的程式碼,但:

  • 如果一段程式碼 可能有副作用,打包工具 不敢隨便刪除,怕造成功能錯誤。
  • 所以需要明確標示模組是否有副作用。

四、如何告訴打包工具副作用資訊?

在專案或套件的 package.json 加入:

{
"sideEffects": false
}

代表:整個專案或套件中,所有模組都無副作用,Webpack/Rollup 就可以放心 Tree Shake。

若某些檔案(如樣式)確實有副作用,可以指定:

{
"sideEffects": ["./src/styles.css"]
}

五、實務建議

  • 撰寫可預測的 純函式(Pure Function),避免模組執行就改變外部狀態。
  • 匯入函式時盡量使用模組化方式,例如只 import 使用到的功能。
  • 避免使用模組中會「立即執行」某些操作的套件,若無法避免,務必在 sideEffects 中設定。

副作用本身並不是壞事,許多實用功能(像是發送請求、改變頁面樣式)本來就需要副作用。但在做效能優化時,了解副作用的存在與影響是 Tree Shaking 成功的關鍵

若模組沒有副作用,且未被使用,就可以安全地刪除。這就是 Tree Shaking 的核心機制。

總結

Tree Shaking 是現代前端打包流程中的一項關鍵技術,對於大型應用或需要精細資源管理的專案尤其重要。透過正確地使用 ES 模組、無副作用模組設計與支援工具設定,開發者可以有效刪除未使用的程式碼,提升效能、加快載入速度,並優化使用者體驗。

在實務專案中,建議開發者持續追蹤打包結果,並使用具備良好模組結構與 Tree Shaking 支援的函式庫,以保持專案維持在最佳的資源狀態。