Webpack 学习

Webpack 学习

Webpack是目前基于React和Redux开发的应用的主要打包工具。


Webpack的核心原理

1. 一切皆模块

正如js文件可以是一个“模块(module)”一样,其他的(如css、image或html)文件也可视作模块。因此,你可以require('myJSfile.js'),亦可以require('myCSSfile.css')。这意味着我们可以将事物(业务)分割成更小的易于管理的片段,从而达到重复利用等的目的。

2. 按需加载

传统的模块打包工具(module bundlers)最终将所有的模块编译生成一个庞大的bundle.js文件。但是在真实的app里边,“bundle.js”文件可能有10M到15M之大可能会导致应用一直处于加载中状态。因此Webpack使用许多特性来分割代码然后生成多个“bundle”文件,而且异步加载部分代码以实现按需加载。


开发模式和生产模式

Webpack有许许多多的特性,一些是”开发模式“下才有的,一些是”生产模式“下才有的,还有一些是两种模式下都有的。通常使用到Webpack如此多特性的项目都会有两个比较大的Webpack配置文件。

为了生成bundles文件,在package.json文件加入如下的scripts项:

"scripts": {
  // 运行npm run build 来编译生成生产模式下的bundles
  "build": "webpack --config webpack.config.prod.js",
  // 运行npm run dev来生成开发模式下的bundles以及启动本地server
  "dev": "webpack-dev-server"
 }

webpack CLI 和webpack-dev-server

Webpack作为模块打包工具,提供两种用户交互接口:

  • Webpack CLI tool:默认的交互方式(已随Webpack本身安装到本地)

  • webpack-dev-server:一个Node.js服务器(需要开发者从npm自行安装)

Webpack CLI(有利于生产模式下打包)

这种方式可以从命令行获取参数也可以从配置文件(默认叫webpack.config.js)获取,将获取到的参数传入Webpack来打包。

用法:

方式1: 
// 全局模式安装webpack
npm install webpack --g
// 在终端输入
$ webpack // <--使用webpack.config.js生成bundle

方式 2 :
// 费全局模式安装webpack然后添加到package.json依赖里边
npm install webpack --save
// 添加build命令到package.json的scripts配置项
"scripts": {
 "build": "webpack --config webpack.config.prod.js -p",
 ...
 }
// 用法:
"npm run build"

webpack-dev-server(有利于在开发模式下编译)

这是一个基于Express.js框架开发的web server,默认监听8080端口。server内部调用Webpack,这样做的好处是提供了额外的功能如热更新“Live Reload”以及热替换“Hot Module Replacement”(即HMR)。

用法:

方式 1:
// 全局安装
npm install webpack-dev-server --save
// 终端输入
$ webpack-dev-server --inline --hot

用法 2:
// 添加到package.json scripts
"scripts": {
 "start": "webpack-dev-server --inline --hot",
 ...
 }
// 运行: 
$ npm start

// 浏览器预览:
http://localhost:8080

可以通过以下两种方式向webpack-dev-server传入参数:

  • 通过webpack.config.js文件的”devServer”对象

  • 通过CLI选项

// 通过CLI传参
webpack-dev-server --hot --inline
// 通过webpack.config.js传参
devServer: {
  inline: true,
  hot:true
}

“inline”选项会为入口页面添加“热加载”功能,“hot”选项则开启“热替换(Hot Module Reloading)”,即尝试重新加载组件改变的部分(而不是重新加载整个页面)。如果两个参数都传入,当资源改变时,webpack-dev-server将会先尝试HRM(即热替换),如果失败则重新加载整个入口页面。

// 当资源发生改变,以下三种方式都会生成新的bundle,但是又有区别:

// 1. 不会刷新浏览器
$ webpack-dev-server
//2. 刷新浏览器
$ webpack-dev-server --inline
//3. 重新加载改变的部分,HRM失败则刷新页面
$ webpack-dev-server  --inline --hot

“entry”:值分别是字符串、数组和对象的情况

Enter配置项告诉Webpack应用的根模块或起始点在哪里,它的值可以是字符串、数组或对象。这看起来可能令人困惑,因为不同类型的值有着不同的目的。
倘若应用只有一个单一的入口,enter项的值可以使用任意类型,最终输出的结果都是一样的。

  • 数组

    如果想添加多个彼此不互相依赖的文件,可以使用数组格式的值。例如,在html文件里引用了“googleAnalytics.js”文件,可以告诉Webpack将其加到bundle.js的最后。

  • 对象
    如果应用是多页面的(multi-page application)而不是SPA,有多个html文件(index.html和profile.html)。通过一个对象告诉Webpack为每一个html生成一个bundle。

    以下的配置将会生成两个js文件:indexEntry.js和profileEntry.js分别会在index.html和profile.html中被引用。

  • 混合类型

    下面的配置将会生成3个文件:vender.js(包含三个文件),index.js和profile.js文件。


output:“path”项和“publicPath”项

output项告诉webpack怎样存储输出结果以及存储到哪里。

  • “path”仅仅告诉Webpack结果存储在哪里

  • “publicPath”项则被许多Webpack的插件用于在生产模式下更新内嵌到css、html文件里的url值。


模块加载和链式模块加载

模块加载器是可自由添加的Node模块,用于将不同类型的文件“load”或“import”并转换成浏览器可以识别的类型,如js、Stylesheet等。更高级的模块加载器甚至可以支持使用ES6里边的“require”或“import”引入模块。

  • 链式(管道式)的加载器(从右往左执行)
    多个loader可以用在同一个文件上并且被链式调用。链式调用时从右到左执行且loader之间用“!”来分割。

    例如,假设有一个名为“myCssFile.css”的css文件,然后将它的内容使用style标签内联到最终输出的html里边。可以使用css-loader和style-loader两个loader来达到目的。

    module: {
     loaders: [{
      test: /\.css$/,
      loader: 'style-loader!css-loader'
     }]
    • Webpack在模块顶部搜索在css的依赖项,即Webpack检查js文件是否有“require(‘myCssFile.css’)”的引用,如果它发现有css的依赖,Webpack将css文件交给“css-loader”去处理

    • css-loader加载所有的css文件以及css自身的依赖(如,@import 其他css)到JSON对象里,Webpack然后将处理结果传给“style-loader”

    • style-loader接受JSON值然后添加一个style标签并将其内嵌到html文件里


loader自身可以配置

模块加载器(loader)自身可以根据传入不同的参数进行配置。

在下面的例子中,可以配置url-loader来将小于1024字节的图片使用DataUrl替换而大于1024字节的图片使用url,可以用如下两种方式通过传入“limit”参数来实现这一目的:


.babelrc 文件

babal-loader 使用“presets”配置项来标识如何将 ES6 语法转成 ES5 以及如何转换 React 的 JSX 成 js 文件。可以用如下的方式使用”query“参数传入配置:

module: {
  loaders: [
    {
      test: /\.jsx?$/,
      exclude: /(node_modules|bower_components)/,
      loader: 'babel',
      query: {
        presets: ['react', 'es2015']
      }
    }
  ]
}

然而在很多项目里babal的配置可能比较大,因此可以把babal-loader的配置项单独保存在一个名为”.babelrc“的文件中,在执行时babal-loader将会自动加载.babelrc文件。

//webpack.config.js 
module: {
  loaders: [
    {
      test: /\.jsx?$/,
      exclude: /(node_modules|bower_components)/,
      loader: 'babel'
    }
  ]
}

//.bablerc
{
 presets: ['react', 'es2015']
}

插件

插件一般都是用于输出bundle的node模块。

例如,uglifyJSPlugin 获取 bundle.js 然后压缩和混淆内容以减小文件体积。

类似的 extract-text-webpack-plugin 内部使用 css-loader 和 style-loader 来收集所有的css到一个地方最终将结果提取结果到一个独立的”styles.css“文件,并且在html里边引用style.css文件。

//webpack.config.js
// 获取所有的.css文件,合并它们的内容然后提取css内容到一个独立的”styles.css“里
var ETP = require("extract-text-webpack-plugin");

module: {
 loaders: [
  {test: /\.css$/, loader:ETP.extract("style-loader","css-loader") }
  ]
},
plugins: [
    new ExtractTextPlugin("styles.css") //Extract to styles.css file
  ]
}

如果只是想把css 使用 style 标签内联到 html 里,不必使用 extract-text-webpack-plugin,仅仅使用 css loader 和 style loader 即可。


加载器(loader)和插件

Loader处理单独的文件级别并且通常作用于包生成之前或生成的过程中。

而插件则是处理包(bundle)或者 chunk 级别,且通常是 bundle 生成的最后阶段。一些插件如 commonschunkplugin 甚至更直接修改bundle的生成方式。


处理文件的扩展名

很多Webpack的配置文件都有一个resolve属性。

{
 resolve: {
   extensions: ['', '.js', '.jsx']
 }
}

空字符串在此是为了resolve一些在import文件时不带文件扩展名的表达式,如require('./myJSFile')或者import myJSFile from './myJSFile'(译者注:实际就是自动添加后缀,默认是当成js文件来查找路径)

本文转载自 [译] Webpack——令人困惑的地方

猜你喜欢

转载自blog.csdn.net/Milk_o3o/article/details/79391603