Flutter 最佳实践和编码准则
Flutter 最佳实践和编码准则

视频
前言
最佳实践是一套既定的准则,可以提高代码质量、可读性和可靠性。它们确保遵循行业标准,鼓励一致性,并促进开发人员之间的合作。通过遵循最佳实践,代码变得更容易理解、修改和调试,从而提高整体软件质量。
原文 https://ducafecat.com/blog/flutter-best-practices-and-coding-guidelines
参考
https://dart.dev/effective-dart/style
正文开始
有许多准则和实践可以采用来提高代码质量和应用性能。
Naming convention 命名规范
-
类、枚举、类型定义、混入和扩展的名称应使用大驼峰命名法。
# Good
class ClassName {}
extension ExtensionName on String {}
enum EnumName {}
mixin MixinName{}
typedef FunctionName = void Function();
# Bad
class Classname {
}
extension Extensionname on String {
}
enum Enumname {
}
mixin Mixinname{}
typedef Functionname = void Function();
-
Libraries、包、目录和源文件的名称应该使用蛇形命名法(小写字母加下划线)。
# Good
my_package
└─ lib
└─ bottom_nav.dart
# Bad
mypackage
└─ lib
└─ bottom-nav.dart
-
导入的前缀命名应该使用蛇形命名法(小写字母加下划线)。
# Good
import 'package:dio/dio.dart' as dio;
#Bad
import 'package:dio/dio.dart' as Dio;
-
变量、常量、参数和命名参数应该使用小驼峰命名法。
# Good
int phoneNumber;
const pieValue=3.14;
// parametrs
double calculateBMI(int weightInKg, int heightInMeter) {
return weightInKg / (heightInMeter * heightInMeter);
}
//named parametrs
double calculateBMI({int? weightInKg, int? heightInMeter}) {
if(weightInKg !=null && heightInMeter !=null){
return weightInKg / (heightInMeter * heightInMeter);
}
}
# Bad
int phone_number;
const pie_value=3.14;
// parametrs
double calculateBMI(int weight_in_kg, int height_in_meter) {
return weight_in_kg / (height_in_meter * height_in_meter);
}
//named parametrs
double calculateBMI({int? weight_in_kg, int? height_in_meter}) {
return weight_in_kg / (height_in_meter * height_in_meter);
}
-
应该遵循适当有意义的命名规范。
# Good
Color backgroundColor;
int calculateAge(Date dob);
# Bad
Color bg;
int age(Date date);
-
私有变量名前面加下划线。
class ClassName {
// private variable
String _variableName;
}
使用可空运算符
在处理条件表达式时,建议使用 ??
(如果为null)和 ?.
(null aware)运算符,而不是显式的null检查。 ??
(如果为空)运算符:
# Bad
String? name;
name= name==null ? "unknown": name;
# Good
String? name;
name= name ?? "unknown";
?.
(空值安全)运算符:
# Bad
String? name;
name= name==null? null: name.length.toString();
# Good
String? name;
name=name?.length.toString();
为了避免潜在的异常情况,在Flutter中建议使用 is
运算符而不是 as
强制转换运算符。 is
运算符允许更安全地进行类型检查,如果转换不可能,也不会抛出异常。
# Bad
(person as Person).name="Ashish";
# Good
if(person is Person){
person.name="Ashish";
}
避免不必要地创建lambda函数
Lambda 函数(也称为匿名函数或闭包)是一种无需声明函数名称即可定义的函数。它是一种简洁、灵活的函数编写方式,通常用于需要传递函数作为参数或以函数作为返回值的语言特性中。
在 Dart 和许多其他编程语言中,Lambda 函数可以使用箭头语法或
() {}
语法来定义。例如,在 Dart 中,下面的代码演示了如何使用箭头语法定义一个 lambda 函数:在可以使用 tear-off 的情况下,避免不必要地创建 lambda 函数。如果一个函数只是简单地调用一个带有相同参数的方法,就没有必要手动将调用包装在 lambda 函数中。
# Bad
void main(){
List<int> oddNumber=[1,3,4,5,6,7,9,11];
oddNumber.forEach((number){
print(number);
});
}
# Good
void main(){
List<int> oddNumber=[1,3,4,5,6,7,9,11];
oddNumber.forEach(print);
}
使用扩展集合简化您的代码
-
当你已经在另一个集合中存储了现有的项目时,利用扩展集合可以简化代码。
# Bad
List<int> firstFiveOddNumber=[1,3,5,7,9];
List<int> secondFiveOddNumber=[11,13,15,17,19];
firstFiveOddNumber.addAll(secondFiveOddNumber);
# Good
List<int> secondFiveOddNumber=[11,13,15,17,19];
List<int> firstFiveOddNumber=[1,3,5,7,9,...secondFiveOddNumber];
使用级联操作简化对象操作
-
Cascades(级联)操作符非常适合在同一对象上执行一系列操作,使代码更加简洁易读。
class Person {
String? name;
int? age;
Person({
this.name,
this.age,
});
@override
String toString() {
return "name: $name age $age";
}
}
# Bad
void main(){
final person=Person();
person.name="Ashish";
person.age=25;
print(person.toString());
}
# Good
void main(){
final person=Person();
person
..name="Ashish"
..age=25;
print(person.toString());
}
使用if条件在行和列中实现最佳widget 渲染
-
在根据行或列中的条件渲染widget 时,建议使用if条件而不是可能返回null的条件表达式。
# Bad
Column(
children: [
isLoggedIn
? ElevatedButton(
onPressed: () {},
child: const Text("Go to Login page"),
)
: const SizedBox(),
],
),
# Good
Column(
children: [
if(isLoggedIn)
ElevatedButton(
onPressed: () {},
child: const Text("Go to Login page"),
)
],
),
使用箭头函数
-
如果一个函数只有一条语句,使用 () =>
箭头函数。
# Bad
double calculateBMI(int weight_in_kg, int height_in_meter) {
return weight_in_kg / (height_in_meter * height_in_meter);
}
# Good
double calculateBMI(int weight_in_kg, int height_in_meter) =>
weight_in_kg / (height_in_meter * height_in_meter);
删除任何打印语句、未使用的和被注释的代码
在 Flutter 中,使用
输出的信息可能难以区分:在 Flutter 应用程序中,输出的信息可能会与应用程序本身的输出混杂在一起,这可能会导致输出的信息难以区分。 输出的信息可能不可靠: 输出的信息可能会影响应用程序性能:在某些情况下,输出的信息可能会大量占用应用程序的资源,影响应用程序的性能。
因此,Flutter 推荐使用专门的日志记录库,如
logger
或flutter_bloc
中的BlocObserver
,以便在应用程序中输出可靠、易于区分和可控制的日志。这些库允许您定义输出的日志级别、输出到不同的目标(如控制台或文件)以及格式化日志消息等。例如,使用logger
库,您可以按以下方式输出日志消息:
# Bad
# production mode
// commented message---main method
void main(){
print("print statement");
//..rest of code
}
void unusedFunction(){
}
# Good
# production mode
void main(){
//..rest of code
}
正确的文件夹结构
-
将代码分离到适当的文件夹结构中,包括提供者(providers)、模型(models)、屏幕/页面(screens/pages)、服务(services)、常量(constants)和工具(utils)。
project/
lib/
providers/
auth_provider.dart
models/
user.dart
screens/
home_screen.dart
login_screen.dart
utils.dart
constants.dart
services.dart
main.dart
-
代码格式正确,适当使用 lints 配置。
include: package:flutter_lints/flutter.yaml
analyzer:
errors:
require_trailing_commas: error
linter:
rules:
require_trailing_commas: true
prefer_relative_imports: true
-
尝试通过在 utils 文件夹中保存的辅助函数中实现代码的可重用性。
# utils.dart
import 'package:intl/intl.dart';
String formatDateTime(DateTime dateTime) {
final formatter = DateFormat('yyyy-MM-dd HH:mm:ss');
return formatter.format(dateTime);
}
-
widget 还应该被设计成可重复使用的,并可以单独保存在widgets文件夹中。
# text_input.dart
import 'package:flutter/material.dart';
class TextInput extends StatelessWidget {
final String? label;
final String? hintText;
final TextEditingController? controller;
final TextInputType keyboardType;
final bool obscureText;
final String? Function(String?)? validator;
final Widget? suffix;
const TextInput({
this.label,
this.hintText,
this.suffix,
this.controller,
this.validator,
this.obscureText = false,
this.keyboardType = TextInputType.text,
});
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration(
labelText: label,
hintText:hintText
suffixIcon:suffix,
),
controller: controller,
obscureText: obscureText,
validator:validator
keyboardType: keyboardType,
);
}
}
-
在UI界面中避免使用静态或硬编码的字符串,建议根据其范围将其组织在单独的文件夹或文件中。
# Good
# validators/
common_validator.dart
mixin CommonValidator{
String? emptyValidator(String value) {
if (value.isEmpty) {
return 'Please enter';
} else {
return null;
}
}
}
#config/themes
colors.dart
class AppColors{
static const white=Color(0xffffffff);
static const black=Color(0xff000000);
}
class LoginPage extends StatelessWidget with CommonValidator {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: AppColors.black, // good
title: const Text("Login page"),
),
body: Column(
children: [
TextInput(
label: "email",
hintText: "email address",
validator: emptyValidator, // good
)
],
),
);
}
}
#Bad
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: const Color(0xff000000), // bad
title: const Text("Login page"),
),
body: Column(
children: [
TextInput(
label: "email",
hintText: "email address",
validator: (value) { // bad
if (value!.isEmpty) {
return 'Please enter';
} else {
return null;
}
},
)
],
),
);
}
}
widget 组织
-
将widget 拆分为不同的widget ,而不是同一个文件。 -
在widget 中使用const -
当在一个State上调用setState()时,所有子孙widget都会重新构建。因此,将widget拆分为小的widget,这样setState()调用只会重新构建那些实际需要改变UI的子树的部分。
# Bad
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
bool _secureText = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Login page"),
),
body: Column(
children: [
const TextInput(
label: "Email",
hintText: "Email address",
),
TextInput(
label: "Password",
hintText: "Password",
obscureText: _secureText,
suffix: IconButton(
onPressed: () {
setState(() {
_secureText = !_secureText;
});
},
icon: Icon(
_secureText ?
Icons.visibility_off
: Icons.visibility)),
),
ElevatedButton(
onPressed: () {},
child: const Text("Login"))
],
),
);
}
}
# Good
class LoginPage extends StatelessWidget {
const LoginPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Login page"),
),
body: Column(
children: [
const TextInput(
label: "Email",
hintText: "Email address",
),
const TextInput(
label: "Password",
hintText: "Password",
obscureText: true,
),
ElevatedButton(
onPressed: () {},
child: const Text("Login"))
],
),
);
}
}
//separate TextFormField Component
class TextInput extends StatefulWidget {
final String? label;
final TextEditingController? controller;
final String? hintText;
final TextInputType keyboardType;
final String? Function(String?)? validator;
final bool obscureText;
const TextInput({
super.key,
this.label,
this.hintText,
this.validator,
this.obscureText = false,
this.controller,
this.keyboardType = TextInputType.text,
});
@override
State<TextInput> createState() => _TextInputState();
}
class _TextInputState extends State<TextInput> {
bool _secureText = false;
@override
void initState() {
_secureText = widget.obscureText;
super.initState();
}
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration(
labelText: widget.label,
hintText: widget.hintText,
suffixIcon: widget.obscureText
? IconButton(
onPressed: () {
setState(() {
_secureText = !_secureText;
});
},
icon: Icon(
_secureText ? Icons.visibility_off : Icons.visibility,
color: Colors.grey,
),
)
: null),
controller: widget.controller,
validator: widget.validator,
obscureText: _secureText,
keyboardType: widget.keyboardType,
);
}
}
遵循代码规范
-
在lib/目录中,避免使用相对导入。请使用包导入。 -
避免使用 print 打印语句
# Bad
import 'widgets/text_input.dart';
import 'widgets/button.dart'
import '../widgets/custom_tile.dart';
# Good
import 'package:coding_guidelines/widgets/text_input.dart';
import 'package:coding_guidelines/widgets/button.dart'
import 'package:coding_guidelines/widgets/custom_tile.dart';
# Bad
void f(int x) {
print('debug: $x');
...
}
# Good
void f(int x) {
debugPrint('debug: $x');
}
linter:
rules:
- avoid_empty_else
- always_use_package_imports
- avoid_print
适当的状态管理
-
使用Provider作为推荐的状态管理包,但是Riverpod与Provider相似,可以被视为其改进版本。 -
您还可以选择使用其他状态管理方法,如Bloc、Riverpod、Getx和Redux。 -
业务逻辑应该与用户界面分离。
# Bad
class CounterScreen extends StatefulWidget {
const CounterScreen({
super.key,
});
@override
State<CounterScreen> createState() => _CounterScreenState();
}
class _CounterScreenState extends State<CounterScreen> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Counter APP"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
# Good
// separte logic from UI
// provider state management
class CounterProvider with ChangeNotifier {
int _counter = 0;
int get counter => _counter;
void incrementCounter() {
_counter++;
notifyListeners();
}
void decrementCounter() {
_counter--;
notifyListeners();
}
}
// UI
class CounterScreen extends StatelessWidget {
const CounterScreen({
super.key,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text("Counter APP"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Consumer<CounterProvider>(
builder: (context, counter, child) {
return Text(
counter.counter.toString(),
style: Theme.of(context).textTheme.headlineMedium,
);
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<CounterProvider>().incrementCounter(),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
升级第三方包
-
在应用程序中使用的任何第三方包都需要进行验证,因为有时它可能会破坏构建或与当前的Flutter版本不同步。特别是在升级Flutter时,务必在升级后检查所有插件和第三方包。请确保它们与当前版本兼容。
错误处理和日志记录
-
使用try-catch块来正确处理代码中的异常和错误。 -
使用像 pretty_dio_logger
或dio_logger
这样的日志记录库来记录重要事件或错误。
# Good
final dio = Dio()
..interceptors.add(PrettyDioLogger(
requestHeader: true,
requestBody: true,
responseBody: true,
responseHeader: false,
compact: false,
));
Future<dynamic> fetchNetworkData() async{
try {
// Simulating an asynchronous network call
final data= await dio.get('endpoint');
return data;
} catch (e, stackTrace) {
print('An exception occurred: $e');
print('Stack trace: $stackTrace');
return e;
// Perform additional error handling actions
}
}
# Bad
final dio = Dio();
Future<dynamic> fetchNetworkData() {
dio.get('endpoint').then((data){
return data;
)}.catchError((e) {
log.error(e);
return e;
});
}
Testing 测试
-
编写单元测试和widget 测试来确保代码的正确性。 -
使用像 flutter_test
这样的测试框架来编写和运行测试。 -
追求高代码覆盖率,尤其是对于应用程序的关键部分。
# Good
// counter app integartion testing
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('end-to-end test', () {
testWidgets('tap on the floating action button, verify counter',
(tester) async {
app.main();
await tester.pumpAndSettle();
// Verify the counter starts at 0.
expect(find.text('0'), findsOneWidget);
// Finds the floating action button to tap on.
final Finder fab = find.byTooltip('Increment');
// Emulate a tap on the floating action button.
await tester.tap(fab);
// Trigger a frame.
await tester.pumpAndSettle();
// Verify the counter increments by 1.
expect(find.text('1'), findsOneWidget);
});
});
}
版本控制和协作
-
使用像Git这样的版本控制系统来跟踪变更并与其他开发者合作。 -
遵循Git的最佳实践,例如创建有意义的提交信息和分支策略。
The commit type can include the following:
feat – a new feature is introduced with the changes
fix – a bug fix has occurred
chore – changes that do not relate to a fix or feature and don't modify src or test files (for example updating dependencies)
refactor – refactored code that neither fixes a bug nor adds a feature
docs – updates to documentation such as a the README or other markdown files
style – changes that do not affect the meaning of the code, likely related to code formatting such as white-space, missing semi-colons, and so on.
test – including new or correcting previous tests
perf – performance improvements
ci – continuous integration related
build – changes that affect the build system or external dependencies
revert – reverts a previous commit
# Good
feat: button component
chore: change login translation
# Bad
fixed bug on login page
Changed button style
empty commit messages
持续集成与交付
-
建立一个持续集成(CI)流水线,自动运行测试和检查你的代码库。 -
控制台可以用 CI services like Jenkins, Travis CI, or GitHub Actions.
写一些文档
-
使用注释来记录你的代码,尤其是对于复杂或不明显的部分。 -
请使用描述性和有意义的注释来解释代码片段的目的、行为或用法。 -
考虑使用Dartdoc等工具生成API文档。
小结
以上的编码准则可以帮助您提高编码标准,增强应用性能,并让您更好地理解最佳实践。通过遵循这些准则,您可以编写更清晰、更易维护的代码,优化应用性能,并避免常见的陷阱。
感谢阅读本文
如果我有什么错?请在评论中让我知道。我很乐意改进。
© 猫哥 ducafecat.com
end
本文由 mdnice 多平台发布
相关文章:

Flutter 最佳实践和编码准则
Flutter 最佳实践和编码准则 视频 前言 最佳实践是一套既定的准则,可以提高代码质量、可读性和可靠性。它们确保遵循行业标准,鼓励一致性,并促进开发人员之间的合作。通过遵循最佳实践,代码变得更容易理解、修改和调试ÿ…...

LangChain Agents深入剖析及源码解密上(一)
LangChain Agents深入剖析及源码解密上(一) LangChain Agents深入剖析及源码解密上 Agent工作原理详解 本节会结合AutoGPT的案例,讲解LangChain代理(Agent)为核心的内容。我们前面已经谈了代理本身的很多内容,也看了绝大部分的源代码,例如:ReAct的源代码,还有mrkl的源代…...

css定义超级链接a标签里面的title的样式
效果: 代码: 总结:此css 使用于任何元素,不仅仅是a标签!...

hcip——路由策略
要求: 基础配置 AR1 [R1]int g 0/0/0 [R1-GigabitEthernet0/0/0]ip add 12.0.0.1 24[R1-GigabitEthernet0/0/0]int g 0/0/1 [R1-GigabitEthernet0/0/1]ip add 14.0.0.1 24[R1]int loop0 [R1-LoopBack0]ip add 1.1.1.1 24[R1]rip 1 [R1-rip-1]vers 2 [R1-rip-1]net…...

ReID网络:MGN网络(1) - 概述
Start MGN 1. 序言 现代基于感知的信息中,视觉信息占了80~85%。基于视觉信息的处理和分析被应用到诸如安防、电力、汽车等领域。 以安防市场为例,早在2017年,行业咨询公司IHS Market,我国在公共和私人领域安装有摄像头约1.76亿…...

C++数据结构笔记(10)递归实现二叉树的三序遍历
对于三种遍历方式来说,均为先左后右!区别在于根结点的位置顺序 先序遍历:根——左——右 中序遍历:左——根——右 后序遍历:左——右——根 (所谓先中后的顺序,是指根结点D先于子树还是后于…...

hMailServer-5.3.3-B1879.exe
hMailServer-5.3.3-B1879.exe...

后端校验JSR303
目录 一、导入依赖 二、实现步骤 三、分组校验 四、自定义校验 一、导入依赖 <dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId><version>2.0.1.Final</version></dependency> 二…...

vmware磁盘组使用率100%处理
今天在外办事时,有客户发过来一个截图,问vmware 磁盘组空间使用率100%咋办?如下图: 直接回复: 1、首先删除iso文件等 2、若不存在ISO文件等,找个最不重要的虚拟机直接删除,删除后稍等就会释放…...

Redis实战(3)——缓存模型与缓存更新策略
1 什么是缓存? 缓存就是数据交换的缓冲区, 是存贮数据的临时区,一般读写性能较高 \textcolor{red}{是存贮数据的临时区,一般读写性能较高} 是存贮数据的临时区,一般读写性能较高。缓存可在多个场景下使用 以一次 w e b 请求为例…...

python与深度学习(十):CNN和cifar10二
目录 1. 说明2. cifar10的CNN模型测试2.1 导入相关库2.2 加载数据和模型2.3 设置保存图片的路径2.4 加载图片2.5 图片预处理2.6 对图片进行预测2.7 显示图片 3. 完整代码和显示结果4. 多张图片进行测试的完整代码以及结果 1. 说明 本篇文章是对上篇文章训练的模型进行测试。首…...

剑指offer12 矩阵中的路径 13 机器人的运动范围 34.二叉树中和为某一值得路径
class Solution { public:bool exist(vector<vector<char>>& board, string word) {int rowboard.size(),colboard[0].size();int index0,i0,j0;if(word.size()>row*col) return 0;//vector<vector<int>> visit[row][col];//标记当前位置有没有…...

Pushgateway+Prometheus监控Flink
思路方案 FlinkMtrics->pushgateway->prometheus->grafnana->altermanager 方案 : Flink任务先将数据推到pushgateway。然后pushgateway将值推送到prometheus,最后grafana展示prometheus中的值, 去这个 https://prometheus.io/download/ 下载最新的 Prometheu…...

OpenCV图像处理-视频分割静态背景-MOG/MOG2/GMG
视频分割背景 1.概念介绍2. 函数介绍MOG算法MOG2算法GMG算法 原视频获取链接 1.概念介绍 视频背景扣除原理:视频是一组连续的帧(一幅幅图组成),帧与帧之间关系密切(GOP/group of picture),在GOP中,背景几乎…...

nginx 反向代理浅谈
前言 通常情况下,客户端向Web服务器发送请求,Web服务器响应请求并返回数据。而在反向代理中,客户端的请求不直接发送到Web服务器,而是发送到反向代理服务器。反向代理服务器会将请求转发给真实的Web服务器,Web服务器响…...

【概率预测】对风力发电进行短期概率预测的分析研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

原型设计模式go实现尝试
文章目录 前言代码结果总结 前言 本文章尝试使用go实现“原型”。 代码 package mainimport ("fmt" )// 不同原型标志枚举 type Type intconst (PROTOTYPE_1 Type iotaPROTOTYPE_2 )// 原型接口 type IPrototype interface {Clone() IPrototypeMethod(value int)P…...

链表是否有环、环长度、环起点
问题引入 如何检测一个链表是否有环,如果有,那么如何确定环的长度及起点。 引自博客:上述问题是一个经典问题,经常会在面试中被问到。我之前在杭州一家网络公司的电话面试中就很不巧的问到,当时是第一次遇到那个问题&…...

有效文档管理离不开这几个特点
在我们日常生活中经常会遇到各式各样的文档类型,想要把它们都统一管理起来也不是一件容易的事情。后来looklook就去研究怎么样可以把这一堆文档整理起来呢?接下来,looklook就从有效的文档管理展开,和大家分享一下! 有效…...

爬虫-requests-cookie登录古诗文网
一、前言 1、requests简介 requests是一个很实用的Python HTTP客户端库,爬虫和测试服务器响应数据时经常会用到,它是python语言的第三方的库,专门用于发送HTTP请求,使用起来比urllib更简洁也更强大。 2、requests的安装 pip i…...

Spring Boot实践三 --数据库
一,使用JdbcTemplate访问MySQL数据库 1,确认本地已正确安装mysql 按【winr】快捷键打开运行;输入services.msc,点击【确定】;在打开的服务列表中查找mysql服务,如果没有mysql服务,说明本机没有…...

分布式锁漫谈
简单解释一下个人理解的分布式锁以及主要的实现手段。 文章目录 什么是分布式锁常用分布式锁实现 什么是分布式锁 以java应用举例,如果是单应用的情况下,我们通常使用synchronized或者lock进行线程锁,主要为了解决多线程或者高并发场景下的共…...

mac 安装 php 与 hyperf 框架依赖的扩展并启动 gptlink 项目
m系列 mac 安装 php 与 hyperf 框架依赖的扩展并启动 gptlink 项目 gptlink 项目是一个前后端一体化的 chatgpt 开源项目 gptlink 项目地址:https://github.com/gptlink/gptlink 安装 php 8.0 版本: brew install php8.0安装完成后提示如下ÿ…...

ansible中run_once的详细介绍和使用说明
在Ansible中,run_once是一个用于控制任务在主机组中只执行一次的关键字参数。当我们在编写Ansible任务时,有时候我们希望某个任务只在主机组中的某个主机上执行一次,而不是在每个主机上都执行。 以下是run_once参数的详细说明和用法…...

短视频矩阵系统源码开发流程
一、视频矩阵系统源码开发流程分为以下几个步骤: 四、技术开发说明: 产品原型PRD需求文档产品交互流程图部署方式说明完整源代码源码编译方式说明三方框架和SDK使用情况说明和代码位置平台操作文档程序架构文档 一、抖音SEO矩阵系统源码开发流程分为以…...

vite+vue3 css scss PC移动布局自适应
1. 安装 postcss-pxtorem 和 autoprefixer npm install postcss-pxtorem autoprefixer --save2. vite.config.js引入并配置 import postCssPxToRem from postcss-pxtorem import autoprefixer from autoprefixerexport default defineConfig({base: ./,resolve: {alias},plug…...

BLE配对和绑定
参考:一篇文章带你解读蓝牙配对绑定 参考:BLE安全之SM剖析(1) 参考:BLE安全之SM剖析(2) 参考:BLE安全之SM剖析(3) 目录 前言基本概念解读Paring(配对)Bonding(绑定)STK短期秘钥、LTK长期秘钥等 …...

无涯教程-jQuery - html( val )方法函数
html(val)方法设置每个匹配元素的html内容。此属性在XML文档上不可用。 html( val ) - 语法 selector.html( val ) 这是此方法使用的所有参数的描述- val - 这是要设置的html内容。 html( val ) - 示例 以下是一个简单的示例,简单说明了此方法的用法- <…...

【单链表OJ题:删除链表中等于给定值 val 的所有节点】
1.删除链表中等于给定值 val 的所有节点 题目来源 给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val val 的节点,并返回 新的头节点 。 /*** Definition for singly-linked list.* struct ListNode {* int val;* s…...

vue element ui web端引入百度地图,并获取经纬度
最近接到一个新需要,要求如下: 当我点击选择地址时,弹出百度地图, 效果如下图: 实现方法: 1、首先要在百度地图开放平台去申请一个账号和key 2、申请好之后,在项目的index.html中引入 3、…...