原生Web API
XMLHttpRequest
XMLHttpRequest 可以在不刷新页面的情况下请求特定 URL,获取数据。尽管名称如此,XMLHttpRequest 可以用于获取任何类型的数据,而不仅仅是 XML。它甚至支持 HTTP 以外的协议(包括 file:// 和 FTP),尽管可能受到更多出于安全等原因的限制。
1
2
3
4
5
6
7
8
9 function reqListener () {
console.log(this.responseText);
}
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/getsomething");
oReq.responseType = "json";
oReq.send();
发送
1
2
3 var xhr = new XMLHttpRequest;
xhr.open("POST", url, false);
xhr.send(data);
ResizeObserver
示例 ResizeObserver
调用构造方法返回一个ResizeObserver对象,传入回调函数(entries)=>{},即当监听到目标width height改变时,执行该函数,参数entries为ResizeObserverEntry接口的集合,可以访问真正在观察的 Element 或 SVGElement 最新的大小1
2
3
4
5
6
7
8
9
10
11
12const observer = new ResizeObserver(entries=>{
for (let entry of entries) {
if(entry.contentBoxSize) { // 内容盒尺寸
h1Elem.style.fontSize = `${Math.max(1.5, entry.contentBoxSize.inlineSize / 200)}rem`; // 前文某标题const h1Elem = document.querySelector('h1');
pElem.style.fontSize = `${Math.max(1, entry.contentBoxSize.inlineSize / 600)}rem`; // 前文某段文字const pElem = document.querySelector('p');
} else {
h1Elem.style.fontSize = `${Math.max(1.5, entry.contentRect.width / 200)}rem`;
pElem.style.fontSize = `${Math.max(1, entry.contentRect.width / 600)}rem`;
}
}
})
关于内容盒和边框盒
ASP和SPA
将单页面应用植入到ASP.Net
方式一:项目模板和spa中间件:
1 | % 安装spa template pack |
angular is short for template”ASP.NET Core with Angular”, -n 指定项目名称,键入dotnet new —help查看选项参数说明。
项目大致结构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│ appsettings.json
│ MyDotNetNgProject.csproj
│ Program.cs
│ Startup.cs
├───ClientApp
│ │ angular.json
│ │ package.json
│ │ tsconfig.json
│ │ tslint.json
│ ├───e2e
│ │ └───src
│ └───src
│ ├───app
│ │ ├───counter
│ │ ├───fetch-data
│ │ ├───home
│ │ └───nav-menu
│ ├───assets
│ └───environments
├───Controllers
├───obj
├───Pages
├───Properties
│ launchSettings.json
└───wwwroot
整个前端项目以ClientApp目录为根目录,在ASP的Startup.Configure中调用spa中间件,如下1
2
3
4
5
6
7
8
9
10
11app.UseSpa(spa => {
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
方式二:前端发布到wwwroot,使用静态文件和默认页
Startup.cs1
2
3
4
5
6
7
8
9
10
11
12
13public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
UseDefaultFiles的搜索规则是1
2
3
4default.htm
default.html
index.htm
index.html
另外可以使用DefaultFilesOptions添加自定义默认页,详见 ASP.NET Core 中的静态文件
Trouble Shooting
Issue: The Angular CLI process did not start listening for requests within the timeout period of 0 seconds.
当前的angular模板是基于Angular 8.2.12(记于2020.8.28),在迁移ng9,ng10项目时出现上述问题。
较深入的分析issue#18062
workaround: edit start script1
"start": "echo start && ng serve --host 0.0.0.0",
路由问题
拓展
ASP.NET Core 上的单页面应用的身份验证简介1
dotnet new angular -o AspNgIndividualAuthProj -au Individual
Caution! 模板生成同时提供认证授权功能的应用,具体是使用一个叫IdentityServer的框架,IdentityServer封装了提供重定向endpint,生成JWT令牌,校验令牌等功能。 通常情况下,后台服务应对接独立的SSO,下面的大部分内容对此的可参考性很有限
MSAL + Angular + .Net Core + AD B2C
Microsoft Authentication Library(微软身份认证库MSAL)
微软栗子真多—> Azure-Sample1
npm i msal @azure/msal-angular -save-dev
引入MSAL模块 @app.module1
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
49
50
51
52...
import { Configuration } from 'msal';
import {
MsalModule,
MsalInterceptor,
MSAL_CONFIG,
MSAL_CONFIG_ANGULAR,
MsalService,
MsalAngularConfiguration
} from '@azure/msal-angular';
import { msalConfig, msalAngularConfig } from './app-config';
import { AppRoutingModule } from './app-routing.module';
import { ProfileComponent } from './profile/profile.component';
function MSALConfigFactory(): Configuration {
return msalConfig;
}
function MSALAngularConfigFactory(): MsalAngularConfiguration {
return msalAngularConfig;
}
@NgModule({
declarations: [
AppComponent,
HomeComponent,
ProfileComponent,
],
imports: [
...
MsalModule
],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true
},
{
provide: MSAL_CONFIG,
useFactory: MSALConfigFactory
},
{
provide: MSAL_CONFIG_ANGULAR,
useFactory: MSALAngularConfigFactory
},
MsalService
],
bootstrap: [AppComponent]
})
export class AppModule { }
另 使用oidc-client的栗子参考oidc-client
关于.csproj
参考:理解 C# 项目 csproj 文件格式的本质和编译流程
- PropertyGroup, 声明编译过程中用到的变量,如一些路径,所谓的group,为了增强可读性,而将一组变量放在一个PropertyGroup中,其他的再放一个PropertyGroup
1
2
3
4
5<PropertyGroup>
...
<SpaRoot>ClientApp\</SpaRoot>
</PropertyGroup> - ItemGroup, 顾名思义,存放集合的项,一个group中各项属性名相同,可以认为是类型为XX(即属性名)的一个集合,下例是第三方的package,另外也可以放其他模块所需的任意内容,用相应的属性标识出
1
2
3
4
5
6<ItemGroup>
<PackageReference Include="EntityFrameworkCore.SqlServer.HierarchyId" Version="1.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.2" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.1.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.7" />
</ItemGroup> - Target上例是ASP.NET Core with Angular模板项目使用的build target,两种编译环境,而且其中还有条件语句,有点厉害
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<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">
<!-- Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- As part of publishing, ensure the JS resources are freshly built in production mode -->
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build:ssr -- --prod" Condition=" '$(BuildServerSideRenderer)' == 'true' " />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="$(SpaRoot)dist\**; $(SpaRoot)dist-server\**" />
<DistFiles Include="$(SpaRoot)node_modules\**" Condition="'$(BuildServerSideRenderer)' == 'true'" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
领域驱动设计
领域驱动设计(Domain-Driven Design, DDD), 对于复杂的系统的开发, 从需求梳理到编码实现,将涉及的专业知识转换成模型、逻辑是必不可少的环节,这些信息和知识会汇集到若干核心的“领域”,接下来的设计重心会落在核心领域范畴内,确定域界地沟通方式,迭代地在域内完成实现,而不似单纯地服务端的功能堆叠。
webassembly
应该首先从官方网站获取知识
webassembly知识储备或许会涵盖“编译原理”,Rust,v8开发,
WebAssembly或称wasm是一个实验性的低端编程语言,应用于浏览器内的客户端。WebAssembly是便携式的抽象语法树,被设计来提供比JavaScript更快速的编译及运行。WebAssembly将让开发者能运用自己熟悉的编程语言编译,再藉虚拟机引擎在浏览器内运行。 —-维基百科
曾几何时,有“一切可以由js实现的,终将用js实现”,而webassembly技术为编译型语言(c/c++,jave,c#等)抢夺浏览器战场打开了传送门。
优势:
- 运行效率高 如应用于文件上传中的扫描注1
- 保密性好 见Google reCAPTCHA 另航妹博客:浅谈前端代码加密
课外:为了提高浏览器性能,曾出现过从 NaCl、PNaCl 到 ASM.js,这些技术作为wasm的前辈,有以下特点————(于航《WebAssembly入门》)
- 源码中都使用了类型明确的变量;
- 应用都拥有独立的运行时环境,并且与原有的 JavaScript 运行时环境分离;
- 支持将原有的 C/C++ 应用通过某种方式转换到基于这些技术的实现,并可以直接运行在 Web 浏览器中。
现状是,四大厂(Mozilla,Google,Microsoft,Apple)共同倾力开发, WebAssembly 技术已成为 W3C 的标准, 其MVP版本(Minimum Viable Product)被主流浏览器支持
工具链Emscripten,Rust, AssemblyScript
In case of conflict, consider users over authors over implementors over specifiers over theoretical purity.
helloworld.wasm
准备一个新的开发环境:
启动一个ubuntu的docker
1
2 docker pull ubuntu
sudo docker run -it -u root --name labdocker -v labdocker_home:/var/labdocker_home ubuntu bash
上面的语句映射的是docker中的/var/labdocker_home和宿主的/var/lib/docker/volumes/labdocker_home/_data wtf???
给‘空’的ubuntu安装必要工具
1
2 apt-get update
apt-get install -y nodejs npm git
编译工具链依赖
1 apt-get install -y cmake python3.8
安装emsdk
1
2
3
4
5
6 git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
git pull
./emsdk install latest // 这一步依赖python
./emsdk activate latest
source ./emsdk_env.sh // 定义环境变量
You always have to source ./emsdk_env.sh first in a new terminal session输入emcc -v查看信息
看起来比较正常,说明工具安装成功
另,其实安装emsdk不是必须的,docker hub中有现成的1
2
3docker pull emscripten/emsdk
docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) \
emscripten/emsdk emcc helloworld.cpp -o helloworld.js
创建一个c项目1
2
3mkdir /home/Workspace/hello
cd /home/Workspace/hello
echo '' > hello.c
回忆起c的hello world1
2
3
4#include<stdio.h>
int main(int argc, char** argv){
printf("hello hello~/n");
}
走你1
emcc hello.c -s WASM=1 -o hello.html
- 参数-s WASM=1 要求生成.wasm否则编译成asm.js
- -o hello.html Emscripten 生成一个我们所写程序的HTML页面,并带有 wasm 和 JavaScript 文件
编译生成hello.wasm hello.js hello.html 可以用emrun运行这个html(直接用浏览器打开会认为读取file://文件违反policy) 因为docker没有映射端口,拷出来用http-server运行,是这样婶的:FFmpeg.js 的实现
原文:Build FFmpeg WebAssembly version (= ffmpeg.wasm) 此链接国内网络或无法访问,可参考国内博客的类似文章,keyword:”webassemby” + “ffmpeg”
关于ffmpeg,video和视频流那篇曾用其进行视频的转码和输出流。
FFmpeg 是一个开放源代码的自由软件,可以运行音频和视频多种格式的录影、转换、流功能,包含了libavcodec(用于多个项目中音频和视频的解码器库),以及libavformat(音频与视频格式转换库)。————维基百科
clone FFmpeg 源码
1 git clone https://github.com/FFmpeg/FFmpeg
源码根路径INSTALL.md为构建/安装说明
1
2 ./configure
make & make install
在windows上开发使用cygwin集成linux开发环境(本不应绕此远路),运行cygwin.setup安装程序安装以下库
1
2
3
4 gcc-core
mingw-gcc-core
binutils
gdb
执行configure加以下参数
1 bash ./configure --enable-cross-compile --disable-x86asm
configure大约需要执行十几分钟(或更久,取决于cpu加网速)
make issue: ./libavutil/mem.h:342:1: warning: ‘alloc_size’ attribute ignored on a function returning ‘int’ [-Wattributes]
342 | av_alloc_size(2, 3) int av_reallocp_array(void ptr, size_t nmemb, size_t size);
| ^~~~~
make: ** [ffbuild/common.mak:60: libavformat/mov_esds.o] Interrupt
旧版本函数参数不一致导致的问题,已被修复,见[FFmpeg-devel] avutil/mem: Fix invalid use of av_alloc_size
——————two weeks later————————1
2
3
4
5git clone https://github.com/FFmpeg/FFmpeg
cd FFmpeg
.configure - --disable-x86asm // 这一步是根据configure生成MakeFile
make // 编译
make install // 将编译产出复制到(安装)到linux的相应位置
测试FFmpeg可以尝试在终端使用媒体编辑功能1
2
3ffmpeg --help
ffmpeg -i test.mov -strict -2 -vf crop=720:405:0:451 out.mp4
// 从test.mov中裁剪720*405大小区域,裁剪偏移(0,451)输出为out.mp4
我们目的是要生成.wasm放到浏览器中,这里用到Emscripten
Emscripten compiles C and C++ to WebAssembly using LLVM and Binaryen. Emscripten output can run on the Web, in Node.js, and in wasm runtimes. ——— 《Emscripten ReadMe》
回到make这步的输出项中:
这个泛着绿光的ffmpeg就是Binaryen(二进制文件)
LLVM(low level virtual machine)不限于字面意思的编译环境
关于emcc
- Emcc 使用 Clang 和 LLVM 编译生成 Wasm或者asm.js
- Emscripten SDK (emsdk) 配置 .emscriten, 用于管理多份SDK和工具,指定当前正在使用的编译代码(Active Tool/SDK)。
工具链依赖1
apt-get install -y cmake python3.8
安装emsdk1
2
3
4
5
6git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
git pull
./emsdk install latest // 这一步依赖python
./emsdk activate latest
source ./emsdk_env.sh // 定义环境变量
You always have to source ./emsdk_env.sh first in a new terminal session
输入emcc -v查看信息
看起来比较正常,说明工具安装成功
另,其实安装emsdk不是必须的,docker hub中有现成的1
2
3docker pull emscripten/emsdk
docker run --rm -v $(pwd):/src -u $(id -u):$(id -g) \
emscripten/emsdk emcc helloworld.cpp -o helloworld.js
将FFmpeg源码编译成LLVM二进制码, 相比之前的configure && make这里不仅要使用emconfigure 命令,而且要设置下述的若干参数,于是做一个build .sh: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#!/bin/bash -x
# verify Emscripten version
emcc -v
# configure FFMpeg with Emscripten
CFLAGS="-s USE_PTHREADS"
LDFLAGS="$CFLAGS -s INITIAL_MEMORY=33554432" # 33554432 bytes = 32 MB
CONFIG_ARGS=(
--target-os=none # use none to prevent any os specific configurations
--arch=x86_32 # use x86_32 to achieve minimal architectural optimization
--enable-cross-compile # enable cross compile
--disable-x86asm # disable x86 asm
--disable-inline-asm # disable inline asm
--disable-stripping # disable stripping
--disable-programs # disable programs build (incl. ffplay, ffprobe & ffmpeg)
--disable-doc # disable doc
--extra-cflags="$CFLAGS"
--extra-cxxflags="$CFLAGS"
--extra-ldflags="$LDFLAGS"
--nm="llvm-nm -g"
--ar=emar
--as=llvm-as
--ranlib=llvm-ranlib
--cc=emcc
--cxx=em++
--objcc=emcc
--dep-cc=emcc
)
emconfigure ./configure "${CONFIG_ARGS[@]}"
# build dependencies
emmake make -j4
# build ffmpeg.wasm
mkdir -p wasm/dist
ARGS=(
-I. -I./fftools
-Llibavcodec -Llibavdevice -Llibavfilter -Llibavformat -Llibavresample -Llibavutil -Llibpostproc -Llibswscale -Llibswresample
-Qunused-arguments
-o wasm/dist/ffmpeg.js fftools/ffmpeg_opt.c fftools/ffmpeg_filter.c fftools/ffmpeg_hw.c fftools/cmdutils.c fftools/ffmpeg.c
-lavdevice -lavfilter -lavformat -lavcodec -lswresample -lswscale -lavutil -lm
-s USE_SDL=2 # use SDL2
-s USE_PTHREADS=1 # enable pthreads support
-s INITIAL_MEMORY=33554432 # 33554432 bytes = 32 MB
)
emcc "${ARGS[@]}"
EMCC_DEBUG
设置EMCC_DEBUG变量使用调试模式编译wasm1
2
3
4
5EMCC_DEBUG=1 emcc dip.cc
-s WASM=1
-O3
--no-entry
-o dip.wasm
issues expected magic word 00 61 73 6d, found …
获取的wasm模块的MIME type不是application/wasm
关于wasm的调用
WebAssembly还没有和 \