前端冗杂知识


1.BFF

Back-end for Front-end,服务于前端的后端

BFF就是老生长谈的中间层概念,初衷是在后台服务与前端之前添加一层,比如在中间加上一个node.js,做到请求的转发 和 数据的转换,Nodejs既配合了前端技术栈,也更适应向微服务的并发请求

BFF作为中间层,优点是:

  • 前后端彻底分离,即便是后期有微服务迁移,也不需改动前端代码
  • 业务更向前靠拢,琐碎的api由前端开发自己决定,更适配前端框架
  • BFF可以自开mock,插件也能生成API文档,相比后端单开这类服务要方便些吧
  • 留给后端更清晰的服务边界,只需要提供粗粒度的接口即可

缺点:

  • 中间层转发会增加请求延迟。
  • 需要保证端到端测试
  • 必须随时准备好后端异常请求
  • BFF分成会增加开发成本

资料来源:https://www.jianshu.com/p/9cca72f9e93c

2.VSCode编译

Emmet语法插件于市面上大多前端编译软件,如VScode所带有的,便于快速写html和CSS

网页移动端适配,开启理想视口,用于做移动端网页的适配

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

h5

想要生成多个标签:div*10,再回车(或者tab),即可生成10个div标签。

生成父子关系标签:div>span,再回车(或者tab),即可生成包含span标签的div标签。

生成兄弟关系标签:div+span,再回车(或者tab)。

输入:.nav 直接生成一个class=“nav”的div标签。(默认div)

输入:#nav 直接生成一个id=“nav”的div标签。

输入:p.nav 直接生成一个class=“nav”的p标签。

输入:.nav$*5 直接生成5个class=“nav1”,class=“nav2”…..的div标签。($是自增符号)

输入:div{前端真好玩}*5 直接生成5个<div> 前端真好玩 </div>

CSS样式内

输入:tac 直接生成text-align: center; 其他语法缩写同样类似,诸如w100—>width: 100

格式化

直接鼠标右键,格式化文档,即可直接让文档变整齐。

(或者想要每次保存后自动格式化,需要在首页—首选项—设置—搜索emmet.include,在setting.json下的用户,添加 “editor.formatOnType”: true, “editor.formatOnSave”: true 即可)

又或者

  • On Windows Shift + Alt + F
  • On Mac Shift + Option + F
  • On Ubuntu Ctrl + Shift + I

生成代码片段

snippet模板网站

https://snippet-generator.app/

然后再vscode中 shift+control+P输入snippet进行代码片段配置,这样我们就可以直接输入我们的前缀(prefix)拿我们的代码片段模板了!

使用Prettier自动格式化

Prettier是一个强大的代码格式化工具

方法一:

在vscode中安装Prettier插件

项目中新生成 .prettierrc文件 (Prettier runtime compiler)

{
  "semi": false,
  "singleQuote": true,
  "trailingComma": "none"
}
  • useTab:使用tab/空格缩进
  • tabWidth:tab是空格的情况下,空格数
  • printWidth:当行字符长度。推荐80
  • singleQuote:使用单引号还是双引号
  • trailingComma:多行输入时,尾部逗号是否添加
  • semi尾部是否添加分号
  • endOfLine:结束行形式

prettier格式化参考表格https://juejin.cn/post/6938687606687432740

一般我们还会给Prettier添加忽略文件

此时在根目录下新建 .prettierignore 文件

/dist/*
.local
.output.js
/node_modules/**

**/*.svg
**/*.sh

/public/*

在设置中 save 中设置保存时自动化

在编辑器中右键有自动格式化选项

方法二:

直接在项目中安装

npm i prettier -D

依旧在根目录下新建文件.prettierrc文件 (Prettier runtime compiler),并且编辑保存

此时在根目录下,对文件保存即可

当然方法一、二一步一步保存嫌麻烦,也可以在 package.json文件下实行一步保存命令的配置

"script": {
    "prettier": "prettier --write ."
}

jsx and tsx

对于tsx、jsx文件,在vscode中Prettier默认不启用

需要在vscode中的setting.json中配置(通过Ctrl+Shift+P 打开 setting.json)

{
  "[typescriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
},
  "[javascriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
},
}

3.RPC

RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。

  • 首先,要解决通讯的问题,主要是通过在客户端和服务器之间建立TCP连接
  • 第二,要解决寻址的问题,连接到对应的服务器(如主机或IP地址)以及特定的端口
  • 两台服务器之间以 序列化传输数据 + 反序列化得到数据结果

以上是它们连接的建立和数据的传输过程

远程调用的问题

  • Call ID映射。我们怎么告诉远程机器我们要调用Multiply,而不是Add或者FooBar呢?在本地调用中,函数体是直接通过函数指针来指定的,我们调用Multiply,编译器就自动帮我们调用它相应的函数指针。但是在远程调用中,函数指针是不行的,因为两个进程的地址空间是完全不一样的。所以,在RPC中,所有的函数都必须有自己的一个ID。这个ID在所有进程中都是唯一确定的。

  • 序列化和反序列化问题

  • 网络传输问题

RPC的大项目适用性:

做一个访问量不大的项目的时候,一台服务器部署上一个应用+数据库也就够了.

访问量稍微大一点之后呢,需要:集群.架设nginx,部署多个服务

再大一点,数据库已经扛不住了,有时候宕机. 那这个时候呢我们需要数据库读写分离,再架设几台数据库服务器,做主从,做分库分表。

再大一点,我们需要拆分模块,拆分项目,比如订单系统分一个项目,但是还是需要切换顶层的nginx.把要重启的服务的流量切到可用服务上. 这个时候就可以使用RPC

所有的服务在启动的时候注册到一个注册机里面,然后顶层处理在接收到nginx的请求时,去注册机找一个可用的服务,并调用接口. 这样子呢,在不加新功能的时候,顶层处理服务我们就不需要动了,那修改了用户信息项目的时候,我们只需要一个个更新用户信息项目的服务群就好了。

部分观点源自https://www.zhihu.com/question/25536695

Native Modules

官方解释

有时,React Native 应用程序需要访问 JavaScript 中默认不可用的原生平台 API,例如用于访问 Apple 或 Google Pay 的原生 API。也许你想重用一些现有的 Objective-C、Swift、Java 或 C++ 库,而不必在 JavaScript 中重新实现它,或者为图像处理之类的事情编写一些高性能、多线程的代码。

NativeModule 系统将 Java/Objective-C/C++(本机)类的实例作为 JS 对象公开给 JavaScript(JS),从而允许您从 JS 内执行任意本机代码。虽然我们不希望此功能成为通常开发过程的一部分,但它的存在至关重要。如果 React Native 没有导出你的 JS 应用程序需要的原生 API,你应该能够自己导出它!

所以我们可以在RN的项目里中看到导入RPC数据使用的是

import {NativeModules} from 'react-native';
const NERCTAppContextModule = NativeModules.NERCTAppContextModule || {};
// iOS审核屏蔽功能
const {
    source = ''
} = NERCTAppContextModule;

4.Worker

4.1 Web Works

有时候我们需要在客户端进行大量的运算,但又不希望来阻塞我们js主线程

我们可能第一时间考虑的是异步,但是实际上运算量大、运算时间长的任务同样会阻塞我们异步的事件循环,甚至会导致浏览器的假死状态

此时html5的新特性:Web Works就派上用场,它允许我们把如此复杂的逻辑放在浏览器的后台线程中进行处理,让js不阻塞ui线程的渲染

//index.js
const worker = new Worker(new URL('./worker.js', import.meta.url))
worker.postMessage({
    question: 'hi worker线程,请告诉我今天的幸运数字是啥'
})
worker.onmessage = (message) => {
    console.log('今天幸运数字是:', message.data.answer)
}
//worker.js
self.onmessage((message) => {
  self.postMessage({
    answer: 111
  })
})

4.2 HTML5中跨文档消息传递

跨文档消息传送(cross-document messaging),有时候也简称为XDM,指的是来自不同域的页面间传递消息。

着是一个bomapi, MDN:window.postMessage

XDM的核心是postMessage()方法,实际上我们可以使用postMessage()方法进行跨域

postMessage两个参数:

  • data:作为postMessage()第一个参数传入的字符串数据。
  • origin:发送消息的文档所在的域,例如“https://www.w3cmm.com”。
// 跨域发送数据到目标源
window.onload = function () {
    document.getElementsByTagName("iframe")[0].contentWindow.postMessage({name: 'wujiang'}, "http://localhost:8090")
}
// 监听目标源发送过来的数据
window.addEventListener('message', function (e) {
    if(event.origin=="http://localhost:8080"){//注意origin是发送消息的文档所在的域。
        console.log('http://localhost:8090/a.ftl ===> http://localhost:8080/index.jsp')
        console.log(e)
    }
})

4.3 Service Worker

概述

Service workers 本质上充当 Web 应用程序、浏览器与网络(可用时)之间的代理服务器。这个 API 旨在创建有效的离线体验,它会拦截网络请求并根据网络是否可用采取来适当的动作、更新来自服务器的的资源。它还提供入口以推送通知和访问后台同步 API。(在webpack中使用service worker需要安装WorkboxPlugin

相当于一个服务器与浏览器之间的中间人角色,一个超级拦截器

细节

  • 基于web worker
  • 在web worker的基础上增加了离线缓存的能力
  • 可以拦截全站的请求,并作出相应的动作->由开发者指定的动作
  • 由事件驱动的,具有生命周期
  • 可以访问cache和indexDB
  • 支持推送
  • 并且可以让开发者自己控制管理缓存的内容以及版本
  • 可以通过 postMessage 接口把数据传递给其他 JS 文件

特点

这个 API 之所以令人兴奋,是因为它可以支持离线体验,让开发者能够全面控制这一体验。(实际上的缓存的效果)

在 Service Worker 出现前,存在能够在网络上为用户提供离线体验的另一个 API,称为 AppCache。 AppCache API 存在的许多相关问题,在设计 Service Worker 时已予以避免。

比如:我们可以以网易新闻的wap页为例,其针对不怎么变化的静态资源开启了sw缓存(例子在里面的第六节部分) https://mp.weixin.qq.com/s/3Ep5pJULvP7WHJvVJNDV-g

使用Service Worker

使用条件:

传输协议必须为 HTTPS。因为 Service Worker 中涉及到请求拦截,所以必须使用 HTTPS 协议来保障安全。

在Firefox浏览器的用户隐私模式,Service Worker不可用。

  1. 注册Service Worker(通过 .register方法进行注册,通过 .then方法进行回调)
  2. 监听install事件,缓存需要的文件(文件过大,或者一些404时,insall会报错,做好容错处理)
  3. 拦截请求查看是否已经有需要的数据
// index.js
/* 判断当前浏览器是否支持serviceWorker */
if (navigator.serviceWorker) {
  navigator.serviceWorker
    .register('sw.js')
    .then(function(registration) {
      console.log('service worker 注册成功') //服务器被停了之后仍然会打印出来
    })
    .catch(function(err) {
      console.log('servcie worker 注册失败')
    })
}

sw内可以使用self也可以使用this,每个sw仅会安装一次,除非发生更新

//sw.js
//监听 `install` 事件,回调中缓存所需文件
//event.waitUntil 你可以理解为 new Promise,
//它接受的实际参数只能是一个 promise,因为,caches 和 cache.addAll 返回的都是 Promise,
this.addEventListener('install', e => {
  e.waitUntil(
    caches.open('my-cache').then(function(cache) {
      return cache.addAll(['./index.html', './index.js'])  //资源列表
    }).catch((e) => {
       console.warn('err', e);
    })
  )
})

// 拦截所有请求事件
// 如果缓存中已经有请求的数据就直接用缓存,否则去请求数据
this.addEventListener('fetch', e => {
  e.respondWith(
      caches.open('my-cache').then((cache) => {
          return cache.match(e.request.url)
      })
  )

此时,我们可以在chrome的F12开发者模式中查看 Application 看到 Service Worker 已经启动了

在 Cache 中也可以发现我们所需的文件已被缓存

该示例部署到服务器上之后,用户第一次打开index页面,仍然会从服务器上拉取,之后便去安装Service Worker,执行Service Worker中的install事件,浏览器会再次拉取需要缓存的资源,这一次的缓存是否从网络拉取取决于资源设置的过期时间。当install事件中的资源均拉取成功,Service Worker算是安装成功。如果有一个资源拉取失败,此次Service Worker安装失败,若用户下次再打开该页面,浏览器仍然会重复之前的安装流程尝试安装。

如果index页面的Service Worker安装成功,用户再次打开index页面发起的资源请求便会先经过Service Woker脚本的fetch事件,在该事件中前端开发可以通过编写逻辑控制请求从网络拉取还是从cache中读取或者自己构造一个response丢给前端。

推送消息

if ('serviceWorker' in window.navigator) {
  navigator.serviceWorker.register('sw.js', { scope: './' })
    .then(function (reg) {
      console.log('success', reg);
      navigator.serviceWorker.controller && 
        navigator.serviceWorker.controller.postMessage("hello im page");
    });
}
//sw.js
this.addEventListener('message', function (event) {
  console.log(event.data);
})

接收信息

//sw.js
this.addEventListener('message', function (event) {
  event.source.postMessage('我是 sw 将发送信息到 page');
});
if ('serviceWorker' in window.navigator) {
  navigator.serviceWorker.addEventListener('message', function (e) {
    console.log(e.data);
  });
}

更多通讯方式可以查看https://www.lmcc.top/articles/73.html(包含跨端通讯、两个sw服务通讯)

激活service worker

但是由于Service Worker 的激活是异步的,因此首次注册 Service Worker 的时候可能Service Worker 不会被立刻激活, reg.activeNull,系统就会报错。
这个时候我们可以采用Promise内部轮询逻辑进行处理如果 Service Worker 已经被激活那就resolve

if ('serviceWorker' in window.navigator) {
    navigator.serviceWorker.register('sw.js')
    .then(function (reg) {
      return new Promise((resolve, reject) => {
        const interval = setInterval(function () {
          if (reg.active) {
            clearInterval(interval);
            resolve(reg.active);
          }
        }, 50)
      })
    }).then(sw => {
      sw.postMessage("im sw");
    })

  navigator.serviceWorker.register('sw2.js')
    .then(function (reg) {
      return new Promise((resolve, reject) => {
        const interval = setInterval(function () {
          if (reg.active) {
            clearInterval(interval);
            resolve(reg.active);
          }
        }, 50)
      })
    }).then(sw => {
      sw.postMessage("im sw2");
    })
}

生命周期

首次加载

  1. 注册(register)
  2. 安装(installing)
  3. 活动(activated)或者异常(error)
    • 可以更新存储在Cache中的key和value
  4. 空闲(idle)
  5. 拦截(fetch)或终止(terminated)
    • fetch是最重要的一个阶段,拦截代理所有指定的请求,并进行对应的操作

更新加载

  1. 更新(update)
  2. 安装(installing)
  3. 等待活动(waiting)或者异常(error)

生命周期流程图(生命周期章节)(十分推荐!条理清晰)https://xie.infoq.cn/article/d6db2099c0064563a403c51ec

注意的点

1、Service Worker线程运行的是js,有着独立的js环境,不能直接操作DOM树,但可以通过postMessage与其服务的前端页面通信。

2、Service Worker服务的不是单个页面,它服务的是当前网络path下所有的页面,只要当前path 的Service Worker被安装,用户访问当前path下的任意页面均会启动该Service Worker。当一段时间没有事件过来,浏览器会自动停止Service Worker来节约资源,所以Service Worker线程中不能保存需要持久化的信息。

3、Service Worker安装是在后台悄悄执行,更新也是如此。每次新唤起Service Worker线程,它都会去检查Service Worker脚本是否有更新,如有一个字节的变化,它都会新起一个Service Worker线程类似于安装一样去安装新的Service Worker脚本,当旧的Service Worker所服务的页面都关闭后,新的Service Worker便会生效。

更多

service worker 框架库由于google推出直接编写原生sw比较繁琐和复杂所以一些工具框架就出现了,而Workbox相对来说较为优秀

Workbox 之前,GoogleChrome 团队较早时间推出过 sw-precache 和 sw-toolbox 库,但是在 GoogleChrome 工程师们看来,workbox 才是真正能方便统一的处理离线能力的更完美的方案,所以停止了对 sw-precache 和 sw-toolbox 的维护。

参考:

https://zhuanlan.zhihu.com/p/115243059

https://x5.tencent.com/product/service-worker.html

https://mp.weixin.qq.com/s/3Ep5pJULvP7WHJvVJNDV-g

https://www.lmcc.top/articles/73.html

https://xie.infoq.cn/article/d6db2099c0064563a403c51ec

5.GIF上报埋点

使用GIF上报的原因:

  • 图像类可以解决跨域的问题
  • 防止阻塞页面的加载
  • 相比png/jpg,gif体积最小,最小的bmp文件需要74B,png需要67B,而合法的gif只需要43B

用gif报埋点大多数采用1*1透明GIF上报

6.jsonserver

可以用于临时模拟后端接口数据,只需要一个json文件即可(jsonserver内置帮你解决了跨域问题,设置了)

使用流程

Install JSON Server

npm install -g json-server

Create a db.json file with some data

{
  "posts": [
    { "id": 1, "title": "json-server", "author": "typicode" }
  ],
  "comments": [
    { "id": 1, "body": "some comment", "postId": 1 }
  ],
  "profile": { "name": "typicode" }
}

Start JSON Server

json-server --watch db.json

或者指定好端口号

json-server --watch db.json --port 8000

Now if you go to http://localhost:3000/posts/1, you’ll get

{ "id": 1, "title": "json-server", "author": "typicode" }

增删改查

此时你还可以通过设置请求的method,来增删改查

get:

http://localhost:8000/posts/1
http://localhost:8000/posts?id=1

直接查询到id为1的接口数据(自动检索 )

增加数据 post

axios.post("http://localhost:8000/posts", {
    { "id": 2, "title": "json-server", "author": "typicode" }
})

修改数据 put、patch

axios.patch("http://localhost:8000/posts/1", {
    { "title": "json-server" }
})

删除数据 delete

在删除数据的时候,若对于不同的数据表,有关联的数据也会一并删除

比如

{
  "posts": [
    {
      "id": 1,
      "title": "json-server",
      "author": "typicode"
    },
    {
      "id": 2,
      "title": "other",
      "author": "othercode"
    }
  ],
  "comments": [
    {
      "id": 1,
      "body": "some comment",
      "postId": 1
    }
  ],
}

此时 comments 第一个数据有一个字段是 postId,会和 posts中id为1的数据进行关联,此时删除掉posts的第一条数据,则连带comments的数据也会被删除掉

关联数据

上面再2删除数据中我们可以看到有部分的数据是关联的

如果我们想要在get的时候,带上关联的数据,我们要用 _embed 或者 _expand 字段

_embed :获取向下关联的数据

_expand :获取向上关联的数据

//关联的表是comments
axios.post("http://localhost:8000/posts?_embed=comments").then(res => {
    console.log(res)
})
//字段是postId,所以要用post,而不是posts
axios.post("http://localhost:8000/comments?_expand=post").then(res => {
    console.log(res)
})

7.网络基础知识

区块链

区块链我个人认为就是一个去中心化的一个数据存储中心,里面记录了每个人是什么、拥有什么,并不被某一公司所垄断,而是由世界上每个人通过互联网来维护的存储中心(记账本)

web3

Web 1.0 阶段,用户是单纯的内容消费者,内容由网站提供,网站让你看什么,你就看什么,典型例子就是新闻门户网站。

Web 2.0 阶段,用户是内容的生产者,网站只是一个向用户提供服务的平台,典型的 Web 2.0 平台有维基百科、抖音、微信等等。

Web3 的很多特征还不明确,但是国外很多文章认为,它跟区块链有关。(通过区块链,我们在虚拟世界中得以生存,无法被某个强大的集团 / 公司 垄断)

Web 1.0 是用户读取互联网,Web 2.0 是用户写入互联网,Web3 是用户生活在互联网

web3就是所有权

参考:

https://www.ruanyifeng.com/blog/2021/12/web3.html

https://pizzaparty.substack.com/p/the-metaverse-abstracted-reality

8.前端发展史

https://developer.51cto.com/article/712519.html


文章作者: Hello
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Hello !
 上一篇
CSS样式 CSS样式
声明:可以使用chrome的F12浏览学习别人的样式并且调试你的代码 引入CSS/*1)link的写法:*/ <link rel="stylesheet" href="index.css"> /*2)import的写法:*/ <
2020-05-10
下一篇 
主题与2d人物 主题与2d人物
Start人物使用命令安装npm:npm uninstall hexo-helper-live2d npm install –save hexo-helper-live2d此时在blog/node_modeles下
2020-05-06 Hello
  目录