IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    [译]Webpack 2和模板打包的初学者指南(上)

    minyillee发表于 2017-03-17 15:57:11
    love 0

    Webpack是模块打包工具。

    Webpack已成为现代Web开发最重要的工具之一。首先,它是一个JavaScript模板打包工具,他能转换所有的前端资源,如HTML和CSS,甚至图片。它可以让你更好地控制你应用程序的HTTP请求数量,并允许你使用其他的风格资源(如Jade,Sass和ES6)。Webpack还允许你轻松地从npm上使用软件包。

    本文面向刚刚接触Webpack的初学者,主要介绍Webpack的初始设置和配置、模板、加载程序、插件、代码拆分和热模块替换。如果你发现视频教程对你更有帮助,我强烈推荐Glen Maddern’s的Webpack from First Principles作为一个起点,了解是什么使得Webpack那么特别。

    接下来,你需要有Node.js installed环境,你也可以 download the demo app from our Github repo

    Setup

    让我们用npm新建一个项目并初始化,安装Webpack:

    mkdir webpack-demo
    cd webpack-demo
    npm init -y
    npm install webpack@beta --save-dev
    mkdir src
    touch index.html src/app.js webpack.config.js

    编辑这些文件:

    <!-- index.html -->
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>Hello webpack</title>
      </head>
      <body>
        <div id="root"></div>
        <script src="dist/bundle.js"></script>
      </body>
    </html>
    // src/app.js
    const root = document.querySelector('#root')
    root.innerHTML = `<p>Hello webpack.</p>`
    // webpack.config.js
    const webpack = require('webpack')
    const path = require('path')
    
    const config = {
      context: path.resolve(__dirname, 'src'),
      entry: './app.js',
      output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
      },
      module: {
        rules: [{
          test: /\.js$/,
          include: path.resolve(__dirname, 'src'),
          use: [{
            loader: 'babel-loader',
            options: {
              presets: [
                ['es2015', { modules: false }]
              ]
            }
          }]
        }]
      }
    }
    
    module.exports = config

    上面的配置是一个常见的起点,它指示webpack将我们的入口点src/app.js编译输出到/dist/bundle.js中,所有的.js文件将通过Babel从ES2015转换到ES5。

    为了让它运行,我们需要安装三个包:babel-core,Webpack模块和资源转换器——babel-loader和预设的babel-preset-es2015。
    { modules: false }启用Tree Shaking把我们包中不被使用的输出删除,从而降低文件大小。

    npm install babel-core babel-loader babel-preset-es2015 --save-dev

    最后,将package.json文件中的script部分替换为:

    "scripts": {
      "start": "webpack --watch",
      "build": "webpack -p"
    },

    在命令行中运行npm start来监视模式启动webpack——每当我们src目录中的.js文件更改时,都会重新编译输出到bundle中。控制台中的输出会告诉我们正在创建的包,重要的是要注意包的数量和大小。
    图片描述
    现在你应该可以在浏览器中加载index.html,并使用“Hello webpack”。

    open index.html

    打开dist/bundle.js查看webpack做了什么,顶部是webpack的模板引导代码,底部是我们的模板。也许你对这不会有很深刻的印象,但如果你跟着进度学到了这里,你可以运用ES6 module和webpack生产一个能在所有浏览器中运行的用于生产的包。

    输入命令行Ctrl + C停止webpack,并运行npm run build用于在生产模式下编译我们的包。

    请注意,此时包的大小已经从2.16kB下降到585字节。
    再查看一下dist/bundle.js,你会看到一个丑恶的代码,我们的包已经压缩或丑化(uglify/minify),代码运行效果是一样的,但它并不符合最少字符需要。

    Modules

    开箱即用的webpack知道如何使用各种格式的JavaScript模块,最显著的两种是:

    • ES2015的import语句

    • CommonJS的require()语句

    我们可以通过安装lodash并从app.js导入它来测试一下:

    npm install lodash --save
    // src/app.js
    import {groupBy} from 'lodash/collection'
    
    const people = [{
      manager: 'Jen',
      name: 'Bob'
    }, {
      manager: 'Jen',
      name: 'Sue'
    }, {
      manager: 'Bob',
      name: 'Shirley'
    }, {
      manager: 'Bob',
      name: 'Terrence'
    }]
    const managerGroups = groupBy(people, 'manager')
    
    const root = document.querySelector('#root')
    root.innerHTML = `<pre>${JSON.stringify(managerGroups, null, 2)}</pre>`

    运行npm start启动webpack,刷新一下index.html页面,你可以看到按manager排序的人数组。

    让我们来把people数组移动到自己的people.js文件中:

    // src/people.js
    const people = [{
      manager: 'Jen',
      name: 'Bob'
    }, {
      manager: 'Jen',
      name: 'Sue'
    }, {
      manager: 'Bob',
      name: 'Shirley'
    }, {
      manager: 'Bob',
      name: 'Terrence'
    }]
    
    export default people

    我们可以使用相对路径简单地从app.js导入它。

    // src/app.js
    import {groupBy} from 'lodash/collection'
    import people from './people'
    
    const managerGroups = groupBy(people, 'manager')
    
    const root = document.querySelector('#root')
    root.innerHTML = `<pre>${JSON.stringify(managerGroups, null, 2)}</pre>`

    注意:没有相对路径的导入,如lodash/collection是从npm安装到node_modules的模块,自己写的模块是需要加一个相对路径,如./people来告诉webpack区分好各个模块。

    Loaders

    我们已经介绍了babel-loader——很多loders之一,通过配置用于告诉webpack当遇到不同文件类型的import时要怎么做。你可以把过个loader整合在一起,我们可以通过从JavaScript中导入Sass来很好地了解这是如何工作的。

    Sass

    这种转换涉及到三个独立的loader和node-sass库:

    npm install css-loader style-loader sass-loader node-sass --save-dev

    在webpack.config.js中给我们的.scss文件添加新的配置规则:

    // webpack.config.js
    rules: [{
      test: /\.scss$/,
      use: [
        'style-loader',
        'css-loader',
        'sass-loader'
      ]
    }, {
      // ...
    }]

    注意:每次更改webpack.config.js中的任何规则时,都需要使用Ctrl+C和npm start命令重新启动项目。

    loader数组会以相反的顺序执行:

    • sass-loader 把Sass转换成CSS

    • css-loader 把CSS解析到JavaScript中,并分解所有的依赖关系

    • style-loader 把我们的CSS输出到文档中的<style>标签中

    你可以把它当做函数调用,一个loader的输出会作为input输入下一个。

    styleLoader(cssLoader(sassLoader('source')))

    让我们来添加一个Sass源文件:

    /* src/style.scss */
    $bluegrey: #2B3A42;
    
    pre {
      padding: 20px;
      background: $bluegrey;
      color: #dedede;
      text-shadow: 0 1px 1px rgba(#000, .5);
    }

    你现在可以从JavaScript中直接请求Sass,从app.js的头部引入:

    // src/app.js
    import './style.scss'
    
    // ...

    刷新一下index.html你就会看到刚添加的样式了。

    CSS in JS

    我们刚刚从JavaScript中导入了Sass文件,作为一个模块。

    打开dist/bundle.js文件并搜索“pre {。事实上,我们的Sass已经被编译成一个CSS字符串,并保存为我们的bundle中的一个模板。当我们在JavaScript中导入此模板时,style-loader会将该字符串输出到嵌入的<style>标签中。

    我知道你肯定是在想,为什么会这样?

    我不会在这里深入讨论这个话题,但是你可以从以下几个方面考虑:

    • 你可能希望包含在项目中的JavaScript组件依赖于其他资源(HTML、CSS、Images、SVG)来正常运行,如果这些资源可以整合在一起,那么导入和使用就会容易很多。

    • 消除死代码:当JS代码不需要导入JS组件时,将不再导入CSS,生成的bundle只会包含执行某些操作的代码。

    • CSS模块:CSS的全局命名空间使得开发者很难确信CSS的一个更改不会产生任何的副作用。CSS modules通过在默认情况下使CSS local和暴露在JavaScript下使用的唯一类名来改变这一问题。

    • 通过巧妙的方法打包/拆分代码,来较少HTTP请求数量。

    Images

    我们能够看到的最后一个关于loader的例子是使用url-loader处理图片。

    在标准的HTML文档中,当浏览器遇到一个<img>标签或background-image属性时会抓取图片。使用webpack,当遇到小图片的时候,你可以通过将图片源作为字符串存储在JavaScript中来优化小图片,这样,你预加载它们,浏览器就不用为了提取它们而发起单独的请求了。

    npm install file-loader url-loader --save-dev

    添加一个加载图片的规则:

    // webpack.config.js
    rules: [{
      test: /\.(png|jpg)$/,
      use: [{
        loader: 'url-loader',
        options: { limit: 10000 } // Convert images < 10k to base64 strings
      }]
    }, {
      // ...
    }]

    重新运行项目:Ctrl + C和npm start

    使用下面命令下载test image

    curl https://raw.githubusercontent.com/sitepoint-editors/webpack-demo/master/src/code.png --output src/code.png

    你现在可以在app.js的头部导入图片源:

    // src/app.js
    import codeURL from './code.png'
    const img = document.createElement('img')
    img.src = codeURL
    img.style.backgroundColor = "#2B3A42"
    img.style.padding = "20px"
    img.width = 32
    document.body.appendChild(img)
    
    // ...

    这将包括一张图片,其中src属性包含图片本身的data URL:

    <img src="data:image/png;base64,iVBO..." style="background: #2B3A42; padding: 20px" width="32">

    此外,由于使用url()引用的css-loader图片也通过url-loader运行,这就好像直接在CSS中嵌入它们。

    /* src/style.scss */
    pre {
      background: $bluegrey url('code.png') no-repeat center center / 32px 32px;
    }

    编译成为:

    pre {
        background: #2b3a42 url("data:image/png;base64,iVBO...") no-repeat scroll center center / 32px 32px;
    }

    Modules to Static Assets

    你现在应该可以看到loders是如何帮助在资源间建立一个依赖关系树的,这是webpack首页上的图片展示:
    图片描述

    尽管JavaScript是入口点,但webpack注意到你的其他资源(如HTML、CSS和SVG)都具有各自的依赖关系——这些依赖关系是应该视为构建过程的一部分的。

    作者:Mark Brown
    原文链接:A Beginner’s Guide to Webpack 2 and Module Bundling



沪ICP备19023445号-2号
友情链接