!!!注意!!! style 标签里要把 scoped 属性去掉,这样才能继承到子组件,否则自定义样式不会生效

html

<swiper :options="swiperOption">
<swiper-slide v-for="(EmojiList,index) in EmojiLists" class="swiper_slide">
<img v-for="(Emoji,subIndex) in EmojiList" :src="Emoji.url">
</swiper-slide>
<div class="swiper-pagination" slot="pagination"></div>
</swiper>
Javascript

<script>
export default {
data() {
return {
swiperOption: {
pagination: {
el: '.swiper-pagination',
clickable: true,
bulletClass: 'my-bullet', // 分页器每一项class
bulletActiveClass: 'my-bullet-active', // 当前选中的class
}
},
}
}
}
</script>
css
<style lang="less">
.swiper-pagination {
.my-bullet {
height: 17px;
width: 17px;
background: #F2F4F6;
border: 1px solid #192A51;
box-sizing: border-box;
display: inline-block;
border-radius: 50%;
margin-right: 8px;

&:last-child {
margin-right: 0;
}
}

.my-bullet-active {
background: #26A9ED;
}
}
</style>
效果图

html
<input onchange="handleChangeInputFile" type="file" accept="image/*">
Javascript
function handleChangeInputFile(e) {
// input调用原声相册后获取到文件对象
let file = this.$refs.InputFile.files[0];

//https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
let reader = new FileReader()
let filename = file.name.replace(/\.(.*)/g, '')
let self = this;
reader.readAsDataURL(file)
reader.onload = function (e) {
// this.result 就是图片的base64
self.uploadBase64Image(this.result, filename).then(url => {
self.$emit('handleAddImage', url, 'photo');
})
}
}

HTML
<div class="textarea" contenteditable="true" placeholder="请输入补充说明"></div>
CSS
.textarea {
width: 100%;
min-height: 210px;

&:empty:before {
content: attr(placeholder); //设置attr属性,使placeholder属性生效
color: rgba(170, 159, 159, 1);
}
&:focus:before{
content:none;
}
}

浏览器

html

  • 减少DOM数量

  • 不要在HTML中拉伸图片 (会重新绘制)

css

  • 避免重复定义CSS,CSS覆盖等
  • 不使用CSS表达式

javascript

  • 减少DOM访问(避免重绘 和 回流)
  • 合理设计事件监听器
  • 避免 DOM 阻塞 (JavaScript放在最后面,或者加上defer 标签)

资源

  • 静态资源压缩,使用CDN

缓存

  • 客户端缓存
  • 服务端缓存
  • http 缓存

网络Http

  • 减少 HTTP 请求数量(合并文件、CSS精灵 (图片合成)、inline Image)
  • 减少 HTTP 请求大小
  • 添加Expires或者Cache-Control响应头

服务器

  • nginx 开启 gzip 压缩
  • 减小cookie大小
  • 引入资源的域名不要包含cookie

content方面

  • 减少HTTP请求:合并文件、CSS精灵 (图片合成)、inline Image
  • 减少DNS查询:DNS查询完成之前浏览器不能从这个主机下载任何任何文件。方法:DNS缓存、将资源分布到恰当数量的主机名,平衡并行下载和DNS查询
  • 避免重定向:多余的中间访问
  • 使Ajax可缓存
  • 非必须组件延迟加载 ( 插件,组件,图片等懒加载同理 )
  • 未来所需组件预加载
  • 减少DOM元素数量
  • 将资源放到不同的域下:浏览器同时从一个域下载资源的数目有限,增加域可以提高并行下载量
  • 减少iframe数量
  • 避免404

Server方面

  • 使用CDN
  • 配置ETag
  • Flush Buffer Early
  • Ajax使用GET进行请求

常见的请求头(request)字段含义:

字段 含义
Accept 浏览器可接受的MIME类型
Accept-Charset 浏览器可接受的字符集
Accept-Encoding 浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间。
Accept-Language 浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。
Authorization 授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中。
Content-Length 表示请求消息正文的长度
Host 主机名
If-Modified-Since 资源的缓存时间。只有当所请求的内容在指定的时间后又经过修改才返回它,否则返回304“Not Modified”应答
Referer 包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
User-Agent User-Agent头域的内容包含发出请求的用户信息。浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用
Cookie 客户机通过这个头可以向服务器带数据,这是最重要的请求头信息之一。
Connection 处理完这次请求后是否断开连接还是继续保持连接 (HTTP 1.1默认进行持久连接 . ‘keep-alive’)

常见的响应头字段含义

字段 含义
Allow 服务器支持哪些请求方法(如GET、POST等)。
Content-Encoding 文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE4、IE5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader(“Accept- Encoding”))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。
Content-Length 表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入 ByteArrayOutputStram,完成后查看其大小,然后把该值放入Content-Length头,最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容
Content- Type 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置 Content-Type,因此HttpServletResponse提供了一个专用的方法setContentType。
Date 当前的GMT时间,例如,Date:Mon,31Dec200104:25:57GMT。Date描述的时间表示世界标准时,换算成本地时间,需要知道用户所在的时区。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦
Expires 告诉浏览器把回送的资源缓存多长时间,-1或0则是不缓存
Last-Modified 文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified)状态。Last-Modified也可用setDateHeader方法来设置。
Location 这个头配合302状态码使用,用于重定向接收者到一个新URI地址。表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。
Refresh 告诉浏览器隔多久刷新一次,以秒计
Server 服务器通过这个头告诉浏览器服务器的类型。Server响应头包含处理请求的原始服务器的软件信息。此域能包含多个产品标识和注释,产品标识一般按照重要性排序。Servlet一般不设置这个值,而是由Web服务器自己设置。
Set-Cookie 设置和页面关联的Cookie。Servlet不应使用response.setHeader(“Set-Cookie”, …),而是应使用HttpServletResponse提供的专用方法addCookie
Transfer-Encoding 告诉浏览器数据的传送格式
WWW-Authenticate 客户应该在Authorization头中提供什么类型的授权信息?在包含401(Unauthorized)状态行的应答中这个头是必需的。例如,response.setHeader(“WWW-Authenticate”, “BASIC realm=\”executives\”“)。注意Servlet一般不进行这方面的处理,而是让Web服务器的专门机制来控制受密码保护页面的访问

准备工作

  • 安装Git

  • 安装Node

  • 安装Hexo-cli

    • npm install -g hexo-cli

初始化项目

hexo init <项目名称>

新建完成后会生成以下目录

目录名称 主要内容
node_modules 项目依赖文件
public 存放生成的页面
scaffolds 生成文章的一些模板
source 用来存放你的文章
themes 主题
_config.yml 博客的配置文件

Hexo 常用命令

hexo new "postName" #新建文章
hexo new page "pageName" #新建页面
hexo generate #生成静态页面至public目录
hexo server #开启预览访问端口(默认端口4000,'ctrl + c'关闭server)
hexo deploy #将.deploy目录部署到GitHub
hexo help # 查看帮助
hexo version #查看Hexo的版本
hexo deploy -g #生成加部署
hexo server -g #生成加预览
hexo clean # 清理多余文件
命令的简写
hexo n == hexo new
hexo g == hexo generate
hexo s == hexo server
hexo d == hexo deploy

hexo g -d #一键部署

localStorage有效期为永久,sessionStorage有效期为顶层窗口关闭前

同源文档可以读取并修改localStorage数据,sessionStorage只允许同一个窗口下的文档访问,如通过iframe引入的同源文档。

共同点

  • 存储大小均为5M左右(不同浏览器之间存在差异)
  • 都有同源策略限制
  • 仅在客户端中保存,不参与和服务器的通信

不同点

  • 生命周期
    • localStorage理论上来说是永久有效的,即不主动清空的话就不会消失,即使保存的数据超出了浏览器所规定的大小,也不会把旧数据清空而只会报错。但需要注意的是,在移动设备上的浏览器或各Native App用到的WebView里,localStorage都是不可靠的,可能会因为各种原因(比如说退出App、网络切换、内存不足等原因)被清空。
    • sessionStorage: 与存储数据的脚本所在的标签页的有效期是相同的。一旦窗口或者标签页被关闭,那么所有通过 sessionStorage 存储的数据也会被删除。
  • 作用域
    • localStorage只要在相同的协议、相同的主机名、相同的端口下,就能读取/修改到同一份localStorage数据。
    • sessionStoragelocalStorage更严苛一点,除了协议、主机名、端口外,还要求在同一窗口(也就是浏览器的标签页)下。

这些图可以更好的帮助理解

数据结构

localstorage为标准的键值对(Key-Value,简称KV)数据类型,简单但也易扩展,只要以某种编码方式把想要存储进localstorage的对象给转化成字符串,就能轻松支持。举点例子:把对象转换成json字符串,就能让存储对象了;把图片转换成DataUrl(base64),就可以存储图片了。另外对于键值对数据类型来说,“键是唯一的”这个特性也是相当重要的,重复以同一个键来赋值的话,会覆盖上次的值。

浏览器兼容性

注意!!! 在ios设备上无法重复setItem(),需要在setItem 前 removeItem()

查看浏览器兼容情况 ☞ MDN

通常在请求拦截器中可以做 Loading加载动画,token过期验证,登陆验证等等…

响应拦截器中可以做 统一报错信息处理

import Axios from 'axios';
import router from '../router';
import { Message } from 'element-ui';

Axios.defaults.baseURL = process.env.VUE_APP_BASE_API; // 设置基础路径
Axios.defaults.timeout = 5000; // 设置请求超时时间

Axios.interceptors.request.use(
config => {
let token = JSON.parse(localStorage.getItem('token') || null);
if (token) {
config.headers['secretKey'] = token.secretKey;
config.headers['timestamp'] = token.timestamp;
config.headers['uuid'] = token.uuid;
}
return config;
},
err => {
console.log(err);
}
);

// response 响应拦截器
Axios.interceptors.response.use(
res => {
// 401 您还未登录,或登录信息已过期
if (res.data.status_code !== 200) {
Message({
message: res.data.message,
type: 'error'
});

if (res.data.status_code === 401) {
router.push({ path: '/login' });
}
}
return res.data;
},
err => {
console.log(err);
// 超时处理
let originalRequest = err.config;

console.log(err.message); //timeout of 100ms exceeded

if (err.code === 'ECONNABORTED' && err.message.indexOf('timeout') !== -1 && !originalRequest._retry) {

originalRequest._retry = true
Message.error('请求超时,尝试重新请求中...')

// 重新发起请求
return Axios.request(originalRequest);
}

}
);

  • 🎨 - 改进结构和代码格式
  • ⚡️ - 优化性能
  • 🔥 - 移除代码或文件
  • 🐛 - 修复 bug
  • ✨ - 引入新功能
  • 🍎 - 修复 MacOS 下的问题
  • 📝 - 写文档
  • 🚀 - 部署新功能
  • ✅ - 添加测试用例
  • 🔖 - 发版/版本标签
  • 🔒 - 修复安全问题
  • 🐧 - 修复 Linux 下的问题
  • 🚨 - 移除 linter 的警告
  • 🚧 - 工作在进行中
  • 💚 - 修复 CI 构建问题
  • ⬇️ - 降级依赖库
  • 🏁 - 修复 Windows 下的问题
  • ⬆️ - 升级依赖库
  • 👷 - 添加 CI 构建系统
  • 🔧 - 改变配置文件
  • 🔨 - 大重构
  • 🎉 - 初次提交
  • 💄 - 升级 UI 和样式文件

/**
* 日期格式化
* @param stamp { Date } Date 实例
* @param format { string }
* @return { string }
*/
export const dateFormat = (stamp = new Date(), format = 'YYYY-MM-DD hh:mm:ss') => {
const d = new Date(stamp);
const month = d.getMonth() + 1;
const date = d.getDate();
const hours = d.getHours();
const minutes = d.getMinutes();
const seconds = d.getSeconds();
const numberFormat = (number) => number < 10 ? `0${number}` : number;

format = format.replace(/yyyy|YYYY/, d.getFullYear());
format = format.replace(/yy|YY/, d.getYear());
format = format.replace(/MM/, numberFormat(month));
format = format.replace(/M/, month);
format = format.replace(/dd|DD/, numberFormat(date));
format = format.replace(/d|D/, date);
format = format.replace(/hh|HH/, numberFormat(hours));
format = format.replace(/h|H/, hours);
format = format.replace(/mm/, numberFormat(minutes));
format = format.replace(/m/, minutes);
format = format.replace(/ss/, numberFormat(seconds));
format = format.replace(/s/, seconds);
return format;
}
0%