【实战】webpack4 + ejs + express 带你撸一个多页应用项目架构

前言

GitHub 完整项目地址

最近接了一个公司官网的项目,需要 SEO 友好,所以不能使用前端框架,前端框架自带的脚手架工具自然也帮不上啥忙。只好自己使用 webpack4 + ejs + express ,从头搭建一个多页应用的项目架构。搭建过程中,遇到许多坑,然而网上的相关参考也是非常少,所以写个博客记录一下搭建过程以及注意事项。

以下我会将重要的细节标红,给需要的朋友参考。

明确需求

在动手开发之前,我们需要先明确这个项目的定位——公司官网,一般来说,官网不会涉及大量的数据交互,比较偏向于数据展示。所以不用前端框架,jquery 即可满足需求。但是考虑到 SEO 所以需要用到服务端渲染,就要使用模板语言(ejs),配合 node 来完成。

根据以上信息,我们就可以确定打包脚本的基本功能,先来简单列个清单:

  1. 需要 webpack 来打包多页应用,且不需要每次新增一个视图文件都添加一个 HTMLWebpackPlugin 和重启 server ,能做到 webpack 配置和文件名解耦,尽量的自动化。
  2. 需要使用 ejs 模板语言编写,能够插入变量和外部 includes 文件,最后运行 build 命令的时候能将通用模板文件(//<header>/<footer></code> 等)自动插入每个视图文件对应位置。</li> <li>需要用到服务端渲染,所以开发环境要脱离 webpack 集成的 <code>webpack-dev-server</code>,能使用自己编写的 node 代码启动服务。</li> <li>拥有完善的 <code>overlay</code> 功能,可以像 <code>webpack-dev-server</code> 那样集成漂亮的 overlay 屏幕报错。</li> <li>能监听文件变化,自动打包和重启服务,最好能做到热更新</li> </ol> <h2 class="heading">开始构建</h2> <p>先建立一个空项目,由于需要自己编写服务端代码,所以我们需要多建一个 <code>/server</code> 文件夹,用来存放 <code>express</code> 的代码,搭建完成后,我们的项目结构看起来是这样。 </p> <p></p> <p>除此以外,我们需要初始化一些通用配置文件,包括:</p> <ul> <li><code>.babelrc</code> babel 配置文件</li> <li><code>.gitignore</code> git 忽略文件</li> <li><code>.editorConfig</code> 编辑器配置文件</li> <li><code>.eslintrc.js</code> eslint 配置文件</li> <li><code>README.md</code> 文件</li> <li><code>package.json</code> 文件</li> </ul> <p>大的框架出来以后,我们开始编写工程代码。</p> <h2 class="heading">打包脚本</h2> <p>首先是编写打包脚本,在<code>/build</code>文件夹里新建几个文件</p> <ol> <li><code>webpack.base.config.js</code>,用来存放生产环境和开发环境通用的 webpack 配置</li> <li><code>webpack.dev.config.js</code>用来存放开发环境的打包配置</li> <li><code>webpack.prod.config.js</code>用来存放生产环境的打包配置</li> <li><code>config.json</code> 用来存放一些配置常量,例如端口名,路径名之类。</li> </ol> <p>一般来说,<code>webpack.base.config</code> 文件里,放一些开发生产环境通用的配置,例如 <code>output</code>、<code>entry</code> 以及一些 <code>loader</code>, 例如编译ES6语法的 <code>babel-loader</code>、打包文件的 <code>file-loader</code> 等。常用的 loader 的使用方式我们可以查看文档 webpack loaders,</p> <p style="color:#FFFFFF;">需要注意的是,这边有个非常重要的 loader ———— ejs-html-loader</p> <p></p> <p>一般来说,我们使用 <code>html-loader</code> 来对<code>.html</code>结尾的视图文件做处理,然后扔给 <code>html-webpack-plugin</code>生成对应的文件,但是 <code>html-loader</code> 无法处理 ejs 模板语法中的 <code><% include ... %></code> 语法,会报错。然而在多页应用里,这个 include 的功能是必须的,不然每个视图文件里都要手动去写一份 <code>header/footer</code> 是什么感觉。。。所以我们需要再多配置一份 ejs-html-loader:</p> <pre><code class="hljs js copyable"><span class="hljs-comment">// webpack.base.config.js 部分代码</span> <span class="hljs-built_in">module</span>: { <span class="hljs-attr">rules</span>: [ ... { <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.ejs$/</span>, <span class="hljs-attr">use</span>: [ { <span class="hljs-attr">loader</span>: <span class="hljs-string">'html-loader'</span>, <span class="hljs-comment">// 使用 html-loader 处理图片资源的引用</span> options: { <span class="hljs-attr">attrs</span>: [<span class="hljs-string">'img:src'</span>, <span class="hljs-string">'img:data-src'</span>] } }, { <span class="hljs-attr">loader</span>: <span class="hljs-string">'ejs-html-loader'</span>, <span class="hljs-comment">// 使用 ejs-html-loader 处理 .ejs 文件的 includes 语法</span> options: { <span class="hljs-attr">production</span>: process.env.ENV === <span class="hljs-string">'production'</span> } } ] } ... ] } <span class="copy-code-btn">复制代码</span></code></pre> <p>第一个坑绕过之后,第二个:</p> <p style="color:#FFFFFF;">entry 入口要怎么写?</p> <p>记得之前公司的一个老项目,五十几个页面,五十几个 <code>entry</code> 和 <code>new HTMLwebpackPlugin()</code> 一个文件展开来可以绕地球一圈。。。这边为了避免这种惨状,写一个方法,返回一个 entry 数组。</p> <p>可以使用 glob 来处理这些文件,获取文件名,当然同样也可以使用原生 node 来实现。只要保证 <code>JavaScript</code> 文件名和视图文件名相同即可,比如,首页的视图文件名是 <code>home.ejs</code>,那么对应的脚本文件名就要用同样的名字 <code>home.js</code> 来命名,webpack 打包的时候会找到脚本文件入口,通过映射关系生成对应视图文件:</p> <pre><code class="hljs js copyable"><span class="hljs-comment">// webpack.base.config.js 部分代码</span> <span class="hljs-keyword">const</span> Webpack = <span class="hljs-built_in">require</span>(<span class="hljs-string">'Webpack'</span>) <span class="hljs-keyword">const</span> glob = <span class="hljs-built_in">require</span>(<span class="hljs-string">'glob'</span>) <span class="hljs-keyword">const</span> { resolve } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>) <span class="hljs-comment">// webpack 入口文件</span> <span class="hljs-keyword">const</span> entry = <span class="hljs-function">(<span class="hljs-params">(filepathList</span>) =></span> { <span class="hljs-keyword">let</span> entry = {} filepathList.forEach(<span class="hljs-function"><span class="hljs-params">filepath</span> =></span> { <span class="hljs-keyword">const</span> list = filepath.split(<span class="hljs-regexp">/[\/|\/\/|\\|\\\\]/g</span>) <span class="hljs-comment">// 斜杠分割文件目录</span> <span class="hljs-keyword">const</span> key = list[list.length - <span class="hljs-number">1</span>].replace(<span class="hljs-regexp">/\.js/g</span>, <span class="hljs-string">''</span>) <span class="hljs-comment">// 拿到文件的 filename</span> <span class="hljs-comment">// 如果是开发环境,才需要引入 hot module</span> entry[key] = process.env.NODE_ENV === <span class="hljs-string">'development'</span> ? [filepath, <span class="hljs-string">'webpack-hot-middleware/client?reload=true'</span>] : filepath }) <span class="hljs-keyword">return</span> entry })(glob.sync(resolve(__dirname, <span class="hljs-string">'../src/js/*.js'</span>))) <span class="hljs-built_in">module</span>.exports = { entry, ... } <span class="copy-code-btn">复制代码</span></code></pre> <p>HTMLWebpackPlugin 的配置也同理:</p> <pre><code class="hljs js copyable"><span class="hljs-comment">// webpack.base.config.js 部分代码</span> ... plugins: [ <span class="hljs-comment">// 打包文件</span> ...glob.sync(resolve(__dirname, <span class="hljs-string">'../src/tpls/*.ejs'</span>)).map(<span class="hljs-function">(<span class="hljs-params">filepath, i</span>) =></span> { <span class="hljs-keyword">const</span> tempList = filepath.split(<span class="hljs-regexp">/[\/|\/\/|\\|\\\\]/g</span>) <span class="hljs-comment">// 斜杠分割文件目录</span> <span class="hljs-keyword">const</span> filename = <span class="hljs-string">`views/<span class="hljs-subst">${tempList[tempList.length - <span class="hljs-number">1</span>]}</span>`</span> <span class="hljs-comment">// 拿到文件的 filename</span> <span class="hljs-keyword">const</span> template = filepath <span class="hljs-comment">// 指定模板地址为对应的 ejs 视图文件路径</span> <span class="hljs-keyword">const</span> fileChunk = filename.split(<span class="hljs-string">'.'</span>)[<span class="hljs-number">0</span>].split(<span class="hljs-regexp">/[\/|\/\/|\\|\\\\]/g</span>).pop() <span class="hljs-comment">// 获取到对应视图文件的 chunkname</span> <span class="hljs-keyword">const</span> chunks = [<span class="hljs-string">'manifest'</span>, <span class="hljs-string">'vendors'</span>, fileChunk] <span class="hljs-comment">// 组装 chunks 数组</span> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> HtmlWebpackPlugin({ filename, template, chunks }) <span class="hljs-comment">// 返回 HtmlWebpackPlugin 实例</span> }) ] ... <span class="copy-code-btn">复制代码</span></code></pre> <p>编写好 <code>webpack.base.config.js</code> 文件,根据自己项目需求编写好 <code>webpack.dev.config.js</code> 和 <code>webpack.prod.config.js</code>,使用 webpack-merge 将基础配置和对应环境下的配置合并。</p> <blockquote> <p>webpack 其他的一些细节配置大家可以参考 webpack 中文网址</p> </blockquote> <h2 class="heading">服务端</h2> <p>打包脚本编写完成,我们开始编写服务,我们使用 <code>express</code> 来搭建服务。<span style="color:#5484c1;font-size:14px;">(由于是工程架构演示,所以这个服务暂不涉及任何的数据库的增删改查,只是包含基本的路由跳转)</span></p> <p><code>server</code> 简单的结构如下: </p> <p></p> <h4 class="heading">服务端启动文件</h4> <p><code>bin/server.js</code> 启动文件,作为服务的入口,需要同时启动本地服务和 webpack 的开发时编译。一般项目 <code>webpack-dev-server</code> 是写在 <code>package.json</code> 里的,当你运行 <code>npm run dev</code> 的时候,就在使用 <code>webpack-dev-server</code> 启动开发服务,这个 webpack-dev-server 功能十分强大,不仅能一键启动本地服务,还可以监听模块,实时编译。这边我们使用 <code>express</code> + webpack-dev-middleware 也可以达到同样的功能。</p> <p style="color:#FFFFFF;">webpack-dev-middleware 可以理解为一个抽离出来的 webpack-dev-server,只是没有启动本地服务的功能,以及使用方式上略有改变。它相比于 webpack-dev-server 的灵活性在于,它以一个中间件的形式存在,允许开发者编写自己的服务来使用它。</p> <blockquote> <p>其实 webpack-dev-server 的内部实现机制也是借助于 webpack-dev-middleware 和 express 有兴趣的朋友可以去看一下。</p> </blockquote> <p>以下是服务入口文件的部分代码</p> <pre><code class="hljs js copyable"><span class="hljs-comment">// server/bin/server.js 文件代码</span> <span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>) <span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>) <span class="hljs-keyword">const</span> webpack = <span class="hljs-built_in">require</span>(<span class="hljs-string">'webpack'</span>) <span class="hljs-keyword">const</span> webpackDevMiddleware = <span class="hljs-built_in">require</span>(<span class="hljs-string">'webpack-dev-middleware'</span>) <span class="hljs-keyword">const</span> webpackHotMiddleware = <span class="hljs-built_in">require</span>(<span class="hljs-string">'webpack-hot-middleware'</span>) <span class="hljs-keyword">const</span> { routerFactory } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../routes'</span>) <span class="hljs-keyword">const</span> isDev = process.env.NODE_ENV === <span class="hljs-string">'development'</span> <span class="hljs-keyword">let</span> app = express() <span class="hljs-keyword">let</span> webpackConfig = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../../build/webpack.dev.config'</span>) <span class="hljs-keyword">let</span> compiler = webpack(webpackConfig) <span class="hljs-comment">// 开发环境下才需要启用实时编译和热更新</span> <span class="hljs-keyword">if</span> (isDev) { <span class="hljs-comment">// 用 webpack-dev-middleware 启动 webpack 编译</span> app.use(webpackDevMiddleware(compiler, { <span class="hljs-attr">publicPath</span>: webpackConfig.output.publicPath, <span class="hljs-attr">overlay</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">hot</span>: <span class="hljs-literal">true</span> })) <span class="hljs-comment">// 使用 webpack-hot-middleware 支持热更新</span> app.use(webpackHotMiddleware(compiler, { <span class="hljs-attr">publicPath</span>: webpackConfig.output.publicPath, <span class="hljs-attr">noInfo</span>: <span class="hljs-literal">true</span> })) } <span class="hljs-comment">// 添加静态资源拦截转发</span> app.use(webpackConfig.output.publicPath, express.static(path.resolve(__dirname, isDev ? <span class="hljs-string">'../../src'</span> : <span class="hljs-string">'../../dist'</span>))) <span class="hljs-comment">// 构造路由</span> routerFactory(app) <span class="hljs-comment">// 错误处理</span> app.use(<span class="hljs-function">(<span class="hljs-params">err, req, res, next</span>) =></span> { res.status(err.status || <span class="hljs-number">500</span>) res.send(err.stack || <span class="hljs-string">'Service Error'</span>) }) app.listen(port, () => <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`development is listening on port 8888`</span>)) <span class="copy-code-btn">复制代码</span></code></pre> <h4 class="heading">服务端路由</h4> <p style="color:#FFFFFF;">路由的跳转方式,属于整个工程中非常重要的一步。不知道阅读文章的朋友有没有疑问,本地的视图文件是 .ejs 后缀结尾的文件,浏览器只能识别 .html 后缀文件,这块视图数据的渲染是怎么做的? webpack-dev-middleware 打包出来的资源都是存在内存中的,存储在内存中的资源文件,服务端要怎么获取?</p>先来看具体的路由代码,此处以首页路由作为演示 <pre><code class="hljs js copyable"><span class="hljs-comment">// server/routs/home.js 文件</span> <span class="hljs-keyword">const</span> ejs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'ejs'</span>) <span class="hljs-keyword">const</span> { getTemplate } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../common/utils'</span>) <span class="hljs-keyword">const</span> homeRoute = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">app</span>) </span>{ app.get(<span class="hljs-string">'/'</span>, <span class="hljs-keyword">async</span> (req, res, next) => { <span class="hljs-keyword">try</span> { <span class="hljs-keyword">const</span> template = <span class="hljs-keyword">await</span> getTemplate(<span class="hljs-string">'index.ejs'</span>) <span class="hljs-comment">// 获取 ejs 模板文件</span> <span class="hljs-keyword">let</span> html = ejs.render(template, { <span class="hljs-attr">title</span>: <span class="hljs-string">'首页'</span> }) res.send(html) } <span class="hljs-keyword">catch</span> (e) { next(e) } }) app.get(<span class="hljs-string">'/home'</span>, <span class="hljs-keyword">async</span> (req, res, next) => { <span class="hljs-keyword">try</span> { <span class="hljs-keyword">const</span> template = <span class="hljs-keyword">await</span> getTemplate(<span class="hljs-string">'index.ejs'</span>) <span class="hljs-comment">// 获取 ejs 模板文件</span> <span class="hljs-keyword">let</span> html = ejs.render(template, { <span class="hljs-attr">title</span>: <span class="hljs-string">'首页'</span> }) res.send(html) } <span class="hljs-keyword">catch</span> (e) { next(e) } }) } <span class="hljs-built_in">module</span>.exports = homeRoute <span class="copy-code-btn">复制代码</span></code></pre> <p>可以看到关键点就在 getTemplate 这个方法,我们看看这个 <code>getTemplate</code> 做了咩</p> <pre><code class="hljs js copyable"><span class="hljs-comment">// server/common/utils.js 文件</span> <span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">'axios'</span>) <span class="hljs-keyword">const</span> CONFIG = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../../build/config'</span>) <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getTemplate</span> (<span class="hljs-params">filename</span>) </span>{ <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =></span> { axios.get(<span class="hljs-string">`http://localhost:8888/public/views/<span class="hljs-subst">${filename}</span>`</span>) <span class="hljs-comment">// 注意这个 'public' 公共资源前缀非常重要</span> .then(<span class="hljs-function"><span class="hljs-params">res</span> =></span> { resolve(res.data) }) .catch(reject) }) } <span class="hljs-built_in">module</span>.exports = { getTemplate } <span class="copy-code-btn">复制代码</span></code></pre> <p style="color:#FFFFFF;">从上面代码可以看到,路由中的做的非常重要的事情,就是直接用对应视图的 ejs 文件名,去请求自身服务,从而获取到存在 webpack 缓存中的资源和数据。<br>通过这种方式拿到模板字符串后,ejs 引擎会用数据渲染对应变量,最终以 html 字符串的形式返回到浏览器进行渲染。<br>本地服务会以一个 publicPath 路径前缀来标记静态资源请求,如果服务接受到的请求是带有 publicPath 前缀,就会被 `/bin/server.js` 中的静态资源中间件拦截到,映射到对应资源目录,返回静态资源,而这个 publicPath 就是 webpack 配置中的 <span style="color:#333;">output.publicPath</span></p> <blockquote> <p>关于 webpack 的打包时缓存,我之前翻了很多地方都没有找到很好的文档和操作工具,这边给大家推荐两个链接</p> <ol> <li>Webpack Custom File Systems (webpack 自定义文件系统官方说明)</li> <li>memory-fs(获取 webpack 编译到内存中的数据)</li> </ol> </blockquote> <h2 class="heading">客户端</h2> <p>完成了服务端渲染、webpack 构建配置后,算是搞定了 80% 的工作量,还有一些小细节需要注意,不然服务启动起来还是会报错。</p> <h4 class="heading">webpack 编译时的坑</h4> <p style="color:#FFFFFF;">这个坑就埋在客户端的视图文件里,先来看看坑是什么:当我们使用 ejs 语法(<span style="color:#333;"><%= title %></span>)这种语法的时候,webpack 编译就会报错,说是 title is undefined </p> <p>要解决这个问题,需要首先明白 webpack 编译时的运行机制,它做了什么。我们知道,webpack 内部模板机制就是基于的 ejs,所以在我们服务端渲染之前,也就是 webpack 的编译阶段,已经执行过了一次 ejs.render 了,这个时候,在 webpack 的配置文件里,我们是没有传递过 title 这个变量的,所以编译会报错。那么要怎么写才能识别呢?答案就在 ejs 的官方文档 </p> <p></p> <p style="color:#FFFFFF;">从官网的介绍上可以看出,当我们使用 <span style="color:#333;"><%%</span> 打头的时候,会被转义成 <span style="color:#333;"><%</span> 字符串,类似于 html 标签的转义,这样才能避免 webpack 中自带的 ejs 的错误识别,生成正确的 ejs 文件。所以以变量为例,在代码中我们需要这样写: <span style="color:#333;"><%%= title %></span> <br>这样,webpack 才能顺利编译完成,将 compiler 继续传递到 ejs-html-loader 这里</p> <h4 class="heading">使用 html-loader 识别图片资源</h4> <p>如果了解 <code>html-loader</code> 的朋友就知道,在项目中,我们之所以能够在 html 中方便的写 <code><img src="../static/imgs/XXX.png"></code> 这种图片格式,还能被 webpack 正确识别,离不开 html-loader 里的 <code>attrs</code> 配置项, 但是在 ejs-html-loader 里,没有提供这种方便的功能,所以我们依旧要使用 <code>html-loader</code> 来对 html 中的图片引用做处理,这边需要注意 loader 的配置顺序</p> <pre><code class="hljs js copyable"><span class="hljs-comment">// webpack.base.config.js 部分代码</span> <span class="hljs-built_in">module</span>: { <span class="hljs-attr">rules</span>: [ ... { <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.ejs$/</span>, <span class="hljs-attr">use</span>: [ { <span class="hljs-attr">loader</span>: <span class="hljs-string">'html-loader'</span>, <span class="hljs-comment">// 使用 html-loader 处理图片资源的引用</span> options: { <span class="hljs-attr">attrs</span>: [<span class="hljs-string">'img:src'</span>, <span class="hljs-string">'img:data-src'</span>] } }, { <span class="hljs-attr">loader</span>: <span class="hljs-string">'ejs-html-loader'</span>, <span class="hljs-comment">// 使用 ejs-html-loader 处理 .ejs 文件的 includes 语法</span> options: { <span class="hljs-attr">production</span>: process.env.ENV === <span class="hljs-string">'production'</span> } } ] } ... ] } <span class="copy-code-btn">复制代码</span></code></pre> <h2 class="heading">配置热更新</h2> <p>接下来是配置热更新,使用 <code>webpack-dev-middleware</code> 时的热更新配置方式和 <code>webpack-dev-server</code> 略有不同,但是 <code>webpack-dev-middleware</code> 稍微简单一点。webpack 打包多页应用配置热更新,一共四步:</p> <ol> <li>在 <code>entry</code> 入口里多写一个 <code>webpack-hot-middleware/client?reload=true</code> 的入口文件</li> </ol> <pre><code class="hljs js copyable"><span class="hljs-comment">// webpack.base.config.js 部分代码</span> <span class="hljs-comment">// webpack 入口文件</span> <span class="hljs-keyword">const</span> entry = <span class="hljs-function">(<span class="hljs-params">(filepathList</span>) =></span> { <span class="hljs-keyword">let</span> entry = {} filepathList.forEach(<span class="hljs-function"><span class="hljs-params">filepath</span> =></span> { ... <span class="hljs-comment">// 如果是开发环境,才需要引入 hot module</span> entry[key] = process.env.NODE_ENV === <span class="hljs-string">'development'</span> ? [filepath, <span class="hljs-string">'webpack-hot-middleware/client?reload=true'</span>] : filepath ... }) <span class="hljs-keyword">return</span> entry })(...) <span class="hljs-built_in">module</span>.exports = { entry, ... } <span class="copy-code-btn">复制代码</span></code></pre> <ol start="2"> <li>在 webpack 的 <code>plugins</code> 里多写三个 plugin:<pre><code class="hljs js copyable"><span class="hljs-comment">// webpack.dev.config.js 文件部分代码</span> plugins: [ ... <span class="hljs-comment">// OccurrenceOrderPlugin is needed for webpack 1.x only</span> <span class="hljs-keyword">new</span> Webpack.optimize.OccurrenceOrderPlugin(), <span class="hljs-keyword">new</span> Webpack.HotModuleReplacementPlugin(), <span class="hljs-comment">// Use NoErrorsPlugin for webpack 1.x</span> <span class="hljs-keyword">new</span> Webpack.NoEmitOnErrorsPlugin() ... ] <span class="copy-code-btn">复制代码</span></code></pre></li> <li>在 <code>bin/server.js</code> 服务入口中引入 <code>webpack-hot-middleware</code>, 并将 <code>webpack-dev-server</code> 打包完成的 <code>compiler</code> 用 <code>webpack-hot-middleware</code> 包装起来:<pre><code class="hljs js copyable"><span class="hljs-comment">// server/bin/server.js 文件</span> <span class="hljs-keyword">let</span> compiler = webpack(webpackConfig) <span class="hljs-comment">// 用 webpack-dev-middleware 启动 webpack 编译</span> app.use(webpackDevMiddleware(compiler, { <span class="hljs-attr">publicPath</span>: webpackConfig.output.publicPath, <span class="hljs-attr">overlay</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">hot</span>: <span class="hljs-literal">true</span> })) <span class="hljs-comment">// 使用 webpack-hot-middleware 支持热更新</span> app.use(webpackHotMiddleware(compiler, { <span class="hljs-attr">publicPath</span>: webpackConfig.output.publicPath, <span class="hljs-attr">reload</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">noInfo</span>: <span class="hljs-literal">true</span> })) <span class="copy-code-btn">复制代码</span></code></pre></li> <li>在视图对应的 js 文件里加一段代码:<pre><code class="hljs js copyable"><span class="hljs-comment">// src/js/index.js 文件</span> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">module</span>.hot) { <span class="hljs-built_in">module</span>.hot.accept() } <span class="copy-code-btn">复制代码</span></code></pre></li> </ol> <p>关于 webpack-hot-middleware 的更多配置细节,请看文档</p> <p style="color:#FFFFFF;">这边需要注意的是:<br>1. 光是这么写的话,webpack hot module 只能支持 JS 部分的修改,如果需要支持样式文件( css / less / sass ... )的 hot reload ,就不能使用 extract-text-webpack-plugin 将样式文件剥离出去,否则无法监听修改、实时刷新。<br><br>2. webpack hot module 原生是不支持 html 的热替换的,但是很多开发者对于这块的需求比较大,于是我找了一个相对比较简单的方法,来支持视图文件的热更新</p> 需要在原有的代码做一点修改,先来看代码: <pre><code class="hljs js copyable"><span class="hljs-comment">// src/js/index.js 文件</span> <span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span> <span class="hljs-comment">// styles</span> <span class="hljs-keyword">import</span> <span class="hljs-string">'less/index.less'</span> <span class="hljs-keyword">const</span> isDev = process.env.NODE_ENV === <span class="hljs-string">'development'</span> <span class="hljs-comment">// 在开发环境下,使用 raw-loader 引入 ejs 模板文件,强制 webpack 将其视为需要热更新的一部分 bundle</span> <span class="hljs-keyword">if</span> (isDev) { <span class="hljs-built_in">require</span>(<span class="hljs-string">'raw-loader!../tpls/index.ejs'</span>) } ... if (<span class="hljs-built_in">module</span>.hot) { <span class="hljs-built_in">module</span>.hot.accept() <span class="hljs-comment">/** * 监听 hot module 完成事件,重新从服务端获取模板,替换掉原来的 document * 这种热更新方式需要注意: * 1. 如果你在元素上之前绑定了事件,那么热更新之后,这些事件可能会失效 * 2. 如果事件在模块卸载之前未销毁,可能会导致内存泄漏 */</span> <span class="hljs-built_in">module</span>.hot.dispose(<span class="hljs-function"><span class="hljs-params">()</span> =></span> { <span class="hljs-keyword">const</span> href = <span class="hljs-built_in">window</span>.location.href axios.get(href).then(<span class="hljs-function"><span class="hljs-params">res</span> =></span> { <span class="hljs-keyword">const</span> template = res.data <span class="hljs-built_in">document</span>.body.innerHTML = template }).catch(<span class="hljs-function"><span class="hljs-params">e</span> =></span> { <span class="hljs-built_in">console</span>.error(e) }) }) } <span class="copy-code-btn">复制代码</span></code></pre> <pre><code class="hljs js copyable"><span class="hljs-comment">// webpack.dev.config.js</span> plugins: [ ... new webpack.DefinePlugin({ <span class="hljs-string">'process.env.NODE_ENV'</span>: <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">'development'</span>) }) ... ] <span class="copy-code-btn">复制代码</span></code></pre> <pre><code class="hljs js copyable"><span class="hljs-comment">// webpack.prod.config.js</span> plugins: [ ... new webpack.DefinePlugin({ <span class="hljs-string">'process.env.NODE_ENV'</span>: <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-string">'production'</span>) }) ... ] <span class="copy-code-btn">复制代码</span></code></pre> <p>OK,如你所愿,现在视图文件也支持热更新啦。??</p> <p><code>webpack-hot-middleware</code> 默认继承了 <code>overlay</code>,所以当热更新配置完成以后,<code>overlay</code> 报错功能也能正常使用了</p> <p></p> <p></p> <h2 class="heading">package.json 启动脚本</h2> <p>最后来看一下 <code>package.json</code> 里的启动脚本,这边没啥难度,就直接上代码了</p> <pre><code class="hljs json copyable"> "scripts": { "clear": "rimraf dist", "server": "cross-env NODE_ENV=production node ./server/bin/server.js", "dev": "cross-env NODE_ENV=development nodemon --watch server ./server/bin/server.js", "build": "npm run clear && cross-env NODE_ENV=production webpack --env production --config ./build/webpack.prod.config.js", "test": "echo \"Error: no test specified\" && exit 1" } <span class="copy-code-btn">复制代码</span></code></pre> <p>当客户端代码变动时 webpack 会自动帮我们编译重启,但是服务端的代码变动却不会实时刷新,这时需要用到 <code>nodemon</code>,设置好监听目录以后,服务端的任何代码修改就能被 <code>nodemon</code> 监听,服务自动重启,非常方便。</p> <p style="color:#FFFFFF;">这边也有一个小细节需要注意,nodemon --watch 最好指定监听服务端文件夹,因为毕竟只有服务端的代码修改才需要重启服务,不然默认监听整个根目录,写个样式都能重启服务,简直要把人烦死。</p> <h2 class="heading">总结</h2> <p>项目整体搭完后再回头看,还是有不少需要注意和值得学习的地方。虽然踩了不少坑,但也对其中的一些原理有了更深入的了解。</p> <p>得益于前端脚手架工具,让我们能在大部分项目中一键生成项目的基础配置,免去了很多工程搭建的烦恼,但这种方便在造福了开发者的同时,却也弱化了前端工程师的工程架构能力。现实中总有一些脚手架工具没办法的触及到的业务场景,这时就需要开发者主动寻求解决方案,甚至自己动手构建工程,以获得开发的最佳灵活性。</p> <p>完整项目地址可以查看我的 <font style="font-size:20px;">GitHub </font>,喜欢的话给个 Star⭐️ ,多谢多谢~??</p> </div> </div> </div> </div> </div> </div> <!--PC和WAP自适应版--> <div id="SOHUCS" sid="1280805328459415552"></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">你可能感兴趣的:(【实战】webpack4 + ejs + express 带你撸一个多页应用项目架构)</h4> <div id="paradigm-article-related"> <div class="recommend-post mb30"> <ul class="widget-links"> <li><a href="/article/1835514462770130944.htm" title="斤斤计较的婚姻到底有多难?" target="_blank">斤斤计较的婚姻到底有多难?</a> <span class="text-muted">白心之岂必有为</span> <div>很多人私聊我会问到在哪个人群当中斤斤计较的人最多?我都会回答他,一般婚姻出现问题的斤斤计较的人士会非常多,以我多年经验,在婚姻落的一塌糊涂的人当中,斤斤计较的人数占比在20~30%以上,也就是说10个婚姻出现问题的斤斤计较的人有2-3个有多不减。在婚姻出问题当中,有大量的心理不平衡的、尖酸刻薄的怨妇。在婚姻中仅斤斤计较有两种类型:第一种是物质上的,另一种是精神上的。在物质与精神上抠门已经严重的影响</div> </li> <li><a href="/article/1835514335561084928.htm" title="芦花鞋一四" target="_blank">芦花鞋一四</a> <span class="text-muted">许叶晗</span> <div>又是在一个寒冷的夏日里,青铜和葵花决定今天一起去卖芦花鞋,奶奶亲手给他们做了一碗热乎乎的粥对他们说:“就靠你们两挣生活费了这碗粥赶紧趁热喝了吧!”于是青铜和葵花喝完了奶奶给她们做的粥,就准备去镇上卖卢花鞋,这回青铜和葵花穿着新的芦花鞋来到了镇上。青铜这回看到了很多人都在卖,用手势表达对葵花说:“这回有好多人在抢我们生意呢!我们必须得吆喝起来。”葵花点了点头。可是谁知他们也大声的叫,卖芦花喽!卖芦花</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/1835509770287673344.htm" title="swagger访问路径" target="_blank">swagger访问路径</a> <span class="text-muted">igotyback</span> <a class="tag" taget="_blank" href="/search/swagger/1.htm">swagger</a> <div>Swagger2.x版本访问地址:http://{ip}:{port}/{context-path}/swagger-ui.html{ip}是你的服务器IP地址。{port}是你的应用服务端口,通常为8080。{context-path}是你的应用上下文路径,如果应用部署在根路径下,则为空。Swagger3.x版本对于Swagger3.x版本(也称为OpenAPI3)访问地址:http://{ip</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/1835508761310097408.htm" title="店群合一模式下的社区团购新发展——结合链动 2+1 模式、AI 智能名片与 S2B2C 商城小程序源码" target="_blank">店群合一模式下的社区团购新发展——结合链动 2+1 模式、AI 智能名片与 S2B2C 商城小程序源码</a> <span class="text-muted">说私域</span> <a class="tag" taget="_blank" href="/search/%E4%BA%BA%E5%B7%A5%E6%99%BA%E8%83%BD/1.htm">人工智能</a><a class="tag" taget="_blank" href="/search/%E5%B0%8F%E7%A8%8B%E5%BA%8F/1.htm">小程序</a> <div>摘要:本文探讨了店群合一的社区团购平台在当今商业环境中的重要性和优势。通过分析店群合一模式如何将互联网社群与线下终端紧密结合,阐述了链动2+1模式、AI智能名片和S2B2C商城小程序源码在这一模式中的应用价值。这些创新元素的结合为社区团购带来了新的机遇,提升了用户信任感、拓展了营销渠道,并实现了线上线下的完美融合。一、引言随着互联网技术的不断发展,社区团购作为一种新兴的商业模式,在满足消费者日常需</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/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/1835507612548952064.htm" title="我的烦恼" target="_blank">我的烦恼</a> <span class="text-muted">余建梅</span> <div>我的烦恼。女儿问我:“你给学生布置什么作文题目?”“《我的烦恼》。”“他们都这么大了,你觉得他们还有烦恼吗?”“有啊!每个人都会有自己烦恼。”“我不相信,大人是没有烦恼的,如果说一定有的话,你的烦恼和我写作业有关,而且是小烦恼。不像我,天天被你说,有这样的妈妈,烦恼是没完没了。”女儿愤愤不平。每个人都会有自己的烦恼,处在上有老下有小的年纪,烦恼多的数不完。想干好工作带好孩子,想孝顺父母又想经营好自</div> </li> <li><a href="/article/122.htm" title="java数字签名三种方式" target="_blank">java数字签名三种方式</a> <span class="text-muted">知了ing</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/jdk/1.htm">jdk</a> <div>以下3钟数字签名都是基于jdk7的 1,RSA String password="test"; // 1.初始化密钥 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(51</div> </li> <li><a href="/article/249.htm" title="Hibernate学习笔记" target="_blank">Hibernate学习笔记</a> <span class="text-muted">caoyong</span> <a class="tag" taget="_blank" href="/search/Hibernate/1.htm">Hibernate</a> <div>1>、Hibernate是数据访问层框架,是一个ORM(Object Relation Mapping)框架,作者为:Gavin King 2>、搭建Hibernate的开发环境      a>、添加jar包:      aa>、hibernatte开发包中/lib/required/所</div> </li> <li><a href="/article/376.htm" title="设计模式之装饰器模式Decorator(结构型)" target="_blank">设计模式之装饰器模式Decorator(结构型)</a> <span class="text-muted">漂泊一剑客</span> <a class="tag" taget="_blank" href="/search/Decorator/1.htm">Decorator</a> <div>1. 概述        若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性。如果已经存在的一个类缺少某些方法,或者须要给方法添加更多的功能(魅力),你也许会仅仅继承这个类来产生一个新类—这建立在额外的代码上。       </div> </li> <li><a href="/article/503.htm" title="读取磁盘文件txt,并输入String" target="_blank">读取磁盘文件txt,并输入String</a> <span class="text-muted">一炮送你回车库</span> <a class="tag" taget="_blank" href="/search/String/1.htm">String</a> <div>public static void main(String[] args) throws IOException {    String fileContent = readFileContent("d:/aaa.txt");    System.out.println(fileContent);    </div> </li> <li><a href="/article/630.htm" title="js三级联动下拉框" target="_blank">js三级联动下拉框</a> <span class="text-muted">3213213333332132</span> <a class="tag" taget="_blank" href="/search/%E4%B8%89%E7%BA%A7%E8%81%94%E5%8A%A8/1.htm">三级联动</a> <div> //三级联动 省/直辖市<select id="province"></select> 市/省直辖<select id="city"></select> 县/区 <select id="area"></select> </div> </li> <li><a href="/article/757.htm" title="erlang之parse_transform编译选项的应用" target="_blank">erlang之parse_transform编译选项的应用</a> <span class="text-muted">616050468</span> <a class="tag" taget="_blank" href="/search/parse_transform/1.htm">parse_transform</a><a class="tag" taget="_blank" href="/search/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E5%99%A8/1.htm">游戏服务器</a><a class="tag" taget="_blank" href="/search/%E5%B1%9E%E6%80%A7%E5%90%8C%E6%AD%A5/1.htm">属性同步</a><a class="tag" taget="_blank" href="/search/abstract_code/1.htm">abstract_code</a> <div>         最近使用erlang重构了游戏服务器的所有代码,之前看过C++/lua写的服务器引擎代码,引擎实现了玩家属性自动同步给前端和增量更新玩家数据到数据库的功能,这也是现在很多游戏服务器的优化方向,在引擎层面去解决数据同步和数据持久化,数据发生变化了业务层不需要关心怎么去同步给前端。由于游戏过程中玩家每个业务中玩家数据更改的量其实是很少</div> </li> <li><a href="/article/884.htm" title="JAVA JSON的解析" target="_blank">JAVA JSON的解析</a> <span class="text-muted">darkranger</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a> <div> // { // “Total”:“条数”, // Code: 1, // // “PaymentItems”:[ // { // “PaymentItemID”:”支款单ID”, // “PaymentCode”:”支款单编号”, // “PaymentTime”:”支款日期”, // ”ContractNo”:”合同号”, // </div> </li> <li><a href="/article/1011.htm" title="POJ-1273-Drainage Ditches" target="_blank">POJ-1273-Drainage Ditches</a> <span class="text-muted">aijuans</span> <a class="tag" taget="_blank" href="/search/ACM_POJ/1.htm">ACM_POJ</a> <div>POJ-1273-Drainage Ditches http://poj.org/problem?id=1273 基本的最大流,按LRJ的白书写的 #include<iostream> #include<cstring> #include<queue> using namespace std; #define INF 0x7fffffff int ma</div> </li> <li><a href="/article/1138.htm" title="工作流Activiti5表的命名及含义" target="_blank">工作流Activiti5表的命名及含义</a> <span class="text-muted">atongyeye</span> <a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C%E6%B5%81/1.htm">工作流</a><a class="tag" taget="_blank" href="/search/Activiti/1.htm">Activiti</a> <div>activiti5 - http://activiti.org/designer/update在线插件安装 activiti5一共23张表 Activiti的表都以ACT_开头。 第二部分是表示表的用途的两个字母标识。 用途也和服务的API对应。 ACT_RE_*: 'RE'表示repository。 这个前缀的表包含了流程定义和流程静态资源 (图片,规则,等等)。 A</div> </li> <li><a href="/article/1265.htm" title="android的广播机制和广播的简单使用" target="_blank">android的广播机制和广播的简单使用</a> <span class="text-muted">百合不是茶</span> <a class="tag" taget="_blank" href="/search/android/1.htm">android</a><a class="tag" taget="_blank" href="/search/%E5%B9%BF%E6%92%AD%E6%9C%BA%E5%88%B6/1.htm">广播机制</a><a class="tag" taget="_blank" href="/search/%E5%B9%BF%E6%92%AD%E7%9A%84%E6%B3%A8%E5%86%8C/1.htm">广播的注册</a> <div>      Android广播机制简介 在Android中,有一些操作完成以后,会发送广播,比如说发出一条短信,或打出一个电话,如果某个程序接收了这个广播,就会做相应的处理。这个广播跟我们传统意义中的电台广播有些相似之处。之所以叫做广播,就是因为它只负责“说”而不管你“听不听”,也就是不管你接收方如何处理。另外,广播可以被不只一个应用程序所接收,当然也可能不被任何应</div> </li> <li><a href="/article/1392.htm" title="Spring事务传播行为详解" target="_blank">Spring事务传播行为详解</a> <span class="text-muted">bijian1013</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%E4%BA%8B%E5%8A%A1%E4%BC%A0%E6%92%AD%E8%A1%8C%E4%B8%BA/1.htm">事务传播行为</a> <div>        在service类前加上@Transactional,声明这个service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。         Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这</div> </li> <li><a href="/article/1519.htm" title="eidtplus operate" target="_blank">eidtplus operate</a> <span class="text-muted">征客丶</span> <a class="tag" taget="_blank" href="/search/eidtplus/1.htm">eidtplus</a> <div>开启列模式: Alt+C 鼠标选择   OR   Alt+鼠标左键拖动 列模式替换或复制内容(多行): 右键-->格式-->填充所选内容-->选择相应操作 OR Ctrl+Shift+V(复制多行数据,必须行数一致) -------------------------------------------------------</div> </li> <li><a href="/article/1646.htm" title="【Kafka一】Kafka入门" target="_blank">【Kafka一】Kafka入门</a> <span class="text-muted">bit1129</span> <a class="tag" taget="_blank" href="/search/kafka/1.htm">kafka</a> <div>这篇文章来自Spark集成Kafka(http://bit1129.iteye.com/blog/2174765),这里把它单独取出来,作为Kafka的入门吧   下载Kafka http://mirror.bit.edu.cn/apache/kafka/0.8.1.1/kafka_2.10-0.8.1.1.tgz 2.10表示Scala的版本,而0.8.1.1表示Kafka</div> </li> <li><a href="/article/1773.htm" title="Spring 事务实现机制" target="_blank">Spring 事务实现机制</a> <span class="text-muted">BlueSkator</span> <a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%E4%BB%A3%E7%90%86/1.htm">代理</a><a class="tag" taget="_blank" href="/search/%E4%BA%8B%E5%8A%A1/1.htm">事务</a> <div>Spring是以代理的方式实现对事务的管理。我们在Action中所使用的Service对象,其实是代理对象的实例,并不是我们所写的Service对象实例。既然是两个不同的对象,那为什么我们在Action中可以象使用Service对象一样的使用代理对象呢?为了说明问题,假设有个Service类叫AService,它的Spring事务代理类为AProxyService,AService实现了一个接口 </div> </li> <li><a href="/article/1900.htm" title="bootstrap源码学习与示例:bootstrap-dropdown(转帖)" target="_blank">bootstrap源码学习与示例:bootstrap-dropdown(转帖)</a> <span class="text-muted">BreakingBad</span> <a class="tag" taget="_blank" href="/search/bootstrap/1.htm">bootstrap</a><a class="tag" taget="_blank" href="/search/dropdown/1.htm">dropdown</a> <div>bootstrap-dropdown组件是个烂东西,我读后的整体感觉。 一个下拉开菜单的设计: <ul class="nav pull-right"> <li id="fat-menu" class="dropdown"> </div> </li> <li><a href="/article/2027.htm" title="读《研磨设计模式》-代码笔记-中介者模式-Mediator" target="_blank">读《研磨设计模式》-代码笔记-中介者模式-Mediator</a> <span class="text-muted">bylijinnan</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/1.htm">设计模式</a> <div>声明: 本文只为方便我个人查阅和理解,详细的分析以及源代码请移步 原作者的博客http://chjavach.iteye.com/ /* * 中介者模式(Mediator):用一个中介对象来封装一系列的对象交互。 * 中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 * * 在我看来,Mediator模式是把多个对象(</div> </li> <li><a href="/article/2154.htm" title="常用代码记录" target="_blank">常用代码记录</a> <span class="text-muted">chenjunt3</span> <a class="tag" taget="_blank" href="/search/UI/1.htm">UI</a><a class="tag" taget="_blank" href="/search/Excel/1.htm">Excel</a><a class="tag" taget="_blank" href="/search/J%23/1.htm">J#</a> <div>  1、单据设置某行或某字段不能修改 //i是行号,"cash"是字段名称 getBillCardPanelWrapper().getBillCardPanel().getBillModel().setCellEditable(i, "cash", false); //取得单据表体所有项用以上语句做循环就能设置整行了 getBillC</div> </li> <li><a href="/article/2281.htm" title="搜索引擎与工作流引擎" target="_blank">搜索引擎与工作流引擎</a> <span class="text-muted">comsci</span> <a class="tag" taget="_blank" href="/search/%E7%AE%97%E6%B3%95/1.htm">算法</a><a class="tag" taget="_blank" href="/search/%E5%B7%A5%E4%BD%9C/1.htm">工作</a><a class="tag" taget="_blank" href="/search/%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/1.htm">搜索引擎</a><a class="tag" taget="_blank" href="/search/%E7%BD%91%E7%BB%9C%E5%BA%94%E7%94%A8/1.htm">网络应用</a> <div>      最近在公司做和搜索有关的工作,(只是简单的应用开源工具集成到自己的产品中)工作流系统的进一步设计暂时放在一边了,偶然看到谷歌的研究员吴军写的数学之美系列中的搜索引擎与图论这篇文章中的介绍,我发现这样一个关系(仅仅是猜想)   -----搜索引擎和流程引擎的基础--都是图论,至少像在我在JWFD中引擎算法中用到的是自定义的广度优先</div> </li> <li><a href="/article/2408.htm" title="oracle Health Monitor" target="_blank">oracle Health Monitor</a> <span class="text-muted">daizj</span> <a class="tag" taget="_blank" href="/search/oracle/1.htm">oracle</a><a class="tag" taget="_blank" href="/search/Health+Monitor/1.htm">Health Monitor</a> <div>About Health Monitor Beginning with Release 11g, Oracle Database includes a framework called Health Monitor for running diagnostic checks on the database. About Health Monitor Checks Health M</div> </li> <li><a href="/article/2535.htm" title="JSON字符串转换为对象" target="_blank">JSON字符串转换为对象</a> <span class="text-muted">dieslrae</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/json/1.htm">json</a> <div>    作为前言,首先是要吐槽一下公司的脑残编译部署方式,web和core分开部署本来没什么问题,但是这丫居然不把json的包作为基础包而作为web的包,导致了core端不能使用,而且我们的core是可以当web来用的(不要在意这些细节),所以在core中处理json串就是个问题.没办法,跟编译那帮人也扯不清楚,只有自己写json的解析了.   </div> </li> <li><a href="/article/2662.htm" title="C语言学习八结构体,综合应用,学生管理系统" target="_blank">C语言学习八结构体,综合应用,学生管理系统</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/C%E8%AF%AD%E8%A8%80/1.htm">C语言</a> <div>实现功能的代码: # include <stdio.h> # include <malloc.h> struct Student { int age; float score; char name[100]; }; int main(void) { int len; struct Student * pArr; int i,</div> </li> <li><a href="/article/2789.htm" title="vagrant学习笔记" target="_blank">vagrant学习笔记</a> <span class="text-muted">dcj3sjt126com</span> <a class="tag" taget="_blank" href="/search/vagrant/1.htm">vagrant</a> <div>想了解多主机是如何定义和使用的, 所以又学习了一遍vagrant   1. vagrant virtualbox 下载安装 https://www.vagrantup.com/downloads.html https://www.virtualbox.org/wiki/Downloads   查看安装在命令行输入vagrant     2.</div> </li> <li><a href="/article/2916.htm" title="14.性能优化-优化-软件配置优化" target="_blank">14.性能优化-优化-软件配置优化</a> <span class="text-muted">frank1234</span> <a class="tag" taget="_blank" href="/search/%E8%BD%AF%E4%BB%B6%E9%85%8D%E7%BD%AE/1.htm">软件配置</a><a class="tag" taget="_blank" href="/search/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/1.htm">性能优化</a> <div>1.Tomcat线程池 修改tomcat的server.xml文件: <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" maxThreads="1200" m</div> </li> <li><a href="/article/3043.htm" title="一个不错的shell 脚本教程 入门级" target="_blank">一个不错的shell 脚本教程 入门级</a> <span class="text-muted">HarborChung</span> <a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/shell/1.htm">shell</a> <div>一个不错的shell 脚本教程 入门级 建立一个脚本   Linux中有好多中不同的shell,但是通常我们使用bash (bourne again shell) 进行shell编程,因为bash是免费的并且很容易使用。所以在本文中笔者所提供的脚本都是使用bash(但是在大多数情况下,这些脚本同样可以在 bash的大姐,bourne shell中运行)。   如同其他语言一样</div> </li> <li><a href="/article/3170.htm" title="Spring4新特性——核心容器的其他改进" target="_blank">Spring4新特性——核心容器的其他改进</a> <span class="text-muted">jinnianshilongnian</span> <a class="tag" taget="_blank" href="/search/spring/1.htm">spring</a><a class="tag" taget="_blank" href="/search/%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86/1.htm">动态代理</a><a class="tag" taget="_blank" href="/search/spring4/1.htm">spring4</a><a class="tag" taget="_blank" href="/search/%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5/1.htm">依赖注入</a> <div>Spring4新特性——泛型限定式依赖注入 Spring4新特性——核心容器的其他改进 Spring4新特性——Web开发的增强 Spring4新特性——集成Bean Validation 1.1(JSR-349)到SpringMVC  Spring4新特性——Groovy Bean定义DSL Spring4新特性——更好的Java泛型操作API  Spring4新</div> </li> <li><a href="/article/3297.htm" title="Linux设置tomcat开机启动" target="_blank">Linux设置tomcat开机启动</a> <span class="text-muted">liuxingguome</span> <a class="tag" taget="_blank" href="/search/tomcat/1.htm">tomcat</a><a class="tag" taget="_blank" href="/search/linux/1.htm">linux</a><a class="tag" taget="_blank" href="/search/%E5%BC%80%E6%9C%BA%E8%87%AA%E5%90%AF%E5%8A%A8/1.htm">开机自启动</a> <div>执行命令sudo gedit /etc/init.d/tomcat6 然后把以下英文部分复制过去。(注意第一句#!/bin/sh如果不写,就不是一个shell文件。然后将对应的jdk和tomcat换成你自己的目录就行了。 #!/bin/bash # # /etc/rc.d/init.d/tomcat # init script for tomcat precesses</div> </li> <li><a href="/article/3424.htm" title="第13章 Ajax进阶(下)" target="_blank">第13章 Ajax进阶(下)</a> <span class="text-muted">onestopweb</span> <a class="tag" taget="_blank" href="/search/Ajax/1.htm">Ajax</a> <div>index.html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/</div> </li> <li><a href="/article/3551.htm" title="Troubleshooting Crystal Reports off BW" target="_blank">Troubleshooting Crystal Reports off BW</a> <span class="text-muted">blueoxygen</span> <a class="tag" taget="_blank" href="/search/BO/1.htm">BO</a> <div>http://wiki.sdn.sap.com/wiki/display/BOBJ/Troubleshooting+Crystal+Reports+off+BW#TroubleshootingCrystalReportsoffBW-TracingBOE   Quite useful, especially this part: SAP BW connectivity For t</div> </li> <li><a href="/article/3678.htm" title="Java开发熟手该当心的11个错误" target="_blank">Java开发熟手该当心的11个错误</a> <span class="text-muted">tomcat_oracle</span> <a class="tag" taget="_blank" href="/search/java/1.htm">java</a><a class="tag" taget="_blank" href="/search/jvm/1.htm">jvm</a><a class="tag" taget="_blank" href="/search/%E5%A4%9A%E7%BA%BF%E7%A8%8B/1.htm">多线程</a><a class="tag" taget="_blank" href="/search/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/1.htm">单元测试</a> <div>#1、不在属性文件或XML文件中外化配置属性。比如,没有把批处理使用的线程数设置成可在属性文件中配置。你的批处理程序无论在DEV环境中,还是UAT(用户验收 测试)环境中,都可以顺畅无阻地运行,但是一旦部署在PROD 上,把它作为多线程程序处理更大的数据集时,就会抛出IOException,原因可能是JDBC驱动版本不同,也可能是#2中讨论的问题。如果线程数目 可以在属性文件中配置,那么使它成为</div> </li> <li><a href="/article/3805.htm" title="正则表达式大全" target="_blank">正则表达式大全</a> <span class="text-muted">yang852220741</span> <a class="tag" taget="_blank" href="/search/html/1.htm">html</a><a class="tag" taget="_blank" href="/search/%E7%BC%96%E7%A8%8B/1.htm">编程</a><a class="tag" taget="_blank" href="/search/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/1.htm">正则表达式</a> <div>今天向大家分享正则表达式大全,它可以大提高你的工作效率 正则表达式也可以被当作是一门语言,当你学习一门新的编程语言的时候,他们是一个小的子语言。初看时觉得它没有任何的意义,但是很多时候,你不得不阅读一些教程,或文章来理解这些简单的描述模式。 一、校验数字的表达式 数字:^[0-9]*$ n位的数字:^\d{n}$ 至少n位的数字:^\d{n,}$ m-n位的数字:^\d{m,n}$</div> </li> </ul> </div> </div> </div> <div> <div class="container"> <div class="indexes"> <strong>按字母分类:</strong> <a href="/tags/A/1.htm" target="_blank">A</a><a href="/tags/B/1.htm" target="_blank">B</a><a href="/tags/C/1.htm" target="_blank">C</a><a href="/tags/D/1.htm" target="_blank">D</a><a href="/tags/E/1.htm" target="_blank">E</a><a href="/tags/F/1.htm" target="_blank">F</a><a href="/tags/G/1.htm" target="_blank">G</a><a href="/tags/H/1.htm" target="_blank">H</a><a href="/tags/I/1.htm" target="_blank">I</a><a href="/tags/J/1.htm" target="_blank">J</a><a href="/tags/K/1.htm" target="_blank">K</a><a href="/tags/L/1.htm" target="_blank">L</a><a href="/tags/M/1.htm" target="_blank">M</a><a href="/tags/N/1.htm" target="_blank">N</a><a href="/tags/O/1.htm" target="_blank">O</a><a href="/tags/P/1.htm" target="_blank">P</a><a href="/tags/Q/1.htm" target="_blank">Q</a><a href="/tags/R/1.htm" target="_blank">R</a><a href="/tags/S/1.htm" target="_blank">S</a><a href="/tags/T/1.htm" target="_blank">T</a><a href="/tags/U/1.htm" target="_blank">U</a><a href="/tags/V/1.htm" target="_blank">V</a><a href="/tags/W/1.htm" target="_blank">W</a><a href="/tags/X/1.htm" target="_blank">X</a><a href="/tags/Y/1.htm" target="_blank">Y</a><a href="/tags/Z/1.htm" target="_blank">Z</a><a href="/tags/0/1.htm" target="_blank">其他</a> </div> </div> </div> <footer id="footer" class="mb30 mt30"> <div class="container"> <div class="footBglm"> <a target="_blank" href="/">首页</a> - <a target="_blank" href="/custom/about.htm">关于我们</a> - <a target="_blank" href="/search/Java/1.htm">站内搜索</a> - <a target="_blank" href="/sitemap.txt">Sitemap</a> - <a target="_blank" href="/custom/delete.htm">侵权投诉</a> </div> <div class="copyright">版权所有 IT知识库 CopyRight © 2000-2050 E-COM-NET.COM , All Rights Reserved. <!-- <a href="https://beian.miit.gov.cn/" rel="nofollow" target="_blank">京ICP备09083238号</a><br>--> </div> </div> </footer> <!-- 代码高亮 --> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shCore.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shLegacy.js"></script> <script type="text/javascript" src="/static/syntaxhighlighter/scripts/shAutoloader.js"></script> <link type="text/css" rel="stylesheet" href="/static/syntaxhighlighter/styles/shCoreDefault.css"/> <script type="text/javascript" src="/static/syntaxhighlighter/src/my_start_1.js"></script> </body> </html>