任务二:Webpack打包

399 阅读15分钟

1、Webpack快速上手

yarn add webpack-cli --dev //安装webpack
"scripts": {
    "build": "webpack"
  },

在package里添加快捷指令,以后每次只需要yarn build就能进行打包

2、Webpack 配置文件

Webpack4以后的版本支持0配置打包,整个过程将按照约定src/index作为打包的入口,打包的结果会存在dist/main.js当中。 创建webpack.congig.js文件,写入一下配置

const path = require('path')//导入node中pash模块
module.exports = {
  entry: './src/main.js',
  output: {
    filename: 'bundle.js',//输出的文件名
    path: path.join(__dirname, 'output')//拿到output的完整绝对路径
  }
}

3、Webpack工作模式

Webpack4中新增了一个工作模式的用法,简化了webpack配置的复杂程度,可以理解为不同环境的几组预设的配置

const path = require('path')
module.exports = {
  // 这个属性有三种取值,分别是 production、development 和 none。
  // 1. 生产模式下,Webpack 会自动优化打包结果;
  // 2. 开发模式下,Webpack 会自动优化打包速度,添加一些调试过程中的辅助;
  // 3. None 模式下,Webpack 就是运行最原始的打包,不做任何额外处理;
  mode: 'development',
  entry: './src/main.js',
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'dist')
  }
}

4、Webpack打包结果运行原理

不懂

5、Webpack资源模块加载

Webpack内部的loader只能处理JS文件,如果要处理其他类型的资源文件,那么就要添加其他的loader。loader是整个前端实现模块化的核心,通过不同的loader,就可以加载任何类型的资源。 image.png 如果要处理css文件就需要安装css-loader

yarn add css-loader --dev

安装完成后,需要在配置文件当中,rules添加相应的配置,如果配置了多个loader,执行顺序是从后往前

const path = require('path')

module.exports = {
  mode: 'none',
  entry: './src/main.css',
  output: {
    filename: 'bundle.js',
    path: path.join(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      }
    ]
  }
}

6、Webpack导入资源模块

根据代码需要动态导入资源,需要资源的不是应用,而是代码。 js驱动整个前端应用

7、Webpack loader集

webpack内部loader只能处理js文件,当遇到不是js当文件就需要下载其他loader来转换成js代码,实现加载。

1.文件资源加载器(处理图片资源文件)

yarn add file-loader --dev 
module: {
    rules: [
      {
        test: /.png$/,
        use: 'file-loader'
      }
    ]
  }

文件加载器的工作过程 webpack在打包时,遇到了图片文件,根据配置文件中的配置,匹配对应的文件加载器,文件加载器开始工作,先是将导入的文件,拷贝到输出的目录,然后将文件拷贝到输出目录过后的路径作为当前模块的返回值返回,所需要的资源就发布出来了,然后就可以通过模块的导出成员拿到

image.png

2.URL加载器

通过Data URLs形式表示文件,Data URL是一种特殊的URL协议,可以用来直接表示一个文件,传统url需要服务器有一个对应的url文件,然后通过请求这个地址得到服务器上对应的文件。

Data URLs是一种当前url就可以直接去表示文件内容的方式,这种url当中的文本就已经包含了文件的内容,使用这种url就不会去发生任何的http请求。

最佳实践,小文件使用Data URLs,减少请求次数,大文件单独提取存放,提高加载速度

yarn add url-loader --dev
module: {
    rules: [
      {
        test: /.png$/,
        use: {
          loader: 'url-loader',
          options: {
            limit: 10 * 1024 // 10 KB 将小于10kb的文件用Data URLs处理
            //使用这个限制的方式,就一定要配上file-loader,超出限制的文件要用
            //file-loader处理
          }
        }
      }
    ]
  }

8、Webpack常用加载器分类

1.编译转换类

  • 会把加载到的资源模块转换成js代码 (css-loader)

2.文件操作类型

  • 会把加载到的资源模块,拷贝到输出的目录,同时又将文件的访问路径向外导出(file-loader)

3.代码检查类型

  • 对代码进行校验的加载器,目的是统一代码风格,提高代码质量

9、Webpack与ES2015

webpck只是打包工具,不会去处理ES6或者更高版本的新特性,如果需要处理,可以安装特定的加载器来完成。

加载器可以用来编译转换代码

10、Webpack模块加载方式

image.png 尽量不再要项目中混合使用这些标准,会降低项目可维护性,使用一个就可以了。

loader加载非js文件也会触发资源加载

几乎所有有引用资源的地方都会被webpack找出来,然后根据配置交给不同的loader处理,最后将处理的结果整体打包到输出目录,webpack就是通过这样的特点实现整个项目的模块化。

11、Webpack核心工作原理

Loader机制是Webpack的核心

image.png

打包核心工作过程

在项目中,都会散落着各种资源文件,webpack会根据配置找到其中一个,作为打包的入口。一般情况下这个文件都是js文件,然后会顺着入口文件的代码,根据代码中出现的import或require之类的语句,解析推断出这个文件所依赖的资源模块,然后分别去解析每一个资源模块对应的依赖,最后就形成了整个项目中所有文件依赖关系的依赖树,有了这个依赖树,webpack会递归这个依赖树,然后找到每个节点所对应的资源文件,最后在根据配置文件中的rules属性,去找到这个模块所对应的加载器,然后交给对应的加载器去加载这个模块,最后将加载到的结果放入到打包结果中,从而去实现整个项目的打包。

12、Webpack Loader工作原理

loader负责资源文件从输入到输出的转换,对于同一个资源可以依次使用多个loader处理,但是loader管道最后必须要返回一个js代码

image.png

13、Webpack 常用插件

1.自动清除目录插件

yarn add clean-webpack-plugin --dev

2.自动生成使用打包结果的HTML

yarn add html-webpack-plugin --dev

3.复制静态资源

yarn add copy-webpack-plugin --dev

14、Webpack 开发一个插件

webpack要求插件必须是一个函数或者是一个包含apply方法的对象,插件是通过在生命周期的钩子中挂载函数实现扩展

class MyPlugin {
  apply (compiler) {
    console.log('MyPlugin 启动')

    compiler.hooks.emit.tap('MyPlugin', compilation => {
      // compilation => 可以理解为此次打包的上下文
      for (const name in compilation.assets) {
        // console.log(name)
        // console.log(compilation.assets[name].source())
        if (name.endsWith('.js')) {
          const contents = compilation.assets[name].source()
          const withoutComments = contents.replace(/\/\*\*+\*\//g, '')
          compilation.assets[name] = {
            source: () => withoutComments,
            size: () => withoutComments.length
          }
        }
      }
    })
  }
}

module.exports = {
  plugins: [
    new CleanWebpackPlugin(),
    // 用于生成 index.html
    new HtmlWebpackPlugin({
      title: 'Webpack Plugin Sample',
      meta: {
        viewport: 'width=device-width'
      },
      template: './src/index.html'
    }),
    // 用于生成 about.html
    new HtmlWebpackPlugin({
      filename: 'about.html'
    }),
    new CopyWebpackPlugin([
      // 'public/**'
      'public'
    ]),
    new MyPlugin()//自己创建的插件
  ]
}

15、Webpack 自动编译

watch工作模式

监听文件变化,自动重新打包

yarn wabpack --watch //这样运行命令webpack会进行监听模式

16、Webpack 自动刷新浏览器

安装BrowserSync插件 弊端比较多

17、Webpack Dev Server

webpack提供用于开发的HTTP Server,集成了自动编译自动刷新浏览器等功能,为了提高工作效率,dev-server并没有将打包结果写入到磁盘当中(暂时存在内存当中),内部的http server就是直接从内存当中读取这些文件,然后发送给浏览器,这样就能减少很多不必要到磁盘读写操作,大大提高构建效率。

yarn add webpack-dev-server --dev

静态资源访问

只要是通过webpack打包输出的文件,都可以正常被访问到,但是如果还有静态资源也需要作为开发服务器到资源被访问,那就需要额外的去告诉webpack dev-server。具体的方法,在webpack的配置当中去添加配置,配置devServer属性。

devServer: {
    contentBase: './public',//contentBase可以是字符串或数组
  },

contentBase能为开发服务器额外指定查找资源目录

代理API服务 不懂

18、Source Map(源代码地图)

用于映射源代码和转换后代码的关系,转换后的代码可以用Source Map逆向转换回源代码。主要是解决了源代码与运行代码不一致所产生的调试问题

19、Webpack 配置Source Map

webpack对source map的风格支持有12种,也就是有很多种实现方式,每种方式的效果和效率都是不同的,效果最好的,生成速度最慢,速度最快,生成的就没什么效果。

image.png

image.png

eval模式

构建最快,效果很简单,只能定位源代码的名称,而不知道具体的行列信息

20、Webpack devtool模式对比

image.png

inline-source-map

hidden-source-map

这个模式下,在开发工具下是看不到source map效果的,在开发第三方包比较有用,

nosource-source-map

这个模式能看到错误输入信息,但是点击进去看不到源代码的,表示没有源代码,但是提供了行列信息,这是为了在生成环境中保护源代码不被别人看到。

21、如何选择合适的Source Map

开发模式

常用cheap-module-eval-source-map模式

image.png

生产模式

不生成,因为会暴露源代码,因为调试是在开发阶段的事情,所以没必要在生成阶段还使用source map,如果一定要使用,可以使用nosource-source-map模式。

22、Webpack HMR(模块热替换)

热拔插

在一个运行的机器上随时插拔设备,而且机器的状态也不会受到插拔设备的影响,且插上的设备可以立即开始工作如生活中的USB接口

模块热替换

在程序运行中,实时替换某个模块,应用的运行状态也不会被改变,算是Webpack中最强大的特性之一,也是最受欢迎的特性。

使用

HMR集成在webpack-dev-server中不用再单独安装模块,只需要在运行webpack-dev-server这个命令时候在后面加上--hot就能开启这个特性,或者在配置文件配置。

image.png

实现所有模块热替换

样式文件可以直接热更新,而js文件不能,是因为js文件编码无规律,所以webpack无法处理更新后的模块, 所以无法现实现所有文件通用的替换方案,就需要手动处理js模块更新后的热替换。

通过脚手架创建的项目,内部都集成了HMR方案,所以不需要手动处理。

webpack中的HMR不是开箱即用的,还需要做一些操作,需要手动处理模块热替换逻辑,

JS模块热替换

image.png

图片模块热替换

image.png

个人理解

就是在开发者改变源代码,页面会刷新,然后在页面编辑的东西就会丢失掉,又要再一次写入,但是如果想加入新代码,状态数据不丢失,就要使用模块热替换。

23、Webpack使用HMR APIs

js文件是通过HMR的API来实现模块的热替换的,使用后就不会触发页面更新了。

image.png

因为每个文件的需要保留的状态不同,无法写出相同的替换逻辑,所以不同状态的js文件替换需要自己写出不同的替换逻辑 image.png

24、Webpack HMR注意事项

1.处理HMR的代码报错会导致自动刷新,使用hotOnly的方式来处理,处理后如果HMR代码报错,就不会自动刷新,就能快速定位到写错的地方。

image.png

2.没有启用HMR的情况下,HMR API报错

当使用HMR的API但是启动Dev Server的时候没有开启HMR的选项,此时就会报错,所以这是后要写判断,判断有这个对象,有才去使用

image.png

25、Webpack环境配置与优化

生产环境和开发环境有很大的差别,生产环境注重运行效率,开发环境注重开发效率,而在开发环境中我们为了让开发效率更高,会采用很多工具如Source Map和HMR等,这些工具都会往输出结果中添加额外的代码,这样就会让打包结果越来越臃肿。webpack建议为不同的工作环境创建不同的配置。

不同环境下的配置方式

  1. 配置文件根据环境不同导出不同配置,判断环境名参数这种方式只适合中小型项目 image.png
yarn webpack //不传入参数,webpack采用开发模式打包
yarn webpack --env production //传入参数production,采用生产模式打包
  1. 一个环境对应一个配置文件,大型项目建议使用的方式,这种情况下至少有三个配置文件,其中两个是用来适配不同环境的(开发和生产),另外一个是公共的配置。

首先建立webpack.common.js用于存放公共配置,webpack.dev.js用于存放开发环境配置,webpack.prod.js用于存放生产环境配置

下载webpack-merge用于与公共环境配置的合并

yarn add webpack-merge --dev

image.png

运行yarn webpack --config webpack.prod.js就能以生产环境的配置打包了

26、Webpack DefinePlugin(插件)

用于为全局注册成员

image.png

27、Webpack Tree-shaking(摇树)

用于摇掉代码中未引用掉部分(未引用代码dead-code),会自动检查未引用掉代码,让后将它们移除,去除冗余代码是生产环境非常重要的一个优化,Tree-shaking这个功能会在生产环境下自动开启。

使用

Tree-shaking不是webpack中的某一个配置选项,是一组功能搭配使用后的优化效果

image.png

28、Webpack 合并模块(concatenateModules)

作用就是,尽可能把所有的模块合并输出到一个函数中,这样既提升了工作效率,又减小了代码体积,这个特性又被称作为Scope Hoisting(作用域提升),是webpack3中添加的特性,配合minimize一起使用会让代码体积减小很多 image.png

29、Webpack Tree-shaking & Babel

很多书中说使用Tree-shaking会导致Babel-loader失效问题

首先Tree-shaking实现的前提是必须要使用ESModules来组织代码,相当于交给webpack的打包代码必须是ESM的方式来去实现的模块化。 webpack在打包前会先把模块根据配置,交给不同的loader处理,然后将所有loader处理的结果打包到一起,那么为了转化代码中的ESAScript新特性,会多时候都会使用babel-loader处理JS,而在babel转化代码时,就会可能处理掉ESModules把它们处理成CommonJS(取决于有没有使用转换ESM的插件),

结论

最新版本的Babel-loader不会导致Tree-shaking失效,因为babel-loader会帮我们自动关掉ESM转换的插件,如果实在不放心,也可以在webpack配置中手动去关掉ESM的转换。

image.png

30、Webpack sideEffects(副作用)

允许我们运用配置的方式来标记代码是否有副作用,从而为Tree-shaking提供更大的压缩空间。副作用:模块执行时除了导出成员之外做的事情。使用这个功能的前提是确保你的代码没有副作用

sideEffects一般用于npm包标记是否有副作用

场景

一个文件导出三个包,而此时你需要导入这个文件,使用这个文件中的一个包,但是打包后三个包都会在打包的结果中出现,想要的结果是,把两个没有使用的包去掉,这时候就可以使用sideEffects这个特性。在webpack中配置sideEffects是为了开启这个功能,在package中为了标识我们的代码没有副作用。

image.png

image.png

31、Webpack 代码分割(code splitting)

1.多个打包入口,同时打包,输出多个打包结果, 适用场景,多页应用程序,一个页面对应一个打包入口,公共的部分单独提取

image.png

提取公共模块

不同的入口肯定有公共模块

image.png

2.动态导入,实现模块的按需加载,节省带宽和流量。 动态导入的模块会被自动分包

image.png

32、Webpack 魔法注释

默认通过动态导入,产生的打包文件,名字只是一个序号,如果需要给打包文件命名,可以使用魔法注释

image.png 这样写后两个文件都会被打包到components的文件中,这样就要可以灵活的让动态加载的文件,到注定的打包位置了。

33、Webpack MiniCssExtractPlugin(提取css到单个文件)

css超过150kb才考虑提取到单个文件当中

yarn add mini-css-extract-plugin --dev

34、Webpack optimize-css-assets-webpack-plugin(压缩css文件)

image.png

34、Webpack 输出文件名Hash

在部署前端资源文件时,都会启用服务器的静态资源缓存,用户浏览器缓存静态资源后就不需要请求服务器得到这些静态资源,这样响应速度就会大幅度提升。

开启客户端资源缓存会带来新的问题,如果在缓存策略中,缓存失效的时间设置过长,在这个时间中应用发生更新,重新部署过后,没有办法及时更新到客户端,为了解决这个问题,在生产模式下,建议输出的文件名使用hash值,一旦资源文件发生改变,文件名称也会一起变化,对于客户端而言,全新的文件名就相当于全新的请求,就解决了上述问题。

支持hash的设置文件名的方式有三种

项目级别hash image.png

chunkhash image.png

contenthash文件级别hash(最适合用来解决缓存问题的) image.png

hash一般为20为,也可以手动设计位数

image.png