uni-app配置eslint + commitlint + editorconfig + husky

我是傲夫靠斯,欢迎关注我的公众号【前端工程师的自我修养】,搜索f2ef2e,每天更新。

uniapp是一个优秀的跨端框架,但官方的创建项目并未提供eslint,这对于我们多人协作开发并不友好。现在网上的很多文章教程安装的都是老版本的husky等,现在已经很难跑通。本文全部使用最新的版本来安装,保证可以是正常使用的。有问题也欢迎交流。

使用vue-cli命令行创建项目

这里啰嗦一句,我并不喜欢使用HBuilder去创建项目和开发,因为它并不能实现自动化的CI/CD,还需要额外的安装一个IDE,所以我选择使用vs code去开发。

# 全局安装vue-cli
npm install -g @vue/cli

# 创建uni-app项目,选择默认模板
vue create -p dcloudio/uni-preset-vue my-uniapp

新创建的项目中并没有任何的Eslint配置

接下来,我们开始一步步安装配置

.editorconfig配置文件

在根目录创建.editorconfig文件

[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 100

editorconfig可以让我们多人开发写出的代码是一致的,大多数编辑都支持,vs code、webstorm等。vs code需要安装editorconfig插件。

husky安装

husky可以让我们使用Git的时候配置钩子,我们可以在git提交时做commit信息检查,eslint检查等等。

npx husky-init && npm install       # npm
npx husky-init && yarn              # Yarn 1
yarn dlx husky-init --yarn2 && yarn # Yarn 2

安装成功后,husky给我们一个创建了一个pre-commit的钩子的例子。在根目录的.husky文件夹中。

.husky
	pre-commit

这个我们先放在这,一会后面会用到。

commitlint安装

commitlint帮助我们检查git commit的信息是否符合团队的规范。这对多人协作的时候是非常必要的。

安装npm依赖

npm install @commitlint/cli @commitlint/config-conventional -D

在根目录创建commitlint.config.js配置文件

module.exports = {
  extends: ['@commitlint/config-conventional'],
};

将commitlint加到husky的钩子中

npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

接下来我们可以尝试提交

git add .
git commit -m "test"

会抛出错误信息

⧗   input: test
✖   subject may not be empty [subject-empty]
✖   type may not be empty [type-empty]

✖   found 2 problems, 0 warnings

然后我们输入正确的提交格式

git add .
git commit -m "chore: add commitlint"

提交成功

[master bf3104c] chore: add commitlint
 6 files changed, 1099 insertions(+), 1 deletion(-)
 create mode 100644 .editorconfig
 create mode 100755 .husky/commit-msg
 create mode 100755 .husky/pre-commit
 create mode 100644 commitlint.config.js

eslint安装

最后我们来安装eslint,我选用了vue 官方推荐airbnb规则

先安装依赖

npm i lint-staged eslint-plugin-vue eslint-plugin-import eslint babel-eslint @vue/eslint-config-airbnb -D

package.json文件做如下修改

1.在scripts中新增lint命令,新增lint-staged配置

{
  "scripts": {
  	...
    "lint": "eslint --fix --ext .js,.vue ./src"
  },
  ...
  "lint-staged": {
    "*.{js,vue}": [
      "npm run lint",
      "git add"
    ]
  }
}

2.在./husky/pre-commit把npm test修改为npx lint-staged

3.创建.eslintrc.js文件

module.exports = {
  root: true,
  env: {
    node: true,
  },
  extends: [
    'plugin:vue/essential',
    '@vue/airbnb',
  ],
  parserOptions: {
    parser: 'babel-eslint',
  },
  rules: {
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
  },
};

到这里就大功告成了。我们来测试一下

git add .
git commit -m "chore: add eslint"

可以看有很多eslint错误

✔ Preparing...
⚠ Running tasks...
  ❯ Running tasks for *.{js,vue}
    ✖ npm run lint [FAILED]
    ◼ git add
↓ Skipped because of errors from tasks. [SKIPPED]
✔ Reverting to original state because of errors...
✔ Cleaning up...

...
46:1  error  Unexpected tab character  no-tabs
47:1  error  Unexpected tab character  no-tabs
48:1  error  Unexpected tab character  no-tabs
✖ 38 problems (38 errors, 0 warnings)

我们去修改所有的eslint错误

✔ Preparing...
✔ Running tasks...
✔ Applying modifications...
✔ Cleaning up...
[master 2ef3b5a] chore: add eslint
 4 files changed, 4627 insertions(+), 2863 deletions(-)
 create mode 100644 .eslintrc.js

修改之后,正常通过提交。

欢迎随时交流。

项目源码:https://github.com/cmdfas/uniapp-eslint

欢迎关注我的公众号【前端工程师的自我修养】,搜索f2ef2e,每天更新。

参考:

https://typicode.github.io/husky/#/

https://commitlint.js.org/#/

https://github.com/okonet/lint-staged

<template> <view class="wrap"> <view class="dev-area"> <view style="width: 100%;padding: 20px;font-size: 24px;font-weight: bold;">实时数据监测</view> <view class="dev-cart"> <view class=""> <view class="dev-name">土壤湿度</view> <image class="dev-logo" src="../../static/temp.png" mode=""></image> </view> <view class="dev-data">{{(datas.Intelligent1)}} ℃</view> </view> <view class="dev-cart"> <view class=""> <view class="dev-name">人体检测</view> <image class="dev-logo" src="../../static/shuiwei.png" mode=""></image> </view> <view class="dev-data">{{datas.HC}} Pa</view> </view> <view class="dev-cart"> <view class=""> <view class="dev-name">天气</view> <image class="dev-logo" src="../../static/qiti.png" mode=""></image> </view> <view class="dev-data">{{ datas.rain >= 0 && datas.PRESSURE <= 100? '闲置' : (datas.PRESSURE > 100 && datas.PRESSURE <= 200? "已穿鞋" : (datas.PRESSURE > 200? '行走' : 0)) }}</view> </view> <view class="dev-cart"> <view class=""> <view class="dev-name">光照强度</view> <image class="dev-logo" src="../../static/gz.png" mode=""></image> </view> <view class="dev-data">{{datas.LDR1}} %</view> </view> <!-- <view style="width: 100%;padding: 20px;font-size: 24px;font-weight: bold;"></view> --> <!-- <view class="dev-cart"> <view class=""> <view class="dev-name">喂食状态</view> <image class="dev-logo" src="../../static/qiti.png" mode=""></image> </view> <view class="dev-data">{{datas.FEED111START==1? '正在喂食' :'暂停'}} </view> </view> <view class="dev-cart"> <view class=""> <view class="dev-name">报警状态</view> <image class="dev-logo" src="../../static/led.png" mode=""></image> </view> <view class="dev-data">{{datas.FEED111ALARM==1? '报警' :'关闭'}} </view> </view> --> <view style="width: 100%;padding: 20px;font-size: 24px;font-weight: bold;">远程控制</view> <button :type="active? 'primary' : 'default'" @click="autoMode('aaa','mode')">自动模式</button> <!--<button :type="active? 'default' : 'danger'" @click="autoMode('ddd','mode')">退出手动</button>--> <button :type="active? 'default' : 'primary'" @click="autoMode('bbb','mode')">手动模式</button> <!-- <view class="dev-cart"> <view class="" style="width: 200px;"> <view class="dev-name">喂食量:{{datas.FEED111LIM}}g</view> <image class="dev-logo" src="../../static/sd.png" mode=""></image> </view> <input class="dev-data" v-model="FEED111LIM" placeholder="输入" /> <button class="small-button" @click="setBfyVal('FEED111LIM','s')">提交</button> </view> <view class="dev-cart"> <view class="" style="width: 200px;"> <view class="dev-name">定时喂食:{{datas.FEED111TIME}}</view> <image class="dev-logo" src="../../static/speed.png" mode=""></image> </view> <picker mode="time" :value="FEED111TIME2" start="09:00" end="17:30" @change="bindTimeChange"> <view style="font-size: 12px;">{{FEED111TIME2?FEED111TIME2:'选择时间'}}</view> </picker> <button class="small-button" @click="setBfyVal('FEED111TIME','s')">提交</button> </view> --> <!-- <view class="dev-cart"> <view class="" style="width: 200px;"> <view class="dev-name">光照阈值:{{datas.BARN111lightLIM}}</view> <image class="dev-logo" src="../../static/gz.png" mode=""></image> </view> <input class="dev-data" v-model="BARN111lightLIM" placeholder="输入" /> <button class="small-button" @click="setBfyVal('BARN111lightLIM','s')">提交</button> </view> <view class="dev-cart"> <view class="" style="width: 200px;"> <view class="dev-name">气体阈值:{{datas.BARN111gasLIM}}</view> <image class="dev-logo" src="../../static/qiti.png" mode=""></image> </view> <input class="dev-data" v-model="BARN111gasLIM" placeholder="输入" /> <button class="small-button" @click="setBfyVal('BARN111gasLIM','s')">提交</button> </view> --> <!--</view> <view v-if="!active"> <view style="width: 100%;padding: 20px;font-size: 24px;font-weight: bold;">灯光控制</view> <view class="dev-cart" style="width: 640rpx;"> <button class="small-button" style="background-color: #2b9939;" @click="autoMode('LEDN')">开启</button> <button v-else class="small-button" style="background-color: #999192;" @click="autoMode('LEDF')">开启</button> <button class="small-button" style="background-color: #2b9939;" @click="autoMode('LEDF')">关闭</button> <button v-else class="small-button" style="background-color: #999192;" @click="setBfyVal2('BARN111CONT',1)">关闭</button> </view> <button v-if="datas.BARN111CONT==2" class="small-button" style="background-color: #2b9939;" @click="setBfyVal2('BARN111CONT',2)">强制关闭</button> <button v-else class="small-button" style="background-color: #999192;" @click="setBfyVal2('BARN111CONT',2)">强制关闭</button> <view style="width: 100%;padding: 20px;font-size: 24px;font-weight: bold;">水泵控制</view> <view class="dev-cart" style="width: 640rpx;"> <button class="small-button" style="background-color: #2b9939;" @click="autoMode('BON')">开启</button> <button v-else class="small-button" style="background-color: #999192;" @click="autoMode('LEDF')">开启</button> <button class="small-button" style="background-color: #2b9939;" @click="autoMode('BOFF')">关闭</button> <button v-if="datas.BARN111CONT==0" class="small-button" style="background-color: #2b9939;" @click="setBfyVal2('BARN111CONT',0)">开启</button> <button v-else class="small-button" style="background-color: #999192;" @click="setBfyVal2('BARN111CONT',0)">开启</button> <button v-if="datas.BARN111CONT==1" class="small-button" style="background-color: #2b9939;" @click="setBfyVal2('BARN111CONT',1)">关闭</button> <button v-else class="small-button" style="background-color: #999192;" @click="setBfyVal2('BARN111CONT',1)">关闭</button> --> <view v-if="!active"> <!-- 灯光控制 --> <view style="width: 100%; padding: 20px; font-size: 24px; font-weight: bold;">灯光控制</view> <view class="dev-cart" style="width: 640rpx;"> <!-- 开启按钮逻辑 --> <button v-if="lightOpenCondition" class="small-button" style="background-color: #2b9939;" @click="autoMode('LEDN')">开启</button> <button v-else class="small-button" style="background-color: #999192;" @click="autoMode('LEDF')">开启</button> <!-- 关闭按钮逻辑 --> <button v-if="lightCloseCondition" class="small-button" style="background-color: #2b9939;" @click="autoMode('LEDF')">关闭</button> <button v-else class="small-button" style="background-color: #999192;" @click="setBfyVal2('BARN111CONT',1)">关闭</button> </view> <!-- 强制关闭按钮逻辑 --> <button v-if="datas.BARN111CONT === 2" class="small-button" style="background-color: #2b9939;" @click="setBfyVal2('BARN111CONT',2)">强制关闭</button> <button v-else class="small-button" style="background-color: #999192;" @click="setBfyVal2('BARN111CONT',2)">强制关闭</button> <!-- 水泵控制 --> <view style="width: 100%; padding: 20px; font-size: 24px; font-weight: bold;">水泵控制</view> <view class="dev-cart" style="width: 640rpx;"> <!-- 开启按钮逻辑 --> <button v-if="pumpOpenCondition" class="small-button" style="background-color: #2b9939;" @click="autoMode('BON')">开启</button> <button v-else class="small-button" style="background-color: #999192;" @click="autoMode('LEDF')">开启</button> <!-- 关闭按钮逻辑 --> <button v-if="pumpCloseCondition" class="small-button" style="background-color: #2b9939;" @click="autoMode('BOFF')">关闭</button> <button v-if="datas.BARN111CONT === 0" class="small-button" style="background-color: #2b9939;" @click="setBfyVal2('BARN111CONT',0)">开启</button> <button v-else class="small-button" style="background-color: #999192;" @click="setBfyVal2('BARN111CONT',0)">开启</button> <button v-if="datas.BARN111CONT === 1" class="small-button" style="background-color: #2b9939;" @click="setBfyVal2('BARN111CONT',1)">关闭</button> <button v-else class="small-button" style="background-color: #999192;" @click="setBfyVal2('BARN111CONT',1)">关闭</button> </view> </view> </view> </view> </view> </template> <script> const { createCommonToken } = require('@/key.js') export default { data() { return { //active: 1, productId: '799knCKRru', deviceName: "test", temp: '', humi: '', led: true, bee: true, //datas: {}, uid: 'a86461193caac133ec583a2dd6e5c8c3', TUL004: '', HUL004: '', LUL004: '', FEED111LIM: '', FEED111TIME2: '', FEED111TIME: '1000', active: false, datas: { BARN111CONT: 0 }, // 新增灯光控制条件变量 lightOpenCondition: true, lightCloseCondition: true, // 新增水泵控制条件变量 pumpOpenCondition: true, pumpCloseCondition: true }; }, onLoad() { const params = { author_key: 'OkuBMchdHrZPKiGNt6kl2fUYSJ8UNbEiznFEArJt/OhmRUvWLKYoGJGE9BUITojxlqnB0cN61//gll6vFEtjauw==', version: '2022-05-01', user_id: '4053575', product: this.productId, device: this.deviceName, } this.token = createCommonToken(params); this.fetchDevData(); setInterval(() => { this.fetchDevData(); }, 3000) }, // computed: { // result() { // if (this.datas.PRESSURE >= 0 && datas.PRESSURE <= 100) { // return '闲置'; // } else if (this.datas.PRESSURE > 100 && this.datas.PRESSURE <= 200) { // return '已穿上'; // } else if (this.datas.PRESSURE > 200) { // return "行走"; // } // } // }, onShow() {}, methods: { autoMode(param) { // 自动模式方法逻辑 console.log('执行自动模式操作', param); }, setBfyVal2(key, value) { // 设置值方法逻辑 console.log('设置值操作', key, value); this.datas[key] = value; // autoMode(msg, mode) { // if (mode == 'mode') { // this.active = !this.active // } // wx.request({ // url: 'https://apis.bemfa.com/va/postJsonMsg', //api接口,详见接入文档 // method: "POST", // data: { // uid: this.uid, // topic: "LED", // type: 3, // msg: msg // }, // success(res) { // console.log(res, 'kkkkkkkk') // // that.fetchDevData(); // } // }) // }, bindTimeChange(e) { this.FEED111TIME2 = e.target.value; this.FEED111TIME = e.target.value.replace(':', ''); }, fetchDevData() { var that = this; uni.request({ url: 'https://apis.bemfa.com/va/alltopic?uid=' + this.uid + "&type=3", //get接口,详见巴法云接入文档 data: { uid: this.uid, type: 3, }, success(res) { var dataArr = res.data.data; var datas = {}; for (let obj of dataArr) { datas[obj.topic] = obj.msg; that.datas = datas; } } }) }, setBfyVal(val, t) { var that = this; var msg = parseInt(this[val]); if (t == 's') { msg = msg + '' } wx.request({ url: 'https://apis.bemfa.com/va/postJsonMsg', //api接口,详见接入文档 method: "POST", data: { uid: this.uid, topic: val, type: 3, msg: msg }, success(res) { console.log(res.data) that.fetchDevData(); } }) }, setBfyVal2(val, t) { var that = this; wx.request({ url: 'https://apis.bemfa.com/va/postJsonMsg', //api接口,详见接入文档 method: "POST", data: { uid: this.uid, topic: val, type: 3, msg: t + '' }, success(res) { console.log(res.data) that.fetchDevData(); } }) }, setFlagVal(flag) { var params = {}; flag = "" + flag; params[flag] = parseInt(this[flag]); uni.request({ url: 'https://iot-api.heclouds.com/thingmodel/set-device-property', method: 'POST', data: { product_id: this.productId, device_name: this.deviceName, params: params }, header: { 'authorization': this.token //自定义请求头信息 }, success: () => { console.log('dantemp updated successfully:'); }, fail: (error) => { console.error('Request failed:'); } }); }, // onDANHUMISwitch(event) { // console.log('Submitting dantemp:', this.dantemp); // 日志输出 // console.log(event.detail.value); // let value = event.detail.value; // uni.request({ // url: 'https://iot-api.heclouds.com/thingmodel/set-device-property', //仅为示例,并非真实接口地址。 // method: 'POST', // data: { // product_id: '799knCKRru', // device_name: 'test', // params: { // "tar_humi": parseInt(this.danhumi) // } // }, // header: { // 'authorization': this.token //自定义请求头信息 // }, // success: () => { // console.log('danhumi updated successfully'); // }, // fail: (error) => { // console.error('Request failed:', error); // } // }); // }, } } </script> <style> .zt { background-color: #2b9939; } .wrap { padding: 30rpx; } .dev-area { display: flex; justify-content: space-between; flex-wrap: wrap; } .dev-cart { height: 150rpx; width: 320rpx; border-radius: 30rpx; margin-top: 30rpx; display: flex; justify-content: space-around; align-items: center; box-shadow: 0 0 15rpx #ccc; } .dev-name { font-size: 20rpx; text-align: center; color: #6d6d6d; } .dev-logo { width: 70rpx; height: 70rpx; margin-top: 10rpx; } .dev-data { font-size: 30rpx; color: #6d6d6d; } .small-button { padding: 10rpx 20rpx; font-size: 24rpx; border-radius: 15rpx; background-color: #2b9939; color: white; border: none; } </style> 检测代码是否有问题
最新发布
05-13
<think>好的,我现在需要解决用户在Vue项目中遇到的babel-loader报错“Missing semicolon”的问题,同时还要检查其他可能的代码问题。首先,我应该先理解这个错误的具体原因。 “Missing semicolon”通常是指JavaScript代码中缺少分号。虽然JavaScript允许在某些情况下省略分号,但某些代码风格检查工具或ESLint规则可能要求必须加分号。用户的项目可能配置ESLint,并且启用了相关的规则,比如`semi`规则设置为强制分号。这时候,代码中的语句如果没有分号结尾,就会触发这个错误。 接下来,我需要考虑解决方案。首先,应该手动检查报错位置,确认是否确实缺少分号。如果是的话,直接补上分号即可。但如果是整个项目存在多处类似问题,可能需要配置ESLint的规则,或者使用自动修复命令来批量处理。例如,运行`eslint --fix`可以自动添加缺失的分号。 然后,用户还提到要检查其他可能的代码问题。结合引用中的内容,可能包括代码风格问题,如单引号使用、箭头函数参数括号、末尾逗号等。这些都属于ESLint的常见规则,需要检查项目的ESLint配置,比如是否启用了`comma-dangle`、`quotes`等规则,并根据团队规范进行调整。 另外,引用[2]提到多人协作时统一代码风格的重要性,所以需要建议用户确保项目中有统一的ESLint配置,可能使用.prettierrc或.editorconfig文件,并且所有成员都遵循这些规则。同时,可以建议在IDE中配置自动格式化插件,如Prettier,以在保存时自动修复格式问题。 还有可能存在其他潜在问题,比如未声明的变量、未使用的变量或导入的组件,这些可以通过ESLint的`no-unused-vars`规则来检测。此外,Vue文件中的模板部分也要检查标签是否正确闭合,属性是否使用正确格式(比如是否遗漏.vue扩展名,引用[1]中提到过),以及是否正确使用指令等。 最后,可能需要检查babel和webpack的配置是否正确,确保依赖版本兼容,特别是如果项目是从旧版本升级而来,可能存在配置差异。例如,babel-loader的版本与webpack或babel核心库的版本是否匹配,是否需要更新或调整配置中的presets和plugins。 总结下来,解决步骤应该是:1. 处理缺失分号的报错;2. 检查并统一其他ESLint规则;3. 使用自动化工具进行修复;4. 检查Vue文件语法和结构;5. 确保构建工具配置正确。同时,建议用户配置团队统一的代码规范,避免未来出现类似问题。</think>### 解决方案:Vue项目中`babel-loader`报错`Missing semicolon`及代码问题检查 --- #### **一、`Missing semicolon`问题定位与修复** 1. **直接修复分号缺失** - 检查报错文件的具体行号,手动添加分号。例如: ```javascript // 原代码 const a = 1 // 修复后 const a = 1; ``` - **注意**:JavaScript允许省略分号,但若项目配置ESLint的`semi`规则(强制分号),则需遵守。 2. **ESLint自动修复** - 运行以下命令自动修复分号问题: ```bash npx eslint --fix --ext .js,.vue src/ ``` - 检查`.eslintrc.js`配置,确认`semi`规则是否为`["error", "always"]`: ```javascript module.exports = { rules: { semi: ["error", "always"] // 强制分号 } }; ``` --- #### **二、其他常见代码问题检查** 1. **引号格式问题** -ESLint报错`Strings must use singlequote`,需将双引号改为单引号: ```javascript // 原代码 const name = "vue" // 修复后 const name = 'vue'; ``` - 配置`.eslintrc.js`中的`quotes`规则: ```javascript rules: { quotes: ["error", "single"] } ``` 2. **箭头函数参数括号** - 若报错`Expected parentheses around arrow function argument`,需为单参数添加括号: ```javascript // 原代码 const fn = x => x * 2; // 修复后 const fn = (x) => x * 2; ``` 3. **末尾逗号问题** - 若报错`Missing trailing comma`,需在对象或数组末尾添加逗号(多行时推荐): ```javascript // 原代码 const obj = { a: 1, b: 2 }; // 修复后 const obj = { a: 1, b: 2, }; ``` 4. **文件扩展名缺失** - 引用组件时需补充`.vue`扩展名: ```javascript // 原代码 import App from './App'; // 修复后 import App from './App.vue'; ``` --- #### **三、潜在问题排查** 1. **未使用的变量或依赖** - 检查是否定义了未使用的变量(ESLint规则`no-unused-vars`): ```javascript // 错误示例 const unusedVar = 'test'; // 未使用 ``` - 删除冗余代码或添加注释说明保留原因。 2. **Vue组件语法检查** - 确保模板标签正确闭合: ```vue <!-- 错误示例 --> <template> <div> </template> <!-- 修复后 --> <template> <div></div> </template> ``` 3. **构建工具兼容性** - 检查`babel-loader`版本是否与Webpack兼容: ```bash # 推荐组合 "webpack": "^5.0.0", "babel-loader": "^8.2.2", "@babel/core": "^7.0.0" ``` - 更新配置(`babel.config.js`): ```javascript module.exports = { presets: ['@babel/preset-env'] }; ``` --- #### **四、团队协作规范建议** 1. **统一代码风格** - 在项目中添加`.prettierrc`: ```json { "singleQuote": true, "semi": true, "trailingComma": "all" } ``` - 配置IDE(如VSCode)保存时自动格式化[^2]。 2. **提交前检查** - 使用`husky` + `lint-staged`在Git提交时自动校验: ```json // package.json "lint-staged": { "*.{js,vue}": ["eslint --fix", "prettier --write"] } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值