element-ui 笔记
快速搜索:
选择框的映射写法 >>> flag_1
表单重置 >>> flag_2
分页 >>> flag_3
时间框的解耦绑定 >>> flag_4
多标签页的切换与多接口调用 >>> flag_5
更新表单 >>> flag_6
新增表单 >>> flag_7
模板
Form 表单
<!-- 概念
ref 绑定控件, 可通过$ref用于重置等操作;
:model 表单数据对象, 配合 el-form-item 的prop使用(:rule表单验证规则, 同理)
:inline 如果不设置就会每一个item为一行
-->
<el-form ref="queryDto" :model="queryDto" :inline="true" label-width="80px">
<!-- 概念
prop 绑定字段名, 从而实现表单校验, 即会验证 el-input 元素绑定的 queryDto.field 是否符合验证
-->
<el-form-item prop="inputField">
<!-- 概念
v-model 双向绑定; placeholder 占位提示
-->
<el-input v-model="queryDto.inputField" placeholder="字段1" size="mini" clearable />
</el-form-item>
<!-- 选择框: flag_1
同时展示与字段处于映射关系写法
-->
<el-form-item prop="selectField">
<el-select v-model="queryDto.selectField" placeholder="字段2" size="mini" clearable>
<el-option v-for="item in queryDic._selectField" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 时间框: flag_4
1. 绑定模型 v-model 因为生成的时间是数组形式, 为与dto解耦因此另外单独使用实体绑定, 因此后续重置表单要单独重置
2. 粘贴快速选择项 :picker-options 注意这是模型data
3. 全局搜索查询剩余要配置的部分
-->
<div class="block">
<span>时间范围样式</span>
<el-date-picker
v-model="timeRange.timeField"
type="daterange"
size="mini"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions"
/>
<el-button size="mini" type="primary" value="search" @click="handleQuery()">查询</el-button>
<!-- 重置: flag_2
1. 绑定表单 resetForm('xxx')
2. 粘贴方法 resetForm
3. 注意: 如果有模型不在表单内需要额外清空(如本案例中的timeRange)
-->
<el-button size="mini" type="reset" @click="resetForm('queryDto')">重置</el-button>
<el-button size="mini" type="primary" @click="handleInsert()">新增</el-button>
</el-form>
<!--
分页: flag_3
1. 绑定当前页 :current-page
2. 设置分页选项: :page-sizes
3. 绑定页总数: :total
4. 粘贴方法: @size-change, @current-change
-->
<div class="page">
<el-pagination
:current-page="queryDto.page"
:page-sizes="[15, 25, 40, 50]"
:page-size="100"
layout="total, sizes, prev, pager, next, jumper"
:total="respData.pager.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
<script>
data(){
return {
queryDto: {
inputField: '',
// flag_1
selectField: '',
// 后端请求参数(具体根据接口文档需要) flag_4
timeFieldStart: '',
timeFieldEnd: '',
// flag_3
page: 1,
size: 15
},
// 通过映射即可实现: 下拉框所展示与传给后端的值形成对应
queryDic: {
// flag_1
_selectField: [
{ id: 0, value: 0, label: '映射1' },
{ id: 1, value: 1, label: '映射2' },
{ id: 2, value: 2, label: '映射3' }
]
},
// 时间范围字段(与dto进行解耦) flag_4
timeRange: {
timeField: ''
},
// 此处不关心返回数据的格式, 仅举例获取total
respData: {
list: [],
// flag_3
pager: {
total: 0 // 注意此处不能用 ''
}
},
// 快速选择时间范围(注意此处是data()内不是methods) flag_4
pickerOptions: {
shortcuts: [{
text: '最近一周',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)
picker.$emit('pick', [start, end])
}
}, {
text: '最近一个月',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
picker.$emit('pick', [start, end])
}
}, {
text: '最近三个月',
onClick(picker) {
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)
picker.$emit('pick', [start, end])
}
}]
}
}
},
methods: {
handleQuery() {
// 查询方法(此处后端因为定义的时间范围是start和end, 因此查询前要做封装) flag_4
if(this.timeRange.timeField) {
// 封装时间dto(方法见下面)
const _timeRange = this.handleDateRange(this.timeRange.timeField)
this.queryDto.timeFieldStart = _timeRange[0]
this.queryDto.timeFieldEnd = _timeRange[1]
// 开始查询
}
},
// 处理日期格式(具体日期格式转换见 附:date-format.js) flag_4
handleDateRange(dateRange) {
if (dateRange) {
return [formatDate(dateRange[0]), formatDate(dateRange[1])]
} else {
return ['', '']
}
},
// 重置表单 flag_2
resetForm(formName) {
this.$refs[formName].resetFields()
// 此处要把时间范围的字段单独重置 flag_4
this.timeRange.timeField = ''
},
// 分页size/page变更 flag_3
handleSizeChange(val) {
this.queryDto.size = val
this.handleQuery() // size变更后重新查询
},
handleCurrentChange(val) {
this.queryDto.page = val
this.handleQuery() // page变更后重新查询
}
}
</script>
<style>
/* 附: 分页栏底部居中样式 */
.page {
/*设置为Flexbox容器*/
display: flex;
/*垂直居中*/
align-items: center;
/*水平居中*/
justify-content: center;
position: absolute;
width: 100%;
bottom: 20px;
}
</style>
附: 时间格式化工具类 date-format.js
// 时间格式化工具类
export function formatDate(value, pattern) {
const dt = new Date(value)
if (pattern === 'yyyy-M-d') { // yyyy-M-d
const year = dt.getFullYear()
const month = dt.getMonth() + 1
const date = dt.getDate()
return `${year}-${month}-${date}`
} else if (pattern === 'yyyy-M-d H:m:s') { // yyyy-M-d H:m:s
const year = dt.getFullYear()
const month = dt.getMonth() + 1
const date = dt.getDate()
const hour = dt.getHours()
const minute = dt.getMinutes()
const second = dt.getSeconds()
return `${year}-${month}-${date} ${hour}:${minute}:${second}`
} else if (pattern === 'yyyy-MM-dd') { // yyyy-MM-dd
const year = dt.getFullYear()
const month = (dt.getMonth() + 1).toString().padStart(2, '0')
const date = dt.getDate().toString().padStart(2, '0')
return `${year}-${month}-${date}`
} else { // yyyy-MM-dd HH:mm:ss
const year = dt.getFullYear()
const month = (dt.getMonth() + 1).toString().padStart(2, '0')
const date = dt.getDate().toString().padStart(2, '0')
const hour = dt.getHours().toString().padStart(2, '0')
const minute = dt.getMinutes().toString().padStart(2, '0')
const second = dt.getSeconds().toString().padStart(2, '0')
return `${year}-${month}-${date} ${hour}:${minute}:${second}`
}
}
Table 表格/Tag 标签
<!-- 标签页(可选)
说明: 多标签可能会存在多个接口对应多个表单的情况, 因此handleSearch(查询)时可以根据表单名进行路由接口 flag_5
handleClick 切换标签方法
el-tab-pane 中 form1 表示当前表单唯一标识(重要)
-->
<el-tabs v-model="activeName" type="border-card" @tab-click="handleClick">
<el-tab-pane label="表单一" name="form1">
<div class="searchTable">
<span>前置说明</span>
<div class="remind">
<span>说明文字</span>
</div>
<el-divider />
<div class="cTable">
<el-table
v-loading="loading.xxx"
element-loading-text="玩命加载中🤣🤣🤣"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(196, 196, 196, 0.8)"
:data="respData.xxxInfo"
border style="width: 100%"
:header-cell-style="{ background: '#f8f8f8', color: '#000' }"
>
<!-- 编号字段 -->
<el-table-column label="编号" type="index" width="auto" />
<!-- 普通字段 -->
<el-table-column prop="field1" label="字段一" width="100" />
<!-- 映射字段 -->
<el-table-column prop="mappingField" label="映射字段" width="120">
<template v-slot="item">
<template v-if="item.row.mappingField === 1">
<div>类型1</div>
</template>
<template v-if="item.row.mappingField === 2">
<div>类型2</div>
</template>
<template v-if="item.row.mappingField === 3">
<div>类型3</div>
</template>
</template>
</el-table-column>
<!-- 右侧 删除/修改(详细见动态表单) 按钮 -->
<el-table-column fixed="right" label="操作" width="100px">
<template v-slot="item">
<el-button type="text" size="small" @click="handleUpdate(item.row)">修改</el-button>
<el-button type="text" size="small" @click="handleDelete(item.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</el-tab-pane>
</el-tabs>
<script>
data() {
return {
formType: 'form1' // 当前表单类型 flag_5
respData: {
xxxInfo: []
}
}
},
methods: {
handlerSearch() {
// 根据当前标签类型调取不同接口 flag_5
if (this.formType === 'form1') {
// 进行form1的接口请求
}
},
// 切换标签 flag_5
handleClick(tab, event) {
this.formType = tab.name
},
// 执行删除
async handleDelete(id) {
try {
await this.$confirm('此操作将删除该数据, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
await deleteById(id)
await this.all()
this.$message({
type: 'success',
message: '删除成功'
})
} catch (e) {
this.$message({
type: 'info',
message: '已取消删除'
})
}
},
// 封装弹框方法
alertMsg(msg) {
Message({
message: msg,
type: 'warning',
duration: 5 * 1000
})
},
}
</script>
<style>
/* 附: 表格样式 */
.searchTable {
width: 90%;
margin: 30px auto;
}
/* 附: 说明狂 */
.remind {
width: 100%;
margin-top: 10px;
box-sizing: border-box;
border: 1px solid #d9d9d9;
padding: 20px 40px;
}
/* 附: 此处与前置说明框保持间距 */
.cTable {
margin-top: 20px;
}
</style>
el-dialog 对话框
<el-table>
<el-table-column fixed="right" label="操作" width="100px">
<template v-slot="item">
<!-- 打开更新表单面板 flag_6 -->
<el-button type="text" size="small" @click="handleUpdate(item.row)">修改</el-button>
<el-button type="text" size="small" @click="handleDelete(item.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 更新表单: flag_6
:visible.sync 绑定是否显示对象
:model 绑定更新data对象
confirmUpdate() 确认
label-width 表单标签文字宽度
label-position 表单控制文字位置, 但要先设置文字宽度
注意: 此处 el-form-item 标签中没加prop属性, 因为不需要校验操作, 具体qi
-->
<el-dialog width="25%" :visible.sync="updateDialogVisible">
<el-form ref="updateForm" :model="updateForm" label-width="80px" label-position="left">
<el-form-item label="id">
<el-input v-model="updateForm.id" size="mini" :readonly="true" style="width: auto" />
</el-form-item>
<el-form-item>
<el-button type="primary" size="mini" style="width: auto" @click="confirmUpdate()">确定</el-button>
<el-button type="primary" size="mini" style="width: auto" @click="cancelUpdate()">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
<!-- 新增表单: flag_7
:visible.sync 绑定是否显示对象
confirmInsert() 新增
cancelInsert() 取消方法
-->
<el-dialog width="25%" :visible.sync="insertDialogVisible">
<el-form ref="insertForm" :model="insertForm" label-width="80px">
<el-form-item>
<el-button type="primary" size="mini" style="width: auto" @click="confirmInsert()">确定</el-button>
<el-button type="primary" size="mini" style="width: auto" @click="cancelInsert()">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
<script>
data() {
return {
// 更新相关模型 flag_6
updateDialogVisible: false,
updateForm: {
id: 0 // 更新唯一键: 通常根据接口从行中所取
},
// 新增相关模型 flag_7
insertDialogVisible: false,
insertForm: {
// 表单内容
}
}
},
methods: {
// 打开更新表单面板(回显) flag_6
handleUpdate(row) {
this.updateDialogVisible = true
// 解构对象(此处是因为还有别的数据封装)
const curUser = { ...row }
curUser.roles = curUser.roles || []
this.updateForm = curUser
},
// 取消更新
cancelUpdate() {
this.updateDialogVisible = false
this.$message({
type: 'info',
message: '已取消'
})
},
// 确认更新
async confirmUpdate() {
await update(this.updateForm, this.updateForm.id) // 调取更新接口
this.updateDialogVisible = false // 面板关闭
await this.handleSearch() // 查询
this.$message({
type: 'warning',
message: '更新成功'
})
},
// 打开新增面板 flag_7
handleInsert() {
this.insertDialogVisible = true
},
// 取消新增
cancelInsert() {
this.insertDialogVisible = false
this.$message({
type: 'info',
message: '已取消'
})
},
// 确认新增
async confirmInsert() {
await insert(this.insertForm) // 新增
this.insertDialogVisible = false // 面板关闭
await this.handleSearch() // 查询
this.$message({
type: 'warning',
message: '新增成功'
})
}
}
</script>
动态渲染表格(imp)
思路
- 前置说明: 由于该接口是动态查询数据库接口, 会有sql注入等风险, 因此仅用于个人运维使用. 同时为了接口传参通用性, 在sql中存在
select 1, fieldxxx from tablexxx
, 因此在前端提取字段时, 要先将1
字段删去 - 前端调取接口, 先将
1
字段删去, 而后遍历数据, 封装字段名 - 动态渲染至
el-table
中
查询并处理表头方法
// methods
// 执行查询 👇
async handLeSelect() {
// 查询之前要把动态表头置为空
this.tableHead = []
// 校验参数
if (!this.selectData.table || this.selectData.field.length === 0) {
this.$message.warning('请先选择表及字段再进行查询')
return
}
// 执行查询
// 赋值到data
this.loading.select = true // loading
const data = await handleSelect(this.selectData)
this.loading.select = false // loading
this.respData.msg = data.data.exceptionMsg
const list = data.data.data
// 遍历删除属性名是数字的
list.forEach(item => {
delete item[1]
})
if (list.length !== 0) {
const keyArr = Object.keys(list[0])
keyArr.map(key => this.tableHead.push({ key: key }))
}
this.respData.selectResult = list
}
// data() 👇
// 动态表头
tableHead: []
template渲染
<div class="searchTable">
<!-- 此处因为
-->
<el-table
v-loading="loading.select"
element-loading-text="玩命加载中🤣🤣🤣"
:data="respData.selectResult"
border
style="width: 100%"
max-height="530px"
:header-cell-style="{ background: '#f8f8f8', color: '#000' }"
>
<!--模板表格-->
<el-table-column
v-for="item in tableHead"
:key="item.key"
:label="item.key"
:property="item.key"
width="250"
>
<template v-slot="scope">
<!-- tooltip提示标签卡
注意: tooltip的content需要string, 因此 scope.row[scope.column.property] 拿到字段值之后要 toString()
-->
<el-tooltip
:content="scope.row[scope.column.property] ? scope.row[scope.column.property].toString() : ''"
>
<!-- 表格一行显示 👇 copyCellData(复制表格方法)-->
<div style="text-align: center" class="oneline">
<!-- 复制按钮 -->
<el-button
type="text"
icon="el-icon-copy-document"
@click="copyCellData(scope.row[scope.column.property])"
/>
{{ scope.row[scope.column.property] }}
</div>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
<script>
// ...methods
copyCellData(text) {
if (!text) {
this.$message.warning('无内容')
return
}
const textarea = document.createElement('textarea')
textarea.value = text
document.body.appendChild(textarea)
textarea.select()
document.execCommand('copy')
document.body.removeChild(textarea)
this.$message.success('复制成功')
},
</script>
<style scoped>
.searchTable {
width: 90%;
margin: auto auto;
}
.oneline {
overflow: hidden;
text-overflow: ellipsis;
/* 将对象作为弹性伸缩盒子模型显示 */
display: -webkit-box;
/* 限制在一个块元素显示的文本的行数 */
/* -webkit-line-clamp 其实是一个不规范属性,使用了WebKit的CSS扩展属性,该方法适用于WebKit浏览器及移动端;*/
-webkit-line-clamp: 1;
/* 设置或检索伸缩盒对象的子元素的排列方式 */
-webkit-box-orient: vertical;
}
/* 深度选择器-此处是为了解决el-table表头无法复制问题 important */
::v-deep .el-table th.el-table__cell {
user-select: initial;
}
</style>
表格选择事件
只需要在 el-table
中添加 @selection-change
事件及新增一个选择列即可, 代码如下👇
<!--
1. 添加@selection-change并绑定事件
2. 新增列 <el-table-column type="selection" />
3. methods及data()见下
附: 加载属性的使用: 新增如下属性即可
v-loading="loading.child" // 绑定的loading字段, 同时要在请求接口前打开, 完成请求后关闭
element-loading-text="玩命加载中🤣🤣🤣"
element-loading-spinner="el-icon-loading" // 如果全页面加载则删去此行
element-loading-background="rgba(196, 196, 196, 0.8)" // 如果全页面加载则删去此行
-->
<el-table
v-loading="loading.detail"
element-loading-text="玩命加载中🤣🤣🤣"
:data="xxx"
border
style="width: 100%"
max-height="530px"
:header-cell-style="{ background: '#f8f8f8', color: '#000' }"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" />
<el-table-column prop="xxx" label="逻辑名称" width="200" />
</el-table>
<script>
// ...data()
// 选择列表
selectedRows: []
// ...methods
// 表格选择事件(配合<el-table-column type="selection" />使用)
handleSelectionChange(selection) {
// 此处会将选中的row加入至数组
this.selectedRows = selection
},
</script>
组件
父子组件的引用
-
子组件引用父组件内容
-
在父组件的子标签中通过
:属性/方法名
传递过来 -
在
export
中通过props
引用进来 -
然后即可使用
<script> export default { name: 'Child', props: { // 子组件引用父组件对象(子组件是不建议修改父组件属性内容) dic: { type: Object, required: true }, // 引用方法后即可调用该方法 query: { required: true } } } </script>
-
-
父组件引用子组件内容
import
中导入子组件,并可以取别名- 在
componetnt
中声明子组件 - 在
template
中创建子组件标签,ref
为上一步的引用名, 如果需要将父组件的方法或者对象传递给子组件, 则定义变量即可
- 在代码中使用子组件方法/变量,如:
$refs.子组件引用名.方法名(参数)
<script> // 导入子组件 import Child from './components/Child.vue' export default { component: { Child }, methods: { invokeChildComponent() { // 引用子组件方法 this.$refs.child.xxxMethod() } } } </script> <template> <div> <!-- 传递给子组件对象/方法 :dic :query 将父组件对象/方法传递给子组件, 子组件通过该名字进行引用 --> <User ref="child" :dic="queryDic" :query="handleQuery" /> <!-- 引用子组件内容 --> <el-button type="text" size="small" @click="$refs.child.queryTbUserByGuid(row.toRowGuid)"> {{ row.toRowGuid }} </el-button> </div> </template>
$refs
是 Vue.js 提供的一个特殊属性,用于获取组件或元素的引用。在模板中,可以使用ref
属性来为组件或元素设置引用,然后在组件实例中使用$refs
来访问这个引用。 -
demo
<!-- 填写账号密码的伪代码 this.$refs.username.focus() 是一种操作 DOM 元素的方式,可以让我们在组件实例中访问元素的引用,并调用元素的方法或属性 --> <template> <el-input ref="username" v-model="loginForm.username" :placeholder="$t('login.username')" ...省略/> </template> <script> mounted() { // 如果用户名和密码的输入框没值, 则自动聚焦 if (this.loginForm.username === '') { this.$refs.username.focus() } else if (this.loginForm.password === '') { this.$refs.password.focus() } } </script>
子组件监听父组件内容,而不直接使用父组件(watch方式)
<script>
props: {
parentValues: {
type: Object,
default: nul
}
},
data() {
localValues: {
...this.parentValues // 创建本地副本
}
}
},
watch: {
values: {
handler (newVal) {
this.localValues = { ...newVal }
},
deep: true
}
},
</script>
emit 的使用
emit
是用于子组件向父组件传递信息的一种机制。它允许子组件触发一个自定义事件,并可以附带传递数据给父组件。
使用方法
-
子组件中触发事件
在子组件内部,使用
this.$emit
来触发一个事件。第一个参数是事件的名称,之后的参数是传递给父组件的数据// 子组件内 this.$emit('eventName', data)
-
父组件监听事件:父组件可通过
v-on
来监听子组件发出的事件,并执行相应的处理函数<!-- 父组件模板 --> <ChildComponent @eventName="handleEvent" />
// 父组件的方法 methods: { handleEvent(data) { console.log('子组件传来的数据:', data); } }
demo
假设有一个简单的计数器应用,其中子组件负责显示计数和提供增加计数的按钮,而父组件需要知道当前的计算值。
-
子组件(Counter.vue)
<template> <div> <p>当前计数: {{ count }}</p> <button @click="increment">+1</button> </div> </template> <script> export default { data() { return { count: 0 }; }, methods: { increment() { this.count++; this.$emit('updateCount', this.count); // 触发 updateCount 事件并传递 count } } }; </script>
-
父组件
<template> <div> <h1>总计数: {{ total }}</h1> <Counter @updateCount="updateTotal" /> </div> </template> <script> import Counter from './Counter.vue'; export default { components: { Counter }, data() { return { total: 0 }; }, methods: { updateTotal(count) { this.total = count; } } }; </script>
当用户点击点击子组件的按钮时,increment
方法被调用,此时会更新子组件的 count
数据属性,并通过 $emit
发出 updateCount
事件,将新的计数值传递给父组件。父组件监听了这个事件,并在 updateTotal
方法中更新自己的 total
数据属性
自定义组件demo
以封装 Tooltip 为例
-
定义 Tooltip.vue
<template> <el-tooltip :content="content" :effect="effect" :placement="placement"> <slot /> </el-tooltip> </template> <script> export default { name: 'Tooltip', props: { content: { type: String, required: true }, // 封装默认参数 effect: { type: String, default: 'dark' }, placement: { type: String, default: 'top' } } } </script>
-
组件的引用
<template>
<div>
<el-table>
<el-table-column prop="content" label="企业简介" width="150">
<template v-slot="{ row }">
<el-tooltip :content="row.content">
<div style="text-align: center" class="oneline">
{{ shortConent(row.content) }}
</div>
</el-tooltip>
</template>
</el-table-column>
</el-table>
</div>
</template>
<style>
.oneline {
overflow: hidden;
text-overflow: ellipsis;
/* 将对象作为弹性伸缩盒子模型显示 */
display: -webkit-box;
/* 限制在一个块元素显示的文本的行数 */
/* -webkit-line-clamp 其实是一个不规范属性,使用了WebKit的CSS扩展属性,该方法适用于WebKit浏览器及移动端;*/
-webkit-line-clamp: 1;
/* 设置或检索伸缩盒对象的子元素的排列方式 */
-webkit-box-orient: vertical;
}
</style>
链接标签的定义
<el-table-column prop="toRowGuid" label="toRowGuid" width="260">
<!-- 1. 定义template, 解构当前行 -->
<template v-slot="{ row }">
<!-- 2. 调用方法 -->
<el-button type="text" size="small" @click="$refs.user.queryTbUserByGuid(row.toRowGuid)">
{{ row.toRowGuid }}
</el-button>
</template>
</el-table-column>
watch的使用
基本用法
data() {
return {
watchField: true
}
},
watch: {
// 监听watchField对象, 如果新值发生了变化, 则执行一下逻辑
watchField(newV, oldV) {
console.log('message changed from', oldV, 'to', newV)
}
}
深度监听
如果要监听的对象时嵌套对象,需要使用 deep
选项来深度监听对象的变化
watch: {
'someObject.nestedProperty': {
handler(newV, oldV) {
console.log('Nested property changed')
},
deep: true
}
}
立即执行
默认情况下,watch
回调只会在监听到变化后触发。如果希望在初始渲染时也执行一次回调,可以使用 immediate
选项
watch: {
someProperty: {
handler(newVal, oldVal) {
console.log('someProperty changed');
},
immediate: true
}
}
监听多个属性
如果想在一个 watcher 中监听多个属性,你可以传递一个数组作为第一个参数:
watch: {
['property1', 'property2'](newVal, oldVal) {
console.log('Either property1 or property2 has changed');
}
}
注:这种方式不能直接获取到具体哪个属性发生了变化以及它的新旧值,因为 newVal
和 oldVal
会是整个对象的最新和旧值。如果需要知道具体哪个属性变化了,需要分别设置 watcher 或者在组件内维护额外的状态来跟踪这些变化。
使用计算属性替代
有时,watch
可能不是最佳选择,尤其是当只需要基于某些数据的变化来计算新值时。在这种情况下,使用计算属性(computed properties)可能是更好的选择。计算属性会根据其依赖的数据自动更新,并且更加简洁高效。
computed: {
computedMessage() {
return this.message + ' (Computed)';
}
}
其他框架积累
CryptoJS 的使用
-
安装、引用
npm install --save crypto-js import CryptoJS from 'crypto-js'
-
使用
// AES ECB加密 const content = '待加密内容' const key = CryptoJS.enc.Utf8.parse('key') const encrypted = CryptoJS.AES.encrypt(content, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }) const encContent = encrypted.ciphertext.toString(CryptoJS.enc.Base64) // AES ECB解密 const content = '待解密内容' const key = CryptoJS.enc.Utf8.parse('key') const decrypted = CryptoJS.AES.decrypt( { ciphertext: CryptoJS.enc.Base64.parse(content) }, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 } ) const decContent = decrypted.toString(CryptoJS.enc.Utf8)
xml提取
npm install xml2js --save
import { Parser } from 'xml2js'
const parser = new Parser({ explicitArray: false }) // 关闭底部数组解析
const detailXml = data.data.data.tableInfo // 待处理的xml字符串
// 解析xml至对象
parser.parseString(detailXml, (err, result) => {
if (err) {
console.error(err)
} else {
this.tableData = result.EcaTable
}
})