Gowhich

Durban's Blog

参考函数如下

实现原理,将base64图片进行分割,取出图片真实的字符串,并获取这个真实图片串的大小,然后提交到七牛

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 上传图片token
var uploadToken = 'xxxxx';

function putb64(base64Data, uploadKey, callback){
// 原始base64图片数据处理 返回一个 真实图片数据大小值 和 真实图片数据值
var imgSizeData = this.getBase64ImgSize(base64Data); // - 解释1

// 获取大小
var len = parseInt(imgSizeData.fileSize, 10);

var data = imgSizeData.base64Data;

// key的base64处理
var key = this.base64Key(uploadKey); // - 解释2

// 请求地址拼接 - 注意这里的http 如果网站是https的话换成https 不然在客户端尤其是iOS 端会出现异常
var url = "http://upload.qiniup.com/putb64/"+len+'/key/'+key;

// 七牛cdn绑定的域名
var domain = 'https://xx.com';

var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function(){
if (xhr.readyState==4){
// json 解析
var data = JSON.parse(xhr.responseText);

// 将代码拼接后返回给程序调用
callback(domain + '/' + data.key);
return true;
}
}
xhr.open("POST", url, true);

// 请求头信息设置
xhr.setRequestHeader("Content-Type", "application/octet-stream");
xhr.setRequestHeader("Authorization", "UpToken " + uploadToken + "");

// 提交数据
xhr.send(data);
}

官方参考: https://developer.qiniu.com/kodo/kb/1326/how-to-upload-photos-to-seven-niuyun-base64-code

解释1 - 请参考 base64图片的大小计算及获取原图字节大小

解释2 - 请参考 javascript的base64实现及使用

做过多年的互联网web开发,也做过大大小小各种类型的网站 包括购物网站,功能性网站,企业宣传网站等,对于web开发这块的架构大体上也就几种开发方式吧 我接触过的我就说记录下,没接触过的,估计也是比较另类的吧,或者是我自己见识短。

第一种方式 - (php、python、java,nodejs)+html+js(jquery)+css

第二种方式 - (php、python、java,nodejs)+vuejs/reactjs+webpack

先说说第一种方式,也算是比较再普通不过的方式了,继续介绍之前先说说后端语言

使用频率更多的后端语言的话应该是PHP,但是对于大公司来说可能还有java,这个我也没做过具体的统计,都是根据经验来的 虽然我自己在使用的过程中,也用过python、nodejs、java但是我用的比较多的还是php

php的话使用过的框架有qeephp,yii2,CI,laravel python的话使用过Django、flask java的话使用过spring-boot nodejs的话使用过koajs、express

php的话公司项目用的比较多,但是我比较喜欢yii2,喜欢它的代码和框架目录风格,导致我后面在学习swoole的时候,试着将swoole集成到yii2中,目前线上的几个博客项目一个是gowhich.com(swoole集成)、一个是xiaorongmao.com,都是我日常记录技术文章的博客站点

python的话我,开始的时候由于跟风喜欢用Django,但是经过一段时间,发现这个有点太庞大了,折腾不起了,于是发现了flask这个优秀的框架,慢慢的边学边做,就开发了另一个博客walkerfree.com。 最近在使用flask1.0系列,改动比较大,不多还是很喜欢,于是使用flask1.0开发了baby.xiaorongmao.com,很多功能出于学习python,都是自己研究查阅各种文章开发的,比如比较明显的验证码功能

java使用过spring-boot,可以说,是个比较方便好用的框架,上手很快,但是需要学的东西很多,后期搁置了。

nodejs使用过connect、express、koajs,后期使用koajs比较多,主要是自己的项目,本来打算做个博客的,但是工作的原因后面就搁置了。公司使用过express、connect,connect主要用来做代理

当然在这个过程中还接触过ruby,也就瞄了一眼,ruby on rails,那个大宝石不错

后端的语言其实都是大同小异,差异不是很大毕竟是做web开发,还能折腾到哪里去,该有的功能都有了,可以说随便选一个自己喜欢的就可以了,当然论速度的话我比较中立的选择php的yii2和python的flask,可能是因为比较熟悉吧,也许有人会说我用spring-boot一样开发很快,这个就不细说,我也只是聊聊我自己的,其中成本之类的,可能就不好说了。php好像比java学习成本低吧,python好像也是

哦,对了,最近在学一门超级快的语言Golang,公司是使用过了,以前php写的接口,耗时10000ms,该用Golang可以达到100ms甚至是10ms左右,这个对比差距还是很大的,不过公司在使用Golang之前我已经在2018年使用Golang开发wiki博客站了,后期由于公司业务多就慢慢的搁置了。当时的Golang资料少,可以直接使用扩展还少,另外,即使是有,使用起来也是各种别扭,因为跟python和php的使用方式不同。所以自己就慢慢的一边进行查询的封装,一边进行session的封装,就是一路上缺啥自己造啥。不过现在回头看下,已经有很多成型的库可以使用了,最近发现了gin这个框架,可以说是达到了我当时的想法,重写了Golang自身对路由的支持

好了,说说第一种开发方式,这个前面说过了,其实很普通的一种开发方式 可以不讲究css放在哪里,我放在页面中,或者放在文件中,或者内连于元素上,都一样的能达效果,开发速度也是很快,不够这个就不要将就什么速度了,将就速度的话,那就要说另外一个问题了,有点多。正常习惯使用原生js的话,可以直接一个html页面加上css+js就可以构建一个页面了,然后各种特效效果,直接网上搜索一下,download下来改一改,弄下样式,就搞定了,可以说对页面的复杂度不是很高的话,这样就可以完整快速的出一个页面了,但是后期维护的话,是个很麻烦的事,但是从做页面开始的时候,谁会想这个问题,因为公司要的就是马上上线。这个也就是现在的速成开发法吧。

下面说下第二种方式,第二种方式使用了框架,可不简单的是jquery,据说这个框架原理是虚拟dom,大概是2014年前后,刚来公司就开始搞单页面开发,当时是因为觉得如果每个页面都是在用户请求后重新去服务器请求,然后再加载的话会比较慢,于是就考虑使用单页面应用,但是也有很多的框架,使用起来是把我搞的云里雾里,最后技术leader说使用reactjs,这个也算是当时比较火的技术,于是我就开始一边学一边做,开始时是用来开发后台,我主要负责页面开发,接口有技术总监提供,当时就觉得这个reactjs很好的原因是,他这个框架是用来做组件化用的,比如一个页面,我们分为上中下三个部分的话,传统的写法就是都写在一个页面,使用reactjs的话,就是上部分封装为一个组件,中间部分封装为一个组件,下部分封装为一个组件,就这样,最后将三个部分封装起来,是不是觉得有点像是在组装小汽车,没错,就是这个作用。既然使用reactjs,当时想着能不能像使用jquery一样使用reactjs,直接在页面引用,然后写组件,但是实际情况是,这个方式不好,因为reactjs的语法默认是不被浏览器支持的,需要通过工具进行编译,编译为能够被浏览器识别的js代码,于是就又开始搞webpack,这个工具不得不说起实用的地方,基本上是解决了前端性能优化的问题,比如css的压缩、js的压缩、按需加载进行打包,将第一种方式中加载时的缺点都解决掉了,而且webpack还有一个我用的比较多的地方就是将静态文件全部发布到cdn平台,这样的话基本上就不需要一个服务器专门来存储静态文件了,而且利用cdn已经成熟的快速加载技术,完全可以不用自己搞服务器,花点钱就解决了。这个方式同时也适用于vuejs,但是我在接触vuejs后不喜欢它的原因是就是它的写法搞乱了html元素,让代码看起来很乱,虽说也能解决问题,但是我不喜欢。就像当初不喜欢angularjs时是一样的。 不过最近发现vuejs可以像使用jquery一样的使用vuejs,可以去试试,毕竟使用jquery来操作dom还是稍微比较费力的事情

大体上吧就这些技术的细节就没办法聊了,太细的话,会触及我的弱点,因为我不擅长理论这些东西

我能用flask做开发,写博客,那么我面试的时候人家问我一些asyncio之类的东西,我就蒙圈了,毕竟我不会没事去记一些不太使用的知识点,但是现状就是如此

不然人家怎么知道这个博客是不是你搭建的,没准是你自己从哪里copy过来的,说实话,互联网开发也不过如此,有些功能就是从网上copy下来的,我们应该充分利用互联网,因为可以节省很多劳动力和脑力,那么程序开发者就是这个技术的结合者,最后再加上创新,才能达到更高的顶点,才能让人类更快乐幸福

CSS省略号一直在用,而且用的比较多,这里做下总结,方便日后查用

CSS省略号可以说是前端开发必不可少的。其原理就是将溢出的字符进行覆盖,并通过css的省略号样式表进行填充。

另外的情况可能会用在多行之后添加省略号

参考这里,我这里摘录如下,国内可能有开发者打不开

单行CSS省略号

1
2
3
4
5
6
.ellipsis {
width: 300px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}

多行CSS省略号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.block-ellipsis {
width: 300px;
display: block;
display: -webkit-box;
max-width: 100%;
height: 43px;
margin: 0 auto;
font-size: 14px;
line-height: 1;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}

这里注意下height的值和webkit-line-clamp的值

Swoole之Process使用记录,Swoole自从发布之后,公司项目一直都只是基于http的情况使用,这次在脚本中应用了下,还是踩了些坑,先分享一个简单的

先看下一个简单的创建Process的流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
class SwooleProcessDemo
{
public $mpid = 0;
public $works = [];
public $max_process = 1;
public $processes = [];
public $new_index = 0;
public $ctime = 0;

public function __construct()
{
swoole_async_set(['enable_coroutine' => false]); // Process中仅用协程

// 由于所有进程是共享使用一个消息队列,所以只需向一个子进程发送消息即可 - 注意队列大小限制
try {
if (!preg_match('/Darwin/', php_uname())) {
swoole_set_process_name(sprintf('php-ps:%s', 'master'));
}
$this->mpid = posix_getpid();
$this->run();

$process = current($this->processes);

swoole_timer_tick(1000, function () use ($process) {
$data = '';
// $data = $this->getData(); 这里是需要自己是实现的

// push data
$process->push(implode(',', $data));
});

$this->processWait();
} catch (\Exception $e) {
var_dump($e);
}
}

public function run()
{
for ($i = 0; $i < $this->max_process; $i++) {
$this->createProcess($i);
}
}

public function createProcess($index = null)
{
$process = new swoole_process(function (swoole_process $worker) use ($index) {
if (is_null($index)) {
$index = $this->new_index;
$this->new_index++;
}

if (!preg_match('/Darwin/', php_uname())) {
try {
swoole_set_process_name(sprintf('php-ps:%s', $index));
} catch (\Exception $e) {
var_dump('ALL ERROR:' . $e->getMessage());
}
}

$data = $worker->pop();

if (!$data) {
$worker->exit(0);
}

if ($data) {
$this->handleData($data);
$this->checkMPid($worker);
}
unset($userId);
}, false, false);

$customMsgKey = 1;
$mod = 2 | swoole_process::IPC_NOWAIT; //这里设置消息队列为非阻塞模式
$process->useQueue($customMsgKey, $mod);
$pid = $process->start();
$this->works[$index] = $pid;
$this->processes[$pid] = $process;

return $pid;
}

public function checkMPid(&$worker)
{
if (!swoole_process::kill($this->mpid, 0)) {
$worker->exit();
}
}

public function rebootProcess($ret)
{
$pid = $ret['pid'];
$index = array_search($pid, $this->works);

if (false !== $index) {
$index = intval($index);
$new_pid = $this->createProcess($index);
}
}

public function processWait()
{
swoole_timer_tick(1000, function () {
if (count($this->works)) {
$ret = swoole_process::wait();
if ($ret) {
$this->rebootProcess($ret);
}
}
});
}

private function handleData($data)
{
// 你自己的逻辑
}
}

首先swoole_async_set([‘enable_coroutine’ => false]);这里我关闭了协程,原因是在进行processWait操作的时候,其swoole_timer_tick是不允许在其内部创建Process的,这个可以试着启用后看下报错信息

这个例子应该是一个比较完整的例子了,实践当中都有在使用,唯一的问题是在push上,之前一个例子比如发送短信,这个要求实时性,也就push了一些用户的ID,大小的话,可以忽略,但是当我向队列中push足够打的字符串的话,就会提示内存不足,原因可以到这里查看:https://wiki.swoole.com/wiki/page/290.html

1
2
3
4
5
6
7
swoole_timer_tick(1000, function () use ($process) {
$data = '';
// $data = $this->getData(); 这里是需要自己是实现的

// push data
$process->push(implode(',', $data));
});

解决方案如上代码,

之前的逻辑是for循环,然后直接执行push操作,在你push的时候,队列大小增加,最后直接内存满了,进程退出了,但是当我用上面代码的时候,这就是一个无限循环的永动机了。当然如果pop那边的操作延迟比较久的话,导致内存满了,也是push不进去的。

另外注意一点是swoole_timer_tick的函数的调用,不要使用while死循环,会导致swoole_timer_tick函数不起作用的,也就是

1
2
3
4
5
6
7
8
9
10
11
public function processWait()
{
swoole_timer_tick(1000, function () {
if (count($this->works)) {
$ret = swoole_process::wait();
if ($ret) {
$this->rebootProcess($ret);
}
}
});
}

这个代码其实也可以用另外一个方式实现的,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
public function processWait()
{
while (1) {
if (count($this->works)) {
$ret = swoole_process::wait();
if ($ret) {
$this->rebootProcess($ret);
}
} else {
break;
}
}
}

总结如下:

1、swoole_timer_tick使用时,不要使用while等类似的死循环阻塞swoole_timer_tick的执行

2、Process在进行push的时候,要注意队列的大小

新增

Vibora - Vibora was designed from scratch to be efficient.

参考地址:http://klen.github.io/py-frameworks-bench/#results

Aiohttp — http client/server for Asyncio.
Bottle — Fast, simple and lightweight WSGI micro web-framework
Django — The Web framework for perfectionists with deadlines
Falcon — A high-performance Python framework for building cloud APIs
Flask — A microframework based on Werkzeug, Jinja2 and good intentions
Muffin — A web-framework based on Asyncio stack
Pyramid 1.7 — A small, fast, down-to-earth, open source Python web framework
Weppy — The webframework for humans
Wheezy Web — A lightweight, high performance, high concurrency WSGI web framework
Tornado 4.3 — A Python web framework and asynchronous networking library

参考的地址里面还有各个框架的性能对比

Siege is an http load tester and benchmarking utility

参数说明:

-C,或-config 在屏幕上打印显示出当前的配置,配置是包括在他的配置文件$HOME/.siegerc 中,可以编辑里面的参数,这样每次siege 都会按照它运行.
-v 运行时能看到详细的运行信息
-c n,或-concurrent=n 模拟有n个用户在同时访问,n不要设得太大,因为越大,siege 消耗本地机器的资源越多
-i,-internet 随机访问urls.txt中的url列表项,以此模拟真实的访问情况(随机性),当 urls.txt存在是有效
-d n,-delay=n hit每个url之间的延迟,在0-n之间
-r n,-reps=n 重复运行测试n次,不能与 -t同时存在
-t n,-time=n 持续运行siege ‘n’秒(如10S),分钟(10M),小时(10H)
-l 运行结束,将统计数据保存到日志文件中siege .log,一般位于/usr/local/var/siege .log中,也可在.siegerc中自定义
-R SIEGERC,-rc=SIEGERC 指定用特定的siege 配置文件来运行,默认的为$HOME/.siegerc
-f FILE, -file=FILE 指定用特定的urls文件运行siege ,默认为urls.txt,位于siege 安装目录下的etc/urls.txt
-u URL,-url=URL 测试指定的一个URL,对它进行”siege “,此选项会忽略有关urls文件的设定

1
2
3
4
5
6
7
8
9
10
11
12
Transactions:		          62 hits
Availability: 98.41 %
Elapsed time: 16.35 secs
Data transferred: 0.65 MB
Response time: 3.72 secs
Transaction rate: 3.79 trans/sec
Throughput: 0.04 MB/sec
Concurrency: 14.09
Successful transactions: 62
Failed transactions: 1
Longest transaction: 15.53
Shortest transaction: 0.00

测试结果参数说明:

Transactions: 28759 hits #完成28759次处理
Availability: 94.97 % #94.97 % 成功率
Elapsed time: 33.58 secs #耗时33.58秒
Data transferred: 46.84 MB #传输数据46.84M
Response time: 0.04 secs #响应时间0.04秒
Transaction rate: 856.43 trans/sec #平均每秒完成856.43次处理,也就是QPS
Throughput: 1.40 MB/sec #平均每秒传送数据
Concurrency: 35.48 #实际最高并发连接数
Successful transactions: 28759 #成功处理次数
Failed transactions: 1523 #失败处理次数
Longest transaction: 7.05 #请求响应最长时间
Shortest transaction: 0.00 #请求响应最短时间

日常开发中总会遇到一些下载文件,然后通过程序处理文件内容的功能,因此也会遇到,读取后的内容不能达到我们的要求,比如从csv文件中读取到的中文是乱码,处理方式如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function getFileContent($file) {

$content = '';
$text = file_get_contents($file);

//$encodType = mb_detect_encoding($text);
define('UTF32_BIG_ENDIAN_BOM', chr(0x00) . chr(0x00) . chr(0xFE) . chr(0xFF));
define('UTF32_LITTLE_ENDIAN_BOM', chr(0xFF) . chr(0xFE) . chr(0x00) . chr(0x00));
define('UTF16_BIG_ENDIAN_BOM', chr(0xFE) . chr(0xFF));
define('UTF16_LITTLE_ENDIAN_BOM', chr(0xFF) . chr(0xFE));
define('UTF8_BOM', chr(0xEF) . chr(0xBB) . chr(0xBF));
$first2 = substr($text, 0, 2);
$first3 = substr($text, 0, 3);
$first4 = substr($text, 0, 3);
$encodType = "";
if (UTF8_BOM == $first3) {
$encodType = 'UTF-8 BOM';
} else if (UTF32_BIG_ENDIAN_BOM == $first4) {
$encodType = 'UTF-32BE';
} else if (UTF32_LITTLE_ENDIAN_BOM == $first4) {
$encodType = 'UTF-32LE';
} else if (UTF16_BIG_ENDIAN_BOM == $first2) {
$encodType = 'UTF-16BE';
} else if (UTF16_LITTLE_ENDIAN_BOM == $first2) {
$encodType = 'UTF-16LE';
}

//下面的判断主要还是判断ANSI编码的·
if ('' == $encodType) {
//即默认创建的txt文本-ANSI编码的
$content = iconv("GBK", "UTF-8", $text);
} else if ('UTF-8 BOM' == $encodType) {
//本来就是UTF-8不用转换
$content = $text;
} else {
//其他的格式都转化为UTF-8就可以了
$content = iconv($encodType, "UTF-8", $text);
}

return $content;
}

输入文件的路径,获取文件正确的中文内容,此函数适合于csv文件,其他文件雷同。

1
2
$file = '/dd/ddd/ddd/ddd.csv';
getFileContent($file);

这里说的直接使用Reactjs,指的是不使用任何打包工具,直接在代码中引入,这种方式类似于在html页面中直接引入javascript脚本文件,不过现在的前端开发基本也不建议使用类似的这种方式,而是喜欢建改代码工程化,包括一些打包发布之类的。比如使用webpack等类似的打包工具,可以包括压缩、分包各种优化放在里面。直接引入的方式就是我们自己写的代码,直接上线使用了,不包括什么压缩、分包之类的,所以我也不推荐直接引入的方式,除非引入的是打包好的,并进行了优化的,方便前端进行加载。

Reactjs安装

1
2
3
<script src="/static/js/@babel/[email protected]/babel.min.js"></script>
<script src="/static/js/[email protected]/umd/react.production.min.js" crossorigin></script>
<script src="/static/js/[email protected]/umd/react-dom.production.min.js" crossorigin></script>

这里使用了babel是为了对jsx语法进行转义,将react语法转义为es5通用语法。同时也可以将一些es6语法进行部分转义,为什么说是部分,因为在遇到async/await的时候,并没有进行转义,这个问题有待解决。我试过绑定plugins的方式,但是结果都不是很理想。

Fetch Api安装

1
<script src="/static/js/3.0.0/fetch.umd.js" crossorigin></script>

这里添加这个fetch的api,主要是为了替换类似jquery值之类的ajax,当然你也可以使用。不过需要谨慎“异步”的逻辑

添加jsx代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
<script type="text/babel">

// store
const Store = React.createContext();

function StoreProvider(props) {
const [state, dispatch] = React.useReducer(reducer, initialState);
const value = { state, dispatch };
return <Store.Provider value={value}>{props.children}</Store.Provider>
}

// reducer
const initialState = {
renderData: {!! json_encode($renderData) !!}, // server端直接进渲染的变量
};

function reducer (state, action) {
switch(action.type) {
case 'FETCH_DATA':
return { ...state, films: action.payload };
default:
return state;
}
}

// view
function MainContainer () {
const {state, dispatch} = React.useContext(Store);

const checkPoint = (data) => {
return fetch('/xxx/xxx/xxx', {
body: JSON.stringify(data),
headers: {
'Content-Type': 'application/json',
},
credentials: "same-origin",
method: 'POST',
});
}

const exchangeHandle = (id) => {
checkPoint({id:id}).then(data => {
data.json().then(o => {
console.log(o);

if (!o.success) {
throw new Error(o.message);
}

window.location.href = '/xxx/xxx/xxx/'+id;
}).catch(e => {
console.log(e);
toast({
'message': e.message
});
});
}).catch(e => {
console.log(e);
toast({
'message': '网络异常'
});
});
}

const pointHandle = () => {
window.location.href = '/xxx/xxx/xxx';
}

const ruleLinkHandle = () => {
window.location.href = state.renderData.ruleLink;
}

React.useEffect(() => {
// 页面加载后的逻辑

});

console.log(state);

let levelComponent = (<span></span>);
if (state.renderData.level == 1) {
levelComponent = (<img src='/images/xxx/xxx/lv1.png' className='level-logo' />);
} else if (state.renderData.level == 2) {
levelComponent = (<img src='/images/xxx/xxx/lv2.png' className='level-logo' />);
}

return (
<React.Fragment>
<div className='main-container'>
<div className="bg-container">
<img src="/images/xxx/xxx/bg.png" className="bg-img"/>
</div>
<div className="content-1-container">
<span className="level-container">
{levelComponent}
</span>
<span className="point-container" onClick={ruleLinkHandle}>等级与积分说明<img src='/images/xxx/xxx/help_icon.png' className='rule-img' /></span>
</div>
<div className="content-2-container">
<span className="point-2-container">{state.renderData.point}</span>
</div>
<div className="content-3-container">
<span className="point-3-container" onClick={pointHandle}>我的积分 <img src='/images/xxx/xxx/point-arrow.png' /></span>
</div>
<div className="line-container"></div>
<div className="goods-container">
<div className="goods-title">积分商城</div>
<div className="goods-item">
{
state.renderData.goods_item.map((v, k) => {
return (
<div className="goods-row" key={v.goods_id}>
<div className="goods-content-container">
<div className="img"><img src={v.icon} className='img-1' /></div>
<div className="title">{v.name}</div>
<div className="price">
<span>{v.point}积分</span><span className="money">¥{v.original_price}</span>
</div>
<div className="btn">
<span className="btn-ele" onClick={() => exchangeHandle(v.goods_id)}>立即兑换</span>
</div>
</div>
</div>
);
})
}
</div>
</div>
</div>
</React.Fragment>
)
}

const domContainer = document.querySelector('#root');

ReactDOM.render(
<StoreProvider>
<MainContainer />
</StoreProvider>,
domContainer
);
</script>

演示的代码稍微有点多,不过确实是使用了本人在开发的项目中的代码,并没有做太多删减,当然这里也只是做了一个例子,这个代码并不能直接copy后直接使用的。

另外,你也许会觉得这个代码放到一个文件中会比较好,是的,那么在说下引入的方式,比如我在实际项目中使用了tost,那我自己就写了一个toast,但是这个toast是在所有页面展示的时候都能统一样式,所以需要抽出来放到一个文件,然后可以在任意页面调用,于是这个文件可以这样引用,

1
<script type="text/babel" src="/js/xxx/xxx/toast.jsx"></script>

这个文件的代码大致如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// store
const Store = React.createContext();

function StoreProvider(props) {
const [state, dispatch] = React.useReducer(reducer, initialState);
const value = { state, dispatch };
return <Store.Provider value={value}>{props.children}</Store.Provider>
}

// reducer
const initialState = {
show: true
};

function reducer (state, action) {
switch(action.type) {
case 'FETCH_DATA':
return { ...state, films: action.payload };
default:
return state;
}
}

function Toast(props) {
return (
<React.Fragment>
<div className='toast-container'>
<div className='toast-text'>
<img src="/images/xxx/xxx/emijo_icon_1.png" />
<p>{props.message}</p>
</div>
</div>
</React.Fragment>
);
}

const toastContainer = [];

function toast(properties) {
const { ...props } = properties || {};

const divEle = document.createElement('div');

if (toastContainer.length > 0) {
return false;
}
const t = setTimeout(() => {
document.body.removeChild(divEle);

clearTimeout(t);
toastContainer.pop();
}, props.timeout || 2000);

document.body.appendChild(divEle);
toastContainer.push(1);

ReactDOM.render(<StoreProvider><Toast {...props} /></StoreProvider>, divEle);
}

在其他页面中可以直接调用方法toast({message: '我是一个提示'})

之所以记录这个Reactjs如何在不安装打包工具的情况下直接使用,是因为安装一个打包工具在取做配置然后在开发还是蛮麻烦的,但是要开发的功能其实并没有多么复杂。可以临时这样用一下。实际上在使用的过程中,如果遇到太复杂的功能建议不这样用,因为babel将jsx语法进行转义的时候,是要花费时间的。

阿里云 Composer 全量镜像(推荐)

镜像类型:全量镜像

更新时间:1 分钟

镜像地址:https://mirrors.aliyun.com/composer/

官方地址:https://developer.aliyun.com/composer

镜像说明:阿里云 CDN 加速,更新速度快,推荐使用

安畅网络镜像

镜像类型:全量镜像

更新时间:1 分钟

镜像地址:https://php.cnpkg.org

官方地址:https://php.cnpkg.org/

镜像说明:此 Composer 镜像由安畅网络赞助,目前支持元数据、下载包全量代理。

交通大学镜像

镜像类型:非全量镜像

镜像地址:https://packagist.mirrors.sjtug.sjtu.edu.cn

官方地址:https://packagist.mirrors.sjtug.sjtu.edu.cn/

镜像说明:上海交通大学提供的 composer 镜像,稳定、快速、现代的镜像服务,推荐使用。

全局配置(推荐)

所有项目都会使用该镜像地址:

1
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

取消配置:

1
composer config -g --unset repos.packagist

项目配置

仅修改当前工程配置,仅当前工程可使用该镜像地址:

1
composer config repo.packagist composer https://mirrors.aliyun.com/composer/

取消配置:

1
composer config --unset repos.packagist

调试

composer 命令增加 -vvv 可输出详细的信息,命令如下:

1
composer -vvv require alibabacloud/sdk

忽略版本匹配

composer可以设置忽略版本匹配,命令是:

1
composer install --ignore-platform-reqs

或者

1
composer update --ignore-platform-reqs

再次执行composer命令可以正常安装包了

参考网址:https://learnku.com/composer/wikis/30594

在使用laravel中的时候,会遇到将扩展包直接加载到现有项目中,而且在项目提交中的时候忘记将composer.lock提交,其中个别原因肯定很多,多数是由于项目管理不善导致的,但是问题已经出来了,何必再去纠结,找到办法解决再谈后话。

下面是我的解决思路

1、将本地的composer.lock删除
2、composer -vvv install
注意,如果你的很慢的话,请适用下阿里云的的库吧

https://mirrors.aliyun.com/composer/

建议来个全局配置吧

1
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/

你也许会想到我想换回原来的怎么办 或者不想用怎么办

1
composer config -g --unset repos.packagist

这个命令可以解决

之后会提示你需要一个mongodb的扩展,好烦哦

3、MongoDB 扩展安装

官方说明
https://php.net/manual/en/mongodb.installation.php

mac下操作

1
brew install homebrew/php/php71-mongodb

不要意思 此命令已经被废弃了

2018-03-31 起弃用 homebrew/php

以后安装php扩展请用如下命令

1
pecl install mongodb

如果你的电脑不是同构brew安装的php请自行下载对应的版本扩展进行安装吧

1
2
3
4
Build process completed successfully
Installing '/usr/local/Cellar/[email protected]/7.1.16/pecl/20160303/mongodb.so'
install ok: channel://pecl.php.net/mongodb-1.5.5
Extension mongodb enabled in php.ini

这个是我的安装成功的结果截图

然后在执行以下

1
composer -vvv install 

问题得到解决

0%