快速搭建一个代码在线编辑预览工具

简介

大家好,我是一个闲着没事热衷于重复造轮子的不知名前端,今天给大家带来的是一个代码在线编辑预览工具的实现介绍,目前这类工具使用很广泛,常见于各种文档网站及代码分享场景,相关工具也比较多,如codepenjsruncodesandboxjsbinplnkrjsfiddle等,这些工具大体分两类,一类可以自由添加多个文件,比较像我们平常使用的编辑器,另一类固定只能单独编辑htmljscss,第二类比较常见,对于demo场景来说其实已经够用,当然,说的只是表象,底层实现方式可能还是各有千秋的。

本文主要介绍的是第二类其中的一种实现方式,完全不依赖于后端,所有逻辑都在前端完成,实现起来相当简单,使用的是vue3全家桶来开发,使用其他框架也完全可以。

ps.在本文基础上笔者开发了一个完整的线上工具,带云端保存,地址:http://lxqnsys.com/code-run/,欢迎使用。

页面结构

快速搭建一个代码在线编辑预览工具_第1张图片

我挑了一个比较典型也比较好看的结构来仿照,默认布局上下分成四部分,工具栏、编辑器、预览区域及控制台,编辑器又分为三部分,分别是HTMLCSSJavaScript,其实就是三个编辑器,用来编辑代码。

各部分都可以拖动进行调节大小,比如按住js编辑器左边的灰色竖条向右拖动,那么js编辑器的宽度会减少,同时css编辑器的宽度会增加,如果向左拖动,那么css编辑器宽度会减少,js编辑器的宽度会增加,当css编辑器宽度已经不能再减少的时候css编辑器也会同时向左移,然后减少html的宽度。

在实现上,水平调节宽度和垂直调节高度原理是一样的,以调节宽度为例,三个编辑器的宽度使用一个数组来维护,用百分比来表示,那么初始就是100/3%,然后每个编辑器都有一个拖动条,位于内部的左侧,那么当按住拖动某个拖动条拖动时的逻辑如下:

1.把本次拖动瞬间的偏移量由像素转换为百分比;

2.如果是向左拖动的话,检测本次拖动编辑器的左侧是否存在还有空间可以压缩的编辑器,没有的话代表不能进行拖动;如果有的话,那么拖动时增加本次拖动编辑器的宽度,同时减少找到的第一个有空间的编辑器的宽度,直到无法再继续拖动;

3.如果是向右拖动的话,检测本次拖动编辑器及其右侧是否存在还有空间可以压缩的编辑器,没有的话也代表不能再拖动,如果有的话,找到第一个并减少该编辑器的宽度,同时增加本次拖动编辑器左侧第一个编辑器的宽度;

核心代码如下:

const onDrag = (index, e) => {
    let client = this._dir === 'v' ? e.clientY : e.clientX
    // 本次移动的距离
    let dx = client - this._last
    // 换算成百分比
    let rx = (dx / this._containerSize) * 100
    // 更新上一次的鼠标位置
    this._last = client
    if (dx < 0) {
        // 向左/上拖动
        if (!this.isCanDrag('leftUp', index)) {
            return
        }
        // 拖动中的编辑器增加宽度
        if (this._dragItemList.value[index][this._prop] - rx < this.getMaxSize(index)) {
            this._dragItemList.value[index][this._prop] -= rx
        } else {
            this._dragItemList.value[index][this._prop] = this.getMaxSize(index)
        }
        // 找到左边第一个还有空间的编辑器索引
        let narrowItemIndex = this.getFirstNarrowItemIndex('leftUp', index)
        let _minSize = this.getMinSize(narrowItemIndex)
        // 左边的编辑器要同比减少宽度
        if (narrowItemIndex >= 0) {
            // 加上本次偏移还大于最小宽度
            if (this._dragItemList.value[narrowItemIndex][this._prop] + rx > _minSize) {
                this._dragItemList.value[narrowItemIndex][this._prop] += rx
            } else {
                // 否则固定为最小宽度
                this._dragItemList.value[narrowItemIndex][this._prop] = _minSize
            }
        }
    } else if (dx > 0) {
        // 向右/下拖动
        if (!this.isCanDrag('rightDown', index)) {
            return
        }
        // 找到拖动中的编辑器及其右边的编辑器中的第一个还有空间的编辑器索引
        let narrowItemIndex = this.getFirstNarrowItemIndex('rightDown', index)
        let _minSize = this.getMinSize(narrowItemIndex)
        if (narrowItemIndex <= this._dragItemList.value.length - 1) {
            let ax = 0
            // 减去本次偏移还大于最小宽度
            if (this._dragItemList.value[narrowItemIndex][this._prop] - rx > _minSize) {
                ax = rx
            } else {
                // 否则本次能移动的距离为到达最小宽度的距离
                ax = this._dragItemList.value[narrowItemIndex][this._prop] - _minSize
            }
            // 更新拖动中的编辑器的宽度
            this._dragItemList.value[narrowItemIndex][this._prop] -= ax
            // 左边第一个编辑器要同比增加宽度
            if (index > 0) {
                if (this._dragItemList.value[index - 1][this._prop] + ax < this.getMaxSize(index - 1)) {
                    this._dragItemList.value[index - 1][this._prop] += ax
                } else {
                    this._dragItemList.value[index - 1][this._prop] = this.getMaxSize(index - 1)
                }
            }
        }
    }
}

实现效果如下:

为了能提供多种布局的随意切换,我们有必要把上述逻辑封装一下,封装成两个组件,一个容器组件Drag.vue,一个容器的子组件DragItem.vueDragItem通过slot来显示其他内容,DragItem主要提供拖动条及绑定相关的鼠标事件,Drag组件里包含了上述提到的核心逻辑,维护对应的尺寸数组,提供相关处理方法给DragItem绑定的鼠标事件,然后只要根据所需的结构进行组合即可,下面的结构就是上述默认的布局:


    
        
    
    
        
    
    
        
    

这部分代码较多,有兴趣的可以查看源码。

编辑器

目前涉及到代码编辑的场景基本使用的都是codemirror,因为它功能强大,使用简单,支持语法高亮、支持多种语言和主题等,但是为了能更方便的支持语法提示,本文选择的是微软的monaco-editor,功能和VSCode一样强大,VSCode有多强就不用我多说了,缺点是整体比较复杂,代码量大,内置主题较少。

monaco-editor支持多种加载方式,esm模块加载的方式需要使用webpack,但是vite底层打包工具用的是Rollup,所以本文使用直接引入js的方式。

在官网上下载压缩包后解压到项目的public文件夹下,然后参考示例的方式在index.html文件里添加:





monaco-editor内置了10种语言,我们选择中文的,其他不用的可以直接删掉:

快速搭建一个代码在线编辑预览工具_第2张图片

接下来创建编辑器就可以了:

const editor = monaco.editor.create(
    editorEl.value,// dom容器
    {
        value: props.content,// 要显示的代码
        language: props.language,// 代码语言,css、javascript等
        minimap: {
            enabled: false,// 关闭小地图
        },
        wordWrap: 'on', // 代码超出换行
        theme: 'vs-dark'// 主题
    }
)

就这么简单,一个带高亮、语法提示、错误提示的编辑器就可以使用了,效果如下:

快速搭建一个代码在线编辑预览工具_第3张图片

其他几个常用的api如下:

// 设置文档内容
editor.setValue(props.content)
// 监听编辑事件
editor.onDidChangeModelContent((e) => {
    console.log(editor.getValue())// 获取文档内容
})
// 监听失焦事件
editor.onDidBlurEditorText((e) => {
    console.log(editor.getValue())
})

预览

代码有了,接下来就可以渲染页面进行预览了,对于预览,显然是使用iframeiframe除了src属性外,HTML5还新增了一个属性srcdoc,用来渲染一段HTML代码到iframe里,这个属性IE目前不支持,不过vue3都要不支持IE了,咱也不管了,如果硬要支持也简单,使用write方法就行了:

iframeRef.value.contentWindow.document.write(htmlStr)

接下来的思路就很清晰了,把htmlcssjs代码组装起来扔给srcdoc不就完了吗:

const assembleHtml = (head, body) => {
    return `
        
        
            
            ${head}
        
        
            ${body}
        
        `
}

const run = () => {
  let head = `
    预览<\/title>
    <style type="text/css">
        ${editData.value.code.css.content}
    <\/style>
  `
  let body = `
    ${editData.value.code.html.content}
    <script>
        ${editData.value.code.javascript.content}
    <\/script>
  `
  let str = assembleHtml(head, body)
  srcdoc.value = str
}</code></pre> 
 <p>效果如下:</p> 
 <p><span class="img-wrap"><a href="http://img.e-com-net.com/image/info9/41dbb037b2dc4236be220cc4918ab1c2.jpg" target="_blank"><img class="lazy" alt="快速搭建一个代码在线编辑预览工具_第4张图片" title="image-20210507141946844.png" src="http://img.e-com-net.com/image/info9/41dbb037b2dc4236be220cc4918ab1c2.jpg" width="650" height="187" style="border:1px solid black;"></a></span></p> 
 <p>为了防止<code>js</code>代码运行出现错误阻塞页面渲染,我们把<code>js</code>代码使用<code>try catch</code>包裹起来:</p> 
 <pre><code class="js">let body = `
    ${editData.value.code.html.content}
    <script>
        try {
          ${editData.value.code.javascript.content}
        } catch (err) {
          console.error('js代码运行出错')
          console.error(err)
        }
    <\/script>
  `</code></pre> 
 <h1>控制台</h1> 
 <h2>极简方式</h2> 
 <p>先介绍一种非常简单的方式,使用一个叫<a href="https://link.segmentfault.com/?url=https%3A%2F%2Fgithub.com%2Fliriliri%2Feruda" rel="nofollow">eruda</a>的库,这个库是用来方便在手机上进行调试的,和<code>vConsole</code>类似,我们直接把它嵌到<code>iframe</code>里就可以支持控制台的功能了,要嵌入<code>iframe</code>里的文件我们都要放到<code>public</code>文件夹下:</p> 
 <pre><code class="js">const run = () => {
  let head = `
    <title>预览<\/title>
    <style type="text/css">
        ${editData.value.code.css.content}
    <\/style>
  `
  let body = `
    ${editData.value.code.html.content}
    <script src="/eruda/eruda.js"><\/script>
    <script>
        eruda.init();
        ${editData.value.code.javascript.content}
    <\/script>
  `
  let str = assembleHtml(head, body)
  srcdoc.value = str
}</code></pre> 
 <p>效果如下:</p> 
 <p><span class="img-wrap"><a href="http://img.e-com-net.com/image/info9/98a90b66bf6e4a709f9be99d5063f2a4.jpg" target="_blank"><img class="lazy" alt="快速搭建一个代码在线编辑预览工具_第5张图片" title="image-20210507154345054.png" src="http://img.e-com-net.com/image/info9/98a90b66bf6e4a709f9be99d5063f2a4.jpg" width="650" height="295" style="border:1px solid black;"></a></span></p> 
 <p>这种方式的缺点是只能嵌入到<code>iframe</code>里,不能把控制台和页面分开,导致每次代码重新运行,控制台也会重新运行,无法保留之前的日志,当然,样式也不方便控制。</p> 
 <h2>自己实现</h2> 
 <p>如果选择自己实现的话,那么这部分会是本项目里最复杂的,自己实现的话一般只实现一个<code>console</code>的功能,其他的比如<code>html</code>结构、请求资源之类的就不做了,毕竟实现起来费时费力,用处也不是很大。</p> 
 <p><code>console</code>大体上要支持输出两种信息,一是<code>console</code>对象打印出来的信息,二是各种报错信息,先看<code>console</code>信息。</p> 
 <h3>console信息</h3> 
 <p>思路很简单,在<code>iframe</code>里拦截<code>console</code>对象的所有方法,当某个方法被调用时使用<code>postMessage</code>来向父页面传递信息,父页面的控制台打印出对应的信息即可。</p> 
 <pre><code class="js">// /public/console/index.js

// 重写的console对象的构造函数,直接修改console对象的方法进行拦截的方式是不行的,有兴趣可以自行尝试
function ProxyConsole() {};
// 拦截console的所有方法
[
    'debug',
    'clear',
    'error',
    'info',
    'log',
    'warn',
    'dir',
    'props',
    'group',
    'groupEnd',
    'dirxml',
    'table',
    'trace',
    'assert',
    'count',
    'markTimeline',
    'profile',
    'profileEnd',
    'time',
    'timeEnd',
    'timeStamp',
    'groupCollapsed'
].forEach((method) => {
    let originMethod = console[method]
    // 设置原型方法
    ProxyConsole.prototype[method] = function (...args) {
        // 发送信息给父窗口
        window.parent.postMessage({
            type: 'console',
            method,
            data: args
        })
        // 调用原始方法
        originMethod.apply(ProxyConsole, args)
    }
})
// 覆盖原console对象
window.console = new ProxyConsole()</code></pre> 
 <p>把这个文件也嵌入到<code>iframe</code>里:</p> 
 <pre><code class="js">const run = () => {
  let head = `
    <title>预览<\/title>
    <style type="text/css">
        ${editData.value.code.css.content}
    <\/style>
    <script src="/console/index.js"><\/script>
  `
  // ...
}</code></pre> 
 <p>父页面监听<code>message</code>事件即可:</p> 
 <pre><code class="js">window.addEventListener('message', (e) => {
  console.log(e)
})</code></pre> 
 <p>如果如下:</p> 
 <p><span class="img-wrap"><a href="http://img.e-com-net.com/image/info9/4842fa85c135410ab22fb8aa74b3e14f.jpg" target="_blank"><img class="lazy" alt="快速搭建一个代码在线编辑预览工具_第6张图片" title="image-20210507165953197.png" src="http://img.e-com-net.com/image/info9/4842fa85c135410ab22fb8aa74b3e14f.jpg" width="650" height="359" style="border:1px solid black;"></a></span></p> 
 <p>监听获取到了信息就可以显示出来,我们一步步来看:</p> 
 <p>首先<code>console</code>的方法都可以同时接收多个参数,打印多个数据,同时打印的在同一行进行显示。</p> 
 <p>1.基本数据类型</p> 
 <p>基本数据类型只要都转成字符串显示出来就可以了,无非是使用颜色区分一下:</p> 
 <pre><code class="js">// /public/console/index.js

// ...

window.parent.postMessage({
    type: 'console',
    method,
    data: args.map((item) => {// 对每个要打印的数据进行处理
        return handleData(item)
    })
})

// ...

// 处理数据
const handleData = (content) => {
    let contentType = type(content)
    switch (contentType) {
        case 'boolean': // 布尔值
            content = content ? 'true' : 'false'
            break;
        case 'null': // null
            content = 'null'
            break;
        case 'undefined': // undefined
            content = 'undefined'
            break;
        case 'symbol': // Symbol,Symbol不能直接通过postMessage进行传递,会报错,需要转成字符串
            content = content.toString()
            break;
        default:
            break;
    }
    return {
        contentType,
        content,
    }
}</code></pre> 
 <pre><code class="js">// 日志列表
const logList = ref([])

// 监听iframe信息
window.addEventListener('message', ({ data = {} }) => {
  if (data.type === 'console') 
    logList.value.push({
      type: data.method,// console的方法名
      data: data.data// 要显示的信息,一个数组,可能同时打印多条信息
    })
  }
})</code></pre> 
 <pre><code class="html"><div class="logBox">
    <div class="logRow" v-for="(log, index) in logList" :key="index">
        <template v-for="(logItem, itemIndex) in log.data" :key="itemIndex">
            <!-- 基本数据类型 -->
            <div class="logItem message" :class="[logItem.contentType]" v-html="logItem.content"></div>
        </template>
    </div>
</div></code></pre> 
 <p><span class="img-wrap"><a href="http://img.e-com-net.com/image/info9/0b8618777f83403b8d57bdd6f1803f73.jpg" target="_blank"><img class="lazy" alt="快速搭建一个代码在线编辑预览工具_第7张图片" title="image-20210508091625420.png" src="http://img.e-com-net.com/image/info9/0b8618777f83403b8d57bdd6f1803f73.jpg" width="650" height="156" style="border:1px solid black;"></a></span></p> 
 <p>2.函数</p> 
 <p>函数只要调用<code>toString</code>方法转成字符串即可:</p> 
 <pre><code class="js">const handleData = (content) => {
        let contentType = type(content)
        switch (contentType) {
            // ...
            case 'function':
                content = content.toString()
                break;
            default:
                break;
        }
    }</code></pre> 
 <p>3.json数据</p> 
 <p><code>json</code>数据需要格式化后进行显示,也就是带高亮、带缩进,以及支持展开收缩。</p> 
 <p>实现也很简单,高亮可以通过<code>css</code>类名控制,缩进换行可以使用<code>div</code>和<code>span</code>来包裹,具体实现就是像深拷贝一样深度优先遍历<code>json</code>树,对象或数组的话就使用一个<code>div</code>来整体包裹,这样可以很方便的实现整体缩进,具体到对象或数组的某项时也使用<code>div</code>来实现换行,需要注意的是如果是作为对象的某个属性的值的话,需要使用<code>span</code>来和属性及冒号显示在同一行,此外,也要考虑到循环引用的情况。</p> 
 <p>展开收缩时针对非空的对象和数组,所以可以在遍历下级属性之前添加一个按钮元素,按钮相对于最外层元素使用绝对定位。</p> 
 <pre><code class="js">const handleData = (content) => {
    let contentType = type(content)
    switch (contentType) {
            // ...
        case 'array': // 数组
        case 'object': // 对象
            content = stringify(content, false, true, [])
            break;
        default:
            break;
    }
}

// 序列化json数据变成html字符串
/* 
    data:数据
    hasKey:是否是作为一个key的属性值
    isLast:是否在所在对象或数组中的最后一项
    visited:已经遍历过的对象/数组,用来检测循环引用
*/
const stringify = (data, hasKey, isLast, visited) => {
    let contentType = type(data)
    let str = ''
    let len = 0
    let lastComma = isLast ? '' : ',' // 当数组或对象在最后一项时,不需要显示逗号
    switch (contentType) {
        case 'object': // 对象
            // 检测到循环引用就直接终止遍历
            if (visited.includes(data)) {
                str += `<span class="string">检测到循环引用</span>`
            } else {
                visited.push(data)
                let keys = Object.keys(data)
                len = keys.length
                // 空对象
                if (len <= 0) {
                    // 如果该对象是作为某个属性的值的话,那么左括号要和key显示在同一行
                    str += hasKey ? `<span class="bracket">{ }${lastComma}</span>` : `<div class="bracket">{ }${lastComma}</div>`
                } else { // 非空对象
                    // expandBtn是展开和收缩按钮
                    str += `<span class="el-icon-arrow-right expandBtn"></span>`
                    str += hasKey ? `<span class="bracket">{</span>` : '<div class="bracket">{</div>'
                    // 这个wrap的div用来实现展开和收缩功能
                    str += '<div class="wrap">'
                    // 遍历对象的所有属性
                    keys.forEach((key, index) => {
                        // 是否是数组或对象
                        let childIsJson = ['object', 'array'].includes(type(data[key]))
                        // 最后一项不显示逗号
                        str += `
                            <div class="objectItem">
                                <span class="key">\"${key}\"</span>
                                <span class="colon">:</span>
                                ${stringify(data[key], true, index >= len - 1, visited)}${index < len - 1 && !childIsJson ? ',' : ''}
                            </div>`
                    })
                    str += '</div>'
                    str += `<div class="bracket">}${lastComma}</div>`
                }
            }
            break;
        case 'array': // 数组
            if (visited.includes(data)) {
                str += `<span class="string">检测到循环引用</span>`
            } else {
                visited.push(data)
                len = data.length
                // 空数组
                if (len <= 0) {
                    // 如果该数组是作为某个属性的值的话,那么左括号要和key显示在同一行
                    str += hasKey ? `<span class="bracket">[ ]${lastComma}</span>` : `<div class="bracket">[ ]${lastComma}</div>`
                } else { // 非空数组
                    str += `<span class="el-icon-arrow-right expandBtn"></span>`
                    str += hasKey ? `<span class="bracket">[</span>` : '<div class="bracket">[</div>'
                    str += '<div class="wrap">'
                    data.forEach((item, index) => {
                        // 最后一项不显示逗号
                        str += `
                            <div class="arrayItem">
                                ${stringify(item, true, index >= len - 1, visited)}${index < len - 1 ? ',' : ''}
                            </div>`
                    })
                    str += '</div>'
                    str += `<div class="bracket">]${lastComma}</div>`
                }
            }
            break;
        default: // 其他类型
            let res = handleData(data)
            let quotationMarks = res.contentType === 'string' ? '\"' : '' // 字符串添加双引号
            str += `<span class="${res.contentType}">${quotationMarks}${res.content}${quotationMarks}</span>`
            break;
    }
    return str
}</code></pre> 
 <p>模板部分也增加一下对<code>json</code>数据的支持:</p> 
 <pre><code class="html"><template v-for="(logItem, itemIndex) in log.data" :key="itemIndex">
    <!-- json对象 -->
    <div
         class="logItem json"
         v-if="['object', 'array'].includes(logItem.contentType)"
         v-html="logItem.content"
         ></div>
    <!-- 字符串、数字 -->
</template></code></pre> 
 <p>最后对不同的类名写一下样式即可,效果如下:</p> 
 <p><span class="img-wrap"><a href="http://img.e-com-net.com/image/info9/368d76414c4d4a968578dcc9c424e9ed.jpg" target="_blank"><img class="lazy" alt="快速搭建一个代码在线编辑预览工具_第8张图片" title="image-20210508195753623.png" src="http://img.e-com-net.com/image/info9/368d76414c4d4a968578dcc9c424e9ed.jpg" width="482" height="732" style="border:1px solid black;"></a></span></p> 
 <p>展开收缩按钮的点击事件我们使用事件代理的方式绑定到外层元素上:</p> 
 <pre><code class="html"><div
     class="logItem json"
     v-if="['object', 'array'].includes(logItem.contentType)"
     v-html="logItem.content"
     @click="jsonClick"
     >
</div></code></pre> 
 <p>点击展开收缩按钮的时候根据当前的展开状态来决定是展开还是收缩,展开和收缩操作的是<code>wrap</code>元素的高度,收缩时同时插入一个省略号的元素来表示此处存在收缩,同时因为按钮使用绝对定位,脱离了正常文档流,所以也需要手动控制它的显示与隐藏,需要注意的是要能区分哪些按钮是本次可以操作的,否则可能下级是收缩状态,但是上层又把该按钮显示出来了:</p> 
 <pre><code class="js">// 在子元素里找到有指定类名的第一个元素
const getChildByClassName = (el, className) => {
  let children = el.children
  for (let i = 0; i < children.length; i++) {
    if (children[i].classList.contains(className)) {
      return children[i]
    }
  }
  return null
}

// json数据展开收缩
let expandIndex = 0
const jsonClick = (e) => {
  // 点击是展开收缩按钮
  if (e.target && e.target.classList.contains('expandBtn')) {
    let target = e.target
    let parent = target.parentNode
    // id,每个展开收缩按钮唯一的标志
    let index = target.getAttribute('data-index')
    if (index === null) {
      index = expandIndex++
      target.setAttribute('data-index', index)
    }
    // 获取当前状态,0表示收缩、1表示展开
    let status = target.getAttribute('expand-status') || '1'
    // 在子节点里找到wrap元素
    let wrapEl = getChildByClassName(parent, 'wrap')
    // 找到下层所有的按钮节点
    let btnEls = wrapEl.querySelectorAll('.expandBtn')
    // 收缩状态 -> 展开状态
    if (status === '0') {
      // 设置状态为展开
      target.setAttribute('expand-status', '1')
      // 展开
      wrapEl.style.height = 'auto'
      // 按钮箭头旋转
      target.classList.remove('shrink')
      // 移除省略号元素
      let ellipsisEl = getChildByClassName(parent, 'ellipsis')
      parent.removeChild(ellipsisEl)
      // 显示下级展开收缩按钮
      for (let i = 0; i < btnEls.length; i++) {
        let _index = btnEls[i].getAttribute('data-for-index')
        // 只有被当前按钮收缩的按钮才显示
        if (_index === index) {
          btnEls[i].removeAttribute('data-for-index')
          btnEls[i].style.display = 'inline-block'
        }
      }
    } else if (status === '1') {
      // 展开状态 -> 收缩状态
      target.setAttribute('expand-status', '0')
      wrapEl.style.height = 0
      target.classList.add('shrink')
      let ellipsisEl = document.createElement('div')
      ellipsisEl.textContent = '...'
      ellipsisEl.className = 'ellipsis'
      parent.insertBefore(ellipsisEl, wrapEl)
      for (let i = 0; i < btnEls.length; i++) {
        let _index = btnEls[i].getAttribute('data-for-index')
        // 只隐藏当前可以被隐藏的按钮
        if (_index === null) {
          btnEls[i].setAttribute('data-for-index', index)
          btnEls[i].style.display = 'none'
        }
      }
    }
  }
}</code></pre> 
 <p>效果如下:</p> 
 <p><span class="img-wrap"></span></p> 
 <p>4.console对象的其他方法</p> 
 <p><code>console</code>对象有些方法是有特定逻辑的,比如<code>console.assert(expression, message)</code>,只有当<code>express</code>表达式为<code>false</code>时才会打印<code>message</code>,又比如<code>console</code>的一些方法支持占位符等,这些都得进行相应的支持,先修改一下<code>console</code>拦截的逻辑:</p> 
 <pre><code class="js"> ProxyConsole.prototype[method] = function (...args) {
     // 发送信息给父窗口
     // 针对特定方法进行参数预处理
     let res = handleArgs(method, args)
     // 没有输出时就不发送信息
     if (res.args) {
         window.parent.postMessage({
             type: 'console',
             method: res.method,
             data: res.args.map((item) => {
                 return handleData(item)
             })
         })
     }
     // 调用原始方法
     originMethod.apply(ProxyConsole, args)
 }</code></pre> 
 <p>增加了<code>handleArgs</code>方法来对特定的方法进行参数处理,比如<code>assert</code>方法:</p> 
 <pre><code class="js">const handleArgs = (method, contents) => {
    switch (method) {
        // 只有当第一个参数为false,才会输出第二个参数,否则不会有任何结果
        case 'assert':
            if (contents[0]) {
                contents = null
            } else {
                method = 'error'
                contents = ['Assertion failed: ' + (contents[1] || 'console.assert')]
            }
            break;
        default:
            break;
    }
    return {
        method,
        args: contents
    }
}</code></pre> 
 <p>再看一下占位符的处理,占位符描述如下:</p> 
 <p><span class="img-wrap"><a href="http://img.e-com-net.com/image/info9/fbbf17388c3d4791938a1f7e2600a47b.jpg" target="_blank"><img class="lazy" alt="快速搭建一个代码在线编辑预览工具_第9张图片" title="image-20210512135732215.png" src="http://img.e-com-net.com/image/info9/fbbf17388c3d4791938a1f7e2600a47b.jpg" width="650" height="212" style="border:1px solid black;"></a></span></p> 
 <p>可以判断第一个参数是否是字符串,以及是否包含占位符,如果包含了,那么就判断是什么占位符,然后取出后面对应位置的参数进行格式化,没有用到的参数也不能丢弃,仍然需要显示:</p> 
 <pre><code class="js">const handleArgs = (method, contents) => {
        // 处理占位符
        if (contents.length > 0) {
            if (type(contents[0]) === 'string') {
                // 只处理%s、%d、%i、%f、%c
                let match = contents[0].match(/(%[sdifc])([^%]*)/gm) // "%d年%d月%d日" -> ["%d年", "%d月", "%d日"]
                if (match) {
                    // 后续参数
                    let sliceArgs = contents.slice(1)
                    let strList = []
                    // 遍历匹配到的结果
                    match.forEach((item, index) => {
                        let placeholder = item.slice(0, 2)
                        let arg = sliceArgs[index]
                        // 对应位置没有数据,那么就原样输出占位符
                        if (arg === undefined) {
                            strList.push(item)
                            return
                        }
                        let newStr = ''
                        switch (placeholder) {
                            // 字符串,此处为简单处理,实际和chrome控制台的输出有差异
                            case '%s':
                                newStr = String(arg) + item.slice(2)
                                break;
                                // 整数
                            case '%d':
                            case '%i':
                                newStr = (type(arg) === 'number' ? parseInt(arg) : 'NaN') + item.slice(2)
                                break;
                                // 浮点数
                            case '%f':
                                newStr = (type(arg) === 'number' ? arg : 'NaN') + item.slice(2)
                                break;
                                // 样式
                            case '%c':
                                newStr = `<span style="${arg}">${item.slice(2)}</span>`
                                break;
                            default:
                                break;
                        }
                        strList.push(newStr)
                    })
                    contents = strList
                    // 超出占位数量的剩余参数也不能丢弃,需要展示
                    if (sliceArgs.length > match.length) {
                        contents = contents.concat(sliceArgs.slice(match.length))   
                    }
                }
            }
        }
        // 处理方法 ...
        switch (method) {}
}</code></pre> 
 <p>效果如下:</p> 
 <p><span class="img-wrap"><a href="http://img.e-com-net.com/image/info9/709dc72b5e37426d90b62e4684e887fa.jpg" target="_blank"><img class="lazy" alt="快速搭建一个代码在线编辑预览工具_第10张图片" title="image-20210512140705004.png" src="http://img.e-com-net.com/image/info9/709dc72b5e37426d90b62e4684e887fa.jpg" width="650" height="469" style="border:1px solid black;"></a></span></p> 
 <h3>报错信息</h3> 
 <p>报错信息上文已经涉及到了,我们对<code>js</code>代码使用<code>try catch</code>进行了包裹,并使用<code>console.error</code>进行错误输出,但是还有些错误可能是<code>try catch</code>监听不到的,比如定时器代码执行出错,或者是没有被显式捕获的<code>Promise</code>异常,我们也需要加上对应的监听及显示。</p> 
 <pre><code class="js">// /public/console/index.js

// 错误监听
window.onerror = function (message, source, lineno, colno, error) {
    window.parent.postMessage({
        type: 'console',
        method: 'string',
        data: [message, source, lineno, colno, error].map((item) => {
            return handleData(item)
        })
    })
}
window.addEventListener('unhandledrejection', err => {
    window.parent.postMessage({
        type: 'console',
        method: 'string',
        data: [handleData(err.reason.stack)]
    })
})

// ...</code></pre> 
 <h3>执行输入的js</h3> 
 <p><code>console</code>的最后一个功能是可以输入<code>js</code>代码然后动态执行,这个可以使用<code>eval</code>方法,<code>eval</code>能动态执行<code>js</code>代码并返回最后一个表达式的值,<code>eval</code>会带来一些安全风险,但是笔者没有找到更好的替代方案,知道的朋友请在下方留言一起探讨吧。</p> 
 <p>动态执行的代码里的输出以及最后表达式的值我们也要显示到控制台里,为了不在上层拦截<code>console</code>,我们把动态执行代码的功能交给预览的<code>iframe</code>,执行完后再把最后的表达式的值使用<code>console</code>打印一下,这样所有的输出都能显示到控制台。</p> 
 <pre><code class="html"><textarea v-model="jsInput" @keydown.enter="implementJs"></textarea></code></pre> 
 <pre><code class="js">const jsInput = ref('')
const implementJs = (e) => {
    // shift+enter为换行,不需要执行
    if (e.shiftKey) {
        return
    }
    e.preventDefault()
    let code = jsInput.value.trim()
    if (code) {
        // 给iframe发送信息
        iframeRef.value.contentWindow.postMessage({
            type: 'command',
            data: code
        })
        jsInput.value = ''
    }
}</code></pre> 
 <pre><code class="js">// /public/console/index.js

// 接收代码执行的事件
const onMessage = ({ data = {} }) => {
    if (data.type === 'command') {
        try {
            // 打印一下要执行的代码
               console.log(data.data)
            // 使用eval执行代码
            console.log(eval(data.data))
        } catch (error) {
            console.error('js执行出错')
            console.error(error)
        }
    }
}
window.addEventListener('message', onMessage)</code></pre> 
 <p>效果如下:</p> 
 <p><span class="img-wrap"><a href="http://img.e-com-net.com/image/info9/9dbfefda32bc4e98a3db21f315799ddf.gif" target="_blank"><img class="lazy" alt="快速搭建一个代码在线编辑预览工具_第11张图片" title="2021-05-12-18-31-12.gif" src="http://img.e-com-net.com/image/info9/9dbfefda32bc4e98a3db21f315799ddf.gif" width="394" height="194" style="border:1px solid black;"></a></span></p> 
 <h1>支持预处理器</h1> 
 <p>除了基本的<code>html</code>、<code>js</code>和<code>css</code>,作为一个强大的工具,我们有必要支持一下常用的预处理器,比如<code>html</code>的<code>pug</code>,<code>js</code>的<code>TypeScript</code>及<code>css</code>的<code>less</code>等,实现思路相当简单,加载对应预处理器的转换器,然后转换一下即可。</p> 
 <h2>动态切换编辑器语言</h2> 
 <p><code>Monaco Editor</code>想要动态修改语言的话我们需要换一种方式来设置文档,上文我们是创建编辑器的同时直接把语言通过<code>language</code>选项传递进去的,然后使用<code>setValue</code>来设置文档内容,这样后期无法再动态修改语言,我们修改为切换文档模型的方式:</p> 
 <pre><code class="js">// 创建编辑器
editor = monaco.editor.create(editorEl.value, {
    minimap: {
        enabled: false, // 关闭小地图
    },
    wordWrap: 'on', // 代码超出换行
    theme: 'vs-dark', // 主题
    fontSize: 18,
    fontFamily: 'MonoLisa, monospace',
})
// 更新编辑器文档模型 
const updateDoc = (code, language) => {
  if (!editor) {
    return
  }
  // 获取当前的文档模型
  let oldModel = editor.getModel()
  // 创建一个新的文档模型
  let newModel = monaco.editor.createModel(code, language)
  // 设置成新的
  editor.setModel(newModel)
  // 销毁旧的模型
  if (oldModel) {
    oldModel.dispose()
  }
}</code></pre> 
 <h2>加载转换器</h2> 
 <p>转换器的文件我们都放在<code>/public/parses/</code>文件夹下,然后进行动态加载,即选择了某个预处理器后再去加载对应的转换器资源,这样可以节省不必要的请求。</p> 
 <p>异步加载<code>js</code>我们使用<a href="https://link.segmentfault.com/?url=https%3A%2F%2Fgithub.com%2Fmuicss%2Floadjs" rel="nofollow">loadjs</a>这个小巧的库,新增一个<code>load.js</code>:</p> 
 <pre><code class="js">// 记录加载状态
const preprocessorLoaded = {
    html: true,
    javascript: true,
    css: true,
    less: false,
    scss: false,
    sass: false,
    stylus: false,
    postcss: false,
    pug: false,
    babel: false,
    typescript: false
}

// 某个转换器需要加载多个文件
const resources = {
    postcss: ['postcss-cssnext', 'postcss']
}

// 异步加载转换器的js资源
export const load = (preprocessorList) => {
    // 过滤出没有加载过的资源
    let notLoaded = preprocessorList.filter((item) => {
        return !preprocessorLoaded[item]
    })
    if (notLoaded.length <= 0) {
        return
    }
    return new Promise((resolve, reject) => {
        // 生成加载资源的路径
        let jsList = []
        notLoaded.forEach((item) => {
            let _resources = (resources[item] || [item]).map((r) => {
                return `/parses/${r}.js`
            })
            jsList.push(..._resources)
        })
        loadjs(jsList, {
            returnPromise: true
        }).then(() => {
            notLoaded.forEach((item) => {
                preprocessorLoaded[item] = true
            })
            resolve()
        }).catch((err) => {
            reject(err)
        })
    })
}</code></pre> 
 <p>然后修改一下上文预览部分的<code>run </code>方法:</p> 
 <pre><code class="js">const run = async () => {
  let h = editData.value.code.HTML.language
  let j = editData.value.code.JS.language
  let c = editData.value.code.CSS.language
  await load([h, j, c])
  // ...
}</code></pre> 
 <h2>转换</h2> 
 <p>所有代码都使用转换器转换一下,因为有的转换器是同步方式的,有的是异步方式的,所以我们统一使用异步来处理,修改一下<code>run</code>方法:</p> 
 <pre><code class="js">const run = async () => {
  // ...
  await load([h, j, c])
  let htmlTransform = transform.html(h, editData.value.code.HTML.content)
  let jsTransform = transform.js(j, editData.value.code.JS.content)
  let cssTransform = transform.css(c, editData.value.code.CSS.content)
  Promise.all([htmlTransform, jsTransform, cssTransform])
    .then(([htmlStr, jsStr, cssStr]) => {
      // ...
    })
    .catch((error) => {
      // ...
    })
}</code></pre> 
 <p>接下来就是最后的转换操作,下面只展示部分代码,完整代码有兴趣的可查看源码:</p> 
 <pre><code class="js">// transform.js

const html = (preprocessor, code) => {
    return new Promise((resolve, reject) => {
        switch (preprocessor) {
            case 'html':
                // html的话原封不动的返回
                resolve(code)
                break;
            case 'pug':
                // 调用pug的api来进行转换
                resolve(window.pug.render(code))
            default:
                resolve('')
                break;
        }
    })
}

const js = (preprocessor, code) => {
    return new Promise((resolve, reject) => {
        let _code = ''
        switch (preprocessor) {
            case 'javascript':
                resolve(code)
                break;
            case 'babel':
                // 调用babel的api来编译,你可以根据需要设置presets
                _code = window.Babel.transform(code, {
                    presets: [
                        'es2015',
                        'es2016',
                        'es2017',
                        'react'
                    ]
                }).code
                resolve(_code)
            default:
                resolve('')
                break;
        }
    })
}

const css = (preprocessor, code) => {
    return new Promise((resolve, reject) => {
        switch (preprocessor) {
            case 'css':
                resolve(code)
                break;
            case 'less':
                window.less.render(code)
                    .then(
                        (output) => {
                            resolve(output.css)
                        },
                        (error) => {
                            reject(error)
                        }
                    );
                break;
            default:
                resolve('')
                break;
        }
    })
}</code></pre> 
 <p>可以看到很简单,就是调一下相关转换器的<code>api</code>来转换一下,不过想要找到这些转换器的浏览器使用版本和<code>api</code>可太难了,笔者基本都没找到,所以这里的大部分代码都是参考<a href="https://link.segmentfault.com/?url=https%3A%2F%2Fgithub.com%2Fegoist%2Fcodepan" rel="nofollow">codepan</a>的。</p> 
 <h1>其他功能</h1> 
 <p>另外还有一些实现起来简单,但是能很大提升用户体验的功能,比如添加额外的<code>css</code>或<code>js</code>资源,免去手写<code>link</code>或<code>script</code>标签的麻烦:</p> 
 <p><span class="img-wrap"><a href="http://img.e-com-net.com/image/info9/c437d310fb864855b8fcb7ecfb17466e.jpg" target="_blank"><img class="lazy" alt="快速搭建一个代码在线编辑预览工具_第12张图片" title="image-20210514140452547.png" src="http://img.e-com-net.com/image/info9/c437d310fb864855b8fcb7ecfb17466e.jpg" width="650" height="289" style="border:1px solid black;"></a></span></p> 
 <p>预设一些常用模板,比如<code>vue3</code>、<code>react</code>等,方便快速开始,免去写基本结构的麻烦:</p> 
 <p><span class="img-wrap"></span></p> 
 <h1>有没有更快的方法</h1> 
 <p>如果你看到这里,你一定会说这是哪门子快速搭建,那有没有更快的方法呢,当然有了,就是直接克隆本项目的仓库或者<a href="https://link.segmentfault.com/?url=https%3A%2F%2Fgithub.com%2Fegoist%2Fcodepan" rel="nofollow">codepan</a>,改改就可以使用啦~</p> 
 <h1>结尾</h1> 
 <p>本文从零开始介绍了如何搭建一个代码在线编辑预览的工具,粗糙实现总有不足之处,欢迎指出。</p> 
 <p>项目仓库<a href="https://link.segmentfault.com/?url=https%3A%2F%2Fgithub.com%2Fwanglin2%2Fcode-run" rel="nofollow">code-run</a>,欢迎<code>star</code>。</p> 
</article>
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1421747511579758592"></div>
                    <script type="text/javascript" src="/views/front/js/chanyan.js"></script>
                    <!-- 文章页-底部 动态广告位 -->
                    <div class="youdao-fixed-ad" id="detail_ad_bottom"></div>
                </div>
                <div class="col-md-3">
                    <div class="row" id="ad">
                        <!-- 文章页-右侧1 动态广告位 -->
                        <div id="right-1" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_1"> </div>
                        </div>
                        <!-- 文章页-右侧2 动态广告位 -->
                        <div id="right-2" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_2"></div>
                        </div>
                        <!-- 文章页-右侧3 动态广告位 -->
                        <div id="right-3" class="col-lg-12 col-md-12 col-sm-4 col-xs-4 ad">
                            <div class="youdao-fixed-ad" id="detail_ad_3"></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <div class="container">
        <h4 class="pt20 mb15 mt0 border-top">你可能感兴趣的:(javascript)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1884898627642519552.htm"
                           title="深入探讨:服务器如何响应前端请求及后端如何查看前端提交的数据" target="_blank">深入探讨:服务器如何响应前端请求及后端如何查看前端提交的数据</a>
                        <span class="text-muted">我的青春不太冷</span>
<a class="tag" taget="_blank" href="/search/%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">服务器</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a>
                        <div>深入探讨:服务器如何响应前端请求及后端如何查看前端提交的数据一、服务器如何响应前端请求前端与后端的交互主要通过HTTP协议实现。以下是详细步骤:1.前端发起HTTP请求GET请求:用于从服务器获取数据。POST请求:用于向服务器提交数据。例如,使用JavaScript的fetchAPI发送POST请求:fetch('https://example.com/api/data',{method:'PO</div>
                    </li>
                    <li><a href="/article/1884894714105229312.htm"
                           title="JavaScript逆向高阶指南:突破基础,掌握核心逆向技术" target="_blank">JavaScript逆向高阶指南:突破基础,掌握核心逆向技术</a>
                        <span class="text-muted">不做超级小白</span>
<a class="tag" taget="_blank" href="/search/web%E9%80%86%E5%90%91%E7%9F%A5%E8%AF%86%E7%A2%8E%E7%89%87/1.htm">web逆向知识碎片</a><a class="tag" taget="_blank" href="/search/web%E5%89%8D%E7%AB%AF/1.htm">web前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a>
                        <div>JavaScript逆向高阶指南:突破基础,掌握核心逆向技术JavaScript逆向工程是Web开发者和安全分析师的核心竞争力。无论是解析混淆代码、分析压缩脚本,还是逆向Web应用架构,掌握高阶逆向技术都将助您深入理解复杂JavaScript逻辑。本文将通过实战案例,带您探索JavaScript逆向的深层技术原理。1.JavaScript反混淆实战现代Web应用常采用多重混淆技术保护代码,以下为高</div>
                    </li>
                    <li><a href="/article/1884894587776987136.htm"
                           title="小白一命速通JS中的window&global对象" target="_blank">小白一命速通JS中的window&global对象</a>
                        <span class="text-muted">不做超级小白</span>
<a class="tag" taget="_blank" href="/search/web%E5%89%8D%E7%AB%AF/1.htm">web前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a>
                        <div>笔者注意到JS中的window对象与global对象经常被混淆,尽管它们在相当一部分使用情况下可以等同,但是本质上仍然存在很多不同,下面是对于两者的详细拆解1.window对象定义:window对象表示浏览器环境中的全局上下文。作用域:它是浏览器中运行的任何JavaScript代码的顶级对象。关键特性:包含所有通过var声明的全局变量和函数(在非模块脚本中)。表示浏览器的窗口或框架,代码运行在其中</div>
                    </li>
                    <li><a href="/article/1884883735384485888.htm"
                           title="OpenAI 函数调用 功能入门" target="_blank">OpenAI 函数调用 功能入门</a>
                        <span class="text-muted">AI火箭</span>
<a class="tag" taget="_blank" href="/search/chatgpt/1.htm">chatgpt</a><a class="tag" taget="_blank" href="/search/openai/1.htm">openai</a>
                        <div>Javascript版Langchain入门作者:AI小火箭的HB我是AI小火箭的HB,我探索和写作人工智能和语言交叉点的所有事物,范围从LLM,聊天机器人,语音机器人,开发框架,以数据为中心的潜在空间等。介绍LangChain是一个开源Python库,用于构建由大型语言模型(LLM)支持的应用程序。它提供了一个框架,将LLM与其他数据源(如互联网或个人文件)连接起来,允许开发人员将多个命令链接在</div>
                    </li>
                    <li><a href="/article/1884867343973150720.htm"
                           title="第23节课:前端调试技巧—掌握浏览器开发者工具与性能优化" target="_blank">第23节课:前端调试技巧—掌握浏览器开发者工具与性能优化</a>
                        <span class="text-muted">学问小小谢</span>
<a class="tag" taget="_blank" href="/search/HTML%E5%AD%A6%E4%B9%A0/1.htm">HTML学习</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/1.htm">性能优化</a><a class="tag" taget="_blank" href="/search/%E4%BA%A4%E4%BA%92/1.htm">交互</a><a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a><a class="tag" taget="_blank" href="/search/%E5%AE%89%E5%85%A8/1.htm">安全</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a>
                        <div>目录浏览器开发者工具常见的浏览器开发者工具浏览器开发者工具的基本使用打开开发者工具开发者工具的面板使用开发者工具进行调试Elements面板检查和编辑HTML检查和编辑CSSConsole面板输出日志和调试信息执行JavaScript代码Network面板监控网络请求分析请求和响应Performance面板记录和分析性能优化性能Application面板检查和管理资源调试存储性能优化与调试性能优化</div>
                    </li>
                    <li><a href="/article/1884867341020360704.htm"
                           title="第20节课: jQuery基础—简化JavaScript编程的强大工具" target="_blank">第20节课: jQuery基础—简化JavaScript编程的强大工具</a>
                        <span class="text-muted">学问小小谢</span>
<a class="tag" taget="_blank" href="/search/HTML%E5%AD%A6%E4%B9%A0/1.htm">HTML学习</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E4%BA%A4%E4%BA%92/1.htm">交互</a><a class="tag" taget="_blank" href="/search/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/1.htm">搜索引擎</a>
                        <div>目录jQuery简介为什么使用jQuery?jQuery选择器基本选择器属性选择器伪类选择器事件处理事件绑定常见事件类型事件触发动画效果基本动画自定义动画实践:使用jQuery增强网页交互示例:创建一个带有动画效果的按钮示例:创建一个交互式的导航菜单结语在Web开发中,JavaScript是实现网页交互和动态效果的核心语言。然而,原生JavaScript的语法有时显得繁琐,为了提高开发效率,jQu</div>
                    </li>
                    <li><a href="/article/1884860534789042176.htm"
                           title="代替Winform、Win32控件的一些界面框架Electron,QT等" target="_blank">代替Winform、Win32控件的一些界面框架Electron,QT等</a>
                        <span class="text-muted">专注VB编程开发20年</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/ui/1.htm">ui</a><a class="tag" taget="_blank" href="/search/%E7%95%8C%E9%9D%A2/1.htm">界面</a><a class="tag" taget="_blank" href="/search/%E6%A1%86%E6%9E%B6/1.htm">框架</a>
                        <div>以下是一些可以代替Winform、Win32控件,在VC++、VBA等EXE程序上用来做控件元素、表格数据绑定、窗口显示的WEBUI框架和工具:1.Electron特点:Electron是一个使用JavaScript、HTML和CSS构建跨平台桌面应用程序的框架。它允许开发者使用Web技术来创建桌面应用,具有良好的跨平台兼容性。适用场景:适用于需要快速开发跨平台桌面应用的场景,尤其是对UI灵活性和</div>
                    </li>
                    <li><a href="/article/1884857755848732672.htm"
                           title="1.“use strict“ 严格模式 - JS" target="_blank">1.“use strict“ 严格模式 - JS</a>
                        <span class="text-muted">个人意志想</span>
<a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/Little/1.htm">Little</a><a class="tag" taget="_blank" href="/search/Points/1.htm">Points</a><a class="tag" taget="_blank" href="/search/in/1.htm">in</a><a class="tag" taget="_blank" href="/search/JS/1.htm">JS</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/js/1.htm">js</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a>
                        <div>JS严格模式JS严格模式是指令在JavaScript1.8.5(ECMAScript5、ES5)开始引入的;是一种旨在消除歧义、语法规范、维护安全的模式;通过语句"usestrict"进行声明。声明与作用域在文件头部声明,整个文件代码都要遵循严格模式;在函数内部开头声明,函数体遵循严格模式;函数的严格模式是最佳选择,没必要整个文件都严格,或者可以一个文件就写一个函数。限制不允许使用未声明的变量(对</div>
                    </li>
                    <li><a href="/article/1884857758193348608.htm"
                           title="JavaScript笔记(5)严格模式" target="_blank">JavaScript笔记(5)严格模式</a>
                        <span class="text-muted">way_hj</span>
<a class="tag" taget="_blank" href="/search/JavaScript%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/1.htm">JavaScript学习笔记</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E4%B8%A5%E6%A0%BC%E6%A8%A1%E5%BC%8F/1.htm">严格模式</a><a class="tag" taget="_blank" href="/search/use/1.htm">use</a><a class="tag" taget="_blank" href="/search/strict/1.htm">strict</a>
                        <div>1.启用严格模式的指令:"usestrict"或'usestrict',即单引号或双引号均可,也许use将来会成为关键字。2."usestrict";以分号结尾,在不支持严格模式的浏览器中(如IE9及以下)被当作一般语句。3.必须作为全局或函数的首条语句才起到严格模式指令的作用,否则即是一条普通语句。usestrict;//严格模式指令必须在首行,如果之前有语句,它将被当作一个普通字符串,而不是启</div>
                    </li>
                    <li><a href="/article/1884853977196064768.htm"
                           title="22、JavaScript学习笔记——ES5严格模式" target="_blank">22、JavaScript学习笔记——ES5严格模式</a>
                        <span class="text-muted">lvh98</span>
<a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>ES5严格模式当前使用的ES语法是基于ES3.0的方法加上ES5.0的新增方法。默认情况下,ES3.0和ES5.0冲突的部分,会沿用ES3.0的方法;而在ES5.0严格模式下,冲突部分会使用ES5.0的方法。1.“usestrict”不再兼容ES3.0的一些不规则语法。使用全新的ES5.0规范。1.1ES5.0严格模式的启动要选择使用严格模式,需要使用严格模式编译指示(pragma),即一个不赋值</div>
                    </li>
                    <li><a href="/article/1884852839122006016.htm"
                           title="用SpringBoot+mysql+html实现ATM 系统总结与扩展" target="_blank">用SpringBoot+mysql+html实现ATM 系统总结与扩展</a>
                        <span class="text-muted">SAFE20242034</span>
<a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/%E4%B8%80/1.htm">一</a><a class="tag" taget="_blank" href="/search/SpringBoot/1.htm">SpringBoot</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a>
                        <div>这里写目录标题ATM系统总结与扩展项目概述主要功能模块1.用户注册2.用户登录3.账户查询4.存款与取款5.转账6.修改密码7.销户系统改进建议功能扩展技术优化完整代码实现数据库表设计后端代码(SpringBoot示例)1.Account实体类2.AccountRepository接口3.AccountController类前端代码(HTML+JavaScript示例)实际开发与部署步骤**1.开</div>
                    </li>
                    <li><a href="/article/1884848932098469888.htm"
                           title="《JS教程》笔记:一、JavaScript编程语言——2.3现代模式use strict(严格模式、旧模式)" target="_blank">《JS教程》笔记:一、JavaScript编程语言——2.3现代模式use strict(严格模式、旧模式)</a>
                        <span class="text-muted">Dontla</span>
<a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>现代JavaScript教程中文版现代JavaScript教程文章目录现代模式,"usestrict""usestrict"确保\"usestrict\"出现在最顶部没有办法取消`usestrict`浏览器控制台默认不启动`usestrict`是否应该显式声明"usestrict"?(非必须,有办法自动启用)现代模式,“usestrict”长久以来,JavaScript不断向前发展且并未带来任何兼</div>
                    </li>
                    <li><a href="/article/1884813618814447616.htm"
                           title="深入解析Vue3响应式系统:从Proxy实现到依赖收集的核心原理" target="_blank">深入解析Vue3响应式系统:从Proxy实现到依赖收集的核心原理</a>
                        <span class="text-muted">苹果酱0567</span>
<a class="tag" taget="_blank" href="/search/%E9%9D%A2%E8%AF%95%E9%A2%98%E6%B1%87%E6%80%BB%E4%B8%8E%E8%A7%A3%E6%9E%90/1.htm">面试题汇总与解析</a><a class="tag" taget="_blank" href="/search/%E8%AF%BE%E7%A8%8B%E8%AE%BE%E8%AE%A1/1.htm">课程设计</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/boot/1.htm">boot</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a>
                        <div>深入解析Vue3响应式系统:从Proxy实现到依赖收集的核心原理响应式系统的基本原理作为一个热门的JavaScript框架,Vue在3.x版本中引入了基于Proxy的响应式系统。这个系统的核心思想是利用Proxy对象拦截对数据的访问和修改,从而实现数据的自动更新。当我们改变一个被代理的对象时,相关的视图会自动更新,无需手动干预。这一创新的设计让Vue3在性能和开发体验上都有了大幅度的改进。如何实现</div>
                    </li>
                    <li><a href="/article/1884812608419196928.htm"
                           title="JavaScript学习记录22" target="_blank">JavaScript学习记录22</a>
                        <span class="text-muted">济南小草根</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>第十节RegExp对象1.概述正则表达式(regularexpression)是一种表达文本模式(即字符串结构)的方法,有点像字符串的模板,常常用来按照“给定模式”匹配文本。比如,正则表达式给出一个Email地址的模式,然后用它来确定一个字符串是否为Email地址。JavaScript的正则表达式体系是参照Perl5建立的。新建正则表达式有两种方法。一种是使用字面量,以斜杠表示开始和结束。varr</div>
                    </li>
                    <li><a href="/article/1884786392710574080.htm"
                           title="TypeScript 学习 - 创建一个项目" target="_blank">TypeScript 学习 - 创建一个项目</a>
                        <span class="text-muted">草明</span>
<a class="tag" taget="_blank" href="/search/TypeScript/1.htm">TypeScript</a><a class="tag" taget="_blank" href="/search/typescript/1.htm">typescript</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a>
                        <div>创建一个项目实际使用中,使用yarncreatereact-app比npxcreate-react-app更顺利一些.使用yarncreatereact-appts-react-app--templatetypescript创建一个TypeScript项目使用yarncreatereact-appts-react-app创建一个JavaScript项目如果不使用脚手架创建,可以安装依赖,以及编写入</div>
                    </li>
                    <li><a href="/article/1884761286818983936.htm"
                           title="开源 CSS 框架 Tailwind CSS v4.0" target="_blank">开源 CSS 框架 Tailwind CSS v4.0</a>
                        <span class="text-muted">timer_017</span>
<a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a>
                        <div>开源CSS框架TailwindCSSv4.0于1月22日正式发布,除了显著提升性能、简化配置体验外,还增强了功能特性,具体如下1:性能提升采用全新的高性能引擎Oxide,带来了构建速度的巨大飞跃:全量构建速度提升超3.5倍。增量构建速度提升超8倍。无新CSS的增量构建速度提升182倍。配置优化CSS优先配置:从JavaScript配置文件改为直接在CSS文件中使用@theme指令进行配置,简化了项</div>
                    </li>
                    <li><a href="/article/1884741367918292992.htm"
                           title="27.useFetch" target="_blank">27.useFetch</a>
                        <span class="text-muted">@大迁世界</span>
<a class="tag" taget="_blank" href="/search/php/1.htm">php</a><a class="tag" taget="_blank" href="/search/react.js/1.htm">react.js</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/react/1.htm">react</a><a class="tag" taget="_blank" href="/search/native/1.htm">native</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>在React应用开发中,处理网络请求是一个常见而重要的任务。虽然JavaScript的fetchAPI提供了一种现代化的方式来进行网络请求,但在React组件中使用它可能会变得复杂。useFetch钩子提供了一种声明式的方法来处理网络请求,简化了错误处理、加载状态管理和请求取消等复杂操作。以下是如何实现和使用这个自定义钩子:const useFetch = (url, options) => { </div>
                    </li>
                    <li><a href="/article/1884727498902663168.htm"
                           title="Django 静态文件配置实战指南" target="_blank">Django 静态文件配置实战指南</a>
                        <span class="text-muted">ivwdcwso</span>
<a class="tag" taget="_blank" href="/search/django/1.htm">django</a><a class="tag" taget="_blank" href="/search/sqlite/1.htm">sqlite</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91/1.htm">开发</a>
                        <div>静态文件(如CSS、JavaScript、图片等)是Django项目中构建用户界面的重要组成部分。然而,静态文件的配置问题常常导致开发和生产环境中的404NotFound错误。本文将详细介绍如何正确配置Django静态文件,结合最佳实践,帮助你解决常见的静态文件问题,并确保项目在不同环境中正常运行。©ivwdcwso(ID:u012172506)1.静态文件的基本概念1.1什么是静态文件?静态文件</div>
                    </li>
                    <li><a href="/article/1884687911941632000.htm"
                           title="814. 二叉树剪枝(JavaScript)" target="_blank">814. 二叉树剪枝(JavaScript)</a>
                        <span class="text-muted">进击的桐人</span>
<a class="tag" taget="_blank" href="/search/leetcode/1.htm">leetcode</a><a class="tag" taget="_blank" href="/search/%E4%B8%AD%E7%AD%89%E9%A2%98/1.htm">中等题</a><a class="tag" taget="_blank" href="/search/medium/1.htm">medium</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/LeetCode/1.htm">LeetCode</a><a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/Binary/1.htm">Binary</a><a class="tag" taget="_blank" href="/search/Tree/1.htm">Tree</a><a class="tag" taget="_blank" href="/search/Pruning/1.htm">Pruning</a>
                        <div>给定二叉树根结点root,此外树的每个结点的值要么是0,要么是1。返回移除了所有不包含1的子树的原二叉树。(节点X的子树为X本身,以及所有X的后代。)示例1:输入:[1,null,0,0,1]输出:[1,null,0,null,1]解释:只有红色节点满足条件“所有不包含1的子树”。右图为返回的答案。示例2:输入:[1,0,1,0,0,0,1]输出:[1,null,1,null,1]示例3:输入:[</div>
                    </li>
                    <li><a href="/article/1884619199783956480.htm"
                           title="Photoshop脚本编程简介" target="_blank">Photoshop脚本编程简介</a>
                        <span class="text-muted">清枫草塘</span>
<a class="tag" taget="_blank" href="/search/UI%E8%AE%BE%E8%AE%A1/1.htm">UI设计</a><a class="tag" taget="_blank" href="/search/photoshop/1.htm">photoshop</a><a class="tag" taget="_blank" href="/search/%E8%84%9A%E6%9C%AC%E7%BC%96%E7%A8%8B/1.htm">脚本编程</a>
                        <div>自动化对每个设计师的工作来说是很有用的。它可以在重复的任务上节省宝贵的时间,还能够帮我们更快捷、更容易的解决一系列问题。你可以使用photoshop的动作来使工作流程自动化,这是很流行的,大多数人都知道并且已经在使用的方法。今天,我们将介绍给你一种高级的自动化技巧:脚本语言。所有的这一切仅仅需要你有一点点关于JavaScript的基本知识,这对于我们中的一些网页设计师往往都是具备的。我很多年前就知</div>
                    </li>
                    <li><a href="/article/1884616427516784640.htm"
                           title="JavaScript系列(49)--游戏引擎实现详解" target="_blank">JavaScript系列(49)--游戏引擎实现详解</a>
                        <span class="text-muted">ᅟᅠ        ‌‍‎‏ 一进制</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E/1.htm">游戏引擎</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>JavaScript游戏引擎实现详解今天,让我们深入探讨JavaScript的游戏引擎实现。游戏引擎是一个复杂的系统,它需要处理渲染、物理、音频、输入等多个方面,让我们一步步实现一个基础但功能完整的游戏引擎。游戏引擎基础概念小知识:游戏引擎是一个为游戏开发提供核心功能的框架,它通常包括渲染系统、物理引擎、音频系统、输入处理、资源管理等模块。通过合理的架构设计,这些模块可以协同工作,为游戏开发提供强</div>
                    </li>
                    <li><a href="/article/1884610372053299200.htm"
                           title="Tailwind CSS 正式发布了 4.0 版本" target="_blank">Tailwind CSS 正式发布了 4.0 版本</a>
                        <span class="text-muted">timer_017</span>
<a class="tag" taget="_blank" href="/search/css/1.htm">css</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>1月22日,TailwindCSS正式发布了4.0版本124。以下是该版本的一些新特性:性能提升采用全新的高性能引擎Oxide,基准测试显示全量构建速度提升超3.5倍,增量构建速度提升超8倍,无新CSS的增量构建速度提升182倍。配置优化CSS优先配置:从JavaScript配置文件改为直接在CSS文件中使用@theme指令进行配置,简化项目文件结构。自动源检测:自动检测内容源,忽略.gitign</div>
                    </li>
                    <li><a href="/article/1884594474865192960.htm"
                           title="Couchbase UI: Eventing" target="_blank">Couchbase UI: Eventing</a>
                        <span class="text-muted">PersistDZ</span>
<a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8/1.htm">数据存储</a><a class="tag" taget="_blank" href="/search/ui/1.htm">ui</a><a class="tag" taget="_blank" href="/search/couchbase/1.htm">couchbase</a>
                        <div>Couchbase的Eventing页面用于创建和管理事件处理函数(EventHandlers),这些函数可以在特定的事件发生时自动触发。Eventing是Couchbase提供的一种功能,允许用户响应数据变更事件(例如文档创建、更新或删除),并在这些事件发生时执行自定义的业务逻辑。Eventing页面功能概述事件处理函数:在Eventing页面中,您可以编写JavaScript函数,以定义在特定</div>
                    </li>
                    <li><a href="/article/1884575172355223552.htm"
                           title="Banana JS,一个严格子集 JavaScript 的解释器" target="_blank">Banana JS,一个严格子集 JavaScript 的解释器</a>
                        <span class="text-muted">沙老师</span>
<a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a><a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a>
                        <div>项目地址:https://github.com/shajunxing/banana-js特色我的目标是剔除我在实践中总结的JavaScript语言的没用的和模棱两可的部分,只保留我喜欢和需要的,创建一个最小的语法解释器。只支持JSON兼容的数据类型和函数,函数是第一类值,函数支持闭包。我不喜欢面向对象编程,所以所有与类相关的内容都不支持。没有任何内置不可删除的全局变量、全局函数或对象成员,哪怕解释</div>
                    </li>
                    <li><a href="/article/1884531637883039744.htm"
                           title="创建一个完整的购物商城代码涉及多个方面,包括前端、后端、数据库等,并且通常不会只用一种语言来完成。不过,我可以为你概述一个购物商城的基本架构,并给出不同部分可能使用的编程语言示例。" target="_blank">创建一个完整的购物商城代码涉及多个方面,包括前端、后端、数据库等,并且通常不会只用一种语言来完成。不过,我可以为你概述一个购物商城的基本架构,并给出不同部分可能使用的编程语言示例。</a>
                        <span class="text-muted">DoloresBerna</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E5%BA%93/1.htm">数据库</a><a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84/1.htm">架构</a>
                        <div>前端yangzhie289.comHTML/CSS/JavaScript:负责用户界面和用户体验。示例:使用HTML构建页面结构,CSS进行样式设计,JavaScript处理交互逻辑。html.........javascript//app.js//示例:使用FetchAPI从后端获取商品数据fetch('/api/products').then(response=>response.json()</div>
                    </li>
                    <li><a href="/article/1884527856751538176.htm"
                           title="python画与x轴平行_少儿编程:python趣味编程第一课" target="_blank">python画与x轴平行_少儿编程:python趣味编程第一课</a>
                        <span class="text-muted">weixin_39762478</span>
<a class="tag" taget="_blank" href="/search/python%E7%94%BB%E4%B8%8Ex%E8%BD%B4%E5%B9%B3%E8%A1%8C/1.htm">python画与x轴平行</a>
                        <div>本文仅针对8-16岁的青少年,所以流程是按如何去教好中小学生走的,并不适合成人找工作学习,因为进度也是按照青少年走的大家好,我是C大叔,从事少儿编程行业三年有余(2016年从事少儿编程行业,少儿编程概念是2015年在中国正式提出的)。一直以来都是在做scratch,JavaScript以及信息学奥赛C++的讲师,教研等工作,但目前发现python课程非常火爆,为了让小朋友也能更好的学python课</div>
                    </li>
                    <li><a href="/article/1884477177651589120.htm"
                           title="JS中的Date()操作与易错点" target="_blank">JS中的Date()操作与易错点</a>
                        <span class="text-muted">不做超级小白</span>
<a class="tag" taget="_blank" href="/search/web%E5%89%8D%E7%AB%AF/1.htm">web前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/ecmascript/1.htm">ecmascript</a>
                        <div>在JavaScript中处理日期的高级策略JavaScript的Date对象是时间和日期处理的核心,但其设计上存在一些隐含的复杂性,可能导致错误的使用和理解。针对研究生及专业研究人员,本文将从理论和实践两方面深入探讨JavaScript日期处理的关键问题和解决方案。1.时间与日期的表示模型JavaScript的Date对象基于Unix时间戳,记录自1970年1月1日00:00:00UTC起的毫秒数</div>
                    </li>
                    <li><a href="/article/1884459871202504704.htm"
                           title="Phaser引擎开发:Phaser基础入门_Phaser引擎概述v1" target="_blank">Phaser引擎开发:Phaser基础入门_Phaser引擎概述v1</a>
                        <span class="text-muted">chenlz2007</span>
<a class="tag" taget="_blank" href="/search/%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%912/1.htm">游戏开发2</a><a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/%E6%B8%B8%E6%88%8F/1.htm">游戏</a><a class="tag" taget="_blank" href="/search/ui/1.htm">ui</a>
                        <div>Phaser引擎概述Phaser是一个开源的HTML5游戏开发框架,广泛用于创建2D游戏。它基于Pixi.js,一个高性能的2D渲染引擎,旨在简化游戏开发过程,使开发者能够专注于游戏逻辑和创意。Phaser支持多种游戏类型,但特别适合用于开发动作游戏,因为它提供了丰富的功能和灵活的API来处理游戏中的动画、物理、输入等关键要素。什么是Phaser引擎?Phaser引擎是一个使用JavaScript</div>
                    </li>
                    <li><a href="/article/1884459867813507072.htm"
                           title="Phaser引擎开发:Phaser基础入门_Phaser引擎概述" target="_blank">Phaser引擎开发:Phaser基础入门_Phaser引擎概述</a>
                        <span class="text-muted">chenlz2007</span>
<a class="tag" taget="_blank" href="/search/%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%912/1.htm">游戏开发2</a><a class="tag" taget="_blank" href="/search/html5/1.htm">html5</a><a class="tag" taget="_blank" href="/search/%E6%B8%B8%E6%88%8F/1.htm">游戏</a><a class="tag" taget="_blank" href="/search/%E9%9F%B3%E8%A7%86%E9%A2%91/1.htm">音视频</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/html/1.htm">html</a>
                        <div>Phaser引擎概述什么是Phaser引擎Phaser是一个免费的、开源的、基于Web的2D游戏开发引擎,使用JavaScript和HTML5Canvas或WebGL技术。Phaser的目的是让游戏开发者能够轻松地创建高性能的2D游戏,适用于桌面和移动平台。Phaser支持多种游戏开发需求,包括精灵管理、物理引擎、动画、输入处理、音频和视频处理等。Phaser的历史Phaser由RichardDa</div>
                    </li>
                    <li><a href="/article/1884448123405791232.htm"
                           title="QT6.5+qt-quick学习笔记" target="_blank">QT6.5+qt-quick学习笔记</a>
                        <span class="text-muted">m0_63052064</span>
<a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a>
                        <div>为什么用QMLQML是一种声明式语言,这意味着开发者只需要描述用户界面的外观和行为,而不需要关心具体的实现细节。这种方式减少了代码量,使得界面设计更加直观和高效。QML提供了丰富的UI组件和动画效果,开发者可以快速创建出现代化、用户友好的应用程序QML基于JavaScript并且与JavaScript的结合使得创建交互式和动画效果变得简单且高效。开发以Debug方式可以按步运行,调试;releas</div>
                    </li>
                                <li><a href="/article/122.htm"
                                       title="java数字签名三种方式" target="_blank">java数字签名三种方式</a>
                                    <span class="text-muted">知了ing</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/jdk/1.htm">jdk</a>
                                    <div>以下3钟数字签名都是基于jdk7的 
1,RSA 
 

String password="test";
			// 1.初始化密钥
			KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
			keyPairGenerator.initialize(51</div>
                                </li>
                                <li><a href="/article/249.htm"
                                       title="Hibernate学习笔记" target="_blank">Hibernate学习笔记</a>
                                    <span class="text-muted">caoyong</span>
<a class="tag" taget="_blank" href="/search/Hibernate/1.htm">Hibernate</a>
                                    <div>1>、Hibernate是数据访问层框架,是一个ORM(Object Relation Mapping)框架,作者为:Gavin King 
2>、搭建Hibernate的开发环境 
     a>、添加jar包: 
      aa>、hibernatte开发包中/lib/required/所</div>
                                </li>
                                <li><a href="/article/376.htm"
                                       title="设计模式之装饰器模式Decorator(结构型)" target="_blank">设计模式之装饰器模式Decorator(结构型)</a>
                                    <span class="text-muted">漂泊一剑客</span>
<a class="tag" taget="_blank" href="/search/Decorator/1.htm">Decorator</a>
                                    <div>1. 概述 
 
 
       若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性。如果已经存在的一个类缺少某些方法,或者须要给方法添加更多的功能(魅力),你也许会仅仅继承这个类来产生一个新类—这建立在额外的代码上。 
 
      </div>
                                </li>
                                <li><a href="/article/503.htm"
                                       title="读取磁盘文件txt,并输入String" target="_blank">读取磁盘文件txt,并输入String</a>
                                    <span class="text-muted">一炮送你回车库</span>
<a class="tag" taget="_blank" href="/search/String/1.htm">String</a>
                                    <div>public static void main(String[] args) throws IOException { 
  
 String fileContent = readFileContent("d:/aaa.txt"); 
  
 System.out.println(fileContent); 
   
</div>
                                </li>
                                <li><a href="/article/630.htm"
                                       title="js三级联动下拉框" target="_blank">js三级联动下拉框</a>
                                    <span class="text-muted">3213213333332132</span>
<a class="tag" taget="_blank" href="/search/%E4%B8%89%E7%BA%A7%E8%81%94%E5%8A%A8/1.htm">三级联动</a>
                                    <div>
//三级联动  
   省/直辖市<select id="province"></select>  
   市/省直辖<select id="city"></select>  
   县/区 <select id="area"></select>  
  </div>
                                </li>
                                <li><a href="/article/757.htm"
                                       title="erlang之parse_transform编译选项的应用" target="_blank">erlang之parse_transform编译选项的应用</a>
                                    <span class="text-muted">616050468</span>
<a class="tag" taget="_blank" href="/search/parse_transform/1.htm">parse_transform</a><a class="tag" taget="_blank" href="/search/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">游戏服务器</a><a class="tag" taget="_blank" href="/search/%E5%B1%9E%E6%80%A7%E5%90%8C%E6%AD%A5/1.htm">属性同步</a><a class="tag" taget="_blank" href="/search/abstract_code/1.htm">abstract_code</a>
                                    <div>         最近使用erlang重构了游戏服务器的所有代码,之前看过C++/lua写的服务器引擎代码,引擎实现了玩家属性自动同步给前端和增量更新玩家数据到数据库的功能,这也是现在很多游戏服务器的优化方向,在引擎层面去解决数据同步和数据持久化,数据发生变化了业务层不需要关心怎么去同步给前端。由于游戏过程中玩家每个业务中玩家数据更改的量其实是很少</div>
                                </li>
                                <li><a href="/article/884.htm"
                                       title="JAVA JSON的解析" target="_blank">JAVA JSON的解析</a>
                                    <span class="text-muted">darkranger</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>
 
//		{
//			“Total”:“条数”,
//			 Code: 1,
//
//			“PaymentItems”:[
//			{
//			“PaymentItemID”:”支款单ID”,
//			“PaymentCode”:”支款单编号”,
//			“PaymentTime”:”支款日期”,
//			”ContractNo”:”合同号”,
//	</div>
                                </li>
                                <li><a href="/article/1011.htm"
                                       title="POJ-1273-Drainage Ditches" target="_blank">POJ-1273-Drainage Ditches</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/ACM_POJ/1.htm">ACM_POJ</a>
                                    <div>POJ-1273-Drainage Ditches 
http://poj.org/problem?id=1273 
基本的最大流,按LRJ的白书写的 
 
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
#define INF 0x7fffffff
int ma</div>
                                </li>
                                <li><a href="/article/1138.htm"
                                       title="工作流Activiti5表的命名及含义" target="_blank">工作流Activiti5表的命名及含义</a>
                                    <span class="text-muted">atongyeye</span>
<a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C%E6%B5%81/1.htm">工作流</a><a class="tag" taget="_blank" href="/search/Activiti/1.htm">Activiti</a>
                                    <div>activiti5 - http://activiti.org/designer/update在线插件安装 
 
activiti5一共23张表 
 
 
Activiti的表都以ACT_开头。 第二部分是表示表的用途的两个字母标识。 用途也和服务的API对应。 
 
ACT_RE_*: 'RE'表示repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。 
 
A</div>
                                </li>
                                <li><a href="/article/1265.htm"
                                       title="android的广播机制和广播的简单使用" target="_blank">android的广播机制和广播的简单使用</a>
                                    <span class="text-muted">百合不是茶</span>
<a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/%E5%B9%BF%E6%92%AD%E6%9C%BA%E5%88%B6/1.htm">广播机制</a><a class="tag" taget="_blank" href="/search/%E5%B9%BF%E6%92%AD%E7%9A%84%E6%B3%A8%E5%86%8C/1.htm">广播的注册</a>
                                    <div>      Android广播机制简介  在Android中,有一些操作完成以后,会发送广播,比如说发出一条短信,或打出一个电话,如果某个程序接收了这个广播,就会做相应的处理。这个广播跟我们传统意义中的电台广播有些相似之处。之所以叫做广播,就是因为它只负责“说”而不管你“听不听”,也就是不管你接收方如何处理。另外,广播可以被不只一个应用程序所接收,当然也可能不被任何应</div>
                                </li>
                                <li><a href="/article/1392.htm"
                                       title="Spring事务传播行为详解" target="_blank">Spring事务传播行为详解</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%E4%BA%8B%E5%8A%A1%E4%BC%A0%E6%92%AD%E8%A1%8C%E4%B8%BA/1.htm">事务传播行为</a>
                                    <div>        在service类前加上@Transactional,声明这个service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。 
        Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这</div>
                                </li>
                                <li><a href="/article/1519.htm"
                                       title="eidtplus operate" target="_blank">eidtplus operate</a>
                                    <span class="text-muted">征客丶</span>
<a class="tag" taget="_blank" href="/search/eidtplus/1.htm">eidtplus</a>
                                    <div>开启列模式: Alt+C 鼠标选择   OR   Alt+鼠标左键拖动 
列模式替换或复制内容(多行): 
右键-->格式-->填充所选内容-->选择相应操作 
OR 
Ctrl+Shift+V(复制多行数据,必须行数一致) 
 
-------------------------------------------------------</div>
                                </li>
                                <li><a href="/article/1646.htm"
                                       title="【Kafka一】Kafka入门" target="_blank">【Kafka一】Kafka入门</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/kafka/1.htm">kafka</a>
                                    <div>这篇文章来自Spark集成Kafka(http://bit1129.iteye.com/blog/2174765),这里把它单独取出来,作为Kafka的入门吧 
  下载Kafka 
 http://mirror.bit.edu.cn/apache/kafka/0.8.1.1/kafka_2.10-0.8.1.1.tgz 
 2.10表示Scala的版本,而0.8.1.1表示Kafka</div>
                                </li>
                                <li><a href="/article/1773.htm"
                                       title="Spring 事务实现机制" target="_blank">Spring 事务实现机制</a>
                                    <span class="text-muted">BlueSkator</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%E4%BB%A3%E7%90%86/1.htm">代理</a><a class="tag" taget="_blank" href="/search/%E4%BA%8B%E5%8A%A1/1.htm">事务</a>
                                    <div>Spring是以代理的方式实现对事务的管理。我们在Action中所使用的Service对象,其实是代理对象的实例,并不是我们所写的Service对象实例。既然是两个不同的对象,那为什么我们在Action中可以象使用Service对象一样的使用代理对象呢?为了说明问题,假设有个Service类叫AService,它的Spring事务代理类为AProxyService,AService实现了一个接口 </div>
                                </li>
                                <li><a href="/article/1900.htm"
                                       title="bootstrap源码学习与示例:bootstrap-dropdown(转帖)" target="_blank">bootstrap源码学习与示例:bootstrap-dropdown(转帖)</a>
                                    <span class="text-muted">BreakingBad</span>
<a class="tag" taget="_blank" href="/search/bootstrap/1.htm">bootstrap</a><a class="tag" taget="_blank" href="/search/dropdown/1.htm">dropdown</a>
                                    <div>bootstrap-dropdown组件是个烂东西,我读后的整体感觉。 
一个下拉开菜单的设计: 
                  <ul class="nav pull-right">
                        <li id="fat-menu" class="dropdown">
</div>
                                </li>
                                <li><a href="/article/2027.htm"
                                       title="读《研磨设计模式》-代码笔记-中介者模式-Mediator" target="_blank">读《研磨设计模式》-代码笔记-中介者模式-Mediator</a>
                                    <span class="text-muted">bylijinnan</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a>
                                    <div>声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/ 
 
 



/*
 * 中介者模式(Mediator):用一个中介对象来封装一系列的对象交互。
 * 中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
 * 
 * 在我看来,Mediator模式是把多个对象(</div>
                                </li>
                                <li><a href="/article/2154.htm"
                                       title="常用代码记录" target="_blank">常用代码记录</a>
                                    <span class="text-muted">chenjunt3</span>
<a class="tag" taget="_blank" href="/search/UI/1.htm">UI</a><a class="tag" taget="_blank" href="/search/Excel/1.htm">Excel</a><a class="tag" taget="_blank" href="/search/J%23/1.htm">J#</a>
                                    <div>  
1、单据设置某行或某字段不能修改

//i是行号,"cash"是字段名称
getBillCardPanelWrapper().getBillCardPanel().getBillModel().setCellEditable(i, "cash", false);
//取得单据表体所有项用以上语句做循环就能设置整行了
getBillC</div>
                                </li>
                                <li><a href="/article/2281.htm"
                                       title="搜索引擎与工作流引擎" target="_blank">搜索引擎与工作流引擎</a>
                                    <span class="text-muted">comsci</span>
<a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C/1.htm">工作</a><a class="tag" taget="_blank" href="/search/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/1.htm">搜索引擎</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%BA%94%E7%94%A8/1.htm">网络应用</a>
                                    <div>  
 
    最近在公司做和搜索有关的工作,(只是简单的应用开源工具集成到自己的产品中)工作流系统的进一步设计暂时放在一边了,偶然看到谷歌的研究员吴军写的数学之美系列中的搜索引擎与图论这篇文章中的介绍,我发现这样一个关系(仅仅是猜想) 
  -----搜索引擎和流程引擎的基础--都是图论,至少像在我在JWFD中引擎算法中用到的是自定义的广度优先</div>
                                </li>
                                <li><a href="/article/2408.htm"
                                       title="oracle Health Monitor" target="_blank">oracle Health Monitor</a>
                                    <span class="text-muted">daizj</span>
<a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/Health+Monitor/1.htm">Health Monitor</a>
                                    <div>About Health Monitor 
 
Beginning with Release 11g, Oracle Database includes a framework called Health Monitor for running diagnostic checks on the database. 
 
About Health Monitor Checks 
 
Health M</div>
                                </li>
                                <li><a href="/article/2535.htm"
                                       title="JSON字符串转换为对象" target="_blank">JSON字符串转换为对象</a>
                                    <span class="text-muted">dieslrae</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/json/1.htm">json</a>
                                    <div>    作为前言,首先是要吐槽一下公司的脑残编译部署方式,web和core分开部署本来没什么问题,但是这丫居然不把json的包作为基础包而作为web的包,导致了core端不能使用,而且我们的core是可以当web来用的(不要在意这些细节),所以在core中处理json串就是个问题.没办法,跟编译那帮人也扯不清楚,只有自己写json的解析了. 
  </div>
                                </li>
                                <li><a href="/article/2662.htm"
                                       title="C语言学习八结构体,综合应用,学生管理系统" target="_blank">C语言学习八结构体,综合应用,学生管理系统</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/C%E8%AF%AD%E8%A8%80/1.htm">C语言</a>
                                    <div>实现功能的代码: 
# include <stdio.h>
# include <malloc.h>

struct Student
{
	int age;
	float score;
	char name[100];
};


int main(void)
{
	int len;
	struct Student * pArr;
	int i,</div>
                                </li>
                                <li><a href="/article/2789.htm"
                                       title="vagrant学习笔记" target="_blank">vagrant学习笔记</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/vagrant/1.htm">vagrant</a>
                                    <div>想了解多主机是如何定义和使用的, 所以又学习了一遍vagrant 
  
1. vagrant virtualbox 下载安装 
https://www.vagrantup.com/downloads.html 
https://www.virtualbox.org/wiki/Downloads 
  
查看安装在命令行输入vagrant 
  
  
2.</div>
                                </li>
                                <li><a href="/article/2916.htm"
                                       title="14.性能优化-优化-软件配置优化" target="_blank">14.性能优化-优化-软件配置优化</a>
                                    <span class="text-muted">frank1234</span>
<a class="tag" taget="_blank" href="/search/%E8%BD%AF%E4%BB%B6%E9%85%8D%E7%BD%AE/1.htm">软件配置</a><a class="tag" taget="_blank" href="/search/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/1.htm">性能优化</a>
                                    <div>1.Tomcat线程池 
 
修改tomcat的server.xml文件: 
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxThreads="1200" m</div>
                                </li>
                                <li><a href="/article/3043.htm"
                                       title="一个不错的shell 脚本教程 入门级" target="_blank">一个不错的shell 脚本教程 入门级</a>
                                    <span class="text-muted">HarborChung</span>
<a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/shell/1.htm">shell</a>
                                    <div>一个不错的shell 脚本教程 入门级 
建立一个脚本   Linux中有好多中不同的shell,但是通常我们使用bash (bourne again shell) 进行shell编程,因为bash是免费的并且很容易使用。所以在本文中笔者所提供的脚本都是使用bash(但是在大多数情况下,这些脚本同样可以在 bash的大姐,bourne shell中运行)。   如同其他语言一样</div>
                                </li>
                                <li><a href="/article/3170.htm"
                                       title="Spring4新特性——核心容器的其他改进" target="_blank">Spring4新特性——核心容器的其他改进</a>
                                    <span class="text-muted">jinnianshilongnian</span>
<a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/1.htm">动态代理</a><a class="tag" taget="_blank" href="/search/spring4/1.htm">spring4</a><a class="tag" taget="_blank" href="/search/%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5/1.htm">依赖注入</a>
                                    <div>Spring4新特性——泛型限定式依赖注入 
Spring4新特性——核心容器的其他改进 
Spring4新特性——Web开发的增强 
Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC  
Spring4新特性——Groovy Bean定义DSL 
Spring4新特性——更好的Java泛型操作API  
Spring4新</div>
                                </li>
                                <li><a href="/article/3297.htm"
                                       title="Linux设置tomcat开机启动" target="_blank">Linux设置tomcat开机启动</a>
                                    <span class="text-muted">liuxingguome</span>
<a class="tag" taget="_blank" href="/search/tomcat/1.htm">tomcat</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%9C%BA%E8%87%AA%E5%90%AF%E5%8A%A8/1.htm">开机自启动</a>
                                    <div>执行命令sudo gedit /etc/init.d/tomcat6 
 
然后把以下英文部分复制过去。(注意第一句#!/bin/sh如果不写,就不是一个shell文件。然后将对应的jdk和tomcat换成你自己的目录就行了。 
 
#!/bin/bash 
 
# 
 
# /etc/rc.d/init.d/tomcat 
 
# init script for tomcat precesses</div>
                                </li>
                                <li><a href="/article/3424.htm"
                                       title="第13章 Ajax进阶(下)" target="_blank">第13章 Ajax进阶(下)</a>
                                    <span class="text-muted">onestopweb</span>
<a class="tag" taget="_blank" href="/search/Ajax/1.htm">Ajax</a>
                                    <div>index.html 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/</div>
                                </li>
                                <li><a href="/article/3551.htm"
                                       title="Troubleshooting Crystal Reports off BW" target="_blank">Troubleshooting Crystal Reports off BW</a>
                                    <span class="text-muted">blueoxygen</span>
<a class="tag" taget="_blank" href="/search/BO/1.htm">BO</a>
                                    <div>http://wiki.sdn.sap.com/wiki/display/BOBJ/Troubleshooting+Crystal+Reports+off+BW#TroubleshootingCrystalReportsoffBW-TracingBOE 
  
Quite useful, especially this part:  
SAP BW connectivity 
For t</div>
                                </li>
                                <li><a href="/article/3678.htm"
                                       title="Java开发熟手该当心的11个错误" target="_blank">Java开发熟手该当心的11个错误</a>
                                    <span class="text-muted">tomcat_oracle</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/jvm/1.htm">jvm</a><a class="tag" taget="_blank" href="/search/%E5%A4%9A%E7%BA%BF%E7%A8%8B/1.htm">多线程</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/1.htm">单元测试</a>
                                    <div>#1、不在属性文件或XML文件中外化配置属性。比如,没有把批处理使用的线程数设置成可在属性文件中配置。你的批处理程序无论在DEV环境中,还是UAT(用户验收 
测试)环境中,都可以顺畅无阻地运行,但是一旦部署在PROD 上,把它作为多线程程序处理更大的数据集时,就会抛出IOException,原因可能是JDBC驱动版本不同,也可能是#2中讨论的问题。如果线程数目 可以在属性文件中配置,那么使它成为</div>
                                </li>
                                <li><a href="/article/3805.htm"
                                       title="正则表达式大全" target="_blank">正则表达式大全</a>
                                    <span class="text-muted">yang852220741</span>
<a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B/1.htm">编程</a><a class="tag" taget="_blank" href="/search/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/1.htm">正则表达式</a>
                                    <div>今天向大家分享正则表达式大全,它可以大提高你的工作效率 
正则表达式也可以被当作是一门语言,当你学习一门新的编程语言的时候,他们是一个小的子语言。初看时觉得它没有任何的意义,但是很多时候,你不得不阅读一些教程,或文章来理解这些简单的描述模式。 
一、校验数字的表达式 
 
数字:^[0-9]*$ 
n位的数字:^\d{n}$ 
至少n位的数字:^\d{n,}$ 
m-n位的数字:^\d{m,n}$</div>
                                </li>
                </ul>
            </div>
        </div>
    </div>

<div>
    <div class="container">
        <div class="indexes">
            <strong>按字母分类:</strong>
            <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a
                href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a
                href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a
                href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a
                href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a
                href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a
                href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a
                href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a
                href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a>
        </div>
    </div>
</div>
<footer id="footer" class="mb30 mt30">
    <div class="container">
        <div class="footBglm">
            <a target="_blank" href="/">首页</a> -
            <a target="_blank" href="/custom/about.htm">关于我们</a> -
            <a target="_blank" href="/search/Java/1.htm">站内搜索</a> -
            <a target="_blank" href="/sitemap.txt">Sitemap</a> -
            <a target="_blank" href="/custom/delete.htm">侵权投诉</a>
        </div>
        <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved.
<!--            <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>-->
        </div>
    </div>
</footer>
<!-- 代码高亮 -->
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script>
<script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script>
<link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/>
<script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script>





</body>

</html>