前端杂学录(十)

发布于:2024-10-17 ⋅ 阅读:(12) ⋅ 点赞:(0)

1.axios 的底层实现

1. XMLHttpRequests

在浏览器环境中,Axios 使用 XMLHttpRequest 对象来发送请求。这是一个古老的 Web API,用于在浏览器中进行异步 HTTP 请求。

2. Node.js 的 http 模块

在 Node.js 环境中,Axios 使用 Node.js 的 httphttps 模块来发送请求。这些模块提供了创建请求的基本功能。

3. Promise

Axios 将请求封装在 Promise 中,这使得你可以使用 .then().catch() 方法来处理异步操作的成功和失败情况,或者使用 async/await 语法。

4. 转换请求和响应数据

Axios 会自动处理请求和响应数据的转换,例如:

  • 将 JSON 字符串转换为 JavaScript 对象。
  • 处理不同格式的数据,如 JSON、FormData 等。

5. 请求和响应拦截器

Axios 允许你添加请求和响应拦截器,这些拦截器可以修改请求配置、响应数据或在请求/响应过程中执行其他操作。

6. 错误处理

Axios 提供了错误处理机制,它会抛出的错误对象包含请求信息、响应信息、JavaScript 异常等。

7. 取消令牌(CancelToken)

Axios 支持取消请求的功能,通过创建一个取消令牌(CancelToken),你可以在请求发送后取消它。

Axios 源码分析

Axios 的核心源码主要包含以下几个部分:

  • axios.js:这是 Axios 的入口文件,它创建了 axios 函数和 Axios 原型链上的方法,如 getpost 等。

  • defaults.js:定义了 Axios 的默认配置,如 baseURL、headers、transform 请求和响应数据的函数等。

  • core/Axios.js:实现了 axios 函数和 Axios 原型链上的方法。这是 Axios 请求的核心逻辑,包括发送请求、处理响应、应用拦截器等。

  • core/dispatchRequest.js:负责根据配置发送请求,并处理请求过程中的 Promise 链。

  • core/transformData.js:处理请求和响应数据的转换。

  • core/createError.js:创建 Axios 错误对象。

  • core/settle.js:处理请求完成后的 Promise 解决(resolve)或拒绝(reject)。

  • cancel/CancelToken.jscancel/isCancel.js:实现了取消令牌的逻辑。

  • helpers/:包含了一些辅助函数,如 URL 处理、头部处理等。

Axios 请求流程

  1. 创建 axios 实例或直接使用 axios 发送请求。
  2. Axios 构造函数创建一个请求配置对象。
  3. 如果提供了请求或响应拦截器,将它们添加到请求配置对象。
  4. 调用 dispatchRequest 方法发送请求。
  5. dispatchRequest 方法根据请求配置对象发送 HTTP 请求,并返回一个 Promise。
  6. 请求完成后,根据响应结果调用 Promise 的 resolve 或 reject 方法。

2.ECharts 底层是用什么绘制的

ECharts 的底层渲染引擎是 Canvas。Canvas 是 HTML5 提供的一个 API,允许通过 JavaScript 在网页上绘制图形。ECharts 利用 Canvas 的 2D 渲染上下文进行绘图,从而实现各种图表的渲染。

为什么选择 Canvas

  1. 性能:Canvas 提供了一种高效的方式来绘制图形,尤其是在处理大量数据和复杂动画时,Canvas 的性能通常优于 SVG。
  2. 灵活性:Canvas 提供了较低级别的绘图接口,使得开发者可以灵活地绘制各种自定义图形。
  3. 跨平台:Canvas 是 HTML5 的一部分,可以在现代浏览器中无缝工作,同时也可以通过一些库(如 node-canvas)在 Node.js 环境中使用。

ECharts 的工作流程

  1. 数据处理:ECharts 允许用户以 JSON 格式配置图表的数据和选项。
  2. 渲染流程:ECharts 内部会解析这些配置,然后使用 Canvas 绘制图表。
  3. 事件处理:ECharts 还提供了丰富的事件回调,例如点击、悬停等,以便开发者可以响应用户交互。

示例代码

以下是一个简单的 ECharts 折线图示例:

<!DOCTYPE html>
<html style="height: 100%">
<head>
    <meta charset="utf-8">
</head>
<body style="height: 100%; margin: 0">
    <div id="container" style="height: 100%"></div>

    <script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
    <script>
        var chartDom = document.getElementById('container');
        var myChart = echarts.init(chartDom);
        var option;

        option = {
            title: {
                text: '主标题'
            },
            tooltip: {},
            legend: {
                data:['销量']
            },
            xAxis: {
                data: ["衬衫","羊毛衫","雪纺衫","裤子","高跟鞋","袜子"]
            },
            yAxis: {},
            series: [{
                name: '销量',
                type: 'line',
                data: [5, 20, 36, 10, 10, 20]
            }]
        };

        option && myChart.setOption(option);
    </script>
</body>
</html>

在这个示例中,我们首先在 HTML 中创建了一个 div 容器,然后使用 ECharts 提供的 echarts.init 方法初始化一个图表实例,并指定容器。接着,我们定义了一个 option 对象来配置图表的标题、提示框、图例、坐标轴和系列数据。最后,我们通过调用 myChart.setOption(option) 方法来渲染图表。

3.husky的原理是什么

Husky 是一个 Git 钩子(hooks)管理器,用于在版本控制的生命周期中自动执行脚本,如提交前(pre-commit)、提交后(post-commit)、推送前(pre-push)等。Husky 通过在 Git 仓库中安装钩子来工作,它的原理主要基于以下几个方面:

1. Git 钩子(Git Hooks)

Git 钩子是 Git 提供的一种机制,允许在特定的动作发生时触发自定义脚本。这些钩子脚本可以用于执行各种任务,如代码格式化、运行测试、检查代码风格等。

2. Husky 的安装和配置

当你在项目中安装 Husky 时,它会在项目的 node_modules/.husky 目录下创建一个 Git 钩子目录。Husky 会修改项目的 .git/config 文件,将 Husky 脚本指向相应的 Git 钩子。

例如,如果你想要在提交前运行一些脚本,Husky 会在 .husky/pre-commit 文件中添加一个脚本,该脚本会在每次执行 git commit 命令时运行。

3. Husky 的工作流程

当执行 Git 操作(如提交、推送等)时,Git 会检查对应的钩子脚本是否存在,并执行它们。Husky 利用这一点来自动运行你配置的脚本。

  • 安装 Husky:通过 npm 或 yarn 安装 Husky。
  • 配置 Husky 钩子:使用 husky 配置对象在 package.json 文件中设置钩子。
  • 运行钩子脚本:当相应的 Git 操作被执行时,Husky 会触发配置的脚本。

4. 示例配置

{
  "husky": {
    "hooks": {
      "pre-commit": "npm run lint",
      "pre-push": "npm test"
    }
  }
}

在这个示例中,我们配置了两个钩子:

  • pre-commit:在提交前运行 npm run lint 脚本。
  • pre-push:在推送前运行 npm test 脚本。

5. 注意事项

  • Husky 只在本地开发环境中工作,它不会影响其他开发者的 Git 钩子配置。
  • Husky 需要 Node.js 环境,因此它通常用于 JavaScript 或 TypeScript 项目。
  • Husky 4.0 及更高版本引入了对 pnpm 和 yarn 工作区的支持。

4.Vite为什么快?是怎么打包的?

Vite 为什么快?

  1. 原生 ES 模块(ESM)

    • Vite 利用了浏览器原生支持的 ES 模块特性,这意味着在开发过程中,Vite 可以直接使用 import 和 export 语句来动态加载模块,而无需打包整个应用。
  2. 服务端渲染(SSR)

    • 在开发过程中,Vite 作为一个静态文件服务器,通过 Node.js 服务端渲染模块,这避免了传统打包器在启动时需要进行的复杂打包操作。
  3. 按需编译

    • Vite 仅在需要时编译文件,例如当模块发生变化时,Vite 才会编译这些更改的模块,而不是每次都编译整个项目。
  4. 高效的热模块替换(HMR)

    • Vite 提供了快速的热更新能力,这意味着开发者在修改代码后可以立即看到更改的效果,而无需刷新整个页面。
  5. 并行处理

    • Vite 利用多核处理器的能力,可以并行处理任务,例如并行编译和压缩文件。
  6. 优化的构建过程

    • 在构建(构建生产版本)过程中,Vite 使用了 Rollup,这是一个零配置的模块打包器,专注于速度和简洁的打包结果。

Vite 是怎么打包的?

  1. 开发模式

    • 在开发模式下,Vite 不会执行完整的打包过程。相反,它提供了一个开发服务器,该服务器处理模块的按需编译和提供热更新。
  2. 构建模式

    • 在构建模式下,Vite 使用 Rollup 作为其打包器。Rollup 会根据 Vite 的配置来打包应用程序。
    • Vite 配置文件(如 vite.config.js)允许你定义构建过程中的各种选项,如别名、插件、外部库等。
  3. 插件系统

    • Vite 提供了一个强大的插件系统,允许开发者根据需要添加插件来扩展其功能。这些插件可以在构建过程中的各个阶段介入,例如在打包之前或之后执行任务。
  4. 构建优化

    • Vite 会自动处理代码分割、摇树优化(Tree-shaking)和模块懒加载,以生成优化的生产代码。
  5. 预构建

    • Vite 支持预构建功能,允许你在构建过程中预编译模块,以加快后续构建的速度。
  6. 多页面应用支持

    • Vite 支持多页面应用的构建,允许你为每个页面生成独立的入口和输出。

5.position 都有什么属性

  1. static

    • 默认值。元素按照正常的文档流进行布局,即它出现在HTML中的位置。
  2. relative

    • 元素脱离正常文档流,相对于它原本的位置进行定位。未占用的空间会保留。
  3. absolute

    • 元素脱离正常文档流,相对于最近的已定位(非 static)祖先元素进行定位。如果不存在这样的祖先元素,则相对于初始包含块(通常是HTML文档的 <html> 元素)。
  4. fixed

    • 元素脱离正常文档流,相对于浏览器窗口进行定位。即使滚动页面,元素也会停留在相同的屏幕位置。
  5. sticky

    • 元素基于用户的滚动位置进行定位。它类似于 relative,但当页面滚动超出一定区域时,元素的表现会转变为 fixed

除了这些定位方式,position 属性还可以配合其他 CSS 属性来确定元素的确切位置,这些属性包括:

  • top

    • 指定元素的上边缘与父元素上边缘的距离。
  • right

    • 指定元素的右边缘与父元素右边缘的距离。
  • bottom

    • 指定元素的下边缘与父元素下边缘的距离。
  • left

    • 指定元素的左边缘与父元素左边缘的距离。

6.flex相关属性

容器属性(应用于父元素)

  1. display:

    • display: flex; 或 display: inline-flex; 定义了一个 Flex 容器。
  2. flex-direction:

    • 定义了主轴的方向(是水平还是垂直)。
    • 值:row(默认)、row-reversecolumncolumn-reverse
  3. flex-wrap:

    • 定义了是否应该包裹,即允许子元素换行。
    • 值:nowrap(默认)、wrapwrap-reverse
  4. flex-flow:

    • 是 flex-direction 和 flex-wrap 的简写形式。
  5. justify-content:

    • 定义了子元素在主轴上的对齐方式。
    • 值:flex-startflex-endcenterspace-betweenspace-aroundspace-evenly
  6. align-items:

    • 定义了子元素在交叉轴上的对齐方式。
    • 值:flex-startflex-endcenterbaselinestretch(默认)。
  7. align-content:

    • 当有多行子元素时,定义了行在交叉轴上的对齐方式。
    • 值:flex-startflex-endcenterspace-betweenspace-aroundstretch

项目属性(应用于子元素)

  1. order:

    • 定义了子元素的排序顺序。
    • 值:整数,数值越小越靠前。
  2. flex-grow:

    • 定义了子元素的放大比例,即在分配多余空间时的增长能力。
    • 值:无单位的数字(默认为 0)。
  3. flex-shrink:

    • 定义了子元素的缩小比例,即在空间不足时的缩小能力。
    • 值:无单位的数字(默认为 1)。
  4. flex-basis:

    • 定义了子元素在分配多余空间之前的默认大小。
    • 值:长度值(如 20%10px 等),auto 或 content
  5. flex:

    • 是 flex-growflex-shrink 和 flex-basis 的简写形式,默认值为 0 1 auto
    • 值:flex: noneflex: autoflex: 2 等。
  6. align-self:

    • 允许单个子元素覆盖默认的 align-items 属性。
    • 值:autoflex-startflex-endcenterbaselinestretch

7.first-paint 首次绘制时间

const Fun = (list)=>{
    for(let entry of list.getEntries()){
        if(entry.name === 'first-paint'){
            observe.disconnect()
            const json = entry.toJSON();
            console.log(json)
            const data = {
                ...json,
                type: 'performance',
                subType: entry.name,
                startTime: entry.startTime,
                pageUrl:window.location.href
            }
            console.log("data:",data)
        }
    }
}
const observe = new PerformanceObserver(Fun);
observe.observe({type: 'paint', buffered: true})

8.first-contentful-paint首次内容绘制

const Fun = (list)=>{
    for(let entry of list.getEntries()){
        if(entry.name === 'first-contentful-paint'){
            observe.disconnect()
            const json = entry.toJSON();
            console.log(json)
            const data = {
                ...json,
                type: 'performance',
                subType: entry.name,

            }
            console.log(data)
        }
    }
}
const observe = new PerformanceObserver(Fun);
observe.observe({type: 'paint', buffered: true})

9.largest-contentful-paint最大内容绘制

const Fun = (list)=>{
    if(observe){
        observe.disconnect();
    }
    for(let entry of list.getEntries()){
        const json = entry.toJSON();
        console.log(json)
        const data = {
            ...json,
            type:'lcp'
        }
        // console.log(data)
    }
}
const observe = new PerformanceObserver(Fun);
observe.observe({type:'largest-contentful-paint',buffered:true})

10load

function observeLoad(){
    window.addEventListener('pageshow',function(event){
        ['load'].forEach(type=>{
            const data = {
                type: type,
                timeStamp: event.timeStamp,
                isReload: event.persisted
            }
        })
    })
}

11.监听资源

const Fun = (list)=>{
    for(let entry of list.getEntries()){
        observe.disconnect()
        const json = entry.toJSON();
        console.log(json)
        const data = {
            ...json
        }
    }
}
const observe = new PerformanceObserver(Fun);
observe.observe({type:'resource',buffered:true})

12.监听axios

const originalProto = XMLHttpRequest.prototype;
const originalOpen = originalProto.open;
const originalSend = originalProto.send;

function overWriteOpenAndSend(){
    originalProto.open = function newOpen(...args){
        this.method = args[0];
        this.url = args[1];
        originalOpen.apply(this,args)

    }
    originalProto.send = function newSend(...args){
        const statTime = Date.now();
        const onLoaded = ()=>{
            this.endTime = Date.now();
            this.duration = endTime - statTime;
            const {url,method,status,statTime,endTime,duration} = this

            this.removeEventListener('load',onLoaded,true)
        }
        this.addEventListener('load',onLoaded,true)
        originalSend.apply(this,args);
    }
}

13.错误监听

try...catch  只能捕获同步

异步使用 window.onerror ,但对于css、image的错误不能监控

使用window.addEventListenner(‘error’{},true)去捕获(设置true,因为在捕获阶段去处理),但对于promise的err不能捕获(当然可以加.catch去捕获)

promise没加catch的使用window.addEventListenner(‘unhandledrejection’)去捕获promise的err

 当然针对promise和 async await的错误,把它throw err出去,然后使用window.addEventListenner(‘error’{},true)去捕获更好

 

window.addEventListener('error',function(e){
    //判断是不是js错误 css错误 image错误
    const target = e.target;
    if(target.src || target.href){
        return
    }
    const url = target.href || target.src;
    const data = {
        url,
        pageUrl:window.location.href,
        msg:e.message,
        line:e.lineno,
        col:e.colno
    }
    //todo 上报信息
},true)

// js错误
window.onerror = function(msg,url,line,col,error){
    const data = {
        url,
        pageUrl:window.location.href,
        msg:error && error.stack || msg,
        line,
        col
    }
    //todo 上报信息
}

//promise错误 async await 错误
window.addEventListener('unhandledrejection',function(e){
    const data = {
        url:window.location.href,
        msg:e.reason && e.reason.stack || e.reason || 'unhandledrejection',
    }
    //todo 上报信息
})

14.监听fetch

const originalFetch = window.fetch;
function overWriteFetch(){
    window.fetch = function newFetch(url,config){
        const startTime = performance.now();
        const data = {
            url,
            method:config.method,
            startTime
        }
        return originalFetch(url,config).then((res)=>{
            const endTime = performance.now();
            data.endTime = endTime;
            data.duration = endTime - startTime;
            const newRes = res.clone();
            data.status = res.status;
            data.success = res.ok;
            console.log(data);
            return res;
        }).catch((err)=>{
            const endTime = performance.now();
            data.endTime = endTime;
            data.duration = endTime - startTime;
            ata.status = 0;
            data.success = false;
            console.log(err);
        })
    }
}