webpack学习笔记

全局安装webpack

    npm install webpack webpack-cli -g

全局安装webpack会有个问题,就是当你有两个项目依赖于不同版本的webpack,就会有一个项目打包不了,所以还是不全局安装webpack比较好。

在当前项目安装webpack

  1. 新建webpack-demo目录,然后进行npm项目初始化
npm init 或者 npm init -y
  1. 在刚创建出来的package.json中添加private字段
"private": true    /**表示私有的**/
  1. 如果webpack已经全局安装,需要卸载
npm uninstall webpack webpack-cli -g  /**卸载全局安装的webpack**/
  1. 在项目根目录下执行安装命令
npm install webpack webpack-cli --save-dev
  1. 使用npx打印出当前的webpack版本
npx webpack -v

安装指定版本的webpack

npm info webpack /*查看webpack所有版本信息*/
npm install [email protected] --save-dev

webpack的配置文件

在项目根目录新建文件webpack.config.js

const path = require('path');

module.exports = {
    entry: './index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'dist')
    }
}

webpack手动打包命令

npx webpack /**默认会使用根目录下的webpack.config.js配置文件进行打包**/
npx webpack --config webpack.config.js /**指定配置文件打包**/

使用npm scripts 简化webpack命令

  1. 在package.json中的scripts字段下添加bundle字段
"bundle": "webpack"
  1. 使用npm命令打包
npm run bundle

使用file-loader打包图片

  1. 安装file-loader
npm install file-loader --save-dev
  1. 在配置文件webpack.config.js中添加字段
module: {
    rules: [
        {
            test: /\.(jpg|png|svg|gif)$/,
            use: {           
                loader: 'file-loader',
                options: {
                   name: '[name].[ext]', /**原名字输出**/
                   outputPath: 'images/', /**打包后存放图片的文件夹**/
                }
            }
        }
    ]
}

使用url-loader打包图片

  1. 安装url-loader
npm install url-loader --save-dev
  1. 在配置文件webpack.config.js中添加字段
module: {
    rules: [
        {
            test: /\.(jpg|png|svg|gif)$/,
            use: {
                loader: 'url-loader',
                options: {
                    name: '[name].[ext]',
                    outputPath: 'images/',  
                    limit: 204800, /**小于20kb的图片,打包成base64放到bundle.js文件**/
                }
            }
        }
    ]
}

使用style-loader和css-loader打包css文件

  1. 安装style-loader 和 css-loader
npm install style-loader css-loader --save-dev
  1. 在webpack.config.js文件中添加配置
module: {
    rules: [
        {
            test: /\.css$/,
            use: ['style-loader', 'css-loader']
        }
    ]
}

使用sass-loader打包.sass文件

  1. 安装sass-loader 和 node-sass
npm install sass-loader node-sass --save-dev
  1. 在webpack.config.js文件中添加配置
module: {
    rules: [{
        test: /\.sass$/,
        use: ['style-loader', 'css-loader', 'sass-loader']
    }]
}

数组形式的loader是从下到上,从右到左执行 sass-loader -> css-loader -> style-loader

使用postcss-loader自动添加css厂商前缀

  1. 安装postcss-loader
npm install postcss-loader --save-dev
  1. postcss-loader需要配合autoprefixer插件使用
npm install autoprefixer --save-dev
  1. 在项目根目录添加postcss.config.js文件
module.exports = {
    plugins: [
        require('autoprefixer');
    ]
}
  1. 在webpack.config.js中添加配置
module: {
    rules: [{
        test: /\.scss$/,
        use: ['style-loader', 'css-loader', 'sass-loader', 'postcss-loader']
    }]
}
  1. 确保所有的scss文件都会被所有loader处理,修改webpack.config.js中的配置
{
    test: /\.scss$/,
    use: [
            'style-loader',
            {
                loader: 'css-loader',
                options: {
                    /**确保每个scss都被所有loader处理**/
                    importLoaders: 2,
                    /**分模块打包css**/
                    modules: true
                }
             },
             'sass-loader',
             'postcss-loader'
     ]
}

使用file-loader打包字体文件

  1. 安装file-loader
npm install file-loader --save-dev
  1. 在webpack.config.js文件中添加modules
{
    test: /\.(eot|ttf|svg)$/,
    use: {
        loader: 'file-loader'
    }
}

使用插件使webpack打包更便捷

1). 安装自动生成index.html插件 html-webpack-plugin

  1. 安装html-webpack-plugin
npm install html-webpack-plugin --save-dev

htmlWebpackPlugin 会在打包结束后,自动生成index.html文件,并把对应的js引入index.htm文件中

  1. 在webpack.config.js中引入插件
const HtmlWebpackPlugin = require('html-webpack-plugin');
plugins: [
    new HtmlWebpackPlugin({
        template: 'src/index.html' /**自动以src/index.html为模板,在dist目录下生成新的index.html文件**/
    })
]

2)使用clean-webpacl-plugin插件自动删除dist插件再打包

  1. 安装插件clean-webpack-plugin
npm install clean-webpack-plugin --save-dev
  1. 在webpack.config.js中使用插件
const CleanWebpackPlugin = require('clean-webpack-plugin');
plugins: [new CleanWebpackPlugin({})] /**默认清除的是dist目录**/

sourceMap配置

sourceMap 映射src目录的源文件,能定位到哪行报错

  1. 开启sourceMap,在wepback.config.js中添加配置
devtool: 'source-map'
  1. sourceMap的最佳实现
devtool: 'cheap-module-eval-source-map' /**开发环境中使用**/

devtool: 'cheap-module-source-map' /**生产环境**/

使用WebpackDevServer提高开发效率

  1. 安装webpack-dev-server
npm install webpack-dev-server --save-dev
  1. 在webpack.config.js中配置devServer
devServer: {
    contentBase: './dist',
    open: true,     /**open true 可以自动打开浏览器**/
    proxy: {
     "/api": "http://new.junbang.com/" /**请求api代理转发**/ 
    },
    port: 8081,  /**端口号**/ 
}
  1. 在package.json中添加watch命令
"scripts": {
    "watch": "webpack --watch",
    "start": "webpack-dev-server"
}

使用:npm run watch 监听文件有变化自动打包
使用:npm run start 可以自动监听,自动打包, 自动刷新浏览器

自定义server

  1. 需要安装express和webpack-dev-middleward这两个插件
npm install express webpack-dev-middleware --save-dev
  1. 新建server.js 并引入插件
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const config = require('./webpack.config.js');
const complier = webpack(config);

const app = express();

app.use(webpackDevMiddleware(complier, {
    publicPath: config.output.publicPath
}));

app.listen(3000,() => {
    console.log('server is running on prot 3000');
});
  1. 在package.json中设置serve命令
"scripts": {
    "serve": "node server.js"
}

npm run serve 使用的就是我们server.js配置的服务器
不过server.js还需要写很多,这只是简单的server

模块热更新HMR

  1. 开启模块热更新,在webpack.config.js中添加配置
devServer: {
    hot: true,
    hotOnly: true,  /**hotOnly: false 浏览器可以自动刷新**/
}
  1. 使用HMR插件,在webpack.config.js中引入插件
const webpack = require('webpack');
plugins: {
    new webpack.HotModuleReplacementPlugin()
}

使用npm run start重启服务使新的配置生效

模块热更新HMR作用:

css编写. 修改无需重新刷新浏览器就可显示效果
js模块发生改变可以指定更新当前js模块,不需要刷新浏览器

  1. js模块热更新,在index.js文件中编写代码
import number from './number';
number();

if (module.hot) {
    module.hot.accept('./number', () => {
    /**当number模块有改变重新渲染**/
     number();
    }
}

使用Babel处理ES6语法

  1. 安装babel-loader 和 @babel/core
npm install --save-dev babel-loader @babel/core
  1. 在webpack.config.js中添加配置
module: {
    rules: [{
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
            presets: ["@babel/preset-env"]
        }
    }]
}
  1. 安装@babel/preset-env模块对ES6语法进行翻译
npm install @babel/preset-env --save-dev
  1. 在webpack.config.js中的modules rules babel-loader 中配置options对象
options: {
    presets: ["@babel/preset-env"]
}
/**或者在项目根目录下创建配置文件.bablerc**/
{
    "presets": ["@babel/preset-env"]
}
  1. 使用@babel/polyfill 加上低版本没有的语法, 比如map() . Promise等
npm install --save @babel/polyfill
  1. 在所以代码运行之前,可以放在入口文件index.js最顶部
import "@babel/polyfill";
  1. polyfill默认会把所有翻译过代码都加进来,有时候我们没有用到的新方法,也有了翻译的方法在里面了,所以我们要过滤掉,没用上的就不要加载进来了,这样包更小,所以在webpack.config.js babel-loader中修改配置
options: [['@babel/preset-env', {
    useBuiltIns: 'usage'
}]]
  1. 已经支持ES6语法的浏览器版本,没必要在翻译所以我们可以指定浏览器版本
options: [['@babel/preset-env', {
    targets: {
        chrome: "67" /**更多浏览器版本配置去babel官网查看**/
    },
    useBuiltIns: 'usage'
}]]
  1. 当指定了useBuiltIns: 'usage',会自动引入@babel/polyfill,所以可以去掉index.js import的@babel/polyfill,但是可以需要安装依赖
npm install --save core-js
  1. 如果是写业务代码以上配置没问题,如果要写框架. 类库. 第三方模块什么的,为了避免变量的全局污染(因为polyfille翻译的变量挂载到全局变量)而使用plugin-transform-runtime插件

  2. 安装transform-runtime插件

npm install --save-dev @babel/plugin-transform-runtime
npm install --save #babel/runtime
  1. 在webpack.config.js中修改配置
module: {
    rules: [{
        test: /\.js$/,
        exclude: /node_modules/,
        loader: 'babel-loader',
        options: {
            plugins: [["@babel/plugin-transform-runtime", {
                corejs: 2
                helpers: true,
                regenerator: true
                useESModules: false
            }]]
        }
    }]
}
  1. 因为我们配置了corejs: 2,所以要加装corejs的依赖
npm install --save @babel/runtime-corejs2

配置React代码的打包

babel也可以打包react代码

  1. 安装react框架
npm install react react-dom --save
  1. 安装@babel/preset-react
npm install --save-dev @babel/preset-react
  1. 在.babelrc中添加配置
presets: ["@babel/preset-env","@babel/preset-react"]

使用Tree Shaking去掉没有引用的方法/ 模块

Tree Shaking只支持 ES Module 就是import export不支持require('')

  1. 在mode: 'development'中配置Tree Shaking, 在webpack.config.js中
optimization: {
    usedExports: true
}
  1. 在mode: 'production'中 默认就有,不需要添加任何配置

  2. 在package.json中配置不需要Tree Shaking的模块

"sideEffects": false   /**所有模块都需要Tree Shaking**/
"sideEffects": ['@babel/polly-fill', '*.css']  /**对这两个模块不做Tree Shaking**/

Development 和 Production 模式的区分打包

  1. 创建webpack.common.js 放公有的配置项

  2. 创建webpack.dev.js 放开发独有的配置项

  3. 创建webpack.prod.js 放生产独有的配置项

  4. 使用webpack-merge 把webpack.common.js合并到其他两个文件

npm install webpack-merge --save-dev
  1. 在webpack.dev.js中引入插件
const merge = require('webpack-merge');
const commonConfig = require('./webpack.common.js');

const devConfig = {
...
}
module.exports = merge(commonConfig, devConfig);

在webpack.prod.js中也作跟webpack.dev.js同样的处理

Code Splitting 代码分割

  1. 同步加载模块 在webpack.config.js中配置splitChunks
optimization: {
   splitChunks: {
       chunks: 'all'
   }
}
  1. 异步加载模块无需任何配置默认就会自动分割

  2. 创建一个异步加载的js模块

/** webpackChunkName: "lodash" 给这个模块起这个文件名 **/
function getComponent() {
    return import(/* webpackChunkName: "lodash"*/ 'lodash').then(({default: _}) => {
        var element = document.createElement('div');
        element.innerHtml = _.join(['@', 'bang']);
        return element
    });
}
getComponent().then(element => {
    document.body.appendChild(element);
});
  1. 安装babel-plugin-dynamic-import-webpack对异步加载模块语法进行翻译
npm install @babel/plugin-syntax-dynamic-import  --save-dev
  1. 在.babelrc配置文件中使用plugin-syntax-dynamic-import插件
plugins: ["@babel/plugin-syntax-dynamic-import"]

SplitChunksPlugin配置参数详解

/** 在webpack.config.js文件中 **/
optimization: {
    splitChunks: {
        /** all:-- 所有模块都分割 **/
        /** async -- 只对异步加载的模块进行分割 **/
        /** initial -- 只对同步模块进行分割 **/
        chunks: 'all', 
        minSize: 30000,  /** 大于30000b(30kb)才会分割 **/
        maxSize: 0,  /**  一般不配置,会对大文件再次分割 **/
        minChunks: 1,  /**  引用这个模块超过一次就会分割 **/
        maxAsyncRequests: 5, /**  同时只能加载分割5个库 **/
        maxInitialRequests: 3, /**  最多分割出3个文件 **/
        automaticNameDelimiter: "~", /** 文件名连接符  **/
        name: true
        cacheGroups: {
            /** 同步模块加载必需配置
            vendors: {
                test: /[\\/]node_modules[\\/]/,
                /** 如果模块同时满足cacheGroups和default, **/
                /** 根据priority来决定模块的归属谁的值大属于谁 **/
                priority: -10, 
                filename: 'vendors.js' /** 打包成的文件名 **/
            },
        /** 同步非node_modules里面的模块配置专用 **/
            default: {
                priority: -10,
                /** 已经打包过的模块忽略 **/
                /** 不再打包分割 **/
                reuseExistingChunk: true,
                filename: 'common.js'
            }
        }
    }
}

lazy loading 懒加载异步模块

  1. 打包分析工具

首先在打包的时候生成josn格式的描述文件,然后在package.json中添加命令

scripts: {
    "dev-build": "webpack --profile --json > stats.json --config webpack.dev.js"

}

使用:npm run dev-build
生成的.stats.json文件可以用来分析

使用Preloading, 空闲时候,静默加载我们的异步模块

  1. 查看js文件中代码的使用率,打开控制台 command + shift + p
  2. 搜索 Coverage 选中show Coverage 然后点击录制按钮变红后刷新页面
  3. 在index.js中创建一个异步加载模块js的方法
document.addEventListener('click', () => {
    import(/* webpackPrefetch: true */ './click.js').then(({default: func}) => {
     func();
    });
});

正常情况下呢,我们只有在点击的时候,才加载click.js,但是配置了Prefetch: true,就会空闲时主动加载我们的click.js。空闲时主动加载解决了异步加载慢的问题,因为文件加载过了,再点击加载就会使用缓存的文件。

css文件代码分割

  1. webpack 用style-loader处理的css会放到文件标签中
  2. 使用mini-css-extract-plugin把css分割成单独文件
npm install --save-dev mini-css-extract-plugin
  1. 这个插件不支持热更新(截止目前),所以一般是生产环境才用
  2. 在webpack.prod.js中 将style-loader替换为MiniCssExtractPlugin.loader
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
plugins: [
    new MiniCssExtractPlugin({});
]
module: {
    rules: [
       {
           test: /\.css$/,
           use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader' ]
    ]
}
  1. 过滤Tree Shakking, 在package.json中添加配置
"sideEffects": ["*.css"]
  1. 配置filename和chunkFilename, 在webpack.prod.js中
plugins: [
    new MiniCssExtractPlugin({
        filename: '[name].css',
        chunkFilename: '[name].chunk.css'
    });
}
  1. 使用optimize-css-assets-webpack-plugin对css合并和压缩
npm install --save-dev optimize-css-assets-webpack-plugin
  1. 在webpack.prod.js中使用optimize-css-assets-webpack-plugin
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
optimization: {
    minimizer: [new OptimizeCssAssetsPlugin({})]
}

多个入口文件引用的css打包到一个css文件

  1. 借助splitChunks, 在webpack.prod.js中添加配置
optimization: {
    splitChunks: {
        cacheGroups: {
            styles: {
                name: 'styles',
                test: /\.css$/,
                chunks: 'all',
                enforce: true
            }
        }
    }
}
  1. 还可以把一个入口文件引入的所有css打包到对应的一个文件。这样每个入口引用的css,就会生成对应一个css文件。这个配置也借助splitChunks,在webpack.prod.js中配置
optimization: {
    splitChunks: {
        cacheGroups: {
            mainStyles: {
                name: 'main',
                test: (m,c,entry = 'main') => m.constructor.name === 'CssModule && recursiveIssuer(m) === entry,
                chunks: 'all',
                enforce: true
            }
        }
    }
}

webpack与浏览器缓存

  1. 在webpack.prod.js中配置contenthash
output: {
   filename: '[name].[contenthash].js',
   chunkFilename: '[name].[contenthash].js'
}

contenthash在文件没有改变时,它不会变,对应的文件有改变它的值就会变,这样浏览器加载的就是新文件

通过webpack.ProvidePlugin插件自动帮我们引用没有import的模块

  1. 在webpack.common.js中配置ProvidePlugin
const webpack = request('webpack');
plugins: [
    new webpack.ProvidePlugin({
    $: 'jquery'
    })
]

解决问题: 有的模块使用的是jquery,但是没有import jquery,$对象找不到,只在首页引入jquery是不行的,这时候借助webpack.ProvidePlugin,帮我们在使用$对象的模块引入jquery。

让this都指向window

  1. 安装
    imports-loader
  npm install imports-loader --save-dev
  1. 在webpack.common.js中添加配置
module: {
    rules: [{
        test: /\.js$/,
        exclude: /node_modules/,
        use: [{
            loader: 'babel-loader',
        }, {
            loader: 'imports-loader?this=>window'
        }]
    }]
}

环境变量的使用

  1. 在webpack.common.js中使用Env
comst merge = require('webpack-merge');
const prodConfig = require('./webpack.prod.js);
const devConfig = require('./webpack.dev.js');
module.exports = (env) => {
 if (env && env.production) {
    return merge(commonConfig, prodConfig);
 } else {
    return merge(commonConfig, devConfig);
 }
}
  1. 在package.json中修改命令
"scripts": {
    "dev-build": "webpack -- config ./webpack.common.js",
    "build": "webpack --env.production  --config ./webpack.common.js"
}

Library的打包

  1. 创建一个自己的包模块library
npm init -y
  1. 在包目录下新文件src/math.js 和 src/string.js
  2. 在src/math.js中编写代码
export function add(a, b) {
    return a + b;
}
export function minus(a, b) {
    return a - b;
}
export function multiply(a, b) {
    return a * b;
}
export function division(a, b) {
    return a / b;
}
  1. 在src/string.js中编写代码
export function join(a, b) {
 return a + " " + b;
}
  1. 在src/index.js中编写代码
import * as math from './math.js';
import * as string from './string';

export default { math, string }
  1. 在项目中安装webpack 和 webpack-cli
npm install webpack webpack-cli --save
  1. 在项目中创建webpack.config.js
const path = require('path');
module.exports =  {
    mode: 'production',
    entry: './src/index.js'
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'library.js',
        library: 'library',
        libraryTarget: 'umd'
    }
}
  1. 在package.js中配置入口文件和打包命令
main: './src/index.js',
scripts: {
    "build": "webpack"
}
  1. 别人使用我们的库可能用到的引入方式
    1、 import library from 'library';
    2、const library = require('library');
    3、 require(['library'], function() {});
    4、

  2. 通过在webpack.config.js中配置libraryTarget: 'umd'可以使用前面三种引用

  3. 通过在webpack.config.js中配置library: 'library'可以使用

你可能感兴趣的:(webpack学习笔记)