mirror of
https://github.com/Pixeval/Pixeval.git
synced 2025-01-08 11:47:34 +08:00
157 lines
9.6 KiB
Markdown
157 lines
9.6 KiB
Markdown
# Pixeval 贡献指南
|
||
|
||
## LoginPage
|
||
|
||
用户登录的界面,应该以尽量简单可靠的方式完成登录。
|
||
|
||
如果可以找到pixiv登录的api是最好的方法,可以直接不使用WebView来登录,但可能没有这种方法,毕竟官方的app也是使用网页登录。
|
||
|
||
如果可以使用外部浏览器来登录也很好,但这样缺点是难以操作代理设置,返回token的时候也需要注册应用链接打开,十分麻烦。
|
||
|
||
原来使用Playwright当自动化登录的方式,但Playwright包太大了(WebView包不包含在应用包内)故使用js代码直接操作。
|
||
pixiv的页面是使用react写的,设置自动化费了好大劲,最终参考[这个方法](https://www.jianshu.com/p/78f5a4baf88c)实现了填写表单。
|
||
|
||
## 控件命名方式
|
||
|
||
### XXXItem
|
||
|
||
如`IllustrationItem`等,表示可以在列表视图中展示的一个卡片元素,可以展示一个画师,一幅插画等。
|
||
一般由`AdvancedItemsView`进行承载,并支持加载更多。
|
||
|
||
### XXXView
|
||
|
||
如`IllustrationView`等,是包含了一个`AdvancedItemsView`来展示`XXXItem`的控件,方便控件的复用。
|
||
也有一些`AdvancedItemsView`控件,由于目前没有复杂的复用需求,所以被直接包含在某些页面里了。
|
||
|
||
### XXXContainer
|
||
|
||
如`IllustrationContainer`等,是包含了一个`XXXView`的控件,并且加上了上部的工具栏,可以简单地往里面添加`ComboBox`等控件。
|
||
由于逻辑过于简单,所以没有对应的XAML文件。
|
||
|
||
### XXXViewerPage
|
||
|
||
如`IllustrationViewerPage`等(`ImageViewerPage`除外),表示用一个单独窗口来展示详细信息的页面,可以展示一个画师,一幅插画等。
|
||
由于单独使用一个窗口,所以要继承自`SupportCustomTitleBarDragRegionPage`来支持拖拽区域定义,
|
||
同时搭配一个`XXXViewerPageHelper`的静态类,用来方便地从任何代码上下文呼出包含`XXXViewerPage`的窗口。
|
||
|
||
### EntryXXX
|
||
|
||
一般表示被Illustration、Illustrator、Novel共用的模型,如`IllustrationView`、`IllustratorView`都包含`EntryView`。
|
||
本来打算是让`IllustrationView`、`IllustratorView`等继承`EntryView`,但由于都有XAML,会导致`InitializeComponent`重定义,所以最终没有这样做。
|
||
|
||
### WorkXXX
|
||
|
||
Work表示作品,所以这个一般表示被Illustration、Novel共用的模型,由于它的ViewModel既可以接受Illustration,也可以接受Novel,
|
||
所以从理论上来说这个取代了相应的`IllustrationXXX`和`IllustratorXXX`。例如`WorkView`就取代了`IllustrationView`和`IllustratorView`。
|
||
|
||
### AdvancedItemsView
|
||
|
||
继承自`ItemsView`,除了可以方便地指定`ItemsViewLayoutType`外,还支持滚动到底部时增量加载新的数据(需要`ItemsSource`实现`ISupportIncrementalLoading`)。
|
||
和`ItemsView`一样,它要求`ItemTemplate`中的控件被`ItemContainer`包裹。
|
||
|
||
## 模型类设计
|
||
|
||
### 下载模型
|
||
|
||
以`XXXDownloadTask`格式命名的都是下载模型,每种模型分为`XXXDownloadTask`、`IntrinsicXXXDownloadTask`、`LazyInitializedXXXDownloadTask`三种,
|
||
分别是普通的下载,已经完成的下载,和已经完成并且要懒加载的下载。其中后两种直接继承于第一种,第三种主要用在重新打开应用时显示。
|
||
|
||
### 增量加载器
|
||
|
||
与此相关的有`XXXDataProvider`和`XXXIncrementalSource`。
|
||
一般来说它们分别继承于`IDataProvider<T, TViewModel>`和`FetchEngineIncrementalSource<T, TModel>`,
|
||
(如`IllustrationViewDataProvider`和`IllustrationFetchEngineIncrementalSource`)但并非必要。
|
||
如果不是从网络上获取的数据源(如本地数据源),则不需要继承那么多方法,可以直接自己实现一个类似的即可
|
||
(如`DownloadListEntryDataProvider`和`DownloadListEntryIncrementalSource`)。
|
||
|
||
总的来说,`XXXIncrementalSource`是从数据源(如`IFetchEngine<T>`等)获取数据,并对外封装为`IEnumerable<T>`的形式,
|
||
`XXXDataProvider`是对`XXXIncrementalSource`的封装,管理它的新建、刷新、`Dispose`等功能,所以不是必要的。
|
||
|
||
## 工具类设计思想
|
||
|
||
### WindowsFactory
|
||
|
||
一个统一的窗口构建类,窗口默认包含一个`Frame`,用来承载页面。
|
||
由于`Window`类并不是继承自`UIElement`,在XAML等各处使用都很不方便,故通过封装隐藏所有操作。
|
||
除了第一个窗口,其他都是第一个窗口的子窗口。
|
||
|
||
### EnhancedPage/EnhancedWindowPage/SupportCustomTitleBarDragRegionPage
|
||
|
||
`EnhancedPage`通过封装将`OnNavigatedTo`、`OnNavigatingFrom`封装为更简单的`OnPageActivated`、`OnPageDeactivated`。
|
||
并且记录了同一个页面的导航次数、自动清理页面缓存。
|
||
|
||
`EnhancedWindowPage`继承自`EnhancedPage`,唯一的区别是在导航参数中隐式传递了所在的`Window`,可以方便使用一些需要`HWnd`的api。
|
||
这个参数在`EnhancedWindowPage`之间传递时是透明的。
|
||
如果调用导航的地方无法获取到现在所在的`Window`(比如说在层次很深的控件内),此时就应该使用`EnhancedPage`。
|
||
|
||
`SupportCustomTitleBarDragRegionPage`除了拥有`EnhancedWindowPage`的功能外,还添加一些方法用来计算、指定所在窗口的拖拽区域,
|
||
这对于自定义标题栏来说十分重要。一般这个页面会作为窗口内的底层页面。
|
||
|
||
### SharedRef
|
||
|
||
`SharedRef<T>`类似于C++的`shared_ptr<T>`,但由于C#语言限制,并不能做的十分完美。大致思想就是通过引用计数,来同步不同对象内对同一个对象引用的释放。
|
||
为了防止同一个对象错误地多次释放,释放时需要提供本对象的哈希值。
|
||
|
||
### AdvancedObservableCollection
|
||
|
||
类似于`AdvancedCollectionView`的泛型版本,但更好用,专门为`ItemsView/AdvancedItemsView`设计。
|
||
本质上是`ObservableCollection<T>`的底子上加了`ISupportIncrementalLoading`和筛选、排序功能。
|
||
|
||
`AdvancedCollectionView`实现了`ICollectionView`,所以支持和`ListView/GridView`中的`SelectedItems`同步,
|
||
但`ItemsView/AdvancedItemsView`并不支持`ICollectionView`,所以本类没有实现`ICollectionView`,也没有`SelectedItems`。
|
||
`AdvancedCollectionView`由于编写者疏忽,在许多地方都有一些BUG(尤其是插入新元素的逻辑),被发现的BUG在本类中都悉数修复了。
|
||
|
||
## 总体思想
|
||
|
||
### 少用WinRT API
|
||
|
||
例如`IRandomAccessStream`和`Stream`,项目中更倾向于使用原生的后者。除了因为WinRT需要COM可能会降低效率,也是因为在类型封送的时候不稳定,
|
||
可能导致`Position`为负数的情况。
|
||
|
||
WinRT自带的Bitmap解码器功能较差,经常出现正常图片无法解码的功能。本项目使用`ImageSharp`代替其解码、编码的职能。
|
||
|
||
剪切板api必须传入原生的`IRandomAccessStream`才能正常展示(`Stream.AsRandomAccessStream()`是不行的),但这个api别无选择。
|
||
|
||
### 重视内存管理
|
||
|
||
由于WinUI的内存泄露问题十分严重,在一切可以释放内存的地方都应该注意释放。例如项目中使用`SoftwareBitmap`和`SharedRef`就是为了尽快释放内存。
|
||
|
||
`RecyclableMemoryStream`可以提高`MemoryStream`的利用效率,在`IoHelper`中声明了`RecyclableMemoryStreamManager`,
|
||
如果需要大量使用`MemoryStream`,应该调用`IoHelper`。
|
||
|
||
### 通过继承、组合减少代码量
|
||
|
||
如`IllustrationView`、`IllustratorView`都包含`EntryView`,这是为了不要将相似的逻辑写两遍,否则在重构某部分时也许会漏掉另一部分。
|
||
提取不同类的共同部分并不总是为了抽象。
|
||
|
||
### XAML中少用复杂的Margin、Padding
|
||
|
||
布局如果可以就尽量使用`Grid`实现,`StackPanel`有时也可以使用,但它的堆叠方向长度是无穷的,导致难以适配父控件大小。
|
||
尽量使用父控件`Spacing`参数实现控件的间隔,指定`Padding`时也使用比较统一、简单的值(如资源中的`CardControlPadding`),
|
||
因为阅读XAML时复杂的值不方便人理解想象,在修改界面时也更难维护。
|
||
|
||
### 注意控件回收(Recycle)带来的BUG
|
||
|
||
被`DataTemplate`包裹的控件都可能会被回收。回收时XAML属性会被重新赋值,但不会重新触发从构造函数到`Loaded`中的内容。
|
||
如果XAML中的绑定没有使用`OneWay`,或者把某些数据加载逻辑写在`Loaded`中或之前,都有可能导致数据对不上的问题。
|
||
|
||
### 捕获网络异常
|
||
|
||
项目中最常出现的异常就是网络的异常,而且是不可避免的。如果要让网络的异常不影响到应用的正常运行,则需要抑制这些异常。
|
||
项目中引入了`FileLogger`,在抑制所有异常的同时记录,方便崩溃分析。
|
||
为了不需要重构更多代码(也为了不需要在每次网络请求时都考虑失败情况),在获得数据失败后会返回默认数据,给界面渲染。
|
||
|
||
相关api:`FactoryAttribute`,根据属性默认值自动生成默认赋值方法`CreateDefault`,在失败后会调用它并返回。
|
||
|
||
### 使用新的控件
|
||
|
||
在WASDK1.4中引入了新的控件`ScrollView`、`ItemsView`等,这些控件完全重写,使用更简洁的实现,获得比原来更多的功能。
|
||
而且在设计上也更符合直觉,黑箱操作更少。不过缺点是不够稳定,如`TagsEntry`中为了避免渲染BUG,
|
||
只好使用旧的`ItemsControl`+`ItemsPanel`代替新的`ItemsRepeater`+`Layout`
|
||
|
||
### 使用统一的命名空间,不必与文件夹结构同步
|
||
|
||
为了使`Controls`文件夹内文件更有层次,项目使用文件夹包裹这些控件,但可能会被提示应该与文件夹同步命名空间。
|
||
这时应该无视,因为在使用这些控件的时候可以用更加统一的命名空间(如`Pixeval.Controls`)来指定,无需写冗长的命名空间声明。
|
||
*CommunityToolkit*中也是这样做的。
|