---
tags: Vue,第四章
---
# 腳手架介紹






# 裝less
npm i less-loader less -D

# main.js
```javascript=
// 引入vue構造函數
import Vue from 'vue'
// 引入app.vue文件
// .vue結尾的文件都可稱為組件
import App from './App.vue'
// 設置生產環境時的提示,不顯示
Vue.config.productionTip = false
// 創建vue實例對象
new Vue({
// render作用:根據app組件來創建dom元素並掛載到指定的容器中
// render(createElement) {
// return createElement(App)
// }
render: h => h(App),
// $mount()完全等同於設置el
}).$mount('#app')
```
# .vue規則
```javascript=
<template>
<!-- 一定要放template標籤 -->
<!-- 一定要放一個一級子元素(不可放兩個以上) -->
<div>
HelloWorld
</div>
</template>
<script>
</script>
<style>
</style>
```
# App.vue範例
```javascript=
<template>
<!-- 一定要放template標籤 -->
<!-- 一定要放一個一級子元素(不可放兩個以上) -->
<div @click="open" class="father">
大盒子
<div class="son">小盒子</div>
</div>
</template>
<script>
// 組件中的js默認導出一個對象,這個對象就是組件對象
export default {
// vue核心成員
// 不要寫el掛載點了
methods: {
open() {
alert('你好哇')
}
}
}
</script>
<!-- 記得要裝less包 -->
<style lang="less">
.father {
width: 400px;
height: 400px;
background-color: orange;
.son {
width: 200px;
height: 200px;
background-color: green;
}
}
</style>
```
# 局部註冊組件

## 快捷鍵
```
<vue>
```
直接在組件打出vue模板
```
<!-- 在根組件裡 -->
<template>
<子組件名>
</template>
```
直接在script標籤裡引好子組件
## 組件命名風格
在vue裡面兩種命名風格是可以混用的
### 大駝峰命名法 Camel-Case
HmHeader
HmMain
HmFooter
### 烤串命名法 Kebab-Case
hm-header
hm-main
hm-footer
# 全局註冊組件

main.js
```javascript=
// 引入vue構造函數
import Vue from 'vue'
// 引入app.vue文件
// .vue結尾的文件都可稱為組件
import App from './App.vue'
// 引入組件
import HmButton from './components/HmButton.vue'
// 註冊組件
Vue.component('HmButton',HmButton)
// 設置生產環境時的提示,不顯示
Vue.config.productionTip = false
// 創建vue實例對象
new Vue({
// render作用:根據app組件來創建dom元素並掛載到指定的容器中
// render(createElement) {
// return createElement(App)
// }
render: h => h(App),
// $mount()完全等同於設置el
}).$mount('#app')
```
App.vue
```javascript=
<template>
<div class="app">
<hm-header></hm-header>
<hm-main></hm-main>
<hm-footer></hm-footer>
</div>
</template>
<script>
import HmHeader from './components/HmHeader.vue';
import HmMain from './components/HmMain.vue';
import HmFooter from './components/HmFooter.vue';
export default {
components: {
// 傳統完整寫法
HmHeader: HmHeader,
// ES6簡寫
HmMain,
HmFooter
}
}
</script>
<style lang="less">
.app{
width: 400px;
height: 600px;
background-color: #87ceeb;
display: flex;
flex-direction: column;
justify-content: space-between;
}
</style>
```
HmButton.vue
```javascript=
<template>
<button class="hm-button">通用按鈕</button>
</template>
<script>
export default {
}
</script>
<style>
.hm-button{
height: 35px;
line-height: 35px;
padding: 0 20px;
background-color: green;
border-radius: 5px;
color: white;
border: none;
cursor: pointer;
}</style>
```
HmHeader.vue
```javascript=
<template>
<div class="hm-header">
我是頭部組件
<hm-button></hm-button>
</div>
</template>
<script>
import HmButton from './HmButton.vue'
export default {
components: { HmButton },
}
</script>
<style>
.hm-header{
height: 80px;
line-height: 80px;
color: white;
font-size: 30px;
text-align: center;
background-color: #8064a2;
}
</style>
```
HmMain.vue
```javascript=
<template>
<div class="hm-main">
我是身體組件
<hm-button></hm-button>
</div>
</template>
<script>
export default {
}
</script>
<style>
.hm-main {
height: 320px;
line-height: 320px;
color: white;
font-size: 30px;
text-align: center;
background-color: #f79646;
}
</style>
```
HmFooter.vue
```javascript=
<template>
<div class="hm-footer">
我是底部組件
<hm-button></hm-button>
</div>
</template>
<script>
export default {
}
</script>
<style>
.hm-footer {
height: 80px;
line-height: 80px;
color: white;
font-size: 30px;
text-align: center;
background-color: #4f8ebd;
}
</style>
```
# scoped




# data是函數
如果用對象的話:對象就像是一個地址(引用數據類型),所有人都讀取同一個地址,無法讓各組件數據獨立不受干擾
如果用函數的話:函數每執行一次就會產生一個新獨立的對象

# 組件通信

## 父子通信


```javascript=
<template>
<div class="app">
我是App組件{{ myTitle }}
<son @changeTitle="change" :title="myTitle"></son>
</div>
</template>
<script>
// 父向子傳數據
// 在App.vue中,給子組件添加自定義數據
// 在Son.vue中,使用props接收自定義屬性傳來的數據
// 分隔線
// 子向父傳數據
// 在Son.vue中,使用$emit傳遞數據
// 在App.vue中,使用自定義事件接收數據
import Son from './components/Son.vue'
export default {
name: 'App',
data() {
return {
myTitle: '學前端'
}
},
methods: {
// val就是子組件this.$emit裡的數據1
change(val) {
this.myTitle = val
}
},
components: {
Son
}
}
</script>
<style scoped lang="less">
.app {
border: 3px solid black;
margin: 10px;
}
</style>
```
```javascript=
<template>
<div class="son">
<button @click="change">修改title</button>
我是Son組件{{ title }}
</div>
</template>
<script>
export default {
name: 'Son-Child',
props: ['title'],
methods: {
change() {
// 父組件通過props傳過來的數據,子組件無法直接修改
// (錯誤寫法)this.title='不想學習了'
// this.$emit('自定義事件名','數據1','數據2')
this.$emit('changeTitle', '不想學習了')
}
}
}
</script>
<style scoped lang="less">
.son {
border: 3px solid black;
margin: 10px;
}
</style>
```
## props作用


## props校驗

```javascript=
<script>
export default {
// 初學者用數組,進階要用對象
// 數據類型首字母一定大寫
props: {
w: Number
}
}
</script>
```
```javascript=
<script>
export default {
props: {
w: {
type: Number, // 數據類型
default: 50, // 默認值,與非空校驗互斥
require: true, // 非空校驗,與默認值互斥,true代表一定要傳東西
// 自定義校驗
validator(val){
if (val >= 0 && val <= 100) {
return true
} else {
console.error('取值範圍是0~100')
return false
}
}
}
}
}
</script>
```
## 單向數據流
父組件
```javascript=
<template>
<div>
<son @change="hChange" :count="count"></son>
</div>
</template>
<script>
import Son from './components/Son.vue'
export default {
data() {
return {
count: 100
}
},
methods: {
hChange(count) {
this.count = count
}
},
components: {
Son
}
}
</script>
<style></style>
```
子組件
```javascript=
<template>
<div>
<span>{{ count }}</span>
<button @click="handleSub">-</button>
</div>
</template>
<script>
export default {
props: {
count: {
type:Number
}
},
methods: {
handleSub() {
// 千萬不能寫this.count --
// 因為this.count -- 等同於 this.count = this.count - 1
this.$emit('change',this.count - 1)
}
}
}
</script>
<style></style>
```
# 小黑記事本組件拆分
App.vue
```javascript=
<template>
<div>
<todo-header @add="hAdd"></todo-header>
<todo-main @del="hDel" :list="list"></todo-main>
<todo-footer @clear="hClear" :list="list"></todo-footer>
</div>
</template>
<script>
import TodoHeader from './components/TodoHeader.vue'
import TodoMain from './components/TodoMain.vue'
import TodoFooter from './components/TodoFooter.vue'
export default {
components: { TodoHeader, TodoMain, TodoFooter },
data() {
return {
list:JSON.parse(localStorage.getItem('list')) || []
// list: [
// { id: 1, name: '跑步十公里' },
// { id: 2, name: '打球一整天' },
// { id: 3, name: '吃飯一公斤' },
// ]
}
},
methods: {
hAdd(taskName) {
this.list.unshift({
id: +new Date(),
name: taskName
})
},
hDel(id) {
this.list = this.list.filter(item => item.id !== id)
},
hClear() {
this.list = []
}
},
watch: {
list: {
deep: true,
handler() {
localStorage.setItem('list',JSON.stringify(this.list))
}
}
}
}
</script>
<style></style>
```
頭部
```javascript=
<template>
<div>
<header>
<h1>小黑記事本</h1>
<input @keydown.enter="hAdd" v-model.trim="taskName" placeholder="請輸入任務" type="text">
<button @click="hAdd">添加任務</button>
</header>
</div>
</template>
<script>
export default {
data() {
return {
taskName: ''
}
},
methods: {
hAdd() {
// 非空校驗
if (!this.taskName) { return alert('請輸入任務名稱') }
// 將數據傳給父組件
this.$emit('add', this.taskName)
// 清空輸入框
this.taskName = '';
}
}
}
</script>
<style></style>
```
身體
```javascript=
<template>
<div>
<section>
<ul>
<li v-for="(item, index) in list" :key="item.id">
<div>
<span>{{ index + 1 }}</span><label>{{ item.name }}</label>
<button @click="hDel(item.id)">按鈕</button>
</div>
</li>
</ul>
</section>
</div>
</template>
<script>
export default {
props: {
list: {
required: true,
type: Array
}
},
methods: {
hDel(id) {
this.$emit('del',id)
}
}
}
</script>
<style></style>
```
腳部
```javascript=
<template>
<div>
<footer>
<!-- 統計 -->
<span>合計:<strong>{{ list.length }}</strong></span>
<!-- 清空 -->
<!-- 在模板中寫代碼可以省略this -->
<button @click="$emit('clear')">清空任務</button>
</footer>
</div>
</template>
<script>
export default {
props: {
list: {
type: Array,
required: true
}
},
// methods: {
// hClear() {
// this.$emit('clear')
// }
// }
}
</script>
<style></style>
```