Vue-cli 中有集成Webpack,所以之前做一个Vue 的项目的时候也了解并使用了Webpack 的部分功能。实战中用到的东西永远只是理论的冰山一角,而且像我们这种习惯使用hack 方法去解决问题的程序员写起代码来顶多只能称得上是野战,哈哈哈~所以在使用过程中犹如盲人摸象,遇到问题也花费了很多时间才得以解决。后来有时间,看了一本叫做《深入浅出Webpack》的书,才对Webpack 有了一个更完整的认识,这篇文章也算是对目前掌握的关于Webpack 知识的一个梳理吧。

1. 为什么会有Webpack?

人总是对未知的事物充满了恐惧。所以尽管很早之前就听过Webpack 的大名,而且知道这是一个对前端开发者来说很重要的东西。但是不会想方设法学会它,然后拿过来用。消除恐惧,通常都要从“为什么”这个最根本的问题开始。了解了它产生的原因,自然而然就会提起兴趣去接近它、使用它。

作为一个前端开发者,理论上来说,只要熟练掌握了HTML、CSS、JavaScript 就可以高枕无忧了。但正是因为很深入地了解,才知道这种传统的开发模式有很多问题、才想用更有效的方法来做事。传统的开发方式大致来讲,主要有以下问题:

首先,单纯使用以上三种语言很难开发出一个复杂的系统。如今越来越复杂的业务需求和应用场景对代码组织提出了新的要求,于是模块化概念在前端开发中变得越来越清晰。在这之前,网页开发过程中会使用命名空间的方式来组织代码,例如JQuery 会定义一个全局变量($),它所有的代码都放在这个全局变量下。但是这种方式存在很多问题,诸如命名空间冲突、无法管理项目依赖等。针对模块化的问题,JavaScript 产生了CommonJs、AMD 等模块化规范,ES6 也提供了相应的模块化解决方案,关于JavaScript 几种模块化解决方案的对比可以查看以下链接:

关于 CommonJs、AMD、ES6

与JavaScript 类似,CSS 也衍生出几中模块化解决方案,类似Sass、Less、Sylus 等。这几种CSS 预处理器在解决模块化问题的同时也具备使编写代码更加便利、扩展性更好、更易于维护等特点。

其次,JavaScript 设计之初是用来完成一些简单的任务。在开发大型应用的时候,许多效率上的问题和语言缺陷暴露了出来,为了解决这些问题和缺陷,产生了诸如ES6、TypeScript、Flow 等新的语言。

第三,很多流行的框架像React、Vue、Angular2 等使用了JavaScript 引擎无法直接运行的语法。

综上,新的语法规范、工具和框架的出现主要解决前端开发中存在的以下几个问题:提高效率、提升性能、提高可扩展性、防止重复造轮子、提高可维护性。但是,浏览器和JavaScript 引擎的“阅历”有限,不能一一识别这些工具和语法。所以就需要有一个工具将这些所有提升工作效率的东西转换为浏览器可识别的HTML、CSS、JavaScript。这个工具就是Webpack,结合以上的问题描述,Webpack 的工作主要包含以下内容:代码转换、代码优化、代码分割、模块合并、自动刷新、单元测试、自动发布等。

2. 除了Webpack,还有啥?

既然有问题,就会有人来解决问题,而且不止一个人。因此,除了Webpack 之外也存在其他的自动化构建工具。这里做一个简单的介绍与对比。

Grunt 是一个任务执行者,它包含封装了很多常见任务的插件,也能管理任务之间的依赖关系。Grunt 的优点是灵活且具备大量可复用插件。缺点是集成度不够高,无法做到开箱即用。

Gulp 是一个基于流的自动化构建工具,采用串行的方式执行任务,也可以进行文件监听与读写。可以将Gulp 看做是Grunt 的加强版。

Fis3 是由百度开发的面向前端的工程构建工具,和gulp差不多,只不过gulp是轻量级的,需要什么操作,下载相对应的插件,而Fis3 已经集成好了很多功能。与Grunt 和Gulp 相比,Fis3 是一种更完整的解决方案,配置简单、开箱即用。

Webpack 是一个打包模块化JavaScript 的工具,通过Loader 和Plugin 转换和处理文件,最终将多个模块整合输出一个文件,Webpack 专注于构建模块化项目。Webpack 的优点是扩展性强、应用场景多、社区庞大且活跃等。缺点是只能应用于模块化开发的项目。

Rollup 是一个与Webpack 类似但是专注于ES6 的模块化打包工具。它能针对ES6 源码进行Tree Shaking,除去那些被定义但未使用的代码以减小文件大小提高性能。与Webpack 相比,Rollup 生态链不够完善,功能不够强大。

综上,Webpack 相对于其他几种工具有以下优势:适应模块化的技术潮流、有良好的生态链和维护团队、用户群体广(能从更多的地方获取教程和经验)。

3. Webpack 核心概念

前面讲到,Webpack 是一个可以将前端模板引擎、预处理语言、JavaScript 模块进行合并、压缩、打包最终成为能被JavaScript 引擎识别可以在浏览器端正常运行的文件。那它是如何做到这些事情的呢?且往下看。为了实现这些功能,Webpack 主要包含以下几个核心概念:

Entry:入口文件,Webpack 执行构建的过程将从这里开始。给定入口文件,Webpack 会分析所有依赖到的静态资源并加载相应代码进行处理,最终打包成指定的文件输出到目标文件夹中。

entry: {
  // 入口文件位置
  app: './src/main.js'
}

Output:与Entry 相对应,Output 可以配置打包完成后输出的文件位置及文件名等信息。    

output: {
  // 输出文件路径
  path: config.build.assetsRoot,
  // 输出文件名
  filename: '[name].js',
  // 文件引用路径,分为开发环境和发布环境两种情况
  publicPath: process.env.NODE_ENV === 'production'
    ? config.build.assetsPublicPath
    : config.dev.assetsPublicPath
}

Module:配置处理模块的规则。Module 通常会包含一个Loader 数组,那Loader 在这个过程中起什么作用呢?Webpack 本身只能理解JavaScript,但是却可以打包Sass、Less、png 等非 JS 资源,主要是因为在加载这些资源前都会链式调用 loader 进行预处理,将其转换成 Webpack可以理解的JavaScript 模块。

module: {
  rules: [
    // vue-loader 用于处理.vue 文件
    {
      test: /\.vue$/,
      loader: 'vue-loader',
      options: vueLoaderConfig
    },
    // babel-loader 用于将ES6 转换为ES5
    {
      test: /\.js$/,
      loader: 'babel-loader',
      include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
    },
    // 找到项目目录中的所有用到的图片文件并对其进行复制或者转换编码
    {
      test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
      loader: 'file-loader',
      options: {
        name: 'img/[name].[ext]'
      }
    },
    // 找到.styl 结尾的文件并依次调用stylus-loader、css-loader、style-loader 最终将其转换为浏览器可识别的css 文件
    {
      test: /\.styl$/,
      loader: 'style-loader!css-loader!stylus-loader',
      options: {
        limit: 10000,
        name: utils.assetsPath('css/[name].[hash:7].[ext]')
      }
    }
  ]
},

Resolve:配置寻找模块的规则。在这个选项中可以配置查找文件的扩展名和文件别名等。

resolve: {
  // 当引入文件不包含扩展名时,按以下顺序作为扩展名在项目目录中搜寻
  extensions: ['.js', '.vue', '.json', '.styl'],
  // 设置文件或文件夹别名
  alias: {
    'vue$': 'vue/dist/vue.esm.js',
    '@': resolve('src'),
    'vendor': path.resolve(__dirname, '../src/vendor')
  }
}

Plugins:配置扩展插件,用于扩展Webpack 的功能。大量的Plugins 插件几乎可以让Webpack 完成任何与构建相关的任务。

plugins: [
  // 设置生产或发布环境
  new webpack.DefinePlugin({
    'process.env': env
  }),
  // 压缩JS
  new UglifyJsPlugin({
    uglifyOptions: {
      compress: {
        warnings: false
      }
    },
    sourceMap: config.build.productionSourceMap,
    parallel: true
  }),
  // 将CSS 文件单独提取出来
  new ExtractTextPlugin({
    filename: utils.assetsPath('css/[name].[contenthash].css'),
    allChunks: true,
  }),
  // 利用html 模板生成html 文件
  new HtmlWebpackPlugin({
    filename: config.build.index,
    template: 'index.html',
    inject: true,
    minify: {
      removeComments: true,
      collapseWhitespace: true,
      removeAttributeQuotes: true
    },
    chunksSortMode: 'dependency'
  }),
  // 提取公共代码模块
  new webpack.optimize.CommonsChunkPlugin({
    name: 'vendor',
    minChunks (module) {
      // any required modules inside node_modules are extracted to vendor
      return (
        module.resource &&
        /\.js$/.test(module.resource) &&
        module.resource.indexOf(
          path.join(__dirname, '../node_modules')
        ) === 0
      )
    }
  }),
  // 复制静态资源
  new CopyWebpackPlugin([
    {
      from: path.resolve(__dirname, '../static'),
      to: config.build.assetsSubDirectory,
      ignore: ['.*']
    }
  ])
]

DevServer:在本地启用一个HTTP 服务器监听特定的端口,供开发时测试与调试使用。

devServer: {
  // 输出日志等级
  clientLogLevel: 'warning',
  // 路由跳转
  historyApiFallback: {
    rewrites: [
      // 单个字符匹配任意次
      { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
    ],
  },
  // 开启热更新
  hot: true,
  // 默认文件目录为项目根目录
  contentBase: false,
  // 是否开启gzip 压缩
  compress: true,
  host: HOST || config.dev.host,
  port: PORT || config.dev.port,
  // 编译完成后自动打开浏览器展示页面
  open: config.dev.autoOpenBrowser,
  // 编译出错的时候,在浏览器页面上显示错误
  overlay: config.dev.errorOverlay
    ? { warnings: false, errors: true }
    : false,
  publicPath: config.dev.assetsPublicPath,
  // 重定向
  proxy: config.dev.proxyTable,
  quiet: true, // necessary for FriendlyErrorsPlugin
  watchOptions: {
    // 每秒轮询次数
    poll: config.dev.poll,
  }
}

4. 有没有Demo?

说了那么多,还是不知道该怎么用,想使用各种Loader 和Plugins 的时候不知该从何写起?这里就提供一个最基本的Demo:在一个简单的项目中引入ES6、ESlint、Scss。

4.1 项目目录结构

创建一个空目录esTry,并在命令行中运行npm init,之后创建index.html 等文件,创建完成后目录结构如下:

esTry

|-- src(自己创建的内容)

     |-- js(可以使用ES6 语法)

          |-- show.js        

     |- scss(scss 样式文件)

          |- main.scss

|-- index.html

|-- main.js(可以import 很多js 和scss 文件)

|-- package.json(包依赖管理)

|-- webpack.config.js(webpack 配置文件)    

|-- .babelrc(ES6 -> ES5 配置文件)

|-- .eslintrc.js(ESlint 配置文件)

4.2 github demo

文件比较多,代码也有些杂,这里就不贴了,想看的可以去github 上下载:

A simple demo of webpack

4.3 其他demo

关于webpack,阮一峰老师给出了更多的demo,想看的可以去这里:

阮一峰webpack demo

Logo

前往低代码交流专区

更多推荐