25
3

回顾我和编程之缘

0
归档:2025年3月分类:C#和.NET

回顾自己十六年来的编程经历,我很乐意分享我和计算机编程结缘的过程。

高中毕业的时候,我从邻居家借到一本Basic语言的书籍,我被它迷住了,那时候我没有计算机,只是在草稿纸上模仿书本上的例子进行四则运算和解方程运算。事实上我从小梦想是做一名数学家,所以我大学专业是数学与应用数学。那时候计算机在中国是热门专业,所以数学系也开了很多计算机的课程,我认真学了一个学期的C语言程序设计,我一直被计算机编程的能力震撼,那时候我知道Matlab和Mathematica可以调用C语言的类库进行非常复杂的数值运行,我在大学参加数学建模比赛就是用计算机算法去验证数学模型。

大二的时候我第一次接触Delphi,第一次接触Pascal语言,被它的简洁语法吸引,不过Delphi那时候已经走下坡路,微软的MFC已经蚕食它的市场份额,而Delphi的创始人安德斯海尔斯伯格也被比尔盖茨挖到微软。大二结束的时候我参加学校编程社团,第一次学习C#并且使用ASP.NET WebForm开发网站,从同学那里,我也了解C#的创始人海尔斯伯格的传奇经历,我被两者深刻吸引。

大学毕业的时候我很幸运加入一家有名的软件公司方正集团,我继续使用C#和.NET开发网站,那时候我独立开发了一个CMS系统,虽然很简单但是非常实用,后来我深入参与电子报纸和电子图书的算法设计和软件开发。移动互联网刚刚诞生的时候,我和另外一位工程师基于Mac版本的FBReader开发了ios版本的图书阅读器,那时候我第一次接触object-c。

我也喜欢linux,自己会安装linux操作系统来学习,所以我在工作之余自学了php语言,但是我更喜欢C#这门语言,所以我一直没有转移到其他开发语言。微软和我Windows的闭源一直是我心疼之痛,后来我接触开源Mono,这是.NET在linux的一个实现,但是Mono的重点在桌面和游戏开发。

我加入百度公司之后有机会在linux生态系统下进行软件开发,我们使用php语言和mysql,这段经历扩宽了我的技术视野,让我对编程语言有了更深刻的理解:语言大同小异,最核心的还是算法和数据结构,以为优秀的开发人员应该能同时使用多种编程语言。

离开百度之后,我加入一家初创企业,我负责组建团队并开发互联网产品和软件,选择公司技术栈的时候我面临在php和c#之间二选一。那时候.NET已经开源,Mono的作者也创立了新公司Xamarin,专注于使用C#开发跨平台的移动应用。加上结合公司的情况,我继续选择使用C#作为主开发语言,但是我们也使用mysql、memcached和Redis等技术。我们的第一个移动应用也用C#来开发,那时候是属于很前卫的选择,还好我们坚持了下来,因为到了2016年微软换CTO了,开始拥抱开源,紧接着是收购Xamarin,还有筹划.NET Core的开发。我一直关注.NET Core的进展,后来看到StackOverflow使用.NET Core2.1,他们极大提升了性能,于是我开始在我们的新开发产品中使用.NET Core,到了.NET Core3.1的时候开始将legacy code迁移到.NET Core。

这里不得不提一段我学习深度学习的插曲,2018年左右我认真学习了Michael A. Nielsen的《神经网络与深度学习》,并熟读第二章的多元微积分公式和证明,我甚至在纸上推导了全过程,这是我再次接触神经网络算法(大学数学建模的时候我学习过)。我似乎又回到了大学学习数学分析的时候,这本书写得非常好,作者从最根本最朴素的思想开始,结合实践进入深度神经网络,我认为这本书堪称神经网络的几何原本。我没有继续深入到深度学习中去,但是我认为它最根本的核心还在这本书里。也许哪一天,我还会重新捡回这段技术经历。

05
3

委托和接口的选择

0
归档:2025年3月分类:C#和.NET

A delegate design might be a better choice than an interface
design if one or more of these conditions are true:
• The interface defines only a single method.
• Multicast capability is needed.
• The subscriber needs to implement the interface multiple times.

如果满足以下一个或多个条件,委托设计可能比接口设计更好:
• 接口仅定义一个方法。
• 需要多播功能。
• 订阅者需要多次实现该接口。

28
2

当我将nextjs应用程序部署到azure web应用程序的时候报错:cannot find module ../server/require-hook。

我通过github-actions构建和部署成功完成,但是部署后,我在url 中收到错误,并且在azure日志流利发现这个错误。刚刚开始我不明白是什么原因造成的,因为“npm start”在我的本地机器上使用相同的 npm 版本构建和运行。

通过网上查找,发现是因为githhb的action默认不会把.next文件夹打包加入zip并且发布到azure。所以需要在默认的工作流基础上修改:

1、adding "startAzure": "./node_modules/next/dist/bin/next start" in package.json; 还需要在azure应用程序网站的配置立加上 the startup command of the azure web app like "npm run startAzure"

2、添加这个到azure的 pipeline (在创建 zip了之后)

- name: Zip artifact for deployment run: zip release.zip ./* -r
- name: Add .next folder to zip artifact for deployment run: zip -r release.zip ".next"
28
9

小提琴演奏主要基本功

0
归档:2024年9月分类:点滴生活

小提琴的演奏技术来说,要练好以下几个主要基本功:

一、运弓

优秀的演奏家能在小提琴上发出千变万化的声音,就运弓而言,取决于运弓的速度、弓在弦上的压力以及弓和弦的接触点这3种因素的不同结合。小提琴的弓法繁多,就其主要的有以下几种:
①分弓:一弓演奏一个音;
②连弓:一弓演奏许多音;
③顿弓:音与音之间断开;
④跳弓:弓毛离开琴弦。

这4类弓法是最基本的,在20世纪中期,连顿弓,即在一弓中连续快速演奏许多音与音之间是断开的音,被人视为绝技,所以人们把小提琴演奏艺术称之为“运弓的艺术”。

二、揉弦

通过左手手指在弦上的颤动,使声音的音高产生有规律的波动,称之为揉弦。

揉弦是小提琴演奏中极为重要的表现手段。揉弦分为手指的、手腕的和手臂的。优秀的演奏者不仅要掌握这3种揉弦方法,而且要学会使用不同速度和不同幅度的揉弦,在演奏不同作家,不同作品,不同乐句时,富于变化地运用揉弦。从揉弦的运用,可以使听众明显地辨认出不同演奏家的音乐个性。

三、把位

左手手指在指板上的位置,称之为把位。靠近琴头的把位为低把,靠近琴马的为高把。从一个把位换到另一个把位,称为换把。

换把位的方法有多种,例如空弦换把,同指换把,不同指以及泛音换把等。换把时产生非音乐需要的滑音,是技巧训练不足的标志。滑音可以使音与音之间的连接富于变化,特别是结合换把使用滑音,是一种富于表现力的演奏手段。

四、双音与和弦

小提琴可以同时演奏两个音甚至是3个音,也可以分奏4个音的和弦,这不仅丰富了它的表现力,并可不依赖其他乐器的伴奏进行单独演奏。小提琴的三度、六度、八度以及十度双音音阶,是演奏双音的基础,也是小提琴家必须终身练习的一项基本功。小提琴演奏中的左手颤音、泛音、拨弦等,都是一些高深的技巧。

09
9

随着前端技术的快速发展,React作为一款领先的JavaScript库,不断推动着前端开发的变革。近期,React官网的一个显著变化引起了广大开发者的关注:它不再推荐使用Create-React-App作为构建React应用的默认工具,而是转向了Next.js。

那么,Next.js究竟有何魔力,让React官网做出如此决策?本文将为你详细解析。

一、Next.js:React应用的“升级版”

Next.js并不是一个全新的框架,而是基于React的服务器端渲染框架。它提供了许多开箱即用的功能,使得开发者能够更高效地构建复杂、高性能的React应用。与Create-React-App相比,Next.js更像是一个“升级版”,它为React应用带来了以下显著的优势:

性能优化:Next.js支持自动静态优化(Automatic Static Optimization)和服务器端渲染(Server-Side Rendering),使得页面加载速度更快,用户体验更流畅。此外,Next.js还支持预渲染(Pre-rendering)和动态导入(Dynamic Imports),进一步提升了应用的性能。
数据获取简化:Next.js内置了数据获取功能,使得开发者能够更方便地从API或其他数据源获取数据,并将其注入到组件中。这种无缝的数据获取流程大大简化了开发过程,提高了开发效率。
强大的路由功能:Next.js提供了基于文件系统的路由功能,使得页面之间的导航变得简单直观。开发者只需创建相应的页面文件,Next.js就能自动为其生成路由。这种方式不仅易于理解,还能减少错误和提高可维护性。
更好的SEO支持:由于Next.js支持服务器端渲染和预渲染,因此它能够更好地处理页面渲染和加载问题,从而提高网站的搜索引擎优化(SEO)效果。

二、Create-React-App的局限性

虽然Create-React-App为React开发者提供了一个快速搭建项目的脚手架,但随着项目规模的扩大和复杂度的增加,其局限性也逐渐显现:

配置不灵活:Create-React-App为开发者提供了一套固定的配置,虽然这降低了入门门槛,但对于有特定需求的开发者来说,这种固定配置可能会成为束缚。
性能优化有限:Create-React-App主要关注于项目的快速搭建和开发体验,对于性能优化方面的支持相对有限。对于需要高性能的应用来说,开发者可能需要花费更多的时间和精力进行手动优化。
扩展性不足:随着项目的发展,开发者可能需要集成更多的功能和工具。然而,Create-React-App的扩展性相对有限,可能无法满足一些高级需求。

三、React官网推荐的背后

React官网之所以推荐Next.js而非Create-React-App,背后有多重原因。

首先,Next.js作为React的“升级版”,在性能、数据获取、路由等方面提供了更强大的支持,能够更好地满足现代Web应用的需求。
其次,随着前端技术的不断发展,开发者对于高性能、易扩展的应用框架的需求也在不断增加。Next.js正好符合这一趋势,能够为开发者提供更高效、更灵活的开发体验。
最后,React官网的推荐也反映了社区对于Next.js的广泛认可和支持。越来越多的企业和项目采用Next.js构建应用,证明了其在实际应用中的价值和优势。

四、结语

React官网推荐使用Next.js而非Create-React-App,是基于对现代Web应用需求的深入理解和对前端技术发展趋势的敏锐洞察。对于广大React开发者来说,了解和掌握Next.js将是一个值得投入时间和精力的选择。它将帮助你更高效地构建高性能、易扩展的React应用,提升你的开发能力和竞争力。

https://segmentfault.com/a/1190000044806695

25
7

Create React App 的演变

在 2016 年发布 Create React App 时,工具的环境是分散的。如果想要将 React 添加到现有应用,需要添加一个 script 标签或从 npm 中导入,然后调整现有的构建工具配置。但是,如果要从头开始创建一个仅使用 React 构建的新应用,则没有明确的方法可以做到这一点。
在 Create React App 之前,必须安装一堆工具并将它们连接在一起,提供正确的预设以使用 JSX,为开发和生产环境进行不同的配置,为资源缓存提供正确的设置,配置 linter 等,想要正确完成这一系列工作非常困难。人们通过创建和共享可以克隆的“样板”存储库来解决了这个问题。然而,这产生了另外一个问题:一旦在项目中调整了克隆的样板文件,就很难再拉取样板的更新。这样,项目的设置会变得旧,要么放弃更新,要么花费大量精力让所有工具再次协同工作。在快速发展的生态系统中,这非常困难。
Create React App 通过将多个工具组合在一个包中解决了这个问题。现在,如果想用 React 开始一个新项目,有一个明确的推荐方法(Create React App)可以做到这一点! 然后,每隔一段时间,可以更新这个包,以获得所有底层工具的更新。这种模型变得很流行,以至于今天有很多工具都以这种方式工作。Vite 确实是拥有相似愿景的最佳工具之一,并且在在某些方面更进一步。
Create React App 的目标是为大多数 React 用户提供启动新 React Web 应用的最佳方式,它支持一组协同工作的精选功能。随着时间的推移,它提供的开箱即用的“baseline”会随着我们找到正确的权衡而扩大。 例如,为运行时错误添加了一个遮罩层,添加了对不同样式选项的支持,默认添加了快速刷新,它允许保存组件的代码并查看更改而不会丢失状态。对于默认的 React 开发体验来说,这是一个巨大的里程碑。总的来说,由于 Create React App 完全控制了编译管道,因此添加编译相关的功能是很容易的。
有这样一个精心策划的设置对生态系统仍然很有价值。当 React Hooks 出现时,React 团队将 React Hooks lint 规则添加到默认设置中。除此之外,Create React App 还允许 React 团队向尽可能广泛的受众部署重要的工具更改(快速刷新支持、React Hooks lint 规则)。 如果没有 React 团队策划的流行模板,将很难如此广泛地推出这些工具更改。

Create React App 的问题

随着时间的推移,Create React App 停滞不前。许多人指出它比替代品慢,并且不支持人们如今想要使用的一些流行工具。原则上,React 团队可以解决这些问题。例如,可以更新Create React App 内部,以使用更快的 bundler,甚至在内部使用 Vite。或者可以建议人们从 Create React App 迁移到 Vite 这样的应用。然而,React 团队还想解决一个更深层次的问题。
按照设计,Create React App 会生成一个纯客户端应用。这意味着用它创建的每个应用都包含一个空的 HTML 文件、一个带有 React 的 script 标签和应用包。当加载空的 HTML 文件时,浏览器会等待 React 代码和全部应用包下载。这在低带宽连接上可能需要一段时间,并且此时用户在屏幕上看不到任何内容。然后,加载应用代码。此时用户会在屏幕上看到一些内容——但通常还需要加载数据。所以代码发送了加载数据的请求,用户需要等待它返回。 最后,数据加载,组件重新渲染数据,用户看到最终结果。
这是非常低效的,尽管如果只在客户端运行 React 很难做得更好。将其与 Rails 这样的服务端框架进行对比:服务端将立即启动数据获取,然后生成包含所有数据的页面。在这种情况下,用户会看到包含所有信息的 HTML 文件,而不是等待加载脚本的空白文件。HTML 是Web 的基石——那么为什么创建React 应用会产生一个空的 HTML 文件?为什么不利用 Web 最基本的功能——在所有交互代码加载之前快速查看内容的能力? 为什么要等到所有客户端代码加载完成后才开始加载数据?
Create React App 只解决了问题的一方面,它提供了良好的开发体验,但它没有强加足够的结构来帮助我们利用 Web 的强大功能获得良好的用户体验。开发者可以尝试自己解决这些问题,但这违背了 Create React App 的宗旨。每个真正高效的 React 设置都是自定义的、不同的,并且是 Create React App 无法实现的。
这些用户体验问题并不是 Create React App 特有的。它们甚至不特定于 React。例如,从 Preact、Vue、Lit 和 Svelte 的 Vite 主页模板创建的应用都会遇到相同的问题。这些问题是没有静态站点生成 (SSG) 或服务端渲染 (SSR) 的纯客户端应用所固有的。

React 框架的兴起

有些人可能不喜欢完全使用 React 进行构建。例如,可以在服务端或在构建过程中使用不同的工具(如 Jekyll 或 Astro)生成 HTML 页面。这解决了空 HTML 文件的问题,但是必须混合使用两种渲染现技术。随着时间的推移,想要添加的交互性越多,这种技术割裂就越明显。
这种割裂不仅会损害开发人员的体验——用户体验也会受到影响。使用真正以 HTML 为中心且未充分利用 React 的工具,每个页面导航都会变成完整的页面重新加载,从而清除了所有客户端状态。如今,许多用户希望在应用内导航顺畅,而不是 90 年代风格的整页重新加载。同样,许多开发人员更喜欢使用单一的渲染模型而不是混合两种不同的模型来构建他们的应用。开发者想用 React 构建整个应用。
如果使用 React 构建整个应用,那么能够使用 SSG/SSR 很重要。 在 Create React App 中缺乏对它们的支持。除此之外,经过多年的生态系统创新,React 的许多其他问题现在都有了成熟的解决方案。 例如,network waterfalls 和 bundle 大小。
即使应用没有像面向内容的网站那样从 SSG 或 SSR 中获益,它也可能会受到网络瀑布的影响。如果在挂载时获取数据,则在加载所有代码并渲染组件之前,第一次数据获取甚至不会开始。这是一个 waterfall:如果应用知道如何在代码仍在加载时开始获取数据,那么就可以并行完成。在导航中,如果父组件和子组件都需要获取某些内容,则会产生更糟糕的 waterfall。当我们谈论 React 性能时,无法回避一个事实:对于如此多的应用来说,waterfall 是性能的瓶颈。要解决这些 waterfall,需要将数据获取与路由集成起来,而Create React App 无法做到这一点。
我们的应用代码会随着添加的每个新功能和额外依赖项而不断增长。如果经常部署,应用在每次使用时加载速度可能会变得非常慢,因为它总是需要加载所有代码。有几种方法可以解决这个问题;可以移动一些代码以在服务端或在构建期间运行(如果工具允许)。理想情况下,还可以按路由拆分代码。然而,如果尝试手动进行代码拆分,通常会使性能更差。要解决这一问题,需要将数据获取与路由和打包相结合,而 Create React App 无法做到这一点。
React 本身只是一个库。它不规定如何使用路由或数据获取。 Create React App 也没有。不幸的是,这意味着单靠 React 和最初设计的 Create React App 都无法解决这些问题。服务端渲染和静态生成、数据获取、打包和路由都是相关联的。当 Create React App 发布时,React 还很新,如何让这些功能独立工作都还有很多东西需要弄清楚,更不用说如何完美地将它们组合在一起了。
时代在发展,现在,越来越难以推荐无法获得这些功能的解决方案。即使不立即使用它们,它们也应该在需要时可用,并且不必迁移到不同的模板并重新构建所有代码即可利用它们。 同样,并非所有数据获取或代码拆分都需要基于路由。但这是一个很好的默认设置,应该适用于大多数 React 应用。
虽然可以自己整合所有这些功能,但很难好。 就像 Create React App 本身集成了与编译相关的几个功能一样,Next.js、Gatsby 和 Remix 等工具跟进一步——将编译与渲染、路由和数据获取集成在一起。这类集编译、渲染、路由和数据获取于一体的工具被称为“框架”(或者,如果喜欢称 React 为框架的话,可以称它们为“元框架”)。这些框架提供了更好的用户体验。
React 作为一个架构
我们喜欢 React 的灵活性,可以使用 React 构建单个按钮,也可以使用它构建整个应用。 可以使用它在已有 20 年历史的 Perl 网站中构建仪表板,或者可以使用 React 制作混合 SSG/SSR 的电子商务网站。这种灵活性是必不可少的,用户也很喜欢它。
React 团队也希望为完全使用 React 构建的新应用提供更好的默认设置。如果默认建议的创建 React 应用的方法支持 SSG 和 SSR、自动代码拆分、路由预加载、保留客户端 UI 状态的导航以及其他可实现出色用户体验的功能,就太好了。至少,创建 React 应用的默认建议方式不应该完全被排除在这些功能之外,因为现有的仅客户端架构没有实现这些功能。
React 面临着挑战,帮助 React 框架提供出色用户体验的最佳方式就是专注于 React 的底层。 React 本身可以在渲染层做一些独特的事情,这些事情大大提高了框架在其他层的能力。例如,与一样,一个 React API 可以在幕后为框架解锁一系列框架优化。
React 是一个库,它提供了一些 API,可让定义和组合组件。 React 也是一种架构,它提供了让框架作者充分利用其渲染模型的构建块。我们可以在没有框架的情况下使用 React。 但需要确保,如果将它与框架一起使用,框架能够充分利用 React。 在过去几年中构建的许多功能(、useTransition、流式 API(如 renderToPipeableStream 和实验性的服务端组件)都是面向框架的。 它们让框架通过集成打包、路由和数据获取来充分利用 React。
可以看到,Next 13、Gatsby 5 和 Remix 1.11 中采用了其中一些功能。还有很多工作要做,其中一些工作正在从实验阶段结束。 尽管如此,React 团队还是很高兴看到多年的努力得到了回报,并使 React 框架(及其用户)能够发布更快的应用。

一个库,多个框架

React 生态系统更适合拥有众多竞争,有多种竞争的数据获取解决方案和路由解决方案。这些选择每年都会变得更好。也有多种集成路由、数据获取、渲染和编译的解决方案——即多个 React 框架。
我们希望保持这种状态,也希望在可能的情况下鼓励融合并使 React 生态系统从中受益。例如,不同的框架可能使用不同的机制来加载数据。但是,如果它们都采用 Suspense 作为加载指示器,那么在 Suspense 中的更高级别的功能将适用于所有框架。
如果大多数 React 应用的最佳方式是从一个框架开始,那我们应该建议使用哪个框架? 我们应该选择一个吗? 我们如何决定选择哪一个? 如果它随着时间的推移停滞不前怎么办? 这就引出了上面提到的问题。
我们应该用 Create React App 做什么?
Create React App 最初的目标是:
• 提供一种无需配置即可启动新 React 项目的简单方法;
• 集成编译相关依赖,方便升级;
• 让 React 团队尽可能广泛地部署工具更新(例如快速刷新支持、Hooks lint 规则)。
然而,它不再满足最初的目标,即成为创建 React 应用的最佳方式。通过提高标准并将编译与渲染、路由和数据获取相集成,框架可以让用户创建 React 应用时:
• 充分利用 Web API 来默认提供快速的应用和网站,无论大小;
• 充分利用 React 及其框架级功能;
• 提供路由和数据获取。
React 生态系统值得推荐一种默认的方法,它可以充分利用 Web 和 React 本身。这甚至并不意味着一定要依赖于 Node.js 服务器。 许多流行的框架不需要服务器并且可以在 SSG 模式下工作,因此它们也可以解决“完全静态”的用例。 框架的好处就是,如果以后需要 SSR,不需要进行迁移,它和其他功能一样开箱即用(例如,Remix 提供了开箱即用的 mutation API)。
那该如何实现这一愿景?有以下选择:
选项 1:从头开始创建一个新框架
可以尝试将 Create React App 重新设计架构成为一个集成数据获取、路由、打包和 SSG/SSR 的框架。构建一个高质量的新框架是一项艰巨的任务,需要大量的生态系统专业知识,即使停止其他项目来实现这一目标,也会存在着随着时间的推移出现停滞不前的重大风险,就像 Create React App 一样。它还会进一步分裂生态系统,尽管没有真正的用户。 所以认为这个选项目前不实用。
选项 2:弃用 Create React App,维护一个Vite模板
可以弃用 Create React App,维护自己的 Vite 模板。 为了实现这个目标,该模板必须非常复杂。 事实上,它必须像 React 框架一样复杂——并且需要集成路由、数据获取等功能。这导致了同样的问题:实际上是在创建另一个框架。
选项 3:弃用 Create React App,建议使用 React 框架
可以不再强调或反对将 Create React App 作为工具,而是更积极地强调 React 框架。这并不意味着必须使用其中一个React框架,但建议在大多数应用中使用其中一个框架。不利的一面就是,这将影响 React 的品牌(“为什么不推荐创建 React 应用?”)。
选项 4:让 Create React App 使用单一框架
可以选择一个指定框架,并更改 Create React App 以默认使用该框架创建应用。这种方法的主要问题是它使其他解决方案很难竞争——尤其是当它们的权衡略有不同,但在流行度、功能集和质量方面大致相同时。 这种行为上的改变也是具有破坏性的,因为所有的旧教程都会以一种不明显的方式中断。
选项 5:将 Create React App 变成启动器
可以将 Create React App 保留为命令,但将其变成启动器。它将建议一个推荐框架列表,然后是“经典”无框架方法。“经典”方法将产生一个像 CRA 现在这样的客户端专用应用(以避免破坏已有教程),但内部最终可能会使用 Vite。
要想进入精选框架列表,React 框架必须满足特定条件。需要考虑社区的流行度和采用率(以保持列表简短)、功能集、性能特征、充分利用 Web 平台和 React 本身的能力、它是否得到积极维护以及是否清楚如何在各种托管服务和环境中托管它。每个框架的入门模板将由 React 团队维护,以确保它们具有一致的设计和品牌,不链接到商业服务,并且结构相似。需要向社区清楚地传达是如何做出这些选择的,并且会定期重新评估它们。

React 团队的建议

React 团队目前倾向于选项 5(“将 Create React App 变成启动器”)。 Create React App 的最初目标是为大多数 React 用户提供启动新的 React web 应用的最佳方式。重新调整它的用途,启动器明确传达了我们认为最适合大多数新 Web 应用的转变。与选项 3 不同,它避免了“创建一个 React 应用”在某种程度上被弃用的看法。
React 团队将制定更详细的 RFC 提案,以充实这些要点。同时,希望听到对这些问题的更多反馈。
对于使用 Vite 替换 Create React App, 你有什么看法?欢迎在评论区分享~
参考:github.com/reactjs/rea…

09
7

Create React App(以下简称 CRA)是一个官方支持的创建 React 单页应用的脚手架,它提供了一个零配置的现代构建设置,将一些复杂工具(比如 webpack, Babel)的配置封装了起来,让使用者不用关心这些工具的具体配置,从而降低了工具的使用难度。

创建方法

npx: npx 来自 npm 5.2+ 或更高版本

npx create-react-app my-app
npm: npm init 在 npm 6+ 中可用

npm init react-app my-app
Yarn: yarn create 在 Yarn 0.25+ 中可用

yarn create react-app my-app
Scripts
在新创建的项目中,你可以运行一些内置命令:

npm start 或 yarn start
在开发模式下运行应用程序, 默认在浏览器打开http://localhost:3000。如果更改代码,页面将自动重新加载。

npm test 或 yarn test
以交互模式运行测试程序。 默认情况下,运行与上次提交后更改的文件相关的测试。

npm run build 或 yarn build
将生产环境的应用程序构建到 build 目录。 它能将 React 正确地打包为生产模式中并优化构建以获得最佳性能。构建将被压缩,文件名中将包含哈希。

npm run eject
注意:这是单向操作。一旦 eject ,就回不去了!
执行完这个命令后会将封装在 CRA 中的配置全部反编译到当前项目,这样开发者完全取得 webpack 文件的控制权,可以自定义修改webpack打包配置。

默认文件结构

创建后,项目文件结构如下所示:

my-app
node_modules
public
favicon.ico
index.html // 页面模板
manifest.json
src
App.css
App.js
App.test.js
index.css
index.js // 项目入口
logo.svg
reportWebVitals.js
setupTests.js
.gitignore
package.json
README.md
yarn.lock
为了加快重新构建的速度,Webpack 只处理 src 中的文件。 你需要将 JS 和 CSS 文件放在 src 中,否则 Webpack 将发现不了。
只能在 public/index.html 中使用 public 中的文件。
manifest.js: 将Web应用程序安装到设备的主屏幕,为用户提供更快的访问和更丰富的体验。

项目升级

Create React App 分为两个包:

create-react-app 是一个全局命令行实用程序,可用于创建新项目。
react-scripts 包含Create React App的脚本与配置
当你运行 create-react-app 时,它始终使用最新版本的 react-scripts 创建项目,新创建的应用会获得所有新功能和改进。

CRA 将所有新特性委托给 react-scripts , 只需要更新 react-scripts, 不需要更新 create-react-app 就可以升级CRA 的特性。比如用老版本 CRA 创建了一个项目,这个项目不具备 PWA 功能,但只要项目升级了 react-scripts 包的版本就可以具备 PWA 的功能,项目本身的代码不需要做任何修改。

如何在启动项目时不清空 terminal?
在运行 yarn start 或 npm start 来启动 create-react-app 创建的项目时,默认会清空 terminal。实际在进行开发调试时,需要在 webpack 中输出一些信息。

解决方法:
1、修改node_modules/react-dev-utils/clearConsole.js 文件:

'use strict';

function clearConsole() {
if (process.env.REACT_APP_NO_CLEAR_CONSOLE) {
return;
}
process.stdout.write(
process.platform === 'win32' ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H'
);
}

module.exports = clearConsole;
2、修改 package.json文件,将变量 REACT_APP_NO_CLEAR_CONSOLE 添加到script命令中:

// package.json
{
"scripts": {
"start": "REACT_APP_NO_CLEAR_CONSOLE=true react-app-rewired start",
}
}
如何扩展 Create React App 的 Webpack 配置
Create React App已经封装了webpack 配置,如果想对 webpack 配置做一些修改,这个时候应该怎么办呢?CRA提供了以下几种方式来修改 webpack 的配置:

eject 命令

替换 react-scripts 包
使用 react-app-rewired
scripts 包 + override 组合
customize-cra 【推荐】
eject 命令
使用 CRA 创建完项目以后,在package.json里面提供了这样一个命令:

{
...
"scripts": {
"eject": "react-scripts eject"
},
...
}
执行yarn eject后会将封装在 CRA 中的配置全部复制到当前项目。eject 后项目根目录下会新增 config与scripts 文件夹,修改package.json与yarn.lock文件。

config
jest
env.js
getHttpsConfig.js
modules.js
paths.js
pnpTs.js
webpack.config.js
webpackDevServer.config.js
scripts
build.js
start.js
test.js
如果使用了eject命令,虽然扩展了 webpack 配置,但是再也享受不到 CRA 升级带来的好处了。因为react-scripts已经是以文件的形式存在于你的项目,而不是以包的形式,所以无法对其升级。

替换 react-scripts 包

react-scripts 是 CRA 的一个核心包,一些脚本和工具的默认配置都集成在里面,使用 CRA 创建项目默认就是使用这个包。但是 CRA 还提供了另外一种方式来创建 CRA 项目,用自定义 scripts 包的方式。

// 默认方式
$ create-react-app my-app

// 自定义 scripts 包方式
$ create-react-app my-app --scripts-version 自定义包
自定义包可以是下面几种形式:

react-scripts包的版本号,比如0.8.2,这种形式可以用来安装低版本的react-scripts包。
一个已经发布到 npm 仓库上的包的名字,比如your-scripts,里面包含了修改过的 webpack 配置。
一个 tgz 格式的压缩文件,比如/your/local/scripts.tgz,通常是未发布到 npm 仓库的自定义 scripts 包,可以用 npm pack 命令生成。
这种方式相对于之前的eject是一种更灵活地修改 webpack 配置的方式,而且可以做到和 CRA 一样,通过升级 scrips 包来升级项目特性。
自定义 scripts 包的结构可以参照react-scripts包的结构,只要修改对应的 webpack 配置文件,并安装上所需的 webpack loader 或 plugin 包就可以。

使用 react-app-rewired
react-app-rewired 是 react 社区开源的一个修改 CRA 配置的工具,这种方式让开发者既不用eject项目也不用自己创建 scripts 包就可以拓展webpack。

如何使用

1.在 CRA 创建的项目中安装react-app-rewired

npm install react-app-rewired --save-dev
2.在项目根目录下创建config-overrides.js 文件(支持自定义文件路径)

/ config-overrides.js /

module.exports = function override(config, env) {
// 参数中的 config 就是默认的 webpack config

// 对 config 进行任意修改
config.mode = 'development';

// 一定要把新的 config 返回
return config;
}
config-overriders.js 导出的是一个函数,这个函数的签名是 const override = (oldWebpackConfig, env) => newWebpackConfig。

3.修改 scripts 命令:

/ package.json /

"scripts": {

  • "start": "react-scripts start",
  • "start": "react-app-rewired start",
  • "build": "react-scripts build",
  • "build": "react-app-rewired build",
  • "test": "react-scripts test",
  • "test": "react-app-rewired test",
    "eject": "react-scripts eject"
    }
    自定义 config-overrides.js 文件路径
    通过package.json 的config-overrides-path设置自定义路径:

/ package.json /
{
...
"config-overrides-path": "src/app", // src/app/config-overrides.js
...
}
config-overrides.js 文件
默认情况下,config-overrides.js文件导出一个函数,这个函数的签名是 const override = (oldWebpackConfig, env) => newWebpackConfig(oldWepbackConfig 和 newWebpackConfig 实际指向同一个对象,因为直接在原来的 webpack config 对象上进行修改)。

也可以改为此文件导出一个对象,该对象最多包含三个字段,每个字段都是一个函数。

module.exports = {
// The Webpack config
webpack: function(config, env) {
// ...add your webpack config
return config;
},
// The Jest config
jest: function(config) {
// ...add your jest config customisation...
return config;
},
// create a webpack dev server
devServer: function(configFunction) {
return function(proxy, allowedHost) {
const config = configFunction(proxy, allowedHost);
const fs = require('fs');
config.https = {
key: fs.readFileSync(process.env.REACT_HTTPS_KEY, 'utf8'),
cert: fs.readFileSync(process.env.REACT_HTTPS_CERT, 'utf8'),
ca: fs.readFileSync(process.env.REACT_HTTPS_CA, 'utf8'),
passphrase: process.env.REACT_HTTPS_PASS
};
return config;
};
},
paths: function(paths, env) {
// ...add your paths config
return paths;
},
}

实现原理实现原理

编译时,react-app-rewired 会先取到 create-react-app 生成的默认的 webpack config,然后调用 override(config) 方法,对 config 进行修改,得到新的 webpack config。webpack 最终会使用这个新的 config 进行打包。

流程大致如下:

const overrides = require('../config-overrides');
const webpackConfigPath = paths.scriptVersion + "/config/webpack.config.prod";

// load original config
const webpackConfig = require(webpackConfigPath);

// override config in memory
require.cache[require.resolve(webpackConfigPath)].exports =
overrides.webpack(webpackConfig, process.env.NODE_ENV);

// run original script
require(paths.scriptVersion + '/scripts/build');
scripts 包 + override 组合
虽然 react-app-rewired 的方式已经可以很方便地修改 webpack 的配置了,但也可以在自定义的 script 包中实现类似的功能。

以 build.js 为例,在获取基本 webpack 配置对象和使用 webpack 对象之间加入以下代码:

// override config
const override = require(paths.configOverrides);
const overrideFn = override || ((config, env) => config);
const overrideConfig = overrideFn(config, process.env.NODE_ENV);
overrideConfig 就是修改后的 webpack 对象,最后修改调用了 webpack 对象的代码,将原来的 webpack 对象替换成修改后的 webpack 对象。

customize-cra
react-app-rewired 原生写法,对 webpack config 的修改全部写在 override() 一个方法中,不够模块化。customized-cra 提供了一些 helper 方法,可以将每一个独立的修改放到单独的函数中,再串行执行这些函数。
customize-cra 依赖于 react-app-rewired 库,通过 config-overrides.js 来修改底层的 webpack,babel等配置。

安装
yarn add customize-cra react-app-rewired --dev

使用
customize-cra 导出 customizers 和 utilities 两种类型的api,查看 api docs了解更多。

customizers: 是对配置对象进行修改的方法, 让用户轻松启用或禁用 webpack,webpack-dev-server,babel等功能。
utilities: 定制一些方法用于浏览其配置。
所有代码都应写在 config-overrides.js 文件中。
参阅 api.md 文档获取有关 customize-cra 提供的功能。
const {
override,
addWebpackAlias,
} = require("customize-cra");
const path = require("path");

module.exports = override(
// add an alias for "page" imports
addWebpackAlias({
page: path.resolve(__dirname, "src/page")
}),
);
override()
新的 override() 方法,是一个高阶函数,接受可变数量的参数,每个参数都是签名为 const fn = (oldConfig) => newConfig 的函数;同时会返回一个新的函数,这个函数的签名也是 const fn = (oldConfig) => newConfig。

override() 会在内部依次调用传入的参数函数,把前一个函数返回的 newConfig 作为后一个函数的 oldConfig 参数,得到最终的 webpack config。

大致实现如下:

function override(fns) {
return function (oriConfig) {
let finalConfig = oriConfig
for (const fn of fns) {
finalConfig = fn(finalConfig)
}
return finalConfig
}
}
如何添加文件别名?
通过 addWebpackAlias,添加文件别名。

// config-overrides.js
const { override, addWebpackAlias } = require('customize-cra')
const path = require("path")

module.exports = override(
addWebpackAlias({
"components": path.resolve(__dirname, "src/components")
}),
)
fixBabelImports()
babel 模块化导入插件。具体查看 babel-plugin-import

const { override, fixBabelImports } = require("customize-cra");

module.exports = override(
fixBabelImports('antd', {
"libraryName": "antd",
libraryDirectory: 'es',
"style": true,
})
);
style
style: true 导入CSS源文件,在编译期间进行优化。可以明显减小分发包的大小,具体取决于对库的使用情况。
style: css 将预捆绑的CSS文件直接导入。

如何自定义 babel?
通过 useBabelRc,实现自定义 babel。

// config-overrides.js
const { override, useBabelRc } = require('customize-cra')

module.exports = override(
useBabelRc(),
)
项目根目录新建 babelrc文件:

// .babelrc
{
"presets": ["@babel/env"],
"plugins": ["@babel/plugin-transform-runtime"]
}
如何添加插件?
通过 addWebpackPlugin,添加插件。

// config-overrides.js
const { override, addWebpackPlugin } = require('customize-cra')
const DefinePlugin = require('webpack').DefinePlugin
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin

module.exports = override(
addWebpackPlugin(
new DefinePlugin({
'test': 12244,
}),
),
addWebpackPlugin(new BundleAnalyzerPlugin()),
)
如何 SplitChunks?
通过 setWebpackOptimizationSplitChunks,自定义拆包。

// config-overrides.js
const { override, setWebpackOptimizationSplitChunks } = require('customize-cra')

module.exports = override(
setWebpackOptimizationSplitChunks({
maxSize: 1024 1024 3,
minChunks: 2,
})
)

26
6

MiniProfiler工具介绍

0
归档:2024年6月分类:C#和.NET

在日常开发中,应用程序的性能是我们需要关注的一个重点问题。当然我们有很多工具来分析程序性能:如:Zipkin等;但这些过于复杂,需要单独搭建。

MiniProfiler就是一款简单,但功能强大的应用新能分析工具;可以帮助我们定位:SQL性能问题、响应慢等问题。

MiniProfiler是一款针对.NET, Ruby, Go and Node.js的性能分析的轻量级程序。可以对一个页面本身,及该页面通过直接引用、Ajax、Iframe形式访问的其它页面进行监控,监控内容包括数据库内容,并可以显示数据库访问的SQL(支持EF、EF CodeFirst等 )。并且以很友好的方式展现在页面上。

MiniProfiler官网:http://miniprofiler.com/

MiniProfiler的一个特别有用的功能是它与数据库框架的集成。除了.NET原生的 DbConnection类,MiniProfiler还内置了对实体框架(Entity Framework)以及LINQ to SQL、RavenDb和MongoDB的支持。任何执行的Step都会包括当时查询的次数和所花费的时间。为了检测常见的错误,如N+1反模式,profiler将检测仅有参数值存在差异的多个查询。

公告栏

欢迎大家来到我的博客,我是dodoro,希望我的博客能给你带来帮助。