美文网首页
webpack配置0~1 基础篇

webpack配置0~1 基础篇

作者: LeoMelody | 来源:发表于2018-09-03 16:59 被阅读0次

    扯淡的话

    • 扯淡1
      我同事问我为什么要用webpack,我自己也在思考为什么。上网也看了很多前辈的见解,也自己思考和比较后。总结下来就和我们用智能手机一样,习惯后,再去拿个普通手机怎么都是不方便的。这一个工具结合其loaders和plugins再配合上其他工具,就像是一个超级强大的智能手机。如果你还在用着老式的手机,可能这强大到你无从下手的东西让你开始有些抗拒,不过一旦你开始用它,你将会爱不释手。

    • 扯淡2
      我一直是把webpack当作一个工具,再加上vue-cli的webpack配置很完善,所以最开始我是拒绝好好学习一下的。不过看着vue-cli中把webpack配置的那么井井有条,又看了看自己的需要不断补全的webpack配置文件。我又忍不住摩拳擦掌想去尝试一下。

    开始的话

    为了方便自己知识的梳理和更加深入的理解一个有模有样的webpack配置的构成。我决定从0开始。所以有可能这篇文章还是在编写中,但会一直更新到我认为这份webpack配置已经能够面向生产使用了。

    还有就是这份webpack配置是围绕vue项目来展开。而且,本着学新不学旧的原则,我使用的是webpack4版本。我总结的东西可能只是方便我自己理解的,当然,我说到的所有配置在webpack文档中,你基本都可以找到。

    从0开始--基础部分

    第一个需求

    我期望可以在我的html中仅仅引入一个js文件而不是像很多比较老的项目一样引入长长的一排js

    先针对这个问题。简单的来实现一个由webpack构建的小应用。

    在此之前,我假设你已经知道了一些webpack的知识。首先要进行项目依赖安装。webpack4还需要安装webpack-cli。

    分析项目

    这个小型的项目对于webpack来说,只需要一个输入和输出就好了。
    项目结构:

    basewebpack.png
    • dist为打包好后的bundle.js存放目录(bundle也就是捆绑~的意思,就是把所有的模块都捆绑到这一个模块中)
    • src 存放源码的目录 index.js 为入口文件 依赖于document.js(超级简化版jq) 和 alert.js(超级简化版jq插件)。这是不是很符合以前使用jq开发的模式,现在我们利用webpack让我们只需要关注index.js即可
    • 其他文件。。。

    webpack配置代码,只有输入输出:

    const path = require('path') // 使用path是为了
    module.exports = {
      entry: path.resolve(__dirname, 'src/index.js'), // 
      output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
      }
    }
    

    运行 webpack --config webpack.conf.js在dist文件中得到bundle.js 并用index.html引用它即可

    为了方便,配置build命令: webpack--mode production --config webpack.conf.js 关于mode,后面会说到

    第二个需求

    我看着打包后的bundle.js的代码

    !function(e) {
      // 此处省略n行webpack构建的模块化代码
    }([function (e, t, n) {
      "use strict";
      n.r(t);
    // 简化版的jq
      class r {
        constructor() {}
        static getEle(e) {
          return document.querySelector(`${e}`)
        }
        static getEles(e) {
          return document.querySelectorAll(`${e}`)
        }
      }
    // index代码
      r.getEle("#btn").addEventListener("click", e => {
    // 简化版jq插件代码
        new class {
          constructor(e) {
            const t = document.createElement("div");
            t.innerHTML = `\n      <div style="position: absolute; width: 100vw; height: 100vh; background: rgba(0,0,0,.5);">\n        <div style="background: #fff; width: 10vw; height: 100px; line-height: 100px; margin: 40px auto; text-align: center;">\n          ${e}\n        </div>\n      </div>\n    `, r.getEle("body").appendChild(t)
          }
        }("哈哈哈")
      })
    }])
    

    我感到一丝的不安,这些花里胡哨的ES6代码再chrome上固然是没问题的,但我还是希望其可以转化为ES5代码,这样更为通用一些,同时,一些H5的新API(例如fetch)可以注入polyfill解决兼容性问题。这是第二个需求。

    现在,只是依赖webpack的输入输出的基本能力已经解决不了这个问题了。需要引入webpack最强大的一个工具: loader。

    我理解的webpack中的loader像一条流水线,不同的产品(代码)会交给不同的流水线(loader)处理。这里我们使用Babel和babel-loader来解决这一问题。

    babel小插曲1

    简介: babel是一个JavaScript编译器,可将ES6代码转换为ES5代码。
    简单使用: 通过配置.babelrc配置文件(JSON格式)。 关于.babelrc配置详解会在后面补充,这里暂时不需要掌握这么多
    安装babel依赖:这里需要安装三个babel的依赖:

    npm i --save-dev babel-core babel-loader babel-preset-env // babel-preset-env可以根据支持的环境来自动决定适合的Babel插件
    
    // 最简单的.babelrc配置:
    {
      "presets": [ // presets作为一个数组,用来告诉babel需要对哪些语音新特性提供支持
        "env"
      ]
    }
    
    
    修改webpack配置文件
    const path = require('path')
    module.exports = {
      entry: path.resolve(__dirname, 'src/index.js'),
      output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
      },
      module: {
        rules: [
          {
            test: /\.js$/, // 提供正则来匹配.js结尾的文件
            use: ['babel-loader'] // 匹配到这一类文件后,将其交给哪些loader去处理,这里我们先配置为最简单的模式
          }
        ]
      }
    }
    

    ok,这样在通过babel处理后的ES6代码就转换为了ES5的通用代码(转换后的代码不做展示了)

    第三个需求

    我想我要开始使用框架了,人家都用vue,我也要尝试下。

    一开始也像前面那样引入js文件一样引入vue

    import {alert} from './libs/alert.js'
    import Vue from 'vue/dist/vue.js' // 关于这里有个问题,大家可以将/dist/vue.js去掉看一下会有什么问题
    const vm = new Vue({
      el: '#app',
      data: {
    
      },
      methods: {
        showModel(str) {
          new alert(`你好${str}`)
        }
      }
    })
    

    但是这样终究没办法让开发者更好地利用vue组件化的威力。何不像vue-cli的项目那样,使用.vue文件来开发项目呢?

    vue-loader

    上面介绍过一个处理js文件的loader,那么对于.vue文件,官方也有一个对应的vue-loader来处理它

    npm i vue-loader css-loader vue-template-compiler --save-dev  // 由于vue-loader依赖于css-loader vue-template-compiler 用于将vue-loader提取的html模板编译为对应的JavaScript代码
    

    现在自己定义一个通过.vue构成的项目

    /*
      index.js 代码
    */
    import Vue from 'vue/dist/vue.esm'
    import App from './App.vue'
    
    const vm = new Vue({
      el: '#app',
      components: { App },
      template: '<App/>'
    })
    
    /*
    webpack 配置
    */
    const path = require('path')
    module.exports = {
      entry: path.resolve(__dirname, 'src/index.js'),
      output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
      },
      module: {
        rules: [
          {
            test: /\.js$/, // 提供正则来匹配.js结尾的文件
            // use: ['babel-loader'] // 匹配到这一类文件后,将其交给哪些loader去处理,这里我们先配置为最简单的模式
            loader: 'babel-loader'
          },
          {
            test: /\.vue$/, // 匹配.vue结尾的文件
            loader: 'vue-loader'
          }
        ]
      }
    }
    
    踩坑1

    这时候,出问题了。
    vue-loader was used without the corresponding plugin. Make sure to include VueLoaderPlugin in your webpack config. 在webpack4中编译打包vue文件的时候需要引入VueLoaderPlugin

    而这里,也要引入webpack的第二个利器,plugins。我理解的plugins就像是工厂中的一些处理工具,是辅助流水线(loader)来更好的处理代码的存在。

    const path = require('path')
    const VueLoaderPlugin = require('vue-loader/lib/plugin')
    
    module.exports = {
      entry: path.resolve(__dirname, 'src/index.js'),
      output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
      },
      module: {
        rules: [
          {
            test: /\.js$/, // 提供正则来匹配.js结尾的文件
            // use: ['babel-loader'] // 匹配到这一类文件后,将其交给哪些loader去处理,这里我们先配置为最简单的模式
            loader: 'babel-loader'
          },
          {
            test: /\.vue$/, // 匹配.vue结尾的文件
            loader: 'vue-loader'
          }
        ]
      },
      plugins: [
        new VueLoaderPlugin()
      ]
    }
    

    这时,运行这个webpack配置文件又出错了:


    cssloader.png

    对于.vue文件中的css代码,需要css-loader来处理。在上面代码中补上这样一段代码:

          {
            test: /\.css$/, 
            loader: 'css-loader'
          }
    

    打包后~~ 为什么样式没有生效啊。这里需要再加上一个style-loader

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

    一般来说需要引入css-loader和style-loader,其中css-loader用于解析,而style-loader则将解析后的样式嵌入js代码。

    到这,很简单的一个vue组件已经可以展示出来了。


    demo1.png

    第四个需求

    用上vue后,我迫不及待的写了几行代码,却发现每次要验证代码的效果都需要执行一次webpack的配置文件。这让我很苦恼。我希望我每次改完我的代码都可以在页面中直接体现出来。

    webpack的开发者服务器亮相了:npm i webpack-dev-server --save-dev devServer文档说明

    在写配置之前,使用devServer的一些基本东西我想了解到:

    • 1、如何启动开发服务器
    • 2、如何配置开发服务器的例如host和port等配置项
    • 3、我们启动的服务器是以哪个html文件为模板显示在浏览器中的。如何配置这个模板

    使用开发服务器来启动项目,那自然命令和之前的不同,那配置一个dev命令来表示以devServer的模式启动项目: webpack-dev-server --mode development --config webpack.conf.js

    现在可以启动服务器后,可以看到通过浏览器自动打开了一个页面:


    server1.png

    webpack提供一个devServer属性,可以在其中方便的配置devServer的各种属性,像页面中体现的localhost(host属性)和8080(port)都是可以自定义来配置的,除此之外,也可在前端使用devServer来进行代理配置等等,属性很多,功能很强大,请参考devServer文档说明,这里不一一赘述。

    最后一个疑惑🤔?如何去配置模板页面?devServer的historyApiFallback属性可配置在URL命中不同规则时返回不同的页面,当然也可以用最简单的historyApiFallback: true来规定无论什么URL,都返回index.html。我把之前的index.html清理一下作为devServer的模板页面:

    <!DOCTYPE html>
    <html lang="en">
    <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>Document</title>
      <style>
        * {
          margin: 0;
          padding: 0;
        }
      </style>
    </head>
    <body>
      <div id="app">
        <!-- <button id="btn" @click="showModel('哈哈')">请点击</button> -->
      </div>
      <!-- <script src="dist/bundle.js"></script> -->
    </body>
    </html>
    

    除了创建模板页面外,还需要html-webpack-plugin插件。因为我们的模板页面是没有引入我们编译后的文件,所以需要这个插件来根据模板html创建一个引入了编译后的js,css文件的html文件并将其展示到浏览器中。这个插件当然不仅仅是这一点作用,这里我们只需要明白它的这一用处即可。

    修改webpack配置文件:

    const path = require('path')
    const VueLoaderPlugin = require('vue-loader/lib/plugin')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
      entry: path.resolve(__dirname, 'src/index.js'),
      output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
      },
      module: {
        rules: [
          {
            test: /\.js$/, // 提供正则来匹配.js结尾的文件
            // use: ['babel-loader'] // 匹配到这一类文件后,将其交给哪些loader去处理,这里我们先配置为最简单的模式
            loader: 'babel-loader',
            exclude: '/node_modules/'
          },
          {
            test: /\.vue$/, // 匹配.vue结尾的文件
            loader: 'vue-loader',
            exclude: '/node_modules/'
          },
          {
            test: /\.css$/, 
            loader: 'style-loader!css-loader',
            exclude: '/node_modules/'
          }
        ]
      },
      plugins: [
        new VueLoaderPlugin(),
        new HtmlWebpackPlugin({
          template: 'index.html' // 告诉HtmlWebpackPlugin插件使用哪个模板
        })
      ],
      devServer: {
        historyApiFallback: true,
      }
    }
    

    执行npm run dev:

    server1.png

    修改一下代码:

    server2.png

    这将会大大提升开发效率

    基础部分完成

    到这里,基础部分的内容就完成了。现在的这个webpack配置是非常粗糙但是能完成基础的需求了。接下来我会结合Vue完成进阶部分的内容。

    第二篇地址

    最后github地址,求star

    相关文章

      网友评论

          本文标题:webpack配置0~1 基础篇

          本文链接:https://www.haomeiwen.com/subject/gvkyiftx.html