SegmentFault 微信小程序开发经验分享最新的文章
2021-05-06T19:21:40+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
为 wxa.js 构建的小程序移除 scss 依赖
https://segmentfault.com/a/1190000039954229
2021-05-06T19:21:40+08:00
2021-05-06T19:21:40+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
3
<p>wxa.js 默认使用的样式语言是 scss,所以其默认创建的项目就会要求安装 node-sass,但由于 node-sass 依赖了 binding.node 等包,导致常常会出现 node-sass 安装失败的问题。</p><p>如果你并没有在项目中使用 scss ,则可以考虑将你项目种的 node-sass 移除,从而缩小项目的依赖体积,提升项目安装成功的概率。</p><h2>思路</h2><p>由于 wxa 默认使用了 scss,因此,我们需要移除项目中针对 scss 的配置,并移除代码中的 scss ,这样才能保证后续在编译的过程中,不会调用 node-sass 的依赖。</p><h2>步骤</h2><ol><li>移除 wxa.config.js 中关于 scss 的配置</li></ol><p>在 wxa 的默认配置中,配置了 sass/scss 的依赖,我们如果不移除这个依赖,就会导致后续在构建的时候,自动安装相关依赖。</p><p><img src="/img/remote/1460000039954231" alt="" title=""></p><p>因此,我们需要在 <code>wxa.config.js</code> 中添加 <code>use</code> 相关配置,且仅保留 babel 作为依赖,具体修改如下:</p><pre><code>module.exports = {
plugins: [
new ReplacePlugin({
list: envlist,
}),
],
// 你的其他配置
use: [
{
test: /\.js$/,
name: 'babel',
},
],
// 你的其他配置
};</code></pre><ol><li>移除项目中标记为 Scss 的语言</li></ol><p>在移除了 wxa.js 的构建依赖后,接下来需要移除代码中关于 scss 的标示,从而使我们的代码可以被正确的渲染工具所渲染。具体修改如下所示,右侧为修改后的结果</p><p><img src="/img/remote/1460000039954232" alt="" title=""></p><ol><li>移除 package.json 中的 相关依赖。</li></ol><p>当我们完成了上述的操作之后,就可以放心的移除系统中关于 sass 的依赖了,从而减少整个项目的体积和对 node-sass 的依赖。</p><p>你只需要执行如下的命令,就可以移除项目中关于 sass 的依赖了。</p><pre><code>npm uninstall @wxa/compiler-sass
// 或者
yarn remove @wxa/compiler-sass</code></pre><h2>总结</h2><p>scss是一个好的语言,但 node-sass 却不是一个好的工具,如果你不使用它,不妨将其移除,提升你的项目构建速度。</p>
wxa.js 引入 tailwindcss
https://segmentfault.com/a/1190000039954219
2021-05-06T19:20:38+08:00
2021-05-06T19:20:38+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
1
<p>TailwindCSS 是我最近一段时间使用比较多的 CSS 框架,相比于传统我们习惯的前端框架,它的限制更少,你可以根据自己的需要来编写样式。如果你配置了清除没用到的 CSS,TailwindCSS 的体积甚至可以远小于其他框架。 </p><p>也因为上面的这些因素,我也自然而然的会选择将其放在小程序中来使用。而由于我使用的是 wxa.js ,所以这里也是一个对应的 wxa.js 的教程。</p><h2>安装</h2><p><strong>1. 安装 TailiwndCSS 到你的项目中</strong></p><p>由于 Taildwind 默认推荐使用的是 PostCSS,但其需要的是 PostCSS 7 或者 8 ,但 WXA.js 提供的 PostCSS 插件使用的是 6 ,所以这里我就选择不将其作为 PostCSS 插件来安装。</p><p>在 WXA 项目根目录中执行如下命令,会在你的项目根目录中生成一个 tailwindcss.config.js,它会作为后续你的项目配置的配置文件。</p><pre><code>npx tailwindcss-cli@latest init</code></pre><p>随后,在你的项目根目录创建一个 tailwind.css 文件,并在其中加入如下代码,这个文件作为你后续样式基础文件。</p><pre><code>@tailwind base;
@tailwind components;
@tailwind utilities;</code></pre><p>添加完成后,你就可以执行如下命令,来生成一个默认的 tailwindcss 样式文件。</p><pre><code>npx tailwindcss-cli@latest build ./src/tailwind.css -o ./src/tailwind.css</code></pre><p>生成的效果如下,可以看到,未任何处理的情况下,整个 CSS 文件足足有 3.81 MB,不过不用担心,我们可以清除其中的样式。</p><p><img src="/img/remote/1460000039954221" alt="未做清理" title="未做清理"></p><blockquote>如果你希望后续不输入这个命令,可以将其添加到你的项目的 <code>package.json</code> 中。</blockquote><p><strong>2. 移除默认的浏览器自动添加的 prefix</strong></p><p>由于不同浏览器在不同的样式上可能有所不同,tailwindcss 会在生成的时候帮助我们生成一些特定的前缀。但小程序不涉及到浏览器的兼容性问题,所以我们可以关闭 taildwindcss 的 autoprefixer。</p><p>将刚刚的生成命令中加入 <code> --no-autoprefixer</code> 的参数,就可以生成不含 prefix 的 CSS 文件</p><pre><code>npx tailwindcss-cli@latest build --no-autoprefixer ./src/tailwind.css -o ./src/tailwind.css</code></pre><p>可以看到,去除 prefix 后,我们的文件缩小至 3.46MB。</p><p><img src="/img/remote/1460000039954222" alt="去除 prefix" title="去除 prefix"></p><p><strong>3. 移除不用的样式</strong></p><p>tailwindcss 提供了根据页面结构分析使用样式的功能,从而可以实现在构建生产版本之时,移除没有使用的样式,从而可以达到缩小样式的目的。</p><p>在项目中的配置 purge 属性,就可以实现 tailwindcss 自动分析 wxa 文件,从而移除没有用到的样式。</p><pre><code>module.exports = {
purge: ['./src/**/*.wxa'],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
screens: [],
},
variants: {
extend: {},
}
}</code></pre><p>配置了 purge 属性后,可以看到,样式文件锐减到 9.93KB (因为我使用的样式很少)</p><p><img src="/img/remote/1460000039954223" alt="移除后的效果" title="移除后的效果"></p><p><strong>4. 在 wxa.js 中引入</strong></p><p>修改生成文件的命令,使生成的文件的后缀为 wxsss,从而可以继续使用微信小程序的 <code>@import</code> 语法。</p><pre><code>npx tailwindcss-cli@latest build --no-autoprefixer -o ./src/tailwind.wxss</code></pre><p>重新生成样式文件后,只需要在 <code>app.wxa</code> 文件中的 <code>style</code> 块中加入如下引入代码,在项目中引入 tailwindcss。</p><blockquote>因为 tailwindcss 只生成一个样式文件,因此,只需要在 app.wxa 中引入,即可确保所有页面都可以正常使用 tailwindcss</blockquote><pre><code>@import "./tailwind.wxss" </code></pre><h2>优化</h2><p>实际上,taildwindcss 的体积还可以进一步优化,你可以完全移除掉那些你没有用到的属性,从而让你的 css 文件特别小,从而控制小程序样式的大小。</p>
用 Python 制作头图 1.1
https://segmentfault.com/a/1190000030687851
2020-10-08T17:49:19+08:00
2020-10-08T17:49:19+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
1
<p>在昨天的文章<a href="https://link.segmentfault.com/?enc=pBDWh7G2UX6PbESex7q%2BrA%3D%3D.81BeFykKMSbBK0MN7PhZj%2BV8AX6Pj7BCJSxYj9M3aW2a5GFlYEco3JUKa9kSbyCazmgxEz2rdSw3eYmk2AdSFA%3D%3D" rel="nofollow">用 Python 制作头图 1.0</a> 中,介绍了如何使用 Python 来生成头图。但昨天的脚本还不够好用,所以今天来让他变得更好用。</p><p>我们在编写公众号的时候,公众号的头图可以选择的一个是两个样式,一个是 900*383 的横幅图片,另一个是 1:1 的正方形图片,一般情况下,这两个图都会是基于一个图来完成的,但在实际的使用过程中,可能会因为图片布局的问题而无法很好的展现。这里要感谢我在上一份工作中的同时 @boqunwu,给我分享了如何让这两个图可以更好的展现。</p><p>简单来说就是,可以生成一个长方形图 + 正方形图的方式,来让这个更好用。 就像下面这样</p><p><img src="/img/remote/1460000030687855" alt="生成的图片" title="生成的图片"></p><p>如果你生成了这样的图片,那么就可以在选择图片的时候,根据实际的需要,选择不同大小的位置,从而让你主图和副图可以很好的展示内容。</p><p><img src="/img/remote/1460000030687856" alt="" title=""></p><p>因此,今天我们来编写代码,来生成这样的图片</p><h2>环境依赖</h2><p>今天的实现是在昨天的基础之上实现了,因此,你只需要保证你安装好了 Pillow 就可以执行今天的代码</p><pre><code>pip install pillow</code></pre><h2>实现思路</h2><p>想要实现我们想要的效果,则需要在代码中生成一个 900x383 + 383x383 的图片,从而实现我们想要的效果</p><p><img src="/img/remote/1460000030687854" alt="" title=""></p><p>因此,我们可以在代码中加入我们想要的文本内容,在一张画布上来生成多个区域,分别作为我们需要的图片内容。</p><h2>代码实现</h2><pre><code class="python"># coding=utf-8
from PIL import Image, ImageDraw, ImageFont
# 定义变量
ctx_bg_color = (255, 255, 255)
bg_size = (900, 766)
# 大图配置
big_image_size = (900, 383)
big_text = "用 Python 制作头图 v1.1"
big_text_color = "#eeeeee"
big_image_bg_color = "#fca652"
big_text_font_size = 80
# 小图配置
small_image_size = (383, 383)
small_image_color = "#ac4b1c"
small_text = "Python"
small_text_color = "#eeeeee"
small_text_font_size = 80
# 逻辑实现
# 定义画布
export_image = Image.new("RGB", bg_size, ctx_bg_color)
# 生成文本样式
big_text_font = ImageFont.truetype("NotoSansSC-Medium.otf", big_text_font_size)
small_text_font = ImageFont.truetype("NotoSansSC-Medium.otf", small_text_font_size)
# 计算大图文字尺寸
big_text_width = big_text_font.getsize(big_text)
big_text_coordinate = int((big_image_size[0] - big_text_width[0]) / 2), int(
(big_image_size[1] - big_text_width[1]) / 2 - 20)
# 计算小图文字尺寸
small_text_width = small_text_font.getsize(small_text)
small_text_coordinate = int((small_image_size[0] - small_text_width[0]) / 2) + 0, int(
(small_image_size[1] - small_text_width[1]) / 2 + 383)
img_draw = ImageDraw.Draw(export_image)
img_draw.rectangle((0, 0, 900, 383), big_image_bg_color, big_image_bg_color)
img_draw.rectangle((0, 383, 383, 766), small_image_color, small_image_color)
img_draw.text(big_text_coordinate, big_text, font=big_text_font, fill=big_text_color)
img_draw.text(small_text_coordinate, small_text, font=small_text_font, fill=small_text_color)
export_image.show()
export_image.save("export-big.jpg", quality=95)</code></pre><h2>总结</h2><p>和昨天相比,今天我们对代码进行了一定的优化,这些优化可以让我们的文章的头图开始有了一些不一样,但在实际使用过程中,我们还是会发现,使用似乎不是那么方便,明天我们将对这个代码进行下一步优化。</p><p>你觉得这个脚本还有哪些地方可以优化呢?</p><p>此外,今天的头图就是用脚本生成的,你可以试试看看他在你信息流中和分享到朋友圈中的效果是否一致?</p>
用 Python 制作头图 1.0
https://segmentfault.com/a/1190000027086480
2020-10-07T22:11:44+08:00
2020-10-07T22:11:44+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
1
<p>我的公众号头图属于特别简洁的,原因是我在乎的是文章中的内容,而不是头图。<strong>虽然我知道一个好的头图会提升点击率</strong>,但其背后耗费的时间是我目前不愿去做的。因此,我目前的头图都是 PS 打开一张图,然后直接放上一行文字来生成的。</p><p><img src="/img/remote/1460000027086483" alt="历史头图" title="历史头图"></p><p>但是,打开 PS 还是太花费精力了,因此,我希望可以用更少的时间来生成简单明了的头图。因此,我决定优化我的头图制作方案,使用代码来完成公众号头图的生成。</p><h2>环境依赖</h2><p>这一次的代码使用 Python 来完成,并基于 Python 的 Pillow 库完成图片的生成,因此,你在运行我的代码时,需要先安装 Pillow</p><pre><code>pip install pillow</code></pre><h2>实现思路</h2><p>在使用 Pillow 绘图的时候,和我们在前端使用 Canvas 绘图的思路基本一致,我们要做的是</p><ol><li>准备特定背景的画布</li><li>加载字体</li><li>绘制文字</li><li>导出图片</li></ol><p>生成的难度并不高,也只需要花费一些时间来编写代码。具体的代码我贴在下方了,供你参考。</p><h2>代码实现</h2><p>具体的代码如下</p><pre><code class="python">from PIL import Image, ImageDraw, ImageFont
# 常用变量
img_size = (900, 383)
bg_color= "#fca652"
big_text_size = (900,383)
text = "代码生成图片"
text_color = "#eeeeee"
filename = "export.jpg"
font_size = 80
# 生成画布
export_image = Image.new("RGB", img_size, bg_color)
# 加载字体
font = ImageFont.truetype("NotoSansSC-Medium.otf", font_size)
# 计算字体宽度
text_width = font.getsize(text)
text_coordinate = int((big_text_size[0]-text_width[0])/2), int((big_text_size[1]-text_width[1])/2-20)
# 生成绘图 ctx
img_draw = ImageDraw.Draw(export_image)
# 绘制文字
img_draw.text(text_coordinate, text, font=font, fill=text_color)
export_image.save(filename, quality=95)</code></pre><h2>总结</h2><p>使用 Python 来绘制头图的难度并不大,比如今天的头图,就是通过代码来绘制的,你可以看看效果。</p><p>不过,上面的代码还是很简陋,你觉得这段代码有没有什么值得优化的点呢?</p>
基于云开发搭建 KV 缓存系统 2.0
https://segmentfault.com/a/1190000025186952
2020-10-02T11:08:57+08:00
2020-10-02T11:08:57+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
1
<p>昨天分享了基于云开发实现 KV 缓存的方法,但在实际的开发过程中,这样的功能其实很难满足所有的需求。在这个时候,就需要对原有的方案进行优化,来解决一些其他的问题。</p><p>具体来说,之前的代码有以下几个问题:</p><ol><li>没有进行错误处理</li><li>没有对数据进行过期时间的处理</li><li>如果没有数据的话,如何触发数据更新</li></ol><p>这些都没有在昨天的文章中提及,今天就补全这些部分。</p><h2>对数据进行错误处理</h2><p>在使用云开发的时候,是有出错的可能的,因此,我们需要对可能出错的部分进行处理,以确保函数本身是可以正常返回的。</p><p>我们需要在原有的函数中进行修改,修改后的结果如下</p><h3>设置缓存</h3><pre><code class="js">async function setCache(key, value) {
try {
let result = await db.collection("cache").add({
data: {
_id: key,
value: value
}
})
return {
code:0
}
} catch (error) {
return {
code:1,
msg: error
}
}
}</code></pre><h3>读取缓存</h3><pre><code class="js">async function getCache(key) {
try {
let { data } = await db.collection("cache").doc(key).get();
return {
code: 0,
value: data.value
};
} catch (error) {
return {
code: 1,
msg: error
}
}
}</code></pre><p>通过这一轮的修改,我们在原有的函数基础之上添加了错误处理,无论如何,缓存函数本身不会报错,不会阻塞外部逻辑的执行。在外部逻辑侧只需要根据返回的 code 来进行判断是获取缓存,还是直接进行逻辑计算,完成所有的工作。</p><h2>对数据进行过期时间的处理</h2><p>在前一个版本中,我们加入了错误处理,但还是没有解决一个问题,那就是数据的过期逻辑。但缓存在实际使用过程中,是存在过期的可能,既然存在过期,我们就需要在缓存中,实现相应的过期逻辑的处理。</p><p>想要实现,成本倒也不高,你只需要在数据中加一个过期时间字段<code>expiredAt</code>就可以完成相应的处理。</p><p><img src="/img/remote/1460000025186955" alt="" title=""></p><h3>设置缓存</h3><p>对设定缓存的函数加入一个新的可选参数,来设定缓存过期时间。未设定缓存过期时间的情况下,你可以缓存 3600 秒(一小时)。</p><pre><code class="js">async function setCache(key, value,expire_time = 3600) {
try {
let expired_time = (new Date()) + expire_time * 1000
let result = await db.collection("cache").add({
data: {
_id: key,
value: value,
expiredAt: expired_time
}
})
return {
code:0
}
} catch (error) {
return {
code:1,
msg: error
}
}
}</code></pre><h3>读取缓存</h3><p>类似的,在缓存中加入相应的配置,可以完成缓存过期的处理</p><pre><code class="js">async function getCache(key) {
try {
let { data } = await db.collection("cache").doc(key).get();
let current = (new Date()).valueOf();
if (data.expiredAt < current) {
return {
code:1,
msg: "Cache expired"
}
}
return {
code: 0,
value: data.value
};
} catch (error) {
return {
code: 1,
msg: error
}
}
}</code></pre><h2>如何触发数据的更新</h2><p>在上一个版本中,我们完成了数据的过期,但目前数据的过期后处理是由业务逻辑来完成的,我们有没有办法在缓存过期的时候,进行相应的触发,从而主动更新缓存,这样可以确保缓存接口始终可以拿到数据?</p><p>也可以。我们可以再新增一个参数来完成。这一部分主要影响的是获取 Cache 的部分,所以,设置缓存和上一个版本相同。</p><h3>设置缓存</h3><pre><code class="js">async function setCache(key, value,expire_time = 3600) {
try {
let expired_time = (new Date()) + expire_time * 1000
let result = await db.collection("cache").add({
data: {
_id: key,
value: value,
expiredAt: expired_time
}
})
return {
code:0
}
} catch (error) {
return {
code:1,
msg: error
}
}
}</code></pre><h3>获取缓存</h3><p>获取缓存时,我们需要能够实现相应的数据更新的能力,因此,我们需要新增一个参数,来完成数据更新的部分。新的参数中我们需要传入一个函数,这个函数将会执行数据更新逻辑,将数据</p><pre><code class="js">async function getCache(key,uploadFunction) {
try {
let { data } = await db.collection("cache").doc(key).get();
let current = (new Date()).valueOf();
if (data.expiredAt < current) {
if(uploadFunction){
const data = uploadFunction();
setCache(key,data)
return {
code:0,
value: data
}
}
return {
code:1,
msg: "Cache expired"
}
}
return {
code: 0,
value: data.value
};
} catch (error) {
return {
code: 1,
msg: error
}
}
}</code></pre><h2>总结</h2><p>在今天的文章中,我们通过一些简单的处理,完成了缓存函数的三步优化,分别加入了缓存处理、过期时间和自动更新,让缓存可以变得更好用。</p><p>那么问题来了,你觉得这个缓存系统还有什么可以优化的点么?</p>
如何在云开发中搭建 KV 缓存系统
https://segmentfault.com/a/1190000025185852
2020-10-01T21:55:18+08:00
2020-10-01T21:55:18+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
0
<p>云开发的数据库在实际使用中的性能是非常不错的。但涉及到一些比较重的计算量时,依然会让查询花费不少的时间(比如提取所有的数据计算排行),在这种情况下,就需要一种方式来优化数据查询,让原本对数据库压力比较大的数据查询消耗更少的数据库性能,降低数据查询耗时,优化数据体验。</p><p>一个简单易行的方式就是加入缓存,比如建设一套 KV 缓存系统,来完成数据的优化。</p><h2>如何在云开发中实现一个简单的 Key - Value 数据库?</h2><p>想要在云开发中实现一个简单的 Key-Value 数据库,十分简单, 你只需要打开云开发的数据库,在其中创建一个<code>cache</code>表,就可以开始编写代码来实现数据的缓存。</p><blockquote>需要注意的是,如果你需要让缓存可以在移动端读取,则需要将数据表的权限设置为 <strong>所有用户可读,仅创建者可写</strong>。</blockquote><p>云开发的数据库中,以 <code>_id</code> 作为数据主键,并默认为 <code>_id</code> 提供了索引,因此,我们只需要将缓存的 key 放在 <code>_id</code> 中,就可以借助数据库的索引机制,避免扫描全表来获取数据。同时,还可以借助云开发的 <code>doc</code> 方法来快速读取数据,简化代码。</p><p>具体的实现,你可以参考下方的代码。</p><h3>设置缓存</h3><pre><code class="js">async function setCache(key,value){
return await db.collection("cache").add({
data:{
_id:key,
value:value
}
})
}</code></pre><h3>读取缓存</h3><pre><code class="js">async function getCache(key){
let { data } = await db.collection("cache").doc(key).get();
return data.value;
}</code></pre><h3>如何使用</h3><p>当你需要设置缓存的时候,你只需要执行 <code>setCache</code> 方法,就可以将特定的数据设置到数据库中,并在需要的地方使用 <code>getCache</code> 方法来获取这些数据,提升你的数据库查询数据。</p><p>借助自建的简易缓存系统,可以快速的完成产品的耗时查询的优化,同时还可以借助官方提供的 API,降低学习的成本,你不再需要去啃厚厚的 Redis 使用教程了。</p><h2>问题</h2><p>在这篇文章中,我简单介绍了如何基于云开发开发出一个简单的缓存系统,那么你发现这个缓存系统的一些问题了么?如果你要优化这个系统,你会怎么做?</p>
键盘设置如何优化小程序使用体验?
https://segmentfault.com/a/1190000025160488
2020-09-28T22:07:19+08:00
2020-09-28T22:07:19+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
1
<p>在小程序开发过程中,用户输入是必不可少的,我们经常会需要用户输入一些内容,来完成产品收集用户信息的需求。</p><p>在这种情况下,我们可以考虑借助小程序提供的一些和键盘相关的 API 来优化小程序的使用体验。</p><h2>Input 组件的 type 属性</h2><p><img src="/img/remote/1460000025160491" alt="Input 组件的 type 属性" title="Input 组件的 type 属性"></p><p>从小程序的 1.0 版本开始,就支持为 input 组件设置 type,不同的 type 会显示不同的手机键盘。默认情况下,显示的是 text 文本输入键盘,这个键盘的特点是显示所有的内容,可以适用于所有的场景。</p><p>但,<strong>适用于所有场景也就意味着不适用于所有场景</strong>,总会在每一个场景中有着种种不便,因此,在实际的开发中,为了获得更佳的体验,你可以通过设置不同的 Type 来控制实际的键盘显示情况。</p><p><img src="/img/remote/1460000025160492" alt="text 类型 input 适用建议" title="text 类型 input 适用建议"></p><p>除了默认的 text 类以外,你还可以使用 <code>number</code>(数字输入键盘)、<code>idcard</code> 身份证输入键盘和 <code>digit</code> 带小数点的数字键盘。</p><p><img src="/img/remote/1460000025160493" alt="idcard 类型 input 适用建议" title="idcard 类型 input 适用建议"></p><p>你可以根据自己的实际使用场景来设置不同的类型,比如说</p><ul><li>如果你的小程序的验证码都是数字的,那么你给出一个 <code>text</code> 类型的键盘,显然不如给一个 <code>number</code> 类型的键盘更合适。</li><li>如果你的小程序中涉及到了手机号的输入,那么这种情况下你就可以选择使用 <code>number</code> 类型的键盘,来优化用户输入时的体验。</li></ul><p><img src="/img/remote/1460000025160494" alt="number 类型 input 适用建议" title="number 类型 input 适用建议"></p><p>这里的思路是类似的,当你预期用户输入的内容只有数字,就可以考虑 <code>number</code>、<code>digit</code>、<code>idcard</code> 等类型,来优化你的小程序的实际使用体验。</p><p><img src="/img/remote/1460000025160495" alt="digit 类型 input 适用建议" title="digit 类型 input 适用建议"></p><p><strong>##</strong> <strong>总结</strong></p><p>input 组件默认提供的 四种 type ,可以通过选择不同的类型,从而获得不同的体验效果,从而对于你的小程序体验进行优化和推进。</p>
小程序如何在业务系统中接入图片安全校验
https://segmentfault.com/a/1190000022271829
2020-04-05T20:16:35+08:00
2020-04-05T20:16:35+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
4
<p>在小程序开发的过程中,如果你的小程序中存在图片上传的能力,经常会遇到同一个原因被拒绝审核:「没有对上传信息做审核」,想要过审核也很简单,只要将用户上传的图片提交审核就可以了。而且,微信也提供了这样的接口给开发者,让开发者做接入。如果你使用了云开发,那就更加的简单,只需要使用云调用,一行代码就可以完成产品的调用。</p>
<h2>如何使用云函数中接入图片安全校验</h2>
<p>不少人看过一个视频,里面的代码大致是这样写的,很简单,但很有效的完成了图片的安全检测</p>
<pre><code class="javascript">// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
// 云函数入口函数
exports.main = async (event, context) => {
const img = (await cloud.downloadFile({
fileID:event.fileID
})).fileContent;
return await cloud.openapi.security.imgSecCheck({
media:{
contentType:'image/png',
value:img
}
});
}</code></pre>
<p>这段代码在绝大多数情况下都是不会出现运行问题的,但是,这个大多数情况是指<strong>这个图片没有安全问题</strong>,如果这个图片的内容有问题,你的小程序运行就会报错,就像下面这样。</p>
<p><img src="/img/remote/1460000022271832" alt="" title=""></p>
<p>这样的报错有问题么?没有的,因为这个就是系统的运行机制,但是,这样的问题如果不做处理,就会在实际应用过程中出现问题。</p>
<p>这段报错的意思是:<strong>由于检测的内容出现了问题,导致系统出现了异常,从未抛出了一个异常</strong>,在这种情况下,开发者需要做的就是捕获这个异常。在 JavaScript 中,我们只需要在我们的项目中加入 <code>try...catch...</code> 就可以处理了。</p>
<p>加入 <code>try...catch...</code> 后,我们得到的代码是这样的</p>
<pre><code class="javascript">// 云函数入口文件
const cloud = require('wx-server-sdk')
cloud.init()
// 云函数入口函数
exports.main = async (event, context) => {
const img = (await cloud.downloadFile({
fileID:event.fileID
})).fileContent;
try {
await cloud.openapi.security.imgSecCheck({
media:{
contentType:'image/png',
value:img
}
});
return {
code: 0,
msg: "ok"
}
} catch (err) {
return {
code: 1,
msg: err
}
}
}</code></pre>
<p>这段代码会在图片没有问题的时候回复一个 <code>{ "code": 0, "code":"ok"}</code>,如果图片有问题的话,就会返回 <code>{"code":1,"msg":"错误的原因"}</code>。</p>
<p>在这种情况下,你只需要在云函数的返回值中获取到 code ,如果等于 0 ,就说明用户上传的图片通过审核。如果返回 1 ,则说明用户的图片没有通过审核。</p>
<h2>如何在业务系统中加入图片安全校验</h2>
<p>前面的内容介绍了如何开发出一个图片安全校验的云函数,接下来,我们来看看如何在业务系统中接入这个功能。</p>
<p>实际上,我们有两种方式在业务系统中实现图片安全校验,一种是前置校验,一种是后置校验。</p>
<p><strong>前置校验</strong>是指用户所提交的数据还没有提交到数据库前对图片进行校验,如果图片不合格,就不允许图片提交到数据库中。</p>
<p><strong>后置校验</strong>则是指用户所提交的数据先提交到数据库中,并标记为审核中状态, 后续通过审核再修改状态为通过,从而让用户提交的内容在前台可见。</p>
<p>一般而言,推荐大家使用前置校验,这样可以避免错误的数据进入到你的数据库中,后续可能有风险。如果你的系统对于性能的要求特别特别的高,而安全校验接口返回速度慢一些,在这种情况下,你可以选择后置校验,降低用户在提交数据时的等待时间。</p>
<p>不过,后置校验的话,一定记得在前端进行查询的时候,将未审核的部分提醒用户,不可访问,避免出现审核问题。</p>
<h2>总结</h2>
<p>在小程序中接入图片安全校验功能很简单, 不过,在实际的接入过程中, 你还是需要配合自己的实际业务情况来选择如何接入。</p>
<p><em>如果你在开发的过程中有任何问题,欢迎在下方留言告诉我。</em></p>
小程序如何访问未备案的 API | 云开发
https://segmentfault.com/a/1190000021744516
2020-02-14T09:12:39+08:00
2020-02-14T09:12:39+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
10
<p>在开发小程序的时候,经常会用到一些第三方的 API。但是第三方的 API 往往会有各种各样的问题,比如:</p>
<ol>
<li>没有HTTPS</li>
<li>没有备案</li>
<li>小程序不支持其中的一些方法</li>
</ol>
<p>等等。但是,在业务中,我们又必须要使用这些接口,应该如何操作呢?</p>
<h2>使用云开发中转</h2>
<p>一个比较简单的方法,就是使用云开发来做中转。将有限制的小程序请求,转化为没有限制的云函数请求,就可以轻松实现未备案、无 HTTPS 接口的请求。</p>
<p><img src="/img/remote/1460000021744519" alt="" title=""></p>
<h2>具体实现方式</h2>
<p>在具体的实现时,主要有以下几个步骤:</p>
<ol>
<li>创建用于中转的云函数</li>
<li>编写请求代码</li>
<li>上传并部署云函数</li>
<li>在小程序端请求云函数</li>
</ol>
<h2>代码部分</h2>
<p>我们重点讲解一下「请求代码的创建」和「在小程序请求云函数」</p>
<h3>API 介绍</h3>
<p><strong>假设我们要请求快递 100 的 API,来查询快递信息。由于其没有 HTTPS 证书,所以我们没有办法直接在小程序端查询</strong>。所以,我们建立了一个新的云函数 getKuaiDi 来查询快递信息。</p>
<p>我们要调用的 API 地址是 <code>http://www.kuaidi100.com/query?type=快递公司代号&postid=快递单号</code> ,我们只需要将这里的快递代号和单号替换,并发送一个 get 请求,就可以获取到下面这样的结果。</p>
<pre><code class="json">{
"message": "ok",
"nu": "11111111111",
"ischeck": "1",
"com": "yuantong",
"status": "200",
"condition": "F00",
"state": "3",
"data": [
{
"time": "2020-02-10 08:47:03",
"context": "查无结果",
"ftime": "2020-02-10 08:47:03"
}
]
}</code></pre>
<h3>请求代码的创建</h3>
<p>接下来来编写代码,首先,我们需要安装依赖,在云函数上右击,选择「在终端中打开」,执行命令安装 <em>got</em></p>
<pre><code class="bash">npm install --save got@9</code></pre>
<p>安装完成后,我们开始编写代码。因为刚刚安装了 got ,我们在云函数中编写代码就简单许多。</p>
<pre><code class="javascript">const got = require('got')
// 云函数入口函数
exports.main = async (event, context) => {
const response = await got(`http://www.kuaidi100.com/query?type=${event.type}&postid=${event.id}`)
return response.body
}</code></pre>
<p>删除掉无用的代码后,我们只需要保留上面这些代码,就完成了云函数侧的代码。</p>
<p>这段代码中,通过 ES6 的新语法,拼接了 event 的参数,形成一个完成的 API 地址,并通过 got 请求了我们刚刚拼接好的 API。再将获取到的 Response 中的 Body 返回给小程序端。</p>
<h3>小程序端的调用</h3>
<p>上传部署云函数以后,我们可以在小程序端调用这个 API 以验证。在你的小程序端输入这样的代码</p>
<pre><code class="javascript">wx.cloud.callFunction({ name:"getKuaiDi",data:{
"type": "yuantong",
"id":"11111111111"
}}).then(console.log).catch(console.error)</code></pre>
<p>你会看到这样的返回,则说明你的 API 接口正常工作。</p>
<pre><code class="json">{"message":"ok","nu":"11111111111","ischeck":"0","condition":"B00","com":"yuantong","status":"200","state":"1","data":[{"time":"2020-02-11 11:59:11","ftime":"2020-02-11 11:59:11","context":"揽收任务已分配给王国贤,配送员电话17767187183","location":""}]}</code></pre>
<p>后续,你只需要在调用云函数的时候,通过 data 参数,传入快递公司,以及快递单号,就可以完成接口的查询了。</p>
<h2>总结</h2>
<p>最后,我们再总结一下。因为小程序限制了 request 的请求必须是备案域名,且必须有 https,如果你想要请求一个没有备案,或者没有 HTTPS 证书的 API,可以借助云函数环境下不受任何限制的 HTTP 请求来获取到数据,并通过云函数的返回,返回给小程序端使用。这样,就可以很方便的绕过小程序请求的一些限制。</p>
基于云开发开发 Web 应用(四):引入统计及 Crash 收集
https://segmentfault.com/a/1190000021704773
2020-02-08T07:28:28+08:00
2020-02-08T07:28:28+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
0
<h2>体验 Linux 小程序</h2>
<h3>小程序端</h3>
<p><img src="/img/remote/1460000021703081" alt="" title=""></p>
<h3>PC 端</h3>
<p>访问 <a href="https://link.segmentfault.com/?enc=JxJzmTs77qvaQcg%2FJOw5LQ%3D%3D.P95QGR7g6oVaN%2BNUkwXe0LNUxCTf9SGH2e8dGT0Fo%2FU%3D" rel="nofollow">https://tldr.linux.cn/</a> 体验</p>
<h2>正文</h2>
<p>在完成了产品的基础开发以后,接下来需要进行一些周边的工作,这些周边工具将会帮助下一步优化产品。</p>
<h2>为什么要加应用统计和 Crash 收集</h2>
<p>不少开发者在开发的时候,很少会意识到需要添加应用统计和 Crash 收集。但对于一个合格的应用来说,这些是必须的。</p>
<ul>
<li>
<strong>应用统计</strong>:应用统计会在后续进行产品迭代的时候给予数据的支持,让能够明确为什么要更新、要更新什么以及为什么这么做。</li>
<li>
<strong>Crash收集</strong>:人无完人,很难开发出一个完美的应用,就随时有可能会出现应用报错的情况。出现报错以后由于用户的水平不同,有效的反馈其实很少。Crash 收集则可以帮助收集必要的 Crash 信息,从而在后续开发的过程中,有针对性的修复 Bug。</li>
</ul>
<h2>应该使用哪些工具?</h2>
<p>在应用统计领域,可选项非常多,大部分人使用的是 <a href="https://link.segmentfault.com/?enc=iVTCpraz6JXMCvXwaJs%2FjA%3D%3D.JNDTpeQT9LRnPND5ghaPqtqlny%2BDltZ%2FMgpd3eZrGkE%3D" rel="nofollow">Google Analytics</a> ,不过由于这个产品的面向用户主要是国内的用户,因此我更倾向选择加载速度更快的产品,最终我选择的是来自腾讯的<a href="https://link.segmentfault.com/?enc=Oq7mUQBMZrHyUjJkz5nR%2BA%3D%3D.Hy2VFZucHlHOM3h0ClYV%2FLUcTiwSTcee2oBHR0qZYXE%3D" rel="nofollow">移动应用分析(MTA)</a>,腾讯的移动应用分析中,提供了 HTML5 产品的接入,因此可以完成 TLDR 的统计。</p>
<p>在 Crash 收集方面,大家用的比较普遍的是 <a href="https://link.segmentfault.com/?enc=8ESPVLe1LOdrKAUj39pZPQ%3D%3D.pBhttaV7AUCV8ASLZJBy4Zq44WcD%2FjlQlyQknGenNh4%3D" rel="nofollow">Sentry.io</a> ,不过因为 Linux 中国并没有足够多的产品业务需要使用 Sentry 来收集 Crash ,因此,一直使用的是官网的免费使用版本。同样因为网络加载速度的原因,选择使用了国内的竞品 —— <a href="https://link.segmentfault.com/?enc=Gumtiz94JirPAhgO2S1PHA%3D%3D.3Rt9x2hVLitpiPLM6Kj3eRFnucvu4NxERtbLC%2BQgw8w%3D" rel="nofollow">FunDebug</a>。</p>
<h2>接入工具</h2>
<h3>腾讯移动应用分析</h3>
<p><img src="/img/remote/1460000021704779" alt="" title=""></p>
<p>腾讯移动应用分析的接入并不复杂,首先,你需要登陆其官网,创建一个 HTML5 应用。</p>
<p><img src="/img/remote/1460000021704778" alt="" title=""></p>
<p>并在创建完成后,根据你自己的需要配置相应的能力,这里我开启了所有的数据统计,用以支持后续的产品迭代决策。</p>
<p><img src="/img/remote/1460000021704780" alt="" title=""></p>
<p>配置完成后,你会获得具体的统计的代码,接下来就可以进行接入。</p>
<p>一个比较简单的方法是直接将代码复制,并粘贴到 <code>public/index.html</code> 中,从而实现统计。不过,这样嵌入会导致如果需要自定义统计时,会无法通过 ESLint 的规则,因此我选择了第二种方式,使用 Vue 插件的方式接入。</p>
<h4>使用 Vue 插件的方式接入</h4>
<p>使用 Vue 插件接入时,需要使用 <a href="https://link.segmentfault.com/?enc=Vhfbl2Bs7WTJFrtkz1v8MA%3D%3D.aKTvf9ogQdULro%2FygUdZbUyWG2kTZQ9tfbpxExOZRg9SM2tV%2FeCPvLLEMnVxgcnY" rel="nofollow">mars-mta</a> 这个包。</p>
<p>先使用 npm 安装依赖,然后在 <code>main.js</code> 中加入相应的统计代码,就可以实现自动的统计。</p>
<pre><code>import Vue from 'vue'
import App from './App.vue'
import router from './router'
import vuetify from './plugins/vuetify';
// 以下为新增代码
import Mars from 'mars-mta'
Vue.use(Mars, {
open: true, // 开关,若为false,则不会发出上报
config: {
sid: '500710182', // 必填,统计用的appid
cid: '500710183', // 如果开启自定义事件,此项目为必填,否则不填
autoReport: 1, // 是否开启自动上报(1:init完成则上报一次,0:使用pgv方法才上报)
senseHash: 1, // hash锚点是否进入url统计
senseQuery: 1, // url参数是否进入url统计
performanceMonitor: 1, // 是否开启性能监控
}
})
// 以上为新增代码
new Vue({
router,
vuetify,
render: h => h(App),
beforeCreate: async function(){
const auth = this.$tcb.auth({ persistence: 'local' });
await auth.signInAnonymously();
}
}).$mount('#app')
</code></pre>
<p>在我的代码中,配置了 sid 和 cid ,这些信息你都需要在腾讯 MTA 的应用管理后台获取。</p>
<p><img src="/img/remote/1460000021704777" alt="" title=""></p>
<p>而下方的配置,则根据你自己的实际需求选择开启即可。</p>
<p>对应 COMMIT:<a href="https://link.segmentfault.com/?enc=kUtqldOTcXZAZn8wxbZifA%3D%3D.9WKo4pLyfYoVyGJiUDCjmaOattOcGbHVVKgXxohdl7drtlVHfcbAWMh5v41Lu7XN37JWn3hich0ItwbovTVg5Vm%2BQfFpWBjQqmv6wNpQb4n7iiEhSu576O9IXyAQbKS9" rel="nofollow">https://github.com/LCTT/tldr....</a></p>
<h3>FunDebug</h3>
<p>Fundebug 是我之前在开发小程序的时候用过的 Crash 收集应用。这次刚好也用上了。</p>
<p>因为预算的问题,这里我使用的是免费版本,有几个 tab 是看不到的,但是基本的 Debug 也是够用了。</p>
<p><img src="/img/remote/1460000021704781" alt="" title=""></p>
<p>Fundebug 的安装也很简单, 访问 <a href="https://link.segmentfault.com/?enc=5BVkAtL0mPvj5Y4fLo0beA%3D%3D.DhPKHabLPMN6yMd6uQaNAQijRT2Ed6goEVnlIGMEDCM%3D" rel="nofollow">https://www.fundebug.com/</a> ,注册账号, 并创建一个项目,你会获得一个 API Key,后续你可以使用这个 API Key 来初始化你的项目。</p>
<p><img src="/img/remote/1460000021704776" alt="" title=""></p>
<p>执行如下命令来安装依赖</p>
<pre><code class="bash">npm install fundebug-javascript fundebug-vue --save</code></pre>
<p>并在 main.js 中添加如下代码(将 API-KEY 替换为你自己的 API KEY)并保存,就可以引入 Fundebug 来进行 Crash 收集了。</p>
<pre><code>import * as fundebug from "fundebug-javascript";
import fundebugVue from "fundebug-vue";
fundebug.init({
apikey: "API-KEY"
})
fundebugVue(fundebug, Vue);</code></pre>
<p>对应 COMMIT:<a href="https://link.segmentfault.com/?enc=09H%2FEcsfy7hlKew7TZouDQ%3D%3D.jYT6K9zSeW2S%2FLBW9CX0sKoekcjtyZ6OfTr6iRlh9Vs5p8508WhIMtd57weZBLvFxZF%2BwBg0WRF0AqXvPIKZ034NWDRDmjW0rBWAauECrEZjaOck56uHb7s9e6EZrR3H" rel="nofollow">https://github.com/LCTT/tldr....</a></p>
<h2>优化</h2>
<p>在开发的过程中经常会出现 Error,这个是难以避免的。在开启了 Crash 收集以后,这些 ERROR 也会被收集到 Fundebug 上,这样会浪费每个月 3000 条的免费额度,因此,需要一个方法在开发环境不启用这些拓展。类似的,也不希望 MTA 统计本地开发的 Page View ,会影响到后续的数据分析。因此,我使用了一些方法来避开这个问题。</p>
<pre><code class="javascript">if (process.env.NODE_ENV === 'production') {
Vue.use(Mars, {
open: true, // 开关,若为false,则不会发出上报
config: {
sid: 'xxx', // 必填,统计用的appid
}
})
fundebug.apikey = "xxx"
fundebugVue(fundebug, Vue);
}</code></pre>
<p>通过将引用统计的代码包裹在环境的判断代码中,可以实现在渲染的时候,只有生产环境才会渲染出相应的统计代码,从而实现了在开发环境不调用 fundebug 和 mta 统计,避免了开发环境的数据干扰。</p>
<p>对应 COMMIT:<a href="https://link.segmentfault.com/?enc=62ejOKLqMNlcCau2mmXjIg%3D%3D.ryr90ohQmag9%2F3EyoDJIh2lnPx2%2FljCFrj9SAkpZvXC2r8yBF309Z9rmTYtAI0hvPx6NhSA%2FsGUpIjDuFII869McrRG84xZMZABcJD8YqIn%2BiqpH9Klm9aACbaNyQCR%2F" rel="nofollow">https://github.com/LCTT/tldr....</a></p>
<h2>总结</h2>
<p>在这篇文章中,介绍了两个服务,分别是用于统计的腾讯移动分析 MTA和用于做 Crash 收集的 fundebug,介绍了应该如何在 Vue 应用中接入这两种服务。此外,还根据实际的需求,优化了两个统计的位置,确保产品在开发环境不会工作,从而避免了影响到我们统计数据的准确性。</p>
<p>这篇文章涉及到的代码你都可以在 <a href="https://link.segmentfault.com/?enc=B22TqFoxbtSfqjBCxRQ8%2Bg%3D%3D.YcHeIuNrOl%2FdZuO9%2BlFeS4rBc%2BiAMaTdyUE9ijlzepAam7KJE%2FrnLduKnQhWtJGHcaNazntbOI14p9RRnUIfjw%3D%3D" rel="nofollow">https://github.com/LCTT/tldr....</a> 看到</p>
基于云开发开发 Web 应用(三):云开发相关数据调用
https://segmentfault.com/a/1190000021704534
2020-02-08T00:27:05+08:00
2020-02-08T00:27:05+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
0
<h2>体验 Linux 小程序</h2>
<h3>小程序端</h3>
<p><img src="/img/remote/1460000021703081" alt="" title=""></p>
<h3>PC 端</h3>
<p>访问 <a href="https://link.segmentfault.com/?enc=WP6gzX2XokhJw5QUwl9aTg%3D%3D.D%2FFVB3CRbZHWBIBRX%2F8CdJKwPFX9ePBdJPMtK7y2mN4%3D" rel="nofollow">https://tldr.linux.cn/</a> 体验</p>
<h2>介绍</h2>
<p>在完成了 UI 界面的实现后,接下来可以开始进行和云开发相关的数据对接。完成数据对接后,应用基础就打好了,接下来的就是发布上线以及一些小的 feature 的加入。</p>
<h2>配置</h2>
<p>在进行相关的配置调用的时候,你需要先登陆腾讯云控制台,进行一些配置。</p>
<p>使用你的小程序账号登陆腾讯云,并在其中找到云开发产品。进入到产品控制台。</p>
<p><img src="/img/remote/1460000021704541" alt="" title=""></p>
<p>在产品控制台中找到你的环境,点击进入详情页</p>
<p><img src="/img/remote/1460000021704538" alt="" title=""></p>
<p>在环境详情页面选择<strong>用户管理</strong>、<strong>登陆设置</strong>、<strong>匿名登陆</strong></p>
<p><img src="/img/remote/1460000021704537" alt="" title=""></p>
<p>启用匿名登陆。</p>
<p><strong>云开发的数据查询目前必须登陆后才可以查询,因为希望给用户提供的是免登陆的解决方案,因此,必须开通匿名登陆,确保可以进行数据查询。</strong></p>
<p>由于需要在网页中调用相应的函数,因此,也需要在同一个页面的 WEB 安全域名中添加应用的上线域名(本地调试用的 localhost 无需添加)。</p>
<p><img src="/img/remote/1460000021704540" alt="" title=""></p>
<h2>为应用程序添加匿名登陆的逻辑</h2>
<blockquote>此部分代码位置:<a href="https://link.segmentfault.com/?enc=E%2BRIIPkLPwGU2Dg590SINw%3D%3D.mpC7EmgARm65yIA%2F3Aff1QQSVHN1VZdi6kcadmCtnFgTfOhTvxwY80SyYLO60JJZ0FX1CgsaLYm1yZCYCAWP7w%3D%3D" rel="nofollow">https://github.com/LCTT/tldr....</a>
</blockquote>
<p>由于希望用户可以打开网页就可以查询数据,因此,必须在用户无感的情况下,完成匿名登陆逻辑。</p>
<p>根据对 Vue 生命周期的预研,将相应的逻辑放在了 <strong>beforeCreate</strong> 中,确保在应用初始化完成后,就可以自动完成匿名登陆。</p>
<p><img src="/img/remote/1460000021704542" alt="" title=""></p>
<p>具体实现代码如下</p>
<pre><code class="javascript">// main.js
new Vue({
router,
vuetify,
render: h => h(App),
beforeCreate: function(){ // 新增匿名登陆逻辑
const auth = this.$tcb.auth(); // 新增匿名登陆逻辑
auth.signInAnonymously(); // 新增匿名登陆逻辑
} // 新增匿名登陆逻辑
}).$mount('#app')</code></pre>
<p>加入完成后,你可以使用云开发的数据库等命令,来完成相应的数据库调用,验证自己的调用是否正常。</p>
<blockquote>在这里需要注意,由于 Vue 默认的 ESLint 规则限制,默认是无法在 Vue 项目代码中使用 <code>console.log</code> 的,你需要使用一些命令来跳过相应的检查<br>只需要在你需要打印变量的前一行加入 <code>// eslint-disable-next-line </code> 就可以避免对应的检查了。</blockquote>
<h2>调用数据</h2>
<blockquote>此部分代码位置:<a href="https://link.segmentfault.com/?enc=8sa%2B8N3nKHxB8FnBF%2FVDOA%3D%3D.ZZ8V%2BuAmBxrv6LX7BOYY9zClOybFw6QvvuOyTPj8iO2iStXvMX8toyi5TuJ2eBtTxYcVyeMGk4vXJUzKF1BR8kWyy9OuLCr7dmFKAXRhJ8I%3D" rel="nofollow">https://github.com/LCTT/tldr....</a>
</blockquote>
<p>完成初始化后,就需要完成相应的数据调用,这里不再针对每一个进行讲解,选择一个例子来说明。</p>
<pre><code class="javascript">const db = this.$tcb.database();
const cmd = db.collection('command');
if(this.id){ // 这里的 id 是 props 传入的参数,为命令对应的 doc id
cmd.doc(this.id).get().then(res => {
this.command = res.data
})
}else{
cmd.where({
name: this.$route.params.cmd // 命令可以从 Route 中获取,但实际场景下,因为开启了 `props: true`,也可以直接从 props 中获取。
}).limit(1).get().then(res => {
this.command = res.data[0]
}).catch((err) => {
alert("命令查询出错,请联系我们")
// eslint-disable-next-line
console.error(err)
})
}</code></pre>
<p>在这段代码中,首先是前期挂载的 $tcb 中抽取 database ,并基于 database 构建 collection.<br>然后就是使用 collection 进行查询。</p>
<p>由于这里涉及到不同的页面逻辑,使用了一个 if 来判断数据。上下两种分别是获取单个数据和使用多个数据的方法。获取到数据以后,将数据更新,同步到 Vue 的 Data 中,完成相应的逻辑的调用。</p>
<h2>云开发登陆的坑</h2>
<blockquote>此部分代码位置:<a href="https://link.segmentfault.com/?enc=PB8R93uq0NNxxYrzlORGOw%3D%3D.r3Su8%2FyuMHQjBn7x3QyJdf7835ihvLEwqhhhMDtOtzvnX6o8enuWdl869ET6yo4I1FTgMuIc%2FbCb8H0cWQETww%3D%3D" rel="nofollow">https://github.com/LCTT/tldr....</a>
</blockquote>
<p>由于为用户提供的是快速查询功能,因此希望用户无论何时都是无感进行查询的。但实际测试的时候,发现用户如果直接通过命令行登陆的时候,会导致报错。根据控制台返回的信息来看,是用户登陆状态尚未完成,就进行了数据查询。</p>
<p>通过查询云开发的文档,发现云开发的 auth 对象在登陆的时候,可以传入一个 persistence 来控制身份信息的持久化。</p>
<p><img src="/img/remote/1460000021704539" alt="" title=""></p>
<p>由于默认使用的是 session ,所以导致用户的登陆态丢失。为了确保应用的状态正常进行,将 <code>persistence</code> 设置为 <code>local</code>,确保应用在一次登陆后可以将用户登陆状态下放到用户的 storage 中,这样可以避免用户总是会遭遇请求失败的问题。</p>
<pre><code class="javascript">// main.js
new Vue({
router,
vuetify,
render: h => h(App),
beforeCreate: async function(){
const auth = this.$tcb.auth({ persistence: 'local' });
await auth.signInAnonymously();
}
}).$mount('#app')</code></pre>
<h2>总结</h2>
<p>在实际开发中,如果你需要通过云开发的 Web SDK 调用相应的数据,则需要先行开启云开发的匿名登陆并配置 Web 安全域名;在数据调用的部分和在小程序端调用云开发没有太大的区别;并通过设置 presistence 设置搞定了登陆状态丢失的问题。</p>
基于云开发开发 Web 应用(二):界面 UI 开发
https://segmentfault.com/a/1190000021704466
2020-02-08T00:02:29+08:00
2020-02-08T00:02:29+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
0
<h2>体验 Linux 小程序</h2>
<h3>小程序端</h3>
<p><img src="/img/remote/1460000021703081" alt="" title=""></p>
<h3>PC 端</h3>
<p>访问 <a href="https://link.segmentfault.com/?enc=22HRNJrbzdGeX5nl1WvbMQ%3D%3D.8og8UE8DMbFI7sPY1DYL36n26w9P1SKGBIZZroP3bC0%3D" rel="nofollow">https://tldr.linux.cn/</a> 体验</p>
<h2>工作量分析</h2>
<p>在我们进行这部分开发的时候,接下来我们需要进行相应的功能安排和分类。</p>
<p><img src="/img/remote/1460000021704470" alt="" title=""></p>
<p>简单看来,我需要开发 3 个页面:</p>
<ol>
<li>
<strong>首页</strong>:首页负责用户默认访问。</li>
<li>
<strong>列表页</strong>:列表页面则是在搜索过程中,如果有多个结果,则进入到列表页面。如果有单个结果,则应该进入到详情页面。</li>
<li>
<strong>结果页</strong>:结果页负责显示命令的具体的翻译结果。</li>
</ol>
<p>根据实际的工作拆分组件的化,我需要有一个 Layout 组件来负责整体的页面的环境渲染。但是,考虑到组件的复用,于是决定将首页的 Title 进行优化,使首页和详情页保持一致。</p>
<p><img src="/img/remote/1460000021704469" alt="" title=""></p>
<p>在新版的布局情况下,我就可以将顶部的的 title 和底部的 Link 放在最外侧的组件中。</p>
<h2>创建 Router & Page</h2>
<p>在思考情况后,接下来我们来创建 Router 和 Page。首先,删除 views 页面的 About.vue(因为这个页面我们不需要)。然后创建 List.vue 和 Result.vue ,用作后续的开发准备。</p>
<p><img src="/img/remote/1460000021704471" alt="" title=""></p>
<p>创建完成后,修改 <code>router/index.js</code> 中的 routes 部分</p>
<pre><code class="javascript">const routes = [
{
path: '/',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '../views/Home.vue')
},
{
path: '/list/:cmd',
name: 'list',
component: () => import(/* webpackChunkName: "list" */ '../views/List.vue')
},
{
path: '/cmd/:cmd',
name: 'command',
component: () => import(/* webpackChunkName: "cmd" */ '../views/Result.vue')
}
]</code></pre>
<p>完成定义后,我们就可以通过形如 <code>https://tldr.linux.cn/list/ls</code> 和 <code>https://tldr.linux.cn/cmd/ls</code> 这样的方式来访问具体的命令了。</p>
<blockquote>这里需要注意,我提前开启了 History Mode</blockquote>
<h2>定义页面</h2>
<p>接下来需要编写 Home、List 和 Result 这三个页面。由于这三个页面在内容方面没有太多的可以借鉴的点,所以我们更多关注于使用页面中 Script 的部分。</p>
<pre><code><template>
<div class="home">
<v-text-field
v-model="cmd"
@keydown="onKeyDown"
autofocus
></v-text-field>
</div>
</template>
<script>
export default {
name: 'home',
data:function(){
return {
cmd:""
}
},
methods:{
onKeyDown: function(e) {
}
},
computed:{
isLoaded:function(){
return !this.loaded
}
}
}
</script></code></pre>
<p>上述代码是我在三个页面几乎都会使用到的结构,删除其中的一些无用的代码以后,基本上在每个页面都可以看到。这里我重点说一下其中的一些比较特殊的用法。</p>
<p>首先第一个是在 <code>v-text-field</code> 上加入的 <code>@keydown="onKeyDown"</code> 这个绑定,这个绑定将会帮助实现用户点击回车以后,自动触发事件。这样在用户输入完命令后,按下回车就自动执行后续的操作,而不需要再移动鼠标指针去点击按钮启动搜索。</p>
<p>其次,在 <code>v-text-field</code> 上加入了 <code>autofocus</code> ,来实现进入页面后,自动为输入框加入<code>focus</code>,从而实现页面加载完成后,用户就可以输入命令。</p>
<p>这样的一些配置,可以让<strong>用户的体验做到最好</strong>。</p>
<p>除此之外,我还用到了 <code>computed</code> ,来做数据调整,确保我可以控制内容。</p>
<h2>一些小的特性的点</h2>
<h3>使用骨架图来优化体验</h3>
<p>由于我们的应用在列表页面和详情页面存在数据的查询时间,为了让应用在加载的时候,不会因为加载中而退出页面,我加入了 v-skeleton-loader 组件,这样用户在数据查询的时候,看骨架图来缓解用户的焦虑。</p>
<p><img src="/img/remote/1460000021704472" alt="" title=""></p>
<p>在组件层面,我配置了 v-if 来做显示的控制,并将 type 设置为 <code>card,article,card,article</code> 来实现多样化的组件加载支持。</p>
<pre><code> <v-skeleton-loader
v-if="isLoaded"
type="card,article,card,article"
min-height="800"
></v-skeleton-loader></code></pre>
<h2>总结</h2>
<p>在这一部分中,借助 Vue 的 method 、onkeydown 和 computed 实现了页面基本逻辑的构建。并借助 Vuetify 的一些基础组件来构建页面。</p>
<p>在这一部分,我想告诉大家的更多是在 UI 的部分,我们在做的时候不仅仅需要考虑的是界面,更多还需要考虑在 UX 侧体验的优化,组件库提供给我们的配置项目,可以优化产品体验。</p>
基于云开发开发 Web 应用(一):项目介绍 & 初始化
https://segmentfault.com/a/1190000021703078
2020-02-07T20:19:20+08:00
2020-02-07T20:19:20+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
2
<h2>体验 Linux 小程序</h2>
<h3>小程序端</h3>
<p><img src="/img/remote/1460000021703081" alt="" title=""></p>
<h3>PC 端</h3>
<p>访问 <a href="https://link.segmentfault.com/?enc=3eMQdcHKqz3QidbA1aL5bg%3D%3D.F6OhmcKMw5TyiMqcVlqzb0qML46eWPngO%2BKJ3tRr4Ro%3D" rel="nofollow">https://tldr.linux.cn/</a> 体验</p>
<h2>背景描述</h2>
<p>Linux 中国曾在过去的 1 年内运行了一个 TL;DR 的中国版。不过当时做的版本是小程序的版本,一直以来,受限于小程序·云开发没有 Web SDK ,因此无法将应用能力迁移到更多的平台上,刚好最近云开发提供了 Web SDK ,于是便可以借此机会,将业务实现 PC 化,服务更多人群。</p>
<h2>项目设计</h2>
<p>在进行项目开发时,先对项目进行了基本的 UI 设计</p>
<p><img src="/img/remote/1460000021703082" alt="主页" title="主页"></p>
<p><img src="/img/remote/1460000021703084" alt="详情页" title="详情页"></p>
<p>这里用到的是 balsamiq 的手绘线框图来完成产品设计,以避免我个人过度追求完美,而让产品延期迟迟不能上线的问题(这样的事情在历史上发生了非常多次)</p>
<h2>技术选项</h2>
<p>由于需要的是一个前端页面,因此,在技术选型方面,几乎没有太多的异议。使用最为熟悉的技术栈来完成。</p>
<ul>
<li>前端框架:Vue</li>
<li>路由器:Vue Router</li>
<li>CSS 框架:Vuetifyjs</li>
</ul>
<h2>mirror 配置</h2>
<p>因为身处国内, npm 的速度必然不太好,因此需要进行相应的 mirror 设定,确保 npm 和 yarn 在安装依赖。这里使用的是腾讯云提供的镜像。</p>
<pre><code class="bash"># Npm 设置
npm config set registry http://mirrors.cloud.tencent.com/npm/
# yarn 设置
yarn config set registry http://mirrors.cloud.tencent.com/npm/ -g</code></pre>
<h2>初始化 Vue 项目</h2>
<p>首先,需要安装 Vue Cli,以进行项目的生成,这里我已经完成安装,就不再赘述。(Vue cli 的安装教程<a href="https://link.segmentfault.com/?enc=62B6JOSJXTWNV20ZaXb%2F%2FA%3D%3D.861vWA097SO%2FFTSDyNHwdUJGgUxCvgFXYE93jCBwLNA%3D" rel="nofollow">点击这里</a>)</p>
<p>执行如下命令初始化项目</p>
<pre><code class="bash">vue create tldr</code></pre>
<p>等待其完成安装以后,进入项目,并启动项目。</p>
<pre><code class="bash">cd tldr
yarn serve</code></pre>
<p>随即,可以在系统浏览器中的 localhost:8080 中查看项目</p>
<p><img src="/img/remote/1460000021703083" alt="预览" title="预览"></p>
<blockquote>记得引入 git 做版本控制,文章里就不介绍了。没意思。</blockquote>
<h2>安装 Vue Router</h2>
<p>在完成 Vue 项目的初始化以后,接下来需要进行 Vue Router 的配置了。</p>
<p>Vue Router 的配置在引入了 Vue 3 以后,显得非常的简单,直接执行如下命令即可</p>
<pre><code>vue add router</code></pre>
<p>执行过程中,会问你是否需要启用 History Mode,根据需要选取,我使用的是 History Model</p>
<p><img src="/img/remote/1460000021703086" alt="" title=""></p>
<p>设置完成以后,保存并重启 Vue 的开发服务器,你会在预览中看到 Router 添加的 Home 和 About</p>
<p><img src="/img/remote/1460000021703085" alt="" title=""></p>
<h2>安装 Vuetifyjs</h2>
<p>接下来安装的是 Vuetify ,由于框架提供了相应的支持,因此在开发时也非常简单,只需要执行如下命令就可以完成初始化。</p>
<pre><code>vue add vuetify</code></pre>
<p>会问你选择那种预设,直接使用 Default 即可。</p>
<p><img src="/img/remote/1460000021703089" alt="" title=""></p>
<p>保存并重启开发服务器,你会看到这样的界面,则说明配置完成。</p>
<p><img src="/img/remote/1460000021703087" alt="" title=""></p>
<h2>部署测试应用</h2>
<p>在进行下一步开发的时候,需要先进行一下项目的部署,从而获得一个测试的域名,方便后续的开发。</p>
<p>这里项目的开发我并没有使用云开发自己的 Web Hosting (因为我们不是按量付费套餐,所以没有办法开启),而是使用了 Now.sh 的,这里就不再过多赘述。</p>
<p><img src="/img/remote/1460000021703088" alt="" title=""></p>
<h2>引入云开发 SDK</h2>
<p>云开发提供了 Web SDK ,可以通过 npm 安装,并引用。</p>
<p>执行如下命令来安装。</p>
<pre><code class="bash">yarn add tcb-js-sdk</code></pre>
<p>安装完成后,在 <code>main.js</code> 中引入 tcb,并通过修改 Vue 的原型来实现挂载 Vue</p>
<pre><code class="javascript">import Vue from 'vue'
import App from './App.vue'
import router from './router'
import vuetify from './plugins/vuetify';
const tcb = require('tcb-js-sdk') // 新增的引入 TCB
Vue.config.productionTip = false
Vue.prototype['$tcb'] = tcb.init({ // 新增的修改原型
env: 'prod-2c59c7' // 新增的修改原型
}) // 新增的修改原型
new Vue({
router,
vuetify,
render: h => h(App)
}).$mount('#app')</code></pre>
<p>这样就可以在应用运行的整个周期中使用 <code>this.$tcb</code> 来调用云开发的相关逻辑。</p>
<h2>总结</h2>
<p>在完成了项目的初始化以后,回过头来看一看这在初始化项目过程中,都做了哪些事情。</p>
<ol>
<li>配置 npm 镜像,以确保 Node package 的安装速度</li>
<li>使用 vue cli 来初始化项目</li>
<li>安装 Vue Router & Vuetifyjs</li>
<li>部署应用</li>
<li>安装 tcb-js-sdk 以调用云开发数据</li>
</ol>
云开发中如何筛选某个字段的结果为/不为空白字符串
https://segmentfault.com/a/1190000021048443
2019-11-19T09:31:14+08:00
2019-11-19T09:31:14+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
0
<h2>需求描述</h2>
<p>有些时候,我们会在数据中添加一些字段,用于特殊场景下的描述、配置等。在添加数据的时候,可能用户会忘记输入对应的内容,导致对应的字段虽然存在,但是其内容为空。比如,正常和错误的数据可能分别是这样的</p>
<pre><code class="javascript">// 正确的数据, tags 的结果是一个字符串
{
"tags":"ABC"
}
// 错误的数据, tags 的结果是一个空字符串
{
"tags":""
}</code></pre>
<p>在实际的使用过程中,我们可能希望筛选出所有 tags 的结果不为空字符串的结果,或者相反,具体应该如何实现呢?</p>
<h2>解决方案</h2>
<h3>查询出所有的字段为空字符串的数据</h3>
<p>出于管理的考虑,你可能会希望实现将所有的字段为空的数据查询。如果你想要查询出所有 <code>tags</code> 为空的结果,有两种方法:</p>
<ol>
<li>直接使用 where 条件查询</li>
<li>使用云开发数据库命令中的 <code>eq</code> 来进行查询</li>
</ol>
<p>具体代码示例如下:</p>
<pre><code class="javascript">// 直接使用 where 查询
db.collection("items").where({
tags:""
}).get()
// 不使用 where 查询
let _ = db.command;
db.collection("items").where({
tags:_.eq('')
}).get()</code></pre>
<p>上述两种方法的效果相同,都可以实现在小程序中查询。</p>
<h3>查询出所有的字段不为空字符串的数据</h3>
<p>和管理端不同,用户端可能主要是展示给普通用户的,可能需要展示特定字段不为空的数据,那么,就可以考虑类似的方式,来查询出所有的字段不为空字符串的数据。</p>
<p>具体的代码示例如下:</p>
<pre><code class="javascript">let _ = db.command;
db.collection("items").where({
tags:_.neq('')
}).get()</code></pre>
<h2>总结</h2>
<p>在小程序·云开发中,实现一些特殊功能的时候,你可以考虑借助小程序·云开发的数据库命令的方法,实现各种不同的功能。</p>
云开发如何实现管理员通知消息
https://segmentfault.com/a/1190000021048407
2019-11-19T09:29:06+08:00
2019-11-19T09:29:06+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
0
<h2>需求描述</h2>
<p>小程序目前的主要能力还都在小程序端实现,但是我们在进行开发的小程序不可能只有小程序端能力,我们也会有一些管理端能力。比如说,当用户在小程序中提交了消息以后,我们的小程序应该可以通知到小程序的管理员,以便让管理员进行下一步操作。</p>
<h2>解决方案</h2>
<h3>架构说明</h3>
<p>由于小程序本身不支持长久性的消息通知能力,因此,我们可以考虑借助一些第三方的服务和能力,来完成我们自己的需求。</p>
<blockquote>这个需求很适合使用小程序新发布的<strong>长期订阅消息</strong>能力,但是目前该能力开放的类目还不足以支持我们的需要。</blockquote>
<p>一般而言,使用短信是我们目前到达率比较高的能力,且更为普遍的能力,其他通道的能力大多受限或不符合国情,为了确保通知信息的到达率,我们这篇文章就使用短信来完成需求。</p>
<h4>架构图示</h4>
<p><img src="/img/remote/1460000021048415" alt="" title=""></p>
<h3>具体操作</h3>
<h4>1. 开通腾讯云短信服务并获取配置信息</h4>
<p>我们想要发送短信,就需要先有一个短信服务,用于发送短信,这里我们可以使用腾讯云提供的云短信服务来发送短信。</p>
<p><strong>开通腾讯云短信,并创建应用</strong></p>
<p>首先,你需要访问 <a href="https://link.segmentfault.com/?enc=P5hyfuQUhx41Y2Fb5zyUhA%3D%3D.DItFizo7oxxTaSDXkrPls7WTLMjL2GwqCNIOkN40MH0sGHxbf1Eu3vAMwmv2SzC7" rel="nofollow">https://console.cloud.tencent...</a> ,点击开通腾讯云·云短信。</p>
<p>在开通完成后,点击界面中的【<strong>添加应用</strong>】,添加一个新的短信应用,你可以根据自己的实际情况,添加短信应用的名称和简介。</p>
<p><img src="/img/remote/1460000021048419" alt="" title=""></p>
<p><strong>获取 AppID、App Key</strong></p>
<p>添加完成后,点击你创建好的应用,进入到应用详情页,在应用的详情页中的应用信息栏目中,你可以找到 AppID 和 AppKey ,复制并保存这两个值,稍候我们会用到。</p>
<p><img src="/img/remote/1460000021048416" alt="" title=""></p>
<h4>2. 配置短信模板、短信签名</h4>
<p>开通了腾讯云短信服务以后,我们需要去创建短信模板,以及短信签名</p>
<blockquote>腾讯云短信并不是让你随意发所有的内容的,而是你需要创建一个模板,并使用特定的模板来完成短信的发送。<p>短信签名则是原来让收到短信的用户知道他所收到的短信来自于他的那一个服务,一般来说,设置为产品的品名。</p>
</blockquote>
<p>在腾讯云控制台中,进入到【云短信】控制台</p>
<p><strong>创建短信签名</strong></p>
<p>首先,点击【<strong>国内短信</strong>】,进入到短信的页面,点击【<strong>创建签名</strong>】,然后在弹出的窗口中输入你的签名的具体信息,比如这里我就是以公众号【程序百晓生】来创建签名。</p>
<p><img src="/img/remote/1460000021048417" alt="" title=""></p>
<p>签名创建完成后,你需要等待腾讯云官方的审核,审核通过以后,你添加的签名才可以被使用。</p>
<p><strong>创建短信模板</strong></p>
<p>创建完签名,你需要创建一个短信的正文模板,用于发送短信。</p>
<p>输入模板名称、短信类型,然后选择标准模板中的模板,这里我们选择“您有新的{1}订单,请注意查收!”这个模板。</p>
<blockquote>除了使用标准模板,你也可以自己编写一个模板,为了方便文章撰写,这里使用标准模板。</blockquote>
<p><img src="/img/remote/1460000021048414" alt="" title=""></p>
<p>然后点击提交,等待审核就可以了。</p>
<h4>3.编写云函数发送短信</h4>
<p>在完成了基础的配置后,我们在微信开发者工具中实现一个云函数,用于调用腾讯云的短信服务,实现具体的通知。</p>
<p>首先,我们创建一个新的云函数,名为 <code>notifyAdmin</code>,意为用于通知管理员的云函数。</p>
<p><img src="/img/remote/1460000021048418" alt="" title=""></p>
<p>然后,选择我们刚刚创建的 <code>notifyAdmin</code> 云函数,在函数上右击,选择【在终端中打开】,进入到控制台,并输入如下命令,安装所需的短信 SDK。</p>
<pre><code class="powershell">npm install --save sms-node-sdk</code></pre>
<p><strong><img src="/img/remote/1460000021048420" alt="" title=""></strong></p>
<p>然后,修改云函数的 <code>index.js</code>,加入如下代码</p>
<pre><code class="javascript">// 云函数入口文件
const cloud = require('wx-server-sdk')
const {
SmsClient
} = require('sms-node-sdk');
const AppID = 1400286810; // SDK AppID是1400开头
// 短信应用SDK AppKey ,替换为你自己的 AppKey
const AppKey = 'xxxx';
// 需要发送短信的手机号码
const phoneNumber = '10000000';
// 短信模板ID,需要在短信应用中申请
const templId = 476457;
// 签名,替换为你自己申请的签名
const smsSign = '程序百晓生';
// 实例化smsClient
cloud.init()
// 云函数入口函数
exports.main = async (event, context) => {
let orderId = event.orderId;
let smsClient = new SmsClient({ AppID, AppKey });
return await smsClient.init({
action: 'SmsSingleSendTemplate',
data: {
nationCode: '86',
phoneNumber,
templId: templId,
params: [orderId],
sign: smsSign // 签名参数未提供或者为空时,会使用默认签名发送短信
}
})
}</code></pre>
<p>完成代码的修改后,就可以部署你的云函数了,右键你的云函数,选择【上传并部署云函数:云端安装依赖】</p>
<h4>4. 在小程序端触发短信</h4>
<p>在前面我们提到,在一些特定的场景下,我们希望用户的操作可以给管理员发送消息通知。在具体的实现的时候,我们可以根据自己的实际业务需求,来设定我们的通知发送的条件,比如说,在用户支付成功后发送消息,则相关代码如下:</p>
<pre><code class="javascript">let orderId = 'this is a orderId';
wx.requestPayment({
success:res => {
console.log("User Payment Success");
// 调用云函数发送短信
wx.cloud.callFunction({
name:"notifyAdmin",
data:{
orderId: orderId
}
});
}
})</code></pre>
<h3>总结</h3>
<p>经过本次的分享,我们了解到了如何借助短信服务,实现云开发的后台通知能力,实际上,除了短信服务,你还可以借助一些其他的工具,比如邮件、企业微信机器人等能力,实现后台管理信息的推送。</p>
<p>明天,我们将分享<strong>如何借助通过微信发送订单消息</strong>。</p>
云开发数据库命令之地理位置查询
https://segmentfault.com/a/1190000020473033
2019-09-23T22:36:01+08:00
2019-09-23T22:36:01+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
1
<p>在进行云开发的数据更新的时候,我们可以进行不同条件的查询,从而提升我们更新的效率。在云开发支持了 Geo Point 等相关数据类型以后,我们可以用云开发实现多种不同地理位置的数据存储和利用,对于我们开发基于地理位置的小程序来说,有很大的帮助。</p>
<p>今天的课程中,我们来介绍小程序数据更新命令中的「地理位置查询」命令。</p>
<h2>geoNear:查询特定点附近的数据</h2>
<p>查询特定点附近的数据可以说是我们在进行应用开发时,最为常用的功能,它可以应用于诸如<strong>查询当前用户坐标周围的店铺</strong>、<strong>查询距离我最近的公交站</strong>等场景,这个时候,我们需要使用 <code>geoNear</code> 来进行数据库查询。</p>
<h3>数据结构需求</h3>
<p>如果你想要使用 <code>geoNear</code>,则要求你在数据库中存储的数据对于地理位置的存储是基于 <code>db.Geo.Point</code> 进行的,这样你就可以完成使用 <code>geoNear</code> 进行查询。</p>
<h3>数据查询实例</h3>
<p>假设我们当前数据库内数据的结构是这样的,每一个数据下有一个 point 属性,这个属性是通过 <code>db.Geo.Point</code> 添加的。</p>
<p><img src="/img/remote/1460000020473036" alt="" title=""></p>
<p>如果我们希望查询距离当前位置,1000米 ~ 2000米的数据,则可以执行这样的命令</p>
<pre><code class="js">const db = wx.cloud.database()
const _ = db.command
db.collection('items').where({
location: _.geoNear({
geometry: db.Geo.Point(113.323809, 23.097732),
minDistance: 1000,
maxDistance: 2000,
})
}).get()</code></pre>
<p>这里的 <code>geometry</code> 中的 <code>Point</code> 的数据是获取到的当前地理位置信息,它必须是 <code>db.Geo.Point</code> 类型的,你可以通过小程序的 <code>wx.getLocation</code> 方法获取当前的地址信息,并将其作为参数设置在这里。</p>
<p><code>minDistance</code> 则是距离中心点的最小距离,单位是米,所以这里将其设置为 1000。类似的,<code>maxDistance</code> 则是距离中心点的最大距离,单位也是米,所以这里将其设置为 2000。</p>
<p>通过这样的方法,我们就可以查询出数据了。</p>
<p>在我们去做一些基于地理位置的应用的时候,<code>db.command.geoNear</code> 可以很大程度上简化我们的开发。</p>
<h2>geoWithin:查询特定区域内的数据</h2>
<p>在开发地理位置应用时,除了基于某一个点的位置进行查询以外,我们还会查询某一个区域内的数据,比如<strong>查询北京市昌平区内的所有的酒吧</strong>、<strong>查询深圳南山区内所有的博物馆</strong>,这样的需求也是切实存在,并且十分常见的需求。这个时候,我们可以考虑,使用 <code>geoWithin</code> 来进行数据查询。</p>
<h3>数据结构需求</h3>
<p>如果你想要使用 <code>geoWithin</code>,则要求你在数据库中存储的数据对于地理位置的存储是基于 <code>db.Geo.Point</code> 进行的,这样你就可以完成使用 <code>geoWithin</code> 进行查询。</p>
<h3>数据查询实例</h3>
<p>假设我们当前数据库内数据的结构是这样的,每一个数据下有一个 point 属性,这个属性是通过 <code>db.Geo.Point</code> 添加的。</p>
<p><img src="/img/remote/1460000020473036" alt="" title=""></p>
<p>如果我们希望查询在东经 112 度 ~ 东经 114 度,北纬 22 度 到 北纬 24 度范围内的数据,则可以执行这样的数据查询</p>
<pre><code class="js">const db = wx.cloud.database()
const _ = db.command
const { Point, LineString, Polygon } = db.Geo
db.collection('items').where({
location: _.geoWithin({
geometry: Polygon([
LineString([
Point(112, 22),
Point(112, 24),
Point(114, 24),
Point(114, 22),
Point(112,22)
])
]),
})
}).get()</code></pre>
<p>我们通过 <code>Polygon</code> 和 <code>LineString</code> 构建出了一个正方形,从而实现了查询一个特定的正方形区域内的数据。</p>
<p>如果你希望查询一个其他形状的范围内的数据,只需要传入多个 <code>Point</code> 的数据就可以完成,比如如果你要查询一个五边形内部的数据,也只需在构建 LineString 时,传入 6 个 Point的数据即可。</p>
<blockquote>
<strong>为什么五边形却是 6 个 Point 呢?</strong><p>因为在云开发中,如果你要构建一个多边形,则需要使得整个多边形是闭合的,也就是说,你的起始点是一样的,因此,如果你想要构建一个四边形,则需要五个点。如果是构建五边形,则是六个点。</p>
</blockquote>
<h2>geoIntersects:查询与特定区域相交的数据</h2>
<p><code>geoIntersects</code> 是用于查询所有数据中和给定数据相交的数据,我们可以将其用作判断某一些特定的点、线、面是否在一个特定区域内。举个例子,假设你已经有了用户当前的活动范围,比如某一条街道,那么你可以基于 <code>geoIntersects</code> 来构建一条线,并基于这条线查询,所有数据中,是否有数据与这个线相交,如果相交,则说明对应的数据点是在用户所在的街道上。你就可以将这个数据告诉用户,让用户去找这些点。</p>
<p>相比于 <code>geoWithin</code>,<code>geoIntersects</code> 对于当前用户的位置数据更为随意,支持 <code>Point</code>、<code>LineString</code>、<code>MultiPoint</code>、 <code>MultiLineString</code>、 <code>Polygon</code>、 <code>MultiPolygon</code> 等多种不同的数据结构,在进行查询的时候,更加的方便。</p>
<h3>数据结构需求</h3>
<p>如果你想要使用 <code>geoIntersects</code>,则要求你在数据库中存储的数据对于地理位置的存储是基于 <code>db.Geo.Point</code> 进行的,这样你就可以完成使用 <code>geoIntersects</code> 进行查询。</p>
<h3>数据查询实例</h3>
<p>假设我们当前数据库内数据的结构是这样的,每一个数据下有一个 point 属性,这个属性是通过 <code>db.Geo.Point</code> 添加的。</p>
<p><img src="/img/remote/1460000020473036" alt="" title=""></p>
<p>如果我们希望查询在东经 112 度 ~ 东经 114 度,北纬 22 度 到 北纬 24 度范围内的数据,则可以执行这样的数据查询</p>
<pre><code class="js">const db = wx.cloud.database()
const _ = db.command
const { Point, LineString, Polygon } = db.Geo
db.collection('items').where({
location: _.geoIntersects({
geometry: Polygon([
LineString([
Point(112, 22),
Point(112, 24),
Point(114, 24),
Point(114, 22),
Point(112,22)
])
]),
})
}).get()</code></pre>
<p>我们通过 <code>Polygon</code> 和 <code>LineString</code> 构建出了一个正方形,从而实现了查询一个特定的正方形区域内的数据。</p>
<p>如果你希望查询一个其他形状的范围内的数据,只需要传入多个 <code>Point</code> 的数据就可以完成,比如如果你要查询一个五边形内部的数据,也只需在构建 LineString 时,传入 6 个 Point的数据即可。</p>
<p>当然,在使用时,你可以根据自己的实际情况,设定不同的图形类型,作为数据库查询的对象,完成自己的数据查询需求。</p>
<h2>总结</h2>
<p>这节课,我们介绍了 <code>geoNear</code>、<code>geoWithin</code>、<code>geoIntersects</code> 三个 API,帮助大家理解其各自在什么样的场景下使用,下一节课,我们将介绍 eq、neq、lt、lte、gt、gte 几个命令。</p>
小程序·云开发 项目开发经验分享
https://segmentfault.com/a/1190000016138249
2018-08-24T15:10:19+08:00
2018-08-24T15:10:19+08:00
白宦成
https://segmentfault.com/u/xiqingongzi
4
<p>近期,小程序开放了新的能力——「小程序·云开发」,帮助开发者快速构建微信小程序的后端服务。我作为一名微信小程序的开发者,也在第一时间尝试了小程序云开发,并将我自己在开发过程中的经验分享给大家。</p>
<h2>小程序云开发是什么</h2>
<p>简单的来说,小程序云开发是一款 Serverless 服务,他为开发者提供了「<strong>云函数</strong>」、「<strong>云数据库</strong>」和「<strong>云文件存储</strong>」,并且将这些能力封装成特定的接口,以 <code>wx.cloud.xxx</code> 来进行调用。</p>
<h2>小程序云开发不是什么</h2>
<p>首先,小程序云开发不是 PaaS ,它和我们所熟悉的 BAE、Google App Engine 不同,所提供的云函数并不是完整的环境,而是以一个特定的事件为单位的。严格来说,它所提供的<strong>云函数</strong>功能其实是 FaaS (Function as a Service),同类型的产品有 LeanCloud 的云函数、 Bmob 的云函数、AWS 的 Lambda、 Azure 的 Functions 和 Google Cloud Functions。</p>
<p>其次,小程序云开发不是 LeanCloud 、 Bmob 之类的 BaaS 。不同于上述的两款产品,他们提供了丰富的 API 接口,能够实现各种各样的功能,小程序·云开发将接口进行深度的封装,仅能在小程序、云函数中通过 wx.cloud 和 wx-server-sdk 来调用(截止2018年8月24日)。因此,小程序云开发 <strong>仅能应用在小程序中</strong>,无法在其他产品中应用(比如 App )</p>
<h2>小程序云开发适合什么样的场景?</h2>
<p>所有的业务逻辑都仅仅需要在小程序端完成,无需过于复杂的管理逻辑(这是因为云函数、云数据库无法在小程序以外的区域调用,因此无法实现强大的 Web 管理界面)</p>
<h2>小程序云开发的优势</h2>
<h3>1. 微信登录逻辑简单</h3>
<p>小程序云开发可以自动实现用户登录的校验,开发者无需再次校验用户身份,直接通过云函数的 <code>event.userInfo.openId</code> 即可获取到用户信息。直接调用数据库 、文件存储 API 时,也会自动关联到用户对应的 openId</p>
<p><img src="/img/remote/1460000016138252" alt="" title=""></p>
<p>如果用户授权小程序获取昵称等信息,这些信息也会自动出现在小程序云开发的管理控制台中的用户登录部分。不需要开发者手动上传。</p>
<h3>2. 免费</h3>
<p>目前小程序云开发提供了免费 1GB 的数据库存储和 免费 5 GB 的文件存储,这个存储量并不是很大,但是对于一些个人开发者来说,还是绰绰有余的,开发者可以使用这些容量来快速开发自己的小程序。</p>
<p><img src="/img/remote/1460000016138253" alt="" title=""></p>
<h3>3. 简单</h3>
<p>小程序云开发的调用非常的简单,你只需要了解 JavaScript 和一些简单的异步的知识 (promise),就可以完成小程序云开发的内容。</p>
<h3>4. 无侵入</h3>
<p>小程序云开发本身是在小程序的基础库层面的封装,你无需再引入其他库就可以使用。</p>
<p>同样的,你也可以在原有的应用程序中,将一部分功能迁移到小程序云开发中。</p>
<h2>小程序开发过程中遇到的一些坑</h2>
<h3>1. 异步请求需要通过 Promise 来处理</h3>
<p>在云函数中,我们大多会实现一些在小程序中无法实现,或受域名限制的请求接口,这时我们不能使用传统的 Callback 方法来进行请求,因为传统的 callback 方法执行完成后,云函数早已将数据返回给客户端,我们需要使用 Promise 来处理。</p>
<p>比如,下面的代码是我请求豆瓣 API 的代码。</p>
<pre><code class="javascript">var rp = require('request-promise')
exports.main = (event, context) => {
var res = rp('https://api.douban.com/v2/book/isbn/'+ event.isbn).then( html => {
return html;
}).catch( err => {
console.log(err);
})
return res
}</code></pre>
<p><em>上述代码出自 Github 项目 <a href="https://link.segmentfault.com/?enc=Yu7pgb3LfyfIer3Lok%2BgTQ%3D%3D.1oS0hPxvkajo7bVRAA%2FawFYnHfrNu%2Ba%2BjX2YowST9zNKCwqga6mFCgx3GDX%2FnZHTP1K4LAe59m4YalbCKBLR5g%3D%3D" rel="nofollow">WXCloud-bookcase</a></em></p>
<h3>2. 权限结构比较简单</h3>
<p><img src="/img/remote/1460000016138254" alt="" title=""></p>
<p>小程序云开发提供的数据库权限非常简单,仅有四种。</p>
<ul>
<li>仅创建者可写,所有人可读:数据只有创建者可写、所有人可读;比如文章。</li>
<li>仅创建者可读写:数据只有创建者可读写,其他用户不可读写;比如用私密相册。</li>
<li>仅管理端可写,所有人可读:该数据只有管理端可写,所有人可读;如商品信息。</li>
<li>仅管理端可读写:该数据只有管理端可读写;如后台用的不暴露的数据。</li>
</ul>
<p>对于绝大多数情况下,简单的使用这四种权限根本无法满足我们的要求,因此,我们还需要在代码层面进行一些判断来确保具体的表现如我们所想的那样。</p>
<p>比如,在做一个书柜的项目时,希望书柜里的书可以设置可被第三方查看和不可被第三方查看,这时你只能将集合的数据设置为「仅创建者可写,所有人可读」,并通过代码来控制具体信息是否显示,比如加入一个 is_private 字段来进行控制。</p>
<h2>广告时间</h2>
<p>关于微信小程序 · 云开发,我写了一个 Demo 项目,放在 Github 上,欢迎大家来 Star ,来 Fork ,来提 PR。</p>
<p>项目地址:<a href="https://link.segmentfault.com/?enc=h6HGIFpRJTkIrUcCR%2Bk5gQ%3D%3D.a5V%2FhNFFAvAKA1NEOx5Bio7g1E447vKFcJtK8F%2BajuZOB1Fjpz2hlgsBeRPRiewbNtZJ3skBdTznMNX9CB%2B1eA%3D%3D" rel="nofollow">https://github.com/Tencent-CloudEDU/WXCloud-bookcase</a></p>
<p><img src="/img/remote/1460000016138255" alt="" title=""></p>
<p>此外,针对该项目,还准备了一个<strong>免费</strong>的<strong>实战开发项目</strong>,大家可以访问 <a href="https://link.segmentfault.com/?enc=Xqyg3lScxhMQkcLOiIP%2Bew%3D%3D.zTZ6FHlqP2Huo4b2Af61qWFALWBTkWia1ng9E%2FVLE9MTrEDu%2BKPjGvN1oFf3jBGHEpBfqWVFx2z6dcIEJ%2BRvDg%3D%3D" rel="nofollow">https://cloud.tencent.com/dev...</a> 查看。</p>