webpack入门

87 阅读3分钟

webpack

默认配置文件(webpack.config.js)

const path = require('path')

module.exports = {
  entry: './src/main.js', // 入口文件, 需要加上./
  mode: 'development',  // 三种取值,production、development 和 none
  // production 生产模式下,Webpack 会自动优化打包结果;
  // development Webpack 会自动优化打包速度,添加一些调试过程中的辅助
  // none 就是运行最原始的打包,不做任何额外处理
  output: {
    filename: 'bundle.js', // 打包之后的文件名
    path: path.join(__dirname, 'output') // 打包之后文件存放的绝对路径
  },
  module: {
    rules: [ // 配置不同的loader, loader执行顺序,默认从下到上,从右到左
      {
        test: /.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      {
        test: /.png$/,
        use: 'file-loader'
      }
    ]
  }
}

常用loader

  1. css处理

    import './main.css'
    // webpack.config.js
    {
      test: /.css$/,
        use: [
          'style-loader', //动态创建style标签,将css-loader处理返回的结果添加进去,并append到head中
          'css-loader' // 处理css文件, 最终返回 module.exports = 'css code'
        ]
    },
    
  2. 图片处理

    import icon from './icon.png'
    // webpack.config.js
    {
       test: /.png$/,
       use: 'file-loader' // 将图片拷贝纸output.path执行的目录中,并返回对应的路径
    }
    {
      test: /.png$/,
        use: {
          loader: 'url-loader', // 依赖于file-loader,在file-loader的基础上进行了优化,如果图片体积小于指定的limit,则转换换成base64,减少http请求
            options: {
              limit: 10 * 1024 // 10 KB
            }
        }
    }
    
  3. js处理

    import createHeading from './heading.js'
    // webpack.config.js
    {
      test: /.js$/,
        use: {
          loader: 'babel-loader', // webpack只是一个模块打包的工具,默认只支持js的打包,并且不会做语法转换,如果js中使用了es6的语法,需要使用babel进行转换, babel的配置选项一般都是放在一个独立的.babelrc文件中
            options: {
              presets: ['@babel/preset-env']
            }
        }
    },
    

常用的plugin

  1. CleanWebpackPlugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
plugins: [
  new CleanWebpackPlugin(), // 默认打包前清除dist目录
]
  1. HtmlWebpackPlugin

    const HtmlWebpackPlugin = require('html-webpack-plugin')
    new HtmlWebpackPlugin({ // 多页应用可以 new 多次,每次都会生成单独的文件
      title: 'Webpack Plugin Sample', // index.html中可以使用ejs语法替换页面title
      meta: {
        viewport: 'width=device-width' // 动态创建meta标签
      },
      template: './src/index.html' // 基于这个模板文件生成html
    }),
    
  2. CopyWebpackPlugin

    const CopyWebpackPlugin = require('copy-webpack-plugin')
    new CopyWebpackPlugin({
      patterns: [
        'public' // 将制定目录下面的文件,copy到打包生成目录中
      ]
    })	
    
  3. DefinePlugin, 定义变量

    // webapck.config.js
    const webpack = require('webpack')
    ... 
    plugins: [
      new webpack.DefinePlugin({
        // 值要求的是一个代码片段
        API_BASE_URL: JSON.stringify('https://api.example.com')
        // API_BASE_URL: 'abc', 千万不能这么写,有坑
      })
    ]
    // main.js
    console.log(API_BASE_URL)
    

webpack-dev-server

devServer: {
  contentBase: './public', // 可以是一个数组
    proxy: { // 本地开发环境设置代理
      '/api': {
        // http://localhost:8080/api/users -> https://api.github.com/api/users
        target: 'https://api.github.com',
          // http://localhost:8080/api/users -> https://api.github.com/users
          pathRewrite: {
            '^/api': ''
          },
            // 不能使用 localhost:8080 作为请求 GitHub 的主机名
            changeOrigin: true
      }
    }
},

dev-tool

推荐生产使用none,开发环境使用cheap-module-eval-source-map,关于不同模式的比较,可以参考github仓库看了仔细了解,传送门

devtool: 'xxx' 
// webpack 提供很多很多种source-map,不用类型具有不同的特点,可以基于几种基本的类型进行组合
// 1. eval sourcemap跟打包后的代码被包裹在eval函数中
// 2. inline sourcemap被转换成base64包含在打包文件中, 体积会很大
// 3. cheap 错误只有行号,没有列
// 4. module 带缓存,不转换语法,最大程度还原原始文件
// 5. hidden 有声场source-map,但是没有引用
// 6. nosources 有具体的报错位置,源代码指向的是一个空白文件

Hot Module Replace

  1. css的热替换

    style-loader处理了,css文件内容一旦变化,会重新将变化后的内容去替换已经append到head中的style标签里的内容

  2. js的热替换

    因为js的场景很多,不同的模块使用的方法和功能都不一样,所以不好统一处理,想要js也具有HMR功能,需要我们自己单独实现。

    import createEditor from './editor'
    import background from './better.png'
    import './global.css'
    
    const editor = createEditor()
    document.body.appendChild(editor)
    
    const img = new Image()
    img.src = background
    document.body.appendChild(img)
    
    // ============ 以下用于处理 HMR,与业务代码无关 ============
    
    // console.log(createEditor)
     // 此处的代码打包之后,会变成 if(false) {}
    if (module.hot) {
      let lastEditor = editor
      module.hot.accept('./editor', () => {
        // console.log('editor 模块更新了,需要这里手动处理热替换逻辑')
        // console.log(createEditor)
    
        const value = lastEditor.innerHTML
        document.body.removeChild(lastEditor)
        const newEditor = createEditor()
        newEditor.innerHTML = value
        document.body.appendChild(newEditor)
        lastEditor = newEditor
      })
    
      module.hot.accept('./better.png', () => {
        img.src = background
        console.log(background)
      })
    }
    

tree-shaking(去掉 dead code)

tree-shaking是基于esmodule特性实现的,所以,要是tree-shaking能够很好的工作,尽量使用符合esmodule规范的代码,webpack只有在mode=production才开始treeshaking

// component.js
export const Button = () => {
  return document.createElement('button')

  console.log('dead-code')
}

export const Link = () => {
  return document.createElement('a')
}

export const Heading = level => {
  return document.createElement('h' + level)
}

// index.js
import { Button } from './components'

document.body.appendChild(Button())

// 打包时候的bundle.js是不会包含Link, Heading以及 console.log('dead-code')

splitChunks

 optimization: {
    splitChunks: {
      // 自动提取所有公共模块到单独 bundle
      chunks: 'all'
    }
  }