webpack

336 阅读12分钟

webpack是什么

webpack是自动打包部署工具

优点:

  • 可以把css、js按照依赖关系,打包合并成一个(压缩)
  • Eslint词法检测(检测语法是否符合规则)
  • 小图转为base64格式编码
  • 想用webpack必须要安装nodejs,webpack的安装和使用,是基于npm包管理器来完成的

缺点:

  • webpack打包(可以看成一个平台),需要下载各种插件,插件越多,打包速度会变慢

模块化开发

什么是模块化,模块化的发展史?

B.js文件里面使用了A.js的方法,则说明B.js需要依赖于A.js才能完成功能,存在依赖关系

单例设计模式:最早的模块化,但是对引入文件的顺序要求严格:比如B.js依赖于A.js,则必须先引入A.js再引入B.js

  • 缺点:虽然解决了模块化开发,防止了全局变量污染,但是依赖关系把数据处理变得异常恶心

AMD[require.js]:AMD思想是在单例模式的基础上,实现了对依赖的管控。

  • 应用:grunt/glup/fis都是基于AMD思想实现的
  • 管控模式(缺点):依赖前置,在具体开发模块代码之前,先把需要的依赖处理好,再开始开发
  • 用法:使用define导出,使用require导入。
//使用之前需要导入require.min.js包
//A.js
    //用define定义导出的对象,变量名是A,导入的时候用A
    //如果这个模块不需要导入其他模块,可以直接写函数
let A=define(function(){
    let sum=function sum(...parmas){
        return parmas.reduce((res,item)=>res+item);
    }
    return {sum};
})

//B.js
    //当需要导入其他模块时,
        //第一个参数传想要导入的模块名数组
        //第二个参数是当前模块的函数代码
let B=define(["A"],function(A){
    const average=function average(...parmas){
        parmas.sort((a,b)=>a-b).pop();
        parmas.shift();
        return A.sum(...parmas)/parmas.length;
    };
    return  {average} ;
})

//main.js
    //配置依赖模块的导入公共路径
require.config({
    baseUrl: "./lib"
})
//require用于导入,
    //第一个参数是要导入的模块,不分先后顺序
    //第二个参数是本身的函数
require(["A", "B"], function (A, B) {
    console.log(A.sum(1, 23, 2, 4, 53, 8));
    console.log(B.average(1, 23, 2, 4, 53, 8));
})
//最后只需要在html文件中带入main文件即可

Common.js:应用在服务器端[node]的模块化

  • 缺点:不能在服务器端使用
  • 应用:node/webpack支持CommonJS规范
  • 用法:导出用module.exports,导入模块用require
//导出:可以导出函数或对象
    // 直接导入一个函数
   module.exports= function sum(...params){

       return params.reduce((result,item,index)=>{
           return result+item;
       })
   }
   //导出一个对象
   module.exports={
       sum
   }


//导入:导入目标路径必须以./开头,文件后缀可以省略
   //如果目标模块导出的是一个函数,则可以直接导入那个函数
   let suma=require("./B.js");//xxx.js后缀可以省略
   //如果目标模块导出的是一个对象,则可以直接导入这个对象
   let B=require("./B");//xxx.js后缀可以省略
   let {suma,sumb}=require("./B");//可以对B直接解构赋值

CMD[sea.js]:应用在浏览器端的模块化,旨在让Common.js能够在浏览器运行,但后期被webpack干掉了

ES6Module:ES6新增的模块化,主要应用在浏览器端的模块化

  • 优点:我们以后在浏览器端模块化开发时,都会用ES6Module开发
  • 注意点:我们在html文件中导入模块时必须声明==type="module":==<script type="module" src="./A.js"></script>
  • 用法:用export/export default导出模块,用import导入模块,一个模块可以导入无数次
//export default导出,import导入:export default只能用一次
//A.js

	let numa="111";
	//export default 写法
    	// export default:导出模块中的一个默认值:(可以是任何类型数据)每个模块只能用一次
    	//模块导出的实际是一个Module对象,我们export default的值,会存在Module对象中的default属性
    	export default numa;

//B.js
    //导入时文件路径必须由./开头,且不能省略文件后缀.js

	//导入A模块export default的值
    	//导入A.js中export default导出的默认值,且把值赋给num
    	//我们这样的写法,A模块导出的Module对象,会把它的default属性值赋值给num

   	 	import num from "./A.js";
       //因为我们接收的不是module对象而是default的值,所以不能直接解构赋值,需要再通过num结构赋值
        let {na,nb}=num;
//export 导出,import导入:export能用无数次
//A.js
    let numb="222";
    let numc="333";
    let obj1={name:"sjh",age:18}
	//export 写法
    	//方式一:导出一个变量,需要在export后定义赋值,不能在外面定义再只传变量名
   	 	export let nume="444";
    	//方式二:通过花括号的,可以同时导出多个已经定义的变量
        //这种导出方式实质上是给Module对象赋值,将numb/numc/obj1赋值给Module,变为Module的私有对象
   		 export {numb,numc,obj1};
//B.js
	 //将A模块导出的整个Module对象导入,拿到对象后就能通过A.xxx调用其内部的属性了    
    	import * as A from "./A.js";
        	console.log(A.numb);//"222"
    	//因为导入的是一个Module对象,所以我们可以对这个对象进行解构赋值,获取其中的属性,因为default是一个内置私有属性,所以不能导出defalut
        import { numb,numc,obj1 } from "./A.js";
        	console.log(numb);//"222"

webpack使用

webpack主要构成是什么?

webpack是一个基于nodejs实现的平台

  • 存在入口和出口:入口:从哪个文件开始打包编译;出口:==打包的文件放到哪里
  • mode:模式,存在生产环境(production)(自动压缩)和开发环境(development)(不压缩)
  • loader:加载器,编译用的,(webpack只能识别Es5的js文件),如果是es6的js文件、css、图片等文件,则无法识别,我们需要安装插件使它能自动编译成webpack能识别的代码格式
  • plugins:插件,打包压缩文件
  • 优化项…

webpack支持什么格式的规范?

webpack即支持ES6Module,又支持CommonJS规范,而且两个规范可以混用

wepack如何使用?

  • 我们需要把配置写在项目的webpack.config.js文件中,可配置:entry/output/mode/loader/plugins/优化项...
  • 执行webpack命令,配置好的内容会进行一系列的处理:文件编译、打包(合并)、压缩…

webpack的安装方式?

方式一:将webpack安装到全局

$ npm i webpack webpack-cli -g / npm i webpack@5.49.0 webpack-cli@4.7.2 -g

  • 优点:所有的项目不用再单独安装webpack包,都能在文件夹下的命令行窗口使用webpack命令执行打包
  • 缺点:所有项目使用的webpack版本一样,容易引起版本冲突

方式二:想给哪个文件夹打包,就给哪个文件夹安装webpack

$ npm i webpack webpack-cli// npm i webpack@5.49.0 webpack-cli@4.7.2

  • 优点:每个项目都有自己的webpack版本号,互不影响,也是我们做项目的时候真正用的安装方式
  • 不能在自己的命令行窗口,用webpack命令执行打包,如果想用,有两种方式:
    • 如果wepack版本在5.2以上:使用npx webpack不用配置命令就可以打包
    • 也可以在package.json中的"scripts"属性下配置"xxx":"webpack","xxx"是自定义名,然后在命令窗口中执行npm run xxx来实现打包

webpack的默认打包?

wepack有默认的打包路径:我们不需要配置webpack.config.js,就可以打包

  • 默认入口是:文件夹/src/index.js
  • 默认出口是:文件夹/dist/main.js(如果没有,会自动生成)

webpack的打包命令?

  • 使用全局的webpack:直接在命令行窗口执行webpack
  • 使用自己项目中的webpack:
    • 需要在package.json中的"scripts"属性下配置"xxx":"webpack","xxx"是自定义名,然后在命令窗口中执行npm run xxx来实现打包;
    • 或者版本在5.2以上,直接使用npx webpack打包

执行webpack打包命令触发的查找机制?

当我们运行 npm run serve,会把webpack命令执行

  • @1 首先到项目的node_modules中的.bin的目录中查找
  • @2找到webpack这个文件,按照这个文件开始执行打包编译

本地项目中可以使用的命令,都按照这个文件开始执行打包编译

webpack使用步骤

webpack在项目中配置的步骤?

第一步:删除全局的webpack:npm uninstall webpack webpack-cli -g

第二步:安装局部的webpack:npm i webpack webpack-cli --save-dev

第三步:检查项目目录中是否存在src路径,src中是否存在index.js目录

第四步:在package.json中的"scripts"属性下配置"xxx":"webpack","xxx"是自定义名,然后在命令窗口中执行npm run xxx来实现打包【使用默认打包方式:默认打包index.js ,默认生成在:dist/main.js】

第五步:配置webpack.config.js,实现自定义的控制webpack打包配置

webpack配置文件

webpack的配置文件有三种形式: webpack.config.js:wepack默认寻找的配置文件,但是我们一般会把它拆分为两个模式文件:production和development 脚本命令:"dev":"webpack --config webpack.min.js"可简写为:"dev":"webpack"

webpack.config.production.js:专门执行webpack生产环境的配置文件 脚本命令:"build":"webpack --config webpack.config.production.js"

webpack.config.development.js:专门执行webpack开发环境的配置文件 脚本命令:"build":"webpack --config webpack.config.development.js"

如何在一个webpack.config.js 文件下实现执行不同的环境(生产或开发)?

我们在项目开发一般创建开发环境配置文件与生产环境配置文件两个配置文件,但是如何实现两个环境放在同一个配置文件中,且能根据命令分环境执行命令内容呢?

第一步:需要引入cross-env依赖:npm i cross-env --save-dev 第二步:通过配置package.json的scrpit脚本命令,实现执行不同的环境

  • "D":"cross-env Node_ENV=development webpack":执行开发环境
  • "P":"cross-env Node_ENV=producton webpack":执行生产环境

第三步:在webpack中声明一个变量let NODE_ENV=process.env.NODE_ENV||'development'

  • process.env.NODE_ENV:是通过脚本命令中的cross-env xxx传过来的值,如果传过来的值是undefined,则默认赋值development环境
  • mode:NODE_ENV:环境根据变量来决定
  • filename:NODE_ENV==='development'?"bundle.[hash].js":"bundle.[hash].min.js"

第四步:运行:

  • npm run D:执行开发环境
  • npm run P:执行生产环境
  • npm run dev:因为没有传NODE_ENV变量值,所以默认执行开发环境

wepack.config.js配置项

webpack在node环境下执行,所以对于模块化的导入和导出,需要遵循node.js规范

  • 导入:let path=require("path")
  • 导出:module.exports={key:value}

导出项内可以配置很多配置项,接下来就分类描述一下各配置项的功能:

mode

mode(模式):表示我们的webpack.config.js文件是在种模式下运行的,存在开发者模式和生产者模式两种

  • 开发者模式:mode:"development",特点->不会自动压缩打包
  • 生产者模式:mode:"production",特点->会自动压缩打包的文件
module.exports={
    //mode:模式,有两种:"production"(生产者模式,默认压缩),"development"(开发者环境,不会压缩)
    //使用:生产者模式(自动压缩)
    mode:"production",
    }
}

entry

entry(入口):表示我们打算把哪个js文件作为打包的入口,就是我们想从哪个文件开始打包,指定一个文件的字符串路径。

webpack从指定的入口触发,会沿着入口文件寻找所有依赖的模块,并导入,最后组合形成一个js文件

output

output(出口):打包完成后导出的文件,可以配置文件存放路径以及文件名

devServer

插件:webpack-dev-server webpack.js.org/configurati…

devServer(development Server开发环境服务):表示开发环境下的配置项,如果不写有默认配置,但我们会通过配置项来实现开发环境下的一些功能

配置步骤:

  • 第一步:下载依赖包:$ npm install webpack-dev-server -save-dev

  • 第二步:在webpack.config.js文件下对devServer数据项进行配置,不用导入

/* webpack.config.js */
//=>配置DEV-SERVER
devServer: {
    //开启服务的主机IP地址
    host: '127.0.0.1',
    //=>端口
    port: 3000,
    //=>开启服务器端GZIP压缩
    compress:true,
    //=>显示编译进度
    client:{
       progress: true 
    },
    
    //=>自动打开浏览器
    open: true,
    //=>开启热更新
    hot:true,
    //=>指定访问资源目录
    contentBase: path.resolve(__dirname, "dist"),//旧版本的,可能不生效
    static:{directory:path.join(__dirname),"dist"},//新版本的
    //=>请求代理
    proxy:{
       "/":{
          target:"http://localhost:8888", //请求访问的服务器地址
          secure: false, //若为true则表示是https,false是http
          changeOrigin: true //把请求头当中的host值改成服务器地址
       } 
    }
}

plugins

plugins(插件):插件是用来扩展webpack功能的,它在整个构建过程中生效,执行相关的任务 使用步骤:

  • 第一步:安装插件包:npm install xxx-webpack-plugin --save-dev

  • 第二步:在webpack.config.js中,用变量接收导入的模块:let XxxWebpackPlugin = require('xxx-webpack-plugin');

  • 第三步:在module.exports的plugins数组中new对应变量的对象:

  • 第四步:在new的对象中根据插件提供的参数配置插件的属性:

//=>在webpack的module.exports中配置插件
plugins: [
    new HtmlWebpackPlugin({
    //在这里可以对插件进行配置
 	})
]

plugins与loader的区别?

plugins:插件是用来扩展webpack功能的,它们在整个构建过程中生效,执行相关任务

loaders:加载器是在打包构建过程中,用来处理源文件的(JSX/less/png),一次处理一个文件

webpack只认识js文件,所以其他格式的文件它无法直接打包,而loader就是用来将我们上传的不同格式文件通过加载器将它编译为webpack认识的格式。

plugin并不直接操作单个文件,它直接对整个构建过程起作用

插件的两种配置形式?

插件其实有两种:单一功能的插件和多功能插件

  • 单一功能插件:插件只存在优化功能,无需任何配置,这样的插件在optimization中的minimizer中new创建
  • 多功能插件:我们可以通过配置项配置这个插件来实现不同的功能,这样的插件在plugins中new

html-webpack-plugin

html-webpack-plugin:专门控制html文件打包时配置的插件

  • 第一步:安装插件包:$ npm i html-webpack-plugin -–save-dev

  • 第二步:用变量接收导入插件模块:let HtmlWebpackPlugin = require('html-webpack-plugin');

  • 第三步:在plugins中new html变量对象:

  • 第四步:根据插件提供的参数配置对象:

let HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
    ...,
    //=>在webpack中使用插件
    plugins: [
        new HtmlWebpackPlugin({
            //=>指定自己的模板
            template: './src/index.html',
            //=>输出的文件名
            filename: 'index.html',
            //=>给引入的文件设置HASH戳(清除缓存的),也可以在output中设置 filename: 'bundle.[hash].js' 来生成不同的文件
            hash: true,
    		chunks:["index"],//指定html文件导入哪些js文件
            //=>minify:控制是否以及以哪种方式压缩优化再输出 
            //=>https://github.com/kangax/html-minifier
            minify: {
                collapseWhitespace: true,//去除各种空格
                removeComments: true,//去掉注释
                removeAttributeQuotes: true,//去掉标签中属性的双引号
                removeEmptyAttributes: true//去除掉标签中空属性
    			//...
            }
        }) 
    ]
}

clean-webpack-plugin

clean-webpack-plugin:每次打包之前,清除之前的打包文件

使用步骤:

  • 第一步:安装clean插件包:$ npm i clean-webpack-plugin –save-dev

  • 第二步:用变量接收导入的clean模块:const { CleanWebpackPlugin } = require('clean-webpack-plugin');,注意clean是导入模块中的一个属性,所以需要解构赋值

  • 第三步:在plugins中new clean插件的对象:

const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports={
    plugins:[
        new CleanWebpackPlugin()
    ]
};

mini-css-extract-plugin

抽离CSS内容,默认的css样式会插入到html文件的style标签中,这个插件会将css样式抽离出来形成新的css文件,并以link的方式导入到对应的html文件中

下载插件:$ npm i mini-css-extract-plugin –save-dev

const MiniCssExtractPlugin=require('mini-css-extract-plugin');
module.exports = {
    plugins: [
        //=>使用插件
        new MiniCssExtractPlugin({
            //=>设置编译后的文件名字
            filename: 'main.[hash].css'
        })
    ],
    module: {
        rules: [{
            test: /\.(css|less)$/,
            use: [
                // "style-loader",
                //=>使用插件中的LOADER代替STYLE方式
                MiniCssExtractPlugin.loader,
                "css-loader",
                "postcss-loader",
                "less-loader"
            ]
        }]
    }
}

重点:导入插件后,用MiniCssExtractPlugin.loader代替"style-loader"

loaders

loaders:加载器是在打包构建过程中,用来处理源文件的(JSX/less/png),一次处理一个文件

webpack只认识js文件,所以其他格式的文件它无法直接打包,而loader就是用来将我们上传的不同格式文件通过加载器将它编译为webpack认识的格式。

使用步骤:以处理样式为例:

  • 第一步:安装加载器包:$ npm i css-loader style-loader less-loader less postcss-loader autoprefixer –save-dev

  • 第二步:无需导入,直接在module.exports中的module的rules中配置

module.exports = {
    //=>配置模块加载器LOADER
    module: {
        //=>模块规则:使用加载器(默认从右向左执行,从下向上)
        rules: [{
            test: /\.(css|less)$/, //=>基于正则匹配哪些模块需要处理
            use: [
                "style-loader", //=>把CSS插入到HEAD中
                "css-loader", //=>编译解析@import/URL()这种语法
                "postcss-loader", //=>设置前缀
                {
                    loader: "less-loader",
                    options: {
                        //=>加载器额外的配置
                    }
                }
            ]
        }]
    }
}
  • rules:值是一个数组,数组中的每一项是一个对象,编写加载器规则(从右到左,从下到上)

  • test:/.(css|less)$/:值是一个正则表达式,表示处理以css和less格式结尾的文件``

  • use:值可以是字符串也可以是对象,表示用哪些加载器对文件进行处理,多个加载器的处理顺序是自下而上

  • 值是对象:loader表示加载器的名字,options表示对该加载器的配置

optimazition

optimazition:(优化项),用来配置专门作为打包优化的插件,这样的插件存在一个特点就是功能单一且具体,就是用来优化的,所以不需要对插件做过多的配置,只需要new即可

使用步骤:

  • 第一步:导入优化的插件:$ npm i optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin terser-webpack-plugin –save-dev

  • 第二步:用变量接收导入的模块:

  • 第三步:在module.export->optimazition->minimizer数组中new创建插件对象:

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

module.exports = {
    //=>设置优化项
    optimization: {
        //=>是否压缩
        minimize: true,
        //=>设置压缩方式
        minimizer: [
            //=>压缩CSS(但是必须指定JS的压缩方式)
            new OptimizeCssAssetsWebpackPlugin(),
            //=>压缩JS
            new TerserPlugin()
        ]
    }
};
  • minimize:设置是否压缩
  • minimizer:指定压缩的方式

webpack中图片的处理

对打包文件中存在的图片(png、jpe/svg…)根据图片格式做处理

安装需要的加载器:$ npm i file-loader url-loader html-withimg-loader –save-dev

module.exports = {
    module: {
        //=>模块规则:使用加载器(默认从右向左执行)
        rules: [{
            test: /\.(png|jpe?g|gif)$/i,
            use: [{
                //=>把指定大小内的图片BASE64
                //=>不在指定范围的采用file-loader进行处理
                loader: 'url-loader',
                options: {
                    limit: 200 * 1024,//小于200kb的图片做base64处理
                    outputPath:'/images',
                    //name:'[name].[ext]'
                }
            }],
            include: path.resolve(__dirname, 'src'),//检测图片的路径范围
            exclude: /node_modules///排除掉的路径,就是不检测的路径
        }, {
           test:/\.(svg|eot|ttf|woff|woff2)$/i,
           use:"file-loader"
        }, {
            test: /\.html$/,
            use: ['html-withimg-loader']
        }]
    }
}

基于babel实现ES6的转换

什么是babel:Babel 是一个工具链,主要用于在当前和旧浏览器或环境中将 ECMAScript 2015+ 代码转换为向后兼容的 JavaScript 版本。

基于babel实现Promise转换

基于webpack 配置规则,这个class 编译成为ES5语法,但是Promise无法编译

解决:需要在 index.js 引入

基于@babel/polyfill处理ES6内置方法的兼容「例如:promise,polyfill中自己实现了一套兼容的promise」