初学教程1部分- 介绍Webpack

这是一些相关WebPACK库的东西介绍:

虽然这些库的工具非常好的放在一起,他们不一定适合放在一起学习。

我的情况是,我很困惑,试图了解发生了什么,但是大量的分散资源的理解并取消了我的注意力。

我希望这个教程可以使Webpack易学。

必要条件

至少你要知道Node.js和npm的基础知识。

目录

为什么要学Webpack?

因为每一个react/redux教程都假设我们知道 :cry:

但是往往我们有一些更现实的原因使我们想使用webpack。

可能有这些:

  • 压缩我们的js文件合并成一个文件
  • 在我们的前端代码项目中使用NPM包管理工具
  • 使用ES6/ES7规范书写代码(借助babel)
  • 缩小/优化代码
  • 编译LESS/SCSS成CSS
  • 使用HMR(Hot Module Replacement/实时的模块监听改变)
  • 把任何类型的文件放进我们的javascript中
  • 我们需要了解、掌握更多先进和潮流的技术
为什么我们想要上面这些?
  • 压缩js文件——让我们可以编写模块化的javascript代码,却不需要为把它放进单独的js文件来被单个的<script>标签所引用。(如果我们需要配置多个js文件时)

  • 使用NPM包管理在我们的前端代码项目——NPM是互联网上最大的开源代码生态系统。我们可以试试把写好的代码保存上传到NPM看看,把想要的库放进你的前端项目。

  • ES6/ES7——为javascript增加了很多新特性,使它更具有潜力又容易编写。看看这里的介绍.

  • 缩小/优化代码——减少我们发布文件的大小,就有助于让我们页面更快地加载的。

  • 编译LESS/SCSS成CSS——更好的方式去编写CSS代码。如果你不熟悉可以看这里的介绍.

  • 使用HMR-生产力的提高,每次保存代码的时候,只要他是被引入到该页面那么不需要完整的页面刷新。这在我们编辑代码时候是非常方便的。

  • 把任何类型的文件包含进我们的javascript中——减少其他构建工具的需要,并允许我们用代码的方式修改和使用这些文件。

基础知识

安装

想使用Webpack大多数的功能只需要我们全局安装一下:

  1. npm install -g webpack

然而Webpack的一些功能,比如优化插件,需要我们把它安装到本地。在这种情况下我们需要:

  1. npm install --save-dev webpack

命令行

运行Webpack只需要下面一行命令:

  1. webpack

如果想要Webpack在我们改变文件的同时监听改变并重新构建:

  1. webpack --watch

如果想要使用自定义名称的Webpack的配置文件:

  1. webpack --config myconfig.js

打包

Example 1

Official Dependency Tree

Webpack被正式称为一个模块打包工具。如果你想对模块和模块化打包有深入且易于理解的解释,肯定要看看这两篇文章:
here
and @preethikasireddy/javascript-modules-part-2-module-bundling-5020383cf306#.lfnspler2">here.
我们保持它的简单。它的工作方式是,我们制定一个单独的文件为入口点。这个文件将是根节点。然后我们每次都需要引用一个文件都是被添加到节点上的文件。当我们运行Webpack时,所有这些文件会被打包成一个文件。

下面是一个简单的例子:

Dependency Tree

鉴于这张图片,我们可以有目录:

  1. MyDirectory
  2. |- index.js
  3. |- UIStuff.js
  4. |- APIStuff.js
  5. |- styles.css
  6. |- extraFile.js

这可能是你的文件的内容

  1. // index.js
  2. require('./styles.css')
  3. require('./UIStuff.js')
  4. require('./APIStuff.js')
  5. // UIStuff.js
  6. var React = require('React')
  7. React.createClass({
  8. // stuff
  9. })
  10. // APIStuff.js
  11. var fetch = require('fetch') // fetch polyfill
  12. fetch('https://google.com')
  1. /* styles.css */
  2. body {
  3. background-color: rgb(200, 56, 97);
  4. }

当我们运行Webpack时,我们将得到一个节点内容的打包,其中没有extraFile.js,虽然文件都在同一个目录,结果却不会打包是因为我们没有引用。

打包的js文件bundle.js将会像这样

  1. // contents of styles.css
  2. // contents of UIStuff.js + React
  3. // contents of APIStuff.js + fetch

被打包压缩的东西只会是我们在文件中明确要求的东西。

加载器

正如你可能注意到的,我在上面的例子做了一些奇怪的事情。我引用了一个css文件到我需要的javascript文件中。

这是很酷的,这是Webpack的一桩非常有趣的功能,就是我们可以引用不仅仅只是javascript文件。

这种功能在Webpack中被叫加载器。使用这些加载器,我们可以引用csshtmlpng文件。

如下上面图片中一样,我这样做:

  1. // index.js
  2. require('./styles.css')

如果我引用了the style-loaderthe css-loader在我跑得webpack配置,这不仅是完全有效的,而且也会应用css到我的页面中来。

这只是我们可以使用Webpack的许多加载器(loader)的其中一个简单例子。

插件

插件,正如其名,可以为Webpack添加额外的功能。一个经常使用的插件UglifyJsPlugin
可以缩小你的javascript代码。我们等下会讨论怎么使用。

你的配置文件

Webpack不会在盒子外自动运行,所以你必须按你的需要。为此,你需要创建一个

  1. webpack.config.js

这是Webpack默认的配置名称,如果你选择使用自定义的名称,必须使用 --config来自定该特殊的配置文件名称。

一个小例子

Example 2

你的目录结构是这样的:

  1. MyDirectory
  2. |- dist
  3. |- src
  4. |- index.js
  5. |- webpack.config.js

然后我们可以使用一个很小的webpack配置:

  1. // webpack.config.js
  2. var path = require('path')
  3. module.exports = {
  4. entry: ['./src/index'], // file extension after index is optional for .js files
  5. output: {
  6. path: path.join(__dirname, 'dist'),
  7. filename: 'bundle.js'
  8. }
  9. }

我们来一个一个看这些新特性:

  • entry - 打包入口,我们想要打包部分文件。这个配置是一个数组,因为Webpack允许有个入口文件,如果我们想要生成多个打包文件。

  • output - Webpack的输出配置

当我们运行Webpack时,这就会创建一个叫bundle.js的文件在我们的dist文件夹。

引入插件

Example 3

想象一下,我们使用Webpack来一起打包我们全部的文件,现在我们知道它们全部打包在一起会有900KB。去缩小我们的捆版好的打包文件是一个问题。因为我们需要使用一个之前提到过的叫UglifyJsPlugin的插件。

此外,我们必须得在本地安装Webpack以使用插件。

  1. npm install --save-dev webpack

现在我们可以引入webpack来缩小我们的代码。

  1. // webpack.config.js
  2. var path = require('path')
  3. var webpack = require('webpack')
  4. module.exports = {
  5. entry: ['./src/index'],
  6. output: {
  7. path: path.join(__dirname, 'dist'),
  8. filename: 'bundle.js'
  9. },
  10. plugins: [
  11. new webpack.optimize.UglifyJsPlugin({
  12. compressor: {
  13. warnings: false,
  14. },
  15. })
  16. ]
  17. }

我们来看看这个新特性:

这一次,当我们运行Webpack时,现在因为我们有UglifyJsPlugin插件,这可以通过比如删除所有空格,可以减少我们本来的900KB文件到200KB。

我们还可以添加OccurenceOderPlugin插件OccurenceOrderPlugin

通过计算模块出现次数来分配模块。这个经常被使用可以较快地获得模块。这使得模块可以预读,建议这样可以减少总文件大小。

事实上,我们是不知道它背后的机制的,但是在目前的测试的webpack2中已经被默认包含了,所以我们不妨引用它。

  1. // webpack.config.js
  2. var path = require('path')
  3. var webpack = require('webpack')
  4. module.exports = {
  5. entry: ['./src/index'],
  6. output: {
  7. path: path.join(__dirname, 'dist'),
  8. filename: 'bundle.js'
  9. },
  10. plugins: [
  11. new webpack.optimize.UglifyJsPlugin({
  12. compressor: {
  13. warnings: false,
  14. },
  15. }),
  16. new webpack.optimize.OccurenceOrderPlugin()
  17. ]
  18. }

所以现在我们已经谢了一个配置,使用我们能够缩小和打包我们的javascript。这个打包文件可以复制并粘贴到另一个项目文件中,并被扔进一个<script>标签中。你可以跳到这个结论,如果你只在乎这个使用webpack仅仅是javascript的基础知识。

一个更完整例子

另外,因为webpack还能做不仅仅只是关于javascript的工作,我们可以避免复制粘贴和使用webpack管理我们的整个项目。

在接下来的部分中,我们将使用webpack来创建一个非常简单的网站。如果你希望跟随这个例子,可以创建一个如下的目录结构:

  1. MyDirectory
  2. |- dist
  3. |- src
  4. |- index.js
  5. |- index.html
  6. |- styles.css
  7. |- package.json
  8. |- webpack.config.js

内容

  1. 介绍加载器(loaders) -我们将增加加载器,来允许我们添加css到我们的打包中。
  2. 添加更多插件(plugins) - 我们将添加一个插件来帮助我们 创建/使用 一个HTML文件。
  3. 开发服务器 - 我们会把我们的webpack配置分割独立的开发和生产文件。然后使用webpack–dev-server 来查看我们的网站,使它能够HMR。
  4. 开始编码 - 我们将会写一些javascript

介绍装载器

Example 4

在前面的教程中,我提到了加载器(#loaders).。这将有助于在我们的代码中引用非js文件。在这种情况下,我们将需要style-loadercss-loader。首先 我们需要安装这些加载器:

  1. npm install --save-dev style-loader css-loader

现在,安装好了它们之后我们可以调整我们的的配置把这些css的加载器包含进来:

  1. // webpack.config.js
  2. var path = require('path')
  3. var webpack = require('webpack')
  4. module.exports = {
  5. entry: ['./src/index'],
  6. output: {
  7. path: path.join(__dirname, 'dist'),
  8. filename: 'bundle.js'
  9. },
  10. plugins: [
  11. new webpack.optimize.UglifyJsPlugin({
  12. compressor: {
  13. warnings: false,
  14. },
  15. }),
  16. new webpack.optimize.OccurenceOrderPlugin()
  17. ],
  18. module: {
  19. loaders: [{
  20. test: /\.css$/,
  21. loaders: ['style', 'css']
  22. }]
  23. }
  24. }

我们来理解这些新特性:

  • module - 影响我们的文件的选项
    • loaders - 一个我们为应用程序指定的一个加载器的数组
      • test - 一个正则表达式,将加载器和文件匹配
      • loaders - 该正则匹配的文件应用的加载器

当你运行WebPACK,如果你rquire一个.css文件,那么我们将请用stylecss的加载器,来打包添加CSS

如果没有加载器,那么我们会得到一个这样的错误:

  1. ERROR in ./test.css
  2. Module parse failed: /Users/Developer/workspace/tutorials/webpack/part1/example1/test.css
  3. Line 1: Unexpected token {
  4. You may need an appropriate loader to handle this file type.

可选

如果你想使用SCSS来代替CSS,你需要运行:

  1. npm install --save-dev sass-loader node-sass webpack

你的加载器将改写成这个样子:

  1. {
  2. test: /\.scss$/,
  3. loaders: ["style", "css", "sass"]
  4. }

这个过程和LESS差不多。

识别的一个重要的点是,这些加载器需要指定一个特定顺序。在上面的例子中,这个sass-loader是优先作用于我们的.scss文件,然后是css-loader,最后是style-loader。正如所见,该模式是应用加载器的优先级从右向左的。

添加更多的插件

Example 5

现在我们有基础的构建在我们的网站,我们需要一个实际的页面样式。我们将会通过html-webpack-plugin插件,来生成一个HTML页面或者一个已经存在的。我们将使用一个已存在的文件。

首先我们安装插件:

  1. npm install --save-dev html-webpack-plugin@2

然后我们需要添加它到我们的配置

  1. // webpack.config.js
  2. var path = require('path')
  3. var webpack = require('webpack')
  4. var HtmlWebpackPlugin = require('html-webpack-plugin')
  5. module.exports = {
  6. entry: ['./src/index'],
  7. output: {
  8. path: path.join(__dirname, 'dist'),
  9. filename: 'bundle.js'
  10. },
  11. plugins: [
  12. new webpack.optimize.UglifyJsPlugin({
  13. compressor: {
  14. warnings: false,
  15. },
  16. }),
  17. new webpack.optimize.OccurenceOrderPlugin(),
  18. new HtmlWebpackPlugin({
  19. template: './src/index.html'
  20. })
  21. ],
  22. module: {
  23. loaders: [{
  24. test: /\.css$/,
  25. loaders: ['style', 'css']
  26. }]
  27. }
  28. }

这一次,当我们运行webpack,因为我们指定了一个HtmlWebpackPlugin 的插件和./src/index.html模版,他将生成一个index.html的文件在我们的dist文件夹有./src/index.html的内容。

如果我使用index.html作为模版,并且他是空的。实际上现在就是一个很好填充它的机会。

  1. <html>
  2. <head>
  3. <title>Webpack Tutorial</title>
  4. </head>
  5. <body>
  6. <h1>Very Website</h1>
  7. <section id="color"></section>
  8. <button id="button">Such Button</button>
  9. </body>
  10. </html>

请注意,我们没有把bundle.js作为一个<script>标签放进我们的html当中。这个插件实际上会自动帮我们做这个。如果你插入了script标签,你将会导致你相同的代码加载两次。

接下来让我们在style.css中添加一些基本的样式:

  1. h1 {
  2. color: rgb(114, 191, 190);
  3. text-align: center;
  4. }
  5. #color {
  6. width: 300px;
  7. height: 300px;
  8. margin: 0 auto;
  9. }
  10. button {
  11. cursor: pointer;
  12. display: block;
  13. width: 100px;
  14. outline: 0;
  15. border: 0;
  16. margin: 20px auto;
  17. }

开发服务器

Example 6

现在我们要在浏览器中看到我们的网站,他需要一个网络服务器来服务我们的代码。方便的是webpack自带的webpack-dev-server,你需要在本地和全局都安装。

  1. npm install -g webpack-dev-server
  2. npm install --save-dev webpack-dev-server

开发服务器是一个非常有用的资源让你的王章看起来想在浏览器中,以便更快的开发。默认情况下,你可以访问http://localhost:8080来访问。不幸的是,如果箱子的热加载功能不工作,则需要一些更多的配置。

这是一个很好的实例来分割我们的webpack配置在一个用于开发一个用于生产。因为我们我们这是一个简单的教程,这不会是一个巨大的差异,但是这是一个介绍webpack差异配置性的介绍。我们将命名他们为webpack.config.dev.jswebpack.config.prod.js

  1. // webpack.config.dev.js
  2. var path = require('path')
  3. var webpack = require('webpack')
  4. var HtmlWebpackPlugin = require('html-webpack-plugin')
  5. module.exports = {
  6. devtool: 'cheap-eval-source-map',
  7. entry: [
  8. 'webpack-dev-server/client?http://localhost:8080',
  9. 'webpack/hot/dev-server',
  10. './src/index'
  11. ],
  12. output: {
  13. path: path.join(__dirname, 'dist'),
  14. filename: 'bundle.js'
  15. },
  16. plugins: [
  17. new webpack.HotModuleReplacementPlugin(),
  18. new HtmlWebpackPlugin({
  19. template: './src/index.html'
  20. })
  21. ],
  22. module: {
  23. loaders: [{
  24. test: /\.css$/,
  25. loaders: ['style', 'css']
  26. }]
  27. },
  28. devServer: {
  29. contentBase: './dist',
  30. hot: true
  31. }
  32. }

变化

  1. 开发配置省略了优化(optimize)配置,因为在我们不断地重构改造我们的项目时,这些优化配置都是不必要的开销。所以开发环境的配置没有webpack.optimize插件。

  2. 开发配置是开发服务器所必要的配置,你可以阅读更多关于这里

总结:

  • entry: 两个2新的连接服务器的入口配置到浏览器允许HMR。
  • devServer
    • contentBase: 服务器文件位置
    • hot: 是否使用HMR

生产环境的配置变化不大:

  1. // webpack.config.prod.js
  2. var path = require('path')
  3. var webpack = require('webpack')
  4. var HtmlWebpackPlugin = require('html-webpack-plugin')
  5. module.exports = {
  6. devtool: 'source-map',
  7. entry: ['./src/index'],
  8. output: {
  9. path: path.join(__dirname, 'dist'),
  10. filename: 'bundle.js'
  11. },
  12. plugins: [
  13. new webpack.optimize.UglifyJsPlugin({
  14. compressor: {
  15. warnings: false,
  16. },
  17. }),
  18. new webpack.optimize.OccurenceOrderPlugin(),
  19. new HtmlWebpackPlugin({
  20. template: './src/index.html'
  21. })
  22. ],
  23. module: {
  24. loaders: [{
  25. test: /\.css$/,
  26. loaders: ['style', 'css']
  27. }]
  28. }
  29. }

我此次增加了一个全新的属性包括开发配置和生产配置:

  • devtool - 这是一个调试工具。基本上,当你出现一个错误,他将帮助你看清楚关于错误的信息,比如chrome开发者控制台。不同的是source-mapcheap-eval-source-map更难从文档中搜集。我肯以肯定的说source-map对生产意味着是一个很大的开销。而cheap-eval-source-map相较是更少的开销,意味着只能开发中使用。

运行dev开发服务器必须要运行

  1. webpack-dev-server --config webpack.config.dev.js

构建生产代码我们需要运行

  1. webpack --config webpack.config.prod.js

T让我们使用得更简单一点,我们现在可以使用一个简单的任务让package.json运行,我们不需要打任何命令。

我们将脚本属性添加到配置

  1. // package.json
  2. {
  3. //...
  4. "scripts": {
  5. "build": "webpack --config webpack.config.prod.js",
  6. "dev" : "webpack-dev-server --config webpack.config.dev.js"
  7. }
  8. //...
  9. }

我们可以运行这些命令:

  1. npm run build
  2. npm run dev

现在我们可以通过运行npm run dev来看到你美丽的网站,导航到http://localhost:8080.

注意: 当我测试这部门时我发现服务器不会实时重载我修的index.html文件。解决这个问题的办法是html-reload。这是一些有用的信息,包括一些webpack的配置选项,我推荐你看,我把它们分开了,因为我觉得它延长了教程可能导致很琐碎。

开始编码

Example 7

人们之所以对webpack看起来很紧张是因为我们需要懂得这一些过程才能让我们开始写javascript代码。好了,现在我们已经完成了这个过程,到了教程的高潮部分。

万一你还没有:运行npm run dev,并导航到http://localhost:8080。构建一个实时重载服务器不能显示。每一次都可以节省你修改你项目任何部分时的时间,浏览器将会重新加载来显示你的改变。
我们也需要一个 新的npm package来说明你可以使用他们在你的前端当中。

  1. npm install --save pleasejs

PleaseJs是一个随机颜色生成器,我们要安装我们的按钮来改变div的颜色。

  1. // index.js
  2. // Accept hot module reloading
  3. if (module.hot) {
  4. module.hot.accept()
  5. }
  6. require('./styles.css') // The page is now styled
  7. var Please = require('pleasejs')
  8. var div = document.getElementById('color')
  9. var button = document.getElementById('button')
  10. function changeColor() {
  11. div.style.backgroundColor = Please.make_color()
  12. }
  13. button.addEventListener('click', changeColor)

有趣的是,为了使实时加载模块工作,我们需要包含这些代码:

  1. if (module.hot) {
  2. module.hot.accept()
  3. }

在一个模块或者这个模块的父级中。

我们已经完成了!

注意: 你可能到了你的css被应用事后的延迟,你可能会讨厌你的css被包含在js文件当中。我留出一个例子,css-extract(提取),描述如何把你的css放进一个不同的文件中。

结论

我希望能帮助到你们。

webpack首先是一个模块打包器。它是一个高度模块化和易用的工具,在事实上,他不限用于ES6和React。

Now given that

因为它们都是最常见的用例。

最后的想法

祝贺你!你做了一个按钮去改变div的颜色!webpack是不是很强大?

是的。但是,如果你做的是一个按钮,去改变div的颜色,他是不可能匹配写配置的价值的。如果你这样做,你可能会…很累 :anguished: