个人笔记【Vue】
1、允许一个自定义组件在使用 v-model
简介:
允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event,但是一些输入类型比如单选框和复选框按钮可能想使用 value prop 来达到不同的目的。使用 model 选项可以回避这些情况产生的冲突。
// 封装的子组件
<template>
<div>
<a-input
v-model="searchData"
@change="handlerSearch"
></a-input>
</div>
</template>
<script>
export default {
name: 'myInput',
model:{ // model属性可以指定父组件通过哪个props和在何种事件(要和$emit()中书写的事件名同步)下进行数据收集
prop:'value', // value 可以默认使用为传出的东西,即组件中$emit()中的第二个值
event:'change'
},
props:{
test:{type:[String,Array,Number]}
},
data(){
return {
searchData:''
}
},
methods: {
handlerSearch(event){
this.$emit('change',event.target.value) // 该emit事件是决定父组件能否收集数据的关键
}
},
filter() {},
watch: {},
}
</script>
// 父组件使用v-model进行数据收集
<myInput v-model="searchData"></myInput>
2、v-if 和 v-for同时使用
简介:
v-if和v-for不能同时使用,但是有时是不可避免的那么就需要借助于 template 标签进行处理。
利用v-for便利template标签
在真实的标签上使用v-if,并且要写 :key 属性
<ul>
<template v-for="item in navList">
<li :key="item.key" v-if="item.path">{{xxx}}</li>
<li :key="item.key" v-else>
{{xxxx}}
</li>
</template>
</ul>
3、通过路由传参动态修改当前页面的title属性
// 通过$router.reslove()获取要跳转的href
let {href} = this.$router.resolve('/myTest/myTest1?name=wowo') // 可写对象形式并携带参数
window.open(href,'_black') // 跳转到目标的href并打开新的网页
// 目标路由组件中解析参数修改title
beforeMount(){
let {name} = this.$route.query
window.document.title = name
}
4、v-bind 和 v-bind:xxx(:xxx) 的区别
简介
v-bind 会将其一个或多个值绑定给当前标签【批量绑定】
v-bind:class 只将某个数据绑定给class属性【单个绑定】
:class 只将某个数据绑定给class属性【单个绑定】
常配合组件二次封装的$attrs使用
// v-bind
<h2><a v-bind="obj">v-bind</a></h2>
export default {
name: '',
props:{},
data(){
return {
obj:{
href:'www.baidu.com',
style:'color:red'
}
}
}
效果
5、v-on 和 v-on:xxx(@xxx)的区别
简介
v-on 会批量的将事件绑定给某个元素【批量绑定】
v-on:click 将单个的click事件绑定给某个元素
@click 将单个的click事件绑定给某个元素
常配合二次封装组件的$listeners属性使用
<a-input v-on="obj"></a-input>
export default {
name: '',
props:{},
data(){
return {
searchData:'',
obj:{
change(){
console.log('输入')
},
focus(){
console.log('聚焦')
}
}
}
}
效果:
6、后端返回的json数据格式并非json而是字符串的异常
简介
有时后端返回的数据data中的为含有转义符( / )的字符串。
原因:后端在从数据库中取数据时,只进行了序列化但忘记了反序列化直接返回给前端
处理:1、让后端进行反序列化 [JSON.parse]2、自己拿到后自己进行反序列化【JSON.parse()】
// 调用接口返回的数据为
{
code: 0
data: "[{\"id\":1,\"name\":\"shenzhen\"}]"
message: "列表数据"
}
7、element-ui表格分页记忆选中问题
简介
element-ui当设置可选择时,分页的情况下翻页后前一页的数据会被清除掉。造成无法记忆选中。
“element-ui”: "^2.0.11"及以上版本官方给了一个 Table-column Attributes 中的 reserve-selection属性用来解决这一问题
<el-table
:data="data"
@select="selectCurrent" // 该回调中可以接收到当前记忆选中的row数组
:row-key="getRowKeys" // :reserve-selection 必须配合该属性使用
>
<el-table-column
type="selection"
width="55"
:reserve-selection="true"
> </el-table-column>
</el-table>
expor default = {
data() {
return {
getRowKeys(row){
return row.id
},
data: [],
selectList:[],
};
},
}
// reserve-selection 必须配合row-key属性使用且row-key必须为唯一的值。
//如果不使用row-key属性,表格默认使用的是index作为当前页的row-key,翻页后再次选择会影响上一页选中的数据。
8、作用域插槽
简介
组用于插槽也是组件间通信的一种方式【父子之间】。
父组件可以向子组件进行结构传递
子组件可以向父组件进行数据传递
子组件中通过v-bind 或 :xxx 的方式给 slot标签绑定父组件需要的数据。并且会以对象形式向父组件传递,其中绑定的属性名为对象中的key,属性值为对象中的value。
父组件通过template标签加上slot-scope属性,可以收到子组件中传给父组件的数据
//========= 方法一 =======
// 子组件
<slot
:test="{a:10}"
></slot>
// 父组键接收数据方法
<Sub_2>
<template slot-scope="{test}"> // 回流的数据格式为 :{test:{a:10}}
{{test}}
</template>
</Sub_2>
// 简写形式
<Sub_2>
<template #default="data"> // 回流的数据格式为 :{test:{a:10}}
{{data}} // { "test": { "a": 10 } }
</template>
</Sub_2>
// ========== 方法二:命名插槽 ===========
// 子组件:
<slot name="test" :dataAll="{name:'xxxx'}"></slot>
// 父组键接收数据方法
<Sbu_2>
<template slot="test" sloat-scope="{dataAll}"> // 回流的数据格式为 :{ "dataAll": { "a": 10 } }
</template>
</Sbu_2>
// 简写形式
<Sbu_2 >
<template #test="data"> // 回流的数据格式为 :{ "dataAll": { "a": 10 } }
{{data}} // { "dataAll": { "a": 10 } }
</template>
</Sbu_2>
// ======= 方法三:模拟生成element-ui 的 el-table-column =====
// 子组件
<slot
v-for="(row,$index) in list"
v-bind="{row,$index}"
></slot>
// 插槽要想设置样式则需要以下方法
<div class="main">
<slot></slot>
</div>
// 父组件
<Sub_2
:list="data"
>
<template slot-scope="{row,$index}">
{{row}}
</template>
</Sub_2>
9、moment.js 常用日期获取
moment().format("YYYY-MM-DD HH:mm:ss"); //当前时间
// ====== 距离当前日期前N天【subtract()】 ================
moment().subtract(10, "days").format("YYYY-MM-DD"); //当前时间的前10天时间
moment().subtract(1, "years").format("YYYY-MM-DD"); //当前时间的前1年时间
moment().subtract(3, "months").format("YYYY-MM-DD"); //当前时间的前3个月时间
moment().subtract(1, "weeks").format("YYYY-MM-DD"); //当前时间的前一个星期时间
// 获取 当天 00:00:00 -- 23:59:59
// 前一周: 00:00:00 -- 23:59:59
// 前一月:00:00:00 -- 23:59:59
const radioTimeMap = {
today:[moment().startOf('day'),moment().endOf('day')],
week:[moment().subtract(1, "weeks").startOf('day'),moment().endOf('day')],
month:[moment().subtract(1, "months").startOf('day'),moment().endOf('day')]
}
// ======= 距离当前日期后N天【add()】 ==========
moment().add(10, "days").format("YYYY-MM-DD"); //当前时间的后10天时间
moment().add(1, "years").format("YYYY-MM-DD"); //当前时间的后1年时间
moment().add(3, "months").format("YYYY-MM-DD"); //当前时间的后3个月时间
moment().add(1, "weeks").format("YYYY-MM-DD"); //当前时间的后一个星期时间
// ========= 距离当前日期的上个月(根据实际的日历月份生成) ===========
const startDate = moment().month(moment().month() - 1).startOf('month').format("YYYY-MM-DD HH:mm:ss");
const endDate = moment().month(moment().month() - 1).endOf('month').format("YYYY-MM-DD HH:mm:ss")
// ========= 距离当前日期的上季度=========
startDate = moment().quarter(moment().quarter() - 1).startOf('quarter').format('YYYY-MM-DD');
endDate = moment().quarter(moment().quarter() - 1).endOf('quarter').format('YYYY-MM-DD');
// ======== 获取当前日期的一号 ===========
moment().startOf('month').format("YYYY-MM-DD") // 获取当月一号
moment().subtract(1, 'months').startOf('month').format('YYYY-MM-DD') // 获取上个月一号
moment().add(1, 'months').startOf('month').format('YYYY-MM-DD') // 获取下个月一号
10、props异步问题【父组件的props更该后子组件props不变】
简介:
项目中通过props传给子组件数据,第一次子组件可以收到props,当父组件的传入的props发生改变时子组件中props却没有获取到最新状态的数据。【props是响应式数据】
原因:
当第一次父组件mounted完成后,会将其父组件内的数据通过props传给子组件
当第一次父组件mounted后,父组件的props数据更新后,会传将最新的props数据传给子组件,但是传给子组件的props时,子组件的视图层已经渲染完毕,虽然此时的props里面数据会重新变化,但是视图层的数据还是mounted时父组件给他传的那些数据,不会响应式变化,所以导致出现了错误。
解决:
通过 watch监视或通过computed进行计算
11、在做查询获取表格数据并对表格数据进行CUDR时的注意事项
注意:
- 每次用户点击搜索或改变页大小【pageSize】时,就要重置页码为第一页
- 当只改变表格中的当前页【currentPage】改变时,不用重置页码
- 编辑数据时要注意地址引用问题编辑时不能改变原有的数据只有提交完成后才更改原有数据,一般都为浅拷贝,特殊的用到深拷贝要使用lodash进行深拷贝处理。
页码归一的解决方法:
思路:
在获取搜索结果的回调中,定义一个初始值为1的currentPage参数,需要修改页码时传入参数即可,不传则默认从1开始。
handlePaginationChange(pageIndex, pageSize) {
this.pagination.pageIndex = pageIndex;
this.pagination.pageSize = pageSize;
this.handleSearch(pageIndex);
},
// 获取搜索结果的回调
handleSearch(currentPage) {
// 重置页码
this.pagination.pageIndex = isNaN(currentPage) ? 1 : currentPage || 1;
this.getSearchResult();
},
12、父子组件中生命周期和Vue配置属性执行顺序
执行顺序
共有特点:
1、在beforeCreate钩子中,无法进行有关this的任何操作
2、watch使用的immediate属性,执行的顺序是在beforeCreate后,Create的最前面
不同特点:
嵌套的父子组件,先执行父组件生命周期到beforeMounted钩子后停止开始执行子组件的生命周期只到完成子组件的mounted钩子后最后执行父组件的mounted钩子。
13、Axios发送POST请求的三种请求头的区别
1、application/json 【默认】
简介:
application/json 这个 Content-Type 作为响应头大家肯定不陌生。现在越来越多的后端把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。
示例:
data(){
return {
params:{
id:10,
name:'xxx1',
password:'xxxxx',
}
}
},
mounted() {
this.upData()
},
methods: {
upData(){
this.$axios({
method:"POST",
url:'/list',
data:this.params // 默认为 application/json 格式
})
}
},
2、application/x-www-form-urlencoded
简介:
HTML原生表单发送POST请求默认的格式,其特点是按照 key1=val1&key2=val2 的方式进行编码,key 和 val 会进行了 URL 转码。大部分服务端语言都对这种方式有很好的支持。但是目前来说很少使用。
示例:
data(){
return {
params:{
id:10,
name:'哈哈哈',
password:'xxxxx',
}
}
},
mounted() {
this.upData()
},
methods: {
upData(){
this.$axios({
headers:{
"Content-Type":"application/x-www-form-urlencoded"
},
method:"POST",
url:'/list',
data:this.params
})
}
},
3、multipart/form-data【文件上传】
简介:
这也是一个常见的 POST 数据提交的方式。我们使用表单上传文件时常使用该请求头。使用该请求头需要配合 formData()构造函数使用。
示例:
data(){
return {
params:{
id:10,
name:'哈哈哈',
password:'xxxxx',
}
}
},
mounted() {
this.upData()
},
methods: {
upData(){
// 整理表单数据
let formData = new FormData()
Object.keys(this.params).forEach(key => {
formData.append(key,this.params[key])
})
this.$axios({
headers:{
'Content-Type': 'multipart/form-data',
},
// 进度条配置
method:"POST",
url:'/list',
data:formData
})
}
},
// 显示文件上传进度【要结合文件信息中的percent属性,且初始化为 0 】
onUploadProgress(progressEvent) {
file.percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
}
// formData 在控制台输出都为 formData{},要想输出看结果需要使用forEach进行便利输出
14、利用FormData实现简单的上传功能
<template>
<div>
<a-upload-dragger
name="file"
:multiple="true"
:beforeUpload="beforeUploadFile"
>
<p class="ant-upload-drag-icon">
<a-icon type="inbox" />
</p>
<p class="ant-upload-text">文件上传</p>
</a-upload-dragger>
<a-table
:columns="columns"
:data-source="data"
bordered
:rowKey="rowKey"
>
<template slot="percent" slot-scope="record">
<a-progress :percent="record" size="small" />
</template>
<template slot="status" slot-scope="record">
<a-tag v-if="record === 2" color="red">
上传失败
</a-tag>
<a-tag v-else-if="record === 1" color="green">
上传成功
</a-tag>
<a-tag v-else color="orange">
等待上传
</a-tag>
</template>
<template slot="actions" slot-scope="text, record,index">
<a href="javascript:;">删除</a>
</template>
</a-table>
<a-button
@click="upLoadFiles"
type="primary"
:disabled="isFileUpLoad"
>点击上传</a-button>
</div>
</template>
<script>
export default {
name: "",
components: {},
props: {},
data() {
return {
upLoadeFileList: [],
rowKey(record){
return record.uid
},
isFileUpLoad:false,
};
},
computed: {
columns() {
return [
{
title: "文件名称",
dataIndex: "fileName",
},
{
title:"上传进度",
dataIndex:"percent",
scopedSlots: { customRender: 'percent' }
},
{
title:"上传状态",
dataIndex:"status",
scopedSlots: { customRender: 'status' }
},
{
title:"删除",
scopedSlots: { customRender: 'actions' },
}
];
},
data(){
return (this.upLoadeFileList || []).map(item => {
return {
uid:item.uid,
fileName:item.fileName,
status:item.status,
percent:item.percent
}
})
}
},
mounted() {
},
filter() {},
watch: {},
methods: {
beforeUploadFile(file, fileList) {
console.log(file);
// 整理上传的文件信息
return new Promise((resolve, reject) => {
let fileName = file.name.split(".")[0] || "";
let fileSize = file.size / 1024 / 1024; // 默认为bit
// 设置验证规则验证文件名称和大小
let fileNameRule = /^([a-zA-Z0-9_~][a-zA-Z0-9-_.~]{0,254})$/;
let fileSizeRule = 2; // 2M
// 验证文件名是否合法
if (!fileNameRule.test(fileName)) {
this.$message.error("文件名不合法");
return reject(false);
}
// 验证文件大小是否合法
if (fileSize > fileSizeRule) {
this.$message.error("文件名过大请重新上传");
return reject(false);
}
// 将符合条件的文件整理属性并放入到上传队列中
this.upLoadeFileList.push({
uid: file.uid,
fileName,
fileSize,
message: "",
status: 0, // 用于保存上传文件状态
percent: 0, // 用于上传进度条
});
});
// 不让其直接上传而是放在上传表格中统一上传
return reject(false);
},
upLoadFiles(){
this.upLoadeFileList.forEach(file => {
this.fileUpLoad(file)
})
},
// 上传文件
fileUpLoad(file = {},){
let formData = new FormData()
let formParams = {
uid:file.uid,
fileName:file.fileName,
fileSize:file.fileSize
}
Object.keys(formParams).forEach(key => {
formData.append(key,formParams[key])
})
return this.$axios({
headers:{
"Content-Type":"multipart/form-data"
},
onUploadProgress(progressEvent) {
file.percent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
},
method:"POST",
url:'/api/formdatata',
data:formData
}).then(response => {
file.status = 1
file.message = '文件上传成功'
}).catch(error => {
file.status = 2
file.message = '文件上传失败'
})
}
},
};
</script>
<style lang="less" scoped>
</style>
效果:
15、ES6好用的新语法
链接:
运算符扩展
例如:
// 对象的链式调用 + 判断是否为undefined或null
const response = {code:0,data:{list:{a:null}}}
// ?.
// response中是否存在data属性,若存在判断是否存在list属性若存在判断是否存在a属性。只要链式调用中有一个为undefined终止对象的链式返回undefined。
// ??
// 判断 ?? 前的是否为 undefined 或 null 若是则返回 ?? 后面值,否则返回 ?? 前的值
const a = response?.data?.list?.a ?? 10
console.log(a) // 10
const response = {code:0,data:{list:{a:20}}}
const a = response?.data?.list?.a ?? 10
console.log(a) // 20
// 对象的结构
// 结构并重新命名
const {name:myName} = {name:'xxx'}
// 结构重新命名并附初始值
const {age:myAge = 10} = {name:'xxx'} // myAge : 10
// 批量结构
const {name,...params} = {name:'xxx',age:10,score:30} // params: {age:10,score:30}
16、IntersectionObserver API
作用:
可以实现对某个DOM对象的监视作用,常用于无线滚动加载、图片懒加载等技术。
链接:
var io = new IntersectionObserver((entries) => {
}, {...options}); // 当满足某些条件后会自动触发回调函数
// 开始观察
io.observe(document.getElementById('example'));
// 停止观察
io.unobserve(element);
// 关闭观察器
io.disconnect();
17、Vue中的mixin
作用:
将Vue项目中的公共部分抽离出来【data/computed/methods …】,在多个模块中使用,减少代码的冗余提高复用性。由于每个模块都是一个vue组件实例,所以在不同的模块下引入同一个mixin并不会产生引用影响关系。
特点:
同名的键值则会优先使用组件内键值,不同名则合并
mixin中的钩子会在组件的钩子前执行
使用:
定义一个mixin:
export const Mixin = {
data(){
return {
num:1
}
},
method:{
getUserInfo(){
}
}
........
}
组件中使用 mixin
import {Mixin} from '../Mixin'
export default {
mixins:['Mixin'] // 引入 mixin
}
18、对象转JSON格式展示
使用
JSON.stringify(value[, replacer [, space]])
参数一 对象 ,参数二要替换的字符,参数三缩进的空格数
异常处理
try{
JSON.stringify(value[, replacer [, space]])
}catch (error) {
console.log('格式转换错误')
}
19、Object.freeze() 方法可以冻结一个对象【性能优化】
简介:
方法用于冻结对象,禁止对于该对象的属性进行修改(由于数组本质也是对象,因此该方法可以对数组使用)。对于纯展示的大数据,都可以使用Object.freeze提升性能。
可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改
const和Object.freeze()并不同,const是防止变量重新分配,而Object.freeze()是使对象具有不可变性。
使用:
Object.freeze(targetObj)
20、iframe 框架渲染后端返回的HTML代码
简介:
后端返回的html代码时,页面可以使用 v-html 进行解析,但是会有可能有样式跟项目中的初始化样式重复造成影响。这时就需要利用 iframe 进行单独隔离渲染。【iframe是单独隔离的,外部无法获取iframe中的DOM】
使用:
<iframe srcdoc="data"></iframe> // html 代码渲染
<iframe src="../dome.html"></iframe> // html文件渲染
iframe 自适应宽高
this.$nextTick(() => {
// iframe 高度自适应
const iframes = document.getElementsByTagName("iframe");
Array.prototype.forEach.call(iframes, (iframe) => {
iframe.onload = () => {
const iframeWin = iframe.contentWindow;
if (iframeWin.document.readyState == "complete") {
if (iframeWin.document.body) {
iframe.height = iframeWin.document.documentElement.scrollHeight || iframeWin.document.body.scrollHeight;
iframe.width = iframeWin.document.documentElement.scrollWidth || iframeWin.document.body.scrollWidth
// 为其加上特殊的css样式
iframe.style.cssText = 'margin: -8px 0px 0px -9px;body:background-color:none;'
}
}
};
});
});
21、vue中页面切换的方法
1、路由切换
2、component 组件 ➕ is 属性
3、v-show 【组件不会重新挂载】
4、v-if
在antd 中的 tag 可以使用 期 v-if 或 component 内部组件 ➕ tag 上的 @click 方法 实现 tag 中内容切换
<component :is="comp"></component>
import UpAvatar from "@/components/page/home/upAvatar.vue"
import UpFile from "@/components/page/home/upFile.vue"
const compMap = {
'avatar':UpAvatar,
'file':UpFile
}
export default {
computed(){
comp(){
return compMap[this.currentTag]
},
}
}
22、antd 中 表格单元格二次渲染方法
1、利用 sloat 方法
colums:[
{
title:'操作',
scopedSlots: { customRender: 'action' },
}
]
2、利用 customRender属性渲染
columns:[
{
title:'操作',
customRender: (text, row, index) => {
return <div>
<a-popconfirm
title={ `重定义为【${row.code_type === 1 ? '失败' : '成功'}】返回码?` }
{ ...{on: { confirm: this.redefine(text, row, index) } } }
// this.redefine()方法返回值也是一个函数,是将返回的函数作为时间函数给绑定的
>
<a-button size="small" type="link" icon="edit">重定义</a-button>
</a-popconfirm>
</div>;
},
}
]
methods:{
redefine(){
return (text,row,index) {
}
}
}
23、antd Form 布局常用手段
1、 一行一列 与 一行N 列 混合布局
form使用 inline 内部使用 a-row + a-col 实现 一行 N 列控制。若 label 不能对其则利用选择器修改label样式
24、H5中的drop事件
被拖动的元素事件
事件 | 描述 |
---|---|
ondragstart | 用户开始拖动元素时触发 |
ondrag | 元素正在拖动时触发 |
ondragend | 用户完成元素拖动后触发 |
容器相关的事件
事件 | 描述 |
---|---|
ondragenter | 当被鼠标拖动的对象进入其容器范围内时触发此事件 |
ondragover | 当被拖动的对象在另一对象容器范围内拖动时触发此事件 |
ondragleave | 当被鼠标拖动的对象离开其容器范围内时触发此事件 |
ondrop | 在一个拖动过程中,释放鼠标键时触发此事件 |
实现
- 将要拖动的元素开启可拖拽属性【draggable=“true”】
- 容器元素绑定期ondragover事件,并且禁用其默认行为【元素默认是无法将一个元素放置到另一个元素中的】
- 容器元素绑定ondrop事件,在其移入后将被移动的元素插入到容器中
<body>
<div id="item" draggable="true" style="width: 100px; height: 100px; background-color: rebeccapurple;"></div>
<div id="wrap" style="width: 200px; height: 200px; background-color: red;"></div>
<script>
const wrapNode = document.querySelector('#wrap')
wrapNode.addEventListener("dragover",(e) => {
e.preventDefault()
})
wrapNode.addEventListener("drop",(e) => {
this.appendChild(document.querySelector('#item'));
})
</script>
</body>
25、arguments 和 rest 区别
- arguments 为 ES5 函数中的变量,普通函数只能在其函数内部进行操作传入的参数。在箭头函数中没有arguments且arguments返回值是一个伪数组。
- rest 为 ES6 新定义的用来接收传入参数的,返回值是一个真数组。且在普通函数和箭头函数中都可使用,并且作为函数参数时只能放在最后【…rest】
26、元素隐藏的方法和将隐藏元素绑定事件是否生效
visibility: hidden; [绑定事件不生效,开始该属性的元素是不可选中的]
display:node ; [开启后页面不存在该元素但是节点存在。页面不存在所以不能被选中]
opacity: 0; [元素隐藏但是页面真是存在,所以可以点击]
overflow: hidden; [超出部分相当于被裁掉了,页面不存在所以事件不生效]
27、兄弟组件生命周期
28、 provide 和 inject 【propsPlus版】
props 的增强版,可以实现穿透性的传值。当provide传入的是非函数数据则后代任意组件都可获取当前书写了provide的属性值。但传入的是函数数据则可以父元素可以接受到子元素传来的数据。
语法:
provide: 对象形式 、 函数形式【返回的必须是一个对象】
inject: 属性字符串数组、对象【属性名为本组件自定义名称,值为provide中提供的属性名。或者使用from + 默认值】
// =============================== 父组件向所有的组件传值
// 父组件
provide:{
name:'小明'
}
provide(){
return {
name:'小明'
}
}
// 子组件 :
inject:['name']
inject:{
subName:'name', // 将provide提供的name赋值给subName
value:{
from:'name',
default:'默认值'
}
}
29、相同路由参数不同或没有参数如何实现组件重新挂载
需求
每次再点击一次首页页面要刷新重新渲染,这个刷新不是。
为什么不会刷新
vue-router 是通过检测当前路由路径(不看params参数或query参数)进行是否重新挂载组件的操作,当相同地路由地址时组件是复用关系不会重新挂载组件
推荐方法【有无参数都可解决】
利用Vue的diff算法根据key值不同重新渲染DOM的特点
保证必须有参数且参数唯一
<template>
<div id="app">
<router-view :key="$route.path + $route.query.key"></router-view>
</div>
</template>
- 利用v-if + provide和inject 去控制router-view标签
// ==============================视图组件 =====================
<template>
<div id="app">
<router-view v-if="refresh"></router-view>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
refresh:true
}
},
provide(){
return {
reload:this.reload
}
},
methods:{
reload(){
this.refresh = false
this.$nextTick(() => { // $nextTick是利用promise对象实现的属于微任务,让其数据卡一波
this.refresh = true
})
}
}
</script>
// 上面可以用 定时器 来代替,因为它是宏任务可以实现数据卡一波
reload(){
if(this.timer){
clearInterval(this.timer)
}
this.refresh = false
this.timer = setTimeout(() => { // 利用宏伟任务实现数据卡一波,让其实现效果
this.refresh = true
})
}
}
// ===============================要刷新的组件==========================
<template>
<div>
<button @click="handlerRouter">跳转当前路由</button>
</div>
</template>
<script>
export default {
name: '',
inject:['reload'],
components:{},
props:{},
data(){
return {
flag:true,
}
},
mounted() {
console.log('home')
},
methods: {
handlerRouter(){
this.reload()
},
},
}
</script>
- 利用给路由添加时间戳query参数,通过监视器query参数实现以上思路的操作
<template>
<div id="app">
<router-view v-if="refresh"></router-view>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
refresh:true
}
},
watch:{
'$route.query':{
deep:true,
handler(){
this.refresh = false
this.$nextTick(() => {
this.refresh = true
})
}
}
},
}
有参数实现刷新
- 监视$route 参数 + v-if 实现
30. vue中使用JSX语法【render方法的使用】
- 创建一个js文件
============== 原生写法 ==================
export default {
data(){
return {
userName:'小明'
}
},
methods:{
click(){
this.$message.success('success')
}
},
render() {
// v-model : 只能使用 v-bind:value + v-on:input 【原始写法】
// 所有的语法糖都不可以用
// v-if / v-else / v-for 都只能用js方法代替
return (
<div>
<h3>用户名称:{this.userName}</h3>
{/* input 在 Input 值改变时触发 (value: string | number) [el-input] */}
<el-input value={this.userName} oninput={(e) => this.userName = e}></el-input>
{/* 原生事件封装则收到的是event */}
<input value={this.userName} oninput={(e) => this.userName = e.target.value}></input>
<el-button onclick={this.click}>点击事件</el-button>
{/* 利用三元表达式代替v-if v-else */}
{this.userName === 'xx' ? <el-tag>显示</el-tag> : ''}
{/* 利用map代替 v-for */}
{
[1,2,1,1].map((item,index) => {
return (<el-tag key={index}>{item}</el-tag>)
})
}
</div>
)
},
}
利用插件实现jsx中使用v-model【升级vue/cli 4 默认自带】
npm install @vue/babel-plugin-jsx -D
babel.config.js
“plugins”: [“@vue/babel-plugin-jsx”]
Vue中数据保存的方式
- 组件自身,Vue原型
- cookie、Storage
- VueRouter
- Vuex
31、Vue.extend()
// store.js
import Vue from 'vue'
export default Vue.extend({
data(){
return {
userName:'小明'
}
},
methods:{
changeUserName(name){
this.userName = name
}
}
})
// 实现一个公共的存储仓库
import store from "../store";
export default new store()
// A组件可以使用之前定义的方法操作state使用:
import Store from './utils.js'
export default {
data(){
return {
store:Store
}
}
}
32 Vue关闭ESLint
lintOnSave: false,
33、将前天组件插入到body中
document.body.appendChild(this.$el)
34、 判断当前鼠标点击是否在当前的盒子范围内
document.querySelector('.wrap').contains(e.target)
35、实现点击复制
let oInput = document.createElement('input')
oInput.value = text
document.body.appendChild(oInput)
oInput.select()
document.execCommand('Copy')
this.$message({
message: '复制成功',
type: 'success'
})
oInput.remove()