前端基础webpack配置

263 阅读6分钟

前言

随着项目规模不断变大,前端工程化显得越来越重要;从维护的方面来说,不仅可以使用eslint对项目进行语法检测,形成统一的代码规范,还可以使用Jest自动化测试,对实现的功能进行测试;从优化的方面来说,对各种静态资源的打包(css,js,img等等),可以减少网络请求,提高网站的访问速度,利用其中hash命名文件方式,可以做到旧文件读取浏览器缓存,新的文件发起新的请求。当然了,还有很多优势,这里就不一一列举了。所以,学会配置最基础的webpack是很重要的。

收获的内容

本文的目的是让编写一个最基础的webpack配置,可以在项目中运行起来。
1.了解到webpack配置的参数
2.webpack常用的插件
3.编写package.json的scripts指令

配置共用的文件

配置一个webpack.base.config.js文件,作为开发环境与线上环境共用的配置文件

const path = require('path');

const config = {
    entry: {
        main: path.join(__dirname, './client-entry.js')
    },
    output: {
        path: path.join(__dirname, './public'),
        filename: '[name].[hash:8].js'
    }   
}

module.exports = config;

指定入口文件并指定chunk为main,output中path用来指定打包在磁盘中的位置,在生产环境中是必须指定的,但是在开发环境不是必须的,因为webpack-dev-server开启后是打包在内存中而非磁盘;这里顺带提下publicPath,代表打包的资源url前缀,这个在生产环境和开发环境都是比较重要的,即img,js,css等等访问时的前缀,可以填写相对路径或绝对路径。
注意这里的filename,使用的是哈希值命名,[hash:8],代表截取生成的hash值的8位,为什么需要哈希值来命名呢?这里涉及到一些http协议的内容,通常来讲,一个网站进行优化,就需要适当地使用浏览器缓存;服务端通过设置Cache-Control:max-age=13800s,在13800s内再次访问该资源只需要从浏览器缓存取得就可以了;但是这个时候需要在该时间内取得最新的文件怎么办?这时候就需要用到hash,更改文件后hash值也就发生了改变,此时,再次访问,文件名发生变化,也就是发起一个新的请求。有可能你会看到以下这几种hash命名方式。

filename: '[name].[hash:8].js'
filename: '[name].[chunkhash:8].js'
filename: '[name].[contenthash:8].css'

第一种hash:就是每次打包生成的所有文件hash值都是一致的,这种方式有缺点,那就是每次打包都会将整个项目的文件名都修改了,也就无法使用浏览器缓存功能;
第二种chunkhash:同一个入口chunk的chunkhash值一致,上面案例中就是main这个chunk下所有的文件chunkhash值一致,在生产环境中,会把公共库和程序文件入口分开,所以只要公共库的文件没有发生改变,就可以保证其chunkhash值不会发生改变;
第三种contenthash: 在chunkhash中,如果一个index.js引入Index.css文件,index.js文件发生改变,那么也会导致index.css文件重复构建。使用MiniCssExtractPlugin中的contenthash,即使js文件发生改变,只要css文件没有更改就不会重复构建,后续会讲到。
接下来配置各种loader,对各种类型文件进行处理;引用webpack官方文档的介绍:

loader 用于对模块的源代码进行转换。loader 可以使你在 import 或"加载"模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!

module: {
    rules: [
      {
        test: /\.(vue|js|jsx)$/,
        loader: 'eslint-loader', //每次代码保存都会进行语法检测
        exclude: /node_modules/, //排除node_modules
        enforce: 'pre' //在其他loader加载前进行预处理
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.jsx$/,
        loader: 'babel-loader' // 对jsx进行编译
      },
      {
        test: /\.js$/,
        loader: 'babel-loader', //ES6转ES5
        exclude: /node_modules/
      },
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.(gif|jpg|jpeg|png|svg)$/,
        use: [
            {
              loader: 'url-loader', //基于file-loader,需要安装
              options: {
                limit: 1024, //在1024bytes(1kb可以确保输出资源不会包含错误)内进行处理
                name: 'resources/[path][name].[ext]' //打包后会按照现有在磁盘建立的文件夹结构呈现
              }
            }
          ]
      }
    ]
  }

以vue为例,还需要引用vue-loader插件

const VueLoaderPlugin = require('vue-loader/lib/plugin');
const config = {
    ..., //同上
    plugins: [
        new HtmlWebpackPlugin({
        template: path.join(__dirname, 'template.html') //建立模板,还可以指定图标,title
        }),
        new VueLoaderPlugin()
    ],
};

module.exports = config;

至此,已完成一个共用文件的配置!

配置开发环境文件

配置一个webpack.dev.config.js文件

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config.base.js');

const config = merge(baseConfig, {
    devtool: '#cheap-module-eval-source-map',
    module: {
      rules: [
        {
          test: /\.styl(us)?$/,
          use: [
            'vue-style-loader', // 开发环境使用, 后续在生产环境中还需要处理
            'css-loader',
            'stylus-loader'
          ]
        }
      ]
    }
  });
  
 module.exports = config;

使用webpack-merge插件合并配置,devtool设置为#cheap-module-eval-source-map,有利于开发中调试问题,可以将源代码位置显示,而不是编译过后的代码。还有其他各种devtool,具体可以去官网查阅: www.webpackjs.com/configurati…
配置webpack-dev-server和plugins:

const config = merge(baseConfig, {
   devServer: {
      port: 8088, 
      // 方便内网的调试
      host: '0.0.0.0',
      overlay: {
        errors: true //将错误显示
      },
      autoOpenBrowser: false, //禁用自动打开浏览器
      hot: true //启用模块热替换特性
    },
    plugins: [
      //设置环境变量,这里要么使用JSON.stringify,或者使用双引号,否则会识别为一个变量,导致报错
      new webpack.DefinePlugin({
        'process.env': {
          NODE_ENV: isDev ? '"development"' : '"production"'
        }
      }),
      new webpack.HotModuleReplacementPlugin(), //启用热替换模块(Hot Module Replacement),也被称为 HMR
      new webpack.NoEmitOnErrorsPlugin(), //可以确保输出资源不会包含错误
      new webpack.NamedModulesPlugin() //显示文件名而不是id名称
    ]
});

module.exports = config;

配置生产环境文件

指定导出的文件名称chunkhash,使用mini-css-extract-plugin插件进行css提取,包括.vue文件下的css也会被提取。

const path = require('path');
const webpack = require('webpack');
const isDev = process.env.NODE_ENV === 'development';
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config.base.js');

const config = merge(baseConfig, {
    output: {
      filename: '[name].[chunkhash:8].js'
    },
    module: {
      rules: [
        {
          test: /\.styl(us)?$/,
          use: [
            MiniCssExtractPlugin.loader, 
            {
              loader: 'css-loader'
            },
            'stylus-loader'
          ]
        }
      ]
    },
    //代码分割
    optimization: {
      minimize: false, //是否进行压缩,默认是true,这里是为了方便看到编译后的代码
      splitChunks: {
        cacheGroups: { //进行分组,提取公共库
          vendor: { //verdor命名
            test: /node_modules/,
            chunks: 'initial',
            name: 'vendor',
            priority: 10,
            enforce: true
          }
        }
      },
      runtimeChunk: true //生成一份runTime文件,与浏览器缓存相关的
    },
    plugins: [
      new MiniCssExtractPlugin({
        filename: '[name].[contenthash:8].css'
      })
    ]
  })

关于optimization优化这部分,自己理解还不到位,就不进行深入讲解了。
至此,已经完成了所有的配置。
最后,需要到package.json配置对应的命令

"scripts": {
    "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.client.js",
    "clean": "rimraf public",
    "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.client.js",
    "build:clean": "npm run clean && npm run build",
    "lint": "eslint --ext .vue --ext .js --ext .jsx client/",
    "lint-fix": "eslint --fix --ext .vue --ext .js --ext .jsx client/",
    "pre-commit": "echo 'husky' && npm run lint"
  },

首先是dev配置,也就是运行在开发环境中,各种平台设置环境变量有所不同,可以安装cross-env插件进行设置cross-env NODE_ENV=development,启用webpack-dev-server, --config指定配置文件;clean配置, 安装rimraf插件,清除目录及文件;build配置,设置为production,进行打包;还可以进行语法检测lint,语法修复lint-fix;最后一个是关于提交到git上面时候,在Pre-commit这个hook时进行语法检测, 安装插件husky(哈士奇), 这里我一直没能成功进行到语法检测,不知道哪里出了问题,有哪位大佬知道什么原因吗?

总结

webpack的基本配置大致如此,里面还有许多配置可以学习,比如optimization更深度地优化,target: 'server'下的服务端打包,在Node下进行webpack编译,结合happyPack插件进行多线程打包。总而言之,言而总之,只有不断学习新技术,才能跟上大前端的步伐!
如有错误,欢迎在评论区指正或私聊我,谢谢!