近况

最近主要学习vue,在网课中使用到了webpack,在实操过程中深受webpack配置的折磨,太痛苦了
想着,如果不记录下来,写一篇webpack博客,对不起我吐血找bug。

本篇主要讲如下内容:

模块化

在讲webpack之前,我想先说说什么是模块化,因为webpack是一个前端模块化打包工具,如果不知道什么是
模块化,那就不要说什么webpack了。ok,下面进入正题

模块化指的是将一个js文件看成一个模块,实际上,不止是js文件,你可以把css样式,图片都看成是一个个模块
通过组织这些模块从而构建我们的项目。那我们来看看怎么组织这些模块(js,css,图片等等)。

组织模块说的是:在js文件中,使用一些别人提供的js语法,使得我们可以使用其他模块,做到模块间的数据共享
请看下面代码:

//创建hello.js,main.js,index.html,代码如下:
//hello.js
'use strict';
let say = function (name) {
  console.log(`hello `+name);
}
export {say};

//main.js
import {say} from './hello.js';
say('万一');

//index.html
<body>
    <script src="main.js" type="module"></script>
    这里是default.html
</body>

/*
	以上代码,当我们在浏览器打开index.html页面的时候,按f12,控制台会输出“hello 万一”
	上面的代码其实讲的就是模块之间的组织,下面来分析一下上面的代码
	
	在上面的代码中,hello.js文件(模块)中有一个say函数,然后使用export {say},将该函数暴露出去了
	以后谁导入该hello模块,谁就可以使用这个say函数,可以看到,在main.js文件(main模块)中,
	使用import {say} from './hello.js';导入了hello模块,拿到的say变量,其实就是say函数
	然后直接就可以使用,最后在index.html页面中,引入main.js,并且type="module",当我们打开
	index.html后,就可以看到控制台输出hello 万一。
	
	以上这种export,import语法是es6语法,浏览器提供支持的,是模块之间相互联系的语法。
	当然还有其他语法也是类似的,例如node.js中使用的CommonJS语法和ES6也是类似的。
*/

模块化不仅可以实现模块之间数据的共享,还可以实现模块之间变量作用域隔离,什么意思呢?
在以前,我们在一个页面引用两个js文件,这两个js文件之间的变量是不允许有重名的,
因为这些变量属于全局变量。而在模块中可以定义同名的变量,相互不影响,因为模块中的代码都被一个函数
包裹住(立即执行函数),所以在模块中定义的变量都是局部变量,自然不影响其他模块。

模块化之ES6语法及CommonJS语法

  • ES6模块化
    上面的例子中是一个简单的ES6模块化语法,下面是一些细节

    • export导出

      //第1种导出方式就和上面案例一样,导出不仅可以导出变量,还可以导出函数,对象,类
      let name='万一';
      let you="明月";
      function say() {
          console.log(`万一爱明月`);
      }
      export {say,name,you};
      
      //第2种导出方式,直接在export后定义变量
      export let name = '万一'
      
      //第3种导出方式,默认导出
      let you="明月";
      export default you;
      /*
      	注意:默认导出这种方式可以在导入时自定义变量名,其他方式导入时只能使用导出时的变量名
      	并且一个模块只能有一个默认导出
      */
    • import导入

      /*
      	第1种导入方式,和上面案例一样,注意需要有{}包裹变量名,对应导出的第1,2种方式
      	注意,这里的路径是相对路径,变量名不可随意取,导出时什么名字,导入时就是什么名字
      */
      import {say,xxx,xxx,xxx} from './a.js';
      
      /*
      	第2种导入方式,通配符导入,当需要导入的功能太多的时候,
      	我们可以使用*表示导入该模块全部的导出的内容,并可以使用as取一个别名
      	该命令表示将a.js中导出的内容,全部导入到本模块中,并存到xxx对象之中,当需要某个功能时,
          直接xxx.变量名,即可取出a.js中导出的变量名。
      */
      import * as xxx from './a.js'
      
      /*
      	第3种导入方式,默认导入,和默认导出一起使用的。与其他导入方式不同的是,
      	这里导入的变量并不需要大括号{}包裹,并且变量名随意取
      */
      import yang from './a.js';
  • CommonJS
    CommonJS的导入导出需要node环境的支持,下面是导入导出语法

    导出:
    module.exports= {
        flag:true,
        test(a,b){
            return a+b;
        },
        demo(a,b){
            return a*b;
        }
    }
    
    导入:
    //解构赋值
    let {test,demo,flag} = require('./moduleA');
    //等同于
    let mA = require('moduleA');
    let test = mA.test;
    let demo = mA.demo;
    let flag = mA.flag;
    /*
    	注意:与ES6不同的是,导入模块时,模块的后缀名可以省略,
    	而在es6中不可以,require('./moduleA');
    */

webpack概念

webpack是一个前端模块化打包工具,模块化我们知道了,打包又是个什么东西呢?
我们可以想象,在一个页面之中导入非常多的js文件,并且js文件之间又有逻辑关系,不能随意导入
而打包是将我们编写的js文件都打包成一个js文件,然后在页面只需要引用这一个js文件就行。
webpack还可以对图片进行压缩,将scss,less转成javascript,将es6语法转成es5语法,
将typeScript转成javascript等等很多的功能,所以它就是一个前端开发工具。

安装webpack

  • node和npm的概念
    webpack的安装需要node环境,需要npm进行安装,node.js并不是一门开发语言,它定义了一套规则,
    语法使得js可以运行在后端服务器上,所以可以做之前不能做的事情,例如可以使用js操作文件,数据库,网络等
    这是node赋予js的能力,而npm,则是node包管理器,node package manage,帮助我们下载各种依赖包

  • npm的一些小概念和命令
    在官网下载好node之后,配置好环境变量(自动配置),npm是随着node一同下载的
    **npm安装依赖分为全局安装和局部安装(本地安装)**指的是把依赖放在哪里,或者说依赖的作用范围。

    • 全局安装指的是将依赖包安装在node根目录的node_globalnode_module中,这个路径可以设置
      使用命令:npm config ls查看npm配置信息,查看npm全局安装的路径:npm get prefix
      当我们使用npm命令安装依赖时加上-g,就为全局安装,否则为局部安装。
    • 而局部安装指的是,当我们对一个文件夹进行初始化后,该文件夹就被npm管理起来了,被当做一个项目
      对文件夹初始化的命令是:npm init,执行命令之后会在该文件夹中生成一个node_module文件
      和一个package.json文件,新生成的node_module文件夹存放局部安装的依赖包,
      package.json文件则是该项目的一些依赖信息等等。

    依赖又分为开发时依赖(又叫编译期依赖),运行时依赖

    • 开发时依赖:只需要在开发时使用的依赖,例如图片压缩,es6转es5等等,项目打包后,
      运行时并不需要这些依赖,安装开发时依赖在npm命令上加上–save-dev就可以把依赖信息
      写到package.json中的devDependencies中,表示这是开发时依赖
    • 运行时依赖:在程序运行时需要的依赖,例如vue等,安装运行时依赖在npm命令加上–save
      就可以把依赖信息写到package.json中的dependencies中,表示这是运行时依赖。

    npm安装依赖的两种方式

    • 通过编辑项目中的package.json文件,编辑dependencies,devDependencies,
      再执行npm install命令。就会使得所依赖的module加载到项目的node_modules文件夹中。
    • 不用package.json,而是直接通过npm install moduleName的方式,逐个添加依赖。

    npm的一些常用命令

    • 安装依赖:npm install 依赖包名@版本号,加上-g则表示全局安装,
      加上--save-dev表示这是开发时依赖,会把依赖信息写到项目中的package.json的devDependencies中
      如果不指定版本号,则安装最新版的依赖
    • 卸载依赖:npm uninstall 依赖包名@版本号 依赖包名@版本号,可以安装卸载多个依赖
    • 查看npm管理的依赖:在项目根目录中执行npm list,查看的是项目的局部安装依赖(或者叫本地安装)
      查看全局安装的依赖:npm list -g --depth 0,–depth 0:只显示第一层,不展开依赖项
    • 重新安装项目依赖:在项目根目录中使用npm install,则会根据项目的package.json
      重新安装所有依赖,在修改package.json文件后,都需要执行一下该命令。
    • 查看全局安装存储的路径:npm get prefix
      查看npm配置信息:npm config ls
  • 安装webpack和其脚手架
    webpack当然也是npm中的软件包,npm几乎管理着大多数的依赖,工具等等,安装命令如下:
    npm install webpack@5.38.0 webpack-cli@4.7.0 --save-dev,直接局部安装,作为开发时依赖。

webpack的基础使用

上面已经介绍了什么是webpack,以及如何安装webpack,接下来我们来看下如何使用webpack

  • 首先使用webStorm或者其他开发工具创建一个空项目,再创建两个目录,一个dist目录
    一个src目录,dist目录存放的是打包后的文件,src目录存放项目的文件,包括js,css,图片
    上面说过,webpack可以将所有js,css等等打包成一个js文件,然后html页面引入这个文件即可使用
    说的就是将src中所有内容都打包进dist目录中,当然,图片不被打包成js文件而是拷贝到dist目录中。

    另外再创建一个index.html页面,index.html页面不放在src中,而是放在项目的根目录中
    因为我们只需要把js,css等等模块打包成一个js文件,然后html引入即可使用。

    值得说明的是,我们需要改变一个思想,一个使用vue开发的项目一般只有一个html页面,
    这被叫做单页面富应用,即SPA页面,利用JavaScript根据后端数据动态的变换 HTML,
    从而实现UI与用户的交互。下图是传统web和单页面应用区别:

  • 在上面,我们在项目根目录中创建好了dist目录,src目录,index.html,我们还是来实现讲模块化时候的例子
    在main.js文件中引入其它js文件的函数,然后在main函数中调用该函数,之前是将该main.js引入html页面
    然后打开浏览器查看控制台输出,而现在是使用webpack对main.js进行打包成dist目录的js文件,
    再在html引入打包后的js文件,虽然相比之前多了一个打包的动作,但是main.js作为入口文件,
    其他js文件只要在main.js中被导入了,都可以打包成一个js文件,而不像以前将很多js文件引入到html中。
    具体做法:在src目录中创建js目录,在js目录中创建将被导入的mathUtil.js,在src根目录中创建main.js

    //mathUtil.js
    function add(a, b) {
      return a+b;
    }
    function cheng(a, b) {
      return a*b;
    }
    export {add,cheng};
    
    //main.js,作为入口文件,导入mathUtil组件中函数并调用
    import {add,cheng} from './js/mathUtil.js';
    console.log(add(3, 2));
    console.log(cheng(3, 2));
  • 请注意,目前为止还没有使用webpack,首先,进入该项目根目录,npm初始化该项目,
    会在项目根路径下生成node_modules目录和package.json文件,当我们局部安装(本地安装)依赖后,
    npm就会将依赖下载到node_modules中,package.json则是npm管理我们项目的文件,
    其中有一些项目的依赖的信息,以及可以配置一些script脚本,简化依赖的使用。
    初始化完毕后,安装webpack依赖。

      //进入项目根目录,初始化项目
      npm init
    /*
      	安装webpack及其脚手架依赖,没有-g表示本地安装,@指定依赖版本号
    	--sace-dev则会将依赖信息写到项目的package.json中
      */
      npm install webpack@5.38.0 webpack-cli@4.8.0 --save-dev
  • 然后我们还需要在项目根目录中创建一个webpack的配置文件,
    webpack.config.js,在这个配置文件中可以配置一些webpack的扩展功能,行为。
    我们需要在webpack配置文件中配置出口入口路径,入口路径指的是入口文件的位置,
    就是我们的main.js的路径,出口路径指的是,打包后的js文件放在哪个目录,
    webpack会根据出口入口路径,找到main.js,任何在该文件被导入的模块,都会被打包
    打包成我们指定的js文件,然后我们在html页面中引入该文件即可,下面是我的webpack.config.js配置信息

    //因为全局安装中存在vue,所以可以直接导入vue的path模块
    const path = require('path');
    
    //CommonJs语法导出
    module.exports={
      entry: './src/main.js', //打包入口,相对于webpack.config.js,main.js所在的位置
      output:{
        /*
          path是出口路径,指要把打包后的文件放在哪里,__dirname是path模块的变量,表示当前文件的绝对路径
          该变量表示当前webpack.config.js的绝对路径,resolve函数表示将两个路径进行自动拼接
          所以整体意思就是打包后文件放在webpack.config.js同级的dist目录中
         */
        path:path.resolve(__dirname,'./dist'),
        filename: 'bundle.js',    //打包后的文件名
      }
    }
  • 配置完毕后,在根目录下执行命令:webpack --mode development进行打包操作,
    --mode development,该参数是webpack新版本需要指定的,表示使用开发模式进行打包,
    开发模式打包,打包后的文件不会被压缩,如果是production,生产模式打包,则打包后的文件会被压缩
    打包后会在dist目录下生成bundle.js文件,然后在我们的index.html页面中引入该js文件即可

    下图是我的目录结构,总体思路很简单,webpack帮我们将入口文件main.js,打包成dist中的bundle.js文件
    我们通过npm命令即可打包,只要模块在main.js中使用到,就可以被打包成bundle.js,往后只需要在html
    中引入打包后的js文件,而不需要在html页面中多个js文件。
    下图引用bundle.js时,不需要增加type="module"

  • 案例并没有结束,我们知道,配置完webpack的配置文件,webpack.config.js文件,
    使用命令webpack --mode development,即可让webpack进行打包,但是这种打包命令并不好
    因为后面我们的webpack命令会加很多参数,这就使得打包的命令变得很长,不方便,
    所以我们可以在package.json中配置一个script,小脚本的意思,配置好后,使用npm运行小脚本
    即可执行相应的webpack命令。下面是我的package.json配置

    {
      "name": "studywebpack",
      "version": "1.0.0",
      "description": "study for webpack",
      "main": "main.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "webpack --mode development",
        "prod": "webpack --mode production"
      },
      "author": "",
      "license": "ISC",
      "dependencies": {
        "webpack": "^5.38.0",
        "webpack-cli": "^4.8.0"
      }
    }

    可以看到scripts属性中,配置了dev和prod,dev表示开发模式打包,prod表示生产模式打包,
    dev,prod这些属性名随便写,当我们在根目录下执行npm run dev时,就会执行dev后面的命令.

  • 上面我们学习了webpack的基本使用,我们知道,webpack就是将多个JS文件,打包成一个JS文件,
    在webpack处理的模块中(JS文件),可以随意使用各种模块化语法进行导入导出,因为webpack会处理解析JS
    的模块化导入导出,但是webpack.config.js中的导出只能使用CommonJS语法,因为webpack依赖node环境

loader的使用

webpack可以做的是:打包和处理JS模块化
随便你在项目中用什么模块化语法,webpack帮你解析,不需要考虑环境问题,但是webpack也有很多不能做的
webpack只能处理JS代码,并不能处理css,图片,loader就是一些别人开发的依赖,可以帮助webpack处理这些
例如将less转成css,将es6语法转成es5,解析图片等等。

loader的使用过程:通过npm安装需要使用的loader,再在webpack.config.js中的module关键字下进行相应的配置

好,那么我们开始配置几个常见的loader,webpack地狱配置开启!

  • 配置css的loader
    配置css的loader指的就是在webpack管理的项目中使用css文件

    • 创建一个normal.css文件,body{background-color: aqua;}

    • 去官网的loaders中寻找css的loader,使用npm安装这两个loader,css-loader,style-loader

      //我的webpack版本是5.38.0,webpack-cli版本是4.8.0,最好保持一致吧
      npm install --save-dev css-loader@6.2.0 style-loader@3.2.1
    • 安装好两个loader之后,配置webpack.config.js

      const path = require('path');
      
      module.exports={
        entry: './src/main.js', 
        output:{
          path:path.resolve(__dirname,'./dist'),
          filename: 'bundle.js',    
        },
        module: {
          rules: [
            {
              test: /\.css$/,
              use: [ 'style-loader', 'css-loader' ]
            }
          ]
        }
      }
      //注意:use数组中,style-loader和css-loader顺序不能反,webpack加载配置从数组右到左加载
    • 配置完毕后,此时webpack能处理css文件了,但是我们需要在入口文件中引用css模块,
      只有在入口文件中导入模块,才能被webpack进行打包处理,所以我的main.js如下:

      //测试模块化
      import {add,cheng} from './js/mathUtil.js';
      console.log(add(3, 2));
      console.log(cheng(3, 2));
      
      //测试css文件
      import css from './css/normal.css'
    • 最后执行打包命令,npm run dev,查看index.html即可查看到页面背景颜色产生变化了

  • 配置less的loader
    less文件是css的预加载文件,配置less的loader的意思是,让webpack认识less文件,并使用相应的loader
    将less文件转成css文件,具体步骤与配置css一样,与less相同的还有scss文件。

    • 在css目录中创建special.less文件,该文件内容如下:

      @fontSize: 50px;
      @fontColor: red;
      
      body{
        font-size: @fontSize;
        color: @fontColor;
      }
    • 然后安装less的loader,使用less-loader还需要less依赖,一起进行安装

      npm install --save-dev less-loader@7.3.0 less@4.1.1
    • 配置webpack.config.js文件

      module: {
          rules: [
              {
                  test: /\.css$/,
                  use: [ 'style-loader', 'css-loader' ]
              },{
                  test: /\.less$/,
                  use: [{
                      loader: "style-loader" // creates style nodes from JS strings
                  }, {
                      loader: "css-loader" // translates CSS into CommonJS
                  }, {
                      loader: "less-loader" // compiles Less to CSS
                  }]
              }
          ]
      }
    • 配置完毕后,需要在入口文件中引入special模块,因为样式是字体样式,所以使用JS输出一个标签

      //测试less
      import less from './css/special.less';
      document.writeln("<h3>我giao</h3>")
    • 最后打包运行:npm run dev,查看页面即可

  • 配置图片的loader配置图片的loader,就是让webpack能够识别图片模块,对图片进行处理。
    webpack处理图片有两种方式,可以在webpack.config.js中配置一个图片大小(limit属性),
    超过这个大小的图片会被拷贝进dist目录,然后浏览器会通过url请求方式得到这张图片,
    而小于这个大小的图片,则会解析成base64字符串发给浏览器,让浏览器自行解析。

    • 第一步,先在src中创建img目录,放上两张图片,然后npm安装图片相关的依赖

      npm install --save-dev url-loader@4.1.1 file-loader@6.2.0
      //不同版本的webpack安装不同版本的各种loader。
    • 然后在webpack.config.js中配置相关loader
      注意:如果你是webpack5版本以上,你可以使用asset方式配置,这种方式我没太懂,没去研究
      你也可以仍然使用loader的方式配置,但需要设置一些属性,下面是两种方式的配置
      asset的配置

      {
        test:/\.(jpg|png|gif)$/,
        type:"asset",
        //解析
        parser: {
          //转base64的条件
          dataUrlCondition: {
            maxSize: 25 * 1024, // 25kb
           }
         },
         generator:{
           //与output.assetModuleFilename是相同的,这个写法引入的时候也会添加好这个路径
           filename: 'img/[name][hash:6].[ext]',
           //打包后对资源的引入,文件命名已经有/img了
           publicPath:'./'
         },
      }

      webpack5以上,使用file-loader时的配置:

      {
        test: /\.(png|jpg|gif|jpeg)$/,
        use: [
            {
              loader: 'url-loader',
              options: {
                limit: 8192,
                /*
                	name是文件名,也可以加个路径(img目录),另外[name],则是图片原文件名
      			[hash:8]则是生成一个随机8位hash数,防止图片重名,
      			[ext]是原文件名的后缀
                */
                name:'img/[name].[hash:8].[ext]',
                //输出路径,这里路径是:path(上面配置的dist目录)+outputPath+name路径
                // outputPath: './',  
                // publicPath: 'dist/'
                /*
                   在url-loader内部封装了file-loader而file-loader在新版本中esModule属性默认为
      			 true,即默认使用ES模块语法导致了引用图片文件的方式和以前的版本不一样,引入路径改变了,自然找不到图片。
                 */
                  esModule: false
                },
            }
         ],
         /*
            当在webpack 5中使用旧的assets loader(如file-loader/url-loader/raw-loader等)
      	  和asset模块时,这可能会导致asset重复,所以你可能想阻止webpack5内置的asset模块的处理,
            你可以通过将asset模块的类型设置为‘javascript/auto’来解决。
         */
         type: 'javascript/auto'
      }

      如果你是webpack4或以下,你可以去除上面的esModule和type两个属性。

    • 配置完毕后,要想让webpack认识这个图片是模块,就需要与入口函数联系起来,
      当我们在css的属性中引用这张图片时,入口文件中只要引入css模块,则这张图片也会被当做模块。
      下面,我们更改normal.css文件,背景换成这张图片

      body{
          /*background-color: aqua;*/
          background: url("../img/2.jpg");
      }
    • 安装完loader,配置好webpack.config.js,引入完图片后,使用命令打包,
      打包完毕后查看页面背景是否成功显示图片。

  • 配置es6转es5的loader
    有些老版本浏览器可能不支持es6,所以,webpack可以通过配置loader,将es6转成es5,适配老版本浏览器

    • 使用npm安装babel-loader等依赖
      npm install babel-loader@8.2.2 @babel/core@7.15.0 @babel/preset-env@7.15.0 --save-dev

    • 配置webpack.config.js文件,配置完毕后,webpack在打包的过程中会将bundle.js文件中es6的语法换成es5的语法。

      {
          test: /\.js$/,
          exclude: /(node_modules|bower_components)/,
          use: {
              loader: 'babel-loader',
              options: {
                  presets: ['@babel/preset-env']
              }
          }
      }

webpack配置vue

我们使用webpack工具,就是为了能够解决模块之间的依赖问题和使用打包功能,
来看看,如何使用webpack去管理vue吧

  • npm安装vue依赖,并且配置webpack.config.js

    //安装vue依赖,vue依赖在项目打包后仍需要使用,所以是运行时依赖,没有-g则是本地安装
    npm install vue@2.6.14 --save	
    
    //配置webpack.config.js,在exports的对象上,加上resolve属性,具体如下
    resolve: {
        alias: {
            'vue$':'vue/dist/vue.esm.js'
        }
    }
  • 在入口文件(main.js)中,创建vue实例

    //导入vue模块
    import Vue from 'vue';
    const app  = new Vue({
        el:'#app',
        data:{
            message:'万一'
        }
    });
  • 在index.html页面中代码如下,打包完成后,打开浏览器就可以看到数据了。

    <body>
        <div id="app">
          <h3>{{message}}</h3>
        </div>
        <script src="dist/bundle.js"></script>	//一定要放在最后
    </body>

vue配置项目别名

当使用命令vue create 项目名或者vue init webpack 项目名创建项目名后,你可能需要给项目的目录配置
一些别名,这样在项目中使用路径时就不需要使用../../../这种方式,而是直接使用配置好的views/xxx
或者components/xxx,如果在template中img的src中使用路径,需要加上~,例如:~assets/xxx/xxx
别名配置,你需要在项目目录下创建vue.config.js,并在其中配置如下设置:

module.exports = {
  configureWebpack: {
    resolve: {
      /*配置项目路径别名*/
      alias: {
        'assets': '@/assets',
        'common': '@/common',
        'components': '@/components',
        'network': '@/network',
        'views': '@/views'
      }
    }
  }
}

vue中el与template的关系

上面那种方式开发中并不会使用,例如并不会在index.html中的div中写任何代码,SPA页面,或者说单页面富应用
说的就是,一旦index.html页面确定下来了,就不会再更改,而是通过JS动态更换DOM元素的方式与用户交互
所以像上面例子div标签中的{{message}},不会这样直接写,会写在组件的template中

当一个组件同时存在el属性和template属性时,template中的内容将会在页面渲染的时候替换el管理的div标签

//例如:index.html如下
<body>
    <div id="app">
    </div>
    <script src="dist/bundle.js"></script>	//一定要放在最后
</body>

//main.js
new Vue({
  el:'#app',
  template:'<h3>{{message}}</h3>',
  data:{
          message:'万一'
      }
});

当页面在渲染的时候,会将template的内容替换index.html中的div,所以实际显示下面这样
<body>
    <h3>{{message}}</h3>
    <script src="dist/bundle.js"></script>	//一定要放在最后
</body>

这部分内容属于vue的知识点,所以单独写出来。

vue的终极方案

说了最初的在webpack中使用vue的方式,又说了el和template的关系,都是为了vue终极方案做铺垫
我们在这里将会对结合上面两种方式,看看实际开发中是怎么使用vue的。

  • 首先我们得有一个简单的index.html页面

    //根据上面例子,不难想象在main中肯定有vue实例的template替换掉这个div标签
    <body>
        <div id="app">
        </div>
    </body>
  • 在我们的main.js入口文件中,代码如下

    //vue终极方案
    import Vue from 'vue';
    import wanyi from './vue/wanyi'
    new Vue({
      el:'#app',
      template:'<wanyi/>',      //template中的内容会替换html页面中的div
      components:{              //这里是根组件的子组件,相当于wanyi:wanyi,使用es6的变量增强写法
        wanyi
      }
    });
  • 上面代码中,我们引入了wanyi组件,这是我们自定义的.vue文件,这个.vue文件可以包含template块
    样式块,和script块,我们以后写组件都是这样写,html代码,样式,js代码都写在一个.vue文件中

    <template>
      <button @click="show" class="bt">{{message}}</button>
    
    </template>
    
    <script>
    export default {		//默认导出对象,该对象是一个组件
      name: 'wanyi',
      data(){
        return{
          message:'万一',
        }
      },
      methods:{
        show(){
          console.log('万一挑一,我是唯一');
        }
      }
    };
    </script>
    
    <style scoped>
    .bt{
      color: blue;
    }
    </style>
  • .vue文件写完后,在main.js被作为模块导入,组件被挂载到vue实例中(根组件),然后在template中使用组件标签,
    最后会替换index.html页面中vue实例管理的div标签。现在我们还不能使用.vue作为模块,
    因为webpack不认识这个文件,也不知道怎么解析该文件,所以我们还需要安装一些loader,
    让webpack能够处理该.vue文件,就像不认识css文件,就安装相应的loader一样。

    /*
    	安装vue-loader等依赖,注意vue-template-compiler版本要和vue版本一致
    	并且vue-loader版本如果高了会需要你配置插件,如果低了打包的时候会报错(Cannot read property 'vue' of undefined)
        报错的解决方法:如果项目中webpack升级到4^ 的话 升级下vue-loader到14^ 然后问题就没有了
    */
    npm install vue-loader@14.2.4 vue-template-compiler@2.6.14 --save-dev
    
    //安装完loader,配置webpack.config.js
    {
        test: /\.vue$/,
        use: ['vue-loader']
    }

    配置完毕后,打包后,在浏览器就可以看到效果。

plugin的使用

plugin,插件,通常是用于对某个现有的架构进行扩展webpack中的插件,
就是对webpack现有功能的各种扩展,比如打包优化,文件压缩等
loader和plugin区别

  • loader主要用于转换某些类型的模块,它是一个转换器,它操作的是文件,将文件A通过loader转换成文件B
    是一个单纯的文件转换过程,例如less转css等
  • plugin是对webpack本身的扩展,是一个扩展器,丰富webpack本身,增强功能,针对的是在loader结束
    之后,webpack打包的整个过程,它并不直接操作文件,而是基于事件机制工作,监听webpack打包过程
    中的某些节点,执行广泛的任务。

plugin的使用过程

  • 1)通过npm安装需要使用的plugin(某些webpack已经内置的插件不需要安装)
  • 2)在webpack.config.js中的plugins中配置插件

几种常见的插件配置

  • 配置添加版权的plugin,BannerPlugin
    该插件作用是在打包后的bundle.js文件中第一行生成指定的版本信息
    因为该插件是webpack内置的模块,所以在webpack.config.js中导入webpack模块
    再配置默认导出对象,增加plugin属性,代码如下:

    const webpack = require('webpack');
    plugins: [	//plugin是导出对象的属性
        new webpack.BannerPlugin('最终版权归万一所有')
    ]

    配置完毕后,重新打包文件,然后打开bundle.js,查看第一行,就会显示我们的版权

  • 配置打包html的插件,html-webpack-plugin
    目前我们的项目的index.html文件是在项目的根目录的,而实际上,我们的dist文件会放到服务器上运行,
    但是打包后的dist中并没有我们的index.html文件,所以该插件可以根据指定模板(就是我们的项目根目录的index.html)
    自动生成一个index.html文件,还可以将打包的js文件,自动通过script标签插入到新生成的body中。使用步骤如下:

    • 安装插件,npm install html-webpack-plugin@5.3.2 --save-dev

    • 配置webpack.config.js文件

      const htmlWebpackPlugin = require('html-webpack-plugin');
      
      plugins: [
          new htmlWebpackPlugin({
              /*
              	用于生成dist/index.html的模板,模板中并不需要写script标签引入bundle.js,
              	插件会自动引用,另外这里是模板路径,相对webpack.config.js路径.
              	我这里index.html和webpack.config.js在同一目录中,所以直接写文件名
              */
              template: 'index.html'     
          })
      ]
      
      //有了该插件,我们的index.html中就不需要引入bundle.js,index.html如下,只有一个div
      <body>
          <div id="app">
          </div>
      </body>

      配置好后,执行打包命令后,会根据根目录的index.html模板在dist中生成新的index.html

  • 配置压缩JS文件的插件
    通过配置可以在打包过程中压缩bundle.js,不过新版本webpack有个production模式,
    以production模式运行webpack,会自动对bundle.js进行压缩。在package.json中配置

    "dev": "webpack --mode development",
    "prod": "webpack --mode production"

    如果是使用插件的方式进行压缩,使用步骤如下:

    • 安装插件:npm install uglifyjs-webpack-plugin --save-dev

    • 配置webpack.config.js

      const uglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
      plugins: [
          new UglifyjsWebpackPlugin() //压缩bundle.js文件会导致你的版权插件失效,被删除掉。
      ]

搭建本地服务器(WDS)

webpack提供了一个可选的本地开发服务器,这个本地服务器基于node.js搭建,内部使用express框架,
可以实现我们想要的:让浏览器自动刷新显示我们修改之后的结果,这个功能,可以通过在webpack.config.js
返回的对象中配置一个watch属性实现,watch属性值为true时,当我们修改文件后,会自动打包我们的文件。

通过插件的方式配置本地服务器,步骤如下

  • 安装本地服务器依赖,webpack,webpack-cli,webpack-dev-server三个依赖之间版本有影响,不能随便安装
    npm install --save-dev webpack-dev-server@4.0.0
    注意:如果安装失败,webpack5.0很可能安装失败,你就需要换种方式安装该依赖
    打开项目的package.json,在devDependencies中增加"webpack-dev-server": "^4.0.0"
    再在命令行执行:npm install,或者npm install devDependencies.

  • 配置webpack.config.js,在导出对象中增加devServer属性

    devServer: {
        //表示为哪个文件夹提供本地服务,默认是根文件夹,相对webpack.config.js的路径
        contentBase: './dist',  
        inline: true  //是否实时监听文件改动
    }
    
    //webpack新版本可能是下面这种
    devServer: {
        //表示为哪个文件夹提供本地服务,默认是根文件夹,相对webpack.config.js的路径
        watchFiles: './dist',
        liveReload:true   //是否实时监听文件改动
      }
  • 我们还需要配置package.json中的script,因为如果我们在项目根目录中执行启动本地服务器命令webpack-dev-server
    则实际上寻找的是全局安装的依赖,所以我们需要将命令配置在package.json中的script中,这样就会去
    本项目的node_modules中寻找依赖。package.json中script配置如下:

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "webpack --mode development",
        "prod": "webpack --mode development",
        "server": "webpack-dev-server --mode development --open"
    	//--open表示本地服务器启动后,自动打开dits目录下的index.html页面
    }

    配置好后,启动本地服务器:npm run server,当修改源文件后,不需要打包即可快速刷新结果
    其并不更新dist文件,而是将修改的代码放在内存中,当多次修改完成后,关闭本地服务器,再打包即可。

配置文件的分离

配置文件的分离指的是,将原来webpack.config.js中的配置分开,开发的配置和生产的配置分开放。
本案例中,我们将webpack.config.js文件,分成三个文件,base.config.js,dev.config,prod.config.js
base.config.js文件内存放开发和生产模式中都需要的配置,而dev.config.js文件存放开发模式时需要的配置
prod.config.js存放生产模式时需要的配置,当开发的时候,我们使用base.config.js+dev.config.js文件,反之亦然
下面是配置文件分离步骤:

  • 首先在项目根目录创建config,并创建好这三个配置文件,在将各个配置写到分开写到这些配置文件之前
    我们需要一个依赖,这个依赖帮助我们,将base.config.js与另外两个文件之间产生关联,也就是说,
    当我们使用dev.config.js配置文件进行打包时,使用依赖,将base.config.js与dev.config.js中的配置进行合并
    安装合并依赖:npm install webpack-merge@5.8.0 --save-dev

  • 安装好依赖后,我们需要在dev.config.js和prod.config.js中使用依赖,使得他们与base.config.js产生关联
    下面是拆分后的三个配置文件的代码

    //base.config.js
    //因为全局安装中存在vue,所以可以直接导入vue的path模块
    const path = require('path');
    
    const htmlWebpackPlugin = require('html-webpack-plugin');
    
    //Common.js语法导出
    module.exports={
      entry: './src/main.js', //打包入口,相对于webpack.config.js,main.js所在的位置
      output:{
        /*
          path是出口路径,指要把打包后的文件放在哪里,__dirname是path模块的变量,表示当前文件的绝对路径
          该变量表示当前webpack.config.js的绝对路径,resolve函数表示将两个路径进行自动拼接
          所以整体意思就是打包后文件放在webpack.config.js同级的dist目录中
         */
        path:path.resolve(__dirname,'./dist'),
        filename: 'bundle.js',    //打包后的文件名
      },
      module: {
        rules: [
          {
            //css的loader
            test: /\.css$/,
            use: [ 'style-loader', 'css-loader' ]
          },{
            //less的loader
            test: /\.less$/,
            use: [{
              loader: "style-loader" // creates style nodes from JS strings
            }, {
              loader: "css-loader" // translates CSS into CommonJS
            }, {
              loader: "less-loader" // compiles Less to CSS
            }]
          },{
            //文件上传的loader
            test: /\.(png|jpg|gif|jpeg)$/,
            use: [
              {
                loader: 'url-loader',
                options: {
                  limit: 8192,
                  name:'img/[name].[hash:8].[ext]',
                  // outputPath: './', //输出路径,这里路径是:path(上面的)+outputPath+name路径
                  // publicPath: 'dist/'
                  /*
                    在url-loader内部封装了file-loader而file-loader在新版本中esModule属性默认为true
                	即默认使用ES模块语法导致了引用图片文件的方式和以前的版本不一样,引入路径改变了,自然找不到图片。
                  */
                  esModule: false
                },
              }
            ],
            /*
              当在webpack 5中使用旧的assets loader(如 file-loader/url-loader/raw-loader等)
    		  和asset模块时,这可能会导致asset重复,所以你可能想阻止webpack 5内置的asset模块的处理,
    		  你可以通过将asset模块的类型设置为‘javascript/auto’来解决。
             */
            type: 'javascript/auto'
          },{
            //es6转es5的loader
            test: /\.js$/,
            exclude: /(node_modules|bower_components)/,
            use: {
              loader: 'babel-loader',
              options: {
                presets: ['@babel/preset-env']
              }
            }
          },{
            //vue-loader
            test: /\.vue$/,
            use: ['vue-loader']
          }
        ]
      },resolve: {
        //vue配置
        alias: {
          'vue$':'vue/dist/vue.esm.js'
        }
      },plugins: [
        new htmlWebpackPlugin({
          /*
            用于生成dist/index.html的模板,模板中并不需要写script标签引入bundle.js,
            插件会自动引用
          */
          template: 'index.html'
        })
      ],
      // mode: 'development', //设置webpack模式
      // watch: true  // 监听文件改动并自动打包
    }
    //dev.config.js
    let webpackMerge = require('webpack-merge');
    let base = require('./base.config');
    
    module.exports = webpackMerge.merge(base,{
      //本地服务器的配置
      devServer: {
        watchFiles: './dist',  //表示为哪个文件夹提供本地服务,默认是根文件夹,相对webpack.config.js的路径
        liveReload:true   //是否实时监听文件改动
      },
    });    
    //prod.config.js
    const webpack = require('webpack')
    let webpackMerge = require('webpack-merge');
    let base = require('./base.config');
    
    module.exports = webpackMerge.merge(base,{
      plugins: [
        new webpack.BannerPlugin('最终版权归万一所有'),
      ],
    });
  • 将原来的webpack.config.js文件区分开后,我们需要更改base.config.js文件中的出口路径,
    将原来出口路径中的./dist改成../dist,因为我们此时的配置文件在项目根路径下的config目录中,而dist目录在外层目录。

    output:{
        /*
          path是出口路径,指要把打包后的文件放在哪里,__dirname是path模块的变量,表示当前文件的绝对路径
          该变量表示当前webpack.config.js的绝对路径,resolve函数表示将两个路径进行自动拼接
          所以整体意思就是打包后文件放在webpack.config.js同级的dist目录中
         */
        path:path.resolve(__dirname,'../dist'),
        filename: 'bundle.js',    //打包后的文件名
    }
  • 最后,我们需要修改package.json文件中的script,因为如果是原来的配置,则webpack会寻找webpack.config.js文件
    这个文件是webpack的默认配置文件名,而我们现在已经不使用这个配置文件了,我们需要在执行打包命令上
    使用自己的配置文件名,下面是更改后的package.json中的script,更改后,执行打包命令即可看到效果。

    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "dev": "webpack --mode development --config ./config/dev.config.js",
        "prod": "webpack --mode development --config ./config/prod.config.js",
        "server": "webpack-dev-server --mode development --open --config ./config/dev.config.js"
    }

完结

上面是目前学习的webpack的全部内容,本次实验的代码,已经上传至github,有需要可以查看,查看案例代码
写完这篇,下一步就是继续看vue视频了,10月份之前一定看完vue网课!
下面是一些大佬文章: