1、环境配置:
实现原理:
采用 node.js 顶层对象中的 process.env(进程环境,返回一个包含用户环境信息的对象)属性,根据不同的配置文件(.env.xxx)来区分和切换环境
配置文件:.env.xxx
// .env.dev
VUE_APP_ENV = development
VUE_APP_XXX_SERVE = "https://xxxxx.xxx.com/xxxxx"
// .env.pro
VUE_APP_ENV = production
VUE_APP_XXX_SERVE = "https://xxxxx.xxx.com/xxxxx"
package.json:
{
"scripts":{
"dev":"vue-cli-service serve --mode dev",
"prod":"vue-cli-service serve --mode pro"
}
}
之后输入命令 npm run xxx 就可以实现对应环境地址的切换啦~
2、vue2 中防抖的做法:
data() {
return {
timer: null // 定义一个变量,用于接收、解除定时器
}
},
watch: {
xxx(val) {
if (this.timer) {
clearTimeout(this.timer)
}
this.timer = setTimeout(() => {
// 业务逻辑,例如 filter、axios 查询请求等等
}, 1000)
}
},
3、Git 操作汇总
# 查看远程仓库
git remote -v
# 添加远程仓库
git remote add <remote name> <ssh url>
# 更名远程仓库
git remote rename <remote name> <new name>
# 拉取远程仓库内容
git pull <remote name> <branch name>
# 推送到远程仓库
git push <remote name> <branch name>
# 推送新分支到远程仓库
git push --set-upstream <remote name> <branch name>
# 删除本地分支
git branch --delete <branch name>
# 拉取远程仓库中的新分支
git checkout -b <newBranchName>
git branch --set-upstream-to=<remote name>/<newBranchName>
git pull <remote name> <newBranchName>
# 回退 commit
git revert -n <commit version>
# 缓存修改(切换分支、合并代码时非常有效)
git stash // 将修改记录缓存到堆栈中
git stash save "message"
git stash list // 查看当前 stash 堆栈中的内容
git stash pop // 将当前 stash 中的内容弹出,并引用到当前分支对应的工作目录中(会将最近一次保存的 stash 删除)
git stash apply // 将堆栈中的内容应用到当前目录(不会从堆栈中删除 stash 记录)
git stash drop "name" // 从堆栈中删除删除
git stash clear
git stash show // 查看堆栈中最新保存的 stash 和当前目录的差异
4、Vuex 模块化:
最外面的 index:
const countOptions = {
// 如果想起别名,必须加上(默认为 false)
namespaced: true,
actions: {...},
mutations: {...},
state: {...},
getters: {...},
}
export default new Vuex Store({
modules: {
// 使用别名
countAbout: countOptions.
// countOptions
}
})
使用数据:
<template>
<p>
{{ sum }}
</p>
<button @click="increment"></button>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
...mapState('countAbout', ['sum', ...]) // 数组
...mapGetters('countAbout', ['bigSum', ...]) // 数组
},
methods: {
...mapMutations('countAbout', { increment: 'JIA', ... }) // 对象
...mapActions('countAbout', { incrementOdd: 'JIAOdd', ... }) // 对象
}
}
</script>
5、vuedraggable 使用经验分享(行拖动)
1、安装:
npm i -S vuedraggable
2、导入并注册
import draggable from 'vuedraggable' // 导入的是一个组件,需要注册
export default {
components: {
draggable,
},
};
3、使用 draggable
<template>
<table>
<!-- 如果要添加表头,目前我的做法是使用 draggable 进行包装,并通过 disabled 设置为不可拖拽;否则会出现显示问题 -->
<draggable :disabled="true">
<tr>
<th>AAA</th>
<th>BBB</th>
<th>CCC</th>
</tr>
</draggable>
<draggable
v-model="listForDrag" (使用 v-model 绑定用于展示数据的列表)
animation="150" (填入拖拽动画的延时时长)
handle=".move" (类似选择器,只有带 move 类的元素才可以被拖动)
@start="onStart" (拖拽开始事件)
@end="onEnd" (拖拽结束事件)
:move="checkMove" (拖拽过程事件)>
<tr v-for="item in listForDrag" :key="item.id" class="move">
<td>{{ item.aaa || '-' }}</td>
<td>{{ item.bbb || '-' }}</td>
<td>{{ item.ccc || '-' }}</td>
</tr>
</draggable>
</table>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: {
draggable,
},
data() {
return {
drag: false, // 记录是否正在执行拖拽
listForDrag: [],
};
},
methods: {
// 可以监听到调用拖拽的对象信息
checkMove(e){
return true;
},
onStart() {
this.drag = true;
},
onEnd() {
this.drag = false;
},
},
};
</script>
6、ES6 运算符
0、升级 babel
该语法在 babel 版本不支持的情况下使用会报错
1、空值合并操作符(??)
x ?? y,当 x 为 null 或者 undefined 时,返回 y,否则返回 x
const nullValue = null
const emptyText = ''
const someNum = 42
console.log(nullValue ?? 'valA 的默认值') // valA 的默认值
console.log(emptyText ?? 'valB 的默认值') // '' (空字符串虽然是空值,但不是 null / undefined)
console.log(someNum ?? 'valC 的默认值') // 42
2、可选链操作符(?.)
允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。在引用为空(null / undefined) 的情况下不会引起错误,短路时返回值是 undefined
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah'
}
}
console.log(adventurer.dog?.name) // undefined
console.log(adventurer.someNonExistentMethod?.()) // undefined
a?.b
// 等同于
a == null ? undefined : a.b
a?.[x]
// 等同于
a == null ? undefined : a[x]
a?.b()
// 等同于
a == null ? undefined : a.b()
a?.()
// 等同于
a == null ? undefined : a()
3、空赋值运算符(??=)
x ??= y,仅在 x 为 null / undefined 时对其赋值
let a = 0
a ??= 1
console.log(a) // 0
let b = null
b ??= 1
console.log(b) // 1
7、Element 中 Switch 无法正常更新的问题
开发中,switch 的 v-model 需要绑定深层次的属性 inherit:
const obj = [{
name: 'xxx',
children: [{
inherit: false
}]
}]
问题:常规的写法并不会根据 switch 的动作实时更新开关状态(点击开关没反应,必须刷新才有效果)
<el-switch
v-model="row.inherit"
</el-switch>
解决方法:需要使用 vue 中自带的 $set 函数来辅助更新数据
<el-switch
v-model="row.inherit"
@change="controlSwitch($event, row)">
<!-- $event 传递的是更改的值 -->
</el-switch>
<script>
export default {
methods: {
controlSwith(val, row) {
// this.$set(target, key, value)
this.$set(row, row.inherit, val)
}
}
}
</script>
8、打开的标签页太多,滚动条太细不好拖动?
【VSCode 设置】-搜索 【Wrap Tabs】-选中
9、ElementUI 中使用嵌套 Dialog 或者连续使用 Dialog 并嵌套 Select 时,Select 层级过低问题
<!-- 为每个 select 都设置以下属性 -->
<el-select :popper-append-to-body="false" popper-class="select-popper">
/* 解决 dialog 嵌套 select 显示层级不正常问题 */
.el-select /deep/ .select-popper {
z-index: 8888 !important;
top: auto !important;
left: auto !important
}
10、vue3 中子组件修改对象形式的 props
<!-- 父组件中引用子组件时使用 v-model -->
<SheetSectionTableButtons
v-model:editingFieldValue="editingFieldValue"
/>
// 子组件中用 defineProps 接收 v-model
const props = defineProps<{
editingFieldValue?: Record<string, FieldType>
}>()
// 子组件中定义修改 props 的自定义事件
const emit = defineEmits<{
(e: 'update:editingFieldValue', newEditingFieldValue: Record<string, FieldType>): void
}>()
// 使用另一个对象更新父组件中的 v-model
function fn() {
const newEditingFieldValue = { ...props.editingFieldValue }
newEditingFieldValue[fieldId].fieldValue = useCopyNames(props.fieldValue, value)
emit('update:editingFieldValue', newEditingFieldValue)
}
注意:虽然对象形式的 props 可以在子组件中直接被修改,但是并不推荐
11、antd 表单校验时,即使填了值也爆红
检查与 AForm 绑定的 model 对象的 key 值是否与 AFormItem 绑定的 name 值相等
12、hover 过快导致一些使用上的不便
在需要对 hover 进行延迟的地方加上:
<li
@mouseenter="setHoverDelay()"
@mouseleave="removeHoverDelay()" />
<script setup lang="ts">
const timer = ref(null)
function setHoverDelay (menu) {
if (timer.value) {
clearTimeout(timer.value)
}
timer.value = setTimeout(() => {
data.selectedMenu = menu
}, 300)
}
function removeHoverDelay () {
if (timer.value) {
clearTimeout(timer.value)
}
}
</script>
13、el-table 仅修改一行更新 tableData 但无法更新 DOM?
<el-table
:tableData="tableData"
:key="refreshFlag">
</el-table>
<script>
data () {
return {
refreshFlag: false,
}
},
watch: {
tableData: {
handler (val) {
if (val) {
this.refreshFlag = !this.refreshFlag
}
},
deep: true
}
}
</script>
14、ESLint 保存时自动修正格式
"eslint.enable": true,
"eslint.run": "onType",
"eslint.options": {
"extensions": [
".js",
".vue",
".jsx",
".tsx"
]
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
15、El-checkbox 点击两次才切换,或者点击根本不切换
使用 :value 而非 v-model
16、Element 下载 Excel 文件
如果有 axios 拦截器,记得放行:
if (response.data instanceof Blob) {
return response
}
<el-upload
drag
action=""
:show-file-list="false"
:before-upload="isExcel"
:http-request="importExcel">
</el-upload>
<el-button type="primary" icon="el-icon-download" @click="export2Excel(true)">下载模板</el-button>
// 判断是否是 Excel 文件(.xlsx 后缀)
isExcel (file) {
if (file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {
this.excelFile = file
return true
} else {
errorMsg('请上传 .xlsx 后缀的 excel 文档')
return false
}
},
/**
* 导入 Excel
* 1、请求头:multipart/form-data
* 2、格式正确响应:response.data.type === 'application/json'
* 3、格式错误响应:response.data.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
* 4、上传成功后需要等待几秒后再刷新
*/
async importExcel (event) {
const formData = new FormData()
formData.append('excelFile', event.file)
await $http.post('api地址', formData, {
headers: {
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundaryVCFSAonTuDbVCoAN'
},
responseType: 'blob'
}).then(
response => {
if (response.data.type !== 'application/json') {
warningMsg('上传失败啦!请接收失败原因')
saveAs(
new Blob([response.data],
{ type: 'application/octet-stream' }),
'Excel 文件上传失败原因.xlsx'
)
} else {
successMsg('上传成功!正在处理,请稍后刷新查看')
}
})
},
/**
* 导出成员列表为 Excel
* 1、响应类型:blob
* 2、使用插件 { savaAs } = file-saver 解析 blob
*/
async export2Excel (template = false) {
await $http.get('api地址', {
responseType: 'blob'
}).then(
response => {
saveAs(
new Blob([response.data],
{ type: 'application/octet-stream' }),
'下载文件名.xlsx'
)
},
reject => {
errorMsg(`导出 Excel 异常:${reject.message ?? '未知错误'}`)
})
},
17、TS项目中引入 JSON 文件报错
找不到模块
../lib/pca-code.json。请考虑使用--resolveJsonModule导入带.json扩展的模块。ts(2732)
处理方法:在根目录的 shims-vue.d.ts 文件中加入以下代码
declare module '*.json' {
const value: any;
export default value;
}
18、如何 inject 一个函数
父组件:
provide('函数名', 目标函数)
子组件:
const 函数名 = inject('函数名', Function, true)
19、动态绑定 SRC 图片不显示的问题
因为动态添加src被当做静态资源处理了,没有进行编译,使用require定义之后,就可以动态使用了。
<img :src="require(src)">
20、如何批量使用 axios 发送请求?
const requestList = []
roleList.forEach(role => {
const request = () => {
return axios.post('/api', {
...params
})
}
requestList.push(request())
})
const results = await Promise.allSettled(requestList)
21、普通链接转换为迅雷下载
可以类比其他应用的超链接
<template>
<div class="container">
<a href="https://puhuiti.oss-cn-hangzhou.aliyuncs.com/AlibabaPuHuiTi-3/AlibabaPuHuiTi-3-35-Thin.zip">阿里巴巴普惠体(普通下载)</a>
<br>
<br>
<a data-thunder href="https://puhuiti.oss-cn-hangzhou.aliyuncs.com/AlibabaPuHuiTi-3/AlibabaPuHuiTi-3-35-Thin.zip">阿里巴巴普惠体(迅雷下载)</a>
</div>
</template>
<script lang="ts" setup>
import { onMounted } from 'vue';
onMounted(() => {
const links = Array.from(document.querySelectorAll('a[data-thunder]')) as HTMLAnchorElement[]
for (const link of links) {
// 迅雷链接编码:AAbase64ZZ,btoa:将二进制字符串编码为 base64 字符串
const base64 = btoa(`AA${link.href}ZZ`)
link.href = `thunder://${base64}`
}
})
</script>
22、跨页签通信方案之 BroadcastChannel
<template>
<div class="container">
<button @click="PostMessage">PostMessage</button>
<iframe src="http://localhost:3000/"></iframe>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
const bc = ref(new BroadcastChannel('centerChannel'))
onMounted(() => {
bc.value.addEventListener('message', (e) => {
if (window.top !== window) {
console.log('iframe', e)
} else {
console.log('origin', e)
}
})
})
function PostMessage () {
bc.value.postMessage({
type: 'castMessage',
content: '哈哈哈',
})
}
</script>