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

简介

大家好,我是一个闲着没事热衷于重复造轮子的不知名前端,今天给大家带来的是一个代码在线编辑预览工具的实现介绍,目前这类工具使用很广泛,常见于各种文档网站及代码分享场景,相关工具也比较多,如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/1835514335561084928.htm"
                           title="芦花鞋一四" target="_blank">芦花鞋一四</a>
                        <span class="text-muted">许叶晗</span>

                        <div>又是在一个寒冷的夏日里,青铜和葵花决定今天一起去卖芦花鞋,奶奶亲手给他们做了一碗热乎乎的粥对他们说:“就靠你们两挣生活费了这碗粥赶紧趁热喝了吧!”于是青铜和葵花喝完了奶奶给她们做的粥,就准备去镇上卖卢花鞋,这回青铜和葵花穿着新的芦花鞋来到了镇上。青铜这回看到了很多人都在卖,用手势表达对葵花说:“这回有好多人在抢我们生意呢!我们必须得吆喝起来。”葵花点了点头。可是谁知他们也大声的叫,卖芦花喽!卖芦花</div>
                    </li>
                    <li><a href="/article/1835514307744460800.htm"
                           title="QQ群采集助手,精准引流必备神器" target="_blank">QQ群采集助手,精准引流必备神器</a>
                        <span class="text-muted">2401_87347160</span>
<a class="tag" taget="_blank" href="/search/%E5%85%B6%E4%BB%96/1.htm">其他</a><a class="tag" taget="_blank" href="/search/%E7%BB%8F%E9%AA%8C%E5%88%86%E4%BA%AB/1.htm">经验分享</a>
                        <div>功能概述微信群查找与筛选工具是一款专为微信用户设计的辅助工具,它通过关键词搜索功能,帮助用户快速找到相关的微信群,并提供筛选是否需要验证的群组的功能。主要功能关键词搜索:用户可以输入关键词,工具将自动查找包含该关键词的微信群。筛选功能:工具提供筛选机制,用户可以选择是否只显示需要验证或不需要验证的群组。精准引流:通过上述功能,用户可以更精准地找到目标群组,进行有效的引流操作。3.设备需求该工具可以</div>
                    </li>
                    <li><a href="/article/1835513570171908096.htm"
                           title="底层逆袭到底有多难,不甘平凡的你准备好了吗?让吴起给你说说" target="_blank">底层逆袭到底有多难,不甘平凡的你准备好了吗?让吴起给你说说</a>
                        <span class="text-muted">造命者说</span>

                        <div>底层逆袭到底有多难,不甘平凡的你准备好了吗?让吴起给你说说我叫吴起,生于公元前440年的战国初期,正是群雄并起、天下纷争不断的时候。后人说我是军事家、政治家、改革家,是兵家代表人物。评价我一生历仕鲁、魏、楚三国,通晓兵家、法家、儒家三家思想,在内政军事上都有极高的成就。周安王二十一年(公元前381年),因变法得罪守旧贵族,被人乱箭射死。我出生在卫国一个“家累万金”的富有家庭,从年轻时候起就不甘平凡</div>
                    </li>
                    <li><a href="/article/1835513571501502464.htm"
                           title="2020-01-25" target="_blank">2020-01-25</a>
                        <span class="text-muted">晴岚85</span>

                        <div>郑海燕坚持分享590天2020.1.24在生活中只存在两个问题。一个问题是:你知道想要达成的目标是什么,但却不知道如何才能达成;另一个问题是:你不知道你的目标是什么。前一个是行动的问题,后一个是结果的问题。通过制定具体的下一步行动,可以解决不知道如何开始行动的问题。而通过去想象结果,对结果做预估,可以解决找不着目标的问题。对于所有吸引我们注意力,想要完成的任务,你可以先想象一下,预期的结果究竟是什</div>
                    </li>
                    <li><a href="/article/1835513568917811200.htm"
                           title="随笔 | 仙一般的灵气" target="_blank">随笔 | 仙一般的灵气</a>
                        <span class="text-muted">海思沧海</span>

                        <div>仙岛今天,我看了你全部,似乎已经进入你的世界我不知道,这是否是梦幻,还是你仙一般的灵气吸引了我也许每一个人都要有一份属于自己的追求,这样才能够符合人生的梦想,生活才能够充满着阳光与快乐我不知道,我为什么会这样的感叹,是在感叹自己的人生,还是感叹自己一直没有孜孜不倦的追求只感觉虚度了光阴,每天活在自己的梦中,活在一个不真实的世界是在逃避自己,还是在逃避周围的一切有时候我嘲笑自己,嘲笑自己如此的虚无,</div>
                    </li>
                    <li><a href="/article/1835513551624695808.htm"
                           title="【iOS】MVC设计模式" target="_blank">【iOS】MVC设计模式</a>
                        <span class="text-muted">Magnetic_h</span>
<a class="tag" taget="_blank" href="/search/ios/1.htm">ios</a><a class="tag" taget="_blank" href="/search/mvc/1.htm">mvc</a><a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a><a class="tag" taget="_blank" href="/search/objective-c/1.htm">objective-c</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/ui/1.htm">ui</a>
                        <div>MVC前言如何设计一个程序的结构,这是一门专门的学问,叫做"架构模式"(architecturalpattern),属于编程的方法论。MVC模式就是架构模式的一种。它是Apple官方推荐的App开发架构,也是一般开发者最先遇到、最经典的架构。MVC各层controller层Controller/ViewController/VC(控制器)负责协调Model和View,处理大部分逻辑它将数据从Mod</div>
                    </li>
                    <li><a href="/article/1835513551142350848.htm"
                           title="OC语言多界面传值五大方式" target="_blank">OC语言多界面传值五大方式</a>
                        <span class="text-muted">Magnetic_h</span>
<a class="tag" taget="_blank" href="/search/ios/1.htm">ios</a><a class="tag" taget="_blank" href="/search/ui/1.htm">ui</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a><a class="tag" taget="_blank" href="/search/objective-c/1.htm">objective-c</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E5%8F%91%E8%AF%AD%E8%A8%80/1.htm">开发语言</a>
                        <div>前言在完成暑假仿写项目时,遇到了许多需要用到多界面传值的地方,这篇博客来总结一下比较常用的五种多界面传值的方式。属性传值属性传值一般用前一个界面向后一个界面传值,简单地说就是通过访问后一个视图控制器的属性来为它赋值,通过这个属性来做到从前一个界面向后一个界面传值。首先在后一个界面中定义属性@interfaceBViewController:UIViewController@propertyNSSt</div>
                    </li>
                    <li><a href="/article/1835513424734416896.htm"
                           title="UI学习——cell的复用和自定义cell" target="_blank">UI学习——cell的复用和自定义cell</a>
                        <span class="text-muted">Magnetic_h</span>
<a class="tag" taget="_blank" href="/search/ui/1.htm">ui</a><a class="tag" taget="_blank" href="/search/%E5%AD%A6%E4%B9%A0/1.htm">学习</a>
                        <div>目录cell的复用手动(非注册)自动(注册)自定义cellcell的复用在iOS开发中,单元格复用是一种提高表格(UITableView)和集合视图(UICollectionView)滚动性能的技术。当一个UITableViewCell或UICollectionViewCell首次需要显示时,如果没有可复用的单元格,则视图会创建一个新的单元格。一旦这个单元格滚动出屏幕,它就不会被销毁。相反,它被添</div>
                    </li>
                    <li><a href="/article/1835512920797179904.htm"
                           title="element实现动态路由+面包屑" target="_blank">element实现动态路由+面包屑</a>
                        <span class="text-muted">软件技术NINI</span>
<a class="tag" taget="_blank" href="/search/vue%E6%A1%88%E4%BE%8B/1.htm">vue案例</a><a class="tag" taget="_blank" href="/search/vue.js/1.htm">vue.js</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>el-breadcrumb是ElementUI组件库中的一个面包屑导航组件,它用于显示当前页面的路径,帮助用户快速理解和导航到应用的各个部分。在Vue.js项目中,如果你已经安装了ElementUI,就可以很方便地使用el-breadcrumb组件。以下是一个基本的使用示例:安装ElementUI(如果你还没有安装的话):你可以通过npm或yarn来安装ElementUI。bash复制代码npmi</div>
                    </li>
                    <li><a href="/article/1835512809883004928.htm"
                           title="10月|愿你的青春不负梦想-读书笔记-01" target="_blank">10月|愿你的青春不负梦想-读书笔记-01</a>
                        <span class="text-muted">Tracy的小书斋</span>

                        <div>本书的作者是俞敏洪,大家都很熟悉他了吧。俞敏洪老师是我行业的领头羊吧,也是我事业上的偶像。本日摘录他书中第一章中的金句:『一个人如果什么目标都没有,就会浑浑噩噩,感觉生命中缺少能量。能给我们能量的,是对未来的期待。第一件事,我始终为了进步而努力。与其追寻全世界的骏马,不如种植丰美的草原,到时骏马自然会来。第二件事,我始终有阶段性的目标。什么东西能给我能量?答案是对未来的期待。』读到这里的时候,我便</div>
                    </li>
                    <li><a href="/article/1835511911769272320.htm"
                           title="C语言如何定义宏函数?" target="_blank">C语言如何定义宏函数?</a>
                        <span class="text-muted">小九格物</span>
<a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a>
                        <div>在C语言中,宏函数是通过预处理器定义的,它在编译之前替换代码中的宏调用。宏函数可以模拟函数的行为,但它们不是真正的函数,因为它们在编译时不会进行类型检查,也不会分配存储空间。宏函数的定义通常使用#define指令,后面跟着宏的名称和参数列表,以及宏展开后的代码。宏函数的定义方式:1.基本宏函数:这是最简单的宏函数形式,它直接定义一个表达式。#defineSQUARE(x)((x)*(x))2.带参</div>
                    </li>
                    <li><a href="/article/1835511912843014144.htm"
                           title="理解Gunicorn:Python WSGI服务器的基石" target="_blank">理解Gunicorn:Python WSGI服务器的基石</a>
                        <span class="text-muted">范范0825</span>
<a class="tag" taget="_blank" href="/search/ipython/1.htm">ipython</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E8%BF%90%E7%BB%B4/1.htm">运维</a>
                        <div>理解Gunicorn:PythonWSGI服务器的基石介绍Gunicorn,全称GreenUnicorn,是一个为PythonWSGI(WebServerGatewayInterface)应用设计的高效、轻量级HTTP服务器。作为PythonWeb应用部署的常用工具,Gunicorn以其高性能和易用性著称。本文将介绍Gunicorn的基本概念、安装和配置,帮助初学者快速上手。1.什么是Gunico</div>
                    </li>
                    <li><a href="/article/1835511669476913152.htm"
                           title="小丽成长记(四十三)" target="_blank">小丽成长记(四十三)</a>
                        <span class="text-muted">玲玲54321</span>

                        <div>小丽发现,即使她好不容易调整好自己的心态下一秒总会有不确定的伤脑筋的事出现,一个接一个的问题,人生就没有停下的时候,小问题不断出现。不过她今天看的书,她接受了人生就是不确定的,厉害的人就是不断创造确定性,在Ta的领域比别人多的确定性就能让自己脱颖而出,显示价值从而获得的比别人多的利益。正是这样的原因,因为从前修炼自己太少,使得她现在在人生道路上打怪起来困难重重,她似乎永远摆脱不了那种无力感,有种习</div>
                    </li>
                    <li><a href="/article/1835511542284644352.htm"
                           title="学点心理知识,呵护孩子健康" target="_blank">学点心理知识,呵护孩子健康</a>
                        <span class="text-muted">静候花开_7090</span>

                        <div>昨天听了华中师范大学教育管理学系副教授张玲老师的《哪里才是学生心理健康的最后庇护所,超越教育与技术的思考》的讲座。今天又重新学习了一遍,收获匪浅。张玲博士也注意到了当今社会上的孩子由于心理问题导致的自残、自杀及伤害他人等恶性事件。她向我们普及了一个重要的命题,她说心理健康的一些基本命题,我们与我们通常的一些教育命题是不同的,她还举了几个例子,让我们明白我们原来以为的健康并非心理学上的健康。比如如果</div>
                    </li>
                    <li><a href="/article/1835510909070569472.htm"
                           title="瑶池防线" target="_blank">瑶池防线</a>
                        <span class="text-muted">谜影梦蝶</span>

                        <div>冥华虽然逃过了影梦的军队,但他是一个忠臣,他选择上报战况。败给影梦后成逃兵,高层亡尔还活着,七重天失守......随便一条,即可处死冥华。冥华自然是知道以仙界高层的习性此信一发自己必死无疑,但他还选择上报实情,因为责任。同样此信送到仙宫后,知道此事的人,大多数人都认定冥华要完了,所以上到仙界高层,下到扫大街的,包括冥华自己,全都准备好迎接冥华之死。如果仙界现在还属于两方之争的话,冥华必死无疑。然而</div>
                    </li>
                    <li><a href="/article/1835509898507546624.htm"
                           title="《策划经理回忆录之二》" target="_blank">《策划经理回忆录之二》</a>
                        <span class="text-muted">路基雅虎</span>

                        <div>话说三年变六年,飘了,飘了……眨眼,2013年5月,老吴回到了他的家乡——油城从新开启他的工作幻想症生涯。很庆幸,这是一家很有追求,同时敢于尝试的,且实力不容低调的新星房企——金源置业(前身泰源置业)更值得庆幸的是第一个盘就是油城十路的标杆之一:金源盛世。2013年5月,到2015年11月,两年的陪伴,迎来了一场大爆发。2000个筹,5万/筹,直接回笼1个亿!!!这……让我开始认真审视这座看似五线</div>
                    </li>
                    <li><a href="/article/1835509897106649088.htm"
                           title="Long类型前后端数据不一致" target="_blank">Long类型前后端数据不一致</a>
                        <span class="text-muted">igotyback</span>
<a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>响应给前端的数据浏览器控制台中response中看到的Long类型的数据是正常的到前端数据不一致前后端数据类型不匹配是一个常见问题,尤其是当后端使用Java的Long类型(64位)与前端JavaScript的Number类型(最大安全整数为2^53-1,即16位)进行数据交互时,很容易出现精度丢失的问题。这是因为JavaScript中的Number类型无法安全地表示超过16位的整数。为了解决这个问</div>
                    </li>
                    <li><a href="/article/1835509643619692544.htm"
                           title="如何在 Fork 的 GitHub 项目中保留自己的修改并同步上游更新?github_fork_update" target="_blank">如何在 Fork 的 GitHub 项目中保留自己的修改并同步上游更新?github_fork_update</a>
                        <span class="text-muted">iBaoxing</span>
<a class="tag" taget="_blank" href="/search/github/1.htm">github</a>
                        <div>如何在Fork的GitHub项目中保留自己的修改并同步上游更新?在GitHub上Fork了一个项目后,你可能会对项目进行一些修改,同时原作者也在不断更新。如果想要在保留自己修改的基础上,同步原作者的最新更新,很多人会不知所措。本文将详细讲解如何在不丢失自己改动的情况下,将上游仓库的更新合并到自己的仓库中。问题描述假设你在GitHub上Fork了一个项目,并基于该项目做了一些修改,随后你发现原作者对</div>
                    </li>
                    <li><a href="/article/1835509390879322112.htm"
                           title="扫地机类清洁产品之直流无刷电机控制" target="_blank">扫地机类清洁产品之直流无刷电机控制</a>
                        <span class="text-muted">悟空胆好小</span>
<a class="tag" taget="_blank" href="/search/%E6%B8%85%E6%B4%81%E6%9C%8D%E5%8A%A1%E6%9C%BA%E5%99%A8%E4%BA%BA/1.htm">清洁服务机器人</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E7%89%87%E6%9C%BA/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>
                        <div>扫地机类清洁产品之直流无刷电机控制1.1前言扫地机产品有很多的电机控制,滚刷电机1个,边刷电机1-2个,清水泵电机,风机一个,部分中高端产品支持抹布功能,也就是存在抹布盘电机,还有追觅科沃斯石头等边刷抬升电机,滚刷抬升电机等的,这些电机有直流有刷电机,直接无刷电机,步进电机,电磁阀,挪动泵等不同类型。电机的原理,驱动控制方式也不行。接下来一段时间的几个文章会作个专题分析分享。直流有刷电机会自动持续</div>
                    </li>
                    <li><a href="/article/1835509266627260416.htm"
                           title="绘本讲师训练营【24期】8/21阅读原创《独生小孩》" target="_blank">绘本讲师训练营【24期】8/21阅读原创《独生小孩》</a>
                        <span class="text-muted">1784e22615e0</span>

                        <div>24016-孟娟《独生小孩》图片发自App今天我想分享一个蛮特别的绘本,讲的是一个特殊的群体,我也是属于这个群体,80后的独生小孩。这是一本中国绘本,作者郭婧,也是一个80厚。全书一百多页,均为铅笔绘制,虽然为黑白色调,但并不显得沉闷。全书没有文字,犹如“默片”,但并不影响读者对该作品的理解,反而显得神秘,梦幻,給读者留下想象的空间。作者在前蝴蝶页这样写到:“我更希望父母和孩子一起分享这本书,使他</div>
                    </li>
                    <li><a href="/article/1835509138126368768.htm"
                           title="30天风格练习-DAY2" target="_blank">30天风格练习-DAY2</a>
                        <span class="text-muted">黄希夷</span>

                        <div>Day2(重义)在一个周日/一周的最后一天,我来到位于市中心/市区繁华地带的一家购物中心/商场,中心内人很多/熙熙攘攘。我注意到/看见一个独行/孤身一人的年轻女孩/,留着一头引人注目/长过腰际的头发,上身穿一件暗红色/比正红色更深的衣服/穿在身体上的东西。走下扶梯的时候,她摔倒了/跌向地面,在她正要站起来/让身体离开地面的时候,过长/超过一般人长度的头发被支撑身体/躯干的手掌压/按在下面,她赶紧用</div>
                    </li>
                    <li><a href="/article/1835508130268672000.htm"
                           title="消息中间件有哪些常见类型" target="_blank">消息中间件有哪些常见类型</a>
                        <span class="text-muted">xmh-sxh-1314</span>
<a class="tag" taget="_blank" href="/search/java/1.htm">java</a>
                        <div>消息中间件根据其设计理念和用途,可以大致分为以下几种常见类型:点对点消息队列(Point-to-PointMessagingQueues):在这种模型中,消息被发送到特定的队列中,消费者从队列中取出并处理消息。队列中的消息只能被一个消费者消费,消费后即被删除。常见的实现包括IBM的MQSeries、RabbitMQ的部分使用场景等。适用于任务分发、负载均衡等场景。发布/订阅消息模型(Pub/Sub</div>
                    </li>
                    <li><a href="/article/1835508130608410624.htm"
                           title="html 中如何使用 uniapp 的部分方法" target="_blank">html 中如何使用 uniapp 的部分方法</a>
                        <span class="text-muted">某公司摸鱼前端</span>
<a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/uni-app/1.htm">uni-app</a><a class="tag" taget="_blank" href="/search/%E5%89%8D%E7%AB%AF/1.htm">前端</a>
                        <div>示例代码:Documentconsole.log(window);效果展示:好了,现在就可以uni.使用相关的方法了</div>
                    </li>
                    <li><a href="/article/1835508131489214464.htm"
                           title="高级编程--XML+socket练习题" target="_blank">高级编程--XML+socket练习题</a>
                        <span class="text-muted">masa010</span>
<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>1.北京华北2114.8万人上海华东2,500万人广州华南1292.68万人成都华西1417万人(1)使用dom4j将信息存入xml中(2)读取信息,并打印控制台(3)添加一个city节点与子节点(4)使用socketTCP协议编写服务端与客户端,客户端输入城市ID,服务器响应相应城市信息(5)使用socketTCP协议编写服务端与客户端,客户端要求用户输入city对象,服务端接收并使用dom4j</div>
                    </li>
                    <li><a href="/article/1835508122383380480.htm"
                           title="抖音乐买买怎么加入赚钱?赚钱方法是什么" target="_blank">抖音乐买买怎么加入赚钱?赚钱方法是什么</a>
                        <span class="text-muted">测评君高省</span>

                        <div>你会在抖音买东西吗?如果会,那么一定要免费注册一个乐买买,抖音直播间,橱窗,小视频里的小黄车买东西都可以返佣金!省下来都是自己的,分享还可以赚钱乐买买是好省旗下的抖音返佣平台,乐买买分析社交电商的价值,乐买买属于今年难得的副业项目风口机会,2019年错过做好省的搞钱的黄金时期,那么2022年千万别再错过乐买买至于我为何转到高省呢?当然是高省APP佣金更高,模式更好,终端用户不流失。【高省】是一个自</div>
                    </li>
                    <li><a href="/article/1835508123746529280.htm"
                           title="那个抄袭的大张伟" target="_blank">那个抄袭的大张伟</a>
                        <span class="text-muted">猫小努</span>

                        <div>最近一直在追《即刻电音》这个综艺,除了觉得出场节目的音乐制作人有意思之外,也觉得有两个导师挺有趣的(另外一个就忽略了吧)。孙艺兴在上一篇文章里面已经说过了,那么这篇就说说我们的大老师,大张伟吧。其实在节目刚开始大张伟出来的时候,我以为他是属于导师里面来活跃气氛负责搞笑的,毕竟孙艺兴属于卖萌卖傻卖老实的,尚雯婕一般负责装逼耍狠的,而大张伟一贯以来上综艺的形象基本上都是蹦蹦跳跳带动气氛的。谁知道,两期</div>
                    </li>
                    <li><a href="/article/1835507995350495232.htm"
                           title="三大师传" target="_blank">三大师传</a>
                        <span class="text-muted">beca酱</span>

                        <div>巴尔扎克的作品被誉为“法国社会的一面镜子”。文学大师维克多·雨果对巴尔扎克的评价是:“在最伟大的人物中间,巴尔扎克是名列前茅者;在最优秀的人物中间,巴尔扎克是佼佼者之一。”一个原本寂寂无名的小人物,从地中海的某个海岛上,只身一人来到巴黎,没有朋友,也没有名望。作为一个一文不名的外乡人,凭着赤手空拳赢得了巴黎,征服了整个法兰西,并且赢得了世界。这个人就是十九世纪法国伟大的军事家、政治家,法兰西第一帝</div>
                    </li>
                    <li><a href="/article/1835507739820912640.htm"
                           title="2018-07-23-催眠日作业-#不一样的31天#-66小鹿" target="_blank">2018-07-23-催眠日作业-#不一样的31天#-66小鹿</a>
                        <span class="text-muted">小鹿_33</span>

                        <div>预言日:人总是在逃避命运的路上,与之不期而遇。心理学上有个著名的名词,叫做自证预言;经济学上也有一个很著名的定律叫做,墨菲定律;在灵修派上,还有一个很著名的法则,叫做吸引力法则。这3个领域的词,虽然看起来不太一样,但是他们都在告诉人们一个现象:你越担心什么,就越有可能会发生什么。同样的道理,你越想得到什么,就应该要积极地去创造什么。无论是自证预言,墨菲定律还是吸引力法则,对人都有正反2个维度的影响</div>
                    </li>
                    <li><a href="/article/1835506996258893824.htm"
                           title="回溯 Leetcode 332 重新安排行程" target="_blank">回溯 Leetcode 332 重新安排行程</a>
                        <span class="text-muted">mmaerd</span>
<a class="tag" taget="_blank" href="/search/Leetcode%E5%88%B7%E9%A2%98%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/1.htm">Leetcode刷题学习记录</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/%E8%81%8C%E5%9C%BA%E5%92%8C%E5%8F%91%E5%B1%95/1.htm">职场和发展</a>
                        <div>重新安排行程Leetcode332学习记录自代码随想录给你一份航线列表tickets,其中tickets[i]=[fromi,toi]表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。所有这些机票都属于一个从JFK(肯尼迪国际机场)出发的先生,所以该行程必须从JFK开始。如果存在多种有效的行程,请你按字典排序返回最小的行程组合。例如,行程[“JFK”,“LGA”]与[“JFK”,“LGB</div>
                    </li>
                    <li><a href="/article/1835506868877881344.htm"
                           title="每日一题——第八十九题" target="_blank">每日一题——第八十九题</a>
                        <span class="text-muted">互联网打工人no1</span>
<a class="tag" taget="_blank" href="/search/C%E8%AF%AD%E8%A8%80%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E6%AF%8F%E6%97%A5%E4%B8%80%E7%BB%83/1.htm">C语言程序设计每日一练</a><a class="tag" taget="_blank" href="/search/c%E8%AF%AD%E8%A8%80/1.htm">c语言</a>
                        <div>题目:在字符串中找到提取数字,并统计一共找到多少整数,a123xxyu23&8889,那么找到的整数为123,23,8889//思想:#include#include#includeintmain(){charstr[]="a123xxyu23&8889";intcount=0;intnum=0;//用于临时存放当前正在构建的整数。boolinNum=false;//用于标记当前是否正在读取一个整</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>