黑马程序员技术交流社区

标题: 长沙黑马精品公开课:模块化打包神器webpack [打印本页]

作者: 长沙-小知姐姐    时间: 2020-4-28 15:43
标题: 长沙黑马精品公开课:模块化打包神器webpack
本帖最后由 长沙-小知姐姐 于 2020-4-28 15:44 编辑

需要视频笔记源码的朋友可以回帖获取网盘密码(如失效请联系vx:csheima7)
https://pan.baidu.com/s/1J2QCSy1d7lbQh4uFjAKG-w
[hide=d100]jj4w[/hide]


###1.webpack的概念
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

通俗的来说,webpack就是一个打包构建工具,将各种各样的资源整合打包到一起,形成一个最终的完整项目。

###2.webpack的基本配置
####A.安装webpack
npm install webpack webpack-cli -D
####B.在项目目录中创建webpack.config.js文件用来配置webpack
在配置文件中可以配置开发模式和发布模式两种,如下:
module.exports = {
    mode:"development"//可以设置为development(开发模式),production(发布模式)
}

也可以在运行webpack命令时,添加参数设置模式,如下:
webpack --mode=development

####C.在webpack.config.js文件中通过entry和output来设置入口和出口
```
const path = require("path");
module.exports = {
    mode:"development",
    //设置入口文件路径
    entry: path.join(__dirname,"./src/js/index.js"),
    //设置出口文件
    output:{
        //设置路径
        path:path.join(__dirname,"./dist"),
        //设置文件名
        filename:"bundle.js"
    }
}
```

补充:配置多入口和多出口
```
const path = require("path");
module.exports = {
    mode:"development",
    //设置入口文件路径
    entry: {
      "one":path.join(__dirname,"./src/js/one.js"),
      "two":path.join(__dirname,"./src/js/one.js")
    },
    //设置出口文件
    output:{
        //设置路径
        path:path.join(__dirname,"./dist"),
        //设置文件名
        filename:"[name].js"
    }
}
```

####D.使用webpack-dev-server自动打包运行项目
第一步,安装webpack-dev-server,安装html-webpack-plugin
npm install webpack-dev-server -D
npm install html-webpack-plugin -D
第二步,修改package.json文件,配置运行指令
在package.json文件中添加或修改scripts节点,添加一个dev指令
"scripts":{
    "dev":"webpack-dev-server --open --host 127.0.0.1 --port 9999"
}
第三步,在webpack.config.js文件中添加html-webpack-plugin插件进行项目预览
```
const HtmlWebpackPlugin = require("html-webpack-plugin");
//创建对象
const htmlPlugin = new HtmlWebpackPlugin({
    //设置生成预览页面的模板文件
    template:"./src/index.html",
    //设置生成的预览页面名称
    filename:"index.html"
})
module.exports = {
    mode:"development",
    //设置入口文件路径
    entry: path.join(__dirname,"./src/js/index.js"),
    //设置出口文件
    output:{
        //设置路径
        path:path.join(__dirname,"./dist"),
        //设置文件名
        filename:"index.js"
    },
    // 使用plugins数组添加webpack需要使用的插件
    plugins:[ htmlPlugin ]
}
```

注意:
无论是修改了package.json还是修改了webpack.config.js,请重启webpack-dev-serve才会让修改的内容生效。

###3.使用加载器预处理文件
加载器loader可以对模块的源代码进行转换。当我们使用import去导入一个文件的时候,加载器会对这个文件进行预处理和转换。
它可以把文件从不同的语言(比如typescript,高级JS语法等)转换成javascript,也可以将图片转换为data URL,甚至可以处理和转换css文件。

一句话,webpack本身只是一个构建工具,它一般只能识别js文件,如果想要让他识别和处理其他类型的文件,我们就需要给他添加功能(加载器)

比如说我们可以安装css的加载器,用来处理css样式,为此我们需要安装css-loader与style-loader,如下:
npm install style-loader css-loader -D

加载器的配置使用方式有三种:
A.使用配置文件
在webpack.config.js文件中可以通过module.rules配置加载器,如下
```
module.exports = {
    mode:"development",
    //设置入口文件路径
    entry: path.join(__dirname,"./src/js/index.js"),
    //设置出口文件
    output:{
        //设置路径
        path:path.join(__dirname,"./dist"),
        //设置文件名
        filename:"index.js"
    },
    // 通过module.rules配置加载器
    module:{
      rules:[
        //rules是一个数组,可以用来配置多个加载器
        //加载器的执行顺序是反向的,也就是说先执行css-load,再执行style-loader
        { test:/\.css$/, use: ['style-loader','css-loader'] }
    }
}
```

B.使用内联语法
就是编写js代码去引入加载器,使用!隔开多个加载器,使用?拼接参数
?后面的modules表示的是打开CSS Modules功能,modules后面跟着的是需要引入并处理的index.css文件路径
import Styles from 'style-loader!css-loader?modules!../css/index.css';

C.在命令行中使用加载器
webpack --module-bind 'css=style-loader!css-loader'

小结:
B形式会导致导入解析的代码过多,C形式会导致命令的参数过多。
所以一般推荐使用A形式,在配置文件中配置,修改和定位都会更加简单。

###4.自定义loader加载器
Webpack中loader是一个CommonJs风格的函数,接收输入的源码,通过同步或异步的方式替换源码后进行输出。
基本形式如下:
module.exports = function(source) {
  //source是源码
  return source.replace(/console/g, '//console')
}

然后需要在webpack.config.js中配置自定义loader

###5.webpack插件
插件是 webpack中一个非常重要的功能。插件目的在于解决 loader 无法实现的其他事
自定义插件的步骤:
A.创建一个函数
function myPlugin(){

}

B.给函数扩展一个apply方法
//注意,定义的函数不能写作箭头函数形式,否则函数中的this指向将会出错
myPlugin.prototype.apply = function( compiler ){
  // compiler是一个对象,包含webpack的所有配置信息,在启动时被初始化,包括entry、output、loaders等配置,这个对象在启动Webpack时被实例化,而且是全局唯一的。Plugin可以通过该对象获取到Webpack的配置信息进行处理
  // compilation Compilation对象可以理解编译对象,包含了模块、依赖、文件等信息,每修改一次文件都会产生一个新的Compilation对象,Plugin可以访问到本次编译过程中的模块、依赖、文件内容等信息
  compiler.plugin('compile', function (compilationParams) {
    console.log(compilationParams)
    console.log('webpack 编译器开始编译...-----')
  })
}

C.指定webpack的事件钩子
常见的钩子如下:

| 钩子           | 说明                | 参数                | 类型   |
| ------------ | ----------------- | ----------------- | ---- |
| afterPlugins | 启动一次新的编译          | compiler          | 同步   |
| compile      | 创建compilation对象之前 | compilationParams | 同步   |
| compilation  | compilation对象创建完成 | compilation       | 同步   |
| emit         | 资源生成完成,输出之前       | compilation       | 异步   |
| afterEmit    | 资源输出到目录完成         | compilation       | 异步   |
| done         | 完成编译        stats        |                   | 同步   |

我们可以使用Tabable暴露的tap,tapAsync和tapPromise方法来调用调用上面的钩子,如下
compiler.hooks.compile.tap('myPlugin', params => {
  console.log('我是同步钩子')
});
compiler.hooks.emit.tapAsync('MyWebpackPlugin', (compilation, callback) => {
  setTimeout(()=>{
    console.log('文件列表', Object.keys(compilation.assets).join(','));
    callback();
  }, 1000);
});

具体的使用案例形式为:
function myPlugin(){

}
myPlugin.prototype.apply = function( compiler ){
  compiler.hooks.emit.tapAsync('MyWebpackPlugin', (compilation, callback) => {
    // compilation.chunks存放了代码块列表
    compilation.chunks.forEach(chunk => {
     // chunk包含多个模块,通过chunk.modulesIterable可以遍历模块列表
                        for(const module of chunk.modulesIterable) {
        // module包含多个依赖,通过module.dependencies进行遍历
              module.dependencies.forEach(dependency => {
          console.log(dependency);
        });
      }
    });
    callback();
  });
}


作者: unde²⁰²⁰    时间: 2020-5-4 08:14
66666666666666666666666666666666666666




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2