通过官方文档复习webpack

127 阅读2分钟

webpack是什么

webpack是一个打包工具,可以将多个模块打包成一个或多个bundle。

webpack的工作流程

① 在package.json可以看到执行npm run build时其实是执行webpack cli的命令,webpack cli会去读取该命令带的参数和读取webpack的配置文件然后合并生成一个options的配置对象。读取命令带的参数使用yargs这个模块,例如yargs.argv。没指定配置文件的话默认会去读webpack.config.js这个文件。

② 载入webpack核心模块,创建compiler对象。let compiler = webpack(options),创建compiler对象时会先去注册配置的plugin,这样后面触发到webpack的生命周期钩子时才能回调到这些plugin。

image.png

③ compiler对象编译项目,执行compiler对象的run方法,开始编译整个项目。根据entry配置找到入口模块,开始依次递归出所有依赖,形成依赖关系树,然后将递归到的每个模块交给不同的loader处理。

image.png

Compilation对象的buidModule方法对模块进行构建,不同的资源使用不同的loader或多个loader进行处理转换,每个资源的最后一个loader输出的是js代码。最后合并生成bundle.js写入dist目录。

webpack的四个核心概念

1.entry 打包入口

module.exports = {
  entry: './path/to/my/entry/file.js'
};

你也可以配置多个入口,在打包多页面应用时这种做法就很有用 `

  entry: {
    pageOne: './src/pageOne/index.js',
    pageTwo: './src/pageTwo/index.js',
    pageThree: './src/pageThree/index.js'
  }

下面是一篇打包多页面应用的文章

webpack多入口文件页面打包配置 - 掘金 (juejin.cn)

2.output 输出

  output: {
    path: path.resolve(__dirname, 'dist'),
    publicPath: "http://cdn.example.com/assets/[hash]/"
    filename: 'my-first-webpack.bundle.js',
  }

当静态资源托管到cdn或者是对象存储如阿里的oss时,就需要配值pulicPath,默认不配置的话是空串。

静态资源最终访问路径 = output.publicPath + 资源loader或插件等配置路径,可以看打包后index.html引入其他资源时的地址查看是否配置成功。

关于开发环境和生产环境如何使用publicpath。可以判断当前环境,对应使用不同的路径。比如开发环境就用./dist 生产环境就用cdn地址。

publicPath: process.env.NODE_ENV === 'production'? config.build.assetsPublicPath : config.dev.assetsPublicPath

3.loader

loader可以处理非js文件,将它们转换成webpack能够处理的模块,接着就可以利用webpack的打包能力对其进行打包了。 `

 module: {
    rules: [
      //意思是在 `require()`/`import` 语句中被解析为 '.txt' 的路径时,在你对它打包之前,先使用`raw-loader` 转换一下
      { test: /.txt$/, use: 'raw-loader' }
    ]
  }

loader 是导出为一个函数的 node 模块。该函数在 loader 转换资源的时候调用。给定的函数将调用 loader API,并通过 this 上下文访问。

一个简单的loader其实就是一个函数,对某种文件使用多个loader其实就是多个函数链式调用。每个函数的参数就是上个loader对资源处理后返回的结果,函数体就是当前loader要做的操作,函数返回值就是当前loader对上个loader传递过来的资源处理后的结果。第一个loader的参数就是你要处理的源文件,最后一个loader的返回值要是js代码。

官网有个例子:对一个text.txt文件里的[name],用loader转成你要替换的文字

module: {
      rules: [{
        //当你import text.txt 时就会先用这个loader对它进行处理
        test: /.txt$/,
        use: {
          loader: path.resolve(__dirname, '../src/loader.js'),
          options: {
            name: 'Alice'
          }
        }
      }]
    }

src/loader.js

//可以在你的loader里使用loader的工具函数,getOptions可以获取你使用该loader时配置的option参数,例如上面的 name: 'Alice'
import { getOptions } from 'loader-utils';

export default function loader(source) {
  const options = getOptions(this);

  source = source.replace(/[name]/g, options.name);

  return `export default ${ JSON.stringify(source) }`;
};

4.plugin

loader被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。有点抽象,后面看看有没有案例解释下。 想要使用一个插件,你只需要 require() 它,然后把它添加到 plugins 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 new 操作符来创建它的一个实例。 `

  plugins: [
    new HtmlWebpackPlugin({template: './src/index.html'})
  ]

下面是我之前写的plugin,目的是为了每次改完项目不用手动部署到服务器,执行npm run build后就会回调相应的钩子帮我自动部署,当然项目部署不该这么做的。

const { NodeSSH } = require('node-ssh')
class AutoUploadPlugin {
    constructor() {
        this.ssh = new NodeSSH()
    }
    async connectServer() {
        await this.ssh.connect({
            host: "*******",
            username: "root",
            password: "*****"
        })
    }
    async uploadFiles(localPath, remotePath) {
        const status = await this.ssh.putDirectory(localPath, remotePath, {
            recursive: true,
            concurrency: 10
        })
        console.log("传送到服务器", status ? "成功" : "失败")
    }
    apply(compiler) {
        compiler.hooks.afterEmit.tapAsync("AutoUploadPlugin", async (compilation, callback) => {
            //获取输出的文件夹
            const outputPath = compilation.outputOptions.path;
            //链接远程服务器
            await this.connectServer()
            //删除原来目录中的内容
            const serverDir = "/usr/share/nginx/vue/dist"
            await this.ssh.execCommand(`rm -rf ${serverDir}/*`)
            //传送到服务器
            await this.uploadFiles(outputPath, serverDir)
            //关闭ssh
            this.ssh.dispose()
        })
    }
}
module.exports = AutoUploadPlugin