webpack实践最后一篇
AAIOpen
9年前
<p> </p> <h2>温故而知新</h2> <p>这应该是目前这个阶段最后一篇关于webpack的实践经验,也许你会学习到该用怎样的思想去使用webpack,也许你会认为这是一坨屎一样的文字。不过,我会尽量的描述,我们的实践以及给出一份在Mac和Win下的Demo,这个项目也是我们应用在PC端的实践,访问 <a href="/misc/goto?guid=4959670709122729604" rel="nofollow,noindex">https://github.com/sapling-team/generator-sapling-pc</a> 来阅读我们PC端的脚手架吧。(完美兼容IE8+)</p> <p>我们对 backbone 也提供了一份有用的扩展,可访问: <a href="/misc/goto?guid=4959670709205123655" rel="nofollow,noindex">https://github.com/sapling-team/base-extend-backbone</a></p> <p>更多基础的配置信息,建议大家阅读 <a href="http://www.open-open.com/lib/view/open1460613801873.html" rel="nofollow,noindex">《webpack在PC项目中的应用》</a></p> <h2>该用什么样的思想来使用</h2> <p>你用的始终还是Node.js环境</p> <p>我觉得你应该要忘记webpack,因为你使用的终归还是Node.js。为什么这么说,因为如果只使用简单的配置,你可能只把它做为一个模块加载器。如果你熟练的使用了Node.Js那么恭喜你,我们将用环境的思维来使用它,并用Node.Js来驱动你的构建流程。(NPM可以使用的模块,你都可以在构建环境中使用)</p> <p>首先,我们应该将脚本文件放置在bin目录下,这是unix编程的常识。接着,对于你的产品,定义两个环境:dev和product,在NPM Scripts中使用 NODE_ENV=dev webpack --config bin/webpack.config ,然后在脚本文件中使用 process.env.NODE_ENV 来获取环境变量并区分执行。(很不幸的是,Win用户无法获取ENV,所以你需要建立两个文件来做dev和product)</p> <h2>抽象dir</h2> <p>抽象你的目录资源,设计一定的规则,可以进行批处理配置信息。</p> <p>npm install glob --save-dev --verbose</p> <p>我们的入口文件都放置在src中,根据业务特点来命名,比如: index.js , code.js 。</p> <pre> <code class="language-javascript">var path = require('path'); var glob = require('glob'); module.exports = getEntry; function getEntry(sourcePath){ var entrys = {}; var basename; glob.sync(sourcePath).forEach(function(entry){ basename = path.basename(entry,path.extname(entry)); entrys[basename] = entry; }); return entrys; } </code></pre> <p>在 webpack.dev.config.js 文件,可以通过getEntry函数来统一处理入口,并得到 entry 配置对象。如果你是多页面多入口的项目,建议你使用统一的命名规则,比如页面叫 index.html ,那么你的js和css入口文件也应该叫 index.js 和 index.css 。</p> <h2>include</h2> <p>在编译期来决定最终呈现什么样的HTML</p> <p>在后端语言的模板中 include 是一个非常有用的特性,因为它可以抽象分离不同的HTML结构,来达到复用的目的。</p> <p>npm install jade-loader --save-dev --verbose</p> <pre> <code class="language-javascript">doctype html html(lang="en") head - var titleValue = htmlWebpackPlugin.options.title title=titleValue meta(charset="UTF-8") body include common/header include index/container include common/footer include common/lib </code></pre> <pre> <code class="language-javascript">- var src; - var map = ['jquery/dist/jquery.min.js','underscore/underscore-min.js','backbone/backbone-min.js'] if htmlWebpackPlugin.options.cdn - src = 'http://127.0.0.1:3000/www/link/' else - src = '/link/' each val in map script(type="text/javascript",src=src+val) </code></pre> <pre> <code class="language-javascript">{ test:/.jade$/, loader:'jade-loader', exclude:/(node_modules)/ } </code></pre> <p>不仅如此 jade 还可以做更高灵活的配置。</p> <h2>html-webpack-plugin</h2> <p>如果你是多页面,或者单页应用都需要这个插件来帮忙处理HTML的内容,比如上述的jade模板的 include 。</p> <pre> <code class="language-javascript">var pages = getEntry('./app/web/*.jade'); for(var chunkname in pages){ var conf = { cdn:false, filename:chunkname+'.html', template:pages[chunkname], inject:true, minify:{ removeComments:true, collapseWhitespace: false }, chunks:['common',chunkname], hash:false } plugins.push(new HtmlWebpackPlugin(conf)); } </code></pre> <p>根据 抽象dir 的方法,我们可以通过 getEntry 来获取一个pages对象,并使用chunks来处理每一个入口页面的依赖。</p> <p>如果你是单页应用,你只需要添加一次HtmlWebpackPlugin插件即可。</p> <h2>如何优化</h2> <p>优化,但不要过度的优化</p> <p>运行 npm run product 来构建你的发布资源。</p> <p>过度优化的结果:</p> <pre> <code class="language-javascript">.title { margin-left: auto; margin-right: auto; .size(margin-top, 15px); .size(margin-bottom, 15px); } </code></pre> <p>最后经过webpack的优化变成了:</p> <pre> <code class="language-javascript">.title { margin: 2.6408vh auto 2.6408vh; margin-top: 1.5rem; margin-bottom: 1.5rem; } </code></pre> <p>那么问题来了Android4.3以下版本是不支持vw,vh单位的。</p> <p>我认为对于一个项目,优化的方式应该可以从下列的几个点中去挖掘:</p> <p>externals 分离</p> <p>善用 externals 将过大的文件分离出去,然后再用 script 标签引用即可。</p> <p>alias机制</p> <p>给一些模块启用别名,来提高webpack的搜索速度。</p> <p>将某些对象暴露在全局</p> <ul> <li>在loader中使用 expose 将对象暴露出去 loader: 'expose?jQuery'</li> <li>使用 ProvidePlugin 插件的帮助</li> </ul> <pre> <code class="language-javascript">new webpack.ProvidePlugin({ "M": "mock", }), </code></pre> <p>假设 mock 对象中有 set , get 方法,那么此刻你可以在使用 M.set , M.get 方式来调用。</p> <p>启用热替换</p> <ul> <li>将代码内敛到入口js文件中,然后启动 webpack.HotModuleReplacementPlugin 插件</li> <li>使用自带的 webpack-dev-server 启动一个服务器</li> <li>直接在 webpack.config.js 文件中配置</li> </ul> <pre> <code class="language-javascript">//配置 devServer:{ port:4000, contentBase:'./app', historyApiFallback:true } </code></pre> <p>合理的使用CommonsChunkPlugin将每一个chunk分割开,并提取</p> <p>CommonsChunkPlugin 有一些属性,比如 minChunks 可以设置如果 require 几次再提取的问题。</p> <p>个别项目环境变量优化</p> <p>定义你的 process.env.NODE_ENV 变量,启用 DefinePlugin 插件来优化警告信息,很多框架,比如react都带有警告,比如:</p> <pre> <code class="language-javascript">if(process.env.NODE_ENV !== 'production'){ } </code></pre> <p>在prodcut期,我们可以通过 DefinePlugin 来打入环境变量来将这些剔除。</p> <pre> <code class="language-javascript">plugins.push(new webpack.DefinePlugin({ 'process.env':{ 'NODE_ENV':JSON.stringify(process.env.NODE_ENV) } })); </code></pre> <p>构建期间 process.env.NODE_ENV !== 'production' 会变成:</p> <pre> <code class="language-javascript">if(false){ } </code></pre> <p>压缩工具,会忽略false内的内容,你会发现体积将减少了很多。</p> <h2>清理工作</h2> <p>每次 npm run product 之后,因为设置了 hash 属性,所以会生成不同的文件,那么问题来了,我无法清理www目录,那么这时候,就可以换到Node.js的文件系统上了。</p> <pre> <code class="language-javascript">var fs = require('fs'); var path = require('path'); var containerPath = path.resolve('./'); module.exports = rmdir; function rmdir(dirPath){ dirPath = path.resolve(containerPath,dirPath); var dirs = []; collection(dirPath,dirs); dirs.forEach(function(v){ var status = fs.rmdirSync(v); if(status){ console.log(status); } }); } function collection(url,dirs){ var stat = fs.statSync(url); if(stat.isDirectory()){ dirs.unshift(url); recursion(url,dirs); }else{ if(stat.isFile()){ fs.unlinkSync(url); } } } function recursion(url,dirs){ var arr = fs.readdirSync(url); var i = 0; var le = arr.length; for(;i<le;i++){ var v = path.resolve(url,arr[i]); collection(v,dirs); } } </code></pre> <h2>运行脚手架Demo</h2> <p>你可以下载脚手架项目跑一跑: <a href="/misc/goto?guid=4959670709122729604" rel="nofollow,noindex">https://github.com/sapling-team/generator-sapling-pc</a></p> <p>最终的发布可能还是需要gulp的一些辅助,不过这不要紧了,它只是帮助我们挪动了一些文件,最终成为了一个 www 目录。</p> <p>来自: <a href="/misc/goto?guid=4959670709304572694" rel="nofollow">http://div.io/topic/1684</a></p>