# 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> ```