亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

前端錯誤日志上報的超詳細(xì)解決方案

 更新時間:2024年11月20日 10:37:27   作者:C+?安口木  
這篇文章主要介紹了如何使用Node.js搭建一個簡單的錯誤監(jiān)控平臺,用于收集和上報Vue.js項目的運行錯誤,文中通過代碼以及圖文介紹的非常詳細(xì),需要的朋友可以參考下

前言

項目上線之后,用戶如果出現(xiàn)錯誤(代碼報錯、資源加載失敗以及其他情況),基本上沒有辦法復(fù)現(xiàn),如果用戶出了問題但是不反饋或直接不用了,對開發(fā)者或公司來說都是損失。

由于我這個項目比較小,只是一個迷你商城,所以不需要收集很復(fù)雜的數(shù)據(jù),只需要知道有沒有資源加載失敗、哪行代碼報錯就可以了,市面上有很多現(xiàn)成的監(jiān)控平臺比如sentry,在這里我選擇通過nodejs自己搭一個服務(wù)。

概述

我的項目是使用Vue2寫的,所以本文主要是講Vue相關(guān)的部署過程

1、部署后臺服務(wù)(使用express)

2、收集前端錯誤(主要是Vue)

3、提交信息到后臺分析源碼位置及記錄日志

js異常處理

function test1 () {
    console.log('test1 Start');
    console.log(a);
    console.log('test1 End');
}

function test2 () {
    console.log('test2 Start');
    console.log('test2 End');
}

test1();
test2();

這里可以看到,當(dāng)js運行報錯后,代碼就不往下執(zhí)行了,這是因為js是單線程,具體可以看看事件循環(huán),這里不做解釋

接下來看看使用異步的方式執(zhí)行,可以看到?jīng)]有影響代碼的繼續(xù)運行

function test1 () {
    console.log('test1 Start');
    console.log(a);
    console.log('test1 End')
}

function test2 () {
    console.log('test2 Start');
    console.log('test2 End')
}

setTimeout(() => {
    test1();
}, 0)

setTimeout(() => {
    test2();
}, 0)

那報錯之后我們?nèi)绾问占e誤呢?

try catch

function test1 () {
    console.log('test1 Start');
    console.log(a);
    console.log('test1 End')
}

try {
  test1();
} catch (e) {
  console.log(e);
}

使用try catch將代碼包裹起來之后,當(dāng)運行報錯時,會將收集到的錯誤傳到catch的形參中,打印之后我們可以拿到錯誤信息和錯誤的堆棧信息,但是try catch無法捕獲到異步的錯誤

function test1 () {
    console.log('test1 Start');
    console.log(a);
    console.log('test1 End')
}

try {
  setTimeout(function() {
    test1();
  }, 100);
} catch (e) {
  console.log(e);
}

可以看到try catch是無法捕獲到異步錯誤的,這時候就要用到window的error事件

監(jiān)聽error事件

window.addEventListener('error', args => {
  console.log(args);
  return true;
}, true)

function test1 () {
    console.log('test1 Start');
    console.log(a);
    console.log('test1 End')
}

setTimeout(function() {
  test1();
}, 100);

除了window.addEventListener可以監(jiān)聽error之后,window.onerror也可以監(jiān)聽error,但是window.onerrorwindow.addEventListener相比,無法監(jiān)聽網(wǎng)絡(luò)異常

window.addEventListener

<img src="https://www.baidu.com/abcdefg.gif">
<script>
  window.addEventListener('error', args => {
    console.log(args);
    return true;
  }, true) // 捕獲
</script>

window.onerror

<img src="https://www.baidu.com/abcdefg.gif">
<script>
  window.onerror = function(...args) {
    console.log(args);
  }
</script>

由于無法監(jiān)聽到,這里就不放圖了

unhandledrejection

到目前為止,Promise已經(jīng)成為了開發(fā)者的標(biāo)配,加上新特性引入了async await,解決了回調(diào)地獄的問題,但window.onerrorwindow.addEventListener,對Promise報錯都是無法捕獲

window.addEventListener('error', error => {
  console.log('window', error);
})
new Promise((resolve, reject) => {
  console.log(a);
}).catch(error => {
  console.log('catch', error);
})

可以看到,監(jiān)聽window上的error事件是沒有用的,可以每一個Promise寫一個catch,如果覺得麻煩,那么就要使用一個新的事件,unhandledrejection

window.addEventListener('unhandledrejection', error => {
  console.log('window', error);
})
new Promise((resolve, reject) => {
  console.log(a);
})

其中,reason中存放著錯誤相關(guān)信息,reason.message是錯誤信息,reason.stack是錯誤堆棧信息

Promise錯誤也可以使用 try catch捕獲到,這里就不做演示了

至此,js中同步、異步、資源加載、Promise、async/await都有相對應(yīng)的捕獲方式

window.addEventListener('unhandledrejection', error => {
  console.log('window', error);
  throw error.reason;
})

window.addEventListener('error', error => {
  console.log(error);
  return true;
}, true)

vue異常處理

由于我的項目使用Vue2搭建的,所以還需要處理一下vue的報錯

export default {
  name: 'App',
  mounted() {
    console.log(aaa);
  }
}

現(xiàn)在的項目基本上都是工程化的,通過工程化工具打包出來的代碼長這樣,上面的代碼打包后運行

通過報錯提示的js文件,查看后都是壓縮混淆之后的js代碼,這時候就需要打包時生成的source map文件了,這個文件中保存著打包后代碼和源碼對應(yīng)的位置,我們只需要拿到報錯的堆棧信息,通過轉(zhuǎn)換,就能通過source map找到對應(yīng)我們源碼的文件及出錯的代碼行列信息
那我們怎么才能監(jiān)聽error事件呢?

使用Vue的全局錯誤處理函數(shù)Vue.config.errorHandlersrc/main.js中寫入以下代碼

Vue.config.errorHandler = (err, vm, info) => {
  console.log('Error: ', err);
  console.log('vm', vm);
  console.log('info: ', info);
}

現(xiàn)在打包vue項目

打包vue之后然后通過端口訪問index.html,不建議你雙擊打開,如果你沒改過打包相關(guān)的東西,雙擊打開是不行的,可以通過vs code裝插件live server,然后將打包文件夾通過vs code打開

上報錯誤數(shù)據(jù)

經(jīng)過上述的異常處理后,我們需要將收集到的錯誤進(jìn)行整理,將需要的信息發(fā)送到后臺,我這里選擇使用ajax發(fā)請求到后端,當(dāng)然你也可以使用創(chuàng)建一個圖片標(biāo)簽,將需要發(fā)送的數(shù)據(jù)拼接到src上

這里我選擇使用tracekit庫來解析錯誤的堆棧信息,axios發(fā)請求,dayjs格式化時間

npm i tracekit
npm i axios
npm i dayjs

安裝完成后在src/main.js中引入tracekit、axios、dayjs

上報Vue錯誤

import TraceKit from 'tracekit';
import axios from 'axios';
import dayjs from 'dayjs';

const protcol = window.location.protocol;
let errorMonitorUrl = `${protcol}//127.0.0.1:9999`;
const errorMonitorVueInterFace = 'reportVueError'; // vue錯誤上報接口
TraceKit.report.subscribe((error) => {
  const { message, stack } = error || {};

  const obj = {
    message,
    stack: {
      column: stack[0].column,
      line: stack[0].line,
      func: stack[0].func,
      url: stack[0].url
    }
  };

  axios({
    method: 'POST',
    url: `${errorMonitorUrl}/${errorMonitorVueInterFace}`,
    data: {
      error: obj,
      data: {
        errTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
        isMobile: /iPhone|iPad|iPod|Android/i.test(navigator.userAgent), // 是否移動端
        isWechat: /MicroMessenger/i.test(navigator.userAgent), // 是否微信瀏覽器
        isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream, // 兩個都是false就是未知設(shè)備
        isAndroid: /Android/.test(navigator.userAgent) && !/Windows Phone/.test(navigator.userAgent)
      },
      browserInfo: {
        userAgent: navigator.userAgent,
        protcol: protcol
      }
    }
  }).then(() => {
    console.log('錯誤上報成功');
  }).catch(() => {
    console.log('錯誤上報失敗');
  });
});

Vue.config.errorHandler = (err, vm, info) => {
  TraceKit.report(err);
}

如果你還需要其他的數(shù)據(jù)就自己加

打包vue之后然后通過端口訪問index.html,不建議你雙擊打開,如果你沒改過打包相關(guān)的東西,雙擊打開是不行的,可以通過vs code裝插件live server,然后將打包文件夾通過vs code打開

現(xiàn)在去項目中看看發(fā)出去的請求參數(shù)是什么

可以看到我們需要的數(shù)據(jù)都已經(jīng)收集到了,上報失敗是肯定的,因為我們還沒有寫好接口

上報window錯誤

接下來在監(jiān)聽window的error事件,也向后臺發(fā)送一個錯誤上報請求

const errorMonitorWindowInterFace = 'reportWindowError'; // window錯誤上報接口
window.addEventListener('error', args => {
  const err = args.target.src || args.target.href;
  const obj = {
    message: '加載異常' + err
  };
  if (!err) {
    return true;
  }
  axios({
    method: 'POST',
    url: `${errorMonitorUrl}/${errorMonitorWindowInterFace}`,
    data: {
      error: obj,
      data: {
        errTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
        isMobile: /iPhone|iPad|iPod|Android/i.test(navigator.userAgent), // 是否移動端
        isWechat: /MicroMessenger/i.test(navigator.userAgent), // 是否微信瀏覽器
        isIOS: /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream, // 兩個都是false就是未知設(shè)備
        isAndroid: /Android/.test(navigator.userAgent) && !/Windows Phone/.test(navigator.userAgent)
      },
      browserInfo: {
        userAgent: navigator.userAgent,
        protcol: protcol
      }
    }
  }).then(() => {
    console.log('錯誤上報成功');
  }).catch(() => {
    console.log('錯誤上報失敗');
  });
  return true;
}, true);

搭建監(jiān)控后臺

創(chuàng)建一個文件夾,名字隨便,然后在終端中打開文件夾,初始化npm

npm init -y

初始化完成后創(chuàng)建一個server.js,這里我使用express進(jìn)行搭建后端,source-map用于解析js.map文件,這些庫后面會用到

npm i express
npm i nodemon
npm i source-map

下好包之后在server.js中輸入以下代碼,然后在終端輸入nodemon server.js

const express = require('express');
const path = require('path');
const fs = require('fs');

const PORT = 9999;

const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());

app.get('/', (req, res) => {
  res.send('Hello World!').status(200);
})

app.listen(PORT, () => {
  console.log(`服務(wù)啟動成功,端口號為:${PORT}`)
})

服務(wù)啟動之后,訪問本地的9999端口,查看是否生效,當(dāng)看到屏幕上顯示Hello World!表示我們的后端服務(wù)成功跑起來了,接下來就是寫錯誤的上傳接口

在這里我將為Vue和Window監(jiān)控分別寫一個接口(因為我懶得一個接口做判斷區(qū)分,如果你覺得兩個接口太麻煩,那你也可以自己優(yōu)化成一個接口)

編寫Vue錯誤上報接口

server.js中繼續(xù)添加

const SourceMap = require('source-map');

app.post('/reportVueError',async (req, res) => {
  const urlParams = req.body;
  console.log(`收到Vue錯誤報告`);
  console.log('urlParams', urlParams);

  const stack = urlParams.error.stack;
  // 獲取文件名
  const fileName = path.basename(stack.url);
  // 查找map文件
  const filePath = path.join(__dirname, 'uploads', fileName + '.map');
  const readFile = function (filePath) {
    return new Promise((resolve, reject) => {
      fs.readFile(filePath, { encoding: 'utf-8'}, (err, data) => {
        if (err) {
          console.log('readFileErr', err)
          return reject(err);
        }
        resolve(JSON.parse(data));
      })
    })
  }

  async function searchSource({ filePath, line, column }) {
    const rawSourceMap = await readFile(filePath);
    const consumer = await new SourceMap.SourceMapConsumer(rawSourceMap);
    const res = consumer.originalPositionFor({ line, column })

    consumer.destroy();
    return res;
  }

  let sourceMapParseResult = '';
  try {
    // 解析sourceMap結(jié)果
    sourceMapParseResult = await searchSource({ filePath, line: stack.line, column: stack.column });
  } catch (err) {
    sourceMapParseResult = err;
  }
  console.log('解析結(jié)果', sourceMapParseResult)
  res.send({
    data: '錯誤上報成功',
    status: 200,
  }).status(200);
})

然后nodemon會自動重啟服務(wù),如果你不是用nodemon啟動的,那自己手動重啟一下

打包vue之后然后通過端口訪問index.html,不建議你雙擊打開,如果你沒改過打包相關(guān)的東西,雙擊打開是不行的,可以通過vs code裝插件live server,然后將打包文件夾通過vs code打開,通過live server運行,此時應(yīng)該會報跨域問題

設(shè)置允許跨域

可以自己手動設(shè)置響應(yīng)頭實現(xiàn)跨域,我這里選擇使用cors庫

npm i cors
const cors = require('cors');
app.use(cors()); // 這條需要放在 const app = express(); 后

此時重新運行后臺,再觀察

此時發(fā)現(xiàn),解析map文件報錯了,那是因為我們還沒有上傳map文件

server.js同級目錄下創(chuàng)建一個uploads文件夾

回到打包vue打包文件目錄dist,將js文件夾中所有js.map結(jié)尾的文件剪切到創(chuàng)建的文件夾中,如果你打包文件中沒有js.map,那是因為你沒有打開生成js.map的開關(guān),打開vue.config.js,在defineConfig中設(shè)置屬性productionSourceMaptrue,然后重新打包就可以了

module.exports = defineConfig({
  productionSourceMap: true, // 設(shè)置為true,然后重新打包
  transpileDependencies: true,
  lintOnSave: false,
  configureWebpack: {
    devServer: {
      client: false
    }
  }
})

為什么是剪切?如果真正的項目上線時,你把js.map文件上傳了,別人拿到之后是可以知道你的源碼的,所以必須剪切,或者復(fù)制之后回到dist目錄刪掉所有js.map

這時候我們再刷新網(wǎng)頁,然后看后臺的輸出,顯示src/App.vue的第10行有錯

編寫window錯誤上傳接口

// 處理Window報錯
app.post('/reportWindowError',async (req, res) => {
  const urlParams = req.body;
  console.log(`收到Window錯誤報告`);
  console.log('urlParams', urlParams);

  res.send({
    data: '錯誤上報成功',
    status: 200,
  }).status(200);
})

此時我們?nèi)ue項目中添加一個img標(biāo)簽,獲取一張不存在的圖片即可出發(fā)錯誤,由于不用解析,所以這里就不再上傳js.map

寫入日志

錯誤上報之后我們需要記錄下來,接下來我們改造一下接口,收到報錯之后寫一下日志

我需要知道哪一天的日志報錯了,所有我在node項目中也下載dayjs用來格式化時間.

npm i dayjs

此處的日志記錄內(nèi)容只是我自己需要的格式,如果你需要其他格式請自己另外添加

vue錯誤寫入日志

// let sourceMapParseResult = '';
// try {
//  // 解析sourceMap結(jié)果
//  sourceMapParseResult = await searchSource({ filePath, line: stack.line, column: //stack.column });
//} catch (err) {
//  sourceMapParseResult = err;
//}
//console.log('解析結(jié)果', sourceMapParseResult)

// 直接將下面的內(nèi)容粘貼在上面的log下面

const today = dayjs().format('YYYY-MM-DD') // 今天

const logDirPath = path.join(__dirname, 'log');
const logFilePath = path.resolve(__dirname, 'log/' + `log-${today}.txt`)

if (!fs.existsSync(logDirPath)) {
  console.log(`創(chuàng)建log文件夾`)
  fs.mkdirSync(logDirPath, { recursive: true });
}
if (!fs.existsSync(logFilePath)) {
  console.log(`創(chuàng)建${today}日志文件`)
  fs.writeFileSync(logFilePath, '', 'utf8');
}

const writeStream = fs.createWriteStream(logFilePath, { flags: 'a' });
writeStream.on('open', () => {
  // writeStream.write('UUID:' + urlParams.data.uuid + '\n');
  writeStream.write('錯誤類型:Window' + '\n');
  writeStream.write('錯誤發(fā)生時間:' + urlParams.data.errTime + '\n');
  writeStream.write('IP:' + req.ip + '\n');
  writeStream.write(`安卓: ${urlParams.data.isAndroid} IOS: ${urlParams.data.isIOS} 移動端: ${urlParams.data.isMobile} 微信: ${urlParams.data.isWechat} (安卓和ios同時為false表示未知設(shè)備)` + '\n');
  writeStream.write('用戶代理:' + urlParams.browserInfo.userAgent + '\n');
  writeStream.write('錯誤信息:' + urlParams.error.message + '\n');
  writeStream.write('---------------------------------- \n');

  writeStream.end(() => {
    console.log('vue錯誤日志寫入成功');
    console.log('---------------------');
    res.send({
      data: '錯誤上報成功',
      status: 200,
    }).status(200);
  });
})

writeStream.on('error', err => {
  res.send({
    data: '錯誤上報失敗',
    status: 404,
  }).status(404);
  console.error('發(fā)生錯誤:', err);
})

window錯誤寫入日志

和vue寫入的方式差不多,存在優(yōu)化空間

const today = dayjs().format('YYYY-MM-DD') // 今天

const logDirPath = path.join(__dirname, 'log');
const logFilePath = path.join(__dirname, 'log' + `/log-${today}.txt`)

if (!fs.existsSync(logDirPath)) {
  console.log(`創(chuàng)建log文件夾`)
  fs.mkdirSync(logDirPath, { recursive: true });
}
if (!fs.existsSync(logFilePath)) {
  console.log(`創(chuàng)建${today}日志文件`)
  fs.writeFileSync(logFilePath, '', 'utf8');
}

const writeStream = fs.createWriteStream(logFilePath, { flags: 'a' });
writeStream.on('open', () => {
  writeStream.write('錯誤類型:Window' + '\n');
  writeStream.write('錯誤發(fā)生時間:' + urlParams.data.errTime + '\n');
  writeStream.write('IP:' + req.ip + '\n');
  writeStream.write(`安卓: ${urlParams.data.isAndroid} IOS: ${urlParams.data.isIOS} 移動端: ${urlParams.data.isMobile} 微信: ${urlParams.data.isWechat} (安卓和ios同時為false表示未知設(shè)備)` + '\n');
  writeStream.write('用戶代理:' + urlParams.browserInfo.userAgent + '\n');
  writeStream.write('錯誤信息:' + urlParams.error.message + '\n');
  writeStream.write('---------------------------------- \n');

  writeStream.end(() => {
    console.log('window錯誤日志寫入成功');
    console.log('---------------------');
    res.send({
      data: '錯誤上報成功',
      status: 200,
    }).status(200);
  });
})

writeStream.on('error', err => {
  res.send({
    data: '錯誤上報失敗',
    status: 404,
  }).status(404);
  console.error('發(fā)生錯誤:', err);
})

至此,收集錯誤,上報錯誤,寫入日志已經(jīng)全部完成。

其他

錯誤監(jiān)控持久化運行在服務(wù)器

這個可以使用pm2,在服務(wù)器上使用node全局安裝pm2

pm2 ls #顯示所有pm2啟動的應(yīng)用
pm2 start /xxx/xxx # 啟動/xxx/xxx應(yīng)用
pm2 save # 保存當(dāng)前應(yīng)用列表
pm2 stop id # id 通過pm2 ls查看
pm2 logs id # 查看日志

自動上傳js.map文件

如果每次打包后都手動復(fù)制js.map文件的到uploads文件夾下,似乎有些麻煩

雖然麻煩,但是我自己還是沒有自動上傳,原因是如果打包就自動上傳,那么如果項目還未發(fā)布,但是文件已經(jīng)替換掉之前的文件了,新版本未發(fā)布之前,vue的錯誤就無法解析了,當(dāng)然,如果你每次上傳都不刪除以前的文件也是可以的

修改vue項目

在vue項目src下創(chuàng)建一個plugin目錄,新建一個UploadSourceMap.js,將下面的代碼粘貼進(jìn)去

const glob = require('glob')
const path = require('path')
const http = require('http')
const fs = require('fs')

class UploadSourceMap {
  constructor (options) {
    this.options = options
  }

  apply (compiler) {
    console.log('UploadSourceMap')

    // 在打包完成后運行
    compiler.hooks.done.tap('UploadSourceMap', async stats => {
      const list = glob.sync(path.join(stats.compilation.outputOptions.path, '**/*.js.map'))
      for (const item of list) {
        const fileName = path.basename(item);
        console.log(`開始上傳${fileName}`)
        await this.upload(this.options.url, item)
        console.log(`上傳${fileName}完成`)
      }
    })
  }

  upload (url, file) {
    return new Promise((resolve, reject) => {
      const req = http.request(
        `${url}/upload?name=${path.basename(file)}`,
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/octet-stream',
            Connection: 'keep-alive',
            'Transfer-Encoding': 'chunked'
          }
        }
      )
      fs.createReadStream(file)
        .on('data', chunk => {
          req.write(chunk)
        })
        .on('end', () => {
          req.end()
          // 刪除文件
          fs.unlink(file, (err) => {
            if (err) {
              console.error(err)
            }
          })
          resolve()
        })
    })
  }
}

module.exports = UploadSourceMap

修改vue.config.js

主要是引入UploadSourceMap,并且在configureWebpack => plugins下使用

const { defineConfig } = require('@vue/cli-service')
const UploadSourceMap = require('./src/plugin/UploadSourceMap')

module.exports = defineConfig({
  productionSourceMap: true,
  transpileDependencies: true,
  lintOnSave: false,
  configureWebpack: {
    plugins: [
      new UploadSourceMap({
        url: 'http://127.0.0.1:9999' // 后面換成自己的服務(wù)器地址
      })
    ]
  }
})

修改后臺

修改server.js,新增一個上傳文件的接口

app.post('/upload', (req, res) => {
  const fileName = req.query.name
  const filePath = path.join(__dirname, 'uploads', fileName)

  if (!fs.existsSync(path.dirname(filePath))) {
    fs.mkdirSync(path.dirname(filePath), { recursive: true })
  }

  const writeStream = fs.createWriteStream(filePath)

  req.on('data', (chunk) => {
    writeStream.write(chunk)
  })

  req.on('end', () => {
    writeStream.end(() => {
      res.status(200).send(`File ${fileName} has been saved.`)
    })
  })

  writeStream.on('error', (err) => {
    fs.unlink(filePath, () => {
      console.error(`Error writing file ${fileName}: ${err}`)
      // res.status(500).send(`Error writing file ${fileName}.`)
    })
  })
})

然后現(xiàn)在重新打包,觀察打包輸出

最后

盡量是不要開啟跨域,否則誰都能給發(fā)請求到后臺,如果要開跨域,那需要做好判斷,主域名不符合的直接返回404終止這次請求。

市面上的監(jiān)控有很多,有些甚至能實現(xiàn)錄制用戶操作生成gif,本文只是實現(xiàn)一個基本的錯誤監(jiān)控,如有錯誤請指出。

源碼參考:https://github.com/ytanck/demos/tree/master/error-monitor-demo

相關(guān)文章

  • js實現(xiàn)的簡單radio背景顏色選擇器代碼

    js實現(xiàn)的簡單radio背景顏色選擇器代碼

    這篇文章主要介紹了js實現(xiàn)的簡單radio背景顏色選擇器代碼,利用鼠標(biāo)事件及頁面元素動態(tài)操作實現(xiàn)頁面背景顏色的改變功能,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-08-08
  • JavaScript中常用的動畫事件使用方法

    JavaScript中常用的動畫事件使用方法

    JavaScript在處理動畫方面極富靈活性,提供了一系列事件,使得開發(fā)者能夠更精準(zhǔn)地控制動畫執(zhí)行的各個階段,使用這些事件,可以在動畫開始、結(jié)束、重復(fù)等動作發(fā)生時執(zhí)行特定的行為,本文將詳細(xì)介紹JavaScript中常用的動畫事件,并展示其使用方法
    2023-12-12
  • 理解javascript中Map代替循環(huán)

    理解javascript中Map代替循環(huán)

    這篇文章主要幫助大家理解javascript中Map代替循環(huán),感興趣的小伙伴們可以參考一下
    2016-02-02
  • 在VSCode中進(jìn)行JavaScript調(diào)試的詳細(xì)流程

    在VSCode中進(jìn)行JavaScript調(diào)試的詳細(xì)流程

    在JavaScript開發(fā)中,調(diào)試是一個關(guān)鍵的過程,它幫助我們理解和修復(fù)代碼中的問題,Visual Studio Code(VSCode)以其豐富的擴展和內(nèi)置調(diào)試工具,為JavaScript開發(fā)者提供了強大的支持,本文將詳細(xì)介紹如何在VSCode中進(jìn)行JavaScript調(diào),需要的朋友可以參考下
    2024-07-07
  • JavaScript 總結(jié)幾個提高性能知識點(推薦)

    JavaScript 總結(jié)幾個提高性能知識點(推薦)

    下面小編就為大家?guī)硪黄狫avaScript 總結(jié)幾個提高性能知識點(推薦)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-02-02
  • 微信小程序?qū)崿F(xiàn)時間選擇

    微信小程序?qū)崿F(xiàn)時間選擇

    這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)時間選擇,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • javaScript函數(shù)中執(zhí)行C#代碼中的函數(shù)方法總結(jié)

    javaScript函數(shù)中執(zhí)行C#代碼中的函數(shù)方法總結(jié)

    這篇文章介紹了javaScript函數(shù)中執(zhí)行C#代碼中的函數(shù)方法總結(jié),有需要的朋友可以參考一下
    2013-08-08
  • vscode使用Prettier Code插件的詳細(xì)教程

    vscode使用Prettier Code插件的詳細(xì)教程

    這篇文章主要介紹了vscode使用Prettier Code插件的詳細(xì)教程,本文通過圖文實例相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • 關(guān)于JS中的apply,call,bind的深入解析

    關(guān)于JS中的apply,call,bind的深入解析

    下面小編就為大家?guī)硪黄P(guān)于JS中的apply,call,bind的深入解析。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-04-04
  • 對于js垃圾回收機制的理解

    對于js垃圾回收機制的理解

    下面小編就為大家?guī)硪黄獙τ趈s垃圾回收機制的理解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09

最新評論