{"action":"closed","issue":{"url":"https://api.github.com/repos/icepy/we-writing/issues/34","repository_url":"https://api.github.com/repos/icepy/we-writing","labels_url":"https://api.github.com/repos/icepy/we-writing/issues/34/labels{/name}","comments_url":"https://api.github.com/repos/icepy/we-writing/issues/34/comments","events_url":"https://api.github.com/repos/icepy/we-writing/issues/34/events","html_url":"https://github.com/icepy/we-writing/issues/34","id":148238515,"number":34,"title":"webpack实践最后一篇","user":{"login":"icepy","id":3321837,"avatar_url":"https://avatars0.githubusercontent.com/u/3321837?v=4","gravatar_id":"","url":"https://api.github.com/users/icepy","html_url":"https://github.com/icepy","followers_url":"https://api.github.com/users/icepy/followers","following_url":"https://api.github.com/users/icepy/following{/other_user}","gists_url":"https://api.github.com/users/icepy/gists{/gist_id}","starred_url":"https://api.github.com/users/icepy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/icepy/subscriptions","organizations_url":"https://api.github.com/users/icepy/orgs","repos_url":"https://api.github.com/users/icepy/repos","events_url":"https://api.github.com/users/icepy/events{/privacy}","received_events_url":"https://api.github.com/users/icepy/received_events","type":"User","site_admin":false},"labels":[{"id":252099201,"url":"https://api.github.com/repos/icepy/we-writing/labels/%E6%9D%82%E8%B0%88%E4%B8%8E%E5%B7%A5%E5%85%B7","name":"杂谈与工具","color":"1d76db","default":false}],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2016-04-14T03:10:22Z","updated_at":"2017-10-06T17:36:19Z","closed_at":"2017-10-06T17:36:19Z","author_association":"OWNER","body":"> 温故而知新\n\n这应该是目前这个阶段最后一篇关于webpack的实践经验，也许你会学习到该用怎样的思想去使用webpack，也许你会认为这是一坨屎一样的文字。不过，我会尽量的描述，我们的实践以及给出一份在Mac和Win下的Demo，这个项目也是我们应用在PC端的实践，访问[https://github.com/sapling-team/generator-sapling-pc](https://github.com/sapling-team/generator-sapling-pc)来阅读我们PC端的脚手架吧。（完美兼容IE8+）\n\n我们对`backbone`也提供了一份有用的扩展，可访问：[https://github.com/sapling-team/base-extend-backbone](https://github.com/sapling-team/base-extend-backbone)\n\n更多基础的配置信息，建议大家阅读[《webpack在PC项目中的应用》](https://github.com/icepy/_posts/issues/25)\n## 该用什么样的思想来使用\n\n> 你用的始终还是Node.js环境\n\n我觉得你应该要忘记webpack，因为你使用的终归还是Node.js。为什么这么说，因为如果只使用简单的配置，你可能只把它做为一个模块加载器。如果你熟练的使用了Node.Js那么恭喜你，我们将用环境的思维来使用它，并用Node.Js来驱动你的构建流程。（NPM可以使用的模块，你都可以在构建环境中使用）\n\n首先，我们应该将脚本文件放置在bin目录下，这是unix编程的常识。接着，对于你的产品，定义两个环境：dev和product，在NPM Scripts中使用`NODE_ENV=dev webpack --config bin/webpack.config`，然后在脚本文件中使用`process.env.NODE_ENV`来获取环境变量并区分执行。（很不幸的是，Win用户无法获取ENV，所以你需要建立两个文件来做dev和product）\n## 抽象dir\n\n抽象你的目录资源，设计一定的规则，可以进行批处理配置信息。\n\n```\nnpm install glob --save-dev --verbose\n```\n\n我们的入口文件都放置在src中，根据业务特点来命名，比如：`index.js`，`code.js`。\n\n``` JavaScript\nvar path = require('path');\nvar glob = require('glob');\n\nmodule.exports = getEntry;\n\nfunction getEntry(sourcePath){\n    var entrys = {};\n    var basename;\n    glob.sync(sourcePath).forEach(function(entry){\n        basename = path.basename(entry,path.extname(entry));\n        entrys[basename] = entry;\n    });\n    return entrys;\n}\n```\n\n在`webpack.dev.config.js`文件，可以通过getEntry函数来统一处理入口，并得到`entry`配置对象。如果你是多页面多入口的项目，建议你使用统一的命名规则，比如页面叫`index.html`，那么你的js和css入口文件也应该叫`index.js`和`index.css`。\n## include\n\n> 在编译期来决定最终呈现什么样的HTML\n\n在后端语言的模板中`include`是一个非常有用的特性，因为它可以抽象分离不同的HTML结构，来达到复用的目的。\n\n```\nnpm install jade-loader --save-dev --verbose\n```\n\n``` jade\ndoctype html\nhtml(lang=\"en\")\n    head\n        - var titleValue = htmlWebpackPlugin.options.title\n        title=titleValue\n        meta(charset=\"UTF-8\")\n    body\n        include common/header\n        include index/container\n        include common/footer\n        include common/lib\n```\n\n``` jade\n- var src;\n- var map = ['jquery/dist/jquery.min.js','underscore/underscore-min.js','backbone/backbone-min.js']\nif htmlWebpackPlugin.options.cdn\n    - src = 'http://127.0.0.1:3000/www/link/'\nelse\n    - src = '/link/'\neach val in map\n    script(type=\"text/javascript\",src=src+val)\n```\n\n``` JavaScript\n{\n    test:/.jade$/,\n    loader:'jade-loader',\n    exclude:/(node_modules)/\n}\n```\n\n不仅如此`jade`还可以做更高灵活的配置。\n## html-webpack-plugin\n\n如果你是多页面，或者单页应用都需要这个插件来帮忙处理HTML的内容，比如上述的jade模板的`include`。\n\n``` JavaScript\nvar pages = getEntry('./app/web/*.jade');\nfor(var chunkname in pages){\n    var conf = {\n        cdn:false,\n        filename:chunkname+'.html',\n        template:pages[chunkname],\n        inject:true,\n        minify:{\n            removeComments:true,\n            collapseWhitespace: false\n        },\n        chunks:['common',chunkname],\n        hash:false\n    }\n    plugins.push(new HtmlWebpackPlugin(conf));\n}\n```\n\n根据`抽象dir`的方法，我们可以通过`getEntry`来获取一个pages对象，并使用chunks来处理每一个入口页面的依赖。\n\n如果你是单页应用，你只需要添加一次HtmlWebpackPlugin插件即可。\n## 如何优化\n\n> 优化，但不要过度的优化\n\n运行`npm run product`来构建你的发布资源。\n\n过度优化的结果：\n\n``` Less\n.title {\n  margin-left: auto;\n  margin-right: auto;\n  .size(margin-top, 15px);\n  .size(margin-bottom, 15px);\n}\n```\n\n最后经过webpack的优化变成了：\n\n``` CSS\n.title {\n  margin: 2.6408vh auto 2.6408vh;\n  margin-top: 1.5rem;\n  margin-bottom: 1.5rem;\n}\n```\n\n那么问题来了Android4.3以下版本是不支持vw,vh单位的。\n\n我认为对于一个项目，优化的方式应该可以从下列的几个点中去挖掘：\n\n**externals 分离**\n\n善用`externals`将过大的文件分离出去，然后再用`script`标签引用即可。\n\n**alias机制**\n\n给一些模块启用别名，来提高webpack的搜索速度。\n\n**将某些对象暴露在全局**\n- 在loader中使用`expose`将对象暴露出去`loader: 'expose?jQuery'`\n- 使用`ProvidePlugin`插件的帮助\n\n``` JavaScript\nnew webpack.ProvidePlugin({\n    \"M\": \"mock\",\n}),\n```\n\n假设`mock`对象中有`set`，`get`方法，那么此刻你可以在使用`M.set`，`M.get`方式来调用。\n\n**启用热替换**\n- 将代码内敛到入口js文件中，然后启动`webpack.HotModuleReplacementPlugin`插件\n- 使用自带的`webpack-dev-server`启动一个服务器\n- 直接在`webpack.config.js`文件中配置\n\n``` JavaScript\n//配置\n\ndevServer:{\n    port:4000,\n    contentBase:'./app',\n    historyApiFallback:true\n}\n\n```\n\n**合理的使用CommonsChunkPlugin将每一个chunk分割开，并提取**\n\n`CommonsChunkPlugin`有一些属性，比如`minChunks`可以设置如果`require`几次再提取的问题。\n\n**个别项目环境变量优化**\n\n定义你的`process.env.NODE_ENV`变量，启用`DefinePlugin`插件来优化警告信息，很多框架，比如react都带有警告，比如：\n\n``` JavaScript\nif(process.env.NODE_ENV !== 'production'){\n\n}\n```\n\n在prodcut期，我们可以通过`DefinePlugin`来打入环境变量来将这些剔除。\n\n``` JavaScript\nplugins.push(new webpack.DefinePlugin({\n    'process.env':{\n        'NODE_ENV':JSON.stringify(process.env.NODE_ENV)\n    }\n}));\n```\n\n构建期间`process.env.NODE_ENV !== 'production'`会变成：\n\n``` JavaScript\nif(false){\n\n}\n```\n\n压缩工具，会忽略false内的内容，你会发现体积将减少了很多。\n## 清理工作\n\n每次`npm run product`之后，因为设置了`hash`属性，所以会生成不同的文件，那么问题来了，我无法清理www目录，那么这时候，就可以换到Node.js的文件系统上了。\n\n``` JavaScript\nvar fs = require('fs');\nvar path = require('path');\nvar containerPath = path.resolve('./');\n\nmodule.exports = rmdir;\n\nfunction rmdir(dirPath){\n    dirPath = path.resolve(containerPath,dirPath);\n    var dirs = [];\n    collection(dirPath,dirs);\n    dirs.forEach(function(v){\n        var status = fs.rmdirSync(v);\n        if(status){\n            console.log(status);\n        }\n    });\n}\n\nfunction collection(url,dirs){\n    var stat = fs.statSync(url);\n    if(stat.isDirectory()){\n        dirs.unshift(url);\n        recursion(url,dirs);\n    }else{\n        if(stat.isFile()){\n            fs.unlinkSync(url);\n        }\n    }\n}\n\nfunction recursion(url,dirs){\n    var arr = fs.readdirSync(url);\n    var i = 0;\n    var le = arr.length;\n    for(;i<le;i++){\n        var v = path.resolve(url,arr[i]);\n        collection(v,dirs);\n    }\n}\n```\n## 运行脚手架Demo\n\n你可以下载脚手架项目跑一跑：[https://github.com/sapling-team/generator-sapling-pc](https://github.com/sapling-team/generator-sapling-pc)\n\n最终的发布可能还是需要gulp的一些辅助，不过这不要紧了，它只是帮助我们挪动了一些文件，最终成为了一个`www`目录。\n## 【公告】\n\n---\n\n微信公众号开通于2016年，内容起于前端而不止于前端，欢迎大家关注。\n\n**关注微信公众号，扫描二维码**\n\n<div align=\"center\">\n<img src=\"https://raw.githubusercontent.com/icepy/_posts/master/img/weixin.jpg\" alt=\"\"/><br>\n</div>\n\n\n如果大家想加入《前端Talk读者》群，可以在公众号留言：你的微信号＋`申请入群`这样的标识。\n"}}
{"actor":{"display_login":"icepy"}}