跳到主要内容

经验与发现

· 阅读需 10 分钟

关于 popup 浮动弹出组件的实现

场景需求(浮出覆盖:vue-popper

我们经常有实现一些浮动弹窗的需求,比如在一个图表里当鼠标悬浮在某个位置上时有自动浮窗提示。

Element-ui里,也有多个组件属于浮动弹窗类型,比如el-popoverel-tooltip等。

查询源代码发现,背后是基于vue-popper.js这个库,而这个库是对popper.js库的一个 vue 封装。

基于此,如果我们只是想实现浮窗,只需要使用vue-popper.js即可,也十分方便。

场景选择(浮出不覆盖:自定义的popup

一般来说,浮窗组件是鼠标滑到哪,哪里就出现,出现的浮窗会覆盖目前的视窗。但有时,这会导致不是很美观。

在我的个人博客里,我在页面底部栏里加入了几个二维码,默认是不显示的。当鼠标划过其中某个触点(比如微信文字)时,该二维码(即微信二维码)浮出,由于布局原因,我发现这个二维码直接显示在布局内(而非浮动覆盖)会更好一些。

基于此,我简单封装了自己的popup组件,这个对相应要哦浮出的元素只是简单做了一个默认不显示、触发显示的处理。但很好地满足了自己的需求。

关于跨平台文件流相关api问题

具体情况

在我一开始的程序设计中,前端使用的是基于jsnuxt后端使用的是基于pythonfastapi

后来经过analyze发现,element-uiali-oss两个包占了很大的空间。

我通过babel的按需导入,成功压缩了element-ui的体积量。考虑到ali-oss非前端 UI 组件,也非build必需模块,而是一个api,因此想将ali-oss整体移植到后端中。

python平台需要cmcmod的问题

根据ali-oss官方文档,是有python版本的。

但很快就遇到了两个问题,首先是版本,它只支持2.7及最高3.6

后来检查发现,我本地安装的是3.7,服务端安装的是3.6,服务端那边版本倒不是问题,本地其实无所谓。

但是,更关键的一个问题出现了。

linux环境下需要单独安装python-dev,以使C++加速的cmcmod更快,否则ali-oss将会使用纯python计算,速度会慢上数十倍。

我尝试了安装多遍,都未能解决问题,后来提交了工单,交涉了多日,依旧无果。

尽管如此,我想抱着暂时先用的想法,结果碰到了一个更棘手的技术相关的问题。

js的和python之间的数据交换没有想象中那么简单

我简单设计了一个较为稳健的图片上传的api,基于fastapi,它支持上传本地图片、网络图片和二进制图片内容。

在移除了ali-ossjs依赖后,我打算使用axios直接与其交互。

结果发现element-ui读取到的图片是File格式,而这个格式是js特有的,需要通过FileReader或其它方式进行转换才能与python进行交互。

具体,binaryString格式并非pythonbytes,而urlblob url,并且是本地的localhost,在开发的时候并不能直接被nanchuan.site读取。

这里也反映了一个关于开发环境和生产环境的域名配置问题,最好还是要保持统一(如果生产环境就是统一的话)。

解决方案

我相信,肯定有一些方法可以将jsFile格式转换成pythonbytes等形式,但我觉得,这中间的成本有一些些高了。在考虑到python版本的性能问题之后,我决定放弃pythonsdk

基于此,我打算暂时先使用jsali-oss-sdk,等之后项目进度放缓时,再对应配置koa版本的后台。

对此,我唯一的顾虑就是,fastapi之后要不要作为本项目的后端,这将是一个需要思考的问题。

关于局部滚动与滚动条问题

关于百度统计在nuxt中的最佳植入形式

首先,如果按照百度统计官方的推荐做法,可以将代码插入到html模板的head标签前,即充当整个网站第一个运行的函数。

nuxt是较高封装的框架,html层基本都是不用考虑的,但要实现这个需求,也不是不可以。

方法一:修改app.html

nuxt默认以nuxt内部的app.template.html文件启动。

<!DOCTYPE html>
<html {{ HTML_ATTRS }}>
<head {{ HEAD_ATTRS }}>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
{{ APP }}
</body>
</html>

路径很深,但我们并不需要直接修改这个文件(虽然我确实傻到修改过)。

我们可以在项目的根目录创建一个app.html文件,内容同上,等价于直接覆盖。

在此基础上插入百度的官方代码,如下。

<!DOCTYPE html>
<script>
var _hmt = _hmt || []
;(function () {
var hm = document.createElement('script')
hm.src = 'https://hm.baidu.com/hm.js?【这里是代码序列号】'
var s = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore(hm, s)
})()
</script>
<html {{ HTML_ATTRS }}>
<head {{ HEAD_ATTRS }}>
{{ HEAD }}
</head>
<body {{ BODY_ATTRS }}>
{{ APP }}
</body>
</html>

方法二:在nuxt.config.jshead中植入代码

nuxt.config.js的扩展能力还是很强的,其中有一个配置项是head,主要用来配置一些settings数据,详情可见 The head Property - NuxtJS

但官方只提供了配置settings的示例,其实还可以加入script,只不过要以src的形式。也就是说,上述方法中的一串代码并不能直接拷贝过来。

但在我仔细分析了百度官方的这串代码后,才意识到,它只是自动把百度的这段代码插入到html的第一个script位置而已。如果我们的脚本本来就是位于网页的第一个位置,那这个脚本就有点多余了。它的核心其实就一句话,就是hm.src = 'https://hm.baidu.com/hm.js?【这里是代码序列号】'

基于此认知,我们只需要把这个网址复制出来,配置到head.script里去就可以了。具体如下。

  //    ** See https://nuxtjs.org/api/configuration-head
head: {
title: '南川笔记' || process.env.npm_package_name,
description: '南川笔记——致力于知识服务',
settings: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
script: [
// 用于百度统计
{
src:
process.env === 'production'
? 'https://hm.baidu.com/hm.js?【这里是代码序列号】'
: '',
},
],
},

这里,值得注意的是,如果在开发模式(本地浏览器)运行nuxt程序,该脚本依旧会触发百度的统计机制,并且在浏览器的storage/session中注入cookie,引发浏览器的跨域警告。

在这里,我也意识到了,之前我的百度统计页面为啥会出现大量的localhost访问了,我还好奇为啥百度能检测到localhost的访问来着。

image-20200822214029461

由于在开发模式下,本身就不需要进行访问统计,因此,我们可以加入一句 process.env === 'production'进行判断,如果是开发模式就不植入脚本了。

最后,完美解决问题。如下,在开发模式下,脚本是不会运行的。

image-20200822213713894

关于nuxt-content的渲染

原来是通过读取document.body里的内容(由md渲染后的markdown)。

具体可以见:

  render (h, { data, props }) {
const { document } = props
const { body } = document || {}
if (!body || !body.children || !Array.isArray(body.children)) {
return
}
...
return h('div', data, body.children.map(child => processNode(child, h, document)))
}

JS 总有奇怪的设计问题

看一段代码。

const langs = ['en', 'zh'];
langs.forEach((lang) => {
console.log({lang: lang});
});

这段代码如果按照python之类语言的逻辑,毫无疑问会输出

{ 'en': 'en'}
{ 'zh': 'zh'}

然而在javascript里,得到的结果却是

{'lang': 'en'}
{'lang': 'zh'}