Vue 项目里的常用写法和坑点。基础语法和原理看 Vue学习笔记

基础

常用:const/let、对象字面量增强、filter/map、Promise。

1
2
3
4
5
6
7
8
9
10
11
12
13
const list = [1, 2, 3]
const result = list.filter(item => item > 1).map(item => item * 2)

new Promise((resolve, reject) => {
const ok = true
if (ok) {
resolve(result)
} else {
reject(new Error('request error'))
}
}).then(res => {
console.log(res)
})

重点:

1、优先 const,变量会变再用 let

2、箭头函数注意 this

3、列表处理优先 filter/map

4、异步请求先把 Promise 链写顺

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const name = 'lemon'
const age = 18

const user = {
name,
age,
print() {
console.log(`${name}-${age}`)
}
}

const nums = [1, 2, 4, 5, 6, 9, 10, 15]
const evenNums = nums.filter(item => item % 2 === 0)
const doubled = nums.map(item => item * 2)

有周趋势、补零这类场景时,Array.from(new Array(7)) 也很常用。

1
2
3
4
5
6
7
8
9
10
11
12
13
function getWeekDays() {
const now = new Date()
const day = now.getDay() || 7
return Array.from({ length: 7 }, (_, index) => {
const current = new Date(now)
current.setDate(now.getDate() - day + index + 1)
return current
})
}

function fillZero(value) {
return value < 10 ? `0${value}` : `${value}`
}

工具函数

列表、搜索、导出、联调时经常要补几段基础工具。

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
function formatDate(date) {
const target = new Date(date)
const year = target.getFullYear()
const month = `${target.getMonth() + 1}`.padStart(2, '0')
const day = `${target.getDate()}`.padStart(2, '0')
const hour = `${target.getHours()}`.padStart(2, '0')
const minute = `${target.getMinutes()}`.padStart(2, '0')
const second = `${target.getSeconds()}`.padStart(2, '0')
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
}

function debounce(fn, delay = 300) {
let timer = null
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => fn.apply(this, args), delay)
}
}

function throttle(fn, delay = 300) {
let lastTime = 0
return function (...args) {
const now = Date.now()
if (now - lastTime < delay) {
return
}
lastTime = now
fn.apply(this, args)
}
}

组件

常见点:

1、父传子、子传父

2、props 校验

3、data 写成函数的原因

4、$refs、插槽、事件派发怎么配合

5、数组和对象更新后页面不刷新的处理

1
2
3
4
5
6
props: {
number1: {
type: Number,
default: 0
}
}

请求

请求层主要看实例配置、拦截器、参数位置、请求头。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const instance = axios.create({
baseURL: '/api',
timeout: 5000
})

instance.interceptors.request.use(config => {
return config
})

axios.post('/api/user', data, {
headers: {
'Content-Type': 'application/json'
}
})

登录态一般配合 sessionStorage 和请求拦截器一起用:

1
2
3
4
5
6
7
8
9
window.sessionStorage.setItem('token', res.data.token)

instance.interceptors.request.use(config => {
const token = window.sessionStorage.getItem('token')
if (token) {
config.headers.Authorization = token
}
return config
})

常见问题:

1、get/post/put/delete 传参位置不对

2、content-type 和后端约定不一致

3、模块加载超时或者代理没配好

4、浏览器缓存和旧数据影响结果

登录态

高频操作:

1、登录成功后写 token

2、退出时清理本地登录态

3、页面初始化后按 token 拉菜单或用户信息

1
2
3
4
logout() {
window.sessionStorage.clear()
this.$router.push('/login')
}

这类逻辑容易漏清理,结果就是退出后页面状态还在,但接口已经 401。

布局和存储

联调时除了接口,最常碰到的就是 flex 布局和浏览器存储。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.box {
display: flex;
justify-content: center;
align-items: center;
}

.between {
display: flex;
justify-content: space-between;
align-items: center;
}

.item {
flex: 1 1 200px;
}
1
2
3
4
5
6
document.querySelector('#preview').src = imageUrl
document.querySelector('#panel').classList.add('is-open')

window.sessionStorage.setItem('token', token)
const token = window.sessionStorage.getItem('token')
document.cookie = `token=${token};path=/;max-age=${7 * 24 * 60 * 60}`

常见问题:

1、父元素没高度,align-items 看起来不生效

2、回显值存进了 sessionStorage,但退出或切页没有清理

3、字段回来了,DOM 也拿到了,但样式层把结果盖掉了

工程化

依赖和脚手架问题先查环境,再查代码。

常见问题:

1、vue-cli-service 找不到

2、npm install 失败

3、loader 版本对不上

4、脚手架能起,但是页面空白或者编译报错

1
2
rm -rf node_modules
npm install

重点排查 Node 版本、锁文件、node_modules、构建日志。

开发环境接口不通时,代理配置也要第一时间看:

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
}

插件类问题不要一上来怀疑业务代码,先确认初始化时机:

1
2
3
4
5
mounted() {
this.$nextTick(() => {
// 插件初始化
})
}

Element UI

高频场景:

1、表单回显不完整

2、树形控件刷新后状态丢失

3、表格和分页联动不对

4、图表配置项改了但页面不生效

5、组件状态和业务状态没有对齐

表单

表单重点是 rulespropref 对齐,重置用 resetFields()

组件库接入先把安装和全局注册做对:

1
2
3
4
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.use(ElementUI)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
reset() {
this.$refs.loginFormRef.resetFields()
}

data() {
const valiCateDeleted = (rule, value, cb) => {
if (value === 0 || value === '1' || value === '0') {
return cb()
}
cb(new Error('请选择状态'))
}

return {
loginFormRules: {
mobile: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
deleted: [{ validator: valiCateDeleted, trigger: 'change' }]
}
}
}

表格和弹框

常一起出现:el-tableel-dialogMessageBox、树形表格。

1
2
3
4
<el-table :data="tableData" border stripe>
<el-table-column prop="username" label="用户名"></el-table-column>
<el-table-column prop="mobile" label="手机号"></el-table-column>
</el-table>
1
2
3
4
5
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})

常见问题:

1、删除确认后没做列表刷新

2、弹框关闭后表单状态没清干净

3、表格分页切换后查询条件丢失

4、树形表格数据结构和列配置没对齐

如果是菜单页或后台壳子页,el-menu 也很常见:

1
2
3
4
5
6
<el-menu :default-active="activePath" router unique-opened>
<el-submenu index="1">
<template slot="title">用户管理</template>
<el-menu-item index="/users">用户列表</el-menu-item>
</el-submenu>
</el-menu>

级联和远程搜索

商品分类、组织树、项目列表常用这两类组件。

1
2
3
4
5
6
<el-cascader
v-model="selectedCatKeys"
:options="cascaderList"
:props="cateProps"
@change="handleChange">
</el-cascader>
1
2
3
4
5
cateProps: {
label: 'catName',
value: 'id',
children: 'children'
}
1
2
3
4
5
6
<el-select
v-model="curProject"
filterable
remote
:remote-method="remoteMethod">
</el-select>

关键点:后端返回结构、回显值、联动刷新一致。

树形表格一般还会配插件:

1
2
3
import TreeTable from 'vue-table-with-tree-grid'

Vue.component('tree-table', TreeTable)

Ant Design Vue

如果项目不是 Element UI,而是 Ant Design Vue,分页和表格配置也经常要改:

1
2
3
4
5
6
<a-table
:columns="columns"
:data-source="dataSource"
:pagination="pagination"
rowKey="id">
</a-table>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
data() {
return {
pagination: {
current: 1,
pageSize: 10,
total: 0,
showTotal: total => `共 ${total} 条`
}
}
}

handleTableChange(pagination) {
this.pagination.current = pagination.current
this.pagination.pageSize = pagination.pageSize
this.loadData()
}

常见坑

最常见:

1、resetFields() 调了但 prop 没配对,结果看起来没重置

2、树控件刷新了数据,但选中状态和展开状态丢了

3、Dialog 关闭后局部状态没清理,二次打开还是旧数据

4、远程搜索请求回来了,但 label/value 映射错了,导致选不上

5、表格行操作、分页、筛选三者互相打架

排查顺序

固定顺序:

1、环境和依赖

2、请求头、参数和接口返回

3、浏览器控制台和 network

4、组件状态和页面样式