Windows下安装npm本地化仓库Sinopia

采用Node.js开发本地项目,有时不同项目之间存在依赖,如果不想把项目发布到npm社区的仓库,则需要有自己本地的仓库。本地仓库候选方案有:sinopia,cnpm和kappa。

sinopia特点

  1. 零配置安装
  2. 使用文件系统作为存储,仅保存用户需要的包,如果本地仓库没有对应的包,则从指定的registry下载,默认为npmjs.org。

安装

以下是安装过程以及遇到的问题

安装环境:

  • windows: 8.1
  • node: 6.9.5
  • npm:3.10.1
  • python: 2.7

Sinopia的安装比较简单,只需使用npm一条安装命令即可:

npm install -g sinopia

安装问题

在windows下直接执行这个命令会遇到一些问题:

1、 python没有安装或版本不对

gyp ERR! stack Error: Can't find Python executable "python", you can set the PYTHON env variable.

node-gyp依赖python 2.7。安装python2.7,并把它添加到环境变量PATH,npm config set python python2.7

2、MSBuild版本不对

MSBUILD : error MSB4132: The tools version "2.0" is unrecognized. Available too ls versions are "4.0"

node-gyp需要用到Visual C++ Build Tools ,安装Visual Studio Express 2013 for Windows Desktop可以解决。

3、windows下不支持fs-ext和crypt3

node-gyp报编译fs-ext和crypt3失败的错误,安装Sinopia时可以忽略。错误信息如下:

fs-ext.cc(195): error C3861: 'fcntl': identifier not found [C:\Users\clcaza\AppData\Roaming\npm\node_modules\sinopia\node_modules\.0.6.0@fs-ext\build\fs-ext.vcxproj]

以及

crypt3.cc(5): fatal error C1083: Cannot open include file: 'unistd.h': No such file or directory [C:\Users\clcaza\AppData\Roaming\npm\node_modules\sinopia\node\_modules\.0.2.0@crypt3\build\crypt3.vcxproj]

sinopia模块sinopia-htpasswd依赖于fs-ext和crypt3,但这两个包是可选的,查看sinopia源码里的package.yaml可以看到

optionalDependencies:
  # those are native modules that could fail to compile
  # and unavailable on windows
  fs-ext: '>=0.4.1 <1.0.0-0'
  crypt3: '>=0.1.6 <1.0.0-0' # for sinopia-htpasswd

在windows,删除sinopia安装目录node_modules里的fs-ext和crypt3相关的包,否则执行npm添加用户和登陆验证时会报错。包括:.0.2.0@crypt3,.0.6.0@fs-ext,crypt3和fs-ext以及sinopia-htpasswd\node_modules下的crypt3和fs-ext

sinopia使用

启动

$ sinopia
warn  --- config file  - C:\Users\clcaza\AppData\Roaming\sinopia\config.yaml
warn  --- http address - http://localhost:4873/

# npm configuration
$ npm set registry http://localhost:4873/

sinopia启动时默认会创建config.yaml文件,文件路径可以看输出的提示。可以使用-c选项指定配置文件。

$ sinopia -c D:\sinopia\config.yaml

添加用户

$npm adduser --registry http://localhost:4873/
Username: clc
Password: clc
Email: (this IS public) cc@cc.cc

如果没有删除crypt3,添加用户时会报错:

error --- unexpected error: Cannot find module './build/Release/crypt3'

添加完用户后,可以使用npm 登陆

$npm login
Username: clc
Password: clc
Email: (this IS public) clc@cc.com
Logged in as clc on http://localhost:4873/.

这样你就可以使用sinopia来发布了。

sinopia配置

sinopia默认配置如下

# 存储的路径
storage: ./storage

auth:
  htpasswd:
    #使用htpasswd保存用户验证信息
    file: ./htpasswd
    # Maximum amount of users allowed to register, defaults to "+inf".
    # You can set this to -1 to disable registration.
    #max_users: 1000

# a list of other known repositories we can talk to
uplinks:
  npmjs:
    #如果sinopia本地仓库不存在包时,默认下载的npm仓库,国内可以配置淘宝的 https://registry.npm.taobao.org
    url: https://registry.npmjs.org/

packages:
  '@*/*':
    # scoped packages
    access: $all
    publish: $authenticated

  '*':
    # allow all users (including non-authenticated users) to read and
    # publish all packages
    #
    # you can specify usernames/groupnames (depending on your auth plugin)
    # and three keywords: "$all", "$anonymous", "$authenticated"
    access: $all

    # allow all known users to publish packages
    # (anyone can register by default, remember?)
    publish: $authenticated

    # if package is not available locally, proxy requests to 'npmjs' registry
    proxy: npmjs

# log settings
logs:
  - {type: stdout, format: pretty, level: http}
  #- {type: file, path: sinopia.log, level: info}

sinopia对npm的支持

sinopia目前支持下面npm的命令:

  • publish
  • unpblish
  • install
  • adduser

参考:

WebStorm支持ES6语法设置

要WebStorm支持ES6语法,只需要设置Javascript语言版本为ECMAScript 6即可。

Windows设置路径:

File -> Settings -> Languages & Frameworks -> JavaScript

Mac设置路径:

Preferences > Languages & Frameworks > JavaScript

设置属性JavaScript language version为ECMAScript 6。

怎么理解JavaScript匿名箭头函数f=>f

箭头函数f => f恒等函数,它会把所传入的值原值返回,不做任何变换,即传入值和返回值恒等。

做变换处理,可以使用恒等函数f=>f作为变换返回的默认值。

Android Studio Gradle构建报错,Error:Execution failed for task ':app:mergeDebugResources'.

方法一

在build.gradle添加配置:

android {
// ...
aaptOptions.cruncherEnabled = false
aaptOptions.useNewCruncher = false
// ...
}

Android Studio不再严格检查PNG图片

方法二

删除drawable文件夹里的图片,重新构建。

Intel x86 Emulator Accelerator (HAXM installer)与windows不兼容

使用Intel x86模拟器的加速器:

  • 在BIOS检查是否打开了Intel Virtualization Technology (VT-x)
  • 安装HAXM
    1. 下载HAXM
    2. 解压运行intelhaxm-android.exe
    3. 运行silent_install.bat

怎么移除视频在Chrome里的下载按钮

在Chrome的控制台打开Show user agent shadow DOM查看Chrome隐藏的代码:

打开Chrome控制台(F12)->Settings(F1) -> Preferences -> Elements -> Show user agent shadow DOM

勾上Show user agent shadow DOM

切换到Elements,查看页面的DOM结构,选中下载按钮的元素可以看到:enter image description here

CSS隐藏下载按钮的方案

video::-internal-media-controls-download-button {
    display:none;
}

video::-webkit-media-controls-enclosure {
    overflow:hidden;
}

video::-webkit-media-controls-panel {
    width: calc(100% + 30px); 
}

注意:用CSS hacker的方式并非好的方案,这依赖于chrome的DOM选择器,一旦DOM结构变化可能就会导致方案失效。

参考chromium bug Disable HTML5 media player download button

Chrome报表单提交错误,Form submission canceled because the form is not connected

原因

HTML标准规定如果form表单没有被添加到document里,那么form表单提交将会被终止。

参考:Form submission algorithm

在Chrome56之前的版本是不符合标准的,Chrome56修复了这个问题,让form表单提交符合标准要求:

参考:Chrome issue 2416033002

解决方法

解决方法就是把form表单添加到document后再提交:

jQuery

$(document).append(form);

document.body.appendChild(form);

Swift 3类型转换报错,cannot convert value of type NSMutableDictionary to expected argument type '[NSObject:AnyObject]?'

Swift的NSError已经更新为接受原生的Swift字典作为参数,和不可变字典NSDictionary不一样, NSMutableDictionary 没有和Swift做转换关联,所以会有这样的错误。

方法一:

更改为使用swift原生的字典:

let errorInfo= [String: AnyObject]()

方法二

如果不方便修改NSMutableDictionary的类型,把可变的NSMutableDictionary转换为NSDictionary,然后再转换为[String: AnyObject]()

var errorInfo = errorInfo as NSDictionary? as? [String: AnyObject] ?? [:]

Swift 3报错:Value of type 'String' has no member 'substringWithRange'

Swift 3截取字符串有下面这三种方法:

str.substring(to: String.Index)
str.substring(from: String.Index)
str.substring(with: Range<String.Index>)

Swift 2的String.substringWithRange的方法可以使用Swift 3的String.substring(with: Range<String.Index>)代替。

可以参考Swift 3字符串截取substring的用法

Swift 3字符串截取substring的用法

Swift 3的String有三个方法用于做字符串截取:

str.substring(to: String.Index)
str.substring(from: String.Index)
str.substring(with: Range<String.Index>)

用于做示范的示例:

var str = "Hello, World"

str.substring(to: String.Index)

这个方法会从字符串的开始截取到to参数指定的索引。

let index = str.index(str.startIndex, offsetBy: 5)  //索引为从开始偏移5个位置
str.substring(to: index)  // 获取Hello

substring(from: String.Index)

这个方法会从from参数指定的索引截取到字符串的末尾。

let index = str.index(str.startIndex, offsetBy: 7) //索引从开始偏移7个位置
str.substring(from: index)  // 输出World

str.substring(with: Range<String.Index>)

这个方法是截取指定的字符串范围,范围由Range指定。类似于Swift 2的String.substringWithRange

let start = str.index(str.startIndex, offsetBy: 7)  //索引从开始偏移7个位置
let end = str.index(str.endIndex, offsetBy: -3)   //所有从末尾往回偏移三个位置
let range = start..<end

str.substring(with: range)  // 输出Wo

怎么使用CSS变量

CSS变量由用户定义,并设置一个值,这个变量可以在文档里重用。使用CSS变量可以提高代码的可维护性和提高可读性。

基本用法

声明变量

element {
  --main-bg-color: brown;
}

使用变量

element {
  background-color: var(--main-bg-color);
}
  • element

    element为CSS选择器,包括id选择器,类选择器,元素选择器等。其中:root伪类在声明CSS变量是比较常用来放置CSS变量。

    :root {
      --main-bg-color: brown;
    }

    :root匹配文档根元素,在HTML里,文档的根元素就是html。把CSS变量放置在:root下,这样文档里的任何元素都可以引用这个变量重用,如果在特定的元素里需要改变变量的值,可以在特定的选择器里定义这个变量。

  • 自定义属性

    与CSS标准属性不一样,自定义属性由用户自己定义属性的名字,它的格式是在属性名前加上'--':

     --my-size: 100px;

    CSS变量就是使用自定义属性来声明。

  • var()函数

    在CSS里,需要使用CSS的var()函数来获取自定义属性的值。

    :root {
       --text-color: #000000;
    }
    p {
      color: var( --text-color );
      font-size: 16px;
    }

变量的继承

自定义属性是可以继承的,如果某个元素的自定义属性没有被设置,自定义属性则会继承元素的父元素的值。

:root {
  --base-button-color : red;
}
form {
  --base-button-color: blue;
}
a{
  background-color: var(--base-button-color);
}

page1

<html>
  <body>
    <a>button</a>
  </body>
<html>

page2

<html>
  <body>
    <form>
      <a>button</a>
    </form>
  </body>
<html>

page1里a元素没有定义对应的--base-button-color,所以它会继承父元素,一直追溯到:root,背景颜色显示红色。

page2里a元素追溯的父元素form定义的--base-button-color,form定义的-base-button-color则会覆盖根元素定义的值。

使用CSS变量的好处

使用CSS变量最明显的两个好处是:提高代码的维护性和提高代码的可读性

  • 可维护性

    在一些比较大的网站,CSS代码量往往也比较大,如果在多个地方会使用同样的值,当有更改时,那么就需要搜索出来做替换。这种人工搜索替换的方式很容易出现人为失误导致bug。

    使用CSS变量,碰到上面的这些情况就比较容易处理了,如果是全部修改,那只需修改变量的值。

  • 可读性

    background-color: yellow;

    对比

    background-color: var( --highlight-color );

    自定义属性的方式具有语义,可读性比较好。

C语言里怎么把宏定义转换为字符串

首先使用str(a) 把参数a的值转换为字符串,然后使用宏扩展把xstr扩展为str(a)

#define str(a) #a
#define xstr(a) str(a)

#define PAGE_SIZE 10

int main()  
{  
    printf("page size = " xstr(PAGE_SIZE) " \n");  
    return 0;  
} 

vagrant 启动报错,The box could not be found or could not be accessed in the remote catalog

这是 vagrant 1.8.7 的问题,vagrant自带的curl版本低与mac里的curl不兼容,把vagrant自带的curl删除即可

sudo rm /opt/vagrant/embedded/bin/curl

执行vagrant box add也会出现这个问题,也是把vagrant自带的curl删除。

参考:embedded libcurl provides out-of-date version

Gradle提示,You have not accepted the license agreements of the following SDK components: [Android SDK Build-Tools 24.0.2]

license存放在$ANDROID_HOME/licenses下

创建license

Linux

mkdir "$ANDROID_HOME/licenses"
echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license"

Windows

mkdir "%ANDROID_HOME%\licenses"
echo |set /p="8933bad161af4178b1185d1a37fbf41ea5269c55" > "%ANDROID_HOME%\licenses\android-sdk-license"

升级到iOS 10.1.1后,用Xcode 8.1 build app失败,file was built for archive which is not the architecture being linked (arm64)

  1. 检查Architectures 和 Valid Architectures是否设置正确

    Project -> target (项目名) -> build settings ,设置architectures 和Valid Architectures,Active Architectures 设置为 YES

  2. 清除DerivedData里的数据

    exit xcode
    rm -rf ~/Library/Developer/Xcode/DerivedData

JavaScript检查2G网络环境

使用Network Information API来检查网络2G状态

if(navigator.connection &&
   navigator.connection.type === 'cellular' &&
   navigator.connection.downlinkMax <= 0.115) {
  // 2G网络
}

A Parser-blocking, cross-origin script is invoked via document.write. This may be blocked by the browser if the device has poor network connectivity.

在弱的网络连接环境下,比如2G网络,在页面上使用document.write()来动态插入外部的脚本会阻塞页面的解析,延迟页面的显示,甚至加载脚本失败,最终导致页面不能正确显示。

为了提高用户的体验,Chrome对于由document.write()动态插入的<script>会做检查,当满足下面所有的条件下,Chrome不会执行加载<script>里的脚本。

  1. 用户处在弱网络连接的环境下,特别是2G网络。
  2. document.write()在主页面里,对于那些嵌入在iframe里的页面没有影响。
  3. document.write()插入的脚本是阻碍解析的(parser-blocking)。如果插入的<script>标签加了 'async' 或着'defer'属性,脚本会异步加载,不影响解析 ,所以也是能被执行的。
  4. 加载的脚本和站点不是同一个域名。
  5. 脚本没有在浏览器的缓存里
  6. 页面不是重新加载

从Chrome 53开始,对于满足2-5条件的代码,在控制台会输出问题里的警告:

A Parser-blocking, cross-origin script, https://example.com/script.js, is invoked via document.write. This may be blocked by the browser if the device has poor network connectivity.

解决方案:

  1. 最好的办法就是不要使用document.write()动态加载脚本
  2. 如果一定要使用document.write()加载脚本,使用异步加载的方式,如<script src="..." async> 或使用DOM API element.appendChild()

CSS实现在圆圈里创建三个竖直排列的小圆点

圆圈实现:把元素的宽和高设为相等,border-radius设为50%。

小圆点可以使用着重点'•',对应的Unicode 编码为'\u2022',三个小圆点'•••'使用transform: rotate做旋转90度即可得到竖直排列。

实现代码如下:

div {
  position: relative;
  background: #3F3C53;
  width: 50px;
  height: 50px;
  color: white;
  border-radius: 50%;
  box-shadow: 0px 0px 15px 1px #4185BC;
  margin: 50px;
}
div:after {
  content: '•••';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%) rotate(90deg);
  font-size: 15px; 
  letter-spacing: 4px;
  margin-top: 2px;
}

JavaScript报错,ReferenceError: invalid assignment left-hand side

var a1, a2; a1 = !a2 = true;

实际等同于:

var a1, a2;
!a2 = true;
a1 = a2;

在赋值表达式!a2 = true;中,左边是一个表达式,不能接收被赋值,所以报这个错误。

所以遇到invalid assignment left-hand side 错误,可以查看下代码哪里出现了不能被赋值。

Chrome:您的连接不是私密连接,ERR_CERTIFICATE_TRANSPARENCY_REQUIRED

这是Chrome 53 的一个bug,如果https链接不能正常通过SSL certificate验证,就会提示Certificate Transparency 错误:ERR_CERTIFICATE_TRANSPARENCY_REQUIRED。

解决方案:

  1. 升级到chrome 54
  2. Google发布更新补丁后,重启chrome 53

参考:bugs