Webpack初体验

Webpack 是当下最热门的前端资源模块化管理和打包工具。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载。通过 loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、 AMD 模块、 ES6 模块、CSS、图片、 JSON、Coffeescript、 LESS 等。

一、安装

首先要安装 Node.js, Node.js 自带了软件包管理器 npm,Webpack 需要 Node.js v0.6 以上支持,建议使用最新版 Node.js。

1、Webpack可以使用npm安装,新建一个空的练习文件夹(此处命名为Webpack),在终端中转到该文件夹后执行下述指令就可以完成安装。

// 切换到项目目录
cd  /Volumes/MacSysterm/workspace/Web/Webpack

//全局安装,以后可以直接在命令端使用webpack命令
npm install -g webpack

//安装到你的项目目录,在你的项目下创建一个node_modules文件夹
npm install --save-dev webpack

2、创建package.json文件
在上述练习文件夹中创建一个package.json文件,这是一个标准的npm说明文件,里面蕴含了丰富的信息,包括当前项目的依赖模块,自定义的脚本任务等等。在终端中使用npm init命令可以自动创建这个package.json文件。

npm init

输入这个命令后,终端会问你一系列诸如项目名称,项目描述,作者等信息,不过不用担心,如果你不准备在npm中发布你的模块,这些问题的答案都不重要,回车默认即可。

3、项目中安装Webpack作为依赖包
package.json文件已经就绪,我们在本项目中安装Webpack作为依赖包

// 安装Webpack
npm install --save-dev webpack

clipboard.png

4、创建资源文件夹
回到之前的空文件夹,并在里面创建两个文件夹,app文件夹和public文件夹,app文件夹用来存放原始数据和我们将写的JavaScript模块,public文件夹用来存放准备给浏览器读取的数据(包括使用webpack生成的打包后的js文件以及一个index.html文件)。在这里还需要创建三个文件,index.html 文件放在public文件夹中,两个js文件(Greeter.js和main.js)放在app文件夹中,此时项目结构如下图所示

clipboard.png

index.html文件只有最基础的html代码,它唯一的目的就是加载打包后的js文件(bundle.js)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Webpack Sample Project</title>
  </head>
  <body>
    <div id='root'>
    </div>
    <script src="bundle.js"></script>
  </body>
</html>

Greeter.js只包括一个用来返回包含问候信息的html元素的函数。

//main.js 
var greeter = require('./Greeter.js');
document.getElementById('root').appendChild(greeter());

main.js用来把Greeter模块返回的节点插入页面。

// Greeter.js
module.exports = function() {
  var greet = document.createElement('div');
  greet.textContent = "Hi there and greetings!";
  return greet;
};

5、在webpack中引入jquery库
在本地项目目录下经过npm init初始化生成package.json文件后,使用下边命令安装jquery包:

➜ npm install jquery --save-dev

安装OK后,我们就可以在js文件中引入并使用jQuery了

// test.js
var $ = require('jquery');
$('div').html('Learning webpack,haha');

二、牛刀小试

module-one.js文件

document.write('One haha~');

module-two.js文件

document.write('Two hey~');

./js/entry.js入口文件

require('./module-one.js');

require('./module-two.js');

document.write('It works.')

clipboard.png

clipboard.png

通过命令打包生成的bundle.js文件

/******/ (function(modules) { // webpackBootstrap
/******/     // The module cache
/******/     var installedModules = {};

/******/     // The require function
/******/     function __webpack_require__(moduleId) {

/******/         // Check if module is in cache
/******/         if(installedModules[moduleId])
/******/             return installedModules[moduleId].exports;

/******/         // Create a new module (and put it into the cache)
/******/         var module = installedModules[moduleId] = {
/******/             i: moduleId,
/******/             l: false,
/******/             exports: {}
/******/         };

/******/         // Execute the module function
/******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/         // Flag the module as loaded
/******/         module.l = true;

/******/         // Return the exports of the module
/******/         return module.exports;
/******/     }


/******/     // expose the modules object (__webpack_modules__)
/******/     __webpack_require__.m = modules;

/******/     // expose the module cache
/******/     __webpack_require__.c = installedModules;

/******/     // identity function for calling harmony imports with the correct context
/******/     __webpack_require__.i = function(value) { return value; };

/******/     // define getter function for harmony exports
/******/     __webpack_require__.d = function(exports, name, getter) {
/******/         if(!__webpack_require__.o(exports, name)) {
/******/             Object.defineProperty(exports, name, {
/******/                 configurable: false,
/******/                 enumerable: true,
/******/                 get: getter
/******/             });
/******/         }
/******/     };

/******/     // getDefaultExport function for compatibility with non-harmony modules
/******/     __webpack_require__.n = function(module) {
/******/         var getter = module && module.__esModule ?
/******/             function getDefault() { return module['default']; } :
/******/             function getModuleExports() { return module; };
/******/         __webpack_require__.d(getter, 'a', getter);
/******/         return getter;
/******/     };

/******/     // Object.prototype.hasOwnProperty.call
/******/     __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

/******/     // __webpack_public_path__
/******/     __webpack_require__.p = "";

/******/     // Load entry module and return exports
/******/     return __webpack_require__(__webpack_require__.s = 2);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {

document.write('One haha~');


/***/ }),
/* 1 */
/***/ (function(module, exports) {

document.write('Two hey~');


/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__(0);

__webpack_require__(1);

document.write('It works.')


/***/ })
/******/ ]);

访问index.html页面

clipboard.png

三、正式使用

1、通过配置文件来使用Webpack

在当前练习文件夹的根目录下新建一个名为webpack.config.js的文件,并在其中进行最最简单的配置,如下所示,它包含入口文件路径和存放打包后文件的地方的路径。

module.exports = {
  entry:  __dirname + "/app/main.js",//已多次提及的唯一入口文件
  output: {
    path: __dirname + "/public",//打包后的文件存放的地方
    filename: "bundle.js"//打包后输出文件的文件名
  }
}

注:“__dirname”是Node.js中的一个全局变量,它指向当前执行脚本所在的目录。

现在如果你需要打包文件只需要在终端里你运行webpack(非全局安装需使用node_modules/.bin/webpack)命令就可以了,这条命令会自动参考webpack.config.js文件中的配置选项打包你的项目,输出结果如下:

clipboard.png

又学会了一种使用Webpack的方法,而且不用管那烦人的命令行参数了,有没有感觉很爽。有没有想过如果可以连webpack(非全局安装需使用node_modules/.bin/webpack)这条命令都可以不用,那种感觉会不会更爽~,继续看下文。

2、更快捷的执行打包任务

执行类似于node_modules/.bin/webpack这样的命令其实是比较烦人且容易出错的,不过值得庆幸的是npm可以引导任务执行,对其进行配置后可以使用简单的**npm
start**命令来代替这些繁琐的命令。在package.json中对npm的脚本部分进行相关设置即可,设置方法如下。

  "name": "webpack-sample-project",
  "version": "1.0.0",
  "description": "Sample webpack project",
  "scripts": {
    "start": "webpack" //配置的地方就是这里啦,相当于把npm的start命令指向webpack命令
  },
  "author": "zhang",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^1.12.9"
  }
}

注:package.json中的脚本部分已经默认在命令前添加了node_modules/.bin路径,所以无论是全局还是局部安装的Webpack,你都不需要写前面那指明详细的路径了。

npm的start是一个特殊的脚本名称,它的特殊性表现在,在命令行中使用npm
start就可以执行相关命令,如果对应的此脚本名称不是start,想要在命令行中运行时,需要这样用npm
run {script name}如npm run build,以下是执行npm
start后命令行的输出显示

现在只需要使用npm start就可以打包文件了,有没有觉得webpack也不过如此嘛,不过不要太小瞧Webpack,其强大的功能包含在其一系列可供配置的选项中,我们一项项来看。

三、Webpack的强大功能

1、Source Maps使调试更容易

开发总是离不开调试,如果可以更加方便的调试当然就能提高开发效率,不过打包后的文件有时候你是不容易找到出错了的地方对应的源代码的位置的,Source Maps就是来帮我们解决这个问题的。
通过简单的配置后,Webpack在打包时可以为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。

在webpack的配置文件中配置source maps,需要配置devtool,它有以下四种不同的配置选项,各具优缺点,描述如下:

clipboard.png

正如上表所述,上述选项由上到下打包速度越来越快,不过同时也具有越来越多的负面作用,较快的构建速度的后果就是对打包后的文件的的执行有一定影响。

在学习阶段以及在小到中性的项目上,eval-source-map是一个很好的选项,不过记得只在开发阶段使用它,继续上面的例子,在webpack.config.js进行如下配置

// webpack.config.js
module.exports = {
  devtool: 'eval-source-map',//配置生成Source Maps,选择合适的选项
  entry:  __dirname + "/app/main.js",
  output: {
    path: __dirname + "/public",
    filename: "bundle.js"
  }
}

cheap-module-eval-source-map方法构建速度更快,但是不利于调试,推荐在大型项目考虑da时间成本是使用。

2、使用webpack构建本地服务器

想不想让你的浏览器监测你都代码的修改,并自动刷新修改后的结果,其实Webpack提供一个可选的本地开发服务器,这个本地服务器基于node.js构建,可以实现你想要的这些功能,不过它是一个单独的组件,在webpack中进行配置之前需要单独安装它作为项目依赖

npm install --save-dev webpack-dev-server

devserver作为webpack配置选项中的一项,具有以下配置选项

clipboard.png

继续把这些命令加到webpack.config.js的配置文件中,现在的配置文件如下所示:

module.exports = {
  devtool: 'eval-source-map',

  entry:  __dirname + "/app/main.js",
  output: {
    path: __dirname + "/public",
    filename: "bundle.js"
  },

  devServer: {
    contentBase: "./public",//本地服务器所加载的页面所在的目录
    colors: true,//终端中输出结果为彩色
    historyApiFallback: true,//不跳转
    inline: true//实时刷新
  } 
}

4.Loaders

Loaders是webpack中最让人激动人心的功能之一了。通过使用不同的loader,webpack通过调用外部的脚本或工具可以对各种各样的格式的文件进行处理,比如说分析JSON文件并把它转换为JavaScript文件,或者说把下一代的JS文件(ES6,ES7)转换为现代浏览器可以识别的JS文件。或者说对React的开发而言,合适的Loaders可以把React的JSX文件转换为JS文件。

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

test:一个匹配loaders所处理的文件的拓展名的正则表达式(必须)
loader:loader的名称(必须)
include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选);
query:为loaders提供额外的设置选项(可选)
继续上面的例子,我们把Greeter.js里的问候消息放在一个单独的JSON文件里,并通过合适的配置使Greeter.js可以读取该JSON文件的值,配置方法如下:

CSS

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

1.安装

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

2、配置文件中使用

module.exports = {
  devtool:"sourcemap",
  entry:"./js/entry.js",
  output:{
    filename:"bundle.js"
  },
  module: {
    loaders:[
      {
        test: /\.css$/,
        loader: 'style!css'//添加对样式表的处理
      }
   ]
 },
}

注:感叹号的作用在于使同一文件能够使用不同类型的loader

3.创建css文件,这里我们给body添加一个简单的背景色pink
css/style.css文件

body{
  background: pink;
}

4.将上述css文件引入到入口entry.js中。

require('./module-one.js');

require('./module-two.js');

require('../css/style.css');  // 引入css样式文件

document.write('It works.')

然后执行打包编译命令:

clipboard.png

擦,怎么报错了-_-!!!

什么原因呢?

原来引入的文件写法有问题,这里有两种改法:

  1. 在引入时需要加后缀loader

  2. 在配置文件中加loader后缀

第一种改法,修改引入文件:

require('./module-one.js');

require('./module-two.js');

// require('../css/style.css');  // 这样不加loader前缀标识会报错
require("!style-loader!css-loader!../css/style.css") // 载入 style.css

document.write('It works.')

第二种改法,修改配置文件中的写法

module.exports = {
  devtool:"sourcemap",
  entry:"./js/entry.js",
  output:{
    filename:"bundle.js"
  },
  module: {
    loaders:[
      {
        test: /\.css$/,
      //  loader: 'style!css' //对这里进行修改,加后缀loader
        loader: 'style-loader!css-loader'//添加对样式表的处理
      }
   ]
 },
}

然后我们在用命令进行编译:

clipboard.png

OK,没问题了,再看下加了pink的背景色的页面。

clipboard.png

四、webpack和Vue组件开发

在开始之前,我们先安装babel,用ES6的语法来进行测试,安装完之后,再进行文件的配置。

➜  webpack npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-stage-0 babel-runtime babel-plugin-transform-runtime

1、安装vue包

➜  webpack npm install vue vue-loader vue-html-loader vue-style-loader --save-dev

进行配置文件webpack.config.json修改:

module.exports = {
  devtool:"sourcemap",
  entry:"./js/entry.js",
  output:{
    filename:"bundle.js"
  },
  module: {
    loaders:[
      {
        test: /\.css$/,
      //  loader: 'style!css'//添加对样式表的处理
        loader: 'style-loader!css-loader'//添加对样式表的处理
      },
      {
        test:/\.js$/,
        loader:"babel-loader",
        exclude:/node_modules/    // 将这个目录忽略掉,将加快加载速度

      },
      {
        test:/\.vue$/,
        loader:"vue"
      }
   ]
 },
 resolve:{
   alias:{
     'vue$':'vue/dist/vue.js'
   }
 }
};

2.新建vue文件

新建js/components/heading.vue文件

<template>
<div>
  <h1>{{ message }}</h1>
</div>
</template>

<script>
// 这里使用的是ES6的写法
  export default{
    data(){
      return {
        message:'hello, vue'
      }
    }
  }
</script>

在入口文件entry.js中引入该文件:

require('./module-one.js');

require('./module-two.js');

 require('../css/style.css');  // 这样不加loader前缀标识会报错
// require("!style-loader!css-loader!../css/style.css") // 载入 style.css

document.write('It works.');

// vue 组件操作,这里使用ES6 babel语法
import Vue from 'vue';

Vue.component('Heading', require('./components/heading.vue'));
new Vue({
  el:"#app",
})

// 或者使用下面这种方法加载组件
/**
import Heading from './components/heading.vue';

new Vue({
  el:"#app",
  components:{ Heading }
})
*/

最后不要忘记,在index.html中添加id为app的标示

<!-- index.html -->
<html>
<head>
  <meta charset="utf-8">
  <title> webpack学习 </title>
</head>
<body>
  <h1>Webpack is very nice !</h1>
  <div id="app">
    <!-- 这里添加的是Vue组件 -->
    <Heading></Heading>
  </div>
  <script src="bundle.js"></script>
</body>
</html>

以上就是在webpack中使用Vue组件的步骤。

下面是需要用到的包package.json

{
  "name": "webpack",
  "version": "1.0.0",
  "description": "webpack learning",
  "main": "bundle.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Corwien",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.23.1",
    "babel-loader": "^6.3.2",
    "babel-plugin-transform-runtime": "^6.23.0",
    "babel-preset-es2015": "^6.22.0",
    "babel-preset-stage-0": "^6.22.0",
    "babel-runtime": "^6.23.0",
    "css-loader": "^0.26.2",
    "jquery": "^3.1.1",
    "style-loader": "^0.13.2",
    "vue": "^2.2.1",
    "vue-html-loader": "^1.2.4",
    "vue-loader": "^11.1.4"
  }
}

五、将示例代码放到GitHub进行托管

1.初始化

git init

2.忽略资源文件推送到github

.gitignore文件中添加忽略的文件或文件夹名

node_modules

3.添加并推送

// 1、保存到暂存区:
git add -A

// 2、检查 Git 状态:
git status

// 3.保留改动并提交:
git commit -m "Initial commit"

// 4.将代码上传到github
$ git remote add origin git@github.com:your_username/hello_laravel.git
$ git push -u origin master

至此,示例代码被推送到GitHub上了。

本项目被推送到GitHub的地址:
https://github.com/corwien/we...

请看原文快速入门Webpack

注:本文转自快速入门Webpack

阅读 1.2k

推荐阅读
Corwien
用户专栏

为者常成,行者常至!

960 人关注
290 篇文章
专栏主页