Write the Code. Change the World.

9月 05

在 Go 1.5 及后续版本中,可以通过创建 internal 代码包让一些程序实体仅仅能被当前模块中的其他代码引用。这是第三种访问权限:模块级私有。

  • 导出路径包含internal关键字的包,只允许internal的父级目录及父级目录的子包导入,其它包无法导入。

阅读全文 >>

9月 05

https://github.com/danceyoung/paper-code/blob/master/package-oriented-design/packageorienteddesign.md

本篇内容主要讲解golang项目的面向包设计准则和基础的架构分层。

信息来自原文

内容进行翻译、加工、整合及结合个人的实践经验,并附有一个真实的例子来解释本篇内容。

当然你也可以直接阅读英文原文。

当然高手如云,只是懒得写罢了。

百年太久,只争朝夕,不负韶华,不枉少年,来日怎方长。

阅读全文 >>

8月 28

el-tooltip 和 el-popover 嵌套使用的时候,可能会遇到你意向不到的情况。怎么处理呢。

  • el-tooltip 嵌套 el-popover
  • 在嵌套 el-popover 的时候,加一层 div

仅此而已,就会出现你想看到的效果了。

<el-tooltip content="我不知道风是往哪一个方向吹" placement="top">
        <div>
              <el-popover placement="bottom-start" trigger="click">
              </el-popover>
        </div>
</el-tooltip>

就这样咯。

阅读全文 >>

8月 24

前端项目,打包后,运行起来就可能会出现一些提示。描述语是 Violation 。看着不舒服,还是要解决的。列举一下

[Violation] Added non-passive event listener to a scroll-blocking 'touchstart' event.

出现报错的原因:

Chrome51 版本以后,Chrome 增加了新的事件捕获机制-Passive Event Listeners;

Passive Event Listeners:被动事件侦听器是 DOM 规范中的一项新功能,它使开发人员能够通过消除滚动来阻止触摸和滚轮事件侦听器来选择加入以获得更好的滚动性能。

开发人员可以使用 {passive: true} 来设置监听器后内部不会调用 preventDefault() 来阻止默认滑动行为,Chrome 浏览器称这类型的监听器为被动(passive)监听器。

Passive Event Listeners 就是告诉前页面内的事件监听器内部是否会调用 preventDefault 函数来阻止事件的默认行为,以便浏览器根据这个信息更好地做出决策来优化页面性能。

目前Chrome主要利用该特性来优化页面的滑动性能,所以Passive Event Listeners特性当前仅支持mousewheel/touch相关事件。

参考这篇课解决 https://juejin.cn/post/7230806990452588581

[Violation] Forced reflow while executing JavaScript

阅读全文 >>

8月 23

列举列举

interface HTMLElementTagNameMap {
    "a": HTMLAnchorElement;
    "abbr": HTMLElement;
    "address": HTMLElement;
    "applet": HTMLAppletElement;
    "area": HTMLAreaElement;
    "article": HTMLElement;
    "aside": HTMLElement;
    "audio": HTMLAudioElement;
    "b": HTMLElement;
    "base": HTMLBaseElement;
    "bdi": HTMLElement;
    "bdo": HTMLElement;
    "blockquote": HTMLQuoteElement;
    "body": HTMLBodyElement;
    "br": HTMLBRElement;
    "button": HTMLButtonElement;
    "canvas": HTMLCanvasElement;
    "caption": HTMLTableCaptionElement;
    "cite": HTMLElement;
    "code": HTMLElement;
    "col": HTMLTableColElement;
    "colgroup": HTMLTableColElement;
    "data": HTMLDataElement;
    "datalist": HTMLDataListElement;
    "dd": HTMLElement;
    "del": HTMLModElement;
    "details": HTMLDetailsElement;
    "dfn": HTMLElement;
    "dialog": HTMLDialogElement;
    "dir": HTMLDirectoryElement;
    "div": HTMLDivElement;
    "dl": HTMLDListElement;
    "dt": HTMLElement;
    "em": HTMLElement;
    "embed": HTMLEmbedElement;
    "fieldset": HTMLFieldSetElement;
    "figcaption": HTMLElement;
    "figure": HTMLElement;
    "font": HTMLFontElement;
    "footer": HTMLElement;
    "form": HTMLFormElement;
    "frame": HTMLFrameElement;
    "frameset": HTMLFrameSetElement;
    "h1": HTMLHeadingElement;
    "h2": HTMLHeadingElement;
    "h3": HTMLHeadingElement;
    "h4": HTMLHeadingElement;
    "h5": HTMLHeadingElement;
    "h6": HTMLHeadingElement;
    "head": HTMLHeadElement;
    "header": HTMLElement;
    "hgroup": HTMLElement;
    "hr": HTMLHRElement;
    "html": HTMLHtmlElement;
    "i": HTMLElement;
    "iframe": HTMLIFrameElement;
    "img": HTMLImageElement;
    "input": HTMLInputElement;
    "ins": HTMLModElement;
    "kbd": HTMLElement;
    "label": HTMLLabelElement;
    "legend": HTMLLegendElement;
    "li": HTMLLIElement;
    "link": HTMLLinkElement;
    "main": HTMLElement;
    "map": HTMLMapElement;
    "mark": HTMLElement;
    "marquee": HTMLMarqueeElement;
    "menu": HTMLMenuElement;
    "meta": HTMLMetaElement;
    "meter": HTMLMeterElement;
    "nav": HTMLElement;
    "noscript": HTMLElement;
    "object": HTMLObjectElement;
    "ol": HTMLOListElement;
    "optgroup": HTMLOptGroupElement;
    "option": HTMLOptionElement;
    "output": HTMLOutputElement;
    "p": HTMLParagraphElement;
    "param": HTMLParamElement;
    "picture": HTMLPictureElement;
    "pre": HTMLPreElement;
    "progress": HTMLProgressElement;
    "q": HTMLQuoteElement;
    "rp": HTMLElement;
    "rt": HTMLElement;
    "ruby": HTMLElement;
    "s": HTMLElement;
    "samp": HTMLElement;
    "script": HTMLScriptElement;
    "section": HTMLElement;
    "select": HTMLSelectElement;
    "slot": HTMLSlotElement;
    "small": HTMLElement;
    "source": HTMLSourceElement;
    "span": HTMLSpanElement;
    "strong": HTMLElement;
    "style": HTMLStyleElement;
    "sub": HTMLElement;
    "summary": HTMLElement;
    "sup": HTMLElement;
    "table": HTMLTableElement;
    "tbody": HTMLTableSectionElement;
    "td": HTMLTableDataCellElement;
    "template": HTMLTemplateElement;
    "textarea": HTMLTextAreaElement;
    "tfoot": HTMLTableSectionElement;
    "th": HTMLTableHeaderCellElement;
    "thead": HTMLTableSectionElement;
    "time": HTMLTimeElement;
    "title": HTMLTitleElement;
    "tr": HTMLTableRowElement;
    "track": HTMLTrackElement;
    "u": HTMLElement;
    "ul": HTMLUListElement;
    "var": HTMLElement;
    "video": HTMLVideoElement;
    "wbr": HTMLElement;
}

阅读全文 >>

8月 23

很多很多年前,网页端图片裁切都会想到 cropper.js 。到了当前, cropper.js 还是值得一选。这个是基于 js,ts 的插件,不局限你使用 vue 还是 使用 react 或其他的。反正,还是很好用。当下 2.0 版本更好用。

官网: https://fengyuanchen.github.io/cropperjs/v2/zh/

演练场:https://fengyuanchen.github.io/cropperjs/v2/zh/playground.html

这个插件越来越自由,也越来越集中。它只关注输入(img dom)和输出(canvas)[转换成 base64 可能会存在跨域问题]。大只分以下几个模块(每个模块都可以单独使用)。

Cropper

CropperCanvas

CropperImage

CropperSelection

CropperShade

CropperGrid

CropperViewer
# 等等

这些,都各自有各自的渲染职责。加上就有,配置就改变。不加则没有。想加就加,想减就减。是真的好用。 先看一个我做的一个 demo。是基于vue3 + ts 实现的。(不对。里边还加了一些 element-plus 的组件)

构建的时候,也很方便。使用其接口就可以。更好的是是基于组件的方式来处理。最终的目的是什么,是要渲染到 dom 中,让人看到。 组件不就是最直接的方式吗。这个时候,你可以很方便的使用 css 去美化你的产品。只需要少量关注一些配置就好。比如裁切比例,覆盖比列等。

阅读全文 >>

8月 17

有封装好的 useDark useToggle 这些插件,做项目就很方便。可是用的时候,会遇到一些问题。其实到最后,不是问题了。比如改变主题的时候,transition 失效了。因为没 transition,强行改变背景颜色等就会很生硬。transition 失效是因为 userColorMode 在处理改变的时候,先禁用 transition 再取消禁用导致的。在创建 useDark 的时候,options 的 disableTransition 可以控制该行为,只是不传值的时候,默认就是 true,就是禁用 trasition。那么默认传入 disableTransition 为 false 的值就可以搞定了。

https://github.com/vueuse/vueuse/blob/main/packages/core/useColorMode/index.ts#L122

const isDark = useDark({disableTransition: false})
 const toggleDark = useToggle(isDark)

只需上边这样实现即可。

阅读全文 >>

8月 15

type 和 interface 相关

https://juejin.cn/post/7063521133340917773

https://blog.csdn.net/qq_42345237/article/details/124895617

namespace 和 modules 相关

https://zhuanlan.zhihu.com/p/56583269

Pick、Omit、Extract、Exclude 等

https://juejin.cn/post/7202153645441892407
https://www.jianshu.com/p/cb8923a7a31b

泛型相关

https://zhuanlan.zhihu.com/p/149767010

https://zhuanlan.zhihu.com/p/149965449

文档

https://www.typescriptlang.org/zh/docs/

https://nodejs.cn/typescript/get-started/

额外

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

阅读全文 >>

8月 14

前端本地测试,mock 可以先用着。终究还是得和真实的服务环境对接。这个时候请求接口,往往就会遇到跨域的问题。对于 laravel框架,对于 laravel10,只需要建立一个中间件,在中间件中对请求进行处理就好。需要的时候,带上中间件,不需要的时候,去掉它就可以。还是很方便的。

Do

php artisan make:middleware EnableCrossRequestMiddleware

# 完整代码如下
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class EnableCrossRequestMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $response = $next($request);
        $origin = $request->server('HTTP_ORIGIN') ? $request->server('HTTP_ORIGIN') : '';
        $allow_origin = [
            'http://localhost:5173',
        ];

        if (in_array($origin, $allow_origin)) {
            $IlluminateResponse = 'Illuminate\Http\Response';
            $SymfonyResopnse = 'Symfony\Component\HttpFoundation\Response';
            $headers = [
                'Access-Control-Allow-Origin' => $origin,
                'Access-Control-Allow-Methods' => 'POST,GET,OPTIONS,PUT,PATCH,DELETE',
                'Access-Control-Allow-Headers' => 'Accept,Content-Type,Referer,User-Agent,Origin,X-Requested-With,X-XSRF-TOKEN,X-CSRF-TOKEN,Authorization,Time'
            ];

            if ($response instanceof $IlluminateResponse) {
                foreach ($headers as $key => $value) {
                    $response->header($key, $value);
                }
                return $response;
            }

            if ($response instanceof $SymfonyResopnse) {
                foreach ($headers as $key => $value) {
                    $response->headers->set($key, $value);
                }
                return $response;
            }
        }
        return $response;
    }
}

然后,在 ./app/Http/Kernel.php 的 $middleware 中添加一个配置。

    protected $middleware = [
        …
        \App\Http\Middleware\EnableCrossRequestMiddleware::class,
    ];

这样就完事了。

本来是想将中间件放在中间件组里(middlewareGroups)或使用 alias (放在 $middlewareAliases)的方式来使用,但都失败了。暂时还没找到原因。

之所以想这样弄,是因为不是所有的请求都需要设置请求头。

阅读全文 >>

8月 13

经过前边的处理,用户登录,侧边栏渲染,路由跳转,登录信息展示,用户退出。这些功能最小化完成。如下图所示。

在线预览,可切换角色。新用户注册,有一个默认角色。只有测试账号有多角色。

https://www.zeipan.com/admin

用户登录

打开后台域名任意 url 。在路由全局守卫中。 如果用户已登录(存在 token),先判断用户信息是否初始化,如果没有,先请求用户信息(返回数据不仅有用户信息,还有该用户对应的后台权限【用户生产菜单】),处理存储用户信息,添加路由信息。再重定向。如果已初始化,就继续。如果用户没登录,先重定向到登录页面。

调用接口,请求登录。获取用户的 token。存储 token。

通过 token,请求用户信息(数据中包括用户的权限数据),存储用户信息以及权限数据。处理权限数据,生成菜单。

阅读全文 >>