使用入门

前言

webpack作为当下最流行的前端打包工具,如果你对它还一无所知,那就可能找不到一份好的工作?其实我们学习或者不学习不仅仅因为某个公司是否在用这个框架,更要因为使用这个框架具有什么特点,能帮我们解决什么问题,会带来其他什么问题,使用成本如何,难度如何,可拓展性如何。

使用场景

在前端生态日益完善的今天,各种针对前端工程化提出的解决方案层出不穷,这些方案一般都能大幅度的提高或者改善我们的开发过程,但是却可能不能直接用于生产环境使用,这些方案可能有:

  • js模块化,针对工具性质的、ui性质的组件模块化开发
  • es6或者typescript语言编写的程序
  • 样式预处理器比如less sass编写的样式
  • html模板引擎比如jade等

综上:这些开发阶段使用的技术产物不能直接用于生产环境部署使用,我们手动去操作这些又过于繁琐,所以提出来webpack这一个工具,可以实现将一切资源进行配置化打包的思想,来简化我们的工作。

是什么,与其他工具什么区别

  • 概念理解
    WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其转换和打包为合适的格式供浏览器使用。换句话说,它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源。
  • 图解webpack以及与其他框架的对比

入门阶段 - 图1

  • 表格说明 | 名称 | 类型 | 特点描述 | | —- | —- | —- | | grunt | 前端流程工具 | 基于配置,较繁琐 ,需要不断的修改配置文件,入手难度大 | | gulp | 前端流程工具 | 基于管道,基于stream,非常快,更加便利,可以灵活的把若干插件配置到管道任务中 | | browserify | 预编译模块化方案 | | | seajs/requirejs | 在线模块化方案 | 通过引入,在线把你的文件进行模块化,不同的只是两个的模块规范不同 | | webpack | 预编译模块化方案 | 更加智能化 , |
  • 工作流程区别

image.png

  • 与其他工具的区别(如果你外语水平有限,建议chrome打开,然后右键翻译为中文)
  • 优点

    代码拆分 Webpack 有两种组织模块依赖的方式,同步和异步。异步依赖作为分割点,形成一个新的快。在优化了依赖树后,每一个异步区块都作为一个文件被打包。 Loader Webpack 本身只能处理原生的 JavaScript 模块,但是 loader 转换器可以将各种类型的资源转换成 JavaScript 模块。这样,任何资源都可以成为 Webpack 可以处理的模块。 智能解析 Webpack 有一个智能解析器,几乎可以处理任何第三方库,无论它们的模块形式是 CommonJS、 AMD 还是普通的 JS 文件。甚至在加载依赖的时候,允许使用动态表达式 require(“./templates/“ + name + “.jade”)。 插件系统 Webpack 还有一个功能丰富的插件系统。大多数内容功能都是基于这个插件系统运行的,还可以开发和使用开源的 Webpack 插件,来满足各式各样的需求。 快速运行 Webpack 使用异步 I/O 和多级缓存提高运行效率,这使得 Webpack 能够以令人难以置信的速度快速增量编译。

能解决什么问题

webpack简单点来说就就是一个配置文件,所有的魔力都是在这一个文件中发生的。 这个配置文件主要分为三大块:

entry 入口文件 让webpack用哪个文件作为项目的入口,所有的项目主要通用依赖通过这个文件作为入口,其他跳转的页面依赖的资源在单独的组件中去加载。 output 出口 让webpack把处理完成的文件放在哪里 module 模块 要用什么不同的模块来处理各种类型的文件

如何使用

准备工作

  • 安装webpack

    1. //全局安装
    2. npm install -g webpack
    3. //安装到你的项目目录
    4. npm install --save-dev webpack
  • 项目初始化

    //填写基本的项目信息,会生成一个package.json的文件,如果有这个文件忽略
    cnpm init
    // webpack安装到包依赖之中,可以安装稳定版本的,webpack@1.12.x
    npm install --save-dev webpack
    
  • 项目结构
    建立两个文件夹,app以及public,app文件夹用来存放原始数据和我们将写的JavaScript模块,public文件夹用来存放之后供浏览器读取的文件(包括使用webpack打包生成的js文件以及一个index.html文件)。接下来我们再创建三个文件:index.html —放在public文件夹中;module.js— 放在app文件夹中;main.js— 放在app文件夹中;
    此时的项目结构如图所示:

  • 初始化一些文件内容
    1.我们在index.html文件中写入最基础的html代码,它在这里目的在于引入打包后的js文件(这里我们先把之后打包后的js文件命名为bundle.js,之后我们还会详细讲述)。

    <!DOCTYPE html>
    <html>
      <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <meta http-equiv="X-UA-Compatible" content="ie=edge">
          <title>webpack demo文件</title>
      </head>
      <body>
          <div id='app'>
          </div>
          <script src="bundle.js"></script>
      </body>
    </html>
    

  • 2.在module.js中定义一个返回包含问候信息的html元素的函数,并依据CommonJS规范导出这个函数为一个模块:语法中我们可以充分利用es6的常量,模块,模板字符串,解构等语法,非常方便。(创建文件可以用pico filename)

    // module.js
    const HELLO="Hello word!";
    var hello= function(name) {
      var greet = document.createElement('h1');
      greet.textContent =`HELLO${HELLO}`;
      return greet;
    }
    module.exports = {
      hello
    }
    

  • 3.main.js文件中我们写入下述代码,用以把module模块返回的节点插入页面。

    const {hello}=require("./module");
    document.querySelector("#app").appendChild(hello('zhangsan'));
    
  • 终端使用webpack模块
    备注:无论哪种环境中,文件路径是必备的常识,如果你不太清楚绝对路径,相对路径,根路径请参考我另外一篇技术文档,点击跳转。

    # {extry file}出填写入口文件的路径,本文中就是上述main.js的路径,
    # {destination for bundled file}处填写打包文件的存放路径
    # 填写路径的时候不用添加{},建议在项目根目录下的文件地址最前面不用加任何路径
    webpack {entry file} {destination for bundled file}
    webpack 'app/main.js' 'public/bundle.js'
    

  • 编译成功后查看页面,已经生效并打包到了对应文件中。从这个结果中可以看到,webpack只能适用于将某些资源打包为一个模块,用于一个页面中,如果你想批量操作打包或者引入文件,还是需要gulp辅助。(在打包成功的报文中,我们可以看到,Webpack 会分析入口文件,解析包含依赖关系的各个文件。这些文件(模块)都打包到 bundle.js 。Webpack 会给每个模块分配一个唯一的 id 并通过这个 id 索引和访问模块。在页面启动时,会先执行 entry.js 中的代码,其它模块会在运行 require 的时候再执行。)

image.png

  • 使用配置文件进行打包
    虽然可以使用终端工具以及对应的模块可以进行很多操作,但为了方便管理,我们还是把这部分操作放在了文件中,比如loader,plugin等。在当前练习文件夹的根目录下新建一个名为webpack.config.js的文件,我们在其中写入如下所示的简单配置代码,目前的配置主要涉及到的内容是入口文件路径和打包后文件的存放路径。

    //注:“__dirname”是node.js中的一个全局变量,它指向当前执行脚本所在的目录。
    module.exports = {
    entry:  __dirname + "/app/main.js",//已多次提及的唯一入口文件
    output: {
      path: __dirname + "/public",//打包后的文件存放的地方
      filename: "bundle.js"//打包后输出文件的文件名
    }
    }
    

  • 有了这个配置之后,再打包文件,只需在终端里运行webpack命令就可以了,这条命令会自动引用webpack.config.js文件中的配置选项,省略了若干的参数。
    进一步简化:你可以通过修改package.json文件中的scripts来把一些常用命令放到npm命令中,比如写入"start":"webpack"就可以通过npm start就实现webpack打包。

  • 拓展阅读
    scripts属性可以自定义很多你想要的任务,其中start是比较特殊的,定义之后直接运行npm start,其他的则需要通过npm run task,如果你不清楚有哪些可执行命令,可以通过npm run 得到所有的可执行命令列表:

image.png

正式使用强大功能

生成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,大型项目为了降低成本,我们可以使用cheap-module-eval-source-map。对应的配置写法:devtool: 'eval-source-map',加了这个配置之后就可以看到你开发的源文件,找到其代码,不加的话是看不到原开发代码文件,只有压缩文件显示的。 module.exports = { devtool: 'eval-source-map'}

image.png

使用webpack作为本地服务器

  • 基本使用
    浏览器监听你的代码的修改,并自动刷新显示修改后的结果,使用并配置webpack-dev-server即可。需要将它加入到项目依赖。点击查看官网配置说明
    这里只说明比较重要的几个配置,port:8080,访问端口,默认为8080,如果你同时开发两个应用,端口要修改其中一个。
// 终端安装模块
 cnpm i webpack-dev-server
//webpack中配置dev-server
  devServer: {
   contentBase: "./public",//本地服务器所加载的页面所在的目录
   historyApiFallback: true,//不跳转
   inline: true//实时刷新
 } 
//package.json中配置启动脚本
 "server":"webpack-dev-server --open"
// 启动服务器
cnpm run server
  • 可能遇到的问题:
    1.开启错误 提示地址解析错误getaddrinfo ENOTFOUND localhost。解决办法:设置host文件127.0.0.1 localhost的映射
    2.如果你不想每次启动都打开一个窗口,设置:open:false,否则设置true则默认打开
    3.希望时时更新展示 inline:true ,那么更新内容后会时时更新页面部分得到打包后的结果

使用babel转化

babel的用途:可以使用最新的js语言版本;可以支持js拓展之后的语言比如jsx。(潜在的原因是因为浏览器不支持部分es6的语法,需要通过babel把它转化为es5,如果不用原来的es6语法就会保留,旧版浏览器不支持就会出现报错)。babel具有非常多的配置选项,在单一的webpack.config.js文件中进行配置往往使得这个文件显得太复杂,因此一些开发者支持把babel的配置选项放在一个单独的名为 “.babelrc” 的配置文件中

// npm一次性安装多个依赖模块,模块之间用空格隔开
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
// 配置babel的解析器  其中 resolve是解析路径的
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
module: {
        rules: [
            {
                test: /(\.jsx|\.js)$/,
                use: {
                    loader: "babel-loader",
                },
                 include:[resolve('app'), resolve('test')],
                 exclude: /node_modules/
            }
        ]
    }

 //.babelrc
{
  "presets": ["react", "es2015"]
}
  • 注意事项
    每一项后缀名的文件都需要追加其对应的loader,否则就会提示你需要正确的loader加载它,导致打包失败。所以对于文件中有多少文件类型需要追加loader都需要详细区分。
    error:You may need an appropriate loader to handle this file type.

参考入门项目

建议

目前三大框架都有基于webpack做的较好的脚手架,如果自己不是特别想钻研或者浪费时间的话,建议直接基于脚手架开发自己的项目,如果对应的配置环境不够,自己根据prod的部分额外增加几个配置即可。

参考文档