--- tags: Vue,第四章 --- # 腳手架介紹 ![](https://hackmd.io/_uploads/rJqVSny6h.png) ![](https://hackmd.io/_uploads/S1UJ82163.png) ![](https://hackmd.io/_uploads/rypfSEDTh.png) ![](https://hackmd.io/_uploads/S1vtCLvTn.png) ![](https://hackmd.io/_uploads/ryES0uwTh.png) ![](https://hackmd.io/_uploads/H1nl1Fwa2.png) # 裝less npm i less-loader less -D ![](https://hackmd.io/_uploads/B1Zw6FkCh.png) # 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> ``` # 局部註冊組件 ![](https://hackmd.io/_uploads/Sy7aq9xA2.png) ## 快捷鍵 ``` <vue> ``` 直接在組件打出vue模板 ``` <!-- 在根組件裡 --> <template> <子組件名> </template> ``` 直接在script標籤裡引好子組件 ## 組件命名風格 在vue裡面兩種命名風格是可以混用的 ### 大駝峰命名法 Camel-Case HmHeader HmMain HmFooter ### 烤串命名法 Kebab-Case hm-header hm-main hm-footer # 全局註冊組件 ![](https://hackmd.io/_uploads/rJT95seA3.png) 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 ![](https://hackmd.io/_uploads/H1uFI8Q0n.png) ![](https://hackmd.io/_uploads/r1RfsOX0n.png) ![](https://hackmd.io/_uploads/rydBjd7R3.png) ![](https://hackmd.io/_uploads/SJNDiOXR2.png) # data是函數 如果用對象的話:對象就像是一個地址(引用數據類型),所有人都讀取同一個地址,無法讓各組件數據獨立不受干擾 如果用函數的話:函數每執行一次就會產生一個新獨立的對象 ![](https://hackmd.io/_uploads/H1tfKcEC3.png) # 組件通信 ![](https://hackmd.io/_uploads/H1HvyoNA3.png) ## 父子通信 ![](https://hackmd.io/_uploads/SyJWxiNRh.png) ![](https://hackmd.io/_uploads/BJadrlIA2.jpg) ```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作用 ![](https://hackmd.io/_uploads/rkDWhxU02.png) ![](https://hackmd.io/_uploads/HJglX3g8A3.png) ## props校驗 ![](https://hackmd.io/_uploads/SybtNHF0n.png) ```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> ```