<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title><![CDATA[世纪红工作室]]></title> 
<atom:link href="https://blog.sjhong.net/rss.php" rel="self" type="application/rss+xml" />
<description><![CDATA[]]></description>
<link>https://blog.sjhong.net/</link>
<language>zh-cn</language>
<generator>www.emlog.net</generator>
<item>
    <title>nuxt和vue开发有什么区别</title>
    <link>https://blog.sjhong.net/?post=31</link>
    <description><![CDATA[<p>Nuxt.js与Vue.js开发的主要区别可以归纳为以下三点：<br />
1、开发模式，2、服务器端渲染，3、内置功能。 Nuxt.js是一个基于Vue.js的框架，专为服务端渲染（SSR）和静态站点生成而设计。虽然两者共享Vue.js的核心，但它们在开发模式、渲染方式和功能集成方面有所不同。以下将详细探讨这些区别。</p>
<p>Nuxt 是一个基于 Vue.js 的轻量级应用框架，可用来创建服务端渲染 (SSR) 应用，也可充当静态站点引擎生成静态站点应用，具有优雅的代码结构分层和热加载等特性。NUXT使用服务端渲染技术，可以获得更快的内容到达时间，无需等待所有的 JavaScript 都完成下载并执行，产生更好的用户体验。服务端渲染又称 SSR (Server Side Render) ，是指在服务端就完成页面的内容，而不是在客户端通过AJAX请求才获取数据</p>
<p>一、开发模式</p>
<hr />
<p>1、项目结构</p>
<p>Nuxt.js：</p>
<p>预定义的目录结构，包括pages、layouts、store等。<br />
强制性路由生成，基于文件系统自动创建路由。<br />
Vue.js：</p>
<p>自由的项目结构，开发者可以根据需求自定义。<br />
路由由开发者手动配置，使用vue-router进行管理。<br />
2、配置文件</p>
<p>Nuxt.js：</p>
<p>使用nuxt.config.js进行全局配置，简化了配置过程。<br />
Vue.js：</p>
<p>依赖于多个配置文件，如vue.config.js、webpack.config.js等，更加灵活但也更复杂。<br />
3、开发体验</p>
<p>Nuxt.js：</p>
<p>提供了开箱即用的功能，如自动路由、热重载等，提升了开发效率。<br />
Vue.js：</p>
<p>更加灵活，但需要手动配置和安装插件，适合有特定需求的项目。</p>
<p><strong>二、服务器端渲染</strong></p>
<hr />
<p>1、渲染方式</p>
<p>Nuxt.js：</p>
<p>支持服务端渲染（SSR）、静态站点生成（SSG）和单页应用（SPA）。<br />
SSR可以显著提升页面加载速度和SEO效果。<br />
Vue.js：</p>
<p>原生支持单页应用（SPA），但要实现SSR需要额外的配置和库，如使用vue-server-renderer。<br />
2、SEO优化</p>
<p>Nuxt.js：</p>
<p>SSR和SSG使得页面在首次加载时即包含完整内容，有助于搜索引擎抓取和索引。<br />
Vue.js：</p>
<p>单页应用在客户端渲染，初次加载时页面为空，需要通过JavaScript填充内容，不利于SEO。<br />
3、性能</p>
<p>Nuxt.js：</p>
<p>SSR可以减少客户端的计算量，提升页面初次加载速度，但增加了服务器的负载。<br />
Vue.js：</p>
<p>客户端渲染减少了服务器压力，但增加了客户端的计算量，可能导致初次加载时间较长。</p>
<p><strong>三、内置功能</strong></p>
<hr />
<p>1、状态管理</p>
<p>Nuxt.js：</p>
<p>默认集成了Vuex，提供集中式状态管理解决方案。<br />
Vue.js：</p>
<p>需要手动安装和配置Vuex，灵活性更高。<br />
2、路由</p>
<p>Nuxt.js：</p>
<p>基于文件系统自动生成，无需手动配置。<br />
Vue.js：</p>
<p>需要使用vue-router手动配置，灵活性更高但也更复杂。<br />
3、插件和模块</p>
<p>Nuxt.js：</p>
<p>提供丰富的官方插件和模块，如PWA、Auth、Axios等，简化了开发过程。<br />
Vue.js：</p>
<p>需要手动查找和配置插件和库，灵活性高但需要更多时间和精力。<br />
4、静态站点生成</p>
<p>Nuxt.js：</p>
<p>内置静态站点生成器，适合生成静态博客、文档网站等。<br />
Vue.js：</p>
<p>需要使用第三方工具如VuePress来生成静态站点。</p>
<p><strong>总结与建议</strong></p>
<hr />
<p>总结：</p>
<p>开发模式： Nuxt.js提供了更为规范和预定义的项目结构，适合快速开发；Vue.js灵活性更高，但需要更多配置。<br />
服务器端渲染： Nuxt.js天然支持SSR和SSG，提升SEO和性能；Vue.js主要针对SPA，SSR需要额外配置。<br />
内置功能： Nuxt.js集成了丰富的官方插件和模块，简化了开发过程；Vue.js则需要手动选择和配置。<br />
建议：</p>
<p>如果你的项目需要服务器端渲染或静态站点生成，Nuxt.js是一个非常好的选择。<br />
如果你更喜欢灵活的项目结构和配置，或者你的项目是一个单页应用，那么Vue.js可能更适合你。<br />
无论选择哪一个框架，都要根据项目的具体需求和团队的技术栈进行决策，确保选择最合适的工具来提升开发效率和项目质量。<br />
通过深入理解Nuxt.js和Vue.js的区别，开发者可以更好地选择适合自己项目的技术栈，从而提高开发效率，优化性能和SEO效果。希望本篇文章能够帮助你更好地理解这两个框架，并在实际项目中做出明智的选择。</p>
<p><strong>相关问答FAQs：</strong></p>
<hr />
<ol>
<li>Nuxt和Vue的基本区别是什么？</li>
</ol>
<p>Nuxt是一个基于Vue.js的通用应用框架，它提供了一些默认配置和约定，使得在开发Vue应用时更加简单和高效。Vue.js本身是一个轻量级的JavaScript框架，用于构建用户界面。Vue.js提供了可组合的组件、响应式数据绑定和虚拟DOM等功能，而Nuxt则在此基础上增加了路由、服务端渲染和静态站点生成等功能。</p>
<ol start="2">
<li>Nuxt和Vue在开发模式上有什么区别？</li>
</ol>
<p>在开发模式上，Nuxt和Vue有一些不同之处。Vue开发时，我们通常需要手动配置路由、状态管理和构建工具等方面的内容。而Nuxt则通过默认的配置和约定，自动为我们做了这些事情，让我们可以更专注于业务逻辑的实现。</p>
<p>另外，Nuxt还提供了一些开发工具，如自动重载、错误日志和性能分析等，以帮助开发者更方便地调试和优化应用程序。</p>
<ol start="3">
<li>Nuxt和Vue在部署和扩展方面有何区别？</li>
</ol>
<p>在部署和扩展方面，Nuxt和Vue也有一些区别。由于Nuxt支持服务端渲染和静态站点生成，因此我们可以将Nuxt应用直接部署到静态文件托管服务或CDN上，从而实现更高的性能和可扩展性。</p>
<p>而Vue.js应用通常是通过构建工具打包生成静态文件，然后部署到服务器上，通过服务器端渲染来实现首屏渲染。虽然也可以通过一些手段来优化性能和扩展性，但相比之下，Nuxt在部署和扩展方面更加简单和方便。</p>
<p>总的来说，Nuxt是建立在Vue.js之上的一个应用框架，它提供了更多的功能和便利，使得开发和部署Vue应用更加高效和简单。但对于一些简单的项目，使用Vue.js本身也是一个不错的选择。</p>]]></description>
    <pubDate>Sat, 17 Feb 2024 16:34:27 +0800</pubDate>
    <dc:creator>一行虎</dc:creator>
    <guid>https://blog.sjhong.net/?post=31</guid>
</item>
<item>
    <title>nuxt3中使用element-plus（集成element-plus）</title>
    <link>https://blog.sjhong.net/?post=30</link>
    <description><![CDATA[<p>一、安装依赖</p>
<pre><code class="language-json">sudo yarn add element-plus --save
sudo yarn add @element-plus/icons-vue
sudo yarn add  @element-plus/nuxt -D</code></pre>
<p>二、配置nuxt.config</p>
<pre><code class="language-json">export default defineNuxtConfig({
  ssr: true,
  devtools: { enabled: true },
  typescript: {
    shim: false,
  },
  modules: ['@element-plus/nuxt'],
  css: ['element-plus/dist/index.css'],
})
</code></pre>
<p>重启后便可以在项目中使用element-plus了</p>]]></description>
    <pubDate>Thu, 15 Feb 2024 11:17:01 +0800</pubDate>
    <dc:creator>一行虎</dc:creator>
    <guid>https://blog.sjhong.net/?post=30</guid>
</item>
<item>
    <title>Nuxt报错TypeError: Cannot read properties of null (reading &#039;length&#039;)</title>
    <link>https://blog.sjhong.net/?post=29</link>
    <description><![CDATA[<p>可能是某个数组属性不存在， 但是判断了他的长度，比如下方代码</p>
<pre><code class="language-json"> &lt;template v-if="arr.length"&gt;
&lt;div v-for="(item,idx)in arr" :key="idx"&gt; {{ item }}&lt;/div&gt;
&lt;/template&gt;</code></pre>
<p>解决方法 :<br />
<strong>v-if=&quot;arr&amp;&amp;arr.length&quot;</strong></p>
<pre><code class="language-json">&lt;template v-if="arr&amp;&amp;arr.length"&gt;
&lt;div v-for="(item,idx)in arr" :key="idx"&gt; {{ item }}&lt;/div&gt;
&lt;/template&gt;</code></pre>]]></description>
    <pubDate>Wed, 14 Feb 2024 08:15:19 +0800</pubDate>
    <dc:creator>一行虎</dc:creator>
    <guid>https://blog.sjhong.net/?post=29</guid>
</item>
<item>
    <title>PM2 快速上手-Nuxt + pm2 部署 PC 商城</title>
    <link>https://blog.sjhong.net/?post=28</link>
    <description><![CDATA[<p>PM2是 Node.js 的优秀运行时管理工具，专为简化和优化 Node.js 应用程序的生产部署与运行而设计。</p>
<p>PM2 官网链接: <a href="https://pm2.keymetrics.io/">https://pm2.keymetrics.io/</a></p>
<p>1.PM2 的优势<br />
持续运行：即使应用出错或崩溃，也能自动重启。<br />
负载均衡：智能地自动分配入站连接到多个应用实例。<br />
日志集中管理：一处集中显示所有应用的日志。<br />
实时监控：实时查看所有应用的状态及细节性能指标。</p>
<ol start="2">
<li>安装 PM2<br />
选择以下任意一种方法全局安装 PM2：
<pre><code class="language-json">yarn global add pm2</code></pre>
<p>或者</p>
<pre><code class="language-json">npm install pm2 -g</code></pre></li>
<li>PM2 常用命令<br />
启动： pm2 start app.js- 启动并保护您的应用。<br />
查看列表： pm2 list- 显示所有 PM2 托管的应用列表。<br />
监控应用： pm2 monit- 查看应用的资源使用和日志。<br />
查看日志： pm2 logs- 获取应用的实时日志。<br />
重启应用： pm2 restart <appName><br />
停止应用： pm2 stop <appName><br />
移除应用： pm2 delete <appName></li>
</ol>
<p>🚀 修改 package.json<br />
<a href="https://sjhong.net/content/uploadfile/202501/a02d1736089573.png"><img src="https://sjhong.net/content/uploadfile/202501/thum-a02d1736089573.png" alt="" /></a></p>
<p>如上图在 script 标签中添加</p>
<pre><code class="language-json">"pm2": "pm2 start ./ecosystem.config.cjs --interpreter babel-node --env production",
"bundle": "rollup -c"</code></pre>
<p>4、配置pm2启动文件<br />
🔧 在根目录创建 ecosystem.config.cjs 内容如下</p>
<p>内容如下:</p>
<pre><code class="language-json">// 配置pm2
module.exports = {
    apps: [
        {
            name: "JavaMerPcMall",
            script: "./.output/server/index.mjs", // 其实使用npm去启动了./node_modules/nuxt/bin/nuxt这个文件。当我们cd到我们的项目目录之后，我们最终可以执行如下命令来启动：
            args: "start", // 传递给脚本的参数
            watch: true, // 开启监听文件变动重启
            ignore_watch: ["node_modules", "public", "logs"], // 不用监听的文件
            exec_mode: "fork",// 自家主机window cluster_mode 模式下启动失败
            instances: "1", // max表示最大的 应用启动实例个数，仅在 cluster 模式有效 默认为 fork
            autorestart: true, // 默认为 true, 发生异常的情况下自动重启
            max_memory_restart: "1G", //  // 最大内存限制数，超出自动重启
            error_file: './logs/app-err.log', // 错误日志文件
            // out_file: './logs/app-out.log', // 正常日志文件
            merge_logs: true, // 设置追加日志而不是新建日志
            log_date_format: "YYYY-MM-DD HH:mm:ss", // 指定日志文件的时间格式
            min_uptime: "60s", // 应用运行少于时间被认为是异常启动
            env: {
                // 环境参数，当前指定为开发环境
                NODE_ENV: "development",
                PORT: "8888",
            },
            env_production: {
                // 环境参数,当前指定为生产环境
                NODE_ENV: "production", //使用production模式 pm2 start ecosystem.config.js --env production
                PORT: "60001",
            },
            env_test: {
                // 环境参数,当前为测试环境
                NODE_ENV: "test",
            }
        }
    ]
};</code></pre>
<p>pm2进程列表</p>
<pre><code class="language-json">pm2 list</code></pre>
<p>pm2重启进程</p>
<pre><code class="language-json">pm2 restart [app-name]

pm2 restart all</code></pre>
<p>pm2停止进程</p>
<pre><code class="language-json">pm2 stop [app-name]

pm2 stop all</code></pre>
<p>pm2的进程日志</p>
<pre><code class="language-json">pm2 logs

pm2 logs [app-name]</code></pre>
<p>如下图项目启动成功<br />
<a href="https://sjhong.net/content/uploadfile/202501/34671736089833.png"><img src="https://sjhong.net/content/uploadfile/202501/thum-34671736089833.png" alt="" /></a></p>
<p>本地测试端口60001访问成功</p>]]></description>
    <pubDate>Fri, 05 Jan 2024 22:57:48 +0800</pubDate>
    <dc:creator>一行虎</dc:creator>
    <guid>https://blog.sjhong.net/?post=28</guid>
</item>
<item>
    <title>快速上手 Python 框架 FastAPI</title>
    <link>https://blog.sjhong.net/?post=27</link>
    <description><![CDATA[<p>FastAPI 是一种现代，快速（高性能）的 Web 框架，基于标准Python 类型提示使用 Python 3.6+ 构建 API。</p>
<h3>FastAPI 文档</h3>
<p>官方文档：<a href="https://fastapi.tiangolo.com/">https://fastapi.tiangolo.com/</a></p>
<h3>FastAPI 安装</h3>
<p>FastAPI 推荐使用 uvicorn 来运行服务，Uvicorn 是基于 uvloop 和 httptools 构建的闪电般快速的 ASGI 服务器。</p>
<h4>在终端中执行以下命令：</h4>
<pre><code class="language-python">pip install fastapi 
pip install uvicorn</code></pre>
<h3>FastAPI 实践</h3>
<h4>创建一个 .py 文件，并写以下代码</h4>
<pre><code class="language-python">from typing import Optional

from fastapi import FastAPI

app = FastAPI()

@app.get("/api/v1/hw")
def read_root():
    return {"Hello": "World"}

@app.get("api/v1/items/{item_id}")
def read_item(item_id: int):
    return {"item_id": item_id}</code></pre>]]></description>
    <pubDate>Thu, 23 Nov 2023 19:54:53 +0800</pubDate>
    <dc:creator>一行虎</dc:creator>
    <guid>https://blog.sjhong.net/?post=27</guid>
</item>
<item>
    <title>Python抽取分数超过60的学生</title>
    <link>https://blog.sjhong.net/?post=25</link>
    <description><![CDATA[<h5>print('1111')</h5>
<h5>示例数据</h5>
<p>students = [<br />
{'name': 'Alice', 'score': 78},<br />
{'name': 'Bob', 'score': 56},<br />
{'name': 'Charlie', 'score': 84},<br />
{'name': 'Dave', 'score': 92}<br />
]</p>
<h5>这个例子中，filter_students_by_score 函数通过列表推导式（list comprehension）筛选出那些分数超过60的学生记录。</h5>
<h5>抽取分数超过60的学生</h5>
<p>def filter_students_by_score(data):<br />
return [student for student in data if student['score'] &gt; 60]</p>
<p>filtered_students = filter_students_by_score(students)</p>
<h5>打印结果</h5>
<p>for student in filtered_students:<br />
print(student)</p>]]></description>
    <pubDate>Sun, 12 Nov 2023 18:39:35 +0800</pubDate>
    <dc:creator>一行虎</dc:creator>
    <guid>https://blog.sjhong.net/?post=25</guid>
</item>
<item>
    <title>Python-生成固定时间且间隔为1分随机测试数据Json实例</title>
    <link>https://blog.sjhong.net/?post=24</link>
    <description><![CDATA[<h3>This is a sample Python script.</h3>
<pre><code class="language-Python">import random
from datetime import datetime, timedelta
import json

if __name__ == '__main__':
    print_hi('PyCharm')
#### See PyCharm help at https://www.jetbrains.com/help/pycharm/

### 定义开始和结束时间
start_time = datetime(2024, 9, 9, 14, 00)  # 2024年3月1日 8:30
end_time = datetime(2024, 9, 9, 14, 30)  # 2024年3月1日 9:30

### 时间间隔设置为1分
interval = timedelta(minutes=1)

### 生成时间列表
time_list = [start_time + i * interval for i in range((end_time - start_time) // interval)]

print(f"随机产生温度参数：")
for ind, time in enumerate(time_list):
    jsonList = {
        'batteryValue': "100",
        "recordDate": f'{time}',
        "longitude": "114.06455",
        "latitude": "22.54846",
        "sensorList": [
            {
                "sensorId": 1,
                "sensorType": "temperature",
                # 随机生成2.5-7.0之间的小数，保留两位小数 f"{random_number:.2f}"
                "sensorValue": f"{random.uniform(2.5, 7.0):.2f}"
            }
        ],
    }
    end = ''
    if ind &lt; len(time_list) - 1:
        print(json.dumps(jsonList) + ',')
    else:
        print(json.dumps(jsonList))
</code></pre>
<p>生成格式：</p>
<pre><code class="language-json">{"batteryValue": "100", "recordDate": "2024-09-09 14:00:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "4.50"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:01:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "3.92"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:02:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "2.96"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:03:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "3.18"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:04:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "4.17"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:05:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "3.14"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:06:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "3.88"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:07:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "3.12"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:08:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "2.77"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:09:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "4.34"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:10:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "2.78"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:11:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "4.88"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:12:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "2.79"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:13:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "3.99"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:14:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "4.51"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:15:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "6.85"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:16:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "6.66"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:17:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "3.87"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:18:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "5.65"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:19:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "3.67"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:20:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "6.75"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:21:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "5.25"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:22:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "5.13"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:23:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "3.13"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:24:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "5.72"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:25:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "5.15"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:26:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "4.02"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:27:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "2.90"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:28:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "2.63"}]},
{"batteryValue": "100", "recordDate": "2024-09-09 14:29:00", "longitude": "114.06455", "latitude": "22.54846", "sensorList": [{"sensorId": 1, "sensorType": "temperature", "sensorValue": "2.94"}]}</code></pre>]]></description>
    <pubDate>Sun, 12 Nov 2023 17:40:18 +0800</pubDate>
    <dc:creator>一行虎</dc:creator>
    <guid>https://blog.sjhong.net/?post=24</guid>
</item>
<item>
    <title>‌link和@import在CSS引入方式上关键区别‌</title>
    <link>https://blog.sjhong.net/?post=23</link>
    <description><![CDATA[<h5>‌本质区别‌：</h5>
<p>‌link‌：link是HTML标签，不仅可以加载CSS文件，还可以定义RSS、rel连接属性等。<br />
‌@import‌：@import是CSS提供的语法规则，只能用于导入样式表，不能定义其他HTML属性‌。</p>
<h5>‌加载顺序‌：</h5>
<p>‌link‌：在页面加载时，link引入的CSS会同时被加载。<br />
‌@import‌：@import引入的CSS会在页面加载完毕后才加载，这可能导致页面在加载过程中出现样式未完全应用的情况，影响用户体验‌</p>
<h5>‌兼容性‌：</h5>
<p>‌link‌：作为HTML元素，link不存在兼容性问题，所有浏览器都支持。<br />
‌@import‌：@import是CSS2.1的语法，仅在IE5及更高版本中支持，较老的浏览器可能无法识别‌。</p>
<h5>‌DOM可控性‌：</h5>
<p>‌link‌：支持使用JavaScript操作DOM，可以通过JavaScript动态改变样式。<br />
‌@import‌：由于@import是基于CSS的语法，无法通过DOM方法插入样式‌。</p>
<h5>‌权重和性能‌：</h5>
<p>‌link‌：引入的样式权重高于@import引入的样式。<br />
‌性能方面‌：link方式可以同时加载CSS，而@import在页面加载完毕后才加载，可能会影响页面加载速度‌。</p>
<p>综上所述，link和@import在本质、加载顺序、兼容性、DOM可控性、权重和性能等方面都有显著区别，开发者应根据具体需求选择合适的方法。</p>]]></description>
    <pubDate>Sun, 12 Nov 2023 10:19:59 +0800</pubDate>
    <dc:creator>一行虎</dc:creator>
    <guid>https://blog.sjhong.net/?post=23</guid>
</item>
<item>
    <title>前后端交互规范</title>
    <link>https://blog.sjhong.net/?post=22</link>
    <description><![CDATA[<h3>一、协作流程规范</h3>
<hr />
<p>在团队协作中，每个人都会提交git commit message，但是由于每个人的开发风格和书写习惯的不同，导致git提交五花八门，不利于维护和阅读。一般来说，大厂都一套自己的代码提交规范的，尤其是在开源项目中，代码提交规范都是一致的。</p>
<p>1、需求分析。确保大家对需求有一致的认知<br />
2、设计接口文档。前端需要确认是否符合要求<br />
3、并行开发。前端需要根据接口文档进行Mock, 模拟对接后端接口；联调之前，要求后端做好接口测试<br />
4、真实环境联调。前端将接口请求代理到后端服务，进行真实环境联调。</p>
<h3>二、接口规范</h3>
<hr />
<p>2.1 接口风格：</p>
<p>RESTful —— 推荐<br />
JSONRPC<br />
2.1.1 RESTful</p>
<p>标准格式</p>
<p>http(s)://server.com/app-name/{version}/{domain}/{rest-convention}<br />
用法</p>
<p>GET<br />
GET /api/teams （对应团队列表）</p>
<p>GET /api/teams/123 （对应 ID 为 123 的团队）</p>
<p>GET /api/teams/123/members （对应 ID 为 123 的团队下的成员列表）</p>
<p>GET /api/teams/123/members/456 （对应 ID 为 123 的团队下 ID 为 456 的成员）</p>
<p>POST<br />
用于创建资源，或者特殊资源提交（如文件上传/下载）</p>
<p>POST /api/teams/123/members/456 （新增ID 为 123 的团队下 ID 为 456 的成员信息）</p>
<p>body参数{name：'xxx'}</p>
<p>POST /api/teams/123/members/456 （下载ID 为 123 的团队下 ID 为 456 的成员信息）</p>
<p>POST /api/teams/123/members/456 （上传ID 为 123 的团队下 ID 为 456 的成员信息）</p>
<p>body参数{file：formData}</p>
<p>UPDATE（PUT和PATCH）<br />
PUT 用于更新资源的全部信息，在请求的 body 中需要传入修改后的全部资源主体<br />
PATCH 用于局部更新，在 body 中只需要传入需要改动的资源字段。</p>
<p>POST /api/teams （创建团队）</p>
<p>POST /api/teams/123/members （创建团队ID 为 123 的成员）</p>
<p>PUT /api/teams/123/members/456 （更新ID 为 123 的团队下 ID 为 456 的成员全部信息）</p>
<p>PATCH /api/teams/123/members/456 （更新ID 为 123 的团队下 ID 为 456 的成员部分信息）</p>
<p>DELETE<br />
// 单个删除</p>
<p>DELETE /api/users/123</p>
<p>// 批量删除</p>
<p>DELETE /api/users</p>
<p>body参数{ids：[]}</p>
<p>HEAD<br />
获取一个资源的元数据，如数据的哈希值或最后的更新时间。</p>
<p>OPTIONS<br />
获取客户端能对资源做什么操作的信息</p>
<p>过滤<br />
?limit=10：指定返回记录的数量</p>
<p>?offset=10：指定返回记录的开始位置。</p>
<p>?page=2&amp;size=100：指定第几页，以及每页的记录数。</p>
<p>?sortby=name&amp;order=asc：指定返回结果按照哪个属性排序，以及排序顺序。</p>
<p>?keyname=1：指定筛选条件复制代码</p>
<p>其他规范：<br />
规则1：URI结尾不应包含（/）<br />
规则2：正斜杠分隔符（/）必须用来指示层级关系<br />
规则3：应使用连字符（-）来提高URI的可读性<br />
规则4：不得在URI中使用下划线（_）<br />
规则5：URI路径中全都使用小写字母</p>
<p>2.2 注意点：</p>
<p>明确数据类型。例如数字应为Int或Float类型，而非String类型<br />
明确空值的意义。比如在做更新操作时，空值是表示重置，还是忽略更新<br />
明确默认值。对象类型的返回空对象{}, 数组类型的返回空数组[]，字符串类型的返回空字符串&quot;&quot;，Number和Boolean类型的返回null<br />
对于大数字(如Java的long 类型，整数类型大于16位浮点数类型大于18位)，返回给前端时需要设置为字符串类型, 防止js发生溢出<br />
关于日期时间格式<br />
对于需要前端再次处理的日期值, 可以使用时间戳<br />
对于纯展示用的日期值, 推荐返回格式化文本, 例如: 2017年1月1日或2017-01-01 20:00:00<br />
对于日期时间需要排序时，返回时间戳或者格式化的包含毫秒数的文本<br />
建议同时返回时间戳的原始值(或 ISO 标准格式)和用于统一显示的格式化文本，例如: {&quot;createTime&quot;: 1543195480357, &quot;createTimeText&quot;: &quot;2018年11月26日&quot;}<br />
关于分页<br />
分页计数，从1开始<br />
避免滚动加载可能出现的重复数据，采用 lastId 分页方式<br />
图片URL<br />
图片的 URL 建议返回完整的 URL，协议为HTTP时可省略协议<br />
文件上传/下载<br />
文件上传限制默认最大为10M<br />
文件上传类型默认支持：<br />
文档类：doc、docx、xls、xlsx、ppt、pptx、pdf、txt<br />
图片类：jpg、jpeg、png、gif<br />
其他：zip、rar<br />
多文件上传不支持IE9及以下版本浏览器<br />
图片上传服务器前需先在前端压缩，默认控制在400万以下像素或2560*1440及以下分辨率<br />
响应避免冗余的嵌套<br />
接口版本化，保持向下兼容；接口不兼容时需升级版本<br />
建议使用Swagger等工具，避免人工维护可能导致的代码和文档不同步<br />
鉴权通过header实现<br />
同时给出 ID 字段和用于显示字段时，前端提交只提交ID字段<br />
2.3 接口文档规范</p>
<p>后端通过接口文档向前端暴露接口相关的信息。通常需要包含这些信息：</p>
<p>版本号<br />
文档描述<br />
服务的入口，例如协议、域名、基本路径<br />
测试服务器，可选<br />
简单使用示例<br />
安全和认证<br />
具体接口定义<br />
方法名称或者URI<br />
方法描述<br />
请求参数及其描述，必须说明类型(数据类型、是否可选等)<br />
响应参数及其描述, 必须说明类型(数据类型、是否可选等)<br />
可能的异常情况、错误代码、以及描述<br />
请求示例，可选<br />
2.3.1 Header</p>
<p>Content-Type<br />
application/json 默认<br />
application/x-www-form-urlencoded 表单提交/APP请求<br />
Authorization<br />
Basic XXXXXXXX<br />
Bearer XXXXXXXX<br />
Cookie<br />
openid = hash(appid+uid)<br />
Expires 过期时间，默认为0<br />
Referer 之前页面URL，跳转第三方地址需要此参数<br />
lang 多语言<br />
zh_cn<br />
zh_hk<br />
en_us<br />
2.3.2 请求参数</p>
<p>4种类型：<br />
1、cookie： 一般用于OAuth认证<br />
2、Request Header： 一般用于OAuth认证<br />
3、请求body数据<br />
4、地址栏参数： 详见【2.1.1过滤】</p>
<p>2.3.3 响应参数</p>
<p>response Demo</p>
<ul>
<li>
<p>表示不必须<br />
{</p>
<p>status: 200,               // 详见【status】</p>
<p>data: {</p>
<p>code: 10000,            // 返回码，详见【4.3.2 公共响应参数】</p>
<p>msg: '成功',            // * 返回码描述</p>
<p>sub_code: 'success',    // 明细返回码</p>
<p>sub_msg: '',            // * 明细返回码描述，显示给客户端用户【须语义化中文提示】，code不为10000时均需toast提示</p>
<p>sysMsg: '',             // * 提示，调试使用，可包括中英文数字及特殊符号</p>
<p>result: [],             // 返回的数据结果对象，可根据需要定义</p>
<p>page: {</p>
<pre><code>current: 1,           // * 当前页，从1开始

size: 10,             // * 每页记录数

total: 100            // * 总记录数</code></pre>
<p>}</p>
<p>},</p>
<p>message: '成功',           // 提示,显示给客户端用户【须语义化中文提示】</p>
<p>sysMessage: 'success'      // * 提示,调试使用,中英文都行</p>
</li>
</ul>
<p>}</p>
<p>2.4 转义与编码</p>
<p>编码规则：encodeURIComponent 与 decodeURIComponent</p>
<p>URI特殊字符需转义<br />
特殊字符及转义符号</p>
<p>特殊字符</p>
<p>转义后的符号</p>
<p>+</p>
<p>%2B</p>
<p>空格</p>
<p>%20</p>
<p>/</p>
<p>%2F</p>
<p>?</p>
<p>%3F</p>
<p>%</p>
<p>%25</p>
<p>#</p>
<p>%23</p>
<p>&amp;</p>
<p>%26</p>
<p>=</p>
<p>%3D</p>
<p>!</p>
<p>%21</p>
<p>第三方回调页面URI需反转义，如：支付宝支付成功回调页面</p>
<p>URI汉字需转为Unicode码<br />
2.5 加密</p>
<p>首选HTTPS，其中以下项目要求必须为HTTPS<br />
APP<br />
微信公众号和小程序<br />
使用第三方API（如海康、高德等）<br />
HTTP项目需要对关键敏感信息进行加密处理，HTTPS项目不做要求<br />
账号、密码<br />
账号、密码禁止明文传输，如admin等<br />
密码加密策略：<br />
方法一<br />
服务器端存储密码为Hash(Random-Salt + Hash(Constant-SALT + Pasword))<br />
客户端传输密码为Hash(Constant-SALT + Password)<br />
Random-Salt为用户的随机盐，每个用户均不同，服务器端收到客户端的hash密码后，在将其与用户随机盐一起Hash运算，从而将结果与数据库存储值相比较。<br />
优点：数据库存储的密码经过两层哈希，且其中包含了固定盐和用户随机盐，安全性较高。<br />
缺点：客户端传输的密码虽然经过了一层哈希，但是使用固定盐值，因此相同密码生成的结果均一致，容易收到重放攻击。<br />
方法二<br />
服务器端存储密码为Hash(Constant-SALT + Pasword)<br />
客户端传输密码为Hash(Random-Salt + Hash(Constant-SALT + Password))<br />
Random-Salt为每次进入登陆页面时生成的随机盐，此处主要是为了防止重放攻击，作用类似与nonce 服务器收到客户端上传的hash之后的密码，根据用户ID取出服务器存储的密码，并通过与之前返回客户端的Random-Salt（可存储于Session中）进行Hash运算，从而与客户端上传的密码进行比较。<br />
优点：客户端有效的防止了重放攻击。<br />
缺点：在前端代码中，固定盐值暴露，一旦后台数据库被攻破，用户密码信息将有危险。<br />
加密库：<br />
MD5<br />
crypto-js<br />
手机号、银行卡号、姓名等私密信息<br />
通用加密策略：<br />
Hash(Constant-SALT + Pasword)<br />
Random-Salt为每个应用or业务系统的随机盐，与其他细节加密字段进行Hash运算后再跳转。<br />
数字加密策略：<br />
转换进制，如：<br />
// 加密</p>
<p>const mask = (+phone).toString(28)</p>
<p>// 解密</p>
<p>const phone = parseInt(mask, 28)</p>
<p>手机号、银行卡号加密策略：<br />
方法一<br />
参考上文数字加密策略<br />
方法二<br />
参考上文通用加密策略<br />
姓名、邮箱、住址等加密策略：<br />
参考上文通用加密策略<br />
第三方跳转时URI加密<br />
加解密策略<br />
参考上文通用加密策略<br />
2.6 跨域</p>
<p>CORS 实现</p>
<h3>三、接口测试与模拟</h3>
<p>3.1 工具：</p>
<p>RESTful<br />
Swagger 这是最为接近上面理想模型的一个解决方案<br />
JSON Server 快速生成JSON mock服务器<br />
Easy Mock 可视化的、在线的接口mock服务<br />
Yapi接口平台 支持本地部署<br />
GraphQl<br />
GraphQL Faker<br />
graphql-tools<br />
模拟数据生成<br />
js 强大的模拟数据生成工具，支持Node和浏览器<br />
js 数据生成和模拟工具</p>
<h3>四、 附录</h3>
<p>4.1 公共状态码（status）</p>]]></description>
    <pubDate>Sat, 11 Nov 2023 14:15:21 +0800</pubDate>
    <dc:creator>一行虎</dc:creator>
    <guid>https://blog.sjhong.net/?post=22</guid>
</item>
<item>
    <title>git基本操作</title>
    <link>https://blog.sjhong.net/?post=21</link>
    <description><![CDATA[<h5>初始化仓库</h5>
<p>git init： 初始化一个新的 Git 仓库。</p>
<p>git init --bare： 初始化一个裸仓库（通常用于远程仓库）。</p>
<h5>配置用户信息</h5>
<p>git config --global user.name &quot;Your Name&quot;：设置全局用户名。<br />
git config --global user.email &quot;your.email@example.com&quot;：设置全局用户邮箱。</p>
<h5>分支管理</h5>
<p>git branch：列出所有本地分支。<br />
git branch <branch-name>：创建新分支。<br />
git checkout <branch-name>：切换到指定分支。</p>
<p>git checkout -b <branch-name>：新建分支并切换到该分支。<br />
git merge <branch-to-merge>：合并指定分支到当前分支。<br />
git branch -d <branch1>：删除分支branch1。</p>
<h5>远程仓库</h5>
<p>git remote add origin <url>：添加远程仓库。<br />
git fetch：获取远程仓库的变化。<br />
git pull：获取远程仓库的变化并合并到本地。</p>
<p>git pull <remote url> <remote branch>：拉取远程仓库的指定分支。</p>
<p>git push：推送本地的提交到远程仓库。</p>
<p>git push <remote url> <local branch>：本地分支推送到远程（远程无该分支会创建）。</p>
<h5>其他命令</h5>
<p>git log：查看提交历史。<br />
git diff：显示未暂存的差异。<br />
git stash：暂存当前工作目录的更改以便稍后恢复。<br />
git clone <repository-url>：克隆一个远程仓库到本地。</p>
<h5>查看状态</h5>
<p>git status：显示工作目录和暂存区的状态。</p>
<h5>添加文件</h5>
<p>git add <file>：将单个文件添加到暂存区。<br />
git add.：将所有改动添加到暂存区。</p>
<h5>忽略文件</h5>
<p>touch .gitignore： 创建一个 .gitignore 文件来指定不需要跟踪的文件模式。</p>
<h5>提交更改</h5>
<p>git commit -m &quot;Commit message&quot;: 提交暂存区的更改到仓库，并附带提交信息。</p>
<h5>常用</h5>
<p>git commit -m &quot;提交信息&quot;：提交已暂存的更改。</p>
<p>git commit -a -m &quot;提交信息&quot;：自动将所有已跟踪文件的更改添加到暂存区，并提交（不包括未跟踪的新文件）。</p>
<p>git commit --amend -m &quot;新的提交信息&quot;：可以修改最近一次提交的信息或者添加新的更改到最近一次提交中。</p>
<p>（commit作用于本地仓库，push将本地仓库的提交历史推送到远程仓库）</p>]]></description>
    <pubDate>Sat, 11 Nov 2023 14:06:03 +0800</pubDate>
    <dc:creator>一行虎</dc:creator>
    <guid>https://blog.sjhong.net/?post=21</guid>
</item></channel>
</rss>