5

webpack 从入门到放弃

随着前端项目复杂程度越来越高,依赖也越来越多,为了提高项目中代码的可复用性,前端开始提出模块化开发的思路,前端模块化会有以下几个痛点:

  1. 命名冲突
  2. 文件依赖
  3. 代码复用

模块化,会将相关的代码封装成一个package包的文件,当需要的时候,直接拿来用即可(import引入)。
至于相关文件的直接依赖如何处理,webpack会帮我们解决这个问题。

那么模块化应该使用什么样的规范呢?常见的模块化的标准主要如下:

  • commonJs

    nodejs使用的就是commonJs的规范,采用的同步方式加载依赖,一个文件就是一个模块。

    导入导出方式如下:

    导入:const aa = require('./aa')
    导出:module.exports = aa.fun

    缺点是加载的模块是同步的,只有加载完才能执行后面的操作。因为 node.js 的模块文件一般存在于本地硬盘,所以一般不会出现这个问题,但是在浏览器环境该规范就不那么适用了。

  • AMD

    因为commonJS不适用于浏览器的环境,所以出现AMD规范。特点: 异步加载/推崇依赖前置、提前执行 。require.js最佳实践

    使用方式:

    定义模块: define()
    加载模块:require()
    引用:require.config()
  • CMD

    CMD的使用方式如上,但是特点不太一样。CMD是异步加载/依赖就近、延迟执行 sea.js最佳实践

  • ES6

    ES6 直接在语言层面上实现了模块化。我们现在用的最多的就是 ES6 模块化的实践方式。

    导入导出方式如下:

    导入:
    import aa from 'aa'
    import {A} from 'aa'
    
    导出:
    export default {A, B}
    export {A, B}
    export funtion aa() {}

    虽然ES6是模块化的最终方案,但是还是在许多浏览器上有兼容问题,需要对代码进行转码处理才行。

  • 样式文件中的模块化

    前端开发中,样式文件也开始支持模块化,以stylus为例,通常将一些通用的文件放到文件中,在使用相关的样式时,直接@import引入文件,就能使用对应的代码片段,当然也能定义一些全局的参数(颜色,圆角类)进行使用。

    // common.styl
    .center-transform
      top 50%
      left 50%
      transform translate(-50% -50%)
    center-transform()
      top 50%
      left 50%
      transform translate(-50% -50%)
    
    // 使用
    @import 'common.styl'
    .box {
      center-transform
    }
    

webpack和Gulp/Grunt、Rollup比较

  • Grunt: 是一套前端自动化工具,帮助处理反复重复的任务。一般用于:编译,压缩,合并文件,简单语法检查等
  • Gulp: 是基于“流”的自动化构建工具,采用代码优于配置的策略,更易于学习和使用
  • Rollup: Rollup 是一个和 Webpack 很类似但专注于 ES6 的模块打包工具。 Rollup 的亮点在于能针对 ES6 源码进行 Tree Shaking 以去除那些已被定义但没被使用的代码,以及 Scope Hoisting 以减小输出文件大小提升运行性能。 但是Rollup 生态链还不完善,体验不如 Webpack。
  • webpack: Webpack 是模块化管理工具和打包工具。通过 loader 的转换,任何形式的资源都可以视作模块,比如 CommonJs 模块、AMD 模块、ES6 模块、CSS、图片等。它可以将许多松散的模块按照依赖和规则打包成符合生产环境部署的前端资源。还可以将按需加载的模块进行代码分隔,等到实际需要的时候再异步加载

开始使用webpack

首先创建一下的目录结构:

.
├── src
│   ├── assemble
│   │   ├── css
│   │   │   ├── index.css
│   │   ├── index.html
│   │   └── js
│   │       ├── index.js
│   │       └── test.js
│   ├── babyTest
│   │   ├── css
│   │   │   └── index.css
│   │   ├── index.html
│   │   └── js
│   │       └── index.js


文件内容:
// 1. assemble-index.html: 
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title></title>
</head>
<body>
</body>
</html>

// 2. assemble-css/index.css:
body {background: red} 

// 3. assemble-js/index.js:
import '../css/index.css'
console.log('index.js')


// 4. assemble-js/test.js:
console.log('test.js')

babyTest目录和上面类似
  1. 创建package.json文件: npm init
  2. 安装webpack(全局、目录安装):npm install webpack -D
  3. 创建webpack.config.js文件

    下面直接配置的是多文件入口:
    // 引入插件
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    // 相关配置
    module.exports = {
     mode: 'development', // 指定webpack的模式  
     entry: {
       'assemble/js/index': './src/assemble/js/index.js',
       'assemble/js/test': './src/assemble/js/test.js',
       'babyTest/js/index': './src/babyTest/js/index.js'
     },
     output: {
       filename: '[name].js',  // 打包后输出文件的文件名
       path: path.resolve('./output/market') // 打包后的文件存放的地方
     },
     module: {
       rules: [
         {
           test: /\.css$/,
           use: ['style-loader', 'css-loader']
         }
       ]
     },
     plugins: [
       new HtmlWebpackPlugin({
         template: './src/assemble/index.html',
         filename: 'assemble/index.html',
         chunks: ['assemble/js/index']
       }),
       new HtmlWebpackPlugin({
         template: './src/babyTest/index.html',
         filename: 'babyTest/index.html',
         hash: true,
         chunks: ['babyTest/js/index']
       })
     ]
    }

    上面写的实例中,将loader和plugins已经使用了,可以先按照上面的使用,后边将进行详细方法的介绍。

    使用前,安装对应的依赖包

    npm install css-loader style-loader html-webpack-plugin -D
    备注:

    mode,参考 模式

    多文件入口配置,参考 多页面应用程序

  4. 终端执行打包命令: webpack

可以看到在根目录下有output目录,里面就行对应的打包文件。直接运行output文件下的目录,能够看到对应效果,css和js都能够执行成功。

备注:
执行打包命令的方式有以下几种方式

  1. node_modules/.bin/webpack
  2. webpack
  3. 在package.json文件中配置对应的命令 "scripts": { "start": "webpack"}

entry

入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

每个依赖项随即被处理,最后输出到称之为 bundles 的文件中,,entry属性 来指定一个入口起点(或多个入口起点)。默认值为 ./src。

output

output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。

Loader

loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!

上面的配置中就用到了 css-loaderstyle-loader,主要是在对引用的css文件会用style-loadercss-loader进行处理,当配置多个loader的时候,按照从左至右的方式进行处理。

Loaders的配置主要包含以下配置:

  1. test(必填): 用正则匹配对应文件的扩展名
  2. use(必填):loader的名称
  3. include/exclude(非必填):手动添加必须处理或者是忽略的文件
  4. query(非必填):为loaders提供额外的设置选项

在webpack中使用的方式

  1. 命令行调用:webpack --module-bind jade --module-bind 'css=style!css'
  2. 通过require:require('style-loader!css-loader?minimize!./main.css')
  3. 使用配置文件: 如上配置

联系紧密的Babel

Babel是一个编译JavaScript的平台,可以帮我们将编译的代码

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

Babel的安装与使用

  1. Babel的安装

Babel其实是几个模块化的包,其核心功能称为babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包(用的最多的就是解析ES6语法的包 babel-preset-env)
npm install --save-dev babel-core babel-loader babel-preset-env

在webpack.config.js中,module的rules中添加;

{
  test: /\.js$/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: [
        'env', 'react'
      ]
    }
  },
  include: /src/,
  exclude: /node_modules/
}
  1. Babel的配置

Babel的配置可以直接在webpack.config.js中进行配置,但是有时配置项过多,但在这个文件中配置过多js编译规则的代码,显得可读性不太好,因此支持在.babelrc中单独配置,webpack会自动调用这个文件中的配置项。

// .babelrc文件
{
  "presets": ["react", "env"]
}

loader的特性(引用官网)

  • loader 支持链式传递。能够对资源使用流水线(pipeline)。一组链式的 loader 将按照相反的顺序执行。loader 链中的第一个 loader 返回值给下一个 loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。
  • loader 可以是同步的,也可以是异步的。
  • loader 运行在 Node.js 中,并且能够执行任何可能的操作。
  • loader 接收查询参数。用于对 loader 传递配置。
  • loader 也能够使用 options 对象进行配置。
  • 除了使用 package.json 常见的 main 属性,还可以将普通的 npm 模块导出为 loader,做法是在 package.json 里定义一个 loader 字段。
  • 插件(plugin)可以为 loader 带来更多特性。
  • loader 能够产生额外的任意文件。

扩展-常使用的loader清单

loader name 介绍
css-loader 解析@import 和 Url()引用的css文件,转化成 import/require() 的方式然后在解析他们
style-loader 将 CSS 放在 <style></style> 标签中注入到 DOM 中
sass-loader 将 SASS/SCSS 转换为 css
less-loader 将 less 转化为 css
html-loader 将 html 输出为字符串,也可以根据配置进行压缩
babel-loader 加载 ES2015+ 代码,然后使用 Babel 转译为 ES5
url-loader 将文件加载为 Base64 编码的 URL
file-loader 可以解析项目中的url引入(不仅限于css),根据我们的配置

...更多请查看上面的链接

plugin

插件是 webpack 的支柱功能。webpack 自身也是构建于,你在 webpack 配置中用到的相同的插件系统之上!

插件目的在于解决 loader 无法实现的其他事。

Plugins在构建的过程中,通过在构建流程中注入对应的钩子,给webpack带来很大的灵活性。

Loaders和Plugins常常被弄混,但是他们其实是完全不同的东西,可以这么来说,loaders是在打包构建过程中用来处理源文件的(JSX,Scss,Less..),一次处理一个,Plugins并不直接操作单个文件,它直接对整个构建过程其作用

上面的实例中就使用了html-webpack-plugin插件,主要的功能就是自动引用使用的js文件,使用css的话,也会根据一定规则进行动态插入,在控制台查看文件代码时,可以看到引用的css文件,会自动插在<head></head>之间。如截图所示:
图片描述

我们可以根据自己的项目构建方式,使用对应的插件,就不一一举例说明了。

扩展-常用的Plugins清单

Plugins name 介绍
HtmlWebpackPlugin 简单创建 HTML 文件,用于服务器访问
clean-webpack-plugin 删除指定的文件夹
webpack-bundle-analyzer webpack打包体积查看,便于优化文件体积
speed-measure-webpack-plugin 查看测量出在你的构建过程中,每一个 Loader 和 Plugin 的执行时长(优化webpack时能帮你看出对应的时间)

...更多优秀插件请查看清单链接

启动静态服务器

启动一个静态服务器,默认会自动刷新,就是说你对html,css,js文件做了修改并保存后,浏览器会默认刷新一次展现修改后的效果

正常情况下我们都是在开发环境中开发项目,所以之前配置的脚本"dev"可以派上用场了,在执行npm run dev命令后,会启动静态服务器,我们访问localhost:3000端口就可以看到开发的页面内容了

对应的配置文件:

// webpack.config.js的配置文件
devServer: {
  host: 'localhost',  // 默认是localhost
  port: '3000',   // 端口
  open: true,    // 自动打开
  hot: true,    // 开启热更新
  openPage: 'assemble'  // 默认打开的文件
}

// package.json中的配置
"scripts": {
  "dev": "webpack-dev-server --open"
}

思考:热更新和自动更新的区别??(待补充...)

开发、测试、上线构建的细微区别

通常本地开发,测试和线上的构建方式略微有点区别,线上文件都会将文件中的空行,空格,换行清除,并且压缩html,css,js和对应image的体积,这样就能保证体积最小,加快页面的加载速度。

但是开发和测试环境通常,在浏览器中,无法对代码逐行打断点进行调试,所有需要使用source maps进行调试,它使得我们在浏览器中可以看到源代码,进而逐行打断点调试。

使用source maps方法

在webpack.config.js文件中,添加devtool属性,赋值为 source-map或者inline-source-map即可,后者报错信息更加具体,会指示源代码中的具体错误位置,而source-map选项无法指示到源代码中的具体位置。

webpack优化

1. 代码编写过程中遇到的现象

问题描述:更改一个文件,导致所有的文件都会重新进行编译。

  • 修改前:
    图片描述
  • 修改后:
    图片描述

优化方案:使用缓存

  1. 安装cache-loader模块 npm install cache-loader -D
  2. 在module.rules对应的loader最前面添加cache-loader

参考文章

浅入浅出webpack

webpack 从入门到放弃(一)

入门 Webpack,看这篇就够了


李银凤
101 声望1 粉丝