超级详细的webpack教程

webpack #

webpack是一款强大的模块加载器兼打包工具,它能把各种资源,例如JS(含JSX)、coffee、样式(含less/sass)、图片等都作为模块来使用和处理,优势如下:

  1. webpack 是以commonJS的形式来书写,但对AMD/CMD的支持也很全面,方便旧项目进行代码迁移
  2. 能被模块化的不仅仅是JS,还包括各种资源文件
  3. 开发便捷,能替代部分gulp的工作,比如打包、混淆压缩、图片转base64等
  4. 扩展性强,插件机制完善,特别是支持React热插拔

what-is-webpack

我们可以直接使用 require(XXX) 的形式来引入各模块,即使它们可能需要经过编译(比如JSX和sass),但我们无须在上面花费太多心思,因为 webpack 有着各种健全的加载器(loader)在默默处理这些事情,这块我们后续会提到。

你可以不打算将其用在你的项目上,但没有理由不去掌握它,因为以近期 Github 上各大主流的(React相关)项目来说,它们仓库上所展示的示例已经是基于 webpack 来开发的,比如 React-Boostrap 和 Redux。

webpack的官网是 http://webpack.github.io/ ,文档地址是 http://webpack.github.io/docs/ ,想对其进行更详细了解的可以点进去瞧一瞧。

我们谈谈第一点。以 AMD/CMD 模式来说,鉴于模块是异步加载的,所以我们常规需要使用 define 函数来帮我们搞回调:

define(['package/lib'], function(lib){
function foo(){
lib.log('hello world!');
}
return {
foo: foo
};
});

另外为了可以兼容 commonJS 的写法,我们也可以将 define 这么写:

define(function (require, exports, module){
var someModule = require("someModule");
var anotherModule = require("anotherModule");

someModule.doTehAwesome();
anotherModule.doMoarAwesome();

exports.asplode = function (){
someModule.doTehAwesome();
anotherModule.doMoarAwesome();
};
});

然而对 webpack 来说,我们可以直接在上面书写 commonJS 形式的语法,无须任何 define (毕竟最终模块都打包在一起,webpack 也会最终自动加上自己的加载器):

var someModule = require("someModule");
var anotherModule = require("anotherModule");

someModule.doTehAwesome();
anotherModule.doMoarAwesome();

exports.asplode = function (){
someModule.doTehAwesome();
anotherModule.doMoarAwesome();
};

1. webpack命令行 #

1.1 全局安装webpack #

$ npm install webpack -g

在安装 Webpack 前,你本地环境需要支持 node.js

由于 npm 安装速度慢,本教程使用了淘宝的镜像及其命令 cnpm,安装使用介绍参照:使用淘宝 NPM 镜像

使用 cnpm 安装 webpack:

1.2 本地安装webpack #

$ npm install webpack --save-dev

1.3 命令行中使用 #

$    webpack index.js bundle.js

index.js 打包的入口文件路径
bundle.js 打包后的输出文件名

1.4 命令行参数 #

  • webpack 开发环境下编译
  • webpack -p 生产环境下编译,会压缩生成后的文件
  • webpack -w 开发环境下持续的监听文件>变动来进行编译
  • webpack -d 生成map映射文件,会在控制台的Sources页签中出现存放打包前原始文件的webpack://目录,可以打断点,帮助调试
    `webpack index.js bundle.js -d'
  • webpack –progress 显示构建百分比进度
  • webpack –display-error-details 显示打包过程中的出错信息(比如 webpack寻找模块的过程)
  • webpack –profile 输出性能数据,可以看到每一步的耗时

2. 使用webpack配置文件 #

2.1 初始化git #

$ mkdir webpack-demos && cd webpack-demos && git init

2.2 初始化项目 #

$ npm init -y

2.3 增加.gitignore #

创建文件

$ touch.gitignore

在文件中增加以下内容

node_modules
.idea

2.4 在项目根目录下创建src和build目录 #

src目录存放源码,build目录存放编译打包之后的资源

$ mkdir src build

2.5 增加以下文件 #

2.5.1 src/component.js #

$ cd src && touch component.js
exports.name = 'zfpx';

2.5.2 src/index.js #

$ cd src && touch index.js
var comp = require('./component.js');
console.log(comp.name);

2.5.3 build/index.html #

$ cd build && touch index.html
<script src="bundle.js"></script>

2.6 下载webpack #

$ npm install webpack --save-dev

2.7 创建webpack的配置文件 #

$ touch webpack.config.js

配置webpack.config.js

var path = require('path');
module.exports = {
     //打包的入口文件  String|Object
    entry: path.resolve(__dirname, 'src/index.js'),
    output: { //配置打包结果     Object
        //定义输出文件路径
        path: path.resolve(__dirname, 'build'),
        //指定打包文件名称
        filename: 'bundle.js'
    },
};

请注意webpack.config.js这个文件名是定死的,不然会报Output filename not configured的错误;另外,如果不按这个命名,那么在webpack运行的时候需要通过--config这个参数指定配置文件,比如:webpack --config conf.js

每个项目下都必须配置有一个 webpack.config.js ,它的作用如同常规的 gulpfile.js/Gruntfile.js ,就是一个配置项,告诉 webpack 它需要做什么。

我们看看下方的示例:

var webpack = require('webpack');
var commonsPlugin = new webpack.optimize.CommonsChunkPlugin('common.js');

module.exports = {
//插件项
plugins: [commonsPlugin],
//页面入口文件配置
entry: {
index : './src/js/page/index.js'
},
//入口文件输出配置
output: {
path: 'dist/js/page',
filename: '[name].js'
},
module: {
//加载器配置
loaders: [
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{ test: /\.js$/, loader: 'jsx-loader?harmony' },
{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}
]
},
//其它解决方案配置
resolve: {
root: 'E:/github/flux-example/src', //绝对路径
extensions: ['', '.js', '.json', '.scss'],
alias: {
AppStore : 'js/stores/AppStores.js',
ActionType : 'js/actions/ActionType.js',
AppAction : 'js/actions/AppAction.js'
}
}
};

webpack 根据模块的依赖关系进行静态分析,这些文件(模块)会被包含到 bundle.js 文件中。Webpack 会给每个模块分配一个唯一的 id 并通过这个 id 索引和访问模块。 在页面启动时,会先执行 runoob1.js 中的代码,其它模块会在运行 require 的时候再执行。

LOADER

Webpack 本身只能处理 JavaScript 模块,如果要处理其他类型的文件,就需要使用 loader 进行转换。

所以如果我们需要在应用中添加 css 文件,就需要使用到 css-loader 和 style-loader,他们做两件不同的事情,css-loader 会遍历 CSS 文件,然后找到 url() 表达式然后处理他们,style-loader 会把原来的 CSS 代码插入页面中的一个 style 标签中。

⑴ plugins 是插件项,这里我们使用了一个 CommonsChunkPlugin 的插件,它用于提取多个入口文件的公共脚本部分,然后生成一个 common.js 来方便多页面之间进行复用。

⑵ entry 是页面入口文件配置,output 是对应输出项配置(即入口文件最终要生成什么名字的文件、存放到哪里),其语法大致为:

{ entry: { page1: "./page1", //支持数组形式,将加载数组中的所有模块,但以最后一个模块作为输出 page2: ["./entry1", "./entry2"] }, output: { path: "dist/js/page", filename: "[name].bundle.js" } }

该段代码最终会生成一个 page1.bundle.js 和 page2.bundle.js,并存放到 ./dist/js/page 文件夹下。

⑶ module.loaders 是最关键的一块配置。它告知 webpack 每一种文件都需要使用什么加载器来处理:

module: {
//加载器配置
loaders: [
//.css 文件使用 style-loader 和 css-loader 来处理
{ test: /\.css$/, loader: 'style-loader!css-loader' },
//.js 文件使用 jsx-loader 来编译处理
{ test: /\.js$/, loader: 'jsx-loader?harmony' },
//.scss 文件使用 style-loader、css-loader 和 sass-loader 来编译处理
{ test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
//图片文件使用 url-loader 来处理,小于8kb的直接转为base64
{ test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}
]
}

如上,”-loader”其实是可以省略不写的,多个loader之间用“!”连接起来。

注意所有的加载器都需要通过 npm 来加载,并建议查阅它们对应的 readme 来看看如何使用。

拿最后一个 url-loader 来说,它会将样式中引用到的图片转为模块来处理,使用该加载器需要先进行安装:

npm install url-loader -save-dev

配置信息的参数“?limit=8192”表示将所有小于8kb的图片都转为base64形式(其实应该说超过8kb的才使用 url-loader 来映射到文件,否则转为data url形式)

⑷ 最后是 resolve 配置,这块很好理解,直接写注释了:

resolve: {
        //查找module的话从这里开始查找
        root: 'E:/github/flux-example/src', //绝对路径
        //自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名
        extensions: ['', '.js', '.json', '.scss'],
        //模块别名定义,方便后续直接引用别名,无须多写长长的地址
        alias: {
            AppStore : 'js/stores/AppStores.js',//后续直接 require('AppStore') 即可
            ActionType : 'js/actions/ActionType.js',
            AppAction : 'js/actions/AppAction.js'
        }
    }

模块引入

上面唠嗑了那么多配置和执行方法,下面开始说说寻常页面和脚本怎么使用呗。

一. HTML

直接在页面引入 webpack 最终生成的页面脚本即可,不用再写什么 data-main 或 seajs.use 了:

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title>demo</title>
</head>
<body>
  <script src="dist/js/page/common.js"></script>
  <script src="dist/js/page/index.js"></script>
</body>
</html>

可以看到我们连样式都不用引入,毕竟脚本执行时会动态生成<style>并标签打到head里。

二. JS

各脚本模块可以直接使用 commonJS 来书写,并可以直接引入未经编译的模块,比如 JSX、sass、coffee等(只要你在 webpack.config.js 里配置好了对应的加载器)。

我们再看看编译前的页面入口文件(index.js):

require('../../css/reset.scss'); //加载初始化样式
require('../../css/allComponent.scss'); //加载组件样式
var React = require('react');
var AppWrap = require('../component/AppWrap'); //加载组件
var createRedux = require('redux').createRedux;
var Provider = require('redux/react').Provider;
var stores = require('AppStore');

var redux = createRedux(stores);

var App = React.createClass({
    render: function() {
        return (
            <Provider redux={redux}>
                {function() { return <AppWrap />; }}
            </Provider>
        );
    }
});

React.render(
    <App />, document.body
);

一切就是这么简单么么哒~ 后续各种有的没的,webpack 都会帮你进行处理。

~~

2.8 修改 package.json #

  "scripts": {
+    "build": "webpack"
  }

2.9 执行命令进行编译 #

$ npm run build

build目录下会新增了一个bundle.js文件,里面就存放着打包后的目录

3. loader #

使用babel-loader来解析es6写成的模块
加载器列表

3.1 安装loader #

babel-loader可以将ES6的代码转为ES5的代码
babel官网

$ npm install babel-loader babel-core --save-dev
$ npm install babel-preset-es2015 babel-preset-stage-0 --save-dev

3.2 修改webpack.config.js #

module.exports = {
    ////打包的入口文件  String|Object
    entry: path.resolve(__dirname, 'src/index.js'),
    output: {
        //定义输出文件路径
        path: path.resolve(__dirname, 'build'),
        //指定打包文件名称
        filename: 'bundle.js'
    },
    //定义了对模块的处理逻辑     Object
+    module: {
+        loaders: [ 定义了一系列的加载器   Array
+            {
+                test: /\.js$/, //正则,匹配到的文件后缀名
+                // loader/loaders:string|array,处理匹配到的文件
+                loader: 'babel-loader'
+                // include:String|Array  包含的文件夹
+                 // exclude:String|Array  排除的文件夹
+
+            }
+        ]
+    }
};

“-loader”其实是可以省略不写的,多个loader之间用“!”连接起来
loaders是一个数组

3.3 添加.babelrc文件 #

内容如下:

{
   "presets": ["es2015", "stage-0"],
   "plugins": []
}

3.4 修改src/component.js #

-  exports.name = 'zfpx';
+  export var name = 'zfpx';

3.4 修改src/index.js #

-  var comp = require('./component.js');
-  console.log(comp.name);
+  import {name} from './component.js';
+  console.log(name);

3.5 增加.babelrc文件 #

{
  "presets": ["es2015", "stage-0"],
  "plugins": []
}

3.6 执行命令进行编译 #

$ npm run build

4. devServer #

webpack-dev-server是一个Web服务器,可以预览项目,并且当修改源码后可以实时刷新页面
server配置

4.1 安装devServer #

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

4.3 修改package.json #

"scripts": {
+    "dev": "webpack-dev-server"
}

4.4 配置webpack.config.js #

+    devServer: {
+        stats: { colors: true }, //显示颜色
+        port: 8080,//端口
+        contentBase: 'build',//指定静态文件的根目录
+    }

4.5 执行命令进行启动服务 #

$ npm run dev

启动此服务的时候,编译后的产出文件放在内存里,在build目录下看不见,但也不会删除原来已经有的文件

4.6 预览项目 #

打开浏览器中访问
http://localhost:8080

4.7 proxy模拟后台接口 #

4.7.1 修改配置webpack.config.js #

   //重写url
+ function rewriteUrl(replacePath) {
+     return function (req, opt) {
          //取得?所在的索引
+         var queryIndex = req.url.indexOf('?');
          //取得查询字符串的内容
+         var query = queryIndex >= 0 ? req.url.substr(queryIndex) : "";
          //$1取自path匹配到的真实路径中的第一个分组
+         //把proxy的path替换为 '/$1\.json',
+         req.url = req.path.replace(opt.path, replacePath) + query;
+     };
+ }

    devServer: {

        stats: { colors: true }, //显示颜色
        port: 8080,//端口
        contentBase: 'build',//指定静态文件的根目录
+       proxy: [
+           {
                //替换符合此正则的接口路径
+               path: /^\/api\/(.*)/,
                //目标域名端口
+               target: "http://localhost:8080/",
                //重新定向到新的地址
                //$1取自path正则匹配到的真实路径的第一个分组
+               rewrite: rewriteUrl('/$1\.json'),
                 //修改来源地址
+               changeOrigin: true
+           }
+       ]
    }

4.7.2 在build目录下添加 book.json #

{"name":"javascript"}

4.7.3 直接访问此接口 #

在浏览器输入此地址
http://localhost:8080/api/book
将会被重写向到
http://localhost:8080/book.json

5. resolve解析 #

5.1 extension #

指定extension之后可以不用在require或是import的时候加文件扩展名,会依次尝试添加扩展名进行匹配

5.1.1 修改 webpack.config.js #

+ resolve: {
    //自动补全后缀,注意第一个必须是空字符串,后缀一定以点开头
+   extensions: ["",".js",".css",".json"],
+ },

5.1.2 修改src/index.js #

- import comp from './component.js';
+ import comp from './component';

5.2 alias #

配置别名可以加快webpack查找模块的速度

  • 每当引入jquery模块的时候,它会直接引入jqueryPath,而不需要从node_modules文件夹中按模块的查找规则查找
  • 不需要webpack去解析jquery.js文件

5.2.1 先安装jquery #

$ npm install jquery --save

5.2.2 修改 webpack.config.js #

+ var jqueryPath = path.join(__dirname,
+  "./node_modules/jquery/dist/jquery.js");

resolve: {
       extensions: ["",".js",".css",".json"],
+        alias: {
+            'jquery': jqueryPath
+        }
    },

module: {
       loaders: [
           {
               test: /\.js$/,
               loader: 'babel-loader'
           }
       ],
       //如果你 确定一个模块中没有其它新的依赖 就可以配置这项,webpack 将不再扫描这个文件中的依赖
+       noParse: [jqueryPath]
},

5.2.3 修改 build/index.html #

+ <div id="app"></div>

5.2.4 修改 src/index.js #

+import $ from 'jquery'
- document.write(comp);
+ $('#app').html(comp);

6. 解析less样式文件 #

6.1 安装loader #

  • less-loader负责把less源码转成css代码
  • css-loader负责读取css代码
  • style-loader负责在css代码转变成style标签并作为页内样式插入到页面中去
    $ npm install less style-loader css-loader less-loader --save-dev
    

6.2 修改配置文件webpack.config.js #

+  {
+        test: /\.less/,
+        loader: 'style!css!less'
+  }

6.3 增加less文件 src/index.less #

@color: red;
.red {
  color: @color;
}

6.4 在src/index.js中引入’less’文件 #

+  require('./index.less');

6.5 修改build/index.html,使用red #

- <div id="app"></div>
+ <div id="app" class="red"></div>

7. 资源文件的加载 #

实现CSS、图标、图片等资源文件加载

7.1 安装bootstrap和相应的loader #

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

7.2 修改 webpack.config.js #

设置css文件和图标文件的加载器

 devServer: {
        stats: {colors: true}, //显示颜色

+ {
+     test: /\.css/,
+     loader: 'style!css'
+ },
+ {
+      test: /\.(woff|woff2|ttf|svg|eot)$/,
+      loader: "url?limit=8192"
+ },
+ {
+       test: /\.(jpg|png)$/,
+       loader: "url?limit=8192"
+  }

配置信息的参数“?limit=8192”表示将所有小于8kb的图片都转为base64形式(其实应该说超过8kb的才使用url-loader 来映射到文件,否则转为data url形式)

DataURL和图片

7.3 修改 src/index.js #

+  import 'bootstrap/dist/css/bootstrap.css';
+  var img = document.createElement("img");
+  img.className = 'img-circle';
+  img.src = require("./zfpx.jpg");
+  document.body.appendChild(img);

7.4 修改 build/index.html #

+ <button class="btn btn-success">bootstrap按钮</button>
+ <span class="glyphicon glyphicon-qrcode"></span>
+ <img src="/zfpx.jpg" class="img-rounded" alt="lufy">

8. 自动刷新 #

在源码修改之后可以自动刷新页面
修改webpack.config.js

         contentBase: 'build',
+        inline:true, //设置自动刷新
         proxy: [

9. 自动产出html #

9.1 创建html模板文件 #

$ cd src && touch index.html

9.2 下载webpack插件 #

npm install html-webpack-plugin --save-dev

9.3 修改webpack.config.js #

+  var HtmlWebpackPlugin = require('html-webpack-plugin');
+  plugins: [
+        new HtmlWebpackPlugin({
+          title: 'zhufeng-react',//标题
+          template: './src/index.html', //模板文件
+          filename:'./index.html' //产出后的文件名称
+        })
+  ]

10. 自动打开浏览器 #

10.1 安装插件 #

$ npm install open-browser-webpack-plugin --save-dev

10.2 修改webpack.config.js #

+ var openBrowserWebpackPlugin = require('open-browser-webpack-plugin');
+ plugin: [
+   new openBrowserWebpackPlugin({ url: 'http://localhost:8080' })
+ ]

11. 区分环境标识 #

11.1 在脚本中设置环境变量 #

WIN系统

+ "scripts": {
+    "publish-dev": "set BUILD_ENV=dev && webpack-dev-server",
+    "publish-prod": "set BUILD_ENV=prod && webpack-dev-server"
+ }

MAC系统

+ "scripts": {
+    "publish-dev": "export BUILD_ENV=dev && webpack-dev-server",
+    "publish-prod": "export BUILD_ENV=prod && webpack-dev-server"
+ }

11.2 修改webpack.config.js #

+ var webpack = require('webpack');
+ var definePlugin = new webpack.DefinePlugin({
+     __DEV__: (process.env.BUILD_DEV||'dev').trim() == 'dev'
+ });

 plugins: [
+        definePlugin,
        new HtmlWebpackPlugin

12. 暴露全局对象 #

12.1 安装暴露组件 #

$ npm install expose-loader --save-dev

12.2 暴露到全局对象 #

+            {
+                test: /jquery.js$/,
+                loader: "expose?jQuery"
+            }

12.3 在html中使用 #

+ <script>
+     console.log(window.jQuery);
+ </script>

13. css文件单独加载 #

13.1 安装插件 #

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

13.2 修改webpack.config.js #

+      var ExtractTextPlugin = require("extract-text-webpack-plugin");

+      {
+           test: /\.less/,
+           loader: ExtractTextPlugin.extract("style-loader"
                     , "css-loader!less-loader")
+      },
+      {
+           test: /\.css/,
+           loader: ExtractTextPlugin.extract("style-loader"
                     , "css-loader")
+      }

        plugins: [
        definePlugin,
+        new ExtractTextPlugin("bundle.css"),

14. 应用代码和第三方代码分离 #

14.1 修改webpack.config.js #

- entry: path.resolve(__dirname, 'src/index.js'),//入口文件
+ entry: {
+        index:path.resolve(__dirname, 'src/index.js'),
+        vendor: ['jquery'],
+    }
 output: {
        path: path.resolve(__dirname, 'build'),//输出路径
-        filename: 'bundle.js' //输出文件名
+        filename: '[name].js' //输出动态文件名
    },
//如果没有这句话,vendor只是一个普通的入口文件而矣,有了此语句会把vendor中的模块
//从index中分离出来,不再包含在打包出来的index.js中,会成生一个zfvendor.js文件
+ new webpack.optimize.CommonsChunkPlugin('vendor', 'zfvendor.js'),

15. 提取其它公共的代码 #

15.1 增加a.js和b.js #

  • components.js
    export var  name = 'zfpx';
    export var  age = 8;
    
  • a.js
    var {name} = require('./component');
    console.log(name);
    
  • b.js
    var {name} = require('./component');
    console.log(age);
    

15.2 修改webpack.config.js #

    entry: {
+        a:path.resolve('src/a.js'),
+        b: path.resolve('src/b.js')
    }

-   new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
+   new webpack.optimize.CommonsChunkPlugin('common.js'),

  plugins:[
+    new htmlWebpackPlugin({
+                title:'珠峰Webpack',
+                template:'./src/index.html',
+                filename:'./a.html',
+                chunks:['a','common.js']//包含产出的资源
+           }),
+    new htmlWebpackPlugin({
+                title:'珠峰Webpack',
+                template:'./src/index.html',
+                filename:'./b.html',
+                chunks:['b','common.js']//包含产出的资源
+            })
   ]

16. 添加哈希值 #

16.1 修改webpack.config.js #

- filename: '[name].js' //输出文件名
+ filename: '[name].[hash].js' //输出文件名

17. 压缩资源 #

17.1 修改webpack.config.js #

+        new webpack.optimize.UglifyJsPlugin({
+            compress: {
+                warnings: false
+            }
+        }),
+        new webpack.optimize.MinChunkSizePlugin({
+            compress: {
+                warnings: false
+            }
+        }),
+        // 查找相等或近似的模块,避免在最终生成的文件中出现重复的模块
+        new webpack.optimize.DedupePlugin(),
+        // 按引用频度来排序 ID,以便达到减少文件大小的效果
+        new webpack.optimize.OccurenceOrderPlugin(),
+        new webpack.optimize.AggressiveMergingPlugin({
+            minSizeReduce: 1.5,
+            moveToParents: true
+        })

18. 打包react #

18.1 安装 #

$ npm install react react-dom babel-preset-react --save-dev

18.2 增加webpack.config.react.js #

var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var openBrowserWebpackPlugin = require('open-browser-webpack-plugin');
var webpack = require('webpack');

module.exports = {
    entry: path.resolve(__dirname,'react/index.js'),
    output: {
        path: path.resolve(__dirname, 'build'),//输出路径
        filename: 'bundle.js' //输出文件名
    },
    module: {
        loaders: [
            {
                test: /\.jsx?$/,
                loader: 'babel',
                query: { presets: ["es2015","react"] },
                exclude:/node_modules/,
                include:path.resolve(__dirname,'react')
            }
        ],
    },
    devServer: {
        inline:true,
        stats: {colors: true}, //显示颜色
        port: 8080,//端口
        contentBase: 'build',//指定静态文件的根目录
    },
    plugins: [
        new HtmlWebpackPlugin({
            title: '珠峰React课程',
            template: './react/index.html'
        }),
        new openBrowserWebpackPlugin({ url: 'http://localhost:8080' }),
    ]
};

关于babel的预设有三种传参方法 #

  1. .babelrc 中配置 "presets": ["es2015", "stage-0","react"],
  2. 在loader中增加 query: { presets: ["es2015","react"] },,这会对所有的loader生效
  3. loaders: ['react-hot','babel?presets[]=es2015&presets[]=react'] 只针对某个loader生效

18.3 在package.json中增加 #

+ "scripts": {
+     "build-react": "webpack --config webpack.config.react.js",
+     "start-react":"webpack-dev-server --config webpack.config.react.js"
+   }

18.4 启动服务 #

npm run start-react

19. react模块热加载 #

19.1 安装 #

$ npm install react-hot-loader --save-dev

19.2 修改webpack.config.react.js #

{
     test: /\.jsx?$/,
-    loaders: ['babel?presets[]=es2015&presets[]=react'],
-     query: { presets: ["es2015","react"] },
+     loaders: ['react-hot','babel?presets[]=es2015&presets[]=react'],
      exclude:/node_modules/,
      include:path.resolve(__dirname,'react')
     }
     devServer: {
+       hot:true,
        inline:true,
     plugins: [
+    new webpack.HotModuleReplacementPlugin()
      ]

注意: 如果有多个loader的话,就不可以用query属性传参了,只能用?查询字符串传参数

19.4 启动服务 #

npm run start-react

只要修改了源代码,就会在不刷新页面的情况下刷新某个组件

20 重命名类名 #

为了避免不同的组件使用的类名重复可以对这些类名进行重命名

20.1 增加style.css文件 #

.red{
    color:green;
}

20.2 修改webpack.config.react.js #

+ {
+  test: /\.css/,
+  loaders: [ 'style-loader',
+   'css-loader?modules&localIdentName=[name]__[local]___[hash:base64:5]']
+ }

20.3 修改 react/app.js #

+    import styles from './style.css'
     export default class App extends React.Component{
         render(){
             return (
-                <h1>欢迎光临珠峰培训</h1>
+                <h1 className={styles.red}>欢迎光临珠峰培训</h1>
            )
        }
    }

21. 资源列表 #

自定义公共模块提取

但有时候我们希望能更加个性化一些,我们可以这样配置:

var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
    entry: {
        p1: "./page1",
        p2: "./page2",
        p3: "./page3",
        ap1: "./admin/page1",
        ap2: "./admin/page2"
    },
    output: {
        filename: "[name].js"
    },
    plugins: [
        new CommonsChunkPlugin("admin-commons.js", ["ap1", "ap2"]),
        new CommonsChunkPlugin("commons.js", ["p1", "p2", "admin-commons.js"])
    ]
};
// <script>s required:
// page1.html: commons.js, p1.js
// page2.html: commons.js, p2.js
// page3.html: p3.js
// admin-page1.html: commons.js, admin-commons.js, ap1.js
// admin-page2.html: commons.js, admin-commons.js, ap2.js

使用CDN/远程文件

有时候我们希望某些模块走CDN并以<script>的形式挂载到页面上来加载,但又希望能在 webpack 的模块中使用上。

这时候我们可以在配置文件里使用 externals 属性来帮忙:

{
    externals: {
        // require("jquery") 是引用自外部模块的
        // 对应全局变量 jQuery
        "jquery": "jQuery"
    }
}

需要留意的是,得确保 CDN 文件必须在 webpack 打包文件引入之前先引入。

我们倒也可以使用 script.js 在脚本中来加载我们的模块:

var $script = require("scriptjs");
$script("//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js", function() {
  $('body').html('It works!')
});

与 grunt/gulp 配合:

以 gulp 为示例,我们可以这样混搭:

gulp.task("webpack", function(callback) {
    // run webpack
    webpack({
        // configuration
    }, function(err, stats) {
        if(err) throw new gutil.PluginError("webpack", err);
        gutil.log("[webpack]", stats.toString({
            // output options
        }));
        callback();
    });
});

当然我们只需要把配置写到 webpack({ … }) 中去即可,无须再写 webpack.config.js 了。

更多参照信息请参阅:grunt配置 / gulp配置

React 相关

⑴ 推荐使用 npm install react 的形式来安装并引用 React 模块,而不是直接使用编译后的 react.js,这样最终编译出来的 React 部分的脚本会减少 10-20 kb左右的大小。

react-hot-loader 是一款非常好用的 React 热插拔的加载插件,通过它可以实现修改-运行同步的效果,配合 webpack-dev-server 使用更佳!

基于 webpack 的入门指引就到这里,希望本文能对你有所帮助,你也可以参考下述的文章来入门:

webpack-howto

共勉~

~~

未经允许不得转载:WEB前端开发 » 超级详细的webpack教程

赞 (0)