webpack今后再也不是咱们的痛点 — 核心基础

webpack一直是前端工程师的痛点,由于他的复杂、分散、loader、plugin这些第三方,让咱们的学习成本陡然上升,使咱们一直对他的配置模棱两可,今天带你们完全明白他如何配置,摆脱困扰咱们好久的痛点。本篇主要是webpack基础配置详解,关于webpack的模块chunk、编译阶段流程、输出阶段流程、loader的编写和手写plugin会在后续文章推出,为了不错过能够关注我或者收藏个人我的博客www.ngaiwe.comcss

1.webpack是什么?

WebPack能够看作是模块打包机:它作的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式以供浏览器使用。而且跟具你的在项目中的各类需求,实现自动化处理,解放咱们的生产力html

  • 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 。
  • 文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。
  • 代码分割:提取多个页面的公共代码、提取首屏不须要执行部分的代码让其异步加载。
  • 模块合并:在采用模块化的项目里会有不少个模块和文件,须要构建功能把模块分类合并成一个文件。
  • 自动刷新:监听本地源代码的变化,自动从新构建、刷新浏览器。
  • 代码校验:在代码被提交到仓库前须要校验代码是否符合规范,以及单元测试是否经过。
  • 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统。

2.项目初始化

mkdir webpack-start
cd webpack-start
npm init
复制代码

3.webpack核心概念

  • Entry:入口,webpack执行构建的第一步将从Entry开始,可抽象理解为输入
  • Module:模块,在webpacl中一切皆为模块,一个模块对应一个文件,webpack会从配置的Entry开始递归找出全部依赖的模块
  • Chunk:代码块,一个chunk由多个模块组合而成,用于将代码合并和分割
  • Loader:模块转换器,用于把模块原内容按照需求转换为须要的新内容
  • Plugin:扩展插件,在webpack构建流程中的特定时机注入扩展逻辑来改变构建结果和想要作的事情
  • Output:输入结果,在webpack通过一系列处理并获得最终想要的代码而后输出结果

Webpack 启动后会从 Entry 里配置的 Module 开始递归解析 Entry 依赖的全部 Module。 每找到一个 Module, 就会根据配置的Loader去找出对应的转换规则,对 Module 进行转换后,再解析出当前 Module 依赖的 Module。 这些模块会以 Entry 为单位进行分组,一个 Entry 和其全部依赖的 Module 被分到一个组也就是一个 Chunk。最后 Webpack 会把全部 Chunk 转换成文件输出。 在整个流程中 Webpack 会在恰当的时机执行 Plugin 里定义的逻辑。前端

1.Entry

context用来解决配置文件和入口文件再也不同一层结构,列如咱们配置文件在config,入口文件在根目录,则以下配置node

module.exports = {
  context: path.join(__dirname, '..'), // 找到根目录
  entry: './main.js' //根目录下的入口文件
}
复制代码

最简单的单页面(SPA)Entry入口,将main.js引入,并根据main.js中引用和依赖的模块开始解析react

module.exports = {
  entry: './main.js'
}
复制代码

多页面(MPA)Entry入口,将多个文件引入,固然通常是读取指定文件夹内的入口文件,而后引入jquery

entry: {
  home: "./home.js",
  about: "./about.js",
  contact: "./contact.js"
}
复制代码

若是是单页面(传入的是字符串或字符串数组),则chunk会被命名为main,若是是多页面(传入一个对象),则每一个键(key)会是chunk的名称,描述了chunk的入口起点webpack

2.Output

Object 指示webpack如何去输出,以及在哪里输出你的bundle、asset 和其余你所打包或使用 webpack 载入的任何内容web

  • path:输出目录对应一个绝对路径正则表达式

    path: path.resolve(__dirname, 'dist')
    复制代码
  • pathinfo:boolean 默认false做用是告诉webpack在bundle中引入所包含模块信息的相关注释,不该用于生产环境(production),对开发环境(development)极其有用npm

  • publicPath:主要做用是针对打包后的文件里面的静态文件路径处理

  • filename:定义每一个输出bundle的名称,这些bundle将写入output.path选项指定的目录下,对于单入口Entry,filename是一个静态名称

    filename: "bundle.js"
    复制代码

    可是在webpack中咱们会用到代码拆分、各类插件plugin或多入口Entry建立多个bundle,这样咱们就应该给每一个bundle一个惟一的名称

    filename: "[name].bundle.js"
    复制代码

    使用内部chunk id

    filename: "[id].bundle.js"
    复制代码

    惟一hash生成

    filename: "[name].[hash].bundle.js"
    复制代码

    使用基于每一个 chunk 内容的 hash

    filename: "[chunkhash].bundle.js"
    复制代码
3.Module模块

处理项目中应用的不一样模块,主要配置皆在Rules中,匹配到请求的规则数组,这些规则可以对模块应用loader,或者修改解析器parser

  • Module.noParse: 防止webpack解析的时候,将规则匹配成功的文件进行解析和忽略大型的library来对性能的优化,在被忽略的文件中不该该含有import、require和define的调用

    module.exports = {
      module: {
        rules: [],
        noParse: function(content) {
          return /jquery|lodash/.test(content) // 忽略jquery文件解析,直接编译打包
        }
      }
    }
    复制代码
  • Rules:建立模块时,匹配请求的规则数组

    • Rule条件:resource(请求文件的绝对路径)、issuer(被请求资源的模块文件的绝对路径,导入时的位置),好比一个文件A导入文件B,resource是/B,issuer是/A是导入文件时的位置,而不是真正的位置,在规则中,test/include/exclude/resource对resource匹配,而issuer只对issuer匹配

    • Test/include/exclude/resource/issuer的用法和区别

      module.exports = {
          modules: {
              rules: [
                {
                  test: /\.js?$/,
                  include: [
                    path.resolve(__dirname, "app")
                  ],
                  exclude: [
                    path.resolve(__dirname, "app/demo")
                  ],
                  resource:{
                    test: /\.js?$/,
                    include: path.resolve(__dirname, "app"),
                    exclude: path.resolve(__dirname, "app/demo")
                  },
                  issuer: {
                    test: /\.js?$/,
                    include: path.resolve(__dirname, "app"),
                    exclude: path.resolve(__dirname, "app/demo")
                  }
                }
              ]
        }
      }
      复制代码

      test:通常是提供一个正则表达式或正则表达式的数组,绝对路径符合这个正则的则意味着知足这个条件

      include:是一个字符串或者字符串数组,指定目录中的文件须要走这个规则

      exclude:一样是一个字符串或者字符串数组,指定目录中的文件不须要走这个规则

      resource:就是对text/include/exclude的一个对象包装,和他们单独写没有区别

      issuer:和resource有殊途同归的做用,不过区别在于它是将这个rule应用于哪一个文件以及这个文件所导入的全部依赖文件

    • resourceQuery:和resource用法同样,不过针对的是匹配结果'?'后面的路径参数,能够调用resource中的text等

    • oneOf:表示对该资源只应用第一个匹配的规则,通常结合resourceQuery

      {
        test: /\.(png|jpe?g|gif|svg)$/,
        oneOf: [
          {
            resourceQuery: /inline/, 
            loader: 'url-loader'
          },
          {
            loader: 'file-loader'
          }
        ]
      }
      复制代码
      • path/to/foo.png?inline: 会匹配url-loader
      • path/to/foo.png?other:会匹配file-loader
      • path/to/foo.png: 会匹配file-loader
    • useEntry:object包含着每个loader而且对应loader的配置文件

      {
        loader: "css-loader",
        options: {
          modules: true
        }
      }
      复制代码

      options会传入loader,能够理解为loader的选项

    • use:是对useEntry的集合,而且对每个入口指定使用一个loader

      use: [
        'style-loader',
        {
          loader: 'css-loader',
          options: {
            importLoaders: 1
          }
        },
        {
          loader: 'less-loader',
          options: {
            noIeCompat: true
          }
        }
      ]
      复制代码
4.Resolve解析

主要用来模块如何被解析,给webpack提供默认值

  • alias:object主要用来让import和require调用更方便,设置初始路径

    module.exports = {
     alias: {
      Utilities: path.resolve(__dirname, 'src/utilities/'),
      Templates: path.resolve(__dirname, 'src/templates/')
     }   
    }
    // 最开始的import
    import Utility from '../../utilities/utility';
    // 配置完之后
    import Utility from 'Utilities/utility';
    复制代码
  • enforceExtension:Boolean 默认false,表示引用不须要扩展名,为true时,import、require中引用必须加扩展名

  • extensions:Array 自动解析不须要扩展名

    extensions: [".js", ".json"]  // .js、.json引入不须要扩展名
    复制代码
  • modules:Array webpack解析模块的时候须要搜索的目录,通常用于优先搜索和非node_modules文件中的自定义模块

    modules: [path.resolve(__dirname, "src"), "node_modules"] //优先搜索src目录
    复制代码
5.Loader

经过使用不一样的Loader,Webpack能够要把不一样的文件都转成JS文件,好比CSS、ES6/七、JSX等,通常用于module的use中

module: {
  rules:[
      {
        test:/\.css$/,
        use:['style-loader','css-loader'],
        include:path.join(__dirname,'./src'),
        exclude:/node_modules/
      }
  ]      
}
复制代码

具体相关loader须要查看你要引入的loader官方文档API,手写Loader会在下一篇文章具体介绍

6.Plugin插件

Array 扩展webpack,在webpack构建流程中的特定时机注入扩展逻辑来改变构建结果和想要作的事情,具体使用查看你引入的plugin官方文档,手写plugin会在后续文章中推出

7.webpack-dev-server

开发中的server,webpack-dev-server能够快速搭建起本地服务,具体使用查看 webpack-dev-server

8.Devtool

此选项控制是否生成,以及如何生成,官方推荐 SourceMapDevToolPluginsource-map-loader 建议看官方文档 Devtool 主要用来控制打包品质和在dev环境的调试便捷度和编译的快慢

9.Watch

webpack 能够监听文件变化,当它们修改后会从新编译和 HotModuleReplacementPlugin 有类似之处,监听文件变更热启动

4.配置webpack

webpack安装命令

npm install webpack webpack-cli -D
复制代码

Webpack.config.js

具体用到的plugin插件

  • clean-webpack-plugin:用于打包前清空输出目录 官方API
  • html-webpack-plugin:用于自动产出HTML和引用产出的资源 官方API
  • copy-webpack-plugin:用于拷贝静态资源,包括未被引用的资源 官方API
  • uglifyjs-webpack-plugin:用于压缩JS可让输出的JS文件体积更小、加载更快、流量更省,还有混淆代码的加密功能 官方API
  • extract-text-webpack-plugin:由于CSS的下载和JS能够并行,当一个HTML文件很大的时候,咱们能够把CSS单独提取出来加载 官方API
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const UglifyjsWebpackPlugin = require('uglifyjs-webpack-plugin')
// npm i extract-text-webpack-plugin@next // @next能够安装下一个非正式版本
const ExtractTextWebpackPlugin = require('extract-text-webpack-plugin');
let cssExtract = new ExtractTextWebpackPlugin({
    filename: 'css/css.css',
    allChunks: true
});
let lessExtract = new ExtractTextWebpackPlugin('css/less.css');
let sassExtract = new ExtractTextWebpackPlugin('css/sass.css');
/**
 * 有些时候咱们但愿把页面中的CSS文件单独拉出来保存加载
 * extract-text-webpack-plugin
 */
//let pages = ['index', 'base'];
// pages = pages.map(page => new HtmlWebpackPlugin({
//     template: './src/index.html',//指定产的HTML模板
//     filename: `${page}.html`,//产出的HTML文件名
//     title: `${page}`,
//     chunks: ['common', `${page}`],//在产出的HTML文件里引入哪些代码块
//     hash: true,// 会在引入的js里加入查询字符串避免缓存,
//     minify: {
//         removeAttributeQuotes: true
//     }
// }));
module.exports = {
    //先找到每一个入口(Entry),而后从各个入口分别出发,找到依赖的模块(Module),
    //而后生成一个Chunk(代码块),最后会把Chunk写到文件系统中(Assets)   
    entry: './src/main.js',
    output: {
        path: path.join(__dirname, 'dist'),//输出的文件夹,只能是绝对路径 
        //name是entry名字main,hash根据打包后的文件内容计算出来的一个hash值
        filename: '[name].[hash].js' //打包后的文件名
    },
    resolve: {
        //引入模块的时候,能够不用扩展名 
        extensions: [".js", ".less", ".json"],
        alias: {//别名
            "bootstrap": "bootstrap/dist/css/bootstrap.css"
        }
    },
    //表示监控源文件的变化,当源文件发生改变后,则从新打包
    watch: false,
    watchOptions: {
        ignored: /node_modules/,
        poll: 1000,//每秒钟询问的次数
        aggregateTimeout: 500//
    },
    //devtool: 'source-map',//单独文件,能够定位到哪一列出错了
    // devtool: 'cheap-module-source-map',//单独文件,体积更小,但只能定位到哪一行出错
    // devtool: 'eval-source-map',//不会生成单独文件,
    // devtool: 'cheap-module-eval-source-map',//不会生成单独文件 只定位到行,体积更小
    /*
    loader有三种写法
    use
    loader
    use+loader
    * */
    module: {
        rules: [
            {
                test: require.resolve('jquery'),
                use: {
                    loader: 'expose-loader',
                    options: '$'
                }
            },
            {
                test: /\.js/,
                use: {
                    loader: 'babel-loader',
                    query: {
                        presets: ["env", "stage-0", "react"]
                    }
                }
            },
            {
                //file-loader是解析图片地址,把图片从源位置拷贝到目标位置而且修改原引用地址
                //能够处理任意的二进制,bootstrap 里字体
                //url-loader能够在文件比较小的时候,直接变成base64字符串内嵌到页面中
                test: /\.(png|jpg|gif|svg|bmp|eot|woff|woff2|ttf)/,
                loader: {
                    loader: 'url-loader',
                    options: {
                        limit: 5 * 1024,
                        //指定拷贝文件的输出目录 
                        outputPath: 'images/'
                    }
                }
            },
            {
                test: /\.css$/,//转换文件的匹配正则
                //css-loader用来解析处理CSS文件中的url路径,要把CSS文件变成一个模块
                //style-loader 能够把CSS文件变成style标签插入head中
                //多个loader是有顺序要求的,从右往左写,由于转换的时候是从右往左转换
                //此插件先用css-loader处理一下css文件
                //若是压缩
                loader: cssExtract.extract({
                    use: ["css-loader?minimize"]
                })
                //loader: ["style-loader", "css-loader", "postcss-loader"]
            },
            {
                test: /\.less$/,
                loader: lessExtract.extract({
                    use: ["css-loader?minimize", "less-loader"]
                })
                //use: ["style-loader", "css-loader", "less-loader"]
            },
            {
                test: /\.scss$/,
                loader: sassExtract.extract({
                    use: ["css-loader?minimize", "sass-loader"]
                })
                // use: ["style-loader", "css-loader", "sass-loader"]
            },
            {
                test: /\.(html|htm)/,
                loader: 'html-withimg-loader'
            }
        ]
    },
    plugins: [
        //用来自动向模块内部注入变量
        // new webpack.ProvidePlugin({
        //     $: 'jquery'
        // }),
        new UglifyjsWebpackPlugin(),
        new CleanWebpackPlugin([path.join(__dirname, 'dist')]),
        //此插件能够自动产出html文件
        new HtmlWebpackPlugin({
            template: './src/index.html',//指定产的HTML模板
            filename: `index.html`,//产出的HTML文件名
            title: 'index',
            hash: true,// 会在引入的js里加入查询字符串避免缓存,
            minify: {
                removeAttributeQuotes: true
            }
        }),
        new CopyWebpackPlugin([{
            from: path.join(__dirname, 'public'),
            to: path.join(__dirname, 'dist', 'public')
        }]),
        cssExtract,
        lessExtract,
        sassExtract
    ],
    //配置此静态文件服务器,能够用来预览打包后项目
    devServer: {
        contentBase: './dist',
        host: 'localhost',
        port: 8000,
        compress: true,//服务器返回给浏览器的时候是否启动gzip压缩
    }
}
复制代码

5.总结

本篇偏向基础,可以搭建起简单的webpack配置,高级进阶会在后续文章推出,而且但愿你们多去看官方API而后自我总结输出,只有将知识输出出来,才能更好的记忆和学习

6.博客

魏燃技术博客

有任何问题可留言或者发送本人邮箱ngaiwe@126.com

相关文章
相关标签/搜索