把 URL encode 一下,然后在接收的地方 decode吧,应该是 nginx 转义了双斜杠
Q是盖E是轮,一个R教做人儿。
没有足够的数据
(゚∀゚ )
暂时没有任何数据
minororange 回答了问题 · 2020-12-21
把 URL encode 一下,然后在接收的地方 decode吧,应该是 nginx 转义了双斜杠
把 URL encode 一下,然后在接收的地方 decode吧,应该是 nginx 转义了双斜杠
关注 2 回答 1
minororange 赞了文章 · 2020-12-07
之前看了雪碧大佬的将 React 渲染到嵌入式液晶屏觉得很有意思,React能被渲染到嵌入式液晶屏,那Vue是不是也可以呢?所以本文我们要做的就是:
如标题所示,就是将Vue渲染到嵌入式液晶屏。这里使用的液晶屏是0.96 寸大128x64分辨率的SSD1306。要将Vue渲染到液晶屏,我们还需要一个桥梁,它必须具备控制液晶屏及运行代码的能力。而树莓派的硬件对接能力和可编程性天然就具备这个条件。最后一个问题来了,我们用什么技术来实现呢?
这里我选择了 Node.js。原因:
npm install
走天下。 🐶这个有趣的实践可拆分为这几个步骤:
Talk is cheap,Let's Go!!!
无论是 基于 React 的 React Native 宣称的「Learn Once, Write Anywhere」,还是基于 Vue 的 Weex 宣称的「Write Once, Run Everywhere」口号,本质上强调的都是它们跨端渲染的能力。那什么是跨端渲染呢?
React: ReactNative Taro ...
Vue: Weex UniApp ...
各种五花八门的前端框架纷纷袭来,前端工程师们纷纷抱怨学不动了~
老板们看到纷纷笑嘻嘻, App 单,前端分,小程序单,前端吞,PC/H5,前端昏。skr~
这些跨平台框架原理其实都大同小异,选定 Vue/React 作为 DSL,以这个 DSL 框架为标准在各端分别编译,在运行时,各端使用各自的渲染引擎(Render Engines)进行渲染,底层渲染引擎中不必关心上层 DSL 的语法和更新策略,只需要处理 JS Framework 中统一定义的节点结构和渲染指令。也正是因为这一渲染层的抽象,使得跨平台/框架成为了可能。
Vue 和 React 现在都实现了自定义渲染器,下面我们简单介绍一下:
React16 采用新的 Reconciler,内部采用了 Fiber 的架构。react-reconciler模块正是基于 v16 的新 Reconciler 实现,它提供了创建 React 自定义渲染器的能力.
const Reconciler = require("react-reconciler");
const HostConfig = {
// You'll need to implement some methods here.
// See below for more information and examples.
};
const MyRenderer = Reconciler(HostConfig);
const RendererPublicAPI = {
render(element, container, callback) {
// Call MyRenderer.updateContainer() to schedule changes on the roots.
// See ReactDOM, React Native, or React ART for practical examples.
},
};
module.exports = RendererPublicAPI;
vue3 提供了createRender API,让我们创建自定义渲染器。
createRenderer 函数接受两个泛型参数: HostNode 和 HostElement,对应于宿主环境中的 节点 和 元素 类型。
自定义渲染器可以传入特定于平台的类型,如下所示:
import { createRenderer } from 'vue'
const { render, createApp } = createRenderer<Node, Element>({
patchProp,
...nodeOps
})
<template>
<text x="0" y="0">Hello Vue</text>
<text x="0" y="20">{{ time }}</text>
<text x="0" y="40">Hi SSD3306</text>
</template>
<script>
import { defineComponent, ref, toRefs, onMounted } from "vue";
import dayjs from "dayjs";
export default defineComponent({
setup() {
const time = ref(dayjs().format("hh:mm:ss"));
onMounted(() => {
setInterval(() => {
time.value = dayjs().format("hh:mm:ss");
}, 800);
});
return {
...toRefs({
time,
}),
};
},
});
</script>
要将 Vue 渲染到液晶屏,我们首先需要让 Vue 能运行在 Node.js 上,但是上面这个 SFC 是没办法被 Node.js 识别的,它只是 vue 的编程规范,是一种方言。所以我们需要做的是先将 SFC 转为 js。这里我使用 Rollup 打包将 SFC 转为 JS(相关配置这里就不啰嗦了,贴个传送门)。到了这一步,Node.js 就能成功运行打包后的 js 代码了,这还不够,这时候 Vue 组件的状态更新是没办法同步到 Node.js 的。
组件状态更新我们需要通知 Node.js 更新并渲染液晶屏内容,我们需要创建自定义的"更新策略"。这里就需要用到了我们前面提到的自定义渲染器:createRenderer API。下面我们简单介绍下我们相关使用:
// index.js
// 自定义渲染器
import { createApp } from "./renderer.js";
// 组件
import App from "./App.vue";
// 容器
function getContainer() {
// ...
}
// 创建渲染器,将组件挂载到容器上
createApp(App).mount(getContainer());
// renderer.js
import { createRenderer } from "vue";
// 定义渲染器,传入自定义nodeOps
const render = createRenderer({
// 创建元素
createElement(type) {},
// 插入元素
insert(el, parent) {},
// props更新
patchProp(el, key, preValue, nextValue) {},
// 设置元素文本
setElementText(node, text) {},
// 以下忽略,有兴趣的童鞋可自行了解
remove(el) {},
createText(type) {},
parentNode(node) {},
nextSibling(nide) {},
});
export function createApp(root) {
return render.createApp(root);
}
vue 渲染器默认实现了 Web 平台 DOM 编程接口,将 Virtual DOM 渲染为真实 DOM。但是这个渲染器只能运行在浏览器中,不具备跨平台能力。所以我们必须重写 nodeOps 相关钩子函数,实现对应宿主环境元素的增删改查操作。接下来我们定义一个适配器,来实现相关逻辑。
在实现前,我们先来理一下我们要实现的逻辑:
// adapter.js
// 文本元素
export class Text {
constructor(parent) {
// 提供一个父节点用于寻址调用更新 (前面提到状态更新由容器进行)
this.parent = parent;
}
// 元素绘制,这里需要实现文本元素渲染逻辑
draw(text) {
console.log(text);
}
}
// 适配器
export class Adapter {
constructor() {
// 装载容器
this.children = [];
}
// 装载子元素
append(child) {
this.children.push(child);
}
// 元素状态更新
update(node, text) {
// 找到目标渲染进行绘制
const target = this.children.find((child) => child === node);
target.draw(text);
}
clear() {}
}
// 容器 === 适配器实例
export function getContainer() {
return new Adapter();
}
好了,基本的适配器已经完成了,接下来我们来实现渲染器。
import { createRenderer } from "vue";
import { Text } from "./adapter";
let uninitialized = [];
const render = createRenderer({
// 创建元素,实例化Text
createElement(type) {
switch (type) {
case "text":
return new Text();
}
},
// 插入元素,调用适配器方法进行装载统一管理
insert(el, parent) {
if (el instanceof Text) {
el.parent = parent;
parent.append(el);
uninitialized.map(({ node, text }) => el.parent.update(node, text));
}
return el;
},
// props更新
patchProp(el, key, preValue, nextValue) {
el[key] = nextValue;
},
// 文本更新,重新绘制
setElementText(node, text) {
if (node.parent) {
console.log(text);
node.parent.clear(node);
node.parent.update(node, text);
} else {
uninitialized.push({ node, text });
}
},
remove(el) {},
createText(type) {},
parentNode(node) {},
nextSibling(nide) {},
});
export function createApp(root) {
return render.createApp(root);
}
OLED,即有机发光二极管( Organic Light Emitting Diode)。是一种液晶显示屏。而 SSD1306 就是一种 OLED 驱动芯片。ssd1306 本身支持多种总线驱动方式:6800/8080 并口、SPI 及 IIC 接口方式。这里我们选择 IIC 接口方式进行通信,理由很简单: 1. 接线简单方便(两根线就可以驱动 OLED) 2.轮子好找...缺点就是 IIC 传输数据效率太慢了,刷新率只有 10FPS 不到。而 SPI 刷新率最大能达到 2200FPS。
IIC 仅需要 4 根线就可以,其中 2 根是电源,另外 2 根是 SDA 和 SCL。我们使用 IIC-1 接口。下面是树莓派的 GPIO 引脚图。
注意:请一定以屏幕的实际引脚编号为准。
sudo apt-get install -y i2c-tools
i2c-tools 提供的 i2cdetect 命令可以查看挂载设备
sudo i2cdetect -y 1
我们先来看几个 Node.js 库,看完你会不得不感叹~任何可以使用 JavaScript 来编写的应用,最....
Johnnt-Five 是一个支持 JavaScript 语言编程的机器人和 IOT 开发平台,基于 Firmata 协议。Firmata 是计算机软件和微控制器之间的一种通信协议。使用它,我们可以很简单的架起树莓派和屏幕芯片之间的桥梁。
Raspi IO 是一个为 Johnny-Five Node.js 机器人平台提供的 I/O 插件,该插件使 Johnny-Five 能够控制一个 Raspberry Pi 上的硬件。
5x7 oled 字体库,将字符转为 16 进制编码,让 oled 程序能够识别。用于绘制文字。
📺 兼容 johnny-five 的 oled 支持库 (johnny-five 本身并不支持 oled),提供了操作 oled 的 API。
// oled.js
const five = require("johnny-five");
const Raspi = require("raspi-io").RaspiIO;
const font = require("oled-font-5x7");
const Oled = require("oled-js");
const OPTS = {
width: 128, // 分辨率 0.96寸 ssd1306 128*64
height: 64, // 分辨率
address: 0x3c, // 控制输入地址,ssd1306 默认为0x3c
};
class OledService {
constructor() {
this.oled = null;
}
/**
* 初始化: 创建一个Oled实例
* 创建后,我们就可以通过操作Oled实例来控制屏幕了
*/
init() {
const board = new five.Board({
io: new Raspi(),
});
// 监听程序退出,关闭屏幕
board.on("exit", () => {
this.oled && this.remove();
});
return new Promise((resolve, reject) => {
board.on("ready", () => {
// Raspberry Pi connect SSD 1306
this.oled = new Oled(board, five, OPTS);
// 打开屏幕显示
this.oled.turnOnDisplay();
resolve();
});
});
}
// 绘制文字
drawText({ text, x, y }) {
// 重置光标位置
this.oled.setCursor(+x, +y);
// 绘制文字
this.oled.writeString(font, 2, text, 1, true, 2);
}
clear({ x, y }) {
this.oled.setCursor(+x, +y);
}
// 刷新屏幕
update() {
this.oled.update();
}
remove() {
// 关闭显示
this.oled.turnOffDisplay();
this.oled = null;
}
}
export function oledService() {
return new OledService();
}
接下来,我们就可以在适配器中调用 oled 程序渲染屏幕了~
// index.js
import { createApp } from "./renderer.js";
import { getContainer } from "./adapter";
import { oledService } from "./oled";
import App from "./App.vue";
const oledIns = oledService();
oledIns.init().then(() => {
createApp(App).mount(getContainer(oledIns));
});
// adapter.js
export class Text {
constructor(parent) {
this.parent = parent;
}
draw(ints, opts) {
ints.drawText(opts);
ints.update();
}
}
export class Adapter {
constructor(oledIns) {
this.children = [];
this.oled = oledIns;
}
append(child) {
this.children.push(child);
}
update(node, text) {
const target = this.children.find((child) => child === node);
target.draw(this.oled, {
text,
x: node.x,
y: node.y,
});
}
clear(opts) {
this.oled.clear(opts);
}
}
export function getContainer(oledIns) {
return new Adapter(oledIns);
}
到这一步,就可以成功点亮屏幕啦,来看看效果~
完整代码已上传到 Github,如果你觉得这个实践对你有启发/帮助,点个 star 吧~
Vue 已经成功渲染到嵌入式液晶屏了,那下一步是不是可以考虑接个摇杆写个贪吃蛇游戏了~哈哈哈,这很"Javascript"。
"阅读式"的学习使我犯困,所以我更倾向通过一些有趣的实践吸收知识。如果你和我一样爱折腾,欢迎关注~
查看原文如标题所示,就是将Vue渲染到嵌入式液晶屏。这里使用的液晶屏是0.96 寸大128x64分辨率的SSD1306。要将Vue渲染到液晶屏,我们还需要一个桥梁,它必须具备控制液晶屏及运行代码的能力。而树莓派的硬件对接能力和可编程性天然就具备这个条件。最后一个问题来了,我们用什么...
赞 14 收藏 7 评论 3
minororange 赞了文章 · 2020-10-27
最近苹果的 iOS 系统升级到了 iOS 14,这次的更新我比较关注的就是升级的小组件功能,这次更新我们可以将小组件放置在主屏幕中的任何位置,可以让我们更加便捷的查看一些信息,从而省去了还需要打开APP去查看消息的步骤,感觉很方便。
看到这里一些同学可能会说,功能是挺不错的,如果我自己也能开发一个小组件展示自己想看的内容就好了。是呀,哪一个小男孩不想拥有一个专属于自己的 iOS 小组件。
别慌,最近发现了一个APP可以让我们通过使用JavaScript来创建我们自己想要的小组件。这个APP的名字就是Scriptable,还是验证了Jeff Atwood那句话,任何可以使用JavaScript来编写的应用,最终会由JavaScript编写。作为一个前端有没有感觉很开心,又有一个地方可以让你来大展身手了,那就趁热赶紧来了解一下吧。
工欲善其事,必先利其器,我们先来了解一下Scriptable有哪些作用吧,从上面官网上的介绍我们可以知道,这个APP可以让我们使用JavaScript来自动化iOS。这句话是什么意思呢?就是我们可以提前编写好一些代码去执行一些特定的任务,比如:获取GitHub上面的Trending项目的名字和介绍、了解Hacker News的最新资讯、获取自己的最近日程、以及自己的TODO列表等等。当然这都只是一些最基本的使用场景,你肯定有自己的想法,看完这篇文章之后就去自己去实现一个独一无二的小应用吧。
上面列举的是一些Scriptable的特性,这些特性包括:
是不是感觉还是挺不错的,这些特性已经可以让我们去做很多可以自动化的事情了。
下载完成之后打开应用,我们可以看到一些已经写好的例子:
点击小卡片会直接运行相应的程序,点击小卡片右上角的更多按钮进入相应程序的代码编辑模式。
底部有一个悬浮的操作栏,左边第一个按钮是一个设置按钮,你可以为当前的小程序设置图标,颜色,等等:
左边第二个按钮是一个文档提示按钮,点击可以搜索想要使用的相关的API:
最右边是一个运行按钮,点击会直接在手机上运行你编写的应用程序。这个大家应该一看就知道了:
我以为通过在手机上的编辑器进行代码的编写会比较费劲和吃力,但是试了一下发现还好。因为手机上的编辑器也有比较完善的语法提示功能,编写代码的体验虽然不如在电脑上那般舒服,但也是在可以接受的范围之内。
上面是一些关于 Scriptable APP的简单的介绍,你可以自己在手机上好好体验一下。我觉得整个APP很简约,但是功能还是很强大的。
我们学习编程语言的第一步就是输出Hello World
,所以我们使用 Scriptable 的第一个小应用就是在主屏幕上展示Hello Wolrd
。
我个人觉得在你开始真正的开发自己想要的小组件之前,开发一个Hello World
的小组件还是很有必要的,因为这个过程相对容易一点,可以增加我们的自信心。我们可以通过这个小程序来建立起我们开发小组件的手感,并且我们是可以直接在手机的屏幕上看到这个结果的,是不是还蛮有成就感的。
在编码的过程中有几个选择,你可以选择直接在手机的编辑器上进行编码,也可以通过 Mac 的 iCloud 云盘 的同步功能,在 Mac 电脑上用自己熟悉的编辑器开发。如果你有蓝牙的键盘,可以直接使用蓝牙键盘连接到手机上使用自己的键盘进行编码。根据自己的条件选择一个自己舒服的方式进行编码。
接下来就是Hello World
小组件的代码了:
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: cyan; icon-glyph: greater-than-equal;
// 以下代码仅供学习交流使用
// 判断是否是运行在桌面的组件中
if (config.runsInWidget) {
// 创建小部件
const widget = new ListWidget();
// 添加文本
const text = widget.addText("Hello, World!");
text.textColor = new Color("#000000");
text.font = Font.boldSystemFont(36);
text.centerAlignText();
// 添加渐变色背景
const gradient = new LinearGradient();
gradient.locations = [0, 1];
gradient.colors = [new Color("#F5DB1A"), new Color("#F3B626")];
widget.backgroundGradient = gradient;
// 设置部件
Script.setWidget(widget);
}
上面的代码还是比较简单的,相信大家看一下就明白了。我再简单介绍一下,最开头的注释是 Scriptable 自己生成的,用来设置小卡片的图标和图标颜色;接下来的一个if
判断表明我们希望接下来的代码是在小组件的条件下运行的,用来生成我们的小组件。
然后接下来的代码就是创建小组件,添加文本,设置本文的颜色、字体以及对齐方式。然后添加了一个渐变的背景,最后把上面生成的小组件通过Script.setWidget()
进行设置。这样我们的Hello World
小组件就算完成啦。
也许5分钟过后你就开始不满足一个简单的 Hello World 小组件了,你知道你的征途是星辰大海,所以你要做出一些有实际应用价值的小组件。但此时的你已经工作到晚上十点多了,十分想给自己点一个夜宵来犒劳一下自己。但是你特别纠结吃啥?
看了看楼下的炒粉干和山东杂粮煎饼以及烤冷面,你十分纠结要吃啥。所以为了节省时间我决定开发一个帮你选择吃什么的小组件。就叫它:“今天吃点啥”吧。
看看这个组件的图标是不是就很有食欲?当你不确定要吃啥的时候就点击这个小组件,然后我们编写的程序就会运行,它会在面板上列出你这次要吃的选项,你点击选择,马上就知道自己要吃啥了,是不是解决了你迟迟下不定决心要吃啥的纠结状态。
为了明确告诉你这次的选择是什么,我特意在选择之后给你发送一个选择结果的通知,是不是很人性化😂,你肯定还发现了为什么食物的名字与图片不符合。是的,我是故意这样做的,为了营造一种你即将吃大餐的假象😁。
下面是一张动图,可以提前感受一下这个过程:
因为我之前有帮助过同事使用Swift
开发原生 iOS 的一些经验,所以这里面跟原生相关的一些API我看着还算熟悉的,也好上手。就算没有相关的开发经验关系也不大,毕竟文档对于相关API的解释都还算清楚的,相信你看一看就可以很快上手的。
因为这个小组件的代码量稍微多一点,就不在这里展示了;大家如果有兴趣的话可以在scriptable-scripts看到这个小组件的源码部分,写的时候比较仓促,还有很多可以完善的地方。如果你有什么好的意见也可以提出来,我们一起学习进步。
更新了 iOS 14 之后,发现手机上的很多APP都新增了相关的小组件,这让用户可以快速方便的浏览一些关键的信息,也可以快速直达具体的服务。对用户来说还是很有帮助的。
对于开发者来说,这里面也存在一些新的机遇。就算不会原生的 iOS 开发,我们也可以借助像Scriptable这样的小组件平台,来创造出一些有趣,有价值,有意义的小组件。
有没有发现小组件是不是跟小程序在某些方面很相似?感觉以后应该会出现系统级别的“小程序”平台,如果Android和iOS再搞一个统一的开发平台,前端开发者又可以扬帆远航了,想想是不是有点小激动呢。。。
如果你对使用 Scriptable 开发小组件很有兴趣,也欢迎大家进Scriptable小组件交流群进行交流讨论。
文章到这里就结束了,如果你有什么意见和建议欢迎给我留言。你还可以关注我的公众号关山不难越,可以及时获取最新好玩有趣文章的更新。
注:“今天吃点啥”小组件的图标使用的是https://undraw.co/网站上的,相关食物的图片来自https://unsplash.com/,每张图片来自哪个创作者在代码的注释中有说明。
查看原文最近苹果的 iOS 系统升级到了 iOS 14,这次的更新我比较关注的就是升级的小组件功能,这次更新我们可以将小组件放置在主屏幕中的任何位置,可以让我们更加便捷的查看一些信息,从而省去了还需要打开APP去查看消息的步骤,感觉很方便。
赞 10 收藏 5 评论 1
minororange 回答了问题 · 2020-10-27
win10 自带 ubuntu 啊,跟主机共享文件夹,共享端口,推荐试试:https://learnku.com/articles/...
win10 自带 ubuntu 啊,跟主机共享文件夹,共享端口,推荐试试:[链接]
关注 4 回答 5
minororange 赞了回答 · 2020-07-30
可以实现,但是,这种查询几乎无法优化啊.
SELECT
DATE_FORMAT( create_time, "%Y-%m-%d" ) AS d,
sum(
IF
( sex = 1, 1, 0 )) AS male,
sum(
IF
( sex = 2, 1, 0 )) AS female,
sum( is_signed ) as signed,
sum(
IF
( DATEDIFF( NOW(), birth_day )/ 365 < 20, 1, 0 )) AS under20,
sum(
IF
( DATEDIFF( NOW(), birth_day )/ 365 >= 20 AND DATEDIFF( NOW(), birth_day )/ 365 < 31, 1, 0 )) AS 20to30,
sum(
IF
( DATEDIFF( NOW(), birth_day )/ 365 >= 31, 1, 0 )) AS 30more
FROM
`user`
GROUP BY
d;
子查询版
SELECT
u.d,
sum(
IF
( u.sex = 1, 1, 0 )) AS male,
sum(
IF
( u.sex = 2, 1, 0 )) AS female,
sum( u.is_signed ) AS signed,
sum(
IF
( u.age < 20, 1, 0 )) AS under20,
sum(
IF
( u.age >= 20 AND u.age < 31, 1, 0 )) AS 20to30,
sum(
IF
( u.age >= 31, 1, 0 )) AS 30more
FROM
( SELECT DATE_FORMAT( create_time, "%Y-%m-%d" ) AS d, sex, is_signed, DATEDIFF( NOW(), birth_day )/ 365 AS age FROM USER ) AS u
GROUP BY
u.d
可以实现,但是,这种查询几乎无法优化啊. {代码...} 子查询版 {代码...}
关注 3 回答 2
minororange 回答了问题 · 2020-07-10
pecl install redis
pecl install redis
关注 2 回答 2
minororange 回答了问题 · 2019-11-16
<el-input-number :min="0"></el-input-number>
{代码...}
关注 2 回答 2
查看全部 个人动态 →
(゚∀゚ )
暂时没有
注册于 2017-03-10
个人主页被 1.9k 人浏览
推荐关注