<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>JavaScript &#8211; 编程技术记录</title>
	<atom:link href="https://blog.z6z8.cn/category/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/javascript-%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.z6z8.cn</link>
	<description>世界你好!</description>
	<lastBuildDate>Thu, 26 May 2022 02:58:33 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>
	<item>
		<title>JavaScript兼容性检查(JavaScript compatibility check)</title>
		<link>https://blog.z6z8.cn/2022/05/26/javascript%e5%85%bc%e5%ae%b9%e6%80%a7%e6%a3%80%e6%9f%a5javascript-compatibility-check/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Thu, 26 May 2022 02:58:33 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=1223</guid>

					<description><![CDATA[查阅资料，找到以下 使用https://caniuse.com/ 数据， 对应的在线工具为 https://s [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>查阅资料，找到以下<br />
使用https://caniuse.com/ 数据， 对应的在线工具为 <a href="https://seedmanc.github.io/jscc/">https://seedmanc.github.io/jscc/</a> 。不过，这个在线工具还有待完善，检查结果漏了很多。</p>
<p>另外一个是，基于eslint的兼容性检查插件，<a href="https://github.com/amilajack/eslint-plugin-compat">https://github.com/amilajack/eslint-plugin-compat</a> ，推荐使用这个，可以配置各种浏览器、语法版本、很灵活</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>设置环境变量关闭react-scripts的sourceMap生成</title>
		<link>https://blog.z6z8.cn/2021/06/15/%e8%ae%be%e7%bd%ae%e7%8e%af%e5%a2%83%e5%8f%98%e9%87%8f%e5%85%b3%e9%97%adreact-scripts%e7%9a%84sourcemap%e7%94%9f%e6%88%90/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Tue, 15 Jun 2021 02:29:22 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[学习笔记]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=1052</guid>

					<description><![CDATA[关于GENERATE_SOURCEMAP的作用原理在这篇文章中介绍过。 http://139.155.43.7 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>关于<code>GENERATE_SOURCEMAP</code>的作用原理在这篇文章中介绍过。<br />
<a href="http://139.155.43.7/2019/10/15/create-react-app-%ef%bc%9a%e4%b8%8d%e4%ba%a7%e7%94%9fsource-map%e6%9d%a5%e7%bc%a9%e5%87%8f%e6%89%93%e5%8c%85%e6%96%87%e4%bb%b6%e4%bd%93%e7%a7%af/">http://139.155.43.7/2019/10/15/create-react-app-%ef%bc%9a%e4%b8%8d%e4%ba%a7%e7%94%9fsource-map%e6%9d%a5%e7%bc%a9%e5%87%8f%e6%89%93%e5%8c%85%e6%96%87%e4%bb%b6%e4%bd%93%e7%a7%af/</a></p>
<p>可以通过创建<code>.env</code>环境变量文件影响<code>react-scripts start/build</code>等命令。其实还有另一种更灵活的方式设置环境变量,使用<code>export</code>可以对不同的命令设置不同的环境变量.</p>
<p>打开<code>package.json</code>，找到<code>scripts</code>配置项</p>
<pre><code class="language-json">  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },</code></pre>
<p>添加<code>export GENERATE_SOURCEMAP=true/false</code>修改为以下配置</p>
<pre><code class="language-json">  "scripts": {
    "start": "export GENERATE_SOURCEMAP=true && react-scripts start",
    "build": "export GENERATE_SOURCEMAP=false && react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },</code></pre>
<p>这样，在运行start调试时，产生sourceMap方便调试；运行build命令时，关闭sourceMap减少产物文件大小。</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>React-ZLView 使用 x-y-width-height坐标布局</title>
		<link>https://blog.z6z8.cn/2021/05/28/zlview-%e4%bd%bf%e7%94%a8-x-y-width-height%e5%9d%90%e6%a0%87%e5%b8%83%e5%b1%80/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Fri, 28 May 2021 02:39:03 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[学习笔记]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=1021</guid>

					<description><![CDATA[https://github.com/gm958spanda/React-ZLView https://git [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://github.com/gm958spanda/React-ZLView">https://github.com/gm958spanda/React-ZLView</a><br />
<a href="https://gitee.com/gm958spanda/React-ZLView">https://gitee.com/gm958spanda/React-ZLView</a></p>
<h1>ZLView</h1>
<p>ZLView base react , super and sub  , coordinate by  x-y-width-height；基于react的视图，使用x-y-width-height坐标系</p>
<h2>示例</h2>
<p>使用多个div节点绘制正弦曲线<br />
<img decoding="async" src="/wp-content/uploads/2021/05/1.png" alt="" /></p>
<pre><code class="language-ts">
import React from 'react';
import * as zl from 'react-zlview'

class App extends React.Component
{
    private appView : zl.View | undefined;
    render()
    {
        if(this.appView === undefined)
        {
            this.appView = new zl.View();
            this.appView.x = 200;
            this.appView.y = 100;
            this.appView.width = 1000;
            this.appView.height = 50;
            this.appView.backgroudColor = "white";

            let colors = ["red","blue","gredd"];
            for (let i = 0 ; 999 > i ; i ++)
            {
                let sub = new zl.View();
                sub.x = i;
                sub.y = Math.sin(i / 3.14) * 15 + this.appView.height / 2;
                sub.width = 3;
                sub.height = 3;
                sub.backgroudColor = colors[i %3];
                this.appView.addSubview(sub);
            }
        }
        // 也可以直接返回 this.appView.reactElement();
        // return  this.appView.reactElement();
        return (
            <div className="App">
                <header className="App-header">
                    {this.appView.reactElement()}
                </header>
            </div>
        );
    }
}</code></pre>
<h2>ZLView的坐标系统</h2>
<p>一个ZLView对应一个<code>React Component</code>，也可说是对<code>React Component</code>的封装；ZLView采用固定CSS样式，将<code>position</code>设置为<code>absolute</code>，然后设置<code>left/right/widht/height</code>。当然ZLView对此作了封装：</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>x</td>
<td>以父视图左上定点为原点的坐标系中，x轴位置</td>
</tr>
<tr>
<td>y</td>
<td>以父视图左上定点为原点的坐标系中，y轴位置</td>
</tr>
<tr>
<td>width</td>
<td>宽度</td>
</tr>
<tr>
<td>height</td>
<td>高度</td>
</tr>
<tr>
<td>left</td>
<td>同x</td>
</tr>
<tr>
<td>right</td>
<td>x+width</td>
</tr>
<tr>
<td>top</td>
<td>同y</td>
</tr>
<tr>
<td>bottom</td>
<td>y+height</td>
</tr>
<tr>
<td>center_x</td>
<td>(x+width)/2</td>
</tr>
<tr>
<td>center_y</td>
<td>(y+height)/2</td>
</tr>
<tr>
<td>center</td>
<td>(center_x,center_y)</td>
</tr>
</tbody>
</table>
<h3>ZLView的尺寸单位</h3>
<p>支持<code>px</code>和<code>rem</code>两种，默认使用<code>px</code>单位。<br />
可以设置<code>ZLCurrentSizeUnit</code>值来改变尺寸单位。另外，px和rem的换算，默认使用<code>1rem=16px</code>，也可以修改<code>ZLCurrentSizeUnitOneRemToPx</code>来改变这个换算比例.</p>
<h2>ZLView的生命周期</h2>
<p>源自React Component的生命周期</p>
<table>
<thead>
<tr>
<th>方法</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>viewDidMount/addListenViewDidMount/removeListenViewDidMount</td>
<td>React.componentDidMount</td>
</tr>
<tr>
<td>viewWillUnmount/addListenViewWillUnMount/removeListenViewWillUnMount</td>
<td>React.componentWillUnmount</td>
</tr>
</tbody>
</table>
<h2>父视图和子视图</h2>
<ul>
<li>
<p><code>superView</code> 获取父视图</p>
</li>
<li>
<p><code>removeFromSuperview</code> 从父图中移除</p>
</li>
<li>
<p><code>subViews</code> 获取子视图列表</p>
</li>
<li>
<p><code>addSubview</code> 添加子视图</p>
<h2>刷新视图</h2>
</li>
<li>
<p><code>refresh</code> 刷新视图，本质是调用 React.setState。 ZLView的属性变更后，需要主动调用 <code>refresh</code>来刷新视图</p>
</li>
<li>
<p><code>layoutSubViews</code>,是一个通知类型的方法，用于重新布局子视图列表；触发时机是在React.render返回结果前</p>
</li>
</ul>
<h2>ZLView获取DOM节点</h2>
<p>可以通过实现方法<code>onReactRefCallback</code>来获取，也可以调用<code>addListenOnReactRefCallback</code>添加新方法获取.</p>
<h2>监听DOM事件</h2>
<p>通过<code>addListenDOMEvent</code>和<code>removeListenDOMEvent</code>可以监听/移除监听DOM事件，例如</p>
<pre><code class="language-ts"> view.addListenDOMEvent("onClick", (e:React.SyntheticEvent)=>{
     console.log("clicked");
 })</code></pre>
<h2>继承ZLView</h2>
<p>ZLView支持继承以自定义视图样式，通常需要重写的方法<code>__reactRender__</code>和<code>__htmlAttributes__</code>，前者用于返回React.render数据，后缀用于修改<code>CSS样式</code></p>
<p>示列:</p>
<pre><code class="language-ts">class CustomView extends zl.View
{
    /**
     * 渲染  React render
     */
    protected __reactRender__(children?:React.ReactNode[]) : React.ReactElement
    {
        // html attributes
        let attr = this.__htmlAttributes__();
        return React.createElement("canvas"/*自定义标签*/, attr.toReactClassAttributes(),children);
    }

    /**
     * 子类可重写
     * @returns html attributes
     */
    protected __htmlAttributes__() : ZLHtmlAttribute
    {
        let attr = super.__htmlAttributes__();
        attr.style.backgroundColor = "white";//背景色永远是白色
        return attr
    }</code></pre>
<h2>Transform</h2>
<p>封装了matrix2d和matrix3d变换<code>ZLTransform</code>，内部实现使用了矩阵乘法，支持<code>translate 平动</code>、<code>scale 缩放</code>、<code>rotate 旋转</code>、<code>skew 倾斜</code>、<code>refect 翻转</code>。</p>
<pre><code class="language-ts">let view:zl.View = new zl.View();
view.width = 200;
view.height = 100;
view.backgroundColor = "yellow";

let transform = new zl.Transform();
transform.rotate(Math.PI);
transform.translate(10,190);
view.transform = transform;
view.refresh();</code></pre>
<h2>背景色、前景色、不透明度、是否可见</h2>
<ul>
<li><code>backgroundColor</code> 略</li>
<li><code>color</code>  略</li>
<li><code>opacity</code>  略</li>
<li><code>visibility</code>  略</li>
</ul>
<h2>设置阴影</h2>
<p>简单封装了<code>css box shadow</code>，可以直接设置<code>ZLView.boxShadow</code>属性</p>
<pre><code class="language-ts">view.backgroundColor = "yellow";

let shadow = new zl.BoxShadow();
shadow.color = "green";
view.boxShadow = shadow;</code></pre>
<h2>边框</h2>
<p>支持简单设置</p>
<ul>
<li><code>borderColor</code> 边框颜色</li>
<li><code>borderStyle</code> 边框样式</li>
<li><code>borderWidth</code> 边框宽度</li>
</ul>
<h2>动画</h2>
<p>简单封装了CSS动画，直接作用在ZLView上</p>
<pre><code class="language-ts">/**
 * 开启一个3秒动画
 * 尺寸从(100,200)变化到（200，100）
 * 背景色从red到yellow
 * 动画曲线使用cubic-bezier（1,0,0,1)
 */

let view = new zl.View()
view.width = 100;
view.height = 200;
view.backgroudColor = "red";

view.cssAnimation({to:()=>{
            view.backgroudColor = "yellow";
            view.x = 100;
            view.width = 200;
            view.height = 100;

            let transform = new zl.Transform();
            transform.rotate(Math.PI);
            transform.translate(10,190);
        },
            duration:3000,
            timingFunction:zl.CSSAnimationTimingFunction.cubicBezier,
            cubicBezierValue:[1,0,0,1],
            end:()=>{
                console.log("animation end");
            }
});</code></pre>
<h2>ZLRouter路由</h2>
<p>封装<code>react-router-dom</code>。引入页面概念<code>ZLViewPage</code>，一个路由对应一个页面，采用严格模式匹配路由的<code>path</code></p>
<pre><code class="language-ts">class HomePage extends zl.ViewPage
{
    viewDidLoad()
    {
        super.viewDidLoad();
        this.view.backgroudColor = "red";
    }

    viewDidMount()
    {
        setTimeout(()=>{
            this.router?.push("/other");
        },5000);
        console.log( this.constructor.name + " mount");
    }

    viewWillUnmount()
    {
        console.log( this.constructor.name + " unmount");
    }
}

class OtherPage extends zl.ViewPage
{
    viewDidLoad()
    {
        super.viewDidLoad();
        this.view.backgroudColor = "blue";
    }

    viewDidMount()
    {
        console.log( this.constructor.name + " mount");
    }

    viewWillUnmount()
    {
        console.log( this.constructor.name + " unmount");
    }
}

class App extends React.Component
{
    private router: zl.Router | undefined;
    render()
    {
        if (this.router === undefined) {
            this.router = new zl.Router();
            this.router.registRoute("/",HomePage);
            this.router.registRoute("/other", OtherPage);
        }
        return this.router.reactElement();
    }
}</code></pre>
<h3>注册路由</h3>
<pre><code class="language-ts">let router = new zl.Router();
router.registRoute("/",HomePage);
router.registRoute("/other",OtherPage);</code></pre>
<p>也可以直接注册<code>ZLViewPage</code></p>
<pre><code class="language-ts">// 等同于  router.registRoute("/OtherPage",OtherPage);
router.registViewPage(OtherPage);</code></pre>
<h3>路由跳转</h3>
<pre><code class="language-ts">router.push("/other");
router.replace("/");</code></pre>
<p>也可以直接push<code>ZLViewPage</code></p>
<pre><code class="language-ts">// 等同于  router.push("/OtherPage");
router.pushViewPage(OtherPage);</code></pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>【转载】使用TypeScript开发项目打包发布到npm</title>
		<link>https://blog.z6z8.cn/2021/05/28/%e3%80%90%e8%bd%ac%e8%bd%bd%e3%80%91%e4%bd%bf%e7%94%a8typescript%e5%bc%80%e5%8f%91%e9%a1%b9%e7%9b%ae%e6%89%93%e5%8c%85%e5%8f%91%e5%b8%83%e5%88%b0npm/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Fri, 28 May 2021 02:35:12 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=1018</guid>

					<description><![CDATA[初始化项目 npm init 通过npm init初始化项目来创建用户package.json文件 也可以np [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>初始化项目</h1>
<pre><code class="language-shell">npm init</code></pre>
<p>通过<code>npm init</code>初始化项目来创建用户<code>package.json</code>文件<br />
也可以<code>npm init -y</code> 这个是使用的默认的配置，我个人使用的是<code>npm init</code></p>
<h1>创建tsconfig.json文件</h1>
<pre><code class="language-shell">tsc --init</code></pre>
<p>就会生成一个tsconfig.json文件</p>
<p>修改tsconfig.json默认文件<br />
把这两个注释打开</p>
<pre><code class="language-json">{
  "compilerOptions": {
        "declaration": true, //打包之后是否生成声明文件
        "outDir": "./dist", //输出文件
    }
}</code></pre>
<p>添加exclude,忽略dist文件<br />
在打包的时候会排除这里面指定的路径文件</p>
<pre><code>{
  "compilerOptions": {
    },
    "exclude": [
    "./dist"
  ]
}</code></pre>
<h1>安装typescript依赖</h1>
<pre><code>npm install typescript -D</code></pre>
<h1>开始编码</h1>
<p>创建xxxxx.ts文件,写入typescript代码<br />
例如</p>
<pre><code class="language-ts">const arrayMap = (array: [], callback:(item: any,index: number, arr: any[]) => any): any => {
    let i = -1
    const len = array.length
    let resArray = []
    while (++i < len){
        resArray.push(callback(array[i],i,array))
    }
    return resArray
}
export = arrayMap</code></pre>
<h1>对代码进行编译</h1>
<pre><code class="language-shell">tsc</code></pre>
<p>此时我们的项目就会多了一个dist目录</p>
<h1>登录npm</h1>
<p>大家没有npm账号的可以注册一个</p>
<p>这个是网址</p>
<p><a href="https://www.npmjs.com">https://www.npmjs.com</a><br />
然后在编辑器终端里面输入</p>
<pre><code class="language-shell">npm login</code></pre>
<p>接着就会出来用户名、密码、邮箱这些依次填一下</p>
<h1>创建.npmignore文件</h1>
<p>在项目根目录里创建一个.npmjgnore</p>
<p>这个其实和.gitignor差不多，就是你发npm包的时候，希望哪些文件或者文件夹不发到这个npm上</p>
<h1>版本号</h1>
<p>在package.json里面版本号，<br />
每发布一次都要修改一下</p>
<h1>发布</h1>
<pre><code>npm publish</code></pre>
<p>发布成功</p>
<p>原文https://segmentfault.com/a/1190000021740976</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>typescript 导出 d.ts声明文件</title>
		<link>https://blog.z6z8.cn/2021/05/24/typescript-%e5%af%bc%e5%87%ba-d-ts%e5%a3%b0%e6%98%8e%e6%96%87%e4%bb%b6/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Mon, 24 May 2021 09:23:36 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[学习笔记]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=1015</guid>

					<description><![CDATA[需要在tsconfig.json中配置以下参数 { "noEmit": false, "declaration [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>需要在<code>tsconfig.json</code>中配置以下参数</p>
<pre><code class="language-json">{
    "noEmit": false,
    "declaration": true,
    "declarationDir": "dist/declaration",
    "outDir": "dist"
}</code></pre>
<ul>
<li><code>noEmit</code> , 若要导出<code>d.ts</code>文件，必须为false。否则会忽略<code>declaration</code>相关参数</li>
<li><code>declaration</code>,是否导出<code>d.ts</code>文件，当为true时且<code>noEmit</code>为true时才会进行导出操作.</li>
<li><code>declarationDir</code>，<code>d.ts</code>文件存放的目录路径。当不设置改参数时，将使用<code>outDir</code>的值</li>
<li><code>outDir</code>，将typescript 转换成js文件、以及<code>d.ts</code>文件的存放目录</li>
</ul>
<p>配置完成后，运行<code>tsc --build tsconfig.json</code>即可</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>VSCode插件开发&#8211;一些API</title>
		<link>https://blog.z6z8.cn/2021/01/07/vscode%e6%8f%92%e4%bb%b6%e5%bc%80%e5%8f%91-%e6%89%93%e5%bc%80%e6%9c%ac%e5%9c%b0%e7%9b%ae%e5%bd%95%ef%bc%88openfolder%ef%bc%89/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Thu, 07 Jan 2021 08:36:53 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[学习笔记]]></category>
		<category><![CDATA[Visual Studio Code]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=951</guid>

					<description><![CDATA[VSCode 打开本地目录或工作空间 命令ID vscode.openFolder ,打开一个目录或者工作空间 [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>VSCode</h1>
<h2>打开本地目录或工作空间</h2>
<p>命令ID <code>vscode.openFolder</code> ,打开一个目录或者工作空间。可以在当前窗口打开，或者新建窗口打开。<br />
参数</p>
<ul>
<li><code>uri</code> ，Uri类型，可选，目录（工作空间）路径。如果不提供，那么将会弹出一个原生的目录选择对话框</li>
<li><code>newWindow</code>，可选，是否打开一个新的窗口</li>
</ul>
<pre><code class="language-javascript">// 在新窗口打开/some/path/to/folder
let uri = vscode.Uri.file("/some/path/to/folder");
vscode.commands.executeCommand("vscode.openFolder",uri,true);</code></pre>
<h2>当前活动的Terminal（Shell终端）</h2>
<p>内置方法<code>vscode.window.activeTerminal</code></p>
<h2>创建Terminal（Shell终端）</h2>
<p>使用内置方法<code>vscode.window.createTerminal</code>，它有三个重载函数, 返回值均为<code>vscode.Terminal</code></p>
<ul>
<li>createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): Terminal</li>
</ul>
<table>
<thead>
<tr>
<th>参数</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>name</td>
<td>可选 ，终端创建后显示在UI上的名字</td>
</tr>
<tr>
<td>shellPath</td>
<td>可选，自定义shell终端的执行路径</td>
</tr>
<tr>
<td>shellArgs</td>
<td>可选 ，传递给自定义终端的执行参数。<br />只对Windows生效</td>
</tr>
</tbody>
</table>
<p>shellArgs 详细格式参见https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6 </p>
<ul>
<li>createTerminal(options: TerminalOptions): Terminal</li>
</ul>
<table>
<thead>
<tr>
<th>参数</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>options</td>
<td>必选 ，TerminalOptions配置参数</td>
</tr>
</tbody>
</table>
<p><code>TerminalOptions</code>可用的值</p>
<table>
<thead>
<tr>
<th>参数</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>占位占位占位占位占位占位占位占位占位占位占位占位占位</td>
<td></td>
</tr>
<tr>
<td>cwd</td>
<td>可选 ，string 或 Uri ，终端的工作目录</td>
</tr>
<tr>
<td>env</td>
<td>可选 ，object ，添加到终端中的环境变量</td>
</tr>
<tr>
<td>hideFromUser</td>
<td>可选 ，boolean ，如果设置为true，终端将正常运行，但直到调用Terminal.show时，才会对用户公开。 通常的用法是在需要运行可能需要交互性，但只想在需要交互时对用户公开</td>
</tr>
<tr>
<td>name</td>
<td>可选 ，string ，终端创建后显示在UI上的名字</td>
</tr>
<tr>
<td>shellArgs</td>
<td>可选 ， string[] 或 string  ，传递给自定义终端的执行参数<br />只对Windows生效</td>
</tr>
<tr>
<td>shellPath</td>
<td>可选 ， string  ，自定义shell终端的执行路径</td>
</tr>
<tr>
<td>strictEnv</td>
<td>可选 ， boolean  ，终端环境是否应完全与TerminalOptions.env中提供的一致。 如果为false（默认），则环境将基于窗口的环境，并且还会在顶部应用配置的平台设置，例如terminal.integrated.windows.env。 如果true，则必须提供完整的环境。</td>
</tr>
</tbody>
</table>
<ul>
<li>createTerminal(options: ExtensionTerminalOptions): Terminal<br />
此方法将创建一个由插件（扩展）控制输入输出的终端环境</li>
</ul>
<table>
<thead>
<tr>
<th>参数</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>options</td>
<td>必选 ，ExtensionTerminalOptions配置参数</td>
</tr>
</tbody>
</table>
<p><code>ExtensionTerminalOptions</code>可用的值</p>
<table>
<thead>
<tr>
<th>参数</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>name</td>
<td>必选 ，string ，终端创建后显示在UI上的名字</td>
</tr>
<tr>
<td>pty</td>
<td>必选 ， Pseudoterminal 配置参数</td>
</tr>
</tbody>
</table>
<p><code>Pseudoterminal</code>可用的值</p>
<table>
<thead>
<tr>
<th>参数</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>name</td>
<td>必选 ，string ，终端创建后显示在UI上的名字</td>
</tr>
<tr>
<td>pty</td>
<td>必选 ， Pseudoterminal 配置参数</td>
</tr>
</tbody>
</table>
<p>Pseudoterminal 参见https://code.visualstudio.com/api/references/vscode-api#Pseudoterminal </p>
<p>参考<br />
<a href="https://code.visualstudio.com/api/references/commands">https://code.visualstudio.com/api/references/commands</a><br />
<a href="https://code.visualstudio.com/api/references/vscode-api">https://code.visualstudio.com/api/references/vscode-api</a></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>VSCode插件开发&#8211;添加侧边栏入口和面板</title>
		<link>https://blog.z6z8.cn/2020/12/15/vscode%e6%8f%92%e4%bb%b6%e5%bc%80%e5%8f%91-%e6%b7%bb%e5%8a%a0%e4%be%a7%e8%be%b9%e6%a0%8f%e5%85%a5%e5%8f%a3%e5%92%8c%e9%9d%a2%e6%9d%bf/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Tue, 15 Dec 2020 10:58:57 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[学习笔记]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=942</guid>

					<description><![CDATA[先看一张图 在VSCode的文档中，侧边栏按钮入口称之为Tree View Container，侧边栏面板称之 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>先看一张图<br />
<img decoding="async" src="/wp-content/uploads/2020/12/workbench-contribution-300x209.png" alt="" /><br />
在VSCode的文档中，侧边栏按钮入口称之为<code>Tree View Container</code>，侧边栏面板称之为<code>Tree View</code>，本文涉及的就是这两个区域。</p>
<h2>创建工程</h2>
<p>使用 <code>yo code</code>脚手架创建一个插件工程，语言选择<code>TypeScript</code>。具体过程略</p>
<h2>配置侧边栏按钮(Tree View Container)和面板视图(Tree View)</h2>
<p>侧边栏按钮(Tree View Container)和面板视图(Tree View)要同时配置，否则不生效。</p>
<p>打开<code>package.json</code>，添加以下内容</p>
<pre><code class="language-json">"contributes": {
        "viewsContainers": {
            "activitybar": [
                {
                    "id":"sidebar_test",
                    "title": "侧边栏测试",
                    "icon": "入口.svg"
                }
            ]
        },
        "views": {
            "sidebar_test":[
                {
                    "id":"sidebar_test_id1",
                    "name":"面板区块名称1"
                },
                {
                    "id":"sidebar_test_id2",
                    "name":"面板区块名称2"
                }
            ]
        }
    }</code></pre>
<blockquote>
<p>按钮图片格式，需要使用svg格式。views中key要和activitybar中的属性id保持一致，如sidebar_test在两者中是一致的。</p>
</blockquote>
<p>以上配置了一个<code>sidebar_test</code>的侧边栏按钮，点击按钮，会出现<code>面板区块名称1</code>和<code>面板区块名称2</code>两个区块。</p>
<p><img decoding="async" src="/wp-content/uploads/2020/12/截屏2020-12-15-下午5.54.38-279x300.png" alt="" /></p>
<h2>定义面板内容</h2>
<p>以上仅仅是将面板入口添加上了，若添加内容与命令，则需要<code>registerTreeDataProvider</code>、<code>TreeDataProvider</code>、<code>TreeItem</code>用于内容管理，以及<code>registerCommand</code>注册命令</p>
<p>新建脚本<code>test.ts</code>,内容如下</p>
<pre><code class="language-ts">import * as vscode from 'vscode';

// 树节点
export class EntryItem extends vscode.TreeItem
{
}

//树的内容组织管理
export class EntryList implements vscode.TreeDataProvider&lt;EntryItem&gt;
{
    onDidChangeTreeData?: vscode.Event&lt;void | EntryItem | null | undefined&gt; | undefined;
    getTreeItem(element: EntryItem): vscode.TreeItem | Thenable&lt;vscode.TreeItem&gt; {
        return element;
    }
    getChildren(element?: EntryItem): vscode.ProviderResult&lt;EntryItem[]&gt; {
        if (element) {//子节点
            var childs = [];
            for (let index = 0; index &lt; 3; index++) {
                let str = index.toString();
                var item = new EntryItem(str,vscode.TreeItemCollapsibleState.None);
                item.command = {command:"sidebar_test_id1.openChild", //命令id
                                title:"标题",
                                arguments:[str] //命令接收的参数
                                };
                childs[index] = item;
            }
            return childs;
        } else { //根节点
            return [new EntryItem("root",vscode.TreeItemCollapsibleState.Collapsed)];
        }
    }
}</code></pre>
<p>然后在<code>extension.ts</code>中添加内容</p>
<pre><code class="language-ts">import * as vscode from 'vscode';
import * as sidebar from './test';

/// 插件激活时
export function activate(context: vscode.ExtensionContext) {

    //注册侧边栏面板的实现
    const sidebar_test = new sidebar.EntryList();
    vscode.window.registerTreeDataProvider("sidebar_test_id1",sidebar_test);
    //注册命令 
    vscode.commands.registerCommand("sidebar_test_id1.openChild",args =&gt; {
        vscode.window.showInformationMessage(args);
    });

//// 其他内容保持不变
}</code></pre>
<p>最后，在<code>package.json</code>，添加面板区块的展示事件，以驱动内容变化</p>
<pre><code class="language-json">"activationEvents": [
        "onView:sidebar_test_id1"
    ],
    "contributes": {
    }</code></pre>
<p>运行结果如下<br />
<img decoding="async" src="/wp-content/uploads/2020/12/截屏2020-12-15-下午6.57.03-261x300.png" alt="" /></p>
<p>参考地址<br />
<a href="https://code.visualstudio.com/docs/extensionAPI/vscode-api">https://code.visualstudio.com/docs/extensionAPI/vscode-api</a></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>JavaScript如何实现sleep？</title>
		<link>https://blog.z6z8.cn/2020/03/30/javascript%e5%a6%82%e4%bd%95%e5%ae%9e%e7%8e%b0sleep%ef%bc%9f/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Mon, 30 Mar 2020 01:37:52 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[学习笔记]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=844</guid>

					<description><![CDATA[JavaScript如何实现sleep？ 一般的，sleep是将当前上下文所在的线程从执行状态转化为挂起状态， [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>JavaScript如何实现sleep？</h1>
<p>一般的，<code>sleep</code>是将当前上下文所在的线程从执行状态转化为挂起状态，直到指定的一段实时时间过去后，再从挂起状态转化为执行状态，在sleep执行结束前，线程不会执行其他任务。</p>
<p>该函数使当前进程从执行状态转化为挂起状态，直到参数seconds所指定的一段实时时间过去后，或者是一个唤醒信号跟踪功能或终止进程功能的信号到来。该挂起时间由于系统的其他调度活动可能会比要求的时间长。</p>
<p>在ES标准中，JavaScript代码的执行环境是一个单线程环境，所以<code>JavaScript如何实现sleep</code>的本质是如何将一个线程阻塞一段时间然后继续工作。</p>
<p>这里的重点是<code>阻塞</code>。所以，使用<code>promise</code>、<code>generator</code>之类的实现是不满足这个需求的，任务调度机制本质不同，因为<code>promise</code>、<code>generator</code>的本质是延迟，而不是阻塞。</p>
<p>在不扩展JavaScript语言底层能力的情况下，实现阻塞方式：</p>
<ul>
<li>计算阻塞</li>
<li>IO阻塞</li>
</ul>
<h1>计算阻塞</h1>
<p>也就是消耗CPU资源进行阻塞，通常是一段循环计算，在循环期间，JavaScript代码执行线程不会执行其他任务，从而达到模拟<code>sleep</code>的效果</p>
<pre><code class="language-js">&lt;script type="text/javascript"&gt;

// bad implementation
function sleep(milliSeconds){
    var startTime = new Date().getTime(); // get the current time
    while (new Date().getTime() &lt; startTime + milliSeconds); // hog cpu
    }
}
&lt;/script&gt;</code></pre>
<p>这种方式在任何标准的ES环境中都可以实现，阻塞和计时在同一个线程中完成。</p>
<h1>IO阻塞</h1>
<p>也就是消耗IO资源进行阻塞，通常是消耗网络IO，如<code>XMLHttpRequest</code>的同步通信。</p>
<pre><code class="language-js">&lt;script type="text/javascript"&gt;

function sleep(milliSeconds)
{
  var resource;
  var response;
  if(typeof ActiveXObject == 'undefined'){
    resource = new XMLHttpRequest();
  }
  else{
    resource = new ActiveXObject("Microsoft.XMLHTTP");
  }
  try{
    resource.open('GET', 'sleep?milliSeconds=' + milliSeconds, false);
    resource.send(null);
    response = resource.responseText; // JavaScript waits for response
  }
  catch(e){
    alert(e);
  }
}
&lt;/script&gt;</code></pre>
<p>这种方式是基于BS模型，Server负责计时，在指定的时间后返回响应，Browser负责阻塞。</p>
<h1>引入扩展阻塞</h1>
<p>参考 <a href="http://devcheater.com/，还可以引入其他语言能力实现">http://devcheater.com/，还可以引入其他语言能力实现</a><code>sleep</code>，如<code>Java Applet</code>、<code>Flash的sleep</code></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>移动端监听单页应用的URL变化</title>
		<link>https://blog.z6z8.cn/2020/03/19/%e7%a7%bb%e5%8a%a8%e7%ab%af%e7%9b%91%e5%90%ac%e5%8d%95%e9%a1%b5%e5%ba%94%e7%94%a8%e7%9a%84url%e5%8f%98%e5%8c%96/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Thu, 19 Mar 2020 01:51:04 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[iOS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[学习笔记]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=840</guid>

					<description><![CDATA[问题 对于Native WebView而言，Andoird/iOS都提供相应的方法/代理监听页面URL的变化。 [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>问题</h1>
<p>对于Native WebView而言，Andoird/iOS都提供相应的方法/代理监听页面URL的变化。例如，Android可以重写<code>doUpdateVisitedHistory</code>方法监听URL变化，iOS可以实现<code>shouldStartLoadWithRequest</code>、<code>decidePolicyForNavigationAction</code>代理监听URL变化</p>
<pre><code class="language-java">@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
  //此处检测url是否有变化
  return super.shouldOverrideUrlLoading(view, url);
}</code></pre>
<pre><code class="language-objc">//UIWebView
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    //此处检测url是否有变化
    return YES;
}

//WKWebView
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    //此处检测url是否有变化
    decisionHandler(WKNavigationActionPolicyAllow);
}</code></pre>
<p>上述技术实现对一些前端应用能正常工作。然而，遇到单页应用（SPA）时，以上方法并不会被触发--这里涉及单页应用的实现原理，尤其是基于 <code>hash</code>路由的单页应用，其<code>前进/后退</code>的问题更为突出。</p>
<h1>解决思路</h1>
<p>对于前端应用而言，如果开发者使用固定的框架，那么在框架的路由层面拦截监听应较为容易做到。但是实际情况是开发者使用的前端框架不确定，甚至开发者连前端框架都不用，所以我们在前端框架层面截获路由器变得不那么容易。</p>
<p>经过调研，要监听前端页面的URL变化，大致有两种模式：</p>
<ul>
<li>定时器模式</li>
<li>观察者模式</li>
</ul>
<h2>定时器模式</h2>
<p>定期监测URL是否变化，如100毫秒监测一次，与上次监测值对比来判断URL是否变化。定时器，前端可以使用<code>setInterval</code>来实现</p>
<pre><code class="language-js">var oldUrl = window.location.href;
setInterval(function(){
  if (oldUrl !== window.location.href) {
    //通知Native Url变化
    oldUrl = window.location.href;
  }
},100);</code></pre>
<p>当然，Native也可以开启定时器监测<code>window.location.href</code>。</p>
<p>定时器模式的特点是实现简单，适用范围广；缺点也显而易见，监测结果不是十分准确，性能耗用高。而其性能耗用问题在移动端将会放大，故此我们不采用此模式。</p>
<h2>观察者模式</h2>
<p>上面提到的<code>doUpdateVisitedHistory</code>、<code>shouldStartLoadWithRequest</code>、<code>decidePolicyForNavigationAction</code>的实现可以算作观察者模式。</p>
<p>前面提到，单页应用页面切换在移动端webview的行为表现，并不会每次都通知Native，我们要做的就是通过某种方法来完善这个页面切换的事件通知。</p>
<p>对于观察者模式，iOS中有KVO（key-value-observing）机制，遍历UIWebView和WKWebView的属性发现：</p>
<ul>
<li>WKWebView的<code>URL</code>属性能够响应KVO机制，当前端页面（包括单页应用的页面）URL有变化时，KVO机制可以监测到。</li>
<li>UIWebView的<code>request</code>属性并不能响应KVO机制，不能用它检测URL变化。也就是说，单纯使用UIWebView的能力接口不能完全监测URL变化</li>
</ul>
<p>至此，在Native侧建立观察者的思路并不适用所有情况，我们需要再返回前端侧研究下单页应用的URL变化。</p>
<p>单页应用，通过<code>hash</code>或<code>window.history</code>可以做到改变URL，使得不刷新页面的情况下重新渲染。我们只要监测<code>hash</code>和<code>window.history</code>，当它们发生变化时去检测URL的变化：</p>
<ul>
<li>
<p>通过<code>hash</code>改变URL，会触发<code>hashchange</code>事件。当监听到<code>hashchange</code>事件时，去检测URL的变化。</p>
</li>
<li>
<p><code>window.history</code>相关的事件为<code>popstate</code>。当监听到<code>popstate</code>事件时，去检测URL的变化。</p>
<p><code>History.back()</code>、<code>History.forward()</code>、<code>History.go()</code>会触发<code>popstate</code>事件</p>
</li>
<li>
<p>另外，<code>History.pushState()</code>和<code>History.replaceState()</code>不会触发<code>popstate</code>事件，所以我们要hook这两个方法，以检测URL的变化。</p>
</li>
</ul>
<h1>方案实现</h1>
<p>结合以上，最终我们可以采用的可行方案如下 :</p>
<table>
<thead>
<tr>
<th>Android WebView</th>
<th>iOS UIWebView</th>
<th>iOS WKWebView</th>
</tr>
</thead>
<tbody>
<tr>
<td>前端监测 <code>hash</code>和<code>window.history</code>，通知Native</td>
<td>前端监测 <code>hash</code>和<code>window.history</code>，通知Native</td>
<td>Native监测<code>WKWebView.URL</code></td>
</tr>
</tbody>
</table>
<p><em>注意：上述方案并不是唯一方案，但确实是一个可行方案</em></p>
<h2>WKWebView监测URL部分实现</h2>
<pre><code class="language-objc">//添加观察者
[self addObserver:wkwebview forKeyPath:@"URL" options:NSKeyValueObservingOptionNew context:nil];

//接收观察事件
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary&lt;NSKeyValueChangeKey,id&gt; *)change context:(void *)context
{
  //url变化，触发相应动作
}

//使用完毕后移除观察者
[self removeObserver:wkwebview forKeyPath:@"URL"];</code></pre>
<h2>前端监测URL部分实现</h2>
<pre><code class="language-js">//监听hash变化
window.addEventListener('hashchange',function(event){
  //通过bridge通知Native URL变化
});

//监听history变化,popstate只能监听History.back(),History.forward()、History.go()
window.addEventListener('popstate',function(event){
  //通过bridge通知Native URL变化
});

//hook History.pushState()  History.replaceState()
var _wr = function(type) {
    var orig = history[type];
    return function() {
        var rv = orig.apply(this, arguments);
        var e = new Event('ffpd'+type);
        e.arguments = arguments;
        window.dispatchEvent(e);
        return rv;
    };
};
history.pushState = _wr('pushState');
history.replaceState = _wr('replaceState');

window.addEventListener('ffpdpushState',function(event){
  //通过bridge通知Native URL变化
});
window.addEventListener('ffpdreplaceState',function(event){
  //通过bridge通知Native URL变化
});</code></pre>
<p>参考：<a href="https://stackoverflow.com/questions/4570093/how-to-get-notified-about-changes-of-the-history-via-history-pushstate">https://stackoverflow.com/questions/4570093/how-to-get-notified-about-changes-of-the-history-via-history-pushstate</a></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>emcc 将C/C++编译成没有WebAssembly支持的JavaScript</title>
		<link>https://blog.z6z8.cn/2020/03/13/emcc-%e5%b0%86c-c%e7%bc%96%e8%af%91%e6%88%90%e6%b2%a1%e6%9c%89webassembly%e6%94%af%e6%8c%81%e7%9a%84javascript/</link>
		
		<dc:creator><![CDATA[holdsky]]></dc:creator>
		<pubDate>Thu, 12 Mar 2020 18:22:55 +0000</pubDate>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[学习笔记]]></category>
		<guid isPermaLink="false">http://blog.z6z8.cn/?p=829</guid>

					<description><![CDATA[No WebAssembly support found. Build with -s WASM=0 to t [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>No WebAssembly support found. Build with -s WASM=0 to target JavaScript instead.<br />
也就是</p>
<pre><code class="language-shell">emcc   -s WASM=0  c/c++文件</code></pre>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
