# Vue.js相關
就筆記,邊開發邊寫給自己看的怕之後忘記自己在幹嘛
目前就當作插件筆記,專案做一做發現技術限太雜亂了,後面統一改用element-plus
所以底下插件基本都捨棄了QQ
說不定之後會用到所以筆記先留著
## Vue.js
前端框架,這邊主要是講Vue3的模式
和傳統HTML+JS+CSS拆分的方式不同
Vue2的方式是在各個html引入script並且new Vue寫內容
Vue3的寫法比較是用切分畫面元件(Component)的方式開發
畫面設計我很廢,基本上就是figma大致設計後,靠套版跟手動排layout
然後用元件(Component)拆分的方式開發,畫面舉例就是


把畫面拆成多個元件,個別開發組裝,至於粒度要切多小看專案規劃跟個人
https://book.vue.tw/CH2/2-1-components.html
可以看這,我覺得講蠻清楚的
元件結構是固定的
包含三個區塊,下面一個一個講
```vue=
<Template>
//寫html標籤
</Template>
<Script>
//寫vue內容
</Script>
<Style>
//寫css
</Style>
```
### Template標籤
拿來放基本html標籤,id/class之類的方式都相同

或是<font color="#f00">放其他元件</font>(像這邊是用slot標籤做插槽的方式),之間有父子元件的關係


其中<font color="#f00"><HeaderLogo /></font>也是另一個元件


如果要使用其他元件
就要用import的方式先==引入元件==(在[Script標籤](###Script標籤)內)
也可以用<font color="#f00">{{propertiesName}}</font>類似放入變數的方式

但一樣需要在[Script標籤](###Script標籤)內設定該變數(data)
Template內也可用vue的指令(Directives)
常用有這幾個
#### v-on
用來做事件觸發
<button <font color="#f00">v-on:click</font>="methodName">
可以簡寫為<font color="#f00">@click</font>
像這樣
#### v-if、v-else-if、v-else
就用來判斷,後面可以放變數(data)或method(但要返回boolean)
```vue=
<p v-if="isVisible">This is visible</p>
<p v-else>This is hidden</p>
```
#### v-for
就迴圈沒什麼好說的,不懂就問gpt
#### v-model
我都是用來綁input標籤,要在script標籤內綁定data(可以直接綁物件)
這樣就不用像傳統方式還要用id取值,雙向綁定可以直接拿變數來用(關於[data](####data))
```vue=
<template>
<input v-model="formData.username" class="inputArea" type="text" placeholder="Enter your username">
<input v-model="formData.password" class="inputArea" type="password"
placeholder="Enter your password">
</template>
<script>
export default {
data() {
return {
formData: {
username: '',
password: '',
},
};
},
}
</script>
```
#### v-bind
用來綁定標籤屬性,一樣是綁data
```vue=
<a v-bind:href="url">Link</a>
//或簡寫成以下
<a :href="url">Link</a>
```
==v-還有很多種,這邊只提比較熟悉的,後續有用到會再補上==
### Script標籤
用來寫該元件JS的地方,主要是寫Vue組件的多個選項(data、methods...)
<font color="#f00">注意:Vue2和Vue3選項(export default)寫法不同</font>
外層寫法固定都是
```vue=
<script>
import 變數名稱 from '其他組件路徑(絕對/相對)';
export default{
//各個選項
}
</script>
```
如果要在template中放入其他元件,就需要在這邊import,並且在[components](####components)掛出該變數
說明export default的部分,
包含多種選項
<font color="#f00">注意版本差別</font>
==Vue2:==
[data](####data):初始化數據
[computed](####computed):可以掛載並依照數據變動做實時計算變化
[methods](####methods):建立方法
[props](####props):接收父元件傳來的參數
[mounted](####mounted):數據初始化後依照需求做更動
[components](####components):數據初始化後依照需求做更動
~~其他我不熟悉就不說了XD~~
==Vue3:==
Vue3之後把Vue2 components以外的選項都整合到[setup](###Vue3-setup)內
關於執行順序流程先看看GPT的解釋

其內容主要就是用來導出建立的選項給template模板使用
如下
```vue=
<template>
<div>
<p>{{ message }}</p>
<p>{{ computedMessage }}</p>
<p>{{ greet() }}</p>
<p>{{ customProperty }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'Hello from data!'
};
},
computed: {
computedMessage() {
return 'Hello from computed!';
}
},
methods: {
greet() {
return 'Hello from method!';
}
},
props: {
customProperty: String
}
};
</script>
```
可供給模板使用的基本上就是這幾種
接下來說明各種選項作用
#### data
主要是初始化用的,也可以用來設預設值
```vue=
<script>
data() {
return {
status: null,
info: '',
formData: {
username: '',
password: '',
},
isChecked: false,
};
},
</script>
```
就當作是初始化建立變數吧,這樣就可以在模板中使用return內的變數
要注意的是,return內可以儲存多個,用','隔開
並且可以儲存物件
還有就是如果要在<script></script>內其他區塊使用該變數要用<font color="#f00">this.變數名</font>不然會抓不到
但Vue3棄用了this,包含[全局變數取法](###Vue3-全局變數取法)都不同
#### mounted
component元件都是先初始化data才跑其他選項
經過data初始化變數後,如果今天data資料是要從api或外部獲得
就會在mounted再賦值給變數,
還有就是需要對DOM元素做處理時會放在mounted
因為mounted主要運行時機是在整個頁面DOM和組件都渲染加載完成後才運行
```vue=
<template>
<BasicBody>
<p>{{ user }}</p>
</BasicBody>
</template>
<script>
export default {
data() {
return {
user: null,
}
},
mounted() {
// 访问或修改 DOM 元素
this.$refs.myElement.innerText = 'Updated text';
}
};
</script>
```
#### computed
```vue=
<template>
<div>
<p>原始值: {{ originalValue }}</p>
<p>计算属性: {{ computedValue }}</p>
<button @click="updateOriginalValue">更新原始值</button>
</div>
</template>
<script>
export default {
data() {
return {
originalValue: 5,
};
},
computed: {
// 计算属性的 getter
computedValue() {
// 这里的计算会基于 originalValue 进行,只有 originalValue 变化时才会重新计算
return this.originalValue * 2;
},
},
methods: {
updateOriginalValue() {
// 更新原始值,触发 computedValue 重新计算
this.originalValue += 1;
},
},
};
</script>
```
當originalValue變動時,computed掛載的computedValue 也會自動計算
當然是也可以另外存變數用事件處理並放上去就是了
#### methods
```vue=
export default {
data() {
return {
isChecked: false,
};
},
methods: {
forgetPassword() {
console.log('forgetPassword!');
},
check() {
this.isChecked = !this.isChecked;
}
}
}
```
就建立方法==注意要使用this.變數名==才能使用組件內變數
可以給事件使用
```vue=
<button @click="check">Click me</button>
```
#### props
用於父元件傳值給子元件,用<font color="#f00">:變數</font>綁==dataName==
```vue=
//父元件.vue
<template>
<div>
<MyComponent :message="parentMessage" :user="parentUser" />
</div>
</template>
<script>
import MyComponent from './MyComponent.vue';
export default {
data() {
return {
parentMessage: 'Hello from parent!',
parentUser: { name: 'John' }
};
},
components: {
MyComponent
}
};
</script>
```
子元件用props接值
```vue=
<template>
<div>
<p>{{ message }}</p>
<p>{{ user.name }}</p>
</div>
</template>
<script>
props: ['message', 'user']
</script>
```
並且可以直接使用<font color="#f00">{{ }}</font>給模板用
接值也可以指定型別和詳細校驗,預設值
```vue=
props: {
message: String,
user: {
type: Object,
required: true,
default: () => ({})
}
}
```
要注意這邊傳值只侷限父子元件如果要跨或是同層要使用[其他方式](###Vuex)
#### components
主要就是用來==在該元件內引入其他元件==
import元件之後,一定要放入這邊才能使用引入的元件

在想放的位置放上<變數名稱/ >

像這樣,元件就會被引入作為子元件
除此之外還有一種使用方式是[全局component](###入口(main.js))
可以繼續往下看會提到
### Vue3-setup
vue3之後把上面的選項都整合到這邊了
基本上除了components之外就只要寫setup了
重點就是==不管是變數還是方法都寫在setup內,並且用return給模板使用==
==computed和mounted之類的預先計算加載也都寫在setup內==
以下說明
#### data
```vue=
import { ref } from 'vue';
export default {
setup() {
const message = ref('Hello Vue!');
const count = ref(0);
return {
message,
count,
};
},
};
```
#### Methods
```vue=
import { ref } from 'vue';
export default {
setup() {
const greet = () => {
console.log('Greetings!');
};
return {
greet,
};
},
};
```
#### Computed
```vue=
import { computed, ref } from 'vue';
export default {
setup() {
const message = ref('Hello Vue!');
const reversedMessage = computed(() => message.value.split('').reverse().join(''));
return {
message,
reversedMessage,
};
},
};
```
#### onMounted (mounted)
==重點是從vue import onMounted方法==
```vue=
//重點是從vue import
import { onMounted } from 'vue';
setup() {
onMounted(() => {
console.log('Component is mounted');
});
}
```
#### props
直接把props當參數傳入
```vue=
export default {
props: {
message: String,
},
//直接把props當參數傳入
setup(props) {
//使用傳入props
console.log(props.message);
},
};
```
### Vue3-setup語法糖**
看完上面Vue2改Vue3的寫法後
有更進階的寫法,不在區分不同選項,`<script>`標籤加上==setup==,
直接建立變數或是import就可以直接在模板中使用和呼叫內容,連export default都省了
使用ref之類的vue函式也不用另外引入
`<script setup>`寫法就會自動引入
至於props,因為不用寫setup(props)所以引入不到prop參數
這邊用新函式defineProps拿props的參數
範例如下
```vue=
<template>
<div>
<p>{{ message }}</p>
<button @click="changeMessage">Change Message</button>
<ChildComponent />
<p>{{ propsVal }}</p>
</div>
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
const message = ref('Hello from setup');
const changeMessage = () => {
message.value = 'New Message';
};
const propsVal = defineProps(['propValue']);
</script>
```
### Style標籤
就寫CSS,沒什麼特別的,一樣是使用id / class寫
唯一要注意就是scope
```vue=
<template>
<div id='body'>
<slot></slot>
</div>
</template>
<style scoped>
#body {
display: flex;
flex: 50%;
}
</style>
```
vue因為元件的特性,會有多層或是共用的關係
所以style的部分是大家共用的會互相影響,不只是該元件本身
如果不想影響到其他元件,就要在style標籤加上`<style`<font color="#f00">scoped</font>`></style>`
限制只有該元件本身套用
## Vue專案結構和入口
### 結構介紹
說明完內容結構,來講入口和加載方式
建專案方式網路上都有這邊不細說

這是建立專案預設結構,這邊只提自己有用到的
==assets==放靜態資源(圖片)
==components==是最主要的,放各個元件(component)
==App.vue==是Vue應用程式的根節點
==main.js==專案入口處,初始化 Vue 應用程式
==package.json==裡面包含該專案所有插件版號和專案相關資訊,像這邊自己指定了port號
如果clone別人專案,可以進入專案package後用<font color="#f00">npm install</font>會自動捕捉這裡安裝所有需求插件

==vue.config.js==配置vue cli相關(devServer、webpack)這邊是配置了proxy

### 入口(main.js)
主要是從==main.js==開始

基本就是這樣,從vue中引入createApp並且把App.vue這個元件的根結點拿來加載初始化頁面
最後會加載進public/index.html的#app

如果今天有用到各種vue插件
就是要在==main.js==引入並使用,如下

app.use(插件變數名)
再來前面在說[components](####components)標籤時有提到,全局引入的方式
就是在main.js引入需要的component
並且用app.component(key, component);放入全局component
後續就可以在任何元件內隨意呼叫<key/ >

呼叫

像這邊使用就不用在該元件內另外import
### Vue3-全局變數取法
Vue3中棄用this,所以沒版法用`this.$route`或是`this.$axios`的方法拿到main.js配置的全局組件
取用方式變成先從vue中引用getCurrentInstance方法,拿到全局的組件集合
```vue=
//固定寫法是這樣
const { proxy } = getCurrentInstance();//獲取全局組件
```
後續就用==proxy取代this==
```vue=
info.value = proxy.$route.query.message;
proxy.$router.push({ name: 'home' });
const response = await proxy.$axios.post(config.api.client.login, formData.value);
```
## 發起請求(Axios)
### 一般配置
其實axios不是vue的一部分,發起請求也有很多方式,只是這邊是選擇用axios,也沒什麼原因,因為我只會用這個,也可以選擇用fetchApi或其他方式,看這
https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/ajax_fetch.html
以下針對axios說明
先npm安裝
```bash=
npm install axios
```
並隨意創建.js文件(這邊命名文件用Axios.js做範例),
寫入import axios from 'axios';
並且創建Axios之後export導出
```javascript=
import axios from "axios";
const instance = axios.create({
baseURL: '',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
})
export default instance;
```
這邊會獨立創建Axios.js原因是因為後續還有可能配置axios相關配置,
就統一文件不要和別的插件混再一起
再來把Axios.js引入[main.js](###入口(main.js))
給全域使用,後續就直接使用
==this.$axios==(Vue2取用方式)就好
==[Vue3取用方式](###Vue3-全局變數取法)==

呼叫方式(任何component都可)

### Axios攔截器(interceptors)
配置關於請求攔截器,類似Filter,針對請求和響應結果做處理
以下是Axios完整配置,主要是針對用戶Token做處理
詳細針對需求自己配置,用不到不配也行
```javascript=
import axios from "axios";
import router from './RouterConfig'
const instance = axios.create({
baseURL: '',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
})
instance.interceptors.request.use(
(config) => {
const matchedRoute = router.router.find(route => config.url.includes(route.path));
if(matchedRoute.meta && matchedRoute.meta.requiresAuth && matchedRoute.meta.requiresAuth === true){
const token = localStorage.getItem('token');
if(token){
config.headers['Authorization'] = `Bearer ${token}`;
}
const refreshToken = localStorage.getItem('refreshToken');
if(refreshToken){
config.headers['X-Refresh-Token'] = refreshToken;
}
}
return config;
}
)
instance.interceptors.response.use(
(response) => {
const token = response.headers['authorization'];
if(token){
localStorage.setItem('token', token.replace('Bearer ', ''))
}
console.log(response);
const refreshToken = response.headers['x-refresh-token'];
if(refreshToken){
localStorage.setItem('refreshToken', refreshToken)
}
return response;
}
);
export default instance;
```
## vue-router
### router介紹
Vue和傳統的網頁模式切換頁面不同,屬於[單頁應用(SPA)](https://zh.wikipedia.org/zh-tw/%E5%8D%95%E9%A1%B5%E5%BA%94%E7%94%A8)
簡單來說就是只使用一個頁面,透過刷新頁面內容(components元件)來實際運行
vue-router是官方的路由器,用來協助切換路由和畫面上的各個元件,如果要切換都是透過router
### router配置
先按裝
```bash=
npm install vue-router
```
這邊一樣用獨立文件的方式引入
創建任意名稱.js(這邊用RouterConfig.js為範例)
引入vue-router
```javascript=
import { createRouter, createWebHistory } from 'vue-router';
//這邊是把相關路徑存成物件變數
const router = [
{
path: '/home',
name: 'home',
components: {
//這邊使用() => import(component)的方式做懶加載
//也可以在最上面import再使用變數import
header: () => import('../components/header/HalfHeader.vue'),
body: () => import('../components/body/HomePage.vue'),
},
meta: { requiresAuth: true },
},
]
//創建Router,放入createWebHistory()和routes(路徑配置)
const r = createRouter({
history: createWebHistory(),//儲存歷史紀錄用,可以使用this.$router.back()回上一頁
routes: router,//配置路由
})
//這邊是配置在每次路由導向前會做什麼事,可不做看需求
//這邊做的是驗證所有元件如果有配置權限需求就必須登入,未登入則導向到'/login'登入頁
r.beforeEach((to, from, next) => {
const requiresAuth = to.matched.some(router => router.meta.requiresAuth) === true;
const isNotAuthenticated = !isAuthenticated();
if ((requiresAuth && isNotAuthenticated)) {
next('/login');
} else {
next();
}
});
//不重要,只是驗證是否登入
function isAuthenticated() {
const user = localStorage.getItem('user');
return user !== null && user !== undefined;
}
//最後export上面創建的創建Router變數,如果需要導出多個變數可以用{p1,p2}
//使用方式是import此元件並用變數.導出名稱(在這邊是r和router)使用,可以看main.js範例
export default {r, router};
```
其中routes內配置所有components和對應路徑屬性
主要有四個屬性
```vue=
{
path: '/home',
name: 'home',
components: {
header: () => import('../components/header/HalfHeader.vue'),
body: () => import('../components/body/HomePage.vue'),
},
meta: { requiresAuth: true },
},
```
==path==:路徑,url會顯示該路徑,<font color="#f00">轉跳可以靠path導向</font>
==name==:該路由節點名稱,<font color="#f00">轉跳可以靠name導向</font>
==components==:配置要加載的元件,可以拆分為一個頁面多個元件,分別命名
==meta==:設定路由的變數,可以做到方便區分路由權限/是否需驗證之類的事
想像routes內每個路徑是一個頁面,設定的component會被投射到APP.vue這個根結點
像這邊是配置每個頁面拆分為兩個component(header和body)
如果沒有要拆分,直接寫==components:對應元件== 就好
以下是APP.vue
```vue=
<template>
<div id="container">
<router-view name="header"/>
<router-view name="body"/>
</div>
</template>
```
name要對應到components內的自訂名稱,
如果沒有拆分就直接寫<router-view>就會把對應元件投射到這
==配置完router後記得要讓main.js使用這個元件,不然後面調用不到==

這邊.use(用router.r匹配到導出的屬性名稱r)
### 透過router轉跳頁面
<font color="#f00">要先配置完router並且配置給main.js</font>
主要使用有兩種方式
==在template中可以配置==
```vue=
//匹配路由的name
<router-link :to="{ name: 'home' }">
<img src="@/assets/icon/svg/loginPage/Register.svg">
</router-link>
//匹配路由的path
<router-link to="/home">
<img src="@/assets/icon/svg/loginPage/Register.svg">
</router-link>
```
元件初始化掛載渲染後,就會變成`<a>`標籤導向到路由指向的路徑,點擊就會重新渲染頁面
==在事件中配置==
```vue=
methods: {
doReset() {
this.$router.push({ name: "messagePage" });
}
}
```
用
==this.$router==(Vue2)
[Vue3取法](###Vue3-全局變數取法)
調用配置的路由,用.push的方式導向頁面,可以[用query攜帶參數](###query)
這邊只能匹配路由對應的name
## 非props傳參
上面有提到,父子元件間可以依靠props傳參,非父子元件的話接下來說明
詳細可看這
https://hackmd.io/@CynthiaChuang/Vue-Router-Passing-Props-to-Route-Components
傳參方式有很多種,這邊只提有用到的,==Vuex是官方推薦大型專案使用==
### query
透過[router.push](###透過router轉跳頁面)攜帶參數並轉跳
==這邊的參數會顯示在url上類似Get請求==
```vue=
methods: {
doReset() {
this.$router.push({ name: "messagePage" , query: {myData:'yourDataHere'},
});
}
}
```
並且路由修改
```javascript=
const routes = [
{
path: '',
name: '',
component: '',
props: true, // 開用 props 接收
},
];
//或是配在路徑上
const routes = [
{
path: '/home:myData',
name: '',
component: '',
},
];
```
接收方式
```vue=
mounted() {
//query後字段命名要相同名稱(data)
const receivedData = this.$route.query.myData;
}
```
### params
~~原先有,類似上面query的傳參方式,目前是已廢棄無法使用~~
https://github.com/vuejs/router/blob/main/packages/router/CHANGELOG.md#important-note
看4.1.4

### Vuex
因為不想讓參數顯示在url上,params又被廢棄,最後非父子元件傳參都使用這個方式
Vuex 是 Vue.js 官方的狀態管理庫,使用範圍蠻廣的,我是直接拿他當本地緩存或是==用來傳參==
安裝插件
```bash=
npm install vuex
```
配置,建立任意名稱.js(這邊用Store.js作為範例),後續一樣要配置給main.js
```javascript=
import { createStore } from 'vuex';
export default createStore({
state: {
dataName: '',
},
mutations: {
setDataName(state, data) {
state.dataName = data;
},
},
actions: {
setDataName({ commit }, data) {
commit('setDataName', data);
},
},
});
```
==state==:配置變數名稱,要配置了才能儲存在store內
==mutations==:就是更動state的方法,<font color="#f00">主要mutations是同步的</font>
==actions==:就是更動state的方法,<font color="#f00">主要actions是非同步的</font>
依照場景不同選擇使用
還有其他核心,這邊只提有用到的,詳細可以看
https://vuex.vuejs.org/zh/guide/#%E6%9C%80%E7%AE%80%E5%8D%95%E7%9A%84-store
配置完後引入==main.js==

後續使用方式
```vue=
<template>
<div id="app">
//使用就是呼叫$store.state.屬姓名
<p>Count: {{ $store.state.dataName }}</p>
<button @click="increment">Increment</button>
<button @click="asyncIncrement">Async Increment</button>
</div>
</template>
<script>
export default {
methods: {
increment() {
//用mutations更改內容
this.$store.commit('setDataName');
},
asyncIncrement() {
//用actions更改內容
this.$store.dispatch('setDataName');
},
},
};
</script>
```
## Vee-Validate==4==(表單驗證)
版本間差異蠻大,這邊只說4.X.X
官方文檔:
https://vee-validate.logaretm.com/v4/guide/global-validators
一個針對Vue.js的表單驗證套件,定義好表單驗證的語系(i18n)、規則後
可以用類似建立一般form表單的方式指定表單Type和指定驗證規則
如果發生錯誤,可以用類似label的方式直接在指定位置放入對應欄位的標籤,自動生成錯誤信息
從必填、長度到正則(Regex)都能驗證,也可以自訂名稱和規則

先安裝插件
```shell=
#插件本人
npm install vee-validate
#驗證規則
npm install @vee-validate/rules
#i18n庫(用不用看個人, 如果沒有語系需求可以直接自訂返回的錯誤信息)
npm install @vee-validate/i18n
```
在來就是配置使用vee-validate,有兩種方式(全域/局部)大同小異
我是直接配全域,這樣之後要用就直接使用就好,這邊也不提局部需要就網上查
首先老樣子,開一個新檔案.js(這邊範例為ValidateConfig.js)
```javascript=
// 引入VeeValidate 元件跟功能
import {
Field, Form, ErrorMessage, defineRule, configure,
} from 'vee-validate';
// 引入VeeValidate 的驗證規則
import * as AllRules from '@vee-validate/rules';
// 引入VeeValidate 的 i18n 功能
import { localize, setLocale } from '@vee-validate/i18n';
//這部分作用是找到使用者瀏覽器預設語系
const getUserLocale = () => {
let userLanguage = navigator.language || navigator.userLanguage;
let userLocale;
if (userLanguage.startsWith('zh')) {
userLocale = 'zh_TW';
} else if (userLanguage.startsWith('en')) {
userLocale = 'en';
} else if (userLanguage.startsWith('es')) {
userLocale = 'es';
} else if (userLanguage.startsWith('fr')) {
userLocale = 'fr';
} else {
userLocale = 'en';
}
return userLocale;
};
let userLocale = getUserLocale();
//動態利用使用者語系載入需要的i18n語言包
import(`@vee-validate/i18n/dist/locale/${userLocale}.json`)
.then((locale) => {
configure({
//設定使用語系對應default errorMessage
generateMessage: localize({ [userLocale]: locale.default}),
validateOnInput: true,
})
setLocale(userLocale)
})
.catch(error => {
console.error('Failed to load language file:', error);
});
//把所有規則包的驗證規則都放進defineRule(就當作是驗證器吧,自定義驗證規則也是寫在這裡面)
//這邊就是把預設的驗證規則都寫入
Object.keys(AllRules).forEach((rule) => {
defineRule(rule, AllRules[rule]);
});
//主要就是靠這三個部分,
//Form就是Form、
//Field為input欄位、
//ErrorMessage則是驗證錯誤時,錯誤信息顯示的位置(預設為<span>)
export default { Field, Form, ErrorMessage };
```
然後是自定義驗證規則的方式
在==任何==想自定義的地方(component就是寫在`<script>`內),
或是寫在全域都行(像這邊就是寫在ValidateConfig.js)
```vue=
//引入defineRule
import { defineRule } from 'vee-validate';
defineRule('自定義規則名稱', (value) => {
if(value ><= '驗證規則' || 'regex'.test(value)){
//驗證通過return true;
}
//如果return false就是會使用預設語言包的defalseMessage當錯誤信息
return false;
//如果return String就是把返回字串當錯誤信息
return "somethings wrong"
})
```
可以簡化
```javascript=
const createValidationRule = (regex, errorMessage) => (value) => regex.test(value) || errorMessage;
// 自定義規則(規則名稱, 驗證規則(包含錯誤返回信息))
defineRule('atLeastOneLowercase', createValidationRule(/.*[a-z].*/, 'Password must contain at least one lowercase letter.'));
defineRule('atLeastOneUppercase', createValidationRule(/.*[A-Z].*/, 'Password must contain at least one uppercase letter.'));
defineRule('atLeastOneNumber', createValidationRule(/.*\d.*/, 'Password must contain at least one number.'));
defineRule('noSpecialChars', createValidationRule(/^[^\s!@#$%^&*()_+={}[\]:;<>,.?~\\/-]+$/, 'Password must not contain special characters.'));
```
然後defineRule的規則是主要吃全預訂定的規則(寫在ValidateConfig.js內的)
如果是寫在個別component就只能在該component內使用
==如果規則名稱同名會互相覆蓋只留最後的==
以上就完成配置的部分
配置完後這邊是放入main.js成全域元件


後續使用就是直接`<VForm><VField><ErrorMessage>`
實際使用
```vue=
//建立form表單, @submit綁表單,會自動驗證所有規則通過才會提交
<VForm @submit="handleSubmit">
//建立input欄位,基本屬性一樣可以使用placeholder之類的屬性
//一定要指定name, rules是欄位的驗證規則,放入規則名稱(自定義或是預設規則),用'|'隔開
//規則驗證是按照順序,放前面的會先驗證所以提示字會以較前面的為優先
<VField name="username" rules="required|min:6|max:20" />
//如果驗證出錯,就會在這顯示errorMessage,要靠name綁定input欄位的name
//as後面可以放要使用什麼標籤包裝,預設是<span>
//像這邊設定最終結果就是<p>errorMessage</p>
<ErrorMessage name="username" as="p"/>
//就submit
<button type="submit" />
</VForm>
```
這裡只簡單使用,還有更多詳細方式去看官方文檔
## Bootstrap
結合Vue3使用,原先是要使用Bootstrap vue,但不知道為什麼會顯示版本問題
後面就沒解了直接改用原生Bootstrap
一樣先安裝插件(bootstrap和popperjs)
```shell=
npm install bootstrap @popperjs/core
```
然後在main.js引入
```javascript=
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.bundle.min.js';
import 'bootstrap';
```
接下來就可以直接在元件中使用相關標籤屬性
```vue=
<template>
<button v-bind:data-bs-toggle="'tooltip'" v-bind:title="'This is a tooltip'" data-bs-placement="bottom">Hover me</button>
</template>
<script setup>
import { onMounted} from 'vue';
import { Tooltip } from 'bootstrap'
//手動初始化Tooltip,不然有可能遇到bootstrap標籤的title屬性和原生html的title屬性衝突
onMounted(() => {
new Tooltip(document.body, {
selector: "[data-bs-toggle='tooltip']",
})
});
</script>
```
效果如下

## vue-chartjs(折線圖、圓餅圖、統計圖表)
僅簡單說明
詳細看官方文檔
https://vue-chart-3.netlify.app/guide/#introduction
這邊是用來做折線圖
先裝插件,vue-chartjs是依賴於chartj所以都要裝
```shell=
npm install vue-chartjs chart.js
```
然後配置預設的折線圖元件(這邊以LineChart.vue為範例)
```vue=
<template>
<Line :data="data" :options="options" />
</template>
<script>
import { Line } from "vue-chartjs";
//導入所有模塊
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
} from "chart.js";
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend
);
export default {
components: { Line },
props: ["data", "options"],
};
</script>
```
創建完後,後續使用方式如下
```vue=
<template>
<div style="flex: 3; display: flex;">
<div class="shadowBlock lineChart">
//重點是底下<LineChart>
<LineChart :data="data1" :options="chartOptions" />
</div>
</div>
</template>
<script>
//引用剛剛配置的元件
import LineChart from "@/components/body/LineChart.vue";
//要統計的資料內容
const data1 = {
labels: getLabels(),//X軸內容, 這邊是直接用當前日期回推30天
datasets: [
{
label: "Assets growing",//圖樣標題
backgroundColor: "#f87979",//點顏色
data: generateRandomData(30),//資料內容
},
],
};
//設定
const chartOptions = {
responsive: true,//自動調整大小以適應其容器的尺寸變化
maintainAspectRatio: false,//允許圖表在改變大小時不維持原始的寬高比
};
</script>
```
顯示結果如下

## vue3-loading-overlay(讀取中轉圈)

呼叫api讀取時可以擋畫面
在main.js加載,放全域
```javascript=
import Loading from 'vue3-loading-overlay'
import 'vue3-loading-overlay/dist/vue3-loading-overlay.css'// Import stylesheet
app.component('VLoading', Loading)
```
後續使用

```vue=
<template>
//擺在template任何位置
<VLoading :active="isLoading"></VLoading>
</template>
```
重點是 ==:active="isLoading"==
在需要加載讀取的地方設置boolean true
要關閉就是false


簡單使用