聊一聊我們前端項(xiàng)目組是如何優(yōu)化項(xiàng)目的

-
方法一:刪除需要預(yù)先加載和預(yù)先獲取的資源,一般使用這種方法
link標(biāo)簽中的
preload和prefetch:
-
preload插件用于預(yù)加載資源。 即在當(dāng)前頁(yè)面加載完成后,立即加載并緩存指定的資源。預(yù)加載的資源被認(rèn)為是當(dāng)前頁(yè)面所需的關(guān)鍵資源,因此會(huì)優(yōu)先下載和緩存。 -
prefetch插件用于預(yù)獲取資源。 即在當(dāng)前頁(yè)面加載完成后,異步地加載并緩存指定的資源,以供將來(lái)可能需要的頁(yè)面使用。預(yù)獲取的資源被認(rèn)為是可能會(huì)在未來(lái)的導(dǎo)航中使用的資源,但不是當(dāng)前頁(yè)面所必需的。
`vue.config.js`
module.exports = {
chainWebpack: (config) => {
`刪除需要預(yù)先加載(當(dāng)前頁(yè)面)的資源,當(dāng)需要這些資源的時(shí)候,頁(yè)面會(huì)自動(dòng)加載`
config.plugins.delete('preload')
`刪除需要預(yù)先獲取(將來(lái)的頁(yè)面)的資源`
config.plugins.delete('prefetch')
}
}
-
方法二:使用
webpack合并小文件合并
js代碼的意義:
- 減少網(wǎng)絡(luò)請(qǐng)求: 每個(gè)文件都需要通過(guò)網(wǎng)絡(luò)進(jìn)行單獨(dú)的請(qǐng)求和響應(yīng)。通過(guò)將多個(gè)文件合并為一個(gè)文件,可以減少頁(yè)面需要發(fā)起的網(wǎng)絡(luò)請(qǐng)求次數(shù),從而降低延遲和提高加載速度。
- 緩存優(yōu)化: 合并代碼可以提高瀏覽器緩存的效率。當(dāng)多個(gè)頁(yè)面共享同一個(gè)文件時(shí),瀏覽器只需要下載并緩存一次該文件,而不是針對(duì)每個(gè)頁(yè)面都下載一次。這樣可以減少整體的重復(fù)下載和提高緩存命中率。
- 減少頁(yè)面渲染阻塞: 當(dāng)瀏覽器下載和執(zhí)行
js代碼時(shí),它會(huì)阻塞頁(yè)面的渲染過(guò)程。通過(guò)合并js代碼,可以減少因?yàn)槎鄠€(gè)js文件的下載和執(zhí)行而造成的頁(yè)面渲染阻塞時(shí)間,提高頁(yè)面的響應(yīng)速度和用戶體驗(yàn)。 - 代碼優(yōu)化和壓縮: 在合并
js代碼之前,可以對(duì)代碼進(jìn)行優(yōu)化和壓縮,去除空格、注釋和不必要的代碼,從而減少文件大小,并提高代碼的執(zhí)行效率。
`vue.config.js: `
const webpack = require('webpack')
const ENV = process.env.NODE_ENV
module.exports = {
chainWebpack: (config) => {
config.when(ENV === 'production', config => {
config.plugin('webpackOptimize')
.use(
webpack.optimize.LimitChunkCountPlugin,
`限制生成的代碼塊(chunks)的數(shù)量`
[{ maxChunks: 10 }]
)
.use(
webpack.optimize.MinChunkSizePlugin,
`指定代碼塊的最小字節(jié)數(shù)`
[{ minChunkSize: 50000 }]
)
})
}
}
- 優(yōu)化效果截圖
優(yōu)化前的本地環(huán)境:
image.png
優(yōu)化后的本地環(huán)境(app.js文件較大,是由于main.js引入了大量的第三方庫(kù)): 
優(yōu)化后的線上環(huán)境(首先,打包后有對(duì)這些文件進(jìn)行壓縮處理,app.js被壓縮至1.24MB。"inspect": "vue inspect > output.js",使用npm run inspect可以查看webPack的配置信息。其次,運(yùn)維都有對(duì)這些文件作gzip壓縮處理, 所以體積都減小了很多, 最終app.js的體積減小至405KB): 
image.png
2. 加減乘除運(yùn)算集成`big.js`[1],解決js小數(shù)精度問(wèn)題
- 前提:
當(dāng)涉及到浮點(diǎn)數(shù)計(jì)算時(shí),js中的精度丟失問(wèn)題, 是由于使用IEEE 754標(biāo)準(zhǔn)來(lái)表示和計(jì)算浮點(diǎn)數(shù)的方式引起的。這個(gè)問(wèn)題不僅僅在js中存在,而是在所有使用IEEE 754標(biāo)準(zhǔn)的編程語(yǔ)言中都會(huì)遇到。
IEEE 754標(biāo)準(zhǔn)定義了兩種常見(jiàn)的浮點(diǎn)數(shù)表示形式:?jiǎn)尉龋?code style="font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;font-size:14px;color:rgb(30,107,184);background-color:rgba(27,31,35,.05);">32位)和雙精度(64位)。在 js中,采用的是雙精度表示法,即64位。
然而,由于二進(jìn)制和十進(jìn)制之間的轉(zhuǎn)換存在差異,某些十進(jìn)制分?jǐn)?shù)無(wú)法精確表示為有限位的二進(jìn)制浮點(diǎn)數(shù)。這導(dǎo)致了舍入誤差和精度丟失。
- 安裝依賴:
npm install --save big.js - 方法封裝:
import Big from 'big.js'
export function accFactory(method = 'add') {
return function (...nums) {
`將傳入的參數(shù)轉(zhuǎn)換為Number類型,并過(guò)濾掉不是Number類型的結(jié)果`
nums = nums.map(Number).filter((num) => num || num === 0)
`如果過(guò)濾后的結(jié)果是長(zhǎng)度為1的數(shù)組,那就返回?cái)?shù)組的第一項(xiàng)`
`如果過(guò)濾后的結(jié)果為空數(shù)組,則返回0`
if (nums.length < 2) return nums[0] || 0
`需要為reduce方法賦初值,是因?yàn)閎ig.js的運(yùn)算操作,是基于new Big格式的數(shù)字`
`可以將Big對(duì)象轉(zhuǎn)換為浮點(diǎn)數(shù),方便后續(xù)Number.toFixed()的操作`
return parseFloat(nums.slice(1).reduce((prev, num) => prev[method](num), new Big(nums[0]))) || 0
}
}
`plus、minus、times、div為big.js中的計(jì)算方法,分別對(duì)應(yīng)加減乘除這四個(gè)運(yùn)算操作`
`浮點(diǎn)數(shù)求和`
export const accAdd = accFactory('plus')
`浮點(diǎn)數(shù)相減`
export const accSub = accFactory('minus')
`浮點(diǎn)數(shù)相乘`
export const accMul = accFactory('times')
`浮點(diǎn)數(shù)相除`
export const accDiv = accFactory('div')
- 測(cè)試:
import { accAdd, accSub, accMul, accDiv } from '@/utils/calculate'
mounted() {
this.calTestHandler()
},
methods: {
calTestHandler() {
const operations = [
{ operator: '+', method: accAdd, a: 0.1, b: 0.2 },
{ operator: '-', method: accSub, a: 0.1, b: 0.3 },
{ operator: '*', method: accMul, a: 0.1, b: 0.2 },
{ operator: '/', method: accDiv, a: 0.1, b: 0.3 }
]
operations.forEach((operation) => {
const { operator, method, a, b } = operation
const result = method(a, b)
console.log(`原生js ${operator} 運(yùn)算:${a} ${operator} ${b}的值是${eval(a + operator + b)}`)
console.log(`big.js ${operator} 運(yùn)算:${a} ${operator} ${b}的值是${result}`)
})
}
}
結(jié)果展示:
image.png
3. 使用`bluebird`[2]提升promise的執(zhí)行速度
- 前提:
bluebird是一個(gè)流行的Promise庫(kù),用于處理異步操作。它提供了強(qiáng)大的異步編程工具,使得編寫和管理異步代碼變得更加簡(jiǎn)單和可靠。
-
Promise功能增強(qiáng):bluebird提供了許多額外的功能和操作,超出了原生Promise的范圍。它支持超時(shí)控制、并發(fā)控制、錯(cuò)誤處理、重試、進(jìn)度報(bào)告和取消等功能。這些功能使得處理復(fù)雜的異步控制流變得更加容易。 - 性能優(yōu)化:
bluebird在性能方面進(jìn)行了優(yōu)化,比原生Promise更快。 它實(shí)現(xiàn)了高效的異步調(diào)度和內(nèi)存管理,以提供更快的執(zhí)行速度和更低的資源消耗。這使得在大規(guī)模異步操作的情況下,bluebird可以提供更高效的性能。 - 錯(cuò)誤追蹤和調(diào)試:
bluebird提供了更好的錯(cuò)誤追蹤和調(diào)試支持。 當(dāng)使用bluebird進(jìn)行異步操作時(shí),它會(huì)生成詳細(xì)的錯(cuò)誤堆棧跟蹤信息,包括異步操作鏈的每個(gè)步驟。這使得在調(diào)試和排查錯(cuò)誤時(shí)更容易定位問(wèn)題所在。 - 可互操作性:
bluebird的api與原生Promise相似,因此可以與其他使用Promise的庫(kù)和代碼進(jìn)行互操作。 這使得在現(xiàn)有的代碼基礎(chǔ)上,遷移到bluebird更加容易,并且可以充分利用bluebird提供的額外功能。
- 安裝依賴:
npm install --save bluebird - 方法封裝(全局掛載):
import Promise from 'bluebird'
Promise.config({
`確定是否啟用警告輸出。當(dāng)設(shè)置為true,bluebird會(huì)在控制臺(tái)輸出警告,例如不推薦使用的方法或潛在問(wèn)題`
warnings: false,
`確定是否啟用長(zhǎng)堆棧跟蹤, bluebird會(huì)生成詳細(xì)的異步調(diào)用堆棧信息,包括Promise鏈中的每個(gè)步驟。`
`這對(duì)于調(diào)試和錯(cuò)誤追蹤非常有用, 但啟用長(zhǎng)堆棧跟蹤,可能會(huì)對(duì)性能產(chǎn)生一些影響`
longStackTraces: false,
`確定是否啟用取消功能。當(dāng)設(shè)置為true時(shí),bluebird允許取消異步操作。`
`取消一個(gè)Promise將導(dǎo)致其相關(guān)的操作被中斷或忽略,有助于優(yōu)化資源使用和控制異步流程。`
cancellation: true,
`確定是否啟用性能監(jiān)視。當(dāng)設(shè)置為true時(shí),bluebird可以收集異步操作的性能數(shù)據(jù),例如執(zhí)行時(shí)間、調(diào)用次數(shù)等。`
`這對(duì)于分析和優(yōu)化異步操作的性能非常有用。`
monitoring: true,
`確定是否啟用異步掛鉤。異步掛鉤是node.js提供的一種機(jī)制,可以在異步操作的不同階段執(zhí)行回調(diào)函數(shù)。`
`當(dāng)設(shè)置為true時(shí),bluebird將使用異步掛鉤來(lái)跟蹤和管理異步操作`
asyncHooks: false
})
window.bluePromise = Promise
`main.js`
import bluebird from '@/utils/bluebird'
- 測(cè)試
mounted() {
this.proTestHandler()
},
methods: {
async proTestHandler() {
const promises = []
const bluebirds = []
const promiseList = (promise, count, arr) => {
for (let i = 0; i < count; i++) {
arr.push(new promise((resolve) => resolve(i)))
}
}
const generatePromises = () => {
promiseList(Promise, 1000000, promises)
promiseList(bluePromise, 1000000, bluebirds)
}
generatePromises()
console.log('promise')
console.time()
await Promise.all(promises)
console.timeEnd()
console.log('bluebirds')
console.time()
await bluePromise.all(bluebirds)
console.timeEnd()
}
}
結(jié)果展示
image.png
4. 使用`hashids`[3]加密路由id
- 前提:
在網(wǎng)址上應(yīng)用hashids有以下4點(diǎn)重要意義:
- 加密隱藏真實(shí)id: 在某些情況下,你可能希望隱藏網(wǎng)址中的真實(shí)
id,以增加安全性和防止直接暴露敏感信息。使用hashids,可以將真實(shí)的數(shù)字id轉(zhuǎn)換為短字符串,并在網(wǎng)址中使用該短字符串代替原始id。這樣,外部用戶只能看到短字符串,而無(wú)法直接推斷出真實(shí)的id值。 - 可讀性和美觀性: 長(zhǎng)的數(shù)字
id在網(wǎng)址中可能顯得冗長(zhǎng)和難以理解。使用hashids將其轉(zhuǎn)換為短字符串,可以大大提升網(wǎng)址的可讀性和美觀性。短字符串通常包含字母和數(shù)字的組合,更易于記憶和分享。 - 防止猜測(cè)和遍歷: 使用連續(xù)的數(shù)字
id在網(wǎng)址中可能導(dǎo)致猜測(cè)和遍歷的風(fēng)險(xiǎn),因?yàn)楣粽呖梢酝ㄟ^(guò)遞增id來(lái)嘗試訪問(wèn)和暴露數(shù)據(jù)。通過(guò)使用hashids生成的短字符串作為id,可以有效地防止這種攻擊。由于短字符串是隨機(jī)生成的,攻擊者無(wú)法根據(jù)短字符串推斷出下一個(gè)id。 - URL縮短和分享:
hashids生成的短字符串可以用作url縮短服務(wù)的替代方案。你可以將長(zhǎng)的url轉(zhuǎn)換為短字符串,并在分享時(shí)使用該短字符串。這對(duì)于限制字符數(shù)、簡(jiǎn)化鏈接以及在社交媒體和短信中共享鏈接都非常有用。
- 安裝依賴:
npm install --save hashids - 方法封裝(全局掛載):
`短碼方法封裝:`
import Hashids from 'hashids'
const hashids = new Hashids(
`鹽值是一個(gè)可選的字符串參數(shù),用于增加生成的短碼的唯一性和安全性。每個(gè)不同的鹽值將產(chǎn)生不同的短碼序列,`
`可以將鹽值視為項(xiàng)目的名稱或標(biāo)識(shí)符。如果不提供鹽值,則默認(rèn)為一個(gè)空字符串。`
'toadditWeb',
`是一個(gè)可選的整數(shù)參數(shù),用于指定生成的短碼的最小長(zhǎng)度。如果生成的短碼長(zhǎng)度小于指定的最小長(zhǎng)度,`
`hashids會(huì)自動(dòng)填充短碼以達(dá)到最小長(zhǎng)度。這只是一個(gè)最小長(zhǎng)度的限制,實(shí)際生成的短碼長(zhǎng)度可能更長(zhǎng)。`
`如果不提供最小長(zhǎng)度,則默認(rèn)為0,即沒(méi)有最小長(zhǎng)度限制。`
8,
`字母表是一個(gè)可選的字符串參數(shù),用于定義生成短碼時(shí)使用的字符集。該字符串包含所有可用于生成短碼的字符。`
`通常,字母表中應(yīng)包含一組不容易混淆的字符,以避免生成的短碼產(chǎn)生歧義。如果不提供字母表,則默認(rèn)為`
`"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"`。
)
`短碼封裝`
export function encode(val) {
return hashids.encode(val)
}
`短碼解析`
export function decode(val) {
return hashids.decode(val)[0]
}
`glboal.js: 全局注冊(cè)`
import { encode, decode } from '@/utils/hashids'
export default {
install(Vue) {
Vue.prototype.$encode = window.$encode = (data) => encode(data)
Vue.prototype.$decode = window.$decode = (data) => decode(data)
}
}
`在main.js中掛載:`
import Vue from 'vue'
import App from './App'
import globalConst from '@/commons/globalConst'
Vue.use(globalConst)
new Vue({
el: '#app',
router,
store,
render: (h) => h(App)
})
- 測(cè)試:
mounted() {
this.hashTestHandler()
},
methods: {
hashTestHandler() {
const testId = 18
const encode = $encode(testId)
console.log(`hashids編碼前: ${testId}`)
console.log(`hashids編碼: ${encode}`)
console.log(`hashids解碼: ${$decode(encode)}`)
}
}
結(jié)果展示(在同一個(gè)鹽值下,不管頁(yè)面是否刷新,編碼結(jié)果都不會(huì)改變):
image.png
5. 登陸時(shí)使用`行為驗(yàn)證碼`[4]
- 前提:
登陸時(shí)使用行為驗(yàn)證碼有以下5點(diǎn)重要意義:
- 增強(qiáng)安全性: 傳統(tǒng)的驗(yàn)證碼可以被自動(dòng)化的機(jī)器人或惡意程序輕易地破解或繞過(guò)。而
行為驗(yàn)證碼通過(guò)分析用戶的行為模式,可以更準(zhǔn)確地識(shí)別是否是真實(shí)用戶,從而提高安全性,防止惡意活動(dòng)和機(jī)器人攻擊。 - 用戶友好性: 相比于傳統(tǒng)的驗(yàn)證碼,
行為驗(yàn)證碼通常對(duì)用戶來(lái)說(shuō)更加友好和便捷。用戶無(wú)需輸入復(fù)雜的文本或解析模糊的圖像,而是通過(guò)正常的交互行為完成驗(yàn)證,例如簡(jiǎn)單的滑動(dòng)、點(diǎn)擊、拖拽等操作。 - 無(wú)感知驗(yàn)證:
行為驗(yàn)證碼可以在用戶進(jìn)行正常的操作過(guò)程中進(jìn)行驗(yàn)證,幾乎無(wú)需用戶額外的干預(yù)或注意。這樣可以減少對(duì)用戶的干擾和阻礙,提升用戶體驗(yàn)。 - 自適應(yīng)性:
行為驗(yàn)證碼可以根據(jù)用戶的行為模式自適應(yīng)地進(jìn)行驗(yàn)證。它可以根據(jù)用戶的設(shè)備、IP地址、瀏覽器指紋、鼠標(biāo)移動(dòng)軌跡等因素來(lái)綜合評(píng)估用戶的真實(shí)性,從而提高準(zhǔn)確性和安全性。 - 防止數(shù)據(jù)濫用:
行為驗(yàn)證碼可以用于防止惡意用戶或攻擊者濫用系統(tǒng)或服務(wù)。通過(guò)分析用戶的行為模式和交互方式,可以及時(shí)識(shí)別和阻止異常行為,保護(hù)系統(tǒng)和數(shù)據(jù)的安全。
前端參考文檔:https://github.com/Yunlingfly/vue-captcha/tree/master
最終效果:
image.png
粉絲福利作者:沐浴在曙光下的貳貨道士
鏈接:https://juejin.cn/post/7264440609129119804
來(lái)源:稀土掘金
分享一個(gè)現(xiàn)代感十足的博客源碼(Headless blog),當(dāng)下流行的無(wú)頭博客方案,這個(gè)源碼使用了React、GatsbyJS v5 技術(shù),并擁有九個(gè)不同類型的博客風(fēng)格。此博客不僅支持本地Markdown文件,還支持Contentful CMS、Strapi CMS、Netlify CMS和Sanity CMS等內(nèi)容管理系統(tǒng),讓你能夠自由創(chuàng)作博客文章,喜歡的趕緊收下吧。
