前端最新场景题,不来看一看吗?(二)

119 阅读9分钟

1.如何实现网页加载进度条?

考虑原生进度条。HTML5 中提供了 progress 元素,可以通过它来实现一个原生的进度条。

<progress id="progressBar" value="0" max="100"></progress>

但是!我选择自己来:

<div id="app">
  <div id="progressBar">0%</div>
</div>

 #progressBar {
    width: 0%;
    height: 30px;
    background-color: #4CAF50;
    text-align: center;
    line-height: 30px; /* Vertically center the text */
    color: white;
    transition: width 0.5s ease;
  }

监听页面加载事件和资源加载事件:

document.addEventListener("DOMContentLoaded", function() {
  let progress = 0;
  const progressBar = document.getElementById('progressBar');

  const resources = document.querySelectorAll('img, script, link[rel="stylesheet"]');
  let totalResources = resources.length;
  let loadedResources = 0;

  resources.forEach(resource => {
    resource.addEventListener('load', () => {
      loadedResources++;
      let newProgress = Math.round((loadedResources / totalResources) * 100);
      progressBar.style.width = newProgress + '%';
      progressBar.textContent = newProgress + '%';
    });
  });
});

eg:加载到100%

let progress = 0;
const progressBar = document.getElementById('progressBar');
const total = 100; // Total steps

function simulateLoading() {
  for(let i = 0; i <= total; i++) {
    setTimeout(() => {
      progress = Math.min((i / total) * 100, 100);
      progressBar.style.width = progress + '%';
      progressBar.textContent = Math.round(progress) + '%';
    }, 100 * i); // 加载时间 结束的时候完成加载
  }
}
simulateLoading();

第二种方法:有插件:npm install --save nprogress

NProgress.start() // 进度条开始
NProgress.set() // 将进度设置到具体的百分比位置,取值范围是0到1.0
NProgress.inc() // 如果少量增加进度,进度将永远不会得到100%
NProgress.done() // 进度条结束消失
NProgress.configure() // 进度条参数配置

main.js

import Vue from 'vue'
import Router from 'vue-router'
import routers from './routers'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
NProgress.configure({ showSpinner: false })

Vue.use(Router)
const router = new Router({
 mode: 'history',
 routes: routers
})

export default router
router.beforeEach((to, from, next) => {
 NProgress.start()
 next()
})

router.afterEach(() => {
 NProgress.done()
})

app.vue:
#nprogress .bar {
 background: #66B1FF !important; // 自定义颜色
 height: 2px !important; // 自定义高度
 }

2.常见的图片懒加载方式有哪几种?

1.HTML5提供的懒加载方式loading="lazy"

<img src="placeholder.jpg" data-src="image-to-lazy-load.jpg" loading="lazy" alt="Lazy Loaded Image">

2.Intersection Observer API 经典视口观察:

document.addEventListener("DOMContentLoaded", function() {
//获取所有img
  const images = document.querySelectorAll('img[data-src]');
  const imageObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const img = entry.target;
        img.src = img.getAttribute('data-src');
        imageObserver.unobserve(img);
      }
    });
  });

  images.forEach(image => {
    imageObserver.observe(image);
  });
});

3.Scroll事件监听 不太建议 消耗性能 建议加节流防抖

window.addEventListener('scroll', function() {
  const images = document.querySelectorAll('img[data-src]');
  images.forEach(img => {
    if (img.offsetTop <= window.innerHeight + window.scrollY) {
      img.src = img.getAttribute('data-src');
    }
  });
});

4Lozad.js第三方库 一行搞定

<script src="lazysizes.min.js" async=""></script>
<img data-src="image.jpg" class="lazyload" alt="image" />

or: vue-lazyload

npm install vue-lazyload--save
// main.js
import Vue from 'vue'
import App from './App.vue'
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)
//或者添加选项,例如加载时的占位图或加载失败时的图像
Vue.use(VueLazyload, {
  preLoad1.3,
  error'error.png',
  loading'loading.gif',
  attempt1
})
new Vue({
  renderh => h(App)
}).$mount('#app')

<img v-lazy="imgUrl" alt="image">

3.cookie构成部分

首先 它是什么?

HTTP Cookie(或简称Cookie)是由服务器发送到客户端浏览器并保存在客户端的一种数据。 Cookie通常用于识别用户和存储用户特定的信息,例如用户偏好、会话信息。

一个Cookie由以下几个基本部分组成:

  1. 名称(Name) :Cookie的唯一标识符,用于区分不同的Cookie。

  2. 值(Value) :与名称相对应的数据值,可以是任何字符串形式的数据。

  3. 域(Domain) :Cookie有效的域名,只有该域名下的页面才能访问该Cookie。

  4. 路径(Path) :Cookie有效的路径,只有该路径下的资源才能访问该Cookie。

  5. 过期时间(Expires/Max-Age)

    • Expires:指定一个具体的日期和时间,在此之后Cookie将不再有效。
    • Max-Age:指定从创建Cookie时开始的秒数,过期后Cookie将不再有效。
  6. 安全标志(Secure) :如果设置了Secure标志,那么Cookie只会在HTTPS连接中被发送。

  7. HttpOnly标志(HttpOnly) :如果设置了HttpOnly标志,那么Cookie不能被客户端脚本(如JavaScript)访问,这可以提高安全性,防止跨站脚本攻击(XSS)。

  8. SameSite属性:这个属性用来控制Cookie的跨站点请求,可以设置为StrictLaxNone,以防止CSRF攻击。

  9. 优先级(Priority) :某些浏览器支持设置Cookie的优先级,如LowMedium(默认)、High

  10. SameParty:这个属性可以控制跨Party的Cookie共享行为,与SameSite类似,但基于第一方域的概念。

  11. Partitioned:某些浏览器支持将Cookie分区存储,以保护用户隐私。

4.扫码登陆实现方式

就个人而言 ,这个问题更偏向与问实现逻辑。前端基本上是展示后端生成的二维码,重要的是这个查询是否登录的方式。 查询方式很多,包括,轮询(setIntervel),websocket,worker等方式。

-   **PC端**:进入二维码登录页面,请求**服务端**获取二维码的ID。

-   **服务端**:生成二维码ID,并将其与请求的设备绑定后,返回有效的二维码ID。

-   **PC端**:根据二维码ID生成二维码图片,并展示出来。

-   **移动端**:扫描二维码,解析出二维码ID。

-   **移动端**:使用移动端的token和二维码ID请求**服务端**进行登录。

-   **服务端**:解析验证请求,绑定用户信息,并返回给移动端一个用于二次确认的临时token。

-   **PC端**:展示二维码为“待确认”状态。

-   **移动端**:使用二维码ID、临时token和移动端的token进行确认登录。

-   **服务端**:验证通过后,修改二维码状态,并返回给PC端一个登录的token。

  

5.DNS协议了解多少

还是那句话 ,dns是谁,是什么?

DNS 即域名系统,全称是 Domain Name System。当我们在浏览器输入一个 URL 地址时,浏览器要向这个 URL 的主机名对应的服务器发送请求,就得知道服务器的 IP,对于浏览器来说,DNS 的作用就是将主机名转换成 IP 地址

DNS服务器

  • 根服务器:全球有13台根服务器,负责解析顶级域名(如.com、.org)。
  • 顶级域(TLD)服务器:负责解析特定顶级域下的域名。
  • 权威服务器:负责解析特定域名的服务器,通常是域名注册时指定的服务器。
  • 递归服务器:通常由ISP(互联网服务提供商)提供,负责处理用户的DNS查询请求,并将查询结果缓存以提高响应速度。

DNS查询过程

  1. 用户发起查询:用户在浏览器中输入域名。
  2. 递归服务器查询:用户的设备首先向本地DNS服务器(通常是ISP的递归服务器)发起查询。
  3. 根服务器查询:如果本地DNS服务器没有缓存结果,它会向根服务器发起查询。
  4. TLD服务器查询:根服务器返回TLD服务器的地址,递归服务器继续查询。
  5. 权威服务器查询:TLD服务器返回权威服务器的地址,递归服务器继续查询。
  6. 返回结果:权威服务器返回域名对应的IP地址,递归服务器将结果返回给用户设备。

DNS缓存

  • DNS服务器会缓存查询结果,以减少对权威服务器的查询次数,提高响应速度。缓存的有效期由TTL(Time to Live)值决定。

6.函数式编程

函数式编程是一种强调以函数使用为主的软件开发风格。 enmmm,就是说 纯函数 无副作用

核心理念包括以下几点:

  1. 纯函数:纯函数是指没有副作用并且只依赖于其输入的函数。这种函数对于相同的输入始终返回相同的输出,而且不会改变任何外部状态。
  2. 不可变性:不可变性意味着数据一旦创建就不能再被修改。在函数式编程中,我们通常创建新的数据结构来代替修改已有的数据。
  3. 函数组合:函数组合是将多个函数按照一定顺序组合起来运行的过程。通过将函数组合起来,我们可以轻松地实现复杂的功能。
  4. 递归:递归是函数式编程的重要特征之一,在函数式编程中,我们经常使用递归来处理列表和其他数据结构。
function add(a, b) { return a + b; } 纯函数

const list1 = [1, 2, 3];
const list2 = [...list1, 4]; // 创建一个新列表 不可变


function add(a, b) { return a + b; } function double(x) { return x * 2; }const addAndDouble = (a, b) => double(add(a, b)); 函数组合

function sum(list) {
  if (list.length === 0) {
    return 0;
  }
  return list[0] + sum(list.slice(1));
}

const result = sum([1, 2, 3, 4, 5]);
console.log(result); // 输出 15  递归

eg: 一般人的helloword:

document.querySelector('#msg').innerHTML = '<h1>Hello World</h1>'

不一般的人:

function printMessage(elementId, format, message) {
    document.querySelector(elementId).innerHTML = `<${format}>${message}</${format}>`
}

printMessage('msg', 'h1', 'Hello World')

大佬:

const printMessage = compose(addToDom('msg'), h1, echo)
//addToDom 是插入职责的函数
printMessage('Hello World')

将一个任务拆分成多个最小颗粒的函数,然后通过组合的方式来完成我们的任务,这跟我们组件化的思想很类似,将整个页面拆分成若干个组件,然后拼装起来完成我们的整个页面。在函数式编程里面,组合是一个非常非常非常重要的思想。

7.前端水印

-   方案一:fixed 定位的 div 元素,重复渲染 div 元素来添加水印。会创建很多无关的 DOM 元素

-   方案二:fixed 定位 canvas 元素,重复填充水印。始终会创建一个无关的 canvas 元素

-   方案三:canvas + 伪类。不会创建无关元素,且兼容性好

-   方案四:svg + 伪类。不会创建无关元素,但兼容性略差于 canvas

 

eg:

body::after {
  content: "版权所有,翻版必究";
  position: fixed;
  bottom: 10px;
  right: 10px;
  color: #ccc;
  font-size: 16px;
  opacity: 0.3;
}

or:

 <canvas id="watermark"></canvas>
 
const canvas = document.getElementById('watermark');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

ctx.font = '30px Arial';
ctx.fillStyle = 'rgba(45, 132, 216,0.6)';
ctx.textAlign = 'center';
ctx.fillText('版权所有,翻版必究', canvas.width / 2, canvas.height / 2);

or:
document.addEventListener('DOMContentLoaded', function() {
  const text = '版权所有,翻版必究';
  const waterMark = document.createElement('div');
  waterMark.style.cssText = `
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    opacity: 0.1;
    color: white;
    font-size: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
  `;
  waterMark.textContent = text;
  document.body.appendChild(waterMark);
});

or:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="0">
  <text x="50%" y="50%" fill="red" font-size="30" text-anchor="middle">版权所有1,翻版必究</text>
</svg>

当然 。花点时间写完啦:

<template>
  <div className="my-page-container" id="my-page-container">
    <div className="my-info">
      <div className="title">这是测试标题</div>
      <div className="content">
        // ...我想这是机密内容 * n
      </div>
    </div>
  </div>
</template>

<script>
  import { defineComponent, ref } from 'vue';

export default defineComponent({
  setup() {
const createImgBase = (options) => {
  const { content, canvasHeight, canvasWidth } = options;
  const canvas = document.createElement('canvas'); // 创建一个画布
  const ctx = canvas.getContext('2d');
  // 设置画布的宽高
  canvas.width = canvasHeight;
  canvas.height = canvasWidth;
  if (ctx) {
    ctx.rotate((-10 * Math.PI) / 180); // 偏移一点距离
    ctx.fillStyle = 'rgba(100,100,100,0.2)'; // 设置绘制的颜色
    ctx.font = '40px'; // 设置字体的大小
    // 遍历水印内容
    content.forEach((text, index) => {
      ctx.fillText(text, 10, 30 * (index + 1)); // 拉开30的间距
    });
  }
  return canvas.toDataURL('image/png'); // 转换程data url,可供img直接使用
};


const listenerDOMChange = (className) => {
  const targetNode = document.querySelector(`.${className}`);
  if (!targetNode) return; // 如果找不到节点,直接返回

  const observer = new MutationObserver((mutationsList) => {
    for (let mutation of mutationsList) {
      if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
        const curClassVal = targetNode.getAttribute('class') || '';
        if (!curClassVal.includes(className)) { // 使用includes来检查className是否存在
          targetNode.setAttribute('class', `${curClassVal.trim()} ${className}`);
        }
      }
    }
  });

  observer.observe(targetNode, {
    attributes: true,
    attributeFilter: ['class'], // 只观察class属性的变化
  });

  // 考虑到性能和内存泄漏问题,可以在适当的时候调用observer.disconnect()来停止观察
};
const genWaterMark = ({
  content,
  className,
  canvasHeight = 140,
  canvasWidth = 150,
}) => {
  // 监听class的变更
  listenerDOMChange(className);
 const dataURL = createImgBase({ content, canvasHeight, canvasWidth });
  const defaultStyle = document.createElement('style');
  defaultStyle.innerHTML = `.${className}::after {
     content: '';
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    background-image: url(${dataURL});
    background-repeat: repeat;
    pointer-events: none;
  }`;
  document.head.appendChild(defaultStyle);
};
genWaterMark({
      content: ['userName', 'userPhone', '内部机密材料', '严禁外泄!'],
      className: 'my-page-container',
    });
    return {
      genWaterMark,
      createImgBase,
      listenerDOMChange
    };
  },
});
</script>

<style>
  .my-page-container {
    height: calc(100vh - 104px);
    overflow: hidden;
  }

  .my-info {
    display: flex;
    flex: 1;
    flex-direction: column;
    height: 100%;
    padding: 24px;
    overflow-y: auto;
  }
</style>

效果:

image.png

8.什么是领域模型?

enmmm 好像闻所未闻。

解释一下: "领域模型"(Domain Model)是一个概念,它源自领域驱动设计(Domain-Driven Design, DDD)的理念,用于表示特定业务领域内的概念和实体。领域模型是业务逻辑和操作的核心,它帮助开发者将业务需求转化为代码结构。后端同学肯定听的更多哈、

问题处于下两个方面:

  • UI层复用性差: 前端UI代码大量采用VM(view + model)的方案,面向不同的设计稿进行开发,代码差异化无法收敛。

  • Data层复用性差: 服务端通信给前端的数据虽然已经是标准视图对象(view object),但是明显是带有鲜明的领域(Domain)属性,即不同业务不同视图对象。

我的理解是 每次业务迭代或者新项目进来 必须重复性操作,复用性和逻辑性需要从头设计。

那么领域模型就至关重要了。

主要就是解决开发效率和代码复用性。

大佬救命....🤔 补充下吧

9.一直在window上搜东西有什么风险?

en?有啥风险? 噢噢 应该是window对象吧1!

全局命名空间污染window对象是全局作用域的一部分,在它上面添加属性或方法会增加全局变量的数量,可能导致命名冲突

内存泄漏:如果在window对象上添加了引用,而这些引用没有在适当的时候被清除,可能会导致内存泄漏,尤其是在长时间运行的应用中。

性能问题:频繁地在window对象上搜索属性可能会影响性能,尤其是在属性很多的情况下,因为属性查找可能需要遍历整个原型链。

代码可维护性:依赖于全局window对象的代码通常难以维护和测试,因为它们与全局状态紧密耦合,难以模拟或替换

安全性问题:在window对象上存储敏感信息可能会增加安全风险,因为前端代码通常可以被用户或其他脚本访问和修改。

依赖注入问题:如果在window对象上动态添加依赖,可能会使得代码的依赖关系不明确,增加理解和维护的难度

框架/库的冲突:不同的前端框架或库可能会在window对象上添加自己的属性或方法,如果你的应用中使用了多个框架或库,这可能会导致冲突

测试困难:在window对象上添加的属性或方法可能会干扰测试,因为测试环境可能需要模拟或重置window对象的状态。

单页应用(SPA)问题:在单页应用中,如果在window对象上存储了状态信息,当页面重新加载或导航到不同的路由时,这些状态可能会丢失或产生意外的行为。

10.深度SEO优化方式有哪些?

1.TDK:TDK是Title(标题)、Description(描述)和 Keywords(关键词)的缩写,是网站SEO的关键 页面标题,是搜索引擎最重视的元素之一,它直接决定了页面在搜索结果中的标题显示。 避免过于冗长。

<title>标题</title>

description:页面描述,内容通常会被用作页面的摘要信息展示。不宜太短。 30《description 《160

<meta name="description" content="网站的描述"> keywords :页面关键词. 尽量不重复

<meta name="Keywords" content="网站的关键词">

  1. OG 协议

由 Facebook 提出的一种元数据标准,它允许页面成为丰富的社交对象。通过在网页中添加特定的 Open Graph 协议,可以帮助提供更丰富的预览信息。

常见的 Open Graph 标签包括:

<meta property="og:title" content="页面标题">
<meta property="og:description" content="页面描述">
<meta property="og:image" content="图片URL">
<meta property="og:url" content="页面URL">
<meta property="og:type" content="网页类型,如website,article">
<meta property="og:release_date" content="定义网页内容的发布时间">

3.HTML语义化

  1. 方便其他设备进行解析,例如盲人阅读器。
  2. 有利于SEO,搜索引擎更容易理解语义化页面的内容结构和主题。
  3. 便于团队开发和维护,语义化更具有可读性。

eg:

h1h2h3h4h5h6

strong  em

<a rel="nofollow" href="http://www.baidu.com/">百度</a>
<a rel="external" href="http://www.baidu.com/">百度</a>

<img alt="imgalt">

<p> ppppppppppppp文字描述</p>

ul:标签表示无序列表

ol:标签表示有序列表

li:标签表示列表的一项

header:定义页眉,通常包含网站标志、主导航、搜索框等

nav:定义导航

article:定义独立的、完整的内容块,如博客文章、新闻报道等

section:定义页面上的独立的、有语义的内容块,如章节

aside:定义侧边栏

footer:定义页脚,通常包含版权信息、联系方式、备案信息等

  

4.sitemap sitemap 站点地图一般是xml格式的文件,用于帮助搜索引擎更好地发现和抓取网站上的所有页面,并提供页面优先级和更新频率的信息,帮助搜索引擎更好地分配抓取资源. eg:

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
    <url>
      <loc>https://acme.com</loc>
      <lastmod>2023-04-06T15:02:24.021Z</lastmod>
      <changefreq>yearly</changefreq>
      <priority>1</priority>
    </url>
    <url>
      <loc>https://acme.com/about</loc>
      <lastmod>2023-04-06T15:02:24.021Z</lastmod>
      <changefreq>monthly</changefreq>
      <priority>0.8</priority>
    </url>
  </urlset>

5.robots 文件

用来告诉搜索引擎机器人(又称爬虫或蜘蛛)应该如何抓取和索引该网站的一种标准

robots.txt 文件需放置在网站的根目录下,以“域名+/robots.txt”的形式直接访问.

User-Agent: *
Disallow: /private/

Sitemap: https://acme.com/sitemap.xml

6.其他:

向各搜索引擎站长平台手动提交网址,以缩短爬虫发现网站链接时间,加快爬虫抓取速度。

使用服务端渲染,eg:nuxt,next

网址规范化,使用规范标准的网址。

网站打开速度越快,识别效果越好