Webpack 学(cai)习(keng)之路

金沙官网线上 1

十四、 BundleAnalyzerPlugin插件

在介绍该插件之前,我们可以先对前面的代码打包,然后看一下打包出来的那个JS文件体积,会发现我们仅仅写了那么几行代码,但是打包出来的js文件都有2M+,此时假设项目已经完成,该上线了,但这种直接打包出来的文件是肯定不能直接推到线上的,因为体积太大。
此时就可以用到该插件了。这个插件作用是分析项目依赖的,配置好该插件之后,运行完打包命令,浏览器会自动打开一个窗口,显示具体依赖的模块,清晰明了直观。

配置:首先安装该模块

npm install webpack-bundle-analyzer --save-dev

在配置文件中引入以及在插件 plugins 数组中实例化出一个插件:

//webpack.config.js

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
  ...
module.exports = {
  ...
  plugins:[
      ...
    new BundleAnalyzerPlugin()
  ]
}

再次运行打包命令,浏览器自动打开页面如下图:

image.png

我们可以清楚地看到生成的 main.js 文件有 2.5Mb,体积很大,我们需要对其进行优化。

二. 解决方案的升级

  • 旧的解决方案预编译语言 + 命名方法论

    在不使用构建工具的时代,开发者使用预编译语言来实现变量定义,选择器嵌套等一些刚需,再使用函数功能来实现一些更为复杂的需求,例如编写简单的@mixin px2rem( )函数来将开发中使用的px单位转换为rem单位,达到移动端自适应的目的,或是编写一些处理兼容性的函数来处理浏览器兼容性。

    命名的方法论非常多,最为流行的当属BEM,也就是采用**block__Element-Modifier**这样的命名方式来进行模块划分,还有提倡碎片化样式的Aotm-CSS及面向对象的OOCSS等,都是一种命名方法论,也意味着没有硬性的检测和预防措施。

  • 新的解决方案预编译语言 + 构建工具 + BEM + ACSS全局样式+CSSModule组件样式+ POSTCSS

    预编译语言的使用基本不变,但现代化开发中已经不再需要通过预定义函数来解决单位转换或是兼容性的问题。首先,构建工具可以通过自动化检测将预编译语言转换为CSS,基于现代化构建工具的CSS-Module功能,可以通过特定的语法解决CSS模块化的问题,而基于POSTCSS实现的autoprefixer插件,可以依据CanIUse网站提供的浏览器支持度数据实现代码的跨浏览器前缀自动补齐。

    新的方案涉及到很多新的概念,但这并不是简单的炫技,每一个概念都有优点和适用的场合,你需要在恰当的场合使用恰当的技术,最愚蠢的做法就是因为某种技术热门而盲目地要求开发人员在整个项目中使用。

webpack

三. 基本使用方法

五、 Source Maps

我们都知道,打包生成的文件其实可读性是很差的。如果我们在本地编写代码时候不小心写错了,然后打包生成的文件肯定是达不到我们想要的效果,而在生成的文件中查错即找bug是不现实的,我们需要直观明显的显示出错位置,这个时候就需要使用Source Maps 来显示出来我们写的代码出错的位置。
同样,我们需要在 webpack.config.js 中进行配置。

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,大型项目考虑时间成本时可以使用cheap-module-eval-source-map。另外,Source Map 只应该在开发阶段使用,生产阶段记得将该配置其去除

更新webpack.config.js:

//webpack.config.js

module.exports ={
        entry:__dirname + '/app/main.js',
        output:{
                path: __dirname + '/build',
                filename:'bundle.js'
        },

        devServer: {
                contentBase:"./build",
                historyApiFallback:true,
                inline:true
        },
        //更新代码:
        devtool: "eval-source-map"    //配置Source Map 
}

四. 使用CSS-Modules

项目地址:CSS Modules开源地址

CSS Module在CSS中使用类选择器,其基本原理是将CSS代码中的样式名替换为哈希值,并建立一个json对照表,在js文件中对于属性名选择器的使用均被替换为哈希字符串,以此来解决CSS模块化的问题。

在webpack中使用CSS Modules功能非常简单,只需要在css-loader的配置参数中设置:{modules:true}即可激活模块化功能。

开启模块化功能后再进行打包,可以看到同样的main.css文件变成了如下样子:

金沙官网线上 2

而在打包文件中增加了如下片段:

金沙官网线上 3

当然CSS Modules的用法远不止如此,更多的信息可以参见上面的项目地址。

十、 插件(Plugins)

插件(Plugins)是用来拓展Webpack功能的,它们会在整个构建过程中生效,执行相关的任务。

Plugins 和 Loaders 的区别:
 >> loaders 是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个。
 >> plugins 并不直接操作单个文件,它直接对整个构建过程起作用,比如切割代码,分离css,压缩代码等。。。

使用插件的方法:
首先通过npm安装插件,然后在配置文件中引入该插件,接着在与 rules 同层级的位置添加一个 plugins 关键字,结构如图, plugins 是一个数组,可以添加多个插件,后面将介绍几种常用插件及其基本配置方法。

plugins 在配置文件中的位置

3.1 常用插件及功能简述

webpack4.0版本为例来演示CSS模块的处理方式,需要用到的插件及功能如下:

  • style-loader——将处理结束的CSS代码存储在js中,运行时嵌入<style>后挂载至html页面上
  • css-loader——加载器,使webpack可以识别css模块
  • postcss-loader——加载器,下一篇将详细描述
  • sass-loader——加载器,使webpack可以识别scss/sass文件,默认使用node-sass进行编译
  • mini-css-extract-plugin——插件,4.0版本启用的插件,替代原extract-text-webpack-plugin插件,将处理后的CSS代码提取为独立的CSS文件
  • optimize-css-assets-webpack-plugin——插件,实现CSS代码压缩
  • autoprefixer——自动化添加跨浏览器兼容前缀
四、 webpack-dev-server

本小节我们将介绍一个本地服务器 webpack-dev-server
webpack-dev-server 基于 node.js 构建,可以让浏览器监听我们的代码修改,代码一旦改动并保存,浏览器会立刻自动刷新。
在使用 webpack-dev-server 之前,我们需要先安装它作为项目依赖,在命令窗口输入以下指令:

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

我们若想使用 webpack-dev-server 包括后面要讲的各种 loader 或者 plugin , 都需要在 webpack.config.js 文件中进行配置:

devServer配置选项 功能描述
contentBase 默认webpack-dev-server会为根文件夹提供本地服务器,如果想为另外目录下的文件夹提供服务器,应该在该配置选项中重新设置所在目录
prot 设置默认监听端口,默认为"8080"
inline 设置为true时,当源文件改变时会自动刷新页面
historyApiFallback 在开发单页应用时非常有用,依赖于HTML5 history API,如果设置为true,所有的跳转将指向index.html

接下来更新 webpack.config.js 文件

//webpack.config.js

module.exports ={
        entry:__dirname + '/app/main.js',
        output:{
                path: __dirname + '/build',
                filename:'bundle.js'
        },
        //更新部分代码
        devServer: {
                contentBase:"./build",      //本地服务器加载页面所在目录
                historyApiFallback:true,    //不跳转
                inline:true                 //实时刷新
        }

}

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

//package.json

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

接下来在命令窗口输入 npm run server 启动服务器,正常情况下会自动在浏览器打开 localhost:8080 ,若没有自动打开可以在浏览器窗口中手动输入。

启动服务成功

然后我们可以在 Greeter.js 中修改一下要输出的内容,保存的同时可以观察到命令窗口中会服务器会自动刷新,同时浏览器窗口会自动刷新html内容。

一. CSS文件基本处理需求

假设项目中的CSS文件均采用预编译语言编写,那么在打包中需要处理的基本问题包括:

  • 预编译语言转换
  • 样式文件挂载方式选择
  • 代码优化(合并及压缩)
  • 去除或保留指定格式的注释
  • 资源定位路径的转换
  • 响应式布局单位转换【可选】
  • 模块化【可选】
  • 处理浏览器兼容【可选】
十一、 BannerPlugin插件

该插件的作用是给打包后的代码添加一个版权声明,是webpack自带的插件,不用再次安装,配置方法如下:
首先在配置文件中引入 webpack :

const webpack = require('webpack');

接着在 plugins 插件数组中创建该插件,代码如下:

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

然后运行打包命令,打开打包生成的文件,会在头部显示版权声明,如图:

版权声明

3.2 webpack的配置

本篇不是webpack教程,在此直接给出带有注释的webpack.config.js的配置以供参考,示例中使用SCSS作为预编译语言,其他预处理语言配置方式基本一致:

const HtmlWebpackPlugin = require('html-webpack-plugin');//用于自动生成html入口文件的插件
const MiniCssExtractPlugin = require("mini-css-extract-plugin");//将CSS代码提取为独立文件的插件
const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin");//CSS模块资源优化插件

module.exports = {
  mode:'development',
  entry:'./main.js',
  output:{
    filename:'main.bundle.js',
    path:__dirname + '/build'
  },
  module: {
    rules: [
      {
        test: /.scss$/,
        exclude: /node_modules/, //排除node_modules文件夹
        use: [{
             loader: MiniCssExtractPlugin.loader//建议生产环境采用此方式解耦CSS文件与js文件
          },{
            loader: 'css-loader',//CSS加载器
            options: {importLoaders: 2}//指定css-loader处理前最多可以经过的loader个数     
          },{
            loader: 'postcss-loader',//承载autoprefixer功能
          },{
            loader: 'sass-loader'//SCSS加载器,webpack默认使用node-sass进行编译
          }
        ]
      }
    ]
  },
  plugins:[
      new HtmlWebpackPlugin(),//生成入口html文件
      new MiniCssExtractPlugin({
        filename: "[name].css"
      })//为抽取出的独立的CSS文件设置配置参数
  ],
  optimization:{
    //对生成的CSS文件进行代码压缩 mode='production'时生效
    minimizer:[
       new OptimizeCssAssetsPlugin()
    ]
  }
}

postcss.config.js的配置较为简单:

module.exports = {
    plugins:[
        require('autoprefixer')
    ]
}

package.json中增加新的参数指定打包需要支持的浏览器类别:

  "browerslist": [
    "last 2 versions",
    "IE 8",
    "UCAndroid"
  ]

编写一段待SCSS代码:

//变量定义
$grey: #1e1e1d;
$yellow: #ffad15;
$offwhite: #f8f8f8;
$darkerwhite: darken($offwhite, 15);//SCSS函数
$baseFontSize:14px;

//循环
@for $i from 1 through 3 {
  .item-#{$i} { width: 2em * $i; }
}

//mixin
@mixin px2rem($name, $px){
  #{$name}: $px / $baseFontSize * 1rem;
}

//嵌套
.class3{
    font-weight: bold;
    display:flex;
    &-small{
        color: $offwhite;
        @include px2rem('font-size',14px);
    }
}

//autoprefixer
::placeholder{
    width:10px;
}

可以看到转换后的结果:

金沙官网线上 4

提示:代码压缩等优化功能在4.0版本中默认当mode : 'production'时有效。

二、 webpack打包js文件
  1. 我们先在 index.html 中写入代码:
// index.html

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>webpackdom</title>
  </head>
  <body>
    <div id='root'>
    </div>
  <!-- 这里引入的 bundle.js 文件现在还不存在,是将来我们用 webpack 打包出来的文件 -->
    <script src="bundle.js"></script>          
  </body>
</html>

这里引入的 bundle.js 是通过webpack打包后的可以供浏览器识别的 js 文件(现在还不存在)

  1. 接着我们在 Greeter.js 中写入一个函数,用途是在页面中打印出来一句话,然后将该函数作为一个模块导出:
// Greeter.js

module.exports = function() {
  var greet = document.createElement('div');
  greet.textContent = "Hi there and greetings!";
  return greet;
};
  1. 接下来编写我们的 main.js 文件,引入Greeter 文件导出的模块,并将该模块添加到页面的div节点中:
// main.js 

const greeter = require('./Greeter.js');
document.querySelector("#root").appendChild(greeter());
  1. 上述文件编写完毕后,我们可以在命令窗口输入webpack打包命令:
//webpack 打包命令

webpack {entry-file} {output for bundled file}
    {entry-file}                  //入口文件路径,即我们所说的主js文件main.js
    {output for bundled file}     //打包输出的js文件路径

在我们的这个demo中,需要输入的命令是:
    webpack app/main.js build/bundle.js

打包成功界面:

命令行打包

上图显示的 bundle.js 即为 main.jsGreeter.jswebpack 打包成功后生成的文件,打开 index.html ,在浏览器页面会显示如下内容

金沙官网线上,页面显示结果

至此为止,我们成功的用webpack打包了 Greeter.js 文件。

五. 图解Css-Process-Chain

从上述配置中可以看出,使用预编译器编写的样式文件需要经过一系列loaderplugin才能得到最终的目标文件,它之所以很抽象是因为中间的处理环节对开发者来说是黑箱操作,只看得到输入和输出,笔者结合自己理解绘制了下面的示意图,希望能够帮助你理解css文件在整个webpack打包流程中是如何被处理的(plugins部分尚未进行研究,处理链中暂不涉及)。

金沙官网线上 5

十三、 HMR插件

Hot Module Replacement (HMR) 也是我们常用的一个插件,它允许你在修改组件代码后,自动刷新实时预览修改后的效果。

配置方式:

  • 在配置文件中添加 HMR 插件
  • 在 devServer 中添加 hot 参数

配置完之后JS还是不能自动热加载的,还需要在JS模块中执行一个webpackk提供的API才能实现热加载。另一种方法是用Babel对React模块进行功能热加载,不过用该种方法需要对babel设置选项进行设置,代码如下:

//webpack.config.js

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');


module.exports = {
    entry: __dirname + "/app/main.js",//已多次提及的唯一入口文件
    output: {
        path: __dirname + "/build",
        filename: "bundle.js"
    },
    devtool: 'eval-source-map',
    devServer: {
        contentBase: "./public",//本地服务器所加载的页面所在的目录
        historyApiFallback: true,//不跳转
        inline: true,
        hot: true
    },
    module: {
        rules: [
            {
                test: /(.jsx|.js)$/,
                use: {
                    loader: "babel-loader"
                },
                exclude: /node_modules/
            },
            {
                test: /.css$/,
                use: [
                    {
                        loader: "style-loader"
                    }, {
                        loader: "css-loader",
                        options: {
                            modules: true
                        }
                    }, {
                        loader: "postcss-loader"
                    }
                ]
            }
        ]
    },
    plugins: [
        new webpack.BannerPlugin('版权所有,翻版必究'),
        new HtmlWebpackPlugin({
            template: __dirname + "/app/index.tmpl.html"//new 一个这个插件的实例,并传入相关的参数
        }),
        new webpack.HotModuleReplacementPlugin()//热加载插件

安装 react-transform-hmr

npm install --save-dev babel-plugin-react-transform react-transform-hmr

配置Babel:

//.babelrc

{
  "presets": ["react", "es2015"],
  "env": {
    "development": {
    "plugins": [["react-transform", {
       "transforms": [{
         "transform": "react-transform-hmr",

         "imports": ["react"],

         "locals": ["module"]
       }]
     }]]
    }
  }
}

接下来我们来修改下 package.json 文件下的 scripts 关键字下的内容:

将 "server" 改变为 "start",再更新后面的内容

如果你使用的是React,那么现在输入npm start就可以热加载模块了,每次保存都能在浏览器中看到内容。

现在大概讲述一下 webpack-dev-server 和 HMR 大概有什么区别,详情参见webpack-dev-server配置

  • webpack-dev-server 为我们提供了本地服务器,通过对其选项的一些设置,我们可以保存代码后让浏览器自动刷新,显示我们修改后的代码
  • HMR 其实是一个插件,可以配合 webpack-dev-server 使用,当对其配置完成后并运行服务器后,我们修改代码,然后保存的同时,浏览器内容并不会全部刷新,而是只更新修改部分内容,比如我们修改了一个边框样式,那么页面中其他的内容样式都不会动,只会刷新边框的样式。
    想了解更深入的童鞋可以去看上面发的链接里的内容,也可以自己在网上搜索两者区别。

webpack作为前端最火的构建工具,是前端自动化工具链最重要的部分,使用门槛较高。本系列是笔者自己的学习记录,比较基础,希望通过问题 + 解决方式的模式,以前端构建中遇到的具体需求为出发点,学习webpack工具中相应的处理办法。(本篇中的参数配置及使用方式均基于webpack4.0版本

六、 编译器

本小节我们将认识webpack的可以说最为强大的功能之一:编译器(装载器)Loaders

webpack1.x 中是loaders,在webpack2.x中将loaders换为了rules,我们在网上搜到的大多教程都是基于webpack1.x的,和现在使用的webpack2.x有些许出入,不同之处详见:Webpack2 升级指南和特性摘要

听说过webpack的童鞋们肯定都见过loaders这个单词,那么问题来了,loaders是用来做什么的?
大家对webpack可以将那些使用浏览器不能识别支持的各种语法编写的文件打包都有所耳闻,那webpack这种强大的功能是用什么实现的呢?答案就是Loaderswebpack通过各种不同的loader调用外部的脚本或工具,实现对不同类型文件的转换编译处理,例如将sass转换为css,ES6/ES7转换为ES5,React中用到的jsx转换为js等。

同上,我们也需要在配置文件webpack.config.js中的module关键字下配置loaders,即webpack2.x中的rules(后面如果再出现rules即为loaders)。rules是一个数组,里面存放着需要使用的许多loader,每个loader都是一个对象,下面是loader中常用的几个属性:

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

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

//config.json

{
  "greetText":"hello webpack!"
}

更新后的Greeter.js

//Greeter.js

//更新内容:
var config = require('./config.json');        //引入上面创建的 config.json 文件
module.exports = function(){                  //将该模块公开,外部其他文件可以调用该模块
        var greet = document.createElement('div');    //创建一个div
        greet.textContent = config.greetText;         //将 config.json 文件中greetText属性中的内容添加到创建的div中
        return greet;                                 //将该div块返回出去
}

webpack1.x 中,json 文件需要在配置文件中单独使用json-loader转换处理,而 webpack2.x 内置了可以处理json的模块,不需要再单独配置。

前言

  • WebPack 是什么?

WebPack 是什么,WebPack 可以看做是模块打包机:它做的事情是,分析你的项目结构,找到 JavaScript 模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript 等),并将其转换和打包为合适的格式供浏览器使用。

  • WebPack 可以用来做什么?

我们都知道,前端技术不断发展,已有的 htmljscss 等基础的技术已经不能满足寻(yue)求(lai)捷(yue)径(lan)的程序员群体了,于是 jsxlesssass,以及有利于模块化开发的 AMDCommonJSES2015 import 等利于完成项目的方案被一个个的提了出来并得以采用,但是这些方案并不能直接被浏览器识别支持,因此与这些方案一同提出的往往会有各种转换工具。

  当我们在项目中运用了 jsxlesssass 等工具时,往往需要使用 babel 以及其他的转换工具将其转换为 jscss 等浏览器识别的样式。当一个项目比较复杂时,这个任务量无疑是庞大的,更不要说当代码体积过大时我们还需要对代码进行压缩,优化,分割,整个项目完成下来耗费的精力和时间想一想都觉得可怕。

  于是,webpack 走进了我们的视野。它的理念是一切皆模块,将所有的文件都当做模块处理(需要注意的是,在 webpack 中,文件是模块,但模块不一定是文件)。我们在 webpack 可以使用各种各样的 loader,用以转换各种不同类型的文件,也可以使用各种各样的插件plugin,对我们体积庞大的项目代码进行优化,分割,压缩,提取等。

  • WebPack和别的打包工具如Grunt以及Gulp的区别在哪里?

Gulp / Grunt 是一种工具,能够优化前端工作流程。比如自动刷新页面、雪碧图、压缩 css、js、编译 less 等等,但是这些操作都需要在gulp的配置文件中详细设置。
  webpack 是预编译的,你在本地直接写 JS,不管是 AMD / CMD / ES6 风格的模块化,它都能认识,不需要另外再在浏览器中加载解释器,它还可以只通过一个文件入口自动寻找其依赖的所有模块,包括文件模块,编译成一个或多个浏览器认识的 JS,还可以通过插件plugin对生成的js代码进行各种优化,这些操作只需要在配置文件中设置用得到的loader或者plugin就可以了,不用告诉webpack具体怎样操作。
  两者功能有重合部分,如压缩,合并等,但是各有优势,可以结合使用

  综上:
    1. gulp/grunt 是工具链,构建工具,自动化,提高效率用。
    2. webpack 是文件打包工具,模块化识别,编译模块代码,优化代码方案用。

一、 webpack环境构建
  1. 首先全局安装 webpack:
    在终端中输入
npm install -g webpack 
  1. 我们可以使用 Webstom 编辑器新建一个空项目 webpackdemo,也可以在桌面新建一个空文件夹,名字自定,然后在里面新建一个 app 文件夹和一个 build 文件夹,在 app 文件夹中创建 main.js文件和Greeter.js文件,在 build 文件夹下创建 index.html 文件。

这里的app文件夹里面就是我们平常写的代码目录,在 webpack 中我们会选择一个主js文件main.js,如果需要其他的 js,css,img文件,可以将其作为模块引入到 main.js 中,而 build 文件夹中存放的是我们使用 webpack 将繁杂的 css 和 js 打包过后生成的文件。

  1. 因为我们是在node环境下打包文件的,因此每一个新的项目都需要一个 package.json 文件,它是一个标准的 npm 说明文件,包含了当前项目依赖的模块,自定义的脚本任务等,创建该文件的方法是进入 webpackdemo 文件夹中,然后在命令窗口输入:npm init,接着一路按回车就可以了。
  1. 我们虽然已经在全局安装了 webpack ,但为了稳妥起见,还是在当前项目中再安装一遍 webpack:
npm install --save-dev webpack

这里的 --save-dev 会将安装的该模块的名称存储在 package.json 文件中的 devDependencies 关键字下,便于我们查看已安装模块信息。

现在我们的项目结构如下图:

项目结构图

本文由金沙官网线上发布于Web前端,转载请注明出处:Webpack 学(cai)习(keng)之路

您可能还会对下面的文章感兴趣: