VUE2实战

frontend

# 一、JS常用语句

# 1.1 深拷贝

方式一:深拷贝基本对象

object.assign(xx,xxx) 可以深拷贝基本对象(对象中包裹数组的不行),object.assign()的方式是直接返回一个新对象。

const obj = Object.assign({}, userInfo);
1

方式二:JSON转换

用json.stringify()将数组转化成json格式,再用json.parse()转换成JavaScript对象

let a= []
this.obj=JSON.parse(JSON.stringify(a))
1
2

# 1. 2 对象合并

将2个对象进行合并为1个对象

const params = Object.assign(param, param2);
1

# 二、CSS样式

# 2.1 居中显示

文字居中

margin: auto;
text-align: center;
1
2

元素居中

.father{
	display: flex;
	justify-content: center;
	align-items: center;
	height: 400px;
}
1
2
3
4
5
6

div居中

.el-dialog {
    position: absolute;
    top: 50%;
    left: 50%;
    -webkit-transform: translate(-50%, -50%);
    transform: translate(-50%, -50%);
    margin: 0 auto;
}
1
2
3
4
5
6
7
8

# 2.2 边框

border: 1px solid #DCDFE6;
1

# 2.3 选中样式

background-color: #1890ff;
color: #fff;
border-color: #1890ff;
1
2
3

# 2.4 滚动条

overflow:auto;
1

# 2.5 鼠标样式

cursor: pointer;
1

# 2.6 动态添加style

<div :style="{height: contentHeight}">

<div :style="{height: cardHieght+'px',backgroundColor: '#bfcbd9',padding: '3px'}">
1
2
3

# 2.7 a标签

a {
  color: #1481f5;
  text-decoration: underline;
}

a:hover, a:focus {
  color: red;
}
1
2
3
4
5
6
7
8

# 2.8 回车符在vue中换行显示

<div style="white-space: pre-wrap;"></div>
1

# 三、SCSS样式

# 3.1 变量使用

定义变量文件variables.scss

/* **** 宽度 ****  */
$dialogTitleHeight: 210px;

:export {
  dialogTitleHeight: $dialogTitleHeight;
}
1
2
3
4
5
6

其他scss文件引用变量文件。

@import './variables.scss';

.content-container {
  width: 100%;
  overflow: auto;
  /* 列表高度计算公式为:屏幕高度-标题栏高度 */
  height: calc(100vh - #{$dialogTitleHeight});
}
1
2
3
4
5
6
7
8

在vue文件中引用scss文件。

<style lang="scss" scoped>
  @import "~@/styles/list.scss";
</style>
1
2
3

# 四、Vue2使用

# 4.1 router

# 路径传参

  • 路由导航方式传参

    1. 路由配置:

      {
        path: '/message',
        component: Layout,
        redirect: '/message/index',
        hidden: true,
        children: [{
          path: 'index/:type',
          name: 'Message',
          component: () => import('@/views/sys/notice/message'),
          meta: {
            title: '我的信息',
          },
        }]
      },
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14

      说明::type标识参数。

    2. 调用方法:

      this.$router.push('/message/index/' + this.type);
      
      1
    3. 地址栏显示效果:

      http://localhost:8088/message/index/2
      
      1
    4. 获取路径参数:

      this.$route.params.num
      
      1

# 4.2 axios

axios 埃可C偶斯

# 使用axios下载文件

  1. 前端axios的config配置responseType: 'blob' (关键);
  2. 服务端读取文件,以content-type: 'application/octet-stream'的格式返回前端(二进制流数据,如常见的文件下载));
  3. 前端接收到数据后,使用Blob来接收数据,并且创建a标签进行下载;

AJAX无法下载文件的原因

下载其实是浏览器的内置事件,浏览器的 GET请求(frame、a)、 POST请求(form)具有如下特点:

  • response会交由浏览器处理;
  • response内容可以为二进制文件、字符串等;

但是AJAX请求不一样:

  • response会交由 Javascript 处理;
  • response内容只能接收字符串才能继续处理;

因此,AJAX本身无法触发浏览器的下载功能。

# 4.3 components

# 子组件向父组件发送事件

this.$emit('closeDialog') // 向父组件发送关闭事件

// 携带参数调用
this.$emit('dateChange', this.dates)
1
2
3
4

# 4.4 props

type类型

  • String、Number、Boolean、Function、Object、Array

使用示例:

<script>
export default {
    props: {
      disabled: {
        type:Boolean,
        default: false
      },
      size: {
        type: String,
        default: 'small'
      },
      name: {
        type: String,
        required: true
      }
    }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 4.5 watch

  • 监听对象
<template>
  <div>
    <el-input v-model="filterText" placeholder="输入关键字进行查询" />
  </div>
</template>

<script>
  export default {
    data() {
      return {
        filterText: '',
        form: null
      }
    },
    props: ['btnType'],
	watch: {
      filterText(val) {
        console.log(val)
        // 操作val值
      },
      btnType() {
        // 调用method方法
        this.setBtnType()
      },
      "form.name": {
        handler(val) {
          if (val != null) {
            this.form.title = val
          }
        },
        immediate: true
      }
    },
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

# 4.6 computed

计算属性

<template>
  <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
    <use :xlink:href="iconName" />
  </svg>
</template>

<script>
export default {
  name: 'SvgIcon',
  props: {
    iconClass: {
      type: String,
      required: true
    },
    className: {
      type: String,
      default: ''
    }
  },
  computed: {
    isExternal() {
      return otherMethod(this.iconClass)
    },
    iconName() {
      return `#icon-${this.iconClass}`
    },
    svgClass() {
      if (this.className) {
        return 'svg-icon ' + this.className
      } else {
        return 'svg-icon'
      }
    },
    styleExternalIcon() {
      return {
        mask: `url(${this.iconClass}) no-repeat 50% 50%`,
        '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
      }
    }
  }
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

# 4.7 mixins

混入

  1. 创建混入文件,编写列表页面常用的js代码。
/* 工具 */
import * as common from '@/utils/base/common'

export default {
  data() {
    return {
      list: null,
      total: null,
      listLoading: true,
      btnDisabled: false, // 按钮禁用状态
    }
  },
  created() {
    this.initData()
  },
  methods: {
    /**
     * 后台获取列表数据
     */
    getList() {
      this.btnDisabled = true // 按钮禁用
      this.listLoading = true
      pageList(this.restPath, this.listQuery).then(response => {
        this.listLoading = false
        this.btnDisabled = false
        if (response.code === 200) {
          this.list = response.data.records
          this.total = response.data.total
        } else {
          common.tips(this, response.message || '调用接口失败!')
        }
      }).catch(err => {
        this.listLoading = false
        this.btnDisabled = false
      })
    },
      
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  1. 业务页面引入混入文件
<script>
  /* mixins */
  import pageListMixin from '@/mixins/pageList/pageListMixin.js'

  export default {
    mixins: [pageListMixin],
  }
</script>
1
2
3
4
5
6
7
8

在该页面中,即可直接引用混入文件定义的变量、方法等。

# 五、Vue2常用组件

# 5.1 input

# 5.2 datepicker

# 5.3 select

# 5.4 treeselect

# 六、自定义组件

# 七、Vue2插件

# 7.1 Vue接入天气插件

接入依赖包

package.json中加入依赖包

  "dependencies": {
    "vue-mini-weather": "^0.3.8",
  },
1
2
3

全局引入组件

main.js中引入天气插件

// 天气插件
import weather from 'vue-mini-weather'
Vue.use(weather)
1
2
3

标签使用

创建weather.vue使用天气

<template>
  <v-mini-weather>
    <template #default="{ weather, icon }">
      <!--插入图标-->
      <v-mini-weather-icon :icon="icon"></v-mini-weather-icon>
      <!--DIY内容-->
      <div class="address">{{ weather.cityname }} / {{ weather.weather }} / {{ weather.temp }}&nbsp;</div>
    </template>
  </v-mini-weather>
</template>

<script>
  export default {
    name: 'weather',
  }
</script>
<style scoped lang="less">
  .address {
    position: absolute;
    top: 30px;
    left: 90px;
    width: 200px;
    font-size: 18px;
    font-weight: 400;
    color: #A9D7FF;
    line-height: 28px;
  }
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 7.2Vue使用tianqi API

不需要依赖插件,直接通过iframe访问API,获取天气情况。

官网 (opens new window)

天气预报代码调用 (opens new window)

感谢您使用全球天气网(tianqi.com)天气预报调用插件,本插件完全免费,具有如下特色:

1、中国全部市县区及全球2500多个主要城市实时和5天预报天气; 2、自动识别访问者ip所在城市判断城市,也可定制默认的城市; 3、丰富的主题和灵活的样式定制:可以自定义字体颜色、背景图案、背景颜色、图标等内容。通过不同的款式、不同的图标、不同的背景,您可以组合出上千种调用代码!尽可能的方便使用! 4、适用面广:代码适用于常规网站、博客、社区论坛等的嵌入。

# 使用

<div class="weather">
	<iframe id="iFrame" width="100%" height="100%" scrolling="no" frameborder="0" allowtransparency="true"
		src="https://i.tianqi.com/index.php?c=code&id=59&icon=1&num=3&site=22"></iframe>
</div>
1
2
3
4

属性说明:

  1. id:标识不同的样式风格(从1到171);
  2. icon:标识图片风格;
  3. num:标识显示几天的天气;
  4. site:标识字体大小;

# 优化

调整大小

修改"width="和height="里面的数值,直到符合自己的需求为止,当然不要低于最小尺寸。

修改组件样式

在iframe里面加入style="border:solid 1px #7ec8ea" "#7ec8ea"是颜色代码,随意更改。

# 7.3 Vue接入WebSocket

接入依赖包

package.json中加入依赖包

  "dependencies": {
    "ws": "^8.13.0",
  },
1
2
3

使用

创建WebSocket连接,并保持心跳。

<script>
  // 列表数据
  export default {
    name: "listComponent",
    data() {
      return {
        firstRequest: true,
        wsUrl: process.env.VUE_APP_WS_URL + '/websocket/' + process.env.VUE_APP_CLIENT_ID,
        num: 0,
        websocket: {},
        // 心跳间隔时间(单位:毫秒)
        heartbeatInterval: 28 * 1000,
        // 心跳定时器
        heartbeatTimer: null,
        // 服务端心跳定时器
        serverHeartbeatTimer: null,
        //断开重连倒计时
        reconnectTimer: null,
        // 重连锁
        lockReconnect: false,
        // 是否需要重连
        isReconnect: true
      }
    },
    mounted() {
      this.initData()
    },
    beforeDestroy() {
      this.disconnect()
    },
    methods: {

      initData() {
        this.num = common.generateRandomNumber(999)
        this.getList()
        this.initWebSocket()
      },

      getList() {
      },

      // 连接WS
      initWebSocket() {
        // 实例化socket,这里的实例化直接赋值给this.websocket是为了后面可以在其它的函数中也能调用websocket方法,例如:this.websocket.close(); 完成通信后关闭WebSocket连接
        this.websocket = new WebSocket(this.wsUrl + '_' + this.num)

        // 监听是否连接成功
        this.websocket.onopen = () => {
          console.log('【open】WS连接状态:' + this.getWsStateName(this.websocket.readyState));
          // 连接成功则发送一个数据
          if (this.firstRequest) {
            this.websocket.send('WS连接成功');
          } else {
            this.websocket.send('WS重连成功');
          }
          // 开启心跳
          this.startHeartbeat();

          // 不是首次连接,才进行重新获取数据
          if (!this.firstRequest) {
            console.log('【open】WS重连,打卡区间内,重新获取列表数据!');
            this.websocket.send('WS重连,重新获取数据!');
            // 重新获取列表数据
            this.getList()
          }
        }

        // 监听服务端发回的信息并处理展示
        this.websocket.onmessage = (res) => {
          let obj = JSON.parse(res.data);
          switch (obj.type) {
            case 'heartbeat':
              //收到服务端心跳,重置客户端心跳
              this.resetHeartbeat();
              break;
            case 'tip':
              //收到服务端提示信息
              console.log('【message】WS监听服务端发来的消息,消息内容为:' + obj.data);
              break;
            default:
              this.processData(obj)
          }
        }

        // 监听连接关闭事件
        this.websocket.onclose = () => {
          //监听整个过程中websocket的状态
          console.log('【close】WS连接状态:' + this.getWsStateName(this.websocket.readyState));
          if (this.isReconnect) {
            console.log('【close】WS重新连接...');
            // 连接关闭后重新连接
            this.reconnectWebSocket();
          }
        }

        // 监听并处理error事件
        this.websocket.onerror = (error) => {
          console.error(error);
          console.error('【error】WS发生异常,重新连接...');
          // 重新连接
          this.reconnectWebSocket();
        }

      },

      // 重新连接WebSocket
      reconnectWebSocket() {
        this.firstRequest = false

        if (this.lockReconnect) return;
        this.lockReconnect = true;
        // 没连接上会一直重连,设置延迟避免请求过多
        this.reconnectTimer && clearTimeout(this.reconnectTimer);
        this.reconnectTimer = setTimeout(() => {
          // 新连接
          this.initWebSocket();
          this.lockReconnect = false;
        }, 5000);
      },

      // 重置心跳
      resetHeartbeat() {
        // 清除时间
        clearTimeout(this.heartbeatTimer);
        clearTimeout(this.serverHeartbeatTimer);
        // 重启心跳
        this.startHeartbeat();
      },

      // 开启心跳
      startHeartbeat() {
        this.heartbeatTimer && clearTimeout(this.heartbeatTimer);
        this.serverHeartbeatTimer && clearTimeout(this.serverHeartbeatTimer);
        this.heartbeatTimer = setTimeout(() => {
          // 这里发送一个心跳,后端收到后,返回一个心跳消息,
          if (this.websocket && this.websocket.readyState == 1) { // 如果连接正常
            // 发送心跳包
            this.websocket.send('heartbeat');
          } else { // 否则重连
            this.reconnectWebSocket();
          }
          this.serverHeartbeatTimer = setTimeout(() => {
            //超时关闭
            this.websocket.close();
          }, this.heartbeatInterval);

        }, this.heartbeatInterval)
      },

      disconnect() {
        console.log('WS断开连接!')
        this.isReconnect = false
        // 清除时间
        clearTimeout(this.heartbeatTimer);
        clearTimeout(this.serverHeartbeatTimer);
        this.websocket.close();
      },

      // 处理数据
      processData(obj) {
        if (obj.data == null || obj.data == 'null' || (typeof obj.data === 'undefined')) {
          console.log('【message】WS监听服务端发来的消息,消息为空,响应内容如下:');
          console.log(obj)
          this.cList = []
        } else if (obj.data == '[]' || obj.data == '{}') {
          this.cList = []
          console.log('【message】WS监听服务端发来的消息,消息为空数组,响应内容如下:');
          console.log(obj)
        } else {
          this.cList = obj.data
          console.log('【message】WS监听服务端发来的消息:数据覆盖!');
          // console.log('【message】WS监听服务端发来的消息,消息内容如下:');
          // console.log(this.cList)
        }
      },

      getWsStateName(state) {
        let result = ''
        if (state == 0) {
          result = 'WebSocket正在连接中'
        } else if (state == 1) {
          result = 'WebSocket已经连接并且通信是开放的'
        } else if (state == 2) {
          result = 'WebSocket正在关闭连接'
        } else if (state == 3) {
          result = 'WebSocket连接已经关闭或未能成功建立'
        }
        return result + '(' + state + ')'
      }
        
    }
  };
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193

# 7.4 Vue接入富文本编辑器

接入依赖包

package.json中加入依赖包

  "dependencies": {
    "wangeditor": "^3.1.1",
  },
1
2
3

创建自定义组件

创建Editor.vue,自定义富文本编辑器。

<template>
	<div class="editor-wrapper">
		<div :id="editorId"></div>
	</div>
</template>

<script>
	import Editor from 'wangeditor'
	import 'wangeditor/release/wangEditor.min.css'

	export default {
		/*  富文本编辑器 */
		name: 'Editor',
		props: {
			value: {
				type: String,
				default: ''
			},
			/**
			 * 设置change事件触发时间间隔
			 */
			changeInterval: {
				type: Number,
				default: 200
			},
			/**
			 * 是否开启本地存储
			 */
			cache: {
				type: Boolean,
				default: true
			}
		},
		computed: {
			editorId() {
				return `editor${this._uid}`
			}
		},
		mounted() {
			this.setEditor()
		},
		watch: {
			value() {
				this.setHtml(this.value)
			},
		},
		methods: {
			setEditor() {
				this.editor = new Editor(`#${this.editorId}`)

				this.editor.customConfig.onchangeTimeout = this.changeInterval

				// 编辑器的事件,每次改变会获取其html内容(html内容是带标签的)
				let this_ = this
				this.editor.customConfig.onchange = (html) => {
					// 将内容同步到父组件中
					let text = this_.editor.txt.text()
					if (this_.cache) localStorage.editorCache = html
					this_.$emit('input', 'html')
					this_.$emit('on-change', html, text)
				}

				// 创建富文本编辑器。create这个方法一定要在所有配置项之后调用
				this.editor.create()

				// 如果本地有存储加载本地存储内容
				let html = this.value || localStorage.editorCache
				this.setHtml(html)

			},

			setHtml(val) {
				this.editor.txt.html(val)
			},
		},
	}
</script>

<style scoped lang="less">
	.editor-wrapper * {
		z-index: 100 !important;
		font-size: 20px;
		color: #000000;
		text-align: left;
	}

	.w-e-menu {
		z-index: 2 !important;
	}

	.w-e-text-container {
		z-index: 1 !important;
	}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

使用

在所需页面引入自定义组件。

<template>
	<editor ref="editorRef" :value="myForm.content" @on-change="setContent" name="内容" :cache="false" />

</template>

<script>
	import Editor from '@/components/Editor/Editor.vue';

	export default {
		components: {
			Editor,
		},
		methods: {
			/* 富文本 start */
			setContent(val) {
				this.myForm.content = val
			},
			/* 富文本 end */
		}
	}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 7.5 Vue接入网址新手引导

前端的页面引导, 对于一些特定的页面, 我们会做一些新手指引, 使得用户快速了解平台的使用。

实现方案:

# Driver.js

Driver.js官网 (opens new window)

# intro.js

intro.js官方文档 (opens new window)

# 7.6 Vue实现背景水印

水印的核心是canvas,基于vue的架构在页面上增加水印,代码如下。

# 使用步骤

1、创建一个 watermark.js文件。

/* **************************************** */
/* ************  页面添加水印    ****************** */
/* *************************************** */

// str 水印文字
let setWatermark = (str) => {
  let id = 'watermark_box'

  if (document.getElementById(id) !== null) {
    document.body.removeChild(document.getElementById(id))
  }

  let can = document.createElement('canvas')
  // 设置canvas画布大小
  can.width = 300
  can.height = 200

  let cans = can.getContext('2d')
  cans.rotate(-20 * Math.PI / 180) // 水印旋转角度
  cans.font = '14px 微软雅黑'
  cans.fillStyle = '#000'
  cans.textAlign = 'center'
  cans.textBaseline = 'Middle'
  cans.fillText(str, can.width / 2, can.height, can.width) // 水印在画布的位置x,y轴

  let div = document.createElement('div')
  div.id = id
  //pointerEvents此CSS属性设置为“none”时,元素不接收悬停/单击事件,而事件将发生在其后面的任何内容上。
  div.style.pointerEvents = 'none'
  div.style.top = '50px'
  div.style.left = '50px'
  div.style.opacity = '0.1'
  div.style.position = 'fixed'
  div.style.zIndex = '100000'
  div.style.width = document.documentElement.clientWidth + 'px'
  div.style.height = document.documentElement.clientHeight + 'px'
  div.style.background = 'url(' + can.toDataURL('image/png') + ') left top repeat'
  document.body.appendChild(div)
  return id
}

// 添加水印方法
export const setWaterMark = (str) => {
  let id = setWatermark(str)
  if (document.getElementById(id) === null) {
    id = setWatermark(str1)
  }
}

// 移除水印方法
export const removeWatermark = () => {
  let id = 'watermark_box'
  if (document.getElementById(id) !== null) {
    document.body.removeChild(document.getElementById(id))
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56

2、在permission.js中调用,路由跳转后生成水印。

import {
  setWaterMark,
  removeWatermark
} from '@/utils/watermark.js'

router.afterEach((item) => {
  if (item.path === '/login') {
    // 登录页面无水印,清除水印
    removeWatermark()
  } else {
    // 添加水印
    setWaterMark('superC')
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 九、FAQ

# vue如何实现父子组件数据的双向绑定

在Vue中,父子组件之间的数据传递是通过props$emit来实现的。父组件可以通过props将数据传递给子组件,子组件可以通过$emit触发事件并将数据传递回父组件。这种方式可以实现父子组件之间的单向数据流

但是如果需要实现双向绑定,就需要使用Vue提供的v-model指令。

因为父组件中的props是只读的,不能直接修改。如果需要修改父组件中的数据,可以通过$emit来触发事件,将修改后的值传递给父组件。

实现方案

  1. 在父组件中定义一个数据属性,并将其通过props传递给子组件;

  2. 在子组件中,可以使用v-model指令将该属性绑定到一个局部变量上。

    这样,当子组件修改该局部变量时,父组件中的数据属性也会相应地更新。

示例

  1. 父组件:
<template>
  <div>
     <c-date-future :myDate="expireDate" @dateChange="expireDateChange" />   
  </div>
</template>

<script>
  export default {
    data() {
      return {
        expireDate: null
      }
    },
    methods: {
      expireDateChange(val) {
        this.expireDate = val
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  1. 子组件:
<template>
  <el-date-picker v-model="inputDate" :placeholder="'请选择日期"
    type="date" format="yyyy-MM-dd" value-format="yyyy-MM-dd"  />
</template>

<script>
  export default {
    name: 'myDateFuture',
    props: {
      myDate: {
        type: String,
        default: ''
      }
    },
    data() {
      return {
        // 日期值
        inputDate: this.myDate,
      }
    },
    watch: {
      inputDate(newVal) {
        // 向父组件发送事件
        this.$emit('dateChange', newVal)
      }
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

详细解读

  1. 父组件中的myDate属性通过props传递给子组件,并在子组件中使用v-model将其绑定到inputDate变量上。

  2. 当子组件中的input框发生变化时,watch监听到变化并通过$emit触发dateChange事件,将新的值传递给父组件。

    这样就实现了父子组件数据的双向绑定。