表单验证: vee-validate
插件
- 安装
- npm i vee-validate@2 --save // 安装'2'版本的
- 插件
模块化
### plugins.validate.js
import Vue from 'vue'
import VeeValidate from 'vee-validate'
console.log('表单验证',VeeValidate) // 测试
Vue.use(VeeValidate)
### main.js
......
import "@/plugins/validate" // 加载插件
- 配置字段
### plugins.validate.js
import Vue from 'vue'
import VeeValidate from 'vee-validate'
import zh_CN from 'vee-validate/dist/locale/zh_CN'
Vue.use(VeeValidate)
// 表单验证
VeeValidate.Validator.localize('zh_CN', {
messages: {
...zh_CN.messages,
is: (field) => `${field}必须与密码相同` // 修改内置规则 message, 让确认密码与密码相同
},
attributes: { // 给校验的field属性名映射中文名称
phone: '手机号',
code: '验证码',
password: '密码',
password1: '确认密码',
agree:'用户协议'
}
})
// 自定义校验规则:用户必须同意协议
VeeValidate.Validator.extend('tongyi', {
validate: value => value,
getMessage: field => field + '必须同意'
})
- 使用: 在
注册页面
补充表单验证
的逻辑
### Register.index.vue
<div class="content">
<label>手机号:</label>
<input type="text" placeholder="手机号" v-model="phone" name="phone"
<!--v-validate传入配置项required和'正则'--> <!--配置错误消息提示-->
v-validate="{ required:true,regex:/^1\d{10}$/ }" :class="{ invalid:errors.has('phone') }">
<span class="error-msg">{{ errors.first("phone") }}</span>
</div>
<div class="content">
<label>验证码:</label>
<input type="text" placeholder="验证码" v-model="code" name="code"
<!--逻辑同上,只不过正则匹配不同-->
v-validate="{ required:true,regex:/^\d{6}$/ }" :class="{ invalid:errors.has('code') }">
<span class="error-msg">{{ errors.first("code") }}</span>
<button style="width: 100px;height: 38px;" @click="getCode">获取验证码</button>
</div>
<div class="content">
<label>登录密码:</label>
<input placeholder="请输入你的密码" v-model="password" name="password" type="password"
<!--正则:允许8~20位数字+密码-->
v-validate="{ required: true, regex: /^[0-9A-Za-z]{8,20}$/ }"
:class="{ invalid: errors.has('password') }"/>
<span class="error-msg">{{ errors.first("password") }}</span>
</div>
<div class="content">
<label>确认密码:</label>
<!--保证和password一致即可-->
<input placeholder="请再次输入你的密码" v-model="password1" name="password1" type="password"
v-validate="{ required: true, is: password}" :class="{ invalid: errors.has('password1') }"/>
<span class="error-msg">{{ errors.first("password1") }}</span>
</div>
<div class="controls">
<!--使用了'自定义校验'-->
<input type="checkbox" v-model="agree" name="agree" v-validate="{ required: true, tongyi: true}"
:class="{ invalid: errors.has('agree') }"/>
<span>同意协议并注册《尚品汇用户协议》</span>
<span class="error-msg">{{ errors.first("agree") }}</span>
</div>
......
methods:{
async getCode(){
......
},
async userRegister(){
// 确保所有的字段通过校验,再进行后续的逻辑
const success = await this.$validator.validateAll()
if(success){
try{
const {phone,code ,password,password1} = this;
phone && code && password && password1 && (await this.$store.dispatch('userRegister',{phone,code,password}))
this.$router.push('/login')
}catch(error){
alert(error.message)
}
}
}
}
路由懒加载
- 引入场景
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效
### router.routes.js
......
{
path: "/home",
// component: Home,
// 访问该路由的时候,才加载该组件,节省性能的开销
component: ()=>import('@/pages/Home'),
meta: {
show: true
}
},
打包上线
- 命令如下:
- npm run build
- 项目打包后,代码都是经过压缩加密的,如果运行时报错,输出的错误信息无法准确得知是哪里的代码报错
- 有了
map
就可以像未加密的代码一样,准确输出哪一个行代码有错(该文件如果项目不需要,可以除去)
### vue.config.js配置
......
productionSourceMap:false
自定义事件
深入
- 给组件绑定
原生DOM
事件,默认绑定的是自定义事件
<template>
<!--此时单击该子组件,是不会触发该事件的-->
<ChildrenComponent1 @click="xxx"></ChildrenComponent1>
</template>
- 此时如果想触发click事件(即触发原生DOM事件),加一句即可
<!--"@click.native"就行了-->
<ChildrenComponent1 @click.native="xxx"></ChildrenComponent1>
- 类似自定义事件这样的写法: <ChildrenComponent1 @click="xxx"></ChildrenComponent1>
本质就是把"click事件"绑定到子组件ChildrenComponent1的根元素div节点上(也就是'事件委托')
所以点击,根div节点下面的所有元素,该click事件均会被触发
- 注意事项: 给原生DOM元素绑定自定义事件没有任何意义,因为不会触发
$emit
v-model
深入解析
- 基本使用: 收集用户输入的数据,实现数据的双向绑定
......
<input type="text" v-model="msg">
<span>{{msg}}</span>
......
data() {
return {
......
msg:''
}
},
- v-model实现的原理: 单向绑定+事件
......
<!--单向绑定 :value + @input-->
<input type="text" :value="msg1" @input="msg1=$event.target.value">
<span>{{msg1}}</span>
......
data() {
return {
......
msg1:''
}
},
- 利用v-model的原理,实现父子组件之间的通讯
### 父组件
......
<!--单向绑定+自定义事件input-->
<!--'$event'表示的是'接收子组件传过来的返回值'-->
<SearchSelector :value="msg" @input="msg=$event"/>
### 子组件
......
<!--接收value值并利用原生DOM的input事件,去触发自定义事件'input',并传值-->
<input type="text" :value="value" @input="$emit('input',$event.target.value)">
export default {
......
props:['value']
}
- 最终实现效果: 只要修改msg的值,父子组件之间的数据实现同步,即相同
- 父组件的代码,可以化简: <SearchSelector v-model="msg"/>,一模一样的效果
- 注意: 此时 子组件的props接收值可以任意命名
sync修饰符
:意思就是同步,实现父子组件之间数据的同步(即一致)
### 父组件
......
<!--传值并绑定自定义事件(接收子组件传过来的值,':money'指明要更新的是money属性)-->
<SearchSelector :money="money" @update:money="money=$event"/>
......
data() {
return {
......
money:1000
}
},
### 子组件
......
<!--接收money值并利用原生DOM的click事件,去触发自定义事件'update:money',并传值-->
<button @click="$emit('update:money',money-100)">{{money}}</button>
export default {
......
props:['money']
}
- 最终实现效果: 点击button,父子组件之间的数据money实现同步,即相同
- 引入sync: 实质就是'父组件'写法的'语法糖',简写
<!--一模一样的效果-->
<SearchSelector :money.sync="money"/>
- 注意: 此时子组件的'update'方法不能随意命名
sync
文档参考地址
- https://www.cnblogs.com/aimee2004/p/15776454.html
- 小结: 看到组件内传
v-model
或者sync
修饰符```即表明,父子组件之间数据互相影响并且保持一致
$attrs
属性介绍
-
作用: 获取父组件传过来的所有属性值(前提是: props不接收,若接收了,$attrs就取不到值)
-
引入场景: 二次封装element-ui-button,增加
鼠标hover提示
- 先安装'element-ui'插件
- 新建组件
### HintButton.vue
<template>
<div>
<!--运用父组件传过来的四个值-->
<a :title="title">
<el-button :type="type" :icon="icon" :size="size">添加</el-button>
</a>
</div>
</template>
<script>
export default {
name: 'HintButton',
props:["type","icon","size","title"], // 接收父组件传过来的四个值
mounted(){
console.log(this.$attrs) // 此时为 {} 空对象,因为被props占用了
}
}
</script>
### 父组件
......
<!--给子组件传4个值-->
<!--当鼠标移动到该按钮时,会有hover提示-->
<HintButton type="primary" icon="el-icon-plus" size="mini" title="提示按钮"></HintButton>
......
<script>
......'
import HintButton from '../HintButton/index.vue'
export default {
name: 'SearchSelector',
......
components:{
HintButton // 注册
}
}
</script>
- 现在,使用$attrs实现一模一样的效果(代码可以更加精简)
### 父组件不变,小改子组件
<template>
<div>
<!--直接从$attrs取值,更为方便-->
<a :title="$attrs.title">
<el-button :type="$attrs.type" :icon="$attrs.icon" :size="$attrs.size">添加</el-button>
</a>
</div>
</template>
<script>
export default {
name: 'HintButton',
// props:["type","icon","size","title"], // 必须注释掉
}
</script>
- 上述写法可以进一步精简成以下模样
<template>
<div>
<a :title="$attrs.title">
<!-- <el-button :type="$attrs.type" :icon="$attrs.icon" :size="$attrs.size">添加</el-button> -->
<!--最终精简:获取所有的$attrs属性,然后打散后分配给el-button-->
<el-button v-bind="$attrs">添加</el-button>
</a>
</div>
</template>
$listeners
属性介绍
-
作用: 获取父组件传过来的自定义事件对象
-
引入场景: 父组件给子组件传递自定义事件,由子组件触发该事件
### 父组件
......
<!--绑定自定义事件click(这里若想实现原生click事件,加'.native'即可)-->
<HintButton ...... @click="handler"></HintButton>
### 子组件
<template>
<div>
<a :title="$attrs.title">
<!--使用v-on触发'自定义事件'-->
<el-button v-bind="$attrs" v-on="$listeners">添加</el-button>
</a>
</div>
</template>
<script>
export default {
name: 'HintButton',
......
mounted(){
console.log(this.$listeners) // {click:f......}
}
}
</script>
$children
和$parent
介绍
-
$children
获取所有的子组件 -
$parent
获取父组件 -
引入场景demo
### 父组件
......
父亲的钱是: {{money}}元
<button @click="borrowFromSon">找儿子借100元</button>
<SearchSelector ref="son"/>
......
data() {
return {
......
money:1000 // 初始化值
}
},
methods:{
......
borrowFromSon(){ // 同时操作本组件和子组件的值
this.money +=100
this.$refs.son.qian -= 100
}
},
### 子组件
......
儿子的钱是: {{qian}}
......
data(){
return {
qian:700 // 初始化
}
},
- 使用
$children
实现上述场景
### 父组件
......
父亲的钱是: {{money}}元
<!--绑定事件-->
<button @click="borrowAll">向所有人借150元</button>
<SearchSelector ref="son"/>
......
data() {
return {
......
money:1000 // 初始化值
}
},
methods:{
......
borrowAll(){
this.money +=150
// $children 是array,存储所有的子组件实例
// 注意不要用索引($children[0])去访问,可能该'项'不是你想要的'项'
this.$children.forEach(item=>{
item.qian -= 150
})
}
},
### 子组件不变
- 使用
$parent
demo
### 子组件
......
儿子的钱是: {{qian}}
<button @click="sendMoney(200)">给父亲200块</button>
......
methods:{
......
sendMoney(money){
this.qian -= money
this.$parent.money += money // 获取父组件实例并赋值
}
}
ref属性
的两个作用- 标识该组件
- 获取该组件实例的所有信息
mixin
: 打包公用的js逻辑
### 新建 myMixin目录,新建 myMixin.js
export default {
methods:{
giveMoney(money){
this.money -= money;
this.$parent.money += money
}
}
}
### 组件使用
......
import myMixin from 'xxxx'
mixins:[myMixin] // 使用钩子配置项