webpack的使用

180 阅读8分钟

引言

webpack是模块化打包工具,是由node构建的,webpack通常会将入口文件以及它的依赖还有每个分支的依赖整理打包,打包后的项目的文件数量还有文件体积会大大减少,并且会自动转化一些内容;比如把浏览器不识别的es6/es7...转化成浏览器能识别的es5

模块化

esmodule

导出:

  • export
  • export default

导入:

  • import * as xxx from "yyy.js
  • import xxx from "yyy.js"
  • import {xxx} from "yyy.js"

commJs

commJS是node的模块化规范 导出:

  • module.exports
  • exports.xxx

导入:

  • let xxx=require("yyy.js")

浏览器与模块化

无论是使用esModule,还是commJs规范实现模块化开发(在js中引入其他js暴露的内容,在当前模块使用-->一个模块就是一个js文件);如果使用了esModule,还不在引用时的 script标签中添加type="module"属性(一般浏览器不支持type属性)或者使用commJS规范实现模块化开发,在浏览器端都是不能识别的;也就是当你要使用多个js文件并且文件之间还有依赖的情况下,只能在html文件中引入多个script,但是这样会在客户端渲染页面的时候,发送多个http请求,影响请求效率;这个时候就要用到webpack打包工具,从入口文件开始,把它所有依赖的文件整合处理成一个文件

webpack的大致应用

webpack会把遇到的以下内容进行转化

  • less/sass-->css
  • typescript-->javascript
  • es6/es7...-->es5
  • 图片-->base64

webpack4.0的零配置

使用前要初始化一个packge.json,记载着包的菜单

npm init -y

然后安装webpack以及webpack-cli

npm init webpack webpack-cli -D

安装完成后,在命令窗口执行

npx webpack

npm run build(在package.json的脚本中配置build)

yarn build

全局安装的命令可以直接执行

webpack

但是一般不会这么做,因为不同项目会有冲突不建议全局安装

如果执行打包命令时没有配置webpack.config.json/webpackfile.js文件就是零配置没有指定入口文件出口文件,默认入口文件scr/index.js,出口文件dist/main.js 也就是说在没有配置文件的情况下会自动把src/index.js文件及其内部依赖打包成 dist文件夹下的main.js

(-D 代表--save-dev 表示这个npm包只有在开发时使用)

webpack配置

定义一个webpack.config.js文件

var path = require('path')
    module.exports = {
    mode: 'development',//webpack4新增的,development,production
    entry: path.join(__dirname, './src/index.js'),
    output: {
        path: path.reslove(__dirname, 'dist'),
        filename: 'bundle.js'
    }
}

webpack-dev-server

安装插件

npm i webpack-dev-server -D

var path = require('path')
    module.exports = {
    mode: 'development',//webpack4新增的,development,production
    entry: path.join(__dirname, './src/index.js'),
    output: {
        path: path.reslove(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    devServer:{
        open:true,
        port:3000,
        constbase:path.resolve(__dirname,dist).
        compress:true
    }
}

devServer参数

参数作用
open当入口文件更新时自动生成bundle.js文件后浏览器自动打开
port设定访问的端口号
host设置访问的主机ip地址
constbase服务默认去找项目根目录的index.html,此参数改变服务请求的文件夹
hot热更新,正常情况下每改变一次都要重新生成完整bundle.js,而此时只需要重新生成更新的内容即可,并实现浏览器的无刷新 ,相当于ajax的效果

设置之后在package.json文件添加脚本

dev:"webpack-dev-serve"

之后直接执行 npm run dev,开启服务,每当打包内容有变化就会自动重新修改内容,实现实时更新

html-webpack-plugin

npm install html-webpack-plugin -D

let HtmlWebpckPlugin=require("html-webpack-plugin")
var path = require('path')
    module.exports = {
    mode: 'development',//webpack4新增的,development,production
    entry: path.join(__dirname, './src/index.js'),
    output: {
        path: path.reslove(__dirname, 'dist'),
        filename: 'bundle,[hash:6].js'
    },
    devServer:{
        open:true,
        port:3000,
        constbase:path.resolve(__dirname,dist).
        compress:true
    },
    plugins:[
        new HtmlWebpckPlugin({
        //原tml文件路径
        template:path.join(__dirname,"./src/index.html"),
        //打包后的html文件名
	    filename:"index.html",
	  //title使用的前提是在html页面上<%= htmlWebpackPlugin.tags.bodyTags%> 
	    title:"haha"
	    
	    //html文件的压缩,
        minify:{
            //除空格
            collapseWhitespace: true,
            //移除标签属性值的双引号
            removeComments: true,
            //移除空属性
            removeRedundantAttributes: true,
}
        })
    ]
}

每次打包完会自动把打包好的js文件插入到html中

clear-webpack-plugin

cnpm i clear-webpack-plugin -D html文件要引入打包好的js文件(使用html-webpack-plugin会自动引入), script标签在页面内,解析页面结构时遇到script发送get请求,浏览器对get请求是有缓存的,所以要保证只要打包的内容改变打包后生成的js文件名不同,因此就在出口文件bundle.[hash:6].js,加上hash每次内容改变生成的打包后的文件的hash值都会改变,那么上次生成的文件就没有用了,使用clear-webpack-plugin可以删除没有使用的打包后文件,也可以在html-webpack-plugin插件配置中配置hash,这样就会在引入 script时 为文件名自动添加hash值

安装 npm i clear-webpack-plugin -D

let HtmlWebpckPlugin=require("html-webpack-plugin")
let {ClearWebpckPlugin}=require("clear-webpack-plugin")

var path = require('path')
    module.exports = {
    mode: 'development',//webpack4新增的,development,production
    entry: path.join(__dirname, './src/index.js'),
    output: {
        path: path.reslove(__dirname, 'dist'),
        filename: 'bundle,[hash:6].js'
    },
    devServer:{
        open:true,			//是否打开浏览器
        port:3000,			//端口号
        constbase:path.resolve(__dirname,dist),		//指定资源访问路径
        compress:true,				//是否开启GZIP压缩
        progress:false,				//是否显示编译进度
        hot:true,					//是否开启热更新
        proxy:{						//跨域代理
        	"/": "http://..."
        }
    },
    plugins:[
        new HtmlWebpckPlugin({
        //原tml文件路径
        template:path.join(__dirname,"./src/index.html"),
        //打包后的html文件名
	    filename:"index.html",
	  //title使用的前提是在html页面上<%= htmlWebpackPlugin.tags.bodyTags%> 
	    title:"haha"
	    
	    //html文件的压缩,
        minify:{
            //移除标签之间空格
            collapseWhitespace: true,
            //移除标签属性值的双引号
            removeAttributeQuotes:true
            //移除注释
            removeComments: true,
            //移除空属性
            removeRedundantAttributes: true,
        }
        }),
        new ClearWebpckPlugin()
    ]
}

多入口多页面的打包配置

多入口文件要在entry中为不同的文件设置名称(是key值),为output中的filename设置[name].[hash].js,得到相应个数的出口文件,有几个页面就要new几个HtmlWebpackPlugin,里面的chunks对应的数组元素就是入口文件的name,想要引入哪个打包后的文件就把name放到数组中,如果有像jquery这种想要单独作为一个入口文件就直接安装jquery yarn add jquery,然后直接把他放到entry对象中,在要引入他的html中的chunks添加它的name值

let HtmlWebpckPlugin=require("html-webpack-plugin")
let {ClearWebpckPlugin}=require("clear-webpack-plugin")

//index与login最好是html的文件名
//修改处
let htmlPlugin=["index","login"].map(item=>{
	return new HtmlWebpckPlugin({
        //原tml文件路径
        template:path.join(__dirname,`./src/${item}.html`),
        //打包后的html文件名
	    filename:`${item}.html`,
	  //title使用的前提是在html页面上<%= htmlWebpackPlugin.tags.bodyTags%> 
	    title:"haha"
	    chunks;[item]				
        //这个页面要引入的打包后的文件的name,就是在entry中定义的key
	    //html文件的压缩,
        minify:{
            //移除标签之间空格
            collapseWhitespace: true,
            //移除标签属性值的双引号
            removeAttributeQuotes:true
            //移除注释
            removeComments: true,
            //移除空属性
            removeRedundantAttributes: true,
        }
        })
})
var path = require('path')
    module.exports = {
    mode: 'development',//webpack4新增的,development,production
    
    //修改处
    entry: {
    	index:path.join(__dirname, './src/index.js'),
    	login:path.join(__dirname,'./src/login.js')
    }
    output: {
        path: path.reslove(__dirname, 'dist'),
        
        //修改处
        filename: '[name].[hash:6].js'			
        //这里的name就是entry中的key
    },
    devServer:{
        open:true,			//是否打开浏览器
        port:3000,			//端口号
        constbase:path.resolve(__dirname,dist),		//指定资源访问路径
        compress:true,				//是否开启GZIP压缩
        progress:false,				//是否显示编译进度
        hot:true,					//是否开启热更新
        proxy:{						//跨域代理
        	"/": "http://..."
        }
    },
    plugins:[
        ...htmlPlugin,
        new ClearWebpckPlugin()
    ]
}

解决css文件的引入

当入口文件或者他依赖的js文件中引入了css文件,现有的配置是不能支撑webpack的打包的,他还要安装很多的loader并完成配置,使得引入得css文件在打包之后可以使用 首先要安装一些loader:

yarn add style-loader css-loader postcss-loader less-loader less sass sass-loader node-sass autoprefixer -D

let HtmlWebpckPlugin=require("html-webpack-plugin")
let {ClearWebpckPlugin}=require("clear-webpack-plugin")

//index与login最好是html的文件名
let htmlPlugin=["index","login"].map(item=>{
	return new HtmlWebpckPlugin({
        //原tml文件路径
        template:path.join(__dirname,`./src/${item}.html`),
        //打包后的html文件名
	    filename:`${item}.html`,
	  //title使用的前提是在html页面上<%= htmlWebpackPlugin.tags.bodyTags%> 
	    title:"haha"
	    chunks;[item]				
        //这个页面要引入的打包后的文件的name,就是在entry中定义的key
	    //html文件的压缩,
        minify:{
            //移除标签之间空格
            collapseWhitespace: true,
            //移除标签属性值的双引号
            removeAttributeQuotes:true
            //移除注释
            removeComments: true,
            //移除空属性
            removeRedundantAttributes: true,
        }
        })
})
var path = require('path')
    module.exports = {
    mode: 'development',//webpack4新增的,development,production
    entry: {
    	index:path.join(__dirname, './src/index.js'),
    	login:path.join(__dirname,'./src/login.js')
    }
    output: {
        path: path.reslove(__dirname, 'dist'),
        filename: '[name].[hash:6].js'			
        //这里的name就是entry中的key
    },
    devServer:{
        open:true,			//是否打开浏览器
        port:3000,			//端口号
        constbase:path.resolve(__dirname,dist),		//指定资源访问路径
        compress:true,				//是否开启GZIP压缩
        progress:false,				//是否显示编译进度
        hot:true,					//是否开启热更新
        proxy:{						//跨域代理
        	"/": "http://..."
        }
    },
    module:{
    	rules:[{
        	test:/\.(less|css)$/i,
            use:[
            style-loader,		//把拿到的css内嵌到页面中
            //
            css-loader,			//解析@import/url
            postcss-loader,		//为样式加上前缀(这里要配合插件使用)
            {
            loader:less-loader,		//把less转成css
            options:{}
            }
            ]
        }]
    },
    plugins:[
        ...htmlPlugin,
        new ClearWebpckPlugin()
    ]
}

上面提到的postcss-loader,为样式加上前缀的要配合一个文件使用(postcss.config.js)在文件中

	module.exports={
    	plugins:[
        	require('autoprefixer')
        ]
    }

还要再package.json文件中浏览器的兼容范围

"browserslist":[
	"> 1%",
    "last 2 versions"
]

最后使用style-loader会把得到的样式内嵌到页面中,但是如果想要外链式的样式就要使用插件(min-css-extract-plugin)抽离样式到一个文件再引入 首先要安装 yarn add min-css-extract-plugin 在配置文件中引入

let HtmlWebpckPlugin=require("html-webpack-plugin")
let {ClearWebpckPlugin}=require("clear-webpack-plugin")
let MinCssExtractPlugin=require("min-css-extract-plgin")
//index与login最好是html的文件名
let htmlPlugin=["index","login"].map(item=>{
	return new HtmlWebpckPlugin({
        //原tml文件路径
        template:path.join(__dirname,`./src/${item}.html`),
        //打包后的html文件名
	    filename:`${item}.html`,
	  //title使用的前提是在html页面上<%= htmlWebpackPlugin.tags.bodyTags%> 
	    title:"haha"
	    chunks;[item]				
        //这个页面要引入的打包后的文件的name,就是在entry中定义的key
	    //html文件的压缩,
        minify:{
            //移除标签之间空格
            collapseWhitespace: true,
            //移除标签属性值的双引号
            removeAttributeQuotes:true
            //移除注释
            removeComments: true,
            //移除空属性
            removeRedundantAttributes: true,
        }
        })
})
var path = require('path')
    module.exports = {
    mode: 'development',//webpack4新增的,development,production
    entry: {
    	index:path.join(__dirname, './src/index.js'),
    	login:path.join(__dirname,'./src/login.js')
    }
    output: {
        path: path.reslove(__dirname, 'dist'),
        filename: '[name].[hash:6].js'			
        //这里的name就是entry中的key
    },
    devServer:{
        open:true,			//是否打开浏览器
        port:3000,			//端口号
        constbase:path.resolve(__dirname,dist),		//指定资源访问路径
        compress:true,				//是否开启GZIP压缩
        progress:false,				//是否显示编译进度
        hot:true,					//是否开启热更新
        proxy:{						//跨域代理
        	"/": "http://..."
        }
    },
    module:{
    	rules:[{
        	test:/\.(less|css)$/i,
            use:[
            
            //修改
            MinCssExtractPlugin.loader,		
            //把拿到的css放到文件中link到页面
            css-loader,			//解析@import/url
            postcss-loader,		//为样式加上前缀(这里要配合插件使用)
            {
            loader:less-loader,		//把less转成css
            options:{}
            }
            ]
        }]
    }
    plugins:[
        ...htmlPlugin,
        new ClearWebpckPlugin(),
        
        //修改
        new MinCssExtractPlugin({
        	filename:"[name].[hash].min.css"
            //这里的name还是入口entry处的name,hash还是为了防止浏览器的缓存使页面得不到最新数据x
        })
    ]
}

css和js的优化压缩

首先要安装yarn add optimize-css-assets-webpack-plugin(css) uglifyjs-webpack-plugin(js) terser-webpack-plugin(js不常用) -D

	const UglifyjsWebpackPlugin=require("uglifyjs-webpack-plugin")
    const TerserWebpackPlugin=require("terser-webpack-plugin")
    const OptimizeCssAssetsWebpackPlugin=require("optimize-css-assets-webpack-plugin")
    
    module.exports={
    	optimization:{
        	minimizer:[
            	new TerserWebpackPlugin(),
                new OptimizeCssAssetsWebpackPlugin()
            ]
        }
    }

图片的引入

图片正常会在css文件作为背景时出现,在js中动态创建img元素并赋值src属性,在html页面中出现引用图片的情况,同样目前的配置无法加载图片,也无法把图片打包到dist文件中,所以就要对配置文件进行进一步的配置 首先安装loader yarn add file-loader url-loader html-widthimg-loader

	module.exports={
    	module:{
        	rules:[{
            	test:[{
                	test:/\.(png|jpe?g|gif)$/i,
                    use:[{
                    	loader:'url-loader',
                        options:{
                        	limit:200*1024,
                            outputPath:"./images",
                            name:"[name].[hash].[ext]",
                            esModule:false
                        }
                    }],
                    include:path.resolve(__dirname,"src"),
                    exclude:/node_modules/
                },{
                	test:/\.(svg|eot|ttf|woff|woff2)$/i,
                    use:"file-loader"
                },{
                //找到html中的图片把他按照上面的规则执行
                	test:/\.html$/,
                    use:["html-withingimg-loader"]
                }]
            }]
        }
    	
    }

这里的url-loader是指在图片大小,小于limit的情况下直接base64处理,而在大于limit的情况下就要交给file-loader处理,outputPath就是打包后图片放入的文件路径,name是打包后没有转成base64的图片名称,打包后文件中图片的地址也会改变,这是指在css中的图片,在js中的src属性要通过require来引入要得到的相对路径的图片,而其他网站绝对路径的图片地址不受影响,加入esModule:false属性才能成功

实现js的转换

正常情况下webpack根据es6Module和CommonJS规范把js打包成一个文件,但是他不会把es6和es7的语法转化成es5的,这样当我们要兼容更低版本的浏览器的时候就做不到了,可以利用babel对js进行转换 安装:

yarn add babel-loader @bebal/core @bebal/perset-env(转换包把es6转成es5) -D

除了转换包之外还有一些插件可以用来转化一些语法

yarn add @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators -D

yarn add @babel/plugin-transform-runtime @babel/runtime @babel/polyfill -S

	moudle.exports={
    	module:{
        	rules:[{
        		test:/\.js$/i,
                use:[{
                	loader:"babel-loader",
                    options:{
                    	persets:[
                        	//es6=>es5
                        	"@babel/preset-env"
                        ],
                        plugins:[
                        	//es7装饰器
                        	["@babel/plugin-proposal-decorators",{										"legacy":true
                            }],
                            //es7的属性
                            ["@babel/plugin-proposal-class-properties",{								"loose":true
                            }],
                            //协助@babel/polifll,在运行时校验js的转换
                            "@babel/plugin-transform-runtime"
                        ]
                    }
                }],
                 include:path.resolve(__dirname,"src"),
                 exclude:/node_modules/
        	}]
        }
    }

特别注意@babel/plugin-transform-runtime @babel/runtime @babel/polyfill他们是安装在生产环境下的为了在代码运行的时候js转化