webpack3入门

webpack3 入门

安装

  1. 安装 nodejs

    打开命令行终端, 输入一下命令, 如果出现安装版本号 ( 如: v8.11.2 ), 说明安装成功

    $ node -v

  2. 安装 webpack

    在命令行终端上输入以下命令, 全局安装 webpack:

    $ npm install webpack@3.6.0 -g

    npm 是 nodejs 管理插件的工具, install 表示安装, @3.6.0 表示安装指定的 webpack 版本, 如果不加@xxx, 则会默认安装最新版本; -g 表示全局安装, 这样就会把可执行文件 webpack 放到 bin 目录下, 以后就可以直接运行 webpack 目录了.

    在 终端上输入以下命令, 如果输出 webpack 的版本号 ( 如 3.6.0 ), 说明安装成功.

    webpack -v

实现 hello world

  1. 使用 npm init 初始化项目

    新建一个 hello-webpack 文件夹, 在该文件目录下打开终端, 输入下面的命令:

    $ npm init

    之后会看到一些提示信息, 不用管, 直接全部回车, 最后, 会发现 hello-webpack 目录下多了一个名为 package.json 的文件, 内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    {
    "name": "hello-webpack",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
    },
    "author": "",
    "license": "ISC"
    }

    整个 json 文件的内容很简单, 主要显示了这个项目的名称、版本、作者、协议等信息。

  2. 集成 webpack

    目前项目是空的, 没有任何东西, 现在需要把 webpack 集成进来, 让这个项目可以用这个 webpack. 在终端上输入如下命令:

    $ npm install –save-dev webpack@3.8.1

    安装成功之后会发现 package.json 文件中多了下面这几行代码:

    1
    2
    3
    "devDependencies": {
    "webpack": "^3.8.1"
    }
  3. 创建 JavaScript 文件

    创建一个 src 目录, 然后在该目录下创建 app.js 文件, 内容如下:

    console.log(“Hello World”);

  4. 把 webpack 用起来

    现在要把刚才的 js 文件用 weback 编译一下

    新建一个 dist 目录, 编译后的文件就报错在 dist 文件中

    在终端中输入下面的命令:

    $ webpack ./src/app.js ./dist/app.bundle.js

    这个命令的意思就是把 ./src/app.js 作为源文件, 把编译后的结果放到 ./dist/app.bundle.js 文件中.

    编译成功后, 我们可以在 dist 目录下看到 app.bundle.js 文件.

  5. webpack 的其他用法

    上面介绍的只是 webpack 一个最简单的功能, 下面介绍一些其他的用法:

    • –watch

      首先, 在开发环境中, 如果要一边改, 一遍看转换效果, 可以使用 -watch 功能实现, 命令如下:

      $ webpack –watch ./src/app.js ./dist/app.bundle.js

      现在改动 src/app.js 文件的内容, 就可以看到 dist/app.bundle.js 实时发生变化啦!

    • -p

      之前编译的 app.bundle.js 文件大约有 74 行代码, 差不多 2k 多的大小.

      在生产环境, 或线上, 我们不希望这么大的体积, 如果要发布到线上环境, 我们要把它压缩一下.

      可以使用 -p 功能来实现: 命令如下

      $ webpack -p ./src/app.js ./dist/app.bundle.js

      编译成功后, 可以看到 app.bundle.js 文件的体积比之前小了一些, 只有 506 个字节.

webpack 配置文件 webpack.config.js

虽然在命令行中使用 webpack 命令可以实现 webpack 的功能, 但我们一般是不这么做的, 我们使用配置文件来处理

  1. 创建配置文件 webpack.config.js

    在 package.json 同级目录下新建 webpack.config.js 文件, 内容如下:

    1
    2
    3
    4
    5
    6
    module.exports = {
    entry: './src/app.js',
    output: {
    filename: './dist/app.bundle.js'
    }
    }

    entry 表示输入文件, output 表示输出的目标文件.

    直接在终端上输入 webpack 就可以编译文件了. webpack 命令会去找 webpack.config.js 文件, 并读取它的内容, 最后进行相应的处理.

  2. 改造 package.json 的 script 部分

    我们可以把一些常用的命令脚本, 比如经常要用到的 webpack 命令放到 package.json 中

    修改 package.json 文件的 scripts 部分, 代码如下:

    1
    2
    3
    4
    5
    6
    7
    "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack -d --watch",
    "prod": "webpack -p"
    },

    // -d 这个参数的意思是说包含 source maps, 就是让你在用浏览器调试的时候, 可以很方便地定位到源文件

    分别在命令行运行如下命令:

    $ npm run dev

    $ npm run prod

    你会发现 npm run dev 和 webpack -d –watch 的效果是一样的. 这样做有如下好处:

    • 简单维护, 所有的命令都放在一起了, 也能方便查看
    • 别人下载了你的源码, 一查看 package.json 就能知道怎么运行这个项目

使用第一个 webpack 插件 html-webpack-plugin

  1. 创建 index.html 文件

    首先, 在 dist 目录下创建 index.html 文件, 内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Project</title>
    </head>
    <body>

    <script src="app.bundle.js"></script>
    </body>
    </html>

    这样的 index.html 文件太死了, 连 js 文件都写死了, 有时候引用的 js 文件是动态变化的, 比如这样的:

    1
    2
    3
    4
    <script src="app.bundle1.js"></script>
    <script src="app.bundle2.js"></script>
    <script src="app.bundle3.js"></script>
    ... ...

    还有一种情况, 有时候为了更好的 cache 处理, 文件名还带着 hash, 例如这样的:

    main.9046fe2bf8166cbe16d7.js

    这个 hash 是文件的 md5 值, 随着文件的内容而变化, 总不能每变化一次就改一下 index.html 文件吧, 效率太低了.

    下面我们使用 webpack 的 html-webpack-plugin 来处理这个问题.

  2. html-webpack-plugin

    • 安装

      npm install html-webpack-plugin –save-dev

      安装成功后, package.json 文件的 “devDependencies” 选项会多出来一行 “html-webpack-plugin”: “^2.30.1”

    • 使用

      现在把之前的 dist/index.html 文件先删除掉, 使用 html-webpack-plugin 插件来自动生成它.

      修改 webpack.config.js 文件, 如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      const HtmlWebpackPlugin = require('html-webpack-plugin');

      module.exports = {
      entry: './src/app.js',
      output: {
      path: __dirname + '/dist',
      filename: './dist/app.bundle.js'
      },
      plugins: [
      new HtmlWebpackPlugin(),
      ]
      };

      最后, 运行一下 npm run dev 命令, 会发现在 dist 目录下生成了 index.html 文件.

  3. 更好的使用 html-webpack-plugin

    打开 index.html 文件可以看到, 连标题 Webpack App 都自动生成了. 如果不想要这样默认的标题怎么办呢?

    要改变 title 很简单, HtmlWebpackPlugin 这个方法可以传入很多参数, 下面这样就可以解决这个问题

    1
    2
    3
    4
    5
    6
    ...	...
    plugins: [
    new HtmlWebpackPlugin({
    title: "hello world"
    }),
    ]

    重新生成 index.html 文件, 可以看到标题已经改变了.

    有时候我们要让 index.html 根据我们的意愿来生成. 就是说它的内容是我们自己定的. 可以使用 html-webpack-plugin 的 template 功能来实现, 修改 webpack.config.js 如下:

    1
    2
    3
    4
    5
    6
    7
    ...	...
    plugins: [
    new HtmlWebpackPlugin({
    title: "hello world",
    template: './src/index.html',
    }),
    ]

    接着新建 src/index.html 文件, 内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Hello World</title>
    </head>
    <body>

    </body>
    </html>

    运行 npm run dev 生成的 dist/index.html 文件内容如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Hello World</title>
    </head>
    <body>

    <script type="text/javascript" src="./dist/app.bundle.js"></script></body>
    </html>

    下面在看看其他几个参数及结果

    • filename: “index.html” 默认情况下生成的 html 文件叫 index.html, 如果不想叫这个名字, 可以修改

    • minify: {

      ​ collapseWhitespace: true,

      }

      // 这个可以把生成的 index.html 文件的内容的没用的空格去掉, 减少空间.

    • hash: true 为了更好的 cache, 可以在文件名后加个 hash. 如: src=”./dist/app.bundle.js?0b5552db1f811f9c43e0”

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      //	最后的 webpack.config.js 内容如下所示
      ... ...
      plugins: [
      new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
      minify: {
      collapseWhitespace: true,
      },
      hash: true,
      }),
      ]

      html-webpack-plugin 还有很多其他用法和选项, 参考官方文档.

使用 loader 处理 CSS 和 Sass

  1. 什么是 loader

    官方的解释是这样的:

    loader 用于对模块的源代码进行转换。loader 可以使你在 import 或”加载”模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!

    其实就是处理文件的, 比如把 Scss 转化成 CSS, TypeScript 转化成 JavaScript 等.

  2. 用 css-loader 和 style-loader 处理 CSS

    • 新建 app.css 文件并在 app.js 文件中引入

      1
      2
      3
      4
      5
      6
      7
      8
      //	src/app.css
      body {
      background: pink;
      }

      // src/app.js
      import css from './app.css';
      console.log("hello world");

      如果现在运行 npm run dev 或者 webpack 命令, 会出现 You may need an appropriate loader to handle this file type. 这样的报错信息.

      意思是说, 默认情况下, webpack 处理不了 CSS 的东西, 你需要一个 loader 来处理这个类型的文件.

    • 安装 css-loader 和 style-loader

      $ npm install css-loader style-loader –save-dev

      在 webpack.config.js 文件中进行配置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      module.exports = {
      ... ...
      module: {
      rules: [
      {
      // 意思是说, 当检测到 .css 结尾的文件时, 使用 style-loader 和 css-loader 来处理
      test: /\.css$/,
      use: ['style-loader', 'css-loader']
      }
      ]
      }
      }

      现在再使用 npm run dev 编译一下, 打开 dist/index.html 可以看到背景色已经变成了粉色了.

      编译出来的 app.bundle.js 是包含 CSS 内容的.

  3. 用 sass-loader 把 SASS 编译成 CSS

    • 修改文件

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      //	src/app.scss	将 src/app.css 改名为 src/app.scss
      body {
      background: pink;
      p {
      color: red;
      }
      }

      // src/index.html
      <body>
      <p>Hello world</p>
      </body>

      // src/app.js
      import css from './app.scss';
    • 安装 sass-loader 和node-sass

      $ npm install sass-loader node-sass –save-dev

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      //	webpack.config.js
      ... ...
      module: {
      rules: [
      ... ...
      {
      test: /\.scss$/,
      use: [ 'style-loader', 'css-loader', 'sass-loader' ]
      }
      ]
      }

      打包完成后可以看到 p 标签内的文本变成了红色的

  4. 使用 extract-text-webpack-plugin 把 CSS 分离成文件

    有时候我们要把 SASS 或者 CSS 处理好后, 放到一个 CSS 文件中, 可以使用这个插件来实现

    $ npm install extract-text-webpack-plugin –save-dev

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //	webpack.config.js
    const ExtractTextPlugin = require('extract-text-webpack-plugin');
    module.exports = {
    ... ...
    plugins: [
    ... ...
    new ExtractTextPlugin('style.css')
    ],
    module: {
    rules: [
    {
    test: /\.scss$/,
    use: ExtractTextPlugin.extract({
    fallback: 'style-loader',
    use: [ 'css-loader', 'sass-loader' ]
    })
    }
    ]
    }
    }

    这样编译后就在 dist 目录下生成了 style.css 文件.

    并且可以在 index.html 文件中看到 style.css 是通过 link 的方式引入进来的.

初识 webpack-dev-server

一般来说, webpack-dev-server 这个插件在开发环境都会使用到.

我们之前使用 webpack -d –watch 来在开发环境下编译静态文件, 这个功能完全可以使用 webpack-dev-server 来代替.

除此之外, webpack-dev-derver 还有其他功能, 比如在本地开启服务、打开浏览器等.

  1. 安装

    1
    2
    3
    4
    //	首先全局安装
    $ npm install webpack-dev-server -g
    // 然后项目本地安装
    $ npm install webpack-dev-server --save-dev
  2. 运行命令

    $ webpack-dev-server

    如果看到 The CLI moved into a separate package: webpack-cli 这个报错信息, 根据提示下载 webpack-cli

    $ npm install webpack-cli -D

    再次运行 webpack-dev-server 命令, 如果看到 throw new Error(‘invalid “instanceof” keyword value ‘ + c); 这个报错信息, 可能是 webpack 版本与 webpack-dev-server 版本不兼容 ( 这里的 webpack 是 ^3.8.1 版本的, webpack-dev-server 是 ^3.1.10 版本的 );

    重新下载一个版本的 webpack-dev-server, 例如 ( ^2.11.3 )

    $ npm install webpack-dev-server@2.11.3 –save-dev

    再次运行 webpack-dev-server 命令, 可以看到 Project is running at http://localhost:8080/ 提示信息

    打开浏览器, 输入 localhost:8080, 可以看到和以前一样的效果.

  3. 修改默认端口

    项目默认是运行在 8080 端口上的, 这个我们可以修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    //	webpack.config.js
    module.exports = {
    ... ...
    devServer: {
    // 修改端口
    port: 9000,
    // 还可以配置一运行 webpack-dev-server 的时候就会自动打开浏览器
    open: true
    },
    ... ...
    }

使用 webpack 和 babel 配置 react 开发环境

  1. 安装 react

    要使用 react, 就必须安装下面两个包

    $ npm install react react-dom –save

  2. 建立 babel

    你可以把 babel 理解为编译器, 它能把 react 代码转成一般浏览器可读可执行的代码, 通常可以用来转化 react 或 vue 这样的前端代码, 或者把 ES6 代码转化成普通的 JavaScript 代码

    要让 babel 很好的转化 react 代码, 首先要安装好 babel, 再安装 babel 转化 react 的包

    $ npm install babel-core babel-preset-react babel-preset-env –save-dev

    创建 .babelrc 文件

    1
    2
    3
    {
    "presets": ["env", "react"]
    }
  1. 在 webpack 使用 babel-loader

    最后我们需要在 webpack 中使用一个 loader 来转化 react 的代码

    首先,安装

    $ npm install babel-loader –save-dev

    然后进行配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //	webpack.config.js
    module: {
    rules: [
    ... ...
    // 这两行是处理 react 相关的内容
    { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ },
    { test: /\.jsx$/, loader: 'babel-loader', exclude: /node_modules/ }
    ]
    }
  1. 写 react 组件

    准备一些 react 的代码, 来测试一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    //	src/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>Hello World</title>
    </head>
    <body>
    <div id="root"></div>
    </body>
    </html>

    // src/app.js
    ... ...
    import React from 'react';
    import ReactDOM from 'react-dom';
    import Root from './Root';

    ReactDOM.render(
    <Root></Root>,
    document.getElementById('root')
    );

    // src/Root.js
    import React from 'react';

    export default class Root extends React.Component {
    render () {
    return (
    <div style={{textAlign: 'center'}}>
    <h1>Hello World</h1>
    </div>
    );
    }
    }
  2. 修改版本问题

    由于安装的 babel-core^6.26.3 版本与 babel-loader^8.0.4 版本不匹配, 所以运行 webpack-dev-server 出现了如下错误: babel-loader@8 requires Babel 7.x (the package ‘@babel/core’). If you’d like to use Babel 6.x (‘babel-core’), you should install ‘babel-loader@7’.

    根据提示重新安装 babel-loader

    $ npm install babel-loader@7.1.5 –save-dev

    重新运行 webpack-dev-server 命令, 可以看到浏览器自动打开并展示出了期望的页面样式.

使用 clean-webpack-plugin 来清除文件

一般这个插件是配合 webpack -p 这条命令来清除文件使用的, 就是说在为生产环境编译文件的时候, 先把 build 或 dist ( 就是放生产环境用的文件 ) 目录里的文件先清除干净, 在生成新的.

  1. 为什么要使用 clean-webpack-plugin 插件

    我们来通过一个例子了解一下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //	webpack.config.js
    const path = require('path')
    ... ...
    module.exports = {
    entry: {
    "app.bundle": './src/app.js'
    },
    output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[chunkhash].js'
    },
    ... ...
    }

    在终端上运行:

    $ npm run prod

    1
    2
    3
    4
    5
    //	可以看到 dist 目录下生成了 app.bundle.14fe4a2f90cf9a717bbd.js 文件
    dist
    ├── app.bundle.14fe4a2f90cf9a717bbd.js
    ├── index.html
    └── style.css

    再把 src/app.js 的内容改改, 然后再执行 npm run prod, 多运行几次可以发现, 生成的带 hash 的 app.bundle.js 文件很多

    1
    2
    3
    4
    5
    6
    dist
    ├── app.bundle.0e380cea371d050137cd.js
    ├── app.bundle.259c34c1603489ef3572.js
    ├── app.bundle.14fe4a2f90cf9a717bbd.js
    ├── index.html
    └── style.css

    这些带 hash 的 app.bundle.js 只有最新的才有用, 其他的都没用, 我们要在 build 之前把他们全都清空, clean-webpack-plugin 插件就是用来干这个的.

  2. 使用 clean-webpack-plugin

    首先安装

    $ npm install clean-webpack-plugin –save-dev

    然后配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    //	webpack.config.js
    const path = require('path')
    ... ...
    const CleanWebpackPlugin = require('clean-webpack-plugin');

    let pathsToClean = [
    'dist',
    ]

    module.exports = {
    entry: {
    "app.bundle": './src/app.js'
    },
    outPut: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[chunkhash].js'
    },
    ... ...
    plugins: [
    new CleanWebpackPlugin(pathsToClean),
    ... ...
    ]
    }

    在运行 npm run dev 可以发现, dist 目录下新生成的 文件只有一个了

    1
    2
    3
    4
    dist
    ├── app.bundle.3ccf5544bcda830c4ec5.js
    ├── index.html
    └── style.css

配置多个 HTML 文件

之前我们只写了一个 html 文件, 就是 src/index.html, 但是有时候我们是需要多个的, 这个时候该怎么办呢?

之前我们是这样做的, 用了 html-webpack-plugin 这个插件来输出 html 文件.

1
2
3
4
5
6
7
8
9
10
11
//	webpack.config.js
... ...
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
minify: {
collapseWhitespace: true,
},
hash: true,
})
... ...

我们现在还这么做, 只需要改个名字就好了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//	webpack.config.js
... ...
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
minify: {
collapseWhitespace: true,
},
hash: true,
}),
new HtmlWebpackPlugin({
template: './src/contact.html',
filename: 'contact.html',
minify: {
collapseWhitespace: true,
},
hash: true,
})

// src/contact.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Contact</title>
</head>
<body>
<p>Contact Page</p>
</body>
</html>

有个问题, contact.html 使用的 js 和 css 跟 index.html 是一模一样的. 如果我们要让 contact.html 使用跟 index.html 不同的 js, 可以这样来改造一下: ( 只要保证 js 不同, css 也会不同的, 因为 css 是由 js 里 import 的 )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
...	...
module.exports = {
entry: {
"app.bundle": './src/app.js',
// 这行是新增的
"contact": './src/contact.js'
},
... ...
plugins: [
new CleanWebpackPlugin(pathsToClean),
new HtmlWebpackPlugin({
template: './src/index.html',
filename: 'index.html',
minify: {
collapseWhitespace: true,
},
hash: true,
// 这行是新增的
excludeChunks: ['contact']
}),
new HtmlWebpackPlugin({
template: './src/contact.html',
filename: 'contact.html',
minify: {
collapseWhitespace: true,
},
hash: true,
// 这行是新增的
chunks: ['contact']
}),
new ExtractTextPlugin('style.css')
]
}

上面的 excludeChunks 指的是不包含, chunks 代表的是包含.

// src/contact.js

console.log( ‘hi from contact.js’ )

结果可以看到, contact 页面里面引用的就只有 contact.js 了.

如何使用 pug (jade) 作为 HTML 的模板

什么是 pug? 如果是 nodejs 程序员的话, 肯定听过 jade 吧, pug 就是从 jade 改名过来的. 说白了, 它就是可以让你以更好的语法来写 html.

我们来看看下面的例子

现在我们要代替之前的 src/index.html 改用 pug 语法来写.

首先把 src/index.html 改名叫 src/index.pug

// 使用命令行重命名

$ rename src/index.html src/index.pug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//	src/index.pug
doctype html
html(lang="en")
head
title= pageTitle
script(type='text/javascript').
if (foo) bar(1+5)
body
h1 Pug - node template engine
#root
#container.col
if youAreUsingPug
p You are amazing
else
p Get on it!
p.
Pug is a terse and simple templating language with a strong focus on performance and powerful features.

// 这里要注意 .pug 文件的内容书写格式
// 如果运行命令后出现了 Invalid indentation, you can use tabs or spaces but not both 这个报错信息, 说明书写缩进格式有问题, 可以统一修改成使用空格缩进.

上面的内容是从 pug 官方的示例中抄的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//	webpack.config.js
... ...
module.exports = {
... ...
plugins: [
... ...
new HtmlWebpackPlugin({
template: './src/index.pug',
... ...
}),
... ...
],
module: {
rules: [
... ...
{ test: /\.pug$/, loader: ['raw-loader', 'pug-html-loader'] }
]
}
};

安装 pug

$ npm install –save-dev pug pug-html-loader raw-loader

现在可以启动服务, 打开页面看看效果.

现在来试试 pug 的 include 功能, 就是可以包含子模板.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//	src/index.pug
... ...
body
include includes/header.pug
... ...

// src/includes/header.pug
h1 from header pug file

// 目录结构是这样的
src
├── Root.js
├── app.js
├── app.scss
├── contact.html
├── contact.js
├── includes
│ └── header.pug
└── index.pug

现在再来启动服务来看看效果.

如何使用模块热替换 HMR 来处理 CSS

模块热替换 是什么意思?

以前我们使用的 webpack –watch 或 webpack-dev-server 的功能是监听文件改变, 就自动刷新浏览器, 而这个 模块热替换 不用刷新浏览器, 它是只让修改到的模块, 才会在浏览器上发生相应的变化, 就是生效, 而不是重新刷新浏览器.

所以有了 模块热替换 的功能, 我们来试一下, 改动 CSS 然后浏览器不用刷新就会让页面生效改变.

  1. 启用 HMR

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    //	webpack.config.js
    ... ...
    const webpack = require('webpack');
    ... ...

    modele.exports = {
    entry: {
    "app.bundle": './src/app.js',
    "contact": './src/contact.js'
    },
    output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[chunkhash].js'
    },
    devServer: {
    port: 9000,
    open: true,
    // 启用 HMR
    hot: true
    },
    plugins: [
    new CleanWebpackPlugin(pathsToClean),
    ... ...
    // 下面这两行是新增加的
    new webpack.NamedModulesPlugin(),
    new webpack.HotModuleReplacementPlugin()
    ],
    ... ...
    }

    启动服务, 可以发现报了一个错误: Cannot use [chunkhash] for chunk in ‘[name].[chunkhash].js’ (use [hash] instead)

    文件名不能使用 chunkhash 了, 要使用 hash 来代替 chunkhash.

    我们来修改一下:

    将: filename: ‘[name].[chunkhash].js’

    改成: filename: ‘[name].[hash].js’

  2. 处理 extract-text-webpack-plugin

    现在试一下改变 src/app.scss 的内容, 会发现页面不动了, 无论怎么改, 页面都不会改变, 也不会刷新.

    之前我们是用 extract-text-webpack-plugin 这个插件来处理 CSS 的, 在用 HMR 的时候要先把它关闭一下.

    用参数 disable: true 就可以关闭掉.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //	webpack.config.js
    将: new ExtractTextPlugin("style.css")
    改成: new ExtractTextPlugin({
    filename: 'style.css',
    disable: true
    }),

    // 然后把处理 scss 文件的 loader 部分变成类似下面这样:
    ... ...
    test: /\.scss$/,
    use: ExtractTextPlugin.extract({
    fallback: 'style-loader',
    use: ['css-loader', 'sass-loader']
    })
    ... ...
    修改为:
    ... ...
    test: /\.scss$/,
    use: ['style-loader', 'css-loader', 'sass-loader']
    ... ...

    再试试, 能够生效.

    为什么要关闭这个插件呢? 其实想想也能知道, 在开发环境下, 用不用 extract-text-webpack-plugin 这个插件意义不大, 把 css 变不变成一个文件, 关系不大, 开发环境只要能调校, 效果能够看到就可以, 但是生产环境需要这个插件, 我们总不能开发环境不使用这个插件, 也导致生产环境也不适用吧.

    那么怎么来解决这个问题呢? 也就是说让生产环境使用这个插件, 而开发环境不使用,

  3. 生产环境 VS 开发环境

    要让生产环境使用 extract-text-webpack-plugin 这个插件, 而开发环境不使用.

    其实原理很简单, 只要能区分出那个事开发环境, 那个是生产环境就可以; 只要判断是生产环境的时候就用, 不是的话, 就不用.

    • 增加环境变量

      首先来看一下之前的开发环境和生产环境分别使用的编译命令:

      1
      2
      3
      4
      5
      //	package.json
      "scripts": {
      "dev": "webpack-dev-server",
      "prod": "webpack -p"
      },

      开发环境使用的是 npm run dev 命令; 生产环境使用的是 npm run prod 命令.

      我们把它改成下面这样:

      1
      2
      3
      4
      5
      "scripts": {
      "dev": "webpack-dev-server",
      "prod": "NODE_ENV=production webpack -p"
      },
      // 开发环境的部分不变, 生产环境的加了一个环境变量: NODE_ENV=production

      很简单, NODE_ENV 是变量名, 而 production 是 NODE_ENV 这个变量的值, 这些都不是固定的, 可以改成你想要的任意内容, 只要能引用到就行.

    • 使用环境变量

      要引用我们之前创建的环境变量, 也非常简单

      // webpack.config.js

      var isProd = process.env.NODE_ENV === ‘production’; // true or false

      process.env.NODE_ENV 就能得到之前设置的变量, 如果运行的是 npm run prod, 那么 process.env.NODE_ENV 的值就是 production, 那么 isProd 就是 true; 如果运行的是 npm run dev, isProd 就是 false, 因为 npm run dev 没有设置这个 NODE_ENV 这个环境变量.

      前面我们有类似下面这样的两段关于 extract-text-webpack-plugin 这个插件的代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      new ExtractTextPlugin({
      filename: 'style.css',
      disable: false
      }),
      test: /\.scss$/,
      use: ['style-loader', 'css-loader', 'sass-loader']

      // 我们把 webpack.config.js 中的代码改成下面这样
      ... ...
      var isProd = process.env.NODE_ENV === 'production';
      var cssDev = ['style-loader', 'css-loader', 'sass-loader'];
      var cssProd = ExtractTextPlugin.extract({
      fallback: 'style-loader',
      use: ['css-loader', 'sass-loader']
      })
      var cssConfig = isProd ? cssProd : cssDev;
      module.exports = {
      ... ...
      plugins: [
      ... ...
      new ExtractTextPlugin({
      filename: 'style.css',
      disable: !isProd
      }),
      ... ...
      ],
      module: {
      rules: [
      {
      test: /\.scss$/,
      use: cssConfig
      },
      ... ...
      ]
      }
      }

      只要能区别出不同的环境, 使用不同的配置内容就可以了.

      现在就可以放心地使用 npm run dev 和 npm run prod 命令了, 再也不用临时关掉一些插件了.

      1
      2
      3
      4
      //	运行 npm run prod 提示 'NODE_ENV' 不是内部或外部命令,也不是可运行的程序 问题
      解决方法: 安装 cross-env, 再在 NODE_ENV=xxx 前面添加 cross-env 如
      $ npm install cross-env --save-dev
      "prod": "cross-env NODE_ENV=production webpack -p"

如何打包图片

我们首先在 src 目录下新建一个 images 文件夹, 然后使用 css 在网站上添加一个背景图片.

1
2
3
4
5
//	src/app.scss
body {
// 要确保 src/images 下有 logo.png 这张图片
background: url('./images/logo.png') 0 0 no-repeat;
}

然后运行 npm run dev, 你会发现类似这样的报错信息: You may need an appropriate loader to handle this file type.

你需要找到一个适合的 loader 来处理扩展名为 png 类型的文件.

  1. file-loader

    官方对这个 loader 是这样定义的:

    Instructs webpack to emit the required object as file and to return its public URL

    大概意思是: 对一些对象作为文件来处理, 然后可以返回它的URL.

    下面我们来使用一下:

    先准备一些数据. 之前我们是用 pug 来作为 HTML 的模板的, 现在还原回来, 重新使用 html 作为模板

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //	src/index.html
    // webpack.config.js
    plugins: [
    new CleanWebpackPlugin(pathsToClean),
    new HtmlWebpackPlugin({
    // 这里不再使用 pug 模板, 重新使用之前的 index.html
    template: './src/index.html',
    filename: 'index.html',
    minify: {
    collapseWhitespace: true,
    },
    hash: true,
    excludeChunks: ['contact']
    }),
    ... ...
    ]

    安装 file-loader:

    $ npm install file-loader –save-dev

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    //	webpack.config.js
    ... ...
    module.exports = {
    ... ...
    module: {
    rules: [
    ... ...
    {
    test: /\.png$/,
    use: [
    {
    loader: 'file-loader',
    }
    ]
    }
    ]
    }
    }

    打开控制台可以看到图片的文件名是带着 hash 的, 怎么才能让 file-loader 输出图片的源文件名呢?

  2. file-loader 的参数

    file-loader 是可以带参数的, 例如下面这样:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    test: /\.(gif|png|jpe?g|svg)$/i,
    use: [
    {
    loader: 'file-loader',
    options: {
    name: '[name].[ext]',
    outputPath: 'images/'
    }
    }
    ]

    /.(gif|png|jpe?g|svg)$/i 表示也可以处理其他格式的图片; [name] 代表文件名; [ext] 代表文件扩展名; outputPath 是输出的路径

    现在再看看控制台, 文件名可以显示出来了, 是不带 hash 的.

  3. 解析 html 代码里面 img 的标签

    前面我们是在 css 里引用图片作为背景的, 但是, 我们经常是在 html 直接使用 src 标签来引用图片的. 例如下面这样:

    alt

    可以在看到控制台中报了 money-bag.svg 404(Not Found) 的错误. 但是 images 目录下确实是有这个文件的.

    其实这是因为缺少一个在 html 代码里处理 img 标签的 html-loader.

    官方对 html-loader 的定义是这样的:

    Exports HTML as string. HTML is minimized when the compiler demands.

    大概意思是说, 把 html 变成导出成字符串的过程中, 还能进行压缩处理 (minimized).

    现在来添加这个 loader.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    //	首先下载
    $ npm install html-loader --save-dev

    ... ...
    {
    test: /\.(gif|png|jpe?g|svg)$/i,
    use: [
    {
    loader: 'file-loader',
    options: {
    name: '[name].[ext]',
    outputPath: 'images/'
    }
    }
    ]
    },
    // html-loader 配置内容
    {
    test: /\.html$/,
    use: [{
    loader: 'html-loader',
    options: {
    minimize: true
    }
    }]
    }
    ... ...

    现在再来试试看, 这张 svg 格式的图片可以显示出来啦.

  4. 压缩图片

    有时候图片太大了, 我们输出到生产环境的时候, 希望可以让图片文件的体积小点, 我们可以使用 image-webpack-loader 来处理. 这个插件主要是用来压缩图片文件的.

    安装:

    $ npm install image-webpack-loader –save-dev

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    //	webpack.config.js
    {
    test: /\.(gif|png|jpe?g|svg)$/i,
    use: [
    {
    loader: 'file-loader',
    options: {
    name: '[name].[ext]',
    outputPath: 'images/'
    }
    },
    {
    loader: 'image-webpack-loader',
    options: {
    bypssOnDebug: true,
    }
    }
    ]
    },
    {
    test: /\.html$/,
    use: [{
    loader: 'html-loader',
    options: {
    minimize: true
    }
    }],
    }

    运行命令 npm run prod, 对图片进行压缩处理.

    在 dist/images 目录下查看压缩后的文件体积大小. 可以看到压缩后的文件相比之前明显减少.

加载和打包 Twitter Bootstrap 框架

  1. 准备工作

    先来复制一些 bootstrap 的代码片段.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    //	src/index.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>
    <body>
    <button type="button" class="btn btn-default" aria-label="Left Align">
    <span class="glyphicon glyphicon-align-left" aria-hidden="true"></span>
    </button>
    <button type="button" class="btn btn-default btn-lg">
    <span class="glyphicon glyphicon-star" aria-hidden="true"></span> Star
    </button>
    <!-- Button trigger modal -->
    <button type="button" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#myModal">
    Launch demo modal
    </button>
    <!-- Modal -->
    <div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog" role="document">
    <div class="modal-content">
    <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
    <h4 class="modal-title" id="myModalLabel">Modal title</h4>
    </div>
    <div class="modal-body">
    ...
    </div>
    <div class="modal-footer">
    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
    <button type="button" class="btn btn-primary">Save changes</button>
    </div>
    </div>
    </div>
    </div>
    </body>
    </html>

    这里使用的是 bootstrap 3.

    启动服务, 打开页面可以看到 图标没有显示出来, css 也没有加载到, js 更是不可用.

  2. 安装 bootstrap-loader

    要加载 bootstrap 框架, 主要是使用这个 bootstrap-loader 这个 loader.

    可以查看它的官方文档, 来了解如何安装和使用它.

    $ npm install bootstrap-loader@2.2.0 bootstrap-sass –save-dev

    $ npm install resolve-url-loader url-loader –save-dev

  3. 使用

    通过官方文档, 我们来了解一下 bootstrap-loader 这个 loader 如何使用

    • 创建 .bootstraprc 文件

      在项目根目录下, 创建 .bootstraprc 文件, 内容复制下面这个链接的内容.

      .bootstraprc-3-default

      这个内容是官方提供的, 主要存放的是 bootstrap 的配置选项, 就是通过它来控制一些 bootstrap 的功能.

    • 创建 webpack.bootstrap.config.js 文件

      在项目跟目录下创建 webpack.bootstrap.config.js 文件, 内容复制下面这个链接的内容.

      webpack.bootstrap.config.js

      这个内容是官方提供的, 主要存放的是关于 bootstrap 的 webpack 配置的内容, 它包含生产环境和开发环境的配置.

    • 引用 bootstrap 的 webpack 配置

      现在把刚才下载的 webpack.bootstrap.config.js 文件利用起来

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      //	webpack.config.js
      const bootstrapEntryPoints = require('./webpack.bootstrap.config')
      var bootstrapConfig = isProd ? bootstrapEntryPoints.prod : bootstrapEntryPoints.dev
      module.exports = {
      entry: {
      "app.bundle": './src/app.js',
      "contact": './src/contact.js',
      "bootstrap": bootstrapConfig
      },
      ... ...
      }

      运行一下 npm run dev, 发现报了一个错误.

打赏功能
-------------本文结束感谢您的阅读-------------
0%