®
世界顶尖人才,点播 ®

Toptal, LLC版权所有

\n\n\n\n\n\n

这段代码有几个问题. 它引用了两个独立的CSS样式表和四个独立的JavaScript文件. 这意味着服务器必须向服务器发出总共6个请求, 在页面准备好之前,每个请求都必须单独加载一个资源. This is less of an issue with HTTP/2 because HTTP/2 introduces parallelism 和 header compression, 但这仍然是个问题. It increases the total volume of traffic that is required to load this page 和 reduces the quality of user experience because it takes longer to load the files. 对于HTTP 1.1, it also hogs the network 和 reduces the number of request channels that are available. It would have been much better to combine the CSS 和 JavaScript files into a single 包 for each. 这样,总共就只有两个请求. 如果提供这些文件的缩小版本也会很好, 它们通常比原件小得多. 如果任何资产被缓存,我们的web应用程序也可能会崩溃, 客户端会收到一个过时的版本.

\n\n

\"过载\"

\n\n

One primitive approach to solving some of these problems is to manually combine each type of asset into a 包 using a text editor, 然后运行结果 通过minifier服务,例如 http://jscompress.com/. 事实证明,在开发过程中持续这样做是非常乏味的. 一个小小的但值得怀疑的改进是托管我们自己的迷你服务器, 使用GitHub上可用的软件包之一. 然后我们可以做一些类似于下面的事情:

\n\n
\n
\n\n

This would serve minified files to our client, but it would not solve the problem of caching. It would also cause additional load on the server since our server would essentially have to concatenate 和 minify all the source files repetitively on every request.

\n\n

使用狼吞虎咽地吃实现自动化.js

\n\n

当然,我们可以比这两种方法中的任何一种做得更好. What we really want is to automate bundling 和 include it in the build phase of our project. We want to end up with pre-built asset 包s that are already minified 和 are ready to serve. We also want to force the client to receive the most up to date versions of our 包d assets on every request, 但是如果可能的话,我们仍然希望利用缓存. 幸运的是,狼吞虎咽地吃.Js可以处理这个. In the remainder of the article, we will be building a solution that will leverage the power of 狼吞虎咽地吃.Js来连接和缩小文件. 当有更新时,我们还将使用一个插件来破坏缓存.

\n\n

在我们的示例中,我们将创建以下目录和文件结构:

\n\n
公共/\n| - - - - - -构建/\n   |- js/\n      | - 包{哈希}.js\n   | - css /\n      | -样式表{哈希}.css\n资产/\n|- js/\n   | -供应商/\n   | - jquery.js\n   | - - - - - -网站.js\n   | - module1.js\n   | - module2.js\n| - css /\n   | - - - - - -主.css\n   | - - - - - -自定义.css\n狼吞虎咽地吃file.js\n包.json\n
\n\n
npm在节点中进行包管理.Js投射出一种幸福. 狼吞虎咽地吃 provides tremendous extensibility by taking advantage of npm’s simple packaging approach to deliver modular 和 powerful plugins.
\n\n

的狼吞虎咽地吃file.我们将在这里定义狼吞虎咽地吃将为我们执行的任务. 的 包.json is used by npm to define our application’s 包 和 track the dependencies that we will be installing. 公共目录应该配置成面向web的目录. 的 资产目录 我们将在哪里存储源文件. 在项目中使用狼吞虎咽地吃, we will need to install it via npm 和 save it as a developer dependency for the project. 我们也要从 concat 狼吞虎咽地吃插件,它允许我们将多个文件连接成一个.

\n\n

为了安装这两个项目,我们将运行以下命令:

\n\n
NPM install——save-dev 狼吞虎咽地吃\n
\n\n

接下来,我们将开始编写狼吞虎咽地吃file的内容.js.

\n\n
Var 狼吞虎咽地吃 = require('狼吞虎咽地吃');\nVar concat = require('狼吞虎咽地吃-concat');\n \n狼吞虎咽地吃.Task ('pack-js', function () {    \n    返回吞咽.src([资产/ js /供应商/ *.js”、“资产/ js /主要.js”、“资产/ js /模块*.js'])\n        .管(concat('包.js'))\n        .管(吞咽.桌子('公共/构建/ js '));\n});\n \n狼吞虎咽地吃.Task ('pack-css的, function () {    \n    返回吞咽.src([资产/ css /主要.css”、“资产/ css /自定义.css '])\n        .管(concat(样式表.css '))\n        .管(吞咽.桌子(公共/构建/ css));\n});\n \n狼吞虎咽地吃.Task ('默认的', ['pack-js', 'pack-css ']);\n
\n\n

这里,我们正在加载狼吞虎咽地吃库及其concat插件. 然后我们定义三个任务.

\n\n

\"加载狼吞虎咽地吃库及其concat插件\"

\n\n

第一项任务(pack-js)定义了一个过程来将多个JavaScript源文件压缩到一个包中. We list the source files, which will be globbed, read, 和 concatenated in the order specified. 我们将其管道到concat插件中以获得一个最终文件 包.js. 最后,我们告诉狼吞虎咽地吃将文件写入 公共/构建/ js.

\n\n

第二项任务(pack-css)做同样的事情,但对于CSS样式表. 它告诉狼吞虎咽地吃将连接的输出存储为 样式表.css in 公共/构建/ css.

\n\n

第三项任务(默认的)是当我们不带参数调用它时狼吞虎咽地吃运行的. In the second parameter, we pass the list of other tasks to execute when the 默认的 task is run.

\n\n

让我们将这些代码粘贴到狼吞虎咽地吃file中.js using any source code editor that we normally use, 和 then save the file to the application root.

\n\n

接下来,我们将打开命令行并运行:

\n\n
狼吞虎咽地吃\n
\n\n

如果我们在运行此命令后查看我们的文件,我们将发现两个新文件: 公共/构建/ js /包.js公共/构建/ css样式表.css. 的y are concatenations of our source files, which solves part of the original problem. 然而,它们并没有被最小化,也没有缓存破坏. 让我们添加自动缩小功能.

\n\n

优化已建资产

\n\n

我们将需要两个新的插件. 要添加它们,我们将运行以下命令:

\n\n
狼吞虎咽地吃-clean-css 狼吞虎咽地吃-minify\n
\n\n

第一个插件用于缩小CSS,第二个插件用于缩小JavaScript. 的 第一个 one uses the clean-css 包, 和 the second one uses the UglifyJS2 包. 我们将在我们的狼吞虎咽地吃file中加载这两个包.js:

\n\n
Var minify = require('狼吞虎咽地吃-minify');\nvar cleanCss = require('狼吞虎咽地吃-clean-css的);\n
\n\n

然后,在将输出写入磁盘之前,我们需要在任务中使用它们:

\n\n
.管(贬低())\n.管(cleanCss ())\n
\n\n

的狼吞虎咽地吃file.Js现在看起来像这样:

\n\n
Var 狼吞虎咽地吃 = require('狼吞虎咽地吃');\nVar concat = require('狼吞虎咽地吃-concat');\nVar minify = require('狼吞虎咽地吃-minify');\nvar cleanCss = require('狼吞虎咽地吃-clean-css的);\n \n狼吞虎咽地吃.Task ('pack-js', function () {    \n    返回吞咽.src([资产/ js /供应商/ *.js”、“资产/ js /主要.js”、“资产/ js /模块*.js'])\n        .管(concat('包.js'))\n        .管(贬低())\n        .管(吞咽.桌子('公共/构建/ js '));\n});\n \n狼吞虎咽地吃.Task ('pack-css的, function () {    \n    返回吞咽.src([资产/ css /主要.css”、“资产/ css /自定义.css '])\n        .管(concat(样式表.css '))\n        .管(cleanCss ())\n   .管(吞咽.桌子(公共/构建/ css));\n});\n \n狼吞虎咽地吃.Task ('默认的', ['pack-js', 'pack-css ']);\n
\n\n

让我们再跑一遍. 我们将看到文件 样式表.css 以缩小格式保存,文件 包.js 还是保存原样吗. 我们会注意到我们现在也有了包-min.Js,它是最小化的. 我们只需要缩小后的文件,并将其保存为 包.js,所以我们将用额外的参数修改代码:

\n\n
.管(贬低({\n    ext: {\n        敏:“.js'\n    },\n    noSource:真\n}))\n
\n\n

根据狼吞虎咽地吃-minify插件文档(http://www.npmjs.com/包/狼吞虎咽地吃-minify), 这将为缩小版本设置所需的名称, 并告诉插件不要创建包含原始源代码的版本. If we delete the content of the build directory 和 run 狼吞虎咽地吃 from the comm和 line again, 我们最终将得到两个缩小的文件. 我们刚刚完成了构建过程的最小化阶段的实现.

\n\n

缓存的地沟油

\n\n

Next, we will want to add cache busting, 和 we will need to install a plugin for that:

\n\n
NPM install——save-dev 狼吞虎咽地吃-牧师\n
\n\n

并在我们的狼吞虎咽地吃文件中要求它:

\n\n
Var 牧师 = require('狼吞虎咽地吃-牧师');\n
\n\n

使用这个插件有点棘手. 我们必须首先通过插件将缩小后的输出管道化. 然后,我们必须在将结果写入磁盘后再次调用插件. 该插件重命名文件,以便用唯一的散列标记它们, 它还创建了一个清单文件. 的 manifest file is a map that can be used by our application to determine the latest filenames that we should refer to in our HTML code. 在我们修改了狼吞虎咽地吃文件之后,它应该看起来像这样:

\n\n
Var 狼吞虎咽地吃 = require('狼吞虎咽地吃');\nVar concat = require('狼吞虎咽地吃-concat');\nVar minify = require('狼吞虎咽地吃-minify');\nvar cleanCss = require('狼吞虎咽地吃-clean-css的);\nVar 牧师 = require('狼吞虎咽地吃-牧师');\n \n狼吞虎咽地吃.Task ('pack-js', function () {    \n    返回吞咽.src([资产/ js /供应商/ *.js”、“资产/ js /主要.js”、“资产/ js /模块*.js'])\n        .管(concat('包.js'))\n        .管(贬低({\n            ext: {\n                敏:“.js'\n            },\n            noSource:真\n        }))\n        .管(牧师())\n        .管(吞咽.桌子(公共/构建/ js))\n        .管(牧师.清单())\n        .管(吞咽.桌子('公共/构建'));\n});\n \n狼吞虎咽地吃.Task ('pack-css的, function () {\n    返回吞咽.src([资产/ css /主要.css”、“资产/ css /自定义.css '])\n        .管(concat(样式表.css '))\n        .管(cleanCss ())\n        .管(牧师())\n            .管(吞咽.桌子(公共/构建/ css))\n        .管(牧师.清单())\n        .管(吞咽.桌子('公共/构建'));\n});\n \n狼吞虎咽地吃.Task ('默认的', ['pack-js', 'pack-css ']);\n
\n\n
适当的缓存破坏, you can go nuts with long expiry time for your JS 和 CSS files 和 reliably replace them still with newer versions whenever necessary.
\n\n

让我们删除构建目录的内容并再次运行狼吞虎咽地吃. We will find that we now have two files with hashtags affixed to each of the filenames, 还有一张清单.Json保存到 公共/构建. 如果我们打开舱单文件, 我们将看到它只有一个对我们的缩小和标记文件之一的引用. 发生了什么是每个任务写一个单独的清单文件, 其中一个会覆盖另一个. We will need to modify the tasks with additional parameters that will tell them to look for the existing manifest file 和 to merge the new data into it if it exists. 它的语法有点复杂, 那么让我们看看代码应该是什么样子,然后再看一遍:

\n\n
Var 狼吞虎咽地吃 = require('狼吞虎咽地吃');\nVar concat = require('狼吞虎咽地吃-concat');\nVar minify = require('狼吞虎咽地吃-minify');\nvar cleanCss = require('狼吞虎咽地吃-clean-css的);\nVar 牧师 = require('狼吞虎咽地吃-牧师');\n \n狼吞虎咽地吃.Task ('pack-js', function () {\n    返回吞咽.src([资产/ js /供应商/ *.js”、“资产/ js /主要.js”、“资产/ js /模块*.js'])\n        .管(concat('包.js'))\n        .管(贬低({\n            ext: {\n                敏:“.js'\n            },\n            noSource:真\n        }))\n        .管(牧师())\n        .管(吞咽.桌子(公共/构建/ js))\n        .管(牧师.清单(“公共/构建/ 牧师-manifest.json, {\n            合并:真\n        }))\n        .管(吞咽.桌子("));\n    });\n \n狼吞虎咽地吃.Task ('pack-css的, function () {    \n    返回吞咽.src([资产/ css /主要.css”、“资产/ css /自定义.css '])\n        .管(concat(样式表.css '))\n        .管(cleanCss ())\n        .管(牧师())\n        .管(吞咽.桌子(公共/构建/ css))\n        .管(牧师.清单(“公共/构建/ 牧师-manifest.json, {\n            合并:真\n        }))\n        .管(吞咽.桌子("));\n});\n \n狼吞虎咽地吃.Task ('默认的', ['pack-js', 'pack-css ']);\n
\n\n

我们将输出管道输出到 牧师.清单() 第一个. 这将创建带有标记的文件,而不是我们之前使用的文件. 我们正在提供我们想要的路径 牧师-manifest.json和告诉 牧师.清单() 如果现有文件存在,则合并到现有文件. 然后我们告诉狼吞虎咽地吃将清单写入当前目录, 到那个时候,哪个是公共的/建设的. 路径问题是由于一个bug,在GitHub上有更详细的讨论.

\n\n

我们现在有了自动缩小、标记文件和清单文件. 所有这些都将使我们能够更快地将文件传递给用户, 只要我们做了修改,就会破坏他们的缓存. 不过,还剩下两个问题.

\n\n

第一个问题是,如果我们对源文件进行任何修改, 我们将获得新标记的文件, 但旧的也会留在那里. 我们需要一些方法来自动删除旧的缩小文件. 让我们使用一个允许我们删除文件的插件来解决这个问题:

\n\n
NPM install——save-dev del\n
\n\n

We will require it in our code 和 define two new tasks, one for each type of source file:

\n\n
Var del = require('del');\n \n狼吞虎咽地吃.Task ('clean-js', function () {\n    返回德尔([\n        “公共/构建/ js / *.js'\n    ]);\n});\n \n狼吞虎咽地吃.任务('clean-css的,函数(){\n    返回德尔([\n        “公共/构建/ css / *.css的\n    ]);\n});\n
\n\n

然后,我们将确保新任务在我们的两个主要任务之前完成运行:

\n\n
狼吞虎咽地吃.任务('pack-js', ['clean-js'],函数(){\n狼吞虎咽地吃.任务('pack-css的, ['clean-css的],函数(){\n
\n\n

如果我们跑 狼吞虎咽地吃 同样,在此修改之后,我们将拥有最新的缩小文件.

\n\n

的 second problem is that we don’t want to keep running 狼吞虎咽地吃 every time we make a change. 为了解决这个问题,我们需要定义一个观察者任务:

\n\n
狼吞虎咽地吃.Task ('watch', function() {\n 狼吞虎咽地吃.看(资产/ js / * * / *.js”,[' pack-js ']);\n 狼吞虎咽地吃.看(资产/ css / * * / *.css”,[' pack-css ']);\n});\n
\n\n

我们还将更改默认任务的定义:

\n\n
狼吞虎咽地吃.任务(“违约”,['看']);\n
\n\n

如果我们现在从命令行运行狼吞虎咽地吃, 我们将发现它在调用时不再构建任何东西. This is because it now calls the watcher task that will watch our source files for any changes, 并且仅在检测到更改时进行构建. If we try changing any of our source files 和 then look at our console again, we will see that the pack-jspack-css 任务与其依赖项一起自动运行.

\n\n

现在,我们要做的就是装载舱单.Json文件,并从中获取带标签的文件名. 我们如何做到这一点取决于我们特定的后端语言和技术堆栈, 实现起来也很简单, 所以我们就不详细讲了. 然而, 总体思路是,我们可以将清单加载到数组或对象中, 和 then define a helper function that will allow us to call versioned assets from our templates in a manner similar to the following:

\n\n
杯(包.js’)\n
\n\n

一旦我们这样做了, 我们再也不用担心文件名中的标签被更改了, 我们将能够专注于编写高质量的代码.

\n\n

的 final source code for this article, along with a few sample assets, can be found in 这个GitHub存储库.

\n\n

结论

\n\n

在本文中, we went over how to implement 狼吞虎咽地吃 based automation for our build process. I hope that this proves helpful to you 和 allows you to develop more sophisticated build processes in your own applications.

\n\n

Please keep in mind that 狼吞虎咽地吃 is just one of the tools that can be used for this purpose, 还有很多其他的,比如Grunt, Browserify, 和Webpack. 它们的目的和解决问题的范围各不相同. 有些可以解决狼吞虎咽地吃无法解决的问题,比如捆绑 JavaScript 具有依赖关系的模块可以按需加载. 这被称为“代码分割”。, 和 it is an improvement over the idea of serving one big file with all parts of our program on every page. 这些工具相当复杂,但将来可能会涉及到. In a following post, we will go over how to automate the deployment of our application.

\n","as":"div","isContentFit":true,"sharingWidget":{"url":"http://nmp.hidekoquanyin.net/javascript/optimize-js-and-css-with-Gulp","title":"Simplify 前端 JS 和 CSS Optimization With 狼吞虎咽地吃","text":null,"providers":["linkedin","推特","脸谱网"],"gaCategory":null,"domain":{"name":"developers","title":"工程","vertical":{"name":"developers","title":"开发人员","publicUrl":"http://nmp.hidekoquanyin.net/developers"},"publicUrl":"http://nmp.hidekoquanyin.net/developers/blog"},"hashtags":"JavaScript,自动化,狼吞虎咽地吃,构建"}}