首页
归档
关于
友人帐
Search
1
Mac OS 终端利器 iTerm2 + Oh My Zsh
13,899 阅读
2
前端 History 路由及 API 反向代理的 Nginx 配置
10,595 阅读
3
解决 Xcode Command Line Tools 错误或无法安装问题
6,250 阅读
4
Mac下右键使用VSCode打开项目
3,945 阅读
5
BrowserHistory刷新页面404问题
3,430 阅读
码上世界
内容分享
生活印记
其他
登录
Search
小小孩
累计撰写
95
篇文章
累计收到
181
条评论
首页
栏目
码上世界
内容分享
生活印记
其他
页面
归档
关于
友人帐
搜索到
95
篇与
小小孩
的结果
2019-08-28
在 Linux 下搭建 Git 服务器步骤
由于代码经常改动,搞得头大,就想找个代码管理工具。查了一些资料,最后选择使用 git 管理代码,下面将搭建的过程记录下来。(亲测可以使用) "content: "### 环境:服务器 CentOS6.6 + git(version 1.7.1)客户端 Windows10 + git(version 2.8.4.windows.1)① 安装 GitLinux 做为服务器端系统,Windows 作为客户端系统,分别安装 Git服务器端:yum install -y git安装完后,查看 Git 版本[root@localhost ~]# git --version git version 1.12.6客户端:下载 Git for Windows,地址:https://git-for-windows.github.io/安装完之后,可以使用 Git Bash 作为命令行客户端。安装完之后,查看 Git 版本 $ git --version git version 2.15.1.windows.2② 服务器端创建 git 用户,用来管理 Git 服务,并为 git 用户设置密码 [root@localhost home]# useradd git [root@localhost home]# passwd git③ 服务器端创建 Git 仓库设置 /home/first.git 为 Git 仓库然后把 Git 仓库的 owner 修改为 git[root@localhost home]# git init --bare first.git Initialized empty Git repository in /home/first.git [root@localhost git]# chown -R git:git first.git给仓库设置 700 权限 尽量 700 权限 chmod -R 700 /home/first.git/*④ 客户端 clone 远程仓库Git 服务器就已经搭得差不多了。下面我们在客户端 clone 一下远程仓库 $ git clone git@xxx.xxx.xxx.xxx:/home/first.git Cloning into 'first'... The authenticity of host 'xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx)' can't be established. RSA key fingerprint is 2b:55:45:e7:4c:29:cc:05:33:78:03:bd:a8:cd:08:9d. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'xxx.xxx.xxx.xxx' (RSA) to the list of known hosts. git@192.168.8.34's password:这里两点需要注意:第一,当你第一次使用 Git 的 clone 或者 push 命令连接 GitHub 时,会得到一个警告:这是因为 Git 使用 SSH 连接,而 SSH 连接在第一次验证 GitHub 服务器的 Key 时,需要你确认 GitHub 的 Key 的指纹信息是否真的来自 GitHub 的服务器,输入 yes 回车即可。Git 会输出一个警告,告诉你已经把 GitHub 的 Key 添加到本机的一个信任列表里了: Warning: Permanently added 'github.com' (RSA) to the list of known hosts.此时 C:\\Users\\用户名\\.ssh 下会多出一个文件 known_hosts,以后在这台电脑上再次连接目标 Git 服务器时不会再提示上面的语句。这个警告只会出现一次,后面的操作就不会有任何警告了。如果你实在担心有人冒充 GitHub 服务器,输入 yes 前可以对照 GitHub 的 RSA Key 的指纹信息是否与 SSH 连接给出的一致。第二,这里提示你输入密码才能 clone,当然如果你知道密码,可以键入密码来进行 clone,但是更为常见的方式,是利用 SSH 的公钥来完成验证。⑤ 客户端创建 SSH 公钥和私钥 $ ssh-keygen -t rsa -C "youremail@example.com"你需要把邮件地址换成你自己的邮件地址,然后一路回车,使用默认值即可,由于这个 Key 也不是用于军事目的,所以也无需设置密码。如果一切顺利的话,可以在用户主目录里找到.ssh 目录,里面有 id_rsa 和 id_rsa.pub 两个文件,这两个就是 SSH Key 的秘钥对,id_rsa 是私钥,不能泄露出去,id_rsa.pub 是公钥,可以放心地告诉任何人。⑥ 服务器端 Git 打开 RSA 认证然后就可以去 Git 服务器上添加你的公钥用来验证你的信息了。在 Git 服务器上首先需要将/etc/ssh/sshd_config 中将 RSA 认证打开,即: RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys这里我们可以看到公钥存放在.ssh/authorized_keys 文件中。所以我们在/home/git 下创建.ssh 目录然后把 .ssh 文件夹的 owner 修改为 git [root@localhost git]# chown -R git:git .ssh⑦ 将公钥导入服务器端 authorized_keys 文件将电脑C:\\Users\\用户名\\.ssh\\id_rsa.pub文本可以导入到/home/git/.ssh/authorized_keys重要:修改 .ssh 目录的权限为 700修改 .ssh/authorized_keys 文件的权限为 600 [root@localhost git]# chmod 700 .ssh [root@localhost git]# cd .ssh [root@localhost .ssh]# chmod 600 authorized_keys再重启 ssh 服务 service sshd start之后 clone 就不要输密码了⑨ 禁止 git 用户 ssh 登录服务器之前在服务器端创建的 git 用户不允许 ssh 登录服务器编辑 /etc/passwd找到:git:x:502:504::/home/git:/bin/bash 修改为git:x:502:504::/home/git:/bin/git-shell此时 git 用户可以正常通过 ssh 使用 git,但无法通过 ssh 登录系统。
2019年08月28日
663 阅读
0 评论
0 点赞
2019-08-04
一个专注于榜单的网站
不知道你有没有这样的困惑.每天上网,不知道最新的实时消息 其他人在谈论最新的梗,最新的热门八卦时 你一脸蒙蔽,如果你有这样的困惑,接下来的网站可能为你解决这样的困惑有这样一个网站专门收集各站热点聚合,包括 微信、今日头条、百度、知乎、V2EX、微博、贴吧、豆瓣、天涯、虎扑、Github、华尔街见闻,几乎全网新闻热点排行榜服务。就是 今日热榜没有你看不到的榜单,只有你想不到! 去嗨吧?
2019年08月04日
2,375 阅读
1 评论
0 点赞
2019-08-04
Mac OS 终端利器 iTerm2 + Oh My Zsh
总结下 Mac OS 上 iTerm2 + Oh My Zsh 的配置 以防又要到处找1. 安装 iTerm2安装包安装下载地址:https://www.iterm2.com/downloads.html 下载的是压缩文件,解压后是执行程序文件,你可以直接双击,或者直接将它拖到 Applications 目录下。推荐使用 Homebrew (mac 神器,如果不清楚请自行搜索)进行安装:$ brew cask install iterm22. 配置 Oh My Zsh因为墙的缘故 安装都很慢,如果本机有代理软件的化,可以使用如下代码进行代理!如果没有代理可忽略这一步export all_proxy=socks5://127.0.0.1:1080Oh My Zsh 是对主题的进一步扩展,地址:https://github.com/robbyrussell/oh-my-zsh一键安装:$ sh -c "$(curl -fsSL https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"安装好之后,需要把 Zsh 设置为当前用户的默认 Shell(这样新建标签的时候才会使用 Zsh):$ chsh -s /bin/zsh然后,我们编辑 .zshrc文件vim ~/.zshrc将主题配置修改为ZSH_THEME="agnoster"agnoster是比较常用的 zsh 主题之一,你可以挑选你喜欢的主题 zsh 主题列表:https://github.com/robbyrussell/oh-my-zsh/wiki/themes3. 配置 Meslo 字体使用上面的主题,需要 Meslo 字体支持,要不然会出现乱码的情况 字体下载地址:Meslo LG M Regular for Powerline.ttf下载好之后,直接双击安装即可。然后打开 iTerm2,打开 Preferences 配置界面,然后Profiles -> Text -> Font -> Chanage Font,选择 Meslo LG M Regular for Powerline 字体。另外,VS Code 的终端字体,也可以进行配置,打开 VS Code,打开用户配置,搜索fontFamily,然后将右边的配置增加"terminal.integrated.fontFamily": "Meslo LG M for Powerline"4. 声明高亮方法1使用 Homebrew 安装:$ brew install zsh-syntax-highlighting安装成功之后source ~/.zshrc在最后一行增加下面配置:source /usr/local/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh方法2使用 git 安装cd ~/.oh-my-zsh/custom/plugins/ git clone https://github.com/zsh-users/zsh-syntax-highlighting.git 安装成功之后在.zshrc最后一行增加下面配置:source ~/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh5. 自动建议填充这个功能是非常实用的,可以方便我们快速的敲命令。cd ~/.oh-my-zsh/custom/plugins/ git clone https://github.com/zsh-users/zsh-autosuggestions.git然后编辑vim ~/.zshrc找到plugins配置,增加zsh-autosuggestions插件。plugins=( git zsh-syntax-highlighting zsh-autosuggestions )注:上面声明高亮,如果配置不生效的话,在plugins配置,再增加zsh-syntax-highlighting插件试试。有时候因为自动填充的颜色和背景颜色很相似,以至于自动填充没有效果,我们可以手动更改下自动填充的颜色配置例如:586e75,示例:6. 左右键跳转主要是按住option + → or ←键,在命令的开始和结尾跳转切换,原本是不生效的,需要手动开启下。打开 iTerm2,打开 Preferences 配置界面,然后Profiles → Keys → Load Preset... → Natural Text Editing,就可以了。7. iTerm2 快速隐藏和显示这个功能也非常使用,就是通过快捷键,可以快速的隐藏和打开 iTerm2,示例配置(Commond + .)8. iTerm2 隐藏用户名和主机名假如你用的主题是 agnoster,修改方法是进入 oh-my-zsh/themes/然后vi agnoster.zsh-theme编辑主题配置文件,找到如下代码:# Context: user@hostname (who am I and where am I) prompt_context() { if [[ "$USER" != "$DEFAULT_USER" || -n "$SSH_CLIENT" ]]; then # 修改如下代码,@Mac可以你自己定义 也可以直接注释,这样什么都不会显示了 # prompt_segment black default "%(!.%{%F{yellow}%}.)$USER@%m" prompt_segment black default "%(!.%{%F{yellow}%}.)$USER@Mac" fi }{alert type="warning"}改了这个之后 会出现 oh my zsh 无法更新的后遗症 ,这是因为改了 git 版本库的原因解决方法如下{/alert}cd ~/.oh-my-zsh git status git stash upgrade_oh_my_zsh git stash popgit stash 讲解请见这里9. iTerm2 配置代理vim ~/.zshrc增加下面配置(按实际使用的协议及端口):# proxy list alias proxy='export all_proxy=socks5://127.0.0.1:1080' alias unproxy='unset all_proxy'iTerm2 需要新建标签页,才有效果:当需要代理时 先运行 proxy 不需要时再运行unproxy$ proxy $ curl myip.ipip.net 当前 IP:xxx.xxx.xxx.xxx 来自:xx $ unproxy $ curl myip.ipip.net 当前 IP:xxx.xxx.xxx.xxx 来自:xx10. iTerm2 快捷命令快捷命令说明:命令说明command + t新建标签command + w关闭标签command + 数字 command + 左右方向键切换标签command + enter切换全屏command + f查找command + d垂直分屏command + shift + d水平分屏command + option + 方向键 command + [ 或 command + ]切换屏幕command + ;查看历史命令command + shift + h查看剪贴板历史ctrl + u清除当前行ctrl + l清屏ctrl + a到行首ctrl + e到行尾ctrl + f/b前进后退ctrl + p上一条命令ctrl + r搜索命令历史
2019年08月04日
13,899 阅读
5 评论
0 点赞
2019-06-28
Nodejs 爬取one和墨迹天气定时发邮件
根据用户配置 爬取 one 和不同地区墨迹天气 每天定时发邮件,支持多人地区个性化定制 可以的话 可以去https://github.com/cyea/email-bot 给个小星星效果展示如何快速使用1. 拉取代码安装依赖这里使用yarn作为包管理器git clone https://github.com/cyea/email-bot.git cd email-bot yarn2. 配置① 修改发送者邮箱账号密码敏感配置新建.env文件 格式是跟.env.example 一样的 填入自己的邮箱账号密码及邮件提供商NODE_ENV = production #正式环境精简代码所用 EmianService = outlook #邮件提供商 支持列表:https://nodemailer.com/smtp/well-known/ EamilAuth_user = xxxx@outlook.com #发送者邮箱地址 EamilAuth_pass = xxxxxxxxx # smtp 授权码② 修改其他不敏感配置修改config/index.js里的配置文件const { env } = process; module.exports = { ONE: "http://wufazhuce.com/", // ONE的web版网站 MOJI_HOST: "https://tianqi.moji.com/weather/china/", // 中国墨迹天气url, EmianService: env.EmianService, // 发送者邮箱厂家 EamilAuth: { // 发送者邮箱账户用户名及密码 user: env.EamilAuth_user, pass: env.EamilAuth_pass }, EmailFrom: "yuyehack@outlook.com", // 发送者昵称与邮箱地址 EmailSubject: "一封暖暖的小邮件", // 邮件主题 /** * @description: 收信人详细 */ EmailToArr: [ { TO: "yuyehack@gmail.com", // 接收者邮箱地址 CITY: "jiangsu", // 墨迹天气链接末尾城市代码 LOCATION: "pukou-district" // 墨迹天气链接末尾详细地区代码 }, { TO: "yuyehack@qq.com", CITY: "jiangsu", LOCATION: "kunshan" } ], //每日发送时间 SENDDATE: "58 15 8 * * *" };③ 运行yarn start代码详解具体代码可见 https://github.com/cyea/email-bot.git先展示下项目结构├─config │ index.js #配置 │ ├─email │ index.js #发送邮件模块 │ ├─superagent │ index.js #获取天气及ONE 数据 │ ├─utils │ index.js #通用工具函数 │ superagent.js #请求发送封装 │ ├─view | index.js #生成邮件样式模块 | index.njk #邮件样式模板模块 │ .env.example #.env │ index.js #服务启动模块 │ schedule.js #定时模块 │ test.js #模板样式调试模块 │ yarn.lock │ .gitignore │ LICENSE │ package.json │ README.md1. 爬取数据使用 superagent 和 cheerio 组合来实现爬虫① superagent 使用因为多次两次使用的superagent 函数代码结构类似 所以我再把 superagent 封装了一次 Promise 抛出 fetch方法// utils/superagent.js const superagent = require("superagent"); //请求 function fetch(url, method, params, data, cookies) { return new Promise(function(resolve, reject) { superagent(method, url) .query(params) .send(data) .set("Content-Type", "application/x-www-form-urlencoded") .end(function(err, response) { if (err) { reject(err); } resolve(response); }); }); } module.exports = fetch;② 数据爬取爬取 ONEconst getOne = async () => { // 获取每日一句 let res = await fetch(config.ONE, "GET"); let $ = cheerio.load(res.text); //转化成类似jquery结构 let todayOneList = $("#carousel-one .carousel-inner .item"); // 通过查看DOM获取今日句子 let info = $(todayOneList[0]) .find(".fp-one-cita") .text() .replace(/(^\s*)|(\s*$)/g, ""); let imgSrc = $(todayOneList[0]) .find(".fp-one-imagen") .attr("src"); return { // 抛出 one 对象 one: { info, imgSrc } }; };爬取天气const getWeather = async (city, location) => { //获取墨迹天气 let url = config.MOJI_HOST + city + "/" + location; // 根据配置得到天气url let res = await fetch(url, "GET"); let $ = cheerio.load(res.text); //获取墨迹天气地址 let addressText = $(".search_default") .text() .trim() .split(", ") .reverse() .join("-"); //获取墨迹天气提示 let weatherTip = $(".wea_tips em").text(); // 获取现在的天气数据 const now = $(".wea_weather.clearfix"); let nowInfo = { Temp: now.find("em").text(), WeatherText: now.find("b").text(), FreshText: now.find(".info_uptime").text() }; // 循环获取未来三天数据 let threeDaysData = []; $(".forecast .days").each(function(i, elem) { // 循环获取未来几天天气数据 const SingleDay = $(elem).find("li"); threeDaysData.push({ Day: $(SingleDay[0]) .text() .replace(/(^\s*)|(\s*$)/g, ""), WeatherImgUrl: $(SingleDay[1]) .find("img") .attr("src"), WeatherText: $(SingleDay[1]) .text() .replace(/(^\s*)|(\s*$)/g, ""), Temperature: $(SingleDay[2]) .text() .replace(/(^\s*)|(\s*$)/g, ""), WindDirection: $(SingleDay[3]) .find("em") .text() .replace(/(^\s*)|(\s*$)/g, ""), WindLevel: $(SingleDay[3]) .find("b") .text() .replace(/(^\s*)|(\s*$)/g, ""), Pollution: $(SingleDay[4]) .text() .replace(/(^\s*)|(\s*$)/g, ""), PollutionLevel: $(SingleDay[4]) .find("strong") .attr("class") }); }); return { moji: { addressText, weatherTip, nowInfo, threeDaysData } }; };③ 数据合并异步获取两个数据const getAllData = async (city, location) => { let oneData = await getOne(); let weatherData = await getWeather(city, location); const allData = { today: formatDate(), ...oneData, ...weatherData }; return allData; }; module.exports = getAllData;2. 模版引擎生成 HTML① 模板编写这里选用 nunjucks 作为模板引擎,因为邮件不支持外链 css 所以使用内联 css 虽然比较麻烦使用刚获取到数据 模板渲染<!-- index.njk --> <div style="padding: 0;max-width: 600px;margin: 0 auto;"> <div style="width:100%; margin: 40px auto;font-size:20px; color:#5f5e5e;text-align:center"> <span>今天是{{today}}</span> </div> <div style="width:100%; margin: 20px auto;font-size:16px;color:#2bbc8a;text-align: center;"> <span><span style="font-family: Arial;font-size: 100px;line-height: 1;">{{moji.nowInfo.Temp}}</span><span style="vertical-align: top;">℃</span></span> <span style="font-size: 30px;padding-top: 50px;">{{moji.nowInfo.WeatherText}}</span> </div> <div style="text-align:center;font-size:12px;color:#d480aa">{{moji.addressText}}</dev> <div style="width:100%; margin: 0 auto;color:#5f5e5e;text-align:center"> <span style="display:block;color:#676767;font-size:20px">{{moji.weatherTip}}</span> <span style="display:block;margin-top:15px;color:#676767;font-size:15px">近期天气预报</span> {% for item in moji.threeDaysData %} <div style="display: flex;margin-top:5px;height: 30px;line-height: 30px;justify-content: space-around;align-items: center;"> <span style="width:15%; text-align:center;">{{ item.Day }}</span> <div style="width:25%; text-align:center;"> <img style="height:26px;vertical-align:middle;" src='{{ item.WeatherImgUrl }}' alt=""> <span style="display:inline-block">{{ item.WeatherText }}</span> </div> <span style="width:25%; text-align:center;">{{ item.Temperature }}</span> {% if (item.PollutionLevel==='level_1') %} <div style="width:35%; "> <span style="display:inline-block;padding:0 8px;line-height:25px;color:#8fc31f; border-radius:15px; text-align:center;">{{ item.Pollution }}</span> </div> {% elif (item.PollutionLevel==='level_2') %} <div style="width:35%; "> <span style="display:inline-block;padding:0 8px;line-height:25px;color:#d7af0e; border-radius:15px; text-align:center;">{{ item.Pollution }}</span> </div> {% elif (item.PollutionLevel==='level_3') %} <div style="width:35%; "> <span style="display:inline-block;padding:0 8px;line-height:25px;color:#f39800; border-radius:15px; text-align:center;">{{ item.Pollution }}</span> </div> {% elif (item.PollutionLevel==='level_4') %} <div style="width:35%; "> <span style="display:inline-block;padding:0 8px;line-height:25px;color:#e2361a; border-radius:15px; text-align:center;">{{ item.Pollution }}</span> </div> {% elif (item.PollutionLevel==='level_5') %} <div style="width:35%; "> <span style="display:inline-block;padding:0 8px;line-height:25px;color:#5f52a0; border-radius:15px; text-align:center;">{{ item.Pollution }}</span> </div> {% elif (item.PollutionLevel==='level_6') %} <div style="width:35%; "> <span style="display:inline-block;padding:0 8px;line-height:25px;color:#631541; border-radius:15px; text-align:center;">{{ item.Pollution }}</span> </div> {% else %} <div style="width:35%; "> <span style="display:inline-block;padding:0 8px;line-height:25px;color:#631541; border-radius:15px; text-align:center;">none</span> </div> {% endif %} </div> {% endfor %} </div> <div style="text-align:center;margin:35px 0;"> <span style="display:block;margin-top:55px;color:#676767;font-size:15px">ONE · 一个</span> <img src="{{ one.imgSrc }}" style="max-width:100%;margin:10px auto;" alt=""> <span style="color:#b0b0b0;font-size:13px;">摄影</span> <div style="margin:10px auto;width:85%;color:#5f5e5e;" >{{one.info}}</div> </div> </div> ② 模板渲染node fs 模块 读取本地模板文件 抛出 渲染好的 html 结构数据const nunjucks = require("nunjucks"); const fs = require("fs"); const path = require("path"); const getHtmlData = njkData => { return new Promise((resolve, reject) => { try { const njkString = fs.readFileSync( path.resolve(__dirname, "index.njk"), "utf8" ); const htmlData = nunjucks.renderString(njkString, njkData); resolve(htmlData); } catch (error) { reject(error); } }); }; module.exports = getHtmlData;3. 使用 Node 发送邮件这里使用 nodemailer注意的是邮箱密码不是你登录邮箱的密码,而是 smtp 授权码,什么是 smtp 授权码呢?就是你的邮箱账号可以使用这个 smtp 授权码在别的地方发邮件,一般 smtp 授权码在邮箱官网的设置中可以看的到.不知道的话可以使用邮箱账号及密码试试const config = require("./../config"); const sendMail = (transporter, To, HtmlData) => { return new Promise((resolve, reject) => { let mailOptions = { from: config.EmailFrom, // 发送者邮箱 to: To, // 接收邮箱 subject: config.EmailSubject, // // 邮件主题 html: HtmlData //模板数据 }; transporter.sendMail(mailOptions, (error, info = {}) => { if (error) { console.error("邮件发送成功" + error); reject(error); } else { console.log("邮件发送成功", info.messageId); console.log("静等下一次发送"); resolve(); } }); }); }; module.exports = sendMail;4. 整合运行let transporter = nodemailer.createTransport({ service: EmianService, port: 465, secureConnection: true, auth: EamilAuth, pool: true }); const getAllDataAndSendMail = async () => { for (let i = 0, len = EmailToArr.length; i < len; i++) { try { let item = EmailToArr[i]; let apiData = await getAllData(item.CITY, item.LOCATION); let htmlData = await getHtmlData(apiData); await sendMail(transporter, item.TO, htmlData); } catch (error) { console.error(error); } } }; getAllDataAndSendMail();5. 定时这里用到了 node-schedule 来定时执行任务,它跟 corn 很类似 之不是基于Node的具体用法可见 node-schedule文档这里我使用了 每天早上的 08:15:58 定时发送 尽量不取整点const schedule = require("node-schedule"); const config = require("./config"); const scheduleRun = fn => { console.log("NodeMail: 开始等待目标时刻..."); let j = schedule.scheduleJob(config.SENDDATE, function() { // SENDDATE: "58 15 8 * * *" console.log("开始执行任务......"); fn(); }); }; module.exports = scheduleRun;所以只要 引入scheduleRun方法scheduleRun(getAllDataAndSendMail);6. 配置详情因为像邮件 smtp 授权码 是敏感信息 建议放进环境变量 env2 是个不错的工具 ,具体使用可以看env2文档具体配置详见这里问题1. 邮箱登陆失败一般是在服务器上运行时,邮箱提供商安全机制 会阻止异地登陆 ,只要去邮箱提供商允许就可以了2. 发送失败因为多人定制因为邮件内容不一样,所以不是同一封邮件,会额外开辟一个线程发送,可能会超过邮件提供商允许线程
2019年06月28日
3,180 阅读
3 评论
0 点赞
2019-06-25
使Typecho支持最流行的Emoji表情
Emoji 表情随着 IOS 的普及和微信的支持越来越常见,比如这些比较常见的表情:⭐️ ✨ ⚡️。所谓 Emoji 就是一种在 Unicode 位于\u1F601-\u1F64F区段的字符。这个显然超过了目前常用的 UTF-8 字符集的编码范围\u0000-\uFFFF。在 MySQL 中,UTF-8 只支持最多 3 个字节,而 emoji 是 4 个字节.Typecho 默认不支持emoji表情,其实不是程序的锅,而是由于编码的问题,只需要将默认的数据库编码utf8修改为utf8mb4即可,当然别忘了,utf8mb4编码只有在PHP5.5以后才支持的哦。简单两步即可让 typecho 支持 emoji修改数据库编码进入 PhpMyadmin,选择您的数据库,操作-----整理----选择 utf8mb4_unicode_ci修改数据库表编码直接运行以下 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;3.修改数据库配置文件网站根目录数据库配置文件 config.inc.php$db->addServer(array ( 'host' => localhost, 'user' => 'youruser', 'password' => 'yourpassword', 'charset' => 'utf8mb4', //修改这一行 'port' => 3306, 'database' => 'yourdatabase' ), Typecho_Db::READ | Typecho_Db::WRITE);大功告成!最后来一波😀😁😂🤣😃😄😅😆😉😊😋😎😍😘😗😙😚☺🙂🤗🤔😐😑😶🙄😏😣😥😮🤐😯😪😫😴😌😛😜😝🤤😒😓😔😕🙃🤑😲☹🙁😖😞😟😤😢😭😦😧😨😩😬😰😱😳😵😡😠😷🤒🤕🤢🤧😇🤠🤡🤥🤓😈👿👹👺💀👻👽🤖💩😺😸😹😻😼😽🙀😿😾🏻🏼🏽🏾🏿🗣👤👥👫👬👭👂👂👂👂👂👂👃👃👃👃👃👃👣👀👁👅👄💋👓🕶👔👕👖👗👘👙👚👛👜👝🎒👞👟👠👡👢👑👒🎩🎓⛑💄💍🌂💼推荐一个 Emoji 表情更新及时且比较全的网站 https://getemoji.com/ 需要哪个表情直接 COPY 即可转自:https://www.marky.cn/typecho-emoji.html
2019年06月25日
2,979 阅读
2 评论
0 点赞
2019-06-06
Mac下右键使用VSCode打开项目
这个在 win10 是个很简单的问题,但在 Mac 确实棘手的问题查找各种方法才找到这个比较方便的方法,记录一下!在启动台打开自动操作新建快速操作文稿配置然后在左上角搜索框输入访达,找到打开访达项目, 拖动这个条目到右边窗口,然后如下图所示操作即可配置完成之后 cmd+s 保存 ,文件名写成 Open in VS Code检测成果打开访达 找到你的项目文件夹,右键文件夹,选择服务即可看到 Open in VS Code ,点击即可
2019年06月06日
3,945 阅读
0 评论
0 点赞
2019-06-04
优雅的处理 async/await 异常
每次使用 async/await 都包裹一层 try/catch ,很麻烦,这里提供另外一个思路更好的处理 async/await思考// 定义一个全局方法 async function errorCaptured(asyncFunc) { let args = [...arguments].slice(1); try { let res = await asyncFunc(...args); return [null, res]; } catch (error) { return [error, null]; } } // 一个示例异步函数 let asyncFunc = (a, b, c) => { return new Promise((resolve, reject) => { const num = Math.random().toFixed(1); if (num > 0.5) { resolve({ a, b, c }); } else { reject({ a, b, c }); } }); }; // 运行 (async () => { let [err, res] = await errorCaptured(asyncFunc, 1, 2, 3); if (res) { console.log("success:", res); } if (err) { console.log("error:", err); } })();await-to-jsawait-to-js 是作者 scopsy 运用 Go-lang 处理异常的灵感源码是用 ts 写的,短小精悍await-to-js ts 源码export function to<T, U = Error>( promise: Promise<T>, errorExt?: object ): Promise<[U | null, T | undefined]> { return promise .then<[null, T]>((data: T) => [null, data]) .catch<[U, undefined]>((err: U) => { if (errorExt) { Object.assign(err, errorExt); } return [err, undefined]; }); } export default to;await-to-js js 代码/** * @param { Promise } promise * @param { Object= } errorExt - Additional Information you can pass to the err object * @return { Promise } */ function to(promise, errorExt) { return promise .then(function(data) { return [null, data]; }) .catch(function(err) { if (errorExt) { Object.assign(err, errorExt); } return [err, undefined]; }); }重新看我的例子function to(promise, errorExt) { return promise .then(data => [null, data]) .catch(err => { if (errorExt) { Object.assign(err, errorExt); } return [err, undefined]; }); } // 一个示例异步函数 let asyncFunc = (a, b, c) => { return new Promise((resolve, reject) => { const num = Math.random().toFixed(1); if (num > 0.5) { resolve({ a, b, c }); } else { reject({ a, b, c }); } }); }; // 运行 (async () => { let [err, res] = await to(asyncFunc(1, 2, 3), { errorTxt: "这是一个错误" }); if (res) { console.log("success:", res); } if (err) { console.log("error:", err); } })();总结两种方法都可以,都是 Go-lang 异常处理的灵感,就性能来说 有一种说法说过多的try catch 影响性能。不过现在已经没啥影响,两种方法都可以!
2019年06月04日
3,382 阅读
1 评论
0 点赞
2019-05-31
自建基于掘金API的图床
掘金改版 暂时不可用前言一直以来,微博图床是很多人的不二选择,图床好处就不说了。只是前段时间开始,微博图床的图 403 好多都不能访问,可能开了防盗链。最近一直在找替代的图床,本来想用picGo 可是是个桌面端 对我不是很方便 ,又找了 语雀 的替代方案 但还是比较麻烦。发现掘金的图片上传有点小操作 ?,花了差不多 2 3 个小时,做了一个基于掘金的图片上传 api的替代图床。关于本图床基于https://sm.ms网站的样式进行了修改及重制,按照掘金 API 修改了上传参数.基本满足了需求 但是 Delete Link 因为掘金没有,所以也就无效了 总体不影响使用上传服务会因为掘金调整可能随时会挂 ,但上传完成的图片肯定在!地址--注意仅供学习之用,请勿商用和上传违反中国大陆和香港法律的图片,违者后果自负
2019年05月31日
665 阅读
0 评论
0 点赞
2019-02-20
从一道题浅说 JavaScript 的事件循环
任务队列首先我们需要明白以下几件事情:JS 分为同步任务和异步任务同步任务都在主线程上执行,形成一个执行栈主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。一旦执行栈中的所有同步任务执行完毕(此时 JS 引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。根据规范,事件循环是通过任务队列的机制来进行协调的。一个 Event Loop 中,可以有一个或者多个任务队列(task queue),一个任务队列便是一系列有序任务(task)的集合;每个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不同源来的则被添加到不同队列。 setTimeout/Promise 等 API 便是任务源,而进入任务队列的是他们指定的具体执行任务。宏任务(macro)task(又称之为宏任务),可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。浏览器为了能够使得 JS 内部(macro)task 与 DOM 任务能够有序的执行,会在一个(macro)task 执行结束后,在下一个(macro)task 执行开始前,对页面进行重新渲染,流程如下:(macro)task->渲染->(macro)task->...(macro)task 主要包含:script(整体代码)、setTimeout、setInterval、I/O、UI 交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)微任务microtask(又称为微任务),可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前 task 任务后,下一个 task 之前,在渲染之前。所以它的响应速度相比 setTimeout(setTimeout 是 task)会更快,因为无需等渲染。也就是说,在某一个 macrotask 执行完后,就会将在它执行期间产生的所有 microtask 都执行完毕(在渲染前)。microtask 主要包含:Promise.then、MutaionObserver、process.nextTick(Node.js 环境)运行机制在事件循环中,每进行一次循环操作称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤如下:执行一个宏任务(栈中没有就从事件队列中获取)执行过程中如果遇到微任务,就将它添加到微任务的任务队列中宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)当前宏任务执行完毕,开始检查渲染,然后 GUI 线程接管渲染渲染完毕后,JS 线程继续接管,开始下一个宏任务(从事件队列中获取)流程图如下:Promise 和 async 中的立即执行我们知道 Promise 中的异步体现在then和catch中,所以写在 Promise 中的代码是被当做同步任务立即执行的。而在 async/await 中,在出现 await 出现之前,其中的代码也是立即执行的。那么出现了 await 时候发生了什么呢?await 做了什么从字面意思上看 await 就是等待,await 等待的是一个表达式,这个表达式的返回值可以是一个 promise 对象也可以是其他值。很多人以为 await 会一直等待之后的表达式执行完之后才会继续执行后面的代码,实际上 await 是一个让出线程的标志。await 后面的表达式会先执行一遍,将 await 后面的代码加入到 microtask 中,然后就会跳出整个 async 函数来执行后面的代码。这里感谢@chenjigeng 的纠正:由于因为 async await 本身就是 promise+generator 的语法糖。所以 await 后面的代码是 microtask。所以对于本题中的async function async1() { console.log("async1 start"); await async2(); console.log("async1 end"); }等价于async function async1() { console.log("async1 start"); Promise.resolve(async2()).then(() => { console.log("async1 end"); }); }回到本题以上就本道题涉及到的所有相关知识点了,下面我们再回到这道题来一步一步看看怎么回事儿。首先,事件循环从宏任务(macrotask)队列开始,这个时候,宏任务队列中,只有一个 script(整体代码)任务;当遇到任务源(task source)时,则会先分发任务到对应的任务队列中去。所以,上面例子的第一步执行如下图所示:然后我们看到首先定义了两个 async 函数,接着往下看,然后遇到了 console 语句,直接输出 script start。输出之后,script 任务继续往下执行,遇到 setTimeout,其作为一个宏任务源,则会先将其任务分发到对应的队列中:script 任务继续往下执行,执行了 async1()函数,前面讲过 async 函数中在 await 之前的代码是立即执行的,所以会立即输出async1 start。遇到了 await 时,会将 await 后面的表达式执行一遍,所以就紧接着输出async2,然后将 await 后面的代码也就是console.log('async1 end')加入到 microtask 中的 Promise 队列中,接着跳出 async1 函数来执行后面的代码。script 任务继续往下执行,遇到 Promise 实例。由于 Promise 中的函数是立即执行的,而后续的 .then 则会被分发到 microtask 的 Promise 队列中去。所以会先输出 promise1,然后执行 resolve,将 promise2 分配到对应队列。script 任务继续往下执行,最后只有一句输出了 script end,至此,全局任务就执行完毕了。根据上述,每次执行完一个宏任务之后,会去检查是否存在 Microtasks;如果有,则执行 Microtasks 直至清空 Microtask Queue。因而在 script 任务执行完毕之后,开始查找清空微任务队列。此时,微任务中, Promise 队列有的两个任务async1 end和promise2,因此按先后顺序输出 async1 end,promise2。当所有的 Microtasks 执行完毕之后,表示第一轮的循环就结束了。第二轮循环开始,这个时候就会跳回 async1 函数中执行后面的代码,然后遇到了同步任务 console 语句,直接输出 async1 end。这样第二轮的循环就结束了。(也可以理解为被加入到 script 任务队列中,所以会先与 setTimeout 队列执行)第二轮循环依旧从宏任务队列开始。此时宏任务中只有一个 setTimeout,取出直接输出即可,至此整个流程结束。下面我会改变一下代码来加深印象。变式一在第一个变式中我将 async2 中的函数也变成了 Promise 函数,代码如下:async function async1() { console.log("async1 start"); await async2(); console.log("async1 end"); } async function async2() { //async2做出如下更改: new Promise(function(resolve) { console.log("promise1"); resolve(); }).then(function() { console.log("promise2"); }); } console.log("script start"); setTimeout(function() { console.log("setTimeout"); }, 0); async1(); new Promise(function(resolve) { console.log("promise3"); resolve(); }).then(function() { console.log("promise4"); }); console.log("script end");可以先自己看看输出顺序会是什么,下面来公布结果:script start async1 start promise1 promise3 script end promise2 async1 end promise4 setTimeout在第一次 macrotask 执行完之后,也就是输出script end之后,会去清理所有 microtask。所以会相继输出promise2,async1 end ,promise4,其余不再多说。变式二在第二个变式中,我将 async1 中 await 后面的代码和 async2 的代码都改为异步的,代码如下:async function async1() { console.log("async1 start"); await async2(); //更改如下: setTimeout(function() { console.log("setTimeout1"); }, 0); } async function async2() { //更改如下: setTimeout(function() { console.log("setTimeout2"); }, 0); } console.log("script start"); setTimeout(function() { console.log("setTimeout3"); }, 0); async1(); new Promise(function(resolve) { console.log("promise1"); resolve(); }).then(function() { console.log("promise2"); }); console.log("script end");可以先自己看看输出顺序会是什么,下面来公布结果:script start async1 start promise1 script end promise2 setTimeout3 setTimeout2 setTimeout1在输出为promise2之后,接下来会按照加入 setTimeout 队列的顺序来依次输出,通过代码我们可以看到加入顺序为3 2 1,所以会按 3,2,1 的顺序来输出。变式三变式三是我在一篇面经中看到的原题,整体来说大同小异,代码如下:async function a1() { console.log("a1 start"); await a2(); console.log("a1 end"); } async function a2() { console.log("a2"); } console.log("script start"); setTimeout(() => { console.log("setTimeout"); }, 0); Promise.resolve().then(() => { console.log("promise1"); }); a1(); let promise2 = new Promise(resolve => { resolve("promise2.then"); console.log("promise2"); }); promise2.then(res => { console.log(res); Promise.resolve().then(() => { console.log("promise3"); }); }); console.log("script end");无非是在微任务那块儿做点文章,前面的内容如果你都看懂了的话这道题一定没问题的,结果如下:script start a1 start a2 promise2 script end promise1 a1 end promise2.then promise3 setTimeout最后原文:https://github.com/Advanced-Frontend/Daily-Interview-Question/issues/7
2019年02月20日
1,929 阅读
0 评论
0 点赞
2019-02-19
Javascript ES2019中的8个新特性
前言JavaScript 不断改进和添加更多功能。TC39 已经完成并批准了 ES2019 的这 8 个功能,它有 4 个阶段,这些阶段是:Stage 0: StrawmanStage 1: ProposalsStage 2: DraftsStage 3: CandidatesStage 4: Finished/Approved以下链接可以查看Stage 0,Stage 1 – 3 和Final Stage可选的 Catch 绑定能够在不使用 catch 绑定的地方选择性地删除它try { // trying to use a new ES2019 feature // which may not be implemented in other browsers } catch (unused) { // revert back to old way }现在可以删除未使用的绑定try { ... } catch { ... }JSON 超集此提议的动机是 JSON 字符串可以包含未转义的 U + 2028 LINE SEPARATOR 和 U + 2029 PARAGRAPH SEPARATOR 字符,而 ECMAScript 字符串则不能。在 ES2019 之前,它会产生错误SyntaxError: Invalid or unexpected tokenconst LS = eval('"\u2028"'); const PS = eval("'\u2029'");符号说明在 ES2015 中引入符号,具有非常独特的功能。在 ES2019 中,它现在可以提供给定的描述。其目的是避免间接获得所提供的描述Symbol.prototype.toStringconst mySymbol = Symbol("myDescription"); console.log(mySymbol); // Symbol(myDescription) console.log(mySymbol.toString()); // Symbol(myDescription) console.log(mySymbol.description); // myDescriptionFunction.prototype.toString - 修订版我们之前已经在函数原型中使用了toString方法,但是在 ES2019 中它已被修改并包含函数内的注释,请注意它在Arrow Functions上不起作用。function /* comment */ foo /* another comment */() {} // Before console.log(foo.toString()); // function foo(){} // Now ES2019 console.log(foo.toString()); // function /* comment */ foo /* another comment */ (){} // Arrow Syntax const bar /* comment */ = /* another comment */ () => {}; console.log(bar.toString()); // () => {}Object.fromEntries它是 Object.entries 的反向方法,它也是克隆对象的方法之一const obj = { prop1: 1, prop2: 2 }; const entries = Object.entries(obj); console.log(entries); // [ [ 'prop1', 1 ], [ 'prop2', 2 ] ] const fromEntries = Object.fromEntries(entries); console.log(fromEntries); // Object { prop1: 1, prop2: 2 } console.log(obj === fromEntries); // false注意:任何嵌入式对象/数组都只是通过引用复制。格式良好的 JSON.stringify这也是由同一个人提出的,并且与 JSON 超集特征有关 。ES2019 不是将未配对的代理代码点作为单个 UTF-16 代码单元返回,而是用 JSON 转义序列表示它们// Before console.log(JSON.stringify("\uD800")); // "�" // Now ES2019 console.log(JSON.stringify("\uD800")); // "\ud800"String.prototype trimStart 和 trimEnd我们已经在 String 原型中使用了trim方法,它删除了字符串开头和结尾之间的空格。但是现在开始介绍 ES2019 的 trimStart和trimEnd// Trim const name = " Codedam "; console.log(name.trim()); // "Codedam" // Trim Start const description = " Unlocks Secret Codes "; console.log(description.trimStart()); // "Unlocks Secret Codes " // Trim End const category = " JavaScript "; console.log(category.trimEnd()); // " JavaScript"Array.prototype flat 和 flatMapflat方法创建一个新数组,所有子数组元素以递归方式连接到指定的深度。 默认情况下,深度为 1,使数组上第一层嵌套数组变平。const arr = [1, 2, [3, 4, [5, 6]]]; arr.flat(); // [1, 2, 3, 4, [5, 6]] arr.flat(2); // [1, 2, 3, 4, 5, 6] // You can use Infinity to flatten all the nested arrays no matter how deep the array is const arrExtreme = [1, [2, [3, [4, [5, 6, 7, [8, 9]]]]]]; arrExtreme.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8, 9]flatMap 类似于 flat 并且与 map 相关,其中它映射数组然后将其展平const arr = ["Codedam", "is Awsome", "!"]; const mapResult = arr.map(item => item.split(" ")); console.log(mapResult); // [ [ 'Codedam' ], [ 'is', 'Awsome' ], [ '!' ] ] const flatMapResult = arr.flatMap(chunk => chunk.split(" ")); console.log(flatMapResult); // ['Codedam', 'is', 'Awsome', '!'];其他强调一下现在 Stage 3 中的一些有用的即将推出的功能。globalThisBigIntimport()Legacy RegExPrivate instance methods and accessorsString.prototype.matchAll最后原文:8 NEW FEATURES in JavaScript ES2019作者:Rienz
2019年02月19日
1,105 阅读
0 评论
0 点赞
1
...
7
8
9
10