webpack 从入门到精通

小实验

我们一步步打包一个小项目看看 webpack 是如何工作的。

  1. 先写一个 hello.js

    function hello(messgae){
           
      alert(messgae);
    }
    
  2. 然后对其打包,发现终端报错。解决后知道在 webpack 2.0 的时候,我们打包一个 js 文件可能是这样的,比如将 hello.js 打包为 hello.bundle.js 。

    webpack hello.js hello.bundle.js
    

    但是在现在 webpack 4.5.0 的时候就需要指定 mode 和输出路径

    webpack --mode=development hello.js --output-file hello.bundle.js
    

    mode 有指定的3种值, development、production、none。区别在于 development 打包出来的东西是没压缩的、可读的,production 打包出来的是压缩的、不可读的。

  3. 然后编写一个 world.js 和 一个 style.css ,然后进行打包

    require('./world.js');
    require('./style.css');
    
    function hello(messgae){
           
      alert(messgae);
    }
    hello("hello webpack");
    

    webpack 从入门到精通_第1张图片

    通过报错信息知道, webpack 对于 css 文件并不是默认支持的,需要指定相应的 loader 对其打包。

  4. 所以我们继续安装 css-loader、style-loader。然后指定 css 文件的 loader 为 css-loader

    require('./world.js');
    require('css-loader!./style.css');
    
    function hello(messgae){
           
      alert(messgae);
    }
    
    hello("hello webpack");
    
  5. 接下来设置页面的背景颜色,发现网页并没有生效。这是因为 webpack 并不知道我们的样式如何作用到 html 中,所以我们需要指定 style-loader

    //style.css
    body,html{
           
      margin: 0;
      padding: 0;
    }
    
    body{
           
      font-size: 17px;
      background: burlywood;
    }
    
    //hello.js
    require('./world.js');
    require('style-loader!css-loader!./style.css');
    
    function hello(messgae){
           
      alert(messgae);
    }
    
    hello("hello webpack");
    
  6. 查看网页效果。发现函数确实执行了,背景颜色也生效了,我们写的 css 代码新建了一个 style标签 被直接写入到 html 中了。

    webpack 从入门到精通_第2张图片

  7. 说说2个 loader 的作用。

    • css-loader 就是 webpack 可以处理 css 文件。
    • style-loader 的作用就是将 css-loader 处理完的文件新建一个 style 标签插入到 html 中
  8. 很多人会想 require(‘style-loader!css-loader!./style.css’); 我每次写一个 css 文件,那么都需要在前面加入 style-loader、css-loader 吗?显然不是,webpack 还为我们提供了简单写法

    require('./world.js');
    // require('style-loader!css-loader!./style.css');
    require('./style.css');
    
    function hello(messgae){
           
      alert(messgae);
    }
    
    hello("hello webpack");
    

    webpack-cli 写法为

     webpack --mode=development hello.js --output-file hello.bundle.js --module-bind 'css=style-loader!css-loader'
    
  9. 之前的做法还存在一个弊端,就是每次修改了代码,我们都需要在终端重新运行打包命令,十分繁琐。这里强大的 webpack 为我们提供了一个 option,可以监听代码改变然后自动打包。如下

    webpack --mode=development hello.js --output-file hello.bundle.js --module-bind 'css=style-loader!css-loader' --watch
    

    这样,我们在源代码每次一修改,webpack 会自动打包

  10. 如果你想看到打包过程,那么可以使用 pregress 参数。这样在打包的时候可以看到左下角有构件的进度

    webpack --mode=development hello.js --output-file hello.bundle.js --module-bind 'css=style-loader!css-loader' --progress
    
  11. 如果像看到打包的模块,可以使用 –display-modules

    webpack --mode=development hello.js --output-file hello.bundle.js --module-bind 'css=style-loader!css-loader' --progress --display-modules
    

    webpack 从入门到精通_第3张图片

  12. 如果想知道打包某个模块的原因,可以使用 –display-reasons

    webpack --mode=development hello.js --output-file hello.bundle.js --module-bind 'css=style-loader!css-loader' --progress --display-modules --display-reasons
    

    webpack 从入门到精通_第4张图片

用 webpack.config.js 完成上述步骤

  1. 初始化项目,编辑 webpack.config.js

    var path = require('path');
    
    module.exports = {
           
      entry: './src/script/main.js',
      output:{
           
        path:  path.resolve(__dirname,'./dist/js'),
        filename: 'bundle.js'
      }
    }
    
  2. 有了 webpack.config.js 文件,就不需要和上面的方式一样,指定对应的 configuration option。在终端运行 **webpack --mode=development **

    webpack 从入门到精通_第5张图片

  3. 注意:如果我们将 webpack.config.js 改名为 webpack.dev.config.js ,然后在命令行打包,会发现没效果。

    webpack 从入门到精通_第6张图片

    webpack 从入门到精通_第7张图片

    要将 webpack.dev.config.js 同样生效,我们需要在命令行使用下面命令。

    webpack --mode=development --config webpack.dev.config.js
    

    webpack 从入门到精通_第8张图片

  4. 如果想像上个实验一样,看到打包时候的一些信息,怎么办呢?

    可以配合 npm 的 package.json 文件中的 scripts 标签,在下面添加 key 为 webpack 的项,然后将命令写到后边。然后在命令行运行 npm run webpack

    "scripts": {
       "test": "echo \"Error: no test specified\" && exit 1",
         "webpack": "webpack --config webpack.config.js --mode=development --progress --display-modules --display-reasons --colors"
       },
    

    webpack 从入门到精通_第9张图片

  5. 对于 webpack 的 entrt 主要有3种写法,每种写法都有不同区别。

    • 如果 webpack 只有单一入口,那么就可以是字符串。

      entry: './src/script/main.js',
      
    • 如果 webpack 有多个入口,那么就可以是数组。

      entry: ['./src/script/main.js','./src/script/a.js'],
      
    • 如果 webpack 有多个入口,那么可以用对象。

       entry: {
               
           main: './src/script/main.js',
           a : './src/script/a.js'
        },
      
  6. 如果指定了多个入口,那么执行打包会报错,因为 webpack 文档说如果多个 entry,且只有一个 output 的 filename,那么打包的结果会覆盖。所以我们需要设置如下

    When combining with the output.library option: If an array is passed only the last item is exported.

    var path = require('path');
    
    module.exports = {
           
      entry: {
           
         main: './src/script/main.js',
         a : './src/script/a.js'
      },
      output:{
           
        path:  path.resolve(__dirname,'./dist/js'),
        filename: '[name]-[hash].js'
      }
    }
    

    webpack 从入门到精通_第10张图片

    将文件修改为 filename: '[name]-[chunkhash].js’

    webpack 从入门到精通_第11张图片

    会发现 hash 和 chunkhash 的输出的文件名并不一样

    说明: chunkhash 是根据文件的内容生成的唯一标示(类似于md5生成的唯一标示、文件版本号)。如果一个资源在打包前后文本没有变过的话,二次打包的生成的 chunkhash 是一致的。

生成项目中 html 页面文件

对于生成的的 js,我们 html 如何使用呢?难道每次一打包,html 中的 script 需要修改 src 吗?不是的,webpack 提供了 html-webpack-plugin

  1. 安装

    npm install html-webpack-plugin --save-dev
    
  2. 然后运行命令,将现有的 js 打包引入到 html 文件中

    var htmlWebpackPlugin = require('html-webpack-plugin');
    //...
    plugins: [
        new htmlWebpackPlugin()
      ]
    

    然后生成的文件是 webpack 帮我们生成的 html 文件。当然我们可以新建一个自己的 html 作为模版。

     plugins: [
        new htmlWebpackPlugin({
           
          template: 'index.html'
        })
      ]
    
    //模版
    <html>
      <head>
        <titile>webpack demotitile>
      head>
      <body>
        <h2>我是 webpack 生成 html 的模版h2>
      body>
      <script type="text/javascript" src="./dist/js/bundle.js" >script>
    html>
    //打包生成的
    <html>
      <head>
        <titile>webpack demotitile>
      head>
      <body>
        <h2>我是 webpack 生成 html 的模版h2>
      <script type="text/javascript" src="main-4166ec84d15023f1cd10.js">script><script type="text/javascript" src="a-c13b6c7a7bfe7bd0293e.js">script>body>
      <script type="text/javascript" src="./dist/js/bundle.js" >script>
    html>
    

    说明:上面选中的 template 写了 index.html 就会找到合适的文件是因为 webpack 有个上下文参数 context,会根据上下文找到对应的 html(这里就是根目录)

  3. 上述的缺点是生成的 html 也会放在 dist/js 目录下。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0rhV8wwB-1585763543195)(https://github.com/FantasticLBP/knowledge-kit/raw/master/[email protected])]

    需要做到的效果就是 html 放在根目录, js 放在 dist 目录下的 js 目录下。需要对 webpack.config.js 的 output 属性做修改

  4. output:{
           
       path:  path.resolve(__dirname,'./dist'),
       filename: 'js/[name]-[chunkhash].js'
    },
    
  5. plugins 的参数很多可以自定义

    plugins: [
        new htmlWebpackPlugin({
           
          template: 'index.html',
          filename: 'index-[hash].html',
          inject: 'head'
        })
      ]
    
    • template: 指定生成 html 的模版
    • filename:指定生成 html 的命名规则
    • inject :指定生成 js 的 script 插入的位置。head、body
  6. 如果想通过 plugins 传值到 生成的 html,怎么办?

    • htmlWebpackPlugin.options 对象就可以拿到传递过来的值
    • <%= htmlWebpackPlugin.options.title %> 模版语法来拿值
    //webpack.config.js
    plugins: [
        new htmlWebpackPlugin({
           
          template: 'index.html',
          filename: 'index.html',
          inject: 'head',
          title: 'Webpack is awesome',
          date : new Date()
        })
      ]
    
    // 模版 html
    <html>
      <head>
        <titile><%= htmlWebpackPlugin.options.title %>titile>
      head>
      <body>
        <h2>我是 webpack 生成 html 的模版h2>
        <h3>时间:<%= htmlWebpackPlugin.options.date %>h3>
      body>
    html>
    
    //生成的html
    <html>
      <head>
        <titile>Webpack is awesometitile>
      <script type="text/javascript" src="js/main-82c7521f0a4a776cc00b.js">script><script type="text/javascript" src="js/a-273641522fd044fc27c7.js">script>head>
      <body>
        <h2>我是 webpack 生成 html 的模版h2>
        <h3>时间:Thu Aug 02 2018 15:40:50 GMT+0800 (CST)h3>
      body>
    html>
    
  7. 我们很好奇 html-webpack-plugin 可以传递什么参数?或者这个对象包含什么信息。做个测试就知道了

    //模版 html
    <% for (var key in htmlWebpackPlugin){  %>
    <%= key %>  
    <% } %>
    
    //生成的 html
    files    
    options
    

    看到最外层的节点就2个:files、options。那么我们分别对这2个节点遍历输出。因为遍历出的 value (**htmlWebpackPlugin.files[key] **)可能是对象、数组。所以用 JSON.Stringfy(htmlWebpackPlugin.files[key]) 打印

    <html>
      <head>
        <titile><%= htmlWebpackPlugin.options.title %>titile>
      head>
      <body>
        <h2>我是 webpack 生成 html 的模版h2>
        <h3>时间:<%= htmlWebpackPlugin.options.date %>h3>
        <% for (var key in htmlWebpackPlugin.files){  %>
        <%= key %>   <%=  JSON.stringify(htmlWebpackPlugin.files[key])  %>
        <% } %>
        <hr>
        <% for (var key in htmlWebpackPlugin.options){  %>
        <%= key %>   <%=  JSON.stringify(htmlWebpackPlugin.options[key])  %>
        <% } %>
      body>
    html>
    
    //生成的 html
            <html>
      <head>
        <titile>Webpack is awesometitile>
      <script type="text/javascript" src="js/main-82c7521f0a4a776cc00b.js">script><script type="text/javascript" src="js/a-273641522fd044fc27c7.js">script>head>
      <body>
        <h2>我是 webpack 生成 html 的模版h2>
        <h3>时间:Thu Aug 02 2018 15:51:27 GMT+0800 (CST)h3>
    
        publicPath   ""
    
        chunks   {"main":{"size":28,"entry":"js/main-82c7521f0a4a776cc00b.js","hash":"82c7521f0a4a776cc00b","css":[]},"a":{"size":18,"entry":"js/a-273641522fd044fc27c7.js","hash":"273641522fd044fc27c7","css":[]} }
    
        js   ["js/main-82c7521f0a4a776cc00b.js","js/a-273641522fd044fc27c7.js"]
    
        css   []
    
        manifest   
    
        <hr>
    
        template   "/Users/liubinpeng/Desktop/webpackdemo/Demo2/node_modules/html-webpack-plugin/lib/loader.js!/Users/liubinpeng/Desktop/webpackdemo/Demo2/index.html"
    
        templateParameters   
    
        filename   "index.html"
    
        hash   false
    
        inject   "head"
    
        compile   true
    
        favicon   false
    
        minify   false
    
        cache   true
    
        showErrors   true
    
        chunks   "all"
    
        excludeChunks   []
    
        chunksSortMode   "auto"
    
        meta   {}
    
        title   "Webpack is awesome"
    
        xhtml   false
    
        date   "2018-08-02T07:51:27.110Z"
    
      body>
    html>
    
  8. 有时候我们想把部分 js 放到 head ,部分 js 放到 body 中。单独通过 webpack.config.js 是没办法实现这个目的,结合上面的成果,我们可以拿到 htmlWebpackPlugin.files.chunks 属性,比如将 a.js 放到 head 标签,main.js 放到 body 标签。

    plugins: [
        new htmlWebpackPlugin({
           
          template: 'index.html',
          filename: 'index.html',
          inject: false,
          title: 'Webpack is awesome',
          date : new Date()
        })
      ]
    
    //模版 html
    <html>
      <head>
        <titile><%= htmlWebpackPlugin.options.title %>titile>
        <script type="text/javascript" src="<%= htmlWebpackPlugin.files.chunks.a.entry %>">script>
      head>
      <body>
        <h2>我是 webpack 生成 html 的模版h2>
        <h3>时间:<%= htmlWebpackPlugin.options.date %>h3>
        <script type="text/javascript" src="<%= htmlWebpackPlugin.files.chunks.a.entry %>">script>
      body>
    html>
    
    //生成 html
    <html>
      <head>
        <titile>Webpack is awesometitile>
        <script type="text/javascript" src="js/a-273641522fd044fc27c7.js">script>
      head>
      <body>
        <h2>我是 webpack 生成 html 的模版h2>
        <h3>时间:Thu Aug 02 2018 15:58:56 GMT+0800 (CST)h3>
        <script type="text/javascript" src="js/a-273641522fd044fc27c7.js">script>
      body>
    html>
    

    需要注意的是当自定义 js 文件的位置的时候,需要将 webpack.config.js 中 plugins 下的 inject 设置为 false

  9. 接下来看到的这种需求绝对很有料。前面我们看到的都是相对路径,但是我们的产品需要上线,所以我们的 js 文件资源路径需要改变。如下:

    //webpack.config.js
    output:{
           
        path:  path.resolve(__dirname,'./dist'),
        filename: 'js/[name]-[chunkhash].js',
        publicPath: 'http://test.lbp.com'
      },
    
    //生成的 html
    <html>
      <head>
        <titile>Webpack is awesometitile>
        <script type="text/javascript" src="http://test.lbp.com/js/a-502c14d352874172253c.js">script>
      head>
      <body>
        <h2>我是 webpack 生成 html 的模版h2>
        <h3>时间:Thu Aug 02 2018 16:25:12 GMT+0800 (CST)h3>
        <script type="text/javascript" src="http://test.lbp.com/js/a-502c14d352874172253c.js">script>
      body>
    html>
    
  10. 利用 webpack 我们还可以打包好的 html 做一些优化,比如删除注释、去掉空格.

    修改 webpack.config.js 中 plugins 节点下的 htmlWebpackPlugin 的 minify 属性

    plugins: [
        new htmlWebpackPlugin({
           
          template: 'index.html',
          filename: 'index.html',
          inject: false,
          title: 'Webpack is awesome',
          date : new Date(),
          minify:{
           
            removeComments: true,
            collapseWhitespace: true
          }
        })
      ],
    

    我们对 模版 html 写一些注释,运行 npm run webpack 后看到生成的页面中注释、空格都被去掉了。

  11. 如果想打包生成多个 html 怎么办?可能使用 plugins 下的 new htmlWebpackPlugin() 多来几组配置项

    plugins: [
       new htmlWebpackPlugin({
           
         template: 'index.html',
         filename: 'a.html',
         title: 'this is a.html',
         chunks: ['main','a'],
         inject: 'body'
       }),
       new htmlWebpackPlugin({
           
         template: 'index.html',
         filename: 'b.html',
         title: 'this is b.html',
         chunks: ['main','b'],
         inject: 'body'
       }),
       new htmlWebpackPlugin({
           
         template: 'index.html',
         filename: 'c.html',
         title: 'this is c.html',
         chunks: ['main','c'],
         inject: 'body'
       })
     ]
    

    注意:这里我们可以指定每个生成的 filename 以及 title。实现上述需求关键点在于 chunks 这个属性。用一个数组的形式来指定需要引用的 chunk。

  12. 上面只是实现了 a、b、c 3个页面,如果多了的话按照上面的写法要烦死人的。 webpack 为我们提供了 excludeChunks 这个属性,它指定了不需要包含的chunk。上面写法的另一种写法

    plugins: [
       new htmlWebpackPlugin({
           
         template: 'index.html',
         filename: 'a.html',
         title: 'this is a.html',
         // chunks: ['main','a'],
         inject: 'body',
         excludeChunks: ['b','c']
       }),
       new htmlWebpackPlugin({
           
         template: 'index.html',
         filename: 'b.html',
         title: 'this is b.html',
         //chunks: ['main','b'],
         inject: 'body',
         excludeChunks: ['a','c']
       }),
       new htmlWebpackPlugin({
           
         template: 'index.html',
         filename: 'c.html',
         title: 'this is c.html',
         // chunks: ['main','c'],
         inject: 'body',
         excludeChunks: ['a','b']
       })
     ],
    
  13. 有种需求是:当我们需要减小首页 HTTP 请求(提高首页的渲染速度),也就是将一些首页必须用到的 JS 文件用内联的方式写在首页 html 的 script 标签里面,不重要的 js 文件通过 script 的 src 引入。要怎么做呢?webpack 在设计之初没想到这种需求,很多人在 github 提了很多 issue ,官方认识到这种需求,所以在后期更新的 demo 中看到了解决方案。

    • htmlWebpackPlugin.files.chunks 对象拿到的是我们在 webpack.config.js 设置过 publicPath 生成的完整路径

    • 通过截取字符串子串的方式拿到文件地址。 **htmlWebpackPlugin.files.chunks.main.entry.substr(htmlWebpackPlugin.files.publicPath.length) **

    • 官方的解决方案

      compilation.assets[jsFile.substr(htmlWebpackPlugin.files.publicPath.length)].source()
      

    在我们的项目中加以改造,为生成的每个页面的 header 里面加入 main.js。在 body 部分加入除了 main.js 之外的其他 js。

    //模版html
    <html>
     <head>
       <title><%= htmlWebpackPlugin.options.title %>title>
       <script type="text/javascript">
      <%= compilation.assets[htmlWebpackPlugin.files.chunks.main.entry.substr(htmlWebpackPlugin.files.publicPath.length)].source() %>
    
       script>
    
     head>
     <body>
       <h2>我是 webpack 生成 html 的模版h2>
       <% for(var key in htmlWebpackPlugin.files.chunks){ %>
         <% if( key !== 'main'){ %>
           <script type="text/javascript" src="<%= htmlWebpackPlugin.files.chunks[key].entry %>">script>
         <% } %>
       <% } %>
     body>
    html>
    
    //webpack.config.js
    var htmlWebpackPlugin = require('html-webpack-plugin');
    var path = require('path');
    
    module.exports = {
           
     // entry: './src/script/main.js', 
     // entry: ['./src/script/main.js','./src/script/a.js'],
     entry: {
           
        main: './src/script/main.js',
        a : './src/script/a.js',
        b : './src/script/b.js',
        c : './src/script/c.js'
     },
     output:{
           
       path:  path.resolve(__dirname,'./dist'),
       filename: 'js/[name]-[hash].js',
       publicPath: 'http://test.lbp.com'
     },
     plugins: [
       new htmlWebpackPlugin({
           
         template: 'index.html',
         filename: 'a.html',
         title: 'this is a.html',
         inject: false,
         excludeChunks: ['b','c']
       }),
       new htmlWebpackPlugin({
           
         template: 'index.html',
         filename: 'b.html',
         title: 'this is b.html',
         inject: false,
         excludeChunks: ['a','c']
       }),
       new htmlWebpackPlugin({
           
         template: 'index.html',
         filename: 'c.html',
         title: 'this is c.html', 
         inject: false,
         excludeChunks: ['a','b']
       })
     ],
    }
    

    为了验证生效,我将 main.js 加入了 alert

    //main.js
    function helloworld(msg){
           
      alert(msg);
    }
    
    helloworld("hello webpack");
    

    看看打包后生成的 a.html

    <html>
     <head>
       <title>this is a.htmltitle>
       <script type="text/javascript">
      /******/ (function(modules) {
             // webpackBootstrap
    /******/     // The module cache
    /******/     var installedModules = {
            };
    /******/
    /******/     // The require function
    /******/     function __webpack_require__(moduleId) {
            
    /******/
    /******/         // Check if module is in cache
    /******/         if(installedModules[moduleId]) {
            
    /******/             return installedModules[moduleId].exports;
    /******/         }
    /******/         // Create a new module (and put it into the cache)
    /******/         var module = installedModules[moduleId] = {
            
    /******/             i: moduleId,
    /******/             l: false,
    /******/             exports: {
            }
    /******/         };
    /******/
    /******/         // Execute the module function
    /******/         modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    /******/
    /******/         // Flag the module as loaded
    /******/         module.l = true;
    /******/
    /******/         // Return the exports of the module
    /******/         return module.exports;
    /******/     }
    /******/
    /******/
    /******/     // expose the modules object (__webpack_modules__)
    /******/     __webpack_require__.m = modules;
    /******/
    /******/     // expose the module cache
    /******/     __webpack_require__.c = installedModules;
    /******/
    /******/     // define getter function for harmony exports
    /******/     __webpack_require__.d = function(exports, name, getter) {
            
    /******/         if(!__webpack_require__.o(exports, name)) {
            
    /******/             Object.defineProperty(exports, name, {
             enumerable: true, get: getter });
    /******/         }
    /******/     };
    /******/
    /******/     // define __esModule on exports
    /******/     __webpack_require__.r = function(exports) {
            
    /******/         if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
            
    /******/             Object.defineProperty(exports, Symbol.toStringTag, {
             value: 'Module' });
    /******/         }
    /******/         Object.defineProperty(exports, '__esModule', {
             value: true });
    /******/     };
    /******/
    /******/     // create a fake namespace object
    /******/     // mode & 1: value is a module id, require it
    /******/     // mode & 2: merge all properties of value into the ns
    /******/     // mode & 4: return value when already ns object
    /******/     // mode & 8|1: behave like require
    /******/     __webpack_require__.t = function(value, mode) {
            
    /******/         if(mode & 1) value = __webpack_require__(value);
    /******/         if(mode & 8) return value;
    /******/         if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
    /******/         var ns = Object.create(null);
    /******/         __webpack_require__.r(ns);
    /******/         Object.defineProperty(ns, 'default', {
             enumerable: true, value: value });
    /******/         if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) {
             return value[key]; }.bind(null, key));
    /******/         return ns;
    /******/     };
    /******/
    /******/     // getDefaultExport function for compatibility with non-harmony modules
    /******/     __webpack_require__.n = function(module) {
            
    /******/         var getter = module && module.__esModule ?
    /******/             function getDefault() {
             return module['default']; } :
    /******/             function getModuleExports() {
             return module; };
    /******/         __webpack_require__.d(getter, 'a', getter);
    /******/         return getter;
    /******/     };
    /******/
    /******/     // Object.prototype.hasOwnProperty.call
    /******/     __webpack_require__.o = function(object, property) {
             return Object.prototype.hasOwnProperty.call(object, property); };
    /******/
    /******/     // __webpack_public_path__
    /******/     __webpack_require__.p = "http://test.lbp.com";
    /******/
    /******/
    /******/     // Load entry module and return exports
    /******/     return __webpack_require__(__webpack_require__.s = "./src/script/main.js");
    /******/ })
    /************************************************************************/
    /******/ ({
            
    
    /***/ "./src/script/main.js":
    /*!****************************!*\
     !*** ./src/script/main.js ***!
     \****************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
            
    
    eval("function helloworld(msg){\n   alert(msg);\n}\n\n\nhelloworld(\"hello webpack\");\n\n//# sourceURL=webpack:///./src/script/main.js?");
    
    /***/ })
    
    /******/ });
    
       script>
    
     head>
     <body>
       <h2>我是 webpack 生成 html 的模版h2>
           <script type="text/javascript" src="http://test.lbp.com/js/a-4caa8a542ab497f63bf8.js">script>
     body>
    html>
    

    在浏览器调试后发现页面是可以正常弹出“hello webpack”

loader

新建项目,一步步认识 loader

  1. Js loader

    我们写的项目中会用 es6,但是并不是所有的浏览器都支持 es6(虽然各个浏览器厂商每年在不断新增对 es6 的支持),所以我们需要使用 babel 将 es6 转换为浏览器都支持的 es2015 。所以使用 babel-loader 的时候需要指定 babel 转换的模式。loader 官方给出了2种方式

    • 可以直接像 url 的 get 形式一样,将参数传递在后面。

      //方式1
      require("babel-loader?presets=latest");
      //方式2
      {
               
          test: /\.png$/,
          loader: 'url-loader?presets=latest'
      }
      
    • 写在 query 参数里面

      {
               
            test: /\.js$/,
            loader: 'babel',
            query: {
               
              presets: ['latest']
            }
          }
      
    • 其实还有一种方式:在 package.json 文件里面添加一个 key 为 babel。

      "babel": {
               
          presets: ['latest']
      }
      

    注意:webpack 现在已经是4.5.0了。以前的版本的写法是

    module: {
           
        loaders: [
          {
           
            test: /\.js$/,
            loader: 'babel-loader',
            query: {
           
              presets: ['latest']
            }
          }
        ]
      },
    

    现在的写法为

    module: {
           
        rules: [
          {
           
            test: /\.js$/,
            loader: 'babel-loader',
            options: {
           
              presets: ['latest']
            }
          }
        ]
      },
    

    注意:我们工程如果安装的依赖非常多,node_modules 文件非常多,babel 转换会很慢,这时候需要指定2个参数可以显著提高速度

    module: {
           
        rules: [
          {
           
            test: /\.js$/,
            loader: 'babel-loader',
            options: {
           
              presets: ['latest']
            },
            exclude: path.resolve(__dirname,'./node_modules/'),
            include: path.resolve(__dirname,'./src')
          }
        ]
      },
    
  2. css loader

    打包 css 经常会用到 css-loader、style-loader。我们经常写 flex 的时候很多浏览器兼容性不一致,所以我们需要加前缀,这时候需要使用 postcss-loader、autoprefixer

    官网给出2种写法

    • loader

      {
               
         test: /\.css$/,
         loader: 'style-loader!css-loader!postcss-loader'
      }
      
    • loaders

      {
               
         test: /\.css$/,
         loaders: ['style-loader','css-loader','postcss-loader']
       }
      

    如果项目中不只是使用了 css 的话,比如还使用了 less 和 sass 的话,我们需要将 css 加额外的设置

    {
           
          test: /\.css$/,
          use:[
            'style-loader',
            {
           loader: 'css-loader', options: {
           importLoaders: 1} },
            {
           
              loader: 'postcss-loader',
              options:{
           
                plugins:function(){
           
                  return [
                    require('postcss-import')(),
                    require('autoprefixer')({
           browsers:['last 5 versions']})
                  ]
                }
              }
            }
          ]
        },
        {
           
          test: /\.less$/,
          use:[
            'style-loader',
            {
           loader: 'css-loader', options: {
           importLoaders: 1} },
            {
           
              loader: 'postcss-loader',
              options:{
           
                plugins:function(){
           
                  return [
                    require('postcss-import')(),
                    require('autoprefixer')({
           browsers:['last 5 versions']})
                  ]
                }
              }
            },
            'less-loader'
          ]
        },
    
  3. 处理模版

    在 webpack 经常打包处理的时候会遇到模版。有普通的 html 模版,也会有 ejs 模式下的 tpl 模版

    • html 模版
    <html>
      <head>
        <title><%= htmlWebpackPlugin.options.title %>title>
    
      head>
      <body>
        <div id="app">
    
        div>
      body>
    html>
    
    //layer.js
    import './layer.less'
    import tpl from './layer.html'
    
    function layer(){
           
      return {
           
        name: 'layer',
        tpl: tpl
      }
    }
    
    export default layer;
    
    //app.js
    import Layer from './components/layer/layer.js';
    import './css/common.css';
    
    
    const App = function(){
           
      var dom = document.getElementById("app");
      var layer = new Layer();
      dom.innerHTML = layer.tpl;
    }
    
    new App()
    

    webpack 从入门到精通_第12张图片

    • ejs 模版

      //layer.tpl
      <div class="layer">
        <div>this is <%= name %> layr</div>
        <% for(var i=0;i < arr.length; i++){
                %>
          <%= arr[i] %>
        <% } %>
      
      </div>
      
      //layer.js
      import './layer.less'
      import tpl from './layer.tpl'
      
      function layer(){
               
        return {
               
          name: 'layer',
          tpl: tpl
        }
      }
      
      export default layer;
      
      
      //app.js
      import Layer from './components/layer/layer.js';
      import './css/common.css';
      
      
      const App = function(){
               
        var dom = document.getElementById("app");
        var layer = new Layer();
        dom.innerHTML = layer.tpl({
               
          name: 'john',
          arr: ['swift','Objective-C','JS','python']
        });
      }
      
      new App()
      

      webpack 从入门到精通_第13张图片

你可能感兴趣的:(Web,webpack,打包工具,前端工程化,构建工具)