简单介绍Webpack 的构建项目的主要环节

安装webpack

首先安装webpack、webpack-cli、webpack-dev-server。javascript

项目初始化

这里使用vue项目作演示,大家能够用别的,不影响webpack的构建。创建webpack.config.js文件css

引入一系列的依赖和loader

vue-loader和vue-template-compiler是vue必须的,官方地址:https://vue-loader.vuejs.org/zh/html

node-sass、less、css-loader、vue-style-loader、less-loader和sass-loader让咱们项目支持css、less和sass。vue

babel-loader、@babel/core、@babel/preset-env让咱们项目支持ES6。java

还有图片文件和字体文件的加载器,url-loader依赖于file-loader因此都要安装 。node

关于devServer

如其名称,devServer是用于启动开发环境的服务的,配置项能够上官网看,比较好理解。这里就提两个重要的配置项,一是hot为true时,能够启动HMR。二是proxy,在先后端分离的项目,可使用这个配置来代理后台的服务地址。webpack

//webpack.config.js
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');

module.exports ={
    entry:'./src/main.js', // 项目的入口文件
    output:{
        filename: 'bundle.js',    // 打包后的文件名
        path: path.join(__dirname, 'dist'),   // 项目的打包文件路径
        publicPath: 'dist/'   // 输出解析文件的目录,指定资源文件引用的目录
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: file => (
                    /node_modules/.test(file) &&
                    !/\.vue\.js/.test(file)
                )
            },
            {
                test: /\.vue$/,
                loader: 'vue-loader'   // vue官网介绍,使用这个loader的同时,还须要VueLoaderPlugin
            },
            {
                test: /\.css$/,  //匹配后缀名为css的文件,而后分别用css-loader,vue-style-loader去解析
                use: [  //解析器的执行顺序是从下往上(先css-loader再vue-style-loader)
                    'vue-style-loader',
                    'css-loader'
                ],
            },
            {
                test: /\.scss$/,
                use: [
                    'vue-style-loader',
                    'css-loader',
                    'sass-loader'
                ],
            },
            {
                test: /\.less$/,
                use: [
                    'vue-style-loader',
                    'css-loader',
                    'less-loader'
                ]
            },
            {
                test:/\.(png|jpe?j|gif|svg)(\?.*)?$/,
                loader:'url-loader',
                options:{
                    limit: 10 * 1024, // 10 KB  //图片文件大小小于limit的数值,就会被改写成base64直接填入url里面
                }
            },
        ]
    },
    plugins: [
        new VueLoaderPlugin()  // 官方使用vue-loader时,必要的plugin
    ],
    devServer: {    // 开发服务器工具
        contentBase: './public', //基于的根目录
        open: true,  // 自动打开浏览器
        overlay: true // 将错误显示在html之上
    }
}

为了之后方便启动,把config文件配置好后,再把package.json的scripts也配置一下web

"scripts": {
    "serve": "webpack-dev-server",  //webpack5.0和webpack-cli4.0不支持webpack-dev-server,使用webpack serve
    "build": "webpack"
  }

这时启动项目,就能够看到编译后的项目了。json

引入经常使用的插件

clean-webpack-plugin能够在每次打包前把上一次打包的文件删除再进行打包。后端

html-webpack-plugin能够将html文件打包后自动引入打包后的js文件,能够经过设置相关属性来指定html,若是想要生成多个html,能够屡次new这个插件。引入这个插件后,就不须要设置output.publicPath和devServer.contentBase属性了。

copy-webpack-plugin将须要拷贝的文件放入打包后的文件,通常不在开发阶段使用。

HotModuleReplacementPlugin热更新插件(也称HMR),这个插件是webpack自己提供的,不须要再单独去下载依赖。在devServer中配置{hot:true}时启动,css自动生效,而js等须要手动添加(使用module.hot.accept(dep,callback))

//webpack.config.js
const webpack = require('webpack')
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin')

module.exports ={
    entry:'./src/main.js', // 项目的入口文件
    output:{...},
    module: {...},
    plugins: [
        new VueLoaderPlugin(),  // 官方使用vue-loader时,必要的plugin
        new CleanWebpackPlugin(),
        new HtmlWebpackPlugin({
            title: 'Webpack Vue Sample',      // 设置title
            meta: {                           // 设置meta
                viewport: 'width=device-width'
            },
            template: './public/index.html'      // 指定模板
        }),
        new webpack.HotModuleReplacementPlugin(),
        // // 开发阶段最好不要使用这个插件
        // new CopyWebpackPlugin(['public'])
    ],
    devServer: {   // 开发服务器工具
        hot: true,   // 启动HMR ,若是要手动添加一些文件的HMR,能够用hotOnly来调试
        open: true,  // 自动打开浏览器
        overlay: true // 将错误显示在html之上
    }
}

不一样工做环境的配置

建立不一样环境配置的方式主要有两种方法,一是在配置文件中添加相应的条件,根据不一样的条件来导出相应的配置;二是根据不一样的环境添加对应的配置文件。在不一样的环境能够有不一样配置项,像mode、devtool等,还有上面用到的clean-webpack-plugin、copy-webpack-plugin等插件均可以区分到所对应的环境中。

根据不一样的条件来导出相应的配置

这个方法主要经过cli配置的环境名参数,而后再module.exports里接收这个参数来进行判断,启动production的方法能够是在命令行输入‘webpack --env production’,也能够配置在scripts中。

module.exports = (env, argv) => {
  const config = {
    mode: 'development',
    entry: './src/main.js',
    output: {...},
    devtool: 'cheap-eval-module-source-map',
    devServer: {...},
    module: {...},
    plugins: [...]
  };

  if (env === 'production') {
    config.mode = 'production';
    config.devtool = false;
    config.plugins = [
      ...config.plugins,
      new CleanWebpackPlugin(),
      new CopyWebpackPlugin(['public'])
    ];
  }

  return config;
}

根据不一样的环境添加对应的配置文件

这个方法如其名,是要创建不一样环境的配置文件来进行匹配。通常须要创建一个webpack.common.js一个公共配置,而后再按开发、生产的等环境在创建对应环境特有的配置文件,最后在启动项目时经过'webpack --config webpack.prod.js'来启动不一样的项目便可。

// webpack.common.js
const path = require('path');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports ={
    entry:'./src/main.js', // 项目的入口文件
    output:{...},
    module: {...},
    plugins: [
        new VueLoaderPlugin(),  // 官方使用vue-loader时,必要的plugin
        new HtmlWebpackPlugin({
            title: 'Webpack Vue Sample',      // 设置title
            meta: {                           // 设置meta
                viewport: 'width=device-width'
            },
            template: './public/index.html'      // 指定模板
        }),

    ],
}
//webpack.dev.js
const webpack = require('webpack');
const {merge} = require('webpack-merge');

const common = require('./webpack.common');

module.exports = merge(common,{
    mode: 'development',
    devtool: 'cheap-eval-module-source-map',
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
    ],
    devServer: {   // 开发服务器工具
        hot: true,   // 启动HMR
        open: true,  // 自动打开浏览器
        overlay: true // 将错误显示在html之上
    }
});
// webpack.prod.js
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin')
const {merge} = require('webpack-merge');

const common = require('./webpack.common');

module.exports = merge(common,{
    mode:'production',
    plugins: [
        new CleanWebpackPlugin(),
        new CopyWebpackPlugin( ["public"])
    ],
});

配置全局常量

经过对开发模式和生产模式配置不一样的常量,从而能够在构建时容许不一样的行为。 可使用webpack自带的DefinePlugin来进行配置。

new webpack.DefinePlugin({
    BASE_URL:JSON.stringify('./')
}),

打包过程的优化

Tree Shaking

Tree Shaking在production模式下会自动开启,咱们想要在其余模式下也使用的话,须要本身来配置。

optimization: {
        usedExports: true,  // 模块只导出被使用的成员
        concatenateModules: true,  // 尽量合并每个模块到一个函数中
        minimize: true, // 压缩输出结果
    },

sideEffects

sideEffects在production模式下也会自动开启,它主要的做用是对所标识的没有反作用文件进行识别,将没有用的到的代码不进行打包。想在除production模式下也启用这个功能,能够对webpack配置文件和package.json文件两个文件进行配置。

// webpack配置文件
  optimization: {
        sideEffects: true, // 开启sideEffects 功能
    },
// package.json
 "sideEffects": [
    "*.css"   //标识有反作用的文件
  ]

Code Splitting

Code Splitting主要做用就是将打包后的代码进行分包,具体实现有两种方法:1.多入口打包;2.动态导入;

多入口打包

多入口打包的功能主要经过webpack的配置来实现

//  多入口打包
module.exports = {
  entry: {   // 入口改成多文件导入
    index: './src/index.js',
    app: './src/app.js'
  },
  output: {
    filename: '[name].bundle.js' // 导出的文件名动态生成
  },
  module: {...},
  optimization: {
    splitChunks: {
      chunks: 'all'   // 防止重复引用,自动提取全部公共模块到单独 bundle
    }
  },
  plugins: [
    new HtmlWebpackPlugin({  
      title: 'index Page',
      template: './src/index.html',
      filename: 'index.html',
      chunks: ['index']  //设置html须要导入的js文件
    }),
    new HtmlWebpackPlugin({
      title: 'app Page',
      template: './src/app.html',
      filename: 'app.html',
      chunks: ['app']
    })
  ]
}

动态导入

动态导入指的是在应用运行到须要某个模块的时候,再来导入这个模块。webpack自动支持这种方式而且会单独进行分包,其主要应用场景是路由切换时。打包后的文件还能够经过魔法注释(/* webpackChunkName: 'name' */)来定义文件的名称

const render = () => {
  const hash = window.location.hash || '#home'

  const mainElement = document.querySelector('.main')

  mainElement.innerHTML = ''

  if (hash === '#home') {
    import(/* webpackChunkName: 'homeCom' */'./home/home').then(({ default: home}) => {
      mainElement.appendChild(home())
    })
  } else if (hash === '#index') {
    import(/* webpackChunkName: 'indexCom' */'./index/index').then(({ default: index}) => {
      mainElement.appendChild(index())
    })
  }
}

render()

window.addEventListener('hashchange', render)

css文件提取到单个文件

当css文件比较大想要单独提取出来时,可使用‘mini-css-extract-plugin’插件,在使用这个插件时,还要同时使用‘optimize-css-assets-webpack-plugin’、'terser-webpack-plugin'这两个插件,‘optimize-css-assets-webpack-plugin’是用来压缩css文件的,'terser-webpack-plugin'是用来压缩js文件的。由于在配置minimizer后,webpack会取消默认的js压缩plugin,多以须要本身再把js的plugin给添加进来。

const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')
const TerserWebpackPlugin = require('terser-webpack-plugin')

module.exports = {
  entry: './src/index.js',
  output: {...},
  optimization: {
    minimizer: [   // webpack官方建议把压缩性质的文件用minimize来统一管理
      new TerserWebpackPlugin(),  //  js文件压缩
      new OptimizeCssAssetsWebpackPlugin()  // css文件压缩
    ]
  },
  module: {
    rules: [
       ...,
      {
        test: /\.css$/,
        use: [
          // 'style-loader', // 将样式经过 style 标签注入
          MiniCssExtractPlugin.loader, // 替换掉'style-loader'
          'css-loader'
        ]
      }
    ]
  },
  plugins: [
    ...,
    new MiniCssExtractPlugin()  // 提取css文件到单个文件,若是单个css文件体积不大,不必单独提取到一个文件
  ]
}

substitution

webpack提供了一个substitution(可替换的模版字符串)的方式,这个方式将根据资源内容建立出惟一 hash 。主要有三个模板:1.'hash',2.'chunckhash',3.'contenthash',具体何时用,就不作多介绍了。

new MiniCssExtractPlugin({
  filename: 'static/css/[name].[contenthash:4].css',   // hash默认20位长度,能够用':'接数字,指定长度
  chunkFilename: 'static/css/[name].[contenthash:4].css',
}),

ESlint

使用eslint-loader做为pre-loader运用,能够在开发过程当中每次保存的时候就会自动进行代码校验。

module.exports = {
  module: {
    rules: [
      {
        enforce: 'pre',
        test: /\.(js|vue)$/,
        loader: 'eslint-loader',
        exclude: /node_modules/
      }
    ]
  }
}
相关文章
相关标签/搜索