在不少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多篇原创技术文章,海量的视频资料便可得到
备注“加群”,我会拉你进技术交流群,群里大牛学霸具在,哪怕您作个潜水鱼也会学到不少东西