suedar


  • Home

  • Tags

  • Categories

  • Archives

  • Commonweal 404

微前端方案调研

Posted on 2022-10-17

1, 微前端概述

巨石应用:将所有功能都部署在一个web容器中运行的系统。
微前端:微前端架构是一种类似于微服务的架构,由 ThoughtWorks 2016年提出,它将微服务的理念应用于浏览器端,即将 Web 应用由单一的单体应用转变为多个小型前端应用聚合为一的应用。
img_1.png[图片]
为了防止概念有点抽象,可以看一个具体的例子:上图是一个微前端的demo,主应用中有导航栏和左边的侧边栏组件,而右面是子应用部分,这里的子应用并没有集成在主应用中,只是通过微前端的框架内嵌到主应用中,可是给用户的感受就是一个完整的项目。

2, 微前端的应用场景

任何新技术的产生都是为了解决现有场景和需求下的技术痛点,微前端也不例外:

  1. 拆分和细化:当下前端领域,单页面应用(SPA)是非常流行的项目形态之一,而随着时间的推移以及应用功能的丰富,单页应用变得不再单一而是越来越庞大也越来越难以维护,往往是改一处而动全身,由此带来的发版成本也越来越高。微前端的意义就是将这些庞大应用进行拆分,并随之解耦,每个部分可以单独进行维护和部署,提升效率。
  2. 整合历史系统:在不少的业务中,或多或少会存在一些历史项目,这些项目大多以采用老框架类似(Backbone.js,Angular.js 1)的 B 端管理系统为主,介于日常运营,这些系统需要结合到新框架中来使用还不能抛弃,对此我们也没有理由浪费时间和精力重写旧的逻辑。而微前端可以将这些系统进行整合,在基本不修改来逻辑的同时来同时兼容新老两套系统并行运行。

微前端架构具备以下几个核心价值:

  • 技术栈无关:主框架不限制接入应用的技术栈,微应用具备完全自主权
  • 独立开发、独立部署:微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
  • 增量升级:在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的实施渐进式重构的手段和策略
  • 独立运行时:每个微应用之间状态隔离,运行时状态不共享

    3. 微前端实现方案

img.png

4. 微前端实现解析

img_2.png[图片]
微前端首先通过对主 parcel 项目进行集成,对微应用进行 umd 打包,集成到主 parcel 项目上。在运行时,首先会进行注册信息的加载,其次是主工程的初始化,监听路由变化,通过改写 history API 实现子应用的加载,从而实现子应用的挂载、卸载等。

在其中我们需要关注样式隔离、js 沙盒问题以及系统通信问题。

4.1 css 隔离

4.1.1 shadow DOM

shadow DOM 并不是一个特别新的概念,html中的video标签就是使用shadow DOM的一个案例。使用它时,你在html只会看到一个video标签,但实际上播放器上还有一系列按钮和其他操作,这些就都是封装到shadow dom中的,对外界是不可见的。所以Shadow DOM天然实现了样式隔离。

4.1.2 Dynamic Stylesheet

动态插入/移除样式DOM, 没错,这是一个及其简单的方式。原理是浏览器会对所有的样式表的插入、移除做整个 CSSOM 的重构,从而达到 插入、卸载 样式的目的

4.2 js 隔离

4.2.1 快照方式

在创建微应用的时候会实例化一个沙盒对象,它有两个方法,active是在激活微应用的时候执行,而inactive是在离开微应用的时候执行。

整体的思路是在激活微应用时将当前的window对象拷贝存起来,然后从modifyPropsMap中恢复这个微应用上次修改的属性到window中。在离开微应用时会与原有的window对象做对比,将有修改的属性保存起来,以便再次进入这个微应用时进行数据恢复,然后把有修改的属性值恢复到以前的状态。

4.2.2 proxy 方式

微应用中的script内容都会加with(global)来执行,这里global是全局对象,如果是proxy的隔离方式那么他就是下面新创建的proxy对象。我们知道with可以改变里面代码的作用域,也就是我们的微应用全局对象会变成下面的这个proxy。当设置属性的时候会设置到proxy对象里,在读取属性时先从proxy里找,没找到再从原始的window中找。也就是你在微应用里修改全局对象的属性时不会在window中修改,而是在proxy对象中修改。因为不会破坏window对象,这样就会隔离各个应用之间的数据影响。

4.3 系统之间如何通信

系统之间通信一般有两种方式:自定义事件和本地存储。如果是两个系统相互跳转,还可以用URL传数据。

一般来说,不会同时存在A、B两个子系统,常见的数据共享就是登陆信息,登陆信息一般使用本地存储记录。另外一个常见的场景就是子系统修改了用户信息,主系统需要重新请求用户信息,这个时候一般用自定义事件通信。

5. 微前端的更多内容

浅析qiankun中的样式隔离与JS沙箱机制
从0实现一个single-spa的前端微服务
http://qiankun.fengxianqi.com/sub-vue

Clash调整为局部代理

Posted on 2022-09-01

在这里插入图片描述
shadowsocks的pac模式非常好找,不过clash的pac藏得比较隐蔽,本篇文章会告诉你它在哪里~

PAC模式:国内网站依旧走本地网络,速度快,绝大部分国外网站都走代理,速度也快。
全局模式:所有网站都走代理,访问国内网站速度变慢。

在这里插入图片描述
另外使用的时候记得开启代理~

在这里插入图片描述

瞅一眼grid(弃文)

Posted on 2019-10-11

grid属性总记不住,故总结此文,代码戳图片标题看哦~

首先看一个栗子🌰

博客

图1 🌰

这是我之前做的博客,数据是mock的,anyway,在grid网格布局出现以前,假如我们要做这么一个博客,是要考虑非常多的布局因素的,这个页面是明显的header + content + footer的一个布局(footer没截到),而content区域是一个三栏布局。假如内容区原本通过固定宽度并margin居中的话,右侧的推荐栏要加上是非常麻烦的,但是在grid布局中实现就非常便利啦,只要在子元素设置grid-column即可。


See the Pen
grid
by cookie (@suedar)
on CodePen.

是时候学习grid了~

grid

grid

图3 Grid属性汇总

grid网格布局是一种二维布局,其将页面划分为一个个网格,通过行(column)和列(row)的巧妙组合,构成响应式的页面。

网格

图4 网格

截止2017年,大多数浏览器都已经支持这个布局。(想康康兼容性戳这里)

grid有作用在容器上以及子元素上面的属性。

阮大大写的真详细 弃文。。。

CSS Grid 布局完全指南(图解 Grid 详细教程)

grid的一个示例

CSS Grid 网格布局教程

文章

Posted on 2019-10-09

单元测试mocha

记一次手忙脚乱的base64debug之旅

Posted on 2019-09-10

这几天做了一个需求,读取上传的公私钥,然后利用私钥采用RSA加密摘要,发送给后端。其中运用到了base64的加解密,RSA加密采用的是node的Crypto模块,base64的转码采用的是js-base64, 然而万万没有想到,这里面有坑啊。

(开个玩笑,这个库还是很不错的)

文件的读取

首先是文件的读取,采用的是FileReader, 并且二进制文件的读取应为readAsArrayBuffer,否则会乱码。

1
2
3
4
5
6
7
8
9
readFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function(evt) {
resolve(evt.target.result)
};
reader.readAsArrayBuffer(file);
})
}

从这里读取到的数据,调用Object.prototype.toString.call([data]),结果为[object Uint8Array]。

ArrayBuffer对象用来表示通用的、固定长度的原始二进制数据缓冲区。ArrayBuffer不能直接操作,而是要通过类型数组对象或DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。

Uint8Array 数组类型表示一个8位无符号整型数组,创建时内容被初始化为0。创建完后,可以以对象的方式或使用数组下标索引的方式引用数组中的元素。

类型数组对象就是Uint8Array之类的TypedArray,我们需要这些对象操作ArrayBuffer。

这是一个摸索了很久的点,因为有的公私钥读取可以直接采用reader.readAsText(file)。

ArrayBuffer与base64的转换

另一个摸索了很久的东西便是ArrayBuffer与base64的转换,缘由于js-base64对这两个的转换并不支持。

ArrayBuffer转base64

很令人捉🐔的是,调用库中的方法Base64.encode([ArrayBuffer]),得出的数据一直不对。
略微看了一下源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var _Base64 = global.Base64;
var version = "2.5.1";
// if node.js and NOT React Native, we use Buffer
var buffer;
if (typeof module !== 'undefined' && module.exports) {
try {
buffer = eval("require('buffer').Buffer");
} catch (err) {
buffer = undefined;
}
}
...
var _encode = buffer ?
buffer.from && Uint8Array && buffer.from !== Uint8Array.from
? function (u) {
return (u.constructor === buffer.constructor ? u : buffer.from(u))
.toString('base64')
}
: function (u) {
return (u.constructor === buffer.constructor ? u : new buffer(u))
.toString('base64')
}
: function (u) { return btoa(utob(u)) }
;
var encode = function(u, urisafe) {
return !urisafe
? _encode(String(u))
: _encode(String(u)).replace(/[+\/]/g, function(m0) {
return m0 == '+' ? '-' : '_';
}).replace(/=/g, '');
};

不知是不是采用IIFE的关系,buffer恒为undefined,并且此时!urisafe为真,会流入第一个条件,而的u为Uint8Array,强制类型转换会导致乱码。

故而不用库,用原生方法。
Buffer.from([key]).toString('base64') 这是ArrayBuffer转base64的原生方法。

base64转ArrayBuffer

1
2
3
4
5
6
7
8
9
function _base64ToArrayBuffer(base64) {
var binary_string = window.atob(base64);
var len = binary_string.length;
var bytes = new Uint8Array( len );
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes.buffer;
}

and…

1
2
3
4
5
6
const encryptDigest = crypto.privateEncrypt({ key: privateKey },
Buffer.from(_base64ToArrayBuffer(digest))
);
const res = crypto.publicDecrypt({ key: publicKey }, encryptDigest);

res.toString('base64') === digest ?

只要用私钥加密的摘要用公钥能成功解密,并且数据不变,就证明这个流程正确了。

其实用window.atob也可以将ArrayBuffer转base64,但是有大小限制,码位应在 0x00 ~ 0xFF 范围内。然而后端小哥说业务不支持ascii格式的,只能作罢。

javascript – 将base64字符串转换为ArrayBuffer

深入学习 Node.js Buffer

再谈promise

Posted on 2019-08-24

两个周末都搭在promise上面啦,所以总结一下吧。

promise1
promise2
error
promise1
promise2
success
error

setTimeout(() => {
console.log(‘once’)
resolve(‘success’)
}, 1000)

a = new Promise((resolve, reject) => {
setTimeout(() => {
console.log(‘once’)
resolve(‘success’)
}, 1000)
console.log(555)
})
console.log(12333)
v = Date.now()
a.then((res) => {
console.log(res, Date.now() - v)
})
a.then((res) => {
console.log(res, Date.now() - v)
})

setTimeout(() => {
console.log(9999)
})

new Promise((resolve, reject) => {
resolve(3)
}).then(a => {
console.log(a)
})

setTimeout(() => {
console.log(1)
})

new Promise((resolve, reject) => {
console.log(2);
resolve(3)
}).then(res => {
console.log(res)
})

console.log(4)

Json-server + mockjs模拟假数据

Posted on 2019-08-11

鉴于这个代码开源出去感觉会被打,还是写个博客吧。

其实操作很简单,只要把文件读取,通过mock转化,再导出文件,然后在json-server填写相应路径即可。

源文件test.json

1
2
3
4
5
6
7
8
9
{
"list|1-10": [{
"id|+1": 1
}],
"user": {
"name": "@name",
"id": "@id"
}
}

通过mockjs处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Mock = require('mockjs');
const fs = require("fs");

let data = fs.readFileSync("test.json", "utf-8");

data = data.toString();
data = JSON.parse(data)
data = Mock.mock(data);
data = JSON.stringify(data);

fs.writeFile('res.json', data, function(err){
if(err){
console.error(err);
}
})

得{"list":[{"id":1},{"id":2},{"id":3}],"user":{"name":"John Martinez","id":"320000199809111043"}}

完整json-server配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

// server.js
const path = require('path');
const fs = require("fs");
const Mock = require('mockjs');
const jsonServer = require('json-server')

let data = fs.readFileSync("test.json", "utf-8");

data = data.toString();
data = JSON.parse(data)
data = Mock.mock(data);
data = JSON.stringify(data);

fs.writeFile('res.json', data, function(err) {
if(err){
console.error(err);
}
const server = jsonServer.create()
const router = jsonServer.router(path.join(__dirname, 'res.json'))
const middlewares = jsonServer.defaults()

// Set default middlewares (logger, static, cors and no-cache)
server.use(middlewares)

// Add custom routes before JSON Server router
server.get('/echo', (req, res) => {
res.jsonp(req.query)
})

// To handle POST, PUT and PATCH you need to use a body-parser
// You can use the one used by JSON Server
server.use(jsonServer.bodyParser)
server.use((req, res, next) => {
if (req.method === 'POST') {
req.body.createdAt = Date.now()
}
// Continue to JSON Server router
next()
})

// Use default router
server.use(router)
server.listen(3000, () => {
console.log('JSON Server is running')
})

})

打开localhost:3000

前端历史小记(该文作废)

Posted on 2019-08-05

各位同学大家好,本次分享的主要内容是关于前端历史的简单介绍,仅适用于前端零基础的初学者。

洪荒时期(1989~2006)

首先我们需要知道的是,一个合格的网页是由什么构成的呢?
答案是html、css和javascript,这三者分管网页的结构,样式和行为,并称为网页三要素。其中HTML决定了网页的结构和内容,CSS用来设定了网页的表现样式,javascript用来控制网页的行为。本文主要讲述JavaScript的发展。JavaScript是一种处理网页的脚本语言。尽管它的名字和Java类似,但是它是由网景公司开发的而不是由太阳计算机系统公司开发的,除了两者的语法都是从C语言发展而来这一点外,它们之间几乎没有什么关系。之所以叫JavaScript,只是当时网景公司希望能借助Java的名气推广它, 也就是传说中的蹭热点~由于网页浏览器中,JavaScript与文档对象模型(Document Object Model)紧密结合,能够很好地处理网页,使得它比它的作者原本预期的要有用得多。它的用途可以用术语DHTML(动态HTML)表达,以强调它和静态HTML网页的区别。

而ECMAScript是JavaScript的标准版本,es是ECMAScript的简称。由于JavaScript是个在10天创立出来的语言,所以,不可避免地有诸多纰漏,在后续的版本迭代中,我们希望去逐渐修复它。在2015年es6发布,2016es7发布,以此类推。

英国科学家蒂姆·伯纳斯-李于1989年发明了万维网。而后网景公司发布了第一款商业浏览器Navigator。
(1994 年,网景浏览器的截图)

(1994 年,网景浏览器的截图)

而后爆发了浏览器大战,第一时期为1998年微软公司的Internet Explorer取代网景公司的Netscape Navigator成为主要浏览器。第二时期为2003年后Internet Explorer份额逐渐受到其他浏览器蚕食,包括Mozilla Firefox,Google Chrome,Safari和Opera。若是众多浏览器按照不同的标准开发的话,前端会有繁复的兼容性问题。

比如我们要写一个事件绑定的函数,兼容所有浏览器。(该方法称为特征检测)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 该方法来自《JavaScript高级程序设计》
var eventUtil = {
add: function(el, type, handler) {
if (el.addEventListener) { // DOM2
el.addEventListener(type, handler, false);
} else if (el.attachEvent) {
el.attachEvent('on' + type, handler); // ie
} else {
el['on' + type] = handler; // DOM0
}
},
off: function(el, type, handler) {
if (el.removeEventListener) {
el.removeEventListener(type, handler, false)
} else if (el.detachEvent) {
el.detachEvent(type, handler);
} else {
el['on' + type] = null;
}
}
};

jQuery时期(2006~2016)

我们知道,JavaScript是一门解释性语言,执行速度要比编译型语言慢得多,所以性能优化尤其重要。2006年到2016年,那是jquery的时代,其声称能提升性能并解决浏览器兼容性问题。

但是,随着硬件设备和浏览器的发展,页面展示从原来的静态页面展示变得越来越多样化,需求在增长,时代在变革,一个页面需要依赖的模块越来越多,于是会引入十多个乃至几十个jQuery插件,页面上塞满了Script标签。众所周知,浏览器是单线程,Script的加载,会影响到页面的解析与呈现,导致著名的白屏问题。jQuery另一个问题是全局污染,由于插件的质量问题,或者开发的素质问题,这已经是IIEF模块或命名空间等传统手段无法解决了。(《前端开发 20 年变迁史》)

三大框架时期(2016~至今)

那么,怎么解决模块越来越多的问题呢?模块层出不穷,模块管理工具亦如雨后春笋般出现,现在成为主流的

https://zh.wikipedia.org/wiki/%E6%B5%8F%E8%A7%88%E5%99%A8%E5%A4%A7%E6%88%98

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

record-19-07~19-08

Posted on 2019-07-24

7.24

void

  1. 箭头函数的void用法

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void

Non-leaking Arrow FunctionsSection

Arrow functions introduce a short-hand braceless syntax that returns an expression. This can cause unintended side effects by returning the result of a function call that previously returned nothing. To be safe, when the return value of a function is not intended to be used, it can be passed to the void operator to ensure that (for example) changing APIs do not cause arrow functions’ behaviors to change.

button.onclick = () => void doSomething();

This ensures the return value of doSomething changing from undefined to true will not change the behavior of this code.

上面所属 即箭头函数不想取其返回值的时候 可用void

  1. void 可代指未被操作的原始值
    某种程度上来说 void 0 === undefined

8.5

vue2.0貌似不能让localhost被占用才可以开启其他域名的网页?

debug lession

Posted on 2019-06-13

不存在的东西应该为 if (var !== undefined)

12

suedar

19 posts
1 tags
GitHub E-Mail
© 2018 — 2022 suedar
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4