webpack

185 阅读6分钟

webpack(官网:webpack.docschina.org/) 是一个现代JavaScript应用程序的静态模块打包工具。当webpack处理应用程序时,它会在内部构建一个依赖图,此依赖图会映射项目所需的每个模块,并生成一个或多个bundle包!webpack本身是基于node.js开发的!

一、模块化思想:每个页面可以理解为一个多个模块,最后很多模块拼成一个大的页面,可以封装一些公共的组件模块进行开发。

CommonJs规范

在 CommonJS 模块中,如果你 require 了一个模块,那就相当于你执行了该文件的代码并最终获取到模块输出的 module.exports 对象的一份拷贝,并且重复引入的模块并不会重复执行,再次获取模块只会获得之前获取到的模块的拷贝

  • module.exports
  • require

导出:

export function sum(x, y) {
    return x + y;
}

export let n = 10;

导入:

import {
    sum,
    n
} from './A.js';

// import * as A from './A';
// A.sum()
// A.n

console.log(sum(10, 20));

ES6 Module模块规范es6.ruanyifeng.com/#docs/modul…

ES6Module是JS新增的模块导入导出规范,它是静态编译的,模块的导入都要放到代码执行的最前面,浏览器不能直接识别,需要先进行编译才可以(webpack可以完成这个编译)

  • 动态编译:代码执行到具体位置的时候才会进行模块的导入导出
  • 静态编译:代码还没有执行,就按照依赖的关系把模块导入导出和编译好了

导出

export default function average(x, y) {
    return x + y;
};

export let n = 10;

导入

import c from './C';

console.log(c(10, 20));

二、webpack的零配置操作

安装:

/ 为防止全局安装webpack导致版本冲突,真实项目中以本地安装为主 
$ npm init -y  //生成的package.json中一般不允许出现中文/大写字母/特殊符号
$ npm install webpack webpack-cli --save-dev  //安装在开发依赖下(不加--save-dev默认安装在生产依赖下)
OR
$ yarn add webpack webpack-cli -D  //安装在开发依赖下

开发依赖:本地开发的环境
生产依赖:服务器部署的环境

零配置的使用:

/*
 * 默认会打包src目录中的js文件(入口默认index.js)
 * 打包完成的目录默认是dist/main.js
 * webpack默认支持CommonJS和ES6 Module的模块规范,依此进行依赖打包
 */
cmd中的指令:npx webpack
或者自己在package.json下的scripts设置相应的指令
'serve' : 'webpack'
然后在cmd中的指令为:npm run serve / yarn serve

自定义基础配置:

文件名固定
文件名(可以自动识别):webpack.config.js / webpackfile.js
	编写自定义的webpack配置项,
	以后webpack打包编译的时候是按照自己配置的内容进行打包编译处理的
 	这个文件放置在项目的根目录下

webpack本身是基于Node开发的,所以配置项的模块处理规则参考CommonJS规范来完成

const path = require('path');  //获取绝对路径

module.exports = {
	// 设置编译打包的模式 development(开发环境:合并但不会压缩) / production(生产环境:合并压缩)(默认)
	mode: 'production',
	// 入口:设置编译的入口文件(真实项目中一般开发的代码都要放置到SRC下)
	entry: './src/main.js',
	// 输出:设置编译的出口文件
	output: {
		// 编译后输出文件的文件名:[hash]编译的时候会随机在名字中生成唯一的哈希值,以此保证每一次编译出来的文件是不一样的
		filename: 'bundle.[hash].min.js',
		// 输出的目录(需要是绝对路径)
		path: path.resolve(__dirname, 'build')  //根目录下,build为文件夹的名字
	}
};

通过npx webpack / yarn serve / npm run serve 进行编译
自定义配置文件名
  • $ npx webpack –config webpack.config.development.js
  • 可在package.json中配置可执行的脚本命令(区分开发环境)
"scripts": {
    "serve": "webpack --config webpack.config.dev.js",
    "build": "webpack --config webpack.config.pro.js"
},

开发环境下的配置项(webpack.config.dev.js):

/* 开发环境下的配置项 */
const path = require('path');
module.exports = {
    mode: 'development',
    entry: './src/main.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build')
    }
};


执行指令:yarn serve

生产环境下的配置项(webpack.config.pro.js):

/* 生产环境下的配置项 */
const path = require('path');
module.exports = {
    mode: 'production',
    entry: './src/main.js',
    output: {
        filename: 'bundle.[hash].min.js',
        path: path.resolve(__dirname, 'build')
    }
};

执行指令:yarn build
配置HTML页面模板进行打包(自动更改html中导入的js)

插件(npmihtmlwebpackpluginsavedev/npm i html-webpack-plugin –save-dev / yarn add html-webpack-plugin -D)可以帮我们对于HTML的编译和导入文件去进行自动的处理(www.webpackjs.com/plugins/htm…)

清空之前打包的内容

yarnaddcleanwebpackpluginD/yarn add clean-webpack-plugin -D / npm install clean-webpack-plugin –sav-dev 每一次打包的时候,都把之前打包的内容清空掉,也就是build下只保留最新打包的文件

html改变后页面自动更新(创建一个web服务(不在用vscode的live server了)):

yarnaddwebpackdevserverD/yarn add webpack-dev-server -D / npm install webpack-dev-server –save-dev

  • 自动监听代码的改变并进行自动编译
  • 自动帮我们打开浏览器渲染页面
  • 重新编译后自动刷新浏览器看到最新的效果
  • 除非配置项更改了,需要自己重新执行,否则直接都基于这个插件完成自动化处理(webpack.js.org/configurati…)
/*package.json*/
"scripts": {
    "serve": "webpack-dev-server",  //开发:编译到计算机的内存中
    "build": "webpack"  //部署:基于这个命令生成部署的文件
},
/*webpack.config.js*/
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');  //html自动更新
const {
	CleanWebpackPlugin
} = require('clean-webpack-plugin');

module.exports = {
    mode: 'production',
    entry: './src/main.js',
    output: {
        filename: 'bundle.[hash].min.js',
        path: path.resolve(__dirname, 'build')
    },
    // 配置DEV-SERVER(dev-server相当于帮我们构建一个后台):编译后的结果放在计算机内存中,并不会向之前的webpack命令一样,把编译后的东西放到build下,dev-server仅仅是在开发模式下,随时编译并且预览的,项目要部署的时候,还是需要基于webpack编译打包的
    devServer: {
        // WEB服务的端口号
        port: '3000',
        // 开启GZIP压缩
        compress: true,
        //显示编译进度
        progress:true,
        // 指定资源访问的路径
        contentBase: path.resolve(__dirname, "build"),
        // 自动打开浏览器
        open: true,
        // 开启热更新
        hot: true,
        // Proxy跨域代理
        // proxy: {
        //  '/': 'http://127.0.0.1:8888'
        // }
    },
    // 在WEBPACK中使用插件
    plugins: [
        // 配置指定的HTML页面模板(后期在编译的时候会把编译好的资源文件自动导入到我们的页面模板中)
        new HtmlWebpackPlugin({
        	// 模板的路径
            template: './public/index.html',
            // 编译后生成的文件名
            filename: 'index.html',
            // 是否把编译的资源文件导入到页面中,设置HASH值(清除强缓存,和OUTPUT设置HASH值是一样的,二者写一个即可)
            // hash: true,
            // 把模板中的HTML代码也进行压缩编译(配置规则)
            // https://github.com/kangax/html-minifier
             minify: {
                collapseWhitespace: true,  //把所有标签和标签之间的空格去掉
                removeComments: true,      //去掉注释
                removeAttributeQuotes: true,  //把属性的双引号去掉
                removeEmptyAttributes: true   //去掉空的属性
            }
        }),
        // 每一次打包都把之前打包的清空
        new CleanWebpackPlugin()
    ]
};

指令:yarn serve

三、在模板中可以自己单独导入一些CSS/JS/图片等资源文件

    1. 自己导入的文件不会受webpack编译的影响(不会和其它模块的文件编译在一起),所以有时候我们会单独导入一些公共的资源文件
    1. 有些资源类库不支持CommonJS规范,也不支持ES6Module规范,这样的资源文件只能在模板中手动导入,因为webpack无法处理他

四、配置多入口的打包编译

/*webpack.config.js*/
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');  //html自动更新
const {
	CleanWebpackPlugin
} = require('clean-webpack-plugin');

/ 配置多页面模板
const htmlPlugins = ['index', 'login'].map(item => {
    return new HtmlWebpackPlugin({
        template: `./public/${item}.html`,
        filename: `${item}.html`,
        // chunks: ['jquery', item], // 指定当前页面的依赖项,想先引入谁就把谁写到前面
        chunks: [item],
        minify: {
            collapseWhitespace: true,
            removeComments: true,
            removeAttributeQuotes: true,
            removeEmptyAttributes: true
        }
    });
});

module.exports = {
    // 基础配置
    mode: 'production',
    // entry: './src/main.js',  //单页面
    // 多入口 KEY:VALUE
    entry: {  //多页面
        index: './src/main.js',
        login: './src/login.js',
        // 如果不想把JQ合并在其它的JS中,想独立打包出来(多个页面公共的部分我们可以独立打包出来),利用chunks
        // jquery: 'jquery'
    },
    output: {
        // [name]多入口中配置的属性名 index/login
        filename: '[name].[hash].min.js',
        path: path.resolve(__dirname, 'build')
    },
    // 配置DEV-SERVER
    devServer: {
        port: '3000',
        compress: true,
        open: true,
        hot: true
    },
    plugins: [
        // 配置指定的HTML页面模板
        ...htmlPlugins,
        // 每一次打包都把之前打包的清空
        new CleanWebpackPlugin()
    ]
};

五、关于css(less)的处理

$ npm install css-loader style-loader less less-loader autoprefixer postcss-loader –save-dev

webpack.config.js

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

postcss.config.js

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

package.json

// https://github.com/browserslist/browserslist
"browserslist": [
    "> 1%",
    "last 2 versions"
]

六、mini-css-extract-plugin 抽离CSS内容

https://www.npmjs.com/package/mini-css-extract-plugin
$ npm install 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"
            ]
        }]
    }
}

七、设置优化项压缩CSS/JS

$ npm install optimize-css-assets-webpack-plugin uglifyjs-webpack-plugin terser-webpack-plugin –save-dev
const UglifyjsWebpackPlugin=require('uglifyjs-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCssAssetsWebpackPlugin= require('optimize-css-assets-webpack-plugin');

module.exports = {
    //=>设置优化项
    optimization: {
        //=>设置压缩方式
        minimizer: [
            //=>压缩CSS(但是必须指定JS的压缩方式)
            new OptimizeCssAssetsWebpackPlugin(),
            //=>压缩JS
            //new UglifyjsWebpackPlugin({
                //cache: true, //=>是否使用缓存
                //parallel: true, //=>是否是并发编译
                //sourceMap: true, //=>启动源码映射(方便调试)
            //}),
            new TerserPlugin()
        ]
    }
};

八、webpack中图片的处理

$ npm install 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,
                    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的转换和ESLint语法检测

https://babeljs.io/
https://eslint.org/
$ npm install babel-loader @babel/core @babel/preset-env @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/plugin-transform-runtime –save-dev
$ npm insall @babel/runtime @babel/polyfill
$ npm install eslint eslint-loader –save-dev
module.exports = {
    module: {
        rules: [{
            test: /\.js$/,
            use: [{
                loader: 'babel-loader',
                options: {
                    //=>转换的语法预设(ES6->ES5)
                    presets: [
                        "@babel/preset-env"
                    ],
                    //=>基于插件处理ES6/ES7中CLASS的特殊语法
                    plugins: [
                        ["@babel/plugin-proposal-decorators", {
                            "legacy": true
                        }],
                        ["@babel/plugin-proposal-class-properties", {
                            "loose": true
                        }],
                        "@babel/plugin-transform-runtime"
                    ]
                }
            }], //=>, "eslint-loader"
            //=>设置编译时忽略的文件和指定编译目录
            include: path.resolve(__dirname, 'src'),
            exclude: /node_modules/
        }]
    }
}

类的装饰器

@log
class A{
    a=1;
}
function log(target){}