关于 ZiShu | 子舒

本RSS包含了奇趣周刊。前端程序员,骑行爱好者,摄影爱好者,INFJ 。

RSS 地址: https://zishu.me/index.xml

请复制 RSS 到你的阅读器,或快速订阅到 :

ZiShu | 子舒 RSS 预览

奇趣周刊 - 第 68 期

2024-09-19 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

2024 年 9 月 12 日,OpenAI 推出了新的 OpenAI o1 系列 AI 模型,专门用于解决复杂问题,并提高了 AI 的推理能力和安全性。

https://openai.com/index/introducing-openai-o1-preview/

1. 开源 PDF 在线工具

https://github.com/Stirling-Tools/Stirling-PDF

一个很强大的开源 PDF 在线工具,拆分 PDF,压缩 PDF,PDF 转换等等,支持 docker 自部署。

1725788411241

2. 自定义配置 tailwindcss 变量的网站

https://customizer.railly.dev/

想要自定义 tailwindcss 变量可以试试这个网站,一次性配置好。

1725862606485

3. 一个二维码生成网站

https://www.qrcode-tiger.com/

最先进的带有在线徽标的二维码生成器。

1725948167790

4. 一个免费制作 logo 的网站

https://icon.kitchen/

它可以直接生成 android、ios 和 web 三端的 logo,你可以从图标、文本、图片去生成 logo,支持设置背景、纹理、字体等。

1726018963937

5. 有趣的在线制作简历

https://rxresu.me

填写基本信息后,利用模板即可生成美观的简历,简洁大方,非常好用。

1726022119578

6. 一则 Twitter 消息

7. 汉语新解

https://hanyuxinjie.com/

将一个汉语词汇进行全新角度的解释,使用 gpt 生成,关于 prompt 的描述。

1726206015149

8. 网络测速工具

https://zhale.me/

全球 1000+ 网络拨测节点,模拟用户访问域名/IP,实用小工具,运维必备。

1726478473414

奇趣周刊 - 第 67 期

2024-09-12 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

2024 年 9 月 6 日,telegra.ph 网站限制一切 api 上传媒体文件。很多绕过 telegraph 的项目上传图片的图床项目,生命因此走到了终结,不过这也在意料之中,白嫖不是长久之计,不过之前已经上传的媒体文件并不会受影响。建议平替到 CloudFlare R2,毕竟是正经的本地存储功能,且背靠互联网大厂信誉很不错,还是值得使用的。

1. Git 客户端

https://ugit.qq.com/

腾讯为数不多的良心软件,自研 Git 客户端,便捷的大文件管理,快速提交,检出子目录,使用很流畅,可以替代 GitHub Desktop。

1725505460403

2. 杂交版植物大战僵尸官网

https://pvzgame.net/

植物大战僵尸杂交版是由 B 站(哔哩哔哩)UP 主潜艇伟伟迷以个人兴趣和创意开发的一款植物大战僵尸同人游戏,完全免费且不涉及商业利益​ ​。这个版本包含了大量新设计的僵尸和植物,以及许多新机制和挑战,玩家可以在不联网的情况下离线进行游戏​。

3. 火烧云分析与记录

https://sunsetbot.top/

国内自然气候爱好者的小众记录网站,主要内容就是火烧云分析与记录。

1725513913681

4. Virtual Try-On Chrome Extension

https://github.com/shyjal/visual-try-on

基于快手 Kolors 图像模型开发,可在任何电商网站上,上传一张自己照片,即可一键让自己试穿喜欢的衣服。

查看插件的演示视频:https://www.youtube.com/watch?v=1LQ2345lANM

1725550106946

5. Logoly —— Pornhub 风格的标志生成器

https://www.logoly.pro

一个简单的在线徽标生成器,适合想要轻松设计徽标的人们,生成类似 PornHub 或 OnlyFans 的徽标,支持下载 PNG/SVG 格式的图片。

1725583704258

6. 文颜 - 多平台写作,一键排版美化。

https://yuzhi.tech/wenyan

「文颜」是一款全自动的文章排版美化工具,专为简化您的内容发布工作而设计。它可以将 Markdown 格式的文章快速转换为适合微信公众号、今日头条、知乎等平台的排版格式,从而省去因平台差异带来的繁琐调整。

1725584932468

7. 动态文档计算器

https://www.inkandswitch.com/potluck/demo/?openDocument=math-pack

可以一边写文档,一边进行计算。

1725607705140

8. 一个可以下载书的网站

https://24hbook.store/

1725635616415


This message is used to verify that this feed (feedId:55064361156653076) belongs to me (userId:54877812003614720). Join me in enjoying the next generation information browser https://follow.is.

This message is used to verify that this feed (feedId:52896630195224576) belongs to me (userId:54877812003614720). Join me in enjoying the next generation information browser https://follow.is.

Python + GPT 自动翻译 README 文档

2024-09-09 08:00:00

在开发个人项目时,写 README 文件一直是一件很头疼的事情,所幸辛苦写完了,想做个多语言版本更头疼,为了解决整个痛点,写了个 python + GPT 脚本,将这一切自动化处理了。

源码:https://github.com/dlzmoe/translate-README

在根目录下新建俩文件 .envtranslate_md.py

OPENAI_URL=https://api.openai.com/v1/chat/completions
API_KEY=your_api_key_here
MODEL=gpt-4o-mini
INPUT_FILE=README.md
OUTPUT_FILE=README_EN.md
TARGET_LANGUAGE=en

个人是比较喜欢 gpt-4o-mini 这个模型,功能足够覆盖我日常使用,生成速度快,对 token 的消耗也比较低,作图生文、理解性也都是前排水平。

# translate_md.py
import os
import requests
from dotenv import load_dotenv

# 加载 .env 文件中的环境变量
load_dotenv()

# 配置选项
OPENAI_URL = os.getenv("OPENAI_URL")
API_KEY = os.getenv("API_KEY")
MODEL = os.getenv("MODEL")
INPUT_FILE = os.getenv("INPUT_FILE")
OUTPUT_FILE = os.getenv("OUTPUT_FILE")
TARGET_LANGUAGE = os.getenv("TARGET_LANGUAGE")

def read_markdown_file(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        return file.read()

def write_markdown_file(file_path, content):
    with open(file_path, 'w', encoding='utf-8') as file:
        file.write(content)

def translate_text(text):
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json"
    }
    
    prompt = f"Translate the following text to {TARGET_LANGUAGE} Markdown syntax is not preserved :\n\n{text}"
    
    data = {
        "model": MODEL,
        "messages": [{"role": "user", "content": prompt}]
    }
    
    response = requests.post(OPENAI_URL, headers=headers, json=data)
    
    if response.status_code == 200:
        return response.json()['choices'][0]['message']['content']
    else:
        print(f"Error: {response.status_code} - {response.text}")
        return None

def main():
    if not os.path.exists(INPUT_FILE):
        print(f"Input file {INPUT_FILE} does not exist.")
        return
    
    # 读取 Markdown 文件
    markdown_content = read_markdown_file(INPUT_FILE)
    
    # 去除头尾的 ``` 语法
    # 这里假设 markdown_content 的开头和结尾没有其他内容,只是代码块
    if markdown_content.startswith('```'):
        markdown_content = markdown_content[3:].strip()
    if markdown_content.endswith('```'):
        markdown_content = markdown_content[:-3].strip()
    
    # 翻译文本
    translated_content = translate_text(markdown_content)
    
    if translated_content:
        # 写入翻译后的内容到输出文件
        write_markdown_file(OUTPUT_FILE, translated_content)
        print(f"Translation completed. Output saved to {OUTPUT_FILE}.")
    else:
        print("Translation failed.")

if __name__ == "__main__":
    main()

文件创建完成后安装一下依赖:

pip install requests
pip install python-dotenv

直接运行即可:

py translate_md.py

奇趣周刊 - 第 66 期

2024-09-05 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 同 wifi 下穿文件

https://localsend.org/

将文件分享到附近的设备,免费、开源、跨平台。

1724895945853

2. 生成 GitHub 仓库贡献名单图

https://contrib.rocks/preview

1724898917324

3. 通过关键词搜索小图标的网站

https://emojispark.com/

1724994855626

4. 查看 GitHub 仓库第一个 commit

https://initcommit.info/

一个好玩的工具,可以查看 GitHub 仓库第一个 commit 并生成分享卡片。

1724995095031

5. API key 连通性检测

可以检测 openai 第三方 api key 是否存活,以及可使用的模型和额度。

https://mykey.zeabur.app/

6. 一个基于 vite 的 electron 模板

https://github.com/alex8088/electron-vite-boilerplate

使用 Vue3 + Vite + TypeScript 的 Electron 应用程序。

1725351816029

7. prompt 提示词汇总

https://agicto.com/prompt

一个 prompt 提示词汇总网站,还挺全面的,可以学习一下。

1725431053881

有趣的 Vue 版本号名称

2024-09-05 08:00:00

虽然本文是有关 Vue 的内容,但是可以作为一件奇闻轶事来阅读,因此放在了随笔分类中。

2024 年 9 月 1 日,Vue3.5 发布,命名为 “Tengen Toppa Gurren Lagann”,因为之前没有特别关注过,所以对这个名称有些好奇,翻译成中文是天元突破红莲螺岩,来源于一部日本动漫《天元突破红莲螺岩》(天元突破グレンラガン)。

继续查阅 Vue 官方博客,发现一件有趣的事情,几乎每个大版本更新都会有一个代号,并且大多都是与动漫或影视作品有关的。花了点时间,查看 https://blog.vuejs.org/ 以及网络上零散的数据,将其整理成以下表格。

按照版本号排序,注意 3.0-3.2 发布时间比 2.7 要早,其实 Vue2 和 Vue3 本质上可以算是两个项目了,常说的 v2 通常指代 Vue2.7。

版本号(发布时间) 代号 来源
3.5(2024.09.01) Tengen Toppa Gurren Lagann 天元突破红莲螺岩
3.4(2023.12.28) Slam Dunk 灌篮高手
3.3(2023.05.11) Rurouni Kenshin 浪客剑心
3.2(2021.08.05) Quintessential Quintuplets 五等分的新娘
3.1(2021.06.07) Pluto PLUTO 冥王
3.0(2020.09.18) One Piece 海贼王
2.7(2022.07.01) Naruto 火影忍者
2.6(2019.02.04) Macross 超时空要塞系列
2.5(2017.10.13) Level E 灵异 E 接触
2.4(2017.07.13) Kill la Kill 斩服少女
2.3(2017.04.27) JoJo’s Bizarre Adventure JoJo 的奇妙冒险
2.2(2017.02.26) Initial D 头文字 D
2.1(2016.11.22) Hunter X Hunter 全职猎人
2.0(2016.09.30) Ghost in the Shell 攻壳机动队
1.0(2015.10.27) Evangelion 新世纪福音战士
0.12(2015.06.12) Dragon Ball 龙珠
0.11(2014.11.07) Cowboy Bebop 星际牛仔
0.10(2014.03.23) Blade Runner 银翼杀手
0.9(2014.02.25) Animatrix 黑客帝国动漫
0.6(2013.12.08) VueJS

记录 vite 中引入 tailwindcss

2024-09-04 08:00:00

安装依赖并生成 tailwind.config.jspostcss.config.js

yarn add -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

注意,配置中要使用 ES6 模块语法 export default

// tailwind.config.js
export default {
  content: [
    "./index.html",
    "./src/**/*.{vue,js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

添加 Tailwind 的 CSS 文件。

/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

main.js 中引入。

// main.js
import './index.css';

奇趣周刊 - 第 65 期

2024-08-29 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 微信公众号文章导出工具

https://github.com/jooooock/wechat-article-exporter

微信公众号文章导出工具,100% 还原原文样式。

2. Ludusavi - PC 游戏存档备份工具

https://github.com/mtkennerly/ludusavi

Ludusavi 是一个用于备份 PC 游戏存档数据的工具,它是跨平台的,支持多个游戏商店如 Steam、GOG、Epic、Heroic、Lutris 和其他游戏库,能够备份超过 19,000 个游戏。

3. Logo.surf - 文本转标志与网站图标生成器

https://github.com/airyland/logo.surf

使用静态 html 构建,可以随意找个平台进行托管,GitHub Pages、CloudFlare Pages、Netlify 等。

1724850071501

4. 集成 Vue 功能组件和主题美化的 VitePress 插件

https://github.com/Theo-Messi/tools

支持自定义主题配色,根据需求调整主题颜色方案,通过简单配置切换主题。包含增强的 Vue 组件,提供更丰富的功能和更好用户体验,易于集成到 VitePress 项目中,提升开发效率和页面质量。

1724849525904

5. Rspack 1.0 发布

https://github.com/web-infra-dev/rspack

Rspack 1.0 正式发布,作为一个基于 Rust 的高性能 JavaScript 打包工具,它兼容 webpack API 和生态,提供了显著提升的构建性能,并在性能、兼容性、包体积优化等方面进行了多项改进和增强,支持了 Module Federation 2.0,同时推出了 Rstack 技术栈,为开发者提供了更丰富的工具和更好的开发体验。

1724852520815

6. AI 版答案之书

https://book-of-answers-ai.vercel.app/

1724853510545

7. 在推特上看到一个笑话

1724850310979

Github 仓库同步到 Cloudflare R2

2024-08-25 08:00:00

我在 GitHub 搭建了一个图床,用于小量存储博客图片,为了稳定安全考虑,决定备份到 Cloudflare R2 存储桶一份,R2 默认有每月免费的 10G 存储、100 万次 A 类操作、1000 万次 B 类操作,备份完全足够了。

说到备份肯定是越自动化越好,手动上传是不可能的,因此我决定利用 GitHub Actions 执行自动任务。

话不多说,开始操作。

1. 建立 Cloudflare R2 存储桶

在 R2 页面新建一个存储桶,名称可以随意,就叫做 github-sync-imgurl,默认不公开就行了,这个不用调整。

1724553937361

然后在管理 R2 API 令牌中,创建一个新的 API 令牌,权限设为 管理员读和写,其他不用管默认设置,保存即可。

会生成一个 KEY_IDACCESS_KEY,这俩等会要用到,先记录一下。

2. GitHub 仓库设置

来到你需要备份的仓库,打开 Setting > Actions > General,勾选这两个设置然后保存,这个是必须的,否则 GitHub Actions 无法自动运行。

1724554184492

然后来到 Setting > Secrets and variables > Actions,点击 New repository secret 按钮开始创建密钥,按照下面的命名开始依次操作:

CLOUDFLARE_ACCOUNT_ID         #你的 Cloudflare 账户 ID
CLOUDFLARE_ACCESS_KEY_ID      # Cloudflare R2 的访问密钥 ID
CLOUDFLARE_SECRET_ACCESS_KEY  # Cloudflare R2 的秘密访问密钥
CLOUDFLARE_BUCKET_NAME        # 你在 Cloudflare R2 上的存储桶名称

图示如下:

1724554376324

最后在 Actions 中新建一个新的任务,可以直接复制代码使用,无需修改,保存后运行即可。

name: Sync to Cloudflare R2

on:
  schedule:
    - cron: '0 16 * * *'  # 每天的16:00 UTC 时间触发(相当于东八区的 00:00)
  workflow_dispatch:  # 允许手动触发

jobs:
  sync:

    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v2

    - name: Install AWS CLI
      run: |
        sudo apt-get update
        sudo apt-get install -y awscli        

    - name: Configure AWS CLI for Cloudflare R2
      run: |
        aws configure set aws_access_key_id ${{ secrets.CLOUDFLARE_ACCESS_KEY_ID }}
        aws configure set aws_secret_access_key ${{ secrets.CLOUDFLARE_SECRET_ACCESS_KEY }}
        aws configure set default.region auto        

    - name: Sync repository to Cloudflare R2
      run: |
        aws s3 sync . s3://${{ secrets.CLOUDFLARE_BUCKET_NAME }} --endpoint-url=https://${{ secrets.CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com --delete --exclude ".git/*"        
      env:
        CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

由于使用了 aws s3 sync 命令,它会进行增量同步,这意味着它只会上传有变化的文件,而不会每次都重新上传整个仓库,避免消耗无用的 Cloudflare R2 流量。

奇趣周刊 - 第 64 期

2024-08-22 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. FList - 一个简洁的在线文件列表

https://github.com/jianjianai/FList

将 GitHub Releases,Hugging Face,文件 url 等,渲染成类似网盘的文件列表显示在网页上,方便用户下载开源软件。支持视频、音频、图片、PDF 等文件的在线预览。

1723624791333

2. 盘友圈 - 网盘资源搜索工具

https://panyq.com/

找资源更方便,很好用的一个工具。

1723647365081

3. vmail - 基本 Cloudflare 实现的临时邮箱服务

https://github.com/oiov/vmail
https://vmail.dev/

使用 Cloudflare email worker 实现的临时电子邮件服务,只需一个域名部署临时邮箱,支持多域名后缀,密码找回。

1723692452740

4. FlClash - 多平台代理客户端

https://github.com/chen08209/FlClash

一款基于 ClashMeta 的多平台代理客户端,简单易用,开源无广告。速度很快,非常好用。

5. moffee - 一个开源的 markdown 幻灯片制作工具

moffee 是一个开源幻灯片制作工具,可将 Markdown 文档转换为干净、专业的幻灯片。

1723793044394

6. podStation - 一个可以听国外播客的浏览器插件

谷歌扩展商店

podStation 是一个简单的 RSS 播客聚合器,一个适用于 Chrome 的自由/开源(FLOSS)播客聚合器,只需在主页面上添加您的订阅源或使用搜索工具。

7. AI 去水印工具

https://www.watermarkremover.io

非常好用,可以自己选择去除的地方。

8. AI 生成的 111 种风格图片生成网站

https://chatgptimage.org/

使用 gpt 生成 111 种风格图片的网站,可以调节参数,描述等,生成的效果还是不错的。

1723854133873

9. 社交媒体视频下载器

https://cobalt.tools/

可以下载绝大多数的社交媒体的音视频资源,比如推特、YouTube 等,支持自定义下载格式。

1724138111375

逃离杭州随想

2024-08-22 08:00:00

标题有些危言耸听了,只是刚好最近遇到了一些事,一些朋友的事,内心有些惆怅。

时代的一粒沙落到个体头上,都是非常沉重的代价,随着年纪的增长,越发感受到这句话。这也可能是自身能力不足的一种推辞,但真真切切落在自己头上,是一种无能为力的苦涩。

我的朋友,暂且称呼他为小 H,我们是 21 年认识的,一起骑过车,爬过山,吃过饭,也算是比较熟络了,出去玩的时候聊天,能感受到小 H 是一个内心想法很丰富的人,他老家是某个省会的,也是 20 年左右来到杭州工作的,比我时间略久一些。我觉得小 H 很厉害,可以很有主见的做一些事情,默默的,闷声办大事。前两天突然得到他的消息,已经回到老家定居,工作生活都安排好了。我瞬间觉得很震惊,缓过一会又觉得很正常,这是他的风格。

经常听他讲起老家的事,希望回到父母身边,对生活的规划和对未来的期翼,我很认同他的观点,有父母在的地方才是家,杭州没有归属感。能感受到他现在心情还是很积极的!

想到我自己,虽然家人都在杭州生活,但是说实话我对未来的期待值很低,想不到有什么能提起精神的地方,每天上班下班打卡,周末出去逛逛,给自己找点事情做。

vite 配置相关笔记

2024-08-15 08:00:00

一、配置 route 路由

使用 vite + vue 构建项目默认是没有 route 的,所以需要手动引入。

1. 基础配置

# 建立一个 vite 项目
npm create vite@latest
yarn create vite

# 安装 route 依赖
npm install vue-router@4
yarn add vue-router@4

接下来,需要在项目中配置 Vue Router。首先在 src 目录下创建一个 router 目录,并在该目录下创建 index.js,并配置路由。

src/
  ├── router/
  │   └── index.js
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';

const routes = [{
    path: '/',
    name: 'Home',
    component: () => import('@/views/Home.vue')
  },
  {
    path: '/login',
    name: 'Login',
    component: () => import('@/views/Login.vue')
  }
]

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes
})

export default router

main.js 声明一下 route 文件。

import { createApp } from 'vue';
import App from './App.vue';
import router from './router';

const app = createApp(App)
app.use(router)
app.mount('#app')

并且在 App.vue 中要使用 router-view 来显示当前路由对应的组件,所以修改下文件。

<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

2. 添加多路由

要添加更多的页面或动态路由,可以继续在 routes 数组中添加配置。例如:

{
  path: '/user/:id',
  name: 'User',
  component: () => import('@/views/User.vue')
}

这样,访问 /user/1 就会加载 User.vue 组件并显示相应内容。

二、配置 @ 路径别名

注意,vite 默认是没有配置路径别名 @ 的,所以要手动添加,配置如下:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { fileURLToPath, URL } from 'node:url'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

配置完别名后,就可以在项目中使用 @ 来代替 src 目录的路径,从而简化模块引用。

三、dev 时自动访问本地服务

open: true 表示当你运行 vite 或 vite dev 时,开发服务器将自动在浏览器中打开本地服务器的 URL(例如 http://localhost:5173)。你也可以指定一个具体的 URL,例如:

// vite.config.js
export default defineConfig({
  server: {
    open: true, // 自动打开浏览器并访问本地服务器网址
    open: 'http://localhost:5173', // 在浏览器中打开指定的路径
  },
});

四、允许本地 ip 访问 local

// vite.config.js
export default {
  server: {
    host: true, // 允许使用本地 IP 访问
    port: 3000 // 可以根据需要修改端口
  }
}

vite.config.js 配置集合

// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from 'node:url';

// https://vitejs.dev/config/
export default defineConfig({
  base: '/single-template/', // 设置打开目录
  plugins: [vue()],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)) // 允许使用 @ 别名
    }
  },
  server: {
    host: true, // 允许本地 ip 访问
    open: true, // 自动打开浏览器并访问本地服务器网址
    // open: 'http://localhost:5173', // 在浏览器中打开指定的路径
  },
})

奇趣周刊 - 第 63 期

2024-08-15 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 可视化股票 + 投资 + 财务分析神器

https://github.com/bklieger-groq/stockbot-on-groq
https://groq-stockbot.vercel.app/

由 Groq 提供支持的 StockBot:通过实时交互式股票图表、财务信息、新闻、筛选器等作出响应的闪电般快速的人工智能聊天机器人。由 Groq 上的 Llama3-70b、Vercel AI SDK 和 TradingView Widgets 提供支持。

2. 缤纷快传 - 无需登录的隔空投送工具

https://send.bitiful.com/

基于 Bitiful S4 对象存储的“Simul-Transfer(即传即收)”技术实现的大文件实时多人分享,Wetransfer 或 奶牛快传 的相似开源替代品。

1723423555335

3. 精致的图标制作工具

https://ray.so/icon

可以生成精美的网站图标或者 logo,可上传自己的图片然后调整,但是无法自定义文字,对于这点我还是有需求的。

1723423784939

4. Github 搜索净化插件

https://github.com/BonjourFeng/Github-Search-Purification

净化 Github 搜索页,在搜索结果中屏蔽 CiroSantilli 和 wumaoland 等 400+ 人的敏感仓库,还你一个清净页面。

5. PDF 在线转换工具

https://qwerpdf.com/zh-cn/

所有工具都是 100% 免费使用。使用 Word 在线编辑器将 PDF 转换为 Word(DOCX) 和 Excel(XLSX),合并、拆分和添加水印 PDF,支持多语言。

1723445024260

6. VideoTogether - 一起看视频的浏览器插件

https://github.com/VideoTogether/VideoTogether

和你的家人朋友一起在线看视频,无论相隔多远,在任何设备上都可使用,支持所有的在线视频网站和本地视频。

7. 基于 Github 的简单网站框架

https://github.com/qiayue/GitBase

GitBase 是一个没有传统数据库的开源动态网站解决方案,使用 Next.js、Tailwind CSS 和 Shadcn/UI 构建。它利用 GitHub 作为内容管理系统,提供创建和管理网站内容的无缝方式,可以在 Vercel 上一键部署。

8. 飞书文档批量导出脚本

https://github.com/xhnbzdl/feishu-doc-export

一个支持 Windows、Mac、Linux 系统的飞书文档一键导出服务,仅需一行命令即可将飞书知识库的全部文档同步到本地电脑。支持导出 markdown,docx,pdf 三种格式。导出速度嘎嘎快,实测 700 多个文档导出只需 25 分钟,且程序是后台挂机运行,不影响正常工作。

9. 用中文浏览国外社交媒体里的热门讨论

https://www.buzzing.cc/

用中文浏览国外社交媒体里的热门讨论,母语快速导读,感兴趣再进原文深度阅读。

1723514959171

serve00 如何登录?

2024-08-14 08:00:00

很多人在申请完 serve00 后无法登录,简单的介绍一下,首先会收到一封邮件,上面有:

Login: xxx
Password: xxx
SSH/SFTP server address: xxx

打开一个终端工具,比如 xshell

1723687086327

然后点击【用户身份验证】,选择 password,将账号密码输入,

1723687094941

点击连接后,会出现一个 SSH 身份验证,Keyboard Interactive(l) ,然后把 password 填入即可。

1723687097371

奇趣周刊 - 第 62 期

2024-08-08 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 将您的 Telegram 频道变成微博

https://github.com/ccbikai/BroadcastChannel

该项目支持在 Cloudflare/Netlify/Vercel 上部署,无需任何学习成本即可部署成功,支持自定义域名和 RSS,并提供自定义社交媒体等功能。

1722953452305

2. Cloudflare Proxy Sites - CF 镜像地址

https://github.com/seadfeng/cloudflare-proxy-sites

一款基于 Cloudflare Workers 的强大且易于使用的网站镜像工具。

3. 基于 vite 的油猴开发模板

https://github.com/lisonge/vite-plugin-monkey

一个 vite 插件,用来辅助开发 Tampermonkey, Violentmonkey, Greasemonkey, ScriptCat 等脚本引擎的脚本,支持热更新。

4. 一个有趣好用的工具站

https://cn.piliapp.com/

里面包含了各种有趣的项目。

1722994469820

5. 一个美观的 chrome 起始页

https://chromewebstore.google.com/detail/ddeaekifelikgnaacipabpmjpffgifek

1722996248006

6. AI 专辑封面生成器

https://remusic.ai/en/ai-album-cover-generator

用于生成艺术作品的封面图,输入提示,选择输出的图像大小,然后等待生成即可,每天有 10 次免费生成额度。

1722999313155

奇趣周刊 - 第 61 期

2024-08-01 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 一款高颜值 Docker 管理面板

https://github.com/lllllllillllllillll/DweebUI

DweebUI 是一个用于管理容器的 WebUI,简单的设置、动态更新的仪表板和多用户权限系统。

1721709404188

2. 一个图鉴网站(宝可梦肉鸽 PokeRogue)

https://pokeroguegame.net/zh-cn

PokéRogue 是一款由粉丝制作的网页游戏,灵感来自 roguelike 类型游戏。潜入随机生成的冒险中,与来自九代所有宝可梦战斗。每次运行都是独一无二的,具有随机布局和对手。

1721792701268

3. 一个简约舒适的网站统计

https://seline.so/

Seline 是一个简单且私人的网站和产品分析,无 Cookie、轻量级、独立,不过免费额度只有 3000 次。

1721870813861

4. 一个简单的海报生成工具

https://slogan.ishell.online/

1721877716083

5. 一个代码分享工具

https://showcode.app/

ShowCode 是一款让代码变得绚丽多彩的神奇工具,可以将其转化成绝美的图片与他人分享。不仅支持多种代码类型,还可以通过调整相关参数进行个性化设置,拥有多个迷人的主题供您选择。愿您尽情打造属于自己的视觉编码之旅!

6. 做旧 PDF

https://zh.lookscanned.io/

Look Scanned 是一个能够让 PDF 看起来就像是扫描件一样的纯前端网站。你再也不需要麻烦地打印之后扫描了,你所需要的就是鼠标点几下。

1721885342636

7. 在线生成带样式的 console.log 代码

https://www.v2ex.com/t/1060031

允许自定义 console 的样式并直接在浏览器或终端中测试其输出。

1721894262913

8. 高颜值的视频压缩工具

https://tools.rotato.app/compress

基于 FFMpeg 的 H264 压缩标准,实现压缩 90% 的视频大小。

1722397873506

马家坞看日出

2024-07-28 08:00:00

多图警告,加载缓慢。

(查看时建议点击图片查看全图~)

台风过去后据说日出特别好看,刚好赶到周日,我在凌晨 2 点定了个闹钟,驾车前往马家坞观景平台,停车还挺方便的,路边可以免费停车。

路程半个小时,又花了半个小时爬山,途中遇到很多同行的人,背着大包小包,摄像机。

大概在凌晨 3 点钟到达。

解锁了新的生活方式,不要被时间和距离束缚了灵魂。


杭州夜景。

1722166489590

小米 13 超级月亮模式,个人感觉有些算法模拟出来的效果,不过拍着还是很过瘾的。

1722166516417

俯瞰观景台,人还是蛮多的。

1722166795273

凌晨已经可以看到一些余晖了。

1722166761293

火烧云。

1722166771106

日出。

1722166779957

1722166787393

奇趣周刊 - 第 60 期

2024-07-25 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. GitHub 源码搜索引擎

https://grep.app/

通过关键词进行搜索,响应速度相当快可以在 1s 内返回结果,比官方搜索快多了,不清楚是如何做到的。

1721267596927

2. css 可视化使用 clip-path 裁剪属性

https://bennettfeely.com/clippy/

1721287688606

3. 网易云自动签到接口

https://github.com/chaunsin/netease-cloud-music

网易云音乐 Golang API 接口 + 命令行工具套件 (签到 + 音乐合伙人 + 云盘 + 下载 Hi-Res 音质 + 刷歌等) + 一键完成任务。

4. 前端面试题目清单

https://github.com/pwstrick/daily

一份搜集的前端面试题目清单、面试相关以及各类学习的资料(不局限于前端)。

5. Windows 下的最佳文件管理器

https://files.community/

Files 是一个现代文件管理器,可以帮助用户组织他们的文件和文件夹。

1721378265867

6. 设计师的 UI 库

https://www.eldoraui.site/

使用 React、Typescript、Tailwind CSS 和 Framer Motion 构建的开源动画组件,100% 开源,可定制。

7. 发现流行软件的开源替代品

https://openalternative.co/

OpenAlternative 是社区驱动的专有软件和应用程序的开源替代品列表,可替代日常使用中所需的软件。

1721614934771

8. 网页开发占位图

https://temp.im/

顾名思义,使用如下:

<img src="https://temp.im/300x300/eeeeee">

temp.im

9. 基于 FunASR 和 Qwen2 构建的音视频转结构化笔记系统

https://github.com/harry0703/AudioNotes

能够快速提取音视频的内容,并且调用大模型进行整理,成为一份结构化的 markdown 笔记,方便快速阅读。

FunASR: https://github.com/modelscope/FunASR

Qwen2: https://ollama.com/library/qwen2

10. 复古的 css 框架集合

https://github.com/matt-auckland/retro-css

里面包含中了各种 95、98 时期 UI 风格的 css 框架。

1721868692737

杭州国家版本馆文润阁

2024-07-20 08:00:00

多图警告,加载缓慢。

(查看时建议点击图片查看全图~)

场馆

1721475405497

1721475210796

古籍

《史记》 1721475004284

《永乐大典》 1721476025056

《万国舆图》 1721475073950

《本草纲目》 1721475173976

《天工开物》 1721475185270

《传习录》 1721475242242

器物

1721475265445

1721475313698

1721475332460

1721475345181

1721475356078

1721475370584

奇趣周刊 - 第 59 期

2024-07-18 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 简约的白噪音网站 - FreeMind

https://freemind.fit/

以大自然的声音为灵感,轻松创建专注或放松的音乐氛围。

1721090792886

2. 基于 Python 和 React 的高仿 ChatGPT UI 项目

https://github.com/orglen/gunara

基本的对话功能,支持自定义模型。

1721195213698

3. 精选的 Cloudflare 工具开源项目

https://github.com/zhuima/awesome-cloudflare

精选的 Cloudflare 工具、开源项目、指南、博客和其他资源列表。

1721195374810

4. 一个简洁且全面的 icon 图标站

https://icones.js.org/

包含各种类型的图标,支持搜索,支持在各个框架中以组件形式调用,或者生成下载各种格式的图标文件,非常简洁全面。

1721221718165

5. 互联网上最佳开源项目清单

https://oss.gallery/

网站风格简洁实用,汇集了大佬们提交的各种最佳开源项目,每个项目都添加了详细的统计信息,你可以通过这个网站发现许多有趣的开源项目。

1721222876407

6. 收录商用免费汉字字体

https://github.com/jaywcjlove/free-font

1721223238389

西溪湿地随手拍

2024-07-14 08:00:00

设备太差劲了,高倍变焦拍摄荷花高糊,有些不堪入目就放在最后了。

(查看时建议点击图片查看全图~)

1720962023228

1720962096325

1720962111313

1720962223523

1720962133210

奇趣周刊 - 第 58 期

2024-07-11 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 开源、简洁、可自部署的 RSS 翻译器

https://github.com/rss-translator/RSS-Translator

翻译标题或内容,双语显示,订阅翻译后的 RSS/JSON,支持多种翻译引擎,每个源都可以指定一个翻译引擎,控制翻译源的更新频率和查看翻译状态,缓存所有翻译内容,尽可能减少翻译费用,可查看每个源所花费的 Token/字符数,AI 内容总结获取全文。

2. STranslate 划词翻译

https://github.com/ZGGSONG/STranslate

STranslate 是一个开源的、即用即走的翻译和 OCR 工具,使用快捷键非常方便,响应速度相当快,支持各种配置,目前我已经安装上了,在电脑上深度使用中。

1720169550613

3. 豆包,浏览器 AI 助手

谷歌扩展

字节旗下的产品豆包所衍生的浏览器插件,拥有各种功能,ai 问答,网站总结,划词翻译,文字重写等,操作非常方便。

4. 在 Chrome 浏览器里如何拿到用户最真实的 CPU 和其他硬件信息

chrome.runtime.sendMessage('nkeimhogjdpnpccoofpliimaahmaaome',
{method: 'cpu.getInfo'},
response => console.table(response));

在使用 chromium 内核的浏览器中,打开谷歌系网站(*.google.com),然后在控制台运行上面的代码。

1720590570736

5. 经济学人、纽约客等英语外刊杂志下载

https://github.com/hehonghui/awesome-english-ebooks

经济学人 (含音频)、纽约客、卫报、连线、大西洋月刊等英语杂志免费下载,支持 epub、mobi、pdf 格式,每周更新的 GitHub 仓库。

6. 静态书签站点生成器

https://pintree.io/

在几分钟内将您的浏览器书签变成漂亮的导航网站。

1720621790994

7. 批量查询域名是否被注册

https://query.domains/

支持自定义域名后缀,输入前缀时可以自动查询是否被注册,以及其他公开的 WHOIS 信息。

1720658734997

奇趣周刊 - 第 57 期

2024-07-04 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. Navicat Premium Lite 17 数据库管理和开发工具

https://www.navicat.com/en/download/navicat-premium-lite

Navicat 提供的免费版本,功能非常全面,适合个人开发者。

1719455709413

2. 将网页转为安卓/IOS 应用

https://median.co/

一个功能强大的网站,可将 PC 网站转为手机安卓或 IOS 软件,对于只会前端,但又有做成 APP 需求的朋友是一个不错的方案。

1719455737455

3. aria2 下载器

https://ariang.mayswind.net/

AriaNg 是一个现代 Web 前端,使 aria2 更易于使用。AriaNg 是用纯 html 和 javascript 编写的,因此它不需要任何编译器或运行时环境。您只需将 AriaNg 放入您的网络服务器并在浏览器中打开它即可。AriaNg 使用响应式布局,并支持任何桌面或移动设备。
官方演示地址:https://ariang.mayswind.net/latest/

4. css 三角形生成器

http://tool.uis.cc/sjmaker/

前端同学可太需要这个工具了,在线生成想要的三角形的 css 代码。

1719883156229

5. CSS 奇技淫巧

https://github.com/chokcoco/iCSS

本 Repo 围绕 CSS/Web动画 展开,谈一些有趣的话题,内容天马行空,想到什么说什么,不仅是为了拓宽解决问题的思路,更涉及一些容易忽视或是十分有趣的 CSS 细节。

6. 生成音频 AI

https://fish.audio/zh-CN/text-to-speech/

提供丰富的音频资源使用 AI 进行合成,可玩性和效果很不错!

1720076433152

奇趣周刊 - 第 56 期

2024-06-27 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. FileWorker - 在线文件管理和剪切板

https://github.com/yllhwa/FileWorker

FileWorker 是一个轻量级的文件管理和在线剪贴板,基于 Cloudflare Pages 和 R2。

2. 一个可交互的动态网站

https://grantkot.com/ll/

试着调整浏览器的尺寸,真的很酷!

3. 一个好用的工具站

https://sinqi.tools/

满足罕见的和不常见需求的工具,简介无广告,本地优先。

1719221469568

4. 上网时间记录工具

https://github.com/sheepzh/timer

一个浏览器扩展,追踪你每天花在每个网站上的浏览时长,提供多方位的数据查询。分析不同时段的浏览习惯。已开源,可以通过 Chrome、Edge 和 Firefox 应用商店下载安装。

5. 图形编辑工具

https://www.drawio.com/

免费的图形编辑工具,本地优先,也可以通过各种云同步,包含协同功能,非常简介好用,无广告。

1719391733585

6. Claude 官方 prompt 提示库

https://docs.anthropic.com/zh-CN/prompt-library/library

探索针对各种商业和个人任务优化的提示,来自 Claude 官方,可以参考着将 prompt 复刻到 GPT 中。

1719400642659

7. 自动生成精美的 GitHub 个人主页

https://github.com/said7388/github-portfolio

只需要在配置文件中修改自己的 GitHub 用户名,并填写相关信息,即可通过 GitHub pages 生成一个精美的个人主页。

1719400943902

8. 阻止机器人抓取网站内容用于模型训练

CloudFlare 的又一壮举,支持在 Security 中设置一个自动程序,阻止机器人抓取网站内容以用于模型训练等 AI 应用程序。

1719401373680

9. 3D 语音对话模型网站

https://vidol.lobehub.com

由 lobehub 开发的网站,通过与 AI 对话,生成语音和模型,提供多种模型切换,与虚拟偶像一起玩。

1719402597578

奇趣周刊 - 第 55 期

2024-06-20 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 随机阅读一篇小说

https://cantsleepreads.com/

打开这个网址,每次都会随机刷新一篇短篇小说,很有意思的网站。

1717657068303

2. 自由的旅行指南 Wikivoyage

https://www.wikivoyage.org/

可以快速了解一个城市的区域环境,景点,饮食,住宿,夜生活等,方便前往游玩做攻略。

1718271427811

3. Google AI for Developers 弃用 Gemini 1.0 Pro Vision

自 2024 年 6 月 12 日起,Gemini 1.0 Pro Vision 模型将从 Google AI 服务和工具中弃用。7 月 12 日之后,在 Google AI Studio 中使用 Gemini 1.0 Pro Vision 保存的提示将切换为使用 Gemini 1.5 Flash。指定 Gemini 1.0 Pro Vision 的 API 调用将失败。

4. 一个在线的 AI 抠图网站

https://removerized.pages.dev/

可以进行在线抠图,消除背景只保留主体部分。

1718271408388

5. GitZip for github

https://chromewebstore.google.com/detail/ffabmkklhbepgcgfonabamgnfafbdlkn?utm_source=ext_app_menu

在网站上可视化下载 GitHub 单文件夹的扩展。

1718271389508

6. CF-Workers-docker.io:Docker 仓库镜像代理工具

https://github.com/cmliu/CF-Workers-docker.io

这个项目是一个基于 Cloudflare Workers 的 Docker 镜像代理工具。它能够中转对 Docker 官方镜像仓库的请求,解决一些访问限制和加速访问的问题。

7. 网站在线 PDF 文件查看器

https://github.com/mozilla/pdf.js
https://mozilla.github.io/pdf.js/web/viewer.html

PDF.js 是使用 HTML5 构建的便携式文档格式 (PDF) 查看器,基于 Web 运行,非常便捷,支持查看本地 PDF 文件,进行标记重新导出。

1718420707636

8. AI 肖像生成器 - OmniZero

https://omnizero.net/

生成独特的 AI 风格化肖像。可以完全指定风格,脸,和姿势!支持多语言。

1718675356735

9. 基于的 HTML 的极简列表看板

https://github.com/apankrat/nullboard

Nullboard 是看板/任务列表管理器的极简风格,设计紧凑、可读且易于使用。单页 Web 应用程序 - 只有一个 HTML 文件、一个古老的 jQuery 包和一个 webfont 包,可以完全离线使用。

1718775225814

随手拍无题

2024-06-08 08:00:00

1717855075533

奇趣周刊 - 第 54 期

2024-06-06 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 在浏览器中运行的 3D 场景

https://github.com/brunosimon/my-room-in-3d
https://my-room-in-3d.vercel.app/

纯前端实现的 3D 动画效果,可简单交互,可通过面板手动调节光影参数。

2. smartup 在谷歌商店被禁用

https://github.com/zimocode/smartup

在之前的周刊中,推荐过一款 chrome 手势扩展非常好用 — smartup。在 6 月 3 日被谷歌扩展商店禁用,目前原因不明,其实早在 2024 年 3 月 16 日 GitHub 仓库已经进入归档状态。smartup 已经无法从商店下载使用,但是可以 fork 一份源码到本地下载打包使用。

3. 看看你的扩展访问了哪些权限 - WhoUsesCookies

https://github.com/sshallow/WhoUsesCookies

近日,chrome 扩展商店下架了一批插件,可能是由于 manifest v2 引起的,因为 chrome 官方逐步减少对 v2 的支持,建议开发者使用 v3,同样能使用的权限也将会被严格限制,所以导致有些高权限的插件被检测出风险,其中最危险的是获取 Cookie 的权限,这个插件可以检测出你的浏览器中的扩展使用了哪些权限,并可以随时关闭它。

4. VSCode 插件 - Tailwind Fold

https://marketplace.visualstudio.com/items?itemName=stivo.tailwind-fold

可以通过这个插件折叠过长的 Tailwind 类名,告别混乱难读的 HTML 标签,提高代码的可读性。

5. 一个显示所有时区精确时间的网站

https://time.is/

Time.is 以 57 种语言显示所有时区(涵盖超过 7,000,000 个地区)精确的官方原子钟时间。

6. 3D 视差悬停效果

https://atroposjs.com/

Atropos 是一个轻量级、免费的开源 JavaScript 库,用于创建令人惊叹的触摸友好 3D 视差悬停效果,可用于 JavaScript、React、Vue 组件。

7. V2EX 正式上线 vxna

https://www.v2ex.com/xna

vxna 是 V2EX 网站中用于聚合优质博客、播客、期刊的栏目,上周从灰度转为正式上线。

奇趣周刊 - 第 53 期

2024-05-30 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. Old Twitter Layout

谷歌扩展

一款可以恢复旧版 Twitter 外观的扩展。

1716441616747

2. 极客时间电子书

https://github.com/it-ebooks-0/geektime-books

3. 城市租房生存指南

https://zufang.ababtools.com/

租房小白修炼手册,掌握租房硬核知识,找到理想住所!

1716460786429

4. Interface UI

https://github.com/interface-ui/interface-ui

Interface UI 是一个 Vue3 组件库,简洁、优雅、美观,帮助你快速构建网站,包含中英文文档。

1716467704731

5. 批量为视频生成字幕

https://github.com/buxuku/video-subtitle-master

批量为视频生成字幕,并可将字幕翻译成其它语言。这是在之前的一个开源项目 VideoSubtitleGenerator 的基础上,制作成的一个客户端工具。

1716519626044

6. AI 将照片转换为动漫风格

https://www.phototoanime.com/

1716540900212

7. Sink - 基于 Cloudflare 带访问统计的短链系统

https://github.com/ccbikai/sink
https://sink.cool/

具有分析功能的简单/快速/安全的链接缩短器,100% 在 Cloudflare 上运行。

8. Kimi Copilot - 网页总结助手

谷歌扩展

支持总结公众号文章,微信读书等网站,非常强大的一款免费插件。

1716601403777

9. 伟大思想家的名言

https://glasp.co/quotes

收藏了上千段名人名言。

1716775168161

无人机航拍青山湖

2024-05-26 08:00:00

上周五研究了好久买了一台大疆 mini4k,非常适合新手,想体验一下在天上俯拍的视角。

今天跟朋友驾车来到青山湖,拍摄了水上森林的视角。

1716713416023

1716713483621

Youtube 支持选择 4K 播放

国内环境用户前往 bilibili https://www.bilibili.com/video/BV1bz421e7qM

奇趣周刊 - 第 52 期

2024-05-23 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 网盘搜索项目

https://github.com/unilei/aipan-netdisk-search

该项目是一个基于 vue、nuxt.js 的网盘搜索项目,可以在 vercel 上自部署。

1716166508927

2. 天涯神贴收藏

https://github.com/jiji262/tianya-docs

收集全网爆火的天涯神贴 200+ 篇,全部为不带水印的原始版本,并且包含了地产预言大神 kk 的全集。

3. 简约翻译

https://github.com/fishjar/kiss-translator

一个简约、开源的 双语对照翻译扩展 & 油猴脚本。

4. Vite & uni-app 快速启动模板

https://github.com/uni-helper/vitesse-uni-app

由 Vite & uni-app 驱动的跨端快速启动模板

5. 利息计算器

https://ic.yolo.blue/

可查询多种利率的历史走势。

1716339287076

6. 宣布 Xshell、Xftp 和 Xmanager 8 公开测试版

https://www.xshell.com/zh/version-8-open-beta/

1716349565078

7. PC 微信读书显示评论的浏览器插件

https://github.com/my19940202/wx-read-comment-extension

一个让网页微信读书页面显示评论的插件,了解他人的见解,解答阅读中的困惑,不做一个孤独的阅读者。请求微信评论接口数据,侧边栏渲染评论数据,支持评论引用原文的展开和收起。

1716376894935

奇趣周刊 - 第 51 期

2024-05-09 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 将任何链接转换为友好的 markdown 文本

https://r.jina.ai/

例如 https://r.jina.ai/https://zishu.me/blog/weekly-50.html,就可以把文件重新排版成 markdown 格式文本。

1714785313326

2. 摄影景深模拟器

https://jherr.github.io/depth-of-field/

1714808380459

3. 通过 Front Matter 直接在 Vscode 中运行 CMS 的扩建

https://github.com/estruyf/vscode-front-matter

如果使用 Vscode 管理你的静态博客文件,并且博客程序使用的是 Hugo、Jekyll、Hexo、NextJs、Gatsby 等框架,那你可以试试这款插件,他是运行在 Vscode 中的 CMS 管理面板,可以进行内容、数据和媒体管理,搜索、过滤、排序等你的所有内容。

1714872642827

4. 免费和公共 api 目录

https://publicapis.io/

发现 700 多个公共和免费 API 的大量集合来增强您的项目!兼容 JavaScript、PHP、Ruby 和其他语言。

1714881113522

5. 浏览器扩展开发模板

https://github.com/cezaraugusto/extension.js

🧩 即插即用、零配置、跨浏览器扩展开发工具。

奇趣周刊 - 第 50 期

2024-05-02 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

上周因为工作问题暂停更新一周,十分抱歉,继续更新。

1. 将任何网页转换为干净的 Markdown 文本

https://markdowndown.vercel.app/

可以清理和转换任何网页为 Markdown 格式,具有删除非内容元素、下载图片和应用 GPT 过滤器进行最终编辑等功能。

1713337780088

2. 一个将文本转为手写体的网站

https://www.beautifulcarrot.com/

在线生成模拟手写文稿,让打印的字看起来和手写的一样,看颇有一些市场,对于我这种写字难看的懒人还有些作用。🧐

1713510788010

3. 一个可以生成可爱 logo 的网站

https://www.logocook.shop/

1714122692241

4. 网页版黑洞吞噬游戏

https://hole-io.com/

玩家控制一个黑洞,不停的吞噬,支持多人在线,非常好玩的网页游戏。

1714227693329

5. 一个开源的生成贴纸的网站

https://aistickergenerator.net/

1714350582314

6. vitepress 插件 - 自动生成左侧菜单

https://github.com/QC2168/vite-plugin-vitepress-auto-sidebar

通过扫描目录自动生成侧边栏数据的 vite 插件,基于 vitepress,每次新加 .md 文件时避免手动在 config.js 中手动修改左侧目录。

7. 接入 AI 的搜索引擎 globe

https://explorer.globe.engineer/

以嵌套网页形式展示搜索结果的产品 globe,层层嵌套,加载速度居然跟不上生成速度,简直离谱。

黄公望隐居地

2024-05-01 08:00:00

坐标:富阳 - 黄公望隐居地,也是富春山居图的实景地点。

从市区开车过去一个小时左右,路上的风景也非常棒,吹着风。

如果有无人机设备的话,从这个角度俯拍,实现的效果将会非常好。

1714569463737

vue 中配置 env 文件

2024-04-24 08:00:00

.env 命名规则。

# 开发环境 .env 文件名
.env.development

# 开发环境 .env 文件名
.env.production

在 vue-cli 中,要求环境配置文件必须以 VUE_APP 开头,如下:

# .env
VUE_APP_BASE_URL = http://0.0.0.0

# 使用
const baseURL = process.env.VUE_APP_BASE_URL;

修改 package.json 文件,在运行指令后加上 --mode 标识。

// package.json
"scripts": {
  "dev": "vue-cli-service serve --mode development",
  "build": "vue-cli-service build --mode production",
}

奇趣周刊 - 第 49 期

2024-04-18 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 网页图片翻译扩展

https://chromewebstore.google.com/detail/dlilbookfdhgpflfgpbdfhnejmcngeeg

一个 chrome 扩展,可以直接翻译网页上的图片中的英文,下载尝试了一下,效果出人意料的好。

这是官方演示:

2. 屏蔽 chrome 快捷键的扩展

https://chromewebstore.google.com/detail/aidbmcboeighgdnilpdljbedbbiocphj

在日常使用 chrome 过程中,因为开发习惯,会经常误触 ctrl+s 快捷键,然后弹出一个大大的保存页面,让人很烦躁。这个扩展可以解决这个问题,可以直接屏蔽在网页中的 ctrl+s 快捷键,使用起来体验很好。

3. 一个将 Web 保存到 Notion 的扩展

https://chromewebstore.google.com/detail/ldmmifpegigmeammaeckplhnjbbpccmm

支持保存文档,推文,帖子,电子邮件等各种信息到 Notion 中,响应速度很快,弹窗也很美观,可以自由拖拽,不用担心点击网页时 popup 消失的问题。

1712818264212

4. 三维可视化项目快速落地的开源框架

https://github.com/hawk86104/icegl-three-vue-tres

5. Windows 文件夹着色工具

https://github.com/kweatherman/Folcolor/

Folcolor 可以在 Windows 上给文件夹着色,对于区分不同类型很有帮助。仅 1M 的小工具,已开源。

1712908756603

6. 网页版文字游戏

https://www.myworldsai.com/

基本 AI 构建的网页版文字游戏,剧情自由度极高。

1712914789231

7. 开源的表单构建工具

https://github.com/heyform/heyform

HeyForm 是一个开源的表单构建工具,使用它你可以轻松创建问卷调查、测验、投票、报名等表单内容,并实时查看数据报表,是真正免费的 Typeform 平替。

1712915077743

8. 在线擦除图像网站

https://hapusobjek.com/

在线擦除照片上不需要物体的网站。

1713316138395

9. 微信公众号文章批量下载工具

https://github.com/qiye45/wechatDownload

一款可以批量下载微信公众号文章内容的小工具,无需安装证书,支持下载微信公众号的历史消息,可以保存文章为 html/md/pdf/docx 文件。

1713322168998

青龙湾游湖

2024-04-14 08:00:00

坐标安徽宁国。

这两天公司出去团建了,到了一些风景秀美的地方,有山有水,烟雾缭绕,颇有意境。

图一是皖南川藏线起点,在酒店的附近,两者相距很近。

早上起来,湿气很重,雾气弥漫。

1713097071969

图二是青龙湾湖面。

1713097731957

1713097129624

图三是乘坐游艇在湖面拍摄。

1713097144520

奇趣周刊 - 第 48 期

2024-04-11 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. Python 现在可以用于 Cloudflare Workers

https://blog.cloudflare.com/python-workers

Cloudflare Workers 现在支持 Python,使用 Pyodide 和 WebAssembly 直接集成了 Python 实现。首日即支持所有绑定,包括与 Vectorize、Workers AI、R2、Durable Objects 等的绑定。Python Workers 可以导入流行的 Python 包的子集,包括 FastAPI、Langchain、Numpy 等,没有额外的构建步骤或外部工具链。

1712108541000

2. Master Plan, Part Deux

https://www.tesla.com/blog/master-plan-part-deux

Musk 在八年前写的关于特斯拉的未来规划。

3. 推文/书签/收藏/列表导出工具

https://github.com/prinsss/twitter-web-exporter

将任何用户的推文、回复和点赞、书签等导出为 JSON/CSV/HTML,具体支持导出的内容可以关注GitHub仓库。

1712462582742

4. 游戏存档管理器

https://github.com/mcthesw/game-save-manager

这是一个简单易用的开源游戏存档管理工具。它可以帮助你管理游戏的存档文件,并且以用户友好的图像化窗口对你的存档进行描述、保存、删除、覆盖等操作。当前版本已经支持了云备份(WebDAV)、快捷操作等功能,且考虑到玩家的性能需求,该软件后台占用极小。

5. 一个精美的个人主页

https://aximoris.com/

1712566690798

6. 运行在 cloudflare 上的流量分析工具

https://github.com/benvinegar/counterscale

Counterscale 是一个类似 Umami,不过目前它还在开发阶段,可自行托管在 Cloudflare 上,可以使用 Cloudflare 的免费套餐,对小开发者用户来说非常友好。

1712629325057

7. 有关 4.8 腾讯云大故障的分析文章

https://mp.weixin.qq.com/s/PgduTGIvWSUgHZhVfnb7Bg

2024 年 04 月 08 日,腾讯云出现了一场全球性的大故障。这是一篇分析文章,从事发及过程,一直到结束,暴露出了一些问题,包括不限于掩盖真实故障范围,status 未同步,也有一种说法叫 status 造假,等等一系列问题,感兴趣的可以看下这篇文章。

8. Github 个人数据分析工具

https://gitroll.io/

一个有意思的工具,通过 Github 个人数据分析自己的技能等等。

1712759822365

9. 一个动漫角色图片生成工具

Github:https://github.com/makegirlsmoe/makegirlsmoe_web 网站:https://make.girls.moe/

一个动漫角色图片生成工具,你可以选择发色、发型、眼睛和皮肤色调,微笑和风格等,创造属于你的梦中二次元形象。

1712738393920

严州古城

2024-04-05 08:00:00

假期出行,前往距离杭州 160km 的严州古城,比较小众的景点,但是人并不少。

体验还是不错的,推荐前往。

1712409843872

1712409686821

1712410039316

奇趣周刊 - 第 47 期

2024-04-04 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. xz 被攻击的时间线

https://research.swtch.com/xz-timeline

在两年多的时间里,一名名为“Jia Tan”的攻击者作为 xz 压缩库的勤奋、有效的贡献者,最终获得了提交访问和维护的权限。利用该访问权限,他们在 liblzma 中安装了一个非常微妙的、精心隐藏的后门,liblzma 是 xz 的一部分,也恰好是 Debian、Ubuntu、Fedora 和其他基于 systemd 的 Linux 系统上 OpenSSH sshd 的依赖项。

该后门监视攻击者在 SSH 会话开始时发送隐藏命令,使攻击者能够在不登录的情况下在目标系统上运行任意命令:未经身份验证的、有针对性的远程代码执行。

2. 网页版 2048 小游戏

https://2024-game.net/

娱乐解压的网页小游戏,记录保存在浏览器本地存储中,关闭网页下次可以接着玩。

3. 开源、简洁、可自部署的 RSS 翻译器

https://github.com/rss-translator/RSS-Translator
https://rsstranslator.com/

1711943874345

4. 使用 React 构建的 Win95 风格网站

https://github95.vercel.app/

1711959243065

5. 面向初学者的生成式人工智能课程

https://microsoft.github.io/generative-ai-for-beginners/#/translations/cn/

通过微软云技术布道师团队提供的十二章系列课程,了解构建生成式 AI 应用程序的基础知识。每章都涵盖了生成式人工智能原理和应用程序开发的一个关键方面。在整个系列课程中,我们将建立我们自己的生成式人工智能初创公司,以便您可以了解如何实现您的想法。

1712024751483

6. 4 月 1 日 vue3 官网改名“威优易”

4 月 1 日 vue3 官网 改名“威优易”,这是一个愚人节玩笑。😂

1711935455188

7. Load Balancing 负载均衡

https://samwho.dev/load-balancing/

一篇分析负载均衡的文章,介绍负载均衡的原理、不同的负载均衡算法以及如何实现负载均衡。在结尾有一个相关的演示小游戏,觉得很有意思,分享出来。

1712027475429

8. 基于 Web 的 PDF 工具

https://pdf.datapipe.top/

本地部署的一站式服务,满足您的所有 PDF 需求,拥有大量的 PDF 相关功能。

1712030109178

9. chatGPT 官网无需登录即可使用

4 月 2 日,chatGPT 官网 https://chat.openai.com/ 无需登录即可使用,这不是灰度,是正式版本,进一步降低了使用者的门槛,不过之前也可以直接使用 Gmail 账户登录。

1712025831054

奇趣周刊 - 第 46 期

2024-03-28 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 小米汽车 SU7 的非官方介绍网页

https://gamemcu.com/su7/

非官方的小米汽车宣传页,特效真的酷爆了!极具艺术感,基于 Three.js,全部由 canvas 绘制而成,前端与前端的区别就是这么大。

1711506431108

2. Suno - 文字生成歌曲

https://app.suno.ai/

一个相当有意思的 AI 工具,可以通过文字生成一首歌,AI 编曲等,比如我上传一首诗就会输出美妙的音乐。

下面是我上传李商隐的《锦瑟》而生成的,可以欣赏一下。

3. Node.js 官网正式改版

3 月 20 日,全新的 Node.js 官网正式上线。

1711091488825

4. X-Hiring - 使用 AI 整理招聘信息

https://x-hiring.hehehai.cn/

每日最新招聘信息,网站数据来源于 V2EX 和电鸭社区,对招聘信息使用 Google Gemini 做摘要处理。

1711093694572

1711093694876

5. 丑丑头像生成器

https://txstc55.github.io/ugly-avatar/

一个非常搞笑的头像网站,会生成丑出天际的头像。😂

1711551392143

6. Node.js 纪录片 | 起源故事

https://www.bilibili.com/video/BV1Mj42197E4/

这应该是目前第一部以 Node.js 为主题的纪录片了,通过该纪录片深入了解 Node.js 的起源,认识一些最早的贡献者,主要包括 Ryan Dahl(Node.js 的创始人)、Isaac Schlueter(NPM 的创始人)等。

1711551476060

樱花飞舞的初春

2024-03-26 08:00:00

樱花飞舞的初春,半空中落英缤纷,蓝紫色桔梗似将画面停顿。

正是满城花开时。

1711446202384

骑行大莲花

2024-03-22 08:00:00

杭州·奥体·大莲花

1711092044620

1711092044611

奇趣周刊 - 第 45 期

2024-03-21 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1710550769638 原图地址:https://www.nasa.gov/wp-content/uploads/2024/03/flight-3-liftoff-20240314-001195.jpg

北京时间 3 月 14 日 21 点 25 分左右,马斯克旗下的 SpaceX(太空探索技术公司)在自家的星舰基地成功发射了“星舰”,并成功达到了太空,完成了该火箭的第三次关键试飞。

1. 开源的图片编辑器 - fabritor web

https://github.com/sleepy-zone/fabritor-web

纯浏览器端操作;操作自动保存到本地,数据不丢失,导出到剪贴板,导出 JPG、PNG、SVG 和模板(JSON),基于 JSON 可以构建模板库。

1710322343949

2. 一款精美的截图美化工具

https://www.photor.fun/

使用漂亮,简单强大,多种尺寸导出,截图、编辑到导出,完全运行本地浏览器中。

拥有 chrome,edge 插件,可以实现快速截图美化。

1710322417161

3. Image preview - 在代码中预览图像的 vsc 扩展

https://marketplace.visualstudio.com/items?itemName=kisstkondoros.vscode-gutter-preview

前端开发比较实用的插件,在代码中鼠标悬浮在图片链接上时,可以直接预览图像信息,不用打开文件夹查看,可查看的信息包括尺寸,内存大小,图片预览,还可以自由设置预览的窗口大小。

1710465713232

4. 自动总结文章的 chrome 扩展

https://chromewebstore.google.com/detail/ipnlcfhfdicbfbchfoihipknbaeenenm

Elmo 可以自动总结当前网站页面文章,速度快总结效率好,点击图标即可在侧边栏生成内容。

1710489652116

5. 开发人员参考备忘清单 (速查表)

https://github.com/jaywcjlove/reference

为开发人员分享快速参考备忘清单,包含各种常用的编程语言,指令,方法等,非常方便,可以 fork 一份自己部署以提高加载速度。

1710923626795

6. 马斯克开源 3140 亿参数的 Grok 模型

马斯克最近开源了一个具有 3140 亿参数的人工智能模型,名为 Grok。这个模型是由他领导的公司开发的,代表了当前人工智能技术中的一个重大突破。这个模型的规模和复杂度让它在处理自然语言理解、生成以及其他复杂任务时表现出色。

开源这个模型意味着研究人员、开发者和企业可以自由使用和修改 Grok,以探索和实现更多的应用可能性。这种开放的做法有助于加速人工智能技术的发展,通过共享知识和技术,整个社区都可以从中受益。

对于感兴趣的技术人员和公司而言,这是一个研究和应用最新人工智能技术的绝佳机会。可以期待在不久的将来,基于 Grok 模型的应用将会出现在各个行业和领域,从而推动整个社会的进步。

奇趣周刊 - 第 44 期

2024-03-14 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. semi design - 抖音团队设计出品

https://semi.design/

由抖音前端与 UED 团队维护,易于定制的现代化设计系统,帮助设计师与开发者打造高质量产品,适用于 react 框架,决策 不准备支持其他框架版本 , 比如 Vue 等。

1709459376922

2. 来自世界各地的公开 IPTV 频道集合

https://github.com/iptv-org/iptv

1709468139089

3. Latent Cat - 给页面添加 WebGL 动态背景

GitHub 源码:https://github.com/latentcat/uvcanvas/ 在线演示:https://uvcanvas.com/

一键给页面添加动态背景,动画很精美,仅支持 react。

1709533555295

4. Office Viewer(Markdown Editor)

https://marketplace.visualstudio.com/items?itemName=cweijan.vscode-office

一款 vscode 插件,支持预览各种形式的文件,比如 Excel,Word,markdown(目录,预览等)。

1709546012602

5. 多功能远程调试工具

https://github.com/HuolalaTech/page-spy-web

基于封装本地 Web API,当调用时,它会过滤和转换本地方法的参数,并将其转换为特定格式的消息,以供调试器客户端使用。调试器在接收到消息数据后,以类似于交互式开发工具的方式呈现用户界面,以便于查看。

1709865745093

6. 在浏览器中的互联网操作系统和桌面环境

https://github.com/HeyPuter/puter/
https://puter.com/

Puter 是一个先进的开源桌面环境,设计用于在浏览器中使用,功能丰富,速度异常快,可高度扩展。

它可以用于构建远程桌面环境,或作为云存储服务、远程服务器、Web 主机平台等的接口。

1709949260881

7. 一款开源的油猴翻译插件

https://github.com/Bistutu/FluentRead

拥有基于上下文语境的人工智能翻译引擎,为网站提供更加友好的翻译,让所有人都能够拥有基于母语般的阅读体验,目前在 GitHub 上拥有 400+ star,使用效果还是非常不错的。

程序开源、免费,代码接受审查、不收集任何用户信息,保证数据安全,支持常见国外或国产 AI 大模型,支持 chatGPT 自定义 API 接口地址,支持使用国内代理访问。

8. Hacker News API

https://github.com/HackerNews/API

Hacker News 新闻的公开 API,支持 Android、iOS 和 Web 的访问,可以利用 API 开发第三方 Hacker News 服务,目前没有速率限制。

支持文章,问答,评论,工作,投票,用户查询,以及热门最新最佳等列表,API 形式如下:

https://hacker-news.firebaseio.com/v0/item/8863.json?print=pretty

9. 深入架构原理与实践

https://www.thebyte.com.cn/

这是一本关于架构设计的开源书籍,整个系列的内容主要集中在 网络、集群以及服务治理、FinOps 这三个主题,这也代表着基础架构的几个核心:稳定、效率、成本。作者尝试使用费曼学习法把这些东西体系化地总结输出。一方面是加深自我的学习认识,另一方面也希望这些输出对其他人有所帮助。

1710294706423

奇趣周刊 - 第 43 期

2024-03-07 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 部署可订阅微信公众号的 rss 源 - WeWe RSS

https://github.com/cooderl/wewe-rss

免费、开源的微信公众号订阅方式,支持私有化部署、微信公众号 RSS 生成。部署文档暂不完善,期待中。

1709169596260

2. Simple Resume

https://github.com/LynanBreeze/simple-resume

一个简约的简历,可自部署,基于 vite 开发。

1709175395345

3. LeapSearch

https://chromewebstore.google.com/detail/lapaieaegaclofackbjhjhbhfiplcgki

高效地在不同搜索引擎之间切换,是一个不错的小工具。

1709191697179

4. ChatGPT-Next-Web

https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web/
https://app.nextchat.dev/

一键拥有你自己的跨平台 ChatGPT/Gemini 应用。

1709303249373

5. Node.js 官网设计草案

https://beta-node-js-org.vercel.app/
https://github.com/nodejs/nodejs.org/pull/6369

Node.js 官网重新设计,目前还只是一个草案,好像出自 vercel 团队,期待正式上线。

1709305320717

6. 文章同步助手 Wechatsync - chrome 扩展

https://github.com/wechatsync/Wechatsync

一键同步文章到多个内容平台,支持今日头条、WordPress、知乎、简书、掘金、CSDN、typecho 各大平台,一次发布,多平台同步发布,解放个人生产力。

1709305604810

7. 一位博主分享自己环游中国的体验

https://twitter.com/ezshine/status/1763538685159465250
https://www.youtube.com/watch?v=STZkkKE8zfc

8. 生成 YouTube 视频摘要

https://summarize.ing/

当您被大量内容淹没时,Summarize.ing 在这里助您一臂之力。它是一个 AI 驱动的 YouTube 工具,能在科技、营销、经济、时事或健康等领域中精准地抓取关键见解。

1709391257952

9. 有道灵动翻译 - chrome 扩展

https://magicfanyi.youdao.com/

功能和【沉浸式翻译】基本一致,速度也挺快的,但整体的体验还有待提高,比如翻译的样式,暂无 YouTube 视频翻译。

但是有道灵动翻译有一个不错的功能,支持图片翻译,如果图片中包含英文可直接翻译,省去了 ocr 的步骤,这点跟微信的图片翻译类似。

这两者应该算是同一赛道的产品,希望功能越做越好。

1709391669335

值传递和引用传递、深拷贝和浅拷贝

2024-03-01 08:00:00

vue 把一个值赋给两个变量,修改一个变量时,另一个变量也会修改。

原因分析

this.a = res.data;
this.b = res.data;

同时给两个变量 a 和 b 赋值时,修改 a 的内容,打印 b 结果是一样的,因为这里的赋值是属于引用传递的,把 res.data 这个地址赋给 a 和 b,当修改 a 时,res.data 会同步变动,导致 b 也发生了改变。

这种赋值方式也被称为浅拷贝,当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。

解决方案

this.a = JSON.parse(JSON.stringify(res.data));
this.b = JSON.parse(JSON.stringify(res.data));

使用 JSON.parse 和 JSON.stringify,将他变成一个常量值,这时再赋值给 a 和 b 的时候,就属于值传递了,这种赋值方式也叫做深拷贝,每个对象都对应一个新的空间,和其他对象互不影响。

wewe-rss 项目部署

2024-02-29 08:00:00

1709203891809

Github 仓库:https://github.com/cooderl/wewe-rss/

免费、开源的微信公众号订阅方式,支持私有化部署、微信公众号 RSS 生成,部署问题也可以提 issues。

在线感谢 @潇潇 !!

部署的一些流程,仅供参考。

在服务器 /data/ 目录下新建 rss 文件夹,新建 docker-compose.yml 文件。

xx.xx.xx.xx 是你服务器的 ip。

# /data/rss/docker-compose.yml
version: '3.9'

services:
  server:
    image: cooderl/wewe-rss-server:latest
    ports:
      - 4000:4000
    depends_on:
      db:
        condition: service_healthy
    environment:
      # 数据库连接地址
      - DATABASE_URL=mysql://root:123456@db:3306/wewe-rss?schema=public&connect_timeout=30&pool_timeout=30&socket_timeout=30
      # 服务接口请求授权码
      - AUTH_CODE=123567
      # 自动提取全文内容
      - FEED_MODE=fulltext
      # 服务接口请求限制,每分钟请求次数
      - MAX_REQUEST_PER_MINUTE=60
      # 外网访问时,需设置为服务器的公网 IP 或者域名地址
      - SERVER_ORIGIN_URL=http://xx.xx.xx.xx:4000/

  web:
    image: cooderl/wewe-rss-web:latest
    ports:
      - 3000:3000
    environment:
      # 同 SERVER_ORIGIN_URL
      - NEXT_PUBLIC_SERVER_ORIGIN_URL=http://xx.xx.xx.xx:4000/

networks:
  wewe-rss:

运行 docker 指令

docker-compose up -d

在服务器运营商处给服务器防火墙放行端口 3000, 3307, 4000。

1709203859913

奇趣周刊 - 第 42 期

2024-02-29 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. Google 推出 Gemma 开放模型

https://ai.google.dev/gemma

一个系列轻量级、先进的开放式模型,采用与创建 Gemini 模型相同的研究和技术构建而成。

1708564997440

2. chinese-poetry: 最全中文诗歌古典文集数据库

https://github.com/chinese-poetry/chinese-poetry

最全中华古诗词数据库,唐宋两朝近一万四千古诗人,接近 5.5 万首唐诗加 26 万宋诗。两宋时期 1564 位词人,21050 首词。

为什么要做这个仓库?古诗是中华民族乃至全世界的瑰宝,我们应该传承下去,虽然有古典文集,但大多数人并没有拥有这些书籍。从某种意义上来说,这些庞大的文集离我们是有一定距离的。而电子版方便拷贝,所以此开源数据库诞生了。此数据库通过 JSON 格式分发,可以让你很方便的开始你的项目。

1708572403880

3. 2024 年的 JavaScript

https://tonsky.me/blog/js-bloat/

Niki 详细研究了 2024 年各种网站的 JavaScript 膨胀问题,比较了不同的网站(包括社交网络、电子邮件、音频/视频流应用、交互式应用等)的 JavaScript 虚拟大小。结果表明,许多网站的 JavaScript 论述过于庞大,不仅对网页的加载速度产生影响,也严重影响了浏览器必须解析、存储和执行的代码加载。Niki 指出,一个博客发布的内容可能只有 10k 字符,但却需要 1000 倍以上的 JavaScript 来呈现,我们需要避免不必要的代码膨胀,以提高网页性能和用户体验。

4. 一款精美的在线简历网站

https://www.resumeis.com/

1708915491077

5. vue-quill-editor

Github 源码:https://github.com/surmon-china/vue-quill-editor 在线演示:https://github.surmon.me/vue-quill-editor

基于 quill 并且适用于 vue2 框架的富文本编辑器(不适用 vue3 版本)。

6. notion-api-worker

https://github.com/splitbee/notion-api-worker

在 cloudflare workers 上部署 notion api,更快更轻松的访问 notion 内容。

无法从非 EcmaScript 模块导入命名导出

2024-02-23 08:00:00

https://github.com/markdown-it/markdown-it

markdown-it 是一个基于 vue 的解析 markdown 语法的库。

在 vue 中引入 markdown-it 模块时遇到一个问题,dev 开发运行时报出以下错误,无法从非 EcmaScript 模块导入命名导出。

# shell
 error  in ./node_modules/markdown-it/lib/common/utils.mjs
Can't import the named export 'P' from non EcmaScript module (only default export is available)
 error  in ./node_modules/markdown-it/lib/index.mjs
Can't import the named export 'decode' from non EcmaScript module (only default export is available)
 error  in ./node_modules/markdown-it/lib/index.mjs
Can't import the named export 'decode' from non EcmaScript module (only default export is available)
 error  in ./node_modules/markdown-it/lib/common/utils.mjs
Can't import the named export 'decodeHTML' from non EcmaScript module (only default export is available)
 error  in ./node_modules/markdown-it/lib/rules_inline/entity.mjs
Can't import the named export 'decodeHTML' from non EcmaScript module (only default export is available)
 error  in ./node_modules/markdown-it/lib/index.mjs
Can't import the named export 'encode' from non EcmaScript module (only default export is available)
 error  in ./node_modules/markdown-it/lib/index.mjs
Can't import the named export 'format' from non EcmaScript module (only default export is available)
 error  in ./node_modules/markdown-it/lib/index.mjs
Can't import the named export 'format' from non EcmaScript module (only default export is available)
 error  in ./node_modules/markdown-it/lib/index.mjs
Can't import the named export 'parse' from non EcmaScript module (only default export is available)
 error  in ./node_modules/markdown-it/lib/index.mjs
Can't import the named export 'parse' from non EcmaScript module (only default export is available)

导致 import 该库出现错误,解决方案需要修改 vue.config.js

// vue.config.js
module.exports = {
  configureWebpack: {
    module: {
      rules: [{
        test: /\.mjs$/,
        include: /node_modules/,
        type: "javascript/auto"
      }]
    }
  },
};

参考文献:https://github.com/vuejs/pinia/issues/675

奇趣周刊 - 第 41 期

2024-02-22 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. OpenAi 推出 Ai 视频模型 Sora

https://openai.com/sora

Sora 是一个人工智能模型,可以根据文本指令创建逼真而富有想象力的场景。其主要目标是教会 AI 理解并模拟现实世界的运动,进而帮助人们解决需要真实世界互动的问题。Sora 可以根据用户的提示,生成长达一分钟的视频,同时保持视觉质量和对用户提示的遵守。

2. 图解 react 源码

https://github.com/7kms/react-illustration-series

react 源码,基于 [email protected] (尽可能跟随 react 版本的升级,持续更新). 用大量配图的方式,致力于将 react 原理表述清楚。

1708263270282

3. 使用 cloudflare 免费服务,搭建临时邮箱

Github 源码:https://github.com/dreamhunter2333/cloudflare_temp_email
在线演示:https://temp-email.dreamhunter2333.xyz/

1708392368444

4. 一个基于 Cloudflare 的临时邮箱工具 (未开源)

https://email-once.com/

1708334903364

5. 放大图片的网站 - Bigjpg

https://bigjpg.app/

将模糊的图片放大到清晰,可以选择倍率,UI 不错的工具。

1708400085251

6. Bard 更名 Gemini

https://gemini.google.com/app
https://blog.google/products/gemini/bard-gemini-advanced-app/

Gemini 是 Bard 的新名称,旨在提供最先进的 AI 模型。便捷的手机应用使协作变得更简单。今天推出的 Gemini Advanced 提供 Ultra 1.0,这是目前最大且最先进的 AI 模型,其在盲评中相较于其他聊天机器人获得了最高的青睐。Gemini Advanced 可以处理更复杂的任务,并提供更长、更详细的对话,能更好地理解前面的提示的上下文。此外,新推出的 Gemini 应用和其高级版本设有 Android 和 iOS 的应用。

1708405699686

7. Web 浏览器登录 Telegram

Github 源码:https://github.com/morethanwords/tweb
Web 网站:https://web.telegram.org/k/

基于 Webogram 源代码,用 TypeScript 重写,进行了修补和改进。

8. Github 个人主页信息图表

https://github.com/lowlighter/metrics

信息图表生成器,具有 30 多个插件和 300 多个选项,可显示有关您的 GitHub 帐户的统计信息并将其呈现为 SVG、Markdown、PDF 或 JSON!

1708485933827

9. 适合每个 GitHub 用户的自动作品集构建器

https://github.com/arifszn/gitprofile

GitProfile 是一个功能强大的作品集构建器,即使您没有编码经验,也可以在几分钟内创建令人惊叹的个性化作品集网站。只需提供您的 GitHub 用户名,GitProfile 就会自动生成一个作品集。

1708486960348

10. 用于在 GitHub 上显示存储库大小

Github 源码:https://github.com/harshjv/github-repo-size
Chrome 扩展商店:https://chromewebstore.google.com/detail/github-repository-size/apnjnioapinblneaedefcnopcjepgkci

自动将存储库大小添加到 GitHub 的存储库摘要中。

11. 绘制 Github Star 历史增长数量

https://github.com/caarlos0/starcharts

1708488006321

12. 一个好玩的仓库

https://github.com/iBug/This-Repo-Has-566-Stars

一个好玩的仓库,当 star 后,会自动改名。

奇趣周刊 - 第 40 期

2024-02-15 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 在线编辑 svg 的工具

https://yqnn.github.io/svg-path-editor/

一个不错的 svg 编辑器工具,可以通过拖拽,生成想要的 svg 图案,还是挺方便的一个工具,开源地址 Github

1707008002313

2. 摸头杀生成器

https://toolwa.com/petpet/

可以通过上传本地图片生成一个摸头的动图,挺有意思的。

1707009716946

3. 基于 vite + vue3 的谷歌扩展模板

https://github.com/mubaidr/vite-vue3-chrome-extension-v3

基于 manifest3、vue3 和 vite 的 Vite 支持的 WebExtension(Chrome、FireFox 等)入门模板,之前推荐的另一个谷歌扩展模板是基于 webpack + vue 的。

4. 一个简单&强大的 Web 思维导图。

Github 源码:https://github.com/wanglin2/mind-map/
在线演示:https://wanglin2.github.io/mind-map/

功能还算强大的节点思维导图,目前在 GitHub 上拥有 2.5k star,基本上所有的功能都支持,项目包含两部分:

  1. 一个 js 思维导图库,不依赖任何框架,你可以使用它来快速完成 Web 思维导图产品的开发。
  2. 一个 Web 思维导图,基于思维导图库、Vue2.x、ElementUI 开发,可以操作电脑本地文件,所以你可以直接把它当做一个在线版思维导图应用使用,如果觉得 github 的响应速度慢,你也可以部署到你的服务器上。

1707025141617

5. 基于 Go 的文件分享工具

Github 源码:https://github.com/songquanpeng/go-file/
在线演示:https://go-file.onrender.com/

基于 Go 的文件分享工具,仅单可执行文件,开箱即用,内置图床和视频播放页面,可用于局域网内分享文件和文件夹,直接跑满本地带宽。

1707964744709

6. OpenAI 尝试为 ChatGPT 提供长期对话记忆

https://arstechnica.com/information-technology/2024/02/amnesia-begone-soon-chatgpt-will-remember-what-you-tell-it-between-sessions/

这篇文章介绍了 OpenAI 为其聊天机器人 ChatGPT 增加长期记忆功能的实验。当这个功能启用后,ChatGPT 能够记住与你之间对话的信息,并在下次对话时提取这些信息。目前,此功能只对少数 ChatGPT 用户进行测试。此外,OpenAI 还表示,记忆功能可能帮助 ChatGPT Enterprise 和 Team 订阅用户更好地协作,因为共享的团队记忆可以记住特定的文件格式化偏好或团队正在使用的编程框架等信息。然而,记忆功能也带来了隐私问题,OpenAI 表示正在采取步骤评估和减少偏见,并引导 ChatGPT 避免主动记住敏感信息,除非用户明确要求,到目前为止,OpenAI 未公布 ChatGPT 记忆功能的推出时间。

新年快乐

2024-02-09 08:00:00

杭州·城市阳台灯光秀。

1707483889469

奇趣周刊 - 第 39 期

2024-02-08 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

明天就是除夕,提前祝大家新年快乐~~

1. 语雀工具集合 - 导出笔记

https://github.com/vannvan/yuque-tools

语雀知识库 + 团队资源批量导出/备份工具 (无需 Token)|浏览器插件助手。

2. 快速涂鸦

https://quickdraw.withgoogle.com/

和 AI 玩你画我猜,暂不支持多人联机,只能单机和 AI 玩,可以在一个人摸鱼的时候玩耍。

1706757894914

3. 一个 GIF 表情网站

https://gifer.com/en/

这是一个提供各种 GIF 动图的网站,可以根据关键词在网站上搜索查看各种 GIF 动图。

1706777344526

4. 网页版地铁快跑

https://github.com/DanielLin0516/SUBWAY-SURFERS

非常不错的网页小游戏。

5. 电影推荐《少年斯派维的奇异旅行》

10 岁男孩 T.S.斯派维对制图学和科学发明很有热情。有一次,史密森尼博物馆告诉他,他的永动机发明赢得了非常著名的贝尔德奖,邀请他前去领奖并发表演讲。事先没有告诉其他人的斯派维带着一个望远镜、四个圆规和种种曾曾祖母留给他的回忆,登上一辆货运火车横跨美国抵达华盛顿特区。

1706845633196

6. soybean-admin:vue3 + vite 优雅的后台管理模板

在线演示:https://soybeanjs.cn
Github 源码:https://github.com/honghuangdc/soybean-admin

一个清新优雅、高颜值且功能强大的后台管理模板,基于最新的前端技术栈,包括 Vue3, Vite5, TypeScript, Pinia, NaiveUI 和 UnoCSS。

1706859742643

7. vue-draggable-plus:一个基于 vue3 的拖拽库

中文文档:https://alfred-skyblue.github.io/vue-draggable-plus
Github 源码:https://github.com/Alfred-Skyblue/vue-draggable-plus

这是一个适用于 vue3 的拖拽组件库,支持组件、函数或者指令方式书写,可以在任何元素上使用拖拽列表,支持表格,动画,嵌套等,目前在 GitHub 上拥有 1.9k star。

npm install vue-draggable-plus
<template>
  <VueDraggable ref="el" v-model="list">
    <div v-for="item in list" :key="item.id">{{ item.name }}</div>
  </VueDraggable>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { VueDraggable } from 'vue-draggable-plus'
const list = ref([
  {name: 'Joao',id: 1},
  {name: 'Jean',id: 2},
  {name: 'Johanna',id: 3},
  {name: 'Juan',id: 4}
])
</script>

8. 寻找书写 css 的灵感

https://csscoco.com/inspiration/

这里可以让你寻找到使用或者是学习 CSS 的灵感,以分类的形式,展示不同 CSS 属性或者不同的课题使用 CSS 来解决的各种方法。

1706920963249

9. 搜索打开的标签页的内容 - 跨标签页 ctrl+f

https://chromewebstore.google.com/detail/dbppkkjmaloclihjakcpnionomlidbhk
https://github.com/MrViolets/Deep-Tab-Search

一款 chrome 扩展,支持搜索当前浏览器所有打开的标签页的内容,相当于一个跨标签页的 ctrl+f,在某些场景下还是很好用的,比如打开了大量了标签页,忘记了自己想要的内容在哪个标签页内,可以使用这个扩展进行搜索,搞笑切换,排版也很美观。

1706921886565

10. 电视直播软件

https://github.com/lizongying/my-tv

安卓电视直播软件,内置直播源。

奇趣周刊 - 第 38 期

2024-02-01 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 幻兽帕鲁自建服务端 docker

https://github.com/thijsvanloef/palworld-server-docker

最近一周,steam 游戏《幻兽帕鲁》非常火热,游戏内核很好玩,并且官方支持自建服务端,该仓库可以使用 docker 一键部署服务端,存档可以放在自己手中,但是自建服务端对机器配置要求还是比较高的,内存最好 16G 往上。

2. 将 warp 节点转换成订阅地址

https://tofree.zeabur.app/
https://github.com/vvbbnn00/WARP-Clash-API/

cloudflare 提供了免费的代理工具 warp+,但是需要借助软件,网络不稳定,这个网站将 warp 的节点转换成订阅地址,让你可以在各种客户端上通过订阅使用 warp+ 节点,代码已开源,提供了 docker 自部署,自动刷新订阅,IP 优选等功能。

1706496643123

3. 没有人关心你的 Git 历史记录

https://spin.atomicobject.com/git-history/

这篇文章讨论了 Git 历史的重要性,以及变基、压缩和提交消息的意义。Git 历史对于软件团队来说是一个无名英雄,但对其他人可能构成了困扰。文章探讨了“干净的 Git 历史”的含义,以及进行重写和压缩的必要性和优点;还讨论了哪些团队和个人应该使用变基,以及使用变基的利弊;同时提到了保持详细的提交历史的价值和重要性,以及重写历史可能带来的问题和风险。

4. 新版本 chrome network 展示修改

新版本 chrome network 资源类型展示变成下来,看起来非常别扭,可以在控制台设置中修改,将此项取消勾选。

1706260620905

1706260504246

5. AITDK:一键查看网站流量/Whois/SEO 等信息

https://chromewebstore.google.com/detail/hhfkpjffbhledfpkhhcoidplcebgdgbk

当我们分析某个网站的流量/Whois/SEO 等信息时,经常需要打开多个插件或多个网站,非常低效,AITDK Extension 可以一键快速查看网站流量/Whois/SEO 等信息。

1706582130424

6. daisyui:使用纯 css 实现的组件库

https://daisyui.com/
https://github.com/saadeghi/daisyui

不局限于前端框架,和 Tailwind 结合使用,将类名整合起来,更简单易用,可以使用 npm / yarn 等包管理工具引入到各类框架中,也可以使用 cdn 引入到 html 中。

1706583481983 1706583682653 1706583682937

7. 掌上宇宙

https://cocosmos.online/

可以通过在线网站实时观测宇宙模型,挺好玩的一个工具。

1706671093303

8. Gemini 在线网页

https://ibcl.us/ChatGemini/
https://github.com/bclswl0827/ChatGemini/

ChatGemini 是一个基于 Google Gemini 的网页客户端,对标 ChatGPT 3.5,使用逻辑同 ChatGPT 3.5 一致,同时支持在聊天中上传图片,自动调用 Gemini-Pro-Vision 模型进行识图。

本项目还可自定义 Gemini API 服务器地址,用户可将本项目部署至支持 PHP 的服务器或虚拟主机上,或是自行配置 Nginx 反向代理,透过修改 Gemini API 路径,从而在中国大陆无障碍使用。

1706748456237

奇趣周刊 - 第 37 期

2024-01-25 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

历经 37 期,正式将周刊名称确定为“奇趣周刊(Funny Weekly)。”

1. Notion 发布新功能 Calendar

https://www.notion.so/product/calendar
https://www.notion.so/blog/introducing-notion-calendar

Notion 发布新功能 Calendar,解决了日历软件与其他工具之间断裂的问题,改善了工作日程与个人计划分离,以及会议与项目时间线脱离等问题。将 Google 日历连接至 Notion Calendar,整合员工的工作任务、会议记录等信息。提高专注力,防止分散注意力,整合工作与生活的日程管理,减少在不同日历间切换的麻烦。自动时间,功能避免时间冲突和重复预定,内置预约功能,分享空闲时间,直接预约会议。

我可能不会使用它,因为目标用户是那些对 Notion 有深入接触且使用 Google 日历的用户。但将它们功能结合起来确实是个不错的想法,进一步壮大了 Notion 生态。

1705548519153

2. 提示用户是否在 GitHub 上关注您

https://github.com/timqian/follows-you

添加一个标签,提示用户是否在 GitHub 上关注您,一个功能很简单的 chrome 插件,不过挺有意思的,支持一下。

1705541877729

3. 基于 react 的 electron 开发模板

https://github.com/codesbiome/electron-react-webpack-typescript-2024

上期分享一个 基于 vite 的 electron 开发模板,我又在 GitHub 上发现一个基于 React + Webpack + Typescript 的 Electron 框架,具有热重载、易于使用的特性。

1705546839975

4. vite + vue3 后台管理系统

https://github.com/un-pany/v3-admin-vite

V3 Admin Vite 是一个免费开源的中后台管理系统基础解决方案,基于 Vue3、TypeScript、Element Plus、Pinia 和 Vite 等主流技术。风格简洁统一,代码清晰,目前在 GitHub 拥有 3.2k star,作为后台管理项目二开非常不错!

1705649734133

5. 打开就能看电影的 Web 应用

https://github.com/movie-web/movie-web
https://movie-web.app

该服务的工作原理是在直观且美观的用户界面中显示来自第三方提供商的视频文件,自动保存进度,为节目或电影添加书签,跟踪您想观看的内容,简约的界面仅显示所需内容。

1705652570738

6. 一款极易搭建的自助 Git 服务

https://github.com/gogs/gogs
https://gogs.io

gogs 是一款极易搭建的自助 git 服务,可以在任何支持 go 语言的平台运行,所需的配置极低,甚至一台小小的树莓派都足以支撑运行,也可以安装在 nas 设备上,非常方便,功能很强大,页面简洁。

1705657649604

7. 自定义滚动条的网站

https://scrollbar.app/

可以通过简单的配置,生成想要的滚动条样式并输出代码,很简单也很实用的小工具。

1705731526788

8. 阿里网盘资源搜索

https://ssgo.app/

可以在线搜索想要的资源,比如电影,音乐,视频等。

1705980532152

9. 大盘云图

https://dapanyuntu.com/

展示股票信息,在交易时间内,每 10 秒刷新一次最新数据,按照流通市值对云图进行排序。

1705902643809

奇趣周刊 - 第 36 期

2024-01-18 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1705398545782

圆明园遗址。


1. 一个纯前端实现的头像生成网站

https://vue-color-avatar.leoku.dev/

矢量风格头像的生成器,可以搭配不同的素材组件,生成自己的个性化头像,支持批量生成。

1705028958816

2. 基于 vite 的 electron 开发模板

https://github.com/electron-vite/electron-vite-vue

开箱即用,可扩展,基于官方模板 template-vue-ts,侵入性较小,非常简单的目录结构,支持在 Electron-Renderer 中使用 Node.js API.

# clone the project
git clone https://github.com/electron-vite/electron-vite-vue.git
# enter the project directory
cd electron-vite-vue
# install dependency
npm install
# develop
npm run dev

3. 执行 es6+ts 代码

https://github.com/TypeStrong/ts-node

这是一个可以同时执行 es6 + typescript 代码的库。

1704942373638

4. 微信公众号转 RSS

https://github.com/ttttmr/Wechat2RSS

顾名思义,这是一个可以将微信公众号转为 rss 订阅源的服务。

5. v2ex 图片灯箱油猴脚本

https://greasyfork.org/zh-CN/scripts/454963-v2ex图片灯箱插件

灵感来源于日常使用时查看图片,会跳转到如 imgurl 网站,不能在当前页面详细查看,所以基于 simplebox.js 搞了这么一个插件,可以满足点击放大图片的需求。

6. pdf,epub,html,txt 文件翻译

https://app.immersivetranslate.com/

这是 沉浸式翻译 插件的一个附属功能,之前分享过 沉浸式翻译,这个网站可以翻译 pdf,epub,html,txt,字幕文件 srt,ass。

1705136286294

7. 马斯克作 SpaceX 公司演讲:总结 2023,展望 2024

来自 YouTube 的视频。

8. shadcn-vue

https://github.com/radix-vue/shadcn-vue
https://www.shadcn-vue.com

是一个基于 vue 的类 shadcn/ui 风格 ui 组件库,对 element 审美疲劳的前端开发者可以尝试使用这个,展示效果非常不错。

1705472993534

圆明园遗址

2024-01-16 08:00:00

有事在北京待了两天,去圆明园遗址溜达一下,建筑物被损坏确实有些可惜。

1705398545782

1705398545716

1705398545674

奇趣周刊 - 第 35 期

2024-01-11 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. Github 热门仓库

https://rising-repo.vercel.app

GitHub 根据 star 增长的趋势排行榜单,可以根据各种条件进行排序,比如变成语言,时间线等。

1704330226216

2. Atom One Dark Theme - vscode 主题插件

https://github.com/akamud/vscode-theme-onedark

VSCode 主题基于 Atom 的 One Dark 主题,市场上评价最高的 One Dark 主题端口,具有完整的 Workbench 主题。

1704683734816

3. 趣味美术画板

https://github.com/LHRUN/paint-board
https://songlh.top/paint-board

基于 Canvas 的画板,包括自由绘图、橡皮擦、文本、选择、图层、撤消、重做、清除、保存、拖动。

1704685787367

4. 纯 JS 实现的 ChatGPT 项目

https://github.com/xqdoo00o/chatgpt-web
https://xqdoo00o.github.io/chatgpt-web

纯 JS 实现的 ChatGPT 项目,部署一个 HTML 文件即可使用,基于 OpenAI API。支持复制/更新/刷新会话,语音输入,朗读等功能,以及众多自定义选项。支持搜索会话,深色模式,自定义头像,快捷键,多语言,环境变量,PWA 应用,API 额度显示等。支持加密 HTML 文件。

1704683709497

https://github.com/Ajay-Dhangar/onelink
https://onelink-ajay.vercel.app

Onelink 是个人主页分享链接,数据被转换为 Base 64 字符串,我们 onelink 将其用作查询参数,与您的朋友分享您的社交链接实时预览,可以展示你的个人信息。

1704684685251

6. feeder.co - rss 订阅器及汉化脚本

https://feeder.co
https://greasyfork.org/zh-CN/scripts/481157-feeder-汉化脚本

feeder 是一款很优秀的 rss 订阅器,我已经使用很久了,这里推荐给大家。支持各种格式的 rss 订阅源,有浏览器插件,可以实时获取订阅数据,并且检测当前页面是否包含 rss 订阅源,效果显著,阅读流畅。

由于网站只支持英文,因此我写了汉化脚本,有需要的可以直接下载到油猴使用。

7. Netlify 汉化脚本

https://greasyfork.org/zh-CN/scripts/484197-netlify-汉化脚本

由于官方不支持中文,汉化 Netlify 大部分常用的翻译。

8. 2024 前端图标趋势

https://iconify.design/

Iconify 包含是一个 20 万个图标的图标库,它将几乎所有的开源图标库都收录并打包成了一个 npm 包,所以我们只用安装 @iconify/json 这个包,就相当于安装了 162 个开源图标库。

icon-[material-symbols–arrow-back-ios-new] 是 @iconify/tailwind 这个 Tailwind 插件引用图标的格式,该插件由 Iconify 作者开发,其中,中括号里的内容是 {图标库名}--{图标名}

npm i @iconify/json @iconify/tailwind -D

在 tailwind.config.js 中声明插件。

// tailwind.config.js
const { addDynamicIconSelectors } = require('@iconify/tailwind')
export default {
  plugins: [addDynamicIconSelectors()]
}

在 HTML 模板中使用图标

<i class="icon-[material-symbols--arrow-back-ios-new]"></i>

9. VS Code 插件开发中文文档

https://github.com/Liiked/VS-Code-Extension-Doc-ZH

该仓库拥有 1.9k Star,翻译审查较为严格,质量还是很不错的。

10. 十年程序员回老家五线城市考察,聊了一些行业和公司

https://www.v2ex.com/t/1005976

这是来自 v2ex 论坛的一个帖子,作者分享了他辞职后回老家考察的经历和对一些行业和公司的了解。他提到了几个面试的机会,包括直播带货、软硬件结合的公司、生态农业电商、民宿平台等,分享了他对这些公司的印象和交流内容。

奇趣周刊 - 第 34 期

2024-01-04 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1703942301990

祝大家元旦快乐!金沙湖歌剧院。

1. JavaScript 工程师的 Python 指南

https://luckrnx09.com/python-guide-for-javascript-engineers/zh-cn/

这是一份为 JavaScript 工程师准备的 Python 指南,包含四个部分。第一部分介绍 Python 的基础知识,包括开发环境、变量定义和赋值、数据类型等内容;第二部分介绍 Python 的进阶知识,如函数定义和调用、模块和包的建设和导入、文件操作等;第三部分介绍 Python 的高级知识,包括面向对象编程、异常处理、项目依赖管理等;最后一部分包含了一些综合案例。同时还包含附录,提供了一些常用的第三方包和参考资料。

1703733767222

2. Hacker News 的 2023 年度回顾

https://public.tableau.com/app/profile/isna/viz/HackerNewsDashboard_17042353296150/HackerNewsYearinReview2023

由 Tableau Public 发布的 Hacker News Dashboard,在这个仪表板中找到与 Hacker News 相关的各种统计和数据。

1704250856738

3. Coze AI 工具

https://www.coze.com/explore

字节出品的 AI 工具,在我一段时间的体验中,感觉非常棒,它是一个下一代 AI 聊天机器人构建平台。

用户可以在该平台上快速创建聊天机器人,无需编码,并将其发布到各种平台上。

Coze 是一个智能且独立的 AI 机器人开发助手,专注于帮助用户创建、调试和优化他们的 AI 机器人应用程序。

1704251065910

4. cobe / vercel

https://cobe.vercel.app/playground
https://github.com/shuding/cobe

一个轻量级 (5kB) WebGL Globe 库。 “COBE”这个名字代表宇宙背景探索者,旋转的地球,通过 canvas 实现。

1704284114391

5. 中国哲学书电子化计划

https://ctext.org/zhs

中国哲学书电子化计划是一个线上开放电子图书馆,为中外学者提供中国历代传世文献,力图超越印刷媒体限制,通过电子科技探索新方式与古代文献进行沟通。收藏的文本已超过三万部著作,并有五十亿字之多,故为历代中文文献资料库最大者。

1704263468387

6. TikTokDownloader 抖音下载工具

https://github.com/JoeanAmier/TikTokDownloader

TikTok 主页/视频/图集/原声;抖音主页/视频/图集/收藏/直播/原声/合集/评论/账号/搜索/热榜数据采集工具:完全开源,基于 Requests 模块实现的免费工具;批量下载抖音账号发布、喜欢、收藏作品;批量下载 TikTok 账号主页作品;下载抖音链接或 TikTok 链接作品;获取抖音直播推流地址;下载抖音直播视频;采集抖音作品评论数据;批量下载抖音合集作品;采集抖音账号详细数据;采集抖音用户 / 作品 / 直播搜索结果;采集抖音热榜数据。

1703814201447

7. 五彩纸屑动画

https://github.com/catdad/canvas-confetti/
https://www.kirilv.com/canvas-confetti/

🎉 浏览器中的高性能五彩纸屑动画。

8. t-ui

https://github.com/wocwin/t-ui

Vue 项目中基于 Element-ui 二次封装基础组件文档;Vue 基础组件文档。里面包含了更多复杂的组件应用,在特定场景下非常好用。

奇趣周刊 - 第 33 期

2023-12-28 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. Obsidian 1.5.3 版本发布

https://obsidian.md/changelog/2023-12-26-desktop-v1.5.3/

Obsidian 1.5.3 版本发布,推出全新的 Markdown 表格编辑器。现在可以更轻松地创建、编辑、排序、重新排序、选择、复制和粘贴表格行和列,表格仍保存为纯文本 Markdown。

2. GeminiProChat

https://github.com/babaohuang/GeminiProChat

官网 demo | 个人部署

基于 Gemini API 的聊天对话工具,可以通过这个项目在 vercel 上部署一个网站并使用 Gemini API。

ps: 但是很奇怪,为什么他说 1.4 万亿个参数对比 100 万亿个参数却是优势。

1703641197568

3. 用 Ollama 轻松玩转本地大模型

https://sspai.com/post/85193

这篇文章介绍了一个名为 Ollama 的本地大模型运行框架。Ollama 是一个简单易用的框架,可以让用户在自己的电脑上运行大型语言模型。

文章首先介绍了 Ollama 的安装和快速上手方法,包括下载模型、运行模型等。然后介绍了一些与 Ollama 相关的周边工具和插件,如网页版、终端版和桌面版等。接着介绍了一些进阶玩法,如更换模型、添加图片支持和自定义系统提示词等。最后,文章总结了 Ollama 的优势和贡献,认为它使得大模型的运行变得更加简单和普及化。

4. Github 个人年度总结

https://year-in-code.com/

将浏览器的 URL 地址更改为你的用户名,将生成用户本年度的总结视频,包括提交的 commit,issues,使用的编程语言等等,功能效果非常出色。

1703497632738

5. leerob.io - blog

https://leerob.io/
Github: https://github.com/leerob/leerob.io/

作者 leerob 是一位前端开发者,同时担任 Vercel 产品副总裁。这是他的博客,他还将自己的框架开源。该框架的主要架构是 react + nextjs + Postgres。

6. 笛卡 – 简中社区的 Hacer News

https://dizkaz.com/

功能和 Hacer News 基本保持一致,推荐中文社区的文章进行讨论。

1703255162870

7. 每个程序员都应该尝试的具有挑战性的项目

https://austinhenley.com/blog/challengingprojects.html

这篇文章是关于 Austin Z. Henley 的博客,题为“每个程序员都应尝试的具有挑战性的项目”。作者列举了一些软件项目,这些项目可以多次构建,并且每次都可以学到新的东西。这些项目包括文本编辑器、2D 游戏(太空侵略者)、编译器(Tiny BASIC)、迷你操作系统等。文章详细介绍了每个项目的挑战和需要学习的知识点,并提供了进一步阅读的推荐资源。此外,文章还列出了一些其他建议的项目,如数据库、光线追踪器、MS Paint 克隆、矢量图形编辑器等。

奇趣周刊 - 第 32 期

2023-12-21 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1703254928022

两棵银杏在寒风中独立,与旁边的事物迥然不同。


1. Cloudflare 2023 年度回顾

https://blog.cloudflare.com/radar-2023-year-in-review/

这篇文章是 Cloudflare 2023 年度回顾,涵盖了全球互联网趋势和模式的分析。文章主要分为三个部分:流量见解与趋势、连接与速度、安全。

在流量见解与趋势部分,文章提到全球互联网流量增长了 25%,谷歌是最受欢迎的互联网服务,安卓设备的流量占比超过了两三分之一。

在连接与速度部分,文章指出全球有超过 180 次的互联网中断,其中许多是由政府指示造成的;IPv6 的采用率仍然较低,印度是 IPv6 采用率最高的国家。

在安全部分,金融行业是最受攻击的行业,Log4j 漏洞和 HTTP/2 Rapid Reset 漏洞是最常见的攻击目标。文章还提到了其他一些有趣的数据和趋势。

1702530977455

2. Notion Web Clipper - Notion 剪藏插件

https://chromewebstore.google.com/detail/knheggckgoiihginacbkhaalnibhilkk

一个 chrome 浏览器扩展应用,利用 Notion Api 快速将文件剪藏到 Notion 数据库中,存档备份很方便。

3. Javascript quiz - JS 测验

http://perfectionkills.com/javascript-quiz/

这个网页是一个由 Perfection Kills 提供的 JavaScript 测试题。测试题包含了一系列关于 JavaScript 作用域、函数表达式、变量声明等知识的问题。每个问题都有一个正确答案,可以在主要的 JavaScript 实现中验证。题目包含了一些相对简单但常见的概念,对于专业的 JavaScript 开发人员来说是必须掌握的。虽然不是所有的问题都非常实用,但这个测试可以帮助开发人员加深对 JavaScript 的理解。你可以通过选择每个问题的答案来完成测试,并且可以在评论中留下你的得分。

4. AI 副业赚钱资讯合集

https://github.com/bleedline/aimoneyhunter

这是一个 ai 副业赚钱资讯信息的大合集,将在全网搜索并整理 ai 副业赚钱的相关方法、技术、工具、以及一些可以赚钱的平台和渠道。期望能在 AI 时代,打破信息茧房,让大家都能在这个时代利用 AI 智能化做副业,赚取工作之余的额外收益。

1702608625113

5. QQ 空间导出助手

https://chromewebstore.google.com/detail/aofadimegphfgllgjblddapiaojbglhf

一个 chrome 浏览器扩展应用,QQ 空间导出助手,用于导出备份 QQ 空间的说说、日志、日记、相册、视频、留言、好友、收藏、分享、访客为文件,便于迁移与保存。

1702752205221

6. Vue3 + tailwindcss + Element Plus 的后台管理面板

https://github.com/xiaoqing-tan/vue3dashboard

演示:https://tanxiaoqing.top

挺精致的一个后台管理系统,功能比较简单,实现了基本的 UI、权限、菜单管理、兼容移动端、中英文切换等。可快速地创建表单和列表一类的,也集成了图表,图标一类的,做一些后台管理的一些基础功能。

1703048882311

7. arco.design 设计系统

https://arco.design/

字节跳动出品的企业级设计系统,支持 react 和 vue 框架。

1702996625315

我的 2023 年度总结

2023-12-15 08:00:00

回顾过去的一年,虽然对自己的经历还不是很满意,但也算是划上了一个记号,结束了一段短暂的旅程,即将迎来新的开始。我期待着新的一年,希望能够继续保持这种积极向上的状态,迎接生活的挑战。

分享软件、工具和网站的周刊

我一直对技术和创新非常感兴趣。因此,我决定每周分享一些我发现的有用的软件、工具和网站。这个周刊不仅让我能够整理自己的收藏夹,还能够帮助他人发现一些实用的资源。通过这个周刊,我结识了许多志同道合的朋友,我们互相交流着自己的发现和经验。这个过程不仅让我更加了解了技术领域的最新动态,还提升了我的写作和分享能力。

我获取资源的途径有很多,比如通过论坛分享、在 GitHub, Hacker News 网站,以及获取国内外其他论坛上的优秀资源。这些渠道让我能够不断发现有用的软件、工具和网站,并与志同道合的朋友交流分享。通过这个过程,我不仅能够了解技术领域的最新动态,还能够提升自己的写作和分享能力。

阅读中内外的文学著作

阅读是我生活中非常重要的一部分。今年,我特别关注了中内外的文学著作。除了享受阅读的乐趣外,我还通过这些作品拓宽了自己的视野。我读了许多经济和哲学方面的书籍,其中我特别喜欢王阳明的心学和一些与清朝经济相关的作品。通过阅读这些书籍,我不仅了解到了不同的思想和观点,还对历史和社会有了更深入的认识。

工作上的学习和成长

在工作方面,今年我获得了丰富的学习经验,不断增长自己的专业知识。特别是 openai 的 gpt 项目,其令人惊叹的功能使我能够有效地应用于工作中,从而提高了工作效率。这些宝贵的知识使我在编写代码时更加熟练娴熟。我不禁对这个科技时代的发展速度感到惊奇,期待未来能够见证更多的科技进步!

发展新的兴趣——骑行

下半年,我发展了一个新的爱好——骑行。每周,我都会找一个适合骑行的地方,出去运动并欣赏周围的风景。骑行让我与大自然更加亲近,让我感受到了身体和心灵的双重放松。

与此同时,骑行也是一种很好的锻炼方式,我通过这个运动来保持身体健康。骑行的过程中,我还结识了一些志同道合的朋友,我们一起分享骑行的乐趣,互相鼓励和支持。

给下半年先定一个小目标,骑行 3000km。

奇趣周刊 - 第 31 期

2023-12-14 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 谷歌发布 AI 大模型 Gemini

https://deepmind.google/technologies/gemini/

1702209102874

谷歌发布 AI 大模型 Gemini,称技术领先于 OpenAI,暴打 gpt4.

2. GoMusic - 歌单迁移

https://github.com/Bistutu/GoMusic

一款迁移网易云/QQ 音乐歌单至 Apple/Youtube/Spotify Music 的 web 程序,已开源。

1702025051931

3. The diminishing half-life of knowledge - 知识的半衰期递减

https://rednafi.com/zephyr/diminishing_half_life_of_knowledge/

这篇文章讨论了知识的半衰期,作者描述了在不同公司工作时使用不同技术栈的经历,以及随着时间的推移,之前掌握的技能逐渐变得过时和无用的困境。

作者认为工程知识的半衰期正在加快,唯一的解决方法是不断学习和管理信息的能力,并且分享了自己保持更新的方法,包括参与开源社区、记录学习进度、采用无趣的技术等。

4. 基于 vue 重构 b 站的 chrome 插件

https://chromewebstore.google.com/detail/bewlybewly/bbbiejemhfihiooipfcjmjmbfdmobobp
https://github.com/hakadao/BewlyBewly

通过重新设计哔哩哔哩主页、添加更多功能并对其进行个性化设置以符合您的偏好来改进您的哔哩哔哩主页。安装了体验发现还蛮好看精致的。

1702251393020

5. Wormhole - 匿名分享网盘

https://wormhole.app/

Wormhole 让你能以端到端加密和自动过期链接的方式分享文件。所以你可以确保你分享的文件私密且不会一直留在互联网上。可以随时擦除痕迹,临时分享很好用的。

1702259107808

6. Google 推出全新人工智能笔记应用 NotebookLM

https://notebooklm.google.com/

Google 推出全新人工智能笔记应用 NotebookLM,仅支持美国 ip,将代理换成美国节点即可。

可通过向其提供 Google Docs 文档,ai 快速理解并生成相关摘要以及关键内容,然后你可以向他进行提问。

1702260637067

7. CookiesClerk - Chrome 浏览器插件

https://github.com/14790897/CookiesClerk

CookiesClerk 是一款谷歌 Chrome 浏览器插件,可简化同一网站多个 cookie 的管理。通过有效管理 cookie,它允许用户在同一浏览器中同时打开多个账户。

追逐秋天的尾巴

2023-12-10 08:00:00

追着秋天的尾巴,沿着黄龙洞,越过初阳台,到达穗庐。

1702213346400

1702213348891

1702213572025

奇趣周刊 - 第 30 期

2023-12-07 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

今日二十四节气大雪,但是杭州的天气很无常,并没有一片雪花,甚至还有点热,看来近期应该不会有雪景了。
本期只有一句诗来应景,“燕山雪花大如席,片片吹落轩辕台。” —— 李白《北风行》

1.New Bing Anywhere (Bing Chat GPT-4)

https://chromewebstore.google.com/detail/new-bing-anywhere-bing-ch/hceobhjokpdbogjkplmfjeomkeckkngi

通过本插件,您可以在任何 Chromium 内核的浏览器中使用 New Bing,比如 Chrome / Edge / Brave / Arc / 360 浏览器等,它自带 Chat GPT-4,需要注意的是你需要一个国外 ip。

1701322678154

1701322700046

1701322723527

2.项目管理器扩展 – Project Manager

https://github.com/alefragnani/vscode-project-manager

Visual Studio Code 的项目管理器扩展,可以把常用的文件夹整理起来,然后点击直接跳转到对应的文件夹。

1701402458597

3.AI 扩图 – uncrop

https://clipdrop.co/uncrop

stability.ai 出品的一款免费扩图工具,基础效果还不错,拿来玩玩很适合。

1701409764748

4.微信解密工具 – WeChatMsg

https://github.com/LC044/WeChatMsg

提取微信聊天记录,将其导出成 HTML、Word、CSV 文档永久保存,对聊天记录进行分析生成年度聊天报告,使用模拟器可支持可视化分析。

5.一个免费的开源社区讨论平台 – Discuit

https://github.com/discuitnet/discuit

Discuit 是一个开源社区平台,是 Reddit 的替代品,使用 Go 语言开发响应速度非常快,作为社区交流来说是非常棒的。

1701417383093

1701417395624

6. 开源的本地音乐播放器 – MusicPlayer2

https://github.com/zhongyang219/MusicPlayer2

MusicPlayer2 是一款集音乐播放、歌词显示、格式转换等众多功能于一身的音频播放软件。支持歌词显示、歌词卡拉 OK 样式显示、歌词在线下载、歌词编辑、歌曲标签识别、专辑封面显示、专辑封面在线下载、频谱分析、音效设置、任务栏缩略图按钮、主题颜色、格式转换等功能。播放内核为 BASS 音频库 (V2.4)。播放器支持几乎所有常见的音频格式。

1701584013787

7. 文章/新闻

  1. https://www.nytimes.com/2023/11/29/us/henry-kissinger-dead.html
    前美国国务卿亨利·基辛格去世,享年 100 岁。

  2. https://juejin.cn/post/7243725406132748349
    最新前端技术趋势。

  3. https://mp.weixin.qq.com/s/rw9HrimkSRGbGpWQL9394Q
    2024 年了,虚拟 DOM 该何去何从?

奇趣周刊 - 第 29 期

2023-11-30 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1700924521161

1. 量子纠缠动画 + 跨窗口交互

跨页面实现交互,非常神奇的技术。

作者只是发了一个演示的推文,代码没有开源。
另一位开发者做了简单版本,跨窗口 3D 场景,除了量子纠缠特效外,已经还原如何跨窗口交互,通过实时获取 localStorage,定位坐标,然后结合 Three.js 库。非常有意思的一个项目,可以学习一下前端思路和代码。

https://github.com/bgstaal/multipleWindow3dScene

2.视频去水印

https://github.com/YaoFANGUK/video-subtitle-remover

基于 AI 的图片/视频硬字幕去除、水印去除,无损分辨率生成去字幕、去水印后的图片/视频文件。无需申请第三方 API,本地实现。

3.screenshot-to-code

https://github.com/abi/screenshot-to-code

将截图转换成 html 和 css 代码,css 使用的是 Tailwind 编写,值得尝试的工具。
需要带上你自己的 OpenAI 密钥,并且必须能够访问 GPT-4 Vision,门槛还是稍微有点高的,有能力的小伙伴强烈建议尝试一下!

4.中文播客榜

https://xyzrank.com/

https://www.xiaoyuzhoufm.com/episode/62d58b0664f141ad8150151f 出品,收录了很多中文播客,通过更新时间,播放量,订阅量等数据排序,可以挑选自己感兴趣的播客。

1701056187862

5.ajax-interceptor

https://github.com/YGYOOO/ajax-interceptor

一个可以修改页面中任意 Ajax 的请求或返回值的 Chrome 插件,可用于调试/排查页面上的问题,或在开发时 mock 数据。(当然你也可以用其它一些工具如 Charles 修改网络请求的返回值,但操作繁琐,该插件方便很多,且不会对 Chrome 之外造成影响)

1701135584935

6.ACG 助手

https://acghelper.com/

第三方哔哩哔哩弹幕网辅助扩展,拥有视频区和直播区的人性化功能,对于喜欢刷 b 站视频的人来说是一个不可多得好工具。

1701135745740

7.艺术照片生成

https://art.aiphotoshoot.com/

人工智能生成的艺术,个人感觉效果还是很不错的。

1701301347920

8.Sam Altman 重新回归 OpenAI 担任 CEO

https://openai.com/blog/sam-altman-returns-as-ceo-openai-has-a-new-initial-board

又迎来了新的转变,Sam Altman 重新回归 OpenAI 担任首席执行官,解散原有的董事会,新的初始董事会将由 Bret Taylor(主席)、Larry Summers 和 Adam D’Angelo 组成。

奇趣周刊 - 第 28 期

2023-11-23 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

好久没见到这种挂着红灯笼的小镇了,平静中带点繁华,叫卖声、饮食文化都很普通,又透露着幸福。—歙县


1.OpenAi 新闻

OpenAi 联合创始人、首席执行官、董事会成员 Sam Altman 被逐出公司,辞去所有职务。OpenA 宣布首席技术官 Mira Murati 将担任临时首席执行官,立即生效。另一位联合创始人 Greg Brockman 也将辞去董事会主席职务,继续担任公司总裁职务。

2.ImageGlass (图像查看器)

https://imageglass.org/

基于 windows 系统的图像查看器,功能强大轻量,个人感觉比系统默认的好用。

3.Shmily (qq 备份器)

http://lqzhgood.github.io/Shmily

这是一个 ( QQ Wechat 短信 通话记录 照片 等) 记录归档的项目,支持将以前导出的数据和现在合并。如 2013 年导出记录(2000-2013)可以和 2020 年导出记录(2007-2020)合并,在同一时间线查看,支持图表与统计等功能,非常不错的项目

4.Inpaint-web

https://github.com/lxfater/inpaint-web
https://inpaintweb.lxfater.com/

基于 Webgpu 技术和 wasm 技术的免费开源 inpainting 工具,纯浏览器端实现,可以实现抠图等功能。

5.vscode 扩展模板

https://github.com/antfu/starter-vscode

一个开发 vscode 扩展的框架模板。

文章阅读

  1. https://www.bloomberg.com/news/articles/2023-11-21/altman-openai-board-open-talks-to-negotiate-his-possible-return
    OpenAI 董事会萨姆·奥尔特曼 (Sam Altman) 公开讨论他可能的回归。
    谈判的重点是奥特曼恢复首席执行官职务,首席执行官希尔要求董事会提供奥特曼不当行为的证据,大多数 OpenAI 员工因奥特曼退出而威胁辞职。

  2. https://wccftech.com/former-openai-employees-allege-deceit-and-manipulation-by-sam-altman-elon-musk-calls-for-investigation/
    OpenAI 前员工指控山姆·奥尔特曼“欺骗和操纵”:埃隆·马斯克呼吁调查。
    这封信最后呼吁 OpenAI 董事会保持“坚定”立场,反对 Sam Altman 和 Greg Brockman。请记住,OpenAI 的一些最著名的投资者正在试图说服 Altman 放弃微软并重新领导这家初创公司。
    其他人正在考虑对董事会提起诉讼,指控奥特曼被解雇的任意方式。微软的 Satya Nadella 也表示有条件地同意 Sam Altman 重返 OpenAI 的职位。

奇趣周刊 - 第 27 期

2023-11-16 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1.smartUp Gestures 手势 - 浏览器扩展

Github: https://github.com/zimocode/smartup

Chrome 扩展商店 | Firefox 扩展商店 | Edge 扩展商店

通过使用鼠标提高生产力和效率且可自定义的 Web 浏览器扩展。

功能包括:鼠标手势、简单拖动、超级拖动、摇杆手势、滚轮手势、弹出操作、图标操作、上下文菜单操作、触摸手势、双击操作和键盘快捷键操作。

2.AI 搜索引擎

https://devv.ai/

最懂程序员的新一代 AI 搜索引擎,支持对话。

3.PicHub - Github 图床工具

https://github.com/lewkamtao/PicHub

一个基于 Vue3 + TypeScript 实现的 Github 图床,页面挺美观的,该有的功能都有。

4.Pictode

https://github.com/JessYan0913/pictode/
https://pictode.com/

又一款类似 excalidraw 的产品,响应速度很快。

基于 Vue 3、TypeScript、Konva、HeadlessUI 和 Tailwind CSS 等酷炫技术构建的画板项目,它让你可以随心所欲地绘制、创作和表达自己的创意。✨

5.Lobe Chat

https://github.com/lobehub/lobe-chat

LobeChat 是一个开源的、可扩展的高性能聊天机器人框架。它支持一键免费部署私人 ChatGPT/LLM 网页应用程序。

6.Airy 工具

https://colink.in/

一款桌面程序,AI 翻译,AI 备忘录,AI 搜索等功能,选中后使用快捷键可以直接调用程序翻译或者和 chatGPT 交流,不仅限于浏览器场景。

阅读

  1. https://linuximpact.com/play-the-first-ever-text-adventure-game-in-your-linux-terminal/
    在 Linux 终端上玩第一个文字冒险游戏

  2. https://twitter.com/steventey/status/1723116716715393446
    现在你只需要 AI 就可以将静态照片转换为视频,超乎我的想象。

  3. https://github.com/samuel-lucas6/Cryptography-Guidelines
    作为开发人员实施密码学的指南。

  4. https://twitter.com/i/status/1724280038869094716
    openAI 已经改变了我们的生活,更有效率,更有创造力,真的感叹。

hugo 的一些模板语法

2023-11-15 08:00:00

分享一下 hugo 的语法模板,如何获取总文章数和总字数,效果可以参考我的首页。

{{ $articleCount := len .Site.RegularPages }}
{{ $totalWordCount := 0 }}
{{ range .Site.Pages }}
{{ $totalWordCount = add $totalWordCount .WordCount }}
{{ end }}
<p>已经写了 {{ $articleCount }} 篇文章,共 {{ $totalWordCount }} 字。</p>

在首页过滤某个分类。

{{ $pages := .Site.RegularPages }}
{{ $paginator := .Paginate ($pages) }}
{{ $count := 0 }}

{{ range $paginator.Pages }}
{{ $title := .Title }}
{{ $summary := .Summary }}

{{ if and (not (in .Params.categories "weekly")) (lt $count 7) }}
{{ $count = add $count 1 }}

<article class="post-entry">
  <header class="entry-header">
    <h2 class="entry-hint-parent">{{ .Title }}</h2>
  </header>
  <div class="entry-content">
    <p>{{ .Summary }}</p>
  </div>
  <footer class="entry-footer">
    <time datetime="{{ .Date.Format " 2006-01-02 15:04:05 MST" }}">{{ .Date.Format "2006-01-02"}}</time>
    <span class="category">
      {{ range $key, $value := .Params.categories -}}
      {{- if $key }}, {{ end }}
      {{ . }}
      {{- end }}
    </span>
  </footer>
  <a class="entry-link" href="{{ .RelPermalink }}"></a>
</article>
{{ end }}

{{ end }}

python 通过 json 生成小文件

2023-11-10 08:00:00

记录一个方法,pyhton 通过 json 文件,在同级目录下生成对应格式的小文本。

import json

def generate_files_from_json(json_file):
    with open(json_file, 'r', encoding='utf-8') as f:
        data = json.load(f)

    for item in data:
        for key, value in item.items():
            if isinstance(value, list):
                for sub_item in value:
                    for sub_key, sub_value in sub_item.items():
                        if isinstance(sub_value, dict) and 'list' in sub_value:
                            for sub_sub_item in sub_value['list']:
                                if 'id' in sub_sub_item and 'css' in sub_sub_item:
                                    id_value = sub_sub_item['id']
                                    css_value = sub_sub_item['css']
                                    create_file(id_value, css_value)

def create_file(file_name, content):
    file_path = f"{file_name}.css"
    with open(file_path, 'w', encoding='utf-8') as file:
        file.write(content)

    print(f"File '{file_path}' created successfully.")

if __name__ == "__main__":
    json_file_path = 'your_json_file.json'
    generate_files_from_json(json_file_path)
import json

def generate_files_from_json(json_file):
    with open(json_file, 'r', encoding='utf-8') as f:
        data = json.load(f)

    for item in data:
        for key, value in item.items():
            if isinstance(value, dict) and 'list' in value:
                for sub_item in value['list']:
                    if 'id' in sub_item and 'css' in sub_item:
                        id_value = sub_item['id']
                        css_value = sub_item['css']
                        create_file(id_value, css_value)

def create_file(file_name, content):
    file_path = f"{file_name}.css"
    with open(file_path, 'w', encoding='utf-8') as file:
        file.write(content)

    print(f"File '{file_path}' created successfully.")

if __name__ == "__main__":
    json_file_path = 'your_json_file.json'
    generate_files_from_json(json_file_path)

奇趣周刊 - 第 26 期

2023-11-09 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

江畔渔火对愁眠。


1.Clash 系列删库事件

2023 年 11 月 2 日,Fndroid/clash_for_windows_pkg 作者由于某些原因,删除仓库中的 releases 包。

3 日,clash 核心也随之删库,紧接着,一系列基于 clash 核心的软件都宣告删库或者 archived。

2.令人困惑的 git 术语

https://jvns.ca/blog/2023/11/01/confusing-git-terminology/

作者举例了很多有关 git 指令奇怪的术语。

3.GitHub 如何恢复旧的 feed 流推送?

https://github.com/wangrongding/github-old-feed

一个油猴脚本,将 GitHub 首页的推送信息进行优化,显示效果也是非常不错的。

4.知乎评论时间精确到秒 - 油猴脚本

https://meta.appinn.net/t/topic/47711/18

来自一位网友的分享,我修改了一些内容和知乎网页风格保持一致。将显示 几小时前 的时间显示改为正常 年-月-日 时-分-秒

油猴脚本代码
// ==UserScript==
// @name         知乎评论时间精确到秒
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  try to take over the world!
// @author       You
// @match        https://www.zhihu.com/*
// @match        https://zhuanlan.zhihu.com/*
// @icon         http://zhihu.com/favicon.ico
// @grant       GM_addStyle
// @run-at document-start
// @require https://scriptcat.org/lib/637/1.3.3/ajaxHooker.js

// ==/UserScript==


(function () {
	'use strict';
	function timestampToTime(timestamp) {
		const milliseconds = timestamp * 1000;
		const date = new Date(milliseconds);
		const year = date.getFullYear();
		const month = addZero(date.getMonth() + 1);
		const day = addZero(date.getDate());
		const hour = addZero(date.getHours());
		const minute = addZero(date.getMinutes());
		const second = addZero(date.getSeconds());

		return `${ year }-${ month }-${ day } ${ hour }:${ minute }:${ second }`;
	}

	function addZero(num) {
		return num < 10 ? `0${ num }` : `${ num }`;
	}

	ajaxHooker.hook(request => {
		if (request.url.includes("https://www.zhihu.com/api/v4/comment_v5/comment/") || request.url.includes("https://www.zhihu.com/api/v4/comment_v5/answers/") || request.url.includes("https://www.zhihu.com/api/v4/comment_v5/articles/")) {

			request.response = res => {
				// console.log('\n== ↓ ↓ ↓ ↓ ↓ == \n', res)

				if (res.json.data) {

					res.json.data.forEach(item => {
						// console.log(timestampToTime(item.created_time));
						item.content =  item.content +   '<span class="css-nm6sok commentTime">' + timestampToTime(item.created_time) + '</span>'
						if (item.child_comments.length >= 1) {
							item.child_comments.forEach(child => {
								child.content =   child.content  + '<span class="css-nm6sok commentTime">' + timestampToTime(child.created_time) + '</<span>'
							});
						}
					});
					GM_addStyle(`
					.CommentContent {
							position: relative;
							overflow: visible;
						}

						/* 	精确时间 */
						.commentTime {
							position: absolute;
							left: 0;
							bottom: -22px;
							color: #999;
						}

						.css-140jo2 {
							position: relative;
						}

						/* 原时间	 */
						.css-12cl38p,
						.css-12cl38p + span,
						.css-nm6sok + span {
							display: none;
						}

						/* ip 属地	 */
						.css-8hxn0r .css-nm6sok {
							position: absolute;
							left: 160px;
							top: 3px;
						}
						/* 热评	 */
						.css-8hxn0r .css-33kuns {
							position: absolute;
							left: 240px;
							top: 3px;
						}
						/*  作者置顶	 */
						.css-1o87v1m{
						    position: absolute;
  						    top: 22px;
   						    left: -4px;
						}
					`);
				}

			};
		}

	});
})();

5.rubick - uTools 的开源替代品

https://github.com/rubickCenter/rubick

虽然我已经购买了 uTools 的永久会员,但还是觉得 rubick 非常棒,试用了一下很符合前端的使用习惯,可以自己本地开发插件。

  • 基于 electron 的开源工具箱,自由集成丰富插件。
  • 基于 npm 包模式的插件管理,安装插件和安装 npm 包一样简单
  • 支持 webdav 多端数据同步,真正的数据安全同步
  • 独一无二的系统插件模式,让插件成为 rubick 的一部分
  • 支持快速启动本地 app、文件、文件夹
  • 支持企业化内网部署
  • 支持多语言

6.OpenAI 发布会

11 月 6 日,OpenAI 官方发布会上公布最新的模型 GPT-4 Turbo 发布,功能非常强大,并且开启了 GPT 4 的内测,小部分 GPT 3.5 用户可以体验到 GPT 4 的快感,预计很快就可以开放到所有 GPT 3.5 用户。

chrome119 版本看视频时会闪动

2023-11-08 08:00:00

在 chrome119 版本下观看视频时,比如 bilibili 或者 douyin,会不时的出现闪动,黑屏,自动刷新等情况,反正我是经常遇到。

在网络上上求助时,看有人说到可能是 NVIDIA 显卡和 Chromium 内核冲突的问题。

进入设置 chrome://flags/#use-angle ,将 Choose ANGLE graphics backend 设置为 D3D11on12 就可以避免出现这个情况。

其实不止是 chrome,包括 edge 浏览器都会出现这个问题(实测),因为他们都是使用 Chromium 内核的浏览器。

也不知道 NVIDIA 和 Chromium 谁该背这个锅,希望早日修复此 bug。

奇趣周刊 - 第 25 期

2023-11-02 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

image

西班牙骑士的“劲敌”卡斯蒂利亚 - 拉曼恰的风车


1.Screenzy - 截图美化

https://screenzy.io/

写博客或者分享截图的时候,可以用来美化图片

1698304102451

2.AutoCorrect - vscode 插件中英混排优化

https://marketplace.visualstudio.com/items?itemName=huacnlee.autocorrect

https://github.com/huacnlee/autocorrect

可以自动优化中英文混排的格式,对于写文章的小伙伴来说很实用,自动化规范写作格式。安装了这个 vscode 插件后,我将所有文章的格式都重新优化了一下,看着顺眼了很多。

AutoCorrect 用于「自动纠正」或「检查并建议」文案,给 CJK(中文、日语、韩语)与英文混写的场景,补充正确的空格,同时尝试以安全的方式自动纠正标点符号等等。

类似 ESlint、Rubocop、Gofmt 等工具,AutoCorrect 结合 VS Code,它提供 Lint 功能能便捷的检测出项目中有问题的文案,起到统一规范的作用。

支持各种类型文件支持,能自动识别文件名,并准确找到字符串、注释做自动纠正。

1698330018753

3.Internet Artifacts - 互联网文物

https://neal.fun/internet-artifacts/

收藏了从因特网早期一直到现在非要有标志性的产物或者事件,很有趣的一个网站,摸鱼的时候可以扩展下知识。

比如世界上第一封垃圾邮件,由 Digital Equipment Corporation 的营销经理 Gary Thuerk 发送的。Thuerk 将电子邮件发送给 ARPANET 上的 320 个收件人,宣传新型 DECSYSTEM-20 大型计算机的产品演示。

1698330719549 1698330744643

4.WxReader - 微信读书自动阅读器

https://github.com/DoooReyn/WxReader

基于 PySide6 的微信读书自动阅读器。

5.wxt - Web 扩展框架

https://github.com/wxt-dev/
https://wxt.dev/

一款开源工具,使 Chrome 扩展程序的开发速度比以往任何时候都快。

⚡ 下一代 Web 扩展框架,它类似于 Nuxt,但适用于 Chrome 扩展,支持所有浏览器,热更新,支持各种框架启动。

1698805357082

文章阅读

  1. https://blog.plover.com/prog/katara-advice.html
    Advice to a novice programmer 给新手程序员的建议。

  2. https://twitter.com/javilopen/status/1719363262179938401
    用 AI 复制《愤怒的小鸟》。
    1698805844066

奇趣周刊 - 第 24 期

2023-10-26 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1697976752212

来自一位叫做 @洋洋 的网友拍摄的黄山,如诗如画,彷佛中梦中走来的景象。


1.Notepads 文本编辑器

https://github.com/0x7c13/Notepads

具有简约设计的现代轻量级文本编辑器,对标 windows txt 文档。

1698072221188 1698072221197

2.Jamstack Themes (Jamstack 主题)

https://jamstackthemes.dev/

收录各种博客程序主题,包括不限于 hexo, hugo, jekyll, astro, gatsby 等等,资源相当丰富。

1698072221202

3.Lorem Picsum 免费的图像 api

https://picsum.photos/

可免费调用的图像 api,只需在我们的 URL 后添加您想要的图像尺寸(宽度和高度),您就会获得随机图像。

  1. 要获得正方形图像,只需添加尺寸即可。
  2. 通过在 URL 开头添加 /id/{image} 来获取特定图像。
  3. 通过将 /seed/{seed} 添加到 URL 的开头,每次都根据种子获取相同的随机图像。
  4. 通过将 ?grayscale 附加到 url 末尾来获取灰度图像。
  5. 通过将 ?blur 附加到 URL 末尾来获取模糊图像。

如下图,每次刷新都会获得一张新的图像。

Lorem Picsum

4.Clash-for-Windows_Chinese

https://github.com/Z-Siqi/Clash-for-Windows_Chinese

提供 clash for windows 的汉化版,汉化补丁以及汉化版 Clash 安装程序。

1698072221194

推荐文章

  1. https://www.dailymail.co.uk/news/article-12646839/Netflix-subscribers-password-sharing-crackdown-revenue.html
    Netflix 打击密码共享行为,新增 900 万订阅用户。

  2. https://www.ex-astris-scientia.org/database/chairs-trek.htm
    《星际迷航》中市售的椅子。

科技新闻

  1. 2023/10/23,语雀官网从下午起全面崩溃,截止到晚间 10 点 左右维修完毕,持续大半天,所有用户查看不了文档和笔记,这显然对一个在线文档网站来说是致命打击,并且这也不是第一次出现这种情况。相信经此之后,会有很多用户出走,迁移出自己的数据,这实在是太不安全了!曾经我也算是语雀资深用户,它本身是一个很不错的产品,奈何运营层面让人很失望。

奇趣周刊 - 第 23 期

2023-10-19 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1697283160572

在骑行过程中发现的一处风景,非常可爱的城堡,外表为粉色,很吸引女孩子前往游玩,位于杭州滨江浦沿。


1.V2EX Polish

https://www.v2p.app/

专为 V2EX 用户设计的浏览器插件,集合了众多实用功能,让原生页面焕然一新!

1697285124941

2.MarkDownload - Markdown Web Clipper

https://chromewebstore.google.com/detail/markdownload-markdown-web/pcmpcfapbekmbjjkdalcgopdkipoggdi

谷歌浏览器扩展,可以将当前网页输出为 markdown 格式,收藏文章到本地很有用。

1697349821802

3.一本记账

https://www.yimuapp.com/

专注效率的智能记账软件,支持安卓端和 ios 端,页面很精美功能强大,不连接云端,数据默认保存在本地,可以通过 WebDAV 同步到坚果云。

1697284712694

4.Qexo

https://github.com/Qexo/Qexo

一个快速、强大、漂亮的在线 Hexo 编辑器,支持多种 Hexo、Hugo、Valaxy 托管商 Github、Gitlab、本地。支持将文章、页面、配置索引一键缓存至数据库,保证您的高速访问,Webhook 全自动清除缓存,时刻保持数据最新。

1697461248559

5.Omnivore

https://omnivore.app/

Omnivore 是一款免费、开源、适合认真阅读的稍后阅读应用程序。保存文章、新闻通讯和文档并稍后阅读——专注且无干扰。添加注释和亮点。按照您想要的方式整理您的阅读列表并在所有设备上同步。

1697461480673

文章

  1. https://stackoverflow.blog/2023/10/16/stack-overflow-company-announcement-october-2023/
    Stack Overflow 宣布裁员 28%

  2. https://urbigenous.net/library/how_to_build.html
    如何建立一个两天后不会分崩离析的宇宙,菲利普·K·迪克,1978。

  3. https://tech.meituan.com/2023/10/12/exploration-of-reliability-governance-based-on-pattern-mining.html
    《基于模式挖掘的可靠性治理探索》作者美团技术团队。对于亿级流量的线上系统来说,可靠性是至关重要的。从字面上理解,可靠性要求故障少、可信赖。与安全性一样,它们都是信息系统的固有属性之一,也是保障产品质量的关键因素。

奇趣周刊 - 第 22 期

2023-10-12 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

来自推特帖子,可爱的小猫在机箱中玩耍。


1.ChatPdf / Sider 谷歌插件

https://sider.ai/chatpdf/

带有 Vision 的 ChatGPT 侧边栏,让 AI 帮你读 PDF!将 PDF 变为你的聊天机器人!

在任意页面都可以使用 Sider 侧边栏,以获得即使的 AI 帮助。提出问题、接收答案并顺利过渡回当前任务。最大限度地减少干扰并提高生产力。拥抱不间断工作流程的自由。

1697106487742

2.直播源

https://github.com/fanmingming/live
https://live.fanmingming.com/

✯ 一个国内可直连的直播源分享项目 ✯
🔕 永久免费 直连访问 完整开源 不含广告 完善的台标 直播源支持 IPv4/IPv6 双栈访问 🔕

1697124432886

3.推特视频下载器

https://savetwitter.net/zh-cn

轻松将 Twitter 视频免费下载到 mp3、mp4。

1697252342214

文章

  1. https://baoyu.io/blog/prompt-engineering/three-ai-agents-and-four-steps-flow-prompt
    怎么让 ChatGPT 的翻译结果更准确?

  2. https://www.gearpatrol.com/tech/audio/a45461959/tascam-portastudio-414-mkii/
    这款四轨录音机让我再次爱上音乐

  3. https://www.cnbc.com/2023/10/11/irs-says-microsoft-owes-an-additional-29-billion-in-back-taxes.html
    IRS 称微软还欠 290 亿美元税款,微软表示,美国国税局通知该公司,该公司还欠 289 亿美元的税款。微软将审理美国国税局的行政上诉,这可能需要数年时间。

游戏

  1. csgo 社区服 https://csgod.top/
    又一个 Counter Strike 玩家聚集地。作为 8 年老玩家,很遗憾,csgo 即将远去,cs2 很好,但不适合我,据传明年 1 月份 V 社将会彻底删除 csgo 的测试包,基于此的社区服也会随之烟消云散,感概万千,因此在周刊推荐了这个社区服,喜欢 csgo 的小伙伴可以关注下。

五丰岛骑行

2023-10-03 08:00:00

不出意外的,今天天气很好,约了个小伙伴一起出来骑行。目的地五丰岛,沿着滨江绿道一路至吴家渡口。

不过岛比较小,找了家面馆吃了午饭后,沿着小岛绕了一圈。时间还早,继续行至湘湖,平时我只在晚上去过,基本没有白天来湘湖,没想到这么热闹,很多人摊了个野餐布在草地休息,还好很多拍婚纱照的,比较好玩就多呆了会。

今日刷里程 60 公里。

1696331276384

1696331276364

1696331276378

1696331276356

奇趣周刊 - 第 21 期

2023-10-03 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

国庆在湘湖骑行,看着来往休息的人群,非常多拍婚纱照的人。

“判断什么事情该做,有两个标准。
第一,做的时候喜欢、快乐,做了以后一定不会后悔的事情。第二,做的时候未必喜欢、快乐,但不做一定会后悔的事情。有这两个标准基本就够了,而且它们很好掌握。”

“There are two criteria for judging what should be done. First, if you like and be happy when you do it, you will definitely not regret it if you do it. Second, you may not like and be happy when you do it, but you will definitely regret it if you don’t do it. Things. These two standards are basically enough, and they are easy to master.”


1.gkd 手机跳过广告

https://github.com/gkd-kit/gkd

基于 无障碍 + 高级选择器 + 订阅规则 的自定义屏幕点击 Android APP。

2.tgState 图床

https://github.com/csznet/tgState

使用 Telegram 作为存储的图床,最大支持 20MB

3.ChatGPT 小助手

https://greasyfork.org/zh-CN/scripts/462447-chatgpt小助手

ChatGPT 小助手可以为你带来更好的使用体验:添加快捷指令(prompts),暂时只适配了官方站点。

4.推荐文章

  1. 怎么让 ChatGPT 的翻译结果更准确?
    https://baoyu.io/blog/prompt-engineering/three-ai-agents-and-four-steps-flow-prompt

翁金线骑行

2023-10-01 08:00:00

国庆与中秋连在一起放假了,可以出去散散心,从杭州出发一路骑行翁金线,至观潮胜地公园,位于嘉兴海宁,全程将近 70 公里。

整体的感受就是非常爽,前半程因为钱塘江大潮的缘故,游客非常之多,路上来来回回都是人,安全起见不敢骑太快,后半程才放开身心,享受骑车的乐趣和沿途的风景。

回家的路上碰到两个骑友和我的路线一致,便结伴而行,一边聊天一边刷里程,也是非常畅快。

路过堤坝,见到了潮水过去渔民捕鱼的景象。

翁金线是个好地方,期待下一次和朋友继续去骑行游玩!

奇趣周刊 - 第 20 期

2023-09-22 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

在钱塘江北岸遥望。


1.Whois 查询

https://who.cx/

一个美观的 Whois 查询网站,加载速度很快,可以识别备案域名。

2.excalidraw 画板

https://excalidraw.com/

在线画板工具,支持协同、分享等。

1695729137648

3.图形生成器

https://itakeo.github.io/tool/

快速生成三角形、箭头、阴影、渐变的 CSS 代码。对我来说是不可多得的好工具,日常使用会用到 css 三角形,每次手动画总是很麻烦,有了这个工具,以后可以省很多力气了。

1695783554430

chrome117 版本隐藏所有标签按钮

2023-09-20 08:00:00

chrome117 版本更新后标签栏多了一个【所有标签】按钮,相当别扭,通过设置可以隐藏。

1695180000022


浏览器输入 url: chrome://flags/#power-bookmarks-side-panel


1695180063755

改成 Disabled 重启浏览器即可。

夜游钱塘

2023-09-11 08:00:00

和小伙伴绕着江边骑行,欣赏了“大莲花”亚运会主场馆,杭州之门地标。

1694436654865

1694436654896

1694436654882

远程工具 RustDesk 安装流程

2023-09-02 08:00:00

RustDesk 是一款功能齐全的远程桌面应用,支持 Windows、macOS、Linux、iOS、Android、Web 等多个平台。支持 VP8 / VP9 / AV1 软件编解码器和 H264 / H265 硬件编解码器。完全掌控数据,轻松自建。P2P 连接,端到端加密。在 Windows 上可以非管理员不安装运行,根据需要在本地或远程提升权限。

下面是在 linux 服务器上部署中继服务器的脚本。

docker 拉取镜像

docker image pull rustdesk/rustdesk-server

运行 hbbs

# 服务器IP >> 替换为你当前终端的ip
docker run -d --name hbbs -p 21115:21115 -p 21116:21116 -p 21116:21116/udp -p 21118:21118 -v `pwd`:/root -it --net=host --rm rustdesk/rustdesk-server hbbs -r 服务器IP
# 如果–net=host运行正常,-p选项就不起作用了, 可以去掉。
sudo docker run --name hbbs  -v /docker/rustdesk:/root -it -d --net=host --restart=always rustdesk/rustdesk-server hbbs -r 服务器IP

运行 hbbr

docker run -d --name hbbr -p 21117:21117 -p 21119:21119 -v `pwd`:/root -it --net=host --rm rustdesk/rustdesk-server hbbr
# 如果–net=host运行正常,-p选项就不起作用了, 可以去掉。
sudo docker run --name hbbr -v /docker/rustdesk:/root -it -d --net=host --restart=always rustdesk/rustdesk-server hbbr

使用 docker ps 命令查看服务是否正常运行。

windows

image

“ID 服务器” 和 “中继服务器” 都填写服务器 ip,不需要加端口,但是在服务器防火墙要记得放行端口号。

国产代码编辑器何愁不崛起

2023-08-25 08:00:00

相关文献:


今天摸鱼发现了一个神奇的东西,CEC-IDE。

据说是:“国内首款适配国产操作系统、自主可控的集成开发环境工具 CEC-IDE”,在某网站有官方资料说明。

下载了一下打算瞅瞅,嚯!前端程序员对这个应该都不陌生 (除了不认识左上角的 logo)

随着深入了解,又发现了一个有意思的地方, 国产代码编辑器安装完第一步,官网文档教你怎么切换成中文模式。

咱好歹把中文默认语言安排上对吧,不然多对不起 “国产” 这个名头。


试用了一下这个软件,确实挺不错,除了 logo 跟 vscode 不同,其他都完美适配了 vscode,简直是非常好用,不愧是…!

… 真的挺无语,就是说 骗经费稍微用点心吧。

湘湖小隐团建

2023-08-19 08:00:00

游湖拍摄了几张照片。

Bitwarden / Vaultwarden 密码管理工具

2023-07-27 08:00:00

1.介绍

Bitwarden 是一款自由且开源的密码管理服务,用户可在加密的保管库中存储敏感信息。Bitwarden 平台提供有多种客户端应用程序,包括网页用户界面、桌面应用,浏览器扩展、移动应用以及命令行界面。Bitwarden 提供云端托管服务,并支持自行部署解决方案。

在调研一些市面常用的密码管理工具之后,我选择了 Bitwarden。然后使用自托管的方式部署在服务器上,定时备份数据库,导出密码,安全性也有保证。

https://bitwarden.com/

而 Vaultwarden 是一个使用 Rust 编写的非官方 Bitwarden 服务器实现,提供了不错的 web 管理界面,并与 Bitwarden 兼容。

2.部署

先在服务器执行指令。

docker run -d --name bitwardenrs \  
  --restart unless-stopped \  
  -e WEBSOCKET_ENABLED=true \  
  -v /data/password/data/:/data/ \  
  -p 6666:80 \  
  -p 3012:3012 \  
  vaultwarden/server:latest

部署完成后打开网站,登陆注册之后,删除容器。

需要配置禁止注册的功能,因为我只想自己使用。如果不禁止注册,可以忽略下面这两步。

docker stop bitwardenrs  #停止容器  
docker rm -f bitwardenrs  #删除容器
docker run -d --name bitwardenrs \  
  --restart unless-stopped \  
  -e SIGNUPS_ALLOWED=false \  
  -e WEBSOCKET_ENABLED=true \  
  -v /data/password/data/:/data/ \  
  -p 6666:80 \  
  -p 3012:3012 \  
  vaultwarden/server:latest

3.docker-compose.yml

通过 ChatGPT 我将这段代码转化成了docker-compose.yml 文件,可以更方便的部署安装。

version: '3'
services:
  bitwardenrs:
    image: vaultwarden/server:latest
    container_name: bitwardenrs
    restart: unless-stopped
    environment:
      - SIGNUPS_ALLOWED=false
      - WEBSOCKET_ENABLED=true
    volumes:
      - /data/password/data/:/data/
    ports:
      - "6666:80"
      - "3012:3012"

4.chrome 扩展

通过反向代理可以使用域名登陆 web 端,并且使用了 chrome 扩展方式,在登陆网站可以自动保存密码。

https://chrome.google.com/webstore/detail/bitwarden-free-password-m/nngceckbapebfimnlniiiahkandclblb

之后我只需要记住一个主密码,其他网站密码全部采用加密方式生成,方便与安全并存。

昨夜梦有感

2023-07-10 08:00:00

2023 年 07 月 09 日夜梦

昨晚我做了一个梦,也许是因为最近太多玩《极限国度》的缘故。在梦中,我在一条陌生的赛道上狂飙,以极速骑行。当我经过一座拱桥时,遇到了一个年轻人,他和我做着相同的动作,优雅地在空中旋转着,360 度的转体后稳稳地降落下来。

随着时间的推移,场景莫名地转变,我来到了小时候的家乡。高耸的水塔和大坝,两旁是湍急的河流。突然间,原本明亮的天空变成了黑色。我抬头仰望,只见一片漆黑,空气中仿佛弥漫着焦糊的味道,给人一种破败的感觉。太阳的踪迹已然消失,不是夜晚降临的景象,而是一个黑色的太阳,或者说太阳熄灭了,但我的眼睛仍能看到周围的景物。

我感到一丝恐慌,不知道光源从何而来。我和那个年轻人沿着小路继续前行,世界变得异常安静,没有任何声音。就在我不知所措之际,路的尽头出现了一个村庄,人影模糊地闪动着。我正打算走过去询问发生了什么事情,那个年轻人却拉住了我,示意我保持安静。

我们继续前行,发现许多生物,看起来像《我的世界》游戏中的末影人,不停地穿梭着。突然间,它们似乎发现了我们,展开翅膀向我们飞来。作为普通人,这让我感到非常害怕。紧张中,我就在这时醒了过来,看了看手机,显示 6 点半。无法再入睡,我起来倒了杯水。

ps: 梦是没有丝毫逻辑的,脑海中的剧情可能前言不可后语,上一秒还在街道上奔跑,下一秒就穿越到了侏罗纪被当作食物吞进腹中。可能是精力太充沛的缘故,已经很久没有做梦了。

为了避免胡思乱想,好好运动一番吧!

image

极美的晚霞画卷

2023-07-09 08:00:00

使用设备 RedmiK50 拍摄。

当太阳逐渐西沉时,天空的色彩变得温暖而温和,绝美的晚霞画卷即将展开。

1688912095539.png

这是我今天最喜欢的一张照片。

aos 事件触发失败

2023-06-29 08:00:00

项目中遇到这个问题,网页往下滑动时加载 aos 事件触发不了,原因也很简单,因为 aos.js 是在页面加载时获取页面高度从而绑定一些事件进去,但是如果这个文件加载速度比框架加载的快,会导致两者高度不一致,从而动画效果触发失败。

我尝试用 window.onload 但是效果并不好,依旧失败。

window.onload=function(){
  AOS.init({offset:100,duration:600,once:true,delay:100,});
}

所以决定加个 100ms 的延迟调用实例,这样就没问题了。

setTimeout(function(){
  AOS.init({offset:100,duration:600,once:true,delay:100,});
},100)

奇趣周刊 - 第 19 期

2023-06-26 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1695084092443

今年刚刚修好的上桥骑行路线,体验一下骑行爬坡过桥。坡度还比较缓,难度不大很轻松。


1.李跳跳自定义规则

https://github.com/Snoopy1866/LiTiaotiao-Custom-Rules

由于一些特殊原因,导致软件开发者不能继续维护发布,有老的安装包可以继续使用,但是规则需要更新。

[李跳跳安装包下载]

本项目收集适用于“李跳跳”的 App 自定义规则,包括基础规则和增强规则。

1695084306451

2.免费视频分享平台

https://catbox.moe/

每次允许上传最多 200 MB。免费的视频分享平台,开发者通过公益捐助维持网站运行。

1695084928449

3.Twitter Block Porn

https://greasyfork.org/zh-CN/scripts/470359-twitter-block-porn

一个油猴脚本,共享黑名单,一键拉黑所有黄推诈骗犯。

4.文章阅读

  1. https://www.vangoghmuseum.nl/en/art-and-stories/stories/all-stories/5-things-you-need-to-know-about-van-goghs-self-portraits
    梵高的自画像

  2. https://www.electrospaces.net/2023/09/some-new-snippets-from-snowden-documents.html
    斯诺登文件中的一些新片段

  3. https://www.zhangxinxu.com/wordpress/2023/09/js-copy-image-clipboard/
    又 get 到了,JS 复制图片到剪切板 - 张鑫旭

晚霞彩虹

2023-06-26 08:00:00

短短几分钟后,彩虹就消失得无影无踪,我庆幸自己按下了快门。

image

奇趣周刊 - 第 18 期

2023-06-19 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1694436654865

杭州亚运会主场馆奥体中心,又称“大莲花”。在夜色的笼罩下灯光变幻莫测,彷佛盛开的莲花。


1.Gitstars

https://gitstars.cfour.top/
https://github.com/cfour-hi/gitstars

管理 GitHub star 的网站。Github 作为开发者的第一社交平台,拥有数不胜数的优秀开源项目,给工作和学习带来巨大方便,遇到自己需要或是喜爱的项目只需点击 Star 便可收入囊中。

Star is easy,可随着 Starred Repositories 增长,在需要使用到某个项目时难免记不清叫什么,而 Github 又只提供简单的搜索,找到目标 Starred Repository 竟也成了件小小的麻烦事。

所以拥有自己的 Github Stars Repositories Manager 也算是开发者的必备需求。

Gitstars 由此诞生 💡

1694583629560

2.Awesome-Selfhosted

https://github.com/awesome-selfhosted/awesome-selfhosted

收录了大量可托管的 Web 程序,可以在您自己的服务器上托管的自由软件网络服务和 Web 应用程序的列表。

3.What OpenAI Really Wants (OpenAI 真正想要什么)

https://www.wired.com/story/what-openai-really-wants/

夜骑湘湖

2023-06-15 08:00:00

突然萌生了骑行的想法,行动力很强的我立刻研究相关知识并付诸实践,两个小时36公里浅浅锻炼一下。绕着湘湖骑了一大圈,开始还有点累腿,后面慢慢就适应节奏了。

看着湖景,赏荷花,夜风迎面吹来,别有一番滋味。

image

image

奇趣周刊 - 第 17 期

2023-06-12 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1. 枫言枫语

https://fyfy.fm/

听见科技与人文的声音,一档由开发者 @枫影 JustinYan 和设计师 @自力 hzlzh 主持的播客节目。

image

2.不明白播客

https://buzzsprout.com
文字版备份:https://github.com/bumingbaipod/podcast

3.Hacker News 黑客新闻

https://news.ycombinator.com/

image

4.Lindy HN

https://hn.lindylearn.io/

Hacker News 是一个宝藏,而且不仅仅是讨论新闻。99% 的最佳内容不是今天创造的,该网站汇集了 Hacker News 上经常转发的经典文章。

image

5.RSS 阅读器

https://www.qireader.com/

一个优雅的 web 端 RSS 阅读器,是我现在经常使用的一个网站,订阅杂志、周刊、博客等。

image

6.css 滚动区域容器

使用“scroll-timeline”的 CSS-only 滚动区域指示器。

诀窍是简单地向这两个箭头元素添加由时间线控制的不透明动画。

https://twitter.com/shuding_/status/1698176794124578923
https://codepen.io/shuding/pen/WNLGQor

image

vue2 中引入 Ant Design 报错问题

2023-06-06 08:00:00

https://1x.antdv.com/docs/vue/getting-started-cn/

根据 Ant Design 官方文档,在 vue2 中安装组件时使用指令:

npm i --save ant-design-vue

但在实际操作中会出现问题,它仅适用于 vue3 项目,在 vue2 中需要带上相匹配的版本号。

npm i --save [email protected]
// main.js
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';
Vue.use(Antd);

奇趣周刊 - 第 16 期

2023-06-03 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1.PicW

https://github.com/Flysky12138/PicW
https://picw.4everland.app/

Github 图床管理 PWA 应用。

image

2.ipfs 图床

https://cdn.ipfsscan.io/

永久的、去中心化保存和共享文件。

image

关于 HugoFast 这个项目

2023-06-02 08:00:00

项目来源于我的突发奇想,并且基于热爱折腾的心理,利用 GitHub 本身提供的 Api,将博客管理面板化。

目前仅支持 hugo,其他程序有对应的工具,就不造轮子了。

Github: https://github.com/dlzmoe/HugoFast
网站:https://hugofast.netlify.app/
用户文档:https://hugofast-docs.netlify.app/

1. 自托管

前往 https://github.com/dlzmoe/HugoFast fork 本项目,然后下载到本地。

git clone https://github.com/dlzmoe/HugoFast.git

::: warning 这里默认你有一定的代码基础,本地提前安装好了 node 依赖等。 :::

# 进入项目安装依赖包
cd HugoFast
yarn

# 使用 `yarn serve` 运行项目
yarn serve
# 打包到 dist 文件
yarn build

打包后的文件可以放在的任意空间部署。

2. 准备工作

(1) 获取 Github Token

前往 https://github.com/settings/tokens/

image

新建一个 token,选择 repo / user 这两个权限,名称随意,最后生成 token。

注意要保存好这个密钥,在新的终端打开网站需要用到这个密钥。

(2) 操作 hugo 源码仓库

前往你的 hugo 源码仓库,如下图。

image

image

在仓库的 setting/actions,滑到最下面,打开 actions 的 pr 权限,不然 actions 自动部署会失败。

image

(3) 添加 .github/workflows/main.yml

点击 actions > set up a workflow yourself 新建一个 actions 工作流。

注意: git config --global user.email ""git config --global user.name "" 里面要填写你的 GitHub 邮箱以及用户名。

name: hugo deploy

on:
    push:
    workflow_dispatch:
    schedule:
        # Runs everyday at 8:00 AM
        - cron: "0 0 * * *"

jobs:
    build:
        runs-on: ubuntu-latest
        steps:
            - name: Checkout
              uses: actions/checkout@v2
              with:
                  submodules: true
                  fetch-depth: 0

            - name: Setup Hugo
              uses: peaceiris/actions-hugo@v2
              with:
                  hugo-version: "latest"

            - name: Build Web
              run: hugo
            - name: Commit changes
              run: |
                git config --global user.email ""
                git config --global user.name ""
                git pull
                git add .
                git commit -m "my commit"                
            - name: Push changes
              uses: ad-m/github-push-action@master
              with:
                github_token: ${{ secrets.GITHUB_TOKEN }}
                branch: main

3. HugoFast 网站

使用官方提供的版本。

在线管理:https://hugofast.netlify.app/

image

第一行:获取的 GitHub Token
第二行:hugo 源码仓库

image

填写完成后,会自动获取文章目录,选择后点击确认。

image

进入网站就可以正常使用功能了,如修改文章,发布新文章,其他功能还在开发中。


未完待续…

奇趣周刊 - 第 15 期

2023-05-26 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1.python-markdown-to-wordpress

https://github.com/nefu-ljw/python-markdown-to-wordpress

批量上传 Markdown 文件到 WordPress,并且支持更新单个 WordPress 文章

2.gpt-shell

https://github.com/Zenyet/gpt-shell
https://g.thoughtflow.org/

仿 shell 终端的 gpt 聊天对话工具。

随想录 (10)

2023-05-23 08:00:00

夕阳伴随着晚霞划破了云层。

image

下班时在路口发现的风景,记录美好的瞬间。


https://github.com/canton7/SyncTrayzor

搞定了 SyncTrayzor,一个 Syncthing 的应用,可以不用挂载终端,始终在电脑后台进行同步,占用也很低,一切都是在无感下进行操作的,适合我这种极简主义风格的人。

流程是:家中的电脑 <–> 服务器 <–> 公司电脑三方同步,写笔记和博客更方便了。

奇趣周刊 - 第 14 期

2023-05-19 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1.Github Desktop 汉化

https://github.com/robotze/GithubDesktopZhTool https://desktop.github.com/

Github Desktop 汉化工具 支持 Windows Mac Linux

2.ByteMD

https://github.com/bytedance/bytemd

ByteMD 是一个使用 Svelte 构建的 Markdown 编辑器组件。它还可以用于其他库/框架,例如 React、Vue 和 Angular。 目前掘金社区使用的就是这款编辑器。

hugo 纯静态编写一个字数统计脚本

2023-05-13 08:00:00

早上起来的时候看到微信群中有博友分享自己的建站时间,聊着聊着说到总字数上面,这时我才发现我的网站没有统计的地方,索性就自己写一个吧,因为是静态博客,所以不涉及后端、服务器等,只用前端的方式来解决。

这里需要借助我之前的一篇文章,《用原生 js 给网站写个搜索功能》,里面有提到如何生成一个文章列表 json 文件,这里我们会用到这个文件 index.json

写一个 ajax 方法,让浏览器遍历每个页面。

$(document).ready(function () {
  $.ajax({
    url: '/index.json',
    type: 'get',
    dataType: 'json',
    success: function (data) {
      const pageUrls = data;
      const totalNum = pageUrls.length;
      $('#totalNum').html(totalNum);
      let totalWords = 0;
      pageUrls.forEach(urlObj => {
        $.get(urlObj.permalink, function(data) {
          const content = data.replace(/(<([^>]+)>)/gi, " ").replace(/[^\w\s]/gi, " ");
          const words = content.split(" ");
          const wordCount = words.filter(word => word !== "").length;
          totalWords += wordCount;
          $('#totalWords').html(totalWords);
        });
      });
    },
    error: function () {
      console.log('error')
    }
  })

});

我是将信息放在我的 关于 页面下。

经过脚本统计,我已经写了 <span id="totalNum"></span> 篇文章,总共 <span id="totalWords"></span> 个字。
<!-- 经过脚本统计,我已经写了 164 篇文章,总共 97577 个字。 -->

image

奇趣周刊 - 第 13 期

2023-05-12 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1.vue-fabric-editor

https://nihaojob.github.io/vue-fabric-editor/#/
https://github.com/nihaojob/vue-fabric-editor

基于 fabric.js 和 Vue 的图片编辑器,可自定义字体、素材、设计模板。fabric.js and Vue based image editor, can customize fonts, materials, design templates.

image

2.Tiptap

https://tiptap.dev/ https://github.com/ueberdosis/tiptap

一款现代化编辑器,支持原生 javascript, vue, react, nuxt, php 等多种框架和语言。

image

3.van-nav

https://github.com/mereithhh/van-nav

一个轻量导航站,汇总你的所有服务。全平台支持,单文件部署,有配套浏览器插件。我已经安装使用了 https://nav.zburu.com/ ,界面美观,作者还在开发中。

image


PS:
1.推荐一部最近在看的英剧《真相捕捉》。

奇趣周刊 - 第 12 期

2023-05-05 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

1.KeepChatGPT

https://github.com/xcanwin/KeepChatGPT

非常好用的一款油猴脚本,提升使用 chatGPT 时的体验。

2.vue-chrome-extension-boilerplate

https://github.com/mubaidr/vue-chrome-extension-boilerplate

基于 Vue.js + Webpack 的 Chrome 扩展模板,开发 chrome 扩展非常方便,并且可以自动打包压压缩。

3.STDF 组件库

https://stdf.design/#/

基于 Svelte 与 Tailwind 的移动 web 组件库。

image

奇趣周刊 - 第 11 期

2023-04-29 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

周刊开源地址:https://github.com/dlzmoe/weekly-note

工具

1.竹白工具箱

https://github.com/utags/zhubai-toolbox

虽然我用不到,看了下文档,确实是一个不错的小工具。

2.chatgpt-miniprogram

https://github.com/leon-fong/chatgpt-miniprogram

⚡️ 一键拥有你自己的 ChatGPT 微信小程序。

3.toastr

https://github.com/CodeSeven/toastr

toastr 是一个用于非阻塞通知的 Javascript 库。jQuery 是必需的。目标是创建一个可以定制和扩展的简单核心库。

拿来作为项目中的消息提示非常好用,轻量,功能健全。

4.LXGW WenKai / 霞鹜文楷

https://github.com/lxgw/LxgwWenKai

An open-source Chinese font derived from Fontworks’ Klee One. 一款开源中文字体,基于 FONTWORKS 出品字体 Klee One 衍生。

话题

  1. https://github.com/hax/heshijun_v_360 贺师俊与 360 的劳动争议诉讼。

奇趣周刊 - 第 10 期

2023-04-22 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

网站

1. 小狼毫 rime

https://github.com/ssnhd/rime

Rime Squirrel 鼠须管配置文件(朙月拼音、小鹤双拼、自然码双拼),极简开源的输入法。

2. vue simple markdown

https://github.com/Vivify-Ideas/vue-simple-markdown

一款非常轻量的解析 markdown 语法的解释器。

3. Color-Name

https://www.color-name.com/

可以获取色值对应的名称,质量很不错,网站设计的很精美。

4. chrome-plugin-memos

https://github.com/dlzmoe/chrome-plugin-memos

自荐一个开源项目,Memos 的 chrome 插件。在设置中中输入网站域名、以及 openId 进行配置,支持编辑,发布,归档,编辑历史笔记功能。

  1. 下载已打包好的 dist-zip 文件,解压拖拽到 chrome 扩展中。
  2. npm run build 重新构建

文章

  1. https://lukasrosenstock.net/2023/04/10/im-single-i.html
    Lukas Rosenstock’s Blog

  2. https://iosifache.me/twitter-architecture-trends
    Twitter 架构的趋势(来自科技爱好者周刊)

周六爬山日常

2023-04-15 08:00:00

本周行程:万松书院出发,翻越凤凰山,登顶玉皇山,俯瞰八卦田。

上周和朋友爬山,由于实力太弱拖了后腿,因此奋发图强,直接下单了一把登山杖,差生文具多。但是有一说一,登山杖是真的好用,不管是上山还是下山都很借力,对膝盖起到了一定的保护作用。

这次徒步旅行相对来说还是比较轻松的,拥抱大自然,见到了很多不一样的风景,这是城市所不能拥有的,特别是登高望远,那种心理状态一下子就升华了。虽然只是个小山坡,但是还是能体会到“会当凌绝顶,一览众山小。”

再加上一路与朋友说说笑笑,聊聊天,说实话还是蛮充实的。最近迷上了爬山这项活动,基本每周都会去一趟,呼吸新鲜空气,观看自然的风光。

每到一个地方都会打卡记录一番。

八卦田遗址公园 八卦田 八卦田遗址公园

凤凰山俯瞰钱塘江 凤凰山俯瞰钱塘江

玉皇山俯瞰西湖 玉皇山俯瞰西湖

万松书院 万松书院

奇趣周刊 - 第 9 期

2023-04-15 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

话题

1.最近 openai 封禁了一批 ip,大多是共享账号等,但也影响了正常服务的运行。

程序

1.ChatGPT-Next-Web

https://github.com/Yidadaa/ChatGPT-Next-Web

https://chat-gpt-next-web.vercel.app/

一键拥有你自己的 ChatGPT 网页服务。One-Click to deploy your own ChatGPT web UI.

Github Actions 自动化打包流程

2023-04-13 08:00:00

Github Actions 监听仓库 commit 事件,然后执行自动化打包流程,并通过第三方平台自动部署,发布非常方便。

name: Build
on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Install dependencies
        run: npm install
      - name: Build
        run: npm run build
      - name: Commit changes
        run: |
          git config --global user.email "youremail"
          git config --global user.name "username"
          git add .
          git commit -m "Action bot commit"          
      - name: Push changes
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: main

程序会自动获取 github token,只需要在仓库设置 /settings/actions 中把 Workflow permissions 设为读写权限。

从云溪竹径到九溪烟树

2023-04-09 08:00:00

这棵树刚种下的时候,还是宋朝初年。

茶园,梅家坞。

奇趣周刊 - 第 8 期

2023-04-08 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

网站

1.https://open-gpt.app/

立即使用海量的 ChatGPT 应用,或在几秒钟内创建属于自己的应用。

2.SNK

https://github.com/Platane/snk

从 github 用户贡献图生成贪吃蛇游戏,并将屏幕截图输出为动画 svg 或 gif.

3.ChatGPT 微信小程序

https://github.com/leon-fong/chatgpt-miniprogram

4.Heimdallr

https://github.com/LeslieLeung/heimdallr

Heimdallr 是一个非常轻量的通知网关,可以聚合各种推送渠道,使用 Serverless 部署,几乎零成本运行。

5.vue-ellipse-progress

https://github.com/setaman/vue-ellipse-progress

一个无依赖性的 Vue.js 插件,用于创建漂亮的动画圆形进度条,使用 SVG 实现。该插件的目的是结合其他可用库的最佳属性并添加独特的功能,以易于使用且界面友好的组件形式提供。

6.Beyond Compare 4

https://www.scootersoftware.com/index.php

代码对比工具,用过一段时间,体验还不错,可以有效对比出两个文件或者文件夹之间的差异。

奇趣周刊 - 第 7 期

2023-04-02 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

程序

1. BlockNote

https://github.com/TypeCellOS/BlockNote

https://www.blocknotejs.org/

基于 react 的编辑器,notion 风格非常优雅。

2. Quill

https://quilljs.com/

一款简简洁美观的编辑器,目前 tg 旗下的网站 https://telegra.ph/ 正在使用该编辑器。

本周因工作繁忙,更新较少。

奇趣周刊 - 第 6 期

2023-03-26 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

程序

1. NotionNext

https://github.com/tangly1024/NotionNext

一个使用 NextJS + Notion API 实现的,部署在 Vercel 上的静态博客系统。为 Notion 和所有创作者设计。

2. Notion Blog

https://github.com/ijjk/notion-blog

一个 Next.js 网站使用新的 SSG 支持和一个支持概念的博客。

3. Next.js Notion Starter Kit

https://github.com/transitive-bullshit/nextjs-notion-starter-kit

使用 Next.js 和 Vercel 在几分钟内部署您自己的基于概念的网站。

4. roomGPT.io

https://www.roomgpt.io/

使用 AI 为每个人打造梦幻房间,给您的房间拍张照片,看看您的房间在不同主题下的样子,100% 免费,立即改造您的房间。

5. AiTxt 智能助手

https://aitxt.io/

基于 chatGPT 开发的工具箱,小红书、日报、周报、OKR、邮件、餐厅点评、节日祝福、甩锅等 Ai 文案助手,效果不错。

推荐

  1. https://www.misalignmentmuseum.com/ 错位博物馆
  2. Misalignment Museum 想象一个 AGI 已经摧毁了大部分人类的后世界末日世界,然后意识到这很糟糕,创建了这个博物馆作为对剩余人类的纪念和道歉。这是一个艺术装置,目的是增加对通用人工智能 (AGI) 及其破坏和善的力量的了解。我们的目标是创造一个空间来反思技术本身并批判性地思考人工智能及其影响。我们的希望是激发和建立支持,以制定和制定我们可以采取的风险缓解措施,以确保随着 AI 的不断发展和 AGI 的出现,我们拥有一个积极的未来。

西湖 · 春游

2023-03-26 08:00:00

春日繁花媚不言,天高云淡乐无边。
雨过天晴添好景,走遍山川任心怜。

持续了一周的降雨,终于迎来了一天晴朗,决定去春游。

杭州西湖 - 郭庄,花圃公园打卡。

d5c32b7965bb7d8169fc474a55e2315

4122319a5b72bbc2c3f6b9c489e6880

85b286fa23c7634b285a571e820da89

奇趣周刊 - 第 5 期

2023-03-19 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

网站

1. AwA UI

https://www.handsome-css.com/

使用 html 和 css 制作的非常漂亮的开源 UI 组件。

2. identicon 生成器

http://identicon.net/

生成 sha256 头像,identicon 生成器是一个免费的在线工具,用于生成 identicons 图像。

3. openai-translator 划词翻译

https://github.com/yetone/openai-translator

一个基于 openai 的翻译插件,需要绑定自己的 key,在浏览器实现划词翻译功能,试用了一下,质量和效果还是很不错的。

4. lew-ui

https://lew.kamtao.com/

https://github.com/lewkamtao/Lew-UI

一个 Vue 3 组件库。

5. Tavler Icons

https://tabler-icons.io/

一组 3512 个免费的 MIT 许可的高质量 SVG 图标,供您在 Web 项目中使用。

6. Scribble Diffusion

https://scribblediffusion.com/

https://github.com/replicate/scribble-diffusion

基于机器学习模型,可以自己画一个草图,然后附带一些描述,最后会生成相关的图像,质量和功能都挺不错的。

文章

https://babyyoung.gitbook.io/english-level-up-tips/ 英语进阶指南,高效学习英语。 https://vue-js.com/learn-vue/start/ Vue 源码系列,适合入门研究源码。

用原生 js 给网站写个搜索功能

2023-03-17 08:00:00

本文也算是一篇教程,可以给 hugo 网站加个搜索功能,并且实现热更新,体验感更好。

如果是其他程序,只需要按照特定的模板生成以下格式的文件即可,主要代码从 第 2 部分 开始,第一章节写的是如何用 hugo 输出文章列表 json 文件。

title 是文章标题,permalink 是文章链接。

[{
  "permalink": "",
  "title": ""
}, {
  "permalink": "",
  "title": ""
}]

1. hugo 模板生成文章列表 json 文件

layouts 文件夹下新建 index.json 文件,模板内容如下:

其中第 2 行最后面的 "blog" 是你文章文件夹的名称。大部分是 posts 等等,这里是我个人的名称。

{{- $.Scratch.Set "posts" slice -}}
{{- range where .Site.RegularPages "Type" "blog" -}}
    {{- $.Scratch.Add "posts" (dict "title" .Title "permalink" .Permalink) -}}
{{- end -}}
{{- $.Scratch.Get "posts" | jsonify -}}

按照这个模板,hugo 本地预览可以打开 http://localhost:1313/index.json 查看,如果输出了一些数据如下图,说明你成功了。

image

2. js 代码

/layouts/_default 新建一个模板文件 search.html,大致的结构参考其他模板文件,然后写入我们需要的内容。

首先是一个简单的 html 结构,给 input 绑定一个事件。

<form class="search"> 
  <input type="text" id="searchTerm" name="searchTerm" autocomplete="off" oninput="initiateSearch()">
</form>
<div id="resultsContainer">请输入关键词进行搜索...</div>

然后通过一个 get 请求获取 json 文件,传入关键词参数,生成搜索列表。

<script>
  function search(jsonData, searchTerm) {
    let results = [];
    for (let i = 0; i < jsonData.length; i++) {
      for (let property in jsonData[i]) {
        if (jsonData[i].hasOwnProperty(property) && jsonData[i][property].toString().indexOf(searchTerm) > -1) {
          results.push(jsonData[i]);
          break;
        }
      }
    }
    return results;
  }

  function displayResults(searchResults) {
    let container = document.getElementById("resultsContainer");
    container.innerHTML = "";
    if (searchResults.length > 0) {
      for (let i = 0; i < searchResults.length; i++) {
        let resultDiv = document.createElement("div");
        let resultTitle = document.createElement("a");
        resultTitle.innerText = searchResults[i].title;
        resultTitle.setAttribute('href', searchResults[i].permalink)
        resultDiv.appendChild(resultTitle);
        container.appendChild(resultDiv);
      }
    } else {
      let noResultsMessage = document.createElement("p");
      noResultsMessage.innerText = "没有找到搜索结果。";
      container.appendChild(noResultsMessage);
    }
  }

  function initiateSearch() {
    let searchTerm = document.getElementById("searchTerm").value;
    let xhr = new XMLHttpRequest();
    xhr.open('GET', '/index.json', true);
    xhr.onreadystatechange = () => {
      if (xhr.readyState === 4 && xhr.status === 200) {
        let jsonData = JSON.parse(xhr.responseText);
        let searchResults = search(jsonData, searchTerm);
        displayResults(searchResults);
      }
    };
    xhr.send();
  }
</script>

然后再在 /content 新建一个 search.md 文件调用该模板即可。

---
slug: search
title: 搜索
layout: search
---

写了一个基础的样式,可以直接使用。

.search {
  width: 100%;
  display: flex;
  align-items: center;
  height: 36px;
}
.search #searchTerm {
  width: 100%;
  height: 100%;
  outline: none;
  border: none;
  padding: 0 15px;
  box-shadow: 1px 2px 10px rgba(0, 0, 0, 0.1);
}

#resultsContainer {
  margin-top: 20px;
}
#resultsContainer div {
  margin-bottom: 10px;
  margin: 0;
}
#resultsContainer div a {
  display: block;
  width: 100%;
  padding: 6px 10px;
  transition: all 0.1s linear;
  border-radius: 4px;
}
#resultsContainer div a:hover {
  background: #f3f3f3;
}

奇趣周刊 - 第 4 期

2023-03-12 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

基于 chat3.5 api 的网站,测试过速度很快,不需要翻墙。

https://freechatgpt.lol/ https://chatgpt.ddiu.io/ https://desk.im/ https://freegpt.cc/

工具

1. DownGit

https://minhaskamal.github.io/DownGit/#/home

可以单独下载 GitHub 仓库中某个文件夹,输入对应的 url 地址,在一些特定的场景下非常好用。

2. Twitter 视频下载器

https://www.getfvid.com/zh/twitter

功能如名,只要复制推文对应的 url 地址即可下载视频,可以选择清晰度。

3. 竹白专栏检索

https://zb.liey.cn/

统计了几百个竹白的周刊,包括各种元信息,可以通过关键词搜索到自己喜欢的周刊。

4. Open-Source UI

https://uiverse.io/

这个网站有很多有趣的网页小元素,都是用 HTML 和 CSS 写的。免费供给个人和商业使用,可以给你的项目增加一些有趣的元素。

5. 周报生成器

https://weeklyreport.avemaria.fun/zh

一个基于 chatGPT3 的开源项目,简单描述工作内容,帮你生成完整周报。效果不错,疲于写各种日报周报的小伙伴的福音。

Github

https://github.com/XIU2/CloudflareSpeedTest cloudflare 自选节点测速,可以选择直连较快稳定的 ip。 https://github.com/mubaidr/vue-chrome-extension-boilerplate 使用 Vue.js + Webpack 搭建的 Chrome 扩展模板,支持热更新,开发起来更方便快捷。 https://github.com/SortableJS/Sortable 适用于现代浏览器和触摸设备的可重新拖拽列表,不需要 jQuery 或框架。 (继续更新…)

博客运行 1000 天了

2023-03-10 08:00:00

img-xdeYVDd4ZQL2iC6i7I3r4RGc

使用 openai 生成。 [大漠孤烟直,长河落日圆]

不知不觉,已经坚持写博客 1000 天了,从 2020 年 6 月 14 日开始,第一次踏入这个圈子。最开始我还会写一些技术类的文章,但随着时间的推移,这意义不大,因为类似的文章在互联网都能找到一大把,因此我放弃这种类型文章的写作。主要进行了生活分享,和一些随笔等。更新日期也不固定,哪天忽然来了灵感会兴奋的更新一次。

虽然频率不高,但是从未间断,而且还让更多的人和我一起就某个话题进行讨论,这都是让我感觉到有意义的事情。分享自己的经验,心路历程和价值观,在互联网留下自己的足迹,或许在很久之后,我渐渐淡出这个圈子,但是我会保存这个网站在 GitHub 等平台,如果有缘分的话,十年、二十年,你都可以在搜索引擎无意中看到我。每当一想到这,我都感觉充满了干劲。

这一路上有朋友慢慢退出,我也表示遗憾,就目前而言,我应该不会放弃做博客,阅读者很少,但总归是有的。不能让自己失望,更不能让其他朋友失望,期待十年后依旧可以在互联网看到我的身影。回首过往萧瑟处,也无风雨也无晴!

GitHub 生成一个新的 ssh 密钥

2023-03-09 08:00:00

要在 GitHub 上生成新的 SSH 密钥,请按照以下步骤操作:

  1. 打开终端或命令提示符。
  2. 输入以下命令:将"your_[email protected]"替换为您在 GitHub 上注册的电子邮件地址。
ssh-keygen -t rsa -b 4096 -C "[email protected]"
  1. 稍后您将被提示输入文件保存位置和密码短语。您可以保持所有默认设置,只需一直按 Enter 键即可。
  2. 然后输入以下命令:eval “$(ssh-agent -s)”
  3. 然后输入以下命令以将新密钥添加到 ssh-agent 中:ssh-add ~/.ssh/id_rsa
  4. 最后,请在 GitHub 上添加您的公共密钥。转到 GitHub 设置并单击"SSH and GPG keys “(“SSH 和 GPG 密钥”)。
  5. 单击“新密钥”按钮。
  6. 在“标题”字段中输入一个描述性标题,以便您可以识别此密钥。
  7. 在“密钥”字段中,将“~/.ssh/id_rsa.pub”文件中的内容复制并粘贴到文本框中。
  8. 单击“添加 SSH 密钥”按钮以保存您的新密钥。

现在,您已经成功为 GitHub 生成了一个新的 SSH 密钥!

来自 chatGPT 的教程,记录一下。

this.$set 给 vue 数组添加数据

2023-03-05 08:00:00

Vue 不允许在已经创建的实例上动态添加新的根级响应式属性,它可以使用 Vue.set(object, key, value) 方法将响应属性添加到嵌套的对象上。

如果直接给数组添加对象,视图不会更新,但是可以输出正确的值;只有通过 this.$set 覆盖才可以更新视图。

 for (let i = 0; i < this.data.length; i++) {
  // this.data[i].state = false;
  this.$set(this.data[i], 'state', false)
}

奇趣周刊 - 第 3 期

2023-03-05 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

每周分享一些软件,程序,文章等。(2 月 19 日 -2 月 25 日)

工具

1.zeabur

https://zeabur.com

类似 netlify 和 vercel 的站点部署,目前免费,还没有推出收费模式,国内速度友好(不知道能坚持多久),我也用它部署了一个备份网站 https://zsh.zeabur.app/ ,没什么问题。

2.sli.dev

https://sli.dev/

专为程序员打造的 ppt 演示文稿工具,ui 美观,配置也很方便,公司会议,技术分享等场景可以用到。

3.maple

https://github.com/subframe7536/obsidian-theme-maple

一款精美的 Obsidian 主题,我目前正在使用,视觉效果很好,作者还提供了一些短代码,扩展文件图标等。

文章

https://github.com/OBKoro1/web-basics 大厂前端需要掌握的 JS 基础能力,大厂场景题、大厂面试真题。
https://eugeneyan.com/writing/what-i-did-not-learn-about-writing-in-school/ 《我在学校没有学到的关于写作的知识》作者对写作的一些探讨。

奇趣周刊 - 第 2 期

2023-02-28 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

使用 cloudflare r2 / zoer + github actions 混合部署。

具体流程在 https://github.com/dlzmoe/microfeed 仓库的 README.md 有写,感谢 https://github.com/microfeed/microfeed 开源项目

奇趣周刊 - 第 1 期

2023-02-21 08:00:00

奇趣周刊,每周四发布,分享有趣的软件,程序,动态新闻等。 周刊列表 | rss 订阅

每周分享一些软件,程序,文章等。2023-02-21

尽量保证每周四发布。

乌镇 · 南浔

2023-02-20 08:00:00

周末出游,去了两个古镇,体验还不错,分享几张旅游风景照。

5

4

6


3

2

1

随想录 (10) 我是否陷入了信息茧房?

2023-02-13 08:00:00

如题,我是否陷入了信息茧房?暂时是以一个疑问句的形式出现,因为我还未确认事实真的如此。

什么是信息茧房?

信息茧房是一种现代的网络语境下的概念,指的是某些人因过度沉溺于网络而逐渐隔离于现实世界,形成了一种"信息茧",难以自拔。这些人可能会在网络上消耗大量的时间,而忽视了现实生活中的关系和活动,甚至对现实世界产生抵触。


最新,openai 推出的 chatGPT 再一次火遍全网。而在去年的 11 月底,我曾经体验过一段时间,当时是它刚刚面世的时候,我也算是第一批体验用户。

那时给我的感觉就像是一个初出茅庐的 ai,我在和它交流的过程中,明显可以感觉到他还能稚嫩,但还是展现出了极强的学习力,这让我很震撼。但随着时间的推移,它的热度逐渐下降,我也很少使用它了。

直接今年 2 月中旬它再一次出现在我的眼前,以一个强大的姿态席卷而来,在短短两个月的时间收获了全球 10 亿用户,而这个数量也正在以指数增长。我想这和微软的投资有着很大关系,再一次证明了其眼光的卓越性。

随着这几天的慢慢使用,我发现它能增强甚至取代一些我的办公工具,我可以用它写文档,比我自己写的更好,以及其他各种使用方法。

但令我疑惑的事情来了,我刷 v2ex,推特以及其他社交论坛的时候,chatGPT 以极大的篇幅出现在我的眼前,已经刷不到别的消息了。十条帖子有一半是关于 chatGPT 的,感觉自己的信息通道快被占满了,这让我很烦躁,我开始质问自己是否陷入了信息茧房。

我仔细研究了一下,被"信息茧房"的人通常具有以下几个特点:


  1. 偏见和偏激:他们往往只信任那些与他们观点相同的信息,不愿接受不同的观点和证据。
  2. 过于相信权威:他们往往把所有信息都当做是权威的,而不是评估信息的可靠性。
  3. 过于固执:他们往往固执已见,不愿接受新的证据和观点,即使这些证据是正确的。
  4. 不愿思考:他们往往不愿自己思考,而是直接相信他人的观点,不愿自己思考和评估信息的正确性。

经过自我判断我觉得我可能还没到达这个程度,那么为什么我会有标题这个错觉呢?

我分析了一下,很大可能是目前一个现象级的态势,就像之前的 web3 一样,如火如荼,却又昙花一现。

我不知道 chatGPT 是否有这个趋势,它能否成为一个划时代的产物,我对此还是保持积极态度的。

如何让 hugo 支持渲染 html 代码?

2023-02-04 08:00:00

想在文章中插入 iframe, video, 以及其他比如 summar y 等标签,可以配置一下 config 文件。

  1. 如果是 config.toml,加上下面配置;
[markup]
  [markup.goldmark]
    [markup.goldmark.renderer]
      unsafe = true
  1. 如果是 config.yaml,加上下面配置;
markup:
  goldmark:
    renderer:
      unsafe: true

这样就可以在文章中插入 iframe, video, 以及其他比如 summary 等标签。

否则就会显示下面注释,自动过滤 html 代码。

<!-- raw HTML omitted -->

对比一下添加配置前后的区别:

1688137354334.png

1688137371574.png

使用 cloudflare 反代 gravatar 生成镜像

2023-02-01 08:00:00

1.介绍

cloudflare 有一个非常好用的功能—Workers,可以在无服务器的状态下运行一些程序,包括常见的反向代理等。

所以我利用这一功能实现对 gravatar 反代,达到国内访问加速的目的。

话不多说,开始流程操作,分享我的部署过程。

1.登陆后,在主页点击左侧的 Workers,然后点击创建服务

2.服务名称随意填写,然后点击右下角创建。

3.点击右上角快速编辑

4.在左侧编辑器中输入下列的代码

2.代码

点击展开代码
// 替换成你想镜像的站点
const upstream = 'gravatar.com'
 
// 如果那个站点有专门的移动适配站点,否则保持和上面一致
const upstream_mobile = 'gravatar.com'
 
// 你希望禁止哪些国家访问
const blocked_region = []
 
// 禁止自访问
const blocked_ip_address = []
 
// 替换成你想镜像的站点
const replace_dict = {
    '$upstream': '$custom_domain',
    '//gravatar.com': ''
}
 
//以下内容都不用动
addEventListener('fetch', event => {
    event.respondWith(fetchAndApply(event.request));
})
 
async function fetchAndApply(request) {
 
    const region = request.headers.get('cf-ipcountry').toUpperCase();
    const ip_address = request.headers.get('cf-connecting-ip');
    const user_agent = request.headers.get('user-agent');
 
    let response = null;
    let url = new URL(request.url);
    let url_host = url.host;
 
    if (url.protocol == 'http:') {
        url.protocol = 'https:'
        response = Response.redirect(url.href);
        return response;
    }
 
    if (await device_status(user_agent)) {
        upstream_domain = upstream
    } else {
        upstream_domain = upstream_mobile
    }
 
    url.host = upstream_domain;
 
    if (blocked_region.includes(region)) {
        response = new Response('Access denied: WorkersProxy is not available in your region yet.', {
            status: 403
        });
    } else if(blocked_ip_address.includes(ip_address)){
        response = new Response('Access denied: Your IP address is blocked by WorkersProxy.', {
            status: 403
        });
    } else{
        let method = request.method;
        let request_headers = request.headers;
        let new_request_headers = new Headers(request_headers);
 
        new_request_headers.set('Host', upstream_domain);
        new_request_headers.set('Referer', url.href);
 
        let original_response = await fetch(url.href, {
            method: method,
            headers: new_request_headers
        })
 
        let original_response_clone = original_response.clone();
        let original_text = null;
        let response_headers = original_response.headers;
        let new_response_headers = new Headers(response_headers);
        let status = original_response.status;
 
        new_response_headers.set('access-control-allow-origin', '*');
        new_response_headers.set('access-control-allow-credentials', true);
        new_response_headers.delete('content-security-policy');
        new_response_headers.delete('content-security-policy-report-only');
        new_response_headers.delete('clear-site-data');
 
        const content_type = new_response_headers.get('content-type');
        if (content_type.includes('text/html') && content_type.includes('UTF-8')) {
            original_text = await replace_response_text(original_response_clone, upstream_domain, url_host);
        } else {
            original_text = original_response_clone.body
        }
 
        response = new Response(original_text, {
            status,
            headers: new_response_headers
        })
    }
    return response;
}
 
async function replace_response_text(response, upstream_domain, host_name) {
    let text = await response.text()
 
    var i, j;
    for (i in replace_dict) {
        j = replace_dict[i]
        if (i == '$upstream') {
            i = upstream_domain
        } else if (i == '$custom_domain') {
            i = host_name
        }
 
        if (j == '$upstream') {
            j = upstream_domain
        } else if (j == '$custom_domain') {
            j = host_name
        }
 
        let re = new RegExp(i, 'g')
        text = text.replace(re, j);
    }
    return text;
}
 
async function device_status (user_agent_info) {
    var agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"];
    var flag = true;
    for (var v = 0; v < agents.length; v++) { if (user_agent_info.indexOf(agents[v]) > 0) {
            flag = false;
            break;
        }
    }
    return flag;
}

然后点击保存并部署,就可以成功反代 gravatar 镜像了。

然后照葫芦画瓢,可以通过这种方式反代任何网站,访问起来大致无压力,延迟 100+ms 左右。

3.自定义域名

在触发器中可以找到自定义域名选项,输入已经绑定 cloudflare 的域名,再次赞美 cloudflare,如果事前绑定好了域名,在这里可以直接输入二级域名,cloudflare 会自动解析,全部都是自动化的。

在主页左侧的 网站 绑定域名。

# 分享我的免费镜像。
https://gravatar.zsh.im/avatar

利用 memos 生成的在线动态列表

2023-01-09 08:00:00

memos 是一个具有知识管理和社交功能的开源自托管备忘录中心。

Github 地址:https://github.com/usememos/memos

可以用它本身提供的 api,然后结合静态博客,做成一个在线的动态列表,类似于朋友圈功能。

接口 url 有固定的格式,openId 是每个用户自动生成的。

其次我又写了两个方法,一个是时间戳转成正常时间格式,一个是利用正则删去内容中多余的标签。

将下面代码复制到你想显示的页面编辑,只需修改 openId,提供了一个基础的样式,如果有其他优化方案,欢迎指出。


由于接口中带了用户密钥,建议完成代码后将 js 加密再引入,不然可能会有一定的安全风险。

推荐网址:https://tool.lu/js/

<!-- 结构和 css -->
<div class="sslist"></div>

<style>
.sslist-item {
  padding: 10px 10px 20px;
  margin-bottom: 20px;
  border-radius: 2px;
  background: #f3f3f3;
  box-shadow: 1px 2px 4px rgba(0, 0, 0, .2);
  transition: all 0.2s linear;
  animation: up 1s forwards;
  transform: translateY(20px);
  opacity: 0;
}

@keyframes up {
  0% {
    transform: translateY(20px);
    opacity: 0;
  }

  100% {
    transform: translateY(0);
    opacity: 1;
  }
}

.sslist-item:hover {
  background: #eee;
}

.sslist-item .sslist-date {
  opacity: 0.6;
  margin: 0;
  font-size: 15px;
  margin-bottom: 5px;
}
</style>
// 调用接口的 js 方法
function memosShow() {
  var memosStr = "";
  var openId = "xxx";
  $.ajax({
    url: "https://memos.zburu.com/api/memo?openId=" + openId + "&tag=说说",
    type: "get",
    dataType: "json",
    success: function (data) {
      // 生成数组
      const sslist = data.data;

      for (let i = 0; i < sslist.length; i++) {
        const element = sslist[i];

        // 把时间戳转为正常时间格式 2023-01-9 13:17:12
        var date = new Date(element.createdTs * 1000);
        Y = date.getFullYear() + "-";
        M =
          (date.getMonth() + 1 < 10
            ? "0" + (date.getMonth() + 1)
            : date.getMonth() + 1) + "-";
        D = (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " ";
        h = date.getHours() + ":";
        m = date.getMinutes() + ":";
        s = date.getSeconds();
        const createdTsNew = Y + M + D + h + m + s;
        sslist[i].createdTs = createdTsNew;

        // 利用js正则删去内容前的标签字符
        const contentNew = element.content.slice(4);
        sslist[i].content = contentNew;
      }

      $.each(data.data, function (i, item) {
        list =
          "<div class='sslist-item'>" +
          "<p class='sslist-date'>" +
          item.createdTs +
          "</p>" +
          item.content +
          "</div>";
        memosStr += list;
      }),
        $(".sslist").html(memosStr);
    },
    error: function () {
      console.log("error");
    }
  });
}
memosShow();

分享一个页面点击特效

2023-01-06 08:00:00

点击页面时可以弹出随机颜色小心心,目前我的网站就使用了这个特效。

直接将下面的代码复制到引入的 js 文件中即可,不用进行其他配置。

!function(e,t,a){function n(){c(".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"),o(),r()}function r(){for(var e=0;e<d.length;e++)d[e].alpha<=0?(t.body.removeChild(d[e].el),d.splice(e,1)):(d[e].y--,d[e].scale+=.004,d[e].alpha-=.013,d[e].el.style.cssText="left:"+d[e].x+"px;top:"+d[e].y+"px;opacity:"+d[e].alpha+";transform:scale("+d[e].scale+","+d[e].scale+") rotate(45deg);background:"+d[e].color+";z-index:99999");requestAnimationFrame(r)}function o(){var t="function"==typeof e.onclick&&e.onclick;e.onclick=function(e){t&&t(),i(e)}}function i(e){var a=t.createElement("div");a.className="heart",d.push({el:a,x:e.clientX-5,y:e.clientY-5,scale:1,alpha:1,color:s()}),t.body.appendChild(a)}function c(e){var a=t.createElement("style");a.type="text/css";try{a.appendChild(t.createTextNode(e))}catch(t){a.styleSheet.cssText=e}t.getElementsByTagName("head")[0].appendChild(a)}function s(){return"rgb("+~~(255*Math.random())+","+~~(255*Math.random())+","+~~(255*Math.random())+")"}var d=[];e.requestAnimationFrame=function(){return e.requestAnimationFrame||e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(e){setTimeout(e,1e3/60)}}(),n()}(window,document);

一周阳人日记

2022-12-22 08:00:00

不出所料,每天往返于公司,地铁,住的地方,我阳了。 很难受,客观记录一下我的症状吧。

第一天:2022/12/19

持续发烧 38℃左右,没有吃药,硬抗过去。

抗原测试呈阳性,应该是第二天或者第三天了,但是前几天没有任何症状,所以就不算在内了,从昨天夜里才开始起反应。

在床上躺着四肢酸痛,皮肤掉皮,呈粉末状,大概是高烧的原因。

嗓子哑疼,喝了梨水 + 冰糖,很润滑,舒服好多。

临睡前测了 38.7℃,吃了感冒药,居然变得精神,刷了会手机。


第二天:2022/12/20

持续头晕 + 头疼,凌晨又吃了一颗感冒药,过了一会浑身出汗,睡衣直接浸透但是没有力气,就坚持睡觉。

喉咙发苦,断断续续的头晕,早上测了体温降到 37°。

一吃东西就想吐,肚子疼,头晕。


第三天:2022/12/21

嗓子疼,喉咙痒,咳嗽,暂无其他症状。

精神状态良好。

不能吃油腻的食物,清淡的可以吃点。


第四天:2022/12/22

夜里出了一身汗,又把睡衣浸湿,应该是免疫系统趁我不注意,夜里大战了一场。

白天的精神状态和第三天差不多,但是咳嗽的力度又加大了。可以正常坐在电脑前办公了,打工人。

未转阴。


第五天:2022/12/23

无限流鼻涕,越来越像大号感冒,不过前两天实在太难受。鼻子塞的通红,伴随着间歇性的咳嗽。

依旧是没什么胃口,吃东西也吃不下去,我预计这种情况还将持续一周左右。


第六天:2022/12/24

今日状态还算可以,间歇性的流鼻涕,没有昨天那么严重,准备明天再测一下抗原。

而且我发现奥密克戎对我最大的影响是食欲,完全提不起吃东西的欲望,看着眼前的美食却味如嚼蜡。

身为一个食客,这才是最令我感到痛苦的!!希望可以快点恢复我的食欲。


接下来的几天基本都在咳嗽中度过,病情也差不多结束。感概良多,希望诸位保重好自己的身体。

我的 2022 年度总结

2022-12-13 08:00:00

枯木逢春

又到了一年一度的总结时间,回顾自己这一年做了些什么。我通常都是在最后一个月开始进行总结,预留出足够的时间让我可以好好思考与回顾,如果时间很短就会显得很仓促。没有什么主题,就是想到哪写到哪,讲讲这一年的感受。

这两天有个消息,彻底取消核酸和行程卡了,健康码也随之取消查看,因为失去了意义。不评价这件事情,单纯说一下感受,今年终于可以回家过年了。这三年来,只有在五一国庆等节假日回老家,一到春节都因为各种缘故取消了回家的行程,多少有些遗憾,在城市过年也缺少那么一些年味。我的春期假期大概 15 天左右,时间还是很充裕的,可以好好计划一下行程,约下朋友出去玩两天,做好个人防护。

开源了一些自己写的小工具和主题,包括一些油猴插件,程序主题,浏览器扩展等,有一二十个用户,虽然很少,但是好评不错,给了我很大的激励。

为爱发电,我也乐于此道,有时候会收到一些有关开源项目的邮件,我都会和对方展开讨论,解决了别人的问题,也让我有了更深刻的认知,学海无涯。

工作上自我感觉有了一点提升,安排我独自负责一个项目,开拓了我的技术栈,会用到一些没接触过的技术,一边搜索翻阅文档一边写代码,面向业务提升自己比纯粹的看代码好像更有意思。但是这样的提升比较流于表面,不够深刻接触不到核心,必须承认这是我的短板,我有必要重新规划一下自己的路线。不过相比去年的浑浑噩噩,这段时间还算是充实。也结识了一些行业内的朋友,休息天会出来闲谈吃饭,去西湖看看荷花。

使用思源作为笔记软件,每天坚持写日记,其实也就是一些生活琐事和心情,坚持记录下来,偶尔会翻阅一下看看自己以前干过什么蠢事,这也是一种乐趣。为了表达支持订阅了年付会员,这的确是一个很优秀的 note 软件,开发者也在不断优化体验。唯一的缺憾就是无法同步配置文件,询问过作者是因为不同设备系统兼容性的问题,所以目前无法做到完美的同步,不过还是很赞。

今年的雪来的格外的早,11 月份就下雪了,而且杭州今年的夏季也是格外的热,高达 40 多度,这很反常,不是气象学专家所以无法对这一现象做出分析,只能表达自己的感概。每年都是 365 天,同样的时间生活出不一样的自己,孑然一身,牵挂的只有自己的家人。

—— 更新于 2022/12/13 日

贰拾肆年

2022-12-02 08:00:00

越长大越不想长大,在人生的第 24 年经历了很多事情。

41edfcbaa94d035a95bacb71ce283b7

未若柳絮因风起

2022-12-01 08:00:00

冬天来了,杭州今年的雪来的猝不及防,前天还是十多度,今天下雪,走在路上只感到寒风刺骨。

“白雪纷纷何所似?
撒盐空中差可拟。
未若柳絮因风起。”

0ce9e6d1414c8e27e01e7663e5b0ca3

ps: 一切都在朝好的方向发展,纪念博客建立 900 天。

js 中 [ ]+{ } 和 { }+[ ] 的区别

2022-11-15 08:00:00

1668517551012

[ ]+{ }:一个数组加一个对象。

加法会进行隐式类型转换,规则是调用其 valueOf() 或 toString() 以取得一个非对象的值 (primitive value)。

如果两个值中的任何一个是字符串,则进行字符串串接,否则进行数字加法。[ ] 和 { } 的 valueOf() 都返回对象自身,所以都会调用 toString(),最后的结果是字符串串接。

[ ].toString() 返回空字符串,({ }).toString() 返回 “[object Object]",最后的结果就是 “[object Object]"。

{ }+[ ]:看上去应该和上面一样。

但是{ }除了表示一个对象之外,也可以表示一个空的 bock。在 [ ] + { }中,[ ]被解析为数组,因此后续的 + 被解析为加法运算符,而{ }就解析为对象。

但在{ } + [ ]中,{ }被解析为空的 bock,随后的 + 被解析为正号运算符。即实际上成了:{∥empty block}+[ ],即对一个空数组执行正号运算,实际上就是把数组转型为数字。首先调用.valueOf(),返回数组自身,不是 primitive value,因此继续调用 [ ].toString(),返回空字符串。空字符串转型为数字,返回 0,即最后的结果。

11 月 8 日月全食全程录像

2022-11-09 08:00:00

转自 youtube: https://youtu.be/o0ewAMw-ftg


很遗憾我自己的手机像素不行,拍不清晰,但是肉眼可见的红,也有些壮观。

138155db93275399de4fd2b73268df2

霜叶红于二月花

2022-10-28 08:00:00

远上寒山石径斜,白云深处有人家。

停车坐爱枫林晚,霜叶红于二月花。

安装一个基于 Github 的静态图床程序

2022-10-22 08:00:00

demo: https://img.zburu.com/

今天分享一下如何使用 Github 作为图床,首先就要推荐这个工具 picx。

Github 仓库:https://github.com/XPoet/picx

本文的图片全部使用该工具上传,然后通过 Staticaly 进行 cdn 加速(这个在上传的时候自动生成链接。)

可以设置自动压缩、转码成 webp 格式。

基于 GitHub API 开发的具有 CDN 加速功能的图床管理工具。无需下载与安装,网页端在线使用!免费!稳定!便捷!极速!

看了一下源码,vue 写的,全部都是纯静态化加上本地存储,所以说没有后端。

也就是说我们也可以部署在自己域名下,不使用开发者提供的域名,虽然这么做没什么区别,但还是折腾一下吧。

我会在最后提供一个打包好的链接(直接打包源码,不做任何修改),下载后自行上传到域名空间下即可使用。

image

说明文档:https://picx-docs.xpoet.cn/tutorial/get-start.html

这是官方说明,试用了一下,效果很棒。图床如何使用文档中标注的很清晰,我就不赘述了,本文只介绍如何编译打包,面向群友。


过程很简单,需要提前准备好 node 环境。

1.下载源码

直接去 https://github.com/XPoet/picx,然后 Download Zip。

2.执行命令

下载后解压来到这个页面,然后在空白处鼠标右键,打开终端。

1666401948613

image

如果你已经有 node 环境了,直接执行下面命令就可以,如果没有,先去安装一下 node 环境,这里就假设环境安装好了。(https://nodejs.org/zh-cn/)。

下面这两种方式都可以,没有区别。

# 安装依赖
# 这个过程因为网速问题可能比较慢

npm install
#or
yarn

依赖安装之后,就开始构建。

# 构建

npm run build
# or
yarn build

构建完成之后就会生成一个 dist 文件夹,这就是打包好后的文件,里面有一个 index.html 和其他静态资源。

3.我应该上传到哪里?

这个时候只需要将他们上传到你想要访问的域名目录下即可。

比如我使用的是 hugo 博客程序,先在 content 下新建一个文件夹,命名为 img,(你可以起任何名字,注意不要和其他文件名冲突即可。) 然后把 dist 文件夹下的所有文件全部复制到 /content/img/ 文件夹下。

1666402810476

这样在执行 hugo 命令后会打包到 public 文件夹下,然后直接访问 https://zburu.com/img 就可以打宝图床。

或者你自己上传到任何你想放的空间下都可以,没有限制~~

图床如何使用对着文档琢磨即可,上面写的很详细。

https://picx-docs.xpoet.cn/tutorial/get-start.html

4.其他问题

  1. 如果后面开发者更新图床版本,可以从第一步重新操作,下载、构建、上传。

如果有其他问题,请在评论区留言~~

localStorage 的相关运用

2022-10-20 08:00:00

localStorage 是浏览器自带的一个属性,只读的 localStorage 属性允许你访问一个 Document 源(origin)的对象 Storage;存储的数据将保存在浏览器会话中。localStorage 类似 sessionStorage,但其区别在于:存储在 localStorage 的数据可以长期保留;而当页面会话结束——也就是说,当页面被关闭时,存储在 sessionStorage 的数据会被清除。

应注意,无论数据存储在 localStorage 还是 sessionStorage,它们都特定于页面的协议。

另外,localStorage 中的键值对总是以字符串的形式存储。 (需要注意,和 js 对象相比,键值对总是以字符串的形式存储意味着数值类型会自动转化为字符串类型).

localStorage 的使用也非常简单,分为存入和读取,可以将其绑定在事件方法中。

// 存入
const arr= 100;
localStorage.setItem("key", JSON.stringify(arr));

// 读取
const arr = JSON.parse(localStorage.getItem("key"));

这里 "key" 指的是存到浏览器中的参数名,arr 则是参数值。

  1. localStorage.setItem("key", JSON.stringify(arr)); 这个方法就是将数组 arr 存到了浏览器的 localStorage 中,它的参数名叫 key
  2. const arr = JSON.parse(localStorage.getItem("key")); 就是读取浏览器中参数名为 key 的参数值。

比如静态保存某个设置参数,可以将其写入数组中,然后通过 localStorage 存储,原本刷新就会显示默认设置,现在可以在每次刷新的适合读取存入的参数。

在一些场景下非常好用,比如开发一个油猴脚本等等。

清除 localStorage,分为清除所以的存储值和清除某个特定的 key。

// 清除本地存储中的所有值
localStorage.clear(); 

// 本地存储中删除特定项
localStorage.removeItem(key);

hugo 一键打包并上传 github

2022-10-14 08:00:00

分享一个 hugo 一键打包并上传 github 的方法,前提是你之前上传过 GitHub 仓库。

采用 window 支持的 sh 可执行文件,在根目录新建一个 hugo.sh 文件,里面放上一些命令。

hugo

# cd public

time=$(date "+%Y-%m-%d %H:%M:%S")
echo $time

git add .
git commit -m "自动执行构建脚本 🎓$time"
git push
exit

可以在文件夹双击运行 hugo.sh,也可以在终端输入命令。

.\hugo.sh

省去了我输入繁琐的 Git 上传指令,并且自动生成当前系统时间作为 commit。

其他

这里我上传的是整体的 hugo 目录,如果你只想上传生成的 /public ,在中间加入 cd public

开发后的反省

2022-10-13 08:00:00

晚上给朋友写了一个油猴脚本,场景是一个拥有很多输入框的表单,多为经常填写的一些字符串。跟我说了一下,刚好没什么事情,吃完晚饭抽两小时出来简单写了写。

需求是预设多套模板,后期可以自己添加内容,置顶在网页空白处,点击按钮让设置好的文本填充到网页的表单中。

处理起来很简单,我原本打算使用ajax调用json文件,把模板写在json中,这样一目了然便于后期自行添加数据,但是在测试后发现目标网站屏蔽了外部文件,所以放弃这种写法。转而new了一个数组,每个对象就是一套模板,接下来就是简单的循环,写了一个可以缩放的弹窗,然后把交互的按钮绑定点击事件,点击对应按钮就会在网站上填充对面模板的字符串。

流程大概就是这样,结束之后在给他润润色,写一下css代码。总体来说没什么难度,两个小时不到就搞定了。

但是我发现,天天写vue,element写多了,最基础的js有些生疏了,比如数组的操作遍历循环,中间竟然还报错,仔细检查才发现原因,实属不应该。

深刻反省一下自己,基础知识还是要巩固的,不能一味的调用框架,即使它写起来很爽。

将数组中多个对象的同名属性值取出合并成新数组

2022-09-26 08:00:00

业务中需求的方法,接口返回一个数组,里面包含了大量的对象,具有同名的属性名,比较常见。但是需要将其中参数为 name 的属性值全部取出,合并成数组。

const num = [
    {
      id: 1,
      name: 'abc',
    },
    {
      id: 2,
      name: 'xyz',
    }
]
function getFields(arrnum, field) {
    const resnum = [];
    for (let i = 0; i < arrnum.length; ++i)
    resnum.push(arrnum[i][field]);
    return resnum;
}

const result = getFields(num, "name");
console.log(result);             // ['abc', 'xyz']
console.log(result.join(' '));   // "abc xyz"

关于密码的一些想法

2022-09-21 08:00:00

今天在坐地铁的过程中,突然想要一个问题,一个安全稳定,便于记忆的密码时什么样子的。是否要包括字母大小写,数字,以及特殊符号?

如何设置并存储?

我想这是必须的,但是密码如果太过复杂,就不便于记忆。而想要记忆就必须有自己的规律,可一旦有了规律,别人在破解的时候就有了更多的思路,安全性相对来说就下降了。

不论怎么想,这两者是冲突的,很难让它们统一起来。

然后就和朋友讨论关于密码的问题,他们也有着自己独特的方法,给了我很多扩展的思路,比如记忆一个公式,圆周率第多少位,拼音 + 数字等等,这样既能够记忆,也拥有足够的混淆性。你不知道它的位置下标,就不能够找到对应的密码字符,安全性比通用一套密码又高了很多。

也有朋友表示自己在使用密码管理软件,比如 1password,bitwarden,KeePass 等,通过算法可以自动生成各种高复杂度的密码字符串。但是在述说的过程中,他们表示了对密码管理软件的担忧。如果软件遇到破解或者勒索,主密码丢失等,那么所有密码就不复存在,尽管这种可能性很少,但是前段事件就发生了这样的事情。

而且大的软件商持有更多的用户,一旦遇到问题,产生的影响就更大,用户只能寄希望于厂商有足够的攻防能力。所有密码在云上都有备份,这本身就是一个致命的缺陷,如果云端数据被攻破,后果不堪设想。

扩展说说

个人习惯使然,让我很难百分百的信任各种云,可能是被害妄想症吧,总觉得不好,把所有数据扔到云上保存对我来说是一件极其难受的事情。所以我一般都是三处备份,本地肯定要留一份在硬盘中,服务器保存一份,然后在 oss 桶中再备份一些不太重要的数据。成本不算高,但是需要持续维护这些数据,相对于我来说,还算是比较轻松的,并且我也乐在其中。

你是否想过成为一名作家?

2022-09-14 08:00:00

https://poets.org/poem/so-you-want-be-writer

这是曾经询问过自己的一个问题,我想很多人在年少时期可能都会有过这样的经历,渴望将自己的文字带给更多人。

这件事情大多发生在高中时期吧,如果我没记错的话。忽然想起来那个年轻段的人都有些中二,看了不少玄幻和悬疑小说,心中泛起了写小说的念头,于是动笔。但最终也没有坚持很久,草草写了几万字,只在几个朋友中传阅。

可惜在高考结束后一些残留的手稿都丢失了,每每想到这件事情都暗自懊恼,也算是一个回忆吧。现在再让我写根本没有那个精力,但我也曾涌起过当一名作家的想法,好像文科生都曾这样。

我有个朋友喜欢写诗,现在看起来语义不那么通顺,不太符合诗词规范,但是他并不在意,很是乐于此道,我也给了他很大的鼓励,虽然文字中伤春悲秋,“为赋新词强说愁”。

持续写作是一个好的习惯,从前是为了抒发情感,现在是为了输出自己的思考。大同小异,归根结底还是对自我的一种输出。


so you want to be a writer?

Charles Bukowski - 1920-1994

if it doesn’t come bursting out of you in spite of everything, don’t do it. unless it comes unasked out of your heart and your mind and your mouth and your gut, don’t do it. if you have to sit for hours staring at your computer screen or hunched over your typewriter searching for words, don’t do it. if you’re doing it for money or fame, don’t do it. if you’re doing it because you want women in your bed, don’t do it. if you have to sit there and rewrite it again and again, don’t do it. if it’s hard work just thinking about doing it, don’t do it. if you’re trying to write like somebody else, forget about it.

if you have to wait for it to roar out of you, then wait patiently. if it never does roar out of you, do something else.

if you first have to read it to your wife or your girlfriend or your boyfriend or your parents or to anybody at all, you’re not ready.

don’t be like so many writers, don’t be like so many thousands of people who call themselves writers, don’t be dull and boring and pretentious, don’t be consumed with self- love. the libraries of the world have yawned themselves to sleep over your kind. don’t add to that. don’t do it. unless it comes out of your soul like a rocket, unless being still would drive you to madness or suicide or murder, don’t do it. unless the sun inside you is burning your gut, don’t do it.

when it is truly time, and if you have been chosen, it will do it by itself and it will keep on doing it until you die or it dies in you.

there is no other way.

and there never was.

文档和视频,你更喜欢哪种学习方式?

2022-09-07 08:00:00

如题,这是我今天思考的话题。灵感来源于我在 dev.to 网站上看到的一个帖子。对于 learning 的方式,更倾向于看文档还是视频?我觉得可以从多个维度进行分析。

  1. 学习进度,也可以说是过程
  2. 行业领域
  3. 后天应该养成什么样的习惯呢?

1.学习进度

学习进度应该是对学习方式影响最大的因素了。

学习的初始阶段以看视频为主,应该对这个领域不熟悉,一直在行内人看起来随手就能做很简单的操作,新人往往不能够很好的了解它,所以最开始的阶段最好是以视频教材为主。

打个比方,上学时老师都是手把手的教学,因为你什么都不会,必须口口相传,这是对待新人的方式,如果让他自己一直干看,一学期下来估计也看不懂几页书。(当然了,天才除外,这不在讨论的范围,大家 90% 都是普通人。)然后到后期的时候一般都是自习因为该教的基础都是都教了,需要学生自己研究琢磨,不然根本不能融会贯通。

从这个角度看,自学似乎是学习方式的终点。

2.行业领域

拿我自己打比方我更倾向于看文档,视频的话偶尔看看,但是看的不多。因为我觉得看视频很难一眼看出里面的内容是否是我所需要的,

看文档的话,通过关键词搜索以及其他方式,我可以在可控的,短时间内找到自己想要的内容,不管学习还是工作都是极其方便的。

看代码的话文档是最清晰可辨的,但是就在我进行思考的时候问了对象一句,“你平时学习更倾向于什么方式呢,看视频还是看文章?”

答:“肯定是看视频啊,不然你看我买那么多视频课程。”

我恍然大悟,将原来没有思考到的方向补充了一下,行业对人学习方式的影响,女朋友做的是视频后期,一般用到的软件是 C4D,pr 等,曾扫了一眼,密密麻麻的功能一排排在那里放着,看得我有些头皮发麻。

我自己也就拿 photoshop 处理一下图像,pp 图或者水印之类的,没想到做视频处理那么复杂,所以如果不看视频学习的话,我估计连软件的功能都摸不懂。

这就是另一个比较重要的因素了。

3.扩展说说

如果培养好的 learning 习惯呢?

其实不论是看视频还是看文档,我们的初衷都是为了提高自己的能力和知识水平。但似乎学习是一件很枯燥的事情,从小到大我们都被灌输着“好好学习,天天向上”的概念,一切都是以提高成绩为目的,教育资源地域区别非常大。因为经济发展不平衡这点也是不可避免,学习是唯一能走出落后的途径。

扯的有点远,讲讲 learning,始终保持学习状态的人群很少,时间和精力是一方便,还有就是方法,这也是大家老生常谈的问题。我认识要从兴趣爱好入手,在需要学习的领域培养出自己的兴趣爱好,然后围绕着这个方向 360 度扩展,个人认为这是一种非常好的方案。

我最开始做网站几乎一窍不通,但是对此还是比较感兴趣的,凭借着仅有的前端知识开始摸索,慢慢了解了各大网站平台,github,vercel,cf 等,玩的多了慢慢开始了解 php,运维等领域,磕磕绊绊的学习简单的知识。

期间结识了很多朋友,给予我帮助,在相关技术上慢慢进步。同时我也会在自己的领域给他们提供技术支持,这好像也算是开源精神吧,没有闭门造车,大家一起讨论各种各种的内容。

久而久之就发现自己相比较之前,知识好像丰富了那么一些,这就是一种正确的 learning 方式。所以我认为在兴趣爱好中学习,不失为一种积极且充满劲头的一种方式。

聊聊你的 learning 方式,留言或者给我发邮件一起探讨!

浴鹄湾游记

2022-09-03 08:00:00

浴鹄湾,东靠杨公堤,西临三台山路,北至三台梦迹景区,南到赤山埠。

水利博物馆照片

2022-08-22 08:00:00

公司团建,随手拍两张照片。

白居易诗。

火烧云

2022-08-12 08:00:00

一些容易遗漏的点

2022-08-10 08:00:00

var arr = [
  "单位 1",
  "单位 2",
  "单位 3",
  "单位 4",
  "单位 5"
]
// 1.数组 []
for (var i = 0; i < arr.length; i++) {
	this.defaultData.hostUnitList.push({
		[arr[i]]: this.defaultData[arr[i]]
	});
}
// 2.对象{}
for (var i = 0; i < arr.length; i++) {
	this.defaultData.hostUnitList[arr[i]] = this.defaultData[arr[i]]
}

随想录 (9)

2022-08-07 08:00:00

落霞与孤鹜齐飞,秋水共长天一色。

mmexport1659868787969.jpg

放个图防止抑郁。


昨天看了一个帖子很有感触,土生土长的城市本地人很难体会每隔一段时间要重新建立社会关系的难受,从小到大,每到一个地方上学,从陌生变为熟悉,中间总有有一段难熬的时间。很多随父母打工而四处的求学的小孩,特别能有这种体会,因此也就有很多人厌恶上学,他们不理解为什么总是要四处奔波,父母也不理解小孩为什么不喜欢学习,两者误会,家庭矛盾也因此诞生了。当然我说的很片面,这只是其中一个原因。

我也只是想让自己牢记,原来艰苦的生活不但让身心变得疲惫,还能带来精神上的贫穷,让自己明白原来还有这种只有四处奔波的人才能体会。说一下我的经历,小学二年级之前在老家乡下读书,然后辗转在厦门上的小学,初中和高中又回到县城读书,大学是在郑州读书,工作后又来到杭州。 看着这个时间段,很难有一个朋友可以从小到大陪着你,大多数人都是这样,悲哀又现实。

踏入工作之后,应该是更能体会,一个人在陌生的城市,除了工作好像也没有其他事可以做,对于社恐来说更是一种严重的环境。而我应该算是比较幸运的,在一个陌生的城市还有家人和恋人,减缓了那种焦虑感,但我认识的一些人确是情绪受到很大影响,包括压力太大而离开这座城市。

写博客并不是为了贩卖焦虑,而且觉得有些话不吐不为快,在文字中发泄情绪也是一种良好的方法,为了明天的生活慢慢加油。

随想录 (8)

2022-08-04 08:00:00

来自一位朋友的意难平。

win11 使用 10 版本的鼠标右键设置

2022-08-02 08:00:00

实测好用,没有坑。

管理员权限下打开终端,输入。

reg add "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}\InprocServer32" /f /ve

随后重新启动资源管理器即可

taskkill /f /im explorer.exe & start explorer.exe

恢复默认指令。

reg delete "HKCU\Software\Classes\CLSID\{86ca1aa0-34aa-4e8b-a509-50c905bae2a2}" /f

Anghunk 主题如何在后台评论列表解析表情?

2022-07-30 08:00:00

本文专为 Anghunk 主题开发文档而写。 https://github.com/dlzmoe/Anghunk

方案是在梦繁星的协助下完成的。

介绍

Anghunk 评论携带很多精美的表情,但是正常情况下 Typecho 后台是没办法正常解析这些表情的。如下图;


但是,经过我解析表情之后,就可以变成下面这样,使用主题如果需要这种功能,可以按照我下面的步骤进行改造。

修改

打开 /admin/manage-comments.php 文件,在题头插入:

/*
* 解析表情
*/
function getparseBiaoQingComment($content) {
	$emopath=$_SERVER['REQUEST_SCHEME'].":". DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR . $_SERVER['HTTP_HOST'];
	$emo = false;
	global $emo;
	if(!$emo) {
		$emo = json_decode(file_get_contents(dirname(dirname(dirname(__FILE__))).'/zburu.com/usr/themes/dlzmoe/libs/OwO.json'), true);
	}
	foreach ($emo as $v) {
		if($v['type'] == 'image') {
			foreach ($v['container'] as $vv) {
				$emoaa="::".$v['name'].":".$vv['icon']."::";
				$content = str_replace($emoaa, '  <img style="max-height:40px;vertical-align:middle;" src="'.$emopath.'/usr/themes/dlzmoe/libs/emotion/'.$v['name'].'/'.$vv['icon'] .'.png"  alt="'.$vv['text'] .'">  ', $content);
			}
		}
	}
	return $content;
}

注意我放置的位置,必须要要 <?php ... ?> 之间

**找到 第166行,修改一下,把标签中的代码替换为下方标注的。 **

<div class="comment-content">
   <!-- 解析输出带表情的评论 -->
   <?php $con=$comments->content; echo getparseBiaoQingComment($con); ?>
</div> 

之后回到后台评论列表就可以看到评论成功被解析了,本方法仅适用于 Anghunk 主题。

随想录 (7) 随便写的别当真

2022-07-29 08:00:00

好像很久没有写随笔了,最近发生的事很多,却不是件件如人心意。上午烈日当空,下午乌云密布,也已经是常事。当然我说的是天气。

生活只是尽量让自己过的更好,喂自己一口毒鸡汤,艰难前行。

“定个小目标,先瘦他个十斤!” 子舒狠狠的咬下一块鸡肉发出豪言壮语。

周五总能让人变的浮躁,所以我才静下心来随便写点,千万别当真。


[夕阳图]

出错了,面板运行时发生错误!

2022-07-18 08:00:00

# 删除之前无法使用的面板环境
rm -rf /www/server/panel/pyenv

#重新获取新的面板环境和更新包
curl http://download.bt.cn/install/update_panel.sh|bash

笔记:vue 中使用 axios 调用数据并渲染

2022-07-12 08:00:00

示例如下。

<template>
  <div class="posts">
    <div
      v-for="item in list"
      :key="item.cid"
    >
      {{ item.title }}
    </div>
  </div>
</template>

<script>
import Axios from 'axios';
export default {
  name: 'index',
  data () {
    return {
      list: []
    }
  },
  methods: {
    getData () {
      var api = 'https://zburu.com/api/posts.php';
      Axios.get(api).then((response) => {
        this.list = response.data;
        console.log(response.data)
      }).catch((error) => {
        console.log(error);
      })
    }
  },
  mounted () {
    this.getData();
  },
}
</script>

<style>
</style>

新闻:2022 年 7 月 8 日安倍晋三遭枪击

2022-07-08 08:00:00

2022 年 7 月 8 日,安倍晋三在街头发表演讲时遭枪击。据奈良市消防局称,遇袭的安倍晋三目前失去意识,心肺停止。嫌疑人 41 岁男子山上彻也(现住奈良市)随后被控制。

相关报道: https://www.sohu.com/a/565207100_121425542 https://new.qq.com/omn/20220708/20220708A03YIA00.html

好久没看到这么蓝的天

2022-07-02 08:00:00

手机像素一般,更主要的是拍照手法不行,拍不出优美的感觉。

obsidian:如何使用坚果云进行多端同步?

2022-06-27 08:00:00

接上篇文章 《关于我选择笔记系统这件小事》 ,obsidian 的个人体验很棒,我也打算长久使用作为个人知识库。

但是它有一个弊端,就是本地离线化导致同步是个问题,官方的同步服务很贵,给我劝退了,因此我寻求了其他的同步备份方案,就是今天我要分享的坚果云 + obsidian。

注册账号

坚果云官网注册一个账号,选择个人用户,创建账号之后登陆。

点击创建 > 个人同步文件夹,以后就同步这个文件夹内的内容了。这个我在里面新建了一个 test 文件夹。

1656308582812.png

pc 端同步

接下来需要将电脑本地的文件和坚果云的文件夹相连接,只要你在本地修改了文件,自动同步到坚果云。

点击页面左侧的下载客户端。

1656308627367.png

下载完成之后登陆,你就可以看到里面有一个 test 文件夹,是刚才在网页官网创建的。然后在右边三个点里面选择同步到本地。

1656308676194.png

1656308745284.png

浏览你的文件夹,找到一个地方存放 test 文件夹,点击确定,坚果云就会自动同步你在本地文件。但是这个时候 test 文件夹是空的,需要将 obsidian 文件全部移动进去就行了。然后在 obsidian 客户端重新打开 test 库。


在另一台电脑,进行上面同样的操作,将坚果云的同步文件夹映射到本地,就会自动下载云端的文件。之后,不管你在哪台电脑修改 obsidian 笔记,都会同步到拥有坚果云客户端的其他电脑。

这是 pc 端同步的方案,之后我会继续分享如果将 pc 同步到手机端。

人民币小写转大写

2022-06-24 08:00:00

一个有趣的 demo

this.smalltoBIG(43533534.78); //肆仟叁佰伍拾叁万叁仟伍佰叁拾肆元柒角捌分
	
smalltoBIG (n) {
  var fraction = ['角', '分'];
  var digit = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
  var unit = [
    ['元', '万', '亿'],['', '拾', '佰', '仟']
  ];
  var head = n < 0 ? '欠' : '';
  n = Math.abs(n);

  var s = '';

  for (var i = 0; i < fraction.length; i++) {
    s += (digit[Math.floor(n * 10 * Math.pow(10, i)) % 10] + fraction[i]).replace(/零./, '');
  }
  s = s || '整';
  n = Math.floor(n);

  for (var i = 0; i < unit[0].length && n > 0; i++) {
    var p = '';
    for (var j = 0; j < unit[1].length && n > 0; j++) {
      p = digit[n % 10] + unit[1][j] + p;
      n = Math.floor(n / 10);
    }
    s = p.replace(/(零.)*零$/, '').replace(/^$/, '零') + unit[0][i] + s;
  }
  return head + s.replace(/(零.)*零元/, '元').replace(/(零.)+/g, '零').replace(/^整$/, '零元整');
},

掘金抽奖脚本

2022-06-22 08:00:00

分享一个掘金抽奖的油猴脚本,可以一键签到,抽奖,梭哈。

攒多一点矿石,体验买彩票的感觉。

油猴脚本代码
```js // ==UserScript== // @name 掘金抽奖 // @namespace http://tampermonkey.net/ // @version 1.2.5 // @description 掘金抽奖 签到 免费抽奖 5 连抽 10 连抽 可视化抽奖 petite-vue // @author 无仙 // @match https://juejin.cn/* // @icon https://lf3-cdn-tos.bytescm.com/obj/static/xitu_juejin_web//static/favicons/favicon-32x32.png // @require https://unpkg.com/petite-vue // ==/UserScript==

(async function () { ‘use strict’;

const { createApp } = PetiteVue; // 不会吧不会吧,不会还有人不知道 petite-vue 吧

const root = document.createElement(‘div’); root.class = ‘wx_draw_wrap’; root.innerHTML = ` <div v-show="!popup" class=“wx_draw” @click=“open”>掘金抽奖

<div v-if="popup" class="wx_popup">
  <div class="wx_mask" @click="popup = false"></div>

  <div class="wx_main">
    <div class="wx_header">
      <div>掘金抽奖</div>
      <div class="wx_score">当前矿石:{{ score }}</div>
    </div>

    <div class="wx_body">
      <div class="wx_options">
        <div @click="check_in" v-if="check_status === -1 || check_status === false">签到</div>
        <div @click="get_free" v-else>签到成功</div>
        <div @click="draw(5)">5 连抽</div>
        <div @click="draw(10)">10 连抽</div>
        <div @click="draw(undefined)">梭哈抽奖</div>
      </div>

      <table cellpadding="0" cellspacing="0" border="0" width="100%">
        <thead>
          <tr>
            <th>奖品图片</th>
            <th>奖品名称</th>
            <th>中奖次数</th>
          </tr>
        </thead>
        <tbody>
          <tr v-for="item in award">
            <td><img :src="item.lottery_image"/></td>
            <td>{{ item.lottery_name }}</td>
            <td>{{ item.times }}</td>
          </tr>
        </tbody>
      </table>

      <div class="wx_loading" v-if="loading">
        <svg class="circular" viewBox="25 25 50 50">
          <circle class="path" cx="50" cy="50" r="20" fill="none" />
        </svg>
      </div>
    </div>

    <div class="wx_footer">
      <div class="wx_confirm wx_btn" @click="popup = false">关闭</div>
    </div>
  </div>
</div>

`;

// 查询奖品列表 const res = await fetch(‘https://api.juejin.cn/growth_api/v1/lottery_config/get', { headers: { cookie: document.cookie }, method: ‘GET’, credentials: ‘include’ }).then((res) => res.json());

const award = (res.data && res.data.lottery ? res.data.lottery : []).map((item) => ({ …item, times: 0 })); const { free_count, point_cost } = res.data; // 剩余免费抽奖次数,单次抽奖消耗数

document.body.appendChild(root); // 插入 DOM

// petite-vue init 初始化 createApp({ award, popup: false, loading: false, score: 0, free_count, check_status: -1,

async open() {
  const res = await fetch('https://api.juejin.cn/growth_api/v1/get_cur_point', {
    headers: {
      cookie: document.cookie
    },
    method: 'GET',
    credentials: 'include'
  }).then((res) => res.json());

  this.score = res.data; // 当前分数

  this.popup = true;

  (this.check_status === -1 || this.check_status === false) && this.get_status();
},
async draw(times, is_not_free = true) {
  if (this.loading || times === 0) return;

  // const is_not_free = !(this.free_count && times === 1);

  if (is_not_free && this.score < point_cost * (times || 1)) return alert('分都不够想啥呢?');

  let i = 0;
  const drawFn = async () => {
    if ((is_not_free && this.score < point_cost) || i === times) {
      this.free_count = 0;
      this.loading = false;
      this.open();
      console.log(`${times ? times + '连抽' : '梭哈'}结束!`);
      return;
    }

    const result = await fetch('https://api.juejin.cn/growth_api/v1/lottery/draw', {
      headers: {
        cookie: document.cookie
      },
      method: 'POST',
      credentials: 'include'
    }).then((res) => res.json());

    if (result.err_no !== 0) {
      console.log(result.err_msg);
      this.loading = false;
      this.open();
      return;
    }

    i++;
    is_not_free && (this.score -= point_cost);

    if (result.data.lottery_type === 1) this.score += 66;

    const item = this.award.find((item) => item.lottery_id === result.data.lottery_id);
    item.times++;

    console.log(`抽到:${result.data.lottery_name}`);
    drawFn();
  };

  console.log(`开始${times ? times + '连抽' : '梭哈'}!`);
  this.loading = true;
  this.award.forEach((item) => {
    item.times = 0;
  });
  try {
    drawFn();
  } catch (error) {
    this.loading = false;
    console.error(error);
  }
},
async check_in() {
  if (this.check_status) {
    this.get_free(); // 免费抽奖
    return;
  }

  // 签到
  const check_in = await fetch('https://api.juejin.cn/growth_api/v1/check_in', {
    headers: {
      cookie: document.cookie
    },
    method: 'POST',
    credentials: 'include'
  }).then((res) => res.json());

  if (check_in.err_no !== 0) {
    alert('签到失败!');
    this.check_status = false;
    return;
  }

  this.check_status = true;
  this.score = check_in.data.sum_point;
  this.get_free(); // 免费抽奖
},
async get_status() {
  // 查询签到状态
  const today_status = await fetch('https://api.juejin.cn/growth_api/v1/get_today_status', {
    headers: {
      cookie: document.cookie
    },
    method: 'GET',
    credentials: 'include'
  }).then((res) => res.json());

  this.check_status = today_status.data;
},
async get_free() {
  // 查询是否有免费抽奖次数
  const res = await fetch('https://api.juejin.cn/growth_api/v1/lottery_config/get', {
    headers: {
      cookie: document.cookie
    },
    method: 'GET',
    credentials: 'include'
  }).then((res) => res.json());

  this.free_count = res.data.free_count;

  if (res.data.free_count) {
    // 有免费抽奖次数
    this.draw(res.data.free_count, false);
  }
}

}).mount();

// 处理样式 const style = .wx_draw_wrap { box-sizing: border-box; position: fixed; top: 50%; left: 0px; z-index: 888888; margin-top: -20px; } .wx_draw { box-sizing: border-box; position: fixed; top: 50%; left: 0px; z-index: 888888; width: 40px; height: 40px; line-height: 16px; font-size: 12px; padding: 4px; background-color: rgb(232, 243, 255); border: 1px solid rgb(232, 243, 255); color: rgb(30, 128, 255); text-align: center; overflow: hidden; cursor: pointer; } .wx_popup { position: fixed; left: 0; top: 0; right: 0; bottom: 0; z-index: 999999; } .wx_mask { width: 100%; height: 100%; background: rgba(0,0,0,0.5); } .wx_main { --width: 460px; position: absolute; left: 50%; top: 50%; width: var(--width); transform: translate(-50%, -50%); background: #fff; border-radius: 4px; } .wx_main .wx_header { height: 40px; line-height: 40px; font-size: 16px; padding: 0 16px; border-bottom: 1px solid #999; display: flex; align-items: center; justify-content: space-between; color: #000; font-weight: 400; } .wx_score { font-size: 12px; font-size: #00a100; } .wx_main .wx_body { padding: 16px; border-bottom: 1px solid #999; position: relative; } .wx_main .wx_options { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; } .wx_main .wx_options div { width: 80px; text-align: center; height: 24px; line-height: 24px; background-color: rgb(232, 243, 255); border: 1px solid #c9d4e3; color: rgb(30, 128, 255); cursor: pointer; border-radius: 2px; } .wx_main .wx_body p { margin: 0 0 8px; } .wx_body table { width: 100%; text-align: center; border-left: 1px solid #ccc; border-top: 1px solid #ccc; } .wx_body table th, .wx_body table td { border-right: 1px solid #ccc; border-bottom: 1px solid #ccc; line-height: 20px; } .wx_body table th { line-height: 28px; } .wx_main .wx_body img { vertical-align: middle; width: 40px; height: 40px; } .wx_main .wx_footer { padding: 12px 16px; text-align: right; } .wx_btn { display: inline-block; width: 48px; cursor: pointer; text-align: center; height: 20px; line-height: 20px; background-color: rgb(232, 243, 255); border: 1px solid #c9d4e3; color: rgb(30, 128, 255); border-radius: 2px; } .wx_loading { position: absolute; left: 0; top: 0; right: 0; bottom: 0; z-index: 9999999; background: rgba(0,0,0,0.65); } .wx_loading .circular { height: 42px; width: 42px; -webkit-animation: loading-rotate 2s linear infinite; animation: loading-rotate 2s linear infinite; position: absolute; left: 50%; top: 50%; margin-top: -21px; margin-left: -21px; } .wx_loading .path { -webkit-animation: loading-dash 1.5s ease-in-out infinite; animation: loading-dash 1.5s ease-in-out infinite; stroke-dasharray: 90, 150; stroke-dashoffset: 0; stroke-width: 2; stroke: #409eff; stroke-linecap: round; } @keyframes loading-rotate { 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } } @keyframes loading-dash { 0% { stroke-dasharray: 1, 200; stroke-dashoffset: 0; } 50% { stroke-dasharray: 90, 150; stroke-dashoffset: -40px; } 100% { stroke-dasharray: 90, 150; stroke-dashoffset: -120px; } };

const styleEl = document.createElement(‘style’); styleEl.textContent = style; document.head.appendChild(styleEl); })();

</details>
</summary>

usb 接口的三面性

2022-06-21 08:00:00

传统的 usb 接口是一个神奇的物品,当你插入第一次的时候肯定是反方向的,所以你要反过来插第二次,但是结果仍然是插不进去。这个时候你就会发现,它存在第三面,所以当你再次反过来的时候就可以正常插进接口了。

这就是 usb 接口的三面性 😐。

所以为了保护精神和心理不受伤害,请离开传统的 usb,投入 type-c 的怀抱吧!

无题

2022-06-18 08:00:00

当情绪破防的时候, 任何事情都是最后一根稻草。


尘埃落定。

—2022/06/23

随想录 (6)

2022-06-10 08:00:00

随着年龄的增长,越发感觉自我想象力的缺失。

最近迷上了科幻小说,遂找到刘慈欣的小说集,大概二十多篇,花上一周时间研读完毕,只有一种惊艳的感觉,不论是文笔还是世界观架构,都令我着迷。

而大刘作为理工生,将充满浪漫色彩的想象力和严谨的物理学知识相结合,我很佩服。

诗云中对宇宙艺术的理解,是我最喜欢的一篇,文明发展到最后,生存就已经变成了婴孩时期的啼哭,除了怀念已经没有意义了,要追求更高层次的满足,唯有艺术感。

对艺术的追求已经强烈到毁灭银河系,简单粗暴而又充满艺术感。

与诗云一样的还有低温艺术家,描述群体文明的终点是个体文明,也同样没有了生存的追求,整篇充满了艺术彩色。但是对于低层次的文明,生存的追求还是极其必要的。

大刘的文笔是非常浪漫的,将残酷的宇宙以温柔的笔墨书写,而又相对严谨的科学知识,都令我着迷。

typecho 评论回复艾特评论人

2022-06-07 08:00:00

typecho 在评论时默认是没有 @ 评论人的功能,可以用代码加一下。

functions.php 中:

/*
* 评论回复时 @ 评论人
*/
function get_comment_at($coid)
{
    $db   = Typecho_Db::get();
    $prow = $db->fetchRow($db->select('parent,status')->from('table.comments')
        ->where('coid = ?', $coid));
    $mail = "";
    $parent = @$prow['parent'];
    if ($parent != "0") {
        $arow = $db->fetchRow($db->select('author,status,mail')->from('table.comments')
            ->where('coid = ?', $parent));
        @$author = @$arow['author'];
        $mail = @$arow['mail'];
        if(@$author && $arow['status'] == "approved"){
            if (@$prow['status'] == "waiting"){
                echo '<p class="commentReview">(评论审核中))</p>';
            }
            echo '<a href="#comment-' . $parent . '">@' . $author . '</a>';
        }else{
            if (@$prow['status'] == "waiting"){
                echo '<p class="commentReview">(评论审核中))</p>';
            }else{
                echo '';
            }
        }

    } else {
        if (@$prow['status'] == "waiting"){
            echo '<p class="commentReview">(评论审核中))</p>';
        }else{
            echo '';
        }
    }
}

然后在 comments.php 中输出评论内容代码的前面加上:

<?php $parentMail = get_comment_at($comments->coid)?><?php echo $parentMail;?>

▼代码所示 1654569311759.png

▼效果图 1654569512094.png


我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=17ctk6evsjk5b

随想录 (5)

2022-06-02 08:00:00

抽空看完了爱死机第三季,个人感受比第二季优秀,但是还是不如第一季。太过追求三者的结合,科幻与死亡的元素,不够灵活。

但是其中的剧情还是比较精彩的,比如邪神那一集,估计只有了解克苏鲁的人才能看懂,结局也比较贴合剧情走向。女子挖去了眼睛和耳朵,听不到邪神的喃语,才免受精神污染,活着走出危机。(2022/05/26)


bootcss4 是一个轻量前端 ui 框架,最近的项目中用到了它,使用体验还不错,不过功能倒是与 element 相比弱了一些(2022/05/27)


今天运气不知是好好坏,刚下地铁,就下了毛毛雨。然后一路上四个路口全是绿灯,一口气骑回家,虽然我也穿了雨披,不过还是为自己的运气感到骄傲,很少碰到连续四个绿灯的情况。看来老天都不想让我淋雨,刚在小区停好车,就下大了。(2022/05/31)


最近又步入了雨季,已经记不清这是杭州第几次了,感觉全年都处于下雨天。闷热加潮湿,基本在屋里呆着都要开空调和风扇,不然感觉不透气。所以吃完饭后我喜欢到小区楼下走走,很凉快空气也很清新。(2022/06/02)

宝塔自动备份网站到邮箱教程

2022-05-25 08:00:00

前言

网站经常备份好处就是遇到问题以后可以迅速的恢复,不那么被动,防止信息丢失,操作性很大。虽然搭建一个网站非常容易,但为了应对网络上各种各样的意外情况,避免自己的网站付诸东流,做好网站备份是非常有必要的。

现在邮箱接收都支持附件文件,将网站文件和数据库文件备份到我们自己的邮箱也是一种很好地备份方式,也防止备份文件丢失。

邮箱接收附件文件大小,取决于使用的邮箱服务。一般来说个人博客网站源文件,数据库文件不大,常见邮箱服务都是可以使用的。

下面开始教程,宝塔安装教程可去宝塔官网查看安装教程。

系统环境:CentOS 7.9.2009 x86_64(其他系统可参考方式,命令安装自行解决)

宝塔面板:7.9.0

安装 mailx

浏览器登录进入宝塔面板,左侧-》终端。首次使用宝塔终端,需要登录服务器,填写 root 用户密码后登录。

13-1.webp

在终端中输入以下 mailx 安装命令,回车安装。

yum install -y mailx

13-2.webp

编辑配置文件

安装完成后,编辑邮箱服务的相关配置文件。 在终端输入以下命令,然后回车进入编辑文件界面。

vim /etc/mail.rc

13-3.webp

使用键盘上下键,找到大约 69 行位置,按‘i’键输入,添加以下配置。

set from=发件人@qq.com #发送邮件后显示的邮件发送方 需要修改
set smtp=smtps://smtp.qq.com:465 #SMTP服务器的网址及端口 需要修改
set smtp-auth-user=发件人@qq.com #发送邮箱 需要修改
set smtp-auth-password=邮箱密码/SMTP授权码 #设置的邮箱密码,SMTP授权密码 需要修改
set smtp-auth=login #动作、登录 无需修改
set ssl-verify=ignore #ssl验证忽略 无需修改
set nss-config-dir=/etc/pki/nssdb #证书存放目录 无需修改

请将配置按要求修改为自己所使用的邮箱服务配置。服务器需要开启 465 端口。

13-4.webp

13-5.webp

配置修改完成后,按 Esc 键,输入:wq回车保存退出编辑界面。

测试发送邮件

在终端输入发送测试邮件命令,需要修改收件人邮箱,回车执行。

echo "这是一封测试邮件" | mail -s "邮件主题" 收件人@qq.com

13-6.webp

查看邮箱是否收到测试邮件。

13-7.webp

如果发现终端报以下错误(但没有影响收件),没有错误请跳过。

Error in certificate: Peer's certificate issuer has been marked as not trusted by the.

13-8.webp

Ctrl+C结束命令后,修改邮箱服务配置文件,将set ssl-verify=ignore修改为set ssl-verify=strict后保存退出(编辑方法同上)。

13-9.webp

修改完成后在终端输入以下命令。

获取邮件服务器证书

echo   -n " " |  openssl s_client -connect smtp.qq.com:465 | sed -ne  '/-BEGIN CERTIFICATE-/,/-END CERTIFIICATE-/p'  >  /etc/pki/nssdb/qq.crt

把证书添加到受信任表

certutil    -A    -n   'yeah'    -t    "P,P,P"    -d    /etc/pki/nssdb    -i    /etc/pki/nssdb/qq.crt

注:如果是 163 或者其他邮箱服务器请将smtp.qq.com改为smtp.163.comqq.crt改为163.crt 以此类推。

命令执行完成后,重新执行发送测试邮件命令,不在报错。

13-10.webp

宝塔定时任务

左侧-》计划任务

首先按自己需求(计划任务执行时间,保留文件数量等)添加网站和数据库源文件备份任务。

13-11.webp

然后添加shell脚本任务,在脚本内容中添加以下脚本命令,修改收件人为自己的邮箱。

#!/bin/bash
cd /www/backup/site
for file in $(ls *)
do
  str="${str} -a ${file}"
done
echo "主人,今天的网站备份又到了哦,请注意查收!" | mail -s "网站备份" $str [email protected]

13-12.webp

执行时间可按需求,晚于网站,数据库备份任务执行时间。

数据库备份和网站备份一样需要再添加一次计划任务,将cd /www/backup/site 修改为cd /www/backup/database数据库备份文件所在目录即可,参考改一下邮件主题,和内容信息。

计划任务添加完成后,可先执行网站,数据库源文件备份任务,再执行网站,数据库邮箱shell脚本备份任务,然后前往邮箱查看文件是否收到,文件过大,邮件会有延迟。

13-13.webp

13-14.webp

注:如果有报错或收不到邮件情况,请仔细检查命令,配置文件是否有误,或者查看脚本执行日志进行排查。

转载自梦繁星博客:https://blog.emoao.com/13.html

如果问题请在评论区留言。

一个优雅的字体压缩解决方案 fonttools

2022-05-24 08:00:00

分享一个 python 的库 —fonttools,针对字体文件进行处理非常方便。

下载

需要先在电脑上下载 python 环境,这个就不多说了。

然后在本地命令行输入:

pip install fonttools

字体库

  1. 本地新建一个文件夹,取名为 fonts ,随意即可。将自己需要压缩整理的字体放在文件夹中,如 OPPOSans.ttf
  2. 然后建立一个文件取名为 word.txt,这时我们需要找到一个常用字体库,这里我在 github 分享一个 https://github.com/dlzmoe/cdn/blob/main/font/汉字常用字体.txt ,把里面的内容常用字体复制到 word.txt 文件。

运行

pyftsubset OPPOSans.ttf --text=$(cat word.txt) --no-hinting

我们看一下命令,其中 OPPOSans.ttf 是字体文件,word.txt 是常用字体,我们要将这些字单独分离出来。

名字都可以按照自己的要求来,但是一定要互相对应。

在命令行输入即可,运行完成之后,可以在文件夹中看到带有 subset 字样的字体文件,占用空间大大缩小,可以直接拿来使用。

随想录 (4)

2022-05-13 08:00:00

好事和坏事总是接踵而至,你永远不知道下一秒会发生什么。但是总有解决的办法,不是吗?但实际上我也不确定。

昨天很晚才睡,想了很多,乱七八糟的。生活哪有容易的事情,就跟摸石头过河一样,跌跌拌拌,最后才找到适合自己生活的模式。(2020/05/09)


coding 构建部署同步 github 好像有 bug,搞了半天最后重新刷新 GitHub.com OAuth 认证就好了。(2020/05/10)


昨天一整天都是雨天,我从地铁出来时,雨是朦朦胧胧的,跟雾一样。索性就直接骑着车了,迎面而来的冷空气让我打了个冷战,疲惫的身体清醒了许多。

向前骑去时看到一对情侣,也是晃晃悠悠的骑着电车,女生侧坐着靠在男生的背上。一瞬间有些恍惚,好像是我羡慕的场景。带着心爱的女孩漫无目的的漂游,或许她可以在后座一边搂着我的腰,一边听我讲述生活遇到的趣事或者烦恼。 (2020/05/11)


这两天读了一本新书《菜根谭》,作者是明代洪应明,内容是儒家通俗读物,采儒、佛、道三家思想。

以心学、禅学为核心,拥有修身、齐家、治国、平天下等大道,同时由于它融处世哲学,生活艺术,审美情趣这些特色,也是一部使人奋发向上的中国文学作品。

挺有意思,说是鸡汤又不太像。如果对网络文学读腻的朋友,可以试着看一看。通篇为五言,也不显得臃肿。

建议在微信读书阅读,可以用无限卡免费。

拨开世尘氛,消却心鄙吝。拨开世尘氛,消却心鄙吝。拨开世尘氛,消却心鄙吝….

(2020/05/13)

分享一款自用的 v2ex 主题样式

2022-05-12 08:00:00

起因是因为觉得 v2ex 网站主题略有拥挤,阅读体验不好,而正好网站支持自定义 css,所以就在参考其他人的基础上,修改了一套比较美观的样式。

本来是自用的,但是看到有人发帖分享主题,我也索性将它分享出来了。

v2ex 帖子:https://www.v2ex.com/t/851735

Github 仓库:https://github.com/dlzmoe/v2ex_theme

不想看文档可以直接用下面的链接。

/* 通过 jsd 加速 github 文件 */
@import "https://cdn.jsdelivr.net/gh/dlzmoe/v2ex_theme/v2ex_theme.min.css";

/* 放在我自己服务器上的链接 */
@import "https://cdn.zburu.com/list/v2ex_theme.css";

随想录 (3)

2022-05-06 08:00:00

五一假期回老家处理一些事情,收拾东西的时候,从角落里翻出来几张老照片,很有怀念意义。(2022/05/03)


好事和坏事总是接踵而至,你永远不知道下一秒会发生什么。但是总有解决的办法,不是吗?(2022/05/05)


整理了一下个人书单,发现读过的书太少了,打算做一个阅读计划,保证持续阅读。

ps: 这周碎碎念很少,因为五一有事出去了,身体和心灵都没停下来,有些疲惫。(2022/05/06)

docker 部署思源笔记

2022-05-05 08:00:00

文章废弃

部署

docker run --name siyuan -itd -p 6806:6806 -v /opt/my_siyuan_path:/opt/siyuan --restart always b3log/siyuan

备份

sudo su

# 查看容器列表
docker ps -a

# 制作备份
docker commit -p 7409ce6aae7b siyuan_backup
# 其中 403e6db0c 是容器id, jenkins_backup是备份名称

# 查看备份是否成功
docker images

# 将镜像制作成文件 docker save -o [filename] [image]
docker save -o siyuan_backup.tar siyuan_backup
ls

# 在 /home/ubuntu/ 中可以看到制作后的压缩包

随想录 (2)

2022-04-29 08:00:00

今天醒的很早,原来以为是失眠,结果是早起,这两天发生的有点多,导致情绪都有些低落,精神萎靡。希望风波赶紧过去,太影响生活了。

(2022/04/25)


很喜欢雨后的阴天,骑着车,微凉的空气扑面而来,清新而舒适,本来早上有些困倦的大脑清醒了很多。

可惜时间很短暂,中午又迎来了焦灼的烈日。

(2022/04/26)


已经连续五天的核酸了,前段时间我还立了个 flag,疫情三年仅仅做过两次核酸,结果这一个月就翻了几倍。按照接下来的发展,这种生活还将持续很长时间。

(2022/04/27)


感觉最近停下了学习的步伐,看着零散的知识和技术文档,却又不知道从何下手,那么就开始整理吧,让坚持成为习惯。

(2022/04/28)

关于我选择笔记系统这件小事

2022-04-28 08:00:00

受年龄增长以及生活影响,人接触到的新事物和知识将会越来越多,大脑就显得有些混乱了,我也不敢保证自己的思维会始终保持一个高强度的在线状态。

因此决定整理个人知识库,将大脑中的片段和思想整理出来。以笔记和文字的形式将他记录下来,但是最近一直在寻求这样的工具,多方对比,也没有一款能真正满足我的需求的工具。

最好是自建的笔记,因为我今天在语雀写了一篇私密笔记,或许因为内容略带敏感吧,然后就被审查了,说我违规。

语雀这是我目前最满意的工具,但是现在对他的热情瞬间降低了很多。我怕哪天一下子账号里面的笔记全部被删了,那样我会很绝望的。

接下来我还会多方对比,最终选择一款平台亦或者是自建的笔记系统。

博客是写给别人看的,笔记是给自己看的,包括一些计划,列表清单等等,都是隐私的内容。还有就是一些文章的草稿,碎片化的记录,所以不宜放在博客上。

而且对于功能方面,我也有较高的要求,比如拖拽,表单,下拉框,卡片等等,都是我比较需要的,隐私和安全性更是我比较看重的。

暂时就这样吧,我会慢慢寻觅。


2022/05/03 更新

经过为期一周的尝试和部署过后,我最终决定了使用思源笔记,可以使用 docker 进行私有部署,本地备份,而且还可以选择付费订阅,备份到官方,这是两种可以同时进行的选择,我觉得很好的满足了我的需要。

主要还是思源的功能以及外表打动了我,页面简洁而美,功能不臃肿,刚好我需要的他都有,一点也没有多余的按键。符合我对笔记和个人知识库的个人,支持双链,不过我暂时用不到。支持 markdown 格式导入导出。

而我也逐步将我其他的笔记都迁移过来了,不过目前唯一的缺陷好像不支持分享,这也算我某种需求吧,我在寻求其解决方案,不过也不算什么大问题。

思源很多细节都打动了我,在单个文章复制的时候,提供了直接复制到其他平台的格式,不用再手动调了。

至于安全性方面,可以设置全局鉴权密码,不支持单篇文章加密。


2022/05/18 更新

半个月过去了,慢慢从思源迁移出来,原因很多,一方面是针对文件的安全性和稳定性抱有一丝怀疑,另外就是同步的问题,成本和精力都有些费事。

因此我选择了一个更不错的软件—obsidian,强大的社区,海量的插件以及可扩展性都是我选择它的原因。

对比之下,它更像是笔记界的 vscode,优势很多,轻量,可扩展,插件多。同时使用坚果云进行多端同步,这样不论是在公司电脑还是在自己的电脑上,我都可以做到无缝切换。


2022/06/18 更新

使用了一个月的 obsidain,反馈一下感受。整体来说很适合我这种记录文字的人,对 markdown 语法支持特别友好,格式都可以根据自己的想法展示出来,还带有一些插件,扩展我的使用方法。

在安全性方面,本地化、离线化也是非常棒的,可以根据自己的需要利用同步盘进行同步,或者自建 webdav 服务,稳定性也是很高的。

在数据迁移方面,完全不用担心,数据本质上还是 markdown 文件,如果哪有你对 obsidian 失望不再使用,或者有了更好的选择,可以直接把文件夹复制过去,都是很方便的。

但是这也只针对纯文件来说,如果是元数据,带有一些属性类的东西时,obsidain 就不是很好的选择了。正因为它是一个纯文件,所以注定没法保存元数据信息,这方面我和群里的阿均作了深刻的探讨。他比较注重“属性,标签,双链的指向,关系链,数值大小,层级隶属”等概念。

obsidian 是一堆小文件集合在一起形成的笔记架构,这和一般的数据库不太一样。

小文件很难做数据管理,反正长期看性能方面会有问题。一致性,也有问题,不重新索引确保不了一致性,重新索引,对于海量小文件,又难以提高性能。

阿均的担忧我也是能理解的,不过我们对笔记的定位有所不同。他更希望打造一个碎片化,利用双链结构形成自己的知识架构;而我是需要一个类 wiki 的东西,将我所获取到的知识按照分类进行归档,然后根据关键词,标签,标题,正文等内容进行搜索查询。

这是不同的方向,所以选择了不同的软件也是很正常的。

浏览器收藏夹一键填写博客评论信息

2022-04-26 08:00:00

先点击收藏一个页面,然后右键它,选择修改,在网址输入框复制下面的 js 代码,就可以一键填入了。

将其中的昵称,邮箱,网址修改成自己的信息。

目前支持绝大多数主流博客平台和系统,如果发现没有起作用,可以在下面代码里面修改增加一下 input 的属性。

如果你不会修改的话,可以在评论区留言没有起作用的网站,我可以更新代码。

javascript:void function(){var lauthor=["#author","input[name='comname']","#inpName","input[name='author']","#ds-dialog-name","input[name='wc_name']", "input[name='nick']",],lmail=["#mail","#email","input[name='commail']","#inpEmail","input[name='email']","#ds-dialog-email","input[name='wc_email']","input[name='mail']",],lurl=["#url","input[name='comurl']","#inpHomePage","#ds-dialog-url","input[name='url']","input[name='wc_website']","input[name='link']",];for(i=0;i<lauthor.length;i++){var author=document.querySelector(lauthor[i]);if(author!=null){author.value='子舒';break}}for(j=0;j<lmail.length;j++){var mail=document.querySelector(lmail[j]);if(mail!=null){mail.value='[email protected]';break}}for(k=0;k<lurl.length;k++){var url=document.querySelector(lurl[k]);if(url!=null){url.value='https://zishu.me';break}}return!1}()

随想录 (1)

2022-04-24 08:00:00

正式启动一周碎碎念计划,起因是无意中发现了一个网站,古天乐的博客 。看完之后震撼了,虽然每天都是很简短的一句话,但是一个习惯坚持了 12 年。

因为我决定仿效他的做法,看看自己是否能坚持足够长的时间。碎片化一些记录,然后通过语雀小记进行记录,最后在每周最后一天工作日总结一下,发布到博客。

(2022/04/20)


很遗憾,之前准备连载的小说,正式宣布烂尾,反思一下问题所在。没有在最可是写好一个大纲,统筹布局,都是一边构思剧情一边写,导致在后来越写越混乱。

地址:https://www.yuque.com/docs/share/8d5456ac-4600-4890-a251-b7648bf48115?# 《夜幕》

(2022/04/21)


千万不要在深夜冲动消费,下了一单又一单,最后又全部取消了,是什么让我克制住了呢?是口袋!

(2022/04/22)

在宝塔中使用青龙面板代挂京豆

2022-04-15 08:00:00

如题所述,想要在闲置的服务器中跑一些服务,青龙面板代挂京豆现存很多开源的脚本,安全性已经经过很多人验证,可以放心使用。

因此我也安排了一个脚本,成功部署之后,就写这篇不算教程的笔记来记录一下整个过程。

Github: https://github.com/6dylan6/jdpro 作者会更新一些部署的指令,如果安装不成功可以对照文档中的指令。

拉取镜像

我是在宝塔中运行服务的,也是非常简单方便。至于怎么安装宝塔,这里就不介绍了,之前写过一篇关于部署宝塔面板的教程,可以点击站内搜索。

如何安装宝塔面板?

需要买服务器可以 点击链接在腾讯云购买服务器有优惠哦~ 不嫖白不嫖,省个十几二十块钱不香吗?


先在软件商店中下载安装一个 Docker管理器 ,之后点击 设置 > 镜像管理 > 镜像获取

然后输入 qinglong 进行搜索,选择第一个链接然后拉取。

创建容器

点击创建容器。

镜像就选择刚才拉取的那个,端口映射中的容器端口和服务器端口都填入 5700,选择开机自启动,其他的都不用填,点击提交。

服务器的防火墙放行 5700 端口。

部署脚本

在浏览器输入 ip:5700 ,ip 地址就是你的服务器 ip,会弹出一个青龙面板初始化配置,按照提示点击安装即可,没有特别复杂的。

都是用户名密码通知设置等,我选择的是邮箱通知。

安装完成后,在首页右上角点击新建任务,里面的内容直接复制即可。

ql repo https://js.dayplus.xyz/https://github.com/6dylan6/jdpro.git "jd_|jx_|jddj_" "backUp" "^jd[^_]|USER|JD|function|sendNotify"
0 0 0 * * *

点击确定即可。

完成之后,什么都先不要点。

环境变量

接下来来到面板左侧导航的 环境变量,点击新建变量,这里就需要京东账号的 cookie 值了,浏览器打开 m.jd.com ,登录账号,最后调成手机模式。

然后在右侧找到 network ,点击搜索 js 文件,找到前缀为 biz.json?..... 的文件,右侧会显示一个 cookie

找到其中的 pt_keypt_pin ,很长看仔细。一起复制到新建变量的值里面,上面的名称输入 JD_COOKIE,最后点击确定。

运行

最后回到定时任务,在分页的最后一页,找到最开始的那个脚本,单击允许,就会一直转圈在运行中,等到运行结束之后,选择禁用它,以后会始终保定时运行的。

最后为了安全起见,把前面的脚本中带有 加密 二字的任务全部禁用。

到这里就结束了,基本每天可以跑个 100-200 左右的豆子,虽然少,时间长了也很可观,以后买东西可以抵扣,心中暗爽….

2022 清明出游

2022-04-07 08:00:00

清明过去两天了,由于出去玩了两天,身体很累等原因,再加上工作忙碌所以没有来得及记录。今天略有闲时间,整理一下随笔。

爬山—杭州半山森林公园

相比于西湖等景点,半山已经算是人非常少的呢,因为疫情原因也不敢去外地太远的地方,万一给隔离就完蛋了,所以就在市内景点挑人少没去过的地方玩了一下。


西溪—花朝节

“迟日江山丽,春风花草香。” 一年一度的西溪花朝节,虽然说平时也都经常去,但是借着这个假期和女朋友出来闲逛,也挺舒服,刚好也不热穿着卫衣,漫步在花丛中,别有一番风味。


朋友的猫

在 nuxt 中实现图片放大预览功能

2022-04-01 08:00:00

v-viewer 基于 vue 的一个插件,可以实现图片放大,缩小,旋转,拖拽,预览等各种功能,效果还是很棒的。这是我在 Github 摸鱼时发现的一个仓库,感觉很有用,就分享一下使用过程。

Github: https://github.com/mirari/v-viewer

1.安装

npm install v-viewer

2.用法

plugins 中新建 viewer.js 文件。

// /plugins/viewer.js

import Vue from 'vue';
import Viewer from 'v-viewer'
import 'viewerjs/dist/viewer.css'
Vue.use(Viewer)
Viewer.setDefaults({
  Options: { 'inline': true, 'button': true, 'navbar': true, 'title': true, 'toolbar': true, 'tooltip': true, 'movable': true, 'zoomable': true, 'rotatable': true, 'scalable': true, 'transition': true, 'fullscreen': true, 'keyboard': true, 'url': 'data-source' }
})

然后再 nuxt.config.js 中引入。

// nuxt.config.js

plugins: [
	// ...
	{ src: '@/plugins/viewer', ssr: false }
],

最后在使用的页面中引入。

<div class="markdown-body-box" v-viewer v-highlight>
<!-- 页面内容 -->
</div>

在任意页面中引入,可以是一个小组件页面,也可以是根页面,取决于你想在哪里引入这个功能,只要将class, v-viewer, v-highlight 这三个参数引入即可。

在 vue 中制作 canvas 波浪图

2022-03-29 08:00:00

新建组件 Canvas.vue

// @/components/Canvas.vue

<template>
  <div :style="{ height: waveAllHeight + 'px', background: bgColor }" class="wave">
    <canvas id="wave1"></canvas>
    <canvas id="wave2"></canvas>
    <canvas id="wave3"></canvas>
  </div>
</template>

<script>
export default {
  name: "Canvas",
  props: {
    bgColor: {
      default: "none",
    }, // 背景色
    waveAllHeight: { default: 160 }, // 波浪的整体高度
    waveCount: { default: 2 }, // 波峰个数
    waveHeight: { default: 50 }, // 波浪起伏高度
    // 波浪颜色
    waveColor: {
      default () {
        return [
          "#f3f3f3",
          "#f3f3f3",
          "#f3f3f3",
        ];
      },
    },
    // 波浪速率
    waveTime: {
      default () {
        return [4000, 4000, 4000];
      },
    },
  },
  data () {
    return {};
  },
  mounted () {
    this.wavePlay("wave1", 140, this.waveColor[0], this.waveTime[0]);
    this.wavePlay("wave2", 140, this.waveColor[1], this.waveTime[1]);
    this.wavePlay("wave3", 140, this.waveColor[2], this.waveTime[2]);
  },
  methods: {
    wavePlay ($canvasID, $progress, $maveColor, $time) {
      const that = this;
      let waveWidth = 3300, // 波浪长度
        offset = 0,
        waveHeight = that.waveHeight, // 波浪起伏高度
        waveCount = that.waveCount, // 波浪个数
        startX = -1200,
        startY = 212, // canvas 高度
        progress = $progress, // 波浪位置高度
        d2 = waveWidth / waveCount, // 单个波浪的宽度
        d = d2 / 2,
        hd = d / 2,
        c = document.getElementById($canvasID),
        ctx = c.getContext("2d");
      c.width = 1920; // 画布宽度
      c.height = that.waveAllHeight; // 画布高度
      function move () {
        offset -= 5;
        if (-1 * offset === d2) {
          offset = 0;
        }
        ctx.clearRect(0, 0, c.width, c.height);
        ctx.fillStyle = $maveColor; // 画布填充色
        ctx.beginPath();
        let offsetY = startY - progress;
        // 绘制贝塞尔曲线
        ctx.moveTo(startX - offset, offsetY); // 开始点
        for (let i = 0; i < waveCount; i++) {
          let dx = i * d2;
          let offsetX = dx + startX - offset;
          ctx.quadraticCurveTo(
            offsetX + hd,
            offsetY + waveHeight,
            offsetX + d,
            offsetY
          );
          ctx.quadraticCurveTo(
            offsetX + hd + d,
            offsetY - waveHeight,
            offsetX + d2,
            offsetY
          );
        }
        ctx.lineTo(startX + waveWidth, 3000);
        ctx.lineTo(startX, 0);
        ctx.fill();
        setTimeout(move, $time / 60); // 速度
      }

      move();
    },
  },
};
</script>

<style scoped lang="scss">
.wave {
  width: 100%;
  height: 100%;
  position: relative;
  top: 0;
  left: 0;
  margin-top: -8%;
  canvas {
    width: 100%;
    opacity: 1;
    position: absolute;
    top: 0;
    left: 0;
  }
}
@media (max-width: 900px) {
  .wave {
    display: none;
  }
}
</style>

增加了站内搜索功能

2022-03-25 08:00:00

如题,我花了一个多小时,参照 nuxt 文档,把搜索功能给加上了,记录一下相关代码。

可以选择放在 components/<Search /> 里面,然后引入到 head 模板,也可以单独创建一个页面,我选择的是后者。

新建一个页面模板 /pages/search.vue

<template>
  <div>
    <HeaderMe />
    <div class="wrapper">
      <PageSidebar />
      <div class="archive">
        <h2>{{ article.attributes.title }}</h2>
        <div class="article-content markdown-body" v-html="article.html"></div>
        <div class="search">
          <input v-model="searchQuery" type="search" autocomplete="off" placeholder="文章关键词" />
          <ul v-if="articles.length">
            <li v-for="article of articles" :key="article.slug">
              <NuxtLink :to="article.path+'/'">{{ article.title }}</NuxtLink>
               <!-- 在url地址后加一个/配置全站的url格式防止错乱 -->
            </li>
          </ul>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import HeaderMe from "@/components/HeaderMe";
import PageSidebar from '@/components/PageSidebar';
export default {
  components: {
    HeaderMe,
    PageSidebar
  },
  data () {
    return {
      searchQuery: '',
      articles: []
    }
  },
  async asyncData () {
    // 调用 search.md 内的数据
    const article = await import(`~/content/search.md`);
    return {
      article
    };
  },
  watch: {
    async searchQuery (searchQuery) {
      if (!searchQuery) {
        this.articles = []
        return
      }
      this.articles = await this.$content('posts') // posts 是文章所在的文件夹的名称
        .limit(10)
        .search(searchQuery)
        .fetch()
    }
  }
}
</script>

关于样式的编写不怎么重要我就放下面了。

css 代码
.wrapper {
  min-height: 300px;
  .archive {
    padding: 25px 2% 15px;
    .article-content {
      font-size: inherit;
      line-height: 1.8;
      color: inherit;
      margin-top: 20px;
    }
    .search {
      margin-top: 16px;
      input {
        width: 100%;
        background-color: #edf2f7;
        color: #2f495e;
        outline: none;
        border: none;
        border-radius: 30px;
        padding: 4px 20px;
        box-sizing: border-box;
        transition: all 0.1s linear;
        &:focus {
          box-shadow: 3px 2px 10px rgb(0 0 0 / 20%);
        }
      }
      ul {
        margin-top: 6px;
        list-style: none;
        li {
          a {
            display: block;
            padding: 6px 14px;
            line-height: 1.6;
            transition: all 0.2s linear;
            &:hover {
              background: #edf2f7;
            }
          }
        }
      }
    }
  }
}
@media (max-width: 520px) {
  .wrapper {
    .archive {
      .search {
        ul {
          li {
            a {
              border-bottom: 1px solid #ddd;
            }
          }
        }
      }
    }
  }
}

sql 中时间戳转日期

2022-03-10 08:00:00

需求:我将博客和 typecho 后台结合起来,打算做一个在线说说的功能,在 typecho 中输入内容,然后调用接口,实现在我的博客查看说说功能的功能。是不是有点绕?我也这么觉得,但是折腾一下也挺好的。

typecho 导出的数据默认是时间戳格式,那我在前端调用的时候就很麻烦,所以选择在 sql 查询时直接转换,created 是表里面的参数。

// 时间转换语句
FROM_UNIXTIME(created)
// 数据库查询语句
$sql = "select FROM_UNIXTIME(created),text from ... order by created desc";

这里导出的数据就是下面这个样子。

所以使用 as 参数将前面的语句自定义一下。

$sql = "select FROM_UNIXTIME(created) as created,text from ... order by created desc";

成功导出,接下来调用就很简单了。

多谢梦繁星的指导。

时隔六年我终于换电脑了

2022-03-03 08:00:00

迫于我之前的电脑已经服役了六年,还是高考结束后购买的,一直陪伴我度过了整个青春 ?。大学时写论文,作业都是通过它。

但是最近越来越严重,主要是屏幕的色域出现了一些问题,大面积发白,很伤眼睛。

其次显卡配置等,虽然凑合一下还能用,但也是很多年前的产物了。显卡、cpu 也是不能满足我目前的使用了(主要是现在的游戏太吃配置了),综合考虑我决定让它退休?。然后在我精挑细选下,选择了惠普暗影精灵 7。

我计划是 6000-7000 之间价位,所以一些高价低配的办公本我直接放弃,主要看的还是游戏本。不要听这个名字,综合来说同等价位的游戏本性能普遍高于办公本,对于我的要求来说是这样。除了体积稍大一些,笨重之外,也没有其他缺点了。

有了预算之后,就可以看品牌了,由于是笔记本,一体的还是买品牌比较稳,小厂商品控和售后不敢保证。我看重的大概有惠普,联想,宏碁这个牌子,里面的暗影精灵,拯救者,暗影骑士等都是性价比相当不错的选择。在反复纠结之后我选择了配有 RTX3050ti 显卡的暗影精灵 7。在知乎上看了一些网友的评价,感觉都差不多,然后我就下单了,最主要还是要上机体验一下才知道合不合适。

具体配置:


京东的效率还是很高的,第二天电脑就到了,也是迫不及待的打开。

配置清单 外观

看了这台电脑心中还是非常满意的,检查一下外观没有问题,屏幕正常,然后看了下验伪码,一切正常以后。开机激活,没有遇到任何问题,充电正常,键盘输入,触控板都正常。

打开 steam,下载 csgo,速度很快也很流畅。基本能稳定在 380fps 以上,游戏性能非常满意。等之后运行代码,本地渲染看看效果。

等到使用一段时间后再去京东售后评价一下。


时隔多年都没有换电脑,平时上班公司也都配备了,再加上旧电脑也能凑合用,所以没有萌生买新的念头。现在觉得是时候换了,也是一段很愉快的购物体验。

旧电脑暂时搁置,具体是挂咸鱼呢,还是搞搞 diy,那都是后话了。毕竟也有一些感情了,想要发挥点最后的余热。

“不如吃茶去”

2022-02-24 08:00:00

想了很久,也算是结合自己的心境吧,打算正式起一个博客名称,以前子舒的博客,也就是挂着自己的名字,总觉得差点意思。所以现在决定更名为 ‘不如吃茶去’。其中含义,听我娓娓道来…


我是从中学开始接触音乐的,当时喜欢唱歌,会听很多当时流行的歌曲。那时候比较火的最多的还是周杰伦的歌,还有就是林俊杰等歌手。

然后再到初入高中,准确的说应该是初三,开始听许嵩的歌,最开始是《自定义》专辑,每天都听,节奏很有旋律感。为此还专门买了一个 mp4,带着一块小屏幕的那种,下载了许嵩的歌,上学放学路上经常会听,包括在上晚自习的时候,也会一边听,一边写作业。

在 14 年的时候,许嵩正式发布的新专辑《不如吃茶去》,当时的我一听到这里面的歌,一下子就爱上了,从此彻底粉上了许嵩。

我觉得有一句话说的特别好:许嵩想通过专辑传递出“大千世界有很多想不通的事、猜不透的心与看不透的人,倒不如不想、不猜、不看,携寄情山水的快意人生吃茶去”的淡然态度。

当时年纪还小,单纯的听旋律和歌词。现在回过头来,仔细琢磨里面的话,突然感觉有了新的变化。这世界一直处于变化之中,正如罗翔老师讲的,这世界唯一不变的就是变化本身。

社会纷纷扰扰,心里也有很多杂绪,滋生了很多欲望。但是我也很清楚的知道,有些东西和欲望充其量是被社会气氛所带动的,而我自身不需要那些,过好自己的生活,不被外物所扰。“不以物喜,不以己悲。”

不如吃茶去,我也希望我的心境能同这句话一般,沉淀下来,不想那些太多遥远的东西,立足眼下,把握住身边的美好。

七碗受至味,一壶得真趣,空持百千偈,不如吃茶去。 —赵朴初

【笔记】使用 php 写接口文件调用数据

2022-02-22 08:00:00

如何使用 php 写一个接口,然后将数据以 json 文件格式调用。

<?php

header('Content-Type:application/json; charset=utf-8');
header("Access-Control-Allow-Origin:*");


$servername = "localhost";
$username = "数据库账号";
$password = "数据库密码";
$dbname = "数据库名称";

// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);
 
// 检测连接
if ($conn->connect_error) {
    die("连接失败:" . $conn->connect_error);
}


$sql = "select slug,title,created,text from typecho_contents";
$result = $conn->query($sql);


if ($result->num_rows > 0) {
    // 输出数据
    while($row = $result->fetch_assoc()) {
        
    $data[]=$row;
    
    }
    
    $json = json_encode($data,JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);//把数据转换为 JSON 数据。
    
    exit($json) ;

} else {
    echo "未查询到结果!";
}

$conn->close();
?>

根据某个条件排序,将 32 行的 sql 语句改为下面的,意为根据 created 进行排序。

$sql = "select slug,title,created,text from typecho_contents order by created desc";

Nuxt.js 如何部署 Artalk 和遇到的问题

2022-02-18 08:00:00

花了两天时间,终于把 Artalk 部署好了,一款数据自托管带后端的评论程序,非常适合我。这篇文章就介绍一下我部署的流程,以及我在部署过程中遇到的一些问题,将它记录一下,以便之后研究,顺便对使用 Nuxt.js 搭建博客的伙伴提供一些参考价值。

Artalk 地址:https://artalk.js.org/

这篇文章我将分为三个部分,后端部署、前端部署、问题研究。

1.后端部署

官方有两个版本,注意分辨,php 和 go,对应的是不同的仓库,目前官方文档写的默认都是 go 语言开发的。

ArtalkGo 仓库:https://github.com/ArtalkJS/ArtalkGo

我使用的也是 go 版本,功能和优化都比较好。


docker 构建 + 宝塔

我的服务器是 Ubuntu 20.04,其他版本类似,没有很大不同。

首先,使用 ssh 连接服务器。

sudo su 
# 启用sudo模式

cd /www/wwwroot/
# 进入站点目录

mkdir ArtalkGo
cd ArtalkGo
# 为 ArtalkGo 创建一个目录

curl -L https://raw.githubusercontent.com/ArtalkJS/ArtalkGo/master/artalk-go.example.yml > conf.yml
# 下载配置文件模版

到这里之后,官方说的是用 vim conf.yml 进入配置文件,修改参数然后配置。我不是很建议,因为用着并不是那么顺手(因为我菜),我建议直接到达 /www/wwwroot/ArtalkGo 目录下修改 conf.yml 文件,文本模式操作起来更顺手。

里面的配置基本都是站点名称,管理员,邮箱等设置,没什么大问题。除此之外,端口等参数都不用碰,不然会报错。

配置完文件之后直接保存就行了。


在宝塔插件里面下载 Docker 管理器,点击镜像管理。

然后在镜像名称输入 artalk/artalk-go 点击获取镜像即可,大概两分钟就下载结束了。

然后回到 ssh 连接,新建 docker 容器。

docker run -d \
   --name artalk-go \
   -p 0.0.0.0:8080:23366 \
   -v $(pwd)/conf.yml:/conf.yml \
   -v $(pwd)/data:/data \
   artalk/artalk-go

将上面的命令行在命令行,直接回车就行了。

这个时候需要去你的服务器厂商那里放行端口 8080 ,如果之前放行过不用管,没有放行的话需要去设置一下。

然后在浏览器输入 http://ip地址:8080

如果出现现在这个页面,说明你部署成功了,如果打不开或者是其他情况,请检查是否成功执行上面的步骤。


如果之后修改配置文件了,一定要注意在修改之后执行命令,需要重启服务才能生效。

docker restart artalk-go

反向代理

这个如果使用宝塔太简单,就不细说了。

https://artalk.js.org/guide/backend/reverse-proxy.html#%E5%AE%9D%E5%A1%94%E9%9D%A2%E6%9D%BF

文档已经说得很详细,如果出现操作失败的情况可以在下面评论。


2.前端部署

博客使用 vue + nuxt 开发。

我使用的是通过 cdn 引入的,npm 我目前发现和我博客有些冲突,暂时不使用该方式。

先在 /components 目录下新建一个组件名为 Comments.vue

<!-- Comments.vue -->

<template>
  <div class="wrapper">
	<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/Artalk.css" rel="stylesheet">
	<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/Artalk.js"></script>
	<div id="Comments"></div>
  </div>
</template>

<script>
export default {
  name: 'Comments',
}
</script>

<style lang="scss" scoped>
	<!-- css代码 -->
</style>

配置代码我单独放在 /static/main.js ,然后在 nuxt.config.js 引入。

// nuxt.config.js

head: {
    title: '子舒的博客 | imhan.cn',
    meta: [
      // ...
    ]
    script: [
      { src: '/js/main.js' },
    ]
  },

main.js 中配置 Artick。

new Artalk({
	el: "#Comments",
	server: 'https://域名/api',
	site: "子舒的博客",
	placeholder: '说点什么...',
	gravatar: {
	  mirror: 'https://sdn.geekzu.org/avatar/',
	  default: 'mp',
	},
	pagination: {
	  pageSize: 15,   // 每页评论数
	  readMore: true, // 加载更多 or 分页条
	  autoLoad: true, // 自动加载 (加载更多)
	},
	heightLimit: {
	  content: 200, // 评论内容限高
	  children: 300, // 子评论区域限高
	},
	versionCheck: true, // 前端版本检测
});

还有一个方案就是将文件放在服务器或者 github,将它引入,不过有些舍本逐末了,不是很建议。


3.问题研究

1. 关于评论路径问题

提交评论之后会在管理后台生成一个列表,表示哪个路径产生了评论,

如果想把某个页面评论转到其他页面,可以直接修改页面的 url,点击KEY变更

Artalk 的路径判断很严格,包括 https://example.com/1https://example.com/1/ 的评论就属于两个页面。

问过作者,觉得他的说法很有道理

qwqcode: https://example.com/1https://example.com/1/ 本来就不属于相同路径,后者等价于 https://example.com/1/index.html (和 web 服务器配置有关)

所以我直接在我博客里把路径然后默认加一个 / 符号,不管打开哪个页面都会跳转到带有 / 的 url,也是非常简单粗暴的解决了这个问题。

path: `/posts/${key.replace('.md', '').replace('./', '')}/`

2. localhost:3000 和域名

由此引发的另一个问题就是我在本地构建 localhost:3000 是不会显示域名后的评论,这个问题也不是大问题,我研究过 Artalk 文档,因为他是一个后端,可以多个前端使用,如果仅仅判断二级目录会造成一个很大的问题。

https://a.com/1https://b.com/1 这两个页面使用了同一套评论数据,所以为了避免这个情况,在判断路径时直接加上域名,这样的话,域名下的评论自然不会同步到 localhost:3000

这对我开发博客来说没有什么影响,所以就不打算解决了。


这是我目前遇到的问题,我会在后续的使用中持续更新本文,以作留存。

2022 新春快乐

2022-01-31 08:00:00

万物更新,旧疾当愈,长安常安。

今日是除夕,极目不见故土,抬头却是一片星空。

今年也是在杭州过的第三个新年,因为疫情的缘故,支持国家防疫政策。

如果所有人都祝你新年快乐,那我祝你历遍山河仍觉得人间值得。

去岁千般皆如意,今年万事定称心。新的一年,愿日子如熹光,温柔又安详,你我赤诚且勇敢,欣喜也在望。

也希望自己能继续不忘初心,一切如少年。

如何使用谷歌插件为网站注入代码

2022-01-26 08:00:00

在浏览网站时,受限于网站的缺陷,我们通常都会写一些脚本和插件来进行扩展,常规方法有油猴和谷歌插件两种,油猴也是一种插件,使用起来很方便,今天要讲的是如何通过谷歌插件进行注入。

按照结构生成下面几个文件

|chrome-plugin
|---manifest.json
|---script.js
|---favicon.ico

manifest.json 中写入代码,这是一个入口文件,声明了插件的基本信息。

  1. default_icon 是插件的图标
  2. matches 是使用的网站,在该域名下的网站才会生效。注意后面要加上 /*
  3. js 是引入的文件
// manifest.json

{
  "name": "Welcome",
  "manifest_version": 2,
  "version": "1.0",
  "description": "扩展插件",
  "browser_action": {
    "default_icon": "favicon.ico"
  },
  "content_scripts": [
    {
      "matches": ["https://github.com/*"],
      "js": [script.js"]
    }
  ]
}

然后在 script.js 中随便插入一段代码即可。

// script.js

console.log('hello, world!')

这样一个基本的插件就构成了,下一步就是上传。但是如果在打包生成 crx 文件后,上传 crx 文件会报错,显示危险,因为没有上架到谷歌商店。所以我们不用打包,直接加载本地文件。

然后选择写好的文件夹

点击确定上传即可,这样一个插件上传的步骤就完成了。我们可以学习相关技术,写上一些自己需要的功能,这也是 Chrome 浏览器这么受欢迎的原因之一,集成了大量的插件。

如果你想上传到商店出售或者免费分享,都是需要注册开发者账号的。缴纳 5 美元费用之后就可以上传了,审核过后就可以在商店列表看到你的插件。

《夜幕》

2022-01-21 08:00:00

0. 引言

如果再来一次的话,我不敢保证还会再做出这样的选择。

“悲剧是一种特殊的,无与伦比的喜剧。任何一种事物走到极端时,都会不可避免的朝着反方向前进。所以正确的方法应该是在随着时间的推移中,通过自身的动力以及外在的压力双重作用下,不断修正自己,那句话怎么说来着,是一种缓慢但稳定的螺旋式上升。”男子响亮而坚定的声音在天花板下回荡。​

1. 悲剧

“威廉·莎士比亚对人性的缺陷有着自己独到的理解和表达方式,这种思想最典型的表现是在戏剧《哈姆雷特》中..”

“哈姆雷特一方面被赋予了为父报仇的重任,另一方面由于其本身存在着性格的缺陷,在关键时刻犹豫不决,错过了最佳时期,”

“如何看待人性缺陷,关键点在于每个个体人性缺陷的大与小、主要与次要。如果为主要矛盾,那么这样的人性缺陷至少在某种程度上是不可原谅的;如果为次要矛盾,那么人们理应给予理解与关怀 …. 好了,今天的课就先到这里。”

陆恒微笑道:“这也是我们本学期的最后,马上就要结课了,希望同学们可以好好完成我们这门选课的结课论文,虽然没那么重要,但这也是你们辛苦一学期的证明。”

他顿了一下,高声道:“这你们本学期最后一次见到我了,希望你们假期愉快!”

陆恒,洛城大学的一名教授,年纪不过三十出头,却已经做到了副教授的位置,前途无量,年轻有为。他教的这门课程叫做《西方悲剧小说文化解析》,对于这方面他拥有独到的研究。很受国内相关领域很受推崇,本来他不是这门课的老师,但是原计划安排的老师意外住院,因此学校安排他负责这门课程,结果却出人意料的受欢迎。

。。。​

阶梯教室一片欢呼,陆恒笑了笑,提着包缓缓走出。

“终于结课了,开心!一会回去开两把游戏啊,好久没玩了,手都生了。”穿着白色衬衫的男孩兴奋的说到。

“再说吧,这两天也没什么心思打,赶紧把结课论文写完。”旁边一名戴着眼镜的男孩说,他推了推眼镜:“再说,还有一周就是寒假了,回家再玩也不迟,先把学校的这些事情办完再说。对了,陈尤,你一会去不去吃饭?”

白色衬衫陈尤苦道:“再说吧,我回宿舍前还得去一趟何静那里,也不知道她找我什么事!谈恋爱好难啊啊…”

“哎,好惨!哎对了,林舒,我听说隔壁班的李妙依今天早上一大清早在宿舍楼下找你,啥情况啊?你们莫不是?”陈尤打趣。

黑衣服林舒道:“得了吧,你这脑子天天想啥呢,我什么人你还不清楚。”他低头黯然道,“再说我也没心思想那方面的,她找我是有别的事情要说。”

陈尤无趣道:“切,开不起玩笑,先这样,我走了,那边催着紧呢。”

“好。”林舒,看着陈尤快步走出教室,他逐渐陷入自己的沉思低语,“或许,那只是一个巧合吧!不想那么多了,先去吃饭了。”

已经十二月份了,地处南方的洛城竟然不冷,空气中带着微风,好不惬意。或许来这里读书的人都抱着这样的想法吧。

收拾好书籍和笔记,林舒背着包慢悠悠的走下阶梯,想着一会吃些什么呢?

一声清脆的女声在左前方响起,正是门口的位置,“林同学!”

林舒抬头望去,有些诧异,正是一袭淡绿色上衣,身着牛仔裤的女生,心想到“李妙依..?她怎么在这”

“你现在有事情吗?我…我想和你再聊一聊那件事情。”还未等林舒开口询问,李妙依快速抢说道:“这都六点了,你肯定还没吃饭吧,我请你吃饭吧!”

林舒有些无语,食指扶了扶眼镜框架。“早上我不是已经说过了吗,那件事…。”

李妙依双手扶着腰,瞪着林舒,嚷嚷道:“行行行,我不跟你说那事,就是单纯的吃个饭,就当我报答你给我补课了。这还不行吗?”

他突然有些后悔了,悔不该当初为了那点钱去给李妙依补课,结果陷入这番纠结的境地。

“你一个大男人怎么磨磨唧唧的,赶紧的,我都快饿死了!”李妙依不由分说,快步走出,不给林舒插话的机会,“你快点的,一会餐厅人都满了。”

林舒无语默默跟上,一边走一边缓声道:“你哄小孩呢?这都快放假了,哪有那么多人。”

李妙依没有搭话,好像陷入了沉思,差点撞上对面来的人。

2. 回忆

餐厅,人群稀稀疏疏的,大多提着饭回宿舍吃,很少有坐下来吃堂食的。

偏僻的角落处,坐着两个人低头吃饭。

“你怎么保证你说的事情是真的呢?我怎么知道这不是你编出来的故事?”林舒喝了口汤,抬头问道。“再说了,如果真的有这么回事,为什么不找警察。我只是一个普通学生。”

李妙依听完这番质问,没有多说,随即从口袋中拿出钱包,翻开夹层,掏出一张照片,递给了林舒。

林舒诧异的瞅了她一眼,接了过去。照片中是一个年轻的女子拉着一个七八岁的小姑娘,女子大约二十出头

,脸上挂着温柔的笑容,竟与对面的李妙依有三分相似,林舒想了想说道:“就凭这个,能证明什么?”

李妙依低语的说道:“这是姐姐,那时我才八岁,这也是我最后一次见到她,整整十年过去了。”

她轻叹了一口气,娓娓道来:“可能你觉得我说的都是故事,我也无法证明什么,毕竟那么久过去了。连警察都无可奈何,只能以一个失踪案件结尾。”

林舒深呼吸,正色到:“你为什么会找上我,如果连警察都没办法,我们又能做什么呢?”

“我知道你是谁。不!准确的来说是你的另一个身份,我在你的平板上看到了’Gerechtigkeit’这个软件。”李妙依低笑到,好像发现了什么秘密。

林舒愣了一下,“喂喂喂!你这样侵犯别人的隐私好嘛。”

Gerechtigkeit,一款国外开发的软件,意为德语中的“正义公道”。也是一个比较小众的侦探社区,一直在一个较小的圈子里流行。大多都是通过系统给出的一些案件进行分析解答,然后获取积分。

但是很多案件在现实中都有原型,因此也比较受欢迎,一直保持自己固定的受众群体。而又通过积分的高低,列出了一个排行榜,其实也不是软件本身的排行榜,是一些玩家自发组织的,统计用户积分,然后列出的一个排名。

“你可是 zs 啊,榜上排行第三的大神。”,李妙依兴奋道,这也是她无意中发现的一个秘密。Gerechtigkeit 软件保密信息做得很好,很难知道屏幕对面的人是谁,如果不是巧合看到了林舒的账号,她无论如何也发现不了,隔壁班的一个普通男生竟然是社区中大名鼎鼎的 ‘zs’。

那天林舒给她补完课,随手把他的平板拿起来玩,结果在一个工具栏夹层里发现了一款熟悉的软件,之前被人提过的,她很有印象。

看到账号的那一刻,她很惊讶,同时也涌出一丝急切。因此,第二天早上就迫不及待的找到林舒,想要寻求他的帮助。

林舒苦笑道:“别了吧,那都是玩玩而已,当不了真。”心中懊悔,那天无聊登上去看看有没有发布新的任务案件,再之后陈尤找他出门,然后就忘记退出登陆这件事。暗道,“大意失荆州啊!平时都及时退出的,这件事还是越少人知道越好。”

李妙依说道:“就算如此,我觉得你肯定能给我提供一些帮助,而且我发现…”

林舒声音很冷静,缓缓说道:“我们也没那么熟吧,我为什么要帮你。顶多帮你补了几次课,不过也都是交易,所以我们也谈不上什么交情吧,我为什么要帮你解决你的事情呢?”

空气变得宁静,李妙依没有说话,只是静静的看着桌子上的黑色花纹,好像一团旋涡一般,让她的眼睛变得暗淡,随即笑了一下,“确实,是我唐突了。这件事本来就是我自己的事情,对不起。”

林舒也是静静的看着她,说实话,他并不想参与这样事情,更何况,他也没有这个能力。

“我先走了!”李妙依突然站起来,低声说道,“打扰了!”说罢,快步离开,似要逃离这个地方。林舒也感觉到了她的情绪低落,但是……

林舒就这么坐着,靠着椅子闭上眼睛,好像在想些什么,很安静,很虚幻。只有风扇不停的“呼…呼”转动,才将他的思绪拉回现实。

他掏出 iPad,看着历史浏览记录中的 Gerechtigkeit,眼神中闪过一丝光彩,抿紧了嘴,嘀咕道,“我也有自己的事情要做啊,都怪你们,整天麻烦我,哎…”

时间过得很快,林舒收拾东西准备起身离开,却看到对面的椅子上有一个厚厚的本子。他猜测到,大概是李妙依走的匆忙,忘记拿走。叹了一口气,拿起来装进包里,准备明天看到她再交还,现在也不敢发消息,刚对峙了一场,突然发消息都尴尬,他讪笑到。

3. 日记

等到林舒回到宿舍已经是八九点的时间了,不算太早,也不算太晚。

“回来了林舒。”两个舍友正在开黑打游戏,看到林舒回来,打了个招呼,随即继续投入激烈的战斗之中。

他应和了两声,坐在椅子上,收拾背包,把物品归纳整齐,林舒有一些强迫症,看到不整齐的东西就很难受。最后他掏出李妙依遗落的那个厚厚的本子,突然有些好奇。

我就看一眼,真的就一眼,不算侵犯别人隐私。然后在内心吐槽道,只要我不眨眼,就一直是一眼。

林舒打开桌子上的小夜灯,双手轻轻放在本子上面并翻开。抿住嘴唇,打开保温杯喝了一口水,然后朝着上面的文字看了起来。

“今天好开心,我在回答问题的时候他抬头看了我一眼,他是不是注意到我了?….可是他压根不认识我。怎么办,要不要主动去搭个话,交换一下电话号码。哎呀,好害羞,哪有女孩子这么主动的。…不知不觉大一都快结束了,怎么办呢?要不要一会去问问小岚她们,哼,一定会笑话我的。

—2007 年 4 月 3 日

林舒愣住了,什么?2007 年?

他看了一眼时间,没错啊,现在是 2017 年 12 月,十年前的日记,什么情况,这不是李妙依的日记吗?想罢,继续向下看去。

“今天我坐在他的前面了,好紧张,我背上有没有东西,头发脏不脏,万一他看到之后很讨厌怎么办?… —5 月 11 日”

“今天终于知道他的名字了,原来他是我们隔壁班级的,但是经常请假,不怎么来学校,我说怎么不经常看到你呢,好可惜啊,像你这么优秀的人,一定不会喜欢我吧?就叫你 H 吧!你的名字很好听。 —5 月 19 日”

其余都是一些杂言碎语,林舒在思考,这是谁的日记,为什么会在李妙依那里。难道?他摇了摇头,算了,顾不了那么多了,还是直接还给她吧!

拿出手机,看着最近通话记录里面的名字,沉思了一下,还是决定拨通。“嘟…嘟嘟…嘟…嘟嘟…您好,您拨打的电话请稍后再拨,Sorry….”

这又是什么情况,给我拉黑了?不能吧,李妙依也不是那么小心眼的人,一直大大咧咧的,林舒倒也觉得相处的很愉快。

渐渐的,林舒心中涌起一股不好的预感,背靠座椅,手指在桌子上轻敲,回想起之前谈话的过程。“她当时要说的是什么来着…?”口中轻轻喃语:“秘密吗?”

正当他心中烦躁,不知如何是好时?门口响起一阵急促的脚步,“林舒!林舒!!”

他抬头看去,一脸严肃的陈尤正看着他,“什么事,你着急忙慌什么?”

陈尤拉起他就往外走,别说了,我这有急事,你赶紧跟我走。林舒见状也不多说,他知自己的好友虽然平时喜欢玩闹,但还是一个非常稳重的人,平时很少看到他这幅模样。

随手拿起一件外套,十二月的洛城夜晚,还是略显瑟瑟寒意。回头看了一眼桌子上的日记,沉思了一下,揣进怀中。

陈尤带着林舒快步冲下楼,林舒问他到底发生什么事情了,陈尤不语,自顾自的走着,步伐很快,这让林舒心中也泛起了嘀咕,莫非?

到了宿舍楼门口的小树旁,陈尤拿出手机对林舒点了两下,说道:“你看,这是静静给我发的短信!现在联系不上她了,好像和李妙依有关?”

林舒低头看向手机上面的文字,“我和妙依去图书馆有点事情,就不陪你了。如果一直没有联系你,去找林舒,跟他说是妙依的事情,他会明白的。如果还是联系不到我们,就马上去报警。”

“到底是什么情况啊林舒?我刚才一直给何静打电话,都打不通,发生什么了,为什么她说你知道和李妙依有关?”陈尤焦急的追问道,显然他现在有些手足无措。“图书馆都闭馆了,按理说她早该回来了。”

林舒也纳闷,她们去图书馆那么就做什么,为什么说我知道,我知道什么?陷入了沉默,突然惊醒,掏出怀中的笔记,借助路灯昏暗的光芒,翻阅着日记,找到书写的最后一页,上面赫然带着崭新的墨迹,大致能判断出书写的时间不超过 5 个小时。

心中盘算了一番,这不就是我刚下课之前的时间吗?林舒突然想到,这是不是李妙依设计的一个圈套,故意遗落的,不论我答不答应她,她都会让我看到这些字。

扶了扶眼镜,扫除脑海中杂乱的想法,定神看去。“林舒,对不起!如果你看到这些字,说明你没有答应我的请求。但是我迫不得已把你拉进来,因为我发现了一个秘密,就在图书馆地下室里面。跟我失踪的姐姐有关!没有人相信我,我只能自己去找寻答案!我请求你帮帮我,我真的没有办法了。”

林舒心中涌出无力感,咬了咬牙暗想道,“你是不是疯了?”

看着满脸焦急的陈尤,定下了心,低声道:“跟我来!!”说罢头也不回的向前跑去。

4. 踏入

最近洛城的天气不错,云烟稀少,月色可以直直的照射到地面上,隐约可以看到有两个身影在向前奔跑。

正是林舒和陈尤,空气中很安静,只有一阵一阵的喘息,前面黑色的夜幕,模糊的映着一栋建筑物的背影。

“林舒,我们要进去吗,可是已经关门了啊?”陈尤疑问着。

林舒小声道:“之前我在图书馆在值班的时候,为了方便,配了一把值班室的钥匙。”从口袋中掏出一串钥匙在陈尤眼前晃了晃。

陈尤大喜道:“好兄弟,不愧是你!真有你的。”

洛城大学图书馆的值班室在靠近正门侧边的位置,图书馆有着很老的历史,反正宣传手册是这么写的。值班室里面有个门是通往图书馆的大厅。

林舒带着陈尤悄悄趁着夜色,摸到一段阶梯处,四处看了看,没人注意到,快速把钥匙插进去,逆时针拧了一圈,顺利打开,俩人闪身进入值班室。然后将门反锁。

“好黑啊!”陈尤捅了捅林舒的胳膊,抱怨道。显然他还不太清楚发生了什么事,只是抱着对林舒绝对的信任才跟着来的。

林舒捣鼓一会,从一个小柜子里拿出一个手电筒。“啪”的一声打开,还好电池都还有电,不然只能用手机照明了。他将手电筒调到最小的档次,轻声道:“好了,别抱怨了,跟我来。不知道她们现在什么情况?”

静谧的建筑物内空无一人,稀稀疏疏的脚步声让他们大气都不敢出一声,两人借助微弱的灯光缓慢前进,怕被外面巡查的保安发现。

“我们去地下室看看。”林舒回想着日记上面的字,回头对陈尤说道:“去那里看看有没有什么线索。”

陈尤应声道:“好,那我们快些吧!”

林舒带头前行,这里他比较熟悉,当了半年的管理员,经常往返于各个区域之间,每个门都记得清清楚楚。走了几步,向前看去,安检门闪烁着昏暗的红光。林舒纵身一跃,翻过阻拦的台阶,陈尤随即跟上,控制着脚步俩人继续朝着地下室走去。

夜色笼罩着空气,林舒顺着走廊拐了两圈,打开一扇安全通道的门,放眼望去一片黑暗,他将手电筒稍微调大一些,才能清晰看到一段向下的楼梯。水泥楼梯地上都是黑色的污迹,尽头的拐角处好似黑洞,散发着幽冷的气息,想将他们吞噬,旁边挂着安全通道的牌子泛着绿光。

陈尤打了下冷战,“这…怎么阴森森的。”

林舒笑道:“这里常年没多少人来,平时都当仓库用,经常搬东西,看你吓成这样。”

“别胡说,谁害怕了?看我的!”说罢,陈尤不满的夺过林舒手中的手电筒,只身向楼梯走去。

林舒有些无奈,只能在后跟随,说道:“你走慢点,这里你不熟,别迷路了!下面有好几条通道。”

可以看到两道身影越走越远,逐渐消失在通道尽头,脚步声慢慢归于平静。静谧的大厅也被月色透过玻璃,照耀着光芒,这时大厅闪过一道人影,模模糊糊的身影大概能分辨出是一个成年男子。

只听他叹了一口气,没有过多地停留,竟也朝着林舒他们的方向前进。

已经进入地下室的两人,缓慢的前进,陈尤顺着墙壁通道一直走,林舒紧跟其后,他掏出手机看了一眼,已经十点二十。

“林舒,你看!”陈尤突然回头对林舒说。

林舒眼前正是一个杂乱无章的仓库,地面上到处都是随意摆放的箱子,还有几把椅子,都铺满的灰尘,陈尤踢了一脚,竟然“咯吱”的一声,直接掉了一个腿,他讪笑道:“这…这跟我没关系啊,本来就散架了。”

林舒在仓库转了几圈,和之前看到的情况一样,打开箱子看了看,都是以前搬进来的书,在这里堆放着。轻轻跺了跺脚,地上的灰尘发生一声厚重的声音,很长时间没人来了。仓库的侧边有几扇木门,倒是关的紧紧的,林舒暗自沉思了一下,心中大致有了计较。

“这么灰尘这么厚,肯定有脚印,赶紧搜索看看!”说完,林舒拿着手机对着地上照去。陈尤见状也是举着手电筒看去。

陈尤刚进来时四处走动了一下,倒是有一阵杂乱的脚印,不过问题不大。

林舒顺着墙边搜索,突然看到有一处地板特别干净,旁边的箱子像是被人推开的,而这处赶紧的地面周围赫然有被人踩过的痕迹,不过很乱,应该是人来回走动造成的。

“陈尤,你过来!”林舒喊道,“应该是这边。”只有这边有人的痕迹,大概就是这里了。

陈尤快速说道,“那我们快走吧!不知道能不能顺利找到何静她们!”

林舒“嗯”了一声,在手机上点了几下,然后放进口袋,对陈尤说道:“走!”

轻轻推开木门,锁已经不见了,所以能够很轻松的进入。眼前是更加黑暗的世界,借助灯光林舒眯着眼看去,“我去,这是什么鬼玩意!”

5. 迷雾

林舒举起手电筒向前走去,却被坑洼的水泥地拌了一下,紧张状态下瞬间失去重心,朝前翻倒,不慎撞到一块石头。

手电筒重重的摔在地上,林舒失去了意识。

“林舒!”陈尤焦急的大喊道:“林舒,你不要烂尾啊!卧槽你大爷!!”


ps: 作者肯定不会让林舒轻易 over 啊,毕竟是以自己为原型的角色。但是最近很忙,没什么思路,想快速解决掉这篇文章。

也算是一个心结,对不起了亲爱的读者们!下次一定好好构思一个完整的剧情,然后找一个悠闲的时间,写完一个故事。

下次再见!

读一读柏拉图的《理想国》

2022-01-19 08:00:00

最近在读柏拉图的《理想国》,源于罗翔老师的推荐,让我也有一些冲动。也抱着对自己灵魂有一丝净化的作用。让浮躁的内心安静下来。

但是通书有一点诡辩的色彩,所以还是要认真分辨的。不可掉入设置的“陷阱”,就像是生活一般,做事都要对其认识分析,不可一概而信。人都是主观的,每个人读到的内容都是不一样的。

当一个人知道自己将要死亡的时候,会有一种恐慌和忧虑在他的脑子里久久不散,即来世会受到今世所做之恶的惩罚。
以前听这些话就觉得很好笑,但是到了现在我总觉得这话不像是假的,所以便感到疑惑不安,经常回忆自己之前有没有害过什么人。
人之所以到了现在才有这样的忧虑和担心,大概是年纪大了精神衰弱的缘故,又或是像漂泊的孤舟将要抵达岸边,因此对岸上的事物看得更加清晰的缘故。如果知道自己造孽颇深,就会过度悲观,常常会像小孩子从梦中惊醒一样,惊恐万分。
但是那些问心无愧的人,“希望”两个字就是他们心灵的看护神。

正义总是相对的。


我很高兴我已经脱离你们所说的情爱了,这种脱离就像从一个暴君那里脱离苦海一样

读到这里,我只想说一句,“柏拉图误我!”(手动狗头)


如果被你禁止的答案中,有我认为是正确的,即使很危险,我也会使用它。

假如在这诸数以外,我能给出更好、更优的正义解释,你说你该怎样?

我应该接受无知的惩罚,自古以来,后知后觉的人理应向先知先觉者学习。

学习总是永无止境的,如果未来的你发现你过去了解的知识有谬误,也不必感到难堪或者失落,你应该虚心学习这些,将它转化为你自己的思想和知识。


凡是伟大的事,本来就没有容易过。

人的决心有的因为别人的劝告而改变,有的因为疏忽而忘却,前者是被外力所动摇,后者是被时间所消磨

Nuxt 项目中如何引入百度统计?

2022-01-17 08:00:00

在部署一个网站之后,我们最关心的事情无异于是访问量,以及对它的分析。国内的百度统计是一个不错的选择,基本的功能都是免费的。

只需要在 head 中引入一串 javascript 代码即可。

<!-- <script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js?xxxxxxxxxxxxxxxxxxx";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script> -->

上面是百度提供的统计代码,需要放在 <head></head>中。

但是在 nuxt 中,没有传统的 <head></head> 。所以要对他进行一些处理。

1. 首先在根目录下 /plugins 新建一个文件 baidu.js

// /plugins/baidu.js

export default ({app: {router}, store}) => {
  /* 每次路由变更时进行 pv 统计 */
  router.afterEach((to, from) => {
    /* 告诉增加一个 PV */
    try {
      window._hmt = window._hmt || []
      window._hmt.push(['_trackPageview', to.fullPath])
    } catch (e) {
    }
  })
}

2. 配置 nuxt.config.js 文件

  1. plugins中:
plugins: [
  {
    src: '~/plugins/baidu'
  }
],
  1. head中:
head: {
  // ...
  link: [
    // ...
  ],
  script: [
    { src: 'https://hm.baidu.com/hm.js?xxxxxxxxxxxxxxxxxxx' }
  ]
},

在 script 中写入百度统计提供的 url 即可,按照对应的字符。

网易云音乐年度报告分享

2021-12-27 08:00:00

我真的老了吗?看看我的网易云音乐年度报告分享吧!

图片 图片

typecho 更换 gravatar 头像源

2021-12-21 08:00:00

找到根目录下 config.inc.php 文件,在最前面加入下面的代码。

define('__TYPECHO_GRAVATAR_PREFIX__', 'https://sdn.geekzu.org/avatar/');

其中 https://sdn.geekzu.org/avatar/ 是一个国内源链接,这个是我目前找到的比较稳定的源。

如果这个源不能用了,可以更换其他的源,直接搜其他的相关国内源就可以了。

如果你有其他可以用的头像源,欢迎留言。

https://gravatar.zishu.me/avatar/ https://cravatar.cn/avatar/ https://sdn.geekzu.org/avatar/

有关 cdn.jsdelivr.net 证书错误的一些想法

2021-12-20 08:00:00

记录一次不大不小的事件,2021 年 12 月 20 日,cdn.jsdelivr.net 国内 ip 全面崩盘,说是证书出错了,然后所有使用 cdn.jsdelivr.net 进行加速的资源全部 404。

我也是在朋友圈看到的这个消息,上线一看果然全部显示丢失。

对国外资源没有影响,所以这是一次只出现在国内的问题,jsd 官网都还在,估计是 cdn 配置出错,应该很快就可以修复了。

然后,我就开始思考将一些文件资源挂在一些比较可靠的网站是否真的可靠,包括之前使用 Github Pages + Vercel,也出现过一段时间的证书错误,导出托管的网站崩了两天了,之后官方更换了新的 ip,虽然没什么大问题,但依旧是会有一些断断续续的连接错误 404。

所以也就加大了我使用自己服务器的决定。但是在这之前,我的一些其他资源,包括 js 文件和一些图片,都是托管在 github 仓库里面。然后通过 cdn.jsdelivr.net 进行 cdn 加速,一直都很相信它的。

但是最近一系列事情的发生,不是一次两次了,我觉得数据还是放在自己手里安全,以后会考虑将存在在各大平台的文件和图片以及数据都导出来,多处备份,防止像今天这样的事情发生。

之前我的图片是上传到 github 用 jsdelivr 加速,现在我将其切换到我的服务器了。不再使用任何 cdn 加速,本身服务器的速度也够了,再加上平时没几个人访问,基本都是自己在看,完全满足我的个人使用了。

但是之前的图片还在 404 中,会慢慢转移到别的地方,之后的文件肯定不会用 jsdelivr 加速了。

写完测试一下 jsdelivr 是否修复,很好,已经半天过去了,没有修复完成的迹象。

最后感叹一句,数据真的是又脆弱又重要!

闲谈 2021

2021-12-17 08:00:00

2021,不知不觉,又过去一年了。

因为最近也比较忙,所以一直拖到了中旬末,现在闲下心准备闲谈一下。 该说不说,坚持写博客也有 500 多天了,大概是我的水平有限,产出的技术文章也是寥寥无几,大多是一些笔记和一些生活中的琐碎事。

认识了几个一起玩博客的新朋友,优化了一下自己网站,然后帮助别人解决一些力所能及的问题,大家都很友善。

但是感觉今年好像没有做出特别有成就的事情,工作闲暇之余看看新的技术内容,给自己写了几个小项目。

令人烦躁的是,疫情起起落落,难以真正结束,很多想法都被搁置了,旅游计划也是寥寥无几,又是在杭州市内和周边闲逛的一年。当然这也跟我自己有很大的关系。

去年定下的 flag,最终也没有完成,希望来年再接再厉,如果运气不错的话。

实现图片灯箱功能

2021-12-07 08:00:00

写博客必不可少的一个功能就是图片灯箱功能,也就是点击放大查看。但是不同程序的博客所使用的插件也都不一样,我这里研究出一套可以满足绝大部分程序的灯箱插件。已经测试过 hexo, hugo, typecho 均无问题。

引入文件

首先引入我们关键的两个文件分别是 zoom.csszoom.js

<!-- head 头部 -->
<link rel="stylesheet" href="https://cdn.zburu.com/list/zoom.css">

<!-- body 底部 -->
<!-- 如果已经有 jq 文件了,就不要引入 jquery -->
<script src="https://cdn.zburu.com/list/jquery3.6.0.js"></script> 
<!--图片灯箱-->
<img src="" alt="" class="bigimg">
<div class="mask"></div>
<script src="https://cdn.zburu.com/list/zoom.js"></script>

因为这个文件是放在我自己服务器上的,所以不敢保证以后还是这个路径,建议在浏览器打开,然后把里面的代码复制到本地使用。

调用 js

然后我们需要给图片一个类名,以及一个父元素盒子,同样也可以使用 jq 来完成。

这里的 .post-content 正文的类名,如果你是别的类名,可以直接将 .post-content 替换掉。

$(function(){
	$('.post-content img').addClass('smallimg')
  	$('.post-content img').wrap('<div class="imgbox"></div>')

})

接着我们需要对插件进行初始化,直接在刚才的代码下面写入。

$(function(){
	$('.post-content img').addClass('smallimg')
		$('.post-content img').wrap('<div class="imgbox"></div>')

		/*
	smallimg   // 小图
	bigimg  //点击放大的图片
	mask   //黑色遮罩
	*/
	var obj = new zoom('mask', 'bigimg', 'smallimg');
	obj.init();
})

回到页面上,点击正文的图片就可以成功放大了。

结束

随便放个图片试验一下吧!

之所以我限制了在正文中才可以放大,是因为网站其他地方也有图片,如果都可以点击放大,就很不合理。

如果在使用中有其他问题,欢迎留言。

关于人际交往的礼节感触

2021-11-24 08:00:00

因为一件小事有感而发,在人际交往的过程中,我们通常都会关注一些细节,从而影响对一个人印象。这是一个作为人很正常的行为。

打个比方,两个人聊天,其中一个人不停拿起手机查看,就很给人一种很没有礼貌的印象。假如说有工作要忙,或者其他要处理的事项,这当然另当别论。换个思维,如果真的有那么忙,也不会发生此次社会交互行为活动。所以这是一个悖论了。基本上不存在忙的要命的情况。

我们在社会交往中,大多抱着一种善意的目的,希望从别人那里获得一些“利益”,这里的利益更多指代一中虚拟的情绪,比如开心,愉快,满足等。所以我在平时跟朋友社交,包括并不限于聚餐,出行游玩等,都是将重心放在此次活动中,如果有其他的事情,我都会提前处理安排好,不会影响活动进行。

我想,这是一个正常的人的社交应该有的,一个必需的态度,是一种比较充满正能量,积极向上的态度,如果双方都是如此,我想此次社交行为都将会有一些愉快的体验。

但有句古话说的挺好, “林子大了什么鸟都有”。你不能保证每个人的社交态度和行为都是一个积极的方向,将其转化为日常说法就是一个人的礼节,你不能确定他 (她) 真的有礼貌,或者在某些方面冒犯到你。

所以在你感觉到情绪不适,或者对他 (她) 有什么看法,最佳的做法就是旁敲侧击,看看他 (她) 是什么有什么重要原因,做出了一些失去礼节的行为,如果真的有原因的话,我都会表示理解。

但是如果真的是性格的方面的问题,导致他 (她) 一直都是这个样子,我一般也是表示理解,但是往后我会重新审视这个人,是否真的可以有成为好朋友的机会。

个人觉得最基本的礼节还是要有的。

另外说,手机真的没有那么好玩,没必要走路,吃饭都在看手机。如果真的想玩,可以独处时尽情的玩耍,多人在一起时,最好还是以社交为主,不然社交不就失去了它的意义吗?

网易云摸鱼,测测你的听歌流量

2021-11-18 08:00:00

格局刚好。

这两天的碎碎念

2021-11-11 08:00:00

这段时间双十一,发现没有了往年那种囤货淘宝的欲望了,翻来翻去不知道买什么,包括那些活动,有好多朋友给我发链接助力我都没怎么弄,可能是生活的打磨让我对它没兴趣了吧~~

唯一的消费活动就是买了一台服务器,腾讯云正在做双十一特惠活动,一台 2h4g6m 的服务器,三年才一百多,个人感觉还是比较划算的,以后可以放一些脚本,跑跑服务啥的,还可以把我的博客挂在上面。

并且购买了一个新的域名 xiabanlo.cn,不过最近正在备案,通过之后就会使用了。坚持,不忘初心!


然后就是突入起来的降温寒潮,虽然提前有了预报,却还是不小心中招了,昨天早上起来喉咙一直痛,想咳嗽。赶紧喝了两杯热水,发现作用不大,依旧是感冒流鼻涕了,不过还好是轻微的。

晚上回到家,把床垫子加厚了,并且换了加绒的被子。像什么毛衣手套啊,也都安排上了,喝两袋 999 感冒灵,今天感觉舒服多了,看了一眼天气预报,温度貌似还有回升,这就很棒了。

不过早上骑车去地铁的时候,还是有些冷,手套带好,裹得紧紧的。

生活不易,注意身体,防寒保暖~~

中午,吃了一碗雪菜黄鱼面,味道很不错,点个赞!!(唯一的缺点就是有点小贵~~)

揭秘箭头函数

2021-11-05 08:00:00

英文地址:https://javascript.plainenglish.io/demystifying-javascript-arrow-functions-7b2a0908a2b3
通过掘金翻译计划活动进行翻译

箭头函数是函数表达式的替代方法,但在语法上有所不同,不能在所有情况下使用。如果您还没有阅读 JavaScript 中的函数表达式,我建议您在继续阅读之前先阅读这篇文章

现在,让我们试着从语法、执行、作用域和提升以及代码示例方面来理解箭头函数。

1. 语法

const arrowFunctionSyntax = () => {
  console.log('Hi, I am an arrow function');
};
arrowFunctionSyntax();

在上面的代码示例中,我们可以看到箭头函数类似于函数表达式,因为它们被赋值为变量。主要区别在于函数的编写方式。以下是我们可以根据其语法从上述代码中得出的观察结果:

  1. 它不包含 function 关键字。
  2. 它没有 函数名 ,这意味着这些是匿名函数。
  3. 引入了箭头 => 符号。
const arrowFunctionWithOneParam = (number) => number + 1;
const arrowFunctionWithMultipleParams = (numberOne, numberTwo) => {
  let sum = numberOne + numberTwo;
  return sum;
};
console.log(arrowFunctionWithOneParam(5));
console.log(arrowFunctionWithMultipleParams(5, 6));

如果我们同时观察 arrowFunctionWithOneParamarrowFunctionWithMultipleParams,我们可以发现三者的不同—括号 ()、块 {}return 关键字的使用。根据箭头函数的语法,如果函数只接受一个参数,可以忽略括号()。如果函数只包含一条语句,则可以忽略块{},最后其实也可以忽略return ,如果函数只包含一个语句。

2. 执行

const arrowFunctionExecution = () => {
  console.log('Hi, my execution is similar to normal function');
};
arrowFunctionExecution();

在执行箭头函数时,它们的执行方式与其他普通函数的执行方式相似。当 JS 引擎执行 arrowFunctionExecution() 时,它会创建一个函数,执行上下文并被推送到调用堆栈。一旦创建了执行上下文,它就会启动创建阶段。在这个阶段,它将创建参数对象并在其本地内存堆中声明所有变量。

与普通函数相比,主要区别在于 this 的声明。箭头函数没有自己的 this 变量; this 在箭头函数中使用时会得到词法解析。在创建阶段之后不久,执行阶段开始。这时它开始执行语句 console.log() ,并将 "Hi, my execution is similar to normal function" 打印到控制台。

3. 作用域

const arrowFunctionScope = () => {
  console.log('Hi, my scoping rules works similar to function expression');
};

这些函数遵循与其他函数表达式相同的作用域规则。这些函数有自己的作用域,函数内部声明的任何变量都不能在函数外部访问。这些函数也不适用于 callapplybind 方法,这些方法通常依赖于作用域。如果您还没有阅读过 JavaScript 中的作用域,我建议您阅读 这篇文章

4. 提升

amIGoingToBeHoisted();
var amIGoingToBeHoisted = () => {
  console.log('The answer is NO');
};

箭头函数没有被提升,因为它们也是函数表达式,其中函数被分配为变量的值。当 JavaScript 引擎执行上述代码时,在创建阶段,声明语句 var amIGoingToBeHoisted 将被移到顶部并使用值 undefined 进行初始化,而将初始值留在后面。在执行阶段,遇到语句amIGoingToBeHoisted 时会抛出类型错误,因为它的值是 undefined,这不是提升后的函数类型。要了解有关 JavaScript 提升的更多信息,我建议您阅读 文章

js 奇怪的知识--console.table

2021-11-04 08:00:00

这个属性对我来说还真有些陌生,无意中发现的,查询过 MDN 之后听得挺有意思的,就记录一下。

根据字面意思就是“将数据以表格的形式显示”。这个方法需要一个必须参数 data,data 必须是一个数组或者是一个对象;还可以使用一个可选参数 columns。

表格的第一列是 index。如果数据 data 是一个数组,那么这一列的单元格的值就是数组的索引。如果数据是一个对象,那么它们的值就是各对象的属性名称。注意(在 FireFox 中)console.table 被限制为只显示 1000 行(第一行是被标记的索引)。

1.直接输出

直接进行尝试,在控制台输出 console.table

console.table(["双十一", "双十二", "双十三"]);

PS E:\demo> node 1.js
┌─────────┬──────────┐
 (index)   Values  
├─────────┼──────────┤
    1     '双十二' 
    2     '双十三' 
└─────────┴──────────┘

复制下来怪怪的,直接上个图,就是下面这个样子。


2.定义对象进行输出

同样也可以定义一个对象然后输出

function Fun(title, date) {
  this.title = title
  this.date = date
}

const one = new Fun("双十一", "11.11")
const two = new Fun("双十二", "12.12")
const three = new Fun("双十三", "你是傻子吗,没有 13 月")

console.table([one, two, three], ["title", "date"])

打印出来,就是下面这个样子。

3.console.table() 的应用场景

前面是在通过在编辑器中使用 node.js 输出数据,看看在浏览器中输出会不会不一样的效果。

可以看到成功输出数据,并且点击表头时,可以按照不同顺序进行排序,这么一看….嗯,还挺有用的(假笑)

应用场景大概就是可以对打印的数据进行更直观的观看,在某些情况下有利于调试代码,以后看看是否能用得上这种方法。

通过 getTimezoneOffset() 方法判断当前时区是否为夏令时

2021-10-22 08:00:00

getTimezoneOffset() 方法 方法返回 UTC 时间和本地时间之间的时差,以分钟为单位。

世界协调时间 (UTC) 是世界时间标准设定的时间,UTC 时间与 GMT 时间(格林威治时间)相同。

测一下我所在时区和标准时区的时间差

function myDate () {
  var d = new Date();
  var n = d.getTimezoneOffset();
  console.log(n)
}
myDate();

// PS E:\demo> node 1.js
// -480

也就是 480 分钟(8 个小时),北京时间是东八区,刚好与格林威治时间相差 8 个小时,这说明 getTimezoneOffset() 方法是很有效的。

接下来利用这个方法,判断一下当前时区是否为夏令时。

const time1 = new Date(2021, 0, 1);
const time2 = new Date(2021, 6, 1);
if (time1.getTimezoneOffset() != time2.getTimezoneOffset()) {
  console.log('夏令时');
}
else {
  console.log('非夏令时');
}


// PS E:\demo> node 1.js
// 非夏令时

有关国庆出行的感想

2021-10-07 08:00:00

好长时间没回老家了,这次本想借着国庆七天回家一趟,顺便处理一些其他的事情。所以我从 9 月 30 日晚,与家人一同驾车回家。

艰难的回家之路

刚上高速跑得很快,原以为已经错开出行高峰期,结果发现是我想多了,不出意外的堵在高速上。

导航呢,这时候也是推荐不走高速,直接从国道走(真的是后悔),然后我们就下了高速,结果这个导航给我整的有点蒙,在临安绕来绕去,11 点从杭州出发,凌晨 3 点还在临安出不去,国道都在堵车,然后没办法只能就地休息,困得实在不行了,其实睡也睡不舒服,跑夜车都是这个样子。

睡到 5 点多,这时候已经是 10 月 1 日了,继续出发,按照导航走,结果走到了一个盘山公路,不知道叫什么山,没注意看。

大概走了 3 个小时,盘山公路弯弯曲曲的,九转十八弯。不过风景还行,权当安慰自己出来旅游了。

中午才从山里出来,这时候在安徽境内,接近六安。

大家随便弄点泡面吃了吃,继续出发,已经是 1 号下午了。跑到下午 5 点多的时候,距离老家还有 100 公里左右,再来两个小时差不多就到了。结果发生了一些小意外,导致轮胎爆了,然后处理事故到后半夜,修好车也不能走了,身心疲惫,就地开了个宾馆休息,一夜的深度睡眠真的很爽,以后再也不熬夜了,难受死。

早上睡到 9 点半起,简单收拾一下,10 点出发,也没有吃饭,不太饿,打算一口气开回家,也是不负众望,午饭前就赶到县城了。

好久没回家,发现物价真的低,四个人吃的撑死,最后没吃完,把剩下的菜打包带回去,结账也才 150 左右。不过工资水平也和物价水平差不多,然后就引发了我的一个新的思考,以后是否可以回县城养老。


回家碎碎念…照片 (无)


6 号踏上返程的道路,7 号回到杭州,真的身心疲惫。又是一路的夜车,在服务区休息了两个小时也顶不了多大的用。

到杭州之后就开始胃疼了,大致算了一下,7 天,有 4 天都在路上奔波,吃的都是饼干泡面,实在是扛不住,到这边之后吃完饭洗了个澡直接睡到下午六点,起来还是精神有些萎靡,我估计至少得三天才能缓成正常。毕竟年纪大了,真的痛苦。

关于开车出行的反思

长了个记性,以后再也不开学出行跑长途了,特别节假日,火车七八个小时,睡一觉就能解决的问题搞得那么复杂,不过也是因为带了一些行李的缘故。下次就打算行李直接寄回去,随身背个包,然后坐火车轻装上阵,岂不是美滋滋。

吐槽高速的一些事情

安徽段高速真的不行,管理不到位,救援迟缓,在安徽境内高速上堵车的时候,应急车道基本没闲着,私家车直接跑,路过别的省份基本很少有这种现象,而且出现事故的时候,救援迟缓,交管不到位,直接导致堵车堵了很长时间,我在河南段,浙江段跑的时候,看到事故都是处理的很快,基本没有太长的堵车现象。

使用 js 随机生成背景颜色

2021-09-29 08:00:00

先写一个简单的 html 页面出来

<style>
  /* ... */
</style>

<ul>
  <li>html</li>
  <li>css</li>
  <li>js</li>
</ul>

通过 Math.random() 属性可以随机生成一个数字,然后通过转化为十六进制的方法进行处理,下面就是随机生成 6 位数字的代码,并进行转化的代码。

const randomHex = () => `#${Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, "0")}`;

可以在控制台输出看一下结果,随机生成了一个结果 #62113b

接下来就需要将这个颜色传给上面的 li 标签,首先需要将所有的 li 遍历然后生成一个数组,可以使用 for 循环进行遍历。

var arr = document.getElementsByTagName('li'), temp = [];
for (var i = 0; i < arr.length; i++) {
  temp.push(arr[i].innerHTML);
}

然后再对数组进行循环处理,每个 li 标签要要运行一遍随机生成颜色,并且传给自身。同样也是通过 for 循环进行操作。

var li = document.getElementsByTagName("li");
for (var i = 0; i < li.length; i++) {
  
  for (var num = 0; num < li.length; num++) {
    li[num].style.background = randomHex();
  }

}

这些事件都是在页面加载完成之后运行的,所以需要通过 window.onload 写入事件,然后把所有的代码合在一起。

看看效果如果。

把这些复制下来放到本地运行一下就可以看到效果了。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>随机生成颜色</title>
  <style>
    body {
      display: flex;
      justify-content: center;
      padding-top: 100px;
    }
    ul {
      list-style: none;
      padding: 0;
      display: flex;
    }
    li {
      width: 100px;
      height: 40px;
      color: #333;
      display: flex;
      justify-content: center;
      align-items: center;
      margin-right: 30px;
    }
  </style>
</head>
<body>
  <ul>
    <li>html</li>
    <li>css</li>
    <li>js</li>
  </ul>
  <script>
    const randomHex = () => `#${Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, "0")}`;
    console.log(randomHex());

    var arr = document.getElementsByTagName('li'), temp = [];
    for (var i = 0; i < arr.length; i++) {
      temp.push(arr[i].innerHTML);
    }

    window.onload = function () {
      var li = document.getElementsByTagName("li");
      for (var i = 0; i < li.length; i++) {
        
        for (var num = 0; num < li.length; num++) {
          li[num].style.background = randomHex();
        }

      }
    }
  </script>
</body>
</html>

如果有更好的写法,欢迎评论!

hugo 如何使用 Cusdis ?

2021-09-16 08:00:00

Cusdis 是一个界面清爽、注重隐私的轻量级 (~5kb gzip) 评论系统,可以很方便地与 React、Vue 或其他博客系统结合,并且还提供了一个后台来管理所有的评论。

由于 Cusdis 自称是 Disqus 的替代方案,因此它还支持一键从 Disqus 导入、支持邮件通知等功能。

官方地址:https://cusdis.com/

他通常是用于静态博客的一个第三方评论系统,但是官方文档没有提供如何在 hugo 中使用的参数,我也是报错了多次才琢磨出来。

1. 本地部署

根据提示,注册账号,然后 Add website ,建立一个仓库。

进入它,然后点击 setting

Embed Code 会提供几行代码以及一些 api,复制他们。

data-app-id 是注册时自动生成的,注意保密,每个人都不一样。

<div id="cusdis_thread"
  data-host="https://cusdis.com"
  data-app-id="xxxxxxxxx"
  data-page-id="{{ PAGE_ID }}"
  data-page-url="{{ PAGE_URL }}"
  data-page-title="{{ PAGE_TITLE }}"
></div>
<script async defer src="https://cusdis.com/js/cusdis.es.js"></script>

将这些代码复制到本地主题的 comments.html 文件内(就是评论的那个模块,每个主题或许都不一样,注意分辨)。

这个时候如果你 hugo server 基本都会报错,因为没有修改 {{ PAGE_ID }} 等内容,这不是 hugo 官方提供的参数,按照我下面的格式,将 id, url, title 重写一下即可。

<div id="cusdis_thread"
  data-host="https://cusdis.com"
  data-app-id="c1d43485-e8a7-4895-972e-247eddaf242d"
  data-page-id="{{ .RelPermalink }}"
  data-page-url="{{ .RelPermalink }}"
  data-page-title="{{ .Title }}"
></div>
<script async defer src="https://cusdis.com/js/cusdis.es.js"></script>

保存重新 hugo server 即可。

这样做的好处是当你接受一条评论信息后,审核列表可以清楚地标明来自哪一篇文章。通常大多数博客都提供了这么一个功能。

2. 邮箱提醒

除了必备的评论功能,Cusdis 还提供了一个非常快速的邮箱提醒功能,不需要绑定密匙,直接输入自己的邮箱即可。

先勾选这个 Email Notification,然后点击下方的 Advanced Notification Settings (高级通知设置)

就可以进入设置,输入自己的邮箱。

3. 中文化

默认的评论和提示文字均为英文,对我们很不友好,这里提供一个不错的解决方案,在引入 Cusdis 之后,顺便复制引入下面这段代码。

<script>
  window.CUSDIS_LOCALE = {
    "powered_by": "评论由 Cusdis 提供",
    "post_comment": "发送",
    "loading": "加载中",
    "email": "邮箱地址 (可选)",
    "nickname": "昵称",
    "reply_placeholder": "回复内容...",
    "reply_btn": "回复",
    "sending": "发送中...",
    "mod_badge": "管理员",
    "content_is_required": "内容不能为空",
    "nickname_is_required": "昵称不能为空",
    "comment_has_been_sent": "评论已发送,管理员审核通过后会展示"
  }
</script>

。。。

使用 jq 做一个数字递增效果

2021-09-14 08:00:00

数字递增,顾名思义就是数字不断增加,数字递增的效果就是要一定时间内,让数字有一个增加的特效,一般用于强调某个内容。

用 jquery 也很好处理,大概就是下面这些。

class="num-ber"id="count-number" 按照这个填入。

<p>
  <span class="num-ber" id="count-number" data-to="40" data-speed="1000"></span>+
</p>

引入 jquery 之后再添加下面的 js 代码。

$.fn.countTo = function (a) {
  a = a || {};
  return $(this).each(function () {
    var c = $.extend({},
      $.fn.countTo.defaults, {
        from: $(this).data("from"),
        to: $(this).data("to"),
        speed: $(this).data("speed"),
        refreshInterval: $(this).data("refresh-interval"),
        decimals: $(this).data("decimals")
      }, a);
    var h = Math.ceil(c.speed / c.refreshInterval),
      i = (c.to - c.from) / h;
    var j = this,
      f = $(this),
      e = 0,
      g = c.from,
      d = f.data("countTo") || {};
    f.data("countTo", d);
    if (d.interval) {
      clearInterval(d.interval)
    }
    d.interval = setInterval(k, c.refreshInterval);
    b(g);

    function k() {
      g += i;
      e++;
      b(g);
      if (typeof (c.onUpdate) == "function") {
        c.onUpdate.call(j, g)
      }
      if (e >= h) {
        f.removeData("countTo");
        clearInterval(d.interval);
        g = c.to;
        if (typeof (c.onComplete) == "function") {
          c.onComplete.call(j, g)
        }
      }
    }

    function b(m) {
      var l = c.formatter.call(j, m, c);
      f.html(l)
    }
  })
};
$.fn.countTo.defaults = {
  from: 0,
  to: 0,
  speed: 1000,
  refreshInterval: 100,
  decimals: 0,
  formatter: formatter,
  onUpdate: "null,
  onComplete: null
};

function formatter(b, a) {
  return b.toFixed(0)
}
$("#count-number").data("countToOptions", {
  formatter: function (b, a) {
    return b.toFixed(0).replace(/\B(?=(?:\d{3})+(?!\d))/g, ",")
  }
});
$(".num-ber").each(count);

function count(a) {
  var b = $(this);
  a = $.extend({},
    a || {},
    b.data("countToOptions") || {});
  b.countTo(a)
};

npm install 报错和取消本地代理的问题

2021-08-19 08:00:00

npm install下载依赖包的时候出现下面错误,请求失败。

npm ERR! FetchError: request to https://registry.npm.taobao.org/cnpm failed, reason: connect ECONNREFUSED 127.0.0.1:1181

调试过后发现是本地代理的问题,取消它。

npm config delete proxy
npm config delete https-proxy

npm install 成功运行,完毕。

关于我为什么要写博客?

2021-07-21 08:00:00

关于这个博客的构成

这个就放在最前面吗,算是起到一个统计整理的作用。

博客基础的框架使用是 hugo, 前端样式是自己慢慢构思的,可能是因为没有设计师的天分吧,构思出来的 UI 就很奇怪。

我也尽量将色彩往黑白上靠,就是为了避免色彩的冲突造成阅读的不适,以文字为主体,也可以将我的重心转移到写文章上来。

然后通过本地渲染构建了 public 静态文件,我将这些文件推送到服务器上,服务器的配置是最基础的 1h2g,不过已经完全可以满足个人正常使用了,这样可以提升访问速度。同时的话,我在 Github 备份了源码,这样就不用害怕如果有什么突发事件。

而且,通过 Github,我在 vercel 也构建了一个备份网站,基于 rope 可以直接生成一个项目网站,也是非常实用。

目前这个博客主题我并没有将其分离起来,一是不想专门维护一个开源项目,二是我也很难保证其稳定性,隔三差五我都会优化或者改变整体布局,如果作为一个开源项目的话,这毫无疑问是不合格的。

如果你想使用同款主题,可以前往仓库 https://github.com/dlzmoe/blog,这是博客全部的源码。

你可以把 themes 文件夹下的代码 copy 走。或者直接 fork 仓库,然后把 /content 内的文章删掉,换成你自己的。


2021.07.21 落笔

写这篇文章,大概是想回顾一下自己从写第一篇博客,一直到现在的心理历程吧!但是动笔的时候,又突然改变想法了。决定好好审视一下自己的内心,“我为什么要写博客?”

伊始

故事最开始源于 2020 年五月份,当时我正频临毕业之际,正处于找工作的状态,对前途还是有些迷茫的,不知道未来该怎么走,往哪方面发展,再加上对自己不够自信,可能有些焦虑吧。

但是不论心态怎么变化,生活总是要继续的,在闲暇之余我浏览很多技术网站,希望可以提升自己,当时我比较热衷于 csdn 和博客园吧,两个比较大的中文博客网站,不论是学习和查资料,百度出来的内容基本都是这两个网站的文章。然后在平时学习累计的过程中,我也会写一些笔记或者总结的内容,开始我是直接存放在本地,后来我放在 Gayhub 上面。

Gayhub: 又名,Github. 全球最大的同性交友网站,在这里你可以畅快的交流技术,copy code。

但是上过这个网站的人都懂,它的速度是多么的令人着急。虽然后来学会了一些科学上网的方法,但是还是很不爽,所以我把目光投向了技术交流平台,论坛社区。我开始试着在把笔记放在 csdn 上面,当做博客发布出去,至今还保留一些浅显易懂的文章在我的 csdn 账号上,不过许久没有登陆了。

接触

有一天,一个做前端的朋友给我推荐了一本书,叫做《Web 全栈工程师的自我修养》,作者是余果,之前我还写过一篇读后感,感兴趣的话也可以读一下,《对全栈的一些思考》

挺好的一本书,也很畅销,读完电子版的,我还专门为此买了一本纸质的书,表示支持一下作者。

然后了解到作者在大学时期就开始写个人博客,在深入了解过程中,我觉得他的思想真的不错,写的内容也很有深度,虽然很多文章跟技术无关,但是不可否认是好文章。(现在他已经转到微信公告号了,有兴趣搜一下:余果的专栏)。然后我就对他挺感兴趣的,以及对于个人博客这件事也突然有了更多的兴趣。

我感觉这是一个自己的私人领域,可以自由发挥,更像是自己盖了一栋房子,至于房子盖成什么样,就看自己的能力了。

一开始我选择的是 hexo 静态博客,他有很多优势,免费,部署快,可以不断的白嫖 github 仓库存储,包括文章,图片等,只要你愿意折腾,总有办法可以解决。

然后还有很多大佬开发了各种各样的插件,还有各种第三方平台。hexo 官网的主题也是相当丰富,都是个人开发者做出来,总能找到自己喜欢的那一款。

为此我还开发一款轻量的纯文字主题,simple99,感兴趣的话可以在官方列表搜索到,这里就不贴链接了,有夹带私货的嫌疑。

之后,我就把自己之前写的笔记都放在了这个博客中,部署之后自己查看的时候,也挺有成就感的。

本质

隔了两个月,开始继续更新这篇博客,接下来我会持续讲述自己的道路…

不知不觉已经写了一年的博客,从最开始兴奋,冲动,到现在的平息了内心的悸动,这一场心理历程,其实走的颇为坎坷。

中间有一段时间,频繁更换博客程序,hexo, hugo, halo, wordpress, typecho

最后终于确定了 hugo,一方面因为它是静态的系统,不需要服务器和数据库,可以随意在任何支持的平台部署(虽然最后我还是为了速度,将它部署在服务器上),而且它渲染的速度相当快,当文章越来越多时,使用 hexo 会渲染 10 多秒,让我感到很难受,因此我选择了速度更快的 hugo。

度过那段时间后,我感觉自己内心平静了很多,不会再为这些事情而折腾,处于一个平静期的状态。我想,这大概就是每个博主都要经历的阶段吧,抛去花里胡哨的外表,直击博客的本质。

我把它总结出四个字 — 记录、分享。

现在我认识很多新的博主,大部分都是刚刚接触,内心感官就是新奇,渴望部署一个让自己满意的博客,我都会指点一些细节和心得,也很开心。毕竟,在这个博客衰败,碎片化信息的时代,还有人能喜欢这个圈子,这确让人感到开心。

我会鼓励他们自己动手解决问题,然后进行思考,得出结果,这无疑是很令各位博主有成就感的。

有时候我也会写一些教程,或者开发的小插件分享出去,有需要的伙伴就可以到搜索引擎输入关键字,或许可以看到我的文章,这也让我感到荣幸。

反思

很多时候,我都会审视自己,在这个时代要不要继续坚持下去写博客,大部分人做的都是公众号,自媒体等等。这也是时代的趋势。然后我自己慢慢思考,拨开云雾见光明,我发现我想要的不是自媒体那样的感觉,我更渴望拥有自己的一片净土,偶尔放一些新奇的玩意,然后写写自己的生活记录和一些技术笔记。

我知道自己的水平远达不到大神的地步,单纯的想写点东西。仅此而已,不为其他。

将数据库中的图片信息导出并调用

2021-07-09 08:00:00

接口链接:http://121.196.166.173/img/img.php 展示 demo:http://121.196.166.173/img

前言

为了写博客以及其他的一些用途,我使用服务器搭建了一个连接 github 仓库的上传网页,并且在数据库中记录上传的信息,比如缩略名、时间戳和图片链接。分别对应 GitHub 仓库中的图片,但是后期我发现在 GitHub 查看图片非常别扭,因此我打算写一个可展示图片的网页,把 GitHub 仓库中的图片通过链接展示出来,当然我们不可能一张张复制,还好有数据库。

看一下具体内容

写一个接口

很好,拥有我们需要的字段。事不宜迟,直接开动,下面是完成的接口代码。

<?php

header('Content-Type:application/json; charset=utf-8');
header("Access-Control-Allow-Origin:*");

$servername = "localhost";
$username = "imgbed";
$password = "imgbed";
$dbname = "imgbed";

// 创建连接
$conn = new mysqli($servername, $username, $password, $dbname);
 
// 检测连接
if ($conn->connect_error) {
    die("连接失败:" . $conn->connect_error);
}

// imgmd5 名称转 md5
// imguploadtime 上传时间戳
// imgurl 链接
// 上传 ip
$sql = "select imgmd5,imguploadtime,imgurl,imguploadip from remote_imgs
";
$result = $conn->query($sql);


if ($result->num_rows > 0) {
    // 输出数据
    while($row = $result->fetch_assoc()) {
        
    $data[]=$row;
    
    }
    
    $json = json_encode($data,JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);//把数据转换为 JSON 数据。
    
    exit($json) ;

} else {
    echo "未查询到结果!";
}

$conn->close();

?>

分别将缩略名,时间戳,图片链接,和上传 ip 通过 json 格式导出,非常完美。接口链接:http://121.196.166.173/img/img.php

使用 ajax 进行调用

然后只需要在前端将接口调用,然后简单写一个页面即可,下面是 html 代码,,通过 ajax 调用。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图床</title>
    <style>
        .container {
            max-width: 1000px;
            margin: 40px auto;
            display: flex;
            flex-wrap: wrap;
            justify-content: space-between;
        }
        .item {
            width:300px;
            /* height: 300px; */
            overflow: hidden;
            border: 2px solid #bbb;
            margin-bottom: 24px;
        }

        .item a{
            display: block;
            width: 300px;
            /* height: 300px; */
            overflow: hidden;
        }
        .item img{
            max-width: 300px;
            max-height: 300px;
        }
    </style>
</head>

<body>
    <div class="container"></div>
    <script src="https://cdn.zburu.com/list/jquery3.6.0.js"></script>
    <script>
        var str = '';
        $.ajax({
            url: 'http://121.196.166.173/img/img.php',
            type: 'get',
            dataType: 'json',
            async: false,
            success: function (data) {
                $.each(data, function (i, item) {
                    console.log(item)
                    list = "<div class='item'><p>缩略名:" + item.imgmd5 + "</p>" +
                        "<p>时间戳:" + item.imguploadtime + "</p>" +
                        "<a target='_blank' href='"+ item.imgurl +"'><img src='" + item.imgurl + "'></a>" +
                        "<p>上传 ip:" + item.imguploadip + "</p></div>"

                    str += list;
                }),
                $(".container").html(str);
                console.log('数据请求成功')

            },
            error: function () {
                console.log('数据请求失败')
            }

        });
    </script>
</body>

</html>

最后我把这个页面传到我的服务器当中,可以看一下效果 http://121.196.166.173/img

时间仓促,我也没使用更好的 ui 进行优化,只是大致写一下这个过程,待到以后具体使用时,我会对这个展示图片的页面进一步优化。

冷知识世界新增一个大洋变成五大洋

2021-06-10 08:00:00

从 6 月 8 日世界海洋日开始,南大洋将被认定为世界第五大洋。世界上就有七大洲,五大洋啦!中学课文需要改一改了。

美国国家地理学会宣布,南极洲周围海域将被称为南大洋,并正式承认南大洋为地球第五大洋。

南大洋是海洋生态系统的重要家园,也是南半球的焦点。它直接包围着南极洲,从大陆的海岸线一直延伸到南纬 60 度。

海洋的边界与其他四个大洋中的三个相连——大西洋、印度洋和太平洋。但南大洋与其它大洋的不同之处在于,它在很大程度上不是由周围的陆地决定的,而是由于内部有一股洋流,这片水域很独特。

在 vue 中使用 axios 调用数据

2021-06-08 08:00:00

如何在 vue 中完成一次接口的调用?首选就是 axios,方便快捷又好用,支持各种 api ,封装也很方便。

先用 node 安装一下。

npm install axios

然后在 main.js 文件中引入。

import axios from 'axios'
import VueAxios from 'vue-axios'

Vue.use(VueAxios, axios)

以我的一个工程目录作为实例,看一下代码。

// ListThere.vue

// html 代码
...
<div>{{ info }}</div>
...

// js 代码
import axios from "axios";

export default {
  name: "ListThere",
  data() {
    return {
      ...
    };
  },
  components: {
    ...
  },
  methods: {
    ...
  },
  mounted() {
    axios
      .get("http://api.h-camel.com/api?mod=interview&ctr=issues&act=today")
      .then((response) => (this.info = response))
  },
};

数据成功的被取到,然后需要将它在页面中展示出来。使用 vue 自带的 v-for 列表渲染。

<ul class="list-api-show">
  <li v-for="(item, index) in show" :key="index">
    {{ item.title }}
  </li>
</ul>

然后通过 axios 把数据指向 v-for.

mounted() {
  axios
    // ...
    .then((response) => (this.show = response.data.result.today))
}

数据成功取出然后展示在 v-for 里面。

当然了,这是成功的情况,加入失败了呢?我们需要浏览器给出提示,所以 axios 给出了一个 api — err 语法

mounted() {
  axios
    // ...
    .catch(function (error) {
      if (error.response) {
        console.log(error.response.data);
        console.log(error.response.status);
        console.log(error.response.headers);
        console.log('err')
      } else if (error.request) {
        console.log(error.request);
        console.log('err')
      } else {
        console.log('Error', error.message);
        console.log('err')
      }
      console.log(error.config);
      console.log('err')
    });
}

如果接口有问题,或者我们调用时代码写错了,都会给出报错提示,具体什么样的错误会有什么样的警告,需要我们一一去经历摸索。

宝塔如何实现进程守护?

2021-05-30 08:00:00

为了方便写一些东西,pc 端和手机端都可以同步,我用 leanote 搭建了一个在线笔记,但是必须在服务器后台运行一个指令

base run.sh

不然就访问不了端口,我也不可能时刻在终端保持指令运行,所以就需要把指令放在服务器后台,让它自己保持运行,刚好有这么一插件 Supervisor管理器 2.2

是一款免费的 Python 插件,点击下载即可,然后进入设置。

点击添加进程守护。

把进程环境目录和启动命令都填好,名称就随便写个自己能分辨出来的就行,最后点击确定。

让它一直保持启动状态就可以,其他的不用动,以后就不用打开服务器终端输入指令了,直接在浏览器访问端口,就可以访问网页。

如果为了更方便,做个反代处理即可。

css 中 fixed 定位属性和动画的冲突问题及解决方法

2021-05-27 08:00:00

1.问题

css 中使用动画属性会和同标签下的 fixed 属性冲突,导致定位失效,那么该如何解决他呢?

2.分析

简单说一下问题产生的背景,昨天夜里我想给我的博客页面做一个简单的动画,浏览器刷新的时候从下往上渐现的效果,代码如下:

/* index-container 类名是页面的主体部分 */
.index-container {
	opacity: 0;
	animation-name: index;
	animation-duration: 0.7s;
	animation-fill-mode: forwards;
}

/* 一个简单的动画实现 */
@keyframes index {
	0% {
		transform: translateY(100px);
		opacity: 0;
}
	100% {
		transform: translateY(0);
		opacity: 1;
	}
}

运行之后发现,动画可以很好的显示,但是当我翻阅文章详情的时候发现,目录固定失效了。

按照原来的设置是当页面往下滑动的时候,目录会紧贴浏览器的顶部,方便跳转和查看目录,但是现在它已经不能紧贴了。看一下浏览器设置,属性确实生效了,但是页面并没有显示我们想要的结果。

之前我写目录固定事件的时候用的是 scroll 事件,然后昨天写动画,绑定的 onload 事件,我初步判断是两个 window 冲突了,导致第二个失效,所以我就改了一种写法,用 addEventListener 事件监听执行他们,但最终无论怎么写,目录固定始终不能生效。

昨天熬得太晚了,就没管它放在那了,今天一早我就去张鑫旭的网站查了一下,果然有这么一个问题。

参考文章:https://www.zhangxinxu.com/wordpress/2015/05/css3-transform-affect/

但是文章只是介绍了问题产生的原因,并没有明确给出一个好的解决办法。

3.方法

虽然没有明确的方法,但是给了我一个思路,因为动画中的一些属性,比如 scaletranslate 等,会造车容器的宽高重新计算,而 fixed 属性则依赖于规定的一个像素值,所以当执行动画的时候,页面的高度发生了变化,所以 fixed 不能安心的执行自己的任务了。

原因找到了,方法自然而然就出来了,看图(图很丑,手画的,能理解意思就行)

这是我最开始的布局,我将动画绑定给 红色框 的容器了,而目录就在红色框内,所以失效。

现在我重新布局一下,

目录依旧处于侧边,但是我将他和通常的侧边栏分开成两个容器了,现在我把动画绑定给 主体部分侧边栏 ,这样目录就和绑定动画的标签分开了。

再到页面测试,发现没有任何问题,动画效果和目录固定互不影响。

很快乐,又可以愉快地折腾了!

Oracle 数据库安装教程(超详细)

2021-05-13 08:00:00

转载自:https://www.moeor.com/26.html

前言

本教程讲解 Oracle Database Express Edition (XE) Release 11.2.0.2.0 (11gR2) 版本的下载安装教程!

该版本是一个便携式安装版本,直接安装,就可以了,非常方便。

由于官网的更新,找不到以前那种提供客户端服务端的安装了,所以这里不再讲解!

准备

访问 Oracle 官网下载该版本数据库。下载时需要登录账号,可百度搜索共享的 Oracle 账号!

下载地址:https://www.oracle.com/database/technologies/xe-prior-releases.html

选择适合自己的系统版本,本文以 Win10 为例。建议使用下载工具或者科学上网,下载更快!

安装 Oracle

下载后解压缩,双击打开文件夹中的“setup.exe" 安装程序,等待程序启动,进入安装程序。

开始安装,单击“下一步”

选择“我接受本许可协议中的条款”,然后单击“下一步”。

这一步是选择安装位置,由于文件程序比较大,建议安装 D 盘。新建一个 Oracle 文件夹,点击“浏览”,选择文件夹,注意查看目标文件夹,文件路径,然后单击“下一步”。

设置数据库的登录口令,即数据库密码,建议简单一点,比如:root,然后单击“下一步”。

单击“安装”,开始安装。

安装过程比较慢,耐心等待安装完成!

安装完成后,单击“完成”,即 Oracle 11gR2 已经安装完成了。

安装完成会在桌面新建一个图标,打开可以通过以 web 形式访问数据库。

双击打开,可能会弹出以下错误提示,这个是没有影响的,安装完成后重启一点电脑就可以了!

验证数据库

选择桌面生成的图标

鼠标右键打开文件所在位置,弹出文件夹,往上翻,在文件夹中找到 bin 文件夹。

双击,进入 bin 文件夹,往下翻,找到 sqlplus.exe 程序。

双击运行。

输入用户名:system(统一默认都是 system),回车

输入口令:(刚才安装时设置的口令),口令输入是不显示的,直接回车就行了。

若出现“连接到:Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production”语句时,则安装成功!

Typecho 博客搭建及优化教程 (详细)

2021-05-11 08:00:00

1.前言

本文从Typecho 简介开始讲解,其中包括服务器的选择,以及域名注册,服务器系统选择,宝塔安装,环境配置,Typecho 安装包括中间的防采坑注意事项,为您提供完整的保姆级搭建流程,请珍惜哦!

如果不喜欢前面的啰嗦讲解,请跳过,直接从宝塔安装开始。

不喜勿喷,谢谢!

需要买服务器可以 点击链接在腾讯云购买服务器有优惠哦~ 不嫖白不嫖,省个十几二十块钱不香吗?

2.Typecho 简介

Typecho 基于 PHP5 开发,支持多种数据库,是一款内核强健﹑扩展方便﹑体验友好﹑运行流畅的轻量级开源博客程序。

Typecho 非常简洁,仅仅 7 张数据表,加上不足 400KB 的代码,就实现了完整的插件与模板机制。超低的 CPU 和内存使用率,足以发挥主机的最高性能。

原生支持 Markdown 语法,易读更易写。支持 BAE/GAE/SAE 等各类云主机,即使面对突如其来的高访问量,也能轻松应对。

Typecho 的默认模板和后台,全部采用了响应式设计。所以 Typecho 的大多数自制模板,都采用了自适应设计。

现在Typecho 拥有很多用户,完整的生态,各种功能插件和简洁优美的主题,是做博客的不二选择,相信你会喜欢上她简洁的风格!

3.准备

服务器 + 域名

搭建Typecho 博客需要使用服务器或者虚拟机,最近也看到一篇使用 Vercel+Mysql 搭建 Typecho 的文章,但是这种是使用外部 Mysql 服务的,相比使用服务器或者虚拟机这种还是不太方便配置和管理的。使用服务器相比虚拟机会更加的好用。

**注:**本文只讲解服务器的搭建流程,虚拟机请参考!

**服务器选择:**推荐去腾讯云或阿里云,有学生机(9 元/月)可供选择,腾讯云还是 25 岁以下免学生认证的,学生价续费三次,所以有条件可以选择 1 年购买和续费,腾讯的还有免费升配置,,阿里云的学生机下架了,但是也可以买为新用户提供的,建议选择 轻量应用服务器,1h2g 的配置完全够用,流量也完全够用。

腾讯云:https://cloud.tencent.com/act/campus

阿里云:https://developer.aliyun.com/plan/grow-up

如果你买不了这种,也可以去其他服务商买新用户优惠的服务器,建议选择大厂。

选择服务器要看下地区,如果是大陆地区的服务器,需要去其对应的服务商进行域名备案,否者你无法使用,如果是香港地区或者国外的服务器,则不需要进行备案。

**注:**阿里和腾讯这两种轻量应用服务器都是需要 备案的,如果嫌麻烦或者不想备案,可选择其他地区的服务器!

备案操作有些复杂,也需要好几天的时间,但是备案也有备案的好处,比如使用国内的 CDN 加速等。另请仔细参考各服务商提供的流程,要注意看清备案流程上的注意事项,防止备案驳回浪费时间。

相比国外和香港的服务器,使用大陆的服务器在国内的访问速度是最好的,当然如果你搞外国业务当我没说。。。

**域名选择:**域名选择很简单,去各大服务商注册一个就完事了,喜欢什么就注册什么,但是如果你要备案使用大陆服务器,一定不要注册不支持备案的域名,当然绝大多数都是支持备案的。另各大服务商也有新用户注册域名优惠活动,在百度仔细找一下就行了。 腾讯云:https://cloud.tencent.com/act/domainsales

阿里云:https://wanwang.aliyun.com/domain

华为云:https://activity.huaweicloud.com/domain1.html

下面开始搭建流程:

4.LNMP 环境

首先我们要搭建的就是 LNMP 环境,LNMP 代表的就是:Linux 系统下 Nginx+MySQL+PHP 这种网站服务器架构。

Linux 是一类 Unix 计算机操作系统的统称,是目前最流行的免费操作系统。代表版本有:debian、centos、ubuntu、fedora、gentoo 等。

Nginx 是一个高性能的 HTTP 和反向代理 web 服务器,同时也提供了 IMAP/POP3/SMTP 服务。

Mysql 是一款安全、跨平台、高效的,并与 PHPJava 等主流编程语言紧密结合的数据库系统。

PHP 是一种在服务器端执行的嵌入 HTML 文档的脚本语言。

当然如果你不喜欢使用 Nginx,或者 Mysql,请自行更换其它的,这里不过多介绍!

Linux 系统的话,本文使用 Centos 8 系统讲解。

搭建 LNMP 环境,我们可以选择自行下载对应的安装包,然后在 Centos 系统中,安装NginxMysqlPHP等,这种纯动手的方式,非常麻烦和复杂,在后期进行多网站管理和配置 HTTPS 证书,以及网站伪静态配置时非常麻烦。

所以本文讲解使用宝塔面板来进行安装部署环境!

5.宝塔是什么

宝塔面板是一款服务器管理软件,支持 windows 和 linux 系统,可以通过 Web 端轻松管理服务器,提升运维效率。例如:创建管理网站、FTP、数据库,拥有可视化文件管理器,可视化软件管理器,可视化 CPU、内存、流量监控图表,计划任务等功能。

宝塔面板拥有极速方便的一键配置与管理,可一键配置服务器环境(LAMP/LNMP/Tomcat/Node.js),一键部署 SSL,异地备份;提供 SSH 开启关闭服务,SSH 端口更改,禁 ping,防火墙端口放行以及操作日志查看;CPU、内存、磁盘 IO、网络 IO 数据监测,可设置记录保存天数以及任意查看某天数据;

计划任务可按周期添加执行,支持 SHELL 脚本,提供网站、数据库备份以及日志切割,且支持一键备份到又拍云存储空间,或者其他云存储空间里;通过 web 界面就可以轻松管理安装所用的服务器软件,还有实用的扩展插件;集成方便高效的文件管理器,支持上传、下载、打包、解压以及文件编辑查看。

进入搭建正题:

6.安装宝塔

选择服务器时,选择系统镜像,不要选择服务商提供的应用镜像,操作系统选择 Centos,版本的话 7 和 8 都可以。当然你也可以选择其他系统,,,

接着到服务商的控制台,找到你的服务器,进入控制台,重置密码。

然后在安全–》防火墙那里添加规则。

端口范围8888,其他默认,备注随意。

**注:**如果你不添加 8888,安装宝塔以后你是进不去的,安装宝塔以后可以修改宝塔的进入端口,再修改一下这里就可以了!

使用 SSH 工具连接你的服务器,如果没有 SSH 工具也可以使用控制台那里提供的远程连接!

在终端界面输入一下命令开始安装!

Centos 安装命令:

yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh

不同操作系统有不同的安装命令,详情请去宝塔官网查看!

遇到安装提示直接输入:y

安装完成后如下:

在浏览器中访问外网面板地址。

**注:**如果你刚才没有在服务器控制台安全防火墙那里添加端口 8888 是打不开的。

使用 username 和 password 登录宝塔面板,点击我已阅读并同意“《用户协议》”后进入宝塔面板!

进入宝塔面板后,会弹出一键安装环境套件选择界面,这时我们选择左边的 LNMP 环境!

Nginx 1.18,Mysql 5.6(如果服务器内存在 2g 以上可以选择 5.7),PHP 7.4(php 建议版本选择 7.0 以上),Pure-Ftpd 1.0.49,phpMyAdmin 5.0(这是一个可以在浏览器页面管理数据库的软件程序,要在服务器控制台安全防火墙那里添加端口 888,否则无法打开)

安装方式有两个,极速安装(安装速度快,节省时间),编译安装(安装速度超级慢,有时会卡死,但会提升服务器性能),可自行选择。本文以极速安装为例!

安装过程:

安装完成后,会提示绑定宝塔账号,点击免费注册,去注册一个,然后绑定就可以了!

**说明:**建议,安装完成后,左侧,面板设置,设置下面板的登录信息,修改一下登录入口,用户名,密码和端口。 注: 如果修改面板设置中的端口,也要在服务器控制台安全防火墙那里修改端口。

7.安装 Typecho

访问Typecho 官网,点击立即下载,点击下载 1.1 正式版。

下载完成后,得到一个 1.1-17.10.30-release.tar.gz 压缩包,解压后可以看到里面有一个 build 文件夹,文件夹里面便是 Typecho 程序文件。

先域名解析一下,去你的域名服务商,控制台找到你的域名,解析,添加记录。主机记录填写@,www 或者 blog 其他的都可以(你填写的是什么就要以什么访问,比如填写 www 便是 www.xxx.com 等),记录类型 A 类型,记录值填写自己服务器的 IP 地址(如果不知道,可查看宝塔页面的左上角),确认就可以了!

打开宝塔页面,左侧–》网站–》添加站点

添加域名,数据库选择 Mysql utf-8,填写数据库账号和密码以及对应的 PHP 版本,然后提交。

这里可以先设置一下站点,点击站点右侧的设置,需要设置的有伪静态,SSL 证书,,,

伪静态选择 typecho,然后保存。

SSL 证书,选择宝塔 SSL 证书,申请证书,填写相关信息,提交资料等申请成功就可以了。

如果申请失败,可以去域名服务商那里,有提供单域名免费 SSL 证书,然后选择其他证书,上传证书,开启右上角的强制 HTTPs 就可以了。

关闭设置窗口,点击根目录下的路径地址**/www/wwwroot/你的域名**,直接进入根目录文件夹,删除 index.html 文件。

点击上传,弹出窗口,点击上传文件,找到下载的 Typecho 压缩包,打开,然后开始上传,上传完毕关闭窗口。

解压该压缩包后(删除压缩包),得到 build 文件夹,进入其文件夹内,选择文件名旁边的框,全选文件,右上角选择剪贴,然后回到上一级根目录下,黏贴,然后删掉没用的 build 文件夹就可以了。

到这一步,浏览器输入你的域名,就可以安装 Typecho 了。

如有在上方遇到Deprecated: Function get_magic_quotes_gpc() is deprecated in /www/wwwroot/woshishabi.top/var/Typecho/Common.php on line 208报错,不用管,没什么影响。

点击下一步,进入初始化配置,这一步需要填写修改的是数据库用户名(添加站点时设置的数据库账号,如果忘了可去宝塔面板页面左侧,数据库查看),数据库密码,数据库名(数据库名和数据库用户名是一样的!),用户名,登录密码,邮件地址,确认,开始安装。

不出意外,安装成功!

现在再浏览器访问域名,便可以查看你的 Typecho 博客了,当然现在是默认主题,还是比较丑的。

去百度你喜欢的主题下载,然后上传根目录/usr/themes/目录里面,在后台外观设置中更换。各主题教程以及配置教程都有主题文档。

插件安装是将下载的插件上传根目录/usr/plugin/ 目录里面,在后台设置中启用插件,设置插件。

你的后台面板地址就是你的域名/admin,用设置的用户名和密码登录就可以了!

基本的安装到此为止了,下面说一些优化问题!

8.优化

**永久链接:**对文章和页面进行重写地址,可以去掉地址栏中的 index.php,对 seo 也是很友好的。

启用重写功能,选择文章路径,选择你喜欢的风格,也可以自定义风格,下面有可选参数,本站使用的是个性化定义 /{cid}.html 。然后保存设置,会出现(重写功能检测失败,请检查你的服务器设置,如果你仍然想启用此功能,请勾选这里。),勾选这里,再次点击保存设置就可以了。

网站引入 iframe 视频,如何实现高度自适应?

2021-05-09 08:00:00

我想很多小伙伴在写博客的时候都能遇到一个令人头疼的问题,某一篇文章想用 iframe 引入一个外链视频,但是大小总是有问题,宽度可以固定到 100% ,但是高度比如用实际高度来表示,比如 100px, 200px,诸如此类。

但是问题来了,在不同的页面宽度下,视频的高度是一致的,就会导致一个很麻烦的问题,

看下面两个图,

pc 端

移动端

很明显,在 pc 端正常显示的视频,放到移动端高度就错位了,很不美观。

解决这个问题,可以同媒体查询,但是显然费时费力,还不那么完美。

其实很简单,只需要不到 10 行代码即可完美实现。

  1. 引入 jq,(一般网站默认都有这个文件)

  2. 加上一段 js 代码,最好放在网站底部, </body> 前即可。

$('iframe').wrap('<p class="iframe"></p>')
  1. 在 css 文件的底部加上:
.iframe{
  position: relative;
  padding-bottom: 56.25%;
  height: 0;
  overflow: hidden;
}
.iframe iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

这个时候再访问带有 iframe 视频的网页,不管宽度如何变化,高度可以随视频自适应。

例如这个网页,可以看一下效果:https://imhan.cn/posts/20210507.html

呼吸之野

2021-05-07 08:00:00

2021 许嵩第 8 张全创作专辑《呼吸之野》首支曲目《乌鸦》5 月 7 日正式发行。

《乌鸦》

我们都曾领受过误解、怀疑、孤立,都品尝过爱而不得或种种挫败的滋味,都有过觉得“一切都糟透了”的时候……但感伤过后,却还是会相信“一切都会好起来的”,还是会为了反转剧情而振作前行——也许是为了心里最在乎的人,也许是因为心底笃定的信念。被生活不断刮蹭的脆弱羽翼之下,有着强韧的骨架。许嵩以从未讨喜的乌鸦为第一视角,飞抵呼吸之野。

人生不过一场呼吸。呼吸不停,则生命与思考不止。当人们习惯于蒙上口罩生活,畅快呼吸日渐成为一种奢侈。比这更为奢侈的,是袒露真心的酣畅对谈,是呼吸于野,吐纳心声。《呼吸之野》从冷色调里起笔,以自如沉着的笔触,描摹情感与境遇。十首音乐,如十场呼吸冥想般令人全神贯注。许嵩在想象与写实的互照里,在温柔与凛冽的交汇处,在浪漫与生猛的切换中,记述着生活与思考。

在《呼吸之野》里,古典气质与当代元素浑然一体。许嵩构建了有别于其过往作品的音乐语汇,有承接亦有创新。《呼吸之野》是赤诚、饱满且充满细节的,它能够让人在专注聆听时回归内心深处。

为拍摄专辑平面相片及音乐录影带,许嵩与二十余人拍摄团队进驻高原上人迹罕至的原始森林。帐篷帘幕外,旷野篝火,雪山江河,独行旅人,构成一幅冷暖交汇的静谧画卷。

《呼吸之野》专辑 10 首曲目将于 5 月 7 日 -5 月 14 日陆续上线!

《假摔》

暴雪天的十字街头,车来人往川流不息,却如临旷野。

歌里的故事也许是真实发生的生活影像,也许是虚构的文本创作,但究竟是实是虚,就像歌里的“真摔”与“假摔”一样,真假虚实在裁判们心里有自己的答案。而切换到故事里主人公的角度来看,摔过的跟头吃过的苦,只有自己知道,很难期待无关人士的理解。

《假摔》是印有许嵩独特标记的叙事性写作,极短的篇幅寥寥数笔就勾勒出故事轮廓。如同一个诚恳而又情绪饱满的目击者,把你带去故事里的现场,却不直接留下他的主观评价,只由你自行体会。故事现场的“假摔”与球场、与人生赛场上的“假摔”互为映照,有着极大的玩味空间。

音乐方面,电子音色铺陈出冰天雪地氛围,生猛鼓点像钝器般无情敲击,副歌的短旋律乐句不断往复,以及电子摇滚曲风经典的节奏型,这些元素共同营造出极寒雪地的麻木感。后半段步步推进至顶点,直到一声放肆呼喊与一声沉闷的“砰”,猛地击中人心后戛然而止。《假摔》既有关怀的温度,也有反思的冷峻,它在冷暖交汇处,不断引发着心里的强降雪。

《冰柜》

许嵩 + 黄龄

当深情与决绝相互碰撞

既是窜动的电光石火

也是致命的寒气冰霜

《冰柜》以惊悚悬疑电影为灵感原点,推演至情感关系的探讨。表层是充满戏剧张力的冷峻叙事,内里是丰富饱满的寓意,内外同步推进。与「惊鸿一面」的黄龄在七年之后再度合唱,默契诠释歌中角色与情感。

《冰柜》由许嵩创作及制作。迷离的电子乐音色营造出扑朔迷离的诡谲气氛,影像感十足,引人入胜。歌曲在幽微的呼吸声中起始,随后二人交替着不疾不徐地抛出意象,到副歌时揭开面纱,接着以奇诡的 Bridge 段落,一步步将气氛推至高潮。

《科幻》

脑海里 记忆与幻想交织 亦真亦幻 爱在散场之后 才给出关于爱的答案 ——我们把美满 托付「科幻」

2021 许嵩第 8 张全创作专辑「呼吸之野」 第四支曲目「科幻」5 月 13 日正式发行

歌曲从星云浩渺的氛围和隐约的电子频率声里起始,古典气息和 R&B 节拍梦幻联动,让心神跟随许嵩感而不伤的真挚演绎,飘忽在回忆的宇宙中。

像一场返璞归真的童梦,像一则年少的浪漫寓言。爱之得失,在他的歌与诗里聚合成辽阔原野。聆听「科幻」,一切都在脑海,一切又都已抛之脑后。

结尾咚咚的鼓声,如心跳不停,如呼吸不止,如爱一直延续。

发行完毕

人生不过一场呼吸。呼吸不停,则生命与思考不止。当人们习惯于蒙上口罩生活,畅快呼吸日渐成为一种奢侈。比这更为奢侈的,是袒露真心的酣畅对谈,是呼吸于野,吐纳心声。

《呼吸之野》从冷色调里起笔,以自如沉着的笔触,描摹情感与境遇。十首音乐,如十场呼吸冥想般令人全神贯注。许嵩在想象与写实的互照里,在温柔与凛冽的交汇处,在浪漫与生猛的切换中,记述着生活与思考。

在《呼吸之野》里,古典气质与当代元素浑然一体。许嵩构建了有别于其过往作品的音乐语汇,有承接亦有创新。《呼吸之野》是赤诚、饱满且充满细节的,它能够让人在专注聆听时回归内心深处。

为拍摄专辑平面相片及音乐录影带,许嵩与二十余人拍摄团队进驻高原上人迹罕至的原始森林。帐篷帘幕外,旷野篝火,雪山江河,独行旅人,构成一幅冷暖交汇的静谧画卷。

呼,向外输出,吸,向内滋长,一呼一吸间,许嵩始终坚持用作品与听众进行交互。自 2009 年发行首张专辑至今 12 年里,许嵩已一人作词、作曲、演唱、制作、策划 8 张个人专辑以及近 50 首原创单曲,佳作不断。2019 年举办“寻宝游戏”巡回演唱会大获好评后,许嵩折返到潜心创作的生活状态,以新作《呼吸之野》再一次展现了强大的创作能量。

基耶斯洛夫斯基和他的现实

2021-04-24 08:00:00

由于博友不在维护博客,导致图片链接丢失,仅以此为纪念。 2023-06-02


文章作者:江江_Jan
文章链接:https://blog.jannns.com/posts/Kieslowski-and-his-reality/
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 江卮可乐!

前言

克日什托夫·基耶斯洛夫斯基(以下简称基氏),20 世纪波兰著名导演,其作品**《蓝白红三部曲》(也称三色系列),被许多评论家视为电影史的巅峰。**

比起身份上电影人的定义,基氏本人更像是一个哲学家、斗争者。他立足于现实生活,擅长用冷静陈述的方式表达对世界的感知,热衷于揭露生活的残酷本质和人性的阴暗内里,并甘愿为【自由的表达】和外界抗衡。

**我对基氏作品的感知是客观且冷静(甚至有点冷漠)。**他的叙事似乎不带任何感情色彩,但回味起来却能感受到他对生命的热爱。

基氏的电影没有二元的善恶对错。残暴的杀人犯可以是为妹妹之死隐忍数年的无助青年(《杀人短片》);权威物理教授会因为相信科学而亲手葬送自己的幼子(《十诫:第一诫》);少年会因为精神爱情的破灭而赴死(《爱情短片》);深爱多年的人是否能因为被证实无血缘而在一起(《十诫:父女情深》)……一些人生困境真实摆在眼前时,比起一些脸谱化的选择,我认为基氏的电影更接近生活的本质,也可能是我个人认同的生活本质。

总之他的电影蛮有意思的,值得挖掘的东西也很多,自己最近沉迷其中无法自拔。

基耶斯洛夫斯基如是说

第一次看基耶斯洛夫斯基的电影是《蓝白红三部曲》,四年前,马哲课老师的推荐。看完感觉三观都没了。女主为什么毁了丈夫遗作却念念不忘?她怎么可以这么对待默默爱她那么多年的人?她为什么对破坏自己婚姻的人那么好?…被一大堆灵魂提问炙烤以后,我默默告诫自己以后不要再看欧洲电影了,超出我的认知范围,感情方面尤其跟不上西方人思路。(当然“不看欧洲电影”这个 flag?,后来因为被其他优秀电影疯狂打脸。)

可能因为叙事方式或者文化背景的原因,欧洲电影没有很强的渲染力(美国大片那种看完就热血沸腾的感觉),它热衷于描绘的都是一些很细小很日常挺东西,无论好的还是不好的都客观的摊在你面前,有时候甚至感觉它的道德边界是模糊的(《两小无猜》令我头大),但回味起来,这些电影表达方式可能戏剧化,但对情感的表述很真实,确定的、不确定的、果断的、犹豫的……它告诉我们生活本来就是残酷的,但也会提示你生活中有很多不容忽视的小美好、值得我们的期待和爱护。

基氏热衷于揭露生活的残酷真相和人性的阴暗面。(可能这也是他电影十分吸引我的原因,我喜欢真实。)他的早期作品都是纪录片,力求反映真实的社会生活,后来因为一些社会原因和本身对纪录片表达方式的失望,他才开始拍摄剧情片。

他所处的时代比较动荡,人们渴望真正的自由。

**他用自己的方式替人们发声,与外部抗衡。**朋友问他为什么不索性做政客去改变环境,他只是表达不适合自己,他在做的是自己应该做的事情。

对他观点感兴趣的朋友可以在去 b 站看纪录片《基耶斯洛夫斯基如是说》。

我比较认同他对人性的看法。

一旦某种特性在人们心中扎了根,很难改变。性善性恶这种论题没有定论,也说不清这个“本”应该追溯哪里,但就未来而言,现在就是本,我比较相信“日久见人心”这个古话,人的本性经不起时间考验。

这是一段废话

突然想到前两年网上疯传的**原生家庭理念,仿佛我们所有的性格缺陷,**只要归在原生家庭的问题上就可以结束了。这个理念让很多人找到了共鸣,大家可以理直气壮的指责原生家庭带给自己的伤害,所以在遇到一些问题时没法很好解决。但这种指责行为只是一种毫无意义的负面情绪宣泄,于家于己都毫无益处。

我从不否认原生家庭会给人影响,《野草莓》里探讨的持续三代的情感疏离,《小偷家族》里典型东方家庭的内敛……个体在遭受重大事件时性格都会受到影响,更何况耳濡目染的家庭。但我觉得原生家庭概念的提出不是为了创造家庭矛盾、或者给自己的问题找开脱,而是给个体更多素材(比如家人互动模式)去找到解决问题的方法。


以下内容含大量剧透及解析,请慎重阅读。

图源豆瓣或自截。


蓝白红三部曲之蓝故事梗概

茱莉在一场意外中失去了丈夫和幼女,不堪回忆烦扰的她选择重新开始生活。清空过去的家、切断和过去人们的联系,但情感方面的东西,始终无法像切断物质关系一样简单,能够清理的干干净净。媒体四处渗透她的生活、想要她续写她丈夫的遗作,丈夫的助手一直爱着他、默默关心着她,最重要的是,她脑海中总是回荡着她和丈夫一起创作的交响乐…一个电视采访让她意外发现这首乐曲和另一个女人有关,并且她还有了丈夫的骨肉……

和贯穿全剧的交响乐相契合,故事也可以分成四个乐章。

第一乐章

常以奏鸣曲或快板形式表现。常常是两个具冲突性的主题交替上演,在最后突出首要主题。

电影开头是一段行车的镜头。基氏通过一家人的默契互动、小女孩玩糖纸的动作、街边玩耍的路人,来表现这一天的平常以及这个家庭的和谐氛围。

突然,车离奇的撞上了路外的树。

第二乐章

复三部曲式或变奏曲,慢板。

女主茱莉的丈夫、女儿,因为车祸和她阴阳两隔。寻死不成,她开始漫长的适应过程。

茱莉变卖了家里的东西,安顿园丁和管家的生活,以决绝的方式和一直对她存有感情的助手道别,自己搬进市区的公寓,想要忘记过去生活的痕迹。

但是不适就像水一样无缝隙的将她包围。过去的生活习惯、一起创作的乐曲、国家要她续写交响曲的请求…人们的互动总是强行把她拉进生活中。

第三乐章

小步舞曲或谐谑曲,中、快板。

茱莉不想与周围世界产生关联,但是她控制不了他人。莫名和同栋公寓的 prostitute 产生了联系以后,她的平静生活慢慢被打破,一次意外看到的电视节目把茱莉彻底逼回现实。助手接受国家委托,续写本已被她销毁的交响曲;丈夫生命中还有另一个女子。原本小心封存的回忆突然就炸开了。她去找助手,责问他凭什么去续写这首乐曲;她去找亡夫的情人,却只能“平静”接受对方已有丈夫骨肉的事实……(女主这段表演真的很精彩)

茱莉重新审视自己的生活、感情。她把准备变卖的房产赠给了丈夫的情人;重新投入作曲,把一直萦绕在脑内的旋律写出来…

第四乐章

终曲。通常是快板,经常采用会选取时或者回旋奏鸣曲式。也就是对主题进行呼应。

电影的最后是本剧的高潮,像走马灯一样回顾了茱莉的遭遇。戴着爱的十字架的男孩突然惊醒、在过去徘徊的母亲永远的沉寂、混乱生活的 prostitute 的沉思、及情人对新生儿的微笑…伴着最终成曲,回顾了茱莉这段人生经历。

导演的布局真的太妙了,每一个要素都紧紧地联系在一起。蓝色基调、交响乐贯穿全剧,既烘托氛围,又表达茱莉沉默背后压抑的感情……

后记

本来想把三色系列都写一遍,但好几遍《蓝》看下来感觉还是需要时间去消化。基耶斯洛夫斯基的电影值得反复看的,不同的人生阶段应该也会有不同的看法。

一人所学有限,若有疏漏,还请大家批评指正。

基于 docsify 搭建一个文档

2021-04-21 08:00:00

一、搭建

docsify 是一个类似 vuepress, gitbook 等静态文档程序,但是对比之前,个人还是喜欢 docsify 的风格和源码。不想要那么多附带的功能,能满足写文章,目录的功能就行了。

打开终端输入指令,把本地环境搭建起来:

npm install docsify-cli -g

初始化文档:

docsify init ./docs

本地运行:

docs serve

然后在浏览器打开 localhost:3000 就可以查看了。

二、页面

我觉得最基本的页面就三个

  1. _navbar.md

导航栏

* [关于](/)

然后在 index.html 调用。

window.$docsify = {
    ...
    loadNavbar: true,
    ...
}

如图:

  1. _sidebar.md

侧边栏

* [基于 docsify 搭建一个文档](基于docsify搭建一个文档.md)

在 index.html 调用。

window.$docsify = {
    ...
    loadSidebar: true,
    ...
}

如图:

  1. README.md

主页

## about

博客地址:[https://imhan.cn](https://imhan.cn)

作者:shuxhan

时间:2021.04.20

本文档作为博客的文章备份,如果对文章有一些其他的评论可以直接点击链接到原地址。

如图:

清明时节雨纷纷

2021-04-03 08:00:00

清明时节雨纷纷,路上行人欲断魂。

云栖竹径

我的 python 学习笔记

2021-04-01 08:00:00

之所以学习 Python,本意是想写一些脚本之类的程序,用来在浏览器爬一些自己想要的文字,图片等资料,这些程序其实 GitHub 蛮多的,但奈何我对后端的语言一窍不通,直接拿过来给我用我都不会。

因此就萌生了先学习 Python 基础知识的想法,一直听说廖雪峰的 Python 教程写的不错,这次趁着这个机会要好好研读一番。也没想说用的多牛逼,至少希望能为自己浅薄的技术栈再增加一点光彩,毕竟我也曾梦想成为一个全栈开发。

下载安装完 Python 环境,就开始学习吧!

运行我的第一个 python 程序

print('hello,world')

数据类型

在 Python 中,能直接处理的数据类型有几种,整数、浮点数、字符串、布尔值、空值、变量。

这与其他语言其实差别并不大,数据类型基本都是相似的,大概只有变量有自己的特性,所以我单独拿出来记录一下。

变量

python 是一门动态语言,在赋值时可以不断改变,比如:

a = 123
print(a) # 终端输出数值 123
a = 'abc'
print(a) # 终端输出字符串 abc

这是允许的,而在 Java 中则不行,它是一门静态语言,如果多次赋值会报错。相对来说,动态语言更灵活,当然,各有利弊罢了。

并且在赋值上面有一个逻辑顺序,比如定义一个 a = 'a' 的变量。在 Python 中其实走了两步,先创造了一个字符串 a ,然后将这个字符串赋值给变量 a。

其实这在数学上是有些反逻辑的,x = x + 2 这个计算是行不通的在数学上,但是在计算机中,先计算 x + 2 ,然后将其赋值给左侧的 x ,这是计算机的逻辑。

x = 1
x = x + 2
print(x) # 3

其实很多计算机语言都是如此,比如我们所熟知的 js 等等。

如果有多个变量相互赋值的话,都是一行一行执行

a = 1
b = a
a = 2
print(a)
print(b)

最后输出的结果分别是 2, 1

Python 支持多种数据类型,在计算机内部,可以把任何数据都看成一个“对象”,而变量就是在程序中用来指向这些数据对象的,对变量赋值就是把数据和变量给关联起来。 对变量赋值 x = y 是把变量 x 指向真正的对象,该对象是变量 y 所指向的。随后对变量 y 的赋值不影响变量 x 的指向。 注意:Python 的整数没有大小限制,而某些语言的整数根据其存储长度是有大小限制的,例如 Java 对 32 位整数的范围限制在 -2147483648-2147483647。 Python 的浮点数也没有大小限制,但是超出一定范围就直接表示为 inf(无限大)。—《廖雪峰的官方网站》

字符串和编码

Python 提供了两个处理编码的属性,ordchr

ord('舒')
# 33298
# 将字符串转换为整数表示

chr(33298)
# '舒'
# 将整数表示的内容转换为字符串

list

list 类似于 js 中的 Array,是多个数据的列表,写法如下

>>> classmates = ['a', 'b', 'c']
>>> classmates
['a', 'b', 'c']

这时候我们就可以说变量 classmates 是一个 list。

有一个 len() 可以输出 list 元素的个数

>>> len(classmates)
>>> 3

同时 list 也有索引,从 0 开始,最后一个索引是 len(classmates)-1,同时如果想直接输出最后一个元素,可以使用 classmates[-1].

>>> classmates[-1]
>>> 'c'

既然都可以用 -1 表示最后一个元素,能不能用 -2 表示倒数第二个呢?答案是可以的。

>>> classmates[-2]
>>> 'b'

另外需要注意的是,在使用索引时不可超出范围,否则会报出下面的错误。

>>> classmates[4]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range

list 是一个可变的有序列表,可以往里面添加或者删除元素。

  1. append 在末尾添加元素
>>> classmates.append('d')
>>> classmates
['a', 'b', 'c', 'd']
  1. insert 在指定位置添加元素

其中 1 是索引,表示在索引为 1 的地方插入元素,后面的元素自然往后顺延。

>>> classmates.insert(1, 'd')
>>> classmates
['a', 'd', 'b', 'c']
  1. pop 删除末尾的元素
>>> classmates.pop()
'c' # 输出的时候被删除的元素

>>> classmates
['a', 'b']
  1. pop(i) 删除指定位置的元素
>>> classmates.pop(1)
'b' # 输出被删除的元素

>>> classmates
['a', 'c']
>>> 

实时监听页面的变化

2021-03-26 08:00:00

问题产生于需求,因为项目需要适配 pc 和移动端多套布局,所以某些元素在特定的宽度下会产生错位的现象。

如果是在 css 中,可以很好的判断,使用媒体查询可以实时检测页面的宽度,从而给予标签不同的属性。

@media (max-width:768px){
    ...
}

在 js 下可以使用下面这种方法

window.addEventListener('load', function() {
  window.addEventListener('resize', function() {
    console.log(window.innerWidth)
    var w = window.innerWidth;
    // ...
  })
})

具体操作的细节可以尝试一下,还是很不错的一个方法,同时注意 window ,因为这是个例子我就直接写了,实际编码中,慎用 window 事件。

ajax 和 js 事件的执行顺序

2021-03-23 08:00:00

有一个需求,滚轮滚动到相应位置的时候执行当前的动画,这个动画在footer里面,而网页的主体通过ajax进行渲染,我在 js 里面调用 ajax 渲染数据,然后再获取主体的高度,滚动到该高度的时候执行动画。

本地测试了一下没什么问题,该有的效果都有了。放在服务器上测试发现无论怎么写,都是先获取主体的高度,然后才进行数据的渲染。那么必然高度是一个极小的值,不符合我想要的属性。

我大致想了两种解决办法,均以失败告终,本地是 ajax 先执行,服务器是 js 先执行。

  1. 让获取高度的事件时间延时 500ms,发现最后获取不到事件了;
  2. 将该事件写在 ajax 的 success 回调里面,结果是只有打开网页第一次能够成功,然后不管刷新多少次均无效,事件被屏蔽,所以方法二也被废除;

最后我想到了,ajax 不就是一种异步方法,我将其改为同步不就行,先让 ajax 执行完在执行 js 事件。

async: false,

我在 ajax 里面写了async这个方法,false 意思是将其默认为同步获取数据,很好,回到服务器打开控制台,发现是先渲染了数据,才获取了主体的高度,问题得以解决。

当然这样做有弊端的,如果接口出问题,ajax 渲染失败,那么整个网页的 js 都将执行不了。不过我想真到了数据都渲染不出的地方,访问网页就没有意义了,所以最后我采用了这种方法。

ajax 拼接模版字符串的几种方法

2021-03-22 08:00:00

起因是因为 IE10 不兼容 es6,所以引发了一系列的问题。

使用ajax拼接字符串有很多方法,最简单的莫过于使用 es6 的语法中的关键符号,

str += 
`<li class="wrap-item">
    <div class="pic-wrap">
        <div class="pic">
            <div class="pic-son">
                <a href=" ` + item.link + `" target="_blank"><img src=" ` + item.pic+ `" alt="缩略图"></a>
            </div>
        </div>

        <div class="pic-main">
            <div class="pic-title">
                <span>编号:</span>
                <span> ` + item.title + ` </span>
            </div>
            <div class="pic-guide"> `
                + item.guide +
            ` </div>
            <div class="pic-link" id="picLink">
                <a href=" ` + item.link + ` " target="_blank">预览</a>
            </div>
        </div>
    </div>
</li> 
`

可以很快的把模版字符串写好,但是正因为它是es6的新特性,因此对于低版本的浏览器兼容性不太好,报错无效字符。

因此还有一种较为麻烦的写法" '' " ,使用单引号和双引号将标签和字符串连接起来。因为太过繁琐,所以需要特别仔细的查看,一不小心漏个符号,就会导致网页报错。

p = "<li class='wrap-item'>"
    +"<div class='pic-wrap'>"
        +"<div class='pic'>"
            +"<div class='pic-son-wrap'>"
                +"<div class='pic-son'>"
                    +"<a href='"+item.link+"'>"
                        +"<img src='"+item.pic+"' alt='缩略图'>"
                    +"</a>"
                +"</div>"
            +"</div>"
            +"<div class='pic-main'>"
                +"<div class='pic-title'>"
                    +"<span>"+"编号:"+"</span>"+"<span>"+item.title+"</span>"
                +"</div>"
                +"<div class='pic-guide'>"
                    +item.guide
                +"</div>"
                +"<div class='pic-link' id='picLink'>"
                    +"<a href='"+item.link+"'>"
                        +"预览"
                    +"</a>"
                +"</div>"
            +"</div>"
        +"</div>"
        +"</div>"
    +"</li>"
str += p;

在不同的情况下使用不同的方法,多掌握几种方法总是不亏的,能适用与各种开发环境。

用 ajax 请求获取数据

2021-03-19 08:00:00

通过 jquery 进行 ajax 请求数据是一种较为简便的方式,我简单写了一个本地的data.json文件,然后用get请求数据,下面是部分代码

// ajax 请求获取数据
function picShow(){
    var str = '';
    $.ajax({
        url:'./js/data.json',
        type:'get',
        dataType:'json',
        success:function(data){
            console.log('success')
        },
        error: function() {
            console.log('error')
        }
    })
}
picShow();

但是这样做有一个弊端,无法在本地预览,因为浏览器禁止客户端直接获取本地数据,这也算一个跨域问题了,不过这也是为了保证用户的安全。当然这对前端来说很不爽!

其实解决的方法也很简单,通过 node 服务,用live server在本地跑一下,就可以让 ajax 获取数据,当然还有其他更好的解决方法,或者直接在服务器跑,通过 jsonp 等等。

live server 不明白的可以百度看看,也是一个技巧。

IE10 的兼容性处理

2021-03-18 08:00:00

  1. IE10 不支持箭头函数,写 js 的时候慎用。

  2. IE10 下对 js 的语法要求严格,正常参数后如果没有多余的代码,写个,会报错,如果加了删掉就 OK,Chrome 下不会产生影响。

  3. zoom 属性能不用,尽量别用。我在 Chrome 使用 zoom 进行大小调整,放到 IE10 运行,整个页面都错位了,找半天才想起来之前给一个容器设置了zoom: 1.1;,真是让人头大。

  4. IE10 下img标签自带白边,在默认样式里面设置border: none;即可。

img {
    border: none;
}
  1. IE10 能兼容大部分的 html5 新标签,唯独不能兼容 main

  2. box-shadow 阴影属性其实 IE10 是支持的,但是需要注意语法问题,在普通浏览器 color 可以用rgba,#等写法,但是在 IE10 下,只能通过#000000写法,#和六位的颜色标记,不可以简写,否则不生效。

  3. IE10 不兼容 es6 新语法。

window.scroll 方法只能存在一次该如何解决?

2021-03-17 08:00:00

页面监听是我们经常用的一个功能,下面两个段代码表示两个事件,分别在页面滚动 500px 和 1000px 的时候触发,在代码中使用了箭头函数。

window.addEventListener('scroll', () => {
   var scrollTop = document.documentElement.scrollTop 
   if (scrollTop > 500) {
       console.log('滚动了 500px')
   }
})

window.addEventListener('scroll', () => {
   var scrollTop = document.documentElement.scrollTop 
   if (scrollTop > 1000) {
        console.log('滚动了 1000px')
   }
})

但是后面又要求代码兼容到 IE10,就不能采用这种写法了。然后我就想使用原生的function写法。

window.onscroll = function() {
    // ...
}

在用的过程中我发现,如果只有一个window.scroll没有任何问题,但是如果有多个window.scroll,后面的就不会生效。

那么如何解决这个问题呢?

window.scroll只能存在一次,第一个会生效,后面的不生效,可以通过 js 处理这个问题。

function addEvent(type, toDo) {
    if (window.attachEvent) {
        window.attachEvent('on' + type, function() {
            toDo.call(window)
        })
    } else {
        window.addEventListener(type, toDo, false)
    }
}

addEvent('scroll', function(){
    console.log('第一次调用window.scroll')
}

addEvent('scroll', function(){
    console.log('第一次调用window.scroll')
}

运行一下,多个scroll可以同时运行。

IE10 下`,`符号不能留白

2021-03-16 08:00:00

$("#test").click(function () {
    $('html,body').animate({ 'scrollTop': '0' }, 500,)
})

这行代码使用的是 jquery 的写法,一个 500 毫秒的动画,这样写在 Chrome 浏览器等没问题,可以正常运行。

但是今天在写项目的过程中,要求代码兼容到 IE10,没办法,被迫在 IE 浏览器下运行,结果发现这行代码会报错。这是怎么回事呢?

简单分析一下,这有点类似 js 中定时器的写法,setimeout,两个参数,分别代表指定的时间,和定时时间。

而 animate 是动画的属性,这个时间共花费 500 毫秒执行完毕。但是这个不是重点,为什么会报错呢?

这时候我注意到 500 后面的,,我突然想到一种可能,先把,删掉。果然不出我所料。控制台不会报错了。

我猜测,这是由 IE 的机制引起的,浏览器识别到,的时候,会自动默认后面跟有语句,但是如果空着不写,与浏览器预期的后果不同,就会产生报错。可能 IE 内核要求也比较严格吧。

怎么让 css3 里面的动画属性看起来更流畅?

2021-03-14 08:00:00

ps:先说点题外话,因为工作的原因很久没更新博客了,最近也是告一段落,处理一了一些事情之后可以安心坐下来,把我的博客重新整理一下。

今天要讲的是 css3 的一个新属性 animation,也算是一个回顾吧,我只是将平时如何使用,以及一些技巧分享出来。如果想学习更基础的内容,可以点击链接:https://www.runoob.com/css3/css3-animations.html,网上类似的教程还是蛮多的。

这篇文章并不细讲动画的基础属性,而是稍微扩展一个话题:怎么让 css3 里面的动画属性看起来更流畅?

当然在讲之前还是要简单介绍一下 animation 基本属性。

animation 属性

animation 属性有下面几个值:

  1. @keyframes | 动画本身(最重要的)
  2. animation-name | 动画的名称
  3. animation-duration | 动画所经历的时间
  4. animation-fill-mode | 当属性为 forwards 时,动画停留在最后一帧

动画主要可以用于一些官网特效,或者自适应的布局。

如何写一个最基本的动画

动画就是由一个状态到另一个状态的过程,比如从左到右的一个移动过程。

首先要先了解各个属性具体的作用是什么,就拿从白到黑举个例子吧,首先写一个 @keyframes 的动画过程。

@keyframes leftright {
    0% {
        transform: translateX(0);
    }
    100% {
        transform: translateX(100px);
    }
}

这段代码的意思就是有一个名为 leftright 的动画,将容器从左到右移动了 100px。

然后我们进一步补全这个动画。

<!DOCTYPE html>
<html>
<head>
    <title>动画</title>
    <style>
        .demo {
            width: 100px;
            height: 100px;
            background: #000;
            animation-name: leftright;
            animation-duration: 1s;
        }

        @keyframes leftright {
            0% {
                transform: translateX(0);
            }
            100% {
                transform: translateX(100px);
            }
        }
    </style>
</head>
<body>
    <div class="demo"></div>
</body>
</html>

然后我们到浏览器跑一下这个页面试试看,可以看到一个黑色的方块从左右移动了 100px 的距离,总计 1 秒完成。

但是这个动画结束的很突兀,容器突然回到原地,这是为什么呢?因为我们没有保留最后一帧,这需要一个属性,最上面我们讲过了。

.demo {
    width: 100px;
    height: 100px;
    background: #fff;
    animation-name: leftright; /* 将动画的名称绑定到该标签 */
    animation-duration: 1s;     /* 给动画的过程设一个时长 */
    animation-fill-mode: forwards; /* 动画停留在最后一帧 */
}

运行一下,可以看到动画最后停住了。

让动画拥有一个惯性

我们仔细观察这个动画,发现他整个过程不拖泥带水,很迅速的完成了,但是我发现一个问题,整个过程不流畅圆滑,很死板,从左到右,一点也不圆滑。

分析一下,因为缺少了一个日常生活的习惯动画,那就是惯性,这个属性可不存在啊,没法直接设置,只能通过动画本身的属性来进行调整,继续看这一段代码。

 @keyframes leftright {
    0% {
        transform: translateX(0);
    }
    100% {
        transform: translateX(100px);
    }
}

0%代表开始,100%代表结束,他们指的是一个时间节点,在这个时间点动画要运行到他们指定的位置。

所以我们可以添加一个新的时间节点,50%,我们让当时间过了一半的时候,方块运行到 120px 的位置,然后再折回来。

下面是完整的代码,可以复制到本地,自己运行一下试试看。

<!DOCTYPE html>
<html>
<head>
    <title>动画</title>
    <style>
        .demo {
            width: 100px;
            height: 100px;
            background: #000;
            animation-name: leftright;
            animation-duration: 1s;
            animation-fill-mode: forwards;
        }

        @keyframes leftright {
            0% {
                transform: translateX(0);
            }
            50% {
                transform: translateX(120px);
            }
            100% {
                transform: translateX(100px);
            }
        }
    </style>
</head>
<body>
    <div class="demo"></div>
</body>
</html>

修改完之后,到浏览器看一下效果。

是不是流畅了很多,有了一种惯性的感觉,这一点在工作中如何遇到写动画的需求就很关键了,怎么让用户的体验更强,是一门技巧。

虽说这都是一些基础的东西,但是我觉得体验感上去了,网站的回头率才会更高。打一个比方,去某个商城网站购物,整个页面看起来很舒服,特效细节很流畅,是不是就增加了购物的欲望。虽然这么说很浅薄,但是真的会有一些心里作用。

如何用好动画特效,是一门学问,将 css 玩出花样来,可不是那么简单的。

chrome 浏览器无法显示 12px 以下的字体

2021-02-26 08:00:00

最近正在面试,遇到了很多有意思的东西,比如今天,在面试前先做了一套面试题,其中有这么一道题,觉得挺有意思,之前也没见过,就打算记录下来:

<!-- chrome 浏览器 12px 字体大小以下无法显示,均显示为 12px,应该怎么解决。 -->

我确实有点蒙,在之前写代码的时候,没有遇到过这样的情况,不过既然这样问了,就得想出解决的办法。

首先我想到之前的一个问题,0.5px 的边框该如何实现,通过 scale(0.5) 可以实现,这是 css3 新属性。

所以当时我就写了通过 transform:scale() 可以实现。不过后续在面试的中,面试官也没有提及这些问题。


回到家之后,吃完饭坐在电脑旁,正准备逛一逛 github,突然想起来了这么一个问题,就自己测试了一遍:

<div style="font-size: 13px;">
    测试
</div>
<div style="font-size: 12px;">
    测试
</div>
<div style="font-size: 11px;">
    测试
</div>

竟然真的存在这样的问题,Chrome 12px 以下的字体无法正常显示,均显示为 12px。

然后我用 scale 设置了一下属性,

<div style="font-size: 13px;">
    测试
</div>
<div style="font-size: 12px;">
    测试
</div>
<div style="font-size: 11px;transform: scale(0.5);">
    测试
</div>

字体确实可以再次缩小到自己想到的程度,但是存在一些弊端,所有带有长度的属性均被缩小,比如图中的 div 的长度,本来默认是 100% ,但是显示只有原来的一半了。

虽然存在这么个问题,但是按照我自己的预估,平时我们浏览的网站基本没有低于 12px 大小的字体吧!反正我是没有遇到过,我估计面试题也是想考验我们对一些生僻知识的处理吧,不过也不算生僻,前有 0.5px,后有 12px,其实都大同小异。

在遇到这个问题的时候,确实可以通过 scale() 处理一下,然后在认真设置其他属性,尽量避免更多的弊端。

使用宝塔面板安装 autoPicCdn 图床工具

2021-02-25 08:00:00

很多小伙伴在做个人网站或者博客时,总能用到各种各样的图床工具,毕竟在写文章的时候需要插入一些图片,我也不例外,来盘点一下之前用的工具,

  1. 最开始是找一些免费的图床网站,比如 sm.ms 等,但是后来为了安全性和稳定性考虑放弃了这种。
  2. 然后是在 github,gitee 仓库存储的(缺点就是 github 访问速度有些尴尬,gitee 其实挺好的,但时用起来怪怪的)
  3. 阿里云 oss 储存桶(挺好使的,搭配 picgo 软件,一年也才 9.9,但是链接很尴尬,直接下载,无法右键访问图片,搜过了解决办法,嫌太麻烦就搁置了,至今在那放着)
  4. 后来在自己的服务器建一个网站,把图片放进去,使用的时候直接引用链接(缺点就是上传太麻烦,然后就是图片太占内存了,影响服务器的性能)
  5. 然后就是今天的重头戏,autoPicCdn,一款开源软件

也是基于 github 实现图床功能,后来才知道可以用 jsdelivr 给资源进行加速,autoPicCdn 就是基于这样的功能实现。

今天就讲讲用宝塔面板配置 autoPicCdn 的流程吧!

准备工作

  1. 进入 github 仓库下载资源到本地。 https://github.com/yumusb/autoPicCdn

  1. 在 github 建立一个新的仓库,自由命名,我的是 shuxhan/pic-cdn,简单易懂
  2. 去这个页面 https://github.com/settings/tokens生成一个有写权限的 token(repo:Full control of private repositories 和 write:packages 前打勾,然后点击确定,会生成一个 token 码,记住它,最好复制到你的记事本,不然一刷新网页就不会再显示了

然后解压。

上传到服务器

先利用自己的域名添加一个新的站点,比如我使用的是 img.shuxhan.com ,然后建立数据库。

站点建立成功后,进入目录将默认生成的文件删除,然后点击上传,找到刚才解压的文件,点击上传即可。

配置文件

找到目录中的 up.php 配置文件,

将下面两个地方配置好,具体的内容在代码里。

//必选项
define("TYPE","GITHUB");//选择 github
//define("TYPE","GITEE");//选择 gitee,如果使用 gitee,需要手动建立 master 分支,可以看这里 https://gitee.com/help/articles/4122

define("USER","shuxhan");//你的 GitHub/Gitee 的用户名

define("REPO","pic-cdn");//必须是上面用户名下的 公开仓库

define("MAIL","[email protected]");//邮箱无所谓,随便写

define("TOKEN","971b0a96ff5af545d5fe081f829cf5542007e70b");
// Github 去这个页面 https://github.com/settings/tokens生成一个有写权限的token(repo:Full control of private repositories 和 write:packages 前打勾)
// gitee  去往这个页面 https://gitee.com/personal_access_tokens
//数据库配置文件
//请确保把当前目录下的 pic.sql 导入到你的数据库
$database = array(
    'dbname' => 'img',//你的数据库名字
    'host' => 'localhost',
    'port' => 3306,
    'user' => 'img',//你的数据库用户名
    'pass' => 'img',//你的数据库用户名对应的密码
);

然后最重要的一步是,将目录下的 pic.sql 导入刚才建立并链接的数据库,先右键复制 pic.sql 文件,然后进入路径

点击粘贴即可。

测试访问

打开你的浏览器,输入绑定的站点

然后随便截个图,然后拖动到上传窗口,显示上传成功,下面是图片的廉价,复制 markdown 可以在写文章的时候使用辣!

通过 jsdelivr 加速,结果非常令人满意,以后就决定使用 github + jsdelivr + autoPicCdn 来做图床工具啦!

typecho 博客小表情无法显示问题

2021-02-04 08:00:00

typecho 博客建立完成之后,会发现如果插入一些小表情,页面渲染完之后,表情不会显示,并且表情之后的内容全部消失,导致体验感很差。

表情无法显示的原因是 uft-8 编码的问题,无法识别小表情,需要将其改成 utf8mb4

第一步,进入数据库

首先登陆博客后台的数据库,然后点击 sql,输入下列代码:

alter table typecho_comments convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_contents convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_fields convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_metas convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_options convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_relationships convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_users convert to character set utf8mb4 collate utf8mb4_unicode_ci;

直接复制,然后点击右下角的执行

第二步,修改 typecho 配置文件

我使用的宝塔面板,点击左侧 网站 ,然后点击我们这个网站的根目录,进入之后,可以找到一个 config.inc.php 的文件,进入然后滑到最下面。

**可以发现 'charset' => 'utf8',**下面我为了更直观的显示,将其注释掉,重新写的,实际操作中,将 utf8 改为 utf8mb4 即可。

/** 定义数据库参数 */
$db = new Typecho_Db('Pdo_Mysql', 'typecho_');
$db->addServer(array (
  'host' => 'localhost',
  'user' => 'blog',
  'password' => 'blog',
  /** 'charset' => 'utf8', */
  'charset' => 'utf8mb4',
  'port' => '3306',
  'database' => 'blog',
), Typecho_Db::READ | Typecho_Db::WRITE);
Typecho_Db::set($db);

在文章后台重新输入小表情,然后保存回到页面,小表情成功显示了。

掘金个人主页头像旋转效果

2021-02-03 08:00:00

在浏览掘金社区的时候,发现一个很有意思的小彩蛋,在个人主页将鼠标悬浮在头像上,会不停的加速旋转,我感觉挺有意思,f12 看看这个样式怎么写的,发现用到了 css3 的相关技术。

下面是具体代码,img 是头像元素,直接写在 hover 中即可。

img:hover {
    transform: rotate(666turn);
    transition-delay: 1s;
    transition-property: all;
    transition-duration: 59s;
    transition-timing-function: cubic-bezier(.34,0,.84,1);
}

ps:今天是 2021 年 2 月 4 号,农历腊月二十三,传统的小年,祝朋友们小年愉快。不知不觉,快过年了。

ES5 和 ES6 的区别?

2021-01-20 08:00:00

一、javascript 由三部分组成

1.ECMAScript(核心)

它规定了语言的组成部分:法语,类型,语句,关键字,操作符等等。

2.DOM(文档对象模型)

DOM 把整个页面映射为一个多层节点结果,开发人员可借助 DOM 提供的 API,轻松地删除、添加、替换或修改任何节点。

3.BOM(浏览器对象模型)

支持可以访问和操作浏览器窗口的浏览器对象模型,开发人员可以控制浏览器显示的页面以外的部分。

二、什么是 ES5?

作为 ECMAScript 第五个版本(第四版因为过于复杂废弃了),浏览器支持情况可看第一副图,增加特性如下。

1.strict 模式

严格模式,限制一些用法,‘use strict’;

2.Array 增加方法

增加了 every、some、forEach、filter、indexOf、lastIndexOf、isArray、map、reduce、reduceRight 方法

PS:还有其他方法 Function.prototype.bind、String.prototype.trim、Date.now

3.Object 方法

PS:只讲有什么,不讲是什么。

2.什么是 ES6?

ECMAScript6 在保证向下兼容的前提下,提供大量新特性,目前浏览器兼容情况如下:

ES6 特性如下:

  1. 块级作用域 关键字 let, 常量 const

  2. 对象字面量的属性赋值简写(property value shorthand)

var obj = {
    // __proto__
    __proto__: theProtoObj,
    // Shorthand for ‘handler: handler’
    handler,
    // Method definitions
    toString() {
    // Super calls
    return "d " + super.toString();
    },
    // Computed (dynamic) property names
    [ 'prop_' + (() => 42)() ]: 42
};
  1. 赋值解构
let singer = { first: "Bob", last: "Dylan" };
let { first: f, last: l } = singer; // 相当于 f = "Bob", l = "Dylan"
let [all, year, month, day] =  /^(\d\d\d\d)-(\d\d)-(\d\d)$/.exec("2015-10-25");
let [x, y] = [1, 2, 3]; // x = 1, y = 2
  1. 函数参数 - 默认值、参数打包、数组展开(Default、Rest、Spread)
//Default
function findArtist(name='lu', age='26') {
    ...
}

//Rest
function f(x, ...y) {
  // y is an Array
  return x * y.length;
}
f(3, "hello", true) == 6

//Spread
function f(x, y, z) {
  return x + y + z;
}
// Pass each elem of array as argument
f(...[1,2,3]) == 6
  1. 箭头函数 Arrow functions

(1) 简化了代码形式,默认 return 表达式结果。

(2) 自动绑定语义 this,即定义函数时的 this。如上面例子中,forEach 的匿名函数参数中用到的 this。

6.字符串模板 Template strings

var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// return "Hello Bob, how are you today?"
  1. Iterators(迭代器)+ for..of

迭代器有个 next 方法,调用会返回:

(1) 返回迭代对象的一个元素:{ done: false, value: elem }

(2) 如果已到迭代对象的末端:{ done: true, value: retVal }

for (var n of ['a','b','c']) {
  console.log(n);
}
// 打印a、b、c
  1. 生成器(Generators)

  2. Class

Class,有 constructor、extends、super,但本质上是语法糖(对语言的功能并没有影响,但是更方便程序员使用)。

class Artist {
    constructor(name) {
        this.name = name;
    }

    perform() {
        return this.name + " performs ";
    }
}

class Singer extends Artist {

    constructor(name, song) {
        super.constructor(name);
        this.song = song;
    }

    perform() {
        return super.perform() + "[" + this.song + "]";
    }
}

let james = new Singer("Etta James", "At last");
james instanceof Artist; // true
james instanceof Singer; // true

james.perform(); // "Etta James performs [At last]"
  1. Modules

ES6 的内置模块功能借鉴了 CommonJS 和 AMD 各自的优点:

(1) 具有 CommonJS 的精简语法、唯一导出出口 (single exports) 和循环依赖 (cyclic dependencies) 的特点。

(2) 类似 AMD,支持异步加载和可配置的模块加载。

// lib/math.js
export function sum(x, y) {
  return x + y;
}
export var pi = 3.141593;

// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));

// otherApp.js
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));

Module Loaders:
// Dynamic loading – ‘System’ is default loader
System.import('lib/math').then(function(m) {
  alert("2π = " + m.sum(m.pi, m.pi));
});

// Directly manipulate module cache
System.get('jquery');
System.set('jquery', Module({$: $})); // WARNING: not yet finalized
  1. Map + Set + WeakMap + WeakSet

四种集合类型,WeakMap、WeakSet 作为属性键的对象如果没有别的变量在引用它们,则会被回收释放掉。

// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;

// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;

//WeakMap
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined

// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });//Because the added object has no other references, it will not be held in the set
  1. Math + Number + String + Array + Object APIs

一些新的 API

Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false

Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2

"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"

Array.from(document.querySelectorAll('*')) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior

[0, 0, 0].fill(7, 1) // [0,7,7]
[1, 2, 3].find(x => x == 3) // 3
[1, 2, 3].findIndex(x => x == 2) // 1
[1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2]
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"

Object.assign(Point, { origin: new Point(0,0) })
  1. Proxies

使用代理(Proxy)监听对象的操作,然后可以做一些相应事情。

var target = {};
var handler = {
  get: function (receiver, name) {
    return `Hello, ${name}!`;
  }
};

var p = new Proxy(target, handler);
p.world === 'Hello, world!';

可监听的操作:get、set、has、deleteProperty、apply、construct、getOwnPropertyDescriptor、defineProperty、getPrototypeOf、setPrototypeOf、enumerate、ownKeys、preventExtensions、isExtensible。

  1. Symbols

Symbol 是一种基本类型。Symbol 通过调用 symbol 函数产生,它接收一个可选的名字参数,该函数返回的 symbol 是唯一的。

var key = Symbol("key");
var key2 = Symbol("key");
key == key2  //false
  1. Promises

Promises 是处理异步操作的对象,使用了 Promise 对象之后可以用一种链式调用的方式来组织代码,让代码更加直观(类似 jQuery 的 deferred 对象)。

function fakeAjax(url) {
  return new Promise(function (resolve, reject) {
    // setTimeouts are for effect, typically we would handle XHR
    if (!url) {
      return setTimeout(reject, 1000);
    }
    return setTimeout(resolve, 1000);
  });
}

// no url, promise rejected
fakeAjax().then(function () {
  console.log('success');
},function () {
  console.log('fail');
});

webpack 的核心概念和构建流程

2021-01-18 08:00:00

1.webpack 的核心概念

  1. entry(入口):一个可执行模块或者库的入口。定义了打包后的入口文件。
  2. output(出口):指示 webpack 如何去输出,以及在哪里输出。 path: 打包文件存放的绝对路径 publicPath: 网站运行时的访问路径 filename: 打包后的文件名
  3. module(模块):在 webpack 里,一切皆模块,一个模块对应一个文件。webpack 会从配置的 entry 中开始递归找出所有依赖的模块。
  4. chunk(代码块):一个 chunk 由多个 模块 组合而成。可以将可执行的模块和他所依赖的模块组合成一个 chunk,这就是打包。
  5. loader(模块转换器):用于把一个模块原内容按照需求转换成新的内容。例如:es6 转换为 es5,scss 转换为 css 等。
  6. plugin(扩展):扩展 webpack 功能的插件。在 webpack 构建的生命周期节点上加入扩展 hook,添加功能。

2.webpack 构建流程

  1. 初始化参数:解析 webpack 的配置参数,合并 shell 传入和 webpack.config.js 文件配置的参数,形成最后的配置结果。
  2. 开始编译:上一步得到的参数初始化 compiler 对象,注册所有配置的插件,插件监听 webpack 构建生命周期的事件节点,做出相应的反应,执行对象的 run 方法开始执行编译。
  3. 确定入口:其配置的 entry 入口,开始解析文件构建的 AST 语法树,找出依赖,递归下去。
  4. 编译模块:根据文件类型和 loader 配置,调用所有配置的 loader 对文件进行转换,再找出该模块依赖的模块,再递归本步骤知道所有入口依赖的文件都经过了本步骤的处理。
  5. 完成模块编译并输出:递归完后,得到每个文件结果,包含了每个模块及她们之间的依赖关系,根据 entry 配置生成代码块 chunk。
  6. 输出完成:输出所有的 chunk 到文件系统。

3.有哪些常见的 loader?

  1. babel-loader:把 es6 转成 es5;
  2. css-loader:加载 css,支持模块化,压缩,文件导入等特性;
  3. style-loader:把 css 代码注入到 js 中,通过 dom 操作去加载 css;
  4. eslint-loader:通过 Eslint 检查 js 代码;
  5. image-loader:加载并且压缩图片晚间;
  6. file-loader:文件输出到一个文件夹中,在代码中通过相对 url 去引用输出的文件;
  7. url-loader:和 file-loader 类似,文件很小的时候可以 base64 方式吧文件内容注入到代码中。
  8. source-map-loader:加载额外的 source map 文件,方便调试。

4.业务场景和对应解决方案

1.单页应用

一个单页应用需要配置一个 entry 指明执行入口,web-webpack-plugin 里的 WebPlugin 可以自动的完成这些工作:webpack 会为 entry 生成一个包含这个入口的所有依赖文件的 chunk,但是还需要一个 html 来加载 chunk 生成的 js,如果还提取出 css 需要 HTML 文件中引入提取的 css。

一个简单的 webpack 配置文件例子:

const { WebPlugin } = require('web-webpack-plugin');
module.exports = {
  entry: {
    app: './src/doc/index.js',
    home: './src/doc/home.js'
  },
  plugins: [
    // 一个 WebPlugin 对应生成一个 html 文件
    new WebPlugin({
      //输出的 html 文件名称
      filename: 'index.html',
      //这个 html 依赖的`entry`
      requires: ['app','home'],
    }),
  ],
};

说明:require: [‘app’, ‘home’]指明这个 html 依赖哪些 entry,entry 生成的 js 和 css 会自动注入到 html 中。

还支持配置这些资源注入方式,支持如下属性:

  1. _dist 只有在生产环境中才引入的资源;
  2. _dev 只有在开发环境中才引入的资源;
  3. _inline 把资源的内容潜入到 html 中;
  4. _ie 只有 IE 浏览器才需要引入的资源。

这些属性可以通过在 js 里配置,看个简单例子:

new WebPlugin({
    filename: 'index.html',
    requires: {
         app:{
              _dist:true,
              _inline:false,
         }
    },
}),

这些属性还可以在模板中设置,使用模板好处就是可以灵活的控制资源的注入点

new WebPlugin({
      filename: 'index.html',
      template: './template.html',
}),
<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <link rel="stylesheet" href="app?_inline">
    <script src="ie-polyfill?_ie"></script>
</head>
<body>
<div id="react-body"></div>
<script src="app"></script>
</body>
</html>

WebPlugin 插件借鉴了 fis3 的思想,补足了 webpack 缺失的以 HTML 为入口的功能。想了解 WebPlugin 的更多功能,见文档。

2.一个项目管理多个单页面

一个项目中会包含多个单页应用,虽然多个单页面应用可以合成一个,但是这样做会导致用户没有访问的部分也加载了,如果项目中有很多的单页应用。为每一个单页应用配置一个 entry 和 WebPlugin?如果又新增,又要新增 webpack 配置,这样做麻烦,这时候有一个插件 web-webpack-plugin 里的 AutoWebPlugin 方法可以解决这些问题。

module.exports = {
    plugins: [
        // 所有页面的入口目录
        new AutoWebPlugin('./src/'),
    ]
};

分析:

  1. AutoWebPlugin会把./src/目录下所有每个文件夹作为一个单页页面的入口,自动为所有的页面入口配置一个 WebPlugin 输出对应的 html。
  2. 要新增一个页面就在 ./src/ 下新建一个文件夹包含这个单页应用所依赖的代码,AutoWebPlugin 自动生成一个名叫文件夹名称的 html 文件。

3.代码分隔优化

一个好的代码分割对浏览器首屏效果提升很大。

最常见的 react 体系:

  1. 先抽出基础库 react react-dom redux react-redux 到一个单独的文件而不是和其它文件放在一起打包为一个文件,这样做的好处是只要你不升级他们的版本这个文件永远不会被刷新。如果你把这些基础库和业务代码打包在一个文件里每次改动业务代码都会导致文件 hash 值变化从而导致缓存失效浏览器重复下载这些包含基础库的代码。所以把基础库打包成一个文件。
// vender.js 文件抽离基础库到单独的一个文件里防止跟随业务代码被刷新
// 所有页面都依赖的第三方库
// react 基础
import 'react';
import 'react-dom';
import 'react-redux';
// redux 基础
import 'redux';
import 'redux-thunk';
// webpack 配置
{
  entry: {
    vendor: './path/to/vendor.js',
  },
}
  1. 通过 CommonsChunkPlugin 可以提取出多个代码块都依赖的代码形成一个单独的 chunk。在应用有多个页面的场景下提取出所有页面公共的代码减少单个页面的代码,在不同页面之间切换时所有页面公共的代码之前被加载过而不必重新加载。所以通过 CommonsChunkPlugin 可以提取出多个代码块都依赖的代码形成一个单独的 chunk。

4.构建服务端渲染

服务端渲染的代码要运行在 nodejs 环境,和浏览器不同的是,服务端渲染代码需要采用 commonjs 规范同时不应该包含除 js 之外的文件比如 css。

webpack 配置如下:

module.exports = {
  target: 'node',
  entry: {
    'server_render': './src/server_render',
  },
  output: {
    filename: './dist/server/[name].js',
    libraryTarget: 'commonjs2',
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        loader: 'babel-loader',
      },
      {
        test: /\.(scss|css|pdf)$/,
        loader: 'ignore-loader',
      },
    ]
  },
};

分析一下:

  1. target: 'node' 指明构建出代码要运行在 node 环境中。
  2. libraryTarget: 'commonjs2' 指明输出的代码要是 commonjs 规范。
  3. {test: /\.(scss|css|pdf)$/,loader: 'ignore-loader'} 是为了防止不能在 node 里执行服务端渲染也用不上的文件被打包进去。

5.fis3 迁移到 webpack

fis3 和 webpack 有很多相似地方也有不同的地方,相似地方:都采用 commonjs 规范,不同地方:导入 css 这些非 js 资源的方式。

fis3 通过@require ‘./index.scss’,而 webpack 是通过 require(’./index.scss’)。

如果想把 fis3 平滑迁移到 webpack,可以使用 comment-require-loader。

比如:你想在 webpack 构建是使用采用了 fis3 方式的 imui 模块

loaders:[{
     test: /\.js$/,
     loaders: ['comment-require-loader'],
     include: [path.resolve(__dirname, 'node_modules/imui'),]
}]

5.自定义 webpack 扩展

如果你在社区找不到你的应用场景的解决方案,那就需要自己动手了写 loader 或者 plugin 了。

在你编写自定义 webpack 扩展前你需要想明白到底是要做一个 loader 还是 plugin 呢?可以这样判断:

如果你的扩展是想对一个个单独的文件进行转换那么就编写 loader 剩下的都是 plugin。

其中对文件进行转换可以是像:

  1. babel-loader 把 es6 转为 es5;
  2. file-loader 把文件替换成对应的 url;
  3. raw-loader 注入文本文件内容到代码中。

1.编写 webpack loader

编写 loader 非常简单,以 comment-require-loader 为例:

module.exports = function (content) {
    return replace(content);
};

loader 的入口需要导出一个函数,这个函数要干的事情就是转换一个文件的内容。

函数接收的参数 content 是一个文件在转换前的字符串形式内容,需要返回一个新的字符串形式内容作为转换后的结果,所有通过模块化倒入的文件都会经过 loader。从这里可以看出 loader 只能处理一个个单独的文件而不能处理代码块。可以参考官方文档。

2. 编写 webpack plugin

plugin 应用场景广泛,所以稍微复杂点。以 end-webpack-plugin 为例:

class EndWebpackPlugin {

    constructor(doneCallback, failCallback) {
        this.doneCallback = doneCallback;
        this.failCallback = failCallback;
    }

    apply(compiler) {
        // 监听 webpack 生命周期里的事件,做相应的处理
        compiler.plugin('done', (stats) => {
            this.doneCallback(stats);
        });
        compiler.plugin('failed', (err) => {
            this.failCallback(err);
        });
    }
}

module.exports = EndWebpackPlugin;

loader 的入口需要导出一个 class,在 new EndWebpackPlugin() 的时候通过构造函数传入这个插件需要的参数,在 webpack 启动的时候会先实例化 plugin,再调用 plugin 的 apply 方法,插件在 apply 函数里监听 webpack 生命周期里的事件,做相应的处理。

webpack plugin 的两个核心概念:

  1. compiler:从 webpack 启动到退出只存在一个 Compiler,compiler 存放着 webpack 的配置。
  2. compilation:由于 webpack 的监听文件变化自动编译机制,compilation 代表一次编译。

Compiler 和 Compilation 都会广播一系列事件。webpack 生命周期里有非常多的事件。

以上只是一个最简单的 demo,更复杂的可以查看 how to write a plugin 或参考 web-webpack-plugin。

参考文章:https://www.cnblogs.com/chengxs/p/11022842.html

小张厨房日记(二)

2021-01-17 08:00:00

自从上次更新厨房日记之后,好像过了两个月多,实际上我一直在进阶我的厨艺,为什么没有更新我的厨房日记呢?因为实际在做的过程中我发现,每种菜的组合太多了,没必要组合一次就写一篇攻略,那样就有些做无用功了,没有太大的意义。

思来想去,我决定了,不再更新做菜的步骤攻略,我想这些攻略网上一抓一大把。把内容着重放在做菜中遇到的一些问题,细节,这样不但可以让我记忆的深刻,还能分享出更多有意思的东西让小伙伴们开心。

今天做了一道菜,红烧茄子,我在茄子的时候发现皮挺硬的,就在想要不要去皮(不要笑我,因为之前没做过茄子)。想了一会没想出个所以然,直接百度一番,“烧茄子要不要去皮?”,第一条搜索结果是这样说的,

“不要。营养专家建议在吃茄子的时候最好不要去皮,因在茄子表皮及表皮与肉质的连接部位,含有大量的有益于人体健康的物质,如芦丁、维生素 E、维生素 P 的含量都隐藏在茄子皮中,如果将茄子皮去掉会降低营养价值。”

嗯,挺好,不用去皮,但是作为百度谷歌资深用户,怎么可能只相信第一条搜索结果呢?我又往下翻了翻,有一条知乎用户是这样说的

茄子去不去皮,关键还是看你做什么菜了。如果是家常茄子,最好是不要去皮;如是是油煎茄子,因为要打花刀的原因,所以也不要去皮;如果是茄子煲,那就非去皮不可了。

略加思考,好像说得有那么几分道理,很是欣喜,所以我就采用了他的说法。今天做的是烧茄子,所以不用去皮,直接炒就可以了。

又增加了一个小技巧。

结束语:当你在心情不好的时候,或者有心事,可以做一些让你分散注意力的事情,就比如说做菜,不仅可以享受食物,享受生活,内心还可以获得成就感,我感觉这是一举两得的事情。

许嵩的歌又一次被抄袭!

2021-01-16 08:00:00

最新网上爆出风风火火的一件事,许嵩的《有何不可》被抄袭,抄袭者为罗聪,《简单的幸福》。

我从 14 年开始听许嵩的歌,一直到现在,许多年过去了,他的歌依旧脍炙人口,从之前的抒情浪漫情歌,批判社会的思想,一直到现在具体哲学内涵,中华传统思想。大部分都是包揽作词作曲为一身,真的很有才华。

因为太有才华了,所以被“碰瓷”抄袭。这也算是一场闹剧了。据说嵩哥本人对此没有发表什么看法和言论,一直都是粉丝和公司在发声。

据网友戏称:“许嵩:这要放十年前,我早写歌骂你了。”,“低情商:这不就是抄袭《有何不可》?高情商:你这《有何不可》唱得有点跑调”。

令人没有想象到的事,罗聪所在公司发表声明:

突然感觉有些搞笑,我也不对他再发表什么看法,只希望官方能解决这件事,给我们松鼠一个交代。

要是靠抄袭就能火的话,还要才华做什么。

这也一直是我的理念,在写博客的时候,我也很注重版权,引用别人写的文章的时候,我都会申请作者授权,标明出处。同时,对于我自己的文章,如果你想要引用转载,只需要标明出处即可。

大部分做技术的人应该都是和我抱有一样的想法,喜欢分享,但是可不能张冠李戴,毕竟写文章也是耗费脑细胞的。并不指望能获得什么成绩,只是希望当有人看到你的文章时,解决了自己的问题,有一丝认可足以。

版权一直是一件很重要的事情,很多年前我们还比较淡漠,但是这几年对版权的重视开始慢慢提升了,审查力度增加,这是一件好事。

让它一直保持启动状态就可以,其他的不用动,以后就不用打开服务器终端输入指令了,直接在浏览器访问端口,就可以访问网页。

如果为了更方便,做个反代处理即可。

```

React 不会意识到应该保留 <li>Duke</li><li>Villanova</li>,而是会重建每一个子元素。这种情况会带来性能问题。

key

为了解决上述问题,React 支持 key 属性,当子元素拥有 key 时,React 使用 key 来匹配原有树上的子元素以及最新树上的子元素。以下例子在新增 key 之后使得之前的低效转换变得高效:

<ul>
  <li key="1">Duke</li>
  <li key="2">Villanova</li>
</ul>

<ul>
  <li key="0">Connecticut</li>
  <li key="1">Duke</li>
  <li key="2">Villanova</li>
</ul>

现在 React 知道只有带着 '0' key 的元素是新元素,带着 '1' 以及 '2' key 的元素仅仅移动了。

现实场景中,产生一个 key 并不困难。你要展现的元素可能已经有了一个唯一 ID,于是 key 可以直接从你的数据中提取:

<li key={item.id}>{item.name}</li>

当以上情况不成立时,你可以新增一个 ID 字段到你的模型中,或者利用一部分内容作为哈希值来生成一个 key。这个 key 不需要全局唯一,但在列表中需要保持唯一。

最后,你也可以使用元素在数组中的下标作为 key。这个策略在元素不进行重新排序时比较合适,如果有顺序修改,diff 就会变得慢。

当基于下标的组件进行重新排序时,组件 state 可能会遇到一些问题。由于组件实例是基于它们的 key 来决定是否更新以及复用,如果 key 是一个下标,那么修改顺序时会修改当前的 key,导致非受控组件的 state(比如输入框)可能相互篡改导致无法预期的变动。

许嵩的歌又一次被抄袭!

2021-01-16 08:00:00

最新网上爆出风风火火的一件事,许嵩的《有何不可》被抄袭,抄袭者为罗聪,《简单的幸福》。

我从 14 年开始听许嵩的歌,一直到现在,许多年过去了,他的歌依旧脍炙人口,从之前的抒情浪漫情歌,批判社会的思想,一直到现在具体哲学内涵,中华传统思想。大部分都是包揽作词作曲为一身,真的很有才华。

因为太有才华了,所以被“碰瓷”抄袭。这也算是一场闹剧了。据说嵩哥本人对此没有发表什么看法和言论,一直都是粉丝和公司在发声。

据网友戏称:“许嵩:这要放十年前,我早写歌骂你了。”,“低情商:这不就是抄袭《有何不可》?高情商:你这《有何不可》唱得有点跑调”。

令人没有想象到的事,罗聪所在公司发表声明:

突然感觉有些搞笑,我也不对他再发表什么看法,只希望官方能解决这件事,给我们松鼠一个交代。

要是靠抄袭就能火的话,还要才华做什么。

这也一直是我的理念,在写博客的时候,我也很注重版权,引用别人写的文章的时候,我都会申请作者授权,标明出处。同时,对于我自己的文章,如果你想要引用转载,只需要标明出处即可。

大部分做技术的人应该都是和我抱有一样的想法,喜欢分享,但是可不能张冠李戴,毕竟写文章也是耗费脑细胞的。并不指望能获得什么成绩,只是希望当有人看到你的文章时,解决了自己的问题,有一丝认可足以。

版权一直是一件很重要的事情,很多年前我们还比较淡漠,但是这几年对版权的重视开始慢慢提升了,审查力度增加,这是一件好事。

基于 React 的富文本编辑器--Braft Editor

2021-01-07 08:00:00

最近发了很多关于 React 的文章,记录遇到的新技术点,以及在写代码过程中遇到的问题,希望可以帮助到和我遇到同样问题的同学。

今天分享的是一款基于 React 的富文本编辑器--Braft Editor。

Braft Editor 官网:https://braft.margox.cn Github 仓库地址:https://github.com/margox/braft-editor

这款插件非常方便,只需要下载然后引入即可直接使用,页面整体风格清新,符合绝大多数人的审美观,支持图片,音视频的插入。

按照官方的说法,假如你对它的功能还不够满意,不够使用,完全可以扩展它,自己写一个插件来强化他。

经过我的测试,功能绝对强大,可以满足市面上绝大多数的需求。接下来就说一下如何使用这款插件。

1.安装

在项目中直接通过 npm 或者 yarn 下载插件:

# 使用 npm 安装
npm install braft-editor --save

# 使用 yarn 安装
yarn add braft-editor

2.使用

新建一个组件 EditorDemo.js,在里面写入下列代码:

// EditorDemo.js

import React from 'react';
import BraftEditor from 'braft-editor';
import 'braft-editor/dist/index.css';

export default class PageDemo extends React.Component {

  state = {
    editorState: BraftEditor.createEditorState(null)
  }

  render () {
    return (
      <BraftEditor value={this.state.editorState} onChange={this.handleChange}/>
    )
  }

  handleChange = (editorState) => {
    this.setState({ editorState })
  }

}

然后引入到 index.js 中去。

3.运行

写完组件之后,npm start 运行看看效果

相当不错,页面很简洁,如果觉得样式不喜欢,完全可以自己重新定制风格,非常方便。

4.文档

如果想使用更多功能、方法,进入 Braft Editor 官方文档吧,还有更多关于它的讲解!

属性、方法、示例,一应俱全。

使用 React 与 Vue 创建同一款 App,差别究竟有多大?

2021-01-06 08:00:00

关于 React 框架Vue 框架 的对比,用两者写出同一个程序,对比其代码实现的过程,看看它们的差异究竟有多大?

转载于:https://blog.csdn.net/csdnnews/article/details/81880378 原文:https://medium.com/javascript-in-plain-english/i-created-the-exact-same-app-in-react-and-vue-here-are-the-differences-e9a1ae8077fd 作者简介:Sunil Sandhu,一位 Web 全栈工程师 + 空想家。 译者:安翔,责编:屠敏

众所周知,Vue 和 React 都是目前非常著名的前端框架。我在工作中经常使用 Vue,因此我对它有很深入的了解。同时,我也对 React 充满了好奇,想要学习一下,一探究竟。

于是我阅读了 React 文档并观看了一些视频教程,虽然这些资料很不错,但是我真正想了解的是 React 与 Vue 之间的不同之处。所谓“不同之处”,我并非想知道它们是否都具有虚拟 DOMS 或者它们如何渲染页面,而是希望有人能够从代码的角度解释这两者之间的差异。我想找到一篇解释这些差异的文章,以便 Vue 或者 React 的初学者可以更好地理解它们两者之间的差异。

很遗憾,我并未找到一篇这样的文章。于是我意识到必须自己动手来比较 Vue 与 React 之间的异同。在我自力更生的过程中,我用这篇文章记录下了具体过程。

1.目标

我将会构建一个标准的待办事项应用程序,允许用户添加和删除列表中的项目。这两个应用程序都使用默认的 CLI(command-line interface,命令行界面)构建,React 使用 create-react-app,Vue 使用 vue-cli。

两个应用程序的外观如下:

两个应用程序的 CSS 代码几乎一样,但这些代码的位置存在差异。考虑到这一点,我们来看看这两个应用程序的文件结构:

你会发现它们的结构几乎完全相同。唯一的区别在于 React App 拥有三个 CSS 文件,而 Vue App 中没有 CSS 文件。这是因为 React 的 create-react-app 组件需要一个附带文件来保存其样式,而 Vue CLI 采用全包方法,其样式在实际组件文件中声明。

两种不同的策略得到的结果是一样的,相信开发者很快能够掌握这两种不同的策略。开发者可以根据自己的偏好做出选择,你会听到开发社区关于如何构建 CSS 的大量讨论。以上,我们遵循两个 CLI 列出了代码结构。

在我们进一步讨论之前,先快速看一下典型的 Vue 和 React 组件的外观:

现在让我们正式开始,深入其中的细节!

2.如何修改数据

首先,我们需要明白“修改数据”的意思是什么。它听起来有些学术,但实际上很简单,就是把我们已经存储好的数据进行更改。比如,如果我们想把一个人的名字变量从“Jhon”改为“Mark”,我们就需要执行“修改数据”的操作。在这一点上,React 和 Vue 的处理方式有所区别。Vue 本质上会创建一个数据对象,其中的数据可以自由更改;React 则创建一个状态对象,更改数据需要一些额外的操作。React 之所以需要额外的操作有着自己的理由,稍后我会深入介绍。在此之前,我们先看看 Vue 中的数据对象和 React 中的状态对象:

vue 数据对象

React 状态对象

从图中可以看出,我们传入了相同的数据,但它们的标记方法不同。因此,将初始数据传递到组件的方式非常相似。但正如我们提到的那样,在两个框架中更改数据的方式有所不同。

假设我们有一个名为 name: ‘Sunil’的数据元素。

在 Vue 中,我们通过调用 this.name 来引用它。我们也可以通过调用 this.name =‘John’ 来更新它。这样一来,名字就被成功改为了“Jhon”。

在 React 中,我们通过调用 this.state.name 来引用同一段数据。现在关键的区别在于,我们不能简单地写成 this.state.name =‘John’,因为 React 有限制机制,它会阻止这种简单的修改方式。在 React 中,我们需要这样写:this.setState({name:‘John’})。

虽然这基本上与我们在 Vue 中实现的结果一样,但是 React 的操作更为繁琐,那是因为 Vue 在每次更新数据时默认组合了自己的 setState 版本。简单来说就是,React 需要 setState,然后更新其内部数据,而对于 Vue 来说,当你更新数据对象的值时它就默认了你的更改意图。那么为什么 React 没有进行简化,为什么需要 setState 呢?Revanth Kumar 对此做出了解释:

“这是因为 React 希望在状态发生变化时重新运行某些生命周期 hook,比如 componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate。当你调用 setState 函数时,它知道状态已经改变。如果你直接改变状态,React 将需要做更多工作来跟踪更改以及运行生命周期 hook 等等。所以为了简单起见,React 使用 setState。"

3.添加新的待办事项

React 的实现方法

createNewToDoItem = () => {
    this.setState( ({ list, todo }) => ({
      list: [
          ...list,
        {
          todo
        }
      ],
      todo: ''
    })
  );
};

在 React 中,我们的输入字段有一个名为 value 的属性。这个 value 通过使用几个函数自动更新,这些函数绑定在一起以创建双向绑定。我们通过在输入字段上附加一个 onChange 事件监听器来创建这种形式的双向绑定。看看代码,一探究竟:

<input type="text" 
       value={this.state.todo} 
       onChange={this.handleInput}/>

只要输入字段的值发生更改,handleInput 函数就会运行。它通过将状态对象设置为输入字段中的任何内容来更新状态对象内的 todo。handleInput 函数如下:

handleInput = e => {
  this.setState({
    todo: e.target.value
  });
};

现在,只要用户按下页面上的 + 按钮添加新项目,createNewToDoItem 函数就会运行 this.setState 并向其传递一个函数。该函数有两个参数,第一个是来自状态对象的整个列表数组,第二个是由 handleInput 函数更新的 todo。然后该函数返回一个新对象,该对象包含之前的整个列表,并在其末尾添加 todo。整个列表是通过使用扩展运算符添加的。

最后,我们将 todo 设置为空字符串,它会自动更新输入字段中的 value。

Vue 的实现方法

createNewToDoItem() {
    this.list.push(
        {
            'todo': this.todo
        }
    );
    this.todo = '';
}

在 Vue 中,我们的输入字段中有一个名为 v-model 的句柄。这实现了**双向绑定。输入字段代码如下:

<input type="text" v-model="todo"/>

V-Model 将输入字段的内容绑定到名为 toDoItem 的数据对象的键(key)上。当页面加载时,我们将 toDoItem 设置为空字符串,比如:todo:’ ‘。如果已经存在数据,例如 todo:‘添加文本处’,输入字段将加载添加文本处的输入内容。无论如何,将其作为空字符串,我们在输入字段中键入的任何文本都会绑定到 todo。这实际上是双向绑定(输入字段可以更新数据对象,数据对象可以更新输入字段)。

因此,回顾前面的 createNewToDoItem() 代码块,我们将 todo 的内容存放到列表数组中,然后将 todo 改为空字符串。

4.删除待办事项

React 的实现方法

deleteItem = indexToDelete => {
    this.setState(({ list }) => ({
      list: list.filter((toDo, index) => index !== indexToDelete)
    }));
};

尽管 deleteItem 函数位于 ToDo.js 文件中,但是从 ToDoItem.js 文件中引用它也很容易,将 deleteItem() 函数作为 上的 prop 传递:

<ToDoItem deleteItem={this.deleteItem.bind(this, key)}/>

这会将该函数传递给子组件,使其可以访问。我们绑定了 this 并传递 key 参数,当用户点击删除项时,函数通过 key 区分用户点击的是哪一条 ToDoItem。然后,在 ToDoItem 组件内部,我们执行以下操作:

<div className=”ToDoItem-Delete” onClick={this.props.deleteItem}>-</div> 

想要引用位于父组件内部的函数,只需引用 this.props.deleteItem 即可。

Vue 的实现方法

onDeleteItem(todo){
  this.list = this.list.filter(item => item !== todo);
}

Vue 的实现方法稍有不同,我们需要做到以下三点:

  1. 首先,在元素上调用函数:
<div class=”ToDoItem-Delete” @click=”deleteItem(todo)”>-</div>
  1. 然后我们必须创建一个 emit 函数,将其作为子组件的内部方法(在本例中为 ToDoItem.vue),如下所示:
deleteItem(todo) {
    this.$emit('delete', todo)
}
  1. 之后,你会发现,当我们添加 ToDo.vue 的 ToDoItem.vue 时,实际上引用了一个函数:
<ToDoItem v-for="todo in list" 
	:todo="todo" 
	@delete="onDeleteItem" // <-- this :)
	:key="todo.id" />

这就是所谓的自定义事件监听器。它会监听任何使用 ‘delete’ 字符串的触发事件。一旦监听到事件,它会触发一个名为 onDeleteItem 的函数。此函数位于 ToDo.vue 内部,而不是 ToDoItem.vue。如前所述,该函数只是过滤数据对象内的 todo 数组,以删除被点击的待办事项。

在 Vue 示例中还需要注意的是,我们可以在 @click 侦听器中编写 $emit 部分,这样更加简单,如下所示:

<div class=”ToDoItem-Delete” @click=”$emit(‘delete’, todo)”>-</div> 

如果你喜欢,这样做可以把 3 步减少到 2 步。

React 中的子组件可以通过 this.props 访问父函数,而在 Vue 中,你需要从子组件中发出事件,父组件来收集事件。

5.如何传递事件监听器

React 的实现方法

事件监听器处理简单事件(比如点击)非常直接。我们为待办事项创建了点击事件,用于创建新的待办事项,代码如下:

<div className=”ToDo-Add” onClick={this.createNewToDoItem}>+</div>

非常简单,就像使用 vanilla JS 处理内联 onClick 一样。正如前文所述,只要按下回车按钮,设置事件监听器就需要花费更长的时间。这需要输入标签处理 onKeyPress 事件,代码如下:

<input type=”text” onKeyPress={this.handleKeyPress}/>

该函数只要识别到’enter’键被按下,它就会触发 createNewToDoItem 函数,代码如下所示:

handleKeyPress = (e) => {
  if (e.key === Enter) {
		this.createNewToDoItem();
	}
};

Vue 的实现方法

Vue 的事件监听器更加直接。我们只需要使用一个简单的 @ 符号,就可以构建出我们想要的事件监听器。例如,想要添加 click 事件监听器,代码:

<div class=”ToDo-Add” @click=”createNewToDoItem()”>+</div> 

注意:@click 实际上是 v-on:click 的简写。Vue 事件监听器很强大,你可以为其选择属性,例如 .once 可以防止事件监听器被多次触发。此外,它还包含很多快捷方式。按下回车按钮时,React 就需要花费更长的时间来创建事件监听器,从而创建新的 ToDo 项目。在 Vue,代码如下:

<input type=”text” v-on:keyup.enter=”createNewToDoItem”/>

6.如何将数据传递给子组件

React 的实现方法

在 React 中,我们将 props 传递到子组件的创建处。比如:

<ToDoItem key={key} item={todo} />

此处我们向 ToDoItem 组件传递了两个 prop。之后,我们可以在子组件中通过 this.props 引用它们。因此,想要访问 item.todo prop,我们只需调用 this.props.item。

Vue 的实现方法

在 Vue 中,我们将 props 传递到子组件创建处的方式如下:

<ToDoItem v-for="todo in list" 
	:todo="todo"
	:key="todo.id"
	@delete="onDeleteItem" />

我们将它们传递给子组件中的 props 数组,如:props:[‘id’,’todo’]。然后可以在子组件中通过名字引用它们。

7.如何将数据发送回父组件

React 的实现方法

我们首先将函数传递给子组件,方法是在我们调用子组件时将其引用为 prop。然后我们通过引用 this.props.whateverTheFunctionIsCalled,为子组件添加调用函数,例如 onClick。然后,这将触发父组件中的函数。删除待办事项一节中详细介绍了整个过程。

Vue 的实现方法

在子组件中我们只需编写一个函数,将一个值发送回父函数。在父组件中编写一个函数来监听子组件何时发出该值的事件,监听到事件之后触发函数调用。同样,删除待办事项一节中详细介绍了整个过程。

8.总结

我们研究了添加、删除和更改数据,以 prop 形式从父组件到子组件传递数据,以及通过事件监听器的形式将数据从子组件发送到父组件。当然,React 和 Vue 之间存在一些小差异,希望本文的内容有助于理解这两个框架。

两个应用程序的 GitHub 地址:

**Vue ToDo:**https://github.com/sunil-sandhu/vue-todo

**React ToDo:**https://github.com/sunil-sandhu/react-todo

React 脚手架项目打包时的路径问题

2021-01-05 08:00:00

create-react-app 脚手架指令可以生成 React 项目,在项目完成之后执行打包命令 npm run build,文件夹内生成 build文件夹,就是打包后的文件,在浏览器运行 index.html 发现页面空白,打开控制台有报错,css 和 js 文件的引用路径有问题。

浏览 index.html 代码可以发现:

<!-- index.html -->

<!doctype html>
<html lang="zh">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta name="description" content="Web site created using create-react-app" />
    <title>React App</title>
    <link href="/static/css/main.3394d54e.chunk.css" rel="stylesheet">
</head>

<body><noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
    <script>!function (e) { function r(r) { for (var n, l, f = r[0], i = r[1], a = r[2], c = 0, s = []; c < f.length; c++)l = f[c], Object.prototype.hasOwnProperty.call(o, l) && o[l] && s.push(o[l][0]), o[l] = 0; for (n in i) Object.prototype.hasOwnProperty.call(i, n) && (e[n] = i[n]); for (p && p(r); s.length;)s.shift()(); return u.push.apply(u, a || []), t() } function t() { for (var e, r = 0; r < u.length; r++) { for (var t = u[r], n = !0, f = 1; f < t.length; f++) { var i = t[f]; 0 !== o[i] && (n = !1) } n && (u.splice(r--, 1), e = l(l.s = t[0])) } return e } var n = {}, o = { 1: 0 }, u = []; function l(r) { if (n[r]) return n[r].exports; var t = n[r] = { i: r, l: !1, exports: {} }; return e[r].call(t.exports, t, t.exports, l), t.l = !0, t.exports } l.m = e, l.c = n, l.d = function (e, r, t) { l.o(e, r) || Object.defineProperty(e, r, { enumerable: !0, get: t }) }, l.r = function (e) { "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) }, l.t = function (e, r) { if (1 & r && (e = l(e)), 8 & r) return e; if (4 & r && "object" == typeof e && e && e.__esModule) return e; var t = Object.create(null); if (l.r(t), Object.defineProperty(t, "default", { enumerable: !0, value: e }), 2 & r && "string" != typeof e) for (var n in e) l.d(t, n, function (r) { return e[r] }.bind(null, n)); return t }, l.n = function (e) { var r = e && e.__esModule ? function () { return e.default } : function () { return e }; return l.d(r, "a", r), r }, l.o = function (e, r) { return Object.prototype.hasOwnProperty.call(e, r) }, l.p = "./"; var f = this.webpackJsonpdemo = this.webpackJsonpdemo || [], i = f.push.bind(f); f.push = r, f = f.slice(); for (var a = 0; a < f.length; a++)r(f[a]); var p = i; t() }([])</script>
    <script src="/static/js/2.1902eda2.chunk.js"></script>
    <script src="/static/js/main.cbf35db7.chunk.js"></script>
</body>

</html>

发现 css 和 js 文件都是用绝对路径引用的,这是 webpack 打包一贯拥有的问题,将 /static 改成 ./static ,再运行,页面没有问题成功显示。

但是在写实际项目的时候手动修改相对路径,不但麻烦,而且容易引起其他问题。因此直接在项目的 package.json 配置文件进行修改,添加 "homepage": "."

// package.json

{
  "name": "demo",
  "version": "0.1.0",
  "private": true,
  "homepage": ".",  // 添加这一行
  "dependencies": {
    "@babel/core": "7.12.3",
    ...

然后再执行 npm run build ,运行打包后的文件,没有任何问题。

如何在 React 中引入 less?

2021-01-04 08:00:00

本文主要写如何在 React 中引入 less。因为 less 和 css 非常像,因此很容易学习。而且 less 仅对 css 语言增加了少许方便的扩展,这就是 less 如此易学的原因之一。

1.安装 less

npm install less less-loader --save-dev

2.暴露 webpack 文件

利用 npx create-react-app 搭建的 React 项目,默认隐藏 webpack 配置文件,引入 less 需要修改 webpack 配置文件,因此我们需要执行命令暴露 webpack 配置文件。

这里需要注意,一旦暴露,无法回退。

npm run eject

如果这一步失败,执行下列命令:

git add .

git commit -m "init"

然后再执行 npm run eject

注意:暴露 webpack 文件只能在 create-react-app 之初,一旦项目结构发生改变,再进行暴露操作就会失败。所以尽量在项目建立时就进行 npm run eject 操作。

3.修改 wenpack.config.js 配置

在合适的位置添加:

// 放在 
// const sassRegex = /\.(scss|sass)$/;
// const sassModuleRegex = /\.module\.(scss|sass)$/; 
// 后面

const lessRegex = /\.less$/;
const lessModuleRegex = /\.module\.less$/;
// 放在 oneof 数组下

{
  test: lessRegex,
  exclude: lessModuleRegex,
  use: getStyleLoaders(
    {
      importLoaders: 2,
      sourceMap: isEnvProduction && shouldUseSourceMap,
    },
    'less-loader'
  ),
  // Don't consider CSS imports dead code even if the
  // containing package claims to have no side effects.
  // Remove this when webpack adds a warning or an error for this.
  // See https://github.com/webpack/webpack/issues/6571
  sideEffects: true,
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
  test: lessModuleRegex,
  use: getStyleLoaders(
    {
      importLoaders: 2,
      sourceMap: isEnvProduction && shouldUseSourceMap,
      modules: true,
      getLocalIdent: getCSSModuleLocalIdent,
    },
    'less-loader'
  ),
},

4.如何使用 less

新建一个 App.less 文件,然后在 App.js 中引入:

import './Map.less'

less 语法手册

React 学习笔记(三)

2020-12-29 08:00:00

来源于:https://react.docschina.org/docs/forms.html

一、表单

在 React 中,html 表单元素的工作方式和其他的 DOM 元素不太一样,因为表单内部通常会保持一些内部的 state,比如下面这个纯 html 表单只接受一个名称:

<form>
  <label>
    名字:
    <input type="text" name="name" />
  </label>
  <input type="submit" value="提交" />
</form>

这个表单具有默认的 html 表单行为,即在用户提交表单之后自动刷新页面。如果你在 React 中执行相同的代码,它仍然有效。

但大多数情况下,使用 JavaScript 函数可以很方便的处理表单的提交,同时还可以访问用户填写的表单数据。实现这种效果的标准方式是使用“受控组件”。

受控组件

在 html 中,表单元素比如<input><textarea><select>,这些表单元素通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState() 来更新。

我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。

例如,如果我们想让前一个示例在提交时打印出名称,我们可以将表单写为受控组件:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('提交的名字:' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          名字:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

由于在表单元素上设置了 value 属性,因此显示的值将始终为 this.state.value,这使得 React 的 state 成为唯一数据源。由于 handlechange 在每次按键时都会执行并更新 React 的 state,因此显示的值将随着用户输入而更新。

对于受控组件来说,输入的值始终由 React 的 state 驱动。你也可以将 value 传递给其他 UI 元素,或者通过其他事件处理函数重置,但这意味着你需要编写更多的代码。

textarea 标签

在 html 中,<textarea> 元素通过其子元素定义其文本:

<textarea>
  测试!
</textarea>

而在 React 中,<textarea> 使用 value 属性代替。这样,可以使得使用 <textarea> 的表单和使用单行 input 的表单非常类似:

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: '请撰写一篇关于你喜欢的 DOM 元素的文章。'
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('提交的文章:' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          文章:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

请注意,this.state.value 初始化于构造函数中,因此文本区域默认有初值。

select 标签

在 HTML 中,<select> 创建下拉列表标签。例如,如下 html 创建了水果相关的下拉列表:

<select>
  <option value="grapefruit">葡萄柚</option>
  <option value="lime">酸橙</option>
  <option selected value="coconut">椰子</option>
  <option value="mango">芒果</option>
</select>

请注意,由于 selected 属性的缘故,椰子选项默认被选中。React 并不会使用 selected 属性,而是在根 select 标签上使用 value 属性。这在受控组件中更便捷,因为您只需要在根标签中更新它。例如:

class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'coconut'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('你喜欢的风味是:' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          选择你喜欢的风味
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">葡萄柚</option>
            <option value="lime">酸橙</option>
            <option value="coconut">椰子</option>
            <option value="mango">芒果</option>
          </select>
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

总的来说,这使得 <input type="text">, <textarea><select> 之类的标签都非常相似—它们都接受一个 value 属性,你可以使用它来实现受控组件。

注意:你可以将数组传递到 value 属性中,以支持在 select 标签中选择多个选项:

<select multiple={true} value={['B', 'C']}>

文件 input 标签

在 HTML 中,<input type="file"> 允许用户从存储设备中选择一个或多个文件,将其上传到服务器,或通过使用 JavaScript 的 File API 进行控制。

<input type="file" />

因为它的 value 只读,所以它是 React 中的一个非受控组件。

处理多个输入

当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作。

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.name === 'isGoing' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          参与:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          来宾人数
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

这里使用了 ES6 计算属性名称的语法更新给定输入名称对应的 state 值:

例如:

this.setState({
  [name]: value
});

等同于 es5:

var partialState = {};
partialState[name] = value;
this.setState(partialState);

另外,由于 setState() 自动将部分 state 合并到当前 state, 只需调用它更改部分 state 即可。

受控输入空值

在受控组件上指定 value 的 prop 会阻止用户更改输入。如果你指定了 value,但输入仍可编辑,则可能是你意外地将 value 设置为 undefinednull

下面的代码演示了这一点。(输入最初被锁定,但在短时间延迟后变为可编辑。)

ReactDOM.render(<input value="hi" />, mountNode);

setTimeout(function() {
  ReactDOM.render(<input value={null} />, mountNode);
}, 1000);

受控组件的替代品

有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理函数,并通过一个 React 组件传递所有的输入 state。当你将之前的代码库转换为 React 或将 React 应用程序与非 React 库集成时,这可能会令人厌烦。在这些情况下,你可能希望使用非受控组件,这是实现输入表单的另一种方式。

成熟的解决方案

如果你想寻找包含验证、追踪访问字段以及处理表单提交的完整解决方案,使用 Formik 是不错的选择。然而,它也是建立在受控组件和管理 state 的基础之上 —— 所以不要忽视学习它们。

2020 年度总结

2020-12-25 08:00:00

年度总结这个东西很有意思(我觉得),可以静下心来好好回顾一下这一年做了些什么事情,希望我能养成这个好习惯,多总结。

这个博客是我在年中搭建起来了,到现在也将近半年了,没怎么做过 seo 优化,也没怎么想过推广,就简简单单的写一些自己的东西。更大的原因是我的文笔太拙劣,不敢让人发现,哈哈哈。

大学四年过的还算充实,学到了自己感兴趣的东西,培养的业余兴趣爱好,旅游爬山等,但有名的大山目前只爬了嵩山(河南境内),因为在郑州读的大学。更远的山也没怎么去过,我给自己定个计划,在两年内爬一次泰山,因为工作太忙咯,不敢说短期内一定有时间。想体会一下"会当凌绝顶,一览众山小"的感觉。

但是吧,我的大学结束的有些虎头蛇尾的感觉,因为疫情的原因,学校禁止进入,一直到毕业前夕才允许分批次返校,很多同学都没有看到就草草结束大学生活。所以心心念念的毕业照也没有了,本来和朋友商量好一起拍一组有意思的毕业照,真的是今年最大的遗憾了。

离开校园,见面的机会真的就少了很多,基本都是打打电话,视频什么的,各奔东西,为生活开始忙碌。

2020 年,是让人印象深刻的一年,从一月份爆发的疫情开始,胆战心惊,一直到现在,依旧在全球蔓延,就在前两天看新闻,说英国要严重了,病毒变异,又封城了。真的希望这场疫情赶快过去。

为什么国外对这方面对管控好像没那么严?真的不理解,或许这就是"向往自由"吧。希望地球每个人都能重视起来,共同对抗病毒,早日结束这一场。

今年从郑州来到了杭州,换了一个新的环境,在工作中和大家有着共同的目标,学到了很多东西,但是也有一些遗憾,本来打算读两本书的的,下半年读了余果的《全栈工程师的自我修养》和《css 世界》,前段时间还写了读书笔记《对全栈的一些思考》,然后十一月份在某宝买了《javascript 高级程序设计》,结果因为时间不充足,最后也没读完。

在这里定一个小目标,2021 年一定要读完四本书。

在技术栈这一块,由 Vue 转向了 React,这里实在想吐槽一句。React 是 Facebook 开发的,Vue 是尤大大开发出来的,感觉写起来思维逻辑不太一样,Vue 的写法更偏向中国程序员的思维方式,React 更偏向外国人,因为我也不是外国人,所以也说不好是一种什么样的思维方式,只是感觉理解起来比 Vue 更难一些。但是因为公司的原因,开始着手 React 开发项目。

希望在新的一年继续延续下去,在学习更多的技术栈的同时,把自己要做的事情做的更好。

在杭州待了也有一段时间,这个城市看起来很年轻,很有朝气,可能因为年轻人的占比比较大吧。偶尔出去玩玩,能发现一些以前发现不了的东西,开阔一下自己的眼界。这里夸一下,西湖的风景确实不错,走在苏堤,吹吹风,整个人都放松了。平时压力大了,都会出来走走,但是好像一个人旅游好像缺了点乐趣。

这两天是圣诞节,虽说是洋节,但是自己也可以乐呵一下,在这里祝愿大家都能在 2020 年有一个好的结束,画上圆满的句号,迎接新的 2021 年。

抽出空闲的时间简单的写了一下总结,就好像跟看电影一样,又回顾了一遍剧情,又得到了不一样的感受。

你好,自己,明年见。

React --万物皆可组件

2020-12-21 08:00:00

一、React 组件是什么?

组件,从概念上类似于 javascript 函数,他接受任意入参(props),并返回作用于描述页面展示内容的 React 元素。

组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。

1.创建一个组件

// 函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// class 组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

2.渲染组件

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

ReactDOM.render(
  <Welcome />,
  document.getElementById('root')
);

3.组件

React 的基础原则有三条,分别是:

  1. React 界面完全由数据驱动
  2. React 中一切都是组件
  3. props 是 React 组件之间通讯的基本方式

给组件输入一个参数,最终返回一个 React Element,React Element 就是在页面上展示的内容,相当于一个 DOM 节点

React 一大核心就是--万物皆可组件

一个复杂的界面可以分割很很多简单的组件,每个简单的组件又可以分割成很多小组件

有些类似于类和对象的概念

二、组件的划分

  1. **无状态组件:**无状态组件 (Stateless Component) 是最基础的组件形式,由于没有状态的影响所以就是纯静态展示的作用。基本组成结构就是属性(props)加上一个渲染函数(render)。由于不涉及到状态的更新,所以这种组件的复用性也最强。例如在各 UI 库中开发的按钮、输入框、图标等等。

  2. **有状态组件:**组件内部包含状态(state)且状态随着事件或者外部的消息而发生改变的时候,这就构成了有状态组件(Stateful Component)。有状态组件通常会带有生命周期 (lifecycle),用以在不同的时刻触发状态的更新。在写业务逻辑时常用到,不同场景所用的状态和生命周期也会不同。

  3. **容器组件:**为使组件的职责更加单一,耦合性进一步地降低,引入了容器组件 (Container Component) 的概念。重要负责对数据获取以及处理的逻辑。下文的设计模式也会提到。

  4. 高阶组件:“高阶组件(HoC)”也算是种组件设计模式。做为一个高阶组件,可以在原有组件的基础上,对其增加新的功能和行为。如打印日志,获取数据和校验数据等和展示无关的逻辑的时候,抽象出一个高阶组件,用以给基础的组件增加这些功能,减少公共的代码。

  5. **Render Callback 组件:**组件模式是在组件中使用渲染回调的方式,将组件中的渲染逻辑委托给其子组件。也是种重用组件逻辑的方式,也叫 render props 模式。

三、设计原则

React 的组件其实是软件设计中的模块,其设计原则也需遵从通用的组件设计原则,简单说来,就是要减少组件之间的耦合性(Coupling),让组件简单,这样才能让整体系统易于理解、易于维护。

即,设计原则:

就像搭积木,复杂的应用和组件都是由简单的界面和组件组成的。划分组件也没有绝对的方法,选择在当下场景合适的方式划分,充分利用组合即可。实际编写代码也是逐步精进的过程,努力做到:

javascript 进阶问题

2020-12-19 08:00:00

Github: https://github.com/lydiahallie/javascript-questions

相当不错的一个 Github 仓库,javascript questions ,作者每周都会发布一些有关 javascript 的题目,虽然不难但是考察细节,刷了一会,其实一些很简单的问题,结果因为细节思考不到位直接出错。

引用作者的话:我在我的 Instagram 上每天都会发布 JavaScript 的多选问题,并且同时也会在这个仓库中发布。

从基础到进阶,测试你有多了解 JavaScript,刷新你的知识,或者帮助你的 coding 面试!

React 学习笔记(二)

2020-12-16 08:00:00

React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同

React 事件的命名采用小驼峰式(camelCase),而不是纯小写

使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串

一、事件处理

1.事件绑定

React 元素的事件处理和 DOM 元素类似,但是在语法上有些区别,比如:

传统的 html:用双引号包裹,后面必须跟参数

<button onclick="myfun()">点击</button>

React:用大括号包裹,后面不跟参数

<button onclick={myfun}>点击</button>

一个完整的事件函数代码如下

class Demo extends React.Component {
    render() {

        // 事件函数
        function myfun() {
            alert('helo,world')
        }

        return (
            // 绑定事件
            <button onClick={this.myfun}>
                Activate Lasers
            </button>
        )
    }
}

ReactDOM.render(
    <Demo />,
    document.getElementById('root')
)

如果方法后面没有(),则需要为这个方法绑定 this

2.阻止默认行为

在 React 中还有一个不同的点,不能通过返回 fasle 阻止默认行为,React 提供了一个属性--preventDefault,可以通过 preventDefault 阻止脚本执行

看一下传统的 html 和 React 的对比

<a href="#" onclick="alert('是否弹窗?');return false">
    Click me
</a>

直接在写上 false 就可以阻止脚本执行

React 通过 preventDefault 属性阻止脚本执行:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

二、条件渲染

在 React 中,可以创建不同的组件来封装各种你需要的行为,然后,根据应用不同的状态,你可以只渲染对应状态下的部分内容。

React 中的条件渲染和 javascript 中的一样,使用 if 运算符来表示元素当前的状态,然后让 React 根据他们来更新 UI。

使用 if..else 语句进行条件渲染

先写一个条件渲染的例子,定义了两个组件,然后通过判断组件 Greeting 中的变量 isLoggedIn 的真伪,让浏览器渲染组件 UserGreeting 或者 GuestGreeting

// App.js
import React, { Component } from 'react'

export default class App extends Component {
  render() {
    function UserGreeting(props) {
      return <h3>Welcome back!</h3>;
    }

    function GuestGreeting(props) {
      return <h3>Please sign up.</h3>;
    }

    function Greeting(props) {
      const isLoggedIn = props.isLoggedIn;
      if (isLoggedIn) {
        return <UserGreeting />;
      }   
      return <GuestGreeting />;
    }

    return (
      <div>
        <Greeting isLoggedIn={false} />
      </div>
    )
  }
}

最后变量 isLoggedIn 定义了 false,因此,浏览器渲染 `GuestGreeting。

怎么阻止条件渲染?

在有些情况下,我们希望能隐藏组件,即使他已经被其他组件渲染。我们可以通过 render 方法返回 null 让组件不渲染。

下面的示例中, 会根据 prop 中 warn 的值来进行条件渲染。如果 warn 的值是 false,那么组件则不会渲染:

function WarningBanner(props) {
  if (!props.warn) {
    return null;
  }

  return (
    <div className="warning">
      Warning!
    </div>
  );
}

class Page extends React.Component {
  constructor(props) {
    super(props);
    this.state = {showWarning: true};
    this.handleToggleClick = this.handleToggleClick.bind(this);
  }

  handleToggleClick() {
    this.setState(state => ({
      showWarning: !state.showWarning
    }));
  }

  render() {
    return (
      <div>
        <WarningBanner warn={this.state.showWarning} />
        <button onClick={this.handleToggleClick}>
          {this.state.showWarning ? 'Hide' : 'Show'}
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <Page />,
  document.getElementById('root')
);

三、渲染列表

先看一段代码,我们使用 map() 函数让数组中的每一项变双倍,然后得到一个新的数组 doubled 并打印出来。

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);

// [2,4,6,8,10]

而在 React 中,把数组转换为元素列表的过程是相似的。

先通过 map() 方法遍历 numbers 数组,将数组中的每个元素变成 <li> 标签,最后将得到的数组赋值给 listItems

然后返回 {listItem}

// Map.js
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li>{number}</li>
);

ReactDOM.render(
  <ul>{listItems}</ul>,
  document.getElementById('root')
);

运行之后浏览器出现 1-5 的无序列表

1.分离组件

上面就是一个基本的列表渲染的例子,但是数据写死了。接下来我们将数组重构成一个组件,以后再进行数组渲染时,可以轻松调用。

// Map.js
export default class Map extends Component {
  render() {

  // 分离出组件 NumberList 作为转换数组的组件
    function NumberList(props) {
      const numbers = props.numbers;
      const listItems = numbers.map((number) =>
        <li>{number}</li>
      );
      return (
        <ul>{listItems}</ul>
      );
    }

    // 传入需要的数据
    const numbers = [1, 2, 3, 4, 5, 6, 7];

    return (
      <div>
        <NumberList numbers={numbers} />
      </div>
    )
  }
}

2.key

运行代码之后,页面会正常显示,但是控制台会报一个错误。Each child in a list should have a unique "key" prop.,意思是当你创建一个元素时,必须包括一个特殊的 key 属性。

现在给每个列表元素分配一个 key:

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

3.使用 id 作为 key

key 帮助 React 识别了哪些元素被改变,比如删除和添加,所以应当给每个元素确定一个标识,也就是 key

一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用数据中的 id 来作为元素的 key

// Map.js
export default class Map extends Component {
  render() {

    function NumberList(props) {
      const numbers = props.numbers;
      const listItems = numbers.map((number) =>
        <li key={number.id}>  // 赋值 key
          {number.text}
        </li>
      );
      return (
        <ul>{listItems}</ul>
      );
    }

    // 传入数据
    const numbers = [
      {id: 1,text: 1},
      {id: 2,text: 2},
      {id: 3,text: 3},
      {id: 4,text: 4},
      {id: 5,text: 5}
    ];

    return (
      <Fragment>
        <NumberList numbers={numbers} />
      </Fragment>
    )
  }
}

4.索引 index 可以作为 key 吗?

当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key

const todoItems = todos.map((todo, index) =>
  // 仅仅当没有确定 id 的时候使用索引 index 作为 key
  <li key={index}>
    {todo.text}
  </li>
);

如果列表项目的顺序可能会变化,我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。

5.用 key 提取组件

比方说,如果你提取出一个 ListItem 组件,你应该把 key 保留在数组中的这个 <ListItem /> 元素上,而不是放在 ListItem 组件中的 <li> 元素上。

错误的使用方法:

function ListItem(props) {
  const value = props.value;
  return (
    // 错误!你不需要在这里指定 key:
    <li key={value.toString()}>
      {value}
    </li>
  );
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // 错误!元素的 key 应该在这里指定:
    <ListItem value={number} />
  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

正确的使用方法:

function ListItem(props) {
  // 正确!这里不需要指定 key:
  return <li>{props.value}</li>;
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // 正确!key 应该在数组的上下文中被指定
    <ListItem key={number.toString()}              value={number} />

  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

React:一个好的经验法则是:在 map( ) 方法中的元素需要设置 key 属性。

6.key 只是在兄弟节点之间必须唯一

数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值:

function Blog(props) {
  const sidebar = (
    <ul>
      {props.posts.map((post) =>
        <li key={post.id}>
          {post.title}
        </li>
      )}
    </ul>
  );
  const content = props.posts.map((post) =>
    <div key={post.id}>
      <h3>{post.title}</h3>
      <p>{post.content}</p>
    </div>
  );
  return (
    <div>
      {sidebar}
      <hr />
      {content}
    </div>
  );
}

const posts = [
  {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
  {id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
  <Blog posts={posts} />,
  document.getElementById('root')
);

7.vue 中渲染列表

Vue 中渲染列表使用的是特殊指令 v-for,其中也有 key 的相关用法

React 中采用的是 map() 方法遍历数组,然后渲染列表


title: React 学习笔记(二) date: 2020-12-16 13:15:00 updated: 2021-11-09 15:19:31 categories:


React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同

React 事件的命名采用小驼峰式(camelCase),而不是纯小写

使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串

一、事件处理

1.事件绑定

React 元素的事件处理和 DOM 元素类似,但是在语法上有些区别,比如:

传统的 html:用双引号包裹,后面必须跟参数

<button onclick="myfun()">点击</button>

React:用大括号包裹,后面不跟参数

<button onclick={myfun}>点击</button>

一个完整的事件函数代码如下

class Demo extends React.Component {
    render() {

        // 事件函数
        function myfun() {
            alert('helo,world')
        }

        return (
            // 绑定事件
            <button onClick={this.myfun}>
                Activate Lasers
            </button>
        )
    }
}

ReactDOM.render(
    <Demo />,
    document.getElementById('root')
)

如果方法后面没有(),则需要为这个方法绑定 this

2.阻止默认行为

在 React 中还有一个不同的点,不能通过返回 fasle 阻止默认行为,React 提供了一个属性--preventDefault,可以通过 preventDefault 阻止脚本执行

看一下传统的 html 和 React 的对比

<a href="#" onclick="alert('是否弹窗?');return false">
    Click me
</a>

直接在写上 false 就可以阻止脚本执行

React 通过 preventDefault 属性阻止脚本执行:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

二、条件渲染

在 React 中,可以创建不同的组件来封装各种你需要的行为,然后,根据应用不同的状态,你可以只渲染对应状态下的部分内容。

React 中的条件渲染和 javascript 中的一样,使用 if 运算符来表示元素当前的状态,然后让 React 根据他们来更新 UI。

使用 if..else 语句进行条件渲染

先写一个条件渲染的例子,定义了两个组件,然后通过判断组件 Greeting 中的变量 isLoggedIn 的真伪,让浏览器渲染组件 UserGreeting 或者 GuestGreeting

// App.js
import React, { Component } from 'react'

export default class App extends Component {
  render() {
    function UserGreeting(props) {
      return <h3>Welcome back!</h3>;
    }

    function GuestGreeting(props) {
      return <h3>Please sign up.</h3>;
    }

    function Greeting(props) {
      const isLoggedIn = props.isLoggedIn;
      if (isLoggedIn) {
        return <UserGreeting />;
      }   
      return <GuestGreeting />;
    }

    return (
      <div>
        <Greeting isLoggedIn={false} />
      </div>
    )
  }
}

最后变量 isLoggedIn 定义了 false,因此,浏览器渲染 `GuestGreeting。

怎么阻止条件渲染?

在有些情况下,我们希望能隐藏组件,即使他已经被其他组件渲染。我们可以通过 render 方法返回 null 让组件不渲染。

下面的示例中, 会根据 prop 中 warn 的值来进行条件渲染。如果 warn 的值是 false,那么组件则不会渲染:

function WarningBanner(props) {
  if (!props.warn) {
    return null;
  }

  return (
    <div className="warning">
      Warning!
    </div>
  );
}

class Page extends React.Component {
  constructor(props) {
    super(props);
    this.state = {showWarning: true};
    this.handleToggleClick = this.handleToggleClick.bind(this);
  }

  handleToggleClick() {
    this.setState(state => ({
      showWarning: !state.showWarning
    }));
  }

  render() {
    return (
      <div>
        <WarningBanner warn={this.state.showWarning} />
        <button onClick={this.handleToggleClick}>
          {this.state.showWarning ? 'Hide' : 'Show'}
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <Page />,
  document.getElementById('root')
);

三、渲染列表

先看一段代码,我们使用 map() 函数让数组中的每一项变双倍,然后得到一个新的数组 doubled 并打印出来。

const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((number) => number * 2);
console.log(doubled);

// [2,4,6,8,10]

而在 React 中,把数组转换为元素列表的过程是相似的。

先通过 map() 方法遍历 numbers 数组,将数组中的每个元素变成 <li> 标签,最后将得到的数组赋值给 listItems

然后返回 {listItem}

// Map.js
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li>{number}</li>
);

ReactDOM.render(
  <ul>{listItems}</ul>,
  document.getElementById('root')
);

运行之后浏览器出现 1-5 的无序列表

1.分离组件

上面就是一个基本的列表渲染的例子,但是数据写死了。接下来我们将数组重构成一个组件,以后再进行数组渲染时,可以轻松调用。

// Map.js
export default class Map extends Component {
  render() {

  // 分离出组件 NumberList 作为转换数组的组件
    function NumberList(props) {
      const numbers = props.numbers;
      const listItems = numbers.map((number) =>
        <li>{number}</li>
      );
      return (
        <ul>{listItems}</ul>
      );
    }

    // 传入需要的数据
    const numbers = [1, 2, 3, 4, 5, 6, 7];

    return (
      <div>
        <NumberList numbers={numbers} />
      </div>
    )
  }
}

2.key

运行代码之后,页面会正常显示,但是控制台会报一个错误。Each child in a list should have a unique "key" prop.,意思是当你创建一个元素时,必须包括一个特殊的 key 属性。

现在给每个列表元素分配一个 key:

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

3.使用 id 作为 key

key 帮助 React 识别了哪些元素被改变,比如删除和添加,所以应当给每个元素确定一个标识,也就是 key

一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用数据中的 id 来作为元素的 key

// Map.js
export default class Map extends Component {
  render() {

    function NumberList(props) {
      const numbers = props.numbers;
      const listItems = numbers.map((number) =>
        <li key={number.id}>  // 赋值 key
          {number.text}
        </li>
      );
      return (
        <ul>{listItems}</ul>
      );
    }

    // 传入数据
    const numbers = [
      {id: 1,text: 1},
      {id: 2,text: 2},
      {id: 3,text: 3},
      {id: 4,text: 4},
      {id: 5,text: 5}
    ];

    return (
      <Fragment>
        <NumberList numbers={numbers} />
      </Fragment>
    )
  }
}

4.索引 index 可以作为 key 吗?

当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key

const todoItems = todos.map((todo, index) =>
  // 仅仅当没有确定 id 的时候使用索引 index 作为 key
  <li key={index}>
    {todo.text}
  </li>
);

如果列表项目的顺序可能会变化,我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。

5.用 key 提取组件

比方说,如果你提取出一个 ListItem 组件,你应该把 key 保留在数组中的这个 <ListItem /> 元素上,而不是放在 ListItem 组件中的 <li> 元素上。

错误的使用方法:

function ListItem(props) {
  const value = props.value;
  return (
    // 错误!你不需要在这里指定 key:
    <li key={value.toString()}>
      {value}
    </li>
  );
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // 错误!元素的 key 应该在这里指定:
    <ListItem value={number} />
  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

正确的使用方法:

function ListItem(props) {
  // 正确!这里不需要指定 key:
  return <li>{props.value}</li>;
}

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    // 正确!key 应该在数组的上下文中被指定
    <ListItem key={number.toString()}              value={number} />

  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

React:一个好的经验法则是:在 map( ) 方法中的元素需要设置 key 属性。

6.key 只是在兄弟节点之间必须唯一

数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值:

function Blog(props) {
  const sidebar = (
    <ul>
      {props.posts.map((post) =>
        <li key={post.id}>
          {post.title}
        </li>
      )}
    </ul>
  );
  const content = props.posts.map((post) =>
    <div key={post.id}>
      <h3>{post.title}</h3>
      <p>{post.content}</p>
    </div>
  );
  return (
    <div>
      {sidebar}
      <hr />
      {content}
    </div>
  );
}

const posts = [
  {id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
  {id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
  <Blog posts={posts} />,
  document.getElementById('root')
);

7.vue 中渲染列表

Vue 中渲染列表使用的是特殊指令 v-for,其中也有 key 的相关用法

React 中采用的是 map() 方法遍历数组,然后渲染列表

吐槽--控制好自己的情绪

2020-12-13 08:00:00

缘于一些小事,本想发在朋友圈里,但是熟人挺多,不方便发,就在博客里吐槽一下

一个正常的人际交往应该是互相传递出好的情绪,开心的事情,这样不但有利于两个人之间的交往,而且对个人对身心健康也是有帮助的

正面的情绪绝对有助于自己的身体和心理健康,反观整体郁郁寡欢,跟别人说自己多不顺,和别人吵架,不好的情绪就传达给另一个人了

并不是说不能找人倾述自己生活的不痛快和不开心,但是作为一个成年人了,应该要把握好那个感觉,不要真把别人当垃圾桶了,什么都往里倒,宣泄

情侣之间亦是如此,再好的关系,如果每天散发着各种压抑的情绪,爆发是迟早的事

家家有本难念的经

每个人都会有自己的烦心事,都能理解,毕竟生活确实很苦闷,但要学会自己调节自己。

我也会经常烦闷,但是我选择做一些爱好的事情来排遣掉自己的不好的情绪,并不会将这些传递给别人,所以我也算过的很开心吧

成年人,不就是要学会自己克制自己的缺点,希望每个人都能生活的很愉快!

React 中的占位符 Fragment

2020-12-12 08:00:00

在 React 项目中,render 方法只能有一个根元素,一般都是 <div> <div/> ,然后在里面写上我们的组件,渲染到浏览器一看,除了我们想要显示的组件,外面还套着一层 div,如果在写项目的时候,套了很多曾组件,那么每一层都会多出来一个父级元素 div,不美观,而且在调整样式的时候会有些麻烦

因此,React 提供了一个占位符 Fragment,写法是:

// index.js

import React, { Component,Fragment } from 'react'

export default class index extends Component {
    render() {
        return (
            <Fragment>
                <h2>hello,wolrd</h2>
            </Fragment>
        )
    }
}

在引入 React 的时候,增加一个属性 Fragment,然后 render()方法下唯一的根元素我们用 <Fragment> </Fragment> 来代替,这时候再看浏览器,就不会显示多余的标签了,直接显示 <h2>标签

为什么不可变性在 React 中那么重要?

2020-12-02 08:00:00

根据官网文档来解释,为什么不可变性的概念在 React 中非常重要的原因,一般来说,有两种改变数据的方式。第一种方式是直接修改变量的值,第二种方式是使用新的一份数据替换旧数据

React 文档

一般来说,有两种改变数据的方式。第一种方式是直接修改变量的值,第二种方式是使用新的一份数据替换旧数据

直接修改数据

var player = {score: 1, name: 'Jeff'};
player.score = 2;
// player 修改后的值为 {score: 2, name: 'Jeff'}

新数据替换旧数据

var player = {score: 1, name: 'Jeff'};

var newPlayer = Object.assign({}, player, {score: 2});
// player 的值没有改变,但是 newPlayer 的值是 {score: 2, name: 'Jeff'}

// 使用对象展开语法,就可以写成:
// var newPlayer = {...player, score: 2};

不直接修改(或改变底层数据)这种方式和前一种方式的结果是一样的,这种方式有以下几点好处:

简化复杂的功能

不可变性使得复杂的特性更容易实现。在后面的章节里,我们会实现一种叫做“时间旅行”的功能。“时间旅行”可以使我们回顾井字棋的历史步骤,并且可以“跳回”之前的步骤。这个功能并不是只有游戏才会用到——撤销和恢复功能在开发中是一个很常见的需求。不直接在数据上修改可以让我们追溯并复用游戏的历史记录

跟踪数据的改变

如果直接修改数据,那么就很难跟踪到数据的改变。跟踪数据的改变需要可变对象可以与改变之前的版本进行对比,这样整个对象树都需要被遍历一次

确定在 React 中何时重新渲染

不可变性最主要的优势在于它可以帮助我们在 React 中创建 pure components。我们可以很轻松的确定不可变数据是否发生了改变,从而确定何时对组件进行重新渲染

关于 react 的一份学习计划

2020-11-27 08:00:00

有关 react 框架的学习计划,除了之前推荐的 react 开源作者写的学习建议,我还参考了阮一峰的 react 实例教程,写的通俗易懂,又难易并进,可以很快的掌握 react 的核心要点

之前说了,为了公司的项目决定学习 react 框架,看了一份 react.js 开源作者的一份学习计划,react 学习路径--怎么学习 react?个人感觉还是不错,但是需要看的文档比较多且官方文档没有那么详细的讲解,只说个原理,有些难以参透。

在网络上浏览很久,发现大家对阮一峰的 react 教程风评不错,就去看了一下。

很适合刚接触 react 的人,可以很快的上手,再参照一下官方文档,理解上来就很深入且进一步了。

react 官方文档

阮一峰的网络日志-react 入门实例教程

阮一峰:我学习 React 时,就很苦恼。有的教程讨论一些细节问题,对入门没帮助;有的教程写得不错,但比较短,无助于看清全貌。我断断续续学了几个月,看过二十几篇教程,在这个过程中,将对自己有帮助的 Demo 都收集下来,做成了一个库React Demos

下面,我就根据这个库,写一篇全面又易懂的 React 入门教程。你只需要跟着每一个 Demo 做一遍,就能初步掌握 React。当然,前提是你必须拥有基本 JavaScript 和 DOM 知识,但是你读完就会发现,React 所要求的预备知识真的很少。

github »> ruanyf/react-demos

把这个库 clone 下来,然后跟着练习。

只要有一定的 javascript 基本,我觉得学习 react 并不困难,当然这只是入门,想要更深入的了解他,我觉得还有一段路程去经历,以后也会慢慢记录在 react 使用中遇到的问题,以及一些知识点的笔记。

webpack 安装配置指令

2020-11-26 08:00:00

进行重新加载编译。实际就是将浏览器不认识的语法编译成浏览器认识的语法。比如 less 编译成 css,ES6 语法 转成 ES5 等等。

减少 io 请求,通常我们在请求后,会返回一个 html 到浏览器。这时,我们如果打开控制台,就会发现在 html 页面通过 script,link 等标签引用的静态资源,浏览器会再次发出请求去获取这些资源。但是 webpack 的打包,将所有的静态资源都合并好了,减少了 io 请求。

# 安装webpack
npm install --save-dev webpack
# 安装webpack-cli依赖
npm install --save-dev webpack-cli
# 创建新项目
mkdir demo
# cd项目
cd demo
# 初始化
npm init -y
# 安装开发版本cli
npm install webpack webpack-cli --save-dev

新建一个 html 文件和一个 js 文件,工程目录如下

  demo
  |- package.json
+ |- index.html
+ |- /src
+   |- index.js

src/index.js:

function component() {
  var element = document.createElement('div');

  // Lodash(目前通过一个 script 脚本引入)对于执行这一行是必需的
  element.innerHTML = _.join(['Hello', 'webpack'], ' ');

  return element;
}

document.body.appendChild(component());

index.html:

<!doctype html>
<html>
  <head>
    <title>起步</title>
    <script src="https://unpkg.com/[email protected]"></script>
  </head>
  <body>
    <script src="./src/index.js"></script>
  </body>
</html>

在 package.json 中:

删除"main": "index.js",添加"private": true

调整工程目录:

  demo
  |- package.json
+ |- /dist
+   |- index.html
- |- index.html
  |- /src
    |- index.js

要在index.js中打包lodash依赖,我们需要在本地安装 library,终端输入指令:

npm install --save lodash

然后调整index.js内容

// 添加一行代码
import _ from 'lodash';

dist/index.html中可以把引入的外部文件lodash删去

删去<script src="./src/index.js"></script>

添加<script src="main.js"></script>

执行终端:

npx webpack

即可在dist中打包生成需要的main.js文件

还可以手动配置文件,在根目录下新建一个webpack.config.js文件

const path = require('path');

module.exports = {
  // 这里放着需要打包的文件,如果多个文件,用数组形式写
  entry: './src/index.js',
  output: {
    // 这里是打包生成的文件名,还可以手动修改
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  }
};

考虑到用 CLI 这种方式来运行本地的 webpack 不是特别方便,我们可以设置一个快捷方式,

package.json中的"scripts"新增

"build": "webpack"

这样就可以使用npm run build指令代替之前的npx

把之前dist文件夹里面的js文件删去,然后重新打包

npm run build

看一下工程目录

demo
|- package.json
|- webpack.config.js
|- /dist
  |- bundle.js
  |- index.html
|- /src
  |- index.js
|- /node_modules

没问题,成功打包,浏览器也正常显示

在 js 文件中import了一个 css 文件,需要在配置中安装并添加style-loaadercss-loader

npm install --save-dev style-loader css-loader

webpack.config.js:

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },

  // 添加的内容
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      }
    ]
  }
};

然后在 js 模块中:

import './style.css'

npm run build就可以自动解析打包啦

加载图片

下载安装file-loader

npm install --save-dev file-loader

webpack.config.js:

module: {
  rules: [
    {
      test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
      ]
      },{
        test: /\.(png|svg|jpg|gif)$/,
        use: [
          'file-loader'
      ]
    }
  ]
}

加载字体

webpack 加载字体资源

记载数据资源

此外,可加载的有用资源还有数据,json文件CSVTSVXML等,实际上,json是内置的,可以直接import data from './data.json'引入

但是CSVTSVXML不可以,需要配置一下依赖

终端下载安装:

npm install --save-dev csv-loader xml-loader

webpack.config.js:

{
  test: /\.(csv|tsv)$/,
  use: [
    'csv-loader'
  ]
},
{
  test: /\.xml$/,
  use: [
    'xml-loader'
  ]
}

src目录下添加一个 json 文件data.json

然后在index.js中引入:

import data from './data.json'

vue 学习笔记 (3)--computed, watch,calss, style

2020-11-25 08:00:00

一、计算属性 computed

1.例子

<div id="app">
  <div>{{message}}</div>
  <div>{{revermessage}}</div>
</div>

<script>
  var vm = new Vue({
    el: '#app',
    data: {
      message: 'hello,wolrd'
    },
    computed: {
      revermessage: function() {
        return this.message.split('').reverse().join('')
      }
    }
  })
</script>

声明一个计算属性severmessage,在computed中被指向对message进行一些方法操作后的返回值

此时vm.severmessage依赖于vm.message的变化,对message改变数据,会引起severmessage的更新

2.计算属性缓存和方法

还可以通过在表达式中调用方法来达到想要的效果

<div id="app">
  <div>{{message}}</div>
  <div>{{revermessage()}}</div>
</div>

<script>
  var vm = new Vue({
    el: '#app',
    data: {
      message: 'hello,wolrd'
    },
    methods: {
      revermessage: function() {
        return this.message.split('').reverse().join('')
      }
    }
  })
</script>

经过测试,两者的结果是一样的

我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的

区别是:计算属性是基于他们的响应式依赖进行缓存的,只有当响应式依赖,也就是message发生改变时才会重新求值,就意味着,只要message还没改变,多次访问revermessage计算属性只会返回之前计算结果的缓存,而不是直接执行计算属性的函数

而方法,在每次触发重新渲染时,调用方法都会执行一次函数,这就多了一份开销

我们为什么需要缓存?假设我们有一个性能开销比较大的计算属性 A,它需要遍历一个巨大的数组并做大量的计算。然后我们可能有其他的计算属性依赖于 A。如果没有缓存,我们将不可避免的多次执行 A 的 getter!如果你不希望有缓存,请用方法来替代

3.计算属性和侦听属性

vue 还有一种方式来观测 vue 实例上的数据变动:侦听属性--watch

<div id="app">
  {{fullname}}
</div>

<script>
  var vm = new Vue({
    el: '#app',
    data: {
      firstname: 'a',
      lastname: 'b',
      fullname: 'ab'
    },
    watch: {
      firstname: function(val) {
        this.fullName = val + ' ' + this.lastName
      },
      lastname: function(val) {
        this.fullName = this.firstName + ' ' + val
      }
    }
  })
</script>

使用过程是极其繁琐且重复的,再来用计算属性试一下

<div id="app">
  {{fullname}}
</div>

<script>
  var vm = new Vue({
    el: '#app',
    data: {
      firstname: 'a',
      lastname: 'b'
    },
    computed: {
      fullname: function() {
        return this.firstname + this.lastname
      }
    }
  })
</script>

很简单快速的就得到了我们想要的函数表达式

4.计算属性的 setter

computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}

现在在控制台输入vm.fullName = 'John Doe',页面会响应,并且setter会被调用,vm.firstnamevm.lastname也会相应地被更新

二、绑定 class 和 style

1.对象语法

可以给v-bind:class传入一个对象,动态的切换class

<div v-bind:class="{active: ok}"></div>

可以通过控制ok的布尔值来动态的控制 class

还可以创建多个字段,并且不影响普通的className的存在

html:

<div
  class="static"
  v-bind:class="{ active: isActive, 'text-danger': hasError }"
></div>

data:

data{
  isActive: true,
  hasError: false
}

结果为:

<div class="static active"></div>

isActive或者hasError变化时,class 列表将相应地更新。例如,如果hasError的值为true,class 列表将变为"static active text-danger"

绑定的数据对象也不必直接写在内联模板里

<div id="app">
  <div v-bind:class="classobject"></div>
</div>

<script>
  var vm = new Vue({
    el: '#app',
    data: {
      classobject: {
        active: true,
        'text-danger': true
      }
    },
  })
</script>

<!-- <div class="active text-danger"></div> -->

如果className有特殊符号,必须加上引号'',或者""

2.数组语法

我们可以把一个数组传给v-bind:class,以应用一个 class 列表

<div v-bind:class="[activeClass, errorClass]"></div>
data: {
  activeClass: 'active',
  errorClass: 'text-danger'
}

渲染为:

<div class="active text-danger"></div>

3.用在组件上

当在一个自定义组件上使用classproperty 时,这些 class 将被添加到该组件的根元素上面。这个元素上已经存在的 class 不会被覆盖。

Vue.component('my-component', {
  template: '<p class="foo bar">Hi</p>'
})

然后在使用的使用的时候添加了一些class

<my-component class="baz boo"></my-component>

html 将会被渲染成:

<p class="foo bar baz boo">Hi</p>

对于带数据绑定的class也同样适用

<my-component v-bind:class="{ active: isActive }"></my-component>

isActive为 truthy 时,HTML 将被渲染成为:

<p class="foo bar active">Hi</p>

4.绑定内联样式 style

v-bind:style的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。CSS property 名可以用驼峰式带引号的短横线来命名

<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>

js:

data: {
  activeColor: 'red',
  fontSize: 30
}

或者直接绑定到一个对象,会让模板更加清晰

<div v-bind:style="styleObject"></div>

js:

data: {
  styleObject: {
    color: 'red',
    fontSize: '13px'
  }
}

也可以将多个对象应用到一个元素上

<div v-bind:style="[baseStyles, overridingStyles]"></div>

自动添加前缀

v-bind:style需要使用浏览器前缀的时候,如:-webkit-等,vue 会自动侦测并帮生成相应的前缀

vue 学习笔记 (2)--vue 实例和模板语法

2020-11-24 08:00:00

一、vue 实例

1.创建 vue 实例

一个 vue 实例应该通过new Vue来创建根实例,所有的 vue 组件其实都是 vue 实例

var vm = new Vue({
  ...
})

当一个实例被创建时,data对象中的 property 都被加入到 vue 的响应式系统中,当值发生改变时,试图也会改变

var data = {a: 1}

var vm = new Vue({
  data: data
})

此时在控制台输入vm.a == data.a会返回 true,变量data已经被赋给vue实例中的data对象

同时,对两个对象的数据进行操作改变也会影响到另一个

vm.a = 2
// data.a = 2

data.a = 3
// vm.a = 3

当数据改变时,视图也会重新渲染,如果在 vue 实例被创建后,又添加了一个新的 property,则不会被加入到响应式系统中

如果一开始就知道后面会添加一个 property,但是一开始不存在或为空,需要设置一些初始值

data: {
  newTodoText: '',
  visitCount: 0,
  hideCompletedTods: fasle,
  todos: [],
  error: null
}

使用 Object.freeze() 方法的时候,无法修改数据引起视图变化

Object.freeze() 方法可以冻结一个对象。一个被冻结的对象再也不能被修改;冻结了一个对象则不能向这个对象添加新的属性,不能删除已有属性,不能修改该对象已有属性的可枚举性、可配置性、可写性,以及不能修改已有属性的值。此外,冻结一个对象后该对象的原型也不能被修改。freeze() 返回和传入的参数相同的对象。

<div id="app">{{message}}</div>

<script>
  var data = {message: 'hello,wolrd'}
  Object.freeze(data)

  var vm = new Vue({
    el: '#app',
    data: data
  })
</script>

在控制台改变vm.message的值会直接报错,因为 data 对象已经被冻结了,不可改变

vue-learn-2.jpg

vm.$data

代表 vue 实例观察的数据对象

console.log(vm.$data)
// 返回数据对象的数组形式
var data = { a: 1 }
var vm = new Vue({
  el: '#app',
  data: data
})

vm.$data === data // true
vm.$el === document.getElementById('app') // true

2.生命周期函数

每个实例在被创建的时候,都要经历一些初始化过程,比如:设置数据监听,编译模板,将实例挂载到 dom,更新 dom 等

在这个过程中会运行一些叫做生命周期的钩子函数,可以在不同阶段添加自己需要的代码

vue-learn-3.png

比如created钩子可以用来表示一个实例被创建之后执行的代码

new Vue({
  data: {
    a: 1
  },
  created: function () {
    console.log('a is: ' + this.a)
  }
})
// "a is: 1"

this用来指向 vm 实例

但是不要在 property 或者回调上使用箭头函数,created: () => console.log(this.a),因为箭头函数没有this的概念,他会把this当作变量一直向上级作用域查找,经常产生Uncaught TypeError: Cannot read property of undefinedUncaught TypeError: this.myMethod is not a function之类的报错

二、模板语法

1.插值

最简单的就是

<div id="app">{{message}}</div>

<script>
    var app = new Vue({
        el: "#app",
        data: {
            message: 'hello,world'
        }
    })
</script>
</script>

。。。

下面这段代码给input绑定了一个disabled属性,但是通过ok的真伪来控制属性是否渲染

okfasle,null,undefined值时,属性都不会被渲染,有点类似于v-if,但是这种写法只是针对属性

<div id="app">
    <input type="text" v-bind:disabled="ok">
</div>

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            message: 'hello,wolrd',
            ok: false
        }
    })
</script>

在模板语法中还可以使用 javascript 表达式

<div id="app">
    <div>{{number + 1}}</div>
    <div v-bind:id="'list-' + id"></div>
    <div>{{message.split('').reverse().join('')}}</div>
</div>

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            number: 2,
            id: 'li',
            message: 'hello,wolrd'
        }
    })
</script>

每个模板只能包含一个 javascript 表达式,如果有多个,则不会生效

<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}

<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}

2.指令

参数

一些指令能够接收一个“参数”,在指令名称之后以冒号表示。例如,v-bind指令可以用于响应式地更新 HTML attribute:

<div id="app">
    <a v-bind:href="url">百度</a>
</div>

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            url: 'https://www.baidu.com'
        }
    })
</script>

在这里href是参数,告知v-bind指令将该元素的hrefattribute 与表达式url的值绑定

另一个例子是v-on指令,它用于监听 dom 事件

<a v-on:click="doSomething">...</a>

动态参数

从 2.6.0 开始,可以用方括号括起来的 JavaScript 表达式作为一个指令的参数

<!--注意,参数表达式的写法存在一些约束,如之后的“对动态参数表达式的约束”章节所述。-->
<a v-bind:[attributeName]="url">...</a>

这里的attributeName会被作为一个 javascript 表达式进行动态求值,最终结果被作为参数来使用

比如:vue 实例中有一个attributeName属性其值为href,则这个绑定将等价于v-bind:href

<a v-bind:href="url>...</a>

也可以使用动态的事件名绑定监听函数

<a v-on:[eventName]="doSomething"> ... </a>

如果eventName值为click,则该绑定等价于v-on:click="doSomething",一个鼠标点击事件

对动态参数的值的约束

动态参数预期会求出一个字符串,异常情况下值为null,这个特殊的null值可以被显性地用于移除绑定。任何其它非字符串类型的值都将会触发一个警告。

对动态参数表达式的约束

动态参数表达式有一些语法约束,因为某些字符,如空格和引号,放在 HTML attribute 名里是无效的,例如:

<!-- 这会触发一个编译警告 -->
<a v-bind:['foo' + bar]="value"> ... </a>

因此,尽量不要使用空格和引号的表达式,或者采用计算属性来替代这种复杂表达式

在 DOM 中使用模板时 (直接在一个 HTML 文件里撰写模板),还需要避免使用大写字符来命名键名,因为浏览器会把 attribute 名全部强制转为小写

修饰符

修饰符是以.指明的特殊后缀,用于指出一个指令应该以特殊方式绑定,例如,.prevent修饰符告诉v-on指令对于触发的事件调用event.preentDefault()

<form v-on:submit.prevent="onSubmit">...</form>

3.缩写

v-前缀作为一种视觉提示,用来识别模板中 Vue 特定的 attribute。当你在使用 Vue.js 为现有标签添加动态行为 (dynamic behavior) 时,v-前缀很有帮助,然而,对于一些频繁用到的指令来说,就会感到使用繁琐。同时,在构建由 Vue 管理所有模板的单页面应用程序 (SPA - single page application) 时,v-前缀也变得没那么重要了。因此,Vue 为v-bindv-on这两个最常用的指令,提供了特定简写

v-bind

<!-- 完整语法 -->
<a v-bind:href="url">...</a>

<!-- 缩写 -->
<a :href="url">...</a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a :[key]="url"> ... </a>

v-on

<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>

<!-- 动态参数的缩写 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>

它们看起来可能与普通的 HTML 略有不同,但:@对于 attribute 名来说都是合法字符,在所有支持 Vue 的浏览器都能被正确地解析。而且,它们不会出现在最终渲染的标记中。缩写语法是完全可选的,但随着你更深入地了解它们的作用,你会庆幸拥有它们。

vue 学习笔记 (1)--什么是 vue?

2020-11-23 08:00:00

一、什么是 vue?

练习时使用,最新版本

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

生产时使用,明确版本号的版本,避免造成不可预见的问题

<script src="https://cdn.jsdelivr.net/npm/[email protected]"></script>

hello world

<div id="app">
    {{message}}
</div>
<script>
    var app = new Vue({
        el: "#app",
        data: {
            message: 'hello,world'
        }
    })
</script>

1.v-指令

  1. v-bind--绑定属性
<div id="app-2">
  <span v-bind:title="message">
    鼠标悬停几秒钟查看此处动态绑定的提示信息!
  </span>
</div>

<script>
  var app = new Vue({
  el: '#app',
  data: {
    message: '页面加载于 ' + new Date().toLocaleString()
  }
})
</script>

v-bind是 vue 特有的提供的属性,他会在 dom 上应用一个响应式的操作

打开 console,输入app.message = "hello",页面会改变内容只显示hello

又或者输入app.message = false,内容会直接不显示

vue 官网例子:

<!-- 绑定一个 attribute -->
<img v-bind:src="imageSrc">

<!-- 动态 attribute 名 (2.6.0+) -->
<button v-bind:[key]="value"></button>

<!-- 缩写 -->
<img :src="imageSrc">

<!-- 动态 attribute 名缩写 (2.6.0+) -->
<button :[key]="value"></button>

<!-- 内联字符串拼接 -->
<img :src="'/path/to/images/' + fileName">

<!-- class 绑定 -->
<div :class="{ red: isRed }"></div>
<div :class="[classA, classB]"></div>
<div :class="[classA, { classB: isB, classC: isC }]">

<!-- style 绑定 -->
<div :style="{ fontSize: size + 'px' }"></div>
<div :style="[styleObjectA, styleObjectB]"></div>

<!-- 绑定一个全是 attribute 的对象 -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>

<!-- 通过 prop 修饰符绑定 DOM attribute -->
<div v-bind:text-content.prop="text"></div>

<!-- prop 绑定。“prop”必须在 my-component 中声明。-->
<my-component :prop="someThing"></my-component>

<!-- 通过 $props 将父组件的 props 一起传给子组件 -->
<child-component v-bind="$props"></child-component>

<!-- XLink -->
<svg><a :xlink:special="foo"></a></svg>

  1. v-if--控制元素的显示隐藏

刚才写了,通过app.message = false,可以让message代表的内容处于隐藏状态,但是控制的只能是通过v-for引入的内容,无法直接让元素隐藏

其实,vue 中还有一个专门控制元素显示隐藏的指令--v-if

<div id="app">
  <span v-if="show">
    {{message}}
  </span>
</div>

<script>
  var app = new Vue({
    el: "#app",
    data: {
      message: 'hello,world',
      show: true
    }
  })
</script>

给元素绑定一个指令v-if = "show",然后在 data 中控制show的布尔值,true 显示,false 隐藏

同时还有这个功能的指令v-show,根据表达式之真假值,切换元素的display CSS property,也是通过绑定的布尔值来显示隐藏

但是v-ifv-show有些区别

(1) v-if 是控制元素是否渲染最终控制元素的显示隐藏,适用于切换频率低的情况 (2) v-show 不管显示隐藏都渲染,然后通过布尔值绑定display: none属性,适用于切换频率高的情况

注意:v-show不支持<tempalte>元素,也不支持v-else

搭配用法

v-elsev-else-if

可以通过使用v-else达到v-if的效果,必须和v-if搭配使用,比如:

<div v-if="1>2">
  hello
</div>
<div v-else>
  hi
</div>

如果v-if不成立,执行v-else,也就是显示 v-else 里面的内容

是不是有点 javascript 里面if函数的感觉了

v-else-if用法大致一样,必须和v-ifv-else搭配使用


  1. v-for--遍历数组并显示到页面上

v-for算是 vue 的核心指令之一了把,主要是渲染一个项目列表的

<div id="app">
  <div v-for="item in list">{{item.message}}</div>
</div>

<script>
  var app = new Vue({
    el: "#app",
    data: {
      list: [
        {message: 'html'},
        {message: 'css'}
      ]
    }
  })
</script>

可以直接把列表数据循环输出

并且在控制台中通过push()还能添加新的列表项

app.list.push({message: "js"})

vue 对数据操作,就是这么神奇

不推荐v-forv-if同时使用,因为v-for拥有更高的优先级


  1. v-on--绑定事件监听器

即绑定事件,通过v-on给 div 绑定了一个点击事件,注意,在reverseMessage方法中,我们更新了应用的状态,但是吗诶呀触碰到 dom,直接通过 vue 来进行处理,编写代码时只需要关注逻辑层即可

v-on:click="messagenone",然后在methods里面写上事件方法

<div id="app">
  <div v-on:click="messagenone" id="demo">{{message}}</div>
</div>

<script>
  var app = new Vue({
    el: "#app",
    data: {
      message: 'hello,wolrd'
    },
    methods: {
      messagenone: function() {
        var demo = document.getElementById('demo');
        demo.innerHTML = ''
      }
    }
  })
</script>

  1. v-model--数据的双向绑定

vue 还提供了v-model指令,它能轻松实现表单输入和应用状态之间的双向绑定

可使用场景:标签 input, select, textarea 和 components

<div id="app">
  <div>{{message}}</div>
  <input type="text" v-model="message">
</div>

<script>
  var app = new Vue({
    el: "#app",
    data: {
      message: 'hello,wolrd'
    }
  })
</script>

  1. v-text 更新元素的内容
<div id="app">
    <div v-text="message"></div>
    <div>{{message}}</div>
</div>

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            message: 'hello,wolrd'
        }
    })
</script>

上面的 div 和下面的 div 内容跟随一致变化


  1. v-html--更新元素的 innerHTML

不建议使用,在网站上动态渲染 html 是非常危险的,容易导致XXS 攻击,不能用在用户提交的内容上,如果必须使用 v-html,可以考虑通过使用组件来代替


  1. v-pre

跳过绑定的元素和他的子元素的编译,直接显示原始内容

跳过没有指令的节点不进行编译,直接显示内容,会加快页面的响应


  1. v-cloak

在编译没有编译完成之前,模板处于的状态

<style>v-cloak] {
  display: none;
}</style>

<div v-cloak>
  {{message}}
</div>

<script>
  var vm = new Vue({
    el: '#app',
    data: {
    	message: 'hello,wolrd'
    }
  })
</script>

上述代码表示的含义是,如果网络不好,{{message}}模板代表的hello,world还没有编译成功,此时{{message}}处于隐藏状态,当编译完成,页面直接显示hello,world


  1. v-once--一次性渲染

绑定该指令的元素及其子元素只会进行一次渲染,之后页面的第二次渲染会将其视为静态资源并跳过,可以用来优化性能

<div id="app">
    <span v-once>{{message}}</span>
</div>

<script>
    var vm = new Vue({
        el: '#app',
        data: {
            message: 'hello,wolrd',
        }
    })
</script>

使用场景:表单提交。可防止用户在请求未及时响应时,多次提交~

2.组件化构建应用

组件化也是 vue 的核心机制之一,它允许我们使用小型,可复用的组件来构建大型应用

vue-learn-3.png

在 vue 里,一个组件本质上是一个拥有预定义选项的一个 vue 实例。在 vue 中注册组件很简单

// 定义名为 todo-item 的新组件
Vue.component('todo-item', {
  template: '<li>这是个待办项</li>'
})

var app = new Vue(...)

现在可以用它构建另一个组件模板

<ol>
  <!-- 创建一个 todo-item 组件的实例 -->
  <todo-item></todo-item>
</ol>

但是这样会为每个待办项渲染同样的文本,这看起来并不炫酷。我们应该能从父作用域将数据传到子组件才对。让我们来修改一下组件的定义,使之能够接受一个props

Vue.component('todo-item', {
  // todo-item 组件现在接受一个
  // "props",类似于一个自定义 attribute。
  // 这个 props 名为 todo。
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

现在,我们可以使用v-bind指令将待办项传到循环输出的每个组件中

现在我们为每个 todo-item 提供 todo 对象 todo 对象是变量,即其内容可以是动态的,我们也需要为每个组件提供一个key

<div id="app-7">
  <ol>
    <todo-item
      v-for="item in groceryList"
      v-bind:todo="item"
      v-bind:key="item.id"
    ></todo-item>
  </ol>
</div>
Vue.component('todo-item', {
  props: ['todo'],
  template: '<li>{{ todo.text }}</li>'
})

var app7 = new Vue({
  el: '#app-7',
  data: {
    groceryList: [
      { id: 0, text: '蔬菜' },
      { id: 1, text: '奶酪' },
      { id: 2, text: '随便其它什么人吃的东西' }
    ]
  }
})

尽管这只是一个刻意设计的例子,但是我们已经设法将应用分割成了两个更小的单元。子单元通过prop接口与父单元进行了良好的解耦。我们现在可以进一步改进 组件,提供更为复杂的模板和逻辑,而不会影响到父单元

在一个大型应用中,有必要将整个应用程序划分为组件,以使开发更易管理

<div id="app">
  <app-nav></app-nav>
  <app-view>
    <app-sidebar></app-sidebar>
    <app-content></app-content>
  </app-view>
</div>

对象,类和接口之间的关系

2020-11-21 08:00:00

类是对的类型,类是具有相同属性和方法的一组集合,一个类可以对应多个对象,对象通过他们公开的方法来定义他们与外界的交互行为,而方法就形成了与外界交互的接口

类,对象和接口

1.类和对象

区别:

  1. 对象是类的一个实例。就比如一个人,他有具体的属性,身高,体重,姓名等状态,跑步,跳舞等行为
  2. 类是抽象的概念,代表一类事物的模版。对一类对象的行为和状态进行描述,抽离出其共性,形成类

关系:

  1. 类就是对事物的一种描述,对象是具体存在的一个实例

类是对的类型,类是具有相同属性和方法的一组集合,一个类可以对应多个对象

2.什么是接口?

对象通过他们公开的方法来定义他们与外界的交互行为,而方法就形成了与外界交互的接口

更深层次的含义是:使定义和实现分离开,他是交互的具体实现的抽象化

接口是面向对象编程的基础,它是一组包含了函数方法的数据结构,他是一个比类更抽象化的东西

可以这么说,类是对象的抽象化,接口是类的抽象化

比如生活中的接口,机顶盒,人们利用它来实现收看不同频道和信号的节目,它犹如对不同类型的信息进行集合和封装的设备,最后把各种不同类型的信息转换为电视能够识别的信息。

在编程语言中的接口,实际上是不同类的封装并提供统一的外部联系通道,这样其他对象就可以利用接口来调用不同类的成员了

接口和类,实际上都是相同的数据结构

在接口中,可以声明属性,方法,事件,类型,但是不能声明变量,也就是说,接口只能定义成员,不能给成员赋值

使用接口的好处,提高代码灵活性,降低对象之间的耦合度,在实际项目中非常有用

vue 使用 cli 脚手架构建项目工程

2020-11-20 08:00:00

vue 使用 cli 脚手架构建项目工程,执行的命令以及遇到的相关的问题

1.下载安装 node

测试一下是否成功安装,

$ node -v
// 返回下载的版本号

2.安装 webpack 环境

$ npm install webpack -g
// 如果失败,可能是因为用户没有权限
// 使用下面这种,管理员权限
$ sudo npm install webpack -g

如果返回版本号代表成功,如果没有,则需要输入下面的命令

$ npm install webpack webpack-cli -g

webpack 4.X 开始,需要安装 webpack-cli 依赖

3.全局安装 vue-cli

$ npm install --global vue-cli
// 如果失败,使用sudo
$ sudo npm install --global vue-cli

安装完成之后,输入

$ vue -V

如果返会版本号,说明安装成功

4.构建项目

前面那些命令执行完之后,就可以构建 ci 项目了,找到我们想要放置项目的文件夹,进入终端,cd 到这个文件夹

输入命令:

$ vue init webpack vuedemo
// 名字自己根据要求起,vuedemo

然后就是缓慢的构建过程,等到构建完成,cd 进入构建的项目

$ cd vuedemo

然后安装需要的依赖

$ npm install

5.运行项目

运行命令,看看是否能够成功运行项目

$ npm run dev

根据提示,浏览器输入http://localhost:8080

如果端口打不开,肯能是因为被占用了,需要修改配置文件,config > index.js

查看项目工程目录

6.其他

一些其他相关的指令

$ npm run build
// 项目完成之后打包

打包完成之后,会在根目录下生成一个 dist 文件夹,需要修改配置文件的路径,可以在本地查看

项目上线发布,直接上传 dist 到服务器即可

react 学习路径--怎么学习 react?

2020-11-19 08:00:00

转载于:https://github.com/petehunt/react-howto,react 开源作者对于 react 框架的一个学习路径的建议,个人感觉还是相当不错的学习路线


如何学习 React?

如果你是一个 React (或者前端) 新手,出于以下的原因,你可能会对这个生态圈感到困惑:

在本文中,我会假设你已有使用 HTML, CSS 和 JavaScript 开发网页的基础。

为什么要听我的?

关于 React, 现在已经有大量的相互冲突的建议了,为什么要听我的?

因为我是在 Facebook 构建并开源 React 的最初成员之一。现在我离开了 Facebook 并加入了一家初创公司,所以我也不会站在 Facebook 的立场上来表态。

如何踏入 React 生态圈

所有的软件都是建立在某个技术栈之上的,你需要对整个技术栈有足够深入的理解,才能建造你的应用。为什么 React 生态圈的工具似乎总让人感觉压力山大呢,因为它总是以错误的顺序被解释:

你应该按照以下的顺序进行学习,而不是跳着学或者同时学习:

  1. React
  2. npm
  3. JavaScript“打包工具”
  4. ES6
  5. Routing
  6. Flux

你不需要把这些都学完才去使用 React. 只需要在你遇到问题需要解决的时候,才进入下一步的学习。

另外,在 React 社区中,有一些前沿主题是经常被提及到的,以下的这些主题很有意思,但也很难弄懂,所以它们远没有上面的主题流行,大多数应用也不需要用到这些。

学习 React 本身

有一种常见的误解是:你需要花费大量时间在配置工具上,然后才开始学习 React. 在官方文档里,你可以找到 copy-paste HTML template. 只需要保存为 .html 文件,你就可以马上开始学习了。这个步骤不需要任何工具,你也无需额外学习工具使用,直到你能熟练掌握 React 基础。

我依然觉得,学习 React 最简单的方法是通过官方教程 the official tutorial.

学习 npm

npm 是 Node.js 包管理工具,也是前端工程师和设计师分享 JavaScript 代码最流行的方式。它包含了名为 CommonJS 的模块系统,让你可以安装 JavaScript 写的命令行工具。作为背景知识,可以阅读 这篇文章 了解 CommonJS 对于浏览器的重要性,阅读 CommonJS Spec Wiki 了解关于 CommonJS API 的更多内容

在 React 生态圈中,大部分可重用的组件、库和工具遵循 CommonJS 模块规范,可通过 npm 来安装。

学习 JavaScript 打包工具

出于若干技术原因,CommonJS 模块 (也就是 npm 里的所有内容) 不能直接用到浏览器。你需要一个 JavaScript“打包工具 (bundler)”来把这些模块打包成 .js 文件,使你可以在网页中通过 <script> 标签引入它们。

JavaScript 打包工具包括 webpackbrowserify. 它们都是好的选择,但我个人更喜欢 webpack , 因为它有许多功能简化大型应用开发。鉴于 webpack 文档可能令人感到困惑,我也写了两篇文章:plug-and-play template for getting started 和针对更复杂用例的 how-to guide for webpack.

要记住的一点:CommonJS 使用了 require() 函数来引入模块,因此许多人对此感到疑惑,并认为需要导入 require.js 到工程里。出于若干技术原因,我建议你避免使用 require.js. 它在 React 生态圈并不流行。

学习 ES6

在 JSX (你会在 React tutorial 中学习到) 以外,你可能会注意到 React 例子中一些有趣的语法。这被称为 ECMAScript6, 是 JavaScript 的最新版本。由于 ES6 很新,你可能还没学习到,浏览器也可能尚未兼容,但别担心,通过合适的配置,你的打包工具会为你自动转换成兼容代码。

如果你只想要使用 React 来把事情做完,你可以跳过 ES6 的学习, 或者留到以后再学习。

你可能会看到一些讨论说更适合用 ES6 的 class 来创建 React 组件。这并不是真的,大多数人 (包括 Facebook) 用的还是 React.createClass().

学习路由 (routing)

“单页面应用”是时下的技术热点。当网页加载完成,用户点击链接或者按钮的时候,JavaScript 会更新页面和改变地址栏,但网页不会刷新。地址栏的管理就是通过 路由 (router) 来完成的。

目前 React 生态圈最受欢迎的路由解决方案是 react-router. 如果你正在创建一个单页面应用,有什么理由不去使用它呢?

如果你创建的并非单页面应用,请不要使用路由。 无论如何,大部分项目都是从大型应用中的小组件开始的。

学习内联样式

在 React 出现之前,很多人通过像 SASS 这样的预处理器来重用复杂的 CSS 样式表。鉴于 React 使开发可重用组件变得容易,你的样式表可以变得没那么复杂了。社区中许多人 (包括我) 正尝试完全抛弃样式表。

由于一些原因,其实这是个相当疯狂的主意。这让媒体查询 (media quries) 更加困难了,而且这种技术可能有性能上的局限性。当你开始用 React 的时候,只要用你平常使用的方法去写就好了。

一旦你找到了用 React 开发的感觉,你就可以关注那些可作为替代的技术了。其中一种流行技术是 BEM. 我建议你逐渐停用 CSS 预处理器,因为 React 给了你一种更强大的方式去重用样式 (通过重用组件), 并且 JavaScript 打包工具可以为你生成更高效的样式表 (我曾经在 OSCON 上发表过关于这个的演讲). 说了这么多,总之 React 就像其他 JavaScript 库一样,可以和 CSS 预处理器很好地配合工作。

另一种可选项是 CSS 模块, 更具体地说,是 react-css-modules. 虽然有了这些 CSS 模块,你还是写 CSS (或者是 SASS/LESS/Stylus), 但你可以像处理 React 中的内联样式那样管理和组织 CSS 文件。你也不需要担心用到 BEM 那样的方法学去管理类名,因为模块系统在底层已经帮你处理好了。

学习服务器端渲染

服务器端渲染经常被称为 “通用应用” 或 “同构应用”. 这意味着你可以用 React 组件在服务器端渲染出静态 HTML. 这样做可以提高初始化加载的性能,因为用户不用等到 JS 下载完才看到初始界面,并且 React 可以重用服务器端渲染出的 HTML, 无需客户端重新生成。

如果你发现首屏渲染速度过慢,或者想提高网站在搜索引擎的排行,你就需要服务器端渲染了。尽管 Google 现在也会索引客户端渲染的内容,但截至 2016 年 1 月,这样做仍被证实会对排行有负面影响,这可能是由于客户端渲染的性能问题所造成的。

服务器端渲染还需要许多工具的辅助,因为显然 React 组件不是在考虑服务器端渲染的情况下写出来的,你应该先构建你的应用,之后再关心服务器端渲染的问题。不用担心,你不需要重写所有组件去支持它。

学习 Flux

你可能听过 Flux, 不过关于 Flux 有大量的错误资讯。

许多人一坐下来刚开始构建应用,就认为需要用 Flux 来定义他们的数据模型。这样采用 Flux 是不对的,Flux 应该在大量组件被建立完成以后才被引入。

React 组件之间存在层级关系。在很多时候,你的数据模型也跟随这种层级。这种情况下,Flux 不会给你很大帮助。但有些时候,你的数据模型没有层次,当你的 React 组件开始接受没有关联的 props 的时候,或者当小部分组件开始变得复杂的时候,你才可能需要看看 Flux.

你会知道什么时候需要用 Flux. 如果你不确定是否需要用它,你就不需要它。

如果你决定使用 Flux, 现在最流行的、文档最全的 Flux 库是 Redux. 当然也有许多其他选择,你或者会有兴趣尝试使用它们,但我的建议是只需要用最流行的 Redux 就足够了。

学习 Immutable.js

Immutable.js 提供了一系列的数据结构,以帮助解决构造 React 应用时的某些性能问题。这是一个很棒的库,你可能会在应用发展的过程里大量用到它,但直到你在意识到性能问题以前,它是完全不必要的。

学习 Relay, Falcor 等

这些技术可以帮你减少 AJAX 请求数,它们仍然是非常前沿的,所以如果你没有遇到过多 AJAX 请求的问题,就不需要用到 Relay 或者 Falcor.

在 js 中运算不能随便把 value 属性定义成变量

2020-11-18 08:00:00

在写一个小 demo 的时候,无意中发现定义变量的时候直接写入 value,会导致获取不到数据,或者获取的不是我们想要的内容,在 js 中运算不能随便把 value 属性定义成变量

先看一段代码

<input type="text"" id="a">
<span>*</span>
<input type="text" id="b">
<input type="button" value="=" onclick="beto()">
<input type="text" id="sub" disabled>

<script>
    function beto() {
        var a = document.getElementById('a').value
        var b = document.getElementById('b').value
        var sub = document.getElementById('sub').value
        
        sub = a + b
    }
</script>

就这么一看,逻辑貌似没有问题,获取 a 和 b 的 value,乘法运算,然后输出 sub

但是放在浏览器运行的时候,发现完全没有反应

为什么呢?

(假装思考五分钟…)

因为 sub.value 不能直接定义在变量中

// 先定义 id 为 sub 的 input 框
var sub = document.getElementById('sub')

// 然后在运算的时候在直接使用 sub.value
sub.value = a * b

顺手放一个写 demo 时候用的简易计算器

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>简易计算器</title>
</head>
<body>
    <input type="text"" id="a">
    <select id="c">
        <option value="+">+</option>
        <option value="-">-</option>
        <option value="*">*</option>
        <option value="/">/</option>
    </select>
    <input type="text" id="b">
    <input type="button" value="=" onclick="beto()">
    <input type="text" id="sub" disabled>
   
    <script>
        function beto() {
            var a = document.getElementById('a').value
            var b = document.getElementById('b').value
            var c = document.getElementById('c').value
            var sub = document.getElementById('sub')
            
            switch(c) {
                case  "+":
                sub.value = parseInt(a) + parseInt(b);
                break;
                case  "-":
                sub.value = parseInt(a) - parseInt(b);
                break;
                case  "*":
                sub.value = parseInt(a) * parseInt(b);
                break;
                case  "/":
                sub.value = parseInt(a) / parseInt(b);
                break;
            }
        }
    </script>
</body>
</html>

事件监听函数,以及事件的捕获和冒泡机制

2020-11-17 08:00:00

事件一般是用于浏览器和用户操作之间的交互,当用户执行某些特殊的操作时,浏览器给予反应,触发绑定的事件,事件流,事件发生时会在元素节点和根节点之间按照约定的顺序传播,事件经过的所有节点都会受到事件的影响,这个传播过程被称为 DOM 事件流

函数事件

事件一般是用于浏览器和用户操作之间的交互,当用户执行某些特殊的操作时,浏览器给予反应,触发绑定的事件

事件流,事件发生时会在元素节点和根节点之间按照约定的顺序传播,事件经过的所有节点都会受到事件的影响,这个传播过程被称为 DOM 事件流

true 是捕获,false 是冒泡,默认为冒泡事件

1.addEventListener()--添加事件监听函数

给元素添加一个事件,假如有多个事件,不会覆盖,会依次执行

<div id="demo">dom</div>
<script>
    document.getElementById('demo').addEventListener("click", myfun)
    document.getElementById('demo').addEventListener("click", myfun1)

    function myfun() {
        console.log('事件监听函数')
    }

    function myfun1() {
        console.log('addEventListener')
    }
</script>

注意: 1.这里有一个细节,addEventListener() 里面有两个参数,第一个表示触发的条件,第二个表示触发的事件 正常情况下,第二个参数直接写函数名并且不加参数(),如果加了参数()则表示立即执行,不需要触发第一个参数要求的条件

2.在这里绑定事件的时候,事件名不能和定义的变量名一样,否则无效

2.removeEventListener()--移除事件监听函数

下面这个 demo,当鼠标在 div 中移动的时候,出现随机数,点击按钮后,移除事件监听函数

<!-- css -->
<style>
    #demo {
        width: 100px;
        height: 100px;
        border: 1px solid #000;
    }
</style>

<!-- html -->
<div id="demo"></div>
<input type="button" value="点击移除" onclick="remove()">
<div id="show"></div>

<!-- js -->
<script>
    document.getElementById('demo').addEventListener("mousemove", myfun)
    function myfun() {
        document.getElementById('show').innerHTML = Math.random()
    }

    function remove() {
        document.getElementById('demo').removeEventListener("mousemove", myfun)
    }
</script>

3.利用事件的捕获和冒泡做点事情

addEventListener() 和 removeEventListener() 其实拥有三个参数,刚才说过了,第一个表示触发条件,第二个表示触发事件,第三个参数正常情况下可以省略,但是要知道它代表的意思

用布尔值来表示,true 或者 false,默认是 false

根据图片可以看出,捕获阶段要先于冒泡阶段,因此,true 事件要先于 flase 事件触发,多个 true 事件按顺序触发,多个 false 事件,写在后面的先触发

结论:写在前面的 true 事件 > 写在后面的 true 事件 > 写在后面的 false 事件 > 写在前面的 false 事件

因此,利用这个参数,可以控制同一个元素的不同事件触发的顺序

<div id="out">
    <p>最外面</p>
    <div id="middle">
        <div id="inner">最里面</div>
    </div>
</div>

<!-- 第一种情况 -->
<script>
    var out = document.getElementById('out'); 
    var middle = document.getElementById('middle'); 
    var inner = document.getElementById('inner'); 
    //点击 inner 时,触发顺序为:inner-------middle------out
    out.addEventListener('click',function(){alert("我是最外面的");},false);   
    middle.addEventListener('click',function(){alert("我是中间的");},false);    
    inner.addEventListener('click',function(){alert("我是最里面的");},false); 
</script>

<!-- 第二种情况 -->
<script>
    var out = document.getElementById('out'); 
    var middle = document.getElementById('middle'); 
    var inner = document.getElementById('inner'); 
    //点击 inner 时,触发顺序为:out------middle-------inner
    out.addEventListener('click',function(){alert("我是最外面的");},true);   
    middle.addEventListener('click',function(){alert("我是中间的");},true);  
    inner.addEventListener('click',function(){alert("我是最里面的");},true); 
</script>

<!-- 第三种情况 -->
<script>
    var out = document.getElementById('out'); 
    var middle = document.getElementById('middle'); 
    var inner = document.getElementById('inner'); 
    //点击 inner 时,触发顺序为:out------inner-------middle
    out.addEventListener('click',function(){alert("我是最外面的");},true);   
    middle.addEventListener('click',function(){alert("我是中间的");},false);    
    inner.addEventListener('click',function(){alert("我是最里面的");},false);
</script>

<!-- 第四种情况 -->
<script>
    var out = document.getElementById('out'); 
    var middle = document.getElementById('middle'); 
    var inner = document.getElementById('inner'); 
    //点击 inner 时,触发顺序为:out-------middle------inner
    out.addEventListener('click',function(){alert("我是最外面的");},true);   
    middle.addEventListener('click',function(){alert("我是中间的");},true);  
    inner.addEventListener('click',function(){alert("我是最里面的");},false);
</script>

<!-- 第五种情况 -->
<script>
    var out = document.getElementById('out'); 
    var middle = document.getElementById('middle'); 
    var inner = document.getElementById('inner'); 
    //点击 inner 时,触发顺序为:middle-------inner------out
    out.addEventListener('click',function(){alert("我是最外面的");},false);   
    middle.addEventListener('click',function(){alert("我是中间的");},true);  
    inner.addEventListener('click',function(){alert("我是最里面的");},false);
</script>

<!-- 第六种情况 -->
<script>
    var out = document.getElementById('out'); 
    var middle = document.getElementById('middle'); 
    var inner = document.getElementById('inner'); 
    //点击 inner 时,触发顺序为:out-------inner------middle
    out.addEventListener('click',function(){alert("我是最外面的");},true);   
    middle.addEventListener('click',function(){alert("我是中间的");},false);    
    inner.addEventListener('click',function(){alert("我是最里面的");},true);
</script>

4.事件处理程序

  1. HTML 事件处理程序
<button onclick="test()">测试</button>
<script>
    function test() {
        alert("HTML 事件处理程序");
    }
</script>

1.优点:事件处理程序中的代码,能够访问全局作用域中的任何变量 2.缺点:时差问题、扩展的作用域链在不同浏览器中会导致不同结果、html 代码与 js 代码高度耦合

  1. DOM0 级事件处理程序
<button id="btn">测试</button>
<script>
    var btn = document.getElementById("btn");
    btn.onclick = function test() {
        alert("DOM0 级事件处理程序");
    }
</script>

1.优点:代码简单,浏览器兼容性好,解决了 html 代码和 js 代码的高度耦合问题 2.缺点:一个元素只能绑定一个事件处理函数,只会在事件冒泡中运行

  1. DOM2 级事件处理程序

该级别的事件处理程序,运用的就是事件捕获和冒泡机制

<button id="btn">测试</button>
<script>
    var btn = document.getElementById("btn");

    // 事件监听
    btn.addEventListener("click", function() {
        alert("DOM2 级事件处理程序,我在捕获阶段执行");
    }, true);

    btn.addEventListener("click", function() {
        alert("DOM2 级事件处理程序,我在冒泡阶段执行");
    }, false);

    // 移除事件监听
    var fun = function() {
        alert("我要被移除了");
    }
    btn.addEventListener("click", fun, false);
    btn.removeEventListener("click", fun, false);
</script>

1.优点:同时支持事件处理的捕获和冒泡阶段,并且一个元素可以绑定多个处理函数 2.缺点:IE 不支持

getElementBy 系列和 querySelector 系列的区别

2020-11-16 08:00:00

querySelectorquerySelectorAll的用法和getElementBy大致一样,获取的时候带上符号,getElementBy 获取的是元素的动态集合,querySelector 获取的是元素的静态集合

但是需要注意:getElementBy 系列和 querySelector 系列的区别

比如,我们写一个 for 循环,每次获取 li 标签的时候,ul 生成一个 li 子元素

<ul id="ul">
    <li>a</li>
    <li>b</li>
    <li>c</li>
</ul>

<script>
    var ul = document.getElementById('ul');
    var li = document.getElementsByTagName('li');

    for(var i = 0;i < li.length; i++) {
        ul.appendChild(document.createElement('li'))
    };

    console.log(li.length);
    // 陷入死循环
</script>

i < li.length来进行判断时,会导致浏览器死循环,因为循环一次的时候,浏览器又重新获取 li 标签数组,每调用一次就会重新对文档进行查询,就会进入死循环

进行修改:把i < li.length改成i < 3,把 li 标签数组静态化,然后打印

conosle.log(li.length)  // 6

重新用 querySelector 获取一遍元素

<ul id="ul">
    <li>a</li>
    <li>b</li>
    <li>c</li>
</ul>

<script>
    var ul = document.querySelector('ul');
    var li = document.querySelectorAll('li');

    for(var i = 0;i<li.length;i++) {
        ul.appendChild(document.createElement('li'))
    };

    console.log(li.length);
    // 输出对结果是原来的 li.length = 3,而不是增加后的 6
</script>

静态集合体现在querySelectorAll('li')获取到 ul 里所有 li 后,不管后续再动态添加了多少 li,都是不会对其参数影响

DOM--文档对象模型

2020-11-15 08:00:00

DOM 中文名文档对象模型,英文名 Document Object Model,我们简称为 DOM,是针对 html 和 xml 文档的一种 API,将 html 以一种树状结构呈现出来,可以更直观去研究文档结构,我们将这种树状文档结构称为--DOM 树,或者节点树,一样的概念

一、什么是 DOM?

DOM 中文名文档对象模型,英文名 Document Object Model,我们简称为 DOM,是针对 html 和 xml 文档的一种 API,将 html 以一种树状结构呈现出来,可以更直观去研究文档结构,我们将这种树状文档结构称为--DOM 树,或者节点树,一样的概念

js 通过 dom 节点,可以对文档的 html 标签,属性,css 样式,以及具体的内容做出修改,并对页面中的所有事件进行响应

二、节点树

1.节点类型

  1. 文档节点--Document
  2. 标签节点--Element
  3. 文本节点--Text
  4. 注释节点--Comment
  5. 属性节点--Attr

2. 属性

  1. 节点类型--nodeType
  2. 节点名称--nodeName
  3. 节点值--nodeValue
  4. 子节点--childNodes
  5. 父节点--parentNodes
  6. 上一个节点--previousSibling
  7. 下一个节点--nextSibling
  8. 第一个子节点--firstChild
  9. 最后一个子节点--lastChild

3.文档节点--document

(1) 获取

  1. getElementById()--通过 id 获取元素
<div id="demo">通过 id 获取元素</div>

<script>
    var demo = document.getElementById('demo');
    demo.onclick = function() {
        console.log('通过 id 获取元素')
    }
</script>

<!-- 点击 id 为 demo 的元素,控制台输出'通过 id 获取元素' -->
  1. getElementsByName()--通过 name 获取元素
<input type="text" name="int" value="通过name获取元素1">
<input type="text" name="int" value="通过name获取元素2">

<script>
    var int = document.getElementsByName('int');
    for(var i = 0;i < int.length; i++) {
        console.log(int[i].value);
    }
</script>

<!-- 控制台输出 name 为 int 的元素的 value 值 -->
  1. getElementsByTagName()--通过标签名获取元素
<div>通过标签名获取元素 1</div>
<div>通过标签名获取元素 2</div>

<script>
    var div = document.getElementsByTagName('div');
    for(var i = 0;i < div.length; i++) {
        console.log(div[i].innerHTML)
    }
</script>

<!-- 控制台输出标签名为 div 的元素的文本内容 -->
  1. getElementsByClassName()--通过 class 获取元素
<div class="demo">通过 class 获取元素 1</div>
<div class="demo">通过 class 获取元素 2</div>

<script>
    var demo = document.getElementsByClassName('demo');
    for(var i = 0;i < demo.length; i++) {
        console.log(demo[i].innerHTML)
    }
</script>

<!-- 控制台输出 class 为 demo 的元素的文本内容 -->
  1. querySelector()--通过选择器获取元素

querySelector() 括号里面要跟上符号,class就写.id就写#标签直接写标签名TagName

<div>通过选择器获取标签名</div>
<div class="div">通过选择器获取 class</div>
<div id="div">通过选择器获取 id</div>

<script>
    var divtag = document.querySelector('div');
    var divclass = document.querySelector('.div');
    var divid = document.querySelector('#div');

    divtag.onclick = function() {
        console.log('通过选择器获取标签名')
    };
    // 点击标签 div,控制台输出"通过选择器获取标签名"

    divclass.onclick = function() {
        console.log('通过选择器获取 class')
    };
    // 点击 class 为 div,控制台输出"通过选择器获取 class"

    divid.onclick = function() {
        console.log('通过选择器获取 id')
    };
    // 点击 id 为 div,控制台输出"通过选择器获取 id"
</script>
  1. querySelectorAll()--通过选择器获取元素集合

获取的是一个数组集合

<input type="text" value="int1">
<input type="text" value="int2">
<input type="text" value="int3">

<script>
    var int = document.querySelectorAll('input')
    for(i = 0; i < int.length; i++) {
        console.log(int[i].value)
    }
</script>

<!-- 浏览器依次打印出`int1`,`int2`,`int3` -->

(2) 创建

  1. createElement()--创建元素(标签)节点
<ul id="ul"></ul>

<script>
    var ul = document.getElementById('ul');
    ul.appendChild(document.createElement('li'))
</script>

可以看到,ul 下面已经生成了一个 li 标签

  1. createTextNode()--创建文本节点
<ul id="ul"></ul>

<script>
    var ul = document.getElementById('ul');
    var li = ul.appendChild(document.createElement('li'));
    var node = document.createTextNode('我是 li');
    li.appendChild(node);
</script>

li 标签中生成了一段文本

  1. createAttribute()--创建属性节点
<input type="text">

<script>
    var int = document.getElementsByTagName('input')[0];
    var value = document.createAttribute('value');
    value.nodeValue = '通过创建属性节点生成';
    int.setAttributeNode(value);
</script>

可以看到 value 属性值被成功创建

  1. createComment()--创建注释节点
<div id="div">创建一个注释节点</div>

<script>
    var div = document.getElementById('div');
    var comment = document.createComment('添加一个注释节点');
    div.appendChild(comment);
</script>

f12 查看源码,可以看到 div 生成一行注释

  1. createDocumentFragment()--创建文档片段

文档片段的作用,就相当于是添加的所有的节点的父元素

1.假如没有 createDocumentFragment,添加了很多节点,也可以在 dom 上呈现,但是每次添加的时候都会调用一次 appendChild() 方法,产生很多次页面渲染,显得比较臃肿 2.把多次添加的节点放在一个 createDocumentFragment 节点里面,页面只会调用一次就可以把所有的节点都渲染了

  1. createEvent()--创建事件对象

  2. addEventListener()--添加事件监听函数

  3. removeEventListener()--移除事件监听函数

事件监听函数详解

  1. dispatchEvent()--触发事件

操作 css

<div id="demo">dom</div>
<script>
    // 直接通过'.'来操作 style
    var demo= document.getElementById('demo');
    demo.style.color = 'red';

    // 通过 setAttribute() 来创建属性节点
    demo.setAttribute('style','background-color: green');

    // 通过 style 的 cssText 属性
    demo.style.cssText = "border: 10px solid black";
</script>

4.元素节点 (element 对象)

有关 innerHTML, innerText, outerHTML, outerText 的区别

一个是元素内容,一个是文本内容

<div id="div1">第一个 div</div>
<div id="div2">第二个 div</div>
<div id="div3">第三个 div</div>
<div id="div4">第四个 div</div>
<div id="div5"></div>
<div id="div6"></div>
<div id="div7"></div>
<div id="div8"></div>
<script>
    var div1 = document.getElementById('div1').innerHTML
    console.log(div1)   // 第一个 div

    var div1 = document.getElementById('div1').outerHTML
    console.log(div1)   // <div id="div1">第一个 div</div>

    var div2 = document.getElementById('div2').innerText
    console.log(div2)   // 第二个 div

    var div2 = document.getElementById('div2').outerText
    console.log(div2)   // 第二个 div

    document.getElementById('div5').innerHTML = '<a>《第 5 个 div》</a>'
    // 在原有的标签内增加内容,如果有标签会被识别

    document.getElementById('div6').outerHTML = '<a>《第 6 个 div》</a>'
    // 原来的标签会被覆盖,新的文本中如果含有标签会自动生成,如果没有标签则直接以文本形式展示

    document.getElementById('div7').innerText = '<a>《第 7 个 div》</a>'
    // 在原有的标签内增加内容,新标签不会被识别为标签元素,而是被当作文本内容直接写入原标签内

    document.getElementById('div8').outerText = '<a>《第 8 个 div》</a>'
    // 原来的标签会被覆盖,新标签不会被识别为标签元素,而是直接被当作文本形式展示
</script>

(1) 节点属性

  1. childElementCount--返回当前元素的子节点的个数
<div id="demo">
    <a></a>
    <span></span>
    <p></p>
    <div></div>
</div>

<script>
    var demo = document.getElementById('demo')
    console.log(demo.childElementCount) // 4
</script>
  1. firstElementChild--返回当前元素的第一个子元素节点 lastElementChild--返回当前元素的最后一个子元素节点
<div id="demo">
    <a></a>
    <span></span>
    <p></p>
    <div></div>
</div>

<script>
    var demo = document.getElementById('demo')
    console.log(demo.firstElementChild) // <a></a>
    console.log(demo.lastElementChild)  // <div></div>
</script>
  1. nextElementSibling--返回当前元素的下一个兄弟元素节点 previousElementSibling--返回当前元素的上一个兄弟元素节点
<span></span>
<div id="demo"></div>
<p></p>

<script>
    var demo = document.getElementById('demo')
    console.log(demo.nextElementSibling)        // <p></p>
    console.log(demo.previousElementSibling)    // <span></span>
</script>
  1. 返回当前元素所有的子节点
<div id="demo">
    <span></span>
    <p></p>
    <a></a>
</div>


<script>
    var demo = document.getElementById('demo').children
    for(var i in demo) {
        console.log(demo[i])
    }
</script>

控制台查看返回结果

  1. 返回所有子节点集合
<div id="demo">
    <span></span>
    <p></p>
    <a id="demo1"></a>
</div>


<script>
    var demo = document.getElementById('demo').children
    for(var i in demo1.childNodes) {
        console.log(demo[i])
    }
</script>

(2) 节点方法

  1. appendChild--插入子节点
<div id="demo"></div>
<script>
    var demo = document.getElementById('demo')
    var node = document.createTextNode('插入一个子节点')
    demo.appendChild(node)
</script>  

f12 可以看到,div 被插入了一个节点

  1. insertBefore(a, b)--在指定位置插入节点

参数 a 表示要插入的内容,b 表示定位,在 b 节点之前插入 a 节点

<div id="demo"></div>
<script>
    var demo = document.getElementById('demo')
    var node = document.createTextNode('插入一个子节点')
    demo.appendChild(node)

    var hr = document.createElement('hr')
    demo.insertBefore(hr, node)
</script>  

可以看到,原来的文本节点之前,被添加了一个新的元素节点

  1. replaceChild(a, b)--替换节点

用参数 a 替换参数 b,a 表示新节点,b 表示旧节点

<div id="demo"></div>
<script>
    var demo = document.getElementById('demo')
    var node = document.createTextNode('插入一个子节点')
    demo.appendChild(node)

    var h3 = document.createElement('h3')
    var h3node = document.createTextNode('title 标题')
    h3.appendChild(h3node)
    demo.replaceChild(h3, node)
</script>  

h3是新节点,node是旧节点,根据图片可以看到,b 替换掉了 a,成功上位

  1. removeChild--父节点删除子节点
<div id="demo">
    <div id="son"></div>
</div>

<script>
    var demo = document.getElementById('demo')
    var son = document.getElementById('son')
    demo.removeChild(son)
</script>  

f12 可以看到,id 为 son 的元素节点,通过demo.removeChild()已经被删除了

  1. removeAttribute--删除属性节点
<div id="demo" class="div"></div>

<script>
    var demo = document.getElementById('demo')
    demo.removeAttribute('class')
</script>  

class 属性已经被删除了

  1. 删除文本节点
<div id="demo">文本</div>

<script>
    var demo = document.getElementById('demo')
    demo.removeChild(demo.childNodes[0])
</script>  

通过demo.childNodesp[0]获取 demo 的第一个节点,即文本节点,然后removeChild它,就删除了

  1. isEqualNode--判断两个元素是否相等 isSameNode--判断两个元素是否相同

两者分别代表相等相同

(1) isEqualNode相等,指的是两个节点是否是同一类型,具有相等的属性(包括:nodeName, nodeValue…等等),还有相等的 attributes,childNodes(相等的位置包含相同的值) (2) isSameNode相同,指的是两个节点引用的是同一个对象

<form action="#">
    <input type="button" />
</form>

<form action="#">
    <input type="button" />
</form>

<form action="#" id="o">
    <input type="button" />
</form>

<form action="#" id="o">
    <input type="text" />
</form>

<script>
    var forms = document.forms;

    var form1 = forms[0];
    var form2 = forms[1];
    var form3 = forms[2];
    var form4 = forms[3];
    var _form1 = document.querySelectorAll('form')[0];


    console.log(form1.isSameNode(form1))  //true  两个节点引用的对象都是第一个 form
    console.log(form1.isSameNode(_form1)) //true  两个节点引用的对象都是第一个 form
    console.log(form1.isSameNode(form2))  //false 两个节点引用的不是一个对象

    console.log(form1.isEqualNode(form2)) //true  两个节点具有完全等同属性
    console.log(form1.isEqualNode(form3)) //false form1 中无等同的 id 属性
    console.log(form3.isEqualNode(form4))  //fasle form4 的 childNodes 中的 input 为 text 类别,与 form3 不同
</script>

根据例子代码可以看出区别:

(1) isSameNode只有引用同一个对象时才相同,比如用两中方法调用同一个对象,再比较这两种方法,比来比去还是那个对象,所以相同 (2) 而isEqualNode比较两个对象的元素节点是否相等,只要两者一致就可以相等 true

  1. hasChildNodes()--判断一个元素是否拥有子节点

判断demo是否拥有子节点,然后输出他的子节点

<div id="demo">
    <!-- <a></a> -->
</div>

<script>
    var demo = document.getElementById('demo')
    console.log(demo.hasChildNodes())
    console.log(demo.childNodes)
</script>
  1. contains(a)--判断一个节点是否包含指定子节点(参数 a 表示要判断的子节点)

存在id="a"的元素,因此 contains 判断成功输出true

不存在id="b"的元素,因此输出false

<div id="demo">
    <a id="a"></a>
    <a></a>
</div>

<script>
    var demo = document.getElementById('demo')
    var a = document.getElementById('a')
    console.log(demo.contains(a))   // true

    var b = document.getElementById('b')
    console.log(demo.contains(b))   // false
</script>

5.属性节点 (Attr 对象)

<input type="text" id="int">

<script>
    var int = document.getElementById('int')

    // 1.获取属性值
    console.log(int.getAttribute("type"))   
    // text
    
    // 2.获取属性节点
    console.log(int.getAttributeNode("type"))
    // type="text"
    
    // 3.设置属性值
    int.setAttribute("value", "input 框")
    // <input type="text" id="int" value="input 框">

    // 4.设置属性节点
    let name = document.createAttribute("name");
    name.nodeValue = "uname";
    int.setAttributeNode(name);
    console.log(int.getAttributeNode('name').value) 
    // <input type="text" id="int" value="input 框" name="uname">

    // 5.删除属性节点
    console.log(int.removeAttribute('name'))
    // <input type="text" id="int" value="input 框">

    // 6.判断是否存在属性
    console.log(int.hasAttributes())
    // true

    // 7.判断是否存在指定属性
    console.log(int.hasAttribute('value'))
    // true
</script>

注意:hasAttributehasAttributes的区别 hasAttributes判断是否存在属性, hasAttribute判断是否存在指定属性

有关 js 函数,方法的一些补充总结

2020-11-14 08:00:00

数据类型,构造函数,原型和原型链的一些讲解补充

一、数据类型

1.5 个基本类型

string number boolean undefined 未定义 null 值为空 symbol 表示一个符号,符号类型是唯一的并且是不可修改的

最常见的还是前三种,字符串,数字,布尔值

2.常见的引用类型

引用类型是一种数据结构,用于将数据和功能组织在一起

对象-Object, 数组-Array, 函数-Function, 正则-RegExp, 日期-Date 等

二、函数

1.什么是函数?

  1. 具有独立功能的代码块,在 js 中使用 function 关键字定义函数
  2. 让代码结构更加清晰,提高代码可用性
  3. js 函数的分类:自定义函数和系统函数

2.自定义函数

有一种匿名函数,没有名字的函数,创建闭包,避免造成全局变量的污染

匿名自执行函数

  1. 概念:匿名函数的定义完成后立即执行,执行函数表达式
  2. 作用:实现闭包和创建独立的命名空间
  3. 使用:分组操作符 (),void 操作符,~操作符,! 操作符等等
  4. 使用场景:函数表达式,对象属性,事件,事件参数,返回值
  5. 定义完匿名函数后,一定要调用
// 函数表达式
window.onload = function() {
    let funcobj = function() {
        alert("函数表达式中的匿名函数")
    }
    funcobj();
}
// 对象属性
window.onload = function() {
    let obj = {
        name: function() {
            alert('对象属性中的匿名函数')
        }
    }
    obj.name();
}

3.内置函数

放在全局下面叫做函数,放在对象里面被叫做方法,对象的方法

  1. 常规函数
alert() // 弹出框
confirm()   // 弹出一个确认框
prompt()    // 弹出一个输入框
isNaN()     // 判断是否为数字
parseInt()  // 将字符串或浮点数转换为整数
parseFloat()    // 将字符串转换为整数或浮点数
eval()      // 计算表达式的结果
  1. 数组函数

for循环遍历数组

var arr = [1,2,3,4];
for(var i = 0; i<arr.length; i++) {
    console.log(arr[i])
}

// 输出 1,2,3,4

通过for in遍历数组

var arr = [1,2,3,4];
for (var i in arr) {
    console.log(arr[i]);
}

// 输出 1,2,3,4
// 增加
unshift()   // 添加元素在数组首部,返回值是数组长度
push()      // 添加元素在数组尾部,返回值是数组长度
conat()     // 连接两个数组,返回值是连接后的数组
    var arr1 = [1];
    var arr2 = [2];
    let arr = arr1.concat(arr2);
    console.log(arr); // [1,2]

/*****************************************/
// 删除
pop()       // 删除数组最后一个元素,返回值是删除的元素
shift()     // 删除数组第一个元素,返回值是删除的元素
splice(a,b) // 删除指定位置a后的b个元素,返回值是删除的元素
slice(a,b)  // 删除从a位置到b位置之间的元素

/*****************************************/
// 查找
indexOf()   // 判断数组是否包含指定元素,存在返回元素,不存在返回-1
includes()  // 判断数组是否包含指定元素,存在返回true,不存在返回false

/*****************************************/
// 其他
sort()      // 将数组按照规则排序
    var arr1 = [4,5,6];
    var arr2 = [1,2,3];

    var arrAscSort = arr5.sort((a, b) => a-b); // 升序排序
    console.log(arrAscSort); // [ 1, 1, 2, 3, 5, 6 ]
    
    var arrDescSort = arr5.sort((a, b) => b-a); // 降序排序
    console.log(arrDescSort); // [ 6, 5, 3, 2, 1, 1 ]

reerse()    // 数组反转
    var arr = [1,2,3,4,5,6];
    // 直接通过reverse()方法调用
    console.log(arr.reverse())  // [6,5,4,3,2,1]

Array.from()    // 将一串数据转换为数组形式
    var str = '将一串数据转换为数组形式'
    console.log(Array.from(str))
    // ["将", "一", "串", "数", "据", "转", "换", "为", "数", "组", "形", "式"]

Array.isArray() // 判断一个变量是否为数组
    var str = '将一串数据转换为数组形式'
    console.log(Array.isArray(str))
    // false
  1. 日期函数 Date()

获取时间

var time = new Date()
// 获取当前时间
// Fri Nov 13 2020 20:21:35 GMT+0800 (中国标准时间)

getFullYear()   // 获取当前年份
getMonth()      // 获取当月月份  -1
getDate()       // 获取当天日期
getHours()      // 获取当前小时
getMinutes()    // 获取当前分钟
getSeconds()    // 获取当前秒
getMilliseconds()   // 获取当前毫秒
getTime()       // 时间戳-获取1970年至今的毫秒数

设置时间

setYear()       // 设置年份
setMonth()      // 设置月份
setDate()       // 设置日期
setHours()      // 设置小时
setMinutes()    // 设置分钟
setSeconds()    // 设置秒
  1. 数学函数 Math

主要常用的数学函数方法

Math.abs()  // 绝对值
Math.ceil() // 向上取整
Math.floor()// 向下取整
Math.round()// 四舍五入
Math.random()   // 生成0-1之间的随机数

random() 的延伸用法---生成指定位的随机数

function getRandomNumber(min, max){
    return Math.floor(Math.random()*(max - min)) + min;
}
console.log(getRandomNumber(1000, 9999));
  1. 字符串函数
indexOf()   // 查找字符串,返回索引值
    var arr = ["字", "符", "串", "函", "数"]
    console.log(arr.indexOf("串"))  // 2

split()     // 将字符串按照指定的分隔符分割开来
    var str = "字符串函数"
    console.log(str.split(''))      // ["字", "符", "串", "函", "数"]

trim()      // 清除字符串两端的空格
    var str = "   字符串函数   "
    console.log(str)        // [   字符串函数   ]
    console.log(str.trim()) // [字符串函数]

match()     // 按照制定规则查找值
    var str = "字符串函数字符串函数"
    console.log(str.match(/字符串/))    // 字符串
    document.write(str.match(/字符串/)) // 字符串

search()    // 返回字符串

首次出现的位置
    var str = "字符串函数字符串函数"
    console.log(str.search('串'))   // 2

replace()   // 替换指定的字符串
    var str = "字符串函数字符串函数"
    console.log(str.replace('字','函'))
    // 函符串函数字符串函数

substring(a,b) // 从指定位置切割字符串,左闭右开
    // 从索引a,截取到b
    var str = "字符串函数字符串函数"
    console.log(str.substring(0,3)) // 字符串

substr(a,b)    // 从指定位置切指定个字符
    // 从索引a开始,截取b个字符
    var str = "字符串函数字符串函数"
    console.log(str.substr(0,3))    // 字符串

为什么 getTime() 返回 1970 年至今的毫秒?

2020-11-13 08:00:00

今天在写 new Date() 时候,无意中发现了一个很有意思的方法,getTime(),百度了一下,有人说是计算从 1970 年 1 月 1 日至今的毫秒数

为什么要是 1970 年呢?

new Date().getTime();

// xxxxxxxxxxx

这个起源于 unix 的诞生,因为 Unix 在 1969 年被开发出来,1971 年正式发布,在这之前没有机器会需要来表示 1970-01-01-00:00:00 之前的时间,后面的语言很多就沿用了这一习惯,js 只是也沿用了这种习惯而已。

当然,这一做法现在看来是很有问题的,例如不方便用它表示更早的时间而且精度有限。

定义 time 从 1970 年 1 月 1 日开始,忽然想到在 JAVA 里,Oracle 数据库时间也是从 1970 年 1 月 1 日开始计算。

比如 java 类代码:

Date date = new Date(0);

System.out.println(date);

// 打印出来的结果:Thu Jan 01 08:00:00 CST 1970也

是 1970 年 1 月 1 日,实际上时分秒是 0 点 0 分 0 秒 (这里打印出来是 8 点,稍后会作解释)。

为什么这个时间会定义在 1970 年 1 月 1 日这个时候呢?

于是开始了 Google,中文网页根本找不到答案。于是试着搜索英文关键字,在 Sun java 论坛总算找到准确的帖子:

http://forums.sun.com/thread.jspa?threadID=595140&start=15

其中有一个回复:

I suspect that Java was born and raised on a UNIX system. UNIX considers the epoch (when did time begin) to be midnight, January 1, 1970. 是说 java 起源于 UNIX 系统,而 UNIX 认为 1970 年 1 月 1 日 0 点是时间纪元。

但这依然没很好的解释"为什么",出于好奇,继续 Google,总算找到了答案:

http://en.wikipedia.org/wiki/Unix_time

这里的解释是:

最初计算机操作系统是 32 位,而时间也是用 32 位表示。

System.out.println(Integer.MAX_VALUE);

2147483647

Integer 在 JAVA 内用 32 位表示,因此 32 位能表示的最大值是 2147483647。另外 1 年 365 天的总秒数是 31536000,2147483647/31536000 = 68.1,也就是说 32 位能表示的最长时间是 68 年,而实际上到 2038 年 01 月 19 日 03 时 14 分 07 秒,便会到达最大时间,过了这个时间点,所有 32 位操作系统时间便会变为 10000000 00000000 00000000 00000000 也就是 1901 年 12 月 13 日 20 时 45 分 52 秒,这样便会出现时间回归的现象,很多软件便会运行异常了。

到这里,我想问题的答案已经出来了:

因为用 32 位来表示时间的最大间隔是 68 年,而最早出现的 UNIX 操作系统考虑到计算机产生的年代和应用的时限综合取了 1970 年 1 月 1 日作为 UNIX TIME 的纪元时间 (开始时间),而 java 自然也遵循了这一约束。

至于时间回归的现象相信随着 64 为操作系统的产生逐渐得到解决,因为用 64 位操作系统可以表示到 292,277,026,596 年 12 月 4 日 15 时 30 分 08 秒,相信我们的 N 代子孙,哪怕地球毁灭那天都不用愁不够用了,因为这个时间已经是千亿年以后了。

最后一个问题:

上面 System.out.println(new Date(0)),打印出来的时间是 8 点而非 0 点,原因是存在系统时间和本地时间的问题,其实系统时间依然是 0 点,只不过我的电脑时区设置为东 8 区,故打印的结果是 8 点。

面向对象编程

2020-11-12 08:00:00

面向对象把构成问题的 transaction 分解成各个对象,而建立对象的目的也不是为了完成一个个步骤,而是为了描述某个事物在解决整个问题的过程中所发生的行为,意在写出通用代码,加强代码重用,屏蔽差异性。

一、什么是面向对象编程

js 是基于原型的,基于面向对象编程

面向对象就是把数据和对数据的操作方法放在一起,作为一个整体——对象。对同类对象抽象出其共性,形成类

1.面向过程程序设计

将一个项目(或者一个事件)从头到尾按顺序,一步一步完成,先做什么,后做什么,一直到结束,也是我们人做事的方法。

自上而下,先确定一个整体的框架,然后添砖加瓦,逐步实现想要得到的效果,适用于简单的系统,容易理解。但是难以应对复杂的系统,不易维护扩展,难以复用

面向过程是分析解决问题的步骤,然后用函数把这些步骤一步一步的实现,然后在使用的时候一一调用则可。强调的是完成这件事儿的动作,更接近我们日常处理事情的思维。

2.面向对象程序设计

将一个项目(或者一个事件)分成更小的项目,每一个部分负责一方面的功能,最后由这些部分组成一个整体,先设计组件,在完成拼装,适用于大型复杂的系统

面向对象把构成问题的 transaction 分解成各个对象,而建立对象的目的也不是为了完成一个个步骤,而是为了描述某个事物在解决整个问题的过程中所发生的行为,意在写出通用代码,加强代码重用,屏蔽差异性。

想要弄明白面向对象,需要先理解类和对象的概念

《什么是类和对象?》

二、创建对象的方法

1.创建字面量和实例

window.onload = function() {
    // 实例
    var person = new Object();
    person.name = '小明';
    person.age = 22;
    person.year = function() {
        console.log(this.name + '今年' + this.age + '岁了!')
    };
    person.year();

    // 字面量
    var student = {
        name: '小明',
        age: 22,
        year: function () {
            console.log(this.name + '今年' + this.age + '岁了!')
        }
    }
    student.year();
}

// 小明今年22岁了!

两者输出的结果是一样的,控制台输出:

缺点:重复实例化对象,代码冗余高

2.工厂模式

window.onload = function() {
    function createObj(name, age) {
        var obj = new Object();
        obj.name = name,
        obj.age = age,
        obj.year = function() {
            console.log(this.name + '今年' + this.age + '岁了!')
        }
        return obj;
    }
    var obj = createObj('小明', 22);
    obj.year();
}

// 小明今年22岁了!

优点:解决重复实例化对象的问题 缺点:无法识别对象的类型,因为所有的实例都指向一个原型

3.构造函数

window.onload = function() {
    function Person(name, age) {
        this.name = name;
        this.age = age;
        this.year = function() {
            console.log(this.name + '今年' + this.age + '岁了!')
        }
    }
    var student = new Person('小明', 22);
    student.year();
}

// 小明今年22岁了!

优点:可以识别对象的类型 缺点:多个实例重复创建方法,无法共享

4. 原型模式

window.onload = function() {
    function Par() {}
    Par.prototype = {
        constructor: 'Par',
        name: '小明',
        age: 22,
        year: function() {
            console.log(this.name + '今年' + this.age + '岁了!')
        }
    };
    var son = new Par();
    son.year();
}

// 小明今年22岁了!

缺点:所有实例共享他的属性和方法,不能传参和初始化属性值

5.混合模式 (推荐使用)

是构造函数和原型模式混合的写法,拥有各自的优点,构造函数共享实例属性,原型模式共享方法和想要共享的属性,可以传参和初始化属性值

先用构造函数定义对象的属性方法,然后用原型模式创建方法,使用的属性通过 prototype 获取,有一个 constructor 属性,可以指向要操作的函数对象(构造函数)

比如constructor: Par,就代表下面这个原型方法指向Par()对象(构造函数)

window.onload = function() {
    function Par(name, age) {
        this.name = name;
        this.age = age;
    }
    Par.prototype = {
        constructor: Par,
        year: function() {
            console.log(this.name + '今年' + this.age + '岁了!');
        }
    };
    var son = new Par('小明', 22)
    son.year();
}

// 小明今年22岁了!

三、原型,原型链

1.原型对象

  1. 函数对象都具有prototype属性,它指向函数的原型对象 (浏览器内存创建的对象),原型对象都具有constructor属性,它指向prototype属性所在的函数对象 (构造函数)
window.onload = function() {
    function Par(name, age) {
        this.name = name;
        this.age = age;
    }
    Par.prototype = {
    // constructor指向对象
        constructor: Par,
        year: function() {
            console.log(this.name + '今年' + this.age + '岁了!');
        }
    };
    var son = new Par('小明', 22)
    son.year();

/*********************************************/
    console.log(Par.prototype)
    console.log(Par.prototype.constructor)
/*********************************************/
}

通过控制台可以看到

构造函数的prototypr属性指向原型对象

原型对象的construcyor属性指向构造函数

  1. 当调用构造函数创建一个实例后,该实例会有一个隐藏属性__proto__ ,它指向构造函数的原型对象
console.log(son.__proto__ === Par.prototype)

// true
  1. 所有的构造函数的 prototype 都是 object 类型
console.log(typeof Par.prototype)

// object
  1. Function 的 prototype 是一个空函数,所有内置函数的__proto__属性都指向这个空函数
console.log(Math.__proto__)
  1. 如果构造函数实例和原型对象中同时定义了一个属性,在调用时,会屏蔽原型对象中的属性,如果想要访问原型对象中的属性值,需要通过delete方法将同名属性在实例(构造函数)中彻底删除
window.onload = function () {
    function Par(name) {
        this.name = name;
    }
    Par.prototype.name = "张三";
    var son = new Par("李四");
    console.log(son.name); // 李四
    console.log(son.__proto__.name); // 张三

    // 使用 delete 删除实例的同名属性值
    console.log(delete son.name);   // true
    console.log(son.name); // 张三
}
  1. 通过hasOwnProperty(属性名)可以判断一个属性存在于构造函数中,还是原型对象中

true表示存在构造函数中;false表示存在原型对象中

console.log(Par.hasOwnProperty(name));  // false
  1. 操作符in,可以判断一个属性是否存在(存在于构造函数和原型对象中皆可)
window.onload = function () {
    function Par(name, age) {
        this.name = name;
        this.age = age;
    }
    Par.prototype = {
        constructor: Par,
        year: function() {
            console.log(this.name + this.age)
        }
    };
    var son = new Par('xm', '22')
    son.year();
    console.log('name' in Par); // true
    console.log('age' in Par);  // false
}

同样的两个属性,判断其是否存在于实例或者原型对象中,输出的结果不一样

参考:《对象中是否有某一个属性 in》https://www.cnblogs.com/IwishIcould/p/12333739.html

2.__proto__和 prototype 的区别

  1. prototype属性只有函数对象上才有,而__proto__属性所有对象都有

  2. prototype是由函数对象指向原型对象,而__proto__是由实例指向函数对象的原型对象

  3. 原型链,将父类型的实例作为子类型的原型对象,这种链式关系叫做原型链

3.继承

  1. 原型链继承

优点:父类原型定义的属性和方法可以复用 缺点:子类实例没有自己的属性,不能向父类传递参数

function test1() {
    function SuperType() {
        this.city = [ "北京", "上海", "天津" ];
        this.property = true;
    }
    SuperType.prototype = {
        constructor : SuperType,     // 保持构造函数和原型对象的完整性
        age : 15,
        getSuperValue : function() {
            return this.property;
        }
    };
    function SonType() {
        this.property = false;
    }

    // 重写子类的原型指向父类的实例:继承父类的原型
    SubType.prototype = new SuperType();

    SubType.prototype = {
        constructor : SubType,
        getSonType : function() {
            return this.property;
        }
    };

    // 优点验证
    let son = new SubType();
    console.log(son.age); // 15
    console.log(son.getSuperValue()); // false

    // 缺点验证
    let instance1 = new SubType();
    instance1.city.push("重庆");
    console.log(instance1.city); // ["北京", "上海", "天津", "重庆"]

    let instance2 = new SubType();
    console.log(instance2.city); // ["北京", "上海", "天津", "重庆"]

}

// test1();
  1. 构造函数继承

优点:子类实例有自己的属性,可以向父类传递参数,解决原型链继承的缺点 缺点:父类原型的属性和方法不可复用

function test2() {
    function SuperType(name) {
        this.name = name;
        this.city = [ "北京", "上海", "天津" ]
    }
    SuperType.prototype = {
        constructor : SuperType,
        age : 18,
        showInfo : function() {
            return this.name;
        }
    };

    function SubType() {
        // 父类调用 call() 或者 apply() 方法和子类共用同一个 this,实现子类实例属性的继承
        SuperType.call(this, "张三");
    }

    // 优点验证
    let instance = new SubType();
    instance.city.push("重庆");
    console.log(instance.city); // ["北京", "上海", "天津", "重庆"]

    let instance1 = new SubType();
    console.log(instance1.city); // ["北京", "上海", "天津"]

    // 缺点验证
    console.log(instance.age); // undefined
    instance.showInfo(); // son.showInfo is not a function
}

// test2();
  1. 组合继承(推荐)

优点:原型的属性和方法可以复用,每个子类实例都有自己的属性 缺点:父类构造函数调用了两次,子类原型中的父类实例属性被子类实例覆盖

function test3() {
    function SuperType(name) {
        this.name = name;
        this.city = [ "北京", "上海", "天津" ]
    }
    SuperType.prototype = {
        constructor : SuperType,
        showInfo : function() {
            console.log(this.name + "今年" + this.age + "岁了");
        }
    };

    function SubType(name, age) {
        // 1. 通过构造方法继承实现实例属性的继承
        SuperType.call(this, name);
        this.age = age;
    }

    // 2. 通过原型链继承实现原型方法的继承
    SubType.prototype = new SuperType();

    // 优点验证
    let instance = new SubType("张三", 15);
    instance.showInfo(); // 张三今年 15 岁了

    let instance1 = new SubType();
    instance1.city.push("重庆");
    console.log(instance1.city); // ["北京", "上海", "天津", "重庆"]

    let instance2 = new SubType();
    console.log(instance2.city); // ["北京", "上海", "天津"]

}

// test3();
  1. 寄生组合继承(推荐)

优点:解决了组合继承的缺点,效率高 缺点:基本没有

function test4() {
    function inheritPrototype(subType, superType) {
        // 1. 继承父类的原型
        var prototype = Object.create(superType.prototype);
        // 2. 重写被污染的 construct
        prototype.constructor = subType;
        // 3. 重写子类的原型
        subType.prototype = prototype;
    }
    function SuperType(name) {
        this.name = name;
        this.city = [ "北京", "上海", "天津" ];
    }

    SuperType.prototype.sayName = function() {
        console.log(this.name);
    };

    function SubType(name, age) {
        SuperType.call(this, name);
        this.age = age;
    }

    // 将父类原型指向子类
    inheritPrototype(SubType, SuperType);

    SubType.prototype.sayAge = function() {
        console.log(this.age);
    }

    // 优点验证
    let instance = new SubType("张三", 15);
    instance.sayName(); // 张三

    let instance1 = new SubType();
    instance1.city.push("重庆");
    console.log(instance1.city); // ["北京", "上海", "天津", "重庆"]

    let instance2 = new SubType();
    console.log(instance2.city); // ["北京", "上海", "天津"]
}

// test4();

4.ES6 新方法--class

新的关键字class在 es6 开始被引入到 javascript 中来,class的目的就是让定义类更简单

用函数方法实现:

function Person(name) {
    this.name = name;
}

Person.prototype.hello = function () {
    console.log('Hello, ' + this.name + '!');
}

var son = new Person('xm')
son.hello();    // Hello, xm!

class来实现:

class Person {
    constructor(name) {
        this.name = name;
    }

    hello() {
        console.log('Hello, ' + this.name + '!');
    }
}

var son = new person('xm')
son.hello();    // Hello, xm!

可以在看到,在定义class中,直接包含了构造函数constructor属性,和原型对象上的函数hello()方法,省略掉了function关键字

需要注意:原来的写法是,构造函数和原型对象分散开来写,现在用class可以直接把两者串在一个对象中,只有最后传参和调用方法时写法是一样的

class 继承

class定义对象的另一个巨大的好处是继承更方便了。想一想我们从Person派生一个PrimaryPerson需要编写的代码量。现在,原型继承的中间对象,原型对象的构造函数等等都不需要考虑了,直接通过extends来实现:

class PrimaryPerson extends Person {
    constructor(name, grade) {
        super(name); // 记得用 super 调用父类的构造方法!
        this.grade = grade;
    }

    myGrade() {
        alert('I am at grade ' + this.grade);
    }
}

注意PrimaryPerson的定义也是通过 class 关键字实现的,而extends则表示原型链对象来自Person,子类的构造函数可能会和父类的不太相同

例如,PrimaryPerson需要namegrade两个参数,并且需要通过super(name)来调用父类的构造函数,否则父类的name属性无法正常初始化。

PrimaryPerson已经自动获得了父类Personhello方法,我们又在子类中定义了新的myGrade方法。

ES6 引入的class和原有的JavaScript原型继承有什么区别呢?

实际上它们没有任何区别,class的作用就是让 JavaScript 引擎去实现原来需要我们自己编写的原型链代码。简而言之,用class的好处就是极大地简化了原型链代码。

但是!

目前并不是所有的浏览器都支持class,所以在选择的时候一定要慎重!

小张厨房日记(更新中...)

2020-11-09 08:00:00

厨房日记,记录我的’高光’时刻

今日菜谱

莴笋炒肉

准备食材:两根莴笋,一块猪肉,两只青椒

调料:盐,酱油,醋,油,料酒,姜,蒜

  1. 炒菜前 10 分钟将猪肉从冰箱取出,放进碗中,用水浸解冻
  2. 将莴笋削皮洗净,用刀切成片状
  3. 将浸泡解冻好的猪肉取出,切成肉丁
  4. 切少许姜和蒜
  5. 准备好两根干辣椒
  6. 往锅中倒入一勺油,待至烧熟后,将姜和蒜倒入翻炒
  7. 将猪肉倒入锅中大火爆炒,并倒入少许料酒和酱油
  8. 一到两分钟过后,将莴笋倒入锅中,并添至少许清水防止太干
  9. 翻炒时倒入少许醋,盐,并放入干辣椒,
  10. 翻炒防止糊锅,静置等待菜熟

没有很复杂的过程,只是细心就可以做出让自己感受舒心的菜


(更新 2020.11.10)

萝卜炖牛肉

准备食材:一根萝卜,四根胡萝卜,一大块牛肉,一卷粉丝

调料:盐,酱油,醋,油,料酒,姜,蒜,胡椒,八角

  1. 将牛肉从冰箱取出,切成两块,取一碗清水,然后放在水中化冻
  2. 牛肉解冻十分钟左右取出,均匀切碎,根据自己口感可随意
  3. 切半块姜,半个蒜,起锅烧油,下葱姜蒜,

暂时没有小葱,等下次有了在下

  1. 加入三四片干辣椒,少许胡椒,八角,
  2. 等到锅中调料榨出香味,下牛肉,翻炒均匀防止糊锅
  3. 倒入料酒,酱油
  4. 在翻炒的过程中,用炖锅烧半锅清水,2-3 分钟后,将烧锅内牛肉、配料全部倒入炖锅,用勺子翻滚均匀
  5. 用盖子盖住
  6. 将萝卜切成片,稍微有点厚度的那种,不薄也不厚,6-7 分钟左右,下萝卜,并加入适当的盐
  7. 下稍加翻滚,盖住盖子,留一个缝隙
  8. 在这个期间洗净胡萝卜,去皮,切成块,等到烧锅 20 分钟左右,下胡萝卜和粉丝

为什么要这么晚才下胡萝卜和粉丝呢? 因为他俩比较软,熟得快,如果放在锅中时间过长,很容易煮烂

  1. 继续煮至 30 分钟左右,煮的差不多了,尝一下咸淡,可适当加入调料
  2. 放一会就可以出锅啦

这是一道耗时比较长的菜,可以慢慢做,大致是这些流程,具体的可根据实际情况调整,比如加入的配菜不同


javascript——函数、变量和方法

2020-11-04 08:00:00

当代码出现有规律的重复之后,可以利用函数,定义变量,调用方法,不用去重复的改动代码,只需要进行函数的修改。基本上所有的高级语言都支持函数,javascript 也不例外,它可以像变量一样被使用,方便且强大,因此本文对 js 函数进行系统的学习,并在学习过程中做了详细的笔记以及样例。

一、函数的定义和调用

1.定义函数

function abs(x) {
    if(x = 0) {
        return x;
    } else {
        return -x;
    }
}
  1. function()指出这是一个函数定义
  2. abs是函数的代码
  3. (x)里面的内容是函数的参数
  4. {...}的内容是函数体,可以包括若干语句,甚至可以没有任何语句

函数体中,必须以return结尾,才可以把结果返回,如果不用 return 结尾的话,就会返回 undefined

也可以直接定义一个对象,这个对象也可以写成函数的方式

var abs = function (x) {
    if (x>=0) {
        return x
    }else {
        return -x
    }
}

function(x)就是一个匿名函数,这个函数被赋值给了变量abs,所以可以直接通过abs调用该函数

这两种定义函数的方式完全一致,,但是用变量定义的时候需要注意,要用;结尾,代表函数语句结束

2.调用函数

调用函数时,直接传参即可 abs(10),根据函数定义,将 10 代入进去即可,返回的结果是 x , 即 10

3.检查参数

可以对参数进行检查,看看是否是自己想要的参数对类型

如果传入对参数abs(x)中非数字,控制台返回结果this is not number,如果传参为数字,则进行条件判断

function abs(x) {
    // 检查参数x是否为数字
    if (typeof x !== 'number') {
        console.log('this is not number')
    }else{
        if (x >= 0) {
            return x
        }else {
            return -x
        }
    }
}

4.arguments

利用arguments,可以获得调用者传入的所有参数

arguments代表传入的参数,arguments.length代表传入参数的长度

console.log(arguments.length)
// 这行代码写在函数中,控制台就可以输出出来

先写一个循环,把参数输出的函数方法,函数写完之后,传入参数,控制台随之打印出传入的参数

function str() {
    var s
    for(var i = 0; i<arguments.length; i++) {
        // 返回传入的参数
        console.log(arguments[i]);
        s += arguments[i] + ",";
    }
    return s;
};
// 传入参数
str("name", "age");
//控制台输出:name, age

5.return

返回 true 时,点击链接直接跳转,返回 false 时,会忽略 a 链接的地址,跳转到 window.location.href 后的地址

<a href="https:www.baidu.com" onclick="return myfun()">baidu</a>
<input type="text" id="test" value="click">
<script>
    function myfun() {
    window.location.href = 'https://www.bilibili.com';
    var test = document.getElementById('test').value;
    console.log(test);
    
    return false;
}
</script>

return 需要注意的地方:函数会自动在行尾添加;,所以在写 return 的时候一定要注意,不要单纯的拆分为两行,很容易报错

return 
    { name: 'foo' }
// 上面这种写法就是有问题的,js的机制会自动将其渲染为
return;     //return undefined
    { naem: 'foo' };

// 正确的写法应该是:
return {
    name: 'foo'
};

6.rest

把传入的参数,多余的部分,以数组的形式保存起来,为了获得额外的参数,需要 i = 2 开始,把已有的 a,b 排除掉

function arr(a, b) {
    var i, rest = [];
    if (arguments.length > 2) {
        
        for (i = 2; i<arguments.length; i++) {
            rest.push(arguments[i]);
        }
    }
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest);
};

arr(1,2,3,4);

控制台打印:

可以看到多余的部分被打印到Array中了

这种写法略显麻烦,下面是更简单的写法

直接在在函数里定义参数rest,并且在前面加上...标识,多余的参数直接以数组的形式交给变量rest,不需要arguments就可以获取全部参数

如果传参数量还没有超过定义参数的数量,函数就会返回一个空数组

function foo(a, b, ...rest) {
    console.log('a = ' + a);
    console.log('b = ' + b);
    console.log(rest)
}

foo(1,2,3,4)
// a = 1
// b = 2
// Array [3,4]

foo(1)
// a = 1
// b = undefined
// Array [ ] 

7.计算

对传入的参数求和

// forEach可以返回数组中所有的元素

function sum(...rest) {
    var sum = 0;
    rest.forEach(function(x) {
        sum += x;
    });
    return sum;
};

//sum(1,2)
//控制台输出 3。求和成功

计算圆的面积

// r 表示圆的半径
// pi 如果没有参数,默认为3.14
function area_of_circle(r, pi){
    var area;
    if(arguments.length == 1) {
        // 当传入的参数只有一位时,计算3.14*r的平方
        area = 3.14*r*r;
    }else{
        area = pi*r*r;
    }
    return area;
}

二、变量和作用域

1.声明变量

在 js 中,通常使用var来生声明变量,而声明的变量实际上是有作用域的

  1. 在函数体内声明的变量,只能在函数体内生效,在函数体外是无法识别的
function fun() {
    var a = 1;
};
a = a + 1;  // err  这行代码直接报错,因为全局中没有a这个变量
  1. 如果两个函数体中各自声明了变量a,互不干扰,在自己的函数体内可以正常作用,出了函数体都没有作用了

  2. js 函数可以嵌套,内部函数可以访问外部函数,外部函数不能访问内部函数

function par() {
    var x = 1;
    function son() {
        var y = x + 1;
    };
    var z = x + y;      // Error:
}

所以var z = x + y会报错,因为变量yson()中,根据函数外部无法访问函数内部y无法被访问,因此var z = x + y报错

  1. 两个嵌套的函数体,各有一个重名变量,js 函数在查找变量的时候,优先从自身开始,如果自身有这个变量就获取,如果没有,有内向外,由下层到上层的查找
function par() {
    var num = 1;
    function son() {
        var num = 2;
        console.log("son() = " + num);
    };
    console.log("par() = " + num);
    son();
};
par();

函数必须经过调用之后才能生效 son()par()

2.变量提升

JavaScript 的函数定义有个特点,它会先扫描整个函数体的语句,把所有申明的变量“提升”到函数顶部,但是并不会将赋值一起提升,很容易产生代码的报错

因此,针对这一问题,我们在声明变量的时候,要将其统一放置在函数的起始位置,严格遵守在函数内部首先声明所有变量的原则

3.全局作用域

不再任何函数内部定义的变量就叫做全局变量,也就是window下,他也被称作全局作用域,全局作用域下的变量实际上被绑定到window

var course = 'learn js';
console.log(course);        // learn js
console.log(window.course)  // learn js

直接访问全局变量或者在前面加上window,结果都是一样的

整个 js 文件只有一个全局作用域,就是window,如果在某一个函数作用域内查找变量,没有查找到,就会由内到外一层层查找,如果最后在全局作用域中也没有查找到,就会ReferenceError 报错

4.局部作用域

在函数内部就是局部作用域,这个代码的名字只在函数的内部起作用

for循环等语句中,无法定义具有局部作用域的变量

5.全局变量和局部变量的区别

  1. 全局变量:在任何一个地方都可以使用,全局变量只有在浏览器关闭的时候才会销毁,比较占用内存资源

  2. 局部变量:只能在函数内部使用,当其所在代码块被执行时,会被初始化;当代码块执行完毕就会销毁,因此更节省节约内存空间

  3. 当在函数作用域中操作一个变量的时候,会先在自身作用域中查找,如果有就直接使用,如果没有就向上级作用域中寻找。如果全局作用域中也没有,那么就报错

6.常量

varlet声明的是一个变量,在 ES6 之间,用大写的变量名,表示定义一个常量

// ES5
var NAME = 'xiaoming'

ES6 新增一个关键字const来定义常量

// ES6
const name = 'xiaoming'

三、解构赋值

1.可以把一个数组的元素分别赋值给不同的变量

var array = ['hello', 'javascript', 'ES6'];
var x = array[0];
var y = array[1];
var z = array[2];

// x = 'hello'
// y = 'javascript'
// z = 'ES6'

2.如果数组本身还有嵌套,也可以进行解构赋值,但是要注意嵌套的层次和数组保持一致

let [x, [y, z]] = ['hello', ['JavaScript', 'ES6']];

x; // 'hello'
y; // 'JavaScript'
z; // 'ES6'

3.解构赋值时可以忽略元素

let [, , z] = ['hello', 'JavaScript', 'ES6'];

z;  // ES6 

4.还可以对对象进行解构赋值

var person = {
    name: 'xiaoming',
    age: 22,
    gender: 'male',
    email: '[email protected]',
    school: 'zyg'
}
// 定义了三个变量,分别对应三个属性
var {name, age, email} = person;
console.log(name, age, email);

控制台就可以打印出我们想要的内容了

对对象进行解构赋值时,也可以进行嵌套

5.可以通过属性名赋值的时候,重新定义一个变量名

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678',
    school: 'No.4 middle school'
};

// 把passport属性赋值给变量id:
let {name, passport:id} = person;
console.log(name);
console.log(age);
console.log(id);
console.log(email);

控制台输出结果:

可以看到,name,age,id 都打印出来了,而 email 报错,因为 email 的内容赋值给了新变量id,而email没有任何内容,所以报错

6.可以使用默认值 true,避免不存在的属性返回 undefined

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678'
};

// 如果person对象没有single属性,默认赋值为true:
var {name, single=true} = person;
name;   // '小明'
single; // true

要注意,赋值的时候不能以{开头,避免 js 将其渲染失败

var person = {
    name: '小明',
    age: 20,
    gender: 'male',
    passport: 'G-12345678'
};
// 声明变量
var x;
var y;

// 解构赋值
{x, y} = { name: '小明', x: 100, y: 200} // Error:

在这里 {x, y} = person会报一个错误,=不合法,所以正确的写法是,在赋值语句外部包裹一层()小括号

({x, y} = { name: '小明', x: 100, y: 200});

7.解构赋值的使用场景

交换两个变量的值

var a = 1;
var b = 2;
[a, b] = [b, a]

四、对象的方法

绑定到对象上的函数被称为方法

在一个对象中绑定函数,称为这个对象的方法

1.this

下面段代码返回的是(今年的年份-出生年份)

var xm = {
    name: 'xiaoming',
    birth: 1998,
    age: function() {
        var year = new Date().getFullYear();
        return year - this.birth
    }
};
// 在对象xm中,调用方法age()
xm.age();   // 22

这里引入了一个新的关键词this

在方法内部,this是一个特殊的变量,它始终指向当前对象,也就是xm这个变量

所以this.birth指的就是变量xm的birth属性

this存在于方法中,想在方法中调用对象的属性,必须通过this

如果在方法写在对象外部时,this的指向问题就要好好分析了,比如:

function getage() {
    var year = new Date().getFullYear();
    return year - this.birth;
}

var xiaoming = {
    name: '小明',
    birth: 1990,
    age: getage
};

单独调用getage()的时候,这时的getage()指的是一个方法,同时这个方法处于全局作用域下,此时this指向的是全局对象window,所以返回NaN

只有xiaoming.age()调用的才是对象xiaoming下面的方法getage()

因此:要保证this的指向正确,必须使用obj.xxx()的形式调用 如果没有使用这种方法,全部报错,如果是在’use strict’模式下,this会指向 undefined

2.that

如果在对象里面的方法,又套了一层事件,此时this指向又有问题了,它指向第一层方法,而不是方法对应的对象

所以,在写方法的时候,直接先声明一个var that = this,这个that指向对象里面的属性,接下来,在方法里面调用属性的时候,直接在前面加上that.即可,它直接指向到对象下面的属性

var xm = {
    name: 'xiaoming',
    birth: 1998,
    age: function() {
        var that = this;
        function getbirthage() {
            var y = new Date().getFullYear();
            return y - that.birth;
        }
        return getbirthage();
    }
};

// xm.age();

可以看到,通过定义var that = this,然后在方法里面使用that.指向属性,不管套了几层方法,都不会报错,直接指向对象下面的属性

通过var that = this,可以放心的在方法里面定义其他函数,不用担心获取不到对象属性的问题

但是有一个需要注意的地方,每个方法结束后,都要返回一下结果,return getbirthage()

3.apply

除了var that = this,还可以通过apply属性控制this的指向

apply是函数本身的方法,它拥有两个参数

function getage() {
    var y = new Date().getFullYear();
    return y - this.birth;
}

var xm = {
    name: 'xiaoming',
    birth: 1998,
    age: getage
}

// xm.age();
// getage.apply(xm, []);

写法就是getage.apply(xm, [])apply的第一个参数代表this指向,即对象,第二个参数,表示函数本身的参数

4.apply( ) 和 call( )

call()是与apply类似的方法,区别是:

  1. apply()将参数打包成Array
  2. call()直接将参数按顺序传入

调用math.max(1,2,3),分别采用两种方式

math.max.apply(null, [1,2,3]);  // 3
math.max.call(null, [1,2,3]);   // 3

两者的结果是一样的,调用普通函数时,把this绑定为null

HTML 事件属性--DOM

2020-10-29 08:00:00

研究 html 的对象,事件和方法,从 js 的角度来思考,这个标签的属性是通过什么方法,触发什么事件来实现的

  1. 键盘鼠标事件

  2. 页面窗口事件

  3. 表单事件

一、window 事件属性

针对 window 对象触发的事件,在<body>中执行

1.onbeforeprint/onafterprint

在浏览器打印之间/后触发

<body onbeforeprint = print()>
<script>
    function print() {
        alert("打印之间!")
    }
</script>

打印前和打印后的事件方法类似,一个是在打印前触发事件,一个是在打印这个事件结束之后触发onafterprint这个事件

demo 查看

2.onbeforeunload/unload

离开页面之间触发的事件/之后触发的事件

打开 f12,然后刷新,或者关闭浏览器,可以在浏览器触发事件

<script>
    window.onbeforeunload = function(){
        return "提示"
        //返回的内容不会显示,
        //在刷新的时候触发,或者关闭浏览器,浏览会直接提示是否重新加载,不能直接返回return
    }
</script>

demo 查看

3.onerror

当图片出错是可以触发该事件,支持该事件的标签只有 <img>,<object>,<style>

<img src="1.gif" onerror = "myfun()">

<script>
    function myfun() {
        alert("图片出错")
    }
</script>

1.gif 不存在,因此触发了一个 myfun() 事件,弹出窗口

demo 查看

4.onhashchange

当文档改变时发生的脚本 ???

5.onload

页面加载完成之后触发

demo 查看

6.onmessage

在消息被触发时运行 ???

7.onoffline

当浏览器离线时触发

<body onoffline="myfun()">

8.ononline

当浏览器在线工作时触发

<body ononline="myfun()">

ononlineonoffline在网络断线或者连线时触发,可以控制电脑网络查看效果

9.onpagehide

当用户从网页离开时触发,也可以理解成当窗口隐藏时运行的脚本

刷新页面可以触发事件

demo 查看

10.onpageshow

打开一个新页面或者刷新的时候触发

demo 查看

相当于onload,加载页面时触发,但是在不同浏览器触发的有所不同

第一次加载页面时,onpageshowie浏览器中不触发,其他情况都触发

11.onresize

当浏览器窗口被改变大小时触发事件

<body onresize="myfun()">
    <script>
        function myfun() {
            alert('浏览器窗口改变了')
        }
    </script>
</body>

demo 查看

12.onunload

页面被下载时触发,就是刷新或者关闭页面时触发

window.onunload = function() {
    return '确定关闭吗?'
}

13.onpopstate

当浏览器窗口记录改变时运行的脚本,

14.onredo

当文档执行撤销时触发的事件

二、form 事件属性

由 html 表单内触发的事件,通常使用在 form 元素中

1.onblur

失去焦点时运行的脚本

<input name="int" id="int" onblur="myfun()">
<script>
    function myfun() {
        var int = document.getElementById('int').value;
        document.getElementById('int').value = int.toUpperCase();
    }
</script>

给 input 绑定一个 onblur=myfun() 的事件,当 input 失去焦点的时候产生什么样的效果

demo 查看

2.onfocus

元素获得焦点时触发事件,和 onblur 相反

demo 查看

3.onchange

当元素值被改变的时候触发事件

<input type="text" id="int" name="int" onchange="myfun()">
<script>
    function myfun() {
        alert('我的元素被改变啦!')
    }
</script>

这个事件的意思就是,当我对 input 内容进行操作改变后,浏览器会弹出一个 alert

demo 查看

4.oncontextmenu

当用户右键操作时触发该事件

可以绑定在任意元素中触发,可以一直触发,不用刷新

<div oncontextmenu="myfun()">这是一个文本框!</div>
<script>
    function myfun() {
        alert('oncontentmenu事件触发')
    }
</script>

demo 查看

5.onformchange

当表单获得输入时可以触发事件

触发发不了脚本

6.onforminput

当表单获得用户输入时触发的事件,无法触发

浏览器不支持

6.oninput

当元素获得用户输入时触发的事件 当输入框输入或者删除时都会触发 oninput

<input type="text" oninput="myfun()">
<script>
    function myfun() {
        alert('oninput触发了')
    }
</script>

onchangeoninput有相似的地方,都是改变文本内容时触发事件
但是onchange是在 input 失去焦点时才触发,oninput是立刻触发

demo 查看

7.oninvalid

oninvalid事件要搭配required属性来使用

required如果使用该属性,代表必填字段,oninvalid是当元素无效时触发的事件

<form>
    <input type="text" oninvalid="myfun()" required>
    <input type="submit">
</form>
    <script>
        function myfun() {
            alert('填写为空')
    }
</script>

demo 查看

8.onreset

表单中重置按钮被点击时触发

<form onreset="myfun()">
    <input type="text">
    <input type="reset" value="reset">
</form>
<script>
    function myfun(){
        alert('重置成功')
    }
</script>

demo 查看

9.onselect

文本被选中时触发

<input onselect="myfun()" value="选中我">
<script>
    function myfun() {
        alert('onselect被触发 ')
    }
</script>

demo 查看

10.onsubmit

当表单被提交时触发

要把事件绑定到 form 标签里面

<form onsubmit="myfun()">
    <!-- 注意:事件要绑定到form标签上面 -->
    <input type="text" name="name">
    <input type="submit" value="submit">
</form>
<script>
    function myfun() {
        alert('提交了')
    }
</script>

demo 查看

注意:提交了之后会自动刷新页面,document 和 console 的触发内容会立刻刷新,一闪而过,只有 alert 能停留在页面上,等待点击确定

三、key 键盘事件

1.onkeydown

按下任意键时触发,包括系统按钮,箭头和功能键

demo 查看

2.onkeypress

按下任意字母数字键时触发,但系统按钮,箭头和功能无法识别

3.onkeyup

松开任何之前按下的键盘时触发

demo 查看

四、mouse 鼠标事件

利用鼠标触发的事件

1.onclick

鼠标点击元素触发的事件

<p onclick="myfun()">点击我</p>
<script>
    function myfun() {
        alert('点击成功')
    }
</script>

demo 查看

2.ondblclick

鼠标双击时触发的事件

<div ondblclick="myfun()">测试文本!</div>
<script>
    function myfun() {
        alert('双击测试成功!')
    }
</script>

demo 查看

3.拖动事件

ondrag/ 元素被拖动时触发的事件

ondragstart/在拖动操作开端运行的脚本

ondrop/ 当元素正在被拖动时触发的事件

ondragend/在拖动操作末端运行的脚本

ondragenter/当元素元素已被拖动到有效拖放区域时运行的脚本

ondragleave/当元素离开有效拖放目标时运行的脚本

ondragover/当元素在有效拖放目标上正在被拖动时运行的脚本

4.onmousedown/onmouseup

当元素按下鼠标时触发的事件/鼠标释放时触发的事件

  1. 一个是按下去的瞬间就触发
  2. 一个是当鼠标被松开的时候触发

onmouseup效果和onclick一样,因为click也是点击过后触发

这两个事件加起来相当于一个click事件,但是并不能用这两个事件来做点击事件的效果

demo 查看

5.onmousemove

当鼠标移动到元素上时触发

相当于 css 里面的 :hover,但是作为事件拥有更灵活多变的用法

demo 查看

6.onmouseout

当鼠标离开元素时触发

demo 查看

7.onmouseover

当鼠标进入元素时触发

onmouseoveronmousemove都是鼠标进入元素触发
区别:
1.
over在进入元素时触发,但在元素内部移动时不触发
move是进入元素时和在元素里面移动时都触发
2.
over优先触发,然后才触发move

8.onmousewheel

当鼠标滚动被滚动时触发的事件

<style>
    div {
        height: 1000px;
    }
</style>

<body>
    <div onmousewheel="myfun()">测试</div>
    <script>
        function myfun() {
            alert('onmousewheel')
        }
    </script>
</body>

每次滚动时都可以触发,可以绑定到 body 上

demo 查看

9.onscroll

当元素滚动条被滚动时触发的事件

<div onscroll="myfun()">In my younger and more vulnerable years my father gave me some advice that I've been turning over in my mind ever since.</div>
<p>滑动了<span id="ci"></span></p>
<script>
    x = 0
    function myfun() {
        document.getElementById('ci').innerHTML = x += 1
    }
</script>

这个案例---滑动滚动计数

demo 查看

区别:
1.wheel在滚轮滑动的时候有效,鼠标拖拽的时候无效
2.onscroll不管在滑动还是拖动的时候,均生效

js 基本语法

2020-10-21 08:00:00

在学习 js 基本语法的过程中,把遇见的知识点和问题记录喜下来,以便未来复习和参考。

数据类型有:数据,字符串,数组,数字,布尔值等。

一、数据类型

1.number

2.布尔值

只有 true 和 false 两种,可以直接用 true 和 false 来表达,比如:

true;	//true
false;	//false
2>1;	//true
2>3;	//fasle

与 && 两者同时为true,运算结果为true

或 || 只要有一个为true,运算结果就是true

非 ! 当程序为true时,运算结果取反为false

var age = 15;
if(age >= 18) {
    alert('adult')
}else{
    alert('teenager')
}

age 值是 15,当 age 大于且等于 18 的时候浏览器弹出 adult,反之弹出 teenager

3.字符串

4.比较运算符

=====

当使用==进行比较的时候,一般会自动转换类型然后再进行比较

当使用===进行比较的时候,如果数据类型不一样,直接返回false,等到类型一致了,然后再进行比较

有一个特殊的number就是NaN,它不与任何值相等,包括它自己NaN

NaN === NaN	//false

5.nullundefined

null表示一个空值,0表示一个数值,''表示长度为 0 的字符串,但是null表示空

undefined表示未定义

但是区分两者意义不大,大多数还是在用nullundefined仅仅在判断函数参数是否传递的情况下有用

6.数组 Array

[ ] ,表示一组按顺序排列的集合,其中的每个值称为元素

new Array(1,2,3)	//[1,2,3]

数组的索引

二、对象和变量

var person = {
	name: 'jack',
	age: 20,
	city: 'hangzhou'
}

person是对象变量,name: 'jack'这就是一个键值对,name是属性名,'jack'是属性

要获取一个对象的属性,用对象变量.属性,也就是person.name,结果是jack

person.name	//jack
person.age	//20

var是一个动态语言,就算 var 定义了 x = 100,后面又有 x = x+ 100,所以就变成了 200,

var x = 100;
x = x+ 100;
console.log(x)	//200

假如用 int 来定义 x,后面的 x = x + 100 就会报错

并且使用 var 定义的变量只会存在与该函数作用域,并不是默认的全局变量

而如果没有使用var,直接定义i = 100,变量 i 会被默认为全局变量

三、字符串

需要用到转义字符\

1.模板字符串

var name = '小明';
var age = 20;
var message = name + '今年' + age + '了';
//小明今年20了
var message = `${naem},你今年${20}了`
//ES6新增的语法,一样的结果

字符串不可改变

如果对字符串的某个索引赋值,不会报错,不会发生任何改变

var a = 'hello,world!'
a[0];	//h
a[0] = k;
console.log(a);	//结果为'hello,world!',不会发生改变

2.toUpperCase 大写

他会返回一个新的字符串,把一个字符串全部变成大写

var a = 'hello';
a.toUpperCase();	//返回HELLO

3.toLowerCase 小写

他会返回一个新的字符串,他会把一个字符串全部变成小写

var a = 'hello'
a.toLowerCase();	//HELLO

4.indexOf

他会返回指定字符串的索引,如果没有找到指定的字符串,则会返回-1

var a = 'hello,world!'
a.indexOf('world');	//6

5.substring

他会返回指定区间索引的字符串,包括前一个数,不包括后一个数

var a = 'hello,world!'
a.substrng(0, 5);	//hello

如果()里面只有一个数值,则从这个数值的索引开始直到结束,比如:

var a = 'hello,world!';
a.substring(6);	//返回world!

四、数组

数组Array可以包含任何类型的数据,并通过索引来访问每个数据

1.length 属性

Array的长度可以用length属性来获取,它和索引不一样,从 1 开始计算

var arr = [1,2,3,'hello',null,true]
arr.length;	//返回6

并且通过改变length的值,可以改变数组的内容大小变化

var arr = [1,2,3,'hello',null,true]
arr.length;	//6
arr.length = 8;	//[1,2,3,'hello',null,true,undefined,undefined]
arr.length = 2;	//[1,2]

2.通过索引改变数组

Array可以直接通过索引修改对应的元素

var arr = [1,2,3]
arr[1] = 'hello'
console.log(arr); 	//[1,'hello',3]

js 允许直接通过索引改变数组的长度,不会报错,但是不建议这么做

3.indexOf

Array可以通过indexOf来搜索一个指定的元素的索引

var arr = [1,2,3,'hello']
arr.indexOf(1);	//0
arr.indexOf(3); //2

4.slice

slice属性与substring类似,后者是截取字符串的内容,slice是截取数组的内容,然后返回一个新的数组

如果有两个数值,包括前面的数值,不包括后面的数值;如果只有一个数值,则从它开始算起一直到结束

var arr = [1,2,3,4,5];
arr.slice[0,2];	//返回[1,2]
arr.slice[2];	//返回[3,4,5]

如果slice不指定数值,则返回数组的全部内容,可以利用这个特性复制一个相同的数组出来

var arr = [1,2,3];
var arr1 = arr.slice();
console.log(arr1);	//[1,2,3]
arr1 === arr;	//true

注意:两个数组相比都是 false,就算数组内容一样也会 false

var arr = [1,2,3]
var arr1 = [1,2,3]
arr === arr1	//false

5.push 和 pop

push()向数组的末尾添加元素

pop()把数组的最后一个元素删掉

6.unshifth 和 shift

unshift()向数组的头部添加元素

shift()把数组的第一个元素删掉

7.sort

sort()可以对数组进行排序,会直接修改当前数组的元素位置,直接调用时,会按照默认的方式排序

var arr = [B,A,C]
arr.sort();
arr	//[A,B,C]

8.reverse

reverse()会把整个数组调个个,不是反向排序

var arr = [2,1,3]
arr.reverse();
arr;	//[3,1,2]

9.splice

这个属性是万能的方法,通过调用splice(),可以从指定的索引删除元素或者添加元素

五、条件判断

在 js 中,使用 if() {...} else {...}进行条件判断

var age = 22;
if(age>20) {
	// 如果age>20成立,执行该语句
	console.log('22>20')
}else {
	// 如果age>20不成立,则执行该语句
	conosle.log('22<20')
}

最终,控制台会打印出22>20,因为条件成立

执行的语句要用{}包裹起来,防止其他情况会报错

多条件判断语句

三个或者三个以上多判断语句,被称为多条件判断语句

var a = 10
if(a<10) {
	console.log('a<10')
}else if(a>20){
	console.log('a>10')
}else{
	console.log('10<=a<=20')
}

如果多个条件中都满足,则取第一个满足的结果,执行其代码,之后满足的自动忽略掉,所以在 进行条件判断的时候,不要重复判断情况

一个复杂的多条件判断语句

var height = parseFloat(prompt('请输入身高(m):'));
var weight = parseFloat(prompt('请输入体重(kg):'));
var bmi = weight/(height*height);
if(bmi < 18.5) {
	console.log('过轻')
}else if(bmi>18.5,bmi<25) {
	console.log('正常')
}else if(bmi>25,bmi<28){
	console.log('过重')
}else  if(bmi>28,bmi<32){
	console.log('肥胖')
}else{
	console.log('严重肥胖')
}

parseFloat可以解析一个字符串,并返回一个数字

六、循环

简单的运算可以手打出来

1 + 2 + 3
// 控制台输出 6 

但是几百次,几千次,几万次的运算无法手打,可以依靠循环语句来进行计算,为了让计算机能够进行成千上万次的运算

循环语句有两种 for 和 while,他们有不同的用法,适应不同的情况

1. for 循环

通过初始条件,结束条件和递增条件来循环执行语句块

var x = 0
var i
for(i = 1; i <= 1000; i++) {
	x = x + i
}

i = 1 是初始条件,i 从 1 开始算起 i<=是判断条件,满足就执行循环,不满足就退出循环 i++ 是递增条件,每次循环过后都是+1,当无数次后不满足了 i<=1000,会跳出循环

2. 通过 for 语句遍历数组

var arr = ['apple', 'banana', 'oringen']
var x,i
for(i = 0; i <= arr.length; i++) {
	x = arr[i]
	console.log(x)
}

3. 用 break 终止 for 循环

var x = 0;
for (  ;  ;  ) { // 将无限循环下去
    if (x > 100) {
	console.log(x)
	break; // 通过if判断来退出循环
    }
    x ++;
}

4. for…in

可以把一个对象对属性循环遍历出来

var person = {
	name: 'jack',
	age: 20,
	city: 'beijing'
};
for(var i in person) {
	console.log(i)
	console.log(person[i])
}

var i in person会把 person 里面所有对属性遍历,然后通过console.log(i)可以把属性名打印出来,console.log(person[i])可以把属性值打印出来

如果对一个数组进行这样的操作,就可以把数组元素对索引打印出来,打印结果是字符串形式

5. while 循环

while循环适用于忽视判断条件的情况,for适用于明确了初始条件和结束条件的情况

比如要计算 1-100 之间的寄数和,可以用while循环

var x = 0
var n = 99
while (n > 0) {
	x = x + n
	n = n - 2
}
x

在变量内部,n 不断自减,直接 n=-1,不满足判断条件,此时退出循环

6. do…while

do...while循环是先循环,然后再判断条件,所以不论条件是否满足,do...while至少循环一次,这是它与forwhile的区别

比如:

var n = 0
do{
	n = n + 1
}while(n > 1)
n;	//1

先定义n=0,然后执行n=n+1,所以n=1,再判断条件,当 n>1 的时候执行,不符合,退出循环,控制台输出n,结果为 1

七、Map 和 Set

1. Map

Map是一组键值对的结构,具有极快的查找速度

只要我们定义一个属性名和属性值对应的数组,就可以从这个数组里面通过 name 直接查找数据

var m = new Map([['jack', 95], ['Bob', 94], ['linda', 93]])
m.get('jack')

首先,要初始化一个Map数组

var m = new Map();	//空map
m.set('jack', 95)	//添加一个新的键值对		key-value
m.has('jack')		//检查是否存在 '			jack'key
m.get('jack')		//获取'jack'对应的数据		value
m.delete('jack')	//删除'jack'键值对			key-value
m.get('jack')		//undefined

一个 key 只能对应一个 value,所以如果重复赋值的话,后面的数据会把前面的数据覆盖掉

var m = new Map();
m.set('Adam', 67);
m.set('Adam', 88);
m.get('Adam'); // 88

2. Set

set中值储存key,不储存value,而且在set中,key不能重复,如果重复了,会自动忽略掉重复

首先,创建一个空的set

var m = new Set()	//空set
m.add(1)			//添加一个key
m.delete(1)			//删除一个key
m	//控制台输入[ ]空数组,没有数据

对全栈的一些思考

2020-09-18 08:00:00

这里我要推荐一本书,叫做《Web 全栈工程师的自我修养》,在看的过程中,自己也代入进去进行分析,然后写了这篇笔记。

“全栈工程师正在成为 IT 行业的新秀,无论是在上市互联网公司还是在创业公司,都对全栈工程师青睐有加。本书作者是腾讯公 司高级工程师,在前端、后端和 APP 开发方面都有丰富的经验,在本书中分享了全栈工程师的技能与要求、核心竞争力、未来发 展方向、对移动端的思考。除此之外,本书还详细记录了作者从零开始、学习心得。本书内容全面、客观务实适合互联网行业新人、程序员。以及期待技术转型的从业者阅读参考。”

作者余果毕业于西安点击科技大学软件工程学院,曾任腾讯社交用户体验设计部高级 UI 工程师、前端开发负责人,负责多个项目的产品设计,以自身的经历为模板撰写了这本书。

从自身说起,因为对技术和机器的热爱让他走向了计算机这门行业,而在书中,他也经常提及自己理性和感性思维相结合,对全栈工程师进行了深入分析。

全栈工程师是指一个能够处理数据库、服务器、系统工程和客户端所有工作的工程师。根据项目的不同,客户需要的可能是移动栈、Web 栈,或者原生应用程序栈。这是官方的定义。而以我目前出入前端的思维通俗点来说,大概也就是一个人包揽前端,后端,一个人处理数据进行项目维护,甚至于设计图都是自己做的。

很多人都说全栈有很多好处,不同担心很多人编写不同步,思维不一致,导致做出来的项目每个人的想法不同,全栈一个人包揽所有的过程,都在自己脑子里。

可能因为我进入这个行业不久,我认为全栈是不太合理的存在,打个比方说,在流水线还没出现之间,手工作坊基本都是一个人负责所有的产品制作,从原料,制作,打磨,成品,或许很经过很多步骤,但是一个人都可以完美的做出来,就这样类似全栈工程师这样的存在。

而生产线就更像一个团队,产品,UI,前端,后端测试等,每个人相互配合。

全栈和团队比起来,我个人觉得团队的优势更大,但架不住老板喜欢一个人干三个人活,更喜欢全栈一点。

培养一个全栈的花费的时间和精力难以想象,甚至不确定培养一定成功,但是如果专精某一技术领域,成为高级工程师的概率绝对远大于一个全栈。当然这也只是我个人的臆测,并没有具体的数据支撑。

书中也提及,流水线带来的另一个好处,不会对某个工程师产生依赖性,即使失去这个员工,也可以让被人接手工作

说了很多团队化的好处,这不代表就没有弊端,肯定是有的,书中总结出来几点:

“因为各司其职的工作流程有效率低下、成本高的缺点,所以很多创业公司都不会配备齐全的流水线,而是希望采用更灵活的方式来组建团队,全栈工程师也因此成为了理想的选择。但是全栈工程师的兴起还离不开这两个重要因素:技术的发展,以及提供 PaaS 服务的平台越来越多。”

虽然做成为一个全栈工程师需要耗费大量的时间精力来打磨,但是这个思维我还是比较喜欢的。比如说,有 a,b,c 三个人,分别擅长各自的领域,有一天他们共同负责的项目出了一个小问题,但是问题出在哪里领域还不知道,虽然是个小问题,但需要每个人去查验一遍,因为是三个领域,这就导致应该有两个人的查验是无效的,这从另一个方向来说也降低了项目效率。

然后这时候,有一个工程师 s,注意他不是全栈工程师,只是喜欢学习多领域的技术,掌握了一些不算太高深的技术知识,查验了一遍,很快就找出了问题所在。

那他一个人效率就相当于三个的效率了,其实这样说并不严谨,我也只是打个比方。

我突然想到一句话,不知道说的准确与否,

“全栈的职位存在是不合理的,但全栈的思维是一个有上进心的工程师所必备的。”

chrome 浏览器中对 autoplay 的一些处理技巧

2020-09-11 08:00:00

我在写博客的时候,想给博客网页添加自己喜欢的音乐,这样我在写作和阅读的时候,心情也会比较愉悦,这个时候,我们就需要用到audio这个标签。

 <audio src=""></audio>

同时 audio 标签还带有很多实用的属性,常用的有以下几个:

但是我在设置 autoplay 的时候发现,chrome 对自动播放深恶痛绝,直接从根源上限制了这个属性的开启,意思是说,这个属性直接禁用,在打开网页没有交互的情况下,不允许自动播放。

但仔细想一想,这个做法其实是对用户比较有利的。假设用户使用的时候宽带流量,直接自动播放视频、音频,就会对用户造成损失。甚至一些广告会自动播放,这就直接影响到了用户的体验。所以,对于 chrome 的做法我还是比较赞同的。

回过头一想,不对啊,我的目的是解决这个问题,不是夸赞谷歌的。我想给自己的个人博客添加喜欢的音乐,所以我找了一个不算办法的办法。我直接在模拟一个事件,当鼠标点击网页任意处的时候,自动触发 autoplay 的音频效果,直接上代码。

<script>
function toggleSound() {
  var music = document.getElementById("vd");          
  //获取ID  
  console.log(music);
  console.log(music.paused);
  if (music.paused) { 
    //判读是否播放  
    music.paused=false;
    music.play(); 
    //没有就播放 
  }  
}
setInterval("toggleSound()",1);
</script>

这样处理过之后,插入音频,任意点击网页某个地方,都会触发播放效果,但凡事有利有弊,这样做的话,一旦开启播放,将无法启用暂停功能,也就是说会一直播放下去

如果是像我这样写个人博客,这个弊端倒是影响不大,我在这里记录一下这个方法。

推荐书籍《CSS 世界》

2020-08-11 08:00:00

本书从前端开发人员的需求出发,以“流”为线索,从结构、内容到美化装饰灯方面,全面且深入地讲解前端开发人员必须了解和掌握的大量的 CSS 知识点。同时,作者结合多年的从业经验,通过大量的实战案例,详尽解析 CSS 的相关知识与常见问题。

作者还为本书开发了专门的配套网站,进行实例展示。问题答疑。作为一本 CSS 深度学习的书,书中介绍大量许多前端开发人员都不知道的 CSS 知识点。通过阅读本书,读者会对 CSS 世界的深度和广度有一个全新的认识。

这本书作者采用口语化的方式,讲述在 css 中遇到的一些有意思的问题,循序渐进,引人入胜,对有前端基础的人来说,读这本书还是很有意思的。我经常在闲余时间看看这本书,会给我一些启发。

文字溢出隐藏以及和 flex 冲突的问题

2020-08-03 08:00:00

在某些段落中,页面要求文字只显示一行,但是width固定,而文字过长,就会出现一个需求,超过长度限制的文字被隐藏且显示省略号,css 支持这样的属性。

单行文本溢出隐藏

div{
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

多行文本溢出隐藏

div {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

简单的几行代码就可以达到我们的要求。

但是有一个我们需要注意的地方!!

flex 布局是前端写代码经常使用的一种布局方式,简单便捷且有效,但是在使用 flex 布局的元素中不能同时使用文字溢出隐藏,也就是两者不能同时出现在同一标签中。

这里,我们也有对应的解决办法。

只要保证 flex 布局和隐藏的样式不在同级元素中就可以,所以在文字外部多包裹一层标签。

可以采用下面这段代码的写法;

<p>   
    <!-- 我们在这一层的标签进行flex布局 -->
    <span>  <!-- 这一层的标签我们引入文字过长隐藏的样式 -->
        <!-- 文本 -->
    </span>
</p>

vue 生命周期

2020-06-19 08:00:00

学习 vue 中一个非常重要的领域,就是生命周期,它包含了很多的内容。每个 vue 实例在被创建的时候都要经历一系列的初始化过程,这个过程就是 vue 的生命周期。

每个 vue 实例在被创建的时候都要经过一系列的初始化过程——例如,需要把设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等,我们要把这一系列的过程称为组件的生命周期(组件从注册到销毁的整个过程)。我们有时候需要在组件生命周期的某个过程中,执行某些代码,基于此,vue 提供了生命周期钩子函数,给了用户在不同阶段添加自己的代码的机会。

但是在此之前,我们要详细的介绍下组件的生命周期,以及生命后期中每个阶段组件完成和未完成的部分。

一、组件的生命周期

放一张大家都很熟悉的官网文章中对生命周期的注释图。

从图中可以看出,vue 为生命周期提供了 8 个钩子函数,分别是:

1.beforeCreate

beforeCreate 在 vue 实例发生之后,数据观测 (data observer) 和 event/watcher 事件配置之前被调用。

我们在上面的例子中在的 beforeCreate 钩子中调用 Vue 的 data 和 method,来看一下结果:

可以看到 Vue 中的 data 和方法都是去不到的,并且是在 wath 之前执行

2.created

实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测 (data observer),属性和方法的运算,watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。

主要应用:调用数据,调用方法,调用异步函数

console 输出的结果我们看一下

可以看到:created 钩子可以获取 Vue 的 data,调用 Vue 方法,获取原本 HTML 上的直接加载出来的 DOM,但是无法获取到通过挂载模板生成的 DOM(例如:v-for 循环遍历 Vue.list 生成 li)

3.beforeMount

在挂载开始之前被调用:相关的 render 函数(模板)首次被调用。

例如通过 v-for 生成的 html 还没有被挂载到页面上

 beforeMount: function () {
    console.log('beforeMount:',document.getElementsByTagName('li').length);
  },

console 打印出来的 beforeMount 为 1

4.mounted

el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。

有初始值的 DOM 渲染,例如我们的初始数据 list,渲染出来的 li,只有这里才能获取

 mounted: function () {
    console.log('mounted:',document.getElementsByTagName('li').length);
  },

结果 mounted: 3 , 可以看到到这里为止,挂载到实例上了,我们可以获取到 li 的个数了

5.beforeUpdate

数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。

当我们更改 Vue 的任何数据,都会触发该函数

 beforeUpdate: "function () {
    console.log('beforeUpdate 钩子执行.');
    console.log('beforeUpdate:'+this.message)
  },

6.updated

由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。

该钩子在服务器端渲染期间不被调用。

数据更新就会触发(vue 所有的数据只有有更新就会触发),如果想数据一遍就做统一的处理,可以用这个,如果想对不同数据的更新做不同的处理可以用 nextTick,或者是 watch 进行监听

 updated: function () {
    console.log('updated 钩子执行...');
    console.log('updated:',this.message)
  },

7.beforeDestroy

实例销毁之前调用。在这一步,实例仍然完全可用。

8.destroyed

Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。该钩子在服务器端渲染期间不被调用。

结果

可以看打到销毁 Vue 实例时会调用这两个函数

hello, world

2020-06-14 08:00:00

2020 年 6 月 14 日

我的博客正式建立。

愿一切安好。

0001-01-01 08:00:00

linuxdo 插件交流群

Search

0001-01-01 08:00:00

关于

0001-01-01 08:00:00

1.个人介绍

const author = {
  name: '子舒',
  create_date: 1998,
  tags: ['前端程序员', '独立博客作者', '骑行爱好者', '摄影爱好者', 'INFJ 人格'],
  adress: '杭州',
  email: '[email protected]',
  github: 'https://github.com/dlzmoe',
  blog: 'https://zishu.me',
  description: '喜欢折腾技术,研究各种奇怪的程序和网站,喜欢户外运动,热爱生活。'
}

我的 计划单

同时将周刊一起发布到了本网站,督促自己多扩展眼界,获取更新的知识,不管是技术类型还是娱乐,让自己的容量不断 +1.

提供一些免费服务

2.友情链接

有时候就会遇到一些文章写的不错的博主,于是就记录在这里了。

余果的博客 // 涛叔 // 江卮可乐 // 东东博客 // Leon Fong // 卢涛南博客 // Airing 的博客 // Jiang’s Blog // ImQi1 的博客

5.版权声明

本站文章所有版权均归本人所有,未经允许,不允许转载。

在线服务

0001-01-01 08:00:00

提供一些免费的在线服务。

工具

镜像源

标签

0001-01-01 08:00:00

留言

0001-01-01 08:00:00

哎呀,终于有人来啦!欢迎欢迎!这里是留言板,也是一个充满奇思妙想的地方。
有什么想说的都可以在这里留下你的足迹,就像小动物在树上留下酷炫的手印一样!

计划

0001-01-01 08:00:00

给自己制定一些小计划。


# 2024 年

# 2023 年

项目

0001-01-01 08:00:00

1. linuxdo-scripts

linux.do 增强插件,功能持续更新,欢迎提出新想法!

https://github.com/dlzmoe/linuxdo-scripts


骑行

0001-01-01 08:00:00

坐骑:
捷安特 atx620
迪卡侬 rc 120.

出门运动注意安全,保持水分补充,杭州的骑友可以一起骑车。

2024 年统计

月份 里程(km)
9 月 103
8 月 83
7 月 51
6 月 27
5 月 12
4 月 12
3 月 169
2 月 23

2023 年统计

月份 里程(km)
11 月 40
10 月 190
9 月 232
8 月 276
7 月 352
6 月 197