MongoDB数据库设计中6条重要的经验法则

MongoDB 模式设计进阶案例_页面_35

MongoDB数据库设计参考指标

  • MongoDB默认占用50%内存,可适当调节
  • Document文档最大16MB
    故设计的时候不要造成文档过大,尤其注意有数组的时候这个数组是不是可以无限push
  • Document中对数组的$elemMatch操作,其delete,push,pop性能巨差
    当文档中的数组大到一定程度,对数组中元素的delete,push,pop操作性能巨差
    尽量保证文档中数组的数量较少或者固定数组的数量
  • Document物理迁移,性能巨差

下面这些是你需要谨记的:

1、优先考虑内嵌,除非有什么迫不得已的原因。

2、需要单独访问一个对象,那这个对象就不适合被内嵌到其他对象中。

3、数组不应该无限制增长。如果many端有数百个文档对象就不要去内嵌他们可以采用引用ObjectID的方案;如果有数千个文档对象,那么就不要内嵌ObjectID的数组。该采取哪些方案取决于数组的大小。

4、不要害怕应用层级别的join:如果索引建的正确并且通过投影条件(第二章提及)限制返回的结果,那么应用层级别的join并不会比关系数据库中join开销大多少。

5、在进行反范式设计时请先确认读写比。一个几乎不更改只是读取的字段才适合冗余到其他对象中。

6、在mongodb中如何对你的数据建模,取决于你的应用程序如何去访问它们。数据的结构要去适应你的程序的读写场景。

设计指南

当你在MongoDB中对“一对多”关系进行建模,你有很多的方案可供选择,所以你必须很谨慎的去考虑数据的结构。下面这些问题是你必须认真思考的:

关系中集合的规模有多大:是一对很少,很多,还是非常多?

对于一对多中”多“的那一端,是否需要单独的访问它们,还是说它们只会在父对象的上下文中被访问。

被冗余的字段的读写的比例是多少?

数据建模设计指南

在一对很少的情况下,你可以在父文档中内嵌数组。

在一对很多或者需要单独访问“N”端的数据时,你可以采用数组引用ObjectID的方式。如果可以加速你的访问也可以在“N”端使用父引用。

在一对非常多的情况下,可以在“N”端使用父引用。

如果你打算在你的设计中引入冗余等反范式设计,那么你必须确保那些冗余的数据读取的频率远远大于更新的频率。而且你也不需要很强的一致性。因为反范式化的设计会让你在更新冗余字段时付出一定的代价(更慢,非原子化)

总结

三种基础方案:内嵌文档,子引用,父引用的补充选择。

使用双向引用来优化你的数据库架构,前提是你能接受无法原子更新的代价。

可以在引用关系中冗余数据到one端或者N端。

在决定是否采用反范式化时需要考虑下面的因素:

你将无法对冗余的数据进行原子更新。

只有读写比较高的情况下才应该采取反范式化的设计。

Linux安装nodejs环境以及路径配置

linux安装nodejs有2种方式一种简单的,解压即可用;另一种,通过下载source code ,通过编译,make,make install命令来安装。

这里只讲第一种,简单方便。不需要执行mak、make install。步骤如下:

一、确定你使用的linux系统,让问 https://nodejs.org/en/download/ 然后下载响应的压缩包。这里我下载的是node-vxx.xx.xx-linux-x64.tar.xz

wget    node-vxx.xx.xx-linux-x64.tar.xz

二、上传到linux相关路径下,一般是/usr/local/,并执行如下命令

xz -d node-xxxx.tar.xz —将tar.xz解压成tar文件

tar -xvf node-xxxx.tar —将tar文件解压成文件夹

mv node-xxx node —-改文件夹的名字,改成node

三、检查是否可以安装成功

./node -v

四、配置软连接,使全局都可以使用node命令

ln -s /usr/local/node/bin/node /usr/bin/node  –将node源文件映射到usr/bin下的node文件

ln -s /usr/local/node/bin/npm /usr/bin/npm

五、配置node文件安装路径

进入/usr/local/node/路径下:

mkdir node_global

mkdir node_cache

npm config set prefix “node_global”

npm config set cache “node_cache”

 

六、当你觉得npm慢的时候,可以安装cnpm

npm install cnpm -g –registry=https://registry.npm.taobao.org

顺便可以检查一下-g这个全局安装有没有按照之前设置的,安装到node_global文件下。

如下全局使用cnpm,也要记得配置一个软连接。

检查 /usr/local/node/node_global/lib/node_modules/cnpm 目录是否存在

 

 

 

Ubuntu中安装MySQL,更改默认用户密码

第一步:进入目录:cd /etc/mysql,查看debian.cnf文件

第二步:使用上图中的账号密码登录MySQL。

第三步:查看数据库中的库。

第四步:使用mysql库。

第五步:使用一下语句设置账号密码:update user set authentication_string=PASSWORD(“自定义新密码”) where user=’root’;

我个人设置的简单。账号为:root。 密码:123456.

第六步:执行一下语句:update user set plugin=”mysql_native_password”;

第七步:刷新:flush privileges;

第八步:退出:quit;

第九步:修改密码完成。在命令框中输入:/etc/init.d/mysql restart; 重启MySQL


最后 一步:再次登录。恭喜你更改默认密码成功。

来源:https://www.cnblogs.com/Tdazheng/p/10742929.html

高效 Dart 语言指南:代码风格

好的代码有一个非常重要的特点就是拥有好的风格。一致的命名、一致的顺序、以及一致的格式让代码看起来是一样的。这非常有利于发挥我们视力系统强大的模式匹配能力。如果我们整个 Dart 生态系统中都使用统一的风格,那么这将让我们彼此之间更容易的互相学习和互相贡献。它使我们所有人都可以更容易地学习并为彼此的代码做出贡献。

标识符

在 Dart 中标识符有三种类型。

  • UpperCamelCase 每个单词的首字母都大写,包含第一个单词。
  • lowerCamelCase 除了第一个字母始终是小写(即使是缩略词),每个单词的首字母都大写。
  • lowercase_with_underscores 只是用小写字母单词,即使是缩略词,并且单词之间使用 _ 连接。

 使用 UpperCamelCase 风格命名类型。

Linter rule: camel_case_types

Classes(类名)、 enums(枚举类型)、 typedefs(类型定义)、以及 type parameters(类型参数)应该把每个单词的首字母都大写(包含第一个单词),不使用分隔符。

class SliderMenu { ... }

class HttpRequest { ... }

typedef Predicate<T> = bool Function(T value);

这里包括使用元数据注解的类。

class Foo {
  const Foo([arg]);
}

@Foo(anArg)
class A { ... }

@Foo()
class B { ... }

如果注解类的构造函数是无参函数,则可以使用一个 lowerCamelCase 风格的常量来初始化这个注解。

const foo = Foo();

@foo
class C { ... }

 使用 UpperCamelCase 风格类型作为扩展名

Linter rule: camel_case_extensions

与类型命名一样,扩展名也应大写每个单词的首字母(包括第一个单词),并且不使用分隔符。

extension MyFancyList<T> on List<T> { ... }

extension SmartIterable<T> on Iterable<T> { ... }

 在文件夹源文件中使用 lowercase_with_underscores 方式命名。

Linter rules: library_names, file_names

一些文件系统不区分大小写,所以很多项目要求文件名必须是小写字母。使用分隔符这种形式可以保证命名的可读性。使用下划线作为分隔符可确保名称仍然是有效的Dart标识符,如果语言后续支持符号导入,这将会起到非常大的帮助。

library peg_parser.source_scanner;

import 'file_system.dart';
import 'slider_menu.dart';
library pegparser.SourceScanner;

import 'file-system.dart';
import 'SliderMenu.dart';

 用 lowercase_with_underscores 风格命名库和源文件名。

Linter rule: library_prefixes

import 'dart:math' as math;
import 'package:angular_components/angular_components'
    as angular_components;
import 'package:js/js.dart' as js;
import 'dart:math' as Math;
import 'package:angular_components/angular_components'
    as angularComponents;
import 'package:js/js.dart' as JS;

 使用 lowerCamelCase 风格来命名其他的标识符。

Linter rule: non_constant_identifier_names

类成员、顶级定义、变量、参数以及命名参数等 除了第一个单词,每个单词首字母都应大写,并且不使用分隔符。

var item;

HttpRequest httpRequest;

void align(bool clearItems) {
  // ...
}

推荐 使用 lowerCamelCase 来命名常量。

Linter rule: constant_identifier_names

在新的代码中,使用 lowerCamelCase 来命名常量,包括枚举的值。

const pi = 3.14;
const defaultTimeout = 1000;
final urlScheme = RegExp('^([a-z]+):');

class Dice {
  static final numberGenerator = Random();
}
const PI = 3.14;
const DefaultTimeout = 1000;
final URL_SCHEME = RegExp('^([a-z]+):');

class Dice {
  static final NUMBER_GENERATOR = Random();
}

您可以使用 SCREAMING_CAPS 与现有代码保持一致,比如:

  • 将代码添加到已使用 SCREAMING_CAPS 的文件或库时。
  • 生成与 Java 代码并行的 Dart 代码时。例如,来自 protobufs 的枚举类型

把超过两个字母的首字母大写缩略词和缩写词当做一般单词来对待。

首字母大写缩略词比较难阅读,特别是多个缩略词连载一起的时候会引起歧义。例如,一个以 HTTPSFTP 开头的名字,没有办法判断它是指 HTTPS FTP 还是 HTTP SFTP。

为了避免上面的情况,缩略词和缩写词要像普通单词一样首字母大写,两个字母的单词除外。(像 ID 和 Mr. 这样的双字母缩写词仍然像一般单词一样首字母大写。)

HttpConnectionInfo
uiHandler
IOStream
HttpRequest
Id
DBIOPort
TVVcr
HTTPConnection
UiHandler
IoStream
HTTPRequest
ID
DbIoPort
TvVcr

DON’T use a leading underscore for identifiers that aren’t private.

Dart uses a leading underscore in an identifier to mark members and top-level declarations as private. This trains users to associate a leading underscore with one of those kinds of declarations. They see “_” and think “private”.

There is no concept of “private” for local variables, parameters, or library prefixes. When one of those has a name that starts with an underscore, it sends a confusing signal to the reader. To avoid that, don’t use leading underscores in those names.

Exception: An unused parameter can be named ______, etc. This happens in things like callbacks where you are passed a value but you don’t need to use it. Giving it a name that consists solely of underscores is the idiomatic way to indicate the value isn’t used.

不要使用前缀字母

在编译器无法帮助你了解自己代码的时, 匈牙利命名法 和其他方案出现在了 BCPL ,但是因为 Dart 可以提示你声明的类型,范围,可变性和其他属性,所以没有理由在标识符名称中对这些属性进行编码。

defaultTimeout
kDefaultTimeout

顺序

为了使文件前面部分保持整洁,我们规定了关键字出现顺序的规则。每个“部分”应该使用空行分割。

A single linter rule handles all the ordering guidelines: directives_ordering.

 把 “dart:” 导入语句放到其他导入语句之前。

Linter rule: directives_ordering

import 'dart:async';
import 'dart:html';

import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

 把 “package:” 导入语句放到项目相关导入语句之前。

Linter rule: directives_ordering

import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

import 'util.dart';

 把导出(export)语句作为一个单独的部分放到所有导入语句之后。

Linter rule: directives_ordering

import 'src/error.dart';
import 'src/foo_bar.dart';

export 'src/error.dart';
import 'src/error.dart';
export 'src/error.dart';
import 'src/foo_bar.dart';

 按照字母顺序来排序每个部分中的语句。

Linter rule: directives_ordering

import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

import 'foo.dart';
import 'foo/foo.dart';
import 'package:foo/foo.dart';
import 'package:bar/bar.dart';

import 'foo/foo.dart';
import 'foo.dart';

格式化

和其他大部分语言一样, Dart 忽略空格。但是,不会。具有一致的空格风格有助于帮助我们能够用编译器相同的方式理解代码。

 使用 dartfmt 格式化你的代码。

格式化是一项繁琐的工作,尤其在重构过程中特别耗时。庆幸的是,你不必担心。我们提供了一个名为 dartfmt 的优秀的自动代码格式化程序,它可以为你完成格式化工作。我们有一些关于它适用的规则的 文档 , Dart 中任何官方的空格处理规则由 dartfmt 生成

其余格式指南用于 dartfmt 无法修复的一些规则。

考虑 修改你的代码让格式更友好。

无论你扔给格式化程序什么样代码,它都会尽力去处理,但是格式化程序不会创造奇迹。如果代码里有特别长的标识符,深层嵌套的表达式,混合的不同类型运算符等。格式化输出的代码可能任然很难阅读。

当有这样的情况发生时,那么就需要重新组织或简化你的代码。考虑缩短局部变量名或者将表达式抽取为一个新的局部变量。换句话说,你应该做一些手动格式化并增加代码的可读性的修改。在工作中应该把 dartfmt 看做一个合作伙伴,在代码的编写和迭代过程中互相协作输出优质的代码。

避免 单行超过 80 个字符。

Linter rule: lines_longer_than_80_chars

可读性研究表明,长行的文字不易阅读,长行文字移动到下一行的开头时,眼睛需要移动更长的距离。这也是为什么报纸和杂志会使用多列样式的文字排版。

如果你真的发现你需要的文字长度超过了 80 个字符,根据我们的经验,你的代码很可能过于冗长,而且有方式可以让它更紧凑。最常见的的一种情况就是使用 VeryLongCamelCaseClassNames (非常长的类名字和变量名字)。当遇到这种情况时,请自问一下:“那个类型名称中的每个单词都会告诉我一些关键的内容或阻止名称冲突吗?”,如果不是,考虑删除它。

注意,dartfmt 能自动处理 99% 的情况,但是剩下的 1% 需要你自己处理。 dartfmt 不会把很长的字符串字面量分割为 80 个字符的列,所以这种情况你需要自己手工确保每行不超过 80 个字符。

例外: 当情况出现在注释或字符串是(通常在导入和导出语句中),即使文字超出行限制,也可能会保留在一行中。这样可以更轻松地搜索给定路径的源文件。

Exception: Multi-line strings can contain lines longer than 80 characters because newlines are significant inside the string and splitting the lines into shorter ones can alter the program.

 对所有流控制结构使用花括号。

Linter rule: curly_braces_in_flow_control_structures

这样可以避免 dangling else(else悬挂)的问题。

if (isWeekDay) {
  print('Bike to work!');
} else {
  print('Go dancing or read a book!');
}

这里有一个例外:一个没有 else 的 if 语句,并且这个 if 语句以及它的执行体适合在一行中实现。在这种情况下,如果您愿意,可以不用括号:

if (arg == null) return defaultValue;

但是,如果执行体包含下一行,请使用大括号:

if (overflowChars != other.overflowChars) {
  return overflowChars < other.overflowChars;
}
if (overflowChars != other.overflowChars)
  return overflowChars < other.overflowChars;

高效 Dart 语言指南:用法示例

每天在你写的 Dart 代码中都会应用到这些准则。库的使用者可能不需要知道你在其中的一些想法,但是维护者肯定是需要的。

这些准则可以帮助你在多个文件编写程序的情况下保证一致性和可维护性。为了让准则简洁,这里使用“import”来同事代表 import 和 export 。准则同时适用于这两者。

 在 part of 中使用字符串。

很多 Dart 开发者会避免直接使用 part 。他们发现当库仅有一个文件的时候很容易读懂代码。如果你确实要使用 part 将库的一部分拆分为另一个文件,则 Dart 要求另一个文件指示它所属库的路径。由于遗留原因, Dart 允许 part of 指令使用它所属的库的名称。这使得工具很难直接查找到这个文件对应主库文件,使得库和文件之间的关系模糊不清。

推荐的现代语法是使用 URI 字符串直接指向库文件。首选的现代语法是使用直接指向库文件的URI字符串,URI 的使用和其他指令中一样。如果你有一些库,my_library.dart,其中包含:

library my_library;

part "some/other/file.dart";

从库中拆分的文件应该如下所示:

part of "../../my_library.dart";

而不是:

part of my_library;

不要 导入 package 中 src 目录下的库。

Linter rule: implementation_imports

lib 下的 src 目录 被指定 为 package 自己实现的私有库。基于包维护者对版本的考虑,package 使用了这种约定。在不破坏 package 的情况下,维护者可以自由地对 src 目录下的代码进行修改。

这意味着,你如果导入了其中的私有库,按理论来讲,一个不破坏 package 的次版本就会影响到你的代码。

 使用相对路径在导入你自己 package 中的 lib 目录。

Linter rules: avoid_relative_lib_imports, prefer_relative_imports

在同一个 package 下其中一个库引用另一个 lib 目录下的库时,应该使用相对的 URI 或者直接使用 package:

比如,下面是你的 package 目录结构:

my_package
└─ lib
   ├─ src
   │  └─ utils.dart
   └─ api.dart

如果 api.dart 想导入 utils.dart ,应该这样使用:

import 'src/utils.dart';

而不是:

import 'package:my_package/src/utils.dart';

“让 package 的 lib 目录”独立分离非常重要。 lib 中的库可以导入 lib(或其子目录)中的其他库。 lib 之外的库可以使用相对导入的方式来访问lib之外的其他库。例如,test 下可能有一个测试实用程序库被其它在 test 下的库导入。

But you can’t “cross the streams”. A library outside of lib should never use a relative import to reach a library under lib, or vice versa. Doing so will break Dart’s ability to correctly tell if two library URIs refer to the same library, which can lead to unexpected duplicated types.

但不能跨越导入。一个在 lib 外部的库应该永远不会引用一个在 lib 内部的库,反之亦然。这样做,会破坏 Dart 正确判断两个库的 URL 是否引用了同一个库的能力。遵循以下两条规则:

  • 导入路径不应包含 /lib/ 。
  • lib 下的库永远不应该使用 ../ 来跨越 lib 目录。

Booleans

DO use ?? to convert null to a boolean value.

This rule applies when an expression can evaluate truefalse, or null, and you need to pass the result to something that doesn’t accept null. A common case is the result of a null-aware method call being used as a condition:

if (optionalThing?.isEnabled) {
  print("Have enabled thing.");
}

This code throws an exception if optionalThing is null. To fix this, you need to “convert” the null value to either true or false. Although you could do this using ==, we recommend using ??:

// If you want null to be false:
optionalThing?.isEnabled ?? false;

// If you want null to be true:
optionalThing?.isEnabled ?? true;
// If you want null to be false:
optionalThing?.isEnabled == true;

// If you want null to be true:
optionalThing?.isEnabled != false;

Both operations produce the same result and do the right thing, but ?? is preferred for three main reasons:

  • The ?? operator clearly signals that the code has something to do with a null value.
  • The == true looks like a common new programmer mistake where the equality operator is redundant and can be removed. That’s true when the boolean expression on the left will not produce null, but not when it can.
  • The ?? false and ?? true clearly show what value will be used when the expression is null. With == true, you have to think through the boolean logic to realize that means that a null gets converted to false.

字符串

下面是一些需要记住的,关于在 Dart 中使用字符串的最佳实践。

 使用临近字符字的方式连接字面量字符串。

Linter rule: prefer_adjacent_string_concatenation

如果你有两个字面量字符串(不是变量,是放在引号中的字符串),你不需要使用 + 来连接它们。应该想 C 和 C++ 一样,只需要将它们挨着在一起就可以了。这种方式非常适合不能放到一行的长字符串的创建。

raiseAlarm(
    'ERROR: Parts of the spaceship are on fire. Other '
    'parts are overrun by martians. Unclear which are which.');
raiseAlarm('ERROR: Parts of the spaceship are on fire. Other ' +
    'parts are overrun by martians. Unclear which are which.');

推荐 使用插值的形式来组合字符串和值。

Linter rule: prefer_interpolation_to_compose_strings

如果你之前使用过其他语言,你一定习惯使用大量 + 将字面量字符串以及字符串变量链接构建字符串。这种方式在 Dart 中同样有效,但是通常情况下使用插值会更清晰简短:

'Hello, $name! You are ${year - birth} years old.';
'Hello, ' + name + '! You are ' + (year - birth).toString() + ' y...';

避免 在字符串插值中使用不必要的大括号。

Linter rule: unnecessary_brace_in_string_interps

如果要插入是一个简单的标识符,并且后面没有紧跟随在其他字母文本,则应省略 {} 。

'Hi, $name!'
    "Wear your wildest $decade's outfit."
    'Wear your wildest ${decade}s outfit.'
'Hi, ${name}!'
    "Wear your wildest ${decade}'s outfit."

集合

Dart 集合中原生支持了四种类型:list, map, queue,和 set。下面是应用于集合的最佳实践。

 尽可能的使用集合字面量。

Linter rule: prefer_collection_literals

Dart 有三种核心集合类型。List、Map 和 Set,这些类和大多数类一样,都有未命名的构造函数,但由于这些集合使用频率很高,Dart 有更好的内置语法来创建它们:

var points = <Point>[];
var addresses = <String, Address>{};
var counts = <int>{};
var points = List<Point>();
var addresses = Map<String, Address>();
var counts = Set<int>();

注意,对于集合类的 命名 构造函数则不适用上面的规则。 List.from()、 Map.fromIterable() 都有其使用场景。如果需要一个固定长度的结合,使用 List() 来创建一个固定长度的 list 也是合理的。

不要 使用 .length 来判断一个集合是否为空。

Linter rules: prefer_is_empty, prefer_is_not_empty

Iterable 合约并不要求集合知道其长度,也没要求在遍历的时候其长度不能改变。通过调用 .length 来判断集合是否包含内容是非常低效的。

相反,Dart 提供了更加高效率和易用的 getter 函数:.isEmpty 和.isNotEmpty。使用这些函数并不需要对结果再次取非。

if (lunchBox.isEmpty) return 'so hungry...';
if (words.isNotEmpty) return words.join(' ');
if (lunchBox.length == 0) return 'so hungry...';
if (!words.isEmpty) return words.join(' ');

考虑 使用高阶(higher-order)函数来转换集合数据。

如果你有一个集合并且想要修改里面的内容转换为另外一个集合,使用 .map()、 .where() 以及 Iterable 提供的其他函数会让代码更加简洁。

使用这些函数替代 for 循环会让代码更加可以表述你的意图,生成一个新的集合系列并不具有副作用。

var aquaticNames = animals
    .where((animal) => animal.isAquatic)
    .map((animal) => animal.name);

与此同时,这可以非常长,如果你串联或者嵌套调用很多高阶函数,则使用一些命令式代码可能会更加清晰。

避免 在 Iterable.forEach() 中使用字面量函数。

Linter rule: avoid_function_literals_in_foreach_calls

forEach() 函数在 JavaScript 中被广泛使用,这因为内置的 for-in 循环通常不能达到你想要的效果。在Dart中,如果要对序列进行迭代,惯用的方式是使用循环。

for (var person in people) {
  ...
}
people.forEach((person) {
  ...
});

例外情况是,如果要执行的操作是调用一些已存在的并且将每个元素作为参数的函数,在这种情况下,forEach() 是很方便的。

people.forEach(print);

对于 Map.forEach() 调用时可以的。Map 是不可迭代的。所它们不包括在该准则内。

不要 使用 List.from() 除非想修改结果的类型。

给定一个可迭代的对象,有两种常见方式来生成一个包含相同元素的 list:

var copy1 = iterable.toList();
var copy2 = List.from(iterable);

明显的区别是前一个更短。更重要的区别在于第一个保留了原始对象的类型参数:

// Creates a List<int>:
var iterable = [1, 2, 3];

// Prints "List<int>":
print(iterable.toList().runtimeType);
// Creates a List<int>:
var iterable = [1, 2, 3];

// Prints "List<dynamic>":
print(List.from(iterable).runtimeType);

如果你想要改变类型,那么可以调用 List.from() :

var numbers = [1, 2.3, 4]; // List<num>.
numbers.removeAt(1); // Now it only contains integers.
var ints = List<int>.from(numbers);

但是如果你的目的只是复制可迭代对象并且保留元素原始类型,或者并不在乎类型,那么请使用 toList()

 使用 whereType() 按类型过滤集合。

Linter rule: prefer_iterable_whereType

假设你有一个 list 里面包含了多种类型的对象,但是你指向从它里面获取整型类型的数据。那么你可以像下面这样使用 where() :

var objects = [1, "a", 2, "b", 3];
var ints = objects.where((e) => e is int);

这个很罗嗦,但是更糟糕的是,它返回的可迭代对象类型可能并不是你想要的。在上面的例子中,虽然你想得到一个 Iterable<int>,然而它返回了一个 Iterable<Object>,这是因为,这是你过滤后得到的类型。

有时候你会看到通过添加 cast() 来“修正”上面的错误:

var objects = [1, "a", 2, "b", 3];
var ints = objects.where((e) => e is int).cast<int>();

代码冗长,并导致创建了两个包装器,获取元素对象要间接通过两层,并进行两次多余的运行时检查。幸运的是,对于这个用例,核心库提供了 whereType() 方法:

var objects = [1, "a", 2, "b", 3];
var ints = objects.whereType<int>();

使用 whereType() 简洁,生成所需的 Iterable(可迭代)类型,并且没有不必要的层级包装。

不要 使用 cast(),如果有更合适的方法。

通常,当处理可迭代对象或 stream 时,你可以对其执行多次转换。最后,生成所希望的具有特定类型参数的对象。尝试查看是否有已有的转换方法来改变类型,而不是去掉用 cast() 。而不是调用 cast(),看看是否有一个现有的转换可以改变类型。

如果你已经使用了 toList() ,那么请使用 List<T>.from() 替换,这里的 T 是你想要的返回值的类型。

var stuff = <dynamic>[1, 2];
var ints = List<int>.from(stuff);
var stuff = <dynamic>[1, 2];
var ints = stuff.toList().cast<int>();

如果你正在调用 map() ,给它一个显式的类型参数,这样它就能产生一个所需类型的可迭代对象。类型推断通常根据传递给 map() 的函数选择出正确的类型,但有的时候需要明确指明。

var stuff = <dynamic>[1, 2];
var reciprocals = stuff.map<double>((n) => 1 / n);
var stuff = <dynamic>[1, 2];
var reciprocals = stuff.map((n) => 1 / n).cast<double>();

避免 使用 cast() 。

这是对先前规则的一个宽松的定义。有些时候,并没有合适的方式来修改对象类型,即便如此,也应该尽可能的避免使用 cast() 来“改变”集合中元素的类型。

推荐使用下面的方式来替代:

  • 用恰当的类型创建集合。 修改集合被首次创建时的代码,为集合提供有一个恰当的类型。
  • 在访问元素时进行 cast 操作。 如果要立即对集合进行迭代,在迭代内部 cast 每个元素。
  • 逼不得已进行 cast,请使用 List.from() 。 如果最终你会使用到集合中的大部分元素,并且不需要对象还原到原始的对象类型,使用 List.from() 来转换它。

    cast() 方法返回一个惰性集合(lazy collection),每个操作都会对元素进行检查。如果只对少数元素执行少量操作,那么这种惰性方式就非常合适。但在许多情况下,惰性验证和包裹(wrapping)所产生的开销已经超过了它们所带来的好处。

下面是 用恰当的类型创建集合 的示例:

List<int> singletonList(int value) {
  var list = <int>[];
  list.add(value);
  return list;
}
List<int> singletonList(int value) {
  var list = []; // List<dynamic>.
  list.add(value);
  return list.cast<int>();
}

下面是 在访问元素时进行 cast 操作 的示例:

void printEvens(List<Object> objects) {
  // We happen to know the list only contains ints.
  for (var n in objects) {
    if ((n as int).isEven) print(n);
  }
}
void printEvens(List<Object> objects) {
  // We happen to know the list only contains ints.
  for (var n in objects.cast<int>()) {
    if (n.isEven) print(n);
  }
}

下面是 使用 List.from() 进行 cast 操作 的示例:

int median(List<Object> objects) {
  // We happen to know the list only contains ints.
  var ints = List<int>.from(objects);
  ints.sort();
  return ints[ints.length ~/ 2];
}
int median(List<Object> objects) {
  // We happen to know the list only contains ints.
  var ints = objects.cast<int>();
  ints.sort();
  return ints[ints.length ~/ 2];
}

当然,这些替代方案并不总能解决问题,显然,这时候就应该选择 cast() 方式了。但是考虑到这种方式的风险和缺点——如果使用不当,可能会导致执行缓慢和运行失败。

函数

在 Dart 中,就连函数也是对象。以下是一些涉及函数的最佳实践。

 使用函数声明的方式为函数绑定名称。

Linter rule: prefer_function_declarations_over_variables

现代语言已经意识到本地嵌套函数和闭包的益处。在一个函数中定义另一个函数非常常见。在许多情况下,这些函数被立即执行并返回结果,而且不需要名字。这种情况下非常适合使用函数表达式来实现。

但是,如果你确实需要给方法一个名字,请使用方法定义而不是把 lambda 赋值给一个变量。

void main() {
  localFunction() {
    ...
  }
}
void main() {
  var localFunction = () {
    ...
  };
}

不要 使用 lambda 表达式来替代 tear-off。

Linter rule: unnecessary_lambdas

如果你在一个对象上调用函数并省略了括号, Dart 称之为”tear-off”—一个和函数使用同样参数的闭包,当你调用闭包的时候执行其中的函数。

如果你有一个方法,这个方法调用了参数相同的另一个方法。那么,你不需要人为将这个方法包装到一个 lambda 表达式中。

names.forEach(print);
names.forEach((name) {
  print(name);
});

参数

 使用 = 来分隔参数名和参数默认值。

Linter rule: prefer_equal_for_default_values

由于遗留原因,Dart 同时支持 : 和 = 作为参数名和默认值的分隔符。为了与可选的位置参数保持一致,请使用 = 。

void insert(Object item, {int at = 0}) { ... }
void insert(Object item, {int at: 0}) { ... }

不要 显式的为参数设置 null 值。

Linter rule: avoid_init_to_null

如果你创建了一个可选参数,那么就不要为其赋默认值, Dart 默认使用 null 作为默认值,所以这里不需要为其 null 赋值语句。

void error([String message]) {
  stderr.write(message ?? '\n');
}
void error([String message = null]) {
  stderr.write(message ?? '\n');
}

变量

下面是关于如何在 Dart 中使用变量的的最佳实践。

不要 显示的为参数初始化 null 值。

Linter rule: avoid_init_to_null

在Dart中,未自动显式初始化的变量或字段将初始化为 null 。语言保证了赋值的可靠性。在 Dart 中没有“未初始化内存”的概念。所以使用 = null 是多余的。

int _nextId;

class LazyId {
  int _id;

  int get id {
    if (_nextId == null) _nextId = 0;
    if (_id == null) _id = _nextId++;

    return _id;
  }
}
int _nextId = null;

class LazyId {
  int _id = null;

  int get id {
    if (_nextId == null) _nextId = 0;
    if (_id == null) _id = _nextId++;

    return _id;
  }
}

避免 保存可计算的结果。

在设计类的时候,你常常希望暴露底层状态的多个表现属性。常常你会发现在类的构造函数中计算这些属性,然后保存起来:

class Circle {
  double radius;
  double area;
  double circumference;

  Circle(double radius)
      : radius = radius,
        area = pi * radius * radius,
        circumference = pi * 2.0 * radius;
}

上面的代码有两个不妥之处。首先,这样浪费了内存。严格来说面积和周长是缓存数据。他们保存的结果可以通过已知的数据计算出来。他们减少了 CPU 消耗却增加了内存消耗。我们还没有权衡,到底存不存在性能问题?

更糟糕的是,代码是错误的。问题在于缓存是无效的 —— 你如何知道缓存何时会过期并且需要重新计算?即便半径是可变的,在这里我们也永远不会这样做。你可以赋一个不同的值,但面积和周长还是以前的值,现在的值是不正确的。

为了正确处理缓存失效,我们需要这样做:

class Circle {
  double _radius;
  double get radius => _radius;
  set radius(double value) {
    _radius = value;
    _recalculate();
  }

  double _area;
  double get area => _area;

  double _circumference;
  double get circumference => _circumference;

  Circle(this._radius) {
    _recalculate();
  }

  void _recalculate() {
    _area = pi * _radius * _radius;
    _circumference = pi * 2.0 * _radius;
  }
}

这需要编写、维护、调试以及阅读更多的代码。如果你一开始这样写代码:

class Circle {
  double radius;

  Circle(this.radius);

  double get area => pi * radius * radius;
  double get circumference => pi * 2.0 * radius;
}

上面的代码更加简洁、使用更少的内存、减少出错的可能性。它尽可能少的保存了表示圆所需要的数据。这里没有字段需要同步,因为这里只有一个有效数据源。

在某些情况下,当计算结果比较费时的时候可能需要缓存,但是只应该在你只有你有这样的性能问题的时候再去处理,处理时要仔细,并留下挂关于优化的注释。

成员

在 Dart 中,对象成员可以是函数(方法)或数据(实例变量)。下面是关于对象成员的最佳实践。

不要 为字段创建不必要的 getter 和 setter 方法。

Linter rule: unnecessary_getters_setters

在 Java 和 C# 中,通常情况下会将所有的字段隐藏到 getter 和 setter 方法中(在 C# 中被称为属性),即使实现中仅仅是指向这些字段。在这种方式下,即使你在这些成员上做多少的事情,你也不需要直接访问它们。这是因为,在 Java 中,调用 getter 方法和直接访问字段是不同的。在 C# 中,访问属性与访问字段不是二进制兼容的。

Dart 不存在这个限制。字段和 getter/setter 是完全无法区分的。你可以在类中公开一个字段,然后将其包装在 getter 和 setter 中,而不会影响任何使用该字段的代码。

class Box {
  var contents;
}
class Box {
  var _contents;
  get contents => _contents;
  set contents(value) {
    _contents = value;
  }
}

推荐 使用 final 关键字来创建只读属性。

Linter rule: unnecessary_getters

如果你有一个变量,对于外部代买来说只能读取不能修改,最简单的做法就是使用 final 关键字来标记这个变量。

class Box {
  final contents = [];
}
class Box {
  var _contents;
  get contents => _contents;
}

当然,如果你需要构造一个内部可以赋值,外部可以访问的字段,你可以需要这种“私有成员变量,公开访问函数”的模式,但是,如非必要,请不要使用这种模式。

考虑 对简单成员使用 => 。

Linter rule: prefer_expression_function_bodies

除了使用 => 可以用作函数表达式以外, Dart 还允许使用它来定义成员。这种风格非常适合,仅进行计算并返回结果的简单成员。

double get area => (right - left) * (bottom - top);

bool isReady(double time) =>
    minTime == null || minTime <= time;

String capitalize(String name) =>
    '${name[0].toUpperCase()}${name.substring(1)}';

编写代码的人似乎很喜欢 => 语法,但是它很容易被滥用,最后导致代码不容易被阅读。如果你有很多行声明或包含深层的嵌套表达式(级联和条件运算符就是常见的罪魁祸首),你以及其他人有谁会愿意读这样的代码!你应该换做使用代码块和一些语句来实现。

Treasure openChest(Chest chest, Point where) {
  if (_opened.containsKey(chest)) return null;

  var treasure = Treasure(where);
  treasure.addAll(chest.contents);
  _opened[chest] = treasure;
  return treasure;
}
Treasure openChest(Chest chest, Point where) =>
    _opened.containsKey(chest) ? null : _opened[chest] = Treasure(where)
      ..addAll(chest.contents);

您还可以对不返回值的成员使用 => 。这里有个惯例,就是当 setter 和 getter 都比较简单的时候使用 => 。

num get x => center.x;
set x(num value) => center = Point(value, center.y);

不要 使用 this. ,在重定向命名函数和避免冲突的情况下除外。

Linter rule: unnecessary_this

JavaScript 需要使用 this. 来引用对象的成员变量,但是 Dart—和 C++, Java, 以及C#—没有这种限制。

只有当局部变量和成员变量名字一样的时候,你才需要使用 this. 来访问成员变量。只有两种情况需要使用 this. 。其中一种情况是要访问的局部变量和成员变量命名一样的时候:

class Box {
  var value;

  void clear() {
    this.update(null);
  }

  void update(value) {
    this.value = value;
  }
}
class Box {
  var value;

  void clear() {
    update(null);
  }

  void update(value) {
    this.value = value;
  }
}

另一种使用 this. 的情况是在重定向到一个命名函数的时候:

class ShadeOfGray {
  final int brightness;

  ShadeOfGray(int val) : brightness = val;

  ShadeOfGray.black() : this(0);

  // This won't parse or compile!
  // ShadeOfGray.alsoBlack() : black();
}
class ShadeOfGray {
  final int brightness;

  ShadeOfGray(int val) : brightness = val;

  ShadeOfGray.black() : this(0);

  // But now it will!
  ShadeOfGray.alsoBlack() : this.black();
}

注意,构造函数初始化列表中的字段有永远不会与构造函数参数列表参数产生冲突。

class Box extends BaseBox {
  var value;

  Box(value)
      : value = value,
        super(value);
}

这看起来很令人惊讶,但是实际结果是你想要的。幸运的是,由于初始化规则的特殊性,上面的代码很少见到。

 尽可能的在定义变量的时候初始化变量值。

如果一个字段不依赖于构造函数中的参数,则应该在定义的时候就初始化字段值。这样可以减少需要的代码并可以确保在有多个构造函数的时候你不会忘记初始化该字段。

class Folder {
  final String name;
  final List<Document> contents;

  Folder(this.name) : contents = [];
  Folder.temp() : name = 'temporary'; // Oops! Forgot contents.
}
class Folder {
  final String name;
  final List<Document> contents = [];

  Folder(this.name);
  Folder.temp() : name = 'temporary';
}

当然,对于变量取值依赖构造函数参数的情况以及不同的构造函数取值也不一样的情况,则不适合本条规则。

构造函数

下面对于类的构造函数的最佳实践。

 尽可能的使用初始化形式。

Linter rule: prefer_initializing_formals

许多字段直接使用构造函数参数来初始化,如:

class Point {
  double x, y;
  Point(double x, double y) {
    this.x = x;
    this.y = y;
  }
}

为了初始化一个字段,我们需要取_四_次 x 。使用下面的方式会更好:

class Point {
  double x, y;
  Point(this.x, this.y);
}

这里的位于构造函数参数之前的 this. 语法被称之为初始化形式(initializing formal)。有些情况下这无法使用这种形式。特别是,这种形式下在初始化列表中无法看到变量。但是如果能使用该方式,就应该尽量使用。

不要 在初始化形式中做类型注释。

Linter rule: type_init_formals

如果构造函数参数使用 this. 的方式来初始化字段,这时参数的类型被认为和字段类型相同。

class Point {
  double x, y;
  Point(this.x, this.y);
}
class Point {
  int x, y;
  Point(int this.x, int this.y);
}

 用 ; 来替代空的构造函数体 {}

Linter rule: empty_constructor_bodies

在 Dart 中,没有具体函数体的构造函数可以使用分号结尾。(事实上,这是不可变构造函数的要求。)

class Point {
  double x, y;
  Point(this.x, this.y);
}
class Point {
  int x, y;
  Point(this.x, this.y) {}
}

不要 使用 new 。

Linter rule: unnecessary_new

Dart 2 new 关键字成为可选项。即使在Dart 1中,其含义也从未明确过,以为在工厂构造函数中,调用 new 可能并不意味着一定会返回一个新对象。

为了减少代码迁移时的痛苦, Dart 语言仍允许使用 new 关键字,但请考在你的代码中弃用和删除 new

Widget build(BuildContext context) {
  return Row(
    children: [
      RaisedButton(
        child: Text('Increment'),
      ),
      Text('Click!'),
    ],
  );
}
Widget build(BuildContext context) {
  return new Row(
    children: [
      new RaisedButton(
        child: new Text('Increment'),
      ),
      new Text('Click!'),
    ],
  );
}

不要 冗余地使用 const 。

Linter rule: unnecessary_const

在表达式一定是常量的上下文中,const 关键字是隐式的,不需要写,也不应该。这里包括:

  • 一个字面量常量集合。
  • 调用一个常量构造函数。
  • 元数据注解。
  • 一个常量声明的初始化方法。
  • switch case 表达式—— case 和 : 中间的部分,不是 case 执行体。

(默认值并不包含在这个列表中,因为在 Dart 将来的版本中可能会在支持非常量的默认值。)

基本上,任何地方用 new 替代 const 的写法都是错的,因为 Dart 2 中允许省略 const 。

const primaryColors = [
  Color("red", [255, 0, 0]),
  Color("green", [0, 255, 0]),
  Color("blue", [0, 0, 255]),
];
const primaryColors = const [
  const Color("red", const [255, 0, 0]),
  const Color("green", const [0, 255, 0]),
  const Color("blue", const [0, 0, 255]),
];

错误处理

Dart 使用异常来表���程序执行错误。下面是关于如何捕获和抛出异常的最佳实践。

避免 使用没有 on 语句的 catch。

Linter rule: avoid_catches_without_on_clauses

没有 on 限定的 catch 语句会捕获 try 代码块中抛出的任何异常。 Pokémon exception handling 可能并不是你想要的。你的代码是否正确的处理 StackOverflowError 或者 OutOfMemoryError 异常?如果你使用错误的参数调用函数,你是期望调试器定位出你的错误使用情况还是,把这个有用的 ArgumentError 给吞噬了?由于你捕获了 AssertionError 异常,导致所有 try 块内的 assert() 语句都失效了,这是你需要的结果吗?

答案和可能是 “no”,在这种情况下,您应该过滤掉捕获的类型。在大多数情况下,您应该有一个 on 子句,这样它能够捕获程序在运行时你所关注的限定类型的异常并进行恰当处理。

In rare cases, you may wish to catch any runtime error. This is usually in framework or low-level code that tries to insulate arbitrary application code from causing problems. Even here, it is usually better to catch Exception than to catch all types. Exception is the base class for all runtime errors and excludes errors that indicate programmatic bugs in the code.

不要 丢弃没有使用 on 语句捕获的异常。

如果你真的期望捕获一段代码内的 所有 异常,请在捕获异常的地方做些事情。记录下来并显示给用户,或者重新抛出(rethrow)异常信息,记得不要默默的丢弃该异常信息。

 只在代表编程错误的情况下才抛出实现了 Error 的异常。

Error 类是所有 编码 错误的基类。当一个该类型或者其子类型,例如 ArgumentError 对象被抛出了,这意味着是你代码中的一个 bug。当你的 API 想要告诉调用者使用错误的时候可以抛出一个 Error 来表明你的意图。

同样的,如果一个异常表示为运行时异常而不是代码 bug,则抛出 Error 则会误导调用者。应该抛出核心定义的 Exception 类或者其他类型。

不要 显示的捕获 Error 或者其子类。

Linter rule: avoid_catching_errors

本条衔接上一条的内容。既然 Error 表示代码中的 bug,应该展开整个调用堆栈,暂停程序并打印堆栈跟踪,以便找到错误并修复。

捕获这类错误打破了处理流程并且代码中有 bug。不要在这里使用错误处理代码,而是需要到导致该错误出现的地方修复你的代码。

 使用 rethrow 来重新抛出捕获的异常。

Linter rule: use_rethrow_when_possible

如果你想重新抛出一个异常,推荐使用 rethrow 语句。 rethrow 保留了原来的异常堆栈信息。而 throw 会把异常堆栈信息重置为最后抛出的位置。

try {
  somethingRisky();
} catch (e) {
  if (!canHandle(e)) throw e;
  handle(e);
}
try {
  somethingRisky();
} catch (e) {
  if (!canHandle(e)) rethrow;
  handle(e);
}

异步

Dart 具有几个语言特性来支持异步编程。下面是针对异步编程的最佳实践。

推荐 使用 async/await 而不是直接使用底层的特性。

显式的异步代码是非常难以阅读和调试的,即使使用很好的抽象(比如 future)也是如此。这就是为何 Dart 提供了 async/await。这样可以显著的提高代码的可读性并且让你可以在异步代码中使用语言提供的所有流程控制语句。

Future<int> countActivePlayers(String teamName) async {
  try {
    var team = await downloadTeam(teamName);
    if (team == null) return 0;

    var players = await team.roster;
    return players.where((player) => player.isActive).length;
  } catch (e) {
    log.error(e);
    return 0;
  }
}
Future<int> countActivePlayers(String teamName) {
  return downloadTeam(teamName).then((team) {
    if (team == null) return Future.value(0);

    return team.roster.then((players) {
      return players.where((player) => player.isActive).length;
    });
  }).catchError((e) {
    log.error(e);
    return 0;
  });
}

不要 在没有有用效果的情况下使用 async 。

当成为习惯之后,你可能会在所有和异步相关的函数使用 async。但是在有些情况下,如果可以忽略 async 而不改变方法的行为,则应该这么做:

Future<void> afterTwoThings(
    Future<void> first, Future<void> second) {
  return Future.wait([first, second]);
}
Future<void> afterTwoThings(Future<void> first, Future<void> second) async {
  return Future.wait([first, second]);
}

下面这些情况 async 是有用的:

  • 你使用了 await。 (这是一个很明显的例子。)
  • 你在异步的抛出一个异常。 async 然后 throw 比 return new Future.error(...) 要简短很多。
  • 你在返回一个值,但是你希望他显式的使用 Future。async 比 Future.value(...) 要简短很多。
Future<void> usesAwait(Future<String> later) async {
  print(await later);
}

Future<void> asyncError() async {
  throw 'Error!';
}

Future<void> asyncValue() async => 'value';

考虑 使用高阶函数来转换事件流(stream)。

This parallels the above suggestion on iterables. Streams support many of the same methods and also handle things like transmitting errors, closing, etc. correctly.

避免 直接使用 Completer。

很多异步编程的新手想要编写生成一个 future 的代码。而 Future 的构造函数看起来并不满足他们的要求,然后他们就发现 Completer 类并使用它:

Future<bool> fileContainsBear(String path) {
  var completer = Completer<bool>();

  File(path).readAsString().then((contents) {
    completer.complete(contents.contains('bear'));
  });

  return completer.future;
}

Completer 是用于两种底层代码的:新的异步原子操作和集成没有使用 Future 的异步代码。大部分的代码都应该使用 async/await 或者 Future.then(),这样代码更加清晰并且异常处理更加容易。

Future<bool> fileContainsBear(String path) {
  return File(path).readAsString().then((contents) {
    return contents.contains('bear');
  });
}
Future<bool> fileContainsBear(String path) async {
  var contents = await File(path).readAsString();
  return contents.contains('bear');
}

 使用 Future<T> 对 FutureOr<T> 参数进行测试,以消除参数可能是 Object 类型的歧义。

在使用 FutureOr<T> 执行任何有用的操作之前,通常需要做 is 检查,来确定你拥有的是 Future<T> 还是一个空的 T。如果类型参数是某个特定类型,如 FutureOr <int>,使用 is int 或 is Future<int> 那种测试都可以。两者都有效,因为这两种类型是不相交的。

但是,如果值的类型是 Object 或者可能使用 Object 实例化的类型参数,这时要分两种情况。 Future<Object> 本身继承 Object ,使用 is Object 或 is T ,其中 T 表示参数的类型,该参数可能是 Object 的实例,在这种情况下,即使是 future 对象也会返回 true 。相反,下面是确切测试 Future 的例子:

Future<T> logValue<T>(FutureOr<T> value) async {
  if (value is Future<T>) {
    var result = await value;
    print(result);
    return result;
  } else {
    print(value);
    return value as T;
  }
}
Future<T> logValue<T>(FutureOr<T> value) async {
  if (value is T) {
    print(value);
    return value;
  } else {
    var result = await value;
    print(result);
    return result;
  }
}

在错误的示例中,如果给它传一个 Future<Object> ,它会错误地将其视为一个空的同步对象值。

vim学习及配置

~/.vimrc

set tabstop=4
set softtabstop=4
set shiftwidth=4

set ts=4
set expandtab
set autoindent
set hlsearch
set showmode
set nofoldenable

syntax enable
syntax on
set encoding=utf8
set fileencoding=utf8
language messages zh_CN.utf-8

set tags+=tags,tags.vendors
map <F2> :NERDTreeToggle<CR>
map <F3> :set number<CR>
map <F12> :!ctags -f tags.vendors vendor<CR>
map <F4> :!ctags app<CR>

autocmd BufWritePost *.php silent execute “![ -d app ] && ctags app”

function! IPhpInsertUse()
call PhpInsertUse()
call feedkeys(‘a’, ‘n’)
endfunction
autocmd FileType php inoremap <Leader>u <Esc>:call IPhpInsertUse()<CR>
autocmd FileType php noremap <Leader>u :call PhpInsertUse()<CR>
function! IPhpExpandClass()
call PhpExpandClass()
call feedkeys(‘a’, ‘n’)
endfunction
autocmd FileType php inoremap <Leader>e <Esc>:call IPhpExpandClass()<CR>
autocmd FileType php noremap <Leader>e :call PhpExpandClass()<CR>

let g:php_namespace_sort_after_insert = 1
autocmd FileType php set omnifunc=phpcomplete#CompletePHP

colorscheme solarized

if has (‘gui_running’)
set background=light
else
set background=dark
set t_Co=256
endif

let g:indent_guides_enable_on_vim_startup = 1
let g:indent_guides_auto_colors = 0
hi IndentGuidesOdd ctermbg=darkgray
hi IndentGuidesEven ctermbg=darkgray

set shell=sh
let g:NERDTreeShowIgnoredStatus = 0

let g:NERDTreeIndicatorMapCustom = {
    \ "Modified"  : "✹",
    \ "Staged"    : "✚",
    \ "Untracked" : "✭",
    \ "Renamed"   : "➜",
    \ "Unmerged"  : "═",
    \ "Deleted"   : "✖",
    \ "Dirty"     : "✗",
    \ "Clean"     : "✔︎",
    \ 'Ignored'   : '☒',
    \ "Unknown"   : "?"
    \ }

let g:solarized_termcolors=256
"设置颜色为256色,默认为16
let g:solarized_termtrans=0
"设置背景色为透明,使用终端的背景色
let g:solarized_degrade=0
"强制使用256级色彩,只用于测试,默认为0
let g:solarized_bold=1
"使用粗体,默认为1
let g:solarized_underline=1
"使用下划线,默认为1
let g:solarized_italic=1
"使用斜体,默认为1
let g:solarized_contrast="normal"
"设置对比度,默认为normal,还可以设置为low或者high
let g:solarized_visibility="normal"
"设置空白符的可见性,默认为normal,还可以设置为low或者high
let g:solarized_hitrail=0
"光标高亮时,空白符仍可见,默认为0
let g:solarized_menu=1
"启用solarized_menu菜单,默认为1
inoremap ( ()<ESC>i
inoremap [ []<ESC>i
inoremap < <><ESC>i
inoremap ' ''<ESC>i
inoremap " ""<ESC>i
inoremap { {}<ESC>i<CR><ESC>O 

~/.bashrc

if [ -e /lib/terminfo/x/xterm-256color ]
then
export TERM=’xterm-256color’
else
export TERM=’xterm-color’
fi

~/.ctags

-R
–languages=php
–php-kinds=cif
–exclude=*test*
–exclude=vendor/*/vendor
–fields=+aimS

添加lua支持

sudo apt install vim-nox

插件

ctags

NERDTree

arnaud-lb/vim-php-namespace

shawncplus/phpcomplete

indent_guides

phpns

Xuyuanp/nerdtree-git-plugin

altercation/vim-colors-solarized

preservim/nerdcommenter

总结:

◈ 安装 03%

◈ 新手指南 04%

◈ 移动光标 05%

◈ 退出 08%

◈ 删除 08%

◈ 修改 10%

◈ 撤销 11%

◈ 复制粘贴剪切 12%

◈ 状态 13%

◈ 查找 13%

◈ 替换 15%

◈ 折叠 16%

◈ 执行外部命令 17%

◈ .vimrc 18%

◈ 基本配置 23%

◈ 取消备份 23%

◈ 文件编码 23%

◈ 显示行号 24%

◈ 取消换行 24%

◈ 显示光标当前位置 25%

◈ 设置缩进 25%

◈ 突出显示当前行 26%

◈ 查找 27%

◈ 左下角显示当前 vim 模式 27%

◈ 代码折叠 28%

◈ 主题 29%

◈ 插件配置 32%

◈ 树形目录 32%

◈ 代码,引号,路径补全 46%

◈ 语法高亮,检查 50%

◈ 文件,代码搜索 56%

◈ 加强版状态栏 59%

◈ 代码注释 63%

◈ git 69%

◈ Markdown 71%

◈ Emmet 74%

◈ html 5 76%

◈ css 3 77%

◈ JavaScipt 82%

◈ React 91%

◈ Prettier 93%

◈ 总结 97%

 

 

Ubuntu14.04 安装 PHP7.3

  1. sudo apt-get install libxml2-dev
    sudo apt-get install libcurl3-openssl-dev
    sudo apt-get install libcurl4-gnutls-dev
    sudo apt-get install libbz2-dev
    sudo apt-get install libjpeg-dev
    sudo apt-get install libpng-dev
    sudo apt-get install libxpm-dev
    sudo apt-get install libfreetype6-dev
    sudo apt-get install libmcrypt-dev
    sudo apt-get install libmysql++-dev
    sudo apt-get install libxslt1-dev
  2. wget https://www.php.net/distributions/php-7.3.9.tar.bz2
  3. tar xvf php-7.3.9.tar.bz2
  4. cd php-7.3.9
  5. ./configure –prefix=/usr/local/php –enable-fpm –enable-sockets –enable-mbstring=all -enable-mysqlnd —with-config-file-path=/usr/local/php/etc —with-mysqli=mysqlnd —with-pdo-mysql=mysqlnd —with-curl —with-gd
  6. make -j2
  7. sudo make install
  8. sudo cp php.ini-development /usr/local/php/etc/php.ini
  9. cd /usr/local/php/etc
  10. sudo cp php-fpm.conf.default php-fpm.conf
  11. cd /usr/local/php/etc/php-fpm.d
  12. sudo cp www.conf.default www.conf
  13. sudo vim /etc/profile
  14. //将下面的一句话加到末尾
    export PATH=$PATH:/usr/local/php/bin
  15.  source /etc/profile
    php -v //出现版本号说明安装成功
    php -m //查看你安装的依赖
  16. 安装扩展需要openssl
    sudo apt-get install m4
    sudo apt-get -y install autoconf
  17. sudo apt-get install libssl-dev
  18. cd /php-7.3.9/ext/openssl
  19. cp config0.m4 config.m4 //有的扩展没有config.m4但有config0.m4
  20. phpize
  21. ./configure –with-php-config=/usr/local/php/bin/php-config //我们的php安装目录下
  22. make
  23. sudo make install
  24. sudo vim /usr/local/php/etc/php.ini
  25. Ubuntu安装php7.3
  26. so文件被拷贝到了/usr/local/php/lib/php/extensions/no-debug-non-zts-20180731/