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

简介

大家好,我是一个闲着没事热衷于重复造轮子的不知名前端,今天给大家带来的是一个代码在线编辑预览工具的实现介绍,目前这类工具使用很广泛,常见于各种文档网站及代码分享场景,相关工具也比较多,如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/1950233451282100224.htm"
                           title="python 读excel每行替换_Python脚本操作Excel实现批量替换功能" target="_blank">python 读excel每行替换_Python脚本操作Excel实现批量替换功能</a>
                        <span class="text-muted">weixin_39646695</span>
<a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E8%AF%BBexcel%E6%AF%8F%E8%A1%8C%E6%9B%BF%E6%8D%A2/1.htm">读excel每行替换</a>
                        <div>Python脚本操作Excel实现批量替换功能大家好,给大家分享下如何使用Python脚本操作Excel实现批量替换。使用的工具Openpyxl,一个处理excel的python库,处理excel,其实针对的就是WorkBook,Sheet,Cell这三个最根本的元素~明确需求原始excel如下我们的目标是把下面excel工作表的sheet1表页A列的内容“替换我吧”批量替换为B列的“我用来替换的</div>
                    </li>
                    <li><a href="/article/1950233199242178560.htm"
                           title="x86-64汇编语言训练程序与实战" target="_blank">x86-64汇编语言训练程序与实战</a>
                        <span class="text-muted">十除以十等于一</span>

                        <div>本文还有配套的精品资源,点击获取简介:汇编语言是一种低级语言,与机器代码紧密相关,特别适用于编写系统级代码及性能要求高的应用。nasm编译器是针对x86和x86-64架构的汇编语言编译器,支持多种语法风格和指令集。项目Euler提供数学和计算机科学问题,鼓励编程技巧应用,前100个问题的答案可共享。x86-64架构扩展了寄存器数量并引入新指令,提升了数据处理效率。学习汇编语言能够深入理解计算机底层</div>
                    </li>
                    <li><a href="/article/1950233167633903616.htm"
                           title="男士护肤品哪个牌子好?十大男士护肤品排行榜" target="_blank">男士护肤品哪个牌子好?十大男士护肤品排行榜</a>
                        <span class="text-muted">高省APP珊珊</span>

                        <div>很多男生意识到护肤的必要性,开始着手护肤,但不知道该选哪个男士护肤品品牌使用好。目前市面上很多男士护肤品品牌,可谓琳琅满目,让人眼花缭乱。男士挑选护肤品时,根据自己皮肤需求去正规渠道挑选合适的知名护肤品比较放心靠谱。高省APP,是2021年推出的平台,0投资,0风险、高省APP佣金更高,模式更好,终端用户不流失。【高省】是一个自用省钱佣金高,分享推广赚钱多的平台,百度有几百万篇报道,也期待你的加入</div>
                    </li>
                    <li><a href="/article/1950232190038110208.htm"
                           title="day15|前端框架学习和算法" target="_blank">day15|前端框架学习和算法</a>
                        <span class="text-muted">universe_01</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E7%AC%94%E8%AE%B0/1.htm">笔记</a>
                        <div>T22括号生成先把所有情况都画出来,然后(在满足什么情况下)把不符合条件的删除。T78子集要画树状图,把思路清晰。可以用暴力法、回溯法和DFS做这个题DFS深度搜索:每个边都走完,再回溯应用:二叉树搜索,图搜索回溯算法=DFS+剪枝T200岛屿数量(非常经典BFS宽度把树状转化成队列形式,lambda匿名函数“一次性的小函数,没有名字”setup语法糖:让代码更简洁好写的语法ref创建:基本类型的</div>
                    </li>
                    <li><a href="/article/1950231381485350912.htm"
                           title="《极简思维》第三部分" target="_blank">《极简思维》第三部分</a>
                        <span class="text-muted">小洋苏兮</span>

                        <div>整理你的人际关系如何改善人际关系?摘录:因为人际关系问题是人们生活中不快乐的主要原因。感想:感觉这个说的挺对,之前我总是埋头学习,不管舍友不管自己的合作伙伴的一些事情,但实际上,这学期关注了之后好多了摘录:“亲密关系与社交会让你健康而快乐。这是基础。太过于关注成就或不太关心人际关系的人都不怎么快乐。基本上来说,人类就是建立在人脉关系上的。”感想:但是如果有时想的太多就不太好,要以一个开放的心态跟别</div>
                    </li>
                    <li><a href="/article/1950231308781285376.htm"
                           title="力扣热题100-------54. 螺旋矩阵" target="_blank">力扣热题100-------54. 螺旋矩阵</a>
                        <span class="text-muted">海航Java之路</span>
<a class="tag" taget="_blank" href="/search/%E5%8A%9B%E6%89%A3/1.htm">力扣</a><a class="tag" taget="_blank" href="/search/leetcode/1.htm">leetcode</a><a class="tag" taget="_blank" href="/search/%E7%9F%A9%E9%98%B5/1.htm">矩阵</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>给你一个m行n列的矩阵matrix,请按照顺时针螺旋顺序,返回矩阵中的所有元素。示例1:输入:matrix=[[1,2,3],[4,5,6],[7,8,9]]输出:[1,2,3,6,9,8,7,4,5]示例2:输入:matrix=[[1,2,3,4],[5,6,7,8],[9,10,11,12]]输出:[1,2,3,4,8,12,11,10,9,5,6,7]提示:m==matrix.lengthn</div>
                    </li>
                    <li><a href="/article/1950231254427299840.htm"
                           title="我不懂什么是爱,但我给你全部我拥有的" target="_blank">我不懂什么是爱,但我给你全部我拥有的</a>
                        <span class="text-muted">香尧</span>

                        <div>因为怕黑,所以愿意陪伴在夜中行走的人,给他一点点的安全感。因为渴望温柔与爱,所以愿意为别的孩子付出爱与温柔。因为曾遭受侮辱和伤害,所以不以同样的方式施于其他人。如果你向别人出之以利刃,对方还了你爱与包容,真的不要感激他,真的不要赞美他。每一个被人伤害过的人心里都留下了一颗仇恨的种子,他也会想要有一天以眼还眼,以牙还牙。但他未让那颗种子生根发芽,他用一把心剑又一次刺向他自己,用他血荐仇恨,开出一朵温</div>
                    </li>
                    <li><a href="/article/1950230804957294592.htm"
                           title="SpringMVC执行流程(原理),通俗易懂" target="_blank">SpringMVC执行流程(原理),通俗易懂</a>
                        <span class="text-muted">国服冰</span>
<a class="tag" taget="_blank" href="/search/SpringMVC/1.htm">SpringMVC</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/mvc/1.htm">mvc</a>
                        <div>SpringMVC执行流程(原理),通俗易懂一、图解SpringMVC流程二、进一步理解Springmvc的执行流程1、导入依赖2、建立展示的视图3、web.xml4、spring配置文件springmvc-servlet5、Controller6、tomcat配置7、访问的url8、视图页面一、图解SpringMVC流程图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提</div>
                    </li>
                    <li><a href="/article/1950230678696161280.htm"
                           title="C++ 计数排序、归并排序、快速排序" target="_blank">C++ 计数排序、归并排序、快速排序</a>
                        <span class="text-muted">每天搬一点点砖</span>
<a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a>
                        <div>计数排序:是一种基于哈希的排序算法。他的基本思想是通过统计每个元素的出现次数,然后根据统计结果将元素依次放入排序后的序列中。这种排序算法适用于范围较小的情况,例如整数范围在0到k之间计数排序步骤:1初始化一个长度为最大元素值加1的计数数组,所有元素初始化为02遍历原始数组,将每个元素值作为索引,在计数数组中对应位置加13将数组清空4遍历计数器数组,按照数组中的元素个数放回到元数组中计数排序的优点和</div>
                    </li>
                    <li><a href="/article/1950229985633562624.htm"
                           title="2023-11-02" target="_blank">2023-11-02</a>
                        <span class="text-muted">一帆f</span>

                        <div>发现浸润心田的感觉:今天一个机缘之下突然想分享我的婆媳关系,我一边分享一边回忆我之前和儿媳妇关系的微妙变化,特别是分享到我能感受到儿媳妇的各种美好,现在也能心平气和的和老公平等对话,看到自己看到老公,以己推人以人推己自然而然的换位思考,心中有一种美好的能量在涌动,一种浸润心田的感觉从心胸向全身扩散,美好极了……我很想记住这种感觉,赶紧把它写下来以留纪念,也就是当我看见他人的美好,美好的美妙的浸润心</div>
                    </li>
                    <li><a href="/article/1950229986984128512.htm"
                           title="贫穷家庭的孩子考上985以后会怎样?" target="_blank">贫穷家庭的孩子考上985以后会怎样?</a>
                        <span class="text-muted">Mellisa蜜思言</span>

                        <div>我出生在一个贫穷的农村家庭,据我妈说,我出生的时候才4斤多,而她生完我以后月子里就瘦到70斤。家里一直很穷,父母都是在菜市场卖菜的,家里还有几亩地种庄稼的。我很小开始就要去帮忙,暑假的生活就是帮忙去卖菜和割稻谷,那时候自己对于割稻谷这种事情有着莫名的恐惧,生怕自己长大以后还是每年都要过着割稻谷这种日子。父母因为忙于生计无暇顾及我的学习,幸好我因为看到他们这样子的生活,内心里有深深的恐惧感,驱使着我</div>
                    </li>
                    <li><a href="/article/1950229040682037248.htm"
                           title="48. 旋转图像 - 力扣(LeetCode)" target="_blank">48. 旋转图像 - 力扣(LeetCode)</a>
                        <span class="text-muted">Fiee-77</span>
<a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E7%BB%84/1.htm">数组</a><a class="tag" taget="_blank" href="/search/leetcode/1.htm">leetcode</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/python/1.htm">python</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/1.htm">数据结构</a><a class="tag" taget="_blank" href="/search/%E6%95%B0%E7%BB%84/1.htm">数组</a>
                        <div>题目:给定一个n×n的二维矩阵matrix表示一个图像。请你将图像顺时针旋转90度。你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。示例1:输入:matrix=[[1,2,3],[4,5,6],[7,8,9]]输出:[[7,4,1],[8,5,2],[9,6,3]]示例2:输入:matrix=[[5,1,9,11],[2,4,8,10],[13,3,6,</div>
                    </li>
                    <li><a href="/article/1950228596207448064.htm"
                           title="日更50天有什么收益?" target="_blank">日更50天有什么收益?</a>
                        <span class="text-muted">星湾二宝</span>

                        <div>坚持在平台上日更50天了,平台也为我生成了日更50天徽章,小开心一下这份坚持。日更50天徽章那坚持50天都有哪些收益呢?收益一,就是最直观的那些钻和贝,我这边确实不太高,但是这些贝足够支撑我保持会员的资格,能够在发文的时候帮助友友们去除广告,方便阅读。钻和贝收益二,文章的收获,日更50天,坚持写作3.7万文字,书写的文字也从开始的流水账/碎碎念逐渐加入自己的思考和观点。以前,一个念头会一晃而过,如</div>
                    </li>
                    <li><a href="/article/1950228468897738752.htm"
                           title="别再讲道理啦,对方听不进去的" target="_blank">别再讲道理啦,对方听不进去的</a>
                        <span class="text-muted">方所</span>

                        <div>我之前写过一篇叫做《你总妄想改变他人》,然后就有朋友跟我说,有一些方法可以改变他人之类的。嗯,是这样,但是任何具体的问题,都要限定好语境,描述清楚前提条件,然后再表达观点,我的这位朋友的说法就犯了一刀切的错误,这样并不能让讨论正常展开(这篇我得先给她看看,不然可能会挨揍)。好了,hhhh,谁让她不能写文章呢,我就来再说一说吧。我前面说过,我们在学到一个道理、学会一种方法之后,总是迫不及待地想要去与</div>
                    </li>
                    <li><a href="/article/1950228285266915328.htm"
                           title="Git 与 GitHub 的对比与使用指南" target="_blank">Git 与 GitHub 的对比与使用指南</a>
                        <span class="text-muted">一念&</span>
<a class="tag" taget="_blank" href="/search/%E5%85%B6%E5%AE%83/1.htm">其它</a><a class="tag" taget="_blank" href="/search/git/1.htm">git</a><a class="tag" taget="_blank" href="/search/github/1.htm">github</a>
                        <div>Git与GitHub的对比与使用指南在软件开发中,Git和GitHub是两个密切相关但本质不同的工具。下面我将逐步解释它们的定义、区别、核心概念以及如何协同使用,确保内容真实可靠,基于广泛的技术实践。1.什么是Git?Git是一个分布式版本控制系统,由LinusTorvalds于2005年创建。它的核心功能是跟踪代码文件的变化,帮助开发者管理项目历史记录、协作和回滚错误。Git是开源的,可以在本地</div>
                    </li>
                    <li><a href="/article/1950228031117258752.htm"
                           title="深入解析JVM工作原理:从字节码到机器指令的全过程" target="_blank">深入解析JVM工作原理:从字节码到机器指令的全过程</a>
                        <span class="text-muted"></span>

                        <div>一、JVM概述Java虚拟机(JVM)是Java平台的核心组件,它实现了Java"一次编写,到处运行"的理念。JVM是一个抽象的计算机器,它有自己的指令集和运行时内存管理机制。JVM的主要职责:加载:读取.class文件并验证其正确性存储:管理内存分配和垃圾回收执行:解释或编译字节码为机器指令安全:提供沙箱环境限制恶意代码二、JVM架构详解JVM由三个主要子系统组成:1.类加载子系统类加载过程分为</div>
                    </li>
                    <li><a href="/article/1950227023859347456.htm"
                           title="Linux系统配置(应用程序)" target="_blank">Linux系统配置(应用程序)</a>
                        <span class="text-muted">1风天云月</span>
<a class="tag" taget="_blank" href="/search/Linux/1.htm">Linux</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F/1.htm">应用程序</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E8%AF%91%E5%AE%89%E8%A3%85/1.htm">编译安装</a><a class="tag" taget="_blank" href="/search/rpm/1.htm">rpm</a><a class="tag" taget="_blank" href="/search/http/1.htm">http</a>
                        <div>目录前言一、应用程序概述1、命令与程序的关系2、程序的组成3、软件包封装类型二、RPM1、RPM概述2、RPM用法三、编译安装1、解包2、配置3、编译4、安装5、启用httpd服务结语前言在Linux中的应用程序被视为将软件包安装到系统中后产生的各种文档,其中包括可执行文件、配置文件、用户手册等内容,这些文档被组织为一个有机的整体,为用户提供特定的功能,因此对于“安装软件包”与“安装应用程序”这两</div>
                    </li>
                    <li><a href="/article/1950227022097739776.htm"
                           title="第28章 汇编语言--- 异常处理" target="_blank">第28章 汇编语言--- 异常处理</a>
                        <span class="text-muted">hummhumm</span>
<a class="tag" taget="_blank" href="/search/%E6%B1%87%E7%BC%96/1.htm">汇编</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/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/%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1/1.htm">程序设计</a><a class="tag" taget="_blank" href="/search/%E9%AB%98%E7%BA%A7%E8%AF%AD%E8%A8%80/1.htm">高级语言</a><a class="tag" taget="_blank" href="/search/%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86/1.htm">异常处理</a><a class="tag" taget="_blank" href="/search/%E6%B1%87%E7%BC%96%E8%AF%AD%E8%A8%80/1.htm">汇编语言</a>
                        <div>在汇编语言中,异常处理是一个重要的概念,它涉及到处理器如何响应和处理程序运行时发生的非正常情况。异常可以是硬件错误(例如除零错误、非法指令)或者软件触发的中断(例如系统调用)。当发生异常时,处理器会暂停当前正在执行的程序,并转移到一个预先定义好的位置来处理这个异常。为了详细阐述第28章关于汇编语言中的异常处理,我们可以考虑一个简化的例子,展示异常处理的基本结构。请注意,实际的代码将取决于具体的处理</div>
                    </li>
                    <li><a href="/article/1950226770049429504.htm"
                           title="【C++算法】76.优先级队列_前 K 个高频单词" target="_blank">【C++算法】76.优先级队列_前 K 个高频单词</a>
                        <span class="text-muted">流星白龙</span>
<a class="tag" taget="_blank" href="/search/%E4%BC%98%E9%80%89%E7%AE%97%E6%B3%95C%2B%2B/1.htm">优选算法C++</a><a class="tag" taget="_blank" href="/search/c%2B%2B/1.htm">c++</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/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>文章目录题目链接:题目描述:解法C++算法代码:题目链接:692.前K个高频单词题目描述:解法利用堆来解决TopK问题预处理一下原始的字符串数组,用一个哈希表统计一下每一个单词出现的频次。创建一个大小为k的堆频次:小根堆字典序(频次相同的时候):大根堆循环让元素依次进堆判断提取结果C++算法代码:classSolution{//定义类型别名,PSI表示对typedefpairPSI;//自定义比较</div>
                    </li>
                    <li><a href="/article/1950226169030832128.htm"
                           title="2022-04-07" target="_blank">2022-04-07</a>
                        <span class="text-muted">上善若水1979527</span>

                        <div>昨天苏阿姨来给我送普门品!和她一块来的还有另外一个阿姨!我下去拿时!那阿姨说你不是干房产的小赵!帮我租房的!我说你认识我?对啊!你帮我租的房子!我没印象了!我和保红一块!我说我想起来了阿姨!你这运动服一换!小电车一骑!一下年轻十岁我早记不得了!我只记得一九年五一带看了河滨美航还有德胜的好多套房子!你都不当意!你告诉我你每晚拜观音菩萨说快让小赵普萨帮我找套合适的房子吧!我把本子上记得半年内的德胜房源</div>
                    </li>
                    <li><a href="/article/1950226137909096448.htm"
                           title="互信息:理论框架、跨学科应用与前沿进展" target="_blank">互信息:理论框架、跨学科应用与前沿进展</a>
                        <span class="text-muted">大千AI助手</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/Python/1.htm">Python</a><a class="tag" taget="_blank" href="/search/%23/1.htm">#</a><a class="tag" taget="_blank" href="/search/OTHER/1.htm">OTHER</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%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/1.htm">深度学习</a><a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E4%BA%92%E4%BF%A1%E6%81%AF/1.htm">互信息</a><a class="tag" taget="_blank" href="/search/%E9%A6%99%E5%86%9C/1.htm">香农</a><a class="tag" taget="_blank" href="/search/%E9%80%9A%E4%BF%A1/1.htm">通信</a><a class="tag" taget="_blank" href="/search/%E9%9A%8F%E6%9C%BA%E5%8F%98%E9%87%8F/1.htm">随机变量</a>
                        <div>1.起源与核心定义互信息(MutualInformation,MI)由克劳德·香农(ClaudeShannon)在1948年开创性论文《AMathematicalTheoryofCommunication》中首次提出,该论文奠定了现代信息论的基础。互信息用于量化两个随机变量之间的统计依赖关系,定义为:若已知一个随机变量的取值,能为另一个随机变量提供的信息量。数学上,对于离散随机变量XXX和YYY,</div>
                    </li>
                    <li><a href="/article/1950226011559882752.htm"
                           title="什么是缓存雪崩?缓存击穿?缓存穿透?分别如何解决?什么是缓存预热?" target="_blank">什么是缓存雪崩?缓存击穿?缓存穿透?分别如何解决?什么是缓存预热?</a>
                        <span class="text-muted">daixin8848</span>
<a class="tag" taget="_blank" href="/search/%E7%BC%93%E5%AD%98/1.htm">缓存</a><a class="tag" taget="_blank" href="/search/redis/1.htm">redis</a><a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>缓存雪崩:在一个时间段内,有大量的key过期,或者Redis服务宕机,导致大量的请求到达数据库,带来巨大压力-给key设置不同的TTL、利用Redis集群提高服务的高可用性、添加多级缓存、添加降级流策略缓存击穿:给某一个key设置了过期时间,当key过期的时间,恰好这个时间点有大量的并发请求访问这个key,可能会瞬间把数据库压垮-互斥锁:缓存失败时,只允许一个请求去加载数据并更新缓存,其他请求阻塞</div>
                    </li>
                    <li><a href="/article/1950225786325757952.htm"
                           title="高省邀请码哪个级别最高?高省原始高级邀请码500888" target="_blank">高省邀请码哪个级别最高?高省原始高级邀请码500888</a>
                        <span class="text-muted">凌风导师</span>

                        <div>高省是杭州长孚科技有限公司开辟的一个项目(高省APP),已获数千万投资,主打外交电商+新零售,提供淘宝好省京东等主流电商途径的大额优惠券(直接在淘宝等途径购买是没有该优惠券的)。用户们可以利用这些软件享受折扣买东西优惠,一起来看看购物省钱app网上买东西省钱的软件有哪些吧!网购省钱软件靠谱吗包围淘宝等途径产品的左右,也便是你可以大概在高省买你的确想买的任何产品,并且享受额定优惠,优惠力度为-(大部</div>
                    </li>
                    <li><a href="/article/1950225785054883840.htm"
                           title="Java | 多线程经典问题 - 售票" target="_blank">Java | 多线程经典问题 - 售票</a>
                        <span class="text-muted">Ada54</span>

                        <div>一、售票需求1)同一个票池2)多个窗口卖票,不能出售同一张票二、售票问题代码实现(线程与进程小总结,请戳:Java|线程和进程,创建线程)step1:定义SaleWindow类实现Runnable接口,覆盖run方法step2:实例化SaleWindow对象,创建Thread对象,将SaleWindow作为参数传给Thread类的构造函数,然后通过Thread.start()方法启动线程step3</div>
                    </li>
                    <li><a href="/article/1950225530758426624.htm"
                           title="2021-会说话的人运气都不会太差-会说话的人更善于倾听(5)沉默、留白" target="_blank">2021-会说话的人运气都不会太差-会说话的人更善于倾听(5)沉默、留白</a>
                        <span class="text-muted">12_德德</span>

                        <div>21、说话时,如想强调某一部分,一个有效方法是,以沉默留出空白。留出多大空白才好呢?7秒。22、沉默所营造的“空白”长度,说的人感觉到的,是实际长度的3倍,而听的人感觉到的,则只有实际长度的1/3。23、只要善于用“一直”来表达,就不会给人以不过是一时任性之感。能说“一直”的人,会得到周围的支持,在合适的时候想到你。24、要得到“是把好手”的高度评价,就要展示你对公司的热爱。特别是,如能对上司或公</div>
                    </li>
                    <li><a href="/article/1950225381961297920.htm"
                           title="SpringMVC的执行流程" target="_blank">SpringMVC的执行流程</a>
                        <span class="text-muted"></span>

                        <div>1、什么是MVCMVC是一种设计模式。MVC的原理图如下所示M-Model模型(完成业务逻辑:有javaBean构成,service+dao+entity)V-View视图(做界面的展示jsp,html……)C-Controller控制器(接收请求—>调用模型—>根据结果派发页面2、SpringMVC是什么SpringMVC是一个MVC的开源框架,SpringMVC=Struts2+Spring,</div>
                    </li>
                    <li><a href="/article/1950224877067759616.htm"
                           title="车载刷写架构 --- 整车刷写中为何增加了ECU 队列刷写策略?" target="_blank">车载刷写架构 --- 整车刷写中为何增加了ECU 队列刷写策略?</a>
                        <span class="text-muted">汽车电子实验室</span>
<a class="tag" taget="_blank" href="/search/%E7%94%B5%E5%AD%90%E7%94%B5%E5%99%A8%E6%9E%B6%E6%9E%84%E2%80%94%E2%80%94%E5%88%B7%E5%86%99%E6%96%B9%E6%A1%88/1.htm">电子电器架构——刷写方案</a><a class="tag" taget="_blank" href="/search/%E8%BD%A6%E8%BD%BD%E7%94%B5%E5%AD%90%E7%94%B5%E6%B0%94%E6%9E%B6%E6%9E%84/1.htm">车载电子电气架构</a><a class="tag" taget="_blank" href="/search/%E6%9E%B6%E6%9E%84/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/%E8%BD%A6%E8%BD%BD%E8%AF%8A%E6%96%AD%E8%BF%9B%E9%98%B6%E7%AF%87/1.htm">车载诊断进阶篇</a><a class="tag" taget="_blank" href="/search/%E6%B1%BD%E8%BD%A6%E4%B8%AD%E5%A4%AE%E6%8E%A7%E5%88%B6%E5%8D%95%E5%85%83HPC%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84/1.htm">汽车中央控制单元HPC软件架构</a><a class="tag" taget="_blank" href="/search/%E5%85%B3%E4%BA%8E%E7%BD%91%E5%85%B3%E8%BD%AC%E5%8F%91%E6%80%A7%E8%83%BD%E5%BC%95%E8%B5%B7%E7%9A%84%E6%80%9D%E8%80%83/1.htm">关于网关转发性能引起的思考</a>
                        <div>我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师:周末洗了一个澡,换了一身衣服,出了门却不知道去哪儿,不知道去找谁,漫无目的走着,大概这就是成年人最深的孤独吧!旧人不知我近况,新人不知我过往,近况不该旧人知,过往不与新人讲。纵你阅人何其多,再无一人恰似我。时间不知不觉中,来到新的一年。2025开始新的忙碌。成年人的我也不知道去哪里渡</div>
                    </li>
                    <li><a href="/article/1950224745093984256.htm"
                           title="在线人数统计业务设计(场景八股文)" target="_blank">在线人数统计业务设计(场景八股文)</a>
                        <span class="text-muted"></span>

                        <div>业务问题在当经的网站中,在线人数的实时统计已经是一个必不可少的模块了,并且该统计功能最好能够按不同的时间间隔做的统计,现在需要你设计一个在线人数统计的模块,你应该怎么进行设计的呢?背景一个网校下会有多个学员。目前平台大概有十个,平台对应的网校大概五十几个,平均一个网校会有5w个用户,预计总人数为200w,最该学员的在线人数在10w左右。设计思路最开始的时候,想到的就是使用mysql直接实现,但是明</div>
                    </li>
                    <li><a href="/article/1950223483048882176.htm"
                           title="【ARM】FPU,VFP,ASE,NEON,SVE...是什么意思?" target="_blank">【ARM】FPU,VFP,ASE,NEON,SVE...是什么意思?</a>
                        <span class="text-muted">亿道电子Emdoor</span>
<a class="tag" taget="_blank" href="/search/ARM/1.htm">ARM</a><a class="tag" taget="_blank" href="/search/arm%E5%BC%80%E5%8F%91/1.htm">arm开发</a><a class="tag" taget="_blank" href="/search/ARM/1.htm">ARM</a>
                        <div>1、文档目标对执行浮点和SIMD操作的逻辑的各种名称的缩写词进行简要解释。2、问题场景Arm处理器内核中有用于执行浮点和SIMD操作的逻辑,有各种名称。它们通常是一系列的缩写形式,因此本文旨在对每一个缩写词进行简要解释。3、软硬件环境1、软件版本:不涉及2、电脑环境:不涉及4、相关缩写FPU(Floating-PointUnit)浮点单元浮点单元是处理器核心中的一个模块,用于使用浮点数执行算术运算</div>
                    </li>
                    <li><a href="/article/1950222978759323648.htm"
                           title="免费排版助手:智能修正段落 + 删除干扰符,杂乱文本一键变规范" target="_blank">免费排版助手:智能修正段落 + 删除干扰符,杂乱文本一键变规范</a>
                        <span class="text-muted"></span>

                        <div>各位文字工作者们!你们有没有被排版折磨到崩溃的时候?我跟你们说,我之前排版一篇文章,那简直就像在走迷宫,头晕眼花的!不过后来我发现了一款软件——排版助手!软件下载地址安装包这玩意儿是个文章智能排版工具,专门给新闻编辑、文摘网站这些文字工作者用的。它功能老多了,能修正段落,把那些乱七八糟的段落变得规规矩矩;还能删除干扰符,就像给文章做了个大扫除,把没用的东西都清理掉;简繁转换也不在话下,不管是简体还</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>