hero image

功不唐捐 日拱一卒

Just Do It

Tailwind 快速入门

前言:在接触 Tailwind 的刚开始,并没有感受到它的好处,反而觉得这是一种非常繁琐的事情,非常不适应。为了更好的使用 Tailwindcss,便有了该系列:梳理总结使用规律和用法。

章节系列共分为 7 个小节,每小节开头介绍使用规律,再介绍具体使用方法,各自小节独立可依照需求进行查阅。

提示

Unocss 兼容 Tailwind,因此仅需学习 Tailwind 的用法即可。


Huy大约 2 分钟CSSCSS
脚本加载失败如何重试

线上的脚本在用户端加载失败,如何重试?普通的解决办法是重新加载页面: 最简单的方法是要求用户手动刷新页面。

但是这只能在用户端网络不好的情况刷新有效,若是,或者脚本本身存在问题,用户手动刷新页面可能无法解决问题。这种情况下,需要将脚本地址指向备用地址,然后进行重试。

设计难点

对于该问题实际上有以下几个方面需要进行考虑。

  1. 脚本加载失败如何判断?
  2. 脚本加载失败什么时候进行重试?
  3. 脚本如何重试?

解决方案

  1. 脚本加载失败如何判断?

    可以通过 window.onerror 来判断脚本加载失败。但是需要做错误处理, 因为除了脚本加载错误外, 还有可能是其他错误。

    window.addEventListener(
      'error',
      (e) => {
        // 判断是否为脚本加载错误
        if (e.target.tagName !== 'SCRIPT' || e instanceof ErrorEvent) return
        console.log('捕获到错误', e)
      },
      true // 捕获时生效
    )
    
  2. 脚本加载失败什么时候进行重试?

    在上述代码中, 我们使用了捕获时监听错误, 这是由于像 throw error 这样的错误是不会冒泡的, 所以需要在捕获时期进行监听。

    此外, 事件的监听应当在脚本加载前执行,只是因为 js 是单线程的,如果脚本先加载,产生错误便不会继续进行执行了。

  3. 脚本如何重试?

    光重试还不行, 应该对脚本设置多个域名, 进行备份, 当一个脚本加载失败后, 还可以尝试加载其他域名下的同脚本。因此,可以用一个数组来保存多个脚本地址。

    其次,重试过程中,还需要保证脚本发生阻塞,以防止后续加载的脚本对重试的脚本产生依赖。

    最终代码:

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>脚本加载错误</title>
      </head>
      <body>
        <script>
          const domains = ["test1new.com", "test2new.com", "test3new.com"];
          const retryDomain = {}; // 重试所用域名
    
          window.addEventListener(
            "error",
            (e) => {
              if (e.target.tagName !== "SCRIPT" || e instanceof ErrorEvent) return;
    
              console.log("捕获到错误", e);
              const url = new URL(e.target.src);
              const key = url.pathname;
    
              if (!(key in retryDomain)) {
                retryDomain[key] = 0; // 初始重试所用域名序号
              }
              const domain = domains[retryDomain[key]];
              url.host = domain; // 设置为新的域名
    
              // 创建新的 script 标签, 但这样加载并不会产生阻塞
              // const newScript = document.createElement("script");
              // newScript.src = url.toString();
              // document.body.insertBefore(newScript, e.target);
    
              // 使其保持阻塞
              document.write(`\<script src="${url.toString()}">\</script>`)
    
              retryDomain[key] = (retryDomain[key] + 1) % domains.length; // index 增加
            },
            true // 冒泡捕获
          );
        </script>
    
        <script src="https://test1.com/test.js"></script>
        <script src="https://testError.com/test.js"></script>
        <script src="https://test3.com/test.js"></script>
      </body>
    </html>
    

Huy大约 2 分钟javascriptjavascript
Scss 快速 复盘

虽然 Tailwindcss 已经较为流行,但是 Scss 还是在很多项目中使用,而一直都是轻量使用,故该文旨在快速梳理一遍 Scss 的基本用法和一些踩坑点。

基础语法

变量

$color: #fff;

Huy大约 2 分钟CSSCSS
使用 Canvas 放大图片

Canvas 除了绘制图片外,还可以对图片进行放大操作。

Canvas 图片放大示例
Canvas 图片放大示例

其放大的原理是:

  1. 获取原始图像数据: 首先,要将原始图片加载到 Canvas 上,需要获取原始图片的像素数据。这些数据包含了图片中每个像素的颜色信息。
  2. 计算放大后的尺寸: 在放大图片时,需要计算放大后的宽度和高度。放大倍数可以通过用户输入或程序预设来确定。例如,放大两倍就意味着新的图片尺寸是原始尺寸的两倍。
  3. 进行插值计算: 放大图片时,需要对每个新像素进行插值计算以确定其颜色。插值是一种技术,用于根据已知数据点的值,在两点之间估计出新点的值。最后绘制放大后的图像: 经过插值计算得到新的像素数据后,就可以将这些数据绘制到 Canvas 上,形成放大后的图像。

Huy大约 2 分钟javascriptjavascript
OpenLayers 安装

在这一节里,参照 OpenLayers 官网创建一个项目。

快速安装

  1. 环境配置: node >= 14,个人目前用的是最新的稳定版本 node@20。下载地址: 传送门 🚪node

  2. 官方提供了一个脚手架, 可以快速安装项目:

    $: npm create ol-app my-app
    $: cd my-app
    $: npm start
    
  3. 或者可以采用笔者所搭建的项目模板: 传送门 🚪


Huy大约 1 分钟框架gis
WebGis

在这一节里,回顾一遍 WeBGis 的一些相关概念。

什么是 WebGis?

WebGIS(网络地理信息系统)是指基于网络平台,客户端应用软件采用网络协议,运行在网络上的地理信息系统,即将 GIS 所能提供的功能通过网络展现给用户。

顾名思义,WebGIS 就是展现在网络上的 GIS,是 GIS 与 Web 融合的产物。GIS 通过 Web 功能得以扩展,使得 GIS 冲破专业圈子,真正成为大众化的 GIS。

什么是 OpenLayers?

OpenLayers 是一个专门为 WebGIS 客户端提供的 JavaScript 类库。用于访问以标准格式发布的地图数据,实现访问空间数据的方法都符合行业标准,支持各种公开的和私有的数据标准和资源。


Huy大约 4 分钟框架gis
Giser

在这个栏目里,开始记录一些自己的老本行 ——— GIS。

从决定做开发,到现在已经快两年了。从对前端一无所知,到现在部门的主心骨,逐渐感觉到其实自己只要愿意,什么都能学会。也是这个时候,想起了自己的老本行,想再捡起来,重新学一遍。地理信息基础什么都自然不用说,实际上都会,但是确实没有做过什么实际研发的项目。

OpenLayers

暂以 OpenLayers 重新入门。

Cesium

关于 Cesium 实际上是老朋友了, 只是读书期间对这个, 只会照着教程写, 现在前端底子打的较为扎实了, 逐渐体会到那种看山又是山的感觉。等有时间后,再做整理。


Huy小于 1 分钟Guide
实现 Promise.allSettled

在 MDN 上, 对 Promise.allSettled 的解释是: Promise.allSettled() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。

const promise1 = Promise.resolve(3)
const promise2 = new Promise((resolve, reject) =>
  setTimeout(reject, 100, 'foo')
)
const promises = [promise1, promise2]

Promise.allSettled(promises).then((results) =>
  results.forEach((result) => console.log(result.status))
)

// Expected output:
// "fulfilled"
// "rejected"

Huy小于 1 分钟javascriptjavascript
手写 Pinia

在 Vuex 中是基于 Flux 架构进行设计的,它的核心思想是单向数据流,即数据只能从 Store 向外传递到组件,组件不能直接修改 Store 中的数据,只能通过提交 Mutations 来修改。

即: view --> dispatch --> action --> mutation --> state --> view

并且 Vuex 使用一个全局的 Store 来存储应用的状态,所有组件都共享这个 Store。由此会产生较多的 modules。

而 pinia 则是将整个 store 体系进行 "拍平"。Pinia 不是使用一个全局的 Store,而是允许创建多个 Store,每个 Store 都可以独立管理自己的状态。


Huy大约 12 分钟框架Vue
React之手写 Hooks

Hooks 的好处很多,为了加强对 Hooks 的理解,手动实现一遍 React 的一些 Hooks 方法,便是再好不过的了。该文项目的源代码在 react-hooks 中。

本文中,我们统一规定 初始 App.jsx 如下:

import { root, useState, ...otherHooks } from "./React";

function App() {
  // useHook 的使用
  return ( <div>....具体代码</div> );
}

root.render(<App />);

export default App;

Huy大约 6 分钟框架React
2
3
4
5
...
14

Yesterday is history, tomorrow is a mystery, today is a gift of God, which is why we call it the present.