Vue-cli3开发单文件

一、单文件组件

​ 在很多Vue项⽬中,我们使⽤ Vue.component 来定义全局组件,紧接着⽤ new Vue({ el: '#app '}) 在每个⻚⾯内指定⼀个容器元素。
​ 这种⽅式在很多中⼩规模的项⽬中运作的很好,在这些项⽬⾥JavaScript 只被⽤来加强特定的视图。但当在更复杂的项⽬中,或者你的前端完全由 JavaScript 驱动的时候,下⾯这些缺点将变得⾮常明显:

  • 全局定义强制要求每个 component 中的命名不得重复
  • 字符串模板 缺乏语法⾼亮,在 HTML 有多⾏的时候,需要用到丑陋的
  • 不⽀持 CSS 意味着当 HTML 和 JavaScript 组件化时, CSS 明显被遗漏
  • 没有构建步骤 限制只能使⽤ HTML 和 ES5 JavaScript, ⽽不能使⽤预处理器,如 Pug (formerly Jade) 和 Babel

⽂件扩展名为 .vue 的 single-file components(单⽂件组件) 为以上所有问题提供了解决⽅法,并且还可以使⽤ webpack 或
Browserify 等构建⼯具。

现在我们获得

在看完上⽂之后,建议使⽤官⽅提供的 Vue CLI 3脚⼿架来开发⼯具,只要遵循提示,就能很快地运⾏⼀个带有 .vue 件,ES2015,webpack和热重载的Vue项⽬

二、Vue CLI3

2.1 基本配置

  • 安装Nodejs
    • 保证Nodejs8.9或更高版本
    • 终端中输入node -v保证已经安装成功
  • 安装 淘宝镜像源
    • npm install -g cnpm --registry=https://registry.npm.taobao.org
    • 以后的npmm可以用cnpm代替
  • 安装Vue Cli3脚手架
    • cnpm install -g @vue/cli
  • 检查其版本是否正确
    • Vue --version

2.2 快速原型开发

使用Vue servevue build命令对单个*.vue文件进行快速原型开发,不过这需要先额外安装一个全局的扩展:

cnpm install -g @vue/cli-service-global

npm init 初始化 //注意好自己当前初始化的路径

vue serve的缺点就是它需要安装全局依赖,这使得它在不同机器上的一致性不能得到保证,因此这只适用于快速原型开发。

需要的仅仅是一个APp.vue文件

App.vue文件内容

 <!--template 编写HTML-->
<template>
    <div>
        <h3>{{msg}}</h3>
    </div>
</template>
<script>
export default {
    data(){
        return {
            msg: 'vue-cli3开发单文件组件'
        };
    }
};
</script>
<!--scoped表示注入,当前的样式只对当前的组件中的结果有效-->
<style scoped>
  h3{
      color:red;
  }
</style>

然后在这个App.vue文件所在的目录下运行:

vue serve

启动效果

123.PNG

网页效果:

123.PNG

​ 但这种⽅式仅限于快速原型开发,终归揭底还是使⽤vue cli3来启动项⽬

2.3 创建项目

vue create mysite

详细的看 官网介绍

123.PNG

出现Successfully,即说明成功

cd mysite

npm run serve

最后出现下方图片。

123.PNG

VScode上生成的文件目录结构

123.PNG

2.4 购物车

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" vmodel='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>

2.5 mock数据

​ 简单的mock,使⽤⾃带的webpack-dev-server即可,新建vue.config.js扩展webpack设置

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://lovalhost: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}=awaitaxios.get('/cartList')
		this.cartList = data;
	} catch (error) {
		console.log(error);
	}
},

2.6 数据持久化

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
		})
	}
})
},

三、组件深入

组建分类

  • 通⽤组件
    • 基础组件,⼤部分UI都是这种组件,⽐如表单 布局 弹窗等
  • 业务组件
    • 与需求挂钩,会被复⽤,⽐如抽奖,摇⼀摇等
  • ⻚⾯组件
    • 每个⻚⾯都是⼀个组件,不会复用

使用第三方组件

​ ⽐如vue最流⾏的element,就是典型的通⽤组件,执⾏npm install element-ui安装

main.js

import Vue from 'vue';
import App from './App.vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

new Vue({
    el:'#app',
  	render: h => h(App),
})

在vue-cli中可以使⽤vue add element 安装
安装之前注意提前提交当前⼯作内容,脚⼿架会覆盖若⼲⽂件

0dfa8J.png

发现项⽬发⽣了变化,打开App.vue, ctrl+z 撤回

0dfW2d.png

此时可以在任意组件中使⽤

​ 关于组件设计,最重要的还是⾃⼰去设计组件,现在我们模仿elementui提供的表单组件,⼿写实现表单组件 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" vmodel="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组件中导⼊该组件,挂载,使⽤

组件设计

表单组件,组件分层

  1. Form负责定义校验规则
  2. FormtItem负责显示错误信息
  3. Input负责数据双向绑定
  4. 使⽤provide和inject内部共享数据

0dhge0.png

表单控件实现双向的数据绑定

Input.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'></minput>
//数据
data() {
	return {
		ruleForm: {
			name: "",
			pwd: ""
		},
		rules: {
			name: [
				{ required: true, message: "请输⼊名称" },
				{ min: 6, max: 10, message: "请输⼊6~10位⽤户名" }
			],
			pwd: [{ require: true, message: "请输⼊密码" }]
	}
  };
},

FormItem

  1. 获取当前输⼊框的规则
  2. 如果输⼊框和rule不匹配 显示错误信息
  3. Input组件中⽤户输⼊内容时,通知FormItem做校验
  4. 使⽤async-validator做出校验

Formltem.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"></minput>
</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);
	}
}

Formltem.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
	}
  }
};

Form

  1. 声明props中获取数据模型(model)和检验规则(rules)
  2. 当FormItem组件挂载完成时,通知Form组件开始缓存需要校验的
    表单项
  3. 将缓存的表单项进⾏统⼀处理,如果有⼀个是错误,则返回false.(思
    路:使⽤ promise.all() 进⾏处理)
  4. 声明校验⽅法,供⽗级组件⽅法调⽤validate()⽅法

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⽅法进⾏改造

Formltem.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('验证失败')
			}
	  });
  }
}
坚持这种真诚,那么总归能遇到良人。

posted on 2020-10-07 19:27  九酒馆  阅读(314)  评论(0编辑  收藏  举报

导航