1. pdf文件下载
需求:服务端返回blob二进制流,前端实现pdf打印操作
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/axios/0.20.0-0/axios.min.js"></script>
</head>
<body>
<div>
<button onclick="handleDwonLoadPdf()">下载pdf</button>
</div>
<script>
window.onload = function(){
function handleDwonLoadPdf() {
let data = {
pageSize: 10,
sportId: 'sr:sport:1',
producer: 3,
country: 3
}
axios({
method: 'get,
url: 'http://127.0.0.1:9090/api/xxx/print/download',
params: data,
responseType: 'blob'
}).then(res=>{
let fileName = res.headers['content-disposition'].split('=')[1];
downloadFile(res.data, fileName);
})
}
function downloadFile(data,fileName){
if(!data) return;
let url = window.URL.createObjectURL(new Blob([data],{type: 'application/pdf'}));
const a = document.createElement('a');
a.style.display = 'none';
a.download = fileName;
a.href = url;
a.click();
if(document.body.contains(a)){
document.body.removeChild(a);
}
}
}
</script>
</body>
</html>
2. 1px边框
需求:解决移动端1px边框显示异常问题
div {
height:1px;
background:#000;
-webkit-transform: scaleY(0.5);
-webkit-transform-origin:0 0;
overflow: hidden;
}
/* 2倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 2.0) {
.border-bottom::after {
-webkit-transform: scaleY(0.5);
transform: scaleY(0.5);
}
}
/* 3倍屏 */
@media only screen and (-webkit-min-device-pixel-ratio: 3.0) {
.border-bottom::after {
-webkit-transform: scaleY(0.33);
transform: scaleY(0.33);
}
}
3. oninput事件和onchange事件区别
需求:搜索功能,失去焦点时等待2s自动进行搜索
<body>
<input type="text" onchange="console.log(this.value);" />
</body>
<script>
/*
事件对比:
oninput事件:在域内容更改时触发(严格是用户输入时触发,事件会频繁调用)
onchange事件:在域内容更改时触发,元素失去焦点时触发
onChange触发条件:
当input博活到焦点后,系统储存当前值;当input焦点离开后,帕努单当前值与之前存储的值是否不等,若为true则触发onchange事件
*/
</script>
4. html打印设置页边距
虽然css控制了html的margin,padding为0,但打印时会默认为页面添加页边距margin值
@page {
margin:1.0in .75in 1.0in .75in; /* 设置页边距 */
mso-header-margin:.5in; /* 设置页眉高度 */
mso-footer-margin:.5in; /* 设置页脚高度 */
mso-page-orientation:landscape; /* 设置打印方向 */
}
5. js按钮防止重复点击、防止点击过快
防止重复点击可设置开关,点击后重置开关
<body>
<button onClick="click()"></button>
</body>
<script>
var isloading = false;
function click(){
if(isloading) return;
if(!isloading) {
isloading = true;
// 指向操作
// 操作结束
isloading = false;
}
}
</script>
6. 理解单向数据流和双向绑定
单项数据流(子组件只能接受父组件数据,却不能直接修改该数据)和双向绑定(对数据的监听)没有任何关系
tips:
+ vue, react都属于单向数据流,vue包含双向绑定,但react需要通过setState()才能修改数据,不同于vue 直接修改data的数据,即可触发视图更新
+ v-show、 v-if、 {{}}、 v-model 都属于双向绑定
+ {} react插值表达式也属于双向绑定
7. new绑定
new操作符可以实现改变this指向,及操作:
- 创建一个新对象,将this绑定到新创建的对象
- 使用传入的参数调用构造函数
- 将创建的对象的_propto_指向构造函数的prototype
- 如果构造函数没有显式返回一个对象,则返回创建的新对象(即this对应的对象),否则返回返回显式对象
function Foo(name) {
this.name=name;
}
let person = new Foo('xiaowang');
console.log(person); // Foo {name: "xiaowang"}
// 显示返回对象
function NewFoo(name) {
this.name = name;
return {
color: name
}
}
let newFoo = new NewFoo('red');
console.log(newFoo); // {color: "xiaowang"}
8. 节流函数优化scroll事件
直接绑定scroll事件,会触发比较频繁,需要使用节流延迟执行回调方法
- 节流函数
function throttle(fn, delay) {
let timer, prevTime;
return function(...args) {
const currTime = Date.now();
const context = this;
if(!prevTime) prevTime = currTime;
clearTimeout(timer);
if(currTime - prevTime > currTime) {
prevTime = currTime;
fn.apply(context, args);
clearTimeout(timer);
return;
}
timer = setTimeout(function() {
prevTime = Date.now();
timer = null;
fn.apply(context, args);
}, delay);
}
}
- scroll调用
window.addEventListener('scroll', throttle(lazyload,200));
9. promise
一个promise有以下几种状态:
- pending: 初始状态,既不是成功,也不是失败状态
- fulfilled: 意味着操作成功完成。状态:pending => fulfilled
- rejected: 意味着操作失败。状态:pending => rejected
var p1 = new Promise((resolve,reject)=>{});
console.log(p1); // pending
var p2 = new Promise((resolve,reject)=>{
resolve('成功');
});
console.log(p2); // fulfilled
var p3 = new Promise((resolve,reject)=>{
reject('失败');
});
console.log(p3); // reject
注意 :promise状态是不可逆的
new Promise((resolve,reject)=>{
var num = 100;
resolve(num);
num = 999;
resolve(num); // resolve 也不会改变已传出去的num 100
console.log(num); // 999
}).then(result => {
console.log(result); // 100
});
10. Generator(ES6)
介绍:
- Generator函数式一个状态机,封装了多个内部状态
- 执行Generator函数会返回一个遍历器对象,可以一次遍历Generator函数内部的每一个状态
- Generator函数只有调用next()方法才会遍历下一个内部状态
关键标识和关键字
- function*: 关键标识
- yield: 暂停执行
- yield*: 语法糖,在Generator函数中指向另一个Generator函数
Generator.prototype.next() // 返回一个yield表达式生成的值
Generator.prototype.return() // 返回给定的值并结束生成器
Generator.prototype.throw() // 向生成器抛出一个错误
yield表达式
- yield 表示暂停指向
- yield表达式后面的表达式,只有当调用next()、内部指针指向该语句时才会执行
- yield表达式的值会作为返回的对象的value属性值
- 调用next()之前,yield前面的语句不会执行
function* hellWorldGenerator() {
console.log('aaa')
yield 'hello';
console.log('bbb');
yield 'world';
console.log('ccc')
return 'ending';
}
var hw = hellWorldGenerator();
console.log(hw); // helloWorldGenerator {<suspended>} 状态: suspended
hw.next(); // aaa {value: 'hello', done: false}
hw.next(); // bbb { value: 'world', done: false }
hw.next(); // ccc { value: 'ending', done: true }
hw.next(); // { value: undefined, done: true }
console.log(hw); // helloWorldGenerator {<closed>} 状态:closed
【补充】js异步请求发展进化:callback -> promise -> generator -> async + await (async/await 的实现,就是将 Generator 函数和自动执行器,包装在一个函数里)
async/await的优点
-
async/await 相对于Promise,优点是处理 then 的调用链,能够更清晰准确的写出代码并且也能优雅地解决回调地狱问题
-
async/await 对 Generator 函数的改进,体现在:
- 内置执行器
- 更好的语义
- 更广的适用性
- 返回值是 Promise 对象
async/await的缺点
如果多个异步代码没有依赖性却使用了 await 会导致性能上的降低。代码没有依赖性的话,可以使用 Promise.all 的方式替代
11. vue相同路由切换组件时无效
触发原因: xx/detail/1切换到xx/detail/2,组件不变,因为vue-rotuer会识别这连个路由使用相同的组件进行复用,并不会创建组件,因此组件的生命周期钩子不会触发
<!--router-view配置不同的key即可触发视图重绘 -->
<router-view :key="$route.fullPath"></router-view>
12. storage封装调用
- storage封装
// storage.js
const storageMap = new Map();
export default function createStorageModel(key, storage = localStorage) {
// 相同key返回单例
if (storageMap.has(key)) {
return storageMap.get(key);
}
const model = {
key,
set(val) {
storage.setItem(this.key, JSON.stringify(val));
},
get() {
let val = storage.getItem(this.key);
return val && JSON.parse(val);
},
remove() {
storage.removeItem(this.key);
}
};
storageMap.set(key, model);
return model;
}
- 缓存数据枚举
// cacheModel.js
import createStorageModel from '@/utils/storage.js';
// 列举出所有需要缓存操作的变量
export const themeModel = createStorageModel('local_theme',localStorage);
export const utcSourceModel = createStorageModel('utm_source',sessionStorage);
- 缓存数据调用
import {themeModel,utcSourceModel} from '@/utils/cacheModel.js';
// 数据写入
themeModel.set({test: '123'}); // 设置数据
utmSourceModel.set({time: 12321123});
// 数据读取
let themeData = themeModel.get(); // 读取数据
let utmSourceData = utmSourceModel.get();
console.log(themeData, '读取数据');
console.log(utmSourceData, '读取数据');
13. 获取当前时区
// 获取当地时区
export function currentTimeRegion() {
let nowTime = Number(moment().format('HH'));
let utcTime = Number(moment.utc().format('HH'));
return nowTime - utcTime > 0 ? `+${nowTime - utcTime}` : `${nowTime - utcTime}`.toString();
}
14. (typeof)判断数据类型
typeof undefined === 'undefined' // true
typeof true === 'boolean' // true
typeof 42 === 'number' // true
typeof '42' === 'string' // true
let obj = {life: 42};
typeof obj === 'object' && obj && !obj.length // true
let arr = [1,2,3];
typeof arr === 'object' && arr && arr.length // true
typeof Symbol() === 'symbol' // true
let a = null;
(!a && typeof a) === 'object' // true
typeof function a(){} === 'function' // true
14. 构造函数 vs class
1. 私有属性(方法)和静态属性(方法)
- 构造函数写法
// 构造函数写法
function Person(name, age) {
let _selfName = '123456'; // 构造函数私有属性,无法被外部直接访问
let _selfFn = () => { // 构造函数私有方法,无法被外部直接访问
return _selfName;
}
this.name = name; // 实例的私有属性
this.age = age;
this.sayHi = function () { // //实例的私有方法
console.log(_selfFn())
}
}
Person.prototype = { // 实例的共有方法
constructor: Person,
fn1() {
},
fn2() {
}
}
Person.prop1 = 'staticProp1'; // 构造函数的静态属性
Person.prop2 = 'staticProp2';
Person.staticFn = function () { // 构造函数的静态方法
}
const p = new Person('jack', 23);
- class写法
// class写法
class Person {
name = ''; // 这里也可以写实例的属性
age = '';
static prop1 = 'staticProp1'; // 构造函数静态属性
static prop2 = 'staticProp2';
#selfName = '123456';
#selfFn = () => { // 构造函数私有方法,无法被外部直接访问
return this.#selfName;
}
constructor(name, age) {
this.name = name; // 实例的私有属性
this.age = age;
};
fn1() { // 实例的共有方法
};
fn2() {
}
sayHi = function () {
console.log(this.#selfFn()) // 构造函数的私有方法
}
}
const p = new Person('jack', 23);
2. 继承
// 构造函数
// 1. 在子类的构造函数中,调用父类的构造函数
function Super(name) {
this.name = name;
}
function Sub(name, age) {
Super.call(this, name);
this.age = age;
}
// 2. 让子类的原型指向父类的原型,这样子类就可以继承父类原型
Super.prototype.sayHi = function () {
console.log('hi')
}
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
const p = new Sub('qiuqiu', 20);
// class写法
class Person {
name;
age;
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHi() {
console.log(this.name);
}
}
class Man extends Person {
constructor(name, age, prop) {
super(name, age);
this.prop = prop;
}
}
const a = new Man('qiuqiu', 20, 'shuai');
15. swiper一屏显示3个半
<!--
显示n个半,需要以下步骤:
1. slidesPerView: "auto"
2. 设置swiper-slide尺寸为百分比 (如 width: 18.77%;)
-->
<!-- 模板结构 -->
<template>
<swiper :options="swiperOption" class="swiper-tab" id="sportSideMenu">
<swiper-slide v-for="(v,i) in sideMenu" :key="i">
<div class="swiper-item" @click="handleClickMenu({val:v,idx:i})">
<div :class="['icon',i==activeIdx?'active':'']">
<i :class="['iconfont',v.cname]"></i>
</div>
<div class="name">{{ v.name }}</div>
</div>
</swiper-slide>
</swiper>
<template>
<!-- data配置 -->
<script>
data() {
return {
swiperOption: {// swiper配置项
slidesPerView: "auto",
// spaceBetween: 60,
speed: 600
},
}
},
components: {
swiper,
swiperSlide,
},
</script>
<style scoped lang="less">
.swiper-tab {
box-sizing: border-box;
border-bottom: 2px solid #F2F3F5;
padding: 36px 0 28px 0;
/deep/ .swiper-slide {
width: 18.77%;
}
}
.swiper-item {
display: flex;
flex-direction: column;
align-items: center;
.icon {
width: 72px;
height: 72px;
line-height: 72px;
text-align: center;
border-radius: 50%;
background: #F2F3F5;
&.active {
background-color: #FFE60F;
}
.iconfont {
font-size: 40px;
color: #32333D;
}
}
.name {
font-size: 22px;
font-family: Roboto-Regular, Roboto;
font-weight: 400;
color: #32333D;
line-height: 28px;
text-align: center;
}
}
</style>
16. require引入静态图片资源添加变量
let imgSrc = 'a.jpg';
const context= require.context('../../',true,/.jpg$/);
var imageUrl = context("./"+imgSrc);
17. [] == ![]
/*
[]在比较之前会先执行[].toString() = '';
![]在比较之前转为false
*/
[] == ![]; // true