项目工程化实施中涉及的流程及知识体系分类
| 项目工程化实施中涉及的 7 个 大致流程及知识体系 |
| |
| 1、技术选型 |
| |
| 2、统一规范 |
| |
| 3、测试 |
| |
| 4、部署 |
| |
| 5、监控 |
| |
| 6、性能优化 |
| |
| 7、重构 |
每一个 知识体系 说明
技术选型
| 三大框架中选一个 【 React Vue Angular 】 |
| |
| 可以依据以下两个特点来选 |
| |
| 1、选你或团队最熟的,保证在遇到棘手的问题时有人能填坑 |
| |
| 2、选市场占有率高的 |
| |
统一规范
| 1、代码规范 |
| |
| a、好处 |
| |
| 1、规范的代码可以促进团队合作 |
| |
| 2、规范的代码可以降低维护成本 |
| |
| 3、规范的代码有助于 code review(代码审查) |
| |
| 4、养成代码规范的习惯,有助于程序员自身的成长 |
| |
| b、如何制订代码规范 |
| |
| 找一份好的代码规范,在此基础上结合团队的需求作个性化修改。 |
| |
| 一些 star 较多的 js 代码规范如下 |
| |
| 1、airbnb (101k star 英文版),airbnb-中文版 【 javascript 】 |
| |
| 2、standard (24.5k star) 中文版 【 javascript 】 |
| |
| 3、百度前端编码规范 3.9k 【 javascript 】 |
| |
| 4、styleguide 2.3k 【 css 】 |
| |
| 5、spec 3.9k 【 css 】 |
| |
| c、如何检查代码规范 【 Eslint 】 |
| |
| 使用 eslint 可以检查代码符不符合团队制订的规范 |
| |
| 2、git 规范 |
| |
| 1、分支管理规范 |
| |
| 2、git commit 规范 |
| |
| 3、项目规范 |
| |
| 主要是项目文件的组织方式和命名方式 |
| |
| Vue 项目举例 |
| |
| ├─public 【 公共资源,不会被 webpack 处理 】 |
| ├─src 【 源码 】 |
| ├─api (接口) |
| ├─assets (静态资源) |
| ├─components (公共组件) |
| ├─styles (公共样式) |
| ├─router (路由) |
| ├─store (vuex 全局数据) |
| ├─utils (工具函数) |
| └─views (页面) |
| ├─test 【 测试代码 】 |
| |
| 文件名称如果过长则用 - 隔开 |
| |
| 4、UI 规范 |
| |
| UI 规范需要前端、UI、产品沟通,互相商量,最后制定下来,建议使用统一的 UI 组件库。 |
| |
| 1、统一页面 UI 标准,节省 UI 设计时间 |
| |
| 2、提高前端开发效率 |
测试
| 测试是前端工程化建设必不可少的一部分,它的作用就是找出 bug,越早发现 bug,所需要付出的成本就越低。并且,它更重要的作用是在将来,而不是当下。 |
| |
| 在前端用得最多的就是 单元测试 |
| |
| 1、单元测试 |
| |
| 定义 |
| |
| 单元测试 就是对一个函数、一个组件、一个类做的测试,它针对的粒度比较小 |
| |
| 用法 |
| |
| 1、根据正确性写测试,即正确的输入应该有正常的结果。 |
| |
| 2、根据异常写测试,即错误的输入应该是错误的结果。 |
| |
| 分类 |
| |
| 1、对一个函数做测试 |
| |
| 2、对一个类做测试 |
| |
| 3、对一个组件做测试 |
| |
| 组件测试比较难,因为很多组件都涉及了 DOM 操作 |
| |
| 2、TDD 测试驱动开发 |
| |
| TDD 就是根据需求提前把测试代码写好,然后根据测试代码实现功能 |
| |
| 3、测试框架推荐 |
| |
| jest 测试框架 |
| |
| Jest 是由 Facebook 开源出来的一个测试框架,它集成了断言库、mock、快照测试、覆盖率报告等功能。 |
| |
| 它非常适合用来测试 React 代码 |
| |
| 所有的 js 代码都可以使用 Jest 进行测试 |
部署
| 1、手动部署 |
| |
| 在没有自动部署前,部署项目通常是这样的 |
| |
| 1、执行测试 npm run test。 |
| |
| 2、构建项目 npm run build。 |
| |
| 3、将打包好的文件放到静态服务器 |
| |
| 2、自动化部署 |
| |
| 自动部署 又叫持续部署 Continuous Deployment,英文缩写 CD ,一般有两种触发方式 |
| |
| 1、轮询 |
| |
| 轮询,就是构建软件每隔一段时间自动执行打包、部署操作。 |
| |
| 这种方式不太好,很有可能软件刚部署完我就改代码了。为了看到新的页面效果,不得不等到下一次构建开始。 |
| |
| 另外还有一个副作用,假如我一天都没更改代码,构建软件还是会不停的执行打包、部署操作,白白的浪费资源。 |
| |
| 所以现在的构建软件基本采用监听 webhook 事件的方式来进行部署。 |
| |
| 2、监听 webhook 事件 |
| |
| webhook 钩子函数,就是在你的构建软件上进行设置,监听某一个事件(一般是监听 push 事件),当事件触发时,自动执行定义好的脚本 |
| |
| 例如 Github Actions,就有这个功能。 |
| |
| 3、工具 |
| |
| Gitea + Jenkins 可以实现自动构建前端项目并部署到服务器 |
| |
| 1、Gitea 用于构建 Git 局域网服务器 |
| |
| 2、Jenkins 是 CI/CD 工具,用于部署前端项目。 |
| |
监控
| 监控,又分 性能监控 和 错误监控,它的作用是预警和追踪定位问题。 |
| |
| 包含 |
| |
| 1、性能监控 |
| |
| 2、错误监控 |
| |
| 3、数据上报 |
性能监控
| 1、性能监控概述 |
| |
| 性能监控一般利用 window.performance 来进行数据采集 |
| |
| Performance 接口可以获取到当前页面中与性能相关的信息 |
| |
| 它是 High Resolution Time API 的一部分, |
| |
| 同时也融合了 Performance Timeline API、Navigation Timing API、 User Timing API 和 Resource Timing API |
| |
| 这个 API 的属性 timing,包含了页面加载各个阶段的起始及结束时间 |
| |
| 几个重要的知识点 |
| |
| a、白屏时间 |
| |
| 它指从输入网址,到页面开始显示内容的时间。 |
| |
| 将以下脚本放在 </head> 前面就能获取白屏时间。 |
| |
| <script> |
| whiteScreen = new Date() - performance.timing.navigationStart |
| </script> |
| |
| 通过这几个时间,就可以得知页面首屏加载性能如何了 |
| |
| b、获取相关资源 【 s、css、img... 】 的加载时间 |
| |
| window.performance.getEntriesByType('resource') |
| |
| 这个方法返回页面当前所加载的所有资源需要的时间 |
| |
| 它一般包括以下几个类型 |
| |
| 1、sciprt |
| |
| 2、link |
| |
| 3、img |
| |
| 4、css |
| |
| 5、fetch |
| |
| 6、other |
| |
| 7、xmlhttprequest |
| |
| 一般只需用到以下几个信息 |
| |
| |
| name: item.name, |
| |
| |
| duration: item.duration.toFixed(2), |
| |
| |
| size: item.transferSize, |
| |
| |
| protocol: item.nextHopProtocol, |
| 2、性能监控 timing 各个属性的意义 |
| |
| timing: { |
| |
| navigationStart: 1543806782096, |
| |
| |
| unloadEventStart: 1543806782523, |
| |
| |
| unloadEventEnd: 1543806782523, |
| |
| |
| redirectStart: 0, |
| |
| |
| |
| redirectEnd: 0, |
| |
| |
| fetchStart: 1543806782096, |
| |
| |
| |
| domainLookupStart: 1543806782096, |
| |
| |
| |
| domainLookupEnd: 1543806782096, |
| |
| |
| |
| connectStart: 1543806782099, |
| |
| |
| |
| connectEnd: 1543806782227, |
| |
| |
| secureConnectionStart: 1543806782162, |
| |
| |
| requestStart: 1543806782241, |
| |
| |
| |
| responseStart: 1543806782516, |
| |
| |
| |
| responseEnd: 1543806782537, |
| |
| |
| domLoading: 1543806782573, |
| |
| |
| domInteractive: 1543806783203, |
| |
| |
| domContentLoadedEventStart: 1543806783203, |
| |
| |
| domContentLoadedEventEnd: 1543806783216, |
| |
| |
| domComplete: 1543806783796, |
| |
| |
| loadEventStart: 1543806783796, |
| |
| |
| loadEventEnd: 1543806783802 |
| } |
| 3、通过以上数据,我们可以得到几个有用的时间 |
| |
| |
| redirect: timing.redirectEnd - timing.redirectStart, |
| |
| |
| dom: timing.domComplete - timing.domLoading, |
| |
| |
| load: timing.loadEventEnd - timing.navigationStart, |
| |
| |
| unload: timing.unloadEventEnd - timing.unloadEventStart, |
| |
| |
| request: timing.responseEnd - timing.requestStart, |
| |
| |
| time: new Date().getTime(), |
| 4、代码实现收集 性能信息 和 资源信息 |
| |
| |
| const getPerformance = () => { |
| if (!window.performance) return |
| const timing = window.performance.timing |
| const performance = { |
| |
| redirect: timing.redirectEnd - timing.redirectStart, |
| |
| whiteScreen: whiteScreen, |
| |
| dom: timing.domComplete - timing.domLoading, |
| |
| load: timing.loadEventEnd - timing.navigationStart, |
| |
| unload: timing.unloadEventEnd - timing.unloadEventStart, |
| |
| request: timing.responseEnd - timing.requestStart, |
| |
| time: new Date().getTime(), |
| } |
| |
| return performance |
| } |
| |
| |
| const getResources = () => { |
| if (!window.performance) return |
| const data = window.performance.getEntriesByType('resource') |
| const resource = { |
| xmlhttprequest: [], |
| css: [], |
| other: [], |
| script: [], |
| img: [], |
| link: [], |
| fetch: [], |
| |
| time: new Date().getTime(), |
| } |
| |
| data.forEach(item => { |
| const arry = resource[item.initiatorType] |
| arry && arry.push({ |
| |
| name: item.name, |
| |
| duration: item.duration.toFixed(2), |
| |
| size: item.transferSize, |
| |
| protocol: item.nextHopProtocol, |
| }) |
| }) |
| |
| return resource |
| } |
| 5、性能监控 小结 |
| |
| 通过对 性能 及 资源信息 的解读,我们可以判断出页面 加载慢 有以下几个原因 |
| |
| 1、资源过多 |
| |
| 2、网速过慢 |
| |
| 3、DOM元素过多 |
| |
| 除了用户网速过慢,我们没办法之外,其他两个原因都是有办法解决的 |
| |
错误监控
| 1、错误监控 概述 |
| |
| 现在能捕捉的错误有 三 种 |
| |
| 1、资源加载错误,通过 addEventListener('error', callback, true) 在捕获阶段捕捉资源加载失败错误。 |
| |
| 2、js 执行错误,通过 window.onerror 捕捉 js 错误。 |
| |
| 3、promise 错误,通过 addEventListener('unhandledrejection', callback)捕捉 promise 错误,但是没有发生错误的行数,列数等信息,只能手动抛出相关错误信息 |
| |
| 我们可以建一个错误数组变量 errors 在错误发生时,将错误的相关信息添加到数组,然后在某个阶段统一上报 |
| |
| |
| addEventListener('error', e => { |
| const target = e.target |
| if (target != window) { |
| monitor.errors.push({ |
| type: target.localName, |
| url: target.src || target.href, |
| msg: (target.src || target.href) + ' is load error', |
| |
| time: new Date().getTime(), |
| }) |
| } |
| }, true) |
| |
| |
| window.onerror = function(msg, url, row, col, error) { |
| monitor.errors.push({ |
| type: 'javascript', |
| row: row, |
| col: col, |
| msg: error && error.stack? error.stack : msg, |
| url: url, |
| |
| time: new Date().getTime(), |
| }) |
| } |
| |
| |
| addEventListener('unhandledrejection', e => { |
| monitor.errors.push({ |
| type: 'promise', |
| msg: (e.reason && e.reason.msg) || e.reason || '', |
| |
| time: new Date().getTime(), |
| }) |
| }) |
| 2、错误监控 小结 |
| |
| 通过错误收集,可以了解到网站错误发生的类型及数量,从而可以做相应的调整,以减少错误发生 |
数据上报
| 1、性能数据上报 |
| |
| 性能数据可以在页面加载完之后上报,尽量不要对页面性能造成影响 |
| |
| window.onload = () => { |
| |
| |
| |
| if (window.requestIdleCallback) { |
| window.requestIdleCallback(() => { |
| monitor.performance = getPerformance() |
| monitor.resources = getResources() |
| }) |
| } else { |
| setTimeout(() => { |
| monitor.performance = getPerformance() |
| monitor.resources = getResources() |
| }, 0) |
| } |
| } |
| |
| 也可以设一个定时器,循环上报。不过每次上报最好做一下对比去重再上报,避免同样的数据重复上报 |
| 2、错误数据上报 |
| |
| |
| window.onerror = function(msg, url, row, col, error) { |
| const data = { |
| type: 'javascript', |
| row: row, |
| col: col, |
| msg: error && error.stack? error.stack : msg, |
| url: url, |
| |
| time: new Date().getTime(), |
| } |
| |
| |
| axios.post({ url: 'xxx', data, }) |
| } |
SPA
| window.performance API 是有缺点的,在 SPA 切换路由时,window.performance.timing 的数据不会更新 |
| |
| 我们需要另想办法来统计切换路由到加载完成的时间 |
| |
| Vue 举例,一个可行的办法就是切换路由时,在路由的全局前置守卫 beforeEach 里获取开始时间,在组件的 mounted 钩子里执行 vm.$nextTick 函数来获取组件的渲染完毕时间。 |
| |
| router.beforeEach((to, from, next) => { |
| store.commit('setPageLoadedStartTime', new Date()) |
| }) |
| |
| mounted() { |
| this.$nextTick(() => { |
| this.$store.commit('setPageLoadedTime', new Date() - this.$store.state.pageLoadedStartTime) |
| }) |
| } |
用户信息收集
| 1、navigator |
| |
| 使用 window.navigator 可以收集到用户的设备信息,操作系统,浏览器信息... |
| |
| 2、UV( Unique visitor ) |
| |
| 指通过互联网访问、浏览这个网页的自然人 |
| |
| 访问您网站的一台电脑客户端为一个访客。00:00-24:00内相同的客户端只被计算一次。一天内同个访客多次访问仅计算一个UV |
| |
| 在用户访问网站时,可以生成一个随机字符串+时间日期,保存在本地。 |
| |
| 在网页发生请求时(如果超过当天24小时,则重新生成),把这些参数传到后端,后端利用这些信息生成 UV 统计报告。 |
| |
| 3、PV( Page View ) |
| |
| 即页面 浏览量 或 点击量,用户每 1 次对网站中的每个网页访问均被记录 1个 PV。 |
| |
| 用户对同一页面的多次访问,访问量累计,用以衡量网站用户访问的网页数量 |
| |
| 4、页面停留时间 |
| |
| 传统网站用户在进入 A 页面时,通过后台请求把用户进入页面的时间捎上。过了 10 分钟,用户进入 B 页面,这时后台可以通过接口捎带的参数可以判断出用户在 A 页面停留了 10 分钟。 |
| |
| SPA 可以利用 router 来获取用户停留时间 |
| |
| Vue 举例,通过 router.beforeEach destroyed 这两个钩子函数来获取用户停留该路由组件的时间。 |
| |
| 5、浏览深度 |
| |
| 通过 document.documentElement.scrollTop 属性以及屏幕高度,可以判断用户是否浏览完网站内容 |
| |
| 6、页面跳转来源 |
| |
| 通过 document.referrer 属性,可以知道用户是从哪个网站跳转而来 |
| |
| 7、小结 |
| |
| 通过分析用户数据,我们可以了解到用户的浏览习惯、爱好等等信息,想想真是恐怖,毫无隐私可言 |
前端监控部署
| 前面说的都是监控原理,但要实现还是得自己动手写代码 |
| |
| 可以用现有的工具 sentry 去做这件事 |
| |
| sentry 是一个用 python 写的性能和错误监控工具,你可以使用 sentry 提供的服务(免费功能少),也可以自己部署服务 |
性能优化
性能优化概述
| 性能优化主要分为两类 |
| |
| 1、加载时优化 |
| |
| 例如 压缩文件、使用 CDN 就属于 加载时优化 |
| |
| 2、运行时优化 |
| |
| 减少 DOM 操作,使用事件委托属于运行时优化 |
| |
| 在解决问题之前,必须先找出问题,否则无从下手 |
| |
| 所以在做性能优化之前,最好先调查一下网站的 加载性能 和 运行性能。 |
手动检查性能
| 1、检查 加载性能 |
| |
| 一个网站 加载性能 如何主要看 白屏时间 和 首屏时间。 |
| |
| 白屏时间:指从输入网址,到页面开始显示内容的时间。 |
| |
| 首屏时间:指从输入网址,到页面完全渲染的时间。 |
| |
| a、获取 白屏时间 |
| |
| 将以下脚本放在 </head> 前面就能获取 白屏时间。 |
| |
| <script> |
| new Date() - performance.timing.navigationStart |
| </script> |
| |
| b、获取 首屏时间 |
| |
| 在 window.onload 事件里 |
| |
| 执行 new Date() - performance.timing.navigationStart 即可获取首屏时间 |
| |
| 2、检查 运行性能 |
| |
| 配合 chrome 的开发者工具,我们可以查看网站在运行时的性能 |
| |
| 打开网站,按 F12 选择 performance,点击左上角的灰色圆点,变成红色就代表开始记录了。 |
| |
| 这时可以模仿用户使用网站,在使用完毕后,点击 stop,然后你就能看到网站运行期间的性能报告。 |
| |
| 如果有红色的块,代表有掉帧的情况;如果是绿色,则代表 FPS 很好。 |
| |
| 另外,在 performance 标签下,按 ESC 会弹出来一个小框。点击小框左边的三个点,把 rendering 勾出来。 |
| |
| 1、Paint flashing 是高亮重绘区域 |
| |
| 2、Frame Rendering Stats 是显示帧渲染信息 |
| |
| 把这两个选项勾上,然后浏览网页,可以实时的看到你网页渲染变化。 |
利用工具检查
| 1、监控工具 |
| |
| 可以部署一个前端监控系统来监控网站性能,如 sentry 就属于这一类。 |
| |
| 2、chrome 工具 Lighthouse 和 Performance 面板 |
| |
| 安装 Chrome 52+ 版本,请按 F12 打开开发者工具 选择 Lighthouse 面板 |
| |
| 它不仅会对你网站的性能打分,还会对 SEO 打分 |
重构
重构概述
| 所谓重构( refactoring )是这样一个过程: |
| |
| 在不改变代码外在行为的前提下,对代码做出修改,以改进程序的内部结构。 |
| |
| 重构是一种经千锤百炼形成的有条不紊的程序整理方法,可以最大限度地减小整理过程中引入错误的概率。 |
| |
| 本质上说,重构就是在代码写好之后改进它的设计 |
| |
| 重构和性能优化有相同点,也有不同点。 |
| |
| 1、相同的地方是它们都在不改变程序功能的情况下修改代码 |
| |
| 2、不同的地方是重构为了让代码变得更加易读、理解,性能优化则是为了让程序运行得更快 |
| |
| 3、重构可以一边写代码一边重构,也可以在程序写完后,拿出一段时间专门去做重构。没有说哪个方式更好,视个人情况而定 |
| |
| 4、如果你专门拿一段时间来做重构,建议你在重构一段代码后,立即进行测试。这样可以避免修改代码太多,在出错时找不到错误点。 |
重构原则
| 1、事不过三,三则重构。即不能重复写同样的代码,在这种情况下要去重构 |
| |
| 2、如果一段代码让人很难看懂,那就该考虑重构了 |
| |
| 3、如果已经理解了代码,但是非常繁琐或者不够好,也可以重构 |
| |
| 4、过长的函数,需要重构 |
| |
| 5、一个函数最好对应一个功能,如果一个函数被塞入多个功能,那就要对它进行重构了 |
重构手法
| 1、提取重复代码,封装成函数 |
| |
| 2、拆分太长 或 功能太多 的函数 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!