vue 生命周期
组件创建
- beforeCreate
- 访问不到 data props methods 数据
结果:
- created
- 可以访问到 data props methods 数据
- 一般用于调用ajax请求 把请求的数据转存到 data 中,供 template 模版渲染使用
- 组建模版结构尚未生成
结果:
- beforeMount
- 将要把内存中编译好的 HTML 结构渲染到浏览器 此时还没有 DOM 结构
结果:
- mounted
- 已经把内存中编译好的 HTML 结构成功渲染到浏览器 可以获取到 dom 数据
- 如果要操作当前组件的 dom 最早只能在 mounted 阶段执行
结果:
组件运行阶段
- beforeUpdate
- 将要根据变化过后、最新的数据,重新渲染组件的模版结构
- data数据是新的 dom还是以前的数据
- update
- 已经根据最新的数据,完成了组件 DOM 结构的重新渲染
- dom是最新的数据
组件销毁阶段
- beforeDestroy
- 将要销毁,此时尚未销毁,组件处于正常工作状态
- destroy
- 已经被销毁,对应的 DOM 结构完全移除
组件data为什么必须是函数
- 以对象形式
componentA和componentB的data都指向同一个对象,导致我们的组件数据相互污染。- eg
class Component {
constructor() {
this.data = {
name: '测试111'
};
}
}
const componentA = new Component();
const componentB = new Component();
console.log(componentA.data, componentB.data);
- 效果图
- 以函数形式
- 组件A和组件B维护两个独立的data互不干扰
- eg
class Components {}
const componentA = new Components();
const componentB = new Components();
componentA.data = function() {
return {
name: 'text1'
}
};
componentB.data = function() {
return {
name: 'text2'
}
};
console.log(componentA.data, componentB.data);
- 效果图
超出省略号...
flex: 1;
white-space: nowrap;
text-overflow: ellipsis;
- 效果图
element ui + sorttable 拖拽排序
ele任意时间范围打开组件默认选中
- default-value: defailtTime
<el-time-picker is-range :default-value=defailtTime v-model="value1" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间" placeholder="选择时间范围"> </el-time-picker>
data:
defailtTime: [STARTTIME, ENDTIME]
- img
form嵌套table校验
- 解决::prop="'tableList.' + scope.$index + '.time'" :rules="rules.time"
- prop只能在el-form-item上使用
<el-form-item class="center" :prop="'tableList.' + scope.$index + '.text'"
:rules="rules.text">
<el-input v-model="formData.text"><el-input>
</el-form-item>
vue表格重绘
- 解决::key="tableKey"
<el-table :key="tableKey">
</el-table>
data:
tableKey: false
init_data() {
this.tableKey = !this.tableKey
}
数组比较是否一样
- 解决:转JSON比较
- JSON.stringify(数组1 === 数组2)
vue页面样式修改eleUI样式不生效
- 解决:::v-deep
::v-deep.el-table {
color: red
}
css属性
- 行内元素添加高度宽度是无效的,块级元素才可以
不同场景显示不同tabs
- 应用场景:当el-tabs通过条件显示对应的tabs时
- 解决:通过计算属性判断
- 计算属性不可以用data里面的属性
- eg
<el-tabs v-model="active">
<el-tab-pane
:label="item.label"
:name="item.value"
v-for="item in tabs_list"
:key="item.value"
>
</el-tab-pane>
<component :is="active"></component>
</el-tabs>
import Text1 form ./text1.vue;
import Text2 form ./text2.vue;
...
// 组件
components: { Text1, Text2 ... },
data() {
active: 'Text',
tabsList: [
{
label:'测试1',
value: 'Text1'
}
]
},
computed: {
tabs_list() {
let _arr = [];
// 通过路由传递的值判断显示tabs
if (this.$route.query.text != '1') {
_arr = this.tabsList;
} else {
_arr = [
...this.tabsList,
{
label: '测试2',
value: 'Text2'
}
];
}
return _arr;
}
}
销毁子组件
- 应用场景:当前页面两个按钮需要调用同一个弹框子组件时
- eg:当点击新增按钮时必填验证会验证一下,再次点击另一个按钮时发现已经交验过了
- 问题:子组件没有销毁
- 解决:利用v-if销毁子组件达到目的
<child v-if="visible == true" :visible.sync="visible"></child>
重置表单数据
- 利用watch监听
- 组件在同一个页面调用多次时,表单有数据时清空
// 父组件
<el-button @click="text_value('add')">增加</el-button>
<el-button @click="text_value('deduction')">删除</el-button>
// 子组件
props: {
// 增加/删除 add/deduction
type: {
type: String,
default: ''
}
},
watch: {
// 当两个按钮切换时需要重置表单数据
'type': {
handler: function(val) {
if (val) {
this.formData = {
text: '',
text2: ''
};
}
}
}
},
data() {
return {
formData: {
text: '1',
text2: ''
}
}
}
下拉单选互斥禁用
- 需求:若选中其他选项,则系统不可选择(禁用)其他的选项还可以选
- 解决根据选中的值find查找禁用的值
<template>
<div>
<el-select v-model="value" @change="text_change" placeholder="请选择">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="item.disabled">
</el-option>
</el-select>
</div>
</template>
<script>
export default {
data() {
return {
options: [{
value: '1',
label: '系统',
disabled: false
}, {
value: '2',
label: '大师',
disabled: false
}, {
value: '3',
label: '宗师',
disabled: false
}, {
value: '4',
label: '王者',
disabled: false
}, {
value: '5',
label: '荣耀王者',
disabled: false
}],
value: '1'
};
},
methods: {
text_change(val) {
if (val != '1') {
this.options.find(item => item.value == '1').disabled = true;
}
}
}
};
</script>
- 效果图
下拉多选互斥
- 需求:若选择某个单独的类型,则不可选中全部
- 解决:根据value的值比较判断
<template>
<div>
<el-select v-model="value" multiple collapse-tags @change="text_sel" placeholder="请选择">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</div>
</template>
<script>
export default {
data() {
return {
options: [
{value: '', label: '全部'},
{value: '1', label: '测试1'},
{value: '2', label: '测试2'},
{value: '3', label: '测试3'},
{value: '4', label: '测试4'}
],
value: ['']
};
},
methods: {
text_sel(val) {
if (val.length > 1) {
// 根据value的值判断
if (val[val.length - 1] == '') {
this.value = [''];
} else {
this.value = val.filter(item => item != '');
}
}
}
}
};
</script>
- 效果
ele添加总计报高度错误
- table添加
:summary-method="get_summaries"show-summary - 错误:ResizeObserver loop limit exceeded
// 解决错误
beforeMount() {
const _windowheight = document.documentElement.clientHeight || document.body.clientHeight;
console.log('页面高度为:', _windowheight);
this.tableHeight = _windowheight;
},
ele时间日期组件(限制此刻和未来时间)
- picker-options限制月份
- 需要引入day.js 具体参考官方
// 当天和未来可选(没有精确到时分秒)
disabledDate(time) {
return time.getTime() < Date.now() - 1 * 24 * 3600 * 1000;
}
// 保存和生效按钮在外部传递index
@chang="(e) => date_change(e, scope.$index)"
date_change(val, index){
// 判断,不然直接修改会报错
if(val){
// 时间戳转换
const _todaystamp = new Date(Date.now()).getTime(); // 当前时间戳
const _valstamp = dayjs(val[0]).valueOf(); // 用户选择的时间戳
// 时间戳转时间
const today = dayjs(_todaystamp).format('YYYY-MM-DD HH:mm:ss');
if(_valstamp < _todaystamp) {
// 传值操作
} else {
// 传值操作
}
} else{
// 可以设置为空
}
}
联想查询(可支持手动输入和联想到的内容)
- 手动输入可以保存,联想到的可以选择也可以不选中
- 联想查询函数:通过接口联想查询
- 清除数据函数:将联想数据清空
- 监听值的函数:拿到值更新替换 (用于离开判断取值)
- 离开失去焦点函数:恢复输入值 (判断页面是否有数据)
- 解决清除属性
clearable搜索词点击搜索框还有以前的搜索记录 - eg
- 解决方法: 添加clear事件并把筛选项列表清空
- eg
相同属性的多个对象快速赋值法
Object.keys().forEach()- 接口获取为所有筛选项下拉列表添加全部属性
Object.keys(_rest.data.select_options).forEach((key) => {
_rest.data.select_options[key].unshift({ label: '全部', value: '' });
});
获取页面滚动位置
- 参考链接:segmentfault.com/a/119000001…
- eg
function debounce(fn, delay) {
let timer;
return function () {
const context = this;
const args = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
}, delay);
}
}
function showTop() {
var scrollTop = document.body.scrollTop || document.documentElement.scrollTop;
console.log('滚动条位置:' + scrollTop);
}
window.onscroll = debounce(showTop, 1000);
user-agent判断用户登陆是否为QQ还是微信
- 参考链接:cloud.tencent.com/developer/a… -eg
is_weixin_qq() {
let ua = navigator.userAgent.toLowerCase();
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
return 'weixin';
} else if (ua.match(/QQ/i) == 'qq') {
return "QQ";
}
return false;
}
this.$set用法
- 给对象添加属性值
- case1:点击后报错
(用case 2 $set来添加)- 原因:objText中没有age属性名
- case2:点击后页面显示 '更改前'
- case1:点击后报错
- eg
<template>
<div>
<p v-if="objText.isEdit">{{objText.name}}</p>
<el-button @click="addAgeHandler">添加isedit</el-button>
<el-button @click="modifyAgeHandler">更改name</el-button>
</div>
</template>
<script>
export default {
data() {
return {
objText: {
name: '更改前'
}
};
},
methods: {
// case 1 给对象添加属性值 报错
addAgeHandler() {
this.student.age = 18;
},
// case 2 给对象添加属性值
addAgeHandler() {
this.$set(this.objText, 'isEdit', true);
},
modifyAgeHandler() {
this.objText.name = '更改后';
}
}
};
</script>
- 效果
el-tabs and keepAlive
- 缓存动态/异步组件:cn.vuejs.org/v2/guide/co… eg:
<el-tabs v-model="active">
<el-tab-pane
:label="item.label"
:name="item.value"
v-for="item in list"
:key="item.value"
>
</el-tab-pane>
<component :is="active"></component>
</el-tabs>
import Text form ./text.vue;
...
// 组件
components: { Text, ... },
data() {
active: 'Text',
list: [
{
label:'测试',
value: 'Text'
}
]
}
在表单外保存数据
- 通过
findIndex来判断是否原数据保存还是新增数据的保存- findIndex:存在返回 -1 没有返回当前行下标 eg:
// 通过 id 来判断
const _index = this.tableList.findIndex( item => item.id === '' );
// 有id返回 -1 没有返回 当前行下标 来判断并特殊处理
v-model双向绑定
-
v-model既是双向绑定:当数据变化之后,视图同步更新,当视图变化之后,数据也会更新。
- 语法糖
-
v-model也是单向数据流:当数据变化之后,视图同步更新,当视图变化之后,数据不会更新。
- 简单来说:
数据向下,事件向上。
- 简单来说:
-
原理:传递一个
value字段、并监听input事件二者结合的语法糖 eg:
<input v-model="value"> === <input :value="value" @input="this.value = $event">
v-for尽量避免用index作为key
eg:
<el-radio v-for="(item, index) in headerList" :key="index" :label="item.value">{{ item.label }}</el-radio>
原因:
-
用 index 作为 key 时,在对数据进行,逆序添加,逆序删除等破坏顺序的操作时,会产生没必要的真实 DOM更新,从而导致效率低
-
用 index 作为 key 时,如果结构中包含输入类的 DOM,会产生错误的 DOM 更新
-
在开发中最好每条数据使用唯一标识固定的数据作为 key,比如后台返回的 ID,手机号,身份证号等唯一值**
-
如果不存在对数据逆序添加,逆序删除等破坏顺序的操作时,仅用于渲染展示用时,使用 index 作为 key 也是可以的(但是还是不建议使用,养成良好开发习惯)。
ref的作用
-
基本用法——获取DOM元素
-
进阶用法——获取子组件中的data 调用子组件中的方法
// 父组件 <home ref="home"/>
mounted(){
console.log(this.$refs.home) //即可拿到子组件的实例,就可以直接操作 data 和 methods
}
v-if和v-show区别
- v-if是控制标签是否渲染(是否存在该标签)
- v-show控制标签是否显示(display:none)
- 什么场景使用v-if,什么场景什么v-show
- 需要大量切换的时候使用v-show,不怎么切换的就使用v-if,
- 多条件使用时优化考虑v-if
- 实际项目过程中基本大部分是v-if,因为它有v-else-if可用于多条件,较灵活一些。
尽量避免使用v-for和v-if同时使用
this.$nextTick()使用
应用场景:父组件通过props传值给子组件,调用子组件第一次拿不到父传递的值时,第二次可以拿到
- 解决 $nextTick() 或者 子组件watch监听下值的变化
- eg:
<el-button @click="show_child">调用弹窗子组建</el-button>
// 传ID给子组件
<child ref="hb-child-dialog" :id="text.id"></child>
show_child() {
this.text.id = id;
this.$nextTick(() => {
// 通过ref调取子组件方法获取初始化数据
this.$refs['hb-child-dialog'].init_data();
})
}
Table表操作
- Table 时间排序参考element ui 组件库 添加
sortable后端排序传参数(custom) 用sort-change监听事件携带参数{column, prop, order} - Table 批量删除操作参考element ui 组件库 添加
type="selection"用selection-change监听事件携带参数 - Table
row-class-name属性中的某一行添加 class eg:给对应行添加颜色
table高度出现滚动条
- 超出table高度出现滚动条
- eg:
<el-table
:height="calc(100vh - 250px)"
>
</el-table>
- 效果:
vue axios -- FormData 上传图片
- eg:
const formdata = new FormData()
//formdata本身是个对象,参数名为file
formFile.append('action', 'upload');
formdata.append('file', file.file)
// 请求
- 要传的数据
- data:formData
- formdata打印出来为空的原因
- 覆盖默认的上传行为,可以自定义上传的实现
- eg:
<el-upload
action="#"
:http-request=" e => { function(e, scope.row, scope.$index ) } "
// e => {} 这样写是为了将当前数据带函数进去,让函数方法可以获取到
>
const formdata = new FormData()
//formdata本身是个对象,参数名为file
formFile.append('action', 'upload');
formdata.append('file', file.file)
console.log(formData) //{} 发现为空
console.log(formData.get('action')) // 'upload'
console.log(formData.get('file')) // 文件
- 效果
时间戳格式化时间
- time: 获取到的时间戳
- eg:
const time = 1579161320;
format_time(time){
const date = new Date(time * 1000 + 8 * 3600 * 1000);
return date.toJSON().substr(0, 19).replace('T', ' ');
}
- 效果
ele日期组件:处理时间和日期的js库
- 用法:遇到默认显示指定哪一天的时间和日期的时候
// 默认显示当前周
const STARTDATE = dayjs().startOf('week').add(1, 'day').format('YYYY-MM-DD 00:00');
const ENDDATE = dayjs().endOf('week').add(1, 'day').format('YYYY-MM-DD 23:59');
// 默认推前1个月
const TODAYD = dayjs().format('YYYY-MM-DD 23:59'); // 当天时间
const BEFORMOUTH = dayjs().subtract(1, 'month').format('YYYY-MM-DD 00:00'); // 推前1个月
表单优化
- template:有判断逻辑特殊处理的用,没有则可以省略
- key值:表头字段名称
- eg:
// 有逻辑特殊处理
<el-table>
<el-table-column>
// 具名插槽
<template slot="header">
<template v-if="['key值'].includes(key)">
// 表头操作
<template >
</template>
// 作用域插槽
<template slot-scope="scope">
<template v-if="key === 'key值'">
// 需要处理的代码 select input switch 等
</template>
<template v-else>
{{scope.row[key]}}
</template>
</template>
</el-table-column>
</el-table>
// 无逻辑
<el-table>
<el-table-column>
</el-table-column>
</el-table>
筛选项
- 用到分页的时候需添加
- 重新获取第一页:防止翻页后搜索不到数据的情况
- eg
// 筛选项
<el-input
placeholder="请输入关键词"
<!-- 重新获取第一页 -->
@keyup.enter.native="handleCurrentChange(1)"
clearable>
</el-input>
// 分页
<el-pagination
@current-change="handleCurrentChange"
layout="prev, pager, next, jumper"
:current-page.sync="pageData.page"
:page-size="pageData.page_size"
:total="pageData.total">
</el-pagination>
data() {
pageData: {
page: '', // 第几页
page_size: '', // 页数
total: '', // 条数
}
}
methods: {
handleCurrentChange(val) {
this.pageData.page = val || 1;
}
}
一级页面向二级页面传递数据
- 以传递id为例子
- 特殊处理下,防止传入空的id
- this.$route.query.id: 路由传递id
- eg
// 一级页面传递id
this.$router.push({
path: '', // 跳转的路径
query: {
id: '',
name: ''
} // 要传的对象
})
// 二级页面接受并判断处理
data() {
formData: {
id: '' // -级页面传递id
}
}
created() {
this.$route.query.id ? this.formData.id = this.$route.query.id :
this.$message.error('id不能为空');
// 初始化数据
this.initData();
}
methods: {
initData() {
// formData.id 为空 则不调取接口
if(!this.formData.id) return;
}
}
切换页面刷新问题 (返回)
- 解决:
添加缓存路由配置name属性 同时vue页面也需要配置name属性(属性名一致) - 实现方法:www.jianshu.com/p/9cefe3d27…
el-input超出3位小数限制输入
- 这里用到正则表达式,每输入一个数字会对输入框进行一次事件的触发,检查是否超过三位小数点,超过则进行删除。
- {}🀄️ 3改成2,这样就是保留两位小数点了
- eg
// 超出3位小数限制输入
<el-input
type="number"
onkeyup="value=value.replace(/^\D*(\d*(?:\.\d{0,3})?).*$/g, '$1')"
// 或者
onkeyup="this.value= this.value.match(/\d+(\.\d{0,3})?/) ? this.value.match(/\d+(\.\d{0,3})?/)[0] : ''"
// 输入纯数字
oninput="value=value.replace(/[^\d]/g,'')"
// 输入大于0的数字
oninput="value=value.replace(/\D|^0/g,'')"
>
</el-input>
根据计算保留3位小数
- eg
// 根据计算保留3位
const a = Math.floor(3.125 * 1000) / 1000;
es6语法
- 非空判断 (变量??'') !== '' 变量不能为:‘’ null undefind
(value??'') !== '' ? console.log('ok') : console.log('no');
- includes 多个值满足条件
['1','2','aaa','3'].includes(value) ? console.log('ok') : console.log('no');