webpack入门

一个包含前端的项目,里面可能有多个.js,多个.css,多个静态图片,多个其他前端资源。一些js资源彼此之间存在依赖关系,当一个页面需要加载多个.js的话,也会拖累整个页面的加载速度。为了解决这个问题,webpack把左边各种各样的静态资源,打包成一个所谓的assets。这样浏览器加载起来就快多了。

WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些预设,打包为合适的格式以供浏览器使用。


webpack打包示意图

webpack的运行需要依赖node.js的运行环境。

简单使用

  • 若是局部安装,先初始化npm init
  • 通过npm install --save-dev webpack安装
  • 配置基本的入口entry及output,简化命令
  • 根据需要配置source maps
  • 配置本地服务器,先要安装webpack-dev-server
  • 若是要用到最新JS代码,可以安装配置Babel
  • 根据需要配置安装将样式表嵌入js的loader(css-loader or style-loader), css的预处理器loader等等
  • 插件使用
初始化

npm init
生成package.json文件

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

安装webpack

//全局安装
npm install -g webpack

//局部安装
npm install --save-dev webpack

文件创建

新建文件夹app,此文件夹用来存放原始数据和JavaScript模块。里面再新建两个js文件
新建文件夹public,存放之后供浏览器读取的文件(包括使用webpack打包生成的js文件以及一个index.html文件)。index.html文件目的在于引入打包后的js文件。


相关目录
使用webpack命令

在终端中使用,基本使用方法

# {extry file}出填写入口文件的路径,本文中就是上述main.js的路径,
# {destination for bundled file}处填写打包文件的存放路径
# 填写路径的时候不用添加{}
webpack {entry file} {destination for bundled file}

如果webpack不是全局安装的,那么在终端中使用此命令时,需要额外指定其在node_modules中的地址,继续上面的例子,在终端中输入如下命令

# webpack非全局安装的情况
node_modules/.bin/webpack app/main.js public/bundle.js

warning: 提醒我未安装webpack-cli

npm install -D webpack-cli

在实际使用中webpack将bundle.js也识别为entry point之一。此时可以去掉output这个路径。建议不要使用这个方式来编译,还是先配置webpack.config.js比较好。

Error: 提醒The 'mode' option has not been set, webpack will fallback to 'production' for this value.
在根目录下创建webpack.config.js文件,写入如下内容

module.exports = {
    mode: 'production'
}
通过配置文件来使用webpack

配置主要涉及到的内容是入口文件路径和打包后文件的存放路径

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

'__dirname'是node.js中的一个全局变量,它指向当前执行脚本所在的目录。

注意我此时用的是windows系统,所以配置的路径,有两个\,这是为了转义。

更快捷的执行打包任务

当webpack局部安装,开启会比较繁琐。可以通过npm引导任务执行,对npm进行配置后,可以在命令行中使用简单的npm start来执行。
package.json文件

{
  "name": "webpack-sample-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node_modules/.bin/webpack"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "webpack": "^4.32.2",
    "webpack-cli": "^3.3.2"
  }
}

webpack的强大功能

生成source maps(使调试更容易)

开发总是离不开调试,方便的调试能极大的提高开发效率,不过有时候通过打包后的文件,你是不容易找到出错了的地方,对应的你写的代码的位置的,Source Maps就是来帮我们解决这个问题的。

通过简单的配置,webpack就可以在打包时为我们生成的source maps,这为我们提供了一种对应编译文件和源文件的方法,使得编译后的代码可读性更高,也更容易调试。

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

devtool 配置结果
source-map 在一个单独的文件中产生一个完整且功能完全的文件。这个文件具有最好的source map,但是它会减慢打包速度
cheap-module-source-map 在一个单独的文件中生成一个不带列映射的map,不带列映射提高了打包速度,但是也使得浏览器开发者工具只能对应到具体的行,不能对应到具体的列(符号),会对调试造成不便
eval-source-map 使用eval打包源文件模块,在同一个文件中生成干净的完整的source map。这个选项可以在不影响构建速度的前提下生成完整的sourcemap,但是对打包后输出的JS文件的执行具有性能和安全的隐患。在开发阶段这是一个非常好的选项,在生产阶段则一定不要启用这个选项
cheap-module-eval-source-map 这是在打包文件时最快的生成source map的方法,生成的Source Map 会和打包后的JavaScript文件同行显示,没有列映射,和eval-source-map选项具有相似的缺点

对小到中型的项目中,eval-source-map是一个很好的选项,再次强调你只应该开发阶段使用它

在webpack.config.js中配置devtool

module.exports = {
    devtool: 'eval-source-map'
}
使用webpack构建本地服务器

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

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

devServer作为webpack配置选项中的一项,以下是它的一些可参考配置。

devserver的配置选项 功能描述
contentBase 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外一个目录下的文件提供本地服务器,应该在这里设置其所在目录(本例设置到“public"目录)
port 设置默认监听端口,如果省略,默认为”8080“
inline 设置为true,当源文件改变时会自动刷新页面
historyApiFallback 在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html
module.exports = {
  devtool: 'eval-source-map',

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

  devServer: {
    contentBase: ".\\public",
    historyApiFallback: true,
    inline: true//实时刷新
  } 
}

在package.json中的scripts对象中添加如下命令,以开启本地服务器

 "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack",
    "server": "webpack-dev-server --open"
  },

在终端中输入npm run server即可在本地的8080端口查看结果

Loaders

通过使用不同的loader,webpack有能力调用外部的脚本或者工具,实现对不同格式的文件的处理。例如:分析转换scss为css,把下一代JS文件转换为现代浏览器兼容的JS文件。

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

  • test:一个用以匹配loaders所处理文件的拓展名的正则表达式(必须)
  • loader:loader的名称(必须)
  • include/exclude:手动添加必须处理的文件(文件夹)或屏蔽不需要处理的文件(文件夹)(可选)
  • query:为loaders提供额外的设置选项(可选)

先把Greeter.js里的问候消息放在一个单独的JSON文件里,并通过合适的配置使Greeter.js可以读取该JSON文件的值,各文件修改后的代码如下:在app文件夹中创建带有问候信息的JSON文件(命名为config.json)

{
  "greetText": "Hi there and greetings from JSON!"
}

更新后Greeter.js

var config = require('./config.json');

module.exports = function() {
  var greet = document.createElement('div');
  greet.textContent = config.greetText;
  return greet;
};

无需再添加webpack1.* 需要的json-loader,已内置可处理JSON文件

Babel

Babel其实是一个编译JavaScript的平台,达到以下目的:

  • 让你能使用最新的JavaScript代码,而不用管标准是否被当前使用的浏览器完全支持
  • 让你能使用基于JavaScript进行拓展了的语言,比如React的JSX

Babel其实是几个模块化的包,其核心功能位于称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用得最多的是解析Es6的babel-env-preset包和解析JSX的babel-preset-react包)。

先一次性安装这些依赖包

(以下命令已经过时)
//模块之间用空格隔开
npm install --save-dev babel-core babel-loader babel-preset-env babel-preset-react

Error: Cannot find module '@babel/core'
babel-loader@8 requires Babel 7.x (the package '@babel/core'). If you'd like to use Babel 6.x ('babel-core'), you should install 'babel-loader@7'.
解决方法

Error: Plugin/Preset files are not allowed to export objects, only functions.
npm install --save-dev @babel/preset-env @babel/preset-react

此时成功了。npm run server之后没有问题,所以以上命令过时,使用如下安装命令代替

npm install --save-dev babel-core @babel/core @babel/preset-env @babel/preset-react

Babel其实可以完全在 webpack.config.js 中进行配置,但是考虑到babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项放在一个单独的名为 ".babelrc" 的配置文件中。(webpack会自动调用.babelrc里的babel配置选项)。

//webpack.config.js
module.exports = {
    entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
    output: {
        path: __dirname + "/public",//打包后的文件存放的地方
        filename: "bundle.js"//打包后输出文件的文件名
    },
    devtool: 'eval-source-map',
    devServer: {
        contentBase: "./public",//本地服务器所加载的页面所在的目录
        historyApiFallback: true,//不跳转
        inline: true//实时刷新
    },
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader",
                    /*此处可以单独放到.babelrc文件中
                    options: {
                        presets: [
                            "@babel/preset-env", "@babel/preset-react"
                        ]
                    }
                    */
                },
                exclude: /node_modules/
            }
        ]
    }
};
//.babelrc
{
  "presets": ["@babel/preset-react", "@babel/preset-env"]
}
一切皆模块

webpack有一个不可不说的优点,它把所有的文件都当做模块处理,JavaScript代码,CSS和fonts以及图片等等通过合适的loader都可以被处理。

CSS

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

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

//使用module.exports = {
 ...
    module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /\.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader"
                    }
                ]
            }
        ]
    }
};

注意引入多个loader的方法。

在app目录下创建一个main.css

/* main.css */html {
  box-sizing: border-box;
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
}

*, *:before, *:after {
  box-sizing: inherit;
}

body {
  margin: 0;
  font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}

h1, h2, h3, h4, h5, h6, p, ul {
  margin: 0;
  padding: 0;
}

为了让webpack能找到main.css,把它导入main.js中。

//main.js
import './main.css';    //使用require导入css文件
CSS module

被称为CSS module的技术意在把JS的模块化思想带入CSS中,通过CSS模块,所有类名,动画名默认都只作用于当前模块。webpack对css模块化提供了非常好的支持,只要在CSS loader中进行简单的配置,就可以直接把CSS的类名传递到组件的代码中,这样做有效避免了全局污染。

//webpack.config.js

{
    test: /\.css$/,
         use: [
                {
                     loader: "style-loader"
                }, {
                     loader: "css-loader",
                     options: {     //此options在install postcss-loader之后,root(节点)不起作用,删除后,又正常
                         modules: true, // 指定启用css modules
                         localIdentName: '[name]__[local]--[hash:base64:5]' // 指定css的类名格式
                     }
                }
              ]
    }
CSS预处理器

Sass 和 Less 之类的预处理器是对原生CSS的拓展,它们允许你使用类似于variables, nesting, mixins, inheritance等不存在于CSS中的特性来写CSS,CSS预处理器可以这些特殊类型的语句转化为浏览器可识别的CSS语句。
以下是常用的CSS 处理loaders:

  • Less Loader
  • Sass Loader
  • Stylus Loader

也存在一个CSS的处理平台-PostCSS,它可以帮助你的CSS实现更多的功能,如为CSS代码自动添加适应不同浏览器的CSS前缀。
首先安装postcss-loader和autoprefixer(自动添加前缀的插件)

npm install --save-dev postcss-loader autoprefixer

接下来,在webpack配置文件中添加postcss-loader,在根目录新建postcss.config.js,并添加如下代码之后,重新使用npm start打包时,你写的css会自动根据Can i use里的数据添加不同前缀了。
webpack.config.js

use: [
    {
        loader: "style-loader"
    }, {
        loader: "css-loader"
    },{
       loader: "postcss-loader"
    }
]

在根目录新建postcss.config.js,并添加如下代码

// postcss.config.jsmodule.exports = {
    plugins: [
        require('autoprefixer')
    ]
}

插件(Plugins)

插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。
Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,可以这么来说,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,插件并不直接操作单个文件,它直接对整个构建过程其作用。

使用插件的方法

要使用某个插件,需要通过npm安装它,后在webpack配置中的plugins关键字部分添加该插件的一个实例(plugins是一个数组)。例如添加一个给打包后代码添加版权声明的插件。

//webpack.config.js
plugins: [
    new webpack.BannerPlugin('版权所有,翻版必究')
    ]



参考:入门Webpack,看这篇就够了

推荐阅读更多精彩内容

  • 2017年12月7日更新,添加了clean-webpack-plugin,babel-env-preset,添加本...
    ZombieBrandg阅读 775评论 0 19
  • webpack 是什么? 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(mo...
    IT老马阅读 2,514评论 2 26
  • 前言 众所周知目前比较火的工具就是gulp和webpack,但webpack和gulp却有所不同,本人两者的底层研...
    cduyzh阅读 860评论 0 13
  • 1.为什么要使用webpack 现今的很多网页其实可以看做是功能丰富的应用,它们拥有着复杂的JavaScript代...
    YINdevelop阅读 138评论 0 5
  • 版权声明:本文为博主原创文章,未经博主允许不得转载。 webpack介绍和使用 一、webpack介绍 1、由来 ...
    it筱竹阅读 6,489评论 0 17