週記-工作小總結

jkCaptain發表於2018-11-17

document.write

document.write 可以用來同步引入外部js。

如果不用模組載入的庫,我們引入外部js的方法是createElement script標籤,然後再監聽onload事件。

我們也可以直接在程式碼裡使用如下程式碼

docuemnt.write('http://xxx.com/xxx.js');

// xxxxxx 後續程式碼
複製程式碼

瀏覽器會等到document.write的js檔案載入並執行完之後,才會執行後續程式碼。不過要注意一點,外部js裡如果又引用裡其他的js,瀏覽器是不會等待其他的js檔案載入完的。

原生端和移動web端通訊方式

在app內部嵌入h5頁面,迫於webview的安全限制,有時會需要呼叫原生端提供的方法,來完成互動。(例如安卓webview不支援input file控制元件

通訊方式有三種:

① url攔截方式 - 不推薦

② JsBridge 方式 - 安全性較高,推薦

Android JsBridge

iOS JsBridge

內容較多,用法可參考文件。

③ 安卓使用註解,ios使用JSContext方式

原理:

安卓端暴露一個物件,並給這個物件新增方法。前端可以在window上下文獲取到這個物件。

iOS 可以獲取到 UIWebview 的當前 window 上下文, 也就是讓OC 和 JS 共享同一個 JSContext。

android :

// 只展示重要程式碼

// 獲取 webview
WebView mwebView = (WebView) findViewById(R.id.webview);
// 開啟js支援
mWebView.getSettings().setJavaScriptEnabled(true); 
// 設定本地呼叫物件, JSInterface 為 js與原生通訊的橋樑介面物件
mWebView.addJavascriptInterface(new JavaScriptInterface(this), "JSInterface");

/*------------------------------------------------------------------------------*/

// 定義可讓javascript呼叫的方法
public Class JavaScriptInterface {
    Context mContext;
    
    public JavaScriptInterface(Context c) {
        mContext = c;    
    }
    
    @JavascriptInterface    // 加註解
    public void showToast(String str) {
        Toast.makeText(mContext, str, Toast.LENGTH_LONG).show();
    }
}
複製程式碼

ios:

// 只展示重要程式碼

-(void)webViewDidFinishLoad:(UIWebView *)webView  
{  
    //獲取當前 JS 環境  
    JSContext *context=[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];   

    //其中 testLog 是 js 的方法名稱,賦給是一個 block 裡面是 iOS 程式碼  
    //此方法最終將列印出所有接收到的引數
    context[@"testLog"] = ^() {  
        NSArray *args = [JSContext currentArguments];  
        for (id obj in args) {  
            NSLog(@"%@",obj);  
        }  
    };  
}  
複製程式碼

javascript:

// Android
window.JSInterface.showToast('js呼叫安卓');

// ios
window.testLog('js呼叫ios');
複製程式碼
注意:WKweview支援用MessageHandler的方式進行通訊

ios:

// 1. WKWebView注入ScriptMessageHandler
 [wkWebView.configuration.userContentController 
    addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:@"xxxx"];
// 2. 提供setWebViewAppearance方法,這樣就能反射出H5即將傳來的字串@"setWebViewAppearance"
- (void)setWebViewAppearance {

}
複製程式碼

javascript:

// window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
window.webkit.messageHandlers.xxxx.postMessage(data);
複製程式碼

分析 vue 原始碼 parseHTML 函式的正規表示式

檔案路徑vue專案下: src/compiler/parser/html-parser

程式碼如下:

// Regular Expressions for parsing tags and attributes
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
// could use https://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-QName
// but for Vue templates we can enforce a simple charset
const ncname = '[a-zA-Z_][\\w\\-\\.]*'
const qnameCapture = `((?:${ncname}\\:)?${ncname})`
const startTagOpen = new RegExp(`^<${qnameCapture}`)
const startTagClose = /^\s*(\/?)>/
const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)
const doctype = /^<!DOCTYPE [^>]+>/i
// #7298: escape - to avoid being pased as HTML comment when inlined in page
const comment = /^<!\--/
const conditionalComment = /^<!\[/
複製程式碼

一個個來

const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/
複製程式碼

attribute

這個正則表示式式用來匹配html屬性的,如'class="adc def"'。

首先我們要清楚幾個知識點:

// 正則中表示式的含義
^  ---- 匹配輸入字串的開始位置,如果在方括號中,則表示排除
?: ---- 表示匹配但不捕獲結果
複製程式碼

這個正則咋一看又臭又長,讓人丈二和尚摸不著頭腦。我們可以試著把它拆分開再看,如圖:

週記-工作小總結

// ^\s*([^\s"'<>\/=]+)   從字串的開頭開始匹配,捕獲非空白字元,非單雙引號,非大於號小於號,非斜槓,非等號的任何字元, 這段是匹配屬性名稱用的。
// \s*(=)\s*   捕獲等號,等號前後可以有多個空白字元
// "([^"]*)"+  捕獲雙引號之間非雙引號的所有內容,也就是屬性的值
// '([^']*)'+  捕獲單引號之間非單雙引號的所有內容,也是匹配屬性值。因為html中允許屬性值寫單引號
// ([^\s"'=<>`]+) 捕獲非空白字元,非單雙引號,非等號,非大於號小於號,非反撇號的任何字元,用於匹配類似 readonly, disabled 之類可以不用寫屬性值的屬性

// 測試一下
const attribute = /^\s*([^\s"'<>\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/

console.log('class="some-class"'.match(attribute))  // 測試雙引號
// 輸出
[
    "class="some-class""
    "class"
    "="
    "some-class"
    undefined
    undefined
]
複製程式碼

ncname

ncname 的概念是不包含冒號(:)的 XML 名稱,關於XML名稱的規範在這裡能查到 XML 詞彙表, 也就是這個正規表示式是用來匹配規範的XML標籤名稱

const ncname = '[a-zA-Z_][\\w\\-\\.]*'
複製程式碼
// [a-zA-Z_]  匹配 a-z, A-z, 以及下劃線內的所有字元
// [\\w\\-\\.]* 匹配所有字母、數字、下劃線,中劃線,以及除換行符(\n、\r)之外的任何單個字元,
// 為什麼要雙斜槓,因為這個是字串,作為下一步 new RegExp 的引數。
複製程式碼

qname

qname 就是:<字首:標籤名稱>,也就是合法的XML標籤

const qnameCapture = `((?:${ncname}\\:)?${ncname})`

// 這個是 qnameCapture 字串的值 
// ((?:[a-zA-Z_][\w\-\.]*\:)?[a-zA-Z_][\w\-\.]*)
複製程式碼

startTagOpen

匹配開始標籤的標籤名稱

const startTagOpen = new RegExp(`^<${qnameCapture}`)

// 這個是 startTagOpen 正規表示式的值 
// /^<((?:[a-zA-Z_][\w\-\.]*\:)?[a-zA-Z_][\w\-\.]*)/
複製程式碼

endTag

匹配結束標籤的標籤名稱

const endTag = new RegExp(`^<\\/${qnameCapture}[^>]*>`)

// 這個是 endTag 正規表示式的值 
// /^<\/((?:[a-zA-Z_][\w\-\.]*\:)?[a-zA-Z_][\w\-\.]*)[^>]*>/
複製程式碼

doctype

匹配文件的DocType 型別

comment

匹配註釋

conditionalComment

匹配條件註釋

我印象中條件註釋語句是長下面這樣的

<!--[if IE 8 ]>
xxx
<![endif]-->
複製程式碼

可是這個表示式是這樣的

const conditionalComment = /^<!\[/
複製程式碼

所以這裡還沒搞清楚。

參考

Vue技術內幕 強力推薦,作者業界良心。