仗剑天涯,从这开始
- 1、正则验证
- 2、vue当中promise的简单使用
- 3、map与forEach
- 4、时间式化---生日
- 5、过滤器
- 6、输入框input触发事件
- 7、截取-字符串-数组
- 8、vuex全局状态管理(接口请求)
- 8.1、async/await
- 8.2、es5数组解构
- 9、vue架构的理解,原型链,注册挂载
- 9.1、vue.use() - new vue() - vue.prototype
- 10、定时器的销毁
- 11、cook验证、鉴权
- 12、时间日期过滤器写成公共js组件
- 13、公共组件的封装,除父子组件外的其他方案
- 13.1、公共js的封装
- 14、 JSON.stringify()与JSON.parse()的区别
- 15、 自定义指令
- 16、vue中的name属性作用
- 17、防抖与节流
- 19、表单的新增编辑和查看
- 20、this.$set的使用详解
- 29、小图标使用data uri
- 30、class的动态使用
- 31、img图片的动态使用
- 32.nodemodules的删除
- 33.vue中使用element-resize-detector
- 合理的创建标题,有助于目录的生成
- 如何改变文本的样式
- 插入链接与图片
1、正则验证
②正则表达式(常用)
点击参考链接查看
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
---|---|---|
经度(-180°-180°) | ` /^(- | +)?(((\d |
纬度(-90°-90°) | `/^(- | +)?([0-8]?\d{1}.\d{0,6} |
手机号1 | `/^(13[0-9] | 14[01456879] |
手机号2 | `/^1(3[0-9] | 4[01456879] |
座机号码 | /^(0\d{2,3})-?(\d{7,8})$/ | |
电子邮箱 | /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/ | |
身份证号码(普通) | `/(^\d{15}$) | (^\d{18}$) |
身份证号码(精准18位) | `/1\d{5}(19 | 20)\d{2}((0[1-9]) |
身份证号码(精准15位) | `/2\d{5}\d{2}((0[1-9]) | (1[0-2]))(([0-2][1-9]) |
身份证号码(后6位) | `/^(([0-2][1-9]) | 10 |
QQ号码 | /^[1-9][0-9]\d{4,9}$/ | |
邮政编码 | /^[1-9]\d{5}$/ | |
车牌号 | /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/ | |
| ||
特殊字符 | /[!%*_~?&#$@/\\()<>{}[\] ]/gi | 特殊字符 |
包含中文 | /[\u4E00-\u9FA5]/ | |
限制字符串长度32位 | `/^(a-z | A-Z |
姓名 | `^([\u4e00-\u9fa5]{1,20} | [a-zA-Z.\s]{1,20})$` |
2-4个中文字符正则(姓名) | /^[\u4e00-\u9fa5]{2,4}$/ | 阿凡 |
注册账号-用户名 | /^[a-zA-Z0-9_-]{4,16}$/ | 用户名正则,4到16位(字母,数字,下划线,减号) |
注册账号-用户名 | /^[a-zA-Z][a-zA-Z0-9_]{4,15}$/ | 搜索使用 |
密码强度 | /^.*(?=.{6,})(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$/ | 密码正则,最少6位,包括至少1个大写字母,1个小写字母,1个数字,1个特殊字符 |
| ||
0和正整数 | /^\d+$/ | |
正整数 | /^[0-9]*[1-9][0-9]*$/ | |
负整数 | /^-\d+$/ | |
整数 | /^-?\d+$/ | |
正数 | /^\d*\.?\d+$/ | |
负数 | /^-\d*\.?\d+$/ | |
正纯数字(可带两位小数)单价 | `/(3\d*(.\d{1,2})?$) | (^0(.\d{1,2})?$)/` |
正纯数字 (可有小数) | /^\d+(\.\d+)?$/ | 12.41241242 |
纯数字-可有小数 | `/-?(0 | [1-9]\d*)(.\d+)?/` |
纯数字-可有小数 | /^-?\d*\.?\d+$/ | 正数、负数、0 |
①经纬度的正则验证
1、经度整数部分为0-180,小数部分为0到6位!';
// 吴凡凡的赵本山
var longrg = /^(\-|\+)?(((\d|[1-9]\d|1[0-7]\d|0{1,3})\.\d{0,6})|(\d|[1-9]\d|1[0-7]\d|0{1,3})|180\.0{0,6}|180)$/;
var bb = 176.1234522
longrg.test(bb)
2、纬度-90-90之间,小数0-6位
var latreg = /^(\-|\+)?([0-8]?\d{1}\.\d{0,6}|90\.0{0,6}|[0-8]?\d{1}|90)$/;
var lat = $("#itemform [id='lat']").val();
if(!latreg.test(lat)){
return '纬度整数部分为0-90,小数部分为0到6位!';
}
3、经度(未测)
var longitude = $("#dia-longitude").val();
var latitude = $("#dia-latitude").val();
var lon = /^-?((0|[1-9]\d?|1[1-7]\d)(\.\d{1,7})?|180(\.0{1,7})?)?$/;
var lat = /^-?((0|[1-8]\d|)(\.\d{1,7})?|90(\.0{1,7})?)?$/;
var lonRe = new RegExp(lon);
var latRe = new RegExp(lat);
alert(lonRe.test(longitude));//返回true OR false
if (!lonRe.test(longitude)) {
alertMsg.warn("经度不符合规范:经度整数部分为0-180,小数部分为0-6位!");
return false;
}
if (!latRe.test(latitude)) {
alertMsg.warn("纬度不符合规范:纬度整数部分为0-90,小数部分为0-6位!!");
return false;
}
4、纬度(未测)
//检查经度是否合法
function checkLon(lon) {
var reg = /^-?((0|1?[0-8]?[0-9]?)(([.][0-9]{1,10})?)|180(([.][0]{1,10})?))$/;
return reg.test(lon);
};
//检查纬度是否合法
function checkLat(lat) {
var reg = /^-?((0|[1-8]?[0-9]?)(([.][0-9]{1,10})?)|90(([.][0]{1,10})?))$/;
return reg.test(lat);
};
③ 输入框正则focus和select判断
<script>
var reg_user = /^[\u4e00-\u9fa5]{2,4}$/; //2-4个中文字符正则
var reg_tel = /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/; //11位手机号码正则
function checkSubmit() {
if (document.form.user.value =="") {
alert("姓名不能为空!");
document.form.user.focus();
return false;
} else if (!reg_user.test(document.form.user.value)) {
alert("姓名只能为2-4中文!");
document.form.user.focus();
document.form.user.select();
return false;
}
if (document.form.tel.value =="") {
alert("请填写您的手机号码!");
document.form.tel.focus();
document.form.tel.select();
return false;
} else if (!reg_tel.test(document.form.tel.value)) {
alert("请正确填写您的手机号码!");
document.form.tel.focus();
document.form.tel.select();
return false;
}
}
</script>
④ iview中正则使用案例
去iview官网查看
<Form-item label="价格" prop="showPrice">
<InputNumber
:max="100000" // 最多6位
:min="0" // 最小为0
:precision="2" //两位小数
size="small"
v-model="addForm.showPrice"></InputNumber>元
</Form-item>
⑤element-ui表单验证
点击参考链接查看
model : 绑定整个表单model值
rules : 整个表单校验规则
ref :获取该表单form组件
prop : 绑定每个表单的规则,写在el-form-item上
validate : 对整个表单进行校验的方法
valid : 每个必填表单项都提交为true,否则为false
简单
//使用element-ui 页面组件
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm">
<el-form-item label="活动名称" prop="name">
<el-input v-model="ruleForm.name"></el-input>
</el-form-item>
<el-form-item label="活动区域" prop="region">
<el-select v-model="ruleForm.region" placeholder="请选择活动区域">
<el-option label="区域一" value="shanghai"></el-option>
<el-option label="区域二" value="beijing"></el-option>
</el-select>
</el-form-item>
</el-form>
<el-form-item>
<el-button type="primary" @click="submitForm('ruleForm')">立即创建</el-button>
</el-form-item>
//data中定义form表单中每项表单model值、和每个表单校验的规则
<script>
export default {
data() {
return {
ruleForm: {
name: '',
region: '',
},
rules: {
name: [
{ required: true, message: '请输入活动名称', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' }
],
region: [
{ required: true, message: '请选择活动区域', trigger: 'change' }
],
};
},
//定义表单提交方法
methods: {
submitForm(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
alert('submit!');
} else {
console.log('error submit!!');
return false;
}
});
},
}
}
复杂版
对于页面有多个整体的form提交,每个form表单里类型为文本框、下拉框、单选框、复选框等类型,可对类型进行封装,使用循环遍历使页面呈现不同类型表单项
<el-form v-if="fromHtmlList.deviceInfo.sitsb.length" :model="fasj" :rules="rules" label-position="left" label-width="125px" ref='sitsb'>
<el-form-item label-width="100%" >
<div slot="label" class="titleInfo">局端设备</div>
</el-form-item>
<el-form-item v-for="(item, i) in fromHtmlList.deviceInfo.sitsb"
:key="`F_key_${i}`"
:label="`${item.label}:`"
:class="item.class"
:label-width="item.labelWidth"
:prop="`${item.rule ? 'deviceInfo.sitsb.'+item.rule : ''}`">
<el-select v-if="item.el === 'slt'" v-model="fasj.deviceInfo.sitsb[item.model]" placeholder="请选择" style="width:200px;" clearable>
<el-option v-for="(op, n) in options[item.option]"
:key="`vop_key_${n}`"
:label="op.label"
:value="op.value">
</el-option>
</el-select>
<el-input v-if="item.el === 'ipt'" v-model="fasj.deviceInfo.sitsb[item.model]" @focus="()=>{ item.focus && handleFocus('sitsb', item.model)}" :placeholder="`${item.placeholder ? item.placeholder : '请输入'}`" style="width:200px;"></el-input>
<span v-if="item.el === 'txt'">{{fasj.base[item.model]}}</span>
</el-form-item>
</el-form>
<div class="btn-list">
<el-button type="primary" @click="checkRequired">提 交</el-button>
</div>
<script>
export default {
data() {
return {
fasj: {
base: {}, // 基本信息
deviceInfo: {},
},
//定义 页面表单结构
fromHtmlList: {
baseInfo: [],
deviceInfo: {
ywzsb: [],
sitsb: []
}
},
//定义 表单校验规则
rules: {
'deviceInfo.sitsb.IPType': [
{ required: true, message: '请输入', trigger: 'blur' }
],
'deviceInfo.sitsb.IPAddr': [
{ required: true, message: '请输入', trigger: 'blur' }
],
'deviceInfo.sitsb.netmask': [
{ required: true, message: '请输入', trigger: 'blur' }
],
'deviceInfo.sitsb.vlanId': [
{ required: true, message: '请输入', trigger: 'blur' }
],
},
options: {
IPType: [
{label: 'ipv4', value: 'ipv4'},
{label: 'ipv6', value: 'ipv6'}
],
rmSite: [],
room: [],
},
}
},
created(){
const orderType = '业务开通' ;//页面初始加载用到的变量值,可通过组件传值获取
//created中初始定义页面表单结构
this.fromHtmlList = {
baseInfo: orderType === '业务开通' ? [
{label: '接入方式', labelWidth: '100px', rule: '' , model: 'serviceType', option: '', el: 'txt', class: 'w2'},
{label: '标准九级地址', labelWidth: '120px', rule: '', model: 'aendAdreesName', option: '', el: 'txt', class: 'w2'},
{label: '城乡类型', labelWidth: '100px', rule: 'cityType', model: 'cityType', option: 'cityType', el: 'slt', class: 'w2'},
{label: '方案设计附件', labelWidth: '120px', rule: '', model: 'fileId', option: '', el: 'btn', class: 'w2'},
] : orderType === '移机' ? [
{label: '接入方式', labelWidth: '100px', rule: '' , model: 'serviceType', option: '', el: 'txt', class: 'w2'},
{label: '标准九级地址', labelWidth: '120px', rule: '', model: 'aendAdreesName', option: '', el: 'txt', class: 'w2'},
{label: '城乡类型', labelWidth: '100px', rule: 'cityType', model: 'cityType', option: 'cityType', el: 'slt', class: 'w2'},
{label: '方案设计附件', labelWidth: '120px', rule: '', model: 'fileId', option: '', el: 'btn', class: 'w2'},
{label: '移机场景', labelWidth: '100px', rule: 'moveScene', model: 'moveScene', option: 'moveScene', el: 'slt', class: 'w2'},
{label: '是否需要网络调整', labelWidth: '', rule: 'netChange', model: 'netChange', option: 'netChange', el: 'slt', class: 'w2'}
] : [
{label: '接入方式', labelWidth: '100px', rule: '' , model: 'serviceType', option: '', el: 'txt', class: 'w2'},
{label: '标准九级地址', labelWidth: '120px', rule: '', model: 'aendAdreesName', option: '', el: 'txt', class: 'w2'},
{label: '城乡类型', labelWidth: '100px', rule: 'cityType', model: 'cityType', option: 'cityType', el: 'slt', class: 'w2'},
{label: '方案设计附件', labelWidth: '120px', rule: '', model: 'fileId', option: '', el: 'btn', class: 'w2'},
{label: '是否需要网络调整', labelWidth: '', rule: 'netChange', model: 'netChange', option: 'netChange', el: 'slt', class: 'w2'},
{label: '是否更换末端设备', labelWidth: '', rule: 'devChange', model: 'devChange', option: 'devChange', el: 'slt', class: 'w2'}
],
deviceInfo: {
ywzsb: serviceType === 'PON' ? [
// 默认PON
{label: '机房', labelWidth: '90px', rule: '' , model: 'siteRoom', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'},
{label: '设备名称', labelWidth: '90px', rule: '', model: 'deviceName', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'},
{label: '设备端口', labelWidth: '90px', rule: '', model: 'devicePort', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'},
{label: '延伸设备', labelWidth: '90px', rule: '', model: 'deviceExtend', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'}
] : [
// 裸光纤、传输下沉、传输延伸(ywzsb)
{label: '机房', labelWidth: '', rule: '' , model: 'siteRoom', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'},
{label: '站点', labelWidth: '', rule: '', model: 'site', option: '', el: 'ipt', class: 'w2'},
{label: '设备名称', labelWidth: '', rule: '', model: 'deviceName', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'},
{label: '设备端口', labelWidth: '', rule: '', model: 'devicePort', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'},
],
sitsb: serviceType === 'PON' ? [
{label: '机房名称', labelWidth: '', rule: '' , model: 'siteRoom', option: '', el: 'ipt', class: 'w2'},
{label: '站点名称', labelWidth: '', rule: '', model: 'site', option: '', el: 'ipt', class: 'w2'},
{label: '汇聚交换机', labelWidth: '', rule: '', model: 'switch', option: '', el: 'ipt', class: 'w2'},
{label: '汇聚交换机端口', labelWidth: '', rule: '', model: 'switchPort', option: '', el: 'ipt', class: 'w2'},
{label: 'SR/BARS', labelWidth: '', rule: '' , model: 'srbars', option: '', el: 'ipt', class: 'w2'},
{label: 'SR/BARS端口', labelWidth: '', rule: '', model: 'srbarsPort', option: '', el: 'ipt', class: 'w2'},
{label: 'IP地址类型', labelWidth: '', rule: 'IPType', model: 'IPType', option: 'IPType', el: 'slt', class: 'w2'},
{label: 'IP地址', labelWidth: '', rule: 'IPAddr', model: 'IPAddr', option: '', el: 'ipt', class: 'w2'},
{label: '子网掩码', labelWidth: '', rule: 'netmask', model: 'netmask', option: '', el: 'ipt', class: 'w2'},
{label: 'VLAN ID', labelWidth: '', rule: 'vlanId', model: 'vlanId', option: '', el: 'ipt', class: 'w2'}
] : serviceType === '裸光纤' ? [
// 裸光纤
{label: '机房名称', labelWidth: '', rule: '' , model: 'siteRoom', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'},
{label: '站点名称', labelWidth: '', rule: '', model: 'site', option: '', el: 'ipt', class: 'w2'},
{label: 'SR', labelWidth: '', rule: '', model: 'SR', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'},
{label: 'SR端口', labelWidth: '', rule: '', model: 'SRPort', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'},
{label: 'BARS', labelWidth: '', rule: '' , model: 'BARS', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'},
{label: 'BARS端口', labelWidth: '', rule: '', model: 'BARSPort', option: '', el: 'ipt', class: 'w2', focus: true, placeholder: '点击查询'},
] : [
// 传输下沉、传输延伸
]
}
}
},
methods:{
async checkRequired() {
let checkName = ['base','sitsb'];//表单校验定义ref
let text;
for(let key of checkName){
if(this.$refs[key]){
this.$refs[key].validate((valid) => {
if(!valid){
text = '请检查必填项';
}else {
return false;
}
});
}
}
if(text){
this.$message.warning(text);
return false;
}
let _res_ = await this.saveFasjData('', true);//页面表单要保存后才能提交
if(!!_res_ && _res_.code === 200){
this.commit(_param, _url);//保存表单再提交
}else{
this.$message.error(_res_.msg)
}
},
async commit(param, url) {
this.loadings.body = true;
let _res = await postDataRequest(url, param);
this.loadings.body = false;
if (_res && _res.code === 200) {
this.$message.success("提交成功");
this.$router.go(-1);
}
}
}
}
</script>
2、vue当中promise的简单使用
promise改进
基础语法
let promise = new Promise((resolve,reject) =>{
setTimout(() =>{
if(true){
//resolve将异步API的执行结果传递出去
resolve({name:"张三"})
}else{
//reject 也是一个函数,当判断失败的时候,将结果传递出去
reject('失败了')
}
},2000);
})
//成功了
promise.then(result => console.log(result));//{name:‘张三’}
.catch(error => console.log(error));//失败了
promise改进1
//1、引入系统模块fS
const fs = require('fs');
//2、创建一个promise对象
let promise = new Promise((resolve,reject) =>{
fs.readFile('./1.txt','utf8',(err,result) =>{
if(err !=null){
reject(err);
}else{
resolve(result);
}
});
});
//文件成功的信息
promise.then((result) =>{
console.log(result);
})
//文件失败的信息
.catch((err) =>{
console.log(err);
})
promise改进2
const fs = require('fs')
function p1(){
return new Promise((resolve,reject) =>{
fs.readFile('./1.txt','utf8',(err,result) =>{
resolve(result);
})
})
}
function p2(){
return new Promise((resolve,reject) =>{
fs.readFile('./2.txt','utf8',(err,result) =>{
resolve(result);
})
})
}
function p3(){
return new Promise((resolve,reject) =>{
fs.readFile('./3.txt','utf8',(err,result) =>{
resolve(result);
})
})
}
//依次执行1、2、3
p1().then((r1) =>{
console.log(r1);
//return p2
return p2();
})
.then((r2) =>{
console.log(r2);
//return p3()
return p3();
})
.then((r3) =>{
console.log(r3);
})
async/await(ES7优化,异步函数)
//1.在普通函数定义的前面加上async关键字 普通函数就变成的异步函数
//2.异步函数默认的返回值是promise对象
//3.在异步函数内部使用throw关键字进行错误的抛出
//await关键字
//1.它只能出现在异步函数中
//2.await promise 它可以暂停异步函数的执行 等待promise对象返回结果后在向下执行
async function p1() {
return '1';
}
async function p2() {
return '2';
}
async function p3() {
return '3';
}
async function run() {
let r1 = await p1()
let r2 = await p2()
let r3 = await p3()
console.log(r1);
console.log(r2);
console.log(r3);
}
run();
3、map与forEach
map返回值的转换
// [a,b,c,d] 转换为 ↓↓↓↓↓↓
// [{id:1;value:a},{id:2;value:b},{id:3;value:c}]
// item当前值,index索引值,array处理的数组
//map可以return出来,不会改变原数组,只要打印当前var的变量即可
var arr = [a,b,c,d];
var wf = arr.map((item,index,array)=>{
var cc = {}
cc.id = index;
cc.value = item;
return arr[index] = cc;
})
console.log(wf)
forEach返回值的转换(不能return出来,不能直接打印)
// [a,b,c,d] 转换为 ↓↓↓↓↓↓
// [{id:1;value:a},{id:2;value:b},{id:3;value:c}]
// item当前值,index索引值,array处理的数组
var arr = [a,b,c,d];
arr.forEach((item,index,array)=>{
var cc = {}
cc.id = index;
cc.value = item;
arr[index] = cc;
})
console.log(arr)
帅帅测试
将
转换为
let obj = [
{
lat:29.632721176569707,
lon: 106.43574027198078
},{
lat: 29.63381009352998,
lon: 106.51008742120835
}
]
let arr=[]
obj.forEach(item=>{
arr.push([item.lon,item.lat])
})
console.log(arr)
//******或者********//
obj.forEach((item,index)=>{
arr[index] = [item.lon,item.lat]
})
//******或者********//
let arr=[]
var wf = obj.map((item,index)=>{
return arr[index] = [item.lon,item.lat]
})
console.log(wf)
//******或者********//
var wf = obj.map((item,index)=>{
return obj[index] = [item.lon,item.lat]
})
console.log(wf)
//filter过滤器
const school = [
{
occupation:"老师",
age:40
},
{
occupation:"学生",
age:23
},{
occupation:"程序猿",
age:1
}
]
var newShool = school.filter(item => item.age > 20)
console.log(newShool);//[ { occupation: '老师', age: 40 }, { occupation: '学生', age: 23 } ]
4、时间式化—生日
①时间戳格式化—生日
iview中将updateAt: 1640447539380(13位)转换为↓
{ title: '更新时间', key: 'updateAt',
render:(h,params)=>{
return h('span',time.getDate(params.row.updateAt))
},
align: 'center', width: 160 },
import time from '@/utils/helpers/timeLite'
import 'date-format-lite'
const DATE_TIME_FORMATTER = 'YYYY-MM-DD hh:mm:ss'
const DATE_FORMATTER = 'YYYY-MM-DD'
const TIME_FORMATTER = 'hh:mm:ss'
export default {
name: 'time',
/**
* 格式化时间
* @param {string} time 时间
* @param {string} formatter 格式
* @return {string}
*/
format (time, formatter) {
return time ? time.date(formatter) : ''
},
/**
* 年-月-日 时:分:秒
* @param {string} time 时间
* @return {string}
*/
getDateTime (time) {
return this.format(time, DATE_TIME_FORMATTER)
},
/**
* 年-月-日
* @param {string} time 时间
* @return {string}
*/
getDate (time) {
return this.format(time, DATE_FORMATTER)
},
/**
* 时:分:秒
* @param {string} time 时间
* @return {string}
*/
getTime (time) {
return this.format(time, TIME_FORMATTER)
},
/**
* 在 time 基础上加 unit 秒、分钟...
* @param {string} time 时间
* @param {string} num 数量
* @param {string} unit 单位(eg: seconds, minutes, hours, days, weeks, months, years)
*/
add (time, num, unit) {
return this.format(time).add(num, unit)
}
}
②时间格式化date-format-lite插件—生日
npm install date-format-lite --save
var now = new Date() // Date {Wed Jul 10 2013 16:47:36 GMT+0300 (EEST)}
now.format("iso") // 2013-07-10T13:47:36Z
now.format("hh:mm") // 16:47 (local time)
now.format("UTC:hh:mm") // 13:47
now.format("hh:mm", 2.5) // 16:17
now.format("iso") // 2013-07-10T13:47:36Z
now.add(1, "days").format("iso") // 2013-07-11T13:47:36Z
now.add(-2, "hours").format("iso") // 2013-07-11T11:47:36Z
"2013-07-10".date() // Date {Wed Jul 10 2013 03:00:00 GMT+0300 (EEST)}
"2013-07-10T13:47:36Z".date() // Date {Wed Jul 10 2013 16:47:36 GMT+0300 (EEST)}
"10/07/2013".date() // Date {Wed Jul 10 2013 03:00:00 GMT+0300 (EEST)}
Date.middleEndian = true
"10/07/2013".date() // Date {Mon Oct 07 2013 03:00:00 GMT+0300 (EEST)}
// Change format
"10/07/2013".date("YYYY-MM-DD")// 2013-07-10
Date.masks.my = '"DayNo "D'
now.format("my") // DayNo 10
Date.masks.default = 'YYYY-MM-DD hh:mm:ss'
now.format() // 2013-07-10 13:47:36
/ Add to estonian-lang.js
Date.names = "Jaan Veeb Märts Apr Mai Juuni Juuli Aug Sept Okt Nov Dets jaanuar veebruar märts aprill mai juuni juuli august september oktoober november detsember P E T K N R L pühapäev esmaspäev teisipäev kolmapäev neljapäev reede laupäev".split(" ")
// Change AM and PM
Date.am = "a.m."
Date.pm = "p.m."
③时间格式化moment插件—生日
点击npm引入moment网友教程查看
//1、moment插件进行npm安装
npm install moment --save
//2、在main.js中进行插件的引入,引入方式和axios的引入方式一致。
import moment from 'moment'
Vue.prototype.$moment = moment
//3、在对应的页面位置进行使用即可
//格式处理为年-月-日:
<div class="timeFormat">{{$moment(item).format('YYYY-MM-DD')}}</div>
//格式只保留小时:分钟
<div class="timeFormat">{{$moment(item).format('HH:SS')}}</div>
//注:item为需要处理的时间变量
//YYYY-MM-DD等为处理成的时间格式
Thu Apr 08 2021 00:00:00 GMT+0800 (中国标准时间) {} ----->1617897599000
//初始化参数
initParams(daterange) {
const params = {
startTime: this.$moment(
this.$moment(timeRange[0]).format("YYYY-MM-DD 00:00:00")
).format("x"),
endTime: this.$moment(
this.$moment(timeRange[1]).format("YYYY-MM-DD 23:59:59")
).format("x"),
// curMiningArea: this.curMine.id,
};
return params;
},
initdaterange: [
this.$moment().subtract(1, "years").format("YYYY-MM-DD"),
this.$moment().format("YYYY-MM-DD"),
],
④时间格式化javascript生日
{ title: '登记时间', key: 'createAt', align: 'center', width:150,
render: (h, params) => {
return h('span',this.dateTransform(params.row.createAt))
}
},
methods: {
dateTransform(inputTime){
// var date = new Date(inputTime*1000); //时间戳为10位需*1000,时间戳为13位的话不需乘1000
var date = new Date(inputTime);
var y = date.getFullYear();
var m = date.getMonth() + 1;
m = m < 10 ? ('0' + m) : m;
var d = date.getDate();
d = d < 10 ? ('0' + d) : d;
var h = date.getHours();
h = h < 10 ? ('0' + h) : h;
var minute = date.getMinutes();
var second = date.getSeconds();
minute = minute < 10 ? ('0' + minute) : minute;
second = second < 10 ? ('0' + second) : second;
return y + '-' + m + '-' + d+' '+h+':'+minute+':'+second;
},
}
5、过滤器
1.组件过滤器
2.全局过滤器
///global.js中,注意是export
function gwf(x){
return 'wf全局封装的' + x
}
export { //注意这里是default
gwf
}
//main.js中*****************
// 1.封装
import * as filters from './utils/global.js'
Object.keys(filters).forEach(key=>{
Vue.filter(key,filters[key])//插入过滤器名和对应方法
})
// 2.直接使用
Vue.filter('wf', function (value) {
return 'wwwwffff'+ value
})
6、输入框input触发事件
element-ui | |
---|---|
@focus | |
@blur | |
@change | 失焦后与原数据对比触发 |
@input | 改变即触发 |
7、截取-字符串-数组
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII–数组 | HTML |
---|---|---|
split | ` /^(- | +)?(((\d |
TYPE | ASCII–字符串 | HTML |
---|---|---|
replace() | 返回新值 | 用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。 |
//replace
//1、把字符串中所有单词的首字母都转换为大写:
name = 'aaa bbb ccc';
uw=name.replace(/\b\w+\b/g, function(word){
return word.substring(0,1).toUpperCase()+word.substring(1);}
);
//2、在本例中,我们将把所有的花引号替换为直引号:
name = '"a", "b"';
name.replace(/"([^"]*)"/g, "'$1'");
//3、把 "Doe, John" 转换为 "John Doe" 的形式:
name = "Doe, John";
name.replace(/(\w+)\s*, \s*(\w+)/, "$2 $1");
//4、使用本例提供的代码来确保匹配字符串大写字符的正确:
text = "javascript Tutorial";
text.replace(/javascript/i, "JavaScript");
//5、全局替换,每当 "Microsoft" 被找到,它就被替换为 "W3School":
<script type="text/javascript">
var str="Welcome to Microsoft! "
str=str + "We are proud to announce that Microsoft has "
str=str + "one of the largest Web Developers sites in the world."
document.write(str.replace(/Microsoft/g, "W3School"))
//5、只替换第一个,将正则的g去掉
var str="Visit Microsoft!"
document.write(str.replace(/Microsoft/, "wf"))
8、vuex全局状态管理(接口请求)
vue在做大型项目时,会用到多状态管理,vuex允许我们将store分割成多个模块,每个模块内都有自己的state、mutation、action、getter。模块内还可以继续嵌套相对应的子模块。
- 页面列表的接口请求(将请求写在vuex当中)
- 用户个人信息管理模块
- 菜单
- 电商项目的购物车模块,每次都调用获取购物车数量的接口(前提得有),效果是实现了,但是每一次的HTTP请求,都是对浏览器性能消耗。 对比下来,用vuex的mutations进行触发,就显得更加有优势;
- 我的订单模块,订单列表也点击取消订单,然后更新对应的订单列表,这种情况也是用Vuex,state储存一个状态,监听这个状态,变化时更新对应的列表;
- 从订单结算页,进入选择优惠券的页面,选择优惠券的页面如何保存选择的优惠券信息?state保存优惠券信息,选择优惠券时,mutations提交,在订单结算页,获取选择的优惠券,并更新订单优惠信息;
Vuex中的mapState、mapMutations、mapActions、mapGetters(辅助函数配合扩展符)
import { mapStete, mapMutations,mapActions,mapGetters } from 'vuex'
export default{
data () {
},
computed: {
...mapState(['count']),//state只能写在计算属性computed中 这里可以在数组内写多个方法名
...mapGetters(['funcname'])//也是只能写在computed中,getters其实就是一个包装state的一个函数
//他改变不了state里面的内容 但是会随着state改变而改变 有点类似于计算属性
//调用方法同样为两种 一种为this.$store.getters.名称 第二种为映射
},
methods: {
...mapMutations(['sub','add']),
//上一篇提到的触发actions的第二种方式 其实就是这个mapActions的映射方法
...mapActions(['subAsync']),//store中actions里面的方法
handel1(){
this.sub('自定义的可选参数')
//注意 这里通过映射的函数方法 可以不在此处定义 而直接应用在事件所触发的方法名里面
//比如本来@click触发的handel1 现在可以直接@click sub('自定义的可选参数')
}
}
}
//ycg发丰
{{this.detail.item.vipData.vipMobile}}
import { mapState } from 'vuex'
computed: mapState([
'dictionaryStore',
'detail',
'projectList'
]),
initSourceInfo1 (val) {
this.$store.dispatch('pageVip1', {
params: {
mobile: val
}
}).then(() => {
this.$Message.success('请求成功!')
this.addButton = true;
// 查询成功将VIP值存起来
this.vipData = this.detail.item.vipData;
}, (res) => {
this.$Message.error(res.data.message)
this.addButton = false;
})
console.log('this.detail')
console.log(this.detail)
},
/**
* 会员中心-会员管理-详情----
*/
pageVip1 ({ commit }, options) {
commit(types.PAGE_STORE_DETAIL_LOADING, {})
// return new Model().addPath('product').addPath('page').GET(options).then((res) => {
return new Model().addPath('vip').GET(options).then((res) => {
commit(types.PAGE_STORE_DETAIL, {
data: res.data
})
})
},
对突变类型使用常量—官网解读
在各种 Flux 实现中使用常量来表示突变类型是一种常见的模式。这允许代码利用像 linter 这样的工具,并将所有常量放在一个文件中,让您的协作者可以一目了然地了解整个应用程序中可能发生的突变:
// mutation-types.js
export const SOME_MUTATION = 'SOME_MUTATION'
// store.js
import { createStore } from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = createStore({
state: { ... },
mutations: {
// we can use the ES2015 computed property name feature
// to use a constant as the function name
[SOME_MUTATION] (state) {
// mutate state
}
}
})
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // map `this.increment()` to `this.$store.commit('increment')`
// `mapMutations` also supports payloads:
'incrementBy' // map `this.incrementBy(amount)` to `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // map `this.add()` to `this.$store.commit('increment')`
})
}
}
// ******************************************** //
const store = createStore({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
在实践中,我们经常使用 ES2015参数解构来稍微简化代码(尤其是当我们需要commit多次调用时):
actions: {
increment ({ commit }) {
commit('increment')
}
}
store.dispatch('increment')
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
// dispatch with a payload
store.dispatch('incrementAsync', {
amount: 10
})
// dispatch with an object
store.dispatch({
type: 'incrementAsync',
amount: 10
})
实际操作的一个更实际的示例是结帐购物车的操作,其中涉及调用异步 API并提交多个突变:
actions: {
checkout ({ commit, state }, products) {
// save the items currently in the cart
const savedCartItems = [...state.cart.added]
// send out checkout request, and optimistically
// clear the cart
commit(types.CHECKOUT_REQUEST)
// the shop API accepts a success callback and a failure callback
shop.buyProducts(
products,
// handle success
() => commit(types.CHECKOUT_SUCCESS),
// handle failure
() => commit(types.CHECKOUT_FAILURE, savedCartItems)
)
}
}
您可以使用 调度组件中的操作this.$store.dispatch(‘xxx’),或使用mapActions将组件方法映射到store.dispatch调用的助手(需要根store注入):
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // map `this.increment()` to `this.$store.dispatch('increment')`
// `mapActions` also supports payloads:
'incrementBy' // map `this.incrementBy(amount)` to `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // map `this.add()` to `this.$store.dispatch('increment')`
})
}
}
组合动作
动作通常是异步的,那么我们如何知道动作何时完成?更重要的是,我们如何组合多个动作来处理更复杂的异步流?
首先要知道的是,它store.dispatch可以处理触发的动作处理程序返回的 Promise,它也返回 Promise:
actions: {
actionA ({ commit }) {
return new Promise((resolve, reject) => {
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
}
}
现在你可以这样做:
store.dispatch('actionA').then(() => {
// ...
})
在另一个动作中:
actions: {
// ...
actionB ({ dispatch, commit }) {
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}
最后,如果我们使用async / await,我们可以像这样组合我们的动作:
// assuming `getData()` and `getOtherData()` return Promises
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // wait for `actionA` to finish
commit('gotOtherData', await getOtherData())
}
}
astore.dispatch可以在不同的模块中触发多个动作处理程序。在这种情况下,返回的值将是一个 Promise,当所有触发的处理程序都已解析时,它会解析。
8.1、async/await
8.2、es5数组解构
//数组解构
const namess = ['Henry','Bucky','Emily'];
// 解构
const [name1,name2,name3] = namess ;
console.log(name1,name2,name3)//Henry Bucky Emily
//返回数组个数
const {length} = namess;//如果接收的方式是数组方式,写多少个返回多少个,如果是对象的方式,会把数组里的个数返回来
console.log(length)//3
// 结合展开运算符
const [name4,...set] = namess;
console.log(name4)//Henry
console.log(set)//[ 'Bucky', 'Emily' ]
console.log(name,set)//henry [ 'Bucky', 'Emily' ]
9、vue架构的理解,原型链,注册挂载
1、assets和static区别
区别一: assets文件时src下的,所以最后运行时需要进行打包;而static文件不需要打包就直接放在最终的文件中了。
区别二: assets中的文件在.vue中的template/style下用 …/ 这种相对路径的形式进行引用,在script下必须用 @import 的方式引入; 而 static 下的文件在.vue中的任何地方都只要使用 …/ 这种相对路径的方式引入。
assets
该目录下的文件会被 webpack 打包,引用时需要使用相对路径或者模块路径。
动态引用路径时写法:require(“./[folder]/[filename]”)
static 中长存放类包、插件等第三方的文件,assets里放属资源文件比如自己资源图片、css文件、js文件。
因为 webpack 使用的是 commenJS 规范,必须使用 require 才可以。
一般在 static 里放一些类库的文件,assets 放属于项目的资源文件。
2、css和js的引入
1、css
方案1、在main.js中引入方式
import '@/assets/css/reset.css' //@ 默认的是 src 文件夹
方案2、在.vue文件的style标签里面引入
@import "../assets/css/index.css";
2、js
1.可以通过
import timeStampChanges from '../../../assets/js/timeStampChange';
来导入该js文件,timeStampChanges为该文件的别名,通过别名.(方法/数据)来调用。
2.也可以直接
import {timeStampChange} from '../../../assets/js/timeStampChange';
来导入该js文件中的方法/数据,其中{}中的名字即为该js文件中的方法/数据。
static下引用
<script>
import xx from '/static/xx.js'
<script>
<style>
@import "xx.css";
</style>
3、img图片的引用
js部分:
data () {
return {
imgUrl: '图片地址',//错误写法
imgUrl: require('图片地址')//正确的写法
}
}
template部分:
img标签形式:
<img :src="img" />
或者p背景图形式:
<p :style="{backgroundImage: 'url(' + img + ')'}"></p>
9.1、vue.use() - new vue() - vue.prototype
1、vue.prototype:实例上挂载属性/方法,例如Vue.prototype.axios = axios;
点链接查看封装的公共的js和方法
2、vue.use:引入插件,例如vuex,vue.use(vuex)—vue.use会初始化插件,运行导出的intsall函数
区别:
1、不是vue写的插件不支持Vue.use()加载方式
2、非vue官方库不支持new Vue()方式
3、每一个vue组件都是Vue的实例,所以组件内this可以拿到Vue.prototype上添加的属性和方法。
总结
Vue的插件是一个对象,
插件对象必须有install字段.
install字段是一个函数.
初始化插件对象需要通过Vue.use()
10、定时器的销毁
const timer = setInterval(() =>{
该方法是通过$once这个事件侦听器器在定义完定时器之后的位置来清除定时器。
// 某些定时器操作
}, 500);
// 通过$once来监听定时器,在beforeDestroy钩子可以被清除。
this.$once('hook:beforeDestroy', () => {
clearInterval(timer);
})
11、cook验证、鉴权
12、时间日期过滤器写成公共js组件
13、公共组件的封装,除父子组件外的其他方案
13.1、公共js的封装
1、对 utils.js 通过 export default 默认导出为对象
//utils.js 内容:
//import {Message, MessageBox} from 'element-ui' // elementui 引入(这里根据个人需求决定是否需要导入)
export default {
//等同于test: function ()...
test(x){
console.log(x)
},
hello(){
console.log("Hello World!")
}
}
//在main.js中引入:
//公共js函数
import utils from "@/util/utils"; // @ 默认的是 src 文件夹,后面省略了utils.js 后面的后缀名js
Vue.prototype.$utils = utils; //直接定义在vue的原型上面
这样在组件中,就可以直接拿来用了,写法为:this.$utils.hello( )
2、对 utils 通过 export 默认导出
//utils.js 内容:
function test(x){
console.log(x)
}
function hello(){
console.log("Hello World!")
}
export default{ //注意这里是default
test,
hello
}
在组件中就可以通过 import utils from ‘@/util/utils.js’ 引入,用法为: utils.hello( )
3、对 utils.js 通过 export 普通导出
//utils.js 内容:
export function test(x){
console.log(x)
}
export function hello(){
console.log("Hello World!")
}
然后在组件中通过 import { test, hello } from ‘@/util/utils.js’ 引入(这里可以根据自己的需求,用到哪些函数,就引入哪些函数),用法为 test( )
14、 JSON.stringify()与JSON.parse()的区别
JSON.stringify()的作用是将 JavaScript 对象转换为 JSON 字符串,而JSON.parse()正好相反,可以将JSON字符串转为一个对象。
点击查看案例1
点击查看案例2
//fn可为函数对象或数组
JSON.stringify(testArr,fn,4)
15、 自定义指令
拖拽、聚焦、懒加载、loading、查看放大图片、防抖、水印、权限、copy
点击链接查看
1、半拖拽
//组件内指令
<template>
<div class="dragBox">
<div v-drag="elementPos" class="dragMe"></div>
</div>
</template>
<script>
export default {
name: 'drag',
data(){
return{
// 元素相对于父级位置
elementPos: {
left: null,
top: null,
},
}
},
watch:{
elementPos:{
handler(val){
console.log(val, 789);// 实时获取拖拽元素相对于父级位置
},
deep: true, immediate: true
}
},
directives:{
drag: {
//1.指令绑定到元素上回立刻执行bind函数,只执行一次
//2.每个函数中第一个参数永远是el,表示绑定指令的元素,el参数是原生js对象
bind: function (el, elementObj) {
let dragBox = el; //获取当前元素
dragBox.style.position = 'absolute'; // 拖拽元素使用定位,脱离文档流
dragBox.onmousedown = e => {
//鼠标相对元素的位置
let disX = e.clientX - dragBox.offsetLeft;
let disY = e.clientY - dragBox.offsetTop;
document.onmousemove = e => {
//鼠标的位置减去鼠标相对元素的位置,得到元素的位置
let left = e.clientX - disX;
let top = e.clientY - disY;
//移动当前元素
dragBox.style.left = left + 'px';
dragBox.style.top = top + 'px';
};
document.onmouseup = e => {
//鼠标弹起来的时候不再移动
document.onmousemove = null;
//预防鼠标弹起来后还会循环(即预防鼠标放上去的时候还会移动)
document.onmouseup = null;
// 对外暴露元素相对于父级位置
elementObj.value.left = dragBox.style.left;
elementObj.value.top = dragBox.style.top;
};
};
}
}
}
}
</script>
<style scoped>
.dragBox{
width:300px;
height:300px;
background: #ccc;
position: relative;
}
.dragMe{
cursor: move;
width: 100px;
height: 100px;
background: burlywood;
display: flex;
justify-content: center;
align-items: center;
}
</style>
//全局注册main.js
Vue.directive('drag', {
//1.指令绑定到元素上回立刻执行bind函数,只执行一次
//2.每个函数中第一个参数永远是el,表示绑定指令的元素,el参数是原生js对象
bind: function (el, elementObj) {
let dragBox = el; //获取当前元素
dragBox.style.position = 'absolute'; // 拖拽元素使用定位,脱离文档流
dragBox.onmousedown = e => {
//鼠标相对元素的位置
let disX = e.clientX - dragBox.offsetLeft;
let disY = e.clientY - dragBox.offsetTop;
document.onmousemove = e => {
//鼠标的位置减去鼠标相对元素的位置,得到元素的位置
let left = e.clientX - disX;
let top = e.clientY - disY;
//移动当前元素
dragBox.style.left = left + 'px';
dragBox.style.top = top + 'px';
};
document.onmouseup = e => {
//鼠标弹起来的时候不再移动
document.onmousemove = null;
//预防鼠标弹起来后还会循环(即预防鼠标放上去的时候还会移动)
document.onmouseup = null;
// 对外暴露元素相对于父级位置
elementObj.value.left = dragBox.style.left;
elementObj.value.top = dragBox.style.top;
};
};
}
});
2、loading
点击查看原文loading链接
1. 创建文件
1.在 src目录下创建一个directiveLoading文件夹,然后这个文件夹下创建1个loading文件夹和index.js文件,loading文件夹下分别创建Loading.vue和index.js文件。
directiveLoading的index.js文件用来暴露安装插件接口。如下所示
import Loading from './loading'
export default {
install(Vue) {
Vue.directive('myLoading', Loading)
}
2.loading.vue,用来插入到自定义指令的目标元素中,就是写静态页面可以自定义样式和一些炫酷的东东。
<template>
<div class="htmleaf-container">
<div class="loader">
<div class="my-loading"></div>
<div class="my-loading-text">加载中...</div>
</div>
</div>
</template>
<script>
export default {
name: "",
data() {
return {};
},
props: {},
components: {},
methods: {},
created() {},
mounted() {},
beforeDestroy() {},
};
</script>
<style lang ="scss" scoped>
/* .container-loading {
height: 100%;
width: 100%;
} */
.htmleaf-container {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
font-size: 12px;
min-height: 60px;
background-color: rgba(255, 255, 255, 0.8);
}
.loader {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 60px;
}
.loader .my-loading {
position: relative;
width: 100%;
height: 10px;
border: 1px solid #30b08f;
border-radius: 10px;
animation: turn 1s linear 0.42s infinite;
}
.loader .my-loading:before {
content: "";
display: block;
position: absolute;
width: 0%;
height: 100%;
background: #30b08f;
box-shadow: 10px 0px 15px 0px #30b08f;
animation: load .5s linear infinite;
}
.loader .my-loading-text {
width: 100%;
position: absolute;
top: 10px;
color: #30b08f;
text-align: center;
animation: bounce .5s linear infinite;
}
@keyframes load {
0% {
width: 0%;
}
87.5%,
100% {
width: 100%;
}
}
@keyframes turn {
0% {
transform: rotateY(0deg);
}
6.25%,
50% {
transform: rotateY(180deg);
}
56.25%,
100% {
transform: rotateY(360deg);
}
}
@keyframes bounce {
0%,
100% {
top: 10px;
}
12.5% {
top: 30px;
}
}
</style>
- 实现自定义指令
在loading文件夹的index.js写
import Vue from 'vue'
import Loading from './Loading.vue'
/**
* Vue.extend 接受参数并返回一个构造器,new 该构造器可以返回一个组件实例
* 当我们 new Mask() 的时候,把该组件实例挂载到一个 div 上
**/
const Mask = Vue.extend(Loading)
// 更新是否显示
const toggleLoading = (el, binding) => {
if (binding.value) {
Vue.nextTick(() => {
// 控制loading组件显示
el.instance.visible = true
el.style.position = 'relative'
// 插入到目标元素
insertDom(el, el)
})
} else {
el.instance.visible = false
el.style.position = 'static'
el.mask && el.mask.parentNode && el.mask.parentNode.removeChild(el.mask)
}
}
// 插入到目标元素
const insertDom = (parent, el) => {
parent.appendChild(el.mask)
}
export default {
// 第一次绑定到元素时调用
bind: function(el, binding, vnode) {
const mask = new Mask({
el: document.createElement('div'),
data() {}
})
// 用一个变量接住mask实例
el.instance = mask
el.mask = mask.$el
el.maskStyle = {}
binding.value && toggleLoading(el, binding)
},
// 所在组件的 VNode 更新时调用--比较更新前后的值
update: function(el, binding) {
if (binding.oldValue !== binding.value) {
toggleLoading(el, binding)
}
},
// 指令与元素解绑时调用
unbind: function(el, binding) {
el.instance && el.instance.$destroy()
}
}
3. 注册插件
在main.js里引入
import DirectiveLoading from './directiveLoading' //自定义loading
Vue.use( DirectiveLoading )
4. 插件使用
<div v-myLoading="dayLoading">
就是这么简单
</div>
16、vue中的name属性作用
1.递归组件运用(指组件自身调用自身组件)
<article>
<div class="item" v-for="(item,index) in list" :key="index">
<div class="item-title">
<span class="item-title-ticket"></span>
{{item.title}}</div>
<div v-if="item.children" class="item-children">
<detail-list :list="item.children"></detail-list> //递归调用
</div>
</div>
</article>
</template>
<script>
export default {
name: "DetailList", /*指组件自身调用自身组件*/
props: {
list: Array
},
data() {
return {};
}
};
</script>
2、配合keep-alive对组件缓存做限制(include/exclude=“name”)
keep-alive的 include和exclude 允许有条件的对组件进行缓存,其中include和exclude所匹配的就是组件的name值。
example:
// 组件 a
export default {
name: 'a',
data () {
return {}
}
}
<keep-alive include="a">
<component>
<!-- name 为 a 的组件将被缓存! -->
</component>
</keep-alive>
<keep-alive exclude="a">
<component>
<!-- 除了 name 为 a 的组件都将被缓存! -->
</component>
</keep-alive>
1、点击kee-alive缓存过程中遇到的坑查看详情
2、keep-alive使用详解
3、在dev-tools中使用
在开发中我们经常需要对代码进行调试,在dev-tools中组件是以组件name进行显示的,这样更有语义化,方便我们快速定位到我们需要审查的位置,结构更清晰明了。(如图):
17、防抖与节流
使用场景
防抖(多次触发 只执行最后一次)
登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖
调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖
文本编辑器实时保存,当无任何更改操作一秒后进行保存
节流(规定时间内 只触发一次)
鼠标连续不断地触发某事件(如点击),单位时间内只触发一次;
监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断。例如:懒加载;
浏览器播放事件,每个一秒计算一次进度信息等
1、防抖
防抖重在清零 clearTimeout(timer)
防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。代码实现重在清零 clearTimeout
function debounce (f, wait) {
let timer
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => {
f(...args)
}, wait)
}
}
使用setTimeout来解决,原理是,在未输入完毕之前不去请求接口,输入完毕之后再去请求,
input输入触发的input事件,所以在input事件中执行操作:
因为要等到输入完毕之后再去请求,所以可以设置在输入完毕500毫秒之后再去请求,接口请求就放在setTimeout中执行,所以,在输入时候,即每次触发input事件时,先清楚当前的定时器,等到不在输入,500毫秒之后再去请求数据。
// input事件
this.timer = false
async userNameserch(val) {
clearTimeout(this.timer);
this.timer = setTimeout(async () => {
console.log(1321)
}, 500);
},
2、节流
节流重在开关锁 timer=null
节流:控制流量,单位时间内事件只能触发一次,如果服务器端的限流即 Rate Limit。代码实现重在开锁关锁 timer=timeout; timer=null
function throttle (f, wait) {
let timer
return (...args) => {
if (timer) { return }
timer = setTimeout(() => {
f(...args)
timer = null
}, wait)
}
}
在规定的时间内函数只执行一次,就是节流。 对于按钮点击事件,快速频次的点击,在间隔相同时间只会请求一次数据,(这里根据业务需求具体衡量)。
因为是需要在间隔一段时间之后执行一次方法,所以首先需要setTimeout,而是否是在规定的时间内函数只执行一次,即是否再次执行,取决于一个变量的值,即可以用一个全局变量来实现。
// 按钮点击事件 valid为全局变量 默认为true
this.valid = true;
buttonClick() {
if(!this.valid) { // 首次点击不会return,
return;
}
// 首次点击之后全局变量置为false,同时向下执行,setTimeout推入待执行队列
this.valid = false;
this.timer = setTimeout(() => {
console.log('按钮点击');
this.valid = true; // 在定时器未执行前,valid为false,所以点击方法会直接return
},1000)
},
3.通过css实现防抖和节流
这样做的最大好处是,这部分禁用的逻辑是完全和业务逻辑是解耦的,可以在任意时候,任意场合下无缝接入
1.通过为元素加动画时间来实现
button{
animation: throttle 2s step-end forwards;
}
button:active{
animation: none;
}
@keyframes throttle {
from {
pointer-events: none;
}
to {
pointer-events: all;
}
}
2.通过监听过度效果(通过:active去触发transition变化,然后通过监听transition回调去动态设置按钮的禁用状态)
// 定义一个无关紧要的过渡属性,比如opacity
button{
opacity: .99;
transition: opacity 2s;
}
button:not(:disabled):active{
opacity: 1;
transition: 0s;
}
// 然后监听transition的起始回调
// 过渡开始
document.addEventListener('transitionstart', function(ev){
ev.target.disabled = true
})
// 过渡结束
document.addEventListener('transitionend', function(ev){
ev.target.disabled = false
})
19、表单的新增编辑和查看
<!-- 用于引入子组件 -->
<JNPF-Form v-if="formVisible" ref="JNPFForm" />
this.$refs.JNPFForm.init(id,isDetail) // init()是子组件函数
init(id, isDetail) {
this.dataForm.id = id || 0; // 如果有id就是编辑或者查看,没有id就是新增
this.visible = true; // 打开弹窗
this.isDetail = isDetail || false; // isDetail为true就是详情,没有isDetail就是false为编辑
this.$refs['elForm'].resetFields(); // 清空表单
// 如果有id就是编辑查看,这里的请求用于做数据回填
if (this.dataForm.id) {
this.dataForm = res.data; // 数据回填
}
},
}
ES6的浅拷贝 Object.assign清空表单
this.ruleForm = Object.assign({}, formData)
this.obj = { a: 1, b: 2 }
this.obj.c = 3
Object.assign(this.obj, { d: 4 })
this.$set(this.obj, 'e', 5)
// 下面这一行代码才触发了视图更新
this.obj = Object.assign({}, this.obj, {e: 5})
console.log("obj", this.obj)
20、this.$set的使用详解
this.$set(obj, key, value)
我们在项目开发的过程中,经常会遇到这种情况:为data中的某一个对象添加一个属性
在这里我们发现虽然这个对象身上已经有了该属性,但是视图层并没有更新该数据,是什么造成的呢?由于受JavaScript的限制,vue.js不能监听对象属性的添加和删除,因为在vue组件初始化的过程中,会调用getter和setter方法,所以该属性必须是存在在data中,视图层才会响应该数据的变化
那么,我们该如何解决这个问题呢
解决这个问题的方法大体有两种:
1.使用this.$set(obj, key, value)/vue.set(obj, key, value)
<script>
export default {
data() {
return {
student: {
name: '张三',
}
}
},
methods: {
setMessage() {
this.$set(this.student, 'age', 15)
console.log(this.student)
}
}
}
</script>
2.通过Object.assign(target, sources)方法
<script>
export default {
data() {
return {
student: {
name: '张三',
}
}
},
methods: {
setMessage() {
this.student.age = 15
this.student = Object.assign({}, this.student)
console.log(this.student)
}
}
}
</script>
我们发现,通过这两种方式为对象添加属性之后,他的对象身上多了get和set方法,所以,此时我们再次操作该属性的时候,就会引起视图的更新
29、小图标使用data uri
在线转换地址
目前,Data URI scheme支持的类型有:
data:,文本数据
data:text/plain,文本数据
data:text/html,HTML代码
data:text/html;base64,base64编码的HTML代码
data:text/css,CSS代码
data:text/css;base64,base64编码的CSS代码
data:text/javascript,Javascript代码
data:text/javascript;base64,base64编码的Javascript代码
data:image/gif;base64,base64编码的gif图片数据
data:image/png;base64,base64编码的png图片数据
data:image/jpeg;base64,base64编码的jpeg图片数据
data:image/x-icon;base64,base64编码的icon图片数据
30、class的动态使用
点击查看链接
1、单
<template>
<span :class="'description'">
This is how you add static classes in Vue.
</span>
</template>
2、混
<template>
<span
class="description"
:class="theme"
>
This is how you add static classes in Vue.
</span>
</template>
export default {
data() {
return {
theme: 'blue-theme',
};
}
};
----------------------------------------
.blue-theme {
color: navy;
background: white;
}
3、三目
<template>
<span
class="description"
:class="darkMode ? 'dark-theme' : 'light-theme'"
>
This is how you add dynamic classes in Vue.
</span>
</template>
4、数组
<template>
<span
class="description"
:class="[
fontTheme,
darkMode ? 'dark-theme' : 'light-theme',
]"
>
This is how you add dynamic classes in Vue.
</span>
</template>
4、对象
<template>
<span
class="description"
:class="{
'dark-theme': darkMode,
'light-theme': !darkMode,
]"
>
This is how you add dynamic classes in Vue.
</span>
</template>
5、动态绑定多个class且是多个条件判断
<template>
<div class="app" :class="[show===true ? 'border' : '' , background_red=== true ? 'red' : '']" >
</div>
</template>
31、img图片的动态使用
vue中img中的src直接写变量,图片不渲染。被解析成了String,如果使用的网络链接,可以直接写变量。
icon_url为后端返回的前端图片路径,图片放在前端的。
<img :src="require(`../assets${icon_url}`)">
方法三.采用背景图做法,通过data将图片源路径引入,利用内联样式。如下代码所示:
<div :style="imgStyle"></div>
data () {
imgStyle: {
background:`url(${require('@/assets/images/xxx.png')})`
}
}
32.nodemodules的删除
33.vue中使用element-resize-detector
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC
语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能