zoukankan      html  css  js  c++  java
  • 第一节:webpack打包、压缩及兼容性处理

    一.前言

    当我们使用vue-cli3创建项目时,会自动生成相应的webpack配置,不过明白webpack的原理和基本设置方法对我们局部修改某些webpack配置还是很有必要的;

    二.为什么需要构建工具?

    • 转换ES6语法;
    • 转换JSX
    • CSS前缀补全/预处理器;
    • 压缩混淆;
    • 图片压缩;

    image-20200419223425094

    官方文档

    三.Webpack五个核心概念

    1.入口(Entry)

    入口(Entry)指示webpack以哪个文件为入口起点开始打包,分析构建内部依赖图;

    2.出口(Output)

    输出(Output)指示Webpack打包后的资源bundles输出到哪里去,以及如何命名;

    3.Loader

    LoaderWebpack能够去处理那些非JavaScript文件(Webpack自身只理解JavaScript

    4.插件(Plugins)

    插件(Plugins)可以用于执行范围更广的任务,插件的作用范围包括,从打包优化和压缩,一直到重新定义环境中的变量等;

    5.模式(Mode)

    通过设置 mode 参数为 developmentproduction 之中的一个,来启用相应模式下 webpack 内置的优化;

    module.exports = {
      mode: 'production'
    };
    
    • development模式:代码本地调试和运行的环境;
    • production模式:代码优化上线运行的环境;

    四.源码仓库

    webpack系列中使用到的演示实例源码已上传至该仓库:

    webpack-demo

    示例:

    创建下图所示的文件目录:

    image-20200419225355431

    其中src表示源文件目录,存储着webpack的入口起点文件,比如index.jsbuild用于webpack打包处理之后输出的目录,其余文件可通过执行:

    可通过下列指令创建一个演示实例:

    npm init//生成package.json
    webpack_test //文件夹名字
    

    一路回车取默认值生成:

    image-20200419225659419

    随后执行以下命令全局安装webpack,其中的-g参数表示全局安装,即使已经安装过了,也没关系。该指令会覆盖原来的安装并进行版本更新。

    五.npm i 和 npm install 的区别

    iinstall的缩写;

    实际使用的区别点主要如下(windows下):

    1. npm i安装的模块无法用npm uninstall删除,用npm uninstall i才卸载掉

    2. npm i会帮助检测与当前node版本最匹配的npm包版本号,并匹配出来相互依赖的npm包应该提升的版本号

    3. 部分npm包在当前node版本下无法使用,必须使用建议版本

    4. 安装报错时intall肯定会出现npm-debug.log 文件,npm i不一定

    npm i webpack webpack-cli -g
    

    image-20200419230103904

    然后在本地安装,参数-D--save-dev的缩写,表示它会将webpack添加到package.json中的开发依赖中:

    npm i webpack webpack-cli -D
    

    image-20200419230256099

    webpack中下载的所有东西都属于开发依赖,不属于生产依赖,所以都使用-D

    index.js文件内容:

    /*
      index.js:webpack入口起点文件
    
      1.运行指令:
        开发环境指令:webpack ./src/index.js -o ./build/built.js --mode=development
        翻译:webpack会以./src/index.js为入口环境开始打包,打包后输出 ./build/built.js;整体打包环境是开发环境;代码不会被压缩,可以看清楚逻辑;
        
        生产环境指令:webpack ./src/index.js -o ./build/built.js --mode=production
        翻译:webpack会以./src/index.js为入口环境开始打包,打包后输出 ./build/built.js;整体打包环境是生产环境;代码会被压缩,无法看清代码逻辑;
    */
    
    function add(x, y){
      return x + y
    }
    
    console.log(add(1, 2));
    

    先进入02-webpack初体验目录,然后打包文件

    开发环境指令:

    输入下列开发环境指令打包文件:

    webpack ./src/index.js -o ./build/built.js --mode=development
    

    输出结果:

    image-20200419231151610

    • 其中的Hash值代表打包的结果,每次打包都会生成一个唯一的哈希值,唯一地ID
    • 其余的有版本,打包时间,资源Asset,打包后的文件built.js的大小等;

    此时build目录下会多出一个built.js文件

    image-20200419231459958

    生产环境指令

    输入下列生产环境指令打包文件:

    webpack ./src/index.js -o ./build/built.js --mode=production
    

    运行结果:

    image-20200419231744095

    查看生成的build目录下的built.js文件,发现代码进行了压缩:

    image-20200419231840427

    这个代码是可执行的:

    image-20200419231927925

    每次src文件夹中的入口文件index.js引入了新依赖之后,都要重新进行打包,才能看到最新的结果;

    也就是说只要指定了入口文件index.js,就可以在index.js中通过import引入很多依赖文件,这样

    webpack在打包入口文件index.js的时候就会根据其中引入关系,一起打包index.js的依赖文件;

    那么引入其他文件呢?

    比如在index.js中引入css文件的时候:

    image-20200419233155746

    会出现打包错误,打包出来的built.js中的该部分会丢出一个错误:

    image-20200419233301997

    得出结论:

    • webpack能处理js/json资源,不能处理css/img等资源;
    • 生产环境和开发环境将ES6模块化编译成浏览器能识别的模块化;
    • 生产环境(production)比开发环境(development)多了一个压缩js代码;

    六.打包样式资源

    虽然webpack不能直接打包css文件,但是可以借助于各种各样的Loaderwebpack不能识别的文件转换成它能识别的格式;

    需要在根目录的package.json.js文件中进行配置:

    整体配置为:

    const { resolve } = require('path')
    
    module.exports= {
      entry: './src/index.js',
       //输出:这是一个对象,里面有两个东西。filename表示输出的文件名,path表示输出的路径,写路径的时候通常会写一个绝对路径,避免出错。绝对路径会引入一个nodejs的path模块
      output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules:[ 
          {
          //详细loader配置
            //匹配哪些文件
            test: /.css$/,
            //处理这些文件
            use: [
              'style-loader',
              'css-loader'
            ]
          }
        ]
      },
      plugins: [
        //详细plugins配置
      ],
      mode: 'development',
      //mode: 'production'
     }
    

    首先, webpack.json.jswebpack的配置文件。作用为:指示webpack干哪些活(当运行webpack指令时,要加载哪些配置);

    所有构建工具都是基于node.js平台运行的,模块化默认采用commonjs。可以看到commonjs会导出一个对象,在该对象中写webpack的配置:

    1.入口起点

      entry: './src/index.js'
    

    表示打包的入口文件为src目录下的index.js文件;

    2.输出

      output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
       }
    

    这是一个对象,其中:

    • filename表示输出的文件名;

    • path表示输出的路径;

    • 写路径的时候为了避免出错,通常会写一个绝对路径。需要引入一个nodejs的模块path模块:

      const { resolve } = require('path')
      

      其中的resolve是用来拼接绝对路径的方法,格式为:

       path: resolve(__dirname, 'build')
      

      传入两个参数:__dirname和当前的build目录;其中__dirnamenodejs的变量,它代表当前文件(指webpack.config.js)的目录的绝对路径:

    image-20200420000219160

    • 该绝对路径就是03-打包样式资源,也就是说__dirname的值就是03-打包样式资源,拼接上build,再加上第一个参数filename指明的built.js一起表示,打包后输出到build目录下的built.js文件中;

    3.Loader配置

    module: {
      rules: {
      	//详细loader配置
      }
    }
    

    4.插件(plugins

      plugins: [
        //详细plugins配置
      ],
    

    5.模式

      mode: 'development',
      //mode: 'production'
    

    开发模式development和生产模式production两种模式选一种,不能同时写;

    6.打包样式资源

    可以使用css-loader来翻译css文件:

    module: {
        rules: [ 
            {
                //详细loader配置
                //使用正则表示式指定匹配哪些文件
                test: /.css$/,//该正则表达式表明,匹配以css结尾的文件,为转义符
                //通过test的正则表达式匹配了文件后,这里指定使用哪些loader进行处理
                use: [
                    //需要使用两个loader
                    //作用:创建style标签,将js中的样式资源插入进去,添加到head中生效
                    'style-loader',
                    //将css文件转换成一个commonjs模块并加载到js中,里面内容是样式字符串
                    'css-loader'
                ]       
            }
        ]
    }
    

    注意:rules属性是一个数组,里面的每一个元素都为对象,每个对象匹配并处理一类文件。并且对象中的use属性也是一个数组,其中loader的执行顺序为:从右到左,从下到上依次执行。为了不用每次都安装同样的依赖文件包,可以在根目录执行下列指令安装这些包,因为子目录中找不到包会依次往根目录找:

    npm init //生成package.json
    npm i webpack webpack-cli -D //下载webpack包,-D是--save-dev的缩写,表示是开发时依赖
    npm i css-loader style-loader -D //下载两个loader,都是开发时依赖
    

    进入03-打包样式资源目录,执行webpack进行打包:

    image-20200420004856474

    打开打包生成的built.js,可以看到index.css被成功打包了:

    image-20200420004940891

    build目录下新建index.html引入生成的打包文件built.js

    <script src="./built.js"></script>
    

    随后使用浏览器打开该文件,发现样式发生了变化,源码多了style标签

    image-20200420143420759

    这便是style-loader的作用。

    注意:不同类型的文件要配置不同的loader来处理;比如为了处理less文件,需要webpack.config.js中的rules数组中再增添一个对象:

      module: {
        rules:[ 
          //匹配并处理css文件
          {
            test: /.css$/,
            //处理这些文件
            use: [
              'style-loader',
              'css-loader'
            ]
          },
          //匹配并处理less文件
          {
           test: /.less$/,
           use: [
             //以style标签的形式在head标签中插入样式
             'style-loader',
             //将css文件装换为js形式
             'css-loader',
             //将less文件编译成css文件
             'less-loader'
           ] 
          }
        ]
      }
    

    可以看到处理less文件需要三个loader,注意loader执行的顺序为由下往上,由右往左:

    • less-loader:将less文件编译成css文件;
    • css-loader:将css文件内容整合到js中;
    • style-loader:从js中找到这些css代码,将它传入style标签,插入head标签中;

    所以处理less文件需要使用三个loader,注意:使用loader之前要先进行下载:

    npm i less less-loader -D //全局安装less和less-loader
    

    注意:最好统一在根目录下载包,这样其他子目录向上查找时都能找到相应的包,避免重复下载;

    安装完依赖包,并且正确配置package.config.js之后,执行webpack指令,就可以成功打包less文件了:

    image-20200420145319811

    七.打包html资源

    1.添加基本配置

    首先给webpack.config.js添加基本的配置:

    const {resolve} = require('path')
    
    module.exports = {
      entry : './src/index.js',
      output : {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          //laoder的配置
        ]
      },
      plugins: [
        //plugins的配置
      ],
      //为了方便调试,使用不压缩代码的development模式
      mode: 'development'
    }
    
    

    打包html文件需要使用插件:

    • 使用loader:下载 、使用(配置laoder);
    • 使用plugins: 下载 、引入 、使用;

    2.下载和引入插件

    首先下载插件,同样采用全局下载,和开发时依赖:

    npm i html-webpack-plugin -D
    

    然后引入插件:

    const HtmlWebpackPlugin = require('html-webpack-plugin')
    

    由于它是构造函数,直接new一个实例就可以使用了:

      plugins: [
        //plugins的配置
        new HtmlWebpackPlugin()
      ],
    

    3.打包文件

    我们执行webpack打包看看有什么效果:

    注意要进入项目目录04-打包html资源再进行打包;

    build目录下多了一个html文件:

    image-20200420151248592

    打开该html文件:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <title>Webpack App</title>
      <meta name="viewport" content="width=device-width, initial-scale=1"></head>
      <body>
      <script src="built.js"></script></body>
    </html>
    

    发现自动引入了built.js文件,注意,src目录下的源文件index.html中是没有引入过built.js文件的。

    所以,插件html-webpack-plugin的作用是,创建一个空的HTML文件,自动引入打包输出的所有资源(包括js/css);

    如果想要打包出有结构的html文件,则需要给该插件传入一个对象,里面有模板参数template

      plugins: [
        //plugins的配置
        new HtmlWebpackPlugin({
          //复制一个/src/index.html文件,并自动引入打包输出的所有资源
          template: './src/index.html'
        })
      ],
    

    此时再次执行webpack打包,打包出来的html文件就拥有了src目录下index.html文件的所有结构,也就是原样复制了一份。

    4.完整配置

    webpack.config.js的完整配置:

    const {resolve} = require('path')
    //引入插件
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
      entry : './src/index.js',
      output : {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          //laoder的配置
        ]
      },
      plugins: [
        //plugins的配置
        new HtmlWebpackPlugin({
          //复制一个/src/index.html文件,并自动引入打包输出的所有资源
          template: './src/index.html'
        })
      ],
      //为了方便调试,使用不压缩代码的development模式
      mode: 'development'
    }
    
    

    5.总结

    我们在入口文件index.js中并没有引入html文件。这个插件会根据template属性指定的路径去寻找该文件,并且把该文件的内容复制进来并输出。只不过在输出之前会将打包生成的所有资源都引入到这个复制的html文件中。如果是js文件,就通过script标签引入;如果是css文件就通过link标签引入;

    需要注意的是,千万不要手动引入这些html文件,因为插件帮我们自动引入了这些文件,我们再引入就重复了。

    注意,与loader的使用不同,使用插件时要多一步引入操作;

    八.打包图片资源

    项目目录如下:

    image-20200420160111287

    在入口文件index.js中引入样式文件index.lessindex.html文件不用引入,该文件会由插件自动引入;在样式文件index.less中又引入图片资源small.pngbig.png;通过配置webpack.config.js来打包这些文件:

    1.初始配置

    const { resolve }  = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: 'built.js',
        path:resolve(__dirname, 'build')
      },
      module: {
        rules: [
          {
            test: /.less$/,
            use: [
              'style-loader',
              'css-loader',
              'less-loader'
            ]
          },
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        }
        )
      ],
      mode: 'development'
    }
    

    此时没有添加处理图片的loader,执行webpack时会出现错误:

    image-20200420161419210

    注意:webpack不能识别资源的时候,第一时间就要想到loader。添加以下loader处理图片资源:

    rules: [
        //匹配并处理图片
        {
            test: /.(jpg|png|gif)$/,
            //只使用一个loader可以不用use数组
            loader: 'url-loader', 
            options: {
                limit: 8 * 1024
            }
        }
    ]
    

    只使用一个loader时可以不使用use数组;使用多个loader时需要使用use数组。还可以通过option对象进行参数的配置;

    关于options对象中的limit属性:

    • 规定图片大小小于8kb,就会被base64处理。这样的好处是:减少服务器的请求数量,减轻服务器压力;缺点是:图片体积会增大,导致文件请求速度更慢;
    • 所以就需要找到一个平衡点,我们规定8~12KB以下的图片进行base64处理。所以上面limit中配置的8就表示小于等于8KB的图片都进行base64处理。开发中可根据实际需要适当调整这个阈值;

    2.打包图片资源

    注意,需要下载两个loaderurl-loaderfile-loader,因为前者依赖于后者;

    回到根目录下,下载这两个包,依旧是开发时依赖:

    npm i url-loader file-loader -D
    

    安装完成后执行webpack进行打包:

    image-20200420163603695

    可以看到,成功地对两张图片进行了打包。

    查看打包文件输出的目录build,发现少了一张图片:

    image-20200420163836459

    打开built.js,可以发现,小于8KBsmall.png被装换为了base64编码,所以会少了一张图片:

    image-20200420164009693

    我们再打开打包出来的index.html文件,可以看到,small.png也正常显示了;

    image-20200420164115771

    打开调试工具,选中small/png,可以看到该图片被转换成了base64编码:

    image-20200420164227872

    3.打包html文件中的图片

    上面是通过样式文件index.less引入的图片,那么通过src目录下的index.html文件引入图片能被正常打包吗:

      <img src="./big.png" alt="">
    

    执行webpack再次打包,发现并没有报错:

    image-20200420164700598

    但是,查看输出文件夹下的index.html文件,发现图片的引入路径并没有发生变化:

    image-20200420164758060

    这显然是不对的,打包过后,输出文件夹build中显然不会存在big.png。因此得出结论:url-loader不能处理html中的图片。

    我们可以添加一个html-loader来处理:

    rules: [
          {
            test: /.html$/,
            loader: 'html-loader'
          }
    ]
    

    不能对该loader顾名思义,它是专门处理html中的img图片的,负责引入img从而能被url-loader进行处理;

    再次提醒:凡是使用到loader都是要先下载;

    npm i html-loader -D
    

    随后打包出来的index.html中的图片路径就是正确的了:

    image-20200420170243522

    较低版本的webpack可能会出现解析问题:打包出来的图片路径为[object module]。这是由于url-loader采用的是es6模块化解析,而html-loader引入图片时采用的是commonjs而发生了冲突;

    解决方法为:通过设置esModule: false,来关闭url-loaderes6模块化,使用commonjs解析:

    rules: [
        {
            test: /.(jpg|png|gif)$/,
            loader: 'url-loader', 
            options: {
                limit: 8 * 1024,
                esModule: false
            }
        }
    ]
    

    注意到,打包过后的图片是一串哈希值,比较长,可以通过name属性重命名打包后的图片:

    rules: [
        {
            test: /.(jpg|png|gif)$/,
            loader: 'url-loader', 
            options: {
                limit: 8 * 1024,
                name: '[hash:10].[ext]'
            }
        }
    ]
    
    • [hash:10]表示:取图片哈希值的前十位;

    • [ext]表示:取文件的原扩展名;

    再次打包,可以看见另外生成了哈希值只有十位的图片:

    image-20200420171222724

    还有一个细节:在样式index.less中我们引入了三张图片,两张是相同的;webpack不会打包出三张图片,它会进行识别,不打包重复的文件,这也是webpack的优点之一;

    4.完整配置

    至此webpack的完整配置为:

    const { resolve }  = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: 'built.js',
        path:resolve(__dirname, 'build')
      },
      module: {
        rules: [
          {
            test: /.less$/,
            //要使用多个loader时使用use数组
            use: [
              'style-loader',
              'css-loader',
              'less-loader'
            ]
          },
          //匹配并处理图片
          {
            test: /.(jpg|png|gif)$/,
            //需要下载url-loader 和 file-loader
            loader: 'url-loader', 
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]'
            }
          },
          {
            test: /.html$/,
            //该loader专门处理html中的img图片
            loader: 'html-loader'
          }
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        }
        )
      ],
      mode: 'development'
    }
    

    九.打包其他资源

    1.安装file-loader

    所谓其他资源,指的是自定义的资源,可能是字体文件,脚本文件等。我们希望这些文件不需要进行压缩或其他处理,直接原封不动地打包到输出文件夹中。

    比如打包iconfont文件,需要采用专门的laoder进行处理:

    module: {
        rules: [
            {
    	      exclude: /.(css|js|html)$/,
              loader: 'file-loader',
            }
        ]
    }
    

    打包其他资源(除了html/css/js以外的资源),可以使用exclude属性排除其他资源。使用file-loader进行打包。

    使用前需要在根目录安装这个loader

    npm i file-loader -D
    

    2.打包

    随后对src文件下的iconfont文件和其他文件进行打包:

    image-20200420180605108

    打包出来的文件目录为:

    image-20200420180623328

    运行其中的index.html可以正常看到iconfont,说明打包成功:

    image-20200420180705323

    同样也可以添加option属性,重命名打包过后的文件名:

    重新打包成功生成重命名后的文件:

    image-20200420181036654

    3.完整配置

    此时webpack的完整配置为:

    const { resolve } = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
      entry: './src/index.js',
      output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules:[
          {
            test: /.css$/,
            use: [
              'style-loader',
              'css-loader'
            ]
          },
          //打包其他资源(除了html/css/js以外的资源),可以使用exclude属性排除其他资源
          {
            exclude: /.(css|js|html)$/,
            loader: 'file-loader',
            options: {
              name: '[hash:10].[ext]'
            }
          }
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        }) 
      ],
      mode: 'development'
    }
    

    十.devServer

    当我们每次给src目录下新增文件和内容时,都需要重新打包。这显然很麻烦,webpack也考虑到这一点,因此提供了devServer的功能。

    1.安装devServer

    开发服务器 devServer:用于自动化(自动编译,自动打开浏览器,自动刷新浏览器);
    特点:只会在内存中编译打包,不会有任何输出;
    启动devServer指令为:webpack-dev-server

    首先,需要在根目录下载该包,同样采用开发时依赖:

    npm i webpack-dev-server -D
    

    2.配置devServer

    webpack.config.js中配置devServer,它与五个基本配置是同一等级的:

      devServer: {
        //代表运行时的目录(打包后目录),也是使用字符串拼接绝对目录
        contentBase: resolve(__dirname, 'build'),
        //该参数表示启动gzip压缩
        compress: true,
        //端口号
    	port: 8081,
        //自动打开浏览器
        open: true
      }
    

    3.开启devServer

    开启时添加npx,找到该指令:

     npx  webpack-dev-server
    

    成功运行后,可通过http://localhost:8081端口查看运行结果:

    image-20200420183602040

    能够成功显示:

    image-20200420183634936

    此时只要改变src目录下的文件,都会进行自动编译。这样不用频繁输入webpack重新打包就可以实时看到变化了。

    一旦开启devServer它就会一直运行,可以通过ctrl+ c关闭它:

    image-20200420184240051

    也可以通过改变参数portopen来设置端口和是否自动打开浏览器,注意:只要重新配置了webpack.config.js就需要重启devServer

    npx webpack-dev-server
    

    我们将打包输出目录build下的文件都删除,再次运行上述指令。打包过后,build目录下并不会生成任何文件。这就验证了:devServer只会在内存中编译打包,不会有任何输出的特点。

    十一.开发环境的基本配置

    通过前面知识的学习,我们学会了打包样式资源,html资源,图片资源和其他资源。以及学会了通过devServer开启热更新。现在我们便可以开始配置基本的开发环境了;

    1.开发环境基本配置

    webpack.config.js的配置如下:

    /**
     * 开发环境配置
     */
    
    const {resolve} = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports ={
      entry: './src/index.js',
      output: {
        filename: 'built.js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          //loader的配置
          //处理less资源
          {
            test: /.less$/,
            use: ['style-loader', 'css-loader', 'less-loader']
          },
          //处理css资源
          {
            test: /.css$/,
            use: ['style-loader', 'css-loader']
          },
          //处理图片资源
          {
            test: /.(jpg|png|gif)$/,
            loader: 'url-loader',
            options: {
              limit: 8 * 1024,
              name: '[hash:10].[ext]',
              // esModule: false
            }
          },
          //处理html中图片的引入
          {
            test: /.html$/,
            loader: 'html-loader'
          },
          //处理其他资源
          {
            exclude: /.(html|js|css|less|jpg|png|gif)/,
            loader: 'file-loader',
            options: {
              name: '[hash:10].[ext]'
            }
          }
        ]
      },
      plugins: [
        //plugins的配置
        new HtmlWebpackPlugin({
          template: './src/index.html'
        })
      ],
      devServer: {
        contentBase: resolve(__dirname, 'build'),
        compress: true,
        port: 3000,
        open: true
      }
    }
    

    2.优化输出目录

    首先我们整理一下src目录下的文件,并修改webpack.config.js中的路径,随后使用webpack打包。打包出来的build目录下的文件还是很乱:

    image-20200420222255871

    如果想要打包出来的build目录下的文件能与src目录结构相同呢?

    可以给webpack.config.js中的output中的filename添加前缀,这样打包过后的文件会自动创建这个指定的目录:

    image-20200420222703680

    在处理图片时,只需要添加outputPath属性就能指定打包后的目录结构了:

    image-20200420222827141

    重新打包,图片文件被打包进了imgs目录:

    image-20200420222859942

    同理,可以在处理其他资源的loaderoption属性中添加outputPath属性指定打包后的目录结构:

    image-20200420223037933

    image-20200420223114609

    注意了,主要的打包输出目录是由五大配置之一的output中的path属性决定的,上面的这一属性为build。所以,之后使用outputPath指定的路径都要拼接在build后面。最后形成完整的路径。

    十二.单独打包CSS文件

    从现在开始我们将部署生成坏境;

    常见错误:

     ERROR in Entry module not found: Error: Can't resolve 'D:webpack59-提取css成单独文件srcindex.html' in 'D:webpack59-提取css成单独文件'
    

    上面的这种错误大部分都是由于相关文件路径错误导致的。

    1.安装mini-css-extract-plugin

    可通过以下指令安装mini-css-extract-plugin插件,提取打包文件built.js中的css文件:

    npm i mini-css-extract-plugin -D
    
    • 第一步:为了使用该插件:首先引入该插件:
    const miniCssExtractPlugin = require('mini-css-extract-plugin')
    
    • 第二步:然后在plugins属性中通过创建实例的方式使用:
    plugins: [
        new miniCssExtractPlugin()
      ],
    
    • 第三步:配合相关的loader
    rules: [
          {
            test: /.css$/,
            use: [
              // 'style-loader':创建style标签,将样式放入
              //使用以下loader取代style-loader,作用:提取js中的css成单独文件
              miniCssExtractPlugin.loader,
              //将css文件整合到js文件中
              'css-loader',
            ]
          }
        ]
    

    2.单独打包css文件

    完成配置后,再次执行webpack打包文件,没有报错,查看生成的打包输出文件目录build,发现其中多了一个main.css文件:

    image-20200420233903378

    这个文件就是提取出来的css文件;

    也可以在第二步中传入参数进行一些配置:比如对输出路径和文件进行命名:

    plugins: [
        new miniCssExtractPlugin({
          //对输出的css文件进行重命名
          filename: 'css/built.css'
        })
      ],
    

    随后,删除原有打包出来的build目录,再次执行webpack,重新生成打包目录build

    image-20200420234306499

    可以看到,成功地设置了css文件存放的目录和文件名。

    3.优点

    这种做法的好处是:

    • 提取后的css文件是通过link标签引入的,这样就避免了闪屏现象:

      image-20200420234514916

    • 并且css文件与js文件分离开了,所以js文件体积缩小了,解析速度也会更好一些。

    十三.CSS兼容性处理

    css3新增了许多的样式属性,但是并不是每一个浏览器都完全支持了这些样式属性,因此我们需要对某些样式属性进行兼容性处理,通过webpack可以很容易地实现这一点;

    需要使用postcss-loaderpostcss-preset-env插件;

    这个插件能帮助postcss识别某些环境,而加载指定的配置,从而使兼容性做到精确到某一个浏览器版本;

    1.安装依赖

    首先,全局下载这个两包,依旧是开发时依赖:

     npm i postcss-loader postcss-preset-env -D
    

    2.基本配置

    配置有两种写法:

    • 第一种:使用loader默认配置。直接写:

      'postcss-loader'
      

      这种方法不能修改配置,如果想要修改配置,采用第二种写法;

    • 第二种:修改配置。写成对象的形式,在options属性中修改配置:

      {
          loader: 'postcss-loader',
              options: {
                  //固定写法
                  ident: 'postcss',
                      plugins: () => [
                          //postcss的插件
                          require('postcss-preset-env')()
                      ]
              }
      }
      

    该插件的作用为:帮助postcss找到package.jsonbrowserslist里面的配置,通过配置加载指定的css兼容性样式。

    此时需要在根目录的package.json中添加相关配置:

    image-20200421161330156

    在其中添加下列配置:

      "browserslist": {
          //这是开发环境
        "development": [
          "last 1 chrome version",
          "last 1 firefox version",
          "last 1 safari version"
        ],
          //生产环境,
        "production": [
          ">0.2%",
          "not dead",
          "not op_mini all"
        ]
      }
    

    browserslist对象中可以写两个参数:

    • development代表开发环境的配置,值为数组;
    • production代表生产环境的配置;

    关于browserslist的配置github上又很详尽的介绍。

    "last 1 chrome version"代表兼容chrome最近的一个版本;其他浏览器也是这个格式。如果是开发模式,我们不需要配置太多浏览器,只需要配置调试用到的浏览器就够了;

    但是,生产环境就要多写一点;他们表示:

    //表示兼容大于99.8%的浏览器
    ">0.2%",
    //不要已经死的浏览器,比如IE10
    "not dead",
    //还有所有的op_mini浏览器
    "not op_mini all"
    

    这样就做到了绝大多数浏览器的兼容了。

    打包时,默认看生产环境,与webpack.config.js中的mode是没关系的;如果想要使用开发环境,需要设置node环境变量:

    process.env.NODE_ENV = 'development'
    

    打包前,webpack的配置是这样的:

    const { resolve } = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    const miniCssExtractPlugin = require('mini-css-extract-plugin')
    
    //设置nodejs环境变量
    process.env.NODE_ENV = 'development'
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/built.js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          {
            test: /.css$/,
            use: [
              miniCssExtractPlugin.loader,
              'css-loader',
              /**
               * css兼容性处理:postcss --> postcss-loader postcss-preset-env
               */
              {
                loader: 'postcss-loader',
                options: {
                  //固定写法
                  ident: 'postcss',
                  plugins: () => [
                    //postcss的插件
                    require('postcss-preset-env')()
                  ]
                }
              }
            ]
          }
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        }),
        new miniCssExtractPlugin({
          filename: 'css/built.css'
        })
      ],
      mode: 'development'
    }
    

    打包前,我们在css文件中添加两个有兼容性问题的新样式:

    image-20200421162801958

    使用webpack打包后,打包后的样式发生了变化:

    image-20200421162858192

    说明兼容性处理成功了。

    这样,我们就能专心使用各种样式,而不需要考虑兼容性问题了,webpack会自动帮我们做好这方面的工作。

    十四.压缩CSS

    这节介绍使用插件来完成css的压缩,可以发现有的时候使用loader,有的时候使用插件;主要的区别在于:loader处理的东西比较少于专一,插件处理的东西比较大;

    1.下载插件

    使用的插件为:optimize-css-assets-webpack-plugin,首先全局下载:

    npm i optimize-css-assets-webpack-plugin -D
    

    使用插件的时候,要先在webpack.config.js中引入:

    const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
    

    2.配置插件

    然后在plugins中使用:

      plugins: [
        //压缩css
        new OptimizeCssAssetsWebpackPlugin()
      ]
    

    只要引用就可以了,不需要额外配置,按照默认的配置已经可以将css文件压缩了。

    然后使用webpack打包,输出的css文件被压缩成了一行:

    image-20200421164202118

    在样式较多的情况下,压缩是很有必要的。能够请求速度更快,加载样式更快,这样就能更快地渲染,用户体验也就更好。

    十五.js语法检查

    1.安装依赖

    语法检查,不仅可以检查语法错误,还可以设置代码风格,让整个团队的代码风格都保持一致;专门做语法检查的如eslint-loader它依赖于eslint库,所以首先要在全局下载这两个包:

    npm i eslint-loader eslint -D
    

    2.配置eslint

    注意:语法检查只会检查自己写的代码,第三方库不会也不用检查;所以需要使用exclude来排除第三方库:

        rules: [
          {
            test: /.js$/,
            loader: 'eslint-loader',
            exclude: /node_modules/,
            options: {}
          }
        ]
    

    此外还需要我们在package.json中的eslintConfig中配置检查规则。关于规则,推荐使用airbnb。在github上的这个项目会详细告诉你,应该如何配置这些规则:

    image-20200421174912249

    npm上有两个将airbnb规则应用到eslint中的包:

    • 不包含React相关内容的插件也分为两种:
      • eslint-config-airbnb-base:包含ES6及以上的内容;开发中一般都会使用ES6语法,所以使用上面这个插件;
      • eslint-config-airbnb-base/legacy:包含ES5ES5以下;
    • eslint-config-airbnb:包含了React的内容;

    所以,我们选择使用eslint-config-airbnb-base,注意,该插件依赖于eslinteslint-plugin-import两个包;上面已经下载过eslint了,这里只需要下载其他两个包:

     npm i eslint-config-airbnb-base eslint-plugin-import -D
    

    package.json中的具体配置为:

      "eslintConfig": {
        //通过extends字段继承airbnb-base就可以了
        "extends": "airbnb-base"
      }
    

    随后在入口文件index.js中故意写一写不规范的代码:不加空格,分号等:

    image-20200421172314880

    然后执行webpack指令,进行打包,发现出现了很多错误:

    image-20200421173740725

    虽然可以根据错误提示,在airbnb文档中查找修改意见。但是手动修改仍然显得麻烦,可以在webpack.config.jseslint-loader中的options里添加fix属性配置,让它自动修复:

        rules: [
          {
            test: /.js$/,
            loader: 'eslint-loader',
            exclude: /node_modules/,
            options: {
                //自动修复eslint的错误
                fix: true
            }
          }
        ]
    

    再次打包,通过自动修复就不会再显示错误信息了:

    image-20200421173943783

    可以看到,虽然没有显示错误,但是有一个警告:不建议使用console;在开发中可以使用console进行调试,但是真正的代码中不应该含有它;此时可以通过添加下列注释,让eslint忽略下一行代码:

    //下一行eslint所有规则都失效(即下一个行不进行eslint检查)
    //eslint-disable-next-line
    console.log('123')
    

    一个坑:安装包的时候,最好按照依赖关系来安装;即首先安装下层的包,再安装上层的包,否则可能会出现意想不到的错误;可以通过查看package.json中的配置来观察相应的包是否成功安装了;

    十六.js兼容性处理

    我们将入口文件index.js内容改为箭头函数:

    image-20200421201636265

    直接执行webpack打包,打包出来的文件中,该部分并没有做兼容性处理:

    image-20200421201727497

    这样的话,不支持ES6的浏览器就会出错;这时候就需要使用babel-loader来进行js的兼容性处理了;

    1.基本配置

    webpack.config.js中的配置为:

        rules: [
          /**
           * js兼容性处理:babel-loader @babel/preset-env @babel/core
           */
          {
            test: /.js/,
            //排除不需要js兼容性处理的文件
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              //预设:指示babel做怎样的兼容性处理,一般使用@babel/preset-env就可以了
              presets: ['@babel/preset-env']
            }
          }
        ]
    

    注意:凡是涉及到兼容性处理都要使用exclude将不需要进行兼容性处理的文件排除;否则会出错,如下图所示,不排除的话,依赖文件node_module中的js都报错了:

    image-20200421202655695

    2.使用babel-loader

    方法一:使用babel-loader进行基本的兼容性处理;

    首先需要在全局下载babel-loader@babel/preset-env@babel/core

    npm i babel-loader @babel/preset-env @babel/core -D  
    

    随后再次执行webpack打包,这次打包出来的js文件中,ES6的语法全都转换为了ES5的语法了,也就是做了兼容性处理:

    image-20200421203218511

    babel-loader存在的问题

    只能转换一些基本语法,如不能转换promise;比如在入口文件index.js中添加Promise对象:

    image-20200422100442319

    直接打包是不会被兼容性处理的。

    解决方案:通过@babel/polyfill对全部js进行兼容性处理;

    3.使用@babel.polyfill

    方法二:使用@babel.polyfill,对全部js进行兼容性处理;

    首先还是全局安装@babel/polyfill

    npm i @babel/polyfill -D
    

    它不是laoder或插件,只需要在入口文件index.js中通过import引入就可以使用了:

    import '@babel/polyfill';
    

    此时再次执行webpack打包,会发现打包出来的js文件变得非常大:

    引入@babel/polyfill前:built.js只有不到4KB

    image-20200421204538934

    引入后,变成了441KB

    image-20200421204639846

    这是因为只要使用了@babel/polyfill,那么它就会将js文件涉及到的所有兼容性问题都解决掉。

    此时入口文件中的Promise就被兼容性处理了:

    image-20200421204947328

    @babel/polyfill存在的问题

    我只要解决部分兼容性问题,但是将所有兼容性代码全部引入,体积太大了;

    解决方法:需要做兼容性处理就做:按需加载(只加载指定的兼容性库),通过core-js实现;

    4.使用core-js

    方法三:使用core-js,实现按需加载。

    首先全局下载core-js

     npm i core-js -D
    

    随后在webpack.config.js中进行相应的配置:

    rules: [
          /**
           * js兼容性处理:babel-loader
           */
          {
            test: /.js/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              //预设:指示babel做怎样的兼容性处理,一般使用@babel/preset-env就可以了
              presets: [
                [
                  '@babel/preset-env',
                  {
                    //按需加载
                    useBuiltIns: 'usage',
                    //指定corejs版本
                    corejs: {
                      version: 3
                    },
                    //指定兼容性做到那个版本浏览器
                    targets: {
                      chrome: '60',
                      firefox: '60',
                      ie: '9',
                      safari: '10',
                      edge: '17'
                    }
                  }
                ]
              ]
            }
          }
        ]
    

    需要注意的是,使用第三种方法就不能使用第二种方法了,所以要在入口文件index.js中不再引入@babel/polyfill

    image-20200422101227194

    随后再次执行webpack进行打包,会发现,打包出来的js文件相比于使用第二种方法时的441KB,缩减到了104KB

    image-20200422101506246

    结合第一种和第三种方法就能实现对所有的js代码进行兼容性处理;

    5.完整配置

    最后整个webpack.config.js的配置如下:

    const { resolve } = require('path')
    const HtmlWebpackPlugin = require('html-webpack-plugin')
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/built.js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          /**
           * js兼容性处理:babel-loader
           */
          {
            test: /.js/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              //预设:指示babel做怎样的兼容性处理,一般使用@babel/preset-env就可以了
              presets: [
                [
                  '@babel/preset-env',
                  {
                    //按需加载
                    useBuiltIns: 'usage',
                    //指定corejs版本
                    corejs: {
                      version: 3
                    },
                    //指定兼容性做到那个版本浏览器
                    targets: {
                      chrome: '60',
                      firefox: '60',
                      ie: '9',
                      safari: '10',
                      edge: '17'
                    }
                  }
                ]
              ]
            }
          }
        ]
      },
      plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html'
        })
      ],
      mode: 'development'
    }
    
    

    十七.压缩js和html

    1.压缩js代码

    只需要就webpack.config.js中的模式mode改为生产模式,就会自动压缩js代码:

    mode: 'production'
    

    内部实现是通过插件UglifyJsPlugin

    执行webpack进行打包,打包出来的js文件被压缩成了一行:

    image-20200422102256861

    不需要做html的兼容性处理,因为标签认识就认识,不认识就不认识,不能转换;只需要对html进行压缩处理即可;

    2.压缩html代码

    只需要在配置文件webpack.config.js中给HtmlWebpackPlugin插件添加minify属性即可:

    plugins: [
        new HtmlWebpackPlugin({
          template: './src/index.html',
          //压缩html代码
          minify: {
            //移除空格
            collapseWhitespace: true,
            //移除注释
            removeComments: true
          }
        })
      ],
    

    执行webpack进行打包,打包出来的html文件被去除了所有空格并移除了注释:

    image-20200422102912002

    十八.生产环境基本配置

    学习了前面的基本配置,现在可以汇总起来配置基本的生产环境了。

    1.配置的复用

    如相同的配置可以抽离出来封装一个复用的loader,比如cssless的兼容性处理,唯一的不同点是多了个less-loaderless转换为css,所以其余部分可以复用;

    未复用前,存在大量的重复代码:

        rules: [
          //处理css文件
          {
            test: /.css$/,
            use: [
              MiniCssExtractPlugin.loader,
              'css-loader',
              //css兼容性处理
              {
                //还需要在webpack.json中定义browserslist
                loader: 'postcss-loader',
                options: {
                  ident: 'postcss',
                  //指定插件
                  plugins: () => [
                    require('postcss-preset-env')()
                  ]
                }
              }
            ]
          },
          //处理less文件
          {
            test: /.less$/,
            use: [
              MiniCssExtractPlugin.loader,
              'css-loader',
              //css兼容性处理
              {
                //还需要在webpack.json中定义browserslist
                loader: 'postcss-loader',
                options: {
                  ident: 'postcss',
                  //指定插件
                  plugins: () => [
                    require('postcss-preset-env')()
                  ]
                }
              },
              //由于use数组执行顺序为从下往上(注意执行顺序),经过less-loader转换为css后再进行兼容性处理
              'less-loader'
            ]
          }
        ]
    

    复用后:

    //复用loader
    const commonCssLoader = [
      MiniCssExtractPlugin.loader,
      'css-loader',
      //css兼容性处理
      {
        //还需要在webpack.json中定义browserslist
        loader: 'postcss-loader',
        options: {
          ident: 'postcss',
          //指定插件
          plugins: () => [
            require('postcss-preset-env')()
          ]
        }
      }
    ]    
    //...
        rules: [
          //处理css文件
          {
            test: /.css$/,
            //通过扩展运算符使用封装的loader
            use: [...commonCssLoader]
          },
          //处理less文件
          {
            test: /.less$/,
            //由于use数组执行顺序为从下往上(注意执行顺序),经过less-loader转换为css后再进行兼容性处理
            use: [...commonCssLoader,'less-loader']
          }
        ]
    //...
    

    所以,当遇到重复代码的时候一定要考虑将重复代码抽离封装,达到复用的效果;

    2.生产环境基本配置

    完整的webpack.config.js配置如下:

    //0.引入path模块解决路径问题
    const { resolve } = require('path');
    //1.引入插件提取和兼容性处理css文件
    const MiniCssExtractPlugin = require('mini-css-extract-plugin');
    //2.引入压缩css插件
    const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin');
    //3.引入处理html图片引入的插件
    const HtmlWebpackPlugin = require('html-webpack-plugin');
    
    //复用loader
    const commonCssLoader = [
      //这一行作用为将css文件抽离出来
      MiniCssExtractPlugin.loader,
      'css-loader',
      //css兼容性处理
      {
        //还需要在webpack.json中定义browserslist
        loader: 'postcss-loader',
        options: {
          ident: 'postcss',
          //指定插件
          plugins: () => [
            require('postcss-preset-env')()
          ]
        }
      }
    ]
    
    //package.json中的browserslist默认使用开发环境,若使用生产环境需要定义nodejs环境变量
    process.env.NODE_ENV = 'production';
    
    module.exports = {
      entry: './src/js/index.js',
      output: {
        filename: 'js/built.js',
        path: resolve(__dirname, 'build')
      },
      module: {
        rules: [
          //1.处理css文件
          {
            test: /.css$/,
            //通过扩展运算符使用封装的loader
            use: [...commonCssLoader]
          },
          //2.处理less文件
          {
            test: /.less$/,
            //由于use数组执行顺序为从下往上(注意执行顺序),经过less-loader转换为css后再进行兼容性处理
            use: [...commonCssLoader,'less-loader']
          },
    /**
     * 正常来说:一个文件只能被一个loader处理
     * 当一个文件要被多个loader处理时,那么一定要指定loader的执行顺序。
     * 比如先执行eslint-loader,再执行babel-loader。这是因为一旦语法出错进行兼容性处理就没意义了。
     * 如何添加顺序:enforce: 'pre'
     */
    
          //3.进行语法检查
          {
            //在package.json中配置eslintConfig指定检查规则 --> airbnb
            test: /.js$/,
            //配出不需要语法检查的文件
            exclude: /node_modules/,
            //优先执行
            enforce: 'pre',
            loader: 'eslint-loader',
            options: {
              //自动修复错误
              fix: true
            }
          },
          //4.js兼容性处理
          {
            test: /.js$/,
            exclude: /node_modules/,
            loader: 'babel-loader',
            options: {
              //添加预设,告诉babel以哪种方式进行兼容性处理
              presets: [
                //由于要使用方法一和三,所以使用数组保存
                [
                  //简单处理
                  '@babel/preset-env',
                  //按需加载
                  {
                    useBuiltIns: 'usage',
                    //指定corejs版本
                    corejs: {version: 3},
                    //指定浏览器版本
                    targets: {
                      chrome: '60',
                      firefox: '50'
                    }
                  }
                ]
              ]
            }
          },
          //5.处理图片
          {
            test: /.(jpg|png|gif)/,
            loader: 'url-loader',
            options: {
              //通过base64编码优化
              limit: 8 * 1024,
              //重命名打包后的图片
              name: '[hash:10].[ext]',
              //指定输出路径
              outputPath: 'imgs'
            }
          },
          //6.处理html中的图片
          {
            test: /.html$/,
            loader: 'html-loader',
          },
          //8.处理其他文件
          {
            //排除其他文件
            //正则中不加$表示只要匹配到这些词就行,是不是后缀都可以
            exclude: /.(js|css|less|html|jpg|png|gif)/,
            //原封不动地输出文件
            loader: 'file-loader',
            options: {
              outputPath: 'media'
            }
          }
        ]
      },
      plugins: [
        //兼容性处理css并单独抽离css文件
        new MiniCssExtractPlugin({
          //设置输出路径
          filename: 'css/built.css',
        }),
        //压缩css
        new OptimizeCssAssetsWebpackPlugin(),
        new HtmlWebpackPlugin({
          //指定html模板
          template: './src/index.html',
          //7.压缩html文件
          minify: {
            //移除空格
            collapseWhitespace: true,
            //移除注释
            removeComments: true
          }
        })
      ],
      //改为production模式自动压缩js文件
      mode: 'production'
    }
    
  • 相关阅读:
    常用的Dos命令
    关于CSS3
    数据渲染
    jQuery中的AJAX
    AJAX
    面向对象3
    克隆对象、对象继承
    面向对象2
    面向对象1
    面向对象
  • 原文地址:https://www.cnblogs.com/AhuntSun-blog/p/13612434.html
Copyright © 2011-2022 走看看