請問websocket
如何兼容低瀏覽器的
websocket
是H5開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議。
WebSocket
使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在WebSocket
API
中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸。對於低版本的瀏覽器我們可以使用下面幾種方法對低版本瀏覽器進行兼容。
- 引入
SockJS
庫,他是JavaScript
的一個庫,支持websocket
的瀏覽器會優先使用原生的websorcket
,如果不支持,則會使用引用的庫文件。- 引用
socket.IO
的庫文件,這同樣是基於時間的雙向通信,如何不支持則會使用替代的方案。
Vue單頁面的SEO解決方案
方法:用prerender-spa-plugin
結合vue-meta-info
來實現首頁的seo
首先就是安裝插件:
npm install prerender-spa-plugin vue-meta-info --save
然后在項目的目錄下找到文件build/webpack.prod.conf.js
,添加如下代碼:
const PrerenderSpaPlugin = require('prerender-spa-plugin')
new PrerenderSpaPlugin(
//將渲染的文件放到dist目錄下
path.join(__dirname, '../dist'),
//需要預渲染的路由信息
[ '/index','/about' ],
{
//在一定時間后再捕獲頁面信息,使得頁面數據信息加載完成
captureAfterTime: 50000,
//忽略打包錯誤
ignoreJSErrors: true,
phantomOptions: '--web-security=false',
maxAttempts: 10,
},
)
描述圖片懶加載的方案,並對其原理進行簡單的描述
圖片懶加載是前端頁面優化的一種方式,在頁面中有很多圖片的時候,圖片加載就需要很多時間,很耗費服務器性能,不僅影響渲染速度還會浪費帶寬,為了解決這個問題,提高用戶體驗,所以就出現了懶加載這種方式來減輕服務器的壓力,優先加載可視區域的內容,其他部分等進入了可視區域再加載,從而提高性能。
思路:在圖片沒有進入可視區域時,先不給的src
賦值,這樣瀏覽器就不會發送請求了,等到圖片進入可視區域再給src
賦值。圖片的真實地址需要存儲在data-src
中。圖片沒有進入可視區域,也就是說圖片的offsetTop需要小於頁面的可視高度,但想一想,當圖片在頁面的下方的時候呢,需要頁面滾動了一段距離之后才能看到圖片,所以這里需要滿足img.scrollTop < 頁面的可視區域高度+頁面滾動的高度,這里是實現圖片懶加載的關鍵,接下來看具體代碼的實現
<img src="loading.gif" data-src="1.jpg" alt="">
<img src="loading.gif" data-src="2.jpg" alt="">
<img src="loading.gif" data-src="3.jpg" alt="">
<img src="loading.gif" data-src="4.jpg" alt="">
<img src="loading.gif" data-src="5.jpg" alt="">
<img src="loading.gif" data-src="6.jpg" alt="">
<img src="loading.gif" data-src="7.jpg" alt="">
<img src="loading.gif" data-src="8.jpg" alt="">
<img src="loading.gif" data-src="9.jpg" alt="">
<img src="loading.gif" data-src="10.jpg" alt="">
......
*{
margin: 0;
padding: 0;
}
img{
vertical-align: top;
width: 100%;
height: auto;
}
let img = document.getElementsByTagName('img');
let n = 0; //存儲圖片加載到的位置,避免每次都從第一張圖片開始遍歷
lazyload();
window.addEventListener('scroll',lazyload);
function lazyload(){ //監聽頁面滾動事件
var seeHeight = window.innerHeight; //可見區域高度
var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
for(let i = n; i < img.length; i++){
if(img[i].offsetTop < seeHeight + scrollTop){
if(img[i].getAttribute("src") == 'loading.gif'){
img[i].src = img[i].getAttribute("data-src");
}
n = i + 1;
}
}
}
請問浮動元素引起的問題和解決方法
浮動元素引起的問題:
- 由於浮動元素已脫離文檔流,所以父元素無法被撐開,影響與父級元素同級的元素。
- 與浮動元素同級的非浮動元素(內聯元素)會跟隨其后,也是由於浮動元素脫離文檔流,不占據文檔流中額位置。
- 如果該浮動元素不是同級第一個浮動的元素,則它之前的元素也應該浮動,否則容易影響頁面的結構顯示。
如何清楚浮動:
- 使用css樣式中的
clear:both
- 給父元素添加
clearfix
樣式
.clearfix:after{
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.clearfix{
display: inline-block;
} /* for IE/Mac */
跨域的原因與解決方式
跨域是指瀏覽器的不執行其他網站腳本的,由於瀏覽器的同源策略造成,是對JavaScript的一種安全限制.說白點理解,當你通過瀏覽器向其他服務器發送請求時,不是服務器不響應,而是服務器返回的結果被瀏覽器限制了.
跨域的解決方案
JSONP方式
缺點: get請求,前后端都要修改
優點:無兼容問題(js哪個瀏覽器不兼容 站出來)
思路:利用js保存全局變量來傳遞數據,因為js不受同源策略控制,script標簽加載機制html
反向代理,ngixn
你在工作中如何優化項目代碼,請舉例說明.
深拷貝和淺拷貝的區別,請你實現深拷貝的幾種方法?
深拷貝和淺拷貝最根本的區別在於是否真正獲取一個對象的復制實體,而不是引用。
淺復制:僅僅是指向被復制的內存地址,如果原地址發生改變,那么淺復制出來的對象也會相應的改變。
深復制:在計算機中開辟一塊新的內存地址用於存放復制的對象。
實現深拷貝:
let obj={
name:'zs',
age:18
}
let deepCloneObj=JSON.parse(JSON.stringify(obj))
deepCloneObj.name=28;
console.log(obj.age);//18
優點:優點是方便快捷,性能相對比較好;
缺點:但是復雜的對象進行JSON轉換有可能會丟失屬性.
let obj={
name:'zs',
main:{
a:1,
b:2
},
fn:function(){},
friends:[1,3,5,7,9]
}
function copy(obj){
let newObj=null;
if(typeof(obj)=='object'&&obj!==null){
newObj=obj instanceof Array ? []:{};
for(var i in obj){
newObj[i]=copy(obj[i])
}
}else{
newObj=obj
}
return newObj
}
var obj2 = copy(obj)
obj2.name = '修改成功'
obj2.main.a = 100
console.log(obj,obj2)
請你實現快速排序算法
function quickSort(arr) {
/*
* 創建len保存數組的長度,每次獲取數組的長度都要實時查詢不利於性能;
* index作為保存取到的中間值;
* pivot保存比較參照物;
* left、right作為子數組的容器;
*/
var len = arr.length,
index,
pivot,
left=[],
right=[];
// 如果數組只有一位,就直接返回數組,遞歸的終止條件;
if (len <= 1) return arr;
//獲取中間值的索引,使用Math.floor向下取整;
index = Math.floor(len / 2);
// 使用splice截取中間值,第一個參數為截取的索引,第二個參數為截取的長度;
// 如果此處使用pivot=arr[index]; 那么將會出現無限遞歸的錯誤;
// splice影響原數組,原數組長度減一;
pivot = arr.splice(index, 1);
len -= 1;
// 小於arr[pivot]的存到left數組里,大於arr[pivot]的存到right數組;
for (var i = 0; i < len; i++) {
if (pivot > arr[i]) {
left.push(arr[i]);
} else {
right.push(arr[i]);
}
}
// 不斷把分割的左右子數組傳入quickSort,直到分割的只有一位直接返回子數組本身,遞歸終止;
// 把每次分割的數組一層一層的用concat連接起來;
// 每一層left里的元素都小於對應的pivot,right里的元素都大於對應的pivot;
return quickSort(left).concat(pivot, quickSort(right));
}
如何判斷JS變量的數據類型
typeof
1 // 特殊的基本數據類型判斷
2 typeof null // 'object'
3 // 特殊的引入數據類型判斷
4 typeof function () {} // 'function'
5 typeof new Function() // 'function'
instanceof
1 function Car () {} // 定義一個構造函數
2 console.log(new Car() instanceof Car)
3 console.log({} instanceof Object) // true
4 console.log([] instanceof Array) // true
5 console.log(new String('') instanceof String) // true
6 console.log('' instanceof String) // false
Object.prototype.toString.call(null) // [object Null]
Object.prototype.toString.call(undefined) // [object Undefined]
Object.prototype.toString.call(1) // [object Number]
Object.prototype.toString.call(new Number(1)) // [object Number]
Object.prototype.toString.call('a') // [object String]
Object.prototype.toString.call(new String('a')) // [object String]
Object.prototype.toString.call({}) // [object Object]
Object.prototype.toString.call([]) // [object Array]
Object.prototype.toString.call(new Date()) // [object Date]
Object.prototype.toString.call(/^d$/g) // [object RegExp]
Object.prototype.toString.call(() => {}) // [object Function]
Object.prototype.toString.call(new Function()) // [object Function]
function Car () {} // 定義一個構造函數
Object.prototype.toString.call(new Car()) // [object Object]
rem要如何配置
rem是只相對於根元素htm的font-size,即只需要設置根元素的font-size,@media
可以針對不同的屏幕尺寸設置不同的樣式
例如:
/*dpi*/
/* for 1080+ px width screen */
/* for 1080 px width screen */
/* for 800 px width screen */
/* for 800 px width screen */
@media only screen and (min-width: 751px) { html, body { font-size: 31.25px; } }
/* for 800 px width screen */
@media only screen and (max-width: 750px) { html, body { font-size: 31.25px; } }
/* for 720 px width screen */
@media only screen and (max-width: 720px) { html, body { font-size: 30px; } }
/* for 640 px width screen */
@media only screen and (max-width: 640px) { html, body { font-size: 27px; } }
/* for 540 px width screen */
@media only screen and (max-width: 540px) { html, body { font-size: 22.5px; } }
/* for 480 px width screen */
@media only screen and (max-width: 480px) { html, body { font-size: 20px; } }
/* for 450 px width screen */
@media only screen and (max-width: 450px) { html, body { font-size: 18.9px; } }
/* for 414 px width screen */
@media only screen and (max-width: 414px) { html, body { font-size: 17.25px; } }
/* for 375 px width screen */
@media only screen and (max-width: 375px) { html, body { font-size: 15.625px; } }
/* for 320 px width screen */
@media only screen and (max-width: 320px) { html, body { font-size: 13.5px; } }
vue中key的作用
key值大多情況下使用在循環語句中,從本質來講主要作用大概有以下兩點:
- 主要用在 Vue 的虛擬 DOM 算法,在新舊 nodes 對比時辨識 VNodes,相當於唯一標識ID。
- Vue 會盡可能高效地渲染元素,通常會復用已有元素而不是從頭開始渲染, 因此使用key值可以提高渲染效率,同理,改變某一元素的key值會使該元素重新被渲染。
至少寫出三種減少頁面加載時間的方法
- 重復的HTTP請求數量應盡量減少
- 減少調用其他頁面,文件的數量
- 精靈圖
- 壓縮JavaScript css代碼
- 在文件頭部配置css樣式的定義
- 在文件末尾放javascript腳本
- css,javascript改為外部調用
- 使用CDN網絡加速
- 服務器啟用Gzip壓縮功能
- ajax采用緩存調用
- 縮減iframe的使用,如無必要盡量別使用
- 優化圖片文件
路由導航(路由)守衛
全局導航守衛
- 全局前置守衛
router.beforeEach(fn)
- n中有三個參數
- to:表示要去哪里
- from:表示從哪里來
- next:表示通不通過
-
關於next的使用
- next() 等價於 next( true ) 表示可以從當前路由跳轉到目標路由
- next( false ) 表示不通過, 表示從當前路由跳轉不到目標路由
- next(’/login’) 等價於 next({path:’/login’}) 跳轉指定的路由
- next(’/login’) 等價於 next({path:’/login’,params,query})
- next( fn ) 數據預載
// 全局前置路由守衛 router.beforeEach( (to,from,next) =>{ const name = localStorage.getItem('name');//查看本地存儲上是否有name對象 if( name || to.path === '/login'){//短路邏輯,有就可以繼續執行,沒有就跳轉到登錄頁面 next() }else{ next( '/login' )//跳轉登錄頁面 } }) // 全局后置路由守衛 router.afterEach(fn),表示離開當前路由時執行 router.afterEach( (to,from,next)=> { if(to.path === '/user'){//當to的path值等於這個時 alert('確定進入user嗎') } })
-
全局的后置守衛
afterEach(fn)
路由獨享守衛
- 寫在路由表中的守衛鈎子
- 針對的是和當前路由相關的,那么其他與之不相關的路由,它是無法監聽它的變化情況的
{
path: '/user',
component: User,
beforeEnter: ( to,from,next ) =>{
// console.log(to)
const name = localStorage.getItem('name');//判斷本地存儲有沒有name對象
if( name){//存在就可以繼續執行
next()
}else{
setTimeout(()=>{
alert('你還沒有登錄,點擊跳轉登錄')//不存在就彈窗告訴沒有登錄,點擊登錄
next('/login')
},0)
}
}
},
DOM阻塞
DOM的阻塞會導致頁面的加載時間延遲,阻塞的資源有html,css(包括web font),javascript
不是單頁面能使用vuex么
看你個人需要了。單頁應用為什么要用的?還不是他的邏輯太復雜了,混亂的不得了,不找個工具管理一下各種坑人。如果一個單頁應用里面什么js都沒有,就是html,那你引入vuex有什么用嗎?那么好了,並不是說單頁應用什么的,是你遇到了什么問題,用什么東西去解決好了附上vuex里面其實也寫了,為了管理你的共享的東西,共享的狀態。
swagger的了解
Swagger
是一個規范和完整的框架,用於生成、描述、調用和可視化RESTful
風格的Web
服務。1、是一款讓你更好的書寫API文檔的規范且完整框架。
2、提供描述、生產、消費和可視化
RESTful
Web Service
。3、是由龐大工具集合支撐的形式化規范。這個集合涵蓋了從終端用戶接口、底層代碼庫到商業API管理的方方面面。
面向對象的理解
在現實生活中任何物體都可以歸為一類事物,而每一個個體都是一個事物的實例,面向對象的編程是以對象為中心.
其中面向對象的有三大特征,分別是封裝,繼承和多態
其中的多態表現為:方法的重載和方法的重寫;
方法重載:重載是指不同的函數使用相同的函數名,但是函數的參數個數或類型不同。調用的時候根據函數的參數來區別不同的函數.
方法重寫:重寫(也叫覆蓋)是指在派生類中重新對基類中的虛函數(注意是虛函數)重新實現。即函數名和參數都一樣,只是函數的實現體不一樣.
H5有哪些屏幕觸碰事件
touchstart:觸摸開始的時候觸發
touchmove:手指在屏幕上滑動的時候觸發
touchend:觸摸結束的時候觸發
而每個觸摸事件都包括了三個觸摸列表,每個列表里包含了對應的一系列觸摸點(用來實現多點觸控):
touches:當前位於屏幕上的所有手指的列表。
targetTouches:位於當前DOM元素上手指的列表。
changedTouches:涉及當前事件手指的列表。
每個觸摸點由包含了如下觸摸信息(常用):
identifier:一個數值,唯一標識觸摸會話(touch session)中的當前手指。一般為從0開始的流水號(android4.1,uc)
target:DOM元素,是動作所針對的目標。
pageX/pageX/clientX/clientY/screenX/screenY:一個數值,動作在屏幕上發生的位置(page包含滾動距離,client不包含滾動距離,screen則以屏幕為基准)。
radiusX/radiusY/rotationAngle:畫出大約相當於手指形狀的橢圓形,分別為橢圓形的兩個半徑和旋轉角度。初步測試瀏覽器不支持,好在功能不常用,歡迎大家反饋。
Label通常怎么用,用什么場景
label
標簽為input元素定義標注(標記),它不會向用戶呈現任何特殊效果,和span
標簽類似。但label
標簽和span
標簽最大的區別就是它為鼠標用戶改進了可用性,可以關聯特定的表單控件。
主要的應用場景:
label
標簽常用於與checkbox
或radio
關聯,以實現點擊文字也能選中/取消checkbox
或radio
css寫一個三角形
<div></div>
<style>
div {
width: 0;
height: 0;
border-top: 50px solid transparent; /*這行去掉也行*/
border-bottom: 50px solid yellow;
border-right: 50px solid transparent;
border-left: 50px solid transparent;
}
</style>
或者
<div></div>
<style>
div {
height:0px;
width:0px;
border-top:solid 100px red;
border-left:solid 100px green;
}
</style>
遞歸組件實現以及有什么需要注意的
所謂的遞歸組件就是組件里面自己調用自己
比如:
<template>
<ul>
<my-ul></my-ul>
<ul>
</template>
<script>
export default{
name:'myUl'
}
</script>
需要注意的是:
一般我們引入的時候都是import myUl from '../..xxx',然后通過component注冊到組件上;
但是如果沒有結束條件的情況直接遞歸的話會報錯的,所以 針對遞歸組件我們需要加上結束條件.
link 的作用
link標簽有兩個作用:
- 定義文檔和外部文件的關系;
- 是鏈接樣式表
created和mounted的區別
created
是 在模板渲染之前調用的,他只是初始化好了一些數據,並沒有到渲染視圖的階段觸發;所以在此階段不能獲取DOM
節點;mounted
在模板渲染之后觸發,通常此時頁面初始化完成,可以獲取DOM
節點.
垃圾回收機制怎么觸發
垃圾回收有兩種實現方式,分別是標記清除和引用計數
- 標記清除
當變量進入執行環境時標記為“進入環境”,當變量離開執行環境時則標記為“離開環境”,被標記為“進入環境”的變量是不能被回收的,因為它們正在被使用,而標記為“離開環境”的變量則可以被回收;
function func(){
const a=1
const b=2
//函數執行時,a,b分別標記,進入環境
}
func()//函數執行結束,a,b被標記,離開環境,被回收
-
引用計數
統計引用類型變量聲明后被引用次數,當次數為0時,變量將被回收
vue中updated和destroyed我們能做什么
vue鈎子函數updated,當事先設置好data里面的數據被改變后會有updeted函數.且視圖更新完畢,所以可以在數據更新時我們做的一些操作寫在updated鈎子中;
destroyed鈎子,當我們離開某個頁面的時候,實例銷毀,便會調用此鈎子,我們可以將離開頁面的某些操作放在此鈎子中,例如清除某些定時器之類的...
vue2.0雙向綁定原理的缺陷
vue2.0的雙向綁定用的是Object.defineProperty()進行數據攔截的操作,其缺點是無法監控到數組下標的變化,導致通過數組下標添加元素不能實時響應.還有就是只能劫持對象的屬性,從而需要對每個對象,每個屬性進行遍歷,如果屬性是對象則要進行深度遍歷.
nextTick
vue中的nextTick是將回調函數延遲在下一次DOM更新數據后調用,可以理解為,當數據更新了,視圖更新后自動執行該函數;
<div id="app">
<div id="text" v-if="isShow">這是一段文本</div>
<button @click="getText">獲取內容</button>
</div>
</body>
<script src="./node_modules/vue/dist/vue.min.js"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
isShow: false,
},
methods: {
getText() {
this.isShow = true;
this.$nextTick(() => {
let text = document.querySelector("#text").innerHTML;
console.log(text);
});
},
},
});
</script>
1累加到100,用遞歸實現
function Add() {
this.nul = 0
this.fn = function(num) {
if (num == 1) {
return this.nul = 1
}
return this.nul = num + this.fn(num - 1)
}
}
const add=new Add()
add.fn(100)
console.log(add.nul);//5050
vue中的e-charts是怎么用的,具體代碼,后台返回的數據是怎么處理的,怎么渲染
首先安裝echarts
來畫圖
yarn add echarts
准備具備寬高的容器裝,用ref獲取dom元素
<div ref="main" style="width: 600px; height:400px;"></div>
導入echarts
import echarts from 'echarts'
初始化echarts實例
handleTabClick (tab) {
if (tab.name === 'img') {
// 使用echarts畫圖
// console.log('畫圖')
// 基於准備好的dom,初始化echarts實例
const myChart = echarts.init(this.$refs.main)
// 指定圖表的配置項和數據
const option = {
title: {
text: 'ECharts 入門示例'
},
tooltip: {},
legend: {
data: ['銷量']
},
xAxis: {
data: ['襯衫', '羊毛衫', '雪紡衫', '褲子', '高跟鞋', '襪子']
},
yAxis: {},
series: [{
name: '銷量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}]
}
// 使用剛指定的配置項和數據顯示圖表。
myChart.setOption(option)
}
}
TCP和UDP以及三次握手四次揮手
組件中的data為啥是一個函數
因為組件是用來復用的,且JS里對象是引用關系,如果組件中的data 是一個對象的話,那么這樣作用域沒有隔離,data會相互影響,data是一個函數時,每個組件實例都有自己的作用域,每個實例相互獨立,不會相互影響,而Vue實例是不會被復用的,所以不會存在引用對象的問題.
axios封裝原理
axios
是一個輕量的 HTTP客戶端
,它基於 XMLHttpRequest
服務來執行 HTTP 請求,支持豐富的配置,支持 Promise
,支持瀏覽器端和 Node.js
端。
data和props的區別
vue提供了兩種不同的存儲變量:props
和data
data
是每個組件的私有內存,可以在其中存儲需要的任何變量。props
是將數據從父組件傳遞到子組件的方式。子組件中的data數據,不是通過父組件傳遞的是子組件私有的,是可讀可寫的。
子組件中的所有 props中的數據,都是通過父組件傳遞給子組件的,是只讀的。
http請求有幾種
http請求中的8種請求方法
1、opions
返回服務器針對特定資源所支持的HTML請求方法 或web服務器發送*測試服務器功能(允許客戶端查看服務器性能)
2、Get
向特定資源發出請求(請求指定頁面信息,並返回實體主體)
3、Post
向指定資源提交數據進行處理請求(提交表單、上傳文件),又可能導致新的資源的建立或原有資源的修改
4、Put
向指定資源位置上上傳其最新內容(從客戶端向服務器傳送的數據取代指定文檔的內容)
5、Head
HEAD就像GET,只不過服務端接受到HEAD請求后只返回響應頭,而不會發送響應內容。當我們只需要查看某個頁面的狀態的時候,使用HEAD是非常高效的,因為在傳輸的過程中省去了頁面內容。
6、Delete
請求服務器刪除request-URL所標示的資源*(請求服務器刪除頁面)
7、Trace
回顯服務器收到的請求,主要用於測試和診斷6386
8、Connect
HTTP/1.1協議中能夠將連接改為管道方式的代理服務器
- 如何優化await導致多個異步操作的延遲問題
- 預加載的優化方法
腳手架幫我們做了什么?
vue 的腳手架的作用是來自動一鍵生成vue+webpack的項目模板.包含依賴庫,免去你手動安裝各種插件.
腳手架中Babel插件幫我們做了js語法降級,兼容性較好;
還可以使用typescript的語法;
里面可以配好路由和vuex及多種css預處理器.
說一下flex布局,以及媒體查詢
flex:彈性布局,用來為盒子模型提供最大的靈活性.任何容器都能指定為flex容器.
給容器加上display:flex后,他的子盒子會自動成為容器成員,默認情況下,所有的成員都排在同一條軸線上.
媒體查詢的作用是通過監聽屏幕的寬度來現實不停的內容的大小,從而能夠在不同尺寸的設備上正常顯示內容.
語法:
@media only screen and (max-width: 420px) {
body {
background-color: red;
}
}
手寫繼承
- 原型鏈繼承:
將父類實例對象 賦值 給子類的原型對象。
function SuperClass(){
this.name = 'super';
this.list = [];
}
SuperClass.prototype.sayHi = function(){
console.log('i am is super');
}
function SubCalss(){
this.name = 'sub'
}
SubCalss.prototype = new SuperClass();
const sub = new SubCalss();
console.log(sub); // Object { name:"sub" }
console.log(sub.name); //sub
console.log(sub.__proto__.name); // super
sub.sayHi();// i am is super
const sub1 = new SubCalss();
sub1.list.push('eee')
console.log(sub.list); // [ "eee" ]
- 借用構造函數
調用父類的構造函數
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
// function Chinese(name,age,gender,skin){
// this.name = name;
// this.age = age;
// this.gender = gender;
// this.skin = skin;
// }
function Chinese(name,age,gender,skin){
Person.call(this,name,age,gender);
this.skin = skin
}
const person = new Chinese('zs',18,'nan','red')
console.log(person.name); // zs
- 組合繼承
原型鏈繼承 + 借用構造函數繼承
function Person(name,age,gender){
this.name = name;
this.age = age;
this.gender = gender;
}
Person.prototype.sayHi = function(){
console.log(`hello ${this.name}`);
}
function Chinese(name,age,gender,skin){
Person.call(this,name,age,gender);
this.skin = skin
}
Chinese.prototype = new Person();
let zs = new Chinese('zs',18,'male','red')
zs.sayHi() // hello zs
手寫節流防抖並說明區別
- 節流函數 throttle
規定在一個單位時間內,只能觸發一次函數。如果這個單位時間內觸發多次函數,只有一次生效。
let btn = document.querySelector('button')
let the = throttle(function(){
console.log(this);
},3000)
btn.onclick = the;
//------------------------------------
function throttle(fn, wait) {
// 當前時間
let prev = new Date();
// 返回方法調用
return function() {
// 獲取當前時間
const now = new Date();
if (now - prev > wait) {
// 時間到了之后調用方法並且傳入 this
fn.apply(this);
// 更新時間
prev = new Date();
}
}
}
- 防抖 debounce
延遲執行/在事件被觸發n秒后再執行回調,如果在這n秒內又被觸發,則重新計時,重新出發定時器。
function throttle(fun,time){
// 定時器
let timeOut;
// 返回方法調用
return function(){
clearTimeout(timeOut)
// 沒達到時間的開啟一個定時器
timeOut = setTimeout(() => {
// 定時器時間到了之后調用方法
fun.call(this)
},time);
}
}
Ajax 二次封裝
function axios(options){
// 創建 promise 對象
return new Promise((resolve,reject) => {
// 1 創建一個 xhr 對象
const xhr = new XMLHttpRequest()
// 2 設置請求方式和地址
xhr.open(options.methods,options.url)
// 3 設置請求體,發送請求
xhr.send(null)
// 4 監聽結果 xhr 狀態:4 才行
xhr.onreadystatechange = function(){
if(xhr.status === 4){
if(xhr.status === 200){
console.log('成功')
resolve(JSON.parse(xhr.responseText))
} else {
console.log('失敗')
reject(new Error(xhr.responseText))
}
}
}
})
}
斐波那契數列以及優化思路
耗時:普通遞歸 > 改進遞歸 > for循環
- 遞歸
①普通遞歸:運行大了會卡死
function fibonacci(n) {
if (n == 1 || n == 2) {
return 1
};
return fibonacci(n - 2) + fibonacci(n - 1);
}
fibonacci(30)
改進:
②改進遞歸-前兩位數字做成參數
function fibonacci(n) {
function fib(n, v1, v2) {
if (n == 1)
return v1;
if (n == 2)
return v2;
else
return fib(n - 1, v2, v1 + v2)
}
return fib(n, 1, 1)
}
fibonacci(30)
③改進遞歸-里用閉包把運算結果存儲在數組里
var fibonacci = function () {
let memo = [0, 1];
let fib = function (n) {
if (memo[n] == undefined) {
memo[n] = fib(n - 2) + fib(n - 1)
}
return memo[n]
}
return fib;
}()
fibonacci(30)
③-1 改進遞歸-摘除存儲計算結果的函數功能
var memoizer = function (func) {
let memo = [];
return function (n) {
if (memo[n] == undefined) {
memo[n] = func(n)
}
return memo[n]
}
};
var fibonacci=memoizer(function(n){
if (n == 1 || n == 2) {
return 1
};
return fibonacci(n - 2) + fibonacci(n - 1);
})
fibonacci(30)
- 循環
① for 循環
function fibonacci(n) {
var n1 = 1, n2 = 1, sum;
for (let i = 2; i < n; i++) {
sum = n1 + n2
n1 = n2
n2 = sum
}
return sum
}
fibonacci(30)
② for循環 + 解構賦值
var fibonacci = function (n) {
let n1 = 1; n2 = 1;
for (let i = 2; i < n; i++) {
[n1, n2] = [n2, n1 + n2]
}
return n2
}
fibonacci(30)
webpack 怎么實現兼容
就是開發模式和生產模式都給出獨立的配置文件,兩邊互不干擾,本地調試調用開發模式的配置文件編譯,最終發布包里的文件由編譯環境調用生產模式的配置文件編譯生成。
webpack 流程
1、建目錄 dist , src/main.js
2、初始化:yarn init -y
3、安裝包:yarn add webpack webpack-cli -D
4、在 package.json 中配置 script
"scripts":{ "build":"webpack ./src/main.js" }
打包 ./src/main.js 到 dist 目錄中去
5、yarn build
webpack 會不會配置 -loaders的配置
webpack 默認只認識 js 文件和 json 文件,但是 webpack 可以使用 loader 來加載預處理文件。
- 處理 css 文件
// 1 安裝依賴
yarn add style-loader css-loader -D
// 2 配置
modules:{
// loader 規則
rules:[
{
test:...,
use:['style-loader','css-loader']
}
]
}
- 分離 css 文件 mini-css-extract-plugin
// 1 安裝依賴
yarn add mini-css-extract-plugin -D
// 2 在 webpack.config.js 中引入這個模塊
// 引入分離 css 文件的 模塊
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
// 3 配置loaders
// 模塊加載
module: {
// loader的規則
rules: [
// 配置 css 文件的解析
{
test: /\.css$/,
use: [ // 根據官方文檔寫的,注意'css-loader'的書寫位置
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath:'../',
},
},
'css-loader'
]
},
],
}
// 4 插件的配置
plugins: [
new HtmlWebpackPlugin({ template: './public/index.html' }),
// 定義打包好的文件的存放路徑和文件名
new MiniCssExtractPlugin({
filename:'css/index.css'
})
],
- 處理 less 文件
yarn add less less-loader -D
// 配置 less 文件的解析
{
test: /\.less$/,
use: [
// 分離出 css 內容
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath:'../',
},
},
'css-loader',
'less-loader'
]
- 處理圖片 url-loader
1 下載依賴
yarn add url-loader file-loader -D
2 配置 loader
{
test: /\.(png|jpg|gif)$/i,
use: [
{
loader: 'url-loader',
options: {
// 超過 8k 就不轉 base64, 小於 8k 才轉
limit: 8 * 1024
}
}
]
}
- 使用 babel 處理高版本的 js
yarn add -D babel-loader @babel/core @babel/preset-env
// 配置規則
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
原型鏈
用 vue 寫項目會有哪些問題
- 列表進入詳情頁的傳參問題
例如商品列表頁面前往商品詳情頁面,需要傳一個商品id。比如在c頁面的路徑為 .../#/detail?id=1 ,可以看到傳了一個參數 id=1 ,刷新頁面還會存在,此時在c頁面可以通過id來獲取對應的詳情數據,獲取 id 的方式是
this.$route.query.id
vue傳參方式有:query、params
+ 動態路由傳參
區別:
1、query
通過path切換路由,params
通過name
切換路由
2、query
通過 this.$route.query
,params
通過this.$route.params
來傳參。
3、query傳參的 url 展現方式:/detail?id=1&user=123&identity=1&更多參數
4、params 動態路由傳參,一定要在路由中定義參數,然后在路由跳轉的時候必須加上參數,否則就是空白頁
- 本地開發環境請求服務器接口跨域的問題
可以更改axios的默認配置axios.defaults.baseURL = '/api'
- axios 封裝和 api 接口統一管理
axios封裝主要是進行請求的攔截和響應的攔截,在請求的攔截中可以攜帶 userToken,post請求頭、qs對post提交數據的序列化等。
-
UI庫按需加載
-
只在當前頁面中覆蓋ui庫組件中的樣式
①給第三方組件寫個class ---> 不優雅,存在全局污染和命名沖突的問題。
②通過深度選擇器解決 /deep/
- Vue-Awesome-Swiper 基本能解決所有的輪播需求
安裝
cnpm install vue-awesome-swiper --save
引入組件:import 'swiper/dist/css/swiper.css'
import { swiper,swiperSlide } from 'vue-awesome-swiper'
-
在config/index.js中設置
productionSourceMap:false
設置不生成 .map 文件 -
websocket 是啥
是html5出的一個持久化的協議,借用了http的協議來完成一部分握手,在握手階段是一樣的。
Vue.mixin的作用 (混入)
混入(mixin)是一種分發Vue組件中可復用功能的方式。混入對象可以包含任意組件選項。當組件使用混入對象時,所有混入對象的選項將被混入該組件本身的選項。
遞歸組件
就是組件里面自己調用自己
注意:如果沒有結束條件的情況直接遞歸的話會報錯的,所以需要加上結束條件
$attrs和$listeners
主要應用是實現多層嵌套傳遞
組件A與組件B通信一般都會使用組件B中轉,即A傳遞給B,B再給C,但是如果A到C組件之間嵌套的組件過多,需要傳遞的事件和屬性較多,會導致代碼繁瑣,代碼維護困難。在vue2.4中,為了解決該需求,引入了$attrs
和$listeners
,新增了inheritAttrs
選項。
-
$attrs
一個組件在父組件中被引用,$attrs
就是組件標簽上的靜態值(attr)和動態屬性值(:attr)的對象集合,這個集合不包含class
,style
和事件屬性 -
與 $props 比較
$props
必須在組件中注冊了props
才能拿到值,所以在嵌套層級比較深的組件中$attrs
拿值更加便捷 -
$listeners 的使用
包含了父作用域中的 (不含
.native
修飾器的)v-on
事件監聽器。它可以通過v-on="$listeners"
傳入內部組件。
兩點:
1、$listeners
是組件的內置屬性,它的值是父組件(不含.native
修飾器的)v-on
事件監聽器。
2、組件可以通過在自己的子組件上使用v-on="$listeners"
,進一步把值傳給自己的子組件。如果子組件已經綁定$listener
中同名的監聽器,則兩個監聽器函數會以冒泡的方式先后執行。
瀏覽器渲染過程
在地址欄中輸入后,按下 enter,會發生什么?
瀏覽器會判斷輸入的是一個地址,還是一個query。
1、瀏覽器會查詢本地緩存的dns列表,以獲取主機地址,若dns緩存未命中,則瀏覽器發出請求,進行查詢,查詢之后的結果緩存起來。
2、連接主機
3、發送請求
4、獲得一個服務器的響應
5、進行渲染
- 過程
1、解析html,得到一個DOM tree
2、解析CSS,得到CSS tree
3、兩者整合成渲染樹,render tree
4、布局
5、繪制
Vuex 頁面刷新數據丟失問題
1、使用localStorage實時保存vuex的數據
2、使用vuex-along插件
uni-app和element中自帶的bug
- uni-app:
①
input
高度更改不了---->解決:在外面包一層view
②v-show
在打包后不起作用 ---->可用v-if
或者通過class
綁定來解決這個問題
③Data.parse()
時間轉化為時間戳出現NaN
,但pc正常 ----> 將-
替換為/
④手機端uni.createSelectorQuery()
為空,但在PC正常 ----> 后面加一個.in(this)
,如果在函數內部還需要在函數外部重新聲明一下this
⑤number.toFixed()
方法報錯失效,但pc正常 ---->number
變量在真機運行的時候可能會變為string
數據類型.通過toFixed()
就無法進行運算了,所以使用parseInt()
或者parseFloat()
將number
變量變為數字類型
⑥真機測試null
會顯示出來而在PC則沒有關系 ---->在真機上加以判斷讓其不顯示或者寫一個三元運算即可
- element:
原生js輪播圖思路
1 首先是輪播切換,就是一個定時器,定時的調用這個切換函數,並且這里需要注意num的值,因為num切換到最后一張,下一張就應該是第一張。
2 接下來是點擊左右切換按鈕,左切換按鈕要留意切換到第一張的時候,需要將下一張定義為最后一張,右切換按鈕需要留意最后一張,下一張是第一張。
3 最后是小圓點點擊跳轉到對應的圖片。
Bus使用的弊端
必須先 on 監聽才能進行 emit 觸發 (用 vuex 解決)
原型鏈prototype和proto區別
prototype
是函數的一個屬性(每個函數都有一個prototype屬性),這個屬性是一個指針,指向一個對象。它是顯示修改對象的原型的屬性。__proto__
是一個對象擁有的內置屬性,__proto__
是js內部使用尋找原型鏈的屬性。簡單來說,在js中每個對象都會有一個__proto__
屬性,當我們訪問一個對象的屬性時,如果這個對象內部不存在這個屬性,那么他就會去__proto__
里找這個屬性,這個__proto__
又會有自己的__proto__
---->原型鏈
es6中遍歷對象
1、for...in
2、Object.key()---返回一個數組,包括對象自身的(不含繼承的)所有可枚舉屬性(不含Symbol屬性)
3、Object.getOwnPropertyNames(obj)---返回一個數組,包含對象自身的所有屬性(不包含Symbol屬性,包括不可枚舉屬性)
4、Reflect.ownKeys(obj)---返回一個數組,包含對象自身的所有屬性,不管屬性名是Symbol或字符串,也不管是否可枚舉
數據類型 (8種)
ES5:
Number,String,Boolean,undefined,object,Null
ES6:Symbol (這種類型的對象永不相等,即始創建的時候傳入相同的值,可以解決屬性名沖突的問題,作為標記)BigInt
:操作大整數
檢測數據類型
1、typeof 一般主要用來檢測基本數據類型,因為它檢測引用數據類型返回的都是object(檢測null返回的也是object)
2、instanceof 這個方法主要是用來准確地檢測引用數據類型(不能用來檢測基本數據類型)
3、Object.prototype.toString() 可以用來准確地檢測所有數據類型
updated 的使用場景
比如即時聊天項目,打開當前會話加載完消息后需要自動滾到窗口最底部,可以用settimeout解決,但是時間不好控制,太短頁面還沒渲染滾動的高度不准確影響用戶體驗,可用updated
深拷貝實現方法思路
1、Json.stringify和Json.parse
//通過js的內置對象JSON來進行數組對象的深拷貝
function deepClone(obj) {
let _obj = JSON.stringify(obj);
let objClone = JSON.parse(_obj);
return objClone;
}
2、Object.assign()拷貝
3、通過JQuery的extend方法實現深拷貝
let $ = require('jquery');
let obj1 = {
a: 1,
b: {
f: {
g: 1
}
},
c: [1, 2, 3]
};
let obj2 = $.extend(true, {}, obj1);
4、lodash.cloneDeep()實現深拷貝
let _ = require('lodash');
let obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
let obj2 = _.cloneDeep(obj1);
5、使用遞歸的方式實現深拷貝
function _deepClone(source) {
let target;
if (typeof source === 'object') {
target = Array.isArray(source) ? [] : {}
for (let key in source) {
if (source.hasOwnProperty(key)) {
if (typeof source[key] !== 'object') {
target[key] = source[key]
} else {
target[key] = _deepClone(source[key])
}
}
}
} else {
target = source
}
return target
}
路由懶加載
為了使首屏組件加載速度更快一些,解決白屏問題
常用方法:Vue異步組件和ES中的import
Vue異步組件:component:resolve=>(require(['需要加載的路由的地址']),resolve)
import方法(最常用):const HelloWorld = ()=>import('需要加載的模塊地址')
token 存在哪里
state + localStorage
ajax 原理
簡單來說通過
XmlHttpRequest
對象來向服務器發送異步請求,從服務器獲得數據,然后用js來操作DOM而更新頁面,其中最關鍵的是從服務器獲得請求數據。
優點:
<1>.無刷新更新數據。
<2>.異步與服務器通信。
<3>.前端和后端負載平衡。
<4>.基於標准被廣泛支持。
<5>.界面與應用分離。
缺點:
<1>.AJAX干掉了Back和History功能,即對瀏覽器機制的破壞。
<2>.AJAX的安全問題。
<3>.對搜索引擎支持較弱。
<4>.破壞程序的異常處理機制。
<5>.違背URL和資源定位的初衷。
<6>.AJAX不能很好支持移動設備。
<7>.客戶端過肥,太多客戶端代碼造成開發上的成本。
axios中before形式
vue解決刷新頁面 vuex數據、params參數消失的問題
關於刷新頁面數據消失原因有兩種,一是通過路由 params 傳值,二是 vuex 傳值
一、通過路由params傳值
路由傳值有兩種方式,params和query,params傳值刷新頁面是要消失的,然而query卻不會,兩者的區別就在於query會把傳遞的參數顯示在url地址中,就像這樣:/adminUser/001001001?jum=001001001211,參數過多的話url地址會變得非常難看,如果你並不在意url地址難看與否,那么你可以不使用params,而是使用query,只是切換個單詞而已,這是一種方法(不是最優辦法)。還有一種方法,就是在定義路由的時候,給path設定參數。
二、使用vuex
需要一個全局的方法來,將store的數據存儲在localStorage里
在App.vue中,created初始化生命周期中寫入以下方法:
//在頁面刷新時將vuex里的信息保存到localStorage里
window.addEventListener("beforeunload",()=>{
localStorage.setItem("messageStore",JSON.stringify(this.$store.state))
})
//在頁面加載時讀取localStorage里的狀態信息
localStorage.getItem("messageStore") && this.$store.replaceState(Object.assign(this.$store.state,JSON.parse(localStorage.getItem("messageStore"))));
// replaceState 這個方法呢,查了api,就是替換 store 的根狀態,然后通過對象賦值assign將localStorage進行賦值
// beforeunload這個方法是在頁面刷新時觸發的,將store中的信息存入localStorage
vue組件中的data為啥必須是函數
data是一個函數時,每個組件實例都有自己的作用域,每個實例相互獨立,不會相互影響.