webpack实践最后一篇

AAIOpen 8年前
   <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>