webpack使用教程(一)

这篇博客是自己看了一些别人写的文章或是参考官方文档后整理的。算是笔记类的吧。对于webpack的使用我分成了两个部分来写,在这篇博客中主要是对webpack的基本概念、使用方法进行说明。在下一篇博客我会对在一个项目中具体怎么应用进行说明。

webpack简介

webpack是一种模块化方案,将JS、CSS和图片等资源都当做模块来处理。提供一个入口文件,它会把该文件以及该文件所依赖的其他模块都打包成一个文件。
在模块化这个角度,与requireJS有点相像,不同的是requireJS是AMD模式的,相当于是在浏览器中增加了一个AMD解释器,这样浏览器就可以识别requiredefine 等标识,而webpack支持commonJS、AMD以及ES6,并且是预编译的,提前就转成了浏览器可以识别的语言。
此外,webpack还提供了gulp等前端构建工具的功能,例如SASS等的预处理、压缩打包等前端流程化的功能。

安装webpack就不说了,用npm非常简单,主要对配置文件进行一下解读吧,当运行命令webpack (前提是全局安装了)后,默认就是查找该文件,当然你也可以通过webpack --config <文件名> 来运行别的配置文件。

webpack.config.js 文件使用说明

该文件中要使用commonjs风格。先将webpack引进来:

var webpack = require('webpack');

需要使用module.exports = {} 来导出具体的配置项。配置选项中通常包括以下一些内容:

context

为entry指定base directory ,需使用绝对路径,如果设置了该项,那么entry需要使用相对路径,相对于base directory:

context: __dirname + '/src',
entry: {
      main: './js/main.js',
      vendor: ['./js/a.js', './js/b.js']
},

entry

其值可以是:

  • 字符串
    当只有一个入口文件

  • 数组
    数组中的所有文件都会被加载,最后一个文件将被输出(其他文件都合并到该文件中),如果output 中没有命名或采用[name] 的形式来命名,会输出文件main.js

  • 对象
    采用该方式可以设置多个入口文件,这样在页面中进行相应的引用就行了。输出的文件名是entry对象中的key值。

entry: {
    main: './js/main.js',
    vendor: ['./js/a.js', './js/b.js']
}

output

配置输出文件。

filename

可以使用[name][hash][chunkhash]作为输出文件的名字:

output: {
    filename: '[hash].js' //或'[name].js''[name]-[hash].js'
}

如果entry不是对象,则这里的[name] 值就是main .注意此处的hash 值是指整体编译一次的值,而不是每个文件单独改变编译后的值,[chunkhash] 才是每个chunk自己的hash值。

path

输出文件的位置,需要是绝对路径。

publicPath

官网上是这么解释的:

The publicPath specifies the public URL address of the output files when referenced in a browser. For loaders that embed <\script> or <\link> tags or reference assets like images, publicPath is used as the href or url() to the file when it’s different than their location on disk (as specified by path). This can be helpful when you want to host some or all output files on a different domain or on a CDN. The Webpack Dev Server also uses this to determine the path where the output files are expected to be served from. As with path you can use the [hash] substitution for a better caching profile.

解释一下就是,path指的输出文件要放到磁盘中哪个位置,而publicpath是指浏览器或者webpack dev server从哪里获取一些如图片、chunks等资源。特别是当需要把输出文件放置到另一个域或者CDN上时。因此,在引用文件的时候使用相对路径,浏览器访问的时候会从publicpath开始找。

还有一些其他解释:

When executed in the browser, webpack needs to know where you’ll host the generated bundle. Thus it is able to request additional chunks (when using code splitting) or referenced files loaded via the file-loader or url-loader respectively.

No, this option is useful in the dev server, but its intention is for asynchronously loading script bundles in production. Say you have a very large single page application (for example Facebook). Facebook wouldn’t want to serve all of its javascript every time you load the homepage, so it serves only whats needed on the homepage. Then, when you go to your profile, it loads some more javascript for that page with ajax. This option tells it where on your server to load that bundle from。

大体意思就是和path的区别是,path是针对本地文件系统,而publicPath是相对于浏览器的。当要进行异步按需加载的时候,publicpath告知了从哪里进行其他的chunk的获取(以ajax方式)。

此外,还可以给path和publicpath添加[hash] 以避免缓存。??不太会用

参考: What does “publicPath” in webpack do?

chunkFileName

定义非入口文件名,比如通过代码分割所提取出来的文件。
还有一些其他的配置项,就不一一列举了。

devtool

生成 source maps文件,对编译后的文件和源文件的对应,方便我们调试。该配置项有以下四个选项:

  • source-map
  • cheap-module-source-map
  • eval-source-map
  • cheap-module-eval-source-map

在文档入门webpack,看这篇就够了 中对该配置项的四个选项的区别有具体说明。

loader

这是webpack实现模块化非常重要的部分。loader用来对各种文件进行处理。

loader本质上做的是一个anything to JS的转换

需要通过npm 将各种loader安装到项目目录下。loader的使用有两种方式,一个是在config文件中进行配置,另一个是直接在需要处理的文件引用处使用loader。

  • config文件中进行具体的配置:
module: {
    loaders: [ 
        {
            test: /\.js$/, //要匹配的文件格式
            loader: 'babel', //使用的插件
            exclude: /node_modules/,
            query: {  //query中追加参数
                presets: ['es2015']
            }
        },
        {
            test: /\.scss/,
            loader: 'style!css!sass' //用!(pipe)来连接多个loader,处理顺序从右到左
        },
        {
            test: /\.html/,
            loader: 'html'
        }
    ]
}
  • 也可以直接在引用需要处理的文件处使用前缀:
import 'style!css!sass!../style/main.scss';
require('babel!?es2015./main.js');

显然在配置文件中使用更好,方便管理。
对于babel的配置可以在项目根目录下使用.babelrc 文件,loader会自己去找这个文件。
对loader指定参数,详见官方文档。
可以看到,webpack将css文件也当做了模块。这样把JS的模块化思想带入CSS中来,通过CSS模块,所有的类名,动画名默认都只作用于当前模块。下面具体解释下:

将CSS文件当做模块

一个CSS文件也可以看做是一个模块,可以在需要的JS文件中进行引入:

import './style/main.scss'; //es6
require('./style/main.scss') //commonJS或AMD

此外,还可以把CSS文件真正地当做模块来使用,而其中的类名当做属性,只需在loader中增加?module

//scss
.root {
    padding: 10px;
    border: 1px solid #ccc;
    background: #eee;
    color: pink;
}

//config
{
    test: /\.scss$/,
    loader: 'style!css?modules!sass'
}
//js
import Styles from './style/main.scss';
let _html = ' 
'

其中的css-loader用来解析css,这样

@import and url(…) are interpreted like require() and will be resolved by the css-loader

而style-loader用来将css文件内联到HTML文件中。这样将CSS文件引入JS中会将其打包到一起。

plugins

loader是用来处理源文件的,而plugins则是对整个构建过程起作用,并不是处理单个文件。

插件html-webpack-plugin

例如插件html-webpack-plugin 用来自动在html文件中引入打包好的JS文件和用ExtractTextPlugin抽离出的CSS文件,并且JS文件是附加好hash值的,然后该HTML5文件最终会生成在output.path指定的目录下:

var HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
    new HtmlWebpackPlugin({
        template: __dirname + '/src/index.html'
    })
]

src目录下的index.html什么文件都不需要引入,该插件会自动帮我们添加打包好的文件到其中:


webpack使用教程(一)_第1张图片

由于页面已经生成到了dist路径下,因此浏览器中需要改变访问路径为:

http://localhost:3000/dist

使用插件ExtractTextPlugin将CSS从JS中抽离

这样可以将CSS单独生成一个文件,从而同JS进行并发下载,减少网络请求时间。
在config中进行配置:

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

//loader
{
    test: /\.scss$/,
    loader: ExtractTextPlugin.extract('style', 'css!sass')
}

//plugin
plugins: [
    new ExtractTextPlugin("[name]-[hash].css")
]

浏览器中查看生成的文件:


webpack使用教程(一)_第2张图片

其他常用插件

  • webpack.optimize.CommonsChunkPlugin, 用于提取多个入口文件中的公用模块。
    用法参考:
    怎么打包公共代码才能避免重复?
    webpack CommonsChunkPlugin详细教程

代码分割

所有的代码都打包到一个JS文件中会造成无法并发加载,首次加载时间过长、无法利用缓存等问题。为了将一个JS文件拆分成多个文件来并发下载,webpack提供了一些机制来进行代码的分割。
可以通过if 语句来进行判断什么时候加载所分割出去的模块。
此外,在html 中还是只引入主js 文件,不用手动引别的分割后生成的脚本,split points会告知webpack代码的分割位置,webpack依次设置代码块(chunk),并在运行环境中由webpack自动加载代码块。需要在config中进行配置publicPath,告诉webpack去哪里找这些chunk.

有以下两种方式在代码中设置分割点:

  • 使用require.ensure

    该方式是commonjs 风格的。

  • AMD动态require

    这两种方式都是在代码中设置分割点,那么一个大文件会被拆分成多个文件来下载。

    require.ensure的模块只会被下载下来,不会被执行,只有在回调函数使用require(模块名)后,这个模块才会被执行。
    而AMD方式会按顺序下载并执行所依赖的模块。

例如在入口文件main.js中有以下依赖的内容:

import sayHello from './test';

sayHello();

// 分割点1
require.ensure(['./a'], function(require) {
    let a = require('./a');
    a.greetFromA(); 
});

// 分割点2
require(['./b'], function(b) {
    b.greetFromB();
});

//注意,如果依赖模块中用es6,好像不能用default来导出,查到了文档,如果要用default导出,那么在require中要用.default来获取,例如var a = require('./a').default;

webpack.config.js中有以下配置:

entry: __dirname + '/src/js/main.js',
output: {
    path: __dirname + '/dist', //需要绝对路径
    filename: '[name].js',
    publicPath: '/dist/' //浏览器访问资源的路径
},

index.html中只用引入打包后的文件:

<script src="./dist/main.js">script>

可以看到,浏览器中总共加载了3个JS文件:


webpack使用教程(一)_第3张图片

此外还有一种方式来解决将所有代码打包到一个JS文件中的问题,就是设置多个入口文件,但多个入口文件就会有很多交叉重复的部分,而且不能利用浏览器缓存。使用插件CommonsChunkPlugin 可以抽取公用的部分,把公用的部分生单独生成一个文件。这种方式webpack不会自动加载,比如手动依次将打包后的文件引入到HTML文件中。

关于代码分割具体可以参看文档:使用webpack分割代码的思路

补充一个知识点,webpack文档里老提起chunk,这个chunk到底是啥玩意儿啊,其实打包成后生成的就是chunk。
关于chunk和代码分割还可以查看官方文档:CODE SPLITTING

参考:
使用webpack分割代码的思路
经典webpack入门(讲的很透彻)

webpack-dev-server

它是一个基于nodejs的本地服务器。

webpack-dev-server 是一个静态资源WEB服务器,只用于开发环境,它会把编译后的静态文件全部保存在内存里,而不会写入到文件目录内,这样做就不用改变webpack输出目录。

webpack-dev-server有两种模式支持自动刷新——iframe模式和inline模式

It’s important to specify a correct output.publicPath otherwise the hot update chunks cannot be loaded.

output.publicPath的参数值是页面运行时bundle模块的访问路径,但它是在内存中的,实际并没有编译成本地文件。

参考资料:
Webpack Development Server 开发服务器详解

webpack dev server 具有自动更新的功能。每当有文件更新,会检测到然后重新打包,并自动刷新页面。

热加载

即Hot Module Replacement,这是一个webpack的插件,通过它可以实现某个模块改变后只单独重新编译替换改模块,而不会整个重新编译一遍。
使用时需要以下步骤:

  1. 在webpack配置文件中添加HMR插件;

  2. 在Webpack Dev Server中添加“hot”参数

  3. 在JS模块中使用webpack提供的API:
  4. 4.

探究Webpack中的HMR(hot module replacement)

与gulp的区别

首先,其实webpack只是具有前端构建的功能而已,其实本质来说webpack是一种模块化的解决方案类似require.js一样,只不过通过插件实现了构建工具的一些功能,例如通过less-loader可以编译less为css并作为模块可以被调用。gulp是通过一系列插件将原本复杂繁琐的任务自动化,是一个纯粹的工具,并不能将你的css等非js资源模块化,但是webpack可以做到这些。总的来说,gulp是一个自动化任务的工具,所以你可以通过gulp来配置webpack的文件。

Gulp是一个前端开发流程工具,并不提供模块化,而webpack等等是模块化方案。

参考:webpack与gulp的区别及实例搭建
Webpack、Browserify和Gulp三者之间到底是怎样的关系?

参考链接

开发环境,生成的文件不要加hash值,在生产环境中才需要。
Vue+Webpack开发可复用的单页面富应用教程

webpack教程:
入门Webpack,看这篇就够了
WebPack 常用功能介绍

根据环境变量加载设置不同的配置项:
webpack学习实践系列(一)
vue+webpack多页应用:
在多页面项目下使用Webpack+Vue

vue-cli-multi-page

你可能感兴趣的:(开发工具)