忘れられたくない

忘れられたくない 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

忘れられたくない 发布了文章 · 10月25日

组件吸顶

主要是通过浏览器事件监听函数,判断距离顶部的高度,是否符合吸顶,利用动态class属性设置样式~

window.pageYOffset:距离顶部偏移量
document.documentElement.scrollTop:谷歌浏览器获取滚动条距离顶部的位置
document.body.scrollTop:ie浏览器获取滚动条距离顶部的位置
<template>
  <div class="wrapper">
    <div class="nav-bar" :class="{'is_fixed':isFixed}">  
        这是内容
    </div>
    <div class="scroll">
        这是滚动内容
    </div>
  </div>
</template>
<script>
  export default{
    name:'nav-var',
    data(){
      return{
        isFixed:false
      }
    },
    mounted(){
      window.addEventListener('scroll',this.initHeight)
    },
    methods:{
      initHeight(){
        let scollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
        this.isFixed = scollTop > 152 ? true : false;
      }
    },
    destroyed(){
        window.removeEventListener('scroll',this.initHeight,false)
    }
  }
</script>
<style lang="scss">
.wrapper{
    .nav-bar{
      height:70px;
      line-height: 70px;
      border-top:1px solid gray;
      background: #ffffff;
      &.is_fixed{
        position: fixed;
        top:0;
        width:100%;
        box-shadow: 0 5px 5px #cccccc;
      }
    }
    .scroll{
        height:500px;
    }
}
</style>
查看原文

赞 0 收藏 0 评论 0

忘れられたくない 发布了文章 · 10月20日

Vuex的使用

  1. 新建下列目录结构

image.png

index.js文件

import Vue from 'vue'
import Vuex from 'vuex'
import mutations from './mutations'
import actions from './action'
Vue.use(Vuex);

const state = {
  username: '',//登录用户名
}
export default new Vuex.Store({
  state,
  mutations,
  actions
})

actions.js文件

/**  * 商城Vuex-actions*/

export default {
  saveUserName(context,username){
    context.commit('saveUserName', username)
  }
}

mutations.js文件

/** 
 * 商城Vuex-mutations
*/

export default{
  saveUserName(state, username) {
    state.username = username;
  }
}

在main.js中引入

import store from './store'
new Vue({
  store,
  render: h => h(App),
}).$mount('#app')

存储

//第一种方法
let username = 'jack'
this.$store.dispatch('saveUserName',username)
//第二种方法
import {mapActions} from 'vuex'
methods:{
    ...mapActions(['saveUserName']),
    setName(){
        let username = 'jack'
        this.saveUserName(username);
    }
}

获取

//第一种方法
computed:{
    username(){
        return this.$store.state.username
    }
}
//第二种方法
import { mapState } from 'vuex'
computed:{
    ...mapState(['username'])
}
查看原文

赞 1 收藏 0 评论 0

忘れられたくない 发布了文章 · 10月12日

VueCli4.0安装及项目框架的搭建

安装版本
npm install -g @vue/cli
yarn global add @vue/cli

vue --version 查看版本

如果出现下面情况
image.png

1、如果使用npm安装,使用命令npm root -g查看npm包的位置
2、如果使用yarn安装,使用命令yarn global dir查看yarn安装的位置
image.png
3、打开环境变量中的系统变量,找到path变量进行编辑添加
image.png


创建项目
vue create mall //(创建名称为mall的项目)
cd mall
yarn add axios
yarn add vue-router
yarn add vuex

在main.js文件中添加

import axios from 'axios'
import VueRouter from 'vue-router'
import router from './router.js'
import Vuex from 'vuex'

Vue.prototype.$axios = axios;
Vue.use(VueRouter)
Vue.use(Vuex)
new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

新建与main.js同级文件router.js

import Vue from 'vue'
import Router from 'vue-router'
import Home from './pages/home.vue'

Vue.use(Router);

export default new Router({
  routes:[
    {
      path:'/',
      name:'home',
      component:Home,
      redirect:'/index',
      children:[
        {
          path: 'index',
          name: 'index',
          component: Index,
        }
      ]
    }
  ]
})

安装vue-devtools拓展工具
vue-devtools官网点击打开,下载master上的代码解压进入此目录执行命令
npm install
npm run build
再通过浏览器的扩展程序进行加载shell/chrome


跨域方案

1、通过CORS进行跨域(后端修改)
2、通过jsonp进行跨域

import jsonp from 'jsonp'
jsonp('https://www.imooc.com/common/adver-getadver',(err,res)=>{
  console.log(res)
})

3、接口代理-通过修改nginx服务器配置来实现

jsonp('/api/activity/servicetime',(err,res)=>{
  console.log(res)
})

新建vue.config.js文件,与package.json同级

module.exports = {
  devServer:{
    host:"localhost",
    port:8080,
    proxy:{//事件代理
      '/api':{
        target:'https://www.imooc.com',
        changeOrigin:true,
        pathRewrite:{
          '/api':''
        }
      }
    }
  }
}
插件安装
//安装图片懒加载、element-ui、sass、轮播
yarn add vue-lazyload element-ui node-sass sass-loader vue-awesome-swiper vue-axios vue-cookie
storage封装
  1. storage本身有api,但是只是简单的key/value形式
  2. storage只存储字符串,需要手工转化成json对象
  3. storage只能一次性清空,不能单个清空
/**
 * Storage封装
 */
 
const STORAGE_KEY = 'mall';

export default {
  // 存储值
  setItem(key, value, module_name) {
    if (module_name) {
      let val = this.getItem(module_name);
      val[key] = value;
      this.setItem(module_name, val);
    } else {
      let val = this.getStorage();
      val[key] = value;
      window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify(val));
    }
  },
  // 获取某一个模块下面的属性user下面的userName
  getItem(key, module_name) {
    if (module_name) {
      let val = this.getItem(module_name);
      if (val) return val[key];
    }
    return this.getStorage()[key];
  },
  getStorage() {
    return JSON.parse(window.sessionStorage.getItem(STORAGE_KEY) || '{}');
  },
  clear(key, module_name) {
    let val = this.getStorage();
    if (module_name) {
      if (!val[module_name]) return;
      delete val[module_name][key];
    } else {
      delete val[key];
    }
    window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify(val));
  }
}
接口错误拦截
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import VueRouter from 'vue-router'
import Vuex from 'vuex'
import router from './router.js'

//根据前端的跨域方式做调整
axios.defaults.baseURL = '/api';
axios.defaults.timeout = 8000;
//接口错误拦截
axios.interceptors.response.use(function (response){
  let res = response.data;
  if(res.status == 0){//接口成功返回的状态值为 0
    return res.data
  }else if(res.status == 10){//未登录接口返回的状态值为 10
    window.location.href = '/#/login'
  }else{
    alert(res.msg);
  }
})

Vue.config.productionTip = false


Vue.use(VueRouter)
Vue.use(Vuex)
Vue.use(VueAxios, axios)

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')
查看原文

赞 0 收藏 0 评论 0

忘れられたくない 发布了文章 · 9月29日

vue项目添加C-Lodop批量打印和单个打印

请在C-Lodop官方中下载插件
1、在src/utils中添加LodopFuncs.js文件

var CreatedOKLodop7766 = null, CLodopIsLocal;

//====判断是否需要 Web打印服务CLodop:===

//===(不支持插件的浏览器版本需要用它)===

export function needCLodop() {

 try {

 var ua = navigator.userAgent;

 if (ua.match(/WindowssPhone/i))

 return true;

 if (ua.match(/iPhone|iPod|iPad/i))

 return true;

 if (ua.match(/Android/i))

 return true;

 if (ua.match(/EdgeD?d+/i))

 return true;

 var verTrident = ua.match(/TridentD?d+/i);

 var verIE = ua.match(/MSIED?d+/i);

 var verOPR = ua.match(/OPRD?d+/i);

 var verFF = ua.match(/FirefoxD?d+/i);

 var x64 = ua.match(/x64/i);

 if ((!verTrident) && (!verIE) && (x64))

 return true;

 else if (verFF) {

 verFF = verFF[0].match(/d+/);

 if ((verFF[0] >= 41) || (x64))

 return true;

 } else if (verOPR) {

 verOPR = verOPR[0].match(/d+/);

 if (verOPR[0] >= 32)

 return true;

 } else if ((!verTrident) && (!verIE)) {

 var verChrome = ua.match(/ChromeD?d+/i);

 if (verChrome) {

 verChrome = verChrome[0].match(/d+/);

 if (verChrome[0] >= 41)

 return true;

 }

 }

 return false;

 } catch (err) {

 return true;

 }

}

//====页面引用CLodop云打印必须的JS文件,用双端口(8000和18000)避免其中某个被占用:====

if (needCLodop()) {

 var src1 = "http://localhost:8000/CLodopfuncs.js?priority=1";

 var src2 = "http://localhost:18000/CLodopfuncs.js?priority=0";

 var head = document.head || document.getElementsByTagName("head")[0] || document.documentElement;

 var oscript = document.createElement("script");

 oscript.src = src1;

 head.insertBefore(oscript, head.firstChild);

 oscript = document.createElement("script");

 oscript.src = src2;

 head.insertBefore(oscript, head.firstChild);

 CLodopIsLocal = !!((src1 + src2).match(///localho|//127.0.0./i));

}

//====获取LODOP对象的主过程:====

export function getLodop(oOBJECT, oEMBED) {

 var strHtmInstall = "<br><font color='#FF00FF'>打印控件未安装!点击这里<a href='install_lodop32.exe' target='_self'>执行安装</a>,安装后请刷新页面或重新进入。</font>";

 var strHtmUpdate = "<br><font color='#FF00FF'>打印控件需要升级!点击这里<a href='install_lodop32.exe' target='_self'>执行升级</a>,升级后请重新进入。</font>";

 var strHtm64_Install = "<br><font color='#FF00FF'>打印控件未安装!点击这里<a href='install_lodop64.exe' target='_self'>执行安装</a>,安装后请刷新页面或重新进入。</font>";

 var strHtm64_Update = "<br><font color='#FF00FF'>打印控件需要升级!点击这里<a href='install_lodop64.exe' target='_self'>执行升级</a>,升级后请重新进入。</font>";

 var strHtmFireFox = "<br><br><font color='#FF00FF'>(注意:如曾安装过Lodop旧版附件npActiveXPLugin,请在【工具】->【附加组件】->【扩展】中先卸它)</font>";

 var strHtmChrome = "<br><br><font color='#FF00FF'>(如果此前正常,仅因浏览器升级或重安装而出问题,需重新执行以上安装)</font>";

 var strCLodopInstall_1 = "<br><font color='#FF00FF'>Web打印服务CLodop未安装启动,点击这里<a href='CLodop_Setup_for_Win32NT.exe' target='_self'>下载执行安装</a>";

 var strCLodopInstall_2 = "<br>(若此前已安装过,可<a href='CLodop.protocol:setup' target='_self'>点这里直接再次启动</a>)";

 var strCLodopInstall_3 = ",成功后请刷新本页面。</font>";

 var strCLodopUpdate = "<br><font color='#FF00FF'>Web打印服务CLodop需升级!点击这里<a href='CLodop_Setup_for_Win32NT.exe' target='_self'>执行升级</a>,升级后请刷新页面。</font>";

 var LODOP;

 try {

 var ua = navigator.userAgent;

 var isIE = !!(ua.match(/MSIE/i)) || !!(ua.match(/Trident/i));

 if (needCLodop()) {

 try {

 LODOP = getCLodop();

 } catch (err) { }

 if (!LODOP && document.readyState !== "complete") {

 alert("网页还没下载完毕,请稍等一下再操作.");

 return;

 }

 if (!LODOP) {

 //document.body.innerHTML = strCLodopInstall_1 + (CLodopIsLocal ? strCLodopInstall_2 : "") + strCLodopInstall_3 + document.body.innerHTML; 

 return;

 } else {

 if (CLODOP.CVERSION < "3.0.8.3") {

 document.body.innerHTML = strCLodopUpdate + document.body.innerHTML;

 }

 if (oEMBED && oEMBED.parentNode)

 oEMBED.parentNode.removeChild(oEMBED);

 if (oOBJECT && oOBJECT.parentNode)

 oOBJECT.parentNode.removeChild(oOBJECT);

 }

 } else {

 var is64IE = isIE && !!(ua.match(/x64/i));

 //=====如果页面有Lodop就直接使用,没有则新建:==========

 if (oOBJECT || oEMBED) {

 if (isIE)

 LODOP = oOBJECT;

 else

 LODOP = oEMBED;

 } else if (!CreatedOKLodop7766) {

 LODOP = document.createElement("object");

 LODOP.setAttribute("width", 0);

 LODOP.setAttribute("height", 0);

 LODOP.setAttribute("style", "position:absolute;left:0px;top:-100px;width:0px;height:0px;");

 if (isIE)

 LODOP.setAttribute("classid", "clsid:2105C259-1E0C-4534-8141-A753534CB4CA");

 else

 LODOP.setAttribute("type", "application/x-print-lodop");

 document.documentElement.appendChild(LODOP);

 CreatedOKLodop7766 = LODOP;

 } else

 LODOP = CreatedOKLodop7766;

 //=====Lodop插件未安装时提示下载地址:==========

 if ((!LODOP) || (!LODOP.VERSION)) {

 if (ua.indexOf('Chrome') >= 0)

 document.body.innerHTML = strHtmChrome + document.body.innerHTML;

 if (ua.indexOf('Firefox') >= 0)

 document.body.innerHTML = strHtmFireFox + document.body.innerHTML;

 document.body.innerHTML = (is64IE ? strHtm64_Install : strHtmInstall) + document.body.innerHTML;

 return LODOP;

 }

 }

 if (LODOP.VERSION < "6.2.2.6") {

 if (!needCLodop())

 document.body.innerHTML = (is64IE ? strHtm64_Update : strHtmUpdate) + document.body.innerHTML;

 }

 //===如下空白位置适合调用统一功能(如注册语句、语言选择等):==

 // 添加注册码,打印就不会出现 “本页由lodop试用版输出 ” 

 LODOP.SET_LICENSES("", "XXXXX", "XXXX", "");

 //=======================================================

 return LODOP;

 } catch (err) {

 alert("getLodop出错:" + err);

 }

}

2、页面引用

import { getLodop } from '@/utils/LodopFuncs'

3、vue中html代码

<!--全部打印-->
<a-dropdown>
 <a-button icon="printer" type="primary">
    打印全部
    <a-icon type="down" />
 </a-button>
 <a-menu slot="overlay">
    <a-menu-item type="primary" @click="printPreview(0,'all')">直接打印</a-menu-item>
    <a-menu-item type="primary" @click="printPreview(1,'all')">打印预览</a-menu-item>
     <a-menu-item type="primary" @click="printPreview(2,'all')">打印维护</a-menu-item>
    <a-menu-item type="primary" @click="printPreview(3,'all')">打印设计</a-menu-item>
  </a-menu>
</a-dropdown>
<!--单个打印,通过index下标-->
<a-dropdown>
 <a-button icon="printer" type="primary">
    打印全部
    <a-icon type="down" />
 </a-button>
 <a-menu slot="overlay">
    <a-menu-item type="primary" @click="printPreview(0,index)">直接打印</a-menu-item>
    <a-menu-item type="primary" @click="printPreview(1,index)">打印预览</a-menu-item>
     <a-menu-item type="primary" @click="printPreview(2,index)">打印维护</a-menu-item>
    <a-menu-item type="primary" @click="printPreview(3,index)">打印设计</a-menu-item>
  </a-menu>
</a-dropdown>

js

printPreview(s,index) {
 this.CreateOneFormPage(index)
 if (s == 0) {
    LODOP.PRINT() //直接打印
 }
 if (s == 1) {
    LODOP.PREVIEW() //打印预览
 }
 if (s == 2) {
    LODOP.PRINT_SETUP() //打印维护
 }
 if (s == 3) {
    LODOP.PRINT_DESIGN() //打印设计
 }
},
CreateOneFormPage(index) {
 LODOP = getLodop()
 LODOP.PRINT_INIT("");//初始化打印标题
 if(index=='all'){
    for(let i=0;i<this.imgList.length;i++){
        var strStyleCSS="<style> .no-print{display:none}</style>"//通过绝对定位居中
        LODOP.ADD_PRINT_HTM(0,0,'100%','100%',strStyleCSS+document.getElementById('printContent'+i).innerHTML);
        LODOP.NewPage();//分页打印
    }
}else{
    var strStyleCSS="<style> .no-print{display:none}</style>"
    LODOP.ADD_PRINT_HTM(0,0,'100%','100%',strStyleCSS+document.getElementById('printContent'+index).innerHTML);
    LODOP.NewPage();
 }
查看原文

赞 1 收藏 0 评论 0

忘れられたくない 发布了文章 · 9月24日

面试真题

var和let const的区别
  • var是ES5语法,let const是ES6语法;var有提升变量
  • var和let是变量可修改;const是常量,不可修改
  • let const有块级作用域,var没有
    • *
typeof返回哪些类型
  • undefined
  • string
  • number
  • boolean
  • symbol
  • object(typeof null === 'object')
  • function
    • *
列举强制类型转换和隐式类型转换

强制:parseInt parseFloat toString
隐式:if、逻辑运算、==、+拼接字符串


手写深度比较isEqual

//判断是否是对象或者是数组
function isObject(obj){
    return typeof obj === 'object' && obj !== null
}
function isEqual(obj1,obj2){
    if(!isObject(obj1) || !isObject(obj2)){
        //值类型
        return obj1 === obj2
    }
    if(obj1 === obj2){
        return true
    }
    //两个都是数组或者对象
    //1、先取出obj1和obj2的keys,比较个数
    const obj1Keys = Object.keys(obj1)
    const obj2Keys = Object.keys(obj2)
    if(obj1Keys.length !== obj2Keys.length){
        return false
    }
    //2、以obj1为基准,和obj2递归依次比较
    for(let key in obj1){
        //比较当前key的value值
        const res = isEqual(obj1[key],obj2[key])
        if(!res){
            return false
        }
    }
    return true
}
const obj1 = {
    a:100,
    b:{
        x:100,
        y:200
    }
}
const obj2 = {
    a:100,
    b:{
        x:100,
        y:200
    }
}

split()和join()的区别

'1-2-3'.split('-')  //[1,2,3]
[1,2,3].join('-')   //'1-2-3'

数组的pop push unshift shift分别是什么
  • pop 在末尾删除一个值,返回的是数组最后的一个值
const arr = [10,20,30,40]
const popRes = arr.pop()
console.log(popRes) //40
console.log(arr)  //[10,20,30]
  • push 在数组末尾插入一个数值,返回的是数组的长度
const arr = [10,20,30,40]
const pushRes = arr.push(50)
console.log(pushRes) // 5
console.log(arr)  //[10,20,30,40,50]
  • unshift 在数组第一位插入一个值,返回的是数组的长度
const arr = [10,20,30,40]
const unshiftRes = arr.unshift(5)
console.log(unshiftRes) // 5
console.log(arr)  //[5,10,20,30,40]
  • shift 删除数组第一个值,返回的是数组的被删除的值
const arr = [10,20,30,40]
const shiftRes = arr.shift(5)
console.log(shiftRes) // 10
console.log(arr)  //[20,30,40]

image.png


纯函数
1、不改变原数组(没有副作用)
2、返回的是一个数组
concat
const arr = [10,20,30,40]
const arr1 = arr.concat([50,60,70])
console.log(arr)  //[10,20,30,40]
console.log(arr1) //[10,20,30,40,50,60,70]
map
const arr = [10,20,30,40]
const arr2 = arr.map(num=>num*10)
console.log(arr)  //[10,20,30,40]
console.log(arr2) //[100,200,300,400]
filter
const arr = [10,20,30,40]
const arr3 = arr.filter(num=>num>25)
console.log(arr)  //[10,20,30,40]
console.log(arr3) //[30,40]
slice
const arr = [10,20,30,40]
const arr4 = arr.slice()
console.log(arr)  //[10,20,30,40]
console.log(arr4) //[10,20,30,40]

slice和splice的区别
slice纯函数
const arr = [10,20,30,40,50]
const arr1 = arr.slice(1,4) //第一个参数为从第几个位置开始,第二个参数为到第几个位置前结束
const arr2 = arr.slice(2)
const arr3 = arr.slice(-2) //截取最后两位
console.log(arr1) //[20,30,40]
console.log(arr2) //[30,40,50]
console.log(arr3) //[40,50]
splice非纯函数
const arr = [10,20,30,40,50]
const spliceRes = arr.splice(1,2,'a','b','c')//参数含义:把第一个参数到第二个参数位置剪切下来,把后面的参数放在剪切区域内
console.log(arr)  //[10,'a','b','c',40,50]
console.log(spliceRes) //[20,30]

[10,20,30].map(parseInt)
const res = [10,20,30].map(parseInt)
console.log(res) //[10,NaN,NaN]
//拆解
[10,20,30].map((num,index) = >{
    return parseInt(num,index)
})

ajax请求get和post的区别
  • get在浏览器回退是无害的,而post则会再次提交请求
  • get产生的url地址可以被收藏,而post不可以
  • get请求会被浏览器主动缓存,而post不会,除非手动设置
  • get请求只能进行url编码,而post支持多种编码方式
  • get请求参数会被完整的保存在浏览器历史记录里,而post参数不会被保留
  • get请求在url中传送参数是有长度限制的,而post没有限制
  • 对参数的数据类型,get只接受ASCLL字符,而post没有限制
  • get比post更不安全,因为参数直接暴漏在url上,不能传递敏感信息
  • get参数通过url传递,post放在request body中
    • *
JavaScript中 call和apply
apply()方法 接收两个参数,一个是函数运行的作用域(this),另一个是参数数组。
call()方法 第一个参数和apply()方法的一样,但是传递给函数的参数必须列举出来。

阻止事件冒泡和默认行为

event.stopPropagation()
event.preventDefault()


查找、添加、移动、删除Dom节点
const div1 = document.getElementById("div1")
//添加新节点
const newP = document.createElement('p')
newP.innerHTML = "this is p1"
div1.appendChild(newP)//添加新创建的元素
//移动已有节点,注意是移动
const p2 = document.getElementById('p2')
div1.appendChild(p2)
//获取子元素列表
const div1 = document.getElementById("div1")
const div1ChildNodes = div1.childNodes
console.log(div1.childNodes)//打印出两种标签,一种是p标签,一种是text标签,因为p标签里面含有文本,text的nodeType为3,p的nodeType为1,所以通过转化为数组过滤。
const div1ChildNodesP = Array.prototype.slice.call(div1.childNodes).filter(child=>{
    if(child.nodeType == 1){
        return true
    }
    return false
})
//删除子节点
div1.removeChild(div1ChildNodesP[0])
//获取父元素
const div1 = document.getElementById("div1")
const parent = div1.parentNode 

如何减少DOM操作
  • 缓存DOM查询结果
  • 多次DOM操作,合并到一次插入
    • *
函数声明和函数表达式的区别

函数声明function fn(){}
函数表达式const fn = function(){}


new Object()和Object.create()的区别

{}等同于new Object(),原型链Object.prototype
Object.create(null)没有原型
Object.create({})可以指定原型


常见的正则表达式
字符串字母开头,后面字母数字下划线,长度6-30
const res = /^[a-zA-Z]\w{5,29}$/
邮政编码
const res = /\d{6}/
去除两端空格
String.prototype.trim = function(){
    return this.replace(/^\s+/,'').replace(/\s+$/,'')
}

获取多个数字中的最大值
//第一种获取最大值
Math.max(10,20,30,40) //40
//第二种获取最大值
function max(){
    const nums = Array.prototype.slice.call(arguments)//变为数组
  let max = nums.length ? nums[0] : null
  nums.forEach(n=>{
    if(n>max) {
        max = n
    }
  })
  return max
}
//获取最小值
Math.min(10,20,30,40) //10

捕获js异常
//第一种手动捕获异常
try{

}.catch(ex){
    console.error(ex) //手动捕获异常
}.finally{

}

//第二种自动捕获异常
window.onerror = function(message,source,lineNum,colNum,error){
    console.log(message,source,lineNum,colNum,error)
}

json
json是一种数据格式,本质是一串字符串
json格式和js对象结构一致,对js语言更友好
window.json是一个全局对象:JSON.stringify JSON.parse

获取当前页面url参数

location.search
URLSearchParams

//传统方式
functoin query1(name){
    const search = location.search.substr(1)//去掉前面的‘?’
    const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`,'i')
    const res = search.match(reg)
    if(res == null){
        return null
    }
    return res[2]
}
//URLSearchParams
funcrion query2(name){
    const search = location.search
    const p = new URLSearchParams(search)
    return p.get(name)
}

//将url参数解析为json对象
function queryToObj(){
    const res = {}
    const search = loction.search.substr(1)
    search.split('&').forEach(paramsStr=>{
        const arr = paramsStr.split('=')
        const key = arr[0]
        const val = arr[1]
        res[key] = val
    })
    return res
}

数组排平[1,2,[3,4,[5]],6] => [1,2,3,4,5,6]
function flat(arr){
    const isDeep = arr.some(item=>item instanceof Array)
    if(!isDeep){
        return arr
    }
    const res = Array.prototype.concat.apply([],arr)
    return flat(res)
}

数组去重
//第一种
function unique(arr){
    const res = []
    arr.forEach(item=>{
        if(res.indexOf(item)<0){
            res.push(item)
        }
    })
    return res
}
//第二种
function unique(arr){
    const set = new Set(arr)
    return [...set]
}

手写深拷贝

Object.assign为浅拷贝

function deepClone(obj = {}) {
    if (typeof obj !== 'object' || obj == null) {
        // obj 是 null ,或者不是对象和数组,直接返回
        return obj
    }
 
    // 初始化返回结果
    let result
    if (obj instanceof Array) {
        result = []
    } else {
        result = {}
    }

    for (let key in obj) {
    // 保证 key 不是原型的属性
        if (obj.hasOwnProperty(key)) {
        // 递归调用!!!
            result[key] = deepClone(obj[key])
        }
    }

     // 返回结果
    return result
  }
RAF requestAnimationFrame动画
更新频率要60帧/s,即16.67ms跟新一次视图
//设置一个div宽度为100px,3s后变为640px,即增加540px
//60帧/s  3s->180帧 每次变化3px
const $div1 = $("#div1")
let curWidth=100
let maxWidth=640
//第一种
function animate(){
    curWidth =+ curWidth
    $div1.css('width',curWidth)
    if(curWidth<naxWidth){
        setTimeout(animate,16.7)
    }
}
//第二种
function animate(){
    curWidth =+ curWidth
    $div1.css('width',curWidth)
    if(curWidth<naxWidth){
        requestAnimationFrame(animate)
    }
}
animate()
查看原文

赞 0 收藏 0 评论 0

忘れられたくない 发布了文章 · 9月23日

运行环境—浏览器—安全

从输入url到页面加载完成发生了什么?——前端角度
  1. 浏览器的地址栏输入URL并按下回车。
  2. 浏览器查找当前URL的DNS缓存记录。
  3. DNS解析URL对应的IP。
  4. 根据IP建立TCP连接(三次握手)。
  5. HTTP发起请求。
  6. 服务器处理请求,浏览器接收HTTP响应。
  7. 渲染页面,构建DOM树。
  8. 关闭TCP连接(四次挥手)
    • *
window.onload和DOMContentLoaded
window.addEventListener('load',function(){
    //页面的资源全部加载完成才会执行,包括图片、视频等
})
document.addEventListener('DOMContentLoaded',function(){
    //dom渲染完即可执行,此时图片、视频还没有加载完成
})
document load和document ready的区别

共同点:这两种事件都代表的是页面文档加载时触发。
异同:ready 事件的触发,表示文档结构已经加载完成(不包含图片等非文字媒体文件)。
onload 事件的触发,表示页面包含图片等文件在内的所有元素都加载完成。


浏览器是如何渲染页面
  1. 解析HTML文件,创建DOM树。自上而下,遇到任何样式(link、style)与脚本(script)都会阻塞(外部样式不阻塞后续外部脚本的加载)。
  2. 解析CSS。优先级:浏览器默认设置<用户设置<外部样式<内联样式<HTML中的style样式;
  3. 将CSS与DOM合并,构建渲染树(Render Tree)
  4. 布局和绘制,重绘(repaint)和重排(reflow)
    • *
前端性能优化
多使用内存、缓存或其他方法
减少CPU计算量、减少网络加载耗时
  • 节流throttle和防抖debounce
  • JavaScript 压缩和模块打包
  • 按需加载资源
  • 缓存
  • 使用索引加速数据库查询   
  • 使用更快的转译方案
  • 避免或最小化 JavaScript 和 CSS 的使用而阻塞渲染
  • 图片编码优化
图片懒加载
image.png

手写防抖debounce
监听一个输入框的,文字变化后触发change事件
直接用keyup事件,则会频繁触发change事件
防抖:用户输入结束或暂停时,才会触发change事件
//js文件
const input1 = document.getElementById('input1')
//let timer = null
//input1.addEventListener('keyup',function(){
//    if(timer){
//        clearTimeout(timer)
//    }
//    timer = setTimeout(()=>{
//        console.log(input1.value)
//        timer = null
//    },500)
//})

function debounce(fn,delay = 500){
    let timer = null
    return function(){
        if(timer){
            clearTimeout(timer)
        }
        timer = setTimeout(()=>{
            fn.apply(this,arguments)
            timer = null
        },delay)
    }
}
input1.addEventListener('keyup',debounce(function(){
    console.log(input1.value)
},600))

节流throttle
拖拽一个元素时,要随时拿到该元素被拖拽的位置
直接用drag事件,则会频繁触发,很容易导致卡顿
节流:无论拖拽速度多快,都会每隔100ms触发一次
//html
<div id="div1" draggable="true">可拖拽</div>

//js
const div1 = document.getElementById('div1')
//let timer = null 
//div1.addEventListener('drag',function(e){
//    if(timer){
//        return
//    }
//    timer = setTimeout(()=>{
//        console.log(e.offsetX,e.offsetY)
//        timer = null
//    },100)   
//})

function throttle(fn,delay = 100){
    let time = null
    return function(){
        if(timer){
            return
        }
        timer = setTimeout (()=>{
            fn.apply(this,arguments)
            timer = null
        },delay)
    }
}
div1.addEventListener('drag',throttle(function(e){
    console.log(e.offsetX,e.offsetY)
},200))

CSS Sprites
CSS Sprites其实就是把网页中一些背景图片整合拼合成一张图片中,再利用DIV CSS的“background-image”,“background- repeat”,“background-position”的组合进行背景定位
sprites优势:
  1. 减少http iis请求数,从而提高页面的性能
  2. 利用图片精灵还能减少图片的字节
  3. 在整理图片的过程中,不需要再纠结如此大量图片的名字问题
  4. 便于后期的维护和修改
sprites缺点:
  1. 开发的时候比较麻烦,你要通过photoshop(PS)或其他工具测量计算每一个背景单元的精确位置,这是针线活,没什么难度,但是很繁琐。你需要考虑当前盒子会不会漏出其他的背景图,比较头疼的是,页面自适应时,背景图的位置就没那么容易掌控了。
  2. 在维护的时候比较麻烦,因为他是多张图在一张图上,牵一发而动全身的感觉。轻易不要改变其他图片的位置,原位能放下就在原位改,放不下就在下面添加。
    • *
页面导入样式时,使用link和@import有什么区别
  1. link属于XHTML标签,除了加载CSS外,还能用于定义RSS, 定义rel连接属性等作用;而@import是CSS提供的,只能用于加载CSS;
  2. 页面被加载的时,link会同时被加载,而@import引用的CSS会等到页面被加载完再加载;  
  3. import是CSS2.1 提出的,只在IE5以上才能被识别,而link是XHTML标签,无兼容问题
    • *
js延迟加载的方式有哪些?
  • defer和async
  • 动态创建DOM方式(创建script,插入到DOM中,加载完毕callBack)
  • 按需异步载入js
    • *
浏览器的内核
  • Trident内核:IE最先开发或使用的,也称IE内核 ,360浏览器使用的也是IE内核;
  • Webkit内核:谷歌chrome浏览器最先开发或使用,也叫谷歌内核 ,枫树浏览器、太阳花使用的也是谷歌内核;
  • Gecko内核: Netscape6开始采用的内核,后来的Mozilla FireFox (火狐浏览器) 也采用了该内核,K-Meleon浏览器也是使用这种内核;
  • Presto内核:目前只有Opera浏览器采用该内核
    • *
W3c标准
w3c标准不是某一个标准,而是一系列标准的集合。网页主要由结构、表现、行为三部分组成,对应的标准有结构化标准语言有XHTML、xml,表现的标准语言有CSS,行为的标准有对象模型(如 w3c dom)、ECMAScripe等

Css语法结构
CSS的语法结构仅仅由三部分组成
  • 选择符(Selector)
  • 属性(property)
  • 值(value)
    • *
常见的web前端攻击方式
  • XSS跨站请求攻击
  • XSRF跨站请求伪造
XSS跨站请求攻击
攻击原理:
不需要你做任何的登录认证,它会通过合法的操作(比如在url中输入、在评论框中输入),向你的页面注入脚本(可能是js、hmtl代码块等)。最后导致的结果可能是:盗用Cookie破坏页面的正常结构,插入广告等恶意内容D-doss攻击。
预防:
替换特殊字符,如<变为&lt;>变为&gt;
<script>变为&lt;script&gt;,直接显示,而不是作为脚本执行
XSRF跨站请求伪造
用户打开浏览器,访问受信任银行网站,输入用户名和密码请求登录网站;
在用户信息通过验证后,银行网站产生Cookie信息并返回给浏览器,此时用户登录网站成功,可以正常发送请求到网站;
用户未退出银行网站之前,在同一浏览器中,打开一个TAB页访问其他网站B
这时候网站B 已被黑客注入诱导信息,加入是一张图片,图片地址指向 data-original=”http://bank.example/withdraw?... 点击之后转账给黑客这个账户
浏览器在接收到这些攻击性代码请求后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,根据用户的Cookie信息以C的权限处理该请求,导致来自黑客请求恶意代码被执行。

预防
使用post接口
增加验证,例如密码、短信验证码、指纹等

查看原文

赞 0 收藏 0 评论 0

忘れられたくない 发布了文章 · 9月22日

git

git常用命令
  • git status 查看文件状态
  • git branch 查看分支
  • git diff 查看修改的内容
  • git log 查看提交记录
  • git add . (将内容从工作目录添加到暂存区)
  • get checkout . 撤销提交之前所有修改的文件内容
  • git commit -m "xxx" 提交记录
  • git push origin master
  • git pull origin master
  • git checkout xxx/git checkout -b xxx (切换分支)
  • git fetch 拉取所有的分支
  • git merge xxx 合并分支,把分支代码合并到当前分支上
  • git stash先将数据存到缓存
  • git stash pop 将之前缓存的文件推出来
  • vi 编辑模式,ctrl+w+q退出编辑模式
tag打包:
  • git tag t1.0.0
  • git push origin tag t1.0.0

image.png

查看原文

赞 0 收藏 0 评论 0

忘れられたくない 发布了文章 · 9月22日

http面试题

http状态码分类
  • 1xx服务器收到请求
  • 2xx请求成功
  • 3xx重定向
  • 4xx客户端错误
  • 5xx服务端错误
    • *
常见状态码
  • 200 成功
  • 301 永久重定向
  • 302 临时重定向
  • 304资源未被修改
  • 404资源未找到
  • 403没有权限
  • 500服务器错误
  • 504网关超时
    • *
methods请求方式
  • get获取数据
  • post新建数据
  • patch/put更新数据
  • delete删除数据
    • *
post和get的区别
  • get在浏览器回退是无害的,而post则会再次提交请求
  • get产生的url地址可以被收藏,而post不可以
  • get请求会被浏览器主动缓存,而post不会,除非手动设置
  • get请求只能进行url编码,而post支持多种编码方式
  • get请求参数会被完整的保存在浏览器历史记录里,而post参数不会被保留
  • get请求在url中传送参数是有长度限制的,而post没有限制
  • 对参数的数据类型,get只接受ASCLL字符,而post没有限制
  • get比post更不安全,因为参数直接暴漏在url上,不能传递敏感信息
  • get参数通过url传递,post放在request body中
    • *
http特点
1.简单快速(资源固定)
2.灵活(完成不同数据类型的传输)
3.无连接(连接一次就会被断掉)
4.无状态(不能区分上一次连接者和这一个连接者的身份)

HTTP协议报文组成部分:请求报文、响应报文

请求报文:请求行,请求头、空行、请求体
请求行:http方法+页面地址+http协议+版本
请求头(key,value值)
响应报文:状态行,响应头,空行,响应体

常用的网络协议http和https
  1. https协议需要ca申请证书,一般免费证书较少,因而需要一定费用;
  2. http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议;
  3. http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443;
  4. http的连接很简单,是无状态的;https协议是由ssl+http协议构建的可进行加密传输,身份认证的网络协议,比http协议安全。
    • *
Restful API

传统的API设计:把每个url当做一个功能
Restful API:把每个url当做一个唯一的资源

http headers—常见的Request headers
  • Accept浏览器可接收的数据格式
  • Accept-Encoding浏览器可接收的压缩算法,如gzip
  • Accept-Language浏览器可接收的语言,如zh-CN
  • connection-keep alive 一次TCP连接重复使用
  • cookie
  • Host
  • User-Agent(简称UA)浏览器信息
  • Content-type 发送数据格式,如application/json
http headers—常见的Response headers
  • Content-type 返回数据格式,如application/json
  • Content-length返回数据的大小,多少字节
  • Content-Encoding返回数据的压缩算法,如gzip
  • set-Cookie
http缓存—Cache control强制缓存
  • 在response headers中
  • 控制强制缓存的逻辑

image.png

cache control 的值
  • max-age设置过期的值,单位为秒
  • no-cache不缓存
  • no-store 不用本地缓存
  • private只允许最终用户做缓存
  • public允许中间的一些路由等做缓存
http缓存—协商缓存(对比缓存)

服务器端缓存策略
image.png

查看原文

赞 0 收藏 0 评论 0

忘れられたくない 发布了文章 · 9月21日

cookies , sessionStorage和localStorage

  • cookies:数据大小不超过4k,cookies过期之前一直有效,会自动传递到服务器
  • sessionStorage:存储大小相对cookies大得多(5M或更大),关闭浏览器窗口后自动删除,仅存本地
  • localStorage:存储大小相对cookies大得多(5M或更大),除非手动删除,关浏览器后不会删除,仅存本地

image.png

查看原文

赞 0 收藏 0 评论 0

忘れられたくない 发布了文章 · 9月21日

浏览器的同源策略(跨域)

同源策略
  • ajax请求时,浏览器要求当前网页和server必须同源(安全)
  • 同源:协议、域名、端口三者必须一致
加载图片,css,js可无视同源策略
<img data-original="跨域的图片地址"/>
<link href="跨域的css地址"/>
<script data-original="跨域的js地址"></script>

image.png

js跨域的原因:关闭会容易受到XSS和CSRF的攻击

js跨域方案

1、js可以解决使用jsonp进行跨域
2、通过修改document.domain来跨子域
3、使用window.name来进行跨域
4、window.postMessage进行跨域
**5、通过CORS进行跨域
6、代理跨域**

通过jQuery实现jsonp

image.png

CORS-服务器设置http-header

image.png

查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 4 次点赞
  • 获得 0 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 0 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2019-12-04
个人主页被 523 人浏览