typescript路径映射踩坑

问题由来

我们通常引用一个npm模块,直接引用模块的名称就可以了,如 import module from 'module' 。
但是如果引用自己开发的模块,往往要使用相对路径的方式找到目标模块,如 import myComponent from '../../../components/myComponent' 。
这种方式可以正常开发,可以完成业务需求,但是维护起来就很难弄了,因为通过这种相对路径的方式你很难一下定位这个组件在哪里,在项目复杂的时候,可能会产生预期外的结果。

别名设置

其实这个问题的解决方案已经很成熟了,从各种构建工具及命令中都有相应的设置。
我这里只针对 typescript 做说明,因为他提供的工具集有点坑。

假如我想实现 import myComponent from '@components/myComponent' 。
而我的目录结构为

./
└── src
    ├── components
    └── views
          └── app.ts

tsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@components/*": ["src/components/*"]
    }
  }
}

这里的关键配置是 baseUrl 和 paths 
具体说明可以参见文档
https://www.tslang.cn/docs/handbook/module-resolution.html#virtual-directories-with-rootdirs

webpack

假设有一个 webpack.config.js 文件在 ./ 下面

resolve: {
  alias: {
      '@components': path.resolve(__dirname, './src/components'),
  }
}

这个比较基础,直接查看webpack文档就可以了
https://www.webpackjs.com/configuration/resolve/#resolve-alias

通过tsc编译会产生的问题

该文章并不是要教你怎么使用别名,而是 typescript 的开发中有个坑。

用tsc编译的后,映射的路径不会处理,将导致编译后的代码找不到模块

比如上面的 import myComponent from '@components/myComponent' ,如果你使用 commonjs 模式编译后应该是 const myComponent require('@components/myComponent') ,结果就是找不到这个模块。

解决方案一:使用webpack等构建工具进行编译

可以通过 ts-loader 等组件进行编译,来规避掉这个问题

解决方案二:使用module-alias

我现在的场景是,就想使用 tsc -w 来进行开发,那么经过一番寻找,比较优秀的方案就是 module-alias 了。
https://www.npmjs.com/package/module-alias
最简单的用法就是将 const moduleAlias = require('module-alias') 放到你的项目入口处。
然后设置你的 package.json ,以前面的结构为例。

"_moduleAliases": {
  "@components" : "./src/components"
}

这样就可以达到 tsc 编译后,仍然可以找到设置别名的路径了。
至于 module-alias 的原理是什么可以看一下他的npm模块的描述,我就不擅自翻译,免得误人子弟。 

Linux下手动将php5 升级到 7

注:看这篇文章之前,请先确保你有一定服务器的操作知识和一定的php基础,不然的话清尽量找相对熟悉的人指导操作,如果升级中发生任何不可挽回的问题,本人不对其负任何责任。

下载你需要的php版本并解压

我下载的是7.3.4版本

wget http://cn2.php.net/distributions/php-7.3.4.tar.gz
tar zxvf php-7.3.4.tar.gz
cd php-7.3.4

准备安装

生成配置文件

[root@ php-7.3.4]# ./buildconf
buildconf: checking installation...
buildconf: autoconf version 2.69 (ok)
rebuilding aclocal.m4
rebuilding configure
rebuilding main/php_config.h.in

开始配置

下面这条命令是配置php7的安装明细,你需要关心以下几点:

  • 我升级php7是为了支持wordpress
  • 核心的模块是
  • mysql[可选]
  • mysqli
  • pdo-mysql
  • 当前配置 php-fpm 用户组是www,你可以根据你机器的情况再进行修改
[root@ php-7.3.4]# ./configure --prefix=/usr/local/php --exec-prefix=/usr/local/php --bindir=/usr/local/php/bin --sbindir=/usr/local/php/sbin --includedir=/usr/local/php/include --libdir=/usr/local/php/lib/php --mandir=/usr/local/php/php/man --with-config-file-path=/usr/local/php/etc --with-mysql-sock=/var/run/mysql/mysql.sock --with-mcrypt=/usr/include --with-mhash --with-openssl --with-mysql=shared,mysqlnd --with-mysqli=shared,mysqlnd --with-pdo-mysql=shared,mysqlnd --with-gd --with-iconv --with-zlib --enable-zip --enable-inline-optimization --disable-debug --disable-rpath --enable-shared --enable-xml --enable-bcmath --enable-shmop --enable-sysvsem --enable-mbregex --enable-mbstring --enable-ftp --enable-gd-native-ttf --enable-pcntl --enable-sockets --with-xmlrpc --enable-soap --without-pear --with-gettext --enable-session --with-curl  --with-jpeg-dir --with-freetype-dir --enable-opcache --enable-fpm --enable-fastcgi --with-fpm-user=www --with-fpm-group=www --without-gdbm --disable-fileinfo

该配置执行后,需要结果中没有报错,才算成功(不算warning)。
如果碰到了问题,就根据问题解决问题,我碰到了如下的问题:

  • libzip版本不对

开始编译和安装

执行下面代码

[root@ php-7.3.4]# make clean && make && make install

该过程根据网速时间可能会比较长。
该安装执行后,结果中没有报错,才算成功。
完成后需要执行 make test 来进行安装检验,这里没碰到问题,所以这里不做深究。
如果碰到了问题,就根据问题解决问题,我碰到了如下的问题:

  • cp: cannot stat `ext/phar/phar.phar’: No such file or directory

验证模块是否安装到对应目录

上面提到了几个核心模块:

  • mysql[可选]
  • mysqli
  • pdo-mysql

如果你没有特殊做配置,文件一般在 /usr/local/php/lib/php/extensions/no-debug-non-zts-xxxxx 下面
可以参考下图:

配置文件

主要需要配置以下文件

  • php.ini
  • php-fpm.conf
  • www.conf
[root@ php-7.3.4]# cp php.ini-production /usr/local/php/etc/php.ini
[root@ php-7.3.4]# cp /root/php-src-master/sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm
[root@ php-7.3.4]# cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf
[root@ php-7.3.4]# cp /usr/local/php/etc/php-fpm.d/www.conf.default /usr/local/php/etc/php-fpm.d/www.conf

php.ini

需要将上面的核心模块在 php.ini 中设置为开启,将下面代码添加到 php.ini 末尾

[php]
zend_extension=/usr/local/php7/lib/php/extensions/no-debug-non-zts-xxxxx/opcache.so
extension=/usr/local/php7/lib/php/extensions/no-debug-non-zts-xxxxx/mysqli.so
extension=/usr/local/php7/lib/php/extensions/no-debug-non-zts-xxxxx/pdo_mysql.so

这里要注意的是我为了保险起见在这里设置了绝对路径,你可以根据个人习惯选择使用相对路径。

php-fpm.conf

这里主要是添加日志和配置文件,将下面代码添加到 php-fpm.conf 末尾

######设置错误日志的路径
error_log = /var/log/php-fpm/error.log
######引入www.conf文件中的配置
include=/usr/local/php7/etc/php-fpm.d/*.conf

www.conf

这里是 php-fpm 的进程服务扩展配置

######设置用户和用户组
user = www
group = www
listen.owner = www
listen.group = www
listen.mode = 0660

######根据nginx.conf中的配置fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;设置PHP监听
; listen = 127.0.0.1:9000   #####不建议使用
listen = /var/run/php-fpm/php-fpm.sock

######开启慢日志
slowlog = /var/log/php-fpm/$pool-slow.log
request_slowlog_timeout = 10s

######设置php的session目录(所属用户和用户组都是nginx)
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/session

其他配置

添加php环境变量

[root@ ~]# echo -e '\nexport PATH=/usr/local/php/bin:/usr/local/php7/sbin:$PATH\n' >> /etc/profile && source /etc/profile

设置PHP日志目录和php-fpm进程文件(php-fpm.sock)目录

#######设置PHP日志目录和php-fpm的运行进程ID文件(php-fpm.sock)目录
[root@ ~]# mkdir -p /var/log/php-fpm/ && mkdir -p /var/run/php-fpm && cd /var/run/ && chown -R nginx:nginx php-fpm
#######修改session的目录配置
[root@ etc]# mkdir -p /var/lib/php/session
[root@ etc]# chown -R www:www /var/lib/php

设置PHP开机启动以及测试配置文件是否正确

######配置开机自启动,增加到主机sysV服务
[root@ php-7.3.4]# chmod +x /etc/init.d/php-fpm
[root@ php-7.3.4]# chkconfig --add php-fpm
[root@ php-7.3.4]# chkconfig php-fpm on
######测试PHP的配置文件是否正确合法
[root@ sbin]# php-fpm -t
[03-Apr-2019 17:50:04] NOTICE: configuration file /usr/local/php/etc/php-fpm.conf test is successful

启动服务

[root@ ]# /etc/init.d/php-fpm start
Starting php-fpm  done

通过命令 ps -aux|grep php 查看启动服务是否成功;
通过命令 php -v 查看升级是否成功;
通过查看 phpinfo.php 页面查看模块是否安装成功;

因为wordpress升级的php7

如果你是因为wordpress升级的php7,那么下面的内容你可能会感兴趣。

nginx

如果nginx启动后页面显示502,你就查一下nginx的日志,我碰到了以下几个问题:

  • fastcgi_pass报错
  • 这里可以看一下上面www.conf配置的第10行,看到 listen = /var/run/php-fpm/php-fpm.sock 
  • 那么你的nginx.conf的配置应该让fastcgi_pass也指向这一行
  • fastcgi_pass unix: /var/run/php-fpm/php-fpm.sock 
  • 如果你有另一个配置 fastcgi_pass unix:/tmp/php-cgi.sock 那么请注释掉
  • Call to undefined function mysqli_connect 找不到mysqli_connect方法
  • 查看php里面的mysqli组件是否没有安装好,可以通过 phpinfo.php 查看到

如果你的nginx没问题了,但是进入wordpress提示建立数据库连接错误,那就去检查 wp-config.php 里面数据库里面的主机设置是不是 localhost ,如果是就改为 127.0.0.1 就好了,同样的方法也适用于 phpMyAdmin 。

问题整理

libzip升级

yum remove libzip -y
wget https://nih.at/libzip/libzip-1.2.0.tar.gz
tar -zxvf libzip-1.2.0.tar.gz
cd libzip-1.2.0
./configure
make && make install
ldconfig

cp: cannot stat `ext/phar/phar.phar’: No such file or directory

进入你的php安装目录

cd  ext/phar/
ls -l
cp ./phar.php  ./phar.phar
make install

Nginx: [error] open() "/usr/local/Nginx/logs/Nginx.pid” failed(2:No such file or directory)

/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf

ZendGuardLoader.so: undefined symbol:

php7不再支持该模块,在 php.ini 中移除该模块。

文章安装及配置部分参考:https://typecodes.com/web/centos7compilephp7.html?utm_source=tuicool&utm_medium=referral

手动升级wordpress5.1.x攻略

我之前是4.x版本,虽然用着没啥问题,但是因为习惯了 markdown 的录入,正好又看到 wordpress 5.x 已经默认支持了 markdown 就考虑做一次升级。

升级步骤

  1. 升级前先备份 wordpress 最好也备份一下数据库
  2. 下载安装包到本地 https://wordpress.org/download/
  3. 解压安装包
  4. 如果你是升级,去掉 wp-content 目录,这个目录主要是你的配置文件和主题
  5. 粘贴剩下的文件到线上的 wordpress 目录
  6. 再浏览器输入你的 http://{wordpress_path}/wp-admin/upgrade.php 
  7. 提示需要升级数据库的话,就点击升级wordpress数据库
  8. 完成

升级的问题

升级过程比较简单,我没碰到什么问题,但是升级后发现 5.x 版本推荐使用 php7.x 版本,而我服务器上的是 5.x 版本,然后我就走上了一条艰难的php升级之路。
具体的php升级心得点击这里

接口文档工具横向评测

评测还在继续…

评测对象

评测维度

  • 编写方式
  • UI
  • 与仓库关联
  • 监控仓库状态
  • 导出PDF
  • markdown
  • 兼容性
  • mock
  • 接口调试
  • 开源
  • 自动化测试
  • 插件

评测方式

通过以下方式得出评测结论

  • 网站简介
  • 提交频率
  • 更新频率
  • 产品示例
  • 舆论口碑

注意:因为没有精力一一部署去测试,所以该评测在一定程度上不够客观。

评测结果

APIDOCYAPINEI
录入接口方式
注释/在线编辑在线编辑在线编辑
UI/交互二次开发自带自带
与仓库关联GIT/SVNNN
监控仓库状态YNN
导出PDFhtml,markdown,jsonPDF
markdownYNN
数据兼容性postmanpostman/swagger/json貌似不支持导入
mock根据接口示例生成支持可编程mock支持可编程mock
接口调试Y 需要是可以访问apidoc服务器的机器才可以YY
自动化测试NYN
开源YYY
插件需要自行二次开发Y
社区维护现在仅有我们自己维护持续维护还有维护

评测总结

这里仅是根据上面数据的个人总结

  • 关于录入接口的方式
  • apidoc更全面更方便,更符合开发流程
  • YApi也可以,值得一提的是YApi支持的导入方式比较多,从某个角度来说也可以通过二次开发实现其他的录入方式
  • NEI仅支持在线录入方式
  • UI/交互
  • apidoc完全就是文档管理的界面交互,因为完全是公司的开发,很多细节还有待提高
  • YApi界面也还可以,满足基本需要
  • NEI的页面交互更像是一个项目管理的交互,而不是针对接口的
  • 与代码仓库的关系
  • 除了apidoc其他的平台都没有直接和代码仓库产生联系
  • YApi可以通过插件的方式,自己开发定时任务或者自己二次开发来跟仓库产生联系
  • 导入导出
  • apidoc支持基本的pdf导出和postman及注释文件的导入,正在规划对swagger的支持
  • YApi的导入导出支持的格式比较多
  • NEI仅支持导出PDF
  • mock
  • apidoc仅支持对文档中示例的基本数据mock
  • YApi支持可编程的mock
  • NEI在mock方面支持的非常好
  • 测试
  • apidoc仅支持数据结果的查看
  • YApi支持自动化测试
  • NEI支持单个或批量的接口测试
  • 其他功能
  • apidoc支持markdown的文档输入方式,除了写接口文档,也支持添加技术文档,甚至可以用来记笔记
  • YApi提供二次开发插件的功能,可根据自己的需要自己开发功能
  • NEI仅有当前提供的功能,因为是开源也可以二次开发,但是社区对二次开发的支持度不高
  • 社区维护
  • apidoc因为是个引擎,所以并没有什么社区维护,仅是根据当前的引擎做需要的开发
  • YApi现在的社区相对活跃,官方代码提交的频率也比较高
  • NEI的社区不是很活跃,代码提交频率也不是很高了,不知道会不会后面就没人维护了

写在最后

如果诉求是对文档进行维护和管理,当前内部开发的apidoc应该是首选,潜力也比较大。
但是要是说对接口本身和接口相关的体系进行管理,从外部开源的项目来看,NEI更优秀。
YApi虽然在两个方面都没有达到最好,但因为对插件开发的支持,以及灵活性,并不输NEI。
所以,如果需求只是对接口文档的管理,建议使用apidoc,同样的投入会得到更高的效果。
但是要对接口这边做全局的管理,可以用YApi+apidoc的方式。
下面提出一个思路以供参考:

  • 从代码中的注释生成文档
  • 然后为apidoc增加一个swagger的文档输出服务
  • 再通过一个定时同步服务,将apidoc输出的swagger直接同步给YApi
  • 这样,所有接口的文档部分由apidoc管理同步给YApi,接口的测试和MOCK能力由Yapi来处理

IME输入判断

原始文档

https://developer.mozilla.org/zh-CN/docs/Web/Events/compositionstart

浏览器一般都兼容,但是移动端没有明示网络上说基本支持,可以一用

VUE

vue的input中集成了这个功能
但是这里要注意
input里面监听值的更换有两种方式

// 该方式是支持IME输入判断的
<input v-model="value" type="text">
// 该方式是不支持IME判断的
<input :value="value" type="text" @input="onInput">

可以看一下vue源码
https://github.com/vuejs/vue/blob/52719ccab8fccffbdf497b96d3731dc86f04c1ce/src/platforms/web/runtime/directives/model.js#L34
https://github.com/vuejs/vue/blob/52719ccab8fccffbdf497b96d3731dc86f04c1ce/src/platforms/web/runtime/directives/model.js#L134

IPhone X 适配

关于底部

~'' 是控制忽略编译,不然css-loader在解析的时候会报错

@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
  .bottom-bar {
    bottom: calc(~'1.2267rem +  constant(safe-area-inset-bottom)');
    bottom: calc(~'1.2267rem +  env(safe-area-inset-bottom)');
  }
}

env() 和 constant()

iOS11 新增特性,Webkit 的一个 CSS 函数,用于设定安全区域与边界的距离,有四个预定义的变量:

  • safe-area-inset-left:安全区域距离左边边界距离
  • safe-area-inset-right:安全区域距离右边边界距离
  • safe-area-inset-top:安全区域距离顶部边界距离
  • safe-area-inset-bottom:安全区域距离底部边界距离

这里我们只需要关注 safe-area-inset-bottom 这个变量,因为它对应的就是小黑条的高度(横竖屏时值不一样)。

注意:当 viewport-fit=contain 时 env() 是不起作用的,必须要配合 viewport-fit=cover 使用。对于不支持env() 的浏览器,浏览器将会忽略它。

在这之前,笔者使用的是 constant(),后来,官方文档加了这么一段注释(坑):

The env() function shipped in iOS 11 with the name constant(). Beginning with Safari Technology Preview 41 and the iOS 11.2 beta, constant() has been removed and replaced with env(). You can use the CSS fallback mechanism to support both versions, if necessary, but should prefer env() going forward.

这就意味着,之前使用的 constant() 在 iOS11.2 之后就不能使用的,但我们还是需要做向后兼容,像这样:

padding-bottom: constant(safe-area-inset-bottom); /* 兼容 iOS < 11.2 */
padding-bottom: env(safe-area-inset-bottom); /* 兼容 iOS >= 11.2 */

注意:env() 跟 constant() 需要同时存在,而且顺序不能换。
更详细说明,参考文档:Designing Websites for iPhone X

iOS滚动定位问题

iOS12非浏览器滚动默认没有弹性问题

Q:增加弹性样式,-webkit-overflow-scrolling: touch;

思考题:到底都有哪些ios容器会有这个问题

如果容器高度变化,从有滚动条变成没有滚动条,里面的元素定位会继承有滚动条时的定位,而导致位置错误

Q:让当前容器初始化的时候就一定会出现滚动条可以避免这个问题,比如设置height: 101%;另外这个问题还要注意一下,如果你是rem布局,不要在height这里用calc(100% + 1px),因为rem有亚像素问题,一像素可能解决不了这个问题,非要优化的话建议+3px。

[转]React.PureComponet

转自谈一谈创建React Component的几种方式

我们知道,当组件的props或者state发生变化的时候:React会对组件当前的Props和State分别与nextProps和nextState进行比较,当发现变化时,就会对当前组件以及子组件进行重新渲染,否则就不渲染。有时候为了避免组件进行不必要的重新渲染,我们通过定义shouldComponentUpdate来优化性能。例如如下代码:

class CounterButton extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
}
shouldComponentUpdate(nextProps, nextState) {
if (this.props.color !== nextProps.color) {
return true;
}
if (this.state.count !== nextState.count) {
return true;
}
return false;
}
render() {
return (
<button> this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
);
}
}

shouldComponentUpdate通过判断props.color和state.count是否发生变化来决定需不需要重新渲染组件,当然有时候这种简单的判断,显得有些多余和样板化,于是React就提供了PureComponent来自动帮我们做这件事,这样就不需要手动来写shouldComponentUpdate了:

class CounterButton extends React.PureComponent {
constructor(props) {
super(props);
this.state = {count: 1};
}
render() {
return (
<button> this.setState(state => ({count: state.count + 1}))}>
Count: {this.state.count}
);
}
}

大多数情况下, 我们使用PureComponent能够简化我们的代码,并且提高性能,但是PureComponent的自动为我们添加的shouldComponentUpate函数,只是对props和state进行浅比较(shadow comparison),当props或者state本身是嵌套对象或数组等时,浅比较并不能得到预期的结果,这会导致实际的props和state发生了变化,但组件却没有更新的问题,例如下面代码有一个ListOfWords组件来将单词数组拼接成逗号分隔的句子,它有一个父组件WordAdder让你点击按钮为单词数组添加单词,但他并不能正常工作:

class ListOfWords extends React.PureComponent {
render() {
return
<div>{this.props.words.join(',')}
;
}
}
class WordAdder extends React.Component {
constructor(props) {
super(props);
this.state = {
words: ['marklar']
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// 这个地方导致了bug
const words = this.state.words;
words.push('marklar');
this.setState({words: words});
}
render() {
return (
<div> );
}
}

这种情况下,PureComponent只会对this.props.words进行一次浅比较,虽然数组里面新增了元素,但是this.props.words与nextProps.words指向的仍是同一个数组,因此this.props.words !== nextProps.words 返回的便是flase,从而导致ListOfWords组件没有重新渲染,笔者之前就因为对此不太了解,而随意使用PureComponent,导致state发生变化,而视图就是不更新,调了好久找不到原因~。

最简单避免上述情况的方式,就是避免使用可变对象作为props和state,取而代之的是每次返回一个全新的对象,如下通过concat来返回新的数组:

handleClick() {
this.setState(prevState => ({
words: prevState.words.concat(['marklar'])
}));
}

你可以考虑使用Immutable.js来创建不可变对象,通过它来简化对象比较,提高性能。
这里还要提到的一点是虽然这里虽然使用了Pure这个词,但是PureComponent并不是纯的,因为对于纯的函数或组件应该是没有内部状态,对于stateless component更符合纯的定义,不了解纯函数的同学,可以参见这篇文章

跨域请求传递cookie,设置withCredentials

场景是一个活动,主域名一样,子域名不同,需要cookie来获取登录态。
因为在默认情况下,跨域是不提供cookie的
需要将xhr的属性进行设置设置
withCredentials = true
同时服务端需要加一个http的头
Access-Control-Allow-Credentials: true
这边还要特殊说明一下兼容性问题,支持withCredentials属性的浏览器有Firefox 3.5+、Safari 4+和Chrome。IE10及更早版本都不支持。