webpack系统配置

简言之,webpack 是一个模块打包器 (module bundler),能够将任何资源如 JavaScript 文件、CSS 文件、图片等打包成一个或少数文件。

为什么要用Webpack?

首先,定义已经说明了 webpack 能将多个资源模块打包成一个或少数文件,这意味着与以往的发起多个 HTTP 请求来获得资源相比,现在只需要发起少量的 HTTP 请求。

其次,webpack 能将你的资源转换为最适合浏览器的“格式”,提升应用性能。比如只引用被应用使用的资源 (剔除未被使用的代码),懒加载资源 (只在需要的时候才加载相应的资源)。再次,对于开发阶段,webpack 也提供了实时加载和热加载的功能,大大地节省了开发时间。除此之外,还有许多优秀之处之处值得去挖掘。不过,webpack 最核心的还是打包的功能。

webpack,gulp/grunt,npm,它们有什么区别?

webpack 是模块打包器(module bundler),把所有的模块打包成一个或少量文件,使你只需加载少量文件即可运行整个应用,而无需像之前那样加载大量的图片,css文件,js文件,字体文件等等。而gulp/grunt 是自动化构建工具,或者叫任务运行器(task runner),是把你所有重复的手动操作让代码来做,例如压缩JS代码、CSS代码,代码检查、代码编译等等,自动化构建工具并不能把所有模块打包到一起,也不能构建不同模块之间的依赖图。两者来比较的话,gulp/grunt 无法做模块打包的事,webpack 虽然有 loader 和 plugin可以做一部分 gulp/grunt 能做的事,但是终究 webpack 的插件还是不如 gulp/grunt 的插件丰富,能做的事比较有限。于是有人两者结合着用,将 webpack 放到 gulp/grunt 中用。然而,更好的方法是用 npm scripts 取代 gulp/grunt,npm 是 node 的包管理器 (node package manager),用于管理 node 的第三方软件包,npm 对于任务命令的良好支持让你最终省却了编写任务代码的必要,取而代之的,是老祖宗的几个命令行,仅靠几句命令行就足以完成你的模块打包和自动化构建的所有需求。

首先看下webpack最基本的应用:

全局使用

1、全局安装webpack:

cnpm install webpack -g

2、创建项目:

mkdir app

在 app 目录下添加 runoob1.js 文件,代码如下:

document.write("It works.");

在 app 目录下添加 index.html 文件,代码如下:

<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <script type="text/javascript" src="bundle.js" charset="utf-8"></script>
    </body>
</html>

接下来我们使用 webpack 命令来打包:

webpack runoob1.js bundle.js

执行以上命令会编译 runoob1.js 文件并生成bundle.js 文件,成功后输出信息如下所示:

Hash: a41c6217554e666594cb
Version: webpack 1.12.13
Time: 50ms
    Asset     Size  Chunks             Chunk Names
bundle.js  1.42 kB       0  [emitted]  main
   [0] ./runoob1.js 29 bytes {0} [built]

在浏览器中打开 index.html,输出结果如下:

webpack的使用,最复杂的一部分是莫过于它的配置项。webpack通过你的配置项,放置所有与打包相关的信息。一个基本的配置包括:

module.exports = {
        entry: '',
        output: {},
        module: {
            rules: []
        },
        plugins: [],
};

你如果要打包一个文件,那首先要指定文件的地址,也就是entry;打包之后放在那里呢,也就是output;打包过程中文件要经过怎么样的处理,也就是rules中的loader;如何能够使webpack打包更快,体积更小呢,也就是plugins。这些配置相辅相成,紧密结合。

安装到本项目:

npm i -D // npm install --save-dev的简写,是指安装模块并保存到package.json的devDependencies
npm i webpack -D //安装最新的稳定版本
npm i webpack@<version> -D // 安装指定版本
npm i webpack@beta -D // 安装最新的体验版本

安装完成后可以通过以下途径运行安装到本项目的webpack:

1、根项目目录下对应的命令行里通过".\node_modules\.bin\webpack"(注意:不是"./node_modules/.bin/webpack")运行webpack的可执行文件。 

2、在npm script里定义的任务会优先使用本项目下的webpack,代码如下:

  "scripts": {
    "start": "webpack --config webpack.config.js"
  }

虽然介绍了以上两种安装方式, 但是我们推荐安装到本项目,原因是可防止不同的项目因依赖不同版本的webpack而导致冲突。

模块化:

1、Common.js:

// 导出

function moduleA(){
...
}

module.exports = moduleA; 

// 导入
const moduleA = require("./moduleA ");

CommonJS的优点在于:

a、代码可复用于Node.js环境下并运行,例如做同构应用;

b、通过NPM发布的很多第三方模块都采用了CommonJS规范。

CommonJS的缺点:

这样的代码无法直接运行在浏览器环境下,必须通过工作转换成标准的ES5。

2、AMD:

与CommonJS最大的不同是采用了异步的方式去加载依赖的模块。

采用AMD导入导出的代码如下:

// 定义一个模块:

define("module", ["dep"], function(dep){
    return exports;
});

// 导入

require("module", function(module){

})

AMD的优点:

a、可在不转换代码的情况下直接在浏览器中运行;

b、可异步加载依赖;

c、可并行加载多个依赖;

d、代码可运行在浏览器环境和Node.js环境下。

缺点:

JavaScript运行环境没有原生支持AMD,需要先导入实现了AMD的库后才能正常使用。

3、es6模块化:

ES6 模块化是国际标准化组织 ECMA 提出的 JavaScript 模块化规范,它在语言层面上实现了模块化。浏览器厂商和 Node.js都宣布要原生支持该规范 。 它将逐渐取代 CommonJS 和AMD 规范,成为浏览器和服务器通用的模块解决方案 。

采用 ES6 模块化导入及导出的代码如下:

//导入
import { readFile } from ’ fs ’;
import React from ’ react ’ ;

//导出
export function hello {) { };
export default {
    // ...
};

ES6 模块虽然是终极模块化方案,但它的缺点在于目前无法直接运行在大部分 JavaScript运行环境下,必须通过工具转换成标准的 ES5 后才能正常运行。 

4. 样式文件申的模块化:

除了 JavaScript 开始进行模块化改造,前端开发里的样式文件也支持模块化。以 scss 为例,将一些常用的样式片段放进一个通用的文件里,再在另 一个文件里通过@ import 语句导入和使用这些样式片段 :
// util.scss 文件

// util.scss 文件

// 定义样式片段
@mixin center {
    //水平坚直居中
    position: absolute;
    left: 50%;
    top : 50%;
    transform : translate(-50%,-50%);
}

// main.scss 文件

// 导入和使用 util.scss 中定义的样式片段
@import "util";
#box{
    @include center ;
}

webpack版本不同,使用方式可能也会不同,下面来看下webpack3的使用:

一、webpack3的使用:

普通打包:

1、首先,创建一个目录,比如test11,使用npm初始化目录:

npm init

执行后会有一系列选项,可以按回车键快速确认,完成后会在test11目录生成一个package.json的文件。

2、之后在本机局部安装webpack:

npm install webpack@3.10.0 --save-dev

--save-dev会做为开发依赖来安装webpack。安装成功后,在package.json中会多了一项配置:

"devDependencies" : {

"webpack": "^3.10.0",

}

此时的package.json文件为:

{
  "name": "test11",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^3.10.0"
  }
}

看到"devDependencies"中含有webpack,就说明已经安装成功了,很快就可以启动一个webpack工程。

接下来新建一个src目录并且在src目录下新建main.js文件:

console.log("hello webpack");

新建一个index.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./dist/bundle.js"></script>
</head>
<body>

</body>
</html>

现在的文件夹目录结构如下:

执行以下命令打包main.js:

webpack src/main.js dist/bundle.js

如果有webpack.config.js文件,则直接输入“webpack”命令即可打包:

webpack

其中webpack.config.js配置如下:

var webpack = require('webpack');
var path = require('path');                   // 引入node的path模块

module.exports={
    entry: {
        entry: './src/main.js'                 // 入口文件
    },
    output: {
        path: path.resolve(__dirname, 'dist'),  // 打包路劲(获取绝对路径)
        filename: 'bundle.js'                   // 打包文件
    },
    module: {},
    plugins: [],
    devServer: {},
}

出现如下信息说明打包成功:

通过npm run build命令打包:

修改package.json中的script命令:

"build": "webpack src/main.js dist/bundle.js"
{
  "name": "test11",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack src/main.js dist/bundle.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.4",
    "webpack": "^3.10.0"
  }
}

 然后命令行输入:

npm run build

同样也可以打包。

 实现自动监听:

修改package.json文件中的script命令:

"watch": "npm run build -- --watch"
{
  "name": "test11",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack src/main.js dist/bundle.js",
    "watch": "npm run build -- --watch"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.26.3",
    "babel-loader": "^7.1.4",
    "webpack": "^3.10.0"
  }
}

命令行输入:npm run watch  打包。

 二、webpack4的使用:

1、首先,创建一个目录,比如demo,使用npm初始化目录:

npm init

执行后会有一系列选项,可以按回车键快速确认,完成后会在demo目录生成一个package.json的文件。

2、之后在本机局部安装webpack:

npm install webpack --save-dev

--save-dev会做为开发依赖来安装webpack。安装成功后,在package.json中会多了一项配置:

"devDependencies" : {

 "webpack": "^4.16.3",

}

此外还需安装webpack-cli:

npm install webpack-cli --save-dev

3、配置webpack:

创建 webpack.config.js 文件作为webpack的配置文件。

var path = require('path');

var config = {
    entry: {
        main: './main'//配置的单入口,webpack会从main.js开始工作
    },
    output: {
        path: path.join(__dirname, './dist'),//存放打包后文件的输出目录
        publicPath: '/dist/',//指定资源文件的引用目录,
        filename: 'bundle.js'//指定输出文件的名称,只要在html中引用它即可

    }
};

module.exports = config;

新建index.html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
<div id='root'></div>
<script type="text/javascript" src="./dist/bundle.js" charset="utf-8"></script>
</body>
</html>

新建main.js文件:

document.write("test webpack4.0");

现在工程目录结构为:

4、使用webpack:

设置开发或者生产模式:

在 webpack.config.js 里设置

mode: 'development'

为了方便使用,我们在package.json里加入webpack打包的命令方便我们使用修改script项 :

"scripts": {
    "build": "npx webpack --config webpack.config.js"
 },

这样再次运行我们直接输入:

npm run build 

就可以了。

二、webpack.config.js:

归根结底,webpack就是一个.js配置文件,我们建立一个webpack.config.js文件,我们可以在其中进行各项配置。

比如我们想要使用一个热加载网页的框架webpack-dev-server(webpack-dev-server是一个轻量级的服务器,修改文件源码后,自动刷新页面将修改同步到页面上),我们先安装:

npm install webpack@4.6.0 webpack@2.0.15 webpack-dev-server@3.1.3 --save-dev

然后在package.json的"scripts"里增加一个快速启动webpack-dev-server服务的脚本:

  "scripts": {
	"dev": "webpack-dev-server --open --config webpack.config.js"
  },

 package.json的详细配置如下:

{
  "name": "test13",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack",
    "dev": "webpack-dev-server --open --config webpack.config.js"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.6.0",
    "webpack-cli": "^2.0.15",
    "webpack-dev-server": "^3.1.3"
  }
}

在webpack.config.js中对webpack.config.js中添加devServer选项对webpack-dev-server进行详细配置:

    devServer:{
        port: 3000,
        publicPath: "./"
    }

 webpack.config.js的详细配置如下:

var webpack = require('webpack');
var path = require('path');                   // 引入node的path模块

module.exports={
    entry: {
        entry: './src/main.js'                 // 入口文件
    },
    output: {
        path: path.resolve(__dirname, 'dist'),  // 打包路劲(获取绝对路径)
        filename: 'bundle.js'                   // 打包文件
    },
    module: {},
    plugins: [],
    devServer: {
        open: true,
        port: 8080
    }
}

当运行:

npm run dev

命令时,就会执行

webpack-dev-server --open --config webpack.config.js

其中--config是指向webpac-dev-server读取的配置文件路径,这里直接读取我们在上一步创建的webpack.config.js文件。

--open会在执行命令时自动在浏览器中打开页面,默认地址是127.0.0.1:8080,不过ip和端口都是可以配的:

webpack配置中最重要的也是必选的两项是入口和出口,入口的作用是告诉webpack从哪里开始寻找依赖,并且编译。出口则用来配置编译后的文件存储位置和文件名。

在终端执行

npm run dev

就会自动在浏览器中打开页面了。

三、完善配置文件:

在webpack的世界里,每一个文件都是一个模块,比如.css,.js,.html,.jpg,.less等。对于不同的模块,需要用不同的加载器来处理,而加载器就是webpack最重要的功能。通过安装不同的加载器可以对各种不同后缀名的文件进行处理,比如现在要写一个css样式,就要用到css-loader和style-loader。用npm方式安装它:

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

安装完成后,在webpack.config.js文件里配置Loader,增加对.css文件的处理:

  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
                'style-loader',
                'css-loader'
            ]
      }
    ]
  }

webpack看似复杂,但它不过是一个js配置文件,只要搞清楚入口、出口、加载器、插件这四个概念,使用起来就不那么困惑了。

四、Loaders

Loaders是webpack提供的最激动人心的功能之一了。通过使用不同的loader,webpack有能力调用外部的脚本或工具,实现对不同格式的文件的处理,比如说分析转换scss为css,或者把下一代的JS文件(ES6,ES7)转换为现代浏览器兼容的JS文件,对React的开发而言,合适的Loaders可以把React的中用到的JSX文件转换为JS文件。

Loaders需要单独安装并且需要在webpack.config.js中的modules关键字下进行配置,Loaders的配置包括以下几方面:

test:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)

loader:loader的名称(必须)

include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
query:为loaders提供额外的设置选项(可选)

下面以css的处理为例来说明:

1、以es6方式导入文件:

现在写一些css,就需要用到style-loader和css-loader,现在通过npm来安装:

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

安装完成后,建立一个css文件:

body {
  background: pink;
}

在main.js文件中增加如下代码:

import css from './app.css';

console.log("hello world");

重新执行: 

npm run build

文件夹结构如下:

其中,app.css:

body {
    background: pink;
}

app.js:

import css from './app.css';

console.log("hello world");

 index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./dist/bundle.js"></script>
</head>
<body>

</body>
</html>

package.json:

{
  "name": "test16",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^2.1.0",
    "style-loader": "^0.23.1",
    "webpack": "^3.6.0"
  }
}

 webpack.config.js:

var webpack = require('webpack');
var path = require('path');                   // 引入node的path模块

module.exports={
    entry: {
        entry: './src/app.js'                 // 入口文件
    },
    output: {
        path: path.resolve(__dirname, 'dist'),  // 打包路劲(获取绝对路径)
        filename: 'bundle.js'                   // 打包文件
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: [ 'style-loader', 'css-loader' ]
            }
        ]
    },
    plugins: [],
    devServer: {},
}

2、以commonJS方式导入文件:

文件目录:

package.json:

{
  "name": "test25",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack --config webpack.config.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^2.1.0",
    "style-loader": "^0.23.1",
    "webpack": "^3.6.0"
  }
}

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app"></div>

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

 show.js:

function show(content){
    document.getElementById("app").innerText = "hello " + content;
}

module.exports = show;

style.css:

body{
    background: pink;
}
#app{
    text-align: center;
}

 main.js:

const style = require("./style.css");
const show = require("./show.js");
show("webpack");

webpack.config.js:

const path = require("path");

module.exports = {
    entry: "./src/main.js",
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            }
        ]
    },
    plugins: [],
    devServer: {}
};

 五、Babel

Babel其实是一个编译JavaScript的平台,它可以编译代码帮你达到以下目的:

让你能使用最新的JavaScript代码(ES6,ES7...),而不用管新标准是否被当前使用的浏览器完全支持;

让你能使用基于JavaScript进行了拓展的语言,比如React的JSX;

Babel的安装与配置

Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析Es6的babel-env-preset包和解析JSX的babel-preset-react包)。

项目目录结构如下:

package.json:

{
  "name": "test26",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
	"dev": "webpack --config webpack.config.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.25.0",
    "babel-loader": "^7.1.1",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-env": "^1.5.2",
    "babel-preset-es2015": "^6.24.1",
    "webpack": "^3.0.0"
  }
}

在webpack中配置Babel的方法如下:

const path = require("path");

module.exports = {
    entry: "./src/main.js",
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: "babel-loader"
            }
        ]
    },
    plugins: [

    ],
    devServer: {}
};

现在你的webpack的配置已经允许你使用ES6以及JSX的语法了。

show.js:

function show(content){
    document.getElementById("app").innerText = "hello, " + content;
}

module.exports = show;

main.js:

let name = "webpack!"
const show = require("./show.js");
show(name);

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="./dist/main_2d6cab1e.css"/>
</head>
<body>
<div id="app"></div>

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

其中的style.css文件可有可无。

Babel其实可以完全在 webpack.config.js 中进行配置,但是考虑到babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项放在一个单独的名为 ".babelrc" 的配置文件中。

六、手动通过终端方式,将第三方库直接打包:

以jquery为例:

在对应的文件夹下安装jquery:

npm install jquery --save-dev

在main.js中导入jquery:

var $ = require('jquery');

导入后就可以开始使用了:

// main.js
var $ = require('jquery');
document.write('<div>Hello World</div>');
$("div").html("导入jquery实例!");

文件夹结构及文件内容如下方式一:

package.json:

{
  "name": "test15",
  "version": "1.0.0",
  "description": "",
  "main": "webpack.config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack src/main.js dist/bundle.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "jquery": "^3.3.1",
    "webpack": "^3.6.0"
  }
}

main.js:

var $ = require('jquery');
document.write('<div>Hello World</div>');
$("div").html("导入jquery实例!");

 index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./dist/bundle.js"></script>
</head>
<body>

</body>
</html>

方式二:

package.json:

{
  "name": "test15",
  "version": "1.0.0",
  "description": "",
  "main": "webpack.config.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "webpack"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "jquery": "^3.3.1",
    "webpack": "^3.6.0"
  }
}

webpack.config.js:

var webpack = require('webpack');
var path = require('path');                   // 引入node的path模块

module.exports={
    entry: {
        entry: './src/main.js'                 // 入口文件
    },
    output: {
        path: path.resolve(__dirname, 'dist'),  // 打包路劲(获取绝对路径)
        filename: 'bundle.js'                   // 打包文件
    },
    module: {},
    plugins: [],
    devServer: {},
}

 main.js:

var $ = require('jquery');
document.write('<div>Hello World</div>');
$("div").html("导入jquery实例!");

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="./dist/bundle.js"></script>
</head>
<body>

</body>
</html>

 七、一切皆模块

Webpack有一个不可不说的优点,它把所有的文件都都当做模块处理,JavaScript代码,CSS和fonts以及图片等等通过合适的loader都可以被处理。

CSS

webpack提供两个工具处理样式表,css-loader 和 style-loader,二者处理的任务不同,css-loader使你能够使用类似@import 和 url(...)的方法实现 require()的功能,style-loader将所有的计算后的样式加入页面中,二者组合在一起使你能够把样式表嵌入webpack打包后的JS文件中。

//安装
npm install --save-dev style-loader css-loader

八、插件(Plugins)

插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。
Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,可以这么来说,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。

Webpack有很多内置插件,同时也有很多第三方插件,可以让我们完成更加丰富的功能。

使用插件的方法

要使用某个插件,我们需要通过npm安装它,然后要做的就是在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)

下面来看一个实例:通过Plugin将注入bundle.js文件里的css提取到单独的文件中

项目目录如下:

app.css:

body {
    background: pink;
}
#app{
    text-align: center;
}

show.js:

function show(content){
    document.getElementById("app").innerText = "hello " + content;
}

module.exports = show;

app.js:

require("./app.css");
const show = require("./show.js");
show("Webpack!");

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app"></div>
<script src="./dist/bundle.js"></script>
</body>
</html>

package.json:

{
  "name": "test22",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack --config webpack.config.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^2.1.0",
    "extract-text-webpack-plugin": "^3.0.2",
    "style-loader": "^0.23.1",
    "webpack": "^3.6.0"
  }
}

webpack.config.js:

const path = require('path');                   // 引入node的path模块
const ExtractTextPlugin = require ('extract-text-webpack-plugin');

module.exports={
    // JavaScript 执行入口文件
    entry: {
        entry: './src/app.js'                 // 入口文件
    },
    output: {
        // 将输出文件都放到 dist 目录下
        path: path.resolve(__dirname, './dist'),
        // 将所有依赖的模块合并输出到一个bundle.js文件中
        filename: 'bundle.js'                   
    },
    module: {
        rules: [
            {
                // 用正则去匹配要用该 loader 转换的 css 文件
                test: /\.css$/,
                loaders: ExtractTextPlugin.extract({
                    // 转换.css文件需要使用的Loader
                    use: ['css-loader'],
                }),
            }
        ]
    },
    plugins: [
            new ExtractTextPlugin({
            // 从.js文件中提取出来的.css文件的名称
            filename:'[name]_[contenthash:8].css',
            }),
    ],
    devServer: {},
}

要让以上代码运行起来,需要先安装新引入的插件:

npm i -D extract-text-webpack-plugin

安装成功后重新执行构建, 我们会发现 dist 目录下多出一个entry_23c6115b.css 文件, bundle .j s 文件里也没有 css 代码了,再将该 css 文件引入 index.html 里就完成了。

从以上代码可以看出, Webpack是通过 plugins属性来配置需要使用的插件列表的。plugins 属性是一个数组,里面的每一项都是插件的一个实例,在实例化一个组件时可以通过构造函数传入这个组件支持的配置属性。

例如,ExtractTextPlugin插件的作用是提取出JavaScript代码里的css到一个单独的文件中。对此我们可以通过插件的filename属性,告诉插件输出的css文件名称是通过[name][contenthash:8] .css字符串模板生成的,里面的[ name]代表文件的名称,[contenthash:8]代表根据文件内容算出的8位Hash值,还有很多配置选项可以在
ExtractTextPlugin ( https://github.com/webpack-contrib/extract-text-webpack-plugin )的主页上查到。

九、使用DevServer:

实际开发中我们可能会需要:

• 提供 HTTP 服务而不是使用本地文件预览;
• 监昕文件的变化并自动刷新网页,做到实时预览:
• 支持 Source Map ,以方便调试。

对于这些, Webpack 都为我们考虑好了。 Webpack 原生支持上述第 2 、 3 点内容,再结合官方提供的开发工具 DevServer ( https://webpack乒org/configuration/dev-server/)也可以很方便地做到第 1 点。 DevServer 会启动一个 HTTP 服务器用于服务网页请求,同时会帮助启动Webpack,并接收 Webpack 发出的文件更变信号,通过 WebSocket 协议自动刷新网页做到实时预览。

下面在一个小项目中集成DevServer。

项目目录如下:

app.css:

body {
    background: pink;
}
#app{
    text-align: center;
}

app.js:

require("./app.css");
const show = require("./show.js");
show("Webpack!");

 show.js:

function show(content){
    document.getElementById("app").innerText = "hello " + content;
}

module.exports = show;

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div id="app"></div>
<script src="./dist/bundle.js"></script>
</body>
</html>

 package.json:

{
  "name": "test23",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack --config webpack.config.js",
    "server": "webpack-dev-server --open"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "css-loader": "^2.1.0",
    "extract-text-webpack-plugin": "^3.0.2",
    "style-loader": "^0.23.1",
    "webpack": "^3.6.0",
    "webpack-dev-server": "^2.9.1"
  }
}

首先需要安装 DevServer:

cnpm i -D webpack-dev-server

webpack.config.js:

const path = require('path');                   // 引入node的path模块
const ExtractTextPlugin = require ('extract-text-webpack-plugin');

module.exports={
    // JavaScript 执行入口文件
    entry: {
        entry: './src/app.js'                 // 入口文件
    },
    output: {
        // 将输出文件都放到 dist 目录下
        path: path.resolve(__dirname, './dist'),
        // 将所有依赖的模块合并输出到一个bundle.js文件中
        filename: 'bundle.js'
    },
    module: {
        rules: [
            {
                // 用正则去匹配要用该 loader 转换的 css 文件
                test: /\.css$/,
                loaders: ExtractTextPlugin.extract({
                    // 转换.css文件需要使用的Loader
                    use: ['css-loader'],
                }),
            }
        ]
    },
    plugins: [
        new ExtractTextPlugin({
            // 从.js文件中提取出来的.css文件的名称
            filename:'[name]_[contenthash:8].css',
        }),
    ],
    devServer: {},
}

 安装成功后执行 "webpack-dev- server"或"npm run server" 命令, DevServer 就启动了,这时我们会看到控制台有一 串日志输出:

Project is running at http://localhost:8080/
webpack output is served from /

这意味着 DevServer 启动的 HTTP 服务器监听在 8080 端口, DevServer 启动后会一直驻留在后台保持运行 ,访问这个网址,就能获取项目根目录下的 index.html 了。用浏览器打开这个地址时我们会发现页面空白,错误的原因是./ dist/bundle.j s 加载 404了 。 同时我们会发现并没有文件输出到 dist 目录,原因是 DevServer 会将 Webpack 构建出的文件保存在 内存中,在要访问输出的文件时,必须通过 HTTP 服务访问。由于 DevServer不会理会 webpack .config.j s 里配置的 output .path 属性,所以要获取 bundle.js 的正确 URL 是 http: //loca lhost:8080/bundle.js,对应的 index.html 应该修改为:

<html>
<head>
<meta charset= ” UTF-8 ” >
</head>
<body>
<div id=” app ”></ div>
〈 !一导入 DevServer 输出的 JavaScript 文件一〉
<script src=”bundle.js"></script>
</body>
</ html>

模块热替换:

除了通过重新刷新整个网页来实现实时预览, DevServer 还有一种被称作模块热替换的刷新技术。模块热替换能做到在不重新加载整个网页的情况下,通过将己更新的模块替换老模块,再重新执行一次来实现实时预览。模块热替换相对于默认的刷新机制能提供更快的响应速度和更好的开发体验。模块热替换默认是关闭的,要开启模块热替换,我们只 需在启动DevServer 时带上一 hot 参数,重启 DevServer 后再去更新文件就能体验到模块热替换的神奇了。 

支持 Source Map:

在浏览器中运行的 JavaScript 代码都是编译器输出的代码,这些代码的可读性很差。如果在开发过程中遇到一个不知道原因的 Bug,则我们可能需要通过断点调试去找出问题。在编译器输出的代码上进行断点调试是一件辛苦和不优雅的事情,调试工具可以通过 Source Map( https://www.html5rocks.com/en/tutorials/developertools/sourcemaps/)映射代码,让我们在源代码上断点调试。 Webpack 支持生成 Source Map,只需在启动时带上一 devtool source-map参数。重启 DevServer 后刷新页面,再打开 Chrome 浏览器的开发者工具,就可以在 Sources栏中看到可调试的源代码了,如图1-2所示。

 
posted @ 2018-07-28 20:42  Samve  阅读(239)  评论(0编辑  收藏  举报