周记-工作小总结

937 阅读4分钟

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