yarn 安裝

yarn

yarn 安裝指令

前置作業node 版本為14.20.1

1
2
nvm ls
nvm use 14.20.1

全局安裝

1
npm install --global yarn

檢查安裝
通過運行檢查是否安裝了 Yarn:

1
yarn --version

1.22.19

Vue & Firebase Realtime Database CRUD

新增資料夾 config/firebaseConfig.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
26
27
28
29
導入資料庫
import firebase from ‘firebase/compat/app’
import ‘firebase/compat/auth’
import ‘firebase/compat/firestore’
import ‘firebase/compat/database’

const config = {
apiKey: “apiKey”,
authDomain: “apiKey”,
databaseURL: “apiKey”,
projectId: “apiKey”,
storageBucket: “apiKey”,
messagingSenderId: “apiKey”,
appId: “apiKey”
}

firebase.initializeApp(config);
let app = null
if (!firebase.apps.length) {
app = firebase.initializeApp(firebaseConfig)
}


export const ref = firebase.database().ref()
export const firebaseAuth = firebase.auth;

export const db = firebase.database()
export const StoreDB = firebase.firestore()
export default firebase

Create

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
39
40
41
42
43
44
45
46
47
48
49
<!–訊息表單新增–>
<template>
<form @submit.prevent=”addMessage“>

<div class=”form-group”>
<label>Name:</label>
<input type=”text” class=”form-control” v-model=”message.subject”/>
</div>

<div class=”form-group”>
<label>message</label>
<input type=”text” class=”form-control” v-model=”message.desc”/>
</div>

<div class=”form-group”>
<input type=”submit” class=”btn btn-primary” value=”送出”/>
</div>

</form>

</template>

<script>
import firebase from ‘@/config/firebaseConfig.js’;
const messageRef = firebase.database().ref(‘/message/’);

export default {
data () {
return {
message:”,
}
},
methods: {
addMessage() {
//資料表 .push({ })
messageRef.push({
subject: this.subject,
desc: this.desc,
timeStamp: new Date()
})
},
}
create(){
this.addMessage();
}

}

</script>

Read

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
<template>
<div>
<ul>
<li v-for=”item in message” :key=”item.index”>
{{ item.subject }} – {{. item.desc. }}
</li>
</ul>
</div>
</template>

<script>
import firebase from ‘@/config/firebaseConfig.js’;
//資料表名稱message
const messageRef = firebase.database().ref(‘/message/’);


export default {
data () {
return {
message:”
}
},
methods: {
getMessage () {
const vm= this;
// 簡寫 messageRef.on(‘value’,(snapshot) =>{
messageRef.on(‘value’, function(snapshot) {
vm.message = snapshot.val();
})
}
},
create(){
this.getMessage () ;
}
}
</script>

Firebase 語法

ref():尋找資料庫路徑
set():新增資料
on(隨時監聽)
push – 新增資料
child 子路徑
remove 移除

SCSS 入門:變數、巢狀、混入、繼承

& ,> ,+,~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.onclick{
//自己
&.onclick{
color:green;
cursor: pointer;
}
//children-必須是親生的才可以
> .icon{
color:#2196f3;
}
//同一階的第一個
+ .content {
color:red;
}
//~,同一階-弟弟妹妹們都獲選
~ .test{
color:blue;
}

&

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.menu {
.open & {
display:flex;
color:green;
font-size:38px;
}
}
//
編譯後
.menu {
.open & {
display:flex;
color:green;
font-size:38px;
}
}

&

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
scss
.list {
color: red;

&__item {
color: cornflowerblue;

&--active {
color: blue;
}
}
}
/*編譯後
.list {
display: flex;
}

.list__item {
color: red;
}

.list__item--active {
color: blue;
}

變數 Variables:$開頭

Scss

1
2
3
4
5
6
7
$font-stack:  Helvetica, sans-serif;
$primary-color: #2196f3;

body {
font: 100% $font-stack;
color: $primary-color;
}

變數編譯後Css

1
2
3
4
5
body {
font: 100% Helvetica, sans-serif;
color: #2196f3;
}

巢狀

Scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
nav {
ul {
margin: 0;
padding: 0;
list-style: none;
}

li { display: inline-block; }

a {
display: block;
padding: 6px 12px;
text-decoration: none;
&:hover {
color: red;
}
}
}

巢狀編譯後Css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
nav ul {
margin: 0;
padding: 0;
list-style: none;
}
nav li {
display: inline-block;
}
nav a {
display: block;
padding: 6px 12px;
text-decoration: none;
}
nav a:hover {
color: red;
}

混入 Mixins

@mixin,@include

Scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 經常重複使用的樣式
@mixin transform($property) {
-webkit-transform: $property;
-ms-transform: $property;
transform: $property;
}

// 需要套用樣式的程式碼
.box {
@include transform(rotate(30deg));
}
.avatar {
@include transform(rotate(90deg));
}

混入 Mixins編譯後Css

1
2
3
4
5
6
7
8
9
10
.box {
-webkit-transform: rotate(30deg);
-ms-transform: rotate(30deg);
transform: rotate(30deg);
}
.avatar {
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}

混繼承 Extent/Inderitance

@mixin,@include

Scss

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/* This CSS will print because %message-shared is extended. */
%message-shared {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}

// This CSS won't print because %equal-heights is never extended.
%equal-heights {
display: flex;
flex-wrap: wrap;
}

.message {
@extend %message-shared;
}

.success {
@extend %message-shared;
border-color: green;
}

.error {
@extend %message-shared;
border-color: red;
}

.warning {
@extend %message-shared;
border-color: yellow;
}

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

.header {
@extend %flex-center;
background-color: red;
}

.section {
@extend %flex-center;
background-color: blue;
}

.footer {
@extend %flex-center;
background-color: green;
}

混繼承 Extent編譯後Css

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
/* This CSS will print because %message-shared is extended. */
.message, .success, .error, .warning {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}

.success {
border-color: green;
}

.error {
border-color: red;
}

.warning {
border-color: yellow;
}

.header, .section, .footer {
display: flex;
justify-content: center;
align-items: center;
}

.header {
background-color: red;
}

.section {
background-color: blue;
}

.footer {
background-color: green;
}

@mixin 和 @extend 使用

@mixin 的好處:減少重複撰寫樣式,卻也可能造成編譯後的 CSS 樣式大量重複,使檔案異常肥大。

@mixin 和 @extend 兩者的使用時機與差異

是否需傳遞參數
是否需考慮編譯後 CSS 大小

模組 Modules

編譯前 SCSS:要作為模組載入的 SCSS 檔案,名稱必須帶有底線,例如 _base.scss。

Sass 安裝

方法一

npm 安裝

建立編譯環境

1
npm install -g sass

sass –version版本號

1
sass --version

參考資料
sass 安裝

# 新增styles.scss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$primary: #30c39e;
%transition{
transition: all .3s ease;
}

.btn {
background: $primary;
@extend %transition;
&:hover {
background: darken($primary, 5%);
}
&:active {
background: darken($primary, 10%);
}
}

進入在你的scss檔案的資料夾,並執行sass watch監控sass 輸出 css
1
2
3
4
//window
sass --watch rwdScss:rwdCss

sass scss\styles.scss css\styles.css

方法二

vs-code 安裝套件:Live Sass Compiler

指定輸出CSS的路徑:

喜好設定->設定打開Setting .json

設置參數放工作區設定的大括弧內

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//css產出設定
"liveSassCompile.settings.formats": [
{
"format": "expanded",
"extensionName": ".css",//
"savePath": "/css"///輸出的路徑
},
{
"extensionName": ".min.css", //正式版副檔名為min.css
"format": "compressed", //壓縮成一行css,正式版本
"savePath": "/dist/style"
}
],

建立一個資料夾:

內有scss/style.scss

視窗下方藍色區域是否出現了一個小小的按鈕寫著Watch Sass,按下執行

畫面會新增 style.css.map
畫面會新增 style.css

Vue Google Sheet Api憑證申請與串接

ConsoleGoogle

一.申請Google Sheet Api憑證

Step1.新增專案

Step2.啟用Api服務

搜尋Google Sheet

啟用Google Sheet服務

Step3.建立憑證(api金鑰 /Oauth /服務帳戶)

api金鑰:key=API_KEY

憑證=>點選API金鑰

=>金鑰限制 => 應用程式限制 =>HTTP 參照網址 (網站)

=>網站限制 =>點選新增:新增網址

=>API 限制:不限制金鑰

如果只是使用Google Sheet 以下(Oauth,服務帳戶)非必要
Oauth



服務帳戶

Api金鑰(憑證Api金鑰)等下串接會用到

二.新增Sheet試算表 粗字體是docId

https://docs.google.com/spreadsheets/d/19YWnM9PrXXEUOcT9zWkQYpiZ1W3o6i-UToEGsUPcFcU/edit#gid=0

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
39
40
41
42
43
<template>
<div class="hello">
<table class="Lists" align="center">
<tr v-for="item in lists" :key="item">
<td v-for="j in item" :key="j">{{ j }}</td><td><i class="fa-solid fa-circle-plus"></i></td>
</tr>
</table>
</div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import axios from 'axios';

const lists = ref<any>();
const getData = async () => {
//docId=>文件id
const docId = '憑證Api金鑰';
const apiKey = '試算表的ID';
const api = `https://sheets.googleapis.com/v4/spreadsheets/${docId}/values/商品列表?alt=json&key=${apiKey}`;

await axios
.get(api)
.then(res => {
lists.value = res.data.values;
console.log(res.data);
for (var i = 0; i <= lists.value.length; i++) {
// title
var title = lists.value[0];
var objTitle = {};
for (var j = 0; j <= title.length; j++) {
objTitle = title[j]
}
}
});

}

onMounted(() => {
getData();
});
</script>

錯誤訊息大全

404

處理方式:=>網站限制 =>點選新增:新增網址 =>API 設置為不限制 =>金鑰限制 => 應用程式限制 =>HTTP 參照網址 (網站) =>網站限制 =>點選新增:新增網址 =>API 限制:不限制金鑰

403

Requests from referer \u003cempty\u003e are blocked.
處理方式:API 設置為不限制
=>API 限制:不限制金鑰

檢查你的 Sheet Id 能否正確取得資料

權限被拒絕

permissionPERMISSION DENIED
處理方式:
命名工作區

將Google Sheet 改為共用

改為 Pinia Composition API寫法

安裝 Pinia

新增stores / googlesheet.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
26
27
import { defineStore } from 'pinia';
import axios from "../utils/http";
import { ref } from 'vue';

export const useGooglesheetStore = defineStore('googlesheet', () =>
{

const getLists = ref();
const getGoogleSheetData = async() =>
{
const docId = import.meta.env.VITE_API_GOOGLE_SHEET_ID;
const apiKey = import.meta.env.VITE_API_GOOGLE_KEY;
const api = `https://sheets.googleapis.com/v4/spreadsheets/${docId}/values/商品列表?alt=json&key=${ apiKey}`;
try {
const res = await axios.get(api);
getLists.value = res.data.values;
console.log(getLists.value)
} catch (error) {
console.log(error)
}

}
return {
getGoogleSheetData,getLists
}
})

#專案內:.env.development,.env.production,.env.staging
新增

1
2
3
VITE_API_GOOGLE_KEY = GOOGLE_KEY

VITE_API_GOOGLE_SHEET_ID = GOOGLE_SHEET_ID

#頁面使用goolgesheet 數據
//引入{ storeToRefs } from ‘pinia’=>

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
<template>
<div class="hello">
<table class="Lists" align="center">
<tr v-for="item in getLists" :key="item">
<td v-for="j in item" :key="j">{{ j }}</td>
<td><i class="fa-solid fa-circle-plus"></i></td>
</tr>
</table>
</div>
</template>
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { onMounted } from 'vue';
// @ts-ignore
import { useGooglesheetStore } from '../stores/googlesheet';
const store = useGooglesheetStore();
//數據的解構
const { getLists } = storeToRefs(store);
//function 的解構
const { getGoogleSheetData } = store;

onMounted(() => {
getGoogleSheetData()
});
</script>

將你的試算表轉成Api

將您的 Google 試算表轉換為 REST API

選擇登入

Step1 建立連結


Step2 將Google Sheet改為共用,並且 貼到共用的連結表單



Vue 天氣Api

OpenWeatherMap
建立一個帳號,建立一個金鑰
https://openweathermap.org/

#使用fetch
${ import.meta.env.VITE_API_URL },${ import.meta.env.VITE_API_KEY }為環境變數
請參閱

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
<script setup lang="ts">
import { ref, onMounted } from 'vue'
interface listType {
dt?: number | null,
message?: number | null,
cnt?: number | null,
list:any[]
}

interface weatherType {
cod?: number|null,
message?: number | null,
cnt?: number | null,
list: listType[]
}

const weathers = ref<any>();
const getData = async () => {
const api = `${import.meta.env.VITE_API_URL}forecast?q=Taichung,tw&APPID=${import.meta.env.VITE_API_KEY }&lang=zh_tw&units=metric`
try {
const data = await fetch(api);
if (!data.ok) {
throw Error('fetch data 失敗');
}
if (data.status === 200) {
weathers.value = await data.json();
console.log(await data.json())
}
} catch (error) {
// throw Error(error?:any)
}
}
onMounted(() => {
getData();
});
</script>

Vite vue語法糖 axios (api 串接方式)

安裝

1
npm install axios --save

Vite 環境變數使用方式

  • 引入axios import axios from 'axios'
  • 新增interface listType
  • 定義list 為陣列 const lists = ref([]);
  • 使用 async ...await
  • try ...catch
  • 環境變數的使用:反斜線與${import.meta.env.環境變數的名稱} 例如:`${import.meta.env.VITE_API_URL}/api/questions`
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
39
40
41
42
43
44
45
46
47
48
<script setup lang="ts">
import { ref,onMounted } from 'vue'
import axios from 'axios'

interface listType {
UID?: string;
category?: string;
comment?: string;
descriptionFilterHtml?: string;
discountInfo?: string | any;
editModifyDate?: string;
endDate?: string;
hitRate?: number;
imageUrl?: string;
masterUnit?: object;
otherUnit?: object;
showInfo?: object;
showUnit?: string;
sourceWebName?: string | any;
sourceWebPromote?: string;
startDate?: string | any;
subUnit?: object;
supportUnit?: object;
version?: string;
title?: string | any;
webSales?: string;
}
const lists = ref<listType[]>([]);
const getData = async () => {
try {
const api = `${import.meta.env.VITE_API_URL}/frontsite/trans/SearchShowAction.do?method=doFindTypeJ&category=200`;
await axios.get(api)
const res = await axios.get(api);
console.log('culture', res.data, typeof res.data[0].masterUnit
, 'otherUnit', typeof res.data[0].otherUnit, 'showInfos', typeof res.data[0].showInfo);
if (res.status === 200) {
lists.value = res.data;
}

} catch (error) {
console.log(error)
}

}
onMounted(() => {
getData();
})
</script>

github 說明-axios 安裝與使用
github 說明-axios 安裝與使用

Vite 安裝與環境變數設定

Vite 6安裝
node 20.15.1
gitHub上的說明(vite ^6.0.1”)

相容性說明

Vite6 出現Pwa loader 404。

必須安裝PWA漸進式參考gitHub上的說明

相容性說明

Vite 需要 Node.js 版本 18+。 20+。 但是,某些模板需要更高的 Node.js 版本才能運作,如果您的套件管理器發出警告,請升級。

這裡使用node版本 v18.12.1 ### 終端機安裝指令
1
npm create vite@latest 資料名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

? Project name: › vite-project //專案名稱
? Select a framework: › - Use arrow-keys. Return to submit.
Vanilla
❯ Vue
React
Preact
Lit
Svelte
Others
? Select a variant: › - Use arrow-keys. Return to submit.
JavaScript
❯ TypeScript
Customize with create-vue ↗
Nuxt ↗

參考官網
1
2
3
4
cd vite-project
npm install
npm run dev

安裝完成
gitHub上的說明

gitHub上的說明(vite ^5.1.0”)

環境變數設定

修改vite.config.js 檔案,加入以下

1
2
3
4
5
6
7
8
9
10
11
import { fileURLToPath, URL } from 'node:url';
import { resolve } from 'path';
export default defineConfig({
base: "/",
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
envDir: resolve(__dirname, './env'),
})

當從命令列執行 vite 時,Vite 會自動嘗試解析專案根目錄中名為 vite.config.js 的設定檔(也支援其他 JS 和 TS 擴充功能)。
最基本的設定檔如下所示:
官網說明
完整檔案

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 { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from "node:url"
import { resolve } from "path"

export default defineConfig({
base: '/',
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
// css 配置
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/base.scss";`
}
}
}
envDir: resolve(__dirname, './env'),
});

處理錯誤:修改vite.config.ts配置與安裝 @types/node

1
npm install --save-dev @types/node

**在專案內加入env資料夾資料夾內新增:.env.development,.env.production,.env.staging

開發端環境.env.development

1
VITE_API_URL = '開發端環境Api網址'

正式主機位置.env.production

1
VITE_API_URL = '正式主機位置Api網址'

測試環境.env.staging

1
2
# QA測試環境
VITE_API_URL = 'QA測試環境Api網址'

在頁面中使用環境變數
如果在開發中會得到開發中的變數,正式環境就得到正式環境中的變數,測試環境以此類推
環境變數的命名都大寫搭配底斜線如=>VITE_API_URL=’https://xxx.tw
使用import.meta.env.VITE_API_URL

1
import.meta.env.VITE_API_URL

gitHub上的說明-新增環境變數
gitHub上的說明 頁面的使用import.meta.env.VITE_API_BASIC_URL
環境變數

TypeScript 的智能提示

在 src 目錄下建立 env.d.ts
增加 ImportMetaEnv

1
2
3
4
5
6
7
8
9
10
/// <reference types="vite/client" />

interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
// 更多環境變量...
}

interface ImportMeta {
readonly env: ImportMetaEnv
}

package.json解析

package.json概述

當執行npm install指令的時候,會根據package.json檔案中的設定自動下載所需的模組
紀錄這個專案的各種模組,以及專案的設定資訊(例如名稱、版本、授權等)

name:必填的

必須小於等於214個字符,不能以.或_開頭,不能有大寫字母,因為名稱最終成為URL的一部分因此不能包含任何非URL安全字符。

version:必填的

名稱和版本一起構成一個標識符,該標識符被認為是完全唯一的

description

是一個字串組成的數組,有助於人們在npm庫中搜尋的時候發現你的模組

private

如果這個屬性被設定為true,npm將拒絕發布它,這是為了防止一個私有模組被無意間發佈出去

homepage

項目的主頁地址

bugs

用於專案問題的回饋issue地址或一個郵箱。

license

是目前專案的協議,讓使用者知道他們有何權限來使用你的模組,以及使用該模組有哪些限制。

author

author是具體一個人,contributors表示一群人,他們都表示當前專案的共享者。同時每個人都是一個物件。具有name欄位和可選的url及email欄位。

scripts 執行腳本指令

執行腳本指令的npm命令列縮寫,例如start指定了執行npm run start時,所要執行的命令。

dependencies 指定了專案運作所依賴的模組

當安裝依賴的時候使用–save參數表示將該模組寫入dependencies屬性

devDependencies

–save-dev表示將該模組寫入devDependencies屬性。

peerDependencies

bundledDependencies 指定發佈的時候會被一起打包的模組

engines 欄位指明了該模組運作的平台

例如Node或npm的某個版本或瀏覽器。

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
{
"name": "unit_test_project", //必填的
"private": true,
"version": "0.0.0",
"description": "unit_test",
"keywords":["node.js","unit test", "theme"],
"homepage": "https://zhiqianduan.com",
"author": "larahuang",
"man" :[ "./doc/calc.1" ]
"type": "module",
"bugs": {
"url" : "https://github.com/owner/project/issues",
"email" : "project@hostname.com"
}
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"test:unit": "vitest --environment jsdom",
"preview": "vite preview"
},
"config": { "port" : "5173" },
"dependencies": {
"vue": "^3.4.31"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.5",
"@vue/test-utils": "^2.4.6",
"jsdom": "^24.1.1",
"typescript": "^5.2.2",
"vite": "^5.3.4",
"vitest": "^2.0.4",
"vue-tsc": "^2.0.24"
},
{ "engines" : { "node" : ">=0.10.3 <0.12", "npm" : "~1.0.20" } }
}

參考資料

package.json文件