在不少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
node -v
,保證已安裝成功npm install -g cnpm --registry=https://registry.npm.taobao.org
cnpm install -g @vue/cli
vue --version
使用 vue serve
和 vue build
命令對單個 *.vue
文件進行快速原型開發,不過這須要先額外安裝一個全局的擴展:webpack
npm install -g @vue/cli-service-global
複製代碼
vue serve
的缺點就是它須要安裝全局依賴,這使得它在不一樣機器上的一致性不能獲得保證。所以這隻適用於快速原型開發。ios
須要的僅僅是一個 App.vue
文件:git
<template>
<div>
<h2>hello world 單頁面組件</h2>
</div>
</template>
<script>
export default {
}
</script>
<style>
</style>
複製代碼
而後在這個 App.vue
文件所在的目錄下運行:github
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每一個實例都有訂閱/發佈模式的額實現,使用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)
});
複製代碼
發現項目發生了變化,打開App.vue,在vue-cli中可使用vue add element 安裝
安裝以前注意提早提交當前工做內容,腳手架會覆蓋若干文件
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多篇原創技術文章,海量的視頻資料便可得到
備註「加羣」,我會拉你進技術交流羣,羣裏大牛學霸具在,哪怕您作個潛水魚也會學到不少東西