文章说明

这篇文章是在看阮一峰老师在GitHub上发表的webpack学习文档的时候,翻译出的文档说明webpack 阮一峰教程 ,纯手打非机翻。

最后两个demo没有打上来,翻译好会补上来。文中有一小部分文字是我针对这个知识点的理解,就几行。

如果有错误,欢迎指出。

再次感谢阮一峰老师。

什么是webpack

前端构建工具,与gulp和grunt 类似

作为打包工具,与browserify 类似

# be equivalent to
$ webpack main.js bundle.js
复制代码

配置文件是 webpack.config.js

  entry: './main.js',
  output: {
    filename: 'bundle.js'
  }
};
复制代码

常见的命令们

  • webpack 供开发构建
  • webpack -p 供生产构建一次
  • webpack --watch – 连续的增量构建
  • webpack -d – 包括源地图
  • webpack --colors – 让内部更好看

生产环境的应用需要在package.json中配置script :

    "dev": "webpack-dev-server --devtool eval --progress --colors",
    "deploy": "NODE_ENV=production webpack -p"
  },
复制代码

入口文件

就是第一个进入的文件,使用入口文件进行打包或者构建 demo1中的bundle.js

在demo1 的例子中, main.js是入口文件

复制代码

在index.html 中:

  <body>
    <script type="text/javascript" src="bundle.js"></script>
  </body>
</html>
复制代码

webpack 会按照webpack.config.js 去构建 bundle.js

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  }
};
复制代码

使用命令webpack-dev-server 启动服务器

多入口文件

对于多页面网站,适合使用多个入口文件

在main1.js 和main2.js 中

document.write('<h1>Hello World</h1>');

// main2.js
document.write('<h2>Hello Webpack</h2>');
复制代码

index.html 中

<html>
  <body>
    <script src="bundle1.js"></script>
    <script src="bundle2.js"></script>
  </body>
</html>
复制代码

在webpack.config.js 中

module.exports = {
  entry: {
    bundle1: './main1.js',
    bundle2: './main2.js'
  },
  output: {
    filename: '[name].js'
  }
};
复制代码

babel-loader

loader 是预处理器,让webpack处理非js文件,将所有的文件转换为webpack能够处理的有效模块

loader的两个目标

  • 识别出应该被loader转化的文件, 使用test属性
  • 转换这些文件,使他们添加到依赖图中,最终添加到bundle中,使用use属性

babel-loader 是一个将jsx、es6格式的文件转换为js文件的工具。

在main.jsx中:

const ReactDOM = require('react-dom');

ReactDOM.render(
  <h1>Hello, world!</h1>,
  document.querySelector('#wrapper')
);
复制代码

在index.html 中

  <body>
    <div id="wrapper"></div>
    <script src="bundle.js"></script>
  </body>
</html>
复制代码

webpack.config.js

  entry: './main.jsx',
  output: {
    filename: 'bundle.js'
  },
  module: {
    loaders:[
      {
        test: /\.js[x]?$/,
        exclude: /node_modules/,
        loader: 'babel-loader?presets[]=es2015&presets[]=react'
      },
    ]
  }
};
复制代码

在config.js中,module.loaders 用来分配loaders,上面的插件使用了babel-loader,babel-preset-es2015,babel-preset-react 进行编译,test用来检测识别

还可以这样写

  loaders: [
    {
      test: /\.jsx?$/,
      exclude: /node_modules/,
      loader: 'babel-loader',
      query: {
        presets: ['es2015', 'react']
      }
    }
  ]
}
复制代码

css-loader

可以预处理css 文件使用css—loader

main.js

require('./app.css');
复制代码

app.css

body {
  background-color: blue;
}
复制代码

index.html

<html>
  <head>
    <script type="text/javascript" src="bundle.js"></script>
  </head>
  <body>
    <h1>Hello World</h1>
  </body>
</html>
复制代码

webpack.config.js

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  module: {
    loaders:[
      { test: /\.css$/, loader: 'style-loader!css-loader' },
    ]
  }
};
复制代码

这里需要注意到是,必须使用两个加载器来转换,css-loader 是用来读取cssfile,style-loader是用来将style标签插入到html页面的,不同的loader使用‘!’来连接

image loader

同样,图片资源也可以在js 文件中 main.js

var img1 = document.createElement("img");
img1.src = require("./small.png");
document.body.appendChild(img1);

var img2 = document.createElement("img");
img2.src = require("./big.png");
document.body.appendChild(img2);
复制代码

index.html

<html>
  <body>
    <script type="text/javascript" src="bundle.js"></script>
  </body>
</html>
复制代码

webpack.config.js

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  module: {
    loaders:[
      { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192' }
    ]
  }
};
复制代码

url转换image文件,如果小于8192不用特殊,就可以转化成数据url,不然的化就是正常的url。‘?’是用来给loader传递参数的

转化之后的图片如下:

<img src="data:image/png;base64,iVBOR...uQmCC">---小图
<img src="4853ca667a2b8b8844eb2693ac1b2578.png"> ---大图
复制代码

css模块

使用css-loader?modules可以满足css模块的需求

main.js

var React = require('react');
var ReactDOM = require('react-dom');
var style = require('./app.css');

ReactDOM.render(
  <div>
    <h1 className={style.h1}>Hello World</h1>
    <h2 className="h2">Hello Webpack</h2>
  </div>,
  document.getElementById('example')
);
复制代码

app.css

.h1 {
  color:red;
}

:global(.h2) {
  color: blue;
}
复制代码

index.html

<html>
<body>
  <h1 class="h1">Hello World</h1>
  <h2 class="h2">Hello Webpack</h2>
  <div id="example"></div>
  <script src="./bundle.js"></script>
</body>
</html>
复制代码

webpack.config.js

module.exports = {
  entry: './main.jsx',
  output: {
    filename: 'bundle.js'
  },
  module: {
    loaders:[
      {
        test: /\.js[x]?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015', 'react']
        }
      },
      {
        test: /\.css$/,
        loader: 'style-loader!css-loader?modules'
      }
    ]
  }
};
复制代码

如果是css模块是行成自己的作用域?可以使用:global来进行开关 在上面的例子中只有第二个h1是红色的,因为css被本地作用域化了,两个h2都是蓝的,因为是全局的

uglify

webpack有很多管道系统来扩展他的功能,uglify是用来压缩js代码的

main.js

var longVariableName = 'Hello';
longVariableName += ' World';
document.write('<h1>' + longVariableName + '</h1>');
复制代码

index.html

<html>
<body>
  <script src="bundle.js"></script>
</body>
</html>
复制代码

webpack.config.js

var webpack = require('webpack');
var uglifyJsPlugin = webpack.optimize.UglifyJsPlugin;
module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  plugins: [
    new uglifyJsPlugin({
      compress: {
        warnings: false
      }
    })
  ]
};
复制代码

使用plugins 来使用uglify,在exports的上面定义uglify

var uglifyJsPlugin = webpack.optimize.UglifyJsPlugin;

以及使用plugins 来使用uglify

加载第三方管道

html-webpack-plugin能够创建index.html.open-browser-webpack-plugin 能在webpack加载的时候打开一个浏览器标签页

main.js

document.write('<h1>Hello World</h1>')
复制代码

index.html不需要写了哈哈哈哈

webpack.config.js

var HtmlwebpackPlugin = require('html-webpack-plugin');
var OpenBrowserPlugin = require('open-browser-webpack-plugin');

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  plugins: [
    new HtmlwebpackPlugin({
      title: 'Webpack-demos',
      filename: 'index.html'
    }),
    new OpenBrowserPlugin({
      url: 'http://localhost:8080'
    })
  ]
};
复制代码

这个例子中可以看到我们不需要写一个index.html 不需要我们自己打开浏览器,webpack做好了

environment tag

main.js

document.write('<h1>Hello World</h1>');

if (__DEV__) {
  document.write(new Date());
}
复制代码

index.html

<html>
<body>
  <script src="bundle.js"></script>
</body>
</html>
复制代码

webpack.config.js

var webpack = require('webpack');

var devFlagPlugin = new webpack.DefinePlugin({
  __DEV__: JSON.stringify(JSON.parse(process.env.DEBUG || 'false'))
});

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  },
  plugins: [devFlagPlugin]
};
复制代码

如果我们使用下面这行代码将环境变量设置为true,可以看到了日期

$ env DEBUG=true webpack-dev-server
复制代码

代码分割code spliting

相对于大型的app来说,所有的代码放到一个单独的文件会导致低效率的,webpack提供拆分成块的这种机制, 尤其是在某些条件小才会有需求的代码块,这些代码块应该在需要的时候加载,不需要的时候就不加载

使用require.ensure 来定义一个拆分点,用来告诉webpack 这个./a.js应该从bundle.js 中拆分,并且生成一个单独的文件

main.js

// main.js
require.ensure(['./a'], function(require) {
  var content = require('./a');
  document.open();
  document.write('<h1>' + content + '</h1>');
  document.close();
});
复制代码

a.js

module.exports = 'Hello World';
复制代码

index.html

<html>
  <body>
    <script src="bundle.js"></script>
  </body>
</html>
复制代码

web.config.js

module.exports = {
  entry: './main.js',
  output: {
    filename: 'bundle.js'
  }
};
复制代码

在运行的时候,其实webpack构建了两个块,bundle.js 和 1.bundle.js(来自a.js),只有在需要的时候加载1.bundle.js

bundle loader and code splitting

另一种代码拆分的方式是使用bundle-loader

在main.js 文件中

// 现在我们需要a.js了,将会绑定在另一个文件中
// 下面这句是告诉webpack 从别的块中加载a.js 
var load = require('bundle-loader!./a.js');

// 等待a.js可用的时候,我们就能获取到这个exports
// 可以使用异步来等待

load(function(file) {
  document.open();
  document.write('<h1>' + file + '</h1>');
  document.close();
});
复制代码

common chunk

当多个script有公共的代码块时候, 我们希望把这个抽取出来行成一个独立的文件。我们可以使用commonschunkplugin来实现这个功能

例如下面的main1.jsx和main2.jsx

// main1.jsx
var React = require('react');
var ReactDOM = require('react-dom');

ReactDOM.render(
  <h1>Hello World</h1>,
  document.getElementById('a')
);

// main2.jsx
var React = require('react');
var ReactDOM = require('react-dom');

ReactDOM.render(
  <h2>Hello Webpack</h2>,
  document.getElementById('b')
);
复制代码

index.html

<html>
  <body>
    <div id="a"></div>
    <div id="b"></div>
    <script src="init.js"></script>
    <script src="bundle1.js"></script>
    <script src="bundle2.js"></script>
  </body>
</html>
复制代码

webpack.config.js

var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
  entry: {
    bundle1: './main1.jsx',
    bundle2: './main2.jsx'
  },
  output: {
    filename: '[name].js'
  },
  module: {
    loaders:[
      {
        test: /\.js[x]?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015', 'react']
        }
      },
    ]
  },
  plugins: [
    new CommonsChunkPlugin('init.js')
  ]
}

复制代码

三方代码块 vendor chunk

使用Commonschunkplugin还可以实现吧script标签内的三方库抽取出形成一个单独的文件

例如jQuery

main.js

var $ = require('jquery');
$('h1').text('Hello World');
复制代码

index.html

<html>
  <body>
    <h1></h1>
    <script src="vendor.js"></script>
    <script src="bundle.js"></script>
  </body>
</html>
复制代码

webpack.config.js

var webpack = require('webpack');

module.exports = {
  entry: {
    app: './main.js',
    vendor: ['jquery'],
  },
  output: {
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.optimize.CommonsChunkPlugin(/* chunkName= */'vendor', /* filename= */'vendor.js')
  ]
};
复制代码

commonchunkplugin中有两个参数,第一个是抽取出的块的名字,第二个是抽取出之后的文件

如果想要让某个模块全局可用, 例如不使用require('jquery')却可以全局使用$和 jQuery, 可以使用ProvidePlugin来实现.

main.js

$('h1').text('Hello World');
复制代码

webpack.config.js

var webpack = require('webpack');

module.exports = {
  entry: {
    app: './main.js'
  },
  output: {
    filename: 'bundle.js'
  },
  plugins: [
    new webpack.ProvidePlugin({
      $: "jquery",
      jQuery: "jquery",
      "window.jQuery": "jquery"
    })
  ]
};
复制代码

暴露全局变量

data.js

var data = 'Hello World';
复制代码

使用externals来暴露全局变量 // webpack.config.js

module.exports = {
  entry: './main.jsx',
  output: {
    filename: 'bundle.js'
  },
  module: {
    loaders:[
      {
        test: /\.js[x]?$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        query: {
          presets: ['es2015', 'react']
        }
      },
    ]
  },
  externals: {
    // require('data') is external and available
    //  on the global var data
    'data': 'data'
  }
};
复制代码

在externals属性中data作为全局变量在script中。

在main.jsx 中使用data:

var data = require('data');
var React = require('react');
var ReactDOM = require('react-dom');

ReactDOM.render(
  <h1>{data}</h1>,
  document.body
);
复制代码
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐