---
tags: Vue,電商,第一章
---
# Vue電商
官方測試網站
https://play.vuejs.org/#eNo9jDsOwjAQRK+yuDEUENEiJxIdN6Bxg5INRPJnZa/TWL47S5BSvnkzU9Wd6LIWVDdl8pgWYsjIhQYbFk8xMVRIOEODOUUPWqraBhvGGDKDz2/of/6oH+hchGdMbjrokw2m+9/JkQCjJ/diFAIwn+tQ6zZuzXRCW7oEKgzr2ccJXW+VeKtEmW5fq/YFaYM9HQ==
必裝插件

要求
node.js 16.0 以上
create vue 指令
```
npm init vue@latest
```

創建好的項目資料夾內執行
```
npm install
npm run dev
```
# setup
```htmlmixed=
<!-- 開關:容許在script書寫組合式API -->
<script setup></script>
<template>
<!-- 不再要求唯一根元素<div id="app"></div> -->
</template>
<style scoped></style>
```

Vue3舊方法
```javascript=
<script>
export default {
setup() {
console.log('setup', this); // undefined
// 數據
const message = 'this is message';
// 函數
const logMessage = () => {
console.log(message);
}
return {
message,
logMessage
}
},
beforeCreate() {
console.log('beforeCreate');
}
}
</script>
<template>
<div>
{{ message }}
<button @click="logMessage">log</button>
</div>
</template>
```
Vue3新方法
```javascript=
<script setup>
console.log('setup', this); // undefined
// 數據
const message = 'this is message';
// 函數
const logMessage = () => {
console.log(message);
}
</script>
<template>
<div>
{{ message }}
<button @click="logMessage">log</button>
</div>
</template>
```
## reactive和ref
reactive
```javascript=
<script setup>
// 1.導入函數
import { reactive } from 'vue';
// 2.執行函數,傳入一個對象類型的參數
const state = reactive({
count: 0
})
const setCount = () => {
state.count++;
}
</script>
<template>
<div>
<button @click="setCount">{{ state.count }}</button>
</div>
</template>
```
ref
```javascript=
<script setup>
// 1.導入函數
import { ref } from 'vue';
// 2.執行函數,傳入一個[簡單類型 或 對象類型]的參數
const count = ref(0);
const setCount = () => {
// 修改ref產生的響應式對象數據,必須通過.value屬性
// ref函數內部還是依賴reactive函數
count.value++;
}
</script>
<template>
<div>
<button @click="setCount">{{ count }}</button>
</div>
</template>
```
## computed
```javascript=
<script setup>
// 1.導入函數
import { ref } from 'vue';
import { computed } from 'vue';
// 2.執行函數,return計算後的值
const list = ref([1, 2, 3, 4, 5, 6, 7, 8]);
// computedList接收用的變量不要直接賦值
const computedList = computed(() => {
return list.value.filter(item => item > 2)
// 不可以異步請求和修改dom
})
// 3.新增定時器來測試看看
setTimeout(() => {
list.value.push(9,10)
},3000)
</script>
<template>
<div>
原始數組{{ list }}
</div>
<div>
計算屬性數組{{ computedList }}
</div>
</template>
```
## watch
單數據
```javascript=
<script setup>
// 1.導入函數
import { ref, watch } from 'vue';
const count = ref(0)
const setCount = () => {
count.value++
}
// 這時ref對象不要加.value
watch(count, (newVal, oldVal) => {
console.log('count變化了', newVal, oldVal);
})
</script>
<template>
<button @click="setCount">{{ count }}</button>
</template>
```
多數據
```javascript=
<script setup>
// 1.導入函數
import { ref, watch } from 'vue';
const count = ref(0)
const setCount = () => {
count.value++;
}
const name = ref('cp');
const changeName = () => {
name.value = 'pc';
}
// 這時ref對象不要加.value
watch([count, name], ([newCount, newName], [oldCount, oldName]) => {
console.log('count或者name變化了', [newCount, newName],[oldCount, oldName]);
})
</script>
<template>
<button @click="setCount">{{ count }}</button>
<button @click="changeName">{{ name }}</button>
</template>
```
立即執行
```javascript=
<script setup>
// 1.導入函數
import { ref, watch } from 'vue';
const count = ref(0)
const setCount = () => {
count.value++
}
// 這時ref對象不要加.value
watch(count, (newVal, oldVal) => {
console.log('count變化了', newVal, oldVal)
}, {
immediate: true
})
</script>
<template>
<button @click="setCount">{{ count }}</button>
</template>
```
如果是修改對象的屬性值不會觸發偵聽,須改用深度偵聽
深度偵聽很耗內存,工作不建議用
```javascript=
<script setup>
// 1.導入函數
import { ref, watch } from 'vue';
const state = ref({count:0})
const change = () => {
state.value.count++;
}
// 這時ref對象不要加.value
watch(state, (newVal, oldVal) => {
// 這時候newVal和oldVal都會顯示相同值
console.log('count變化了', newVal, oldVal)
}, {
deep:true
})
</script>
<template>
<button @click="change">{{ state.count }}</button>
</template>
```
精確偵聽
```javascript=
<script setup>
// 1.導入函數
import { ref, watch } from 'vue';
const state = ref({
name: 'cp',
age:18
})
const changeName = () => {
state.value.name = 'pink';
}
const changeAge = () => {
state.value.age = 20;
}
// 精確偵聽
watch(
() => state.value.age,
() => {
console.log('age變化了');
}
)
</script>
<template>
<button @click="changeName">{{ state.name }}</button>
<button @click="changeAge">{{ state.age }}</button>
</template>
```
## 生命週期

```javascript=
<script setup>
// 1.導入函數
import { onMounted } from 'vue';
onMounted(() => {
console.log('組件掛載完畢1');
// 前人寫的邏輯
})
onMounted(() => {
console.log('組件掛載完畢3');
// 自己寫的邏輯
})
onMounted(() => {
console.log('組件掛載完畢2');
})
</script>
<template>
<div>
<!-- 組件掛載完畢1 -->
<!-- 組件掛載完畢3 -->
<!-- 組件掛載完畢2 -->
</div>
</template>
```
## 父子通信
### 父傳子
```javascript=
<script setup>
import { ref } from 'vue';
import SonCom from './son-com.vue'
const count = ref(100)
setTimeout(() => {
count.value = 200
}, 3000)
</script>
<template>
<div class="father">
<h2>父組件app</h2>
<!-- 綁屬性 -->
<SonCom :count="count" message="father message" />
</div>
</template>
```
```javascript=
<script setup>
// 使用編譯器宏函數
// 接收數據
const props = defineProps({
message: String,
count: Number
})
console.log(props);
</script>
<template>
<div class="son">
<h3>子組件app</h3>
<div>
父組件傳入{{ message }} - {{ count }}
</div>
</div>
</template>
```
### 子傳父
```javascript=
<script setup>
import SonCom from './son-com.vue'
const getMessage = (msg) => {
console.log(msg);
}
</script>
<template>
<div class="father">
<h2>父組件app</h2>
<!-- 綁屬性 -->
<SonCom @get-message="getMessage" />
</div>
</template>
```
```javascript=
<script setup>
const emit = defineEmits(['get-message']);
const sendMsg = () => {
// 用自定義事件傳數據給父組件
emit('get-message','this is son message')
}
</script>
<template>
<div class="son">
<h3>子組件app</h3>
<button @click="sendMsg">觸發自定義事件</button>
</div>
</template>
```
## 模版引用
```javascript=
<script setup>
import { onMounted, ref } from 'vue';
import TestCom from './test-com.vue'
// 用ref對象
const h1Ref = ref(null);
const comRef = ref(null);
// 組件掛載完才能獲取
onMounted(() => {
console.log(h1Ref.value);
console.log(comRef.value);
})
</script>
<template>
<h1 ref="h1Ref">我是dom標籤h1</h1>
<TestCom ref="comRef" />
</template>
```
```javascript=
<script setup>
import { ref } from 'vue';
const name = ref('test name')
const setName = () => {
name.value = 'test new name'
}
// 暴露給父組件
defineExpose({
name,
setName
})
</script>
<template>
<div>我是test組件</div>
<div>{{ name }}</div>
<button @click="setName">改名</button>
</template>
```
## provide和inject
```javascript=
<script setup>
import { provide, ref } from 'vue';
import Mid from './mid-page.vue'
// 提供數據
provide('data-key', 'this is top data');
// 傳遞響應式數據
const count = ref(0);
provide('count-key', count)
setTimeout(() => {
count.value = 100
}, 3000)
// 傳遞方法
const setCount = () => {
count.value++
}
provide('setCount-key', setCount)
</script>
<template>
<div>頂層組件喔
<Mid />
</div>
</template>
<style scoped></style>
```
```javascript=
<script setup>
import Bot from './bot-page.vue'
</script>
<template>
<div class="main">我是Mid組件
<Bot />
</div>
</template>
<style scoped>
.main{
margin: 20px;
}
</style>
```
```javascript=
<script setup>
import { inject } from 'vue';
// 接收數據
const topData = inject('data-key');
// 接收響應式數據
const countData = inject('count-key');
// 接收方法
const setCount = inject('setCount-key');
</script>
<template>
<div class="main">底層組件</div>
<div>來自頂層的數據{{ topData }}</div>
<div>來自頂層的響應數據{{ countData }}</div>
<div><button @click="setCount">修改</button></div>
</template>
<style scoped>
.main {
margin: 20px;
}
</style>
```
:::danger
小案例待補充 :fire:
:::
## pinia
https://pinia.vuejs.org/zh/core-concepts/

`npm install pinia`
### main.js
```javascript=
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
// 1.導入
import { createPinia } from 'pinia'
// 2.執行方法得到實例
const pinia = createPinia()
// 3.把pinia實例加到app應用裡
createApp(App).use(pinia).mount('#app')
```
### counter
src/stores
新增counter.js
```javascript=
// 導入一個方法
import { defineStore } from "pinia";
import { ref } from "vue";
export const useCounterStore = defineStore("counter", () => {
// 定義數據(state)
const count = ref(0);
// 定義修改數據方法(action 同步+異步)
const increment = () => {
count.value++;
};
// 以對象方式return供組件使用
return {
count,
increment,
};
});
```
在app.vue裡
```javascript=
<script setup>
// 1.導入use打頭的方法
import { useCounterStore } from '@/stores/counter'
// 2.執行方法得到store實例對象
const counterStore = useCounterStore();
console.log(counterStore);
</script>
<template>
<button @click="counterStore.increment">{{ counterStore.count }}</button>
</template>
```
### getters和異步action
```javascript=
// 導入一個方法
import { defineStore } from "pinia";
import { computed, ref } from "vue";
import axios from "axios";
const API_URL = "http://geek.itheima.net/v1_0/channels";
export const useCounterStore = defineStore("counter", () => {
// 定義數據(state)
const count = ref(0);
// 定義修改數據方法(action 同步+異步)
const increment = () => {
count.value++;
};
// getter定義
const doubleCount = computed(() => count.value * 2);
// 定義異步action
const list = ref([]);
const getList = async () => {
const res = await axios.get(API_URL);
// 第一個data是axios語法
// 第二個data是接口裡的對象名
list.value = res.data.data.channels;
};
// 以對象方式return供組件使用
return {
count,
doubleCount,
increment,
list,
getList,
};
});
```
```javascript=
<script setup>
// 1.導入use打頭的方法
import { useCounterStore } from '@/stores/counter'
import { onMounted } from 'vue';
// 2.執行方法得到store實例對象
const counterStore = useCounterStore();
console.log(counterStore);
// 觸發action
onMounted(() => {
counterStore.getList()
})
</script>
<template>
<button @click="counterStore.increment">{{ counterStore.count }}</button>
{{ counterStore.doubleCount }}
<ul>
<li v-for="item in counterStore.list" :key="item.id">{{ item.name }}</li>
</ul>
</template>
```
### storeToRefs
```javascript=
// 導入一個方法
import { defineStore } from "pinia";
import { computed, ref } from "vue";
import axios from "axios";
const API_URL = "http://geek.itheima.net/v1_0/channels";
export const useCounterStore = defineStore("counter", () => {
// 定義數據(state)
const count = ref(0);
// 定義修改數據方法(action 同步+異步)
const increment = () => {
count.value++;
};
// getter定義
const doubleCount = computed(() => count.value * 2);
// 定義異步action
const list = ref([]);
const getList = async () => {
const res = await axios.get(API_URL);
// 第一個data是axios語法
// 第二個data是接口裡的對象名
list.value = res.data.data.channels;
};
// 以對象方式return供組件使用
return {
count,
doubleCount,
increment,
list,
getList,
};
});
```
```javascript=
<script setup>
// 1.導入use打頭的方法
import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia';
import { onMounted } from 'vue';
// 2.執行方法得到store實例對象
const counterStore = useCounterStore();
console.log(counterStore);
// 直接解構賦值(響應式丟失)
// const { count, doubleCount } = counterStore
// 方法包裹(保持響應式更新)
const { count, doubleCount } = storeToRefs(counterStore)
console.log(count, doubleCount);
// 函數直接從原來的counterStore解構賦值
const { increment } = counterStore
// 觸發action
onMounted(() => {
counterStore.getList()
})
</script>
<template>
<button @click="increment">{{ count }}</button>
{{ doubleCount }}
<ul>
<li v-for="item in counterStore.list" :key="item.id">{{ item.name }}</li>
</ul>
</template>
```
## module-alias應用在vue
不用安裝包
在根目錄新增json文件
命名為jsconfig.json
貼以下代碼
```json=
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@/*": ["src/*"]
}
}
}
```
這個只是聯想提示功能而已
實際的路徑轉換在vue本身帶有的vite.config.js檔案中實現