- 💻 A web developer
- 🏋️ Marathon & Trail runner 🏃, Swim 🏊
- 🌱 I’m currently learning something about AI 🤖️
genffy / genffy Goto Github PK
View Code? Open in Web Editor NEWAbout me.
Home Page: https://genffy.com
About me.
Home Page: https://genffy.com
对于 git,大家再也熟悉不过了,几乎会天天跟它打交道,一般用的就是些很基础的功能。但是,最近接触到一个带有 submodules
的项目,感觉好神奇啊,刚开始完全懵逼,运行的时候发现依赖的文件夹下面是空的,一度怀疑这是不是个 bug,但是这明明是线上跑的好好的一个项目啊。然后就仔细对比了下工程目录,发现除了常见的 .gitignore
外还多了个 .gitmodules
。打开看下是酱紫的:
➜ F2ECodeSnippet git:(master) ✗ cat .gitmodules
[submodule "git-submodules-action"]
path = practice/git/submodules
url = [email protected]:genffy/git-submodules-action.git
然后就好开心啊,去 google 了下。唔,似乎发现了个新天地。(原谅我这么的野生...
Git 通过子模块处理这个问题(你想将两个项目单独处理但是又需要在其中一个中使用另外一个)。子模块允许你将一个 Git 仓库当作另外一个Git仓库的子目录。这允许你克隆另外一个仓库到你的项目中并且保持你的提交相对独立。 --Git-工具-子模块
看到这可能会有人要叫起来,这不就是包管理么,现在不都是通过npm(或者bower)来做,干嘛又搞这个玩意儿?个人的理解,这种场景大多出现在一个企业内部,公用模块一种管理机制,相对私有 npm 来说, gitsubmodule
这种方式或许或更加的灵活些,方便原 repo 和 submodule使用者同时进行修改和同步(相对提PR来说)。anyway,存在即合理。下面主要说下它的一些使用方法和一些需要注意的坑(文档&资料的搬运工)。
首先,看下它包含哪些命令 或者看这里:
➜ F2ECodeSnippet git:(master) ✗ git submodule -h
usage: git submodule [--quiet] add [-b <branch>] [-f|--force] [--name <name>] [--reference <repository>] [--] <repository> [<path>]
or: git submodule [--quiet] status [--cached] [--recursive] [--] [<path>...]
or: git submodule [--quiet] init [--] [<path>...]
or: git submodule [--quiet] deinit [-f|--force] [--] <path>...
or: git submodule [--quiet] update [--init] [--remote] [-N|--no-fetch] [-f|--force] [--checkout|--merge|--rebase] [--reference <repository>] [--recursive] [--] [<path>...]
or: git submodule [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
or: git submodule [--quiet] foreach [--recursive] <command>
or: git submodule [--quiet] sync [--recursive] [--] [<path>...]
对于一个干净的项目来说,常规的操作是 init
->add
,等待 checkout 完成,项目里就有 .gitmodules
这个文件了,然后看下 .gitmodules
、.git/config
和 .git/modules
对比下,具体的一些命令参数代表啥大家可以去看官方文档。
➜ Desktop mkdir -p git-submodules-action-new
➜ Desktop cd git-submodules-action-new
➜ git-submodules-action-new git init
Initialized empty Git repository in /Users/genffy/Desktop/git-submodules-action-new/.git/
➜ git-submodules-action-new git:(master) git submodule init
➜ git-submodules-action-new git:(master) git submodule add --name repoA [email protected]:genffy/repoA.git lib_modules/repoA
Cloning into 'lib_modules/repoA'...
remote: Counting objects: 17, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 17 (delta 2), reused 0 (delta 0)
Receiving objects: 100% (17/17), done.
Resolving deltas: 100% (2/2), done.
Checking connectivity... done.
➜ git-submodules-action-new git:(master) ✗ la
total 8
drwxr-xr-x 5 genffy staff 170B 7 6 00:43 .
drwxr-xr-x+ 22 genffy staff 748B 7 6 00:41 ..
drwxr-xr-x 12 genffy staff 408B 7 6 00:43 .git
-rw-r--r-- 1 genffy staff 89B 7 6 00:43 .gitmodules
drwxr-xr-x 3 genffy staff 102B 7 6 00:43 lib_modules
➜ git-submodules-action-new git:(master) ✗ cat .gitmodules
[submodule "repoA"]
path = lib_modules/repoA
url = [email protected]:genffy/repoA.git
➜ git-submodules-action-new git:(master) ✗ cat .git/config
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[submodule "repoA"]
url = [email protected]:genffy/repoA.git
➜ git-submodules-action-new git:(master) ✗ ls .git/modules
repoA
然而,对于菜鸟的我来说几乎没有机会去初始化一个干净的项目的,更多的clone现有的代码然后更新啥的。其实 clone 一个包含submodule的项目并初始化,是件很简单的事情(鄙视下前面懵逼的自己) :
➜ Desktop git clone [email protected]:genffy/git-submodules-action.git && cd git-submodules-action && git submodule update --init
Cloning into 'git-submodules-action'...
remote: Counting objects: 46, done.
remote: Compressing objects: 100% (39/39), done.
remote: Total 46 (delta 12), reused 0 (delta 0)
Receiving objects: 100% (46/46), 7.95 KiB | 0 bytes/s, done.
Resolving deltas: 100% (12/12), done.
Checking connectivity... done.
Submodule 'repoA' ([email protected]:genffy/repoA.git) registered for path 'lib_modules/repoA'
Cloning into 'lib_modules/repoA'...
remote: Counting objects: 17, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 17 (delta 2), reused 0 (delta 0)
Receiving objects: 100% (17/17), done.
Resolving deltas: 100% (2/2), done.
Checking connectivity... done.
Submodule path 'lib_modules/repoA': checked out '9e468932f3529082b14d650969c429e89ed7fc36'
嗯,真的只有一句话。如果是有多个 submoule 需要用到 git foreach
这个方法;如果涉及到层级嵌套,可以加上 --recursive
这个参数。
嗯,以上差不多是些比较常规的用法了。
但是,项目总是要不断迭代更新的,对于的库共用模块则无法避免的要更新,有时候还需要使用者贡献代码或者 fix bug
之类的。下面就介绍下如何更新以及 submodule
同步的事情,其实也是比较简单的。
假如在一个 git-submodules-action 下是这样的:
➜ git-submodules-action git:(master) cat .gitmodules
[submodule "repoA"]
path = lib_modules/repoA
url = [email protected]:genffy/repoA.git
所依赖的 repoA 是这样的:
➜ repoA git:(master) git remote -v
origin [email protected]:genffy/repoA.git (fetch)
origin [email protected]:genffy/repoA.git (push)
➜ repoA git:(master) git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean
然后在 repoA 下修改点东西:
➜ repoA git:(master) vi base.css
➜ repoA git:(master) ✗ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: base.css
no changes added to commit (use "git add" and/or "git commit -a")
➜ repoA git:(master) ✗ git commit -am 'add class name'
[master c19e6fe] add class name
1 file changed, 3 insertions(+)
➜ repoA git:(master) git push
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 344 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To [email protected]:genffy/repoA.git
9e46893..c19e6fe master -> master
然后在git-submodules-action
下执行:
➜ git-submodules-action git:(master) git submodule update --remote
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From git.coding.net:genffy/repoA
9e46893..c19e6fe master -> origin/master
Submodule path 'lib_modules/repoA': checked out 'c19e6fea3b148595a092979d8bd51ba98d8ebff4'
就能看到已经同步了,然后在这个 commit 提交,保证 submodule 的版本信息在主仓库中同步。这里其实有个坑的,详细参见 更新 submodule 的坑 的描述。唔同步原repoA的东西就没了。
作为一个 team,时常为共用组件贡献代码也是件好玩
的事。在 submodule 这中模式下,可以直接操作了。
还是上面两个 repo,这次直接在 git-submodules-action
更新。
➜ git-submodules-action git:(master) cd lib_modules/repoA
➜ repoA git:(a0e2b17) git branch -v
* (HEAD detached at a0e2b17) a0e2b17 fix confilct
master a0e2b17 fix confilct
➜ repoA git:(a0e2b17) vi base.css
➜ repoA git:(a0e2b17) ✗ git status
HEAD detached at a0e2b17
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: base.css
no changes added to commit (use "git add" and/or "git commit -a")
➜ repoA git:(a0e2b17) ✗ git commit -a -m 'add green color '
[detached HEAD de05a18] add green color
1 file changed, 3 insertions(+)
➜ repoA git:(de05a18) git push origin master
Everything up-to-date
➜ repoA git:(de05a18) git status
HEAD detached from a0e2b17
nothing to commit, working directory clean
然后在 repoA 中查看更新
➜ repoA git:(master) git branch -v
* master a0e2b17 fix confilct
➜ repoA git:(master) git fetch
➜ repoA git:(master)
啥?貌似并没有什么卵用对不对?嗯,这里就是一个比较大的坑了,详细参见 修改 submodule 的坑 的描述,当然这里也给出了解决方法,操作下
➜ repoA git:(de05a18) git status
HEAD detached from a0e2b17
nothing to commit, working directory clean
➜ repoA git:(de05a18) git checkout master
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
de05a18 add green color
If you want to keep it by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> de05a18
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
➜ repoA git:(master) git cherry-pick de05a18
[master f004fa6] add green color
Date: Wed Jul 6 01:38:34 2016 +0800
1 file changed, 3 insertions(+)
➜ repoA git:(master) git push
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 343 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To [email protected]:genffy/repoA.git
a0e2b17..f004fa6 master -> master
然后再到 repoA 下看下效果
➜ repoA git:(master) git fetch
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From git.coding.net:genffy/repoA
a0e2b17..f004fa6 master -> origin/master
➜ repoA git:(master) git pull
Updating a0e2b17..f004fa6
Fast-forward
base.css | 3 +++
1 file changed, 3 insertions(+)
嗯,这下同步和更行也就弄完了。
其实,以上一路走下来其实还算是比较顺利的。真正困扰我的还有另外一个问题。比如我clone了别人的项目,执行了 rm -fr .git
的操作,然后 git init && git remote add origin <repo-url>
的操作,再执行上面的操作发现 submodule
无法自动更新,目前的解决方法是删掉 .gitmodules
重新开始😳
我想这应该是有更好的方法的吧?如过没有,那感觉又有的玩了,可以去看看 git 的代码了。
PS: 上面🌰的代码在 coding.net
上,分别是 repoA git-submodules-action 大家随便玩😊。
Why note this? PR's CI checked failed as i skipped install dependency and update pnpm-lock file, so i must to install it and update pnpm-lock file to pass CI.
PR's details: originjs/vite-plugin-federation#127
When install pnpm install playwright-chromium
always pending like this:
node_modules/.pnpm/[email protected]/node_modules/playwright-chromium: Running install script...
The solution is try to manual install chromium
and ffmpeg
, the deitails: details:https://github.com/microsoft/playwright/blob/main/packages/playwright-chromium/install.js
The version's info and install details pls check this file: https://github.com/microsoft/playwright/blob/main/packages/playwright-core/src/utils/registry.ts
download it directly
brew install ffmpeg
FYI: If you encounter such an error
tar: Error opening archive: Failed to open '/path/to/xxxx.tar.gz'
# tar --extract --no-same-owner --file /path/to/xxxx.tar.gz --directory /private/tmp/yyyyy
can try export HOMEBREW_BOTTLE_DOMAIN=''
and rerun
Rerun pnpm install playwright-chromium
will be smoothly and worked.
# 设置公司的
ssh-keygen -t rsa -C "[email protected]" -f ~/.ssh/gitlab_rsa
# 设置默认的,一般都是给 gitlab 用的
ssh-keygen -t rsa -C "[email protected]"
# 查看生成的密钥对
ls -la ~/.ssh
# -rw------- 1 genffy staff 2610 Feb 15 17:48 gitlab_rsa
# -rw-r--r-- 1 genffy staff 578 Feb 15 17:48 gitlab_rsa.pub
# -rw------- 1 genffy staff 2602 Feb 15 17:51 id_rsa
# -rw-r--r-- 1 genffy staff 571 Feb 15 17:51 id_rsa.pub
vim .ssh/config
填入以下内容
# gitlab.orgname.com
Host gitlab.orgname.com
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/gitlab_rsa
User [email protected]
# github.com
#Host github.com
# AddKeysToAgent yes
# UseKeychain yes
# IdentityFile ~/.ssh/github_rsa
# User [email protected]
ssh -T [email protected]
# Hi genffy! You've successfully authenticated, but GitHub does not provide shell access.
ssh -T [email protected]
# Welcome to GitLab, @zhengfei.li!
.gitconfig
cp .gitconfig .gitconfig-gitlab
cp .gitconfig .gitconfig-id
user
信息vim .gitconfig-gitlab
[user]
name = zhengfei.li
email = z[email protected]
signingkey = ~/.ssh/gitlab_rsa.pub
[pull]
rebase = false
[filter "lfs"]
process = git-lfs filter-process
required = true
clean = git-lfs clean -- %f
smudge = git-lfs smudge -- %f
[init]
defaultBranch = main
[commit]
gpgsign = true
[gpg]
format = ssh
vim .gitconfig-id
[user]
name = genffy
email = g[email protected]
signingkey = ~/.ssh/id_rsa.pub
[pull]
rebase = false
[filter "lfs"]
process = git-lfs filter-process
required = true
clean = git-lfs clean -- %f
smudge = git-lfs smudge -- %f
[init]
defaultBranch = main
[commit]
gpgsign = true
[gpg]
format = ssh
.gitconfig
vim .gitconfig
[includeIf "gitdir:~/workspace/"]
path = .gitconfig-id
[includeIf "gitdir:~/Documents/workspace/"]
path = .gitconfig-gitlab
“NGINX是一个免费的,开源的,高性能的HTTP服务器和反向代理,以及一个IMAP / POP3代理服务器。 NGINX以其高性能,稳定性,丰富的功能集,简单的配置和低资源消耗而闻名。
NGINX是为解决C10K问题而编写的少数服务器之一。 与传统的服务器不同,NGINX不依赖线程来处理请求。 相反,它使用更加可扩展的事件驱动(异步)架构。 这种架构使用小的,但更重要的是,在负载下的可预测的存储器量。 即使您不希望处理数千个并发请求,您仍然可以从NGINX的高性能和小内存占用中受益。 NGINX在所有方向上扩展:从最小的VPS一直到大集群的服务器。”
据统计,就大家所知道web server中,Nginx的使用率占到了30%多,Nginx 的轻量、高效、高扩展性等特性越来越受到web开发者的青睐,不仅仅用作web服务器,向负载均衡、反向代理、分析统计、网络安全等方向都很多的实践和应用。
既然Nginx这么牛逼,但是跟我们前端开发来说又有什么关系呢?能给我们提供什么便利,以至于我们需要去了解它呢?
对于前端,大多数时候是不需要去了解关于web server相关的知识的,前端基本的开发都是静态的,最多只需要一个静态文件服务器即可,很多的 Node 小工具就提供了静态文件服务器的功能,比如 anywhere,http-server,webpack-dev-server,一个命令就能帮我们做好了。在这点上,Nginx能做的跟大伙儿差不多,除了能多配置点缓存、http头、端口映射之类的。
在前后端分离盛行的今天,有个痛点是很多前端er遇到的问题,那就是跨域,众所周知跨域的根源是源自浏览器的“同源策略”,以至于javascript在浏览器中无法加载其他域的对象或者数据。对于各种乱七八糟的跨域问题,解决方法很多,W3C还为此专门出了一个CORS(跨域资源共享)标准,这是一个比较完美的方案,在服务器端配置一个HTTP请求头Access-Control-Allow-Origin即可。但是在解决跨域的过程中,还有很多的痛点的,比如比较常见的Cookie问题。
对于以上说的两点,我们只需要Nginx就能帮我们轻松搞定,废话说了这么多,下面我们就简单实践下是如何做到的。
domain | name | port | desc |
---|---|---|---|
demo.com | web | 8082 | 主站 |
api.demo.com | api | 9000 | api 服务器(web server) |
static.demo.com | static | 8083 | 静态文件服务器(css,image,javascript) |
stream.demo.com | stream | 8084 | 测试负载均衡 |
静态文件服务器,顾名思义其主要作用是提供静态资源的访问,直接返回访问 URL 请求的内容即可。很多包含有 HTTP 模块的编程语言都能搭建一个简单的静态文件服务器,比如:
PHP
利用其内建的 web服务器 ,很简单的就创建了一个8000端口的静态文件服务器,只可用于开发环境。
$ php -S localhost:8000
Node
// reference https://gist.github.com/ryanflorence/701407
var http = require("http"),
url = require("url"),
path = require("path"),
fs = require("fs")
port = process.argv[2] || 8888;
http.createServer(function(request, response) {
var uri = url.parse(request.url).pathname
, filename = path.join(process.cwd(), uri);
path.exists(filename, function(exists) {
if(!exists) {
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not Found\n");
response.end();
return;
}
if (fs.statSync(filename).isDirectory()) filename += '/index.html';
fs.readFile(filename, "binary", function(err, file) {
if(err) {
response.writeHead(500, {"Content-Type": "text/plain"});
response.write(err + "\n");
response.end();
return;
}
response.writeHead(200);
response.write(file, "binary");
response.end();
});
});
}).listen(parseInt(port, 10));
console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown");
GO
// reference https://gist.github.com/paulmach/7271283
package main
import (
"flag"
"log"
"net/http"
)
func main() {
port := flag.String("p", "8100", "port to serve on")
directory := flag.String("d", ".", "the directory of static file to host")
flag.Parse()
http.Handle("/", http.FileServer(http.Dir(*directory)))
log.Printf("Serving %s on HTTP port: %s\n", *directory, *port)
log.Fatal(http.ListenAndServe(":"+*port, nil))
}
就像上面所看到的,对于编程语言本身构建一个web服务器并不难,但是对于业务本身的帮助其实不是很大。最关键的是,对于静态文件件服务器来说,在生产环境中还会加上缓存,压缩,等一系列的特性,如此加上nginx的高性能,使用nginx来搭建静态文件服务器是不二之选,且看下nginx是怎么做的。
server {
# 设置需要监听的端口
listen 8083;
# 配置静态文件根目录
root /Users/dpDev/Documents/github/vue-starter/dist/static;
# 开启文件浏览的功能,生产环境需要关闭
autoindex on;
# 设置图片的缓存策略
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires off;
}
# 设置静态资源的缓存策略
location ~ .*\.(js|css)?$
{
expires off;
}
# ...... 继续添加其他配置
}
在电脑网络中,反向代理是代理服务器的一种。它根据客户端的请求,从后端的服务器(如Web服务器)上获取资源,然后再将这些资源返回给客户端。与前向代理不同,前向代理作为一个媒介将互联网上获取的资源返回给相关联的客户端,而反向代理是在服务器端(如Web服务器)作为代理使用,而不是客户端。客户端通过前向代理可以访问很多不同的资源,而反向代理是很多客户端都通过它访问不同后端服务器上的资源,而不需要知道这些后端服务器的存在,而以为所有资源都来自于这个反向代理服务器。(维基百科:反向代理)
另外,维基百科上也说了反向代理主要的用途有:
上面说的那些点,对于前端er来说属于比较高阶的内容了,看客们稍微了解下就好,对于前端er来说,主要是想利用反向代理来解决跨域的问题,下面看个例子:
业务场景:一个前端应用部署在domainA上,有一个数据请求是从domainB上获取
常规情况下会出现这样的问题:
大家都知道,这就是典型的发生了跨域的问题。针对出现跨域的问题解决方案就不赘述了,这里简单说下通过一行简单的配置解决这问题。
server {
listen 8082;
index index.html;
root /Users/dpDev/Documents/github/vue-starter/dist;
# proxy
# 当访问 http://demo.com/demo/ 时,会被代理到 http://api.demo.com/demo/
# 就实现了一个简单的反向代理的功能
location /demo/ {
proxy_pass http://api.demo.com/demo/;
proxy_set_header X-Real-IP $remote_addr;
}
access_log /usr/local/etc/nginx/logs/demo.access.log;
}
重启下nginx,然后再看下结果,完美解决。
除此之外,还能针对该代理设置其他的请求头信息。
Note:
CORS
规范在服务端设置Access-Control-Allow-Origin
会更加方便些prefix
,比如http://domain/api/...
这样方便做统一配置对于互联网服务,负载平衡器通常是一个软体程序,这个程序侦听一个外部端口,互联网用户可以通过这个端口来访问服务,而作为负载平衡器的软体会将用户的请求转发给后台内网服务器,内网服务器将请求的响应返回给负载平衡器,负载平衡器再将响应发送到用户,这样就向互联网用户隐藏了内网结构,阻止了用户直接访问后台(内网)服务器,使得服务器更加安全,可以阻止对核心网络栈和运行在其它端口服务的攻击。(维基百科:负载均衡_(计算机))
在nginx中主要通过upstream这个模块来实现负载均衡的目的,upstream是Nginx的HTTP Upstream模块,这个模块通过一个简单的调度算法来实现客户端IP到后端服务器的负载均衡。
通过nginx来配置负载均衡也很简单,直接看例子:
upstream mySvr {
server 127.0.0.1:8082 weight=2;
server 127.0.0.1:8000 weight=5;
}
server {
listen 8084;
server_name _;
index index.html;
location / {
proxy_pass http://mySvr;
}
}
然后我们请求http://stream.demo.com/
看效果
Nginx的负载均衡模块目前支持5种调度算法,下面进行分别介绍,其中后两项属于第三方调度算法。
在HTTP Upstream模块中,可以通过server指令指定后端服务器的IP地址和端口,同时还可以设定每个后端服务器在负载均衡调度中的状态。常用的状态有:
以上几个例子都用到了一个nginx中比较常见的location配置,下面简单说一下location配置后面url的规则:
location = / {
# 精确匹配 / ,主机名后面不能带任何字符串
[ configuration A ]
}
location / {
# 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求
# 但是正则和最长字符串会优先匹配
[ configuration B ]
}
location /documents/ {
# 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
[ configuration C ]
}
location ~ /documents/Abc {
# 匹配任何以 /documents/Abc 开头的地址,匹配符合以后,还要继续往下搜索
# 只有后面的正则表达式没有匹配到时,这一条才会采用这一条
[ configuration CC ]
}
location ^~ /images/ {
# 匹配任何以 /images/ 开头的地址,匹配符合以后,停止往下搜索正则,采用这一条。
[ configuration D ]
}
location ~* \.(gif|jpg|jpeg)$ {
# 匹配所有以 gif,jpg或jpeg 结尾的请求
# 然而,所有请求 /images/ 下的图片会被 config D 处理,因为 ^~ 到达不了这一条正则
[ configuration E ]
}
location /images/ {
# 字符匹配到 /images/,继续往下,会发现 ^~ 存在
[ configuration F ]
}
location /images/abc {
# 最长字符匹配到 /images/abc,继续往下,会发现 ^~ 存在
# F与G的放置顺序是没有关系的
[ configuration G ]
}
location ~ /images/abc/ {
# 只有去掉 config D 才有效:先最长匹配 config G 开头的地址,继续往下搜索,匹配到这一条正则,采用
[ configuration H ]
}
以上,大概介绍了下关于nginx的一些简单的用法,如有发现不妥,欢迎指正批评。
PS:对于前端er来说,最熟悉还是Node了,顺便推荐几款比较常用的Node的静态文件服务器
vite
用的实在是太爽,但是由于不可抗力的因素(qiankun
亮点依赖 import-html-entry
不支持 type="module"
的问题,参见 issue
#756 和 #1024),被迫做了一次构建的改造,从 vite
换成了 @vue/cli
,准确来说只是 build 的时候用了@vue/cli
,开发环境可控,实在不想失去vite
那么流畅的开发体验。
两者默认加载的变量前缀不一样,vite
是 VITE_
,vue-cli
是 VUE_APP_
,所以你的 .env
文件可能长这样
NODE_ENV=production
VITE_SYS_AUTH_CODE=99865f3f4f2a4f3ebe0b9f94528f72d7
VUE_APP_SYS_AUTH_CODE=99865f3f4f2a4f3ebe0b9f94528f72d7
获取ENV
变量的方式也不一样,在 vite
里面是 import.meta.env
,在 @vue/cli
里面是 process.env
。
为了磨平开发环境下的差异性,可以稍微修改下 vite.config.ts
,让 env
的获取统一
import { defineConfig, loadEnv } from 'vite'
import type { UserConfigExport, ConfigEnv } from 'vite'
export default ({ mode }: ConfigEnv): UserConfigExport => {
process.env = { ...process.env, ...loadEnv(mode, process.cwd(), 'VUE_APP_') }
return defineConfig({
define: {
'process.env': JSON.stringify(process.env),
},
})
}
所以一个比较好的实践是,在平时开发的时,把这些配置都放到一个地方统一管理,切换也就是一个 if else
的事情
vite
下默认是读 index.html
里面 <script type="module" src="/src/main.ts"></script>
标记,而 @vue/cli
里面是通过 vue-config.js
里面的 pages
处理
{
"pro:vite": "node_modules/.bin/vite build",
"pro:cli": "node_modules/.bin/vue-cli-service build"
}
在我的项目里,其实主要只增加了
"@babel/plugin-proposal-optional-chaining": "^7.12.17"
"@vue/cli-plugin-babel": "~4.5.0"
"@vue/cli-plugin-eslint": "~4.5.0"
"@vue/cli-plugin-typescript": "~4.5.0"
"@vue/cli-service": "~4.5.0"
以及对应的 CSS
文件处理 loader
,这里要注意,如果用的是 less
,对应的 loader
版本要注意看下,latest
版本是为 webpack5
准备的
"less": "^4.1.1",
"less-loader": "^7.3.0",
通过将 devServer
alias
等这些相同的配置抽取出来后,其实差异不大,但是开发模式下速度差几十个数量级
/* eslint-disable @typescript-eslint/no-var-requires */
const { name } = require('./package.json')
const { baseUrl, devServer, alias } = require('./config')
module.exports = {
publicPath: baseUrl,
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${name}`,
},
resolve: {
alias,
},
},
pages: {
index: {
entry: 'src/main.ts',
template: 'index.html',
},
},
devServer,
}
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { baseUrl, devServer, alias } = require('./config')
export default defineConfig({
plugins: [vue()],
base: baseUrl,
resolve: {
alias,
},
server: devServer,
})
在 build 模式下,两者的速度差别其实不大。
EOF
Q: 请列一个“介绍 web 开发中 stream 技术的使用”文章的提纲
I. 引言
介绍stream技术的背景和意义
II. 什么是stream技术
介绍stream技术的概念和定义
III. stream技术的应用场景
分析stream技术在web开发中的应用场景,如数据传输、大文件上传和下载等
IV. stream技术的优点
分析stream技术相较于传统web开发技术的优点,如降低内存消耗、提高性能等
V. stream技术的实现方式
介绍stream技术的实现方式,如Node.js中的stream模块
VI. stream技术的实际应用
分析stream技术在实际web开发中的应用案例,如实现文件压缩、实时音视频流等
VII. stream技术的未来
分析stream技术在未来web开发中的发展趋势和应用前景
VIII. 结论
总结stream技术在web开发中的作用和应用价值,强调它对web开发的重要性。
openai 配置代理遇到的一些问题与解决方案
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.