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 方式 - 安全性較高,推薦
內容較多,用法可參考文件。
③ 安卓使用註解,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技術內幕 強力推薦,作者業界良心。