美团前端一面面经整理

webpack5和4

Webpack 5 是 Webpack 4 的升级版本,它引入了许多新功能和改进。一些重要的区别包括:

  • 性能优化:Webpack 5 引入了多个性能优化,包括更好的 Tree Shaking(用于剔除未使用代码的工具)和持久性缓存(Persistent Caching)。
  • 模块联邦:Webpack 5 引入了模块联邦(Module Federation)的概念,允许将代码动态地加载到其他独立的应用程序中。
  • 更好的长期缓存:Webpack 5 通过引入 chunk 和 module id 的持久性,改善了长期缓存策略。
  • 默认值更改:一些默认值和配置选项在 Webpack 5 中有所改变,可能需要更新配置文件。

plugin和loader区别

更多webpack面试题

webpack原理:从配置文件定义的模块列表开始,处理应用程序,从入口文件开始递归构建一个依赖图,然后将所有模块打包为少量的bundle,通常只有一个,可由浏览器加载。

loader:loader从字面的意思理解,是加载的意思。由于webpack 本身只能打包js文件,所以,针对css,图片等格式的文件没法打包,就需要引入第三方的模块进行打包。loader虽然是扩展了 webpack ,但是它只专注于转化文件(transform)这一个领域,完成压缩,打包,语言翻译。loader是运行在NodeJS中。仅仅只是为了打包。

  • css-loader和style-loader模块是为了打包css的
  • babel-loader和babel-core模块时为了把ES6的代码转成ES5
  • url-loader和file-loader是把图片进行打包的。

plugin: plugin 丰富了webpack本身,针对是loader结束后,webpack打包的整个过程。目的在于解决loader无法实现的其他事,从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。webpack提供了很多开箱即用的插件:CommonChunkPlugin主要用于提取第三方库和公共模块,避免首屏加载的bundle文件,或者按需加载的bundle文件体积过大,导致加载时间过长,是一把优化的利器。而在多页面应用中,更是能够为每个页面间的应用程序共享代码创建bundle。

插件可以携带参数,所以在plugins属性传入new实例。

针对html文件打包和拷贝(还有很多设置)的插件:html-webpack-plugin。 不但完成了html文件的拷贝,打包,还给html中自动增加了引入打包后的js文件的代码(),还能指明把js文件引入到html文件的底部等等。
代码如下所示:

plugins:[   
//对html模板进行处理,生成对应的html,引入需要的资源模块
new HtmlWebpackPlugin({
    template:'./index.html',//模板文件,即需要打包和拷贝到build目录下的html文件
    filename:'index.html',//目标html文件
    chunks:['useperson'],//对应加载的资源,即html文件需要引入的js模块
    inject:true//资源加入到底部,把模块引入到html文件的底部
    })
]

Webpack构建流程

Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:

  • 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数
  • 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译
  • 确定入口:根据配置中的 entry 找出所有的入口文件
  • 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理
  • 完成模块编译:在经过第4步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系
  • 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会
  • 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统

在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。

简单说

  • 初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler
  • 编译:从 Entry 出发,针对每个 Module 串行调用对应的 Loader 去翻译文件的内容,再找到该 Module 依赖的 Module,递归地进行编译处理
  • 输出:将编译后的 Module 组合成 Chunk,将 Chunk 转换成文件,输出到文件系统中

ES6新增的proxy

手写:用proxy实现访问对象时,输出别的数字

proxy的使用

const target = {
  name: "John",
  age: 30,
};

const handler = {
  get: function(target, property, receiver) {
    console.log(`Getting property "${property}"`);
    return 22;
  },
};

const proxy = new Proxy(target, handler);
console.log(proxy.age)

Vue2.0和3.0最大的区别

跨域

跨域是如何形成的

当我们请求一个url的协议、域名、端口三者之间任意一个与当前页面url的协议、域名、端口不同这种现象我们把它称之为跨域。

浏览器在解析JavaScript出于安全方面的考虑,只允许在同域名下页面进行相互资源请求调用,不允许调用其他域名下的页面的对象;简单的理解就是因为JavaScript同源策略的限制。

注意:跨域并不是请求发不出去,请求能发出去,服务器能收到请求并正常返回结果,只是结果被浏览器拦截了,所以页面无法正常使用数据。

跨域会导致

  1. 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
  2. 无法接触非同源网页的 DOM
  3. 无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)

导致跨域的根本原因是请求浏览器的同源策略导致的 ,而跨域请求报错的原因是: 浏览器同源策略 && 请求是ajax类型 && 请求确实跨域了。

页面发起跨域请求后,浏览器会先发起预检请求,预检通过后,在发起正式请求。如果预检请求不过,浏览器就会停止后面的业务请求,导致访问失败。

怎么跨域

解决方法: jsonp,cors,代理转发
点击查看更多

  1. jsonp
    利用<script src="xxx">元素的这个天然支持跨域的策略,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。一般是在请求的url中添加一个回调函数参数,服务器的响应将被包裹在这个参数中,从而实现跨域读取数据。

    <script type="text/javascript">
        function handleResponse(data){
            alert(data.msg);
        }
    </script>
    <script type="text/javascript" src="http://crossdomain.com/jsonServerResponse?callback=handleResponse">
    

    服务端支持的代码:服务端获取回调函数的名称,然后将数据包裹在回调函数中作为响应返回给客户端。

    const express = require('express');
    const app = express();
    
    app.get('/data', (req, res) => {
        const data = { message: 'Hello, World!' };
        const callbackName = req.query.callback; // 获取回调函数名称
    
        // 返回数据,并将数据包裹在回调函数中
        res.send(`${callbackName}(${JSON.stringify(data)})`);
    });
    
    app.listen(80, () => {
        console.log('Server started');
    });
    
  2. CORS(Cross-Origin Resource Sharing)

    实现CORS通信的关键是服务器,需要在服务器端做一些小小的改造。
    只要服务器实现了CORS接口,就可以跨源通信。
    在响应头上添加Access-Control-Allow-Origin属性,指定同源策略的地址。同源策略默认地址是网页的本身。只要浏览器检测到响应头带上了CORS,并且允许的源包括了本网站,那么就不会拦截请求响应。

    用Express框架,配置了一个中间件来处理CORS。设置了允许跨域的源http://www.example.com

    const express = require('express');
    const app = express();
    
    // 配置CORS中间件
    app.use(function(req, res, next) {
        // 允许特定域的跨域访问
        res.header('Access-Control-Allow-Origin', 'http://www.example.com');
        // 允许发送跨域请求的HTTP方法
        res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
        // 允许的请求头
        res.header('Access-Control-Allow-Headers', 'Content-Type');
        // 是否允许发送Cookie
        res.header('Access-Control-Allow-Credentials', 'true');
        next();
    });
    
    // 跨域请求处理
    app.get('/data', (req, res) => {
        const data = { message: 'Hello, World!' };
        res.send(data);
    });
    
    app.listen(80, () => {
        console.log('Server started');
    });
    
  3. 代理服务器
    在客户端和服务端中间加一个代理服务器,使得客户端向代理服务器发送请求,再由代理服务器向真正的服务器发送请求,并将结果转发给客户端。

    vue 在 vue.config.js 配置代理服务器

    // vue.config.js
    module.exports = {
        devServer: {
            proxy: {
            '/api': {
                target: 'http://api.example.com', // 目标服务器的URL
                changeOrigin: true,
                pathRewrite: {
                '^/api': '', // 可选,用于重写路径
                },
            },
            },
        },
    };
    

怎么配置代理服务器

  1. Node.js 中使用 Express 作为代理服务器:

    安装 Express 和 http-proxy-middleware:npm install express http-proxy-middleware --save

    创建一个 Express 应用并配置代理:

    const express = require('express');
    const { createProxyMiddleware } = require('http-proxy-middleware');
    
    const app = express();
    
    // 配置代理
    app.use('/api', createProxyMiddleware({ 
      target: 'https://example.com', // 目标服务器的地址
      changeOrigin: true, // 启用跨域
      pathRewrite: {
        '^/api': '', // 可选的路径重写规则
      },
    }));
    
    // 启动 Express 服务器
    app.listen(3000, () => {
      console.log('Proxy server is running on port 3000');
    });
    

    在上述示例中,Express 应用会将以 /api 开头的请求代理到 https://example.com,并启用跨域。

  2. Webpack DevServer 配置代理:

    如果您正在使用 Webpack DevServer 来开发前端应用,可以使用其代理配置选项来设置代理服务器:

    // webpack.config.js
    module.exports = {
      // ...
      devServer: {
        proxy: {
          '/api': {
            target: 'https://example.com', // 目标服务器的地址
            changeOrigin: true, // 启用跨域
            pathRewrite: {
              '^/api': '', // 可选的路径重写规则
            },
          },
        },
      },
    };
    

    在这种情况下,Webpack DevServer 会将以 /api 开头的请求代理到目标服务器,并启用跨域。

  3. Nginx 配置反向代理:

    如果您使用 Nginx 作为 Web 服务器,可以配置反向代理来解决跨域问题。以下是一个简单的 Nginx 配置示例:

    location /api/ {
        proxy_pass https://example.com/;  # 目标服务器的地址
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    

    在上述示例中,Nginx 会将以 /api/ 开头的请求代理到目标服务器,并设置一些必要的请求头以确保代理正常工作。

async和await的理解

async 和 await 是 JavaScript 中用于处理异步操作的特性,这两个关键字通常与 Promise 一起使用。

async 函数:

async 函数是一个特殊的函数,它始终返回一个 Promise 对象。
在 async 函数内部,您可以使用 await 关键字来等待一个 Promise 对象完成。这将暂停函数的执行,直到 Promise 完成并返回结果。
async 函数可以包含多个 await 表达式,使您能够按顺序执行异步操作。

async function fetchData() {
  // async 函数内部可以使用 try...catch 来捕获和处理异步操作中的错误。
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Error:', error);
  }
}

await 关键字:

await 关键字只能在 async 函数内部使用。
它用于等待一个 Promise 对象的解决或拒绝,然后返回该 Promise 的解决值。
如果 await 后面的表达式不是 Promise,它会将其包装成一个已解决的 Promise。

async function example() {
  // await 用于等待 someAsyncFunction 的完成,然后将其结果赋给 result 变量
  const result = await someAsyncFunction();
  console.log(result);
}

手写:看输出结果(事件循环)

promise的理解

ES6引入了 Promise 对象,它是一种处理异步操作的机制,用于更容易和可读地处理异步代码。Promise 的主要目的是解决回调地狱(Callback Hell)问题,使异步代码更加清晰和可维护。

Promise 状态:Promise 有三种状态,分别是:

  • Pending(进行中):初始状态,表示异步操作还没有完成。
  • Fulfilled(已完成):表示异步操作成功完成。
  • Rejected(已拒绝):表示异步操作失败或被拒绝。

创建 Promise:你可以使用 Promise 构造函数来创建一个 Promise 对象,它接受一个函数作为参数,该函数有两个参数,分别是 resolve 和 reject,用于手动控制 Promise 的状态。

const myPromise = new Promise((resolve, reject) => {
  // 异步操作
  if (/* 异步操作成功 */) {
    resolve(result); // 将 Promise 状态设置为 Fulfilled
  } else {
    reject(error); // 将 Promise 状态设置为 Rejected
  }
});

Promise 链:Promise 可以通过 .then() 方法来处理异步操作的结果,形成链式调用。.then() 方法接受两个回调函数,分别处理成功和失败的情况。

myPromise
  .then(result => {
    // 处理成功情况
  })
  .catch(error => {
    // 处理失败情况
  });

Promise.all():Promise.all() 方法接受一个 Promise 数组,只有当所有 Promise 都成功时,它才会成功,返回一个包含所有结果的数组。如果任何一个 Promise 失败,它就会失败。

const promises = [promise1, promise2, promise3];

Promise.all(promises)
  .then(results => {
    // 处理所有成功的结果
  })
  .catch(error => {
    // 处理失败情况
  });

Promise.race():Promise.race() 方法接受一个 Promise 数组,只要有一个 Promise 完成(无论成功或失败),它就会完成,并返回第一个完成的 Promise 的结果或错误。

const promises = [promise1, promise2, promise3];

Promise.race(promises)
  .then(result => {
    // 处理第一个完成的结果
  })
  .catch(error => {
    // 处理失败情况
  });

async/await:ES6 还引入了 async 和 await 关键字,用于更简单地处理 Promise。async 用于定义一个异步函数,而 await 用于等待一个 Promise 完成,并返回其结果。

async function myAsyncFunction() {
  try {
    const result = await myPromise;
    // 处理成功情况
  } catch (error) {
    // 处理失败情况
  }
}

手写:防抖函数(好像里面的this指向会有问题

function debounce(func, delay) {
  let timeoutId;
  
  return function (...args) {
    clearTimeout(timeoutId); // 清除之前的定时器
    
    timeoutId = setTimeout(() => {
      func.apply(this, args); // 执行传入的函数
    }, delay);
  };
}

js类型判断

typeof 操作符:typeof 操作符返回一个表示值类型的字符串。它通常用于判断原始数据类型(如字符串、数字、布尔值、undefined、symbol)。

typeof 42; // "number"
typeof "Hello"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof Symbol("example"); // "symbol"

需要注意的是,typeof null 返回 “object”,这是一个历史性的错误,但在实际开发中要注意这一点。

instanceof 操作符:instanceof 操作符用于检查对象的原型链,判断一个对象是否是某个构造函数的实例。

const arr = [1, 2, 3];
arr instanceof Array; // true

但要注意,instanceof 只适用于对象和构造函数之间的关系,不适用于原始数据类型。

constructor 属性:你可以使用对象的 constructor 属性来判断它的构造函数。

const str = "Hello";
str.constructor === String; // true

这种方法同样只适用于对象。

Object.prototype.toString.call():这是一种通用的方法,可以判断任何 JavaScript 值的类型。

Object.prototype.toString.call(42); // "[object Number]"
Object.prototype.toString.call("Hello"); // "[object String]"
Object.prototype.toString.call(true); // "[object Boolean]"

使用这种方法可以精确地判断值的类型。

Array.isArray():用于检查一个值是否是数组类型。

Array.isArray([1, 2, 3]); // true
Array.isArray("Hello"); // false

页面间通信

使用 URL 参数

  • 你可以在不同页面之间通过 URL 参数传递数据。在一个页面的 URL 中附加参数,然后在另一个页面中解析 URL 来获取这些参数。
  • 示例:page2.html?param1=value1&param2=value2

使用 Cookie

  • Cookie 是存储在用户计算机上的小型文本文件,可以用于在不同页面之间传递数据。
  • 通过设置 Cookie,在一个页面上存储数据,然后在另一个页面上读取它。
  • 示例:
    // 设置 Cookie
    document.cookie = "username=John Doe; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/";
    
    // 读取 Cookie
    const username = document.cookie.split(";")[0].split("=")[1];
    

使用 Web Storage

  • Web Storage API 包括 localStorage 和 sessionStorage,它们允许你在不同页面之间存储数据。
  • localStorage 存储的数据在不同页面间共享,而 sessionStorage 只在同一个浏览器窗口或标签页中共享。
    示例:
    // 存储数据
    localStorage.setItem("key", "value");
    
    // 读取数据
    const data = localStorage.getItem("key");
    

使用 PostMessage

  • window.postMessage() 方法允许你在不同窗口或 iframe 之间安全地发送消息。
  • 示例:
    // 发送消息
    window.parent.postMessage("Hello from child window!", "*");
    
    // 接收消息
    window.addEventListener("message", function(event) {
      if (event.origin === "https://parent.com") {
        console.log("Received message: " + event.data);
      }
    });
    

使用服务器

  • 可以将数据发送到服务器,然后从不同的页面向服务器请求数据。这是一种在不同页面之间共享数据的常见方法。
    使用 AJAX、Fetch API 或 WebSocket 可以从服务器获取数据。

点击按钮,然后页面上会弹窗。这期间的浏览器都做了什么

当用户点击一个按钮并触发与浏览器渲染相关的事件时,涉及到多个步骤,这些步骤可以简要概括如下:

用户点击按钮:用户在页面上点击按钮,触发一个用户交互事件,比如点击事件(click)。

事件处理器执行:在 HTML 或 JavaScript 中,你可以为按钮或其他元素添加事件处理器(例如,onclick 属性或 addEventListener 方法)。当用户点击按钮时,与按钮相关的事件处理器将被执行。

事件冒泡或捕获:如果有多个元素嵌套在一起,事件可能会在它们之间冒泡或捕获。这是 DOM 事件传播模型的一部分,浏览器会根据事件的冒泡或捕获阶段触发相应的事件处理器。

执行 JavaScript 代码:事件处理器可能包含 JavaScript 代码,该代码会在按钮点击时执行。这可以包括修改页面的 DOM 结构、发送 AJAX 请求、更新变量等。

浏览器渲染更新:如果 JavaScript 代码修改了页面的内容或样式,浏览器会触发重新渲染页面。这通常包括以下步骤:

  • 计算样式:浏览器计算页面元素的样式。
  • 布局:浏览器确定页面上元素的位置。
  • 绘制:浏览器将页面元素绘制到屏幕上。

重绘和重排:在重新渲染页面时,浏览器可能需要进行重绘(Repaint)和重排(Reflow)操作。重绘是在不改变页面布局的情况下更新元素的可视外观,而重排是在页面布局发生改变时重新计算元素的位置和大小。

呈现到屏幕:最终,浏览器将更新后的页面呈现到用户的屏幕上。

用户看到更新:用户最终会看到页面上的更新内容。

这个过程中,浏览器的性能和优化扮演着重要的角色,因为频繁的重新渲染和重排操作可能导致性能问题。因此,在编写 JavaScript 代码时,应该尽量减少对 DOM 的频繁操作,以提高页面性能。此外,使用浏览器的开发者工具可以帮助你分析和优化渲染性能。

重绘和重排的区别

绘(Repaint)和重排(Reflow)是浏览器渲染引擎执行的两种不同的操作,它们会影响网页性能。理解它们之间的区别很重要,以便更好地优化网页性能。

重排(Reflow)

  • 重排是指浏览器需要重新计算并确定页面上元素的位置和大小的过程。
  • 当页面结构发生变化、元素的样式发生变化、窗口大小改变或者字体大小改变时,都可能触发重排。
  • 重排是一项耗费计算资源的操作,因为它涉及到重新计算并布局页面上的所有元素。
  • 重排的代价较高,因此需要谨慎处理,以避免触发不必要的重排。

重绘(Repaint)

  • 重绘是指浏览器需要重新绘制页面上的元素的可视外观,但不涉及页面布局的变化。
  • 当元素的样式属性(如颜色、背景颜色、边框等)发生变化时,可能触发重绘。
  • 重绘的代价相对较低,因为它只涉及元素的可视外观,而不需要重新计算布局。

重排的性能开销包括什么

重排(Reflow)是浏览器重新计算和确定页面上元素的位置和大小的过程,它的性能开销相对较高,因为涉及到多个计算和操作步骤。重排的性能开销包括以下几个方面:

布局计算:浏览器需要计算和确定元素的位置、大小和布局。这包括计算元素的宽度、高度、边距、内边距等属性,以及它们相对于父元素和其他元素的位置。

元素的重新排列:当一个或多个元素的位置或大小发生变化时,浏览器需要重新排列这些元素,以确保它们按正确的顺序显示在页面上。

样式计算:重排可能会导致涉及元素的样式属性(如颜色、字体、背景、边框等)的重新计算。这些属性的计算也会增加性能开销。

页面重绘:重排通常伴随着页面的重绘(Repaint),浏览器需要重新绘制页面上受影响的部分。这包括清除旧的可视外观,并根据新的布局信息绘制元素。

样式和布局的传播:重排可能会触发其他元素的样式和布局计算,因为元素之间的相互关系可能会改变。

重排的传播:重排通常会从触发元素开始,向上或向下传播,可能会影响到整个文档树的部分或全部元素。

I/O 操作:在进行某些重排操作时,浏览器可能需要从硬盘或网络获取一些信息,这可能涉及到 I/O 操作,增加了延迟。

因此,重排是一项性能开销较高的操作,应该尽量避免在页面上频繁触发。为了降低重排的开销,可以采取以下一些优化策略:

  • 使用 CSS 动画或过渡来代替 JavaScript 动画,因为它们不会触发重排。
  • 使用 CSS transform 和 opacity 属性来实现动画效果,因为它们不会触发重排。
  • 批量操作 DOM 元素,减少对 DOM 的频繁操作。
  • 缓存布局信息,避免重复查询元素的布局属性。
  • 使用文档片段(Document Fragments)来批量操作 DOM 元素。
  • 使用虚拟滚动或分页加载等技术来减少页面上元素的数量,降低重排的频率。

怎么衡量重排的时间

要衡量重排(Reflow)的时间,你可以使用浏览器的开发者工具(DevTools)来进行性能分析。浏览器的开发者工具提供了一些有用的工具和指标,帮助你测量和分析页面上重排的时间以及其他性能相关的信息。

使用 Performance 面板

  • 打开浏览器的开发者工具(通常是按F12或右键单击页面并选择"检查")。
  • 转到 Performance 面板。
  • 点击录制按钮(通常是一个圆形红色按钮)来开始记录性能信息。
  • 进行与页面上可能的重排相关的交互,例如改变元素的大小、位置或样式。
  • 停止录制性能信息。
  • 在记录的事件时间轴上,你可以看到重排事件,包括其发生的时间、持续时间等信息。

使用 Timeline 面板(特定于 Chrome 开发者工具):

  • 打开 Chrome 的开发者工具。
  • 转到 Timeline 面板。
  • 开始记录事件,然后执行可能触发重排的操作。
  • 停止记录并查看时间轴上的事件,你可以找到重排事件并查看其详细信息。

使用 JavaScript 高分辨率时间戳

  • 你可以使用 JavaScript 中的 performance.now() 方法来记录代码执行时间。
  • 在执行可能引起重排的 JavaScript 代码前后分别调用 performance.now(),然后计算两者之间的时间差,从而得到代码执行的时间。
    const startTime = performance.now();
    // 执行可能触发重排的代码
    const endTime = performance.now();
    const duration = endTime - startTime;
    console.log(`重排时间: ${duration} 毫秒`);
    

手写:下一个更高温度出现在几天后(只讲了一下思路

单调栈实现。这个函数遍历整个温度数组,使用一个单调递减的栈来维护未找到更高温度的日期的索引。当找到更高温度的日期时,栈中的元素出栈,并计算它们距离当前日期的天数,然后更新结果数组。最终,结果数组中的值表示每一天距离下一个更高温度的天数。如果没有更高温度的日期,则对应位置的值为0。

function dailyTemperatures(temperatures) {
  const n = temperatures.length;
  const stack = [];
  const answer = new Array(n).fill(0);

  for (let i = 0; i < n; i++) {
    while (stack.length > 0 && temperatures[i] > temperatures[stack[stack.length - 1]]) {
      const prevIndex = stack.pop();
      answer[prevIndex] = i - prevIndex;
    }
    stack.push(i);
  }

  return answer;
}

// 示例用法
const temperatures = [73, 74, 75, 71, 69, 72, 76, 73];
const result = dailyTemperatures(temperatures);
console.log(result); // 输出 [1, 1, 4, 2, 1, 1, 0, 0]

移动端是如何做适配的

在移动端开发中,页面的适配是非常重要的,因为不同移动设备具有不同的屏幕尺寸、分辨率和像素密度。以下是一些常见的移动端页面适配方法:

使用视口标签

  • 在 HTML 文档的 部分添加视口(viewport)标签,以确保页面能够根据设备屏幕自动调整布局。

  • width=device-width 表示页面宽度与设备宽度一致,initial-scale=1.0 表示初始缩放级别为1。

    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    

使用百分比布局

  • 尽量使用百分比来设置元素的宽度和高度,而不是固定像素值。这样可以确保页面在不同屏幕尺寸下能够自适应布局。

媒体查询(感觉是PC端的):

  • 使用 CSS 媒体查询来根据不同的屏幕尺寸应用不同的样式。这允许你为不同的设备或屏幕宽度定义不同的样式规则。
    @media screen and (max-width: 768px) {
      /* 在小屏幕下应用的样式 */
    }
    

弹性布局(Flexbox)和网格布局(Grid)

  • 使用 CSS Flexbox 和 Grid 布局来创建灵活的、自适应的页面布局。它们允许你更轻松地控制页面中的元素排列方式。

字体适配

  • 使用相对单位(如em、rem)来设置字体大小,以确保在不同屏幕分辨率下字体大小保持合适。
  • 可以使用 CSS3 中的 @font-face 来引入自定义字体,以确保在各种设备上字体渲染一致。

图片适配

  • 使用响应式图片技术,根据不同屏幕分辨率加载不同尺寸的图片,以减少页面加载时间和带宽消耗。
  • 使用 srcset 和 sizes 属性来设置不同尺寸的图片源。

测试和调试

  • 使用模拟器或真实移动设备来测试页面的适配性。
  • 使用浏览器开发者工具中的移动设备模式来模拟不同的移动设备。
  • 检查页面在不同设备和屏幕尺寸下的显示效果,确保页面布局和样式正确。

第三方框架和库

使用流行的移动端开发框架和库,如Bootstrap、Foundation、Materialize等,它们提供了一些现成的组件和样式,可以加速移动端页面的开发和适配。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值