Write the Code. Change the World.

5月 06

一切都是在发展,一切都是在进步。一切是如此,产品也是如此。 如果还停留在过去,产品的体验(服务、外观、交互、程序自身)就会变差,因为有了对比。这个时候,重构就很有必要了。
我是喜欢求新,求干净,愿意去学,愿意去使用。那么,开始选型和重来吧。

旧的技术栈

服务器:阿里g7 2核8G 6M 带宽 centos8 操作系统
服务器环境:php7.4 + nginx + mysql 8 + redis + …
服务端语言:php
服务端框架:laravel 8
后台框架: vue-element-admin (vue2)
网页版:blade + bootstrap 4
前端框架:uniapp + vue2
上架发布:微信小程序 + 苹果商店
辅助工具:postman + charles

将用的技术栈

服务器:阿里g7 2核8G 5M 带宽 Alibaba Cloud Linux release 3 操作系统
服务器环境:php8.2 + nginx + mysql 8 + redis + …
服务端语言:php
服务端框架:laravel 10
后台框架: 在选择中…
网页版:blade + bootstrap 5(当然也可以选择 Inertia )
前端框架:uniapp + vue3
上架发布:微信小程序 + 百度小程序 + 字节小程序 + 苹果商店
辅助工具:postman + charles

曾经也同时上架过百度小程序和微信小程序。现在要增加到发布四端了。如果用户用起来,web 端会做的更好,还会发布 android 端。上边那四端是一定要发的。

后话

看似改变不大,其实很多地方都变了。包括一些写法。有云,条条大路通罗马,总有那么几条,那么一条是最好的。所以在选择的问题和使用的问题上很重要。

为什么还用 php。目前,服务端语言就 php 熟练一些。go 和 node.js 虽然使用过,也仅仅是使用过。太久没碰,也差不多丢了。 java 也能看懂一些,还是不够做一个完整的项目。php 有 laravel 框架,足够可以做很多很多事情了。

为什么用 uniapp 。uniapp 的确是个好东西。国内很多公司很多人都在用。一套代码,可以发布多个平台(多家小程序 + app + h5 + 快应用),而且支持 vue 的写法。如果没有特殊要求的 app,用 uniapp 来开发绝对是一个又快又好用的工具。如果有特殊需要,可以用 flutter。想当初,用 flutter 也干出个 app 出来。后来出了 dart2.0,更好了。

还有,如果有需要,可以开发桌面应用,比如使用 Electron,还有 Tauri。如果这两个都没用过,估计会选择 Tauri 吧。

为什么用 vue3。vue3 在性能和写法,还有一些新的功能特性上,都比 vue2 要强。有新的版本,为什么不用呢。总得前进吧。为什么不用 react 呢。用的东西太多太杂了,都记不住了。先不学 react 和 用它吧。

阅读全文 >>

5月 05

vue3 出来那么久了。以前的项目 vue2 也该更更更了。新项目那必须得 vue3 了。不过,有些关键的写法,会有些不同。一点点的来。

globalProperties

在 vue2,想定义一个全局的属性(方法或变量),只需要定义在 Vue.prototype 上就可以。然后在 template 中可以直接使用,在 script 中,通过 this 来访问使用。

到了 vue3 这里,就不一样了。这些方法和属性定义在 globalProperties, 就是 app.config.globalProperties 上。

比如:

import App from './App'

import {
    createSSRApp
} from 'vue'

const app = createSSRApp(App)

app.config.globalProperties.$test = (value) => {
    console.log('test ' + value)
}

vue3 是干掉 this 的。使用的时候这样。

import { onMounted, getCurrentInstance } from 'vue'

onMounted(() => {
    const { proxy,  ctx } = getCurrentInstance()
    proxy.$test('全局调用')
})

template 中,则可以直接使用。 getCurrentInstance 还有一个输出 ctx,这个和 vue2 时候的 this 一个样。

阅读全文 >>

5月 04

laravel orm 的多态有的时候是真的好用。比如,我们在设计一个消息表的时候,这个多态就很有用。消息对于整个项目,是一个基础的统称。比如,有人注册账号了,给他一个欢迎的消息。 有人充值了一个会员,给他一个充值消息。有人参加了一个活动,给他一个参加的消息。就这样一个场景。在消息表里边,我们仅仅需要构建这些字段 user_id、content、messageable_id、messageable_type、status、updated_at、created_at 就可以了。是不是很干净,是不是很舒适。后端的东西往往是给前端服务的。我们要会联想到前端的一个场景。有一个消息列表,我们点击消息,应该可以查看到消息的详情。比如会员消息详情、充值详情(当然有的时候是不需要详情,比如欢迎语。)这个时候,多态一对一在这个场景就很符合。这个多态和面向对象里的多态(继承、接口)不一样,但有相似的意境。

开始

多态的重要信息是 xxxable_id、和 xxxable_type。一个对应的是目标对象的 id,一个对应的是目标对象的类名(比如 App\Models\Topic)。

在 xxx 模型中。我们要定义一个 xxxable 方法。这里以 Message 模型为例。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\MorphTo;

class Message extends Model
{
    use HasFactory;

    protected $fillable = ['user_id', 'content', 'messageable_id', 'messageable_type'];

    public function user()
    {
        return $this->belongsTo(User::class);
    }

    /**
     * 获取父级的对象
     */
    public function messageable(): MorphTo
    {
        return $this->morphTo();
    }
}

在父级对象中,你也可以定义相对应的关系。当然也可以不定义。在这个场景中,我们重要的是消息列表以及消息对应的对象。所以仅仅在消息中定义 morph 就好。

使用

比如: Message::query()->take(10)->with('messageable')->get();

一个完整的过程。我们有这三个模型:Message(消息)、Topic(话题)、Member(会员)。

$user = $request->user();
$data = [xxx];
$topic = Topic::create($data);

# 获取类名
$topicClass = get_class($topic);

# 创建消息
Message::create([
    'user_id' => $user->id,
    'content' => '您的话题已创建,请等待审核',
    'messageable_id' => $topic->id,
    'messageable_type' => $topicClass
 ]);

---------------------------------------
$member = Member::create($data);

Message::create([
    'user_id' => $user->id,
    'content' => '您已成为会员,请愉快的玩耍吧',
    'messageable_id' => $member->id,
    'messageable_type' => get_class($member)
 ]);

再啰嗦一点点

上边说了,前端是要点击消息,做对应的跳转的。既然后端是动态绑定(多态算是动态的一种绑定吧),那么前端就是一个强制的硬性的绑定了,可以理解为是枚举。可以用 if 来搞定。因为消息的种类就那么多,不像数据库,有成千上万百万千万的数据。

if (消息类型一) {
    跳转到一
} else if (消息类型二) {
    跳转到二
}

因为有了 messageable_id,我们就可以通过接口来请求各自所想要的信息了。

对于消息列表。如果没有特殊的要求 messageable 我们也是不需要在这里就请求上的。因为请求了,就需要数据库去查询。查询了又不用,那不是浪费。这里看具体情况哈。

中文文档

https://learnku.com/docs/laravel/10.x/eloquent-relationshipsmd/14889#5981a6

阅读全文 >>

5月 01

虽然有 docker , 但还是喜欢用 homestead 开发环境。这个时候,一些旧的项目可能会用到比较低版本的 php,这个时候切换 php 版本就是一个常用的操作。

使用 php 版本

homestead 环境中,项目使用 php 的哪个版本,可以在 Homestead.yaml 文件中进行配置。比如:

sites:
    - map: work.xiangrong.com
      to: /home/vagrant/code/zhoulin/work.xiangrong.com/public
      php: "7.4"

这里指定好 php 版本就好。

但是,当 homestead 环境中当前使用的 php 版本是 php 8php 5.6 这些。使用 composer 或项目本身就有可能报错。这个时候,我们就需要将 homestead 环境的 php 版本切换的和项目所配置的一样。

补充

如果切换成对应的 php 版本还会出 badgateway 这样的错误,可以试着重启 php 看看。

# 这里以 php 7.4 为例
sudo /etc/init.d/php7.4-fpm restart

操作

# 查看当前环境中的 php 版本
php -v

# 查看所有 php 版本和当前版本
sudo update-alternatives --display php

# 执行后,会列出当前 php 所有版本和编号,输入编号,切换到执行的版本
sudo update-alternatives --config php

其实

其实,想切换到某个 php 版本。直接输入就可以了,也不用向上边那样敲那么长的命令。 比如,想切换到 php7.4 版本。直接输入 php74 回车即可。同理,想切换到 php8.2 版本,直接输入 php82 回车即可。这些命令,可以在 Homestead 目录下的 aliases 文件中找到。

function php73() {
    sudo update-alternatives --set php /usr/bin/php7.3
    sudo update-alternatives --set php-config /usr/bin/php-config7.3
    sudo update-alternatives --set phpize /usr/bin/phpize7.3
}

function php74() {
    sudo update-alternatives --set php /usr/bin/php7.4
    sudo update-alternatives --set php-config /usr/bin/php-config7.4
    sudo update-alternatives --set phpize /usr/bin/phpize7.4
}

function php80() {
    sudo update-alternatives --set php /usr/bin/php8.0
    sudo update-alternatives --set php-config /usr/bin/php-config8.0
    sudo update-alternatives --set phpize /usr/bin/phpize8.0
}

function php81() {
    sudo update-alternatives --set php /usr/bin/php8.1
    sudo update-alternatives --set php-config /usr/bin/php-config8.1
    sudo update-alternatives --set phpize /usr/bin/phpize8.1
}

function php82() {
    sudo update-alternatives --set php /usr/bin/php8.2
    sudo update-alternatives --set php-config /usr/bin/php-config8.2
    sudo update-alternatives --set phpize /usr/bin/phpize8.2
}

今天五月一日。

阅读全文 >>

4月 16

在项目中,我们通常会用到 mysql、redis 数据库。有时候再额外使用 sqlite 数据库也不错。 那就开搞。

centos 一般会默认安装了 sqlite 数据库。只是版本都比较落后。

# 查看 sqlite3 的版本
sqlite3 --version

# 如果没有安装那就更好。如果安装了,就先卸载这玩意

yum remove sqlite3

安装最新版本的 sqlite3

https://www.sqlite.org/download.html

官网列出了 sqlite3 的下载地址。我们选择最新的版本地址,是包含 includes a "configure" script 的版本。当前最新版本是 3.41.2。

开始安装吧。

# 喜欢将一些需要编译安装的包放在这里
cd /usr/local/src/service

wget https://www.sqlite.org/2023/sqlite-autoconf-3410200.tar.gz

tar -xzvf sqlite-autoconf-3410200.tar.gz

cd sqlite-autoconf-3410200

./configure --prefix=/usr/local

make && make install

# 建立软链
ln -s /usr/local/bin/sqlite3 /usr/bin/sqlite3

# 查看版本
sqlite3 --version

到此 sqlite3 最新版本已经安装完成了。

阅读全文 >>

4月 15

css 是没有父元素选择器的。可是现在有伪选择器 has,这个可以实现父选择器的愿望。通过 has 这个词,也很容易记住和理解。就像一个反向思维一样,本来需要一个父级,那如果我父级就在那,我判断是否有子集不就通了吗。而 css 从来都是从父到子到兄的。

https://developer.mozilla.org/zh-CN/docs/Web/CSS/:has

为什么要搞这玩意呢。还是因为在使用 element-ui 的上传组件时,当用户选择了一张图片后,上传按钮还是显示在那地方。这样体验很差也不好看。即使你加了 limit:1 的控制也不行。不知道为啥会这样(我没找到对应的 api 还是就是这样呢)。

既然这样了,那总得处理。最直接的一种方式是通过 js + css 的方式。对于 html,只要 js + css 是没有搞不定的。可是这样代码不好看了啊,也增加了多余的逻辑。通过观察组件的 dom 结构,可以发现,不管是否有选择的图片,上传按钮的兄弟级存在一个 li标签。 只要选择了图片或默认存在的图片,都会存在一个
样式为 .el-upload-list__item的 div。那好,这个时候就想到,如果存在父选择器,再通过父选择器的兄弟选择,通过 display:none,不就可以隐藏了吗。
可是,可是就是没有父选择器啊。可是,可是 has 伪选择器就是可以实现这样一个场景就是父选择器。

下边是 element ui 的上传组件的结构(精简模拟的)

# 没选择图片的
<div class="upload-card">
    <li></li>
    <div class="upload-btn">

    </div>
</div>

# 选择图片的
<div  class="upload-card">
    <li>
    <div class="el-upload-list__item"></div>
    </li>
    <div class="upload-btn">

    </div>
</div>

通过 has 就可以实现隐藏了。

.upload-card:has(.el-upload-list__item) .upload-btn {
    display:none;
}
 ```

 ### 真实 code

 ```
 #template
 <el-form-item label="封面" prop="cover">
 <el-upload ref="coverUpload" class="upload-card" action="#" :multiple="false"  list-type="picture-card" :file-list="image.cover" :http-request="uploadCover" :auto-upload="false">
 <i class="el-icon-plus" />
 </el-upload>
</el-form-item>

# scss
::v-deep .upload-card:has(.el-upload-list__item) .el-upload--picture-card {
  display: none !important;
}

阅读全文 >>

4月 07

如果发现配置了定时任务,却发现没有执行到。可以先看看日志。

# 看日志
tail -f /var/log/cron

# 查看日志文件
ll /var/log/cron*

# 看哪些用户在使用定时任务
ls /var/spool/cron/

# 查看某个用户的任务详情。比如 nginx
more /var/spool/cron/nginx

# 开机自启动
systemctl enable crond

如果没有日志,请查看下边的方法

情况是这样的。本来是有日志的。如果把日志删除了,即使重启 crontab 服务也是没有日志的。还有一种就是以前根本就没打开日志。这两种情况下边的方法都可以搞定。还有说在 /etc/rsyslog.d 下建立 50-default.conf。那还不如下边的方法。 下边的方法是通过 chatGPT 查询到的。有时候还是很方便的。

要查看CentOS 8 crontab的日志,您可以按照以下步骤进行处理:

  1. 打开 /etc/rsyslog.conf 文件并找到以下行:

cron.* /var/log/cron

  1. 将上述行的注释符号(“#”)移除,以启用cron日志记录功能。保存并关闭该文件。

  2. 重新启动 rsyslog 服务,以确保更改生效:

$ sudo systemctl restart rsyslog

  1. 检查 /var/log/cron 文件,以查看生成的 cron 日志。

如果上述方法不起作用,则您可以尝试在 crontab 命令中将日志记录级别设置为更高的级别。例如,将以下行添加到 crontab 文件的顶部:

MAILTO=user@domain.com

          • /path/to/script > /dev/null 2>&1

在上述命令中,将 MAILTO 设置为您的邮件地址,以接收有关 crontab 任务运行情况的通知。将脚本路径替换为实际的脚本路径,并将输出重定向到 /dev/null,以避免在终端中看到任何输出。将 "> /dev/null 2>&1" 去掉就可以在/var/spool/mail用户文件夹下查看cron任务的执行情况了。

阅读全文 >>

3月 30

瀑布流是一种展现方式。无论 h5 还是小程序,在一定场景下会需要。可以通过把数据分组,再 flex-column 布局。也可以用过计算浮动来实现布局。这些搞起来,还是比较麻烦,而且代码不好看。现在微信小程序实现瀑布流都新的方式。还有吸顶等操作。

开始

https://developers.weixin.qq.com/community/develop/article/doc/000ae631788f400c9a7f151f65b013

https://developers.weixin.qq.com/community/develop/article/doc/000a088c5c471062bf0f0af1a5b813

文章尾部,有代码测试片段。打开,就可以尝试。不过,需要将渲染方式切换到 Skyline 。

切换操作看这里 https://developers.weixin.qq.com/miniprogram/dev/framework/runtime/skyline/migration/#%E5%BF%AB%E6%8D%B7%E5%88%87%E6%8D%A2%E5%85%A5%E5%8F%A3

计算的方式

https://juejin.cn/post/7066778804827619364

阅读全文 >>

3月 20

小白一枚,从前端开始,开发区块链项目。

需要知道的事情

  1. chrome 浏览器,先要安装 metaMask 插件。
  2. 代码中,检测 metaMask 插件是否已经安装,并是可行的状态。
    
    # 不需要任何引入,就是这样直接判断
    if (window.ethereum != null)  {

}

阅读全文 >>

3月 20

通过给 div 定义 id 属性,然后在 a 标签中 href 设置定义好的 id,用户点击 a 标签,页面就可以滑到目标位置。只是这样,点击的时候,url 会出现变化。这里了还有一种方法。

html 跳转到指定的位置

 const goView = function() {
    document.querySelector("#roll_user").scrollIntoView(true);
 }

这种,是通过 js 调用方法来实现。优点是 url 地址不变,还可以设置更多的参数。显得安静丝滑。缺点是,如果刷新浏览器,位置会变化。

当然,还可以通过 js 用其他方式实现这种结果。只是不划算。

阅读全文 >>