在不少Vue項目中,咱們使用 Vue.component
來定義全局組件,緊接着用 new Vue({ el: '#app '})
在每一個頁面內指定一個容器元素。css
這種方式在不少中小規模的項目中運做的很好,在這些項目裏 JavaScript 只被用來增強特定的視圖。但當在更復雜的項目中,或者你的前端徹底由 JavaScript 驅動的時候,下面這些缺點將變得很是明顯:html
\
文件擴展名爲 .vue
的 single-file components(單文件組件) 爲以上全部問題提供瞭解決方法,而且還可使用 webpack 或 Browserify 等構建工具。前端
這是一個文件名爲 Hello.vue
的簡單實例:vue
如今咱們得到node
在看完上文以後,建議使用官方提供的 Vue CLI 3腳手架來開發工具,只要遵循提示,就能很快地運行一個帶有.vue
組件,ES2015,webpack和熱重載的Vue項目python
安裝Nodejswebpack
node -v
,保證已安裝成功安裝淘寶鏡像源ios
npm install -g cnpm --registry=https://registry.npm.taobao.org
安裝Vue Cli3腳手架git
cnpm install -g @vue/cli
檢查其版本是否正確github
vue --version
使用 vue serve
和 vue build
命令對單個 *.vue
文件進行快速原型開發,不過這須要先額外安裝一個全局的擴展:
npm install -g @vue/cli-service-global
vue serve
的缺點就是它須要安裝全局依賴,這使得它在不一樣機器上的一致性不能獲得保證。所以這隻適用於快速原型開發。
須要的僅僅是一個 App.vue
文件:
<template> <div> <h2>hello world 單頁面組件</h2> </div> </template> <script> export default { } </script> <style> </style>
而後在這個 App.vue
文件所在的目錄下運行:
vue serve
啓動效果:
網頁效果:
但這種方式僅限於快速原型開發,終歸揭底仍是使用vue cli3來啓動項目
vue create mysite
詳細的看官網介紹
App.vue
<ul> <li v-for="(item, index) in cartList" :key="index"> <h3>{{item.title}}</h3> <p>¥{{item.price}}</p> <button @click='addCart(index)'>加購物車</button> </li> </ul>
cartList: [ { id:1, title:'web全棧開發', price:1999 }, { id: 2, title: 'python全棧開發', price: 2999 } ],
新建Cart.vue購物車組件
<template> <div> <table border='1'> <tr> <th>#</th> <th>課程</th> <th>單價</th> <th>數量</th> <th>價格</th> </tr> <tr v-for="(c, index) in cart" :key="c.id" :class='{active:c.active}'> <td> <input type="checkbox" v-model='c.active'> </td> <td>{{c.title}}</td> <td>{{c.price}}</td> <td> <button @click='subtract(index)'>-</button> {{c.count}} <button @click='add(index)'>+</button> </td> <td>¥{{c.price*c.count}}</td> </tr> <tr> <td></td> <td colspan="2">{{activeCount}}/{{count}}</td> <td colspan="2">{{total}}</td> </tr> </table> </div> </template> <script> export default { name: "Cart", props: ['name', 'cart'], methods: { subtract(i) { let count = this.cart[i].count; // if(count > 1){ // this.cart[i].count-=1 // }else{ // this.remove(i) // } count > 1 ? this.cart[i].count -= 1 : this.remove(i); }, add(i) { this.cart[i].count++; }, remove(i) { if (window.confirm('肯定是否要刪除')) { this.cart.splice(i, 1); } } }, data() { return {} }, created() {}, computed: { activeCount() { return this.cart.filter(v => v.active).length; }, count() { return this.cart.length; }, total() { // let num = 0; // this.cart.forEach(c => { // if (c.active) { // num += c.price * c.count // } // }); // return num; return this.cart.reduce((sum, c) => { if (c.active) { sum += c.price * c.count } return sum; }, 0) } }, } </script> <style scoped> .active { color: red; } </style>
簡單的mock,使用自帶的webpack-dev-server便可,新建vue.config.js擴展webpack設置
module.exports = { configureWebpack:{ devServer:{ // mock數據模擬 before(app,server){ app.get('/api/cartList',(req,res)=>{ res.json([ { id:1, title:'web全棧開發', price:1999 }, { id: 2, title: 'web全棧開發', price: 2999 } ]) }) } } } }
訪問http://localhost:8080/api/cartList 查看mock數據
使用axios獲取接口數據npm install axios -S
created() { axios.get('/api/cartList').then(res=>{ this.cartList = res.data }) }
使用ES7的async+await語法
async created() { // try-catch解決async-awiat錯誤處理 try { const { data } = await axios.get('/cartList') this.cartList = data; } catch (error) { console.log(error); } },
localstorage+vue監聽器
若是組件沒有明顯的父子關係,使用中央事件總線進行傳遞
Vue每一個實例都有訂閱/發佈模式的額實現,使用$on和$emit
main.js
Vue.prototype.$bus = new Vue();
App.vue
methods: { addCart(index) { const good = this.cartList[index]; this.$bus.$emit('addGood',good); } }
Cart.vue
data() { return { cart:JSON.parse(localStorage.getItem('cart')) || [] } }, //數組和對象要深度監聽 watch: { cart: { handler(n, o) { const total = n.reduce((total, c) => { total += c.count return total; }, 0) localStorage.setItem('total', total); localStorage.setItem('cart', JSON.stringify(n)); this.$bus.$emit('add', total); }, deep: true } }, created() { this.$bus.$on('addGood', good => { const ret = this.cart.find(v => v.id === good.id); if (ret) { //購物車已有數據 ret.count += 1; } else { //購物車無數據 this.cart.push({ ...good, count: 1, active: true }) } }) },
更復雜的數據傳遞,可使用vuex,後面課程會詳細介紹
通用組件
業務組件
頁面組件
好比vue最流行的element,就是典型的通用組件,執行npm install element-ui
安裝
import Vue from 'vue'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; import App from './App.vue'; Vue.use(ElementUI); new Vue({ el: '#app', render: h => h(App) });
在vue-cli中可使用vue add element 安裝安裝以前注意提早提交當前工做內容,腳手架會覆蓋若干文件
發現項目發生了變化,打開App.vue,ctrl+z
撤回
此時能夠在任意組件中使用<el-button>
官網element-ui的通用組件,基本上都是複製粘貼使用,在這裏就不一一贅述,後面項目中用到該庫,我們再一一去使用
關於組件設計,最重要的仍是本身去設計組件,如今咱們模仿element-ui提供的表單組件,手寫實現表單組件m-form
先看一下element-ui的表單
新建FormElement.vue
<template> <div> <h3>element表單</h3> <el-form :model="ruleForm" status-icon :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm" > <el-form-item label="用戶名" prop="name"> <el-input type="text" v-model="ruleForm.name" autocomplete="off"></el-input> </el-form-item> <el-form-item label="確認密碼" prop="pwd"> <el-input type="password" v-model="ruleForm.pwd" autocomplete="off"></el-input> </el-form-item> <el-form-item> <el-button type="primary" @click="submitForm('ruleForm')">提交</el-button> </el-form-item> </el-form> </div> </template> <script> export default { name: "FormElement", data() { return { ruleForm: { name:'', pwd:'' }, rules:{ name:[ {required:true,message:'請輸入名稱'}, {min:6,max:10,message:'請輸入6~10位用戶名'} ], pwd:[{require:true,message:'請輸入密碼'}], } } }, methods: { submitForm(name) { this.$refs[name].validate(valid=>{ console.log(valid); if(valid){ alert('驗證成功,能夠提交') }else{ alert('error 提交'); return false; } }) } }, }; </script>
在App.vue組件中導入該組件,掛載,使用
表單組件,組件分層
表單控件實現雙向的數據綁定
<template> <div> <input :type="type" @input="handleInput" :value="inputVal"> </div> </template> <script> export default { props: { value: { type: String, default: "" }, type: { type: String, default: "text" } }, data() { return { //單向數據流的原則:組件內不能修改props inputVal: this.value }; }, methods: { handleInput(e) { this.inputVal = e.target.value; // 通知父組件值的更新 this.$emit("input", this.inputVal); } } }; </script> <style scoped> </style>
FormElement.vue
若是不傳type表示默認值,在Input.vue的props中有說明 <m-input v-model="ruleForm.name"></m-input> <m-input v-model="ruleForm.name" type='password'></m-input>
//數據 data() { return { ruleForm: { name: "", pwd: "" }, rules: { name: [ { required: true, message: "請輸入名稱" }, { min: 6, max: 10, message: "請輸入6~10位用戶名" } ], pwd: [{ require: true, message: "請輸入密碼" }] } }; },
FormItem.vue
<template> <div> <label v-if="label">{{label}}</label> <slot></slot> <!-- 校驗的錯誤信息 --> <p v-if="validateStatus=='error'" class="error">{{errorMessage}}</p> </div> </template> <script> import schema from "async-validator"; export default { name: "FormItem", data() { return { validateStatus: "", errorMessage: "" }; }, props: { label: { type: String, default: "" }, prop: { type: String } } }; </script> <style scoped> .error { color: red; } </style>
FormElement.vue
<m-form-item label="用戶名" prop="name"> <m-input v-model="ruleForm.name"></m-input> </m-form-item> <m-form-item label="密碼" prop="pwd"> <m-input v-model="ruleForm.pwd" type="password"></m-input> </m-form-item>
此時網頁正常顯示,但沒有校驗規則,添加校驗規則
思路:好比對用戶名進行校驗,用戶輸入的用戶名必須是6~10位
npm i asycn-validator -S
Input.vue
methods: { handleInput(e) { this.inputVal = e.target.value; //.... //通知父組件校驗,將輸入框的值實時傳進去 this.$parent.$emit("validate", this.inputVal); } }
FormItem.vue
import schema from "async-validator"; export default { name: "FormItem", data() { return { validateStatus: "", errorMessage: "" }; }, methods: { validate(value) {//value爲當前輸入框的值 // 校驗當前項:依賴async-validate let descriptor = {}; descriptor[this.prop] = this.form.rules[this.prop]; // const descriptor = { [this.prop]: this.form.rules[this.prop] }; const validator = new schema(descriptor); let obj = {}; obj[this.prop] = value; // let obj = {[this.prop]:this.form.model[this.prop]}; validator.validate(obj, errors => { if (errors) { this.validateStatus = "error"; this.errorMessage = errors[0].message; } else { this.validateStatus = ""; this.errorMessage = ""; } }); } }, created() { //監聽子組件Input的派發的validate事件 this.$on("validate", this.validate); }, //注入名字 獲取父組件Form 此時Form咱們還沒建立 inject: ["form"], props: { label: { type: String, default: "" }, prop: { type: String } } };
promise.all()
進行處理)Form.vue
聲明props中獲取數據模型(model)和檢驗規則(rules)
<template> <div> <slot></slot> </div> </template> <script> export default { name:'Form', //依賴 provide(){ return { // 將表單的實例傳遞給後代,在子組件中咱們就能夠獲取this.form.rules和this.form.rules form: this } }, props:{ model:{ type:Object, required:true }, rules:{ type:Object } }, } </script>
當FormItem組件掛載完成時,通知Form組件開始緩存須要校驗的表單項
FormItem.vue
mounted() { //掛載到form上時,派發一個添加事件 //必須作判斷,由於Form組件的子組件可能不是FormItem if (this.prop) { //通知將表單項緩存 this.$parent.$emit("formItemAdd", this); } }
Form.vue
created () { // 緩存須要校驗的表單項 this.fileds = [] this.$on('formItemAdd',(item)=>{ this.fileds.push(item); }) },
將緩存的表單項進行統一處理,若是有一個是錯誤,則返回false.(思路:使用Promise.all()
進行處理).
注意:由於Promise.all方法的第一個參數是數組對象,該數組對象保存多個promise對象,因此要對FormItem的validate方法進行改造
FormItem.vue
validate() { // 校驗當前項:依賴async-validate return new Promise(resolve => { const descriptor = { [this.prop]: this.form.rules[this.prop] }; const validator = new schema(descriptor); validator.validate({[this.prop]:this.form.model[this.prop]}, errors => { if (errors) { this.validateStatus = "error"; this.errorMessage = errors[0].message; resolve(false); } else { this.validateStatus = ""; this.errorMessage = ""; resolve(true); } }); }); }
Form.vue
methods: { validate(callback) { // 獲取全部的驗證結果統一處理 只要有一個失敗就失敗, // 將formItem的validate方法 驗證修改成promise對象,而且保存驗證以後的布爾值 // tasks保存着驗證以後的多個promise對象 const tasks = this.fileds.map(item=>item.validate()); let ret = true; // 統一處理多個promise對象來驗證,只要有一個錯誤,就返回false, Promise.all(tasks).then(results=>{ results.forEach(valid=>{ if(!valid){ ret = false; } }) callback(ret); }) } },
測試:
<m-form :model="ruleForm" :rules="rules" ref="ruleForm2"> <m-form-item label="用戶名" prop="name"> <m-input v-model="ruleForm.name"></m-input> </m-form-item> <m-form-item label="密碼" prop="pwd"> <m-input v-model="ruleForm.pwd" type="password"></m-input> </m-form-item> <m-form-item> <m-button type="danger" @click="submitForm2('ruleForm2')">提交</m-button> </m-form-item> </m-form>
methods:{ submitForm2(name) { this.$refs[name].validate(valid=>{ console.log(valid); if(valid){ alert('驗證成功'); }else{ alert('驗證失敗') } }); } }
還有2件事拜託你們一:求贊 求收藏 求分享 求留言,讓更多的人看到這篇內容
二:歡迎添加個人我的微信
備註「資料」, 300多篇原創技術文章,海量的視頻資料便可得到
備註「加羣」,我會拉你進技術交流羣,羣裏大牛學霸具在,哪怕您作個潛水魚也會學到不少東西