# Vite & Vue3 筆記
###### tags: `vite`,`vue`,`vue3`
## vite+eslint
安裝專案:
```
npm init vue@latest
```
**選擇Add Eslint跟Add prrettier的時候都要選yes**
安裝eslint plugin:
https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint
設定:
https://daveceddia.com/vscode-use-eslintrc/
```jsonld!
{
"eslint.format.enable": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
...
}
```
## example
https://codesandbox.io/s/peaceful-sara-c335p6?file=/src/plugins/dialog/alert.vue
## 套件
### bootstrap & jquery
#### 解決eslint無法編譯$的問題(待研究)
https://momane.com/how-to-use-jquery-with-typescript
```c!
npm install --save @types/jquery
```
修改tsconfig.json
```jsonld!
{
...
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"allowSyntheticDefaultImports": true, //加上這句
},
...
}
```
vue檔案
```javascript!
<script setup lang="ts">
import $ from "jquery"; //加上這行,但是編譯會出問題
import { onMounted } from "vue";
onMounted(() => {
$("#btn").click(() => {
console.log("clicked");
});
});
</script>
```
### vuetify
https://github.com/vuetifyjs/vuetify
### 元件傳遞: useeventbus
https://vueuse.org/core/useeventbus/
### 元件傳遞: mitt
https://github.com/developit/mitt
https://ithelp.ithome.com.tw/articles/10279292
https://codesandbox.io/s/vue-3-mitt-shi-fan-ox46v?file=/src/components/Button.vue
安裝
```htmlembedded!
// using ES6 modules
import mitt from 'mitt'
```
在main.ts註冊讓mitt可全域使用
```javascript!
import mitt from 'mitt';
...
app.config.globalProperties.mitt=mitt;
```
## 安裝
### 基本安裝
```htmlembedded!
# 個人推薦用這種安裝方式
#install and execute create-vue
npm init vue@latest
$ npm create vite@latest
# npm 6.x
npm create vite@latest my-vue-app --template vue
# 2022/10/12:這個方式好像有點問題
# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-app -- --template vue
```
### 運行
```htmlembedded!
cd ur_project_name
npm install
npm run dev
#編譯用
npm run build
```
### 安裝tailwind
```htmlembedded!
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```
tailwind.config.cjs
```javascript!
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
```
style.css
```css!
@tailwind base;
@tailwind components;
@tailwind utilities;
```
## deploy
### to github pages
#### github page設定看這裡
https://medium.com/%E9%80%B2%E6%93%8A%E7%9A%84-git-git-git/%E5%BE%9E%E9%9B%B6%E9%96%8B%E5%A7%8B-%E7%94%A8github-pages-%E4%B8%8A%E5%82%B3%E9%9D%9C%E6%85%8B%E7%B6%B2%E7%AB%99-fa2ae83e6276
#### 修改index.html路徑
在index.html手動加上路徑:
```htmlembedded
<base href="/ur_github_project_name/">
```
#### 修改輸出檔案路徑
https://stackoverflow.com/questions/66863200/changing-the-input-and-output-directory-in-vite
vite.config.js
```javascript
...
export default defineConfig({
...
build: {
root: 'src',
outDir: 'docs'
}
})
...
```
### 用xampp部署vue
https://gist.github.com/Prezens/f99fd28124b5557eb16816229391afee
把編譯好的dist file放到C:\xampp\htdocs
新增.htaccess加上mod rewrite規則
```c!
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
```
## compile
### 基本指令
```htmlembedded!
npm run build
```
### ts編譯常見問題
#### ts.config.json
有些新語法會導致vue專案無法編譯成功,需要修改target跟lib屬性,範例如下:
```javascript!
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"compilerOptions": {
"target": "es2017",//<--這裡
"lib": ["dom","es2017", "es2018"],//<--這裡
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
},
"references": [
{
"path": "./tsconfig.config.json"
}
]
}
```
#### localstorage
https://stackoverflow.com/questions/46915002/argument-of-type-string-null-is-not-assignable-to-parameter-of-type-string
```javascript!
JSON.parse(localStorage.getItem(ADMIN_STORAGE_NAME)|| "")
```
## 設定
### 在每次重新編譯時清除console.log
https://github.com/vitejs/vite/discussions/3143
src/main.js
```htmlembedded!
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
import './assets/main.css'
const app = createApp(App)
app.use(createPinia())
app.use(router)
// 加上這段
//----------------
if (import.meta.hot) {
import.meta.hot.on(
"vite:beforeUpdate",
() => console.clear()
);
}
//----------------
app.mount('#app')
``
```
### vite.config.js
#### example
```javascript!
const commonConfig = {
plugins: [vue(), vueJsx()],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
};
export default defineConfig(({ command, mode }) => {
if (command === 'serve') {
return {
...commonConfig,
server: {
https: {
key: fs.readFileSync('C:/xampp/apache/crt/localhost/server.key'),
cert: fs.readFileSync('C:/xampp/apache/crt/localhost/server.crt'),
},
},
}
}else{
return {
...commonConfig,
build: {
root: 'src',
outDir: 'docs'
},
}
}
})
```
## 環境變數
### 在vite生成的vue3 project使用.env檔案
https://jasonwatmore.com/post/2022/05/28/vue-3-vite-access-environment-variables-from-dotenv-env
在專案根目錄建立的.env
```
VITE_MY_ENV_VARIABLE=environemnt variable value from .env file
```
vue
```htmlembedded!
...
const MY_ENV_VARIABLE=import.meta.env.VITE_MY_ENV_VARIABLE;
```
---
## directives
### v-if
#### 使用v-if時不產生外面包覆的element
https://stackoverflow.com/questions/49218040/how-to-use-v-if-and-v-else-without-any-html-tag-or-else
```htmlembedded!
<template v-if="...">
</template>
```
## event handling
### 讓input一開始設定初始值指定至變數,使用者輸入時綁定另一變數
```htmlembedded!
<script>
import { ref } from 'vue'
let str_old_input = ref(null);
let str_new_input = ref('');
function setOldInput() {
str_old_input = input_edit_item.value.value;
}
</script>
<template>
<input
:value="item.content"
ref="input_edit_item"
@focus="setOldInput()"
@input="event => str_new_input = event.target.value">
</template>
```
## routes
### watch router name
```htmlembedded!
import { useRoute } from 'vue-router';
..
watch(() => route.name, () => {
// console.log(`MyCoolComponent - watch route.name changed to ${route.name}`);
// Do something here...
// Optionally you can set immediate: true config for the watcher to run on init
}, { immediate: true });
```
### navigation guard
https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
### 基本用法
vue
```vb!
<RouterLink to="/">Home</RouterLink>
<RouterLink to="/about">About</RouterLink>
<router-link :to="{ name: 'user', params: { username: 'erina' }}">
User
</router-link>
<RouterView />
```
src/router/index.js
```javascript!
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/AboutView.vue')
}
]
})
export default router
```
### 取得目前路由的資訊
```htmlembedded!
//需引用此行
import router from '@/router';
...
//router.currentRoute.value可取得許多路由資訊
let data=router.currentRoute.value.name;
```
### 重新導向至其他頁面
```htmlembedded
<script setup lang="ts">
import router from '@/router';
function login(){
router.push('/');
//使用named routes
//router.push({name:'home'});
}
</script>
<template>
<button @click='login()'>
click me
</button>
</template>
```
### 設定巢狀式路由的首頁
index.js
```javascript=
const router= [{
path: '/cp/',
name: 'CpMain',
component: CpMain,
children:[
{
path:'',//<--設為空格可將該component設為首頁
name:'HomePage',
component:HomePage
}
]
}
...
]
```
## Composition API
https://vuejs.org/api/composition-api-setup.html
### basic usage
```htmlembedded!
<script setup>
//常引用的module
import { ref, onMounted, computed, watch } from 'vue'
//使用ref函式回傳一個響應式物件
//此物件提供一個value屬性可供讀取及更新
//用ref物件之前需先import
const name = ref('');
function getName() {
//在script內使用ref物件記得要加上.value
return name.value;
}
</script>
<template>
<!--在html裡使用ref物件不須加上.value-->
<div>
{{ name }}
</div>
</template>
```
### defineProps & defineEmits
#### defineProps basic usage
child component
```htmlembedded!
<script setup>
const props = defineProps(['text','text2']);
</script>
<template>
<span>{{ text }}</span>
</template>
```
parent component
```htmlembedded!
<script setup>
import ChildComponent from './childComponent.vue';
</script>
<template>
<ChildComponent :text="tabText[0]" :text2="'pure string'"></ChildComponent>
</template>
```
#### defineEmits basic usage
child component
```htmlembedded!
<script setup>
const emits = defineEmits(['onClickEvent'])
const value1='some value';
function fn(){
emits('onClickEvent',value1)
}
</script>
<template>
<div @click="fn()">click me</div>
</template>
```
parent component
```htmlembedded!
<script setup>
import ChildComponent from './childComponent.vue';
function parentFn(val){
console.log(val);
}
</script>
<template>
<ChildComponent @on-click-event="parentFn"></ChildComponent>
</template>
```
### provide & inject
parent component
```htmlembedded!
<script setup lang="ts">
import Card from "./card.vue";
import { provide } from 'vue'
provide('message','hello')
</script>
<template>
<Card></Card>
</template>
```
child component
```htmlembedded!
<script setup lang="ts">
import { inject } from 'vue'
const message = inject('message')
</script>
<template>
<div>{{ message }}</div>
</template>
```
### dynamic components
https://vuejs.org/guide/essentials/component-basics.html#dynamic-components
```htmlembedded
<script lang="ts" setup>
import ConfirmVue from './modals/confirm.vue';
import ModalVue from './modals/modal.vue';
import AlertVue from './modals/alert.vue';
</script>
<template>
<component :is="AlertVue"></component>
</template>
```
### watch
#### example
```javascript!
import { watch,ref} from 'vue';
const a = ref();
...
watch(() => a.value, (newVal) => {
console.log(newVal);
});
watch(() => route.name, () => {
// console.log(`MyCoolComponent - watch route.name changed to ${route.name}`);
// Do something here...
// Optionally you can set immediate: true config for the watcher to run on init
}, { immediate: true });
```
## state management
### pinia
https://pinia.vuejs.org/core-concepts/
https://stackblitz.com/github/piniajs/example-vue-3-vite?file=src%2Fstores%2Fcart.js
#### basic usage
src/stores/todo.js
```javascript!
import { defineStore, acceptHMRUpdate } from 'pinia'
export const useTodoStore = defineStore({
id: 'todo',
state: () => ({
rawItems: [],
}),
getters: {
items: (state) => {
return state.rawItems;
},
},
actions: {
setItems(todos) {
this.rawItems = todos;
}
},
})
```
vue
```htmlembedded!
<script setup>
import { onMounted } from 'vue'
import { useTodoStore } from '../stores/todo'
const todoStore = useTodoStore();
onMounted(() => {
console.log(todoStore.items())
})
</script>
<template>
...
</template>
```