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技术内幕 强力推荐,作者业界良心。