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

简介

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

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

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

页面结构

image-20210427170009062.png

我挑了一个比较典型也比较好看的结构来仿照,默认布局上下分成四部分,工具栏、编辑器、预览区域及控制台,编辑器又分为三部分,分别是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)
                }
            }
        }
    }
}

实现效果如下:

2021-04-29-19-15-42.gif

为了能提供多种布局的随意切换,我们有必要把上述逻辑封装一下,封装成两个组件,一个容器组件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种语言,我们选择中文的,其他不用的可以直接删掉:

image-20210430163748892.png

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

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

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

image-20210430154406199.png

其他几个常用的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> 
 <div class="image-package"> 
  <div class="image-container" style="max-width: 700px; max-height: 551px;"> 
   <div class="image-view">  
   </div> 
  </div> 
  <div class="image-caption">
    image-20210507141946844.png 
  </div> 
 </div> 
 <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>先介绍一种非常简单的方式,使用一个叫eruda的库,这个库是用来方便在手机上进行调试的,和<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> 
 <div class="image-package"> 
  <div class="image-container" style="max-width: 700px; max-height: 318px;"> 
   <div class="image-view">  
   </div> 
  </div> 
  <div class="image-caption">
    image-20210507154345054.png 
  </div> 
 </div> 
 <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> 
 <div class="image-package"> 
  <div class="image-container" style="max-width: 700px; max-height: 387px;"> 
   <div class="image-view">  
   </div> 
  </div> 
  <div class="image-caption">
    image-20210507165953197.png 
  </div> 
 </div> 
 <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> 
 <div class="image-package"> 
  <div class="image-container" style="max-width: 700px; max-height: 226px;"> 
   <div class="image-view">  
   </div> 
  </div> 
  <div class="image-caption">
    image-20210508091625420.png 
  </div> 
 </div> 
 <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> 
 <div class="image-package"> 
  <div class="image-container" style="max-width: 576px; max-height: 1064px;"> 
   <div class="image-view">  
   </div> 
  </div> 
  <div class="image-caption">
    image-20210508195753623.png 
  </div> 
 </div> 
 <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> 
 <div class="image-package"> 
  <div class="image-container" style="max-width: 394px; max-height: 1308px;"> 
   <div class="image-view">  
   </div> 
  </div> 
  <div class="image-caption">
    2021-05-08-20-00-57.gif 
  </div> 
 </div> 
 <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> 
 <div class="image-package"> 
  <div class="image-container" style="max-width: 700px; max-height: 276px;"> 
   <div class="image-view">  
   </div> 
  </div> 
  <div class="image-caption">
    image-20210512135732215.png 
  </div> 
 </div> 
 <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> 
 <div class="image-package"> 
  <div class="image-container" style="max-width: 700px; max-height: 506px;"> 
   <div class="image-view">  
   </div> 
  </div> 
  <div class="image-caption">
    image-20210512140705004.png 
  </div> 
 </div> 
 <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> 
 <div class="image-package"> 
  <div class="image-container" style="max-width: 394px; max-height: 194px;"> 
   <div class="image-view">  
   </div> 
  </div> 
  <div class="image-caption">
    2021-05-12-18-31-12.gif 
  </div> 
 </div> 
 <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>我们使用loadjs这个小巧的库,新增一个<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>可太难了,笔者基本都没找到,所以这里的大部分代码都是参考codepan的。</p> 
 <h1>其他功能</h1> 
 <p>另外还有一些实现起来简单,但是能很大提升用户体验的功能,比如添加额外的<code>css</code>或<code>js</code>资源,免去手写<code>link</code>或<code>script</code>标签的麻烦:</p> 
 <div class="image-package"> 
  <div class="image-container" style="max-width: 700px; max-height: 312px;"> 
   <div class="image-view">  
   </div> 
  </div> 
  <div class="image-caption">
    image-20210514140452547.png 
  </div> 
 </div> 
 <p>预设一些常用模板,比如<code>vue3</code>、<code>react</code>等,方便快速开始,免去写基本结构的麻烦:</p> 
 <div class="image-package"> 
  <div class="image-container" style="max-width: 700px; max-height: 660px;"> 
   <div class="image-view">  
   </div> 
  </div> 
  <div class="image-caption">
    2021-05-14-14-37-28.gif 
  </div> 
 </div> 
 <h1>有没有更快的方法</h1> 
 <p>如果你看到这里,你一定会说这是哪门子快速搭建,那有没有更快的方法呢,当然有了,就是直接克隆本项目的仓库或者codepan,改改就可以使用啦~</p> 
 <h1>结尾</h1> 
 <p>本文从零开始介绍了如何搭建一个代码在线编辑预览的工具,粗糙实现总有不足之处,欢迎指出。</p> 
 <p>项目仓库code-run,欢迎<code>star</code>。</p> 
</article>
                            </div>
                        </div>
                    </div>
                    <!--PC和WAP自适应版-->
                    <div id="SOHUCS" sid="1643666063432409088"></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">你可能感兴趣的:(快速搭建一个代码在线编辑预览工具)</h4>
        <div id="paradigm-article-related">
            <div class="recommend-post mb30">
                <ul class="widget-links">
                    <li><a href="/article/1899369078150197248.htm"
                           title="centos7使用yum网络安装" target="_blank">centos7使用yum网络安装</a>
                        <span class="text-muted"></span>

                        <div>CentOS7Yum网络安装完全指南核心原理分析Yum(YellowdogUpdater,Modified)作为RPM系统的智能化软件包管理工具,通过以下机制实现自动化安装:依赖解析:自动识别软件包的前置依赖关系仓库同步:连接配置的软件仓库(repo)获取元数据事务处理:采用原子化操作保证安装/更新的完整性️全流程安装步骤详解步骤1:连接CentOS7服务器sshusername@server-i</div>
                    </li>
                    <li><a href="/article/1899368789884071936.htm"
                           title="Git前言(版本控制)" target="_blank">Git前言(版本控制)</a>
                        <span class="text-muted">Starbright.</span>
<a class="tag" taget="_blank" href="/search/Git/1.htm">Git</a><a class="tag" taget="_blank" href="/search/git/1.htm">git</a>
                        <div>1.Git目前世界上最先进的分布式版本控制系统。git官网:https://git-scm.com/2.版本控制2.1什么是版本控制版本控制(Revisioncontrol)是一种在开发的过程中用于管理我们对文件、目录或工程等内容修改历史,方便查看更改历史记录备份以便恢复以前的版本的软件工程技术。实现跨区域多人协同开发追踪和记载一个或者多个文件的历史记录组织和保护你的源代码和文档统计工作量并行开发</div>
                    </li>
                    <li><a href="/article/1899368663480332288.htm"
                           title="Android入门(七) | 常用控件" target="_blank">Android入门(七) | 常用控件</a>
                        <span class="text-muted">·Jormungand</span>
<a class="tag" taget="_blank" href="/search/Android/1.htm">Android</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/studio/1.htm">studio</a>
                        <div>文章目录TextView控件:文本信息Button控件:按钮EditText控件:输入框ImageView控件:图片ProgressBar控件:进度条AlertDialog控件:提示框ProgressDialog控件:带有进度条的提示框TextView控件:文本信息TextView是Android中较常用的一个控件。主要用于在界面上显示一段文本信息,配置在每个活动的xml文件中。除了之前用到的an</div>
                    </li>
                    <li><a href="/article/1899368189171658752.htm"
                           title="HarmonyNext实战:基于ArkTS的高性能音视频播放器开发" target="_blank">HarmonyNext实战:基于ArkTS的高性能音视频播放器开发</a>
                        <span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/harmonyos-next/1.htm">harmonyos-next</a>
                        <div>HarmonyNext实战:基于ArkTS的高性能音视频播放器开发引言在HarmonyNext生态系统中,音视频播放是一个复杂且具有挑战性的领域。本文将深入探讨如何利用ArkTS语言开发一个高性能的音视频播放器,涵盖从基础播放功能到高级控制与优化的完整流程。我们将通过一个实战案例,详细讲解如何实现一个支持多种格式、流畅播放的音视频播放器,并确保其性能优化。1.环境准备与项目初始化首先,确保你的开发</div>
                    </li>
                    <li><a href="/article/1899368190434144256.htm"
                           title="HarmonyNext实战案例:基于ArkTS的高性能音视频处理应用开发" target="_blank">HarmonyNext实战案例:基于ArkTS的高性能音视频处理应用开发</a>
                        <span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/harmonyos-next/1.htm">harmonyos-next</a>
                        <div>HarmonyNext实战案例:基于ArkTS的高性能音视频处理应用开发引言在HarmonyNext生态系统中,ArkTS作为新一代的编程语言,为开发者提供了强大的工具来构建高性能、跨平台的应用。本文将深入探讨如何使用ArkTS12+语法开发一个高性能的音视频处理应用,涵盖从基础概念到高级技巧的全面讲解。通过本案例,您将学习到如何利用HarmonyNext的特性,结合ArkTS的强大功能,实现复杂</div>
                    </li>
                    <li><a href="/article/1899367781682442240.htm"
                           title="Solana中的程序派生地址(PDAs):是什么,为什么,以及如何?" target="_blank">Solana中的程序派生地址(PDAs):是什么,为什么,以及如何?</a>
                        <span class="text-muted">GTokenTool发币平台</span>
<a class="tag" taget="_blank" href="/search/%E5%8C%BA%E5%9D%97%E9%93%BE/1.htm">区块链</a>
                        <div>程序派生地址(PDA)在Solana中的应用:什么、为什么和如何?在学习Solana时,你会经常听到关于程序派生地址(PDAs)的讨论。它们就像这样——强大、多功能,而且最重要的是,稍微被误解。如果你是一个开发者,试图理解它们,不用担心。我们将在本文中一起揭开PDAs的面纱。在本文中,我将从基础开始解释PDAs,假设你刚刚开始接触Solana。因此,不需要任何先前的知识——让我们开始吧。什么是PD</div>
                    </li>
                    <li><a href="/article/1899367277074116608.htm"
                           title="利用Java爬虫获取衣联网商品详情:实战指南" target="_blank">利用Java爬虫获取衣联网商品详情:实战指南</a>
                        <span class="text-muted">Jason-河山</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E7%88%AC%E8%99%AB/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>在电商领域,获取商品详情是数据分析和市场研究的重要环节。衣联网作为知名的电商平台,提供了丰富的服装商品资源。本文将详细介绍如何利用Java编写爬虫程序,通过商品ID获取衣联网商品详情。一、准备工作(一)环境搭建Java安装:确保已安装Java开发环境,推荐使用JDK11或更高版本。开发工具配置:使用IntelliJIDEA或Eclipse等Java开发工具,创建一个新的Maven项目。依赖库添加:</div>
                    </li>
                    <li><a href="/article/1899367055342235648.htm"
                           title="面试了一个 7 年 Java 程序员,结果真让我哭笑不得。。。" target="_blank">面试了一个 7 年 Java 程序员,结果真让我哭笑不得。。。</a>
                        <span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>大家好,我是R哥。作为一名资深的Java程序员、面试官,同时也做后端面试辅导,面试过许多人,也见过不少神奇的面试经历。但昨晚的一次模拟面试,真的让我哭笑不得。这兄弟来自92名校,毕业7年,干了几个中厂,想冲大厂,目标:40K,于是想模拟面试一下,体验下我们导师的实力。模拟面试之前,说自己八股文准备好了,面试完,竟然连许多常见的八股文都答不上来,而且他还很疑惑地问我:“你们的面试题是哪来的?怎么和我</div>
                    </li>
                    <li><a href="/article/1899366676525281280.htm"
                           title="Yarn:包管理优化与工作空间的最佳实践" target="_blank">Yarn:包管理优化与工作空间的最佳实践</a>
                        <span class="text-muted"></span>

                        <div>在现代前端开发中,包管理工具是不可或缺的工具之一。Yarn作为一个快速、可靠且安全的包管理工具,相对于npm,提供了一些独特的功能和优化,尤其是在工作空间管理和性能优化方面尤为突出。本文将深入探讨Yarn的专业使用,包括其工作空间的强大功能、性能优化技术以及在大型项目中的最佳实践。Yarn简介Yarn是由Facebook开发的一个JavaScript包管理工具,它旨在解决npm的一些关键问题,如安</div>
                    </li>
                    <li><a href="/article/1899366044628217856.htm"
                           title="ELK Stack 安装教程 - 构建日志存储告警系统" target="_blank">ELK Stack 安装教程 - 构建日志存储告警系统</a>
                        <span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a>
                        <div>介绍“ELK”是三个开源项目的首字母缩写,这三个项目分别是:Elasticsearch、Logstash和Kibana。Elasticsearch是一个搜索和分析引擎。Logstash是服务器端数据处理管道,能够同时从多个来源采集数据,转换数据,然后将数据发送到诸如Elasticsearch等“存储库”中。Kibana则可以让用户在Elasticsearch中使用图形和图表对数据进行可视化。目前最</div>
                    </li>
                    <li><a href="/article/1899365764373213184.htm"
                           title="《颠覆认知,我用大模型+Redis实现SQL智能补全,开发效率暴涨500%》" target="_blank">《颠覆认知,我用大模型+Redis实现SQL智能补全,开发效率暴涨500%》</a>
                        <span class="text-muted">煜bart</span>
<a class="tag" taget="_blank" href="/search/mysql/1.htm">mysql</a><a class="tag" taget="_blank" href="/search/AI%E7%BC%96%E7%A8%8B/1.htm">AI编程</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a>
                        <div>一、前言:当SQL补全遇到大模型(插入传统SQL补全工具与ChatGPT对比图)你是否还在为这些场景抓狂?-凌晨3点记不清HiveQL的窗口函数语法-面对新接触的ClickHouse方言不知所措-团队新人总把STR_TO_DATE写成DATE_FORMAT传统IDE的SQL补全就像"人工智障",直到我把大模型装进Redis…##二、效果展示:智能补全的降维打击(GIF动图展示输入SELECT*FR</div>
                    </li>
                    <li><a href="/article/1899365638070136832.htm"
                           title="《 YOLOv5、YOLOv8、YOLO11训练的关键文件:data.yaml文件编写全解》" target="_blank">《 YOLOv5、YOLOv8、YOLO11训练的关键文件:data.yaml文件编写全解》</a>
                        <span class="text-muted">空云风语</span>
<a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/YOLO/1.htm">YOLO</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E8%A7%86%E8%A7%89/1.htm">机器视觉</a><a class="tag" taget="_blank" href="/search/%E7%9B%AE%E6%A0%87%E8%B7%9F%E8%B8%AA/1.htm">目标跟踪</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/1.htm">计算机视觉</a><a class="tag" taget="_blank" href="/search/YOLO/1.htm">YOLO</a>
                        <div>走进YOLOv5、YOLOv8、YOLO11的data.yaml在计算机视觉领域的广袤星空中,目标检测无疑是一颗璀璨的明星,它广泛应用于自动驾驶、智能安防、工业检测、医疗影像分析等众多关键领域,发挥着不可或缺的作用。而YOLO系列算法,更是以其独特的“一次看全(YouOnlyLookOnce)”理念和卓越的性能,在目标检测领域中独树一帜,成为了众多研究者和开发者的首选工具。从最初的YOLOv1横空</div>
                    </li>
                    <li><a href="/article/1899365511448293376.htm"
                           title="【LLM】从零开始实现 LLaMA3" target="_blank">【LLM】从零开始实现 LLaMA3</a>
                        <span class="text-muted">FOUR_A</span>
<a class="tag" taget="_blank" href="/search/LLM/1.htm">LLM</a><a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/1.htm">机器学习</a><a class="tag" taget="_blank" href="/search/%E5%A4%A7%E6%A8%A1%E5%9E%8B/1.htm">大模型</a><a class="tag" taget="_blank" href="/search/llama/1.htm">llama</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a>
                        <div>分词器在这里,我们不会实现一个BPE分词器(但AndrejKarpathy有一个非常简洁的实现)。BPE(BytePairEncoding,字节对编码)是一种数据压缩算法,也被用于自然语言处理中的分词方法。它通过逐步将常见的字符或子词组合成更长的词元(tokens),从而有效地表示文本中的词汇。在自然语言处理中的BPE分词器的工作原理如下:初始化:首先,将所有词汇表中的单词分解为单个字符或符号。例</div>
                    </li>
                    <li><a href="/article/1899365007011934208.htm"
                           title="Zookeeper学习" target="_blank">Zookeeper学习</a>
                        <span class="text-muted">种豆走天下</span>
<a class="tag" taget="_blank" href="/search/zookeeper/1.htm">zookeeper</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%88%86%E5%B8%83%E5%BC%8F/1.htm">分布式</a>
                        <div>Zookeeper是一个开源的分布式协调框架,它主要用于处理分布式系统中的一些常见问题,如同步、配置管理、命名服务和集群管理等。Zookeeper是由Apache提供的,并且广泛应用于各种分布式应用中,特别是在高可用、高可靠性和高性能的系统中。Zookeeper的主要功能分布式协调:Zookeeper提供了协调多个节点(服务器)间行为的机制。例如,分布式锁、选举、配置管理等。命名服务:Zookee</div>
                    </li>
                    <li><a href="/article/1899364628991897600.htm"
                           title="uniApp实战二:仿今日相机水印功能" target="_blank">uniApp实战二:仿今日相机水印功能</a>
                        <span class="text-muted">博主逸尘</span>
<a class="tag" taget="_blank" href="/search/uniApp%E5%AE%9E%E6%88%98/1.htm">uniApp实战</a><a class="tag" taget="_blank" href="/search/uni-app/1.htm">uni-app</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E7%A0%81%E7%9B%B8%E6%9C%BA/1.htm">数码相机</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a>
                        <div>文章目录1.最终效果预览2.页面实现1.最终效果预览2.页面实现页面布局拍照data定义data(){return{snapSrc:"",cvHeight:"",cvWidth:"",tKey:"时间:",addKey:"地点:",};},点击事件及方法handleTakePhotoNew(){this.snapSrc=""uni.chooseImage({count:1,success:(res</div>
                    </li>
                    <li><a href="/article/1899364249357053952.htm"
                           title="Zookeeper实践指南" target="_blank">Zookeeper实践指南</a>
                        <span class="text-muted">Kale又菜又爱玩</span>
<a class="tag" taget="_blank" href="/search/zookeeper/1.htm">zookeeper</a><a class="tag" taget="_blank" href="/search/%E5%88%86%E5%B8%83%E5%BC%8F/1.htm">分布式</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>Zookeeper实践指南1.什么是Zookeeper?Zookeeper是Apache旗下的一个开源分布式协调框架,主要用于解决分布式系统中的一致性问题,提供高效可靠的分布式数据管理能力。1.1Zookeeper的核心特性顺序一致性:客户端的更新请求按顺序执行。原子性:更新操作要么成功要么失败,不存在中间状态。可靠性:一旦数据写入Zookeeper,它就不会丢失,除非主动删除。高可用性:采用主从</div>
                    </li>
                    <li><a href="/article/1899362611724611584.htm"
                           title=".NET 6 WebApi使用JWT" target="_blank">.NET 6 WebApi使用JWT</a>
                        <span class="text-muted">wenqi.xu</span>
<a class="tag" taget="_blank" href="/search/.net/1.htm">.net</a><a class="tag" taget="_blank" href="/search/.netcore/1.htm">.netcore</a>
                        <div>JWT(JsonWebToken)jwt是一种用于身份验证的开放标准,他可以在网络之间传递信息,jwt由三部分组成:头部,载荷,签名。头部包含了令牌的类型和加密算法,载荷包含了用户的信息,签名则是对头部和载荷的加密结果。jwt鉴权验证是指在用户登录成功后,服务器生成一个jwt令牌并返回给客户端,客户端在后续的请求中携带该令牌,服务通过令牌的签名来确定用户的身份和权限。这种方式可以避免在每个请求中都</div>
                    </li>
                    <li><a href="/article/1899362233620688896.htm"
                           title="qt c++线程中的同步和异步" target="_blank">qt c++线程中的同步和异步</a>
                        <span class="text-muted">我要进步!</span>
<a class="tag" taget="_blank" href="/search/qt/1.htm">qt</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a>
                        <div>一、线程同步用于协调多个线程对共享资源的访问,避免竞态条件。常用工具:QMutex(互斥锁)保护临界区,确保一次仅一个线程访问资源。QMutexmutex;intsharedData=0;voidThread::run(){mutex.lock();sharedData++;//安全操作mutex.unlock();}QMutexLocker自动管理锁生命周期:{QMutexLockerlocke</div>
                    </li>
                    <li><a href="/article/1899361855600652288.htm"
                           title="vscode 好用插件" target="_blank">vscode 好用插件</a>
                        <span class="text-muted">yqcoder</span>
<a class="tag" taget="_blank" href="/search/vscode/1.htm">vscode</a><a class="tag" taget="_blank" href="/search/ide/1.htm">ide</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E8%BE%91%E5%99%A8/1.htm">编辑器</a>
                        <div>一、通用效率类1.AutoRenameTag在编写HTML或XML代码时,当你修改一个标签的名称,它会自动同步修改对应的结束标签,节省了手动修改的时间,提高了代码的准确性和编写效率。2.BracketPairColorizer为代码中的括号对(如()、{}、[])添加不同的颜色,让你可以更清晰地分辨嵌套的括号层次,尤其在处理复杂代码时,能有效减少因括号匹配错误导致的问题。3.CodeRunner支</div>
                    </li>
                    <li><a href="/article/1899361476049694720.htm"
                           title="数字IC前端设计究竟怎样?薪资前景如何?" target="_blank">数字IC前端设计究竟怎样?薪资前景如何?</a>
                        <span class="text-muted">IC观察者</span>
<a class="tag" taget="_blank" href="/search/fpga%E5%BC%80%E5%8F%91/1.htm">fpga开发</a><a class="tag" taget="_blank" href="/search/%E9%9B%86%E6%88%90%E7%94%B5%E8%B7%AF/1.htm">集成电路</a><a class="tag" taget="_blank" href="/search/%E6%A8%A1%E6%8B%9FIC/1.htm">模拟IC</a><a class="tag" taget="_blank" href="/search/%E6%A8%A1%E6%8B%9F%E7%89%88%E5%9B%BE/1.htm">模拟版图</a><a class="tag" taget="_blank" href="/search/%E6%A8%A1%E6%8B%9F%E7%89%88%E5%9B%BE%E5%85%A5%E9%97%A8/1.htm">模拟版图入门</a>
                        <div>数字ic前端岗位介绍:数字ic前端设计处于数字IC设计流程的前端,属于数字IC设计类岗位的一种。数字ic前端设计主要分成几种层次的设计:IPlevel,unitlevel,fullchip/SoClevel,gatelevel等。作为数字IC前端工程师,为了让写的RTL代码没有bug,会经常与验证工程师要求debugcase;为了了解芯片整体架构和功能属性,还要与架构工程师打交道;还要与后端工程师</div>
                    </li>
                    <li><a href="/article/1899361383024226304.htm"
                           title="2025精选VS经典:10款项目管理软件横评榜单" target="_blank">2025精选VS经典:10款项目管理软件横评榜单</a>
                        <span class="text-muted"></span>

                        <div>为什么90%的项目经理都在用错工具?深夜11点,项目经理李然盯着甘特图上交错的红色预警线,第6次推翻项目排期——这已是本季度因工具协作不畅导致的第3次交付延期。在敏捷开发成为主流的2025年,“工具选型偏差”正以每年23%的速度吞噬着企业效率(数据来源:PMI2025年行业报告)。作为操盘过金融、IT、制造业等全领域项目的战略官,我发现“工具适配度”比功能强大更重要。本文将用WBS(工作分解结构)</div>
                    </li>
                    <li><a href="/article/1899361381703020544.htm"
                           title="程序员如何用DeepSeek让代码效率翻倍?这份实战手册请收好" target="_blank">程序员如何用DeepSeek让代码效率翻倍?这份实战手册请收好</a>
                        <span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E5%90%8E%E7%AB%AF/1.htm">后端</a>
                        <div>最近公司新来的实习生小张让我眼前一亮,上周他只用三小时就完成了原本需要两天的工作量——优化一个老旧的后端接口。当我翻开他的代码才发现,这个00后小伙子的秘密武器居然是个叫DeepSeek的AI工具。你可能已经注意到,GitHub上越来越多的开源项目开始标注"DeepSeek适配"的字样。这个由中国团队自主研发的大模型,正在悄然改变程序员的工作方式。还记得去年调试分布式系统时的痛苦经历吗?当时我对着</div>
                    </li>
                    <li><a href="/article/1899361127486255104.htm"
                           title="使用Yarn创建Grafana模板的完整指南" target="_blank">使用Yarn创建Grafana模板的完整指南</a>
                        <span class="text-muted"></span>
<a class="tag" taget="_blank" href="/search/%E4%BA%91%E6%9C%8D%E5%8A%A1%E5%99%A8linux%E8%BF%90%E7%BB%B4yarn/1.htm">云服务器linux运维yarn</a>
                        <div>在本篇文章中,我将带你逐步完成如何使用Yarn生成Grafana模板的过程。Grafana是一款开源的数据可视化工具,我们可以使用它来创建各种仪表板,以便更好地监控和展示数据。请跟随我一起来完成这一过程。整体流程概览在开始之前,我们先来看看整个操作的流程。以下是步骤的概述,以表格形式展示:步骤描述1安装Node.js和Yarn2创建新的Yarn项目3安装Grafana的API客户端库4编写Graf</div>
                    </li>
                    <li><a href="/article/1899361098004492288.htm"
                           title="ES6 Class 转 ES5 实现" target="_blank">ES6 Class 转 ES5 实现</a>
                        <span class="text-muted">Tiffany_Ho</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/es6/1.htm">es6</a>
                        <div>ES6示例代码classAnimal{constructor(name){this.name=name;}eat(){console.log(`${this.name}iseating`);}staticsleep(){console.log("Sleeping");}}classDogextendsAnimal{//原型链继承+静态方法继承constructor(name,breed){supe</div>
                    </li>
                    <li><a href="/article/1899360845343813632.htm"
                           title="ES6 解构赋值详解" target="_blank">ES6 解构赋值详解</a>
                        <span class="text-muted">修己xj</span>
<a class="tag" taget="_blank" href="/search/web/1.htm">web</a><a class="tag" taget="_blank" href="/search/es6/1.htm">es6</a><a class="tag" taget="_blank" href="/search/javascript/1.htm">javascript</a><a class="tag" taget="_blank" href="/search/es6/1.htm">es6</a>
                        <div>ES6是JavaScript语言的一次重大更新,引入了许多新特性和语法改进,其中解构赋值是一个非常实用和灵活的语法特性。它可以让我们从数组或对象中提取值,并赋给对应的变量,让代码变得更加简洁和易读。本文将深入探讨ES6解构赋值的语法、用法及其在实际开发中的应用。数组解构赋值数组解构赋值允许我们通过类似模式匹配的方式,从数组中提取值并赋给变量,即只要等会两边的变量模式相同,左边的变量就会被赋予对应的</div>
                    </li>
                    <li><a href="/article/1899360746203049984.htm"
                           title="设计一个优秀 API 的秘诀" target="_blank">设计一个优秀 API 的秘诀</a>
                        <span class="text-muted"></span>

                        <div>设计一个优秀API的秘诀本指南深入探讨了顶级API设计,强调了它不仅仅是代码的集合。一个设计良好的API就像五星级礼宾服务,能够顺畅地引导用户达到他们的预期目标。拿起一杯咖啡,让我们一起探索创建一个功能强大、用户友好的API的秘诀吧!理解API基础第一步:拥抱REST-API设计的基础优秀API构建的基石是REST。RESTfulAPI使用HTTP方法(GET、POST、PUT、DELETE),与</div>
                    </li>
                    <li><a href="/article/1899360719070097408.htm"
                           title="Qt程序闪退如何查原因呢,闪退点不是自己应用代码" target="_blank">Qt程序闪退如何查原因呢,闪退点不是自己应用代码</a>
                        <span class="text-muted">bug菌¹</span>
<a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/CSDN%E9%97%AE%E7%AD%94%E8%A7%A3%E6%83%91%28%E5%85%A8%E6%A0%88%E7%89%88%29/1.htm">CSDN问答解惑(全栈版)</a><a class="tag" taget="_blank" href="/search/%E5%85%A8%E6%A0%88Bug%E8%B0%83%E4%BC%98%28%E5%AE%9E%E6%88%98%E7%89%88%29/1.htm">全栈Bug调优(实战版)</a><a class="tag" taget="_blank" href="/search/qt/1.htm">qt</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/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a>
                        <div>本文收录于《CSDN问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!问题描述  Qt程序闪退如何查原因呢,闪退点不是自己应用代码如图,因为是qt底层,这种闪退该怎么查原因和避免呢,现在遇到很多这种底层报错又没办法查代码如上问题有来自我自身项目</div>
                    </li>
                    <li><a href="/article/1899360717161689088.htm"
                           title="tauri + vue3 如何实现在一个页面上局部加载外部网页?" target="_blank">tauri + vue3 如何实现在一个页面上局部加载外部网页?</a>
                        <span class="text-muted">bug菌¹</span>
<a class="tag" taget="_blank" href="/search/%E5%85%A8%E6%A0%88Bug%E8%B0%83%E4%BC%98%28%E5%AE%9E%E6%88%98%E7%89%88%29/1.htm">全栈Bug调优(实战版)</a><a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/CSDN%E9%97%AE%E7%AD%94%E8%A7%A3%E6%83%91%28%E5%85%A8%E6%A0%88%E7%89%88%29/1.htm">CSDN问答解惑(全栈版)</a><a class="tag" taget="_blank" href="/search/tauri/1.htm">tauri</a><a class="tag" taget="_blank" href="/search/vue3/1.htm">vue3</a>
                        <div>本文收录于「Bug调优」专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!问题描述  tauriv1(1.6左右)+vue3我想在vue3前端页面上在一个页面而不是window.open打开一个新的窗口去加载外部网页我想在一个页面中局部中间加载一个外部网页(试过</div>
                    </li>
                    <li><a href="/article/1899360212922462208.htm"
                           title="职场中的压力管理有哪些有效方法?" target="_blank">职场中的压力管理有哪些有效方法?</a>
                        <span class="text-muted">weixin_42220130</span>
<a class="tag" taget="_blank" href="/search/%E7%94%9F%E6%B4%BB/1.htm">生活</a><a class="tag" taget="_blank" href="/search/%E8%81%8C%E5%9C%BA/1.htm">职场</a><a class="tag" taget="_blank" href="/search/%E5%8E%8B%E5%8A%9B/1.htm">压力</a><a class="tag" taget="_blank" href="/search/%E7%AE%A1%E7%90%86/1.htm">管理</a><a class="tag" taget="_blank" href="/search/%E6%96%B9%E6%B3%95/1.htm">方法</a><a class="tag" taget="_blank" href="/search/%E6%9C%89%E6%95%88/1.htm">有效</a>
                        <div>有制订计划表、坚决不做全优生、不可或缺的一日三餐、放弃无意义的固执、勇于做个挑战者、通过沟通释放压力、尝试打破现状、劳逸结合的生活状态等方法。制订计划表[2]当个人有一个完美的计划表,并且正在逐步实施时,就不会产生无谓的压力。因为,一切尽在掌握之中。计划表是一个很好的监督者,叮嘱你每一个目标的实现,又是一个软性的压力,只有跳起来才能够得着。当你心里有底时,也就没有了压力。坚决不做全优生[2]许多白</div>
                    </li>
                    <li><a href="/article/1899359709018779648.htm"
                           title="有趣的学习Python-第十篇:Python的“魔法宝库”:标准库之旅" target="_blank">有趣的学习Python-第十篇:Python的“魔法宝库”:标准库之旅</a>
                        <span class="text-muted">王盼达</span>
<a class="tag" taget="_blank" href="/search/%E6%9C%89%E8%B6%A3%E7%9A%84%E5%AD%A6%E4%B9%A0Python/1.htm">有趣的学习Python</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</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%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>Python不仅是一门强大的编程语言,更像是一座充满宝藏的“魔法宝库”,里面装满了各种各样的“魔法工具”(标准库)。这些“魔法工具”可以帮助你轻松地完成各种任务,从文件操作到网络编程,从数据处理到性能优化。接下来,让我们一起探索Python的“魔法宝库”,看看这些“魔法工具”到底有多神奇!10.1操作系统接口:与“魔法世界”互动os模块就像是一个“魔法接口”,可以帮助你与操作系统进行互动。你可以用</div>
                    </li>
                                <li><a href="/article/13.htm"
                                       title="github中多个平台共存" target="_blank">github中多个平台共存</a>
                                    <span class="text-muted">jackyrong</span>
<a class="tag" taget="_blank" href="/search/github/1.htm">github</a>
                                    <div>在个人电脑上,如何分别链接比如oschina,github等库呢,一般教程之列的,默认 
ssh链接一个托管的而已,下面讲解如何放两个文件 
 
 
1) 设置用户名和邮件地址 
 
$ git config --global user.name "xx" 
 
$ git config --global user.email "test@gmail.com"</div>
                                </li>
                                <li><a href="/article/140.htm"
                                       title="ip地址与整数的相互转换(javascript)" target="_blank">ip地址与整数的相互转换(javascript)</a>
                                    <span class="text-muted">alxw4616</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a>
                                    <div>//IP转成整型
function ip2int(ip){
    var num = 0;
    ip = ip.split(".");
    num = Number(ip[0]) * 256 * 256 * 256 + Number(ip[1]) * 256 * 256 + Number(ip[2]) * 256 + Number(ip[3]);
    n</div>
                                </li>
                                <li><a href="/article/267.htm"
                                       title="读书笔记-jquey+数据库+css" target="_blank">读书笔记-jquey+数据库+css</a>
                                    <span class="text-muted">chengxuyuancsdn</span>
<a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/jquery/1.htm">jquery</a><a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a>
                                    <div>1、grouping ,group by rollup, GROUP BY GROUPING SETS区别 
2、$("#totalTable tbody>tr td:nth-child(" + i + ")").css({"width":tdWidth, "margin":"0px", &q</div>
                                </li>
                                <li><a href="/article/394.htm"
                                       title="javaSE javaEE javaME == API下载" target="_blank">javaSE javaEE javaME == API下载</a>
                                    <span class="text-muted">Array_06</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                                    <div>oracle下载各种API文档: 
http://www.oracle.com/technetwork/java/embedded/javame/embed-me/documentation/javame-embedded-apis-2181154.html 
 
JavaSE文档: 
http://docs.oracle.com/javase/8/docs/api/ 
JavaEE文档: 
ht</div>
                                </li>
                                <li><a href="/article/521.htm"
                                       title="shiro入门学习" target="_blank">shiro入门学习</a>
                                    <span class="text-muted">cugfy</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/Web/1.htm">Web</a><a class="tag" taget="_blank" href="/search/%E6%A1%86%E6%9E%B6/1.htm">框架</a>
                                    <div>声明本文只适合初学者,本人也是刚接触而已,经过一段时间的研究小有收获,特来分享下希望和大家互相交流学习。 
首先配置我们的web.xml代码如下,固定格式,记死就成 
 <filter> 
        <filter-name>shiroFilter</filter-name> 
&nbs</div>
                                </li>
                                <li><a href="/article/648.htm"
                                       title="Array添加删除方法" target="_blank">Array添加删除方法</a>
                                    <span class="text-muted">357029540</span>
<a class="tag" taget="_blank" href="/search/js/1.htm">js</a>
                                    <div>     刚才做项目前台删除数组的固定下标值时,删除得不是很完整,所以在网上查了下,发现一个不错的方法,也提供给需要的同学。 
 
//给数组添加删除 
            Array.prototype.del = function(n){ 
</div>
                                </li>
                                <li><a href="/article/775.htm"
                                       title="navigation bar 更改颜色" target="_blank">navigation bar 更改颜色</a>
                                    <span class="text-muted">张亚雄</span>
<a class="tag" taget="_blank" href="/search/IO/1.htm">IO</a>
                                    <div>今天郁闷了一下午,就因为objective-c默认语言是英文,我写的中文全是一些乱七八糟的样子,到不是乱码,但是,前两个自字是粗体,后两个字正常体,这可郁闷死我了,问了问大牛,人家告诉我说更改一下字体就好啦,比如改成黑体,哇塞,茅塞顿开。 
      翻书看,发现,书上有介绍怎么更改表格中文字字体的,代码如下 
   </div>
                                </li>
                                <li><a href="/article/902.htm"
                                       title="unicode转换成中文" target="_blank">unicode转换成中文</a>
                                    <span class="text-muted">adminjun</span>
<a class="tag" taget="_blank" href="/search/unicode/1.htm">unicode</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A0%81%E8%BD%AC%E6%8D%A2/1.htm">编码转换</a>
                                    <div>  
在Java程序中总会出现\u6b22\u8fce\u63d0\u4ea4\u5fae\u535a\u641c\u7d22\u4f7f\u7528\u53cd\u9988\uff0c\u8bf7\u76f4\u63a5这个的字符,这是unicode编码,使用时有时候不会自动转换成中文就需要自己转换了使用下面的方法转换一下即可。 
	/**
	 * unicode 转换成 中文
	</div>
                                </li>
                                <li><a href="/article/1029.htm"
                                       title="一站式 Java Web 框架 firefly" target="_blank">一站式 Java Web 框架 firefly</a>
                                    <span class="text-muted">aijuans</span>
<a class="tag" taget="_blank" href="/search/Java+Web/1.htm">Java Web</a>
                                    <div>Firefly是一个高性能一站式Web框架。 涵盖了web开发的主要技术栈。 包含Template engine、IOC、MVC framework、HTTP Server、Common tools、Log、Json parser等模块。 
firefly-2.0_07修复了模版压缩对javascript单行注释的影响,并新增了自定义错误页面功能。 
更新日志: 
 
 增加自定义系统错误页面功能</div>
                                </li>
                                <li><a href="/article/1156.htm"
                                       title="设计模式——单例模式" target="_blank">设计模式——单例模式</a>
                                    <span class="text-muted">ayaoxinchao</span>
<a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a>
                                    <div>定义 
  
       Java中单例模式定义:“一个类有且仅有一个实例,并且自行实例化向整个系统提供。” 
  
分析 
  
       从定义中可以看出单例的要点有三个:一是某个类只能有一个实例;二是必须自行创建这个实例;三是必须自行向系统提供这个实例。 
  &nb</div>
                                </li>
                                <li><a href="/article/1283.htm"
                                       title="Javascript 多浏览器兼容性问题及解决方案" target="_blank">Javascript 多浏览器兼容性问题及解决方案</a>
                                    <span class="text-muted">BigBird2012</span>
<a class="tag" taget="_blank" href="/search/JavaScript/1.htm">JavaScript</a>
                                    <div>不论是网站应用还是学习js,大家很注重ie与firefox等浏览器的兼容性问题,毕竟这两中浏览器是占了绝大多数。 
一、document.formName.item(”itemName”) 问题  
问题说明:IE下,可以使用 document.formName.item(”itemName”) 或 document.formName.elements ["elementName&quo</div>
                                </li>
                                <li><a href="/article/1410.htm"
                                       title="JUnit-4.11使用报java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing错误" target="_blank">JUnit-4.11使用报java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing错误</a>
                                    <span class="text-muted">bijian1013</span>
<a class="tag" taget="_blank" href="/search/junit4.11/1.htm">junit4.11</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/1.htm">单元测试</a>
                                    <div>        下载了最新的JUnit版本,是4.11,结果尝试使用发现总是报java.lang.NoClassDefFoundError: org/hamcrest/SelfDescribing这样的错误,上网查了一下,一般的解决方案是,换一个低一点的版本就好了。还有人说,是缺少hamcrest的包。去官网看了一下,如下发现: 
   </div>
                                </li>
                                <li><a href="/article/1537.htm"
                                       title="[Zookeeper学习笔记之二]Zookeeper部署脚本" target="_blank">[Zookeeper学习笔记之二]Zookeeper部署脚本</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/zookeeper/1.htm">zookeeper</a>
                                    <div>Zookeeper伪分布式安装脚本(此脚本在一台机器上创建Zookeeper三个进程,即创建具有三个节点的Zookeeper集群。这个脚本和zookeeper的tar包放在同一个目录下,脚本中指定的名字是zookeeper的3.4.6版本,需要根据实际情况修改): 
  
#!/bin/bash  
  
#!!!Change the name!!!  
#The zookeepe</div>
                                </li>
                                <li><a href="/article/1664.htm"
                                       title="【Spark八十】Spark RDD API二" target="_blank">【Spark八十】Spark RDD API二</a>
                                    <span class="text-muted">bit1129</span>
<a class="tag" taget="_blank" href="/search/spark/1.htm">spark</a>
                                    <div>coGroup 
package spark.examples.rddapi

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.SparkContext._

object CoGroupTest_05 {
  def main(args: Array[String]) {
    v</div>
                                </li>
                                <li><a href="/article/1791.htm"
                                       title="Linux中编译apache服务器modules文件夹缺少模块(.so)的问题" target="_blank">Linux中编译apache服务器modules文件夹缺少模块(.so)的问题</a>
                                    <span class="text-muted">ronin47</span>
<a class="tag" taget="_blank" href="/search/modules/1.htm">modules</a>
                                    <div>在modules目录中只有httpd.exp,那些so文件呢? 
 
我尝试在fedora core 3中安装apache 2. 当我解压了apache 2.0.54后使用configure工具并且加入了 --enable-so 或者 --enable-modules=so (两个我都试过了) 
去make并且make install了。我希望在/apache2/modules/目录里有各种模块,</div>
                                </li>
                                <li><a href="/article/1918.htm"
                                       title="Java基础-克隆" target="_blank">Java基础-克隆</a>
                                    <span class="text-muted">BrokenDreams</span>
<a class="tag" taget="_blank" href="/search/java%E5%9F%BA%E7%A1%80/1.htm">java基础</a>
                                    <div>        Java中怎么拷贝一个对象呢?可以通过调用这个对象类型的构造器构造一个新对象,然后将要拷贝对象的属性设置到新对象里面。Java中也有另一种不通过构造器来拷贝对象的方式,这种方式称为 
克隆。 
        Java提供了java.lang.</div>
                                </li>
                                <li><a href="/article/2045.htm"
                                       title="读《研磨设计模式》-代码笔记-适配器模式-Adapter" target="_blank">读《研磨设计模式》-代码笔记-适配器模式-Adapter</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/ 
 
 
package design.pattern;

/*
 * 适配器模式解决的主要问题是,现有的方法接口与客户要求的方法接口不一致
 * 可以这样想,我们要写这样一个类(Adapter):
 * 1.这个类要符合客户的要求 ---> 那显然要</div>
                                </li>
                                <li><a href="/article/2172.htm"
                                       title="HDR图像PS教程集锦&心得" target="_blank">HDR图像PS教程集锦&心得</a>
                                    <span class="text-muted">cherishLC</span>
<a class="tag" taget="_blank" href="/search/PS/1.htm">PS</a>
                                    <div>HDR是指高动态范围的图像,主要原理为提高图像的局部对比度。 
软件有photomatix和nik hdr efex。 
 一、教程 
叶明在知乎上的回答: 
http://www.zhihu.com/question/27418267/answer/37317792 
大意是修完后直方图最好是等值直方图,方法是HDR软件调一遍,再结合不透明度和蒙版细调。 
 
 二、心得 
 1、去除阴影部分的</div>
                                </li>
                                <li><a href="/article/2299.htm"
                                       title="maven-3.3.3 mvn archetype 列表" target="_blank">maven-3.3.3 mvn archetype 列表</a>
                                    <span class="text-muted">crabdave</span>
<a class="tag" taget="_blank" href="/search/ArcheType/1.htm">ArcheType</a>
                                    <div>maven-3.3.3 mvn archetype 列表 
可以参考最新的:http://repo1.maven.org/maven2/archetype-catalog.xml 
  
[INFO] Scanning for projects... 
[INFO]                 </div>
                                </li>
                                <li><a href="/article/2426.htm"
                                       title="linux shell 中文件编码查看及转换方法" target="_blank">linux shell 中文件编码查看及转换方法</a>
                                    <span class="text-muted">daizj</span>
<a class="tag" taget="_blank" href="/search/shell/1.htm">shell</a><a class="tag" taget="_blank" href="/search/%E4%B8%AD%E6%96%87%E4%B9%B1%E7%A0%81/1.htm">中文乱码</a><a class="tag" taget="_blank" href="/search/vim/1.htm">vim</a><a class="tag" taget="_blank" href="/search/%E6%96%87%E4%BB%B6%E7%BC%96%E7%A0%81/1.htm">文件编码</a>
                                    <div>一、查看文件编码。 
    在打开文件的时候输入:set fileencoding 
    即可显示文件编码格式。 
 
 
 
二、文件编码转换 
    1、在Vim中直接进行转换文件编码,比如将一个文件转换成utf-8格式 
      &</div>
                                </li>
                                <li><a href="/article/2553.htm"
                                       title="MySQL--binlog日志恢复数据" target="_blank">MySQL--binlog日志恢复数据</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/binlog/1.htm">binlog</a>
                                    <div>   恢复数据的重要命令如下   mysql> flush logs;  默认的日志是mysql-bin.000001,现在刷新了重新开启一个就多了一个mysql-bin.000002                    </div>
                                </li>
                                <li><a href="/article/2680.htm"
                                       title="数据库中数据表数据迁移方法" target="_blank">数据库中数据表数据迁移方法</a>
                                    <span class="text-muted">dcj3sjt126com</span>
<a class="tag" taget="_blank" href="/search/sql/1.htm">sql</a>
                                    <div>刚开始想想好像挺麻烦的,后来找到一种方法了,就SQL中的 INSERT 语句,不过内容是现从另外的表中查出来的,其实就是 MySQL中INSERT INTO SELECT的使用 
  
下面看看如何使用 
  
语法:MySQL中INSERT INTO SELECT的使用 
1. 语法介绍 
      有三张表a、b、c,现在需要从表b</div>
                                </li>
                                <li><a href="/article/2807.htm"
                                       title="Java反转字符串" target="_blank">Java反转字符串</a>
                                    <span class="text-muted">dyy_gusi</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%8F%8D%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2/1.htm">反转字符串</a>
                                    <div>       前几天看见一篇文章,说使用Java能用几种方式反转一个字符串。首先要明白什么叫反转字符串,就是将一个字符串到过来啦,比如"倒过来念的是小狗"反转过来就是”狗小是的念来过倒“。接下来就把自己能想到的所有方式记录下来了。 
1、第一个念头就是直接使用String类的反转方法,对不起,这样是不行的,因为Stri</div>
                                </li>
                                <li><a href="/article/2934.htm"
                                       title="UI设计中我们为什么需要设计动效" target="_blank">UI设计中我们为什么需要设计动效</a>
                                    <span class="text-muted">gcq511120594</span>
<a class="tag" taget="_blank" href="/search/UI/1.htm">UI</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a>
                                    <div>随着国际大品牌苹果和谷歌的引领,最近越来越多的国内公司开始关注动效设计了,越来越多的团队已经意识到动效在产品用户体验中的重要性了,更多的UI设计师们也开始投身动效设计领域。 
 
但是说到底,我们到底为什么需要动效设计?或者说我们到底需要什么样的动效?做动效设计也有段时间了,于是尝试用一些案例,从产品本身出发来说说我所思考的动效设计。 
 
一、加强体验舒适度 
 
嗯,就是让用户更加爽更加爽的用</div>
                                </li>
                                <li><a href="/article/3061.htm"
                                       title="JBOSS服务部署端口冲突问题" target="_blank">JBOSS服务部署端口冲突问题</a>
                                    <span class="text-muted">HogwartsRow</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%BA%94%E7%94%A8%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">应用服务器</a><a class="tag" taget="_blank" href="/search/jboss/1.htm">jboss</a><a class="tag" taget="_blank" href="/search/server/1.htm">server</a><a class="tag" taget="_blank" href="/search/EJB3/1.htm">EJB3</a>
                                    <div>服务端口冲突问题的解决方法,一般修改如下三个文件中的部分端口就可以了。 
  
1、jboss5/server/default/conf/bindingservice.beans/META-INF/bindings-jboss-beans.xml 
  
2、./server/default/deploy/jbossweb.sar/server.xml 
  
3、.</div>
                                </li>
                                <li><a href="/article/3188.htm"
                                       title="第三章 Redis/SSDB+Twemproxy安装与使用" target="_blank">第三章 Redis/SSDB+Twemproxy安装与使用</a>
                                    <span class="text-muted">jinnianshilongnian</span>
<a class="tag" taget="_blank" href="/search/ssdb/1.htm">ssdb</a><a class="tag" taget="_blank" href="/search/reids/1.htm">reids</a><a class="tag" taget="_blank" href="/search/twemproxy/1.htm">twemproxy</a>
                                    <div>目前对于互联网公司不使用Redis的很少,Redis不仅仅可以作为key-value缓存,而且提供了丰富的数据结果如set、list、map等,可以实现很多复杂的功能;但是Redis本身主要用作内存缓存,不适合做持久化存储,因此目前有如SSDB、ARDB等,还有如京东的JIMDB,它们都支持Redis协议,可以支持Redis客户端直接访问;而这些持久化存储大多数使用了如LevelDB、RocksD</div>
                                </li>
                                <li><a href="/article/3315.htm"
                                       title="ZooKeeper原理及使用 " target="_blank">ZooKeeper原理及使用 </a>
                                    <span class="text-muted">liyonghui160com</span>

                                    <div>  
  
       ZooKeeper是Hadoop Ecosystem中非常重要的组件,它的主要功能是为分布式系统提供一致性协调(Coordination)服务,与之对应的Google的类似服务叫Chubby。今天这篇文章分为三个部分来介绍ZooKeeper,第一部分介绍ZooKeeper的基本原理,第二部分介绍ZooKeeper</div>
                                </li>
                                <li><a href="/article/3442.htm"
                                       title="程序员解决问题的60个策略" target="_blank">程序员解决问题的60个策略</a>
                                    <span class="text-muted">pda158</span>
<a class="tag" taget="_blank" href="/search/%E6%A1%86%E6%9E%B6/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/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/1.htm">单元测试</a>
                                    <div>根本的指导方针 
1. 首先写代码的时候最好不要有缺陷。最好的修复方法就是让 bug 胎死腹中。 
 
  良好的单元测试  
  强制数据库约束  
  使用输入验证框架  
  避免未实现的“else”条件  
  在应用到主程序之前知道如何在孤立的情况下使用  
 
  
日志 
2. print 语句。往往额外输出个一两行将有助于隔离问题。 
3. 切换至详细的日志记录。详细的日</div>
                                </li>
                                <li><a href="/article/3569.htm"
                                       title="Create the Google Play Account" target="_blank">Create the Google Play Account</a>
                                    <span class="text-muted">sillycat</span>
<a class="tag" taget="_blank" href="/search/Google/1.htm">Google</a>
                                    <div>Create the Google Play Account 
 
Having a Google account, pay 25$, then you get your google developer account. 
 
References: 
http://developer.android.com/distribute/googleplay/start.html 
https://p</div>
                                </li>
                                <li><a href="/article/3696.htm"
                                       title="JSP三大指令" target="_blank">JSP三大指令</a>
                                    <span class="text-muted">vikingwei</span>
<a class="tag" taget="_blank" href="/search/jsp/1.htm">jsp</a>
                                    <div>JSP三大指令 
  一个jsp页面中,可以有0~N个指令的定义! 
1. page --> 最复杂:<%@page language="java" info="xxx"...%> 
  * pageEncoding和contentType: 
    > pageEncoding:它</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>