[webpack] webpack 食用手册

Webpack 食用手册

还是老老实实写一篇中文的使用手册吧

本篇不讨论原理、模块化之类的,只记录如何使用

目录
  • Webpack 食用手册
    • 安装
    • 创建配置
    • 入口 Entry
    • 输出 Output
      • publicPath
      • chunkFilename
    • 处理器 Loader
      • 三种方式使用 Loaders
      • 特性
      • 常用 loaders
        • css
        • JavaScript
        • 图片 & 字体
    • 插件 Plugin
      • 使用
      • 常用插件:
        • html-webpack-plugin
        • uglifyjs-webpack-plugin
        • mini-css-extract-plugin
        • optimize-css-assets-webpack-plugin
    • 模式 Mode
    • 常用命令
    • devtool属性
    • devServer
    • resolve
    • 一份配置
    • 再来一份

安装

npm init

都安装在本地开发环境 --save-dev

npm install -D webpack webpack-cli

npm install -D webpack-dev-server 安装 webpack 服务器

创建配置

项目根目录下创建webpack.config.js文件

基本大致内容如下

modules.export={
    entry:{
        // 入口文件
    },
    output:{
        // 出口文件
    },
    module:{
        // loaders
        rules:[{},{},{}]
    },
    plugins:[ 
        // 插件 
    ],
    devtool: ...
    devServer: {...}
    resolve:{...}
}

入口 Entry

webpack 创建依赖图的入口文件,递归遍历这些文件,然后用 Loaders 去进行对应处理

官网给了三种方式:

  1. 字符串

    entry: './src/index.js'
    
  2. 对象(多个入口作为属性,都会打包,互不依赖)

    entry: {
      main: './src/app.js',
      vendor: './src/vendor.js'
    }
    

    这里的属性值对应着入口文件的 name

  3. 数组

    entry: ["app.js","main.js"];
    

使用场景:

  • 分离 App 和 Vendor 的入口( App 就是应用主程序, Vendor 可以认为是通用的库,分发给 App 中不同模块使用)
  • 多页应用,每个页面依赖的模块都不同

输出 Output

打包后输出的文件

output: {
  path: path.resolve(__dirname, '/dist'),		// 打包后文件所在的路径
  filename: '[name].[hash].bundle.js',  // 文件名
  publicPath: '/assets/', 	// 上线时的路径,用于线上
  chunkFilename: 'js/[name].bundle.js', 	// 按需加载模块输出的文件名
}

主要来看看下面两个属性

publicPath

这个路径配置之后,例如/assets/,会访问www.xxx.xxx/assets/yyy.js来获取输出的文件

这个其实在之前部署项目的时候遇到过,当初需要将项目部署在主域名的一个子路径下,可以通过 www.coyoo.xyz/wisdom 来访问,在 nginx 中将打包后的文件放在对应wisdom目录即可,然后发现所有获取的静态资源都挂了,因为在index.html中访问的都是没有子路径的资源。

由于使用的是quasarui框架,之后查阅了资料发现需要在配置文件中的build中配置publicPath: '/wisdom',打包也是基于webpack的,所以现在就清楚了。。

chunkFilename

在动态加载(按需加载)的时候,用注释import(/* webpackChunkName: 'chunk'*/chunkModule)来给输出对象指定名字

处理器 Loader

浏览器只认识那三剑客,所以 webpack 可以通过 loaders 将其他语言处理成浏览器认识的语言。能够分析不同类型的文件,反正都认为是模块嘛,甚至可以在 js 模块中import css 和 图片!

基础结构:

module:{
  rules:[
    {
      test:/\.xxx$/,//以 .xxx 结尾的文件
      use: [{loader: 'xxx-loader'}] // 从最后一个往前的顺序执行
      loader: "xxx-loader",
      exclude: {排除的路径},
      include: {包含的路径},
      options: {Loader 配置}
    }
  ]
}

三种方式使用 Loaders

不需要引入,指明名字,配置一下就行

  1. module.rules中配置

    module.exports = {
      module: {
        rules: [
          {
            test: /\.css$/,
            use: [
              // style-loader
              { loader: 'style-loader' },
              // css-loader
              {
                loader: 'css-loader',
                options: {
                  modules: true
                }
              },
              // sass-loader
              { loader: 'sass-loader' }
            ]
            // 这个顺序是从下至上的
          }
        ]
      }
    };
    
  2. 在 import 的时候用 inline 方式:

    import Styles from 'style-loader!css-loader?modules!./styles.css';
    
    // 配置中
    {loader: "style-loader!css-loader?importLoaders=1!less-loader"}
    

    ! 分割 loaders

  3. CLI 的方式

    webpack --module-bind pug-loader --module-bind 'css=style-loader!css-loader
    

特性

  • 可以将加载起链式串起,顺序都是从右往左(数组, inline ),为感觉这个原因可以解释为:正序读取 loaders ,然后处理成loaderA(loaderB(loaderC(loaderD( source ))))
  • 异步/同步执行
  • 插件给 loader 赋能
  • 可以输出任何文件,在 node 环境中可以干很多事情

常用 loaders

css

  1. style-loader

    通过注入style标签将 CSS 添加到 DOM

    npm install style-loader --save-dev
    
  2. css-loader

    css-loader 像 import / require()一样解释@import 和 url()并解析它们。

    npm install css-loader --save-dev
    
  3. postcss-loader

    补充不兼容的 css 属性 的浏览器前缀

    npm install postcss-loader postcss-preset-env cssnano --save-dev
    # 后者会在 package.json 找 browserslist 
    

    package.json: 默认找生产环境(可以设置 process.env.NODE_ENV = 'development')

    "browserslist": {
      "development": [
    	  "last 1 chrome version",
    	  "last 1 firefox version",
        "last 1 safari version",
      ],
      "production": [
        ">0.01%",		// 99.99% 的兼容
        "not dead", // 活着的
        "not op_mini all"
      ]
    }
    

    webpack.config.js

    {
    	loader: 'postcss-loader',
      options: {
        ident: 'postcss',
        plugins: () => [
          require('postcss-preset-env')(),
          require('cssnano')()		// 可以压缩
        ]
      }
    }
    

    可以参考官方https://github.com/postcss/postcss-loader

  4. less/sass-loader

    npm install less --save-dev
    npm install less-loader --save-dev
    

JavaScript

babel

npm install -D babel-core babel-loader babel-preset-es2015

图片 & 字体

  1. file-loader 用于压缩文件

    // module.rules:
    {
      test: /\.(jpg|svg|png|gif|jpeg)$/,
      use: ['file-loader'],
    },
    // ...
    

    字体:

    {
    	test: /\.(woff|woff2|eot|ttf|otf)$/,
    	use: [
    		'file-loader',
    	],
    }
    
  2. url-loader 将会 url 转换成二进制编码(这个包依赖于 file-loader )

    npm install --save-dev url-loader
    
    modules: {
    	rules: [
        {
          test: /\.(jpe?g|png|gif)$/,
          use: [
            {
              loader: 'url-loader',
              options: {
                esModule: false, // 不加的话会有这种情况 img属性src="[object Module]"
                // 图片大小小于10kb的都会转为 base64 编码 来减少http请求
                limit: 1024 * 10, // 当大于100kb时候,将文件打包到publicPath中
                outputPath: 'images', // 将文件打包到哪里
                publicPath: '/images/',
                name: '[name].[hash:8].[ext]'		// 取前 8 位 hash
              }
            }
          ]
        }
      ]
    }
    

    base64 是啥:一种编码数据的方式,直接将图片二进制内容编码到 URL 中,浏览器直接可以从中读出图片,不需要发送请求了。注意,大的图片还是发请求吧, URL 太长了。转换成字符串之后大小还可能会更大。 8 - 12 kb 比较适合。

    最终打包成:data:image/png;base64,............的数据data:;base64,

  3. html-loader

    专门负责引入 html 中的 img 标签,从而能被 url-loader 引入

{
  test: /\.html$/,
  use: 'html-loader',
}

会有小问题, url-loader 默认是 es module 解析的, html-loader 是 commonJS 解析的,关闭URL的 es6 模块化

上面的例子已经关闭了esModule: false。但是好像开了也没问题。。。

如果使用框架啥的,比如 vue ,直接 import 进来放到 data 里面作为字符串,也是会被 url-loader 处理的

插件 Plugin

Plugins are the backbone of webpack.

——来自官网

一个插件是一个 js 对象,必须有 apply 方法

使用

需要引入模块,生成一个对象

plugins: [
  new webpack.ProgressPlugin(),
  new HtmlWebpackPlugin({template: './src/index.html'})
]
plugins: [
  // for customize the option, you need to create an instance
  new HtmlWebpackPlugin({template: './src/index.html'})
]

常用插件:

html-webpack-plugin

主要用于生成 HTML,可以规定 模板 HTML,也可以为 模板传入参数,压缩文件等

npm install --save-dev html-webpack-plugin

配置很多

new htmlWebpackPlugin({
  // 打包后的文件名
  filename: "index.html",
  
  // 模板 复制这个文件 自动引入所有的打包后资源 输出到目标
  template: "index.html",
 
  // 为 true 自动生成 script 标签添加到 html 中
  // 或者写 body/head 标签名
  inject: false,//js 的注入标签
 
  // 通过<%= htmlWebpackPlugin.options.title  %>引用
  // 自动会修改到模版
  title: "参数 title",
 
  //通过<%= htmlWebpackPlugin.options.date %> 引用
  date: new Date()
 
  //网站的图标
  favicon: 'path/to/yourfile.ico'
 
  //生成此次打包的 hash
  //如果文件名中有哈希,便代表有 合理的缓冲
  hash: true,
 
   //排除的块
	excludeChunks: [''],
  
  //选中的块 与入口文件相关
  chunks: ['app','people'],
 
  //压缩
  minify:{ 
   removeComments: true,  // 移除注释
   collapseWhitespace: true,  // 折叠空格
   removeRedundantAttributes: true,
   useShortDoctype: true,
   removeEmptyAttributes: true,
   removeStyleLinkTypeAttributes: true,
   keepClosingSlash: true,
   minifyJS: true,
   minifyCSS: true,
   minifyURLs: true,
  }
 
}),

用 ejs 的语法使用



<%= htmlWebpackPlugin.options.date %>

uglifyjs-webpack-plugin

mini-css-extract-plugin

提取 js 中的 css 样式到文件,在 html 中用 link 引入

npm install -D mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

// 在loader 也要改
{
  test: /\.css$/,
  use: [
    // 'style-loader',  // 不需要创建标签了
    MiniCssExtractPlugin.loader,
    'css-loader'
  ],
},
// plugins 中
plugins: [
  new MiniCssExtractPlugin(),
]  

optimize-css-assets-webpack-plugin

压缩 css 成一行,用 cssnano 在 postcss 中也可以的

// plugins:
new OptimizeCssAssetsWebpackPlugin()

模式 Mode

mode: 'development' // 'production'
选项 描述 特点
development 会设置 process.env.NODE_ENV 为 development ,启用 NamedChunksPlugin 和 NamedModuluesPlugin 让代码能本地调试
production 会设置 process.env.NODE_ENV 为 production ,启用各种优化插件 优化线上环境的代码

生产模式下:

  • css 需要在 js 中分离出来(之前是导入到 js 文件然后生成