• 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('验证失败')
    			}
    	  });
      }
    }
    
  • 相关阅读:
    前端性能优化
    CSS实现文字在容器中对齐方式
    spring里面的context:component-scan
    Builder模式
    idea无法新建maven项目
    Java-位运算
    pg数据库数据表异常挂起
    Java-多线程与单例
    Java-数据结构之二叉树练习
    Java-JNA使用心得
  • 原文地址:https://www.cnblogs.com/jiaxiaozia/p/13778563.html
Copyright © 2020-2023  润新知