2.Webpack

注意此处:内容不全,等待补充...

🗂第一部分:webpack初步探索

1.webpack是什么

  • 要从一种写代码的思想说起,就是把不同的代码拆分成不同的文件,然后进行引用,这样做很好,但是对于web会有一个问题
  • 通过引用的方式,如果引入关系很多的话,前后顺序,依赖关系等等,非常的混乱
  • 后来出现了 CommonJS, CMD, AMD, ESM 的出现,逐渐的解决了这个问题
  • 如果你真的体会过前端的原始传统的写法,你就会明白,人力是多么的渺小和无力
// ES Module 导出
export default {

}

// 引入
import {} from ...

基本使用

  • 安装webpack: npm i webpack webpack-cli --save-dev
  • npx webpack index.js 就会自动识别 import 这些依赖,然后也会自动压缩到 dist/main.js(这是webpack的自动设置,我记得我之前用的时候还需要自己配置)
  • ❣️ 注意:npx 会找 node_modules 下面的,就会执行了, webpack-cli 的作用是,能让我们在命令行中运行 webpack

2.什么是模块打包工具?

  • 你可以会认为webpack是一个JS的翻译器,错了,webpack其实只认识 import ,还是为了寻求依赖才具有的这个功能,真的JS翻译器是 babel
  • webpack 是一个 模块打包工具(也能识别 CommonJS, CMD, AMD 规范的代码)

3.使用webpack的配置文件(输入 && 输出 && mode)

  • 安装好了之后,默认 npx webpack index.js 就能进行打包了(webpack 4.x 默认值功能)
  • 新建文件夹:webpack.config.js
import path from 'path'

export default {

    // 环境(默认是production || development是开发环境,不压缩...)
    mode: 'production',

    // 入口
    entry: {
        main: '入口文件'
    },

    // 出口
    output: {
        filename: '输入文件名',
        path: path.resolve(__dirname, '输入文件夹')
    },

    // ......

}

📕第二部分:loader

1.示例 如何打包图片

  • webpack刚开始写的时候,是只想做一个JS打包工具,后来才有的打包所有的功能,而除了打包JS之外的功能,其余的功能都需要来进行配置,例如 打包图片,就需要在 module 进行配置
  • npm i file-loader -D
const path = require('path');

module.exports = {

  // 环境(默认是production || development是开发环境,不压缩...)
  mode: 'production',

  // 入口
  entry: {
      main: '入口文件'
  },

  // 出口
  output: {
      filename: '输入文件名',
      path: path.resolve(__dirname, '输入文件夹')
  },

  // 模块规则
  module: {
    rules: [
      {
        test: /\.jpg$/,
        use: {
          loader: 'file-loader'
        }
      }
    ]
  }

};
  • ❣️ 思考:file-loader 会把文件放到输出文件夹中,然后换个名字,然后返回名称
  • 我们在Vue中,会有 .vue 的文件,这时候就需要 在 module 中配置 .vue 的 loader 就可以了

2.使用Loader打包静态资源(图片篇)

file-loader插件

module.exports = {
  // 模块规则
  module: {
    rules: [
      {
        test: /\.(jpg|png|gif)$/,
        use: {
          loader: 'file-loader',
          options: {
            name: '[name].[ext]', // 
            outputPath: 'images/' // 打包输出文件夹
          }
        }
      }
    ]
  }

}

url-loader

  • 安装:npm i url-loader --save-dev
  • url-loader 能实现所有 file-loader的功能,配置也不用变
  • 不同的地方在于,url-loader 会把你的图片转为 base64 的字符串(但是这样如果图片很大的话,会导致页面JS也很大)
  • 最佳的实践是,如果图片小的话,就用 url-loader 打包,图片很大的话,就打包到文件夹中
module.exports = {
  // 模块规则
  module: {
    rules: [
      {
        test: /\.(jpg|png|gif)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name].[ext]', // 
            outputPath: 'images/', // 打包输出文件夹
            limit: 2048 // 如果小于 2kb,就会打包进JS,如果大于就打包到文件夹中
          }
        }
      }
    ]
  }

}

3.使用loader打包静态资源(样式篇)

  • npm i css-loader style-loader --save-dev
  • css-loader:会分析几个CSS之间的依赖关系,最后帮我们打包成一个css,也就是说,css里面可以像JS一样,通过CSS进行引用了
  • style-loader:把css-loader生成的CSS内容加载到 HTML 里面的 header 里面
module.exports = {

  // ...

  module: {
    rules: [
      // ...
      {
        test: '/\.css$/',
        use: ['style-loader', 'css-loader']
      }
    ]
  }

}

如果用其他的语法,例如 scss

  • 安装:npm i node-sass sass-loader --save-dev
module.exports = {

  // ...

  module: {
    rules: [
      // ...
      {
        test: '/\.scss$/',
        use: ['style-loader', 'css-loader', 'sass-loader']
      }
    ]
  }

}

❣️:loader 执行顺序是,从上到下,从右到左的顺序


加入厂商前缀的loader:postcss-loader

  • npm i postcss-loader --save-dev
  • 需要创建一个 postcss.config.js 文件
  • 下载自动添加前缀的插件:npm i autoprefixer -save-dev
// 默认是这样写的
module.exports = {
  parser: 'sugarss',
  plugins: {
    'postcss-import': {},
    'postcss-cssnext': {},
    'cssnano': {}
  }
}

// 我们也可以这么写
// 这么写的作用是,当我们使用 postcss-loader 的时候,会使用 autoprefixer 这个插件
module.exports = {
  plugins: [
    require('autoprefixer')
  ]
}
module.exports = {

  // ...

  module: {
    rules: [
      // ...
      {
        test: '/\.scss$/',
        use: ['style-loader', 'css-loader', 'sass-loader', 'postcss-loader']
      }
    ]
  }

}

我们在上面处理静态样式文件,写了四个loader,如果我们想给这个文件进行配置呢?例如给 css-loader 进行配置呢?

module.exports = {

  // ...

  module: {
    rules: [
      // ...
      {
        test: '/\.scss$/',
        use: ['style-loader', {
          loader: 'css-loader',
          options: {
            importLoaders: 2 // 这个表示,我们在css中引入的 文件,不管是 css也好还是scss也好,都要先走前面两个文件,也就是 postcss-loader, sass-loader
          }
        }, 'sass-loader', 'postcss-loader']
      }
    ]
  }

}

css模块(css只作用于一个模块,类似于Vue的 spoct)

module.exports = {

  // ...

  module: {
    rules: [
      // ...
      {
        test: '/\.scss$/',
        use: ['style-loader', {
          loader: 'css-loader',
          options: {
            importLoaders: 2,
            modules: true // 开启css的模块化打包
          }
        }, 'sass-loader', 'postcss-loader']
      }
    ]
  }

}


// 使用
import style from 'index.scss'

btn.style = style.red

如何打包字体文件

  • 主要用 file-loader 进行打包,将字体文件放到合适的目录下

📗第三部分:plugins(插件)

1.使用 plugins 让打包更便捷

使用第一个插件:html-webpack-plugin

  • npm i html-webpack-plugin --save-dev
  • 功能:会在打包完成之后,自动生成一个HTML文件,并把打包之后的JS自动引入到这个HTML文件中
// 先引入,再实例化
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  // 先开发模式,等做完了再生产模式
  mode: 'development',
  
  entry: './src/vue.js',
  
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'vue.js'
  },

  // 插件
  plugins: [
    new HtmlWebpackPlugin({
      // 配置,index.html 模板,以此为模板进行注入
      template: './test/index.html'
    })
  ]

}


🔥 plugin解释

  • plugin可以在webpack运行到某个时刻的时候,帮你做一些事情(有点像Vue的生命周期函数)
  • 例如:html-webpack-plugin 是在打包结束之后,帮我们做一些事情

介绍另一个插件:clean-webpack-plugin

  • 安装:npm i clean-webpack-plugin --save-dev
const htmlWebpackPlugin = requiere('html-webpack-plugin ')
const cleanWebpackPlugin = require('clean-webpack-plugin')


module.exports = {
  // 先开发模式,等做完了再生产模式
  mode: 'development',
  
  entry: './src/vue.js',
  
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'vue.js'
  },

  // 插件
  plugins: [
    new htmlWebpackPlugin({
      // 配置,index.html 模板,以此为模板进行注入
      template: 'src/index.html'
    }),
    new cleanWebpackPlugin(['dist'])
  ]

}


📘第四部分:其他配置

1.entry 与 output


module.exports = {

  entry: {
    main: './src/index.js',
    main1: './src/index.js'
  },
  
  output: {
    filename: '[name].js',
    publicPath: 'https://cdn.com.cn', // 配置引入前缀
    path: path.resolve(__dirname, 'dist')
  },

  // 会打包成 main.js, main1.js,还有更多,就要查阅官方文档了

}



2.SourceMap配置

  • 为什么要有这个东西,因为我们打包之后,所有的都在一个JS中,如果发生错误,调试的话,没有办法准确定位到是哪里的问题
  • SourceMap 是一种映射,它知道我们打包之后的文件每一行,是源文件中的哪一行
  • 解决了我们打包之后,还能正确的找到具体代码

module.exports = {
  
  // 先开发模式,等做完了再生产模式
  mode: 'development',
  
  // 使用这个就可以了,这个devtool 还有很多配置,具体查阅文档
  devtool: 'cheap-module-eval-source-map',

  entry: './src/vue.js',
  
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'vue.js'
  },

  // ...
}

3.使用WebpackDevServer提高开发效率

  • 我们更改了文件
  • 解决方法一: webpack --watch
  • 解决方法二:webpackDevServer
  • 安装:npm i webpack-dev-server --save-dev
module.exports = {
  
  // 启动一个serve
  devServer: {
    contentBase: './dist',
    open: true
  }, 

  // ...
  
}

4.Hot Module Replacement 热模块更新

  • 为什么有这个功能呢?
  • 因为当我们启动 webpackDevServe 或者 --watch 之后都会有一个问题,就是一改变文件,就会重新刷新了,我们之前的一系列操作都不行
const webpack = require('webpack')

module.exports = {
  // 启动一个serve
  devServer: {
    contentBase: './dist',
    open: true,
    hot: true,
    hotOnly: true
  },

  plugins: [
    new webpack.HotModuleReplacementPlugn()
  ]

  // ...

}

5.使用babel处理ES6语法

首选方案一:建立.babelrc文件夹



方案二:使用webpack插件进行配置

  • 安装:npm i babel-loader @babel/core --save-dev
    • @babel/core
    • babel-loader:webpack 与 babel 做通信的桥梁
  • 安装:npm i @babel/preset-env --save-dev(这个是真正ES6转ES5的所有规则)
// ❣️ 我们这里只能转换语法,说白了只能转换 ES6 中的语法糖,转不了 浏览器内置的一些东西,例如 Promise 等等,这就需要 Polyfill
module.exports = {

  module: {
    rules: [
      {
        test: '/\.js$/',
        exclude: '/node_modules',
        loader: 'babel-loader',
        options: {
          presets: ['@babel/preset-env']
        }
      }
    ]
  },

  // ...
}

  • 安装:npm i @babel/polyfill --save

📙第五部分:webpack高级

1.Tree Shaking概念

我引入什么,就让webpack打包什么,(Tree Shaking 只支持ES Module)

module.exports = {

}

🗂第六部分:Webpack 实战配置案例讲解


🗓第七部分:Webpack 底层原理及脚手架工具分析

Loader 和 Plugin 的区别

Loader:处理引用的文件,帮助我们处理模块 Plugin:打包的过程中,有些时刻你想做一些事情,这个时候是插件生效的时刻


1.插件的基本书写

  • npm i webpack webpack-cli -D

书写一个基础webpack 配置文件

// webpack.config.js
const path = require('path')

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  }
}
// /plugins/plugins-test.js
class PluginsTest {
  constructor() {}

  // compiler 这个可以理解成 webpack 实例
  apply(compiler) {

  }
}

// 这就表明了,为什么 webpack plugins 都需要 new,因为 插件都是 class
module.exports = PluginsTest

插件基本写法,没写完

const path = require('path')
const PluginsTest = require('./plugins/plugins-test.js')

module.exports = {
  mode: 'development',
  entry: {
    main: './src/index.js'
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js'
  },
  plugins: [
    new PluginsTest({
      name: '朱昆鹏'
    })
  ]
}
class PluginsTest {
  constructor(opt) {
    console.log('插件被使用了', opt)
  }

  // compiler 这个可以理解成 webpack 实例
  apply(compiler) {
    // hooks 在某一时刻会自动执行的函数
    // compilation 存放跟这次打包相关的所有内容
    // 同步使用 tap | 异步使用 tapAsync(异步需要多传一个cb参数,并执行)
    compiler.hooks.compilation.tap('PluginsTest', (compilation) => {
      console.log(compilation.assets['main.js'])
      // cb()
      compilation.hooks.optimizeChunkAssets.tapAsync('PluginsTest', (chunks, cb) => {
        // console.log(chunks[0])
        // wrapChunks(compilation, chunks);
        cb();
      })
    })
  }
}

module.exports = PluginsTest