版游戲一直占據著游戲界的半壁江山,但是對于H5游戲來說,它本身沒有權利要求瀏覽器橫屏,這給橫版游戲很大的限制。本文探討在Phaser中,如何完美地解決這個問題。
Phaser的屏幕適配問題是游戲開發中討論最多的問題,也是很關鍵的一個問題。稍微用過Phaser的同學都應該知道了,Phaser中的ScaleManager是專門用來解決Phaser屏幕適配問題的。如果不知道ScaleManager是什么的同學,可以查看官方文檔。
在了解了ScaleManager之后,我們還會有兩個問題。第一、ScaleManager有沒有辦法做到不變形,有能充滿整個屏幕?第二、怎么讓瀏覽器橫屏?
首先,我們來說第一個問題,這其實是一個數學問題。這個問題的本質是,兩個寬高比不想等的矩形,有沒有辦法通過等比縮放讓它們完全重疊?相信任何一個小學數學學得還不錯的人都能給出答案,不可能。
但是有沒有辦法解決這個問題呢?當然有,那就是動態設置游戲區域,拿到屏幕大小之后再等比縮放成一個游戲區域大小不就行了。但是這會帶來一個問題,那就是你的元素無法按照坐標來定位了,因為你不知道游戲的寬高是多少,它和屏幕尺寸有關。這在大多數情況下會讓你很難受,但是在一種情況下不會,那就是你的世界大小比你的游戲區域大,這樣,你可以固定世界大小,然后元素按照世界大小來定位。
當然,我們還有別的方法,那就是在html中做一些背景,可以讓游戲融入到背景中,而不是出現兩邊的黑邊或者白邊。具體使用什么方法,就看大家自己的實際情況了。
接下來說說本文重頭戲:橫屏適配。
html能不能讓瀏覽器強制橫屏,phaser中設置了橫屏為什么沒有用?答案肯定是不能,也沒有用。其實道理很簡單,因為瀏覽器是html的環境,html是沒有權利去改變它外在的環境的,至少現在不能。
所以我們會看到很多游戲,打開之后,它建議你橫屏玩耍。但是這樣其實代價很大,因為很多人并不知道怎么橫屏。其實我想要的是這樣的效果:
也就是雖然是豎屏,但是游戲以橫屏的樣子展示,這樣,再傻的用戶都知道要把手機橫過來玩吧?那么當用戶橫過手機的時候,有兩種情況,第一,用戶的手機設置了豎屏鎖定,即使橫過來,其實還是豎屏,這種情況沒問題。第二,用戶沒有設置豎屏鎖定,橫過來之后,系統切換成橫屏,那么我們這個旋轉90度的屏幕正好又錯位了?答案當然不是。
當屏幕橫過來的時候,我們就不旋轉90度,游戲依然完美呈現。下面來說說怎么實現的。
這個實現涉及到Phaser底層源碼,還有PIXI的一些機制,如果大家看不懂,可以跳過,直接看最后怎么用就行了。
首先,我們要考慮,旋轉90度應該怎么做,其實只要把世界旋轉90度,就可以了,但是世界旋轉了之后,它是按照那個點進行旋轉的,旋轉之后是否要進行坐標調整?看代碼。
Phaser.World.prototype.displayObjectUpdateTransform=function() { if(!game.scale.correct) { this.x=game.camera.y + game.width; this.y=-game.camera.x; this.rotation=Phaser.Math.degToRad(Phaser.Math.wrapAngle(90)); } else { this.x=-game.camera.x; this.y=-game.camera.y; this.rotation=0; } PIXI.DisplayObject.prototype.updateTransform.call(this); }
注意,這里調整坐標的時候,加入了camera的修正,這是因為在世界大小比游戲區域大小大的時候,camera就不一定在(0,0)點了,所以要考慮進去。
加了這個當然不夠,還需要在游戲中加入:
game.scale.onOrientationChange.add(function() { if(game.scale.isLandscape) { game.scale.correct=true; game.scale.setGameSize(WIDTH, HEIGHT); } else { game.scale.correct=false; game.scale.setGameSize(HEIGHT, WIDTH); } }, this)
每一次橫豎屏變化的時候,我們需要實時切換游戲的寬高,道理也是顯而易見的。橫屏的時候,你的游戲是1920x1080,豎屏的時候,你的游戲就應該是1080x1920,這樣才能正好匹配屏幕。
怎么使用也很簡單,在游戲的BootState里面加入這些代碼就行,其余的事情和之前的做法一樣,只是有一點提醒大家,你的game.width和game.height是多少呢?已經不一定了,所以在元素定位的時候不要用它們去定位。我在tacit中的做法是,全局定義好游戲寬高,后面所有定位按照它來做,就不會有問題了。
好了,橫屏適配就說到這里。
前言
文章涉及的內容可能不全面,但量很多,需要慢慢看。來源于各個地方,我花了很長的時間整理,希望對大家有幫助。但是難免會有打字的錯誤或理解的錯誤,我會及時的進行修改,旨在能幫到大家,謝謝。
Html相關
1 html語義化
意義:根據內容的結構化(內容語義化),選擇合適的標簽(代碼語義化)便于開發者閱讀和寫出更優雅的代碼的同時讓瀏覽器的爬蟲和機器很好地解析。 注意:
1.盡可能少的使用無語義的標簽div和span;
2.在語義不明顯時,既可以使用div或者p時,盡量用p, 因為p在默認情況下有上下間距,對兼容特殊終端有利;
3.不要使用純樣式標簽,如:b、font、u等,改用css設置。
4.需要強調的文本,可以包含在strong或者em標簽中(瀏覽器預設樣式,能用CSS指定就不用他們),strong默認樣式是加粗(不要用b),em是斜體(不用i);
5.使用表格時,標題要用caption,表頭用thead,主體部分用tbody包圍,尾部用tfoot包圍。表頭和一般單元格要區分開,表頭用th,單元格用td;
6.表單域要用fieldset標簽包起來,并用legend標簽說明表單的用途;
7.每個input標簽對應的說明文本都需要使用label標簽,并且通過為input設置id屬性,在lable標簽中設置for=someld來讓說明文本和相對應的input關聯起來。
新標簽:
2 meta viewport相關
<!DOCTYPE html> H5標準聲明,使用 HTML5 doctype,不區分大小寫 <head lang=”en”> 標準的 lang 屬性寫法 <meta charset=’utf-8′> 聲明文檔使用的字符編碼 <meta http-equiv=”X-UA-Compatible” content=”IE=edge,chrome=1″/> 優先使用 IE 最新版本和 Chrome <meta name=”description” content=”不超過150個字符”/> 頁面描述 <meta name=”keywords” content=””/> 頁面關鍵詞 <meta name=”author” content=”name, email@gmail.com”/> 網頁作者 <meta name=”robots” content=”index,follow”/> 搜索引擎抓取 <meta name=”viewport” content=”initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no”> 為移動設備添加 viewport <meta name=”apple-mobile-web-app-title” content=”標題”> iOS 設備 begin <meta name=”apple-mobile-web-app-capable” content=”yes”/> 添加到主屏后的標題(iOS 6 新增) 是否啟用 WebApp 全屏模式,刪除蘋果默認的工具欄和菜單欄 <meta name=”apple-itunes-app” content=”app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL”> 添加智能 App 廣告條 Smart App Banner(iOS 6+ Safari) <meta name=”apple-mobile-web-app-status-bar-style” content=”black”/> <meta name=”format-detection” content=”telphone=no, email=no”/> 設置蘋果工具欄顏色 <meta name=”renderer” content=”webkit”> 啟用360瀏覽器的極速模式(webkit) <meta http-equiv=”X-UA-Compatible” content=”IE=edge”> 避免IE使用兼容模式 <meta http-equiv=”Cache-Control” content=”no-siteapp” /> 不讓百度轉碼 <meta name=”HandheldFriendly” content=”true”> 針對手持設備優化,主要是針對一些老的不識別viewport的瀏覽器,比如黑莓 <meta name=”MobileOptimized” content=”320″> 微軟的老式瀏覽器 <meta name=”screen-orientation” content=”portrait”> uc強制豎屏 <meta name=”x5-orientation” content=”portrait”> QQ強制豎屏 <meta name=”full-screen” content=”yes”> UC強制全屏 <meta name=”x5-fullscreen” content=”true”> QQ強制全屏 <meta name=”browsermode” content=”application”> UC應用模式 <meta name=”x5-page-mode” content=”app”> QQ應用模式 <meta name=”msapplication-tap-highlight” content=”no”> windows phone 點擊無高光 設置頁面不緩存 <meta http-equiv=”pragma” content=”no-cache”> <meta http-equiv=”cache-control” content=”no-cache”> <meta http-equiv=”expires” content=”0″>
3 canvas 相關
使用前需要獲得上下文環境,暫不支持3d 常用api: 1.fillRect(x,y,width,height)實心矩形 2.strokeRect(x,y,width,height)空心矩形 3.fillText("Hello world",200,200);實心文字 4.strokeText("Hello world",200,300)空心文字 各種東西!!!
新標簽兼容低版本
CSS相關
1.盒模型
1.ie盒模型算上border、padding及自身(不算margin),標準的只算上自身窗體的大小 css設置方法如下
/* 標準模型 */ box-sizing:content-box; /*IE模型*/ box-sizing:border-box; 復制代碼
2.margin、border、padding、content由外到里 3.幾種獲得寬高的方式
4.拓展 各種獲得寬高的方式
5.邊距重疊解決方案(BFC) BFC原理
<section class="top"> <h1>上</h1> 這塊margin-bottom:30px; </section> <!-- 給下面這個塊添加一個父元素,在父元素上創建bfc --> <div style="overflow:hidden"> <section class="bottom"> <h1>下</h1> 這塊margin-top:50px; </section> </div>
css reset 和 normalize.css 有什么區別
居中方法
水平方向上
針對inline, 內聯塊inline-block, 內聯表inline-table, inline-flex元素及img,span,button等元素 .text_div{ text-align:center; } 復制代碼 不定寬塊狀元素居中 .text_div{ margin:0 auto;//且需要設置父級寬度 } 復制代碼 通過給父元素設置 float,然后給父元素設置 position:relative 和 left:50%,子元素設置 position:relative 和 left: -50% 來實現水平居中。 .wrap{ float:left; position:relative; left:50%; clear:both; } .wrap-center{ left:-50%; }
垂直居中
單行內聯(inline-)元素垂直居中 通過設置內聯元素的高度(height)和行高(line-height)相等,從而使元素垂直居中。 .text_div{ height: 120px; line-height: 120px; } 利用表布局 .father { display: table; } .children { display: table-cell; vertical-align: middle; text-align: center; } flex布局 .center-flex { display: flex; flex-direction: column;//上下排列 justify-content: center; } 絕對布局方式 已知高度 .parent { position: relative; } .child { position: absolute; top: 50%; height: 100px; margin-top: -50px; } 未知高度 .parent { position: relative; } .child { position: absolute; top: 50%; transform: translateY(-50%); }
垂直水平居中根據上方結合
flex方式 .parent { display: flex; justify-content: center; align-items: center; } grid方式 .parent { height: 140px; display: grid; } .child { margin: auto; }
css優先級確定
bfc內容見盒模型
如何清除浮動
不清楚浮動會發生高度塌陷:浮動元素父元素高度自適應(父元素不寫高度時,子元素寫了浮動后,父元素會發生高度塌陷)
.float_div:after{ content:"."; clear:both; display:block; height:0; overflow:hidden; visibility:hidden; } .float_div{ zoom:1 }
自適應布局
思路:
畫三角形
#item { width: 0; height: 0; border-left: 50px solid transparent; border-right: 50px solid transparent; border-top: 50px solid transparent; border-bottom: 50px solid blue; background: white; }
link @import導入css
animation
長寬比方案
display相關
JavaScript相關
1 ["1", "2", "3"].map(parseInt)
首先, map接受兩個參數, 一個回調函數 callback, 一個回調函數的this值 其中回調函數接受三個參數 currentValue, index, arrary; 而題目中, map只傳入了回調函數--parseInt. 其次, parseInt 只接受兩個兩個參數 string, radix(基數). 本題理解來說也就是key與 index 所以本題即問 parseInt('1', 0); parseInt('2', 1); parseInt('3', 2); parseInt(string, radix) string 必需。要被解析的字符串。 radix 可選。表示要解析的數字的基數。該值介于 2 ~ 36 之間。 如果省略該參數或其值為 0,則數字將以 10 為基礎來解析。如果它以 “0x” 或 “0X” 開頭,將以 16 為基數。
2 [[3,2,1].reduce(Math.pow), [].reduce(Math.pow)]
arr.reduce(callback[, initialValue]) reduce接受兩個參數, 一個回調, 一個初始值. 回調函數接受四個參數 previousValue, currentValue, currentIndex, array 需要注意的是 If the array is empty and no initialValue was provided, TypeError would be thrown. 所以第二個表達式會報異常. 第一個表達式等價于 Math.pow(3, 2)=> 9; Math.pow(9, 1)=>9
3
var ary=[0,1,2]; ary[10]=10; ary.filter(function(x) { return x===undefined;}); 我們看到在迭代這個數組的時候, 首先檢查了這個索引值是不是數組的一個屬性, 那么我們測試一下. 0 in ary;=> true 3 in ary;=> false 10 in ary;=> true 也就是說 從 3 - 9 都是沒有初始化的bug !, 這些索引并不存在與數組中. 在 array 的函數調用的時候是會跳過這些坑的.
4 [typeof null, null instanceof Object]
typeof 返回一個表示類型的字符串. instanceof 運算符用來檢測 constructor.prototype 是否存在于參數 object 的原型鏈上. type result Undefined "undefined" Null "object" Boolean "boolean" Number "number" String "string" Symbol "symbol" Host object Implementation-dependent Function "function" Object "object"
5 js數據類型
1.number;
2.string;
3.boolean;
4.undefined;
5.null;
6.symbol(ES6新增,文章后面有對著新類型的解釋)Symbol 生成一個全局唯一的值。
7.Object.(包括Object,Array,Function)
8.BigInt:JavaScript新增 任意精度整數
6 promise 用法
定義 var promise=new Promise(function(resolve, reject) { // ... some code if (/* 異步操作成功 */){ resolve(value); } else { reject(error); } }); 使用 promise.then(function(value) { // success }, function(error) { // failure }); //等價于: promise.then(function(){ //success }).catch(function(){ //failure }) 復制代碼
7 es6 promise ajax
定義 const myHttpClient=url=> { return new Promise((resolve, reject)=> { let client=new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange=handler; client.responseType="json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if (this.readyState !==4) { return; } if (this.status===200) { resolve(this.response); } else { reject(new Error(this.statusText)); } } }); }; 使用 myHttpClient('https://www.baidu.com').then(res=> { console.log(res); }).catch(error=> { console.log(error); }); 復制代碼
8閉包
function foo(x) { var tmp=3; return function (y) { alert(x + y + (++tmp)); } } var bar=foo(2); // bar 現在是一個閉包 bar(10); 結果是16 es6通常用let const塊級作用域代替, 閉包缺點,ie中會引起內存泄漏,嚴格來說是ie的缺點不是閉包的問題 復制代碼
9 什么是立即執行函數?使用立即執行函數的目的是什么?
常見兩種方式 1.(function(){...})() (function(x){ console.log(x); })(12345) 2.(function(){...}()) (function(x){ console.log(x); }(12345)) 作用 不破壞污染全局的命名空間,若需要使用,將其用變量傳入如 (function(window){...}(window)) 復制代碼
10 async/await 語法
作用:異步代碼的新方式 promise示例 const makeRequest=()=> { return getJSON() .then(data=> { if (data.needsAnotherRequest) { return makeAnotherRequest(data) .then(moreData=> { console.log(moreData) return moreData }) } else { console.log(data) return data } }) } async/await示例 const makeRequest=async ()=> { const data=await getJSON() if (data.needsAnotherRequest) { const moreData=await makeAnotherRequest(data); console.log(moreData) return moreData } else { console.log(data) return data } } 函數前面多了一個aync關鍵字。await關鍵字只能用在aync定義的函數內。async函數會隱式地返回一個promise,該promise的reosolve值就是函數return的值。(示例中reosolve值就是字符串"done")
11 深淺拷貝
let a={ aa: 1, bb: 2, cc: 3, dd: { ee: 5, }, ff: { gg: 6, } }; let d=JSON.parse(JSON.stringify(a));//深復制包含子對象 let c={...a};//拷貝一層但不包含子對象 let b=a;//淺拷貝 b.bb=22; c.cc=33; c.dd.ee=55; d.ff.gg=66; console.log(a); console.log(b); console.log(c); console.log(d);
12數組去重
思路1:定義一個新數組,并存放原數組的第一個元素,然后將元素組一一和新數組的元素對比,若不同則存放在新數組中 思路2:先將原數組排序,在與相鄰的進行比較,如果不同則存入新數組。 思路3:利用對象屬性存在的特性,如果沒有該屬性則存入新數組。 思路4(最常用):使用es6 set let arr=[1, 2, 3, 3, 5, 7, 2, 6, 8]; console.log([...new Set(arr)]);
13正則實現trim()功能
function myTrim(str) { let reg=/^\s+|\s+$/g; return str.replace(reg, ""); } console.log(myTrim(' asdf '));
14 JS原型
1.每個對象都有 __proto__ 屬性,但只有函數對象才有 prototype 屬性 2.個人粗略理解與python的類方法靜態方法實例方法差不多
15 es6 class
面向對象,java中類
16 JS 如何實現繼承
1.使用原型繼承(既繼承了父類的模板,又繼承了父類的原型對象。優點是繼承了父類的模板,又繼承了父類的原型對象,缺點就是父類實例傳參,不是子類實例化傳參,不符合常規語言的寫法) 2.使用call的方式(繼承了父類的模板,不繼承了父類的原型對象。優點是方便了子類實例傳參,缺點就是不繼承了父類的原型對象)
17 手寫jquery插件
(function ($) { $.fn.myPlugins=function (options) { //參數賦值 options=$.extend(defaults, options);//對象合并 this.each(function () { //執行代碼邏輯 }); }; })(jQuery); $(selector).myPlugins({參數});
18 數組合并去重排序
let arr1=[1, 25, 2, 26, 1234, 6, 213]; let arr2=[2, 6, 2134, 6, 31, 623]; let c=[...new Set([...arr1, ...arr2])].sort((a, b)=> { return a - b; });
19 call apply
作用:在函數調用時改變函數的執行上下文也就是this的值 區別:call采用不定長的參數列表,而apply使用一個參數數組。 性能優化圖
20 for 中setTimeOut
要為循環題創建不同的循環副本
21 sort函數
V8 引擎 sort 函數只給出了兩種排序 InsertionSort 和 QuickSort,數量小于10的數組使用 插入,比10大的數組則使用 快排。
22 navigator
23 jquery綁定方式
24 事件流向
25原生操作class
//判斷有無 function hasClass(ele, cls) { return ele.className.match(new RegExp("(\\s|^)" + cls + "(\\s|$)")); } //添加 function addClass(ele, cls) { if (!this.hasClass(ele, cls)) ele.className +=" " + cls; } //刪除 function removeClass(ele, cls) { if (hasClass(ele, cls)) { let reg=new RegExp("(\\s|^)" + cls + "(\\s|$)"); ele.className=ele.className.replace(reg, " "); } } html5中加入classList 一系列操作 兼容至IE10
DOM相關
dom事件模型
DOM之事件模型分腳本模型、內聯模型(同類一個,后者覆蓋)、動態綁定(同類多個) demo
<body> <!--行內綁定:腳本模型--> <button onclick="javascrpt:alert('Hello')">Hello1</button> <!--內聯模型--> <button onclick="showHello()">Hello2</button> <!--動態綁定--> <button id="btn3">Hello3</button> </body> <script> /*DOM0:同一個元素,同類事件只能添加一個,如果添加多個, * 后面添加的會覆蓋之前添加的*/ function shoeHello() { alert("Hello"); } var btn3=document.getElementById("btn3"); btn3.onclick=function () { alert("Hello"); } /*DOM2:可以給同一個元素添加多個同類事件*/ btn3.addEventListener("click",function () { alert("hello1"); }); btn3.addEventListener("click",function () { alert("hello2"); }) if (btn3.attachEvent){ /*IE*/ btn3.attachEvent("onclick",function () { alert("IE Hello1"); }) }else { /*W3C*/ btn3.addEventListener("click",function () { alert("W3C Hello"); }) } </script>
冒泡解釋:當點擊一個元素觸發事件時. 事件會先從元素的最外層父元素一層一層進入到觸發的元素, 然后在從觸發元素一層一層返回到最外層父元素, 從最外層一層一層進入的階段叫事件捕獲階段, 從最里層一層一層往外的階段叫事件冒泡,
移動端觸摸事件
①touchstart:當手指觸碰到屏幕的時候觸發 ②touchmove:當手指在屏幕上滑動的時候觸發 ③touchend:當手指離開屏幕的時候時候觸發 ④touchcancel事件:當系統停止跟蹤觸摸的時候觸發(這個事件很少會用,一般不做深入研究)。 電話接入或者彈出信息等其他事件切入 event:
每個touch對象包含的屬性
事件委托
參考定義:事件委托就是利用事件冒泡,只指定一個事件處理程序,就可以管理某一類型的所有事件 好處:給重復的節點添加相同操作,減少dom交互,提高性能 實現思路:給父組件添加事件,通過事件冒泡,排查元素是否為指定元素,并進行系列操作
HTTP相關
1 常見狀態碼
2開頭 (請求成功)表示成功處理了請求的狀態代碼。
200 (成功) 服務器已成功處理了請求。 通常,這表示服務器提供了請求的網頁。 201 (已創建) 請求成功并且服務器創建了新的資源。 202 (已接受) 服務器已接受請求,但尚未處理。 203 (非授權信息) 服務器已成功處理了請求,但返回的信息可能來自另一來源。 204 (無內容) 服務器成功處理了請求,但沒有返回任何內容。 205 (重置內容) 服務器成功處理了請求,但沒有返回任何內容。 206 (部分內容) 服務器成功處理了部分 GET 請求。
3開頭 (請求被重定向)表示要完成請求,需要進一步操作。 通常,這些狀態代碼用來重定向。
300 (多種選擇) 針對請求,服務器可執行多種操作。 服務器可根據請求者 (user agent) 選擇一項操作,或提供操作列表供請求者選擇。 301 (永久移動) 請求的網頁已永久移動到新位置。 服務器返回此響應(對 GET 或 HEAD 請求的響應)時,會自動將請求者轉到新位置。 302 (臨時移動) 服務器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來進行以后的請求。 303 (查看其他位置) 請求者應當對不同的位置使用單獨的 GET 請求來檢索響應時,服務器返回此代碼。 304 (未修改) 自從上次請求后,請求的網頁未修改過。 服務器返回此響應時,不會返回網頁內容。 305 (使用代理) 請求者只能使用代理訪問請求的網頁。 如果服務器返回此響應,還表示請求者應使用代理。 307 (臨時重定向) 服務器目前從不同位置的網頁響應請求,但請求者應繼續使用原有位置來進行以后的請求。
4開頭 (請求錯誤)這些狀態代碼表示請求可能出錯,妨礙了服務器的處理。
400 (錯誤請求) 服務器不理解請求的語法。 401 (未授權) 請求要求身份驗證。 對于需要登錄的網頁,服務器可能返回此響應。 403 (禁止) 服務器拒絕請求。 404 (未找到) 服務器找不到請求的網頁。 405 (方法禁用) 禁用請求中指定的方法。 406 (不接受) 無法使用請求的內容特性響應請求的網頁。 407 (需要代理授權) 此狀態代碼與 401(未授權)類似,但指定請求者應當授權使用代理。 408 (請求超時) 服務器等候請求時發生超時。 409 (沖突) 服務器在完成請求時發生沖突。 服務器必須在響應中包含有關沖突的信息。 410 (已刪除) 如果請求的資源已永久刪除,服務器就會返回此響應。 411 (需要有效長度) 服務器不接受不含有效內容長度標頭字段的請求。 412 (未滿足前提條件) 服務器未滿足請求者在請求中設置的其中一個前提條件。 413 (請求實體過大) 服務器無法處理請求,因為請求實體過大,超出服務器的處理能力。 414 (請求的 URI 過長) 請求的 URI(通常為網址)過長,服務器無法處理。 415 (不支持的媒體類型) 請求的格式不受請求頁面的支持。 416 (請求范圍不符合要求) 如果頁面無法提供請求的范圍,則服務器會返回此狀態代碼。 417 (未滿足期望值) 服務器未滿足"期望"請求標頭字段的要求。
5開頭(服務器錯誤)這些狀態代碼表示服務器在嘗試處理請求時發生內部錯誤。 這些錯誤可能是服務器本身的錯誤,而不是請求出錯。
500 (服務器內部錯誤) 服務器遇到錯誤,無法完成請求。 501 (尚未實施) 服務器不具備完成請求的功能。 例如,服務器無法識別請求方法時可能會返回此代碼。 502 (錯誤網關) 服務器作為網關或代理,從上游服務器收到無效響應。 503 (服務不可用) 服務器目前無法使用(由于超載或停機維護)。 通常,這只是暫時狀態。 504 (網關超時) 服務器作為網關或代理,但是沒有及時從上游服務器收到請求。 505 (HTTP 版本不受支持) 服務器不支持請求中所用的 HTTP 協議版本。
緩存
2. cdn
Cache-Control 和 Etag 的區別
如下圖
Cookie sessionStorage localStorage
共同點:都是保存在瀏覽器端,且同源的。 區別:cookie數據始終在同源的http請求中攜帶,即cookie在瀏覽器和服務器間來回傳遞。而sessionStorage和localStorage不會自動把數據發給服務器,僅在本地保存。cookie數據不能超過4k(適合保存小數據)。 sessionStorage和localStorage容量較大,數據有效期不同,sessionStorage:僅在當前瀏覽器窗口關閉前有效。localStorage:始終有效,窗口或瀏覽器關閉也一直保存,需手動清楚;cookie只在設置的cookie過期時間之前一直有效,即使窗口或瀏覽器關閉。作用域不同。 sessionStorage不在不同的瀏覽器窗口中共享;localStorage 在所有同源窗口中都是共享的;cookie也是在所有同源窗口中都是共享的。
應用場景:localStorage:常用于長期登錄(+判斷用戶是否已登錄),適合長期保存在本地的數據。sessionStorage :敏感賬號一次性登錄; cookies與服務器交互。
GET POST區別
請求行,請求頭,請求體詳解
1,2,3請求行,4請求體,5請求體跨域、JSONP 、CORS、postMessage
跨域概念解釋:當前發起請求的域與該請求指向的資源所在的域不一樣。這里的域指的是這樣的一個概念:我們認為若協議 + 域名 + 端口號均相同,那么就是同域。 如下表
jsonp實現
原生 <script> var script=document.createElement('script'); script.type='text/javascript'; // 傳參并指定回調執行函數為onBack script.src='http://www.domain2.com:8080/login?user=admin&callback=onBack'; document.head.appendChild(script); // 回調執行函數 function onBack(res) { alert(JSON.stringify(res)); } </script> jquery $.ajax({ url: 'http://www.domain2.com:8080/login', type: 'get', dataType: 'jsonp', // 請求方式為jsonp jsonpCallback: "onBack", // 自定義回調函數名 data: {} }); vue this.$http.jsonp('http://www.domain2.com:8080/login', { params: {}, jsonp: 'onBack' }).then((res)=> { console.log(res); }) 配合的后端node實現,其他服務器語言也可以 const querystring=require('querystring'); const http=require('http'); const server=http.createServer(); server.on('request', function(req, res) { var params=qs.parse(req.url.split('?')[1]); var fn=params.callback; // jsonp返回設置 res.writeHead(200, { 'Content-Type': 'text/javascript' }); res.write(fn + '(' + JSON.stringify(params) + ')'); res.end(); }); server.listen('8080'); jsoup缺點只能實現get請求
CORS:跨源資源共享 Cross-Origin Resource Sharing(CORS),通常服務器設置,若帶cookie請求,則前后端都需要設置 后端常見設置 response.setHeader("Access-Control-Allow-Origin", "www.domain1.com"); // 若有端口需寫全(協議+域名+端口),允許那些外源請求 response.setHeader("Access-Control-Allow-Credentials", "true"); //是否需要驗證
前端示例
原生 var xhr=new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容 // 前端設置是否帶cookie xhr.withCredentials=true; xhr.open('post', 'http://www.domain2.com:8080/login', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('user=admin'); xhr.onreadystatechange=function() { if (xhr.readyState==4 && xhr.status==200) { alert(xhr.responseText); } jquery $.ajax({ ... xhrFields: { withCredentials: true // 前端設置是否帶cookie }, crossDomain: true, // 會讓請求頭中包含跨域的額外信息,但不會含cookie ... }); postMessage(data,origin)方法接受兩個參數 demo a.html <iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe> <script> var iframe=document.getElementById('iframe'); iframe.onload=function() { var data={ name: 'aym' }; // 向domain2傳送跨域數據 iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com'); }; // 接受domain2返回數據 window.addEventListener('message', function(e) { alert('data from domain2 ---> ' + e.data); }, false); </script> b.html 與a.html不同源 <script> // 接收domain1的數據 window.addEventListener('message', function(e) { alert('data from domain1 ---> ' + e.data); var data=JSON.parse(e.data); if (data) { data.number=16; // 處理后再發回domain1 window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com'); } }, false); </script>
osi模型
七層結構:物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層、應用層 tcp ucp屬于傳輸層;http屬于應用層
http2.0 http1
Vue相關
生命周期順序
組件通信
1.父傳子用props,父用子用ref 子調父用$emit,無關系用Bus
Vuex
組件通信庫,可以避免子組件無法改變props的弊端等 mutations 同步操作, 用于改變狀態 官方不推薦異步 action 執行多個mutaions,官方推薦異步操作 mapState、mapGetters、mapActions使用示例
<template> <el-dialog :visible.sync="show"></el-dialog> </template> <script> import {mapState} from 'vuex'; export default { computed:{ //這里的三點叫做 : 擴展運算符 ...mapState({ show:state=>state.dialog.show }), } } </script> 后兩者類似
VueRouter
定義 var routes=[ { path:"/one", component:導入的組件1 }, { path:"/two", component:導入的組件2 }, ]; // 定義路由組件 var router=new VueRouter({ routes }); // 定義路由 new Vue({ el:"#box", router }); 訪問設定的路由后 會將<router-view></router-view>替換成相應的模版 html訪問方式 <router-link to="/one">One</router-link>(類似a標簽) js訪問方式 this.$router.push('/one'); replace方式 替換當前頁面 攜帶的參數 可以通過this.$route.query.xxxx來獲取
Vue雙向綁定
原理:利用了 Object.defineProperty() 這個方法重新定義了對象獲取屬性值(get)和設置屬性值(set)的操作來實現的。 缺點:雙向數據流是自動管理狀態的, 但是在實際應用中會有很多不得不手動處理狀態變化的邏輯, 使得程序復雜度上升, 難以調試。
computed watch methods
用法: 區別:
算法相關
各種排序實現
相關數據
// 冒泡排序: 比較兩個相鄰的項,如果第一個大于第二個則交換他們的位置,元素項向上移動至正確的順序,就好像氣泡往上冒一樣 冒泡demo: function bubbleSort(arr) { let len=arr.length; for (let i=0; i < len; i++) { for (let j=0; j < len - 1 - i; j++) { if (arr[j] > arr[j+1]) { //相鄰元素兩兩對比 [arr[j + 1], arr[j]]=[arr[j], arr[j + 1]]; } } } return arr; } // 1) 首先,在數組中選擇一個中間項作為主元 // 2) 創建兩個指針,左邊的指向數組第一個項,右邊的指向最后一個項,移動左指針,直到找到一個比主元大的項,接著,移動右邊的指針,直到找到一個比主元小的項,然后交換它們。重復這個過程,直到 // 左側的指針超過了右側的指針。這個使比主元小的都在左側,比主元大的都在右側。這一步叫劃分操作 // 3) 接著,算法對劃分后的小數組(較主元小的值組成的的小數組, 以及較主元大的值組成的小數組)重復之前的兩個步驟,直到排序完成 快排demo: function quickSort(arr, left, right) { let len=arr.length; let partitionIndex; left=typeof left !=='number' ? 0 : left; right=typeof right !=='number' ? len - 1 : right; if (left < right) { partitionIndex=partition(arr, left, right); quickSort(arr, left, partitionIndex - 1); quickSort(arr, partitionIndex + 1, right); } return arr; } function partition(arr, left, right) { //分區操作 let pivot=left; //設定基準值(pivot) let index=pivot + 1; for (let i=index; i <=right; i++) { if (arr[i] < arr[pivot]) { [arr[i], arr[index]]=[arr[index], arr[i]]; index++; } } [arr[pivot], arr[index - 1]]=[arr[index - 1], arr[pivot]]; return index - 1; } // 選擇排序:大概思路是找到最小的放在第一位,找到第二小的放在第二位,以此類推 算法復雜度O(n^2) 選擇demo: function selectionSort(arr) { let len=arr.length; let minIndex; for (let i=0; i < len - 1; i++) { minIndex=i; for (let j=i + 1; j < len; j++) { if (arr[j] < arr[minIndex]) { //尋找最小的數 minIndex=j; //將最小數的索引保存 } } [arr[i], arr[minIndex]]=[arr[minIndex], arr[i]]; } return arr; } // 插入排序:每次排一個數組項,假設數組的第一項已經排序,接著,把第二項與第一項進行對比,第二項是該插入到第一項之前還是之后,第三項是該插入到第一項之前還是第一項之后還是第三項 插入demo: function insertionSort(arr) { let len=arr.length; let preIndex, current; for (let i=1; i < len; i++) { preIndex=i - 1; current=arr[i]; while (preIndex >=0 && arr[preIndex] > current) { arr[preIndex + 1]=arr[preIndex]; preIndex--; } arr[preIndex + 1]=current; } return arr; } // 歸并排序:Mozilla Firefox 使用歸并排序作為Array.prototype.sort的實現,而chrome使用快速排序的一個變體實現的,前面三種算法性能不好,但歸并排序性能不錯 算法復雜度O(nlog^n) // 歸并排序是一種分治算法。本質上就是把一個原始數組切分成較小的數組,直到每個小數組只有一個位置,接著把小數組歸并成較大的數組,在歸并過程中也會完成排序,直到最后只有一個排序完畢的大數組 歸并demo: function mergeSort(arr) { //采用自上而下的遞歸方法 let len=arr.length; if(len < 2) { return arr; } let middle=Math.floor(len / 2), left=arr.slice(0, middle), right=arr.slice(middle); return merge(mergeSort(left), mergeSort(right)); } function merge(left, right){ let result=[]; while (left.length && right.length) { if (left[0] <=right[0]) { result.push(left.shift()); } else { result.push(right.shift()); } } result.push(...left); result.push(...right); return result; } //堆排序:堆排序把數組當中二叉樹來排序而得名。 // 1)索引0是樹的根節點;2)除根節點為,任意節點N的父節點是N/2;3)節點L的左子節點是2*L;4)節點R的右子節點為2*R + 1 // 本質上就是先構建二叉樹,然后把根節點與最后一個進行交換,然后對剩下對元素進行二叉樹構建,進行交換,直到剩下最后一個 堆demo: var len; //因為聲明的多個函數都需要數據長度,所以把len設置成為全局變量 function buildMaxHeap(arr) { //建立大頂堆 len=arr.length; for (let i=Math.floor(len / 2); i >=0; i--) { heapify(arr, i); } } function heapify(arr, i) { //堆調整 let left=2 * i + 1; let right=2 * i + 2; let largest=i; if (left < len && arr[left] > arr[largest]) { largest=left; } if (right < len && arr[right] > arr[largest]) { largest=right; } if (largest !==i) { [arr[i], arr[largest]]=[arr[largest], arr[i]]; heapify(arr, largest); } } function heapSort(arr) { buildMaxHeap(arr); for (let i=arr.length - 1; i > 0; i--) { [arr[0],arr[i]]=[arr[i],arr[0]]; len--; heapify(arr, 0); } return arr; }
二分查找
思路 (1)首先,從有序數組的中間的元素開始搜索,如果該元素正好是目標元素(即要查找的元素),則搜索過程結束,否則進行下一步。 (2)如果目標元素大于或者小于中間元素,則在數組大于或小于中間元素的那一半區域查找,然后重復第一步的操作。 (3)如果某一步數組為空,則表示找不到目標元素。
// 非遞歸算法 function binary_search(arr, key) { let low=0; let high=arr.length - 1; while(low <=high){ let mid=parseInt((high + low) / 2); if(key===arr[mid]){ return mid; }else if(key > arr[mid]){ low=mid + 1; }else if(key < arr[mid]){ high=mid -1; }else{ return -1; } } } // 遞歸算法 function binary_search(arr,low, high, key) { if (low > high){ return -1; } let mid=parseInt((high + low) / 2); if(arr[mid]===key){ return mid; }else if (arr[mid] > key){ high=mid - 1; return binary_search(arr, low, high, key); }else if (arr[mid] < key){ low=mid + 1; return binary_search(arr, low, high, key); } }; 復制代碼
二叉樹相關
創建 function Node(data,left,right){ this.data=data;//數值 this.left=left;//左節點 this.right=right;//右節點 }; 插入二叉樹 function insert(node,data){ //創建一個新的節點 let newNode=new Node(data,null,null); //判斷是否存在根節點,沒有將新節點存入 if(node==null){ node=newNode; }else{ //獲取根節點 let current=node; let parent; while(true){ //將當前節點保存為父節點 parent=current; //將小的數據放在左節點 if(data < current.data){ //獲取當前節點的左節點 //判斷當前節點下的左節點是否有數據 current=current.left; if(current==null){ //如果沒有數據將新節點存入當前節點下的左節點 parent.left=newNode; break; } }else{ current=current.right; if(current==null){ parent.right=newNode; break; } } } } } 翻轉二叉樹 function invertTree(node) { if (node !==null) { node.left, node.right=node.left, node.right; invertTree(node.left); invertTree(node.right); } return node; } 查找鏈表中倒數第k個結點 2個思路 1:先遍歷出長度,然后查找長度-k+1的值 2:2個指針,一個指針先走k-1,然后兩個一起走到底部,后者就是結果
網絡安全相關
XSS CSRF
XSS(跨站腳本攻擊),惡意的注入html代碼,其他用戶訪問時,會被執行 特點:能注入惡意的HTML/JavaScript代碼到用戶瀏覽的網頁上,從而達到Cookie資料竊取、會話劫持、釣魚欺騙等攻擊 防御手段:
webpack相關
#####打包體積 優化思路
Loader
編寫一個loader
loader就是一個node模塊,它輸出了一個函數。當某種資源需要用這個loader轉換時,這個函數會被調用。并且,這個函數可以通過提供給它的this上下文訪問Loader API。 reverse-txt-loader 定義 module.exports=function(src) { //src是原文件內容(abcde),下面對內容進行處理,這里是反轉 var result=src.split('').reverse().join(''); //返回JavaScript源碼,必須是String或者Buffer return `module.exports='${result}'`; } 使用 { test: /\.txt$/, use: [ { './path/reverse-txt-loader' } ] },
plugins
使用范圍更廣,通常只需要require()然后添加到plugins數組中,且需要new一個
其他
URL到界面顯示發生了什么
POST / HTTP1.1 Host:www.wrox.com User-Agent:Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022) Content-Type:application/x-www-form-urlencoded Content-Length:40 Connection: Keep-Alive name=Professional%20Ajax&publisher=Wiley 第一部分:請求行,第一行說明是post請求,以及http1.1版本。 第二部分:請求頭部,第二行至第六行。 第三部分:空行,第七行的空行。 第四部分:請求數據,第八行。 4. 服務器處理請求并返回HTTP報文 后端處理返回http報文如下 HTTP/1.1 200 OK Date: Fri, 22 May 2009 06:07:21 GMT Content-Type: text/html; charset=UTF-8 <html> <head></head> <body> <!--body goes here--> </body> </html> 第一行為狀態行,(HTTP/1.1)表明HTTP版本為1.1版本,狀態碼為200,狀態消息為(ok) 第二行和第三行為消息報頭, Date:生成響應的日期和時間;Content-Type:指定了MIME類型的HTML(text/html),編碼類型是UTF-8 第三部分:空行,消息報頭后面的空行是必須的 第四部分:響應正文,服務器返回給客戶端的文本信息。 空行后面的html部分為響應正文。
組件封裝
目的:為了重用,提高開發效率和代碼質量 注意:低耦合,單一職責,可復用性,可維護性 常用操作:
JS異步加載
css與js動畫差異
負載均衡
多臺服務器共同協作,不讓其中某一臺或幾臺超額工作,發揮服務器的最大作用
CDN
內容分發網絡,基本思路是盡可能避開互聯網上有可能影響數據傳輸速度和穩定性的瓶頸和環節,使內容傳輸的更快、更穩定。
內存泄漏
定義:程序中己動態分配的堆內存由于某種原因程序未釋放或無法釋放引發的各種問題 js中可能出現的內存泄漏情況 結果:變慢,崩潰,延遲大等 原因:
避免策略:
babel原理
ES6、7代碼輸入 -> babylon進行解析 -> 得到AST(抽象語法樹)-> plugin用babel-traverse對AST樹進行遍歷轉譯 ->得到新的AST樹->用babel-generator通過AST樹生成ES5代碼、
promise
特性:Promise 對象的錯誤具有冒泡性質,會一直向后傳遞,直到被捕獲為止,也即是說,錯誤總會被下一個catch語句捕獲
js自定義事件
三要素: document.createEvent() event.initEvent() element.dispatchEvent()
demo: (en:自定義事件名稱,fn:事件處理函數,addEvent:為DOM元素添加自定義事件,triggerEvent:觸發自定義事件) window.onload=function(){ var demo=document.getElementById("demo"); demo.addEvent("test",function(){console.log("handler1")}); demo.addEvent("test",function(){console.log("handler2")}); demo.onclick=function(){ this.triggerEvent("test"); } } Element.prototype.addEvent=function(en,fn){ this.pools=this.pools || {}; if(en in this.pools){ this.pools[en].push(fn); }else{ this.pools[en]=[]; this.pools[en].push(fn); } } Element.prototype.triggerEvent=function(en){ if(en in this.pools){ var fns=this.pools[en]; for(var i=0,il=fns.length;i<il;i++){ fns[i](); } }else{ return; } }
es6模塊 commonjs amd cmd
前后端路由差別
1.后端每次路由請求都是重新訪問服務器
2.前端路由實際上只是JS根據URL來操作DOM元素,根據每個頁面需要的去服務端請求數據,返回數據后和模板進行組合。
文從移動互聯網廣告的定義、類型、策略、趨勢等角度,做了全局梳理與分析。
互聯網行業發展日新月異,互聯網廣告作為“伴生物”,也隨行業巨頭、行業風口流轉和追隨。
其中最重要的轉折莫過于,在4G大范圍普及,用戶注意力逐漸轉移到手機、平板電腦等移動設備的背景下,移動互聯網逐漸成為行業主流,互聯網廣告亦是如此。
據艾瑞咨詢數據,從2013到2019年,移動廣告占網絡廣告的比重從12.1%一路暴漲至82.8%,已成網絡廣告的核心。
移動廣告的崛起引人注目,趁著2019即將收尾,本文就當前移動互聯網廣告定義、類型和策略做一次全面的梳理和總結,并嗅探未來趨勢。
本文框架圖
移動互聯網廣告是通過移動設備(手機、PSP、平板電腦等)訪問移動APP或移動網頁時顯示的廣告。
具體來說,即Wap廣告、APP廣告兩類,這兩種廣告投放形式和展現模式有著很大的差異,前者依托于html架構,是展示在在瀏覽器網頁端的廣告,由網站的開發者獲取收益,份額占比較少,后者依托于OS操作系統架構,是展現在APP應用里面的廣告,是主流形式。
Wap廣告姑且不提,據筆者觀察,目前業內有兩種APP廣告劃分方法:
按照APP所屬行業進行劃分,可以分為搜索廣告、電商廣告、門戶及新聞廣告、垂直廣告(即豎屏視頻)、視頻廣告(即橫屏視頻)等。這種劃分形式適用于研究、分析機構,用來洞察各行業發展趨勢,比如18年抖音、快手APP的崛起,就極大的推升了垂直廣告份額的增長。
按照廣告位的屬性進行劃分,可以分為開屏廣告、橫幅廣告、插屏廣告、貼片廣告、信息流廣告、激勵廣告等。在筆者看來,這種劃分形式最為準確,廣告位是廣告主在廣告投放中直接的采買物,同一APP內也往往擁有多個廣告位,同一廣告位也有著多種形式。
開屏廣告(Splash Ad.)也被稱為啟動頁廣告,出現在 APP 啟動加載時,將廣告圖片或視頻展示固定時間(5秒),展示完畢后自動關閉并進入APP主頁面。
作為進入載體app的首要入口,開屏廣告在設計上有著尺寸大(APP內置最大的廣告位)、可跳過(一般放置于用戶較少觸碰的位置)的特點,基于此,開屏廣告的廣告可見性高、廣告效果好,是品牌廣告主的首選。
橫幅廣告(Banner Ad.)是網絡廣告最早采用的形式,也一直延用至移動端,以文字、圖片等富媒體形式,在 APP 首頁、發現頁、專題詳情頁等頁面的頂部(含下拉刷新)、底部或中部呈現。
相對于開屏廣告,橫幅廣告在移動端設計上有著尺寸小、位置偏(照顧用戶體驗、避免誤觸)、支持輪播(動態展示不同廣告主的文字鏈/圖片)的特點,基于此,橫幅廣告難以吸引用戶注意力,廣告可見性和廣告效果都較差。
插屏廣告(Interstitial Ad.)是觸發式廣告,在用戶做出相應的操作(如開啟、暫停、過關、跳轉、退出)后,彈出的以圖片、動圖、視頻等為表現形式的半屏或全屏廣告。
相較于橫幅廣告,插屏廣告曝光性強,很吸引用戶注意力,但容易引起用戶反感(打斷用戶正常使用APP的操作),立即關掉廣告。整體來看,插屏廣告影響用戶體驗,廣告可見性和廣告效果都很一般。
貼片廣告(Roll Ad.)即將廣告內容貼入視頻之中。可以分為視頻貼片和創可貼兩種形式,前者是將5s-60s不等的橫版視頻廣告,添加至視頻播放前、視頻播放中或視頻播放后這三個位置,后者將圖片/動圖等元素放在正在播放的視頻中。
視頻貼片廣告沿襲了PC時代的廣告位設計,整體廣告效果和視頻類型和質量相關較為密切,一般以傳統采買、競標的交易模式,而創可貼廣告可結合劇情設計,尺寸小的特點,使得用戶體驗更好,廣告可見性更高、廣告效果更好。
信息流廣告(Feeds Ad.)是當前APP最流行的形式,出現于有內容產出的APP,是與APP的日常內容(如一則資訊、動態、圖片、視頻)融為一體的廣告形式。
相較于其他廣告位,信息流廣告是延展性、可玩性最強的廣告位,目前可記錄的形式就有數十種,文字鏈,小圖、組圖、大圖、豎版視頻等等。
由于原生性、內容性較強,信息流廣告能夠最大限度地保護用戶體驗,還能形成二次傳播,廣告效果優于大多數廣告位。
搜索廣告(Search Ad.)也是觸發式廣告,用戶搜索關鍵詞后,在搜索聯想、搜索結果中出現廣告,一般為廣告主的APP/商品,或者是帶有推廣性質的內容,以信息流的形式呈現。一般是應用商店,電商,搜索工具類的主流廣告位,近年來,大型社交、資訊類APP也在加快布局。
不可置否,搜索廣告是轉化效果最強的廣告位,能夠形成用戶搜索——廣告引導——完成轉化(下載/下單)的環路。但提供搜索廣告的APP,需要有一定的搜索基數以及口碑(用戶信賴度),所以目前在移動廣告中,搜索廣告是應用門檻最高的廣告位。
激勵廣告(Incentive Ad.),是利用激勵讓用戶接受廣告或做出指定行為,比如下載APP、觀看視頻等。可以分為積分墻和激勵視頻兩種形式,前者用戶可以完成指定操作獲取積分,并兌換獎勵,后者則讓用戶完成指定操作,獲取權益,比如游戲復活,新增特權等。
互惠互利是激勵廣告的核心特點,對于APP來說,提供激勵廣告,能夠對付費意愿不強的用戶流量變現,對于用戶來說,能通過操作獲取相應的權益,提升APP使用體驗。整體來看,激勵廣告的點擊成本低,廣告效果好。
策略,是據形勢發展而制定的行動方針和斗爭方法。過去部分文章中經常將提升廣告效果的策略,如原生廣告,定性為可售賣的廣告位、混為一談。本部分筆者重新梳理和定義這些新策略:
原生廣告誕生于2011年、爆紅于2013年,發源于國外,流行至全球。
Solve Media給出的定義是:“原生廣告是指一種通過在信息流里發布具有相關性的內容產生價值,提升用戶體驗的特定商業模式。具體實現形式是將廣告作為內容的一部分植入到實際APP(PC)頁面設計中。
IDEAinside的創始人認為原生廣告有價值性、原生性、主動性這三個特點,簡而言之,原生廣告的內容是有價值的有意義,且和頁面融為一體,能夠讓用戶樂于閱讀和分享。
原生廣告的原理,經過近5年的發展和實踐,已經有了詳實的應用方式和案例:
從國內的發展節奏來看,互動廣告于16年年底摸索,在17年發跡、爆發,于18年行業井噴,并不斷涌現出新的形式和玩法。
互動廣告是一種廣告載體或廣告形式,其最大的價值是挖掘出了一個新的流量場景,并通過和用戶互動交流的方法,搭建廣告主和用戶之間的溝通橋梁。
不同于原生廣告融入當代廣告設計理念,互動廣告已經形成了完善的行業產業鏈,目前國內有豆盟(19年已上市)、推啊、互動推等數十家互動廣告企業,App開發者接入一個互動廣告SDK,交由互動廣告平臺分發售賣廣告,據筆者觀察,可以分為以下兩大類:
傳統活動廣告:廣告即“活動”,用戶可點擊APP上的活動icon(實質上是廣告位),進入活動H5參與小游戲,例如“轉盤抽獎、砸金蛋抽獎、刮刮卡抽獎”等,獲取廣告主的權益或福利,引導轉化。
互動視頻廣告:本質上也是活動廣告,但“青出于藍”,交互式體驗比較強,不僅流行,可玩性、定制性也強:
“一鏡到底”是指拍攝中沒有cut情況,運用一定技巧將作品一次性拍攝完成的拍攝手法,常見于一些短視頻,當它跨界到廣告中,會碰撞出什么樣的火花呢?2019年的聯動廣告給了我們答案。
聯動廣告是將多個廣告位進行聯動展示同一廣告主的創意的形式,持續吸引用戶的注意力,目前常見的是開屏廣告和信息流廣告的組合,有以下特點:
可以預見的是,聯動廣告在未來會結合營銷需要,推出更多新的形式,諸如多個信息流廣告位、多頁面廣告位的聯動,將重復曝光,多創意形態、傳播模式的營銷價值發揮得淋漓盡致。
據艾瑞咨詢發布的《2019年中國網絡廣告市場分析報告》,2018年移動廣告市場規模達到3663.0億元,移動廣告的整體市場增速遠遠高于網絡廣告市場增速,未來,預計2021年移動廣告占網絡廣告的比例將超過85%。
移動廣告份額繼續擴大的同時,程序化正在以前所未有的速度流行起來。通過InMobi Exchange的數據預估,2019年全球程序化移動廣告支出增長將超過250%,而中國程序化移動廣告支出增長也將達到111%,突破1900億人民幣。越來越多的廣告交易通過程序化購買的方式產生。
據CTR媒介智訊發布的《2019Q3中國廣告市場》的數據,經濟環境影響下,廣告主對2019年整體經濟市場的信心有所波動,致使中國廣告市場重新進入調整期。截至2019年前三季度,中國廣告市場整體下滑8.0%,近4年來首次下滑。
預算減少了,廣告主選擇把錢花在更有效果的媒體上。過去,品牌廣告是大多數廣告主的選擇,廣撒網,尋求長效影響力,價格不菲。而在經濟寒冬期,由于效果廣告能夠直接導向廣告主銷售KPI,因此也成為了企業的重點。諸如上文中提到的互動廣告、聯動廣告的營銷玩法,也得到了廣泛的開拓和應用。
歷經4年的發展,大流量平臺紛紛布局小程序領域,更多類型的小程序將接入各平臺。據iiMedia Research(艾媒咨詢)數據顯示,2019年中國小程序數量預計超650萬個,到2020年預計將超1400萬個。
小程序是基于網頁開發、不需要下載安裝即可使用的平臺內應用,不管是廣告主還是移動APP開發者都在布局,在微信、支付寶、百度推出自己的小程序。基于小程序的推廣和變現,也可以窺其一二:
虛假廣告治理:對虛假廣告的治理一直是國家相關部門的重點工作,近期沸沸揚揚的“歐萊雅”因虛假廣告被罰,移動互聯網不是法外之地,一旦傳播虛假廣告,媒體方等廣告服務方也將一同擔責,互聯網廣告準入機制已成形。
用戶隱私保護:4-5月,相關部門先后發布《互聯網個人信息安全保護指南》、《數據安全管理辦法(征求意見稿)》,明確指出用戶有拒絕精準廣告的權力,推動互聯網數據安全保護有法可依。
虛假流量監測:全國互聯網廣告監測中心正式啟動2年來,目前已實現對全國46個副省級以上行政區劃的1004家重點網站及4家廣告聯盟和電商平臺廣告數據的監測,互聯網廣告的違法率從開展監測前的7.1%降至1.98%。
“關注變化的,守住不變的。”
在廣告圈,年底販賣焦慮的文章一時風起云涌,“我太難(南了)”似乎成為了廣告人、廣告行業、廣告代理公司的真實寫照。艱難、焦慮、浮躁不如回歸本心,不如理清思路,重新出發。
作者:AdBright,公眾號(id:AdBright01),程序化廣告新人,分享相關程序化廣告技術、產品、市場知識。
本文由 @AdBright 原創發布于人人都是產品經理,未經許可,禁止轉載。
題圖來自Unsplash,基于CC0協議。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。