vue组件化编程
組件(Component)是 Vue.js 最強大的功能之一。
組件可以擴展 HTML 元素,封裝可重用的代碼。
組件系統讓我們可以用獨立可復用的小組件來構建大型應用,幾乎任意類型的應用的界面都可以抽象為一個組件樹:
?
1. 使用vue-cli創建模板項目
1.1 簡介
1) vue-cli 是 vue 官方提供的腳手架工具
2) github: https://github.com/vuejs/vue-cli
3) 作用: 從 https://github.com/vuejs-templates 下載模板項目
1.2 創建vue項目
npm install -g vue-cli ? vue init webpack vue_demo ? cd vue_demo ? npm install ? npm run dev ? 訪問: http://localhost:8080/1.3 模板項目的結構
|-- build : webpack 相關的配置文件夾(基本不需要修改)
|-- dev-server.js : 通過 express 啟動后臺服務器
|-- config: webpack 相關的配置文件夾(基本不需要修改)
|-- index.js: 指定的后臺服務的端口號和靜態資源文件夾
|-- node_modules
|-- src : 源碼文件夾
|-- components: vue 組件及其相關資源文件夾
|-- App.vue: 應用根主組件
|-- main.js: 應用入口 js
|-- static: 靜態資源文件夾
|-- .babelrc: babel 的配置文件
|-- .eslintignore: eslint 檢查忽略的配置
|-- .eslintrc.js: eslint 檢查的配置
|-- .gitignore: git 版本管制忽略的配置
|-- index.html: 主頁面文件
|-- package.json: 應用包配置文件
|-- README.md: 應用描述說明的 readme 文件
1.4 效果
訪問 http://localhost:8080/
?
2. 項目的打包和發布
2.1 打包
npm run build會生成如下目錄:
?
2.2 使用靜態服務器發布
npm install -g serve serve dist 訪問: http://localhost:5000?
2.3 使用tomcat發布
1.修改配置: webpack.prod.conf.js output: { publicPath: '/xxx/' //打包文件夾的名稱 } 2.重新打包: npm run build 3.修改 dist 文件夾為項目名稱: xxx 4.將 xxx 拷貝到運行的 tomcat 的 webapps 目錄下 訪問: http://localhost:8080/xxx3. 組件定義與使用
3.1 vue文件的組成
1)模板頁面
<template><!--頁面模板 --> </template>2)js模塊對象
<script>export default {data() {return {}},methods: {},computed: {},components: {}} </script>3)樣式
<style scoped>/* 樣式定義 */ </style>3.2 基本使用
1) 引入組件
2) 映射成標簽
3) 使用組件標簽
<template> <!-- 寫法一: 一模一樣 --><HelloWorld></HelloWorld> <!-- 寫法二: 大寫變小寫, 并用-連接 --><hello-world></hello-world> </template> <script> import HelloWorld from './components/HelloWorld' export default { components: { HelloWorld } } </script>4. 組件間通信
4.1 基本原則
1) 不要在子組件中直接修改父組件的狀態數據
2) 數據在哪, 更新數據的行為(函數)就應該定義在哪
4.2 組件間通信 1:props
使用組件標簽時:
<my-component name='tom' :age='3' :set-name='setName'></my-component>定義MyComponent時,在組件內聲明所有的 props:
// 方式一: 只指定名稱 props: ['name', 'age', 'setName'] ? // 方式二: 指定名稱和類型 props: { name: String, age: Number, setName: Function } ? // 方式三: 指定名稱/類型/必要性/默認值 props: { name: {type: String, required: true, default:xxx}, ...}注意:
-
此方式用于父組件向子組件傳遞數據
-
所有標簽屬性都會成為組件對象的屬性, 模板頁面可以直接引用
-
問題:
-
如果需要向非子后代傳遞數據必須多層逐層傳遞
-
兄弟組件間也不能直接 props 通信, 必須借助父組件才可以
-
4.3 組件間通信2: vue自定義事件
綁定事件監聽
// 方式一: 通過 v-on 綁定 @click="deleteTodo" // 方式二: 通過$on() 綁定自定義事件(delete_todo)監聽 <TodoHeader ref="xxx"/> this.$refs.xxx.$on('delete_todo', function (todo) { this.deleteTodo(todo) })觸發事件
// 觸發事件(只能在父組件中接收) this.$emit(eventName, data)注意:
-
此方式只用于子組件向父組件發送消息(數據)
-
問題: 隔代組件或兄弟組件間通信此種方式不合適
4.4 組件間通信3:消息訂閱與發布
訂閱消息
PubSub.subscribe('msg', function(msg, data){})發布消息
PubSub.publish('msg', data)注意:
-
此方式可實現任意關系組件間通信(數據)
比如我們訂閱一個消息,實現數組的刪除操作
? ? mounted () { // 訂閱消息(deleteTodo)PubSub.subscribe('deleteTodo', (msg, index) => {this.deleteTodo(index)})} ?methods: {deleteTodo (index) {this.todos.splice(index, 1)},} ?<button class="btn btn-danger" v-show="isShow" @click="deleteItem">刪除</button>deleteItem () {// 發布消息(deleteTodo)PubSub.publish('deleteTodo', this.index)}4.5 組件間通信4:slot
此方式用于父組件向子組件傳遞標簽數據
子組件: Child.vue
<template> <div><slot name="xxx">不確定的標簽結構 1</slot> <div>組件確定的標簽結構</div> <slot name="yyy">不確定的標簽結構 2</slot> </div> </template>父組件: Parent.vue
<child><div slot="xxx">xxx 對應的標簽結構</div> <div slot="yyy">yyyy 對應的標簽結構</div> </child>5.vue-ajax
Vue 要實現異步加載需要使用到 vue-resource 庫。
Vue.js 2.0 版本推薦使用 axios 來完成 ajax 請求。
下面我們介紹一下 axios
使用 npm:
npm install axios使用 cdn:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>5.1 GET
下面是axios發送get請求的一個示例
//引入axios模塊 import axios from 'axios' ? // 發ajax請求進行搜索 // 直接在 URL 上添加參數 q=xxx const url = `https://api.github.com/search/users?q=${searchName}` axios.get(url).then(response => {// 成功了, 更新數據(成功)this.users = response.data.items.map(item => ({url: item.html_url,avatarUrl: item.avatar_url,username: item.login})) }).catch(error => {this.errorMsg = '請求失敗!' })如果需要傳遞數據,可以使用 this.$http.get('url',{params : jsonData})格式,第二個參數 jsonData就是傳到后端的數據。
// 通過params設置參數 axios.get('https://api.github.com/search/users',{params : {q:searchName}}).then(function(res){// ... },function(res){// ... });5.2 POST
axios.post('/user', {firstName: 'Fred', ? ? ? ?// 參數 firstNamelastName: 'Flintstone' ? ?// 參數 lastName}).then(function (response) {console.log(response);}).catch(function (error) {console.log(error);});5.3 API
可以通過向 axios 傳遞相關配置來創建請求。
axios(config) // 發送 POST 請求 axios({method: 'post',url: '/user/12345',data: {firstName: 'Fred',lastName: 'Flintstone'} }); // GET 請求遠程圖片 axios({method:'get',url:'http://bit.ly/2mTM3nY',responseType:'stream' }).then(function(response) {response.data.pipe(fs.createWriteStream('ada_lovelace.jpg')) }); axios(url[, config]) // 發送 GET 請求(默認的方法) axios('/user/12345');為方便使用,官方為所有支持的請求方法提供了別名,可以直接使用別名來發起請求:
axios.request(config) axios.get(url[, config]) axios.delete(url[, config]) axios.head(url[, config]) axios.post(url[, data[, config]]) axios.put(url[, data[, config]]) axios.patch(url[, data[, config]])在使用別名方法時, url、method、data 這些屬性都不必在配置中指定。
5.4 響應結構
axios請求的響應包含以下信息:
{// `data` 由服務器提供的響應data: {}, ?// `status` HTTP 狀態碼status: 200, ?// `statusText` 來自服務器響應的 HTTP 狀態信息statusText: "OK", ?// `headers` 服務器響應的頭headers: {}, ?// `config` 是為請求提供的配置信息config: {} }6. 組件入門案例演示
4.1 初始化顯示
?
我們有這樣一個頁面,我們把這個頁面按照組件化的形式開發。
我們把頁面劃分成了3個組件,文件目錄結構為:
?
1)首先,創建我們的入口文件
index.js
import Vue from 'vue'; import App from './App.vue'; ? new Vue({el: '#app',components: { App },template: '<App/>', });2)我們的頁面使用了bootstrap的樣式。我們在static下面先導入樣式文件
然后在index.html中引入
? ?<link rel="stylesheet" href="./static/css/bootstrap.css">3)App.vue中引入組件,并將基本結構寫出來
<template><div><header class="site-header jumbotron"><div class="container"><div class="row"><div class="col-xs-12"><h1>請發表對Vue的評論</h1></div></div></div></header><div class="container"><Add/><List/></div></div> </template> ? <script>//1.引入組件import Add from './components/Add.vue';import List from './components/List.vue'; ?export default {name: 'App',//2.映射組件標簽components: {Add, List},}; </script> ? <style> ? </style>4)Add.vue頁面
<template><div class="col-md-4"><form class="form-horizontal"><div class="form-group"><label>用戶名</label><input type="text" class="form-control" placeholder="用戶名"></div><div class="form-group"><label>評論內容</label><textarea class="form-control" rows="6" placeholder="評論內容"></textarea></div><div class="form-group"><div class="col-sm-offset-2 col-sm-10"><button type="button" class="btn btn-default pull-right">提交</button></div></div></form></div> </template> ? <script>export default {name: "add"} </script> ? <style scoped> ? </style>5)List.vue頁面
<template><div class="col-md-8"><h3 class="reply">評論回復:</h3><h2 style='display: none'>暫無評論,點擊左側添加評論!!!</h2><ul class="list-group"><li class="list-group-item"><div class="handle"><a href="javascript:;">刪除</a></div><p class="user"><span >xxx</span><span>說:</span></p><p class="centence">vue不錯!</p></li><li class="list-group-item"><div class="handle"><a href="javascript:;">刪除</a></div><p class="user"><span >yyy</span><span>說:</span></p><p class="centence">vue有點難!</p></li></ul></div> </template> ? <script>export default {name: "list"} </script> ? <style scoped> ? </style>6)引入樣式
List.vue中引入樣式
?.reply {margin-top: 0px;}7)定義初始化data數據
因為我們的評論數據在add和list里面都有用到,所以我們定義到app.vue中
? ?data () {return {// 數據在哪個組件,更新數據的行為(方法)就應該定義在哪個組件comments:[{name: 'Tom',content: 'vue真好用'},{name: 'Jack',content: 'vue真簡單'}]}}然后將數據傳給list
<List :comments="comments"/>List.vue中聲明接收屬性
? ?export default {name: "list",// 聲明接收屬性,這個屬性就會成為組件對象的屬性props: ['comments']}我們這里可以把item單獨定義成一個組件,所以可以把comment傳給Item.vue
8)數據傳遞
數據是一個數組,所以我們使用v-for遍歷。將剛才的寫死的數據修改成如下:
? ?<ul class="list-group"><Item v-for="(comment, index) in comments" :key="index" :comment="comment"/></ul> ?<script>import Item from './Item.vue'export default {// ...components: {Item}}</script>9)Item.vue
List.vue將comment對象傳過來了,我們使用props接收。然后編寫樣式。
<template><li class="list-group-item"><div class="handle"><a href="javascript:;">刪除</a></div><p class="user"><span >{{comment.name}}</span><span>說:</span></p><p class="centence">{{comment.content}}</p></li> </template> ? <script>export default {name: "Item",props:{// 指定了屬性名和屬性值的類型comment: Object}} </script> ? <style scoped> ?li {transition: .5s;overflow: hidden;} ?.handle {width: 40px;border: 1px solid #ccc;background: #fff;position: absolute;right: 10px;top: 1px;text-align: center;} ?.handle a {display: block;text-decoration: none;} ?.list-group-item .centence {padding: 0px 50px;} ?.user {font-size: 22px;} </style> ?最終效果如下:
?
4.2 交互添加
因為我們的comments數組定義在App.vue中,所以我們在該文件中定義添加的方法。
? ?methods:{addComment(comment){this.comments.unshift(comment);}},我們想要在Add.vue中使用它,所以需要將該方法傳遞給該組件
? ? ?<Add :addComment="addComment"/>我們需要在Add.vue中做添加操作
<template><div class="col-md-4"><form class="form-horizontal"><div class="form-group"><label>用戶名</label><input type="text" class="form-control" placeholder="用戶名" v-model="name"></div><div class="form-group"><label>評論內容</label><textarea class="form-control" rows="6" placeholder="評論內容" v-model="content"></textarea></div><div class="form-group"><div class="col-sm-offset-2 col-sm-10"><button type="button" class="btn btn-default pull-right" @click="add">提交</button></div></div></form></div> </template> ? <script>export default {props: {addComment: {// 指定了屬性名/屬性值的類型type: Function,// 指定了必要性required: true}},data() {return {name: '',content: ''}},name: "add",methods: {add() {// 1.檢查輸入的合法性const name = this.name.trim();const content = this.content.trim();if (!name || !content) {alert("姓名或內容不能為空");return;}// 2.根據輸入的數據封裝成一個comment對象const comment = {name,content};// 3.添加到comments中this.addComment(comment);// 5.清除輸入this.name = '';this.content = '';}}} </script> ? <style scoped> ? </style>4.3 交互刪除
在App.vue中,定義刪除的方法。
? ? ?// 刪除指定下標的評論deleteComment(index){this.comments.splice(index,1);}將該方法傳給List.vue組件
? ? ?<List :comments="comments" :deleteComment="deleteComment"/>List.vue接收該方法,并將該方法和數組的index傳給Item.vue
<Item v-for="(comment, index) in comments" :key="index" :comment="comment" :deleteComment="deleteComment" :index="index"/> ?<script> ? ? // ...props: ['comments','deleteComment']</script> ?沒有評論時,展示對應的說明
? ?<h2 v-show="comments.length===0">暫無評論,點擊左側添加評論!!!</h2>Item.vue中做刪除操作
<a href="javascript:;" @click="deleteItem()">刪除</a> ? <script>props:{// ...deleteComment: Function,index: Number},methods:{deleteItem(){const {comment} =this;if(confirm(`確定刪除${comment.name}的評論嗎?`)){this.deleteComment(this.index);}}} ? </script>?
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
- 上一篇: VueX(Vue状态管理模式)
- 下一篇: Vue路由案例演示